pythonでUniswapV2Routerコントラクトを呼び出してBaseネットワークでswapしてみた話
※自身の備忘録的な意味合いが強いので、使用は自己責任の参考程度でお願いします。
仮想通貨まわりの作業を自動化したいと思い、少しずつ勉強していこうと思っています。その足掛かりとしてプログラムを組んでスワップする、ということを今回やってみました。
舞台はBaseネットワークです。
WETH→AERO(Swap)
#base_swap1.py
import json
from web3 import Web3
from datetime import datetime
baseRpc = "https://mainnet.base.org"
walletAddress = 'my_wallet_address'
walletKey = 'my_private_key'
WETH = '0x4200000000000000000000000000000000000006'
USDC = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913'
AERO = '0x940181a94A35A4569E4529A3CDfB74e38FD98631'
UniV2Router = '0x4752ba5DBc23f44D87826276BF6Fd6b1C372aD24'
gas = 200000
gasPrice = 3
web3 = Web3(Web3.HTTPProvider(baseRpc))
with open('UniSwap_Router_ABI.json') as f:
uniRouterABI = json.load(f)
uniRouterContract = web3.eth.contract(address=UniV2Router, abi=uniRouterABI)
now = int(datetime.now().timestamp())
nonce = web3.eth.get_transaction_count(walletAddress)
funcSwap = uniRouterContract.functions.swapExactETHForTokens(
0,
[WETH, AERO],
walletAddress,
now+200
)
tx = funcSwap.build_transaction({
'value': web3.to_wei(0.01, 'ether'),
'gas': gas,
'gasPrice': web3.to_wei(gasPrice, 'gwei'),
'nonce': nonce
})
signedTx = web3.eth.account.sign_transaction(tx, walletKey)
txHash = web3.eth.send_raw_transaction(signedTx.raw_transaction)
print('Send transaction')
print(web3.to_hex(txHash))
result = web3.eth.wait_for_transaction_receipt(txHash)
status = result['status']
if status == 1:
print('Transaction Succeeded')
else:
print('Transaction Failed')
my_wallet_addressはmetamaskなどのアドレス
my_private_keyは秘密鍵
上記ファイルと同じディレクトリにUniSwap_Router_ABI.jsonというファイルが必要(中身はUniswap V2 RouterコントラクトのABI)。
Base mainnetのUniswap V2 Routerのコントラクトアドレスは
0x4752ba5DBc23f44D87826276BF6Fd6b1C372aD24
なので、BaseScanでこのアドレスを検索し、ContractタブのCodeの下の方でABIを見つけることができる(コピーしてUniSwap_Router_ABI.jsonにペースト)。
https://basescan.org/address/0x4752ba5dbc23f44d87826276bf6fd6b1c372ad24#code
また、ContractタブのWrite Contractで「swapExactETHForTokens」ファンクションについても確認できる。
amountOutMin(許容できる引出しトークン最小量)
path(どのトークンとどのトークンをスワップするか)
to(トークンの送り先:自分のウォレット)
deadline(トランザクションが失効するまでの秒数)
が引数として必要であることが分かる。
実際の実行結果が以下
https://basescan.org/tx/0xcd2472786eb81545c2372a471847f5eac97494a508907782acb768f15a8831bb
0.01WETHがこれに応じたAEROにSwapされたことが分かる
このSwapで0.79ドルの手数料が掛かっている
AERO→WETH(Swap)
#base_swap2.py
import json
from web3 import Web3
from datetime import datetime
baseRpc = "https://mainnet.base.org"
walletAddress = 'my_wallet_address'
walletKey = 'my_private_key'
WETH = '0x4200000000000000000000000000000000000006'
USDC = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913'
AERO = '0x940181a94A35A4569E4529A3CDfB74e38FD98631'
UniV2Router = '0x4752ba5DBc23f44D87826276BF6Fd6b1C372aD24'
gas = 200000
gasPrice = 3
web3 = Web3(Web3.HTTPProvider(baseRpc))
with open('UniSwap_Router_ABI.json') as f:
uniRouterABI = json.load(f)
uniRouterContract = web3.eth.contract(address=UniV2Router, abi=uniRouterABI)
now = int(datetime.now().timestamp())
nonce = web3.eth.get_transaction_count(walletAddress)
funcSwap = uniRouterContract.functions.swapExactTokensForETH(
web3.to_wei(20, 'ether'),
0,
[AERO, WETH],
walletAddress,
now+200
)
tx = funcSwap.build_transaction({
'value': 0,
'gas': gas,
'gasPrice': web3.to_wei(gasPrice, 'gwei'),
'nonce': nonce
})
signedTx = web3.eth.account.sign_transaction(tx, walletKey)
txHash = web3.eth.send_raw_transaction(signedTx.rawTransaction)
print('Send transaction')
print(web3.to_hex(txHash))
result = web3.eth.wait_for_transaction_receipt(txHash)
status = result['status']
if status == 1:
print('Transaction Succeeded')
else:
print('Transaction Failed')
実行結果は以下
https://basescan.org/tx/0xac9b148e666913ba2af9e8f41ef9de40251e0c50988437afccc7c41898ddd18b
20AEROがこれに対応する量のWETHにスワップされたことが分かる。
また、手数料として0.88ドル掛かったことが分かる。
AERO→WETHが失敗(failed)する場合
AEROトークンへの操作がUniswapに許可されていない可能性がある(私はこれでしばらくハマりました)ので下記コードでApproveする。
#base_approve.py
import json
from web3 import Web3
from datetime import datetime
baseRpc = "https://mainnet.base.org"
walletAddress = 'my_wallet_address'
walletKey = 'my_private_key'
WETH = '0x4200000000000000000000000000000000000006'
USDC = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913'
AERO = '0x940181a94A35A4569E4529A3CDfB74e38FD98631'
UniV2Router = '0x4752ba5DBc23f44D87826276BF6Fd6b1C372aD24'
gas = 200000
gasPrice = 3
web3 = Web3(Web3.HTTPProvider(baseRpc))
with open('UniSwap_Router_ABI.json') as f:
uniRouterABI = json.load(f)
uniRouterContract = web3.eth.contract(address=UniV2Router, abi=uniRouterABI)
with open('Aero_Contract_ABI.json') as f:
aeroABI = json.load(f)
aeroContract= web3.eth.contract(address=AERO, abi=aeroABI)
now = int(datetime.now().timestamp())
nonce = web3.eth.get_transaction_count(walletAddress)
inf = 2**256 - 1
func = aeroContract.functions.approve(UniV2Router, inf )
tx = func.build_transaction({
'value': 0,
'gas': gas,
'gasPrice': web3.to_wei(gasPrice, 'gwei'),
'nonce': nonce
})
signedTx = web3.eth.account.sign_transaction(tx, walletKey)
txHash = web3.eth.send_raw_transaction(signedTx.rawTransaction)
print('Send transaction')
print(web3.to_hex(txHash))
result = web3.eth.wait_for_transaction_receipt(txHash)
status = result['status']
if status == 1:
print('Transaction Succeeded')
else:
print('Transaction Failed')
上記ファイルと同じ場所にAero_Contract_ABI.jsonを新規作成し、AEROトークンコントラクトのABIを貼り付ける必要がある(BaseScanでAEROトークンのコントラクトアドレス
0x940181a94A35A4569E4529A3CDfB74e38FD98631
を検索して、ContractタブのCodeのABIを貼り付け)。
実行結果は以下
https://basescan.org/tx/0x9b5d556b429308406d42d635c6eb066947bf7c2a5825c4d6ca5d04a8f1bc6720
承認作業にも0.21ドル掛かっていることがわかる。
公開されているDEXでSwapしてみる(WETH→AERO)
この結果を見てみる
https://basescan.org/tx/0xa83122fe8101e0374d975772108923003f3f75613f6912c3f2beb0fa60655777
なんと、25.99ドル分のWETHが25.73ドル分のAEROにスワップされ、手数料は0.001801ドルしか掛かっていないことが分かる。
今回のSwapにおける課題
WETH→AEROでは、25.99ドル分のWETHが24.73ドル分のAEROになり手数料は0.79ドル
AERO→WETHでは、19.37ドル分のAEROが18.79ドル分のWETHになり手数料は0.88ドル
AEROの承認作業には手数料0.21ドル
一方で、公開DEXでのWETH→AEROでは25.99ドル分のWETHが25.73ドルのAEROにスワップされ、手数料は0.001801ドル
pythonで動かしたプログラムに対して、圧倒的に公開DEXの方が優秀なことがわかる。
改善策としては、
「swapExactETHForTokens」や「swapExactTokensForETH」に設定しているamountOutMinを0としているが、これは引出しトークンが0でもOKとする(何が何でもSwapする)という設定。
ここをきちんと最善の取引条件になるようなロジックに変更する。
また、今回はガス代の設定を適当に行った
gas = 200000
gasPrice = 3
が、ここをどこまで減らしてもトランザクションが通るか、という部分について攻める必要がある。
他にも色々あるんだろうとは思いますが、詳しい方、アドバイス下さい笑
この記事が気に入ったらサポートをしてみませんか?