tests: DynamicFeeTx (#649)
* test DynamicFeeTx against state_transition_benchmark_test * add feemarketGenesis in the app setup param * add dynamicTxFee flag to KeeperTestSuite * add feemarketGenesis.BaseFee setup * update TestAddLog * fix gasFeeCap assignment in newMsgEthereumTx * modify keeperTestSuite helper functions to support dynamicTxFee * update test cases in grpc_query_test w/ dynamicTxFee * update the evm keeper utils tests * add dynamic tx fee in the ante tests * remove duplicate type define * fix error return type * update changelog Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com>
This commit is contained in:
parent
2f531af3f2
commit
1076307e6b
@ -55,6 +55,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
|
|||||||
* (rpc, test) [tharsis#608](https://github.com/tharsis/ethermint/pull/608) Fix rpc test.
|
* (rpc, test) [tharsis#608](https://github.com/tharsis/ethermint/pull/608) Fix rpc test.
|
||||||
* (rpc) [tharsis#661](https://github.com/tharsis/ethermint/pull/661) Fix OOM bug when creating too many filters using JSON-RPC.
|
* (rpc) [tharsis#661](https://github.com/tharsis/ethermint/pull/661) Fix OOM bug when creating too many filters using JSON-RPC.
|
||||||
* (evm) [tharsis#660](https://github.com/tharsis/ethermint/pull/660) Fix `nil` pointer panic in `ApplyNativeMessage`.
|
* (evm) [tharsis#660](https://github.com/tharsis/ethermint/pull/660) Fix `nil` pointer panic in `ApplyNativeMessage`.
|
||||||
|
* (evm, test) [tharsis#649](https://github.com/tharsis/ethermint/pull/649) Test DynamicFeeTx.
|
||||||
|
|
||||||
## [v0.7.0] - 2021-10-07
|
## [v0.7.0] - 2021-10-07
|
||||||
|
|
||||||
|
@ -6,11 +6,16 @@ import (
|
|||||||
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
ethparams "github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/tharsis/ethermint/tests"
|
"github.com/tharsis/ethermint/tests"
|
||||||
evmtypes "github.com/tharsis/ethermint/x/evm/types"
|
evmtypes "github.com/tharsis/ethermint/x/evm/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (suite AnteTestSuite) TestAnteHandler() {
|
func (suite AnteTestSuite) TestAnteHandler() {
|
||||||
|
suite.dynamicTxFee = false
|
||||||
|
suite.SetupTest() // reset
|
||||||
|
|
||||||
addr, privKey := tests.NewAddrKey()
|
addr, privKey := tests.NewAddrKey()
|
||||||
to := tests.GenerateAddress()
|
to := tests.GenerateAddress()
|
||||||
|
|
||||||
@ -145,3 +150,242 @@ func (suite AnteTestSuite) TestAnteHandler() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (suite AnteTestSuite) TestAnteHandlerWithDynamicTxFee() {
|
||||||
|
suite.dynamicTxFee = true
|
||||||
|
suite.SetupTest() // reset
|
||||||
|
|
||||||
|
addr, privKey := tests.NewAddrKey()
|
||||||
|
to := tests.GenerateAddress()
|
||||||
|
|
||||||
|
acc := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, addr.Bytes())
|
||||||
|
suite.Require().NoError(acc.SetSequence(1))
|
||||||
|
suite.app.AccountKeeper.SetAccount(suite.ctx, acc)
|
||||||
|
|
||||||
|
suite.app.EvmKeeper.AddBalance(addr, big.NewInt((ethparams.InitialBaseFee+10)*100000))
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
txFn func() sdk.Tx
|
||||||
|
checkTx bool
|
||||||
|
reCheckTx bool
|
||||||
|
expPass bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"success - DeliverTx (contract)",
|
||||||
|
func() sdk.Tx {
|
||||||
|
signedContractTx :=
|
||||||
|
evmtypes.NewTxContract(
|
||||||
|
suite.app.EvmKeeper.ChainID(),
|
||||||
|
1,
|
||||||
|
big.NewInt(10),
|
||||||
|
100000,
|
||||||
|
nil,
|
||||||
|
big.NewInt(ethparams.InitialBaseFee+1),
|
||||||
|
big.NewInt(1),
|
||||||
|
nil,
|
||||||
|
&types.AccessList{},
|
||||||
|
)
|
||||||
|
signedContractTx.From = addr.Hex()
|
||||||
|
|
||||||
|
tx := suite.CreateTestTx(signedContractTx, privKey, 1, true)
|
||||||
|
return tx
|
||||||
|
},
|
||||||
|
false, false, true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"success - CheckTx (contract)",
|
||||||
|
func() sdk.Tx {
|
||||||
|
signedContractTx :=
|
||||||
|
evmtypes.NewTxContract(
|
||||||
|
suite.app.EvmKeeper.ChainID(),
|
||||||
|
1,
|
||||||
|
big.NewInt(10),
|
||||||
|
100000,
|
||||||
|
nil,
|
||||||
|
big.NewInt(ethparams.InitialBaseFee+1),
|
||||||
|
big.NewInt(1),
|
||||||
|
nil,
|
||||||
|
&types.AccessList{},
|
||||||
|
)
|
||||||
|
signedContractTx.From = addr.Hex()
|
||||||
|
|
||||||
|
tx := suite.CreateTestTx(signedContractTx, privKey, 1, true)
|
||||||
|
return tx
|
||||||
|
},
|
||||||
|
true, false, true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"success - ReCheckTx (contract)",
|
||||||
|
func() sdk.Tx {
|
||||||
|
signedContractTx :=
|
||||||
|
evmtypes.NewTxContract(
|
||||||
|
suite.app.EvmKeeper.ChainID(),
|
||||||
|
1,
|
||||||
|
big.NewInt(10),
|
||||||
|
100000,
|
||||||
|
nil,
|
||||||
|
big.NewInt(ethparams.InitialBaseFee+1),
|
||||||
|
big.NewInt(1),
|
||||||
|
nil,
|
||||||
|
&types.AccessList{},
|
||||||
|
)
|
||||||
|
signedContractTx.From = addr.Hex()
|
||||||
|
|
||||||
|
tx := suite.CreateTestTx(signedContractTx, privKey, 1, true)
|
||||||
|
return tx
|
||||||
|
},
|
||||||
|
false, true, true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"success - DeliverTx",
|
||||||
|
func() sdk.Tx {
|
||||||
|
signedTx :=
|
||||||
|
evmtypes.NewTx(
|
||||||
|
suite.app.EvmKeeper.ChainID(),
|
||||||
|
1,
|
||||||
|
&to,
|
||||||
|
big.NewInt(10),
|
||||||
|
100000,
|
||||||
|
nil,
|
||||||
|
big.NewInt(ethparams.InitialBaseFee+1),
|
||||||
|
big.NewInt(1),
|
||||||
|
nil,
|
||||||
|
&types.AccessList{},
|
||||||
|
)
|
||||||
|
signedTx.From = addr.Hex()
|
||||||
|
|
||||||
|
tx := suite.CreateTestTx(signedTx, privKey, 1, true)
|
||||||
|
return tx
|
||||||
|
},
|
||||||
|
false, false, true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"success - CheckTx",
|
||||||
|
func() sdk.Tx {
|
||||||
|
signedTx :=
|
||||||
|
evmtypes.NewTx(
|
||||||
|
suite.app.EvmKeeper.ChainID(),
|
||||||
|
2,
|
||||||
|
&to,
|
||||||
|
big.NewInt(10),
|
||||||
|
100000,
|
||||||
|
nil,
|
||||||
|
big.NewInt(ethparams.InitialBaseFee+1),
|
||||||
|
big.NewInt(1),
|
||||||
|
nil,
|
||||||
|
&types.AccessList{},
|
||||||
|
)
|
||||||
|
signedTx.From = addr.Hex()
|
||||||
|
|
||||||
|
tx := suite.CreateTestTx(signedTx, privKey, 1, true)
|
||||||
|
return tx
|
||||||
|
},
|
||||||
|
true, false, true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"success - ReCheckTx",
|
||||||
|
func() sdk.Tx {
|
||||||
|
signedTx :=
|
||||||
|
evmtypes.NewTx(
|
||||||
|
suite.app.EvmKeeper.ChainID(),
|
||||||
|
3,
|
||||||
|
&to,
|
||||||
|
big.NewInt(10),
|
||||||
|
100000,
|
||||||
|
nil,
|
||||||
|
big.NewInt(ethparams.InitialBaseFee+1),
|
||||||
|
big.NewInt(1),
|
||||||
|
nil,
|
||||||
|
&types.AccessList{},
|
||||||
|
)
|
||||||
|
signedTx.From = addr.Hex()
|
||||||
|
|
||||||
|
tx := suite.CreateTestTx(signedTx, privKey, 1, true)
|
||||||
|
return tx
|
||||||
|
}, false, true, true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"success - CheckTx (cosmos tx not signed)",
|
||||||
|
func() sdk.Tx {
|
||||||
|
signedTx :=
|
||||||
|
evmtypes.NewTx(
|
||||||
|
suite.app.EvmKeeper.ChainID(),
|
||||||
|
4,
|
||||||
|
&to,
|
||||||
|
big.NewInt(10),
|
||||||
|
100000,
|
||||||
|
nil,
|
||||||
|
big.NewInt(ethparams.InitialBaseFee+1),
|
||||||
|
big.NewInt(1),
|
||||||
|
nil,
|
||||||
|
&types.AccessList{},
|
||||||
|
)
|
||||||
|
signedTx.From = addr.Hex()
|
||||||
|
|
||||||
|
tx := suite.CreateTestTx(signedTx, privKey, 1, false)
|
||||||
|
return tx
|
||||||
|
}, false, true, true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fail - CheckTx (cosmos tx is not valid)",
|
||||||
|
func() sdk.Tx {
|
||||||
|
signedTx :=
|
||||||
|
evmtypes.NewTx(
|
||||||
|
suite.app.EvmKeeper.ChainID(),
|
||||||
|
4,
|
||||||
|
&to,
|
||||||
|
big.NewInt(10),
|
||||||
|
100000,
|
||||||
|
nil,
|
||||||
|
big.NewInt(ethparams.InitialBaseFee+1),
|
||||||
|
big.NewInt(1),
|
||||||
|
nil,
|
||||||
|
&types.AccessList{},
|
||||||
|
)
|
||||||
|
signedTx.From = addr.Hex()
|
||||||
|
|
||||||
|
txBuilder := suite.CreateTestTxBuilder(signedTx, privKey, 1, false)
|
||||||
|
// bigger than MaxGasWanted
|
||||||
|
txBuilder.SetGasLimit(uint64(1 << 63))
|
||||||
|
return txBuilder.GetTx()
|
||||||
|
}, false, true, false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fail - CheckTx (memo too long)",
|
||||||
|
func() sdk.Tx {
|
||||||
|
signedTx :=
|
||||||
|
evmtypes.NewTx(
|
||||||
|
suite.app.EvmKeeper.ChainID(),
|
||||||
|
5,
|
||||||
|
&to,
|
||||||
|
big.NewInt(10),
|
||||||
|
100000,
|
||||||
|
nil,
|
||||||
|
big.NewInt(ethparams.InitialBaseFee+1),
|
||||||
|
big.NewInt(1),
|
||||||
|
nil,
|
||||||
|
&types.AccessList{},
|
||||||
|
)
|
||||||
|
signedTx.From = addr.Hex()
|
||||||
|
|
||||||
|
txBuilder := suite.CreateTestTxBuilder(signedTx, privKey, 1, true)
|
||||||
|
txBuilder.SetMemo(strings.Repeat("*", 257))
|
||||||
|
return txBuilder.GetTx()
|
||||||
|
}, true, false, false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
suite.Run(tc.name, func() {
|
||||||
|
suite.ctx = suite.ctx.WithIsCheckTx(tc.reCheckTx).WithIsReCheckTx(tc.reCheckTx)
|
||||||
|
_, err := suite.anteHandler(suite.ctx, tc.txFn(), false)
|
||||||
|
if tc.expPass {
|
||||||
|
suite.Require().NoError(err)
|
||||||
|
} else {
|
||||||
|
suite.Require().Error(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
suite.dynamicTxFee = false
|
||||||
|
}
|
||||||
|
@ -24,6 +24,7 @@ import (
|
|||||||
"github.com/tharsis/ethermint/encoding"
|
"github.com/tharsis/ethermint/encoding"
|
||||||
"github.com/tharsis/ethermint/tests"
|
"github.com/tharsis/ethermint/tests"
|
||||||
evmtypes "github.com/tharsis/ethermint/x/evm/types"
|
evmtypes "github.com/tharsis/ethermint/x/evm/types"
|
||||||
|
feemarkettypes "github.com/tharsis/ethermint/x/feemarket/types"
|
||||||
|
|
||||||
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
||||||
)
|
)
|
||||||
@ -36,11 +37,23 @@ type AnteTestSuite struct {
|
|||||||
clientCtx client.Context
|
clientCtx client.Context
|
||||||
anteHandler sdk.AnteHandler
|
anteHandler sdk.AnteHandler
|
||||||
ethSigner ethtypes.Signer
|
ethSigner ethtypes.Signer
|
||||||
|
dynamicTxFee bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *AnteTestSuite) SetupTest() {
|
func (suite *AnteTestSuite) SetupTest() {
|
||||||
checkTx := false
|
checkTx := false
|
||||||
suite.app = app.Setup(checkTx)
|
|
||||||
|
if suite.dynamicTxFee {
|
||||||
|
// setup feemarketGenesis params
|
||||||
|
feemarketGenesis := feemarkettypes.DefaultGenesisState()
|
||||||
|
feemarketGenesis.Params.EnableHeight = 1
|
||||||
|
feemarketGenesis.Params.NoBaseFee = false
|
||||||
|
feemarketGenesis.BaseFee = sdk.NewInt(feemarketGenesis.Params.InitialBaseFee)
|
||||||
|
suite.app = app.Setup(checkTx, feemarketGenesis)
|
||||||
|
} else {
|
||||||
|
suite.app = app.Setup(checkTx, nil)
|
||||||
|
}
|
||||||
|
|
||||||
suite.ctx = suite.app.BaseApp.NewContext(checkTx, tmproto.Header{Height: 2, ChainID: "ethermint_9000-1", Time: time.Now().UTC()})
|
suite.ctx = suite.app.BaseApp.NewContext(checkTx, tmproto.Header{Height: 2, ChainID: "ethermint_9000-1", Time: time.Now().UTC()})
|
||||||
suite.ctx = suite.ctx.WithMinGasPrices(sdk.NewDecCoins(sdk.NewDecCoin(evmtypes.DefaultEVMDenom, sdk.OneInt())))
|
suite.ctx = suite.ctx.WithMinGasPrices(sdk.NewDecCoins(sdk.NewDecCoin(evmtypes.DefaultEVMDenom, sdk.OneInt())))
|
||||||
suite.ctx = suite.ctx.WithBlockGasMeter(sdk.NewGasMeter(1000000000000000000))
|
suite.ctx = suite.ctx.WithBlockGasMeter(sdk.NewGasMeter(1000000000000000000))
|
||||||
|
@ -12,6 +12,7 @@ import (
|
|||||||
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
||||||
tmtypes "github.com/tendermint/tendermint/types"
|
tmtypes "github.com/tendermint/tendermint/types"
|
||||||
dbm "github.com/tendermint/tm-db"
|
dbm "github.com/tendermint/tm-db"
|
||||||
|
feemarkettypes "github.com/tharsis/ethermint/x/feemarket/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
// DefaultConsensusParams defines the default Tendermint consensus params used in
|
// DefaultConsensusParams defines the default Tendermint consensus params used in
|
||||||
@ -34,12 +35,22 @@ var DefaultConsensusParams = &abci.ConsensusParams{
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Setup initializes a new EthermintApp. A Nop logger is set in EthermintApp.
|
// Setup initializes a new EthermintApp. A Nop logger is set in EthermintApp.
|
||||||
func Setup(isCheckTx bool) *EthermintApp {
|
func Setup(isCheckTx bool, feemarketGenesis *feemarkettypes.GenesisState) *EthermintApp {
|
||||||
db := dbm.NewMemDB()
|
db := dbm.NewMemDB()
|
||||||
app := NewEthermintApp(log.NewNopLogger(), db, nil, true, map[int64]bool{}, DefaultNodeHome, 5, encoding.MakeConfig(ModuleBasics), simapp.EmptyAppOptions{})
|
app := NewEthermintApp(log.NewNopLogger(), db, nil, true, map[int64]bool{}, DefaultNodeHome, 5, encoding.MakeConfig(ModuleBasics), simapp.EmptyAppOptions{})
|
||||||
if !isCheckTx {
|
if !isCheckTx {
|
||||||
// init chain must be called to stop deliverState from being nil
|
// init chain must be called to stop deliverState from being nil
|
||||||
genesisState := NewDefaultGenesisState()
|
genesisState := NewDefaultGenesisState()
|
||||||
|
|
||||||
|
// Verify feeMarket genesis
|
||||||
|
if feemarketGenesis != nil {
|
||||||
|
if err := feemarketGenesis.Validate(); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
genesisState[feemarkettypes.ModuleName] = app.AppCodec().MustMarshalJSON(feemarketGenesis)
|
||||||
|
}
|
||||||
|
|
||||||
stateBytes, err := json.MarshalIndent(genesisState, "", " ")
|
stateBytes, err := json.MarshalIndent(genesisState, "", " ")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
@ -13,6 +13,7 @@ import (
|
|||||||
"github.com/cosmos/cosmos-sdk/simapp"
|
"github.com/cosmos/cosmos-sdk/simapp"
|
||||||
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
|
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
|
||||||
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
|
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
|
||||||
|
feemarkettypes "github.com/tharsis/ethermint/x/feemarket/types"
|
||||||
|
|
||||||
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
|
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
@ -53,6 +54,8 @@ type EvmTestSuite struct {
|
|||||||
ethSigner ethtypes.Signer
|
ethSigner ethtypes.Signer
|
||||||
from common.Address
|
from common.Address
|
||||||
to sdk.AccAddress
|
to sdk.AccAddress
|
||||||
|
|
||||||
|
dynamicTxFee bool
|
||||||
}
|
}
|
||||||
|
|
||||||
/// DoSetupTest setup test environment, it uses`require.TestingT` to support both `testing.T` and `testing.B`.
|
/// DoSetupTest setup test environment, it uses`require.TestingT` to support both `testing.T` and `testing.B`.
|
||||||
@ -70,7 +73,15 @@ func (suite *EvmTestSuite) DoSetupTest(t require.TestingT) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
consAddress := sdk.ConsAddress(priv.PubKey().Address())
|
consAddress := sdk.ConsAddress(priv.PubKey().Address())
|
||||||
|
|
||||||
suite.app = app.Setup(checkTx)
|
if suite.dynamicTxFee {
|
||||||
|
feemarketGenesis := feemarkettypes.DefaultGenesisState()
|
||||||
|
feemarketGenesis.Params.EnableHeight = 1
|
||||||
|
feemarketGenesis.Params.NoBaseFee = false
|
||||||
|
suite.app = app.Setup(checkTx, feemarketGenesis)
|
||||||
|
} else {
|
||||||
|
suite.app = app.Setup(checkTx, nil)
|
||||||
|
}
|
||||||
|
|
||||||
coins := sdk.NewCoins(sdk.NewCoin(types.DefaultEVMDenom, sdk.NewInt(100000000000000)))
|
coins := sdk.NewCoins(sdk.NewCoin(types.DefaultEVMDenom, sdk.NewInt(100000000000000)))
|
||||||
genesisState := app.ModuleBasics.DefaultGenesis(suite.app.AppCodec())
|
genesisState := app.ModuleBasics.DefaultGenesis(suite.app.AppCodec())
|
||||||
b32address := sdk.MustBech32ifyAddressBytes(sdk.GetConfig().GetBech32AccountAddrPrefix(), priv.PubKey().Address().Bytes())
|
b32address := sdk.MustBech32ifyAddressBytes(sdk.GetConfig().GetBech32AccountAddrPrefix(), priv.PubKey().Address().Bytes())
|
||||||
|
@ -489,28 +489,29 @@ func (suite *KeeperTestSuite) TestEstimateGas() {
|
|||||||
malleate func()
|
malleate func()
|
||||||
expPass bool
|
expPass bool
|
||||||
expGas uint64
|
expGas uint64
|
||||||
|
dynamicTxFee bool
|
||||||
}{
|
}{
|
||||||
// should success, because transfer value is zero
|
// should success, because transfer value is zero
|
||||||
{"default args", func() {
|
{"default args", func() {
|
||||||
args = types.TransactionArgs{To: &common.Address{}}
|
args = types.TransactionArgs{To: &common.Address{}}
|
||||||
}, true, 21000},
|
}, true, 21000, false},
|
||||||
// should fail, because the default From address(zero address) don't have fund
|
// should fail, because the default From address(zero address) don't have fund
|
||||||
{"not enough balance", func() {
|
{"not enough balance", func() {
|
||||||
args = types.TransactionArgs{To: &common.Address{}, Value: (*hexutil.Big)(big.NewInt(100))}
|
args = types.TransactionArgs{To: &common.Address{}, Value: (*hexutil.Big)(big.NewInt(100))}
|
||||||
}, false, 0},
|
}, false, 0, false},
|
||||||
// should success, enough balance now
|
// should success, enough balance now
|
||||||
{"enough balance", func() {
|
{"enough balance", func() {
|
||||||
args = types.TransactionArgs{To: &common.Address{}, From: &suite.address, Value: (*hexutil.Big)(big.NewInt(100))}
|
args = types.TransactionArgs{To: &common.Address{}, From: &suite.address, Value: (*hexutil.Big)(big.NewInt(100))}
|
||||||
}, false, 0},
|
}, false, 0, false},
|
||||||
// should success, because gas limit lower than 21000 is ignored
|
// should success, because gas limit lower than 21000 is ignored
|
||||||
{"gas exceed allowance", func() {
|
{"gas exceed allowance", func() {
|
||||||
args = types.TransactionArgs{To: &common.Address{}, Gas: &gasHelper}
|
args = types.TransactionArgs{To: &common.Address{}, Gas: &gasHelper}
|
||||||
}, true, 21000},
|
}, true, 21000, false},
|
||||||
// should fail, invalid gas cap
|
// should fail, invalid gas cap
|
||||||
{"gas exceed global allowance", func() {
|
{"gas exceed global allowance", func() {
|
||||||
args = types.TransactionArgs{To: &common.Address{}}
|
args = types.TransactionArgs{To: &common.Address{}}
|
||||||
gasCap = 20000
|
gasCap = 20000
|
||||||
}, false, 0},
|
}, false, 0, false},
|
||||||
// estimate gas of an erc20 contract deployment, the exact gas number is checked with geth
|
// estimate gas of an erc20 contract deployment, the exact gas number is checked with geth
|
||||||
{"contract deployment", func() {
|
{"contract deployment", func() {
|
||||||
ctorArgs, err := ContractABI.Pack("", &suite.address, sdk.NewIntWithDecimal(1000, 18).BigInt())
|
ctorArgs, err := ContractABI.Pack("", &suite.address, sdk.NewIntWithDecimal(1000, 18).BigInt())
|
||||||
@ -520,7 +521,7 @@ func (suite *KeeperTestSuite) TestEstimateGas() {
|
|||||||
From: &suite.address,
|
From: &suite.address,
|
||||||
Data: (*hexutil.Bytes)(&data),
|
Data: (*hexutil.Bytes)(&data),
|
||||||
}
|
}
|
||||||
}, true, 1186778},
|
}, true, 1186778, false},
|
||||||
// estimate gas of an erc20 transfer, the exact gas number is checked with geth
|
// estimate gas of an erc20 transfer, the exact gas number is checked with geth
|
||||||
{"erc20 transfer", func() {
|
{"erc20 transfer", func() {
|
||||||
contractAddr := suite.DeployTestContract(suite.T(), suite.address, sdk.NewIntWithDecimal(1000, 18).BigInt())
|
contractAddr := suite.DeployTestContract(suite.T(), suite.address, sdk.NewIntWithDecimal(1000, 18).BigInt())
|
||||||
@ -528,11 +529,46 @@ func (suite *KeeperTestSuite) TestEstimateGas() {
|
|||||||
transferData, err := ContractABI.Pack("transfer", common.HexToAddress("0x378c50D9264C63F3F92B806d4ee56E9D86FfB3Ec"), big.NewInt(1000))
|
transferData, err := ContractABI.Pack("transfer", common.HexToAddress("0x378c50D9264C63F3F92B806d4ee56E9D86FfB3Ec"), big.NewInt(1000))
|
||||||
suite.Require().NoError(err)
|
suite.Require().NoError(err)
|
||||||
args = types.TransactionArgs{To: &contractAddr, From: &suite.address, Data: (*hexutil.Bytes)(&transferData)}
|
args = types.TransactionArgs{To: &contractAddr, From: &suite.address, Data: (*hexutil.Bytes)(&transferData)}
|
||||||
}, true, 51880},
|
}, true, 51880, false},
|
||||||
|
|
||||||
|
// repeated tests with dynamicTxFee
|
||||||
|
{"default args w/ dynamicTxFee", func() {
|
||||||
|
args = types.TransactionArgs{To: &common.Address{}}
|
||||||
|
}, true, 21000, true},
|
||||||
|
{"not enough balance w/ dynamicTxFee", func() {
|
||||||
|
args = types.TransactionArgs{To: &common.Address{}, Value: (*hexutil.Big)(big.NewInt(100))}
|
||||||
|
}, false, 0, true},
|
||||||
|
{"enough balance w/ dynamicTxFee", func() {
|
||||||
|
args = types.TransactionArgs{To: &common.Address{}, From: &suite.address, Value: (*hexutil.Big)(big.NewInt(100))}
|
||||||
|
}, false, 0, true},
|
||||||
|
{"gas exceed allowance w/ dynamicTxFee", func() {
|
||||||
|
args = types.TransactionArgs{To: &common.Address{}, Gas: &gasHelper}
|
||||||
|
}, true, 21000, true},
|
||||||
|
{"gas exceed global allowance w/ dynamicTxFee", func() {
|
||||||
|
args = types.TransactionArgs{To: &common.Address{}}
|
||||||
|
gasCap = 20000
|
||||||
|
}, false, 0, true},
|
||||||
|
{"contract deployment w/ dynamicTxFee", func() {
|
||||||
|
ctorArgs, err := ContractABI.Pack("", &suite.address, sdk.NewIntWithDecimal(1000, 18).BigInt())
|
||||||
|
suite.Require().NoError(err)
|
||||||
|
data := append(ContractBin, ctorArgs...)
|
||||||
|
args = types.TransactionArgs{
|
||||||
|
From: &suite.address,
|
||||||
|
Data: (*hexutil.Bytes)(&data),
|
||||||
|
}
|
||||||
|
}, true, 1186778, true},
|
||||||
|
{"erc20 transfer w/ dynamicTxFee", func() {
|
||||||
|
contractAddr := suite.DeployTestContract(suite.T(), suite.address, sdk.NewIntWithDecimal(1000, 18).BigInt())
|
||||||
|
suite.Commit()
|
||||||
|
transferData, err := ContractABI.Pack("transfer", common.HexToAddress("0x378c50D9264C63F3F92B806d4ee56E9D86FfB3Ec"), big.NewInt(1000))
|
||||||
|
suite.Require().NoError(err)
|
||||||
|
args = types.TransactionArgs{To: &contractAddr, From: &suite.address, Data: (*hexutil.Bytes)(&transferData)}
|
||||||
|
}, true, 51880, true},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
suite.Run(fmt.Sprintf("Case %s", tc.msg), func() {
|
suite.Run(fmt.Sprintf("Case %s", tc.msg), func() {
|
||||||
|
suite.dynamicTxFee = tc.dynamicTxFee
|
||||||
suite.SetupTest()
|
suite.SetupTest()
|
||||||
gasCap = 25_000_000
|
gasCap = 25_000_000
|
||||||
tc.malleate()
|
tc.malleate()
|
||||||
@ -553,6 +589,7 @@ func (suite *KeeperTestSuite) TestEstimateGas() {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
suite.dynamicTxFee = false // reset flag
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *KeeperTestSuite) TestTraceTx() {
|
func (suite *KeeperTestSuite) TestTraceTx() {
|
||||||
@ -568,6 +605,7 @@ func (suite *KeeperTestSuite) TestTraceTx() {
|
|||||||
malleate func()
|
malleate func()
|
||||||
expPass bool
|
expPass bool
|
||||||
traceResponse []byte
|
traceResponse []byte
|
||||||
|
dynamicTxFee bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
msg: "default trace",
|
msg: "default trace",
|
||||||
@ -586,10 +624,30 @@ func (suite *KeeperTestSuite) TestTraceTx() {
|
|||||||
expPass: true,
|
expPass: true,
|
||||||
traceResponse: []byte{0x5b, 0x5d},
|
traceResponse: []byte{0x5b, 0x5d},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
msg: "default trace with dynamicTxFee",
|
||||||
|
malleate: func() {
|
||||||
|
traceConfig = nil
|
||||||
|
},
|
||||||
|
expPass: true,
|
||||||
|
traceResponse: []byte{0x7b, 0x22, 0x67, 0x61, 0x73, 0x22, 0x3a, 0x33, 0x34, 0x38, 0x32, 0x38, 0x2c, 0x22, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x22, 0x3a, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x22, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x3a, 0x22, 0x22, 0x2c, 0x22, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x4c, 0x6f, 0x67, 0x73, 0x22, 0x3a, 0x5b, 0x5d, 0x7d},
|
||||||
|
dynamicTxFee: true,
|
||||||
|
}, {
|
||||||
|
msg: "javascript tracer with dynamicTxFee",
|
||||||
|
malleate: func() {
|
||||||
|
traceConfig = &types.TraceConfig{
|
||||||
|
Tracer: "{data: [], fault: function(log) {}, step: function(log) { if(log.op.toString() == \"CALL\") this.data.push(log.stack.peek(0)); }, result: function() { return this.data; }}",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
expPass: true,
|
||||||
|
traceResponse: []byte{0x5b, 0x5d},
|
||||||
|
dynamicTxFee: true,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
suite.Run(fmt.Sprintf("Case %s", tc.msg), func() {
|
suite.Run(fmt.Sprintf("Case %s", tc.msg), func() {
|
||||||
|
suite.dynamicTxFee = tc.dynamicTxFee
|
||||||
suite.SetupTest()
|
suite.SetupTest()
|
||||||
// Deploy contract
|
// Deploy contract
|
||||||
contractAddr := suite.DeployTestContract(suite.T(), suite.address, sdk.NewIntWithDecimal(1000, 18).BigInt())
|
contractAddr := suite.DeployTestContract(suite.T(), suite.address, sdk.NewIntWithDecimal(1000, 18).BigInt())
|
||||||
@ -614,4 +672,6 @@ func (suite *KeeperTestSuite) TestTraceTx() {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suite.dynamicTxFee = false // reset flag
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@ import (
|
|||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
|
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
|
||||||
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
|
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
|
||||||
|
feemarkettypes "github.com/tharsis/ethermint/x/feemarket/types"
|
||||||
|
|
||||||
"github.com/tharsis/ethermint/app"
|
"github.com/tharsis/ethermint/app"
|
||||||
"github.com/tharsis/ethermint/crypto/ethsecp256k1"
|
"github.com/tharsis/ethermint/crypto/ethsecp256k1"
|
||||||
@ -79,6 +80,8 @@ type KeeperTestSuite struct {
|
|||||||
|
|
||||||
appCodec codec.Codec
|
appCodec codec.Codec
|
||||||
signer keyring.Signer
|
signer keyring.Signer
|
||||||
|
|
||||||
|
dynamicTxFee bool
|
||||||
}
|
}
|
||||||
|
|
||||||
/// DoSetupTest setup test environment, it uses`require.TestingT` to support both `testing.T` and `testing.B`.
|
/// DoSetupTest setup test environment, it uses`require.TestingT` to support both `testing.T` and `testing.B`.
|
||||||
@ -96,7 +99,17 @@ func (suite *KeeperTestSuite) DoSetupTest(t require.TestingT) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
suite.consAddress = sdk.ConsAddress(priv.PubKey().Address())
|
suite.consAddress = sdk.ConsAddress(priv.PubKey().Address())
|
||||||
|
|
||||||
suite.app = app.Setup(checkTx)
|
if suite.dynamicTxFee {
|
||||||
|
// setup feemarketGenesis params
|
||||||
|
feemarketGenesis := feemarkettypes.DefaultGenesisState()
|
||||||
|
feemarketGenesis.Params.EnableHeight = 1
|
||||||
|
feemarketGenesis.Params.NoBaseFee = false
|
||||||
|
feemarketGenesis.BaseFee = sdk.NewInt(feemarketGenesis.Params.InitialBaseFee)
|
||||||
|
suite.app = app.Setup(checkTx, feemarketGenesis)
|
||||||
|
} else {
|
||||||
|
suite.app = app.Setup(checkTx, nil)
|
||||||
|
}
|
||||||
|
|
||||||
suite.ctx = suite.app.BaseApp.NewContext(checkTx, tmproto.Header{
|
suite.ctx = suite.app.BaseApp.NewContext(checkTx, tmproto.Header{
|
||||||
Height: 1,
|
Height: 1,
|
||||||
ChainID: "ethermint_9000-1",
|
ChainID: "ethermint_9000-1",
|
||||||
@ -197,7 +210,22 @@ func (suite *KeeperTestSuite) DeployTestContract(t require.TestingT, owner commo
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
nonce := suite.app.EvmKeeper.GetNonce(suite.address)
|
nonce := suite.app.EvmKeeper.GetNonce(suite.address)
|
||||||
erc20DeployTx := types.NewTxContract(
|
|
||||||
|
var erc20DeployTx *types.MsgEthereumTx
|
||||||
|
if suite.dynamicTxFee {
|
||||||
|
erc20DeployTx = types.NewTxContract(
|
||||||
|
chainID,
|
||||||
|
nonce,
|
||||||
|
nil, // amount
|
||||||
|
res.Gas, // gasLimit
|
||||||
|
nil, // gasPrice
|
||||||
|
suite.app.FeeMarketKeeper.GetBaseFee(suite.ctx),
|
||||||
|
big.NewInt(1),
|
||||||
|
data, // input
|
||||||
|
ðtypes.AccessList{}, // accesses
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
erc20DeployTx = types.NewTxContract(
|
||||||
chainID,
|
chainID,
|
||||||
nonce,
|
nonce,
|
||||||
nil, // amount
|
nil, // amount
|
||||||
@ -207,6 +235,8 @@ func (suite *KeeperTestSuite) DeployTestContract(t require.TestingT, owner commo
|
|||||||
data, // input
|
data, // input
|
||||||
nil, // accesses
|
nil, // accesses
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
erc20DeployTx.From = suite.address.Hex()
|
erc20DeployTx.From = suite.address.Hex()
|
||||||
err = erc20DeployTx.Sign(ethtypes.LatestSignerForChainID(chainID), suite.signer)
|
err = erc20DeployTx.Sign(ethtypes.LatestSignerForChainID(chainID), suite.signer)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@ -231,7 +261,23 @@ func (suite *KeeperTestSuite) TransferERC20Token(t require.TestingT, contractAdd
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
nonce := suite.app.EvmKeeper.GetNonce(suite.address)
|
nonce := suite.app.EvmKeeper.GetNonce(suite.address)
|
||||||
ercTransferTx := types.NewTx(
|
|
||||||
|
var ercTransferTx *types.MsgEthereumTx
|
||||||
|
if suite.dynamicTxFee {
|
||||||
|
ercTransferTx = types.NewTx(
|
||||||
|
chainID,
|
||||||
|
nonce,
|
||||||
|
&contractAddr,
|
||||||
|
nil,
|
||||||
|
res.Gas,
|
||||||
|
nil,
|
||||||
|
suite.app.FeeMarketKeeper.GetBaseFee(suite.ctx),
|
||||||
|
big.NewInt(1),
|
||||||
|
transferData,
|
||||||
|
ðtypes.AccessList{}, // accesses
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
ercTransferTx = types.NewTx(
|
||||||
chainID,
|
chainID,
|
||||||
nonce,
|
nonce,
|
||||||
&contractAddr,
|
&contractAddr,
|
||||||
@ -242,6 +288,8 @@ func (suite *KeeperTestSuite) TransferERC20Token(t require.TestingT, contractAdd
|
|||||||
transferData,
|
transferData,
|
||||||
nil,
|
nil,
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
ercTransferTx.From = suite.address.Hex()
|
ercTransferTx.From = suite.address.Hex()
|
||||||
err = ercTransferTx.Sign(ethtypes.LatestSignerForChainID(chainID), suite.signer)
|
err = ercTransferTx.Sign(ethtypes.LatestSignerForChainID(chainID), suite.signer)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -31,6 +31,15 @@ var templateLegacyTx = ðtypes.LegacyTx{
|
|||||||
Data: []byte{},
|
Data: []byte{},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var templateDynamicFeeTx = ðtypes.DynamicFeeTx{
|
||||||
|
GasFeeCap: big.NewInt(10),
|
||||||
|
GasTipCap: big.NewInt(2),
|
||||||
|
Gas: 21000,
|
||||||
|
To: &common.Address{},
|
||||||
|
Value: big.NewInt(0),
|
||||||
|
Data: []byte{},
|
||||||
|
}
|
||||||
|
|
||||||
func newSignedEthTx(
|
func newSignedEthTx(
|
||||||
txData ethtypes.TxData,
|
txData ethtypes.TxData,
|
||||||
nonce uint64,
|
nonce uint64,
|
||||||
@ -46,6 +55,9 @@ func newSignedEthTx(
|
|||||||
case *ethtypes.LegacyTx:
|
case *ethtypes.LegacyTx:
|
||||||
txData.Nonce = nonce
|
txData.Nonce = nonce
|
||||||
ethTx = ethtypes.NewTx(txData)
|
ethTx = ethtypes.NewTx(txData)
|
||||||
|
case *ethtypes.DynamicFeeTx:
|
||||||
|
txData.Nonce = nonce
|
||||||
|
ethTx = ethtypes.NewTx(txData)
|
||||||
default:
|
default:
|
||||||
return nil, errors.New("unknown transaction type!")
|
return nil, errors.New("unknown transaction type!")
|
||||||
}
|
}
|
||||||
@ -70,7 +82,7 @@ func newNativeMessage(
|
|||||||
cfg *params.ChainConfig,
|
cfg *params.ChainConfig,
|
||||||
krSigner keyring.Signer,
|
krSigner keyring.Signer,
|
||||||
ethSigner ethtypes.Signer,
|
ethSigner ethtypes.Signer,
|
||||||
isLegacy bool,
|
txType byte,
|
||||||
) (core.Message, error) {
|
) (core.Message, error) {
|
||||||
msgSigner := ethtypes.MakeSigner(cfg, big.NewInt(blockHeight))
|
msgSigner := ethtypes.MakeSigner(cfg, big.NewInt(blockHeight))
|
||||||
|
|
||||||
@ -78,12 +90,20 @@ func newNativeMessage(
|
|||||||
ethTx *ethtypes.Transaction
|
ethTx *ethtypes.Transaction
|
||||||
baseFee *big.Int
|
baseFee *big.Int
|
||||||
)
|
)
|
||||||
if isLegacy {
|
|
||||||
|
switch txType {
|
||||||
|
case ethtypes.LegacyTxType:
|
||||||
templateLegacyTx.Nonce = nonce
|
templateLegacyTx.Nonce = nonce
|
||||||
ethTx = ethtypes.NewTx(templateLegacyTx)
|
ethTx = ethtypes.NewTx(templateLegacyTx)
|
||||||
} else {
|
case ethtypes.AccessListTxType:
|
||||||
templateAccessListTx.Nonce = nonce
|
templateAccessListTx.Nonce = nonce
|
||||||
ethTx = ethtypes.NewTx(templateAccessListTx)
|
ethTx = ethtypes.NewTx(templateAccessListTx)
|
||||||
|
case ethtypes.DynamicFeeTxType:
|
||||||
|
templateDynamicFeeTx.Nonce = nonce
|
||||||
|
ethTx = ethtypes.NewTx(templateDynamicFeeTx)
|
||||||
|
baseFee = big.NewInt(3)
|
||||||
|
default:
|
||||||
|
return nil, errors.New("unsupport tx type")
|
||||||
}
|
}
|
||||||
|
|
||||||
msg := &evmtypes.MsgEthereumTx{}
|
msg := &evmtypes.MsgEthereumTx{}
|
||||||
@ -94,7 +114,7 @@ func newNativeMessage(
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
m, err := msg.AsMessage(msgSigner, baseFee) // TODO: add DynamicFeeTx
|
m, err := msg.AsMessage(msgSigner, baseFee)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -156,6 +176,33 @@ func BenchmarkApplyTransactionWithLegacyTx(b *testing.B) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func BenchmarkApplyTransactionWithDynamicFeeTx(b *testing.B) {
|
||||||
|
suite := KeeperTestSuite{dynamicTxFee: true}
|
||||||
|
suite.DoSetupTest(b)
|
||||||
|
|
||||||
|
ethSigner := ethtypes.LatestSignerForChainID(suite.app.EvmKeeper.ChainID())
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
b.ReportAllocs()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
b.StopTimer()
|
||||||
|
tx, err := newSignedEthTx(templateDynamicFeeTx,
|
||||||
|
suite.app.EvmKeeper.GetNonce(suite.address),
|
||||||
|
sdk.AccAddress(suite.address.Bytes()),
|
||||||
|
suite.signer,
|
||||||
|
ethSigner,
|
||||||
|
)
|
||||||
|
require.NoError(b, err)
|
||||||
|
|
||||||
|
b.StartTimer()
|
||||||
|
resp, err := suite.app.EvmKeeper.ApplyTransaction(tx)
|
||||||
|
b.StopTimer()
|
||||||
|
|
||||||
|
require.NoError(b, err)
|
||||||
|
require.False(b, resp.Failed())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func BenchmarkApplyNativeMessage(b *testing.B) {
|
func BenchmarkApplyNativeMessage(b *testing.B) {
|
||||||
suite := KeeperTestSuite{}
|
suite := KeeperTestSuite{}
|
||||||
suite.DoSetupTest(b)
|
suite.DoSetupTest(b)
|
||||||
@ -176,7 +223,7 @@ func BenchmarkApplyNativeMessage(b *testing.B) {
|
|||||||
ethCfg,
|
ethCfg,
|
||||||
suite.signer,
|
suite.signer,
|
||||||
signer,
|
signer,
|
||||||
false,
|
ethtypes.AccessListTxType,
|
||||||
)
|
)
|
||||||
require.NoError(b, err)
|
require.NoError(b, err)
|
||||||
|
|
||||||
@ -209,7 +256,40 @@ func BenchmarkApplyNativeMessageWithLegacyTx(b *testing.B) {
|
|||||||
ethCfg,
|
ethCfg,
|
||||||
suite.signer,
|
suite.signer,
|
||||||
signer,
|
signer,
|
||||||
true,
|
ethtypes.LegacyTxType,
|
||||||
|
)
|
||||||
|
require.NoError(b, err)
|
||||||
|
|
||||||
|
b.StartTimer()
|
||||||
|
resp, err := suite.app.EvmKeeper.ApplyNativeMessage(m)
|
||||||
|
b.StopTimer()
|
||||||
|
|
||||||
|
require.NoError(b, err)
|
||||||
|
require.False(b, resp.Failed())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkApplyNativeMessageWithDynamicFeeTx(b *testing.B) {
|
||||||
|
suite := KeeperTestSuite{dynamicTxFee: true}
|
||||||
|
suite.DoSetupTest(b)
|
||||||
|
|
||||||
|
params := suite.app.EvmKeeper.GetParams(suite.ctx)
|
||||||
|
ethCfg := params.ChainConfig.EthereumConfig(suite.app.EvmKeeper.ChainID())
|
||||||
|
signer := ethtypes.LatestSignerForChainID(suite.app.EvmKeeper.ChainID())
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
b.ReportAllocs()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
b.StopTimer()
|
||||||
|
|
||||||
|
m, err := newNativeMessage(
|
||||||
|
suite.app.EvmKeeper.GetNonce(suite.address),
|
||||||
|
suite.ctx.BlockHeight(),
|
||||||
|
suite.address,
|
||||||
|
ethCfg,
|
||||||
|
suite.signer,
|
||||||
|
signer,
|
||||||
|
ethtypes.DynamicFeeTxType,
|
||||||
)
|
)
|
||||||
require.NoError(b, err)
|
require.NoError(b, err)
|
||||||
|
|
||||||
|
@ -563,6 +563,20 @@ func (suite *KeeperTestSuite) TestAddLog() {
|
|||||||
msg2, _ = tx2.GetMsgs()[0].(*types.MsgEthereumTx)
|
msg2, _ = tx2.GetMsgs()[0].(*types.MsgEthereumTx)
|
||||||
txHash2 := msg2.AsTransaction().Hash()
|
txHash2 := msg2.AsTransaction().Hash()
|
||||||
|
|
||||||
|
msg3 := types.NewTx(big.NewInt(1), 0, &suite.address, big.NewInt(1), 100000, nil, big.NewInt(1), big.NewInt(1), []byte("test"), nil)
|
||||||
|
msg3.From = addr.Hex()
|
||||||
|
|
||||||
|
tx3 := suite.CreateTestTx(msg3, privKey)
|
||||||
|
msg3, _ = tx3.GetMsgs()[0].(*types.MsgEthereumTx)
|
||||||
|
txHash3 := msg3.AsTransaction().Hash()
|
||||||
|
|
||||||
|
msg4 := types.NewTx(big.NewInt(1), 1, &suite.address, big.NewInt(1), 100000, nil, big.NewInt(1), big.NewInt(1), []byte("test"), nil)
|
||||||
|
msg4.From = addr.Hex()
|
||||||
|
|
||||||
|
tx4 := suite.CreateTestTx(msg4, privKey)
|
||||||
|
msg4, _ = tx4.GetMsgs()[0].(*types.MsgEthereumTx)
|
||||||
|
txHash4 := msg4.AsTransaction().Hash()
|
||||||
|
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
name string
|
name string
|
||||||
hash common.Hash
|
hash common.Hash
|
||||||
@ -601,6 +615,38 @@ func (suite *KeeperTestSuite) TestAddLog() {
|
|||||||
suite.app.EvmKeeper.IncreaseTxIndexTransient()
|
suite.app.EvmKeeper.IncreaseTxIndexTransient()
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"dynamicfee tx hash from message",
|
||||||
|
txHash3,
|
||||||
|
ðtypes.Log{
|
||||||
|
Address: addr,
|
||||||
|
},
|
||||||
|
ðtypes.Log{
|
||||||
|
Address: addr,
|
||||||
|
TxHash: txHash3,
|
||||||
|
},
|
||||||
|
func() {},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"log index keep increasing in new dynamicfee tx",
|
||||||
|
txHash4,
|
||||||
|
ðtypes.Log{
|
||||||
|
Address: addr,
|
||||||
|
},
|
||||||
|
ðtypes.Log{
|
||||||
|
Address: addr,
|
||||||
|
TxHash: txHash4,
|
||||||
|
TxIndex: 1,
|
||||||
|
Index: 1,
|
||||||
|
},
|
||||||
|
func() {
|
||||||
|
suite.app.EvmKeeper.SetTxHashTransient(txHash)
|
||||||
|
suite.app.EvmKeeper.AddLog(ðtypes.Log{
|
||||||
|
Address: addr,
|
||||||
|
})
|
||||||
|
suite.app.EvmKeeper.IncreaseTxIndexTransient()
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
|
@ -62,7 +62,12 @@ func (k Keeper) DeductTxCostsFromUserBalance(
|
|||||||
|
|
||||||
if london && !feeMktParams.NoBaseFee && txData.TxType() == ethtypes.DynamicFeeTxType {
|
if london && !feeMktParams.NoBaseFee && txData.TxType() == ethtypes.DynamicFeeTxType {
|
||||||
baseFee := k.feeMarketKeeper.GetBaseFee(ctx)
|
baseFee := k.feeMarketKeeper.GetBaseFee(ctx)
|
||||||
effectiveTip = cmath.BigMin(txData.GetGasTipCap(), new(big.Int).Sub(txData.GetGasFeeCap(), baseFee))
|
gasFeeGap := new(big.Int).Sub(txData.GetGasFeeCap(), baseFee)
|
||||||
|
if gasFeeGap.Sign() == -1 {
|
||||||
|
return nil, sdkerrors.Wrapf(sdkerrors.ErrInsufficientFee, "the tx gasfeecap is lower than the tx baseFee: %s (gasfeecap), %s (basefee) ", txData.GetGasFeeCap(), baseFee)
|
||||||
|
}
|
||||||
|
|
||||||
|
effectiveTip = cmath.BigMin(txData.GetGasTipCap(), gasFeeGap)
|
||||||
}
|
}
|
||||||
|
|
||||||
gasUsed := new(big.Int).SetUint64(txData.GetGas())
|
gasUsed := new(big.Int).SetUint64(txData.GetGas())
|
||||||
|
@ -5,7 +5,9 @@ import (
|
|||||||
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
cmath "github.com/ethereum/go-ethereum/common/math"
|
||||||
ethtypes "github.com/ethereum/go-ethereum/core/types"
|
ethtypes "github.com/ethereum/go-ethereum/core/types"
|
||||||
|
ethparams "github.com/ethereum/go-ethereum/params"
|
||||||
evmkeeper "github.com/tharsis/ethermint/x/evm/keeper"
|
evmkeeper "github.com/tharsis/ethermint/x/evm/keeper"
|
||||||
evmtypes "github.com/tharsis/ethermint/x/evm/types"
|
evmtypes "github.com/tharsis/ethermint/x/evm/types"
|
||||||
)
|
)
|
||||||
@ -23,10 +25,13 @@ func (suite *KeeperTestSuite) TestCheckSenderBalance() {
|
|||||||
to string
|
to string
|
||||||
gasLimit uint64
|
gasLimit uint64
|
||||||
gasPrice *sdk.Int
|
gasPrice *sdk.Int
|
||||||
|
gasFeeCap *big.Int
|
||||||
|
gasTipCap *big.Int
|
||||||
cost *sdk.Int
|
cost *sdk.Int
|
||||||
from string
|
from string
|
||||||
accessList *ethtypes.AccessList
|
accessList *ethtypes.AccessList
|
||||||
expectPass bool
|
expectPass bool
|
||||||
|
dynamicTxFee bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "Enough balance",
|
name: "Enough balance",
|
||||||
@ -108,6 +113,94 @@ func (suite *KeeperTestSuite) TestCheckSenderBalance() {
|
|||||||
accessList: ðtypes.AccessList{},
|
accessList: ðtypes.AccessList{},
|
||||||
expectPass: false,
|
expectPass: false,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "Enough balance w/ dynamicTxFee",
|
||||||
|
to: suite.address.String(),
|
||||||
|
gasLimit: 10,
|
||||||
|
gasFeeCap: big.NewInt(1),
|
||||||
|
cost: &oneInt,
|
||||||
|
from: suite.address.String(),
|
||||||
|
accessList: ðtypes.AccessList{},
|
||||||
|
expectPass: true,
|
||||||
|
dynamicTxFee: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Equal balance w/ dynamicTxFee",
|
||||||
|
to: suite.address.String(),
|
||||||
|
gasLimit: 99,
|
||||||
|
gasFeeCap: big.NewInt(1),
|
||||||
|
cost: &oneInt,
|
||||||
|
from: suite.address.String(),
|
||||||
|
accessList: ðtypes.AccessList{},
|
||||||
|
expectPass: true,
|
||||||
|
dynamicTxFee: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "negative cost w/ dynamicTxFee",
|
||||||
|
to: suite.address.String(),
|
||||||
|
gasLimit: 1,
|
||||||
|
gasFeeCap: big.NewInt(1),
|
||||||
|
cost: &negInt,
|
||||||
|
from: suite.address.String(),
|
||||||
|
accessList: ðtypes.AccessList{},
|
||||||
|
expectPass: false,
|
||||||
|
dynamicTxFee: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Higher gas limit, not enough balance w/ dynamicTxFee",
|
||||||
|
to: suite.address.String(),
|
||||||
|
gasLimit: 100,
|
||||||
|
gasFeeCap: big.NewInt(1),
|
||||||
|
cost: &oneInt,
|
||||||
|
from: suite.address.String(),
|
||||||
|
accessList: ðtypes.AccessList{},
|
||||||
|
expectPass: false,
|
||||||
|
dynamicTxFee: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Higher gas price, enough balance w/ dynamicTxFee",
|
||||||
|
to: suite.address.String(),
|
||||||
|
gasLimit: 10,
|
||||||
|
gasFeeCap: big.NewInt(5),
|
||||||
|
cost: &oneInt,
|
||||||
|
from: suite.address.String(),
|
||||||
|
accessList: ðtypes.AccessList{},
|
||||||
|
expectPass: true,
|
||||||
|
dynamicTxFee: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Higher gas price, not enough balance w/ dynamicTxFee",
|
||||||
|
to: suite.address.String(),
|
||||||
|
gasLimit: 20,
|
||||||
|
gasFeeCap: big.NewInt(5),
|
||||||
|
cost: &oneInt,
|
||||||
|
from: suite.address.String(),
|
||||||
|
accessList: ðtypes.AccessList{},
|
||||||
|
expectPass: false,
|
||||||
|
dynamicTxFee: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Higher cost, enough balance w/ dynamicTxFee",
|
||||||
|
to: suite.address.String(),
|
||||||
|
gasLimit: 10,
|
||||||
|
gasFeeCap: big.NewInt(5),
|
||||||
|
cost: &fiftyInt,
|
||||||
|
from: suite.address.String(),
|
||||||
|
accessList: ðtypes.AccessList{},
|
||||||
|
expectPass: true,
|
||||||
|
dynamicTxFee: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Higher cost, not enough balance w/ dynamicTxFee",
|
||||||
|
to: suite.address.String(),
|
||||||
|
gasLimit: 10,
|
||||||
|
gasFeeCap: big.NewInt(5),
|
||||||
|
cost: &hundredInt,
|
||||||
|
from: suite.address.String(),
|
||||||
|
accessList: ðtypes.AccessList{},
|
||||||
|
expectPass: false,
|
||||||
|
dynamicTxFee: true,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
suite.app.EvmKeeper.AddBalance(suite.address, hundredInt.BigInt())
|
suite.app.EvmKeeper.AddBalance(suite.address, hundredInt.BigInt())
|
||||||
@ -118,15 +211,25 @@ func (suite *KeeperTestSuite) TestCheckSenderBalance() {
|
|||||||
suite.Run(tc.name, func() {
|
suite.Run(tc.name, func() {
|
||||||
to := common.HexToAddress(tc.from)
|
to := common.HexToAddress(tc.from)
|
||||||
|
|
||||||
var amount, gasPrice *big.Int
|
var amount, gasPrice, gasFeeCap, gasTipCap *big.Int
|
||||||
if tc.cost != nil {
|
if tc.cost != nil {
|
||||||
amount = tc.cost.BigInt()
|
amount = tc.cost.BigInt()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if tc.dynamicTxFee {
|
||||||
|
gasFeeCap = tc.gasFeeCap
|
||||||
|
if tc.gasTipCap == nil {
|
||||||
|
gasTipCap = oneInt.BigInt()
|
||||||
|
} else {
|
||||||
|
gasTipCap = tc.gasTipCap
|
||||||
|
}
|
||||||
|
} else {
|
||||||
if tc.gasPrice != nil {
|
if tc.gasPrice != nil {
|
||||||
gasPrice = tc.gasPrice.BigInt()
|
gasPrice = tc.gasPrice.BigInt()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
tx := evmtypes.NewTx(zeroInt.BigInt(), 1, &to, amount, tc.gasLimit, gasPrice, nil, nil, nil, tc.accessList)
|
tx := evmtypes.NewTx(zeroInt.BigInt(), 1, &to, amount, tc.gasLimit, gasPrice, gasFeeCap, gasTipCap, nil, tc.accessList)
|
||||||
tx.From = tc.from
|
tx.From = tc.from
|
||||||
|
|
||||||
txData, _ := evmtypes.UnpackTxData(tx.Data)
|
txData, _ := evmtypes.UnpackTxData(tx.Data)
|
||||||
@ -154,14 +257,18 @@ func (suite *KeeperTestSuite) TestDeductTxCostsFromUserBalance() {
|
|||||||
oneInt := sdk.NewInt(1)
|
oneInt := sdk.NewInt(1)
|
||||||
fiveInt := sdk.NewInt(5)
|
fiveInt := sdk.NewInt(5)
|
||||||
fiftyInt := sdk.NewInt(50)
|
fiftyInt := sdk.NewInt(50)
|
||||||
|
hundredBaseFeeInt := sdk.NewInt(ethparams.InitialBaseFee * 100)
|
||||||
|
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
name string
|
name string
|
||||||
gasLimit uint64
|
gasLimit uint64
|
||||||
gasPrice *sdk.Int
|
gasPrice *sdk.Int
|
||||||
|
gasFeeCap *big.Int
|
||||||
|
gasTipCap *big.Int
|
||||||
cost *sdk.Int
|
cost *sdk.Int
|
||||||
accessList *ethtypes.AccessList
|
accessList *ethtypes.AccessList
|
||||||
expectPass bool
|
expectPass bool
|
||||||
|
dynamicTxFee bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "Enough balance",
|
name: "Enough balance",
|
||||||
@ -213,24 +320,82 @@ func (suite *KeeperTestSuite) TestDeductTxCostsFromUserBalance() {
|
|||||||
accessList: ðtypes.AccessList{},
|
accessList: ðtypes.AccessList{},
|
||||||
expectPass: true,
|
expectPass: true,
|
||||||
},
|
},
|
||||||
|
// testcases with dynamicTxFee enabled.
|
||||||
|
{
|
||||||
|
name: "Invalid gasFeeCap w/ dynamicTxFee",
|
||||||
|
gasLimit: 10,
|
||||||
|
gasFeeCap: big.NewInt(1),
|
||||||
|
gasTipCap: big.NewInt(1),
|
||||||
|
cost: &oneInt,
|
||||||
|
accessList: ðtypes.AccessList{},
|
||||||
|
expectPass: false,
|
||||||
|
dynamicTxFee: true,
|
||||||
|
},
|
||||||
|
// TODO: is this case valid?
|
||||||
|
{
|
||||||
|
name: "empty fee failed to deduct",
|
||||||
|
gasLimit: 10,
|
||||||
|
gasFeeCap: big.NewInt(ethparams.InitialBaseFee),
|
||||||
|
gasTipCap: big.NewInt(1),
|
||||||
|
cost: &oneInt,
|
||||||
|
accessList: ðtypes.AccessList{},
|
||||||
|
expectPass: false,
|
||||||
|
dynamicTxFee: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "effectiveTip equal to gasTipCap",
|
||||||
|
gasLimit: 100,
|
||||||
|
gasFeeCap: big.NewInt(ethparams.InitialBaseFee + 2),
|
||||||
|
cost: &oneInt,
|
||||||
|
accessList: ðtypes.AccessList{},
|
||||||
|
expectPass: true,
|
||||||
|
dynamicTxFee: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "effectiveTip equal to (gasFeeCap - baseFee)",
|
||||||
|
gasLimit: 105,
|
||||||
|
gasFeeCap: big.NewInt(ethparams.InitialBaseFee + 1),
|
||||||
|
gasTipCap: big.NewInt(2),
|
||||||
|
cost: &oneInt,
|
||||||
|
accessList: ðtypes.AccessList{},
|
||||||
|
expectPass: true,
|
||||||
|
dynamicTxFee: true,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, tc := range testCases {
|
for i, tc := range testCases {
|
||||||
suite.Run(tc.name, func() {
|
suite.Run(tc.name, func() {
|
||||||
|
suite.dynamicTxFee = tc.dynamicTxFee
|
||||||
suite.SetupTest()
|
suite.SetupTest()
|
||||||
suite.app.EvmKeeper.AddBalance(suite.address, hundredInt.BigInt())
|
|
||||||
balance := suite.app.EvmKeeper.GetBalance(suite.address)
|
|
||||||
suite.Require().Equal(balance, hundredInt.BigInt())
|
|
||||||
|
|
||||||
var amount, gasPrice *big.Int
|
var amount, gasPrice, gasFeeCap, gasTipCap *big.Int
|
||||||
if tc.cost != nil {
|
if tc.cost != nil {
|
||||||
amount = tc.cost.BigInt()
|
amount = tc.cost.BigInt()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if suite.dynamicTxFee {
|
||||||
|
if tc.gasFeeCap != nil {
|
||||||
|
gasFeeCap = tc.gasFeeCap
|
||||||
|
}
|
||||||
|
if tc.gasTipCap == nil {
|
||||||
|
gasTipCap = oneInt.BigInt()
|
||||||
|
} else {
|
||||||
|
gasTipCap = tc.gasTipCap
|
||||||
|
}
|
||||||
|
suite.app.EvmKeeper.AddBalance(suite.address, hundredBaseFeeInt.BigInt())
|
||||||
|
balance := suite.app.EvmKeeper.GetBalance(suite.address)
|
||||||
|
suite.Require().Equal(balance, hundredBaseFeeInt.BigInt())
|
||||||
|
} else {
|
||||||
if tc.gasPrice != nil {
|
if tc.gasPrice != nil {
|
||||||
gasPrice = tc.gasPrice.BigInt()
|
gasPrice = tc.gasPrice.BigInt()
|
||||||
}
|
}
|
||||||
|
|
||||||
tx := evmtypes.NewTx(zeroInt.BigInt(), 1, &suite.address, amount, tc.gasLimit, gasPrice, nil, nil, nil, tc.accessList)
|
suite.app.EvmKeeper.AddBalance(suite.address, hundredInt.BigInt())
|
||||||
|
balance := suite.app.EvmKeeper.GetBalance(suite.address)
|
||||||
|
suite.Require().Equal(balance, hundredInt.BigInt())
|
||||||
|
}
|
||||||
|
|
||||||
|
tx := evmtypes.NewTx(zeroInt.BigInt(), 1, &suite.address, amount, tc.gasLimit, gasPrice, gasFeeCap, gasTipCap, nil, tc.accessList)
|
||||||
tx.From = suite.address.String()
|
tx.From = suite.address.String()
|
||||||
|
|
||||||
txData, _ := evmtypes.UnpackTxData(tx.Data)
|
txData, _ := evmtypes.UnpackTxData(tx.Data)
|
||||||
@ -242,11 +407,24 @@ func (suite *KeeperTestSuite) TestDeductTxCostsFromUserBalance() {
|
|||||||
evmtypes.DefaultEVMDenom,
|
evmtypes.DefaultEVMDenom,
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
false, // london
|
suite.dynamicTxFee, // london
|
||||||
)
|
)
|
||||||
|
|
||||||
if tc.expectPass {
|
if tc.expectPass {
|
||||||
suite.Require().NoError(err, "valid test %d failed", i)
|
suite.Require().NoError(err, "valid test %d failed", i)
|
||||||
|
if tc.dynamicTxFee {
|
||||||
|
baseFee := suite.app.FeeMarketKeeper.GetBaseFee(suite.ctx)
|
||||||
|
gasFeeGap := new(big.Int).Sub(txData.GetGasFeeCap(), baseFee)
|
||||||
|
effectiveTip := cmath.BigMin(txData.GetGasTipCap(), gasFeeGap)
|
||||||
|
|
||||||
|
suite.Require().Equal(
|
||||||
|
fees,
|
||||||
|
sdk.NewCoins(
|
||||||
|
sdk.NewCoin(evmtypes.DefaultEVMDenom, sdk.NewIntFromBigInt(effectiveTip).Mul(sdk.NewIntFromUint64(tc.gasLimit))),
|
||||||
|
),
|
||||||
|
"valid test %d failed, fee value is wrong ", i,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
suite.Require().Equal(
|
suite.Require().Equal(
|
||||||
fees,
|
fees,
|
||||||
sdk.NewCoins(
|
sdk.NewCoins(
|
||||||
@ -254,11 +432,12 @@ func (suite *KeeperTestSuite) TestDeductTxCostsFromUserBalance() {
|
|||||||
),
|
),
|
||||||
"valid test %d failed, fee value is wrong ", i,
|
"valid test %d failed, fee value is wrong ", i,
|
||||||
)
|
)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
suite.Require().Error(err, "invalid test %d passed", i)
|
suite.Require().Error(err, "invalid test %d passed", i)
|
||||||
suite.Require().Nil(fees, "invalid test %d passed. fees value must be nil", i)
|
suite.Require().Nil(fees, "invalid test %d passed. fees value must be nil", i)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
suite.dynamicTxFee = false // reset flag
|
||||||
}
|
}
|
||||||
|
@ -94,7 +94,7 @@ func newMsgEthereumTx(
|
|||||||
}
|
}
|
||||||
case accesses != nil && gasFeeCap != nil && gasTipCap != nil:
|
case accesses != nil && gasFeeCap != nil && gasTipCap != nil:
|
||||||
gtc := sdk.NewIntFromBigInt(gasTipCap)
|
gtc := sdk.NewIntFromBigInt(gasTipCap)
|
||||||
gfc := sdk.NewIntFromBigInt(gasTipCap)
|
gfc := sdk.NewIntFromBigInt(gasFeeCap)
|
||||||
|
|
||||||
txData = &DynamicFeeTx{
|
txData = &DynamicFeeTx{
|
||||||
ChainID: cid,
|
ChainID: cid,
|
||||||
|
Loading…
Reference in New Issue
Block a user