1
This commit is contained in:
113
utils/ethbalanceofhelper/baalanceof_helper.go
Normal file
113
utils/ethbalanceofhelper/baalanceof_helper.go
Normal file
@ -0,0 +1,113 @@
|
||||
package ethbalanceofhelper
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"go-admin/abis"
|
||||
"math/big"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/ethereum/go-ethereum"
|
||||
"github.com/ethereum/go-ethereum/accounts/abi"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/ethclient"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
"github.com/shopspring/decimal"
|
||||
)
|
||||
|
||||
// ERC-20 代币合约 ABI (部分,只包含 balanceOf)
|
||||
// ERC-20 代币合约 ABI (部分,只包含 balanceOf 和 decimals)
|
||||
const erc20ABI = `[
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [ { "name": "_owner", "type": "address" } ],
|
||||
"name": "balanceOf",
|
||||
"outputs": [ { "name": "balance", "type": "uint256" } ],
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [],
|
||||
"name": "decimals",
|
||||
"outputs": [ { "name": "", "type": "uint8" } ],
|
||||
"type": "function"
|
||||
}
|
||||
]`
|
||||
|
||||
// GetERC20Balance 查询 ERC-20 代币余额并转换为正常单位 (使用 decimal)
|
||||
func GetERC20Balance(client *ethclient.Client, tokenAbi abis.TokenABI, accountAddress string) (decimal.Decimal, error) {
|
||||
// 1. 解析 ABI
|
||||
contractABI, err := abi.JSON(strings.NewReader(erc20ABI))
|
||||
if err != nil {
|
||||
return decimal.Zero, fmt.Errorf("解析 ABI 失败: %w", err)
|
||||
}
|
||||
|
||||
// 2. 构造 balanceOf 函数调用数据
|
||||
balanceOfCallData, err := contractABI.Pack("balanceOf", common.HexToAddress(accountAddress))
|
||||
if err != nil {
|
||||
return decimal.Zero, fmt.Errorf("构造 balanceOf 调用数据失败: %w", err)
|
||||
}
|
||||
|
||||
address := common.HexToAddress(tokenAbi.TestAddress)
|
||||
// 3. 执行 balanceOf 调用
|
||||
balanceResult, err := client.CallContract(context.Background(), ethereum.CallMsg{
|
||||
To: &address,
|
||||
Data: balanceOfCallData,
|
||||
}, nil)
|
||||
if err != nil {
|
||||
return decimal.Zero, fmt.Errorf("调用 balanceOf 失败: %w", err)
|
||||
}
|
||||
|
||||
// 4. 解析 balanceOf 结果
|
||||
unpackedBalance, err := contractABI.Unpack("balanceOf", balanceResult)
|
||||
if err != nil {
|
||||
return decimal.Zero, fmt.Errorf("解析 balanceOf 结果失败: %w", err)
|
||||
}
|
||||
|
||||
balance, ok := unpackedBalance[0].(*big.Int)
|
||||
if !ok {
|
||||
return decimal.Zero, errors.New("解析 balanceOf 结果为 *big.Int 失败")
|
||||
}
|
||||
|
||||
// 8. 转换为正常单位 (使用 decimal)
|
||||
balanceDecimal := decimal.NewFromBigInt(balance, 0) // Create decimal from big.Int
|
||||
decimalFactor := decimal.NewFromInt(10).Pow(decimal.NewFromInt(int64(tokenAbi.Decimals)))
|
||||
readableBalance := balanceDecimal.Div(decimalFactor)
|
||||
|
||||
return readableBalance, nil
|
||||
}
|
||||
|
||||
// 初始化以太坊客户端连接代理
|
||||
func EthClientWithProxy(rawURL string, proxyURL string) (*ethclient.Client, error) {
|
||||
u, err := url.Parse(rawURL)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid URL: %v", err)
|
||||
}
|
||||
|
||||
if proxyURL == "" {
|
||||
return ethclient.Dial(rawURL)
|
||||
}
|
||||
|
||||
proxy, err := url.Parse(proxyURL)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid proxy URL: %v", err)
|
||||
}
|
||||
|
||||
httpClient := &http.Client{
|
||||
Transport: &http.Transport{
|
||||
Proxy: http.ProxyURL(proxy),
|
||||
},
|
||||
}
|
||||
dialOptions := []rpc.ClientOption{
|
||||
rpc.WithHTTPClient(httpClient),
|
||||
}
|
||||
rpcClient, err := rpc.DialOptions(context.Background(), u.String(), dialOptions...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to dial RPC client with options: %v", err)
|
||||
}
|
||||
|
||||
return ethclient.NewClient(rpcClient), nil
|
||||
}
|
||||
Reference in New Issue
Block a user