github.com/machinefi/w3bstream@v1.6.5-rc9.0.20240426031326-b8c7c4876e72/pkg/modules/blockchain/clients/eth.go (about) 1 package clients 2 3 import ( 4 "context" 5 "encoding/hex" 6 "math/big" 7 "strings" 8 9 "github.com/ethereum/go-ethereum" 10 "github.com/ethereum/go-ethereum/common" 11 ethtypes "github.com/ethereum/go-ethereum/core/types" 12 "github.com/ethereum/go-ethereum/crypto" 13 "github.com/ethereum/go-ethereum/ethclient" 14 "github.com/pkg/errors" 15 16 "github.com/machinefi/w3bstream/pkg/enums" 17 optypes "github.com/machinefi/w3bstream/pkg/modules/operator/pool/types" 18 ) 19 20 // EthClient is a client for ethereum compatible chain 21 type EthClient struct { 22 endpoint string 23 } 24 25 // NewEthClient creates a new EthClient 26 func NewEthClient(endpoint string) *EthClient { 27 return &EthClient{ 28 endpoint: endpoint, 29 } 30 } 31 32 // TransactionByHash returns transaction by hash 33 func (c *EthClient) TransactionByHash(ctx context.Context, hash string) (any, error) { 34 client, err := ethclient.Dial(c.endpoint) 35 if err != nil { 36 return nil, errors.Wrap(err, "dial chain address failed") 37 } 38 defer client.Close() 39 40 tx, _, err := client.TransactionByHash(ctx, common.HexToHash(hash)) 41 if err != nil { 42 return nil, errors.Wrap(err, "query transaction failed") 43 } 44 return tx, nil 45 } 46 47 // TransactionState returns transaction state 48 // TODO: refactor to reduce duplicate code with other chain clients (e.g. zksync.go) 49 func (c *EthClient) TransactionState(ctx context.Context, hash string) (enums.TransactionState, error) { 50 client, err := ethclient.Dial(c.endpoint) 51 if err != nil { 52 return enums.TRANSACTION_STATE_UNKNOWN, errors.Wrap(err, "dial chain failed") 53 } 54 defer client.Close() 55 nh := common.HexToHash(hash) 56 57 _, p, err := client.TransactionByHash(ctx, nh) 58 if err != nil { 59 if err == ethereum.NotFound { 60 return enums.TRANSACTION_STATE__FAILED, nil 61 } 62 return enums.TRANSACTION_STATE_UNKNOWN, errors.Wrap(err, "get transaction by hash failed") 63 } 64 if p { 65 return enums.TRANSACTION_STATE__PENDING, nil 66 } 67 68 receipt, err := client.TransactionReceipt(ctx, nh) 69 if err != nil { 70 if err == ethereum.NotFound { 71 return enums.TRANSACTION_STATE__IN_BLOCK, nil 72 } 73 return enums.TRANSACTION_STATE_UNKNOWN, errors.Wrap(err, "get transaction receipt failed") 74 } 75 if receipt.Status == 0 { 76 return enums.TRANSACTION_STATE__FAILED, nil 77 } 78 return enums.TRANSACTION_STATE__CONFIRMED, nil 79 } 80 81 // SendTransaction sends transaction 82 func (c *EthClient) SendTransaction(ctx context.Context, toStr, valueStr, dataStr string, op *optypes.SyncOperator) (*ethtypes.Transaction, error) { 83 cli, err := ethclient.Dial(c.endpoint) 84 if err != nil { 85 return nil, err 86 } 87 defer cli.Close() 88 89 pk := crypto.ToECDSAUnsafe(common.FromHex(op.Op.PrivateKey)) 90 sender := crypto.PubkeyToAddress(pk.PublicKey) 91 to := common.HexToAddress(toStr) 92 93 value, ok := new(big.Int).SetString(valueStr, 10) 94 if !ok { 95 return nil, errors.New("fail to read tx value") 96 } 97 data, err := hex.DecodeString(strings.TrimPrefix(dataStr, "0x")) 98 if err != nil { 99 return nil, err 100 } 101 102 gasPrice, err := cli.SuggestGasPrice(ctx) 103 if err != nil { 104 return nil, err 105 } 106 107 msg := ethereum.CallMsg{ 108 From: sender, 109 To: &to, 110 GasPrice: gasPrice, 111 Value: value, 112 Data: data, 113 } 114 gasLimit, err := cli.EstimateGas(ctx, msg) 115 if err != nil { 116 return nil, err 117 } 118 119 chainid, err := cli.ChainID(ctx) 120 if err != nil { 121 return nil, err 122 } 123 124 nonce, err := cli.PendingNonceAt(ctx, sender) 125 if err != nil { 126 return nil, err 127 } 128 129 // Create a new transaction 130 tx := ethtypes.NewTx( 131 ðtypes.LegacyTx{ 132 Nonce: nonce, 133 GasPrice: gasPrice, 134 Gas: gasLimit, 135 To: &to, 136 Value: value, 137 Data: data, 138 }) 139 140 signedTx, err := ethtypes.SignTx(tx, ethtypes.NewLondonSigner(chainid), pk) 141 if err != nil { 142 return nil, err 143 } 144 145 if err = cli.SendTransaction(ctx, signedTx); err != nil { 146 return nil, err 147 } 148 149 return signedTx, nil 150 }