前言
由於 PostgreSQL 的 numeric 型別可以處理非常大的數字,因此在處理加密貨幣金額時,可以使用 PostgreSQL 來儲存。
以 Solidity 的 uint256 型別為例,可以使用數字型別的 numeric(78,0) 來儲存。
常用的數字型別如下:
| Type | 
Storage Size | 
Range | 
smallint | 
2 bytes | 
-32768 到 +32767 | 
integer | 
4 bytes | 
-2147483648 到 +2147483647 | 
bigint | 
8 bytes | 
-9223372036854775808 到 +9223372036854775807 | 
numeric | 
variable | 
小數點前 131,072 位,小數點後 16,383 位 | 
安裝套件
安裝套件。
1 2 3 4
   | go get github.com/joho/godotenv go get gorm.io/gorm go get gorm.io/driver/postgres go get github.com/jackc/pgtype
   | 
 
連線
在 database.go 檔建立一個 DB() 方法,以建立連線並初始化一個 DB 實例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
   | package database
  import ( 	"fmt" 	"gorm.io/driver/postgres" 	"gorm.io/gorm" 	"log" 	"os" )
  func DB() *gorm.DB { 	dsn := fmt.Sprintf( 		"host=%s port=%s user=%s password=%s dbname=%s sslmode=disable", 		os.Getenv("DB_HOST"), 		os.Getenv("DB_PORT"), 		os.Getenv("DB_USERNAME"), 		os.Getenv("DB_PASSWORD"), 		os.Getenv("DB_DATABASE"), 	) 	db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{}) 	if err != nil { 		log.Fatal(err) 	} 	return db }
   | 
 
定義模型
在 model/log.go 檔定義資料模型。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
   | package model
  import ( 	"github.com/jackc/pgtype" 	"time" )
  type Log struct { 	TransactionHash    string         `gorm:"not null;type:char(66);primaryKey;" json:"transactionHash"` 	BlockNumber        uint64         `gorm:"not null;" json:"blockNumber"` 	EventName          string         `gorm:"not null;type:varchar(255);" json:"eventName"` 	Amount             pgtype.Numeric `gorm:"not null;type:numeric(78,0);" json:"amount"` 	CreatedAt          time.Time      `gorm:"not null;" json:"createdAt"` 	UpdatedAt          time.Time      `gorm:"not null;" json:"updatedAt"` }
   | 
 
TransactionHash 為 64 個固定字元再加上 0x 前綴,因此使用 char(66) 來儲存。 
EventName 為一個簡短的可變字串,因此使用 varchar(255) 來儲存。 
Amount 為一個龐大數字(uint256),因此使用 numeric(78,0) 來儲存。 
數字轉型
轉換 big.Int 型別到 pgtype.Numeric 型別。
1 2 3 4 5 6 7 8 9 10 11
   | import ( 	"github.com/jackc/pgtype" )
  func ToNumeric(v interface{}) *pgtype.Numeric { 	n := pgtype.Numeric{} 	if err := n.Set(v); err != nil { 		log.Fatal(err) 	} 	return &n }
   | 
 
新增資料
新增多資料。
1
   | database.DB().Create(logs)
   | 
 
批次新增資料。
1
   | database.DB().CreateInBatches(logs, 100)
   | 
 
型別擴充
如果需要將 Numeric 序列化或反序列化,需要改用 shopspring-numeric 包。
1 2 3 4 5 6 7 8 9 10 11
   | package model
  import ( 	"github.com/jackc/pgtype/ext/shopspring-numeric" )
  type Log struct { 	 	Amount             pgtype.Numeric `gorm:"not null;type:numeric(78,0);" json:"amount"` 	 }
   | 
 
遷移
在 database.go 檔建立一個 Migrate() 方法,以新增資料表。
1 2 3 4 5 6 7
   | func Migrate() { 	if err := DB().AutoMigrate( 		&model.Log{}, 	); err != nil { 		log.Fatal(err) 	} }
  | 
 
參考資料