import { Hex } from 'ox' import { encodeAbiParameters, encodeFunctionData, parseAbi, parseAbiParameters } from 'viem' import * as Provider from '../Provider.js' import { uniswap, validateApprovalResponse, validateExactSourceMaximum, validateExactSourceMinimum, validateRouterCalldata, } from './uniswap.js' const routerAbi = parseAbi([ 'function execute(bytes commands, bytes[] inputs)', 'function execute(bytes commands, bytes[] inputs, uint256 deadline)', ]) const execute = (commands: `0x${string}`, inputs: readonly `0x${string}`[]) => encodeFunctionData({ abi: routerAbi, args: [commands, inputs], functionName: 'execute', }) describe('uniswap', () => { test('creates an exchange provider', () => { expect(uniswap({ apiKey: 'uniswap-key' })).toMatchObject({ id: 'uniswap', name: 'Uniswap', type: 'exchange', }) }) test('rejects a blank API key', () => { expect(() => uniswap({ apiKey: ' ' })).toThrow(Provider.ProviderConfigurationError) }) }) describe('validateApprovalResponse', () => { test('accepts hex transaction values', () => { expect( validateApprovalResponse({ approval: { chainId: 4217, data: '0x1234', from: `0x${'11'.repeat(20)}`, to: `0x${'22'.repeat(20)}`, value: '0x0', }, cancel: null, }), ).toMatchInlineSnapshot(` { "approval": { "chainId": 4217, "data": "0x1234", "from": "0x1111111111111111111111111111111111111111", "to": "0x2222222222222222222222222222222222222222", "value": "0x0", }, "cancel": null, } `) }) }) describe('validateRouterCalldata', () => { test('accepts swap commands and nested plans', () => { const nested = encodeAbiParameters(parseAbiParameters('bytes, bytes[]'), ['0x00', ['0x']]) expect(() => validateRouterCalldata(execute('0x0021', ['0x', nested]))).not.toThrow() }) test('accepts supported V4 swap actions', () => { const plan = encodeAbiParameters(parseAbiParameters('bytes, bytes[]'), ['0x060e', ['0x', '0x']]) expect(() => validateRouterCalldata(execute('0x10', [plan]))).not.toThrow() }) test('rejects unsupported commands and mismatched inputs', () => { expect(() => validateRouterCalldata(execute('0x11', ['0x']))).toThrow( Provider.ProviderPayloadError, ) expect(() => validateRouterCalldata(execute(Hex.fromBytes(new Uint8Array([0x00])), [])), ).toThrow(Provider.ProviderPayloadError) }) }) describe('validateExactSourceMaximum', () => { test('accepts an absent or equal maximum', () => { expect(() => validateExactSourceMaximum({ amount: '100' })).not.toThrow() expect(() => validateExactSourceMaximum({ amount: '100', maximumAmount: '100' })).not.toThrow() }) test('rejects a larger or smaller maximum', () => { expect(() => validateExactSourceMaximum({ amount: '100', maximumAmount: '101' })).toThrow( Provider.ProviderPayloadError, ) expect(() => validateExactSourceMaximum({ amount: '100', maximumAmount: '99' })).toThrow( Provider.ProviderPayloadError, ) }) }) describe('validateExactSourceMinimum', () => { test('accepts the integer slippage floor', () => { expect(() => validateExactSourceMinimum({ amount: '101', minimumAmount: '100', slippageBps: 50 }), ).not.toThrow() }) test('rejects missing, excessive, or insufficient minimums', () => { expect(() => validateExactSourceMinimum({ amount: '101', slippageBps: 50 })).toThrow( Provider.ProviderPayloadError, ) expect(() => validateExactSourceMinimum({ amount: '101', minimumAmount: '99', slippageBps: 50 }), ).toThrow(Provider.ProviderPayloadError) expect(() => validateExactSourceMinimum({ amount: '101', minimumAmount: '102', slippageBps: 50 }), ).toThrow(Provider.ProviderPayloadError) }) })