Skip to content

Commit

Permalink
fix: type-level human-readable abi item parameters/scope/state mutabi…
Browse files Browse the repository at this point in the history
…lity parsing (#244)

* fix: human-readable abi item nested tuple parsing

* chore: format

---------

Co-authored-by: tmm <tmm@users.noreply.github.com>
  • Loading branch information
tmm and tmm authored Jun 28, 2024
1 parent f35e72e commit 8a5e5dc
Show file tree
Hide file tree
Showing 5 changed files with 220 additions and 6 deletions.
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

0 comments on commit 8a5e5dc

Please sign in to comment.