Files
eth_transfer_go/utils/ethbalanceofhelper/baalanceof_helper.go
2025-05-13 15:44:44 +08:00

114 lines
3.3 KiB
Go

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
}