Post by @alexgiantwhale • Hey
**监听以太坊mempool**
以太坊`mempool`(内存池)是**已经提交、随时可以打包、但还没有成功打包**的交易,它随时都可能被打包广播。
默认`mempool`是公开的,任何人都可以访问。这给`MEV`提供了非常重要的基础。而监听`mempool`里的交易,也是非常重要非常基础的操作。
这
Comments
- 所谓的“监听”,其实就是订阅(`subscribe`),利用`eth_subscribe`接口订阅类型为`newPendingTransactions`的消息
发送请求
{
"id": 1,
"jsonrpc": "2.0",
"method": "eth_subscribe",
"params": [
"newPendingTransactions"
]
}
响应
{"jsonrpc":"2.0","id":2,"result":"0xc3b33aa549fb9a60e95d21862596617c"}
{
"jsonrpc":"2.0",
"method":"eth_subscription",
"params":{
"subscription":"0xc3b33aa549fb9a60e95d21862596617c",
"result":"0xd6fdc5cc41a9959e922f30cb772a9aef46f4daea279307bc5f7024edc4ccd7fa"
}
}
它会返回一个标示(用于取消订阅),之后如果有新的`pending`的交易,会把交易`hash`发送过来。只有交易`hash`没有细节,其他的参数,需要你自己再查询一次。
- 当然了,先知道`hash`其实什么也干不了,还需要再查询一次,拿到具体的数据才行。好消息是,从`v1.11.0`,`geth`直接就添加了新的`SubscribeFullPendingTransactions`接口,可以直接拿到`transaction`了,简直不要太幸福。
package main
import (
"context"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethclient/gethclient"
"github.com/ethereum/go-ethereum/rpc"
"log"
"os"
"os/signal"
"syscall"
)
-
`Go`代码如下
package main
import (
"context"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethclient/gethclient"
"github.com/ethereum/go-ethereum/rpc"
"log"
"os"
"os/signal"
"syscall"
)
func watch() {
wss := os.Getenv("WSS")
rc, err := rpc.Dial(wss)
if err != nil {
log.Printf("failed to dial: %v", err)
return
}
log.Printf("connected to %s", wss)
gc := gethclient.New(rc)
hashes := make(chan common.Hash, 100)
_, err = gc.SubscribePendingTransactions(context.Background(), hashes)
if err != nil {
log.Printf("failed to SubscribePendingTransactions: %v", err)
return
}
log.Print("subscribed pending txs now")
for {
select {
case hash := <-hashes:
log.Printf("received tx %s", hash)
}
}
}
func main() {
go watch()
signalChan := make(chan os.Signal, 1)
signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM)
<-signalChan
}
-
func watch() {
wss := os.Getenv("WSS")
rc, err := rpc.Dial(wss)
if err != nil {
log.Printf("failed to dial: %v", err)
return
}
log.Printf("connected to %s", wss)
gc := gethclient.New(rc)
transactions := make(chan *types.Transaction, 100)
_, err = gc.SubscribeFullPendingTransactions(context.Background(), transactions)
if err != nil {
log.Printf("failed to SubscribePendingTransactions: %v", err)
return
}
log.Print("subscribed pending txs now")
for {
select {
case transaction := <-transactions:
// 这里的transaction是完整数据,可以直接使用
txBytes, err := transaction.MarshalJSON()
if err != nil {
continue
}
log.Printf("received tx %s", string(txBytes))
}
}
}
func main() {
go watch()
signalChan := make(chan os.Signal, 1)
signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM)
<-signalChan
}
-
打印的`transaction`内容是如下格式
{
"type": "0x2",
"chainId": "0x14a33",
"nonce": "0x5e1",
"to": "0x290b54a504a3b0cb21888e3e405afc1b2946598c",
"gas": "0xdc2d",
"gasPrice": null,
"maxPriorityFeePerGas": "0x59682f00",
"maxFeePerGas": "0x59682f64",
"value": "0x0",
"input": "0xa9059cbb000000000000000000000000f3902d46ffa3730f4733b19e5fca5acd8057316a0000000000000000000000000000000000000000000000000de0b6b3a7640000",
"accessList": [],
"v": "0x1",
"r": "0x5783d7fc6ac88e39593de34eff58b059d2bb6045227eea277969a0c4778ec63",
"s": "0x591ac696363bcaa4943b30ab680c8a7df58d27dd1b28f1a90e0345aaca0cd0b4",
"hash": "0xb0d0b538207efc9da6daf97b80e1e5e6c3a18a8a7aa45c84f413af3b0ea2c89d"
}
可见是签过名直接可以用的。
祝朋友们玩得开心,可以加我推特交流 @alexgiantwhale