diff --git a/.github/workflows/interchaintest.yaml b/.github/workflows/interchaintest.yaml index 71643719..64f462f0 100644 --- a/.github/workflows/interchaintest.yaml +++ b/.github/workflows/interchaintest.yaml @@ -59,6 +59,24 @@ jobs: env: BRANCH_CI: 'latest' + + test-ibc-ibcfee: + runs-on: ubuntu-latest + needs: build-and-push-image + steps: + - name: Set up Go 1.21 + uses: actions/setup-go@v3 + with: + go-version: 1.21 + + - name: checkout code + uses: actions/checkout@v3 + + - run: make ictest-ibc-custom + env: + BRANCH_CI: 'latest' + + test-packet-forward: runs-on: ubuntu-latest needs: build-and-push-image diff --git a/Makefile b/Makefile index 8363ef3c..15997377 100644 --- a/Makefile +++ b/Makefile @@ -118,7 +118,7 @@ ictest-basic: # Executes IBC tests via interchaintest ictest-ibc: - cd tests/interchaintest && go test -timeout=25m -race -v -run TestFeeabsGaiaIBCTransfer . + cd tests/interchaintest && go test -timeout=25m -race -v -run '^TestFeeabsGaiaIBCTransfer$$' . # Executes IBC tests via interchaintest ictest-packet-forward: @@ -133,6 +133,9 @@ ictest-feeabs: ictest-query-osmosis-twap: cd tests/interchaintest && go test -timeout=25m -race -v -run TestQueryOsmosisTwap . +ictest-ibc-custom: + cd tests/interchaintest && go test -timeout=25m -race -v -run TestFeeabsGaiaIBCTransferWithIBCFee . + # ictest-feeabs-ibc-transfer: # cd tests/interchaintest && go test -timeout=25m -race -v -run TestIBCTransferWithFeeAbs . diff --git a/go.mod b/go.mod index fc217ee5..a585da52 100644 --- a/go.mod +++ b/go.mod @@ -332,4 +332,6 @@ replace ( // latest grpc doesn't work with our modified proto compiler, so we need to enforce // the following version across all dependencies. google.golang.org/grpc => google.golang.org/grpc v1.54.0 + + ) diff --git a/tests/interchaintest/feeabs/proposal.go b/tests/interchaintest/feeabs/proposal.go index ebae0b1c..b54a63eb 100644 --- a/tests/interchaintest/feeabs/proposal.go +++ b/tests/interchaintest/feeabs/proposal.go @@ -195,6 +195,7 @@ func ParamChangeProposal(c *cosmos.CosmosChain, ctx context.Context, keyName str "param-change", proposalPath, "--gas", "auto", + "--gas-adjustment", "1.5", } txHash, err := tn.ExecTx(ctx, keyName, command...) diff --git a/tests/interchaintest/feeabs/query.go b/tests/interchaintest/feeabs/query.go index df2facd0..e8c71d26 100644 --- a/tests/interchaintest/feeabs/query.go +++ b/tests/interchaintest/feeabs/query.go @@ -3,6 +3,8 @@ package feeabs import ( "context" "encoding/json" + "fmt" + "time" "github.com/strangelove-ventures/interchaintest/v7/chain/cosmos" @@ -39,6 +41,7 @@ func QueryAllHostZoneConfig(c *cosmos.CosmosChain, ctx context.Context) (*AllQue if err != nil { return &AllQueryHostChainConfigResponse{}, err } + fmt.Println(hostZoneConfig) return &hostZoneConfig, nil } @@ -73,3 +76,20 @@ func QueryOsmosisArithmeticTwap(c *cosmos.CosmosChain, ctx context.Context, ibcD } return &response, nil } + +func QueryOsmosisArithmeticTwapOsmosis(c *cosmos.CosmosChain, ctx context.Context, poolID, ibcDenom string) (*feeabstypes.QueryOsmosisArithmeticTwapResponse, error) { + node := getFullNode(c) + currentEpoch := time.Now().Unix() + + cmd := []string{"twap", "arithmetic", poolID, ibcDenom, fmt.Sprintf("%d", currentEpoch-20), fmt.Sprintf("%d", currentEpoch-10)} + stdout, _, err := node.ExecQuery(ctx, cmd...) + if err != nil { + return &feeabstypes.QueryOsmosisArithmeticTwapResponse{}, err + } + + var response feeabstypes.QueryOsmosisArithmeticTwapResponse + if err = json.Unmarshal(stdout, &response); err != nil { + return &feeabstypes.QueryOsmosisArithmeticTwapResponse{}, err + } + return &response, nil +} diff --git a/tests/interchaintest/feeabs_test.go b/tests/interchaintest/feeabs_test.go deleted file mode 100644 index ff1156a2..00000000 --- a/tests/interchaintest/feeabs_test.go +++ /dev/null @@ -1,233 +0,0 @@ -package interchaintest - -import ( - "context" - "fmt" - "os" - "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" - "github.com/strangelove-ventures/interchaintest/v7/chain/cosmos" - "github.com/strangelove-ventures/interchaintest/v7/ibc" - "github.com/strangelove-ventures/interchaintest/v7/testutil" - "github.com/stretchr/testify/require" - - feeabsCli "github.com/osmosis-labs/fee-abstraction/tests/interchaintest/feeabs" -) - -func TestFeeAbs(t *testing.T) { - if testing.Short() { - t.Skip("skipping in short mode") - } - // Set up chains, users and channels - ctx := context.Background() - chains, users, channels := SetupChain(t, ctx) - feeabs, gaia, osmosis := chains[0].(*cosmos.CosmosChain), chains[1].(*cosmos.CosmosChain), chains[2].(*cosmos.CosmosChain) - - feeabsUser, gaiaUser, osmosisUser := users[0], users[1], users[2] - - channFeeabsOsmosis, channOsmosisFeeabs, channFeeabsGaia, channGaiaFeeabs, channOsmosisGaia, channGaiaOsmosis := channels[0], channels[1], channels[2], channels[3], channels[4], channels[5] - - contracts, err := SetupOsmosisContracts(t, ctx, osmosis, osmosisUser) - require.NoError(t, err) - 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, - channFeeabsGaia.ChannelID, - channGaiaFeeabs.ChannelID, - channOsmosisGaia.ChannelID, - channGaiaOsmosis.ChannelID) - _, err = osmosis.ExecuteContract(ctx, osmosisUser.KeyName(), registryContractAddr, msg, "--gas", "1000000") - require.NoError(t, err) - - // Modify bech32 prefixes on registry contract - msg = `{ - "modify_bech32_prefixes": - { - "operations": - [ - {"operation": "set", "chain_name": "feeabs", "prefix": "feeabs"}, - {"operation": "set", "chain_name": "osmosis", "prefix": "osmo"}, - {"operation": "set", "chain_name": "gaia", "prefix": "cosmos"} - ] - } - }` - _, err = osmosis.ExecuteContract(ctx, osmosisUser.KeyName(), registryContractAddr, msg) - require.NoError(t, err) - - osmosisPrefix := osmosis.Config().Bech32Prefix - - 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) - - 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, amountToSend, osmosisUserBalance) - - // Setup propose_pfm - // propose_pfm for feeabs - _, 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{ - Chain: "feeabs", - }, - } - // {"data":false} - 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(), 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{ - Chain: "gaia", - }, - } - var gaiaRes QuerySmartMsgResponse - err = osmosis.QueryContract(ctx, registryContractAddr, queryMsg, &gaiaRes) - require.NoError(t, err) - require.Equal(t, true, gaiaRes.Data) - - // 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\"}]}}", - uatomOnOsmosis, - stakeOnOsmosis, - poolID, - stakeOnOsmosis, - ) - _, err = osmosis.ExecuteContract(ctx, osmosisUser.KeyName(), swapRouterContractAddr, msg) - require.NoError(t, err) - - // Swap Feeabs(uatom) to Osmosis - // send ibc token to feeabs module account - gaiaHeight, err := gaia.Height(ctx) - require.NoError(t, err) - - feeabsModule, err := feeabsCli.QueryModuleAccountBalances(feeabs, ctx) - require.NoError(t, err) - transfer := ibc.WalletAmount{ - Address: feeabsModule.GetAddress(), - Denom: gaia.Config().Denom, - Amount: math.NewInt(1000000), - } - - ibcTx, err := gaia.SendIBCTransfer(ctx, channGaiaFeeabs.ChannelID, gaiaUser.KeyName(), transfer, ibc.TransferOptions{}) - require.NoError(t, err) - require.NoError(t, ibcTx.Validate()) - - _, err = testutil.PollForAck(ctx, gaia, gaiaHeight, gaiaHeight+30, ibcTx.Packet) - require.NoError(t, err) - - err = testutil.WaitForBlocks(ctx, 1, feeabs, gaia, osmosis) - require.NoError(t, err) - - 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") - - changeParamProposal, err := paramsutils.ParseParamChangeProposalJSON(feeabs.Config().EncodingConfig.Amino, paramChangePath) - require.NoError(t, err) - - paramTx, err := feeabsCli.ParamChangeProposal(feeabs, ctx, feeabsUser.KeyName(), &changeParamProposal) - require.NoError(t, err, "error submitting param change proposal tx") - - err = feeabs.VoteOnProposalAllValidators(ctx, paramTx.ProposalID, cosmos.ProposalVoteYes) - require.NoError(t, err, "failed to submit votes") - - height, err := feeabs.Height(ctx) - require.NoError(t, err) - - _, 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.AddHostZoneProposal(feeabs, ctx, feeabsUser.KeyName(), "./proposal/add_host_zone.json") - require.NoError(t, err) - - err = feeabs.VoteOnProposalAllValidators(ctx, "2", cosmos.ProposalVoteYes) - require.NoError(t, err, "failed to submit votes") - - height, err = feeabs.Height(ctx) - require.NoError(t, err) - - _, 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") - - // wait for next 5 blocks - require.NoError(t, err) - testutil.WaitForBlocks(ctx, 5, feeabs) - - // there must be exactly 1 host zone configs - res, err := feeabsCli.QueryAllHostZoneConfig(feeabs, ctx) - require.NoError(t, err) - require.Equal(t, len(res.AllHostChainConfig), 1) - - // xcs - feeabsHeight, err := feeabs.Height(ctx) - require.NoError(t, err) - - feeabsModule, err = feeabsCli.QueryModuleAccountBalances(feeabs, ctx) - require.NoError(t, err) - t.Logf("Module Account Balances before swap: %v\n", feeabsModule.Balances) - - transferTx, err := feeabsCli.CrossChainSwap(feeabs, ctx, feeabsUser.KeyName(), uatomOnFeeabs) - require.NoError(t, err) - _, err = testutil.PollForAck(ctx, feeabs, feeabsHeight, feeabsHeight+25, transferTx.Packet) - require.NoError(t, err) - err = testutil.WaitForBlocks(ctx, 50, feeabs, gaia, osmosis) - require.NoError(t, err) - - feeabsModule, err = feeabsCli.QueryModuleAccountBalances(feeabs, ctx) - require.NoError(t, err) - 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) - require.True(t, balance.GT(math.NewInt(1))) -} diff --git a/tests/interchaintest/go.mod b/tests/interchaintest/go.mod index 178c724f..aa627b24 100644 --- a/tests/interchaintest/go.mod +++ b/tests/interchaintest/go.mod @@ -1,17 +1,31 @@ -module github.com/osmosis-labs/fee-abstraction/tests/interchaintest +module github.com/osmosis-labs/fee-abstraction/v7/tests/interchaintest go 1.21 require ( + cosmossdk.io/errors v1.0.1 cosmossdk.io/math v1.2.0 + github.com/CosmWasm/wasmvm v1.3.0 github.com/avast/retry-go/v4 v4.5.0 + github.com/cometbft/cometbft v0.37.4 + github.com/cometbft/cometbft-db v0.9.1 + github.com/cosmos/cosmos-proto v1.0.0-beta.3 github.com/cosmos/cosmos-sdk v0.47.8 + github.com/cosmos/gogoproto v1.4.10 + github.com/cosmos/iavl v0.20.1 github.com/cosmos/ibc-go/v7 v7.3.1 github.com/icza/dyno v0.0.0-20220812133438-f0b6f8a18845 github.com/osmosis-labs/fee-abstraction/v7 v7.0.0-20240308160138-d1eb119cae30 + github.com/spf13/cast v1.5.1 + github.com/spf13/cobra v1.8.0 + github.com/spf13/pflag v1.0.5 github.com/strangelove-ventures/interchaintest/v7 v7.0.0 github.com/stretchr/testify v1.8.4 go.uber.org/zap v1.26.0 + golang.org/x/exp v0.0.0-20230711153332-06a737ee72cb + google.golang.org/grpc v1.60.1 + google.golang.org/protobuf v1.32.0 + gopkg.in/yaml.v2 v2.4.0 ) require ( @@ -23,7 +37,6 @@ require ( cosmossdk.io/api v0.3.1 // indirect cosmossdk.io/core v0.6.1 // indirect cosmossdk.io/depinject v1.0.0-alpha.4 // indirect - cosmossdk.io/errors v1.0.1 // indirect cosmossdk.io/log v1.3.0 // indirect cosmossdk.io/tools/rosetta v0.2.1 // indirect filippo.io/edwards25519 v1.0.0 // indirect @@ -34,7 +47,6 @@ require ( github.com/ChainSafe/go-schnorrkel/1 v0.0.0-00010101000000-000000000000 // indirect github.com/ComposableFi/go-subkey/v2 v2.0.0-tm03420 // indirect github.com/CosmWasm/wasmd v0.41.0 // indirect - github.com/CosmWasm/wasmvm v1.3.0 // indirect github.com/FactomProject/basen v0.0.0-20150613233007-fe3947df716e // indirect github.com/FactomProject/btcutilecc v0.0.0-20130527213604-d3a63a5752ec // indirect github.com/Microsoft/go-winio v0.6.0 // indirect @@ -53,15 +65,10 @@ require ( github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect github.com/cockroachdb/redact v1.1.5 // indirect github.com/coinbase/rosetta-sdk-go/types v1.0.0 // indirect - github.com/cometbft/cometbft v0.37.4 // indirect - github.com/cometbft/cometbft-db v0.9.1 // indirect github.com/confio/ics23/go v0.9.0 // indirect github.com/cosmos/btcutil v1.0.5 // indirect - github.com/cosmos/cosmos-proto v1.0.0-beta.3 // indirect github.com/cosmos/go-bip39 v1.0.0 // indirect github.com/cosmos/gogogateway v1.2.0 // indirect - github.com/cosmos/gogoproto v1.4.10 // indirect - github.com/cosmos/iavl v0.20.1 // indirect github.com/cosmos/ibc-go/modules/capability v1.0.0-rc1 // indirect github.com/cosmos/ics23/go v0.10.0 // indirect github.com/cosmos/interchain-security/v3 v3.1.1-0.20231102122221-81650a84f989 // indirect @@ -183,10 +190,7 @@ require ( github.com/sasha-s/go-deadlock v0.3.1 // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect github.com/spf13/afero v1.9.5 // indirect - github.com/spf13/cast v1.5.1 // indirect - github.com/spf13/cobra v1.8.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect - github.com/spf13/pflag v1.0.5 // indirect github.com/spf13/viper v1.16.0 // indirect github.com/subosito/gotenv v1.4.2 // indirect github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect @@ -204,7 +208,6 @@ require ( go.opentelemetry.io/otel/trace v1.19.0 // indirect go.uber.org/multierr v1.11.0 // indirect golang.org/x/crypto v0.16.0 // indirect - golang.org/x/exp v0.0.0-20230711153332-06a737ee72cb // indirect golang.org/x/mod v0.12.0 // indirect golang.org/x/net v0.19.0 // indirect golang.org/x/oauth2 v0.13.0 // indirect @@ -218,11 +221,8 @@ require ( google.golang.org/genproto v0.0.0-20240102182953-50ed04b92917 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20231212172506-995d672761c0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240108191215-35c7eff3a6b1 // indirect - google.golang.org/grpc v1.60.1 // indirect - google.golang.org/protobuf v1.32.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect lukechampine.com/blake3 v1.1.7 // indirect lukechampine.com/uint128 v1.2.0 // indirect @@ -247,5 +247,6 @@ replace ( github.com/cometbft/cometbft => github.com/cometbft/cometbft v0.37.2 github.com/gogo/protobuf => github.com/regen-network/protobuf v1.3.3-alpha.regen.1 github.com/osmosis-labs/fee-abstraction/v7 => ../../ + github.com/osmosis-labs/fee-abstraction/v7/tests/interchaintest/osmosis => ./osmosis github.com/vedhavyas/go-subkey => github.com/strangelove-ventures/go-subkey v1.0.7 ) diff --git a/tests/interchaintest/host_zone_proposal_test.go b/tests/interchaintest/host_zone_proposal_test.go index 89eb6db6..3cab4aeb 100644 --- a/tests/interchaintest/host_zone_proposal_test.go +++ b/tests/interchaintest/host_zone_proposal_test.go @@ -2,13 +2,12 @@ package interchaintest import ( "context" - "fmt" "testing" "github.com/strangelove-ventures/interchaintest/v7/chain/cosmos" "github.com/stretchr/testify/require" - feeabsCli "github.com/osmosis-labs/fee-abstraction/tests/interchaintest/feeabs" + feeabsCli "github.com/osmosis-labs/fee-abstraction/v7/tests/interchaintest/feeabs" ) func TestHostZoneProposal(t *testing.T) { @@ -17,71 +16,16 @@ func TestHostZoneProposal(t *testing.T) { } ctx := context.Background() - chains, users, _ := SetupChain(t, ctx) - feeabs, _, _ := chains[0].(*cosmos.CosmosChain), chains[1].(*cosmos.CosmosChain), chains[2].(*cosmos.CosmosChain) + chains, users, channels := SetupChain(t, ctx) + feeabs, _, osmosis := chains[0].(*cosmos.CosmosChain), chains[1].(*cosmos.CosmosChain), chains[2].(*cosmos.CosmosChain) + channFeeabsOsmosis, _, channFeeabsOsmosisICQ := channels[0], channels[1], channels[6] feeabsUser, _, _ := users[0], users[1], users[2] + osmoOnFeeabs := GetOsmoOnFeeabs(channFeeabsOsmosis, osmosis.Config().Denom) - // Start testing for add host zone proposal - _, err := feeabsCli.AddHostZoneProposal(feeabs, ctx, feeabsUser.KeyName(), "./proposal/add_host_zone.json") - require.NoError(t, err) - - err = feeabs.VoteOnProposalAllValidators(ctx, "1", cosmos.ProposalVoteYes) - require.NoError(t, err, "failed to submit votes") - - height, err := feeabs.Height(ctx) - require.NoError(t, err) - - _, err = cosmos.PollForProposalStatus(ctx, feeabs, height, height+10, "1", cosmos.ProposalStatusPassed) - require.NoError(t, err, "proposal status did not change to passed in expected number of blocks") - - config, err := feeabsCli.QueryHostZoneConfigWithDenom(feeabs, ctx, "ibc/C4CFF46FD6DE35CA4CF4CE031E643C8FDC9BA4B99AE598E9B0ED98FE3A2319F9") - require.NoError(t, err) - require.Equal(t, config, &feeabsCli.HostChainFeeAbsConfigResponse{HostChainConfig: feeabsCli.HostChainFeeAbsConfig{ - IbcDenom: "ibc/C4CFF46FD6DE35CA4CF4CE031E643C8FDC9BA4B99AE598E9B0ED98FE3A2319F9", - OsmosisPoolTokenDenomIn: "ibc/C4CFF46FD6DE35CA4CF4CE031E643C8FDC9BA4B99AE598E9B0ED98FE3A2319F9", - PoolId: "1", - Status: feeabsCli.HostChainFeeAbsStatus_UPDATED, - MinSwapAmount: "0", - }}) - - // Start testing for set host zone proposal - _, err = feeabsCli.SetHostZoneProposal(feeabs, ctx, feeabsUser.KeyName(), "./proposal/set_host_zone.json") - require.NoError(t, err) - - err = feeabs.VoteOnProposalAllValidators(ctx, "2", cosmos.ProposalVoteYes) - require.NoError(t, err, "failed to submit votes") + ParamChangeProposal(t, ctx, feeabs, feeabsUser, &channFeeabsOsmosis, &channFeeabsOsmosisICQ, osmoOnFeeabs) + AddHostZoneProposal(t, ctx, feeabs, feeabsUser) - height, err = feeabs.Height(ctx) + _, err := feeabsCli.QueryHostZoneConfigWithDenom(feeabs, ctx, osmoOnFeeabs) require.NoError(t, err) - - _, err = cosmos.PollForProposalStatus(ctx, feeabs, height, height+10, "2", cosmos.ProposalStatusPassed) - require.NoError(t, err, "proposal status did not change to passed in expected number of blocks") - - config, err = feeabsCli.QueryHostZoneConfigWithDenom(feeabs, ctx, "ibc/C4CFF46FD6DE35CA4CF4CE031E643C8FDC9BA4B99AE598E9B0ED98FE3A2319F9") - require.NoError(t, err) - require.Equal(t, config, &feeabsCli.HostChainFeeAbsConfigResponse{HostChainConfig: feeabsCli.HostChainFeeAbsConfig{ - IbcDenom: "ibc/C4CFF46FD6DE35CA4CF4CE031E643C8FDC9BA4B99AE598E9B0ED98FE3A2319F9", - OsmosisPoolTokenDenomIn: "ibc/C4CFF46FD6DE35CA4CF4CE031E643C8FDC9BA4B99AE598E9B0ED98FE3A2319F9", - PoolId: "1", - Status: feeabsCli.HostChainFeeAbsStatus_FROZEN, - MinSwapAmount: "10", - }}) - - // Start testing for delete host zone proposal - _, err = feeabsCli.DeleteHostZoneProposal(feeabs, ctx, feeabsUser.KeyName(), "./proposal/delete_host_zone.json") - require.NoError(t, err) - - err = feeabs.VoteOnProposalAllValidators(ctx, "3", cosmos.ProposalVoteYes) - require.NoError(t, err, "failed to submit votes") - - height, err = feeabs.Height(ctx) - require.NoError(t, err) - - response, err := cosmos.PollForProposalStatus(ctx, feeabs, height, height+10, "3", cosmos.ProposalStatusPassed) - require.NoError(t, err, "proposal status did not change to passed in expected number of blocks") - fmt.Printf("response: %s\n", response) - - _, err = feeabsCli.QueryHostZoneConfigWithDenom(feeabs, ctx, "ibc/C4CFF46FD6DE35CA4CF4CE031E643C8FDC9BA4B99AE598E9B0ED98FE3A2319F9") - require.Error(t, err) // not found } diff --git a/tests/interchaintest/ibc_transfer_customfee_test.go b/tests/interchaintest/ibc_transfer_customfee_test.go new file mode 100644 index 00000000..53318a82 --- /dev/null +++ b/tests/interchaintest/ibc_transfer_customfee_test.go @@ -0,0 +1,301 @@ +package interchaintest + +import ( + "context" + "fmt" + "strconv" + "testing" + + "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" + sdktypes "github.com/cosmos/cosmos-sdk/types" + transfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" + feeabsCli "github.com/osmosis-labs/fee-abstraction/v7/tests/interchaintest/feeabs" + "github.com/osmosis-labs/fee-abstraction/v7/tests/interchaintest/tendermint" + "github.com/strangelove-ventures/interchaintest/v7/chain/cosmos" + "github.com/strangelove-ventures/interchaintest/v7/ibc" + "github.com/strangelove-ventures/interchaintest/v7/testutil" + "github.com/stretchr/testify/require" + + feeabstypes "github.com/osmosis-labs/fee-abstraction/v7/x/feeabs/types" +) + +// TestFeeabsGaiaIBCTransfer spins up a Feeabs and Gaia network, initializes an IBC connection between them, +// and sends an ICS20 token transfer from Feeabs->Gaia and then back from Gaia->Feeabs. +func TestFeeabsGaiaIBCTransferWithIBCFee(t *testing.T) { + if testing.Short() { + t.Skip("skipping in short mode") + } + // Set up chains, users and channels + ctx := context.Background() + chains, users, channels := SetupChain(t, ctx) + feeabs, gaia, osmosis := chains[0].(*cosmos.CosmosChain), chains[1].(*cosmos.CosmosChain), chains[2].(*cosmos.CosmosChain) + + feeabsUser, _, osmosisUser := users[0], users[1], users[2] + + channFeeabsOsmosis, channOsmosisFeeabs, channFeeabsGaia, channGaiaFeeabs, channOsmosisGaia, channGaiaOsmosis, channFeeabsOsmosisICQ := channels[0], channels[1], channels[2], channels[3], channels[4], channels[5], channels[6] + + // Setup contract on Osmosis + // Store code crosschain Registry + crossChainRegistryContractID, err := osmosis.StoreContract(ctx, osmosisUser.KeyName(), "./bytecode/crosschain_registry.wasm") + 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 + 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, + channFeeabsGaia.ChannelID, + channGaiaFeeabs.ChannelID, + channOsmosisGaia.ChannelID, + channGaiaOsmosis.ChannelID) + _, err = osmosis.ExecuteContract(ctx, osmosisUser.KeyName(), registryContractAddress, msg, "--gas", "1000000") + require.NoError(t, err) + // Execute + msg = `{ + "modify_bech32_prefixes": + { + "operations": + [ + {"operation": "set", "chain_name": "feeabs", "prefix": "feeabs"}, + {"operation": "set", "chain_name": "osmosis", "prefix": "osmo"}, + {"operation": "set", "chain_name": "gaia", "prefix": "cosmos"} + ] + } + }` + _, err = osmosis.ExecuteContract(ctx, osmosisUser.KeyName(), registryContractAddress, 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) + + 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) + require.NoError(t, err) + require.Equal(t, amountToSend, osmosisUserBalance) + + // Create pool Osmosis(stake)/uosmo on Osmosis, with 1:1 ratio + poolID, err := feeabsCli.CreatePool(osmosis, ctx, osmosisUser.KeyName(), cosmos.OsmosisPoolParams{ + Weights: fmt.Sprintf("5%s,5%s", stakeOnOsmosis, osmosis.Config().Denom), + InitialDeposit: fmt.Sprintf("95000000%s,95000000%s", stakeOnOsmosis, osmosis.Config().Denom), + SwapFee: "0.01", + ExitFee: "0", + FutureGovernor: "", + }) + require.NoError(t, err) + require.Equal(t, poolID, "1") + + //////////////////////////////////////////////////////////////////////////////////////// + // Setup propose_pfm + //////////////////////////////////////////////////////////////////////////////////////// + + // propose_pfm for feeabs + _, err = feeabsCli.SetupProposePFM(osmosis, ctx, osmosisUser.KeyName(), registryContractAddress, `{"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{ + Chain: "feeabs", + }, + } + res := QuerySmartMsgResponse{} + err = osmosis.QueryContract(ctx, registryContractAddress, queryMsg, &res) + require.NoError(t, err) + // propose_pfm for gaia + _, err = feeabsCli.SetupProposePFM(osmosis, ctx, osmosisUser.KeyName(), registryContractAddress, `{"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{ + Chain: "gaia", + }, + } + res = QuerySmartMsgResponse{} + err = osmosis.QueryContract(ctx, registryContractAddress, queryMsg, &res) + require.NoError(t, err) + + //////////////////////////////////////////////////////////////////////////////////////// + // Setup feeabs module & add host zone via proposals + //////////////////////////////////////////////////////////////////////////////////////// + + ParamChangeProposal(t, ctx, feeabs, feeabsUser, &channFeeabsOsmosis, &channFeeabsOsmosisICQ, stakeOnOsmosis) + AddHostZoneProposal(t, ctx, feeabs, feeabsUser) + _, err = feeabsCli.QueryAllHostZoneConfig(feeabs, ctx) + require.NoError(t, err) + + //////////////////////////////////////////////////////////////////////////////////////// + // Test IBC transfer with custom fee + //////////////////////////////////////////////////////////////////////////////////////// + + // Wait a few blocks for relayer to start and for user accounts to be created + err = testutil.WaitForBlocks(ctx, 5, feeabs, gaia) + require.NoError(t, err) + + // Get our Bech32 encoded user addresses + feeabsUser, gaiaUser := users[0], users[1] + + feeabsUserAddr := sdktypes.MustBech32ifyAddressBytes(feeabs.Config().Bech32Prefix, feeabsUser.Address()) + gaiaUserAddr := sdktypes.MustBech32ifyAddressBytes(gaia.Config().Bech32Prefix, gaiaUser.Address()) + + // Compose an IBC transfer and send from Gaia -> Feeabs + osmoTokenDenom := transfertypes.GetPrefixedDenom(channFeeabsOsmosis.PortID, channFeeabsOsmosis.ChannelID, osmosis.Config().Denom) + osmoIBCDenom := transfertypes.ParseDenomTrace(osmoTokenDenom).IBCDenom() + fmt.Println("osmoIBCDenom", osmoIBCDenom) + + transferAmount := math.NewInt(1_000) + transfer := ibc.WalletAmount{ + Address: feeabsUserAddr, + Denom: osmosis.Config().Denom, + Amount: transferAmount, + } + + // Compose an IBC transfer and send from Osmo -> Feeabs + feeabsInitialBal, err := feeabs.GetBalance(ctx, feeabsUserAddr, osmoIBCDenom) + require.NoError(t, err) + osmoInitialBal, err := osmosis.GetBalance(ctx, osmosisUser.FormattedAddress(), osmosis.Config().Denom) + require.NoError(t, err) + + transferTx, err := osmosis.SendIBCTransfer(ctx, channOsmosisFeeabs.ChannelID, osmosisUser.KeyName(), transfer, ibc.TransferOptions{}) + require.NoError(t, err) + + osmosisHeight, err := osmosis.Height(ctx) + require.NoError(t, err) + + // Poll for the ack to know the transfer was successful + _, err = testutil.PollForAck(ctx, osmosis, osmosisHeight, osmosisHeight+10, transferTx.Packet) + require.NoError(t, err) + + // Assert that the OSMO funds are deducted in user acc on Gaia and are in the user acc on Feeabs + feeabsUpdateBal, err := feeabs.GetBalance(ctx, feeabsUserAddr, osmoIBCDenom) + require.NoError(t, err) + require.Equal(t, feeabsInitialBal.Add(transferAmount), feeabsUpdateBal) + + osmoUpdateBal, err := osmosis.GetBalance(ctx, osmosisUser.FormattedAddress(), osmosis.Config().Denom) + require.NoError(t, err) + require.GreaterOrEqual(t, osmoInitialBal.Sub(transferAmount).Int64(), osmoUpdateBal.Int64()) + + // Fund the feeabs module account with stake in order to pay native fee + feeabsModuleAddr, err := feeabs.GetModuleAddress(ctx, feeabstypes.ModuleName) + require.NoError(t, err) + transfer = ibc.WalletAmount{ + Address: feeabsModuleAddr, + Denom: feeabs.Config().Denom, + Amount: transferAmount.Mul(sdk.NewInt(2)), + } + err = feeabs.SendFunds(ctx, feeabsUser.KeyName(), transfer) + require.NoError(t, err) + // Compose an IBC transfer and send from Feeabs -> Gaia + transferAmount = math.NewInt(1_000) + ibcFee := sdk.NewCoin(osmoIBCDenom, sdk.NewInt(1000)) + transfer = ibc.WalletAmount{ + Address: gaiaUserAddr, + Denom: feeabs.Config().Denom, + Amount: transferAmount, + } + + customTransferTx, err := SendIBCTransferWithCustomFee(feeabs, ctx, feeabsUser.KeyName(), channFeeabsGaia.ChannelID, transfer, sdk.Coins{ibcFee}) + require.NoError(t, err) + + feeabsHeight, err := feeabs.Height(ctx) + require.NoError(t, err) + + // Poll for the ack to know the transfer was successful + _, err = testutil.PollForAck(ctx, feeabs, feeabsHeight, feeabsHeight+20, customTransferTx.Packet) + require.NoError(t, err) + + // Get the IBC denom for stake on Gaia + feeabsTokenDenom := transfertypes.GetPrefixedDenom(channGaiaFeeabs.PortID, channGaiaFeeabs.ChannelID, feeabs.Config().Denom) + feeabsIBCDenom := transfertypes.ParseDenomTrace(feeabsTokenDenom).IBCDenom() + + // Assert that gaia usre receive the funds from feeabs after the custom fee IBC transfer + stakeOnGaiaBalance, err := gaia.GetBalance(ctx, gaiaUserAddr, feeabsIBCDenom) + require.NoError(t, err) + + require.Equal(t, transferAmount, stakeOnGaiaBalance) + + // Compose an IBC transfer and send from Feeabs -> Gaia, with insufficient fee, should fail + ibcFee = sdk.NewCoin(osmoIBCDenom, sdk.NewInt(1)) + transfer = ibc.WalletAmount{ + Address: gaiaUserAddr, + Denom: feeabs.Config().Denom, + Amount: transferAmount, + } + + // Compose an IBC transfer and send from Feeabs -> Gaia, with insufficient fee, should fail + customTransferTx, err = SendIBCTransferWithCustomFee(feeabs, ctx, feeabsUser.KeyName(), channFeeabsGaia.ChannelID, transfer, sdk.Coins{ibcFee}) + require.Error(t, err) + +} + +func SendIBCTransferWithCustomFee(c *cosmos.CosmosChain, ctx context.Context, keyName string, channelID string, amount ibc.WalletAmount, fees sdk.Coins) (ibc.Tx, error) { + tn := c.Validators[0] + if len(c.FullNodes) > 0 { + tn = c.FullNodes[0] + } + command := []string{ + "ibc-transfer", "transfer", "transfer", channelID, + amount.Address, fmt.Sprintf("%s%s", amount.Amount.String(), amount.Denom), "--fees", fees.String(), + } + var tx ibc.Tx + txHash, err := tn.ExecTx(ctx, keyName, command...) + + if err != nil { + return tx, fmt.Errorf("send ibc transfer: %w", err) + } + txResp, err := c.GetTransaction(txHash) + if err != nil { + return tx, fmt.Errorf("failed to get transaction %s: %w", txHash, err) + } + if txResp.Code != 0 { + return tx, fmt.Errorf("error in transaction (code: %d): %s", txResp.Code, txResp.RawLog) + } + tx.Height = uint64(txResp.Height) + tx.TxHash = txHash + // In cosmos, user is charged for entire gas requested, not the actual gas used. + tx.GasSpent = txResp.GasWanted + + const evType = "send_packet" + events := txResp.Events + + var ( + seq, _ = tendermint.AttributeValue(events, evType, "packet_sequence") + srcPort, _ = tendermint.AttributeValue(events, evType, "packet_src_port") + srcChan, _ = tendermint.AttributeValue(events, evType, "packet_src_channel") + dstPort, _ = tendermint.AttributeValue(events, evType, "packet_dst_port") + dstChan, _ = tendermint.AttributeValue(events, evType, "packet_dst_channel") + timeoutHeight, _ = tendermint.AttributeValue(events, evType, "packet_timeout_height") + timeoutTs, _ = tendermint.AttributeValue(events, evType, "packet_timeout_timestamp") + data, _ = tendermint.AttributeValue(events, evType, "packet_data") + ) + tx.Packet.SourcePort = srcPort + tx.Packet.SourceChannel = srcChan + tx.Packet.DestPort = dstPort + tx.Packet.DestChannel = dstChan + tx.Packet.TimeoutHeight = timeoutHeight + tx.Packet.Data = []byte(data) + + seqNum, err := strconv.Atoi(seq) + if err != nil { + return tx, fmt.Errorf("invalid packet sequence from events %s: %w", seq, err) + } + tx.Packet.Sequence = uint64(seqNum) + + timeoutNano, err := strconv.ParseUint(timeoutTs, 10, 64) + if err != nil { + return tx, fmt.Errorf("invalid packet timestamp timeout %s: %w", timeoutTs, err) + } + tx.Packet.TimeoutTimestamp = ibc.Nanoseconds(timeoutNano) + + return tx, nil +} diff --git a/tests/interchaintest/ibc_transfer_test.go b/tests/interchaintest/ibc_transfer_test.go index 1757e3bc..deb76faf 100644 --- a/tests/interchaintest/ibc_transfer_test.go +++ b/tests/interchaintest/ibc_transfer_test.go @@ -153,7 +153,9 @@ func TestFeeabsGaiaIBCTransfer(t *testing.T) { // Assert that the funds are no longer present in user acc on feeabs and are in the user acc on Gaia feeabsUpdateBal, err := feeabs.GetBalance(ctx, feeabsUserAddr, feeabs.Config().Denom) require.NoError(t, err) - require.Equal(t, feeabsOrigBal.Sub(transferAmount), feeabsUpdateBal) + + // The feeabs account should have the original balance minus the transfer amount and the fee + require.GreaterOrEqual(t, feeabsOrigBal.Sub(transferAmount).Int64(), feeabsUpdateBal.Int64()) gaiaUpdateBal, err := gaia.GetBalance(ctx, gaiaUserAddr, feeabsIBCDenom) require.NoError(t, err) @@ -177,11 +179,11 @@ func TestFeeabsGaiaIBCTransfer(t *testing.T) { require.NoError(t, err) // Assert that the funds are now back on feeabs and not on Gaia - feeabsUpdateBal, err = feeabs.GetBalance(ctx, feeabsUserAddr, feeabs.Config().Denom) + feeabsBalAfterGettingBackToken, err := feeabs.GetBalance(ctx, feeabsUserAddr, feeabs.Config().Denom) require.NoError(t, err) - require.Equal(t, feeabsOrigBal, feeabsUpdateBal) + require.Equal(t, feeabsUpdateBal.Add(transferAmount).Int64(), feeabsBalAfterGettingBackToken.Int64()) gaiaUpdateBal, err = gaia.GetBalance(ctx, gaiaUserAddr, feeabsIBCDenom) require.NoError(t, err) - require.Equal(t, math.ZeroInt(), gaiaUpdateBal) + require.Equal(t, math.ZeroInt().Int64(), gaiaUpdateBal.Int64()) } diff --git a/tests/interchaintest/osmosis/gamm/pool-models/balancer/amm.go b/tests/interchaintest/osmosis/gamm/pool-models/balancer/amm.go new file mode 100644 index 00000000..f7f5a2cd --- /dev/null +++ b/tests/interchaintest/osmosis/gamm/pool-models/balancer/amm.go @@ -0,0 +1,243 @@ +package balancer + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" + + errorsmod "cosmossdk.io/errors" + + "github.com/osmosis-labs/fee-abstraction/v7/tests/interchaintest/osmosis/gamm/types" + "github.com/osmosis-labs/fee-abstraction/v7/tests/interchaintest/osmosis/osmomath" +) + +// subPoolAssetWeights subtracts the weights of two different pool asset slices. +// It assumes that both pool assets have the same token denominations, +// with the denominations in the same order. +// Returned weights can (and probably will have some) be negative. +func subPoolAssetWeights(base []PoolAsset, other []PoolAsset) []PoolAsset { + weightDifference := make([]PoolAsset, len(base)) + // TODO: Consider deleting these panics for performance + if len(base) != len(other) { + panic("subPoolAssetWeights called with invalid input, len(base) != len(other)") + } + for i, asset := range base { + if asset.Token.Denom != other[i].Token.Denom { + panic(fmt.Sprintf("subPoolAssetWeights called with invalid input, "+ + "expected other's %vth asset to be %v, got %v", + i, asset.Token.Denom, other[i].Token.Denom)) + } + curWeightDiff := asset.Weight.Sub(other[i].Weight) + weightDifference[i] = PoolAsset{Token: asset.Token, Weight: curWeightDiff} + } + return weightDifference +} + +// addPoolAssetWeights adds the weights of two different pool asset slices. +// It assumes that both pool assets have the same token denominations, +// with the denominations in the same order. +// Returned weights can be negative. +func addPoolAssetWeights(base []PoolAsset, other []PoolAsset) []PoolAsset { + weightSum := make([]PoolAsset, len(base)) + // TODO: Consider deleting these panics for performance + if len(base) != len(other) { + panic("addPoolAssetWeights called with invalid input, len(base) != len(other)") + } + for i, asset := range base { + if asset.Token.Denom != other[i].Token.Denom { + panic(fmt.Sprintf("addPoolAssetWeights called with invalid input, "+ + "expected other's %vth asset to be %v, got %v", + i, asset.Token.Denom, other[i].Token.Denom)) + } + curWeightSum := asset.Weight.Add(other[i].Weight) + weightSum[i] = PoolAsset{Token: asset.Token, Weight: curWeightSum} + } + return weightSum +} + +// assumes 0 < d < 1 +func poolAssetsMulDec(base []PoolAsset, d osmomath.Dec) []PoolAsset { + newWeights := make([]PoolAsset, len(base)) + for i, asset := range base { + // TODO: This can adversarially panic at the moment! (as can Pool.TotalWeight) + // Ensure this won't be able to panic in the future PR where we bound + // each assets weight, and add precision + newWeight := d.MulInt(asset.Weight).RoundInt() + newWeights[i] = PoolAsset{Token: asset.Token, Weight: newWeight} + } + return newWeights +} + +// ValidateUserSpecifiedWeight ensures that a weight that is provided from user-input anywhere +// for creating a pool obeys the expected guarantees. +// Namely, that the weight is in the range [1, MaxUserSpecifiedWeight) +func ValidateUserSpecifiedWeight(weight osmomath.Int) error { + if !weight.IsPositive() { + return errorsmod.Wrap(types.ErrNotPositiveWeight, weight.String()) + } + + if weight.GTE(MaxUserSpecifiedWeight) { + return errorsmod.Wrap(types.ErrWeightTooLarge, weight.String()) + } + return nil +} + +// solveConstantFunctionInvariant solves the constant function of an AMM +// that determines the relationship between the differences of two sides +// of assets inside the pool. +// For fixed balanceXBefore, balanceXAfter, weightX, balanceY, weightY, +// we could deduce the balanceYDelta, calculated by: +// balanceYDelta = balanceY * (1 - (balanceXBefore/balanceXAfter)^(weightX/weightY)) +// balanceYDelta is positive when the balance liquidity decreases. +// balanceYDelta is negative when the balance liquidity increases. +// +// panics if tokenWeightUnknown is 0. +func solveConstantFunctionInvariant( + tokenBalanceFixedBefore, + tokenBalanceFixedAfter, + tokenWeightFixed, + tokenBalanceUnknownBefore, + tokenWeightUnknown osmomath.Dec, +) osmomath.Dec { + // weightRatio = (weightX/weightY) + weightRatio := tokenWeightFixed.Quo(tokenWeightUnknown) + + // y = balanceXBefore/balanceXAfter + y := tokenBalanceFixedBefore.Quo(tokenBalanceFixedAfter) + + // amountY = balanceY * (1 - (y ^ weightRatio)) + yToWeightRatio := osmomath.Pow(y, weightRatio) + paranthetical := osmomath.OneDec().Sub(yToWeightRatio) + amountY := tokenBalanceUnknownBefore.Mul(paranthetical) + return amountY +} + +// balancer notation: pAo - pool shares amount out, given single asset in +// the second argument requires the tokenWeightIn / total token weight. +func calcPoolSharesOutGivenSingleAssetIn( + tokenBalanceIn, + normalizedTokenWeightIn, + poolShares, + tokenAmountIn, + spreadFactor osmomath.Dec, +) osmomath.Dec { + // deduct spread factor on the in asset. + // We don't charge spread factor on the token amount that we imagine as unswapped (the normalized weight). + // So effective_swapfee = spread factor * (1 - normalized_token_weight) + tokenAmountInAfterFee := tokenAmountIn.Mul(feeRatio(normalizedTokenWeightIn, spreadFactor)) + // To figure out the number of shares we add, first notice that in balancer we can treat + // the number of shares as linearly related to the `k` value function. This is due to the normalization. + // e.g. + // if x^.5 y^.5 = k, then we `n` x the liquidity to `(nx)^.5 (ny)^.5 = nk = k'` + // We generalize this linear relation to do the liquidity add for the not-all-asset case. + // Suppose we increase the supply of x by x', so we want to solve for `k'/k`. + // This is `(x + x')^{weight} * old_terms / (x^{weight} * old_terms) = (x + x')^{weight} / (x^{weight})` + // The number of new shares we need to make is then `old_shares * ((k'/k) - 1)` + // What very cool, is that this turns out to be the exact same `solveConstantFunctionInvariant` code + // with the answer's sign reversed. + poolAmountOut := solveConstantFunctionInvariant( + tokenBalanceIn.Add(tokenAmountInAfterFee), + tokenBalanceIn, + normalizedTokenWeightIn, + poolShares, + osmomath.OneDec()).Neg() + return poolAmountOut +} + +// getPoolAssetsByDenom return a mapping from pool asset +// denom to the pool asset itself. There must be no duplicates. +// Returns error, if any found. +func getPoolAssetsByDenom(poolAssets []PoolAsset) (map[string]PoolAsset, error) { + poolAssetsByDenom := make(map[string]PoolAsset) + for _, poolAsset := range poolAssets { + _, ok := poolAssetsByDenom[poolAsset.Token.Denom] + if ok { + return nil, fmt.Errorf(formatRepeatingPoolAssetsNotAllowedErrFormat, poolAsset.Token.Denom) + } + + poolAssetsByDenom[poolAsset.Token.Denom] = poolAsset + } + return poolAssetsByDenom, nil +} + +// updateIntermediaryPoolAssetsLiquidity updates poolAssetsByDenom with liquidity. +// +// all liquidity coins must exist in poolAssetsByDenom. Returns error, if not. +// +// This is a helper function that is useful for updating the pool asset amounts +// as an intermediary step in a multi-join methods such as CalcJoinPoolShares. +// In CalcJoinPoolShares with multi-asset joins, we first attempt to do +// a MaximalExactRatioJoin that might leave out some tokens in. +// Then, for every remaining tokens in, we attempt to do a single asset join. +// Since the first step (MaximalExactRatioJoin) affects the pool liqudity due to slippage, +// we would like to account for that in the subsequent steps of single asset join. +func updateIntermediaryPoolAssetsLiquidity(liquidity sdk.Coins, poolAssetsByDenom map[string]PoolAsset) error { + for _, coin := range liquidity { + poolAsset, ok := poolAssetsByDenom[coin.Denom] + if !ok { + return fmt.Errorf(failedInterimLiquidityUpdateErrFormat, coin.Denom) + } + + poolAsset.Token.Amount = poolAssetsByDenom[coin.Denom].Token.Amount.Add(coin.Amount) + poolAssetsByDenom[coin.Denom] = poolAsset + } + return nil +} + +// feeRatio returns the fee ratio that is defined as follows: +// 1 - ((1 - normalizedTokenWeightOut) * spreadFactor) +func feeRatio(normalizedWeight, spreadFactor osmomath.Dec) osmomath.Dec { + return osmomath.OneDec().Sub((osmomath.OneDec().Sub(normalizedWeight)).Mul(spreadFactor)) +} + +// calcSingleAssetInGivenPoolSharesOut returns token amount in with fee included +// given the swapped out shares amount, using solveConstantFunctionInvariant +func calcSingleAssetInGivenPoolSharesOut( + tokenBalanceIn, + normalizedTokenWeightIn, + totalPoolSharesSupply, + sharesAmountOut, + spreadFactor osmomath.Dec, +) osmomath.Dec { + // delta balanceIn is negative(tokens inside the pool increases) + // pool weight is always 1 + tokenAmountIn := solveConstantFunctionInvariant(totalPoolSharesSupply.Add(sharesAmountOut), totalPoolSharesSupply, osmomath.OneDec(), tokenBalanceIn, normalizedTokenWeightIn).Neg() + // deduct spread factor on the in asset + tokenAmountInFeeIncluded := tokenAmountIn.Quo(feeRatio(normalizedTokenWeightIn, spreadFactor)) + return tokenAmountInFeeIncluded +} + +// calcPoolSharesInGivenSingleAssetOut returns pool shares amount in, given single asset out. +// the returned shares in have the fee included in them. +// the second argument requires the tokenWeightOut / total token weight. +func calcPoolSharesInGivenSingleAssetOut( + tokenBalanceOut, + normalizedTokenWeightOut, + totalPoolSharesSupply, + tokenAmountOut, + spreadFactor, + exitFee osmomath.Dec, +) osmomath.Dec { + tokenAmountOutFeeIncluded := tokenAmountOut.Quo(feeRatio(normalizedTokenWeightOut, spreadFactor)) + + // delta poolSupply is positive(total pool shares decreases) + // pool weight is always 1 + sharesIn := solveConstantFunctionInvariant(tokenBalanceOut.Sub(tokenAmountOutFeeIncluded), tokenBalanceOut, normalizedTokenWeightOut, totalPoolSharesSupply, osmomath.OneDec()) + + // charge exit fee on the pool token side + // pAi = pAiAfterExitFee/(1-exitFee) + sharesInFeeIncluded := sharesIn.Quo(osmomath.OneDec().Sub(exitFee)) + return sharesInFeeIncluded +} + +// ensureDenomInPool check to make sure the input denoms exist in the provided pool asset map +func ensureDenomInPool(poolAssetsByDenom map[string]PoolAsset, tokensIn sdk.Coins) error { + for _, coin := range tokensIn { + _, ok := poolAssetsByDenom[coin.Denom] + if !ok { + return errorsmod.Wrapf(types.ErrDenomNotFoundInPool, invalidInputDenomsErrFormat, coin.Denom) + } + } + + return nil +} diff --git a/tests/interchaintest/osmosis/gamm/pool-models/balancer/balancerPool.pb.go b/tests/interchaintest/osmosis/gamm/pool-models/balancer/balancerPool.pb.go new file mode 100644 index 00000000..2a23e536 --- /dev/null +++ b/tests/interchaintest/osmosis/gamm/pool-models/balancer/balancerPool.pb.go @@ -0,0 +1,1524 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: osmosis/gamm/v1beta1/balancerPool.proto + +// this is a legacy package that requires additional migration logic +// in order to use the correct package. Decision made to use legacy package path +// until clear steps for migration logic and the unknowns for state breaking are +// investigated for changing proto package. + +package balancer + +import ( + cosmossdk_io_math "cosmossdk.io/math" + fmt "fmt" + _ "github.com/cosmos/cosmos-proto" + types "github.com/cosmos/cosmos-sdk/types" + _ "github.com/cosmos/cosmos-sdk/types/tx/amino" + _ "github.com/cosmos/cosmos-sdk/x/auth/types" + _ "github.com/cosmos/gogoproto/gogoproto" + proto "github.com/cosmos/gogoproto/proto" + github_com_cosmos_gogoproto_types "github.com/cosmos/gogoproto/types" + _ "google.golang.org/protobuf/types/known/durationpb" + _ "google.golang.org/protobuf/types/known/timestamppb" + io "io" + math "math" + math_bits "math/bits" + time "time" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf +var _ = time.Kitchen + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// Parameters for changing the weights in a balancer pool smoothly from +// a start weight and end weight over a period of time. +// Currently, the only smooth change supported is linear changing between +// the two weights, but more types may be added in the future. +// When these parameters are set, the weight w(t) for pool time `t` is the +// following: +// +// t <= start_time: w(t) = initial_pool_weights +// start_time < t <= start_time + duration: +// w(t) = initial_pool_weights + (t - start_time) * +// (target_pool_weights - initial_pool_weights) / (duration) +// t > start_time + duration: w(t) = target_pool_weights +type SmoothWeightChangeParams struct { + // The start time for beginning the weight change. + // If a parameter change / pool instantiation leaves this blank, + // it should be generated by the state_machine as the current time. + StartTime time.Time `protobuf:"bytes,1,opt,name=start_time,json=startTime,proto3,stdtime" json:"start_time" yaml:"start_time"` + // Duration for the weights to change over + Duration time.Duration `protobuf:"bytes,2,opt,name=duration,proto3,stdduration" json:"duration,omitempty" yaml:"duration"` + // The initial pool weights. These are copied from the pool's settings + // at the time of weight change instantiation. + // The amount PoolAsset.token.amount field is ignored if present, + // future type refactorings should just have a type with the denom & weight + // here. + InitialPoolWeights []PoolAsset `protobuf:"bytes,3,rep,name=initial_pool_weights,json=initialPoolWeights,proto3" json:"initial_pool_weights" yaml:"initial_pool_weights"` + // The target pool weights. The pool weights will change linearly with respect + // to time between start_time, and start_time + duration. The amount + // PoolAsset.token.amount field is ignored if present, future type + // refactorings should just have a type with the denom & weight here. + TargetPoolWeights []PoolAsset `protobuf:"bytes,4,rep,name=target_pool_weights,json=targetPoolWeights,proto3" json:"target_pool_weights" yaml:"target_pool_weights"` +} + +func (m *SmoothWeightChangeParams) Reset() { *m = SmoothWeightChangeParams{} } +func (m *SmoothWeightChangeParams) String() string { return proto.CompactTextString(m) } +func (*SmoothWeightChangeParams) ProtoMessage() {} +func (*SmoothWeightChangeParams) Descriptor() ([]byte, []int) { + return fileDescriptor_8bed8b78c08e572f, []int{0} +} +func (m *SmoothWeightChangeParams) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SmoothWeightChangeParams) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SmoothWeightChangeParams.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *SmoothWeightChangeParams) XXX_Merge(src proto.Message) { + xxx_messageInfo_SmoothWeightChangeParams.Merge(m, src) +} +func (m *SmoothWeightChangeParams) XXX_Size() int { + return m.Size() +} +func (m *SmoothWeightChangeParams) XXX_DiscardUnknown() { + xxx_messageInfo_SmoothWeightChangeParams.DiscardUnknown(m) +} + +var xxx_messageInfo_SmoothWeightChangeParams proto.InternalMessageInfo + +func (m *SmoothWeightChangeParams) GetStartTime() time.Time { + if m != nil { + return m.StartTime + } + return time.Time{} +} + +func (m *SmoothWeightChangeParams) GetDuration() time.Duration { + if m != nil { + return m.Duration + } + return 0 +} + +func (m *SmoothWeightChangeParams) GetInitialPoolWeights() []PoolAsset { + if m != nil { + return m.InitialPoolWeights + } + return nil +} + +func (m *SmoothWeightChangeParams) GetTargetPoolWeights() []PoolAsset { + if m != nil { + return m.TargetPoolWeights + } + return nil +} + +// PoolParams defined the parameters that will be managed by the pool +// governance in the future. This params are not managed by the chain +// governance. Instead they will be managed by the token holders of the pool. +// The pool's token holders are specified in future_pool_governor. +type PoolParams struct { + SwapFee cosmossdk_io_math.LegacyDec `protobuf:"bytes,1,opt,name=swap_fee,json=swapFee,proto3,customtype=cosmossdk.io/math.LegacyDec" json:"swap_fee" yaml:"swap_fee"` + // N.B.: exit fee is disabled during pool creation in x/poolmanager. While old + // pools can maintain a non-zero fee. No new pool can be created with non-zero + // fee anymore + ExitFee cosmossdk_io_math.LegacyDec `protobuf:"bytes,2,opt,name=exit_fee,json=exitFee,proto3,customtype=cosmossdk.io/math.LegacyDec" json:"exit_fee" yaml:"exit_fee"` + SmoothWeightChangeParams *SmoothWeightChangeParams `protobuf:"bytes,3,opt,name=smooth_weight_change_params,json=smoothWeightChangeParams,proto3" json:"smooth_weight_change_params,omitempty" yaml:"smooth_weight_change_params"` +} + +func (m *PoolParams) Reset() { *m = PoolParams{} } +func (m *PoolParams) String() string { return proto.CompactTextString(m) } +func (*PoolParams) ProtoMessage() {} +func (*PoolParams) Descriptor() ([]byte, []int) { + return fileDescriptor_8bed8b78c08e572f, []int{1} +} +func (m *PoolParams) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *PoolParams) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_PoolParams.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *PoolParams) XXX_Merge(src proto.Message) { + xxx_messageInfo_PoolParams.Merge(m, src) +} +func (m *PoolParams) XXX_Size() int { + return m.Size() +} +func (m *PoolParams) XXX_DiscardUnknown() { + xxx_messageInfo_PoolParams.DiscardUnknown(m) +} + +var xxx_messageInfo_PoolParams proto.InternalMessageInfo + +func (m *PoolParams) GetSmoothWeightChangeParams() *SmoothWeightChangeParams { + if m != nil { + return m.SmoothWeightChangeParams + } + return nil +} + +// Pool asset is an internal struct that combines the amount of the +// token in the pool, and its balancer weight. +// This is an awkward packaging of data, +// and should be revisited in a future state migration. +type PoolAsset struct { + // Coins we are talking about, + // the denomination must be unique amongst all PoolAssets for this pool. + Token types.Coin `protobuf:"bytes,1,opt,name=token,proto3" json:"token" yaml:"token"` + // Weight that is not normalized. This weight must be less than 2^50 + Weight cosmossdk_io_math.Int `protobuf:"bytes,2,opt,name=weight,proto3,customtype=cosmossdk.io/math.Int" json:"weight" yaml:"weight"` +} + +func (m *PoolAsset) Reset() { *m = PoolAsset{} } +func (m *PoolAsset) String() string { return proto.CompactTextString(m) } +func (*PoolAsset) ProtoMessage() {} +func (*PoolAsset) Descriptor() ([]byte, []int) { + return fileDescriptor_8bed8b78c08e572f, []int{2} +} +func (m *PoolAsset) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *PoolAsset) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_PoolAsset.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *PoolAsset) XXX_Merge(src proto.Message) { + xxx_messageInfo_PoolAsset.Merge(m, src) +} +func (m *PoolAsset) XXX_Size() int { + return m.Size() +} +func (m *PoolAsset) XXX_DiscardUnknown() { + xxx_messageInfo_PoolAsset.DiscardUnknown(m) +} + +var xxx_messageInfo_PoolAsset proto.InternalMessageInfo + +func (m *PoolAsset) GetToken() types.Coin { + if m != nil { + return m.Token + } + return types.Coin{} +} + +type Pool struct { + Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty" yaml:"address"` + Id uint64 `protobuf:"varint,2,opt,name=id,proto3" json:"id,omitempty"` + PoolParams PoolParams `protobuf:"bytes,3,opt,name=pool_params,json=poolParams,proto3" json:"pool_params" yaml:"balancer_pool_params"` + // This string specifies who will govern the pool in the future. + // Valid forms of this are: + // {token name},{duration} + // {duration} + // where {token name} if specified is the token which determines the + // governor, and if not specified is the LP token for this pool.duration is + // a time specified as 0w,1w,2w, etc. which specifies how long the token + // would need to be locked up to count in governance. 0w means no lockup. + // TODO: Further improve these docs + FuturePoolGovernor string `protobuf:"bytes,4,opt,name=future_pool_governor,json=futurePoolGovernor,proto3" json:"future_pool_governor,omitempty" yaml:"future_pool_governor"` + // sum of all LP tokens sent out + TotalShares types.Coin `protobuf:"bytes,5,opt,name=total_shares,json=totalShares,proto3" json:"total_shares" yaml:"total_shares"` + // These are assumed to be sorted by denomiation. + // They contain the pool asset and the information about the weight + PoolAssets []PoolAsset `protobuf:"bytes,6,rep,name=pool_assets,json=poolAssets,proto3" json:"pool_assets" yaml:"pool_assets"` + // sum of all non-normalized pool weights + TotalWeight cosmossdk_io_math.Int `protobuf:"bytes,7,opt,name=total_weight,json=totalWeight,proto3,customtype=cosmossdk.io/math.Int" json:"total_weight" yaml:"total_weight"` +} + +func (m *Pool) Reset() { *m = Pool{} } +func (*Pool) ProtoMessage() {} +func (*Pool) Descriptor() ([]byte, []int) { + return fileDescriptor_8bed8b78c08e572f, []int{3} +} +func (m *Pool) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Pool) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Pool.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Pool) XXX_Merge(src proto.Message) { + xxx_messageInfo_Pool.Merge(m, src) +} +func (m *Pool) XXX_Size() int { + return m.Size() +} +func (m *Pool) XXX_DiscardUnknown() { + xxx_messageInfo_Pool.DiscardUnknown(m) +} + +var xxx_messageInfo_Pool proto.InternalMessageInfo + +func init() { + proto.RegisterType((*SmoothWeightChangeParams)(nil), "osmosis.gamm.v1beta1.SmoothWeightChangeParams") + proto.RegisterType((*PoolParams)(nil), "osmosis.gamm.v1beta1.PoolParams") + proto.RegisterType((*PoolAsset)(nil), "osmosis.gamm.v1beta1.PoolAsset") + proto.RegisterType((*Pool)(nil), "osmosis.gamm.v1beta1.Pool") +} + +func init() { + proto.RegisterFile("osmosis/gamm/v1beta1/balancerPool.proto", fileDescriptor_8bed8b78c08e572f) +} + +var fileDescriptor_8bed8b78c08e572f = []byte{ + // 871 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x55, 0xcd, 0x8e, 0x1b, 0x45, + 0x10, 0xf6, 0x78, 0xbd, 0xbb, 0xd9, 0xde, 0x10, 0xb4, 0x1d, 0x23, 0x8d, 0x6d, 0xe1, 0x5e, 0x35, + 0x48, 0x44, 0x51, 0x76, 0x46, 0x6b, 0x10, 0x87, 0xbd, 0x20, 0x26, 0x21, 0x28, 0x12, 0x87, 0x30, + 0x41, 0x0a, 0x41, 0x48, 0xa3, 0xb6, 0xdd, 0x9e, 0x69, 0x65, 0x66, 0xda, 0x9a, 0x6e, 0x3b, 0xd9, + 0x37, 0x40, 0x9c, 0x72, 0x0c, 0x9c, 0xf2, 0x08, 0x1c, 0x78, 0x02, 0x4e, 0x2b, 0xb8, 0xe4, 0x88, + 0x38, 0x0c, 0x68, 0xf7, 0x80, 0xc4, 0xd1, 0xe2, 0x01, 0x50, 0xff, 0x8c, 0x7f, 0x36, 0xb6, 0x12, + 0x71, 0xb1, 0xa6, 0xaa, 0xab, 0xbe, 0xfa, 0xaa, 0xea, 0xeb, 0x36, 0xf8, 0x80, 0x8b, 0x8c, 0x0b, + 0x26, 0xfc, 0x98, 0x64, 0x99, 0x3f, 0x3d, 0xee, 0x53, 0x49, 0x8e, 0xfd, 0x3e, 0x49, 0x49, 0x3e, + 0xa0, 0xc5, 0x7d, 0xce, 0x53, 0x6f, 0x5c, 0x70, 0xc9, 0x61, 0xd3, 0x06, 0x7a, 0x2a, 0xd0, 0xb3, + 0x81, 0xed, 0xd6, 0x40, 0xbb, 0x23, 0x1d, 0xe3, 0x1b, 0xc3, 0x24, 0xb4, 0x9b, 0x31, 0x8f, 0xb9, + 0xf1, 0xab, 0x2f, 0xeb, 0x3d, 0x20, 0x19, 0xcb, 0xb9, 0xaf, 0x7f, 0xad, 0xab, 0x1b, 0x73, 0x1e, + 0xa7, 0xd4, 0xd7, 0x56, 0x7f, 0x32, 0xf2, 0x87, 0x93, 0x82, 0x48, 0xc6, 0x73, 0x7b, 0x8e, 0x2e, + 0x9f, 0x4b, 0x96, 0x51, 0x21, 0x49, 0x36, 0xae, 0x00, 0x4c, 0x5d, 0x9f, 0x4c, 0x64, 0x32, 0x6f, + 0x41, 0x19, 0x97, 0xce, 0xfb, 0x44, 0xd0, 0xf9, 0xf9, 0x80, 0x33, 0x5b, 0x00, 0xff, 0xb6, 0x05, + 0xdc, 0x07, 0x19, 0xe7, 0x32, 0x79, 0x48, 0x59, 0x9c, 0xc8, 0xdb, 0x09, 0xc9, 0x63, 0x7a, 0x9f, + 0x14, 0x24, 0x13, 0xf0, 0x6b, 0x00, 0x84, 0x24, 0x85, 0x8c, 0x54, 0x55, 0xd7, 0x39, 0x74, 0x6e, + 0xec, 0xf7, 0xda, 0x9e, 0xa1, 0xe4, 0x55, 0x94, 0xbc, 0xaf, 0x2a, 0x4a, 0xc1, 0xbb, 0x67, 0x25, + 0xaa, 0xcd, 0x4a, 0x74, 0x70, 0x4a, 0xb2, 0xf4, 0x04, 0x2f, 0x72, 0xf1, 0xb3, 0x3f, 0x91, 0x13, + 0xee, 0x69, 0x87, 0x0a, 0x87, 0x09, 0xb8, 0x52, 0x75, 0xea, 0xd6, 0x35, 0x6e, 0xeb, 0x15, 0xdc, + 0x3b, 0x36, 0x20, 0x38, 0x56, 0xb0, 0xff, 0x94, 0x08, 0x56, 0x29, 0xb7, 0x78, 0xc6, 0x24, 0xcd, + 0xc6, 0xf2, 0x74, 0x56, 0xa2, 0xb7, 0x4d, 0xb1, 0xea, 0x0c, 0x3f, 0x57, 0xa5, 0xe6, 0xe8, 0x70, + 0x0a, 0x9a, 0x2c, 0x67, 0x92, 0x91, 0x34, 0x1a, 0x73, 0x9e, 0x46, 0x4f, 0x74, 0x9b, 0xc2, 0xdd, + 0x3a, 0xdc, 0xba, 0xb1, 0xdf, 0x43, 0xde, 0xba, 0xd5, 0x7a, 0x6a, 0xf7, 0x9f, 0x0a, 0x41, 0x65, + 0xf0, 0x9e, 0x6d, 0xa9, 0x63, 0xaa, 0xac, 0x83, 0xc2, 0x21, 0xb4, 0x6e, 0x95, 0x66, 0xc6, 0x28, + 0xa0, 0x00, 0xd7, 0x25, 0x29, 0x62, 0x2a, 0x57, 0xcb, 0x36, 0xde, 0xac, 0x2c, 0xb6, 0x65, 0xdb, + 0xa6, 0xec, 0x1a, 0x24, 0x1c, 0x1e, 0x18, 0xef, 0x52, 0x51, 0xfc, 0x6f, 0x1d, 0x00, 0x65, 0xdb, + 0xfd, 0x7d, 0x09, 0xae, 0x88, 0x27, 0x64, 0x1c, 0x8d, 0xa8, 0xd9, 0xde, 0x5e, 0xf0, 0xb1, 0xc2, + 0xfd, 0xa3, 0x44, 0x1d, 0x23, 0x0b, 0x31, 0x7c, 0xec, 0x31, 0xee, 0x67, 0x44, 0x26, 0xde, 0x17, + 0x34, 0x26, 0x83, 0xd3, 0x3b, 0x74, 0xb0, 0x98, 0x69, 0x95, 0x8c, 0xc3, 0x5d, 0xf5, 0x79, 0x97, + 0x52, 0x05, 0x49, 0x9f, 0x32, 0xa9, 0x21, 0xeb, 0xff, 0x03, 0xb2, 0x4a, 0xc6, 0xe1, 0xae, 0xfa, + 0x54, 0x90, 0x3f, 0x38, 0xa0, 0x23, 0xb4, 0x04, 0x6d, 0x6f, 0xd1, 0x40, 0x8b, 0x30, 0x1a, 0xeb, + 0x2e, 0xdc, 0x2d, 0xad, 0x0f, 0x6f, 0xfd, 0xc8, 0x36, 0x69, 0x37, 0xb8, 0x79, 0x56, 0x22, 0x67, + 0x56, 0x22, 0x6c, 0x5b, 0xd9, 0x5c, 0x00, 0x87, 0xae, 0xd8, 0x80, 0x72, 0xf2, 0xfe, 0xf7, 0x7f, + 0xff, 0x74, 0x13, 0xad, 0xbc, 0x13, 0xc1, 0xd2, 0xfb, 0x60, 0xa2, 0xf0, 0x8f, 0x0e, 0xd8, 0x9b, + 0xef, 0x0e, 0x7e, 0x06, 0xb6, 0x25, 0x7f, 0x4c, 0x73, 0x7b, 0x61, 0x5a, 0x9e, 0x7d, 0x1a, 0xd4, + 0x15, 0x9c, 0xf3, 0xbe, 0xcd, 0x59, 0x1e, 0x34, 0xed, 0x96, 0xaf, 0xda, 0x2d, 0xab, 0x2c, 0x1c, + 0x9a, 0x6c, 0x78, 0x17, 0xec, 0x18, 0xb6, 0x76, 0xce, 0x9e, 0x9d, 0xf3, 0x3b, 0xaf, 0xce, 0xf9, + 0x5e, 0x2e, 0x67, 0x25, 0x7a, 0xcb, 0xa0, 0x98, 0x24, 0x1c, 0xda, 0x6c, 0xfc, 0x4b, 0x03, 0x34, + 0x14, 0x39, 0x78, 0x0b, 0xec, 0x92, 0xe1, 0xb0, 0xa0, 0x42, 0x58, 0x31, 0xc0, 0x59, 0x89, 0xae, + 0x99, 0x24, 0x7b, 0x80, 0xc3, 0x2a, 0x04, 0x5e, 0x03, 0x75, 0x36, 0xd4, 0xa5, 0x1b, 0x61, 0x9d, + 0x0d, 0xe1, 0x08, 0xec, 0x6b, 0xf9, 0xad, 0x2c, 0xe5, 0x70, 0xb3, 0x8e, 0xed, 0x1a, 0x2e, 0xdd, + 0x9f, 0xea, 0x71, 0x8d, 0x96, 0xb0, 0x70, 0x08, 0xc6, 0xcb, 0x9a, 0x6d, 0x8e, 0x26, 0x72, 0x52, + 0x50, 0x13, 0x12, 0xf3, 0x29, 0x2d, 0x72, 0x5e, 0xb8, 0x0d, 0x4d, 0x19, 0x2d, 0xa0, 0xd6, 0x45, + 0xe1, 0x10, 0x1a, 0xb7, 0x62, 0xf0, 0xb9, 0x75, 0xc2, 0x47, 0xe0, 0xaa, 0xe4, 0x92, 0xa4, 0x91, + 0x48, 0x48, 0x41, 0x85, 0xbb, 0xfd, 0xba, 0xbd, 0x74, 0x2c, 0xe9, 0xeb, 0xd5, 0x5e, 0x16, 0xc9, + 0x38, 0xdc, 0xd7, 0xe6, 0x03, 0x6d, 0xc1, 0x6f, 0xed, 0x54, 0x88, 0xda, 0xbc, 0x70, 0x77, 0xde, + 0xec, 0x76, 0xb7, 0x2d, 0x3e, 0x34, 0xf8, 0x4b, 0x08, 0x76, 0x16, 0x3a, 0x4c, 0xc0, 0x87, 0x15, + 0x71, 0x2b, 0x84, 0x5d, 0x3d, 0x83, 0x8f, 0x5e, 0x27, 0x84, 0x15, 0xda, 0x95, 0x1c, 0x0c, 0x6d, + 0x23, 0xf1, 0x13, 0xff, 0xbb, 0x17, 0xa8, 0xf6, 0xfc, 0x05, 0xaa, 0xfd, 0xfa, 0xf3, 0xd1, 0xb6, + 0xe2, 0x75, 0x4f, 0xe9, 0xbc, 0xb5, 0x51, 0xe7, 0xc1, 0xa3, 0xb3, 0xf3, 0xae, 0xf3, 0xf2, 0xbc, + 0xeb, 0xfc, 0x75, 0xde, 0x75, 0x9e, 0x5d, 0x74, 0x6b, 0x2f, 0x2f, 0xba, 0xb5, 0xdf, 0x2f, 0xba, + 0xb5, 0x6f, 0x3e, 0x89, 0x99, 0x4c, 0x26, 0x7d, 0x6f, 0xc0, 0x33, 0xdf, 0xe6, 0x1f, 0xa5, 0xa4, + 0x2f, 0x2a, 0xc3, 0x9f, 0xf6, 0x7a, 0xfe, 0x53, 0x03, 0xa9, 0x5a, 0x3b, 0xca, 0xf8, 0x90, 0xa6, + 0x62, 0xfe, 0x37, 0xdb, 0xdf, 0xd1, 0x0f, 0xfe, 0x87, 0xff, 0x05, 0x00, 0x00, 0xff, 0xff, 0x30, + 0xfc, 0xb1, 0x6b, 0x8e, 0x07, 0x00, 0x00, +} + +func (m *SmoothWeightChangeParams) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *SmoothWeightChangeParams) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SmoothWeightChangeParams) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.TargetPoolWeights) > 0 { + for iNdEx := len(m.TargetPoolWeights) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.TargetPoolWeights[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintBalancerPool(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + } + if len(m.InitialPoolWeights) > 0 { + for iNdEx := len(m.InitialPoolWeights) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.InitialPoolWeights[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintBalancerPool(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + } + n1, err1 := github_com_cosmos_gogoproto_types.StdDurationMarshalTo(m.Duration, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdDuration(m.Duration):]) + if err1 != nil { + return 0, err1 + } + i -= n1 + i = encodeVarintBalancerPool(dAtA, i, uint64(n1)) + i-- + dAtA[i] = 0x12 + n2, err2 := github_com_cosmos_gogoproto_types.StdTimeMarshalTo(m.StartTime, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdTime(m.StartTime):]) + if err2 != nil { + return 0, err2 + } + i -= n2 + i = encodeVarintBalancerPool(dAtA, i, uint64(n2)) + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *PoolParams) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *PoolParams) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *PoolParams) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.SmoothWeightChangeParams != nil { + { + size, err := m.SmoothWeightChangeParams.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintBalancerPool(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + { + size := m.ExitFee.Size() + i -= size + if _, err := m.ExitFee.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintBalancerPool(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + { + size := m.SwapFee.Size() + i -= size + if _, err := m.SwapFee.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintBalancerPool(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *PoolAsset) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *PoolAsset) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *PoolAsset) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size := m.Weight.Size() + i -= size + if _, err := m.Weight.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintBalancerPool(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + { + size, err := m.Token.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintBalancerPool(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *Pool) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Pool) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Pool) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size := m.TotalWeight.Size() + i -= size + if _, err := m.TotalWeight.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintBalancerPool(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x3a + if len(m.PoolAssets) > 0 { + for iNdEx := len(m.PoolAssets) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.PoolAssets[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintBalancerPool(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x32 + } + } + { + size, err := m.TotalShares.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintBalancerPool(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + if len(m.FuturePoolGovernor) > 0 { + i -= len(m.FuturePoolGovernor) + copy(dAtA[i:], m.FuturePoolGovernor) + i = encodeVarintBalancerPool(dAtA, i, uint64(len(m.FuturePoolGovernor))) + i-- + dAtA[i] = 0x22 + } + { + size, err := m.PoolParams.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintBalancerPool(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + if m.Id != 0 { + i = encodeVarintBalancerPool(dAtA, i, uint64(m.Id)) + i-- + dAtA[i] = 0x10 + } + if len(m.Address) > 0 { + i -= len(m.Address) + copy(dAtA[i:], m.Address) + i = encodeVarintBalancerPool(dAtA, i, uint64(len(m.Address))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintBalancerPool(dAtA []byte, offset int, v uint64) int { + offset -= sovBalancerPool(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *SmoothWeightChangeParams) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = github_com_cosmos_gogoproto_types.SizeOfStdTime(m.StartTime) + n += 1 + l + sovBalancerPool(uint64(l)) + l = github_com_cosmos_gogoproto_types.SizeOfStdDuration(m.Duration) + n += 1 + l + sovBalancerPool(uint64(l)) + if len(m.InitialPoolWeights) > 0 { + for _, e := range m.InitialPoolWeights { + l = e.Size() + n += 1 + l + sovBalancerPool(uint64(l)) + } + } + if len(m.TargetPoolWeights) > 0 { + for _, e := range m.TargetPoolWeights { + l = e.Size() + n += 1 + l + sovBalancerPool(uint64(l)) + } + } + return n +} + +func (m *PoolParams) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.SwapFee.Size() + n += 1 + l + sovBalancerPool(uint64(l)) + l = m.ExitFee.Size() + n += 1 + l + sovBalancerPool(uint64(l)) + if m.SmoothWeightChangeParams != nil { + l = m.SmoothWeightChangeParams.Size() + n += 1 + l + sovBalancerPool(uint64(l)) + } + return n +} + +func (m *PoolAsset) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Token.Size() + n += 1 + l + sovBalancerPool(uint64(l)) + l = m.Weight.Size() + n += 1 + l + sovBalancerPool(uint64(l)) + return n +} + +func (m *Pool) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Address) + if l > 0 { + n += 1 + l + sovBalancerPool(uint64(l)) + } + if m.Id != 0 { + n += 1 + sovBalancerPool(uint64(m.Id)) + } + l = m.PoolParams.Size() + n += 1 + l + sovBalancerPool(uint64(l)) + l = len(m.FuturePoolGovernor) + if l > 0 { + n += 1 + l + sovBalancerPool(uint64(l)) + } + l = m.TotalShares.Size() + n += 1 + l + sovBalancerPool(uint64(l)) + if len(m.PoolAssets) > 0 { + for _, e := range m.PoolAssets { + l = e.Size() + n += 1 + l + sovBalancerPool(uint64(l)) + } + } + l = m.TotalWeight.Size() + n += 1 + l + sovBalancerPool(uint64(l)) + return n +} + +func sovBalancerPool(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozBalancerPool(x uint64) (n int) { + return sovBalancerPool(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *SmoothWeightChangeParams) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBalancerPool + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: SmoothWeightChangeParams: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SmoothWeightChangeParams: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field StartTime", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBalancerPool + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthBalancerPool + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthBalancerPool + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := github_com_cosmos_gogoproto_types.StdTimeUnmarshal(&m.StartTime, dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Duration", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBalancerPool + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthBalancerPool + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthBalancerPool + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := github_com_cosmos_gogoproto_types.StdDurationUnmarshal(&m.Duration, dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field InitialPoolWeights", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBalancerPool + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthBalancerPool + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthBalancerPool + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.InitialPoolWeights = append(m.InitialPoolWeights, PoolAsset{}) + if err := m.InitialPoolWeights[len(m.InitialPoolWeights)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TargetPoolWeights", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBalancerPool + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthBalancerPool + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthBalancerPool + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.TargetPoolWeights = append(m.TargetPoolWeights, PoolAsset{}) + if err := m.TargetPoolWeights[len(m.TargetPoolWeights)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipBalancerPool(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthBalancerPool + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *PoolParams) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBalancerPool + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: PoolParams: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PoolParams: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SwapFee", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBalancerPool + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthBalancerPool + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthBalancerPool + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.SwapFee.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ExitFee", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBalancerPool + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthBalancerPool + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthBalancerPool + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.ExitFee.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SmoothWeightChangeParams", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBalancerPool + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthBalancerPool + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthBalancerPool + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.SmoothWeightChangeParams == nil { + m.SmoothWeightChangeParams = &SmoothWeightChangeParams{} + } + if err := m.SmoothWeightChangeParams.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipBalancerPool(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthBalancerPool + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *PoolAsset) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBalancerPool + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: PoolAsset: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PoolAsset: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Token", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBalancerPool + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthBalancerPool + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthBalancerPool + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Token.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Weight", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBalancerPool + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthBalancerPool + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthBalancerPool + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Weight.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipBalancerPool(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthBalancerPool + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Pool) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBalancerPool + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Pool: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Pool: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Address", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBalancerPool + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthBalancerPool + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthBalancerPool + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Address = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) + } + m.Id = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBalancerPool + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Id |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PoolParams", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBalancerPool + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthBalancerPool + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthBalancerPool + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.PoolParams.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FuturePoolGovernor", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBalancerPool + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthBalancerPool + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthBalancerPool + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.FuturePoolGovernor = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TotalShares", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBalancerPool + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthBalancerPool + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthBalancerPool + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.TotalShares.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PoolAssets", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBalancerPool + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthBalancerPool + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthBalancerPool + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.PoolAssets = append(m.PoolAssets, PoolAsset{}) + if err := m.PoolAssets[len(m.PoolAssets)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TotalWeight", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBalancerPool + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthBalancerPool + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthBalancerPool + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.TotalWeight.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipBalancerPool(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthBalancerPool + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipBalancerPool(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowBalancerPool + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowBalancerPool + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowBalancerPool + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthBalancerPool + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupBalancerPool + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthBalancerPool + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthBalancerPool = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowBalancerPool = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupBalancerPool = fmt.Errorf("proto: unexpected end of group") +) diff --git a/tests/interchaintest/osmosis/gamm/pool-models/balancer/codec.go b/tests/interchaintest/osmosis/gamm/pool-models/balancer/codec.go new file mode 100644 index 00000000..69e4c209 --- /dev/null +++ b/tests/interchaintest/osmosis/gamm/pool-models/balancer/codec.go @@ -0,0 +1,60 @@ +package balancer + +import ( + types "github.com/osmosis-labs/fee-abstraction/v7/tests/interchaintest/osmosis/gamm/types" + poolmanagertypes "github.com/osmosis-labs/fee-abstraction/v7/tests/interchaintest/osmosis/poolmanager/types" + + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/msgservice" + proto "github.com/cosmos/gogoproto/proto" +) + +// RegisterLegacyAminoCodec registers the necessary x/gamm interfaces and concrete types +// on the provided LegacyAmino codec. These types are used for Amino JSON serialization. +func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { + cdc.RegisterConcrete(&Pool{}, "osmosis/gamm/BalancerPool", nil) + cdc.RegisterConcrete(&MsgCreateBalancerPool{}, "osmosis/gamm/create-balancer-pool", nil) + cdc.RegisterConcrete(&PoolParams{}, "osmosis/gamm/BalancerPoolParams", nil) +} + +func RegisterInterfaces(registry codectypes.InterfaceRegistry) { + registry.RegisterInterface( + "osmosis.poolmanager.v1beta1.PoolI", + (*poolmanagertypes.PoolI)(nil), + &Pool{}, + ) + registry.RegisterInterface( + "osmosis.gamm.v1beta1.PoolI", // N.B.: the old proto-path is preserved for backwards-compatibility. + (*types.CFMMPoolI)(nil), + &Pool{}, + ) + registry.RegisterImplementations( + (*sdk.Msg)(nil), + &MsgCreateBalancerPool{}, + ) + registry.RegisterImplementations( + (*proto.Message)(nil), + &PoolParams{}, + ) + + msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc) +} + +var ( + amino = codec.NewLegacyAmino() + + // ModuleCdc references the global x/bank module codec. Note, the codec should + // ONLY be used in certain instances of tests and for JSON encoding as Amino is + // still used for that purpose. + // + // The actual codec used for serialization should be provided to x/staking and + // defined at the application level. + ModuleCdc = codec.NewAminoCodec(amino) +) + +func init() { + RegisterLegacyAminoCodec(amino) + amino.Seal() +} diff --git a/tests/interchaintest/osmosis/gamm/pool-models/balancer/constants.go b/tests/interchaintest/osmosis/gamm/pool-models/balancer/constants.go new file mode 100644 index 00000000..cb24ba93 --- /dev/null +++ b/tests/interchaintest/osmosis/gamm/pool-models/balancer/constants.go @@ -0,0 +1,22 @@ +package balancer + +import ( + "github.com/osmosis-labs/fee-abstraction/v7/tests/interchaintest/osmosis/osmomath" +) + +var ( + // Pool creators can specify a weight in [1, MaxUserSpecifiedWeight) + // for every token in the balancer pool. + // + // The weight used in the balancer equation is then creator-specified-weight * GuaranteedWeightPrecision. + // This is done so that LBP's / smooth weight changes can actually happen smoothly, + // without complex precision loss / edge effects. + MaxUserSpecifiedWeight osmomath.Int = osmomath.NewIntFromUint64(1 << 20) + // Scaling factor for every weight. The pool weight is: + // weight_in_MsgCreateBalancerPool * GuaranteedWeightPrecision + // + // This is done so that smooth weight changes have enough precision to actually be smooth. + GuaranteedWeightPrecision int64 = 1 << 30 + + PoolTypeName string = "Balancer" +) diff --git a/tests/interchaintest/osmosis/gamm/pool-models/balancer/doc.go b/tests/interchaintest/osmosis/gamm/pool-models/balancer/doc.go new file mode 100644 index 00000000..e8b33f88 --- /dev/null +++ b/tests/interchaintest/osmosis/gamm/pool-models/balancer/doc.go @@ -0,0 +1,6 @@ +/* +Package balancer implements weighted constant product AMMs, satisfying the AMM pool interface from +x/gamm/types. Please refer to the specification under /spec for further information, and the +Balancer projects documentation for more details on the mathematical equations. +*/ +package balancer diff --git a/tests/interchaintest/osmosis/gamm/pool-models/balancer/marshal.go b/tests/interchaintest/osmosis/gamm/pool-models/balancer/marshal.go new file mode 100644 index 00000000..bc98768d --- /dev/null +++ b/tests/interchaintest/osmosis/gamm/pool-models/balancer/marshal.go @@ -0,0 +1,65 @@ +package balancer + +import ( + "encoding/json" + + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/osmosis-labs/fee-abstraction/v7/tests/interchaintest/osmosis/osmomath" +) + +type balancerPoolPretty struct { + Address sdk.AccAddress `json:"address" yaml:"address"` + Id uint64 `json:"id" yaml:"id"` + PoolParams PoolParams `json:"pool_params" yaml:"pool_params"` + FuturePoolGovernor string `json:"future_pool_governor" yaml:"future_pool_governor"` + TotalWeight osmomath.Dec `json:"total_weight" yaml:"total_weight"` + TotalShares sdk.Coin `json:"total_shares" yaml:"total_shares"` + PoolAssets []PoolAsset `json:"pool_assets" yaml:"pool_assets"` +} + +func (p Pool) String() string { + out, err := p.MarshalJSON() + if err != nil { + panic(err) + } + return string(out) +} + +// MarshalJSON returns the JSON representation of a Pool. +func (p Pool) MarshalJSON() ([]byte, error) { + accAddr, err := sdk.AccAddressFromBech32(p.Address) + if err != nil { + return nil, err + } + + decTotalWeight := osmomath.NewDecFromInt(p.TotalWeight) + + return json.Marshal(balancerPoolPretty{ + Address: accAddr, + Id: p.Id, + PoolParams: p.PoolParams, + FuturePoolGovernor: p.FuturePoolGovernor, + TotalWeight: decTotalWeight, + TotalShares: p.TotalShares, + PoolAssets: p.PoolAssets, + }) +} + +// UnmarshalJSON unmarshals raw JSON bytes into a Pool. +func (p *Pool) UnmarshalJSON(bz []byte) error { + var alias balancerPoolPretty + if err := json.Unmarshal(bz, &alias); err != nil { + return err + } + + p.Address = alias.Address.String() + p.Id = alias.Id + p.PoolParams = alias.PoolParams + p.FuturePoolGovernor = alias.FuturePoolGovernor + p.TotalWeight = alias.TotalWeight.RoundInt() + p.TotalShares = alias.TotalShares + p.PoolAssets = alias.PoolAssets + + return nil +} diff --git a/tests/interchaintest/osmosis/gamm/pool-models/balancer/msgs.go b/tests/interchaintest/osmosis/gamm/pool-models/balancer/msgs.go new file mode 100644 index 00000000..7f3101c3 --- /dev/null +++ b/tests/interchaintest/osmosis/gamm/pool-models/balancer/msgs.go @@ -0,0 +1,106 @@ +package balancer + +import ( + errorsmod "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + + "github.com/osmosis-labs/fee-abstraction/v7/tests/interchaintest/osmosis/gamm/types" + poolmanagertypes "github.com/osmosis-labs/fee-abstraction/v7/tests/interchaintest/osmosis/poolmanager/types" +) + +const ( + TypeMsgCreateBalancerPool = "create_balancer_pool" +) + +var ( + _ sdk.Msg = &MsgCreateBalancerPool{} + _ poolmanagertypes.CreatePoolMsg = &MsgCreateBalancerPool{} +) + +func NewMsgCreateBalancerPool( + sender sdk.AccAddress, + poolParams PoolParams, + poolAssets []PoolAsset, + futurePoolGovernor string, +) MsgCreateBalancerPool { + return MsgCreateBalancerPool{ + Sender: sender.String(), + PoolParams: &poolParams, + PoolAssets: poolAssets, + FuturePoolGovernor: futurePoolGovernor, + } +} + +func (msg MsgCreateBalancerPool) Route() string { return types.RouterKey } +func (msg MsgCreateBalancerPool) Type() string { return TypeMsgCreateBalancerPool } +func (msg MsgCreateBalancerPool) ValidateBasic() error { + _, err := sdk.AccAddressFromBech32(msg.Sender) + if err != nil { + return errorsmod.Wrapf(sdkerrors.ErrInvalidAddress, "Invalid sender address (%s)", err) + } + + err = validateUserSpecifiedPoolAssets(msg.PoolAssets) + if err != nil { + return err + } + + err = msg.PoolParams.Validate(msg.PoolAssets) + if err != nil { + return err + } + + // validation for future owner + if err = types.ValidateFutureGovernor(msg.FuturePoolGovernor); err != nil { + return err + } + + return nil +} + +func (msg MsgCreateBalancerPool) GetSignBytes() []byte { + return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(&msg)) +} + +func (msg MsgCreateBalancerPool) GetSigners() []sdk.AccAddress { + sender, err := sdk.AccAddressFromBech32(msg.Sender) + if err != nil { + panic(err) + } + return []sdk.AccAddress{sender} +} + +/// Implement the CreatePoolMsg interface + +func (msg MsgCreateBalancerPool) PoolCreator() sdk.AccAddress { + sender, err := sdk.AccAddressFromBech32(msg.Sender) + if err != nil { + panic(err) + } + return sender +} + +func (msg MsgCreateBalancerPool) Validate(ctx sdk.Context) error { + return msg.ValidateBasic() +} + +func (msg MsgCreateBalancerPool) InitialLiquidity() sdk.Coins { + var coins sdk.Coins + for _, asset := range msg.PoolAssets { + coins = append(coins, asset.Token) + } + if coins == nil { + panic("InitialLiquidity coins is equal to nil - this shouldn't happen") + } + coins = coins.Sort() + return coins +} + +func (msg MsgCreateBalancerPool) CreatePool(ctx sdk.Context, poolID uint64) (poolmanagertypes.PoolI, error) { + poolI, err := NewBalancerPool(poolID, *msg.PoolParams, msg.PoolAssets, msg.FuturePoolGovernor, ctx.BlockTime()) + return &poolI, err +} + +func (msg MsgCreateBalancerPool) GetPoolType() poolmanagertypes.PoolType { + return poolmanagertypes.Balancer +} diff --git a/tests/interchaintest/osmosis/gamm/pool-models/balancer/pool.go b/tests/interchaintest/osmosis/gamm/pool-models/balancer/pool.go new file mode 100644 index 00000000..1b0622f3 --- /dev/null +++ b/tests/interchaintest/osmosis/gamm/pool-models/balancer/pool.go @@ -0,0 +1,990 @@ +package balancer + +import ( + "errors" + "fmt" + "sort" + "strings" + "time" + + errorsmod "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/osmosis-labs/fee-abstraction/v7/tests/interchaintest/osmosis/gamm/pool-models/internal/cfmm_common" + "github.com/osmosis-labs/fee-abstraction/v7/tests/interchaintest/osmosis/gamm/types" + "github.com/osmosis-labs/fee-abstraction/v7/tests/interchaintest/osmosis/osmomath" + poolmanagertypes "github.com/osmosis-labs/fee-abstraction/v7/tests/interchaintest/osmosis/poolmanager/types" +) + +//nolint:deadcode +const ( + nonPostiveSharesAmountErrFormat = "shares amount must be positive, was %s" + nonPostiveTokenAmountErrFormat = "token amount must be positive, was %s" + sharesLargerThanMaxErrFormat = "%s resulted shares is larger than the max amount of %s" + invalidInputDenomsErrFormat = "input denoms must already exist in the pool (%s)" + + failedInterimLiquidityUpdateErrFormat = "failed to update interim liquidity - pool asset %s does not exist" + formatRepeatingPoolAssetsNotAllowedErrFormat = "repeating pool assets not allowed, found %s" + formatNoPoolAssetFoundErrFormat = "can't find the PoolAsset (%s)" +) + +var ( + _ poolmanagertypes.PoolI = &Pool{} + _ types.PoolAmountOutExtension = &Pool{} + _ types.WeightedPoolExtension = &Pool{} + _ types.CFMMPoolI = &Pool{} +) + +// NewPool returns a weighted CPMM pool with the provided parameters, and initial assets. +// Invariants that are assumed to be satisfied and not checked: +// (This is handled in ValidateBasic) +// * 2 <= len(assets) <= 8 +// * FutureGovernor is valid +// * poolID doesn't already exist +func NewBalancerPool(poolId uint64, balancerPoolParams PoolParams, assets []PoolAsset, futureGovernor string, blockTime time.Time) (Pool, error) { + poolAddr := poolmanagertypes.NewPoolAddress(poolId) + + // pool that created up to ensuring the assets and params are valid. + // We assume that FuturePoolGovernor is valid. + pool := &Pool{ + Address: poolAddr.String(), + Id: poolId, + PoolParams: PoolParams{}, + TotalWeight: osmomath.ZeroInt(), + TotalShares: sdk.NewCoin(types.GetPoolShareDenom(poolId), types.InitPoolSharesSupply), + PoolAssets: nil, + FuturePoolGovernor: futureGovernor, + } + + err := pool.SetInitialPoolAssets(assets) + if err != nil { + return Pool{}, err + } + + sortedPoolAssets := pool.GetAllPoolAssets() + err = balancerPoolParams.Validate(sortedPoolAssets) + if err != nil { + return Pool{}, err + } + + err = pool.setInitialPoolParams(balancerPoolParams, sortedPoolAssets, blockTime) + if err != nil { + return Pool{}, err + } + + return *pool, nil +} + +// GetAddress returns the address of a pool. +// If the pool address is not bech32 valid, it returns an empty address. +func (p Pool) GetAddress() sdk.AccAddress { + addr, err := sdk.AccAddressFromBech32(p.Address) + if err != nil { + panic(fmt.Sprintf("could not bech32 decode address of pool with id: %d", p.GetId())) + } + return addr +} + +func (p Pool) GetId() uint64 { + return p.Id +} + +func (p Pool) GetSpreadFactor(_ sdk.Context) osmomath.Dec { + return p.PoolParams.SwapFee +} + +func (p Pool) GetTotalPoolLiquidity(_ sdk.Context) sdk.Coins { + return poolAssetsCoins(p.PoolAssets) +} + +func (p Pool) GetExitFee(_ sdk.Context) osmomath.Dec { + return p.PoolParams.ExitFee +} + +func (p Pool) GetPoolParams() PoolParams { + return p.PoolParams +} + +func (p Pool) GetTotalWeight() osmomath.Int { + return p.TotalWeight +} + +func (p Pool) GetTotalShares() osmomath.Int { + return p.TotalShares.Amount +} + +func (p *Pool) AddTotalShares(amt osmomath.Int) { + p.TotalShares.Amount = p.TotalShares.Amount.Add(amt) +} + +func (p *Pool) SubTotalShares(amt osmomath.Int) { + p.TotalShares.Amount = p.TotalShares.Amount.Sub(amt) +} + +// SetInitialPoolAssets sets the PoolAssets in the pool. It is only designed to +// be called at the pool's creation. If the same denom's PoolAsset exists, will +// return error. +// +// The list of PoolAssets must be sorted. This is done to enable fast searching +// for a PoolAsset by denomination. +// TODO: Unify story for validation of []PoolAsset, some is here, some is in +// CreatePool.ValidateBasic() +func (p *Pool) SetInitialPoolAssets(PoolAssets []PoolAsset) error { + exists := make(map[string]bool) + for _, asset := range p.PoolAssets { + exists[asset.Token.Denom] = true + } + + newTotalWeight := p.TotalWeight + scaledPoolAssets := make([]PoolAsset, 0, len(PoolAssets)) + + // TODO: Refactor this into PoolAsset.validate() + for _, asset := range PoolAssets { + if asset.Token.Amount.LTE(osmomath.ZeroInt()) { + return fmt.Errorf("can't add the zero or negative balance of token") + } + + err := asset.validateWeight() + if err != nil { + return err + } + + if exists[asset.Token.Denom] { + return fmt.Errorf("same PoolAsset already exists") + } + exists[asset.Token.Denom] = true + + // Scale weight from the user provided weight to the correct internal weight + asset.Weight = asset.Weight.MulRaw(GuaranteedWeightPrecision) + scaledPoolAssets = append(scaledPoolAssets, asset) + newTotalWeight = newTotalWeight.Add(asset.Weight) + } + + // TODO: Change this to a more efficient sorted insert algorithm. + // Furthermore, consider changing the underlying data type to allow in-place modification if the + // number of PoolAssets is expected to be large. + p.PoolAssets = append(p.PoolAssets, scaledPoolAssets...) + sortPoolAssetsByDenom(p.PoolAssets) + + p.TotalWeight = newTotalWeight + + return nil +} + +// setInitialPoolParams +func (p *Pool) setInitialPoolParams(params PoolParams, sortedAssets []PoolAsset, curBlockTime time.Time) error { + p.PoolParams = params + if params.SmoothWeightChangeParams != nil { + // set initial assets + initialWeights := make([]PoolAsset, len(sortedAssets)) + for i, v := range sortedAssets { + initialWeights[i] = PoolAsset{ + Weight: v.Weight, + Token: sdk.Coin{Denom: v.Token.Denom, Amount: osmomath.ZeroInt()}, + } + } + params.SmoothWeightChangeParams.InitialPoolWeights = initialWeights + + // sort target weights by denom + targetPoolWeights := params.SmoothWeightChangeParams.TargetPoolWeights + sortPoolAssetsByDenom(targetPoolWeights) + + // scale target pool weights by GuaranteedWeightPrecision + for i, v := range targetPoolWeights { + err := ValidateUserSpecifiedWeight(v.Weight) + if err != nil { + return err + } + p.PoolParams.SmoothWeightChangeParams.TargetPoolWeights[i] = PoolAsset{ + Weight: v.Weight.MulRaw(GuaranteedWeightPrecision), + Token: v.Token, + } + } + + // Set start time if not present. + if params.SmoothWeightChangeParams.StartTime.Unix() <= 0 { + // Per https://golang.org/pkg/time/#Time.Unix, should be timezone independent + params.SmoothWeightChangeParams.StartTime = time.Unix(curBlockTime.Unix(), 0) + } + } + + return nil +} + +// GetPoolAssets returns the denom's PoolAsset, If the PoolAsset doesn't exist, will return error. +// As above, it will search the denom's PoolAsset by using binary search. +// So, it is important to make sure that the PoolAssets are sorted. +func (p Pool) GetPoolAsset(denom string) (PoolAsset, error) { + _, asset, err := p.getPoolAssetAndIndex(denom) + return asset, err +} + +// Returns a pool asset, and its index. If err != nil, then the index will be valid. +func (p Pool) getPoolAssetAndIndex(denom string) (int, PoolAsset, error) { + if denom == "" { + return -1, PoolAsset{}, fmt.Errorf("you tried to find the PoolAsset with empty denom") + } + + if len(p.PoolAssets) == 0 { + return -1, PoolAsset{}, errorsmod.Wrapf(types.ErrDenomNotFoundInPool, fmt.Sprintf(formatNoPoolAssetFoundErrFormat, denom)) + } + + i := sort.Search(len(p.PoolAssets), func(i int) bool { + PoolAssetA := p.PoolAssets[i] + + compare := strings.Compare(PoolAssetA.Token.Denom, denom) + return compare >= 0 + }) + + if i < 0 || i >= len(p.PoolAssets) { + return -1, PoolAsset{}, errorsmod.Wrapf(types.ErrDenomNotFoundInPool, fmt.Sprintf(formatNoPoolAssetFoundErrFormat, denom)) + } + + if p.PoolAssets[i].Token.Denom != denom { + return -1, PoolAsset{}, errorsmod.Wrapf(types.ErrDenomNotFoundInPool, fmt.Sprintf(formatNoPoolAssetFoundErrFormat, denom)) + } + + return i, p.PoolAssets[i], nil +} + +func (p Pool) parsePoolAssetsByDenoms(tokenADenom, tokenBDenom string) ( + Aasset PoolAsset, Basset PoolAsset, err error, +) { + Aasset, found1 := getPoolAssetByDenom(p.PoolAssets, tokenADenom) + Basset, found2 := getPoolAssetByDenom(p.PoolAssets, tokenBDenom) + + if !found1 { + return PoolAsset{}, PoolAsset{}, fmt.Errorf("(%s) does not exist in the pool", tokenADenom) + } + if !found2 { + return PoolAsset{}, PoolAsset{}, fmt.Errorf("(%s) does not exist in the pool", tokenBDenom) + } + return Aasset, Basset, nil +} + +func (p Pool) parsePoolAssets(tokensA sdk.Coins, tokenBDenom string) ( + tokenA sdk.Coin, Aasset PoolAsset, Basset PoolAsset, err error, +) { + if len(tokensA) != 1 { + return tokenA, Aasset, Basset, errors.New("expected tokensB to be of length one") + } + Aasset, Basset, err = p.parsePoolAssetsByDenoms(tokensA[0].Denom, tokenBDenom) + if err != nil { + return sdk.Coin{}, PoolAsset{}, PoolAsset{}, err + } + return tokensA[0], Aasset, Basset, nil +} + +func (p Pool) parsePoolAssetsCoins(tokensA sdk.Coins, tokensB sdk.Coins) ( + Aasset PoolAsset, Basset PoolAsset, err error, +) { + if len(tokensB) != 1 { + return Aasset, Basset, errors.New("expected tokensA to be of length one") + } + _, Aasset, Basset, err = p.parsePoolAssets(tokensA, tokensB[0].Denom) + return Aasset, Basset, err +} + +func (p *Pool) IncreaseLiquidity(sharesOut osmomath.Int, coinsIn sdk.Coins) { + err := p.addToPoolAssetBalances(coinsIn) + if err != nil { + panic(err) + } + p.AddTotalShares(sharesOut) +} + +func (p *Pool) UpdatePoolAssetBalance(coin sdk.Coin) error { + // Check that PoolAsset exists. + assetIndex, existingAsset, err := p.getPoolAssetAndIndex(coin.Denom) + if err != nil { + return err + } + + if coin.Amount.LTE(osmomath.ZeroInt()) { + return fmt.Errorf("can't set the pool's balance of a token to be zero or negative") + } + + // Update the supply of the asset + existingAsset.Token = coin + p.PoolAssets[assetIndex] = existingAsset + return nil +} + +func (p *Pool) UpdatePoolAssetBalances(coins sdk.Coins) error { + // Ensures that there are no duplicate denoms, all denom's are valid, + // and amount is > 0 + err := coins.Validate() + if err != nil { + return fmt.Errorf("provided coins are invalid, %v", err) + } + + for _, coin := range coins { + // TODO: We may be able to make this log(|coins|) faster in how it + // looks up denom -> Coin by doing a multi-search, + // but as we don't anticipate |coins| to be large, we omit this. + err = p.UpdatePoolAssetBalance(coin) + if err != nil { + return err + } + } + + return nil +} + +func (p *Pool) addToPoolAssetBalances(coins sdk.Coins) error { + for _, coin := range coins { + i, poolAsset, err := p.getPoolAssetAndIndex(coin.Denom) + if err != nil { + return err + } + poolAsset.Token.Amount = poolAsset.Token.Amount.Add(coin.Amount) + p.PoolAssets[i] = poolAsset + } + return nil +} + +func (p Pool) GetPoolAssets(denoms ...string) ([]PoolAsset, error) { + result := make([]PoolAsset, 0, len(denoms)) + + for _, denom := range denoms { + PoolAsset, err := p.GetPoolAsset(denom) + if err != nil { + return nil, err + } + + result = append(result, PoolAsset) + } + + return result, nil +} + +func (p Pool) GetAllPoolAssets() []PoolAsset { + copyslice := make([]PoolAsset, len(p.PoolAssets)) + copy(copyslice, p.PoolAssets) + return copyslice +} + +// updateAllWeights updates all of the pool's internal weights to be equal to +// the new weights. It assumes that `newWeights` are sorted by denomination, +// and only contain the same denominations as the pool already contains. +// This does not affect the asset balances. +// If any of the above are not satisfied, this will panic. +// (As all input to this should be generated from the state machine) +// TODO: (post-launch) If newWeights includes a new denomination, +// add the balance as well to the pool's internal measurements. +// TODO: (post-launch) If newWeights excludes an existing denomination, +// remove the weight from the pool, and figure out something to do +// with any remaining coin. +func (p *Pool) updateAllWeights(newWeights []PoolAsset) { + if len(p.PoolAssets) != len(newWeights) { + panic("updateAllWeights called with invalid input, len(newWeights) != len(existingWeights)") + } + totalWeight := osmomath.ZeroInt() + for i, asset := range p.PoolAssets { + if asset.Token.Denom != newWeights[i].Token.Denom { + panic(fmt.Sprintf("updateAllWeights called with invalid input, "+ + "expected new weights' %vth asset to be %v, got %v", + i, asset.Token.Denom, newWeights[i].Token.Denom)) + } + err := newWeights[i].validateWeight() + if err != nil { + panic("updateAllWeights: Tried to set an invalid weight") + } + p.PoolAssets[i].Weight = newWeights[i].Weight + totalWeight = totalWeight.Add(p.PoolAssets[i].Weight) + } + p.TotalWeight = totalWeight +} + +// PokePool checks to see if the pool's token weights need to be updated, and +// if so, does so. Currently doesn't do anything outside out LBPs. +func (p *Pool) PokePool(blockTime time.Time) { + // check if pool weights didn't change + poolWeightsChanging := p.PoolParams.SmoothWeightChangeParams != nil + if !poolWeightsChanging { + return + } + + params := *p.PoolParams.SmoothWeightChangeParams + + // The weights w(t) for the pool at time `t` is defined in one of three + // possible ways: + // + // 1. t <= start_time: w(t) = initial_pool_weights + // + // 2. start_time < t <= start_time + duration: + // w(t) = initial_pool_weights + (t - start_time) * + // (target_pool_weights - initial_pool_weights) / (duration) + // + // 3. t > start_time + duration: w(t) = target_pool_weights + switch { + case blockTime.Before(params.StartTime) || params.StartTime.Equal(blockTime): + // case 1: t <= start_time + return + + case blockTime.After(params.StartTime.Add(params.Duration)): + // case 2: start_time < t <= start_time + duration: + + // Update weights to be the target weights. + // + // TODO: When we add support for adding new assets via this method, ensure + // the new asset has some token sent with it. + p.updateAllWeights(params.TargetPoolWeights) + + // we've finished updating the weights, so reset the following fields + p.PoolParams.SmoothWeightChangeParams = nil + return + + default: + // case 3: t > start_time + duration: w(t) = target_pool_weights + + shiftedBlockTime := blockTime.Sub(params.StartTime).Milliseconds() + percentDurationElapsed := osmomath.NewDec(shiftedBlockTime).QuoInt64(params.Duration.Milliseconds()) + + // If the duration elapsed is equal to the total time, or a rounding error + // makes it seem like it is, just set to target weight. + if percentDurationElapsed.GTE(osmomath.OneDec()) { + p.updateAllWeights(params.TargetPoolWeights) + return + } + + // below will be auto-truncated according to internal weight precision routine + totalWeightsDiff := subPoolAssetWeights(params.TargetPoolWeights, params.InitialPoolWeights) + scaledDiff := poolAssetsMulDec(totalWeightsDiff, percentDurationElapsed) + updatedWeights := addPoolAssetWeights(params.InitialPoolWeights, scaledDiff) + + p.updateAllWeights(updatedWeights) + } +} + +func (p Pool) GetTokenWeight(denom string) (osmomath.Int, error) { + PoolAsset, err := p.GetPoolAsset(denom) + if err != nil { + return osmomath.Int{}, err + } + + return PoolAsset.Weight, nil +} + +func (p Pool) GetTokenBalance(denom string) (osmomath.Int, error) { + PoolAsset, err := p.GetPoolAsset(denom) + if err != nil { + return osmomath.Int{}, err + } + + return PoolAsset.Token.Amount, nil +} + +func (p Pool) NumAssets() int { + return len(p.PoolAssets) +} + +func (p Pool) IsActive(ctx sdk.Context) bool { + return true +} + +func (p Pool) GetType() poolmanagertypes.PoolType { + return poolmanagertypes.Balancer +} + +// CalcOutAmtGivenIn calculates tokens to be swapped out given the provided +// amount and fee deducted, using solveConstantFunctionInvariant. +func (p Pool) CalcOutAmtGivenIn( + ctx sdk.Context, + tokensIn sdk.Coins, + tokenOutDenom string, + spreadFactor osmomath.Dec, +) (sdk.Coin, error) { + tokenIn, poolAssetIn, poolAssetOut, err := p.parsePoolAssets(tokensIn, tokenOutDenom) + if err != nil { + return sdk.Coin{}, err + } + + tokenAmountInAfterFee := tokenIn.Amount.ToLegacyDec().Mul(osmomath.OneDec().Sub(spreadFactor)) + poolTokenInBalance := poolAssetIn.Token.Amount.ToLegacyDec() + poolPostSwapInBalance := poolTokenInBalance.Add(tokenAmountInAfterFee) + + // deduct spread factor on the tokensIn + // delta balanceOut is positive(tokens inside the pool decreases) + tokenAmountOut := solveConstantFunctionInvariant( + poolTokenInBalance, + poolPostSwapInBalance, + poolAssetIn.Weight.ToLegacyDec(), + poolAssetOut.Token.Amount.ToLegacyDec(), + poolAssetOut.Weight.ToLegacyDec(), + ) + + // We ignore the decimal component, as we round down the token amount out. + tokenAmountOutInt := tokenAmountOut.TruncateInt() + if !tokenAmountOutInt.IsPositive() { + return sdk.Coin{}, errorsmod.Wrapf(types.ErrInvalidMathApprox, "token amount must be positive") + } + + return sdk.NewCoin(tokenOutDenom, tokenAmountOutInt), nil +} + +// SwapOutAmtGivenIn is a mutative method for CalcOutAmtGivenIn, which includes the actual swap. +func (p *Pool) SwapOutAmtGivenIn( + ctx sdk.Context, + tokensIn sdk.Coins, + tokenOutDenom string, + spreadFactor osmomath.Dec, +) ( + tokenOut sdk.Coin, err error, +) { + tokenOutCoin, err := p.CalcOutAmtGivenIn(ctx, tokensIn, tokenOutDenom, spreadFactor) + if err != nil { + return sdk.Coin{}, err + } + + err = p.applySwap(ctx, tokensIn, sdk.Coins{tokenOutCoin}) + if err != nil { + return sdk.Coin{}, err + } + return tokenOutCoin, nil +} + +// CalcInAmtGivenOut calculates token to be provided, fee added, +// given the swapped out amount, using solveConstantFunctionInvariant. +func (p Pool) CalcInAmtGivenOut( + ctx sdk.Context, tokensOut sdk.Coins, tokenInDenom string, spreadFactor osmomath.Dec) ( + tokenIn sdk.Coin, err error, +) { + tokenOut, poolAssetOut, poolAssetIn, err := p.parsePoolAssets(tokensOut, tokenInDenom) + if err != nil { + return sdk.Coin{}, err + } + + // delta balanceOut is positive(tokens inside the pool decreases) + poolTokenOutBalance := poolAssetOut.Token.Amount.ToLegacyDec() + poolPostSwapOutBalance := poolTokenOutBalance.Sub(tokenOut.Amount.ToLegacyDec()) + // (x_0)(y_0) = (x_0 + in)(y_0 - out) + tokenAmountIn := solveConstantFunctionInvariant( + poolTokenOutBalance, poolPostSwapOutBalance, poolAssetOut.Weight.ToLegacyDec(), + poolAssetIn.Token.Amount.ToLegacyDec(), poolAssetIn.Weight.ToLegacyDec()).Neg() + + // We deduct a spread factor on the input asset. The swap happens by following the invariant curve on the input * (1 - spread factor) + // and then the spread factor is added to the pool. + // Thus in order to give X amount out, we solve the invariant for the invariant input. However invariant input = (1 - spread factor) * trade input. + // Therefore we divide by (1 - spread factor) here + tokenAmountInBeforeFee := tokenAmountIn.Quo(osmomath.OneDec().Sub(spreadFactor)) + + // We round up tokenInAmt, as this is what charged for the swap, for the precise amount out. + // Otherwise, the pool would under-charge by this rounding error. + tokenInAmt := tokenAmountInBeforeFee.Ceil().TruncateInt() + + if !tokenInAmt.IsPositive() { + return sdk.Coin{}, errorsmod.Wrapf(types.ErrInvalidMathApprox, "token amount must be positive") + } + return sdk.NewCoin(tokenInDenom, tokenInAmt), nil +} + +// SwapInAmtGivenOut is a mutative method for CalcOutAmtGivenIn, which includes the actual swap. +func (p *Pool) SwapInAmtGivenOut( + ctx sdk.Context, tokensOut sdk.Coins, tokenInDenom string, spreadFactor osmomath.Dec) ( + tokenIn sdk.Coin, err error, +) { + tokenInCoin, err := p.CalcInAmtGivenOut(ctx, tokensOut, tokenInDenom, spreadFactor) + if err != nil { + return sdk.Coin{}, err + } + + err = p.applySwap(ctx, sdk.Coins{tokenInCoin}, tokensOut) + if err != nil { + return sdk.Coin{}, err + } + return tokenInCoin, nil +} + +// ApplySwap. +func (p *Pool) applySwap(ctx sdk.Context, tokensIn sdk.Coins, tokensOut sdk.Coins) error { + // Fixed gas consumption per swap to prevent spam + ctx.GasMeter().ConsumeGas(types.BalancerGasFeeForSwap, "balancer swap computation") + // Also ensures that len(tokensIn) = 1 = len(tokensOut) + inPoolAsset, outPoolAsset, err := p.parsePoolAssetsCoins(tokensIn, tokensOut) + if err != nil { + return err + } + inPoolAsset.Token.Amount = inPoolAsset.Token.Amount.Add(tokensIn[0].Amount) + outPoolAsset.Token.Amount = outPoolAsset.Token.Amount.Sub(tokensOut[0].Amount) + + return p.UpdatePoolAssetBalances(sdk.NewCoins( + inPoolAsset.Token, + outPoolAsset.Token, + )) +} + +// SpotPrice returns the spot price of the pool +// This is the weight-adjusted balance of the tokens in the pool. +// To reduce the propagated effect of incorrect trailing digits, +// we take the ratio of weights and divide this by ratio of supplies +// this is equivalent to spot_price = (Quote Supply / Quote Weight) / (Base Supply / Base Weight) +// +// As an example, assume equal weights. uosmo supply of 2 and uatom supply of 4. +// +// Case 1: base = uosmo, quote = uatom -> for one uosmo, get 2 uatom = 4 / 2 = 2 +// In other words, it costs 2 uatom to get one uosmo. +// +// Case 2: base = uatom, quote = uosmo -> for one uatom, get 0.5 uosmo = 2 / 4 = 0.5 +// In other words, it costs 0.5 uosmo to get one uatom. +// +// panics if the pool in state is incorrect, and has any weight that is 0. +func (p Pool) SpotPrice(ctx sdk.Context, quoteAsset, baseAsset string) (spotPrice osmomath.BigDec, err error) { + quote, base, err := p.parsePoolAssetsByDenoms(quoteAsset, baseAsset) + if err != nil { + return osmomath.BigDec{}, err + } + if base.Weight.IsZero() || quote.Weight.IsZero() { + return osmomath.BigDec{}, errors.New("pool is misconfigured, got 0 weight") + } + + // spot_price = (Quote Supply / Quote Weight) / (Base Supply / Base Weight) + // = (Quote Supply / Quote Weight) * (Base Weight / Base Supply) + // = (Base Weight / Quote Weight) * (Quote Supply / Base Supply) + invWeightRatio := base.Weight.ToLegacyDec().Quo(quote.Weight.ToLegacyDec()) + supplyRatio := quote.Token.Amount.ToLegacyDec().Quo(base.Token.Amount.ToLegacyDec()) + spotPriceDec := supplyRatio.Mul(invWeightRatio) + + return osmomath.BigDecFromDec(spotPriceDec), err +} + +// calcPoolOutGivenSingleIn - balance pAo. +func (p *Pool) calcSingleAssetJoin(tokenIn sdk.Coin, spreadFactor osmomath.Dec, tokenInPoolAsset PoolAsset, totalShares osmomath.Int) (numShares osmomath.Int, err error) { + _, err = p.GetPoolAsset(tokenIn.Denom) + if err != nil { + return osmomath.ZeroInt(), err + } + + totalWeight := p.GetTotalWeight() + if totalWeight.IsZero() { + return osmomath.ZeroInt(), errors.New("pool misconfigured, total weight = 0") + } + normalizedWeight := tokenInPoolAsset.Weight.ToLegacyDec().Quo(totalWeight.ToLegacyDec()) + return calcPoolSharesOutGivenSingleAssetIn( + tokenInPoolAsset.Token.Amount.ToLegacyDec(), + normalizedWeight, + totalShares.ToLegacyDec(), + tokenIn.Amount.ToLegacyDec(), + spreadFactor, + ).TruncateInt(), nil +} + +// JoinPool calculates the number of shares needed given tokensIn with spreadFactor applied. +// It updates the liquidity if the pool is joined successfully. If not, returns error. +// and updates pool accordingly. +func (p *Pool) JoinPool(ctx sdk.Context, tokensIn sdk.Coins, spreadFactor osmomath.Dec) (numShares osmomath.Int, err error) { + numShares, newLiquidity, err := p.CalcJoinPoolShares(ctx, tokensIn, spreadFactor) + if err != nil { + return osmomath.Int{}, err + } + + // update pool with the calculated share and liquidity needed to join pool + p.IncreaseLiquidity(numShares, newLiquidity) + return numShares, nil +} + +// JoinPoolNoSwap calculates the number of shares needed for an all-asset join given tokensIn with spreadFactor applied. +// It updates the liquidity if the pool is joined successfully. If not, returns error. +func (p *Pool) JoinPoolNoSwap(ctx sdk.Context, tokensIn sdk.Coins, spreadFactor osmomath.Dec) (numShares osmomath.Int, err error) { + numShares, tokensJoined, err := p.CalcJoinPoolNoSwapShares(ctx, tokensIn, spreadFactor) + if err != nil { + return osmomath.Int{}, err + } + + // update pool with the calculated share and liquidity needed to join pool + p.IncreaseLiquidity(numShares, tokensJoined) + return numShares, nil +} + +// CalcJoinPoolShares calculates the number of shares created to join pool with the provided amount of `tokenIn`. +// The input tokens must either be: +// - a single token +// - contain exactly the same tokens as the pool contains +// +// It returns the number of shares created, the amount of coins actually joined into the pool +// (in case of not being able to fully join), or an error. +func (p *Pool) CalcJoinPoolShares(ctx sdk.Context, tokensIn sdk.Coins, spreadFactor osmomath.Dec) (numShares osmomath.Int, tokensJoined sdk.Coins, err error) { + // 1) Get pool current liquidity + and token weights + // 2) If single token provided, do single asset join and exit. + // 3) If multi-asset join, first do as much of a join as we can with no swaps. + // 4) Update pool shares / liquidity / remaining tokens to join accordingly + // 5) For every remaining token to LP, do a single asset join, and update pool shares / liquidity. + // + // Note that all single asset joins do incur spread factor. + // + // Since CalcJoinPoolShares is non-mutative, the steps for updating pool shares / liquidity are + // more complex / don't just alter the state. + // We should simplify this logic further in the future, using balancer multi-join equations. + + // 1) get all 'pool assets' (aka current pool liquidity + balancer weight) + poolAssetsByDenom, err := getPoolAssetsByDenom(p.GetAllPoolAssets()) + if err != nil { + return osmomath.ZeroInt(), sdk.NewCoins(), err + } + + // check to make sure the input denom exists in the pool + err = ensureDenomInPool(poolAssetsByDenom, tokensIn) + if err != nil { + return osmomath.ZeroInt(), sdk.NewCoins(), err + } + + totalShares := p.GetTotalShares() + if tokensIn.Len() == 1 { + // 2) Single token provided, so do single asset join and exit. + numShares, err = p.calcSingleAssetJoin(tokensIn[0], spreadFactor, poolAssetsByDenom[tokensIn[0].Denom], totalShares) + if err != nil { + return osmomath.ZeroInt(), sdk.NewCoins(), err + } + // we join all the tokens. + tokensJoined = tokensIn + return numShares, tokensJoined, nil + } else if tokensIn.Len() != p.NumAssets() { + return osmomath.ZeroInt(), sdk.NewCoins(), errors.New("balancer pool only supports LP'ing with one asset or all assets in pool") + } + + // 3) JoinPoolNoSwap with as many tokens as we can. (What is in perfect ratio) + // * numShares is how many shares are perfectly matched. + // * remainingTokensIn is how many coins we have left to join, that have not already been used. + // if remaining coins is empty, logic is done (we joined all tokensIn) + numShares, tokensJoined, err = p.CalcJoinPoolNoSwapShares(ctx, tokensIn, spreadFactor) + if err != nil { + return osmomath.ZeroInt(), sdk.NewCoins(), err + } + + // safely ends the calculation if all input tokens are successfully LP'd + if tokensJoined.IsAnyGT(tokensIn) { + return osmomath.ZeroInt(), sdk.NewCoins(), errors.New("an error has occurred, more coins joined than tokens passed in") + } else if tokensJoined.IsEqual(tokensIn) { + return numShares, tokensJoined, nil + } + + // 4) Still more coins to join, so we update our pool tracker map here to account for + // join that just happened. Importantly, this step does not actually change the pool state. + // Instead, it mutates the pool assets argument to be further used by the caller. + // * We add the joined coins to our "current pool liquidity" object (poolAssetsByDenom) + // * We increment a variable for our "newTotalShares" to add in the shares that've been added. + if err := updateIntermediaryPoolAssetsLiquidity(tokensJoined, poolAssetsByDenom); err != nil { + return osmomath.ZeroInt(), sdk.NewCoins(), err + } + newTotalShares := totalShares.Add(numShares) + + // 5) Now single asset join each remaining coin. + remainingTokensIn := tokensIn.Sub(tokensJoined...) + newNumSharesFromRemaining, newLiquidityFromRemaining, err := p.calcJoinSingleAssetTokensIn(remainingTokensIn, newTotalShares, poolAssetsByDenom, spreadFactor) + if err != nil { + return osmomath.ZeroInt(), sdk.NewCoins(), err + } + // update total amount LP'd variable, and total new LP shares variable, run safety check, and return + numShares = numShares.Add(newNumSharesFromRemaining) + tokensJoined = tokensJoined.Add(newLiquidityFromRemaining...) + + if tokensJoined.IsAnyGT(tokensIn) { + return osmomath.ZeroInt(), sdk.NewCoins(), errors.New("an error has occurred, more coins joined than token In") + } + + return numShares, tokensJoined, nil +} + +// CalcJoinPoolNoSwapShares calculates the number of shares created to execute an all-asset pool join with the provided amount of `tokensIn`. +// The input tokens must contain the same tokens as in the pool. +// +// Returns the number of shares created, the amount of coins actually joined into the pool, (in case of not being able to fully join), +// and the remaining tokens in `tokensIn` after joining. If an all-asset join is not possible, returns an error. +// +// Since CalcJoinPoolNoSwapShares is non-mutative, the steps for updating pool shares / liquidity are +// more complex / don't just alter the state. +// We should simplify this logic further in the future using multi-join equations. +func (p *Pool) CalcJoinPoolNoSwapShares(ctx sdk.Context, tokensIn sdk.Coins, spreadFactor osmomath.Dec) (numShares osmomath.Int, tokensJoined sdk.Coins, err error) { + // get all 'pool assets' (aka current pool liquidity + balancer weight) + poolAssetsByDenom, err := getPoolAssetsByDenom(p.GetAllPoolAssets()) + if err != nil { + return osmomath.ZeroInt(), sdk.NewCoins(), err + } + + err = ensureDenomInPool(poolAssetsByDenom, tokensIn) + if err != nil { + return osmomath.ZeroInt(), sdk.NewCoins(), err + } + + // ensure that there aren't too many or too few assets in `tokensIn` + if tokensIn.Len() != p.NumAssets() { + return osmomath.ZeroInt(), sdk.NewCoins(), errors.New("no-swap joins require LP'ing with all assets in pool") + } + + // execute a no-swap join with as many tokens as possible given a perfect ratio: + // * numShares is how many shares are perfectly matched. + // * remainingTokensIn is how many coins we have left to join that have not already been used. + numShares, remainingTokensIn, err := cfmm_common.MaximalExactRatioJoin(p, ctx, tokensIn) + if err != nil { + return osmomath.ZeroInt(), sdk.NewCoins(), err + } + + // ensure that no more tokens have been joined than is possible with the given `tokensIn` + tokensJoined = tokensIn.Sub(remainingTokensIn...) + if tokensJoined.IsAnyGT(tokensIn) { + return osmomath.ZeroInt(), sdk.NewCoins(), errors.New("an error has occurred, more coins joined than token In") + } + + return numShares, tokensJoined, nil +} + +// calcJoinSingleAssetTokensIn attempts to calculate single +// asset join for all tokensIn given totalShares in pool, +// poolAssetsByDenom and spreadFactor. totalShares is the number +// of shares in pool before beginning to join any of the tokensIn. +// +// Returns totalNewShares and totalNewLiquidity from joining all tokensIn +// by mimicking individually single asset joining each. +// or error if fails to calculate join for any of the tokensIn. +func (p *Pool) calcJoinSingleAssetTokensIn(tokensIn sdk.Coins, totalShares osmomath.Int, poolAssetsByDenom map[string]PoolAsset, spreadFactor osmomath.Dec) (osmomath.Int, sdk.Coins, error) { + totalNewShares := osmomath.ZeroInt() + totalNewLiquidity := sdk.NewCoins() + for _, coin := range tokensIn { + newShares, err := p.calcSingleAssetJoin(coin, spreadFactor, poolAssetsByDenom[coin.Denom], totalShares.Add(totalNewShares)) + if err != nil { + return osmomath.ZeroInt(), sdk.Coins{}, err + } + + totalNewLiquidity = totalNewLiquidity.Add(coin) + totalNewShares = totalNewShares.Add(newShares) + } + return totalNewShares, totalNewLiquidity, nil +} + +func (p *Pool) ExitPool(ctx sdk.Context, exitingShares osmomath.Int, exitFee osmomath.Dec) (exitingCoins sdk.Coins, err error) { + exitingCoins, err = p.CalcExitPoolCoinsFromShares(ctx, exitingShares, exitFee) + if err != nil { + return sdk.Coins{}, err + } + + if err := p.exitPool(ctx, exitingCoins, exitingShares); err != nil { + return sdk.Coins{}, err + } + + return exitingCoins, nil +} + +// exitPool exits the pool given exitingCoins and exitingShares. +// updates the pool's liquidity and totalShares. +func (p *Pool) exitPool(ctx sdk.Context, exitingCoins sdk.Coins, exitingShares osmomath.Int) error { + balances := p.GetTotalPoolLiquidity(ctx).Sub(exitingCoins...) + if err := p.UpdatePoolAssetBalances(balances); err != nil { + return err + } + + totalShares := p.GetTotalShares() + p.TotalShares = sdk.NewCoin(p.TotalShares.Denom, totalShares.Sub(exitingShares)) + + return nil +} + +func (p *Pool) CalcExitPoolCoinsFromShares(ctx sdk.Context, exitingShares osmomath.Int, exitFee osmomath.Dec) (exitedCoins sdk.Coins, err error) { + return cfmm_common.CalcExitPool(ctx, p, exitingShares, exitFee) +} + +func (p *Pool) CalcTokenInShareAmountOut( + ctx sdk.Context, + tokenInDenom string, + shareOutAmount osmomath.Int, + spreadFactor osmomath.Dec, +) (tokenInAmount osmomath.Int, err error) { + _, poolAssetIn, err := p.getPoolAssetAndIndex(tokenInDenom) + if err != nil { + return osmomath.Int{}, err + } + + normalizedWeight := poolAssetIn.Weight.ToLegacyDec().Quo(p.GetTotalWeight().ToLegacyDec()) + + // We round up tokenInAmount, as this is what's charged for the swap, for the precise amount out. + // Otherwise, the pool would under-charge by this rounding error. + tokenInAmount = calcSingleAssetInGivenPoolSharesOut( + poolAssetIn.Token.Amount.ToLegacyDec(), + normalizedWeight, + p.GetTotalShares().ToLegacyDec(), + shareOutAmount.ToLegacyDec(), + spreadFactor, + ).Ceil().TruncateInt() + + if !tokenInAmount.IsPositive() { + return osmomath.Int{}, errorsmod.Wrapf(types.ErrNotPositiveRequireAmount, nonPostiveTokenAmountErrFormat, tokenInAmount) + } + + return tokenInAmount, nil +} + +func (p *Pool) JoinPoolTokenInMaxShareAmountOut( + ctx sdk.Context, + tokenInDenom string, + shareOutAmount osmomath.Int, +) (tokenInAmount osmomath.Int, err error) { + _, poolAssetIn, err := p.getPoolAssetAndIndex(tokenInDenom) + if err != nil { + return osmomath.Int{}, err + } + + normalizedWeight := poolAssetIn.Weight.ToLegacyDec().Quo(p.GetTotalWeight().ToLegacyDec()) + + tokenInAmount = calcSingleAssetInGivenPoolSharesOut( + poolAssetIn.Token.Amount.ToLegacyDec(), + normalizedWeight, + p.GetTotalShares().ToLegacyDec(), + shareOutAmount.ToLegacyDec(), + p.GetSpreadFactor(ctx), + ).TruncateInt() + + if !tokenInAmount.IsPositive() { + return osmomath.Int{}, errorsmod.Wrapf(types.ErrNotPositiveRequireAmount, nonPostiveTokenAmountErrFormat, tokenInAmount) + } + + poolAssetIn.Token.Amount = poolAssetIn.Token.Amount.Add(tokenInAmount) + err = p.UpdatePoolAssetBalance(poolAssetIn.Token) + if err != nil { + return osmomath.Int{}, err + } + + return tokenInAmount, nil +} + +func (p *Pool) ExitSwapExactAmountOut( + ctx sdk.Context, + tokenOut sdk.Coin, + shareInMaxAmount osmomath.Int, +) (shareInAmount osmomath.Int, err error) { + _, poolAssetOut, err := p.getPoolAssetAndIndex(tokenOut.Denom) + if err != nil { + return osmomath.Int{}, err + } + + sharesIn := calcPoolSharesInGivenSingleAssetOut( + poolAssetOut.Token.Amount.ToLegacyDec(), + poolAssetOut.Weight.ToLegacyDec().Quo(p.TotalWeight.ToLegacyDec()), + p.GetTotalShares().ToLegacyDec(), + tokenOut.Amount.ToLegacyDec(), + p.GetSpreadFactor(ctx), + p.GetExitFee(ctx), + ).TruncateInt() + + if !sharesIn.IsPositive() { + return osmomath.Int{}, errorsmod.Wrapf(types.ErrNotPositiveRequireAmount, nonPostiveSharesAmountErrFormat, sharesIn) + } + + if sharesIn.GT(shareInMaxAmount) { + return osmomath.Int{}, errorsmod.Wrapf(types.ErrLimitMaxAmount, sharesLargerThanMaxErrFormat, sharesIn, shareInMaxAmount) + } + + if err := p.exitPool(ctx, sdk.NewCoins(tokenOut), sharesIn); err != nil { + return osmomath.Int{}, err + } + + return sharesIn, nil +} + +func (p *Pool) AsSerializablePool() poolmanagertypes.PoolI { + return p +} + +// GetPoolDenoms implements types.CFMMPoolI. +func (p *Pool) GetPoolDenoms(ctx sdk.Context) []string { + liquidity := p.GetTotalPoolLiquidity(ctx) + return liquidity.Denoms() +} diff --git a/tests/interchaintest/osmosis/gamm/pool-models/balancer/pool_asset.go b/tests/interchaintest/osmosis/gamm/pool-models/balancer/pool_asset.go new file mode 100644 index 00000000..903d098c --- /dev/null +++ b/tests/interchaintest/osmosis/gamm/pool-models/balancer/pool_asset.go @@ -0,0 +1,117 @@ +package balancer + +import ( + "fmt" + "sort" + "strings" + + "gopkg.in/yaml.v2" + + "github.com/osmosis-labs/fee-abstraction/v7/tests/interchaintest/osmosis/gamm/types" + "github.com/osmosis-labs/fee-abstraction/v7/tests/interchaintest/osmosis/osmomath" + + errorsmod "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +type poolAssetPretty struct { + Token sdk.Coin `json:"token" yaml:"token"` + Weight osmomath.Dec `json:"weight" yaml:"weight"` +} + +// validates a pool asset, to check if it has a valid weight. +func (pa PoolAsset) validateWeight() error { + if pa.Weight.LTE(osmomath.ZeroInt()) { + return fmt.Errorf("a token's weight in the pool must be greater than 0") + } + + // TODO: add validation for asset weight overflow: + // https://github.com/osmosis-labs/osmosis/issues/1958 + + return nil +} + +func (pa PoolAsset) prettify() poolAssetPretty { + return poolAssetPretty{ + Weight: osmomath.NewDecFromInt(pa.Weight).QuoInt64(GuaranteedWeightPrecision), + Token: pa.Token, + } +} + +// MarshalYAML returns the YAML representation of a PoolAsset. +// This is assumed to not be called on a stand-alone instance, so it removes the first marshalled line. +func (pa PoolAsset) MarshalYAML() (interface{}, error) { + bz, err := yaml.Marshal(pa.prettify()) + if err != nil { + return nil, err + } + s := string(bz) + return s, nil +} + +// sortPoolAssetsOutOfPlaceByDenom sorts pool assets in place, by weight +// Doesn't deep copy the underlying weights, but it does place the assets +// into a new slice. +func sortPoolAssetsOutOfPlaceByDenom(assets []PoolAsset) []PoolAsset { + assets_copy := make([]PoolAsset, len(assets)) + copy(assets_copy, assets) + sortPoolAssetsByDenom(assets_copy) + return assets_copy +} + +// sortPoolAssetsByDenom sorts pool assets in place, by weight. +func sortPoolAssetsByDenom(assets []PoolAsset) { + sort.Slice(assets, func(i, j int) bool { + PoolAssetA := assets[i] + PoolAssetB := assets[j] + + return strings.Compare(PoolAssetA.Token.Denom, PoolAssetB.Token.Denom) == -1 + }) +} + +func validateUserSpecifiedPoolAssets(assets []PoolAsset) error { + // The pool must be swapping between at least two assets + if len(assets) < types.MinNumOfAssetsInPool { + return types.ErrTooFewPoolAssets + } + + if len(assets) > types.MaxNumOfAssetsInPool { + return errorsmod.Wrapf(types.ErrTooManyPoolAssets, "%d", len(assets)) + } + + assetExistsMap := map[string]bool{} + for _, asset := range assets { + err := ValidateUserSpecifiedWeight(asset.Weight) + if err != nil { + return err + } + + if !asset.Token.IsValid() || !asset.Token.IsPositive() { + return errorsmod.Wrap(sdkerrors.ErrInvalidCoins, asset.Token.String()) + } + if _, exists := assetExistsMap[asset.Token.Denom]; exists { + return errorsmod.Wrapf(types.ErrTooFewPoolAssets, "pool asset %s already exists", asset.Token.Denom) + } + assetExistsMap[asset.Token.Denom] = true + } + return nil +} + +// poolAssetsCoins returns all the coins corresponding to a slice of pool assets. +func poolAssetsCoins(assets []PoolAsset) sdk.Coins { + coins := sdk.Coins{} + for _, asset := range assets { + coins = coins.Add(asset.Token) + } + return coins +} + +func getPoolAssetByDenom(assets []PoolAsset, denom string) (PoolAsset, bool) { + for _, asset := range assets { + if asset.Token.Denom == denom { + return asset, true + } + } + return PoolAsset{}, false +} diff --git a/tests/interchaintest/osmosis/gamm/pool-models/balancer/pool_params.go b/tests/interchaintest/osmosis/gamm/pool-models/balancer/pool_params.go new file mode 100644 index 00000000..949478ed --- /dev/null +++ b/tests/interchaintest/osmosis/gamm/pool-models/balancer/pool_params.go @@ -0,0 +1,77 @@ +package balancer + +import ( + "errors" + + "github.com/osmosis-labs/fee-abstraction/v7/tests/interchaintest/osmosis/gamm/types" + "github.com/osmosis-labs/fee-abstraction/v7/tests/interchaintest/osmosis/osmomath" +) + +func NewPoolParams(spreadFactor, exitFee osmomath.Dec, params *SmoothWeightChangeParams) PoolParams { + return PoolParams{ + SwapFee: spreadFactor, + ExitFee: exitFee, + SmoothWeightChangeParams: params, + } +} + +func (params PoolParams) Validate(poolWeights []PoolAsset) error { + if params.ExitFee.IsNegative() { + return types.ErrNegativeExitFee + } + + if params.ExitFee.GTE(osmomath.OneDec()) { + return types.ErrTooMuchExitFee + } + + if params.SwapFee.IsNegative() { + return types.ErrNegativeSpreadFactor + } + + if params.SwapFee.GTE(osmomath.OneDec()) { + return types.ErrTooMuchSpreadFactor + } + + if params.SmoothWeightChangeParams != nil { + targetWeights := params.SmoothWeightChangeParams.TargetPoolWeights + // Ensure it has the right number of weights + if len(targetWeights) != len(poolWeights) { + return types.ErrPoolParamsInvalidNumDenoms + } + // Validate all user specified weights + for _, v := range targetWeights { + err := ValidateUserSpecifiedWeight(v.Weight) + if err != nil { + return err + } + } + // Ensure that all the target weight denoms are same as pool asset weights + sortedTargetPoolWeights := sortPoolAssetsOutOfPlaceByDenom(targetWeights) + sortedPoolWeights := sortPoolAssetsOutOfPlaceByDenom(poolWeights) + for i, v := range sortedPoolWeights { + if sortedTargetPoolWeights[i].Token.Denom != v.Token.Denom { + return types.ErrPoolParamsInvalidDenom + } + } + + // No start time validation needed + + // We do not need to validate InitialPoolWeights, as we set that ourselves + // in setInitialPoolParams + + // TODO: Is there anything else we can validate for duration? + if params.SmoothWeightChangeParams.Duration <= 0 { + return errors.New("params.SmoothWeightChangeParams must have a positive duration") + } + } + + return nil +} + +func (params PoolParams) GetPoolSpreadFactor() osmomath.Dec { + return params.SwapFee +} + +func (params PoolParams) GetPoolExitFee() osmomath.Dec { + return params.ExitFee +} diff --git a/tests/interchaintest/osmosis/gamm/pool-models/balancer/tx.pb.go b/tests/interchaintest/osmosis/gamm/pool-models/balancer/tx.pb.go new file mode 100644 index 00000000..66561e1d --- /dev/null +++ b/tests/interchaintest/osmosis/gamm/pool-models/balancer/tx.pb.go @@ -0,0 +1,755 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: osmosis/gamm/poolmodels/balancer/v1beta1/tx.proto + +package balancer + +import ( + context "context" + fmt "fmt" + _ "github.com/cosmos/cosmos-sdk/types" + _ "github.com/cosmos/cosmos-sdk/types/tx/amino" + _ "github.com/cosmos/gogoproto/gogoproto" + grpc1 "github.com/cosmos/gogoproto/grpc" + proto "github.com/cosmos/gogoproto/proto" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + _ "google.golang.org/protobuf/types/known/timestamppb" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// ===================== MsgCreatePool +type MsgCreateBalancerPool struct { + Sender string `protobuf:"bytes,1,opt,name=sender,proto3" json:"sender,omitempty" yaml:"sender"` + PoolParams *PoolParams `protobuf:"bytes,2,opt,name=pool_params,json=poolParams,proto3" json:"pool_params,omitempty" yaml:"pool_params"` + PoolAssets []PoolAsset `protobuf:"bytes,3,rep,name=pool_assets,json=poolAssets,proto3" json:"pool_assets"` + FuturePoolGovernor string `protobuf:"bytes,4,opt,name=future_pool_governor,json=futurePoolGovernor,proto3" json:"future_pool_governor,omitempty" yaml:"future_pool_governor"` +} + +func (m *MsgCreateBalancerPool) Reset() { *m = MsgCreateBalancerPool{} } +func (m *MsgCreateBalancerPool) String() string { return proto.CompactTextString(m) } +func (*MsgCreateBalancerPool) ProtoMessage() {} +func (*MsgCreateBalancerPool) Descriptor() ([]byte, []int) { + return fileDescriptor_4d22c5192b37962a, []int{0} +} +func (m *MsgCreateBalancerPool) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgCreateBalancerPool) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgCreateBalancerPool.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgCreateBalancerPool) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgCreateBalancerPool.Merge(m, src) +} +func (m *MsgCreateBalancerPool) XXX_Size() int { + return m.Size() +} +func (m *MsgCreateBalancerPool) XXX_DiscardUnknown() { + xxx_messageInfo_MsgCreateBalancerPool.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgCreateBalancerPool proto.InternalMessageInfo + +func (m *MsgCreateBalancerPool) GetSender() string { + if m != nil { + return m.Sender + } + return "" +} + +func (m *MsgCreateBalancerPool) GetPoolParams() *PoolParams { + if m != nil { + return m.PoolParams + } + return nil +} + +func (m *MsgCreateBalancerPool) GetPoolAssets() []PoolAsset { + if m != nil { + return m.PoolAssets + } + return nil +} + +func (m *MsgCreateBalancerPool) GetFuturePoolGovernor() string { + if m != nil { + return m.FuturePoolGovernor + } + return "" +} + +// Returns the poolID +type MsgCreateBalancerPoolResponse struct { + PoolID uint64 `protobuf:"varint,1,opt,name=pool_id,json=poolId,proto3" json:"pool_id,omitempty"` +} + +func (m *MsgCreateBalancerPoolResponse) Reset() { *m = MsgCreateBalancerPoolResponse{} } +func (m *MsgCreateBalancerPoolResponse) String() string { return proto.CompactTextString(m) } +func (*MsgCreateBalancerPoolResponse) ProtoMessage() {} +func (*MsgCreateBalancerPoolResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_4d22c5192b37962a, []int{1} +} +func (m *MsgCreateBalancerPoolResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgCreateBalancerPoolResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgCreateBalancerPoolResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgCreateBalancerPoolResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgCreateBalancerPoolResponse.Merge(m, src) +} +func (m *MsgCreateBalancerPoolResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgCreateBalancerPoolResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgCreateBalancerPoolResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgCreateBalancerPoolResponse proto.InternalMessageInfo + +func (m *MsgCreateBalancerPoolResponse) GetPoolID() uint64 { + if m != nil { + return m.PoolID + } + return 0 +} + +func init() { + proto.RegisterType((*MsgCreateBalancerPool)(nil), "osmosis.gamm.poolmodels.balancer.v1beta1.MsgCreateBalancerPool") + proto.RegisterType((*MsgCreateBalancerPoolResponse)(nil), "osmosis.gamm.poolmodels.balancer.v1beta1.MsgCreateBalancerPoolResponse") +} + +func init() { + proto.RegisterFile("osmosis/gamm/poolmodels/balancer/v1beta1/tx.proto", fileDescriptor_4d22c5192b37962a) +} + +var fileDescriptor_4d22c5192b37962a = []byte{ + // 493 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x93, 0x41, 0x6f, 0xd3, 0x30, + 0x14, 0xc7, 0x9b, 0xb5, 0x2a, 0xc2, 0x15, 0x87, 0x59, 0x03, 0x55, 0x45, 0x24, 0x25, 0x48, 0x50, + 0x90, 0x6a, 0xab, 0xe1, 0xb6, 0xcb, 0x44, 0x98, 0x98, 0x76, 0x98, 0x34, 0x72, 0x1b, 0x97, 0xc9, + 0x49, 0xbd, 0x10, 0x29, 0xce, 0x8b, 0xe2, 0xb4, 0x1a, 0x5f, 0x81, 0x13, 0x9f, 0x80, 0x23, 0x67, + 0x3e, 0xc6, 0x8e, 0x3b, 0x72, 0x8a, 0xa6, 0xf6, 0xc0, 0xbd, 0x9f, 0x00, 0xd9, 0x71, 0xb6, 0x16, + 0x15, 0x09, 0x69, 0x97, 0x28, 0x7e, 0xfe, 0xfd, 0xff, 0xef, 0xf9, 0x3d, 0x1b, 0x4d, 0x40, 0x0a, + 0x90, 0x89, 0xa4, 0x31, 0x13, 0x82, 0xe6, 0x00, 0xa9, 0x80, 0x29, 0x4f, 0x25, 0x0d, 0x59, 0xca, + 0xb2, 0x88, 0x17, 0x74, 0x3e, 0x09, 0x79, 0xc9, 0x26, 0xb4, 0xbc, 0x24, 0x79, 0x01, 0x25, 0xe0, + 0x91, 0x91, 0x10, 0x25, 0x21, 0x77, 0x12, 0xd2, 0x48, 0x88, 0x91, 0x0c, 0xf6, 0x62, 0x88, 0x41, + 0x8b, 0xa8, 0xfa, 0xab, 0xf5, 0x83, 0x5d, 0x26, 0x92, 0x0c, 0xa8, 0xfe, 0x9a, 0xd0, 0xab, 0x8d, + 0x2a, 0x9a, 0x8c, 0x8d, 0xdf, 0x29, 0x40, 0x6a, 0x40, 0x3b, 0xd2, 0x24, 0x0d, 0x99, 0xe4, 0xb7, + 0x5c, 0x04, 0x49, 0x66, 0xf6, 0x9d, 0x18, 0x20, 0x4e, 0x39, 0xd5, 0xab, 0x70, 0x76, 0x41, 0xcb, + 0x44, 0x70, 0x59, 0x32, 0x91, 0xd7, 0x80, 0x7b, 0xb3, 0x83, 0x1e, 0x9f, 0xc8, 0xf8, 0x7d, 0xc1, + 0x59, 0xc9, 0xfd, 0xb5, 0x04, 0xf8, 0x35, 0xea, 0x4a, 0x9e, 0x4d, 0x79, 0xd1, 0xb7, 0x86, 0xd6, + 0xe8, 0xa1, 0xbf, 0xbb, 0xaa, 0x9c, 0x47, 0x5f, 0x98, 0x48, 0xf7, 0xdd, 0x3a, 0xee, 0x06, 0x06, + 0xc0, 0x67, 0xa8, 0xa7, 0x8e, 0x7d, 0x9e, 0xb3, 0x82, 0x09, 0xd9, 0xdf, 0x19, 0x5a, 0xa3, 0x9e, + 0x37, 0x24, 0x1b, 0x7d, 0x31, 0xc5, 0x11, 0xe5, 0x7d, 0xaa, 0x39, 0xff, 0xc9, 0xaa, 0x72, 0x70, + 0xed, 0xb8, 0x26, 0x77, 0x03, 0x94, 0xdf, 0x32, 0xf8, 0x83, 0xb1, 0x66, 0x52, 0xf2, 0x52, 0xf6, + 0xdb, 0xc3, 0xf6, 0xa8, 0xe7, 0x39, 0xff, 0xb6, 0x7e, 0xa7, 0x38, 0xbf, 0x73, 0x55, 0x39, 0xad, + 0xda, 0x47, 0x07, 0x24, 0xfe, 0x88, 0xf6, 0x2e, 0x66, 0xe5, 0xac, 0xe0, 0xe7, 0xda, 0x2e, 0x86, + 0x39, 0x2f, 0x32, 0x28, 0xfa, 0x1d, 0x7d, 0x36, 0x67, 0x55, 0x39, 0x4f, 0xeb, 0x4a, 0xb6, 0x51, + 0x6e, 0x80, 0xeb, 0xb0, 0xca, 0x70, 0x64, 0x82, 0xfb, 0x2f, 0xbf, 0xfe, 0xfe, 0xf9, 0xe6, 0xf9, + 0xc6, 0xa4, 0x22, 0xdd, 0xc6, 0x71, 0x33, 0xa8, 0xb1, 0x72, 0x71, 0x0f, 0xd1, 0xb3, 0xad, 0x1d, + 0x0e, 0xb8, 0xcc, 0x21, 0x93, 0x1c, 0xbf, 0x40, 0x0f, 0x74, 0xba, 0x64, 0xaa, 0x5b, 0xdd, 0xf1, + 0xd1, 0xa2, 0x72, 0xba, 0x0a, 0x39, 0x3e, 0x0c, 0xba, 0x6a, 0xeb, 0x78, 0xea, 0xfd, 0xb0, 0x50, + 0xfb, 0x44, 0xc6, 0xf8, 0xbb, 0x85, 0xf0, 0x96, 0x69, 0x1d, 0x90, 0xff, 0xbd, 0x85, 0x64, 0x6b, + 0x31, 0x83, 0xa3, 0x7b, 0x1a, 0x34, 0xa7, 0xf1, 0xcf, 0xae, 0x16, 0xb6, 0x75, 0xbd, 0xb0, 0xad, + 0x9b, 0x85, 0x6d, 0x7d, 0x5b, 0xda, 0xad, 0xeb, 0xa5, 0xdd, 0xfa, 0xb5, 0xb4, 0x5b, 0x9f, 0x0e, + 0xe2, 0xa4, 0xfc, 0x3c, 0x0b, 0x49, 0x04, 0x82, 0x9a, 0x64, 0xe3, 0x94, 0x85, 0xb2, 0x59, 0xd0, + 0xb9, 0xe7, 0xd1, 0xcb, 0xbb, 0x97, 0x37, 0xfe, 0xeb, 0xe9, 0x85, 0x5d, 0x7d, 0x67, 0xdf, 0xfe, + 0x09, 0x00, 0x00, 0xff, 0xff, 0x35, 0x36, 0x8b, 0xda, 0xa5, 0x03, 0x00, 0x00, +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// MsgClient is the client API for Msg service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type MsgClient interface { + CreateBalancerPool(ctx context.Context, in *MsgCreateBalancerPool, opts ...grpc.CallOption) (*MsgCreateBalancerPoolResponse, error) +} + +type msgClient struct { + cc grpc1.ClientConn +} + +func NewMsgClient(cc grpc1.ClientConn) MsgClient { + return &msgClient{cc} +} + +func (c *msgClient) CreateBalancerPool(ctx context.Context, in *MsgCreateBalancerPool, opts ...grpc.CallOption) (*MsgCreateBalancerPoolResponse, error) { + out := new(MsgCreateBalancerPoolResponse) + err := c.cc.Invoke(ctx, "/osmosis.gamm.poolmodels.balancer.v1beta1.Msg/CreateBalancerPool", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// MsgServer is the server API for Msg service. +type MsgServer interface { + CreateBalancerPool(context.Context, *MsgCreateBalancerPool) (*MsgCreateBalancerPoolResponse, error) +} + +// UnimplementedMsgServer can be embedded to have forward compatible implementations. +type UnimplementedMsgServer struct { +} + +func (*UnimplementedMsgServer) CreateBalancerPool(ctx context.Context, req *MsgCreateBalancerPool) (*MsgCreateBalancerPoolResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method CreateBalancerPool not implemented") +} + +func RegisterMsgServer(s grpc1.Server, srv MsgServer) { + s.RegisterService(&_Msg_serviceDesc, srv) +} + +func _Msg_CreateBalancerPool_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgCreateBalancerPool) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).CreateBalancerPool(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/osmosis.gamm.poolmodels.balancer.v1beta1.Msg/CreateBalancerPool", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).CreateBalancerPool(ctx, req.(*MsgCreateBalancerPool)) + } + return interceptor(ctx, in, info, handler) +} + +var _Msg_serviceDesc = grpc.ServiceDesc{ + ServiceName: "osmosis.gamm.poolmodels.balancer.v1beta1.Msg", + HandlerType: (*MsgServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "CreateBalancerPool", + Handler: _Msg_CreateBalancerPool_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "osmosis/gamm/poolmodels/balancer/v1beta1/tx.proto", +} + +func (m *MsgCreateBalancerPool) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgCreateBalancerPool) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgCreateBalancerPool) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.FuturePoolGovernor) > 0 { + i -= len(m.FuturePoolGovernor) + copy(dAtA[i:], m.FuturePoolGovernor) + i = encodeVarintTx(dAtA, i, uint64(len(m.FuturePoolGovernor))) + i-- + dAtA[i] = 0x22 + } + if len(m.PoolAssets) > 0 { + for iNdEx := len(m.PoolAssets) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.PoolAssets[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + } + if m.PoolParams != nil { + { + size, err := m.PoolParams.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if len(m.Sender) > 0 { + i -= len(m.Sender) + copy(dAtA[i:], m.Sender) + i = encodeVarintTx(dAtA, i, uint64(len(m.Sender))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgCreateBalancerPoolResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgCreateBalancerPoolResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgCreateBalancerPoolResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.PoolID != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.PoolID)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func encodeVarintTx(dAtA []byte, offset int, v uint64) int { + offset -= sovTx(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *MsgCreateBalancerPool) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Sender) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if m.PoolParams != nil { + l = m.PoolParams.Size() + n += 1 + l + sovTx(uint64(l)) + } + if len(m.PoolAssets) > 0 { + for _, e := range m.PoolAssets { + l = e.Size() + n += 1 + l + sovTx(uint64(l)) + } + } + l = len(m.FuturePoolGovernor) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgCreateBalancerPoolResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.PoolID != 0 { + n += 1 + sovTx(uint64(m.PoolID)) + } + return n +} + +func sovTx(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozTx(x uint64) (n int) { + return sovTx(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *MsgCreateBalancerPool) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgCreateBalancerPool: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgCreateBalancerPool: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Sender", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Sender = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PoolParams", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.PoolParams == nil { + m.PoolParams = &PoolParams{} + } + if err := m.PoolParams.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PoolAssets", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.PoolAssets = append(m.PoolAssets, PoolAsset{}) + if err := m.PoolAssets[len(m.PoolAssets)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FuturePoolGovernor", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.FuturePoolGovernor = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgCreateBalancerPoolResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgCreateBalancerPoolResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgCreateBalancerPoolResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field PoolID", wireType) + } + m.PoolID = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.PoolID |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipTx(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTx + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTx + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTx + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthTx + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupTx + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthTx + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthTx = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowTx = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupTx = fmt.Errorf("proto: unexpected end of group") +) diff --git a/tests/interchaintest/osmosis/gamm/pool-models/internal/cfmm_common/lp.go b/tests/interchaintest/osmosis/gamm/pool-models/internal/cfmm_common/lp.go new file mode 100644 index 00000000..bc13d973 --- /dev/null +++ b/tests/interchaintest/osmosis/gamm/pool-models/internal/cfmm_common/lp.go @@ -0,0 +1,186 @@ +package cfmm_common + +import ( + "errors" + + sdk "github.com/cosmos/cosmos-sdk/types" + + errorsmod "cosmossdk.io/errors" + + "github.com/osmosis-labs/fee-abstraction/v7/tests/interchaintest/osmosis/gamm/types" + "github.com/osmosis-labs/fee-abstraction/v7/tests/interchaintest/osmosis/osmomath" +) + +const errMsgFormatSharesLargerThanMax = "cannot exit all shares in a pool. Attempted to exit %s shares, max allowed is %s" + +// CalcExitPool returns how many tokens should come out, when exiting k LP shares against a "standard" CFMM +func CalcExitPool(ctx sdk.Context, pool types.CFMMPoolI, exitingShares osmomath.Int, exitFee osmomath.Dec) (sdk.Coins, error) { + totalShares := pool.GetTotalShares() + if exitingShares.GTE(totalShares) { + return sdk.Coins{}, errorsmod.Wrapf(types.ErrLimitMaxAmount, errMsgFormatSharesLargerThanMax, exitingShares, totalShares.Sub(osmomath.OneInt())) + } + + // refundedShares = exitingShares * (1 - exit fee) + // with 0 exit fee optimization + var refundedShares osmomath.Dec + if !exitFee.IsZero() { + // exitingShares * (1 - exit fee) + oneSubExitFee := osmomath.OneDec().Sub(exitFee) + refundedShares = oneSubExitFee.MulIntMut(exitingShares) + } else { + refundedShares = exitingShares.ToLegacyDec() + } + + shareOutRatio := refundedShares.QuoInt(totalShares) + // exitedCoins = shareOutRatio * pool liquidity + exitedCoins := sdk.Coins{} + poolLiquidity := pool.GetTotalPoolLiquidity(ctx) + + for _, asset := range poolLiquidity { + // round down here, due to not wanting to over-exit + exitAmt := shareOutRatio.MulInt(asset.Amount).TruncateInt() + if exitAmt.LTE(osmomath.ZeroInt()) { + continue + } + if exitAmt.GTE(asset.Amount) { + return sdk.Coins{}, errors.New("too many shares out") + } + exitedCoins = exitedCoins.Add(sdk.NewCoin(asset.Denom, exitAmt)) + } + + return exitedCoins, nil +} + +// MaximalExactRatioJoin calculates the maximal amount of tokens that can be joined whilst maintaining pool asset's ratio +// returning the number of shares that'd be and how many coins would be left over. +// +// e.g) suppose we have a pool of 10 foo tokens and 10 bar tokens, with the total amount of 100 shares. +// if `tokensIn` provided is 1 foo token and 2 bar tokens, `MaximalExactRatioJoin` +// would be returning (10 shares, 1 bar token, nil) +// +// This can be used when `tokensIn` are not guaranteed the same ratio as assets in the pool. +// Calculation for this is done in the following steps. +// 1. iterate through all the tokens provided as an argument, calculate how much ratio it accounts for the asset in the pool +// 2. get the minimal share ratio that would work as the benchmark for all tokens. +// 3. calculate the number of shares that could be joined (total share * min share ratio), return the remaining coins +func MaximalExactRatioJoin(p types.CFMMPoolI, ctx sdk.Context, tokensIn sdk.Coins) (numShares osmomath.Int, remCoins sdk.Coins, err error) { + coinShareRatios := make([]osmomath.Dec, len(tokensIn)) + minShareRatio := sdk.MaxSortableDec + maxShareRatio := osmomath.ZeroDec() + + poolLiquidity := p.GetTotalPoolLiquidity(ctx) + totalShares := p.GetTotalShares() + + for i, coin := range tokensIn { + // Note: QuoInt implements floor division, unlike Quo + // This is because it calls the native golang routine big.Int.Quo + // https://pkg.go.dev/math/big#Int.Quo + shareRatio := coin.Amount.ToLegacyDec().QuoInt(poolLiquidity.AmountOfNoDenomValidation(coin.Denom)) + if shareRatio.LT(minShareRatio) { + minShareRatio = shareRatio + } + if shareRatio.GT(maxShareRatio) { + maxShareRatio = shareRatio + } + coinShareRatios[i] = shareRatio + } + + if minShareRatio.Equal(sdk.MaxSortableDec) { + return numShares, remCoins, errors.New("unexpected error in MaximalExactRatioJoin") + } + + remCoins = sdk.Coins{} + // critically we round down here (TruncateInt), to ensure that the returned LP shares + // are always less than or equal to % liquidity added. + numShares = minShareRatio.MulInt(totalShares).TruncateInt() + + // if we have multiple share values, calculate remainingCoins + if !minShareRatio.Equal(maxShareRatio) { + // we have to calculate remCoins + for i, coin := range tokensIn { + // if coinShareRatios[i] == minShareRatio, no remainder + if coinShareRatios[i].Equal(minShareRatio) { + continue + } + + usedAmount := minShareRatio.MulInt(poolLiquidity.AmountOfNoDenomValidation(coin.Denom)).Ceil().TruncateInt() + newAmt := coin.Amount.Sub(usedAmount) + // if newAmt is non-zero, add to RemCoins. (It could be zero due to rounding) + if !newAmt.IsZero() { + remCoins = remCoins.Add(sdk.Coin{Denom: coin.Denom, Amount: newAmt}) + } + } + } + + return numShares, remCoins, nil +} + +// We binary search a number of LP shares, s.t. if we exited the pool with the updated liquidity, +// and swapped all the tokens back to the input denom, we'd get the same amount. (under 0 spread factor) +// Thanks to CFMM path-independence, we can estimate slippage with these swaps to be sure to get the right numbers here. +// (by path-independence, swap all of B -> A, and then swap all of C -> A will yield same amount of A, regardless +// of order and interleaving) +// +// This implementation requires each of pool.GetTotalPoolLiquidity, pool.ExitPool, and pool.SwapExactAmountIn +// to not update or read from state, and instead only do updates based upon the pool struct. +func BinarySearchSingleAssetJoin( + pool types.CFMMPoolI, + tokenIn sdk.Coin, + poolWithAddedLiquidityAndShares func(newLiquidity sdk.Coin, newShares osmomath.Int) types.CFMMPoolI, +) (numLPShares osmomath.Int, err error) { + // use dummy context + ctx := sdk.Context{} + // should be guaranteed to converge if above 256 since osmomath.Int has 256 bits + maxIterations := 300 + // upperbound of number of LP shares = existingShares * tokenIn.Amount / pool.totalLiquidity.AmountOf(tokenIn.Denom) + existingTokenLiquidity := pool.GetTotalPoolLiquidity(ctx).AmountOf(tokenIn.Denom) + existingLPShares := pool.GetTotalShares() + LPShareUpperBound := existingLPShares.Mul(tokenIn.Amount).ToLegacyDec().QuoInt(existingTokenLiquidity).Ceil().TruncateInt() + LPShareLowerBound := osmomath.ZeroInt() + + // Creates a pool with tokenIn liquidity added, where it created `sharesIn` number of shares. + // Returns how many tokens you'd get, if you then exited all of `sharesIn` for tokenIn.Denom + estimateCoinOutGivenShares := func(sharesIn osmomath.Int) (tokenOut osmomath.Int, err error) { + // new pool with added liquidity & LP shares, which we can mutate. + poolWithUpdatedLiquidity := poolWithAddedLiquidityAndShares(tokenIn, sharesIn) + swapToDenom := tokenIn.Denom + // so now due to correctness of exitPool, we exitPool and swap all remaining assets to base asset + exitFee := osmomath.ZeroDec() + exitedCoins, err := poolWithUpdatedLiquidity.ExitPool(ctx, sharesIn, exitFee) + if err != nil { + return osmomath.Int{}, err + } + + return SwapAllCoinsToSingleAsset(poolWithUpdatedLiquidity, ctx, exitedCoins, swapToDenom, osmomath.ZeroDec()) + } + + // We accept an additive tolerance of 1 LP share error and round down + errTolerance := osmomath.ErrTolerance{AdditiveTolerance: osmomath.OneDec(), MultiplicativeTolerance: osmomath.Dec{}, RoundingDir: osmomath.RoundDown} + + numLPShares, err = osmomath.BinarySearch( + estimateCoinOutGivenShares, + LPShareLowerBound, LPShareUpperBound, tokenIn.Amount, errTolerance, maxIterations) + if err != nil { + return osmomath.Int{}, err + } + + return numLPShares, nil +} + +// SwapAllCoinsToSingleAsset iterates through each token in the input set and trades it against the same pool sequentially +func SwapAllCoinsToSingleAsset(pool types.CFMMPoolI, ctx sdk.Context, inTokens sdk.Coins, swapToDenom string, + spreadFactor osmomath.Dec, +) (osmomath.Int, error) { + tokenOutAmt := inTokens.AmountOfNoDenomValidation(swapToDenom) + for _, coin := range inTokens { + if coin.Denom == swapToDenom { + continue + } + tokenOut, err := pool.SwapOutAmtGivenIn(ctx, sdk.NewCoins(coin), swapToDenom, spreadFactor) + if err != nil { + return osmomath.Int{}, err + } + tokenOutAmt = tokenOutAmt.Add(tokenOut.Amount) + } + return tokenOutAmt, nil +} diff --git a/tests/interchaintest/osmosis/gamm/pool-models/stableswap/amm.go b/tests/interchaintest/osmosis/gamm/pool-models/stableswap/amm.go new file mode 100644 index 00000000..d65a7e42 --- /dev/null +++ b/tests/interchaintest/osmosis/gamm/pool-models/stableswap/amm.go @@ -0,0 +1,319 @@ +package stableswap + +import ( + "errors" + + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/osmosis-labs/fee-abstraction/v7/tests/interchaintest/osmosis/gamm/pool-models/internal/cfmm_common" + types "github.com/osmosis-labs/fee-abstraction/v7/tests/interchaintest/osmosis/gamm/types" + "github.com/osmosis-labs/fee-abstraction/v7/tests/interchaintest/osmosis/osmomath" +) + +// Simplified multi-asset CFMM is xy(x^2 + y^2 + w) = k, +// where w is the sum of the squares of the +// reserve assets (e.g. w = m^2 + n^2). +// When w = 0, this is equivalent to solidly's CFMM +// We use this version for calculations since the u +// term in the full CFMM is constant. +func cfmmConstantMultiNoV(xReserve, yReserve, wSumSquares osmomath.BigDec) osmomath.BigDec { + return cfmmConstantMultiNoVY(xReserve, yReserve, wSumSquares).Mul(yReserve) +} + +// returns x(x^2 + y^2 + w) = k +// For use in comparing values with the same y +func cfmmConstantMultiNoVY(xReserve, yReserve, wSumSquares osmomath.BigDec) osmomath.BigDec { + if !xReserve.IsPositive() || !yReserve.IsPositive() || wSumSquares.IsNegative() { + panic("invalid input: reserves must be positive") + } + + x2 := xReserve.Mul(xReserve) + y2 := yReserve.Mul(yReserve) + return xReserve.Mul(x2.Add(y2).Add(wSumSquares)) +} + +// Solidly's CFMM is xy(x^2 + y^2) = k, and our multi-asset CFMM is xyz(x^2 + y^2 + w) = k +// So we want to solve for a given addition of `b` units of y into the pool, +// how many units `a` of x do we get out. +// So we solve the following expression for `a`: +// xy(x^2 + y^2 + w) = (x - a)(y + b)((x - a)^2 + (y + b)^2 + w) +// with w set to 0 for 2 asset pools +func solveCfmm(xReserve, yReserve osmomath.BigDec, remReserves []osmomath.BigDec, yIn osmomath.BigDec) osmomath.BigDec { + wSumSquares := osmomath.ZeroBigDec() + for _, assetReserve := range remReserves { + wSumSquares = wSumSquares.Add(assetReserve.Mul(assetReserve)) + } + return solveCFMMBinarySearchMulti(xReserve, yReserve, wSumSquares, yIn) +} + +// $$k_{target} = \frac{x_0 y_0 (x_0^2 + y_0^2 + w)}{y_f} - (x_0 (y_f^2 + w) + x_0^3)$$ +func targetKCalculator(x0, y0, w, yf osmomath.BigDec) osmomath.BigDec { + // cfmmNoV(x0, y0, w) = x_0 y_0 (x_0^2 + y_0^2 + w) + startK := cfmmConstantMultiNoV(x0, y0, w) + // remove extra yf term + yfRemoved := startK.Quo(yf) + // removed constant term from expression + // namely - (x_0 (y_f^2 + w) + x_0^3) = x_0(y_f^2 + w + x_0^2) + // innerTerm = y_f^2 + w + x_0^2 + innerTerm := yf.Mul(yf).Add(w).Add((x0.Mul(x0))) + constantTerm := innerTerm.Mul(x0) + return yfRemoved.Sub(constantTerm) +} + +// $$k_{iter}(x_f) = -x_{out}^3 + 3 x_0 x_{out}^2 - (y_f^2 + w + 3x_0^2)x_{out}$$ +// where x_out = x_0 - x_f +func iterKCalculator(x0, w, yf osmomath.BigDec) func(osmomath.BigDec) osmomath.BigDec { + // compute coefficients first. Notice that the leading coefficient is -1, we will use this to compute faster. + // cubicCoeff := -1 + quadraticCoeff := x0.MulInt64(3) + linearCoeff := quadraticCoeff.Mul(x0).Add(w).Add(yf.Mul(yf)).NegMut() + return func(xf osmomath.BigDec) osmomath.BigDec { + xOut := x0.Sub(xf) + // horners method + // ax^3 + bx^2 + cx = x(c + x(b + ax)) + // a = -1 + res := xOut.Neg() + res = res.AddMut(quadraticCoeff).MulMut(xOut) + res = res.AddMut(linearCoeff).MulMut(xOut) + return res + } +} + +var ( + zero = osmomath.ZeroBigDec() + one = osmomath.OneBigDec() +) + +func deriveUpperLowerXFinalReserveBounds(xReserve, yReserve, wSumSquares, yFinal osmomath.BigDec) ( + xFinalLowerbound, xFinalUpperbound osmomath.BigDec, +) { + xFinalLowerbound, xFinalUpperbound = xReserve, xReserve + + k0 := cfmmConstantMultiNoV(xReserve, yFinal, wSumSquares) + k := cfmmConstantMultiNoV(xReserve, yReserve, wSumSquares) + // fmt.Println(k0, k) + if k0.Equal(zero) || k.Equal(zero) { + panic("k should never be zero") + } + kRatio := k0.Quo(k) + if kRatio.LT(one) { + // k_0 < k. Need to find an upperbound. Worst case assume a linear relationship, gives an upperbound + // TODO: In the future, we can derive better bounds via reasoning about coefficients in the cubic + // These are quite close when we are in the "stable" part of the curve though. + xFinalUpperbound = xReserve.Quo(kRatio).Ceil() + } else if kRatio.GT(one) { + // need to find a lowerbound. We could use a cubic relation, but for now we just set it to 0. + xFinalLowerbound = osmomath.ZeroBigDec() + } + // else + // k remains unchanged. + // So we keep bounds equal to each other + return xFinalLowerbound, xFinalUpperbound +} + +// solveCFMMBinarySearch searches the correct dx using binary search over constant K. +func solveCFMMBinarySearchMulti(xReserve, yReserve, wSumSquares, yIn osmomath.BigDec) osmomath.BigDec { + if !xReserve.IsPositive() || !yReserve.IsPositive() || wSumSquares.IsNegative() { + panic("invalid input: reserves and input must be positive") + } else if yIn.Abs().GTE(yReserve) { + panic("cannot input more than pool reserves") + } + // fmt.Printf("solve cfmm xreserve %v, yreserve %v, w %v, yin %v\n", xReserve, yReserve, wSumSquares, yIn) + yFinal := yReserve.Add(yIn) + xLowEst, xHighEst := deriveUpperLowerXFinalReserveBounds(xReserve, yReserve, wSumSquares, yFinal) + targetK := targetKCalculator(xReserve, yReserve, wSumSquares, yFinal) + iterKCalc := iterKCalculator(xReserve, wSumSquares, yFinal) + maxIterations := 256 + + // we use a geometric error tolerance that guarantees approximately 10^-12 precision on outputs + errTolerance := osmomath.ErrTolerance{AdditiveTolerance: osmomath.Dec{}, MultiplicativeTolerance: osmomath.NewDecWithPrec(1, 12)} + + // if yIn is positive, we want to under-estimate the amount of xOut. + // This means, we want x_out to be rounded down, as x_out = x_init - x_final, for x_init > x_final. + // Thus we round-up x_final, to make it greater (and therefore ) x_out smaller. + // If yIn is negative, the amount of xOut will also be negative (representing that we must add tokens into the pool) + // this means x_out = x_init - x_final, for x_init < x_final. + // we want to over_estimate |x_out|, which means rounding x_out down as its a negative quantity. + // This means rounding x_final up, to give us a larger negative. + // Therefore we always round up. + roundingDirection := osmomath.RoundUp + errTolerance.RoundingDir = roundingDirection + + xEst, err := osmomath.BinarySearchBigDec(iterKCalc, xLowEst, xHighEst, targetK, errTolerance, maxIterations) + if err != nil { + panic(err) + } + + xOut := xReserve.Sub(xEst) + // fmt.Printf("xOut %v\n", xOut) + + // We check the absolute value of the output against the xReserve amount to ensure that: + // 1. Swaps cannot more than double the input token's pool supply + // 2. Swaps cannot output more than the output token's pool supply + if xOut.Abs().GTE(xReserve) { + panic("invalid output: greater than full pool reserves") + } + return xOut +} + +func (p Pool) spotPrice(quoteDenom, baseDenom string) (spotPrice osmomath.Dec, err error) { + // Define f_{y -> x}(a) as the function that outputs the amount of tokens X you'd get by + // trading "a" units of Y against the pool, assuming 0 spread factor, at the current liquidity. + // The spot price of the pool is then lim a -> 0, f_{y -> x}(a) / a + // For uniswap f_{y -> x}(a) = x - xy/(y + a), + // The spot price equation of y in terms of x is X_SUPPLY/Y_SUPPLY. + // You can work out that it follows from the above relation! + // + // Now we have to work this out for the much more complex CFMM xy(x^2 + y^2). + // Or we can sidestep this, by just picking a small value a, and computing f_{y -> x}(a) / a, + // and accept the precision error. + + // We arbitrarily choose a = 1, and anticipate that this is a small value at the scale of + // xReserve & yReserve. + a := osmomath.OneInt() + + res, err := p.calcOutAmtGivenIn(sdk.NewCoin(baseDenom, a), quoteDenom, osmomath.ZeroDec()) + // fmt.Println("spot price res", res) + return res, err +} + +func oneMinus(spreadFactor osmomath.Dec) osmomath.BigDec { + return osmomath.BigDecFromDec(osmomath.OneDec().Sub(spreadFactor)) +} + +// calcOutAmtGivenIn calculate amount of specified denom to output from a pool in osmomath.Dec given the input `tokenIn` +func (p Pool) calcOutAmtGivenIn(tokenIn sdk.Coin, tokenOutDenom string, spreadFactor osmomath.Dec) (osmomath.Dec, error) { + // round liquidity down, and round token in down + reserves, err := p.scaledSortedPoolReserves(tokenIn.Denom, tokenOutDenom, osmomath.RoundDown) + if err != nil { + return osmomath.Dec{}, err + } + tokenInSupply, tokenOutSupply, remReserves := reserves[0], reserves[1], reserves[2:] + tokenInDec, err := p.scaleCoin(tokenIn, osmomath.RoundDown) + if err != nil { + return osmomath.Dec{}, err + } + + // amm input = tokenIn * (1 - spread factor) + ammIn := tokenInDec.Mul(oneMinus(spreadFactor)) + // We are solving for the amount of token out, hence x = tokenOutSupply, y = tokenInSupply + // fmt.Printf("outSupply %s, inSupply %s, remReservs %s, ammIn %s\n ", tokenOutSupply, tokenInSupply, remReserves, ammIn) + cfmmOut := solveCfmm(tokenOutSupply, tokenInSupply, remReserves, ammIn) + // fmt.Println("cfmmout ", cfmmOut) + outAmt := p.getDescaledPoolAmt(tokenOutDenom, cfmmOut) + return outAmt, nil +} + +// calcInAmtGivenOut calculates exact input amount given the desired output and return as a decimal +func (p *Pool) calcInAmtGivenOut(tokenOut sdk.Coin, tokenInDenom string, spreadFactor osmomath.Dec) (osmomath.Dec, error) { + // round liquidity down, and round token out up + reserves, err := p.scaledSortedPoolReserves(tokenInDenom, tokenOut.Denom, osmomath.RoundDown) + if err != nil { + return osmomath.Dec{}, err + } + tokenInSupply, tokenOutSupply, remReserves := reserves[0], reserves[1], reserves[2:] + tokenOutAmount, err := p.scaleCoin(tokenOut, osmomath.RoundUp) + if err != nil { + return osmomath.Dec{}, err + } + + // We are solving for the amount of token in, cfmm(x,y) = cfmm(x + x_in, y - y_out) + // x = tokenInSupply, y = tokenOutSupply, yIn = -tokenOutAmount + cfmmIn := solveCfmm(tokenInSupply, tokenOutSupply, remReserves, tokenOutAmount.Neg()) + // returned cfmmIn is negative, representing we need to add this many tokens to pool. + // We invert that negative here. + cfmmIn = cfmmIn.Neg() + // divide by (1 - spread factor) to force a corresponding increase in input asset + inAmt := cfmmIn.QuoRoundUpMut(oneMinus(spreadFactor)) + inCoinAmt := p.getDescaledPoolAmt(tokenInDenom, inAmt) + return inCoinAmt, nil +} + +// calcSingleAssetJoinShares calculates the number of LP shares that +// should be granted given the passed in single-token input (non-mutative) +func (p *Pool) calcSingleAssetJoinShares(tokenIn sdk.Coin, spreadFactor osmomath.Dec) (osmomath.Int, error) { + poolWithAddedLiquidityAndShares := func(newLiquidity sdk.Coin, newShares osmomath.Int) types.CFMMPoolI { + paCopy := p.Copy() + paCopy.updatePoolForJoin(sdk.NewCoins(newLiquidity), newShares) + return &paCopy + } + + // We apply the spread factor by multiplying by: + // 1) getting what % of the input the spread factor should apply to + // 2) multiplying that by spread factor + // 3) oneMinusSpreadFactor := (1 - spread_factor * spread_factor_applicable_percent) + // 4) Multiplying token in by one minus spread factor. + spreadFactorApplicableRatio, err := p.singleAssetJoinSpreadFactorRatio(tokenIn.Denom) + if err != nil { + return osmomath.Int{}, err + } + oneMinusSpreadFactor := osmomath.OneDec().Sub(spreadFactor.Mul(spreadFactorApplicableRatio)) + tokenInAmtAfterFee := tokenIn.Amount.ToLegacyDec().Mul(oneMinusSpreadFactor).TruncateInt() + + return cfmm_common.BinarySearchSingleAssetJoin(p, sdk.NewCoin(tokenIn.Denom, tokenInAmtAfterFee), poolWithAddedLiquidityAndShares) +} + +// returns the ratio of input asset liquidity, to total liquidity in pool, post-scaling. +// We use this as the portion of input liquidity to apply a spread factor too, for single asset joins. +// So if a pool is currently comprised of 80% of asset A, and 20% of asset B (post-scaling), +// and we input asset A, this function will return 20%. +// Note that this will over-estimate spread factor for single asset joins slightly, +// as in the swapping process into the pool, the A to B ratio would decrease the relative supply of B. +func (p *Pool) singleAssetJoinSpreadFactorRatio(tokenInDenom string) (osmomath.Dec, error) { + // get a second denom in pool + tokenOut := p.PoolLiquidity[0] + if tokenOut.Denom == tokenInDenom { + tokenOut = p.PoolLiquidity[1] + } + // We round bankers scaled liquidity, since we care about the ratio of liquidity. + scaledLiquidity, err := p.scaledSortedPoolReserves(tokenInDenom, tokenOut.Denom, osmomath.RoundDown) + if err != nil { + return osmomath.Dec{}, err + } + + totalLiquidityDenominator := osmomath.ZeroBigDec() + for _, amount := range scaledLiquidity { + totalLiquidityDenominator = totalLiquidityDenominator.Add(amount) + } + ratioOfInputAssetLiquidityToTotalLiquidity := scaledLiquidity[0].Quo(totalLiquidityDenominator) + // Dec() rounds down (as it truncates), therefore 1 - term is rounded up, as desired. + nonInternalAssetRatio := osmomath.OneDec().Sub(ratioOfInputAssetLiquidityToTotalLiquidity.Dec()) + return nonInternalAssetRatio, nil +} + +// Route a pool join attempt to either a single-asset join or all-asset join (mutates pool state) +// Eventually, we intend to switch this to a COW wrapped pa for better performance +func (p *Pool) joinPoolSharesInternal(ctx sdk.Context, tokensIn sdk.Coins, spreadFactor osmomath.Dec) (numShares osmomath.Int, tokensJoined sdk.Coins, err error) { + if !tokensIn.DenomsSubsetOf(p.GetTotalPoolLiquidity(ctx)) { + return osmomath.ZeroInt(), sdk.NewCoins(), errors.New("attempted joining pool with assets that do not exist in pool") + } + + if len(tokensIn) == 1 && tokensIn[0].Amount.GT(osmomath.OneInt()) { + numShares, err = p.calcSingleAssetJoinShares(tokensIn[0], spreadFactor) + if err != nil { + return osmomath.ZeroInt(), sdk.NewCoins(), err + } + + tokensJoined = tokensIn + } else if len(tokensIn) != p.NumAssets() { + return osmomath.ZeroInt(), sdk.NewCoins(), errors.New( + "stableswap pool only supports LP'ing with one asset, or all assets in pool") + } else { + // Add all exact coins we can (no swap). ctx arg doesn't matter for Stableswap + var remCoins sdk.Coins + numShares, remCoins, err = cfmm_common.MaximalExactRatioJoin(p, sdk.Context{}, tokensIn) + if err != nil { + return osmomath.ZeroInt(), sdk.NewCoins(), err + } + + tokensJoined = tokensIn.Sub(remCoins...) + } + + p.updatePoolForJoin(tokensJoined, numShares) + + if err = validatePoolLiquidity(p.PoolLiquidity, p.ScalingFactors); err != nil { + return osmomath.ZeroInt(), sdk.NewCoins(), err + } + + return numShares, tokensJoined, nil +} diff --git a/tests/interchaintest/osmosis/gamm/pool-models/stableswap/cfmm.py b/tests/interchaintest/osmosis/gamm/pool-models/stableswap/cfmm.py new file mode 100644 index 00000000..af70f079 --- /dev/null +++ b/tests/interchaintest/osmosis/gamm/pool-models/stableswap/cfmm.py @@ -0,0 +1,34 @@ +# This file is for solving for close enough to exact solutions +# to the CFMM equation, to generate test vectors for amm_test.go + +cfmm = lambda x,y: x*y*(x*x + y*y) + +x0, y0 = 100, 100 +yin = 1000 +err_threshold = .001 + +def approx_eq(a, b, tol): + return abs(a - b) <= tol + +def binary_search(x0, y0, yin): + k = cfmm(x0, y0) + yf = y0 + yin + x_low_est = 0 + x_high_est = x0 + x_est = (x_high_est + x_low_est) / 2. + cur_k = cfmm(x_est, yf) + while not approx_eq(cur_k, k, err_threshold): + # x is too high + if cur_k > k: + x_high_est = x_est + elif cur_k < k: + x_low_est = x_est + x_est = (x_high_est + x_low_est) / 2. + cur_k = cfmm(x_est, yf) + + return x0 - x_est + +xOut = binary_search(x0, y0, yin) +print(cfmm(x0, y0)) +print(cfmm(x0 - xOut, y0 + yin)) +print(xOut) \ No newline at end of file diff --git a/tests/interchaintest/osmosis/gamm/pool-models/stableswap/codec.go b/tests/interchaintest/osmosis/gamm/pool-models/stableswap/codec.go new file mode 100644 index 00000000..9bc5c9d1 --- /dev/null +++ b/tests/interchaintest/osmosis/gamm/pool-models/stableswap/codec.go @@ -0,0 +1,64 @@ +package stableswap + +import ( + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/msgservice" + + authzcodec "github.com/cosmos/cosmos-sdk/x/authz/codec" + + types "github.com/osmosis-labs/fee-abstraction/v7/tests/interchaintest/osmosis/gamm/types" + poolmanagertypes "github.com/osmosis-labs/fee-abstraction/v7/tests/interchaintest/osmosis/poolmanager/types" +) + +// RegisterLegacyAminoCodec registers the necessary x/gamm interfaces and concrete types +// on the provided LegacyAmino codec. These types are used for Amino JSON serialization. +func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { + cdc.RegisterConcrete(&Pool{}, "osmosis/gamm/StableswapPool", nil) + cdc.RegisterConcrete(&MsgCreateStableswapPool{}, "osmosis/gamm/create-stableswap-pool", nil) + cdc.RegisterConcrete(&MsgStableSwapAdjustScalingFactors{}, "osmosis/gamm/stableswap-adjust-scaling-factors", nil) + cdc.RegisterConcrete(&PoolParams{}, "osmosis/gamm/StableswapPoolParams", nil) +} + +func RegisterInterfaces(registry codectypes.InterfaceRegistry) { + registry.RegisterInterface( + "osmosis.poolmanager.v1beta1.PoolI", + (*poolmanagertypes.PoolI)(nil), + &Pool{}, + ) + registry.RegisterInterface( + "osmosis.gamm.v1beta1.PoolI", // N.B.: the old proto-path is preserved for backwards-compatibility. + (*types.CFMMPoolI)(nil), + &Pool{}, + ) + registry.RegisterImplementations( + (*sdk.Msg)(nil), + &MsgCreateStableswapPool{}, + &MsgStableSwapAdjustScalingFactors{}, + ) + msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc) +} + +var ( + amino = codec.NewLegacyAmino() + + // ModuleCdc references the global x/bank module codec. Note, the codec should + // ONLY be used in certain instances of tests and for JSON encoding as Amino is + // still used for that purpose. + // + // The actual codec used for serialization should be provided to x/staking and + // defined at the application level. + ModuleCdc = codec.NewAminoCodec(amino) +) + +func init() { + RegisterLegacyAminoCodec(amino) + // Register all Amino interfaces and concrete types on the authz Amino codec so that this can later be + // used to properly serialize MsgGrant and MsgExec instances + sdk.RegisterLegacyAminoCodec(amino) + RegisterLegacyAminoCodec(authzcodec.Amino) + amino.Seal() +} + +const PoolTypeName string = "Stableswap" diff --git a/tests/interchaintest/osmosis/gamm/pool-models/stableswap/msgs.go b/tests/interchaintest/osmosis/gamm/pool-models/stableswap/msgs.go new file mode 100644 index 00000000..8f0627f7 --- /dev/null +++ b/tests/interchaintest/osmosis/gamm/pool-models/stableswap/msgs.go @@ -0,0 +1,171 @@ +package stableswap + +import ( + errorsmod "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + + "github.com/osmosis-labs/fee-abstraction/v7/tests/interchaintest/osmosis/gamm/types" + poolmanagertypes "github.com/osmosis-labs/fee-abstraction/v7/tests/interchaintest/osmosis/poolmanager/types" +) + +const ( + TypeMsgCreateStableswapPool = "create_stableswap_pool" + TypeMsgStableSwapAdjustScalingFactors = "stable_swap_adjust_scaling_factors" +) + +var ( + _ sdk.Msg = &MsgCreateStableswapPool{} + _ poolmanagertypes.CreatePoolMsg = &MsgCreateStableswapPool{} +) + +func NewMsgCreateStableswapPool( + sender sdk.AccAddress, + poolParams PoolParams, + initialLiquidity sdk.Coins, + scalingFactors []uint64, + futurePoolGovernor string, +) MsgCreateStableswapPool { + return MsgCreateStableswapPool{ + Sender: sender.String(), + PoolParams: &poolParams, + InitialPoolLiquidity: initialLiquidity, + ScalingFactors: scalingFactors, + FuturePoolGovernor: futurePoolGovernor, + } +} + +func (msg MsgCreateStableswapPool) Route() string { return types.RouterKey } +func (msg MsgCreateStableswapPool) Type() string { return TypeMsgCreateStableswapPool } +func (msg MsgCreateStableswapPool) ValidateBasic() error { + _, err := sdk.AccAddressFromBech32(msg.Sender) + if err != nil { + return errorsmod.Wrapf(sdkerrors.ErrInvalidAddress, "Invalid sender address (%s)", err) + } + + err = msg.PoolParams.Validate() + if err != nil { + return err + } + + // validation for scaling factors + scalingFactors := msg.ScalingFactors + // The message's scaling factors must be empty or a valid set of scaling factors + if len(scalingFactors) != 0 { + if err = validateScalingFactors(scalingFactors, len(msg.InitialPoolLiquidity)); err != nil { + return err + } + } else { + for i := 0; i < len(msg.InitialPoolLiquidity); i += 1 { + scalingFactors = append(scalingFactors, 1) + } + } + + // validation for pool initial liquidity + // The message's pool liquidity must have between 2 and 8 assets with at most 10B post-scaled units in each + if err = validatePoolLiquidity(msg.InitialPoolLiquidity, scalingFactors); err != nil { + return err + } + + // validation for scaling factor owner + if err = validateScalingFactorController(msg.ScalingFactorController); err != nil { + return err + } + + // validation for future governor + if err = types.ValidateFutureGovernor(msg.FuturePoolGovernor); err != nil { + return err + } + + return nil +} + +func (msg MsgCreateStableswapPool) GetSignBytes() []byte { + return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(&msg)) +} + +func (msg MsgCreateStableswapPool) GetSigners() []sdk.AccAddress { + sender, err := sdk.AccAddressFromBech32(msg.Sender) + if err != nil { + panic(err) + } + return []sdk.AccAddress{sender} +} + +/// Implement the CreatePoolMsg interface + +func (msg MsgCreateStableswapPool) PoolCreator() sdk.AccAddress { + sender, err := sdk.AccAddressFromBech32(msg.Sender) + if err != nil { + panic(err) + } + return sender +} + +func (msg MsgCreateStableswapPool) Validate(ctx sdk.Context) error { + return msg.ValidateBasic() +} + +func (msg MsgCreateStableswapPool) InitialLiquidity() sdk.Coins { + return msg.InitialPoolLiquidity +} + +func (msg MsgCreateStableswapPool) CreatePool(ctx sdk.Context, poolId uint64) (poolmanagertypes.PoolI, error) { + stableswapPool, err := NewStableswapPool(poolId, *msg.PoolParams, msg.InitialPoolLiquidity, + msg.ScalingFactors, msg.ScalingFactorController, msg.FuturePoolGovernor) + if err != nil { + return nil, err + } + + return &stableswapPool, nil +} + +func (msg MsgCreateStableswapPool) GetPoolType() poolmanagertypes.PoolType { + return poolmanagertypes.Stableswap +} + +var _ sdk.Msg = &MsgStableSwapAdjustScalingFactors{} + +// Implement sdk.Msg +func NewMsgStableSwapAdjustScalingFactors( + sender string, + poolID uint64, + scalingFactors []uint64, +) MsgStableSwapAdjustScalingFactors { + return MsgStableSwapAdjustScalingFactors{ + Sender: sender, + PoolID: poolID, + ScalingFactors: scalingFactors, + } +} + +func (msg MsgStableSwapAdjustScalingFactors) Route() string { + return types.RouterKey +} + +func (msg MsgStableSwapAdjustScalingFactors) Type() string { return TypeMsgCreateStableswapPool } +func (msg MsgStableSwapAdjustScalingFactors) ValidateBasic() error { + if msg.Sender == "" { + return nil + } + + _, err := sdk.AccAddressFromBech32(msg.Sender) + if err != nil { + return errorsmod.Wrapf(sdkerrors.ErrInvalidAddress, "Invalid sender address (%s)", err) + } + + return nil +} + +func (msg MsgStableSwapAdjustScalingFactors) GetSignBytes() []byte { + return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(&msg)) +} + +func (msg MsgStableSwapAdjustScalingFactors) GetSigners() []sdk.AccAddress { + scalingFactorGovernor, err := sdk.AccAddressFromBech32(msg.Sender) + if err != nil { + panic(err) + } + + return []sdk.AccAddress{scalingFactorGovernor} +} diff --git a/tests/interchaintest/osmosis/gamm/pool-models/stableswap/pool.go b/tests/interchaintest/osmosis/gamm/pool-models/stableswap/pool.go new file mode 100644 index 00000000..16c2cc9d --- /dev/null +++ b/tests/interchaintest/osmosis/gamm/pool-models/stableswap/pool.go @@ -0,0 +1,500 @@ +package stableswap + +import ( + "encoding/json" + "errors" + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" + + errorsmod "cosmossdk.io/errors" + + "github.com/osmosis-labs/fee-abstraction/v7/tests/interchaintest/osmosis/gamm/pool-models/internal/cfmm_common" + "github.com/osmosis-labs/fee-abstraction/v7/tests/interchaintest/osmosis/gamm/types" + "github.com/osmosis-labs/fee-abstraction/v7/tests/interchaintest/osmosis/osmomath" + poolmanagertypes "github.com/osmosis-labs/fee-abstraction/v7/tests/interchaintest/osmosis/poolmanager/types" +) + +var ( + _ poolmanagertypes.PoolI = &Pool{} + _ types.CFMMPoolI = &Pool{} +) + +// NewStableswapPool returns a stableswap pool +// Invariants that are assumed to be satisfied and not checked: +// * poolID doesn't already exist +func NewStableswapPool(poolId uint64, + stableswapPoolParams PoolParams, initialLiquidity sdk.Coins, + scalingFactors []uint64, scalingFactorController string, + futureGovernor string, +) (Pool, error) { + if len(scalingFactors) == 0 { + scalingFactors = make([]uint64, len(initialLiquidity)) + for i := range scalingFactors { + scalingFactors[i] = 1 + } + } + + scalingFactors, err := applyScalingFactorMultiplier(scalingFactors) + if err != nil { + return Pool{}, err + } + + if err = validateScalingFactors(scalingFactors, len(initialLiquidity)); err != nil { + return Pool{}, err + } + + if err = validatePoolLiquidity(initialLiquidity, scalingFactors); err != nil { + return Pool{}, err + } + + if err = types.ValidateFutureGovernor(futureGovernor); err != nil { + return Pool{}, err + } + + pool := Pool{ + Address: poolmanagertypes.NewPoolAddress(poolId).String(), + Id: poolId, + PoolParams: stableswapPoolParams, + TotalShares: sdk.NewCoin(types.GetPoolShareDenom(poolId), types.InitPoolSharesSupply), + PoolLiquidity: initialLiquidity, + ScalingFactors: scalingFactors, + ScalingFactorController: scalingFactorController, + FuturePoolGovernor: futureGovernor, + } + + return pool, nil +} + +func (p Pool) GetAddress() sdk.AccAddress { + addr, err := sdk.AccAddressFromBech32(p.Address) + if err != nil { + panic(fmt.Sprintf("could not bech32 decode address of pool with id: %d", p.GetId())) + } + return addr +} + +func (p Pool) String() string { + out, err := json.Marshal(p) + if err != nil { + panic(err) + } + return string(out) +} + +func (p Pool) GetId() uint64 { + return p.Id +} + +func (p Pool) GetSpreadFactor(ctx sdk.Context) osmomath.Dec { + return p.PoolParams.SwapFee +} + +func (p Pool) GetExitFee(ctx sdk.Context) osmomath.Dec { + return p.PoolParams.ExitFee +} + +func (p Pool) IsActive(ctx sdk.Context) bool { + return true +} + +// Returns the coins in the pool owned by all LP shareholders +func (p Pool) GetTotalPoolLiquidity(ctx sdk.Context) sdk.Coins { + return p.PoolLiquidity +} + +func (p Pool) GetTotalShares() osmomath.Int { + return p.TotalShares.Amount +} + +func (p Pool) GetScalingFactors() []uint64 { + return p.ScalingFactors +} + +func (p Pool) GetType() poolmanagertypes.PoolType { + return poolmanagertypes.Stableswap +} + +// CONTRACT: scaling factors follow the same index with pool liquidity denoms +func (p Pool) GetScalingFactorByDenom(denom string) uint64 { + for i, coin := range p.PoolLiquidity { + if denom == coin.Denom { + return p.ScalingFactors[i] + } + } + + return 0 +} + +func (p Pool) NumAssets() int { + return len(p.PoolLiquidity) +} + +// scaleCoin returns the BigDec amount of the +// input token after scaling it by the token's scaling factor +func (p Pool) scaleCoin(input sdk.Coin, roundingDirection osmomath.RoundingDirection) (osmomath.BigDec, error) { + scalingFactor := p.GetScalingFactorByDenom(input.Denom) + scaledAmount, err := osmomath.DivIntByU64ToBigDec(input.Amount, scalingFactor, roundingDirection) + if err != nil { + return osmomath.BigDec{}, err + } + return scaledAmount, nil +} + +// getDescaledPoolAmt descales the passed in amount +// by the scaling factor of the passed in denom +func (p Pool) getDescaledPoolAmt(denom string, amount osmomath.BigDec) osmomath.Dec { + scalingFactor := p.GetScalingFactorByDenom(denom) + + return amount.MulInt64(int64(scalingFactor)).Dec() +} + +// scaledSortedPoolReserves sorts and scales passed in pool reserves such that the denom +// `first` and the denom `second` are ordered first and second, +// respectively. The rest of the ordering is not specified but +// deterministic. +// +// Returns reserve amounts as an array of type BigDec. +func (p Pool) scaledSortedPoolReserves(first string, second string, round osmomath.RoundingDirection) ([]osmomath.BigDec, error) { + reorderedLiquidity, reorderedScalingFactors, err := p.reorderReservesAndScalingFactors(first, second) + if err != nil { + return nil, err + } + + if err := validateScalingFactors(reorderedScalingFactors, len(reorderedLiquidity)); err != nil { + return nil, err + } + + return osmomath.DivCoinAmtsByU64ToBigDec(reorderedLiquidity, reorderedScalingFactors, round) +} + +// reorderReservesAndScalingFactors takes the pool liquidity and scaling factors, and reorders them s.t. +// reorderedReserves[0] = p.GetLiquidity().AmountOf(first) +// reorderedScalingFactors[0] = p.ScalingFactors[p.getLiquidityIndexMap()[first]] +// Similarly, reordering happens for second and index 1. +// +// The remainder of the lists includes every remaining (reserve asset, scaling factor) pair, +// in a deterministic but unspecified order. +// +// Returns an error if the pool does not contain either of first or second. +func (p Pool) reorderReservesAndScalingFactors(first string, second string) ([]sdk.Coin, []uint64, error) { + coins := p.PoolLiquidity + scalingFactors := p.ScalingFactors + reorderedReserves := make([]sdk.Coin, len(coins)) + reorderedScalingFactors := make([]uint64, len(coins)) + curIndex := 2 + for i, coin := range coins { + if coin.Denom == first { + reorderedReserves[0] = coin + reorderedScalingFactors[0] = scalingFactors[i] + } else if coin.Denom == second { + reorderedReserves[1] = coin + reorderedScalingFactors[1] = scalingFactors[i] + } else { + // if we hit this case, then oneof first or second is not in pool liquidity + if curIndex == len(coins) { + return nil, nil, fmt.Errorf("one of denom (%s, %s) not found in pool liquidity", first, second) + } + reorderedReserves[curIndex] = coin + reorderedScalingFactors[curIndex] = scalingFactors[i] + curIndex += 1 + } + } + return reorderedReserves, reorderedScalingFactors, nil +} + +// updatePoolLiquidityForSwap updates the pool liquidity. +// It requires caller to validate that tokensIn and tokensOut only consist of +// denominations in the pool. +// The function sanity checks this, and panics if not the case. +func (p *Pool) updatePoolLiquidityForSwap(tokensIn sdk.Coins, tokensOut sdk.Coins) { + numTokens := p.PoolLiquidity.Len() + // update liquidity + p.PoolLiquidity = p.PoolLiquidity.Add(tokensIn...).Sub(tokensOut...) + // sanity check that no new denoms were added + if len(p.PoolLiquidity) != numTokens { + panic("updatePoolLiquidityForSwap changed number of tokens in pool") + } +} + +// updatePoolLiquidityForExit updates the pool liquidity and total shares after an exit. +// The function sanity checks that not all tokens of a given denom are removed, +// and panics if that's the case. +func (p *Pool) updatePoolLiquidityForExit(tokensOut sdk.Coins, exitingShares osmomath.Int) { + p.updatePoolLiquidityForSwap(sdk.Coins{}, tokensOut) + p.TotalShares.Amount = p.TotalShares.Amount.Sub(exitingShares) +} + +// updatePoolForJoin updates the pool liquidity and total shares after a join. +// The function sanity checks that no new denoms were added to the pool +// and panics if this is the case. +func (p *Pool) updatePoolForJoin(tokensIn sdk.Coins, newShares osmomath.Int) { + numTokens := p.NumAssets() + p.PoolLiquidity = p.PoolLiquidity.Add(tokensIn...) + if len(p.PoolLiquidity) != numTokens { + panic(fmt.Sprintf("updatePoolForJoin changed number of tokens in pool from %d to %d", numTokens, len(p.PoolLiquidity))) + } + p.TotalShares.Amount = p.TotalShares.Amount.Add(newShares) +} + +// TODO: These should all get moved to amm.go +// CalcOutAmtGivenIn calculates expected output amount given input token +func (p Pool) CalcOutAmtGivenIn(ctx sdk.Context, tokenIn sdk.Coins, tokenOutDenom string, spreadFactor osmomath.Dec) (tokenOut sdk.Coin, err error) { + if tokenIn.Len() != 1 { + return sdk.Coin{}, errors.New("stableswap CalcOutAmtGivenIn: tokenIn is of wrong length") + } + outAmtDec, err := p.calcOutAmtGivenIn(tokenIn[0], tokenOutDenom, spreadFactor) + if err != nil { + return sdk.Coin{}, err + } + + // we ignore the decimal component, as token out amount must round down + tokenOutAmt := outAmtDec.TruncateInt() + if !tokenOutAmt.IsPositive() { + return sdk.Coin{}, errorsmod.Wrapf(types.ErrInvalidMathApprox, + fmt.Sprintf("token amount must be positive, got %v", tokenOutAmt)) + } + return sdk.NewCoin(tokenOutDenom, tokenOutAmt), nil +} + +// SwapOutAmtGivenIn executes a swap given a desired input amount +func (p *Pool) SwapOutAmtGivenIn(ctx sdk.Context, tokenIn sdk.Coins, tokenOutDenom string, spreadFactor osmomath.Dec) (tokenOut sdk.Coin, err error) { + if err = validatePoolLiquidity(p.PoolLiquidity.Add(tokenIn...), p.ScalingFactors); err != nil { + return sdk.Coin{}, err + } + + tokenOut, err = p.CalcOutAmtGivenIn(ctx, tokenIn, tokenOutDenom, spreadFactor) + if err != nil { + return sdk.Coin{}, err + } + + p.updatePoolLiquidityForSwap(tokenIn, sdk.NewCoins(tokenOut)) + + return tokenOut, nil +} + +// CalcInAmtGivenOut calculates input amount needed to receive given output +func (p Pool) CalcInAmtGivenOut(ctx sdk.Context, tokenOut sdk.Coins, tokenInDenom string, spreadFactor osmomath.Dec) (tokenIn sdk.Coin, err error) { + if tokenOut.Len() != 1 { + return sdk.Coin{}, errors.New("stableswap CalcInAmtGivenOut: tokenOut is of wrong length") + } + + amt, err := p.calcInAmtGivenOut(tokenOut[0], tokenInDenom, spreadFactor) + if err != nil { + return sdk.Coin{}, err + } + + // We round up tokenInAmt, as this is what charged for the swap, for the precise amount out. + // Otherwise, the pool would under-charge by this rounding error. + tokenInAmt := amt.Ceil().TruncateInt() + + if !tokenInAmt.IsPositive() { + return sdk.Coin{}, errorsmod.Wrapf(types.ErrInvalidMathApprox, "token amount must be positive") + } + return sdk.NewCoin(tokenInDenom, tokenInAmt), nil +} + +// SwapInAmtGivenOut executes a swap given a desired output amount +func (p *Pool) SwapInAmtGivenOut(ctx sdk.Context, tokenOut sdk.Coins, tokenInDenom string, spreadFactor osmomath.Dec) (tokenIn sdk.Coin, err error) { + tokenIn, err = p.CalcInAmtGivenOut(ctx, tokenOut, tokenInDenom, spreadFactor) + if err != nil { + return sdk.Coin{}, err + } + + if err = validatePoolLiquidity(p.PoolLiquidity.Add(tokenIn), p.ScalingFactors); err != nil { + return sdk.Coin{}, err + } + + p.updatePoolLiquidityForSwap(sdk.NewCoins(tokenIn), tokenOut) + + return tokenIn, nil +} + +// SpotPrice calculates the approximate amount of `baseDenom` one would receive for +// an input dx of `quoteDenom` (to simplify calculations, we approximate dx = 1) +func (p Pool) SpotPrice(ctx sdk.Context, quoteAssetDenom string, baseAssetDenom string) (osmomath.BigDec, error) { + spotPriceDec, err := p.spotPrice(quoteAssetDenom, baseAssetDenom) + if err != nil { + return osmomath.BigDec{}, err + } + return osmomath.BigDecFromDec(spotPriceDec), nil +} + +func (p Pool) Copy() Pool { + p2 := p + p2.PoolLiquidity = sdk.NewCoins(p.PoolLiquidity...) + return p2 +} + +func (p *Pool) CalcJoinPoolShares(ctx sdk.Context, tokensIn sdk.Coins, spreadFactor osmomath.Dec) (numShares osmomath.Int, newLiquidity sdk.Coins, err error) { + pCopy := p.Copy() + return pCopy.joinPoolSharesInternal(ctx, tokensIn, spreadFactor) +} + +// CalcJoinPoolNoSwapShares calculates the number of shares created to execute an all-asset pool join with the provided amount of `tokensIn`. +// The input tokens must contain the same tokens as in the pool. +// +// Returns the number of shares created, the amount of coins actually joined into the pool as not all may tokens may be joinable. +// If an all-asset join is not possible, returns an error. +func (p Pool) CalcJoinPoolNoSwapShares(ctx sdk.Context, tokensIn sdk.Coins, spreadFactor osmomath.Dec) (numShares osmomath.Int, tokensJoined sdk.Coins, err error) { + // ensure that there aren't too many or too few assets in `tokensIn` + if tokensIn.Len() != p.NumAssets() || !tokensIn.DenomsSubsetOf(p.GetTotalPoolLiquidity(ctx)) { + return osmomath.ZeroInt(), sdk.NewCoins(), errors.New("no-swap joins require LP'ing with all assets in pool") + } + + // execute a no-swap join with as many tokens as possible given a perfect ratio: + // * numShares is how many shares are perfectly matched. + // * remainingTokensIn is how many coins we have left to join that have not already been used. + numShares, remainingTokensIn, err := cfmm_common.MaximalExactRatioJoin(&p, ctx, tokensIn) + if err != nil { + return osmomath.ZeroInt(), sdk.NewCoins(), err + } + + // ensure that no more tokens have been joined than is possible with the given `tokensIn` + tokensJoined = tokensIn.Sub(remainingTokensIn...) + if tokensJoined.IsAnyGT(tokensIn) { + return osmomath.ZeroInt(), sdk.NewCoins(), errors.New("an error has occurred, more coins joined than token In") + } + + return numShares, tokensJoined, nil +} + +func (p *Pool) JoinPool(ctx sdk.Context, tokensIn sdk.Coins, spreadFactor osmomath.Dec) (osmomath.Int, error) { + numShares, _, err := p.joinPoolSharesInternal(ctx, tokensIn, spreadFactor) + return numShares, err +} + +func (p *Pool) JoinPoolNoSwap(ctx sdk.Context, tokensIn sdk.Coins, spreadFactor osmomath.Dec) (osmomath.Int, error) { + newShares, tokensJoined, err := p.CalcJoinPoolNoSwapShares(ctx, tokensIn, spreadFactor) + if err != nil { + return osmomath.Int{}, err + } + + // update pool with the calculated share and liquidity needed to join pool + p.updatePoolForJoin(tokensJoined, newShares) + return newShares, nil +} + +func (p *Pool) ExitPool(ctx sdk.Context, exitingShares osmomath.Int, exitFee osmomath.Dec) (exitingCoins sdk.Coins, err error) { + exitingCoins, err = p.CalcExitPoolCoinsFromShares(ctx, exitingShares, exitFee) + if err != nil { + return sdk.Coins{}, err + } + + postExitLiquidity := p.PoolLiquidity.Sub(exitingCoins...) + if err := validatePoolLiquidity(postExitLiquidity, p.ScalingFactors); err != nil { + return sdk.Coins{}, err + } + + p.updatePoolLiquidityForExit(exitingCoins, exitingShares) + + return exitingCoins, nil +} + +func (p Pool) CalcExitPoolCoinsFromShares(ctx sdk.Context, exitingShares osmomath.Int, exitFee osmomath.Dec) (exitingCoins sdk.Coins, err error) { + return cfmm_common.CalcExitPool(ctx, &p, exitingShares, exitFee) +} + +// SetScalingFactors sets scaling factors for pool to the given amount +// It should only be able to be successfully called by the pool's ScalingFactorGovernor +// TODO: move commented test for this function from x/gamm/keeper/pool_service_test.go once a pool_test.go file has been created for stableswap +func (p *Pool) SetScalingFactors(ctx sdk.Context, scalingFactors []uint64, sender string) error { + if sender != p.ScalingFactorController { + return types.ErrNotScalingFactorGovernor + } + + scalingFactors, err := applyScalingFactorMultiplier(scalingFactors) + if err != nil { + return err + } + + if err = validateScalingFactors(scalingFactors, p.PoolLiquidity.Len()); err != nil { + return err + } + + if err = validatePoolLiquidity(p.PoolLiquidity, scalingFactors); err != nil { + return err + } + + p.ScalingFactors = scalingFactors + return nil +} + +func validateScalingFactorController(scalingFactorController string) error { + if len(scalingFactorController) == 0 { + return nil + } + _, err := sdk.AccAddressFromBech32(scalingFactorController) + return err +} + +func validateScalingFactors(scalingFactors []uint64, numAssets int) error { + if len(scalingFactors) != numAssets { + return types.ErrInvalidScalingFactorLength + } + + for _, scalingFactor := range scalingFactors { + if int64(scalingFactor) <= 0 { + return types.ErrInvalidScalingFactors + } + } + + return nil +} + +// assumes liquidity is all pool liquidity, in correct sorted order +func validatePoolLiquidity(liquidity sdk.Coins, scalingFactors []uint64) error { + liquidityCount := len(liquidity) + scalingFactorCount := len(scalingFactors) + if liquidityCount != scalingFactorCount { + return types.LiquidityAndScalingFactorCountMismatchError{LiquidityCount: liquidityCount, ScalingFactorCount: scalingFactorCount} + } + + if liquidityCount < types.MinNumOfAssetsInPool { + return types.ErrTooFewPoolAssets + } else if liquidityCount > types.MaxNumOfAssetsInPool { + return types.ErrTooManyPoolAssets + } + + liquidityCopy := make(sdk.Coins, liquidityCount) + copy(liquidityCopy, liquidity) + liquidityCopy.Sort() + + for i, asset := range liquidity { + if asset != liquidityCopy[i] { + return types.UnsortedPoolLiqError{ActualLiquidity: liquidity} + } + + scaledAmount := asset.Amount.Quo(osmomath.NewInt(int64(scalingFactors[i]))) + if scaledAmount.GT(types.StableswapMaxScaledAmtPerAsset) { + return types.ErrHitMaxScaledAssets + } else if scaledAmount.LT(osmomath.NewInt(types.StableswapMinScaledAmtPerAsset)) { + return types.ErrHitMinScaledAssets + } + } + + return nil +} + +func applyScalingFactorMultiplier(scalingFactors []uint64) ([]uint64, error) { + newScalingFactors := make([]uint64, len(scalingFactors)) + for i, scalingFactor := range scalingFactors { + newScalingFactors[i] = scalingFactor * types.ScalingFactorMultiplier + + if newScalingFactors[i] < scalingFactor { + return nil, types.ErrInvalidScalingFactors + } + } + + return newScalingFactors, nil +} + +func (p *Pool) AsSerializablePool() poolmanagertypes.PoolI { + return p +} + +// GetPoolDenoms implements types.CFMMPoolI. +func (p *Pool) GetPoolDenoms(ctx sdk.Context) []string { + liquidity := p.GetTotalPoolLiquidity(ctx) + return liquidity.Denoms() +} diff --git a/tests/interchaintest/osmosis/gamm/pool-models/stableswap/pool_params.go b/tests/interchaintest/osmosis/gamm/pool-models/stableswap/pool_params.go new file mode 100644 index 00000000..31724529 --- /dev/null +++ b/tests/interchaintest/osmosis/gamm/pool-models/stableswap/pool_params.go @@ -0,0 +1,25 @@ +package stableswap + +import ( + "github.com/osmosis-labs/fee-abstraction/v7/tests/interchaintest/osmosis/gamm/types" + "github.com/osmosis-labs/fee-abstraction/v7/tests/interchaintest/osmosis/osmomath" +) + +func (params PoolParams) Validate() error { + if params.ExitFee.IsNegative() { + return types.ErrNegativeExitFee + } + + if params.ExitFee.GTE(osmomath.OneDec()) { + return types.ErrTooMuchExitFee + } + + if params.SwapFee.IsNegative() { + return types.ErrNegativeSpreadFactor + } + + if params.SwapFee.GTE(osmomath.OneDec()) { + return types.ErrTooMuchSpreadFactor + } + return nil +} diff --git a/tests/interchaintest/osmosis/gamm/pool-models/stableswap/stableswap_pool.pb.go b/tests/interchaintest/osmosis/gamm/pool-models/stableswap/stableswap_pool.pb.go new file mode 100644 index 00000000..03d4fd68 --- /dev/null +++ b/tests/interchaintest/osmosis/gamm/pool-models/stableswap/stableswap_pool.pb.go @@ -0,0 +1,948 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: osmosis/gamm/poolmodels/stableswap/v1beta1/stableswap_pool.proto + +package stableswap + +import ( + cosmossdk_io_math "cosmossdk.io/math" + fmt "fmt" + _ "github.com/cosmos/cosmos-proto" + github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" + types "github.com/cosmos/cosmos-sdk/types" + _ "github.com/cosmos/cosmos-sdk/types/tx/amino" + _ "github.com/cosmos/cosmos-sdk/x/auth/types" + _ "github.com/cosmos/gogoproto/gogoproto" + proto "github.com/cosmos/gogoproto/proto" + _ "google.golang.org/protobuf/types/known/durationpb" + _ "google.golang.org/protobuf/types/known/timestamppb" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// PoolParams defined the parameters that will be managed by the pool +// governance in the future. This params are not managed by the chain +// governance. Instead they will be managed by the token holders of the pool. +// The pool's token holders are specified in future_pool_governor. +type PoolParams struct { + SwapFee cosmossdk_io_math.LegacyDec `protobuf:"bytes,1,opt,name=swap_fee,json=swapFee,proto3,customtype=cosmossdk.io/math.LegacyDec" json:"swap_fee" yaml:"swap_fee"` + // N.B.: exit fee is disabled during pool creation in x/poolmanager. While old + // pools can maintain a non-zero fee. No new pool can be created with non-zero + // fee anymore + ExitFee cosmossdk_io_math.LegacyDec `protobuf:"bytes,2,opt,name=exit_fee,json=exitFee,proto3,customtype=cosmossdk.io/math.LegacyDec" json:"exit_fee" yaml:"exit_fee"` +} + +func (m *PoolParams) Reset() { *m = PoolParams{} } +func (m *PoolParams) String() string { return proto.CompactTextString(m) } +func (*PoolParams) ProtoMessage() {} +func (*PoolParams) Descriptor() ([]byte, []int) { + return fileDescriptor_b99ab4400f54fe92, []int{0} +} +func (m *PoolParams) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *PoolParams) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_PoolParams.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *PoolParams) XXX_Merge(src proto.Message) { + xxx_messageInfo_PoolParams.Merge(m, src) +} +func (m *PoolParams) XXX_Size() int { + return m.Size() +} +func (m *PoolParams) XXX_DiscardUnknown() { + xxx_messageInfo_PoolParams.DiscardUnknown(m) +} + +var xxx_messageInfo_PoolParams proto.InternalMessageInfo + +// Pool is the stableswap Pool struct +type Pool struct { + Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty" yaml:"address"` + Id uint64 `protobuf:"varint,2,opt,name=id,proto3" json:"id,omitempty"` + PoolParams PoolParams `protobuf:"bytes,3,opt,name=pool_params,json=poolParams,proto3" json:"pool_params" yaml:"stableswap_pool_params"` + // This string specifies who will govern the pool in the future. + // Valid forms of this are: + // {token name},{duration} + // {duration} + // where {token name} if specified is the token which determines the + // governor, and if not specified is the LP token for this pool.duration is + // a time specified as 0w,1w,2w, etc. which specifies how long the token + // would need to be locked up to count in governance. 0w means no lockup. + FuturePoolGovernor string `protobuf:"bytes,4,opt,name=future_pool_governor,json=futurePoolGovernor,proto3" json:"future_pool_governor,omitempty" yaml:"future_pool_governor"` + // sum of all LP shares + TotalShares types.Coin `protobuf:"bytes,5,opt,name=total_shares,json=totalShares,proto3" json:"total_shares" yaml:"total_shares"` + // assets in the pool + PoolLiquidity github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,6,rep,name=pool_liquidity,json=poolLiquidity,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"pool_liquidity"` + // for calculation amognst assets with different precisions + ScalingFactors []uint64 `protobuf:"varint,7,rep,packed,name=scaling_factors,json=scalingFactors,proto3" json:"scaling_factors,omitempty" yaml:"stableswap_scaling_factors"` + // scaling_factor_controller is the address can adjust pool scaling factors + ScalingFactorController string `protobuf:"bytes,8,opt,name=scaling_factor_controller,json=scalingFactorController,proto3" json:"scaling_factor_controller,omitempty" yaml:"scaling_factor_controller"` +} + +func (m *Pool) Reset() { *m = Pool{} } +func (*Pool) ProtoMessage() {} +func (*Pool) Descriptor() ([]byte, []int) { + return fileDescriptor_b99ab4400f54fe92, []int{1} +} +func (m *Pool) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Pool) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Pool.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Pool) XXX_Merge(src proto.Message) { + xxx_messageInfo_Pool.Merge(m, src) +} +func (m *Pool) XXX_Size() int { + return m.Size() +} +func (m *Pool) XXX_DiscardUnknown() { + xxx_messageInfo_Pool.DiscardUnknown(m) +} + +var xxx_messageInfo_Pool proto.InternalMessageInfo + +func init() { + proto.RegisterType((*PoolParams)(nil), "osmosis.gamm.poolmodels.stableswap.v1beta1.PoolParams") + proto.RegisterType((*Pool)(nil), "osmosis.gamm.poolmodels.stableswap.v1beta1.Pool") +} + +func init() { + proto.RegisterFile("osmosis/gamm/poolmodels/stableswap/v1beta1/stableswap_pool.proto", fileDescriptor_b99ab4400f54fe92) +} + +var fileDescriptor_b99ab4400f54fe92 = []byte{ + // 684 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x94, 0xbb, 0x6f, 0xd3, 0x40, + 0x18, 0xc0, 0xe3, 0x36, 0x6d, 0x8a, 0x0b, 0xa9, 0x30, 0x95, 0x70, 0x1b, 0x61, 0xa7, 0x16, 0x45, + 0x51, 0x45, 0x7c, 0xb4, 0x48, 0x1d, 0x3a, 0x41, 0x8a, 0x8a, 0x90, 0x2a, 0x54, 0xdc, 0x09, 0x18, + 0xc2, 0xd9, 0xbe, 0x38, 0xa7, 0xda, 0x39, 0xe3, 0x3b, 0x97, 0x66, 0x61, 0x46, 0x4c, 0x8c, 0x8c, + 0x9d, 0x99, 0x18, 0xf8, 0x23, 0x2a, 0x58, 0x3a, 0xa2, 0x0e, 0x06, 0xb5, 0x03, 0x7b, 0xfe, 0x02, + 0x74, 0xe7, 0xcb, 0xab, 0x85, 0x0a, 0xb1, 0x24, 0xf7, 0xbd, 0x7e, 0xdf, 0xe3, 0x3e, 0x9f, 0xfa, + 0x80, 0xd0, 0x88, 0x50, 0x4c, 0x41, 0x00, 0xa3, 0x08, 0xc4, 0x84, 0x84, 0x11, 0xf1, 0x51, 0x48, + 0x01, 0x65, 0xd0, 0x0d, 0x11, 0x7d, 0x03, 0x63, 0xb0, 0xbf, 0xea, 0x22, 0x06, 0x57, 0x47, 0x54, + 0x4d, 0xee, 0x68, 0xc7, 0x09, 0x61, 0x44, 0x5b, 0x91, 0x04, 0x9b, 0x13, 0xec, 0x21, 0xc1, 0x1e, + 0xba, 0xdb, 0x92, 0xb0, 0xb8, 0xe0, 0x09, 0xe7, 0xa6, 0x88, 0x04, 0xb9, 0x90, 0x63, 0x16, 0xe7, + 0x03, 0x12, 0x90, 0x5c, 0xcf, 0x4f, 0x52, 0x7b, 0x1d, 0x46, 0xb8, 0x43, 0x80, 0xf8, 0x95, 0x2a, + 0x23, 0x20, 0x24, 0x08, 0x11, 0x10, 0x92, 0x9b, 0xb6, 0x80, 0x9f, 0x26, 0x90, 0x61, 0xd2, 0x91, + 0x76, 0xf3, 0xbc, 0x9d, 0xe1, 0x08, 0x51, 0x06, 0xa3, 0xb8, 0x0f, 0xc8, 0xf3, 0x02, 0x98, 0xb2, + 0xf6, 0xa0, 0x37, 0x2e, 0x9c, 0xb3, 0xbb, 0x90, 0xa2, 0x81, 0xdd, 0x23, 0x58, 0x26, 0xb0, 0x4e, + 0x14, 0x55, 0xdd, 0x21, 0x24, 0xdc, 0x81, 0x09, 0x8c, 0xa8, 0xf6, 0x4c, 0x9d, 0x11, 0x23, 0x69, + 0x21, 0xa4, 0x2b, 0x55, 0xa5, 0x76, 0xa5, 0xb1, 0x7e, 0x94, 0x99, 0x85, 0x93, 0xcc, 0xac, 0xe4, + 0x20, 0xea, 0xef, 0xd9, 0x98, 0x80, 0x08, 0xb2, 0xb6, 0xbd, 0x8d, 0x02, 0xe8, 0x75, 0x1f, 0x21, + 0xaf, 0x97, 0x99, 0x73, 0x5d, 0x18, 0x85, 0x1b, 0x56, 0x3f, 0xd8, 0x72, 0x4a, 0xfc, 0xb8, 0x85, + 0x10, 0x47, 0xa2, 0x03, 0xcc, 0x04, 0x72, 0xe2, 0x3f, 0x90, 0xfd, 0x60, 0xcb, 0x29, 0xf1, 0xe3, + 0x16, 0x42, 0x1b, 0x77, 0xde, 0xff, 0xfa, 0xbc, 0xb2, 0x34, 0x76, 0xd9, 0xbb, 0x83, 0xfb, 0x19, + 0x76, 0x63, 0x7d, 0x9b, 0x52, 0x8b, 0x5c, 0xd4, 0xee, 0xaa, 0x25, 0xe8, 0xfb, 0x09, 0xa2, 0x54, + 0x76, 0xa5, 0xf5, 0x32, 0xb3, 0x9c, 0xf3, 0xa5, 0xc1, 0x72, 0xfa, 0x2e, 0x5a, 0x59, 0x9d, 0xc0, + 0xbe, 0xa8, 0xb5, 0xe8, 0x4c, 0x60, 0x5f, 0x7b, 0xab, 0xce, 0xf2, 0x4d, 0x68, 0xc6, 0x82, 0xaa, + 0x4f, 0x56, 0x95, 0xda, 0xec, 0xda, 0xba, 0xfd, 0xef, 0xab, 0x62, 0x0f, 0x6b, 0x6a, 0x2c, 0xf3, + 0xe6, 0x7b, 0x99, 0x79, 0x4b, 0x0e, 0x6c, 0x7c, 0x0d, 0x65, 0x0e, 0xcb, 0x51, 0xe3, 0xd1, 0x4b, + 0x99, 0x6f, 0xa5, 0x2c, 0x4d, 0x50, 0xee, 0x12, 0x90, 0x7d, 0x94, 0x74, 0x48, 0xa2, 0x17, 0x45, + 0x2b, 0x66, 0x2f, 0x33, 0x2b, 0x39, 0xec, 0x4f, 0x5e, 0x96, 0xa3, 0xe5, 0x6a, 0x5e, 0xc3, 0x63, + 0xa9, 0xd4, 0x9e, 0xab, 0x57, 0x19, 0x61, 0x30, 0x6c, 0xd2, 0x36, 0x4c, 0x10, 0xd5, 0xa7, 0x44, + 0x4f, 0x0b, 0xb6, 0xdc, 0x62, 0xbe, 0x2d, 0x83, 0xe2, 0x37, 0x09, 0xee, 0x34, 0x2a, 0xb2, 0xec, + 0x1b, 0x79, 0xa6, 0xd1, 0x60, 0xcb, 0x99, 0x15, 0xe2, 0xae, 0x90, 0xb4, 0x44, 0x2d, 0x8b, 0x02, + 0x42, 0xfc, 0x3a, 0xc5, 0x3e, 0x66, 0x5d, 0x7d, 0xba, 0x3a, 0x79, 0x39, 0xfc, 0x1e, 0x87, 0x7f, + 0xfa, 0x61, 0xd6, 0x02, 0xcc, 0xda, 0xa9, 0x6b, 0x7b, 0x24, 0x92, 0xdf, 0x93, 0xfc, 0xab, 0x53, + 0x7f, 0x0f, 0xb0, 0x6e, 0x8c, 0xa8, 0x08, 0xa0, 0xce, 0x35, 0x9e, 0x62, 0xbb, 0x9f, 0x41, 0x7b, + 0xaa, 0xce, 0x51, 0x0f, 0x86, 0xb8, 0x13, 0x34, 0x5b, 0xd0, 0x63, 0x24, 0xa1, 0x7a, 0xa9, 0x3a, + 0x59, 0x2b, 0x36, 0x96, 0x7b, 0x99, 0xb9, 0x74, 0x61, 0xd2, 0xe7, 0x7c, 0x2d, 0xa7, 0x2c, 0x35, + 0x5b, 0xb9, 0x42, 0x7b, 0xa5, 0x2e, 0x8c, 0xfb, 0x34, 0x3d, 0xd2, 0x61, 0x09, 0x09, 0x43, 0x94, + 0xe8, 0x33, 0x62, 0xec, 0xb7, 0x7b, 0x99, 0x59, 0x95, 0xe4, 0xbf, 0xb9, 0x5a, 0xce, 0xcd, 0x31, + 0xf0, 0xe6, 0xc0, 0xb2, 0xb1, 0xfa, 0xee, 0xd0, 0x2c, 0x7c, 0x3c, 0x34, 0x0b, 0x5f, 0xbf, 0xd4, + 0xa7, 0xf8, 0xd5, 0x3c, 0xe1, 0x3b, 0x5d, 0xb9, 0x64, 0xa7, 0x1b, 0x2f, 0x8f, 0x4e, 0x0d, 0xe5, + 0xf8, 0xd4, 0x50, 0x7e, 0x9e, 0x1a, 0xca, 0x87, 0x33, 0xa3, 0x70, 0x7c, 0x66, 0x14, 0xbe, 0x9f, + 0x19, 0x85, 0x17, 0x0f, 0x47, 0xe6, 0x26, 0x09, 0xf5, 0x10, 0xba, 0xb4, 0x2f, 0x80, 0xfd, 0xb5, + 0x35, 0x70, 0x30, 0x7c, 0x15, 0xeb, 0x17, 0x9e, 0x45, 0x77, 0x5a, 0x3c, 0x07, 0xf7, 0x7f, 0x07, + 0x00, 0x00, 0xff, 0xff, 0x8e, 0x35, 0x4f, 0x93, 0x43, 0x05, 0x00, 0x00, +} + +func (m *PoolParams) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *PoolParams) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *PoolParams) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size := m.ExitFee.Size() + i -= size + if _, err := m.ExitFee.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintStableswapPool(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + { + size := m.SwapFee.Size() + i -= size + if _, err := m.SwapFee.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintStableswapPool(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *Pool) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Pool) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Pool) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ScalingFactorController) > 0 { + i -= len(m.ScalingFactorController) + copy(dAtA[i:], m.ScalingFactorController) + i = encodeVarintStableswapPool(dAtA, i, uint64(len(m.ScalingFactorController))) + i-- + dAtA[i] = 0x42 + } + if len(m.ScalingFactors) > 0 { + dAtA2 := make([]byte, len(m.ScalingFactors)*10) + var j1 int + for _, num := range m.ScalingFactors { + for num >= 1<<7 { + dAtA2[j1] = uint8(uint64(num)&0x7f | 0x80) + num >>= 7 + j1++ + } + dAtA2[j1] = uint8(num) + j1++ + } + i -= j1 + copy(dAtA[i:], dAtA2[:j1]) + i = encodeVarintStableswapPool(dAtA, i, uint64(j1)) + i-- + dAtA[i] = 0x3a + } + if len(m.PoolLiquidity) > 0 { + for iNdEx := len(m.PoolLiquidity) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.PoolLiquidity[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintStableswapPool(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x32 + } + } + { + size, err := m.TotalShares.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintStableswapPool(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + if len(m.FuturePoolGovernor) > 0 { + i -= len(m.FuturePoolGovernor) + copy(dAtA[i:], m.FuturePoolGovernor) + i = encodeVarintStableswapPool(dAtA, i, uint64(len(m.FuturePoolGovernor))) + i-- + dAtA[i] = 0x22 + } + { + size, err := m.PoolParams.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintStableswapPool(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + if m.Id != 0 { + i = encodeVarintStableswapPool(dAtA, i, uint64(m.Id)) + i-- + dAtA[i] = 0x10 + } + if len(m.Address) > 0 { + i -= len(m.Address) + copy(dAtA[i:], m.Address) + i = encodeVarintStableswapPool(dAtA, i, uint64(len(m.Address))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintStableswapPool(dAtA []byte, offset int, v uint64) int { + offset -= sovStableswapPool(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *PoolParams) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.SwapFee.Size() + n += 1 + l + sovStableswapPool(uint64(l)) + l = m.ExitFee.Size() + n += 1 + l + sovStableswapPool(uint64(l)) + return n +} + +func (m *Pool) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Address) + if l > 0 { + n += 1 + l + sovStableswapPool(uint64(l)) + } + if m.Id != 0 { + n += 1 + sovStableswapPool(uint64(m.Id)) + } + l = m.PoolParams.Size() + n += 1 + l + sovStableswapPool(uint64(l)) + l = len(m.FuturePoolGovernor) + if l > 0 { + n += 1 + l + sovStableswapPool(uint64(l)) + } + l = m.TotalShares.Size() + n += 1 + l + sovStableswapPool(uint64(l)) + if len(m.PoolLiquidity) > 0 { + for _, e := range m.PoolLiquidity { + l = e.Size() + n += 1 + l + sovStableswapPool(uint64(l)) + } + } + if len(m.ScalingFactors) > 0 { + l = 0 + for _, e := range m.ScalingFactors { + l += sovStableswapPool(uint64(e)) + } + n += 1 + sovStableswapPool(uint64(l)) + l + } + l = len(m.ScalingFactorController) + if l > 0 { + n += 1 + l + sovStableswapPool(uint64(l)) + } + return n +} + +func sovStableswapPool(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozStableswapPool(x uint64) (n int) { + return sovStableswapPool(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *PoolParams) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStableswapPool + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: PoolParams: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PoolParams: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SwapFee", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStableswapPool + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthStableswapPool + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthStableswapPool + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.SwapFee.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ExitFee", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStableswapPool + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthStableswapPool + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthStableswapPool + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.ExitFee.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipStableswapPool(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthStableswapPool + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Pool) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStableswapPool + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Pool: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Pool: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Address", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStableswapPool + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthStableswapPool + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthStableswapPool + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Address = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) + } + m.Id = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStableswapPool + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Id |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PoolParams", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStableswapPool + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthStableswapPool + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthStableswapPool + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.PoolParams.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FuturePoolGovernor", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStableswapPool + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthStableswapPool + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthStableswapPool + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.FuturePoolGovernor = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TotalShares", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStableswapPool + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthStableswapPool + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthStableswapPool + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.TotalShares.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PoolLiquidity", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStableswapPool + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthStableswapPool + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthStableswapPool + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.PoolLiquidity = append(m.PoolLiquidity, types.Coin{}) + if err := m.PoolLiquidity[len(m.PoolLiquidity)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 7: + if wireType == 0 { + var v uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStableswapPool + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.ScalingFactors = append(m.ScalingFactors, v) + } else if wireType == 2 { + var packedLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStableswapPool + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + packedLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if packedLen < 0 { + return ErrInvalidLengthStableswapPool + } + postIndex := iNdEx + packedLen + if postIndex < 0 { + return ErrInvalidLengthStableswapPool + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var elementCount int + var count int + for _, integer := range dAtA[iNdEx:postIndex] { + if integer < 128 { + count++ + } + } + elementCount = count + if elementCount != 0 && len(m.ScalingFactors) == 0 { + m.ScalingFactors = make([]uint64, 0, elementCount) + } + for iNdEx < postIndex { + var v uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStableswapPool + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.ScalingFactors = append(m.ScalingFactors, v) + } + } else { + return fmt.Errorf("proto: wrong wireType = %d for field ScalingFactors", wireType) + } + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ScalingFactorController", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStableswapPool + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthStableswapPool + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthStableswapPool + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ScalingFactorController = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipStableswapPool(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthStableswapPool + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipStableswapPool(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowStableswapPool + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowStableswapPool + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowStableswapPool + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthStableswapPool + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupStableswapPool + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthStableswapPool + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthStableswapPool = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowStableswapPool = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupStableswapPool = fmt.Errorf("proto: unexpected end of group") +) diff --git a/tests/interchaintest/osmosis/gamm/pool-models/stableswap/tx.pb.go b/tests/interchaintest/osmosis/gamm/pool-models/stableswap/tx.pb.go new file mode 100644 index 00000000..8643dbca --- /dev/null +++ b/tests/interchaintest/osmosis/gamm/pool-models/stableswap/tx.pb.go @@ -0,0 +1,1402 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: osmosis/gamm/poolmodels/stableswap/v1beta1/tx.proto + +package stableswap + +import ( + context "context" + fmt "fmt" + github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" + types "github.com/cosmos/cosmos-sdk/types" + _ "github.com/cosmos/cosmos-sdk/types/tx/amino" + _ "github.com/cosmos/gogoproto/gogoproto" + grpc1 "github.com/cosmos/gogoproto/grpc" + proto "github.com/cosmos/gogoproto/proto" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// ===================== MsgCreatePool +type MsgCreateStableswapPool struct { + Sender string `protobuf:"bytes,1,opt,name=sender,proto3" json:"sender,omitempty" yaml:"sender"` + PoolParams *PoolParams `protobuf:"bytes,2,opt,name=pool_params,json=poolParams,proto3" json:"pool_params,omitempty" yaml:"pool_params"` + InitialPoolLiquidity github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,3,rep,name=initial_pool_liquidity,json=initialPoolLiquidity,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"initial_pool_liquidity"` + ScalingFactors []uint64 `protobuf:"varint,4,rep,packed,name=scaling_factors,json=scalingFactors,proto3" json:"scaling_factors,omitempty" yaml:"stableswap_scaling_factor"` + FuturePoolGovernor string `protobuf:"bytes,5,opt,name=future_pool_governor,json=futurePoolGovernor,proto3" json:"future_pool_governor,omitempty" yaml:"future_pool_governor"` + ScalingFactorController string `protobuf:"bytes,6,opt,name=scaling_factor_controller,json=scalingFactorController,proto3" json:"scaling_factor_controller,omitempty" yaml:"scaling_factor_controller"` +} + +func (m *MsgCreateStableswapPool) Reset() { *m = MsgCreateStableswapPool{} } +func (m *MsgCreateStableswapPool) String() string { return proto.CompactTextString(m) } +func (*MsgCreateStableswapPool) ProtoMessage() {} +func (*MsgCreateStableswapPool) Descriptor() ([]byte, []int) { + return fileDescriptor_3a59a47ae7445405, []int{0} +} +func (m *MsgCreateStableswapPool) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgCreateStableswapPool) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgCreateStableswapPool.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgCreateStableswapPool) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgCreateStableswapPool.Merge(m, src) +} +func (m *MsgCreateStableswapPool) XXX_Size() int { + return m.Size() +} +func (m *MsgCreateStableswapPool) XXX_DiscardUnknown() { + xxx_messageInfo_MsgCreateStableswapPool.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgCreateStableswapPool proto.InternalMessageInfo + +func (m *MsgCreateStableswapPool) GetSender() string { + if m != nil { + return m.Sender + } + return "" +} + +func (m *MsgCreateStableswapPool) GetPoolParams() *PoolParams { + if m != nil { + return m.PoolParams + } + return nil +} + +func (m *MsgCreateStableswapPool) GetInitialPoolLiquidity() github_com_cosmos_cosmos_sdk_types.Coins { + if m != nil { + return m.InitialPoolLiquidity + } + return nil +} + +func (m *MsgCreateStableswapPool) GetScalingFactors() []uint64 { + if m != nil { + return m.ScalingFactors + } + return nil +} + +func (m *MsgCreateStableswapPool) GetFuturePoolGovernor() string { + if m != nil { + return m.FuturePoolGovernor + } + return "" +} + +func (m *MsgCreateStableswapPool) GetScalingFactorController() string { + if m != nil { + return m.ScalingFactorController + } + return "" +} + +// Returns a poolID with custom poolName. +type MsgCreateStableswapPoolResponse struct { + PoolID uint64 `protobuf:"varint,1,opt,name=pool_id,json=poolId,proto3" json:"pool_id,omitempty"` +} + +func (m *MsgCreateStableswapPoolResponse) Reset() { *m = MsgCreateStableswapPoolResponse{} } +func (m *MsgCreateStableswapPoolResponse) String() string { return proto.CompactTextString(m) } +func (*MsgCreateStableswapPoolResponse) ProtoMessage() {} +func (*MsgCreateStableswapPoolResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_3a59a47ae7445405, []int{1} +} +func (m *MsgCreateStableswapPoolResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgCreateStableswapPoolResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgCreateStableswapPoolResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgCreateStableswapPoolResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgCreateStableswapPoolResponse.Merge(m, src) +} +func (m *MsgCreateStableswapPoolResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgCreateStableswapPoolResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgCreateStableswapPoolResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgCreateStableswapPoolResponse proto.InternalMessageInfo + +func (m *MsgCreateStableswapPoolResponse) GetPoolID() uint64 { + if m != nil { + return m.PoolID + } + return 0 +} + +// Sender must be the pool's scaling_factor_governor in order for the tx to +// succeed. Adjusts stableswap scaling factors. +type MsgStableSwapAdjustScalingFactors struct { + Sender string `protobuf:"bytes,1,opt,name=sender,proto3" json:"sender,omitempty" yaml:"sender"` + PoolID uint64 `protobuf:"varint,2,opt,name=pool_id,json=poolId,proto3" json:"pool_id,omitempty"` + ScalingFactors []uint64 `protobuf:"varint,3,rep,packed,name=scaling_factors,json=scalingFactors,proto3" json:"scaling_factors,omitempty" yaml:"stableswap_scaling_factor"` +} + +func (m *MsgStableSwapAdjustScalingFactors) Reset() { *m = MsgStableSwapAdjustScalingFactors{} } +func (m *MsgStableSwapAdjustScalingFactors) String() string { return proto.CompactTextString(m) } +func (*MsgStableSwapAdjustScalingFactors) ProtoMessage() {} +func (*MsgStableSwapAdjustScalingFactors) Descriptor() ([]byte, []int) { + return fileDescriptor_3a59a47ae7445405, []int{2} +} +func (m *MsgStableSwapAdjustScalingFactors) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgStableSwapAdjustScalingFactors) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgStableSwapAdjustScalingFactors.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgStableSwapAdjustScalingFactors) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgStableSwapAdjustScalingFactors.Merge(m, src) +} +func (m *MsgStableSwapAdjustScalingFactors) XXX_Size() int { + return m.Size() +} +func (m *MsgStableSwapAdjustScalingFactors) XXX_DiscardUnknown() { + xxx_messageInfo_MsgStableSwapAdjustScalingFactors.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgStableSwapAdjustScalingFactors proto.InternalMessageInfo + +func (m *MsgStableSwapAdjustScalingFactors) GetSender() string { + if m != nil { + return m.Sender + } + return "" +} + +func (m *MsgStableSwapAdjustScalingFactors) GetPoolID() uint64 { + if m != nil { + return m.PoolID + } + return 0 +} + +func (m *MsgStableSwapAdjustScalingFactors) GetScalingFactors() []uint64 { + if m != nil { + return m.ScalingFactors + } + return nil +} + +type MsgStableSwapAdjustScalingFactorsResponse struct { +} + +func (m *MsgStableSwapAdjustScalingFactorsResponse) Reset() { + *m = MsgStableSwapAdjustScalingFactorsResponse{} +} +func (m *MsgStableSwapAdjustScalingFactorsResponse) String() string { + return proto.CompactTextString(m) +} +func (*MsgStableSwapAdjustScalingFactorsResponse) ProtoMessage() {} +func (*MsgStableSwapAdjustScalingFactorsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_3a59a47ae7445405, []int{3} +} +func (m *MsgStableSwapAdjustScalingFactorsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgStableSwapAdjustScalingFactorsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgStableSwapAdjustScalingFactorsResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgStableSwapAdjustScalingFactorsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgStableSwapAdjustScalingFactorsResponse.Merge(m, src) +} +func (m *MsgStableSwapAdjustScalingFactorsResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgStableSwapAdjustScalingFactorsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgStableSwapAdjustScalingFactorsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgStableSwapAdjustScalingFactorsResponse proto.InternalMessageInfo + +func init() { + proto.RegisterType((*MsgCreateStableswapPool)(nil), "osmosis.gamm.poolmodels.stableswap.v1beta1.MsgCreateStableswapPool") + proto.RegisterType((*MsgCreateStableswapPoolResponse)(nil), "osmosis.gamm.poolmodels.stableswap.v1beta1.MsgCreateStableswapPoolResponse") + proto.RegisterType((*MsgStableSwapAdjustScalingFactors)(nil), "osmosis.gamm.poolmodels.stableswap.v1beta1.MsgStableSwapAdjustScalingFactors") + proto.RegisterType((*MsgStableSwapAdjustScalingFactorsResponse)(nil), "osmosis.gamm.poolmodels.stableswap.v1beta1.MsgStableSwapAdjustScalingFactorsResponse") +} + +func init() { + proto.RegisterFile("osmosis/gamm/poolmodels/stableswap/v1beta1/tx.proto", fileDescriptor_3a59a47ae7445405) +} + +var fileDescriptor_3a59a47ae7445405 = []byte{ + // 673 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x54, 0xbf, 0x6f, 0xd3, 0x4e, + 0x14, 0x8f, 0x9b, 0x7c, 0xf3, 0x15, 0x57, 0x01, 0xaa, 0x15, 0xb5, 0x69, 0x90, 0xec, 0xe0, 0x32, + 0xa4, 0x45, 0xb6, 0x69, 0x2a, 0x31, 0x74, 0xa2, 0x09, 0x2a, 0xaa, 0x20, 0x52, 0x71, 0xc5, 0x02, + 0x43, 0xb8, 0xd8, 0x57, 0x73, 0x60, 0xfb, 0x8c, 0xef, 0xd2, 0x1f, 0x23, 0x2b, 0x13, 0x7f, 0x06, + 0x62, 0xe2, 0xaf, 0x40, 0x1d, 0x3b, 0x32, 0x05, 0x94, 0x0e, 0xdd, 0xb3, 0x20, 0x36, 0x74, 0xe7, + 0xcb, 0x0f, 0x8b, 0x86, 0x12, 0xd4, 0x25, 0xb1, 0x9f, 0xdf, 0xe7, 0xf3, 0x79, 0xef, 0xf3, 0xde, + 0x1d, 0xd8, 0x20, 0x34, 0x24, 0x14, 0x53, 0xdb, 0x87, 0x61, 0x68, 0xc7, 0x84, 0x04, 0x21, 0xf1, + 0x50, 0x40, 0x6d, 0xca, 0x60, 0x27, 0x40, 0xf4, 0x10, 0xc6, 0xf6, 0xc1, 0x7a, 0x07, 0x31, 0xb8, + 0x6e, 0xb3, 0x23, 0x2b, 0x4e, 0x08, 0x23, 0xea, 0x9a, 0x04, 0x59, 0x1c, 0x64, 0x8d, 0x41, 0xd6, + 0x18, 0x64, 0x49, 0x50, 0x45, 0x73, 0x45, 0xb2, 0xdd, 0x81, 0x14, 0x8d, 0x98, 0x5c, 0x82, 0xa3, + 0x94, 0xab, 0x52, 0xf2, 0x89, 0x4f, 0xc4, 0xa3, 0xcd, 0x9f, 0x64, 0x74, 0x01, 0x86, 0x38, 0x22, + 0xb6, 0xf8, 0x95, 0xa1, 0x07, 0x33, 0x54, 0x3a, 0x0e, 0xb5, 0x79, 0x62, 0xca, 0x60, 0x9c, 0x17, + 0xc0, 0x52, 0x8b, 0xfa, 0xcd, 0x04, 0x41, 0x86, 0xf6, 0x46, 0x29, 0xbb, 0x84, 0x04, 0xea, 0x2a, + 0x28, 0x52, 0x14, 0x79, 0x28, 0x29, 0x2b, 0x55, 0xa5, 0x76, 0xad, 0xb1, 0x30, 0xe8, 0xe9, 0xd7, + 0x8f, 0x61, 0x18, 0x6c, 0x1a, 0x69, 0xdc, 0x70, 0x64, 0x82, 0x4a, 0xc0, 0x3c, 0x27, 0x6d, 0xc7, + 0x30, 0x81, 0x21, 0x2d, 0xcf, 0x55, 0x95, 0xda, 0x7c, 0xfd, 0xbe, 0xf5, 0xf7, 0x9e, 0x58, 0x5c, + 0x71, 0x57, 0xa0, 0x1b, 0x8b, 0x83, 0x9e, 0xae, 0xa6, 0x3a, 0x13, 0xa4, 0x86, 0x03, 0xe2, 0x51, + 0x8e, 0xfa, 0x4e, 0x01, 0x8b, 0x38, 0xc2, 0x0c, 0xc3, 0x40, 0xb4, 0xd3, 0x0e, 0xf0, 0xdb, 0x2e, + 0xf6, 0x30, 0x3b, 0x2e, 0xe7, 0xab, 0xf9, 0xda, 0x7c, 0x7d, 0xd9, 0x4a, 0x4d, 0xb6, 0xb8, 0xc9, + 0x23, 0x95, 0x26, 0xc1, 0x51, 0xe3, 0xde, 0x49, 0x4f, 0xcf, 0x7d, 0xfa, 0xa6, 0xd7, 0x7c, 0xcc, + 0x5e, 0x75, 0x3b, 0x96, 0x4b, 0x42, 0x5b, 0x4e, 0x24, 0xfd, 0x33, 0xa9, 0xf7, 0xc6, 0x66, 0xc7, + 0x31, 0xa2, 0x02, 0x40, 0x9d, 0x92, 0x94, 0xe2, 0x45, 0x3e, 0x19, 0x0a, 0xa9, 0x2d, 0x70, 0x93, + 0xba, 0x30, 0xc0, 0x91, 0xdf, 0xde, 0x87, 0x2e, 0x23, 0x09, 0x2d, 0x17, 0xaa, 0xf9, 0x5a, 0xa1, + 0x71, 0x67, 0xd0, 0xd3, 0xab, 0xd2, 0xa8, 0xb1, 0xeb, 0xd9, 0x5c, 0xc3, 0xb9, 0x21, 0x03, 0xdb, + 0x29, 0x56, 0x7d, 0x0a, 0x4a, 0xfb, 0x5d, 0xd6, 0x4d, 0x50, 0xda, 0x90, 0x4f, 0x0e, 0x50, 0x12, + 0x91, 0xa4, 0xfc, 0x9f, 0x30, 0x5f, 0x1f, 0xf4, 0xf4, 0x5b, 0x29, 0xe7, 0x45, 0x59, 0x86, 0xa3, + 0xa6, 0x61, 0x5e, 0xe2, 0x23, 0x19, 0x54, 0x5f, 0x82, 0xe5, 0xac, 0x6a, 0xdb, 0x25, 0x11, 0x4b, + 0x48, 0x10, 0xa0, 0xa4, 0x5c, 0x14, 0xbc, 0x93, 0xb5, 0x4e, 0x4b, 0x35, 0x9c, 0xa5, 0x4c, 0xad, + 0xcd, 0xd1, 0x97, 0xcd, 0xda, 0xfb, 0xf3, 0xcf, 0x6b, 0x2b, 0x99, 0x35, 0x74, 0xc5, 0x2e, 0x99, + 0xe3, 0xce, 0x4d, 0x5e, 0xa9, 0xb1, 0x0d, 0xf4, 0x29, 0x8b, 0xe6, 0x20, 0x1a, 0x93, 0x88, 0x22, + 0x75, 0x05, 0xfc, 0x2f, 0x9a, 0xc2, 0x9e, 0xd8, 0xb8, 0x42, 0x03, 0xf4, 0x7b, 0x7a, 0x91, 0xa7, + 0xec, 0x3c, 0x74, 0x8a, 0xfc, 0xd3, 0x8e, 0x67, 0xfc, 0x50, 0xc0, 0xed, 0x16, 0xf5, 0x53, 0x8a, + 0xbd, 0x43, 0x18, 0x6f, 0x79, 0xaf, 0xbb, 0x94, 0xed, 0x65, 0xcd, 0x9c, 0x61, 0x77, 0x27, 0x54, + 0xe7, 0xa6, 0xa9, 0x5e, 0x34, 0xeb, 0xfc, 0xbf, 0xcf, 0x7a, 0x73, 0x83, 0xdb, 0x66, 0x65, 0x6c, + 0x9b, 0xf0, 0x0b, 0x8a, 0x8e, 0x4c, 0x89, 0x31, 0xa5, 0xa0, 0x71, 0x17, 0xac, 0x5e, 0xda, 0xf8, + 0xd0, 0xcb, 0xfa, 0xcf, 0x39, 0x90, 0x6f, 0x51, 0x5f, 0xfd, 0xa8, 0x80, 0xd2, 0x85, 0xa7, 0xbb, + 0x39, 0xcb, 0xe9, 0x9c, 0x32, 0xb9, 0xca, 0xe3, 0x2b, 0x20, 0x19, 0x8d, 0xff, 0x8b, 0x02, 0xb4, + 0x4b, 0xc6, 0xda, 0x9a, 0x51, 0xef, 0xcf, 0x74, 0x95, 0x67, 0x57, 0x4a, 0x37, 0x6c, 0xa4, 0xf1, + 0xe2, 0xa4, 0xaf, 0x29, 0xa7, 0x7d, 0x4d, 0xf9, 0xde, 0xd7, 0x94, 0x0f, 0x67, 0x5a, 0xee, 0xf4, + 0x4c, 0xcb, 0x7d, 0x3d, 0xd3, 0x72, 0xcf, 0xb7, 0x26, 0xae, 0x1c, 0x29, 0x6d, 0x06, 0xb0, 0x43, + 0x87, 0x2f, 0xf6, 0x41, 0xbd, 0x6e, 0x1f, 0x8d, 0xaf, 0x73, 0xf3, 0xb7, 0xfb, 0xbc, 0x53, 0x14, + 0x17, 0xf7, 0xc6, 0xaf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x36, 0x27, 0xd1, 0x33, 0xa6, 0x06, 0x00, + 0x00, +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// MsgClient is the client API for Msg service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type MsgClient interface { + CreateStableswapPool(ctx context.Context, in *MsgCreateStableswapPool, opts ...grpc.CallOption) (*MsgCreateStableswapPoolResponse, error) + StableSwapAdjustScalingFactors(ctx context.Context, in *MsgStableSwapAdjustScalingFactors, opts ...grpc.CallOption) (*MsgStableSwapAdjustScalingFactorsResponse, error) +} + +type msgClient struct { + cc grpc1.ClientConn +} + +func NewMsgClient(cc grpc1.ClientConn) MsgClient { + return &msgClient{cc} +} + +func (c *msgClient) CreateStableswapPool(ctx context.Context, in *MsgCreateStableswapPool, opts ...grpc.CallOption) (*MsgCreateStableswapPoolResponse, error) { + out := new(MsgCreateStableswapPoolResponse) + err := c.cc.Invoke(ctx, "/osmosis.gamm.poolmodels.stableswap.v1beta1.Msg/CreateStableswapPool", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) StableSwapAdjustScalingFactors(ctx context.Context, in *MsgStableSwapAdjustScalingFactors, opts ...grpc.CallOption) (*MsgStableSwapAdjustScalingFactorsResponse, error) { + out := new(MsgStableSwapAdjustScalingFactorsResponse) + err := c.cc.Invoke(ctx, "/osmosis.gamm.poolmodels.stableswap.v1beta1.Msg/StableSwapAdjustScalingFactors", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// MsgServer is the server API for Msg service. +type MsgServer interface { + CreateStableswapPool(context.Context, *MsgCreateStableswapPool) (*MsgCreateStableswapPoolResponse, error) + StableSwapAdjustScalingFactors(context.Context, *MsgStableSwapAdjustScalingFactors) (*MsgStableSwapAdjustScalingFactorsResponse, error) +} + +// UnimplementedMsgServer can be embedded to have forward compatible implementations. +type UnimplementedMsgServer struct { +} + +func (*UnimplementedMsgServer) CreateStableswapPool(ctx context.Context, req *MsgCreateStableswapPool) (*MsgCreateStableswapPoolResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method CreateStableswapPool not implemented") +} +func (*UnimplementedMsgServer) StableSwapAdjustScalingFactors(ctx context.Context, req *MsgStableSwapAdjustScalingFactors) (*MsgStableSwapAdjustScalingFactorsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method StableSwapAdjustScalingFactors not implemented") +} + +func RegisterMsgServer(s grpc1.Server, srv MsgServer) { + s.RegisterService(&_Msg_serviceDesc, srv) +} + +func _Msg_CreateStableswapPool_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgCreateStableswapPool) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).CreateStableswapPool(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/osmosis.gamm.poolmodels.stableswap.v1beta1.Msg/CreateStableswapPool", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).CreateStableswapPool(ctx, req.(*MsgCreateStableswapPool)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_StableSwapAdjustScalingFactors_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgStableSwapAdjustScalingFactors) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).StableSwapAdjustScalingFactors(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/osmosis.gamm.poolmodels.stableswap.v1beta1.Msg/StableSwapAdjustScalingFactors", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).StableSwapAdjustScalingFactors(ctx, req.(*MsgStableSwapAdjustScalingFactors)) + } + return interceptor(ctx, in, info, handler) +} + +var _Msg_serviceDesc = grpc.ServiceDesc{ + ServiceName: "osmosis.gamm.poolmodels.stableswap.v1beta1.Msg", + HandlerType: (*MsgServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "CreateStableswapPool", + Handler: _Msg_CreateStableswapPool_Handler, + }, + { + MethodName: "StableSwapAdjustScalingFactors", + Handler: _Msg_StableSwapAdjustScalingFactors_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "osmosis/gamm/poolmodels/stableswap/v1beta1/tx.proto", +} + +func (m *MsgCreateStableswapPool) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgCreateStableswapPool) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgCreateStableswapPool) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ScalingFactorController) > 0 { + i -= len(m.ScalingFactorController) + copy(dAtA[i:], m.ScalingFactorController) + i = encodeVarintTx(dAtA, i, uint64(len(m.ScalingFactorController))) + i-- + dAtA[i] = 0x32 + } + if len(m.FuturePoolGovernor) > 0 { + i -= len(m.FuturePoolGovernor) + copy(dAtA[i:], m.FuturePoolGovernor) + i = encodeVarintTx(dAtA, i, uint64(len(m.FuturePoolGovernor))) + i-- + dAtA[i] = 0x2a + } + if len(m.ScalingFactors) > 0 { + dAtA2 := make([]byte, len(m.ScalingFactors)*10) + var j1 int + for _, num := range m.ScalingFactors { + for num >= 1<<7 { + dAtA2[j1] = uint8(uint64(num)&0x7f | 0x80) + num >>= 7 + j1++ + } + dAtA2[j1] = uint8(num) + j1++ + } + i -= j1 + copy(dAtA[i:], dAtA2[:j1]) + i = encodeVarintTx(dAtA, i, uint64(j1)) + i-- + dAtA[i] = 0x22 + } + if len(m.InitialPoolLiquidity) > 0 { + for iNdEx := len(m.InitialPoolLiquidity) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.InitialPoolLiquidity[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + } + if m.PoolParams != nil { + { + size, err := m.PoolParams.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if len(m.Sender) > 0 { + i -= len(m.Sender) + copy(dAtA[i:], m.Sender) + i = encodeVarintTx(dAtA, i, uint64(len(m.Sender))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgCreateStableswapPoolResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgCreateStableswapPoolResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgCreateStableswapPoolResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.PoolID != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.PoolID)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *MsgStableSwapAdjustScalingFactors) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgStableSwapAdjustScalingFactors) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgStableSwapAdjustScalingFactors) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ScalingFactors) > 0 { + dAtA5 := make([]byte, len(m.ScalingFactors)*10) + var j4 int + for _, num := range m.ScalingFactors { + for num >= 1<<7 { + dAtA5[j4] = uint8(uint64(num)&0x7f | 0x80) + num >>= 7 + j4++ + } + dAtA5[j4] = uint8(num) + j4++ + } + i -= j4 + copy(dAtA[i:], dAtA5[:j4]) + i = encodeVarintTx(dAtA, i, uint64(j4)) + i-- + dAtA[i] = 0x1a + } + if m.PoolID != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.PoolID)) + i-- + dAtA[i] = 0x10 + } + if len(m.Sender) > 0 { + i -= len(m.Sender) + copy(dAtA[i:], m.Sender) + i = encodeVarintTx(dAtA, i, uint64(len(m.Sender))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgStableSwapAdjustScalingFactorsResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgStableSwapAdjustScalingFactorsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgStableSwapAdjustScalingFactorsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func encodeVarintTx(dAtA []byte, offset int, v uint64) int { + offset -= sovTx(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *MsgCreateStableswapPool) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Sender) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if m.PoolParams != nil { + l = m.PoolParams.Size() + n += 1 + l + sovTx(uint64(l)) + } + if len(m.InitialPoolLiquidity) > 0 { + for _, e := range m.InitialPoolLiquidity { + l = e.Size() + n += 1 + l + sovTx(uint64(l)) + } + } + if len(m.ScalingFactors) > 0 { + l = 0 + for _, e := range m.ScalingFactors { + l += sovTx(uint64(e)) + } + n += 1 + sovTx(uint64(l)) + l + } + l = len(m.FuturePoolGovernor) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.ScalingFactorController) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgCreateStableswapPoolResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.PoolID != 0 { + n += 1 + sovTx(uint64(m.PoolID)) + } + return n +} + +func (m *MsgStableSwapAdjustScalingFactors) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Sender) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if m.PoolID != 0 { + n += 1 + sovTx(uint64(m.PoolID)) + } + if len(m.ScalingFactors) > 0 { + l = 0 + for _, e := range m.ScalingFactors { + l += sovTx(uint64(e)) + } + n += 1 + sovTx(uint64(l)) + l + } + return n +} + +func (m *MsgStableSwapAdjustScalingFactorsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func sovTx(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozTx(x uint64) (n int) { + return sovTx(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *MsgCreateStableswapPool) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgCreateStableswapPool: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgCreateStableswapPool: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Sender", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Sender = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PoolParams", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.PoolParams == nil { + m.PoolParams = &PoolParams{} + } + if err := m.PoolParams.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field InitialPoolLiquidity", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.InitialPoolLiquidity = append(m.InitialPoolLiquidity, types.Coin{}) + if err := m.InitialPoolLiquidity[len(m.InitialPoolLiquidity)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType == 0 { + var v uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.ScalingFactors = append(m.ScalingFactors, v) + } else if wireType == 2 { + var packedLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + packedLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if packedLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + packedLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var elementCount int + var count int + for _, integer := range dAtA[iNdEx:postIndex] { + if integer < 128 { + count++ + } + } + elementCount = count + if elementCount != 0 && len(m.ScalingFactors) == 0 { + m.ScalingFactors = make([]uint64, 0, elementCount) + } + for iNdEx < postIndex { + var v uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.ScalingFactors = append(m.ScalingFactors, v) + } + } else { + return fmt.Errorf("proto: wrong wireType = %d for field ScalingFactors", wireType) + } + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FuturePoolGovernor", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.FuturePoolGovernor = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ScalingFactorController", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ScalingFactorController = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgCreateStableswapPoolResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgCreateStableswapPoolResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgCreateStableswapPoolResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field PoolID", wireType) + } + m.PoolID = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.PoolID |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgStableSwapAdjustScalingFactors) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgStableSwapAdjustScalingFactors: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgStableSwapAdjustScalingFactors: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Sender", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Sender = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field PoolID", wireType) + } + m.PoolID = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.PoolID |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType == 0 { + var v uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.ScalingFactors = append(m.ScalingFactors, v) + } else if wireType == 2 { + var packedLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + packedLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if packedLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + packedLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var elementCount int + var count int + for _, integer := range dAtA[iNdEx:postIndex] { + if integer < 128 { + count++ + } + } + elementCount = count + if elementCount != 0 && len(m.ScalingFactors) == 0 { + m.ScalingFactors = make([]uint64, 0, elementCount) + } + for iNdEx < postIndex { + var v uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.ScalingFactors = append(m.ScalingFactors, v) + } + } else { + return fmt.Errorf("proto: wrong wireType = %d for field ScalingFactors", wireType) + } + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgStableSwapAdjustScalingFactorsResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgStableSwapAdjustScalingFactorsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgStableSwapAdjustScalingFactorsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipTx(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTx + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTx + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTx + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthTx + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupTx + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthTx + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthTx = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowTx = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupTx = fmt.Errorf("proto: unexpected end of group") +) diff --git a/tests/interchaintest/osmosis/gamm/types/codec.go b/tests/interchaintest/osmosis/gamm/types/codec.go new file mode 100644 index 00000000..85d6aa6c --- /dev/null +++ b/tests/interchaintest/osmosis/gamm/types/codec.go @@ -0,0 +1,78 @@ +package types + +import ( + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/msgservice" + authzcodec "github.com/cosmos/cosmos-sdk/x/authz/codec" +) + +// RegisterLegacyAminoCodec registers the necessary x/gamm interfaces and concrete types +// on the provided LegacyAmino codec. These types are used for Amino JSON serialization. +func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { + cdc.RegisterInterface((*CFMMPoolI)(nil), nil) + cdc.RegisterConcrete(&MsgJoinPool{}, "osmosis/gamm/join-pool", nil) + cdc.RegisterConcrete(&MsgExitPool{}, "osmosis/gamm/exit-pool", nil) + cdc.RegisterConcrete(&MsgSwapExactAmountIn{}, "osmosis/gamm/swap-exact-amount-in", nil) + cdc.RegisterConcrete(&MsgSwapExactAmountOut{}, "osmosis/gamm/swap-exact-amount-out", nil) + cdc.RegisterConcrete(&MsgJoinSwapExternAmountIn{}, "osmosis/gamm/join-swap-extern-amount-in", nil) + cdc.RegisterConcrete(&MsgJoinSwapShareAmountOut{}, "osmosis/gamm/join-swap-share-amount-out", nil) + cdc.RegisterConcrete(&MsgExitSwapExternAmountOut{}, "osmosis/gamm/exit-swap-extern-amount-out", nil) + cdc.RegisterConcrete(&MsgExitSwapShareAmountIn{}, "osmosis/gamm/exit-swap-share-amount-in", nil) + // cdc.RegisterConcrete(&UpdateMigrationRecordsProposal{}, "osmosis/gamm/update-migration-records-proposal", nil) + // cdc.RegisterConcrete(&ReplaceMigrationRecordsProposal{}, "osmosis/gamm/replace-migration-records-proposal", nil) + // cdc.RegisterConcrete(&CreateConcentratedLiquidityPoolsAndLinktoCFMMProposal{}, "osmosis/gamm/create-cl-pool-and-cfmm-link", nil) + // cdc.RegisterConcrete(&SetScalingFactorControllerProposal{}, "osmosis/gamm/scaling-factor-controller", nil) +} + +func RegisterInterfaces(registry types.InterfaceRegistry) { + registry.RegisterInterface( + "osmosis.gamm.v1beta1.PoolI", // N.B.: the old proto-path is preserved for backwards-compatibility. + (*CFMMPoolI)(nil), + ) + + registry.RegisterImplementations( + (*sdk.Msg)(nil), + &MsgJoinPool{}, + &MsgExitPool{}, + &MsgSwapExactAmountIn{}, + &MsgSwapExactAmountOut{}, + &MsgJoinSwapExternAmountIn{}, + &MsgJoinSwapShareAmountOut{}, + &MsgExitSwapExternAmountOut{}, + &MsgExitSwapShareAmountIn{}, + ) + + // registry.RegisterImplementations( + // (*govtypesv1.Content)(nil), + // &UpdateMigrationRecordsProposal{}, + // &ReplaceMigrationRecordsProposal{}, + // &CreateConcentratedLiquidityPoolsAndLinktoCFMMProposal{}, + // &SetScalingFactorControllerProposal{}, + // ) + + msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc) +} + +var ( + amino = codec.NewLegacyAmino() + + // ModuleCdc references the global x/bank module codec. Note, the codec should + // ONLY be used in certain instances of tests and for JSON encoding as Amino is + // still used for that purpose. + // + // The actual codec used for serialization should be provided to x/staking and + // defined at the application level. + ModuleCdc = codec.NewAminoCodec(amino) +) + +func init() { + RegisterLegacyAminoCodec(amino) + // Register all Amino interfaces and concrete types on the authz Amino codec so that this can later be + // used to properly serialize MsgGrant and MsgExec instances + sdk.RegisterLegacyAminoCodec(amino) + RegisterLegacyAminoCodec(authzcodec.Amino) + + amino.Seal() +} diff --git a/tests/interchaintest/osmosis/gamm/types/constants.go b/tests/interchaintest/osmosis/gamm/types/constants.go new file mode 100644 index 00000000..0294b17e --- /dev/null +++ b/tests/interchaintest/osmosis/gamm/types/constants.go @@ -0,0 +1,47 @@ +package types + +import ( + "github.com/osmosis-labs/fee-abstraction/v7/tests/interchaintest/osmosis/osmomath" +) + +const ( + OneShareExponent = 18 + // Raise 10 to the power of SigFigsExponent to determine number of significant figures. + // i.e. SigFigExponent = 8 is 10^8 which is 100000000. This gives 8 significant figures. + SigFigsExponent = 8 + BalancerGasFeeForSwap = 10_000 + + StableswapMinScaledAmtPerAsset = 1 + // We keep this multiplier at 1, but can increase if needed in the unlikely scenario where default scaling factors of 1 cannot accommodate enough assets + ScalingFactorMultiplier = 1 + + // pools can be created with min and max number of assets defined with this constants + MinNumOfAssetsInPool = 2 + MaxNumOfAssetsInPool = 8 +) + +var ( + // OneShare represents the amount of subshares in a single pool share. + OneShare = osmomath.NewIntWithDecimal(1, OneShareExponent) + + // InitPoolSharesSupply is the amount of new shares to initialize a pool with. + InitPoolSharesSupply = OneShare.MulRaw(100) + + // SpotPriceSigFigs is the amount of significant figures used in return value of calculate SpotPrice + SpotPriceSigFigs = osmomath.NewDec(10).Power(SigFigsExponent).TruncateInt() + // MaxSpotPrice is the maximum supported spot price. Anything greater than this will error. + // Internal note: Ctrl+F for MaxSpotPrice in code if ever changed. + // Other tests depend on being equal to MaxSpotPrice, + // but don't directly import it due to import issues. + MaxSpotPrice = osmomath.NewDec(2).Power(128).Sub(osmomath.OneDec()) + MaxSpotPriceBigDec = osmomath.BigDecFromDec(MaxSpotPrice) + // MinSpotPrice is the minimum supported spot price. Anything less than this will error. + // It is limited by osmomath.Dec's precision. + MinSpotPrice = osmomath.SmallestDec() + + // MultihopSpreadFactorMultiplierForOsmoPools if a spread factor multiplier for trades consists of just two OSMO pools during a single transaction. + MultihopSpreadFactorMultiplierForOsmoPools = osmomath.NewDecWithPrec(5, 1) // 0.5 + + // Maximum amount per asset after the application of scaling factors should be 10e34. + StableswapMaxScaledAmtPerAsset = osmomath.NewDec(10).Power(34).TruncateInt() +) diff --git a/tests/interchaintest/osmosis/gamm/types/errors.go b/tests/interchaintest/osmosis/gamm/types/errors.go new file mode 100644 index 00000000..2c9781ff --- /dev/null +++ b/tests/interchaintest/osmosis/gamm/types/errors.go @@ -0,0 +1,111 @@ +package types + +import ( + fmt "fmt" + + errorsmod "cosmossdk.io/errors" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +type PoolDoesNotExistError struct { + PoolId uint64 +} + +func (e PoolDoesNotExistError) Error() string { + return fmt.Sprintf("pool with ID %d does not exist", e.PoolId) +} + +type UnsortedPoolLiqError struct { + ActualLiquidity sdk.Coins +} + +func (e UnsortedPoolLiqError) Error() string { + return fmt.Sprintf(`unsorted initial pool liquidity: %s. + Please sort and make sure scaling factor order matches initial liquidity coin order`, e.ActualLiquidity) +} + +type LiquidityAndScalingFactorCountMismatchError struct { + LiquidityCount int + ScalingFactorCount int +} + +func (e LiquidityAndScalingFactorCountMismatchError) Error() string { + return fmt.Sprintf("liquidity count (%d) must match scaling factor count (%d)", e.LiquidityCount, e.ScalingFactorCount) +} + +type ConcentratedPoolMigrationLinkNotFoundError struct { + PoolIdLeaving uint64 +} + +func (e ConcentratedPoolMigrationLinkNotFoundError) Error() string { + return fmt.Sprintf("given poolIdLeaving (%d) does not have a canonical link for any concentrated pool", e.PoolIdLeaving) +} + +type BalancerPoolMigrationLinkNotFoundError struct { + PoolIdEntering uint64 +} + +func (e BalancerPoolMigrationLinkNotFoundError) Error() string { + return fmt.Sprintf("given PoolIdEntering (%d) does not have a canonical link for any balancer pool", e.PoolIdEntering) +} + +type NoDesiredDenomInPoolError struct { + DesiredDenom string +} + +func (e NoDesiredDenomInPoolError) Error() string { + return fmt.Sprintf("desired denom (%s) was not found in the pool", e.DesiredDenom) +} + +type MustHaveTwoDenomsError struct { + NumDenoms int +} + +func (e MustHaveTwoDenomsError) Error() string { + return fmt.Sprintf("can only have 2 denoms in CL pool, got (%d)", e.NumDenoms) +} + +// x/gamm module sentinel errors. +var ( + ErrPoolNotFound = errorsmod.Register(ModuleName, 1, "pool not found") + ErrPoolAlreadyExist = errorsmod.Register(ModuleName, 2, "pool already exist") + ErrPoolLocked = errorsmod.Register(ModuleName, 3, "pool is locked") + ErrTooFewPoolAssets = errorsmod.Register(ModuleName, 4, "pool should have at least 2 assets, as they must be swapping between at least two assets") + ErrTooManyPoolAssets = errorsmod.Register(ModuleName, 5, "pool has too many assets (currently capped at 8 assets for both balancer and stableswap)") + ErrLimitMaxAmount = errorsmod.Register(ModuleName, 6, "calculated amount is larger than max amount") + ErrLimitMinAmount = errorsmod.Register(ModuleName, 7, "calculated amount is lesser than min amount") + ErrInvalidMathApprox = errorsmod.Register(ModuleName, 8, "invalid calculated result") + ErrAlreadyInvalidPool = errorsmod.Register(ModuleName, 9, "destruction on already invalid pool") + ErrInvalidPool = errorsmod.Register(ModuleName, 10, "attempting to create an invalid pool") + ErrDenomNotFoundInPool = errorsmod.Register(ModuleName, 11, "denom does not exist in pool") + ErrDenomAlreadyInPool = errorsmod.Register(ModuleName, 12, "denom already exists in the pool") + + ErrEmptyRoutes = errorsmod.Register(ModuleName, 21, "routes not defined") + ErrEmptyPoolAssets = errorsmod.Register(ModuleName, 22, "PoolAssets not defined") + ErrNegativeSpreadFactor = errorsmod.Register(ModuleName, 23, "spread factor is negative") + ErrNegativeExitFee = errorsmod.Register(ModuleName, 24, "exit fee is negative") + ErrTooMuchSpreadFactor = errorsmod.Register(ModuleName, 25, "spread factor should be lesser than 1 (100%)") + ErrTooMuchExitFee = errorsmod.Register(ModuleName, 26, "exit fee should be lesser than 1 (100%)") + ErrNotPositiveWeight = errorsmod.Register(ModuleName, 27, "token weight should be greater than 0") + ErrWeightTooLarge = errorsmod.Register(ModuleName, 28, "user specified token weight should be less than 2^20") + ErrNotPositiveCriteria = errorsmod.Register(ModuleName, 29, "min out amount or max in amount should be positive") + ErrNotPositiveRequireAmount = errorsmod.Register(ModuleName, 30, "required amount should be positive") + ErrTooManyTokensOut = errorsmod.Register(ModuleName, 31, "tx is trying to get more tokens out of the pool than exist") + ErrSpotPriceOverflow = errorsmod.Register(ModuleName, 32, "invalid spot price (overflowed)") + ErrSpotPriceInternal = errorsmod.Register(ModuleName, 33, "internal spot price error") + + ErrPoolParamsInvalidDenom = errorsmod.Register(ModuleName, 50, "pool params' LBP params has an invalid denomination") + ErrPoolParamsInvalidNumDenoms = errorsmod.Register(ModuleName, 51, "pool params' LBP doesn't have same number of params as underlying pool") + + ErrNotImplemented = errorsmod.Register(ModuleName, 60, "function not implemented") + + ErrNotStableSwapPool = errorsmod.Register(ModuleName, 61, "not stableswap pool") + ErrInvalidScalingFactorLength = errorsmod.Register(ModuleName, 62, "pool liquidity and scaling factors must have same length") + ErrNotScalingFactorGovernor = errorsmod.Register(ModuleName, 63, "not scaling factor governor") + ErrInvalidScalingFactors = errorsmod.Register(ModuleName, 64, "scaling factors cannot be 0 or use more than 63 bits") + ErrHitMaxScaledAssets = errorsmod.Register(ModuleName, 65, "post-scaled pool assets can not exceed 10^34") + ErrHitMinScaledAssets = errorsmod.Register(ModuleName, 66, "post-scaled pool assets can not be less than 1") + ErrNoGaugeToRedirect = errorsmod.Register(ModuleName, 67, "could not find gauge to redirect") + ErrMustHaveTwoDenoms = errorsmod.Register(ModuleName, 68, "can only have 2 denoms in CL pool") +) diff --git a/tests/interchaintest/osmosis/gamm/types/events.go b/tests/interchaintest/osmosis/gamm/types/events.go new file mode 100644 index 00000000..39002c5a --- /dev/null +++ b/tests/interchaintest/osmosis/gamm/types/events.go @@ -0,0 +1,21 @@ +package types + +const ( + TypeEvtPoolJoined = "pool_joined" + TypeEvtPoolExited = "pool_exited" + TypeEvtTokenSwapped = "token_swapped" + TypeEvtMigrateShares = "migrate_shares" + + AttributeValueCategory = ModuleName + AttributeKeyPoolId = "pool_id" + AttributeKeyPoolIdEntering = "pool_id_entering" + AttributeKeyPoolIdLeaving = "pool_id_leaving" + AttributeKeySwapFee = "swap_fee" + AttributeKeyTokensIn = "tokens_in" + AttributeKeyTokensOut = "tokens_out" + + AttributePositionId = "position_id" + AttributeAmount0 = "amount0" + AttributeAmount1 = "amount1" + AttributeLiquidity = "liquidity" +) diff --git a/tests/interchaintest/osmosis/gamm/types/hooks.go b/tests/interchaintest/osmosis/gamm/types/hooks.go new file mode 100644 index 00000000..20d1040c --- /dev/null +++ b/tests/interchaintest/osmosis/gamm/types/hooks.go @@ -0,0 +1,55 @@ +package types + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/osmosis-labs/fee-abstraction/v7/tests/interchaintest/osmosis/osmomath" +) + +type GammHooks interface { + // AfterCFMMPoolCreated is called after a CFMM pool is created + AfterCFMMPoolCreated(ctx sdk.Context, sender sdk.AccAddress, poolId uint64) + + // AfterJoinPool is called after JoinPool, JoinSwapExternAmountIn, and JoinSwapShareAmountOut + AfterJoinPool(ctx sdk.Context, sender sdk.AccAddress, poolId uint64, enterCoins sdk.Coins, shareOutAmount osmomath.Int) + + // AfterExitPool is called after ExitPool, ExitSwapShareAmountIn, and ExitSwapExternAmountOut + AfterExitPool(ctx sdk.Context, sender sdk.AccAddress, poolId uint64, shareInAmount osmomath.Int, exitCoins sdk.Coins) + + // AfterSwap is called after SwapExactAmountIn and SwapExactAmountOut in x/gamm. + AfterCFMMSwap(ctx sdk.Context, sender sdk.AccAddress, poolId uint64, input sdk.Coins, output sdk.Coins) +} + +var _ GammHooks = MultiGammHooks{} + +// combine multiple gamm hooks, all hook functions are run in array sequence. +type MultiGammHooks []GammHooks + +// Creates hooks for the Gamm Module. +func NewMultiGammHooks(hooks ...GammHooks) MultiGammHooks { + return hooks +} + +func (h MultiGammHooks) AfterCFMMPoolCreated(ctx sdk.Context, sender sdk.AccAddress, poolId uint64) { + for i := range h { + h[i].AfterCFMMPoolCreated(ctx, sender, poolId) + } +} + +func (h MultiGammHooks) AfterJoinPool(ctx sdk.Context, sender sdk.AccAddress, poolId uint64, enterCoins sdk.Coins, shareOutAmount osmomath.Int) { + for i := range h { + h[i].AfterJoinPool(ctx, sender, poolId, enterCoins, shareOutAmount) + } +} + +func (h MultiGammHooks) AfterExitPool(ctx sdk.Context, sender sdk.AccAddress, poolId uint64, shareInAmount osmomath.Int, exitCoins sdk.Coins) { + for i := range h { + h[i].AfterExitPool(ctx, sender, poolId, shareInAmount, exitCoins) + } +} + +func (h MultiGammHooks) AfterCFMMSwap(ctx sdk.Context, sender sdk.AccAddress, poolId uint64, input sdk.Coins, output sdk.Coins) { + for i := range h { + h[i].AfterCFMMSwap(ctx, sender, poolId, input, output) + } +} diff --git a/tests/interchaintest/osmosis/gamm/types/key.go b/tests/interchaintest/osmosis/gamm/types/key.go new file mode 100644 index 00000000..36a00421 --- /dev/null +++ b/tests/interchaintest/osmosis/gamm/types/key.go @@ -0,0 +1,70 @@ +package types + +import ( + "fmt" + "strconv" + "strings" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +const ( + ModuleName = "gamm" + + StoreKey = ModuleName + + RouterKey = ModuleName + + QuerierRoute = ModuleName + + GAMMTokenPrefix = "gamm/pool/" +) + +var ( + // KeyNextGlobalPoolId defines key to store the next Pool ID to be used. + KeyNextGlobalPoolId = []byte{0x01} + // KeyPrefixPools defines prefix to store pools. + KeyPrefixPools = []byte{0x02} + // KeyTotalLiquidity defines key to store total liquidity. + KeyTotalLiquidity = []byte{0x03} + + KeyPrefixMigrationInfoBalancerPool = []byte{0x04} + KeyPrefixMigrationInfoCLPool = []byte{0x05} +) + +func MustGetPoolIdFromShareDenom(denom string) uint64 { + number, err := GetPoolIdFromShareDenom(denom) + if err != nil { + panic(err) + } + return number +} + +func GetPoolIdFromShareDenom(denom string) (uint64, error) { + numberStr := strings.TrimLeft(denom, GAMMTokenPrefix) + number, err := strconv.Atoi(numberStr) + if err != nil { + return 0, err + } + return uint64(number), nil +} + +func GetDenomPrefix(denom string) []byte { + return append(KeyTotalLiquidity, []byte(denom)...) +} + +func GetPoolShareDenom(poolId uint64) string { + return fmt.Sprintf("%s%d", GAMMTokenPrefix, poolId) +} + +func GetKeyPrefixPools(poolId uint64) []byte { + return append(KeyPrefixPools, sdk.Uint64ToBigEndian(poolId)...) +} + +func GetKeyPrefixMigrationInfoBalancerPool(balancerPoolId uint64) []byte { + return append(KeyPrefixMigrationInfoBalancerPool, sdk.Uint64ToBigEndian(balancerPoolId)...) +} + +func GetKeyPrefixMigrationInfoPoolCLPool(concentratedPoolId uint64) []byte { + return append(KeyPrefixMigrationInfoCLPool, sdk.Uint64ToBigEndian(concentratedPoolId)...) +} diff --git a/tests/interchaintest/osmosis/gamm/types/msg_lp.go b/tests/interchaintest/osmosis/gamm/types/msg_lp.go new file mode 100644 index 00000000..65b000f8 --- /dev/null +++ b/tests/interchaintest/osmosis/gamm/types/msg_lp.go @@ -0,0 +1,50 @@ +package types + +type LiquidityChangeType int + +const ( + AddLiquidity LiquidityChangeType = iota + RemoveLiquidity +) + +// LiquidityChangeMsg defines a simple interface for determining if an LP msg +// is removing or adding liquidity. +type LiquidityChangeMsg interface { + LiquidityChangeType() LiquidityChangeType +} + +var ( + _ LiquidityChangeMsg = MsgExitPool{} + _ LiquidityChangeMsg = MsgExitSwapShareAmountIn{} + _ LiquidityChangeMsg = MsgExitSwapExternAmountOut{} +) + +var ( + _ LiquidityChangeMsg = MsgJoinPool{} + _ LiquidityChangeMsg = MsgJoinSwapExternAmountIn{} + _ LiquidityChangeMsg = MsgJoinSwapShareAmountOut{} +) + +func (msg MsgExitPool) LiquidityChangeType() LiquidityChangeType { + return RemoveLiquidity +} + +func (msg MsgExitSwapShareAmountIn) LiquidityChangeType() LiquidityChangeType { + return RemoveLiquidity +} + +func (msg MsgExitSwapExternAmountOut) LiquidityChangeType() LiquidityChangeType { + return RemoveLiquidity +} + +func (msg MsgJoinPool) LiquidityChangeType() LiquidityChangeType { + return AddLiquidity +} + +func (msg MsgJoinSwapExternAmountIn) LiquidityChangeType() LiquidityChangeType { + return AddLiquidity +} + +func (msg MsgJoinSwapShareAmountOut) LiquidityChangeType() LiquidityChangeType { + return AddLiquidity +} diff --git a/tests/interchaintest/osmosis/gamm/types/msg_swap.go b/tests/interchaintest/osmosis/gamm/types/msg_swap.go new file mode 100644 index 00000000..253c8ead --- /dev/null +++ b/tests/interchaintest/osmosis/gamm/types/msg_swap.go @@ -0,0 +1,46 @@ +package types + +import poolmanagertypes "github.com/osmosis-labs/fee-abstraction/v7/tests/interchaintest/osmosis/poolmanager/types" + +// SwapMsg defines a simple interface for getting the token denoms on a swap message route. +type SwapMsgRoute = poolmanagertypes.SwapMsgRoute + +var ( + _ SwapMsgRoute = MsgSwapExactAmountOut{} + _ SwapMsgRoute = MsgSwapExactAmountIn{} +) + +func (msg MsgSwapExactAmountOut) TokenInDenom() string { + return msg.Routes[0].GetTokenInDenom() +} + +func (msg MsgSwapExactAmountOut) TokenOutDenom() string { + return msg.TokenOut.Denom +} + +func (msg MsgSwapExactAmountOut) TokenDenomsOnPath() []string { + denoms := make([]string, 0, len(msg.Routes)+1) + for i := 0; i < len(msg.Routes); i++ { + denoms = append(denoms, msg.Routes[i].TokenInDenom) + } + denoms = append(denoms, msg.TokenOutDenom()) + return denoms +} + +func (msg MsgSwapExactAmountIn) TokenInDenom() string { + return msg.TokenIn.Denom +} + +func (msg MsgSwapExactAmountIn) TokenOutDenom() string { + lastRouteIndex := len(msg.Routes) - 1 + return msg.Routes[lastRouteIndex].GetTokenOutDenom() +} + +func (msg MsgSwapExactAmountIn) TokenDenomsOnPath() []string { + denoms := make([]string, 0, len(msg.Routes)+1) + denoms = append(denoms, msg.TokenInDenom()) + for i := 0; i < len(msg.Routes); i++ { + denoms = append(denoms, msg.Routes[i].TokenOutDenom) + } + return denoms +} diff --git a/tests/interchaintest/osmosis/gamm/types/msgs.go b/tests/interchaintest/osmosis/gamm/types/msgs.go new file mode 100644 index 00000000..a24eb31f --- /dev/null +++ b/tests/interchaintest/osmosis/gamm/types/msgs.go @@ -0,0 +1,350 @@ +package types + +import ( + "fmt" + "strings" + "time" + + errorsmod "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +// constants. +const ( + TypeMsgSwapExactAmountIn = "swap_exact_amount_in" + TypeMsgSwapExactAmountOut = "swap_exact_amount_out" + TypeMsgJoinPool = "join_pool" + TypeMsgExitPool = "exit_pool" + TypeMsgJoinSwapExternAmountIn = "join_swap_extern_amount_in" + TypeMsgJoinSwapShareAmountOut = "join_swap_share_amount_out" + TypeMsgExitSwapExternAmountOut = "exit_swap_extern_amount_out" + TypeMsgExitSwapShareAmountIn = "exit_swap_share_amount_in" +) + +func ValidateFutureGovernor(governor string) error { + // allow empty governor + if governor == "" { + return nil + } + + // validation for future owner + // "osmo1fqlr98d45v5ysqgp6h56kpujcj4cvsjnjq9nck" + _, err := sdk.AccAddressFromBech32(governor) + if err == nil { + return nil + } + + lockTimeStr := "" + splits := strings.Split(governor, ",") + if len(splits) > 2 { + return errorsmod.Wrap(sdkerrors.ErrInvalidAddress, fmt.Sprintf("invalid future governor: %s", governor)) + } + + // token,100h + if len(splits) == 2 { + lpTokenStr := splits[0] + if sdk.ValidateDenom(lpTokenStr) != nil { + return errorsmod.Wrap(sdkerrors.ErrInvalidAddress, fmt.Sprintf("invalid future governor: %s", governor)) + } + lockTimeStr = splits[1] + } + + // 100h + if len(splits) == 1 { + lockTimeStr = splits[0] + } + + // Note that a duration of 0 is allowed + _, err = time.ParseDuration(lockTimeStr) + if err != nil { + return errorsmod.Wrap(sdkerrors.ErrInvalidAddress, fmt.Sprintf("invalid future governor: %s", governor)) + } + return nil +} + +var _ sdk.Msg = &MsgSwapExactAmountIn{} + +func (msg MsgSwapExactAmountIn) Route() string { return RouterKey } +func (msg MsgSwapExactAmountIn) Type() string { return TypeMsgSwapExactAmountIn } +func (msg MsgSwapExactAmountIn) ValidateBasic() error { + _, err := sdk.AccAddressFromBech32(msg.Sender) + if err != nil { + return errorsmod.Wrapf(sdkerrors.ErrInvalidAddress, "Invalid sender address (%s)", err) + } + + err = SwapAmountInRoutes(msg.Routes).Validate() + if err != nil { + return err + } + + if !msg.TokenIn.IsValid() || !msg.TokenIn.IsPositive() { + return errorsmod.Wrap(sdkerrors.ErrInvalidCoins, msg.TokenIn.String()) + } + + if !msg.TokenOutMinAmount.IsPositive() { + return ErrNotPositiveCriteria + } + + return nil +} + +func (msg MsgSwapExactAmountIn) GetSignBytes() []byte { + return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(&msg)) +} + +func (msg MsgSwapExactAmountIn) GetSigners() []sdk.AccAddress { + sender, err := sdk.AccAddressFromBech32(msg.Sender) + if err != nil { + panic(err) + } + return []sdk.AccAddress{sender} +} + +var _ sdk.Msg = &MsgSwapExactAmountOut{} + +func (msg MsgSwapExactAmountOut) Route() string { return RouterKey } +func (msg MsgSwapExactAmountOut) Type() string { return TypeMsgSwapExactAmountOut } +func (msg MsgSwapExactAmountOut) ValidateBasic() error { + _, err := sdk.AccAddressFromBech32(msg.Sender) + if err != nil { + return errorsmod.Wrapf(sdkerrors.ErrInvalidAddress, "Invalid sender address (%s)", err) + } + + err = SwapAmountOutRoutes(msg.Routes).Validate() + if err != nil { + return err + } + + if !msg.TokenOut.IsValid() || !msg.TokenOut.IsPositive() { + return errorsmod.Wrap(sdkerrors.ErrInvalidCoins, msg.TokenOut.String()) + } + + if !msg.TokenInMaxAmount.IsPositive() { + return ErrNotPositiveCriteria + } + + return nil +} + +func (msg MsgSwapExactAmountOut) GetSignBytes() []byte { + return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(&msg)) +} + +func (msg MsgSwapExactAmountOut) GetSigners() []sdk.AccAddress { + sender, err := sdk.AccAddressFromBech32(msg.Sender) + if err != nil { + panic(err) + } + return []sdk.AccAddress{sender} +} + +var _ sdk.Msg = &MsgJoinPool{} + +func (msg MsgJoinPool) Route() string { return RouterKey } +func (msg MsgJoinPool) Type() string { return TypeMsgJoinPool } +func (msg MsgJoinPool) ValidateBasic() error { + _, err := sdk.AccAddressFromBech32(msg.Sender) + if err != nil { + return errorsmod.Wrapf(sdkerrors.ErrInvalidAddress, "Invalid sender address (%s)", err) + } + + if !msg.ShareOutAmount.IsPositive() { + return errorsmod.Wrap(ErrNotPositiveRequireAmount, msg.ShareOutAmount.String()) + } + + tokenInMaxs := sdk.Coins(msg.TokenInMaxs) + if !tokenInMaxs.IsValid() { + return errorsmod.Wrap(sdkerrors.ErrInvalidCoins, tokenInMaxs.String()) + } + + return nil +} + +func (msg MsgJoinPool) GetSignBytes() []byte { + return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(&msg)) +} + +func (msg MsgJoinPool) GetSigners() []sdk.AccAddress { + sender, err := sdk.AccAddressFromBech32(msg.Sender) + if err != nil { + panic(err) + } + return []sdk.AccAddress{sender} +} + +var _ sdk.Msg = &MsgExitPool{} + +func (msg MsgExitPool) Route() string { return RouterKey } +func (msg MsgExitPool) Type() string { return TypeMsgExitPool } +func (msg MsgExitPool) ValidateBasic() error { + _, err := sdk.AccAddressFromBech32(msg.Sender) + if err != nil { + return errorsmod.Wrapf(sdkerrors.ErrInvalidAddress, "Invalid sender address (%s)", err) + } + + if !msg.ShareInAmount.IsPositive() { + return errorsmod.Wrap(ErrNotPositiveRequireAmount, msg.ShareInAmount.String()) + } + + tokenOutMins := sdk.Coins(msg.TokenOutMins) + if !tokenOutMins.IsValid() { + return errorsmod.Wrap(sdkerrors.ErrInvalidCoins, tokenOutMins.String()) + } + + return nil +} + +func (msg MsgExitPool) GetSignBytes() []byte { + return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(&msg)) +} + +func (msg MsgExitPool) GetSigners() []sdk.AccAddress { + sender, err := sdk.AccAddressFromBech32(msg.Sender) + if err != nil { + panic(err) + } + return []sdk.AccAddress{sender} +} + +var _ sdk.Msg = &MsgJoinSwapExternAmountIn{} + +func (msg MsgJoinSwapExternAmountIn) Route() string { return RouterKey } +func (msg MsgJoinSwapExternAmountIn) Type() string { return TypeMsgJoinSwapExternAmountIn } +func (msg MsgJoinSwapExternAmountIn) ValidateBasic() error { + _, err := sdk.AccAddressFromBech32(msg.Sender) + if err != nil { + return errorsmod.Wrapf(sdkerrors.ErrInvalidAddress, "Invalid sender address (%s)", err) + } + + if !msg.TokenIn.IsValid() || !msg.TokenIn.IsPositive() { + return errorsmod.Wrap(sdkerrors.ErrInvalidCoins, msg.TokenIn.String()) + } + + if !msg.ShareOutMinAmount.IsPositive() { + return errorsmod.Wrap(ErrNotPositiveCriteria, msg.ShareOutMinAmount.String()) + } + + return nil +} + +func (msg MsgJoinSwapExternAmountIn) GetSignBytes() []byte { + return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(&msg)) +} + +func (msg MsgJoinSwapExternAmountIn) GetSigners() []sdk.AccAddress { + sender, err := sdk.AccAddressFromBech32(msg.Sender) + if err != nil { + panic(err) + } + return []sdk.AccAddress{sender} +} + +var _ sdk.Msg = &MsgJoinSwapShareAmountOut{} + +func (msg MsgJoinSwapShareAmountOut) Route() string { return RouterKey } +func (msg MsgJoinSwapShareAmountOut) Type() string { return TypeMsgJoinSwapShareAmountOut } +func (msg MsgJoinSwapShareAmountOut) ValidateBasic() error { + _, err := sdk.AccAddressFromBech32(msg.Sender) + if err != nil { + return errorsmod.Wrapf(sdkerrors.ErrInvalidAddress, "Invalid sender address (%s)", err) + } + + err = sdk.ValidateDenom(msg.TokenInDenom) + if err != nil { + return err + } + + if !msg.ShareOutAmount.IsPositive() { + return errorsmod.Wrap(ErrNotPositiveRequireAmount, msg.ShareOutAmount.String()) + } + + if !msg.TokenInMaxAmount.IsPositive() { + return errorsmod.Wrap(ErrNotPositiveCriteria, msg.TokenInMaxAmount.String()) + } + + return nil +} + +func (msg MsgJoinSwapShareAmountOut) GetSignBytes() []byte { + return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(&msg)) +} + +func (msg MsgJoinSwapShareAmountOut) GetSigners() []sdk.AccAddress { + sender, err := sdk.AccAddressFromBech32(msg.Sender) + if err != nil { + panic(err) + } + return []sdk.AccAddress{sender} +} + +var _ sdk.Msg = &MsgExitSwapExternAmountOut{} + +func (msg MsgExitSwapExternAmountOut) Route() string { return RouterKey } +func (msg MsgExitSwapExternAmountOut) Type() string { return TypeMsgExitSwapExternAmountOut } +func (msg MsgExitSwapExternAmountOut) ValidateBasic() error { + _, err := sdk.AccAddressFromBech32(msg.Sender) + if err != nil { + return errorsmod.Wrapf(sdkerrors.ErrInvalidAddress, "Invalid sender address (%s)", err) + } + + if !msg.TokenOut.IsValid() || !msg.TokenOut.IsPositive() { + return errorsmod.Wrap(sdkerrors.ErrInvalidCoins, msg.TokenOut.String()) + } + + if !msg.ShareInMaxAmount.IsPositive() { + return errorsmod.Wrap(ErrNotPositiveCriteria, msg.ShareInMaxAmount.String()) + } + + return nil +} + +func (msg MsgExitSwapExternAmountOut) GetSignBytes() []byte { + return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(&msg)) +} + +func (msg MsgExitSwapExternAmountOut) GetSigners() []sdk.AccAddress { + sender, err := sdk.AccAddressFromBech32(msg.Sender) + if err != nil { + panic(err) + } + return []sdk.AccAddress{sender} +} + +var _ sdk.Msg = &MsgExitSwapShareAmountIn{} + +func (msg MsgExitSwapShareAmountIn) Route() string { return RouterKey } +func (msg MsgExitSwapShareAmountIn) Type() string { return TypeMsgExitSwapShareAmountIn } +func (msg MsgExitSwapShareAmountIn) ValidateBasic() error { + _, err := sdk.AccAddressFromBech32(msg.Sender) + if err != nil { + return errorsmod.Wrapf(sdkerrors.ErrInvalidAddress, "Invalid sender address (%s)", err) + } + + err = sdk.ValidateDenom(msg.TokenOutDenom) + if err != nil { + return err + } + + if !msg.ShareInAmount.IsPositive() { + return errorsmod.Wrap(ErrNotPositiveRequireAmount, msg.ShareInAmount.String()) + } + + if !msg.TokenOutMinAmount.IsPositive() { + return errorsmod.Wrap(ErrNotPositiveCriteria, msg.TokenOutMinAmount.String()) + } + + return nil +} + +func (msg MsgExitSwapShareAmountIn) GetSignBytes() []byte { + return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(&msg)) +} + +func (msg MsgExitSwapShareAmountIn) GetSigners() []sdk.AccAddress { + sender, err := sdk.AccAddressFromBech32(msg.Sender) + if err != nil { + panic(err) + } + return []sdk.AccAddress{sender} +} diff --git a/tests/interchaintest/osmosis/gamm/types/pool.go b/tests/interchaintest/osmosis/gamm/types/pool.go new file mode 100644 index 00000000..fad81035 --- /dev/null +++ b/tests/interchaintest/osmosis/gamm/types/pool.go @@ -0,0 +1,115 @@ +package types + +import ( + "time" + + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/osmosis-labs/fee-abstraction/v7/tests/interchaintest/osmosis/osmomath" + poolmanagertypes "github.com/osmosis-labs/fee-abstraction/v7/tests/interchaintest/osmosis/poolmanager/types" +) + +// CFMMPoolI defines an interface for pools representing constant function +// AMM. +type CFMMPoolI interface { + poolmanagertypes.PoolI + + // JoinPool joins the pool using all of the tokensIn provided. + // The AMM swaps to the correct internal ratio should be and returns the number of shares created. + // This function is mutative and updates the pool's internal state if there is no error. + // It is up to pool implementation if they support LP'ing at arbitrary ratios, or a subset of ratios. + // Pools are expected to guarantee LP'ing at the exact ratio, and single sided LP'ing. + JoinPool(ctx sdk.Context, tokensIn sdk.Coins, spreadFactor osmomath.Dec) (numShares osmomath.Int, err error) + // JoinPoolNoSwap joins the pool with an all-asset join using the maximum amount possible given the tokensIn provided. + // This function is mutative and updates the pool's internal state if there is no error. + // Pools are expected to guarantee LP'ing at the exact ratio. + JoinPoolNoSwap(ctx sdk.Context, tokensIn sdk.Coins, spreadFactor osmomath.Dec) (numShares osmomath.Int, err error) + + // ExitPool exits #numShares LP shares from the pool, decreases its internal liquidity & LP share totals, + // and returns the number of coins that are being returned. + // This mutates the pool and state. + ExitPool(ctx sdk.Context, numShares osmomath.Int, exitFee osmomath.Dec) (exitedCoins sdk.Coins, err error) + // CalcJoinPoolNoSwapShares returns how many LP shares JoinPoolNoSwap would return on these arguments. + // This does not mutate the pool, or state. + CalcJoinPoolNoSwapShares(ctx sdk.Context, tokensIn sdk.Coins, spreadFactor osmomath.Dec) (numShares osmomath.Int, newLiquidity sdk.Coins, err error) + // CalcExitPoolCoinsFromShares returns how many coins ExitPool would return on these arguments. + // This does not mutate the pool, or state. + CalcExitPoolCoinsFromShares(ctx sdk.Context, numShares osmomath.Int, exitFee osmomath.Dec) (exitedCoins sdk.Coins, err error) + // CalcJoinPoolShares returns how many LP shares JoinPool would return on these arguments. + // This does not mutate the pool, or state. + CalcJoinPoolShares(ctx sdk.Context, tokensIn sdk.Coins, spreadFactor osmomath.Dec) (numShares osmomath.Int, newLiquidity sdk.Coins, err error) + // SwapOutAmtGivenIn swaps 'tokenIn' against the pool, for tokenOutDenom, with the provided spreadFactor charged. + // Balance transfers are done in the keeper, but this method updates the internal pool state. + SwapOutAmtGivenIn(ctx sdk.Context, tokenIn sdk.Coins, tokenOutDenom string, spreadFactor osmomath.Dec) (tokenOut sdk.Coin, err error) + // CalcOutAmtGivenIn returns how many coins SwapOutAmtGivenIn would return on these arguments. + // This does not mutate the pool, or state. + CalcOutAmtGivenIn(ctx sdk.Context, tokenIn sdk.Coins, tokenOutDenom string, spreadFactor osmomath.Dec) (tokenOut sdk.Coin, err error) + + // SwapInAmtGivenOut swaps exactly enough tokensIn against the pool, to get the provided tokenOut amount out of the pool. + // Balance transfers are done in the keeper, but this method updates the internal pool state. + SwapInAmtGivenOut(ctx sdk.Context, tokenOut sdk.Coins, tokenInDenom string, spreadFactor osmomath.Dec) (tokenIn sdk.Coin, err error) + // CalcInAmtGivenOut returns how many coins SwapInAmtGivenOut would return on these arguments. + // This does not mutate the pool, or state. + CalcInAmtGivenOut(ctx sdk.Context, tokenOut sdk.Coins, tokenInDenom string, spreadFactor osmomath.Dec) (tokenIn sdk.Coin, err error) + // GetTotalShares returns the total number of LP shares in the pool + GetTotalShares() osmomath.Int + // GetTotalPoolLiquidity returns the coins in the pool owned by all LPs + GetTotalPoolLiquidity(ctx sdk.Context) sdk.Coins + // GetExitFee returns the pool's exit fee, based on the current state. + // Pools may choose to make their exit fees dependent upon state. + GetExitFee(ctx sdk.Context) osmomath.Dec +} + +// PoolAmountOutExtension is an extension of the PoolI +// interface definiting an abstraction for pools that hold tokens. +// In addition, it supports JoinSwapShareAmountOut and ExitSwapExactAmountOut methods +// that allow joining with the exact amount of shares to get out, and exiting with exact +// amount of coins to get out. +// See definitions below. +type PoolAmountOutExtension interface { + CFMMPoolI + + // CalcTokenInShareAmountOut returns the number of tokenInDenom tokens + // that would be returned if swapped for an exact number of shares (shareOutAmount). + // Returns error if tokenInDenom is not in the pool or if fails to approximate + // given the shareOutAmount. + // This method does not mutate the pool + CalcTokenInShareAmountOut( + ctx sdk.Context, + tokenInDenom string, + shareOutAmount osmomath.Int, + spreadFactor osmomath.Dec, + ) (tokenInAmount osmomath.Int, err error) + + // JoinPoolTokenInMaxShareAmountOut add liquidity to a specified pool with a maximum amount of tokens in (tokenInMaxAmount) + // and swaps to an exact number of shares (shareOutAmount). + JoinPoolTokenInMaxShareAmountOut( + ctx sdk.Context, + tokenInDenom string, + shareOutAmount osmomath.Int, + ) (tokenInAmount osmomath.Int, err error) + + // ExitSwapExactAmountOut removes liquidity from a specified pool with a maximum amount of LP shares (shareInMaxAmount) + // and swaps to an exact amount of one of the token pairs (tokenOut). + ExitSwapExactAmountOut( + ctx sdk.Context, + tokenOut sdk.Coin, + shareInMaxAmount osmomath.Int, + ) (shareInAmount osmomath.Int, err error) + + // IncreaseLiquidity increases the pool's liquidity by the specified sharesOut and coinsIn. + IncreaseLiquidity(sharesOut osmomath.Int, coinsIn sdk.Coins) +} + +// WeightedPoolExtension is an extension of the PoolI interface +// That defines an additional API for handling the pool's weights. +type WeightedPoolExtension interface { + CFMMPoolI + + // PokePool determines if a pool's weights need to be updated and updates + // them if so. + PokePool(blockTime time.Time) + + // GetTokenWeight returns the weight of the specified token in the pool. + GetTokenWeight(denom string) (osmomath.Int, error) +} diff --git a/tests/interchaintest/osmosis/gamm/types/route.go b/tests/interchaintest/osmosis/gamm/types/route.go new file mode 100644 index 00000000..97ab161a --- /dev/null +++ b/tests/interchaintest/osmosis/gamm/types/route.go @@ -0,0 +1,95 @@ +package types + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + + poolmanagertypes "github.com/osmosis-labs/fee-abstraction/v7/tests/interchaintest/osmosis/poolmanager/types" +) + +type SwapAmountInRoutes []poolmanagertypes.SwapAmountInRoute + +func (routes SwapAmountInRoutes) Validate() error { + if len(routes) == 0 { + return ErrEmptyRoutes + } + + for _, route := range routes { + err := sdk.ValidateDenom(route.TokenOutDenom) + if err != nil { + return err + } + } + + return nil +} + +func (routes SwapAmountInRoutes) IntermediateDenoms() []string { + if len(routes) < 2 { + return nil + } + intermediateDenoms := make([]string, 0, len(routes)-1) + for _, route := range routes[:len(routes)-1] { + intermediateDenoms = append(intermediateDenoms, route.TokenOutDenom) + } + + return intermediateDenoms +} + +func (routes SwapAmountInRoutes) PoolIds() []uint64 { + poolIds := make([]uint64, 0, len(routes)) + for _, route := range routes { + poolIds = append(poolIds, route.PoolId) + } + return poolIds +} + +func (routes SwapAmountInRoutes) Length() int { + return len(routes) +} + +type SwapAmountOutRoutes []poolmanagertypes.SwapAmountOutRoute + +func (routes SwapAmountOutRoutes) Validate() error { + if len(routes) == 0 { + return ErrEmptyRoutes + } + + for _, route := range routes { + err := sdk.ValidateDenom(route.TokenInDenom) + if err != nil { + return err + } + } + + return nil +} + +func (routes SwapAmountOutRoutes) IntermediateDenoms() []string { + if len(routes) < 2 { + return nil + } + intermediateDenoms := make([]string, 0, len(routes)-1) + for _, route := range routes[1:] { + intermediateDenoms = append(intermediateDenoms, route.TokenInDenom) + } + + return intermediateDenoms +} + +func (routes SwapAmountOutRoutes) PoolIds() []uint64 { + poolIds := make([]uint64, 0, len(routes)) + for _, route := range routes { + poolIds = append(poolIds, route.PoolId) + } + return poolIds +} + +func (routes SwapAmountOutRoutes) Length() int { + return len(routes) +} + +type MultihopRoute interface { + Length() int + PoolIds() []uint64 + IntermediateDenoms() []string +} diff --git a/tests/interchaintest/osmosis/gamm/types/tx.pb.go b/tests/interchaintest/osmosis/gamm/types/tx.pb.go new file mode 100644 index 00000000..aaed6c6a --- /dev/null +++ b/tests/interchaintest/osmosis/gamm/types/tx.pb.go @@ -0,0 +1,4568 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: osmosis/gamm/v1beta1/tx.proto + +package types + +import ( + context "context" + cosmossdk_io_math "cosmossdk.io/math" + fmt "fmt" + types "github.com/cosmos/cosmos-sdk/types" + _ "github.com/cosmos/cosmos-sdk/types/tx/amino" + _ "github.com/cosmos/gogoproto/gogoproto" + grpc1 "github.com/cosmos/gogoproto/grpc" + proto "github.com/cosmos/gogoproto/proto" + types1 "github.com/osmosis-labs/fee-abstraction/v7/tests/interchaintest/osmosis/poolmanager/types" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// ===================== MsgJoinPool +// This is really MsgJoinPoolNoSwap +type MsgJoinPool struct { + Sender string `protobuf:"bytes,1,opt,name=sender,proto3" json:"sender,omitempty" yaml:"sender"` + PoolId uint64 `protobuf:"varint,2,opt,name=pool_id,json=poolId,proto3" json:"pool_id,omitempty" yaml:"pool_id"` + ShareOutAmount cosmossdk_io_math.Int `protobuf:"bytes,3,opt,name=share_out_amount,json=shareOutAmount,proto3,customtype=cosmossdk.io/math.Int" json:"share_out_amount" yaml:"pool_amount_out"` + TokenInMaxs []types.Coin `protobuf:"bytes,4,rep,name=token_in_maxs,json=tokenInMaxs,proto3" json:"token_in_maxs" yaml:"token_in_max_amounts"` +} + +func (m *MsgJoinPool) Reset() { *m = MsgJoinPool{} } +func (m *MsgJoinPool) String() string { return proto.CompactTextString(m) } +func (*MsgJoinPool) ProtoMessage() {} +func (*MsgJoinPool) Descriptor() ([]byte, []int) { + return fileDescriptor_cfc8fd3ac7df3247, []int{0} +} +func (m *MsgJoinPool) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgJoinPool) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgJoinPool.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgJoinPool) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgJoinPool.Merge(m, src) +} +func (m *MsgJoinPool) XXX_Size() int { + return m.Size() +} +func (m *MsgJoinPool) XXX_DiscardUnknown() { + xxx_messageInfo_MsgJoinPool.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgJoinPool proto.InternalMessageInfo + +func (m *MsgJoinPool) GetSender() string { + if m != nil { + return m.Sender + } + return "" +} + +func (m *MsgJoinPool) GetPoolId() uint64 { + if m != nil { + return m.PoolId + } + return 0 +} + +func (m *MsgJoinPool) GetTokenInMaxs() []types.Coin { + if m != nil { + return m.TokenInMaxs + } + return nil +} + +type MsgJoinPoolResponse struct { + ShareOutAmount cosmossdk_io_math.Int `protobuf:"bytes,1,opt,name=share_out_amount,json=shareOutAmount,proto3,customtype=cosmossdk.io/math.Int" json:"share_out_amount" yaml:"share_out_amount"` + TokenIn []types.Coin `protobuf:"bytes,2,rep,name=token_in,json=tokenIn,proto3" json:"token_in" yaml:"token_in"` +} + +func (m *MsgJoinPoolResponse) Reset() { *m = MsgJoinPoolResponse{} } +func (m *MsgJoinPoolResponse) String() string { return proto.CompactTextString(m) } +func (*MsgJoinPoolResponse) ProtoMessage() {} +func (*MsgJoinPoolResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_cfc8fd3ac7df3247, []int{1} +} +func (m *MsgJoinPoolResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgJoinPoolResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgJoinPoolResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgJoinPoolResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgJoinPoolResponse.Merge(m, src) +} +func (m *MsgJoinPoolResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgJoinPoolResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgJoinPoolResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgJoinPoolResponse proto.InternalMessageInfo + +func (m *MsgJoinPoolResponse) GetTokenIn() []types.Coin { + if m != nil { + return m.TokenIn + } + return nil +} + +// ===================== MsgExitPool +type MsgExitPool struct { + Sender string `protobuf:"bytes,1,opt,name=sender,proto3" json:"sender,omitempty" yaml:"sender"` + PoolId uint64 `protobuf:"varint,2,opt,name=pool_id,json=poolId,proto3" json:"pool_id,omitempty" yaml:"pool_id"` + ShareInAmount cosmossdk_io_math.Int `protobuf:"bytes,3,opt,name=share_in_amount,json=shareInAmount,proto3,customtype=cosmossdk.io/math.Int" json:"share_in_amount" yaml:"share_in_amount"` + TokenOutMins []types.Coin `protobuf:"bytes,4,rep,name=token_out_mins,json=tokenOutMins,proto3" json:"token_out_mins" yaml:"token_out_min_amounts"` +} + +func (m *MsgExitPool) Reset() { *m = MsgExitPool{} } +func (m *MsgExitPool) String() string { return proto.CompactTextString(m) } +func (*MsgExitPool) ProtoMessage() {} +func (*MsgExitPool) Descriptor() ([]byte, []int) { + return fileDescriptor_cfc8fd3ac7df3247, []int{2} +} +func (m *MsgExitPool) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgExitPool) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgExitPool.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgExitPool) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgExitPool.Merge(m, src) +} +func (m *MsgExitPool) XXX_Size() int { + return m.Size() +} +func (m *MsgExitPool) XXX_DiscardUnknown() { + xxx_messageInfo_MsgExitPool.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgExitPool proto.InternalMessageInfo + +func (m *MsgExitPool) GetSender() string { + if m != nil { + return m.Sender + } + return "" +} + +func (m *MsgExitPool) GetPoolId() uint64 { + if m != nil { + return m.PoolId + } + return 0 +} + +func (m *MsgExitPool) GetTokenOutMins() []types.Coin { + if m != nil { + return m.TokenOutMins + } + return nil +} + +type MsgExitPoolResponse struct { + TokenOut []types.Coin `protobuf:"bytes,1,rep,name=token_out,json=tokenOut,proto3" json:"token_out" yaml:"token_out"` +} + +func (m *MsgExitPoolResponse) Reset() { *m = MsgExitPoolResponse{} } +func (m *MsgExitPoolResponse) String() string { return proto.CompactTextString(m) } +func (*MsgExitPoolResponse) ProtoMessage() {} +func (*MsgExitPoolResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_cfc8fd3ac7df3247, []int{3} +} +func (m *MsgExitPoolResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgExitPoolResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgExitPoolResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgExitPoolResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgExitPoolResponse.Merge(m, src) +} +func (m *MsgExitPoolResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgExitPoolResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgExitPoolResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgExitPoolResponse proto.InternalMessageInfo + +func (m *MsgExitPoolResponse) GetTokenOut() []types.Coin { + if m != nil { + return m.TokenOut + } + return nil +} + +// ===================== MsgSwapExactAmountIn +type MsgSwapExactAmountIn struct { + Sender string `protobuf:"bytes,1,opt,name=sender,proto3" json:"sender,omitempty" yaml:"sender"` + Routes []types1.SwapAmountInRoute `protobuf:"bytes,2,rep,name=routes,proto3" json:"routes"` + TokenIn types.Coin `protobuf:"bytes,3,opt,name=token_in,json=tokenIn,proto3" json:"token_in" yaml:"token_in"` + TokenOutMinAmount cosmossdk_io_math.Int `protobuf:"bytes,4,opt,name=token_out_min_amount,json=tokenOutMinAmount,proto3,customtype=cosmossdk.io/math.Int" json:"token_out_min_amount" yaml:"token_out_min_amount"` +} + +func (m *MsgSwapExactAmountIn) Reset() { *m = MsgSwapExactAmountIn{} } +func (m *MsgSwapExactAmountIn) String() string { return proto.CompactTextString(m) } +func (*MsgSwapExactAmountIn) ProtoMessage() {} +func (*MsgSwapExactAmountIn) Descriptor() ([]byte, []int) { + return fileDescriptor_cfc8fd3ac7df3247, []int{4} +} +func (m *MsgSwapExactAmountIn) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgSwapExactAmountIn) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgSwapExactAmountIn.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgSwapExactAmountIn) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgSwapExactAmountIn.Merge(m, src) +} +func (m *MsgSwapExactAmountIn) XXX_Size() int { + return m.Size() +} +func (m *MsgSwapExactAmountIn) XXX_DiscardUnknown() { + xxx_messageInfo_MsgSwapExactAmountIn.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgSwapExactAmountIn proto.InternalMessageInfo + +func (m *MsgSwapExactAmountIn) GetSender() string { + if m != nil { + return m.Sender + } + return "" +} + +func (m *MsgSwapExactAmountIn) GetRoutes() []types1.SwapAmountInRoute { + if m != nil { + return m.Routes + } + return nil +} + +func (m *MsgSwapExactAmountIn) GetTokenIn() types.Coin { + if m != nil { + return m.TokenIn + } + return types.Coin{} +} + +type MsgSwapExactAmountInResponse struct { + TokenOutAmount cosmossdk_io_math.Int `protobuf:"bytes,1,opt,name=token_out_amount,json=tokenOutAmount,proto3,customtype=cosmossdk.io/math.Int" json:"token_out_amount" yaml:"token_out_amount"` +} + +func (m *MsgSwapExactAmountInResponse) Reset() { *m = MsgSwapExactAmountInResponse{} } +func (m *MsgSwapExactAmountInResponse) String() string { return proto.CompactTextString(m) } +func (*MsgSwapExactAmountInResponse) ProtoMessage() {} +func (*MsgSwapExactAmountInResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_cfc8fd3ac7df3247, []int{5} +} +func (m *MsgSwapExactAmountInResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgSwapExactAmountInResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgSwapExactAmountInResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgSwapExactAmountInResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgSwapExactAmountInResponse.Merge(m, src) +} +func (m *MsgSwapExactAmountInResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgSwapExactAmountInResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgSwapExactAmountInResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgSwapExactAmountInResponse proto.InternalMessageInfo + +type MsgSwapExactAmountOut struct { + Sender string `protobuf:"bytes,1,opt,name=sender,proto3" json:"sender,omitempty" yaml:"sender"` + Routes []types1.SwapAmountOutRoute `protobuf:"bytes,2,rep,name=routes,proto3" json:"routes"` + TokenInMaxAmount cosmossdk_io_math.Int `protobuf:"bytes,3,opt,name=token_in_max_amount,json=tokenInMaxAmount,proto3,customtype=cosmossdk.io/math.Int" json:"token_in_max_amount" yaml:"token_in_max_amount"` + TokenOut types.Coin `protobuf:"bytes,4,opt,name=token_out,json=tokenOut,proto3" json:"token_out" yaml:"token_out"` +} + +func (m *MsgSwapExactAmountOut) Reset() { *m = MsgSwapExactAmountOut{} } +func (m *MsgSwapExactAmountOut) String() string { return proto.CompactTextString(m) } +func (*MsgSwapExactAmountOut) ProtoMessage() {} +func (*MsgSwapExactAmountOut) Descriptor() ([]byte, []int) { + return fileDescriptor_cfc8fd3ac7df3247, []int{6} +} +func (m *MsgSwapExactAmountOut) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgSwapExactAmountOut) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgSwapExactAmountOut.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgSwapExactAmountOut) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgSwapExactAmountOut.Merge(m, src) +} +func (m *MsgSwapExactAmountOut) XXX_Size() int { + return m.Size() +} +func (m *MsgSwapExactAmountOut) XXX_DiscardUnknown() { + xxx_messageInfo_MsgSwapExactAmountOut.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgSwapExactAmountOut proto.InternalMessageInfo + +func (m *MsgSwapExactAmountOut) GetSender() string { + if m != nil { + return m.Sender + } + return "" +} + +func (m *MsgSwapExactAmountOut) GetRoutes() []types1.SwapAmountOutRoute { + if m != nil { + return m.Routes + } + return nil +} + +func (m *MsgSwapExactAmountOut) GetTokenOut() types.Coin { + if m != nil { + return m.TokenOut + } + return types.Coin{} +} + +type MsgSwapExactAmountOutResponse struct { + TokenInAmount cosmossdk_io_math.Int `protobuf:"bytes,1,opt,name=token_in_amount,json=tokenInAmount,proto3,customtype=cosmossdk.io/math.Int" json:"token_in_amount" yaml:"token_in_amount"` +} + +func (m *MsgSwapExactAmountOutResponse) Reset() { *m = MsgSwapExactAmountOutResponse{} } +func (m *MsgSwapExactAmountOutResponse) String() string { return proto.CompactTextString(m) } +func (*MsgSwapExactAmountOutResponse) ProtoMessage() {} +func (*MsgSwapExactAmountOutResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_cfc8fd3ac7df3247, []int{7} +} +func (m *MsgSwapExactAmountOutResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgSwapExactAmountOutResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgSwapExactAmountOutResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgSwapExactAmountOutResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgSwapExactAmountOutResponse.Merge(m, src) +} +func (m *MsgSwapExactAmountOutResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgSwapExactAmountOutResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgSwapExactAmountOutResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgSwapExactAmountOutResponse proto.InternalMessageInfo + +// ===================== MsgJoinSwapExternAmountIn +// TODO: Rename to MsgJoinSwapExactAmountIn +type MsgJoinSwapExternAmountIn struct { + Sender string `protobuf:"bytes,1,opt,name=sender,proto3" json:"sender,omitempty" yaml:"sender"` + PoolId uint64 `protobuf:"varint,2,opt,name=pool_id,json=poolId,proto3" json:"pool_id,omitempty" yaml:"pool_id"` + TokenIn types.Coin `protobuf:"bytes,3,opt,name=token_in,json=tokenIn,proto3" json:"token_in" yaml:"token_in"` + ShareOutMinAmount cosmossdk_io_math.Int `protobuf:"bytes,4,opt,name=share_out_min_amount,json=shareOutMinAmount,proto3,customtype=cosmossdk.io/math.Int" json:"share_out_min_amount" yaml:"share_out_min_amount"` +} + +func (m *MsgJoinSwapExternAmountIn) Reset() { *m = MsgJoinSwapExternAmountIn{} } +func (m *MsgJoinSwapExternAmountIn) String() string { return proto.CompactTextString(m) } +func (*MsgJoinSwapExternAmountIn) ProtoMessage() {} +func (*MsgJoinSwapExternAmountIn) Descriptor() ([]byte, []int) { + return fileDescriptor_cfc8fd3ac7df3247, []int{8} +} +func (m *MsgJoinSwapExternAmountIn) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgJoinSwapExternAmountIn) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgJoinSwapExternAmountIn.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgJoinSwapExternAmountIn) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgJoinSwapExternAmountIn.Merge(m, src) +} +func (m *MsgJoinSwapExternAmountIn) XXX_Size() int { + return m.Size() +} +func (m *MsgJoinSwapExternAmountIn) XXX_DiscardUnknown() { + xxx_messageInfo_MsgJoinSwapExternAmountIn.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgJoinSwapExternAmountIn proto.InternalMessageInfo + +func (m *MsgJoinSwapExternAmountIn) GetSender() string { + if m != nil { + return m.Sender + } + return "" +} + +func (m *MsgJoinSwapExternAmountIn) GetPoolId() uint64 { + if m != nil { + return m.PoolId + } + return 0 +} + +func (m *MsgJoinSwapExternAmountIn) GetTokenIn() types.Coin { + if m != nil { + return m.TokenIn + } + return types.Coin{} +} + +type MsgJoinSwapExternAmountInResponse struct { + ShareOutAmount cosmossdk_io_math.Int `protobuf:"bytes,1,opt,name=share_out_amount,json=shareOutAmount,proto3,customtype=cosmossdk.io/math.Int" json:"share_out_amount" yaml:"share_out_amount"` +} + +func (m *MsgJoinSwapExternAmountInResponse) Reset() { *m = MsgJoinSwapExternAmountInResponse{} } +func (m *MsgJoinSwapExternAmountInResponse) String() string { return proto.CompactTextString(m) } +func (*MsgJoinSwapExternAmountInResponse) ProtoMessage() {} +func (*MsgJoinSwapExternAmountInResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_cfc8fd3ac7df3247, []int{9} +} +func (m *MsgJoinSwapExternAmountInResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgJoinSwapExternAmountInResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgJoinSwapExternAmountInResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgJoinSwapExternAmountInResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgJoinSwapExternAmountInResponse.Merge(m, src) +} +func (m *MsgJoinSwapExternAmountInResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgJoinSwapExternAmountInResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgJoinSwapExternAmountInResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgJoinSwapExternAmountInResponse proto.InternalMessageInfo + +// ===================== MsgJoinSwapShareAmountOut +type MsgJoinSwapShareAmountOut struct { + Sender string `protobuf:"bytes,1,opt,name=sender,proto3" json:"sender,omitempty" yaml:"sender"` + PoolId uint64 `protobuf:"varint,2,opt,name=pool_id,json=poolId,proto3" json:"pool_id,omitempty" yaml:"pool_id"` + TokenInDenom string `protobuf:"bytes,3,opt,name=token_in_denom,json=tokenInDenom,proto3" json:"token_in_denom,omitempty" yaml:"token_in_denom"` + ShareOutAmount cosmossdk_io_math.Int `protobuf:"bytes,4,opt,name=share_out_amount,json=shareOutAmount,proto3,customtype=cosmossdk.io/math.Int" json:"share_out_amount" yaml:"share_out_amount"` + TokenInMaxAmount cosmossdk_io_math.Int `protobuf:"bytes,5,opt,name=token_in_max_amount,json=tokenInMaxAmount,proto3,customtype=cosmossdk.io/math.Int" json:"token_in_max_amount" yaml:"token_in_max_amount"` +} + +func (m *MsgJoinSwapShareAmountOut) Reset() { *m = MsgJoinSwapShareAmountOut{} } +func (m *MsgJoinSwapShareAmountOut) String() string { return proto.CompactTextString(m) } +func (*MsgJoinSwapShareAmountOut) ProtoMessage() {} +func (*MsgJoinSwapShareAmountOut) Descriptor() ([]byte, []int) { + return fileDescriptor_cfc8fd3ac7df3247, []int{10} +} +func (m *MsgJoinSwapShareAmountOut) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgJoinSwapShareAmountOut) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgJoinSwapShareAmountOut.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgJoinSwapShareAmountOut) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgJoinSwapShareAmountOut.Merge(m, src) +} +func (m *MsgJoinSwapShareAmountOut) XXX_Size() int { + return m.Size() +} +func (m *MsgJoinSwapShareAmountOut) XXX_DiscardUnknown() { + xxx_messageInfo_MsgJoinSwapShareAmountOut.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgJoinSwapShareAmountOut proto.InternalMessageInfo + +func (m *MsgJoinSwapShareAmountOut) GetSender() string { + if m != nil { + return m.Sender + } + return "" +} + +func (m *MsgJoinSwapShareAmountOut) GetPoolId() uint64 { + if m != nil { + return m.PoolId + } + return 0 +} + +func (m *MsgJoinSwapShareAmountOut) GetTokenInDenom() string { + if m != nil { + return m.TokenInDenom + } + return "" +} + +type MsgJoinSwapShareAmountOutResponse struct { + TokenInAmount cosmossdk_io_math.Int `protobuf:"bytes,1,opt,name=token_in_amount,json=tokenInAmount,proto3,customtype=cosmossdk.io/math.Int" json:"token_in_amount" yaml:"token_in_amount"` +} + +func (m *MsgJoinSwapShareAmountOutResponse) Reset() { *m = MsgJoinSwapShareAmountOutResponse{} } +func (m *MsgJoinSwapShareAmountOutResponse) String() string { return proto.CompactTextString(m) } +func (*MsgJoinSwapShareAmountOutResponse) ProtoMessage() {} +func (*MsgJoinSwapShareAmountOutResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_cfc8fd3ac7df3247, []int{11} +} +func (m *MsgJoinSwapShareAmountOutResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgJoinSwapShareAmountOutResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgJoinSwapShareAmountOutResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgJoinSwapShareAmountOutResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgJoinSwapShareAmountOutResponse.Merge(m, src) +} +func (m *MsgJoinSwapShareAmountOutResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgJoinSwapShareAmountOutResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgJoinSwapShareAmountOutResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgJoinSwapShareAmountOutResponse proto.InternalMessageInfo + +// ===================== MsgExitSwapShareAmountIn +type MsgExitSwapShareAmountIn struct { + Sender string `protobuf:"bytes,1,opt,name=sender,proto3" json:"sender,omitempty" yaml:"sender"` + PoolId uint64 `protobuf:"varint,2,opt,name=pool_id,json=poolId,proto3" json:"pool_id,omitempty" yaml:"pool_id"` + TokenOutDenom string `protobuf:"bytes,3,opt,name=token_out_denom,json=tokenOutDenom,proto3" json:"token_out_denom,omitempty" yaml:"token_out_denom"` + ShareInAmount cosmossdk_io_math.Int `protobuf:"bytes,4,opt,name=share_in_amount,json=shareInAmount,proto3,customtype=cosmossdk.io/math.Int" json:"share_in_amount" yaml:"share_in_amount"` + TokenOutMinAmount cosmossdk_io_math.Int `protobuf:"bytes,5,opt,name=token_out_min_amount,json=tokenOutMinAmount,proto3,customtype=cosmossdk.io/math.Int" json:"token_out_min_amount" yaml:"token_out_min_amount"` +} + +func (m *MsgExitSwapShareAmountIn) Reset() { *m = MsgExitSwapShareAmountIn{} } +func (m *MsgExitSwapShareAmountIn) String() string { return proto.CompactTextString(m) } +func (*MsgExitSwapShareAmountIn) ProtoMessage() {} +func (*MsgExitSwapShareAmountIn) Descriptor() ([]byte, []int) { + return fileDescriptor_cfc8fd3ac7df3247, []int{12} +} +func (m *MsgExitSwapShareAmountIn) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgExitSwapShareAmountIn) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgExitSwapShareAmountIn.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgExitSwapShareAmountIn) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgExitSwapShareAmountIn.Merge(m, src) +} +func (m *MsgExitSwapShareAmountIn) XXX_Size() int { + return m.Size() +} +func (m *MsgExitSwapShareAmountIn) XXX_DiscardUnknown() { + xxx_messageInfo_MsgExitSwapShareAmountIn.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgExitSwapShareAmountIn proto.InternalMessageInfo + +func (m *MsgExitSwapShareAmountIn) GetSender() string { + if m != nil { + return m.Sender + } + return "" +} + +func (m *MsgExitSwapShareAmountIn) GetPoolId() uint64 { + if m != nil { + return m.PoolId + } + return 0 +} + +func (m *MsgExitSwapShareAmountIn) GetTokenOutDenom() string { + if m != nil { + return m.TokenOutDenom + } + return "" +} + +type MsgExitSwapShareAmountInResponse struct { + TokenOutAmount cosmossdk_io_math.Int `protobuf:"bytes,1,opt,name=token_out_amount,json=tokenOutAmount,proto3,customtype=cosmossdk.io/math.Int" json:"token_out_amount" yaml:"token_out_amount"` +} + +func (m *MsgExitSwapShareAmountInResponse) Reset() { *m = MsgExitSwapShareAmountInResponse{} } +func (m *MsgExitSwapShareAmountInResponse) String() string { return proto.CompactTextString(m) } +func (*MsgExitSwapShareAmountInResponse) ProtoMessage() {} +func (*MsgExitSwapShareAmountInResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_cfc8fd3ac7df3247, []int{13} +} +func (m *MsgExitSwapShareAmountInResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgExitSwapShareAmountInResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgExitSwapShareAmountInResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgExitSwapShareAmountInResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgExitSwapShareAmountInResponse.Merge(m, src) +} +func (m *MsgExitSwapShareAmountInResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgExitSwapShareAmountInResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgExitSwapShareAmountInResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgExitSwapShareAmountInResponse proto.InternalMessageInfo + +// ===================== MsgExitSwapExternAmountOut +type MsgExitSwapExternAmountOut struct { + Sender string `protobuf:"bytes,1,opt,name=sender,proto3" json:"sender,omitempty" yaml:"sender"` + PoolId uint64 `protobuf:"varint,2,opt,name=pool_id,json=poolId,proto3" json:"pool_id,omitempty" yaml:"pool_id"` + TokenOut types.Coin `protobuf:"bytes,3,opt,name=token_out,json=tokenOut,proto3" json:"token_out" yaml:"token_out"` + ShareInMaxAmount cosmossdk_io_math.Int `protobuf:"bytes,4,opt,name=share_in_max_amount,json=shareInMaxAmount,proto3,customtype=cosmossdk.io/math.Int" json:"share_in_max_amount" yaml:"share_in_max_amount"` +} + +func (m *MsgExitSwapExternAmountOut) Reset() { *m = MsgExitSwapExternAmountOut{} } +func (m *MsgExitSwapExternAmountOut) String() string { return proto.CompactTextString(m) } +func (*MsgExitSwapExternAmountOut) ProtoMessage() {} +func (*MsgExitSwapExternAmountOut) Descriptor() ([]byte, []int) { + return fileDescriptor_cfc8fd3ac7df3247, []int{14} +} +func (m *MsgExitSwapExternAmountOut) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgExitSwapExternAmountOut) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgExitSwapExternAmountOut.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgExitSwapExternAmountOut) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgExitSwapExternAmountOut.Merge(m, src) +} +func (m *MsgExitSwapExternAmountOut) XXX_Size() int { + return m.Size() +} +func (m *MsgExitSwapExternAmountOut) XXX_DiscardUnknown() { + xxx_messageInfo_MsgExitSwapExternAmountOut.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgExitSwapExternAmountOut proto.InternalMessageInfo + +func (m *MsgExitSwapExternAmountOut) GetSender() string { + if m != nil { + return m.Sender + } + return "" +} + +func (m *MsgExitSwapExternAmountOut) GetPoolId() uint64 { + if m != nil { + return m.PoolId + } + return 0 +} + +func (m *MsgExitSwapExternAmountOut) GetTokenOut() types.Coin { + if m != nil { + return m.TokenOut + } + return types.Coin{} +} + +type MsgExitSwapExternAmountOutResponse struct { + ShareInAmount cosmossdk_io_math.Int `protobuf:"bytes,1,opt,name=share_in_amount,json=shareInAmount,proto3,customtype=cosmossdk.io/math.Int" json:"share_in_amount" yaml:"share_in_amount"` +} + +func (m *MsgExitSwapExternAmountOutResponse) Reset() { *m = MsgExitSwapExternAmountOutResponse{} } +func (m *MsgExitSwapExternAmountOutResponse) String() string { return proto.CompactTextString(m) } +func (*MsgExitSwapExternAmountOutResponse) ProtoMessage() {} +func (*MsgExitSwapExternAmountOutResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_cfc8fd3ac7df3247, []int{15} +} +func (m *MsgExitSwapExternAmountOutResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgExitSwapExternAmountOutResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgExitSwapExternAmountOutResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgExitSwapExternAmountOutResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgExitSwapExternAmountOutResponse.Merge(m, src) +} +func (m *MsgExitSwapExternAmountOutResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgExitSwapExternAmountOutResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgExitSwapExternAmountOutResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgExitSwapExternAmountOutResponse proto.InternalMessageInfo + +func init() { + proto.RegisterType((*MsgJoinPool)(nil), "osmosis.gamm.v1beta1.MsgJoinPool") + proto.RegisterType((*MsgJoinPoolResponse)(nil), "osmosis.gamm.v1beta1.MsgJoinPoolResponse") + proto.RegisterType((*MsgExitPool)(nil), "osmosis.gamm.v1beta1.MsgExitPool") + proto.RegisterType((*MsgExitPoolResponse)(nil), "osmosis.gamm.v1beta1.MsgExitPoolResponse") + proto.RegisterType((*MsgSwapExactAmountIn)(nil), "osmosis.gamm.v1beta1.MsgSwapExactAmountIn") + proto.RegisterType((*MsgSwapExactAmountInResponse)(nil), "osmosis.gamm.v1beta1.MsgSwapExactAmountInResponse") + proto.RegisterType((*MsgSwapExactAmountOut)(nil), "osmosis.gamm.v1beta1.MsgSwapExactAmountOut") + proto.RegisterType((*MsgSwapExactAmountOutResponse)(nil), "osmosis.gamm.v1beta1.MsgSwapExactAmountOutResponse") + proto.RegisterType((*MsgJoinSwapExternAmountIn)(nil), "osmosis.gamm.v1beta1.MsgJoinSwapExternAmountIn") + proto.RegisterType((*MsgJoinSwapExternAmountInResponse)(nil), "osmosis.gamm.v1beta1.MsgJoinSwapExternAmountInResponse") + proto.RegisterType((*MsgJoinSwapShareAmountOut)(nil), "osmosis.gamm.v1beta1.MsgJoinSwapShareAmountOut") + proto.RegisterType((*MsgJoinSwapShareAmountOutResponse)(nil), "osmosis.gamm.v1beta1.MsgJoinSwapShareAmountOutResponse") + proto.RegisterType((*MsgExitSwapShareAmountIn)(nil), "osmosis.gamm.v1beta1.MsgExitSwapShareAmountIn") + proto.RegisterType((*MsgExitSwapShareAmountInResponse)(nil), "osmosis.gamm.v1beta1.MsgExitSwapShareAmountInResponse") + proto.RegisterType((*MsgExitSwapExternAmountOut)(nil), "osmosis.gamm.v1beta1.MsgExitSwapExternAmountOut") + proto.RegisterType((*MsgExitSwapExternAmountOutResponse)(nil), "osmosis.gamm.v1beta1.MsgExitSwapExternAmountOutResponse") +} + +func init() { proto.RegisterFile("osmosis/gamm/v1beta1/tx.proto", fileDescriptor_cfc8fd3ac7df3247) } + +var fileDescriptor_cfc8fd3ac7df3247 = []byte{ + // 1208 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x58, 0x4d, 0x6f, 0xdb, 0x46, + 0x13, 0x36, 0x25, 0xc5, 0x76, 0xd6, 0xaf, 0xbf, 0x18, 0x7f, 0xc8, 0x74, 0x22, 0xd9, 0xfb, 0x16, + 0x89, 0x13, 0x47, 0x64, 0xa2, 0x00, 0x4d, 0xe0, 0x16, 0x28, 0xaa, 0x36, 0x07, 0x05, 0x15, 0x14, + 0x30, 0x97, 0xa0, 0x17, 0x81, 0xb2, 0x08, 0x99, 0x89, 0xb9, 0x2b, 0x68, 0x97, 0x8e, 0x72, 0x6a, + 0x11, 0x34, 0x2d, 0xd0, 0x53, 0x7f, 0x4a, 0xd1, 0x9e, 0xfa, 0x0f, 0xdc, 0x9e, 0x72, 0x2c, 0x7a, + 0x50, 0x5b, 0xfb, 0xd0, 0xbb, 0x80, 0x02, 0x3d, 0x16, 0x4b, 0x2e, 0x29, 0x92, 0x22, 0x4d, 0x49, + 0x95, 0x7c, 0x31, 0x2c, 0xee, 0xcc, 0xec, 0xcc, 0x3c, 0xcf, 0x3c, 0xbb, 0x24, 0xb8, 0x81, 0x89, + 0x89, 0x89, 0x41, 0x94, 0xa6, 0x66, 0x9a, 0xca, 0xc9, 0xfd, 0xba, 0x4e, 0xb5, 0xfb, 0x0a, 0xed, + 0xc8, 0xad, 0x36, 0xa6, 0x58, 0x5c, 0xe3, 0xcb, 0x32, 0x5b, 0x96, 0xf9, 0xb2, 0xb4, 0xd6, 0xc4, + 0x4d, 0x6c, 0x1b, 0x28, 0xec, 0x3f, 0xc7, 0x56, 0x5a, 0xd5, 0x4c, 0x03, 0x61, 0xc5, 0xfe, 0xcb, + 0x1f, 0xe5, 0x0e, 0x6d, 0x7f, 0xa5, 0xae, 0x11, 0xdd, 0x0b, 0x7e, 0x88, 0x0d, 0xc4, 0xd7, 0xef, + 0xba, 0xbb, 0xb7, 0x30, 0x3e, 0x36, 0x35, 0xa4, 0x35, 0xf5, 0xb6, 0x67, 0x47, 0x5e, 0x69, 0xad, + 0x5a, 0x1b, 0x5b, 0x54, 0x77, 0xac, 0xe1, 0xcf, 0x29, 0xb0, 0x50, 0x21, 0xcd, 0x27, 0xd8, 0x40, + 0x4f, 0x31, 0x3e, 0x16, 0x6f, 0x83, 0x59, 0xa2, 0xa3, 0x86, 0xde, 0xce, 0x0a, 0x3b, 0xc2, 0xde, + 0xd5, 0xd2, 0x6a, 0xaf, 0x9b, 0x5f, 0x7c, 0xad, 0x99, 0xc7, 0x07, 0xd0, 0x79, 0x0e, 0x55, 0x6e, + 0x20, 0xee, 0x83, 0x39, 0xb6, 0x45, 0xcd, 0x68, 0x64, 0x53, 0x3b, 0xc2, 0x5e, 0xa6, 0x24, 0xf6, + 0xba, 0xf9, 0x25, 0xc7, 0x96, 0x2f, 0x40, 0x75, 0x96, 0xfd, 0x57, 0x6e, 0x88, 0x1a, 0x58, 0x21, + 0x47, 0x5a, 0x5b, 0xaf, 0x61, 0x8b, 0xd6, 0x34, 0x13, 0x5b, 0x88, 0x66, 0xd3, 0xf6, 0x0e, 0x0f, + 0x4f, 0xbb, 0xf9, 0x99, 0xdf, 0xba, 0xf9, 0x75, 0xa7, 0x2e, 0xd2, 0x78, 0x29, 0x1b, 0x58, 0x31, + 0x35, 0x7a, 0x24, 0x97, 0x11, 0xed, 0x75, 0xf3, 0x1b, 0xbe, 0x90, 0x8e, 0x27, 0x0b, 0x02, 0xd5, + 0x25, 0x3b, 0x60, 0xd5, 0xa2, 0x1f, 0xdb, 0x0f, 0xc5, 0x3a, 0x58, 0xa4, 0xf8, 0xa5, 0x8e, 0x6a, + 0x06, 0xaa, 0x99, 0x5a, 0x87, 0x64, 0x33, 0x3b, 0xe9, 0xbd, 0x85, 0xe2, 0x96, 0xec, 0x04, 0x96, + 0x59, 0xc3, 0xdc, 0x76, 0xcb, 0x9f, 0x60, 0x03, 0x95, 0xfe, 0xcf, 0xb6, 0xee, 0x75, 0xf3, 0xdb, + 0xce, 0x0e, 0x7e, 0x6f, 0xbe, 0x13, 0x81, 0xea, 0x82, 0xfd, 0xb8, 0x8c, 0x2a, 0x5a, 0x87, 0x1c, + 0x6c, 0x7f, 0xfb, 0xd7, 0xf7, 0x77, 0x36, 0x02, 0xf8, 0xbe, 0xc0, 0x06, 0x2a, 0xb0, 0xe4, 0xe0, + 0xa9, 0x00, 0xae, 0xf9, 0x7a, 0xa9, 0xea, 0xa4, 0x85, 0x11, 0xd1, 0xc5, 0x7a, 0x44, 0xed, 0x4e, + 0x77, 0x1f, 0x25, 0xd5, 0xbe, 0xc9, 0x5b, 0x1f, 0x72, 0x1f, 0x2c, 0xbe, 0x02, 0xe6, 0xdd, 0xf4, + 0xb3, 0xa9, 0xa4, 0xba, 0x37, 0x79, 0xdd, 0xcb, 0xc1, 0xba, 0xa1, 0x3a, 0xc7, 0x6b, 0x85, 0xbf, + 0x38, 0xb4, 0x78, 0xdc, 0x31, 0xe8, 0x54, 0x69, 0x51, 0x03, 0xcb, 0x4e, 0x6d, 0x06, 0x1a, 0x8f, + 0x15, 0x21, 0x6f, 0xa8, 0x2e, 0xda, 0x4f, 0xca, 0x88, 0xf7, 0x45, 0x07, 0x4b, 0x4e, 0x79, 0xac, + 0x79, 0xa6, 0x81, 0x86, 0x60, 0xc5, 0x7b, 0xbc, 0x3b, 0xd7, 0xfd, 0xdd, 0xe1, 0xee, 0x7d, 0x5a, + 0xfc, 0xcf, 0x7e, 0x5e, 0xb5, 0x68, 0xc5, 0x40, 0x91, 0xbc, 0xd0, 0x3b, 0x06, 0x75, 0x78, 0xd1, + 0xb4, 0x69, 0xe1, 0xf6, 0xd2, 0xa3, 0xc5, 0x53, 0x70, 0xd5, 0x8b, 0x9d, 0x15, 0x92, 0xb2, 0xca, + 0xf2, 0xac, 0x56, 0x42, 0x59, 0x41, 0x75, 0xde, 0xcd, 0x04, 0xfe, 0x9d, 0x02, 0x6b, 0x15, 0xd2, + 0x7c, 0xf6, 0x4a, 0x6b, 0x3d, 0xee, 0x68, 0x87, 0x9c, 0x1b, 0x65, 0x34, 0x0a, 0x7c, 0x9f, 0x81, + 0x59, 0x5b, 0x1f, 0x08, 0xa7, 0x91, 0x2c, 0xbb, 0x72, 0xe5, 0xd3, 0x13, 0x2f, 0x35, 0xb6, 0x95, + 0xbb, 0x8b, 0xca, 0xdc, 0x4a, 0x19, 0x96, 0xa7, 0xca, 0x63, 0x04, 0x68, 0xc9, 0x80, 0xfd, 0x6f, + 0xb4, 0x14, 0x4d, 0xb0, 0x16, 0x05, 0x47, 0x36, 0x63, 0x57, 0xf5, 0x61, 0x12, 0x67, 0xb6, 0xe3, + 0x11, 0x85, 0xea, 0xaa, 0x0f, 0x50, 0xa7, 0xa4, 0x83, 0x9b, 0x0c, 0xd5, 0xdd, 0x00, 0xaa, 0x4c, + 0x40, 0x0b, 0x3a, 0x6b, 0x6e, 0xc1, 0x71, 0x2c, 0x18, 0x08, 0xbe, 0x11, 0xc0, 0xf5, 0xa8, 0xbe, + 0xfb, 0x15, 0xa0, 0xbf, 0xe9, 0x58, 0x0a, 0x10, 0x76, 0x87, 0xea, 0x92, 0x9b, 0xaf, 0xb3, 0x1b, + 0xfc, 0x27, 0x05, 0xd6, 0x07, 0x93, 0xa8, 0x5a, 0x74, 0x14, 0xf4, 0x2b, 0x21, 0xf4, 0x95, 0x21, + 0xd1, 0xaf, 0x5a, 0x34, 0x0a, 0xfe, 0x17, 0xe0, 0x5a, 0x84, 0xa8, 0xf2, 0x11, 0xff, 0x20, 0xa9, + 0x74, 0x29, 0x56, 0x96, 0xa1, 0xba, 0xd2, 0x57, 0x65, 0x3e, 0xe9, 0x81, 0x71, 0xca, 0x24, 0x71, + 0x6d, 0x88, 0x71, 0x3a, 0xb8, 0xc5, 0xe0, 0x87, 0x09, 0xf0, 0x33, 0x9f, 0x2f, 0x05, 0x70, 0x23, + 0xb2, 0xf5, 0x1e, 0x01, 0x6a, 0x60, 0xd9, 0x2b, 0x23, 0x80, 0xff, 0xb0, 0x3a, 0x17, 0xf2, 0x86, + 0xea, 0x22, 0x6f, 0x00, 0x47, 0xff, 0xf7, 0x14, 0xd8, 0xe2, 0x67, 0x8f, 0x93, 0x06, 0xd5, 0xdb, + 0x68, 0x9c, 0xf9, 0x1f, 0x49, 0xbe, 0x27, 0x3f, 0xde, 0xfd, 0x93, 0x6e, 0xec, 0xf1, 0x8e, 0x0a, + 0x01, 0xd5, 0x55, 0xf7, 0xc0, 0xec, 0x8f, 0xf7, 0x5d, 0x86, 0xef, 0xad, 0xc1, 0xc3, 0x9c, 0x83, + 0xcc, 0x3a, 0xe8, 0x1b, 0xf2, 0x6f, 0x04, 0xb0, 0x1b, 0xdb, 0xe1, 0xcb, 0x3c, 0xeb, 0xe1, 0x0f, + 0xe9, 0x00, 0xd6, 0xcf, 0xd8, 0xea, 0x58, 0xd3, 0x3e, 0x12, 0xd6, 0x1f, 0xb9, 0x27, 0xa9, 0x81, + 0x6a, 0x0d, 0x1d, 0x61, 0x93, 0x8f, 0xf1, 0x56, 0xaf, 0x9b, 0x5f, 0x0f, 0x91, 0xd4, 0x5e, 0x77, + 0xcf, 0xc8, 0x32, 0xfa, 0x94, 0xfd, 0x8c, 0x6c, 0x4d, 0x66, 0xc2, 0xd7, 0xa0, 0x18, 0xc1, 0xb9, + 0x32, 0x05, 0xc1, 0xb9, 0x98, 0x3e, 0x76, 0x5e, 0x7e, 0x8d, 0xf8, 0x2a, 0x48, 0x9f, 0x20, 0x68, + 0x97, 0xa7, 0x13, 0x3f, 0xa6, 0x41, 0x96, 0x5f, 0x46, 0x42, 0x69, 0x4c, 0x51, 0x26, 0x4a, 0x6e, + 0x55, 0x0c, 0x3a, 0x3f, 0x77, 0xa4, 0x70, 0xe2, 0x9e, 0x81, 0x9b, 0x78, 0xd5, 0xa2, 0x0e, 0x7b, + 0x22, 0x6e, 0x8a, 0x99, 0x89, 0xde, 0x14, 0xe3, 0xee, 0x16, 0x57, 0xa6, 0x73, 0xb7, 0xd8, 0x67, + 0xec, 0xb9, 0x39, 0x78, 0x63, 0x1c, 0x64, 0x8f, 0x81, 0xe0, 0xd7, 0x02, 0xd8, 0x89, 0x43, 0xed, + 0x52, 0x2f, 0x19, 0x7f, 0xa6, 0x80, 0xe4, 0x4b, 0xc4, 0x2f, 0x82, 0xd3, 0xd4, 0x9e, 0xc0, 0xd9, + 0x9e, 0x9e, 0xc0, 0xd9, 0xce, 0x84, 0xc2, 0x23, 0x84, 0x4f, 0x28, 0x32, 0x23, 0x09, 0x45, 0x44, + 0x04, 0xa8, 0xae, 0x70, 0x5a, 0xf5, 0x85, 0xa2, 0xc0, 0xa0, 0xde, 0x8b, 0x81, 0x3a, 0x78, 0xce, + 0xb0, 0x2c, 0xdf, 0x0a, 0x00, 0xc6, 0xf7, 0xd8, 0x2f, 0x15, 0xe1, 0x81, 0x10, 0x26, 0x39, 0x10, + 0xc5, 0x9f, 0xe6, 0x40, 0xba, 0x42, 0x9a, 0xe2, 0x73, 0x30, 0xef, 0x7d, 0x1e, 0xd8, 0x95, 0xa3, + 0x3e, 0x5e, 0xc8, 0xbe, 0xb7, 0x5e, 0xe9, 0x76, 0xa2, 0x89, 0x57, 0xc2, 0x73, 0x30, 0xef, 0xbd, + 0x61, 0xc6, 0x47, 0x76, 0x4d, 0x2e, 0x88, 0x3c, 0xf0, 0x6e, 0x45, 0xc0, 0xea, 0xe0, 0x5b, 0xd0, + 0x9d, 0x58, 0xff, 0x01, 0x5b, 0xa9, 0x38, 0xbc, 0xad, 0xb7, 0xe9, 0x09, 0x10, 0x23, 0x6e, 0xdf, + 0xfb, 0xc3, 0x46, 0xaa, 0x5a, 0x54, 0x7a, 0x30, 0x82, 0xb1, 0xb7, 0xef, 0x1b, 0x01, 0x6c, 0xc4, + 0x5c, 0xfc, 0x94, 0x0b, 0xc1, 0x18, 0x74, 0x90, 0x1e, 0x8e, 0xe8, 0x10, 0x99, 0x44, 0xe8, 0x46, + 0x92, 0x9c, 0x44, 0xd0, 0x61, 0x88, 0x24, 0x62, 0x8e, 0xcf, 0xb7, 0x02, 0xd8, 0x8c, 0xd3, 0xa6, + 0x7b, 0x17, 0xb2, 0x27, 0xc2, 0x43, 0x7a, 0x34, 0xaa, 0x87, 0x97, 0xc7, 0x17, 0x60, 0x3d, 0xfa, + 0x84, 0x95, 0x13, 0x43, 0x06, 0xec, 0xa5, 0xf7, 0x47, 0xb3, 0x77, 0x13, 0x28, 0x3d, 0x39, 0x3d, + 0xcb, 0x09, 0xef, 0xce, 0x72, 0xc2, 0x1f, 0x67, 0x39, 0xe1, 0xbb, 0xf3, 0xdc, 0xcc, 0xbb, 0xf3, + 0xdc, 0xcc, 0xaf, 0xe7, 0xb9, 0x99, 0xcf, 0xef, 0x35, 0x0d, 0x7a, 0x64, 0xd5, 0xe5, 0x43, 0x6c, + 0x2a, 0x3c, 0x76, 0xe1, 0x58, 0xab, 0x13, 0xf7, 0x87, 0x72, 0x52, 0x2c, 0x2a, 0x1d, 0x47, 0xa5, + 0xe8, 0xeb, 0x96, 0x4e, 0xea, 0xb3, 0xf6, 0x97, 0xc2, 0x07, 0xff, 0x06, 0x00, 0x00, 0xff, 0xff, + 0xa0, 0x55, 0x2e, 0x69, 0xd7, 0x14, 0x00, 0x00, +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// MsgClient is the client API for Msg service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type MsgClient interface { + JoinPool(ctx context.Context, in *MsgJoinPool, opts ...grpc.CallOption) (*MsgJoinPoolResponse, error) + ExitPool(ctx context.Context, in *MsgExitPool, opts ...grpc.CallOption) (*MsgExitPoolResponse, error) + SwapExactAmountIn(ctx context.Context, in *MsgSwapExactAmountIn, opts ...grpc.CallOption) (*MsgSwapExactAmountInResponse, error) + SwapExactAmountOut(ctx context.Context, in *MsgSwapExactAmountOut, opts ...grpc.CallOption) (*MsgSwapExactAmountOutResponse, error) + JoinSwapExternAmountIn(ctx context.Context, in *MsgJoinSwapExternAmountIn, opts ...grpc.CallOption) (*MsgJoinSwapExternAmountInResponse, error) + JoinSwapShareAmountOut(ctx context.Context, in *MsgJoinSwapShareAmountOut, opts ...grpc.CallOption) (*MsgJoinSwapShareAmountOutResponse, error) + ExitSwapExternAmountOut(ctx context.Context, in *MsgExitSwapExternAmountOut, opts ...grpc.CallOption) (*MsgExitSwapExternAmountOutResponse, error) + ExitSwapShareAmountIn(ctx context.Context, in *MsgExitSwapShareAmountIn, opts ...grpc.CallOption) (*MsgExitSwapShareAmountInResponse, error) +} + +type msgClient struct { + cc grpc1.ClientConn +} + +func NewMsgClient(cc grpc1.ClientConn) MsgClient { + return &msgClient{cc} +} + +func (c *msgClient) JoinPool(ctx context.Context, in *MsgJoinPool, opts ...grpc.CallOption) (*MsgJoinPoolResponse, error) { + out := new(MsgJoinPoolResponse) + err := c.cc.Invoke(ctx, "/osmosis.gamm.v1beta1.Msg/JoinPool", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) ExitPool(ctx context.Context, in *MsgExitPool, opts ...grpc.CallOption) (*MsgExitPoolResponse, error) { + out := new(MsgExitPoolResponse) + err := c.cc.Invoke(ctx, "/osmosis.gamm.v1beta1.Msg/ExitPool", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) SwapExactAmountIn(ctx context.Context, in *MsgSwapExactAmountIn, opts ...grpc.CallOption) (*MsgSwapExactAmountInResponse, error) { + out := new(MsgSwapExactAmountInResponse) + err := c.cc.Invoke(ctx, "/osmosis.gamm.v1beta1.Msg/SwapExactAmountIn", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) SwapExactAmountOut(ctx context.Context, in *MsgSwapExactAmountOut, opts ...grpc.CallOption) (*MsgSwapExactAmountOutResponse, error) { + out := new(MsgSwapExactAmountOutResponse) + err := c.cc.Invoke(ctx, "/osmosis.gamm.v1beta1.Msg/SwapExactAmountOut", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) JoinSwapExternAmountIn(ctx context.Context, in *MsgJoinSwapExternAmountIn, opts ...grpc.CallOption) (*MsgJoinSwapExternAmountInResponse, error) { + out := new(MsgJoinSwapExternAmountInResponse) + err := c.cc.Invoke(ctx, "/osmosis.gamm.v1beta1.Msg/JoinSwapExternAmountIn", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) JoinSwapShareAmountOut(ctx context.Context, in *MsgJoinSwapShareAmountOut, opts ...grpc.CallOption) (*MsgJoinSwapShareAmountOutResponse, error) { + out := new(MsgJoinSwapShareAmountOutResponse) + err := c.cc.Invoke(ctx, "/osmosis.gamm.v1beta1.Msg/JoinSwapShareAmountOut", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) ExitSwapExternAmountOut(ctx context.Context, in *MsgExitSwapExternAmountOut, opts ...grpc.CallOption) (*MsgExitSwapExternAmountOutResponse, error) { + out := new(MsgExitSwapExternAmountOutResponse) + err := c.cc.Invoke(ctx, "/osmosis.gamm.v1beta1.Msg/ExitSwapExternAmountOut", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) ExitSwapShareAmountIn(ctx context.Context, in *MsgExitSwapShareAmountIn, opts ...grpc.CallOption) (*MsgExitSwapShareAmountInResponse, error) { + out := new(MsgExitSwapShareAmountInResponse) + err := c.cc.Invoke(ctx, "/osmosis.gamm.v1beta1.Msg/ExitSwapShareAmountIn", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// MsgServer is the server API for Msg service. +type MsgServer interface { + JoinPool(context.Context, *MsgJoinPool) (*MsgJoinPoolResponse, error) + ExitPool(context.Context, *MsgExitPool) (*MsgExitPoolResponse, error) + SwapExactAmountIn(context.Context, *MsgSwapExactAmountIn) (*MsgSwapExactAmountInResponse, error) + SwapExactAmountOut(context.Context, *MsgSwapExactAmountOut) (*MsgSwapExactAmountOutResponse, error) + JoinSwapExternAmountIn(context.Context, *MsgJoinSwapExternAmountIn) (*MsgJoinSwapExternAmountInResponse, error) + JoinSwapShareAmountOut(context.Context, *MsgJoinSwapShareAmountOut) (*MsgJoinSwapShareAmountOutResponse, error) + ExitSwapExternAmountOut(context.Context, *MsgExitSwapExternAmountOut) (*MsgExitSwapExternAmountOutResponse, error) + ExitSwapShareAmountIn(context.Context, *MsgExitSwapShareAmountIn) (*MsgExitSwapShareAmountInResponse, error) +} + +// UnimplementedMsgServer can be embedded to have forward compatible implementations. +type UnimplementedMsgServer struct { +} + +func (*UnimplementedMsgServer) JoinPool(ctx context.Context, req *MsgJoinPool) (*MsgJoinPoolResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method JoinPool not implemented") +} +func (*UnimplementedMsgServer) ExitPool(ctx context.Context, req *MsgExitPool) (*MsgExitPoolResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ExitPool not implemented") +} +func (*UnimplementedMsgServer) SwapExactAmountIn(ctx context.Context, req *MsgSwapExactAmountIn) (*MsgSwapExactAmountInResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method SwapExactAmountIn not implemented") +} +func (*UnimplementedMsgServer) SwapExactAmountOut(ctx context.Context, req *MsgSwapExactAmountOut) (*MsgSwapExactAmountOutResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method SwapExactAmountOut not implemented") +} +func (*UnimplementedMsgServer) JoinSwapExternAmountIn(ctx context.Context, req *MsgJoinSwapExternAmountIn) (*MsgJoinSwapExternAmountInResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method JoinSwapExternAmountIn not implemented") +} +func (*UnimplementedMsgServer) JoinSwapShareAmountOut(ctx context.Context, req *MsgJoinSwapShareAmountOut) (*MsgJoinSwapShareAmountOutResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method JoinSwapShareAmountOut not implemented") +} +func (*UnimplementedMsgServer) ExitSwapExternAmountOut(ctx context.Context, req *MsgExitSwapExternAmountOut) (*MsgExitSwapExternAmountOutResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ExitSwapExternAmountOut not implemented") +} +func (*UnimplementedMsgServer) ExitSwapShareAmountIn(ctx context.Context, req *MsgExitSwapShareAmountIn) (*MsgExitSwapShareAmountInResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ExitSwapShareAmountIn not implemented") +} + +func RegisterMsgServer(s grpc1.Server, srv MsgServer) { + s.RegisterService(&_Msg_serviceDesc, srv) +} + +func _Msg_JoinPool_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgJoinPool) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).JoinPool(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/osmosis.gamm.v1beta1.Msg/JoinPool", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).JoinPool(ctx, req.(*MsgJoinPool)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_ExitPool_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgExitPool) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).ExitPool(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/osmosis.gamm.v1beta1.Msg/ExitPool", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).ExitPool(ctx, req.(*MsgExitPool)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_SwapExactAmountIn_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgSwapExactAmountIn) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).SwapExactAmountIn(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/osmosis.gamm.v1beta1.Msg/SwapExactAmountIn", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).SwapExactAmountIn(ctx, req.(*MsgSwapExactAmountIn)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_SwapExactAmountOut_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgSwapExactAmountOut) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).SwapExactAmountOut(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/osmosis.gamm.v1beta1.Msg/SwapExactAmountOut", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).SwapExactAmountOut(ctx, req.(*MsgSwapExactAmountOut)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_JoinSwapExternAmountIn_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgJoinSwapExternAmountIn) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).JoinSwapExternAmountIn(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/osmosis.gamm.v1beta1.Msg/JoinSwapExternAmountIn", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).JoinSwapExternAmountIn(ctx, req.(*MsgJoinSwapExternAmountIn)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_JoinSwapShareAmountOut_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgJoinSwapShareAmountOut) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).JoinSwapShareAmountOut(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/osmosis.gamm.v1beta1.Msg/JoinSwapShareAmountOut", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).JoinSwapShareAmountOut(ctx, req.(*MsgJoinSwapShareAmountOut)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_ExitSwapExternAmountOut_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgExitSwapExternAmountOut) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).ExitSwapExternAmountOut(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/osmosis.gamm.v1beta1.Msg/ExitSwapExternAmountOut", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).ExitSwapExternAmountOut(ctx, req.(*MsgExitSwapExternAmountOut)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_ExitSwapShareAmountIn_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgExitSwapShareAmountIn) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).ExitSwapShareAmountIn(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/osmosis.gamm.v1beta1.Msg/ExitSwapShareAmountIn", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).ExitSwapShareAmountIn(ctx, req.(*MsgExitSwapShareAmountIn)) + } + return interceptor(ctx, in, info, handler) +} + +var _Msg_serviceDesc = grpc.ServiceDesc{ + ServiceName: "osmosis.gamm.v1beta1.Msg", + HandlerType: (*MsgServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "JoinPool", + Handler: _Msg_JoinPool_Handler, + }, + { + MethodName: "ExitPool", + Handler: _Msg_ExitPool_Handler, + }, + { + MethodName: "SwapExactAmountIn", + Handler: _Msg_SwapExactAmountIn_Handler, + }, + { + MethodName: "SwapExactAmountOut", + Handler: _Msg_SwapExactAmountOut_Handler, + }, + { + MethodName: "JoinSwapExternAmountIn", + Handler: _Msg_JoinSwapExternAmountIn_Handler, + }, + { + MethodName: "JoinSwapShareAmountOut", + Handler: _Msg_JoinSwapShareAmountOut_Handler, + }, + { + MethodName: "ExitSwapExternAmountOut", + Handler: _Msg_ExitSwapExternAmountOut_Handler, + }, + { + MethodName: "ExitSwapShareAmountIn", + Handler: _Msg_ExitSwapShareAmountIn_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "osmosis/gamm/v1beta1/tx.proto", +} + +func (m *MsgJoinPool) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgJoinPool) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgJoinPool) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.TokenInMaxs) > 0 { + for iNdEx := len(m.TokenInMaxs) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.TokenInMaxs[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + } + { + size := m.ShareOutAmount.Size() + i -= size + if _, err := m.ShareOutAmount.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + if m.PoolId != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.PoolId)) + i-- + dAtA[i] = 0x10 + } + if len(m.Sender) > 0 { + i -= len(m.Sender) + copy(dAtA[i:], m.Sender) + i = encodeVarintTx(dAtA, i, uint64(len(m.Sender))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgJoinPoolResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgJoinPoolResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgJoinPoolResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.TokenIn) > 0 { + for iNdEx := len(m.TokenIn) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.TokenIn[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + { + size := m.ShareOutAmount.Size() + i -= size + if _, err := m.ShareOutAmount.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *MsgExitPool) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgExitPool) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgExitPool) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.TokenOutMins) > 0 { + for iNdEx := len(m.TokenOutMins) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.TokenOutMins[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + } + { + size := m.ShareInAmount.Size() + i -= size + if _, err := m.ShareInAmount.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + if m.PoolId != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.PoolId)) + i-- + dAtA[i] = 0x10 + } + if len(m.Sender) > 0 { + i -= len(m.Sender) + copy(dAtA[i:], m.Sender) + i = encodeVarintTx(dAtA, i, uint64(len(m.Sender))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgExitPoolResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgExitPoolResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgExitPoolResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.TokenOut) > 0 { + for iNdEx := len(m.TokenOut) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.TokenOut[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *MsgSwapExactAmountIn) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgSwapExactAmountIn) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgSwapExactAmountIn) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size := m.TokenOutMinAmount.Size() + i -= size + if _, err := m.TokenOutMinAmount.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + { + size, err := m.TokenIn.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + if len(m.Routes) > 0 { + for iNdEx := len(m.Routes) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Routes[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if len(m.Sender) > 0 { + i -= len(m.Sender) + copy(dAtA[i:], m.Sender) + i = encodeVarintTx(dAtA, i, uint64(len(m.Sender))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgSwapExactAmountInResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgSwapExactAmountInResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgSwapExactAmountInResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size := m.TokenOutAmount.Size() + i -= size + if _, err := m.TokenOutAmount.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *MsgSwapExactAmountOut) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgSwapExactAmountOut) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgSwapExactAmountOut) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.TokenOut.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + { + size := m.TokenInMaxAmount.Size() + i -= size + if _, err := m.TokenInMaxAmount.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + if len(m.Routes) > 0 { + for iNdEx := len(m.Routes) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Routes[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if len(m.Sender) > 0 { + i -= len(m.Sender) + copy(dAtA[i:], m.Sender) + i = encodeVarintTx(dAtA, i, uint64(len(m.Sender))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgSwapExactAmountOutResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgSwapExactAmountOutResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgSwapExactAmountOutResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size := m.TokenInAmount.Size() + i -= size + if _, err := m.TokenInAmount.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *MsgJoinSwapExternAmountIn) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgJoinSwapExternAmountIn) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgJoinSwapExternAmountIn) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size := m.ShareOutMinAmount.Size() + i -= size + if _, err := m.ShareOutMinAmount.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + { + size, err := m.TokenIn.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + if m.PoolId != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.PoolId)) + i-- + dAtA[i] = 0x10 + } + if len(m.Sender) > 0 { + i -= len(m.Sender) + copy(dAtA[i:], m.Sender) + i = encodeVarintTx(dAtA, i, uint64(len(m.Sender))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgJoinSwapExternAmountInResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgJoinSwapExternAmountInResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgJoinSwapExternAmountInResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size := m.ShareOutAmount.Size() + i -= size + if _, err := m.ShareOutAmount.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *MsgJoinSwapShareAmountOut) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgJoinSwapShareAmountOut) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgJoinSwapShareAmountOut) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size := m.TokenInMaxAmount.Size() + i -= size + if _, err := m.TokenInMaxAmount.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + { + size := m.ShareOutAmount.Size() + i -= size + if _, err := m.ShareOutAmount.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + if len(m.TokenInDenom) > 0 { + i -= len(m.TokenInDenom) + copy(dAtA[i:], m.TokenInDenom) + i = encodeVarintTx(dAtA, i, uint64(len(m.TokenInDenom))) + i-- + dAtA[i] = 0x1a + } + if m.PoolId != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.PoolId)) + i-- + dAtA[i] = 0x10 + } + if len(m.Sender) > 0 { + i -= len(m.Sender) + copy(dAtA[i:], m.Sender) + i = encodeVarintTx(dAtA, i, uint64(len(m.Sender))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgJoinSwapShareAmountOutResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgJoinSwapShareAmountOutResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgJoinSwapShareAmountOutResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size := m.TokenInAmount.Size() + i -= size + if _, err := m.TokenInAmount.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *MsgExitSwapShareAmountIn) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgExitSwapShareAmountIn) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgExitSwapShareAmountIn) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size := m.TokenOutMinAmount.Size() + i -= size + if _, err := m.TokenOutMinAmount.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + { + size := m.ShareInAmount.Size() + i -= size + if _, err := m.ShareInAmount.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + if len(m.TokenOutDenom) > 0 { + i -= len(m.TokenOutDenom) + copy(dAtA[i:], m.TokenOutDenom) + i = encodeVarintTx(dAtA, i, uint64(len(m.TokenOutDenom))) + i-- + dAtA[i] = 0x1a + } + if m.PoolId != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.PoolId)) + i-- + dAtA[i] = 0x10 + } + if len(m.Sender) > 0 { + i -= len(m.Sender) + copy(dAtA[i:], m.Sender) + i = encodeVarintTx(dAtA, i, uint64(len(m.Sender))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgExitSwapShareAmountInResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgExitSwapShareAmountInResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgExitSwapShareAmountInResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size := m.TokenOutAmount.Size() + i -= size + if _, err := m.TokenOutAmount.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *MsgExitSwapExternAmountOut) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgExitSwapExternAmountOut) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgExitSwapExternAmountOut) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size := m.ShareInMaxAmount.Size() + i -= size + if _, err := m.ShareInMaxAmount.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + { + size, err := m.TokenOut.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + if m.PoolId != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.PoolId)) + i-- + dAtA[i] = 0x10 + } + if len(m.Sender) > 0 { + i -= len(m.Sender) + copy(dAtA[i:], m.Sender) + i = encodeVarintTx(dAtA, i, uint64(len(m.Sender))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgExitSwapExternAmountOutResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgExitSwapExternAmountOutResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgExitSwapExternAmountOutResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size := m.ShareInAmount.Size() + i -= size + if _, err := m.ShareInAmount.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func encodeVarintTx(dAtA []byte, offset int, v uint64) int { + offset -= sovTx(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *MsgJoinPool) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Sender) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if m.PoolId != 0 { + n += 1 + sovTx(uint64(m.PoolId)) + } + l = m.ShareOutAmount.Size() + n += 1 + l + sovTx(uint64(l)) + if len(m.TokenInMaxs) > 0 { + for _, e := range m.TokenInMaxs { + l = e.Size() + n += 1 + l + sovTx(uint64(l)) + } + } + return n +} + +func (m *MsgJoinPoolResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.ShareOutAmount.Size() + n += 1 + l + sovTx(uint64(l)) + if len(m.TokenIn) > 0 { + for _, e := range m.TokenIn { + l = e.Size() + n += 1 + l + sovTx(uint64(l)) + } + } + return n +} + +func (m *MsgExitPool) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Sender) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if m.PoolId != 0 { + n += 1 + sovTx(uint64(m.PoolId)) + } + l = m.ShareInAmount.Size() + n += 1 + l + sovTx(uint64(l)) + if len(m.TokenOutMins) > 0 { + for _, e := range m.TokenOutMins { + l = e.Size() + n += 1 + l + sovTx(uint64(l)) + } + } + return n +} + +func (m *MsgExitPoolResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.TokenOut) > 0 { + for _, e := range m.TokenOut { + l = e.Size() + n += 1 + l + sovTx(uint64(l)) + } + } + return n +} + +func (m *MsgSwapExactAmountIn) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Sender) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if len(m.Routes) > 0 { + for _, e := range m.Routes { + l = e.Size() + n += 1 + l + sovTx(uint64(l)) + } + } + l = m.TokenIn.Size() + n += 1 + l + sovTx(uint64(l)) + l = m.TokenOutMinAmount.Size() + n += 1 + l + sovTx(uint64(l)) + return n +} + +func (m *MsgSwapExactAmountInResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.TokenOutAmount.Size() + n += 1 + l + sovTx(uint64(l)) + return n +} + +func (m *MsgSwapExactAmountOut) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Sender) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if len(m.Routes) > 0 { + for _, e := range m.Routes { + l = e.Size() + n += 1 + l + sovTx(uint64(l)) + } + } + l = m.TokenInMaxAmount.Size() + n += 1 + l + sovTx(uint64(l)) + l = m.TokenOut.Size() + n += 1 + l + sovTx(uint64(l)) + return n +} + +func (m *MsgSwapExactAmountOutResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.TokenInAmount.Size() + n += 1 + l + sovTx(uint64(l)) + return n +} + +func (m *MsgJoinSwapExternAmountIn) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Sender) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if m.PoolId != 0 { + n += 1 + sovTx(uint64(m.PoolId)) + } + l = m.TokenIn.Size() + n += 1 + l + sovTx(uint64(l)) + l = m.ShareOutMinAmount.Size() + n += 1 + l + sovTx(uint64(l)) + return n +} + +func (m *MsgJoinSwapExternAmountInResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.ShareOutAmount.Size() + n += 1 + l + sovTx(uint64(l)) + return n +} + +func (m *MsgJoinSwapShareAmountOut) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Sender) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if m.PoolId != 0 { + n += 1 + sovTx(uint64(m.PoolId)) + } + l = len(m.TokenInDenom) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = m.ShareOutAmount.Size() + n += 1 + l + sovTx(uint64(l)) + l = m.TokenInMaxAmount.Size() + n += 1 + l + sovTx(uint64(l)) + return n +} + +func (m *MsgJoinSwapShareAmountOutResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.TokenInAmount.Size() + n += 1 + l + sovTx(uint64(l)) + return n +} + +func (m *MsgExitSwapShareAmountIn) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Sender) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if m.PoolId != 0 { + n += 1 + sovTx(uint64(m.PoolId)) + } + l = len(m.TokenOutDenom) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = m.ShareInAmount.Size() + n += 1 + l + sovTx(uint64(l)) + l = m.TokenOutMinAmount.Size() + n += 1 + l + sovTx(uint64(l)) + return n +} + +func (m *MsgExitSwapShareAmountInResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.TokenOutAmount.Size() + n += 1 + l + sovTx(uint64(l)) + return n +} + +func (m *MsgExitSwapExternAmountOut) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Sender) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if m.PoolId != 0 { + n += 1 + sovTx(uint64(m.PoolId)) + } + l = m.TokenOut.Size() + n += 1 + l + sovTx(uint64(l)) + l = m.ShareInMaxAmount.Size() + n += 1 + l + sovTx(uint64(l)) + return n +} + +func (m *MsgExitSwapExternAmountOutResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.ShareInAmount.Size() + n += 1 + l + sovTx(uint64(l)) + return n +} + +func sovTx(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozTx(x uint64) (n int) { + return sovTx(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *MsgJoinPool) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgJoinPool: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgJoinPool: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Sender", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Sender = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field PoolId", wireType) + } + m.PoolId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.PoolId |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ShareOutAmount", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.ShareOutAmount.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TokenInMaxs", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.TokenInMaxs = append(m.TokenInMaxs, types.Coin{}) + if err := m.TokenInMaxs[len(m.TokenInMaxs)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgJoinPoolResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgJoinPoolResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgJoinPoolResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ShareOutAmount", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.ShareOutAmount.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TokenIn", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.TokenIn = append(m.TokenIn, types.Coin{}) + if err := m.TokenIn[len(m.TokenIn)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgExitPool) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgExitPool: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgExitPool: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Sender", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Sender = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field PoolId", wireType) + } + m.PoolId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.PoolId |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ShareInAmount", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.ShareInAmount.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TokenOutMins", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.TokenOutMins = append(m.TokenOutMins, types.Coin{}) + if err := m.TokenOutMins[len(m.TokenOutMins)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgExitPoolResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgExitPoolResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgExitPoolResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TokenOut", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.TokenOut = append(m.TokenOut, types.Coin{}) + if err := m.TokenOut[len(m.TokenOut)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgSwapExactAmountIn) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgSwapExactAmountIn: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgSwapExactAmountIn: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Sender", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Sender = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Routes", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Routes = append(m.Routes, types1.SwapAmountInRoute{}) + if err := m.Routes[len(m.Routes)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TokenIn", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.TokenIn.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TokenOutMinAmount", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.TokenOutMinAmount.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgSwapExactAmountInResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgSwapExactAmountInResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgSwapExactAmountInResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TokenOutAmount", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.TokenOutAmount.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgSwapExactAmountOut) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgSwapExactAmountOut: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgSwapExactAmountOut: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Sender", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Sender = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Routes", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Routes = append(m.Routes, types1.SwapAmountOutRoute{}) + if err := m.Routes[len(m.Routes)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TokenInMaxAmount", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.TokenInMaxAmount.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TokenOut", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.TokenOut.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgSwapExactAmountOutResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgSwapExactAmountOutResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgSwapExactAmountOutResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TokenInAmount", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.TokenInAmount.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgJoinSwapExternAmountIn) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgJoinSwapExternAmountIn: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgJoinSwapExternAmountIn: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Sender", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Sender = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field PoolId", wireType) + } + m.PoolId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.PoolId |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TokenIn", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.TokenIn.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ShareOutMinAmount", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.ShareOutMinAmount.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgJoinSwapExternAmountInResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgJoinSwapExternAmountInResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgJoinSwapExternAmountInResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ShareOutAmount", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.ShareOutAmount.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgJoinSwapShareAmountOut) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgJoinSwapShareAmountOut: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgJoinSwapShareAmountOut: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Sender", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Sender = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field PoolId", wireType) + } + m.PoolId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.PoolId |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TokenInDenom", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.TokenInDenom = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ShareOutAmount", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.ShareOutAmount.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TokenInMaxAmount", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.TokenInMaxAmount.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgJoinSwapShareAmountOutResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgJoinSwapShareAmountOutResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgJoinSwapShareAmountOutResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TokenInAmount", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.TokenInAmount.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgExitSwapShareAmountIn) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgExitSwapShareAmountIn: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgExitSwapShareAmountIn: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Sender", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Sender = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field PoolId", wireType) + } + m.PoolId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.PoolId |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TokenOutDenom", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.TokenOutDenom = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ShareInAmount", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.ShareInAmount.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TokenOutMinAmount", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.TokenOutMinAmount.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgExitSwapShareAmountInResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgExitSwapShareAmountInResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgExitSwapShareAmountInResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TokenOutAmount", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.TokenOutAmount.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgExitSwapExternAmountOut) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgExitSwapExternAmountOut: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgExitSwapExternAmountOut: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Sender", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Sender = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field PoolId", wireType) + } + m.PoolId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.PoolId |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TokenOut", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.TokenOut.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ShareInMaxAmount", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.ShareInMaxAmount.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgExitSwapExternAmountOutResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgExitSwapExternAmountOutResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgExitSwapExternAmountOutResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ShareInAmount", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.ShareInAmount.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipTx(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTx + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTx + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTx + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthTx + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupTx + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthTx + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthTx = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowTx = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupTx = fmt.Errorf("proto: unexpected end of group") +) diff --git a/tests/interchaintest/osmosis/osmomath/binary_search.go b/tests/interchaintest/osmosis/osmomath/binary_search.go new file mode 100644 index 00000000..e6d449fb --- /dev/null +++ b/tests/interchaintest/osmosis/osmomath/binary_search.go @@ -0,0 +1,300 @@ +package osmomath + +import ( + "errors" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// ErrTolerance is used to define a compare function, which checks if two +// ints are within a certain error tolerance of one another, +// and (optionally) that they are rounding in the correct direction. +// ErrTolerance.Compare(a, b) returns true iff: +// * RoundingMode = RoundUp, then a <= b +// * RoundingMode = RoundDown, then a >= b +// * |a - b| <= AdditiveTolerance +// * |a - b| / min(a, b) <= MultiplicativeTolerance +// +// Each check is respectively ignored if the entry is nil. +// So AdditiveTolerance = Int{} or ZeroInt() +// MultiplicativeTolerance = Dec{} +// RoundingDir = RoundUnconstrained. +// Note that if AdditiveTolerance == 0, then this is equivalent to a standard compare. +type ErrTolerance struct { + AdditiveTolerance Dec + MultiplicativeTolerance Dec + RoundingDir RoundingDirection +} + +// Compare returns if actual is within errTolerance of expected. +// returns 0 if it is +// returns 1 if not, and expected > actual. +// returns -1 if not, and expected < actual +func (e ErrTolerance) Compare(expected Int, actual Int) int { + diff := expected.ToLegacyDec().Sub(actual.ToLegacyDec()).Abs() + + comparisonSign := 0 + if expected.GT(actual) { + comparisonSign = 1 + } else { + comparisonSign = -1 + } + + // Ensure that even if expected is within tolerance of actual, we don't count it as equal if its in the wrong direction. + // so if were supposed to round down, it must be that `expected >= actual`. + // likewise if were supposed to round up, it must be that `expected <= actual`. + // If neither of the above, then rounding direction does not enforce a constraint. + if e.RoundingDir == RoundDown { + if expected.LT(actual) { + return -1 + } + } else if e.RoundingDir == RoundUp { + if expected.GT(actual) { + return 1 + } + } + + // Check additive tolerance equations + if !e.AdditiveTolerance.IsNil() { + // if no error accepted, do a direct compare. + if e.AdditiveTolerance.IsZero() { + if expected.Equal(actual) { + return 0 + } + } + + if diff.GT(e.AdditiveTolerance) { + return comparisonSign + } + } + // Check multiplicative tolerance equations + if !e.MultiplicativeTolerance.IsNil() && !e.MultiplicativeTolerance.IsZero() { + minValue := MinInt(expected.Abs(), actual.Abs()) + if minValue.IsZero() { + return comparisonSign + } + + errTerm := diff.Quo(minValue.ToLegacyDec()) + if errTerm.GT(e.MultiplicativeTolerance) { + return comparisonSign + } + } + + return 0 +} + +// CompareBigDec validates if actual is within errTolerance of expected. +// returns 0 if it is +// returns 1 if not, and expected > actual. +// returns -1 if not, and expected < actual +func (e ErrTolerance) CompareBigDec(expected BigDec, actual BigDec) int { + // Ensure that even if expected is within tolerance of actual, we don't count it as equal if its in the wrong direction. + // so if were supposed to round down, it must be that `expected >= actual`. + // likewise if were supposed to round up, it must be that `expected <= actual`. + // If neither of the above, then rounding direction does not enforce a constraint. + if e.RoundingDir == RoundDown { + if expected.LT(actual) { + return -1 + } + } else if e.RoundingDir == RoundUp { + if expected.GT(actual) { + return 1 + } + } + + diff := expected.Sub(actual).Abs() + + comparisonSign := 0 + if expected.GT(actual) { + comparisonSign = 1 + } else if expected.LT(actual) { + comparisonSign = -1 + } + + // Check additive tolerance equations + if !e.AdditiveTolerance.IsNil() { + // if no error accepted, do a direct compare. + if e.AdditiveTolerance.IsZero() { + if expected.Equal(actual) { + return 0 + } + } + + if diff.GT(BigDecFromDec(e.AdditiveTolerance)) { + return comparisonSign + } + } + // Check multiplicative tolerance equations + if !e.MultiplicativeTolerance.IsNil() && !e.MultiplicativeTolerance.IsZero() { + minValue := MinBigDec(expected.Abs(), actual.Abs()) + if minValue.IsZero() { + return comparisonSign + } + + errTerm := diff.Quo(minValue) + // fmt.Printf("err term %v\n", errTerm) + if errTerm.GT(BigDecFromDec(e.MultiplicativeTolerance)) { + return comparisonSign + } + } + + return 0 +} + +// CompareDec validates if actual is within errTolerance of expected. +// returns 0 if it is +// returns 1 if not, and expected > actual. +// returns -1 if not, and expected < actual +func (e ErrTolerance) CompareDec(expected Dec, actual Dec) int { + // Ensure that even if expected is within tolerance of actual, we don't count it as equal if its in the wrong direction. + // so if we're supposed to round down, it must be that `expected >= actual`. + // likewise if we're supposed to round up, it must be that `expected <= actual`. + // If neither of the above, then rounding direction does not enforce a constraint. + if e.RoundingDir == RoundDown { + if expected.LT(actual) { + return -1 + } + } else if e.RoundingDir == RoundUp { + if expected.GT(actual) { + return 1 + } + } + + diff := expected.Sub(actual).Abs() + + comparisonSign := 0 + if expected.GT(actual) { + comparisonSign = 1 + } else if expected.LT(actual) { + comparisonSign = -1 + } + + // Check additive tolerance equations + if !e.AdditiveTolerance.IsNil() { + // if no error accepted, do a direct compare. + if e.AdditiveTolerance.IsZero() { + if expected.Equal(actual) { + return 0 + } + } + + if diff.GT(e.AdditiveTolerance) { + return comparisonSign + } + } + // Check multiplicative tolerance equations + if !e.MultiplicativeTolerance.IsNil() && !e.MultiplicativeTolerance.IsZero() { + minValue := MinDec(expected.Abs(), actual.Abs()) + if minValue.IsZero() { + return comparisonSign + } + + errTerm := diff.Quo(minValue) + if errTerm.GT(e.MultiplicativeTolerance) { + return comparisonSign + } + } + + return 0 +} + +// EqualCoins returns true iff the two coins are equal within the ErrTolerance constraints and false otherwise. +// TODO: move error tolerance functions to a separate file. +func (e ErrTolerance) EqualCoins(expectedCoins sdk.Coins, actualCoins sdk.Coins) bool { + if len(expectedCoins) < len(actualCoins) { + return false + } + + for _, expectedCoin := range expectedCoins { + curCoinEqual := e.Compare(expectedCoin.Amount, actualCoins.AmountOf(expectedCoin.Denom)) + if curCoinEqual != 0 { + return false + } + } + + return true +} + +// Binary search inputs between [lowerbound, upperbound] to a monotonic increasing function f. +// We stop once f(found_input) meets the ErrTolerance constraints. +// If we perform more than maxIterations (or equivalently lowerbound = upperbound), we return an error. +func BinarySearch(f func(Int) (Int, error), + lowerbound Int, + upperbound Int, + targetOutput Int, + errTolerance ErrTolerance, + maxIterations int, +) (Int, error) { + var ( + curEstimate, curOutput Int + err error + ) + + curIteration := 0 + for ; curIteration < maxIterations; curIteration += 1 { + curEstimate = lowerbound.Add(upperbound).QuoRaw(2) + curOutput, err = f(curEstimate) + if err != nil { + return Int{}, err + } + + compRes := errTolerance.Compare(targetOutput, curOutput) + if compRes < 0 { + upperbound = curEstimate + } else if compRes > 0 { + lowerbound = curEstimate + } else { + return curEstimate, nil + } + } + + return Int{}, errors.New("hit maximum iterations, did not converge fast enough") +} + +// SdkDec +type SdkDec[D any] interface { + Add(SdkDec[D]) SdkDec[D] + Quo(SdkDec[D]) SdkDec[D] + QuoRaw(int64) SdkDec[D] +} + +// BinarySearchBigDec takes as input: +// * an input range [lowerbound, upperbound] +// * an increasing function f +// * a target output x +// * max number of iterations (for gas control / handling does-not-converge cases) +// +// It binary searches on the input range, until it finds an input y s.t. f(y) meets the err tolerance constraints for how close it is to x. +// If we perform more than maxIterations (or equivalently lowerbound = upperbound), we return an error. +func BinarySearchBigDec(f func(BigDec) BigDec, + lowerbound BigDec, + upperbound BigDec, + targetOutput BigDec, + errTolerance ErrTolerance, + maxIterations int, +) (BigDec, error) { + var ( + curEstimate, curOutput BigDec + ) + + curIteration := 0 + for ; curIteration < maxIterations; curIteration += 1 { + // (lowerbound + upperbound) / 2 + curEstimate = lowerbound.Add(upperbound) + curEstimateBi := curEstimate.BigIntMut() + curEstimateBi.Rsh(curEstimateBi, 1) + curOutput = f(curEstimate) + + // fmt.Println("binary search, input, target output, cur output", curEstimate, targetOutput, curOutput) + compRes := errTolerance.CompareBigDec(targetOutput, curOutput) + if compRes < 0 { + upperbound = curEstimate + } else if compRes > 0 { + lowerbound = curEstimate + } else { + return curEstimate, nil + } + } + + return BigDec{}, errors.New("hit maximum iterations, did not converge fast enough") +} diff --git a/tests/interchaintest/osmosis/osmomath/decimal.go b/tests/interchaintest/osmosis/osmomath/decimal.go new file mode 100644 index 00000000..02db83a5 --- /dev/null +++ b/tests/interchaintest/osmosis/osmomath/decimal.go @@ -0,0 +1,1196 @@ +package osmomath + +import ( + "encoding/json" + "errors" + "fmt" + "math/big" + "strconv" + "strings" + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// NOTE: never use new(BigDec) or else we will panic unmarshalling into the +// nil embedded big.Int +type BigDec struct { + i *big.Int +} + +const ( + // number of decimal places + BigDecPrecision = 36 + + // bytes required to represent the above precision + // Ceiling[Log2[10**Precision - 1]] + BigDecimalPrecisionBits = 120 + + maxDecBitLen = maxBitLen + BigDecimalPrecisionBits + + // max number of iterations in ApproxRoot function + maxApproxRootIterations = 100 + + // max number of iterations in Log2 function + maxLog2Iterations = 300 +) + +var ( + precisionReuse = new(big.Int).Exp(big.NewInt(10), big.NewInt(BigDecPrecision), nil) + squaredPrecisionReuse = new(big.Int).Mul(precisionReuse, precisionReuse) + precisionReuseSDK = new(big.Int).Exp(big.NewInt(10), big.NewInt(DecPrecision), nil) + fivePrecision = new(big.Int).Quo(precisionReuse, big.NewInt(2)) + precisionMultipliers []*big.Int + zeroInt = big.NewInt(0) + oneInt = big.NewInt(1) + tenInt = big.NewInt(10) + + // log_2(e) + // From: https://www.wolframalpha.com/input?i=log_2%28e%29+with+37+digits + logOfEbase2 = MustNewBigDecFromStr("1.442695040888963407359924681001892137") + + // log_2(1.0001) + // From: https://www.wolframalpha.com/input?i=log_2%281.0001%29+to+33+digits + tickLogOf2 = MustNewBigDecFromStr("0.000144262291094554178391070900057480") + // initialized in init() since requires + // precision to be defined. + twoBigDec BigDec = MustNewBigDecFromStr("2") + + // precisionFactors are used to adjust the scale of big.Int values to match the desired precision + precisionFactors = make(map[uint64]*big.Int) + + zeroBigDec BigDec = ZeroBigDec() + oneBigDec BigDec = OneBigDec() + oneHalfBigDec BigDec = oneBigDec.Quo(twoBigDec) + negOneBigDec BigDec = oneBigDec.Neg() +) + +// Decimal errors +var ( + ErrEmptyDecimalStr = errors.New("decimal string cannot be empty") + ErrInvalidDecimalLength = errors.New("invalid decimal length") + ErrInvalidDecimalStr = errors.New("invalid decimal string") +) + +// Set precision multipliers +func init() { + precisionMultipliers = make([]*big.Int, BigDecPrecision+1) + for i := 0; i <= BigDecPrecision; i++ { + precisionMultipliers[i] = calcPrecisionMultiplier(int64(i)) + } + + for precision := uint64(0); precision <= BigDecPrecision; precision++ { + precisionFactor := new(big.Int).Exp(big.NewInt(10), big.NewInt(BigDecPrecision-int64(precision)), nil) + precisionFactors[precision] = precisionFactor + } +} + +func precisionInt() *big.Int { + return new(big.Int).Set(precisionReuse) +} + +func ZeroBigDec() BigDec { return BigDec{new(big.Int).Set(zeroInt)} } +func OneBigDec() BigDec { return BigDec{precisionInt()} } +func SmallestBigDec() BigDec { return BigDec{new(big.Int).Set(oneInt)} } + +// calculate the precision multiplier +func calcPrecisionMultiplier(prec int64) *big.Int { + if prec > BigDecPrecision { + panic(fmt.Sprintf("too much precision, maximum %v, provided %v", BigDecPrecision, prec)) + } + zerosToAdd := BigDecPrecision - prec + multiplier := new(big.Int).Exp(tenInt, big.NewInt(zerosToAdd), nil) + return multiplier +} + +// get the precision multiplier, do not mutate result +func precisionMultiplier(prec int64) *big.Int { + if prec > BigDecPrecision { + panic(fmt.Sprintf("too much precision, maximum %v, provided %v", BigDecPrecision, prec)) + } + return precisionMultipliers[prec] +} + +// create a new NewBigDec from integer assuming whole number +func NewBigDec(i int64) BigDec { + return NewBigDecWithPrec(i, 0) +} + +// create a new BigDec from integer with decimal place at prec +// CONTRACT: prec <= BigDecPrecision +func NewBigDecWithPrec(i, prec int64) BigDec { + return BigDec{ + new(big.Int).Mul(big.NewInt(i), precisionMultiplier(prec)), + } +} + +// create a new BigDec from big integer assuming whole numbers +// CONTRACT: prec <= BigDecPrecision +func NewBigDecFromBigInt(i *big.Int) BigDec { + return NewBigDecFromBigIntWithPrec(i, 0) +} + +// create a new BigDec from big integer assuming whole numbers +// CONTRACT: prec <= BigDecPrecision +func NewBigDecFromBigIntWithPrec(i *big.Int, prec int64) BigDec { + return BigDec{ + new(big.Int).Mul(i, precisionMultiplier(prec)), + } +} + +// create a new BigDec from big integer assuming whole numbers +// CONTRACT: prec <= BigDecPrecision +func NewBigDecFromInt(i BigInt) BigDec { + return NewBigDecFromIntWithPrec(i, 0) +} + +// create a new BigDec from big integer with decimal place at prec +// CONTRACT: prec <= BigDecPrecision +func NewBigDecFromIntWithPrec(i BigInt, prec int64) BigDec { + return BigDec{ + new(big.Int).Mul(i.BigInt(), precisionMultiplier(prec)), + } +} + +// create a decimal from an input decimal string. +// valid must come in the form: +// +// (-) whole integers (.) decimal integers +// +// examples of acceptable input include: +// +// -123.456 +// 456.7890 +// 345 +// -456789 +// +// NOTE - An error will return if more decimal places +// are provided in the string than the constant Precision. +// +// CONTRACT - This function does not mutate the input str. +func NewBigDecFromStr(str string) (BigDec, error) { + if len(str) == 0 { + return BigDec{}, ErrEmptyDecimalStr + } + + // first extract any negative symbol + neg := false + if str[0] == '-' { + neg = true + str = str[1:] + } + + if len(str) == 0 { + return BigDec{}, ErrEmptyDecimalStr + } + + strs := strings.Split(str, ".") + lenDecs := 0 + combinedStr := strs[0] + + if len(strs) == 2 { // has a decimal place + lenDecs = len(strs[1]) + if lenDecs == 0 || len(combinedStr) == 0 { + return BigDec{}, ErrInvalidDecimalLength + } + combinedStr += strs[1] + } else if len(strs) > 2 { + return BigDec{}, ErrInvalidDecimalStr + } + + if lenDecs > BigDecPrecision { + return BigDec{}, fmt.Errorf("invalid precision; max: %d, got: %d", BigDecPrecision, lenDecs) + } + + // add some extra zero's to correct to the Precision factor + zerosToAdd := BigDecPrecision - lenDecs + zeros := fmt.Sprintf(`%0`+strconv.Itoa(zerosToAdd)+`s`, "") + combinedStr += zeros + + combined, ok := new(big.Int).SetString(combinedStr, 10) // base 10 + if !ok { + return BigDec{}, fmt.Errorf("failed to set decimal string: %s", combinedStr) + } + if combined.BitLen() > maxBitLen { + return BigDec{}, fmt.Errorf("decimal out of range; bitLen: got %d, max %d", combined.BitLen(), maxBitLen) + } + if neg { + combined = new(big.Int).Neg(combined) + } + + return BigDec{combined}, nil +} + +// Decimal from string, panic on error +func MustNewBigDecFromStr(s string) BigDec { + dec, err := NewBigDecFromStr(s) + if err != nil { + panic(err) + } + return dec +} + +func (d BigDec) IsNil() bool { return d.i == nil } // is decimal nil +func (d BigDec) IsZero() bool { return (d.i).Sign() == 0 } // is equal to zero +func (d BigDec) IsNegative() bool { return (d.i).Sign() == -1 } // is negative +func (d BigDec) IsPositive() bool { return (d.i).Sign() == 1 } // is positive +func (d BigDec) Equal(d2 BigDec) bool { return (d.i).Cmp(d2.i) == 0 } // equal decimals +func (d BigDec) GT(d2 BigDec) bool { return (d.i).Cmp(d2.i) > 0 } // greater than +func (d BigDec) GTE(d2 BigDec) bool { return (d.i).Cmp(d2.i) >= 0 } // greater than or equal +func (d BigDec) LT(d2 BigDec) bool { return (d.i).Cmp(d2.i) < 0 } // less than +func (d BigDec) LTE(d2 BigDec) bool { return (d.i).Cmp(d2.i) <= 0 } // less than or equal +func (d BigDec) Neg() BigDec { return BigDec{new(big.Int).Neg(d.i)} } // reverse the decimal sign +// nolint: stylecheck +func (d BigDec) Abs() BigDec { return BigDec{new(big.Int).Abs(d.i)} } // absolute value + +// BigInt returns a copy of the underlying big.Int. +func (d BigDec) BigInt() *big.Int { + if d.IsNil() { + return nil + } + + cp := new(big.Int) + return cp.Set(d.i) +} + +// BigIntMut returns the pointer of the underlying big.Int. +func (d BigDec) BigIntMut() *big.Int { + if d.IsNil() { + return nil + } + + return d.i +} + +// addition +func (d BigDec) Add(d2 BigDec) BigDec { + copy := d.Clone() + copy.AddMut(d2) + return copy +} + +// mutative addition +func (d BigDec) AddMut(d2 BigDec) BigDec { + d.i.Add(d.i, d2.i) + + if d.i.BitLen() > maxDecBitLen { + panic("Int overflow") + } + + return d +} + +// subtraction +func (d BigDec) Sub(d2 BigDec) BigDec { + res := new(big.Int).Sub(d.i, d2.i) + + if res.BitLen() > maxDecBitLen { + panic("Int overflow") + } + return BigDec{res} +} + +func (d BigDec) NegMut() BigDec { + d.i.Neg(d.i) + return d +} // reverse the decimal sign + +// Clone performs a deep copy of the receiver +// and returns the new result. +func (d BigDec) Clone() BigDec { + copy := BigDec{new(big.Int)} + copy.i.Set(d.i) + return copy +} + +// Mut performs non-mutative multiplication. +// The receiver is not modifier but the result is. +func (d BigDec) Mul(d2 BigDec) BigDec { + copy := d.Clone() + copy.MulMut(d2) + return copy +} + +// Mut performs non-mutative multiplication. +// The receiver is not modifier but the result is. +func (d BigDec) MulMut(d2 BigDec) BigDec { + d.i.Mul(d.i, d2.i) + d.i = chopPrecisionAndRound(d.i) + + if d.i.BitLen() > maxDecBitLen { + panic("Int overflow") + } + return BigDec{d.i} +} + +// multiplication truncate +func (d BigDec) MulTruncate(d2 BigDec) BigDec { + mul := new(big.Int).Mul(d.i, d2.i) + chopped := chopPrecisionAndTruncate(mul) + + if chopped.BitLen() > maxDecBitLen { + panic("Int overflow") + } + return BigDec{chopped} +} + +// multiplication round up +func (d BigDec) MulRoundUp(d2 BigDec) BigDec { + mul := new(big.Int).Mul(d.i, d2.i) + chopped := chopPrecisionAndRoundUpBigDec(mul) + + if chopped.BitLen() > maxDecBitLen { + panic("Int overflow") + } + return BigDec{chopped} +} + +// multiplication +func (d BigDec) MulInt(i BigInt) BigDec { + mul := new(big.Int).Mul(d.i, i.i) + + if mul.BitLen() > maxDecBitLen { + panic("Int overflow") + } + return BigDec{mul} +} + +// MulInt64 - multiplication with int64 +func (d BigDec) MulInt64(i int64) BigDec { + mul := new(big.Int).Mul(d.i, big.NewInt(i)) + + if mul.BitLen() > maxDecBitLen { + panic("Int overflow") + } + return BigDec{mul} +} + +// quotient +func (d BigDec) Quo(d2 BigDec) BigDec { + copy := d.Clone() + copy.QuoMut(d2) + return copy +} + +// mutative quotient +func (d BigDec) QuoMut(d2 BigDec) BigDec { + // multiply precision twice + d.i.Mul(d.i, precisionReuse) + d.i.Mul(d.i, precisionReuse) + + d.i.Quo(d.i, d2.i) + chopPrecisionAndRound(d.i) + + if d.i.BitLen() > maxDecBitLen { + panic("Int overflow") + } + return d +} +func (d BigDec) QuoRaw(d2 int64) BigDec { + // multiply precision, so we can chop it later + mul := new(big.Int).Mul(d.i, precisionReuse) + + quo := mul.Quo(mul, big.NewInt(d2)) + chopped := chopPrecisionAndRound(quo) + + if chopped.BitLen() > maxDecBitLen { + panic("Int overflow") + } + return BigDec{chopped} +} + +// quotient truncate +func (d BigDec) QuoTruncate(d2 BigDec) BigDec { + // multiply precision twice + mul := new(big.Int).Mul(d.i, precisionReuse) + mul.Mul(mul, precisionReuse) + + quo := mul.Quo(mul, d2.i) + chopped := chopPrecisionAndTruncate(quo) + + if chopped.BitLen() > maxDecBitLen { + panic("Int overflow") + } + return BigDec{chopped} +} + +// quotient truncate (mutative) +func (d BigDec) QuoTruncateMut(d2 BigDec) BigDec { + // multiply precision twice + d.i.Mul(d.i, squaredPrecisionReuse) + d.i.Quo(d.i, d2.i) + + chopPrecisionAndTruncateMut(d.i) + if d.i.BitLen() > maxDecBitLen { + panic("Int overflow") + } + return d +} + +// quotient, round up +func (d BigDec) QuoRoundUp(d2 BigDec) BigDec { + // multiply precision twice + mul := new(big.Int).Mul(d.i, precisionReuse) + mul.Mul(mul, precisionReuse) + + quo := new(big.Int).Quo(mul, d2.i) + chopped := chopPrecisionAndRoundUpBigDec(quo) + + if chopped.BitLen() > maxDecBitLen { + panic("Int overflow") + } + return BigDec{chopped} +} + +// quotient, round up (mutative) +func (d BigDec) QuoRoundUpMut(d2 BigDec) BigDec { + // multiply precision twice + d.i.Mul(d.i, squaredPrecisionReuse) + d.i.Quo(d.i, d2.i) + + chopPrecisionAndRoundUpMut(d.i, precisionReuse) + + if d.i.BitLen() > maxDecBitLen { + panic("Int overflow") + } + return BigDec{d.i} +} + +// quotient +func (d BigDec) QuoInt(i BigInt) BigDec { + mul := new(big.Int).Quo(d.i, i.i) + return BigDec{mul} +} + +// QuoInt64 - quotient with int64 +func (d BigDec) QuoInt64(i int64) BigDec { + mul := new(big.Int).Quo(d.i, big.NewInt(i)) + return BigDec{mul} +} + +// ApproxRoot returns an approximate estimation of a Dec's positive real nth root +// using Newton's method (where n is positive). The algorithm starts with some guess and +// computes the sequence of improved guesses until an answer converges to an +// approximate answer. It returns `|d|.ApproxRoot() * -1` if input is negative. +// A maximum number of 100 iterations is used a backup boundary condition for +// cases where the answer never converges enough to satisfy the main condition. +func (d BigDec) ApproxRoot(root uint64) (guess BigDec, err error) { + defer func() { + if r := recover(); r != nil { + var ok bool + err, ok = r.(error) + if !ok { + err = errors.New("out of bounds") + } + } + }() + + if d.IsNegative() { + absRoot, err := d.MulInt64(-1).ApproxRoot(root) + return absRoot.MulInt64(-1), err + } + + if root == 1 || d.IsZero() || d.Equal(OneBigDec()) { + return d, nil + } + + if root == 0 { + return OneBigDec(), nil + } + + rootInt := NewBigIntFromUint64(root) + guess, delta := OneBigDec(), OneBigDec() + + for iter := 0; delta.Abs().GT(SmallestBigDec()) && iter < maxApproxRootIterations; iter++ { + prev := guess.PowerInteger(root - 1) + if prev.IsZero() { + prev = SmallestBigDec() + } + delta = d.Quo(prev) + delta = delta.Sub(guess) + delta = delta.QuoInt(rootInt) + + guess = guess.Add(delta) + } + + return guess, nil +} + +// ApproxSqrt is a wrapper around ApproxRoot for the common special case +// of finding the square root of a number. It returns -(sqrt(abs(d)) if input is negative. +func (d BigDec) ApproxSqrt() (BigDec, error) { + return d.ApproxRoot(2) +} + +// is integer, e.g. decimals are zero +func (d BigDec) IsInteger() bool { + return new(big.Int).Rem(d.i, precisionReuse).Sign() == 0 +} + +// format decimal state +func (d BigDec) Format(s fmt.State, verb rune) { + _, err := s.Write([]byte(d.String())) + if err != nil { + panic(err) + } +} + +// String returns a BigDec as a string. +func (d BigDec) String() string { + if d.i == nil { + return d.i.String() + } + + isNeg := d.IsNegative() + + if isNeg { + d = d.Neg() + } + + bzInt, err := d.i.MarshalText() + if err != nil { + return "" + } + inputSize := len(bzInt) + + var bzStr []byte + + // TODO: Remove trailing zeros + // case 1, purely decimal + if inputSize <= BigDecPrecision { + bzStr = make([]byte, BigDecPrecision+2) + + // 0. prefix + bzStr[0] = byte('0') + bzStr[1] = byte('.') + + // set relevant digits to 0 + for i := 0; i < BigDecPrecision-inputSize; i++ { + bzStr[i+2] = byte('0') + } + + // set final digits + copy(bzStr[2+(BigDecPrecision-inputSize):], bzInt) + } else { + // inputSize + 1 to account for the decimal point that is being added + bzStr = make([]byte, inputSize+1) + decPointPlace := inputSize - BigDecPrecision + + copy(bzStr, bzInt[:decPointPlace]) // pre-decimal digits + bzStr[decPointPlace] = byte('.') // decimal point + copy(bzStr[decPointPlace+1:], bzInt[decPointPlace:]) // post-decimal digits + } + + if isNeg { + return "-" + string(bzStr) + } + + return string(bzStr) +} + +// Float64 returns the float64 representation of a BigDec. +// Will return the error if the conversion failed. +func (d BigDec) Float64() (float64, error) { + return strconv.ParseFloat(d.String(), 64) +} + +// MustFloat64 returns the float64 representation of a BigDec. +// Would panic if the conversion failed. +func (d BigDec) MustFloat64() float64 { + if value, err := strconv.ParseFloat(d.String(), 64); err != nil { + panic(err) + } else { + return value + } +} + +// Dec returns the osmomath.Dec representation of a BigDec. +// Values in any additional decimal places are truncated. +func (d BigDec) Dec() Dec { + return d.DecWithPrecision(DecPrecision) +} + +// DecWithPrecision converts BigDec to Dec with desired precision +// Example: +// BigDec: 1.010100000000153000000000000000000000 +// precision: 4 +// Output Dec: 1.010100000000000000 +// Panics if precision exceeds DecPrecision +func (d BigDec) DecWithPrecision(precision uint64) Dec { + var precisionFactor *big.Int + if precision > DecPrecision { + panic(fmt.Sprintf("maximum Dec precision is (%v), provided (%v)", DecPrecision, precision)) + } else { + precisionFactor = precisionFactors[precision] + } + + // Truncate any additional decimal values that exist due to BigDec's additional precision + // This relies on big.Int's Quo function doing floor division + intRepresentation := new(big.Int).Quo(d.BigInt(), precisionFactor) + + // convert int representation back to SDK Dec precision + truncatedDec := NewDecFromBigIntWithPrec(intRepresentation, int64(precision)) + + return truncatedDec +} + +// ChopPrecisionMut truncates all decimals after precision numbers after decimal point. Mutative +// CONTRACT: precision <= BigDecPrecision +// Panics if precision exceeds BigDecPrecision +func (d *BigDec) ChopPrecisionMut(precision uint64) BigDec { + if precision > BigDecPrecision { + panic(fmt.Sprintf("maximum BigDec precision is (%v), provided (%v)", DecPrecision, precision)) + } + + precisionFactor := precisionFactors[precision] + // big.Quo truncates numbers that would have been after decimal point + d.i.Quo(d.i, precisionFactor) + d.i.Mul(d.i, precisionFactor) + return BigDec{d.i} +} + +// ChopPrecision truncates all decimals after precision numbers after decimal point +// CONTRACT: precision <= BigDecPrecision +// Panics if precision exceeds BigDecPrecision +func (d *BigDec) ChopPrecision(precision uint64) BigDec { + copy := d.Clone() + return copy.ChopPrecisionMut(precision) +} + +// DecRoundUp returns the osmomath.Dec representation of a BigDec. +// Round up at precision end. +// Values in any additional decimal places are truncated. +func (d BigDec) DecRoundUp() Dec { + return NewDecFromBigIntWithPrec(chopPrecisionAndRoundUpDec(d.i), DecPrecision) +} + +// BigDecFromDec returns the BigDec representation of an Dec. +// Values in any additional decimal places are truncated. +func BigDecFromDec(d Dec) BigDec { + return NewBigDecFromBigIntWithPrec(d.BigInt(), DecPrecision) +} + +// BigDecFromSDKInt returns the BigDec representation of an sdkInt. +// Values in any additional decimal places are truncated. +func BigDecFromSDKInt(i Int) BigDec { + return NewBigDecFromBigIntWithPrec(i.BigInt(), 0) +} + +// BigDecFromDecSlice returns the []BigDec representation of an []Dec. +// Values in any additional decimal places are truncated. +func BigDecFromDecSlice(ds []Dec) []BigDec { + result := make([]BigDec, len(ds)) + for i, d := range ds { + result[i] = NewBigDecFromBigIntWithPrec(d.BigInt(), DecPrecision) + } + return result +} + +// BigDecFromDecSlice returns the []BigDec representation of an []Dec. +// Values in any additional decimal places are truncated. +func BigDecFromDecCoinSlice(ds []sdk.DecCoin) []BigDec { + result := make([]BigDec, len(ds)) + for i, d := range ds { + result[i] = NewBigDecFromBigIntWithPrec(d.Amount.BigInt(), DecPrecision) + } + return result +} + +// ____ +// __| |__ "chop 'em +// ` \ round!" +// ___|| ~ _ -bankers +// | | __ +// | | | __|__|__ +// |_____: / | $$$ | +// |________| + +// Remove a Precision amount of rightmost digits and perform bankers rounding +// on the remainder (gaussian rounding) on the digits which have been removed. +// +// Mutates the input. Use the non-mutative version if that is undesired +func chopPrecisionAndRound(d *big.Int) *big.Int { + // remove the negative and add it back when returning + if d.Sign() == -1 { + // make d positive, compute chopped value, and then un-mutate d + d = d.Neg(d) + d = chopPrecisionAndRound(d) + d = d.Neg(d) + return d + } + + // get the truncated quotient and remainder + quo, rem := d, big.NewInt(0) + quo, rem = quo.QuoRem(d, precisionReuse, rem) + + if rem.Sign() == 0 { // remainder is zero + return quo + } + + switch rem.Cmp(fivePrecision) { + case -1: + return quo + case 1: + return quo.Add(quo, oneInt) + default: // bankers rounding must take place + // always round to an even number + if quo.Bit(0) == 0 { + return quo + } + return quo.Add(quo, oneInt) + } +} + +// chopPrecisionAndRoundUpBigDec removes a Precision amount of rightmost digits and rounds up. +// Non-mutative. +func chopPrecisionAndRoundUpBigDec(d *big.Int) *big.Int { + // make copy + copy := new(big.Int).Set(d) + return chopPrecisionAndRoundUpMut(copy, precisionReuse) +} + +// chopPrecisionAndRoundUpDec removes DecPrecision amount of rightmost digits and rounds up. +// Non-mutative. +func chopPrecisionAndRoundUpDec(d *big.Int) *big.Int { + copy := new(big.Int).Set(d) + return chopPrecisionAndRoundUpMut(copy, precisionReuseSDK) +} + +// chopPrecisionAndRoundUp removes a Precision amount of rightmost digits and rounds up. +// Mutates input d. +// Mutations occur: +// - By calling chopPrecisionAndTruncateMut. +// - Using input d directly in QuoRem. +func chopPrecisionAndRoundUpMut(d *big.Int, precisionReuse *big.Int) *big.Int { + // remove the negative and add it back when returning + if d.Sign() == -1 { + // make d positive, compute chopped value, and then un-mutate d + d = d.Neg(d) + // truncate since d is negative... + d = chopPrecisionAndTruncateMut(d) + d = d.Neg(d) + return d + } + + // get the truncated quotient and remainder + _, rem := d.QuoRem(d, precisionReuse, big.NewInt(0)) + + if rem.Sign() == 0 { // remainder is zero + return d + } + + return d.Add(d, oneInt) +} + +func chopPrecisionAndRoundNonMutative(d *big.Int) *big.Int { + tmp := new(big.Int).Set(d) + return chopPrecisionAndRound(tmp) +} + +// RoundInt64 rounds the decimal using bankers rounding +func (d BigDec) RoundInt64() int64 { + chopped := chopPrecisionAndRoundNonMutative(d.i) + if !chopped.IsInt64() { + panic("Int64() out of bound") + } + return chopped.Int64() +} + +// RoundInt round the decimal using bankers rounding +func (d BigDec) RoundInt() BigInt { + return NewBigIntFromBigInt(chopPrecisionAndRoundNonMutative(d.i)) +} + +// chopPrecisionAndTruncate is similar to chopPrecisionAndRound, +// but always rounds down. It does not mutate the input. +func chopPrecisionAndTruncate(d *big.Int) *big.Int { + return new(big.Int).Quo(d, precisionReuse) +} + +// chopPrecisionAndTruncate is similar to chopPrecisionAndRound, +// but always rounds down. It mutates the input. +func chopPrecisionAndTruncateMut(d *big.Int) *big.Int { + return d.Quo(d, precisionReuse) +} + +// TruncateInt64 truncates the decimals from the number and returns an int64 +func (d BigDec) TruncateInt64() int64 { + chopped := chopPrecisionAndTruncate(d.i) + if !chopped.IsInt64() { + panic("Int64() out of bound") + } + return chopped.Int64() +} + +// TruncateInt truncates the decimals from the number and returns an Int +func (d BigDec) TruncateInt() BigInt { + return NewBigIntFromBigInt(chopPrecisionAndTruncate(d.i)) +} + +// TruncateDec truncates the decimals from the number and returns a Dec +func (d BigDec) TruncateDec() BigDec { + return NewBigDecFromBigInt(chopPrecisionAndTruncate(d.i)) +} + +// Ceil returns the smallest integer value (as a decimal) that is greater than +// or equal to the given decimal. +func (d BigDec) Ceil() BigDec { + tmp := new(big.Int).Set(d.i) + + quo, rem := tmp, big.NewInt(0) + quo, rem = quo.QuoRem(tmp, precisionReuse, rem) + + // no need to round with a zero remainder regardless of sign + if rem.Cmp(zeroInt) == 0 { + return NewBigDecFromBigInt(quo) + } + + if rem.Sign() == -1 { + return NewBigDecFromBigInt(quo) + } + + return NewBigDecFromBigInt(quo.Add(quo, oneInt)) +} + +// MaxSortableDec is the largest Dec that can be passed into SortableDecBytes() +// Its negative form is the least Dec that can be passed in. +var MaxSortableDec = OneBigDec().Quo(SmallestBigDec()) + +// ValidSortableDec ensures that a Dec is within the sortable bounds, +// a BigDec can't have a precision of less than 10^-18. +// Max sortable decimal was set to the reciprocal of SmallestDec. +func ValidSortableDec(dec BigDec) bool { + return dec.Abs().LTE(MaxSortableDec) +} + +// SortableDecBytes returns a byte slice representation of a Dec that can be sorted. +// Left and right pads with 0s so there are 18 digits to left and right of the decimal point. +// For this reason, there is a maximum and minimum value for this, enforced by ValidSortableDec. +func SortableDecBytes(dec BigDec) []byte { + if !ValidSortableDec(dec) { + panic("dec must be within bounds") + } + // Instead of adding an extra byte to all sortable decs in order to handle max sortable, we just + // makes its bytes be "max" which comes after all numbers in ASCIIbetical order + if dec.Equal(MaxSortableDec) { + return []byte("max") + } + // For the same reason, we make the bytes of minimum sortable dec be --, which comes before all numbers. + if dec.Equal(MaxSortableDec.Neg()) { + return []byte("--") + } + // We move the negative sign to the front of all the left padded 0s, to make negative numbers come before positive numbers + if dec.IsNegative() { + return append([]byte("-"), []byte(fmt.Sprintf(fmt.Sprintf("%%0%ds", BigDecPrecision*2+1), dec.Abs().String()))...) + } + return []byte(fmt.Sprintf(fmt.Sprintf("%%0%ds", BigDecPrecision*2+1), dec.String())) +} + +// reuse nil values +var nilJSON []byte + +func init() { + empty := new(big.Int) + bz, _ := empty.MarshalText() + nilJSON, _ = json.Marshal(string(bz)) +} + +// MarshalJSON marshals the decimal +func (d BigDec) MarshalJSON() ([]byte, error) { + if d.i == nil { + return nilJSON, nil + } + return json.Marshal(d.String()) +} + +// UnmarshalJSON defines custom decoding scheme +func (d *BigDec) UnmarshalJSON(bz []byte) error { + if d.i == nil { + d.i = new(big.Int) + } + + var text string + err := json.Unmarshal(bz, &text) + if err != nil { + return err + } + + // TODO: Reuse dec allocation + newDec, err := NewBigDecFromStr(text) + if err != nil { + return err + } + + d.i = newDec.i + return nil +} + +// MarshalYAML returns the YAML representation. +func (d BigDec) MarshalYAML() (interface{}, error) { + return d.String(), nil +} + +// Marshal implements the gogo proto custom type interface. +func (d BigDec) Marshal() ([]byte, error) { + if d.i == nil { + d.i = new(big.Int) + } + return d.i.MarshalText() +} + +// MarshalTo implements the gogo proto custom type interface. +func (d *BigDec) MarshalTo(data []byte) (n int, err error) { + if d.i == nil { + d.i = new(big.Int) + } + + if d.i.Cmp(zeroInt) == 0 { + copy(data, []byte{0x30}) + return 1, nil + } + + bz, err := d.Marshal() + if err != nil { + return 0, err + } + + copy(data, bz) + return len(bz), nil +} + +// Unmarshal implements the gogo proto custom type interface. +func (d *BigDec) Unmarshal(data []byte) error { + if len(data) == 0 { + d = nil + return nil + } + + if d.i == nil { + d.i = new(big.Int) + } + + if err := d.i.UnmarshalText(data); err != nil { + return err + } + + if d.i.BitLen() > maxBitLen { + return fmt.Errorf("decimal out of range; got: %d, max: %d", d.i.BitLen(), maxBitLen) + } + + return nil +} + +// Size implements the gogo proto custom type interface. +func (d *BigDec) Size() int { + bz, _ := d.Marshal() + return len(bz) +} + +// Override Amino binary serialization by proxying to protobuf. +func (d BigDec) MarshalAmino() ([]byte, error) { return d.Marshal() } +func (d *BigDec) UnmarshalAmino(bz []byte) error { return d.Unmarshal(bz) } + +// helpers + +// DecsEqual tests if two decimal arrays are equal +func DecsEqual(d1s, d2s []BigDec) bool { + if len(d1s) != len(d2s) { + return false + } + + for i, d1 := range d1s { + if !d1.Equal(d2s[i]) { + return false + } + } + return true +} + +// MinBigDec gets minimum decimal between two +func MinBigDec(d1, d2 BigDec) BigDec { + if d1.LT(d2) { + return d1 + } + return d2 +} + +// MaxBigDec gets maximum decimal between two +func MaxBigDec(d1, d2 BigDec) BigDec { + if d1.LT(d2) { + return d2 + } + return d1 +} + +// DecEq returns true if two given decimals are equal. +// Intended to be used with require/assert: require.True(t, DecEq(...)) +// +//nolint:thelper +func DecEq(t *testing.T, exp, got BigDec) (*testing.T, bool, string, string, string) { + return t, exp.Equal(got), "expected:\t%v\ngot:\t\t%v", exp.String(), got.String() +} + +// DecApproxEq returns true if the differences between two given decimals are smaller than the tolerance range. +// Intended to be used with require/assert: require.True(t, DecEq(...)) +// +//nolint:thelper +func DecApproxEq(t *testing.T, d1 BigDec, d2 BigDec, tol BigDec) (*testing.T, bool, string, string, string) { + diff := d1.Sub(d2).Abs() + return t, diff.LTE(tol), "expected |d1 - d2| <:\t%v\ngot |d1 - d2| = \t\t%v", tol.String(), diff.String() +} + +// LogBase2 returns log_2 {x}. +// Rounds down by truncations during division and right shifting. +// Accurate up to 32 precision digits. +// Implementation is based on: +// https://stm32duinoforum.com/forum/dsp/BinaryLogarithm.pdf +func (x BigDec) LogBase2() BigDec { + // create a new decimal to avoid mutating + // the receiver's int buffer. + xCopy := BigDec{} + xCopy.i = new(big.Int).Set(x.i) + if xCopy.LTE(zeroBigDec) { + panic(fmt.Sprintf("log is not defined at <= 0, given (%s)", xCopy)) + } + + // Normalize x to be 1 <= x < 2. + + // y is the exponent that results in a whole multiple of 2. + y := ZeroBigDec() + + // repeat until: x >= 1. + for xCopy.LT(oneBigDec) { + xCopy.i.Lsh(xCopy.i, 1) + y.AddMut(negOneBigDec) + } + + // repeat until: x < 2. + for xCopy.GTE(twoBigDec) { + xCopy.i.Rsh(xCopy.i, 1) + y.AddMut(oneBigDec) + } + + b := oneHalfBigDec.Clone() + + // N.B. At this point x is a positive real number representing + // mantissa of the log. We estimate it using the following + // algorithm: + // https://stm32duinoforum.com/forum/dsp/BinaryLogarithm.pdf + // This has shown precision of 32 digits relative + // to Wolfram Alpha in tests. + for i := 0; i < maxLog2Iterations; i++ { + xCopy.MulMut(xCopy) + if xCopy.GTE(twoBigDec) { + xCopy.i.Rsh(xCopy.i, 1) + y.AddMut(b) + } + b.i.Rsh(b.i, 1) + } + + return y +} + +// Natural logarithm of x. +// Formula: ln(x) = log_2(x) / log_2(e) +func (x BigDec) Ln() BigDec { + log2x := x.LogBase2() + + y := log2x.Quo(logOfEbase2) + + return y +} + +// log_1.0001(x) "tick" base logarithm +// Formula: log_1.0001(b) = log_2(b) / log_2(1.0001) +func (x BigDec) TickLog() BigDec { + log2x := x.LogBase2() + + y := log2x.Quo(tickLogOf2) + + return y +} + +// log_a(x) custom base logarithm +// Formula: log_a(b) = log_2(b) / log_2(a) +func (x BigDec) CustomBaseLog(base BigDec) BigDec { + if base.LTE(ZeroBigDec()) || base.Equal(OneBigDec()) { + panic(fmt.Sprintf("log is not defined at base <= 0 or base == 1, base given (%s)", base)) + } + + log2x_argument := x.LogBase2() + log2x_base := base.LogBase2() + + y := log2x_argument.Quo(log2x_base) + + return y +} + +// PowerInteger takes a given decimal to an integer power +// and returns the result. Non-mutative. Uses square and multiply +// algorithm for performing the calculation. +func (d BigDec) PowerInteger(power uint64) BigDec { + clone := d.Clone() + return clone.PowerIntegerMut(power) +} + +// PowerIntegerMut takes a given decimal to an integer power +// and returns the result. Mutative. Uses square and multiply +// algorithm for performing the calculation. +func (d BigDec) PowerIntegerMut(power uint64) BigDec { + if power == 0 { + return OneBigDec() + } + tmp := OneBigDec() + + for i := power; i > 1; { + if i%2 != 0 { + tmp = tmp.MulMut(d) + } + i /= 2 + d = d.MulMut(d) + } + + return d.MulMut(tmp) +} + +// Power returns a result of raising the given big dec to +// a positive decimal power. Panics if the power is negative. +// Panics if the base is negative. Does not mutate the receiver. +// The max supported exponent is defined by the global maxSupportedExponent. +// If a greater exponent is given, the function panics. +// The error is not bounded but expected to be around 10^-18, use with care. +// See the underlying Exp2, LogBase2 and Mul for the details of their bounds. +// WARNING: This function is broken for base < 1. The reason is that logarithm function is +// negative between zero and 1, and the Exp2(k) is undefined for negative k. +// As a result, this function panics if called for d < 1. +func (d BigDec) Power(power BigDec) BigDec { + if d.IsNegative() { + panic(fmt.Sprintf("negative base is not supported for Power(), base was (%s)", d)) + } + if power.IsNegative() { + panic(fmt.Sprintf("negative power is not supported for Power(), power was (%s)", power)) + } + if power.Abs().GT(maxSupportedExponent) { + panic(fmt.Sprintf("integer exponent %s is too large, max (%s)", power, maxSupportedExponent)) + } + if power.IsInteger() { + return d.PowerInteger(power.TruncateInt().Uint64()) + } + if power.IsZero() { + return OneBigDec() + } + if d.IsZero() { + return ZeroBigDec() + } + if d.LT(OneBigDec()) { + panic(fmt.Sprintf("Power() is not supported for base < 1, base was (%s)", d)) + } + if d.Equal(twoBigDec) { + return Exp2(power) + } + + // d^power = exp2(power * log_2{base}) + result := Exp2(d.LogBase2().Mul(power)) + + return result +} diff --git a/tests/interchaintest/osmosis/osmomath/exp2.go b/tests/interchaintest/osmosis/osmomath/exp2.go new file mode 100644 index 00000000..7379741a --- /dev/null +++ b/tests/interchaintest/osmosis/osmomath/exp2.go @@ -0,0 +1,101 @@ +package osmomath + +import "fmt" + +var ( + // Truncated at precision end. + // See scripts/approximations/main.py exponent_approximation_choice function for details. + numeratorCoefficients13Param = []BigDec{ + MustNewBigDecFromStr("1.000000000000000000000044212244679434"), + MustNewBigDecFromStr("0.352032455817400196452603772766844426"), + MustNewBigDecFromStr("0.056507868883666405413116800969512484"), + MustNewBigDecFromStr("0.005343900728213034434757419480319916"), + MustNewBigDecFromStr("0.000317708814342353603087543715930732"), + MustNewBigDecFromStr("0.000011429747507407623028722262874632"), + MustNewBigDecFromStr("0.000000198381965651614980168744540366"), + } + + // Rounded up at precision end. + // See scripts/approximations/main.py exponent_approximation_choice function for details. + denominatorCoefficients13Param = []BigDec{ + OneBigDec(), + MustNewBigDecFromStr("0.341114724742545112949699755780593311").Neg(), + MustNewBigDecFromStr("0.052724071627342653404436933178482287"), + MustNewBigDecFromStr("0.004760950735524957576233524801866342").Neg(), + MustNewBigDecFromStr("0.000267168475410566529819971616894193"), + MustNewBigDecFromStr("0.000008923715368802211181557353097439").Neg(), + MustNewBigDecFromStr("0.000000140277233177373698516010555916"), + } + + // maxSupportedExponent = 2^10. The value is chosen by benchmarking + // when the underlying internal functions overflow. + // If needed in the future, Exp2 can be reimplemented to allow for greater exponents. + maxSupportedExponent = MustNewBigDecFromStr("2").PowerInteger(9) +) + +// Exp2 takes 2 to the power of a given non-negative decimal exponent +// and returns the result. +// The computation is performed by using th following property: +// 2^decimal_exp = 2^{integer_exp + fractional_exp} = 2^integer_exp * 2^fractional_exp +// The max supported exponent is defined by the global maxSupportedExponent. +// If a greater exponent is given, the function panics. +// Panics if the exponent is negative. +// The answer is correct up to a factor of 10^-18. +// Meaning, result = result * k for k in [1 - 10^(-18), 1 + 10^(-18)] +// Note: our Python script plots show accuracy up to a factor of 10^22. +// However, in Go tests we only test up to 10^18. Therefore, this is the guarantee. +func Exp2(exponent BigDec) BigDec { + if exponent.Abs().GT(maxSupportedExponent) { + panic(fmt.Sprintf("integer exponent %s is too large, max (%s)", exponent, maxSupportedExponent)) + } + if exponent.IsNegative() { + panic(fmt.Sprintf("negative exponent %s is not supported", exponent)) + } + + integerExponent := exponent.TruncateDec() + + fractionalExponent := exponent.Sub(integerExponent) + fractionalResult := exp2ChebyshevRationalApprox(fractionalExponent) + + // Left bit shift is equivalent to multiplying by 2^integerExponent. + fractionalResult.i = fractionalResult.i.Lsh(fractionalResult.i, uint(integerExponent.TruncateInt().Uint64())) + + return fractionalResult +} + +// exp2ChebyshevRationalApprox takes 2 to the power of a given decimal exponent. +// The result is approximated by a 13 parameter Chebyshev rational approximation. +// f(x) = h(x) / p(x) (7, 7) terms. We set the first term of p(x) to 1. +// As a result, this ends up being 7 + 6 = 13 parameters. +// The numerator coefficients are truncated at precision end. The denominator +// coefficients are rounded up at precision end. +// See scripts/approximations/README.md for details of the scripts used +// to compute the coefficients. +// CONTRACT: exponent must be in the range [0, 1], panics if not. +// The answer is correct up to a factor of 10^-18. +// Meaning, result = result * k for k in [1 - 10^(-18), 1 + 10^(-18)] +// Note: our Python script plots show accuracy up to a factor of 10^22. +// However, in Go tests we only test up to 10^18. Therefore, this is the guarantee. +func exp2ChebyshevRationalApprox(x BigDec) BigDec { + if x.LT(ZeroBigDec()) || x.GT(OneBigDec()) { + panic(fmt.Sprintf("exponent must be in the range [0, 1], got %s", x)) + } + if x.IsZero() { + return OneBigDec() + } + if x.Equal(OneBigDec()) { + return twoBigDec + } + + h_x := numeratorCoefficients13Param[0].Clone() + p_x := denominatorCoefficients13Param[0].Clone() + x_exp_i := OneBigDec() + for i := 1; i < len(numeratorCoefficients13Param); i++ { + x_exp_i.MulMut(x) + + h_x.AddMut(numeratorCoefficients13Param[i].Mul(x_exp_i)) + p_x.AddMut(denominatorCoefficients13Param[i].Mul(x_exp_i)) + } + + return h_x.QuoMut(p_x) +} diff --git a/tests/interchaintest/osmosis/osmomath/int.go b/tests/interchaintest/osmosis/osmomath/int.go new file mode 100644 index 00000000..25333409 --- /dev/null +++ b/tests/interchaintest/osmosis/osmomath/int.go @@ -0,0 +1,440 @@ +package osmomath + +import ( + "encoding" + "encoding/json" + "fmt" + "math/big" + "testing" +) + +const maxBitLen = 1024 + +func newIntegerFromString(s string) (*big.Int, bool) { + return new(big.Int).SetString(s, 0) +} + +func equal(i *big.Int, i2 *big.Int) bool { return i.Cmp(i2) == 0 } + +// Greater than +func gt(i *big.Int, i2 *big.Int) bool { return i.Cmp(i2) == 1 } + +// Greater than or equal to +func gte(i *big.Int, i2 *big.Int) bool { return i.Cmp(i2) >= 0 } + +// Less than +func lt(i *big.Int, i2 *big.Int) bool { return i.Cmp(i2) == -1 } + +// Less than or equal to +func lte(i *big.Int, i2 *big.Int) bool { return i.Cmp(i2) <= 0 } + +func add(i *big.Int, i2 *big.Int) *big.Int { return new(big.Int).Add(i, i2) } + +func sub(i *big.Int, i2 *big.Int) *big.Int { return new(big.Int).Sub(i, i2) } + +func mul(i *big.Int, i2 *big.Int) *big.Int { return new(big.Int).Mul(i, i2) } + +func div(i *big.Int, i2 *big.Int) *big.Int { return new(big.Int).Quo(i, i2) } + +func mod(i *big.Int, i2 *big.Int) *big.Int { return new(big.Int).Mod(i, i2) } + +func neg(i *big.Int) *big.Int { return new(big.Int).Neg(i) } + +func abs(i *big.Int) *big.Int { return new(big.Int).Abs(i) } + +func min(i *big.Int, i2 *big.Int) *big.Int { + if i.Cmp(i2) == 1 { + return new(big.Int).Set(i2) + } + + return new(big.Int).Set(i) +} + +func max(i *big.Int, i2 *big.Int) *big.Int { + if i.Cmp(i2) == -1 { + return new(big.Int).Set(i2) + } + + return new(big.Int).Set(i) +} + +func unmarshalText(i *big.Int, text string) error { + if err := i.UnmarshalText([]byte(text)); err != nil { + return err + } + + if i.BitLen() > maxBitLen { + return fmt.Errorf("integer out of range: %s", text) + } + + return nil +} + +// Int wraps big.Int with a 257 bit range bound +// Checks overflow, underflow and division by zero +// Exists in range from -(2^256 - 1) to 2^256 - 1 +type BigInt struct { + i *big.Int +} + +// BigInt converts Int to big.Int +func (i BigInt) BigInt() *big.Int { + if i.IsNil() { + return nil + } + return new(big.Int).Set(i.i) +} + +// IsNil returns true if Int is uninitialized +func (i BigInt) IsNil() bool { + return i.i == nil +} + +// NewBigInt constructs Int from int64 +func NewBigInt(n int64) BigInt { + return BigInt{big.NewInt(n)} +} + +// NewBigIntFromUint64 constructs an Int from a uint64. +func NewBigIntFromUint64(n uint64) BigInt { + b := big.NewInt(0) + b.SetUint64(n) + return BigInt{b} +} + +// NewBigIntFromBigInt constructs Int from big.Int. If the provided big.Int is nil, +// it returns an empty instance. This function panics if the bit length is > 256. +func NewBigIntFromBigInt(i *big.Int) BigInt { + if i == nil { + return BigInt{} + } + + if i.BitLen() > maxBitLen { + panic("NewIntFromBigInt() out of bound") + } + return BigInt{i} +} + +// NewBigIntFromString constructs Int from string +func NewBigIntFromString(s string) (res BigInt, ok bool) { + i, ok := newIntegerFromString(s) + if !ok { + return + } + // Check overflow + if i.BitLen() > maxBitLen { + ok = false + return + } + return BigInt{i}, true +} + +// NewBigIntWithDecimal constructs Int with decimal +// Result value is n*10^dec +func NewBigIntWithDecimal(n int64, dec int) BigInt { + if dec < 0 { + panic("NewIntWithDecimal() decimal is negative") + } + exp := new(big.Int).Exp(big.NewInt(10), big.NewInt(int64(dec)), nil) + i := new(big.Int) + i.Mul(big.NewInt(n), exp) + + // Check overflow + if i.BitLen() > maxBitLen { + panic("NewIntWithDecimal() out of bound") + } + return BigInt{i} +} + +// ZeroBigInt returns Int value with zero +func ZeroBigInt() BigInt { return BigInt{big.NewInt(0)} } + +// OneBigInt returns Int value with one +func OneBigInt() BigInt { return BigInt{big.NewInt(1)} } + +// ToDec converts Int to Dec +func (i BigInt) ToDec() BigDec { + return NewBigDecFromInt(i) +} + +// Int64 converts Int to int64 +// Panics if the value is out of range +func (i BigInt) Int64() int64 { + if !i.i.IsInt64() { + panic("Int64() out of bound") + } + return i.i.Int64() +} + +// IsInt64 returns true if Int64() not panics +func (i BigInt) IsInt64() bool { + return i.i.IsInt64() +} + +// Uint64 converts Int to uint64 +// Panics if the value is out of range +func (i BigInt) Uint64() uint64 { + if !i.i.IsUint64() { + panic("Uint64() out of bounds") + } + return i.i.Uint64() +} + +// IsUint64 returns true if Uint64() not panics +func (i BigInt) IsUint64() bool { + return i.i.IsUint64() +} + +// IsZero returns true if Int is zero +func (i BigInt) IsZero() bool { + return i.i.Sign() == 0 +} + +// IsNegative returns true if Int is negative +func (i BigInt) IsNegative() bool { + return i.i.Sign() == -1 +} + +// IsPositive returns true if Int is positive +func (i BigInt) IsPositive() bool { + return i.i.Sign() == 1 +} + +// Sign returns sign of Int +func (i BigInt) Sign() int { + return i.i.Sign() +} + +// Equal compares two Ints +func (i BigInt) Equal(i2 BigInt) bool { + return equal(i.i, i2.i) +} + +// GT returns true if first Int is greater than second +func (i BigInt) GT(i2 BigInt) bool { + return gt(i.i, i2.i) +} + +// GTE returns true if receiver Int is greater than or equal to the parameter +// Int. +func (i BigInt) GTE(i2 BigInt) bool { + return gte(i.i, i2.i) +} + +// LT returns true if first Int is lesser than second +func (i BigInt) LT(i2 BigInt) bool { + return lt(i.i, i2.i) +} + +// LTE returns true if first Int is less than or equal to second +func (i BigInt) LTE(i2 BigInt) bool { + return lte(i.i, i2.i) +} + +// Add adds Int from another +func (i BigInt) Add(i2 BigInt) (res BigInt) { + res = BigInt{add(i.i, i2.i)} + // Check overflow + if res.i.BitLen() > maxBitLen { + panic("Int overflow") + } + return +} + +// AddRaw adds int64 to Int +func (i BigInt) AddRaw(i2 int64) BigInt { + return i.Add(NewBigInt(i2)) +} + +// Sub subtracts Int from another +func (i BigInt) Sub(i2 BigInt) (res BigInt) { + res = BigInt{sub(i.i, i2.i)} + // Check overflow + if res.i.BitLen() > maxBitLen { + panic("Int overflow") + } + return +} + +// SubRaw subtracts int64 from Int +func (i BigInt) SubRaw(i2 int64) BigInt { + return i.Sub(NewBigInt(i2)) +} + +// Mul multiples two Ints +func (i BigInt) Mul(i2 BigInt) (res BigInt) { + // Check overflow + if i.i.BitLen()+i2.i.BitLen()-1 > maxBitLen { + panic("Int overflow") + } + res = BigInt{mul(i.i, i2.i)} + // Check overflow if sign of both are same + if res.i.BitLen() > maxBitLen { + panic("Int overflow") + } + return +} + +// MulRaw multiplies Int and int64 +func (i BigInt) MulRaw(i2 int64) BigInt { + return i.Mul(NewBigInt(i2)) +} + +// Quo divides Int with Int +func (i BigInt) Quo(i2 BigInt) (res BigInt) { + // Check division-by-zero + if i2.i.Sign() == 0 { + panic("Division by zero") + } + return BigInt{div(i.i, i2.i)} +} + +// QuoRaw divides Int with int64 +func (i BigInt) QuoRaw(i2 int64) BigInt { + return i.Quo(NewBigInt(i2)) +} + +// Mod returns remainder after dividing with Int +func (i BigInt) Mod(i2 BigInt) BigInt { + if i2.Sign() == 0 { + panic("division-by-zero") + } + return BigInt{mod(i.i, i2.i)} +} + +// ModRaw returns remainder after dividing with int64 +func (i BigInt) ModRaw(i2 int64) BigInt { + return i.Mod(NewBigInt(i2)) +} + +// Neg negates Int +func (i BigInt) Neg() (res BigInt) { + return BigInt{neg(i.i)} +} + +// Abs returns the absolute value of Int. +func (i BigInt) Abs() BigInt { + return BigInt{abs(i.i)} +} + +// return the minimum of the ints +func MinBigInt(i1, i2 BigInt) BigInt { + return BigInt{min(i1.BigInt(), i2.BigInt())} +} + +// MaxBigInt returns the maximum between two integers. +func MaxBigInt(i, i2 BigInt) BigInt { + return BigInt{max(i.BigInt(), i2.BigInt())} +} + +// Human readable string +func (i BigInt) String() string { + return i.i.String() +} + +// MarshalJSON defines custom encoding scheme +func (i BigInt) MarshalJSON() ([]byte, error) { + if i.i == nil { // Necessary since default Uint initialization has i.i as nil + i.i = new(big.Int) + } + return marshalJSON(i.i) +} + +// UnmarshalJSON defines custom decoding scheme +func (i *BigInt) UnmarshalJSON(bz []byte) error { + if i.i == nil { // Necessary since default Int initialization has i.i as nil + i.i = new(big.Int) + } + return unmarshalJSON(i.i, bz) +} + +// MarshalJSON for custom encoding scheme +// Must be encoded as a string for JSON precision +func marshalJSON(i encoding.TextMarshaler) ([]byte, error) { + text, err := i.MarshalText() + if err != nil { + return nil, err + } + + return json.Marshal(string(text)) +} + +// UnmarshalJSON for custom decoding scheme +// Must be encoded as a string for JSON precision +func unmarshalJSON(i *big.Int, bz []byte) error { + var text string + if err := json.Unmarshal(bz, &text); err != nil { + return err + } + + return unmarshalText(i, text) +} + +// MarshalYAML returns the YAML representation. +func (i BigInt) MarshalYAML() (interface{}, error) { + return i.String(), nil +} + +// Marshal implements the gogo proto custom type interface. +func (i BigInt) Marshal() ([]byte, error) { + if i.i == nil { + i.i = new(big.Int) + } + return i.i.MarshalText() +} + +// MarshalTo implements the gogo proto custom type interface. +func (i *BigInt) MarshalTo(data []byte) (n int, err error) { + if i.i == nil { + i.i = new(big.Int) + } + if i.i.BitLen() == 0 { // The value 0 + copy(data, []byte{0x30}) + return 1, nil + } + + bz, err := i.Marshal() + if err != nil { + return 0, err + } + + copy(data, bz) + return len(bz), nil +} + +// Unmarshal implements the gogo proto custom type interface. +func (i *BigInt) Unmarshal(data []byte) error { + if len(data) == 0 { + i = nil + return nil + } + + if i.i == nil { + i.i = new(big.Int) + } + + if err := i.i.UnmarshalText(data); err != nil { + return err + } + + if i.i.BitLen() > maxBitLen { + return fmt.Errorf("integer out of range; got: %d, max: %d", i.i.BitLen(), maxBitLen) + } + + return nil +} + +// Size implements the gogo proto custom type interface. +func (i *BigInt) Size() int { + bz, _ := i.Marshal() + return len(bz) +} + +// Override Amino binary serialization by proxying to protobuf. +func (i BigInt) MarshalAmino() ([]byte, error) { return i.Marshal() } +func (i *BigInt) UnmarshalAmino(bz []byte) error { return i.Unmarshal(bz) } + +// intended to be used with require/assert: require.True(IntEq(...)) +func BigIntEq(t *testing.T, exp, got BigInt) (*testing.T, bool, string, string, string) { + return t, exp.Equal(got), "expected:\t%v\ngot:\t\t%v", exp.String(), got.String() +} diff --git a/tests/interchaintest/osmosis/osmomath/int_test.go b/tests/interchaintest/osmosis/osmomath/int_test.go new file mode 100644 index 00000000..c33e9a48 --- /dev/null +++ b/tests/interchaintest/osmosis/osmomath/int_test.go @@ -0,0 +1,469 @@ +package osmomath + +import ( + "fmt" + "math/big" + "math/rand" + "strconv" + "testing" + + "github.com/stretchr/testify/suite" +) + +type intTestSuite struct { + suite.Suite +} + +func TestIntTestSuite(t *testing.T) { + suite.Run(t, new(intTestSuite)) +} + +func (s *intTestSuite) SetupSuite() { + s.T().Parallel() +} + +func (s *intTestSuite) TestFromInt64() { + for n := 0; n < 20; n++ { + r := rand.Int63() + s.Require().Equal(r, NewBigInt(r).Int64()) + } +} + +func (s *intTestSuite) TestFromUint64() { + for n := 0; n < 20; n++ { + r := rand.Uint64() + s.Require().True(NewBigIntFromUint64(r).IsUint64()) + s.Require().Equal(r, NewBigIntFromUint64(r).Uint64()) + } +} + +func (s *intTestSuite) TestIntPanic() { + // Max Int = 2^1024-1 = 8.988466e+308 + // Min Int = -(2^1024-1) = -8.988466e+308 + s.Require().NotPanics(func() { NewBigIntWithDecimal(4, 307) }) + i1 := NewBigIntWithDecimal(4, 307) + s.Require().NotPanics(func() { NewBigIntWithDecimal(5, 307) }) + i2 := NewBigIntWithDecimal(5, 307) + s.Require().NotPanics(func() { NewBigIntWithDecimal(92, 306) }) + i3 := NewBigIntWithDecimal(92, 306) + + s.Require().Panics(func() { NewBigIntWithDecimal(2, 308) }) + s.Require().Panics(func() { NewBigIntWithDecimal(9, 340) }) + + // Overflow check + s.Require().NotPanics(func() { i1.Add(i1) }) + s.Require().NotPanics(func() { i2.Add(i2) }) + s.Require().Panics(func() { i3.Add(i3) }) + + s.Require().NotPanics(func() { i1.Sub(i1.Neg()) }) + s.Require().NotPanics(func() { i2.Sub(i2.Neg()) }) + s.Require().Panics(func() { i3.Sub(i3.Neg()) }) + + s.Require().Panics(func() { i1.Mul(i1) }) + s.Require().Panics(func() { i2.Mul(i2) }) + s.Require().Panics(func() { i3.Mul(i3) }) + + s.Require().Panics(func() { i1.Neg().Mul(i1.Neg()) }) + s.Require().Panics(func() { i2.Neg().Mul(i2.Neg()) }) + s.Require().Panics(func() { i3.Neg().Mul(i3.Neg()) }) + + // Underflow check + i3n := i3.Neg() + s.Require().NotPanics(func() { i3n.Sub(i1) }) + s.Require().NotPanics(func() { i3n.Sub(i2) }) + s.Require().Panics(func() { i3n.Sub(i3) }) + + s.Require().NotPanics(func() { i3n.Add(i1.Neg()) }) + s.Require().NotPanics(func() { i3n.Add(i2.Neg()) }) + s.Require().Panics(func() { i3n.Add(i3.Neg()) }) + + s.Require().Panics(func() { i1.Mul(i1.Neg()) }) + s.Require().Panics(func() { i2.Mul(i2.Neg()) }) + s.Require().Panics(func() { i3.Mul(i3.Neg()) }) + + // Bound check + intmax := NewBigIntFromBigInt(new(big.Int).Sub(new(big.Int).Exp(big.NewInt(2), big.NewInt(1024), nil), big.NewInt(1))) + intmin := intmax.Neg() + s.Require().NotPanics(func() { intmax.Add(ZeroBigInt()) }) + s.Require().NotPanics(func() { intmin.Sub(ZeroBigInt()) }) + s.Require().Panics(func() { intmax.Add(OneBigInt()) }) + s.Require().Panics(func() { intmin.Sub(OneBigInt()) }) + + s.Require().NotPanics(func() { NewBigIntFromBigInt(nil) }) + s.Require().True(NewBigIntFromBigInt(nil).IsNil()) + + // Division-by-zero check + s.Require().Panics(func() { i1.Quo(NewBigInt(0)) }) + + s.Require().NotPanics(func() { BigInt{}.BigInt() }) +} + +// Tests below uses randomness +// Since we are using *big.Int as underlying value +// and (U/)Int is immutable value(see TestImmutability(U/)Int) +// it is safe to use randomness in the tests +func (s *intTestSuite) TestIdentInt() { + for d := 0; d < 1000; d++ { + n := rand.Int63() + i := NewBigInt(n) + + ifromstr, ok := NewBigIntFromString(strconv.FormatInt(n, 10)) + s.Require().True(ok) + + cases := []int64{ + i.Int64(), + i.BigInt().Int64(), + ifromstr.Int64(), + NewBigIntFromBigInt(big.NewInt(n)).Int64(), + NewBigIntWithDecimal(n, 0).Int64(), + } + + for tcnum, tc := range cases { + s.Require().Equal(n, tc, "Int is modified during conversion. tc #%d", tcnum) + } + } +} + +func minint(i1, i2 int64) int64 { + if i1 < i2 { + return i1 + } + return i2 +} + +func maxint(i1, i2 int64) int64 { + if i1 > i2 { + return i1 + } + return i2 +} + +func (s *intTestSuite) TestArithInt() { + for d := 0; d < 1000; d++ { + n1 := int64(rand.Int31()) + i1 := NewBigInt(n1) + n2 := int64(rand.Int31()) + i2 := NewBigInt(n2) + + cases := []struct { + ires BigInt + nres int64 + }{ + {i1.Add(i2), n1 + n2}, + {i1.Sub(i2), n1 - n2}, + {i1.Mul(i2), n1 * n2}, + {i1.Quo(i2), n1 / n2}, + {i1.AddRaw(n2), n1 + n2}, + {i1.SubRaw(n2), n1 - n2}, + {i1.MulRaw(n2), n1 * n2}, + {i1.QuoRaw(n2), n1 / n2}, + {MinBigInt(i1, i2), minint(n1, n2)}, + {MaxBigInt(i1, i2), maxint(n1, n2)}, + {i1.Neg(), -n1}, + {i1.Abs(), n1}, + {i1.Neg().Abs(), n1}, + } + + for tcnum, tc := range cases { + s.Require().Equal(tc.nres, tc.ires.Int64(), "Int arithmetic operation does not match with int64 operation. tc #%d", tcnum) + } + } +} + +func (s *intTestSuite) TestCompInt() { + for d := 0; d < 1000; d++ { + n1 := int64(rand.Int31()) + i1 := NewBigInt(n1) + n2 := int64(rand.Int31()) + i2 := NewBigInt(n2) + + cases := []struct { + ires bool + nres bool + }{ + {i1.Equal(i2), n1 == n2}, + {i1.GT(i2), n1 > n2}, + {i1.LT(i2), n1 < n2}, + {i1.LTE(i2), n1 <= n2}, + } + + for tcnum, tc := range cases { + s.Require().Equal(tc.nres, tc.ires, "Int comparison operation does not match with int64 operation. tc #%d", tcnum) + } + } +} + +func randint() BigInt { + return NewBigInt(rand.Int63()) +} + +func (s *intTestSuite) TestImmutabilityAllInt() { + ops := []func(*BigInt){ + func(i *BigInt) { _ = i.Add(randint()) }, + func(i *BigInt) { _ = i.Sub(randint()) }, + func(i *BigInt) { _ = i.Mul(randint()) }, + func(i *BigInt) { _ = i.Quo(randint()) }, + func(i *BigInt) { _ = i.AddRaw(rand.Int63()) }, + func(i *BigInt) { _ = i.SubRaw(rand.Int63()) }, + func(i *BigInt) { _ = i.MulRaw(rand.Int63()) }, + func(i *BigInt) { _ = i.QuoRaw(rand.Int63()) }, + func(i *BigInt) { _ = i.Neg() }, + func(i *BigInt) { _ = i.Abs() }, + func(i *BigInt) { _ = i.IsZero() }, + func(i *BigInt) { _ = i.Sign() }, + func(i *BigInt) { _ = i.Equal(randint()) }, + func(i *BigInt) { _ = i.GT(randint()) }, + func(i *BigInt) { _ = i.LT(randint()) }, + func(i *BigInt) { _ = i.String() }, + } + + for i := 0; i < 1000; i++ { + n := rand.Int63() + ni := NewBigInt(n) + + for opnum, op := range ops { + op(&ni) + + s.Require().Equal(n, ni.Int64(), "Int is modified by operation. tc #%d", opnum) + s.Require().Equal(NewBigInt(n), ni, "Int is modified by operation. tc #%d", opnum) + } + } +} + +func (s *intTestSuite) TestEncodingTableInt() { + var i BigInt + + cases := []struct { + i BigInt + jsonBz []byte + rawBz []byte + }{ + { + NewBigInt(0), + []byte("\"0\""), + []byte{0x30}, + }, + { + NewBigInt(100), + []byte("\"100\""), + []byte{0x31, 0x30, 0x30}, + }, + { + NewBigInt(-100), + []byte("\"-100\""), + []byte{0x2d, 0x31, 0x30, 0x30}, + }, + { + NewBigInt(51842), + []byte("\"51842\""), + []byte{0x35, 0x31, 0x38, 0x34, 0x32}, + }, + { + NewBigInt(-51842), + []byte("\"-51842\""), + []byte{0x2d, 0x35, 0x31, 0x38, 0x34, 0x32}, + }, + { + NewBigInt(19513368), + []byte("\"19513368\""), + []byte{0x31, 0x39, 0x35, 0x31, 0x33, 0x33, 0x36, 0x38}, + }, + { + NewBigInt(-19513368), + []byte("\"-19513368\""), + []byte{0x2d, 0x31, 0x39, 0x35, 0x31, 0x33, 0x33, 0x36, 0x38}, + }, + { + NewBigInt(999999999999), + []byte("\"999999999999\""), + []byte{0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39}, + }, + { + NewBigInt(-999999999999), + []byte("\"-999999999999\""), + []byte{0x2d, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39}, + }, + } + + for tcnum, tc := range cases { + bz, err := tc.i.MarshalJSON() + s.Require().Nil(err, "Error marshaling Int. tc #%d, err %s", tcnum, err) + s.Require().Equal(tc.jsonBz, bz, "Marshaled value is different from exported. tc #%d", tcnum) + + err = (&i).UnmarshalJSON(bz) + s.Require().Nil(err, "Error unmarshaling Int. tc #%d, err %s", tcnum, err) + s.Require().Equal(tc.i, i, "Unmarshaled value is different from exported. tc #%d", tcnum) + + bz, err = tc.i.Marshal() + s.Require().Nil(err, "Error marshaling Int. tc #%d, err %s", tcnum, err) + s.Require().Equal(tc.rawBz, bz, "Marshaled value is different from exported. tc #%d", tcnum) + + err = (&i).Unmarshal(bz) + s.Require().Nil(err, "Error unmarshaling Int. tc #%d, err %s", tcnum, err) + s.Require().Equal(tc.i, i, "Unmarshaled value is different from exported. tc #%d", tcnum) + } +} + +func (s *intTestSuite) TestEncodingTableUint() { + var i Uint + + cases := []struct { + i Uint + jsonBz []byte + rawBz []byte + }{ + { + NewUint(0), + []byte("\"0\""), + []byte{0x30}, + }, + { + NewUint(100), + []byte("\"100\""), + []byte{0x31, 0x30, 0x30}, + }, + { + NewUint(51842), + []byte("\"51842\""), + []byte{0x35, 0x31, 0x38, 0x34, 0x32}, + }, + { + NewUint(19513368), + []byte("\"19513368\""), + []byte{0x31, 0x39, 0x35, 0x31, 0x33, 0x33, 0x36, 0x38}, + }, + { + NewUint(999999999999), + []byte("\"999999999999\""), + []byte{0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39}, + }, + } + + for tcnum, tc := range cases { + bz, err := tc.i.MarshalJSON() + s.Require().Nil(err, "Error marshaling Int. tc #%d, err %s", tcnum, err) + s.Require().Equal(tc.jsonBz, bz, "Marshaled value is different from exported. tc #%d", tcnum) + + err = (&i).UnmarshalJSON(bz) + s.Require().Nil(err, "Error unmarshaling Int. tc #%d, err %s", tcnum, err) + s.Require().Equal(tc.i, i, "Unmarshaled value is different from exported. tc #%d", tcnum) + + bz, err = tc.i.Marshal() + s.Require().Nil(err, "Error marshaling Int. tc #%d, err %s", tcnum, err) + s.Require().Equal(tc.rawBz, bz, "Marshaled value is different from exported. tc #%d", tcnum) + + err = (&i).Unmarshal(bz) + s.Require().Nil(err, "Error unmarshaling Int. tc #%d, err %s", tcnum, err) + s.Require().Equal(tc.i, i, "Unmarshaled value is different from exported. tc #%d", tcnum) + } +} + +func (s *intTestSuite) TestIntMod() { + tests := []struct { + name string + x int64 + y int64 + ret int64 + wantPanic bool + }{ + {"3 % 10", 3, 10, 3, false}, + {"10 % 3", 10, 3, 1, false}, + {"4 % 2", 4, 2, 0, false}, + {"2 % 0", 2, 0, 0, true}, + } + + for _, tt := range tests { + if tt.wantPanic { + s.Require().Panics(func() { NewBigInt(tt.x).Mod(NewBigInt(tt.y)) }) + s.Require().Panics(func() { NewBigInt(tt.x).ModRaw(tt.y) }) + return + } + s.Require().True(NewBigInt(tt.x).Mod(NewBigInt(tt.y)).Equal(NewBigInt(tt.ret))) + s.Require().True(NewBigInt(tt.x).ModRaw(tt.y).Equal(NewBigInt(tt.ret))) + } +} + +func (s *intTestSuite) TestIntEq() { + _, resp, _, _, _ := BigIntEq(s.T(), ZeroBigInt(), ZeroBigInt()) + s.Require().True(resp) + _, resp, _, _, _ = BigIntEq(s.T(), OneBigInt(), ZeroBigInt()) + s.Require().False(resp) +} + +func TestRoundTripMarshalToInt(t *testing.T) { + values := []int64{ + 0, + 1, + 1 << 10, + 1<<10 - 3, + 1<<63 - 1, + 1<<32 - 7, + 1<<22 - 8, + } + + for _, value := range values { + value := value + t.Run(fmt.Sprintf("%d", value), func(t *testing.T) { + t.Parallel() + + var scratch [20]byte + iv := NewBigInt(value) + n, err := iv.MarshalTo(scratch[:]) + if err != nil { + t.Fatal(err) + } + rt := new(BigInt) + if err := rt.Unmarshal(scratch[:n]); err != nil { + t.Fatal(err) + } + if !rt.Equal(iv) { + t.Fatalf("roundtrip=%q != original=%q", rt, iv) + } + }) + } +} + +func (s *intTestSuite) TestEncodingRandom() { + for i := 0; i < 1000; i++ { + n := rand.Int63() + ni := NewBigInt(n) + var ri BigInt + + str, err := ni.Marshal() + s.Require().Nil(err) + err = (&ri).Unmarshal(str) + s.Require().Nil(err) + + s.Require().Equal(ni, ri, "binary mismatch; tc #%d, expected %s, actual %s", i, ni.String(), ri.String()) + s.Require().True(ni.i != ri.i, "pointer addresses are equal; tc #%d", i) + + bz, err := ni.MarshalJSON() + s.Require().Nil(err) + err = (&ri).UnmarshalJSON(bz) + s.Require().Nil(err) + + s.Require().Equal(ni, ri, "json mismatch; tc #%d, expected %s, actual %s", i, ni.String(), ri.String()) + s.Require().True(ni.i != ri.i, "pointer addresses are equal; tc #%d", i) + } + + for i := 0; i < 1000; i++ { + n := rand.Uint64() + ni := NewUint(n) + + var ri Uint + + str, err := ni.Marshal() + s.Require().Nil(err) + err = (&ri).Unmarshal(str) + s.Require().Nil(err) + + s.Require().Equal(ni, ri, "binary mismatch; tc #%d, expected %s, actual %s", i, ni.String(), ri.String()) + + bz, err := ni.MarshalJSON() + s.Require().Nil(err) + err = (&ri).UnmarshalJSON(bz) + s.Require().Nil(err) + + s.Require().Equal(ni, ri, "json mismatch; tc #%d, expected %s, actual %s", i, ni.String(), ri.String()) + } +} diff --git a/tests/interchaintest/osmosis/osmomath/math.go b/tests/interchaintest/osmosis/osmomath/math.go new file mode 100644 index 00000000..6da8d5e4 --- /dev/null +++ b/tests/interchaintest/osmosis/osmomath/math.go @@ -0,0 +1,205 @@ +package osmomath + +import ( + "fmt" +) + +// Don't EVER change after initializing +// TODO: Analyze choice here. +var powPrecision, _ = NewDecFromStr("0.00000001") + +const powIterationLimit = 150_000 + +var ( + one_half Dec = MustNewDecFromStr("0.5") + one Dec = OneDec() + two Dec = MustNewDecFromStr("2") + ten Dec = MustNewDecFromStr("10") + + // https://www.wolframalpha.com/input?i=2.718281828459045235360287471352662498&assumption=%22ClashPrefs%22+-%3E+%7B%22Math%22%7D + // nolint: unused + eulersNumber = MustNewBigDecFromStr("2.718281828459045235360287471352662498") +) + +// Returns the internal "power precision". +// All fractional exponentiation in osmosis is expected to be accurate up to +// powPrecision. +// *technically* the error term can be greater than this powPrecision, +// but for small bases this bound applies. See comments in the PowApprox function +// for more detail. +func GetPowPrecision() Dec { + return powPrecision.Clone() +} + +/*********************************************************/ + +// AbsDifferenceWithSign returns | a - b |, (a - b).sign() +// a is mutated and returned. +func AbsDifferenceWithSign(a, b Dec) (Dec, bool) { + if a.GTE(b) { + return a.SubMut(b), false + } else { + return a.NegMut().AddMut(b), true + } +} + +// func largeBasePow(base Dec, exp Dec) Dec { +// // pow requires the base to be <= 2 +// } + +// Pow computes base^(exp) +// However since the exponent is not an integer, we must do an approximation algorithm. +// TODO: In the future, lets add some optimized routines for common exponents, e.g. for common wIn / wOut ratios +// Many simple exponents like 2:1 pools. +func Pow(base Dec, exp Dec) Dec { + // Exponentiation of a negative base with an arbitrary real exponent is not closed within the reals. + // You can see this by recalling that `i = (-1)^(.5)`. We have to go to complex numbers to define this. + // (And would have to implement complex logarithms) + // We don't have a need for negative bases, so we don't include any such logic. + if !base.IsPositive() { + panic(fmt.Errorf("base must be greater than 0")) + } + // TODO: Remove this if we want to generalize the function, + // we can adjust the algorithm in this setting. + if base.GTE(two) { + panic(fmt.Errorf("base must be lesser than two")) + } + + // We will use an approximation algorithm to compute the power. + // Since computing an integer power is easy, we split up the exponent into + // an integer component and a fractional component. + integer := exp.TruncateDec() + fractional := exp.Sub(integer) + + integerPow := base.Power(uint64(integer.TruncateInt64())) + + if fractional.IsZero() { + return integerPow + } + + fractionalPow := PowApprox(base, fractional, powPrecision) + + return integerPow.Mul(fractionalPow) +} + +// Contract: 0 < base <= 2 +// 0 <= exp < 1. +func PowApprox(originalBase Dec, exp Dec, precision Dec) Dec { + if !originalBase.IsPositive() { + panic(fmt.Errorf("base must be greater than 0")) + } + + if exp.IsZero() { + return OneDec() + } + + // Common case optimization + // Optimize for it being equal to one-half + if exp.Equal(one_half) { + output, err := originalBase.ApproxSqrt() + if err != nil { + panic(err) + } + return output + } + // TODO: Make an approx-equal function, and then check if exp * 3 = 1, and do a check accordingly + + // We compute this via taking the maclaurin series of (1 + x)^a + // where x = base - 1. + // The maclaurin series of (1 + x)^a = sum_{k=0}^{infty} binom(a, k) x^k + // Binom(a, k) takes the natural continuation on the first parameter, namely that + // Binom(a, k) = N/D, where D = k!, and N = a(a-1)(a-2)...(a-k+1) + // Next we show that the absolute value of each term is less than the last term. + // Note that the change in term n's value vs term n + 1 is a multiplicative factor of + // v_n = x(a - n) / (n+1) + // So if |v_n| < 1, we know that each term has a lesser impact on the result than the last. + // For our bounds on |x| < 1, |a| < 1, + // it suffices to see for what n is |v_n| < 1, + // in the worst parameterization of x = 1, a = -1. + // v_n = |(-1 + epsilon - n) / (n+1)| + // So |v_n| is always less than 1, as n ranges over the integers. + // + // Note that term_n of the expansion is 1 * prod_{i=0}^{n-1} v_i + // The error if we stop the expansion at term_n is: + // error_n = sum_{k=n+1}^{infty} term_k + // At this point we further restrict a >= 0, so 0 <= a < 1. + // Now we take the _INCORRECT_ assumption that if term_n < p, then + // error_n < p. + // This assumption is obviously wrong. + // However our usages of this function don't use the full domain. + // With a > 0, |x| << 1, and p sufficiently low, perhaps this actually is true. + + // TODO: Check with our parameterization + // TODO: If there's a bug, balancer is also wrong here :thonk: + + base := originalBase.Clone() + x, xneg := AbsDifferenceWithSign(base, one) + term := OneDec() + sum := OneDec() + negative := false + + a := exp.Clone() + bigK := NewDec(0) + // TODO: Document this computation via taylor expansion + for i := int64(1); term.GTE(precision); i++ { + // At each iteration, we need two values, i and i-1. + // To avoid expensive big.Int allocation, we reuse bigK variable. + // On this line, bigK == i-1. + c, cneg := AbsDifferenceWithSign(a, bigK) + // On this line, bigK == i. + bigK.SetInt64(i) + term.MulMut(c).MulMut(x).QuoMut(bigK) + + // a is mutated on absDifferenceWithSign, reset + a.Set(exp) + + if term.IsZero() { + break + } + if xneg { + negative = !negative + } + + if cneg { + negative = !negative + } + + if negative { + sum.SubMut(term) + } else { + sum.AddMut(term) + } + + if i == powIterationLimit { + panic(fmt.Errorf("failed to reach precision within %d iterations, best guess: %s for %s^%s", powIterationLimit, sum, originalBase, exp)) + } + } + return sum +} + +// OrderOfMagnitude calculates the order of magnitude without using logarithms. +// CONTRACT: num must be positive or zero. Panics if not +func OrderOfMagnitude(num Dec) int { + if num.IsZero() { + return 0 + } + if !num.IsPositive() { + panic(fmt.Errorf("num must be positive or zero, was (%s)", num)) + } + + // Make a copy so we don't mutate the original + num = num.Clone() + + order := 0 + for num.GTE(ten) { + num.QuoMut(ten) + order++ + } + + for num.LT(one) { + num.MulMut(ten) + order-- + } + + return order +} diff --git a/tests/interchaintest/osmosis/osmomath/rounding_direction.go b/tests/interchaintest/osmosis/osmomath/rounding_direction.go new file mode 100644 index 00000000..a4cc4764 --- /dev/null +++ b/tests/interchaintest/osmosis/osmomath/rounding_direction.go @@ -0,0 +1,44 @@ +package osmomath + +import ( + "errors" + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +type RoundingDirection int + +const ( + RoundUnconstrained RoundingDirection = 0 + RoundUp RoundingDirection = 1 + RoundDown RoundingDirection = 2 + RoundBankers RoundingDirection = 3 +) + +func DivIntByU64ToBigDec(i Int, u uint64, round RoundingDirection) (BigDec, error) { + if u == 0 { + return BigDec{}, errors.New("div by zero") + } + d := BigDecFromDec(i.ToLegacyDec()) + if round == RoundUp { + return d.QuoRoundUp(NewBigDec(int64(u))), nil + } else if round == RoundDown { + return d.QuoInt64(int64(u)), nil + } else if round == RoundBankers { + return d.Quo(NewBigDec(int64(u))), nil + } + return BigDec{}, fmt.Errorf("invalid rounding mode %d", int(round)) +} + +func DivCoinAmtsByU64ToBigDec(coins []sdk.Coin, scales []uint64, round RoundingDirection) ([]BigDec, error) { + result := make([]BigDec, len(coins)) + for i, coin := range coins { + res, err := DivIntByU64ToBigDec(coin.Amount, scales[i], round) + if err != nil { + return nil, err + } + result[i] = res + } + return result, nil +} diff --git a/tests/interchaintest/osmosis/osmomath/sdk_math_alias.go b/tests/interchaintest/osmosis/osmomath/sdk_math_alias.go new file mode 100644 index 00000000..5dcdac1f --- /dev/null +++ b/tests/interchaintest/osmosis/osmomath/sdk_math_alias.go @@ -0,0 +1,55 @@ +// This file creates type and function aliases the sdkmath.LegacyDec +// This is done for reducing verbosity and improving readability. +// +// For consistency, we also alias Int and Uint so that sdkmath does not have +// to be directly imported in files where both decimal and integer types are used. +package osmomath + +import ( + sdkmath "cosmossdk.io/math" +) + +type ( + Dec = sdkmath.LegacyDec + Int = sdkmath.Int + Uint = sdkmath.Uint +) + +const ( + DecPrecision = sdkmath.LegacyPrecision + DecimalPrecisionBits = sdkmath.LegacyDecimalPrecisionBits +) + +var ( + // Dec + NewDec = sdkmath.LegacyNewDec + NewDecWithPrec = sdkmath.LegacyNewDecWithPrec + NewDecFromBigInt = sdkmath.LegacyNewDecFromBigInt + NewDecFromBigIntWithPrec = sdkmath.LegacyNewDecFromBigIntWithPrec + NewDecFromInt = sdkmath.LegacyNewDecFromInt + NewDecFromIntWithPrec = sdkmath.LegacyNewDecFromIntWithPrec + NewDecFromStr = sdkmath.LegacyNewDecFromStr + MustNewDecFromStr = sdkmath.LegacyMustNewDecFromStr + ZeroDec = sdkmath.LegacyZeroDec + OneDec = sdkmath.LegacyOneDec + SmallestDec = sdkmath.LegacySmallestDec + + // Int + NewInt = sdkmath.NewInt + NewIntFromUint64 = sdkmath.NewIntFromUint64 + NewIntFromBigInt = sdkmath.NewIntFromBigInt + NewIntFromString = sdkmath.NewIntFromString + NewIntWithDecimal = sdkmath.NewIntWithDecimal + ZeroInt = sdkmath.ZeroInt + OneInt = sdkmath.OneInt + IntEq = sdkmath.IntEq + MinInt = sdkmath.MinInt + MaxInt = sdkmath.MaxInt + + // Uint + NewUint = sdkmath.NewUint + NewUintFromString = sdkmath.NewUintFromString + + MinDec = sdkmath.LegacyMinDec + MaxDec = sdkmath.LegacyMaxDec +) diff --git a/tests/interchaintest/osmosis/osmomath/sigfig_round.go b/tests/interchaintest/osmosis/osmomath/sigfig_round.go new file mode 100644 index 00000000..26ccbaa1 --- /dev/null +++ b/tests/interchaintest/osmosis/osmomath/sigfig_round.go @@ -0,0 +1,27 @@ +package osmomath + +var pointOne = OneDec().QuoInt64(10) + +// SigFigRound rounds to a specified significant figure. +func SigFigRound(d Dec, tenToSigFig Int) Dec { + if d.IsZero() { + return d + } + // for d > .1, we do round(d * 10^sigfig) / 10^sigfig + // for k, where 10^k*d > .1 && 10^{k-1}*d < .1, we do: + // (round(10^k * d * 10^sigfig) / (10^sigfig * 10^k) + // take note of floor div, vs normal div + k := uint64(0) + dTimesK := d + for ; dTimesK.LT(pointOne); k += 1 { + dTimesK.MulInt64Mut(10) + } + // d * 10^k * 10^sigfig + dkSigFig := dTimesK.MulInt(tenToSigFig) + numerator := dkSigFig.RoundInt().ToLegacyDec() + + // TODO: Use pre-computed table for 10^k + tenToK := NewInt(10).ToLegacyDec().Power(k) + denominator := tenToSigFig.Mul(tenToK.TruncateInt()) + return numerator.QuoIntMut(denominator) +} diff --git a/tests/interchaintest/osmosis/osmomath/sqrt.go b/tests/interchaintest/osmosis/osmomath/sqrt.go new file mode 100644 index 00000000..9750657e --- /dev/null +++ b/tests/interchaintest/osmosis/osmomath/sqrt.go @@ -0,0 +1,98 @@ +package osmomath + +import ( + "errors" + "math/big" +) + +var smallestDec = SmallestDec() +var tenTo18 = big.NewInt(1e18) +var tenTo36 = big.NewInt(0).Mul(tenTo18, tenTo18) +var oneBigInt = big.NewInt(1) + +// Returns square root of d +// returns an error if one of the following conditions is met: +// - d is negative +// - d is too small to have a representable square root. +// This function guarantees: +// the returned root r, will be such that r^2 >= d +// This function is monotonic, i.e. if d1 >= d2, then sqrt(d1) >= sqrt(d2) +func MonotonicSqrt(d Dec) (Dec, error) { + if d.IsNegative() { + return d, errors.New("cannot take square root of negative number") + } + + // A decimal value of d, is represented as an integer of value v = 10^18 * d. + // We have an integer square root function, and we'd like to get the square root of d. + // recall integer square root is floor(sqrt(x)), hence its accurate up to 1 integer. + // we want sqrt d accurate to 18 decimal places. + // So first we multiply our current value by 10^18, then we take the integer square root. + // since sqrt(10^18 * v) = 10^9 * sqrt(v) = 10^18 * sqrt(d), we get the answer we want. + // + // We can than interpret sqrt(10^18 * v) as our resulting decimal and return it. + // monotonicity is guaranteed by correctness of integer square root. + dBi := d.BigInt() + r := big.NewInt(0).Mul(dBi, tenTo18) + r.Sqrt(r) + // However this square root r is s.t. r^2 <= d. We want to flip this to be r^2 >= d. + // To do so, we check that if r^2 < d, do r += 1. Then by correctness we will be in the case we want. + // To compare r^2 and d, we can just compare r^2 and 10^18 * v. (recall r = 10^18 * sqrt(d), v = 10^18 * d) + check := big.NewInt(0).Mul(r, r) + // dBi is a copy of d, so we can modify it. + shiftedD := dBi.Mul(dBi, tenTo18) + if check.Cmp(shiftedD) == -1 { + r.Add(r, oneBigInt) + } + root := NewDecFromBigIntWithPrec(r, 18) + + return root, nil +} + +func MonotonicSqrtBigDec(d BigDec) (BigDec, error) { + if d.IsNegative() { + return d, errors.New("cannot take square root of negative number") + } + + // A decimal value of d, is represented as an integer of value v = 10^18 * d. + // We have an integer square root function, and we'd like to get the square root of d. + // recall integer square root is floor(sqrt(x)), hence its accurate up to 1 integer. + // we want sqrt d accurate to 18 decimal places. + // So first we multiply our current value by 10^18, then we take the integer square root. + // since sqrt(10^18 * v) = 10^9 * sqrt(v) = 10^18 * sqrt(d), we get the answer we want. + // + // We can than interpret sqrt(10^18 * v) as our resulting decimal and return it. + // monotonicity is guaranteed by correctness of integer square root. + dBi := d.BigInt() + r := big.NewInt(0).Mul(dBi, tenTo36) + r.Sqrt(r) + // However this square root r is s.t. r^2 <= d. We want to flip this to be r^2 >= d. + // To do so, we check that if r^2 < d, do r += 1. Then by correctness we will be in the case we want. + // To compare r^2 and d, we can just compare r^2 and 10^18 * v. (recall r = 10^18 * sqrt(d), v = 10^18 * d) + check := big.NewInt(0).Mul(r, r) + // dBi is a copy of d, so we can modify it. + shiftedD := dBi.Mul(dBi, tenTo36) + if check.Cmp(shiftedD) == -1 { + r.Add(r, oneBigInt) + } + root := NewBigDecFromBigIntWithPrec(r, 36) + + return root, nil +} + +// MustMonotonicSqrt returns the output of MonotonicSqrt, panicking on error. +func MustMonotonicSqrt(d Dec) Dec { + sqrt, err := MonotonicSqrt(d) + if err != nil { + panic(err) + } + return sqrt +} + +// MustMonotonicSqrt returns the output of MonotonicSqrt, panicking on error. +func MustMonotonicSqrtBigDec(d BigDec) BigDec { + sqrt, err := MonotonicSqrtBigDec(d) + if err != nil { + panic(err) + } + return sqrt +} diff --git a/tests/interchaintest/osmosis/osmoutils/accum/accum.go b/tests/interchaintest/osmosis/osmoutils/accum/accum.go new file mode 100644 index 00000000..59ef051b --- /dev/null +++ b/tests/interchaintest/osmosis/osmoutils/accum/accum.go @@ -0,0 +1,442 @@ +package accum + +import ( + "errors" + "fmt" + "strings" + + "github.com/cosmos/cosmos-sdk/store" + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/osmosis-labs/fee-abstraction/v7/tests/interchaintest/osmosis/osmomath" + "github.com/osmosis-labs/fee-abstraction/v7/tests/interchaintest/osmosis/osmoutils" +) + +// We keep this object as a way to interface with the methods, even though +// only the Accumulator inside is stored in state +type AccumulatorObject struct { + // Store where accumulator is stored + store store.KVStore + + // Accumulator's name (pulled from AccumulatorContent) + name string + + // Accumulator's current valuePerShare (pulled from AccumulatorContent) + valuePerShare sdk.DecCoins + + // Accumulator's total shares across all positions + totalShares osmomath.Dec +} + +// Makes a new accumulator at store/accum/{accumName} +// Returns error if: +// * accumName already exists +// * there's some overlapping keys +// * Accumulator name contains "||" +func MakeAccumulator(accumStore store.KVStore, accumName string) error { + if accumStore.Has(formatAccumPrefixKey(accumName)) { + return errors.New("Accumulator with given name already exists in store") + } + + // New accumulator values start out at zero + // TODO: consider whether this should be a parameter instead of always zero + initAccumValue := sdk.NewDecCoins() + initTotalShares := osmomath.ZeroDec() + + newAccum := &AccumulatorObject{accumStore, accumName, initAccumValue, initTotalShares} + + // Stores accumulator in state + return setAccumulator(newAccum, initAccumValue, initTotalShares) +} + +// Makes a new accumulator at store/accum/{accumName} +// Returns error if: +// * accumName already exists +// * there's some overlapping keys +// * Accumulator name contains "||" +func MakeAccumulatorWithValueAndShare(accumStore store.KVStore, accumName string, accumValue sdk.DecCoins, totalShares osmomath.Dec) error { + if accumStore.Has(formatAccumPrefixKey(accumName)) { + return errors.New("Accumulator with given name already exists in store") + } + + newAccum := AccumulatorObject{accumStore, accumName, accumValue, totalShares} + + // Stores accumulator in state + return setAccumulator(&newAccum, accumValue, totalShares) +} + +// Gets the current value of the accumulator corresponding to accumName in accumStore +func GetAccumulator(accumStore store.KVStore, accumName string) (*AccumulatorObject, error) { + accumContent := AccumulatorContent{} + found, err := osmoutils.Get(accumStore, formatAccumPrefixKey(accumName), &accumContent) + if err != nil { + return &AccumulatorObject{}, err + } + if !found { + return &AccumulatorObject{}, AccumDoesNotExistError{AccumName: accumName} + } + + accum := AccumulatorObject{accumStore, accumName, accumContent.AccumValue, accumContent.TotalShares} + + return &accum, nil +} + +// MustGetPosition returns the position associated with the given address. No errors in position retrieval are allowed. +func (accum AccumulatorObject) MustGetPosition(name string) Record { + position := Record{} + osmoutils.MustGet(accum.store, FormatPositionPrefixKey(accum.name, name), &position) + return position +} + +// GetPosition returns the position associated with the given address. If the position does not exist, returns an error. +func (accum AccumulatorObject) GetPosition(name string) (Record, error) { + position := Record{} + found, err := osmoutils.Get(accum.store, FormatPositionPrefixKey(accum.name, name), &position) + if err != nil { + return Record{}, err + } + + if !found { + return Record{}, NoPositionError{Name: name} + } + return position, nil +} + +func setAccumulator(accum *AccumulatorObject, value sdk.DecCoins, shares osmomath.Dec) error { + if strings.Contains(accum.name, KeySeparator) { + return fmt.Errorf("Accumulator name cannot contain '%s', provided name %s", KeySeparator, accum.name) + } + newAccum := AccumulatorContent{value, shares} + osmoutils.MustSet(accum.store, formatAccumPrefixKey(accum.name), &newAccum) + return nil +} + +// AddToAccumulator updates the accumulator's value by amt. +// It does so by increasing the value of the accumulator by +// the given amount. Persists to store. Mutates the receiver. +func (accum *AccumulatorObject) AddToAccumulator(amt sdk.DecCoins) { + accum.valuePerShare = accum.valuePerShare.Add(amt...) + // its safe to ignore error here. + //nolint:errcheck + setAccumulator(accum, accum.valuePerShare, accum.totalShares) +} + +// NewPosition creates a new position for the given name, with the given number of share units. +// The name can be an owner's address, or any other unique identifier for a position. +// It takes a snapshot of the current accumulator value, and sets the position's initial value to that. +// The position is initialized with empty unclaimed rewards +// If there is an existing position for the given address, it is overwritten. +func (accum *AccumulatorObject) NewPosition(name string, numShareUnits osmomath.Dec, options *Options) error { + return accum.NewPositionIntervalAccumulation(name, numShareUnits, accum.valuePerShare, options) +} + +// NewPositionIntervalAccumulation creates a new position for the given name, with the given number of share units. +// The name can be an owner's address, or any other unique identifier for a position. +// It sets the position's accumulator to the given value of intervalAccumulationPerShare. +// This is useful for when the accumulation happens at a sub-range of the full accumulator +// rewards range. For example, a concentrated liquidity narrow range position. +// intervalAccumulationPerShare DecCoin values are allowed to be negative. +// The position is initialized with empty unclaimed rewards +// If there is an existing position for the given address, it is overwritten. +func (accum *AccumulatorObject) NewPositionIntervalAccumulation(name string, numShareUnits osmomath.Dec, intervalAccumulationPerShare sdk.DecCoins, options *Options) error { + if err := options.validate(); err != nil { + return err + } + + initOrUpdatePosition(accum, intervalAccumulationPerShare, name, numShareUnits, sdk.NewDecCoins(), options) + + // Update total shares in accum (re-fetch accum from state to ensure it's up to date) + updatedAccum, err := GetAccumulator(accum.store, accum.name) + if err != nil { + return err + } + accum.totalShares = updatedAccum.totalShares.Add(numShareUnits) + return setAccumulator(accum, accum.valuePerShare, accum.totalShares) +} + +// AddToPosition adds newShares of shares to an existing position with the given name. +// This is functionally equivalent to claiming rewards, closing down the position, and +// opening a fresh one with the new number of shares. We can represent this behavior by +// claiming rewards and moving up the accumulator start value to its current value. +// +// An alternative approach is to simply generate an additional position every time an +// address adds to its position. We do not pursue this path because we want to ensure +// that withdrawal and claiming functions remain constant time and do not scale with the +// number of times a user has added to their position. +// +// Returns nil on success. Returns error when: +// - newShares are negative or zero. +// - there is no existing position at the given address +// - other internal or database error occurs. +func (accum *AccumulatorObject) AddToPosition(name string, newShares osmomath.Dec) error { + return accum.AddToPositionIntervalAccumulation(name, newShares, accum.valuePerShare) +} + +// AddToPositionIntervalAccumulation adds newShares of shares to an existing position with the given name. +// This is functionally equivalent to claiming rewards, closing down the position, and +// opening a fresh one with the new number of shares. +// The accumulator of the new position is set to given intervalAccumulationPerShare. +// intervalAccumulationPerShare DecCoin values must be non-negative. They must also be a superset of the +// old accumulator value associated with the position. +// Providing intervalAccumulationPerShare is useful for when the accumulation happens at a sub-range of the full accumulator +// rewards range. For example, a concentrated liquidity narrow range position. +// +// An alternative approach is to simply generate an additional position every time an +// address adds to its position. We do not pursue this path because we want to ensure +// that withdrawal and claiming functions remain constant time and do not scale with the +// number of times a user has added to their position. +// +// Returns nil on success. Returns error when: +// - newShares are negative or zero. +// - there is no existing position at the given address +// - other internal or database error occurs. +func (accum *AccumulatorObject) AddToPositionIntervalAccumulation(name string, newShares osmomath.Dec, intervalAccumulationPerShare sdk.DecCoins) error { + if !newShares.IsPositive() { + return errors.New("Attempted to add zero or negative number of shares to a position") + } + + // Get addr's current position + position, err := GetPosition(accum, name) + if err != nil { + return err + } + + // Save current number of shares and unclaimed rewards + unclaimedRewards := GetTotalRewards(accum, position) + oldNumShares, err := accum.GetPositionSize(name) + if err != nil { + return err + } + + // Update user's position with new number of shares while moving its unaccrued rewards + // into UnclaimedRewards. Starting accumulator value is moved up to accum'scurrent value + initOrUpdatePosition(accum, intervalAccumulationPerShare, name, oldNumShares.Add(newShares), unclaimedRewards, position.Options) + + // Update total shares in accum (re-fetch accum from state to ensure it's up to date) + updatedAccum, err := GetAccumulator(accum.store, accum.name) + if err != nil { + return err + } + accum.totalShares = updatedAccum.totalShares.Add(newShares) + return setAccumulator(accum, accum.valuePerShare, accum.totalShares) +} + +// RemovePosition removes the specified number of shares from a position. Specifically, it claims +// the unclaimed and newly accrued rewards and returns them alongside the redeemed shares. Then, it +// overwrites the position record with the updated number of shares. Since it accrues rewards, it +// also moves up the position's accumulator value to the current accum val. +func (accum *AccumulatorObject) RemoveFromPosition(name string, numSharesToRemove osmomath.Dec) error { + return accum.RemoveFromPositionIntervalAccumulation(name, numSharesToRemove, accum.valuePerShare) +} + +// RemovePositionIntervalAccumulation removes the specified number of shares from a position. Specifically, it claims +// the unclaimed and newly accrued rewards and returns them alongside the redeemed shares. Then, it +// overwrites the position record with the updated number of shares. Since it accrues rewards, it +// also resets the position's accumulator value to the given intervalAccumulationPerShare. +// Providing intervalAccumulationPerShare is useful for when the accumulation happens at a sub-range of the full accumulator +// rewards range. For example, a concentrated liquidity narrow range position. +// All intervalAccumulationPerShare DecCoin values must be non-negative. They must also be a superset of the +// old accumulator value associated with the position. +func (accum *AccumulatorObject) RemoveFromPositionIntervalAccumulation(name string, numSharesToRemove osmomath.Dec, intervalAccumulationPerShare sdk.DecCoins) error { + // Cannot remove zero or negative shares + if !numSharesToRemove.IsPositive() { + return fmt.Errorf("Attempted to remove no/negative shares (%s)", numSharesToRemove) + } + + // Get addr's current position + position, err := GetPosition(accum, name) + if err != nil { + return err + } + + // Ensure not removing more shares than exist + if numSharesToRemove.GT(position.NumShares) { + return fmt.Errorf("Attempted to remove more shares (%s) than exist in the position (%s)", numSharesToRemove, position.NumShares) + } + + // Save current number of shares and unclaimed rewards + unclaimedRewards := GetTotalRewards(accum, position) + oldNumShares, err := accum.GetPositionSize(name) + if err != nil { + return err + } + + // Update user's position with new number of shares + initOrUpdatePosition(accum, intervalAccumulationPerShare, name, oldNumShares.Sub(numSharesToRemove), unclaimedRewards, position.Options) + + updatedAccum, err := GetAccumulator(accum.store, accum.name) + if err != nil { + return err + } + accum.totalShares = updatedAccum.totalShares.Sub(numSharesToRemove) + return setAccumulator(accum, accum.valuePerShare, accum.totalShares) +} + +// UpdatePosition updates the position with the given name by adding or removing +// the given number of shares. If numShares is positive, it is equivalent to calling +// AddToPosition. If numShares is negative, it is equivalent to calling RemoveFromPosition. +// Also, it moves up the position's accumulator value to the current accum value. +// Fails with error if numShares is zero. Returns nil on success. +func (accum *AccumulatorObject) UpdatePosition(name string, numShares osmomath.Dec) error { + return accum.UpdatePositionIntervalAccumulation(name, numShares, accum.valuePerShare) +} + +// UpdatePositionIntervalAccumulation updates the position with the given name by adding or removing +// the given number of shares. If numShares is positive, it is equivalent to calling +// AddToPositionIntervalAccumulation. If numShares is negative, it is equivalent to calling RemoveFromPositionIntervalAccumulation. +// Fails with error if numShares is zero. Returns nil on success. +// It also resets the position's accumulator value to the given intervalAccumulationPerShare. +// Providing intervalAccumulationPerShare is useful for when the accumulation happens at a sub-range of the full accumulator +// rewards range. For example, a concentrated liquidity narrow range position. +// All intervalAccumulationPerShare DecCoin value must be non-negative. They must also be a superset of the +// old accumulator value associated with the position. +func (accum *AccumulatorObject) UpdatePositionIntervalAccumulation(name string, numShares osmomath.Dec, intervalAccumulationPerShare sdk.DecCoins) error { + if numShares.IsZero() { + return ZeroSharesError + } + + if numShares.IsNegative() { + return accum.RemoveFromPositionIntervalAccumulation(name, numShares.Neg(), intervalAccumulationPerShare) + } + + return accum.AddToPositionIntervalAccumulation(name, numShares, intervalAccumulationPerShare) +} + +// SetPositionIntervalAccumulation sets the position's accumulator to the given value. +// This is useful for when the accumulation happens at a sub-range of the full accumulator +// rewards range. For example, a concentrated liquidity narrow range position. +// This method does not update shares or attempt to claim rewards. +// The new accumulator value must be greater than or equal to the old accumulator value. +// Returns nil on success, error otherwise. +func (accum *AccumulatorObject) SetPositionIntervalAccumulation(name string, intervalAccumulationPerShare sdk.DecCoins) error { + // Get addr's current position + position, err := GetPosition(accum, name) + if err != nil { + return err + } + + // Update the user's position with the new accumulator value. The unclaimed rewards, options, and + // the number of shares stays the same as in the original position. + initOrUpdatePosition(accum, intervalAccumulationPerShare, name, position.NumShares, position.UnclaimedRewardsTotal, position.Options) + + return nil +} + +// DeletePosition claims rewards and deletes the position from the accumulator state. +// Prior to deletion, claims rewards and returns them. Decrements total accumulator share +// counter by the number of shares in the position tracker. +// Returns error if: +// - fails to fetch a position +// - fails to claim rewards +// - fails to retrieve total accumulator shares +func (accum *AccumulatorObject) DeletePosition(positionName string) (sdk.DecCoins, error) { + position, err := accum.GetPosition(positionName) + if err != nil { + return sdk.DecCoins{}, err + } + + remainingRewards, dust, err := accum.ClaimRewards(positionName) + if err != nil { + return sdk.DecCoins{}, err + } + + accum.store.Delete(FormatPositionPrefixKey(accum.name, positionName)) + accum.totalShares.SubMut(position.NumShares) + err = setAccumulator(accum, accum.valuePerShare, accum.totalShares) + if err != nil { + return sdk.DecCoins{}, err + } + + return sdk.NewDecCoinsFromCoins(remainingRewards...).Add(dust...), nil +} + +// deletePosition deletes the position with the given name from state. +func (accum AccumulatorObject) deletePosition(positionName string) { + accum.store.Delete(FormatPositionPrefixKey(accum.name, positionName)) +} + +// GetPositionSize returns the number of shares the position with the given +// name has in the accumulator. Returns error if position does not exist +// or if fails to retrieve position from state. +func (accum *AccumulatorObject) GetPositionSize(name string) (osmomath.Dec, error) { + position, err := GetPosition(accum, name) + if err != nil { + return osmomath.Dec{}, err + } + + return position.NumShares, nil +} + +// HasPosition returns true if a position with the given name exists, +// false otherwise. +func (accum AccumulatorObject) HasPosition(name string) bool { + containsKey := accum.store.Has(FormatPositionPrefixKey(accum.name, name)) + return containsKey +} + +// GetValue returns the current value of the accumulator. +func (accum AccumulatorObject) GetName() string { + return accum.name +} + +// GetValue returns the current value of the accumulator. +func (accum AccumulatorObject) GetValue() sdk.DecCoins { + return accum.valuePerShare +} + +// ClaimRewards claims the rewards for the given address, and returns the amount of rewards claimed. +// Upon claiming the rewards, the position at the current address is reset to have no +// unclaimed rewards. The position's accumulator is also set to the current accumulator value. +// The position state is removed if the position shares is equal to zero. +// +// Returns error if +// - no position exists for the given address +// - any database errors occur. +func (accum *AccumulatorObject) ClaimRewards(positionName string) (sdk.Coins, sdk.DecCoins, error) { + position, err := GetPosition(accum, positionName) + if err != nil { + return sdk.Coins{}, sdk.DecCoins{}, NoPositionError{positionName} + } + + totalRewards := GetTotalRewards(accum, position) + + // Return the integer coins to the user + // The remaining change is thrown away. + // This is acceptable because we round in favor of the protocol. + truncatedRewardsTotal, dust := totalRewards.TruncateDecimal() + + if position.NumShares.IsZero() { + // remove the position from state entirely if numShares = zero + accum.deletePosition(positionName) + } else { + // else, update the position with no rewards + initOrUpdatePosition(accum, accum.valuePerShare, positionName, position.NumShares, sdk.NewDecCoins(), position.Options) + } + + return truncatedRewardsTotal, dust, nil +} + +// GetTotalShares returns the total number of shares in the accumulator +func (accum AccumulatorObject) GetTotalShares() osmomath.Dec { + return accum.totalShares +} + +// AddToUnclaimedRewards adds the given amount of rewards to the unclaimed rewards +// for the given position. Returns error if no position exists for the given position name. +// Returns error if any database errors occur or if neggative rewards are provided. +func (accum *AccumulatorObject) AddToUnclaimedRewards(positionName string, rewardsToAddTotal sdk.DecCoins) error { + position, err := GetPosition(accum, positionName) + if err != nil { + return err + } + + if rewardsToAddTotal.IsAnyNegative() { + return NegativeRewardsAdditionError{PositionName: positionName, AccumName: accum.name} + } + + // Update the user's position with the new unclaimed rewards. The accumulator, options, and + // the number of shares stays the same as in the original position. + initOrUpdatePosition(accum, position.AccumValuePerShare, positionName, position.NumShares, position.UnclaimedRewardsTotal.Add(rewardsToAddTotal...), position.Options) + + return nil +} diff --git a/tests/interchaintest/osmosis/osmoutils/accum/accum.pb.go b/tests/interchaintest/osmosis/osmoutils/accum/accum.pb.go new file mode 100644 index 00000000..cfb4cf29 --- /dev/null +++ b/tests/interchaintest/osmosis/osmoutils/accum/accum.pb.go @@ -0,0 +1,891 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: osmosis/accum/v1beta1/accum.proto + +package accum + +import ( + cosmossdk_io_math "cosmossdk.io/math" + fmt "fmt" + github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" + types "github.com/cosmos/cosmos-sdk/types" + _ "github.com/cosmos/gogoproto/gogoproto" + proto "github.com/cosmos/gogoproto/proto" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// AccumulatorContent is the state-entry for the global accumulator. +// It contains the name of the global accumulator and the total value of +// shares belonging to it from all positions. +type AccumulatorContent struct { + AccumValue github_com_cosmos_cosmos_sdk_types.DecCoins `protobuf:"bytes,1,rep,name=accum_value,json=accumValue,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.DecCoins" json:"accum_value"` + TotalShares cosmossdk_io_math.LegacyDec `protobuf:"bytes,2,opt,name=total_shares,json=totalShares,proto3,customtype=cosmossdk.io/math.LegacyDec" json:"total_shares"` +} + +func (m *AccumulatorContent) Reset() { *m = AccumulatorContent{} } +func (m *AccumulatorContent) String() string { return proto.CompactTextString(m) } +func (*AccumulatorContent) ProtoMessage() {} +func (*AccumulatorContent) Descriptor() ([]byte, []int) { + return fileDescriptor_4866f7c74a169dc2, []int{0} +} +func (m *AccumulatorContent) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *AccumulatorContent) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_AccumulatorContent.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *AccumulatorContent) XXX_Merge(src proto.Message) { + xxx_messageInfo_AccumulatorContent.Merge(m, src) +} +func (m *AccumulatorContent) XXX_Size() int { + return m.Size() +} +func (m *AccumulatorContent) XXX_DiscardUnknown() { + xxx_messageInfo_AccumulatorContent.DiscardUnknown(m) +} + +var xxx_messageInfo_AccumulatorContent proto.InternalMessageInfo + +func (m *AccumulatorContent) GetAccumValue() github_com_cosmos_cosmos_sdk_types.DecCoins { + if m != nil { + return m.AccumValue + } + return nil +} + +type Options struct { +} + +func (m *Options) Reset() { *m = Options{} } +func (m *Options) String() string { return proto.CompactTextString(m) } +func (*Options) ProtoMessage() {} +func (*Options) Descriptor() ([]byte, []int) { + return fileDescriptor_4866f7c74a169dc2, []int{1} +} +func (m *Options) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Options) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Options.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Options) XXX_Merge(src proto.Message) { + xxx_messageInfo_Options.Merge(m, src) +} +func (m *Options) XXX_Size() int { + return m.Size() +} +func (m *Options) XXX_DiscardUnknown() { + xxx_messageInfo_Options.DiscardUnknown(m) +} + +var xxx_messageInfo_Options proto.InternalMessageInfo + +// Record corresponds to an individual position value belonging to the +// global accumulator. +type Record struct { + // num_shares is the number of shares belonging to the position associated + // with this record. + NumShares cosmossdk_io_math.LegacyDec `protobuf:"bytes,1,opt,name=num_shares,json=numShares,proto3,customtype=cosmossdk.io/math.LegacyDec" json:"num_shares"` + // accum_value_per_share is the subset of coins per shar of the global + // accumulator value that allows to infer how much a position is entitled to + // per share that it owns. + // + // In the default case with no intervals, this value equals to the global + // accumulator value at the time of the position creation, the last update or + // reward claim. + // + // In the interval case such as concentrated liquidity, this value equals to + // the global growth of rewards inside the interval during one of: the time of + // the position creation, the last update or reward claim. Note, that + // immediately prior to claiming or updating rewards, this value must be + // updated to "the growth inside at the time of last update + the growth + // outside at the time of the current block". This is so that the claiming + // logic can subtract this updated value from the global accumulator value to + // get the growth inside the interval from the time of last update up until + // the current block time. + AccumValuePerShare github_com_cosmos_cosmos_sdk_types.DecCoins `protobuf:"bytes,2,rep,name=accum_value_per_share,json=accumValuePerShare,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.DecCoins" json:"accum_value_per_share"` + // unclaimed_rewards_total is the total amount of unclaimed rewards that the + // position is entitled to. This value is updated whenever shares are added or + // removed from an existing position. We also expose API for manually updating + // this value for some custom use cases such as merging pre-existing positions + // into a single one. + UnclaimedRewardsTotal github_com_cosmos_cosmos_sdk_types.DecCoins `protobuf:"bytes,3,rep,name=unclaimed_rewards_total,json=unclaimedRewardsTotal,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.DecCoins" json:"unclaimed_rewards_total"` + Options *Options `protobuf:"bytes,4,opt,name=options,proto3" json:"options,omitempty"` +} + +func (m *Record) Reset() { *m = Record{} } +func (m *Record) String() string { return proto.CompactTextString(m) } +func (*Record) ProtoMessage() {} +func (*Record) Descriptor() ([]byte, []int) { + return fileDescriptor_4866f7c74a169dc2, []int{2} +} +func (m *Record) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Record) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Record.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Record) XXX_Merge(src proto.Message) { + xxx_messageInfo_Record.Merge(m, src) +} +func (m *Record) XXX_Size() int { + return m.Size() +} +func (m *Record) XXX_DiscardUnknown() { + xxx_messageInfo_Record.DiscardUnknown(m) +} + +var xxx_messageInfo_Record proto.InternalMessageInfo + +func (m *Record) GetAccumValuePerShare() github_com_cosmos_cosmos_sdk_types.DecCoins { + if m != nil { + return m.AccumValuePerShare + } + return nil +} + +func (m *Record) GetUnclaimedRewardsTotal() github_com_cosmos_cosmos_sdk_types.DecCoins { + if m != nil { + return m.UnclaimedRewardsTotal + } + return nil +} + +func (m *Record) GetOptions() *Options { + if m != nil { + return m.Options + } + return nil +} + +func init() { + proto.RegisterType((*AccumulatorContent)(nil), "osmosis.accum.v1beta1.AccumulatorContent") + proto.RegisterType((*Options)(nil), "osmosis.accum.v1beta1.Options") + proto.RegisterType((*Record)(nil), "osmosis.accum.v1beta1.Record") +} + +func init() { proto.RegisterFile("osmosis/accum/v1beta1/accum.proto", fileDescriptor_4866f7c74a169dc2) } + +var fileDescriptor_4866f7c74a169dc2 = []byte{ + // 430 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x93, 0x3f, 0x6f, 0xd3, 0x40, + 0x18, 0xc6, 0x73, 0x0d, 0x6a, 0x95, 0x0b, 0xd3, 0x89, 0x08, 0xab, 0x20, 0x27, 0x84, 0x25, 0x12, + 0xea, 0x9d, 0xda, 0x2e, 0xac, 0xa4, 0x15, 0x12, 0x12, 0x12, 0xc8, 0x20, 0x06, 0x16, 0xeb, 0x7c, + 0x3e, 0x25, 0xa7, 0xda, 0xf7, 0x46, 0xf7, 0xa7, 0xa8, 0x3b, 0x0b, 0x1b, 0x2b, 0x5f, 0x81, 0x4f, + 0xd2, 0x31, 0x23, 0x62, 0x28, 0x28, 0xf9, 0x22, 0xc8, 0x77, 0x76, 0xe9, 0xc0, 0xc0, 0x92, 0xe9, + 0xfc, 0xda, 0xcf, 0xfb, 0x7b, 0x5e, 0x3d, 0x7e, 0x0f, 0x3f, 0x01, 0x5b, 0x83, 0x55, 0x96, 0x71, + 0x21, 0x7c, 0xcd, 0x2e, 0x8f, 0x0b, 0xe9, 0xf8, 0x71, 0xac, 0xe8, 0xca, 0x80, 0x03, 0x32, 0x6a, + 0x25, 0x34, 0xbe, 0x6c, 0x25, 0x87, 0x0f, 0x16, 0xb0, 0x80, 0xa0, 0x60, 0xcd, 0x53, 0x14, 0x1f, + 0xa6, 0x22, 0xa8, 0x59, 0xc1, 0xad, 0xbc, 0xa5, 0x09, 0x50, 0x3a, 0x7e, 0x9f, 0xae, 0x11, 0x26, + 0x2f, 0x1a, 0x8e, 0xaf, 0xb8, 0x03, 0x73, 0x06, 0xda, 0x49, 0xed, 0x88, 0xc1, 0xc3, 0x40, 0xcf, + 0x2f, 0x79, 0xe5, 0x65, 0x82, 0x26, 0xfd, 0xd9, 0xf0, 0xe4, 0x31, 0x8d, 0x30, 0xda, 0xc0, 0x3a, + 0x5f, 0x7a, 0x2e, 0xc5, 0x19, 0x28, 0x3d, 0x3f, 0xbd, 0xbe, 0x19, 0xf7, 0xbe, 0xff, 0x1a, 0x3f, + 0x5b, 0x28, 0xb7, 0xf4, 0x05, 0x15, 0x50, 0xb3, 0xd6, 0x3c, 0x1e, 0x47, 0xb6, 0xbc, 0x60, 0xee, + 0x6a, 0x25, 0x6d, 0xd7, 0x63, 0x33, 0x1c, 0x5c, 0x3e, 0x34, 0x26, 0xe4, 0x25, 0xbe, 0xef, 0xc0, + 0xf1, 0x2a, 0xb7, 0x4b, 0x6e, 0xa4, 0x4d, 0xf6, 0x26, 0x68, 0x36, 0x98, 0x3f, 0x6d, 0xb0, 0x3f, + 0x6f, 0xc6, 0x8f, 0x22, 0xc4, 0x96, 0x17, 0x54, 0x01, 0xab, 0xb9, 0x5b, 0xd2, 0xd7, 0x72, 0xc1, + 0xc5, 0xd5, 0xb9, 0x14, 0xd9, 0x30, 0x34, 0xbe, 0x0b, 0x7d, 0xd3, 0x01, 0x3e, 0x78, 0xb3, 0x72, + 0x0a, 0xb4, 0x9d, 0x7e, 0xeb, 0xe3, 0xfd, 0x4c, 0x0a, 0x30, 0x25, 0x99, 0x63, 0xac, 0x7d, 0xdd, + 0xb1, 0xd1, 0xff, 0xb3, 0x07, 0xda, 0xd7, 0x91, 0x4c, 0x3e, 0x23, 0x3c, 0xba, 0x13, 0x4b, 0xbe, + 0x92, 0x26, 0x02, 0x93, 0xbd, 0x5d, 0x05, 0x44, 0xfe, 0x06, 0xf4, 0x56, 0x9a, 0x30, 0x07, 0xf9, + 0x82, 0xf0, 0x43, 0xaf, 0x45, 0xc5, 0x55, 0x2d, 0xcb, 0xdc, 0xc8, 0x4f, 0xdc, 0x94, 0x36, 0x0f, + 0x11, 0x24, 0xfd, 0x5d, 0x0d, 0x32, 0xba, 0x75, 0xcc, 0xa2, 0xe1, 0xfb, 0xc6, 0x8f, 0x3c, 0xc7, + 0x07, 0x10, 0xc3, 0x4e, 0xee, 0x4d, 0xd0, 0x6c, 0x78, 0x92, 0xd2, 0x7f, 0xae, 0x27, 0x6d, 0x7f, + 0x49, 0xd6, 0xc9, 0xe7, 0xaf, 0xae, 0x37, 0x29, 0x5a, 0x6f, 0x52, 0xf4, 0x7b, 0x93, 0xa2, 0xaf, + 0xdb, 0xb4, 0xb7, 0xde, 0xa6, 0xbd, 0x1f, 0xdb, 0xb4, 0xf7, 0x91, 0xdd, 0x99, 0xab, 0x85, 0x1d, + 0x55, 0xbc, 0xb0, 0x5d, 0x11, 0x4e, 0xef, 0x54, 0xd5, 0xde, 0x92, 0x62, 0x3f, 0xec, 0xf2, 0xe9, + 0x9f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x02, 0x19, 0x52, 0x8b, 0x3d, 0x03, 0x00, 0x00, +} + +func (m *AccumulatorContent) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *AccumulatorContent) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *AccumulatorContent) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size := m.TotalShares.Size() + i -= size + if _, err := m.TotalShares.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintAccum(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + if len(m.AccumValue) > 0 { + for iNdEx := len(m.AccumValue) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.AccumValue[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintAccum(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *Options) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Options) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Options) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *Record) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Record) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Record) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Options != nil { + { + size, err := m.Options.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintAccum(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + if len(m.UnclaimedRewardsTotal) > 0 { + for iNdEx := len(m.UnclaimedRewardsTotal) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.UnclaimedRewardsTotal[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintAccum(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + } + if len(m.AccumValuePerShare) > 0 { + for iNdEx := len(m.AccumValuePerShare) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.AccumValuePerShare[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintAccum(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + { + size := m.NumShares.Size() + i -= size + if _, err := m.NumShares.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintAccum(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func encodeVarintAccum(dAtA []byte, offset int, v uint64) int { + offset -= sovAccum(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *AccumulatorContent) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.AccumValue) > 0 { + for _, e := range m.AccumValue { + l = e.Size() + n += 1 + l + sovAccum(uint64(l)) + } + } + l = m.TotalShares.Size() + n += 1 + l + sovAccum(uint64(l)) + return n +} + +func (m *Options) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *Record) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.NumShares.Size() + n += 1 + l + sovAccum(uint64(l)) + if len(m.AccumValuePerShare) > 0 { + for _, e := range m.AccumValuePerShare { + l = e.Size() + n += 1 + l + sovAccum(uint64(l)) + } + } + if len(m.UnclaimedRewardsTotal) > 0 { + for _, e := range m.UnclaimedRewardsTotal { + l = e.Size() + n += 1 + l + sovAccum(uint64(l)) + } + } + if m.Options != nil { + l = m.Options.Size() + n += 1 + l + sovAccum(uint64(l)) + } + return n +} + +func sovAccum(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozAccum(x uint64) (n int) { + return sovAccum(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *AccumulatorContent) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAccum + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: AccumulatorContent: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: AccumulatorContent: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AccumValue", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAccum + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthAccum + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthAccum + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.AccumValue = append(m.AccumValue, types.DecCoin{}) + if err := m.AccumValue[len(m.AccumValue)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TotalShares", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAccum + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthAccum + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAccum + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.TotalShares.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipAccum(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthAccum + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Options) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAccum + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Options: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Options: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipAccum(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthAccum + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Record) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAccum + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Record: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Record: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NumShares", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAccum + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthAccum + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAccum + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.NumShares.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AccumValuePerShare", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAccum + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthAccum + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthAccum + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.AccumValuePerShare = append(m.AccumValuePerShare, types.DecCoin{}) + if err := m.AccumValuePerShare[len(m.AccumValuePerShare)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field UnclaimedRewardsTotal", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAccum + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthAccum + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthAccum + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.UnclaimedRewardsTotal = append(m.UnclaimedRewardsTotal, types.DecCoin{}) + if err := m.UnclaimedRewardsTotal[len(m.UnclaimedRewardsTotal)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Options", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAccum + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthAccum + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthAccum + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Options == nil { + m.Options = &Options{} + } + if err := m.Options.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipAccum(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthAccum + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipAccum(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowAccum + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowAccum + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowAccum + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthAccum + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupAccum + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthAccum + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthAccum = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowAccum = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupAccum = fmt.Errorf("proto: unexpected end of group") +) diff --git a/tests/interchaintest/osmosis/osmoutils/accum/accum_helpers.go b/tests/interchaintest/osmosis/osmoutils/accum/accum_helpers.go new file mode 100644 index 00000000..c1fc92b7 --- /dev/null +++ b/tests/interchaintest/osmosis/osmoutils/accum/accum_helpers.go @@ -0,0 +1,45 @@ +package accum + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/osmosis-labs/fee-abstraction/v7/tests/interchaintest/osmosis/osmomath" + "github.com/osmosis-labs/fee-abstraction/v7/tests/interchaintest/osmosis/osmoutils" +) + +// initOrUpdatePosition creates a new position or override an existing position +// at accumulator's current value with a specific number of shares and unclaimed rewards +func initOrUpdatePosition(accum *AccumulatorObject, accumulatorValuePerShare sdk.DecCoins, index string, numShareUnits osmomath.Dec, unclaimedRewardsTotal sdk.DecCoins, options *Options) { + position := Record{ + NumShares: numShareUnits, + AccumValuePerShare: accumulatorValuePerShare, + UnclaimedRewardsTotal: unclaimedRewardsTotal, + Options: options, + } + osmoutils.MustSet(accum.store, FormatPositionPrefixKey(accum.name, index), &position) +} + +// Gets addr's current position from store +func GetPosition(accum *AccumulatorObject, name string) (Record, error) { + position := Record{} + found, err := osmoutils.Get(accum.store, FormatPositionPrefixKey(accum.name, name), &position) + if err != nil { + return Record{}, err + } + if !found { + return Record{}, NoPositionError{name} + } + + return position, nil +} + +// Gets total unclaimed rewards, including existing and newly accrued unclaimed rewards +func GetTotalRewards(accum *AccumulatorObject, position Record) sdk.DecCoins { + totalRewards := position.UnclaimedRewardsTotal + + // TODO: add a check that accum.value is greater than position.InitAccumValue + accumulatorRewards := accum.valuePerShare.Sub(position.AccumValuePerShare).MulDec(position.NumShares) + totalRewards = totalRewards.Add(accumulatorRewards...) + + return totalRewards +} diff --git a/tests/interchaintest/osmosis/osmoutils/accum/errors.go b/tests/interchaintest/osmosis/osmoutils/accum/errors.go new file mode 100644 index 00000000..87cc6fc2 --- /dev/null +++ b/tests/interchaintest/osmosis/osmoutils/accum/errors.go @@ -0,0 +1,53 @@ +package accum + +import ( + "errors" + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +var ( + ZeroSharesError = errors.New("shares must be non-zero") +) + +type NoPositionError struct { + Name string +} + +func (e NoPositionError) Error() string { + return fmt.Sprintf("no position found for position key (%s)", e.Name) +} + +type NegativeIntervalAccumulationPerShareError struct { + IntervalAccumulationPerShare sdk.DecCoins +} + +func (e NegativeIntervalAccumulationPerShareError) Error() string { + return fmt.Sprintf("interval accumulation per share must be non-negative, was (%s)", e.IntervalAccumulationPerShare) +} + +type NegativeAccDifferenceError struct { + AccumulatorDifference sdk.DecCoins +} + +func (e NegativeAccDifferenceError) Error() string { + return fmt.Sprintf("difference (%s) between the old and the new accumulator value is negative", e.AccumulatorDifference) +} + +type AccumDoesNotExistError struct { + AccumName string +} + +func (e AccumDoesNotExistError) Error() string { + return fmt.Sprintf("Accumulator name %s does not exist in store", e.AccumName) +} + +type NegativeRewardsAdditionError struct { + AccumName string + PositionName string +} + +func (e NegativeRewardsAdditionError) Error() string { + return fmt.Sprintf("Attempted to add negative rewards to position %s of the accumulator %s", e.PositionName, e.AccumName) +} diff --git a/tests/interchaintest/osmosis/osmoutils/accum/export_test.go b/tests/interchaintest/osmosis/osmoutils/accum/export_test.go new file mode 100644 index 00000000..3dd761ce --- /dev/null +++ b/tests/interchaintest/osmosis/osmoutils/accum/export_test.go @@ -0,0 +1,88 @@ +package accum + +import ( + "errors" + + "github.com/cosmos/gogoproto/proto" + + "github.com/cosmos/cosmos-sdk/store" + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/osmosis-labs/fee-abstraction/v7/tests/interchaintest/osmosis/osmomath" + "github.com/osmosis-labs/fee-abstraction/v7/tests/interchaintest/osmosis/osmoutils" +) + +// GetAllPositions returns all positions associated with the receiver accumulator. +// Returns error if any database errors occur. +// This function is currently used for testing purposes only. +// If there is a need to use this function in production, it +// can be moved to a non-test file. +func (accum AccumulatorObject) GetAllPositions() ([]Record, error) { + return osmoutils.GatherValuesFromStorePrefix(accum.store, FormatPositionPrefixKey(accum.name, ""), parseRecordFromBz) +} + +// Creates an accumulator object for testing purposes +func MakeTestAccumulator(store store.KVStore, name string, value sdk.DecCoins, totalShares osmomath.Dec) *AccumulatorObject { + // We store an accumulator object in state even if unused in tests + // because position operations still require GetAccumulator to work + _ = MakeAccumulator(store, name) + acc := AccumulatorObject{ + store: store, + name: name, + valuePerShare: value, + totalShares: totalShares, + } + setAccumulator(&acc, value, totalShares) + return &acc +} + +// Gets store from accumulator for testing purposes +func GetStore(accum AccumulatorObject) store.KVStore { + return accum.store +} + +// parseRecordFromBz parses a record from a byte slice. +// Returns error if fails to unmarshal or if the given bytes slice +// is empty. +func parseRecordFromBz(bz []byte) (record Record, err error) { + if len(bz) == 0 { + return Record{}, errors.New("record not found") + } + err = proto.Unmarshal(bz, &record) + if err != nil { + return Record{}, err + } + return record, nil +} + +// WithPosition is a decorator test function to append a position with the given name to the given accumulator. +func WithPosition(accum *AccumulatorObject, name string, position Record) *AccumulatorObject { + osmoutils.MustSet(accum.store, FormatPositionPrefixKey(accum.name, name), &position) + return accum +} + +// SetValue is a convenience test helper for updatung the value of an accumulator object +// in tests. +func (accum *AccumulatorObject) SetValue(value sdk.DecCoins) { + accum.valuePerShare = value +} + +func (o *Options) Validate() error { + return o.validate() +} + +// WARNING: only used in tests to make sure that receiver is mutated. +// Do not move out of export_test.go and do not use in production code. +func (accum AccumulatorObject) GetTotalShareField() osmomath.Dec { + return accum.totalShares +} + +// WARNING: only used in tests to make sure that receiver is mutated. +// Do not move out of export_test.go and do not use in production code. +func (accum AccumulatorObject) GetValueField() sdk.DecCoins { + return accum.valuePerShare +} + +func InitOrUpdatePosition(accum *AccumulatorObject, accumulatorValue sdk.DecCoins, index string, numShareUnits osmomath.Dec, unclaimedRewards sdk.DecCoins, options *Options) { + initOrUpdatePosition(accum, accumulatorValue, index, numShareUnits, unclaimedRewards, options) +} diff --git a/tests/interchaintest/osmosis/osmoutils/accum/module.go b/tests/interchaintest/osmosis/osmoutils/accum/module.go new file mode 100644 index 00000000..297d7feb --- /dev/null +++ b/tests/interchaintest/osmosis/osmoutils/accum/module.go @@ -0,0 +1,3 @@ +// package accumulator allows one to define an accumulator to accommodate constant-rate +// (linear) distribution mechanisms with constant runtime and linear memory +package accum diff --git a/tests/interchaintest/osmosis/osmoutils/accum/options.go b/tests/interchaintest/osmosis/osmoutils/accum/options.go new file mode 100644 index 00000000..9c8ba7c6 --- /dev/null +++ b/tests/interchaintest/osmosis/osmoutils/accum/options.go @@ -0,0 +1,18 @@ +package accum + +import "github.com/osmosis-labs/fee-abstraction/v7/tests/interchaintest/osmosis/osmomath" + +var one = osmomath.OneDec() + +// validate returns nil if Options are valid. +// Error otherwise. Note, that, currently, +// options do not contain any fields. We +// create them to be able to extend in the +// future with auto-compounding logic. +// As a result, this always returns nil. +func (o *Options) validate() error { + if o == nil { + return nil + } + return nil +} diff --git a/tests/interchaintest/osmosis/osmoutils/accum/prefix.go b/tests/interchaintest/osmosis/osmoutils/accum/prefix.go new file mode 100644 index 00000000..08f7bbe5 --- /dev/null +++ b/tests/interchaintest/osmosis/osmoutils/accum/prefix.go @@ -0,0 +1,29 @@ +package accum + +import "fmt" + +const ( + modulePrefix = "accum" + accumulatorPrefix = "acc" + positionPrefix = "pos" + KeySeparator = "||" // needs to be different from other modules. + + accumPrefixKey = modulePrefix + KeySeparator + accumulatorPrefix + KeySeparator + positionPrefixKey = modulePrefix + KeySeparator + positionPrefix + KeySeparator +) + +// formatAccumPrefix returns the key prefix used +// specifically for accumulator values in the KVStore. +// Returns "accum||acc||{accumName}" as bytes. +func formatAccumPrefixKey(accumName string) []byte { + return []byte(fmt.Sprintf(accumPrefixKey+"%s", accumName)) +} + +// FormatPositionPrefixKey returns the key prefix used +// specifically for position values in the KVStore. +// Returns "accum||pos||{accumName}||{name}" as bytes. +// We use a different key separator, namely `||`, to separate the accumulator name and the position name. +// This is because we require that accumName does not contain this as a substring. +func FormatPositionPrefixKey(accumName, name string) []byte { + return []byte(fmt.Sprintf(positionPrefixKey+"%s"+KeySeparator+"%s", accumName, name)) +} diff --git a/tests/interchaintest/osmosis/osmoutils/address.go b/tests/interchaintest/osmosis/osmoutils/address.go new file mode 100644 index 00000000..fb47d79d --- /dev/null +++ b/tests/interchaintest/osmosis/osmoutils/address.go @@ -0,0 +1,12 @@ +package osmoutils + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/address" +) + +// NewModuleAddressWithPrefix returns a new module address with the given prefix and identifier. +func NewModuleAddressWithPrefix(moduleName, prefix string, identifier []byte) sdk.AccAddress { + key := append([]byte(prefix), identifier...) + return address.Module(moduleName, key) +} diff --git a/tests/interchaintest/osmosis/osmoutils/cache_ctx.go b/tests/interchaintest/osmosis/osmoutils/cache_ctx.go new file mode 100644 index 00000000..b5653ae3 --- /dev/null +++ b/tests/interchaintest/osmosis/osmoutils/cache_ctx.go @@ -0,0 +1,79 @@ +package osmoutils + +import ( + "errors" + "fmt" + "runtime" + "runtime/debug" + + "github.com/cosmos/cosmos-sdk/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// This function lets you run the function f, but if there's an error or panic +// drop the state machine change and log the error. +// If there is no error, proceeds as normal (but with some slowdown due to SDK store weirdness) +// Try to avoid usage of iterators in f. +// +// If its an out of gas panic, this function will also panic like in normal tx execution flow. +// This is still safe for beginblock / endblock code though, as they do not have out of gas panics. +func ApplyFuncIfNoError(ctx sdk.Context, f func(ctx sdk.Context) error) (err error) { + // Add a panic safeguard + defer func() { + if recoveryError := recover(); recoveryError != nil { + if isErr, _ := IsOutOfGasError(recoveryError); isErr { + // We panic with the same error, to replicate the normal tx execution flow. + panic(recoveryError) + } else { + PrintPanicRecoveryError(ctx, recoveryError) + err = errors.New("panic occurred during execution") + } + } + }() + // makes a new cache context, which all state changes get wrapped inside of. + cacheCtx, write := ctx.CacheContext() + err = f(cacheCtx) + if err != nil { + ctx.Logger().Error(err.Error()) + } else { + // no error, write the output of f + write() + } + return err +} + +// Frustratingly, this has to return the error descriptor, not an actual error itself +// because the SDK errors here are not actually errors. (They don't implement error interface) +func IsOutOfGasError(err any) (bool, string) { + switch e := err.(type) { + case types.ErrorOutOfGas: + return true, e.Descriptor + case types.ErrorGasOverflow: + return true, e.Descriptor + default: + return false, "" + } +} + +// PrintPanicRecoveryError error logs the recoveryError, along with the stacktrace, if it can be parsed. +// If not emits them to stdout. +func PrintPanicRecoveryError(ctx sdk.Context, recoveryError interface{}) { + errStackTrace := string(debug.Stack()) + switch e := recoveryError.(type) { + case types.ErrorOutOfGas: + ctx.Logger().Debug("out of gas error inside panic recovery block: " + e.Descriptor) + return + case string: + ctx.Logger().Error("Recovering from (string) panic: " + e) + case runtime.Error: + ctx.Logger().Error("recovered (runtime.Error) panic: " + e.Error()) + case error: + ctx.Logger().Error("recovered (error) panic: " + e.Error()) + default: + ctx.Logger().Error("recovered (default) panic. Could not capture logs in ctx, see stdout") + fmt.Println("Recovering from panic ", recoveryError) + debug.PrintStack() + return + } + ctx.Logger().Error("stack trace: " + errStackTrace) +} diff --git a/tests/interchaintest/osmosis/osmoutils/coin_helper.go b/tests/interchaintest/osmosis/osmoutils/coin_helper.go new file mode 100644 index 00000000..0cb81f8d --- /dev/null +++ b/tests/interchaintest/osmosis/osmoutils/coin_helper.go @@ -0,0 +1,129 @@ +package osmoutils + +import ( + "fmt" + + wasmvmtypes "github.com/CosmWasm/wasmvm/types" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// SubDecCoinArrays subtracts the contents of the second param from the first (decCoinsArrayA - decCoinsArrayB) +// Note that this takes in two _arrays_ of DecCoins, meaning that each term itself is of type DecCoins (i.e. an array of DecCoin). +func SubDecCoinArrays(decCoinsArrayA []sdk.DecCoins, decCoinsArrayB []sdk.DecCoins) ([]sdk.DecCoins, error) { + if len(decCoinsArrayA) != len(decCoinsArrayB) { + return []sdk.DecCoins{}, fmt.Errorf("DecCoin arrays must be of equal length to be subtracted") + } + + finalDecCoinArray := []sdk.DecCoins{} + for i := range decCoinsArrayA { + finalDecCoinArray = append(finalDecCoinArray, decCoinsArrayA[i].Sub(decCoinsArrayB[i])) + } + + return finalDecCoinArray, nil +} + +// SafeSubDecCoinArrays subtracts the contents of the second param from the first (decCoinsArrayA - decCoinsArrayB) +// Note that this takes in two _arrays_ of DecCoins, meaning that each term itself is of type DecCoins (i.e. an array of DecCoin). +// Contrary to SubDecCoinArrays, this subtractions allows for negative result values. +func SafeSubDecCoinArrays(decCoinsArrayA []sdk.DecCoins, decCoinsArrayB []sdk.DecCoins) ([]sdk.DecCoins, error) { + if len(decCoinsArrayA) != len(decCoinsArrayB) { + return []sdk.DecCoins{}, fmt.Errorf("DecCoin arrays must be of equal length to be subtracted") + } + + finalDecCoinArray := []sdk.DecCoins{} + for i := range decCoinsArrayA { + subResult, _ := decCoinsArrayA[i].SafeSub(decCoinsArrayB[i]) + finalDecCoinArray = append(finalDecCoinArray, subResult) + } + + return finalDecCoinArray, nil +} + +// AddDecCoinArrays adds the contents of the second param from the first (decCoinsArrayA + decCoinsArrayB) +// Note that this takes in two _arrays_ of DecCoins, meaning that each term itself is of type DecCoins (i.e. an array of DecCoin). +func AddDecCoinArrays(decCoinsArrayA []sdk.DecCoins, decCoinsArrayB []sdk.DecCoins) ([]sdk.DecCoins, error) { + if len(decCoinsArrayA) != len(decCoinsArrayB) { + return []sdk.DecCoins{}, fmt.Errorf("DecCoin arrays must be of equal length to be added") + } + + finalDecCoinArray := []sdk.DecCoins{} + for i := range decCoinsArrayA { + finalDecCoinArray = append(finalDecCoinArray, decCoinsArrayA[i].Add(decCoinsArrayB[i]...)) + } + + return finalDecCoinArray, nil +} + +// CollapseDecCoinsArray takes an array of DecCoins and returns the sum of all the DecCoins in the array. +func CollapseDecCoinsArray(decCoinsArray []sdk.DecCoins) sdk.DecCoins { + finalDecCoins := sdk.DecCoins{} + for _, decCoins := range decCoinsArray { + finalDecCoins = finalDecCoins.Add(decCoins...) + } + return finalDecCoins +} + +// ConvertCoinsToDecCoins takes sdk.Coins and converts it to sdk.DecCoins +func ConvertCoinsToDecCoins(coins sdk.Coins) sdk.DecCoins { + decCoins := sdk.DecCoins{} + for _, coin := range coins { + decCoins = append(decCoins, sdk.NewDecCoin(coin.Denom, coin.Amount)) + } + return decCoins +} + +// FilterDenoms returns the coins with only the passed in denoms +func FilterDenoms(coins sdk.Coins, denoms []string) sdk.Coins { + filteredCoins := sdk.NewCoins() + + for _, denom := range denoms { + filteredCoins = filteredCoins.Add(sdk.NewCoin(denom, coins.AmountOf(denom))) + } + + return filteredCoins +} + +// MergeCoinMaps takes two maps of type map[T]sdk.Coins and merges them together, adding the values of the second map to the first. +func MergeCoinMaps[T comparable](currentEpochExpectedDistributionsOne map[T]sdk.Coins, poolIDToExpectedDistributionMapOne map[T]sdk.Coins) map[T]sdk.Coins { + newMap := map[T]sdk.Coins{} + + // Iterate over the first map and add all the values to the new map + for poolID, expectedDistribution := range currentEpochExpectedDistributionsOne { + newMap[poolID] = expectedDistribution + } + + // Iterate over the second map and add all the values to the new map + for poolID, expectedDistribution := range poolIDToExpectedDistributionMapOne { + if _, ok := newMap[poolID]; ok { + newMap[poolID] = newMap[poolID].Add(expectedDistribution...) + } else { + newMap[poolID] = expectedDistribution + } + } + return newMap +} + +// Convert sdk.Coins to wasmvmtypes.Coins +func CWCoinsFromSDKCoins(in sdk.Coins) wasmvmtypes.Coins { + var cwCoins wasmvmtypes.Coins + for _, coin := range in { + cwCoins = append(cwCoins, CWCoinFromSDKCoin(coin)) + } + return cwCoins +} + +// Convert sdk.Coin to wasmvmtypes.Coin +func CWCoinFromSDKCoin(in sdk.Coin) wasmvmtypes.Coin { + return wasmvmtypes.Coin{ + Denom: in.GetDenom(), + Amount: in.Amount.String(), + } +} + +func ConvertCoinArrayToCoins(coinArray []sdk.Coin) sdk.Coins { + coins := sdk.Coins{} + for _, coin := range coinArray { + coins = append(coins, coin) + } + return coins +} diff --git a/tests/interchaintest/osmosis/osmoutils/coinutil/coin_math.go b/tests/interchaintest/osmosis/osmoutils/coinutil/coin_math.go new file mode 100644 index 00000000..7b6be31c --- /dev/null +++ b/tests/interchaintest/osmosis/osmoutils/coinutil/coin_math.go @@ -0,0 +1,138 @@ +package coinutil + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/osmosis-labs/fee-abstraction/v7/tests/interchaintest/osmosis/osmomath" +) + +// Mutative helpers that mutate the input coins + +// MulIntMut multiplies the coins by the given integer +// Mutates the input coins +func MulIntMut(coins sdk.Coins, num osmomath.Int) { + for i := range coins { + coins[i].Amount = coins[i].Amount.Mul(num) + } +} + +// MulRawMut multiplies the coins by the given integer +// Mutates the input coins +func MulRawMut(coins sdk.Coins, num int64) sdk.Coins { + for i := range coins { + coins[i].Amount = coins[i].Amount.MulRaw(num) + } + return coins +} + +// MulDecMut multiplies the coins by the given decimal +// Mutates the input coins +func MulDecMut(coins sdk.Coins, num osmomath.Dec) { + for i := range coins { + coins[i].Amount = coins[i].Amount.ToLegacyDec().Mul(num).TruncateInt() + } +} + +// QuoIntMut divides the coins by the given integer +// Mutates the input coins +func QuoIntMut(coins sdk.Coins, num osmomath.Int) { + for i := range coins { + coins[i].Amount = coins[i].Amount.Quo(num) + } +} + +// QuoRawMut divides the coins by the given integer +// Mutates the input coins +func QuoRawMut(coins sdk.Coins, num int64) { + for i := range coins { + coins[i].Amount = coins[i].Amount.QuoRaw(num) + } +} + +// QuoIntMut divides the coins by the given decimal +// Mutates the input coins +func QuoDecMut(coins sdk.Coins, num osmomath.Dec) { + for i := range coins { + coins[i].Amount = coins[i].Amount.ToLegacyDec().Quo(num).TruncateInt() + } +} + +// Non-mutative coin helpers that reallocate and return new coins + +// MulInt multiplies the coins by the given integer +// Does not mutate the input coins and returns new coins. +func MulInt(coins sdk.Coins, num osmomath.Int) sdk.Coins { + newCoins := make(sdk.Coins, len(coins)) + + for i := range coins { + newCoins[i].Amount = coins[i].Amount.Mul(num) + newCoins[i].Denom = coins[i].Denom + } + + return newCoins +} + +// MulRaw multiplies the coins by the given integer +// Does not mutate the input coins and returns new coins. +func MulRaw(coins sdk.Coins, num int64) sdk.Coins { + newCoins := make(sdk.Coins, len(coins)) + + for i := range coins { + newCoins[i].Amount = coins[i].Amount.MulRaw(num) + newCoins[i].Denom = coins[i].Denom + } + + return newCoins +} + +// MulDec multiplies the coins by the given decimal +// Does not mutate the input coins and returns new coins. +func MulDec(coins sdk.Coins, num osmomath.Dec) sdk.Coins { + newCoins := make(sdk.Coins, len(coins)) + + for i := range coins { + newCoins[i].Amount = coins[i].Amount.ToLegacyDec().Mul(num).TruncateInt() + newCoins[i].Denom = coins[i].Denom + } + + return newCoins +} + +// QuoInt divides the coins by the given integer +// Does not mutate the input coins and returns new coins. +func QuoInt(coins sdk.Coins, num osmomath.Int) sdk.Coins { + newCoins := make(sdk.Coins, len(coins)) + + for i := range coins { + newCoins[i].Amount = coins[i].Amount.Quo(num) + newCoins[i].Denom = coins[i].Denom + } + + return newCoins +} + +// QuoRaw divides the coins by the given integer +// Does not mutate the input coins and returns new coins. +func QuoRaw(coins sdk.Coins, num int64) sdk.Coins { + newCoins := make(sdk.Coins, len(coins)) + + for i := range coins { + newCoins[i].Amount = coins[i].Amount.QuoRaw(num) + newCoins[i].Denom = coins[i].Denom + } + + return newCoins +} + +// QuoDec divides the coins by the given integer +// Does not mutate the input coins and returns new coins. +func QuoDec(coins sdk.Coins, num osmomath.Dec) sdk.Coins { + newCoins := make(sdk.Coins, len(coins)) + + for i := range coins { + newCoins[i].Amount = coins[i].Amount.ToLegacyDec().Quo(num).TruncateInt() + newCoins[i].Denom = coins[i].Denom + } + + return newCoins +} diff --git a/tests/interchaintest/osmosis/osmoutils/coinutil/coin_math_test.go b/tests/interchaintest/osmosis/osmoutils/coinutil/coin_math_test.go new file mode 100644 index 00000000..631a89d2 --- /dev/null +++ b/tests/interchaintest/osmosis/osmoutils/coinutil/coin_math_test.go @@ -0,0 +1,135 @@ +package coinutil_test + +import ( + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" + + "github.com/osmosis-labs/fee-abstraction/v7/tests/interchaintest/osmosis/osmomath" + "github.com/osmosis-labs/fee-abstraction/v7/tests/interchaintest/osmosis/osmoutils/coinutil" +) + +var ( + defaultCoins = sdk.NewCoins( + sdk.NewCoin("foo", sdk.NewInt(100)), + sdk.NewCoin("bar", sdk.NewInt(200)), + ) + + defaultMultiplier = osmomath.NewInt(2) + + defaultMulExpectedResult = sdk.NewCoins( + sdk.NewCoin(defaultCoins[0].Denom, defaultCoins[0].Amount.Mul(defaultMultiplier)), + sdk.NewCoin(defaultCoins[1].Denom, defaultCoins[1].Amount.Mul(defaultMultiplier)), + ) + + defaultQuoExpectedResult = sdk.NewCoins( + sdk.NewCoin(defaultCoins[0].Denom, defaultCoins[0].Amount.Quo(defaultMultiplier)), + sdk.NewCoin(defaultCoins[1].Denom, defaultCoins[1].Amount.Quo(defaultMultiplier)), + ) +) + +// makes a deep copy to avoid accidentally mutating the input to a test. +func deepCopy(coins sdk.Coins) sdk.Coins { + newCoins := make(sdk.Coins, len(coins)) + for i := range coins { + newCoins[i].Amount = coins[i].Amount + newCoins[i].Denom = coins[i].Denom + } + return newCoins +} + +// Basic multiplication test. +func TestMul(t *testing.T) { + t.Run("test mutative multiplication", func(t *testing.T) { + t.Run("MulIntMut", func(t *testing.T) { + defaulCoins := deepCopy(defaultCoins) + coinutil.MulIntMut(defaulCoins, defaultMultiplier) + require.Equal(t, defaultMulExpectedResult, defaulCoins) + }) + + t.Run("MulIntRawMut", func(t *testing.T) { + defaulCoins := deepCopy(defaultCoins) + coinutil.MulRawMut(defaulCoins, defaultMultiplier.Int64()) + require.Equal(t, defaultMulExpectedResult, defaulCoins) + }) + + t.Run("MulDecMut", func(t *testing.T) { + defaulCoins := deepCopy(defaultCoins) + coinutil.MulDecMut(defaulCoins, osmomath.NewDecFromInt(defaultMultiplier)) + require.Equal(t, defaultMulExpectedResult, defaulCoins) + }) + }) + + // Make a deep copy of the default coins for the input. + // Validate that the copy input coins are not mutated. + t.Run("test non-mutative multiplication", func(t *testing.T) { + t.Run("MulInt", func(t *testing.T) { + defaulCoinsCopy := deepCopy(defaultCoins) + result := coinutil.MulInt(defaulCoinsCopy, defaultMultiplier) + require.Equal(t, defaultMulExpectedResult, result) + require.Equal(t, defaultCoins, defaulCoinsCopy) + }) + + t.Run("MulIntRaw", func(t *testing.T) { + defaulCoinsCopy := deepCopy(defaultCoins) + result := coinutil.MulRaw(defaulCoinsCopy, defaultMultiplier.Int64()) + require.Equal(t, defaultMulExpectedResult, result) + require.Equal(t, defaultCoins, defaulCoinsCopy) + }) + + t.Run("MulDec", func(t *testing.T) { + defaulCoinsCopy := deepCopy(defaultCoins) + result := coinutil.MulDec(defaulCoinsCopy, osmomath.NewDecFromInt(defaultMultiplier)) + require.Equal(t, defaultMulExpectedResult, result) + require.Equal(t, defaultCoins, defaulCoinsCopy) + }) + }) +} + +func TestQuo(t *testing.T) { + t.Run("test mutative division", func(t *testing.T) { + t.Run("QuoIntMut", func(t *testing.T) { + defaulCoins := deepCopy(defaultCoins) + coinutil.QuoIntMut(defaulCoins, defaultMultiplier) + require.Equal(t, defaultQuoExpectedResult, defaulCoins) + }) + + t.Run("QuoIntRawMut", func(t *testing.T) { + defaulCoins := deepCopy(defaultCoins) + coinutil.QuoRawMut(defaulCoins, defaultMultiplier.Int64()) + require.Equal(t, defaultQuoExpectedResult, defaulCoins) + }) + + t.Run("QuoDecMut", func(t *testing.T) { + defaulCoins := deepCopy(defaultCoins) + coinutil.QuoDecMut(defaulCoins, osmomath.NewDecFromInt(defaultMultiplier)) + require.Equal(t, defaultQuoExpectedResult, defaulCoins) + }) + }) + + // Make a deep copy of the default coins for the input. + // Validate that the copy input coins are not mutated. + t.Run("test non-mutative division", func(t *testing.T) { + t.Run("QuoInt", func(t *testing.T) { + defaulCoinsCopy := deepCopy(defaultCoins) + result := coinutil.QuoInt(defaulCoinsCopy, defaultMultiplier) + require.Equal(t, defaultQuoExpectedResult, result) + require.Equal(t, defaultCoins, defaulCoinsCopy) + }) + + t.Run("QuoIntRaw", func(t *testing.T) { + defaulCoinsCopy := deepCopy(defaultCoins) + result := coinutil.QuoRaw(defaulCoinsCopy, defaultMultiplier.Int64()) + require.Equal(t, defaultQuoExpectedResult, result) + require.Equal(t, defaultCoins, defaulCoinsCopy) + }) + + t.Run("QuoDec", func(t *testing.T) { + defaulCoinsCopy := deepCopy(defaultCoins) + result := coinutil.QuoDec(defaulCoinsCopy, osmomath.NewDecFromInt(defaultMultiplier)) + require.Equal(t, defaultQuoExpectedResult, result) + require.Equal(t, defaultCoins, defaulCoinsCopy) + }) + }) +} diff --git a/tests/interchaintest/osmosis/osmoutils/compare.go b/tests/interchaintest/osmosis/osmoutils/compare.go new file mode 100644 index 00000000..d6b01997 --- /dev/null +++ b/tests/interchaintest/osmosis/osmoutils/compare.go @@ -0,0 +1,111 @@ +package osmoutils + +import "sort" + +// Max returns the maximum value among the given values of any type that supports comparison. +func Max(values ...interface{}) interface{} { + if len(values) == 0 { + return nil + } + + max := values[0] + for _, value := range values[1:] { + switch value := value.(type) { + case int: + if intValue, ok := max.(int); ok && value > intValue { + max = value + } + case int8: + if int8Value, ok := max.(int8); ok && value > int8Value { + max = value + } + case int16: + if int16Value, ok := max.(int16); ok && value > int16Value { + max = value + } + case int32: + if int32Value, ok := max.(int32); ok && value > int32Value { + max = value + } + case int64: + if int64Value, ok := max.(int64); ok && value > int64Value { + max = value + } + case uint: + if uintValue, ok := max.(uint); ok && value > uintValue { + max = value + } + case uint8: + if uint8Value, ok := max.(uint8); ok && value > uint8Value { + max = value + } + case uint16: + if uint16Value, ok := max.(uint16); ok && value > uint16Value { + max = value + } + case uint32: + if uint32Value, ok := max.(uint32); ok && value > uint32Value { + max = value + } + case uint64: + if uint64Value, ok := max.(uint64); ok && value > uint64Value { + max = value + } + case uintptr: + if uintptrValue, ok := max.(uintptr); ok && value > uintptrValue { + max = value + } + } + } + return max +} + +// DifferenceBetweenUint64Arrays takes two slices of uint64, 'a' and 'b', as input. +// It returns a new slice containing the elements that are unique to either 'a' or 'b'. +// The function uses two maps for efficient lookup of elements. +// +// Example: +// a := []uint64{1, 2, 3, 4, 5} +// b := []uint64{4, 5, 6, 7, 8} +// result := DisjointArrays(a, b) +// result will be []uint64{1, 2, 3, 6, 7, 8} +// +// Note: This function returns the difference between the two arrays in ascending order, +// and does not preserve the order of the elements in the original arrays. +func DisjointArrays(a, b []uint64) []uint64 { + if len(a) == 0 && len(b) == 0 { + return []uint64{} + } + + m1 := make(map[uint64]bool) + m2 := make(map[uint64]bool) + + for _, item := range a { + m1[item] = true + } + + for _, item := range b { + m2[item] = true + } + + var result []uint64 + for item := range m1 { + if !m2[item] { + result = append(result, item) + } + } + + for item := range m2 { + if !m1[item] { + result = append(result, item) + } + } + + if len(result) == 0 { + return []uint64{} + } + + sort.Slice(result, func(i, j int) bool { return result[i] < result[j] }) + + return result +} diff --git a/tests/interchaintest/osmosis/osmoutils/compare_test.go b/tests/interchaintest/osmosis/osmoutils/compare_test.go new file mode 100644 index 00000000..1c2216f1 --- /dev/null +++ b/tests/interchaintest/osmosis/osmoutils/compare_test.go @@ -0,0 +1,133 @@ +package osmoutils_test + +import ( + "reflect" + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/osmosis-labs/fee-abstraction/v7/tests/interchaintest/osmosis/osmoutils" +) + +func TestMax(t *testing.T) { + testCases := []struct { + name string + values []interface{} + max interface{} + }{ + { + name: "Empty values", + values: []interface{}{}, + max: nil, + }, + { + name: "int", + values: []interface{}{1, 5, 3, 2}, + max: 5, + }, + { + name: "uint", + values: []interface{}{uint(10), uint(7), uint(9)}, + max: uint(10), + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + result := osmoutils.Max(tc.values...) + assert.Equal(t, tc.max, result) + }) + } +} + +func TestDisjointArrays(t *testing.T) { + tests := []struct { + name string + a []uint64 + b []uint64 + want []uint64 + }{ + { + name: "Both arrays contain some unique elements, both sorted", + a: []uint64{1, 2, 3, 4, 5}, + b: []uint64{4, 5, 6, 7, 8}, + want: []uint64{1, 2, 3, 6, 7, 8}, + }, + { + name: "Only array 'a' contains some unique elements, both sorted", + a: []uint64{10, 20, 30, 40}, + b: []uint64{20, 30}, + want: []uint64{10, 40}, + }, + { + name: "Only array 'b' contains some unique elements, both sorted", + a: []uint64{20, 30}, + b: []uint64{10, 20, 30, 40}, + want: []uint64{10, 40}, + }, + { + name: "Both arrays contain some unique elements, both unsorted", + a: []uint64{5, 4, 3, 2, 1}, + b: []uint64{8, 7, 6, 5, 4}, + want: []uint64{1, 2, 3, 6, 7, 8}, + }, + { + name: "Both arrays contain some unique elements, a sorted, b unsorted", + a: []uint64{5, 4, 3, 2, 1}, + b: []uint64{6, 7, 8, 5, 4}, + want: []uint64{1, 2, 3, 6, 7, 8}, + }, + { + name: "Only array 'a' contains some unique elements, both unsorted", + a: []uint64{40, 30, 20, 10}, + b: []uint64{30, 20}, + want: []uint64{10, 40}, + }, + { + name: "Only array 'b' contains some unique elements, both unsorted", + a: []uint64{30, 20}, + b: []uint64{40, 30, 20, 10}, + want: []uint64{10, 40}, + }, + { + name: "Both arrays empty", + a: []uint64{}, + b: []uint64{}, + want: []uint64{}, + }, + { + name: "Array 'a' empty, unsorted", + a: []uint64{}, + b: []uint64{2, 1, 3}, + want: []uint64{1, 2, 3}, + }, + { + name: "Array 'b' empty, unsorted", + a: []uint64{2, 1, 3}, + b: []uint64{}, + want: []uint64{1, 2, 3}, + }, + { + name: "Both arrays contain completely different elements, both unsorted", + a: []uint64{2, 1, 3}, + b: []uint64{5, 4, 6}, + want: []uint64{1, 2, 3, 4, 5, 6}, + }, + { + name: "Both arrays equal, both unsorted", + a: []uint64{2, 1, 3}, + b: []uint64{2, 1, 3}, + want: []uint64{}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := osmoutils.DisjointArrays(tt.a, tt.b) + + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("DifferenceBetweenUint64Arrays() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/tests/interchaintest/osmosis/osmoutils/config_parser.go b/tests/interchaintest/osmosis/osmoutils/config_parser.go new file mode 100644 index 00000000..c232f80e --- /dev/null +++ b/tests/interchaintest/osmosis/osmoutils/config_parser.go @@ -0,0 +1,95 @@ +package osmoutils + +import ( + "fmt" + "strconv" + "strings" + + servertypes "github.com/cosmos/cosmos-sdk/server/types" + + "github.com/spf13/cast" +) + +// ParseBool parses a boolean value from a server type option. +func ParseBool(opts servertypes.AppOptions, groupOptName, optName string, defaultValue bool) bool { + fullOptName := "osmosis-sqs." + optName + valueInterface := opts.Get(fullOptName) + value := defaultValue + if valueInterface != nil { + valueStr, ok := valueInterface.(string) + if !ok { + panic("invalidly configured osmosis-sqs." + optName) + } + valueStr = strings.TrimSpace(valueStr) + v, err := strconv.ParseBool(valueStr) + if err != nil { + fmt.Println("error in parsing" + optName + " as bool, setting to false") + return false + } + return v + } + + return value +} + +// ParseInt parses an integer value from a server type option. +func ParseInt(opts servertypes.AppOptions, groupOptName, optName string) int { + valueInterface := opts.Get(groupOptName + "." + optName) + if valueInterface == nil { + panic("missing config for osmosis-sqs." + optName) + } + value := cast.ToInt(valueInterface) + return value +} + +// ParseUint64 parses a uint64 value from a server type option. +func ParseUint64Slice(opts servertypes.AppOptions, groupOptName, optName string) []uint64 { + stringSlice := ParseString(opts, groupOptName, optName) + + valueUint64Slice, err := ParseStringToUint64Slice(stringSlice) + if err != nil { + panic(fmt.Sprintf("invalidly configured osmosis-sqs.%s, err= %v", optName, err)) + } + + return valueUint64Slice +} + +// ParseStringToUint64Slice parses a string to a slice of uint64 values. +func ParseStringToUint64Slice(input string) ([]uint64, error) { + // Remove "[" and "]" from the input string + input = strings.Trim(input, "[]") + + // Split the string into individual values + values := strings.Split(input, ",") + + // Initialize a slice to store uint64 values + var result []uint64 + + if len(values) == 1 && values[0] == "" { + return result, nil + } + + // Iterate over the values and convert them to uint64 + for _, v := range values { + // Parse the string value to uint64 + u, err := strconv.ParseUint(strings.TrimSpace(v), 10, 64) + if err != nil { + return nil, err + } + + // Append the uint64 value to the result slice + result = append(result, u) + } + + return result, nil +} + +// ParseString parses a string value from a server type option. +func ParseString(opts servertypes.AppOptions, groupOptName, optName string) string { + valueInterface := opts.Get(groupOptName + "." + optName) + if valueInterface == nil { + panic("missing config for osmosis-sqs." + optName) + } + value := cast.ToString(valueInterface) + return value +} diff --git a/tests/interchaintest/osmosis/osmoutils/conv_helper.go b/tests/interchaintest/osmosis/osmoutils/conv_helper.go new file mode 100644 index 00000000..bd675e48 --- /dev/null +++ b/tests/interchaintest/osmosis/osmoutils/conv_helper.go @@ -0,0 +1,16 @@ +package osmoutils + +import ( + "encoding/binary" + "strconv" +) + +func Uint64ToBytes(i uint64) []byte { + key := make([]byte, 8) + binary.LittleEndian.PutUint64(key, i) + return key +} + +func Uint64ToString(i uint64) string { + return strconv.FormatUint(i, 10) +} diff --git a/tests/interchaintest/osmosis/osmoutils/cosmwasm/helpers.go b/tests/interchaintest/osmosis/osmoutils/cosmwasm/helpers.go new file mode 100644 index 00000000..dc623ade --- /dev/null +++ b/tests/interchaintest/osmosis/osmoutils/cosmwasm/helpers.go @@ -0,0 +1,228 @@ +package cosmwasm + +import ( + "encoding/json" + + storetypes "github.com/cosmos/cosmos-sdk/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// ContracKeeper defines the interface needed to be fulfilled for +// the ContractKeeper. +type ContractKeeper interface { + Instantiate( + ctx sdk.Context, + codeID uint64, + creator, admin sdk.AccAddress, + initMsg []byte, + label string, + deposit sdk.Coins, + ) (sdk.AccAddress, []byte, error) + + Sudo(ctx sdk.Context, contractAddress sdk.AccAddress, msg []byte) ([]byte, error) + + Execute(ctx sdk.Context, contractAddress sdk.AccAddress, caller sdk.AccAddress, msg []byte, coins sdk.Coins) ([]byte, error) +} + +// WasmKeeper defines the interface needed to be fulfilled for +// the WasmKeeper. +type WasmKeeper interface { + QuerySmart(ctx sdk.Context, contractAddress sdk.AccAddress, queryMsg []byte) ([]byte, error) + QueryGasLimit() storetypes.Gas +} + +// Query is a generic function to query a CosmWasm smart contract with the given request. +// The function marshals the request into JSON, performs a smart query using the Wasm keeper, and then +// unmarshals the response into the provided response type. +// It returns the response of the specified type and an error if the JSON marshaling, smart query, +// or JSON unmarshaling process fails. +// +// The function uses generics, and T and K represent the request and response types, respectively. +// +// Parameters: +// - ctx: The SDK context. +// - wasmKeeper: The Wasm keeper to query the smart contract. +// - contractAddress: The Bech32 address of the smart contract. +// - request: The request of type T to be sent to the smart contract. +// +// Returns: +// - response: The response of type K from the smart contract. +// - err: An error if the JSON marshaling, smart query, or JSON unmarshaling process fails; otherwise, nil. +func Query[T any, K any](ctx sdk.Context, wasmKeeper WasmKeeper, contractAddress string, request T) (response K, err error) { + bz, err := json.Marshal(request) + if err != nil { + return response, err + } + + childCtx := ctx.WithGasMeter(storetypes.NewGasMeter(wasmKeeper.QueryGasLimit())) + responseBz, err := wasmKeeper.QuerySmart(childCtx, sdk.MustAccAddressFromBech32(contractAddress), bz) + if err != nil { + return response, err + } + + ctx.GasMeter().ConsumeGas(childCtx.GasMeter().GasConsumed(), "query smart") + + if err := json.Unmarshal(responseBz, &response); err != nil { + return response, err + } + + return response, nil +} + +// MustQuery is a generic function to query a CosmWasm smart contract with the given request. +// It is similar to the Query function but panics if an error occurs during the query. +// The function uses the Query function to perform the smart query and panics if an error is returned. +// +// The function uses generics, and T and K represent the request and response types, respectively. +// +// Parameters: +// - ctx: The SDK context. +// - wasmKeeper: The Wasm keeper to query the smart contract. +// - contractAddress: The Bech32 address of the smart contract. +// - request: The request of type T to be sent to the smart contract. +// +// Returns: +// - response: The response of type K from the smart contract. +// +// Panics: +// - If an error occurs during the JSON marshaling, smart query, or JSON unmarshaling process. +func MustQuery[T any, K any](ctx sdk.Context, wasmKeeper WasmKeeper, contractAddress string, request T) (response K) { + response, err := Query[T, K](ctx, wasmKeeper, contractAddress, request) + if err != nil { + panic(err) + } + + return response +} + +// Sudo is a generic function to execute a sudo message on a CosmWasm smart contract with the given request. +// The function marshals the request into JSON, performs a sudo call using the contract keeper, and then +// unmarshals the response into the provided response type. +// It returns the response of the specified type and an error if the JSON marshaling, sudo call, +// or JSON unmarshaling process fails. +// +// The function uses generics, and T and K represent the request and response types, respectively. +// +// Parameters: +// - ctx: The SDK context. +// - contractKeeper: The Contract keeper to execute the sudo call on the smart contract. +// - contractAddress: The Bech32 address of the smart contract. +// - request: The request of type T to be sent to the smart contract. +// +// Returns: +// - response: The response of type K from the smart contract. +// - err: An error if the JSON marshaling, sudo call, or JSON unmarshaling process fails; otherwise, nil. +func Sudo[T any, K any](ctx sdk.Context, contractKeeper ContractKeeper, contractAddress string, request T) (response K, err error) { + bz, err := json.Marshal(request) + if err != nil { + return response, err + } + + responseBz, err := contractKeeper.Sudo(ctx, sdk.MustAccAddressFromBech32(contractAddress), bz) + if err != nil { + return response, err + } + + // valid empty response + if len(responseBz) == 0 { + return response, nil + } + + if err := json.Unmarshal(responseBz, &response); err != nil { + return response, err + } + + return response, nil +} + +// MustSudo is a generic function to execute a sudo message on a CosmWasm smart contract with the given request. +// It is similar to the Sudo function but panics if an error occurs during the sudo call. +// The function uses the Sudo function to perform the sudo call and panics if an error is returned. +// +// The function uses generics, and T and K represent the request and response types, respectively. +// +// Parameters: +// - ctx: The SDK context. +// - contractKeeper: The Contract keeper to execute the sudo call on the smart contract. +// - contractAddress: The Bech32 address of the smart contract. +// - request: The request of type T to be sent to the smart contract. +// +// Returns: +// - response: The response of type K from the smart contract. +// +// Panics: +// - If an error occurs during the JSON marshaling, sudo call, or JSON unmarshaling process. +func MustSudo[T any, K any](ctx sdk.Context, contractKeeper ContractKeeper, contractAddress string, request T) (response K) { + response, err := Sudo[T, K](ctx, contractKeeper, contractAddress, request) + if err != nil { + panic(err) + } + + return response +} + +// Execute is a generic function to execute a contract call on a given contract address with a specified request. +// It accepts a context, contract keeper, contract address, caller address, coins, and request data. +// This function works with any data type for the request and response (T and K). +// It marshals the request data into JSON format, executes the contract call, and then unmarshals the response +// data back into the specified response type (K). In case of any error, it returns the zero value of the response +// type along with the error. +// +// Parameters: +// - ctx: The SDK context. +// - contractKeeper: An instance of the contract keeper to manage contract interactions. +// - contractAddress: The bech32 address of the contract to be executed. +// - caller: The address of the account making the call. +// - coins: The coins to be transferred during the call. +// - request: The request data, can be of any data type. +// +// Returns: +// - response: The response data, can be of any data type (K). Returns the zero value of K in case of an error. +// - err: An error object that indicates any error during the contract execution or data marshalling/unmarshalling process. +func Execute[T any, K any](ctx sdk.Context, contractKeeper ContractKeeper, contractAddress string, caller sdk.AccAddress, coins sdk.Coins, request T) (response K, err error) { + bz, err := json.Marshal(request) + if err != nil { + return response, err + } + + responseBz, err := contractKeeper.Execute(ctx, sdk.MustAccAddressFromBech32(contractAddress), caller, bz, coins) + if err != nil { + return response, err + } + + // valid empty response + if len(responseBz) == 0 { + return response, nil + } + + if err := json.Unmarshal(responseBz, &response); err != nil { + return response, err + } + + return response, nil +} + +// MustExecute is a wrapper around the Execute function, which provides a more concise API for +// executing a contract call on a given contract address with a specified request. +// It works with any data type for the request and response (T and K). +// This function panics if an error occurs during the contract execution or data marshalling/unmarshalling process. +// Use this function when you're confident that the contract execution will not encounter any errors. +// +// Parameters: +// - ctx: The SDK context. +// - contractKeeper: An instance of the contract keeper to manage contract interactions. +// - contractAddress: The bech32 address of the contract to be executed. +// - caller: The address of the account making the call. +// - coins: The coins to be transferred during the call. +// - request: The request data, can be of any data type. +// +// Returns: +// - response: The response data, can be of any data type (K). +func MustExecute[T any, K any](ctx sdk.Context, contractKeeper ContractKeeper, contractAddress string, caller sdk.AccAddress, coins sdk.Coins, request T) (response K) { + response, err := Execute[T, K](ctx, contractKeeper, contractAddress, caller, coins, request) + if err != nil { + panic(err) + } + + return response +} diff --git a/tests/interchaintest/osmosis/osmoutils/encoding_helper.go b/tests/interchaintest/osmosis/osmoutils/encoding_helper.go new file mode 100644 index 00000000..8d81fb82 --- /dev/null +++ b/tests/interchaintest/osmosis/osmoutils/encoding_helper.go @@ -0,0 +1,25 @@ +package osmoutils + +import ( + "fmt" + "time" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +func FormatFixedLengthU64(d uint64) string { + return fmt.Sprintf("%0.20d", d) +} + +func FormatTimeString(t time.Time) string { + return t.UTC().Round(0).Format(sdk.SortableTimeFormat) +} + +// Parses a string encoded using FormatTimeString back into a time.Time +func ParseTimeString(s string) (time.Time, error) { + t, err := time.Parse(sdk.SortableTimeFormat, s) + if err != nil { + return t, err + } + return t.UTC().Round(0), nil +} diff --git a/tests/interchaintest/osmosis/osmoutils/encoding_helper_test.go b/tests/interchaintest/osmosis/osmoutils/encoding_helper_test.go new file mode 100644 index 00000000..35690501 --- /dev/null +++ b/tests/interchaintest/osmosis/osmoutils/encoding_helper_test.go @@ -0,0 +1,29 @@ +package osmoutils + +import ( + "math" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestFormatFixedLengthU64(t *testing.T) { + tests := map[string]struct { + d uint64 + want string + }{ + "0": {0, "00000000000000000000"}, + "1": {1, "00000000000000000001"}, + "9": {9, "00000000000000000009"}, + "10": {10, "00000000000000000010"}, + "123": {123, "00000000000000000123"}, + "max u64": {math.MaxUint64, "18446744073709551615"}, + } + for name, tt := range tests { + t.Run(name, func(t *testing.T) { + got := FormatFixedLengthU64(tt.d) + assert.Equal(t, tt.want, got) + assert.Equal(t, len(got), 20) + }) + } +} diff --git a/tests/interchaintest/osmosis/osmoutils/errors.go b/tests/interchaintest/osmosis/osmoutils/errors.go new file mode 100644 index 00000000..54b6d10c --- /dev/null +++ b/tests/interchaintest/osmosis/osmoutils/errors.go @@ -0,0 +1,11 @@ +package osmoutils + +import "fmt" + +type DecNotFoundError struct { + Key string +} + +func (e DecNotFoundError) Error() string { + return fmt.Sprintf("no osmomath.Dec at key (%s)", e.Key) +} diff --git a/tests/interchaintest/osmosis/osmoutils/export_test.go b/tests/interchaintest/osmosis/osmoutils/export_test.go new file mode 100644 index 00000000..f6cfd341 --- /dev/null +++ b/tests/interchaintest/osmosis/osmoutils/export_test.go @@ -0,0 +1,11 @@ +package osmoutils + +import db "github.com/cometbft/cometbft-db" + +func GatherValuesFromIterator[T any](iterator db.Iterator, parseValue func([]byte) (T, error), stopFn func([]byte) bool) ([]T, error) { + return gatherValuesFromIterator(iterator, parseValue, stopFn) +} + +func NoStopFn(key []byte) bool { + return noStopFn(key) +} diff --git a/tests/interchaintest/osmosis/osmoutils/generic_helper.go b/tests/interchaintest/osmosis/osmoutils/generic_helper.go new file mode 100644 index 00000000..0a008afc --- /dev/null +++ b/tests/interchaintest/osmosis/osmoutils/generic_helper.go @@ -0,0 +1,17 @@ +package osmoutils + +import "reflect" + +// MakeNew makes a new instance of generic T. +// if T is a pointer, makes a new instance of the underlying struct via reflection, +// and then a pointer to it. +func MakeNew[T any]() T { + var v T + if typ := reflect.TypeOf(v); typ.Kind() == reflect.Ptr { + elem := typ.Elem() + //nolint:forcetypeassert + return reflect.New(elem).Interface().(T) // must use reflect + } else { + return *new(T) // v is not ptr, alloc with new + } +} diff --git a/tests/interchaintest/osmosis/osmoutils/ibc.go b/tests/interchaintest/osmosis/osmoutils/ibc.go new file mode 100644 index 00000000..8ebc06ec --- /dev/null +++ b/tests/interchaintest/osmosis/osmoutils/ibc.go @@ -0,0 +1,91 @@ +package osmoutils + +import ( + "encoding/json" + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" + transfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + ibcexported "github.com/cosmos/ibc-go/v7/modules/core/exported" +) + +const IbcAcknowledgementErrorType = "ibc-acknowledgement-error" + +// NewEmitErrorAcknowledgement creates a new error acknowledgement after having emitted an event with the +// details of the error. +func NewEmitErrorAcknowledgement(ctx sdk.Context, err error, errorContexts ...string) channeltypes.Acknowledgement { + EmitIBCErrorEvents(ctx, err, errorContexts) + + return channeltypes.NewErrorAcknowledgement(err) +} + +// NewSuccessAckRepresentingAnError creates a new success acknowledgement that represents an error. +// This is useful for notifying the sender that an error has occurred in a way that does not allow +// the received tokens to be reverted (which means they shouldn't be released by the sender's ics20 escrow) +func NewSuccessAckRepresentingAnError(ctx sdk.Context, err error, errorContent []byte, errorContexts ...string) channeltypes.Acknowledgement { + EmitIBCErrorEvents(ctx, err, errorContexts) + + return channeltypes.NewResultAcknowledgement(errorContent) +} + +// EmitIBCErrorEvents Emit and Log errors +func EmitIBCErrorEvents(ctx sdk.Context, err error, errorContexts []string) { + logger := ctx.Logger().With("module", IbcAcknowledgementErrorType) + + attributes := make([]sdk.Attribute, len(errorContexts)+1) + attributes[0] = sdk.NewAttribute("error", err.Error()) + for i, s := range errorContexts { + attributes[i+1] = sdk.NewAttribute("error-context", s) + logger.Error(fmt.Sprintf("error-context: %v", s)) + } + + ctx.EventManager().EmitEvents(sdk.Events{ + sdk.NewEvent( + IbcAcknowledgementErrorType, + attributes..., + ), + }) +} + +// MustExtractDenomFromPacketOnRecv takes a packet with a valid ICS20 token data in the Data field and returns the +// denom as represented in the local chain. +// If the data cannot be unmarshalled this function will panic +func MustExtractDenomFromPacketOnRecv(packet ibcexported.PacketI) string { + var data transfertypes.FungibleTokenPacketData + if err := json.Unmarshal(packet.GetData(), &data); err != nil { + panic("unable to unmarshal ICS20 packet data") + } + + var denom string + if transfertypes.ReceiverChainIsSource(packet.GetSourcePort(), packet.GetSourceChannel(), data.Denom) { + // remove prefix added by sender chain + voucherPrefix := transfertypes.GetDenomPrefix(packet.GetSourcePort(), packet.GetSourceChannel()) + + unprefixedDenom := data.Denom[len(voucherPrefix):] + + // coin denomination used in sending from the escrow address + denom = unprefixedDenom + + // The denomination used to send the coins is either the native denom or the hash of the path + // if the denomination is not native. + denomTrace := transfertypes.ParseDenomTrace(unprefixedDenom) + if denomTrace.Path != "" { + denom = denomTrace.IBCDenom() + } + } else { + prefixedDenom := transfertypes.GetDenomPrefix(packet.GetDestPort(), packet.GetDestChannel()) + data.Denom + denom = transfertypes.ParseDenomTrace(prefixedDenom).IBCDenom() + } + return denom +} + +// IsAckError checks an IBC acknowledgement to see if it's an error. +// This is a replacement for ack.Success() which is currently not working on some circumstances +func IsAckError(acknowledgement []byte) bool { + var ackErr channeltypes.Acknowledgement_Error + if err := json.Unmarshal(acknowledgement, &ackErr); err == nil && len(ackErr.Error) > 0 { + return true + } + return false +} diff --git a/tests/interchaintest/osmosis/osmoutils/module_account.go b/tests/interchaintest/osmosis/osmoutils/module_account.go new file mode 100644 index 00000000..c8d9ba70 --- /dev/null +++ b/tests/interchaintest/osmosis/osmoutils/module_account.go @@ -0,0 +1,86 @@ +package osmoutils + +import ( + "errors" + "fmt" + "reflect" + + sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + vestingtypes "github.com/cosmos/cosmos-sdk/x/auth/vesting/types" +) + +// OsmoUtilsExtraAccountTypes is a map of extra account types that can be overridden. +// This is defined as a global variable so it can be modified in the chain's app.go and used here without +// having to import the chain. Specifically, this is used for compatibility with Osmosis' Cosmos SDK fork +var OsmoUtilsExtraAccountTypes map[reflect.Type]struct{} + +type AccountKeeper interface { + NewAccount(sdk.Context, authtypes.AccountI) authtypes.AccountI + + GetAccount(ctx sdk.Context, addr sdk.AccAddress) authtypes.AccountI + SetAccount(ctx sdk.Context, acc authtypes.AccountI) +} + +// CanCreateModuleAccountAtAddr tells us if we can safely make a module account at +// a given address. By collision resistance of the address (given API safe construction), +// the only way for an account to be already be at this address is if its claimed by the same +// pre-image from the correct module, +// or some SDK command breaks assumptions and creates an account at designated address. +// This function checks if there is an account at that address, and runs some safety checks +// to be extra-sure its not a user account (e.g. non-zero sequence, pubkey, of fore-seen account types). +// If there is no account, or if we believe its not a user-spendable account, we allow module account +// creation at the address. +// else, we do not. +// +// TODO: This is generally from an SDK design flaw +// code based off wasmd code: https://github.com/CosmWasm/wasmd/pull/996 +// Its _mandatory_ that the caller do the API safe construction to generate a module account addr, +// namely, address.Module(ModuleName, {key}) +func CanCreateModuleAccountAtAddr(ctx sdk.Context, ak AccountKeeper, addr sdk.AccAddress) error { + existingAcct := ak.GetAccount(ctx, addr) + if existingAcct == nil { + return nil + } + if existingAcct.GetSequence() != 0 || existingAcct.GetPubKey() != nil { + return fmt.Errorf("cannot create module account %s, "+ + "due to an account at that address already existing & having sent txs", addr) + } + overrideAccountTypes := map[reflect.Type]struct{}{ + reflect.TypeOf(&authtypes.BaseAccount{}): {}, + reflect.TypeOf(&vestingtypes.DelayedVestingAccount{}): {}, + reflect.TypeOf(&vestingtypes.ContinuousVestingAccount{}): {}, + reflect.TypeOf(&vestingtypes.BaseVestingAccount{}): {}, + reflect.TypeOf(&vestingtypes.PeriodicVestingAccount{}): {}, + reflect.TypeOf(&vestingtypes.PermanentLockedAccount{}): {}, + } + for extraAccountType := range OsmoUtilsExtraAccountTypes { + overrideAccountTypes[extraAccountType] = struct{}{} + } + + if _, clear := overrideAccountTypes[reflect.TypeOf(existingAcct)]; clear { + return nil + } + return errors.New("cannot create module account %s, " + + "due to an account at that address already existing & not being an overridable type") +} + +// CreateModuleAccount creates a module account at the provided address. +// It overrides an account if it exists at that address, with a non-zero sequence number & pubkey +// Contract: addr is derived from `address.Module(ModuleName, key)` +func CreateModuleAccount(ctx sdk.Context, ak AccountKeeper, addr sdk.AccAddress) error { + err := CanCreateModuleAccountAtAddr(ctx, ak, addr) + if err != nil { + return err + } + + acc := ak.NewAccount( + ctx, + authtypes.NewModuleAccount( + authtypes.NewBaseAccountWithAddress(addr), + addr.String(), + ), + ) + ak.SetAccount(ctx, acc) + return nil +} diff --git a/tests/interchaintest/osmosis/osmoutils/noapptest/cdc.go b/tests/interchaintest/osmosis/osmoutils/noapptest/cdc.go new file mode 100644 index 00000000..8f9a2590 --- /dev/null +++ b/tests/interchaintest/osmosis/osmoutils/noapptest/cdc.go @@ -0,0 +1,45 @@ +package noapptest + +// NOTE: This file is pulled from the SDK: +// https://github.com/cosmos/cosmos-sdk/blob/2eb51447494163bc9beef1b2cc8aa91c3691b2a7/types/module/testutil/codec.go#L23 +import ( + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/std" + "github.com/cosmos/cosmos-sdk/types/module" + "github.com/cosmos/cosmos-sdk/x/auth/tx" +) + +// TestEncodingConfig defines an encoding configuration that is used for testing +// purposes. Note, MakeTestEncodingConfig takes a series of AppModuleBasic types +// which should only contain the relevant module being tested and any potential +// dependencies. +type TestEncodingConfig struct { + InterfaceRegistry types.InterfaceRegistry + Codec codec.Codec + TxConfig client.TxConfig + Amino *codec.LegacyAmino +} + +func MakeTestEncodingConfig(modules ...module.AppModuleBasic) TestEncodingConfig { + cdc := codec.NewLegacyAmino() + interfaceRegistry := types.NewInterfaceRegistry() + codec := codec.NewProtoCodec(interfaceRegistry) + + encCfg := TestEncodingConfig{ + InterfaceRegistry: interfaceRegistry, + Codec: codec, + TxConfig: tx.NewTxConfig(codec, tx.DefaultSignModes), + Amino: cdc, + } + + mb := module.NewBasicManager(modules...) + + std.RegisterLegacyAminoCodec(encCfg.Amino) + std.RegisterInterfaces(encCfg.InterfaceRegistry) + mb.RegisterLegacyAminoCodec(encCfg.Amino) + mb.RegisterInterfaces(encCfg.InterfaceRegistry) + + return encCfg +} diff --git a/tests/interchaintest/osmosis/osmoutils/noapptest/ctx.go b/tests/interchaintest/osmosis/osmoutils/noapptest/ctx.go new file mode 100644 index 00000000..2363d0b2 --- /dev/null +++ b/tests/interchaintest/osmosis/osmoutils/noapptest/ctx.go @@ -0,0 +1,33 @@ +package noapptest + +import ( + "time" + + "github.com/cometbft/cometbft/libs/log" + tmproto "github.com/cometbft/cometbft/proto/tendermint/types" + "github.com/cosmos/cosmos-sdk/store" + sdk "github.com/cosmos/cosmos-sdk/types" + + dbm "github.com/cometbft/cometbft-db" + storetypes "github.com/cosmos/cosmos-sdk/store/types" +) + +func CtxWithStoreKeys(keys []storetypes.StoreKey, header tmproto.Header, isCheckTx bool) sdk.Context { + db := dbm.NewMemDB() + logger := log.NewNopLogger() + cms := store.NewCommitMultiStore(db) + for _, key := range keys { + cms.MountStoreWithDB(key, storetypes.StoreTypeIAVL, db) + } + err := cms.LoadLatestVersion() + if err != nil { + panic(err) + } + return sdk.NewContext(cms, header, isCheckTx, logger) +} + +func DefaultCtxWithStoreKeys(storeKeys []storetypes.StoreKey) sdk.Context { + header := tmproto.Header{Height: 1, ChainID: "osmoutils-test-1", Time: time.Now().UTC()} + deliverTx := false + return CtxWithStoreKeys(storeKeys, header, deliverTx) +} diff --git a/tests/interchaintest/osmosis/osmoutils/osmoassert/helpers.go b/tests/interchaintest/osmosis/osmoutils/osmoassert/helpers.go new file mode 100644 index 00000000..3382b1b8 --- /dev/null +++ b/tests/interchaintest/osmosis/osmoutils/osmoassert/helpers.go @@ -0,0 +1,15 @@ +package osmoassert + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +var diffTypesErrorMessage = "cannot compare variables of different types" + +func failNowIfNot(t *testing.T, ok bool) { + if !ok { + require.FailNow(t, diffTypesErrorMessage) + } +} diff --git a/tests/interchaintest/osmosis/osmoutils/osmocli/cli_tester.go b/tests/interchaintest/osmosis/osmoutils/osmocli/cli_tester.go new file mode 100644 index 00000000..3c88feef --- /dev/null +++ b/tests/interchaintest/osmosis/osmoutils/osmocli/cli_tester.go @@ -0,0 +1,113 @@ +package osmocli + +import ( + "strings" + "testing" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/gogoproto/proto" + "github.com/spf13/cobra" + "github.com/spf13/pflag" + "github.com/stretchr/testify/require" +) + +type TxCliTestCase[M sdk.Msg] struct { + Cmd string + ExpectedMsg M + ExpectedErr bool + OnlyCheckValidateBasic bool +} + +type QueryCliTestCase[Q proto.Message] struct { + Cmd string + ExpectedQuery Q + ExpectedErr bool +} + +func RunTxTestCases[M sdk.Msg](t *testing.T, desc *TxCliDesc, testcases map[string]TxCliTestCase[M]) { + t.Helper() + for name, tc := range testcases { + t.Run(name, func(t *testing.T) { + RunTxTestCase(t, desc, &tc) + }) + } +} + +func RunQueryTestCases[Q proto.Message](t *testing.T, desc *QueryDescriptor, testcases map[string]QueryCliTestCase[Q]) { + t.Helper() + for name, tc := range testcases { + t.Run(name, func(t *testing.T) { + RunQueryTestCase(t, desc, &tc) + }) + } +} + +func RunTxTestCase[M sdk.Msg](t *testing.T, desc *TxCliDesc, tc *TxCliTestCase[M]) { + t.Helper() + cmd := BuildTxCli[M](desc) + err := resetCommandFlagValues(cmd) + require.NoError(t, err, "error in resetCommandFlagValues") + args := strings.Split(tc.Cmd, " ") + err = cmd.Flags().Parse(args) + require.NoError(t, err, "error in cmd.Flags().Parse(args)") + clientCtx := newClientContextWithFrom(t, cmd.Flags()) + + msg, err := desc.ParseAndBuildMsg(clientCtx, args, cmd.Flags()) + if tc.ExpectedErr { + require.Error(t, err) + return + } + require.NoError(t, err, "error in desc.ParseAndBuildMsg") + if tc.OnlyCheckValidateBasic { + require.NoError(t, msg.ValidateBasic()) + return + } + + require.Equal(t, tc.ExpectedMsg, msg) +} + +func RunQueryTestCase[Q proto.Message](t *testing.T, desc *QueryDescriptor, tc *QueryCliTestCase[Q]) { + t.Helper() + cmd := BuildQueryCli[Q, int](desc, nil) + err := resetCommandFlagValues(cmd) + require.NoError(t, err, "error in resetCommandFlagValues") + args := strings.Split(tc.Cmd, " ") + err = cmd.Flags().Parse(args) + require.NoError(t, err, "error in cmd.Flags().Parse(args)") + + req, err := desc.ParseQuery(args, cmd.Flags()) + if tc.ExpectedErr { + require.Error(t, err) + return + } + require.NoError(t, err, "error in desc.ParseQuery") + require.Equal(t, tc.ExpectedQuery, req) +} + +// This logic is copied from the SDK, it should've just been publicly exposed. +// But instead its buried within a mega-method. +func newClientContextWithFrom(t *testing.T, fs *pflag.FlagSet) client.Context { + t.Helper() + clientCtx := client.Context{} + from, _ := fs.GetString(flags.FlagFrom) + clientCtx = clientCtx.WithFrom(from).WithFromAddress(sdk.MustAccAddressFromBech32(from)).WithFromName(from) + return clientCtx +} + +// taken from https://github.com/golang/debug/pull/8, +// due to no cobra command for resetting flag value +func resetCommandFlagValues(cmd *cobra.Command) error { + var retErr error = nil + cmd.Flags().VisitAll(func(f *pflag.Flag) { + if f.Changed { + err := f.Value.Set(f.DefValue) + if err != nil { + retErr = err + } + f.Changed = false + } + }) + return retErr +} diff --git a/tests/interchaintest/osmosis/osmoutils/osmocli/dynamic.go b/tests/interchaintest/osmosis/osmoutils/osmocli/dynamic.go new file mode 100644 index 00000000..60c46d4b --- /dev/null +++ b/tests/interchaintest/osmosis/osmoutils/osmocli/dynamic.go @@ -0,0 +1,48 @@ +package osmocli + +import ( + "fmt" + "reflect" + "regexp" + "strings" + + "github.com/cosmos/gogoproto/proto" + + "github.com/osmosis-labs/fee-abstraction/v7/tests/interchaintest/osmosis/osmoutils" +) + +type Descriptor interface { + GetCustomFlagOverrides() map[string]string + AttachToUse(str string) +} + +// fields that are not provided as arguments +var nonAttachableFields []string = []string{"sender", "pagination", "owner", "admin"} + +// attachFieldsToUse extracts fields from reqP proto message and dynamically appends them into Use field +func attachFieldsToUse[reqP proto.Message](desc Descriptor) { + req := osmoutils.MakeNew[reqP]() + v := reflect.ValueOf(req).Type().Elem() // get underlying non-pointer struct + var useField string + for i := 0; i < v.NumField(); i++ { + fn := pascalToKebab(v.Field(i).Name) + + // if a field is parsed from a flag, skip it + if desc.GetCustomFlagOverrides()[fn] != "" || osmoutils.Contains(nonAttachableFields, fn) { + continue + } + + useField += fmt.Sprintf(" [%s]", fn) + } + + desc.AttachToUse(useField) +} + +// pascalToKebab converts PascalCase string to kebab-case string +func pascalToKebab(s string) string { + reg := regexp.MustCompile(`([a-z0-9])([A-Z])`) + s = reg.ReplaceAllString(s, `${1}-${2}`) + + // Convert everything to lowercase + return strings.ToLower(s) +} diff --git a/tests/interchaintest/osmosis/osmoutils/osmocli/flag_advice.go b/tests/interchaintest/osmosis/osmoutils/osmocli/flag_advice.go new file mode 100644 index 00000000..11da922a --- /dev/null +++ b/tests/interchaintest/osmosis/osmoutils/osmocli/flag_advice.go @@ -0,0 +1,79 @@ +package osmocli + +import ( + "strings" + + "github.com/spf13/cobra" + "github.com/spf13/pflag" +) + +type FlagAdvice struct { + HasPagination bool + + // Map of FieldName -> FlagName + CustomFlagOverrides map[string]string + CustomFieldParsers map[string]CustomFieldParserFn + + // Tx sender value + IsTx bool + TxSenderFieldName string + FromValue string +} + +type FlagDesc struct { + RequiredFlags []*pflag.FlagSet + OptionalFlags []*pflag.FlagSet +} + +type FieldReadLocation = bool + +const ( + UsedArg FieldReadLocation = true + UsedFlag FieldReadLocation = false +) + +// CustomFieldParser function. +type CustomFieldParserFn = func(arg string, flags *pflag.FlagSet) (valueToSet any, usedArg FieldReadLocation, err error) + +func (f FlagAdvice) Sanitize() FlagAdvice { + // map CustomFlagOverrides & CustomFieldParser keys to lower-case + // initialize if uninitialized + newFlagOverrides := make(map[string]string, len(f.CustomFlagOverrides)) + for k, v := range f.CustomFlagOverrides { + newFlagOverrides[strings.ToLower(k)] = v + } + f.CustomFlagOverrides = newFlagOverrides + newFlagParsers := make(map[string]CustomFieldParserFn, len(f.CustomFieldParsers)) + for k, v := range f.CustomFieldParsers { + newFlagParsers[strings.ToLower(k)] = v + } + f.CustomFieldParsers = newFlagParsers + return f +} + +func FlagOnlyParser[v any](f func(fs *pflag.FlagSet) (v, error)) CustomFieldParserFn { + return func(_arg string, fs *pflag.FlagSet) (any, FieldReadLocation, error) { + t, err := f(fs) + return t, UsedFlag, err + } +} + +// AddFlags from desc to cmd. +// Required flags are marked as required. +func AddFlags(cmd *cobra.Command, desc FlagDesc) { + for i := 0; i < len(desc.OptionalFlags); i++ { + cmd.Flags().AddFlagSet(desc.OptionalFlags[i]) + } + for i := 0; i < len(desc.RequiredFlags); i++ { + fs := desc.RequiredFlags[i] + cmd.Flags().AddFlagSet(fs) + + // mark all these flags as required. + fs.VisitAll(func(flag *pflag.Flag) { + err := cmd.MarkFlagRequired(flag.Name) + if err != nil { + panic(err) + } + }) + } +} diff --git a/tests/interchaintest/osmosis/osmoutils/osmocli/index_cmd.go b/tests/interchaintest/osmosis/osmoutils/osmocli/index_cmd.go new file mode 100644 index 00000000..b1d3f0da --- /dev/null +++ b/tests/interchaintest/osmosis/osmoutils/osmocli/index_cmd.go @@ -0,0 +1,31 @@ +package osmocli + +import ( + "fmt" + + "github.com/spf13/cobra" +) + +// Index command, but short is not set. That is left to caller. +func IndexCmd(moduleName string) *cobra.Command { + return &cobra.Command{ + Use: moduleName, + Short: fmt.Sprintf("Querying commands for the %s module", moduleName), + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: indexRunCmd, + } +} + +func indexRunCmd(cmd *cobra.Command, args []string) error { + usageTemplate := `Usage:{{if .HasAvailableSubCommands}} + {{.CommandPath}} [command]{{end}} + +{{if .HasAvailableSubCommands}}Available Commands:{{range .Commands}}{{if .IsAvailableCommand}} + {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}} + +Use "{{.CommandPath}} [command] --help" for more information about a command.{{end}} +` + cmd.SetUsageTemplate(usageTemplate) + return cmd.Help() +} diff --git a/tests/interchaintest/osmosis/osmoutils/osmocli/parsers.go b/tests/interchaintest/osmosis/osmoutils/osmocli/parsers.go new file mode 100644 index 00000000..bd008d87 --- /dev/null +++ b/tests/interchaintest/osmosis/osmoutils/osmocli/parsers.go @@ -0,0 +1,455 @@ +package osmocli + +import ( + "fmt" + "reflect" + "strconv" + "strings" + "time" + + "github.com/cosmos/cosmos-sdk/client" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/address" + "github.com/cosmos/cosmos-sdk/x/gov/client/cli" + "github.com/spf13/cobra" + "github.com/spf13/pflag" + + "github.com/osmosis-labs/fee-abstraction/v7/tests/interchaintest/osmosis/osmomath" + "github.com/osmosis-labs/fee-abstraction/v7/tests/interchaintest/osmosis/osmoutils" +) + +var DefaultGovAuthority = sdk.AccAddress(address.Module("gov")) + +const ( + FlagIsExpedited = "is-expedited" + FlagAuthority = "authority" +) + +// Parses arguments 1-1 from args +// makes an exception, where it allows Pagination to come from flags. +func ParseFieldsFromFlagsAndArgs[reqP any](flagAdvice FlagAdvice, flags *pflag.FlagSet, args []string) (reqP, error) { + req := osmoutils.MakeNew[reqP]() + v := reflect.ValueOf(req).Elem() + t := v.Type() + + argIndexOffset := 0 + // Iterate over the fields in the struct + for i := 0; i < t.NumField(); i++ { + arg := "" + if len(args) > i+argIndexOffset { + arg = args[i+argIndexOffset] + } + usedArg, err := ParseField(v, t, i, arg, flagAdvice, flags) + if err != nil { + return req, err + } + if !usedArg { + argIndexOffset -= 1 + } + } + return req, nil +} + +func ParseNumFields[reqP any]() int { + req := osmoutils.MakeNew[reqP]() + v := reflect.ValueOf(req).Elem() + t := v.Type() + return t.NumField() +} + +func ParseExpectedQueryFnName[reqP any]() string { + req := osmoutils.MakeNew[reqP]() + v := reflect.ValueOf(req).Elem() + s := v.Type().String() + // handle some non-std queries + var prefixTrimmed string + if strings.Contains(s, "Query") { + prefixTrimmed = strings.Split(s, "Query")[1] + } else { + prefixTrimmed = strings.Split(s, ".")[1] + } + suffixTrimmed := strings.TrimSuffix(prefixTrimmed, "Request") + return suffixTrimmed +} + +func ParseHasPagination[reqP any]() bool { + req := osmoutils.MakeNew[reqP]() + t := reflect.ValueOf(req).Elem().Type() + for i := 0; i < t.NumField(); i++ { + fType := t.Field(i) + if fType.Type.String() == paginationType { + return true + } + } + return false +} + +const paginationType = "*query.PageRequest" + +// ParseField parses field #fieldIndex from either an arg or a flag. +// Returns true if it was parsed from an argument. +// Returns error if there was an issue in parsing this field. +func ParseField(v reflect.Value, t reflect.Type, fieldIndex int, arg string, flagAdvice FlagAdvice, flags *pflag.FlagSet) (bool, error) { + fVal := v.Field(fieldIndex) + fType := t.Field(fieldIndex) + // fmt.Printf("Field %d: %s %s %s\n", fieldIndex, fType.Name, fType.Type, fType.Type.Kind()) + + lowercaseFieldNameStr := strings.ToLower(fType.Name) + if parseFn, ok := flagAdvice.CustomFieldParsers[lowercaseFieldNameStr]; ok { + v, usedArg, err := parseFn(arg, flags) + if err == nil { + fVal.Set(reflect.ValueOf(v)) + } + return usedArg, err + } + + parsedFromFlag, err := ParseFieldFromFlag(fVal, fType, flagAdvice, flags) + if err != nil { + return false, err + } + if parsedFromFlag { + return false, nil + } + return true, ParseFieldFromArg(fVal, fType, arg) +} + +// ParseFieldFromFlag attempts to parses the value of a field in a struct from a flag. +// The field is identified by the provided `reflect.StructField`. +// The flag advice and `pflag.FlagSet` are used to determine the flag to parse the field from. +// If the field corresponds to a value from a flag, true is returned. +// Otherwise, `false` is returned. +// In the true case, the parsed value is set on the provided `reflect.Value`. +// An error is returned if there is an issue parsing the field from the flag. +func ParseFieldFromFlag(fVal reflect.Value, fType reflect.StructField, flagAdvice FlagAdvice, flags *pflag.FlagSet) (bool, error) { + lowercaseFieldNameStr := strings.ToLower(fType.Name) + if flagName, ok := flagAdvice.CustomFlagOverrides[lowercaseFieldNameStr]; ok { + return true, parseFieldFromDirectlySetFlag(fVal, fType, flagName, flags) + } + + kind := fType.Type.Kind() + switch kind { + case reflect.String: + if flagAdvice.IsTx { + // matchesFieldName is true if lowercaseFieldNameStr is the same as TxSenderFieldName, + // or if TxSenderFieldName is left blank, then matches fields named "sender" or "owner" + matchesFieldName := (flagAdvice.TxSenderFieldName == lowercaseFieldNameStr) || + (flagAdvice.TxSenderFieldName == "" && (lowercaseFieldNameStr == "sender" || lowercaseFieldNameStr == "owner")) + if matchesFieldName { + fVal.SetString(flagAdvice.FromValue) + return true, nil + } + } + case reflect.Ptr: + if flagAdvice.HasPagination { + typeStr := fType.Type.String() + if typeStr == paginationType { + pageReq, err := client.ReadPageRequest(flags) + if err != nil { + return true, err + } + fVal.Set(reflect.ValueOf(pageReq)) + return true, nil + } + } + } + return false, nil +} + +func parseFieldFromDirectlySetFlag(fVal reflect.Value, fType reflect.StructField, flagName string, flags *pflag.FlagSet) error { + // get string. If its a string great, run through arg parser. Otherwise try setting directly + s, err := flags.GetString(flagName) + if err != nil { + flag := flags.Lookup(flagName) + if flag == nil { + return fmt.Errorf("Programmer set the flag name wrong. Flag %s does not exist", flagName) + } + t := flag.Value.Type() + if t == "uint64" { + u, err := flags.GetUint64(flagName) + if err != nil { + return err + } + fVal.SetUint(u) + return nil + } + } + return ParseFieldFromArg(fVal, fType, s) +} + +func ParseFieldFromArg(fVal reflect.Value, fType reflect.StructField, arg string) error { + // We can't pass in a negative number due to the way pflags works... + // This is an (extraordinarily ridiculous) workaround that checks if a negative int is encapsulated in square brackets, + // and if so, trims the square brackets + if strings.HasPrefix(arg, "[") && strings.HasSuffix(arg, "]") && arg[1] == '-' { + arg = strings.TrimPrefix(arg, "[") + arg = strings.TrimSuffix(arg, "]") + } + + switch fType.Type.Kind() { + case reflect.Bool: + b, err := strconv.ParseBool(arg) + if err != nil { + return fmt.Errorf("could not parse %s as bool for field %s: %w", arg, fType.Name, err) + } + fVal.SetBool(b) + return nil + // SetUint allows anyof type u8, u16, u32, u64, and uint + case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: + u, err := ParseUint(arg, fType.Name) + if err != nil { + return err + } + fVal.SetUint(u) + return nil + // SetInt allows anyof type i8,i16,i32,i64 and int + case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: + typeStr := fType.Type.String() + var i int64 + var err error + if typeStr == "time.Duration" { + dur, err2 := time.ParseDuration(arg) + i, err = int64(dur), err2 + } else { + i, err = ParseInt(arg, fType.Name) + } + if err != nil { + return err + } + fVal.SetInt(i) + return nil + case reflect.Float32, reflect.Float64: + typeStr := fType.Type.String() + f, err := ParseFloat(arg, typeStr) + if err != nil { + return err + } + fVal.SetFloat(f) + return nil + case reflect.String: + s, err := ParseDenom(arg, fType.Name) + if err != nil { + return err + } + fVal.SetString(s) + return nil + case reflect.Ptr: + case reflect.Slice: + typeStr := fType.Type.String() + if typeStr == "[]uint64" { + // Parse comma-separated uint64 values into []uint64 slice + strValues := strings.Split(arg, ",") + values := make([]uint64, len(strValues)) + for i, strValue := range strValues { + u, err := strconv.ParseUint(strValue, 10, 64) + if err != nil { + return err + } + values[i] = u + } + fVal.Set(reflect.ValueOf(values)) + return nil + } + if typeStr == "types.Coins" { + coins, err := ParseCoins(arg, fType.Name) + if err != nil { + return err + } + fVal.Set(reflect.ValueOf(coins)) + return nil + } + case reflect.Struct: + typeStr := fType.Type.String() + var v any + var err error + if typeStr == "types.Coin" { + v, err = ParseCoin(arg, fType.Name) + } else if typeStr == "math.Int" { + v, err = ParseSdkInt(arg, fType.Name) + } else if typeStr == "time.Time" { + v, err = ParseUnixTime(arg, fType.Name) + } else if typeStr == "math.LegacyDec" { + v, err = ParseSdkDec(arg, fType.Name) + } else { + return fmt.Errorf("struct field type not recognized. Got type %v", fType) + } + + if err != nil { + return err + } + fVal.Set(reflect.ValueOf(v)) + return nil + } + fmt.Println(fType.Type.Kind().String()) + return fmt.Errorf("field type not recognized. Got type %v", fType) +} + +func ParseUint(arg string, fieldName string) (uint64, error) { + v, err := strconv.ParseUint(arg, 10, 64) + if err != nil { + return 0, fmt.Errorf("could not parse %s as uint for field %s: %w", arg, fieldName, err) + } + return v, nil +} + +func ParseFloat(arg string, fieldName string) (float64, error) { + v, err := strconv.ParseFloat(arg, 64) + if err != nil { + return 0, fmt.Errorf("could not parse %s as float for field %s: %w", arg, fieldName, err) + } + return v, nil +} + +func ParseInt(arg string, fieldName string) (int64, error) { + v, err := strconv.ParseInt(arg, 10, 64) + if err != nil { + return 0, fmt.Errorf("could not parse %s as int for field %s: %w", arg, fieldName, err) + } + return v, nil +} + +func ParseUnixTime(arg string, fieldName string) (time.Time, error) { + timeUnix, err := strconv.ParseInt(arg, 10, 64) + if err != nil { + parsedTime, err := time.Parse(sdk.SortableTimeFormat, arg) + if err != nil { + return time.Time{}, fmt.Errorf("could not parse %s as time for field %s: %w", arg, fieldName, err) + } + + return parsedTime, nil + } + startTime := time.Unix(timeUnix, 0) + return startTime, nil +} + +func ParseDenom(arg string, fieldName string) (string, error) { + return strings.TrimSpace(arg), nil +} + +// TODO: Make this able to read from some local alias file for denoms. +func ParseCoin(arg string, fieldName string) (sdk.Coin, error) { + coin, err := sdk.ParseCoinNormalized(arg) + if err != nil { + return sdk.Coin{}, fmt.Errorf("could not parse %s as sdk.Coin for field %s: %w", arg, fieldName, err) + } + return coin, nil +} + +// TODO: Make this able to read from some local alias file for denoms. +func ParseCoins(arg string, fieldName string) (sdk.Coins, error) { + coins, err := sdk.ParseCoinsNormalized(arg) + if err != nil { + return sdk.Coins{}, fmt.Errorf("could not parse %s as sdk.Coins for field %s: %w", arg, fieldName, err) + } + return coins, nil +} + +// TODO: This really shouldn't be getting used in the CLI, its misdesign on the CLI ux +func ParseSdkInt(arg string, fieldName string) (osmomath.Int, error) { + i, ok := osmomath.NewIntFromString(arg) + if !ok { + return osmomath.Int{}, fmt.Errorf("could not parse %s as osmomath.Int for field %s", arg, fieldName) + } + return i, nil +} + +func ParseSdkDec(arg, fieldName string) (osmomath.Dec, error) { + i, err := osmomath.NewDecFromStr(arg) + if err != nil { + return osmomath.Dec{}, fmt.Errorf("could not parse %s as osmomath.Dec for field %s: %w", arg, fieldName, err) + } + return i, nil +} + +func ParseStringTo2DArray(input string) ([][]uint64, error) { + // Split the input string into sub-arrays + subArrays := strings.Split(input, ";") + + // Initialize the 2D array + result := make([][]uint64, len(subArrays)) + + // Iterate over each sub-array + for i, subArray := range subArrays { + // Split the sub-array into elements + elements := strings.Split(subArray, ",") + + // Initialize the sub-array in the 2D array + result[i] = make([]uint64, len(elements)) + + // Iterate over each element + for j, element := range elements { + // Parse the element into a uint64 + value, err := strconv.ParseUint(element, 10, 64) + if err != nil { + return nil, err + } + + // Store the value in the 2D array + result[i][j] = value + } + } + + return result, nil +} + +// ParseUint64SliceToString converts a slice of uint64 values into a string. +// Each uint64 value in the slice is converted to a string using base 10. +// The resulting strings are then joined together with a comma separator. +// The resulting string is returned. +func ParseUint64SliceToString(values []uint64) string { + strs := make([]string, len(values)) + for i, v := range values { + strs[i] = strconv.FormatUint(v, 10) + } + return strings.Join(strs, ", ") +} + +func GetProposalInfo(cmd *cobra.Command) (client.Context, string, string, sdk.Coins, bool, sdk.AccAddress, error) { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return client.Context{}, "", "", nil, false, nil, err + } + + proposalTitle, err := cmd.Flags().GetString(cli.FlagTitle) + if err != nil { + return clientCtx, proposalTitle, "", nil, false, nil, err + } + + summary, err := cmd.Flags().GetString(cli.FlagSummary) + if err != nil { + return client.Context{}, proposalTitle, summary, nil, false, nil, err + } + + depositArg, err := cmd.Flags().GetString(cli.FlagDeposit) + if err != nil { + return client.Context{}, proposalTitle, summary, nil, false, nil, err + } + + deposit, err := sdk.ParseCoinsNormalized(depositArg) + if err != nil { + return client.Context{}, proposalTitle, summary, deposit, false, nil, err + } + + isExpedited, err := cmd.Flags().GetBool(FlagIsExpedited) + if err != nil { + return client.Context{}, proposalTitle, summary, deposit, false, nil, err + } + + authorityString, err := cmd.Flags().GetString(FlagAuthority) + if err != nil { + return client.Context{}, proposalTitle, summary, deposit, false, nil, err + } + authority, err := sdk.AccAddressFromBech32(authorityString) + if err != nil { + return client.Context{}, proposalTitle, summary, deposit, false, nil, err + } + + return clientCtx, proposalTitle, summary, deposit, isExpedited, authority, nil +} + +func AddCommonProposalFlags(cmd *cobra.Command) { + cmd.Flags().String(cli.FlagTitle, "", "Title of proposal") + cmd.Flags().String(cli.FlagSummary, "", "Summary of proposal") + cmd.Flags().String(cli.FlagDeposit, "", "Deposit of proposal") + cmd.Flags().Bool(FlagIsExpedited, false, "Whether the proposal is expedited") + cmd.Flags().String(FlagAuthority, DefaultGovAuthority.String(), "The address of the governance account. Default is the sdk gov module account") +} diff --git a/tests/interchaintest/osmosis/osmoutils/osmocli/parsers_test.go b/tests/interchaintest/osmosis/osmoutils/osmocli/parsers_test.go new file mode 100644 index 00000000..60e34ca5 --- /dev/null +++ b/tests/interchaintest/osmosis/osmoutils/osmocli/parsers_test.go @@ -0,0 +1,207 @@ +package osmocli + +import ( + "reflect" + "testing" + "time" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" + + "github.com/osmosis-labs/fee-abstraction/v7/tests/interchaintest/osmosis/osmomath" +) + +type testingStruct struct { + Int int64 + UInt uint64 + String string + Float float64 + Duration time.Duration + Pointer *testingStruct + Slice sdk.Coins + Struct interface{} + Dec osmomath.Dec +} + +func TestParseFieldFromArg(t *testing.T) { + tests := map[string]struct { + testingStruct + arg string + fieldIndex int + + expectedStruct testingStruct + expectingErr bool + }{ + "Int value changes from -20 to 10": { + testingStruct: testingStruct{Int: -20}, + arg: "10", + fieldIndex: 0, + expectedStruct: testingStruct{Int: 10}, + }, + "Attempt to change Int value 20 to string value": { // does not return error, simply does not change the struct + testingStruct: testingStruct{Int: 20}, + arg: "hello", + fieldIndex: 0, + expectedStruct: testingStruct{Int: 20}, + }, + "UInt value changes from 20 to 10": { + testingStruct: testingStruct{UInt: 20}, + arg: "10", + fieldIndex: 1, + expectedStruct: testingStruct{UInt: 10}, + }, + "String value change": { + testingStruct: testingStruct{String: "hello"}, + arg: "world", + fieldIndex: 2, + expectedStruct: testingStruct{String: "world"}, + }, + "Changing unset value (simply sets the value)": { + testingStruct: testingStruct{Int: 20}, + arg: "hello", + fieldIndex: 2, + expectedStruct: testingStruct{Int: 20, String: "hello"}, + }, + "Float value change": { + testingStruct: testingStruct{Float: 20.0}, + arg: "30.0", + fieldIndex: 3, + expectedStruct: testingStruct{Float: 30.0}, + }, + "Duration value changes from .Hour to .Second": { + testingStruct: testingStruct{Duration: time.Hour}, + arg: "1s", + fieldIndex: 4, + expectedStruct: testingStruct{Duration: time.Second}, + }, + "Attempt to change pointer": { // for reflect.Ptr kind ParseFieldFromArg does nothing, hence no changes take place + testingStruct: testingStruct{Pointer: &testingStruct{}}, + arg: "*whatever", + fieldIndex: 5, + expectedStruct: testingStruct{Pointer: &testingStruct{}}, + }, + "Slice change": { + testingStruct: testingStruct{Slice: sdk.Coins{ + sdk.NewCoin("foo", osmomath.NewInt(100)), + sdk.NewCoin("bar", osmomath.NewInt(100)), + }}, + arg: "10foo,10bar", // Should be of a format suitable for ParseCoinsNormalized + fieldIndex: 6, + expectedStruct: testingStruct{Slice: sdk.Coins{ // swapped places due to lexicographic order + sdk.NewCoin("bar", osmomath.NewInt(10)), + sdk.NewCoin("foo", osmomath.NewInt(10)), + }}, + }, + "Struct (sdk.Coin) change": { + testingStruct: testingStruct{Struct: sdk.NewCoin("bar", osmomath.NewInt(10))}, // only supports osmomath.Int, sdk.Coin or time.Time, other structs are not recognized + arg: "100bar", + fieldIndex: 7, + expectedStruct: testingStruct{Struct: sdk.NewCoin("bar", osmomath.NewInt(10))}, + }, + "Unrecognizable struct": { + testingStruct: testingStruct{Struct: testingStruct{}}, // only supports osmomath.Int, sdk.Coin or time.Time, other structs are not recognized + arg: "whatever", + fieldIndex: 7, + expectingErr: true, + }, + "Multiple fields in struct are set": { + testingStruct: testingStruct{Int: 20, UInt: 10, String: "hello", Pointer: &testingStruct{}}, + arg: "world", + fieldIndex: 2, + expectedStruct: testingStruct{Int: 20, UInt: 10, String: "world", Pointer: &testingStruct{}}, + }, + "All fields in struct set": { + testingStruct: testingStruct{ + Int: 20, + UInt: 10, + String: "hello", + Float: 30.0, + Duration: time.Second, + Pointer: &testingStruct{}, + Slice: sdk.Coins{ + sdk.NewCoin("foo", osmomath.NewInt(100)), + sdk.NewCoin("bar", osmomath.NewInt(100)), + }, + Struct: sdk.NewCoin("bar", osmomath.NewInt(10)), + }, + arg: "1foo,15bar", + fieldIndex: 6, + expectedStruct: testingStruct{ + Int: 20, + UInt: 10, + String: "hello", + Float: 30.0, + Duration: time.Second, + Pointer: &testingStruct{}, + Slice: sdk.Coins{ + sdk.NewCoin("bar", osmomath.NewInt(15)), + sdk.NewCoin("foo", osmomath.NewInt(1)), + }, + Struct: sdk.NewCoin("bar", osmomath.NewInt(10)), + }, + }, + "Dec struct": { + testingStruct: testingStruct{Dec: osmomath.MustNewDecFromStr("100")}, + arg: "10", + fieldIndex: 8, + expectedStruct: testingStruct{Dec: osmomath.MustNewDecFromStr("10")}, + }, + } + + for name, tc := range tests { + tc := tc + t.Run(name, func(t *testing.T) { + val := reflect.ValueOf(&tc.testingStruct).Elem() + typ := reflect.TypeOf(&tc.testingStruct).Elem() + + fVal := val.Field(tc.fieldIndex) + fType := typ.Field(tc.fieldIndex) + + err := ParseFieldFromArg(fVal, fType, tc.arg) + + if !tc.expectingErr { + require.Equal(t, tc.expectedStruct, tc.testingStruct) + } else { + require.Error(t, err) + } + }) + } +} + +func TestParseUint64SliceToString(t *testing.T) { + tests := []struct { + name string + input []uint64 + expected string + }{ + { + name: "Test with empty slice", + input: []uint64{}, + expected: "", + }, + { + name: "Test with single element", + input: []uint64{1}, + expected: "1", + }, + { + name: "Test with multiple elements", + input: []uint64{1, 2, 3, 4, 5}, + expected: "1, 2, 3, 4, 5", + }, + { + name: "Test with multiple elements out of order", + input: []uint64{9, 1, 2, 3, 4, 5}, + expected: "9, 1, 2, 3, 4, 5", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := ParseUint64SliceToString(tt.input) + if result != tt.expected { + t.Errorf("expected %s, got %s", tt.expected, result) + } + }) + } +} diff --git a/tests/interchaintest/osmosis/osmoutils/osmocli/query_cmd_wrap.go b/tests/interchaintest/osmosis/osmoutils/osmocli/query_cmd_wrap.go new file mode 100644 index 00000000..68d5fc6a --- /dev/null +++ b/tests/interchaintest/osmosis/osmoutils/osmocli/query_cmd_wrap.go @@ -0,0 +1,194 @@ +package osmocli + +import ( + "context" + "fmt" + "reflect" + "strings" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + grpc1 "github.com/cosmos/gogoproto/grpc" + "github.com/cosmos/gogoproto/proto" + "github.com/spf13/cobra" + "github.com/spf13/pflag" +) + +// global variable set on index command. +// helps populate Longs, when not set in QueryDescriptor. +var lastQueryModuleName string + +type QueryDescriptor struct { + Use string + Short string + Long string + + HasPagination bool + + QueryFnName string + + Flags FlagDesc + // Map of FieldName -> FlagName + CustomFlagOverrides map[string]string + // Map of FieldName -> CustomParseFn + CustomFieldParsers map[string]CustomFieldParserFn + + ParseQuery func(args []string, flags *pflag.FlagSet) (proto.Message, error) + + ModuleName string + numArgs int +} + +var _ Descriptor = &QueryDescriptor{} + +// Implement Descriptor interface +func (desc QueryDescriptor) GetCustomFlagOverrides() map[string]string { + return desc.CustomFlagOverrides +} + +// Implement Descriptor interface +func (desc *QueryDescriptor) AttachToUse(str string) { + desc.Use += str +} + +func QueryIndexCmd(moduleName string) *cobra.Command { + cmd := IndexCmd(moduleName) + cmd.Short = fmt.Sprintf("Querying commands for the %s module", moduleName) + lastQueryModuleName = moduleName + return cmd +} + +func AddQueryCmd[Q proto.Message, querier any](cmd *cobra.Command, newQueryClientFn func(grpc1.ClientConn) querier, f func() (*QueryDescriptor, Q)) { + desc, _ := f() + subCmd := BuildQueryCli[Q](desc, newQueryClientFn) + cmd.AddCommand(subCmd) +} + +func (desc *QueryDescriptor) FormatLong(moduleName string) { + desc.Long = FormatLongDesc(desc.Long, NewLongMetadata(moduleName).WithShort(desc.Short)) +} + +func prepareDescriptor[reqP proto.Message](desc *QueryDescriptor) { + if !desc.HasPagination { + desc.HasPagination = ParseHasPagination[reqP]() + } + if desc.QueryFnName == "" { + desc.QueryFnName = ParseExpectedQueryFnName[reqP]() + } + if strings.Contains(desc.Long, "{") { + if desc.ModuleName == "" { + desc.ModuleName = lastQueryModuleName + } + desc.FormatLong(desc.ModuleName) + } + + desc.numArgs = ParseNumFields[reqP]() - len(desc.CustomFlagOverrides) + if desc.HasPagination { + desc.numArgs = desc.numArgs - 1 + } +} + +func BuildQueryCli[reqP proto.Message, querier any](desc *QueryDescriptor, newQueryClientFn func(grpc1.ClientConn) querier) *cobra.Command { + prepareDescriptor[reqP](desc) + if desc.ParseQuery == nil { + desc.ParseQuery = func(args []string, fs *pflag.FlagSet) (proto.Message, error) { + flagAdvice := FlagAdvice{ + HasPagination: desc.HasPagination, + CustomFlagOverrides: desc.CustomFlagOverrides, + CustomFieldParsers: desc.CustomFieldParsers, + }.Sanitize() + return ParseFieldsFromFlagsAndArgs[reqP](flagAdvice, fs, args) + } + } + + attachFieldsToUse[reqP](desc) + cmd := &cobra.Command{ + Use: desc.Use, + Short: desc.Short, + Long: desc.Long, + Args: cobra.ExactArgs(desc.numArgs), + RunE: queryLogic(desc, newQueryClientFn), + } + flags.AddQueryFlagsToCmd(cmd) + AddFlags(cmd, desc.Flags) + if desc.HasPagination { + cmdName := strings.Split(desc.Use, " ")[0] + flags.AddPaginationFlagsToCmd(cmd, cmdName) + } + + return cmd +} + +// SimpleQueryCmd builds a query, for the common, simple case. +// It detects that the querier function name is the same as the ProtoMessage name, +// with just the "Query" and "Request" args chopped off. +// It expects all proto fields to appear as arguments, in order. +func SimpleQueryCmd[reqP proto.Message, querier any](use string, short string, long string, + moduleName string, newQueryClientFn func(grpc1.ClientConn) querier, +) *cobra.Command { + desc := QueryDescriptor{ + Use: use, + Short: short, + Long: FormatLongDesc(long, NewLongMetadata(moduleName).WithShort(short)), + } + return BuildQueryCli[reqP](&desc, newQueryClientFn) +} + +func GetParams[reqP proto.Message, querier any](moduleName string, + newQueryClientFn func(grpc1.ClientConn) querier, +) *cobra.Command { + return BuildQueryCli[reqP](&QueryDescriptor{ + Use: "params [flags]", + Short: fmt.Sprintf("Get the params for the x/%s module", moduleName), + QueryFnName: "Params", + }, newQueryClientFn) +} + +func callQueryClientFn(ctx context.Context, fnName string, req proto.Message, q any) (res proto.Message, err error) { + qVal := reflect.ValueOf(q) + method := qVal.MethodByName(fnName) + if (method == reflect.Value{}) { + return nil, fmt.Errorf("Method %s does not exist on the querier."+ + " You likely need to override QueryFnName in your Query descriptor", fnName) + } + args := []reflect.Value{ + reflect.ValueOf(ctx), + reflect.ValueOf(req), + } + results := method.Call(args) + if len(results) != 2 { + panic("We got something wrong") + } + if !results[1].IsNil() { + //nolint:forcetypeassert + err = results[1].Interface().(error) + return res, err + } + //nolint:forcetypeassert + res = results[0].Interface().(proto.Message) + return res, nil +} + +func queryLogic[querier any](desc *QueryDescriptor, + newQueryClientFn func(grpc1.ClientConn) querier, +) func(cmd *cobra.Command, args []string) error { + return func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + queryClient := newQueryClientFn(clientCtx) + + req, err := desc.ParseQuery(args, cmd.Flags()) + if err != nil { + return err + } + + res, err := callQueryClientFn(cmd.Context(), desc.QueryFnName, req, queryClient) + if err != nil { + return err + } + + return clientCtx.PrintProto(res) + } +} diff --git a/tests/interchaintest/osmosis/osmoutils/osmocli/string_formatter.go b/tests/interchaintest/osmosis/osmoutils/osmocli/string_formatter.go new file mode 100644 index 00000000..43c57725 --- /dev/null +++ b/tests/interchaintest/osmosis/osmoutils/osmocli/string_formatter.go @@ -0,0 +1,49 @@ +package osmocli + +import ( + "fmt" + "strings" + "text/template" + + "github.com/cosmos/cosmos-sdk/version" +) + +type LongMetadata struct { + BinaryName string + CommandPrefix string + Short string + + // Newline Example: + ExampleHeader string +} + +func NewLongMetadata(moduleName string) *LongMetadata { + commandPrefix := fmt.Sprintf("$ %s q %s", version.AppName, moduleName) + return &LongMetadata{ + BinaryName: version.AppName, + CommandPrefix: commandPrefix, + } +} + +func (m *LongMetadata) WithShort(short string) *LongMetadata { + m.Short = short + return m +} + +func FormatLongDesc(longString string, meta *LongMetadata) string { + template, err := template.New("long_description").Parse(longString) + if err != nil { + panic("incorrectly configured long message") + } + bld := strings.Builder{} + meta.ExampleHeader = "\n\nExample:" + err = template.Execute(&bld, meta) + if err != nil { + panic("incorrectly configured long message") + } + return strings.TrimSpace(bld.String()) +} + +func FormatLongDescDirect(longString string, moduleName string) string { + return FormatLongDesc(longString, NewLongMetadata(moduleName)) +} diff --git a/tests/interchaintest/osmosis/osmoutils/osmocli/tx_cmd_wrap.go b/tests/interchaintest/osmosis/osmoutils/osmocli/tx_cmd_wrap.go new file mode 100644 index 00000000..62af6a37 --- /dev/null +++ b/tests/interchaintest/osmosis/osmoutils/osmocli/tx_cmd_wrap.go @@ -0,0 +1,117 @@ +package osmocli + +import ( + "fmt" + "strings" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/client/tx" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/spf13/cobra" + "github.com/spf13/pflag" +) + +func TxIndexCmd(moduleName string) *cobra.Command { + cmd := IndexCmd(moduleName) + cmd.Short = fmt.Sprintf("%s transactions subcommands", moduleName) + return cmd +} + +type TxCliDesc struct { + Use string + Short string + Long string + Example string + + NumArgs int + // Contract: len(args) = NumArgs + ParseAndBuildMsg func(clientCtx client.Context, args []string, flags *pflag.FlagSet) (sdk.Msg, error) + TxSignerFieldName string + + Flags FlagDesc + // Map of FieldName -> FlagName + CustomFlagOverrides map[string]string + // Map of FieldName -> CustomParseFn + CustomFieldParsers map[string]CustomFieldParserFn +} + +var _ Descriptor = &TxCliDesc{} + +// Implement Descriptor interface +func (desc TxCliDesc) GetCustomFlagOverrides() map[string]string { + return desc.CustomFlagOverrides +} + +// Implement Descriptor interface +func (desc *TxCliDesc) AttachToUse(str string) { + desc.Use += str +} + +func AddTxCmd[M sdk.Msg](cmd *cobra.Command, f func() (*TxCliDesc, M)) { + desc, _ := f() + subCmd := BuildTxCli[M](desc) + cmd.AddCommand(subCmd) +} + +func BuildTxCli[M sdk.Msg](desc *TxCliDesc) *cobra.Command { + desc.TxSignerFieldName = strings.ToLower(desc.TxSignerFieldName) + if desc.NumArgs == 0 { + // NumArgs = NumFields - 1, since 1 field is from the msg + desc.NumArgs = ParseNumFields[M]() - 1 - len(desc.CustomFlagOverrides) - len(desc.CustomFieldParsers) + } + if desc.ParseAndBuildMsg == nil { + desc.ParseAndBuildMsg = func(clientCtx client.Context, args []string, flags *pflag.FlagSet) (sdk.Msg, error) { + flagAdvice := FlagAdvice{ + IsTx: true, + TxSenderFieldName: desc.TxSignerFieldName, + FromValue: clientCtx.GetFromAddress().String(), + CustomFlagOverrides: desc.CustomFlagOverrides, + CustomFieldParsers: desc.CustomFieldParsers, + }.Sanitize() + return ParseFieldsFromFlagsAndArgs[M](flagAdvice, flags, args) + } + } + + attachFieldsToUse[M](desc) + return desc.BuildCommandCustomFn() +} + +// Creates a new cobra command given the description. +// Its up to then caller to add CLI flags, aside from `flags.AddTxFlagsToCmd(cmd)` +func (desc TxCliDesc) BuildCommandCustomFn() *cobra.Command { + cmd := &cobra.Command{ + Use: desc.Use, + Short: desc.Short, + Args: cobra.ExactArgs(desc.NumArgs), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + + txf, err := tx.NewFactoryCLI(clientCtx, cmd.Flags()) + if err != nil { + return err + } + txf = txf.WithTxConfig(clientCtx.TxConfig).WithAccountRetriever(clientCtx.AccountRetriever) + + msg, err := desc.ParseAndBuildMsg(clientCtx, args, cmd.Flags()) + if err != nil { + return err + } + + return tx.GenerateOrBroadcastTxWithFactory(clientCtx, txf, msg) + }, + } + if desc.Example != "" { + cmd.Example = desc.Example + } + if desc.Long != "" { + cmd.Long = desc.Long + } + + flags.AddTxFlagsToCmd(cmd) + AddFlags(cmd, desc.Flags) + return cmd +} diff --git a/tests/interchaintest/osmosis/osmoutils/params_validation.go b/tests/interchaintest/osmosis/osmoutils/params_validation.go new file mode 100644 index 00000000..e5f2a0bd --- /dev/null +++ b/tests/interchaintest/osmosis/osmoutils/params_validation.go @@ -0,0 +1,30 @@ +package osmoutils + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// ValidateAddressList validates a slice of addresses. +// +// Parameters: +// - i: The parameter to validate. +// +// Returns: +// - An error if any of the strings are not addresses +func ValidateAddressList(i interface{}) error { + whitelist, ok := i.([]string) + + if !ok { + return fmt.Errorf("invalid parameter type: %T", i) + } + + for _, a := range whitelist { + if _, err := sdk.AccAddressFromBech32(a); err != nil { + return fmt.Errorf("invalid address") + } + } + + return nil +} diff --git a/tests/interchaintest/osmosis/osmoutils/parse.go b/tests/interchaintest/osmosis/osmoutils/parse.go new file mode 100644 index 00000000..cf172a37 --- /dev/null +++ b/tests/interchaintest/osmosis/osmoutils/parse.go @@ -0,0 +1,71 @@ +package osmoutils + +import ( + "encoding/json" + "fmt" + "os" + + "github.com/cosmos/cosmos-sdk/x/gov/client/cli" + "github.com/spf13/pflag" +) + +type Proposal struct { + Title string + Description string + Deposit string +} + +var ProposalFlags = []string{ + cli.FlagTitle, + cli.FlagDescription, //nolint:staticcheck + cli.FlagDeposit, +} + +func (p Proposal) validate() error { + if p.Title == "" { + return fmt.Errorf("proposal title is required") + } + + if p.Description == "" { + return fmt.Errorf("proposal description is required") + } + return nil +} + +func ParseProposalFlags(fs *pflag.FlagSet) (*Proposal, error) { + proposal := &Proposal{} + proposalFile, _ := fs.GetString(cli.FlagProposal) + + if proposalFile == "" { + proposal.Title, _ = fs.GetString(cli.FlagTitle) + proposal.Description, _ = fs.GetString(cli.FlagDescription) //nolint:staticcheck + proposal.Deposit, _ = fs.GetString(cli.FlagDeposit) + if err := proposal.validate(); err != nil { + return nil, err + } + + return proposal, nil + } + + for _, flag := range ProposalFlags { + if v, _ := fs.GetString(flag); v != "" { + return nil, fmt.Errorf("--%s flag provided alongside --proposal, which is a noop", flag) + } + } + + contents, err := os.ReadFile(proposalFile) + if err != nil { + return nil, err + } + + err = json.Unmarshal(contents, proposal) + if err != nil { + return nil, err + } + + if err := proposal.validate(); err != nil { + return nil, err + } + + return proposal, nil +} diff --git a/tests/interchaintest/osmosis/osmoutils/partialord/internal/dag/dag.go b/tests/interchaintest/osmosis/osmoutils/partialord/internal/dag/dag.go new file mode 100644 index 00000000..b81c350a --- /dev/null +++ b/tests/interchaintest/osmosis/osmoutils/partialord/internal/dag/dag.go @@ -0,0 +1,320 @@ +package dag + +import ( + "fmt" + "sort" +) + +// DAG struct maintains a directed acyclic graph, using adjacency lists to track edges. +type DAG struct { + // there is a directed edge from u -> v, if directedEdgeList[u][v] = 1 + // there is a directed edge from v -> u, if directedEdgeList[u][v] = -1 + directedEdgeList []map[int]int8 + nodeNameToId map[string]int + idToNodeNames map[int]string +} + +func NewDAG(nodes []string) DAG { + nodeNameToId := make(map[string]int, len(nodes)) + idToNodeNames := make(map[int]string, len(nodes)) + directedEdgeList := make([]map[int]int8, len(nodes)) + for i, node := range nodes { + nodeNameToId[node] = i + idToNodeNames[i] = node + directedEdgeList[i] = map[int]int8{} + } + if len(nodeNameToId) != len(nodes) { + panic("provided multiple nodes with the same name") + } + return DAG{ + directedEdgeList: directedEdgeList, + nodeNameToId: nodeNameToId, + idToNodeNames: idToNodeNames, + } +} + +// Copy returns a new dag struct that is a copy of the original dag. +// Edges can be mutated in the copy, without altering the original. +func (dag DAG) Copy() DAG { + directedEdgeList := make([]map[int]int8, len(dag.nodeNameToId)) + for i := 0; i < len(dag.nodeNameToId); i++ { + originalEdgeList := dag.directedEdgeList[i] + directedEdgeList[i] = make(map[int]int8, len(originalEdgeList)) + for k, v := range originalEdgeList { + directedEdgeList[i][k] = v + } + } + // we reuse nodeNameToId and idToNodeNames as these are fixed at dag creation. + return DAG{ + directedEdgeList: directedEdgeList, + nodeNameToId: dag.nodeNameToId, + idToNodeNames: dag.idToNodeNames, + } +} + +func (dag DAG) hasDirectedEdge(u, v int) bool { + uAdjacencyList := dag.directedEdgeList[u] + _, exists := uAdjacencyList[v] + return exists +} + +// addEdge adds a directed edge from u -> v. +func (dag *DAG) addEdge(u, v int) error { + if u == v { + return fmt.Errorf("can't make self-edge") + } + if dag.hasDirectedEdge(v, u) { + return fmt.Errorf("dag has conflicting edge") + } + dag.directedEdgeList[u][v] = 1 + dag.directedEdgeList[v][u] = -1 + return nil +} + +// replaceEdge adds a directed edge from u -> v. +// it removes any edge that may already exist between the two. +func (dag *DAG) replaceEdge(u, v int) error { + if u == v { + return fmt.Errorf("can't make self-edge") + } + + dag.directedEdgeList[u][v] = 1 + dag.directedEdgeList[v][u] = -1 + return nil +} + +// resetEdges deletes all edges directed to or from node `u` +func (dag *DAG) resetEdges(u int) { + edges := dag.directedEdgeList[u] + for v := range edges { + delete(dag.directedEdgeList[v], u) + } + dag.directedEdgeList[u] = map[int]int8{} +} + +// deleteEdge deletes edges between u and v. +func (dag *DAG) deleteEdge(u, v int) { + delete(dag.directedEdgeList[u], v) + delete(dag.directedEdgeList[v], u) +} + +// AddEdge checks if either edge between u and v exists and adds a directed edge from u -> v +func (dag *DAG) AddEdge(u, v string) error { + uIndex, uExists := dag.nodeNameToId[u] + vIndex, vExists := dag.nodeNameToId[v] + if !uExists || !vExists { + return fmt.Errorf("one of %s, %s does not exist in dag", u, v) + } + return dag.addEdge(uIndex, vIndex) +} + +// ReplaceEdge adds a directed edge from u -> v. +// it removes any edge that may already exist between the two. +func (dag *DAG) ReplaceEdge(u, v string) error { + uIndex, uExists := dag.nodeNameToId[u] + vIndex, vExists := dag.nodeNameToId[v] + if !uExists || !vExists { + return fmt.Errorf("one of %s, %s does not exist in dag", u, v) + } + return dag.replaceEdge(uIndex, vIndex) +} + +// AddFirstElements sets the provided elements to be first in all orderings. +// So if were making an ordering over {A, B, C, D, E}, and elems provided is {D, B, A} +// then we are guaranteed that the total ordering will begin with {D, B, A} +func (dag *DAG) AddFirstElements(nodes ...string) error { + nodeIds, err := dag.namesToIds(nodes) + if err != nil { + return err + } + + return dag.addFirst(nodeIds) +} + +func (dag *DAG) addFirst(nodes []int) error { + nodeMap := map[int]bool{} + for i := 0; i < len(nodes); i++ { + nodeMap[nodes[i]] = true + } + // First we add an edge from nodes[-1] to every node in the graph aside from the provided first nodes. + // then we make nodes[-1] depend on nodes[-2], etc. + // We also clear all other edges from nodes[-2], to override previous settings. + lastOfFirstNodes := nodes[len(nodes)-1] + for i := 0; i < len(dag.nodeNameToId); i++ { + // skip any node in the 'first set' + _, inMap := nodeMap[i] + if inMap { + continue + } + // We make everything on `lastOfFirstNodes`, and therefore have an edge from `lastOfFirstNodes` to it + err := dag.replaceEdge(lastOfFirstNodes, i) + // can't happen by above check + if err != nil { + return err + } + } + + // Make nodes[i+1] depend on nodes[i] + for i := len(nodes) - 2; i >= 0; i-- { + dag.resetEdges(nodes[i]) + err := dag.replaceEdge(nodes[i], nodes[i+1]) + // can't happen by above check + if err != nil { + return err + } + } + return nil +} + +// AddLastElements sets the provided elements to be last in all orderings. +// So if were making an ordering over {A, B, C, D, E}, and elems provided is {D, B, A} +// then we are guaranteed that the total ordering will end with {D, B, A} +func (dag *DAG) AddLastElements(nodes ...string) error { + nodeIds, err := dag.namesToIds(nodes) + if err != nil { + return err + } + + return dag.addLast(nodeIds) +} + +func (dag *DAG) addLast(nodes []int) error { + nodeMap := map[int]bool{} + for i := 0; i < len(nodes); i++ { + nodeMap[nodes[i]] = true + } + // First we add an edge from every node in the graph aside from the provided last nodes, to nodes[0] + // then we make nodes[1] depend on nodes[0], etc. + // We also clear all other edges from nodes[1], to override previous settings. + firstOfLastNodes := nodes[0] + for i := 0; i < len(dag.nodeNameToId); i++ { + // skip any node in the 'last set' + _, inMap := nodeMap[i] + if inMap { + continue + } + // We make `firstOfLastNodes` depend on every node, and therefore have an edge from each node to `firstOfLastNodes` + err := dag.replaceEdge(i, firstOfLastNodes) + // can't happen by above check + if err != nil { + return err + } + } + + // Make nodes[i] depend on nodes[i-1], and clear all other edges from nodes[i] + for i := 1; i < len(nodes); i++ { + dag.resetEdges(nodes[i]) + err := dag.replaceEdge(nodes[i-1], nodes[i]) + // can't happen by above check + if err != nil { + return err + } + } + return nil +} + +func (dag DAG) hasEdges() bool { + for _, m := range dag.directedEdgeList { + if len(m) > 0 { + return true + } + } + return false +} + +func (dag *DAG) namesToIds(names []string) ([]int, error) { + nodeIds := []int{} + for _, name := range names { + nodeIndex, nodeExists := dag.nodeNameToId[name] + if !nodeExists { + return []int{}, fmt.Errorf("%s does not exist in dag", name) + } + nodeIds = append(nodeIds, nodeIndex) + } + return nodeIds, nil +} + +func (dag DAG) idsToNames(ids []int) []string { + sortedNames := make([]string, 0, len(ids)) + for i := 0; i < len(dag.nodeNameToId); i++ { + id := ids[i] + sortedNames = append(sortedNames, dag.idToNodeNames[id]) + } + return sortedNames +} + +func (dag DAG) hasIncomingEdge(u int) bool { + adjacencyList := dag.directedEdgeList[u] + for _, v := range adjacencyList { + if v == -1 { + return true + } + } + return false +} + +// returns nodes with no incoming edges. +func (dag *DAG) topologicalTopLevelNodes() []int { + topLevelNodes := []int{} + + for i := 0; i < len(dag.nodeNameToId); i++ { + if !dag.hasIncomingEdge(i) { + topLevelNodes = append(topLevelNodes, i) + } + } + + return topLevelNodes +} + +// Returns a Topological Sort of the DAG, using Kahn's algorithm. +// https://en.wikipedia.org/wiki/Topological_sorting#Kahn's_algorithm +func (dag DAG) TopologicalSort() []string { + // G is the mutable graph we work on, which we remove edges from. + G := dag.Copy() + // L in pseudocode + sortedIDs := make([]int, 0, len(dag.nodeNameToId)) + topLevelNodes := dag.topologicalTopLevelNodes() + + // while len(topLevelNodes) != 0 + for { + if len(topLevelNodes) == 0 { + break + } + // pop a node `n`` off of topLevelNodes + n := topLevelNodes[0] + topLevelNodes = topLevelNodes[1:] + // add it to the sorted list + sortedIDs = append(sortedIDs, n) + nEdgeList := G.directedEdgeList[n] + + // normally we'd do map iteration, but because we need cross-machine determinism, + // we gather all the nodes M for which there is an edge n -> m, sort that list, + // and then iterate over it. + nodesM := make([]int, 0, len(nEdgeList)) + for m, direction := range nEdgeList { + if direction != 1 { + panic("dag: topological sort correctness error. " + + "Popped node n was expected to have no incoming edges") + } + nodesM = append(nodesM, m) + } + + sort.Ints(nodesM) + + for _, m := range nodesM { + // remove edge from n -> m + G.deleteEdge(n, m) + // if m has no incomingEdges, add to topLevelNodes + if !G.hasIncomingEdge(m) { + topLevelNodes = append(topLevelNodes, m) + } + } + } + + if G.hasEdges() { + fmt.Println(G) + panic("dag: invalid construction, attempted to topologically sort a tree that is not a dag. A cycle exists") + } + + return dag.idsToNames(sortedIDs) +} diff --git a/tests/interchaintest/osmosis/osmoutils/partialord/internal/dag/dag_test.go b/tests/interchaintest/osmosis/osmoutils/partialord/internal/dag/dag_test.go new file mode 100644 index 00000000..1e06461e --- /dev/null +++ b/tests/interchaintest/osmosis/osmoutils/partialord/internal/dag/dag_test.go @@ -0,0 +1,154 @@ +package dag_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/osmosis-labs/fee-abstraction/v7/tests/interchaintest/osmosis/osmoutils/partialord/internal/dag" +) + +type edge struct { + start, end string +} + +func TestTopologicalSort(t *testing.T) { + // Tests that topological sort works for various inputs. + // We hardcode the satisfying solution in the tests, even though it suffices + // to check that the partial ordering is sufficient. (and that's the only guarantee given externally) + // This is to ensure we catch differences in order between changes, and across machines. + simpleNodes := []string{"dog", "cat", "banana", "apple"} + simpleNodesRev := []string{"apple", "banana", "cat", "dog"} + tests := []struct { + nodes []string + edges []edge + expectedTopologicalOrder []string + }{ + { + // alphabetical ordering of simple nodes + nodes: simpleNodes, + edges: []edge{{"banana", "apple"}, {"cat", "banana"}, {"dog", "cat"}}, + expectedTopologicalOrder: simpleNodes, + }, + { + // apple > dog + nodes: simpleNodes, + edges: []edge{{"apple", "dog"}}, + expectedTopologicalOrder: []string{"cat", "banana", "apple", "dog"}, + }, + { + // apple > everything + nodes: simpleNodes, + edges: []edge{{"apple", "banana"}, {"apple", "cat"}, {"apple", "dog"}}, + expectedTopologicalOrder: []string{"apple", "dog", "cat", "banana"}, + }, + { + // apple > everything, on list with reversed initial order + nodes: simpleNodesRev, + edges: []edge{{"apple", "banana"}, {"apple", "cat"}, {"apple", "dog"}}, + expectedTopologicalOrder: []string{"apple", "banana", "cat", "dog"}, + }, + } + for _, tc := range tests { + dag := dag.NewDAG(tc.nodes) + for _, edge := range tc.edges { + err := dag.AddEdge(edge.start, edge.end) + require.NoError(t, err) + } + order := dag.TopologicalSort() + require.Equal(t, tc.expectedTopologicalOrder, order) + } +} + +func TestAddFirst(t *testing.T) { + simpleNodes := []string{"frog", "elephant", "dog", "cat", "banana", "apple"} + tests := []struct { + nodes []string + first []string + replaceEdges []edge + expectedTopologicalOrder []string + }{ + { + nodes: simpleNodes, + first: []string{"frog"}, + replaceEdges: []edge{{"banana", "apple"}, {"cat", "banana"}, {"dog", "cat"}}, + expectedTopologicalOrder: simpleNodes, + }, + { + nodes: simpleNodes, + first: []string{"elephant"}, + replaceEdges: []edge{{"banana", "apple"}, {"apple", "frog"}, {"dog", "cat"}}, + expectedTopologicalOrder: []string{"elephant", "dog", "banana", "cat", "apple", "frog"}, + }, + { + nodes: simpleNodes, + first: []string{"elephant", "frog"}, + replaceEdges: []edge{}, + expectedTopologicalOrder: []string{"elephant", "frog", "dog", "cat", "banana", "apple"}, + }, + { + // add three items in first, if implemented incorrectly could cause a cycle + nodes: simpleNodes, + first: []string{"dog", "elephant", "frog"}, + replaceEdges: []edge{}, + expectedTopologicalOrder: []string{"dog", "elephant", "frog", "cat", "banana", "apple"}, + }, + } + for _, tc := range tests { + dag := dag.NewDAG(tc.nodes) + dag.AddFirstElements(tc.first...) + for _, edge := range tc.replaceEdges { + err := dag.ReplaceEdge(edge.start, edge.end) + require.NoError(t, err) + } + order := dag.TopologicalSort() + require.Equal(t, tc.expectedTopologicalOrder, order) + } +} + +func TestAddLast(t *testing.T) { + simpleNodes := []string{"frog", "elephant", "dog", "cat", "banana", "apple"} + tests := []struct { + nodes []string + last []string + replaceEdges []edge + expectedTopologicalOrder []string + }{ + { + // causes no order change + nodes: simpleNodes, + last: []string{"apple"}, + replaceEdges: []edge{{"banana", "apple"}, {"cat", "banana"}, {"dog", "cat"}}, + expectedTopologicalOrder: simpleNodes, + }, + { + nodes: simpleNodes, + last: []string{"elephant"}, + replaceEdges: []edge{{"banana", "apple"}, {"apple", "frog"}, {"dog", "cat"}}, + expectedTopologicalOrder: []string{"dog", "banana", "cat", "apple", "frog", "elephant"}, + }, + { + nodes: simpleNodes, + last: []string{"elephant", "frog"}, + replaceEdges: []edge{}, + expectedTopologicalOrder: []string{"dog", "cat", "banana", "apple", "elephant", "frog"}, + }, + { + // add three items in last, if implemented incorrectly could cause a cycle + nodes: simpleNodes, + last: []string{"dog", "elephant", "frog"}, + replaceEdges: []edge{}, + expectedTopologicalOrder: []string{"cat", "banana", "apple", "dog", "elephant", "frog"}, + }, + } + for _, tc := range tests { + dag := dag.NewDAG(tc.nodes) + dag.AddLastElements(tc.last...) + for _, edge := range tc.replaceEdges { + err := dag.ReplaceEdge(edge.start, edge.end) + require.NoError(t, err) + } + order := dag.TopologicalSort() + require.Equal(t, tc.expectedTopologicalOrder, order) + } +} diff --git a/tests/interchaintest/osmosis/osmoutils/partialord/internal/dag/module.go b/tests/interchaintest/osmosis/osmoutils/partialord/internal/dag/module.go new file mode 100644 index 00000000..4b4bcd97 --- /dev/null +++ b/tests/interchaintest/osmosis/osmoutils/partialord/internal/dag/module.go @@ -0,0 +1,9 @@ +// Package dag implements a simple Directed Acyclical Graph (DAG) for deterministic topological sorts +// +// It should not be externally exposed, and is intended to be a very simple dag implementation +// utilizing adjacency lists to store edges. +// +// This package is intended to be used for small scales, where performance of the algorithms is not critical. +// (e.g. sub 10k entries) +// Thus none of the algorithms in here are benchmarked, and just have correctness checks. +package dag diff --git a/tests/interchaintest/osmosis/osmoutils/partialord/module.go b/tests/interchaintest/osmosis/osmoutils/partialord/module.go new file mode 100644 index 00000000..1d02c866 --- /dev/null +++ b/tests/interchaintest/osmosis/osmoutils/partialord/module.go @@ -0,0 +1,2 @@ +// package partialord allows one to define partial orderings, and derive a total ordering +package partialord diff --git a/tests/interchaintest/osmosis/osmoutils/partialord/partialord.go b/tests/interchaintest/osmosis/osmoutils/partialord/partialord.go new file mode 100644 index 00000000..93fd2bee --- /dev/null +++ b/tests/interchaintest/osmosis/osmoutils/partialord/partialord.go @@ -0,0 +1,97 @@ +package partialord + +import ( + "sort" + + "github.com/osmosis-labs/fee-abstraction/v7/tests/interchaintest/osmosis/osmoutils/partialord/internal/dag" +) + +type PartialOrdering struct { + // underlying dag, the partial ordering is stored via a dag + // https://en.wikipedia.org/wiki/Topological_sorting#Relation_to_partial_orders + dag dag.DAG + // bools for sealing, to prevent repeated invocation of first or last methods. + firstSealed bool + lastSealed bool +} + +// NewPartialOrdering creates a new partial ordering over the set of provided elements. +func NewPartialOrdering(elements []string) PartialOrdering { + elementsCopy := make([]string, len(elements)) + copy(elementsCopy, elements) + sort.Strings(elementsCopy) + return PartialOrdering{ + dag: dag.NewDAG(elementsCopy), + firstSealed: false, + lastSealed: false, + } +} + +func handleDAGErr(err error) { + // all dag errors are logic errors that the intended users of this package should not make. + if err != nil { + panic(err) + } +} + +// After marks that A should come after B +func (ord *PartialOrdering) After(A string, B string) { + // Set that A depends on B / an edge from B -> A + err := ord.dag.AddEdge(B, A) + handleDAGErr(err) +} + +// After marks that A should come before B +func (ord *PartialOrdering) Before(A string, B string) { + // Set that B depends on A / an edge from A -> B + err := ord.dag.AddEdge(A, B) + handleDAGErr(err) +} + +// Sets elems to be the first elements in the ordering. +// So if were making an ordering over {A, B, C, D, E}, and elems provided is {D, B, A} +// then we are guaranteed that the total ordering will begin with {D, B, A} +func (ord *PartialOrdering) FirstElements(elems ...string) { + if ord.firstSealed { + panic("FirstElements has already been called") + } + // We make every node in the dag have a dependency on elems[-1] + // then we change elems[-1] to depend on elems[-2], and so forth. + err := ord.dag.AddFirstElements(elems...) + handleDAGErr(err) + ord.firstSealed = true +} + +// Sets elems to be the last elements in the ordering. +// So if were making an ordering over {A, B, C, D, E}, and elems provided is {D, B, A} +// then we are guaranteed that the total ordering will end with {D, B, A} +func (ord *PartialOrdering) LastElements(elems ...string) { + if ord.lastSealed { + panic("FirstElements has already been called") + } + // We make every node in the dag have a dependency on elems[0] + // then we make elems[1] depend on elems[0], and so forth. + err := ord.dag.AddLastElements(elems...) + handleDAGErr(err) + ord.lastSealed = true +} + +// Sequence sets a sequence of ordering constraints. +// So if were making an ordering over {A, B, C, D, E}, and elems provided is {D, B, A} +// then we are guaranteed that the total ordering will have D comes before B comes before A. +// (They're may be elements interspersed, e.g. {D, C, E, B, A} is a valid ordering) +func (ord *PartialOrdering) Sequence(seq ...string) { + // We make every node in the sequence have a prior node + for i := 0; i < (len(seq) - 1); i++ { + err := ord.dag.AddEdge(seq[i], seq[i+1]) + handleDAGErr(err) + } +} + +// TotalOrdering returns a deterministically chosen total ordering that satisfies all specified +// partial ordering constraints. +// +// Panics if no total ordering exists. +func (ord *PartialOrdering) TotalOrdering() []string { + return ord.dag.TopologicalSort() +} diff --git a/tests/interchaintest/osmosis/osmoutils/partialord/partialord_test.go b/tests/interchaintest/osmosis/osmoutils/partialord/partialord_test.go new file mode 100644 index 00000000..984fc0aa --- /dev/null +++ b/tests/interchaintest/osmosis/osmoutils/partialord/partialord_test.go @@ -0,0 +1,74 @@ +package partialord_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/osmosis-labs/fee-abstraction/v7/tests/interchaintest/osmosis/osmoutils/partialord" +) + +func TestAPI(t *testing.T) { + // begin block use case, we have a dozen modules, but only care about a couple orders. + // In practice this will be gotten from some API, e.g. app.AllModuleNames() + moduleNames := []string{ + "auth", "authz", "bank", "capabilities", + "staking", "distribution", "epochs", "mint", "upgrades", "wasm", "ibc", + "ibctransfers", + } + beginBlockOrd := partialord.NewPartialOrdering(moduleNames) + beginBlockOrd.FirstElements("upgrades", "epochs", "capabilities") + beginBlockOrd.After("ibctransfers", "ibc") + beginBlockOrd.Before("mint", "distribution") + // This is purely just to test last functionality, doesn't make sense in context + beginBlockOrd.LastElements("auth", "authz", "wasm") + + totalOrd := beginBlockOrd.TotalOrdering() + expTotalOrd := []string{ + "upgrades", "epochs", "capabilities", + "bank", "ibc", "mint", "staking", "ibctransfers", "distribution", + "auth", "authz", "wasm", + } + require.Equal(t, expTotalOrd, totalOrd) +} + +func TestNonStandardAPIOrder(t *testing.T) { + // This test uses direct ordering before First, and after Last + names := []string{"A", "B", "C", "D", "E", "F", "G"} + ord := partialord.NewPartialOrdering(names) + ord.After("A", "C") + ord.After("A", "D") + ord.After("E", "B") + // overrides the "A" after "C" & "A" after "D" constraints + ord.FirstElements("A", "B", "C") + expOrdering := []string{"A", "B", "C", "D", "E", "F", "G"} + require.Equal(t, expOrdering, ord.TotalOrdering()) + + ord.After("E", "D") + expOrdering = []string{"A", "B", "C", "D", "F", "G", "E"} + require.Equal(t, expOrdering, ord.TotalOrdering()) + + ord.LastElements("G") + ord.After("F", "E") + expOrdering = []string{"A", "B", "C", "D", "E", "F", "G"} + require.Equal(t, expOrdering, ord.TotalOrdering()) +} + +// This test ad-hocly tests combination of multiple sequences, first elements, and an After +// invocation. +func TestSequence(t *testing.T) { + // This test uses direct ordering before First, and after Last + names := []string{"A", "B", "C", "D", "E", "F", "G"} + ord := partialord.NewPartialOrdering(names) + // Make B A C a sequence + ord.Sequence("B", "A", "C") + // Make A G E a sub-sequence + ord.Sequence("A", "G", "E") + // make first elements D B F + ord.FirstElements("D", "B", "F") + // make C come after E + ord.After("C", "G") + + expOrdering := []string{"D", "B", "F", "A", "G", "C", "E"} + require.Equal(t, expOrdering, ord.TotalOrdering()) +} diff --git a/tests/interchaintest/osmosis/osmoutils/slice_helper.go b/tests/interchaintest/osmosis/osmoutils/slice_helper.go new file mode 100644 index 00000000..6d70234a --- /dev/null +++ b/tests/interchaintest/osmosis/osmoutils/slice_helper.go @@ -0,0 +1,119 @@ +package osmoutils + +import ( + "math/rand" + "reflect" + "sort" + + "golang.org/x/exp/constraints" +) + +// SortSlice sorts a slice of type T elements that implement constraints.Ordered. +// Mutates input slice s +func SortSlice[T constraints.Ordered](s []T) { + sort.Slice(s, func(i, j int) bool { + return s[i] < s[j] + }) +} + +func Filter[T interface{}](filter func(T) bool, s []T) []T { + filteredSlice := []T{} + for _, s := range s { + if filter(s) { + filteredSlice = append(filteredSlice, s) + } + } + return filteredSlice +} + +// ReverseSlice reverses the input slice in place. +// Does mutate argument. +func ReverseSlice[T any](s []T) []T { + maxIndex := len(s) + for i := 0; i < maxIndex/2; i++ { + temp := s[i] + s[i] = s[maxIndex-i-1] + s[maxIndex-1-i] = temp + } + return s +} + +// ContainsDuplicate checks if there are any duplicate +// elements in the slice. +func ContainsDuplicate[T any](arr []T) bool { + visited := make(map[any]bool, 0) + for i := 0; i < len(arr); i++ { + if visited[arr[i]] { + return true + } else { + visited[arr[i]] = true + } + } + return false +} + +// ContainsDuplicateDeepEqual returns true if there are duplicates +// in the slice by performing deep comparison. This is useful +// for comparing matrices or slices of pointers. +// Returns false if there are no deep equal duplicates. +func ContainsDuplicateDeepEqual[T any](multihops []T) bool { + for i := 0; i < len(multihops)-1; i++ { + if reflect.DeepEqual(multihops[i], multihops[i+1]) { + return true + } + } + return false +} + +type LessFunc[T any] func(a, b T) bool + +// MergeSlices efficiently merges two sorted slices into a single sorted slice. +// The resulting slice contains all elements from slice1 and slice2, sorted according to the less function. +// The input slices must be sorted in ascending order according to the less function. +// The less function takes two elements of type T and returns a boolean value indicating whether the first element is less than the second element. +// The function returns a new slice containing all elements from slice1 and slice2, sorted according to the less function. +// The function does not modify the input slices. +func MergeSlices[T any](slice1, slice2 []T, less LessFunc[T]) []T { + result := make([]T, 0, len(slice1)+len(slice2)) + i, j := 0, 0 + + for i < len(slice1) && j < len(slice2) { + if less(slice1[i], slice2[j]) { + result = append(result, slice1[i]) + i++ + } else { + result = append(result, slice2[j]) + j++ + } + } + + // Append any remaining elements from slice1 and slice2 + result = append(result, slice1[i:]...) + result = append(result, slice2[j:]...) + + return result +} + +// Contains returns true if the slice contains the item, false otherwise. +func Contains[T comparable](slice []T, item T) bool { + for _, a := range slice { + if a == item { + return true + } + } + return false +} + +// GetRandomSubset returns a random subset of the given slice +func GetRandomSubset[T any](slice []T) []T { + if len(slice) == 0 { + return []T{} + } + + rand.Shuffle(len(slice), func(i, j int) { + slice[i], slice[j] = slice[j], slice[i] + }) + + n := rand.Intn(len(slice)) + return slice[:n] +} diff --git a/tests/interchaintest/osmosis/osmoutils/slice_helper_test.go b/tests/interchaintest/osmosis/osmoutils/slice_helper_test.go new file mode 100644 index 00000000..72084226 --- /dev/null +++ b/tests/interchaintest/osmosis/osmoutils/slice_helper_test.go @@ -0,0 +1,174 @@ +package osmoutils_test + +import ( + "math/rand" + "reflect" + "testing" + "time" + + "github.com/stretchr/testify/require" + + "github.com/osmosis-labs/fee-abstraction/v7/tests/interchaintest/osmosis/osmoutils" +) + +func TestReverseSlice(t *testing.T) { + tests := map[string]struct { + s []string + + expectedSolvedInput []string + }{ + "Even length array": {s: []string{"a", "b", "c", "d"}, expectedSolvedInput: []string{"d", "c", "b", "a"}}, + "Empty array": {s: []string{}, expectedSolvedInput: []string{}}, + "Odd length array": {s: []string{"a", "b", "c"}, expectedSolvedInput: []string{"c", "b", "a"}}, + "Single element array": {s: []string{"a"}, expectedSolvedInput: []string{"a"}}, + "Array with empty string": {s: []string{"a", "b", "c", "", "d"}, expectedSolvedInput: []string{"d", "", "c", "b", "a"}}, + "Array with numbers": {s: []string{"a", "b", "c", "1", "2", "3"}, expectedSolvedInput: []string{"3", "2", "1", "c", "b", "a"}}, + } + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + actualSolvedInput := osmoutils.ReverseSlice(tc.s) + require.Equal(t, tc.expectedSolvedInput, actualSolvedInput) + }) + } +} + +func TestMergeSlices(t *testing.T) { + lessInt := func(a, b int) bool { + return a < b + } + testCases := []struct { + name string + slice1 []int + slice2 []int + less func(a, b int) bool + want []int + }{ + { + name: "basic merge", + slice1: []int{1, 3, 5}, + slice2: []int{2, 4, 6}, + less: lessInt, + want: []int{1, 2, 3, 4, 5, 6}, + }, + { + name: "Empty slice1", + slice1: []int{}, + slice2: []int{2, 4, 6}, + less: lessInt, + want: []int{2, 4, 6}, + }, + { + name: "Empty slice2", + slice1: []int{1, 3, 5}, + slice2: []int{}, + less: lessInt, + want: []int{1, 3, 5}, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + got := osmoutils.MergeSlices(tc.slice1, tc.slice2, lessInt) + if !reflect.DeepEqual(got, tc.want) { + t.Errorf("got: %v, want: %v", got, tc.want) + } + }) + } +} + +func TestContainsDuplicateDeepEqual(t *testing.T) { + tests := []struct { + input []interface{} + want bool + }{ + {[]interface{}{[]int{1, 2, 3}, []int{4, 5, 6}}, false}, + {[]interface{}{[]int{1, 2, 3}, []int{1, 2, 3}}, true}, + {[]interface{}{[]string{"hello", "world"}, []string{"goodbye", "world"}}, false}, + {[]interface{}{[]string{"hello", "world"}, []string{"hello", "world"}}, true}, + {[]interface{}{[][]int{{1, 2}, {3, 4}}, [][]int{{1, 2}, {3, 4}}}, true}, + } + + for _, tt := range tests { + got := osmoutils.ContainsDuplicateDeepEqual(tt.input) + require.Equal(t, tt.want, got) + } +} + +func TestContains(t *testing.T) { + testCases := []struct { + name string + slice []int + item int + expect bool + }{ + { + name: "Contains - item is in the slice", + slice: []int{1, 2, 3, 4, 5}, + item: 3, + expect: true, + }, + { + name: "Contains - item is not in the slice", + slice: []int{1, 2, 3, 4, 5}, + item: 6, + expect: false, + }, + // add more test cases here... + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + got := osmoutils.Contains(tc.slice, tc.item) + if got != tc.expect { + t.Fatalf("Contains(%v, %v): expected %v, got %v", tc.slice, tc.item, tc.expect, got) + } + }) + } +} + +func TestGetRandomSubset(t *testing.T) { + tests := []struct { + name string + slice []int + }{ + { + name: "Empty slice", + slice: []int{}, + }, + { + name: "Slice of integers", + slice: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, + }, + } + + rand.Seed(time.Now().UnixNano()) + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := osmoutils.GetRandomSubset(tt.slice) + + // Check if the length of the returned subset is less than or equal to the length of the original slice + if len(got) > len(tt.slice) { + t.Errorf("GetRandomSubset() returned subset length %d, expected less than or equal to %d", len(got), len(tt.slice)) + } + + // Check if the returned subset contains only elements from the original slice + for _, v := range got { + if !contains(tt.slice, v) { + t.Errorf("GetRandomSubset() returned element %v not found in the original slice", v) + } + } + }) + } +} + +// contains checks if a slice contains a specific element +func contains(slice interface{}, element interface{}) bool { + s := reflect.ValueOf(slice) + for i := 0; i < s.Len(); i++ { + if reflect.DeepEqual(s.Index(i).Interface(), element) { + return true + } + } + return false +} diff --git a/tests/interchaintest/osmosis/osmoutils/store_helper.go b/tests/interchaintest/osmosis/osmoutils/store_helper.go new file mode 100644 index 00000000..c03f8008 --- /dev/null +++ b/tests/interchaintest/osmosis/osmoutils/store_helper.go @@ -0,0 +1,286 @@ +package osmoutils + +import ( + "bytes" + "errors" + "fmt" + + db "github.com/cometbft/cometbft-db" + "github.com/cosmos/cosmos-sdk/store/prefix" + sdk "github.com/cosmos/cosmos-sdk/types" + + storetypes "github.com/cosmos/cosmos-sdk/store/types" + + "github.com/osmosis-labs/fee-abstraction/v7/tests/interchaintest/osmosis/osmomath" + + "github.com/cosmos/cosmos-sdk/store" + "github.com/cosmos/gogoproto/proto" +) + +var ( + ErrNoValuesInRange = errors.New("No values in range") +) + +func GatherAllKeysFromStore(storeObj store.KVStore) []string { + iterator := storeObj.Iterator(nil, nil) + defer iterator.Close() + + keys := []string{} + for ; iterator.Valid(); iterator.Next() { + keys = append(keys, string(iterator.Key())) + } + return keys +} + +func GatherValuesFromStore[T any](storeObj store.KVStore, keyStart []byte, keyEnd []byte, parseValue func([]byte) (T, error)) ([]T, error) { + iterator := storeObj.Iterator(keyStart, keyEnd) + defer iterator.Close() + return gatherValuesFromIterator(iterator, parseValue, noStopFn) +} + +// GatherValuesFromStorePrefix is a decorator around GatherValuesFromStorePrefixWithKeyParser. It overwrites the parse function to +// disable parsing keys, only keeping values +func GatherValuesFromStorePrefix[T any](storeObj store.KVStore, prefix []byte, parseValue func([]byte) (T, error)) ([]T, error) { + // Replace a callback with the one that takes both key and value + // but ignores the key. + parseOnlyValue := func(_ []byte, value []byte) (T, error) { + return parseValue(value) + } + return GatherValuesFromStorePrefixWithKeyParser(storeObj, prefix, parseOnlyValue) +} + +// GatherValuesFromStorePrefixWithKeyParser is a helper function that gathers values from a given store prefix. While iterating through +// the entries, it parses both key and the value using the provided parse function to return the desired type. +// Returns error if: +// - the parse function returns an error. +// - internal database error +func GatherValuesFromStorePrefixWithKeyParser[T any](storeObj store.KVStore, prefix []byte, parse func(key []byte, value []byte) (T, error)) ([]T, error) { + iterator := sdk.KVStorePrefixIterator(storeObj, prefix) + defer iterator.Close() + return gatherValuesFromIteratorWithKeyParser(iterator, parse, noStopFn) +} + +func GetValuesUntilDerivedStop[T any](storeObj store.KVStore, keyStart []byte, stopFn func([]byte) bool, parseValue func([]byte) (T, error)) ([]T, error) { + // SDK iterator is broken for nil end time, and non-nil start time + // https://github.com/cosmos/cosmos-sdk/issues/12661 + // hence we use []byte{0xff} + keyEnd := []byte{0xff} + return GetIterValuesWithStop(storeObj, keyStart, keyEnd, false, stopFn, parseValue) +} + +func makeIterator(storeObj store.KVStore, keyStart []byte, keyEnd []byte, reverse bool) store.Iterator { + if reverse { + return storeObj.ReverseIterator(keyStart, keyEnd) + } + return storeObj.Iterator(keyStart, keyEnd) +} + +func GetIterValuesWithStop[T any]( + storeObj store.KVStore, + keyStart []byte, + keyEnd []byte, + reverse bool, + stopFn func([]byte) bool, + parseValue func([]byte) (T, error), +) ([]T, error) { + iter := makeIterator(storeObj, keyStart, keyEnd, reverse) + defer iter.Close() + + return gatherValuesFromIterator(iter, parseValue, stopFn) +} + +// HasAnyAtPrefix returns true if there is at least one value in the given prefix. +func HasAnyAtPrefix[T any](storeObj store.KVStore, prefix []byte, parseValue func([]byte) (T, error)) (bool, error) { + _, err := GetFirstValueInRange(storeObj, prefix, sdk.PrefixEndBytes(prefix), false, parseValue) + if err != nil { + if err == ErrNoValuesInRange { + return false, nil + } + return false, err + } + + return true, nil +} + +func GetFirstValueAfterPrefixInclusive[T any](storeObj store.KVStore, keyStart []byte, parseValue func([]byte) (T, error)) (T, error) { + // SDK iterator is broken for nil end time, and non-nil start time + // https://github.com/cosmos/cosmos-sdk/issues/12661 + // hence we use []byte{0xff} + return GetFirstValueInRange(storeObj, keyStart, []byte{0xff}, false, parseValue) +} + +func GetFirstValueInRange[T any](storeObj store.KVStore, keyStart []byte, keyEnd []byte, reverseIterate bool, parseValue func([]byte) (T, error)) (T, error) { + iterator := makeIterator(storeObj, keyStart, keyEnd, reverseIterate) + defer iterator.Close() + + if !iterator.Valid() { + var blankValue T + return blankValue, ErrNoValuesInRange + } + + return parseValue(iterator.Value()) +} + +func gatherValuesFromIterator[T any](iterator db.Iterator, parseValue func([]byte) (T, error), stopFn func([]byte) bool) ([]T, error) { + // Replace a callback with the one that takes both key and value + // but ignores the key. + parseKeyValue := func(_ []byte, value []byte) (T, error) { + return parseValue(value) + } + return gatherValuesFromIteratorWithKeyParser(iterator, parseKeyValue, stopFn) +} + +func gatherValuesFromIteratorWithKeyParser[T any](iterator db.Iterator, parse func(key []byte, value []byte) (T, error), stopFn func([]byte) bool) ([]T, error) { + values := []T{} + for ; iterator.Valid(); iterator.Next() { + if stopFn(iterator.Key()) { + break + } + val, err := parse(iterator.Key(), iterator.Value()) + if err != nil { + return nil, err + } + values = append(values, val) + } + return values, nil +} + +func noStopFn([]byte) bool { + return false +} + +// MustSet runs store.Set(key, proto.Marshal(value)) +// but panics on any error. +func MustSet(storeObj store.KVStore, key []byte, value proto.Message) { + bz, err := proto.Marshal(value) + if err != nil { + panic(err) + } + + storeObj.Set(key, bz) +} + +// MustGet gets key from store by mutating result +// Panics on any error. +func MustGet(store store.KVStore, key []byte, result proto.Message) { + b := store.Get(key) + if b == nil { + panic(fmt.Errorf("getting at key (%v) should not have been nil", key)) + } + if err := proto.Unmarshal(b, result); err != nil { + panic(err) + } +} + +// MustSetDec sets dec value to store at key. Panics on any error. +func MustSetDec(store store.KVStore, key []byte, value osmomath.Dec) { + MustSet(store, key, &sdk.DecProto{ + Dec: value, + }) +} + +// MustGetDec gets dec value from store at key. Panics on any error. +func MustGetDec(store store.KVStore, key []byte) osmomath.Dec { + result := &sdk.DecProto{} + MustGet(store, key, result) + return result.Dec +} + +// GetDec gets dec value from store at key. Returns error if: +// - database error occurs. +// - no value at given key is found. +func GetDec(store store.KVStore, key []byte) (osmomath.Dec, error) { + result := &sdk.DecProto{} + isFound, err := Get(store, key, result) + if err != nil { + return osmomath.Dec{}, err + } + if !isFound { + return osmomath.Dec{}, DecNotFoundError{Key: string(key)} + } + return result.Dec, nil +} + +// Get returns a value at key by mutating the result parameter. Returns true if the value was found and the +// result mutated correctly. If the value is not in the store, returns false. +// Returns error only when database or serialization errors occur. (And when an error occurs, returns false) +func Get(store store.KVStore, key []byte, result proto.Message) (found bool, err error) { + b := store.Get(key) + if b == nil { + return false, nil + } + if err := proto.Unmarshal(b, result); err != nil { + return true, err + } + return true, nil +} + +// DeleteAllKeysFromPrefix deletes all store records that contains the given prefixKey. +func DeleteAllKeysFromPrefix(store store.KVStore, prefixKey []byte) { + prefixStore := prefix.NewStore(store, prefixKey) + iter := prefixStore.Iterator(nil, nil) + defer iter.Close() + + for ; iter.Valid(); iter.Next() { + prefixStore.Delete(iter.Key()) + } +} + +// GetCoinArrayFromPrefix returns all coins from the store that has the given prefix. +func GetCoinArrayFromPrefix(ctx sdk.Context, storeKey storetypes.StoreKey, storePrefix []byte) []sdk.Coin { + coinArray := make([]sdk.Coin, 0) + + store := ctx.KVStore(storeKey) + iterator := sdk.KVStorePrefixIterator(store, storePrefix) + + defer iterator.Close() + for ; iterator.Valid(); iterator.Next() { + bz := iterator.Value() + sdkInt := osmomath.Int{} + if err := sdkInt.Unmarshal(bz); err == nil { + denom := bytes.TrimPrefix(iterator.Key(), storePrefix) + coinArray = append(coinArray, sdk.NewCoin(string(denom), sdkInt)) + } + } + + return coinArray +} + +// GetCoinByDenomFromPrefix returns the coin from the store that has the given prefix and denom. +// If the denom is not found, a zero coin is returned. +func GetCoinByDenomFromPrefix(ctx sdk.Context, storeKey storetypes.StoreKey, storePrefix []byte, denom string) (sdk.Coin, error) { + store := prefix.NewStore(ctx.KVStore(storeKey), storePrefix) + key := []byte(denom) + + bz := store.Get(key) + if len(bz) == 0 { + return sdk.NewCoin(denom, osmomath.ZeroInt()), nil + } + + sdkInt := osmomath.Int{} + if err := sdkInt.Unmarshal(bz); err != nil { + return sdk.NewCoin(denom, osmomath.ZeroInt()), err + } + + return sdk.NewCoin(denom, sdkInt), nil +} + +// IncreaseCoinByDenomFromPrefix increases the coin from the store that has the given prefix and denom by the specified amount. +func IncreaseCoinByDenomFromPrefix(ctx sdk.Context, storeKey storetypes.StoreKey, storePrefix []byte, denom string, increasedAmt osmomath.Int) error { + store := prefix.NewStore(ctx.KVStore(storeKey), storePrefix) + key := []byte(denom) + + coin, err := GetCoinByDenomFromPrefix(ctx, storeKey, storePrefix, denom) + if err != nil { + return err + } + + coin.Amount = coin.Amount.Add(increasedAmt) + bz, err := coin.Amount.Marshal() + if err != nil { + return err + } + + store.Set(key, bz) + return nil +} diff --git a/tests/interchaintest/osmosis/osmoutils/sumtree/README.md b/tests/interchaintest/osmosis/osmoutils/sumtree/README.md new file mode 100644 index 00000000..1d8d2d54 --- /dev/null +++ b/tests/interchaintest/osmosis/osmoutils/sumtree/README.md @@ -0,0 +1,151 @@ +# Prefix-Sum B-Tree specification + +This module implements a B-Tree suitable for efficiently computing a +random prefix sum of data, while allowing the data to be efficiently +updated. + +The prefix sums for N elements x\_1, x\_2, ... x\_N, each with a weight +field, is the sequence y\_1, y\_2, ... y\_N, where +`y_i = sum_{0 <= j <= i} x_j.weight`. This data structure allows one to +edit, insert, and delete entries in the x sequence efficiently, and +efficiently retrieve the prefix sum at any index, where efficiently is +`O(log(N))` state operations. (Note that in the cosmos SDK stack, a +state operation is itself liable to take `O(log(N))` time) + +This is built for the use-case of we have a series of data that is +sorted by time. Each given time has an associated weight field. We want +to be able to very quickly find the total weight for all leaves with +times less than or equal to `t`. The actual implementation is agnostic +to what is the field we sort by. + +## Data structure idea + +The idea underlying this can be decomposed into two parts: + +1. Do some extra (`O(log(N))`) work when modifying the data, to allow + efficiently computing any prefix sum +2. Allow the data entries to be inserted into and deleted from, while + remaining sorted + +The solution for 1. is to build a balanced tree on top of the data. +Every inner node in the tree will be augmented with an "accumulated +weight" field, which contains the sum of the weights of all entries +below it. Notice that upon updating the weight of any leaf, the weights +of all nodes that are "above" this node can be updated efficiently. (As +there ought to only be log(N) such nodes) Furthermore, the root of the +tree's augmented value is the sum of all weights in the tree. + +Then to query the jth prefix sum, you first identify the path to the jth +node in the tree. You keep a running tally of "prefix sum thus far", +which is initialized to the sum of all weights in the tree. Then as you +walk the path from the tree root to the jth leaf, whenever there are +siblings on the right, you subtract their weight from your running +total. The weight when you arrive at the leaf is then the prefix sum. + +Lets illustrate this with a binary tree. + +![binary +tree](https://user-images.githubusercontent.com/6440154/116960474-142bf980-ac66-11eb-9a07-af84ab6d0bfa.png) + +If we want the prefix sum for leaf `12` we compute it as +`1.weight - 7.weight - 13.weight`. (We took a subtraction every time we +took a left) + +Now notice that this solution works for any tree type, where efficiency +just depends on `number of siblings * depth` and as long as that is +parameterized to be `O(log(N))` we maintain our definition of efficient. + +Thus we immediately get a solution to 2., by using a tree that supports +efficient inserts, and deletions, while still maintaining log depth and +a constant number of siblings. We opt for using a B+ tree for this, as +it performs the task well and does not require rebalance operations. +(Which can be costly in an adversarial environment) + +```{=html} + +``` + +## Implementation Details + +The B-Tree implementation under `osmoutils/sumtree` is designed specifically +for allowing efficient computation of a random prefix sum, with the +underlying data being updatable as explained above. + +Every Leaf has a `Weight` field, and the address its stored at in state +is the key which we want to sort by. The implementation sorts leaves as +byteslices, Leafs are sorted under their byteslice key, and the branch +nodes have accumulation for each child. + +A node is pointed by a `node` struct, used internally. + +``` {.go} +type node struct { + t Tree + level uint8 + key []byte +} +``` + +A `node` struct is a pointer to the key-value pair under +`nodeKey(node.level, node.key)` + +A leaf node is simply a `uint64` integer value stored under +`nodeKey(0, key)`. The key is arbitrary length of byte slice. + +``` {.go} +type Leaf struct { + Value uint64 +} +``` + +A branch node consists of keys and accumulation of the children nodes. + +``` {.go} +type Branch struct { + Children []Child +} + +type Child struct { + Key []byte + Acc uint64 +} +``` + +The following constraints are valid for all branch nodes: + +1. For `c` in `node.Branch().Children`, the node corresponding to `c` + is stored under `nodeKey(node.level-1, c.Key)`. +2. For `c` in `node.Branch().Children`, `c.Acc` is the sum of all + `c'.Acc` where `c'` is in `c.Children`. If `c'` is leaf node, + substitute `c'.Acc` to `Leaf.Value`. +3. For `c` in `node.Branch().Children`, `c.Key` is equal or greater + than `node.key` and lesser than `node.rightSibling().key`. +4. There are no duplicate child stored in more than one of node's + `.Children`. + +### Example + +Here is an example tree data: + + - Level 2 nil + - Level 1 0xaaaa + - Level 0 0xaaaa Value 10 + - Level 0 0xaaaa01 Value 20 + - Level 0 0xaabb Value 30 + - Level 1 0xbb44 + - Level 0 0xbb55 Value 100 + - Level 0 0xbe Value 200 + - Level 1 0xeeaaaa + - Level 0 0xef1234 Value 300 + - Level 0 0xffff Value 400 + +The branch nodes will have the following children: + +``` {.go} +require.Equal(sumtree.Get(nodeKey(2, nil)), Children{{0xaaaa, 60}, {0xbb44, 300}, {0xeeaaaa, 700}}) +require.Equal(sumtree.Get(nodeKey(1, 0xaaaa)), Children{{0xaaaa, 10}, {0xaaaa01, 20}, {0xaabb, 30}}) +require.Equal(sumtree.Get(nodeKey(1, 0xbb44)), Children{{0xbb55, 100}, {0xbe, 200}}) +require.Equal(sumtree.Get(nodeKey(1, 0xeeaaaa)), Children{{0xef1234, 300}, {0xffff, 400}}) +``` diff --git a/tests/interchaintest/osmosis/osmoutils/sumtree/constants.go b/tests/interchaintest/osmosis/osmoutils/sumtree/constants.go new file mode 100644 index 00000000..ea5b72af --- /dev/null +++ b/tests/interchaintest/osmosis/osmoutils/sumtree/constants.go @@ -0,0 +1,13 @@ +package sumtree + +var nodeKeyPrefix []byte + +const nodeKeyPrefixLen = 5 + +func init() { + // nodeKeyPrefix is assumed to be 5 bytes + nodeKeyPrefix = []byte("node/") + if len(nodeKeyPrefix) != nodeKeyPrefixLen { + panic("Invalid constants in accumulation store") + } +} diff --git a/tests/interchaintest/osmosis/osmoutils/sumtree/legacy/v101/old_tree.json b/tests/interchaintest/osmosis/osmoutils/sumtree/legacy/v101/old_tree.json new file mode 100644 index 00000000..b98e97c8 --- /dev/null +++ b/tests/interchaintest/osmosis/osmoutils/sumtree/legacy/v101/old_tree.json @@ -0,0 +1,12 @@ +[ + ["bm9kZS8AAA==","Ijg0Ig=="], + ["bm9kZS8AABY/Xw+aYtHixkmBhVrYaB0NhtE=","IjU2NiI="], + ["bm9kZS8AAE8=","IjYyOSI="], + ["bm9kZS8AAHWSHmZo0tY=","IjY1MCI="], + ["bm9kZS8AAIPxX7loC058i3Y6Gx1J","Ijc5MCI="], + ["bm9kZS8AAJwWDwcC9QWY","IjE4NiI="], + ["bm9kZS8AAMUvUFTb2Wiw9xc=","IjM0NCI="], + ["bm9kZS8AANSVXIQhEQ==","Ijk1NiI="], + ["bm9kZS8AAOk2eVG6ov9s1HHE","IjMyMyI="], + ["bm9kZS8AAQ==","W3siSW5kZXgiOm51bGwsIkFjYyI6Ijg0In0seyJJbmRleCI6IkZqOWZENXBpMGVMR1NZR0ZXdGhvSFEyRzBRPT0iLCJBY2MiOiI1NjYifSx7IkluZGV4IjoiVHc9PSIsIkFjYyI6IjYyOSJ9LHsiSW5kZXgiOiJkWkllWm1qUzFnPT0iLCJBY2MiOiI2NTAifSx7IkluZGV4IjoiZy9GZnVXZ0xUbnlMZGpvYkhVaz0iLCJBY2MiOiI3OTAifSx7IkluZGV4IjoibkJZUEJ3TDFCWmc9IiwiQWNjIjoiMTg2In0seyJJbmRleCI6InhTOVFWTnZaYUxEM0Z3PT0iLCJBY2MiOiIzNDQifSx7IkluZGV4IjoiMUpWY2hDRVIiLCJBY2MiOiI5NTYifSx7IkluZGV4IjoiNlRaNVVicWkvMnpVY2NRPSIsIkFjYyI6IjMyMyJ9XQ=="] +] diff --git a/tests/interchaintest/osmosis/osmoutils/sumtree/legacy/v101/tree.go b/tests/interchaintest/osmosis/osmoutils/sumtree/legacy/v101/tree.go new file mode 100644 index 00000000..871e96f5 --- /dev/null +++ b/tests/interchaintest/osmosis/osmoutils/sumtree/legacy/v101/tree.go @@ -0,0 +1,102 @@ +package v101 + +import ( + "encoding/binary" + "encoding/json" + "fmt" + + "github.com/cosmos/gogoproto/proto" + + stypes "github.com/cosmos/cosmos-sdk/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/osmosis-labs/fee-abstraction/v7/tests/interchaintest/osmosis/osmomath" + "github.com/osmosis-labs/fee-abstraction/v7/tests/interchaintest/osmosis/osmoutils/sumtree" +) + +type Child struct { + Index []byte + Acc osmomath.Int +} + +type Children []Child // branch nodes + +func migrateBranchValue(oldValueBz []byte) *sumtree.Node { + var oldValue Children + fmt.Println(string(oldValueBz)) + err := json.Unmarshal(oldValueBz, &oldValue) + if err != nil { + panic(err) + } + cs := make([]*sumtree.Child, len(oldValue)) + for i, oldChild := range oldValue { + cs[i] = &sumtree.Child{Index: oldChild.Index, Accumulation: oldChild.Acc} + } + return &sumtree.Node{Children: cs} +} + +func migrateLeafValue(index []byte, oldValueBz []byte) *sumtree.Leaf { + oldValue := osmomath.ZeroInt() + err := json.Unmarshal(oldValueBz, &oldValue) + if err != nil { + panic(err) + } + return sumtree.NewLeaf(index, oldValue) +} + +func nodeKey(level uint16, key []byte) []byte { + bz := make([]byte, 2) + binary.BigEndian.PutUint16(bz, level) + return append(append([]byte("node/"), bz...), key...) +} + +func leafKey(key []byte) []byte { + return nodeKey(0, key) +} + +func migrateTreeNode(store sdk.KVStore, level uint16, key []byte) { + if level == 0 { + migrateTreeLeaf(store, key) + } else { + migrateTreeBranch(store, level, key) + } +} + +func migrateTreeBranch(store sdk.KVStore, level uint16, key []byte) { + keyBz := nodeKey(level, key) + oldValueBz := store.Get(keyBz) + fmt.Println("migrate", keyBz, string(oldValueBz), level) + newValue := migrateBranchValue(oldValueBz) + newValueBz, err := proto.Marshal(newValue) + if err != nil { + panic(err) + } + store.Set(keyBz, newValueBz) + + for _, child := range newValue.Children { + migrateTreeNode(store, level-1, child.Index) + } +} + +func migrateTreeLeaf(store sdk.KVStore, key []byte) { + keyBz := leafKey(key) + oldValueBz := store.Get(keyBz) + newValue := migrateLeafValue(key, oldValueBz) + newValueBz, err := proto.Marshal(newValue) + if err != nil { + panic(err) + } + store.Set(keyBz, newValueBz) +} + +func MigrateTree(store sdk.KVStore) { + iter := stypes.KVStoreReversePrefixIterator(store, []byte("node/")) + defer iter.Close() + if !iter.Valid() { + return + } + keybz := iter.Key()[5:] + level := binary.BigEndian.Uint16(keybz[:2]) + key := keybz[2:] + migrateTreeNode(store, level, key) +} diff --git a/tests/interchaintest/osmosis/osmoutils/sumtree/legacy/v101/tree_test.go b/tests/interchaintest/osmosis/osmoutils/sumtree/legacy/v101/tree_test.go new file mode 100644 index 00000000..e0ad8627 --- /dev/null +++ b/tests/interchaintest/osmosis/osmoutils/sumtree/legacy/v101/tree_test.go @@ -0,0 +1,148 @@ +package v101_test + +import ( + "bytes" + "encoding/json" + "fmt" + "os" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/gogoproto/proto" + + "github.com/cosmos/iavl" + + dbm "github.com/cometbft/cometbft-db" + + iavlstore "github.com/cosmos/cosmos-sdk/store/iavl" + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/osmosis-labs/fee-abstraction/v7/tests/interchaintest/osmosis/osmomath" + "github.com/osmosis-labs/fee-abstraction/v7/tests/interchaintest/osmosis/osmoutils/sumtree" + v101 "github.com/osmosis-labs/fee-abstraction/v7/tests/interchaintest/osmosis/osmoutils/sumtree/legacy/v101" +) + +func setupStore() sdk.KVStore { + db := dbm.NewMemDB() + tree, _ := iavl.NewMutableTree(db, 100, false) + _, _, err := tree.SaveVersion() + if err != nil { + panic(err) + } + kvstore := iavlstore.UnsafeNewStore(tree) + return kvstore +} + +func compareBranch(oldValueBz []byte, valueBz []byte) (err error) { + oldValue := v101.Children{} + value := sumtree.Node{} + err = json.Unmarshal(oldValueBz, &oldValue) + if err != nil { + return + } + err = proto.Unmarshal(valueBz, &value) + if err != nil { + return + } + + for i, c := range oldValue { + c2 := value.Children[i] + if !bytes.Equal(c.Index, c2.Index) || !c.Acc.Equal(c2.Accumulation) { + err = fmt.Errorf("branch value mismatch: %+v / %+v", oldValue, value) + return + } + } + return +} + +func compareLeaf(oldValueBz []byte, valueBz []byte) (err error) { + oldValue := osmomath.ZeroInt() + value := sumtree.Leaf{} + err = json.Unmarshal(oldValueBz, &oldValue) + if err != nil { + return + } + err = proto.Unmarshal(valueBz, &value) + if err != nil { + return + } + + if !oldValue.Equal(value.Leaf.Accumulation) { + return fmt.Errorf("leaf value mismatch: %+v / %+v", oldValue, value) + } + return +} + +func comparePair(oldKeyBz, oldValueBz, keyBz, valueBz []byte) (err error) { + if !bytes.Equal(oldKeyBz, keyBz) { + err = fmt.Errorf("key bytes mismatch: %x / %x", oldKeyBz, keyBz) + } + + // TODO: properly select error + err = compareBranch(oldValueBz, valueBz) + if err == nil { + return nil + } + err = compareLeaf(oldValueBz, valueBz) + return err +} + +type kvPair struct { + key []byte + value []byte +} + +func pair(iter sdk.Iterator) kvPair { + res := kvPair{iter.Key(), iter.Value()} + iter.Next() + return res +} + +func extract(store sdk.KVStore) (res []kvPair) { + res = []kvPair{} + iter := store.Iterator(nil, nil) + defer iter.Close() + for iter.Valid() { + res = append(res, pair(iter)) + } + return +} + +func readold() []kvPair { + bz, err := os.ReadFile("./old_tree.json") + if err != nil { + panic(err) + } + var data [][][]byte + err = json.Unmarshal(bz, &data) + if err != nil { + panic(err) + } + res := make([]kvPair, len(data)) + for i, pair := range data { + res[i] = kvPair{pair[0], pair[1]} + } + return res +} + +func TestMigrate(t *testing.T) { + store := setupStore() + + oldpairs := readold() + for _, pair := range oldpairs { + fmt.Println("set", pair.key, pair.value) + store.Set(pair.key, pair.value) + } + + v101.MigrateTree(store) + + newpairs := extract(store) + + for i, oldpair := range oldpairs { + fmt.Println(i) + newpair := newpairs[i] + err := comparePair(oldpair.key, oldpair.value, newpair.key, newpair.value) + require.NoError(t, err) + } +} diff --git a/tests/interchaintest/osmosis/osmoutils/sumtree/node.go b/tests/interchaintest/osmosis/osmoutils/sumtree/node.go new file mode 100644 index 00000000..346460f3 --- /dev/null +++ b/tests/interchaintest/osmosis/osmoutils/sumtree/node.go @@ -0,0 +1,261 @@ +package sumtree + +import ( + "bytes" + + "github.com/cosmos/gogoproto/proto" + + "github.com/osmosis-labs/fee-abstraction/v7/tests/interchaintest/osmosis/osmomath" +) + +func NewLeaf(key []byte, acc osmomath.Int) *Leaf { + return &Leaf{Leaf: &Child{ + Index: key, + Accumulation: acc, + }} +} + +func (ptr *ptr) isLeaf() bool { + return ptr.level == 0 +} + +func (ptr *ptr) node() (res *Node) { + res = new(Node) + bz := ptr.tree.store.Get(ptr.tree.nodeKey(ptr.level, ptr.key)) + if bz != nil { + if err := proto.Unmarshal(bz, res); err != nil { + panic(err) + } + } + return +} + +func (ptr *ptr) set(node *Node) { + bz, err := proto.Marshal(node) + if err != nil { + panic(err) + } + ptr.tree.store.Set(ptr.tree.nodeKey(ptr.level, ptr.key), bz) +} + +func (ptr *ptr) setLeaf(leaf *Leaf) { + if !ptr.isLeaf() { + panic("setLeaf should only be called on pointers to leaf nodes. This ptr is a branch") + } + bz, err := proto.Marshal(leaf) + if err != nil { + panic(err) + } + ptr.tree.store.Set(ptr.tree.leafKey(ptr.key), bz) +} + +func (ptr *ptr) delete() { + ptr.tree.store.Delete(ptr.tree.nodeKey(ptr.level, ptr.key)) +} + +func (ptr *ptr) leftSibling() *ptr { + iter := ptr.tree.ptrReverseIterator(ptr.level, nil, ptr.key) + defer iter.Close() + return iter.ptr() +} + +func (ptr *ptr) rightSibling() *ptr { + iter := ptr.tree.ptrIterator(ptr.level, ptr.key, nil) + defer iter.Close() + if !iter.Valid() { + return nil + } + if ptr.exists() { + // exclude ptr itself + iter.Next() + } + return iter.ptr() +} + +func (ptr *ptr) child(n uint16) *ptr { + // TODO: set end to prefix iterator end + iter := ptr.tree.ptrIterator(ptr.level-1, ptr.node().Children[n].Index, nil) + defer iter.Close() + return iter.ptr() +} + +// parent returns the parent of the provided pointer. +// Behavior is not well defined if the calling pointer does not exist in the tree. +func (ptr *ptr) parent() *ptr { + // See if there is a parent with the same 'key' as this ptr. + parent := ptr.tree.ptrGet(ptr.level+1, ptr.key) + if parent.exists() { + return parent + } + // If not, take the node in the above layer that is lexicographically the closest + // from the left of the key. + parent = parent.leftSibling() + if parent.exists() { + return parent + } + // If there is no such ptr (the parent is not in the tree), return nil + return ptr.tree.ptrGet(ptr.level+1, nil) +} + +// exists returns true if the calling pointer has a node in the tree. +func (ptr *ptr) exists() bool { + if ptr == nil { + return false + } + return ptr.tree.store.Has(ptr.tree.nodeKey(ptr.level, ptr.key)) +} + +// updateAccumulation changes the accumulation value of a ptr in the tree, +// and handles updating the accumulation for all of its parent's augmented data. +func (ptr *ptr) updateAccumulation(c *Child) { + if !ptr.exists() { + return // reached at the root + } + + node := ptr.node() + idx, match := node.find(c.Index) + if !match { + panic("non existing key pushed from the child") + } + node = node.setAcc(idx, c.Accumulation) + ptr.set(node) + ptr.parent().updateAccumulation(&Child{ptr.key, node.accumulate()}) +} + +func (ptr *ptr) push(c *Child) { + if !ptr.exists() { + ptr.create(NewNode(c)) + return + } + + cs := ptr.node() + idx, match := cs.find(c.Index) + + // setting already existing child, move to updateAccumulation + if match { + ptr.updateAccumulation(c) + return + } + + // inserting new child ptr + cs = cs.insert(idx, c) + parent := ptr.parent() + + // split and push-up if overflow + if len(cs.Children) > int(ptr.tree.m) { + split := ptr.tree.m/2 + 1 + leftnode, rightnode := cs.split(int(split)) + ptr.tree.ptrGet(ptr.level, cs.Children[split].Index).create(rightnode) + if !parent.exists() { + parent.create(NewNode( + &Child{ptr.key, leftnode.accumulate()}, + &Child{cs.Children[split].Index, rightnode.accumulate()}, + )) + ptr.set(leftnode) + return + } + // constructing right child + parent.push(&Child{cs.Children[split].Index, rightnode.accumulate()}) + cs = leftnode + parent = ptr.parent() // parent might be changed during the pushing process + } + + parent.updateAccumulation(&Child{ptr.key, cs.accumulate()}) + ptr.set(cs) +} + +func (ptr *ptr) pull(key []byte) { + if !ptr.exists() { + return // reached at the root + } + node := ptr.node() + idx, match := node.find(key) + + if !match { + panic("pulling non existing child") + } + + node = node.delete(idx) + // For sake of efficiently on our use case, we pull only when a ptr gets + // empty. + // if len(data.Index) >= int(ptr.tree.m/2) { + if len(node.Children) > 0 { + ptr.set(node) + ptr.parent().updateAccumulation(&Child{ptr.key, node.accumulate()}) + return + } + + // merge if possible + left := ptr.leftSibling() + right := ptr.rightSibling() + parent := ptr.parent() + ptr.delete() + parent.pull(ptr.key) + + if left.exists() && right.exists() { + // parent might be deleted, retrieve from left + parent = left.parent() + if bytes.Equal(parent.key, right.parent().key) { + leftnode := left.node() + rightnode := right.node() + if len(leftnode.Children)+len(rightnode.Children) < int(ptr.tree.m) { + left.set(leftnode.merge(rightnode)) + right.delete() + parent.pull(right.key) + parent.updateAccumulation(&Child{left.key, leftnode.accumulate()}) + } + } + } +} + +func (node Node) accumulate() (res osmomath.Int) { + res = osmomath.ZeroInt() + for _, child := range node.Children { + res = res.Add(child.Accumulation) + } + return +} + +func NewNode(cs ...*Child) *Node { + return &Node{Children: cs} +} + +// find returns the appropriate position that key should be inserted +// if match is true, idx is the exact position for the key +// if match is false, idx is the position where the key should be inserted. +func (node Node) find(key []byte) (idx int, match bool) { + for idx, child := range node.Children { + if bytes.Equal(child.Index, key) { + return idx, true + } + // Push new key to the appropriate position + if bytes.Compare(child.Index, key) > 0 { + return idx, false + } + } + + return len(node.Children), false +} + +func (node *Node) setAcc(idx int, acc osmomath.Int) *Node { + node.Children[idx] = &Child{node.Children[idx].Index, acc} + return node +} + +func (node *Node) insert(idx int, c *Child) *Node { + arr := append(node.Children[:idx], append([]*Child{c}, node.Children[idx:]...)...) + return NewNode(arr...) +} + +func (node *Node) delete(idx int) *Node { + node = NewNode(append(node.Children[:idx], node.Children[idx+1:]...)...) + return node +} + +func (node *Node) split(idx int) (*Node, *Node) { + return NewNode(node.Children[:idx]...), NewNode(node.Children[idx:]...) +} + +func (node *Node) merge(node2 *Node) *Node { + return NewNode(append(node.Children, node2.Children...)...) +} diff --git a/tests/interchaintest/osmosis/osmoutils/sumtree/tree.go b/tests/interchaintest/osmosis/osmoutils/sumtree/tree.go new file mode 100644 index 00000000..a2ba6642 --- /dev/null +++ b/tests/interchaintest/osmosis/osmoutils/sumtree/tree.go @@ -0,0 +1,296 @@ +/// B+ tree implementation on KVStore + +package sumtree + +import ( + "bytes" + "encoding/binary" + "fmt" + + "github.com/cosmos/gogoproto/proto" + + "github.com/osmosis-labs/fee-abstraction/v7/tests/interchaintest/osmosis/osmomath" + + store "github.com/cosmos/cosmos-sdk/store" + stypes "github.com/cosmos/cosmos-sdk/store/types" +) + +// Tree is an augmented B+ tree implementation. +// Branches have m sized key index slice. Each key index represents +// the starting index of the child node's index(inclusive), and the +// ending index of the previous node of the child node's index(exclusive). +// TODO: We should abstract out the leaves of this tree to allow more data aside from +// the accumulation value to go there. +type Tree struct { + store store.KVStore + m uint8 +} + +func NewTree(store store.KVStore, m uint8) Tree { + tree := Tree{store, m} + if tree.IsEmpty() { + tree.Set(nil, osmomath.ZeroInt()) + } + return tree +} + +func (t Tree) IsEmpty() bool { + return !t.store.Has(t.leafKey(nil)) +} + +func (t Tree) Set(key []byte, acc osmomath.Int) { + ptr := t.ptrGet(0, key) + leaf := NewLeaf(key, acc) + ptr.setLeaf(leaf) + + ptr.parent().push(leaf.Leaf) +} + +func (t Tree) Remove(key []byte) { + node := t.ptrGet(0, key) + if !node.exists() { + return + } + parent := node.parent() + node.delete() + parent.pull(key) +} + +func (t Tree) Increase(key []byte, amt osmomath.Int) { + value := t.Get(key) + t.Set(key, value.Add(amt)) +} + +func (t Tree) Decrease(key []byte, amt osmomath.Int) { + t.Increase(key, amt.Neg()) +} + +func (t Tree) Clear() { + iter := t.store.Iterator(nil, nil) + defer iter.Close() + for ; iter.Valid(); iter.Next() { + t.store.Delete(iter.Key()) + } +} + +// ptr is pointer to a specific node inside the tree. +type ptr struct { + tree Tree + level uint16 + key []byte + // XXX: cache stored value? +} + +// ptrIterator iterates over ptrs in a given level. It only iterates directly over the pointers +// to the nodes, not the actual nodes themselves, to save loading additional data into memory. +type ptrIterator struct { + tree Tree + level uint16 + store.Iterator +} + +func (iter ptrIterator) ptr() *ptr { + if !iter.Valid() { + return nil + } + res := ptr{ + tree: iter.tree, + level: iter.level, + key: iter.Key()[7:], + } + // ptrIterator becomes invalid once retrieve ptr + err := iter.Close() + if err != nil { + panic(err) + } + return &res +} + +// nodeKey takes in a nodes layer, and its key, and constructs the +// its key in the underlying datastore. +func (t Tree) nodeKey(level uint16, key []byte) []byte { + // node key prefix is of len 7 + bz := make([]byte, nodeKeyPrefixLen+2+len(key)) + copy(bz, nodeKeyPrefix) + binary.BigEndian.PutUint16(bz[5:], level) + copy(bz[nodeKeyPrefixLen+2:], key) + return bz +} + +// leafKey constructs a key for a node pointer representing a leaf node. +func (t Tree) leafKey(key []byte) []byte { + return t.nodeKey(0, key) +} + +func (t Tree) root() *ptr { + iter := stypes.KVStoreReversePrefixIterator(t.store, nodeKeyPrefix) + defer iter.Close() + if !iter.Valid() { + return nil + } + key := iter.Key()[5:] + return &ptr{ + tree: t, + level: binary.BigEndian.Uint16(key[:2]), + key: key[2:], + } +} + +// Get returns the (osmomath.Int) accumulation value at a given leaf. +func (t Tree) Get(key []byte) osmomath.Int { + res := new(Leaf) + keybz := t.leafKey(key) + if !t.store.Has(keybz) { + return osmomath.ZeroInt() + } + bz := t.store.Get(keybz) + err := proto.Unmarshal(bz, res) + if err != nil { + panic(err) + } + return res.Leaf.Accumulation +} + +func (ptr *ptr) create(node *Node) { + keybz := ptr.tree.nodeKey(ptr.level, ptr.key) + bz, err := proto.Marshal(node) + if err != nil { + panic(err) + } + ptr.tree.store.Set(keybz, bz) +} + +func (t Tree) ptrGet(level uint16, key []byte) *ptr { + return &ptr{ + tree: t, + level: level, + key: key, + } +} + +func (t Tree) ptrIterator(level uint16, begin, end []byte) ptrIterator { + var endBytes []byte + if end != nil { + endBytes = t.nodeKey(level, end) + } else { + endBytes = stypes.PrefixEndBytes(t.nodeKey(level, nil)) + } + return ptrIterator{ + tree: t, + level: level, + Iterator: t.store.Iterator(t.nodeKey(level, begin), endBytes), + } +} + +func (t Tree) ptrReverseIterator(level uint16, begin, end []byte) ptrIterator { + var endBytes []byte + if end != nil { + endBytes = t.nodeKey(level, end) + } else { + endBytes = stypes.PrefixEndBytes(t.nodeKey(level, nil)) + } + return ptrIterator{ + tree: t, + level: level, + Iterator: t.store.ReverseIterator(t.nodeKey(level, begin), endBytes), + } +} + +func (t Tree) Iterator(begin, end []byte) store.Iterator { + return t.ptrIterator(0, begin, end) +} + +func (t Tree) ReverseIterator(begin, end []byte) store.Iterator { + return t.ptrReverseIterator(0, begin, end) +} + +// accumulationSplit returns the accumulated value for all of the following: +// left: all leaves under nodePointer with key < provided key +// exact: leaf with key = provided key +// right: all leaves under nodePointer with key > provided key +// Note that the equalities here are _exclusive_. +func (ptr *ptr) accumulationSplit(key []byte) (left osmomath.Int, exact osmomath.Int, right osmomath.Int) { + left, exact, right = osmomath.ZeroInt(), osmomath.ZeroInt(), osmomath.ZeroInt() + if ptr.isLeaf() { + var leaf Leaf + bz := ptr.tree.store.Get(ptr.tree.leafKey(ptr.key)) + err := proto.Unmarshal(bz, &leaf) + if err != nil { + panic(err) + } + // Check if the leaf key is to the left of the input key, + // if so this value is on the left. Similar for the other cases. + // Recall that all of the output arguments default to 0, if unset internally. + switch bytes.Compare(ptr.key, key) { + case -1: + left = leaf.Leaf.Accumulation + case 0: + exact = leaf.Leaf.Accumulation + case 1: + right = leaf.Leaf.Accumulation + } + return + } + + node := ptr.node() + idx, match := node.find(key) + if !match { + idx-- + } + left, exact, right = ptr.tree.ptrGet(ptr.level-1, node.Children[idx].Index).accumulationSplit(key) + left = left.Add(NewNode(node.Children[:idx]...).accumulate()) + right = right.Add(NewNode(node.Children[idx+1:]...).accumulate()) + return left, exact, right +} + +// TotalAccumulatedValue returns the sum of the weights for all leaves. +func (t Tree) TotalAccumulatedValue() osmomath.Int { + return t.SubsetAccumulation(nil, nil) +} + +// Prefix sum returns the total weight of all leaves with keys <= to the provided key. +func (t Tree) PrefixSum(key []byte) osmomath.Int { + return t.SubsetAccumulation(nil, key) +} + +// SubsetAccumulation returns the total value of all leaves with keys +// between start and end (inclusive of both ends) +// if start is nil, it is the beginning of the tree. +// if end is nil, it is the end of the tree. +func (t Tree) SubsetAccumulation(start []byte, end []byte) osmomath.Int { + if start == nil { + left, exact, _ := t.root().accumulationSplit(end) + return left.Add(exact) + } + if end == nil { + _, exact, right := t.root().accumulationSplit(start) + return exact.Add(right) + } + _, leftexact, leftrest := t.root().accumulationSplit(start) + _, _, rightest := t.root().accumulationSplit(end) + return leftexact.Add(leftrest).Sub(rightest) +} + +func (t Tree) SplitAcc(key []byte) (osmomath.Int, osmomath.Int, osmomath.Int) { + return t.root().accumulationSplit(key) +} + +func (ptr *ptr) visualize(depth int, acc osmomath.Int) { + if !ptr.exists() { + return + } + for i := 0; i < depth; i++ { + fmt.Printf(" ") + } + fmt.Printf("- ") + fmt.Printf("{%d %+v %v}\n", ptr.level, ptr.key, acc) + for i, child := range ptr.node().Children { + childnode := ptr.child(uint16(i)) + childnode.visualize(depth+1, child.Accumulation) + } +} + +// DebugVisualize prints the entire tree to stdout. +func (t Tree) DebugVisualize() { + t.root().visualize(0, osmomath.Int{}) +} diff --git a/tests/interchaintest/osmosis/osmoutils/sumtree/tree.pb.go b/tests/interchaintest/osmosis/osmoutils/sumtree/tree.pb.go new file mode 100644 index 00000000..b00a4da5 --- /dev/null +++ b/tests/interchaintest/osmosis/osmoutils/sumtree/tree.pb.go @@ -0,0 +1,735 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: osmosis/store/v1beta1/tree.proto + +package sumtree + +import ( + cosmossdk_io_math "cosmossdk.io/math" + fmt "fmt" + _ "github.com/cosmos/cosmos-sdk/types" + _ "github.com/cosmos/gogoproto/gogoproto" + proto "github.com/cosmos/gogoproto/proto" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +type Node struct { + Children []*Child `protobuf:"bytes,1,rep,name=children,proto3" json:"children,omitempty"` +} + +func (m *Node) Reset() { *m = Node{} } +func (m *Node) String() string { return proto.CompactTextString(m) } +func (*Node) ProtoMessage() {} +func (*Node) Descriptor() ([]byte, []int) { + return fileDescriptor_72b0b7af579d13be, []int{0} +} +func (m *Node) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Node) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Node.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Node) XXX_Merge(src proto.Message) { + xxx_messageInfo_Node.Merge(m, src) +} +func (m *Node) XXX_Size() int { + return m.Size() +} +func (m *Node) XXX_DiscardUnknown() { + xxx_messageInfo_Node.DiscardUnknown(m) +} + +var xxx_messageInfo_Node proto.InternalMessageInfo + +func (m *Node) GetChildren() []*Child { + if m != nil { + return m.Children + } + return nil +} + +type Child struct { + Index []byte `protobuf:"bytes,1,opt,name=index,proto3" json:"index,omitempty"` + Accumulation cosmossdk_io_math.Int `protobuf:"bytes,2,opt,name=accumulation,proto3,customtype=cosmossdk.io/math.Int" json:"accumulation"` +} + +func (m *Child) Reset() { *m = Child{} } +func (m *Child) String() string { return proto.CompactTextString(m) } +func (*Child) ProtoMessage() {} +func (*Child) Descriptor() ([]byte, []int) { + return fileDescriptor_72b0b7af579d13be, []int{1} +} +func (m *Child) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Child) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Child.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Child) XXX_Merge(src proto.Message) { + xxx_messageInfo_Child.Merge(m, src) +} +func (m *Child) XXX_Size() int { + return m.Size() +} +func (m *Child) XXX_DiscardUnknown() { + xxx_messageInfo_Child.DiscardUnknown(m) +} + +var xxx_messageInfo_Child proto.InternalMessageInfo + +func (m *Child) GetIndex() []byte { + if m != nil { + return m.Index + } + return nil +} + +type Leaf struct { + Leaf *Child `protobuf:"bytes,1,opt,name=leaf,proto3" json:"leaf,omitempty"` +} + +func (m *Leaf) Reset() { *m = Leaf{} } +func (m *Leaf) String() string { return proto.CompactTextString(m) } +func (*Leaf) ProtoMessage() {} +func (*Leaf) Descriptor() ([]byte, []int) { + return fileDescriptor_72b0b7af579d13be, []int{2} +} +func (m *Leaf) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Leaf) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Leaf.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Leaf) XXX_Merge(src proto.Message) { + xxx_messageInfo_Leaf.Merge(m, src) +} +func (m *Leaf) XXX_Size() int { + return m.Size() +} +func (m *Leaf) XXX_DiscardUnknown() { + xxx_messageInfo_Leaf.DiscardUnknown(m) +} + +var xxx_messageInfo_Leaf proto.InternalMessageInfo + +func (m *Leaf) GetLeaf() *Child { + if m != nil { + return m.Leaf + } + return nil +} + +func init() { + proto.RegisterType((*Node)(nil), "osmosis.store.v1beta1.Node") + proto.RegisterType((*Child)(nil), "osmosis.store.v1beta1.Child") + proto.RegisterType((*Leaf)(nil), "osmosis.store.v1beta1.Leaf") +} + +func init() { proto.RegisterFile("osmosis/store/v1beta1/tree.proto", fileDescriptor_72b0b7af579d13be) } + +var fileDescriptor_72b0b7af579d13be = []byte{ + // 297 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0xd0, 0x41, 0x4b, 0xc3, 0x30, + 0x14, 0x07, 0xf0, 0x46, 0x37, 0xd1, 0xb8, 0x53, 0xd9, 0x60, 0x0c, 0xcd, 0xca, 0x4e, 0xbb, 0x98, + 0x58, 0xbd, 0xec, 0xa8, 0xf3, 0x24, 0x8a, 0x87, 0x1e, 0x3d, 0x99, 0xa6, 0x59, 0x1b, 0x6c, 0xfb, + 0xa4, 0x49, 0xc5, 0x8f, 0xe1, 0xc7, 0xda, 0x71, 0x47, 0xf1, 0x30, 0xa4, 0xfd, 0x22, 0xd2, 0xb4, + 0x2a, 0x82, 0xe0, 0x29, 0x79, 0xc9, 0x2f, 0xff, 0x3c, 0x1e, 0xf6, 0x40, 0x67, 0xa0, 0x95, 0x66, + 0xda, 0x40, 0x21, 0xd9, 0xb3, 0x1f, 0x4a, 0xc3, 0x7d, 0x66, 0x0a, 0x29, 0xe9, 0x53, 0x01, 0x06, + 0xdc, 0x51, 0x27, 0xa8, 0x15, 0xb4, 0x13, 0x93, 0x61, 0x0c, 0x31, 0x58, 0xc1, 0x9a, 0x5d, 0x8b, + 0x27, 0x44, 0x58, 0xcd, 0x42, 0xae, 0x7f, 0xc2, 0x04, 0xa8, 0xbc, 0xbd, 0x9f, 0x5d, 0xe0, 0xde, + 0x1d, 0x44, 0xd2, 0x5d, 0xe0, 0x7d, 0x91, 0xa8, 0x34, 0x2a, 0x64, 0x3e, 0x46, 0xde, 0xee, 0xfc, + 0xf0, 0xec, 0x88, 0xfe, 0xf9, 0x0f, 0xbd, 0x6a, 0x58, 0xf0, 0xad, 0x67, 0x0f, 0xb8, 0x6f, 0x8f, + 0xdc, 0x21, 0xee, 0xab, 0x3c, 0x92, 0x2f, 0x63, 0xe4, 0xa1, 0xf9, 0x20, 0x68, 0x0b, 0xf7, 0x12, + 0x0f, 0xb8, 0x10, 0x65, 0x56, 0xa6, 0xdc, 0x28, 0xc8, 0xc7, 0x3b, 0x1e, 0x9a, 0x1f, 0x2c, 0x8f, + 0xd7, 0xdb, 0xa9, 0xf3, 0xbe, 0x9d, 0x8e, 0xda, 0xf6, 0x74, 0xf4, 0x48, 0x15, 0xb0, 0x8c, 0x9b, + 0x84, 0x5e, 0xe7, 0x26, 0xf8, 0xf5, 0x64, 0xb6, 0xc0, 0xbd, 0x5b, 0xc9, 0x57, 0xee, 0x29, 0xee, + 0xa5, 0x92, 0xaf, 0x6c, 0xfe, 0x7f, 0xfd, 0x59, 0xb9, 0xbc, 0x59, 0x57, 0x04, 0x6d, 0x2a, 0x82, + 0x3e, 0x2a, 0x82, 0x5e, 0x6b, 0xe2, 0x6c, 0x6a, 0xe2, 0xbc, 0xd5, 0xc4, 0xb9, 0xf7, 0x63, 0x65, + 0x92, 0x32, 0xa4, 0x02, 0x32, 0xd6, 0xe5, 0x9c, 0xa4, 0x3c, 0xd4, 0x5f, 0x85, 0x5d, 0x4b, 0xa3, + 0x52, 0xcd, 0x74, 0x99, 0x35, 0xd3, 0x0f, 0xf7, 0xec, 0xc4, 0xce, 0x3f, 0x03, 0x00, 0x00, 0xff, + 0xff, 0x27, 0x89, 0xf1, 0x3a, 0xa2, 0x01, 0x00, 0x00, +} + +func (m *Node) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Node) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Node) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Children) > 0 { + for iNdEx := len(m.Children) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Children[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTree(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *Child) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Child) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Child) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size := m.Accumulation.Size() + i -= size + if _, err := m.Accumulation.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintTree(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + if len(m.Index) > 0 { + i -= len(m.Index) + copy(dAtA[i:], m.Index) + i = encodeVarintTree(dAtA, i, uint64(len(m.Index))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Leaf) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Leaf) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Leaf) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Leaf != nil { + { + size, err := m.Leaf.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTree(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintTree(dAtA []byte, offset int, v uint64) int { + offset -= sovTree(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *Node) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Children) > 0 { + for _, e := range m.Children { + l = e.Size() + n += 1 + l + sovTree(uint64(l)) + } + } + return n +} + +func (m *Child) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Index) + if l > 0 { + n += 1 + l + sovTree(uint64(l)) + } + l = m.Accumulation.Size() + n += 1 + l + sovTree(uint64(l)) + return n +} + +func (m *Leaf) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Leaf != nil { + l = m.Leaf.Size() + n += 1 + l + sovTree(uint64(l)) + } + return n +} + +func sovTree(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozTree(x uint64) (n int) { + return sovTree(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *Node) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTree + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Node: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Node: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Children", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTree + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTree + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTree + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Children = append(m.Children, &Child{}) + if err := m.Children[len(m.Children)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTree(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTree + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Child) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTree + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Child: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Child: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Index", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTree + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTree + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTree + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Index = append(m.Index[:0], dAtA[iNdEx:postIndex]...) + if m.Index == nil { + m.Index = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Accumulation", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTree + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTree + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTree + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Accumulation.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTree(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTree + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Leaf) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTree + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Leaf: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Leaf: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Leaf", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTree + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTree + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTree + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Leaf == nil { + m.Leaf = &Child{} + } + if err := m.Leaf.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTree(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTree + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipTree(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTree + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTree + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTree + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthTree + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupTree + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthTree + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthTree = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowTree = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupTree = fmt.Errorf("proto: unexpected end of group") +) diff --git a/tests/interchaintest/osmosis/osmoutils/sumtree/tree_test.go b/tests/interchaintest/osmosis/osmoutils/sumtree/tree_test.go new file mode 100644 index 00000000..3108ee3c --- /dev/null +++ b/tests/interchaintest/osmosis/osmoutils/sumtree/tree_test.go @@ -0,0 +1,141 @@ +package sumtree_test + +import ( + "bytes" + "fmt" + "math/rand" + "sort" + "testing" + + "github.com/stretchr/testify/suite" + + "github.com/cosmos/iavl" + + dbm "github.com/cometbft/cometbft-db" + + iavlstore "github.com/cosmos/cosmos-sdk/store/iavl" + + "github.com/osmosis-labs/fee-abstraction/v7/tests/interchaintest/osmosis/osmomath" + "github.com/osmosis-labs/fee-abstraction/v7/tests/interchaintest/osmosis/osmoutils/sumtree" +) + +type TreeTestSuite struct { + suite.Suite + + tree sumtree.Tree +} + +func (suite *TreeTestSuite) SetupTest() { + db := dbm.NewMemDB() + tree, err := iavl.NewMutableTree(db, 100, false) + suite.Require().NoError(err) + _, _, err = tree.SaveVersion() + suite.Require().Nil(err) + kvstore := iavlstore.UnsafeNewStore(tree) + suite.tree = sumtree.NewTree(kvstore, 10) +} + +func TestTreeTestSuite(t *testing.T) { + suite.Run(t, new(TreeTestSuite)) +} + +type pair struct { + key []byte + value uint64 +} + +type pairs []pair + +var _ sort.Interface = pairs{} + +func (p pairs) Len() int { + return len(p) +} + +func (p pairs) Less(i, j int) bool { + return bytes.Compare(p[i].key, p[j].key) < 0 +} + +func (p pairs) Swap(i, j int) { + temp := p[i] + p[i] = p[j] + p[j] = temp +} + +func (p pairs) sum() (res uint64) { + for _, pair := range p { + res += pair.value + } + return +} + +func (suite *TreeTestSuite) TestTreeInvariants() { + suite.SetupTest() + + pairs := pairs{pair{[]byte("hello"), 100}} + suite.tree.Set([]byte("hello"), osmomath.NewIntFromUint64(100)) + + seed := rand.Int63() + fmt.Printf("running seed %d: \n", seed) + r := rand.New(rand.NewSource(seed)) + + // tested up to 2000 + for i := 0; i < 500; i++ { + // add a single element + key := make([]byte, r.Int()%20) + value := r.Uint64() % 100 + r.Read(key) + idx := sort.Search(len(pairs), func(n int) bool { return bytes.Compare(pairs[n].key, key) >= 0 }) + if idx < len(pairs) { + if bytes.Equal(pairs[idx].key, key) { + pairs[idx] = pair{key, value} + } else { + pairs = append(pairs, pair{key, value}) + sort.Sort(pairs) + } + } else { + pairs = append(pairs, pair{key, value}) + } + + suite.tree.Set(key, osmomath.NewIntFromUint64(value)) + + // check all is right + for _, pair := range pairs { + suite.Require().Equal(suite.tree.Get(pair.key).Uint64(), pair.value) + // XXX: check all branch nodes + } + + // check accumulation calc is alright + left, exact, right := uint64(0), pairs[0].value, pairs[1:].sum() + for idx, pair := range pairs { + tleft, texact, tright := suite.tree.SplitAcc(pair.key) + suite.Require().Equal(left, tleft.Uint64()) + suite.Require().Equal(exact, texact.Uint64()) + suite.Require().Equal(right, tright.Uint64()) + + key := append(pair.key, 0x00) + if idx == len(pairs)-1 { + break + } + if bytes.Equal(key, pairs[idx+1].key) { + break + } + + tleft, texact, tright = suite.tree.SplitAcc(key) + suite.Require().Equal(left+exact, tleft.Uint64()) + suite.Require().Equal(uint64(0), texact.Uint64()) + suite.Require().Equal(right, tright.Uint64()) + + left += exact + exact = pairs[idx+1].value + right -= exact + } + + if r.Int()%2 == 0 { + idx := r.Int() % len(pairs) + pair := pairs[idx] + pairs = append(pairs[:idx], pairs[idx+1:]...) + suite.tree.Remove(pair.key) + } + } +} diff --git a/tests/interchaintest/osmosis/poolmanager/types/codec.go b/tests/interchaintest/osmosis/poolmanager/types/codec.go new file mode 100644 index 00000000..f2ff02a6 --- /dev/null +++ b/tests/interchaintest/osmosis/poolmanager/types/codec.go @@ -0,0 +1,44 @@ +package types + +import ( + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/msgservice" + authzcodec "github.com/cosmos/cosmos-sdk/x/authz/codec" +) + +// RegisterLegacyAminoCodec registers the necessary x/gamm interfaces and concrete types +// on the provided LegacyAmino codec. These types are used for Amino JSON serialization. +func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { + cdc.RegisterConcrete(&MsgSwapExactAmountIn{}, "osmosis/poolmanager/swap-exact-amount-in", nil) + cdc.RegisterConcrete(&MsgSwapExactAmountOut{}, "osmosis/poolmanager/swap-exact-amount-out", nil) + cdc.RegisterConcrete(&MsgSplitRouteSwapExactAmountIn{}, "osmosis/poolmanager/split-amount-in", nil) + cdc.RegisterConcrete(&MsgSplitRouteSwapExactAmountOut{}, "osmosis/poolmanager/split-amount-out", nil) +} + +func RegisterInterfaces(registry types.InterfaceRegistry) { + registry.RegisterImplementations( + (*sdk.Msg)(nil), + &MsgSwapExactAmountIn{}, + &MsgSwapExactAmountOut{}, + &MsgSplitRouteSwapExactAmountIn{}, + &MsgSplitRouteSwapExactAmountOut{}, + ) + msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc) +} + +var ( + amino = codec.NewLegacyAmino() + ModuleCdc = codec.NewAminoCodec(amino) +) + +func init() { + RegisterLegacyAminoCodec(amino) + // Register all Amino interfaces and concrete types on the authz Amino codec so that this can later be + // used to properly serialize MsgGrant and MsgExec instances + sdk.RegisterLegacyAminoCodec(amino) + RegisterLegacyAminoCodec(authzcodec.Amino) + + amino.Seal() +} diff --git a/tests/interchaintest/osmosis/poolmanager/types/errors.go b/tests/interchaintest/osmosis/poolmanager/types/errors.go new file mode 100644 index 00000000..d8e29613 --- /dev/null +++ b/tests/interchaintest/osmosis/poolmanager/types/errors.go @@ -0,0 +1,135 @@ +package types + +import ( + "errors" + "fmt" + + "github.com/osmosis-labs/fee-abstraction/v7/tests/interchaintest/osmosis/osmomath" +) + +const ( + amountOutPlaceholder = "out" + amountInPlaceholder = "in" +) + +var ( + ErrEmptyRoutes = errors.New("provided empty routes") + ErrTooFewPoolAssets = errors.New("pool should have at least 2 assets, as they must be swapping between at least two assets") + ErrTooManyPoolAssets = errors.New("pool has too many assets (currently capped at 8 assets per pool)") + ErrDuplicateRoutesNotAllowed = errors.New("duplicate multihop routes are not allowed") +) + +type nonPositiveAmountError struct { + Amount string +} + +func (e nonPositiveAmountError) Error() string { + return fmt.Sprintf("min out amount or max in amount should be positive, was (%s)", e.Amount) +} + +type FailedToFindRouteError struct { + PoolId uint64 +} + +func (e FailedToFindRouteError) Error() string { + return fmt.Sprintf("failed to find route for pool id (%d)", e.PoolId) +} + +type UndefinedRouteError struct { + PoolType PoolType + PoolId uint64 +} + +func (e UndefinedRouteError) Error() string { + return fmt.Sprintf("route is not defined for the given pool type (%s) and pool id (%d)", e.PoolType, e.PoolId) +} + +type FinalAmountIsNotPositiveError struct { + IsAmountOut bool + Amount osmomath.Int +} + +func (e FinalAmountIsNotPositiveError) Error() string { + amountPlaceholder := amountOutPlaceholder + if !e.IsAmountOut { + amountPlaceholder = amountInPlaceholder + } + return fmt.Sprintf("final total amount (%s) must be positive, was (%d)", amountPlaceholder, e.Amount) +} + +type PriceImpactProtectionExactInError struct { + Actual osmomath.Int + MinAmount osmomath.Int +} + +func (e PriceImpactProtectionExactInError) Error() string { + return fmt.Sprintf("price impact protection: expected %s to be at least %s", e.Actual, e.MinAmount) +} + +type PriceImpactProtectionExactOutError struct { + Actual osmomath.Int + MaxAmount osmomath.Int +} + +func (e PriceImpactProtectionExactOutError) Error() string { + return fmt.Sprintf("price impact protection: expected %s to be at most %s", e.Actual, e.MaxAmount) +} + +type InvalidFinalTokenOutError struct { + TokenOutGivenA string + TokenOutGivenB string +} + +func (e InvalidFinalTokenOutError) Error() string { + return fmt.Sprintf("invalid final token out, each path must end on the same token out, had (%s) and (%s) mismatch", e.TokenOutGivenA, e.TokenOutGivenB) +} + +type InvalidSenderError struct { + Sender string +} + +func (e InvalidSenderError) Error() string { + return fmt.Sprintf("Invalid sender address (%s)", e.Sender) +} + +type InvalidPoolCreatorError struct { + CreatorAddresss string +} + +func (e InvalidPoolCreatorError) Error() string { + return fmt.Sprintf("invalid pool creator (%s), only poolmanager can create pools with no fee", e.CreatorAddresss) +} + +type InvalidPoolTypeError struct { + PoolType PoolType +} + +func (e InvalidPoolTypeError) Error() string { + return fmt.Sprintf("invalid pool type (%s)", PoolType_name[int32(e.PoolType)]) +} + +type IncorrectPoolIdError struct { + ExpectedPoolId uint64 + ActualPoolId uint64 +} + +func (e IncorrectPoolIdError) Error() string { + return fmt.Sprintf("Pool was attempted to be created with incorrect pool ID. Expected (%d), actual (%d)", e.ExpectedPoolId, e.ActualPoolId) +} + +type IncorrectPoolAddressError struct { + ExpectedPoolAddress string + ActualPoolAddress string +} + +func (e IncorrectPoolAddressError) Error() string { + return fmt.Sprintf("Pool was attempted to be created with incorrect pool address. Expected (%s), actual (%s)", e.ExpectedPoolAddress, e.ActualPoolAddress) +} + +type InactivePoolError struct { + PoolId uint64 +} + +func (e InactivePoolError) Error() string { + return fmt.Sprintf("Pool %d is not active.", e.PoolId) +} diff --git a/tests/interchaintest/osmosis/poolmanager/types/events.go b/tests/interchaintest/osmosis/poolmanager/types/events.go new file mode 100644 index 00000000..0133711a --- /dev/null +++ b/tests/interchaintest/osmosis/poolmanager/types/events.go @@ -0,0 +1,13 @@ +package types + +const ( + AttributeValueCategory = ModuleName + TypeEvtPoolCreated = "pool_created" + TypeEvtSplitRouteSwapExactIn = "split_route_swap_exact_in" + AttributeKeyTokensIn = "tokens_in" + AttributeKeyTokensOut = "tokens_out" + AttributeKeyPoolId = "pool_id" + AttributeKeyDenom0 = "denom0" + AttributeKeyDenom1 = "denom1" + AttributeKeyTakerFee = "taker_fee" +) diff --git a/tests/interchaintest/osmosis/poolmanager/types/expected_keepers.go b/tests/interchaintest/osmosis/poolmanager/types/expected_keepers.go new file mode 100644 index 00000000..9428f8e9 --- /dev/null +++ b/tests/interchaintest/osmosis/poolmanager/types/expected_keepers.go @@ -0,0 +1,114 @@ +package types + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/osmosis-labs/fee-abstraction/v7/tests/interchaintest/osmosis/osmomath" + + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" +) + +// AccountI defines the account contract that must be fulfilled when +// creating a x/gamm keeper. +type AccountI interface { + GetModuleAccount(ctx sdk.Context, moduleName string) authtypes.ModuleAccountI + NewAccount(sdk.Context, authtypes.AccountI) authtypes.AccountI + GetAccount(ctx sdk.Context, addr sdk.AccAddress) authtypes.AccountI + SetAccount(ctx sdk.Context, acc authtypes.AccountI) +} + +// BankI defines the banking contract that must be fulfilled when +// creating a x/gamm keeper. +type BankI interface { + SendCoins(ctx sdk.Context, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins) error + SetDenomMetaData(ctx sdk.Context, denomMetaData banktypes.Metadata) + GetAllBalances(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins + SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error +} + +// CommunityPoolI defines the contract needed to be fulfilled for distribution keeper. +type CommunityPoolI interface { + FundCommunityPool(ctx sdk.Context, amount sdk.Coins, sender sdk.AccAddress) error +} + +// PoolModuleI is the interface that must be fulfillled by the module +// storing and containing the pools. +type PoolModuleI interface { + InitializePool(ctx sdk.Context, pool PoolI, creatorAddress sdk.AccAddress) error + + GetPool(ctx sdk.Context, poolId uint64) (PoolI, error) + + GetPools(ctx sdk.Context) ([]PoolI, error) + + GetPoolDenoms(ctx sdk.Context, poolId uint64) (denoms []string, err error) + + CalculateSpotPrice( + ctx sdk.Context, + poolId uint64, + quoteAssetDenom string, + baseAssetDenom string, + ) (price osmomath.BigDec, err error) + + SwapExactAmountIn( + ctx sdk.Context, + sender sdk.AccAddress, + pool PoolI, + tokenIn sdk.Coin, + tokenOutDenom string, + tokenOutMinAmount osmomath.Int, + spreadFactor osmomath.Dec, + ) (osmomath.Int, error) + // CalcOutAmtGivenIn calculates the amount of tokenOut given tokenIn and the pool's current state. + // Returns error if the given pool is not a CFMM pool. Returns error on internal calculations. + CalcOutAmtGivenIn( + ctx sdk.Context, + poolI PoolI, + tokenIn sdk.Coin, + tokenOutDenom string, + spreadFactor osmomath.Dec, + ) (tokenOut sdk.Coin, err error) + + SwapExactAmountOut( + ctx sdk.Context, + sender sdk.AccAddress, + pool PoolI, + tokenInDenom string, + tokenInMaxAmount osmomath.Int, + tokenOut sdk.Coin, + spreadFactor osmomath.Dec, + ) (tokenInAmount osmomath.Int, err error) + // CalcInAmtGivenOut calculates the amount of tokenIn given tokenOut and the pool's current state. + // Returns error if the given pool is not a CFMM pool. Returns error on internal calculations. + CalcInAmtGivenOut( + ctx sdk.Context, + poolI PoolI, + tokenOut sdk.Coin, + tokenInDenom string, + spreadFactor osmomath.Dec, + ) (tokenIn sdk.Coin, err error) + + // GetTotalPoolLiquidity returns the coins in the pool owned by all LPs + GetTotalPoolLiquidity(ctx sdk.Context, poolId uint64) (sdk.Coins, error) + + // GetTotalLiquidity returns the total liquidity of all the pools in the module. + GetTotalLiquidity(ctx sdk.Context) (sdk.Coins, error) +} + +type PoolIncentivesKeeperI interface { + IsPoolIncentivized(ctx sdk.Context, poolId uint64) (bool, error) +} + +type MultihopRoute interface { + Length() int + PoolIds() []uint64 + IntermediateDenoms() []string +} + +type StakingKeeper interface { + BondDenom(ctx sdk.Context) string +} + +type ProtorevKeeper interface { + GetPoolForDenomPair(ctx sdk.Context, baseDenom, denomToMatch string) (uint64, error) +} diff --git a/tests/interchaintest/osmosis/poolmanager/types/genesis.pb.go b/tests/interchaintest/osmosis/poolmanager/types/genesis.pb.go new file mode 100644 index 00000000..5fa92ff3 --- /dev/null +++ b/tests/interchaintest/osmosis/poolmanager/types/genesis.pb.go @@ -0,0 +1,2146 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: osmosis/poolmanager/v1beta1/genesis.proto + +package types + +import ( + cosmossdk_io_math "cosmossdk.io/math" + fmt "fmt" + _ "github.com/cosmos/cosmos-proto" + _ "github.com/cosmos/cosmos-sdk/codec/types" + github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" + types "github.com/cosmos/cosmos-sdk/types" + _ "github.com/cosmos/gogoproto/gogoproto" + proto "github.com/cosmos/gogoproto/proto" + _ "google.golang.org/protobuf/types/known/durationpb" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// Params holds parameters for the poolmanager module +type Params struct { + PoolCreationFee github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,1,rep,name=pool_creation_fee,json=poolCreationFee,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"pool_creation_fee" yaml:"pool_creation_fee"` + // taker_fee_params is the container of taker fee parameters. + TakerFeeParams TakerFeeParams `protobuf:"bytes,2,opt,name=taker_fee_params,json=takerFeeParams,proto3" json:"taker_fee_params" yaml:"taker_fee_params"` + // authorized_quote_denoms is a list of quote denoms that can be used as + // token1 when creating a concentrated pool. We limit the quote assets to a + // small set for the purposes of having convenient price increments stemming + // from tick to price conversion. These increments are in a human readable + // magnitude only for token1 as a quote. For limit orders in the future, this + // will be a desirable property in terms of UX as to allow users to set limit + // orders at prices in terms of token1 (quote asset) that are easy to reason + // about. + AuthorizedQuoteDenoms []string `protobuf:"bytes,3,rep,name=authorized_quote_denoms,json=authorizedQuoteDenoms,proto3" json:"authorized_quote_denoms,omitempty" yaml:"authorized_quote_denoms"` +} + +func (m *Params) Reset() { *m = Params{} } +func (m *Params) String() string { return proto.CompactTextString(m) } +func (*Params) ProtoMessage() {} +func (*Params) Descriptor() ([]byte, []int) { + return fileDescriptor_aa099d9fbdf68b35, []int{0} +} +func (m *Params) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Params) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Params.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Params) XXX_Merge(src proto.Message) { + xxx_messageInfo_Params.Merge(m, src) +} +func (m *Params) XXX_Size() int { + return m.Size() +} +func (m *Params) XXX_DiscardUnknown() { + xxx_messageInfo_Params.DiscardUnknown(m) +} + +var xxx_messageInfo_Params proto.InternalMessageInfo + +func (m *Params) GetPoolCreationFee() github_com_cosmos_cosmos_sdk_types.Coins { + if m != nil { + return m.PoolCreationFee + } + return nil +} + +func (m *Params) GetTakerFeeParams() TakerFeeParams { + if m != nil { + return m.TakerFeeParams + } + return TakerFeeParams{} +} + +func (m *Params) GetAuthorizedQuoteDenoms() []string { + if m != nil { + return m.AuthorizedQuoteDenoms + } + return nil +} + +// GenesisState defines the poolmanager module's genesis state. +type GenesisState struct { + // the next_pool_id + NextPoolId uint64 `protobuf:"varint,1,opt,name=next_pool_id,json=nextPoolId,proto3" json:"next_pool_id,omitempty"` + // params is the container of poolmanager parameters. + Params Params `protobuf:"bytes,2,opt,name=params,proto3" json:"params"` + // pool_routes is the container of the mappings from pool id to pool type. + PoolRoutes []ModuleRoute `protobuf:"bytes,3,rep,name=pool_routes,json=poolRoutes,proto3" json:"pool_routes"` + // KVStore state + TakerFeesTracker *TakerFeesTracker `protobuf:"bytes,4,opt,name=taker_fees_tracker,json=takerFeesTracker,proto3" json:"taker_fees_tracker,omitempty"` + PoolVolumes []*PoolVolume `protobuf:"bytes,5,rep,name=pool_volumes,json=poolVolumes,proto3" json:"pool_volumes,omitempty"` + DenomPairTakerFeeStore []DenomPairTakerFee `protobuf:"bytes,6,rep,name=denom_pair_taker_fee_store,json=denomPairTakerFeeStore,proto3" json:"denom_pair_taker_fee_store"` +} + +func (m *GenesisState) Reset() { *m = GenesisState{} } +func (m *GenesisState) String() string { return proto.CompactTextString(m) } +func (*GenesisState) ProtoMessage() {} +func (*GenesisState) Descriptor() ([]byte, []int) { + return fileDescriptor_aa099d9fbdf68b35, []int{1} +} +func (m *GenesisState) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GenesisState) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GenesisState.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *GenesisState) XXX_Merge(src proto.Message) { + xxx_messageInfo_GenesisState.Merge(m, src) +} +func (m *GenesisState) XXX_Size() int { + return m.Size() +} +func (m *GenesisState) XXX_DiscardUnknown() { + xxx_messageInfo_GenesisState.DiscardUnknown(m) +} + +var xxx_messageInfo_GenesisState proto.InternalMessageInfo + +func (m *GenesisState) GetNextPoolId() uint64 { + if m != nil { + return m.NextPoolId + } + return 0 +} + +func (m *GenesisState) GetParams() Params { + if m != nil { + return m.Params + } + return Params{} +} + +func (m *GenesisState) GetPoolRoutes() []ModuleRoute { + if m != nil { + return m.PoolRoutes + } + return nil +} + +func (m *GenesisState) GetTakerFeesTracker() *TakerFeesTracker { + if m != nil { + return m.TakerFeesTracker + } + return nil +} + +func (m *GenesisState) GetPoolVolumes() []*PoolVolume { + if m != nil { + return m.PoolVolumes + } + return nil +} + +func (m *GenesisState) GetDenomPairTakerFeeStore() []DenomPairTakerFee { + if m != nil { + return m.DenomPairTakerFeeStore + } + return nil +} + +// TakerFeeParams consolidates the taker fee parameters for the poolmanager. +type TakerFeeParams struct { + // default_taker_fee is the fee used when creating a new pool that doesn't + // fall under a custom pool taker fee or stableswap taker fee category. + DefaultTakerFee cosmossdk_io_math.LegacyDec `protobuf:"bytes,1,opt,name=default_taker_fee,json=defaultTakerFee,proto3,customtype=cosmossdk.io/math.LegacyDec" json:"default_taker_fee"` + // osmo_taker_fee_distribution defines the distribution of taker fees + // generated in OSMO. As of this writing, it has two categories: + // - staking_rewards: the percent of the taker fee that gets distributed to + // stakers. + // - community_pool: the percent of the taker fee that gets sent to the + // community pool. + OsmoTakerFeeDistribution TakerFeeDistributionPercentage `protobuf:"bytes,2,opt,name=osmo_taker_fee_distribution,json=osmoTakerFeeDistribution,proto3" json:"osmo_taker_fee_distribution"` + // non_osmo_taker_fee_distribution defines the distribution of taker fees + // generated in non-OSMO. As of this writing, it has two categories: + // - staking_rewards: the percent of the taker fee that gets swapped to OSMO + // and then distributed to stakers. + // - community_pool: the percent of the taker fee that gets sent to the + // community pool. Note: If the non-OSMO asset is an authorized_quote_denom, + // that denom is sent directly to the community pool. Otherwise, it is + // swapped to the community_pool_denom_to_swap_non_whitelisted_assets_to and + // then sent to the community pool as that denom. + NonOsmoTakerFeeDistribution TakerFeeDistributionPercentage `protobuf:"bytes,3,opt,name=non_osmo_taker_fee_distribution,json=nonOsmoTakerFeeDistribution,proto3" json:"non_osmo_taker_fee_distribution"` + // admin_addresses is a list of addresses that are allowed to set and remove + // custom taker fees for denom pairs. Governance also has the ability to set + // and remove custom taker fees for denom pairs, but with the normal + // governance delay. + AdminAddresses []string `protobuf:"bytes,4,rep,name=admin_addresses,json=adminAddresses,proto3" json:"admin_addresses,omitempty" yaml:"admin_addresses"` + // community_pool_denom_to_swap_non_whitelisted_assets_to is the denom that + // non-whitelisted taker fees will be swapped to before being sent to + // the community pool. + CommunityPoolDenomToSwapNonWhitelistedAssetsTo string `protobuf:"bytes,5,opt,name=community_pool_denom_to_swap_non_whitelisted_assets_to,json=communityPoolDenomToSwapNonWhitelistedAssetsTo,proto3" json:"community_pool_denom_to_swap_non_whitelisted_assets_to,omitempty" yaml:"community_pool_denom_to_swap_non_whitelisted_assets_to"` + // reduced_fee_whitelist is a list of addresses that are + // allowed to pay a reduce taker fee when performing a swap + // (i.e. swap without paying the taker fee). + // It is intended to be used for integrators who meet qualifying factors + // that are approved by governance. + // Initially, the taker fee is allowed to be bypassed completely. However + // In the future, we will charge a reduced taker fee instead of no fee at all. + ReducedFeeWhitelist []string `protobuf:"bytes,6,rep,name=reduced_fee_whitelist,json=reducedFeeWhitelist,proto3" json:"reduced_fee_whitelist,omitempty" yaml:"reduced_fee_whitelist"` +} + +func (m *TakerFeeParams) Reset() { *m = TakerFeeParams{} } +func (m *TakerFeeParams) String() string { return proto.CompactTextString(m) } +func (*TakerFeeParams) ProtoMessage() {} +func (*TakerFeeParams) Descriptor() ([]byte, []int) { + return fileDescriptor_aa099d9fbdf68b35, []int{2} +} +func (m *TakerFeeParams) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *TakerFeeParams) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_TakerFeeParams.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *TakerFeeParams) XXX_Merge(src proto.Message) { + xxx_messageInfo_TakerFeeParams.Merge(m, src) +} +func (m *TakerFeeParams) XXX_Size() int { + return m.Size() +} +func (m *TakerFeeParams) XXX_DiscardUnknown() { + xxx_messageInfo_TakerFeeParams.DiscardUnknown(m) +} + +var xxx_messageInfo_TakerFeeParams proto.InternalMessageInfo + +func (m *TakerFeeParams) GetOsmoTakerFeeDistribution() TakerFeeDistributionPercentage { + if m != nil { + return m.OsmoTakerFeeDistribution + } + return TakerFeeDistributionPercentage{} +} + +func (m *TakerFeeParams) GetNonOsmoTakerFeeDistribution() TakerFeeDistributionPercentage { + if m != nil { + return m.NonOsmoTakerFeeDistribution + } + return TakerFeeDistributionPercentage{} +} + +func (m *TakerFeeParams) GetAdminAddresses() []string { + if m != nil { + return m.AdminAddresses + } + return nil +} + +func (m *TakerFeeParams) GetCommunityPoolDenomToSwapNonWhitelistedAssetsTo() string { + if m != nil { + return m.CommunityPoolDenomToSwapNonWhitelistedAssetsTo + } + return "" +} + +func (m *TakerFeeParams) GetReducedFeeWhitelist() []string { + if m != nil { + return m.ReducedFeeWhitelist + } + return nil +} + +// TakerFeeDistributionPercentage defines what percent of the taker fee category +// gets distributed to the available categories. +type TakerFeeDistributionPercentage struct { + StakingRewards cosmossdk_io_math.LegacyDec `protobuf:"bytes,1,opt,name=staking_rewards,json=stakingRewards,proto3,customtype=cosmossdk.io/math.LegacyDec" json:"staking_rewards" yaml:"staking_rewards"` + CommunityPool cosmossdk_io_math.LegacyDec `protobuf:"bytes,2,opt,name=community_pool,json=communityPool,proto3,customtype=cosmossdk.io/math.LegacyDec" json:"community_pool" yaml:"community_pool"` +} + +func (m *TakerFeeDistributionPercentage) Reset() { *m = TakerFeeDistributionPercentage{} } +func (m *TakerFeeDistributionPercentage) String() string { return proto.CompactTextString(m) } +func (*TakerFeeDistributionPercentage) ProtoMessage() {} +func (*TakerFeeDistributionPercentage) Descriptor() ([]byte, []int) { + return fileDescriptor_aa099d9fbdf68b35, []int{3} +} +func (m *TakerFeeDistributionPercentage) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *TakerFeeDistributionPercentage) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_TakerFeeDistributionPercentage.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *TakerFeeDistributionPercentage) XXX_Merge(src proto.Message) { + xxx_messageInfo_TakerFeeDistributionPercentage.Merge(m, src) +} +func (m *TakerFeeDistributionPercentage) XXX_Size() int { + return m.Size() +} +func (m *TakerFeeDistributionPercentage) XXX_DiscardUnknown() { + xxx_messageInfo_TakerFeeDistributionPercentage.DiscardUnknown(m) +} + +var xxx_messageInfo_TakerFeeDistributionPercentage proto.InternalMessageInfo + +type TakerFeesTracker struct { + TakerFeesToStakers []types.Coin `protobuf:"bytes,1,rep,name=taker_fees_to_stakers,json=takerFeesToStakers,proto3" json:"taker_fees_to_stakers"` + TakerFeesToCommunityPool []types.Coin `protobuf:"bytes,2,rep,name=taker_fees_to_community_pool,json=takerFeesToCommunityPool,proto3" json:"taker_fees_to_community_pool"` + HeightAccountingStartsFrom int64 `protobuf:"varint,3,opt,name=height_accounting_starts_from,json=heightAccountingStartsFrom,proto3" json:"height_accounting_starts_from,omitempty" yaml:"height_accounting_starts_from"` +} + +func (m *TakerFeesTracker) Reset() { *m = TakerFeesTracker{} } +func (m *TakerFeesTracker) String() string { return proto.CompactTextString(m) } +func (*TakerFeesTracker) ProtoMessage() {} +func (*TakerFeesTracker) Descriptor() ([]byte, []int) { + return fileDescriptor_aa099d9fbdf68b35, []int{4} +} +func (m *TakerFeesTracker) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *TakerFeesTracker) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_TakerFeesTracker.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *TakerFeesTracker) XXX_Merge(src proto.Message) { + xxx_messageInfo_TakerFeesTracker.Merge(m, src) +} +func (m *TakerFeesTracker) XXX_Size() int { + return m.Size() +} +func (m *TakerFeesTracker) XXX_DiscardUnknown() { + xxx_messageInfo_TakerFeesTracker.DiscardUnknown(m) +} + +var xxx_messageInfo_TakerFeesTracker proto.InternalMessageInfo + +func (m *TakerFeesTracker) GetTakerFeesToStakers() []types.Coin { + if m != nil { + return m.TakerFeesToStakers + } + return nil +} + +func (m *TakerFeesTracker) GetTakerFeesToCommunityPool() []types.Coin { + if m != nil { + return m.TakerFeesToCommunityPool + } + return nil +} + +func (m *TakerFeesTracker) GetHeightAccountingStartsFrom() int64 { + if m != nil { + return m.HeightAccountingStartsFrom + } + return 0 +} + +// PoolVolume stores the KVStore entries for each pool's volume, which +// is used in export/import genesis. +type PoolVolume struct { + // pool_id is the id of the pool. + PoolId uint64 `protobuf:"varint,1,opt,name=pool_id,json=poolId,proto3" json:"pool_id,omitempty"` + // pool_volume is the cumulative volume of the pool. + PoolVolume github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,2,rep,name=pool_volume,json=poolVolume,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"pool_volume"` +} + +func (m *PoolVolume) Reset() { *m = PoolVolume{} } +func (m *PoolVolume) String() string { return proto.CompactTextString(m) } +func (*PoolVolume) ProtoMessage() {} +func (*PoolVolume) Descriptor() ([]byte, []int) { + return fileDescriptor_aa099d9fbdf68b35, []int{5} +} +func (m *PoolVolume) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *PoolVolume) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_PoolVolume.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *PoolVolume) XXX_Merge(src proto.Message) { + xxx_messageInfo_PoolVolume.Merge(m, src) +} +func (m *PoolVolume) XXX_Size() int { + return m.Size() +} +func (m *PoolVolume) XXX_DiscardUnknown() { + xxx_messageInfo_PoolVolume.DiscardUnknown(m) +} + +var xxx_messageInfo_PoolVolume proto.InternalMessageInfo + +func (m *PoolVolume) GetPoolId() uint64 { + if m != nil { + return m.PoolId + } + return 0 +} + +func (m *PoolVolume) GetPoolVolume() github_com_cosmos_cosmos_sdk_types.Coins { + if m != nil { + return m.PoolVolume + } + return nil +} + +func init() { + proto.RegisterType((*Params)(nil), "osmosis.poolmanager.v1beta1.Params") + proto.RegisterType((*GenesisState)(nil), "osmosis.poolmanager.v1beta1.GenesisState") + proto.RegisterType((*TakerFeeParams)(nil), "osmosis.poolmanager.v1beta1.TakerFeeParams") + proto.RegisterType((*TakerFeeDistributionPercentage)(nil), "osmosis.poolmanager.v1beta1.TakerFeeDistributionPercentage") + proto.RegisterType((*TakerFeesTracker)(nil), "osmosis.poolmanager.v1beta1.TakerFeesTracker") + proto.RegisterType((*PoolVolume)(nil), "osmosis.poolmanager.v1beta1.PoolVolume") +} + +func init() { + proto.RegisterFile("osmosis/poolmanager/v1beta1/genesis.proto", fileDescriptor_aa099d9fbdf68b35) +} + +var fileDescriptor_aa099d9fbdf68b35 = []byte{ + // 1080 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x56, 0x4d, 0x6f, 0xdc, 0x44, + 0x18, 0x8e, 0xb3, 0xe9, 0xa2, 0x4c, 0x42, 0x92, 0x0e, 0xa4, 0x71, 0x93, 0xb2, 0x5e, 0xb9, 0x95, + 0x58, 0x84, 0xe2, 0xa5, 0x41, 0x2a, 0x12, 0xd0, 0x43, 0x36, 0x51, 0x10, 0xa8, 0xb4, 0xa9, 0x13, + 0x81, 0x54, 0x0e, 0xa3, 0x59, 0xfb, 0x8d, 0xd7, 0x5a, 0xdb, 0xb3, 0xcc, 0x8c, 0xf3, 0xc1, 0x5f, + 0xe0, 0x82, 0xd4, 0x2b, 0x67, 0x0e, 0xdc, 0x90, 0xf8, 0x11, 0x3d, 0xf6, 0x88, 0x38, 0x6c, 0x51, + 0x72, 0xe6, 0xb2, 0xbf, 0x00, 0x79, 0x66, 0xf6, 0xb3, 0xc9, 0x36, 0xc0, 0x69, 0xd7, 0xef, 0xfb, + 0x3e, 0x8f, 0x9f, 0xf7, 0x6b, 0xc6, 0xe8, 0x03, 0x26, 0x52, 0x26, 0x62, 0x51, 0xef, 0x30, 0x96, + 0xa4, 0x34, 0xa3, 0x11, 0xf0, 0xfa, 0xf1, 0xfd, 0x26, 0x48, 0x7a, 0xbf, 0x1e, 0x41, 0x06, 0x22, + 0x16, 0x5e, 0x87, 0x33, 0xc9, 0xf0, 0x86, 0x09, 0xf5, 0x46, 0x42, 0x3d, 0x13, 0xba, 0xfe, 0x6e, + 0xc4, 0x22, 0xa6, 0xe2, 0xea, 0xc5, 0x3f, 0x0d, 0x59, 0xbf, 0x1d, 0x31, 0x16, 0x25, 0x50, 0x57, + 0x4f, 0xcd, 0xfc, 0xa8, 0x4e, 0xb3, 0xb3, 0xbe, 0x2b, 0x50, 0x74, 0x44, 0x63, 0xf4, 0x83, 0x71, + 0x55, 0x26, 0x51, 0x61, 0xce, 0xa9, 0x8c, 0x59, 0xd6, 0xf7, 0xeb, 0xe8, 0x7a, 0x93, 0x0a, 0x18, + 0x68, 0x0d, 0x58, 0xdc, 0xf7, 0x7b, 0xd3, 0x72, 0x4a, 0x59, 0x98, 0x27, 0x40, 0x38, 0xcb, 0x25, + 0x98, 0xf8, 0x7b, 0xd3, 0xe2, 0xe5, 0xa9, 0x8e, 0x72, 0x7b, 0xb3, 0xa8, 0xbc, 0x4f, 0x39, 0x4d, + 0x05, 0x7e, 0x6e, 0xa1, 0x9b, 0x45, 0x2c, 0x09, 0x38, 0x28, 0x61, 0xe4, 0x08, 0xc0, 0xb6, 0xaa, + 0xa5, 0xda, 0xc2, 0xd6, 0x6d, 0xcf, 0xe4, 0x52, 0xa8, 0xeb, 0x97, 0xc7, 0xdb, 0x61, 0x71, 0xd6, + 0x78, 0xf4, 0xa2, 0xeb, 0xcc, 0xf4, 0xba, 0x8e, 0x7d, 0x46, 0xd3, 0xe4, 0x53, 0xf7, 0x35, 0x06, + 0xf7, 0xd7, 0x57, 0x4e, 0x2d, 0x8a, 0x65, 0x2b, 0x6f, 0x7a, 0x01, 0x4b, 0x4d, 0x51, 0xcc, 0xcf, + 0xa6, 0x08, 0xdb, 0x75, 0x79, 0xd6, 0x01, 0xa1, 0xc8, 0x84, 0xbf, 0x5c, 0xe0, 0x77, 0x0c, 0x7c, + 0x0f, 0x00, 0x1f, 0xa3, 0x15, 0x49, 0xdb, 0xc0, 0x0b, 0x2a, 0xd2, 0x51, 0x4a, 0xed, 0xd9, 0xaa, + 0x55, 0x5b, 0xd8, 0xfa, 0xd0, 0x9b, 0xd2, 0x3a, 0xef, 0xb0, 0x00, 0xed, 0x01, 0xe8, 0xe4, 0x1a, + 0x8e, 0x51, 0xb9, 0xa6, 0x55, 0x4e, 0x52, 0xba, 0xfe, 0x92, 0x1c, 0x03, 0xe0, 0x67, 0x68, 0x8d, + 0xe6, 0xb2, 0xc5, 0x78, 0xfc, 0x03, 0x84, 0xe4, 0xfb, 0x9c, 0x49, 0x20, 0x21, 0x64, 0x2c, 0x15, + 0x76, 0xa9, 0x5a, 0xaa, 0xcd, 0x37, 0xdc, 0x5e, 0xd7, 0xa9, 0x68, 0xb6, 0x2b, 0x02, 0x5d, 0x7f, + 0x75, 0xe8, 0x79, 0x5a, 0x38, 0x76, 0xb5, 0xfd, 0x55, 0x09, 0x2d, 0x7e, 0xa1, 0xa7, 0xf0, 0x40, + 0x52, 0x09, 0xb8, 0x8a, 0x16, 0x33, 0x38, 0x95, 0x44, 0x15, 0x2f, 0x0e, 0x6d, 0xab, 0x6a, 0xd5, + 0xe6, 0x7c, 0x54, 0xd8, 0xf6, 0x19, 0x4b, 0xbe, 0x0c, 0xf1, 0x36, 0x2a, 0x8f, 0x25, 0x7f, 0x77, + 0x6a, 0xf2, 0x26, 0xe9, 0xb9, 0x22, 0x69, 0xdf, 0x00, 0xf1, 0x13, 0xb4, 0xa0, 0xf8, 0xd5, 0x90, + 0xe8, 0x2c, 0x16, 0xb6, 0x6a, 0x53, 0x79, 0xbe, 0x56, 0x63, 0xe5, 0x17, 0x00, 0x43, 0x86, 0x8a, + 0x30, 0x65, 0x10, 0xf8, 0x3b, 0x84, 0x07, 0x75, 0x14, 0x44, 0x72, 0x1a, 0xb4, 0x81, 0xdb, 0x73, + 0x4a, 0xdf, 0xe6, 0xb5, 0x9a, 0x23, 0x0e, 0x35, 0xc8, 0x5f, 0x91, 0x13, 0x16, 0xfc, 0x15, 0x5a, + 0x54, 0x6a, 0x8f, 0x59, 0x92, 0xa7, 0x20, 0xec, 0x1b, 0x4a, 0xee, 0xfb, 0xd3, 0xd3, 0x66, 0x2c, + 0xf9, 0x46, 0xc5, 0xfb, 0x2a, 0x55, 0xfd, 0x5f, 0xe0, 0x0e, 0x5a, 0x57, 0x1d, 0x21, 0x1d, 0x1a, + 0x73, 0x32, 0xec, 0xbd, 0x90, 0x8c, 0x83, 0x5d, 0x56, 0xcc, 0xde, 0x54, 0x66, 0xd5, 0xb8, 0x7d, + 0x1a, 0xf3, 0xbe, 0x72, 0x53, 0x8e, 0x5b, 0xe1, 0xa4, 0xe3, 0xa0, 0xe0, 0x74, 0x7f, 0x2c, 0xa3, + 0xa5, 0xf1, 0x09, 0xc4, 0x4d, 0x74, 0x33, 0x84, 0x23, 0x9a, 0x27, 0x72, 0xa8, 0x40, 0x35, 0x7a, + 0xbe, 0xf1, 0xa0, 0xe0, 0xfa, 0xb3, 0xeb, 0x6c, 0xe8, 0xa5, 0x10, 0x61, 0xdb, 0x8b, 0x59, 0x3d, + 0xa5, 0xb2, 0xe5, 0x3d, 0x82, 0x88, 0x06, 0x67, 0xbb, 0x10, 0x9c, 0x77, 0x9d, 0xe5, 0x5d, 0x8d, + 0xef, 0x13, 0xfb, 0xcb, 0xe1, 0xb8, 0x01, 0xff, 0x6c, 0x21, 0x75, 0x9e, 0x8d, 0xe4, 0x18, 0xc6, + 0x42, 0xf2, 0xb8, 0x99, 0x17, 0xfb, 0x64, 0x66, 0xe7, 0xb3, 0x6b, 0xf5, 0x66, 0x77, 0x04, 0xb8, + 0x0f, 0x3c, 0x80, 0x4c, 0xd2, 0x08, 0x1a, 0xd5, 0x42, 0xeb, 0x79, 0xd7, 0xb1, 0x9f, 0x88, 0x94, + 0x5d, 0x16, 0xeb, 0xdb, 0xec, 0x0a, 0x0f, 0xfe, 0xc5, 0x42, 0x4e, 0xc6, 0x32, 0x32, 0x4d, 0x62, + 0xe9, 0xff, 0x4b, 0xbc, 0x6b, 0x24, 0x6e, 0x3c, 0x66, 0xd9, 0x95, 0x2a, 0x37, 0xb2, 0xab, 0x9d, + 0x78, 0x07, 0x2d, 0xd3, 0x30, 0x8d, 0x33, 0x42, 0xc3, 0x90, 0x83, 0x10, 0x20, 0xec, 0x39, 0xb5, + 0xf4, 0xeb, 0xbd, 0xae, 0x73, 0xcb, 0x2c, 0xfd, 0x78, 0x80, 0xeb, 0x2f, 0x29, 0xcb, 0x76, 0xdf, + 0x80, 0x7f, 0xb3, 0xd0, 0x83, 0x80, 0xa5, 0x69, 0x9e, 0xc5, 0xf2, 0x4c, 0xaf, 0xb6, 0x9e, 0x42, + 0xc9, 0x88, 0x38, 0xa1, 0x1d, 0x52, 0x94, 0xe2, 0xa4, 0x15, 0x4b, 0x48, 0x62, 0x21, 0x21, 0x24, + 0x54, 0x08, 0x90, 0x82, 0x48, 0x66, 0xdf, 0x50, 0x63, 0xb1, 0xdd, 0xeb, 0x3a, 0x0f, 0xf5, 0xcb, + 0xfe, 0x1b, 0x8f, 0xeb, 0x7b, 0x03, 0x60, 0xb1, 0x1b, 0x6a, 0x8a, 0x0f, 0xd9, 0xc1, 0x09, 0xed, + 0x3c, 0x66, 0xd9, 0xb7, 0x43, 0xc8, 0xb6, 0x42, 0x1c, 0x32, 0x7c, 0x88, 0x56, 0x39, 0x84, 0x79, + 0x00, 0xa1, 0xea, 0xcc, 0x80, 0x55, 0x2d, 0xc9, 0x7c, 0xa3, 0xda, 0xeb, 0x3a, 0x77, 0xb4, 0xa2, + 0x4b, 0xc3, 0x5c, 0xff, 0x1d, 0x63, 0xdf, 0x03, 0x18, 0xf0, 0xbb, 0x7f, 0x5b, 0xa8, 0x32, 0xbd, + 0x67, 0xf8, 0x08, 0x2d, 0x0b, 0x49, 0xdb, 0x71, 0x16, 0x11, 0x0e, 0x27, 0x94, 0x87, 0xc2, 0xec, + 0xc6, 0xc3, 0x6b, 0xec, 0xc6, 0xb0, 0x29, 0x13, 0x1c, 0xae, 0xbf, 0x64, 0x2c, 0xbe, 0x36, 0xe0, + 0x00, 0x2d, 0x8d, 0xd7, 0x52, 0xed, 0xc4, 0x7c, 0xe3, 0xf3, 0xeb, 0xbd, 0x66, 0xf5, 0xb2, 0x76, + 0xb8, 0xfe, 0xdb, 0x63, 0x65, 0x76, 0x7f, 0x9f, 0x45, 0x2b, 0x93, 0x47, 0x1c, 0xf6, 0xd1, 0xea, + 0xe8, 0x69, 0xc9, 0x88, 0x50, 0x8f, 0xe2, 0xcd, 0x37, 0xac, 0x3e, 0x6a, 0xf0, 0xf0, 0x88, 0x64, + 0x07, 0x1a, 0x8a, 0x09, 0xba, 0x33, 0xce, 0xf9, 0x5a, 0x6e, 0xd7, 0xa2, 0xb6, 0x47, 0xa8, 0x77, + 0x46, 0x33, 0xc1, 0x6d, 0xf4, 0x5e, 0x0b, 0xe2, 0xa8, 0x25, 0x09, 0x0d, 0x02, 0x96, 0x67, 0xb2, + 0x28, 0xae, 0x90, 0x94, 0x4b, 0x41, 0x8e, 0x38, 0x4b, 0xd5, 0xba, 0x96, 0x1a, 0xb5, 0x5e, 0xd7, + 0xb9, 0xa7, 0x4b, 0x33, 0x35, 0xdc, 0xf5, 0xd7, 0xb5, 0x7f, 0x7b, 0xe0, 0x3e, 0x50, 0xde, 0xbd, + 0xc2, 0xf9, 0xdc, 0x42, 0x68, 0x78, 0x84, 0xe3, 0x35, 0xf4, 0xd6, 0xf8, 0x7d, 0x58, 0xee, 0xe8, + 0xbb, 0x30, 0x31, 0x17, 0x99, 0xbe, 0x1a, 0xde, 0x9c, 0xe4, 0x47, 0x45, 0x92, 0xff, 0xea, 0x2b, + 0x04, 0x0d, 0x6f, 0x8f, 0xc6, 0xd3, 0x17, 0xe7, 0x15, 0xeb, 0xe5, 0x79, 0xc5, 0xfa, 0xeb, 0xbc, + 0x62, 0xfd, 0x74, 0x51, 0x99, 0x79, 0x79, 0x51, 0x99, 0xf9, 0xe3, 0xa2, 0x32, 0xf3, 0xec, 0x93, + 0x11, 0x3e, 0x73, 0x5c, 0x6d, 0x26, 0xb4, 0x29, 0xfa, 0x0f, 0xf5, 0xe3, 0xad, 0xad, 0xfa, 0xe9, + 0xd8, 0xf7, 0x97, 0x7a, 0x49, 0xb3, 0xac, 0xbe, 0xbd, 0x3e, 0xfe, 0x27, 0x00, 0x00, 0xff, 0xff, + 0xf3, 0x27, 0x68, 0xcc, 0xa7, 0x0a, 0x00, 0x00, +} + +func (m *Params) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Params) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Params) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.AuthorizedQuoteDenoms) > 0 { + for iNdEx := len(m.AuthorizedQuoteDenoms) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.AuthorizedQuoteDenoms[iNdEx]) + copy(dAtA[i:], m.AuthorizedQuoteDenoms[iNdEx]) + i = encodeVarintGenesis(dAtA, i, uint64(len(m.AuthorizedQuoteDenoms[iNdEx]))) + i-- + dAtA[i] = 0x1a + } + } + { + size, err := m.TakerFeeParams.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + if len(m.PoolCreationFee) > 0 { + for iNdEx := len(m.PoolCreationFee) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.PoolCreationFee[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *GenesisState) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *GenesisState) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GenesisState) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.DenomPairTakerFeeStore) > 0 { + for iNdEx := len(m.DenomPairTakerFeeStore) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.DenomPairTakerFeeStore[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x32 + } + } + if len(m.PoolVolumes) > 0 { + for iNdEx := len(m.PoolVolumes) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.PoolVolumes[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + } + } + if m.TakerFeesTracker != nil { + { + size, err := m.TakerFeesTracker.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + if len(m.PoolRoutes) > 0 { + for iNdEx := len(m.PoolRoutes) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.PoolRoutes[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + } + { + size, err := m.Params.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + if m.NextPoolId != 0 { + i = encodeVarintGenesis(dAtA, i, uint64(m.NextPoolId)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *TakerFeeParams) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *TakerFeeParams) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TakerFeeParams) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ReducedFeeWhitelist) > 0 { + for iNdEx := len(m.ReducedFeeWhitelist) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.ReducedFeeWhitelist[iNdEx]) + copy(dAtA[i:], m.ReducedFeeWhitelist[iNdEx]) + i = encodeVarintGenesis(dAtA, i, uint64(len(m.ReducedFeeWhitelist[iNdEx]))) + i-- + dAtA[i] = 0x32 + } + } + if len(m.CommunityPoolDenomToSwapNonWhitelistedAssetsTo) > 0 { + i -= len(m.CommunityPoolDenomToSwapNonWhitelistedAssetsTo) + copy(dAtA[i:], m.CommunityPoolDenomToSwapNonWhitelistedAssetsTo) + i = encodeVarintGenesis(dAtA, i, uint64(len(m.CommunityPoolDenomToSwapNonWhitelistedAssetsTo))) + i-- + dAtA[i] = 0x2a + } + if len(m.AdminAddresses) > 0 { + for iNdEx := len(m.AdminAddresses) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.AdminAddresses[iNdEx]) + copy(dAtA[i:], m.AdminAddresses[iNdEx]) + i = encodeVarintGenesis(dAtA, i, uint64(len(m.AdminAddresses[iNdEx]))) + i-- + dAtA[i] = 0x22 + } + } + { + size, err := m.NonOsmoTakerFeeDistribution.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + { + size, err := m.OsmoTakerFeeDistribution.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + { + size := m.DefaultTakerFee.Size() + i -= size + if _, err := m.DefaultTakerFee.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *TakerFeeDistributionPercentage) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *TakerFeeDistributionPercentage) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TakerFeeDistributionPercentage) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size := m.CommunityPool.Size() + i -= size + if _, err := m.CommunityPool.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + { + size := m.StakingRewards.Size() + i -= size + if _, err := m.StakingRewards.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *TakerFeesTracker) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *TakerFeesTracker) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TakerFeesTracker) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.HeightAccountingStartsFrom != 0 { + i = encodeVarintGenesis(dAtA, i, uint64(m.HeightAccountingStartsFrom)) + i-- + dAtA[i] = 0x18 + } + if len(m.TakerFeesToCommunityPool) > 0 { + for iNdEx := len(m.TakerFeesToCommunityPool) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.TakerFeesToCommunityPool[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if len(m.TakerFeesToStakers) > 0 { + for iNdEx := len(m.TakerFeesToStakers) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.TakerFeesToStakers[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *PoolVolume) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *PoolVolume) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *PoolVolume) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.PoolVolume) > 0 { + for iNdEx := len(m.PoolVolume) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.PoolVolume[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if m.PoolId != 0 { + i = encodeVarintGenesis(dAtA, i, uint64(m.PoolId)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func encodeVarintGenesis(dAtA []byte, offset int, v uint64) int { + offset -= sovGenesis(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *Params) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.PoolCreationFee) > 0 { + for _, e := range m.PoolCreationFee { + l = e.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + } + l = m.TakerFeeParams.Size() + n += 1 + l + sovGenesis(uint64(l)) + if len(m.AuthorizedQuoteDenoms) > 0 { + for _, s := range m.AuthorizedQuoteDenoms { + l = len(s) + n += 1 + l + sovGenesis(uint64(l)) + } + } + return n +} + +func (m *GenesisState) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.NextPoolId != 0 { + n += 1 + sovGenesis(uint64(m.NextPoolId)) + } + l = m.Params.Size() + n += 1 + l + sovGenesis(uint64(l)) + if len(m.PoolRoutes) > 0 { + for _, e := range m.PoolRoutes { + l = e.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + } + if m.TakerFeesTracker != nil { + l = m.TakerFeesTracker.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + if len(m.PoolVolumes) > 0 { + for _, e := range m.PoolVolumes { + l = e.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + } + if len(m.DenomPairTakerFeeStore) > 0 { + for _, e := range m.DenomPairTakerFeeStore { + l = e.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + } + return n +} + +func (m *TakerFeeParams) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.DefaultTakerFee.Size() + n += 1 + l + sovGenesis(uint64(l)) + l = m.OsmoTakerFeeDistribution.Size() + n += 1 + l + sovGenesis(uint64(l)) + l = m.NonOsmoTakerFeeDistribution.Size() + n += 1 + l + sovGenesis(uint64(l)) + if len(m.AdminAddresses) > 0 { + for _, s := range m.AdminAddresses { + l = len(s) + n += 1 + l + sovGenesis(uint64(l)) + } + } + l = len(m.CommunityPoolDenomToSwapNonWhitelistedAssetsTo) + if l > 0 { + n += 1 + l + sovGenesis(uint64(l)) + } + if len(m.ReducedFeeWhitelist) > 0 { + for _, s := range m.ReducedFeeWhitelist { + l = len(s) + n += 1 + l + sovGenesis(uint64(l)) + } + } + return n +} + +func (m *TakerFeeDistributionPercentage) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.StakingRewards.Size() + n += 1 + l + sovGenesis(uint64(l)) + l = m.CommunityPool.Size() + n += 1 + l + sovGenesis(uint64(l)) + return n +} + +func (m *TakerFeesTracker) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.TakerFeesToStakers) > 0 { + for _, e := range m.TakerFeesToStakers { + l = e.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + } + if len(m.TakerFeesToCommunityPool) > 0 { + for _, e := range m.TakerFeesToCommunityPool { + l = e.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + } + if m.HeightAccountingStartsFrom != 0 { + n += 1 + sovGenesis(uint64(m.HeightAccountingStartsFrom)) + } + return n +} + +func (m *PoolVolume) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.PoolId != 0 { + n += 1 + sovGenesis(uint64(m.PoolId)) + } + if len(m.PoolVolume) > 0 { + for _, e := range m.PoolVolume { + l = e.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + } + return n +} + +func sovGenesis(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozGenesis(x uint64) (n int) { + return sovGenesis(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *Params) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Params: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Params: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PoolCreationFee", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.PoolCreationFee = append(m.PoolCreationFee, types.Coin{}) + if err := m.PoolCreationFee[len(m.PoolCreationFee)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TakerFeeParams", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.TakerFeeParams.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AuthorizedQuoteDenoms", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.AuthorizedQuoteDenoms = append(m.AuthorizedQuoteDenoms, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenesis(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenesis + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *GenesisState) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: GenesisState: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GenesisState: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field NextPoolId", wireType) + } + m.NextPoolId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.NextPoolId |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Params", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Params.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PoolRoutes", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.PoolRoutes = append(m.PoolRoutes, ModuleRoute{}) + if err := m.PoolRoutes[len(m.PoolRoutes)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TakerFeesTracker", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.TakerFeesTracker == nil { + m.TakerFeesTracker = &TakerFeesTracker{} + } + if err := m.TakerFeesTracker.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PoolVolumes", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.PoolVolumes = append(m.PoolVolumes, &PoolVolume{}) + if err := m.PoolVolumes[len(m.PoolVolumes)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DenomPairTakerFeeStore", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.DenomPairTakerFeeStore = append(m.DenomPairTakerFeeStore, DenomPairTakerFee{}) + if err := m.DenomPairTakerFeeStore[len(m.DenomPairTakerFeeStore)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenesis(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenesis + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *TakerFeeParams) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: TakerFeeParams: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: TakerFeeParams: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DefaultTakerFee", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.DefaultTakerFee.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field OsmoTakerFeeDistribution", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.OsmoTakerFeeDistribution.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NonOsmoTakerFeeDistribution", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.NonOsmoTakerFeeDistribution.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AdminAddresses", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.AdminAddresses = append(m.AdminAddresses, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CommunityPoolDenomToSwapNonWhitelistedAssetsTo", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.CommunityPoolDenomToSwapNonWhitelistedAssetsTo = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ReducedFeeWhitelist", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ReducedFeeWhitelist = append(m.ReducedFeeWhitelist, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenesis(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenesis + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *TakerFeeDistributionPercentage) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: TakerFeeDistributionPercentage: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: TakerFeeDistributionPercentage: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field StakingRewards", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.StakingRewards.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CommunityPool", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.CommunityPool.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenesis(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenesis + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *TakerFeesTracker) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: TakerFeesTracker: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: TakerFeesTracker: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TakerFeesToStakers", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.TakerFeesToStakers = append(m.TakerFeesToStakers, types.Coin{}) + if err := m.TakerFeesToStakers[len(m.TakerFeesToStakers)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TakerFeesToCommunityPool", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.TakerFeesToCommunityPool = append(m.TakerFeesToCommunityPool, types.Coin{}) + if err := m.TakerFeesToCommunityPool[len(m.TakerFeesToCommunityPool)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field HeightAccountingStartsFrom", wireType) + } + m.HeightAccountingStartsFrom = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.HeightAccountingStartsFrom |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipGenesis(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenesis + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *PoolVolume) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: PoolVolume: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PoolVolume: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field PoolId", wireType) + } + m.PoolId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.PoolId |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PoolVolume", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.PoolVolume = append(m.PoolVolume, types.Coin{}) + if err := m.PoolVolume[len(m.PoolVolume)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenesis(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenesis + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipGenesis(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowGenesis + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowGenesis + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowGenesis + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthGenesis + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupGenesis + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthGenesis + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthGenesis = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowGenesis = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupGenesis = fmt.Errorf("proto: unexpected end of group") +) diff --git a/tests/interchaintest/osmosis/poolmanager/types/gov.go b/tests/interchaintest/osmosis/poolmanager/types/gov.go new file mode 100644 index 00000000..3c086e6d --- /dev/null +++ b/tests/interchaintest/osmosis/poolmanager/types/gov.go @@ -0,0 +1,68 @@ +package types + +import ( + "fmt" + "strings" + + govtypesv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" +) + +const ( + ProposalTypeDenomPairTakerFee = "DenomPairTakerFee" +) + +func init() { + govtypesv1.RegisterProposalType(ProposalTypeDenomPairTakerFee) +} + +var ( + _ govtypesv1.Content = &DenomPairTakerFeeProposal{} +) + +// NewDenomPairTakerFeeProposal returns a new instance of a denom pair taker fee proposal struct. +func NewDenomPairTakerFeeProposal(title, description string, records []DenomPairTakerFee) govtypesv1.Content { + return &DenomPairTakerFeeProposal{ + Title: title, + Description: description, + DenomPairTakerFee: records, + } +} + +func (p *DenomPairTakerFeeProposal) GetTitle() string { return p.Title } + +// GetDescription gets the description of the proposal +func (p *DenomPairTakerFeeProposal) GetDescription() string { return p.Description } + +// ProposalRoute returns the router key for the proposal +func (p *DenomPairTakerFeeProposal) ProposalRoute() string { return RouterKey } + +// ProposalType returns the type of the proposal +func (p *DenomPairTakerFeeProposal) ProposalType() string { + return ProposalTypeDenomPairTakerFee +} + +// ValidateBasic validates a governance proposal's abstract and basic contents +func (p *DenomPairTakerFeeProposal) ValidateBasic() error { + err := govtypesv1.ValidateAbstract(p) + if err != nil { + return err + } + + return validateDenomPairTakerFees(p.DenomPairTakerFee) +} + +// String returns a string containing the denom pair taker fee proposal. +func (p DenomPairTakerFeeProposal) String() string { + recordsStr := "" + for _, record := range p.DenomPairTakerFee { + recordsStr = recordsStr + fmt.Sprintf("(Denom0: %s, Denom1: %s, TakerFee: %s) ", record.Denom0, record.Denom1, record.TakerFee.String()) + } + + var b strings.Builder + b.WriteString(fmt.Sprintf(`Denom Pair Taker Fee Proposal: +Title: %s +Description: %s +Records: %s +`, p.Title, p.Description, recordsStr)) + return b.String() +} diff --git a/tests/interchaintest/osmosis/poolmanager/types/gov.pb.go b/tests/interchaintest/osmosis/poolmanager/types/gov.pb.go new file mode 100644 index 00000000..b8bcce01 --- /dev/null +++ b/tests/interchaintest/osmosis/poolmanager/types/gov.pb.go @@ -0,0 +1,418 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: osmosis/poolmanager/v1beta1/gov.proto + +package types + +import ( + fmt "fmt" + _ "github.com/cosmos/gogoproto/gogoproto" + proto "github.com/cosmos/gogoproto/proto" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// DenomPairTakerFeeProposal is a type for adding/removing a custom taker fee(s) +// for one or more denom pairs. +type DenomPairTakerFeeProposal struct { + Title string `protobuf:"bytes,1,opt,name=title,proto3" json:"title,omitempty"` + Description string `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty"` + DenomPairTakerFee []DenomPairTakerFee `protobuf:"bytes,3,rep,name=denom_pair_taker_fee,json=denomPairTakerFee,proto3" json:"denom_pair_taker_fee"` +} + +func (m *DenomPairTakerFeeProposal) Reset() { *m = DenomPairTakerFeeProposal{} } +func (*DenomPairTakerFeeProposal) ProtoMessage() {} +func (*DenomPairTakerFeeProposal) Descriptor() ([]byte, []int) { + return fileDescriptor_c95b3c1cda2a8632, []int{0} +} +func (m *DenomPairTakerFeeProposal) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *DenomPairTakerFeeProposal) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_DenomPairTakerFeeProposal.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *DenomPairTakerFeeProposal) XXX_Merge(src proto.Message) { + xxx_messageInfo_DenomPairTakerFeeProposal.Merge(m, src) +} +func (m *DenomPairTakerFeeProposal) XXX_Size() int { + return m.Size() +} +func (m *DenomPairTakerFeeProposal) XXX_DiscardUnknown() { + xxx_messageInfo_DenomPairTakerFeeProposal.DiscardUnknown(m) +} + +var xxx_messageInfo_DenomPairTakerFeeProposal proto.InternalMessageInfo + +func init() { + proto.RegisterType((*DenomPairTakerFeeProposal)(nil), "osmosis.poolmanager.v1beta1.DenomPairTakerFeeProposal") +} + +func init() { + proto.RegisterFile("osmosis/poolmanager/v1beta1/gov.proto", fileDescriptor_c95b3c1cda2a8632) +} + +var fileDescriptor_c95b3c1cda2a8632 = []byte{ + // 288 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0xcd, 0x2f, 0xce, 0xcd, + 0x2f, 0xce, 0x2c, 0xd6, 0x2f, 0xc8, 0xcf, 0xcf, 0xc9, 0x4d, 0xcc, 0x4b, 0x4c, 0x4f, 0x2d, 0xd2, + 0x2f, 0x33, 0x4c, 0x4a, 0x2d, 0x49, 0x34, 0xd4, 0x4f, 0xcf, 0x2f, 0xd3, 0x2b, 0x28, 0xca, 0x2f, + 0xc9, 0x17, 0x92, 0x86, 0x2a, 0xd3, 0x43, 0x52, 0xa6, 0x07, 0x55, 0x26, 0x25, 0x92, 0x9e, 0x9f, + 0x9e, 0x0f, 0x56, 0xa7, 0x0f, 0x62, 0x41, 0xb4, 0x48, 0xa9, 0xe0, 0x33, 0xb9, 0xa4, 0x02, 0xa2, + 0x4a, 0xe9, 0x08, 0x23, 0x97, 0xa4, 0x4b, 0x6a, 0x5e, 0x7e, 0x6e, 0x40, 0x62, 0x66, 0x51, 0x48, + 0x62, 0x76, 0x6a, 0x91, 0x5b, 0x6a, 0x6a, 0x40, 0x51, 0x7e, 0x41, 0x7e, 0x71, 0x62, 0x8e, 0x90, + 0x08, 0x17, 0x6b, 0x49, 0x66, 0x49, 0x4e, 0xaa, 0x04, 0xa3, 0x02, 0xa3, 0x06, 0x67, 0x10, 0x84, + 0x23, 0xa4, 0xc0, 0xc5, 0x9d, 0x92, 0x5a, 0x9c, 0x5c, 0x94, 0x59, 0x50, 0x92, 0x99, 0x9f, 0x27, + 0xc1, 0x04, 0x96, 0x43, 0x16, 0x12, 0x4a, 0xe5, 0x12, 0x49, 0x01, 0x19, 0x1a, 0x5f, 0x90, 0x98, + 0x59, 0x14, 0x5f, 0x02, 0x32, 0x36, 0x3e, 0x2d, 0x35, 0x55, 0x82, 0x59, 0x81, 0x59, 0x83, 0xdb, + 0x48, 0x4f, 0x0f, 0x8f, 0x6f, 0xf4, 0x30, 0x5c, 0xe3, 0xc4, 0x72, 0xe2, 0x9e, 0x3c, 0x43, 0x90, + 0x60, 0x0a, 0xba, 0x84, 0x15, 0x47, 0xc7, 0x02, 0x79, 0x86, 0x19, 0x0b, 0xe4, 0x19, 0x9c, 0x02, + 0x4f, 0x3c, 0x92, 0x63, 0xbc, 0xf0, 0x48, 0x8e, 0xf1, 0xc1, 0x23, 0x39, 0xc6, 0x09, 0x8f, 0xe5, + 0x18, 0x2e, 0x3c, 0x96, 0x63, 0xb8, 0xf1, 0x58, 0x8e, 0x21, 0xca, 0x3c, 0x3d, 0xb3, 0x24, 0xa3, + 0x34, 0x49, 0x2f, 0x39, 0x3f, 0x57, 0x1f, 0x6a, 0xad, 0x6e, 0x4e, 0x62, 0x52, 0x31, 0x8c, 0xa3, + 0x5f, 0x66, 0x64, 0xa4, 0x5f, 0x81, 0x12, 0x48, 0x25, 0x95, 0x05, 0xa9, 0xc5, 0x49, 0x6c, 0xe0, + 0x00, 0x32, 0x06, 0x04, 0x00, 0x00, 0xff, 0xff, 0xe0, 0x79, 0xbd, 0x9a, 0xa2, 0x01, 0x00, 0x00, +} + +func (m *DenomPairTakerFeeProposal) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *DenomPairTakerFeeProposal) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *DenomPairTakerFeeProposal) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.DenomPairTakerFee) > 0 { + for iNdEx := len(m.DenomPairTakerFee) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.DenomPairTakerFee[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGov(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + } + if len(m.Description) > 0 { + i -= len(m.Description) + copy(dAtA[i:], m.Description) + i = encodeVarintGov(dAtA, i, uint64(len(m.Description))) + i-- + dAtA[i] = 0x12 + } + if len(m.Title) > 0 { + i -= len(m.Title) + copy(dAtA[i:], m.Title) + i = encodeVarintGov(dAtA, i, uint64(len(m.Title))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintGov(dAtA []byte, offset int, v uint64) int { + offset -= sovGov(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *DenomPairTakerFeeProposal) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Title) + if l > 0 { + n += 1 + l + sovGov(uint64(l)) + } + l = len(m.Description) + if l > 0 { + n += 1 + l + sovGov(uint64(l)) + } + if len(m.DenomPairTakerFee) > 0 { + for _, e := range m.DenomPairTakerFee { + l = e.Size() + n += 1 + l + sovGov(uint64(l)) + } + } + return n +} + +func sovGov(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozGov(x uint64) (n int) { + return sovGov(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *DenomPairTakerFeeProposal) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGov + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: DenomPairTakerFeeProposal: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: DenomPairTakerFeeProposal: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Title", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGov + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGov + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGov + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Title = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Description", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGov + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGov + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGov + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Description = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DenomPairTakerFee", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGov + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGov + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGov + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.DenomPairTakerFee = append(m.DenomPairTakerFee, DenomPairTakerFee{}) + if err := m.DenomPairTakerFee[len(m.DenomPairTakerFee)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGov(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGov + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipGov(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowGov + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowGov + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowGov + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthGov + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupGov + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthGov + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthGov = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowGov = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupGov = fmt.Errorf("proto: unexpected end of group") +) diff --git a/tests/interchaintest/osmosis/poolmanager/types/keys.go b/tests/interchaintest/osmosis/poolmanager/types/keys.go new file mode 100644 index 00000000..e4d1c046 --- /dev/null +++ b/tests/interchaintest/osmosis/poolmanager/types/keys.go @@ -0,0 +1,110 @@ +package types + +import ( + "fmt" + "sort" + "strconv" + "strings" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/gogoproto/proto" +) + +const ( + ModuleName = "poolmanager" + KeySeparator = "|" + + StoreKey = ModuleName + + RouterKey = ModuleName +) + +var ( + // KeyNextGlobalPoolId defines key to store the next Pool ID to be used. + KeyNextGlobalPoolId = []byte{0x01} + + // SwapModuleRouterPrefix defines prefix to store pool id to swap module mappings. + SwapModuleRouterPrefix = []byte{0x02} + + // KeyPoolVolumePrefix defines prefix to store pool volume. + KeyPoolVolumePrefix = []byte{0x03} + + // DenomTradePairPrefix defines prefix to store denom trade pair for taker fee. + DenomTradePairPrefix = []byte{0x04} + + // KeyTakerFeeStakersProtoRev defines key to store the taker fee for stakers tracker. + // Deprecated: Now utilizes KeyTakerFeeStakersProtoRevArray. + KeyTakerFeeStakersProtoRev = []byte{0x05} + + // KeyTakerFeeCommunityPoolProtoRev defines key to store the taker fee for community pool tracker. + // Deprecated: Now utilizes KeyTakerFeeCommunityPoolProtoRevArray. + KeyTakerFeeCommunityPoolProtoRev = []byte{0x06} + + // KeyTakerFeeProtoRevAccountingHeight defines key to store the accounting height for the above taker fee trackers. + KeyTakerFeeProtoRevAccountingHeight = []byte{0x07} + + // KeyTakerFeeStakersProtoRevArray defines key to store the taker fee for stakers tracker coin array. + KeyTakerFeeStakersProtoRevArray = []byte{0x08} + + // KeyTakerFeeCommunityPoolProtoRevArray defines key to store the taker fee for community pool tracker coin array. + KeyTakerFeeCommunityPoolProtoRevArray = []byte{0x09} +) + +// ModuleRouteToBytes serializes moduleRoute to bytes. +func FormatModuleRouteKey(poolId uint64) []byte { + // Estimate the length of the string representation of poolId + // 11 is a very safe upper bound, (99,999,999,999) pools, and is a 12 byte allocation + length := 11 + result := make([]byte, 1, 1+length) + result[0] = SwapModuleRouterPrefix[0] + // Write poolId into the byte slice starting after the prefix + written := strconv.AppendUint(result[1:], poolId, 10) + + // Slice result to the actual length used + return result[:1+len(written)] +} + +// FormatDenomTradePairKey serializes denom trade pair to bytes. +// Denom trade pair is automatically sorted lexicographically. +func FormatDenomTradePairKey(denom0, denom1 string) []byte { + denoms := []string{denom0, denom1} + sort.Strings(denoms) + return []byte(fmt.Sprintf("%s%s%s%s%s", DenomTradePairPrefix, KeySeparator, denoms[0], KeySeparator, denoms[1])) +} + +// ParseModuleRouteFromBz parses the raw bytes into ModuleRoute. +// Returns error if fails to parse or if the bytes are empty. +func ParseModuleRouteFromBz(bz []byte) (ModuleRoute, error) { + moduleRoute := ModuleRoute{} + err := proto.Unmarshal(bz, &moduleRoute) + if err != nil { + return ModuleRoute{}, err + } + return moduleRoute, err +} + +// KeyPoolVolume returns the key for the pool volume corresponding to the given poolId. +func KeyPoolVolume(poolId uint64) []byte { + return []byte(fmt.Sprintf("%s%s%d%s", KeyPoolVolumePrefix, KeySeparator, poolId, KeySeparator)) +} + +// ParseDenomTradePairKey parses the raw bytes of the DenomTradePairKey into a denom trade pair. +func ParseDenomTradePairKey(key []byte) (denom0, denom1 string, err error) { + keyStr := string(key) + parts := strings.Split(keyStr, KeySeparator) + + denom0 = parts[1] + denom1 = parts[2] + + err = sdk.ValidateDenom(denom0) + if err != nil { + return "", "", err + } + + err = sdk.ValidateDenom(denom1) + if err != nil { + return "", "", err + } + + return denom0, denom1, nil +} diff --git a/tests/interchaintest/osmosis/poolmanager/types/module_route.pb.go b/tests/interchaintest/osmosis/poolmanager/types/module_route.pb.go new file mode 100644 index 00000000..15b57ce6 --- /dev/null +++ b/tests/interchaintest/osmosis/poolmanager/types/module_route.pb.go @@ -0,0 +1,391 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: osmosis/poolmanager/v1beta1/module_route.proto + +package types + +import ( + fmt "fmt" + _ "github.com/cosmos/gogoproto/gogoproto" + proto "github.com/cosmos/gogoproto/proto" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// PoolType is an enumeration of all supported pool types. +type PoolType int32 + +const ( + // Balancer is the standard xy=k curve. Its pool model is defined in x/gamm. + Balancer PoolType = 0 + // Stableswap is the Solidly cfmm stable swap curve. Its pool model is defined + // in x/gamm. + Stableswap PoolType = 1 + // Concentrated is the pool model specific to concentrated liquidity. It is + // defined in x/concentrated-liquidity. + Concentrated PoolType = 2 + // CosmWasm is the pool model specific to CosmWasm. It is defined in + // x/cosmwasmpool. + CosmWasm PoolType = 3 +) + +var PoolType_name = map[int32]string{ + 0: "Balancer", + 1: "Stableswap", + 2: "Concentrated", + 3: "CosmWasm", +} + +var PoolType_value = map[string]int32{ + "Balancer": 0, + "Stableswap": 1, + "Concentrated": 2, + "CosmWasm": 3, +} + +func (x PoolType) String() string { + return proto.EnumName(PoolType_name, int32(x)) +} + +func (PoolType) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_96bfcc7b6d387cee, []int{0} +} + +// ModuleRouter defines a route encapsulating pool type. +// It is used as the value of a mapping from pool id to the pool type, +// allowing the pool manager to know which module to route swaps to given the +// pool id. +type ModuleRoute struct { + // pool_type specifies the type of the pool + PoolType PoolType `protobuf:"varint,1,opt,name=pool_type,json=poolType,proto3,enum=osmosis.poolmanager.v1beta1.PoolType" json:"pool_type,omitempty"` + PoolId uint64 `protobuf:"varint,2,opt,name=pool_id,json=poolId,proto3" json:"pool_id,omitempty" yaml:"pool_id"` +} + +func (m *ModuleRoute) Reset() { *m = ModuleRoute{} } +func (m *ModuleRoute) String() string { return proto.CompactTextString(m) } +func (*ModuleRoute) ProtoMessage() {} +func (*ModuleRoute) Descriptor() ([]byte, []int) { + return fileDescriptor_96bfcc7b6d387cee, []int{0} +} +func (m *ModuleRoute) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ModuleRoute) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ModuleRoute.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ModuleRoute) XXX_Merge(src proto.Message) { + xxx_messageInfo_ModuleRoute.Merge(m, src) +} +func (m *ModuleRoute) XXX_Size() int { + return m.Size() +} +func (m *ModuleRoute) XXX_DiscardUnknown() { + xxx_messageInfo_ModuleRoute.DiscardUnknown(m) +} + +var xxx_messageInfo_ModuleRoute proto.InternalMessageInfo + +func (m *ModuleRoute) GetPoolType() PoolType { + if m != nil { + return m.PoolType + } + return Balancer +} + +func (m *ModuleRoute) GetPoolId() uint64 { + if m != nil { + return m.PoolId + } + return 0 +} + +func init() { + proto.RegisterEnum("osmosis.poolmanager.v1beta1.PoolType", PoolType_name, PoolType_value) + proto.RegisterType((*ModuleRoute)(nil), "osmosis.poolmanager.v1beta1.ModuleRoute") +} + +func init() { + proto.RegisterFile("osmosis/poolmanager/v1beta1/module_route.proto", fileDescriptor_96bfcc7b6d387cee) +} + +var fileDescriptor_96bfcc7b6d387cee = []byte{ + // 320 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x90, 0x41, 0x4b, 0x32, 0x41, + 0x1c, 0xc6, 0x77, 0x7c, 0xc5, 0xd7, 0x26, 0x91, 0x65, 0x89, 0x10, 0x83, 0x51, 0x84, 0x40, 0x82, + 0x66, 0xd0, 0x0e, 0x41, 0xc7, 0xf5, 0xd4, 0xa1, 0x28, 0x0b, 0x82, 0x2e, 0x32, 0xeb, 0x0e, 0x9b, + 0x30, 0xb3, 0xff, 0x61, 0x67, 0xb4, 0xbc, 0x76, 0xea, 0xd8, 0x77, 0xe8, 0xcb, 0x78, 0xf4, 0xd8, + 0x49, 0x42, 0xbf, 0x41, 0x9f, 0x20, 0x66, 0x5b, 0xa1, 0x2e, 0xdd, 0x9e, 0x61, 0x7e, 0xcf, 0x0f, + 0xfe, 0x0f, 0xa6, 0x60, 0x14, 0x98, 0x89, 0x61, 0x1a, 0x40, 0x2a, 0x9e, 0xf2, 0x44, 0x64, 0x6c, + 0xd6, 0x8b, 0x84, 0xe5, 0x3d, 0xa6, 0x20, 0x9e, 0x4a, 0x31, 0xca, 0x60, 0x6a, 0x05, 0xd5, 0x19, + 0x58, 0x08, 0x0e, 0x0a, 0x9e, 0xfe, 0xe0, 0x69, 0xc1, 0x37, 0xf7, 0x12, 0x48, 0x20, 0xe7, 0x98, + 0x4b, 0xdf, 0x95, 0xce, 0x33, 0xc2, 0xbb, 0x17, 0xb9, 0x69, 0xe8, 0x44, 0x41, 0x88, 0x77, 0x5c, + 0x79, 0x64, 0xe7, 0x5a, 0x34, 0x50, 0x1b, 0x75, 0xeb, 0xfd, 0x43, 0xfa, 0x87, 0x96, 0x5e, 0x01, + 0xc8, 0xdb, 0xb9, 0x16, 0xc3, 0xaa, 0x2e, 0x52, 0xc0, 0xf0, 0xff, 0xdc, 0x31, 0x89, 0x1b, 0xa5, + 0x36, 0xea, 0x96, 0xc3, 0xfd, 0xc5, 0xaa, 0x85, 0x3e, 0x57, 0xad, 0xfa, 0x9c, 0x2b, 0x79, 0xd6, + 0x29, 0x3e, 0x3b, 0xc3, 0x8a, 0x4b, 0xe7, 0xf1, 0xd1, 0x25, 0xae, 0x6e, 0x35, 0x41, 0x0d, 0x57, + 0x43, 0x2e, 0x79, 0x3a, 0x16, 0x99, 0xef, 0x05, 0x75, 0x8c, 0x6f, 0x2c, 0x8f, 0xa4, 0x30, 0x8f, + 0x5c, 0xfb, 0x28, 0xf0, 0x71, 0x6d, 0x00, 0xe9, 0x58, 0xa4, 0x36, 0xe3, 0x56, 0xc4, 0x7e, 0xc9, + 0xf1, 0x03, 0x30, 0xea, 0x8e, 0x1b, 0xe5, 0xff, 0x6b, 0x96, 0x5f, 0xde, 0x88, 0x17, 0x5e, 0x2f, + 0xd6, 0x04, 0x2d, 0xd7, 0x04, 0x7d, 0xac, 0x09, 0x7a, 0xdd, 0x10, 0x6f, 0xb9, 0x21, 0xde, 0xfb, + 0x86, 0x78, 0xf7, 0xa7, 0xc9, 0xc4, 0x3e, 0x4c, 0x23, 0x3a, 0x06, 0xc5, 0x8a, 0xab, 0x8e, 0x25, + 0x8f, 0xcc, 0xf6, 0xc1, 0x66, 0xfd, 0x3e, 0x7b, 0xfa, 0xb5, 0xb7, 0x5b, 0xc2, 0x44, 0x95, 0x7c, + 0xae, 0x93, 0xaf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x37, 0x0b, 0xbc, 0x2a, 0x93, 0x01, 0x00, 0x00, +} + +func (m *ModuleRoute) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ModuleRoute) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ModuleRoute) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.PoolId != 0 { + i = encodeVarintModuleRoute(dAtA, i, uint64(m.PoolId)) + i-- + dAtA[i] = 0x10 + } + if m.PoolType != 0 { + i = encodeVarintModuleRoute(dAtA, i, uint64(m.PoolType)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func encodeVarintModuleRoute(dAtA []byte, offset int, v uint64) int { + offset -= sovModuleRoute(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *ModuleRoute) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.PoolType != 0 { + n += 1 + sovModuleRoute(uint64(m.PoolType)) + } + if m.PoolId != 0 { + n += 1 + sovModuleRoute(uint64(m.PoolId)) + } + return n +} + +func sovModuleRoute(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozModuleRoute(x uint64) (n int) { + return sovModuleRoute(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *ModuleRoute) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowModuleRoute + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ModuleRoute: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ModuleRoute: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field PoolType", wireType) + } + m.PoolType = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowModuleRoute + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.PoolType |= PoolType(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field PoolId", wireType) + } + m.PoolId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowModuleRoute + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.PoolId |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipModuleRoute(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthModuleRoute + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipModuleRoute(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowModuleRoute + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowModuleRoute + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowModuleRoute + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthModuleRoute + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupModuleRoute + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthModuleRoute + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthModuleRoute = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowModuleRoute = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupModuleRoute = fmt.Errorf("proto: unexpected end of group") +) diff --git a/tests/interchaintest/osmosis/poolmanager/types/msg_create_pool.go b/tests/interchaintest/osmosis/poolmanager/types/msg_create_pool.go new file mode 100644 index 00000000..ae8cedd9 --- /dev/null +++ b/tests/interchaintest/osmosis/poolmanager/types/msg_create_pool.go @@ -0,0 +1,21 @@ +package types + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// CreatePoolMsg defines an interface that every CreatePool transaction should implement. +// The gamm logic will use this to create a pool. +type CreatePoolMsg interface { + // GetPoolType returns the type of the pool to create. + GetPoolType() PoolType + // The creator of the pool, who pays the PoolCreationFee, provides initial liquidity, + // and gets the initial LP shares. + PoolCreator() sdk.AccAddress + // A stateful validation function. + Validate(ctx sdk.Context) error + // Initial Liquidity for the pool that the sender is required to send to the pool account + InitialLiquidity() sdk.Coins + // CreatePool creates a pool implementing PoolI, using data from the message. + CreatePool(ctx sdk.Context, poolID uint64) (PoolI, error) +} diff --git a/tests/interchaintest/osmosis/poolmanager/types/msg_swap.go b/tests/interchaintest/osmosis/poolmanager/types/msg_swap.go new file mode 100644 index 00000000..5b799818 --- /dev/null +++ b/tests/interchaintest/osmosis/poolmanager/types/msg_swap.go @@ -0,0 +1,110 @@ +package types + +// SwapMsg defines a simple interface for getting the token denoms on a swap message route. +type SwapMsgRoute interface { + TokenInDenom() string + TokenOutDenom() string + TokenDenomsOnPath() []string +} + +type MultiSwapMsgRoute interface { + GetSwapMsgs() []SwapMsgRoute +} + +func (msg MsgSplitRouteSwapExactAmountIn) GetSwapMsgs() []SwapMsgRoute { + routes := make([]SwapMsgRoute, len(msg.Routes)) + for i := 0; i < len(msg.Routes); i++ { + routes[i] = SwapAmountInSplitRouteWrapper{msg.Routes[i].Pools, msg.TokenInDenom} + } + return routes +} + +func (msg MsgSplitRouteSwapExactAmountOut) GetSwapMsgs() []SwapMsgRoute { + routes := make([]SwapMsgRoute, len(msg.Routes)) + for i := 0; i < len(msg.Routes); i++ { + routes[i] = SwapAmountOutSplitRouteWrapper{msg.Routes[i].Pools, msg.TokenOutDenom} + } + return routes +} + +type SwapAmountInSplitRouteWrapper struct { + Pools []SwapAmountInRoute `json:"pools"` + InDenom string `json:"in_denom"` +} + +type SwapAmountOutSplitRouteWrapper struct { + Pools []SwapAmountOutRoute `json:"pools"` + OutDenom string `json:"out_denom"` +} + +var ( + _ SwapMsgRoute = MsgSwapExactAmountIn{} + _ SwapMsgRoute = MsgSwapExactAmountOut{} + _ SwapMsgRoute = SwapAmountInSplitRouteWrapper{} + _ SwapMsgRoute = SwapAmountOutSplitRouteWrapper{} +) + +func (msg SwapAmountOutSplitRouteWrapper) TokenInDenom() string { + return msg.Pools[0].TokenInDenom +} +func (msg SwapAmountOutSplitRouteWrapper) TokenOutDenom() string { + return msg.OutDenom +} +func (msg SwapAmountOutSplitRouteWrapper) TokenDenomsOnPath() []string { + denoms := make([]string, 0, len(msg.Pools)+1) + for i := 0; i < len(msg.Pools); i++ { + denoms = append(denoms, msg.Pools[i].TokenInDenom) + } + denoms = append(denoms, msg.TokenOutDenom()) + return denoms +} + +func (msg SwapAmountInSplitRouteWrapper) TokenInDenom() string { + return msg.InDenom +} +func (msg SwapAmountInSplitRouteWrapper) TokenOutDenom() string { + return msg.Pools[len(msg.Pools)-1].TokenOutDenom +} +func (msg SwapAmountInSplitRouteWrapper) TokenDenomsOnPath() []string { + denoms := make([]string, 0, len(msg.Pools)+1) + denoms = append(denoms, msg.TokenInDenom()) + for i := 0; i < len(msg.Pools); i++ { + denoms = append(denoms, msg.Pools[i].TokenOutDenom) + } + return denoms +} + +func (msg MsgSwapExactAmountOut) TokenInDenom() string { + return msg.Routes[0].GetTokenInDenom() +} + +func (msg MsgSwapExactAmountOut) TokenOutDenom() string { + return msg.TokenOut.Denom +} + +func (msg MsgSwapExactAmountOut) TokenDenomsOnPath() []string { + denoms := make([]string, 0, len(msg.Routes)+1) + for i := 0; i < len(msg.Routes); i++ { + denoms = append(denoms, msg.Routes[i].TokenInDenom) + } + denoms = append(denoms, msg.TokenOutDenom()) + return denoms +} + +func (msg MsgSwapExactAmountIn) TokenInDenom() string { + return msg.TokenIn.Denom +} + +func (msg MsgSwapExactAmountIn) TokenOutDenom() string { + lastRouteIndex := len(msg.Routes) - 1 + return msg.Routes[lastRouteIndex].GetTokenOutDenom() +} + +func (msg MsgSwapExactAmountIn) TokenDenomsOnPath() []string { + denoms := make([]string, 0, len(msg.Routes)+1) + denoms = append(denoms, msg.TokenInDenom()) + for i := 0; i < len(msg.Routes); i++ { + denoms = append(denoms, msg.Routes[i].TokenOutDenom) + } + return denoms +} diff --git a/tests/interchaintest/osmosis/poolmanager/types/msgs.go b/tests/interchaintest/osmosis/poolmanager/types/msgs.go new file mode 100644 index 00000000..17034266 --- /dev/null +++ b/tests/interchaintest/osmosis/poolmanager/types/msgs.go @@ -0,0 +1,224 @@ +package types + +import ( + fmt "fmt" + + errorsmod "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + osmomath "github.com/osmosis-labs/fee-abstraction/v7/tests/interchaintest/osmosis/osmomath" +) + +// constants. +const ( + TypeMsgSwapExactAmountIn = "swap_exact_amount_in" + TypeMsgSwapExactAmountOut = "swap_exact_amount_out" + TypeMsgSplitRouteSwapExactAmountIn = "split_route_swap_exact_amount_in" + TypeMsgSplitRouteSwapExactAmountOut = "split_route_swap_exact_amount_out" + TypeMsgSetDenomPairTakerFee = "set_denom_pair_taker_fee" +) + +var _ sdk.Msg = &MsgSwapExactAmountIn{} + +func (msg MsgSwapExactAmountIn) Route() string { return RouterKey } +func (msg MsgSwapExactAmountIn) Type() string { return TypeMsgSwapExactAmountIn } +func (msg MsgSwapExactAmountIn) ValidateBasic() error { + _, err := sdk.AccAddressFromBech32(msg.Sender) + if err != nil { + return errorsmod.Wrapf(sdkerrors.ErrInvalidAddress, "Invalid sender address (%s)", err) + } + + err = SwapAmountInRoutes(msg.Routes).Validate() + if err != nil { + return err + } + + if !msg.TokenIn.IsValid() || !msg.TokenIn.IsPositive() { + // TODO: remove sdk errors + return errorsmod.Wrap(sdkerrors.ErrInvalidCoins, msg.TokenIn.String()) + } + + if !msg.TokenOutMinAmount.IsPositive() { + return nonPositiveAmountError{msg.TokenOutMinAmount.String()} + } + + return nil +} + +func (msg MsgSwapExactAmountIn) GetSignBytes() []byte { + return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(&msg)) +} + +func (msg MsgSwapExactAmountIn) GetSigners() []sdk.AccAddress { + sender, err := sdk.AccAddressFromBech32(msg.Sender) + if err != nil { + panic(err) + } + return []sdk.AccAddress{sender} +} + +var _ sdk.Msg = &MsgSwapExactAmountOut{} + +func (msg MsgSwapExactAmountOut) Route() string { return RouterKey } +func (msg MsgSwapExactAmountOut) Type() string { return TypeMsgSwapExactAmountOut } +func (msg MsgSwapExactAmountOut) ValidateBasic() error { + _, err := sdk.AccAddressFromBech32(msg.Sender) + if err != nil { + return errorsmod.Wrapf(sdkerrors.ErrInvalidAddress, "Invalid sender address (%s)", err) + } + + err = SwapAmountOutRoutes(msg.Routes).Validate() + if err != nil { + return err + } + + if !msg.TokenOut.IsValid() || !msg.TokenOut.IsPositive() { + return errorsmod.Wrap(sdkerrors.ErrInvalidCoins, msg.TokenOut.String()) + } + + if !msg.TokenInMaxAmount.IsPositive() { + return nonPositiveAmountError{msg.TokenInMaxAmount.String()} + } + + return nil +} + +func (msg MsgSwapExactAmountOut) GetSignBytes() []byte { + return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(&msg)) +} + +func (msg MsgSwapExactAmountOut) GetSigners() []sdk.AccAddress { + sender, err := sdk.AccAddressFromBech32(msg.Sender) + if err != nil { + panic(err) + } + return []sdk.AccAddress{sender} +} + +var _ sdk.Msg = &MsgSplitRouteSwapExactAmountIn{} + +func (msg MsgSplitRouteSwapExactAmountIn) Route() string { return RouterKey } +func (msg MsgSplitRouteSwapExactAmountIn) Type() string { return TypeMsgSplitRouteSwapExactAmountIn } + +func (msg MsgSplitRouteSwapExactAmountIn) ValidateBasic() error { + _, err := sdk.AccAddressFromBech32(msg.Sender) + if err != nil { + return InvalidSenderError{Sender: msg.Sender} + } + + if err := sdk.ValidateDenom(msg.TokenInDenom); err != nil { + return err + } + + if err := ValidateSwapAmountInSplitRoute(msg.Routes); err != nil { + return err + } + + if !msg.TokenOutMinAmount.IsPositive() { + return nonPositiveAmountError{msg.TokenOutMinAmount.String()} + } + + return nil +} + +func (msg MsgSplitRouteSwapExactAmountIn) GetSignBytes() []byte { + return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(&msg)) +} + +func (msg MsgSplitRouteSwapExactAmountIn) GetSigners() []sdk.AccAddress { + sender, err := sdk.AccAddressFromBech32(msg.Sender) + if err != nil { + panic(err) + } + return []sdk.AccAddress{sender} +} + +var _ sdk.Msg = &MsgSplitRouteSwapExactAmountOut{} + +func (msg MsgSplitRouteSwapExactAmountOut) Route() string { return RouterKey } +func (msg MsgSplitRouteSwapExactAmountOut) Type() string { return TypeMsgSplitRouteSwapExactAmountOut } + +func (msg MsgSplitRouteSwapExactAmountOut) ValidateBasic() error { + _, err := sdk.AccAddressFromBech32(msg.Sender) + if err != nil { + return InvalidSenderError{Sender: msg.Sender} + } + + if err := sdk.ValidateDenom(msg.TokenOutDenom); err != nil { + return err + } + + if err := ValidateSwapAmountOutSplitRoute(msg.Routes); err != nil { + return err + } + + if !msg.TokenInMaxAmount.IsPositive() { + return nonPositiveAmountError{msg.TokenInMaxAmount.String()} + } + + return nil +} + +func (msg MsgSplitRouteSwapExactAmountOut) GetSignBytes() []byte { + return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(&msg)) +} + +func (msg MsgSplitRouteSwapExactAmountOut) GetSigners() []sdk.AccAddress { + sender, err := sdk.AccAddressFromBech32(msg.Sender) + if err != nil { + panic(err) + } + return []sdk.AccAddress{sender} +} + +var _ sdk.Msg = &MsgSetDenomPairTakerFee{} + +func (msg MsgSetDenomPairTakerFee) Route() string { return RouterKey } +func (msg MsgSetDenomPairTakerFee) Type() string { return TypeMsgSetDenomPairTakerFee } + +func (msg MsgSetDenomPairTakerFee) ValidateBasic() error { + _, err := sdk.AccAddressFromBech32(msg.Sender) + if err != nil { + return InvalidSenderError{Sender: msg.Sender} + } + + return validateDenomPairTakerFees(msg.DenomPairTakerFee) +} + +func (msg MsgSetDenomPairTakerFee) GetSignBytes() []byte { + return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(&msg)) +} + +func (msg MsgSetDenomPairTakerFee) GetSigners() []sdk.AccAddress { + sender, err := sdk.AccAddressFromBech32(msg.Sender) + if err != nil { + panic(err) + } + return []sdk.AccAddress{sender} +} + +func validateDenomPairTakerFees(pairs []DenomPairTakerFee) error { + if len(pairs) == 0 { + return fmt.Errorf("Empty denom pair taker fee") + } + + for _, record := range pairs { + if record.Denom0 == record.Denom1 { + return fmt.Errorf("denom0 and denom1 must be different") + } + + if sdk.ValidateDenom(record.Denom0) != nil { + return fmt.Errorf("denom0 is invalid: %s", sdk.ValidateDenom(record.Denom0)) + } + + if sdk.ValidateDenom(record.Denom1) != nil { + return fmt.Errorf("denom1 is invalid: %s", sdk.ValidateDenom(record.Denom1)) + } + + takerFee := record.TakerFee + if takerFee.IsNegative() || takerFee.GTE(osmomath.OneDec()) { + return fmt.Errorf("taker fee must be between 0 and 1: %s", takerFee.String()) + } + } + return nil +} diff --git a/tests/interchaintest/osmosis/poolmanager/types/pool.go b/tests/interchaintest/osmosis/poolmanager/types/pool.go new file mode 100644 index 00000000..25758922 --- /dev/null +++ b/tests/interchaintest/osmosis/poolmanager/types/pool.go @@ -0,0 +1,45 @@ +package types + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + proto "github.com/cosmos/gogoproto/proto" + + "github.com/osmosis-labs/fee-abstraction/v7/tests/interchaintest/osmosis/osmomath" + "github.com/osmosis-labs/fee-abstraction/v7/tests/interchaintest/osmosis/osmoutils" +) + +var MaxPoolId uint64 = 99_999_999_999 + +// PoolI defines an interface for pools that hold tokens. +type PoolI interface { + proto.Message + + GetAddress() sdk.AccAddress + String() string + GetId() uint64 + // GetSpreadFactor returns the pool's spread factor, based on the current state. + // Pools may choose to make their spread factors dependent upon state + // (prior TWAPs, network downtime, other pool states, etc.) + // hence Context is provided as an argument. + GetSpreadFactor(ctx sdk.Context) osmomath.Dec + // Returns whether the pool has swaps enabled at the moment + IsActive(ctx sdk.Context) bool + + // GetPoolDenoms returns the pool's denoms. + GetPoolDenoms(sdk.Context) []string + + // Returns the spot price of the 'base asset' in terms of the 'quote asset' in the pool, + // errors if either baseAssetDenom, or quoteAssetDenom does not exist. + // For example, if this was a UniV2 50-50 pool, with 2 ETH, and 8000 UST + // pool.SpotPrice(ctx, "eth", "ust") = 4000.00 + SpotPrice(ctx sdk.Context, quoteAssetDenom string, baseAssetDenom string) (osmomath.BigDec, error) + // GetType returns the type of the pool (Balancer, Stableswap, Concentrated, etc.) + GetType() PoolType + // AsSerializablePool returns the pool in a serializable form (useful when a model wraps the proto) + AsSerializablePool() PoolI +} + +// NewPoolAddress returns an address for a pool from a given id. +func NewPoolAddress(poolId uint64) sdk.AccAddress { + return osmoutils.NewModuleAddressWithPrefix(ModuleName, "pool", sdk.Uint64ToBigEndian(poolId)) +} diff --git a/tests/interchaintest/osmosis/poolmanager/types/routes.go b/tests/interchaintest/osmosis/poolmanager/types/routes.go new file mode 100644 index 00000000..7fad6434 --- /dev/null +++ b/tests/interchaintest/osmosis/poolmanager/types/routes.go @@ -0,0 +1,169 @@ +package types + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/osmosis-labs/fee-abstraction/v7/tests/interchaintest/osmosis/osmoutils" +) + +type SwapAmountInRoutes []SwapAmountInRoute + +func (routes SwapAmountInRoutes) Validate() error { + if len(routes) == 0 { + return ErrEmptyRoutes + } + + for _, route := range routes { + err := sdk.ValidateDenom(route.TokenOutDenom) + if err != nil { + return err + } + } + + return nil +} + +func (routes SwapAmountInRoutes) IntermediateDenoms() []string { + if len(routes) < 2 { + return nil + } + intermediateDenoms := make([]string, 0, len(routes)-1) + for _, route := range routes[:len(routes)-1] { + intermediateDenoms = append(intermediateDenoms, route.TokenOutDenom) + } + + return intermediateDenoms +} + +func (routes SwapAmountInRoutes) PoolIds() []uint64 { + poolIds := make([]uint64, 0, len(routes)) + for _, route := range routes { + poolIds = append(poolIds, route.PoolId) + } + return poolIds +} + +func (routes SwapAmountInRoutes) Length() int { + return len(routes) +} + +type SwapAmountOutRoutes []SwapAmountOutRoute + +func (routes SwapAmountOutRoutes) Validate() error { + if len(routes) == 0 { + return ErrEmptyRoutes + } + + for _, route := range routes { + err := sdk.ValidateDenom(route.TokenInDenom) + if err != nil { + return err + } + } + + return nil +} + +func (routes SwapAmountOutRoutes) IntermediateDenoms() []string { + if len(routes) < 2 { + return nil + } + intermediateDenoms := make([]string, 0, len(routes)-1) + for _, route := range routes[1:] { + intermediateDenoms = append(intermediateDenoms, route.TokenInDenom) + } + + return intermediateDenoms +} + +func (routes SwapAmountOutRoutes) PoolIds() []uint64 { + poolIds := make([]uint64, 0, len(routes)) + for _, route := range routes { + poolIds = append(poolIds, route.PoolId) + } + return poolIds +} + +func (routes SwapAmountOutRoutes) Length() int { + return len(routes) +} + +// ValidateSwapAmountInSplitRoute validates a slice of SwapAmountInSplitRoute. +// +// returns an error if any of the following are true: +// - the slice is empty +// - any SwapAmountInRoute in the slice is invalid +// - the last TokenOutDenom of any SwapAmountInRoute in the slice does not match the TokenOutDenom of the previous SwapAmountInRoute in the slice +// - there are duplicate SwapAmountInRoutes in the slice +func ValidateSwapAmountInSplitRoute(splitRoutes []SwapAmountInSplitRoute) error { + if len(splitRoutes) == 0 { + return ErrEmptyRoutes + } + + // validate every multihop path + previousLastDenomOut := "" + multihopRoutes := make([]SwapAmountInRoutes, 0, len(splitRoutes)) + for _, splitRoute := range splitRoutes { + multihopRoute := splitRoute.Pools + + err := SwapAmountInRoutes(multihopRoute).Validate() + if err != nil { + return err + } + + lastDenomOut := multihopRoute[len(multihopRoute)-1].TokenOutDenom + + if previousLastDenomOut != "" && lastDenomOut != previousLastDenomOut { + return InvalidFinalTokenOutError{TokenOutGivenA: previousLastDenomOut, TokenOutGivenB: lastDenomOut} + } + + previousLastDenomOut = lastDenomOut + + multihopRoutes = append(multihopRoutes, multihopRoute) + } + + if osmoutils.ContainsDuplicateDeepEqual(multihopRoutes) { + return ErrDuplicateRoutesNotAllowed + } + + return nil +} + +// ValidateSwapAmountOutSplitRoute validates a slice of SwapAmountOutSplitRoute and returns an error if any of the following are true: +// - the slice is empty +// - any SwapAmountOutRoute in the slice is invalid +// - the first TokenInDenom of any SwapAmountOutRoute in the slice does not match the TokenInDenom of the previous SwapAmountOutRoute in the slice +// - there are duplicate SwapAmountOutRoutes in the slice +func ValidateSwapAmountOutSplitRoute(splitRoutes []SwapAmountOutSplitRoute) error { + if len(splitRoutes) == 0 { + return ErrEmptyRoutes + } + + // validate every multihop path + previousFirstDenomIn := "" + multihopRoutes := make([]SwapAmountOutRoutes, 0, len(splitRoutes)) + for _, splitRoute := range splitRoutes { + multihopRoute := splitRoute.Pools + + err := SwapAmountOutRoutes(multihopRoute).Validate() + if err != nil { + return err + } + + firstDenomIn := multihopRoute[0].TokenInDenom + + if previousFirstDenomIn != "" && firstDenomIn != previousFirstDenomIn { + return InvalidFinalTokenOutError{TokenOutGivenA: previousFirstDenomIn, TokenOutGivenB: firstDenomIn} + } + + previousFirstDenomIn = firstDenomIn + + multihopRoutes = append(multihopRoutes, multihopRoute) + } + + if osmoutils.ContainsDuplicateDeepEqual(multihopRoutes) { + return ErrDuplicateRoutesNotAllowed + } + + return nil +} diff --git a/tests/interchaintest/osmosis/poolmanager/types/routes_test.go b/tests/interchaintest/osmosis/poolmanager/types/routes_test.go new file mode 100644 index 00000000..51102837 --- /dev/null +++ b/tests/interchaintest/osmosis/poolmanager/types/routes_test.go @@ -0,0 +1,352 @@ +package types + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/osmosis-labs/fee-abstraction/v7/tests/interchaintest/osmosis/osmomath" +) + +const ( + foo = "foo" + bar = "bar" + baz = "baz" + uosmo = "uosmo" +) + +var ( + twentyFiveBaseUnitsAmount = osmomath.NewInt(25_000_000) + + // Note: These are iniialized in such a way as it makes + // it easier to reason about the test cases. + fooBarPoolId = uint64(1) + fooBazPoolId = fooBarPoolId + 1 + fooUosmoPoolId = fooBazPoolId + 1 + barBazPoolId = fooUosmoPoolId + 1 + barUosmoPoolId = barBazPoolId + 1 + bazUosmoPoolId = barUosmoPoolId + 1 + + // Amount in default routes + + defaultSingleRouteOneHopAmountIn = []SwapAmountInSplitRoute{ + { + Pools: []SwapAmountInRoute{ + { + PoolId: fooBarPoolId, + TokenOutDenom: bar, + }, + }, + TokenInAmount: twentyFiveBaseUnitsAmount, + }, + } + + defaultTwoHopRoutesAmountIn = []SwapAmountInRoute{ + { + PoolId: fooBarPoolId, + TokenOutDenom: bar, + }, + { + PoolId: barBazPoolId, + TokenOutDenom: baz, + }, + } + + defaultSingleRouteTwoHopsAmountIn = SwapAmountInSplitRoute{ + Pools: defaultTwoHopRoutesAmountIn, + TokenInAmount: twentyFiveBaseUnitsAmount, + } + + defaultSingleRouteThreeHopsAmountIn = SwapAmountInSplitRoute{ + Pools: []SwapAmountInRoute{ + { + PoolId: fooBarPoolId, + TokenOutDenom: bar, + }, + { + PoolId: barUosmoPoolId, + TokenOutDenom: uosmo, + }, + { + PoolId: bazUosmoPoolId, + TokenOutDenom: baz, + }, + }, + TokenInAmount: osmomath.NewInt(twentyFiveBaseUnitsAmount.Int64() * 3), + } + + // Amount out default routes + + defaultSingleRouteOneHopAmounOut = []SwapAmountOutSplitRoute{ + { + Pools: []SwapAmountOutRoute{ + { + PoolId: fooBarPoolId, + TokenInDenom: foo, + }, + }, + TokenOutAmount: twentyFiveBaseUnitsAmount, + }, + } + + defaultTwoHopRoutesAmountOut = []SwapAmountOutRoute{ + { + PoolId: fooBarPoolId, + TokenInDenom: foo, + }, + { + PoolId: barBazPoolId, + TokenInDenom: bar, + }, + } + + defaultSingleRouteTwoHopsAmountOut = SwapAmountOutSplitRoute{ + Pools: defaultTwoHopRoutesAmountOut, + TokenOutAmount: twentyFiveBaseUnitsAmount, + } + + defaultSingleRouteThreeHopsAmountOut = SwapAmountOutSplitRoute{ + Pools: []SwapAmountOutRoute{ + { + PoolId: fooBarPoolId, + TokenInDenom: foo, + }, + { + PoolId: barUosmoPoolId, + TokenInDenom: bar, + }, + { + PoolId: bazUosmoPoolId, + TokenInDenom: uosmo, + }, + }, + TokenOutAmount: osmomath.NewInt(twentyFiveBaseUnitsAmount.Int64() * 3), + } +) + +func TestValidateSwapAmountInSplitRoute(t *testing.T) { + tests := []struct { + name string + routes []SwapAmountInSplitRoute + expectErr error + }{ + { + name: "single route one hop", + routes: defaultSingleRouteOneHopAmountIn, + }, + { + name: "single route two hops", + routes: []SwapAmountInSplitRoute{defaultSingleRouteTwoHopsAmountIn}, + }, + { + name: "multi route two and three hops", + routes: []SwapAmountInSplitRoute{defaultSingleRouteTwoHopsAmountIn, defaultSingleRouteThreeHopsAmountIn}, + }, + { + name: "empty split routes", + routes: []SwapAmountInSplitRoute{}, + expectErr: ErrEmptyRoutes, + }, + { + name: "empty multihop route", + routes: []SwapAmountInSplitRoute{ + { + Pools: []SwapAmountInRoute{}, + TokenInAmount: osmomath.OneInt(), + }, + }, + expectErr: ErrEmptyRoutes, + }, + { + name: "invalid final token out", + routes: []SwapAmountInSplitRoute{ + { + Pools: []SwapAmountInRoute{ + { + PoolId: 1, + + TokenOutDenom: bar, + }, + }, + TokenInAmount: osmomath.OneInt(), + }, + { + Pools: []SwapAmountInRoute{ + { + PoolId: 2, + TokenOutDenom: baz, + }, + }, + TokenInAmount: osmomath.OneInt(), + }, + }, + expectErr: InvalidFinalTokenOutError{TokenOutGivenA: bar, TokenOutGivenB: baz}, + }, + { + name: "duplicate routes", + routes: []SwapAmountInSplitRoute{ + defaultSingleRouteTwoHopsAmountIn, + defaultSingleRouteTwoHopsAmountIn, + }, + expectErr: ErrDuplicateRoutesNotAllowed, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := ValidateSwapAmountInSplitRoute(tt.routes) + + if tt.expectErr != nil { + require.Error(t, err) + require.ErrorIs(t, err, tt.expectErr) + } else { + require.NoError(t, err) + } + }) + } +} + +func TestValidateSwapAmountOutSplitRoute(t *testing.T) { + tests := []struct { + name string + routes []SwapAmountOutSplitRoute + expectErr error + }{ + { + name: "single route one hop", + routes: defaultSingleRouteOneHopAmounOut, + }, + { + name: "single route two hops", + routes: []SwapAmountOutSplitRoute{defaultSingleRouteTwoHopsAmountOut}, + }, + { + name: "multi route two and three hops", + routes: []SwapAmountOutSplitRoute{defaultSingleRouteTwoHopsAmountOut, defaultSingleRouteThreeHopsAmountOut}, + }, + { + name: "empty split routes", + routes: []SwapAmountOutSplitRoute{}, + expectErr: ErrEmptyRoutes, + }, + { + name: "empty multihop route", + routes: []SwapAmountOutSplitRoute{ + { + Pools: []SwapAmountOutRoute{}, + TokenOutAmount: osmomath.OneInt(), + }, + }, + expectErr: ErrEmptyRoutes, + }, + { + name: "invalid first token in", + routes: []SwapAmountOutSplitRoute{ + { + Pools: []SwapAmountOutRoute{ + { + PoolId: 1, + + TokenInDenom: bar, + }, + }, + TokenOutAmount: osmomath.OneInt(), + }, + { + Pools: []SwapAmountOutRoute{ + { + PoolId: 2, + TokenInDenom: baz, + }, + }, + TokenOutAmount: osmomath.OneInt(), + }, + }, + expectErr: InvalidFinalTokenOutError{TokenOutGivenA: bar, TokenOutGivenB: baz}, + }, + { + name: "duplicate routes", + routes: []SwapAmountOutSplitRoute{ + defaultSingleRouteTwoHopsAmountOut, + defaultSingleRouteTwoHopsAmountOut, + }, + expectErr: ErrDuplicateRoutesNotAllowed, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := ValidateSwapAmountOutSplitRoute(tt.routes) + + if tt.expectErr != nil { + require.Error(t, err) + require.ErrorIs(t, err, tt.expectErr) + } else { + require.NoError(t, err) + } + }) + } +} + +func TestIntermediateDenoms(t *testing.T) { + + tests := map[string]struct { + route SwapAmountInRoutes + expectedDenoms []string + }{ + "happy path: one intermediate denom": { + route: SwapAmountInRoutes([]SwapAmountInRoute{ + { + PoolId: 1, + TokenOutDenom: bar, + }, + { + PoolId: 2, + TokenOutDenom: baz, + }, + }), + + expectedDenoms: []string{bar}, + }, + "multiple intermediate denoms": { + route: SwapAmountInRoutes([]SwapAmountInRoute{ + { + PoolId: 1, + TokenOutDenom: bar, + }, + { + PoolId: 2, + TokenOutDenom: baz, + }, + { + PoolId: 5, + TokenOutDenom: uosmo, + }, + { + PoolId: 3, + TokenOutDenom: foo, + }, + }), + + expectedDenoms: []string{bar, baz, uosmo}, + }, + "no intermediate denoms (single pool)": { + route: SwapAmountInRoutes([]SwapAmountInRoute{ + { + PoolId: 1, + TokenOutDenom: bar, + }, + }), + + // Note that we expect the function to fail quietly + expectedDenoms: nil, + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + actualIntermediateDenoms := tc.route.IntermediateDenoms() + require.Equal(t, tc.expectedDenoms, actualIntermediateDenoms) + }) + } +} diff --git a/tests/interchaintest/osmosis/poolmanager/types/swap_route.pb.go b/tests/interchaintest/osmosis/poolmanager/types/swap_route.pb.go new file mode 100644 index 00000000..8dad0939 --- /dev/null +++ b/tests/interchaintest/osmosis/poolmanager/types/swap_route.pb.go @@ -0,0 +1,1032 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: osmosis/poolmanager/v1beta1/swap_route.proto + +package types + +import ( + cosmossdk_io_math "cosmossdk.io/math" + fmt "fmt" + _ "github.com/cosmos/gogoproto/gogoproto" + proto "github.com/cosmos/gogoproto/proto" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +type SwapAmountInRoute struct { + PoolId uint64 `protobuf:"varint,1,opt,name=pool_id,json=poolId,proto3" json:"pool_id,omitempty" yaml:"pool_id"` + TokenOutDenom string `protobuf:"bytes,2,opt,name=token_out_denom,json=tokenOutDenom,proto3" json:"token_out_denom,omitempty" yaml:"token_out_denom"` +} + +func (m *SwapAmountInRoute) Reset() { *m = SwapAmountInRoute{} } +func (m *SwapAmountInRoute) String() string { return proto.CompactTextString(m) } +func (*SwapAmountInRoute) ProtoMessage() {} +func (*SwapAmountInRoute) Descriptor() ([]byte, []int) { + return fileDescriptor_cddd97a9a05492a8, []int{0} +} +func (m *SwapAmountInRoute) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SwapAmountInRoute) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SwapAmountInRoute.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *SwapAmountInRoute) XXX_Merge(src proto.Message) { + xxx_messageInfo_SwapAmountInRoute.Merge(m, src) +} +func (m *SwapAmountInRoute) XXX_Size() int { + return m.Size() +} +func (m *SwapAmountInRoute) XXX_DiscardUnknown() { + xxx_messageInfo_SwapAmountInRoute.DiscardUnknown(m) +} + +var xxx_messageInfo_SwapAmountInRoute proto.InternalMessageInfo + +func (m *SwapAmountInRoute) GetPoolId() uint64 { + if m != nil { + return m.PoolId + } + return 0 +} + +func (m *SwapAmountInRoute) GetTokenOutDenom() string { + if m != nil { + return m.TokenOutDenom + } + return "" +} + +type SwapAmountOutRoute struct { + PoolId uint64 `protobuf:"varint,1,opt,name=pool_id,json=poolId,proto3" json:"pool_id,omitempty" yaml:"pool_id"` + TokenInDenom string `protobuf:"bytes,2,opt,name=token_in_denom,json=tokenInDenom,proto3" json:"token_in_denom,omitempty" yaml:"token_in_denom"` +} + +func (m *SwapAmountOutRoute) Reset() { *m = SwapAmountOutRoute{} } +func (m *SwapAmountOutRoute) String() string { return proto.CompactTextString(m) } +func (*SwapAmountOutRoute) ProtoMessage() {} +func (*SwapAmountOutRoute) Descriptor() ([]byte, []int) { + return fileDescriptor_cddd97a9a05492a8, []int{1} +} +func (m *SwapAmountOutRoute) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SwapAmountOutRoute) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SwapAmountOutRoute.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *SwapAmountOutRoute) XXX_Merge(src proto.Message) { + xxx_messageInfo_SwapAmountOutRoute.Merge(m, src) +} +func (m *SwapAmountOutRoute) XXX_Size() int { + return m.Size() +} +func (m *SwapAmountOutRoute) XXX_DiscardUnknown() { + xxx_messageInfo_SwapAmountOutRoute.DiscardUnknown(m) +} + +var xxx_messageInfo_SwapAmountOutRoute proto.InternalMessageInfo + +func (m *SwapAmountOutRoute) GetPoolId() uint64 { + if m != nil { + return m.PoolId + } + return 0 +} + +func (m *SwapAmountOutRoute) GetTokenInDenom() string { + if m != nil { + return m.TokenInDenom + } + return "" +} + +type SwapAmountInSplitRoute struct { + Pools []SwapAmountInRoute `protobuf:"bytes,1,rep,name=pools,proto3" json:"pools" yaml:"pools"` + TokenInAmount cosmossdk_io_math.Int `protobuf:"bytes,2,opt,name=token_in_amount,json=tokenInAmount,proto3,customtype=cosmossdk.io/math.Int" json:"token_in_amount" yaml:"token_in_amount"` +} + +func (m *SwapAmountInSplitRoute) Reset() { *m = SwapAmountInSplitRoute{} } +func (m *SwapAmountInSplitRoute) String() string { return proto.CompactTextString(m) } +func (*SwapAmountInSplitRoute) ProtoMessage() {} +func (*SwapAmountInSplitRoute) Descriptor() ([]byte, []int) { + return fileDescriptor_cddd97a9a05492a8, []int{2} +} +func (m *SwapAmountInSplitRoute) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SwapAmountInSplitRoute) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SwapAmountInSplitRoute.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *SwapAmountInSplitRoute) XXX_Merge(src proto.Message) { + xxx_messageInfo_SwapAmountInSplitRoute.Merge(m, src) +} +func (m *SwapAmountInSplitRoute) XXX_Size() int { + return m.Size() +} +func (m *SwapAmountInSplitRoute) XXX_DiscardUnknown() { + xxx_messageInfo_SwapAmountInSplitRoute.DiscardUnknown(m) +} + +var xxx_messageInfo_SwapAmountInSplitRoute proto.InternalMessageInfo + +func (m *SwapAmountInSplitRoute) GetPools() []SwapAmountInRoute { + if m != nil { + return m.Pools + } + return nil +} + +type SwapAmountOutSplitRoute struct { + Pools []SwapAmountOutRoute `protobuf:"bytes,1,rep,name=pools,proto3" json:"pools" yaml:"pools"` + TokenOutAmount cosmossdk_io_math.Int `protobuf:"bytes,2,opt,name=token_out_amount,json=tokenOutAmount,proto3,customtype=cosmossdk.io/math.Int" json:"token_out_amount" yaml:"token_out_amount"` +} + +func (m *SwapAmountOutSplitRoute) Reset() { *m = SwapAmountOutSplitRoute{} } +func (m *SwapAmountOutSplitRoute) String() string { return proto.CompactTextString(m) } +func (*SwapAmountOutSplitRoute) ProtoMessage() {} +func (*SwapAmountOutSplitRoute) Descriptor() ([]byte, []int) { + return fileDescriptor_cddd97a9a05492a8, []int{3} +} +func (m *SwapAmountOutSplitRoute) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SwapAmountOutSplitRoute) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SwapAmountOutSplitRoute.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *SwapAmountOutSplitRoute) XXX_Merge(src proto.Message) { + xxx_messageInfo_SwapAmountOutSplitRoute.Merge(m, src) +} +func (m *SwapAmountOutSplitRoute) XXX_Size() int { + return m.Size() +} +func (m *SwapAmountOutSplitRoute) XXX_DiscardUnknown() { + xxx_messageInfo_SwapAmountOutSplitRoute.DiscardUnknown(m) +} + +var xxx_messageInfo_SwapAmountOutSplitRoute proto.InternalMessageInfo + +func (m *SwapAmountOutSplitRoute) GetPools() []SwapAmountOutRoute { + if m != nil { + return m.Pools + } + return nil +} + +func init() { + proto.RegisterType((*SwapAmountInRoute)(nil), "osmosis.poolmanager.v1beta1.SwapAmountInRoute") + proto.RegisterType((*SwapAmountOutRoute)(nil), "osmosis.poolmanager.v1beta1.SwapAmountOutRoute") + proto.RegisterType((*SwapAmountInSplitRoute)(nil), "osmosis.poolmanager.v1beta1.SwapAmountInSplitRoute") + proto.RegisterType((*SwapAmountOutSplitRoute)(nil), "osmosis.poolmanager.v1beta1.SwapAmountOutSplitRoute") +} + +func init() { + proto.RegisterFile("osmosis/poolmanager/v1beta1/swap_route.proto", fileDescriptor_cddd97a9a05492a8) +} + +var fileDescriptor_cddd97a9a05492a8 = []byte{ + // 443 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x93, 0xcb, 0xaa, 0xd3, 0x40, + 0x1c, 0xc6, 0x33, 0x5e, 0x8e, 0x38, 0x1e, 0xab, 0x86, 0x73, 0xa9, 0x47, 0x48, 0x4a, 0x56, 0x05, + 0x75, 0x86, 0xd6, 0x45, 0xc5, 0x8d, 0x18, 0xdc, 0x64, 0x55, 0x4c, 0x77, 0x75, 0x11, 0x26, 0x4d, + 0x48, 0x43, 0x93, 0x99, 0xd0, 0x99, 0xb4, 0x76, 0x2b, 0x3e, 0x80, 0x8f, 0xd5, 0x65, 0x37, 0x82, + 0x74, 0x11, 0xa4, 0x7d, 0x83, 0x3c, 0x81, 0xe4, 0x66, 0x93, 0x0a, 0x55, 0xce, 0x6e, 0x2e, 0xff, + 0x6f, 0xfe, 0xbf, 0xef, 0x9b, 0x19, 0xf8, 0x8a, 0xf1, 0x90, 0x71, 0x9f, 0xe3, 0x88, 0xb1, 0x20, + 0x24, 0x94, 0x78, 0xee, 0x1c, 0x2f, 0x7a, 0xb6, 0x2b, 0x48, 0x0f, 0xf3, 0x25, 0x89, 0xac, 0x39, + 0x8b, 0x85, 0x8b, 0xa2, 0x39, 0x13, 0x4c, 0x7e, 0x51, 0x56, 0xa3, 0x5a, 0x35, 0x2a, 0xab, 0x6f, + 0x2e, 0x3c, 0xe6, 0xb1, 0xbc, 0x0e, 0x67, 0xa3, 0x42, 0xa2, 0x7d, 0x03, 0xf0, 0xd9, 0x68, 0x49, + 0xa2, 0x0f, 0x21, 0x8b, 0xa9, 0x30, 0xa8, 0x99, 0x1d, 0x27, 0xbf, 0x84, 0x0f, 0xb2, 0x23, 0x2c, + 0xdf, 0x69, 0x83, 0x0e, 0xe8, 0xde, 0xd3, 0xe5, 0x34, 0x51, 0x5b, 0x2b, 0x12, 0x06, 0xef, 0xb4, + 0x72, 0x43, 0x33, 0xcf, 0xb2, 0x91, 0xe1, 0xc8, 0x3a, 0x7c, 0x22, 0xd8, 0xcc, 0xa5, 0x16, 0x8b, + 0x85, 0xe5, 0xb8, 0x94, 0x85, 0xed, 0x3b, 0x1d, 0xd0, 0x7d, 0xa8, 0xdf, 0xa4, 0x89, 0x7a, 0x55, + 0x88, 0x8e, 0x0a, 0x34, 0xf3, 0x71, 0xbe, 0x32, 0x8c, 0xc5, 0xc7, 0x7c, 0xfe, 0x15, 0x40, 0xf9, + 0x80, 0x31, 0x8c, 0xc5, 0x2d, 0x38, 0xde, 0xc3, 0x56, 0xd1, 0xc6, 0xa7, 0x0d, 0x8c, 0xe7, 0x69, + 0xa2, 0x5e, 0xd6, 0x31, 0xaa, 0x7d, 0xcd, 0x3c, 0xcf, 0x17, 0x0c, 0x5a, 0x40, 0xfc, 0x00, 0xf0, + 0xaa, 0x9e, 0xc5, 0x28, 0x0a, 0xfc, 0x12, 0x64, 0x0c, 0xef, 0x67, 0x5d, 0x78, 0x1b, 0x74, 0xee, + 0x76, 0x1f, 0xf5, 0x11, 0x3a, 0x91, 0x34, 0xfa, 0x2b, 0x4f, 0xfd, 0x62, 0x9d, 0xa8, 0x52, 0x9a, + 0xa8, 0xe7, 0x07, 0x74, 0xae, 0x99, 0xc5, 0x91, 0xb2, 0x55, 0xe5, 0xe7, 0x53, 0x8b, 0xe4, 0xb2, + 0x12, 0x7c, 0x90, 0xa9, 0xb6, 0x89, 0x7a, 0x39, 0xc9, 0xbb, 0x71, 0x67, 0x86, 0x7c, 0x86, 0x43, + 0x22, 0xa6, 0xc8, 0xa0, 0xe2, 0x38, 0xdc, 0x3f, 0xea, 0x2a, 0x5c, 0x83, 0x16, 0x10, 0xda, 0x16, + 0xc0, 0xeb, 0x46, 0xb8, 0x35, 0x63, 0x9f, 0x9b, 0xc6, 0xf0, 0x7f, 0x1a, 0xab, 0x6e, 0xe8, 0xb4, + 0x33, 0x1b, 0x3e, 0x3d, 0x5c, 0x7c, 0xc3, 0xda, 0xdb, 0x7f, 0x59, 0xbb, 0x3e, 0x7e, 0x37, 0x95, + 0xb7, 0x56, 0xf5, 0x70, 0x0a, 0x10, 0xfd, 0xd3, 0x7a, 0xa7, 0x80, 0xcd, 0x4e, 0x01, 0xbf, 0x76, + 0x0a, 0xf8, 0xbe, 0x57, 0xa4, 0xcd, 0x5e, 0x91, 0x7e, 0xee, 0x15, 0x69, 0x3c, 0xf0, 0x7c, 0x31, + 0x8d, 0x6d, 0x34, 0x61, 0x21, 0x2e, 0x5d, 0xbd, 0x0e, 0x88, 0xcd, 0xab, 0x09, 0x5e, 0xf4, 0xfb, + 0xf8, 0x4b, 0xe3, 0x67, 0x89, 0x55, 0xe4, 0x72, 0xfb, 0x2c, 0xff, 0x1a, 0x6f, 0x7e, 0x07, 0x00, + 0x00, 0xff, 0xff, 0x52, 0xd3, 0x7b, 0x83, 0x7d, 0x03, 0x00, 0x00, +} + +func (m *SwapAmountInRoute) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *SwapAmountInRoute) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SwapAmountInRoute) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.TokenOutDenom) > 0 { + i -= len(m.TokenOutDenom) + copy(dAtA[i:], m.TokenOutDenom) + i = encodeVarintSwapRoute(dAtA, i, uint64(len(m.TokenOutDenom))) + i-- + dAtA[i] = 0x12 + } + if m.PoolId != 0 { + i = encodeVarintSwapRoute(dAtA, i, uint64(m.PoolId)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *SwapAmountOutRoute) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *SwapAmountOutRoute) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SwapAmountOutRoute) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.TokenInDenom) > 0 { + i -= len(m.TokenInDenom) + copy(dAtA[i:], m.TokenInDenom) + i = encodeVarintSwapRoute(dAtA, i, uint64(len(m.TokenInDenom))) + i-- + dAtA[i] = 0x12 + } + if m.PoolId != 0 { + i = encodeVarintSwapRoute(dAtA, i, uint64(m.PoolId)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *SwapAmountInSplitRoute) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *SwapAmountInSplitRoute) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SwapAmountInSplitRoute) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size := m.TokenInAmount.Size() + i -= size + if _, err := m.TokenInAmount.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintSwapRoute(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + if len(m.Pools) > 0 { + for iNdEx := len(m.Pools) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Pools[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSwapRoute(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *SwapAmountOutSplitRoute) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *SwapAmountOutSplitRoute) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SwapAmountOutSplitRoute) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size := m.TokenOutAmount.Size() + i -= size + if _, err := m.TokenOutAmount.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintSwapRoute(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + if len(m.Pools) > 0 { + for iNdEx := len(m.Pools) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Pools[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSwapRoute(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func encodeVarintSwapRoute(dAtA []byte, offset int, v uint64) int { + offset -= sovSwapRoute(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *SwapAmountInRoute) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.PoolId != 0 { + n += 1 + sovSwapRoute(uint64(m.PoolId)) + } + l = len(m.TokenOutDenom) + if l > 0 { + n += 1 + l + sovSwapRoute(uint64(l)) + } + return n +} + +func (m *SwapAmountOutRoute) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.PoolId != 0 { + n += 1 + sovSwapRoute(uint64(m.PoolId)) + } + l = len(m.TokenInDenom) + if l > 0 { + n += 1 + l + sovSwapRoute(uint64(l)) + } + return n +} + +func (m *SwapAmountInSplitRoute) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Pools) > 0 { + for _, e := range m.Pools { + l = e.Size() + n += 1 + l + sovSwapRoute(uint64(l)) + } + } + l = m.TokenInAmount.Size() + n += 1 + l + sovSwapRoute(uint64(l)) + return n +} + +func (m *SwapAmountOutSplitRoute) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Pools) > 0 { + for _, e := range m.Pools { + l = e.Size() + n += 1 + l + sovSwapRoute(uint64(l)) + } + } + l = m.TokenOutAmount.Size() + n += 1 + l + sovSwapRoute(uint64(l)) + return n +} + +func sovSwapRoute(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozSwapRoute(x uint64) (n int) { + return sovSwapRoute(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *SwapAmountInRoute) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSwapRoute + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: SwapAmountInRoute: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SwapAmountInRoute: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field PoolId", wireType) + } + m.PoolId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSwapRoute + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.PoolId |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TokenOutDenom", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSwapRoute + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthSwapRoute + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSwapRoute + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.TokenOutDenom = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipSwapRoute(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthSwapRoute + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *SwapAmountOutRoute) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSwapRoute + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: SwapAmountOutRoute: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SwapAmountOutRoute: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field PoolId", wireType) + } + m.PoolId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSwapRoute + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.PoolId |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TokenInDenom", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSwapRoute + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthSwapRoute + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSwapRoute + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.TokenInDenom = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipSwapRoute(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthSwapRoute + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *SwapAmountInSplitRoute) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSwapRoute + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: SwapAmountInSplitRoute: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SwapAmountInSplitRoute: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pools", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSwapRoute + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthSwapRoute + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSwapRoute + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Pools = append(m.Pools, SwapAmountInRoute{}) + if err := m.Pools[len(m.Pools)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TokenInAmount", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSwapRoute + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthSwapRoute + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSwapRoute + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.TokenInAmount.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipSwapRoute(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthSwapRoute + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *SwapAmountOutSplitRoute) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSwapRoute + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: SwapAmountOutSplitRoute: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SwapAmountOutSplitRoute: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pools", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSwapRoute + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthSwapRoute + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSwapRoute + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Pools = append(m.Pools, SwapAmountOutRoute{}) + if err := m.Pools[len(m.Pools)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TokenOutAmount", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSwapRoute + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthSwapRoute + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSwapRoute + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.TokenOutAmount.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipSwapRoute(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthSwapRoute + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipSwapRoute(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowSwapRoute + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowSwapRoute + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowSwapRoute + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthSwapRoute + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupSwapRoute + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthSwapRoute + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthSwapRoute = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowSwapRoute = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupSwapRoute = fmt.Errorf("proto: unexpected end of group") +) diff --git a/tests/interchaintest/osmosis/poolmanager/types/tracked_volume.pb.go b/tests/interchaintest/osmosis/poolmanager/types/tracked_volume.pb.go new file mode 100644 index 00000000..4c6f5da6 --- /dev/null +++ b/tests/interchaintest/osmosis/poolmanager/types/tracked_volume.pb.go @@ -0,0 +1,336 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: osmosis/poolmanager/v1beta1/tracked_volume.proto + +package types + +import ( + fmt "fmt" + github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" + types "github.com/cosmos/cosmos-sdk/types" + _ "github.com/cosmos/gogoproto/gogoproto" + proto "github.com/cosmos/gogoproto/proto" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +type TrackedVolume struct { + Amount github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,1,rep,name=amount,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"amount"` +} + +func (m *TrackedVolume) Reset() { *m = TrackedVolume{} } +func (m *TrackedVolume) String() string { return proto.CompactTextString(m) } +func (*TrackedVolume) ProtoMessage() {} +func (*TrackedVolume) Descriptor() ([]byte, []int) { + return fileDescriptor_0a2e3e91de3baf1a, []int{0} +} +func (m *TrackedVolume) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *TrackedVolume) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_TrackedVolume.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *TrackedVolume) XXX_Merge(src proto.Message) { + xxx_messageInfo_TrackedVolume.Merge(m, src) +} +func (m *TrackedVolume) XXX_Size() int { + return m.Size() +} +func (m *TrackedVolume) XXX_DiscardUnknown() { + xxx_messageInfo_TrackedVolume.DiscardUnknown(m) +} + +var xxx_messageInfo_TrackedVolume proto.InternalMessageInfo + +func (m *TrackedVolume) GetAmount() github_com_cosmos_cosmos_sdk_types.Coins { + if m != nil { + return m.Amount + } + return nil +} + +func init() { + proto.RegisterType((*TrackedVolume)(nil), "osmosis.poolmanager.v1beta1.TrackedVolume") +} + +func init() { + proto.RegisterFile("osmosis/poolmanager/v1beta1/tracked_volume.proto", fileDescriptor_0a2e3e91de3baf1a) +} + +var fileDescriptor_0a2e3e91de3baf1a = []byte{ + // 255 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x32, 0xc8, 0x2f, 0xce, 0xcd, + 0x2f, 0xce, 0x2c, 0xd6, 0x2f, 0xc8, 0xcf, 0xcf, 0xc9, 0x4d, 0xcc, 0x4b, 0x4c, 0x4f, 0x2d, 0xd2, + 0x2f, 0x33, 0x4c, 0x4a, 0x2d, 0x49, 0x34, 0xd4, 0x2f, 0x29, 0x4a, 0x4c, 0xce, 0x4e, 0x4d, 0x89, + 0x2f, 0xcb, 0xcf, 0x29, 0xcd, 0x4d, 0xd5, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x92, 0x86, 0xea, + 0xd0, 0x43, 0xd2, 0xa1, 0x07, 0xd5, 0x21, 0x25, 0x92, 0x9e, 0x9f, 0x9e, 0x0f, 0x56, 0xa7, 0x0f, + 0x62, 0x41, 0xb4, 0x48, 0xc9, 0x25, 0x83, 0xf5, 0xe8, 0x27, 0x25, 0x16, 0xa7, 0xc2, 0x0d, 0x4f, + 0xce, 0xcf, 0xcc, 0x83, 0xc8, 0x2b, 0x95, 0x70, 0xf1, 0x86, 0x40, 0xac, 0x0a, 0x03, 0xdb, 0x24, + 0x94, 0xcc, 0xc5, 0x96, 0x98, 0x9b, 0x5f, 0x9a, 0x57, 0x22, 0xc1, 0xa8, 0xc0, 0xac, 0xc1, 0x6d, + 0x24, 0xa9, 0x07, 0x31, 0x41, 0x0f, 0x64, 0x02, 0xcc, 0x32, 0x3d, 0xe7, 0xfc, 0xcc, 0x3c, 0x27, + 0x83, 0x13, 0xf7, 0xe4, 0x19, 0x56, 0xdd, 0x97, 0xd7, 0x48, 0xcf, 0x2c, 0xc9, 0x28, 0x4d, 0xd2, + 0x4b, 0xce, 0xcf, 0xd5, 0x87, 0x5a, 0x07, 0xa1, 0x74, 0x8b, 0x53, 0xb2, 0xf5, 0x4b, 0x2a, 0x0b, + 0x52, 0x8b, 0xc1, 0x1a, 0x8a, 0x83, 0xa0, 0x46, 0x3b, 0x05, 0x9e, 0x78, 0x24, 0xc7, 0x78, 0xe1, + 0x91, 0x1c, 0xe3, 0x83, 0x47, 0x72, 0x8c, 0x13, 0x1e, 0xcb, 0x31, 0x5c, 0x78, 0x2c, 0xc7, 0x70, + 0xe3, 0xb1, 0x1c, 0x43, 0x94, 0x39, 0x92, 0x59, 0x50, 0xdf, 0xea, 0xe6, 0x24, 0x26, 0x15, 0xc3, + 0x38, 0xfa, 0x65, 0x46, 0x46, 0xfa, 0x15, 0x28, 0x41, 0x06, 0xb6, 0x20, 0x89, 0x0d, 0xec, 0x1f, + 0x63, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0xc1, 0xde, 0x04, 0x3f, 0x56, 0x01, 0x00, 0x00, +} + +func (m *TrackedVolume) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *TrackedVolume) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TrackedVolume) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Amount) > 0 { + for iNdEx := len(m.Amount) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Amount[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTrackedVolume(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func encodeVarintTrackedVolume(dAtA []byte, offset int, v uint64) int { + offset -= sovTrackedVolume(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *TrackedVolume) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Amount) > 0 { + for _, e := range m.Amount { + l = e.Size() + n += 1 + l + sovTrackedVolume(uint64(l)) + } + } + return n +} + +func sovTrackedVolume(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozTrackedVolume(x uint64) (n int) { + return sovTrackedVolume(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *TrackedVolume) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTrackedVolume + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: TrackedVolume: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: TrackedVolume: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Amount", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTrackedVolume + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTrackedVolume + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTrackedVolume + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Amount = append(m.Amount, types.Coin{}) + if err := m.Amount[len(m.Amount)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTrackedVolume(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTrackedVolume + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipTrackedVolume(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTrackedVolume + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTrackedVolume + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTrackedVolume + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthTrackedVolume + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupTrackedVolume + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthTrackedVolume + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthTrackedVolume = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowTrackedVolume = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupTrackedVolume = fmt.Errorf("proto: unexpected end of group") +) diff --git a/tests/interchaintest/osmosis/poolmanager/types/tx.pb.go b/tests/interchaintest/osmosis/poolmanager/types/tx.pb.go new file mode 100644 index 00000000..a5a844eb --- /dev/null +++ b/tests/interchaintest/osmosis/poolmanager/types/tx.pb.go @@ -0,0 +1,3090 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: osmosis/poolmanager/v1beta1/tx.proto + +package types + +import ( + context "context" + cosmossdk_io_math "cosmossdk.io/math" + fmt "fmt" + types "github.com/cosmos/cosmos-sdk/types" + _ "github.com/cosmos/cosmos-sdk/types/tx/amino" + _ "github.com/cosmos/gogoproto/gogoproto" + grpc1 "github.com/cosmos/gogoproto/grpc" + proto "github.com/cosmos/gogoproto/proto" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// ===================== MsgSwapExactAmountIn +type MsgSwapExactAmountIn struct { + Sender string `protobuf:"bytes,1,opt,name=sender,proto3" json:"sender,omitempty" yaml:"sender"` + Routes []SwapAmountInRoute `protobuf:"bytes,2,rep,name=routes,proto3" json:"routes"` + TokenIn types.Coin `protobuf:"bytes,3,opt,name=token_in,json=tokenIn,proto3" json:"token_in" yaml:"token_in"` + TokenOutMinAmount cosmossdk_io_math.Int `protobuf:"bytes,4,opt,name=token_out_min_amount,json=tokenOutMinAmount,proto3,customtype=cosmossdk.io/math.Int" json:"token_out_min_amount" yaml:"token_out_min_amount"` +} + +func (m *MsgSwapExactAmountIn) Reset() { *m = MsgSwapExactAmountIn{} } +func (m *MsgSwapExactAmountIn) String() string { return proto.CompactTextString(m) } +func (*MsgSwapExactAmountIn) ProtoMessage() {} +func (*MsgSwapExactAmountIn) Descriptor() ([]byte, []int) { + return fileDescriptor_acd130b4825d67dc, []int{0} +} +func (m *MsgSwapExactAmountIn) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgSwapExactAmountIn) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgSwapExactAmountIn.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgSwapExactAmountIn) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgSwapExactAmountIn.Merge(m, src) +} +func (m *MsgSwapExactAmountIn) XXX_Size() int { + return m.Size() +} +func (m *MsgSwapExactAmountIn) XXX_DiscardUnknown() { + xxx_messageInfo_MsgSwapExactAmountIn.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgSwapExactAmountIn proto.InternalMessageInfo + +func (m *MsgSwapExactAmountIn) GetSender() string { + if m != nil { + return m.Sender + } + return "" +} + +func (m *MsgSwapExactAmountIn) GetRoutes() []SwapAmountInRoute { + if m != nil { + return m.Routes + } + return nil +} + +func (m *MsgSwapExactAmountIn) GetTokenIn() types.Coin { + if m != nil { + return m.TokenIn + } + return types.Coin{} +} + +type MsgSwapExactAmountInResponse struct { + TokenOutAmount cosmossdk_io_math.Int `protobuf:"bytes,1,opt,name=token_out_amount,json=tokenOutAmount,proto3,customtype=cosmossdk.io/math.Int" json:"token_out_amount" yaml:"token_out_amount"` +} + +func (m *MsgSwapExactAmountInResponse) Reset() { *m = MsgSwapExactAmountInResponse{} } +func (m *MsgSwapExactAmountInResponse) String() string { return proto.CompactTextString(m) } +func (*MsgSwapExactAmountInResponse) ProtoMessage() {} +func (*MsgSwapExactAmountInResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_acd130b4825d67dc, []int{1} +} +func (m *MsgSwapExactAmountInResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgSwapExactAmountInResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgSwapExactAmountInResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgSwapExactAmountInResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgSwapExactAmountInResponse.Merge(m, src) +} +func (m *MsgSwapExactAmountInResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgSwapExactAmountInResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgSwapExactAmountInResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgSwapExactAmountInResponse proto.InternalMessageInfo + +// ===================== MsgSplitRouteSwapExactAmountIn +type MsgSplitRouteSwapExactAmountIn struct { + Sender string `protobuf:"bytes,1,opt,name=sender,proto3" json:"sender,omitempty" yaml:"sender"` + Routes []SwapAmountInSplitRoute `protobuf:"bytes,2,rep,name=routes,proto3" json:"routes"` + TokenInDenom string `protobuf:"bytes,3,opt,name=token_in_denom,json=tokenInDenom,proto3" json:"token_in_denom,omitempty" yaml:"token_in_denom"` + TokenOutMinAmount cosmossdk_io_math.Int `protobuf:"bytes,4,opt,name=token_out_min_amount,json=tokenOutMinAmount,proto3,customtype=cosmossdk.io/math.Int" json:"token_out_min_amount" yaml:"token_out_min_amount"` +} + +func (m *MsgSplitRouteSwapExactAmountIn) Reset() { *m = MsgSplitRouteSwapExactAmountIn{} } +func (m *MsgSplitRouteSwapExactAmountIn) String() string { return proto.CompactTextString(m) } +func (*MsgSplitRouteSwapExactAmountIn) ProtoMessage() {} +func (*MsgSplitRouteSwapExactAmountIn) Descriptor() ([]byte, []int) { + return fileDescriptor_acd130b4825d67dc, []int{2} +} +func (m *MsgSplitRouteSwapExactAmountIn) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgSplitRouteSwapExactAmountIn) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgSplitRouteSwapExactAmountIn.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgSplitRouteSwapExactAmountIn) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgSplitRouteSwapExactAmountIn.Merge(m, src) +} +func (m *MsgSplitRouteSwapExactAmountIn) XXX_Size() int { + return m.Size() +} +func (m *MsgSplitRouteSwapExactAmountIn) XXX_DiscardUnknown() { + xxx_messageInfo_MsgSplitRouteSwapExactAmountIn.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgSplitRouteSwapExactAmountIn proto.InternalMessageInfo + +func (m *MsgSplitRouteSwapExactAmountIn) GetSender() string { + if m != nil { + return m.Sender + } + return "" +} + +func (m *MsgSplitRouteSwapExactAmountIn) GetRoutes() []SwapAmountInSplitRoute { + if m != nil { + return m.Routes + } + return nil +} + +func (m *MsgSplitRouteSwapExactAmountIn) GetTokenInDenom() string { + if m != nil { + return m.TokenInDenom + } + return "" +} + +type MsgSplitRouteSwapExactAmountInResponse struct { + TokenOutAmount cosmossdk_io_math.Int `protobuf:"bytes,1,opt,name=token_out_amount,json=tokenOutAmount,proto3,customtype=cosmossdk.io/math.Int" json:"token_out_amount" yaml:"token_out_amount"` +} + +func (m *MsgSplitRouteSwapExactAmountInResponse) Reset() { + *m = MsgSplitRouteSwapExactAmountInResponse{} +} +func (m *MsgSplitRouteSwapExactAmountInResponse) String() string { return proto.CompactTextString(m) } +func (*MsgSplitRouteSwapExactAmountInResponse) ProtoMessage() {} +func (*MsgSplitRouteSwapExactAmountInResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_acd130b4825d67dc, []int{3} +} +func (m *MsgSplitRouteSwapExactAmountInResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgSplitRouteSwapExactAmountInResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgSplitRouteSwapExactAmountInResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgSplitRouteSwapExactAmountInResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgSplitRouteSwapExactAmountInResponse.Merge(m, src) +} +func (m *MsgSplitRouteSwapExactAmountInResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgSplitRouteSwapExactAmountInResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgSplitRouteSwapExactAmountInResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgSplitRouteSwapExactAmountInResponse proto.InternalMessageInfo + +// ===================== MsgSwapExactAmountOut +type MsgSwapExactAmountOut struct { + Sender string `protobuf:"bytes,1,opt,name=sender,proto3" json:"sender,omitempty" yaml:"sender"` + Routes []SwapAmountOutRoute `protobuf:"bytes,2,rep,name=routes,proto3" json:"routes"` + TokenInMaxAmount cosmossdk_io_math.Int `protobuf:"bytes,3,opt,name=token_in_max_amount,json=tokenInMaxAmount,proto3,customtype=cosmossdk.io/math.Int" json:"token_in_max_amount" yaml:"token_in_max_amount"` + TokenOut types.Coin `protobuf:"bytes,4,opt,name=token_out,json=tokenOut,proto3" json:"token_out" yaml:"token_out"` +} + +func (m *MsgSwapExactAmountOut) Reset() { *m = MsgSwapExactAmountOut{} } +func (m *MsgSwapExactAmountOut) String() string { return proto.CompactTextString(m) } +func (*MsgSwapExactAmountOut) ProtoMessage() {} +func (*MsgSwapExactAmountOut) Descriptor() ([]byte, []int) { + return fileDescriptor_acd130b4825d67dc, []int{4} +} +func (m *MsgSwapExactAmountOut) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgSwapExactAmountOut) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgSwapExactAmountOut.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgSwapExactAmountOut) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgSwapExactAmountOut.Merge(m, src) +} +func (m *MsgSwapExactAmountOut) XXX_Size() int { + return m.Size() +} +func (m *MsgSwapExactAmountOut) XXX_DiscardUnknown() { + xxx_messageInfo_MsgSwapExactAmountOut.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgSwapExactAmountOut proto.InternalMessageInfo + +func (m *MsgSwapExactAmountOut) GetSender() string { + if m != nil { + return m.Sender + } + return "" +} + +func (m *MsgSwapExactAmountOut) GetRoutes() []SwapAmountOutRoute { + if m != nil { + return m.Routes + } + return nil +} + +func (m *MsgSwapExactAmountOut) GetTokenOut() types.Coin { + if m != nil { + return m.TokenOut + } + return types.Coin{} +} + +type MsgSwapExactAmountOutResponse struct { + TokenInAmount cosmossdk_io_math.Int `protobuf:"bytes,1,opt,name=token_in_amount,json=tokenInAmount,proto3,customtype=cosmossdk.io/math.Int" json:"token_in_amount" yaml:"token_in_amount"` +} + +func (m *MsgSwapExactAmountOutResponse) Reset() { *m = MsgSwapExactAmountOutResponse{} } +func (m *MsgSwapExactAmountOutResponse) String() string { return proto.CompactTextString(m) } +func (*MsgSwapExactAmountOutResponse) ProtoMessage() {} +func (*MsgSwapExactAmountOutResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_acd130b4825d67dc, []int{5} +} +func (m *MsgSwapExactAmountOutResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgSwapExactAmountOutResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgSwapExactAmountOutResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgSwapExactAmountOutResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgSwapExactAmountOutResponse.Merge(m, src) +} +func (m *MsgSwapExactAmountOutResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgSwapExactAmountOutResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgSwapExactAmountOutResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgSwapExactAmountOutResponse proto.InternalMessageInfo + +// ===================== MsgSplitRouteSwapExactAmountOut +type MsgSplitRouteSwapExactAmountOut struct { + Sender string `protobuf:"bytes,1,opt,name=sender,proto3" json:"sender,omitempty" yaml:"sender"` + Routes []SwapAmountOutSplitRoute `protobuf:"bytes,2,rep,name=routes,proto3" json:"routes"` + TokenOutDenom string `protobuf:"bytes,3,opt,name=token_out_denom,json=tokenOutDenom,proto3" json:"token_out_denom,omitempty" yaml:"token_out_denom"` + TokenInMaxAmount cosmossdk_io_math.Int `protobuf:"bytes,4,opt,name=token_in_max_amount,json=tokenInMaxAmount,proto3,customtype=cosmossdk.io/math.Int" json:"token_in_max_amount" yaml:"token_in_max_amount"` +} + +func (m *MsgSplitRouteSwapExactAmountOut) Reset() { *m = MsgSplitRouteSwapExactAmountOut{} } +func (m *MsgSplitRouteSwapExactAmountOut) String() string { return proto.CompactTextString(m) } +func (*MsgSplitRouteSwapExactAmountOut) ProtoMessage() {} +func (*MsgSplitRouteSwapExactAmountOut) Descriptor() ([]byte, []int) { + return fileDescriptor_acd130b4825d67dc, []int{6} +} +func (m *MsgSplitRouteSwapExactAmountOut) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgSplitRouteSwapExactAmountOut) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgSplitRouteSwapExactAmountOut.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgSplitRouteSwapExactAmountOut) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgSplitRouteSwapExactAmountOut.Merge(m, src) +} +func (m *MsgSplitRouteSwapExactAmountOut) XXX_Size() int { + return m.Size() +} +func (m *MsgSplitRouteSwapExactAmountOut) XXX_DiscardUnknown() { + xxx_messageInfo_MsgSplitRouteSwapExactAmountOut.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgSplitRouteSwapExactAmountOut proto.InternalMessageInfo + +func (m *MsgSplitRouteSwapExactAmountOut) GetSender() string { + if m != nil { + return m.Sender + } + return "" +} + +func (m *MsgSplitRouteSwapExactAmountOut) GetRoutes() []SwapAmountOutSplitRoute { + if m != nil { + return m.Routes + } + return nil +} + +func (m *MsgSplitRouteSwapExactAmountOut) GetTokenOutDenom() string { + if m != nil { + return m.TokenOutDenom + } + return "" +} + +type MsgSplitRouteSwapExactAmountOutResponse struct { + TokenInAmount cosmossdk_io_math.Int `protobuf:"bytes,1,opt,name=token_in_amount,json=tokenInAmount,proto3,customtype=cosmossdk.io/math.Int" json:"token_in_amount" yaml:"token_in_amount"` +} + +func (m *MsgSplitRouteSwapExactAmountOutResponse) Reset() { + *m = MsgSplitRouteSwapExactAmountOutResponse{} +} +func (m *MsgSplitRouteSwapExactAmountOutResponse) String() string { return proto.CompactTextString(m) } +func (*MsgSplitRouteSwapExactAmountOutResponse) ProtoMessage() {} +func (*MsgSplitRouteSwapExactAmountOutResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_acd130b4825d67dc, []int{7} +} +func (m *MsgSplitRouteSwapExactAmountOutResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgSplitRouteSwapExactAmountOutResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgSplitRouteSwapExactAmountOutResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgSplitRouteSwapExactAmountOutResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgSplitRouteSwapExactAmountOutResponse.Merge(m, src) +} +func (m *MsgSplitRouteSwapExactAmountOutResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgSplitRouteSwapExactAmountOutResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgSplitRouteSwapExactAmountOutResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgSplitRouteSwapExactAmountOutResponse proto.InternalMessageInfo + +// ===================== MsgSetDenomPairTakerFee +type MsgSetDenomPairTakerFee struct { + Sender string `protobuf:"bytes,1,opt,name=sender,proto3" json:"sender,omitempty" yaml:"sender"` + DenomPairTakerFee []DenomPairTakerFee `protobuf:"bytes,2,rep,name=denom_pair_taker_fee,json=denomPairTakerFee,proto3" json:"denom_pair_taker_fee" yaml:"denom_pair_taker_fee"` +} + +func (m *MsgSetDenomPairTakerFee) Reset() { *m = MsgSetDenomPairTakerFee{} } +func (m *MsgSetDenomPairTakerFee) String() string { return proto.CompactTextString(m) } +func (*MsgSetDenomPairTakerFee) ProtoMessage() {} +func (*MsgSetDenomPairTakerFee) Descriptor() ([]byte, []int) { + return fileDescriptor_acd130b4825d67dc, []int{8} +} +func (m *MsgSetDenomPairTakerFee) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgSetDenomPairTakerFee) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgSetDenomPairTakerFee.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgSetDenomPairTakerFee) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgSetDenomPairTakerFee.Merge(m, src) +} +func (m *MsgSetDenomPairTakerFee) XXX_Size() int { + return m.Size() +} +func (m *MsgSetDenomPairTakerFee) XXX_DiscardUnknown() { + xxx_messageInfo_MsgSetDenomPairTakerFee.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgSetDenomPairTakerFee proto.InternalMessageInfo + +func (m *MsgSetDenomPairTakerFee) GetSender() string { + if m != nil { + return m.Sender + } + return "" +} + +func (m *MsgSetDenomPairTakerFee) GetDenomPairTakerFee() []DenomPairTakerFee { + if m != nil { + return m.DenomPairTakerFee + } + return nil +} + +type MsgSetDenomPairTakerFeeResponse struct { + Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"` +} + +func (m *MsgSetDenomPairTakerFeeResponse) Reset() { *m = MsgSetDenomPairTakerFeeResponse{} } +func (m *MsgSetDenomPairTakerFeeResponse) String() string { return proto.CompactTextString(m) } +func (*MsgSetDenomPairTakerFeeResponse) ProtoMessage() {} +func (*MsgSetDenomPairTakerFeeResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_acd130b4825d67dc, []int{9} +} +func (m *MsgSetDenomPairTakerFeeResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgSetDenomPairTakerFeeResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgSetDenomPairTakerFeeResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgSetDenomPairTakerFeeResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgSetDenomPairTakerFeeResponse.Merge(m, src) +} +func (m *MsgSetDenomPairTakerFeeResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgSetDenomPairTakerFeeResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgSetDenomPairTakerFeeResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgSetDenomPairTakerFeeResponse proto.InternalMessageInfo + +func (m *MsgSetDenomPairTakerFeeResponse) GetSuccess() bool { + if m != nil { + return m.Success + } + return false +} + +type DenomPairTakerFee struct { + // denom0 and denom1 get automatically lexigographically sorted + // when being stored, so the order of input here does not matter. + Denom0 string `protobuf:"bytes,1,opt,name=denom0,proto3" json:"denom0,omitempty" yaml:"denom0"` + Denom1 string `protobuf:"bytes,2,opt,name=denom1,proto3" json:"denom1,omitempty" yaml:"denom1"` + TakerFee cosmossdk_io_math.LegacyDec `protobuf:"bytes,3,opt,name=taker_fee,json=takerFee,proto3,customtype=cosmossdk.io/math.LegacyDec" json:"taker_fee" yaml:"taker_fee"` +} + +func (m *DenomPairTakerFee) Reset() { *m = DenomPairTakerFee{} } +func (m *DenomPairTakerFee) String() string { return proto.CompactTextString(m) } +func (*DenomPairTakerFee) ProtoMessage() {} +func (*DenomPairTakerFee) Descriptor() ([]byte, []int) { + return fileDescriptor_acd130b4825d67dc, []int{10} +} +func (m *DenomPairTakerFee) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *DenomPairTakerFee) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_DenomPairTakerFee.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *DenomPairTakerFee) XXX_Merge(src proto.Message) { + xxx_messageInfo_DenomPairTakerFee.Merge(m, src) +} +func (m *DenomPairTakerFee) XXX_Size() int { + return m.Size() +} +func (m *DenomPairTakerFee) XXX_DiscardUnknown() { + xxx_messageInfo_DenomPairTakerFee.DiscardUnknown(m) +} + +var xxx_messageInfo_DenomPairTakerFee proto.InternalMessageInfo + +func (m *DenomPairTakerFee) GetDenom0() string { + if m != nil { + return m.Denom0 + } + return "" +} + +func (m *DenomPairTakerFee) GetDenom1() string { + if m != nil { + return m.Denom1 + } + return "" +} + +func init() { + proto.RegisterType((*MsgSwapExactAmountIn)(nil), "osmosis.poolmanager.v1beta1.MsgSwapExactAmountIn") + proto.RegisterType((*MsgSwapExactAmountInResponse)(nil), "osmosis.poolmanager.v1beta1.MsgSwapExactAmountInResponse") + proto.RegisterType((*MsgSplitRouteSwapExactAmountIn)(nil), "osmosis.poolmanager.v1beta1.MsgSplitRouteSwapExactAmountIn") + proto.RegisterType((*MsgSplitRouteSwapExactAmountInResponse)(nil), "osmosis.poolmanager.v1beta1.MsgSplitRouteSwapExactAmountInResponse") + proto.RegisterType((*MsgSwapExactAmountOut)(nil), "osmosis.poolmanager.v1beta1.MsgSwapExactAmountOut") + proto.RegisterType((*MsgSwapExactAmountOutResponse)(nil), "osmosis.poolmanager.v1beta1.MsgSwapExactAmountOutResponse") + proto.RegisterType((*MsgSplitRouteSwapExactAmountOut)(nil), "osmosis.poolmanager.v1beta1.MsgSplitRouteSwapExactAmountOut") + proto.RegisterType((*MsgSplitRouteSwapExactAmountOutResponse)(nil), "osmosis.poolmanager.v1beta1.MsgSplitRouteSwapExactAmountOutResponse") + proto.RegisterType((*MsgSetDenomPairTakerFee)(nil), "osmosis.poolmanager.v1beta1.MsgSetDenomPairTakerFee") + proto.RegisterType((*MsgSetDenomPairTakerFeeResponse)(nil), "osmosis.poolmanager.v1beta1.MsgSetDenomPairTakerFeeResponse") + proto.RegisterType((*DenomPairTakerFee)(nil), "osmosis.poolmanager.v1beta1.DenomPairTakerFee") +} + +func init() { + proto.RegisterFile("osmosis/poolmanager/v1beta1/tx.proto", fileDescriptor_acd130b4825d67dc) +} + +var fileDescriptor_acd130b4825d67dc = []byte{ + // 991 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x97, 0x4d, 0x6f, 0x1b, 0x45, + 0x18, 0xc7, 0x33, 0x4e, 0x48, 0x93, 0x29, 0x6d, 0xe3, 0xc5, 0x21, 0xae, 0x53, 0xec, 0x68, 0x5b, + 0x81, 0x83, 0xd8, 0x5d, 0xec, 0x56, 0x2a, 0x38, 0x91, 0x10, 0x6e, 0x40, 0x8a, 0x54, 0xcb, 0xed, + 0xd2, 0x13, 0x97, 0xd5, 0xd8, 0x19, 0xdc, 0x25, 0xd9, 0x1d, 0xcb, 0x33, 0xdb, 0x3a, 0x37, 0x40, + 0x3d, 0x45, 0x1c, 0xf8, 0x06, 0x48, 0x7c, 0x02, 0xbe, 0x01, 0xd7, 0x1e, 0x7b, 0x44, 0x1c, 0x2c, + 0x94, 0x20, 0x71, 0xcf, 0x09, 0x09, 0x04, 0x68, 0x5e, 0x76, 0x6d, 0xaf, 0x37, 0x7e, 0x49, 0xd4, + 0x5c, 0x22, 0xef, 0xec, 0xf3, 0xfa, 0x7f, 0x7e, 0xfb, 0x68, 0x02, 0xef, 0x10, 0xea, 0x11, 0xea, + 0x52, 0xab, 0x4d, 0xc8, 0x81, 0x87, 0x7c, 0xd4, 0xc2, 0x1d, 0xeb, 0x59, 0xa9, 0x81, 0x19, 0x2a, + 0x59, 0xac, 0x6b, 0xb6, 0x3b, 0x84, 0x11, 0x6d, 0x5d, 0x59, 0x99, 0x03, 0x56, 0xa6, 0xb2, 0xca, + 0x65, 0x5a, 0xa4, 0x45, 0x84, 0x9d, 0xc5, 0x7f, 0x49, 0x97, 0x5c, 0x1a, 0x79, 0xae, 0x4f, 0x2c, + 0xf1, 0x57, 0x1d, 0xe5, 0x9b, 0x22, 0x8c, 0xd5, 0x40, 0x14, 0x47, 0x39, 0x9a, 0xc4, 0xf5, 0xd5, + 0xfb, 0x0f, 0xc6, 0xd5, 0x42, 0x9f, 0xa3, 0xb6, 0xd3, 0x21, 0x01, 0xc3, 0xd2, 0x5a, 0xff, 0x27, + 0x05, 0x33, 0x35, 0xda, 0xfa, 0xe2, 0x39, 0x6a, 0x7f, 0xd6, 0x45, 0x4d, 0xf6, 0xa9, 0x47, 0x02, + 0x9f, 0xed, 0xfa, 0xda, 0x26, 0x5c, 0xa4, 0xd8, 0xdf, 0xc3, 0x9d, 0x2c, 0xd8, 0x00, 0xc5, 0xe5, + 0x6a, 0xfa, 0xb4, 0x57, 0xb8, 0x76, 0x88, 0xbc, 0x83, 0x8a, 0x2e, 0xcf, 0x75, 0x5b, 0x19, 0x68, + 0x0f, 0xe1, 0xa2, 0x08, 0x49, 0xb3, 0xa9, 0x8d, 0xf9, 0xe2, 0xd5, 0xb2, 0x69, 0x8e, 0x69, 0xd4, + 0xe4, 0xa9, 0xc2, 0x2c, 0x36, 0x77, 0xab, 0x2e, 0xbc, 0xec, 0x15, 0xe6, 0x6c, 0x15, 0x43, 0xab, + 0xc1, 0x25, 0x46, 0xf6, 0xb1, 0xef, 0xb8, 0x7e, 0x76, 0x7e, 0x03, 0x14, 0xaf, 0x96, 0x6f, 0x9a, + 0xb2, 0x65, 0x93, 0xb7, 0x1c, 0xc5, 0x79, 0x40, 0x5c, 0xbf, 0xba, 0xc6, 0x5d, 0x4f, 0x7b, 0x85, + 0x1b, 0xb2, 0xb2, 0xd0, 0x51, 0xb7, 0xaf, 0x88, 0x9f, 0xbb, 0xbe, 0xe6, 0xc1, 0x8c, 0x3c, 0x25, + 0x01, 0x73, 0x3c, 0xd7, 0x77, 0x90, 0xc8, 0x9d, 0x5d, 0x10, 0x5d, 0x6d, 0x73, 0xff, 0xdf, 0x7a, + 0x85, 0x55, 0x99, 0x81, 0xee, 0xed, 0x9b, 0x2e, 0xb1, 0x3c, 0xc4, 0x9e, 0x9a, 0xbb, 0x3e, 0x3b, + 0xed, 0x15, 0xd6, 0x07, 0x03, 0x0f, 0x87, 0xd0, 0xed, 0xb4, 0x38, 0xae, 0x07, 0xac, 0xe6, 0xfa, + 0xb2, 0xa5, 0x8a, 0x71, 0xf4, 0xe7, 0xcf, 0xef, 0x17, 0x93, 0x46, 0xc0, 0xa5, 0x37, 0x30, 0xd7, + 0xd8, 0x90, 0xfe, 0x86, 0xeb, 0xeb, 0xdf, 0x01, 0x78, 0x2b, 0x49, 0x7e, 0x1b, 0xd3, 0x36, 0xf1, + 0x29, 0xd6, 0x1a, 0x70, 0xa5, 0x9f, 0x5b, 0x95, 0x2e, 0x07, 0xf2, 0xd1, 0xa4, 0xd2, 0xd7, 0xe2, + 0xa5, 0x87, 0x65, 0x5f, 0x0f, 0xcb, 0x96, 0xd9, 0xf4, 0xbf, 0x52, 0x30, 0xcf, 0x8b, 0x68, 0x1f, + 0xb8, 0x4c, 0x4c, 0xe4, 0x42, 0x34, 0x3c, 0x8e, 0xd1, 0x70, 0x77, 0x6a, 0x1a, 0xfa, 0x05, 0xc4, + 0x90, 0xf8, 0x04, 0x5e, 0x0f, 0x27, 0xeb, 0xec, 0x61, 0x9f, 0x78, 0x02, 0x8c, 0xe5, 0xea, 0xcd, + 0xd3, 0x5e, 0x61, 0x75, 0x78, 0xf2, 0xf2, 0xbd, 0x6e, 0xbf, 0xa9, 0xe6, 0xbf, 0xc3, 0x1f, 0x2f, + 0x1b, 0x82, 0x22, 0x87, 0xe0, 0x76, 0x22, 0x04, 0xbc, 0xc5, 0x81, 0xf9, 0x7f, 0x0f, 0xe0, 0xbb, + 0xe3, 0xa5, 0xbf, 0x54, 0x12, 0xfe, 0x4b, 0xc1, 0xd5, 0x51, 0x1c, 0xeb, 0x01, 0x9b, 0x05, 0x80, + 0x5a, 0x0c, 0x00, 0x6b, 0x4a, 0x00, 0xea, 0x41, 0xe2, 0xf0, 0xbf, 0x86, 0x6f, 0x45, 0xc3, 0xf5, + 0x50, 0x37, 0x6c, 0x5d, 0x12, 0xb0, 0x35, 0xa9, 0xf5, 0x5c, 0x0c, 0x8f, 0x7e, 0x04, 0xdd, 0x5e, + 0x51, 0x8c, 0xd4, 0x50, 0x57, 0x56, 0xa0, 0x3d, 0x82, 0xcb, 0x91, 0x48, 0x02, 0x8e, 0xb1, 0xcb, + 0x27, 0xab, 0x96, 0xcf, 0x4a, 0x4c, 0x5e, 0xdd, 0x5e, 0x0a, 0x75, 0xad, 0x98, 0x1c, 0x85, 0xcd, + 0xe9, 0xf6, 0x01, 0x77, 0xfd, 0x06, 0xc0, 0x77, 0x12, 0x27, 0x10, 0x71, 0xe0, 0xc0, 0x1b, 0x51, + 0x37, 0x43, 0x18, 0xdc, 0x9f, 0xa4, 0xc5, 0xdb, 0x31, 0x2d, 0x42, 0x1d, 0xae, 0x29, 0x1d, 0x14, + 0x04, 0x7f, 0xa7, 0x60, 0x61, 0x1c, 0x93, 0x33, 0xe2, 0x60, 0xc7, 0x70, 0xb8, 0x37, 0x3d, 0x0e, + 0x67, 0x2e, 0x84, 0x6a, 0xa8, 0x01, 0x87, 0x79, 0x70, 0x23, 0xe4, 0xe2, 0x6d, 0x46, 0x06, 0x61, + 0x9b, 0xf5, 0x80, 0xc9, 0x9d, 0x70, 0x06, 0x57, 0x0b, 0xaf, 0x81, 0xab, 0xca, 0x26, 0xa7, 0xe0, + 0xce, 0xc4, 0x85, 0xc0, 0x01, 0x38, 0x02, 0xf0, 0xbd, 0x09, 0xea, 0x5f, 0x1e, 0x0a, 0xff, 0x02, + 0xb8, 0xc6, 0x8b, 0xc1, 0x52, 0xb3, 0x47, 0xc8, 0xed, 0x3c, 0x41, 0xfb, 0xb8, 0xf3, 0x39, 0xc6, + 0xb3, 0x20, 0xf0, 0x02, 0xc0, 0x8c, 0x18, 0x82, 0xd3, 0x46, 0x6e, 0xc7, 0x61, 0x3c, 0x84, 0xf3, + 0x15, 0xc6, 0x53, 0xdd, 0x17, 0x46, 0x32, 0x57, 0x6f, 0xab, 0xef, 0x4e, 0xad, 0xe5, 0xa4, 0xc8, + 0xba, 0x9d, 0xde, 0x8b, 0xfb, 0x55, 0x4a, 0x7c, 0x0a, 0x89, 0xd7, 0x23, 0x8a, 0x99, 0x21, 0xec, + 0x0d, 0x1e, 0xc6, 0x10, 0x61, 0x0c, 0x1e, 0x66, 0x4b, 0x7e, 0x0a, 0x09, 0xfd, 0x47, 0x43, 0xc8, + 0xc2, 0x2b, 0x34, 0x68, 0x36, 0x31, 0xa5, 0x42, 0x88, 0x25, 0x3b, 0x7c, 0xd4, 0x7f, 0x01, 0x30, + 0x9d, 0xa8, 0x9b, 0x48, 0xf5, 0xe1, 0xa8, 0x6e, 0xf2, 0x5c, 0xb7, 0x95, 0x41, 0x64, 0x5a, 0xca, + 0xa6, 0x12, 0x4d, 0x4b, 0xa1, 0x69, 0x49, 0x7b, 0x02, 0x97, 0xfb, 0xb2, 0xce, 0x0f, 0x41, 0xb0, + 0x3e, 0x0a, 0xc1, 0x43, 0xdc, 0x42, 0xcd, 0xc3, 0x1d, 0xdc, 0x1c, 0xd8, 0x5e, 0x7d, 0xe9, 0x96, + 0x98, 0xaa, 0xb5, 0xfc, 0xc7, 0x1b, 0x70, 0xbe, 0x46, 0x5b, 0xda, 0xb7, 0x00, 0xa6, 0x47, 0x2f, + 0x05, 0xa5, 0xb1, 0x73, 0x4b, 0xba, 0xd6, 0xe4, 0x3e, 0x9e, 0xd9, 0x25, 0xd2, 0xf9, 0x05, 0x80, + 0x5a, 0xc2, 0x26, 0x2a, 0xcf, 0x18, 0xb1, 0x1e, 0xb0, 0x5c, 0x65, 0x76, 0x9f, 0xa8, 0x8c, 0x1f, + 0x01, 0x5c, 0x1f, 0x77, 0x53, 0xda, 0x9a, 0x18, 0xfb, 0x6c, 0xe7, 0xdc, 0x83, 0x0b, 0x38, 0x47, + 0x15, 0xfe, 0x04, 0xe0, 0xad, 0xb1, 0xcb, 0x7b, 0xfb, 0xdc, 0x59, 0xb8, 0x78, 0x3b, 0x17, 0xf1, + 0x8e, 0x8a, 0x3c, 0x02, 0x30, 0x93, 0xb8, 0x56, 0xee, 0x4d, 0x0c, 0x9f, 0xe0, 0x95, 0xdb, 0x3e, + 0x8f, 0x57, 0x58, 0x4c, 0xf5, 0xf1, 0xcb, 0xe3, 0x3c, 0x78, 0x75, 0x9c, 0x07, 0xbf, 0x1f, 0xe7, + 0xc1, 0x0f, 0x27, 0xf9, 0xb9, 0x57, 0x27, 0xf9, 0xb9, 0x5f, 0x4f, 0xf2, 0x73, 0x5f, 0xde, 0x6f, + 0xb9, 0xec, 0x69, 0xd0, 0x30, 0x9b, 0xc4, 0xb3, 0x54, 0x06, 0xe3, 0x00, 0x35, 0x68, 0xf8, 0x60, + 0x3d, 0x2b, 0x97, 0xad, 0xee, 0xd0, 0x2e, 0x61, 0x87, 0x6d, 0x4c, 0x1b, 0x8b, 0xe2, 0xdf, 0xab, + 0xbb, 0xff, 0x07, 0x00, 0x00, 0xff, 0xff, 0x96, 0x15, 0x36, 0xd8, 0x1a, 0x0e, 0x00, 0x00, +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// MsgClient is the client API for Msg service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type MsgClient interface { + SwapExactAmountIn(ctx context.Context, in *MsgSwapExactAmountIn, opts ...grpc.CallOption) (*MsgSwapExactAmountInResponse, error) + SwapExactAmountOut(ctx context.Context, in *MsgSwapExactAmountOut, opts ...grpc.CallOption) (*MsgSwapExactAmountOutResponse, error) + SplitRouteSwapExactAmountIn(ctx context.Context, in *MsgSplitRouteSwapExactAmountIn, opts ...grpc.CallOption) (*MsgSplitRouteSwapExactAmountInResponse, error) + SplitRouteSwapExactAmountOut(ctx context.Context, in *MsgSplitRouteSwapExactAmountOut, opts ...grpc.CallOption) (*MsgSplitRouteSwapExactAmountOutResponse, error) + SetDenomPairTakerFee(ctx context.Context, in *MsgSetDenomPairTakerFee, opts ...grpc.CallOption) (*MsgSetDenomPairTakerFeeResponse, error) +} + +type msgClient struct { + cc grpc1.ClientConn +} + +func NewMsgClient(cc grpc1.ClientConn) MsgClient { + return &msgClient{cc} +} + +func (c *msgClient) SwapExactAmountIn(ctx context.Context, in *MsgSwapExactAmountIn, opts ...grpc.CallOption) (*MsgSwapExactAmountInResponse, error) { + out := new(MsgSwapExactAmountInResponse) + err := c.cc.Invoke(ctx, "/osmosis.poolmanager.v1beta1.Msg/SwapExactAmountIn", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) SwapExactAmountOut(ctx context.Context, in *MsgSwapExactAmountOut, opts ...grpc.CallOption) (*MsgSwapExactAmountOutResponse, error) { + out := new(MsgSwapExactAmountOutResponse) + err := c.cc.Invoke(ctx, "/osmosis.poolmanager.v1beta1.Msg/SwapExactAmountOut", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) SplitRouteSwapExactAmountIn(ctx context.Context, in *MsgSplitRouteSwapExactAmountIn, opts ...grpc.CallOption) (*MsgSplitRouteSwapExactAmountInResponse, error) { + out := new(MsgSplitRouteSwapExactAmountInResponse) + err := c.cc.Invoke(ctx, "/osmosis.poolmanager.v1beta1.Msg/SplitRouteSwapExactAmountIn", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) SplitRouteSwapExactAmountOut(ctx context.Context, in *MsgSplitRouteSwapExactAmountOut, opts ...grpc.CallOption) (*MsgSplitRouteSwapExactAmountOutResponse, error) { + out := new(MsgSplitRouteSwapExactAmountOutResponse) + err := c.cc.Invoke(ctx, "/osmosis.poolmanager.v1beta1.Msg/SplitRouteSwapExactAmountOut", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) SetDenomPairTakerFee(ctx context.Context, in *MsgSetDenomPairTakerFee, opts ...grpc.CallOption) (*MsgSetDenomPairTakerFeeResponse, error) { + out := new(MsgSetDenomPairTakerFeeResponse) + err := c.cc.Invoke(ctx, "/osmosis.poolmanager.v1beta1.Msg/SetDenomPairTakerFee", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// MsgServer is the server API for Msg service. +type MsgServer interface { + SwapExactAmountIn(context.Context, *MsgSwapExactAmountIn) (*MsgSwapExactAmountInResponse, error) + SwapExactAmountOut(context.Context, *MsgSwapExactAmountOut) (*MsgSwapExactAmountOutResponse, error) + SplitRouteSwapExactAmountIn(context.Context, *MsgSplitRouteSwapExactAmountIn) (*MsgSplitRouteSwapExactAmountInResponse, error) + SplitRouteSwapExactAmountOut(context.Context, *MsgSplitRouteSwapExactAmountOut) (*MsgSplitRouteSwapExactAmountOutResponse, error) + SetDenomPairTakerFee(context.Context, *MsgSetDenomPairTakerFee) (*MsgSetDenomPairTakerFeeResponse, error) +} + +// UnimplementedMsgServer can be embedded to have forward compatible implementations. +type UnimplementedMsgServer struct { +} + +func (*UnimplementedMsgServer) SwapExactAmountIn(ctx context.Context, req *MsgSwapExactAmountIn) (*MsgSwapExactAmountInResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method SwapExactAmountIn not implemented") +} +func (*UnimplementedMsgServer) SwapExactAmountOut(ctx context.Context, req *MsgSwapExactAmountOut) (*MsgSwapExactAmountOutResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method SwapExactAmountOut not implemented") +} +func (*UnimplementedMsgServer) SplitRouteSwapExactAmountIn(ctx context.Context, req *MsgSplitRouteSwapExactAmountIn) (*MsgSplitRouteSwapExactAmountInResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method SplitRouteSwapExactAmountIn not implemented") +} +func (*UnimplementedMsgServer) SplitRouteSwapExactAmountOut(ctx context.Context, req *MsgSplitRouteSwapExactAmountOut) (*MsgSplitRouteSwapExactAmountOutResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method SplitRouteSwapExactAmountOut not implemented") +} +func (*UnimplementedMsgServer) SetDenomPairTakerFee(ctx context.Context, req *MsgSetDenomPairTakerFee) (*MsgSetDenomPairTakerFeeResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method SetDenomPairTakerFee not implemented") +} + +func RegisterMsgServer(s grpc1.Server, srv MsgServer) { + s.RegisterService(&_Msg_serviceDesc, srv) +} + +func _Msg_SwapExactAmountIn_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgSwapExactAmountIn) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).SwapExactAmountIn(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/osmosis.poolmanager.v1beta1.Msg/SwapExactAmountIn", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).SwapExactAmountIn(ctx, req.(*MsgSwapExactAmountIn)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_SwapExactAmountOut_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgSwapExactAmountOut) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).SwapExactAmountOut(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/osmosis.poolmanager.v1beta1.Msg/SwapExactAmountOut", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).SwapExactAmountOut(ctx, req.(*MsgSwapExactAmountOut)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_SplitRouteSwapExactAmountIn_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgSplitRouteSwapExactAmountIn) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).SplitRouteSwapExactAmountIn(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/osmosis.poolmanager.v1beta1.Msg/SplitRouteSwapExactAmountIn", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).SplitRouteSwapExactAmountIn(ctx, req.(*MsgSplitRouteSwapExactAmountIn)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_SplitRouteSwapExactAmountOut_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgSplitRouteSwapExactAmountOut) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).SplitRouteSwapExactAmountOut(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/osmosis.poolmanager.v1beta1.Msg/SplitRouteSwapExactAmountOut", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).SplitRouteSwapExactAmountOut(ctx, req.(*MsgSplitRouteSwapExactAmountOut)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_SetDenomPairTakerFee_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgSetDenomPairTakerFee) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).SetDenomPairTakerFee(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/osmosis.poolmanager.v1beta1.Msg/SetDenomPairTakerFee", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).SetDenomPairTakerFee(ctx, req.(*MsgSetDenomPairTakerFee)) + } + return interceptor(ctx, in, info, handler) +} + +var _Msg_serviceDesc = grpc.ServiceDesc{ + ServiceName: "osmosis.poolmanager.v1beta1.Msg", + HandlerType: (*MsgServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "SwapExactAmountIn", + Handler: _Msg_SwapExactAmountIn_Handler, + }, + { + MethodName: "SwapExactAmountOut", + Handler: _Msg_SwapExactAmountOut_Handler, + }, + { + MethodName: "SplitRouteSwapExactAmountIn", + Handler: _Msg_SplitRouteSwapExactAmountIn_Handler, + }, + { + MethodName: "SplitRouteSwapExactAmountOut", + Handler: _Msg_SplitRouteSwapExactAmountOut_Handler, + }, + { + MethodName: "SetDenomPairTakerFee", + Handler: _Msg_SetDenomPairTakerFee_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "osmosis/poolmanager/v1beta1/tx.proto", +} + +func (m *MsgSwapExactAmountIn) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgSwapExactAmountIn) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgSwapExactAmountIn) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size := m.TokenOutMinAmount.Size() + i -= size + if _, err := m.TokenOutMinAmount.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + { + size, err := m.TokenIn.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + if len(m.Routes) > 0 { + for iNdEx := len(m.Routes) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Routes[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if len(m.Sender) > 0 { + i -= len(m.Sender) + copy(dAtA[i:], m.Sender) + i = encodeVarintTx(dAtA, i, uint64(len(m.Sender))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgSwapExactAmountInResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgSwapExactAmountInResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgSwapExactAmountInResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size := m.TokenOutAmount.Size() + i -= size + if _, err := m.TokenOutAmount.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *MsgSplitRouteSwapExactAmountIn) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgSplitRouteSwapExactAmountIn) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgSplitRouteSwapExactAmountIn) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size := m.TokenOutMinAmount.Size() + i -= size + if _, err := m.TokenOutMinAmount.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + if len(m.TokenInDenom) > 0 { + i -= len(m.TokenInDenom) + copy(dAtA[i:], m.TokenInDenom) + i = encodeVarintTx(dAtA, i, uint64(len(m.TokenInDenom))) + i-- + dAtA[i] = 0x1a + } + if len(m.Routes) > 0 { + for iNdEx := len(m.Routes) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Routes[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if len(m.Sender) > 0 { + i -= len(m.Sender) + copy(dAtA[i:], m.Sender) + i = encodeVarintTx(dAtA, i, uint64(len(m.Sender))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgSplitRouteSwapExactAmountInResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgSplitRouteSwapExactAmountInResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgSplitRouteSwapExactAmountInResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size := m.TokenOutAmount.Size() + i -= size + if _, err := m.TokenOutAmount.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *MsgSwapExactAmountOut) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgSwapExactAmountOut) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgSwapExactAmountOut) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.TokenOut.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + { + size := m.TokenInMaxAmount.Size() + i -= size + if _, err := m.TokenInMaxAmount.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + if len(m.Routes) > 0 { + for iNdEx := len(m.Routes) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Routes[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if len(m.Sender) > 0 { + i -= len(m.Sender) + copy(dAtA[i:], m.Sender) + i = encodeVarintTx(dAtA, i, uint64(len(m.Sender))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgSwapExactAmountOutResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgSwapExactAmountOutResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgSwapExactAmountOutResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size := m.TokenInAmount.Size() + i -= size + if _, err := m.TokenInAmount.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *MsgSplitRouteSwapExactAmountOut) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgSplitRouteSwapExactAmountOut) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgSplitRouteSwapExactAmountOut) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size := m.TokenInMaxAmount.Size() + i -= size + if _, err := m.TokenInMaxAmount.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + if len(m.TokenOutDenom) > 0 { + i -= len(m.TokenOutDenom) + copy(dAtA[i:], m.TokenOutDenom) + i = encodeVarintTx(dAtA, i, uint64(len(m.TokenOutDenom))) + i-- + dAtA[i] = 0x1a + } + if len(m.Routes) > 0 { + for iNdEx := len(m.Routes) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Routes[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if len(m.Sender) > 0 { + i -= len(m.Sender) + copy(dAtA[i:], m.Sender) + i = encodeVarintTx(dAtA, i, uint64(len(m.Sender))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgSplitRouteSwapExactAmountOutResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgSplitRouteSwapExactAmountOutResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgSplitRouteSwapExactAmountOutResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size := m.TokenInAmount.Size() + i -= size + if _, err := m.TokenInAmount.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *MsgSetDenomPairTakerFee) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgSetDenomPairTakerFee) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgSetDenomPairTakerFee) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.DenomPairTakerFee) > 0 { + for iNdEx := len(m.DenomPairTakerFee) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.DenomPairTakerFee[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if len(m.Sender) > 0 { + i -= len(m.Sender) + copy(dAtA[i:], m.Sender) + i = encodeVarintTx(dAtA, i, uint64(len(m.Sender))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgSetDenomPairTakerFeeResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgSetDenomPairTakerFeeResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgSetDenomPairTakerFeeResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Success { + i-- + if m.Success { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *DenomPairTakerFee) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *DenomPairTakerFee) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *DenomPairTakerFee) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size := m.TakerFee.Size() + i -= size + if _, err := m.TakerFee.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + if len(m.Denom1) > 0 { + i -= len(m.Denom1) + copy(dAtA[i:], m.Denom1) + i = encodeVarintTx(dAtA, i, uint64(len(m.Denom1))) + i-- + dAtA[i] = 0x12 + } + if len(m.Denom0) > 0 { + i -= len(m.Denom0) + copy(dAtA[i:], m.Denom0) + i = encodeVarintTx(dAtA, i, uint64(len(m.Denom0))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintTx(dAtA []byte, offset int, v uint64) int { + offset -= sovTx(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *MsgSwapExactAmountIn) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Sender) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if len(m.Routes) > 0 { + for _, e := range m.Routes { + l = e.Size() + n += 1 + l + sovTx(uint64(l)) + } + } + l = m.TokenIn.Size() + n += 1 + l + sovTx(uint64(l)) + l = m.TokenOutMinAmount.Size() + n += 1 + l + sovTx(uint64(l)) + return n +} + +func (m *MsgSwapExactAmountInResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.TokenOutAmount.Size() + n += 1 + l + sovTx(uint64(l)) + return n +} + +func (m *MsgSplitRouteSwapExactAmountIn) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Sender) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if len(m.Routes) > 0 { + for _, e := range m.Routes { + l = e.Size() + n += 1 + l + sovTx(uint64(l)) + } + } + l = len(m.TokenInDenom) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = m.TokenOutMinAmount.Size() + n += 1 + l + sovTx(uint64(l)) + return n +} + +func (m *MsgSplitRouteSwapExactAmountInResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.TokenOutAmount.Size() + n += 1 + l + sovTx(uint64(l)) + return n +} + +func (m *MsgSwapExactAmountOut) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Sender) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if len(m.Routes) > 0 { + for _, e := range m.Routes { + l = e.Size() + n += 1 + l + sovTx(uint64(l)) + } + } + l = m.TokenInMaxAmount.Size() + n += 1 + l + sovTx(uint64(l)) + l = m.TokenOut.Size() + n += 1 + l + sovTx(uint64(l)) + return n +} + +func (m *MsgSwapExactAmountOutResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.TokenInAmount.Size() + n += 1 + l + sovTx(uint64(l)) + return n +} + +func (m *MsgSplitRouteSwapExactAmountOut) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Sender) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if len(m.Routes) > 0 { + for _, e := range m.Routes { + l = e.Size() + n += 1 + l + sovTx(uint64(l)) + } + } + l = len(m.TokenOutDenom) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = m.TokenInMaxAmount.Size() + n += 1 + l + sovTx(uint64(l)) + return n +} + +func (m *MsgSplitRouteSwapExactAmountOutResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.TokenInAmount.Size() + n += 1 + l + sovTx(uint64(l)) + return n +} + +func (m *MsgSetDenomPairTakerFee) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Sender) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if len(m.DenomPairTakerFee) > 0 { + for _, e := range m.DenomPairTakerFee { + l = e.Size() + n += 1 + l + sovTx(uint64(l)) + } + } + return n +} + +func (m *MsgSetDenomPairTakerFeeResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Success { + n += 2 + } + return n +} + +func (m *DenomPairTakerFee) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Denom0) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.Denom1) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = m.TakerFee.Size() + n += 1 + l + sovTx(uint64(l)) + return n +} + +func sovTx(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozTx(x uint64) (n int) { + return sovTx(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *MsgSwapExactAmountIn) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgSwapExactAmountIn: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgSwapExactAmountIn: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Sender", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Sender = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Routes", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Routes = append(m.Routes, SwapAmountInRoute{}) + if err := m.Routes[len(m.Routes)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TokenIn", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.TokenIn.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TokenOutMinAmount", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.TokenOutMinAmount.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgSwapExactAmountInResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgSwapExactAmountInResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgSwapExactAmountInResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TokenOutAmount", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.TokenOutAmount.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgSplitRouteSwapExactAmountIn) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgSplitRouteSwapExactAmountIn: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgSplitRouteSwapExactAmountIn: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Sender", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Sender = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Routes", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Routes = append(m.Routes, SwapAmountInSplitRoute{}) + if err := m.Routes[len(m.Routes)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TokenInDenom", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.TokenInDenom = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TokenOutMinAmount", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.TokenOutMinAmount.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgSplitRouteSwapExactAmountInResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgSplitRouteSwapExactAmountInResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgSplitRouteSwapExactAmountInResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TokenOutAmount", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.TokenOutAmount.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgSwapExactAmountOut) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgSwapExactAmountOut: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgSwapExactAmountOut: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Sender", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Sender = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Routes", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Routes = append(m.Routes, SwapAmountOutRoute{}) + if err := m.Routes[len(m.Routes)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TokenInMaxAmount", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.TokenInMaxAmount.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TokenOut", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.TokenOut.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgSwapExactAmountOutResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgSwapExactAmountOutResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgSwapExactAmountOutResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TokenInAmount", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.TokenInAmount.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgSplitRouteSwapExactAmountOut) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgSplitRouteSwapExactAmountOut: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgSplitRouteSwapExactAmountOut: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Sender", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Sender = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Routes", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Routes = append(m.Routes, SwapAmountOutSplitRoute{}) + if err := m.Routes[len(m.Routes)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TokenOutDenom", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.TokenOutDenom = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TokenInMaxAmount", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.TokenInMaxAmount.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgSplitRouteSwapExactAmountOutResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgSplitRouteSwapExactAmountOutResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgSplitRouteSwapExactAmountOutResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TokenInAmount", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.TokenInAmount.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgSetDenomPairTakerFee) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgSetDenomPairTakerFee: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgSetDenomPairTakerFee: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Sender", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Sender = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DenomPairTakerFee", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.DenomPairTakerFee = append(m.DenomPairTakerFee, DenomPairTakerFee{}) + if err := m.DenomPairTakerFee[len(m.DenomPairTakerFee)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgSetDenomPairTakerFeeResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgSetDenomPairTakerFeeResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgSetDenomPairTakerFeeResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Success", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Success = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *DenomPairTakerFee) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: DenomPairTakerFee: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: DenomPairTakerFee: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Denom0", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Denom0 = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Denom1", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Denom1 = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TakerFee", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.TakerFee.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipTx(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTx + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTx + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTx + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthTx + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupTx + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthTx + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthTx = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowTx = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupTx = fmt.Errorf("proto: unexpected end of group") +) diff --git a/tests/interchaintest/packet_foward_test.go b/tests/interchaintest/packet_foward_test.go index fe635b33..3640c67c 100644 --- a/tests/interchaintest/packet_foward_test.go +++ b/tests/interchaintest/packet_foward_test.go @@ -11,7 +11,7 @@ import ( "github.com/strangelove-ventures/interchaintest/v7/testutil" "github.com/stretchr/testify/require" - feeabsCli "github.com/osmosis-labs/fee-abstraction/tests/interchaintest/feeabs" + feeabsCli "github.com/osmosis-labs/fee-abstraction/v7/tests/interchaintest/feeabs" ) func TestPacketForwardMiddleware(t *testing.T) { diff --git a/tests/interchaintest/proposal/add_host_zone.json b/tests/interchaintest/proposal/add_host_zone.json index 7916119b..fc3c0d84 100644 --- a/tests/interchaintest/proposal/add_host_zone.json +++ b/tests/interchaintest/proposal/add_host_zone.json @@ -1,13 +1,12 @@ { - "title": "Add Fee Abbtraction Host Zone Proposal", - "description": "Add Fee Abbtraction Host Zone", - "host_chain_fee_abs_config": - { - "ibc_denom": "ibc/C4CFF46FD6DE35CA4CF4CE031E643C8FDC9BA4B99AE598E9B0ED98FE3A2319F9", - "osmosis_pool_token_denom_in": "ibc/C4CFF46FD6DE35CA4CF4CE031E643C8FDC9BA4B99AE598E9B0ED98FE3A2319F9", - "pool_id": "1", - "status": 0, - "min_swap_amount": "0" - }, - "deposit": "100000000stake" -} \ No newline at end of file + "title": "Add Fee Abbtraction Host Zone Proposal", + "description": "Add Fee Abbtraction Host Zone", + "host_chain_fee_abs_config": { + "ibc_denom": "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B", + "osmosis_pool_token_denom_in": "uosmo", + "pool_id": "1", + "status": 0, + "min_swap_amount": "0" + }, + "deposit": "100000000stake" +} diff --git a/tests/interchaintest/proposal/proposal.json b/tests/interchaintest/proposal/proposal.json index 33dd9232..9f9cae8a 100644 --- a/tests/interchaintest/proposal/proposal.json +++ b/tests/interchaintest/proposal/proposal.json @@ -1,32 +1,32 @@ { - "title": "Enable Fee Abtraction", - "description": "Change params for enable fee abstraction", - "changes": [ - { - "subspace": "feeabs", - "key": "NativeIbcedInOsmosis", - "value": "ibc/C053D637CCA2A2BA030E2C5EE1B28A16F71CCB0E45E8BE52766DC1B241B77878" - }, - { - "subspace": "feeabs", - "key": "ChainName", - "value": "feeabs" - }, - { - "subspace": "feeabs", - "key": "IbcTransferChannel", - "value": "channel-0" - }, - { - "subspace": "feeabs", - "key": "IbcQueryIcqChannel", - "value": "channel-1" - }, - { - "subspace": "feeabs", - "key": "OsmosisCrosschainSwapAddress", - "value": "osmo17p9rzwnnfxcjp32un9ug7yhhzgtkhvl9jfksztgw5uh69wac2pgs5yczr8" - } - ], - "deposit": "5000000000stake" - } \ No newline at end of file + "title": "Enable Fee Abtraction", + "description": "Change params for enable fee abstraction", + "changes": [ + { + "subspace": "feeabs", + "key": "NativeIbcedInOsmosis", + "value": "ibc/C053D637CCA2A2BA030E2C5EE1B28A16F71CCB0E45E8BE52766DC1B241B77878" + }, + { + "subspace": "feeabs", + "key": "ChainName", + "value": "feeabs" + }, + { + "subspace": "feeabs", + "key": "IbcTransferChannel", + "value": "channel-0" + }, + { + "subspace": "feeabs", + "key": "IbcQueryIcqChannel", + "value": "channel-1" + }, + { + "subspace": "feeabs", + "key": "OsmosisCrosschainSwapAddress", + "value": "osmo17p9rzwnnfxcjp32un9ug7yhhzgtkhvl9jfksztgw5uh69wac2pgs5yczr8" + } + ], + "deposit": "5000000000stake" +} diff --git a/tests/interchaintest/query_osmosis_twap_test.go b/tests/interchaintest/query_osmosis_twap_test.go index d1682517..cc9ec93c 100644 --- a/tests/interchaintest/query_osmosis_twap_test.go +++ b/tests/interchaintest/query_osmosis_twap_test.go @@ -2,6 +2,7 @@ package interchaintest import ( "context" + "encoding/json" "fmt" "os" "path" @@ -11,10 +12,11 @@ import ( paramsutils "github.com/cosmos/cosmos-sdk/x/params/client/utils" transfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" "github.com/strangelove-ventures/interchaintest/v7/chain/cosmos" + "github.com/strangelove-ventures/interchaintest/v7/ibc" "github.com/strangelove-ventures/interchaintest/v7/testutil" "github.com/stretchr/testify/require" - feeabsCli "github.com/osmosis-labs/fee-abstraction/tests/interchaintest/feeabs" + feeabsCli "github.com/osmosis-labs/fee-abstraction/v7/tests/interchaintest/feeabs" ) func TestQueryOsmosisTwap(t *testing.T) { @@ -28,7 +30,7 @@ func TestQueryOsmosisTwap(t *testing.T) { feeabsUser, _, osmosisUser := users[0], users[1], users[2] - channFeeabsOsmosis, channOsmosisFeeabs, channFeeabsGaia, channGaiaFeeabs, channOsmosisGaia, channGaiaOsmosis := channels[0], channels[1], channels[2], channels[3], channels[4], channels[5] + channFeeabsOsmosis, channOsmosisFeeabs, channFeeabsGaia, channGaiaFeeabs, channOsmosisGaia, channGaiaOsmosis, channFeeabsOsmosisICQ := channels[0], channels[1], channels[2], channels[3], channels[4], channels[5], channels[6] // Setup contract on Osmosis // Store code crosschain Registry @@ -65,22 +67,15 @@ func TestQueryOsmosisTwap(t *testing.T) { _, err = osmosis.ExecuteContract(ctx, osmosisUser.KeyName(), registryContractAddress, 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) - - 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) + // Create pool Osmosis(stake)/uosmo on Osmosis + stakeOnOsmosis := GetStakeOnOsmosis(channOsmosisFeeabs, feeabs.Config().Denom) + osmosisUserBalance, err := osmosis.GetBalance(ctx, sdktypes.MustBech32ifyAddressBytes(osmosis.Config().Bech32Prefix, osmosisUser.Address()), stakeOnOsmosis) 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), + Weights: fmt.Sprintf("5%s,5%s", stakeOnOsmosis, osmosis.Config().Denom), + InitialDeposit: fmt.Sprintf("95000000%s,950000000%s", stakeOnOsmosis, osmosis.Config().Denom), SwapFee: "0.01", ExitFee: "0", FutureGovernor: "", @@ -102,28 +97,55 @@ func TestQueryOsmosisTwap(t *testing.T) { res := QuerySmartMsgResponse{} err = osmosis.QueryContract(ctx, registryContractAddress, queryMsg, &res) require.NoError(t, err) - // propose_pfm for gaia - _, err = feeabsCli.SetupProposePFM(osmosis, ctx, osmosisUser.KeyName(), registryContractAddress, `{"propose_pfm":{"chain": "gaia"}}`, uatomOnOsmosis) - require.NoError(t, err) - err = testutil.WaitForBlocks(ctx, 15, feeabs, gaia, osmosis) + + ParamChangeProposal(t, ctx, feeabs, feeabsUser, &channFeeabsOsmosis, &channFeeabsOsmosisICQ, stakeOnOsmosis) + AddHostZoneProposal(t, ctx, feeabs, feeabsUser) + + // ensure that the host zone is added + allHost, err := feeabsCli.QueryAllHostZoneConfig(feeabs, ctx) require.NoError(t, err) - queryMsg = QuerySmartMsg{ - Packet: HasPacketForwarding{ - Chain: "gaia", - }, - } - res = QuerySmartMsgResponse{} - err = osmosis.QueryContract(ctx, registryContractAddress, queryMsg, &res) + fmt.Printf("QueryAllHostZoneConfig %+v", allHost) + err = testutil.WaitForBlocks(ctx, 15, feeabs) require.NoError(t, err) - denomTrace = transfertypes.ParseDenomTrace(transfertypes.GetPrefixedDenom(channFeeabsGaia.PortID, channFeeabsGaia.ChannelID, gaia.Config().Denom)) - uatomOnFeeabs := denomTrace.IBCDenom() - - current_directory, _ := os.Getwd() - param_change_path := path.Join(current_directory, "proposal", "proposal.json") + // query the twap of uosmo/stake, stored in feeabs module + osmoOnFeeabs := GetOsmoOnFeeabs(channFeeabsOsmosis, osmosis.Config().Denom) + twapOsmosis, err := feeabsCli.QueryOsmosisArithmeticTwap(feeabs, ctx, osmoOnFeeabs) + require.NoError(t, err) + fmt.Println(twapOsmosis) - changeParamProposal, err := paramsutils.ParseParamChangeProposalJSON(feeabs.Config().EncodingConfig.Amino, param_change_path) + // query the twap of uosmo/stake + twap, err := feeabsCli.QueryOsmosisArithmeticTwapOsmosis(osmosis, ctx, "1", stakeOnOsmosis) + fmt.Println(twap) require.NoError(t, err) +} + +func ParamChangeProposal(t *testing.T, ctx context.Context, feeabs *cosmos.CosmosChain, feeabsUser ibc.Wallet, channFeeabsOsmosis, channFeeabsOsmosisFeeabs *ibc.ChannelOutput, stakeOnOsmosis string) { + t.Helper() + // propose to change feeabs parameters accordingly to the ibcdenom + curDir, _ := os.Getwd() + paramChangePath := path.Join(curDir, "proposal", "proposal.json") + + changeParamProposal, err := paramsutils.ParseParamChangeProposalJSON(feeabs.Config().EncodingConfig.Amino, paramChangePath) + require.NoError(t, err) + + // modify change proposal + for i := range changeParamProposal.Changes { + change := &changeParamProposal.Changes[i] + if change.Subspace == "feeabs" && change.Key == "IbcTransferChannel" { + fmt.Println("ibc transfer channel changed", channFeeabsOsmosis.ChannelID) + change.Value = json.RawMessage(fmt.Sprintf("\"%s\"", channFeeabsOsmosis.ChannelID)) + } + if change.Subspace == "feeabs" && change.Key == "IbcQueryIcqChannel" { + fmt.Println("ibc query icq channel changed", channFeeabsOsmosisFeeabs.ChannelID) + change.Value = json.RawMessage(fmt.Sprintf("\"%s\"", channFeeabsOsmosisFeeabs.ChannelID)) + } + if change.Subspace == "feeabs" && change.Key == "NativeIbcedInOsmosis" { + fmt.Println("NativeIbcedInOsmosis changed", stakeOnOsmosis) + change.Value = json.RawMessage(fmt.Sprintf("\"%s\"", stakeOnOsmosis)) + } + } + fmt.Printf("changeParamProposal %+v", changeParamProposal) paramTx, err := feeabsCli.ParamChangeProposal(feeabs, ctx, feeabsUser.KeyName(), &changeParamProposal) require.NoError(t, err, "error submitting param change proposal tx") @@ -134,26 +156,33 @@ func TestQueryOsmosisTwap(t *testing.T) { height, err := feeabs.Height(ctx) require.NoError(t, err) - _, err = cosmos.PollForProposalStatus(ctx, feeabs, height, height+10, paramTx.ProposalID, cosmos.ProposalStatusPassed) + _, err = cosmos.PollForProposalStatus(ctx, feeabs, height, height+20, paramTx.ProposalID, cosmos.ProposalStatusPassed) require.NoError(t, err, "proposal status did not change to passed in expected number of blocks") +} - _, err = feeabsCli.AddHostZoneProposal(feeabs, ctx, feeabsUser.KeyName(), "./proposal/add_host_zone.json") +func AddHostZoneProposal(t *testing.T, ctx context.Context, feeabs *cosmos.CosmosChain, feeabsUser ibc.Wallet) { + t.Helper() + _, err := feeabsCli.AddHostZoneProposal(feeabs, ctx, feeabsUser.KeyName(), "./proposal/add_host_zone.json") require.NoError(t, err) err = feeabs.VoteOnProposalAllValidators(ctx, "2", cosmos.ProposalVoteYes) require.NoError(t, err, "failed to submit votes") - height, err = feeabs.Height(ctx) + 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+20, "2", cosmos.ProposalStatusPassed) require.NoError(t, err, "proposal status did not change to passed in expected number of blocks") +} - _, err = feeabsCli.QueryAllHostZoneConfig(feeabs, ctx) - require.NoError(t, err) +func GetStakeOnOsmosis(channOsmosisFeeabs ibc.ChannelOutput, feeabsDenom string) string { + denomTrace := transfertypes.ParseDenomTrace(transfertypes.GetPrefixedDenom(channOsmosisFeeabs.PortID, channOsmosisFeeabs.ChannelID, feeabsDenom)) + stakeOnOsmosis := denomTrace.IBCDenom() + return stakeOnOsmosis +} - twap, err := feeabsCli.QueryOsmosisArithmeticTwap(feeabs, ctx, uatomOnFeeabs) - fmt.Println(err) - fmt.Println(twap) - // require.NoError(t, err) +func GetOsmoOnFeeabs(channFeeabsOsmosis ibc.ChannelOutput, osmosisDenom string) string { + denomTrace := transfertypes.ParseDenomTrace(transfertypes.GetPrefixedDenom(channFeeabsOsmosis.PortID, channFeeabsOsmosis.ChannelID, osmosisDenom)) + osmoOnFeeabs := denomTrace.IBCDenom() + return osmoOnFeeabs } diff --git a/tests/interchaintest/setup.go b/tests/interchaintest/setup.go index f0216f4c..76f50c2b 100644 --- a/tests/interchaintest/setup.go +++ b/tests/interchaintest/setup.go @@ -22,6 +22,8 @@ import ( "github.com/stretchr/testify/require" "go.uber.org/zap/zaptest" + balancertypes "github.com/osmosis-labs/fee-abstraction/v7/tests/interchaintest/osmosis/gamm/pool-models/balancer" + gammtypes "github.com/osmosis-labs/fee-abstraction/v7/tests/interchaintest/osmosis/gamm/types" feeabstype "github.com/osmosis-labs/fee-abstraction/v7/x/feeabs/types" ) @@ -68,8 +70,8 @@ var ( Bech32Prefix: "feeabs", Denom: "stake", CoinType: "118", - GasPrices: "0.00stake", - GasAdjustment: 1.1, + GasPrices: "0.005stake", + GasAdjustment: 1.5, TrustingPeriod: "112h", NoHostMount: false, ModifyGenesis: modifyGenesisShortProposals(votingPeriod, maxDepositPeriod, queryEpochTime), @@ -80,7 +82,7 @@ var ( pathFeeabsGaia = "feeabs-gaia" pathFeeabsOsmosis = "feeabs-osmosis" pathOsmosisGaia = "osmosis-gaia" - genesisWalletAmount = math.NewInt(10_000_000) + genesisWalletAmount = math.NewInt(100_000_000_000) amountToSend = math.NewInt(1_000_000_000) ) @@ -98,8 +100,8 @@ func feeabsEncoding() *moduletestutil.TestEncodingConfig { func osmosisEncoding() *moduletestutil.TestEncodingConfig { cfg := wasm.WasmEncoding() - // gammtypes.RegisterInterfaces(cfg.InterfaceRegistry) - // balancertypes.RegisterInterfaces(cfg.InterfaceRegistry) + gammtypes.RegisterInterfaces(cfg.InterfaceRegistry) + balancertypes.RegisterInterfaces(cfg.InterfaceRegistry) return cfg } @@ -121,6 +123,26 @@ func GetDockerImageInfo() (repo, version string) { return repo, branchVersion } +func modifyGenesisWhitelistTwapQueryOsmosis() func(ibc.ChainConfig, []byte) ([]byte, error) { + return func(chainConfig ibc.ChainConfig, genbz []byte) ([]byte, error) { + g := make(map[string]interface{}) + if err := json.Unmarshal(genbz, &g); err != nil { + return nil, fmt.Errorf("failed to unmarshal genesis file: %w", err) + } + // "interchainquery":{"host_port":"icqhost","params":{"allow_queries":[],"host_enabled":true}} + whitelist := "/osmosis.twap.v1beta1.Query/ArithmeticTwapToNow" + if err := dyno.Append(g, whitelist, "app_state", "interchainquery", "params", "allow_queries"); err != nil { + return nil, fmt.Errorf("failed to set whitelist in genesis json: %w", err) + } + fmt.Println("Genesis file updated", g) + out, err := json.Marshal(g) + if err != nil { + return nil, fmt.Errorf("failed to marshal genesis bytes to json: %w", err) + } + return out, nil + } +} + func modifyGenesisShortProposals(votingPeriod string, maxDepositPeriod string, queryEpochTime string) func(ibc.ChainConfig, []byte) ([]byte, error) { return func(chainConfig ibc.ChainConfig, genbz []byte) ([]byte, error) { g := make(map[string]interface{}) @@ -139,6 +161,10 @@ func modifyGenesisShortProposals(votingPeriod string, maxDepositPeriod string, q if err := dyno.Set(g, queryEpochTime, "app_state", "feeabs", "epochs", 0, "duration"); err != nil { return nil, fmt.Errorf("failed to set query epoch time in genesis json: %w", err) } + if err := dyno.Set(g, queryEpochTime, "app_state", "feeabs", "epochs", 1, "duration"); err != nil { + return nil, fmt.Errorf("failed to set query epoch time in genesis json: %w", err) + } + fmt.Println("Genesis file updated", g) out, err := json.Marshal(g) if err != nil { return nil, fmt.Errorf("failed to marshal genesis bytes to json: %w", err) @@ -156,7 +182,7 @@ func SetupChain(t *testing.T, ctx context.Context) ([]ibc.Chain, []ibc.Wallet, [ // Create chain factory with Feeabs and Gaia numVals := 1 - numFullNodes := 1 + numFullNodes := 0 cf := interchaintest.NewBuiltinChainFactory(zaptest.NewLogger(t), []*interchaintest.ChainSpec{ { @@ -180,6 +206,7 @@ func SetupChain(t *testing.T, ctx context.Context) ([]ibc.Chain, []ibc.Wallet, [ ChainConfig: ibc.ChainConfig{ GasPrices: "0.005uosmo", EncodingConfig: osmosisEncoding(), + ModifyGenesis: modifyGenesisWhitelistTwapQueryOsmosis(), }, NumValidators: &numVals, NumFullNodes: &numFullNodes, @@ -222,10 +249,10 @@ func SetupChain(t *testing.T, ctx context.Context) ([]ibc.Chain, []ibc.Wallet, [ }) require.NoError(t, ic.Build(ctx, eRep, interchaintest.InterchainBuildOptions{ - TestName: t.Name(), - Client: client, - NetworkID: network, - BlockDatabaseFile: interchaintest.DefaultBlockDatabaseFilepath(), + TestName: t.Name(), + Client: client, + NetworkID: network, + // BlockDatabaseFile: interchaintest.DefaultBlockDatabaseFilepath(), SkipPathCreation: true, })) @@ -233,8 +260,7 @@ func SetupChain(t *testing.T, ctx context.Context) ([]ibc.Chain, []ibc.Wallet, [ _ = ic.Close() }) - const userFunds = int64(10_000_000_000) - users := interchaintest.GetAndFundTestUsers(t, ctx, t.Name(), userFunds, feeabs, gaia, osmosis) + users := interchaintest.GetAndFundTestUsers(t, ctx, t.Name(), genesisWalletAmount.Int64(), feeabs, gaia, osmosis) // rly feeabs-osmo // Generate new path @@ -255,10 +281,10 @@ func SetupChain(t *testing.T, ctx context.Context) ([]ibc.Chain, []ibc.Wallet, [ require.NoError(t, err) // Create channel err = r.CreateChannel(ctx, eRep, pathFeeabsOsmosis, ibc.CreateChannelOptions{ - SourcePortName: "transfer", - DestPortName: "transfer", + SourcePortName: "feeabs", + DestPortName: "icqhost", Order: ibc.Unordered, - Version: "ics20-1", + Version: "icq-1", }) require.NoError(t, err) @@ -274,10 +300,36 @@ func SetupChain(t *testing.T, ctx context.Context) ([]ibc.Chain, []ibc.Wallet, [ require.Len(t, channsFeeabs, 1) require.Len(t, channsOsmosis, 1) - channFeeabsOsmosis := channsFeeabs[0] - require.NotEmpty(t, channFeeabsOsmosis.ChannelID) - channOsmosisFeeabs := channsOsmosis[0] - require.NotEmpty(t, channOsmosisFeeabs.ChannelID) + chanFeeabsOsmosisFeeapp := channsFeeabs[0] + channOsmosisFeeabsICQ := channsOsmosis[0] + err = r.CreateChannel(ctx, eRep, pathFeeabsOsmosis, ibc.CreateChannelOptions{ + SourcePortName: "transfer", + DestPortName: "transfer", + Order: ibc.Unordered, + Version: "ics20-1", + }) + require.NoError(t, err) + + err = testutil.WaitForBlocks(ctx, 5, feeabs, gaia) + require.NoError(t, err) + channFeeabsIncludeTransfer, err := r.GetChannels(ctx, eRep, feeabs.Config().ChainID) + require.NoError(t, err) + channOsmosisIncludeTransfer, err := r.GetChannels(ctx, eRep, osmosis.Config().ChainID) + require.NoError(t, err) + var channFeeabsOsmosisTransfer ibc.ChannelOutput + var channOsmosisFeeabsTransfer ibc.ChannelOutput + for _, chann := range channFeeabsIncludeTransfer { + if chann.ChannelID != chanFeeabsOsmosisFeeapp.ChannelID { + channFeeabsOsmosisTransfer = chann + } + } + require.NotEmpty(t, channFeeabsOsmosisTransfer) + for _, chann := range channOsmosisIncludeTransfer { + if chann.ChannelID != channOsmosisFeeabsICQ.ChannelID { + channOsmosisFeeabsTransfer = chann + } + } + // rly feeabs-gaia // Generate new path err = r.GeneratePath(ctx, eRep, feeabs.Config().ChainID, gaia.Config().ChainID, pathFeeabsGaia) @@ -314,12 +366,12 @@ func SetupChain(t *testing.T, ctx context.Context) ([]ibc.Chain, []ibc.Wallet, [ channsGaia, err := r.GetChannels(ctx, eRep, gaia.Config().ChainID) require.NoError(t, err) - require.Len(t, channsFeeabs, 2) + require.Len(t, channsFeeabs, 3) require.Len(t, channsGaia, 1) var channFeeabsGaia ibc.ChannelOutput for _, chann := range channsFeeabs { - if chann.ChannelID != channFeeabsOsmosis.ChannelID { + if chann.ChannelID != channFeeabsOsmosisTransfer.ChannelID && chann.ChannelID != chanFeeabsOsmosisFeeapp.ChannelID { channFeeabsGaia = chann } } @@ -361,14 +413,14 @@ func SetupChain(t *testing.T, ctx context.Context) ([]ibc.Chain, []ibc.Wallet, [ channsGaia, err = r.GetChannels(ctx, eRep, gaia.Config().ChainID) require.NoError(t, err) - require.Len(t, channsOsmosis, 2) + require.Len(t, channsOsmosis, 3) require.Len(t, channsGaia, 2) var channOsmosisGaia ibc.ChannelOutput var channGaiaOsmosis ibc.ChannelOutput for _, chann := range channsOsmosis { - if chann.ChannelID != channOsmosisFeeabs.ChannelID { + if chann.ChannelID != channOsmosisFeeabsTransfer.ChannelID && chann.ChannelID != channOsmosisFeeabsICQ.ChannelID { channOsmosisGaia = chann } } @@ -382,8 +434,10 @@ func SetupChain(t *testing.T, ctx context.Context) ([]ibc.Chain, []ibc.Wallet, [ require.NotEmpty(t, channGaiaOsmosis) fmt.Println("-----------------------------------") - fmt.Printf("channFeeabsOsmosis: %s - %s\n", channFeeabsOsmosis.ChannelID, channFeeabsOsmosis.Counterparty.ChannelID) - fmt.Printf("channOsmosisFeeabs: %s - %s\n", channOsmosisFeeabs.ChannelID, channOsmosisFeeabs.Counterparty.ChannelID) + fmt.Printf("channFeeabsOsmosisTransfer: %s - %s\n", channFeeabsOsmosisTransfer.ChannelID, channFeeabsOsmosisTransfer.Counterparty.ChannelID) + fmt.Printf("channOsmosisFeeabsTransfer: %s - %s\n", channOsmosisFeeabsTransfer.ChannelID, channOsmosisFeeabsTransfer.Counterparty.ChannelID) + fmt.Printf("channFeeabsOsmosisfeeabs: %s - %s\n", chanFeeabsOsmosisFeeapp.ChannelID, chanFeeabsOsmosisFeeapp.Counterparty.ChannelID) + fmt.Printf("channOsmosisFeeabsIcq: %s - %s\n", channOsmosisFeeabsICQ.ChannelID, channOsmosisFeeabsICQ.Counterparty.ChannelID) fmt.Printf("channFeeabsGaia: %s - %s\n", channFeeabsGaia.ChannelID, channFeeabsGaia.Counterparty.ChannelID) fmt.Printf("channGaiaFeeabs: %s - %s\n", channGaiaFeeabs.ChannelID, channGaiaFeeabs.Counterparty.ChannelID) fmt.Printf("channOsmosisGaia: %s - %s\n", channOsmosisGaia.ChannelID, channOsmosisGaia.Counterparty.ChannelID) @@ -402,7 +456,7 @@ func SetupChain(t *testing.T, ctx context.Context) ([]ibc.Chain, []ibc.Wallet, [ } }, ) - chanels = append(chanels, channFeeabsOsmosis, channOsmosisFeeabs, channFeeabsGaia, channGaiaFeeabs, channOsmosisGaia, channGaiaOsmosis) + chanels = append(chanels, channFeeabsOsmosisTransfer, channOsmosisFeeabsTransfer, channFeeabsGaia, channGaiaFeeabs, channOsmosisGaia, channGaiaOsmosis, chanFeeabsOsmosisFeeapp, channOsmosisFeeabsICQ) feeabsUser, gaiaUser, osmosisUser := users[0], users[1], users[2] // Send Gaia uatom to Osmosis @@ -434,7 +488,7 @@ func SetupChain(t *testing.T, ctx context.Context) ([]ibc.Chain, []ibc.Wallet, [ Amount: amountToSend, } - tx, err = feeabs.SendIBCTransfer(ctx, channFeeabsOsmosis.ChannelID, feeabsUser.KeyName(), transfer, ibc.TransferOptions{}) + tx, err = feeabs.SendIBCTransfer(ctx, channFeeabsOsmosisTransfer.ChannelID, feeabsUser.KeyName(), transfer, ibc.TransferOptions{}) require.NoError(t, err) require.NoError(t, tx.Validate()) diff --git a/tests/interchaintest/tendermint/events.go b/tests/interchaintest/tendermint/events.go new file mode 100644 index 00000000..f05952cf --- /dev/null +++ b/tests/interchaintest/tendermint/events.go @@ -0,0 +1,37 @@ +package tendermint + +import ( + "encoding/base64" + + abcitypes "github.com/cometbft/cometbft/abci/types" +) + +// AttributeValue returns an event attribute value given the eventType and attribute key tuple. +// In the event of duplicate types and keys, returns the first attribute value found. +// If not found, returns empty string and false. +func AttributeValue(events []abcitypes.Event, eventType, attrKey string) (string, bool) { + for _, event := range events { + if event.Type != eventType { + continue + } + for _, attr := range event.Attributes { + if attr.Key == attrKey { + return attr.Value, true + } + + // tendermint < v0.37-alpha returns base64 encoded strings in events. + key, err := base64.StdEncoding.DecodeString(attr.Key) + if err != nil { + continue + } + if string(key) == attrKey { + value, err := base64.StdEncoding.DecodeString(attr.Value) + if err != nil { + continue + } + return string(value), true + } + } + } + return "", false +} diff --git a/x/feeabs/ante/decorate.go b/x/feeabs/ante/decorate.go index 30f70986..abaa1df7 100644 --- a/x/feeabs/ante/decorate.go +++ b/x/feeabs/ante/decorate.go @@ -118,9 +118,9 @@ func (fadfd FeeAbstractionDeductFeeDecorate) abstractionDeductFeeHandler(ctx sdk return ctx, sdkerrors.Wrap(feeabstypes.ErrHostZoneFrozen, "cannot deduct fee as host zone is frozen") } - if hostChainConfig.Status == feeabstypes.HostChainFeeAbsStatus_OUTDATED { - return ctx, sdkerrors.Wrap(feeabstypes.ErrHostZoneOutdated, "cannot deduct fee as host zone is outdated") - } + // if hostChainConfig.Status == feeabstypes.HostChainFeeAbsStatus_OUTDATED { + // return ctx, sdkerrors.Wrap(feeabstypes.ErrHostZoneOutdated, "cannot deduct fee as host zone is outdated") + // } fee := feeTx.GetFee() feePayer := feeTx.FeePayer() @@ -277,16 +277,19 @@ func (famfd FeeAbstrationMempoolFeeDecorator) AnteHandle(ctx sdk.Context, tx sdk return ctx, sdkerrors.Wrapf(feeabstypes.ErrHostZoneFrozen, "cannot deduct fee as host zone is frozen") } - if hostChainConfig.Status == feeabstypes.HostChainFeeAbsStatus_OUTDATED { - return ctx, sdkerrors.Wrapf(feeabstypes.ErrHostZoneOutdated, "cannot deduct fee as host zone is outdated") - } + // if hostChainConfig.Status == feeabstypes.HostChainFeeAbsStatus_OUTDATED { + // return ctx, sdkerrors.Wrapf(feeabstypes.ErrHostZoneOutdated, "cannot deduct fee as host zone is outdated") + // } nativeCoinsFees, err := famfd.feeabsKeeper.CalculateNativeFromIBCCoins(ctx, feeCoinsNonZeroDenom, hostChainConfig) if err != nil { - return ctx, sdkerrors.Wrapf(errorstypes.ErrInsufficientFee, "insufficient fees") + return ctx, sdkerrors.Wrapf(errorstypes.ErrInsufficientFee, "unable to calculate native fees from ibc fees: %s", err) } + fmt.Println("nativeCoinsFees", nativeCoinsFees) feeCoinsNonZeroDenom = nativeCoinsFees } + } else if feeCoinsNonZeroDenom.Len() > 1 { + return ctx, sdkerrors.Wrapf(errorstypes.ErrNotSupported, "should have only one fee denom in feeCoinsNonZeroDenom, got %d", feeCoinsNonZeroDenom.Len()) } // After replace the feeCoinsNonZeroDenom, feeCoinsNonZeroDenom must be in denom subset of nonZeroCoinFeesReq @@ -306,7 +309,7 @@ func (famfd FeeAbstrationMempoolFeeDecorator) AnteHandle(ctx sdk.Context, tx sdk } if feeCoinsLen == 0 { - return ctx, sdkerrors.Wrapf(errorstypes.ErrInsufficientFee, "insufficient fees; got: %s required: %s", feeCoins, feeRequired) + return ctx, sdkerrors.Wrapf(errorstypes.ErrInsufficientFee, "no fee provided, required: %s", 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 ca6b211c..4460304e 100644 --- a/x/feeabs/keeper/ibc.go +++ b/x/feeabs/keeper/ibc.go @@ -114,6 +114,7 @@ func (k Keeper) SendInterchainQuery( func (k Keeper) OnAcknowledgementPacket(ctx sdk.Context, ack channeltypes.Acknowledgement, icqReqs []abci.RequestQuery) error { switch resp := ack.Response.(type) { + case *channeltypes.Acknowledgement_Result: var ackData types.InterchainQueryPacketAck if err := types.ModuleCdc.UnmarshalJSON(resp.Result, &ackData); err != nil { @@ -282,6 +283,7 @@ func (k Keeper) HandleOsmosisIbcQuery(ctx sdk.Context) (int, error) { // fee abstraction will not send query to a frozen host zone // however, it will continue to send query to other host zone if UPDATED, or OUTDATED // this logic iterate through registered host zones and collect requests before sending it + k.Logger(ctx).Info("start iterate host zone") k.IterateHostZone(ctx, func(hostZoneConfig types.HostChainFeeAbsConfig) (stop bool) { if k.IbcQueryHostZoneFilter(ctx, hostZoneConfig, queryTwapEpochInfo) { return false @@ -371,6 +373,7 @@ func (k Keeper) ExecuteAllHostChainSwap(ctx sdk.Context) { func (k Keeper) IbcQueryHostZoneFilter(ctx sdk.Context, hostZoneConfig types.HostChainFeeAbsConfig, queryTwapEpochInfo types.EpochInfo) bool { if hostZoneConfig.Status == types.HostChainFeeAbsStatus_FROZEN { + k.Logger(ctx).Info(fmt.Sprintf("Host zone %s is frozen", hostZoneConfig.IbcDenom)) return true } @@ -384,6 +387,7 @@ func (k Keeper) IbcQueryHostZoneFilter(ctx sdk.Context, hostZoneConfig types.Hos } if queryTwapEpochInfo.CurrentEpoch < exponential.FutureEpoch { + k.Logger(ctx).Info(fmt.Sprintf("Host zone %s is not ready to query", hostZoneConfig.IbcDenom)) return true }