-
-
Save normanlmfung/37de5fa945278f7d8ac2705eb8ea46f7 to your computer and use it in GitHub Desktop.
web3 decoding transactions
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from typing import Dict | |
from web3 import Web3 | |
from web3.exceptions import TransactionNotFound | |
ONE_BILLION : int = 1000000000 | |
EIP20_ABI = '[{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_value","type":"uint256"}],"name":"approve","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"payable":true,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"},{"name":"_spender","type":"address"}],"name":"allowance","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_from","type":"address"},{"indexed":true,"name":"_to","type":"address"},{"indexed":false,"name":"_value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_owner","type":"address"},{"indexed":true,"name":"_spender","type":"address"},{"indexed":false,"name":"_value","type":"uint256"}],"name":"Approval","type":"event"}]' | |
cronos_mainnet_rpc = "https://evm.cronos.org" | |
w3 = Web3(Web3.HTTPProvider(cronos_mainnet_rpc)) | |
pool_address = Web3.toChecksumAddress("0x0fBAB8A90CAC61b481530AAd3a64fE17B322C25d") | |
pool_abi = '[{"type":"constructor","stateMutability":"nonpayable","payable":false,"inputs":[]},{"type":"event","name":"Approval","inputs":[{"type":"address","name":"owner","internalType":"address","indexed":true},{"type":"address","name":"spender","internalType":"address","indexed":true},{"type":"uint256","name":"value","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"Burn","inputs":[{"type":"address","name":"sender","internalType":"address","indexed":true},{"type":"uint256","name":"amount0","internalType":"uint256","indexed":false},{"type":"uint256","name":"amount1","internalType":"uint256","indexed":false},{"type":"address","name":"to","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"Mint","inputs":[{"type":"address","name":"sender","internalType":"address","indexed":true},{"type":"uint256","name":"amount0","internalType":"uint256","indexed":false},{"type":"uint256","name":"amount1","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"Swap","inputs":[{"type":"address","name":"sender","internalType":"address","indexed":true},{"type":"uint256","name":"amount0In","internalType":"uint256","indexed":false},{"type":"uint256","name":"amount1In","internalType":"uint256","indexed":false},{"type":"uint256","name":"amount0Out","internalType":"uint256","indexed":false},{"type":"uint256","name":"amount1Out","internalType":"uint256","indexed":false},{"type":"address","name":"to","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"Sync","inputs":[{"type":"uint112","name":"reserve0","internalType":"uint112","indexed":false},{"type":"uint112","name":"reserve1","internalType":"uint112","indexed":false}],"anonymous":false},{"type":"event","name":"Transfer","inputs":[{"type":"address","name":"from","internalType":"address","indexed":true},{"type":"address","name":"to","internalType":"address","indexed":true},{"type":"uint256","name":"value","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"DOMAIN_SEPARATOR","inputs":[],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"MINIMUM_LIQUIDITY","inputs":[],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"PERMIT_TYPEHASH","inputs":[],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"allowance","inputs":[{"type":"address","name":"","internalType":"address"},{"type":"address","name":"","internalType":"address"}],"constant":true},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"approve","inputs":[{"type":"address","name":"spender","internalType":"address"},{"type":"uint256","name":"value","internalType":"uint256"}],"constant":false},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"balanceOf","inputs":[{"type":"address","name":"","internalType":"address"}],"constant":true},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[{"type":"uint256","name":"amount0","internalType":"uint256"},{"type":"uint256","name":"amount1","internalType":"uint256"}],"name":"burn","inputs":[{"type":"address","name":"to","internalType":"address"}],"constant":false},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint8","name":"","internalType":"uint8"}],"name":"decimals","inputs":[],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"address","name":"","internalType":"address"}],"name":"factory","inputs":[],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint112","name":"_reserve0","internalType":"uint112"},{"type":"uint112","name":"_reserve1","internalType":"uint112"},{"type":"uint32","name":"_blockTimestampLast","internalType":"uint32"}],"name":"getReserves","inputs":[],"constant":true},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[],"name":"initialize","inputs":[{"type":"address","name":"_token0","internalType":"address"},{"type":"address","name":"_token1","internalType":"address"}],"constant":false},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"kLast","inputs":[],"constant":true},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[{"type":"uint256","name":"liquidity","internalType":"uint256"}],"name":"mint","inputs":[{"type":"address","name":"to","internalType":"address"}],"constant":false},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"string","name":"","internalType":"string"}],"name":"name","inputs":[],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"nonces","inputs":[{"type":"address","name":"","internalType":"address"}],"constant":true},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[],"name":"permit","inputs":[{"type":"address","name":"owner","internalType":"address"},{"type":"address","name":"spender","internalType":"address"},{"type":"uint256","name":"value","internalType":"uint256"},{"type":"uint256","name":"deadline","internalType":"uint256"},{"type":"uint8","name":"v","internalType":"uint8"},{"type":"bytes32","name":"r","internalType":"bytes32"},{"type":"bytes32","name":"s","internalType":"bytes32"}],"constant":false},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"price0CumulativeLast","inputs":[],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"price1CumulativeLast","inputs":[],"constant":true},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[],"name":"skim","inputs":[{"type":"address","name":"to","internalType":"address"}],"constant":false},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[],"name":"swap","inputs":[{"type":"uint256","name":"amount0Out","internalType":"uint256"},{"type":"uint256","name":"amount1Out","internalType":"uint256"},{"type":"address","name":"to","internalType":"address"},{"type":"bytes","name":"data","internalType":"bytes"}],"constant":false},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"string","name":"","internalType":"string"}],"name":"symbol","inputs":[],"constant":true},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[],"name":"sync","inputs":[],"constant":false},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"address","name":"","internalType":"address"}],"name":"token0","inputs":[],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"address","name":"","internalType":"address"}],"name":"token1","inputs":[],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"totalSupply","inputs":[],"constant":true},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"transfer","inputs":[{"type":"address","name":"to","internalType":"address"},{"type":"uint256","name":"value","internalType":"uint256"}],"constant":false},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"transferFrom","inputs":[{"type":"address","name":"from","internalType":"address"},{"type":"address","name":"to","internalType":"address"},{"type":"uint256","name":"value","internalType":"uint256"}],"constant":false}]' | |
pool_contract = w3.eth.contract(address=pool_address, abi=pool_abi) | |
''' | |
Example 1. Single-hop. Buy USDC with SINGLE https://cronoscan.com/tx/0xbd958b205291355d681cf01fa01f2064078d7be3b1ba63be8a9d2dff72bcce48 | |
Example 2. Single-hop. Buy SINGLE with USDC https://cronoscan.com/tx/0x4f8898c244bd7bc7f77d6d8aec7af670ff6d86f252d9d231860dc1528dacf91c | |
Example 3. Two hops. Buy USDT with SINGLE https://cronoscan.com/tx/0x1b09a08c87a2015a34fa05883cf1005715ff64d90772c044b02f80e646a69951 | |
Example 4. Two hops. Buy SINGLE with USDT https://cronoscan.com/tx/0x4eff6d10c7310d272dc999d8c0acffcf18ff43b6802411a069570c43564d5579 | |
''' | |
trx_hash = '0x4eff6d10c7310d272dc999d8c0acffcf18ff43b6802411a069570c43564d5579' | |
trx = w3.eth.getTransaction(trx_hash) # This only means you sent the transaction. It does not mean it succeeded. | |
trx_block_number = trx.blockNumber | |
trx_unix_ts = w3.eth.getBlock(trx_block_number).timestamp | |
status : int = -1 | |
while True: | |
try: | |
''' | |
https://web3py.readthedocs.io/en/stable/web3.eth.html#methods | |
Returns the transaction receipt specified by transaction_hash. If the transaction has not yet been mined throws web3.exceptions.TransactionNotFound. | |
If status in response equals 1 the transaction was successful. If it is equals 0 the transaction was reverted by EVM. | |
''' | |
trx_receipt = w3.eth.get_transaction_receipt(trx_hash) | |
sender = trx_receipt['from'] | |
status = trx_receipt['status'] | |
if status == 0 or status == 1: | |
decoded_logs = [] | |
from web3._utils.events import EventLogErrorFlags | |
''' | |
Note | |
1) Here, we are checking "Transfer" events! Not "Swap"! That's the confusing bit! | |
For the example transaction hash '0xaba4f0d9fb2606cab22328ea98297526e5ac4f98358e7fcceb098b755d8290ad', event is a 'Transfer'. If you specify 'Swap' below, you'd get error: | |
... error during processing: MismatchedABI(The event signature did not match the provided ABI). It has been discarded. | |
2) If you look under pool_contract.events, you'd find these: Approval, Burn, Mint, Swap, Sync, Transfer. You should seem them defined in smart contract as well: https://cronos.org/explorer/address/0x0fBAB8A90CAC61b481530AAd3a64fE17B322C25d/contracts | |
Search for "event" | |
''' | |
logs = pool_contract.events.Transfer().processReceipt(trx_receipt, EventLogErrorFlags.Warn) | |
for log in logs: | |
transaction_type = log.event | |
log_index = log.logIndex # Each 'log' has different log_index, you can use it to determine which is first and which comes after. | |
transaction_index = log.transactionIndex # But they have same transaction_index! | |
direction : str = None | |
if log.args['from'] == sender: | |
direction = "SELL" | |
elif log.args['to'] == sender: | |
direction = "BUY" | |
else: | |
direction = "INTERMEDIATE" | |
token_address = log["address"] | |
token_contract = w3.eth.contract(address=token_address, abi=EIP20_ABI) | |
token_symbol = token_contract.functions.symbol().call() | |
token_decimals = token_contract.functions.decimals().call() | |
amount = log.args['value'] | |
amount = amount / 10**token_decimals | |
decoded_logs.append( { | |
'log_index' : log_index, | |
'transaction_index' : transaction_index, | |
'transaction_type' : transaction_type, | |
'ccy' : token_symbol, | |
'direction' : direction, | |
'amount' : amount | |
}) | |
gas_used_in_units = trx_receipt['gasUsed'] | |
gas_price = w3.eth.gasPrice | |
gas_used_in_wei = gas_used_in_units * gas_price | |
gas_used_in_coin = gas_used_in_wei / (ONE_BILLION * ONE_BILLION) | |
break | |
except TransactionNotFound: | |
# Transaction not found! | |
pass | |
trx_status : str = "Pending" | |
if status==1: | |
trx_status = "Confirmmed" | |
elif status==0: | |
trx_status = "Reverted" | |
transaction_report : Dict = { | |
'trx_hash' : trx_hash, | |
'trx_block_number' : trx.blockNumber, | |
'status' : trx_status, | |
'trx_unix_ts' : trx_unix_ts, | |
'gas' : { | |
'gas_used_in_units' : gas_used_in_units, | |
'gas_price' : gas_price, | |
'gas_used_in_wei' : gas_used_in_wei, | |
'gas_used_in_coin' : gas_used_in_coin # On CRONOS, "Coin" refers to CRO. On Ethereum, this is ETH. | |
} | |
} | |
ticker : str = None | |
execution_price : float = 0 | |
if len(decoded_logs)>0: | |
STABLES = ['USDC', 'USDT'] | |
# By convention, USDT is quote currency | |
if decoded_logs[0]['ccy'] in STABLES: | |
ticker = f"{decoded_logs[-1]['ccy']}/{decoded_logs[0]['ccy']}" | |
execution_price = decoded_logs[0]['amount'] / decoded_logs[-1]['amount'] | |
else: | |
ticker = f"{decoded_logs[0]['ccy']}/{decoded_logs[-1]['ccy']}" | |
execution_price = decoded_logs[-1]['amount'] / decoded_logs[0]['amount'] | |
transaction_report['execution'] : Dict = { | |
"ticker" : ticker, | |
"price" : execution_price, | |
"decoded_logs" : decoded_logs | |
} | |
print(transaction_report) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment