Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: type-level human-readable abi item parameters/scope/state mutability parsing #244

Merged
merged 2 commits into from
Jun 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/plenty-pandas-kneel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"abitype": patch
---

Fixed human-readable ABI item parsing with nested tuple parameters.
9 changes: 9 additions & 0 deletions packages/abitype/src/human-readable/parseAbiItem.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,3 +156,12 @@ test('parseAbiItem', () => {
const signature: string = 'function foo()'
expectTypeOf(parseAbiItem(signature)).toEqualTypeOf<Abi[number]>()
})

test('nested tuples', () => {
const formattedAbiItem =
'function stepChanges((uint256 characterID, uint64 newPosition, uint24 xp, uint24 epoch, uint8 hp, (int32 x, int32 y, uint8 hp, uint8 kind)[5] monsters, (uint8 monsterIndexPlus1, uint8 attackCardsUsed1, uint8 attackCardsUsed2, uint8 defenseCardsUsed1, uint8 defenseCardsUsed2) battle) stateChanges, uint256 action, bool revetOnInvalidMoves) pure returns ((uint256 characterID, uint64 newPosition, uint24 xp, uint24 epoch, uint8 hp, (int32 x, int32 y, uint8 hp, uint8 kind)[5] monsters, (uint8 monsterIndexPlus1, uint8 attackCardsUsed1, uint8 attackCardsUsed2, uint8 defenseCardsUsed1, uint8 defenseCardsUsed2) battle))'

const abiItem = parseAbiItem(formattedAbiItem)
expectTypeOf(abiItem.stateMutability).toEqualTypeOf<'pure'>()
expectTypeOf(abiItem.inputs.length).toEqualTypeOf<3>()
})
173 changes: 173 additions & 0 deletions packages/abitype/src/human-readable/parseAbiItem.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,3 +87,176 @@ test.each([
])('parseAbiItem($signature)', ({ signature, expected }) => {
expect(parseAbiItem(signature)).toEqual(expected)
})

