UNPKG

@reown/appkit-pay

Version:
210 lines 11.6 kB
import { beforeEach, describe, expect, test, vi } from 'vitest'; import { ConstantsUtil, erc20ABI } from '@reown/appkit-common'; import { ChainController, ConnectionController, CoreHelperUtil } from '@reown/appkit-controllers'; import { AppKitPayError, AppKitPayErrorCodes } from '../../src/types/errors'; import { ensureCorrectNetwork, processEvmErc20Payment, processEvmNativePayment } from '../../src/utils/PaymentUtil'; vi.mock('@reown/appkit-controllers', () => ({ ChainController: { switchActiveNetwork: vi.fn(), getNetworkProp: vi.fn() }, ConnectionController: { parseUnits: vi.fn((amount, decimals) => BigInt(parseFloat(amount) * 10 ** decimals)), sendTransaction: vi.fn(), writeContract: vi.fn() }, CoreHelperUtil: { sortRequestedNetworks: vi.fn() } })); const MOCK_EVM_NETWORK = { caipNetworkId: 'eip155:1', chainNamespace: ConstantsUtil.CHAIN.EVM, rpcUrls: { default: { http: ['http://example.com:8545'] } }, id: '1', name: 'Ethereum', nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 } }; const MOCK_OTHER_EVM_NETWORK = { caipNetworkId: 'eip155:137', chainNamespace: ConstantsUtil.CHAIN.EVM, rpcUrls: { default: { http: ['http://example:8546'] } }, id: '137', name: 'Polygon', nativeCurrency: { name: 'Matic', symbol: 'MATIC', decimals: 18 } }; const MOCK_FROM_ADDRESS = '0x1234567890abcdef1234567890abcdef12345678'; const MOCK_RECIPIENT_ADDRESS = '0xabcdef1234567890abcdef1234567890abcdef12'; const MOCK_TOKEN_ADDRESS = '0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9'; describe('PaymentUtil', () => { beforeEach(() => { vi.resetAllMocks(); }); describe('ensureCorrectNetwork', () => { test('should return if network is already correct', async () => { const options = { paymentAssetNetwork: MOCK_EVM_NETWORK.caipNetworkId, activeCaipNetwork: MOCK_EVM_NETWORK, approvedCaipNetworkIds: [MOCK_EVM_NETWORK.caipNetworkId], requestedCaipNetworks: [MOCK_EVM_NETWORK] }; vi.mocked(CoreHelperUtil.sortRequestedNetworks).mockReturnValue([MOCK_EVM_NETWORK]); await expect(ensureCorrectNetwork(options)).resolves.toBeUndefined(); expect(ChainController.switchActiveNetwork).not.toHaveBeenCalled(); }); test('should switch network if different but allowed', async () => { const options = { paymentAssetNetwork: MOCK_OTHER_EVM_NETWORK.caipNetworkId, activeCaipNetwork: MOCK_EVM_NETWORK, approvedCaipNetworkIds: [ MOCK_EVM_NETWORK.caipNetworkId, MOCK_OTHER_EVM_NETWORK.caipNetworkId ], requestedCaipNetworks: [MOCK_EVM_NETWORK, MOCK_OTHER_EVM_NETWORK] }; vi.mocked(CoreHelperUtil.sortRequestedNetworks).mockReturnValue([ MOCK_EVM_NETWORK, MOCK_OTHER_EVM_NETWORK ]); vi.mocked(ChainController.getNetworkProp).mockReturnValue(false); await ensureCorrectNetwork(options); expect(ChainController.switchActiveNetwork).toHaveBeenCalledWith(MOCK_OTHER_EVM_NETWORK); }); test('should switch network if different and supportsAllNetworks is true', async () => { const options = { paymentAssetNetwork: MOCK_OTHER_EVM_NETWORK.caipNetworkId, activeCaipNetwork: MOCK_EVM_NETWORK, approvedCaipNetworkIds: [MOCK_EVM_NETWORK.caipNetworkId], requestedCaipNetworks: [MOCK_EVM_NETWORK, MOCK_OTHER_EVM_NETWORK] }; vi.mocked(CoreHelperUtil.sortRequestedNetworks).mockReturnValue([ MOCK_EVM_NETWORK, MOCK_OTHER_EVM_NETWORK ]); vi.mocked(ChainController.getNetworkProp).mockReturnValue(true); await ensureCorrectNetwork(options); expect(ChainController.switchActiveNetwork).toHaveBeenCalledWith(MOCK_OTHER_EVM_NETWORK); }); test('should throw if payment asset network not found', async () => { const options = { paymentAssetNetwork: 'eip155:unknown', activeCaipNetwork: MOCK_EVM_NETWORK, approvedCaipNetworkIds: [MOCK_EVM_NETWORK.caipNetworkId], requestedCaipNetworks: [MOCK_EVM_NETWORK] }; vi.mocked(CoreHelperUtil.sortRequestedNetworks).mockReturnValue([MOCK_EVM_NETWORK]); await expect(ensureCorrectNetwork(options)).rejects.toThrow(new AppKitPayError(AppKitPayErrorCodes.INVALID_PAYMENT_CONFIG)); }); test('should throw if network switch is needed but not allowed', async () => { const options = { paymentAssetNetwork: MOCK_OTHER_EVM_NETWORK.caipNetworkId, activeCaipNetwork: MOCK_EVM_NETWORK, approvedCaipNetworkIds: [MOCK_EVM_NETWORK.caipNetworkId], requestedCaipNetworks: [MOCK_EVM_NETWORK, MOCK_OTHER_EVM_NETWORK] }; vi.mocked(CoreHelperUtil.sortRequestedNetworks).mockReturnValue([ MOCK_EVM_NETWORK, MOCK_OTHER_EVM_NETWORK ]); vi.mocked(ChainController.getNetworkProp).mockReturnValue(false); await expect(ensureCorrectNetwork(options)).rejects.toThrow(new AppKitPayError(AppKitPayErrorCodes.INVALID_PAYMENT_CONFIG)); }); test('should throw generic error if switch fails', async () => { const switchError = new Error('Switch failed'); const options = { paymentAssetNetwork: MOCK_OTHER_EVM_NETWORK.caipNetworkId, activeCaipNetwork: MOCK_EVM_NETWORK, approvedCaipNetworkIds: [ MOCK_EVM_NETWORK.caipNetworkId, MOCK_OTHER_EVM_NETWORK.caipNetworkId ], requestedCaipNetworks: [MOCK_EVM_NETWORK, MOCK_OTHER_EVM_NETWORK] }; vi.mocked(CoreHelperUtil.sortRequestedNetworks).mockReturnValue([ MOCK_EVM_NETWORK, MOCK_OTHER_EVM_NETWORK ]); vi.mocked(ChainController.getNetworkProp).mockReturnValue(false); vi.mocked(ChainController.switchActiveNetwork).mockRejectedValue(switchError); await expect(ensureCorrectNetwork(options)).rejects.toThrow(new AppKitPayError(AppKitPayErrorCodes.GENERIC_PAYMENT_ERROR, switchError)); }); }); describe('processEvmNativePayment', () => { const paymentAsset = { asset: 'native', network: MOCK_EVM_NETWORK.caipNetworkId, recipient: MOCK_RECIPIENT_ADDRESS, amount: 1.5, metadata: { name: 'Ether', symbol: 'ETH', decimals: 18 } }; test('should send native EVM transaction successfully', async () => { const mockTxHash = '0xtxhash123'; const expectedAmountBigInt = BigInt(1.5 * 10 ** paymentAsset.metadata.decimals); vi.mocked(ConnectionController.parseUnits).mockReturnValue(expectedAmountBigInt); vi.mocked(ConnectionController.sendTransaction).mockResolvedValue(mockTxHash); const txHash = await processEvmNativePayment(paymentAsset, ConstantsUtil.CHAIN.EVM, MOCK_FROM_ADDRESS); expect(ConnectionController.parseUnits).toHaveBeenCalledWith('1.5', paymentAsset.metadata.decimals); expect(ConnectionController.sendTransaction).toHaveBeenCalledWith({ chainNamespace: ConstantsUtil.CHAIN.EVM, to: MOCK_RECIPIENT_ADDRESS, address: MOCK_FROM_ADDRESS, value: expectedAmountBigInt, data: '0x' }); expect(txHash).toBe(mockTxHash); }); test('should use default decimals (18) if metadata is missing', async () => { const paymentAssetNoMeta = { ...paymentAsset, metadata: undefined }; const mockTxHash = '0xtxhash456'; const expectedAmountBigInt = BigInt(1.5 * 10 ** 18); vi.mocked(ConnectionController.parseUnits).mockReturnValue(expectedAmountBigInt); vi.mocked(ConnectionController.sendTransaction).mockResolvedValue(mockTxHash); await processEvmNativePayment(paymentAssetNoMeta, ConstantsUtil.CHAIN.EVM, MOCK_FROM_ADDRESS); expect(ConnectionController.parseUnits).toHaveBeenCalledWith('1.5', 18); expect(ConnectionController.sendTransaction).toHaveBeenCalledWith(expect.objectContaining({ value: expectedAmountBigInt })); }); test('should throw if parsed amount is not bigint', async () => { vi.mocked(ConnectionController.parseUnits).mockReturnValue(undefined); await expect(processEvmNativePayment(paymentAsset, ConstantsUtil.CHAIN.EVM, MOCK_FROM_ADDRESS)).rejects.toThrow(new AppKitPayError(AppKitPayErrorCodes.GENERIC_PAYMENT_ERROR)); vi.mocked(ConnectionController.parseUnits).mockImplementation((amount, decimals) => BigInt(parseFloat(amount) * 10 ** decimals)); }); test('should throw if chain namespace is not EVM', async () => { await expect(processEvmNativePayment(paymentAsset, ConstantsUtil.CHAIN.SOLANA, MOCK_FROM_ADDRESS)).rejects.toThrow(new AppKitPayError(AppKitPayErrorCodes.INVALID_CHAIN_NAMESPACE)); }); test('should return undefined if sendTransaction returns undefined', async () => { vi.mocked(ConnectionController.parseUnits).mockReturnValue(BigInt(1.5 * 10 ** 18)); vi.mocked(ConnectionController.sendTransaction).mockResolvedValue(undefined); const txHash = await processEvmNativePayment(paymentAsset, ConstantsUtil.CHAIN.EVM, MOCK_FROM_ADDRESS); expect(txHash).toBeUndefined(); }); }); describe('processEvmErc20Payment', () => { const paymentAsset = { asset: MOCK_TOKEN_ADDRESS, network: MOCK_EVM_NETWORK.caipNetworkId, recipient: MOCK_RECIPIENT_ADDRESS, amount: 100.5, metadata: { name: 'USDC', symbol: 'USDC', decimals: 6 } }; const expectedAmountBigInt = BigInt(100.5 * 10 ** 6); test('should write ERC20 transfer contract successfully', async () => { vi.mocked(ConnectionController.parseUnits).mockReturnValue(expectedAmountBigInt); await processEvmErc20Payment(paymentAsset, MOCK_FROM_ADDRESS); expect(ConnectionController.parseUnits).toHaveBeenCalledWith('100.5', 6); expect(ConnectionController.writeContract).toHaveBeenCalledWith({ fromAddress: MOCK_FROM_ADDRESS, tokenAddress: MOCK_TOKEN_ADDRESS, args: [MOCK_RECIPIENT_ADDRESS, expectedAmountBigInt], method: 'transfer', abi: erc20ABI, chainNamespace: ConstantsUtil.CHAIN.EVM }); }); test('should throw if parsed amount is not bigint', async () => { vi.mocked(ConnectionController.parseUnits).mockReturnValue(undefined); await expect(processEvmErc20Payment(paymentAsset, MOCK_FROM_ADDRESS)).rejects.toThrow(new AppKitPayError(AppKitPayErrorCodes.GENERIC_PAYMENT_ERROR)); vi.mocked(ConnectionController.parseUnits).mockImplementation((amount, decimals) => BigInt(parseFloat(amount) * 10 ** decimals)); }); }); }); //# sourceMappingURL=PaymentUtil.test.js.map