【仮想通貨botter Advent Calendar 2022】 ABIを使わずにPancakeswapでSwapしよう

 本記事は仮想通貨botter Advent Calendar 2022に掲載させていただく記事です。

はじめに

 はじめまして。qash_titと申します。普段はBOTを用いたアービトラージを主体として、クラウド代とメンタルを溶かしながら戦っております。

 今年度は、仮想通貨botter Advent Calendar 2022に参加させていただきました。
 昨年度の仮想通貨botter Advent Calendar 2021では、良質な記事(ポエム)や気づきなどがあり、参加しなかったことを後で後悔しました。
 私も最近はNote弊ブログでポエムを書いているので、良かったら読んでみてください。

 過度に利益志向にならないように、とのことなので、絆チェーンのブリッジを止めたりとか、ハニーポットを投げつけたりとか、そういうテクニック系の話はせず、「へーそうなんだ」となるくらいのことを書こうと思います。(長くて濃い話は年末にNote出します。
 「welcome to the underground」な話はオフ会とかで会ったら聞いてください。話せるところは話します。

 さて、今回の題材は「ABIを使わずにPancakeswapでSwapしよう」です。

 なんでこれを取り上げるかと言うと、考え方を応用すると色々なコントラクトを操作することが可能になるからです。
 では、順を追って説明していきます。

i)PancakeswapのWebサイトを使う。

Pancakeswap公式サイト

これは説明不要だと思います。

ii)BscscanのWriteContract(直コン)を使う。

swapExactTokensForTokens

 例えば、swapExactTokensForTokensはトークンAをトークンBに交換する関数です。
 PancakeswapなどのDEXでは、UIから操作するため、適切な関数を自動で選んでくれます。
 そのため、普段はBNB->BUSDをSwapする時はswapExactETHForTokensを使って、とか、BUSD->USDTをする時はswapExactTokensForTokensを使って・・・、と意識することはありません。
 一方、所謂直コンを使う場合は、適切な関数を選ぶ必要があります。Uniswapフォーク(Pancakeswap)のコントラクトの場合、以下の関数を使用することができます。

swapExactTokensForTokens:正確な量のトークンAをトークンBにSwap
swapTokensForExactTokens:トークンAを正確な量のトークンBにSwap
swapExactETHForTokens:正確な量のETHをトークンBにSwap
swapTokensForExactETH:トークンAを正確な量のETHにSwap
swapExactTokensForETH:正確な量のトークンAをETHにSwap
swapETHForExactTokens:ETHを正確な量のトークンBにSwap
swapExactTokensForTokensSupportingFeeOnTransferTokens:正確な量のトークンAをトークンBにSwap(Feeあり)swapExactETHForTokensSupportingFeeOnTransferTokens:正確な量のETHをトークンBにSwap(Feeあり)swapExactTokensForETHSupportingFeeOnTransferTokens:正確な量のトークンAをETHにSwap(Feeあり)

 適宜、ETHをBNBと読み替えたり、そのチェーンの基軸通貨に読み替える必要があります。PancakeswapはUniswapをそのままフォークしているのでswapExactBNBForTokensではなくswapExactETHForTokensのようです。

 今回のNoteではswapExactTokensForTokensに焦点を当てたいので、Inputについて少し細かく見ます。

例: 1USDTを0.99BUSD以上にSwapしたい。

 なお、事前にトークンApproveは済ませているものとします。
(*PancakeswapのRouter addressUSDTの1.approveで必要量をApproveする)

swapExactTokensForTokensの中身
・amountIn (uint256):1000000000000000000
(使うUSDTの量
USDTのDecimalsは18なので、1*10^18をすると1USDT扱い。)

・amountOutMin (uint256):990000000000000000
(Swapした際に最低限得たいBUSDの量
BUSDのDecimalsは18なので、0.99*10^18をすると0.99BUSD扱い)

・path (address[]):0x55d398326f99059fF775485246999027B3197955,0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56
(USDT、BUSDのコントラクトアドレスを入力)

・to (address):自分のアドレス(Swapしたトークンの行先)

