package tron2 import ( "encoding/json" "errors" "fmt" "io/ioutil" "math/big" "net/http" "strconv" "strings" "time" "github.com/JFJun/trx-sign-go/grpcs" "github.com/JFJun/trx-sign-go/sign" "github.com/btcsuite/btcd/btcec" addr "github.com/fbsobreira/gotron-sdk/pkg/address" "github.com/fbsobreira/gotron-sdk/pkg/client" "github.com/fbsobreira/gotron-sdk/pkg/common" "github.com/fbsobreira/gotron-sdk/pkg/proto/core" "github.com/google/uuid" "github.com/sirupsen/logrus" "github.com/spf13/viper" "google.golang.org/grpc" ) type GenAddressInfo struct { Seed string Addr string PrvKey string } func GenAddressBySeed() (*GenAddressInfo, error) { uuid := uuid.New() seed := uuid.String() seed = strings.Replace(seed, "-", "", -1) if len(seed) != 32 { return nil, fmt.Errorf("seed len=[%d] is not equal 32", len(seed)) } priv, _ := btcec.PrivKeyFromBytes(btcec.S256(), []byte(seed)) if priv == nil { return nil, errors.New("priv is nil ptr") } a := addr.PubkeyToAddress(priv.ToECDSA().PublicKey) addrInfo := GenAddressInfo{ Seed: seed, Addr: a.String(), PrvKey: priv.D.Text(16), } return &addrInfo, nil } type txInfoItem struct { TransactionID string `json:"transaction_id"` BlockTimestamp int64 `json:"block_timestamp"` From string `json:"from"` To string `json:"to"` Type string `json:"type"` Value string `json:"value"` } var rpcClient *grpcs.Client func getClient() (*grpcs.Client, error) { if rpcClient != nil { return rpcClient, nil } grpcHost := viper.GetString("tron.grpcHost") if grpcHost == "" { grpcHost = "grpc.trongrid.io:50051" } c := new(grpcs.Client) //c.node = node c.GRPC = client.NewGrpcClient(grpcHost) err := c.GRPC.Start(grpc.WithInsecure()) if err != nil { return nil, fmt.Errorf("grpc client start error: %v", err) } // c, err := grpcs.NewClient(grpcHost) // if err != nil { // logrus.Error(err) // return nil, err // } rpcClient = c return c, err } func TransferTrx(from string, to string, amount int64, key string) (bool, string) { c, err := getClient() if err != nil { fmt.Println(err) } tx, err := c.Transfer(from, to, amount) if err != nil { fmt.Println(err) return false, "" } signTx, err := sign.SignTransaction(tx.Transaction, key) if err != nil { fmt.Println(err) return false, "" } err = c.BroadcastTransaction(signTx) if err != nil { fmt.Println(err) return false, "" } return true, common.BytesToHexString(tx.GetTxid()) } func TransferTrc20(contractAddress string, from string, to string, amount int64, key string) (bool, string, *core.Transaction) { if len(key) != 64 { logrus.Error("key not valid:", len(key), "!=64") return false, "", nil } // grpcHost := viper.GetString("tron.grpcHost") c, err := getClient() if err != nil { logrus.Error(err) return false, "", nil } amountBig := big.NewInt(amount) tx, err := c.TransferTrc20(from, to, contractAddress, amountBig, 10000000) if err != nil { logrus.Error(err) return false, "", nil } signTx, err := sign.SignTransaction(tx.Transaction, key) if err != nil { logrus.Error(err) return false, "", nil } // err = c.BroadcastTransaction(signTx) // if err != nil { // fmt.Println(err) // return false, nil // } return true, common.BytesToHexString(tx.GetTxid()), signTx } func BroadcastTransaction(signTx *core.Transaction) error { c, err := getClient() if err != nil { logrus.Error(err) return err } return c.BroadcastTransaction(signTx) } func GetBalance(addr string) (int64, error) { c, err := getClient() if err != nil { return 0, err } acc, err := c.GetTrxBalance(addr) if err != nil { return 0, err } // d, _ := json.Marshal(acc) // fmt.Println(string(d)) return acc.GetBalance(), nil } func GetTrc20BalanceByHash(hash string) (int64, error) { type HashInfoResp struct { Block int64 `json:"block"` Confirmed bool `json:"confirmed"` Revert bool `json:"revert"` ContractRet string `json:"contractRet"` ContractData struct { Amount int `json:"amount"` OwnerAddress string `json:"owner_address"` ToAddress string `json:"to_address"` } `json:"contractData"` Trc20TransferInfo []struct { IconURL string `json:"icon_url"` Symbol string `json:"symbol"` Level string `json:"level"` Decimals int `json:"decimals"` Name string `json:"name"` ToAddress string `json:"to_address"` ContractAddress string `json:"contract_address"` Type string `json:"type"` Vip bool `json:"vip"` TokenType string `json:"tokenType"` FromAddress string `json:"from_address"` AmountStr string `json:"amount_str"` } `json:"trc20TransferInfo"` } reqUrl := "https://apilist.tronscan.org/api/transaction-info?hash=" + hash + "&t=" + fmt.Sprintf("%d", time.Now().Unix()) logrus.Info("req url:", reqUrl) res, err := http.Get(reqUrl) if err != nil { logrus.Error(err) return 0, err } body, err := ioutil.ReadAll(res.Body) if err != nil { logrus.Error(err) return 0, err } defer res.Body.Close() hashResp := HashInfoResp{} err = json.Unmarshal(body, &hashResp) if err != nil { logrus.Error(err, string(body)) return 0, err } if hashResp.Block == 0 { logrus.Error("block id未生效:", string(body)) return 0, errors.New("not valid") } if hashResp.ContractRet != "SUCCESS" { return 0, errors.New("transfer failed") } if !hashResp.Confirmed { return 0, errors.New("not confirm") } if len(hashResp.Trc20TransferInfo) != 1 { return 0, errors.New("data not valid") } val, err := strconv.ParseInt(hashResp.Trc20TransferInfo[0].AmountStr, 10, 64) if err != nil { return 0, err } return val, nil } func GetTrc20Balance(addr string, contractAddress string) (int64, error) { c, err := getClient() if err != nil { return 0, err } amount, err := c.GetTrc20Balance(addr, contractAddress) if err != nil { return 0, err } val, err := strconv.ParseInt(amount.String(), 10, 64) if err != nil { return 0, err } return val, nil } func IsTransactionSucc_(txid string) (bool, error) { c, err := getClient() if err != nil { logrus.Error(err) return false, err } txInfo, err := c.GRPC.GetTransactionByID(txid) if err != nil { logrus.Error(err) return false, err } if len(txInfo.Ret) == 0 { //半小时也没有,认为失败 if txInfo.GetRawData().Timestamp-time.Now().Unix() > 30*60 { return false, nil } logrus.Error("len(txInfo.Ret) == 0") return false, errors.New("no result") } if txInfo.Ret[0].ContractRet == core.Transaction_Result_DEFAULT { return false, errors.New("wait contract result") } return txInfo.Ret[0].Ret == core.Transaction_Result_SUCESS && txInfo.Ret[0].ContractRet == core.Transaction_Result_SUCCESS, nil } func IsTransactionSucc(hash string) (bool, error) { c, err := getClient() if err != nil { logrus.Error(err) return false, err } body, err := c.GRPC.GetTransactionInfoByID(hash) if err != nil { logrus.Error(err) return false, err } switch body.Receipt.GetResult().String() { case "SUCCESS": return true, nil case "REVERT": return false, nil default: return false, errors.New("no result") } } func CheckToComtainSince(addr string, val int64, since int64) (bool, string, error) { fp := "" for { his, retfp, err := GetTxHistoryByAddr(addr, fp, 20) if err != nil { fmt.Println(err) return false, "", err } if len(his) == 0 { break } for _, info := range his { if info.BlockTimestamp < since { continue } value, err := strconv.ParseInt(info.Value, 10, 64) if err != nil { fmt.Println(err) continue } if value != val { continue } return true, info.From, nil } if retfp == "" { break } fp = retfp } return false, "", nil } func GetTxHistoryByAddr(addr string, fingerprint string, limit int) ([]txInfoItem, string, error) { apiHost := viper.GetString("tron.apiHost") contractAddr := viper.GetString("contractaddr") if contractAddr == "" { logrus.Fatal("contractaddr is empty") return nil, "", errors.New("contractaddr is empty") } reqUrl := fmt.Sprintf(`https://%s/v1/accounts/%s/transactions/trc20?limit=%d&contract_address=%s&only_confirmed=true&only_to=true&order_by=block_timestamp,desc`, apiHost, addr, limit, contractAddr) logrus.Info("reqUrl:", reqUrl) if fingerprint != "" { reqUrl = reqUrl + "&fingerprint=" + fingerprint } resp, err := http.Get(reqUrl) if err != nil { logrus.Error(err, " ", reqUrl) return nil, "", err } type Meta struct { Fingerprint string `json:"fingerprint"` } type DataInfo struct { Data []txInfoItem `json:"data"` Meta Meta `json:"meta"` } dataInfo := DataInfo{} body, err := ioutil.ReadAll(resp.Body) if err != nil { logrus.Error(err, " ", reqUrl) return nil, "", err } defer resp.Body.Close() err = json.Unmarshal(body, &dataInfo) if err != nil { logrus.Error(err, " ", reqUrl) return nil, "", err } if dataInfo.Data == nil { return nil, "", errors.New(string(body)) } resultList := make([]txInfoItem, 0) for _, item := range dataInfo.Data { if item.Type != "Transfer" { continue } resultList = append(resultList, item) } return resultList, dataInfo.Meta.Fingerprint, nil }