You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何从内存池(Mem Pool)筛选ERC721 Mint交易?

从内存池筛选ERC721 Mint交易的解决方案

核心思路

由于Pending交易未上链,无法直接获取其触发的Transfer事件,因此需通过解析交易Calldata结合ERC721合约规则来判断:

  1. 先验证目标合约是否为ERC721(通过ERC165的supportsInterface方法)
  2. 模拟交易在pending区块执行,检查是否会产生从零地址转出的Transfer事件(这是ERC721 Mint操作的核心特征)

关键判断逻辑

ERC721的Mint操作本质是将Token从0x0000000000000000000000000000000000000000(零地址)转移到目标地址,无论公开方法名是什么,最终都会触发Transfer(address(0), recipient, tokenId)事件。通过模拟pending交易执行并捕获日志,即可判断是否为Mint操作。


Go 实现示例

步骤1:订阅Pending交易并获取交易详情

package main

import (
	"context"
	"fmt"
	"math/big"
	"strings"

	"github.com/ethereum/go-ethereum/accounts/abi"
	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/core/types"
	"github.com/ethereum/go-ethereum/ethclient"
	"github.com/ethereum/go-ethereum/ethclient/gethclient"
)

func main() {
	// 连接以太坊WS节点
	client, err := ethclient.Dial("wss://your-node-url")
	if err != nil {
		panic(err)
	}
	gethCli := gethclient.New(client)

	// 订阅Pending交易
	txCh := make(chan common.Hash, 1000)
	sub, err := client.SubscribePendingTransactions(context.Background(), txCh)
	if err != nil {
		panic(err)
	}
	defer sub.Unsubscribe()

	for {
		select {
		case txHash := <-txCh:
			tx, isPending, err := client.TransactionByHash(context.Background(), txHash)
			if err != nil || !isPending || tx.To() == nil {
				continue
			}

			contractAddr := *tx.To()
			// 验证是否为ERC721合约
			isERC721, err := isERC721Contract(client, contractAddr)
			if err != nil || !isERC721 {
				continue
			}

			// 判断是否为Mint交易
			isMint, err := isERC721MintTx(gethCli, tx)
			if err != nil || !isMint {
				continue
			}

			fmt.Printf("Found ERC721 Mint Pending Tx: %s\n", txHash.Hex())
		case err := <-sub.Err():
			panic(err)
		}
	}
}

步骤2:通过ERC165验证ERC721合约

const erc721InterfaceID = "0x80ac58cd"

func isERC721Contract(client *ethclient.Client, addr common.Address) (bool, error) {
	// 构造ERC165调用ABI
	erc165ABI, err := abi.JSON(strings.NewReader(`[{"constant":true,"inputs":[{"name":"interfaceID","type":"bytes4"}],"name":"supportsInterface","outputs":[{"name":"","type":"bool"}],"type":"function"}]`))
	if err != nil {
		return false, err
	}

	input, err := erc165ABI.Pack("supportsInterface", common.HexToBytes(erc721InterfaceID[2:]))
	if err != nil {
		return false, err
	}

	// 调用合约方法
	result, err := client.CallContract(context.Background(), ethclient.CallMsg{
		To:   &addr,
		Data: input,
	}, nil)
	if err != nil {
		return false, err
	}

	var supports bool
	err = erc165ABI.UnpackIntoInterface(&supports, "supportsInterface", result)
	return supports, err
}

步骤3:模拟交易执行并检查Transfer事件

// ERC721 Transfer事件签名哈希
var transferEventSig = common.HexToHash("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")

func isERC721MintTx(client *gethclient.Client, tx *types.Transaction) (bool, error) {
	sender, err := types.Sender(types.NewEIP155Signer(tx.ChainId()), tx)
	if err != nil {
		return false, err
	}

	// 模拟pending区块执行交易,捕获日志
	msg := ethclient.CallMsg{
		From:     sender,
		To:       tx.To(),
		Data:     tx.Data(),
		Value:    tx.Value(),
		Gas:      tx.Gas(),
		GasPrice: tx.GasPrice(),
	}

	logs, err := client.CallContractWithLogs(context.Background(), msg, big.NewInt(-1)) // -1代表pending区块
	if err != nil {
		return false, err
	}

	for _, log := range logs {
		if log.Topics[0] == transferEventSig {
			// 解析转出地址,判断是否为零地址
			fromAddr := common.BytesToAddress(log.Topics[1].Bytes()[12:])
			if fromAddr == common.Address{} {
				return true, nil
			}
		}
	}
	return false, nil
}

JavaScript 实现示例

步骤1:订阅Pending交易并获取交易详情

const { ethers } = require("ethers");

async function run() {
	// 连接以太坊WS节点
	const provider = new ethers.WebSocketProvider("wss://your-node-url");

	// 监听Pending交易
	provider.on("pending", async (txHash) => {
		try {
			const tx = await provider.getTransaction(txHash);
			if (!tx || !tx.to) return; // 跳过合约部署交易

			const contractAddr = tx.to;
			// 验证是否为ERC721合约
			const isERC721 = await checkERC721Contract(provider, contractAddr);
			if (!isERC721) return;

			// 判断是否为Mint交易
			const isMint = await checkERC721MintTx(provider, tx);
			if (!isMint) return;

			console.log(`Found ERC721 Mint Pending Tx: ${txHash}`);
		} catch (err) {
			console.error(err);
		}
	});
}

run();

步骤2:通过ERC165验证ERC721合约

const ERC165_ABI = ["function supportsInterface(bytes4 interfaceID) external view returns (bool)"];
const ERC721_INTERFACE_ID = "0x80ac58cd";

async function checkERC721Contract(provider, contractAddr) {
	const contract = new ethers.Contract(contractAddr, ERC165_ABI, provider);
	return await contract.supportsInterface(ERC721_INTERFACE_ID);
}

步骤3:模拟交易执行并检查Transfer事件

const TRANSFER_EVENT_ABI = ["event Transfer(address indexed from, address indexed to, uint256 indexed tokenId)"];

async function checkERC721MintTx(provider, tx) {
	const txData = {
		from: await tx.getSenderAddress(),
		to: tx.to,
		data: tx.data,
		value: tx.value,
		gasLimit: tx.gasLimit,
		gasPrice: tx.gasPrice
	};

	// 模拟pending区块执行,获取日志
	const logs = await provider.call(txData, "pending");
	const iface = new ethers.Interface(TRANSFER_EVENT_ABI);

	for (const log of logs) {
		try {
			const parsed = iface.parseLog(log);
			if (parsed.args.from === ethers.ZeroAddress) {
				return true;
			}
		} catch (e) {
			// 非Transfer事件,跳过
			continue;
		}
	}
	return false;
}

注意事项

  • 需使用支持pending区块调用的以太坊节点(如自部署Geth/Erigon节点,或付费节点服务)
  • 模拟交易执行可能存在一定延迟,需根据节点性能调整处理逻辑
  • 部分合约可能使用自定义Mint逻辑,但只要最终触发从零地址转出的Transfer事件,该方法即可有效识别

内容的提问来源于stack exchange,提问作者THJA JUNGLE

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 05:42:08