diff --git a/contract/bridge_fee_quote.go b/contract/bridge_fee_quote.go index 4c3d52f6..6ab90c42 100644 --- a/contract/bridge_fee_quote.go +++ b/contract/bridge_fee_quote.go @@ -62,6 +62,12 @@ func (k BridgeFeeQuoteKeeper) GetDefaultOracleQuote(ctx context.Context, chainNa if err := k.QueryContract(sdk.UnwrapSDKContext(ctx), k.from, k.contract, k.abi, "getDefaultOracleQuote", &res, chainName, tokenName); err != nil { return nil, err } + for i := 0; i < len(res.Quotes); i++ { + if res.Quotes[i].Id.Sign() <= 0 { + res.Quotes = append(res.Quotes[:i], res.Quotes[i+1:]...) + i-- + } + } return res.Quotes, nil } diff --git a/contract/bridge_fee_quote_test.go b/contract/bridge_fee_quote_test.go new file mode 100644 index 00000000..6fbde0fc --- /dev/null +++ b/contract/bridge_fee_quote_test.go @@ -0,0 +1,20 @@ +package contract_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/pundiai/fx-core/v8/contract" + "github.com/pundiai/fx-core/v8/testutil/helpers" + fxtypes "github.com/pundiai/fx-core/v8/types" + ethtypes "github.com/pundiai/fx-core/v8/x/eth/types" +) + +func TestBridgeFeeQuoteKeeper_GetDefaultOracleQuote(t *testing.T) { + app, ctx := helpers.NewAppWithValNumber(t, 1) + keeper := contract.NewBridgeFeeQuoteKeeper(app.EvmKeeper) + quote, err := keeper.GetDefaultOracleQuote(ctx, contract.MustStrToByte32(ethtypes.ModuleName), contract.MustStrToByte32(fxtypes.DefaultDenom)) + require.NoError(t, err) + require.Empty(t, quote) +} diff --git a/precompiles/crosschain/contract_test.go b/precompiles/crosschain/contract_test.go index 015aaa83..de130257 100644 --- a/precompiles/crosschain/contract_test.go +++ b/precompiles/crosschain/contract_test.go @@ -26,6 +26,7 @@ type CrosschainPrecompileTestSuite struct { chainName string helpers.CrosschainPrecompileSuite + bridgeFeeSuite helpers.BridgeFeeSuite } func TestCrosschainPrecompileTestSuite(t *testing.T) { @@ -54,6 +55,7 @@ func (suite *CrosschainPrecompileTestSuite) SetupTest() { } suite.CrosschainPrecompileSuite = helpers.NewCrosschainPrecompileSuite(suite.Require(), suite.signer, suite.App.EvmKeeper, suite.crosschainAddr) + suite.bridgeFeeSuite = helpers.NewBridgeFeeSuite(suite.Require(), suite.App.EvmKeeper) chainNames := fxtypes.GetSupportChains() suite.chainName = chainNames[tmrand.Intn(len(chainNames))] diff --git a/precompiles/crosschain/crosschain_test.go b/precompiles/crosschain/crosschain_test.go index 764a06ef..9f3e9e67 100644 --- a/precompiles/crosschain/crosschain_test.go +++ b/precompiles/crosschain/crosschain_test.go @@ -3,6 +3,7 @@ package crosschain_test import ( "math/big" "testing" + "time" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" "github.com/ethereum/go-ethereum/common" @@ -30,6 +31,15 @@ func (suite *CrosschainPrecompileTestSuite) TestContract_Crosschain() { suite.App.CrosschainKeepers.GetKeeper(suite.chainName). SetLastObservedBlockHeight(suite.Ctx, 100, 100) + suite.bridgeFeeSuite.Quote(suite.Ctx, contract.IBridgeFeeQuoteQuoteInput{ + Cap: 0, + GasLimit: 21000, + Expiry: uint64(time.Now().Add(time.Hour).Unix()), + ChainName: contract.MustStrToByte32(suite.chainName), + TokenName: contract.MustStrToByte32(fxtypes.DefaultDenom), + Amount: big.NewInt(1), + }) + balance := suite.Balance(suite.signer.AccAddress()) txResponse := suite.Crosschain(suite.Ctx, big.NewInt(2), suite.signer.Address(), diff --git a/testutil/helpers/bridge_fee_suite.go b/testutil/helpers/bridge_fee_suite.go new file mode 100644 index 00000000..c7c18fd1 --- /dev/null +++ b/testutil/helpers/bridge_fee_suite.go @@ -0,0 +1,49 @@ +package helpers + +import ( + "context" + + evmtypes "github.com/evmos/ethermint/x/evm/types" + "github.com/stretchr/testify/require" + + "github.com/pundiai/fx-core/v8/contract" +) + +type BridgeFeeSuite struct { + require *require.Assertions + err error + + contract.BridgeFeeQuoteKeeper + contract.BridgeFeeOracleKeeper +} + +func NewBridgeFeeSuite(require *require.Assertions, caller contract.Caller) BridgeFeeSuite { + return BridgeFeeSuite{ + require: require, + BridgeFeeQuoteKeeper: contract.NewBridgeFeeQuoteKeeper(caller), + BridgeFeeOracleKeeper: contract.NewBridgeFeeOracleKeeper(caller), + } +} + +func (s BridgeFeeSuite) WithError(err error) BridgeFeeSuite { + suite := s + suite.err = err + return suite +} + +func (s BridgeFeeSuite) requireError(err error) { + if s.err != nil { + s.require.ErrorIs(err, evmtypes.ErrVMExecution.Wrap(s.err.Error())) + return + } + s.require.NoError(err) +} + +func (s BridgeFeeSuite) Quote(ctx context.Context, args contract.IBridgeFeeQuoteQuoteInput) *evmtypes.MsgEthereumTxResponse { + defOracle, err := s.BridgeFeeOracleKeeper.DefaultOracle(ctx) + s.require.NoError(err) + + res, err := s.BridgeFeeQuoteKeeper.Quote(ctx, defOracle, []contract.IBridgeFeeQuoteQuoteInput{args}) + s.requireError(err) + return res +} diff --git a/x/crosschain/keeper/outgoing_tx.go b/x/crosschain/keeper/outgoing_tx.go index cdfb8703..9a7f1ad9 100644 --- a/x/crosschain/keeper/outgoing_tx.go +++ b/x/crosschain/keeper/outgoing_tx.go @@ -23,12 +23,15 @@ func (k Keeper) BuildOutgoingTxBatch(ctx sdk.Context, caller contract.Caller, se } var quoteInfo *contract.IBridgeFeeQuoteQuoteInfo for _, quote := range quoteInfos { + if quote.Id.Sign() <= 0 { + continue + } if fee.Amount.GTE(sdkmath.NewIntFromBigInt(quote.Amount)) && !quote.IsTimeout(ctx.BlockTime()) { quoteInfo = "e break } } - if quoteInfo == nil { + if quoteInfo == nil || quoteInfo.Id.Sign() <= 0 { return 0, types.ErrInvalid.Wrapf("bridge fee is too small or expired") }