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

Support tuple type #84

Merged
merged 1 commit into from
Oct 19, 2022
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
6 changes: 3 additions & 3 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,20 @@ repos:
- id: isort

- repo: https://github.com/ambv/black
rev: 22.3.0
rev: 22.10.0
hooks:
- id: black
exclude: venv

- repo: https://github.com/pre-commit/mirrors-mypy
rev: v0.942
rev: v0.982
hooks:
- id: mypy
entry: mypy web3_input_decoder/
pass_filenames: false

- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.2.0
rev: v4.3.0
hooks:
- id: trailing-whitespace
- id: check-yaml
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ If you have lots of inputs in the same contract to decode, consider using [`Inpu
>>> for _ in range(10000):
>>> decoder.decode_function(
(
"0xa9059cbb000000000000000000000000f050227be1a7ce587aa83d5013f900dbc3be"
"0611000000000000000000000000000000000000000000000000000000000ecdd350"
"0xa9059cbb000000000000000000000000f050227be1a7ce587aa83d5013f900dbc3b"
"e0611000000000000000000000000000000000000000000000000000000000ecdd350"
),
)
```
Expand Down
107 changes: 74 additions & 33 deletions poetry.lock

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion tests/data/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +0,0 @@

13 changes: 13 additions & 0 deletions tests/data/defi.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,16 @@
]

ROUTER_SWAP_CALL_INPUT = "0xa2a1623d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000006ef4158bf7304b966929945248927fb400ece8b500000000000000000000000000000000000000000000000000000000622bc5e10000000000000000000000000000000000000000000000000000000000000002000000000000000000000000b31f66aa3c1e785363f0875a1b74e27b85fd66c70000000000000000000000003df307e8e9a897da488211682430776cdf0f17cc"
ROUTER_SWAP_CALL_ARGUMENT = [
("uint256", "amountOutMin", 0),
(
"address[]",
"path",
(
"0xb31f66aa3c1e785363f0875a1b74e27b85fd66c7",
"0x3df307e8e9a897da488211682430776cdf0f17cc",
),
),
("address", "to", "0x6ef4158bf7304b966929945248927fb400ece8b5"),
("uint256", "deadline", 1647035873),
]
144 changes: 144 additions & 0 deletions tests/data/nft.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
# Contract: 0x00000000006c3852cbef3e08e8df289169ede581
SEAPORT_ABI = [
{
"inputs": [
{
"components": [
{
"internalType": "address",
"name": "considerationToken",
"type": "address",
},
{
"internalType": "uint256",
"name": "considerationIdentifier",
"type": "uint256",
},
{
"internalType": "uint256",
"name": "considerationAmount",
"type": "uint256",
},
{
"internalType": "address payable",
"name": "offerer",
"type": "address",
},
{"internalType": "address", "name": "zone", "type": "address"},
{
"internalType": "address",
"name": "offerToken",
"type": "address",
},
{
"internalType": "uint256",
"name": "offerIdentifier",
"type": "uint256",
},
{
"internalType": "uint256",
"name": "offerAmount",
"type": "uint256",
},
{
"internalType": "enum BasicOrderType",
"name": "basicOrderType",
"type": "uint8",
},
{"internalType": "uint256", "name": "startTime", "type": "uint256"},
{"internalType": "uint256", "name": "endTime", "type": "uint256"},
{"internalType": "bytes32", "name": "zoneHash", "type": "bytes32"},
{"internalType": "uint256", "name": "salt", "type": "uint256"},
{
"internalType": "bytes32",
"name": "offererConduitKey",
"type": "bytes32",
},
{
"internalType": "bytes32",
"name": "fulfillerConduitKey",
"type": "bytes32",
},
{
"internalType": "uint256",
"name": "totalOriginalAdditionalRecipients",
"type": "uint256",
},
{
"components": [
{
"internalType": "uint256",
"name": "amount",
"type": "uint256",
},
{
"internalType": "address payable",
"name": "recipient",
"type": "address",
},
],
"internalType": "struct AdditionalRecipient[]",
"name": "additionalRecipients",
"type": "tuple[]",
},
{"internalType": "bytes", "name": "signature", "type": "bytes"},
],
"internalType": "struct BasicOrderParameters",
"name": "parameters",
"type": "tuple",
}
],
"name": "fulfillBasicOrder",
"outputs": [{"internalType": "bool", "name": "fulfilled", "type": "bool"}],
"stateMutability": "payable",
"type": "function",
},
]

