리소스 로딩... 로딩...

FMZ를 사용하여 에테리움 기반의 웹3 개발에 대한 쉬운 입문

저자:발명가들의 수량화 - 작은 꿈, 창작: 2023-03-28 13:32:48, 업데이트: 2024-11-11 22:28:24

: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:entent},{anonymous:false,inputs:[[anonymous:trindexed,name:src,type:adress:evaluated,type:drawn,type:uint256],{type:additional:deposit,type:ent},{anonymous:false

// WETH合约地址
var wethAddress = "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"

// 注册WETH合约的ABI
exchange.IO("abi", wethAddress, abiWETH)

// 当前配置的交易所对象的钱包地址
var walletAddress = exchange.IO("address")

// 编码WETH合约的deposit方法调用
var calldataForDeposit = exchange.IO("encode", wethAddress, "deposit")
Log("calldataForDeposit:", "0x" + calldataForDeposit)

// 获取nonce
var nonce = exchange.IO("api", "eth", "eth_getTransactionCount", walletAddress, "pending")

// 获取gasPrice
var gasPrice = exchange.IO("api", "eth", "eth_gasPrice")

// 调用deposit方法把ETH换为WETH,需要转账ETH,这里把0.01ETH转换为以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,
}

// 计算gasLimit
var gasLimit = exchange.IO("api", "eth", "eth_estimateGas", obj)

// 构造交易
var transaction = {
    "to": wethAddress,
    "value": toAmount("0x" + innerAmount, 0),   // 转换为10进制
    "data": "0x" + calldataForDeposit,
    "gasLimit": toAmount(gasLimit, 0),   // 转换为10进制
    "gasPrice": toAmount(gasPrice, 0),   // 转换为10进制
    "nonce": toAmount(nonce, 0),         // 转换为10进制
    "chainId": 1,                        // 以太坊主网Id
}
Log("transaction:", transaction)

// 签名,your key 替换为你的私钥
var signedTx = Encode("signTx", "string", "hex", JSON.stringify(transaction), "hex", "0x" + "your key")
Log("signedTx:", "0x" + signedTx)

// 调用eth_sendRawTransaction发送交易
var ret = exchange.IO("api", "eth", "eth_sendRawTransaction", "0x" + signedTx)
return ret 

}


调试工具中运行:

```run
2023-06-15 09:58:50		信息	signedTx: 0xf86f4f8504202067888...
2023-06-15 09:58:50		信息	transaction: {"to":"0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2","value":5000000000000000,"data":"0xd0e30db0","gasLimit":27938,"gasPrice":17718863752,"nonce":79,"chainId":1}
2023-06-15 09:58:50		信息	calldataForDeposit: 0xd0e30db0

실행var ret = exchange.IO("api", "eth", "eth_sendRawTransaction", "0x" + signedTx)이 함수에서 반환되는 트랜잭션 해시는:0x2ff585504b0fe59b0122f696e8808abfe2f3ce263448066533f3bb8a4f55e8e6ᅳ이건eth_sendRawTransaction이 호출은 WETH 계약에 대한 호출 데이터를 실행합니다.deposit이 방법은 0.005 ETH를 WETH로 변환합니다.

엠포울을 들어보세요

이더리움에 있는 모든 거래는 이더리움에 있는 모든 거래가 마이너에 의해 이더리움 블록체인에 포장되기 전에Mempool(거래 메모리 풀) 에서, 광부 은 또한 여기서 높은 비용의 거래 우선 포장을 찾고 광업 이익을 극대화하기 위해 있습니다. 따라서 일반적으로 거래되는 가스 가격 설정이 높을수록 더 쉽게 포장을 할 수 있습니다.

어떤 트랜잭션 스크립트도 맞습니다.Mempool유리한 거래를 찾아내기 위해 스냅 스캔을 한다. 예를 들어 거래가 너무 높은 환전 스리프트를 설정하면 거래는 거래 스크립트에서 샌드위치 공격으로 실행될 수 있다. 그렇다면 스크립트들은 어떻게 감청을 하는가?Mempool그 중pending거래가 끝나지 않았나요?

REST 프로토콜을 사용하여 경청

우리는 이미 배운 RPC 방법을 사용했습니다.eth_getBlockByNumber이 글의 내용은 이쪽에서 보여드리겠습니다.blockNumber그리고 우리는"pending"이 글은 멘토의 글입니다.

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		信息	{"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		信息	{"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		信息	{"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"}
...

이 자료를 한 번 읽어보세요.

{
	"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"
}

웹소켓 프로토콜을 사용하여 감청

우리는 발명가들의 양적 거래 플랫폼을 사용했습니다.Dial함수를 만들 수 있습니다.WebSocket연결, 볼 수 있습니다FMZ API문서를 확인하세요Dial이 함수들은

이 장의 테스트 코드는 이더리움 네트워크 환경에서 실행되며, 웹소켓 프로토콜을 사용하는 통신으로 인해 발명자를 사용하여 실제 디스크 테스트를 측정하는 것이 더 편리합니다. 웹소켓 프로토콜 가입 메시지는 다음과 같습니다.

{"jsonrpc": "2.0", "id": 1, "method": "eth_subscribe", "params": ["newPendingTransactions"]}

하지만newPendingTransactions이 글은newHeadslogs

수신되었습니다WebSocket이 글은 이보다 더 많은 정보들을 전달합니다.

{
	"jsonrpc": "2.0",
	"method": "eth_subscription",
	"params": {
		"subscription": "0x2c5c087b4aa188e008f4747828ef4e61",
		"result": "0x69c4251cecb814e17cfe7a5ee41742a616f9a4d1bbf245c49b186b1006fd14d3"
	}
}

그리고 그 중 하나에 따라:"result": "0x69c4251cecb814e17cfe7a5ee41742a616f9a4d1bbf245c49b186b1006fd14d3"더 많은 질문transactionᅳ 특정 분야에 대해transaction우리는 이더리움의 RPC 방법을 사용합니다.eth_getTransactionByHash

var ws = null 

function main () {    
    // {"jsonrpc": "2.0", "id": 1, "method": "eth_subscribe", "params": ["xxxxx"]}  , "xxxxx" 是订阅的具体消息
    var payload = {"jsonrpc": "2.0", "id": 1, "method": "eth_subscribe", "params": ["newPendingTransactions"]}
    
    // wss://mainnet.infura.io/ws/v3/xxxxx , "xxxxx" 是你的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链接infura失败!"
    }
    
    // eth_getTransactionByHash 调用计数
    var getTransactionCounter = 0

    var beginTS = new Date().getTime()

    // 循环获取消息
    while (true) {
        // 接收推送的消息
        var data = ws.read()
        if (data) {
            var ts = new Date().getTime()

            if (ts - beginTS >= 1000) {
                getTransactionCounter = 0
                beginTS = ts 
            }

            // 根据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("断开WS连接")
    ws.close()
}

웹소켓 연결에서 전송되는 데이터를 받을 수 있는 상위 코드를 실행하는 디스크를 만들고, 데이터를 계속 전송하고,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		信息	断开WS连接
2023-06-18 16:20:07		信息	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		信息	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		信息	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		信息	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"}
...

암호 해독 거래 세부 사항

지난 강의에서 우리는 이더리움에서 풀리지 않는 거래를 모니터링하는 모니터링 프로그램을 작성하여 웹소켓 프로토콜을 통해 푸시드 트랜잭션 해시를 취득하고 트랜잭션 해시에 따라 특정 트랜잭션 세부 정보를 검색했습니다.

이제 우리는 거래 세부 데이터에 대해 알아볼 것입니다.input필드 데이터는 더 분석됩니다.input필드 데이터는 혼란스러운 16진수 데이터처럼 보이지만, 실제로는 트랜잭션의 내용을 코딩합니다. 호출되는 함수와 입력된 매개 변수 등을 포함합니다.

반복된, 많은 테스트를 통해 웹소켓 연결이 전달되는 데이터의 시간 효율성과, 현재 사용 중인 RPC 노트의 수와 큰 관련이 있다는 것을 발견했습니다. 두 개의 다른 RPC 노트 서비스 (예를 들어, infura, ALCHEMY) 가 동시에 웹소켓 연결을 생성하고, 수신되는 전달 데이터는 완전히 동일하지 않습니다. 현재 상황이 많은 요청을 생성하기 때문에 비교적 안정적이고 빠른 RPC 서비스를 사용해야합니다. 웹소켓 연결은 또한 오랫동안 대기 중인 많은 트랜잭션 해시를 전송합니다.eth_getTransactionByHash去查询时往往得到一个空值(在FMZ上测试、node.js测试)。

우리는 이것을 사용했습니다alchemyRPC 노드:wss://eth-mainnet.g.alchemy.com/v2/oKmOQKbneVkxgHZfibs-iFhIlIAl6HDN이 노드는 웹소켓 프로토콜을 지원하고 REST 프로토콜을 지원합니다.

우리는 유니스와프의 중앙집권화 거래소의 스마트 계약의 경로를 감시합니다.multicall(uint256,bytes[])그래서 먼저 이 방법의 함수 서명 hash을 계산해야 합니다.

// 取完整哈希值的前8个字符
// multicall: 0x5ae401dc
var sigHash = "0x" + Encode("keccak256", "string", "hex", "multicall(uint256,bytes[])").slice(0, 8)

이전 강의의 예시를 기반으로 몇 가지 변경을 했습니다.var data = ws.read(-2)이 사이트는 인터넷에 접속하는 모든 사용자들에게read()함수 파라그램을 -2로 설정하면 바로 최신 데이터를 반환합니다. 우리는 단지 포함하는 것에 관심이 있습니다.multicall호출Transaction, 사용if (tx && tx.input.indexOf(sigHash) !== -1)필터를 판단합니다.

두 개의 사용자 정의 함수를 설계해야 합니다.

  • calcAllFuncSigHash()ABI에 기반한 모든 계산 방법의 서명 해시.
  • decodeCall(): 해독 함수.

그리고 그 다음에는multicall이 경우, 이 문자를 클릭할 때,multicall방법의 매개 변수:deadline그리고datadeadline이 모든 것은 우리가 알고 있는 것 보다 훨씬 더 중요한 것입니다.data또한, 암호화된 콜데이터입니다.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"])
                            // 原型
                            argPrototype.push("tuple" + "(" + protoType.join() + ")")
                        } else {
                            arr.push(inputType)
                            arrName.push(ele["inputs"][j]["name"])
                            // 原型
                            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 "解码后的args与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" 是订阅的具体消息
    var payload = {"jsonrpc": "2.0", "id": 1, "method": "eth_subscribe", "params": ["newPendingTransactions"]}
    
    // 使用alchemy服务
    ws = Dial("wss://eth-mainnet.g.alchemy.com/v2/oKmOQKbneVkxgHZfibs-iFhIlIAl6HDN" + "|reconnect=true&payload=" + JSON.stringify(payload))
    if (!ws) {
        throw "websocket链接alchemy失败!"
    }
    
    // eth_getTransactionByHash 调用计数
    var getTransactionCounter = 0
    
    // 起始时间戳
    var beginTS = new Date().getTime()
    
    // 计算函数签名哈希
    var sigHash = "0x" + Encode("keccak256", "string", "hex", "multicall(uint256,bytes[])").slice(0, 8)
    Log("sigHash:", sigHash)

    // 循环获取消息
    while (true) {
        var msg = ""
        var recv = null
        // 接收推送的消息,使用read参数-2,立即返回最新数据
        var data = ws.read(-2)
        if (data && data != "") {
            var ts = new Date().getTime()

            if (ts - beginTS >= 1000) {
                getTransactionCounter = 0
                beginTS = ts 
            }

            // 根据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) {
                    // 解码交易详情
                    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"]["data"].length; i++) {
                        var calldata = "0x" + decodedInput["args"]["data"][i]
                        var decodedCalldata = decodeCall(calldata, ABI_Route)

                        // Log("----------------", decodedCalldata["funcName"], "----------------", "#FF0000")
                        arrLog.push("----------------" + decodedCalldata["funcName"] + "----------------" + "#FF0000")

                        for (var key in decodedCalldata["args"]) {
                            // Log(key, decodedCalldata["args"][key])
                            arrLog.push(key + ": " + JSON.stringify(decodedCalldata["args"][key]))
                        }
                    }

                    // 输出日志
                    for (var logIdx = arrLog.length - 1; logIdx >= 0; logIdx--) {
                        Log(arrLog[logIdx])
                    }
                }
                
                getTransactionCounter++
            }
            recv = obj
        } else if (data == null) {
            msg = "缓冲区队列空了,时间:" + _D()
        }
        
        LogStatus(_D(), ", msg:", msg, ", recv:", recv)
    }
}

function onexit() {
    Log("断开WS连接")
    ws.close()
}

function onerror() {
    Log("断开WS连接")
    ws.close()

    for (var logIdx = arrLog.length - 1; logIdx >= 0; logIdx--) {        
        Log(arrLog[logIdx])
    }
}

실제 디스크 실행 테스트를 만드는 방법:

2023-06-20 17:01:00		信息	----------------0x5288a7bd6e0f57162ca763df722de73793e542734d7d2b7af5755664e2e67910/multicall----------------
2023-06-20 17:01:00		信息	0x851b594033d57c98af753bcb3a7d0237a615de32 -> 0x68b3465833fb72a70ecdf485e0e4c7bd8665fc45
2023-06-20 17:01:00		信息	----------------exactInputSingle----------------
2023-06-20 17:01:00		信息	params: {"tokenOut":"0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2","fee":"10000","recipient":"0x0000000000000000000000000000000000000002","amountIn":"8952087000296027130940868","amountOutMinimum":"41638694112306829","sqrtPriceLimitX96":"0","tokenIn":"0xe1283567345349942acdfad3692924a1b16cf3cc"}
2023-06-20 17:01:00		信息	----------------unwrapWETH9----------------
2023-06-20 17:01:00		信息	amountMinimum: "41638694112306829"
2023-06-20 17:01:00		信息	recipient: "0x851b594033d57c98af753bcb3a7d0237a615de32"
2023-06-20 16:59:03		信息	----------------0x55e0c4a38a17d3aa6e8f558a66c77e9defa9f8f6e347536363ac1b921de9aaf3/multicall----------------
2023-06-20 16:59:03		信息	0x27457ada2dd725c7d0f28e1737bdd0bf583c0f0b -> 0x68b3465833fb72a70ecdf485e0e4c7bd8665fc45
2023-06-20 16:59:03		信息	----------------swapExactTokensForTokens----------------
2023-06-20 16:59:03		信息	amountIn: "816769666850161"
2023-06-20 16:59:03		信息	amountOutMin: "40404501509302321"
2023-06-20 16:59:03		信息	path: ["0x7863e06bca47ded821fcb53ab788eeb371243eda","0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"]
2023-06-20 16:59:03		信息	to: "0x27457ada2dd725c7d0f28e1737bdd0bf583c0f0b"
2023-06-20 16:58:25		信息	sigHash: 0x5ae401dc

사진:

img

트랜잭션 해쉬가0x5288a7bd6e0f57162ca763df722de73793e542734d7d2b7af5755664e2e67910이 트랜잭션의 입력 데이터 데이터에 포함된 호출은multicall방법의 호출. 이 거래의 전송 방향은: 0x851b594033d57c98af753bcb3a7d0237a615de32 -> 0x68b3465833fb72a70ecdf485e0e4c7bd8665fc45이다.0x68b3465833fb72a70ecdf485e0e4c7bd8665fc45유니스업의 라우팅 계약 주소입니다.

img

해제되었습니다.multicall그리고 그 후로,exactInputSingle그리고unwrapWETH9그리고 그 방법의 구체적인 매개 변수와 함께.

----------------exactInputSingle----------------
params: {
    "tokenOut":"0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
    "fee":"10000",
    "recipient":"0x0000000000000000000000000000000000000002",
    "amountIn":"8952087000296027130940868",
    "amountOutMinimum":"41638694112306829",
    "sqrtPriceLimitX96":"0",
    "tokenIn":"0xe1283567345349942acdfad3692924a1b16cf3cc"
}

----------------unwrapWETH9----------------
amountMinimum: "41638694112306829"
recipient: "0x851b594033d57c98af753bcb3a7d0237a615de32"

관심있는 학생들은 이 사례를 바탕으로 더 많은 거래를 수정, 확장, 모니터링, 그리고 연쇄에서 동작을 분석할 수 있습니다.


더 많은