Easy-Go-Web3
知识图谱Go 教程React Web3智能合约
需求分析系统设计设计模式Go 微服务
项目实战DevOps
Go 生态React 生态智能合约生态Web3 生态AI × Web3工具箱Web3 公司远程Web3求职
🎯 AA 工程师面试手册博客
GitHub
项目实战代币转账历史查询
初级区块链开发2周

代币转账历史查询

查询地址的所有代币转账记录,支持 ERC20/ERC721/ERC1155

技术栈

GoGingo-ethereumPostgreSQLElasticsearch

核心功能

ERC20/ERC721/ERC1155 转账解析
Transfer 事件日志索引
分页与时间范围查询
地址标签识别
导出 CSV/JSON
全文搜索支持

系统架构


┌──────────────┐     ┌─────────────┐     ┌──────────────┐
│   Clients    │────▶│   Gin API   │────▶│  Blockchain  │
│  (REST/HTTP) │     │   Service   │     │ (Event Logs) │
└──────────────┘     └─────────────┘     └──────────────┘
                            │
               ┌────────────┼────────────┐
               ▼            ▼            ▼
         ┌──────────┐ ┌──────────┐ ┌──────────┐
         │PostgreSQL│ │Elasticsearch││  Indexer │
         │ (Storage)│ │ (Search)  │ │ (Worker) │
         └──────────┘ └──────────┘ └──────────┘
  

课程章节

第一章:Transfer 事件解析

ERC20 Transfer 事件1小时
ERC721/1155 事件差异1小时
Topics 结构解析45分钟

第二章:历史数据索引

区块范围扫描1.5小时
批量日志过滤1小时
增量同步策略1小时

第三章:查询服务实现

多维度查询1小时
分页优化45分钟
导出功能45分钟

第四章:Elasticsearch 全文搜索

ES 索引设计1小时
地址标签丰富1小时
搜索 API 实现1小时

核心代码实现

Transfer 事件解析

go
1// Transfer 事件签名
2var (
3 // ERC20/ERC721: Transfer(address,address,uint256)
4 TransferEventSig = crypto.Keccak256Hash([]byte("Transfer(address,address,uint256)"))
5 // ERC1155: TransferSingle(address,address,address,uint256,uint256)
6 TransferSingleSig = crypto.Keccak256Hash([]byte("TransferSingle(address,address,address,uint256,uint256)"))
7)
8
9type TokenTransfer struct {
10 TxHash string `json:"tx_hash"`
11 BlockNumber uint64 `json:"block_number"`
12 Timestamp time.Time `json:"timestamp"`
13 Contract string `json:"contract"`
14 TokenType string `json:"token_type"`
15 From string `json:"from"`
16 To string `json:"to"`
17 TokenID *big.Int `json:"token_id,omitempty"`
18 Amount *big.Int `json:"amount"`
19}
20
21func (s *TransferService) ParseTransferLog(log *types.Log) (*TokenTransfer, error) {
22 switch log.Topics[0] {
23 case TransferEventSig:
24 return s.parseERC20or721Transfer(log)
25 case TransferSingleSig:
26 return s.parseERC1155Single(log)
27 default:
28 return nil, fmt.Errorf("unknown event")
29 }
30}

历史数据索引

go
1func (i *Indexer) IndexRange(ctx context.Context, from, to uint64) error {
2 for start := from; start <= to; start += uint64(i.batch) {
3 end := min(start+uint64(i.batch)-1, to)
4
5 query := ethereum.FilterQuery{
6 FromBlock: big.NewInt(int64(start)),
7 ToBlock: big.NewInt(int64(end)),
8 Topics: [][]common.Hash{{TransferEventSig, TransferSingleSig}},
9 }
10
11 logs, err := i.client.FilterLogs(ctx, query)
12 if err != nil {
13 return fmt.Errorf("filter logs failed: %w", err)
14 }
15
16 transfers := make([]*TokenTransfer, 0, len(logs))
17 for _, log := range logs {
18 transfer, err := i.parseTransferLog(&log)
19 if err != nil {
20 continue
21 }
22 transfers = append(transfers, transfer)
23 }
24
25 if err := i.storage.BatchSave(ctx, transfers); err != nil {
26 return err
27 }
28 log.Printf("Indexed blocks %d-%d: %d transfers", start, end, len(transfers))
29 }
30 return nil
31}

多维度查询

go
1type TransferQuery struct {
2 Address string `form:"address"`
3 Contract string `form:"contract"`
4 TokenType string `form:"token_type"`
5 Direction string `form:"direction"` // in, out, all
6 StartTime *time.Time `form:"start_time"`
7 EndTime *time.Time `form:"end_time"`
8 Page int `form:"page"`
9 PageSize int `form:"page_size"`
10}
11
12func (s *TransferStorage) Query(ctx context.Context, q *TransferQuery) (*TransferResult, error) {
13 query := s.db.Model(&TokenTransfer{})
14
15 if q.Address != "" {
16 addr := strings.ToLower(q.Address)
17 switch q.Direction {
18 case "in":
19 query = query.Where("LOWER(to_address) = ?", addr)
20 case "out":
21 query = query.Where("LOWER(from_address) = ?", addr)
22 default:
23 query = query.Where("LOWER(from_address) = ? OR LOWER(to_address) = ?", addr, addr)
24 }
25 }
26
27 var total int64
28 query.Count(&total)
29
30 var transfers []*TokenTransfer
31 offset := (q.Page - 1) * q.PageSize
32 query.Order("timestamp DESC").Offset(offset).Limit(q.PageSize).Find(&transfers)
33
34 return &TransferResult{Transfers: transfers, Total: total}, nil
35}
Gas 价格追踪器DEX 聚合器后端
Easy-Go-Web3

构建 Go 后端与 Web3 的学习之路。从基础到进阶,从理论到实践,助你成为全栈区块链开发者。

学习路径

  • 知识图谱
  • Go 教程
  • Go 微服务
  • 面试手册

资源中心

  • 工具箱
  • DevOps 工具
  • Web3 生态
  • 博客

© 2025 Easy-Go-Web3. All rights reserved.

Created withbyhardybao