I've tried using this encoder a thousand ways. Using a tester blockchain with eth-tester
. On Goerli. On Ethereum. Only doing a V2 swap. Only doing a V3 swap. Swapping through WETH with wrap_eth
, and swapping between two ERC-20 tokens without it. Not even once have I gotten this encoder to actually work. Sometimes the revert message is stack underflow (0 <=> 1)
. Other times it's TRANSFER_FAILED
. Other times, there's no feedback at all. Please help.
from web3 import Web3
from web3.providers.rpc import HTTPProvider
from uniswap_universal_router_decoder import FunctionRecipient, RouterCodec
rpc_url = ...
w3 = Web3(HTTPProvider(rpc_url))
account_address = Web3.to_checksum_address("0x01DCDb24406CB31C7eC7FcaD7766F6a584D25DE2")
weth_abi = '[{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"guy","type":"address"},{"name":"wad","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":"src","type":"address"},{"name":"dst","type":"address"},{"name":"wad","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"wad","type":"uint256"}],"name":"withdraw","outputs":[],"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":"","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":"dst","type":"address"},{"name":"wad","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"deposit","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"},{"name":"","type":"address"}],"name":"allowance","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":true,"name":"src","type":"address"},{"indexed":true,"name":"guy","type":"address"},{"indexed":false,"name":"wad","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"src","type":"address"},{"indexed":true,"name":"dst","type":"address"},{"indexed":false,"name":"wad","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"dst","type":"address"},{"indexed":false,"name":"wad","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"src","type":"address"},{"indexed":false,"name":"wad","type":"uint256"}],"name":"Withdrawal","type":"event"}]' # noqa
weth_address = Web3.to_checksum_address("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2")
usdc_address = Web3.to_checksum_address("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48")
weth_contract = w3.eth.contract(address=weth_address, abi=weth_abi)
ur_address = Web3.to_checksum_address("0xEf1c6E67703c7BD7107eed8303Fbe6EC2554BF6B")
# Verify account has allowances of weth and usdc
weth_allowance = weth_contract.functions.allowance(account_address, ur_address).call()
assert weth_allowance >= 1000
v2_path = [weth_address, usdc_address]
v2_in_amount = 500
v2_out_amount = 0
v3_path = [weth_address, 500, usdc_address]
v3_in_amount = 500
v3_out_amount = 0
total_in_amount = v2_in_amount + v3_in_amount
codec = RouterCodec()
encoded_data = (
codec
.encode
.chain()
.wrap_eth(FunctionRecipient.ROUTER, total_in_amount)
.v2_swap_exact_in(FunctionRecipient.SENDER, v2_in_amount, v2_out_amount, v2_path, payer_is_sender=False)
.v3_swap_exact_in(FunctionRecipient.SENDER, v3_in_amount, v3_out_amount, v3_path, payer_is_sender=False)
.build(codec.get_default_deadline())
)
tx_params = {
"from": account_address,
"to": ur_address,
"gas": 800_000,
"maxPriorityFeePerGas": w3.eth.max_priority_fee,
"maxFeePerGas": int(w3.eth.gas_price * 1.1),
"type": '0x2',
"chainId": w3.eth.chain_id,
"value": total_in_amount,
"nonce": w3.eth.get_transaction_count(account_address),
"data": encoded_data,
}
# ContractLogicError
w3.eth.estimate_gas(tx_params)
This example uses a throwaway account without much gas money, but you can always use a different account, as I have, and I assume you'd get the same result. I've never been able to get this encoder to actually work. If I'm missing something obvious, I'd be happy for it to be shown to me.