Skip to content

Instantly share code, notes, and snippets.

@normanlmfung
Last active July 27, 2022 08:56
Show Gist options
  • Save normanlmfung/37de5fa945278f7d8ac2705eb8ea46f7 to your computer and use it in GitHub Desktop.
Save normanlmfung/37de5fa945278f7d8ac2705eb8ea46f7 to your computer and use it in GitHub Desktop.
web3 decoding transactions
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