first commit of restructured project
This commit is contained in:
118
trahn-trade-backend/internal/ethereum/client.go
Normal file
118
trahn-trade-backend/internal/ethereum/client.go
Normal file
@@ -0,0 +1,118 @@
|
||||
package ethereum
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/ecdsa"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"strings"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/ethclient"
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
rpc *ethclient.Client
|
||||
privateKey *ecdsa.PrivateKey
|
||||
wallet common.Address
|
||||
chainID *big.Int
|
||||
gasLimit uint64
|
||||
gasMul float64
|
||||
}
|
||||
|
||||
func NewClient(rpcURL, privateKeyHex string, chainID int64, gasLimit int, gasMultiplier float64) (*Client, error) {
|
||||
rpc, err := ethclient.Dial(rpcURL)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("dial RPC: %w", err)
|
||||
}
|
||||
|
||||
pkHex := strings.TrimPrefix(privateKeyHex, "0x")
|
||||
pk, err := crypto.HexToECDSA(pkHex)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parse private key: %w", err)
|
||||
}
|
||||
|
||||
addr := crypto.PubkeyToAddress(pk.PublicKey)
|
||||
|
||||
return &Client{
|
||||
rpc: rpc,
|
||||
privateKey: pk,
|
||||
wallet: addr,
|
||||
chainID: big.NewInt(chainID),
|
||||
gasLimit: uint64(gasLimit),
|
||||
gasMul: gasMultiplier,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *Client) WalletAddress() common.Address { return c.wallet }
|
||||
func (c *Client) GasLimit() uint64 { return c.gasLimit }
|
||||
func (c *Client) Close() { c.rpc.Close() }
|
||||
|
||||
func (c *Client) ETHBalance(ctx context.Context) (*big.Int, error) {
|
||||
return c.rpc.BalanceAt(ctx, c.wallet, nil)
|
||||
}
|
||||
|
||||
func (c *Client) GasPrice(ctx context.Context) (*big.Int, error) {
|
||||
price, err := c.rpc.SuggestGasPrice(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Apply multiplier
|
||||
mul := new(big.Float).SetFloat64(c.gasMul)
|
||||
adjusted := new(big.Float).Mul(new(big.Float).SetInt(price), mul)
|
||||
result, _ := adjusted.Int(nil)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (c *Client) Nonce(ctx context.Context) (uint64, error) {
|
||||
return c.rpc.PendingNonceAt(ctx, c.wallet)
|
||||
}
|
||||
|
||||
// SignAndSend signs a legacy transaction and broadcasts it, returning the tx hash.
|
||||
func (c *Client) SignAndSend(ctx context.Context, to common.Address, value *big.Int, data []byte) (string, error) {
|
||||
nonce, err := c.Nonce(ctx)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("get nonce: %w", err)
|
||||
}
|
||||
gasPrice, err := c.GasPrice(ctx)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("get gas price: %w", err)
|
||||
}
|
||||
|
||||
tx := types.NewTx(&types.LegacyTx{
|
||||
Nonce: nonce,
|
||||
To: &to,
|
||||
Value: value,
|
||||
Gas: c.gasLimit,
|
||||
GasPrice: gasPrice,
|
||||
Data: data,
|
||||
})
|
||||
|
||||
signer := types.NewEIP155Signer(c.chainID)
|
||||
signed, err := types.SignTx(tx, signer, c.privateKey)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("sign tx: %w", err)
|
||||
}
|
||||
|
||||
if err := c.rpc.SendTransaction(ctx, signed); err != nil {
|
||||
return "", fmt.Errorf("send tx: %w", err)
|
||||
}
|
||||
|
||||
return signed.Hash().Hex(), nil
|
||||
}
|
||||
|
||||
// CallContract performs a read-only eth_call and returns the raw result.
|
||||
func (c *Client) CallContract(ctx context.Context, to common.Address, data []byte) ([]byte, error) {
|
||||
msg := map[string]interface{}{
|
||||
"to": to.Hex(),
|
||||
"data": fmt.Sprintf("0x%x", data),
|
||||
}
|
||||
var result string
|
||||
err := c.rpc.Client().CallContext(ctx, &result, "eth_call", msg, "latest")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return common.FromHex(result), nil
|
||||
}
|
||||
Reference in New Issue
Block a user