UNPKG

ox

Version:

Ethereum Standard Library

1,273 lines (1,190 loc) 37.1 kB
import { Abi, AbiFunction, AbiParameters } from 'ox' import { describe, expect, test } from 'vp/test' import { erc20Abi, wagmiContractConfig } from '../../../test/constants/abis.js' import { address } from '../../../test/constants/addresses.js' import { anvilMainnet } from '../../../test/prool.js' describe('decodeData', () => { test('default', () => { const abiItem = AbiFunction.fromAbi(erc20Abi, 'decimals') const data = AbiFunction.encodeData(abiItem) const input = AbiFunction.decodeData(abiItem, data) const input2 = AbiFunction.decodeData(erc20Abi, 'decimals', data) const input3 = AbiFunction.decodeData(erc20Abi, data) expect(input).toEqual(undefined) expect(input2).toEqual(undefined) expect(input3).toEqual(undefined) }) test('behavior: with data', () => { const abiItem = AbiFunction.fromAbi(erc20Abi, 'approve', { prepare: false, }) const data = AbiFunction.encodeData(abiItem, [address.vitalik, 1n]) const input = AbiFunction.decodeData(abiItem, data) const input2 = AbiFunction.decodeData(erc20Abi, 'approve', data) const input3 = AbiFunction.decodeData(erc20Abi, data) expect(input).toEqual(['0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045', 1n]) expect(input2).toEqual(['0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045', 1n]) expect(input3).toEqual(['0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045', 1n]) }) test('options: checksumAddress = false', () => { const abiItem = AbiFunction.fromAbi(erc20Abi, 'approve', { prepare: false, }) const data = AbiFunction.encodeData(abiItem, [address.vitalik, 1n]) const input = AbiFunction.decodeData(abiItem, data, { checksumAddress: false, }) const input2 = AbiFunction.decodeData(erc20Abi, 'approve', data, { checksumAddress: false, }) const input3 = AbiFunction.decodeData(erc20Abi, data, { checksumAddress: false, }) expect(input).toEqual([address.vitalik, 1n]) expect(input2).toEqual([address.vitalik, 1n]) expect(input3).toEqual([address.vitalik, 1n]) }) test('behavior: with overloads', () => { const abi = Abi.from([ { inputs: [{ type: 'bytes' }], name: 'balanceOf', outputs: [{ type: 'uint256' }], stateMutability: 'view', type: 'function', }, { inputs: [{ name: 'x', type: 'uint256' }], name: 'balanceOf', outputs: [{ name: '', type: 'uint256' }], stateMutability: 'view', type: 'function', }, ]) const abiItem = AbiFunction.fromAbi(abi, 'balanceOf') expect( AbiFunction.decodeData( abiItem, '0x9cc7f7080000000000000000000000000000000000000000000000000000000000000001', ), ).toEqual([1n]) expect( AbiFunction.decodeData( abi, '0x9cc7f7080000000000000000000000000000000000000000000000000000000000000001', ), ).toEqual([1n]) expect( AbiFunction.decodeData( abiItem, '0x7841536500000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000004deadbeef00000000000000000000000000000000000000000000000000000000', ), ).toEqual(['0xdeadbeef']) expect( AbiFunction.decodeData( abi, '0x7841536500000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000004deadbeef00000000000000000000000000000000000000000000000000000000', ), ).toEqual(['0xdeadbeef']) }) }) describe('decodeResult', () => { test('default', () => { const abiItem = AbiFunction.from( 'function test() returns (uint a, (uint x, string y) b)', ) const args = [420n, { x: 420n, y: 'lol' }] as const const result = AbiFunction.decodeResult( abiItem, AbiParameters.encode(abiItem.outputs, args), ) expect(result).toMatchInlineSnapshot(` [ 420n, { "x": 420n, "y": "lol", }, ] `) }) test('behavior: single output parameter', () => { const abiItem = AbiFunction.from('function test() returns (uint a)') const args = [420n] as const const result = AbiFunction.decodeResult( abiItem, AbiParameters.encode(abiItem.outputs, args), ) expect(result).toMatchInlineSnapshot('420n') }) test('options: checksumAddress = false', () => { const abiItem = AbiFunction.from('function owner() returns (address)') const data = AbiParameters.encode(abiItem.outputs, [address.vitalik]) expect(AbiFunction.decodeResult(abiItem, data)).toMatchInlineSnapshot( `"0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"`, ) expect( AbiFunction.decodeResult(abiItem, data, { checksumAddress: false }), ).toMatchInlineSnapshot(`"0xd8da6bf26964af9d7eed9e03e53415d37aa96045"`) }) test('behavior: no output parameter', () => { const abiItem = AbiFunction.from('function test()') const result = AbiFunction.decodeResult(abiItem, '0x') expect(result).toEqual(undefined) }) test('options: as = Object', () => { const abiItem = AbiFunction.from( 'function test() returns (uint a, (uint x, string y) b)', ) const args = [420n, { x: 420n, y: 'lol' }] as const const result = AbiFunction.decodeResult( abiItem, AbiParameters.encode(abiItem.outputs, args), { as: 'Object' }, ) expect(result).toMatchInlineSnapshot(` { "a": 420n, "b": { "x": 420n, "y": "lol", }, } `) }) test('options: as = Object, behavior: single output parameter', () => { const abiItem = AbiFunction.from('function test() returns (uint a)') const args = [420n] as const const result = AbiFunction.decodeResult( abiItem, AbiParameters.encode(abiItem.outputs, args), { as: 'Object' }, ) expect(result).toMatchInlineSnapshot('420n') }) test('options: as = Object, behavior: no output parameter', () => { const abiItem = AbiFunction.from('function test()') const result = AbiFunction.decodeResult(abiItem, '0x', { as: 'Object', }) expect(result).toMatchInlineSnapshot('undefined') }) }) describe('encodeData', () => { test('default', () => { { const abiFunction = AbiFunction.fromAbi(erc20Abi, 'decimals') expect(AbiFunction.encodeData(abiFunction)).toEqual('0x313ce567') } AbiFunction.encodeData(erc20Abi, 'decimals') }) test('behavior: abiFunction not prepared', () => { const abiFunction = AbiFunction.fromAbi(erc20Abi, 'decimals', { prepare: false, }) expect(AbiFunction.encodeData(abiFunction)).toEqual('0x313ce567') }) test('behavior: with data', () => { { const abiFunction = AbiFunction.fromAbi(erc20Abi, 'approve', { prepare: false, }) expect( AbiFunction.encodeData(abiFunction, [address.vitalik, 1n]), ).toEqual( '0x095ea7b3000000000000000000000000d8da6bf26964af9d7eed9e03e53415d37aa960450000000000000000000000000000000000000000000000000000000000000001', ) } expect( AbiFunction.encodeData(erc20Abi, 'approve', [address.vitalik, 1n]), ).toEqual( '0x095ea7b3000000000000000000000000d8da6bf26964af9d7eed9e03e53415d37aa960450000000000000000000000000000000000000000000000000000000000000001', ) }) test('behavior: with overloads', () => { const abi = Abi.from([ { inputs: [{ type: 'bytes' }], name: 'balanceOf', outputs: [{ type: 'uint256' }], stateMutability: 'view', type: 'function', }, { inputs: [{ name: 'x', type: 'uint256' }], name: 'balanceOf', outputs: [{ name: '', type: 'uint256' }], stateMutability: 'view', type: 'function', }, ]) const abiFunction = AbiFunction.fromAbi(abi, 'balanceOf') expect(AbiFunction.encodeData(abiFunction, [1n])).toEqual( '0x9cc7f7080000000000000000000000000000000000000000000000000000000000000001', ) expect(AbiFunction.encodeData(abiFunction, ['0xdeadbeef'])).toEqual( '0x7841536500000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000004deadbeef00000000000000000000000000000000000000000000000000000000', ) expect(AbiFunction.encodeData(abi, 'balanceOf', [1n])).toEqual( '0x9cc7f7080000000000000000000000000000000000000000000000000000000000000001', ) expect(AbiFunction.encodeData(abi, 'balanceOf', ['0xdeadbeef'])).toEqual( '0x7841536500000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000004deadbeef00000000000000000000000000000000000000000000000000000000', ) }) }) describe('encodeResult', () => { test('default', () => { const abiItem = AbiFunction.from( 'function test() returns (uint a, (uint x, string y) b)', ) const output = [420n, { x: 420n, y: 'lol' }] as const const result = AbiFunction.encodeResult(abiItem, output) expect(AbiFunction.decodeResult(abiItem, result)).toEqual(output) expect(result).toMatchInlineSnapshot( `"0x00000000000000000000000000000000000000000000000000000000000001a4000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001a4000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000036c6f6c0000000000000000000000000000000000000000000000000000000000"`, ) }) test('behavior: single param', () => { const abiItem = AbiFunction.from( 'function test() returns ((uint x, string y) b)', ) const output = { x: 420n, y: 'lol' } as const const result = AbiFunction.encodeResult(abiItem, output) expect(AbiFunction.decodeResult(abiItem, result)).toEqual(output) expect(result).toMatchInlineSnapshot( `"0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001a4000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000036c6f6c0000000000000000000000000000000000000000000000000000000000"`, ) }) test('options: as = Object', () => { const abiItem = AbiFunction.from( 'function test() returns (uint a, (uint x, string y) b)', ) const output = { a: 420n, b: { x: 420n, y: 'lol' } } as const const result = AbiFunction.encodeResult(abiItem, output, { as: 'Object' }) expect(AbiFunction.decodeResult(abiItem, result, { as: 'Object' })).toEqual( output, ) expect(result).toMatchInlineSnapshot( `"0x00000000000000000000000000000000000000000000000000000000000001a4000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001a4000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000036c6f6c0000000000000000000000000000000000000000000000000000000000"`, ) }) test('options: as = Object, behavior: single param', () => { const abiItem = AbiFunction.from( 'function test() returns ((uint x, string y) b)', ) const output = { x: 420n, y: 'lol' } as const const result = AbiFunction.encodeResult(abiItem, output, { as: 'Object' }) expect(AbiFunction.decodeResult(abiItem, result, { as: 'Object' })).toEqual( output, ) expect(result).toMatchInlineSnapshot( `"0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001a4000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000036c6f6c0000000000000000000000000000000000000000000000000000000000"`, ) }) test('behavior: overload with abi', () => { const abi = Abi.from([ 'function test() returns (uint a, (uint x, string y) b)', ]) const output = [420n, { x: 420n, y: 'lol' }] as const // Test both overload patterns const abiItem = AbiFunction.fromAbi(abi, 'test') const result1 = AbiFunction.encodeResult(abiItem, output) const result2 = AbiFunction.encodeResult(abi, 'test', output) expect(result1).toEqual(result2) expect(AbiFunction.decodeResult(abi, 'test', result2)).toEqual(output) expect(result2).toMatchInlineSnapshot( `"0x00000000000000000000000000000000000000000000000000000000000001a4000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001a4000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000036c6f6c0000000000000000000000000000000000000000000000000000000000"`, ) }) test('behavior: overload with abi and options', () => { const abi = Abi.from([ 'function test() returns (uint a, (uint x, string y) b)', ]) const output = { a: 420n, b: { x: 420n, y: 'lol' } } as const // Test both overload patterns with options const abiItem = AbiFunction.fromAbi(abi, 'test') const result1 = AbiFunction.encodeResult(abiItem, output, { as: 'Object' }) const result2 = AbiFunction.encodeResult(abi, 'test', output, { as: 'Object', }) expect(result1).toEqual(result2) expect( AbiFunction.decodeResult(abi, 'test', result2, { as: 'Object' }), ).toEqual(output) expect(result2).toMatchInlineSnapshot( `"0x00000000000000000000000000000000000000000000000000000000000001a4000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001a4000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000036c6f6c0000000000000000000000000000000000000000000000000000000000"`, ) }) test('behavior: overload with erc20 abi', () => { const abiItem = AbiFunction.fromAbi(erc20Abi, 'totalSupply') const output = 69420n // Test both overload patterns const result1 = AbiFunction.encodeResult(abiItem, output) const result2 = AbiFunction.encodeResult(erc20Abi, 'totalSupply', output) expect(result1).toEqual(result2) expect(AbiFunction.decodeResult(erc20Abi, 'totalSupply', result2)).toEqual( output, ) expect(result2).toMatchInlineSnapshot( `"0x0000000000000000000000000000000000000000000000000000000000010f2c"`, ) }) }) describe('format', () => { test('default', () => { const approve = AbiFunction.from({ type: 'function', name: 'approve', stateMutability: 'nonpayable', inputs: [ { name: 'spender', type: 'address', }, { name: 'amount', type: 'uint256', }, ], outputs: [{ type: 'bool' }], }) const formatted = AbiFunction.format(approve) expect(formatted).toMatchInlineSnapshot( `"function approve(address spender, uint256 amount) returns (bool)"`, ) }) }) describe('from', () => { test('default', () => { { const abiItem = AbiFunction.from({ type: 'function', name: 'approve', stateMutability: 'nonpayable', inputs: [ { name: 'spender', type: 'address', }, { name: 'amount', type: 'uint256', }, ], outputs: [{ type: 'bool' }], }) expect(abiItem).toMatchInlineSnapshot(` { "hash": "0x095ea7b334ae44009aa867bfb386f5c3b4b443ac6f0ee573fa91c4608fbadfba", "inputs": [ { "name": "spender", "type": "address", }, { "name": "amount", "type": "uint256", }, ], "name": "approve", "outputs": [ { "type": "bool", }, ], "stateMutability": "nonpayable", "type": "function", } `) } { const abiItem = AbiFunction.from( 'function approve(address spender, uint256 amount) returns (bool)', ) expect(abiItem).toMatchInlineSnapshot(` { "hash": "0x095ea7b334ae44009aa867bfb386f5c3b4b443ac6f0ee573fa91c4608fbadfba", "inputs": [ { "name": "spender", "type": "address", }, { "name": "amount", "type": "uint256", }, ], "name": "approve", "outputs": [ { "type": "bool", }, ], "stateMutability": "nonpayable", "type": "function", } `) } { const abiItem = AbiFunction.from([ 'struct Foo { address owner; address spender; uint256 amount; }', 'function approve(Foo foo) returns (bool)', ]) expect(abiItem).toMatchInlineSnapshot(` { "hash": "0xe0ac316e47844d88260c3fd7bfe62c3348cee1a015524358c41abe32d0d91266", "inputs": [ { "components": [ { "name": "owner", "type": "address", }, { "name": "spender", "type": "address", }, { "name": "amount", "type": "uint256", }, ], "name": "foo", "type": "tuple", }, ], "name": "approve", "outputs": [ { "type": "bool", }, ], "stateMutability": "nonpayable", "type": "function", } `) } }) test('options: prepare', () => { const abiItem = AbiFunction.from( { type: 'function', name: 'approve', stateMutability: 'nonpayable', inputs: [ { name: 'spender', type: 'address', }, { name: 'amount', type: 'uint256', }, ], outputs: [{ type: 'bool' }], }, { prepare: false }, ) expect(abiItem).toMatchInlineSnapshot(` { "inputs": [ { "name": "spender", "type": "address", }, { "name": "amount", "type": "uint256", }, ], "name": "approve", "outputs": [ { "type": "bool", }, ], "stateMutability": "nonpayable", "type": "function", } `) }) }) describe('fromAbi', () => { test('default', () => { expect(AbiFunction.fromAbi(wagmiContractConfig.abi, 'balanceOf')) .toMatchInlineSnapshot(` { "hash": "0x70a08231b98ef4ca268c9cc3f6b4590e4bfec28280db06bb5d45e689f2a360be", "inputs": [ { "name": "owner", "type": "address", }, ], "name": "balanceOf", "outputs": [ { "name": "", "type": "uint256", }, ], "stateMutability": "view", "type": "function", } `) }) test('behavior: prepare = false', () => { expect( AbiFunction.fromAbi(wagmiContractConfig.abi, 'balanceOf', { args: ['0x0000000000000000000000000000000000000000'], prepare: false, }), ).toMatchInlineSnapshot(` { "inputs": [ { "name": "owner", "type": "address", }, ], "name": "balanceOf", "outputs": [ { "name": "", "type": "uint256", }, ], "stateMutability": "view", "type": "function", } `) }) test('behavior: data', () => { const item = AbiFunction.fromAbi(wagmiContractConfig.abi, 'approve') const data = AbiFunction.encodeData(item, [ '0x0000000000000000000000000000000000000000', 1n, ]) expect(AbiFunction.fromAbi(wagmiContractConfig.abi, data)) .toMatchInlineSnapshot(` { "hash": "0x095ea7b334ae44009aa867bfb386f5c3b4b443ac6f0ee573fa91c4608fbadfba", "inputs": [ { "name": "to", "type": "address", }, { "name": "tokenId", "type": "uint256", }, ], "name": "approve", "outputs": [], "stateMutability": "nonpayable", "type": "function", } `) }) test('error: no matching name', () => { expect(() => // @ts-expect-error AbiFunction.fromAbi(wagmiContractConfig.abi, 'Approval'), ).toThrowErrorMatchingInlineSnapshot( `[AbiItem.NotFoundError: ABI function with name "Approval" not found.]`, ) }) test('error: no matching name', () => { expect(() => AbiFunction.fromAbi([] as readonly unknown[], 'balanceOf', { args: ['0x0000000000000000000000000000000000000000'], }), ).toThrowErrorMatchingInlineSnapshot( `[AbiItem.NotFoundError: ABI item with name "balanceOf" not found.]`, ) }) test('error: no matching data', () => { expect(() => AbiFunction.fromAbi([], '0xdeadbeef'), ).toThrowErrorMatchingInlineSnapshot( `[AbiItem.NotFoundError: ABI item with name "0xdeadbeef" not found.]`, ) }) test('behavior: overloads', () => { const abi = Abi.from([ 'function bar()', 'function foo(bytes)', 'function foo(uint256)', ]) const item = AbiFunction.fromAbi(abi, 'foo') expect(item).toMatchInlineSnapshot(` { "hash": "0x30c8d1da93067416f4fed4bc024d665b120d7271f9d1000c7632a48d39765324", "inputs": [ { "type": "bytes", }, ], "name": "foo", "outputs": [], "overloads": [ { "inputs": [ { "type": "uint256", }, ], "name": "foo", "outputs": [], "stateMutability": "nonpayable", "type": "function", }, ], "stateMutability": "nonpayable", "type": "function", } `) }) test('behavior: overloads: no inputs', () => { const abi = Abi.from([ 'function bar()', 'function foo()', 'function foo(uint256)', ]) const item = AbiFunction.fromAbi(abi, 'foo') expect(item).toMatchInlineSnapshot(` { "hash": "0xc2985578b8f3b75f7dc66a767be2a4ef7d7c2224896a1c86e92ccf30bae678b7", "inputs": [], "name": "foo", "outputs": [], "stateMutability": "nonpayable", "type": "function", } `) }) test('overloads: no args', () => { const abi = Abi.from([ 'function bar()', 'function foo(uint256)', 'function foo()', ]) const item = AbiFunction.fromAbi(abi, 'foo') expect(item).toMatchInlineSnapshot(` { "hash": "0xc2985578b8f3b75f7dc66a767be2a4ef7d7c2224896a1c86e92ccf30bae678b7", "inputs": [], "name": "foo", "outputs": [], "stateMutability": "nonpayable", "type": "function", } `) }) test('behavior: overloads: different lengths without abi order define effect', () => { const abi = wagmiContractConfig.abi.filter( (abi) => abi.type === 'function' && abi.name === 'safeTransferFrom', ) const shortArgs = [ '0x0000000000000000000000000000000000000000', '0x0000000000000000000000000000000000000000', 420n, ] as const const shortSnapshot = ` { "hash": "0x42842e0eb38857a7775b4e7364b2775df7325074d088e7fb39590cd6281184ed", "inputs": [ { "name": "from", "type": "address", }, { "name": "to", "type": "address", }, { "name": "tokenId", "type": "uint256", }, ], "name": "safeTransferFrom", "outputs": [], "stateMutability": "nonpayable", "type": "function", } ` const longArgs = [ '0x0000000000000000000000000000000000000000', '0x0000000000000000000000000000000000000000', 420n, '0x0000000000000000000000000000000000000000', ] as const const longSnapshot = ` { "hash": "0xb88d4fde60196325a28bb7f99a2582e0b46de55b18761e960c14ad7a32099465", "inputs": [ { "name": "from", "type": "address", }, { "name": "to", "type": "address", }, { "name": "tokenId", "type": "uint256", }, { "name": "_data", "type": "bytes", }, ], "name": "safeTransferFrom", "outputs": [], "stateMutability": "nonpayable", "type": "function", } ` expect( AbiFunction.fromAbi(abi, 'safeTransferFrom', { args: shortArgs, }), ).toMatchInlineSnapshot(shortSnapshot) expect( AbiFunction.fromAbi(abi.reverse(), 'safeTransferFrom', { args: shortArgs, }), ).toMatchInlineSnapshot(shortSnapshot) expect( AbiFunction.fromAbi(abi, 'safeTransferFrom', { args: longArgs, }), ).toMatchInlineSnapshot(longSnapshot) expect( AbiFunction.fromAbi(abi.reverse(), 'safeTransferFrom', { args: longArgs, }), ).toMatchInlineSnapshot(longSnapshot) }) test('behavior: overloads: different types', () => { const abi = Abi.from([ 'function mint()', 'function mint(uint256)', 'function mint(string)', ]) const item = AbiFunction.fromAbi(abi, 'mint') expect(item).toMatchInlineSnapshot(` { "hash": "0x1249c58b84ff771f36a0d1d2bf0b42e48832b1567c4213f113d3990903cea57d", "inputs": [], "name": "mint", "outputs": [], "stateMutability": "nonpayable", "type": "function", } `) const item_2 = AbiFunction.fromAbi(abi, 'mint', { args: [420n], }) expect(item_2).toMatchInlineSnapshot(` { "hash": "0xa0712d680358d64f694230b7cc0b277c73686bdf768385d55cd7c547d0ffd30e", "inputs": [ { "type": "uint256", }, ], "name": "mint", "outputs": [], "stateMutability": "nonpayable", "type": "function", } `) const item_3 = AbiFunction.fromAbi(abi, 'mint', { args: ['foo'], }) expect(item_3).toMatchInlineSnapshot(` { "hash": "0xd85d3d2718c3cc9b33ff08eede5eea8b009f9d6e830a4fb9f651e3174175a5a1", "inputs": [ { "type": "string", }, ], "name": "mint", "outputs": [], "stateMutability": "nonpayable", "type": "function", } `) }) test('behavior: overloads: tuple', () => { const abi = Abi.from([ 'function foo(uint256 foo, (string a, string b, uint256 c) bar)', 'function foo(uint256 foo, (string a, (string merp, string meep) b, address c) bar)', ]) const item = AbiFunction.fromAbi(abi, 'foo', { args: [ 420n, { a: '0xA0Cf798816D4b9b9866b5330EEa46a18382f251e', b: { merp: 'test', meep: 'test' }, c: '0xA0Cf798816D4b9b9866b5330EEa46a18382f251e', }, ], }) expect(item).toMatchInlineSnapshot(` { "hash": "0x7994c7de29322475e8941809c4c1405aef16daab07153633b8863148687cb0c5", "inputs": [ { "name": "foo", "type": "uint256", }, { "components": [ { "name": "a", "type": "string", }, { "components": [ { "name": "merp", "type": "string", }, { "name": "meep", "type": "string", }, ], "name": "b", "type": "tuple", }, { "name": "c", "type": "address", }, ], "name": "bar", "type": "tuple", }, ], "name": "foo", "outputs": [], "stateMutability": "nonpayable", "type": "function", } `) }) test('behavior: overloads: ambiguious types', () => { expect(() => AbiFunction.fromAbi( Abi.from(['function foo(address)', 'function foo(bytes20)']), 'foo', { args: ['0xA0Cf798816D4b9b9866b5330EEa46a18382f251e'], }, ), ).toThrowErrorMatchingInlineSnapshot(` [AbiItem.AmbiguityError: Found ambiguous types in overloaded ABI Items. \`bytes20\` in \`foo(bytes20)\`, and \`address\` in \`foo(address)\` These types encode differently and cannot be distinguished at runtime. Remove one of the ambiguous items in the ABI.] `) expect(() => AbiFunction.fromAbi( Abi.from([ 'function foo(string)', 'function foo(uint)', 'function foo(address)', ]), 'foo', { args: ['0xA0Cf798816D4b9b9866b5330EEa46a18382f251e'], }, ), ).toThrowErrorMatchingInlineSnapshot(` [AbiItem.AmbiguityError: Found ambiguous types in overloaded ABI Items. \`address\` in \`foo(address)\`, and \`string\` in \`foo(string)\` These types encode differently and cannot be distinguished at runtime. Remove one of the ambiguous items in the ABI.] `) expect( AbiFunction.fromAbi( Abi.from([ 'function foo(string)', 'function foo(uint)', 'function foo(address)', ]), 'foo', { // 21 bytes (invalid address) args: ['0xA0Cf798816D4b9b9866b5330EEa46a18382f251eee'], }, ), ).toMatchInlineSnapshot(` { "hash": "0xf31a6969fc2f2e0b01964045ad21a28ad3ee38d276e1e6cf5b80124e63ba8190", "inputs": [ { "type": "string", }, ], "name": "foo", "outputs": [], "stateMutability": "nonpayable", "type": "function", } `) expect( AbiFunction.fromAbi( Abi.from([ 'function foo(string)', 'function foo(uint)', 'function foo(address)', ]), 'foo', { // non-hex (invalid address) args: ['0xA0Cf798816D4b9b9866b5330EEa46a18382f251z'], }, ), ).toMatchInlineSnapshot(` { "hash": "0xf31a6969fc2f2e0b01964045ad21a28ad3ee38d276e1e6cf5b80124e63ba8190", "inputs": [ { "type": "string", }, ], "name": "foo", "outputs": [], "stateMutability": "nonpayable", "type": "function", } `) expect(() => AbiFunction.fromAbi( Abi.from([ 'function foo(address)', 'function foo(uint)', 'function foo(string)', ]), 'foo', { args: ['0xA0Cf798816D4b9b9866b5330EEa46a18382f251e'], }, ), ).toThrowErrorMatchingInlineSnapshot(` [AbiItem.AmbiguityError: Found ambiguous types in overloaded ABI Items. \`string\` in \`foo(string)\`, and \`address\` in \`foo(address)\` These types encode differently and cannot be distinguished at runtime. Remove one of the ambiguous items in the ABI.] `) expect(() => AbiFunction.fromAbi( Abi.from(['function foo((address))', 'function foo((bytes20))']), 'foo', { args: [['0xA0Cf798816D4b9b9866b5330EEa46a18382f251e']], }, ), ).toThrowErrorMatchingInlineSnapshot(` [AbiItem.AmbiguityError: Found ambiguous types in overloaded ABI Items. \`bytes20\` in \`foo((bytes20))\`, and \`address\` in \`foo((address))\` These types encode differently and cannot be distinguished at runtime. Remove one of the ambiguous items in the ABI.] `) expect(() => AbiFunction.fromAbi( Abi.from([ 'function foo(string, (address))', 'function foo(string, (bytes))', ]), 'foo', { args: ['foo', ['0xA0Cf798816D4b9b9866b5330EEa46a18382f251e']], }, ), ).toThrowErrorMatchingInlineSnapshot(` [AbiItem.AmbiguityError: Found ambiguous types in overloaded ABI Items. \`bytes\` in \`foo(string,(bytes))\`, and \`address\` in \`foo(string,(address))\` These types encode differently and cannot be distinguished at runtime. Remove one of the ambiguous items in the ABI.] `) }) test.skip('behavior: network', async () => { const abi = Abi.from(wagmiContractConfig.abi) const totalSupplyItem = AbiFunction.fromAbi(abi, 'totalSupply') const totalSupply = await anvilMainnet .request({ method: 'eth_call', params: [ { data: AbiFunction.encodeData(totalSupplyItem), to: wagmiContractConfig.address, }, ], }) .then((data) => AbiFunction.decodeResult(totalSupplyItem, data)) expect(totalSupply).toEqual(648n) }) }) describe('getSelector', () => { test('creates function signature', () => { expect( AbiFunction.getSelector('_compound(uint256,uint256,uint256)'), ).toEqual('0xf4fbb312') expect( AbiFunction.getSelector( 'function _compound(uint256 a, uint256 b, uint256 c)', ), ).toEqual('0xf4fbb312') expect( AbiFunction.getSelector('function ownerOf(uint256 tokenId)'), ).toEqual('0x6352211e') expect(AbiFunction.getSelector('ownerOf(uint256)')).toEqual('0x6352211e') expect( AbiFunction.getSelector('processInvestment(address,uint256,bool)'), ).toEqual('0xcf4b8f61') expect( AbiFunction.getSelector('processAccount(uint256 , address )'), ).toEqual('0x73933128') expect(AbiFunction.getSelector('claimed()')).toEqual('0xe834a834') expect(AbiFunction.getSelector('function claimed()')).toEqual('0xe834a834') }) test('creates function signature from `AbiFunction`', () => { expect( AbiFunction.getSelector({ name: '_compound', type: 'function', inputs: [ { name: 'a', type: 'uint256' }, { name: 'b', type: 'uint256' }, { name: 'c', type: 'uint256' }, ], outputs: [], stateMutability: 'nonpayable', }), ).toEqual('0xf4fbb312') expect( AbiFunction.getSelector({ name: '_compound', type: 'function', inputs: [ { name: '', type: 'uint256' }, { name: '', type: 'uint256' }, { name: '', type: 'uint256' }, ], outputs: [], stateMutability: 'nonpayable', }), ).toEqual('0xf4fbb312') expect( AbiFunction.getSelector({ name: 'ownerOf', type: 'function', inputs: [{ name: 'tokenId', type: 'uint256' }], outputs: [], stateMutability: 'view', }), ).toEqual('0x6352211e') expect( AbiFunction.getSelector({ name: 'ownerOf', type: 'function', inputs: [{ name: '', type: 'uint256' }], outputs: [], stateMutability: 'view', }), ).toEqual('0x6352211e') expect( AbiFunction.getSelector({ name: 'processInvestment', type: 'function', inputs: [ { name: '', type: 'address' }, { name: '', type: 'uint256' }, { name: '', type: 'bool' }, ], outputs: [], stateMutability: 'nonpayable', }), ).toEqual('0xcf4b8f61') expect( AbiFunction.getSelector({ name: 'processAccount', type: 'function', inputs: [ { name: '', type: 'uint256' }, { name: '', type: 'address' }, ], outputs: [], stateMutability: 'nonpayable', }), ).toEqual('0x73933128') expect( AbiFunction.getSelector({ name: 'claimed', type: 'function', inputs: [], outputs: [], stateMutability: 'view', }), ).toEqual('0xe834a834') expect( AbiFunction.getSelector({ inputs: [ { components: [ { name: 'position', type: 'uint64', }, { name: 'owner', type: 'address', }, { name: 'color', type: 'uint8', }, { name: 'life', type: 'uint8', }, ], name: 'cells', type: 'tuple[]', }, ], name: 'forceSimpleCells', outputs: [], stateMutability: 'nonpayable', type: 'function', }), ).toEqual('0xd703f50a') }) }) test('exports', () => { expect(Object.keys(AbiFunction)).toMatchInlineSnapshot(` [ "decodeData", "decodeResult", "encodeData", "encodeResult", "format", "from", "fromAbi", "getSelector", ] `) })