From 886d97413bfe6f08036033d46f9555e803847f3c Mon Sep 17 00:00:00 2001 From: Alissa Crane Date: Tue, 20 Aug 2024 14:52:08 -0700 Subject: [PATCH] chore: remove address prop and update sponsor txn check (#1114) Co-authored-by: Alissa Crane --- .changeset/brave-icons-fix.md | 5 ++ site/docs/pages/transaction/transaction.mdx | 8 +- site/docs/pages/transaction/types.mdx | 1 - src/transaction/components/Transaction.tsx | 2 - .../components/TransactionButton.test.tsx | 5 +- .../components/TransactionButton.tsx | 5 +- .../components/TransactionProvider.test.tsx | 77 ++++++++++++------- .../components/TransactionProvider.tsx | 2 - .../components/TransactionSponsor.test.tsx | 28 +++++-- .../components/TransactionSponsor.tsx | 2 +- src/transaction/types.ts | 3 - 11 files changed, 86 insertions(+), 52 deletions(-) create mode 100644 .changeset/brave-icons-fix.md diff --git a/.changeset/brave-icons-fix.md b/.changeset/brave-icons-fix.md new file mode 100644 index 0000000000..523fcb5a1e --- /dev/null +++ b/.changeset/brave-icons-fix.md @@ -0,0 +1,5 @@ +--- +"@coinbase/onchainkit": patch +--- + +- **patch**: Remove unneccessary address prop from Transaction component and fix issue where Sponsor component isn't visible. By @abcrane123 #1114 diff --git a/site/docs/pages/transaction/transaction.mdx b/site/docs/pages/transaction/transaction.mdx index d235dacde4..3c0245f181 100644 --- a/site/docs/pages/transaction/transaction.mdx +++ b/site/docs/pages/transaction/transaction.mdx @@ -72,7 +72,6 @@ export default function TransactionComponents() { return address ? ( // [!code focus] @@ -104,7 +103,6 @@ export default function TransactionComponents() { if (address) { return ( // [!code focus] // [!code focus] @@ -158,7 +155,6 @@ export default function TransactionComponents() { if (address) { return ( { if (address) { return ( - ({ vi.mock('wagmi', () => ({ useChainId: vi.fn(), + useAccount: vi.fn(), })); vi.mock('wagmi/experimental', () => ({ @@ -25,6 +26,7 @@ vi.mock('../../network/getChainExplorer', () => ({ describe('TransactionButton', () => { beforeEach(() => { (useChainId as vi.Mock).mockReturnValue(123); + (useAccount as vi.Mock).mockReturnValue({ address: '123' }); (useShowCallsStatus as vi.Mock).mockReturnValue({ showCallsStatus: vi.fn(), }); @@ -116,7 +118,6 @@ describe('TransactionButton', () => { it('should enable button when not in progress, not missing props, and not waiting for receipt', () => { (useTransactionContext as vi.Mock).mockReturnValue({ - address: '0x123', contracts: {}, isLoading: false, lifeCycleStatus: { statusName: 'init', statusData: null }, diff --git a/src/transaction/components/TransactionButton.tsx b/src/transaction/components/TransactionButton.tsx index 08c95ab15a..f3948c2a38 100644 --- a/src/transaction/components/TransactionButton.tsx +++ b/src/transaction/components/TransactionButton.tsx @@ -1,5 +1,5 @@ import { useCallback, useMemo } from 'react'; -import { useChainId } from 'wagmi'; +import { useAccount, useChainId } from 'wagmi'; import { useShowCallsStatus } from 'wagmi/experimental'; import { Spinner } from '../../internal/components/Spinner'; import { getChainExplorer } from '../../network/getChainExplorer'; @@ -14,7 +14,6 @@ export function TransactionButton({ text: buttonText = 'Transact', }: TransactionButtonReact) { const { - address, contracts, chainId, errorMessage, @@ -26,6 +25,8 @@ export function TransactionButton({ transactionId, } = useTransactionContext(); + const { address } = useAccount(); + const accountChainId = chainId ?? useChainId(); const { showCallsStatus } = useShowCallsStatus(); diff --git a/src/transaction/components/TransactionProvider.test.tsx b/src/transaction/components/TransactionProvider.test.tsx index ae488760bc..1a08cf48c2 100644 --- a/src/transaction/components/TransactionProvider.test.tsx +++ b/src/transaction/components/TransactionProvider.test.tsx @@ -104,7 +104,7 @@ describe('TransactionProvider', () => { it('should emit onError when setLifeCycleStatus is called with error', async () => { const onErrorMock = vi.fn(); render( - + , ); @@ -116,11 +116,7 @@ describe('TransactionProvider', () => { it('should emit onStatus when setLifeCycleStatus is called with transactionLegacyExecuted', async () => { const onStatusMock = vi.fn(); render( - + , ); @@ -139,11 +135,7 @@ describe('TransactionProvider', () => { it('should emit onStatus when setLifeCycleStatus is called', async () => { const onStatusMock = vi.fn(); render( - + , ); @@ -162,7 +154,6 @@ describe('TransactionProvider', () => { }); render( @@ -186,7 +177,7 @@ describe('TransactionProvider', () => { writeContractsAsync: writeContractsAsyncMock, }); render( - + , ); @@ -207,7 +198,7 @@ describe('TransactionProvider', () => { writeContractsAsync: writeContractsAsyncMock, }); render( - + , ); @@ -228,7 +219,7 @@ describe('TransactionProvider', () => { writeContractsAsync: writeContractsAsyncMock, }); render( - + , ); @@ -248,7 +239,7 @@ describe('TransactionProvider', () => { writeContractsAsync: writeContractsAsyncMock, }); render( - + , ); @@ -270,7 +261,7 @@ describe('TransactionProvider', () => { switchChainAsync: switchChainAsyncMock, }); render( - + , ); @@ -287,7 +278,7 @@ describe('TransactionProvider', () => { writeContractsAsync: vi.fn().mockRejectedValue(new Error('Test error')), }); render( - + , ); @@ -302,7 +293,7 @@ describe('TransactionProvider', () => { it('should not fetch receipts if contract list is empty', async () => { const waitForTransactionReceiptMock = vi.fn(); render( - + , ); @@ -322,7 +313,7 @@ describe('TransactionProvider', () => { writeContractsAsync: writeContractsAsyncMock, }); render( - + , ); @@ -341,7 +332,7 @@ describe('TransactionProvider', () => { }); (useAccount as ReturnType).mockReturnValue({ chainId: 1 }); render( - + , ); @@ -362,11 +353,11 @@ describe('TransactionProvider', () => { writeContractsAsync: writeContractsAsyncMock, }); (useWriteContract as ReturnType).mockReturnValue({ - status: 'IDLE', + status: 'idle', writeContractAsync: writeContractAsyncMock, }); render( - + , ); @@ -389,11 +380,11 @@ describe('TransactionProvider', () => { writeContractsAsync: writeContractsAsyncMock, }); (useWriteContract as ReturnType).mockReturnValue({ - status: 'IDLE', + status: 'idle', writeContractAsync: writeContractAsyncMock, }); render( - + , ); @@ -421,11 +412,11 @@ describe('TransactionProvider', () => { writeContractsAsync: writeContractsAsyncMock, }); (useWriteContract as ReturnType).mockReturnValue({ - status: 'IDLE', + status: 'idle', writeContractAsync: writeContractAsyncMock, }); render( - + , ); @@ -440,6 +431,38 @@ describe('TransactionProvider', () => { ); }); }); + + it('should call setLifeCycleStatus when calling fallbackToWriteContract when user rejects request', async () => { + const writeContractsAsyncMock = vi + .fn() + .mockRejectedValue(new Error(METHOD_NOT_SUPPORTED_ERROR_SUBSTRING)); + const writeContractAsyncMock = vi + .fn() + .mockRejectedValue({ cause: { name: 'UserRejectedRequestError' } }); + (useWriteContracts as ReturnType).mockReturnValue({ + status: 'idle', + writeContractsAsync: writeContractsAsyncMock, + }); + (useWriteContract as ReturnType).mockReturnValue({ + status: 'idle', + writeContractAsync: writeContractAsyncMock, + }); + render( + + + , + ); + const button = screen.getByText('Submit'); + fireEvent.click(button); + await waitFor(() => { + expect(screen.getByTestId('context-value-errorCode').textContent).toBe( + 'TmTPc02', + ); + expect(screen.getByTestId('context-value-errorMessage').textContent).toBe( + 'Request denied.', + ); + }); + }); }); describe('useTransactionContext', () => { diff --git a/src/transaction/components/TransactionProvider.tsx b/src/transaction/components/TransactionProvider.tsx index 0a3e6733da..c927471c80 100644 --- a/src/transaction/components/TransactionProvider.tsx +++ b/src/transaction/components/TransactionProvider.tsx @@ -43,7 +43,6 @@ export function useTransactionContext() { } export function TransactionProvider({ - address, capabilities, chainId, children, @@ -256,7 +255,6 @@ export function TransactionProvider({ ]); const value = useValue({ - address, chainId, contracts, errorCode, diff --git a/src/transaction/components/TransactionSponsor.test.tsx b/src/transaction/components/TransactionSponsor.test.tsx index 69ad817a87..62c98aacad 100644 --- a/src/transaction/components/TransactionSponsor.test.tsx +++ b/src/transaction/components/TransactionSponsor.test.tsx @@ -8,23 +8,41 @@ vi.mock('./TransactionProvider', () => ({ })); describe('TransactionSponsor', () => { - it('renders correctly', () => { + it('should render correctly', () => { (useTransactionContext as vi.Mock).mockReturnValue({ - lifeCycleStatus: { statusName: 'transactionIdle', statusData: null }, + lifeCycleStatus: { statusName: 'init', statusData: null }, hasPaymaster: true, }); render(); - const element = screen.getByText('Zero transaction fee'); expect(element).toBeInTheDocument(); }); - it('does not render if hasPaymaster is false', () => { + + it('should not render if hasPaymaster is false', () => { (useTransactionContext as vi.Mock).mockReturnValue({ - lifeCycleStatus: { statusName: 'transactionIdle', statusData: null }, + lifeCycleStatus: { statusName: 'init', statusData: null }, hasPaymaster: false, }); render(); + expect(screen.queryByText('Zero transaction fee')).not.toBeInTheDocument(); + }); + it('should not render if statusName is not init', () => { + (useTransactionContext as vi.Mock).mockReturnValue({ + lifeCycleStatus: { statusName: 'blah', statusData: null }, + hasPaymaster: false, + }); + render(); expect(screen.queryByText('Zero transaction fee')).not.toBeInTheDocument(); }); + + it('should render if statusName is init', () => { + (useTransactionContext as vi.Mock).mockReturnValue({ + lifeCycleStatus: { statusName: 'init', statusData: null }, + hasPaymaster: true, + }); + render(); + const element = screen.getByText('Zero transaction fee'); + expect(element).toBeInTheDocument(); + }); }); diff --git a/src/transaction/components/TransactionSponsor.tsx b/src/transaction/components/TransactionSponsor.tsx index 9c1f7f3c2a..8598221d57 100644 --- a/src/transaction/components/TransactionSponsor.tsx +++ b/src/transaction/components/TransactionSponsor.tsx @@ -14,7 +14,7 @@ export function TransactionSponsor({ className }: TransactionSponsorReact) { const transactionInProgress = transactionId || transactionHash; if ( - lifeCycleStatus.statusName !== 'transactionIdle' || + lifeCycleStatus.statusName !== 'init' || !hasPaymaster || errorMessage || transactionInProgress || diff --git a/src/transaction/types.ts b/src/transaction/types.ts index 6700aca672..766594c69b 100644 --- a/src/transaction/types.ts +++ b/src/transaction/types.ts @@ -61,7 +61,6 @@ export type TransactionButtonReact = { }; export type TransactionContextType = { - address: Address; // The wallet address involved in the transaction. chainId?: number; // The chainId for the transaction. contracts: ContractFunctionParameters[]; // An array of contracts for the transaction. errorCode?: string; // An error code used to localize errors and provide more context with unit-tests. @@ -96,7 +95,6 @@ export type TransactionError = { }; export type TransactionProviderReact = { - address: Address; // The wallet address to be provided to child components. capabilities?: WalletCapabilities; // Capabilities that a wallet supports (e.g. paymasters, session keys, etc). chainId?: number; // The chainId for the transaction. children: ReactNode; // The child components to be rendered within the provider component. @@ -110,7 +108,6 @@ export type TransactionProviderReact = { * Note: exported as public Type */ export type TransactionReact = { - address: Address; // The wallet address involved in the transaction. capabilities?: WalletCapabilities; // Capabilities that a wallet supports (e.g. paymasters, session keys, etc). chainId?: number; // The chainId for the transaction. children: ReactNode; // The child components to be rendered within the transaction component.