Contract name: BoredApeYachtClub
2023-06-13 16:32:57 インフォ 契約アドレス: 0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d
The contract with the address ```0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d``` is determined to be ERC721 standard.
In this part, we introduced how to determine ERC721 contracts, so contracts like ERC20, which do not support the ERC165 standard, will have to be identified in another way. Do you know how to check if a contract is ERC20 standard?
## Encoding calldata
What is ```calldata```? By the author's understanding, a simple layman's description here is:
> The "calldata" is the encoding of a function call or parameter in Ethereum, and the "calldata" is encoded according to the ABI (Application Binary Interface) specification of the contract.
For example, we can encode the ```balanceOf``` and ```transfer``` method calls of the ERC20 contract we studied in the previous course, together with the parameters of the calls, into a ```calldata```. In some application scenarios, such as **interaction between contracts**, this scenario will use ```calldata```, and of course there are many other application scenarios that are not listed here.
How to code a smart contract function call to get ```calldata```?
In the FMZ Quant Trading Platform, you can use ```exchange.IO("encode", ...)``` to encode smart contract function calls, the use of exchange.IO("encode", ...) is very simple. The first parameter of the function is the fixed string ```"encode"```; the second parameter is the address of the smart contract; the third parameter is the name of the smart contract method to be encoded; the rest of the parameters are passed to the specific parameter value of the smart contract method to be encoded.
### eth_sendRawTransaction
When we encode a smart contract method call and generate the corresponding ```calldata``` data, if this smart contract method is a Write method (i.e.: write operation), we need to use the generated ```calldata``` data as the data field of the transaction and then use the Ethereum RPC method ```eth_ sendRawTransaction``` to send a request containing the raw data of that transaction to the Ethereum network.
The ```eth_sendRawTransaction``` method has only one parameter, ```data```:
> data: The signed transaction (typically signed with a library, using your private key)
The ```data``` parameter is a transaction data after the signature calculation, and the transaction data structure of Ethereum has the following main fields:
```javascript
{
"nonce": "0x1", // Number of transactions on the account of the sender of the transaction
"gasPrice": "0x12a05f200", // Traded Gas price
"gasLimit": "0x5208", // Gas limit for trading
"to": "0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2", // Target contract address or recipient address
"value": "0x4563918244F40000", // Number of Ethereum transferred
"data": "0x0123456789ABCDEF", // Data to send to the contract
}
Ethereumのトランザクションに署名するには?
FMZの量子取引プラットフォームでは,Encode()
署名計算を行うための関数です. 具体的な例は次のコースで書きます.
執行についてcalldata
先程学習した RPC 方法を使いますeth_call
実行する方法を説明しましたeth_call
Ethereum の RPC メソッドは,ただ演示したWrite
このセクションでは,我々は使用しますcalldata
スマートコントラクトの実行を実証するための方法です.balanceOf
WETH契約の方法で,ウォレット内のWETHトークンの現在の残高を読み取ります.
以太坊のメインネットでテストします.
function toAmount(s, decimals) {
return Number((BigDecimal(BigInt(s)) / BigDecimal(Math.pow(10, decimals))).toString())
}
function main() {
// ABI for WETH contracts
var abiWETH = `[{"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"}]`
// WETH contract address
var wethAddress = "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"
// Register ABI for WETH contracts
exchange.IO("abi", wethAddress, abiWETH)
// The wallet address of the currently configured exchange object
var walletAddress = exchange.IO("address")
// Coded WETH contract's deposit method call
var calldataForDeposit = exchange.IO("encode", wethAddress, "balanceOf(address)", walletAddress)
Log("calldataForDeposit:", "0x" + calldataForDeposit)
// Construct the transaction as the first parameter of eth_call
var transaction = {
"from" : walletAddress,
"to" : wethAddress,
"data" : "0x" + calldataForDeposit,
}
// The second parameter of eth_call
var blockNumber = "latest"
// Call with eth_call
var ret = exchange.IO("api", "eth", "eth_call", transaction, blockNumber)
var wethBalance = exchange.IO("decode", "uint256", ret) // You can use exchange.IO("decode", ...) function to decode
Log("wethBalance:", toAmount(wethBalance, 18)) // Converted from wei to WETH units
}
デバッグツールで実行:
2023-06-15 11:51:31 Info wethBalance: 0.015
2023-06-15 11:51:31 Info calldataForDeposit: 0x70a082310000000000000000000000006b3f11d807809b0b1e5e3243df04a280d9f94bf4
スマートコントラクトのメソッドがリターン値を持っている場合,exchange.IO("decode", ...)
解読する機能です.calldata
スマートコントラクトを呼び出す方法と同じですbalanceOf
テスト財布の0.015WETHの残高を得ました
Write メソッドのコールデータを実行するには,RPC メソッドを使用する必要があります.eth_sendRawTransaction
.
以下のEthereum メインネットでテストしてみましょう
function toAmount(s, decimals) {
return Number((BigDecimal(BigInt(s)) / BigDecimal(Math.pow(10, decimals))).toString())
}
function toInnerAmount(s, decimals) {
return (BigDecimal(s)*BigDecimal(Math.pow(10, decimals))).toFixed(0)
}
function main() {
// ABI for WETH contracts
var abiWETH = `[{"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"}]`
// WETH contract address
var wethAddress = "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"
// Register ABI for WETH contract
exchange.IO("abi", wethAddress, abiWETH)
// The wallet address of the currently configured exchange object
var walletAddress = exchange.IO("address")
// Coded WETH contract's deposit method call
var calldataForDeposit = exchange.IO("encode", wethAddress, "deposit")
Log("calldataForDeposit:", "0x" + calldataForDeposit)
// Get nonce
var nonce = exchange.IO("api", "eth", "eth_getTransactionCount", walletAddress, "pending")
// Get gasPrice
var gasPrice = exchange.IO("api", "eth", "eth_gasPrice")
// Call the deposit method to change ETH to WETH, you need to transfer ETH, here we convert 0.01ETH to a hexadecimal value in wei
var innerAmount = BigInt(Number(toInnerAmount(0.005, 18))).toString(16)
// The transaction call object:
var obj = {
"from" : walletAddress,
"to" : wethAddress,
"gasPrice" : gasPrice,
"value" : "0x" + innerAmount,
"data" : "0x" + calldataForDeposit,
}
// Calculate gasLimit
var gasLimit = exchange.IO("api", "eth", "eth_estimateGas", obj)
// Construct a transaction
var transaction = {
"to": wethAddress,
"value": toAmount("0x" + innerAmount, 0), // Convert to decimal
"data": "0x" + calldataForDeposit,
"gasLimit": toAmount(gasLimit, 0), // Convert to decimal
"gasPrice": toAmount(gasPrice, 0), // Convert to decimal
"nonce": toAmount(nonce, 0), // Convert to decimal
"chainId": 1, // Ethereum mainnet Id
}
Log("transaction:", transaction)
// Signature, your key is replaced with your private key
var signedTx = Encode("signTx", "string", "hex", JSON.stringify(transaction), "hex", "0x" + "your key")
Log("signedTx:", "0x" + signedTx)
// Call eth_sendRawTransaction to send a transaction
var ret = exchange.IO("api", "eth", "eth_sendRawTransaction", "0x" + signedTx)
return ret
}
デバッグツールで実行:
2023-06-15 09:58:50 Info signedTx: 0xf86f4f8504202067888...
2023-06-15 09:58:50 Info transaction: {"to":"0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2","value":5000000000000000,"data":"0xd0e30db0","gasLimit":27938,"gasPrice":17718863752,"nonce":79,"chainId":1}
2023-06-15 09:58:50 Info calldataForDeposit: 0xd0e30db0
実行するvar ret = exchange.IO("api", "eth", "eth_sendRawTransaction", "0x" + signedTx)
返されたトランザクションハッシュは:0x2ff585504b0fe59b0122f696e8808abfe2f3ce263448066533f3bb8a4f55e8e6
.....eth_sendRawTransaction
callは,その内のコールデータを実行し,deposit
WETH 契約の方法で送られた 0.005 ETH を WETH に交換する.
すべてのトランザクションが集約されます.Mempool
(トランザクションメモリープール) で,高料金で取引が最初にパッケージ化されるように,マイニングの利益を最大化するために,鉱夫も探します.したがって,通常,トランザクションのガス価格設定が高くなるほど,パッケージ化される可能性が高まります.
取引のスクリプトもMempool
例えば,取引が高い為替スリップで設定されている場合,取引はこれらのトランザクションスクリプトによって"サンドイッチ攻撃"の対象となる可能性があります.pending
(待機,パッケージ化される) 取引Mempool
?
RPC 方法を使いますeth_getBlockByNumber
しかし,我々は,特定の通過しないblockNumber
このとき,私たちは,"pending"
tag.
function main() {
var data = exchange.IO("api", "eth", "eth_getBlockByNumber", "pending", true)
if (Array.isArray(data.transactions)) {
for (var i = 0; i < data.transactions.length; i++) {
Log(data.transactions[i])
}
}
}
デバッグツールで実行:
2023-06-18 19:23:05 Info {"blockNumber":"0x10b2027","type":"0x2","accessList":[],"blockHash":"0xf833ed36435c53d63bd7109bb1e85383075534410c14573881bf26d912f46a89","from":"0xd50521974d62f1fa34b8e81cb742ccf6147d05ff","gasPrice":"0x32ea2db37","hash":"0xf8f10f8f473c340b021298feb48d0affe529e8737a309c4cc1902e8989ef0914","input":"0xa22cb4650000000000000000000000001e0049783f008a0085193e00003d00cd54003c710000000000000000000000000000000000000000000000000000000000000001","v":"0x0","value":"0x0","maxFeePerGas":"0x48a413364","maxPriorityFeePerGas":"0x5f5e100","nonce":"0x8","r":"0x8c1cc36f43b02c9e9e454153588cc9d38757f1da69ec49d3cfdda74ab69e06a8","s":"0x2f3dd3e5ddf9e5d42c128a8e900026aca7568fa83c68cf332e1328066ee8d03a","transactionIndex":"0x3a","chainId":"0x1","gas":"0x1142d","to":"0x8c3c0274c33f263f0a55d129cfc8eaa3667a9e8b"}
2023-06-18 19:23:05 Info {"input":"0x646174613a2c7b2270223a226572632d3230222c226f70223a226d696e74222c227469636b223a2265746873222c226964223a223139323732222c22616d74223a2231303030227d","nonce":"0x1d","blockHash":"0xf833ed36435c53d63bd7109bb1e85383075534410c14573881bf26d912f46a89","from":"0xe7fa86855af674837cea1b58f88b5352543ca27b","gas":"0x81cc","gasPrice":"0x32ea2db37","to":"0xe7fa86855af674837cea1b58f88b5352543ca27b","chainId":"0x1","transactionIndex":"0x39","type":"0x2","value":"0x0","accessList":[],"blockNumber":"0x10b2027","hash":"0x55702f5d14736fc9d0c58fdac2d2052a602db171c46b5e1fa9ff6af5c277f9a2","maxFeePerGas":"0x48a413364","maxPriorityFeePerGas":"0x5f5e100","r":"0x5a703d389d23b51adf8ef0f55db8876e7392636797b68a4be6afe73e76d7e1f2","s":"0x4b4bb11257c4434a0acc2672357f8793476e4bfdf98bc30d2389ce335e7de64e","v":"0x1"}
2023-06-18 19:23:05 Info {"gas":"0x186a0","nonce":"0x46533","r":"0xfeea052a4ac2283ca058a657a806ba0916d8e7d52d2a577f150c40eb1dfbec65","s":"0x5bf0089a3c060ba787b67a205b44e1065a0d11d132b41737ab9adf0f55066811","transactionIndex":"0x38","value":"0x78f0975742c400","blockHash":"0xf833ed36435c53d63bd7109bb1e85383075534410c14573881bf26d912f46a89","chainId":"0x1","hash":"0x56bdf1b38e23db66e8d1c4014d1e9f690a9217d8a0232489210325fc69e25cf9","v":"0x25","input":"0x","type":"0x0","blockNumber":"0x10b2027","gasPrice":"0x4a817c800","from":"0x97b9d2102a9a65a26e1ee82d59e42d1b73b68689","to":"0xcb513e99c020e9d15a6eafef873fef5d9f078221"}
...
1つのデータを抽出します.
{
"blockNumber": "0x10b2027",
"type": "0x2",
"accessList": [],
"blockHash": "0xf833ed36435c53d63bd7109bb1e85383075534410c14573881bf26d912f46a89",
"from": "0xd50521974d62f1fa34b8e81cb742ccf6147d05ff",
"gasPrice": "0x32ea2db37",
"hash": "0xf8f10f8f473c340b021298feb48d0affe529e8737a309c4cc1902e8989ef0914",
"input": "0xa22cb4650000000000000000000000001e0049783f008a0085193e00003d00cd54003c710000000000000000000000000000000000000000000000000000000000000001",
"v": "0x0",
"value": "0x0",
"maxFeePerGas": "0x48a413364",
"maxPriorityFeePerGas": "0x5f5e100",
"nonce": "0x8",
"r": "0x8c1cc36f43b02c9e9e454153588cc9d38757f1da69ec49d3cfdda74ab69e06a8",
"s": "0x2f3dd3e5ddf9e5d42c128a8e900026aca7568fa83c68cf332e1328066ee8d03a",
"transactionIndex": "0x3a",
"chainId": "0x1",
"gas": "0x1142d",
"to": "0x8c3c0274c33f263f0a55d129cfc8eaa3667a9e8b"
}
FMZの量子取引プラットフォームでは,Dial
作成する機能WebSocket
接続,あなたはチェックすることができますFMZ API
学習するためのドキュメントDial
function.
このセクションのテストコードはEthereum メインネット環境で実行され,WebSocket プロトコル通信の使用により,ライブ取引でテストするために FMZ Quant を使用することが容易である. Websocket プロトコルサブスクリプションメッセージは:
{"jsonrpc": "2.0", "id": 1, "method": "eth_subscribe", "params": ["newPendingTransactions"]}
更にnewPendingTransactions
登録することもできますnewHeads
, logs
.
送信されたデータを受け取るWebSocket
接続:
{
"jsonrpc": "2.0",
"method": "eth_subscription",
"params": {
"subscription": "0x2c5c087b4aa188e008f4747828ef4e61",
"result": "0x69c4251cecb814e17cfe7a5ee41742a616f9a4d1bbf245c49b186b1006fd14d3"
}
}
更に質問ですtransaction
そのとおり"result": "0x69c4251cecb814e17cfe7a5ee41742a616f9a4d1bbf245c49b186b1006fd14d3"
特定の目的のためにtransaction
Ethereum RPC 方法を使っていますeth_getTransactionByHash
問い合わせをする
var ws = null
function main () {
// {"jsonrpc": "2.0", "id": 1, "method": "eth_subscribe", "params": ["xxxxx"]} , "xxxxx" is the specific message to subscribe to
var payload = {"jsonrpc": "2.0", "id": 1, "method": "eth_subscribe", "params": ["newPendingTransactions"]}
// wss://mainnet.infura.io/ws/v3/xxxxx , "xxxxx" is your infura key
var infuraKey = "your key"
ws = Dial("wss://mainnet.infura.io/ws/v3/" + infuraKey + "|reconnect=true&payload=" + JSON.stringify(payload))
if (!ws) {
throw "websocket link infura failed!"
}
// eth_getTransactionByHash call count
var getTransactionCounter = 0
var beginTS = new Date().getTime()
// Loop to get messages
while (true) {
// Receive push messages
var data = ws.read()
if (data) {
var ts = new Date().getTime()
if (ts - beginTS >= 1000) {
getTransactionCounter = 0
beginTS = ts
}
// Check transaction details based on txHash
if (ts - beginTS < 1000 && getTransactionCounter >= 100) {
Sleep(1000)
getTransactionCounter = 0
beginTS = ts
}
var obj = JSON.parse(data)
if (obj["params"] && obj["params"]["result"]) {
var transcationInfo = exchange.IO("api", "eth", "eth_getTransactionByHash", obj["params"]["result"])
Log(obj["params"]["result"], "transcationInfo:", transcationInfo)
}
getTransactionCounter++
}
LogStatus(_D())
}
}
function onexit() {
Log("Disconnect WS connection")
ws.close()
}
Webソケット接続によって送られるデータを受信できます. データは常に送られます.transaction
:
{
"maxPriorityFeePerGas": "0x5f5e100",
"nonce": "0x1a9",
"accessList": [],
"blockNumber": "0x10b1c9f",
"from": "0x5888700be02f52c8adf85890886ef84a6b8a7829",
"blockHash": "0x92c3d77ea218cdc0967ab74b6005bb393b92355047f206c7e2d59d41828e7fa9",
"chainId": "0x1",
"gasPrice": "0x34fdbf43d",
"s": "0x7d86ae29a786a61b9e74a7a9e2cc4b39b7913aa3d4c3816ccb07528fed82048a",
"to": "0xfc2068c3d47b575a60f6a4a7bf60dea0ac368e01",
"type": "0x2",
"v": "0x1",
"value": "0x0",
"gas": "0x1aad3",
"hash": "0x2c77c0704aefbb26db460cbb71efdb488df968ad53d2c2b3f1e1172056b40b22",
"input": "0x42842e0e0000000000000000000000005888700be02f52c8adf85890886ef84a6b8a7829000000000000000000000000d2d07e4d1bb0f40ac3e4aa7cc3ad05d348bfd2c3000000000000000000000000000000000000000000000000000000000000180b",
"maxFeePerGas": "0x4712d1273",
"r": "0x8ec58f95f6d9729a6eee075e6976658b6c5346cbc90eb68ac361a40af073b10e",
"transactionIndex": "0xc1"
}
抜粋 (部分省略) ログデータ:
2023-06-18 16:20:07 Info Disconnect WS connection
2023-06-18 16:20:07 Info 0xba07ca903f9eafbfa7d494bb26197713034b9ca2dd3c19bc0898af3f35b59343 transcationInfo: {"accessList":[],"from":"0xe2977d60182da068dfd78693f96362ee7a2e9644","nonce":"0xf","value":"0x0","blockHash":"0x92c3d77ea218cdc0967ab74b6005bb393b92355047f206c7e2d59d41828e7fa9","blockNumber":"0x10b1c9f","chainId":"0x1","hash":"0xba07ca903f9eafbfa7d494bb26197713034b9ca2dd3c19bc0898af3f35b59343","maxFeePerGas":"0x530c30b70","r":"0xf28bfdf372a5401a2e00675c6ebe8d5e73f2c955db44b1aa56240b9197d6cbc7","type":"0x2","v":"0x0","gas":"0x21079","gasPrice":"0x367b3783d","input":"0x657bb1130000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000001e0300000000000000000000000033c6eec1723b12c46732f7ab41398de45641fa42000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000041976bd7d021a5b94cbba72b291093b50a0ecf21d1c6cd8193fbfcd685c4723ce068feb249bdcace58c28eb3b6cc647e8c839b0826c84f8dfe4c31d57d1ac1f0111b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000648ebef50000000000000000000000000000000000000000000000000000000000000000","maxPriorityFeePerGas":"0x1dcd6500","s":"0x71d51246bb60e792f963a3c75c46fd8f557921ce6face7224c944e1768a76ca","to":"0x0b51eb9d0e54c562fedc07ceba453f05b70c4b79","transactionIndex":"0x40"}
2023-06-18 16:20:07 Info 0x2c77c0704aefbb26db460cbb71efdb488df968ad53d2c2b3f1e1172056b40b22 transcationInfo: {"maxPriorityFeePerGas":"0x5f5e100","nonce":"0x1a9","accessList":[],"blockNumber":"0x10b1c9f","from":"0x5888700be02f52c8adf85890886ef84a6b8a7829","blockHash":"0x92c3d77ea218cdc0967ab74b6005bb393b92355047f206c7e2d59d41828e7fa9","chainId":"0x1","gasPrice":"0x34fdbf43d","s":"0x7d86ae29a786a61b9e74a7a9e2cc4b39b7913aa3d4c3816ccb07528fed82048a","to":"0xfc2068c3d47b575a60f6a4a7bf60dea0ac368e01","type":"0x2","v":"0x1","value":"0x0","gas":"0x1aad3","hash":"0x2c77c0704aefbb26db460cbb71efdb488df968ad53d2c2b3f1e1172056b40b22","input":"0x42842e0e0000000000000000000000005888700be02f52c8adf85890886ef84a6b8a7829000000000000000000000000d2d07e4d1bb0f40ac3e4aa7cc3ad05d348bfd2c3000000000000000000000000000000000000000000000000000000000000180b","maxFeePerGas":"0x4712d1273","r":"0x8ec58f95f6d9729a6eee075e6976658b6c5346cbc90eb68ac361a40af073b10e","transactionIndex":"0xc1"}
2023-06-18 16:20:07 Info 0xbc42d5db10e5cb2e888c76005c522cb2474a0c0a7325feb867b618f69ff26f2a transcationInfo: {"accessList":[],"blockNumber":"0x10b1c9f","gas":"0x1cc12b","hash":"0xbc42d5db10e5cb2e888c76005c522cb2474a0c0a7325feb867b618f69ff26f2a","maxFeePerGas":"0x6ab262e5c","value":"0x0","v":"0x1","chainId":"0x1","from":"0xc1b634853cb333d3ad8663715b08f41a3aec47cc","input":"0x8f111f3c000000000000000000000000000000000000000000000000000000000003b83700000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000e0fa2000000000000000000000000e64a54e2533fd126c2e452c5fab544d80e2e4eb50000000000000000000000000000000000000000000000000000000004c6ff1c0000000000000000000000000000000000000000000000000000000004c70029000000000000000000000000000000000000000000000000000000000001822d005b1979341221e80ed20b20d832de88a8a4b535fe9990a90c165f3c95ad085ab9445c0a998c70edff76f1c2de3f4263d7e4fe3c3fb73fe7dcfbdede92371842fb883267f5408c8aaf08ba2f6c22463f19da98183d2302735615460d7380d6f9ff5e764e75bcaca9a93946cf644cd4d4448f314c4cf60cd0353f085aa0562d70e16a510b8bc4c2a09b5e7fafcd43f07dc1b5dd1782962af8f6fff7a6965bfc127e11501a72c64913d58e624333f9ec51687c7cb1bb4a9850541f1e03b2790ed4ee508052910dfe22542d900548d5243ca238811427491d49e98cf269ccab5b1724f0f9698120e406c00910c4090c0e84e0400e2706822d2a001a3964a0ca8101700a547342c2c1fff8934a988416f020a0c98f0909c7f529875f8443914e10b58145c79d38914d1fafbc9ee57ebcb377e4ac1cd252bdebe3c59e8e917fea7dbc7bf66dfc1846482a858645b95555b3ecc9ab4f9e2b0e3e78d68379b009e606a1cefe675670a5eabd5f5a2efa5d77a1084288480c98d01c70a3d8c6b854496e2a966dc9051b13b872b7c6c2c5d82676fd8e82c680514333db21db2006d23f42074021de7e61c54d88b01824d40f03d1505eb6ec6d0cb7ccd38deb821517a5e63d0e89f6bf0385f109c81ea36dd00e7a903a100290f5b47a940ed146ae9338ff8bc17a2b5bc457614d0831e743e485c0de84636b034400bf6bd192ff723045cc170e109aabf273dc9de19c9987038515b6613249f471f9ddeb31331cc1643902212d20241c417532ad7e4a9ac742b4b5f68e1019795cf9386dcf36037502c13ff51f50a2202b2c1cac1c0b38a21ec798deff778c9a6b679d16d0521d2df89c439f4f8f9425ed378f4194d03d00
2023-06-18 16:20:06 Info 0xff0945c3d682a37e18ee433d56c8bedbb93d9ac368af968ed8d53b655575e8e5 transcationInfo: {"gas":"0x5208","s":"0x63572e1fa060841b939cea0849154e55781fe0efcbdfe5ce6979b44ce0980e4a","transactionIndex":"0xa7","value":"0x113e9d515e400","blockHash":"0x92c3d77ea218cdc0967ab74b6005bb393b92355047f206c7e2d59d41828e7fa9","hash":"0xff0945c3d682a37e18ee433d56c8bedbb93d9ac368af968ed8d53b655575e8e5","nonce":"0x2","r":"0x698fe26331ad39ba89c4d30985b707792ea4ab09b25205727f8fac2a6120b54a","gasPrice":"0x35458af00","from":"0x228d93af92d03184c07aa9e39b3d2d61b666686d","input":"0x","to":"0x0246177b98a5e42835cdcfaac1c274d3e6c39486","v":"0x26","blockNumber":"0x10b1c9f","type":"0x0","chainId":"0x1"}
...
以前のコースでは,Ethereumで待機しているトランザクションを監視する 監視プログラムを書きました. WebSocketプロトコルの経由で 送られたトランザクションハッシュを取得し, 取引ハッシュに基づいて特定のトランザクションの詳細をクエリしました.
解析したものです.input
トランザクション詳細データinput
実際にはトランザクションの内容をコードします. 呼び出し関数,入力されたパラメータなどです.
WebSocket 接続によって転送されるデータのタイミングと量は,現在使用されている RPC ノードと大きく関係しており,WebSocket 接続を同時に作成する際に 2 つの異なる RPC ノードサービス (例えば,infura,ALCHEMY) によって受信される転送されたデータは,まったく同じではないことが判明しました.現在のシナリオは多くのリクエストを生成するため,私たちはまだより安定して高速な RPC サービスを使用する必要があります. WebSocket 接続は,長い間待機している多くのトランザクションハッシュもプッシュし,eth_getTransactionByHash
FMZ,node.jsでテストしました.
コンピュータをalchemy
RPCノード このとき:wss://eth-mainnet.g.alchemy.com/v2/oKmOQKbneVkxgHZfibs-iFhIlIAl6HDN
. ノードは WebSocket と REST プロトコルの両方をサポートします.
監視していますmulticall(uint256,bytes[])
まず,このメソッドの機能署名ハッシュを計算する必要があります. このメソッドは,
// Take the first 8 characters of the complete hash
// multicall: 0x5ae401dc
var sigHash = "0x" + Encode("keccak256", "string", "hex", "multicall(uint256,bytes[])").slice(0, 8)
WebSocket 接続で送られるメッセージを受信する際,最新のデータは WebSocket 接続で送られるメッセージを受信します.var data = ws.read(-2)
方法,およびread()
最新のデータがすぐに返されることを示すために -2 に設定されています.Transaction
その中にmulticall
呼び出し,使用if (tx && tx.input.indexOf(sigHash) ! == -1)
フィルターを決定するために
2つのカスタム機能が設計されなければなりません.
calcAllFuncSigHash()
: ABI をベースにしたすべてのメソッドの署名ハッシュを計算する.decodeCall()
: 解読機能次に,multicall
コードを解読操作が開始され,multicall
方法が初めて解読される場合:deadline
そしてdata
. deadline
よりよく理解できるタイムスタンプであり,data
暗号化されているcalldata
治療を継続する必要があります.decodeCall()
解読する機能です
完全な実装の例:
var ws = null
var arrLog = []
const ABI_Route = '[{"inputs":[{"internalType":"address","name":"_factoryV2","type":"address"},{"internalType":"address","name":"factoryV3","type":"address"},{"internalType":"address","name":"_positionManager","type":"address"},{"internalType":"address","name":"_WETH9","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"WETH9","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"approveMax","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"approveMaxMinusOne","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"approveZeroThenMax","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"approveZeroThenMaxMinusOne","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes","name":"data","type":"bytes"}],"name":"callPositionManager","outputs":[{"internalType":"bytes","name":"result","type":"bytes"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes[]","name":"paths","type":"bytes[]"},{"internalType":"uint128[]","name":"amounts","type":"uint128[]"},{"internalType":"uint24","name":"maximumTickDivergence","type":"uint24"},{"internalType":"uint32","name":"secondsAgo","type":"uint32"}],"name":"checkOracleSlippage","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"path","type":"bytes"},{"internalType":"uint24","name":"maximumTickDivergence","type":"uint24"},{"internalType":"uint32","name":"secondsAgo","type":"uint32"}],"name":"checkOracleSlippage","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"bytes","name":"path","type":"bytes"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOutMinimum","type":"uint256"}],"internalType":"struct IV3SwapRouter.ExactInputParams","name":"params","type":"tuple"}],"name":"exactInput","outputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint24","name":"fee","type":"uint24"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOutMinimum","type":"uint256"},{"internalType":"uint160","name":"sqrtPriceLimitX96","type":"uint160"}],"internalType":"struct IV3SwapRouter.ExactInputSingleParams","name":"params","type":"tuple"}],"name":"exactInputSingle","outputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"bytes","name":"path","type":"bytes"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"uint256","name":"amountInMaximum","type":"uint256"}],"internalType":"struct IV3SwapRouter.ExactOutputParams","name":"params","type":"tuple"}],"name":"exactOutput","outputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint24","name":"fee","type":"uint24"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"uint256","name":"amountInMaximum","type":"uint256"},{"internalType":"uint160","name":"sqrtPriceLimitX96","type":"uint160"}],"internalType":"struct IV3SwapRouter.ExactOutputSingleParams","name":"params","type":"tuple"}],"name":"exactOutputSingle","outputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"factory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"factoryV2","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"getApprovalType","outputs":[{"internalType":"enum IApproveAndCall.ApprovalType","name":"","type":"uint8"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"token0","type":"address"},{"internalType":"address","name":"token1","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"amount0Min","type":"uint256"},{"internalType":"uint256","name":"amount1Min","type":"uint256"}],"internalType":"struct IApproveAndCall.IncreaseLiquidityParams","name":"params","type":"tuple"}],"name":"increaseLiquidity","outputs":[{"internalType":"bytes","name":"result","type":"bytes"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"token0","type":"address"},{"internalType":"address","name":"token1","type":"address"},{"internalType":"uint24","name":"fee","type":"uint24"},{"internalType":"int24","name":"tickLower","type":"int24"},{"internalType":"int24","name":"tickUpper","type":"int24"},{"internalType":"uint256","name":"amount0Min","type":"uint256"},{"internalType":"uint256","name":"amount1Min","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"}],"internalType":"struct IApproveAndCall.MintParams","name":"params","type":"tuple"}],"name":"mint","outputs":[{"internalType":"bytes","name":"result","type":"bytes"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"previousBlockhash","type":"bytes32"},{"internalType":"bytes[]","name":"data","type":"bytes[]"}],"name":"multicall","outputs":[{"internalType":"bytes[]","name":"","type":"bytes[]"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"bytes[]","name":"data","type":"bytes[]"}],"name":"multicall","outputs":[{"internalType":"bytes[]","name":"","type":"bytes[]"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes[]","name":"data","type":"bytes[]"}],"name":"multicall","outputs":[{"internalType":"bytes[]","name":"results","type":"bytes[]"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"positionManager","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"pull","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"refundETH","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"selfPermit","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint256","name":"expiry","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"selfPermitAllowed","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint256","name":"expiry","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"selfPermitAllowedIfNecessary","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"selfPermitIfNecessary","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"}],"name":"swapExactTokensForTokens","outputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"}],"stateMutability":"payable","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"}],"name":"swapTokensForExactTokens","outputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amountMinimum","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"}],"name":"sweepToken","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amountMinimum","type":"uint256"}],"name":"sweepToken","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amountMinimum","type":"uint256"},{"internalType":"uint256","name":"feeBips","type":"uint256"},{"internalType":"address","name":"feeRecipient","type":"address"}],"name":"sweepTokenWithFee","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amountMinimum","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"feeBips","type":"uint256"},{"internalType":"address","name":"feeRecipient","type":"address"}],"name":"sweepTokenWithFee","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"int256","name":"amount0Delta","type":"int256"},{"internalType":"int256","name":"amount1Delta","type":"int256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"uniswapV3SwapCallback","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountMinimum","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"}],"name":"unwrapWETH9","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountMinimum","type":"uint256"}],"name":"unwrapWETH9","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountMinimum","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"feeBips","type":"uint256"},{"internalType":"address","name":"feeRecipient","type":"address"}],"name":"unwrapWETH9WithFee","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountMinimum","type":"uint256"},{"internalType":"uint256","name":"feeBips","type":"uint256"},{"internalType":"address","name":"feeRecipient","type":"address"}],"name":"unwrapWETH9WithFee","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"value","type":"uint256"}],"name":"wrapETH","outputs":[],"stateMutability":"payable","type":"function"},{"stateMutability":"payable","type":"receive"}]'
function calcAllFuncSigHash(jsonABI) {
var mapSigHash = {}
for (var i in jsonABI) {
var ele = jsonABI[i]
if (typeof(ele["name"]) != "undefined") {
if (ele["inputs"]) {
var funcName = ele["name"]
if (ele["inputs"].length == 0) {
var methodId = "0x" + Encode("keccak256", "string", "hex", funcName + "()").slice(0, 8)
mapSigHash[methodId] = {"argsTypeList": [], "argsNameList": [], "funcName": funcName}
} else {
var arr = []
var arrName = []
var argPrototype = []
for (var j in ele["inputs"]) {
var inputType = ele["inputs"][j]["type"]
if (inputType == "tuple") {
var components = ele["inputs"][j]["components"]
var tupleType = []
var protoType = []
for (var componentsIdx = 0; componentsIdx < components.length; componentsIdx++) {
tupleType.push(components[componentsIdx]["type"])
protoType.push(components[componentsIdx]["name"] + " " + components[componentsIdx]["type"])
}
arr.push("(" + tupleType.join() + ")")
arrName.push(ele["inputs"][j]["name"])
// Prototype
argPrototype.push("tuple" + "(" + protoType.join() + ")")
} else {
arr.push(inputType)
arrName.push(ele["inputs"][j]["name"])
// Prototype
argPrototype.push(inputType)
}
}
var functionSignature = funcName + "(" + arr.join() + ")"
var methodId = "0x" + Encode("keccak256", "string", "hex", functionSignature).slice(0, 8)
mapSigHash[methodId] = {"argsTypeList": arr, "argsNameList": arrName, "funcName": funcName, "argPrototype": argPrototype}
}
}
}
}
return mapSigHash
}
function decodeCall(input, abi) {
var mapSigHash = calcAllFuncSigHash(JSON.parse(abi))
var methodId = input.slice(0, 10)
var data = input.slice(10)
var decodedArgs = {}
var infoMethod = mapSigHash[methodId]
if (typeof(infoMethod) == "undefined") {
return [methodId, mapSigHash]
}
var arr = []
for (var i = 0; i < infoMethod["argsTypeList"].length; i++) {
if (infoMethod["argsTypeList"][i].startsWith("(")) {
arr.push(infoMethod["argPrototype"][i])
} else {
arr.push(infoMethod["argsTypeList"][i])
}
}
if (arr.length == 0) {
return {"funcName": infoMethod["funcName"], "args": decodedArgs}
}
var args = exchange.IO("decode", arr.join(), data)
if (!Array.isArray(args)) {
args = [args]
}
if (args.length != infoMethod["argsNameList"].length) {
Log("args:", args)
Log("infoMethod:", infoMethod)
throw "The decoded args are not equal to the argsNameList"
}
for (var i = 0; i < infoMethod["argsNameList"].length; i++) {
var key = infoMethod["argsNameList"][i]
var value = args[i]
decodedArgs[key] = value
}
return {"funcName": infoMethod["funcName"], "args": decodedArgs}
}
function main () {
// {"jsonrpc": "2.0", "id": 1, "method": "eth_subscribe", "params": ["xxxxx"]} , "xxxxx" is the specific message of the subscription
var payload = {"jsonrpc": "2.0", "id": 1, "method": "eth_subscribe", "params": ["newPendingTransactions"]}
// Use the alchemy service
ws = Dial("wss://eth-mainnet.g.alchemy.com/v2/oKmOQKbneVkxgHZfibs-iFhIlIAl6HDN" + "|reconnect=true&payload=" + JSON.stringify(payload))
if (!ws) {
throw "websocket link to alchemy failed!"
}
// eth_getTransactionByHash call count
var getTransactionCounter = 0
// Start Timestamp
var beginTS = new Date().getTime()
// Calculate function signature hash
var sigHash = "0x" + Encode("keccak256", "string", "hex", "multicall(uint256,bytes[])").slice(0, 8)
Log("sigHash:", sigHash)
// Loop for messages
while (true) {
var msg = ""
var recv = null
// Receive pushed messages, use the read parameter -2, and return the latest data immediately
var data = ws.read(-2)
if (data && data != "") {
var ts = new Date().getTime()
if (ts - beginTS >= 1000) {
getTransactionCounter = 0
beginTS = ts
}
// Check transaction details based on txHash
if (ts - beginTS < 1000 && getTransactionCounter >= 100) {
Sleep(1000)
getTransactionCounter = 0
beginTS = ts
}
var obj = JSON.parse(data)
if (obj["params"] && obj["params"]["result"]) {
var txHash = obj["params"]["result"]
var tx = exchange.IO("api", "eth", "eth_getTransactionByHash", txHash)
if (tx && tx.input.indexOf(sigHash) !== -1) {
// Decode transaction details
arrLog = []
var decodedInput = decodeCall(tx.input, ABI_Route)
// Log("----------------", txHash, "/", decodedInput["funcName"], "----------------", "#FF0000")
arrLog.push("----------------" + txHash + "/" + decodedInput["funcName"] + "----------------" + "#FF0000")
arrLog.push(tx.from + " -> " + tx.to)
for (var i = 0; i < decodedInput["args"][