2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

UniswapV2でトークンの価格取得/スワップ(web3py)

Last updated at Posted at 2022-05-25

概要

Uniswapはその誕生以降、様々なクローンが様々なチェーンで作られています。そのほとんどがUniswapV2に対応しており、適宜アドレスを変えるだけで同じように扱えます。
今回はgas代が安く、Infuraなどの登録なしでRPCが使える、PolygonチェーンのQuickswapを例にトークンの価格取得とスワップの実装を行います。

DAI/ETHの価格取得

はじめにUniswapのfactoryとrouterのコントラクトインスタンスを取得します。factoryは、DAI-ETHのペアコントラクトのアドレスを取得するために必要です。routerはスワップする際に必要になります。

from web3 import Web3,HTTPProvider
import json, os, time

RPC_URL = "https://polygon-rpc.com" # Polygon Matic RPC address
FACTORY_ADDRESS = "0x5757371414417b8C6CAad45bAeF941aBc7d3Ab32" # QuickSwap UniswapV2Factory
ROUTER_ADDRESS = "0xa5E0829CaCEd8fFDD4De3c43696c57F7D7A678ff" # QuickSwap UniswapV2Router02
DAI_ADDRESS = "0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063" # DAI on polygon
WETH_ADDRESS ="0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619"# WETH on polygon

with open("abis/IUniswapV2Pair.json", "r") as f:
    IUniswapV2Pair =json.loads(f.read())
with open("abis/IUniswapV2Factory.json", "r") as f:
    IUniswapV2Factory =json.loads(f.read())
with open("abis/IUniswapV2Router02.json", "r") as f:
    IUniswapV2Router02 =json.loads(f.read())
with open("abis/IERC20.json", "r") as f:
    IERC20 =json.loads(f.read())

w3 = Web3(HTTPProvider(RPC_URL))
factory = w3.eth.contract(address = FACTORY_ADDRESS , abi = IUniswapV2Factory["abi"])
router = w3.eth.contract(address = ROUTER_ADDRESS , abi = IUniswapV2Router02["abi"])

FACTORY_ADDRESS, ROUTER_ADDRESSこのページ、DAIやWETHのコントラクトアドレスはこのページから確認できます。ABIファイルは以下のリンクからダウンロードできます。

次にDAI-ETHペアコントラクトのアドレスをgetPairメソッドで取得し、そのアドレスを使ってコントラクトインスタンスを作ります。

eth_dai_address = factory.functions.getPair(DAI_ADDRESS , WETH_ADDRESS).call() # eth_daiのコントラクトアドレスを取得
eth_dai = w3.eth.contract(address = eth_dai_address, abi = IUniswapV2Pair["abi"]) # eth_daiのコントラクトインスタンスを取得
print("token0 = ",  eth_dai.functions.token0().call())
print("token1 = ",  eth_dai.functions.token1().call())

出力結果

token0 =  0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619
token1 =  0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063

ペア通貨はそれぞれtoken0, token1と呼ばれます。今回はWETHがtoken0, DAIがtoken1に対応していることがわかります。

次に価格を取得します。

reserves = eth_dai.functions.getReserves().call()
print("token0 reserver = ", reserves[0]*10**(-18))
print("token1 reserver = ", reserves[1]*10**(-18))
print(" block.timestamp = ", reserves[2])
print("token0 price = ", reserves[1]/reserves[0]) # DAI/ETH price in this case

出力結果

token0 reserver =  3025.605972350233
token1 reserver =  6015799.838675748
block.timestamp =  1653458719
token0 price =  1988.2958632590182

reserverとはプールされているトークン量です。ETHもDAIも最小単位に対し小数点が18桁目にあるので、$10^{-18}$を掛けています。reserversの第3要素は値を取得したブロックのタイムスタンプになっています。reserves[1]/reserves[0]token01単位あたりのtoken1の量、この場合はDAI/ETHの価格になっています。

DAI→WETHのスワップ

DAI→WETHのスワップを実装してみます。あらかじめウォレットにいくらかDAIを用意しておいて下さい。

approve

スワップはUniswapのRouterコントラクトが、ウォレットからDAIをtransferFromメソッドで引き抜き、ETHに交換します。そのためスワップに先立って、Routerコントラクトのアドレスに対しapproveを行う必要があります。

my_address = os.getenv("ADDRESS")
my_private_key = os.getenv("PRIVATE_KEY")
dai = w3.eth.contract(address = DAI_ADDRESS, abi = IERC20["abi"])

dai_amount = Web3.toWei("0.1", "ether") # スワップしたい量

nonce = w3.eth.get_transaction_count(my_address)
tx_approve = dai.functions.approve(ROUTER_ADDRESS, dai_amount).buildTransaction(\
    {'chainId': 137, \
     'type': 2, \
     'gas': 10**6, \
    'maxFeePerGas': w3.toWei('100', 'gwei'),\
    'nonce': nonce, })
signed_tx_approve = w3.eth.account.sign_transaction(tx_approve, private_key = my_private_key)
w3.eth.send_raw_transaction(signed_tx_approve.rawTransaction)

swap

いよいよスワップです。
スワップのメソッドはいくつかありますが、今回はswapExactTokensForTokensを使います。

function swapExactTokensForTokens(
  uint amountIn,
  uint amountOutMin,
  address[] calldata path,
  address to,
  uint deadline
) external returns (uint[] memory amounts);

これはamountInで指定した量のトークン(path[0]で指定されたもの)をトークン(path[path.length - 1]で指定されたもの)になるべく多く交換し、アドレスtoに送金するメソッドです。
pathはスワップの経路を指定します。今回はDAIとWETHのペアプールが存在するので直接スワップすることができて、path[0]にDAIのコントラクトアドレス、path[1]にETHのコントラクトアドレスを指定すれば良いです。amountOutMinは交換後の最低トークン量を指定します。この量を下回るならばrevertされます。deadlineには取引の期限を指定します。

amountIn = dai_amount
amountOutMin =int(reserves[0]/reserves[1]*amountIn*0.99) # 1%以内のスリッページで実行
path = [DAI_ADDRESS, WETH_ADDRESS] # daiからwethへ両替することを指定
to = my_address
deadline = int(time.time() + 60*10) # 10分以内に実行されなければrevert
nonce = w3.eth.get_transaction_count(my_address)
tx_swap = router.functions.swapExactTokensForTokens(amountIn, amountOutMin, path, to, deadline).buildTransaction(\
    {'chainId': 137, \
     'type': 2, \
     'gas': 10**6, \
    'maxFeePerGas': w3.toWei('100', 'gwei'),\
    'nonce': nonce, })
signed_tx_swap = w3.eth.account.sign_transaction(tx_swap, private_key = my_private_key)
w3.eth.send_raw_transaction(signed_tx_swap.rawTransaction)

以上でスワップが完了します。

参考文献

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?