test('nested tuples', () => {
const formattedAbiItem =
'function stepChanges((uint256 characterID, uint64 newPosition, uint24 xp, uint24 epoch, uint8 hp, (int32 x, int32 y, uint8 hp, uint8 kind)[5] monsters, (uint8 monsterIndexPlus1, uint8 attackCardsUsed1, uint8 attackCardsUsed2, uint8 defenseCardsUsed1, uint8 defenseCardsUsed2) battle) stateChanges, uint256 action, bool revetOnInvalidMoves) pure returns ((uint256 characterID, uint64 newPosition, uint24 xp, uint24 epoch, uint8 hp, (int32 x, int32 y, uint8 hp, uint8 kind)[5] monsters, (uint8 monsterIndexPlus1, uint8 attackCardsUsed1, uint8 attackCardsUsed2, uint8 defenseCardsUsed1, uint8 defenseCardsUsed2) battle))'
expect(parseAbiItem(formattedAbiItem)).toMatchInlineSnapshot(
`
{
"inputs": [
{
"components": [
{
"name": "characterID",
"type": "uint256",
},
{
"name": "newPosition",
"type": "uint64",
},
{
"name": "xp",
"type": "uint24",
},
{
"name": "epoch",
"type": "uint24",
},
{
"name": "hp",
"type": "uint8",
},
{
"components": [
{
"name": "x",
"type": "int32",
},
{
"name": "y",
"type": "int32",
},
{
"name": "hp",
"type": "uint8",
},
{
"name": "kind",
"type": "uint8",
},
],
"name": "monsters",
"type": "tuple[5]",
},
{
"components": [
{
"name": "monsterIndexPlus1",
"type": "uint8",
},
{
"name": "attackCardsUsed1",
"type": "uint8",
},
{
"name": "attackCardsUsed2",
"type": "uint8",
},
{
"name": "defenseCardsUsed1",
"type": "uint8",
},
{
"name": "defenseCardsUsed2",
"type": "uint8",
},
],
"name": "battle",
"type": "tuple",
},
],
"name": "stateChanges",
"type": "tuple",
},
{
"name": "action",
"type": "uint256",
},
{
"name": "revetOnInvalidMoves",
"type": "bool",
},
],
"name": "stepChanges",
"outputs": [
{
"components": [
{
"name": "characterID",
"type": "uint256",
},
{
"name": "newPosition",
"type": "uint64",
},
{
"name": "xp",
"type": "uint24",
},
{
"name": "epoch",
"type": "uint24",
},
{
"name": "hp",
"type": "uint8",
},
{
"components": [
{
"name": "x",
"type": "int32",
},
{
"name": "y",
"type": "int32",
},
{
"name": "hp",
"type": "uint8",
},
{
"name": "kind",
"type": "uint8",
},
],
"name": "monsters",
"type": "tuple[5]",
},
{
"components": [
{
"name": "monsterIndexPlus1",
"type": "uint8",
},
{
"name": "attackCardsUsed1",
"type": "uint8",
},
{
"name": "attackCardsUsed2",
"type": "uint8",
},
{
"name": "defenseCardsUsed1",
"type": "uint8",
},
{
"name": "defenseCardsUsed2",
"type": "uint8",
},
],
"name": "battle",
"type": "tuple",
},
],
"type": "tuple",
},
],
"stateMutability": "pure",
"type": "function",
}
`,
)
})
14 changes: 14 additions & 0 deletions packages/abitype/src/human-readable/types/utils.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -676,6 +676,20 @@ test('_ParseFunctionParametersAndStateMutability', () => {
Inputs: 'string bar'
StateMutability: 'view'
}>()

expectTypeOf<
_ParseFunctionParametersAndStateMutability<'function foo(string bar, uint256) external view'>
>().toEqualTypeOf<{
Inputs: 'string bar, uint256'
StateMutability: 'view'
}>()

expectTypeOf<
_ParseFunctionParametersAndStateMutability<'function stepChanges((uint256 characterID, uint64 newPosition, uint24 xp, uint24 epoch, uint8 hp, (int32 x, int32 y, uint8 hp, uint8 kind)[5] monsters, (uint8 monsterIndexPlus1, uint8 attackCardsUsed1, uint8 attackCardsUsed2, uint8 defenseCardsUsed1, uint8 defenseCardsUsed2) battle) stateChanges, uint256 action, bool revetOnInvalidMoves) pure returns ((uint256 characterID, uint64 newPosition, uint24 xp, uint24 epoch, uint8 hp, (int32 x, int32 y, uint8 hp, uint8 kind)[5] monsters, (uint8 monsterIndexPlus1, uint8 attackCardsUsed1, uint8 attackCardsUsed2, uint8 defenseCardsUsed1, uint8 defenseCardsUsed2) battle))'>
>().toEqualTypeOf<{
Inputs: '(uint256 characterID, uint64 newPosition, uint24 xp, uint24 epoch, uint8 hp, (int32 x, int32 y, uint8 hp, uint8 kind)[5] monsters, (uint8 monsterIndexPlus1, uint8 attackCardsUsed1, uint8 attackCardsUsed2, uint8 defenseCardsUsed1, uint8 defenseCardsUsed2) battle) stateChanges, uint256 action, bool revetOnInvalidMoves'
StateMutability: 'pure'
}>()
})

test('_ParseTuple', () => {
Expand Down
25 changes: 19 additions & 6 deletions packages/abitype/src/human-readable/types/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -255,13 +255,26 @@ export type _ParseFunctionParametersAndStateMutability<
| `${Scope} ${AbiStateMutability}`}`
? {
Inputs: parameters
StateMutability: scopeOrStateMutability extends `${Scope} ${infer stateMutability extends AbiStateMutability}`
? stateMutability
: scopeOrStateMutability extends AbiStateMutability
? scopeOrStateMutability
: 'nonpayable'
StateMutability: _ParseStateMutability<scopeOrStateMutability>
}
: never
: signature extends `function ${string}(${infer tail}`
? _UnwrapNameOrModifier<tail> extends {
nameOrModifier: infer scopeOrStateMutability extends string
End: infer parameters
}
? {
Inputs: parameters
StateMutability: _ParseStateMutability<scopeOrStateMutability>
}
: never
: never

type _ParseStateMutability<signature extends string> =
signature extends `${Scope} ${infer stateMutability extends AbiStateMutability}`
? stateMutability
: signature extends AbiStateMutability
? signature
: 'nonpayable'

type _ParseConstructorParametersAndStateMutability<signature extends string> =
signature extends `constructor(${infer parameters}) payable`
Expand Down