# 监听以太坊mempool **Published by:** [Alex](https://paragraph.com/@alex-2/) **Published on:** 2023-06-25 **URL:** https://paragraph.com/@alex-2/mempool ## Content 以太坊mempool(内存池)是已经提交、随时可以打包、但还没有成功打包的交易,它随时都可能被打包广播。 默认mempool是公开的,任何人都可以访问。这给MEV提供了非常重要的基础。而监听mempool里的交易,也是非常重要非常基础的操作。 这一切,靠的是geth提供的API(不是标准API ,是geth特有的API)。 标准API列表: https://ethereum.github.io/execution-apis/api-documentation/ geth特有API eth_subscribe https://geth.ethereum.org/docs/interacting-with-geth/rpc/pubsub#newpendingtransactions 所谓的“监听”,其实就是订阅(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没有细节,其他的参数,需要你自己再查询一次。 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 } 当然了,先知道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" ) 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 ## Publication Information - [Alex](https://paragraph.com/@alex-2/): Publication homepage - [All Posts](https://paragraph.com/@alex-2/): More posts from this publication - [RSS Feed](https://api.paragraph.com/blogs/rss/@alex-2): Subscribe to updates - [Twitter](https://twitter.com/alexgiantwhale): Follow on Twitter