・deadline (uint256):Unixtimeでの制限時間
Unixtimeと通常の日時を変換する場合はこのサイトが便利
2022-12-18 00:00:00までにSwapを行う場合1671289200となる。

上記を入力すれば所謂直コンでのSwapはできます。USDT->BUSDではなく、USDT->BUSD->CAKEをしたいならPathに入力するものを変えればOKです。簡単ですね。

iii)web3.pyなどからABIを用いてSwapする。

(コード:https://colab.research.google.com/drive/16KtsMGo30FBWBAYGEEAmQ0YWsWcQX9Ti?usp=sharing

#Python
import time
from web3 import Web3

web3 = Web3(Web3.HTTPProvider('https://bsc-dataseed.binance.org/'))


contract_address = Web3.toChecksumAddress('0x10ED43C718714eb63d5aA57B78B54704E256024E')#pancakeのRouter Address
account_address="自分のアドレス"#自分のアドレスに変更すること
secret_key="秘密鍵"#ホントはベタ打ちはよくない。
abi_pancake=[{"inputs":[{"internalType":"address","name":"_factory","type":"address"},{"internalType":"address","name":"_WETH","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"WETH","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenA","type":"address"},{"internalType":"address","name":"tokenB","type":"address"},{"internalType":"uint256","name":"amountADesired","type":"uint256"},{"internalType":"uint256","name":"amountBDesired","type":"uint256"},{"internalType":"uint256","name":"amountAMin","type":"uint256"},{"internalType":"uint256","name":"amountBMin","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"addLiquidity","outputs":[{"internalType":"uint256","name":"amountA","type":"uint256"},{"internalType":"uint256","name":"amountB","type":"uint256"},{"internalType":"uint256","name":"liquidity","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amountTokenDesired","type":"uint256"},{"internalType":"uint256","name":"amountTokenMin","type":"uint256"},{"internalType":"uint256","name":"amountETHMin","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"addLiquidityETH","outputs":[{"internalType":"uint256","name":"amountToken","type":"uint256"},{"internalType":"uint256","name":"amountETH","type":"uint256"},{"internalType":"uint256","name":"liquidity","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"factory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"uint256","name":"reserveIn","type":"uint256"},{"internalType":"uint256","name":"reserveOut","type":"uint256"}],"name":"getAmountIn","outputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"reserveIn","type":"uint256"},{"internalType":"uint256","name":"reserveOut","type":"uint256"}],"name":"getAmountOut","outputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"}],"name":"getAmountsIn","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"}],"name":"getAmountsOut","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountA","type":"uint256"},{"internalType":"uint256","name":"reserveA","type":"uint256"},{"internalType":"uint256","name":"reserveB","type":"uint256"}],"name":"quote","outputs":[{"internalType":"uint256","name":"amountB","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"tokenA","type":"address"},{"internalType":"address","name":"tokenB","type":"address"},{"internalType":"uint256","name":"liquidity","type":"uint256"},{"internalType":"uint256","name":"amountAMin","type":"uint256"},{"internalType":"uint256","name":"amountBMin","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"removeLiquidity","outputs":[{"internalType":"uint256","name":"amountA","type":"uint256"},{"internalType":"uint256","name":"amountB","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"liquidity","type":"uint256"},{"internalType":"uint256","name":"amountTokenMin","type":"uint256"},{"internalType":"uint256","name":"amountETHMin","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"removeLiquidityETH","outputs":[{"internalType":"uint256","name":"amountToken","type":"uint256"},{"internalType":"uint256","name":"amountETH","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"liquidity","type":"uint256"},{"internalType":"uint256","name":"amountTokenMin","type":"uint256"},{"internalType":"uint256","name":"amountETHMin","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"removeLiquidityETHSupportingFeeOnTransferTokens","outputs":[{"internalType":"uint256","name":"amountETH","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"liquidity","type":"uint256"},{"internalType":"uint256","name":"amountTokenMin","type":"uint256"},{"internalType":"uint256","name":"amountETHMin","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"bool","name":"approveMax","type":"bool"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"removeLiquidityETHWithPermit","outputs":[{"internalType":"uint256","name":"amountToken","type":"uint256"},{"internalType":"uint256","name":"amountETH","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"liquidity","type":"uint256"},{"internalType":"uint256","name":"amountTokenMin","type":"uint256"},{"internalType":"uint256","name":"amountETHMin","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"bool","name":"approveMax","type":"bool"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"removeLiquidityETHWithPermitSupportingFeeOnTransferTokens","outputs":[{"internalType":"uint256","name":"amountETH","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenA","type":"address"},{"internalType":"address","name":"tokenB","type":"address"},{"internalType":"uint256","name":"liquidity","type":"uint256"},{"internalType":"uint256","name":"amountAMin","type":"uint256"},{"internalType":"uint256","name":"amountBMin","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"bool","name":"approveMax","type":"bool"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"removeLiquidityWithPermit","outputs":[{"internalType":"uint256","name":"amountA","type":"uint256"},{"internalType":"uint256","name":"amountB","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swapETHForExactTokens","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountOutMin","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swapExactETHForTokens","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountOutMin","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swapExactETHForTokensSupportingFeeOnTransferTokens","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOutMin","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swapExactTokensForETH","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOutMin","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swapExactTokensForETHSupportingFeeOnTransferTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOutMin","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swapExactTokensForTokens","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOutMin","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swapExactTokensForTokensSupportingFeeOnTransferTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"uint256","name":"amountInMax","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swapTokensForExactETH","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"uint256","name":"amountInMax","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swapTokensForExactTokens","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]
#https://bscscan.com/address/0x10ed43c718714eb63d5aa57b78b54704e256024e#code のContract ABIをコピペ

swap_contract = web3.eth.contract(contract_address, abi=abi_pancake)


amountIn = int(1*10**18)#使うUSDTの量
amountOutMin = int(0.99*10**18)#Swapした際に最低限得たいBUSDの量

input_token_address="0x55d398326f99059fF775485246999027B3197955"#USDT
output_token_address="0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56"#BUSD
path = [input_token_address, output_token_address]

to="自分のアドレス"#自分のアドレスに変更すること

deadline = int(time.time() + 60)#Unixtimeでの制限時間

#swapExactTokensForTokensの中身をinput_swapにいれる
input_swap = swap_contract.functions.swapExactTokensForTokens(
  amountIn,
  amountOutMin,
  path,
  to,
  deadline
)
'''
buildTransactionで
'value','gas','chainId','from','nonce','gasPrice','to','data':の値を作る
'''
tx = input_swap.buildTransaction({
  'from': account_address,
  'nonce': web3.eth.getTransactionCount(account_address),
  'gasPrice': Web3.toWei('5', 'gwei'),
})
print(tx['data'])

signed_tx = web3.eth.account.signTransaction(tx, secret_key)
#emitted = web3.eth.sendRawTransaction(signed_tx.rawTransaction) #実際にTXを送る時はコメントアウトすること。

*注意
buildTransaction → build_transaction 
signTransaction → sign_transaction
sendRawTransaction → send_raw_transaction
estimategas → estimate_gas が現在推奨されてます。

https://web3py.readthedocs.io/en/latest/web3.eth.html

 流れとしてはii)のようにswapExactTokensForTokensで必要なInputをinput_swapの中に入れ、buildTransactionでTXの中身を作り、signTransactionで秘密鍵で署名、sendRawTransactionでTxを送る、となって います。
 細かい変数の意味はコメントに書いておいたのでそっちを読んでください。
 
 web3.pyでSwapする場合、ほとんどの人はこのやり方を採用しているのではないでしょうか。
 ただ、これは下記の理由でTXを送る速度は遅いです。

a)nonceを事前取得していないので、nonce取得のためにRPCと一回通信が必要
b)buildTransactionはestimategasをするため、RPCと一回通信する。
(gaslimitを見積もる操作が入る)

 なので、私はこの方法でTXを送っていません。

iv)web3.pyでABIを用いずにSwapする。

 やっと今日の本題です。
 ABIを用いず、かつ通信回数をできる限り減らしてSwapする方法について述べます。
 やることとしては、

 a)Inputdataを生成する。
 b)生成したInputdataを用いてRouter addressにTXを送信する。

 これだけです。

a)Inputdataを生成する。

 Inputdataというのは、コントラクトに含まれる関数を実行する際に必要な関数名と中身を渡すためのものです。BSCScanでは以下のような感じの数字の羅列を見たことがある人もいるかもしれません。

適当なTX https://bscscan.com/tx/0x15f9b664acc870e1eb3969579f0c5d3b060d831bbd27fda762e669bcf758662d

構成としてはMethodID(関数名みたいなもの)+64文字ずつに区分した引数となっています。

swapExactTokensForTokensの場合、コントラクト内では

 swapExactTokensForTokens
(uint256 amountIn, uint256 amountOutMin, address[] path
, address to, uint256 deadline)

と定義されており、これを日本語に直すと、
 swapExactTokensForTokensという関数は、
 uint256型のamountIn
 uint256型のamountOutMin
 address[]型のpath
 address型のto
 uint256型のdeadline
 で構成されることを意味します。
 
 これをInputDataに変換する場合、swapExactTokensForTokensをMethodIDに変換(0x38ed1739)し、amountInなどの必要な事項を書けばOKです。0x38ed1739はルーターアドレスを見て、他の人がSwapしている内容を10文字分パクればOKです。


(*ちゃんとやりたい場合は、以下のようなコードを動かせばMethodIDは取得できます。)

#web3.pyのインストール Resetruntimeを押すこと
!pip install web3
!pip uninstall -y sha3
!pip uninstall -y pysha3
!pip install pysha3
from Crypto.Hash import keccak
import binascii

keccak256 = keccak.new(data=b"swapExactTokensForTokens(uint256,uint256,address[],address,uint256)", digest_bits=256).digest()
'0x'+str(binascii.hexlify(keccak256))[2:10]


 swapExactTokensForTokensの場合、具体的にどのようなInputDataを作れば良いかと言うと、

#作る必要があるInputDataの中身
0x38ed1739#MethodID
0000000000000000000000000000000000000000000000008ac7230489e80000#amountIn
0000000000000000000000000000000000000000000000008963dd8c2c5e0000#amountOutMin
00000000000000000000000000000000000000000000000000000000000000a0#謎
0000000000000000000000000000000000000000000000000000000000000000#to
0000000000000000000000000000000000000000000000000000000066fe9a32#deadline
0000000000000000000000000000000000000000000000000000000000000002#Pathの数
00000000000000000000000055d398326f99059fF775485246999027B3197955#USDT
000000000000000000000000e9e7CEA3DedcA5984780Bafc599bD69ADd087D56#BUSD

 って感じです。

 これを自動化したのがinput_generator(amountIn,amountOutMin,path)という関数で、これはRPCとの通信を必要としませんし、buildTransactionも不要です。さらに、MethodIDさえわかればABIも不要です。
 input_generatorは以下です。

def input_generator(amountIn,amountOutMin,account_address,path):
    mes1='0x38ed1739'
    m=''
    ll=int(amountIn*10**18)
    for n in range(64-len(hex(ll)[2:])):
            m=m+'0'
    amountIn_mes=m+hex(ll)[2:]  

    m2=''
    ll2=int(amountOutMin*10**18)
    for s in range(64-len(hex(ll2)[2:])):
            m2=m2+'0'
    amountOutMin_mes=m2+hex(ll2)[2:]  

    mes2=amountIn_mes
    mes3=amountOutMin_mes
    mes4='00000000000000000000000000000000000000000000000000000000000000a0'
    mes5='000000000000000000000000'+account_address[2:]
    dt1 = datetime.datetime.now()
    dt2 = dt1 + datetime.timedelta(weeks=100)
    next_time=hex(int(dt2.timestamp()))[2:]
    time_mes='00000000000000000000000000000000000000000000000000000000'+next_time
    mes6=time_mes
    mes7='0000000000000000000000000000000000000000000000000000000000000002'#path数
    mes8='000000000000000000000000'+path[0][2:]#USDT
    mes9='000000000000000000000000'+path[1][2:]#BUSD

    
    all_mes=mes1+mes2+mes3+mes4+mes5+mes6+mes7+mes8+mes9
    return all_mes

 上記の関数を読み込んだ上で以下の関数を実行すると、iii)で取り上げたbuildTransactionとほぼ同じものができます。

b)生成したInputdataを用いてRouter addressにTXを送信する。

#Python
import time
from web3 import Web3
import datetime
web3 = Web3(Web3.HTTPProvider('https://bsc-dataseed.binance.org/'))
account_address="0x2f9c38bD6E2F382C031bfda65aeA2b76BEcD6808"#自分のアドレスに変更すること このアドレスは適当
secret_key="秘密鍵"#ホントはベタ打ちはよくない。
nonce=web3.eth.getTransactionCount(account_address)
amountIn=int(1*10**18)#USDTのSwap量
amountOutMin=int(0.99*10**18)#BUSDのSwap量
input_token_address="0x55d398326f99059fF775485246999027B3197955"#USDT
output_token_address="0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56"#BUSD
path = [input_token_address, output_token_address]
all_mes=input_generator(amountIn,amountOutMin,account_address,path)

tx={'chainId': 56,#BSCのChainID
'nonce': nonce,#nonceはweb3.eth.getTransactionCount(account_address)で取得
'to': '0x10ED43C718714eb63d5aA57B78B54704E256024E',#pancakeのRouterアドレス
'value': web3.toWei(0, 'gwei'),#送金するBNBの量
'gas': 30000000,#Gaslimit
'gasPrice': web3.toWei(5, 'gwei'),#Gas代
'data':all_mes}#インプット

#signed_tx = web3.eth.account.signTransaction(tx, secret_key)
#emitted = web3.eth.sendRawTransaction(signed_tx.rawTransaction) #実際にTXを送る時はコメントアウトすること。

tx

 これはiii)とほぼ同じで、signTransaction、sendRawTransactionを使えばOKです。

(動かしたい人は以下からどうぞ)
https://colab.research.google.com/drive/1h0fz_Y5bm8bzqa2SXmg3Y84zNbfmcrol?usp=sharing


まとめと応用

 今回は簡単のためにswapExactTokensForTokensを取り上げましたが、MethodIDと中身さえわかれば、ABI不要でどんなコントラクトでも実行することが可能です。(*onlyOwnerなどのホワリスが設定されていない場合のみ)

 具体的な応用は以下です。
①Uniswap型のDEXであることはわかっているけど、コントラクトがVerifyされておらず、ABIが取得できないパターン。
 例えばSyscoinのpegasys.financeはコードがVerifyされていない。 
 また、swapExactTokensForXXXなど、独自DEXになっている場合は既存のABIを読み込む方式では対応不可ですが、今回の方式ではMethodIDさえわかれば実行可能。

https://explorer.syscoin.org/address/0x017dAd2578372CAEE5c6CddfE35eEDB3728544C4/contracts

②NFTの早押しMint
 例えばJOJOはInputdataが割れていたので以下のようなInputでTXを送れば早押しMintには勝てた。

https://bscscan.com/tx/0xbe2d0fd7f296f340d87d6ef4faa5afae9feef8fc273e6b983036e10e9455726a

③他人のアビトラコントラクト
 具体例を出すのは辞めておきますが、他人のアビトラコントラクトにハニポトークン(Scam)を踏ませることも可能。
 もちろん、onlyOwnerなどがないコントラクトでないと不可能。


 今回の内容は以上です! 以前とあるDiscordでBOT配布したのですが、buildTransactionをしないのは初めて見た、と言われたので意外と知られてないのかもな、と思って書きました。

  getAmountsoutをRPCで呼び出しつつ、鞘がある時に上記のコードでswapするだけでも、魔界(Metis)の最初期は30万ドルほど取れたのでバブルが恋しいです。
 コメント、指摘などはTwitterまで!


いいなと思ったら応援しよう!