├── demo ├── __init__.py ├── desc_order_events.py ├── historical_calls.py ├── live_calls.py ├── desc_order_calls.py ├── live_events.py └── benchmark.py ├── benchmark ├── __init__.py └── web3py_weth_events_benchmark.py ├── transpose ├── __init__.py ├── sql │ ├── __init__.py │ ├── general.py │ ├── events.py │ └── calls.py ├── stream │ ├── __init__.py │ ├── base.py │ ├── event.py │ └── call.py ├── utils │ ├── __init__.py │ ├── address.py │ ├── time.py │ ├── request.py │ ├── exceptions.py │ └── decode.py └── contract.py ├── requirements.txt ├── .gitignore ├── LICENSE ├── setup.py ├── abi ├── weth-abi.json └── opensea-seaport-abi.json └── README.md /demo/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /benchmark/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /transpose/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /transpose/sql/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /transpose/stream/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /transpose/utils/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | dateutils 2 | eth-event 3 | pip-chill 4 | web3 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | venv 2 | env 3 | __pycache__ 4 | .pyc 5 | dist 6 | transpose_decoding_sdk.egg-info 7 | test.py 8 | benchmark.py 9 | DEPLOY.md 10 | 11 | .idea 12 | build -------------------------------------------------------------------------------- /transpose/sql/general.py: -------------------------------------------------------------------------------- 1 | def latest_block_query(chain: str) -> str: 2 | """ 3 | Defines a SQL query that returns the latest block number. 4 | 5 | :param chain: The chain name. 6 | :return: The SQL query. 7 | """ 8 | 9 | return \ 10 | f""" 11 | SELECT block_number 12 | FROM {chain}.blocks 13 | ORDER BY block_number DESC 14 | LIMIT 1; 15 | """ -------------------------------------------------------------------------------- /transpose/utils/address.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | from web3 import Web3 3 | 4 | 5 | def to_checksum_address(address: str) -> Optional[str]: 6 | """ 7 | Convert an address to a checksum address. Will return None 8 | if the address is invalid. 9 | 10 | :param address: The address to convert. 11 | :return: The checksum address. 12 | """ 13 | 14 | try: return Web3.to_checksum_address(address) 15 | except: return None -------------------------------------------------------------------------------- /transpose/utils/time.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime, timezone 2 | from dateutil import parser 3 | 4 | 5 | def to_iso_timestamp(timestamp: str) -> datetime: 6 | """ 7 | Parses a timestamp string into a valid ISO-8601 timestamp. 8 | 9 | :param timestamp: The timestamp to parse. 10 | :return: The parsed timestamp. 11 | """ 12 | 13 | dt = parser.parse(timestamp) 14 | if dt.tzinfo is None: dt = dt.replace(tzinfo=timezone.utc) 15 | return dt.astimezone(timezone.utc) -------------------------------------------------------------------------------- /demo/desc_order_events.py: -------------------------------------------------------------------------------- 1 | from transpose.contract import TransposeDecodedContract 2 | 3 | 4 | def descending_events_demo(api_key: str) -> None: 5 | """ 6 | This demo shows how to stream all events to a contract in descending 7 | order by block number. 8 | 9 | :param api_key: Your Transpose API key. 10 | """ 11 | 12 | # initialize WETH contract 13 | contract = TransposeDecodedContract( 14 | contract_address='0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', 15 | abi_path='abi/weth-abi.json', 16 | chain='ethereum', 17 | api_key=api_key 18 | ) 19 | 20 | # build call stream 21 | stream = contract.stream_events( 22 | start_block=4753925, 23 | order='desc' 24 | ) 25 | 26 | # iterate over stream 27 | for event in stream: 28 | print('{} ({}, {})'.format( 29 | event['item']['event_name'], 30 | event['context']['block_number'], 31 | event['context']['log_index'] 32 | )) -------------------------------------------------------------------------------- /demo/historical_calls.py: -------------------------------------------------------------------------------- 1 | from transpose.contract import TransposeDecodedContract 2 | 3 | 4 | def historical_weth_calls_demo(api_key: str) -> None: 5 | """ 6 | This demo shows how to stream historical calls from the Wrapped 7 | Ether contract. To do so, it initializes a stream of all withdrawal 8 | calls from the Wrapped Ether contract, starting at block 15M. It 9 | then prints the next 10 calls to the console. 10 | 11 | :param api_key: Your Transpose API key. 12 | """ 13 | 14 | # initialize OpenSea Seaport contract 15 | contract = TransposeDecodedContract( 16 | contract_address='0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', 17 | abi_path='abi/weth-abi.json', 18 | chain='ethereum', 19 | api_key=api_key 20 | ) 21 | 22 | # build call stream 23 | stream = contract.stream_calls( 24 | function_name='withdraw', 25 | start_block=15000000 26 | ) 27 | 28 | # get first 10 calls 29 | print(stream.next(10)) 30 | -------------------------------------------------------------------------------- /demo/live_calls.py: -------------------------------------------------------------------------------- 1 | from transpose.contract import TransposeDecodedContract 2 | 3 | 4 | def live_seaport_calls_demo(api_key: str) -> None: 5 | """ 6 | This demo shows how to stream live calls from the OpenSea Seaport 7 | contract. To do so, it initializes a stream of all calls from the 8 | OpenSea Seaport contract, starting at the latest block. It then 9 | iterates over the stream and prints each call to the console as they 10 | are received the Transpose backend. 11 | 12 | :param api_key: Your Transpose API key. 13 | """ 14 | 15 | # initialize OpenSea Seaport contract 16 | contract = TransposeDecodedContract( 17 | contract_address='0x00000000006c3852cbEf3e08E8dF289169EdE581', 18 | abi_path='abi/opensea-seaport-abi.json', 19 | chain='ethereum', 20 | api_key=api_key 21 | ) 22 | 23 | # build call stream 24 | stream = contract.stream_calls( 25 | live_stream=True 26 | ) 27 | 28 | # iterate over stream 29 | for call in stream: 30 | print(call) -------------------------------------------------------------------------------- /demo/desc_order_calls.py: -------------------------------------------------------------------------------- 1 | from transpose.contract import TransposeDecodedContract 2 | 3 | 4 | def descending_calls_demo(api_key: str) -> None: 5 | """ 6 | This demo shows how to stream all calls to a contract in descending 7 | order by block number. 8 | 9 | :param api_key: Your Transpose API key. 10 | """ 11 | 12 | # initialize WETH contract 13 | contract = TransposeDecodedContract( 14 | contract_address='0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', 15 | abi_path='abi/weth-abi.json', 16 | chain='ethereum', 17 | api_key=api_key 18 | ) 19 | 20 | # build call stream 21 | stream = contract.stream_calls( 22 | start_block=4753925, 23 | order='desc' 24 | ) 25 | 26 | # iterate over stream 27 | for call in stream: 28 | print('{} ({}, {}, {})'.format( 29 | call['item']['function_name'], 30 | call['context']['block_number'], 31 | call['context']['transaction_position'], 32 | call['context']['trace_index'] 33 | )) -------------------------------------------------------------------------------- /demo/live_events.py: -------------------------------------------------------------------------------- 1 | from transpose.contract import TransposeDecodedContract 2 | 3 | 4 | def live_seaport_events_demo(api_key: str) -> None: 5 | """ 6 | This demo shows how to stream a specific live event from the OpenSea 7 | Seaport contract. To do so, it initializes a stream of all OrderFulfilled 8 | events from the OpenSea Seaport contract, starting at the latest block. 9 | It then iterates over the stream and prints each event to the console as 10 | they are received the Transpose backend. 11 | 12 | :param api_key: Your Transpose API key. 13 | """ 14 | 15 | # initialize OpenSea Seaport contract 16 | contract = TransposeDecodedContract( 17 | contract_address='0x00000000006c3852cbEf3e08E8dF289169EdE581', 18 | abi_path='abi/opensea-seaport-abi.json', 19 | chain='ethereum', 20 | api_key=api_key 21 | ) 22 | 23 | # build call stream 24 | stream = contract.stream_events( 25 | event_name='OrderFulfilled', 26 | live_stream=True 27 | ) 28 | 29 | # iterate over stream 30 | for call in stream: 31 | print(call) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Transpose Data Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /demo/benchmark.py: -------------------------------------------------------------------------------- 1 | from transpose.contract import TransposeDecodedContract 2 | import time 3 | 4 | 5 | def benchmark_demo(api_key: str) -> None: 6 | """ 7 | This benchmark demo tests the speed of the Transpose Decoding SDK 8 | by measuring the number of events per second that can be streamed 9 | from the Ethereum WETH contract. To do so, it initializes a stream 10 | of all events from the WETH contract, starting at block 0 and ending 11 | at the latest block (live_stream=False). It then iterates over the 12 | stream and prints the number of events per second to the console. 13 | 14 | :param api_key: Your Transpose API key. 15 | """ 16 | 17 | # initialize WETH contract 18 | contract = TransposeDecodedContract( 19 | contract_address='0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', 20 | abi_path='abi/weth-abi.json', 21 | chain='ethereum', 22 | api_key=api_key 23 | ) 24 | 25 | # build call stream 26 | stream = contract.stream_events() 27 | 28 | # iterate over stream 29 | t1 = time.time() 30 | counter = 0 31 | for _ in stream: 32 | counter += 1 33 | print(f'\rEPS: {counter / (time.time() - t1)}\t', end='') -------------------------------------------------------------------------------- /transpose/utils/request.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | import requests 3 | 4 | from transpose.utils.exceptions import TransposeAPIError 5 | 6 | 7 | def send_transpose_sql_request(api_key: str, query: str, 8 | debug: bool=False) -> List[dict]: 9 | 10 | """ 11 | Send a SQL query to the Transpose API and return the response results. Will 12 | raise a TransposeAPIError if the API returns an error. 13 | 14 | :param api_key: A valid API key for Transpose. 15 | :param query: A valid SQL query. 16 | :param debug: Whether to print the query. 17 | :return: The response from the Transpose API. 18 | """ 19 | 20 | # send POST request to Transpose API 21 | response = requests.post( 22 | url='https://api.transpose.io/sql', 23 | json={'sql': query}, 24 | headers={'X-Api-Key': api_key, 'X-Request-Source': 'decoding-sdk'} 25 | ) 26 | 27 | # check for errors 28 | api_response = response.json() 29 | if api_response['status'] == 'error': 30 | raise TransposeAPIError( 31 | status_code=response.status_code, 32 | message=api_response['message'] 33 | ) 34 | 35 | # print query 36 | if debug: print(query) 37 | 38 | # return results 39 | return api_response['results'] -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import find_packages, setup 2 | with open('README.md', 'r') as f: long_description = f.read() 3 | 4 | setup( 5 | name='transpose_decoding_sdk', 6 | version='1.0.5', 7 | 8 | # meta 9 | long_description=long_description, 10 | long_description_content_type='text/markdown', 11 | include_package_data=True, 12 | description='The Transpose Decoding SDK is a Python package that makes decoding contract activity on EVM blockchains as simple as possible. Simply specify a contract address and ABI to start streaming historical or live activity across decoded logs, transactions, and traces.', 13 | keywords=['web3', 'defi', 'ethereum', 'transpose', 'polygon', 'goerli', 'abi', 'decode', 'event', 'log', 'traces', 'rpc', 'api'], 14 | license='MIT', 15 | 16 | # classifiers 17 | classifiers=[ 18 | 'Development Status :: 3 - Alpha', 19 | 'Intended Audience :: Developers', 20 | 'Intended Audience :: Information Technology', 21 | 'License :: OSI Approved :: MIT License', 22 | 'Natural Language :: English', 23 | 'Topic :: Database', 24 | 'Topic :: Utilities' 25 | ], 26 | 27 | # homepage 28 | url='https://github.com/TransposeData/transpose-decoding-sdk', 29 | 30 | # author 31 | author='Alex Langshur (alangshur)', 32 | author_email='alex@transpose.io', 33 | 34 | # packages 35 | packages=find_packages(exclude=['demo', 'tests']), 36 | 37 | requires=["wheel"], 38 | 39 | # dependencies 40 | install_requires=[ 41 | 'eth-event', 42 | 'pip-chill', 43 | 'dateutils', 44 | 'web3' 45 | ] 46 | ) -------------------------------------------------------------------------------- /transpose/sql/events.py: -------------------------------------------------------------------------------- 1 | def events_query(chain: str, contract_address: str, from_block: int, from_log_index: int, 2 | topic_0: str=None, 3 | stop_block: int=None, 4 | order: str='asc', 5 | limit: int=None) -> str: 6 | 7 | """ 8 | Defines a SQL query that returns logs for a given contract. 9 | 10 | :param chain: The chain name. 11 | :param contract_address: The contract address. 12 | :param from_block: The starting block number, inclusive. 13 | :param from_log_index: The starting log index, inclusive. 14 | :param topic_0: The event signature. 15 | :param stop_block: The ending block number, exclusive. 16 | :param order: The order to return the logs in. 17 | :param limit: The maximum number of logs to return. 18 | :return: The SQL query. 19 | """ 20 | 21 | if order == 'asc': 22 | return \ 23 | f""" 24 | SELECT timestamp, block_number, log_index, transaction_hash, transaction_position, address, data, topic_0, topic_1, topic_2, topic_3, __confirmed 25 | FROM {chain}.logs 26 | WHERE address = '{contract_address}' 27 | {f"AND topic_0 = '{topic_0}'" if topic_0 is not None else ""} 28 | AND (block_number, log_index) >= ({from_block}, {from_log_index}) 29 | {f"AND block_number < {stop_block}" if stop_block is not None else ""} 30 | ORDER BY block_number ASC, log_index ASC 31 | {f"LIMIT {limit}" if limit is not None else ""} 32 | """ 33 | 34 | else: 35 | return \ 36 | f""" 37 | SELECT timestamp, block_number, log_index, transaction_hash, transaction_position, address, data, topic_0, topic_1, topic_2, topic_3, __confirmed 38 | FROM {chain}.logs 39 | WHERE address = '{contract_address}' 40 | {f"AND topic_0 = '{topic_0}'" if topic_0 is not None else ""} 41 | AND (block_number, log_index) <= ({from_block}, {from_log_index}) 42 | {f"AND block_number > {stop_block}" if stop_block is not None else ""} 43 | ORDER BY block_number DESC, log_index DESC 44 | {f"LIMIT {limit}" if limit is not None else ""} 45 | """ -------------------------------------------------------------------------------- /transpose/utils/exceptions.py: -------------------------------------------------------------------------------- 1 | class ContractError(Exception): 2 | """ 3 | The ContractError exception class is raised for errors that 4 | occur during contract execution. 5 | """ 6 | 7 | def __init__(self, message: str) -> None: 8 | """ 9 | Initialize the exception class. 10 | 11 | :param message: The error message. 12 | """ 13 | 14 | self.message = message 15 | 16 | 17 | def __str__(self) -> str: 18 | """ 19 | Return the string-formated error message. 20 | 21 | :return: The error message. 22 | """ 23 | 24 | return 'ContractError ({})'.format(self.message) 25 | 26 | 27 | class StreamError(Exception): 28 | """ 29 | The StreamError exception class is raised for errors that 30 | occur when during stream execution. 31 | """ 32 | 33 | def __init__(self, message: str) -> None: 34 | """ 35 | Initialize the exception class. 36 | 37 | :param message: The error message. 38 | """ 39 | 40 | self.message = message 41 | 42 | 43 | def __str__(self) -> str: 44 | """ 45 | Return the string-formated error message. 46 | 47 | :return: The error message. 48 | """ 49 | 50 | return 'StreamError ({})'.format(self.message) 51 | 52 | 53 | class TransposeAPIError(Exception): 54 | """ 55 | The TransposeAPIError exception class is raised for errors that occur 56 | when calling the Transpose API. Each error returned by the API has 57 | a status code and a message. 58 | """ 59 | 60 | def __init__(self, status_code: int, message: str) -> None: 61 | """ 62 | Initialize the exception class. 63 | 64 | :param status_code: The status code returned by the API. 65 | :param message: The error message returned by the API. 66 | """ 67 | 68 | self.status_code = status_code 69 | self.message = message 70 | 71 | 72 | def __str__(self) -> str: 73 | """ 74 | Return the string-formated error message. 75 | 76 | :return: The error message. 77 | """ 78 | 79 | return 'TransposeAPIError ({}, {})'.format(self.status_code, self.message) -------------------------------------------------------------------------------- /benchmark/web3py_weth_events_benchmark.py: -------------------------------------------------------------------------------- 1 | from transpose.contract import TransposeDecodedContract 2 | from web3 import Web3, HTTPProvider 3 | import json 4 | import time 5 | 6 | 7 | def web3py_weth_events_benchmark(rpc_api_url: str, transpose_api_key: str, from_block: int, to_block: int) -> None: 8 | """ 9 | Benchmark Web3.py library for streaming decoded Ethereum events with a RPC 10 | API against the Transpose Decoding SDK with a Transpose API key. 11 | 12 | :param rpc_api_url: The RPC API URL to connect to. 13 | :param transpose_api_key: The Transpose API key to use. 14 | :param from_block: The block to start streaming events from. 15 | :param to_block: The block to stop streaming events at. 16 | """ 17 | 18 | # run benchmark on Web3.py 19 | print('\rBenchmarking Web3.py... ', end='') 20 | start_time_web3py = time.time() 21 | web3py_events_streamed = run_web3py_benchmark(rpc_api_url, from_block, to_block) 22 | end_time_web3py = time.time() 23 | print('Done.') 24 | 25 | # run benchmark on Transpose 26 | print('\rBenchmarking Transpose... ', end='') 27 | start_time_transpose = time.time() 28 | transpose_events_streamed = run_transpose_benchmark(transpose_api_key, from_block, to_block) 29 | end_time_transpose = time.time() 30 | print('Done.') 31 | 32 | # print results 33 | print('Events streamed: {}'.format(web3py_events_streamed)) 34 | print('\n========== Web3.py ==========') 35 | print('Time elapsed: {} seconds'.format(end_time_web3py - start_time_web3py)) 36 | print('Events per second: {}'.format(web3py_events_streamed / (end_time_web3py - start_time_web3py))) 37 | print('\n========== Transpose ==========') 38 | print('Time elapsed: {} seconds'.format(end_time_transpose - start_time_transpose)) 39 | print('Events per second: {}'.format(transpose_events_streamed / (end_time_transpose - start_time_transpose))) 40 | 41 | 42 | def run_web3py_benchmark(rpc_api_url: str, from_block: int, to_block: int) -> int: 43 | """ 44 | Run the Web3.py component of the benchmark. 45 | 46 | :param rpc_api_url: The RPC API URL. 47 | :param from_block: The block to start streaming events from. 48 | :param to_block: The block to stop streaming events at. 49 | :return: The number of events streamed. 50 | """ 51 | 52 | # connect to Ethereum node 53 | web3 = Web3(HTTPProvider(rpc_api_url)) 54 | 55 | # load ABI 56 | with open('abi/weth-abi.json') as f: 57 | weth_abi = json.load(f) 58 | 59 | # create contract object 60 | contract = web3.eth.contract( 61 | address='0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', 62 | abi=weth_abi 63 | ) 64 | 65 | # stream events in batches of 2000 blocks 66 | total_events_streamed = 0 67 | for block in range(from_block, to_block, 2000): 68 | filter = contract.events.Transfer.createFilter( 69 | fromBlock=block, 70 | toBlock=block + 2000 71 | ) 72 | 73 | # stream events 74 | total_events_streamed += len(filter.get_all_entries()) 75 | 76 | return total_events_streamed 77 | 78 | 79 | def run_transpose_benchmark(api_key: str, from_block: int, to_block: int) -> int: 80 | """ 81 | Run the Transpose component of the benchmark. 82 | 83 | :param api_key: The API key to use for the Transpose API. 84 | :param from_block: The block to start streaming events from. 85 | :param to_block: The block to stop streaming events at. 86 | :return: The number of events streamed. 87 | """ 88 | 89 | # initialize WETH contract 90 | contract = TransposeDecodedContract( 91 | contract_address='0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', 92 | abi_path='abi/weth-abi.json', 93 | chain='ethereum', 94 | api_key=api_key 95 | ) 96 | 97 | # build call stream 98 | stream = contract.stream_events( 99 | start_block=from_block, 100 | end_block=to_block, 101 | event_name='Transfer' 102 | ) 103 | 104 | # iterate over stream 105 | total_events_streamed = 0 106 | for _ in stream: total_events_streamed += 1 107 | return total_events_streamed 108 | -------------------------------------------------------------------------------- /transpose/sql/calls.py: -------------------------------------------------------------------------------- 1 | def calls_query(chain: str, contract_address: str, from_block: int, from_transaction_position: int, from_trace_index: int, 2 | function_selector: str=None, 3 | stop_block: int=None, 4 | order: str='asc', 5 | limit: int=None) -> str: 6 | 7 | """ 8 | Defines a SQL query that returns transactions and traces for a given contract. 9 | 10 | :param chain: The chain name. 11 | :param contract_address: The contract address. 12 | :param from_block: The starting block number, inclusive. 13 | :param from_transaction_position: The starting transaction position, inclusive. 14 | :param from_trace_index: The starting trace index, inclusive. 15 | :param function_selector: The function selector. 16 | :param stop_block: The ending block number, exclusive. 17 | :param order: The order to return the transactions and traces in. 18 | :param limit: The maximum number of transactions and traces to return. 19 | :return: The SQL query. 20 | """ 21 | 22 | if order == 'asc': 23 | return \ 24 | f""" 25 | SELECT * FROM ( 26 | 27 | (SELECT 28 | timestamp, block_number, transaction_hash, position AS transaction_position, 29 | 0 AS trace_index, array[]::integer[] AS trace_address, 'call' AS trace_type, 30 | from_address, value, input, output, __confirmed 31 | FROM {chain}.transactions 32 | WHERE to_address = '{contract_address}' 33 | {f"AND LEFT(input, 10) = '{function_selector}'" if function_selector is not None else ""} 34 | AND (block_number, position) >= ({from_block}, {from_transaction_position}) 35 | {f"AND block_number < {stop_block}" if stop_block is not None else ""} 36 | ORDER BY block_number ASC, transaction_position ASC 37 | {f"LIMIT {limit}" if limit is not None else ""}) 38 | 39 | UNION ALL 40 | 41 | (SELECT 42 | timestamp, block_number, transaction_hash, transaction_position, 43 | trace_index + 1, trace_address, trace_type, 44 | from_address, value, input, output, __confirmed 45 | FROM {chain}.traces 46 | WHERE to_address = '{contract_address}' 47 | {f"AND LEFT(input, 10) = '{function_selector}'" if function_selector is not None else ""} 48 | AND (block_number, transaction_position, trace_index) >= ({from_block}, {from_transaction_position}, {from_trace_index}) 49 | {f"AND block_number < {stop_block}" if stop_block is not None else ""} 50 | ORDER BY block_number ASC, transaction_position ASC, trace_index ASC 51 | {f"LIMIT {limit}" if limit is not None else ""}) 52 | 53 | ) AS t 54 | 55 | ORDER BY block_number ASC, transaction_position ASC, trace_index ASC 56 | {f"LIMIT {limit}" if limit is not None else ""} 57 | """ 58 | 59 | else: 60 | return \ 61 | f""" 62 | SELECT * FROM ( 63 | 64 | (SELECT 65 | timestamp, block_number, transaction_hash, position AS transaction_position, 66 | 0 AS trace_index, array[]::integer[] AS trace_address, 'call' AS trace_type, 67 | from_address, value, input, output, __confirmed 68 | FROM {chain}.transactions 69 | WHERE to_address = '{contract_address}' 70 | {f"AND LEFT(input, 10) = '{function_selector}'" if function_selector is not None else ""} 71 | AND (block_number, position) <= ({from_block}, {from_transaction_position}) 72 | {f"AND block_number > {stop_block}" if stop_block is not None else ""} 73 | ORDER BY block_number DESC, transaction_position DESC 74 | {f"LIMIT {limit}" if limit is not None else ""}) 75 | 76 | UNION ALL 77 | 78 | (SELECT 79 | timestamp, block_number, transaction_hash, transaction_position, 80 | trace_index + 1, trace_address, trace_type, 81 | from_address, value, input, output, __confirmed 82 | FROM {chain}.traces 83 | WHERE to_address = '{contract_address}' 84 | {f"AND LEFT(input, 10) = '{function_selector}'" if function_selector is not None else ""} 85 | AND (block_number, transaction_position, trace_index) <= ({from_block}, {from_transaction_position}, {from_trace_index}) 86 | {f"AND block_number > {stop_block}" if stop_block is not None else ""} 87 | ORDER BY block_number DESC, transaction_position DESC, trace_index DESC 88 | {f"LIMIT {limit}" if limit is not None else ""}) 89 | 90 | ) AS t 91 | 92 | ORDER BY block_number DESC, transaction_position DESC, trace_index DESC 93 | {f"LIMIT {limit}" if limit is not None else ""} 94 | """ -------------------------------------------------------------------------------- /abi/weth-abi.json: -------------------------------------------------------------------------------- 1 | [{ 2 | "constant": true, 3 | "inputs": [], 4 | "name": "name", 5 | "outputs": [{ 6 | "name": "", 7 | "type": "string" 8 | }], 9 | "payable": false, 10 | "stateMutability": "view", 11 | "type": "function" 12 | }, { 13 | "constant": false, 14 | "inputs": [{ 15 | "name": "guy", 16 | "type": "address" 17 | }, { 18 | "name": "wad", 19 | "type": "uint256" 20 | }], 21 | "name": "approve", 22 | "outputs": [{ 23 | "name": "", 24 | "type": "bool" 25 | }], 26 | "payable": false, 27 | "stateMutability": "nonpayable", 28 | "type": "function" 29 | }, { 30 | "constant": true, 31 | "inputs": [], 32 | "name": "totalSupply", 33 | "outputs": [{ 34 | "name": "", 35 | "type": "uint256" 36 | }], 37 | "payable": false, 38 | "stateMutability": "view", 39 | "type": "function" 40 | }, { 41 | "constant": false, 42 | "inputs": [{ 43 | "name": "src", 44 | "type": "address" 45 | }, { 46 | "name": "dst", 47 | "type": "address" 48 | }, { 49 | "name": "wad", 50 | "type": "uint256" 51 | }], 52 | "name": "transferFrom", 53 | "outputs": [{ 54 | "name": "", 55 | "type": "bool" 56 | }], 57 | "payable": false, 58 | "stateMutability": "nonpayable", 59 | "type": "function" 60 | }, { 61 | "constant": false, 62 | "inputs": [{ 63 | "name": "wad", 64 | "type": "uint256" 65 | }], 66 | "name": "withdraw", 67 | "outputs": [], 68 | "payable": false, 69 | "stateMutability": "nonpayable", 70 | "type": "function" 71 | }, { 72 | "constant": true, 73 | "inputs": [], 74 | "name": "decimals", 75 | "outputs": [{ 76 | "name": "", 77 | "type": "uint8" 78 | }], 79 | "payable": false, 80 | "stateMutability": "view", 81 | "type": "function" 82 | }, { 83 | "constant": true, 84 | "inputs": [{ 85 | "name": "", 86 | "type": "address" 87 | }], 88 | "name": "balanceOf", 89 | "outputs": [{ 90 | "name": "", 91 | "type": "uint256" 92 | }], 93 | "payable": false, 94 | "stateMutability": "view", 95 | "type": "function" 96 | }, { 97 | "constant": true, 98 | "inputs": [], 99 | "name": "symbol", 100 | "outputs": [{ 101 | "name": "", 102 | "type": "string" 103 | }], 104 | "payable": false, 105 | "stateMutability": "view", 106 | "type": "function" 107 | }, { 108 | "constant": false, 109 | "inputs": [{ 110 | "name": "dst", 111 | "type": "address" 112 | }, { 113 | "name": "wad", 114 | "type": "uint256" 115 | }], 116 | "name": "transfer", 117 | "outputs": [{ 118 | "name": "", 119 | "type": "bool" 120 | }], 121 | "payable": false, 122 | "stateMutability": "nonpayable", 123 | "type": "function" 124 | }, { 125 | "constant": false, 126 | "inputs": [], 127 | "name": "deposit", 128 | "outputs": [], 129 | "payable": true, 130 | "stateMutability": "payable", 131 | "type": "function" 132 | }, { 133 | "constant": true, 134 | "inputs": [{ 135 | "name": "", 136 | "type": "address" 137 | }, { 138 | "name": "", 139 | "type": "address" 140 | }], 141 | "name": "allowance", 142 | "outputs": [{ 143 | "name": "", 144 | "type": "uint256" 145 | }], 146 | "payable": false, 147 | "stateMutability": "view", 148 | "type": "function" 149 | }, { 150 | "payable": true, 151 | "stateMutability": "payable", 152 | "type": "fallback" 153 | }, { 154 | "anonymous": false, 155 | "inputs": [{ 156 | "indexed": true, 157 | "name": "src", 158 | "type": "address" 159 | }, { 160 | "indexed": true, 161 | "name": "guy", 162 | "type": "address" 163 | }, { 164 | "indexed": false, 165 | "name": "wad", 166 | "type": "uint256" 167 | }], 168 | "name": "Approval", 169 | "type": "event" 170 | }, { 171 | "anonymous": false, 172 | "inputs": [{ 173 | "indexed": true, 174 | "name": "src", 175 | "type": "address" 176 | }, { 177 | "indexed": true, 178 | "name": "dst", 179 | "type": "address" 180 | }, { 181 | "indexed": false, 182 | "name": "wad", 183 | "type": "uint256" 184 | }], 185 | "name": "Transfer", 186 | "type": "event" 187 | }, { 188 | "anonymous": false, 189 | "inputs": [{ 190 | "indexed": true, 191 | "name": "dst", 192 | "type": "address" 193 | }, { 194 | "indexed": false, 195 | "name": "wad", 196 | "type": "uint256" 197 | }], 198 | "name": "Deposit", 199 | "type": "event" 200 | }, { 201 | "anonymous": false, 202 | "inputs": [{ 203 | "indexed": true, 204 | "name": "src", 205 | "type": "address" 206 | }, { 207 | "indexed": false, 208 | "name": "wad", 209 | "type": "uint256" 210 | }], 211 | "name": "Withdrawal", 212 | "type": "event" 213 | }] -------------------------------------------------------------------------------- /transpose/utils/decode.py: -------------------------------------------------------------------------------- 1 | from eth_event import get_log_topic 2 | from eth_utils import decode_hex 3 | from eth_abi import decode 4 | from typing import List, Dict 5 | import re 6 | 7 | 8 | def extract_params(abi_params: List[dict]) -> List[str]: 9 | """ 10 | Returns a list of the types of the parameters for a single 11 | ABI item. 12 | 13 | :param abi_params: The ABI parameters. 14 | :return: The list of types. 15 | """ 16 | 17 | types = [] 18 | pattern = re.compile(r'tuple(\[(\d*)\])?') 19 | 20 | # iterate over ABI parameters 21 | for param in abi_params: 22 | tuple_match = pattern.match(param['type']) 23 | if not tuple_match: types.append(param['type']) 24 | else: 25 | array, size = tuple_match.group(1, 2) 26 | tuple_type_tail = f'[{size}]' if array is not None else '' 27 | types.append(f"({','.join(x for x in extract_params(param['components']))}){tuple_type_tail}") 28 | continue 29 | 30 | return types 31 | 32 | 33 | def build_function_map(abi: List[dict]) -> Dict[str, dict]: 34 | """ 35 | Builds a dictionary that maps function selectors to the 36 | function name, types, and params. 37 | 38 | :param abi: The ABI. 39 | :return: The function map. 40 | """ 41 | 42 | function_map = {} 43 | for item in abi: 44 | if 'type' not in item or item['type'] != 'function': continue 45 | elif 'name' not in item: continue 46 | function_map[get_log_topic(item)[:10]] = { 47 | 'name': item['name'], 48 | 'input_order': [i['name'] for i in item['inputs']], 49 | 'output_order': [i['name'] for i in item['outputs']], 50 | 'inputs': { 51 | 'types': extract_params(item['inputs']), 52 | 'params': item['inputs'] 53 | }, 54 | 'outputs': { 55 | 'types': extract_params(item['outputs']), 56 | 'params': item['outputs'] 57 | } 58 | } 59 | 60 | return function_map 61 | 62 | 63 | def build_topic_map(abi: List[dict]) -> Dict[str, dict]: 64 | """ 65 | Builds a dictionary that maps event topics to the event name, 66 | types, and params. 67 | 68 | :param abi: The ABI. 69 | :return: The topic map. 70 | """ 71 | 72 | topic_map = {} 73 | for item in abi: 74 | if 'type' not in item or item['type'] != 'event': continue 75 | elif 'name' not in item: continue 76 | topic_map[get_log_topic(item)] = { 77 | 'name': item['name'], 78 | 'order': [i['name'] for i in item['inputs']], 79 | 'topics': { 80 | 'types': extract_params([i for i in item['inputs'] if i['indexed']]), 81 | 'params': [i for i in item['inputs'] if i['indexed']] 82 | }, 83 | 'data': { 84 | 'types': extract_params([i for i in item['inputs'] if not i['indexed']]), 85 | 'params': [i for i in item['inputs'] if not i['indexed']] 86 | } 87 | } 88 | 89 | return topic_map 90 | 91 | 92 | def decode_hex_data(types: List[str], hex_data: str) -> tuple: 93 | """ 94 | Decodes the data hex string into a tuple of the parameters. 95 | 96 | :param types: The list of parameter types. 97 | :param data: The data hex string. 98 | :return: The decoded tuple. 99 | """ 100 | 101 | if hex_data is None: return tuple() 102 | data = decode_hex(hex_data) 103 | decoded_data = decode(types, data) 104 | return decoded_data 105 | 106 | 107 | def resolve_decoded_data(abi_params: List[dict], decoded_data: tuple) -> dict: 108 | """ 109 | Resolve the decoded data tuple from an eth_abi.decode_abi call 110 | and the ABI parameters to a dictionary of the parameter names and 111 | values. 112 | 113 | :param abi_params: The ABI parameters. 114 | :param decoded_data: The decoded data tuple. 115 | :return: The dictionary of parameter names and values. 116 | """ 117 | 118 | # enforce that the number of params matches the number of decoded items 119 | if len(abi_params) != len(decoded_data): 120 | raise ValueError('Length of decoded items does not match the number of ABI parameters') 121 | 122 | # recursively resolve the params and decoded items 123 | decoded_params = {} 124 | for abi_item, decoded_item in zip(abi_params, decoded_data): 125 | if abi_item['type'] == 'tuple': 126 | decoded_tuple_params = resolve_decoded_data(abi_item['components'], decoded_item) 127 | decoded_params[abi_item['name']] = decoded_tuple_params 128 | elif abi_item['type'] == 'tuple[]': 129 | decoded_tuple_array_params = [resolve_decoded_data(abi_item['components'], i) for i in decoded_item] 130 | decoded_params[abi_item['name']] = decoded_tuple_array_params 131 | elif abi_item['type'].endswith('[][]'): 132 | raise NotImplementedError('Nested arrays are not supported') 133 | elif abi_item['type'].endswith('[]'): 134 | decoded_params[abi_item['name']] = list(decoded_item) 135 | else: 136 | decoded_params[abi_item['name']] = decoded_item 137 | 138 | return decoded_params -------------------------------------------------------------------------------- /transpose/stream/base.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | from typing import List, Tuple 3 | import time 4 | 5 | from transpose.utils.exceptions import StreamError 6 | 7 | 8 | class Stream(ABC): 9 | """ 10 | The Stream class is an abstract base class for all stream objects. Each stream 11 | implementation must inherit from this class and implement the fetch() and 12 | decode() methods. Then, you can continuously call the next() method to 13 | retrieve the next batch of data from the stream. You may also use the stream 14 | as an iterator, which will return a single item on each iteration. 15 | """ 16 | 17 | def __init__(self, api_key: str, 18 | start_block: int=0, 19 | end_block: int=None, 20 | order: str='asc', 21 | live_stream: bool=False, 22 | live_refresh_interval: int=3) -> None: 23 | 24 | """ 25 | Initialize the stream. 26 | 27 | :param api_key: The API key for the Transpose API. 28 | :param start_block: The block to start streaming from, inclusive. 29 | :param end_block: The block to stop streaming at, exclusive. 30 | :param order: The order to stream the events in. 31 | :param live_stream: Whether to scroll the iterator when reaches live. 32 | :param live_refresh_interval: The delay between scroll attempts in seconds. 33 | """ 34 | 35 | self.api_key = api_key 36 | self.start_block = start_block 37 | self.end_block = end_block 38 | self.order = order 39 | self.live_stream = live_stream 40 | self.live_refresh_interval = live_refresh_interval 41 | self.__state = None 42 | self.__it_idx = None 43 | self.__it_data = None 44 | 45 | # validate order 46 | if order not in ['asc', 'desc']: 47 | raise StreamError('Invalid order (must be "asc" or "desc")') 48 | 49 | # validate block range 50 | if not isinstance(start_block, int) or start_block < 0: 51 | raise StreamError('Invalid start block') 52 | elif end_block is not None: 53 | if not isinstance(end_block, int): raise StreamError('Invalid end block') 54 | elif order == 'asc' and end_block < start_block: raise StreamError('Invalid block range') 55 | elif order == 'desc' and end_block > start_block: raise StreamError('Invalid block range') 56 | 57 | # validate scroll iterator 58 | if not isinstance(live_stream, bool): 59 | raise StreamError('Invalid scroll iterator') 60 | elif live_stream and end_block is not None: 61 | raise StreamError('Cannot scroll iterator when end block is specified') 62 | 63 | # validate scroll delay 64 | if not isinstance(live_refresh_interval, int) or live_refresh_interval < 0: 65 | raise StreamError('Invalid scroll delay') 66 | 67 | 68 | def __iter__(self) -> 'Stream': 69 | """ 70 | Return the stream object as an iterator. 71 | 72 | :return: The stream object. 73 | """ 74 | 75 | return self 76 | 77 | 78 | def next(self, 79 | limit: int=100) -> List[dict]: 80 | 81 | """ 82 | Return the next batch of data from the stream. 83 | 84 | :param limit: The maximum number of items to return. 85 | :return: The next batch of data. 86 | """ 87 | 88 | return self.__load_next_batch(limit) 89 | 90 | 91 | def __next__(self) -> dict: 92 | """ 93 | Return the next item from the stream. 94 | 95 | :return: The next item. 96 | """ 97 | 98 | try: 99 | 100 | # check if we have any data left 101 | if self.__it_idx is None or self.__it_idx >= len(self.__it_data): 102 | self.__it_data = self.__load_next_batch(None) 103 | self.__it_idx = 0 104 | 105 | # if scroll iterator is enabled, wait for data 106 | if len(self.__it_data) == 0 and self.live_stream: 107 | while len(self.__it_data) == 0: 108 | time.sleep(self.live_refresh_interval) 109 | self.__it_data = self.__load_next_batch(None) 110 | 111 | # otherwise, raise StopIteration 112 | elif len(self.__it_data) == 0: 113 | raise StopIteration 114 | 115 | # get next item 116 | item = self.__it_data[self.__it_idx] 117 | self.__it_idx += 1 118 | return item 119 | 120 | except KeyboardInterrupt: 121 | raise StopIteration 122 | 123 | 124 | def __load_next_batch(self, limit: int) -> List[dict]: 125 | """ 126 | Private implementation to fetch the next batch of data from the stream. 127 | 128 | :param limit: The maximum number of items to return. 129 | """ 130 | 131 | # set initial state 132 | if self.__state is None: 133 | self.__state = self.reset(self.start_block) 134 | 135 | # fetch data 136 | data, self.__state = self.fetch( 137 | state=self.__state, 138 | stop_block=self.end_block, 139 | order=self.order, 140 | limit=limit 141 | ) 142 | 143 | # decode data 144 | decoded_data = [] 145 | for item in data: 146 | decoded_item = self.decode(item) 147 | if decoded_item is not None: 148 | decoded_data.append(decoded_item) 149 | 150 | return decoded_data 151 | 152 | 153 | @abstractmethod 154 | def reset(self, start_block: int) -> dict: 155 | """ 156 | Reset the stream by returning the default stream state as defined by the 157 | implementation. This is an abstract method that must be implemented by 158 | the child class. 159 | 160 | :param start_block: The block to start streaming from, exclusive. 161 | :return: The default stream state. 162 | """ 163 | 164 | raise NotImplementedError 165 | 166 | 167 | @abstractmethod 168 | def fetch(self, state: dict, 169 | stop_block: int=None, 170 | order: str='asc', 171 | limit: int=None) -> Tuple[List[dict], dict]: 172 | 173 | """ 174 | Retrieve the next batch of raw data from the stream. This is an abstract 175 | method that must be implemented by the child class. 176 | 177 | :param state: The current stream state. 178 | :param stop_block: The block to stop streaming at, exclusive. 179 | :param order: The order to stream the events in. 180 | :param limit: The maximum number of items to return in the batch. 181 | :return: A tuple containing the batch data and the resulting state. 182 | """ 183 | 184 | raise NotImplementedError 185 | 186 | 187 | @abstractmethod 188 | def decode(self, data: dict) -> dict: 189 | """ 190 | Decode a single item from the stream. This is an abstract method that 191 | must be implemented by the child class. 192 | 193 | :param data: The data to decode. 194 | """ 195 | 196 | raise NotImplementedError -------------------------------------------------------------------------------- /transpose/stream/event.py: -------------------------------------------------------------------------------- 1 | from typing import Tuple, List 2 | 3 | from transpose.stream.base import Stream 4 | from transpose.sql.events import events_query 5 | from transpose.utils.exceptions import StreamError 6 | from transpose.utils.request import send_transpose_sql_request 7 | from transpose.utils.decode import build_topic_map, decode_hex_data, resolve_decoded_data 8 | from transpose.utils.time import to_iso_timestamp 9 | 10 | 11 | class EventStream(Stream): 12 | """ 13 | The EventStream class implements the Stream class to stream events 14 | from a contract. See the Stream class for more information on the 15 | interface for this class. 16 | """ 17 | 18 | def __init__(self, api_key: str, chain: str, contract_address: str, abi: dict, 19 | event_name: str=None, 20 | start_block: int=0, 21 | end_block: int=None, 22 | order: str='asc', 23 | live_stream: bool=False, 24 | live_refresh_interval: int=3) -> None: 25 | 26 | """ 27 | Initialize the stream. 28 | 29 | :param api_key: The API key. 30 | :param chain: The chain name. 31 | :param contract_address: The contract address. 32 | :param abi: The contract ABI. 33 | :param start_block: The block to start streaming from, inclusive. 34 | :param end_block: The block to stop streaming at, exclusive. 35 | :param order: The order to stream the events in. 36 | :param live_stream: Whether to stream live data. 37 | :param live_refresh_interval: The interval for refreshing the data in seconds when live. 38 | """ 39 | 40 | super().__init__( 41 | api_key=api_key, 42 | start_block=start_block, 43 | end_block=end_block, 44 | order=order, 45 | live_stream=live_stream, 46 | live_refresh_interval=live_refresh_interval 47 | ) 48 | 49 | self.chain = chain 50 | self.contract_address = contract_address 51 | self.abi = abi 52 | 53 | # build topic map 54 | try: self.topic_map = build_topic_map(self.abi) 55 | except Exception as e: raise StreamError('Invalid ABI') from e 56 | 57 | # get target event signature 58 | self.event_signature = None 59 | if event_name is not None: 60 | matching_event_signatures = [k for k, v in self.topic_map.items() if v['name'] == event_name] 61 | if len(matching_event_signatures) != 1: raise StreamError('Invalid event name') 62 | self.event_signature = matching_event_signatures[0] 63 | 64 | 65 | def reset(self, start_block: int) -> dict: 66 | """ 67 | Reset the stream state to the default state. The default stream 68 | state is simply the start block and the zero log index. 69 | 70 | :param start_block: The block to reset the stream to. 71 | :return: The default stream state. 72 | """ 73 | 74 | return { 75 | 'block_number': start_block, 76 | 'log_index': 0 77 | } 78 | 79 | 80 | def fetch(self, state: dict, 81 | stop_block: int=None, 82 | order: str='asc', 83 | limit: int=None) -> Tuple[List[dict], dict]: 84 | 85 | """ 86 | Fetch the next set of raw events for the stream and update the 87 | stream state. 88 | 89 | :param state: The current stream state. 90 | :param stop_block: The block to stop fetching at, exclusive. 91 | :param order: The order to fetch the events in. 92 | :param limit: The maximum number of events to fetch. 93 | """ 94 | 95 | # build query 96 | query = events_query( 97 | chain=self.chain, 98 | contract_address=self.contract_address, 99 | from_block=state['block_number'], 100 | from_log_index=state['log_index'], 101 | topic_0=self.event_signature, 102 | stop_block=stop_block, 103 | order=order, 104 | limit=limit 105 | ) 106 | 107 | # send request 108 | data = send_transpose_sql_request( 109 | api_key=self.api_key, 110 | query=query 111 | ) 112 | 113 | # update state 114 | if len(data) > 0: 115 | if order == 'asc': 116 | state['block_number'] = data[-1]['block_number'] 117 | state['log_index'] = data[-1]['log_index'] + 1 118 | else: 119 | if data[0]['log_index'] == 0: 120 | state['block_number'] = data[-1]['block_number'] - 1 121 | state['log_index'] = int(1e9) 122 | else: 123 | state['block_number'] = data[-1]['block_number'] 124 | state['log_index'] = data[-1]['log_index'] - 1 125 | 126 | return data, state 127 | 128 | 129 | def decode(self, data: dict) -> dict: 130 | """ 131 | Decode the raw log data into a decoded event. The decoded event 132 | is a dictionary with three fields: the item, the context, and 133 | the event_data. The item field contains information on the target 134 | activity, the context field contains information on the context 135 | of the activity, and the event_data field contains the decoded 136 | data from the log. 137 | 138 | :param data: The raw log data. 139 | :return: The decoded log. 140 | """ 141 | 142 | # check if log is in topic map 143 | if data['topic_0'] not in self.topic_map: return None 144 | target_topic = self.topic_map[data['topic_0']] 145 | 146 | # decode topics 147 | try: 148 | topics_data = '0x' + ''.join(data[f'topic_{i}'][2:] for i in range(1, 4) if data[f'topic_{i}'] is not None) 149 | decoded_topics = decode_hex_data(target_topic['topics']['types'], topics_data) 150 | topics_data = resolve_decoded_data(target_topic['topics']['params'], decoded_topics) 151 | except Exception as e: 152 | raise StreamError('Failed to decode log') from e 153 | 154 | # decode data 155 | try: 156 | decoded_data = decode_hex_data(target_topic['data']['types'], data['data']) 157 | data_data = resolve_decoded_data(target_topic['data']['params'], decoded_data) 158 | except Exception as e: 159 | raise StreamError('Failed to decode data') from e 160 | 161 | # order event data 162 | event_data = dict(sorted( 163 | {**topics_data, **data_data}.items(), 164 | key=lambda item: target_topic['order'].index(item[0]) 165 | )) 166 | 167 | # format decoded log 168 | return { 169 | 'item': { 170 | 'contract_address': self.contract_address, 171 | 'event_name': target_topic['name'] 172 | }, 173 | 'context': { 174 | 'timestamp': to_iso_timestamp(data['timestamp']), 175 | 'block_number': data['block_number'], 176 | 'log_index': data['log_index'], 177 | 'transaction_hash': data['transaction_hash'], 178 | 'transaction_position': data['transaction_position'], 179 | 'confirmed': data['__confirmed'] 180 | }, 181 | 'event_data': event_data 182 | } -------------------------------------------------------------------------------- /transpose/stream/call.py: -------------------------------------------------------------------------------- 1 | from typing import Tuple, List 2 | 3 | from transpose.stream.base import Stream 4 | from transpose.sql.calls import calls_query 5 | from transpose.utils.exceptions import StreamError 6 | from transpose.utils.request import send_transpose_sql_request 7 | from transpose.utils.decode import build_function_map, decode_hex_data, resolve_decoded_data 8 | from transpose.utils.time import to_iso_timestamp 9 | 10 | 11 | class CallStream(Stream): 12 | """ 13 | The CallStream class implements the Stream class to stream calls 14 | from a contract. See the Stream class for more information on the 15 | interface for this class. 16 | """ 17 | 18 | def __init__(self, api_key: str, chain: str, contract_address: str, abi: dict, 19 | function_name: str=None, 20 | start_block: int=0, 21 | end_block: int=None, 22 | order: str='asc', 23 | live_stream: bool=False, 24 | live_refresh_interval: int=3) -> None: 25 | 26 | """ 27 | Initialize the stream. 28 | 29 | :param api_key: The API key. 30 | :param chain: The chain name. 31 | :param contract_address: The contract address. 32 | :param abi: The contract ABI. 33 | :param start_block: The block to start streaming from, inclusive. 34 | :param end_block: The block to stop streaming at, exclusive. 35 | :param order: The order to stream the calls in. 36 | :param live_stream: Whether to stream live data. 37 | :param live_refresh_interval: The interval for refreshing the data in seconds when live. 38 | """ 39 | 40 | super().__init__( 41 | api_key=api_key, 42 | start_block=start_block, 43 | end_block=end_block, 44 | order=order, 45 | live_stream=live_stream, 46 | live_refresh_interval=live_refresh_interval 47 | ) 48 | 49 | self.chain = chain 50 | self.contract_address = contract_address 51 | self.abi = abi 52 | 53 | # build function map 54 | try: self.function_map = build_function_map(self.abi) 55 | except Exception as e: raise StreamError('Invalid ABI') from e 56 | 57 | # get target function selector 58 | self.function_selector = None 59 | if function_name is not None: 60 | matching_function_selectors = [k for k, v in self.function_map.items() if v['name'] == function_name] 61 | if len(matching_function_selectors) != 1: raise StreamError('Invalid function name') 62 | self.function_selector = matching_function_selectors[0] 63 | 64 | 65 | def reset(self, start_block: int) -> dict: 66 | """ 67 | Reset the stream state to the default state. The default stream 68 | state is simply the start block, the zero transaction position, 69 | and the zero trace index. 70 | 71 | :param start_block: The block to reset the stream to. 72 | :return: The default stream state. 73 | """ 74 | 75 | return { 76 | 'block_number': start_block, 77 | 'transaction_position': 0, 78 | 'trace_index': 0 79 | } 80 | 81 | 82 | def fetch(self, state: dict, 83 | stop_block: int=None, 84 | order: str='asc', 85 | limit: int=None) -> Tuple[List[dict], dict]: 86 | 87 | """ 88 | Fetch the next set of raw calls for the stream and update 89 | the stream state. 90 | 91 | :param state: The current stream state. 92 | :param stop_block: The block to stop fetching at, exclusive. 93 | :param order: The order to fetch the calls in. 94 | :param limit: The maximum number of calls to fetch. 95 | """ 96 | 97 | # build query 98 | query = calls_query( 99 | chain=self.chain, 100 | contract_address=self.contract_address, 101 | from_block=state['block_number'], 102 | from_transaction_position=state['transaction_position'], 103 | from_trace_index=state['trace_index'], 104 | function_selector=self.function_selector, 105 | stop_block=stop_block, 106 | order=order, 107 | limit=limit 108 | ) 109 | 110 | # send request 111 | data = send_transpose_sql_request( 112 | api_key=self.api_key, 113 | query=query 114 | ) 115 | 116 | # update state 117 | if len(data) > 0: 118 | if order == 'asc': 119 | state['block_number'] = data[-1]['block_number'] 120 | state['transaction_position'] = data[-1]['transaction_position'] 121 | state['trace_index'] = data[-1]['trace_index'] + 1 122 | else: 123 | if data[0]['trace_index'] == 0: 124 | state['block_number'] = data[-1]['block_number'] 125 | state['transaction_position'] = data[-1]['transaction_position'] - 1 126 | state['trace_index'] = int(1e9) 127 | elif data[0]['transaction_position'] == 0: 128 | state['block_number'] = data[-1]['block_number'] - 1 129 | state['transaction_position'] = int(1e9) 130 | state['trace_index'] = int(1e9) 131 | else: 132 | state['block_number'] = data[-1]['block_number'] 133 | state['transaction_position'] = data[-1]['transaction_position'] 134 | state['trace_index'] = data[-1]['trace_index'] - 1 135 | 136 | return data, state 137 | 138 | 139 | def decode(self, data: dict) -> dict: 140 | """ 141 | Decode the raw transaction/trace data into a decoded call. The decoded 142 | call is a dictionary with five fields: the item, the context, the call_data, 143 | the input_data, and the output_data. The item field contains information 144 | on the target activity, the context field contains information on the 145 | context of the activity, the call_data contains information on the underlying 146 | call, and the input_data and output_data fields contain the decoded input 147 | and output data for the call. 148 | 149 | :param data: The raw transaction/trace data. 150 | :return: The decoded call data. 151 | """ 152 | 153 | # check if function selector is in function map 154 | function_selector = data['input'][:10] 155 | if function_selector not in self.function_map: return None 156 | target_function = self.function_map[function_selector] 157 | 158 | # decode input 159 | try: 160 | decoded_input = decode_hex_data(target_function['inputs']['types'], '0x' + data['input'][10:]) 161 | input_data = resolve_decoded_data(target_function['inputs']['params'], decoded_input) 162 | except Exception as e: 163 | raise StreamError('Failed to decode input data') from e 164 | 165 | # order input data 166 | input_data = dict(sorted( 167 | input_data.items(), 168 | key=lambda item: target_function['input_order'].index(item[0]) 169 | )) 170 | 171 | # decode output 172 | try: 173 | decoded_output = decode_hex_data(target_function['outputs']['types'], data['output']) 174 | output_data = resolve_decoded_data(target_function['outputs']['params'], decoded_output) 175 | except Exception as e: 176 | raise StreamError('Failed to decode output data') from e 177 | 178 | # order output data 179 | output_data = dict(sorted( 180 | output_data.items(), 181 | key=lambda item: target_function['output_order'].index(item[0]) 182 | )) 183 | 184 | # format decoded log 185 | return { 186 | 'item': { 187 | 'contract_address': self.contract_address, 188 | 'function_name': target_function['name'] 189 | }, 190 | 'context': { 191 | 'timestamp': to_iso_timestamp(data['timestamp']), 192 | 'block_number': data['block_number'], 193 | 'transaction_hash': data['transaction_hash'], 194 | 'transaction_position': data['transaction_position'], 195 | 'trace_index': data['trace_index'], 196 | 'trace_address': data['trace_address'], 197 | 'trace_type': data['trace_type'], 198 | 'confirmed': data['__confirmed'] 199 | }, 200 | 'call_data': { 201 | 'type': 'transaction' if data['trace_index'] == 0 else 'internal_transaction', 202 | 'from_address': data['from_address'], 203 | 'to_address': self.contract_address, 204 | 'eth_value': data['value'] // 10**18 205 | }, 206 | 'input_data': input_data, 207 | 'output_data': output_data 208 | } -------------------------------------------------------------------------------- /transpose/contract.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | from transpose.stream.base import Stream 4 | from transpose.stream.event import EventStream 5 | from transpose.stream.call import CallStream 6 | from transpose.sql.general import latest_block_query 7 | from transpose.utils.request import send_transpose_sql_request 8 | from transpose.utils.exceptions import ContractError 9 | from transpose.utils.address import to_checksum_address 10 | 11 | 12 | class TransposeDecodedContract: 13 | """ 14 | The TransposeDecodedContract class is used to stream decoded events and contract calls for a contract 15 | given its contract address and ABI. The class is initialized with a valid API key, contract address, and ABI. 16 | The class can then be used to stream decoded events and contract calls by either calling the stream_events() 17 | or stream_calls() methods with a number of parameters. The stream_events() method will return an EventStream 18 | object, which can be used to stream decoded events. The stream_calls() method will return a CallStream object, 19 | which can be used to stream decoded contract calls. 20 | 21 | The stream can then be used to get the next decoded event or contract call with the next() method. The stream 22 | can also be used as an iterator, which will return the next decoded event or contract call on each iteration. 23 | """ 24 | 25 | def __init__(self, contract_address: str, 26 | abi: dict=None, 27 | abi_path: str=None, 28 | chain: str='ethereum', 29 | api_key: str=None) -> None: 30 | 31 | """ 32 | Initialize the TransposeDecodedContract class with a valid target contract and 33 | Transpose API key. 34 | 35 | :param contract_address: The contract address. 36 | :param abi: The ABI, supplied as a dict. 37 | :param abi_path: The path to the ABI, supplied as a JSON file. 38 | :param chain: The chain the contract is deployed on. 39 | :param api_key: The API key for the Transpose API. 40 | """ 41 | 42 | # validate contract address 43 | self.contract_address = to_checksum_address(contract_address) 44 | if self.contract_address is None: raise ContractError('Invalid contract address') 45 | 46 | # validate ABI 47 | if abi is None and abi_path is None: raise ContractError('ABI is required') 48 | elif abi is not None and abi_path is not None: raise ContractError('Only one of ABI or ABI path can be supplied') 49 | elif abi is not None: self.abi = abi 50 | elif abi_path is not None: 51 | try: self.abi = json.load(open(abi_path)) 52 | except: raise ContractError('Invalid ABI path') 53 | 54 | # validate ABI object 55 | if not isinstance(self.abi, list): raise ContractError('ABI must be a list of dicts') 56 | elif not all([isinstance(item, dict) for item in self.abi]): raise ContractError('ABI must be a list of dicts') 57 | 58 | # validate chain 59 | self.chain = chain 60 | if chain not in ['ethereum', 'goerli', 'polygon', 'arbitrum', 'scroll', 'canto']: 61 | raise ContractError('Invalid chain') 62 | 63 | # validate API key 64 | if api_key is None or not isinstance(api_key, str) or len(api_key) <= 0: 65 | raise ContractError('Transpose API key is required') 66 | self.api_key = api_key 67 | 68 | # run test query 69 | send_transpose_sql_request( 70 | api_key=self.api_key, 71 | query=latest_block_query('ethereum') 72 | ) 73 | 74 | 75 | def stream_events(self, 76 | event_name: str=None, 77 | start_block: int=None, 78 | end_block: int=None, 79 | order: str='asc', 80 | live_stream: bool=False, 81 | live_refresh_interval: int=3) -> Stream: 82 | 83 | """ 84 | Initiate a stream for contract events. 85 | 86 | :param event_name: The name of the event. 87 | :param start_block: The block to start streaming from, inclusive. 88 | :param end_block: The block to stop streaming at, exclusive. 89 | :param order: The order to stream the events in. 90 | :param live_stream: Whether to stream live data. 91 | :param live_refresh_interval: The interval for refreshing the data in seconds when live. 92 | :return: A Stream object. 93 | """ 94 | 95 | # check order 96 | if order not in ['asc', 'desc']: 97 | raise ContractError('Invalid order (must be one of "asc" or "desc")') 98 | elif order == 'desc' and live_stream: 99 | raise ContractError('Cannot stream in descending order when live') 100 | 101 | # set start and stop blocks 102 | next_block = self.__get_latest_block() + 1 103 | if live_stream: 104 | start_block = min(start_block, next_block) if start_block is not None else next_block 105 | end_block = None 106 | else: 107 | if order == 'asc': 108 | start_block = max(start_block, 0) if start_block is not None else 0 109 | end_block = min(end_block, next_block) if end_block is not None else next_block 110 | if start_block > end_block: raise ContractError('Invalid start and end blocks') 111 | else: 112 | start_block = min(start_block, next_block) if start_block is not None else next_block 113 | end_block = max(end_block, 0) if end_block is not None else 0 114 | if start_block < end_block: raise ContractError('Invalid start and end blocks') 115 | 116 | # return stream 117 | return EventStream( 118 | api_key=self.api_key, 119 | chain=self.chain, 120 | contract_address=self.contract_address, 121 | abi=self.abi, 122 | event_name=event_name, 123 | start_block=start_block, 124 | end_block=end_block, 125 | order=order, 126 | live_stream=live_stream, 127 | live_refresh_interval=live_refresh_interval 128 | ) 129 | 130 | 131 | def stream_calls(self, 132 | function_name: str=None, 133 | start_block: int=None, 134 | end_block: int=None, 135 | order: str='asc', 136 | live_stream: bool=False, 137 | live_refresh_interval: int=3) -> Stream: 138 | 139 | """ 140 | Initiate a stream for contract calls. 141 | 142 | :param function_name: The name of the function. 143 | :param start_block: The block to start streaming from, inclusive. 144 | :param end_block: The block to stop streaming at, exclusive. 145 | :param order: The order to stream the calls in. 146 | :param live_stream: Whether to stream live data. 147 | :param live_refresh_interval: The interval for refreshing the data in seconds when live. 148 | :return: A Stream object. 149 | """ 150 | 151 | # check order 152 | if order not in ['asc', 'desc']: 153 | raise ContractError('Invalid order (must be one of "asc" or "desc")') 154 | elif order == 'desc' and live_stream: 155 | raise ContractError('Cannot stream in descending order when live') 156 | 157 | # set start and stop blocks 158 | next_block = self.__get_latest_block() + 1 159 | if live_stream: 160 | start_block = min(start_block, next_block) if start_block is not None else next_block 161 | end_block = None 162 | else: 163 | if order == 'asc': 164 | start_block = max(start_block, 0) if start_block is not None else 0 165 | end_block = min(end_block, next_block) if end_block is not None else next_block 166 | if start_block > end_block: raise ContractError('Invalid start and end blocks') 167 | else: 168 | start_block = min(start_block, next_block) if start_block is not None else next_block 169 | end_block = max(end_block, 0) if end_block is not None else 0 170 | if start_block < end_block: raise ContractError('Invalid start and end blocks') 171 | 172 | # return stream 173 | return CallStream( 174 | api_key=self.api_key, 175 | chain=self.chain, 176 | contract_address=self.contract_address, 177 | abi=self.abi, 178 | function_name=function_name, 179 | start_block=start_block, 180 | end_block=end_block, 181 | order=order, 182 | live_stream=live_stream, 183 | live_refresh_interval=live_refresh_interval 184 | ) 185 | 186 | 187 | def __get_latest_block(self) -> int: 188 | """ 189 | Get the latest block number for the current chain. 190 | 191 | :return: The latest block number. 192 | """ 193 | 194 | return send_transpose_sql_request( 195 | api_key=self.api_key, 196 | query=latest_block_query(self.chain) 197 | )[0]['block_number'] -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Transpose Decoding SDK 2 | 3 | The Transpose Decoding SDK is a Python package that makes decoding contract activity on EVM blockchains as simple as possible. Simply specify a contract address and ABI to start streaming historical or live activity across decoded logs, transactions, and traces. 4 | 5 | ## Features 6 | 7 | - **Simple interface**: Simple, minimal code interface to stream decoded events or calls. 8 | - **High performance**: Streaming benchmarked at 3,000+ items per second. 9 | - **Automatic decoding**: Automatically decode inputs/outputs for transactions and traces, and logs for events. 10 | - **Live & historical**: Stream activity in a historical block range or live with a ~3s delay from nodes. 11 | - **Full traces support**: Stream decoded traces (a.k.a. internal transactions) with no added complexity. 12 | - **Multi-chain**: Supports Ethereum, Polygon, and Goerli with additional chains coming soon. 13 | - **Event & function filtering**: Stream all activity from a contract or specify event and function names. 14 | 15 | ## Setup 16 | 17 | ### Retrieving an API key 18 | 19 | To use the SDK, you will need an API key for Transpose. You can sign up for a free API key by visting the [Transpose App](https://app.transpose.io). If you have any questions on getting started, feature requests, or contributing to the SDK, please reach out to us on [Discord](http://discord.gg/transpose). 20 | 21 | ### Installation 22 | 23 | To install the package, run the following command in your Python environment: 24 | 25 | ```bash 26 | pip install transpose-decoding-sdk 27 | ``` 28 | 29 | The SDK requires Python 3.6 or higher and has only 4 dependencies: 30 | 31 | - `eth-event` 32 | - `pip-chill` 33 | - `web3` 34 | - `dateutils` 35 | 36 | ## Getting Started 37 | 38 | ### Load a Contract 39 | 40 | The first step in using the SDK is to specify a contract to target. Later, we will stream activity from this contract. To do so, we will import the `TransposeDecodedContract` class and instantiate it with the contract's address, ABI, and chain, as well as a Transpose API key. 41 | 42 | In the example below, we specify the contract address for the OpenSea Seaport contract on Ethereum, as well as the path to its ABI (application binary interface): 43 | 44 | ```python 45 | from transpose.contract import TransposeDecodedContract 46 | 47 | contract = TransposeDecodedContract( 48 | contract_address='0x00000000006c3852cbEf3e08E8dF289169EdE581', 49 | abi_path='abi/opensea-seaport-abi.json', 50 | chain='ethereum', 51 | api_key='YOUR API KEY' 52 | ) 53 | ``` 54 | 55 | If you already have the ABI loaded into your Python application, you can pass it directly to the `abi` parameter instead of specifying a path to the ABI file. 56 | 57 | ### Stream Events 58 | 59 | The event streaming routine will stream and decode events emitted by the contract. To use it, simply use the `stream_events` method to generate a new stream. By default, this will start streaming all events in the ABI from the genesis block and will stop once it reaches the latest block. You can consume the stream with an iterator or by calling `next` with the number of events to return: 60 | 61 | ```python 62 | stream = contract.stream_events() 63 | 64 | # read stream with iterator -> returns a single event per loop 65 | for event in stream: 66 | print(event) 67 | 68 | # read stream with `next` -> returns a list of events 69 | print(stream.next(10)) 70 | ``` 71 | 72 | #### Block Range 73 | 74 | To stream a specific block range, you can specify the `from_block` and `to_block` parameters. The `from_block` parameter is inclusive, while the `to_block` parameter is exclusive. For example, the following code will stream events from block 15M to 16M: 75 | 76 | ```python 77 | stream = contract.stream_events( 78 | from_block=16000000, 79 | to_block=17000000 80 | ) 81 | ``` 82 | 83 | You may also specify descending order to stream in the reverse direction. For example, to stream two batches of 10 events in reverse order from the latest block: 84 | 85 | ```python 86 | stream = contract.stream_events(order='desc') 87 | stream.next(10) 88 | stream.next(10) 89 | ``` 90 | 91 | #### Live Streaming 92 | 93 | In order to stream live data, you can specify the `live_stream` parameter. If you use this parameter with a stream iterator, it will continously stream new events as they are added to the blockchain (with a ~3s delay from nodes): 94 | 95 | ```python 96 | stream = contract.stream_events( 97 | live_stream=True 98 | ) 99 | 100 | # continuously iterate over new events 101 | for event in stream: 102 | print(event) 103 | ``` 104 | 105 | #### Event Filtering 106 | 107 | To stream only a specific event, you can specify the `event_name` parameter. You may combine this with the other parameters to further filter by block range and stream the activity live: 108 | 109 | ```python 110 | stream = contract.stream_events( 111 | event_name='OrderFulfilled, 112 | start_block=16000000, 113 | live_stream=True 114 | ) 115 | ``` 116 | 117 | #### Event Format 118 | 119 | Each event from the stream will be returned as a dictionary with the same structure, containing `target`, `context`, and `event_data` fields. The `target` field contains information about the contract and event that was decoded, while the `context` field contains information about the block and transaction that the event was emitted in. The `event_data` field contains the decoded event data. 120 | 121 | ```python 122 | { 123 | 'item': { 124 | 'contract_address': '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', 125 | 'event_name': 'Transfer' 126 | }, 127 | 'context': { 128 | 'timestamp': datetime.datetime(2018, 1, 10, 12, 32, 45, tzinfo=datetime.timezone.utc), 129 | 'block_number': 4885242, 130 | 'log_index': 35, 131 | 'transaction_hash': '0x12968bdfc268efaae78a2c1193412ee2d0116a29b85182c7f77a476f6bd2b527', 132 | 'transaction_position': 197, 133 | 'confirmed': True 134 | }, 135 | 'event_data': { 136 | 'src': '0x004075e4d4b1ce6c48c81cc940e2bad24b489e64', 137 | 'dst': '0x14fbca95be7e99c15cc2996c6c9d841e54b79425', 138 | 'wad': 8000000000000000000 139 | } 140 | } 141 | ``` 142 | 143 | In the example above, the `context.confirmed` field indicates whether the block containing the event has been confirmed by the network. 144 | 145 | ### Stream Calls 146 | 147 | The call streaming routine will stream and decode transactions and traces (a.k.a. internal transactions) to the contract's functions. To use it, simply use the `stream_calls` method to generate a new stream. By default, this will start streaming all calls in the ABI from the genesis block and will stop once it reaches the latest block. You can consume the stream with an iterator or by calling `next` with the number of calls to return: 148 | 149 | ```python 150 | stream = contract.stream_calls() 151 | 152 | # read stream with iterator -> returns a single call per loop 153 | for call in stream: 154 | print(call) 155 | 156 | # read stream with `next` -> returns a list of calls 157 | print(stream.next(10)) 158 | ``` 159 | 160 | #### Block Range 161 | 162 | To stream a specific block range, you can specify the `from_block` and `to_block` parameters. The `from_block` parameter is inclusive, while the `to_block` parameter is exclusive. For example, the following code will stream calls from block 15M to 16M: 163 | 164 | ```python 165 | stream = contract.stream_calls( 166 | from_block=16000000, 167 | to_block=17000000 168 | ) 169 | ``` 170 | 171 | #### Live Streaming 172 | 173 | In order to stream live data, you can specify the `live_stream` parameter. If you use this parameter with a stream iterator, it will continously stream new calls as they are added to the blockchain (with a ~3s delay from nodes): 174 | 175 | ```python 176 | stream = contract.stream_calls( 177 | live_stream=True 178 | ) 179 | 180 | # continuously iterate over new calls 181 | for call in stream: 182 | print(call) 183 | ``` 184 | 185 | #### Call Filtering 186 | 187 | To stream only a specific function call, for both transactions and internal transactions, you can specify the `function_name` parameter. You may combine this with the other parameters to further filter by block range and stream the activity live: 188 | 189 | ```python 190 | stream = contract.stream_calls( 191 | function_name='transfer', 192 | start_block=16000000, 193 | live_stream=True 194 | ) 195 | ``` 196 | 197 | You may also specify descending order to stream in the reverse direction. For example, to stream two batches of 10 calls in reverse order from the latest block: 198 | 199 | ```python 200 | stream = contract.stream_calls(order='desc') 201 | stream.next(10) 202 | stream.next(10) 203 | ``` 204 | 205 | #### Call Format 206 | 207 | Each call from the stream will be returned as a dictionary with the same structure, containing `target`, `context`, `call_data`, `input_data`, and `output_data` fields. The `target` field contains information about the contract and function that was decoded, while the `context` field contains information about the block, transaction, and trace that the call was made in. The `call_data` field contains the decoded call data, while the `input_data` and `output_data` fields contain the decoded input and output data, respectively. 208 | 209 | ```python 210 | { 211 | 'item': { 212 | 'contract_address': '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', 213 | 'function_name': 'withdraw' 214 | }, 215 | 'context': { 216 | 'timestamp': datetime.datetime(2022, 6, 21, 2, 28, 20, tzinfo=datetime.timezone.utc), 217 | 'block_number': 15000000, 218 | 'transaction_hash': '0x3b5abcc2e67901638b944f5db15f5b13231b5392a6d6c4115407d9106136ac2f', 219 | 'transaction_position': 5, 220 | 'trace_index': 5, 221 | 'trace_address': [0, 2, 0], 222 | 'trace_type': 'call', 223 | 'confirmed': True 224 | }, 225 | 'call_data': { 226 | 'type': 'internal_transaction', 227 | 'from_address': '0x2339d36BCe71c97772e54C76fF6b4C74C9DD8f86', 228 | 'to_address': '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', 229 | 'eth_value': 0 230 | }, 231 | 'input_data': { 232 | 'wad': 6640125949176909399 233 | }, 234 | 'output_data': {} 235 | } 236 | ``` 237 | 238 | In the example above, the `context.confirmed` field indicates whether the block containing the event has been confirmed by the network. Additionally, the `call.type` field will either be set to `transaction` or `internal_transaction` depending on whether the call was a transaction or trace, respectively. If the call was a transaction, the `trace_index` will be zero, the `trace_address` will be an empty list, and the `trace_type` will be `call`. 239 | 240 | ## License 241 | 242 | This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details. 243 | 244 | ## Citations 245 | 246 | If you use this library in your research, please cite it with the following: 247 | 248 | ```bibtex 249 | @misc{transpose-decoding-sdk, 250 | author = {Alex Langshur}, 251 | title = {Transpose Decoding SDK}, 252 | year = {2022}, 253 | publisher = {GitHub}, 254 | journal = {GitHub repository}, 255 | howpublished = {\url{https://github.com/TransposeData/transpose-decoding-sdk}}, 256 | commit = {64dc9c870df4326b07009876cdfdf749e882d191} 257 | } 258 | ``` 259 | -------------------------------------------------------------------------------- /abi/opensea-seaport-abi.json: -------------------------------------------------------------------------------- 1 | [{ 2 | "inputs": [{ 3 | "internalType": "address", 4 | "name": "conduitController", 5 | "type": "address" 6 | }], 7 | "stateMutability": "nonpayable", 8 | "type": "constructor" 9 | }, { 10 | "inputs": [], 11 | "name": "BadContractSignature", 12 | "type": "error" 13 | }, { 14 | "inputs": [], 15 | "name": "BadFraction", 16 | "type": "error" 17 | }, { 18 | "inputs": [{ 19 | "internalType": "address", 20 | "name": "token", 21 | "type": "address" 22 | }, { 23 | "internalType": "address", 24 | "name": "from", 25 | "type": "address" 26 | }, { 27 | "internalType": "address", 28 | "name": "to", 29 | "type": "address" 30 | }, { 31 | "internalType": "uint256", 32 | "name": "amount", 33 | "type": "uint256" 34 | }], 35 | "name": "BadReturnValueFromERC20OnTransfer", 36 | "type": "error" 37 | }, { 38 | "inputs": [{ 39 | "internalType": "uint8", 40 | "name": "v", 41 | "type": "uint8" 42 | }], 43 | "name": "BadSignatureV", 44 | "type": "error" 45 | }, { 46 | "inputs": [], 47 | "name": "ConsiderationCriteriaResolverOutOfRange", 48 | "type": "error" 49 | }, { 50 | "inputs": [{ 51 | "internalType": "uint256", 52 | "name": "orderIndex", 53 | "type": "uint256" 54 | }, { 55 | "internalType": "uint256", 56 | "name": "considerationIndex", 57 | "type": "uint256" 58 | }, { 59 | "internalType": "uint256", 60 | "name": "shortfallAmount", 61 | "type": "uint256" 62 | }], 63 | "name": "ConsiderationNotMet", 64 | "type": "error" 65 | }, { 66 | "inputs": [], 67 | "name": "CriteriaNotEnabledForItem", 68 | "type": "error" 69 | }, { 70 | "inputs": [{ 71 | "internalType": "address", 72 | "name": "token", 73 | "type": "address" 74 | }, { 75 | "internalType": "address", 76 | "name": "from", 77 | "type": "address" 78 | }, { 79 | "internalType": "address", 80 | "name": "to", 81 | "type": "address" 82 | }, { 83 | "internalType": "uint256[]", 84 | "name": "identifiers", 85 | "type": "uint256[]" 86 | }, { 87 | "internalType": "uint256[]", 88 | "name": "amounts", 89 | "type": "uint256[]" 90 | }], 91 | "name": "ERC1155BatchTransferGenericFailure", 92 | "type": "error" 93 | }, { 94 | "inputs": [{ 95 | "internalType": "address", 96 | "name": "account", 97 | "type": "address" 98 | }, { 99 | "internalType": "uint256", 100 | "name": "amount", 101 | "type": "uint256" 102 | }], 103 | "name": "EtherTransferGenericFailure", 104 | "type": "error" 105 | }, { 106 | "inputs": [], 107 | "name": "InexactFraction", 108 | "type": "error" 109 | }, { 110 | "inputs": [], 111 | "name": "InsufficientEtherSupplied", 112 | "type": "error" 113 | }, { 114 | "inputs": [], 115 | "name": "Invalid1155BatchTransferEncoding", 116 | "type": "error" 117 | }, { 118 | "inputs": [], 119 | "name": "InvalidBasicOrderParameterEncoding", 120 | "type": "error" 121 | }, { 122 | "inputs": [{ 123 | "internalType": "address", 124 | "name": "conduit", 125 | "type": "address" 126 | }], 127 | "name": "InvalidCallToConduit", 128 | "type": "error" 129 | }, { 130 | "inputs": [], 131 | "name": "InvalidCanceller", 132 | "type": "error" 133 | }, { 134 | "inputs": [{ 135 | "internalType": "bytes32", 136 | "name": "conduitKey", 137 | "type": "bytes32" 138 | }, { 139 | "internalType": "address", 140 | "name": "conduit", 141 | "type": "address" 142 | }], 143 | "name": "InvalidConduit", 144 | "type": "error" 145 | }, { 146 | "inputs": [], 147 | "name": "InvalidERC721TransferAmount", 148 | "type": "error" 149 | }, { 150 | "inputs": [], 151 | "name": "InvalidFulfillmentComponentData", 152 | "type": "error" 153 | }, { 154 | "inputs": [{ 155 | "internalType": "uint256", 156 | "name": "value", 157 | "type": "uint256" 158 | }], 159 | "name": "InvalidMsgValue", 160 | "type": "error" 161 | }, { 162 | "inputs": [], 163 | "name": "InvalidNativeOfferItem", 164 | "type": "error" 165 | }, { 166 | "inputs": [], 167 | "name": "InvalidProof", 168 | "type": "error" 169 | }, { 170 | "inputs": [{ 171 | "internalType": "bytes32", 172 | "name": "orderHash", 173 | "type": "bytes32" 174 | }], 175 | "name": "InvalidRestrictedOrder", 176 | "type": "error" 177 | }, { 178 | "inputs": [], 179 | "name": "InvalidSignature", 180 | "type": "error" 181 | }, { 182 | "inputs": [], 183 | "name": "InvalidSigner", 184 | "type": "error" 185 | }, { 186 | "inputs": [], 187 | "name": "InvalidTime", 188 | "type": "error" 189 | }, { 190 | "inputs": [], 191 | "name": "MismatchedFulfillmentOfferAndConsiderationComponents", 192 | "type": "error" 193 | }, { 194 | "inputs": [{ 195 | "internalType": "enum Side", 196 | "name": "side", 197 | "type": "uint8" 198 | }], 199 | "name": "MissingFulfillmentComponentOnAggregation", 200 | "type": "error" 201 | }, { 202 | "inputs": [], 203 | "name": "MissingItemAmount", 204 | "type": "error" 205 | }, { 206 | "inputs": [], 207 | "name": "MissingOriginalConsiderationItems", 208 | "type": "error" 209 | }, { 210 | "inputs": [{ 211 | "internalType": "address", 212 | "name": "account", 213 | "type": "address" 214 | }], 215 | "name": "NoContract", 216 | "type": "error" 217 | }, { 218 | "inputs": [], 219 | "name": "NoReentrantCalls", 220 | "type": "error" 221 | }, { 222 | "inputs": [], 223 | "name": "NoSpecifiedOrdersAvailable", 224 | "type": "error" 225 | }, { 226 | "inputs": [], 227 | "name": "OfferAndConsiderationRequiredOnFulfillment", 228 | "type": "error" 229 | }, { 230 | "inputs": [], 231 | "name": "OfferCriteriaResolverOutOfRange", 232 | "type": "error" 233 | }, { 234 | "inputs": [{ 235 | "internalType": "bytes32", 236 | "name": "orderHash", 237 | "type": "bytes32" 238 | }], 239 | "name": "OrderAlreadyFilled", 240 | "type": "error" 241 | }, { 242 | "inputs": [], 243 | "name": "OrderCriteriaResolverOutOfRange", 244 | "type": "error" 245 | }, { 246 | "inputs": [{ 247 | "internalType": "bytes32", 248 | "name": "orderHash", 249 | "type": "bytes32" 250 | }], 251 | "name": "OrderIsCancelled", 252 | "type": "error" 253 | }, { 254 | "inputs": [{ 255 | "internalType": "bytes32", 256 | "name": "orderHash", 257 | "type": "bytes32" 258 | }], 259 | "name": "OrderPartiallyFilled", 260 | "type": "error" 261 | }, { 262 | "inputs": [], 263 | "name": "PartialFillsNotEnabledForOrder", 264 | "type": "error" 265 | }, { 266 | "inputs": [{ 267 | "internalType": "address", 268 | "name": "token", 269 | "type": "address" 270 | }, { 271 | "internalType": "address", 272 | "name": "from", 273 | "type": "address" 274 | }, { 275 | "internalType": "address", 276 | "name": "to", 277 | "type": "address" 278 | }, { 279 | "internalType": "uint256", 280 | "name": "identifier", 281 | "type": "uint256" 282 | }, { 283 | "internalType": "uint256", 284 | "name": "amount", 285 | "type": "uint256" 286 | }], 287 | "name": "TokenTransferGenericFailure", 288 | "type": "error" 289 | }, { 290 | "inputs": [], 291 | "name": "UnresolvedConsiderationCriteria", 292 | "type": "error" 293 | }, { 294 | "inputs": [], 295 | "name": "UnresolvedOfferCriteria", 296 | "type": "error" 297 | }, { 298 | "inputs": [], 299 | "name": "UnusedItemParameters", 300 | "type": "error" 301 | }, { 302 | "anonymous": false, 303 | "inputs": [{ 304 | "indexed": false, 305 | "internalType": "uint256", 306 | "name": "newCounter", 307 | "type": "uint256" 308 | }, { 309 | "indexed": true, 310 | "internalType": "address", 311 | "name": "offerer", 312 | "type": "address" 313 | }], 314 | "name": "CounterIncremented", 315 | "type": "event" 316 | }, { 317 | "anonymous": false, 318 | "inputs": [{ 319 | "indexed": false, 320 | "internalType": "bytes32", 321 | "name": "orderHash", 322 | "type": "bytes32" 323 | }, { 324 | "indexed": true, 325 | "internalType": "address", 326 | "name": "offerer", 327 | "type": "address" 328 | }, { 329 | "indexed": true, 330 | "internalType": "address", 331 | "name": "zone", 332 | "type": "address" 333 | }], 334 | "name": "OrderCancelled", 335 | "type": "event" 336 | }, { 337 | "anonymous": false, 338 | "inputs": [{ 339 | "indexed": false, 340 | "internalType": "bytes32", 341 | "name": "orderHash", 342 | "type": "bytes32" 343 | }, { 344 | "indexed": true, 345 | "internalType": "address", 346 | "name": "offerer", 347 | "type": "address" 348 | }, { 349 | "indexed": true, 350 | "internalType": "address", 351 | "name": "zone", 352 | "type": "address" 353 | }, { 354 | "indexed": false, 355 | "internalType": "address", 356 | "name": "recipient", 357 | "type": "address" 358 | }, { 359 | "components": [{ 360 | "internalType": "enum ItemType", 361 | "name": "itemType", 362 | "type": "uint8" 363 | }, { 364 | "internalType": "address", 365 | "name": "token", 366 | "type": "address" 367 | }, { 368 | "internalType": "uint256", 369 | "name": "identifier", 370 | "type": "uint256" 371 | }, { 372 | "internalType": "uint256", 373 | "name": "amount", 374 | "type": "uint256" 375 | }], 376 | "indexed": false, 377 | "internalType": "struct SpentItem[]", 378 | "name": "offer", 379 | "type": "tuple[]" 380 | }, { 381 | "components": [{ 382 | "internalType": "enum ItemType", 383 | "name": "itemType", 384 | "type": "uint8" 385 | }, { 386 | "internalType": "address", 387 | "name": "token", 388 | "type": "address" 389 | }, { 390 | "internalType": "uint256", 391 | "name": "identifier", 392 | "type": "uint256" 393 | }, { 394 | "internalType": "uint256", 395 | "name": "amount", 396 | "type": "uint256" 397 | }, { 398 | "internalType": "address payable", 399 | "name": "recipient", 400 | "type": "address" 401 | }], 402 | "indexed": false, 403 | "internalType": "struct ReceivedItem[]", 404 | "name": "consideration", 405 | "type": "tuple[]" 406 | }], 407 | "name": "OrderFulfilled", 408 | "type": "event" 409 | }, { 410 | "anonymous": false, 411 | "inputs": [{ 412 | "indexed": false, 413 | "internalType": "bytes32", 414 | "name": "orderHash", 415 | "type": "bytes32" 416 | }, { 417 | "indexed": true, 418 | "internalType": "address", 419 | "name": "offerer", 420 | "type": "address" 421 | }, { 422 | "indexed": true, 423 | "internalType": "address", 424 | "name": "zone", 425 | "type": "address" 426 | }], 427 | "name": "OrderValidated", 428 | "type": "event" 429 | }, { 430 | "inputs": [{ 431 | "components": [{ 432 | "internalType": "address", 433 | "name": "offerer", 434 | "type": "address" 435 | }, { 436 | "internalType": "address", 437 | "name": "zone", 438 | "type": "address" 439 | }, { 440 | "components": [{ 441 | "internalType": "enum ItemType", 442 | "name": "itemType", 443 | "type": "uint8" 444 | }, { 445 | "internalType": "address", 446 | "name": "token", 447 | "type": "address" 448 | }, { 449 | "internalType": "uint256", 450 | "name": "identifierOrCriteria", 451 | "type": "uint256" 452 | }, { 453 | "internalType": "uint256", 454 | "name": "startAmount", 455 | "type": "uint256" 456 | }, { 457 | "internalType": "uint256", 458 | "name": "endAmount", 459 | "type": "uint256" 460 | }], 461 | "internalType": "struct OfferItem[]", 462 | "name": "offer", 463 | "type": "tuple[]" 464 | }, { 465 | "components": [{ 466 | "internalType": "enum ItemType", 467 | "name": "itemType", 468 | "type": "uint8" 469 | }, { 470 | "internalType": "address", 471 | "name": "token", 472 | "type": "address" 473 | }, { 474 | "internalType": "uint256", 475 | "name": "identifierOrCriteria", 476 | "type": "uint256" 477 | }, { 478 | "internalType": "uint256", 479 | "name": "startAmount", 480 | "type": "uint256" 481 | }, { 482 | "internalType": "uint256", 483 | "name": "endAmount", 484 | "type": "uint256" 485 | }, { 486 | "internalType": "address payable", 487 | "name": "recipient", 488 | "type": "address" 489 | }], 490 | "internalType": "struct ConsiderationItem[]", 491 | "name": "consideration", 492 | "type": "tuple[]" 493 | }, { 494 | "internalType": "enum OrderType", 495 | "name": "orderType", 496 | "type": "uint8" 497 | }, { 498 | "internalType": "uint256", 499 | "name": "startTime", 500 | "type": "uint256" 501 | }, { 502 | "internalType": "uint256", 503 | "name": "endTime", 504 | "type": "uint256" 505 | }, { 506 | "internalType": "bytes32", 507 | "name": "zoneHash", 508 | "type": "bytes32" 509 | }, { 510 | "internalType": "uint256", 511 | "name": "salt", 512 | "type": "uint256" 513 | }, { 514 | "internalType": "bytes32", 515 | "name": "conduitKey", 516 | "type": "bytes32" 517 | }, { 518 | "internalType": "uint256", 519 | "name": "counter", 520 | "type": "uint256" 521 | }], 522 | "internalType": "struct OrderComponents[]", 523 | "name": "orders", 524 | "type": "tuple[]" 525 | }], 526 | "name": "cancel", 527 | "outputs": [{ 528 | "internalType": "bool", 529 | "name": "cancelled", 530 | "type": "bool" 531 | }], 532 | "stateMutability": "nonpayable", 533 | "type": "function" 534 | }, { 535 | "inputs": [{ 536 | "components": [{ 537 | "components": [{ 538 | "internalType": "address", 539 | "name": "offerer", 540 | "type": "address" 541 | }, { 542 | "internalType": "address", 543 | "name": "zone", 544 | "type": "address" 545 | }, { 546 | "components": [{ 547 | "internalType": "enum ItemType", 548 | "name": "itemType", 549 | "type": "uint8" 550 | }, { 551 | "internalType": "address", 552 | "name": "token", 553 | "type": "address" 554 | }, { 555 | "internalType": "uint256", 556 | "name": "identifierOrCriteria", 557 | "type": "uint256" 558 | }, { 559 | "internalType": "uint256", 560 | "name": "startAmount", 561 | "type": "uint256" 562 | }, { 563 | "internalType": "uint256", 564 | "name": "endAmount", 565 | "type": "uint256" 566 | }], 567 | "internalType": "struct OfferItem[]", 568 | "name": "offer", 569 | "type": "tuple[]" 570 | }, { 571 | "components": [{ 572 | "internalType": "enum ItemType", 573 | "name": "itemType", 574 | "type": "uint8" 575 | }, { 576 | "internalType": "address", 577 | "name": "token", 578 | "type": "address" 579 | }, { 580 | "internalType": "uint256", 581 | "name": "identifierOrCriteria", 582 | "type": "uint256" 583 | }, { 584 | "internalType": "uint256", 585 | "name": "startAmount", 586 | "type": "uint256" 587 | }, { 588 | "internalType": "uint256", 589 | "name": "endAmount", 590 | "type": "uint256" 591 | }, { 592 | "internalType": "address payable", 593 | "name": "recipient", 594 | "type": "address" 595 | }], 596 | "internalType": "struct ConsiderationItem[]", 597 | "name": "consideration", 598 | "type": "tuple[]" 599 | }, { 600 | "internalType": "enum OrderType", 601 | "name": "orderType", 602 | "type": "uint8" 603 | }, { 604 | "internalType": "uint256", 605 | "name": "startTime", 606 | "type": "uint256" 607 | }, { 608 | "internalType": "uint256", 609 | "name": "endTime", 610 | "type": "uint256" 611 | }, { 612 | "internalType": "bytes32", 613 | "name": "zoneHash", 614 | "type": "bytes32" 615 | }, { 616 | "internalType": "uint256", 617 | "name": "salt", 618 | "type": "uint256" 619 | }, { 620 | "internalType": "bytes32", 621 | "name": "conduitKey", 622 | "type": "bytes32" 623 | }, { 624 | "internalType": "uint256", 625 | "name": "totalOriginalConsiderationItems", 626 | "type": "uint256" 627 | }], 628 | "internalType": "struct OrderParameters", 629 | "name": "parameters", 630 | "type": "tuple" 631 | }, { 632 | "internalType": "uint120", 633 | "name": "numerator", 634 | "type": "uint120" 635 | }, { 636 | "internalType": "uint120", 637 | "name": "denominator", 638 | "type": "uint120" 639 | }, { 640 | "internalType": "bytes", 641 | "name": "signature", 642 | "type": "bytes" 643 | }, { 644 | "internalType": "bytes", 645 | "name": "extraData", 646 | "type": "bytes" 647 | }], 648 | "internalType": "struct AdvancedOrder", 649 | "name": "advancedOrder", 650 | "type": "tuple" 651 | }, { 652 | "components": [{ 653 | "internalType": "uint256", 654 | "name": "orderIndex", 655 | "type": "uint256" 656 | }, { 657 | "internalType": "enum Side", 658 | "name": "side", 659 | "type": "uint8" 660 | }, { 661 | "internalType": "uint256", 662 | "name": "index", 663 | "type": "uint256" 664 | }, { 665 | "internalType": "uint256", 666 | "name": "identifier", 667 | "type": "uint256" 668 | }, { 669 | "internalType": "bytes32[]", 670 | "name": "criteriaProof", 671 | "type": "bytes32[]" 672 | }], 673 | "internalType": "struct CriteriaResolver[]", 674 | "name": "criteriaResolvers", 675 | "type": "tuple[]" 676 | }, { 677 | "internalType": "bytes32", 678 | "name": "fulfillerConduitKey", 679 | "type": "bytes32" 680 | }, { 681 | "internalType": "address", 682 | "name": "recipient", 683 | "type": "address" 684 | }], 685 | "name": "fulfillAdvancedOrder", 686 | "outputs": [{ 687 | "internalType": "bool", 688 | "name": "fulfilled", 689 | "type": "bool" 690 | }], 691 | "stateMutability": "payable", 692 | "type": "function" 693 | }, { 694 | "inputs": [{ 695 | "components": [{ 696 | "components": [{ 697 | "internalType": "address", 698 | "name": "offerer", 699 | "type": "address" 700 | }, { 701 | "internalType": "address", 702 | "name": "zone", 703 | "type": "address" 704 | }, { 705 | "components": [{ 706 | "internalType": "enum ItemType", 707 | "name": "itemType", 708 | "type": "uint8" 709 | }, { 710 | "internalType": "address", 711 | "name": "token", 712 | "type": "address" 713 | }, { 714 | "internalType": "uint256", 715 | "name": "identifierOrCriteria", 716 | "type": "uint256" 717 | }, { 718 | "internalType": "uint256", 719 | "name": "startAmount", 720 | "type": "uint256" 721 | }, { 722 | "internalType": "uint256", 723 | "name": "endAmount", 724 | "type": "uint256" 725 | }], 726 | "internalType": "struct OfferItem[]", 727 | "name": "offer", 728 | "type": "tuple[]" 729 | }, { 730 | "components": [{ 731 | "internalType": "enum ItemType", 732 | "name": "itemType", 733 | "type": "uint8" 734 | }, { 735 | "internalType": "address", 736 | "name": "token", 737 | "type": "address" 738 | }, { 739 | "internalType": "uint256", 740 | "name": "identifierOrCriteria", 741 | "type": "uint256" 742 | }, { 743 | "internalType": "uint256", 744 | "name": "startAmount", 745 | "type": "uint256" 746 | }, { 747 | "internalType": "uint256", 748 | "name": "endAmount", 749 | "type": "uint256" 750 | }, { 751 | "internalType": "address payable", 752 | "name": "recipient", 753 | "type": "address" 754 | }], 755 | "internalType": "struct ConsiderationItem[]", 756 | "name": "consideration", 757 | "type": "tuple[]" 758 | }, { 759 | "internalType": "enum OrderType", 760 | "name": "orderType", 761 | "type": "uint8" 762 | }, { 763 | "internalType": "uint256", 764 | "name": "startTime", 765 | "type": "uint256" 766 | }, { 767 | "internalType": "uint256", 768 | "name": "endTime", 769 | "type": "uint256" 770 | }, { 771 | "internalType": "bytes32", 772 | "name": "zoneHash", 773 | "type": "bytes32" 774 | }, { 775 | "internalType": "uint256", 776 | "name": "salt", 777 | "type": "uint256" 778 | }, { 779 | "internalType": "bytes32", 780 | "name": "conduitKey", 781 | "type": "bytes32" 782 | }, { 783 | "internalType": "uint256", 784 | "name": "totalOriginalConsiderationItems", 785 | "type": "uint256" 786 | }], 787 | "internalType": "struct OrderParameters", 788 | "name": "parameters", 789 | "type": "tuple" 790 | }, { 791 | "internalType": "uint120", 792 | "name": "numerator", 793 | "type": "uint120" 794 | }, { 795 | "internalType": "uint120", 796 | "name": "denominator", 797 | "type": "uint120" 798 | }, { 799 | "internalType": "bytes", 800 | "name": "signature", 801 | "type": "bytes" 802 | }, { 803 | "internalType": "bytes", 804 | "name": "extraData", 805 | "type": "bytes" 806 | }], 807 | "internalType": "struct AdvancedOrder[]", 808 | "name": "advancedOrders", 809 | "type": "tuple[]" 810 | }, { 811 | "components": [{ 812 | "internalType": "uint256", 813 | "name": "orderIndex", 814 | "type": "uint256" 815 | }, { 816 | "internalType": "enum Side", 817 | "name": "side", 818 | "type": "uint8" 819 | }, { 820 | "internalType": "uint256", 821 | "name": "index", 822 | "type": "uint256" 823 | }, { 824 | "internalType": "uint256", 825 | "name": "identifier", 826 | "type": "uint256" 827 | }, { 828 | "internalType": "bytes32[]", 829 | "name": "criteriaProof", 830 | "type": "bytes32[]" 831 | }], 832 | "internalType": "struct CriteriaResolver[]", 833 | "name": "criteriaResolvers", 834 | "type": "tuple[]" 835 | }, { 836 | "components": [{ 837 | "internalType": "uint256", 838 | "name": "orderIndex", 839 | "type": "uint256" 840 | }, { 841 | "internalType": "uint256", 842 | "name": "itemIndex", 843 | "type": "uint256" 844 | }], 845 | "internalType": "struct FulfillmentComponent[][]", 846 | "name": "offerFulfillments", 847 | "type": "tuple[][]" 848 | }, { 849 | "components": [{ 850 | "internalType": "uint256", 851 | "name": "orderIndex", 852 | "type": "uint256" 853 | }, { 854 | "internalType": "uint256", 855 | "name": "itemIndex", 856 | "type": "uint256" 857 | }], 858 | "internalType": "struct FulfillmentComponent[][]", 859 | "name": "considerationFulfillments", 860 | "type": "tuple[][]" 861 | }, { 862 | "internalType": "bytes32", 863 | "name": "fulfillerConduitKey", 864 | "type": "bytes32" 865 | }, { 866 | "internalType": "address", 867 | "name": "recipient", 868 | "type": "address" 869 | }, { 870 | "internalType": "uint256", 871 | "name": "maximumFulfilled", 872 | "type": "uint256" 873 | }], 874 | "name": "fulfillAvailableAdvancedOrders", 875 | "outputs": [{ 876 | "internalType": "bool[]", 877 | "name": "availableOrders", 878 | "type": "bool[]" 879 | }, { 880 | "components": [{ 881 | "components": [{ 882 | "internalType": "enum ItemType", 883 | "name": "itemType", 884 | "type": "uint8" 885 | }, { 886 | "internalType": "address", 887 | "name": "token", 888 | "type": "address" 889 | }, { 890 | "internalType": "uint256", 891 | "name": "identifier", 892 | "type": "uint256" 893 | }, { 894 | "internalType": "uint256", 895 | "name": "amount", 896 | "type": "uint256" 897 | }, { 898 | "internalType": "address payable", 899 | "name": "recipient", 900 | "type": "address" 901 | }], 902 | "internalType": "struct ReceivedItem", 903 | "name": "item", 904 | "type": "tuple" 905 | }, { 906 | "internalType": "address", 907 | "name": "offerer", 908 | "type": "address" 909 | }, { 910 | "internalType": "bytes32", 911 | "name": "conduitKey", 912 | "type": "bytes32" 913 | }], 914 | "internalType": "struct Execution[]", 915 | "name": "executions", 916 | "type": "tuple[]" 917 | }], 918 | "stateMutability": "payable", 919 | "type": "function" 920 | }, { 921 | "inputs": [{ 922 | "components": [{ 923 | "components": [{ 924 | "internalType": "address", 925 | "name": "offerer", 926 | "type": "address" 927 | }, { 928 | "internalType": "address", 929 | "name": "zone", 930 | "type": "address" 931 | }, { 932 | "components": [{ 933 | "internalType": "enum ItemType", 934 | "name": "itemType", 935 | "type": "uint8" 936 | }, { 937 | "internalType": "address", 938 | "name": "token", 939 | "type": "address" 940 | }, { 941 | "internalType": "uint256", 942 | "name": "identifierOrCriteria", 943 | "type": "uint256" 944 | }, { 945 | "internalType": "uint256", 946 | "name": "startAmount", 947 | "type": "uint256" 948 | }, { 949 | "internalType": "uint256", 950 | "name": "endAmount", 951 | "type": "uint256" 952 | }], 953 | "internalType": "struct OfferItem[]", 954 | "name": "offer", 955 | "type": "tuple[]" 956 | }, { 957 | "components": [{ 958 | "internalType": "enum ItemType", 959 | "name": "itemType", 960 | "type": "uint8" 961 | }, { 962 | "internalType": "address", 963 | "name": "token", 964 | "type": "address" 965 | }, { 966 | "internalType": "uint256", 967 | "name": "identifierOrCriteria", 968 | "type": "uint256" 969 | }, { 970 | "internalType": "uint256", 971 | "name": "startAmount", 972 | "type": "uint256" 973 | }, { 974 | "internalType": "uint256", 975 | "name": "endAmount", 976 | "type": "uint256" 977 | }, { 978 | "internalType": "address payable", 979 | "name": "recipient", 980 | "type": "address" 981 | }], 982 | "internalType": "struct ConsiderationItem[]", 983 | "name": "consideration", 984 | "type": "tuple[]" 985 | }, { 986 | "internalType": "enum OrderType", 987 | "name": "orderType", 988 | "type": "uint8" 989 | }, { 990 | "internalType": "uint256", 991 | "name": "startTime", 992 | "type": "uint256" 993 | }, { 994 | "internalType": "uint256", 995 | "name": "endTime", 996 | "type": "uint256" 997 | }, { 998 | "internalType": "bytes32", 999 | "name": "zoneHash", 1000 | "type": "bytes32" 1001 | }, { 1002 | "internalType": "uint256", 1003 | "name": "salt", 1004 | "type": "uint256" 1005 | }, { 1006 | "internalType": "bytes32", 1007 | "name": "conduitKey", 1008 | "type": "bytes32" 1009 | }, { 1010 | "internalType": "uint256", 1011 | "name": "totalOriginalConsiderationItems", 1012 | "type": "uint256" 1013 | }], 1014 | "internalType": "struct OrderParameters", 1015 | "name": "parameters", 1016 | "type": "tuple" 1017 | }, { 1018 | "internalType": "bytes", 1019 | "name": "signature", 1020 | "type": "bytes" 1021 | }], 1022 | "internalType": "struct Order[]", 1023 | "name": "orders", 1024 | "type": "tuple[]" 1025 | }, { 1026 | "components": [{ 1027 | "internalType": "uint256", 1028 | "name": "orderIndex", 1029 | "type": "uint256" 1030 | }, { 1031 | "internalType": "uint256", 1032 | "name": "itemIndex", 1033 | "type": "uint256" 1034 | }], 1035 | "internalType": "struct FulfillmentComponent[][]", 1036 | "name": "offerFulfillments", 1037 | "type": "tuple[][]" 1038 | }, { 1039 | "components": [{ 1040 | "internalType": "uint256", 1041 | "name": "orderIndex", 1042 | "type": "uint256" 1043 | }, { 1044 | "internalType": "uint256", 1045 | "name": "itemIndex", 1046 | "type": "uint256" 1047 | }], 1048 | "internalType": "struct FulfillmentComponent[][]", 1049 | "name": "considerationFulfillments", 1050 | "type": "tuple[][]" 1051 | }, { 1052 | "internalType": "bytes32", 1053 | "name": "fulfillerConduitKey", 1054 | "type": "bytes32" 1055 | }, { 1056 | "internalType": "uint256", 1057 | "name": "maximumFulfilled", 1058 | "type": "uint256" 1059 | }], 1060 | "name": "fulfillAvailableOrders", 1061 | "outputs": [{ 1062 | "internalType": "bool[]", 1063 | "name": "availableOrders", 1064 | "type": "bool[]" 1065 | }, { 1066 | "components": [{ 1067 | "components": [{ 1068 | "internalType": "enum ItemType", 1069 | "name": "itemType", 1070 | "type": "uint8" 1071 | }, { 1072 | "internalType": "address", 1073 | "name": "token", 1074 | "type": "address" 1075 | }, { 1076 | "internalType": "uint256", 1077 | "name": "identifier", 1078 | "type": "uint256" 1079 | }, { 1080 | "internalType": "uint256", 1081 | "name": "amount", 1082 | "type": "uint256" 1083 | }, { 1084 | "internalType": "address payable", 1085 | "name": "recipient", 1086 | "type": "address" 1087 | }], 1088 | "internalType": "struct ReceivedItem", 1089 | "name": "item", 1090 | "type": "tuple" 1091 | }, { 1092 | "internalType": "address", 1093 | "name": "offerer", 1094 | "type": "address" 1095 | }, { 1096 | "internalType": "bytes32", 1097 | "name": "conduitKey", 1098 | "type": "bytes32" 1099 | }], 1100 | "internalType": "struct Execution[]", 1101 | "name": "executions", 1102 | "type": "tuple[]" 1103 | }], 1104 | "stateMutability": "payable", 1105 | "type": "function" 1106 | }, { 1107 | "inputs": [{ 1108 | "components": [{ 1109 | "internalType": "address", 1110 | "name": "considerationToken", 1111 | "type": "address" 1112 | }, { 1113 | "internalType": "uint256", 1114 | "name": "considerationIdentifier", 1115 | "type": "uint256" 1116 | }, { 1117 | "internalType": "uint256", 1118 | "name": "considerationAmount", 1119 | "type": "uint256" 1120 | }, { 1121 | "internalType": "address payable", 1122 | "name": "offerer", 1123 | "type": "address" 1124 | }, { 1125 | "internalType": "address", 1126 | "name": "zone", 1127 | "type": "address" 1128 | }, { 1129 | "internalType": "address", 1130 | "name": "offerToken", 1131 | "type": "address" 1132 | }, { 1133 | "internalType": "uint256", 1134 | "name": "offerIdentifier", 1135 | "type": "uint256" 1136 | }, { 1137 | "internalType": "uint256", 1138 | "name": "offerAmount", 1139 | "type": "uint256" 1140 | }, { 1141 | "internalType": "enum BasicOrderType", 1142 | "name": "basicOrderType", 1143 | "type": "uint8" 1144 | }, { 1145 | "internalType": "uint256", 1146 | "name": "startTime", 1147 | "type": "uint256" 1148 | }, { 1149 | "internalType": "uint256", 1150 | "name": "endTime", 1151 | "type": "uint256" 1152 | }, { 1153 | "internalType": "bytes32", 1154 | "name": "zoneHash", 1155 | "type": "bytes32" 1156 | }, { 1157 | "internalType": "uint256", 1158 | "name": "salt", 1159 | "type": "uint256" 1160 | }, { 1161 | "internalType": "bytes32", 1162 | "name": "offererConduitKey", 1163 | "type": "bytes32" 1164 | }, { 1165 | "internalType": "bytes32", 1166 | "name": "fulfillerConduitKey", 1167 | "type": "bytes32" 1168 | }, { 1169 | "internalType": "uint256", 1170 | "name": "totalOriginalAdditionalRecipients", 1171 | "type": "uint256" 1172 | }, { 1173 | "components": [{ 1174 | "internalType": "uint256", 1175 | "name": "amount", 1176 | "type": "uint256" 1177 | }, { 1178 | "internalType": "address payable", 1179 | "name": "recipient", 1180 | "type": "address" 1181 | }], 1182 | "internalType": "struct AdditionalRecipient[]", 1183 | "name": "additionalRecipients", 1184 | "type": "tuple[]" 1185 | }, { 1186 | "internalType": "bytes", 1187 | "name": "signature", 1188 | "type": "bytes" 1189 | }], 1190 | "internalType": "struct BasicOrderParameters", 1191 | "name": "parameters", 1192 | "type": "tuple" 1193 | }], 1194 | "name": "fulfillBasicOrder", 1195 | "outputs": [{ 1196 | "internalType": "bool", 1197 | "name": "fulfilled", 1198 | "type": "bool" 1199 | }], 1200 | "stateMutability": "payable", 1201 | "type": "function" 1202 | }, { 1203 | "inputs": [{ 1204 | "components": [{ 1205 | "components": [{ 1206 | "internalType": "address", 1207 | "name": "offerer", 1208 | "type": "address" 1209 | }, { 1210 | "internalType": "address", 1211 | "name": "zone", 1212 | "type": "address" 1213 | }, { 1214 | "components": [{ 1215 | "internalType": "enum ItemType", 1216 | "name": "itemType", 1217 | "type": "uint8" 1218 | }, { 1219 | "internalType": "address", 1220 | "name": "token", 1221 | "type": "address" 1222 | }, { 1223 | "internalType": "uint256", 1224 | "name": "identifierOrCriteria", 1225 | "type": "uint256" 1226 | }, { 1227 | "internalType": "uint256", 1228 | "name": "startAmount", 1229 | "type": "uint256" 1230 | }, { 1231 | "internalType": "uint256", 1232 | "name": "endAmount", 1233 | "type": "uint256" 1234 | }], 1235 | "internalType": "struct OfferItem[]", 1236 | "name": "offer", 1237 | "type": "tuple[]" 1238 | }, { 1239 | "components": [{ 1240 | "internalType": "enum ItemType", 1241 | "name": "itemType", 1242 | "type": "uint8" 1243 | }, { 1244 | "internalType": "address", 1245 | "name": "token", 1246 | "type": "address" 1247 | }, { 1248 | "internalType": "uint256", 1249 | "name": "identifierOrCriteria", 1250 | "type": "uint256" 1251 | }, { 1252 | "internalType": "uint256", 1253 | "name": "startAmount", 1254 | "type": "uint256" 1255 | }, { 1256 | "internalType": "uint256", 1257 | "name": "endAmount", 1258 | "type": "uint256" 1259 | }, { 1260 | "internalType": "address payable", 1261 | "name": "recipient", 1262 | "type": "address" 1263 | }], 1264 | "internalType": "struct ConsiderationItem[]", 1265 | "name": "consideration", 1266 | "type": "tuple[]" 1267 | }, { 1268 | "internalType": "enum OrderType", 1269 | "name": "orderType", 1270 | "type": "uint8" 1271 | }, { 1272 | "internalType": "uint256", 1273 | "name": "startTime", 1274 | "type": "uint256" 1275 | }, { 1276 | "internalType": "uint256", 1277 | "name": "endTime", 1278 | "type": "uint256" 1279 | }, { 1280 | "internalType": "bytes32", 1281 | "name": "zoneHash", 1282 | "type": "bytes32" 1283 | }, { 1284 | "internalType": "uint256", 1285 | "name": "salt", 1286 | "type": "uint256" 1287 | }, { 1288 | "internalType": "bytes32", 1289 | "name": "conduitKey", 1290 | "type": "bytes32" 1291 | }, { 1292 | "internalType": "uint256", 1293 | "name": "totalOriginalConsiderationItems", 1294 | "type": "uint256" 1295 | }], 1296 | "internalType": "struct OrderParameters", 1297 | "name": "parameters", 1298 | "type": "tuple" 1299 | }, { 1300 | "internalType": "bytes", 1301 | "name": "signature", 1302 | "type": "bytes" 1303 | }], 1304 | "internalType": "struct Order", 1305 | "name": "order", 1306 | "type": "tuple" 1307 | }, { 1308 | "internalType": "bytes32", 1309 | "name": "fulfillerConduitKey", 1310 | "type": "bytes32" 1311 | }], 1312 | "name": "fulfillOrder", 1313 | "outputs": [{ 1314 | "internalType": "bool", 1315 | "name": "fulfilled", 1316 | "type": "bool" 1317 | }], 1318 | "stateMutability": "payable", 1319 | "type": "function" 1320 | }, { 1321 | "inputs": [{ 1322 | "internalType": "address", 1323 | "name": "offerer", 1324 | "type": "address" 1325 | }], 1326 | "name": "getCounter", 1327 | "outputs": [{ 1328 | "internalType": "uint256", 1329 | "name": "counter", 1330 | "type": "uint256" 1331 | }], 1332 | "stateMutability": "view", 1333 | "type": "function" 1334 | }, { 1335 | "inputs": [{ 1336 | "components": [{ 1337 | "internalType": "address", 1338 | "name": "offerer", 1339 | "type": "address" 1340 | }, { 1341 | "internalType": "address", 1342 | "name": "zone", 1343 | "type": "address" 1344 | }, { 1345 | "components": [{ 1346 | "internalType": "enum ItemType", 1347 | "name": "itemType", 1348 | "type": "uint8" 1349 | }, { 1350 | "internalType": "address", 1351 | "name": "token", 1352 | "type": "address" 1353 | }, { 1354 | "internalType": "uint256", 1355 | "name": "identifierOrCriteria", 1356 | "type": "uint256" 1357 | }, { 1358 | "internalType": "uint256", 1359 | "name": "startAmount", 1360 | "type": "uint256" 1361 | }, { 1362 | "internalType": "uint256", 1363 | "name": "endAmount", 1364 | "type": "uint256" 1365 | }], 1366 | "internalType": "struct OfferItem[]", 1367 | "name": "offer", 1368 | "type": "tuple[]" 1369 | }, { 1370 | "components": [{ 1371 | "internalType": "enum ItemType", 1372 | "name": "itemType", 1373 | "type": "uint8" 1374 | }, { 1375 | "internalType": "address", 1376 | "name": "token", 1377 | "type": "address" 1378 | }, { 1379 | "internalType": "uint256", 1380 | "name": "identifierOrCriteria", 1381 | "type": "uint256" 1382 | }, { 1383 | "internalType": "uint256", 1384 | "name": "startAmount", 1385 | "type": "uint256" 1386 | }, { 1387 | "internalType": "uint256", 1388 | "name": "endAmount", 1389 | "type": "uint256" 1390 | }, { 1391 | "internalType": "address payable", 1392 | "name": "recipient", 1393 | "type": "address" 1394 | }], 1395 | "internalType": "struct ConsiderationItem[]", 1396 | "name": "consideration", 1397 | "type": "tuple[]" 1398 | }, { 1399 | "internalType": "enum OrderType", 1400 | "name": "orderType", 1401 | "type": "uint8" 1402 | }, { 1403 | "internalType": "uint256", 1404 | "name": "startTime", 1405 | "type": "uint256" 1406 | }, { 1407 | "internalType": "uint256", 1408 | "name": "endTime", 1409 | "type": "uint256" 1410 | }, { 1411 | "internalType": "bytes32", 1412 | "name": "zoneHash", 1413 | "type": "bytes32" 1414 | }, { 1415 | "internalType": "uint256", 1416 | "name": "salt", 1417 | "type": "uint256" 1418 | }, { 1419 | "internalType": "bytes32", 1420 | "name": "conduitKey", 1421 | "type": "bytes32" 1422 | }, { 1423 | "internalType": "uint256", 1424 | "name": "counter", 1425 | "type": "uint256" 1426 | }], 1427 | "internalType": "struct OrderComponents", 1428 | "name": "order", 1429 | "type": "tuple" 1430 | }], 1431 | "name": "getOrderHash", 1432 | "outputs": [{ 1433 | "internalType": "bytes32", 1434 | "name": "orderHash", 1435 | "type": "bytes32" 1436 | }], 1437 | "stateMutability": "view", 1438 | "type": "function" 1439 | }, { 1440 | "inputs": [{ 1441 | "internalType": "bytes32", 1442 | "name": "orderHash", 1443 | "type": "bytes32" 1444 | }], 1445 | "name": "getOrderStatus", 1446 | "outputs": [{ 1447 | "internalType": "bool", 1448 | "name": "isValidated", 1449 | "type": "bool" 1450 | }, { 1451 | "internalType": "bool", 1452 | "name": "isCancelled", 1453 | "type": "bool" 1454 | }, { 1455 | "internalType": "uint256", 1456 | "name": "totalFilled", 1457 | "type": "uint256" 1458 | }, { 1459 | "internalType": "uint256", 1460 | "name": "totalSize", 1461 | "type": "uint256" 1462 | }], 1463 | "stateMutability": "view", 1464 | "type": "function" 1465 | }, { 1466 | "inputs": [], 1467 | "name": "incrementCounter", 1468 | "outputs": [{ 1469 | "internalType": "uint256", 1470 | "name": "newCounter", 1471 | "type": "uint256" 1472 | }], 1473 | "stateMutability": "nonpayable", 1474 | "type": "function" 1475 | }, { 1476 | "inputs": [], 1477 | "name": "information", 1478 | "outputs": [{ 1479 | "internalType": "string", 1480 | "name": "version", 1481 | "type": "string" 1482 | }, { 1483 | "internalType": "bytes32", 1484 | "name": "domainSeparator", 1485 | "type": "bytes32" 1486 | }, { 1487 | "internalType": "address", 1488 | "name": "conduitController", 1489 | "type": "address" 1490 | }], 1491 | "stateMutability": "view", 1492 | "type": "function" 1493 | }, { 1494 | "inputs": [{ 1495 | "components": [{ 1496 | "components": [{ 1497 | "internalType": "address", 1498 | "name": "offerer", 1499 | "type": "address" 1500 | }, { 1501 | "internalType": "address", 1502 | "name": "zone", 1503 | "type": "address" 1504 | }, { 1505 | "components": [{ 1506 | "internalType": "enum ItemType", 1507 | "name": "itemType", 1508 | "type": "uint8" 1509 | }, { 1510 | "internalType": "address", 1511 | "name": "token", 1512 | "type": "address" 1513 | }, { 1514 | "internalType": "uint256", 1515 | "name": "identifierOrCriteria", 1516 | "type": "uint256" 1517 | }, { 1518 | "internalType": "uint256", 1519 | "name": "startAmount", 1520 | "type": "uint256" 1521 | }, { 1522 | "internalType": "uint256", 1523 | "name": "endAmount", 1524 | "type": "uint256" 1525 | }], 1526 | "internalType": "struct OfferItem[]", 1527 | "name": "offer", 1528 | "type": "tuple[]" 1529 | }, { 1530 | "components": [{ 1531 | "internalType": "enum ItemType", 1532 | "name": "itemType", 1533 | "type": "uint8" 1534 | }, { 1535 | "internalType": "address", 1536 | "name": "token", 1537 | "type": "address" 1538 | }, { 1539 | "internalType": "uint256", 1540 | "name": "identifierOrCriteria", 1541 | "type": "uint256" 1542 | }, { 1543 | "internalType": "uint256", 1544 | "name": "startAmount", 1545 | "type": "uint256" 1546 | }, { 1547 | "internalType": "uint256", 1548 | "name": "endAmount", 1549 | "type": "uint256" 1550 | }, { 1551 | "internalType": "address payable", 1552 | "name": "recipient", 1553 | "type": "address" 1554 | }], 1555 | "internalType": "struct ConsiderationItem[]", 1556 | "name": "consideration", 1557 | "type": "tuple[]" 1558 | }, { 1559 | "internalType": "enum OrderType", 1560 | "name": "orderType", 1561 | "type": "uint8" 1562 | }, { 1563 | "internalType": "uint256", 1564 | "name": "startTime", 1565 | "type": "uint256" 1566 | }, { 1567 | "internalType": "uint256", 1568 | "name": "endTime", 1569 | "type": "uint256" 1570 | }, { 1571 | "internalType": "bytes32", 1572 | "name": "zoneHash", 1573 | "type": "bytes32" 1574 | }, { 1575 | "internalType": "uint256", 1576 | "name": "salt", 1577 | "type": "uint256" 1578 | }, { 1579 | "internalType": "bytes32", 1580 | "name": "conduitKey", 1581 | "type": "bytes32" 1582 | }, { 1583 | "internalType": "uint256", 1584 | "name": "totalOriginalConsiderationItems", 1585 | "type": "uint256" 1586 | }], 1587 | "internalType": "struct OrderParameters", 1588 | "name": "parameters", 1589 | "type": "tuple" 1590 | }, { 1591 | "internalType": "uint120", 1592 | "name": "numerator", 1593 | "type": "uint120" 1594 | }, { 1595 | "internalType": "uint120", 1596 | "name": "denominator", 1597 | "type": "uint120" 1598 | }, { 1599 | "internalType": "bytes", 1600 | "name": "signature", 1601 | "type": "bytes" 1602 | }, { 1603 | "internalType": "bytes", 1604 | "name": "extraData", 1605 | "type": "bytes" 1606 | }], 1607 | "internalType": "struct AdvancedOrder[]", 1608 | "name": "advancedOrders", 1609 | "type": "tuple[]" 1610 | }, { 1611 | "components": [{ 1612 | "internalType": "uint256", 1613 | "name": "orderIndex", 1614 | "type": "uint256" 1615 | }, { 1616 | "internalType": "enum Side", 1617 | "name": "side", 1618 | "type": "uint8" 1619 | }, { 1620 | "internalType": "uint256", 1621 | "name": "index", 1622 | "type": "uint256" 1623 | }, { 1624 | "internalType": "uint256", 1625 | "name": "identifier", 1626 | "type": "uint256" 1627 | }, { 1628 | "internalType": "bytes32[]", 1629 | "name": "criteriaProof", 1630 | "type": "bytes32[]" 1631 | }], 1632 | "internalType": "struct CriteriaResolver[]", 1633 | "name": "criteriaResolvers", 1634 | "type": "tuple[]" 1635 | }, { 1636 | "components": [{ 1637 | "components": [{ 1638 | "internalType": "uint256", 1639 | "name": "orderIndex", 1640 | "type": "uint256" 1641 | }, { 1642 | "internalType": "uint256", 1643 | "name": "itemIndex", 1644 | "type": "uint256" 1645 | }], 1646 | "internalType": "struct FulfillmentComponent[]", 1647 | "name": "offerComponents", 1648 | "type": "tuple[]" 1649 | }, { 1650 | "components": [{ 1651 | "internalType": "uint256", 1652 | "name": "orderIndex", 1653 | "type": "uint256" 1654 | }, { 1655 | "internalType": "uint256", 1656 | "name": "itemIndex", 1657 | "type": "uint256" 1658 | }], 1659 | "internalType": "struct FulfillmentComponent[]", 1660 | "name": "considerationComponents", 1661 | "type": "tuple[]" 1662 | }], 1663 | "internalType": "struct Fulfillment[]", 1664 | "name": "fulfillments", 1665 | "type": "tuple[]" 1666 | }], 1667 | "name": "matchAdvancedOrders", 1668 | "outputs": [{ 1669 | "components": [{ 1670 | "components": [{ 1671 | "internalType": "enum ItemType", 1672 | "name": "itemType", 1673 | "type": "uint8" 1674 | }, { 1675 | "internalType": "address", 1676 | "name": "token", 1677 | "type": "address" 1678 | }, { 1679 | "internalType": "uint256", 1680 | "name": "identifier", 1681 | "type": "uint256" 1682 | }, { 1683 | "internalType": "uint256", 1684 | "name": "amount", 1685 | "type": "uint256" 1686 | }, { 1687 | "internalType": "address payable", 1688 | "name": "recipient", 1689 | "type": "address" 1690 | }], 1691 | "internalType": "struct ReceivedItem", 1692 | "name": "item", 1693 | "type": "tuple" 1694 | }, { 1695 | "internalType": "address", 1696 | "name": "offerer", 1697 | "type": "address" 1698 | }, { 1699 | "internalType": "bytes32", 1700 | "name": "conduitKey", 1701 | "type": "bytes32" 1702 | }], 1703 | "internalType": "struct Execution[]", 1704 | "name": "executions", 1705 | "type": "tuple[]" 1706 | }], 1707 | "stateMutability": "payable", 1708 | "type": "function" 1709 | }, { 1710 | "inputs": [{ 1711 | "components": [{ 1712 | "components": [{ 1713 | "internalType": "address", 1714 | "name": "offerer", 1715 | "type": "address" 1716 | }, { 1717 | "internalType": "address", 1718 | "name": "zone", 1719 | "type": "address" 1720 | }, { 1721 | "components": [{ 1722 | "internalType": "enum ItemType", 1723 | "name": "itemType", 1724 | "type": "uint8" 1725 | }, { 1726 | "internalType": "address", 1727 | "name": "token", 1728 | "type": "address" 1729 | }, { 1730 | "internalType": "uint256", 1731 | "name": "identifierOrCriteria", 1732 | "type": "uint256" 1733 | }, { 1734 | "internalType": "uint256", 1735 | "name": "startAmount", 1736 | "type": "uint256" 1737 | }, { 1738 | "internalType": "uint256", 1739 | "name": "endAmount", 1740 | "type": "uint256" 1741 | }], 1742 | "internalType": "struct OfferItem[]", 1743 | "name": "offer", 1744 | "type": "tuple[]" 1745 | }, { 1746 | "components": [{ 1747 | "internalType": "enum ItemType", 1748 | "name": "itemType", 1749 | "type": "uint8" 1750 | }, { 1751 | "internalType": "address", 1752 | "name": "token", 1753 | "type": "address" 1754 | }, { 1755 | "internalType": "uint256", 1756 | "name": "identifierOrCriteria", 1757 | "type": "uint256" 1758 | }, { 1759 | "internalType": "uint256", 1760 | "name": "startAmount", 1761 | "type": "uint256" 1762 | }, { 1763 | "internalType": "uint256", 1764 | "name": "endAmount", 1765 | "type": "uint256" 1766 | }, { 1767 | "internalType": "address payable", 1768 | "name": "recipient", 1769 | "type": "address" 1770 | }], 1771 | "internalType": "struct ConsiderationItem[]", 1772 | "name": "consideration", 1773 | "type": "tuple[]" 1774 | }, { 1775 | "internalType": "enum OrderType", 1776 | "name": "orderType", 1777 | "type": "uint8" 1778 | }, { 1779 | "internalType": "uint256", 1780 | "name": "startTime", 1781 | "type": "uint256" 1782 | }, { 1783 | "internalType": "uint256", 1784 | "name": "endTime", 1785 | "type": "uint256" 1786 | }, { 1787 | "internalType": "bytes32", 1788 | "name": "zoneHash", 1789 | "type": "bytes32" 1790 | }, { 1791 | "internalType": "uint256", 1792 | "name": "salt", 1793 | "type": "uint256" 1794 | }, { 1795 | "internalType": "bytes32", 1796 | "name": "conduitKey", 1797 | "type": "bytes32" 1798 | }, { 1799 | "internalType": "uint256", 1800 | "name": "totalOriginalConsiderationItems", 1801 | "type": "uint256" 1802 | }], 1803 | "internalType": "struct OrderParameters", 1804 | "name": "parameters", 1805 | "type": "tuple" 1806 | }, { 1807 | "internalType": "bytes", 1808 | "name": "signature", 1809 | "type": "bytes" 1810 | }], 1811 | "internalType": "struct Order[]", 1812 | "name": "orders", 1813 | "type": "tuple[]" 1814 | }, { 1815 | "components": [{ 1816 | "components": [{ 1817 | "internalType": "uint256", 1818 | "name": "orderIndex", 1819 | "type": "uint256" 1820 | }, { 1821 | "internalType": "uint256", 1822 | "name": "itemIndex", 1823 | "type": "uint256" 1824 | }], 1825 | "internalType": "struct FulfillmentComponent[]", 1826 | "name": "offerComponents", 1827 | "type": "tuple[]" 1828 | }, { 1829 | "components": [{ 1830 | "internalType": "uint256", 1831 | "name": "orderIndex", 1832 | "type": "uint256" 1833 | }, { 1834 | "internalType": "uint256", 1835 | "name": "itemIndex", 1836 | "type": "uint256" 1837 | }], 1838 | "internalType": "struct FulfillmentComponent[]", 1839 | "name": "considerationComponents", 1840 | "type": "tuple[]" 1841 | }], 1842 | "internalType": "struct Fulfillment[]", 1843 | "name": "fulfillments", 1844 | "type": "tuple[]" 1845 | }], 1846 | "name": "matchOrders", 1847 | "outputs": [{ 1848 | "components": [{ 1849 | "components": [{ 1850 | "internalType": "enum ItemType", 1851 | "name": "itemType", 1852 | "type": "uint8" 1853 | }, { 1854 | "internalType": "address", 1855 | "name": "token", 1856 | "type": "address" 1857 | }, { 1858 | "internalType": "uint256", 1859 | "name": "identifier", 1860 | "type": "uint256" 1861 | }, { 1862 | "internalType": "uint256", 1863 | "name": "amount", 1864 | "type": "uint256" 1865 | }, { 1866 | "internalType": "address payable", 1867 | "name": "recipient", 1868 | "type": "address" 1869 | }], 1870 | "internalType": "struct ReceivedItem", 1871 | "name": "item", 1872 | "type": "tuple" 1873 | }, { 1874 | "internalType": "address", 1875 | "name": "offerer", 1876 | "type": "address" 1877 | }, { 1878 | "internalType": "bytes32", 1879 | "name": "conduitKey", 1880 | "type": "bytes32" 1881 | }], 1882 | "internalType": "struct Execution[]", 1883 | "name": "executions", 1884 | "type": "tuple[]" 1885 | }], 1886 | "stateMutability": "payable", 1887 | "type": "function" 1888 | }, { 1889 | "inputs": [], 1890 | "name": "name", 1891 | "outputs": [{ 1892 | "internalType": "string", 1893 | "name": "contractName", 1894 | "type": "string" 1895 | }], 1896 | "stateMutability": "pure", 1897 | "type": "function" 1898 | }, { 1899 | "inputs": [{ 1900 | "components": [{ 1901 | "components": [{ 1902 | "internalType": "address", 1903 | "name": "offerer", 1904 | "type": "address" 1905 | }, { 1906 | "internalType": "address", 1907 | "name": "zone", 1908 | "type": "address" 1909 | }, { 1910 | "components": [{ 1911 | "internalType": "enum ItemType", 1912 | "name": "itemType", 1913 | "type": "uint8" 1914 | }, { 1915 | "internalType": "address", 1916 | "name": "token", 1917 | "type": "address" 1918 | }, { 1919 | "internalType": "uint256", 1920 | "name": "identifierOrCriteria", 1921 | "type": "uint256" 1922 | }, { 1923 | "internalType": "uint256", 1924 | "name": "startAmount", 1925 | "type": "uint256" 1926 | }, { 1927 | "internalType": "uint256", 1928 | "name": "endAmount", 1929 | "type": "uint256" 1930 | }], 1931 | "internalType": "struct OfferItem[]", 1932 | "name": "offer", 1933 | "type": "tuple[]" 1934 | }, { 1935 | "components": [{ 1936 | "internalType": "enum ItemType", 1937 | "name": "itemType", 1938 | "type": "uint8" 1939 | }, { 1940 | "internalType": "address", 1941 | "name": "token", 1942 | "type": "address" 1943 | }, { 1944 | "internalType": "uint256", 1945 | "name": "identifierOrCriteria", 1946 | "type": "uint256" 1947 | }, { 1948 | "internalType": "uint256", 1949 | "name": "startAmount", 1950 | "type": "uint256" 1951 | }, { 1952 | "internalType": "uint256", 1953 | "name": "endAmount", 1954 | "type": "uint256" 1955 | }, { 1956 | "internalType": "address payable", 1957 | "name": "recipient", 1958 | "type": "address" 1959 | }], 1960 | "internalType": "struct ConsiderationItem[]", 1961 | "name": "consideration", 1962 | "type": "tuple[]" 1963 | }, { 1964 | "internalType": "enum OrderType", 1965 | "name": "orderType", 1966 | "type": "uint8" 1967 | }, { 1968 | "internalType": "uint256", 1969 | "name": "startTime", 1970 | "type": "uint256" 1971 | }, { 1972 | "internalType": "uint256", 1973 | "name": "endTime", 1974 | "type": "uint256" 1975 | }, { 1976 | "internalType": "bytes32", 1977 | "name": "zoneHash", 1978 | "type": "bytes32" 1979 | }, { 1980 | "internalType": "uint256", 1981 | "name": "salt", 1982 | "type": "uint256" 1983 | }, { 1984 | "internalType": "bytes32", 1985 | "name": "conduitKey", 1986 | "type": "bytes32" 1987 | }, { 1988 | "internalType": "uint256", 1989 | "name": "totalOriginalConsiderationItems", 1990 | "type": "uint256" 1991 | }], 1992 | "internalType": "struct OrderParameters", 1993 | "name": "parameters", 1994 | "type": "tuple" 1995 | }, { 1996 | "internalType": "bytes", 1997 | "name": "signature", 1998 | "type": "bytes" 1999 | }], 2000 | "internalType": "struct Order[]", 2001 | "name": "orders", 2002 | "type": "tuple[]" 2003 | }], 2004 | "name": "validate", 2005 | "outputs": [{ 2006 | "internalType": "bool", 2007 | "name": "validated", 2008 | "type": "bool" 2009 | }], 2010 | "stateMutability": "nonpayable", 2011 | "type": "function" 2012 | }] --------------------------------------------------------------------------------