# TX: 0xa139231454fd021dd227a94fff6a1b6260890bb95e5f5bf8517af36e228575e6
SEAPORT_FULFILL_ORDER_CALL_INPUT = (
"0xfb0f3ee1000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000"
"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
"00000000000000000000000000000000000000000003b53d9d99ecb800000000000000000000000000001850dd8fb9323b01c34"
"0d0eb1da1ec16cc8ee1a2000000000000000000000000004c00500000ad104d7dbd00e3ae0a5c00560c00000000000000000000"
"000000bc4ca0eda7647a8ab7c2061c2e118a18a936f13d000000000000000000000000000000000000000000000000000000000"
"0000561000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000"
"00000000000000000000000000000002000000000000000000000000000000000000000000000000000000006346e1d20000000"
"0000000000000000000000000000000000000000000000000636fadcd0000000000000000000000000000000000000000000000"
"000000000000000000360c6ebe00000000000000000000000000000000000000000589c7ee474bc5850000007b02230091a7ed0"
"1230072f7006a004d60a8d4e71d599b8104250f00000000007b02230091a7ed01230072f7006a004d60a8d4e71d599b8104250f"
"0000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000"
"0000000000000000000000000024000000000000000000000000000000000000000000000000000000000000002e00000000000"
"0000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000001"
"8fae27693b400000000000000000000000000000000a26b00c1f0df003000390027140000faa719000000000000000000000000"
"00000000000000000000000018fae27693b40000000000000000000000000000a858ddc0445d8131dac4d1de01f834ffcba52ef"
"10000000000000000000000000000000000000000000000000000000000000041046bd0fda5b934a96ef4700da1b64e03e7451e"
"6a6ee45a5004b93823ff3baae34b9f1c4f667781b5fedd27dc67339d4fd3e4ae2a873b315090e6312853732f9a1c00000000000"
"000000000000000000000000000000000000000000000000000360c6ebe"
)
SEAPORT_FULFILL_ORDER_CALL_ARGUMENT = [
(
"(address,uint256,uint256,address,address,address,uint256,uint256,uint8,uint256,uint256,bytes32,uint256,bytes32,bytes32,uint256,(uint256,address),bytes)",
"parameters",
(
"0x0000000000000000000000000000000000000000",
0,
68400000000000000000,
"0x1850dd8fb9323b01c340d0eb1da1ec16cc8ee1a2",
"0x004c00500000ad104d7dbd00e3ae0a5c00560c00",
"0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d",
1377,
1,
2,
1665589714,
1668263373,
b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
24446860302761739304752683030156737591518664810215442929800781758303463785861,
b"\x00\x00\x00{\x02#\x00\x91\xa7\xed\x01#\x00r\xf7\x00j\x00M`\xa8\xd4\xe7\x1dY\x9b\x81\x04%\x0f\x00\x00",
b"\x00\x00\x00{\x02#\x00\x91\xa7\xed\x01#\x00r\xf7\x00j\x00M`\xa8\xd4\xe7\x1dY\x9b\x81\x04%\x0f\x00\x00",
2,
(576, "0x00000000000000000000000000000000000002e0"),
b"",
),
)
]
135 changes: 53 additions & 82 deletions tests/test_decode.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
import pytest

from web3_input_decoder import InputDecoder, decode_constructor, decode_function
from web3_input_decoder.exceptions import InputDataError
from web3_input_decoder import decode_constructor, decode_function

