Send dynamic fee tx parameters
This commit is contained in:
parent
81b58b39ec
commit
03700c934c
|
@ -8,6 +8,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
gethtypes "github.com/ethereum/go-ethereum/core/types"
|
gethtypes "github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
|
|
||||||
"github.com/status-im/status-go/eth-node/types"
|
"github.com/status-im/status-go/eth-node/types"
|
||||||
|
@ -60,10 +61,13 @@ func (w *rpcWrapper) EstimateGas(ctx context.Context, msg ethereum.CallMsg) (uin
|
||||||
// If the transaction was a contract creation use the TransactionReceipt method to get the
|
// If the transaction was a contract creation use the TransactionReceipt method to get the
|
||||||
// contract address after the transaction has been mined.
|
// contract address after the transaction has been mined.
|
||||||
func (w *rpcWrapper) SendTransaction(ctx context.Context, tx *gethtypes.Transaction) error {
|
func (w *rpcWrapper) SendTransaction(ctx context.Context, tx *gethtypes.Transaction) error {
|
||||||
|
log.Info("encoding transaction to bytes", "tx", tx)
|
||||||
data, err := rlp.EncodeToBytes(tx)
|
data, err := rlp.EncodeToBytes(tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Error("failed to encode transaction to bytes", "tx", tx, "err", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
log.Info("encoded transaction to bytes", "tx", tx)
|
||||||
return w.rpcClient.CallContext(ctx, nil, "eth_sendRawTransaction", types.EncodeHex(data))
|
return w.rpcClient.CallContext(ctx, nil, "eth_sendRawTransaction", types.EncodeHex(data))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -264,7 +264,7 @@ func (t *Transactor) validateAndPropagate(selectedAccount *account.SelectedExtKe
|
||||||
nonce = uint64(*args.Nonce)
|
nonce = uint64(*args.Nonce)
|
||||||
}
|
}
|
||||||
gasPrice := (*big.Int)(args.GasPrice)
|
gasPrice := (*big.Int)(args.GasPrice)
|
||||||
if args.GasPrice == nil {
|
if !args.IsDynamicFeeTx() && args.GasPrice == nil {
|
||||||
ctx, cancel = context.WithTimeout(context.Background(), t.rpcCallTimeout)
|
ctx, cancel = context.WithTimeout(context.Background(), t.rpcCallTimeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
gasPrice, err = t.gasCalculator.SuggestGasPrice(ctx)
|
gasPrice, err = t.gasCalculator.SuggestGasPrice(ctx)
|
||||||
|
@ -277,7 +277,9 @@ func (t *Transactor) validateAndPropagate(selectedAccount *account.SelectedExtKe
|
||||||
value := (*big.Int)(args.Value)
|
value := (*big.Int)(args.Value)
|
||||||
|
|
||||||
var gas uint64
|
var gas uint64
|
||||||
if args.Gas == nil {
|
if args.Gas != nil {
|
||||||
|
gas = uint64(*args.Gas)
|
||||||
|
} else if args.Gas == nil && !args.IsDynamicFeeTx() {
|
||||||
ctx, cancel = context.WithTimeout(context.Background(), t.rpcCallTimeout)
|
ctx, cancel = context.WithTimeout(context.Background(), t.rpcCallTimeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
|
@ -303,20 +305,20 @@ func (t *Transactor) validateAndPropagate(selectedAccount *account.SelectedExtKe
|
||||||
t.log.Info("default gas will be used because estimated is lower", "estimated", gas, "default", defaultGas)
|
t.log.Info("default gas will be used because estimated is lower", "estimated", gas, "default", defaultGas)
|
||||||
gas = defaultGas
|
gas = defaultGas
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
gas = uint64(*args.Gas)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tx := t.buildTransactionWithOverrides(nonce, value, gas, gasPrice, args)
|
tx := t.buildTransactionWithOverrides(nonce, value, gas, gasPrice, args)
|
||||||
|
|
||||||
signedTx, err := gethtypes.SignTx(tx, gethtypes.NewLondonSigner(chainID), selectedAccount.AccountKey.PrivateKey)
|
signedTx, err := gethtypes.SignTx(tx, gethtypes.NewLondonSigner(chainID), selectedAccount.AccountKey.PrivateKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
t.log.Info("ERROR SIGNIN TRANSACTION", "hash", hash)
|
||||||
return hash, err
|
return hash, err
|
||||||
}
|
}
|
||||||
ctx, cancel = context.WithTimeout(context.Background(), t.rpcCallTimeout)
|
ctx, cancel = context.WithTimeout(context.Background(), t.rpcCallTimeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
if err := t.sender.SendTransaction(ctx, signedTx); err != nil {
|
if err := t.sender.SendTransaction(ctx, signedTx); err != nil {
|
||||||
|
t.log.Info("ERROR SENDING TRANSACTION", "hash", hash)
|
||||||
return hash, err
|
return hash, err
|
||||||
}
|
}
|
||||||
return types.Hash(signedTx.Hash()), nil
|
return types.Hash(signedTx.Hash()), nil
|
||||||
|
@ -335,7 +337,37 @@ func (t *Transactor) buildTransactionWithOverrides(nonce uint64, value *big.Int,
|
||||||
var tx *gethtypes.Transaction
|
var tx *gethtypes.Transaction
|
||||||
|
|
||||||
if args.To != nil {
|
if args.To != nil {
|
||||||
tx = gethtypes.NewTransaction(nonce, common.Address(*args.To), value, gas, gasPrice, args.GetInput())
|
to := common.Address(*args.To)
|
||||||
|
var txData gethtypes.TxData
|
||||||
|
|
||||||
|
t.log.Info("checking fee for dynamic", "args", args)
|
||||||
|
|
||||||
|
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(),
|
||||||
|
}
|
||||||
|
t.log.Info("is dynamic", "txdata", txData)
|
||||||
|
} else {
|
||||||
|
txData = &gethtypes.LegacyTx{
|
||||||
|
Nonce: nonce,
|
||||||
|
GasPrice: gasPrice,
|
||||||
|
Gas: gas,
|
||||||
|
To: &to,
|
||||||
|
Value: value,
|
||||||
|
Data: args.GetInput(),
|
||||||
|
}
|
||||||
|
t.log.Info("is not dynamic", "txdata", txData)
|
||||||
|
}
|
||||||
|
tx = gethtypes.NewTx(txData)
|
||||||
t.logNewTx(args, gas, gasPrice, value)
|
t.logNewTx(args, gas, gasPrice, value)
|
||||||
} else {
|
} else {
|
||||||
tx = gethtypes.NewContractCreation(nonce, value, gas, gasPrice, args.GetInput())
|
tx = gethtypes.NewContractCreation(nonce, value, gas, gasPrice, args.GetInput())
|
||||||
|
|
|
@ -80,17 +80,19 @@ func (s *TransactorSuite) setupTransactionPoolAPI(args SendTxArgs, returnNonce,
|
||||||
var usedGas hexutil.Uint64
|
var usedGas hexutil.Uint64
|
||||||
var usedGasPrice *big.Int
|
var usedGasPrice *big.Int
|
||||||
s.txServiceMock.EXPECT().GetTransactionCount(gomock.Any(), gomock.Eq(common.Address(account.Address)), gethrpc.PendingBlockNumber).Return(&returnNonce, nil)
|
s.txServiceMock.EXPECT().GetTransactionCount(gomock.Any(), gomock.Eq(common.Address(account.Address)), gethrpc.PendingBlockNumber).Return(&returnNonce, nil)
|
||||||
if args.GasPrice == nil {
|
if !args.IsDynamicFeeTx() {
|
||||||
usedGasPrice = (*big.Int)(testGasPrice)
|
if args.GasPrice == nil {
|
||||||
s.txServiceMock.EXPECT().GasPrice(gomock.Any()).Return(testGasPrice, nil)
|
usedGasPrice = (*big.Int)(testGasPrice)
|
||||||
} else {
|
s.txServiceMock.EXPECT().GasPrice(gomock.Any()).Return(testGasPrice, nil)
|
||||||
usedGasPrice = (*big.Int)(args.GasPrice)
|
} else {
|
||||||
}
|
usedGasPrice = (*big.Int)(args.GasPrice)
|
||||||
if args.Gas == nil {
|
}
|
||||||
s.txServiceMock.EXPECT().EstimateGas(gomock.Any(), gomock.Any()).Return(testGas, nil)
|
if args.Gas == nil {
|
||||||
usedGas = testGas
|
s.txServiceMock.EXPECT().EstimateGas(gomock.Any(), gomock.Any()).Return(testGas, nil)
|
||||||
} else {
|
usedGas = testGas
|
||||||
usedGas = *args.Gas
|
} else {
|
||||||
|
usedGas = *args.Gas
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Prepare the transaction and RLP encode it.
|
// Prepare the transaction and RLP encode it.
|
||||||
data := s.rlpEncodeTx(args, s.nodeConfig, account, &resultNonce, usedGas, usedGasPrice)
|
data := s.rlpEncodeTx(args, s.nodeConfig, account, &resultNonce, usedGas, usedGasPrice)
|
||||||
|
@ -99,16 +101,35 @@ func (s *TransactorSuite) setupTransactionPoolAPI(args SendTxArgs, returnNonce,
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *TransactorSuite) rlpEncodeTx(args SendTxArgs, config *params.NodeConfig, account *account.SelectedExtKey, nonce *hexutil.Uint64, gas hexutil.Uint64, gasPrice *big.Int) hexutil.Bytes {
|
func (s *TransactorSuite) rlpEncodeTx(args SendTxArgs, config *params.NodeConfig, account *account.SelectedExtKey, nonce *hexutil.Uint64, gas hexutil.Uint64, gasPrice *big.Int) hexutil.Bytes {
|
||||||
newTx := gethtypes.NewTransaction(
|
var txData gethtypes.TxData
|
||||||
uint64(*nonce),
|
to := common.Address(*args.To)
|
||||||
common.Address(*args.To),
|
if args.IsDynamicFeeTx() {
|
||||||
args.Value.ToInt(),
|
gasTipCap := (*big.Int)(args.MaxPriorityFeePerGas)
|
||||||
uint64(gas),
|
gasFeeCap := (*big.Int)(args.MaxFeePerGas)
|
||||||
gasPrice,
|
|
||||||
[]byte(args.Input),
|
txData = &gethtypes.DynamicFeeTx{
|
||||||
)
|
Nonce: uint64(*nonce),
|
||||||
|
Gas: uint64(gas),
|
||||||
|
GasTipCap: gasTipCap,
|
||||||
|
GasFeeCap: gasFeeCap,
|
||||||
|
To: &to,
|
||||||
|
Value: args.Value.ToInt(),
|
||||||
|
Data: args.GetInput(),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
txData = &gethtypes.LegacyTx{
|
||||||
|
Nonce: uint64(*nonce),
|
||||||
|
GasPrice: gasPrice,
|
||||||
|
Gas: uint64(gas),
|
||||||
|
To: &to,
|
||||||
|
Value: args.Value.ToInt(),
|
||||||
|
Data: args.GetInput(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
newTx := gethtypes.NewTx(txData)
|
||||||
chainID := big.NewInt(int64(config.NetworkID))
|
chainID := big.NewInt(int64(config.NetworkID))
|
||||||
signedTx, err := gethtypes.SignTx(newTx, gethtypes.NewEIP155Signer(chainID), account.AccountKey.PrivateKey)
|
signedTx, err := gethtypes.SignTx(newTx, gethtypes.NewLondonSigner(chainID), account.AccountKey.PrivateKey)
|
||||||
s.NoError(err)
|
s.NoError(err)
|
||||||
data, err := rlp.EncodeToBytes(signedTx)
|
data, err := rlp.EncodeToBytes(signedTx)
|
||||||
s.NoError(err)
|
s.NoError(err)
|
||||||
|
@ -122,29 +143,47 @@ func (s *TransactorSuite) TestGasValues() {
|
||||||
AccountKey: &types.Key{PrivateKey: key},
|
AccountKey: &types.Key{PrivateKey: key},
|
||||||
}
|
}
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
name string
|
name string
|
||||||
gas *hexutil.Uint64
|
gas *hexutil.Uint64
|
||||||
gasPrice *hexutil.Big
|
gasPrice *hexutil.Big
|
||||||
|
maxFeePerGas *hexutil.Big
|
||||||
|
maxPriorityFeePerGas *hexutil.Big
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
"noGasDef",
|
"noGasDef",
|
||||||
nil,
|
nil,
|
||||||
nil,
|
nil,
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"gasDefined",
|
"gasDefined",
|
||||||
&testGas,
|
&testGas,
|
||||||
nil,
|
nil,
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"gasPriceDefined",
|
"gasPriceDefined",
|
||||||
nil,
|
nil,
|
||||||
testGasPrice,
|
testGasPrice,
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"nilSignTransactionSpecificArgs",
|
"nilSignTransactionSpecificArgs",
|
||||||
nil,
|
nil,
|
||||||
nil,
|
nil,
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"maxFeeAndPriorityset",
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
testGasPrice,
|
||||||
|
testGasPrice,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -152,10 +191,12 @@ func (s *TransactorSuite) TestGasValues() {
|
||||||
s.T().Run(testCase.name, func(t *testing.T) {
|
s.T().Run(testCase.name, func(t *testing.T) {
|
||||||
s.SetupTest()
|
s.SetupTest()
|
||||||
args := SendTxArgs{
|
args := SendTxArgs{
|
||||||
From: account.FromAddress(utils.TestConfig.Account1.WalletAddress),
|
From: account.FromAddress(utils.TestConfig.Account1.WalletAddress),
|
||||||
To: account.ToAddress(utils.TestConfig.Account2.WalletAddress),
|
To: account.ToAddress(utils.TestConfig.Account2.WalletAddress),
|
||||||
Gas: testCase.gas,
|
Gas: testCase.gas,
|
||||||
GasPrice: testCase.gasPrice,
|
GasPrice: testCase.gasPrice,
|
||||||
|
MaxFeePerGas: testCase.maxFeePerGas,
|
||||||
|
MaxPriorityFeePerGas: testCase.maxPriorityFeePerGas,
|
||||||
}
|
}
|
||||||
s.setupTransactionPoolAPI(args, testNonce, testNonce, selectedAccount, nil)
|
s.setupTransactionPoolAPI(args, testNonce, testNonce, selectedAccount, nil)
|
||||||
|
|
||||||
|
@ -309,7 +350,7 @@ func (s *TransactorSuite) TestSendTransactionWithSignature() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// simulate transaction signed externally
|
// simulate transaction signed externally
|
||||||
signer := gethtypes.NewEIP155Signer(chainID)
|
signer := gethtypes.NewLondonSigner(chainID)
|
||||||
tx := gethtypes.NewTransaction(uint64(nonce), common.Address(to), (*big.Int)(value), uint64(gas), (*big.Int)(gasPrice), data)
|
tx := gethtypes.NewTransaction(uint64(nonce), common.Address(to), (*big.Int)(value), uint64(gas), (*big.Int)(gasPrice), data)
|
||||||
hash := signer.Hash(tx)
|
hash := signer.Hash(tx)
|
||||||
sig, err := gethcrypto.Sign(hash[:], privKey)
|
sig, err := gethcrypto.Sign(hash[:], privKey)
|
||||||
|
|
|
@ -38,12 +38,14 @@ type GasCalculator interface {
|
||||||
// This struct is based on go-ethereum's type in internal/ethapi/api.go, but we have freedom
|
// This struct is based on go-ethereum's type in internal/ethapi/api.go, but we have freedom
|
||||||
// over the exact layout of this struct.
|
// over the exact layout of this struct.
|
||||||
type SendTxArgs struct {
|
type SendTxArgs struct {
|
||||||
From types.Address `json:"from"`
|
From types.Address `json:"from"`
|
||||||
To *types.Address `json:"to"`
|
To *types.Address `json:"to"`
|
||||||
Gas *hexutil.Uint64 `json:"gas"`
|
Gas *hexutil.Uint64 `json:"gas"`
|
||||||
GasPrice *hexutil.Big `json:"gasPrice"`
|
GasPrice *hexutil.Big `json:"gasPrice"`
|
||||||
Value *hexutil.Big `json:"value"`
|
Value *hexutil.Big `json:"value"`
|
||||||
Nonce *hexutil.Uint64 `json:"nonce"`
|
Nonce *hexutil.Uint64 `json:"nonce"`
|
||||||
|
MaxFeePerGas *hexutil.Big `json:"maxFeePerGas"`
|
||||||
|
MaxPriorityFeePerGas *hexutil.Big `json:"maxPriorityFeePerGas"`
|
||||||
// We keep both "input" and "data" for backward compatibility.
|
// We keep both "input" and "data" for backward compatibility.
|
||||||
// "input" is a preferred field.
|
// "input" is a preferred field.
|
||||||
// see `vendor/github.com/ethereum/go-ethereum/internal/ethapi/api.go:1107`
|
// see `vendor/github.com/ethereum/go-ethereum/internal/ethapi/api.go:1107`
|
||||||
|
@ -62,6 +64,11 @@ func (args SendTxArgs) Valid() bool {
|
||||||
return bytes.Equal(args.Input, args.Data)
|
return bytes.Equal(args.Input, args.Data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsDynamicFeeTx checks whether dynamic fee parameters are set for the tx
|
||||||
|
func (args SendTxArgs) IsDynamicFeeTx() bool {
|
||||||
|
return args.MaxFeePerGas != nil && args.MaxPriorityFeePerGas != nil
|
||||||
|
}
|
||||||
|
|
||||||
// GetInput returns either Input or Data field's value dependent on what is filled.
|
// GetInput returns either Input or Data field's value dependent on what is filled.
|
||||||
func (args SendTxArgs) GetInput() types.HexBytes {
|
func (args SendTxArgs) GetInput() types.HexBytes {
|
||||||
if !isNilOrEmpty(args.Input) {
|
if !isNilOrEmpty(args.Input) {
|
||||||
|
|
Loading…
Reference in New Issue