diff --git a/ante/ante.go b/ante/ante.go deleted file mode 100644 index 5183bdf..0000000 --- a/ante/ante.go +++ /dev/null @@ -1,58 +0,0 @@ -package ante - -import ( - ibcante "github.com/cosmos/ibc-go/v7/modules/core/ante" - ibckeeper "github.com/cosmos/ibc-go/v7/modules/core/keeper" - - sdkerrors "cosmossdk.io/errors" - - sdk "github.com/cosmos/cosmos-sdk/types" - errorstypes "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/cosmos/cosmos-sdk/x/auth/ante" -) - -// HandlerOptions extend the SDK's AnteHandler options by requiring the IBC -// channel keeper. -type HandlerOptions struct { - ante.HandlerOptions - - IBCkeeper *ibckeeper.Keeper - BypassMinFeeMsgTypes []string -} - -func NewAnteHandler(opts HandlerOptions) (sdk.AnteHandler, error) { - if opts.AccountKeeper == nil { - return nil, sdkerrors.Wrap(errorstypes.ErrLogic, "account keeper is required for AnteHandler") - } - if opts.BankKeeper == nil { - return nil, sdkerrors.Wrap(errorstypes.ErrLogic, "bank keeper is required for AnteHandler") - } - if opts.SignModeHandler == nil { - return nil, sdkerrors.Wrap(errorstypes.ErrLogic, "sign mode handler is required for ante builder") - } - - sigGasConsumer := opts.SigGasConsumer - if sigGasConsumer == nil { - sigGasConsumer = ante.DefaultSigVerificationGasConsumer - } - - anteDecorators := []sdk.AnteDecorator{ - ante.NewSetUpContextDecorator(), - ante.RejectExtensionOptionsDecorator{}, - NewMempoolFeeDecorator(opts.BypassMinFeeMsgTypes), - ante.NewValidateBasicDecorator(), - ante.NewTxTimeoutHeightDecorator(), - ante.NewValidateMemoDecorator(opts.AccountKeeper), - ante.NewConsumeGasForTxSizeDecorator(opts.AccountKeeper), - ante.NewDeductFeeDecorator(opts.AccountKeeper, opts.BankKeeper, opts.FeegrantKeeper, opts.TxFeeChecker), - // SetPubKeyDecorator must be called before all signature verification decorators - ante.NewSetPubKeyDecorator(opts.AccountKeeper), - ante.NewValidateSigCountDecorator(opts.AccountKeeper), - ante.NewSigGasConsumeDecorator(opts.AccountKeeper, sigGasConsumer), - ante.NewSigVerificationDecorator(opts.AccountKeeper, opts.SignModeHandler), - ante.NewIncrementSequenceDecorator(opts.AccountKeeper), - ibcante.NewRedundantRelayDecorator(opts.IBCkeeper), - } - - return sdk.ChainAnteDecorators(anteDecorators...), nil -} diff --git a/ante/ante_test.go b/ante/ante_test.go deleted file mode 100644 index 5ac968c..0000000 --- a/ante/ante_test.go +++ /dev/null @@ -1,101 +0,0 @@ -package ante_test - -import ( - "fmt" - "testing" - - "github.com/stretchr/testify/suite" - - "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/client/tx" - cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" - "github.com/cosmos/cosmos-sdk/testutil/testdata" - sdk "github.com/cosmos/cosmos-sdk/types" - moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil" - "github.com/cosmos/cosmos-sdk/types/tx/signing" - xauthsigning "github.com/cosmos/cosmos-sdk/x/auth/signing" - - tmrand "github.com/cometbft/cometbft/libs/rand" - tmproto "github.com/cometbft/cometbft/proto/tendermint/types" - - "github.com/osmosis-labs/fee-abstraction/v7/app" - apphelpers "github.com/osmosis-labs/fee-abstraction/v7/app/helpers" -) - -type IntegrationTestSuite struct { - suite.Suite - - app *app.FeeAbs - // anteHandler sdk.AnteHandler - ctx sdk.Context - clientCtx client.Context - txBuilder client.TxBuilder -} - -func TestIntegrationTestSuite(t *testing.T) { - suite.Run(t, new(IntegrationTestSuite)) -} - -func (s *IntegrationTestSuite) SetupTest() { - feeapp := apphelpers.Setup(s.T(), false, 1) - ctx := feeapp.BaseApp.NewContext(false, tmproto.Header{ - ChainID: fmt.Sprintf("test-chain-%s", tmrand.Str(4)), - Height: 1, - }) - - encodingConfig := moduletestutil.MakeTestEncodingConfig() - encodingConfig.Amino.RegisterConcrete(&testdata.TestMsg{}, "testdata.TestMsg", nil) - testdata.RegisterInterfaces(encodingConfig.InterfaceRegistry) - - s.app = feeapp - s.ctx = ctx - s.clientCtx = client.Context{}.WithTxConfig(encodingConfig.TxConfig) -} - -func (s *IntegrationTestSuite) CreateTestTx(privs []cryptotypes.PrivKey, accNums []uint64, accSeqs []uint64, chainID string) (xauthsigning.Tx, error) { - var sigsV2 []signing.SignatureV2 - for i, priv := range privs { - sigV2 := signing.SignatureV2{ - PubKey: priv.PubKey(), - Data: &signing.SingleSignatureData{ - SignMode: s.clientCtx.TxConfig.SignModeHandler().DefaultMode(), - Signature: nil, - }, - Sequence: accSeqs[i], - } - - sigsV2 = append(sigsV2, sigV2) - } - - if err := s.txBuilder.SetSignatures(sigsV2...); err != nil { - return nil, err - } - - sigsV2 = []signing.SignatureV2{} - for i, priv := range privs { - signerData := xauthsigning.SignerData{ - ChainID: chainID, - AccountNumber: accNums[i], - Sequence: accSeqs[i], - } - sigV2, err := tx.SignWithPrivKey( - s.clientCtx.TxConfig.SignModeHandler().DefaultMode(), - signerData, - s.txBuilder, - priv, - s.clientCtx.TxConfig, - accSeqs[i], - ) - if err != nil { - return nil, err - } - - sigsV2 = append(sigsV2, sigV2) - } - - if err := s.txBuilder.SetSignatures(sigsV2...); err != nil { - return nil, err - } - - return s.txBuilder.GetTx(), nil -} diff --git a/ante/fee.go b/ante/fee.go deleted file mode 100644 index 7014f15..0000000 --- a/ante/fee.go +++ /dev/null @@ -1,78 +0,0 @@ -package ante - -import ( - sdkerrors "cosmossdk.io/errors" - - sdk "github.com/cosmos/cosmos-sdk/types" - errorstypes "github.com/cosmos/cosmos-sdk/types/errors" - - tmstrings "github.com/cometbft/cometbft/libs/strings" -) - -const maxBypassMinFeeMsgGasUsage = uint64(200_000) - -// MempoolFeeDecorator will check if the transaction's fee is at least as large -// as the local validator's minimum gasFee (defined in validator config). -// -// If fee is too low, decorator returns error and tx is rejected from mempool. -// Note this only applies when ctx.CheckTx = true. If fee is high enough or not -// CheckTx, then call next AnteHandler. -// -// CONTRACT: Tx must implement FeeTx to use MempoolFeeDecorator -type MempoolFeeDecorator struct { - BypassMinFeeMsgTypes []string -} - -func NewMempoolFeeDecorator(bypassMsgTypes []string) MempoolFeeDecorator { - return MempoolFeeDecorator{ - BypassMinFeeMsgTypes: bypassMsgTypes, - } -} - -func (mfd MempoolFeeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { - feeTx, ok := tx.(sdk.FeeTx) - if !ok { - return ctx, sdkerrors.Wrap(errorstypes.ErrTxDecode, "Tx must be a FeeTx") - } - - feeCoins := feeTx.GetFee() - gas := feeTx.GetGas() - msgs := feeTx.GetMsgs() - - // Only check for minimum fees if the execution mode is CheckTx and the tx does - // not contain operator configured bypass messages. If the tx does contain - // operator configured bypass messages only, it's total gas must be less than - // or equal to a constant, otherwise minimum fees are checked to prevent spam. - if ctx.IsCheckTx() && !simulate && !(mfd.bypassMinFeeMsgs(msgs) && gas <= uint64(len(msgs))*maxBypassMinFeeMsgGasUsage) { - minGasPrices := ctx.MinGasPrices() - if !minGasPrices.IsZero() { - requiredFees := make(sdk.Coins, len(minGasPrices)) - - // Determine the required fees by multiplying each required minimum gas - // price by the gas limit, where fee = ceil(minGasPrice * gasLimit). - glDec := sdk.NewDec(int64(gas)) - for i, gp := range minGasPrices { - fee := gp.Amount.Mul(glDec) - requiredFees[i] = sdk.NewCoin(gp.Denom, fee.Ceil().RoundInt()) - } - - if !feeCoins.IsAnyGTE(requiredFees) { - return ctx, sdkerrors.Wrapf(errorstypes.ErrInsufficientFee, "insufficient fees; got: %s required: %s", feeCoins, requiredFees) - } - } - } - - return next(ctx, tx, simulate) -} - -func (mfd MempoolFeeDecorator) bypassMinFeeMsgs(msgs []sdk.Msg) bool { - for _, msg := range msgs { - if tmstrings.StringInSlice(sdk.MsgTypeURL(msg), mfd.BypassMinFeeMsgTypes) { - continue - } - - return false - } - - return true -} diff --git a/ante/fee_test.go b/ante/fee_test.go deleted file mode 100644 index d3b9a69..0000000 --- a/ante/fee_test.go +++ /dev/null @@ -1,61 +0,0 @@ -package ante_test - -import ( - ibcclienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" - ibcchanneltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" - - cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" - "github.com/cosmos/cosmos-sdk/testutil/testdata" - sdk "github.com/cosmos/cosmos-sdk/types" - - "github.com/osmosis-labs/fee-abstraction/v7/ante" -) - -func (s *IntegrationTestSuite) TestMempoolFeeDecorator() { - s.SetupTest() - s.txBuilder = s.clientCtx.TxConfig.NewTxBuilder() - - mfd := ante.NewMempoolFeeDecorator([]string{ - sdk.MsgTypeURL(&ibcchanneltypes.MsgRecvPacket{}), - sdk.MsgTypeURL(&ibcchanneltypes.MsgAcknowledgement{}), - sdk.MsgTypeURL(&ibcclienttypes.MsgUpdateClient{}), - }) - antehandler := sdk.ChainAnteDecorators(mfd) - priv1, _, addr1 := testdata.KeyTestPubAddr() - - msg := testdata.NewTestMsg(addr1) - feeAmount := testdata.NewTestFeeAmount() - gasLimit := testdata.NewTestGasLimit() - s.Require().NoError(s.txBuilder.SetMsgs(msg)) - s.txBuilder.SetFeeAmount(feeAmount) - s.txBuilder.SetGasLimit(gasLimit) - - privs, accNums, accSeqs := []cryptotypes.PrivKey{priv1}, []uint64{0}, []uint64{0} - tx, err := s.CreateTestTx(privs, accNums, accSeqs, s.ctx.ChainID()) - s.Require().NoError(err) - - // Set high gas price so standard test fee fails - feeAmt := sdk.NewDecCoinFromDec("uatom", sdk.NewDec(200).Quo(sdk.NewDec(100000))) - minGasPrice := []sdk.DecCoin{feeAmt} - s.ctx = s.ctx.WithMinGasPrices(minGasPrice).WithIsCheckTx(true) - - // antehandler errors with insufficient fees - _, err = antehandler(s.ctx, tx, false) - s.Require().Error(err, "expected error due to low fee") - - // ensure no fees for certain IBC msgs - s.Require().NoError(s.txBuilder.SetMsgs( - ibcchanneltypes.NewMsgRecvPacket(ibcchanneltypes.Packet{}, nil, ibcclienttypes.Height{}, ""), - )) - - oracleTx, err := s.CreateTestTx(privs, accNums, accSeqs, s.ctx.ChainID()) - s.Require().NoError(err) - _, err = antehandler(s.ctx, oracleTx, false) - s.Require().NoError(err, "expected min fee bypass for IBC messages") - - s.ctx = s.ctx.WithIsCheckTx(false) - - // antehandler should not error since we do not check min gas prices in DeliverTx - _, err = antehandler(s.ctx, tx, false) - s.Require().NoError(err, "unexpected error during DeliverTx") -} diff --git a/tests/interchaintest/bytecode/crosschain_registry.wasm b/tests/interchaintest/bytecode/crosschain_registry.wasm index 2a648d6..cb6a87b 100644 Binary files a/tests/interchaintest/bytecode/crosschain_registry.wasm and b/tests/interchaintest/bytecode/crosschain_registry.wasm differ diff --git a/tests/interchaintest/bytecode/crosschain_swaps.wasm b/tests/interchaintest/bytecode/crosschain_swaps.wasm index bea2852..acffbaa 100644 Binary files a/tests/interchaintest/bytecode/crosschain_swaps.wasm and b/tests/interchaintest/bytecode/crosschain_swaps.wasm differ diff --git a/tests/interchaintest/bytecode/swaprouter.wasm b/tests/interchaintest/bytecode/swaprouter.wasm index 124d031..15c43ed 100644 Binary files a/tests/interchaintest/bytecode/swaprouter.wasm and b/tests/interchaintest/bytecode/swaprouter.wasm differ diff --git a/tests/interchaintest/feeabs_test.go b/tests/interchaintest/feeabs_test.go index b0b77dc..464a803 100644 --- a/tests/interchaintest/feeabs_test.go +++ b/tests/interchaintest/feeabs_test.go @@ -7,7 +7,6 @@ import ( "path" "testing" - "cosmossdk.io/math" sdktypes "github.com/cosmos/cosmos-sdk/types" paramsutils "github.com/cosmos/cosmos-sdk/x/params/client/utils" transfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" @@ -31,17 +30,15 @@ func TestFeeAbs(t *testing.T) { channFeeabsOsmosis, channOsmosisFeeabs, channFeeabsGaia, channGaiaFeeabs, channOsmosisGaia, channGaiaOsmosis := channels[0], channels[1], channels[2], channels[3], channels[4], channels[5] - // Setup contract on Osmosis - // Store code crosschain Registry - crossChainRegistryContractID, err := osmosis.StoreContract(ctx, osmosisUser.KeyName(), "./bytecode/crosschain_registry.wasm") + contracts, err := SetupOsmosisContracts(t, ctx, osmosis, osmosisUser) require.NoError(t, err) - _ = crossChainRegistryContractID - // // Instatiate - owner := sdktypes.MustBech32ifyAddressBytes(osmosis.Config().Bech32Prefix, osmosisUser.Address()) - initMsg := fmt.Sprintf("{\"owner\":\"%s\"}", owner) - registryContractAddress, err := osmosis.InstantiateContract(ctx, osmosisUser.KeyName(), crossChainRegistryContractID, initMsg, true) - require.NoError(t, err) - // Execute + require.Equal(t, len(contracts), 3) + + registryContractAddr := contracts[0] + swapRouterContractAddr := contracts[1] + _ = contracts[2] + + // Modify chain channel links on registry contract msg := fmt.Sprintf("{\"modify_chain_channel_links\": {\"operations\": [{\"operation\": \"set\",\"source_chain\": \"feeabs\",\"destination_chain\": \"osmosis\",\"channel_id\": \"%s\"},{\"operation\": \"set\",\"source_chain\": \"osmosis\",\"destination_chain\": \"feeabs\",\"channel_id\": \"%s\"},{\"operation\": \"set\",\"source_chain\": \"feeabs\",\"destination_chain\": \"gaia\",\"channel_id\": \"%s\"},{\"operation\": \"set\",\"source_chain\": \"gaia\",\"destination_chain\": \"feeabs\",\"channel_id\": \"%s\"},{\"operation\": \"set\",\"source_chain\": \"osmosis\",\"destination_chain\": \"gaia\",\"channel_id\": \"%s\"},{\"operation\": \"set\",\"source_chain\": \"gaia\",\"destination_chain\": \"osmosis\",\"channel_id\": \"%s\"}]}}", channFeeabsOsmosis.ChannelID, channOsmosisFeeabs.ChannelID, @@ -49,9 +46,10 @@ func TestFeeAbs(t *testing.T) { channGaiaFeeabs.ChannelID, channOsmosisGaia.ChannelID, channGaiaOsmosis.ChannelID) - _, err = osmosis.ExecuteContract(ctx, osmosisUser.KeyName(), registryContractAddress, msg) + _, err = osmosis.ExecuteContract(ctx, osmosisUser.KeyName(), registryContractAddr, msg) require.NoError(t, err) - // Execute + + // Modify bech32 prefixes on registry contract msg = `{ "modify_bech32_prefixes": { @@ -63,65 +61,74 @@ func TestFeeAbs(t *testing.T) { ] } }` - _, err = osmosis.ExecuteContract(ctx, osmosisUser.KeyName(), registryContractAddress, msg) + _, err = osmosis.ExecuteContract(ctx, osmosisUser.KeyName(), registryContractAddr, msg) require.NoError(t, err) - // Create pool Osmosis(uatom)/Osmosis(stake) on Osmosis - denomTrace := transfertypes.ParseDenomTrace(transfertypes.GetPrefixedDenom(channOsmosisGaia.PortID, channOsmosisGaia.ChannelID, gaia.Config().Denom)) - uatomOnOsmosis := denomTrace.IBCDenom() - osmosisUserBalance, err := osmosis.GetBalance(ctx, sdktypes.MustBech32ifyAddressBytes(osmosis.Config().Bech32Prefix, osmosisUser.Address()), uatomOnOsmosis) - require.NoError(t, err) - require.Equal(t, amountToSend, osmosisUserBalance) + osmosisPrefix := osmosis.Config().Bech32Prefix - denomTrace = transfertypes.ParseDenomTrace(transfertypes.GetPrefixedDenom(channOsmosisFeeabs.PortID, channOsmosisFeeabs.ChannelID, feeabs.Config().Denom)) - stakeOnOsmosis := denomTrace.IBCDenom() - osmosisUserBalance, err = osmosis.GetBalance(ctx, sdktypes.MustBech32ifyAddressBytes(osmosis.Config().Bech32Prefix, osmosisUser.Address()), stakeOnOsmosis) + uatomOnOsmosis := transfertypes.ParseDenomTrace(transfertypes.GetPrefixedDenom(channOsmosisGaia.PortID, channOsmosisGaia.ChannelID, gaia.Config().Denom)).IBCDenom() + osmosisUserBalance, err := osmosis.GetBalance( + ctx, + sdktypes.MustBech32ifyAddressBytes(osmosisPrefix, osmosisUser.Address()), + uatomOnOsmosis, + ) require.NoError(t, err) require.Equal(t, amountToSend, osmosisUserBalance) - poolID, err := feeabsCli.CreatePool(osmosis, ctx, osmosisUser.KeyName(), cosmos.OsmosisPoolParams{ - Weights: fmt.Sprintf("5%s,5%s", stakeOnOsmosis, uatomOnOsmosis), - InitialDeposit: fmt.Sprintf("95000000%s,950000000%s", stakeOnOsmosis, uatomOnOsmosis), - SwapFee: "0.01", - ExitFee: "0", - FutureGovernor: "", - }) + stakeOnOsmosis := transfertypes.ParseDenomTrace(transfertypes.GetPrefixedDenom(channOsmosisFeeabs.PortID, channOsmosisFeeabs.ChannelID, feeabs.Config().Denom)).IBCDenom() + osmosisUserBalance, err = osmosis.GetBalance( + ctx, + sdktypes.MustBech32ifyAddressBytes(osmosisPrefix, osmosisUser.Address()), + stakeOnOsmosis, + ) require.NoError(t, err) - require.Equal(t, poolID, "1") + require.Equal(t, amountToSend, osmosisUserBalance) // Setup propose_pfm // propose_pfm for feeabs - _, err = feeabsCli.SetupProposePFM(osmosis, ctx, osmosisUser.KeyName(), registryContractAddress, `{"propose_pfm":{"chain": "feeabs"}}`, stakeOnOsmosis) + _, err = feeabsCli.SetupProposePFM(osmosis, ctx, osmosisUser.KeyName(), registryContractAddr, `{"propose_pfm":{"chain": "feeabs"}}`, stakeOnOsmosis) require.NoError(t, err) + err = testutil.WaitForBlocks(ctx, 15, feeabs, gaia, osmosis) require.NoError(t, err) + queryMsg := QuerySmartMsg{ Packet: HasPacketForwarding{ ChainID: "feeabs", }, } - res := QuerySmartMsgResponse{} - err = osmosis.QueryContract(ctx, registryContractAddress, queryMsg, &res) + var feeabsRes QuerySmartMsgResponse + err = osmosis.QueryContract(ctx, registryContractAddr, queryMsg, &feeabsRes) require.NoError(t, err) + require.Equal(t, true, feeabsRes.Data) + // propose_pfm for gaia - _, err = feeabsCli.SetupProposePFM(osmosis, ctx, osmosisUser.KeyName(), registryContractAddress, `{"propose_pfm":{"chain": "gaia"}}`, uatomOnOsmosis) + _, err = feeabsCli.SetupProposePFM(osmosis, ctx, osmosisUser.KeyName(), registryContractAddr, `{"propose_pfm":{"chain": "gaia"}}`, uatomOnOsmosis) require.NoError(t, err) + err = testutil.WaitForBlocks(ctx, 15, feeabs, gaia, osmosis) require.NoError(t, err) + queryMsg = QuerySmartMsg{ Packet: HasPacketForwarding{ ChainID: "gaia", }, } - res = QuerySmartMsgResponse{} - err = osmosis.QueryContract(ctx, registryContractAddress, queryMsg, &res) - require.NoError(t, err) - // store swaprouter - swapRouterContractID, err := osmosis.StoreContract(ctx, osmosisUser.KeyName(), "./bytecode/swaprouter.wasm") + var gaiaRes QuerySmartMsgResponse + err = osmosis.QueryContract(ctx, registryContractAddr, queryMsg, &gaiaRes) require.NoError(t, err) - // instantiate - swapRouterContractAddress, err := osmosis.InstantiateContract(ctx, osmosisUser.KeyName(), swapRouterContractID, initMsg, true) + require.Equal(t, true, gaiaRes) + + // Create pool uatom/stake on Osmosis + poolID, err := feeabsCli.CreatePool(osmosis, ctx, osmosisUser.KeyName(), cosmos.OsmosisPoolParams{ + Weights: fmt.Sprintf("5%s,5%s", stakeOnOsmosis, uatomOnOsmosis), + InitialDeposit: fmt.Sprintf("95000000%s,950000000%s", stakeOnOsmosis, uatomOnOsmosis), + SwapFee: "0.01", + ExitFee: "0", + FutureGovernor: "", + }) require.NoError(t, err) + require.Equal(t, "1", poolID) // execute msg = fmt.Sprintf("{\"set_route\":{\"input_denom\":\"%s\",\"output_denom\":\"%s\",\"pool_route\":[{\"pool_id\":\"%s\",\"token_out_denom\":\"%s\"}]}}", @@ -130,17 +137,9 @@ func TestFeeAbs(t *testing.T) { poolID, stakeOnOsmosis, ) - _, err = osmosis.ExecuteContract(ctx, osmosisUser.KeyName(), swapRouterContractAddress, msg) + _, err = osmosis.ExecuteContract(ctx, osmosisUser.KeyName(), swapRouterContractAddr, msg) require.NoError(t, err) - // store xcs - xcsContractID, err := osmosis.StoreContract(ctx, osmosisUser.KeyName(), "./bytecode/crosschain_swaps.wasm") - require.NoError(t, err) - // instantiate - initMsg = fmt.Sprintf("{\"swap_contract\":\"%s\",\"governor\": \"%s\"}", swapRouterContractAddress, owner) - xcsContractAddress, err := osmosis.InstantiateContract(ctx, osmosisUser.KeyName(), xcsContractID, initMsg, true) - _ = xcsContractAddress - require.NoError(t, err) // Swap Feeabs(uatom) to Osmosis // send ibc token to feeabs module account gaiaHeight, err := gaia.Height(ctx) @@ -148,11 +147,10 @@ func TestFeeAbs(t *testing.T) { feeabsModule, err := feeabsCli.QueryModuleAccountBalances(feeabs, ctx) require.NoError(t, err) - dstAddress := feeabsModule.Address transfer := ibc.WalletAmount{ - Address: dstAddress, + Address: feeabsModule.GetAddress(), Denom: gaia.Config().Denom, - Amount: math.NewInt(1_000_000).Int64(), + Amount: 1000000, } tx, err := gaia.SendIBCTransfer(ctx, channGaiaFeeabs.ChannelID, gaiaUser.KeyName(), transfer, ibc.TransferOptions{}) @@ -161,12 +159,13 @@ func TestFeeAbs(t *testing.T) { _, err = testutil.PollForAck(ctx, gaia, gaiaHeight, gaiaHeight+30, tx.Packet) require.NoError(t, err) + err = testutil.WaitForBlocks(ctx, 1, feeabs, gaia, osmosis) require.NoError(t, err) - denomTrace = transfertypes.ParseDenomTrace(transfertypes.GetPrefixedDenom(channFeeabsGaia.PortID, channFeeabsGaia.ChannelID, gaia.Config().Denom)) - uatomOnFeeabs := denomTrace.IBCDenom() + uatomOnFeeabs := transfertypes.ParseDenomTrace(transfertypes.GetPrefixedDenom(channFeeabsGaia.PortID, channFeeabsGaia.ChannelID, gaia.Config().Denom)).IBCDenom() + // Proposal change params of feeabs currentDirectory, _ := os.Getwd() paramChangePath := path.Join(currentDirectory, "proposal", "proposal.json") @@ -194,18 +193,19 @@ func TestFeeAbs(t *testing.T) { height, err = feeabs.Height(ctx) require.NoError(t, err) - _, err = cosmos.PollForProposalStatus(ctx, feeabs, height, height+10, "2", cosmos.ProposalStatusPassed) + _, err = cosmos.PollForProposalStatus(ctx, feeabs, height, height+10, paramTx.ProposalID, cosmos.ProposalStatusPassed) require.NoError(t, err, "proposal status did not change to passed in expected number of blocks") _, err = feeabsCli.QueryHostZoneConfig(feeabs, ctx) require.NoError(t, err) + // xcs feeabsHeight, err := feeabs.Height(ctx) require.NoError(t, err) feeabsModule, err = feeabsCli.QueryModuleAccountBalances(feeabs, ctx) require.NoError(t, err) - fmt.Printf("Module Account Balances before swap: %v\n", feeabsModule.Balances) + t.Logf("Module Account Balances before swap: %v\n", feeabsModule.Balances) transferTx, err := feeabsCli.CrossChainSwap(feeabs, ctx, feeabsUser.KeyName(), uatomOnFeeabs) require.NoError(t, err) @@ -216,7 +216,7 @@ func TestFeeAbs(t *testing.T) { feeabsModule, err = feeabsCli.QueryModuleAccountBalances(feeabs, ctx) require.NoError(t, err) - fmt.Printf("Module Account Balances after swap: %v\n", feeabsModule.Balances) + t.Logf("Module Account Balances after swap: %v\n", feeabsModule.Balances) balance, err := feeabs.GetBalance(ctx, feeabsModule.Address, feeabs.Config().Denom) require.NoError(t, err) diff --git a/tests/interchaintest/setup.go b/tests/interchaintest/setup.go index 3debe6b..d1856de 100644 --- a/tests/interchaintest/setup.go +++ b/tests/interchaintest/setup.go @@ -463,3 +463,67 @@ func SetupChain(t *testing.T, ctx context.Context) ([]ibc.Chain, []ibc.Wallet, [ return chains, users, chanels } + +// SetupOsmosisContracts setup osmosis contracts for crosschain swap. +// There are three main contracts +// 1. crosschain-registry: https://github.com/osmosis-labs/osmosis/blob/main/cosmwasm/contracts/crosschain-swaps/README.md +// 2. swaprouter: https://github.com/osmosis-labs/osmosis/tree/main/cosmwasm/contracts/swaprouter +// 3. crosschain-swaps: https://github.com/osmosis-labs/osmosis/blob/main/cosmwasm/contracts/crosschain-swaps/README.md +func SetupOsmosisContracts(t *testing.T, + ctx context.Context, + osmosis *cosmos.CosmosChain, + user ibc.Wallet, +) ([]string, error) { + registryWasm := "./bytecode/crosschain_registry.wasm" + swaprouterWasm := "./bytecode/swaprouter.wasm" + xcsV2Wasm := "./bytecode/crosschain_swaps.wasm" + + // Store crosschain registry contract + registryCodeId, err := osmosis.StoreContract(ctx, user.KeyName(), registryWasm) + if err != nil { + return nil, err + } + t.Logf("crosschain registry code id: %s\n", registryCodeId) + + // Store swap router contract + swaprouterCodeId, err := osmosis.StoreContract(ctx, user.KeyName(), swaprouterWasm) + if err != nil { + return nil, err + } + t.Logf("swap router code id: %s\n", swaprouterCodeId) + + // Store crosschain swaps contract + xcsV2CodeId, err := osmosis.StoreContract(ctx, user.KeyName(), xcsV2Wasm) + if err != nil { + return nil, err + } + t.Logf("crosschain swaps code id: %s\n", xcsV2CodeId) + + // Instantiate contracts + // 1. Crosschain Registry Contract + owner := sdktypes.MustBech32ifyAddressBytes(osmosis.Config().Bech32Prefix, user.Address()) + initMsg := fmt.Sprintf("{\"owner\":\"%s\"}", owner) + + registryContractAddr, err := osmosis.InstantiateContract(ctx, user.KeyName(), registryCodeId, initMsg, true) + if err != nil { + return nil, err + } + t.Logf("registry contract address: %s\n", registryContractAddr) + + // 2. Swap Router Contract + swaprouterContractAddr, err := osmosis.InstantiateContract(ctx, user.KeyName(), swaprouterCodeId, initMsg, true) + if err != nil { + return nil, err + } + t.Logf("swap router contract address: %s\n", swaprouterContractAddr) + + // 3. Crosschain Swaps Contract + initMsg = fmt.Sprintf("{\"swap_contract\":\"%s\",\"governor\": \"%s\"}", swaprouterContractAddr, owner) + xcsV2ContractAddr, err := osmosis.InstantiateContract(ctx, user.KeyName(), xcsV2Wasm, initMsg, true) + if err != nil { + return nil, err + } + t.Logf("crosschain swaps contract address: %s", xcsV2ContractAddr) + + return []string{registryContractAddr, swaprouterContractAddr, xcsV2ContractAddr}, nil +} diff --git a/tests/interchaintest/testnet/configs/feeapp.json b/tests/interchaintest/testnet/configs/feeapp.json new file mode 100644 index 0000000..db8dc83 --- /dev/null +++ b/tests/interchaintest/testnet/configs/feeapp.json @@ -0,0 +1,17 @@ +{ + "type": "cosmos", + "value": { + "key": "relayer", + "chain-id": "test-1", + "rpc-addr": "tcp://65.21.224.114:26657", + "grpc-addr": "", + "account-prefix": "feeabs", + "keyring-backend": "test", + "gas-adjustment": 1.5, + "gas-prices": "0.00stake", + "debug": true, + "timeout": "20s", + "output-format": "json", + "sign-mode": "direct" + } +} diff --git a/tests/interchaintest/testnet/configs/osmosis.json b/tests/interchaintest/testnet/configs/osmosis.json new file mode 100644 index 0000000..ff66114 --- /dev/null +++ b/tests/interchaintest/testnet/configs/osmosis.json @@ -0,0 +1,17 @@ +{ + "type": "cosmos", + "value": { + "key": "relayer", + "chain-id": "osmo-test-5", + "rpc-addr": "https://rpc.testnet.osmosis.zone:443", + "grpc-addr": "", + "account-prefix": "osmo", + "keyring-backend": "test", + "gas-adjustment": 1.5, + "gas-prices": "0.01uosmo", + "debug": true, + "timeout": "20s", + "output-format": "json", + "sign-mode": "direct" + } +} diff --git a/tests/interchaintest/testnet/paths/transfer.json b/tests/interchaintest/testnet/paths/transfer.json new file mode 100644 index 0000000..8f4a52d --- /dev/null +++ b/tests/interchaintest/testnet/paths/transfer.json @@ -0,0 +1,15 @@ +{ + "src": { + "chain-id": "test-1", + "port-id": "transfer", + "order": "unordered", + "version": "ics20-1" + }, + "dst": { + "chain-id": "osmo-test-5", + "port-id": "transfer", + "order": "unordered", + "version": "ics20-1" + }, + "strategy": { "type": "naive" } +} diff --git a/tests/interchaintest/testnet/pool/pool-1.json b/tests/interchaintest/testnet/pool/pool-1.json new file mode 100644 index 0000000..f839ae8 --- /dev/null +++ b/tests/interchaintest/testnet/pool/pool-1.json @@ -0,0 +1,7 @@ +{ + "weights": "10ibc/1C812795FFE318D29581174B1A04CD6F242E1A3DA504E98D7EDD339B1AF00466,2uosmo", + "initial-deposit": "100000ibc/1C812795FFE318D29581174B1A04CD6F242E1A3DA504E98D7EDD339B1AF00466,20000uosmo", + "swap-fee": "0.01", + "exit-fee": "0", + "future-governor": "" +} diff --git a/tests/interchaintest/testnet/proposal/add_host_zone.json b/tests/interchaintest/testnet/proposal/add_host_zone.json new file mode 100644 index 0000000..049ccb3 --- /dev/null +++ b/tests/interchaintest/testnet/proposal/add_host_zone.json @@ -0,0 +1,11 @@ +{ + "title": "Add Fee Abbtraction Host Zone Proposal", + "description": "Add Fee Abbtraction Host Zone", + "host_chain_fee_abs_config": { + "ibc_denom": "ibc/1C812795FFE318D29581174B1A04CD6F242E1A3DA504E98D7EDD339B1AF00466", + "osmosis_pool_token_denom_in": "ibc/1C812795FFE318D29581174B1A04CD6F242E1A3DA504E98D7EDD339B1AF00466", + "pool_id": "1", + "frozen": false + }, + "deposit": "100000000stake" +} diff --git a/x/feeabs/ante/decorate.go b/x/feeabs/ante/decorate.go index a08ed64..86c5aaf 100644 --- a/x/feeabs/ante/decorate.go +++ b/x/feeabs/ante/decorate.go @@ -21,7 +21,12 @@ type FeeAbstractionDeductFeeDecorate struct { feegrantKeeper FeegrantKeeper } -func NewFeeAbstractionDeductFeeDecorate(ak AccountKeeper, bk BankKeeper, feeabsKeeper feeabskeeper.Keeper, fk FeegrantKeeper) FeeAbstractionDeductFeeDecorate { +func NewFeeAbstractionDeductFeeDecorate( + ak AccountKeeper, + bk BankKeeper, + feeabsKeeper feeabskeeper.Keeper, + fk FeegrantKeeper, +) FeeAbstractionDeductFeeDecorate { return FeeAbstractionDeductFeeDecorate{ accountKeeper: ak, bankKeeper: bk, @@ -54,7 +59,15 @@ func (fadfd FeeAbstractionDeductFeeDecorate) AnteHandle(ctx sdk.Context, tx sdk. return fadfd.abstractionDeductFeeHandler(ctx, tx, simulate, next, feeTx, hostChainConfig) } -func (fadfd FeeAbstractionDeductFeeDecorate) normalDeductFeeAnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler, feeTx sdk.FeeTx) (newCtx sdk.Context, err error) { +// normalDeductFeeAnteHandle deducts the fee from fee payer or fee granter (if set) and ensure +// the fee collector module account is set +func (fadfd FeeAbstractionDeductFeeDecorate) normalDeductFeeAnteHandle( + ctx sdk.Context, + tx sdk.Tx, + simulate bool, + next sdk.AnteHandler, + feeTx sdk.FeeTx, +) (newCtx sdk.Context, err error) { fee := feeTx.GetFee() feePayer := feeTx.FeePayer() feeGranter := feeTx.FeeGranter() @@ -97,6 +110,9 @@ func (fadfd FeeAbstractionDeductFeeDecorate) normalDeductFeeAnteHandle(ctx sdk.C return next(ctx, tx, simulate) } +// abstractionDeductFeeHandler calculates the equivalent native tokens from +// IBC tokens and deducts the fees accordingly if the transaction involves IBC tokens +// and the host chain configuration is set. func (fadfd FeeAbstractionDeductFeeDecorate) abstractionDeductFeeHandler(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler, feeTx sdk.FeeTx, hostChainConfig feeabstypes.HostChainFeeAbsConfig) (newCtx sdk.Context, err error) { if hostChainConfig.Frozen { return ctx, sdkerrors.Wrap(feeabstypes.ErrHostZoneFrozen, "cannot deduct fee as host zone is frozen") @@ -171,7 +187,7 @@ func DeductFees(bankKeeper types.BankKeeper, ctx sdk.Context, accAddress sdk.Acc return nil } -// MempoolFeeDecorator will check if the transaction's fee is at least as large +// FeeAbstrationMempoolFeeDecorator will check if the transaction's fee is at least as large // as the local validator's minimum gasFee (defined in validator config). // If fee is too low, decorator returns error and tx is rejected from mempool. // Note this only applies when ctx.CheckTx = true @@ -232,8 +248,8 @@ func (famfd FeeAbstrationMempoolFeeDecorator) AnteHandle(ctx sdk.Context, tx sdk return ctx, err } - // split feeRequired into zero and non-zero coins(nonZeroCoinFeesReq, zeroCoinFeesDenomReq), split feeCoins according to - // nonZeroCoinFeesReq, zeroCoinFeesDenomReq, + // split feeRequired into zero and non-zero coins(nonZeroCoinFeesReq, zeroCoinFeesDenomReq) + // split feeCoins according to nonZeroCoinFeesReq, zeroCoinFeesDenomReq, // so that feeCoins can be checked separately against them. nonZeroCoinFeesReq, zeroCoinFeesDenomReq := getNonZeroFees(feeRequired) @@ -280,7 +296,7 @@ func (famfd FeeAbstrationMempoolFeeDecorator) AnteHandle(ctx sdk.Context, tx sdk } if feeCoinsLen == 0 { - return ctx, sdkerrors.Wrapf(errorstypes.ErrInsufficientFee, "insufficient fees; got: %s required 12: %s", feeCoins, feeRequired) + return ctx, sdkerrors.Wrapf(errorstypes.ErrInsufficientFee, "insufficient fees; got: %s required: %s", feeCoins, feeRequired) } // After all the checks, the tx is confirmed: // feeCoins denoms subset off feeRequired (or replaced with fee-abstraction) diff --git a/x/feeabs/keeper/ibc.go b/x/feeabs/keeper/ibc.go index 13ddea3..b1ba980 100644 --- a/x/feeabs/keeper/ibc.go +++ b/x/feeabs/keeper/ibc.go @@ -328,7 +328,6 @@ func (k Keeper) executeAllHostChainSwap(ctx sdk.Context) { } err = k.transferOsmosisCrosschainSwap(ctx, hostZoneConfig) - if err != nil { k.Logger(ctx).Error(fmt.Sprintf("Failed to transfer IBC token %s", err.Error())) err = k.FreezeHostZoneByIBCDenom(ctx, hostZoneConfig.IbcDenom) diff --git a/x/feeabs/keeper/msgserver.go b/x/feeabs/keeper/msgserver.go index eeecd28..9626cf7 100644 --- a/x/feeabs/keeper/msgserver.go +++ b/x/feeabs/keeper/msgserver.go @@ -54,7 +54,6 @@ func (k Keeper) SwapCrossChain(goCtx context.Context, msg *types.MsgSwapCrossCha } err = k.transferOsmosisCrosschainSwap(ctx, hostChainConfig) - if err != nil { return nil, err } diff --git a/x/feeabs/types/epoch.go b/x/feeabs/types/epoch.go index d4b28bc..32cd37e 100644 --- a/x/feeabs/types/epoch.go +++ b/x/feeabs/types/epoch.go @@ -6,8 +6,8 @@ import ( ) const ( - DefaultSwapPeriod = time.Minute * 180 - DefaultQueryPeriod = time.Minute * 60 + DefaultSwapPeriod = time.Minute * 1 + DefaultQueryPeriod = time.Minute * 1 DefaultSwapEpochIdentifier = "swap" DefaultQueryEpochIdentifier = "query" )