ox
Version:
Ethereum Standard Library
1,273 lines (1,190 loc) • 37.1 kB
text/typescript
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",
]
`)
})