ox
Version:
Ethereum Standard Library
1,834 lines (1,726 loc) • 60.6 kB
text/typescript
import {
Abi,
AbiEvent,
AbiFunction,
AbiItem,
AbiParameters,
Bytes,
type Hex,
} from 'ox'
import { describe, expect, test } from 'vp/test'
import {
erc20Abi,
seaportContractConfig,
wagmiContractConfig,
} from '../../../test/constants/abis.js'
import {
getAmbiguousTypes,
isArgOfType,
normalizeSignature,
} from '../internal/abiItem.js'
describe('format', () => {
test('default', () => {
const abiItem = AbiItem.from({
type: 'function',
name: 'approve',
stateMutability: 'nonpayable',
inputs: [
{
name: 'spender',
type: 'address',
},
{
name: 'amount',
type: 'uint256',
},
],
outputs: [{ type: 'bool' }],
})
const formatted = AbiItem.format(abiItem)
expect(formatted).toMatchInlineSnapshot(
`"function approve(address spender, uint256 amount) returns (bool)"`,
)
})
})
describe('from', () => {
test('default', () => {
{
const abiItem = AbiItem.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 = AbiItem.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 = AbiItem.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 = AbiItem.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(AbiItem.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(
AbiItem.fromAbi(wagmiContractConfig.abi, 'balanceOf', {
prepare: false,
}),
).toMatchInlineSnapshot(`
{
"inputs": [
{
"name": "owner",
"type": "address",
},
],
"name": "balanceOf",
"outputs": [
{
"name": "",
"type": "uint256",
},
],
"stateMutability": "view",
"type": "function",
}
`)
})
describe('behavior: data', () => {
test('function', () => {
const item = AbiItem.fromAbi(wagmiContractConfig.abi, 'approve')
const data = AbiFunction.encodeData(item, [
'0x0000000000000000000000000000000000000000',
1n,
])
expect(AbiItem.fromAbi(wagmiContractConfig.abi, data))
.toMatchInlineSnapshot(`
{
"hash": "0x095ea7b334ae44009aa867bfb386f5c3b4b443ac6f0ee573fa91c4608fbadfba",
"inputs": [
{
"name": "to",
"type": "address",
},
{
"name": "tokenId",
"type": "uint256",
},
],
"name": "approve",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function",
}
`)
})
test('event', () => {
const hash = AbiItem.getSignatureHash(
AbiItem.fromAbi(seaportContractConfig.abi, 'CounterIncremented'),
)
expect(AbiItem.fromAbi(seaportContractConfig.abi, hash))
.toMatchInlineSnapshot(`
{
"anonymous": false,
"hash": "0x721c20121297512b72821b97f5326877ea8ecf4bb9948fea5bfcb6453074d37f",
"inputs": [
{
"indexed": false,
"name": "newCounter",
"type": "uint256",
},
{
"indexed": true,
"name": "offerer",
"type": "address",
},
],
"name": "CounterIncremented",
"type": "event",
}
`)
})
test('error', () => {
const selector = AbiItem.getSelector(
AbiItem.fromAbi(seaportContractConfig.abi, 'BadSignatureV'),
)
expect(AbiItem.fromAbi(seaportContractConfig.abi, selector))
.toMatchInlineSnapshot(`
{
"hash": "0x1f003d0ab3c21a082e88d5c936eb366321476aa1508b9238066e9f135aa38772",
"inputs": [
{
"name": "v",
"type": "uint8",
},
],
"name": "BadSignatureV",
"type": "error",
}
`)
})
})
test('no matching name', () => {
expect(() =>
AbiItem.fromAbi([] as readonly unknown[], 'balanceOf', {
args: ['0x0000000000000000000000000000000000000000'],
}),
).toThrowErrorMatchingInlineSnapshot(
`[AbiItem.NotFoundError: ABI item with name "balanceOf" not found.]`,
)
})
test('no matching data', () => {
expect(() =>
AbiItem.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 = AbiItem.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 = AbiItem.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 = AbiItem.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(
AbiItem.fromAbi(abi, 'safeTransferFrom', {
args: shortArgs,
}),
).toMatchInlineSnapshot(shortSnapshot)
expect(
AbiItem.fromAbi(abi.reverse(), 'safeTransferFrom', {
args: shortArgs,
}),
).toMatchInlineSnapshot(shortSnapshot)
expect(
AbiItem.fromAbi(abi, 'safeTransferFrom', {
args: longArgs,
}),
).toMatchInlineSnapshot(longSnapshot)
expect(
AbiItem.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 = AbiItem.fromAbi(abi, 'mint')
expect(item).toMatchInlineSnapshot(`
{
"hash": "0x1249c58b84ff771f36a0d1d2bf0b42e48832b1567c4213f113d3990903cea57d",
"inputs": [],
"name": "mint",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function",
}
`)
const item_2 = AbiItem.fromAbi(abi, 'mint', {
args: [420n],
})
expect(item_2).toMatchInlineSnapshot(`
{
"hash": "0xa0712d680358d64f694230b7cc0b277c73686bdf768385d55cd7c547d0ffd30e",
"inputs": [
{
"type": "uint256",
},
],
"name": "mint",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function",
}
`)
const item_3 = AbiItem.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 = AbiItem.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(() =>
AbiItem.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(() =>
AbiItem.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(
AbiItem.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(
AbiItem.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(() =>
AbiItem.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(() =>
AbiItem.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(() =>
AbiItem.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.]
`)
})
describe('getAmbiguousTypes', () => {
test('string/address', () => {
expect(
getAmbiguousTypes(
AbiParameters.from('string'),
AbiParameters.from('address'),
['0xA0Cf798816D4b9b9866b5330EEa46a18382f2522'],
),
).toMatchInlineSnapshot(`
[
"string",
"address",
]
`)
expect(
getAmbiguousTypes(
AbiParameters.from('string'),
AbiParameters.from('address'),
// 21 bytes (invalid address)
['0xA0Cf798816D4b9b9866b5330EEa46a18382f252223'],
),
).toBeUndefined()
expect(
getAmbiguousTypes(
AbiParameters.from('(string)'),
AbiParameters.from('(address)'),
[['0xA0Cf798816D4b9b9866b5330EEa46a18382f2522']],
),
).toMatchInlineSnapshot(`
[
"string",
"address",
]
`)
expect(
getAmbiguousTypes(
AbiParameters.from('(address)'),
AbiParameters.from('(string)'),
[['0xA0Cf798816D4b9b9866b5330EEa46a18382f2522']],
),
).toMatchInlineSnapshot(`
[
"address",
"string",
]
`)
expect(
getAmbiguousTypes(
AbiParameters.from('uint, (string, (string))'),
AbiParameters.from('uint, (string, (address))'),
[69420n, ['lol', ['0xA0Cf798816D4b9b9866b5330EEa46a18382f2522']]],
),
).toMatchInlineSnapshot(`
[
"string",
"address",
]
`)
})
test('bytes/address', () => {
expect(
getAmbiguousTypes(
AbiParameters.from('address'),
AbiParameters.from('bytes20'),
['0xA0Cf798816D4b9b9866b5330EEa46a18382f251e'],
),
).toMatchInlineSnapshot(`
[
"address",
"bytes20",
]
`)
expect(
getAmbiguousTypes(
AbiParameters.from('bytes20'),
AbiParameters.from('address'),
['0xA0Cf798816D4b9b9866b5330EEa46a18382f251e'],
),
).toMatchInlineSnapshot(`
[
"bytes20",
"address",
]
`)
expect(
getAmbiguousTypes(
AbiParameters.from('address'),
AbiParameters.from('bytes'),
['0xA0Cf798816D4b9b9866b5330EEa46a18382f251e'],
),
).toMatchInlineSnapshot(`
[
"address",
"bytes",
]
`)
expect(
getAmbiguousTypes(
AbiParameters.from('bytes'),
AbiParameters.from('address'),
// 21 bytes (invalid address)
['0xA0Cf798816D4b9b9866b5330EEa46a18382f251eee'],
),
).toBeUndefined()
expect(
getAmbiguousTypes(
AbiParameters.from('bytes'),
AbiParameters.from('address'),
['0xA0Cf798816D4b9b9866b5330EEa46a18382f251e'],
),
).toMatchInlineSnapshot(`
[
"bytes",
"address",
]
`)
expect(
getAmbiguousTypes(
AbiParameters.from('(address)'),
AbiParameters.from('(bytes20)'),
[['0xA0Cf798816D4b9b9866b5330EEa46a18382f251e']],
),
).toMatchInlineSnapshot(`
[
"address",
"bytes20",
]
`)
expect(
getAmbiguousTypes(
AbiParameters.from('uint256, (address)'),
AbiParameters.from('uint128, (bytes20)'),
[69420n, ['0xA0Cf798816D4b9b9866b5330EEa46a18382f251e']],
),
).toMatchInlineSnapshot(`
[
"address",
"bytes20",
]
`)
expect(
getAmbiguousTypes(
AbiParameters.from('uint256, (string, (address))'),
AbiParameters.from('uint128, (string, (bytes20))'),
[69420n, ['foo', ['0xA0Cf798816D4b9b9866b5330EEa46a18382f251e']]],
),
).toMatchInlineSnapshot(`
[
"address",
"bytes20",
]
`)
expect(
getAmbiguousTypes(
AbiParameters.from('uint256, (string, (address, bytes))'),
AbiParameters.from('uint128, (string, (bytes20, address))'),
[
123n,
[
'foo',
[
'0xA0Cf798816D4b9b9866b5330EEa46a18382f251e',
'0xA0Cf798816D4b9b9866b5330EEa46a18382f251e',
],
],
],
),
).toMatchInlineSnapshot(`
[
"address",
"bytes20",
]
`)
})
})
describe.each([
// array
{ arg: ['foo'], abiParameter: { type: 'string[]' }, expected: true },
{ arg: ['foo'], abiParameter: { type: 'string[1]' }, expected: true },
{ arg: [['foo']], abiParameter: { type: 'string[][]' }, expected: true },
{ arg: [['foo']], abiParameter: { type: 'string[][1]' }, expected: true },
{
arg: [1n],
abiParameter: { type: 'uint256[]' },
expected: true,
},
{
arg: [{ foo: 1n, bar: [{ baz: 1n }] }],
abiParameter: {
type: 'tuple[]',
components: [
{ name: 'foo', type: 'uint256' },
{
name: 'bar',
type: 'tuple[]',
components: [{ name: 'baz', type: 'uint256' }],
},
],
},
expected: true,
},
{ arg: ['foo'], abiParameter: { type: 'string[test]' }, expected: false },
{ arg: [1], abiParameter: { type: 'uint69[]' }, expected: false },
// address
{
arg: '0xA0Cf798816D4b9b9866b5330EEa46a18382f251e',
abiParameter: { type: 'address' },
expected: true,
},
{
arg: 'A0Cf798816D4b9b9866b5330EEa46a18382f251e',
abiParameter: { type: 'address' },
expected: false,
},
{ arg: 'test', abiParameter: { type: 'address' }, expected: false },
// bool
{ arg: true, abiParameter: { type: 'bool' }, expected: true },
{ arg: false, abiParameter: { type: 'bool' }, expected: true },
{ arg: 'true', abiParameter: { type: 'bool' }, expected: false },
// bytes
{ arg: 'foo', abiParameter: { type: 'bytes' }, expected: true },
{ arg: 'foo', abiParameter: { type: 'bytes32' }, expected: true },
{
arg: Bytes.fromString('foo'),
abiParameter: { type: 'bytes' },
expected: true,
},
{ arg: 1, abiParameter: { type: 'bytes32' }, expected: false },
// function
{ arg: 'foo', abiParameter: { type: 'function' }, expected: true },
{ arg: 1, abiParameter: { type: 'function' }, expected: false },
// int
{ arg: 1, abiParameter: { type: 'int' }, expected: true },
{ arg: 1n, abiParameter: { type: 'int' }, expected: true },
{ arg: 1n, abiParameter: { type: 'int' }, expected: true },
{ arg: 1, abiParameter: { type: 'uint' }, expected: true },
{ arg: 1n, abiParameter: { type: 'uint' }, expected: true },
{ arg: 1n, abiParameter: { type: 'uint' }, expected: true },
{ arg: 1, abiParameter: { type: 'int256' }, expected: true },
{ arg: 1, abiParameter: { type: 'uint256' }, expected: true },
{ arg: 1, abiParameter: { type: 'int69' }, expected: false },
{ arg: 1, abiParameter: { type: 'uint69' }, expected: false },
// string
{ arg: 'foo', abiParameter: { type: 'string' }, expected: true },
{ arg: 1, abiParameter: { type: 'string' }, expected: false },
// tuple
{
arg: { bar: 1, baz: 'test' },
abiParameter: {
name: 'foo',
type: 'tuple',
components: [
{ name: 'bar', type: 'uint256' },
{ name: 'baz', type: 'string' },
],
},
expected: true,
},
{
arg: [1, 'test'],
abiParameter: {
name: 'foo',
type: 'tuple',
components: [
{ name: 'bar', type: 'uint256' },
{ name: 'baz', type: 'string' },
],
},
expected: true,
},
{
arg: { bar: ['test'] },
abiParameter: {
name: 'foo',
type: 'tuple',
components: [
{
name: 'bar',
type: 'tuple',
components: [{ name: 'baz', type: 'string' }],
},
],
},
expected: true,
},
{
arg: {},
abiParameter: {
name: 'foo',
type: 'tuple',
components: [
{ name: 'bar', type: 'uint256' },
{ name: 'baz', type: 'uint256' },
],
},
expected: false,
},
] as {
arg: unknown
abiParameter: AbiParameters.Parameter
expected: boolean
}[])(
'isArgOfType($arg, $abiParameter)',
({ arg, abiParameter, expected }) => {
test(`isArgOfType: returns ${expected}`, () => {
expect(isArgOfType(arg, abiParameter)).toEqual(expected)
})
},
)
})
describe('getSelector', () => {
test('creates function signature', () => {
expect(AbiItem.getSelector('_compound(uint256,uint256,uint256)')).toEqual(
'0xf4fbb312',
)
expect(
AbiItem.getSelector(
'function _compound(uint256 a, uint256 b, uint256 c)',
),
).toEqual('0xf4fbb312')
expect(AbiItem.getSelector('function ownerOf(uint256 tokenId)')).toEqual(
'0x6352211e',
)
expect(AbiItem.getSelector('ownerOf(uint256)')).toEqual('0x6352211e')
expect(
AbiItem.getSelector('processInvestment(address,uint256,bool)'),
).toEqual('0xcf4b8f61')
expect(AbiItem.getSelector('processAccount(uint256 , address )')).toEqual(
'0x73933128',
)
expect(AbiItem.getSelector('claimed()')).toEqual('0xe834a834')
expect(AbiItem.getSelector('function claimed()')).toEqual('0xe834a834')
})
test('creates function signature from `AbiFunction`', () => {
expect(
AbiItem.getSelector({
name: '_compound',
type: 'function',
inputs: [
{ name: 'a', type: 'uint256' },
{ name: 'b', type: 'uint256' },
{ name: 'c', type: 'uint256' },
],
outputs: [],
stateMutability: 'nonpayable',
}),
).toEqual('0xf4fbb312')
expect(
AbiItem.getSelector({
name: '_compound',
type: 'function',
inputs: [
{ name: '', type: 'uint256' },
{ name: '', type: 'uint256' },
{ name: '', type: 'uint256' },
],
outputs: [],
stateMutability: 'nonpayable',
}),
).toEqual('0xf4fbb312')
expect(
AbiItem.getSelector({
name: 'ownerOf',
type: 'function',
inputs: [{ name: 'tokenId', type: 'uint256' }],
outputs: [],
stateMutability: 'view',
}),
).toEqual('0x6352211e')
expect(
AbiItem.getSelector({
name: 'ownerOf',
type: 'function',
inputs: [{ name: '', type: 'uint256' }],
outputs: [],
stateMutability: 'view',
}),
).toEqual('0x6352211e')
expect(
AbiItem.getSelector({
name: 'processInvestment',
type: 'function',
inputs: [
{ name: '', type: 'address' },
{ name: '', type: 'uint256' },
{ name: '', type: 'bool' },
],
outputs: [],
stateMutability: 'nonpayable',
}),
).toEqual('0xcf4b8f61')
expect(
AbiItem.getSelector({
name: 'processAccount',
type: 'function',
inputs: [
{ name: '', type: 'uint256' },
{ name: '', type: 'address' },
],
outputs: [],
stateMutability: 'nonpayable',
}),
).toEqual('0x73933128')
expect(
AbiItem.getSelector({
name: 'claimed',
type: 'function',
inputs: [],
outputs: [],
stateMutability: 'view',
}),
).toEqual('0xe834a834')
expect(
AbiItem.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')
})
})
describe('getSignature', () => {
test('creates function signature', () => {
expect(AbiItem.getSignature('_compound(uint256,uint256,uint256)')).toEqual(
'_compound(uint256,uint256,uint256)',
)
expect(
AbiItem.getSignature(
'function _compound(uint256 a, uint256 b, uint256 c)',
),
).toEqual('_compound(uint256,uint256,uint256)')
expect(AbiItem.getSignature('function ownerOf(uint256 tokenId)')).toEqual(
'ownerOf(uint256)',
)
expect(AbiItem.getSignature('ownerOf(uint256)')).toEqual('ownerOf(uint256)')
expect(
AbiItem.getSignature('processInvestment(address,uint256,bool)'),
).toEqual('processInvestment(address,uint256,bool)')
expect(AbiItem.getSignature('processAccount(uint256 , address)')).toEqual(
'processAccount(uint256,address)',
)
expect(AbiItem.getSignature('claimed()')).toEqual('claimed()')
expect(AbiItem.getSignature('function claimed()')).toEqual('claimed()')
expect(AbiItem.getSignature(erc20Abi, 'approve')).toEqual(
'approve(address,uint256)',
)
})
test('creates function signature from `AbiFunction`', () => {
expect(
AbiItem.getSignature({
name: '_compound',
type: 'function',
inputs: [
{ name: 'a', type: 'uint256' },
{ name: 'b', type: 'uint256' },
{ name: 'c', type: 'uint256' },
],
outputs: [],
stateMutability: 'nonpayable',
}),
).toEqual('_compound(uint256,uint256,uint256)')
expect(
AbiItem.getSignature({
name: 'ownerOf',
type: 'function',
inputs: [{ name: 'tokenId', type: 'uint256' }],
outputs: [],
stateMutability: 'view',
}),
).toEqual('ownerOf(uint256)')
expect(
AbiItem.getSignature({
name: 'ownerOf',
type: 'function',
inputs: [{ name: '', type: 'uint256' }],
outputs: [],
stateMutability: 'view',
}),
).toEqual('ownerOf(uint256)')
expect(
AbiItem.getSignature({
name: 'processInvestment',
type: 'function',
inputs: [
{ name: '', type: 'address' },
{ name: '', type: 'uint256' },
{ name: '', type: 'bool' },
],
outputs: [],
stateMutability: 'nonpayable',
}),
).toEqual('processInvestment(address,uint256,bool)')
expect(
AbiItem.getSignature({
name: 'processAccount',
type: 'function',
inputs: [
{ name: '', type: 'uint256' },
{ name: '', type: 'address' },
],
outputs: [],
stateMutability: 'nonpayable',
}),
).toEqual('processAccount(uint256,address)')
expect(
AbiItem.getSignature({
name: 'claimed',
type: 'function',
inputs: [],
outputs: [],
stateMutability: 'view',
}),
).toEqual('claimed()')
expect(
AbiItem.getSignature({
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('forceSimpleCells((uint64,address,uint8,uint8)[])')
expect(
AbiItem.getSignature({
inputs: [
{ name: 'name', type: 'string' },
{ name: 'symbol', type: 'string' },
{ name: 'editionSize', type: 'uint64' },
{ name: 'royaltyBPS', type: 'uint16' },
{
name: 'fundsRecipient',
type: 'address',
},
{ name: 'defaultAdmin', type: 'address' },
{
components: [
{
name: 'publicSalePrice',
type: 'uint104',
},
{
name: 'maxSalePurchasePerAddress',
type: 'uint32',
},
{ name: 'publicSaleStart', type: 'uint64' },
{ name: 'publicSaleEnd', type: 'uint64' },
{ name: 'presaleStart', type: 'uint64' },
{ name: 'presaleEnd', type: 'uint64' },
{
name: 'presaleMerkleRoot',
type: 'bytes32',
},
],
name: 'saleConfig',
type: 'tuple',
},
{ name: 'description', type: 'string' },
{ name: 'animationURI', type: 'string' },
{ name: 'imageURI', type: 'string' },
],
name: 'createEdition',
outputs: [{ name: '', type: 'address' }],
stateMutability: 'nonpayable',
type: 'function',
}),
).toEqual(
'createEdition(string,string,uint64,uint16,address,address,(uint104,uint32,uint64,uint64,uint64,uint64,bytes32),string,string,string)',
)
expect(
AbiItem.getSignature({
inputs: [
{
name: 't',
type: 'address',
},
{
name: 'ah',
type: 'address',
},
{
name: '_owner',
type: 'address',
},
{
components: [
{
name: 'maxBid',
type: 'uint256',
},
{
name: 'minBid',
type: 'uint256',
},
{
name: 'bidWindow',
type: 'uint256',
},
{
name: 'tip',
type: 'uint256',
},
{
name: 'receiver',
type: 'address',
},
],
name: 'cfg',
type: 'tuple',
},
],
name: 'clone',
outputs: [
{
name: '',
type: 'address',
},
],
stateMutability: 'payable',
type: 'function',
}),
).toEqual(
'clone(address,address,address,(uint256,uint256,uint256,uint256,address))',
)
expect(
AbiItem.getSignature({
inputs: [
{ name: 'payer', type: 'address' },
{ name: 'recipient', type: 'address' },
{ name: 'tokenAmount', type: 'uint256' },
{ name: 'tokenAddress', type: 'address' },
{ name: 'startTime', type: 'uint256' },
{ name: 'stopTime', type: 'uint256' },
{ name: 'nonce', type: 'uint8' },
],
name: 'createStream',
outputs: [{ name: 'stream', type: 'address' }],
stateMutability: 'nonpayable',
type: 'function',
}),
).toEqual(
'createStream(address,address,uint256,address,uint256,uint256,uint8)',
)
})
test('creates event signature', () => {
expect(
AbiItem.getSignature('Transfer(address,address,uint256)'),
).toMatchInlineSnapshot('"Transfer(address,address,uint256)"')
expect(
AbiItem.getSignature(
'Transfer(address indexed from, address indexed to, uint256 amount)',
),
).toMatchInlineSnapshot('"Transfer(address,address,uint256)"')
expect(
AbiItem.getSignature(
'event Transfer(address indexed from, address indexed to, uint256 amount)',
),
).toMatchInlineSnapshot('"Transfer(address,address,uint256)"')
expect(AbiItem.getSignature('drawNumber()')).toMatchInlineSnapshot(
'"drawNumber()"',
)
expect(AbiItem.getSignature('drawNumber( )')).toMatchInlineSnapshot(
'"drawNumber()"',
)
expect(
AbiItem.getSignature(
'ProcessedAccountDividendTracker(uint256,uint256,uint256,uint256,bool,uint256,address)',
),
).toMatchInlineSnapshot(
'"ProcessedAccountDividendTracker(uint256,uint256,uint256,uint256,bool,uint256,address)"',
)
expect(
AbiItem.getSignature(
'ProcessedAccountDividendTracker(uint256 indexed foo, uint256 indexed bar, uint256 baz, uint256 a, bool b, uint256 c, address d)',
),
).toMatchInlineSnapshot(
'"ProcessedAccountDividendTracker(uint256,uint256,uint256,uint256,bool,uint256,address)"',
)
expect(
AbiItem.getSignature('BlackListMultipleAddresses(address[], bool)'),
).toMatchInlineSnapshot('"BlackListMultipleAddresses(address[],bool)"')
expect(AbiItem.getSignature('checkBatch(bytes)')).toMatchInlineSnapshot(
'"checkBatch(bytes)"',
)
expect(
AbiItem.getSignature(
'Approval(address indexed owner, address indexed approved, uint256 indexed tokenId)',
),
).toMatchInlineSnapshot('"Approval(address,address,uint256)"')
expect(
AbiItem.getSignature(
'ApprovalForAll(address indexed owner, address indexed operator, bool approved)',
),
).toMatchInlineSnapshot('"ApprovalForAll(address,address,bool)"')
})
test('creates event signature for `AbiEvent`', () => {
expect(
AbiItem.getSignature({
name: 'Transfer',
type: 'event',
inputs: [
{ name: 'address', type: 'address', indexed: true },
{ name: 'address', type: 'address', indexed: true },
{ name: 'uint256', type: 'uint256', indexed: false },
],
}),
).toMatchInlineSnapshot('"Transfer(address,address,uint256)"')
expect(
AbiItem.getSignature({
name: 'Transfer',
type: 'event',
inputs: [
{ name: 'from', type: 'address', indexed: true },
{ name: 'to', type: 'address', indexed: true },
{ name: 'amount', type: 'uint256', indexed: false },
],
}),
).toMatchInlineSnapshot('"Transfer(address,address,uint256)"')
expect(
AbiItem.getSignature({
name: 'drawNumber',
type: 'event',
inputs: [],
}),
).toMatchInlineSnapshot('"drawNumber()"')
expect(
AbiItem.getSignature({
name: 'drawNumber',
type: 'event',
inputs: [],
}),
).toMatchInlineSnapshot('"drawNumber()"')
expect(
AbiItem.getSignature({
name: 'ProcessedAccountDividendTracker',
type: 'event',
inputs: [
{ name: 'uint256', type: 'uint256', indexed: false },
{ name: 'uint256', type: 'uint256', indexed: false },
{ name: 'uint256', type: 'uint256', indexed: false },
{ name: 'uint256', type: 'uint256', indexed: false },
{ name: 'bool', type: 'bool', indexed: false },
{ name: 'uint256', type: 'uint256', indexed: false },
{ name: 'address', type: 'address', indexed: false },
],
}),
).toMatchInlineSnapshot(
'"ProcessedAccountDividendTracker(uint256,uint256,uint256,uint256,bool,uint256,address)"',
)
expect(
AbiItem.getSignature({
name: 'ProcessedAccountDividendTracker',
type: 'event',
inputs: [
{ name: 'foo', type: 'uint256', indexed: true },
{ name: 'bar', type: 'uint256', indexed: true },
{ name: 'baz', type: 'uint256', indexed: false },
{ name: 'a', type: 'uint256', indexed: false },
{ name: 'b', type: 'bool', indexed: false },
{ name: 'c', type: 'uint256', indexed: false },
{ name: 'd', type: 'address', indexed: false },
],
}),
).toMatchInlineSnapshot(
'"ProcessedAccountDividendTracker(uint256,uint256,uint256,uint256,bool,uint256,address)"',
)
expect(
AbiItem.getSignature({
name: 'BlackListMultipleAddresses',
type: 'event',
inputs: [
{ name: 'addresses', type: 'address[]', indexed: false },
{ name: 'isBlackListed', type: 'bool', indexed: false },
],
}),
).toMatchInlineSnapshot('"BlackListMultipleAddresses(address[],bool)"')
expect(
AbiItem.getSignature({
name: 'checkBatch',
type: 'event',
inputs: [{ name: '', type: 'bytes', indexed: false }],
}),
).toMatchInlineSnapshot('"checkBatch(bytes)"')
expect(
AbiItem.getSignature({
name: 'Approval',
type: 'event',
inputs: [
{ name: 'owner', type: 'address', indexed: true },
{ name: 'approved', type: 'address', indexed: true },
{ name: 'tokenId', type: 'uint256', indexed: true },
],
}),
).toMatchInlineSnapshot('"Approval(address,address,uint256)"')
expect(
AbiItem.getSignature({
name: 'ApprovalForAll',
type: 'event',
inputs: [
{ name: 'owner', type: 'address', indexed: true },
{ name: 'operator', type: 'address', indexed: true },
{ name: 'approved', type: 'bool', indexed: false },
],
}),
).toMatchInlineSnapshot('"ApprovalForAll(address,address,bool)"')
expect(
AbiItem.getSignature({
anonymous: false,
inputs: [
{
indexed: false,
name: 'smolRecipeId',
type: 'uint256',
},
{
components: [
{
components: [
{
name: 'background',
type: 'uint24',
},
{ name: 'body', type: 'uint24' },
{ name: 'clothes', type: 'uint24' },
{ name: 'mouth', type: 'uint24' },
{ name: 'glasses', type: 'uint24' },
{ name: 'hat', type: 'uint24' },
{ name: 'hair', type: 'uint24' },
{ name: 'skin', type: 'uint24' },
{ name: 'gender', type: 'uint8' },
{ name: 'headSize', type: 'uint8' },
],
name: 'smol',
type: 'tuple',
},
{ name: 'exists', type: 'bool' },
{
name: 'smolInputAmount',
type: 'uint8',
},
],
indexed: false,
name: 'smolData',
type: 'tuple',
},
],
name: 'SmolRecipeAdded',
type: 'event',
}),
).toMatchInlineSnapshot(
'"SmolRecipeAdded(uint256,((uint24,uint24,uint24,uint24,uint24,uint24,uint24,uint24,uint8,uint8),bool,uint8))"',
)
})
describe('normalizeSignature', () => {
test('foo()', () => {
expect(normalizeSignature('foo()')).toBe('foo()')
})
test('bar(uint foo)', () => {
expect(normalizeSignature('bar(uint foo)')).toBe('bar(uint)')
})
test('processAccount(uint256 , address )', () => {
expect(normalizeSignature('processAccount(uint256 , address )')).toBe(
'processAccount(uint256,address)',
)
})
test('function bar()', () => {
expect(normalizeSignature('function bar()')).toBe('bar()')
})
test('function bar()', () => {
expect(normalizeSignature('function bar( )')).toBe('bar()')
})
test('event Bar()', () => {
expect(normalizeSignature('event Bar()')).toBe('Bar()')
})
test('function bar(uint foo)', () => {
expect(normalizeSignature('function bar(uint foo)')).toBe('bar(uint)')
})
test('function bar(uint foo, address baz)', () => {
expect(normalizeSignature('function bar(uint foo, address baz)')).toBe(
'bar(uint,address)',
)
})
test('event Barry(uint foo)', () => {
expect(normalizeSignature('event Barry(uint foo)')).toBe('Barry(uint)')
})
test('Barry(uint indexed foo)', () => {
expect(normalizeSignature('Barry(uint indexed foo)')).toBe('Barry(uint)')
})
test('event Barry(uint indexed foo)', () => {
expect(normalizeSignature('event Barry(uint indexed foo)')).toBe(
'Barry(uint)',
)
})
test('function _compound(uint256 a, uint256 b, uint256 c)', () => {
expect(
normalizeSignature(
'function _compound(uint256 a, uint256 b, uint256 c)',
),
).toBe('_compound(uint256,uint256,uint256)')
})
test('bar(uint foo, (uint baz))', () => {
expect(normalizeSignature('bar(uint foo, (uint baz))')).toBe(
'bar(uint,(uint))',
)
})
test('bar(uint foo, (uint baz, bool x))', () => {
expect(normalizeSignature('bar(uint foo, (uint baz, bool x))')).toBe(
'bar(uint,(uint,bool))',
)
})
test('bar(uint foo, (uint baz, bool x) foo)', () => {
expect(normalizeSignature('bar(uint foo, (uint baz, bool x) foo)')).toBe(
'bar(uint,(uint,bool))',
)
})
test('bar(uint[] foo, (uint baz, bool x))', () => {
expect(normalizeSignature('bar(uint[] foo, (uint baz, bool x))')).toBe(
'bar(uint[],(uint,bool))',
)
})
test('bar(uint[] foo, (uint baz, bool x)[])', () => {
expect(normalizeSignature('bar(uint[] foo, (uint baz, bool x)[])')).toBe(
'bar(uint[],(uint,bool)[])',
)
})
test('foo(uint bar)', () => {
expect(normalizeSignature('foo(uint bar) payable returns (uint)')).toBe(
'foo(uint)',
)
})
test('function submitBlocksWithCallbacks(bool isDataCompressed, bytes calldata data, ((uint