mirror of
https://github.com/status-im/status-go.git
synced 2025-01-09 14:16:21 +00:00
d20c91cb99
1. Fixing a crash on `ValidateAndBuildTransaction`: tx.Nonce() is called on a null tx whenever it fails to build the transaction 2. Fixing gas extimations. The estimations are always done on mainnet and the requested chainId is ignored in the estimation
562 lines
17 KiB
Go
562 lines
17 KiB
Go
package transactions
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"math/big"
|
|
"time"
|
|
|
|
ethereum "github.com/ethereum/go-ethereum"
|
|
"github.com/ethereum/go-ethereum/common"
|
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
|
gethtypes "github.com/ethereum/go-ethereum/core/types"
|
|
"github.com/ethereum/go-ethereum/log"
|
|
|
|
"github.com/status-im/status-go/account"
|
|
"github.com/status-im/status-go/eth-node/crypto"
|
|
"github.com/status-im/status-go/eth-node/types"
|
|
"github.com/status-im/status-go/params"
|
|
"github.com/status-im/status-go/rpc"
|
|
"github.com/status-im/status-go/services/wallet/bigint"
|
|
wallet_common "github.com/status-im/status-go/services/wallet/common"
|
|
)
|
|
|
|
const (
|
|
// sendTxTimeout defines how many seconds to wait before returning result in sentTransaction().
|
|
sendTxTimeout = 300 * time.Second
|
|
|
|
defaultGas = 90000
|
|
|
|
ValidSignatureSize = 65
|
|
)
|
|
|
|
// ErrInvalidSignatureSize is returned if a signature is not 65 bytes to avoid panic from go-ethereum
|
|
var ErrInvalidSignatureSize = errors.New("signature size must be 65")
|
|
|
|
type ErrBadNonce struct {
|
|
nonce uint64
|
|
expectedNonce uint64
|
|
}
|
|
|
|
func (e *ErrBadNonce) Error() string {
|
|
return fmt.Sprintf("bad nonce. expected %d, got %d", e.expectedNonce, e.nonce)
|
|
}
|
|
|
|
// Transactor is an interface that defines the methods for validating and sending transactions.
|
|
type TransactorIface interface {
|
|
NextNonce(rpcClient rpc.ClientInterface, chainID uint64, from types.Address) (uint64, error)
|
|
EstimateGas(network *params.Network, from common.Address, to common.Address, value *big.Int, input []byte) (uint64, error)
|
|
SendTransaction(sendArgs SendTxArgs, verifiedAccount *account.SelectedExtKey, lastUsedNonce int64) (hash types.Hash, nonce uint64, err error)
|
|
SendTransactionWithChainID(chainID uint64, sendArgs SendTxArgs, lastUsedNonce int64, verifiedAccount *account.SelectedExtKey) (hash types.Hash, nonce uint64, err error)
|
|
ValidateAndBuildTransaction(chainID uint64, sendArgs SendTxArgs, lastUsedNonce int64) (tx *gethtypes.Transaction, nonce uint64, err error)
|
|
AddSignatureToTransaction(chainID uint64, tx *gethtypes.Transaction, sig []byte) (*gethtypes.Transaction, error)
|
|
SendRawTransaction(chainID uint64, rawTx string) error
|
|
BuildTransactionWithSignature(chainID uint64, args SendTxArgs, sig []byte) (*gethtypes.Transaction, error)
|
|
SendTransactionWithSignature(from common.Address, symbol string, multiTransactionID wallet_common.MultiTransactionIDType, tx *gethtypes.Transaction) (hash types.Hash, err error)
|
|
StoreAndTrackPendingTx(from common.Address, symbol string, chainID uint64, multiTransactionID wallet_common.MultiTransactionIDType, tx *gethtypes.Transaction) error
|
|
}
|
|
|
|
// Transactor validates, signs transactions.
|
|
// It uses upstream to propagate transactions to the Ethereum network.
|
|
type Transactor struct {
|
|
rpcWrapper *rpcWrapper
|
|
pendingTracker *PendingTxTracker
|
|
sendTxTimeout time.Duration
|
|
rpcCallTimeout time.Duration
|
|
networkID uint64
|
|
log log.Logger
|
|
}
|
|
|
|
// NewTransactor returns a new Manager.
|
|
func NewTransactor() *Transactor {
|
|
return &Transactor{
|
|
sendTxTimeout: sendTxTimeout,
|
|
log: log.New("package", "status-go/transactions.Manager"),
|
|
}
|
|
}
|
|
|
|
// SetPendingTracker sets a pending tracker.
|
|
func (t *Transactor) SetPendingTracker(tracker *PendingTxTracker) {
|
|
t.pendingTracker = tracker
|
|
}
|
|
|
|
// SetNetworkID selects a correct network.
|
|
func (t *Transactor) SetNetworkID(networkID uint64) {
|
|
t.networkID = networkID
|
|
}
|
|
|
|
func (t *Transactor) NetworkID() uint64 {
|
|
return t.networkID
|
|
}
|
|
|
|
// SetRPC sets RPC params, a client and a timeout
|
|
func (t *Transactor) SetRPC(rpcClient *rpc.Client, timeout time.Duration) {
|
|
t.rpcWrapper = newRPCWrapper(rpcClient, rpcClient.UpstreamChainID)
|
|
t.rpcCallTimeout = timeout
|
|
}
|
|
|
|
func (t *Transactor) NextNonce(rpcClient rpc.ClientInterface, chainID uint64, from types.Address) (uint64, error) {
|
|
wrapper := newRPCWrapper(rpcClient, chainID)
|
|
ctx := context.Background()
|
|
nonce, err := wrapper.PendingNonceAt(ctx, common.Address(from))
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
// We need to take into consideration all pending transactions in case of Optimism, cause the network returns always
|
|
// the nonce of last executed tx + 1 for the next nonce value.
|
|
if chainID == wallet_common.OptimismMainnet ||
|
|
chainID == wallet_common.OptimismSepolia ||
|
|
chainID == wallet_common.OptimismGoerli {
|
|
if t.pendingTracker != nil {
|
|
countOfPendingTXs, err := t.pendingTracker.CountPendingTxsFromNonce(wallet_common.ChainID(chainID), common.Address(from), nonce)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return nonce + countOfPendingTXs, nil
|
|
}
|
|
}
|
|
|
|
return nonce, err
|
|
}
|
|
|
|
func (t *Transactor) EstimateGas(network *params.Network, from common.Address, to common.Address, value *big.Int, input []byte) (uint64, error) {
|
|
rpcWrapper := newRPCWrapper(t.rpcWrapper.RPCClient, network.ChainID)
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), t.rpcCallTimeout)
|
|
defer cancel()
|
|
|
|
msg := ethereum.CallMsg{
|
|
From: from,
|
|
To: &to,
|
|
Value: value,
|
|
Data: input,
|
|
}
|
|
|
|
return rpcWrapper.EstimateGas(ctx, msg)
|
|
}
|
|
|
|
// SendTransaction is an implementation of eth_sendTransaction. It queues the tx to the sign queue.
|
|
func (t *Transactor) SendTransaction(sendArgs SendTxArgs, verifiedAccount *account.SelectedExtKey, lastUsedNonce int64) (hash types.Hash, nonce uint64, err error) {
|
|
hash, nonce, err = t.validateAndPropagate(t.rpcWrapper, verifiedAccount, sendArgs, lastUsedNonce)
|
|
return
|
|
}
|
|
|
|
func (t *Transactor) SendTransactionWithChainID(chainID uint64, sendArgs SendTxArgs, lastUsedNonce int64, verifiedAccount *account.SelectedExtKey) (hash types.Hash, nonce uint64, err error) {
|
|
wrapper := newRPCWrapper(t.rpcWrapper.RPCClient, chainID)
|
|
hash, nonce, err = t.validateAndPropagate(wrapper, verifiedAccount, sendArgs, lastUsedNonce)
|
|
return
|
|
}
|
|
|
|
func (t *Transactor) ValidateAndBuildTransaction(chainID uint64, sendArgs SendTxArgs, lastUsedNonce int64) (tx *gethtypes.Transaction, nonce uint64, err error) {
|
|
wrapper := newRPCWrapper(t.rpcWrapper.RPCClient, chainID)
|
|
tx, err = t.validateAndBuildTransaction(wrapper, sendArgs, lastUsedNonce)
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
|
|
return tx, tx.Nonce(), err
|
|
}
|
|
|
|
func (t *Transactor) AddSignatureToTransaction(chainID uint64, tx *gethtypes.Transaction, sig []byte) (*gethtypes.Transaction, error) {
|
|
if len(sig) != ValidSignatureSize {
|
|
return nil, ErrInvalidSignatureSize
|
|
}
|
|
|
|
rpcWrapper := newRPCWrapper(t.rpcWrapper.RPCClient, chainID)
|
|
chID := big.NewInt(int64(rpcWrapper.chainID))
|
|
|
|
signer := gethtypes.NewLondonSigner(chID)
|
|
txWithSignature, err := tx.WithSignature(signer, sig)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return txWithSignature, nil
|
|
}
|
|
|
|
func (t *Transactor) SendRawTransaction(chainID uint64, rawTx string) error {
|
|
rpcWrapper := newRPCWrapper(t.rpcWrapper.RPCClient, chainID)
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), t.rpcCallTimeout)
|
|
defer cancel()
|
|
|
|
return rpcWrapper.SendRawTransaction(ctx, rawTx)
|
|
}
|
|
|
|
func createPendingTransaction(from common.Address, symbol string, chainID uint64, multiTransactionID wallet_common.MultiTransactionIDType, tx *gethtypes.Transaction) (pTx *PendingTransaction) {
|
|
|
|
pTx = &PendingTransaction{
|
|
Hash: tx.Hash(),
|
|
Timestamp: uint64(time.Now().Unix()),
|
|
Value: bigint.BigInt{Int: tx.Value()},
|
|
From: from,
|
|
To: *tx.To(),
|
|
Nonce: tx.Nonce(),
|
|
Data: string(tx.Data()),
|
|
Type: WalletTransfer,
|
|
ChainID: wallet_common.ChainID(chainID),
|
|
MultiTransactionID: multiTransactionID,
|
|
Symbol: symbol,
|
|
AutoDelete: new(bool),
|
|
}
|
|
// Transaction downloader will delete pending transaction as soon as it is confirmed
|
|
*pTx.AutoDelete = false
|
|
return
|
|
}
|
|
|
|
func (t *Transactor) StoreAndTrackPendingTx(from common.Address, symbol string, chainID uint64, multiTransactionID wallet_common.MultiTransactionIDType, tx *gethtypes.Transaction) error {
|
|
if t.pendingTracker == nil {
|
|
return nil
|
|
}
|
|
|
|
pTx := createPendingTransaction(from, symbol, chainID, multiTransactionID, tx)
|
|
return t.pendingTracker.StoreAndTrackPendingTx(pTx)
|
|
}
|
|
|
|
func (t *Transactor) sendTransaction(rpcWrapper *rpcWrapper, from common.Address, symbol string,
|
|
multiTransactionID wallet_common.MultiTransactionIDType, tx *gethtypes.Transaction) (hash types.Hash, err error) {
|
|
ctx, cancel := context.WithTimeout(context.Background(), t.rpcCallTimeout)
|
|
defer cancel()
|
|
|
|
if err := rpcWrapper.SendTransaction(ctx, tx); err != nil {
|
|
return hash, err
|
|
}
|
|
|
|
err = t.StoreAndTrackPendingTx(from, symbol, rpcWrapper.chainID, multiTransactionID, tx)
|
|
if err != nil {
|
|
return hash, err
|
|
}
|
|
|
|
return types.Hash(tx.Hash()), nil
|
|
}
|
|
|
|
func (t *Transactor) SendTransactionWithSignature(from common.Address, symbol string,
|
|
multiTransactionID wallet_common.MultiTransactionIDType, tx *gethtypes.Transaction) (hash types.Hash, err error) {
|
|
rpcWrapper := newRPCWrapper(t.rpcWrapper.RPCClient, tx.ChainId().Uint64())
|
|
|
|
return t.sendTransaction(rpcWrapper, from, symbol, multiTransactionID, tx)
|
|
}
|
|
|
|
// BuildTransactionAndSendWithSignature receive a transaction and a signature, serialize them together
|
|
// It's different from eth_sendRawTransaction because it receives a signature and not a serialized transaction with signature.
|
|
// Since the transactions is already signed, we assume it was validated and used the right nonce.
|
|
func (t *Transactor) BuildTransactionWithSignature(chainID uint64, args SendTxArgs, sig []byte) (*gethtypes.Transaction, error) {
|
|
if !args.Valid() {
|
|
return nil, ErrInvalidSendTxArgs
|
|
}
|
|
|
|
if len(sig) != ValidSignatureSize {
|
|
return nil, ErrInvalidSignatureSize
|
|
}
|
|
|
|
tx := t.buildTransaction(args)
|
|
expectedNonce, err := t.NextNonce(t.rpcWrapper.RPCClient, chainID, args.From)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if tx.Nonce() != expectedNonce {
|
|
return nil, &ErrBadNonce{tx.Nonce(), expectedNonce}
|
|
}
|
|
|
|
txWithSignature, err := t.AddSignatureToTransaction(chainID, tx, sig)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return txWithSignature, nil
|
|
}
|
|
|
|
func (t *Transactor) HashTransaction(args SendTxArgs) (validatedArgs SendTxArgs, hash types.Hash, err error) {
|
|
if !args.Valid() {
|
|
return validatedArgs, hash, ErrInvalidSendTxArgs
|
|
}
|
|
|
|
validatedArgs = args
|
|
|
|
nonce, err := t.NextNonce(t.rpcWrapper.RPCClient, t.rpcWrapper.chainID, args.From)
|
|
if err != nil {
|
|
return validatedArgs, hash, err
|
|
}
|
|
|
|
gasPrice := (*big.Int)(args.GasPrice)
|
|
gasFeeCap := (*big.Int)(args.MaxFeePerGas)
|
|
gasTipCap := (*big.Int)(args.MaxPriorityFeePerGas)
|
|
if args.GasPrice == nil && !args.IsDynamicFeeTx() {
|
|
ctx, cancel := context.WithTimeout(context.Background(), t.rpcCallTimeout)
|
|
defer cancel()
|
|
gasPrice, err = t.rpcWrapper.SuggestGasPrice(ctx)
|
|
if err != nil {
|
|
return validatedArgs, hash, err
|
|
}
|
|
}
|
|
|
|
chainID := big.NewInt(int64(t.networkID))
|
|
value := (*big.Int)(args.Value)
|
|
|
|
var gas uint64
|
|
if args.Gas == nil {
|
|
ctx, cancel := context.WithTimeout(context.Background(), t.rpcCallTimeout)
|
|
defer cancel()
|
|
|
|
var (
|
|
gethTo common.Address
|
|
gethToPtr *common.Address
|
|
)
|
|
if args.To != nil {
|
|
gethTo = common.Address(*args.To)
|
|
gethToPtr = &gethTo
|
|
}
|
|
if args.IsDynamicFeeTx() {
|
|
gas, err = t.rpcWrapper.EstimateGas(ctx, ethereum.CallMsg{
|
|
From: common.Address(args.From),
|
|
To: gethToPtr,
|
|
GasFeeCap: gasFeeCap,
|
|
GasTipCap: gasTipCap,
|
|
Value: value,
|
|
Data: args.GetInput(),
|
|
})
|
|
} else {
|
|
gas, err = t.rpcWrapper.EstimateGas(ctx, ethereum.CallMsg{
|
|
From: common.Address(args.From),
|
|
To: gethToPtr,
|
|
GasPrice: gasPrice,
|
|
Value: value,
|
|
Data: args.GetInput(),
|
|
})
|
|
}
|
|
if err != nil {
|
|
return validatedArgs, hash, err
|
|
}
|
|
} else {
|
|
gas = uint64(*args.Gas)
|
|
}
|
|
|
|
newNonce := hexutil.Uint64(nonce)
|
|
newGas := hexutil.Uint64(gas)
|
|
validatedArgs.Nonce = &newNonce
|
|
if !args.IsDynamicFeeTx() {
|
|
validatedArgs.GasPrice = (*hexutil.Big)(gasPrice)
|
|
} else {
|
|
validatedArgs.MaxPriorityFeePerGas = (*hexutil.Big)(gasTipCap)
|
|
validatedArgs.MaxPriorityFeePerGas = (*hexutil.Big)(gasFeeCap)
|
|
}
|
|
validatedArgs.Gas = &newGas
|
|
|
|
tx := t.buildTransaction(validatedArgs)
|
|
hash = types.Hash(gethtypes.NewLondonSigner(chainID).Hash(tx))
|
|
|
|
return validatedArgs, hash, nil
|
|
}
|
|
|
|
// make sure that only account which created the tx can complete it
|
|
func (t *Transactor) validateAccount(args SendTxArgs, selectedAccount *account.SelectedExtKey) error {
|
|
if selectedAccount == nil {
|
|
return account.ErrNoAccountSelected
|
|
}
|
|
|
|
if !bytes.Equal(args.From.Bytes(), selectedAccount.Address.Bytes()) {
|
|
return ErrInvalidTxSender
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (t *Transactor) validateAndBuildTransaction(rpcWrapper *rpcWrapper, args SendTxArgs, lastUsedNonce int64) (tx *gethtypes.Transaction, err error) {
|
|
if !args.Valid() {
|
|
return tx, ErrInvalidSendTxArgs
|
|
}
|
|
|
|
var nonce uint64
|
|
if args.Nonce != nil {
|
|
nonce = uint64(*args.Nonce)
|
|
} else {
|
|
// some chains, like arbitrum doesn't count pending txs in the nonce, so we need to calculate it manually
|
|
if lastUsedNonce < 0 {
|
|
nonce, err = t.NextNonce(rpcWrapper.RPCClient, rpcWrapper.chainID, args.From)
|
|
if err != nil {
|
|
return tx, err
|
|
}
|
|
} else {
|
|
nonce = uint64(lastUsedNonce) + 1
|
|
}
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), t.rpcCallTimeout)
|
|
defer cancel()
|
|
|
|
gasPrice := (*big.Int)(args.GasPrice)
|
|
// GasPrice should be estimated only for LegacyTx
|
|
if !args.IsDynamicFeeTx() && args.GasPrice == nil {
|
|
gasPrice, err = rpcWrapper.SuggestGasPrice(ctx)
|
|
if err != nil {
|
|
return tx, err
|
|
}
|
|
}
|
|
|
|
value := (*big.Int)(args.Value)
|
|
var gas uint64
|
|
if args.Gas != nil {
|
|
gas = uint64(*args.Gas)
|
|
} else {
|
|
ctx, cancel = context.WithTimeout(context.Background(), t.rpcCallTimeout)
|
|
defer cancel()
|
|
|
|
var (
|
|
gethTo common.Address
|
|
gethToPtr *common.Address
|
|
)
|
|
if args.To != nil {
|
|
gethTo = common.Address(*args.To)
|
|
gethToPtr = &gethTo
|
|
}
|
|
if args.IsDynamicFeeTx() {
|
|
gasFeeCap := (*big.Int)(args.MaxFeePerGas)
|
|
gasTipCap := (*big.Int)(args.MaxPriorityFeePerGas)
|
|
gas, err = rpcWrapper.EstimateGas(ctx, ethereum.CallMsg{
|
|
From: common.Address(args.From),
|
|
To: gethToPtr,
|
|
GasFeeCap: gasFeeCap,
|
|
GasTipCap: gasTipCap,
|
|
Value: value,
|
|
Data: args.GetInput(),
|
|
})
|
|
} else {
|
|
gas, err = rpcWrapper.EstimateGas(ctx, ethereum.CallMsg{
|
|
From: common.Address(args.From),
|
|
To: gethToPtr,
|
|
GasPrice: gasPrice,
|
|
Value: value,
|
|
Data: args.GetInput(),
|
|
})
|
|
}
|
|
if err != nil {
|
|
return tx, err
|
|
}
|
|
}
|
|
|
|
tx = t.buildTransactionWithOverrides(nonce, value, gas, gasPrice, args)
|
|
return tx, nil
|
|
}
|
|
|
|
func (t *Transactor) validateAndPropagate(rpcWrapper *rpcWrapper, selectedAccount *account.SelectedExtKey, args SendTxArgs, lastUsedNonce int64) (hash types.Hash, nonce uint64, err error) {
|
|
if err = t.validateAccount(args, selectedAccount); err != nil {
|
|
return hash, nonce, err
|
|
}
|
|
|
|
tx, err := t.validateAndBuildTransaction(rpcWrapper, args, lastUsedNonce)
|
|
if err != nil {
|
|
return hash, nonce, err
|
|
}
|
|
|
|
chainID := big.NewInt(int64(rpcWrapper.chainID))
|
|
signedTx, err := gethtypes.SignTx(tx, gethtypes.NewLondonSigner(chainID), selectedAccount.AccountKey.PrivateKey)
|
|
if err != nil {
|
|
return hash, nonce, err
|
|
}
|
|
|
|
hash, err = t.sendTransaction(rpcWrapper, common.Address(args.From), args.Symbol, args.MultiTransactionID, signedTx)
|
|
return hash, tx.Nonce(), err
|
|
}
|
|
|
|
func (t *Transactor) buildTransaction(args SendTxArgs) *gethtypes.Transaction {
|
|
var (
|
|
nonce uint64
|
|
value *big.Int
|
|
gas uint64
|
|
gasPrice *big.Int
|
|
)
|
|
if args.Nonce != nil {
|
|
nonce = uint64(*args.Nonce)
|
|
}
|
|
if args.Value != nil {
|
|
value = (*big.Int)(args.Value)
|
|
}
|
|
if args.Gas != nil {
|
|
gas = uint64(*args.Gas)
|
|
}
|
|
if args.GasPrice != nil {
|
|
gasPrice = (*big.Int)(args.GasPrice)
|
|
}
|
|
|
|
return t.buildTransactionWithOverrides(nonce, value, gas, gasPrice, args)
|
|
}
|
|
|
|
func (t *Transactor) buildTransactionWithOverrides(nonce uint64, value *big.Int, gas uint64, gasPrice *big.Int, args SendTxArgs) *gethtypes.Transaction {
|
|
var tx *gethtypes.Transaction
|
|
|
|
if args.To != nil {
|
|
to := common.Address(*args.To)
|
|
var txData gethtypes.TxData
|
|
|
|
if args.IsDynamicFeeTx() {
|
|
gasTipCap := (*big.Int)(args.MaxPriorityFeePerGas)
|
|
gasFeeCap := (*big.Int)(args.MaxFeePerGas)
|
|
|
|
txData = &gethtypes.DynamicFeeTx{
|
|
Nonce: nonce,
|
|
Gas: gas,
|
|
GasTipCap: gasTipCap,
|
|
GasFeeCap: gasFeeCap,
|
|
To: &to,
|
|
Value: value,
|
|
Data: args.GetInput(),
|
|
}
|
|
} else {
|
|
txData = &gethtypes.LegacyTx{
|
|
Nonce: nonce,
|
|
GasPrice: gasPrice,
|
|
Gas: gas,
|
|
To: &to,
|
|
Value: value,
|
|
Data: args.GetInput(),
|
|
}
|
|
}
|
|
tx = gethtypes.NewTx(txData)
|
|
t.logNewTx(args, gas, gasPrice, value)
|
|
} else {
|
|
if args.IsDynamicFeeTx() {
|
|
gasTipCap := (*big.Int)(args.MaxPriorityFeePerGas)
|
|
gasFeeCap := (*big.Int)(args.MaxFeePerGas)
|
|
|
|
txData := &gethtypes.DynamicFeeTx{
|
|
Nonce: nonce,
|
|
Value: value,
|
|
Gas: gas,
|
|
GasTipCap: gasTipCap,
|
|
GasFeeCap: gasFeeCap,
|
|
Data: args.GetInput(),
|
|
}
|
|
tx = gethtypes.NewTx(txData)
|
|
} else {
|
|
tx = gethtypes.NewContractCreation(nonce, value, gas, gasPrice, args.GetInput())
|
|
}
|
|
t.logNewContract(args, gas, gasPrice, value, nonce)
|
|
}
|
|
|
|
return tx
|
|
}
|
|
|
|
func (t *Transactor) logNewTx(args SendTxArgs, gas uint64, gasPrice *big.Int, value *big.Int) {
|
|
t.log.Info("New transaction",
|
|
"From", args.From,
|
|
"To", *args.To,
|
|
"Gas", gas,
|
|
"GasPrice", gasPrice,
|
|
"Value", value,
|
|
)
|
|
}
|
|
|
|
func (t *Transactor) logNewContract(args SendTxArgs, gas uint64, gasPrice *big.Int, value *big.Int, nonce uint64) {
|
|
t.log.Info("New contract",
|
|
"From", args.From,
|
|
"Gas", gas,
|
|
"GasPrice", gasPrice,
|
|
"Value", value,
|
|
"Contract address", crypto.CreateAddress(args.From, nonce),
|
|
)
|
|
}
|