from .data.caller import (
CALLER_CONSTRUCTOR_CALL_ARGUMENT,
CALLER_CONSTRUCTOR_CALL_INPUT,
CALLER_CONTRACT_ABI,
)
from .data.defi import ROUTER_ABI, ROUTER_SWAP_CALL_INPUT
from .data.defi import ROUTER_ABI, ROUTER_SWAP_CALL_ARGUMENT, ROUTER_SWAP_CALL_INPUT
from .data.example import (
EXAMPLE_CONSTRUCTOR_CALL_ARGUMENT,
EXAMPLE_CONSTRUCTOR_CALL_INPUT,
EXAMPLE_CONTRACT_ABI,
EXAMPLE_CONTRACT_BYTECODE,
)
from .data.nft import (
SEAPORT_ABI,
SEAPORT_FULFILL_ORDER_CALL_ARGUMENT,
SEAPORT_FULFILL_ORDER_CALL_INPUT,
)
from .data.tether import (
TETHER_ABI,
TETHER_BYTECODE,
Expand All @@ -25,90 +27,59 @@


def test_decode_function():
assert decode_function(
TETHER_ABI,
(
"0xa9059cbb000000000000000000000000f050227be1a7ce587aa83d5013f900dbc3be"
"0611000000000000000000000000000000000000000000000000000000000ecdd350"
),
) == [
tether_input = (
"0xa9059cbb000000000000000000000000f050227be1a7ce587aa83d5013f900dbc3b"
"e0611000000000000000000000000000000000000000000000000000000000ecdd350"
)
tether_args = [
("address", "_to", "0xf050227be1a7ce587aa83d5013f900dbc3be0611"),
("uint256", "_value", 248370000),
]

with pytest.raises(InputDataError, match="Specified method is not found in ABI"):
decode_function(TETHER_ABI, "0x00000000")

assert decode_function(ROUTER_ABI, ROUTER_SWAP_CALL_INPUT) == [
("uint256", "amountOutMin", 0),
(
"address[]",
"path",
(
"0xb31f66aa3c1e785363f0875a1b74e27b85fd66c7",
"0x3df307e8e9a897da488211682430776cdf0f17cc",
),
),
("address", "to", "0x6ef4158bf7304b966929945248927fb400ece8b5"),
("uint256", "deadline", 1647035873),
abis = [
TETHER_ABI,
ROUTER_ABI,
SEAPORT_ABI,
]
inputs = [
tether_input,
ROUTER_SWAP_CALL_INPUT,
SEAPORT_FULFILL_ORDER_CALL_INPUT,
]
expected_args = [
tether_args,
ROUTER_SWAP_CALL_ARGUMENT,
SEAPORT_FULFILL_ORDER_CALL_ARGUMENT,
]

for abi, input, expected in zip(abis, inputs, expected_args):
assert decode_function(abi, input) == expected

def test_decode_constructor():
assert (
decode_constructor(TETHER_ABI, TETHER_CONSTRUCTOR_TX_INPUT)
== TETHER_CONSTRUCTOR_ARGS
)

assert (
decode_constructor(
TETHER_ABI,
TETHER_CONSTRUCTOR_TX_INPUT_WITH_BYTECODE,
TETHER_BYTECODE,
)
== TETHER_CONSTRUCTOR_ARGS
)

assert (
decode_constructor(
EXAMPLE_CONTRACT_ABI,
EXAMPLE_CONSTRUCTOR_CALL_INPUT,
EXAMPLE_CONTRACT_BYTECODE,
)
== EXAMPLE_CONSTRUCTOR_CALL_ARGUMENT
)
assert (
decode_constructor(
CALLER_CONTRACT_ABI,
CALLER_CONSTRUCTOR_CALL_INPUT,
)
== CALLER_CONSTRUCTOR_CALL_ARGUMENT
)

with pytest.raises(
InputDataError, match="Unable to detect arguments including array"
):
decode_constructor(EXAMPLE_CONTRACT_ABI, EXAMPLE_CONSTRUCTOR_CALL_INPUT)

with pytest.raises(InputDataError, match="Constructor is not found in ABI"):
decode_constructor([{"type": "function", "name": "test"}], "0x00")

with pytest.raises(InputDataError, match="Specified method is not found in ABI"):
decode_function([{"type": "function", "name": "test"}], "0x00")

def test_decode_constructor():
abis = [TETHER_ABI, CALLER_CONTRACT_ABI]
inputs = [TETHER_CONSTRUCTOR_TX_INPUT, CALLER_CONSTRUCTOR_CALL_INPUT]
expected_args = [TETHER_CONSTRUCTOR_ARGS, CALLER_CONSTRUCTOR_CALL_ARGUMENT]
for abi, input, expected in zip(abis, inputs, expected_args):
assert decode_constructor(abi, input) == expected

def test_performance():
from pyinstrument import Profiler

p = Profiler()
with p:
decoder = InputDecoder(TETHER_ABI)
for _ in range(10000):
func_call = decoder.decode_function(
(
"0xa9059cbb000000000000000000000000f050227be1a7ce587aa83d5013f900dbc3be"
"0611000000000000000000000000000000000000000000000000000000000ecdd350"
),
)
assert func_call.name == "transfer"
p.print()
def test_decode_constructor_with_bytecode():
abis = [
TETHER_ABI,
EXAMPLE_CONTRACT_ABI,
]
inputs = [
TETHER_CONSTRUCTOR_TX_INPUT_WITH_BYTECODE,
EXAMPLE_CONSTRUCTOR_CALL_INPUT,
]
bytecodes = [
TETHER_BYTECODE,
EXAMPLE_CONTRACT_BYTECODE,
]
expected_args = [
TETHER_CONSTRUCTOR_ARGS,
EXAMPLE_CONSTRUCTOR_CALL_ARGUMENT,
]
for abi, input, bytecode, expected in zip(abis, inputs, bytecodes, expected_args):
assert decode_constructor(abi, input, bytecode) == expected
25 changes: 25 additions & 0 deletions tests/test_decode_error.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import pytest

from web3_input_decoder import decode_constructor, decode_function
from web3_input_decoder.exceptions import InputDataError

from .data.example import EXAMPLE_CONSTRUCTOR_CALL_INPUT, EXAMPLE_CONTRACT_ABI
from .data.tether import TETHER_ABI


def test_decode_constructor_error():
with pytest.raises(
InputDataError, match="Unable to detect arguments including array"
):
decode_constructor(EXAMPLE_CONTRACT_ABI, EXAMPLE_CONSTRUCTOR_CALL_INPUT)

with pytest.raises(InputDataError, match="Constructor is not found in ABI"):
decode_constructor([{"type": "function", "name": "test"}], "0x00")


def test_decode_function_error():
with pytest.raises(InputDataError, match="Specified method is not found in ABI"):
decode_function(TETHER_ABI, "0x00000000")

with pytest.raises(InputDataError, match="Specified method is not found in ABI"):
decode_function([{"type": "function", "name": "test"}], "0x00")
Loading