From 559d42d60f9a4fa73a5153e2b9b48fa239211726 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Luque?= Date: Mon, 24 Aug 2026 11:59:12 +0200 Subject: [PATCH 1/2] feat(swap): add x/swap for denom exchange against held inventory --- app/app.go | 18 + app/upgrades.go | 56 + proto/swap/genesis.proto | 20 + proto/swap/params.proto | 21 + proto/swap/query.proto | 77 + proto/swap/swap.proto | 49 + proto/swap/tx.proto | 143 + scripts/mockgen.sh | 3 +- tests/integration/swap_test.go | 313 ++ .../integration/cbdc/integration/keepers.go | 5 + .../integration/cbdc/integration/setup.go | 2 + x/swap/keeper/common_test.go | 153 + x/swap/keeper/genesis.go | 28 + x/swap/keeper/keeper.go | 48 + x/swap/keeper/migrations.go | 64 + x/swap/keeper/migrations_test.go | 78 + x/swap/keeper/msg_server.go | 183 ++ x/swap/keeper/msg_server_set_price_test.go | 121 + x/swap/keeper/msg_server_swap_test.go | 151 + x/swap/keeper/params.go | 45 + x/swap/keeper/price.go | 83 + x/swap/keeper/price_test.go | 175 ++ x/swap/keeper/query.go | 19 + x/swap/keeper/query_params.go | 20 + x/swap/keeper/query_pending_swap.go | 35 + x/swap/keeper/query_price.go | 48 + x/swap/keeper/query_price_test.go | 45 + x/swap/keeper/swap.go | 210 ++ x/swap/keeper/swap_test.go | 350 +++ x/swap/module.go | 128 + x/swap/testutil/expected_keepers_mock.go | 157 + x/swap/types/codec.go | 41 + x/swap/types/errors.go | 33 + x/swap/types/events.go | 19 + x/swap/types/expected_keepers.go | 25 + x/swap/types/genesis.go | 33 + x/swap/types/genesis.pb.go | 453 +++ x/swap/types/keys.go | 48 + x/swap/types/message_pay_swap.go | 64 + x/swap/types/message_set_price.go | 34 + x/swap/types/message_set_price_test.go | 157 + x/swap/types/message_swap.go | 56 + x/swap/types/params.go | 34 + x/swap/types/params.pb.go | 329 ++ x/swap/types/price.go | 36 + x/swap/types/query.pb.go | 1885 +++++++++++ x/swap/types/query.pb.gw.go | 402 +++ x/swap/types/swap.pb.go | 772 +++++ x/swap/types/tx.pb.go | 2749 +++++++++++++++++ 49 files changed, 10017 insertions(+), 1 deletion(-) create mode 100644 proto/swap/genesis.proto create mode 100644 proto/swap/params.proto create mode 100644 proto/swap/query.proto create mode 100644 proto/swap/swap.proto create mode 100644 proto/swap/tx.proto create mode 100644 tests/integration/swap_test.go create mode 100644 x/swap/keeper/common_test.go create mode 100644 x/swap/keeper/genesis.go create mode 100644 x/swap/keeper/keeper.go create mode 100644 x/swap/keeper/migrations.go create mode 100644 x/swap/keeper/migrations_test.go create mode 100644 x/swap/keeper/msg_server.go create mode 100644 x/swap/keeper/msg_server_set_price_test.go create mode 100644 x/swap/keeper/msg_server_swap_test.go create mode 100644 x/swap/keeper/params.go create mode 100644 x/swap/keeper/price.go create mode 100644 x/swap/keeper/price_test.go create mode 100644 x/swap/keeper/query.go create mode 100644 x/swap/keeper/query_params.go create mode 100644 x/swap/keeper/query_pending_swap.go create mode 100644 x/swap/keeper/query_price.go create mode 100644 x/swap/keeper/query_price_test.go create mode 100644 x/swap/keeper/swap.go create mode 100644 x/swap/keeper/swap_test.go create mode 100644 x/swap/module.go create mode 100644 x/swap/testutil/expected_keepers_mock.go create mode 100644 x/swap/types/codec.go create mode 100644 x/swap/types/errors.go create mode 100644 x/swap/types/events.go create mode 100644 x/swap/types/expected_keepers.go create mode 100644 x/swap/types/genesis.go create mode 100644 x/swap/types/genesis.pb.go create mode 100644 x/swap/types/keys.go create mode 100644 x/swap/types/message_pay_swap.go create mode 100644 x/swap/types/message_set_price.go create mode 100644 x/swap/types/message_set_price_test.go create mode 100644 x/swap/types/message_swap.go create mode 100644 x/swap/types/params.go create mode 100644 x/swap/types/params.pb.go create mode 100644 x/swap/types/price.go create mode 100644 x/swap/types/query.pb.go create mode 100644 x/swap/types/query.pb.gw.go create mode 100644 x/swap/types/swap.pb.go create mode 100644 x/swap/types/tx.pb.go diff --git a/app/app.go b/app/app.go index f62f39c..f97a620 100644 --- a/app/app.go +++ b/app/app.go @@ -138,6 +138,9 @@ import ( poatypes "github.com/peersyst/cbdc-node/x/poa/types" "github.com/peersyst/cbdc-node/x/qbftclient" qbfttypes "github.com/peersyst/cbdc-node/x/qbftclient/types" + "github.com/peersyst/cbdc-node/x/swap" + swapkeeper "github.com/peersyst/cbdc-node/x/swap/keeper" + swaptypes "github.com/peersyst/cbdc-node/x/swap/types" srvflags "github.com/cosmos/evm/server/flags" @@ -184,6 +187,9 @@ var ( feemarkettypes.ModuleName: nil, poatypes.ModuleName: {authtypes.Minter, authtypes.Burner}, cbdctypes.ModuleName: {authtypes.Minter, authtypes.Burner}, + // No permissions: x/swap mints nothing. Its module account holds only the + // transient escrow between a swap's two legs. + swaptypes.ModuleName: nil, } ) @@ -261,6 +267,7 @@ type App struct { // cbdc keepers PoaKeeper poakeeper.Keeper CbdcKeeper cbdckeeper.Keeper + SwapKeeper swapkeeper.Keeper // mm is the module manager mm *module.Manager @@ -315,6 +322,7 @@ func New( // Ethermint evmtypes.StoreKey, feemarkettypes.StoreKey, erc20types.StoreKey, + swaptypes.StoreKey, ) tkeys := storetypes.NewTransientStoreKeys(paramstypes.TStoreKey, evmtypes.TransientKey, feemarkettypes.TransientKey) @@ -492,6 +500,13 @@ func New( BaseDenom, ) + app.SwapKeeper = *swapkeeper.NewKeeper( + appCodec, + runtime.NewKVStoreService(keys[swaptypes.StoreKey]), + app.BankKeeper, + authtypes.NewModuleAddress(govtypes.ModuleName).String(), + ) + // Ethermint keepers // Feemarket Keeper @@ -739,6 +754,7 @@ func New( // cbdc app modules poa.NewAppModule(appCodec, app.PoaKeeper, app.BankKeeper, app.StakingKeeper, app.AccountKeeper, app.interfaceRegistry), cbdc.NewAppModule(appCodec, app.CbdcKeeper, app.AccountKeeper, app.interfaceRegistry), + swap.NewAppModule(appCodec, app.SwapKeeper, app.AccountKeeper), ) // BasicModuleManager defines the module BasicManager which is in charge of setting up basic, @@ -831,6 +847,7 @@ func New( erc20types.ModuleName, poatypes.ModuleName, cbdctypes.ModuleName, + swaptypes.ModuleName, crisistypes.ModuleName, ratelimittypes.ModuleName, } @@ -887,6 +904,7 @@ func New( app.setPostHandler() app.setupUpgradeHandlers() + app.setupStoreLoader() // At startup, after all modules have been registered, check that all prot // annotations are correct. diff --git a/app/upgrades.go b/app/upgrades.go index 11e4ba5..e4e3e30 100644 --- a/app/upgrades.go +++ b/app/upgrades.go @@ -4,10 +4,12 @@ import ( "context" "errors" + storetypes "cosmossdk.io/store/types" upgradetypes "cosmossdk.io/x/upgrade/types" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" appupgrades "github.com/peersyst/cbdc-node/app/upgrades" + swaptypes "github.com/peersyst/cbdc-node/x/swap/types" ) // UpgradeNameIBCV2 is the on-chain upgrade name that activates IBC v2 (Eureka) @@ -18,6 +20,22 @@ import ( // the v2-capable binary through the coordinated validator-vote upgrade flow. const UpgradeNameIBCV2 = "ibc-v2" +// UpgradeNameSwap is the on-chain upgrade name that mounts x/swap. +// +// Unlike ibc-v2 this one DOES add a store key, and a new store cannot simply +// appear under a running chain: the root store refuses to load a store whose +// version is 0 while the rest are at the current height. The store has to be +// declared through StoreUpgrades so the upgrade module creates it at the halt +// height, which is what setupStoreLoader below arranges. +const UpgradeNameSwap = "swap" + +// UpgradeNameSwapPairKey re-keys x/swap prices onto one entry per denom pair. +// +// No store key is added, so no StoreLoader is needed — only the keys inside the +// swap store move. It must still run: switching binary without it strands the v1 +// entries at keys nothing looks up, where GetAllPrices still reports them. +const UpgradeNameSwapPairKey = "swap-pair-key" + var rollingUpgradeHandlers = []struct { name string handler upgradetypes.UpgradeHandler @@ -39,6 +57,44 @@ func (app *App) setupUpgradeHandlers() { return app.mm.RunMigrations(ctx, app.configurator, fromVM) }, ) + + // x/swap's own InitGenesis does not run for a module added to a live chain, + // so RunMigrations is what registers it — and it comes up with the default + // params, i.e. no exchange address and conversion disabled. That is the + // intended landing state: the address is a group policy that has to be set + // deliberately afterwards, not guessed at upgrade time. + app.UpgradeKeeper.SetUpgradeHandler( + UpgradeNameSwap, + func(ctx context.Context, _ upgradetypes.Plan, fromVM module.VersionMap) (module.VersionMap, error) { + return app.mm.RunMigrations(ctx, app.configurator, fromVM) + }, + ) + + // RunMigrations sees x/swap at consensus version 1 and runs Migrate1to2. + app.UpgradeKeeper.SetUpgradeHandler( + UpgradeNameSwapPairKey, + func(ctx context.Context, _ upgradetypes.Plan, fromVM module.VersionMap) (module.VersionMap, error) { + return app.mm.RunMigrations(ctx, app.configurator, fromVM) + }, + ) +} + +// setupStoreLoader mounts stores added by a pending upgrade. +// +// Read from disk before the store is loaded, because by the time an upgrade +// handler runs the root store has already had to open every key — including the +// one the upgrade is introducing. +func (app *App) setupStoreLoader() { + upgradeInfo, err := app.UpgradeKeeper.ReadUpgradeInfoFromDisk() + if err != nil { + panic(err) + } + + if upgradeInfo.Name == UpgradeNameSwap && !app.UpgradeKeeper.IsSkipHeight(upgradeInfo.Height) { + app.SetStoreLoader(upgradetypes.UpgradeStoreLoader(upgradeInfo.Height, &storetypes.StoreUpgrades{ + Added: []string{swaptypes.StoreKey}, + })) + } } func (app *App) setupPreBlockUpgradeHandlers(ctx sdk.Context) error { diff --git a/proto/swap/genesis.proto b/proto/swap/genesis.proto new file mode 100644 index 0000000..de3f019 --- /dev/null +++ b/proto/swap/genesis.proto @@ -0,0 +1,20 @@ +syntax = "proto3"; +package swap; + +import "gogoproto/gogo.proto"; +import "swap/params.proto"; +import "swap/swap.proto"; + +option go_package = "github.com/peersyst/cbdc-node/x/swap/types"; + +// GenesisState defines the swap module's genesis state. +message GenesisState { + // prices is the full set of stored exchange rates. + repeated Price prices = 1 [ (gogoproto.nullable) = false ]; + // params is the module's parameter set. + Params params = 2 [ (gogoproto.nullable) = false ]; + // pending_swaps are escrowed legs awaiting payout. Normally empty: an entry + // lives only between MsgSwap and MsgPaySwap, usually within one transaction. + // Exported so an escrow that outlived a restart is not silently dropped. + repeated PendingSwap pending_swaps = 3 [ (gogoproto.nullable) = false ]; +} diff --git a/proto/swap/params.proto b/proto/swap/params.proto new file mode 100644 index 0000000..4e4bc2e --- /dev/null +++ b/proto/swap/params.proto @@ -0,0 +1,21 @@ +syntax = "proto3"; +package swap; + +import "cosmos_proto/cosmos.proto"; + +option go_package = "github.com/peersyst/cbdc-node/x/swap/types"; + +// Params defines the parameters for the module. +message Params { + // exchange_address receives the incoming asset when a swap converts it. It + // holds the collateral backing every unit this module has minted. + // + // It is gov-controlled via MsgUpdateParams rather than fixed at genesis + // because it is an x/group policy address, which does not exist until the + // group is created — after the chain is already running. + // + // Empty, the default, disables swapping entirely: with nowhere to put the + // incoming asset there is no way to convert without minting against + // collateral the chain never took custody of. + string exchange_address = 1 [ (cosmos_proto.scalar) = "cosmos.AddressString" ]; +} diff --git a/proto/swap/query.proto b/proto/swap/query.proto new file mode 100644 index 0000000..cde6fb5 --- /dev/null +++ b/proto/swap/query.proto @@ -0,0 +1,77 @@ +syntax = "proto3"; +package swap; + +import "gogoproto/gogo.proto"; +import "google/api/annotations.proto"; +import "cosmos/base/query/v1beta1/pagination.proto"; +import "swap/params.proto"; +import "swap/swap.proto"; + +option go_package = "github.com/peersyst/cbdc-node/x/swap/types"; + +// Query defines the gRPC querier service. +service Query { + // Price queries the exchange rate stored for a single ordered denom pair. + // + // The denoms are query-string parameters rather than path segments because + // IBC voucher denoms contain a slash, which no path template can match. + rpc Price(QueryPriceRequest) returns (QueryPriceResponse) { + option (google.api.http).get = "/cbdc/swap/price"; + } + // Prices queries every stored exchange rate. + rpc Prices(QueryPricesRequest) returns (QueryPricesResponse) { + option (google.api.http).get = "/cbdc/swap/prices"; + } + // Params queries the module's parameter set. + rpc Params(QueryParamsRequest) returns (QueryParamsResponse) { + option (google.api.http).get = "/cbdc/swap/params"; + } + // PendingSwap queries the escrowed-but-unpaid swap for an address, if any. + // + // The address is a query-string parameter for the same reason the price pair + // is: nothing about it is guaranteed path-safe. + rpc PendingSwap(QueryPendingSwapRequest) returns (QueryPendingSwapResponse) { + option (google.api.http).get = "/cbdc/swap/pending"; + } +} + +// QueryPendingSwapRequest is request type for the Query/PendingSwap RPC method. +message QueryPendingSwapRequest { string address = 1; } + +// QueryPendingSwapResponse is response type for the Query/PendingSwap RPC method. +message QueryPendingSwapResponse { + // found is false when the address has nothing outstanding, which is the normal + // state — a pending swap exists only between an exchange's two legs. + bool found = 1; + PendingSwap pending_swap = 2 [ (gogoproto.nullable) = false ]; +} + +// QueryParamsRequest is request type for the Query/Params RPC method. +message QueryParamsRequest {} + +// QueryParamsResponse is response type for the Query/Params RPC method. +message QueryParamsResponse { + Params params = 1 [ (gogoproto.nullable) = false ]; +} + +// QueryPriceRequest is request type for the Query/Price RPC method. +message QueryPriceRequest { + string from_denom = 1; + string to_denom = 2; +} + +// QueryPriceResponse is response type for the Query/Price RPC method. +message QueryPriceResponse { + Price price = 1 [ (gogoproto.nullable) = false ]; +} + +// QueryPricesRequest is request type for the Query/Prices RPC method. +message QueryPricesRequest { + cosmos.base.query.v1beta1.PageRequest pagination = 1; +} + +// QueryPricesResponse is response type for the Query/Prices RPC method. +message QueryPricesResponse { + repeated Price prices = 1 [ (gogoproto.nullable) = false ]; + cosmos.base.query.v1beta1.PageResponse pagination = 2; +} diff --git a/proto/swap/swap.proto b/proto/swap/swap.proto new file mode 100644 index 0000000..f670ddf --- /dev/null +++ b/proto/swap/swap.proto @@ -0,0 +1,49 @@ +syntax = "proto3"; +package swap; + +import "gogoproto/gogo.proto"; +import "cosmos_proto/cosmos.proto"; +import "cosmos/base/v1beta1/coin.proto"; + +option go_package = "github.com/peersyst/cbdc-node/x/swap/types"; + +// Price is a one-directional exchange rate: how many units of to_denom one unit +// of from_denom is worth. +// +// A pair has exactly ONE entry, holding the direction it was quoted in. Both +// orderings are stored under the same key, so quoting from_denom -> to_denom +// replaces any to_denom -> from_denom rate rather than sitting beside it, and +// the opposite direction is derived as 1/price at settlement (see +// Keeper.GetEffectivePrice). +// +// Two independently stored directions would be a contradiction rather than two +// opinions: each leg would settle at its own number, and a pair quoted 0.402 one +// way and 50 the other turns a round trip into a 20x gain against the exchange. +// The cost of deriving instead is that the inverse carries no spread and its +// division truncates, always in the exchange's favour. +message Price { + string from_denom = 1; + string to_denom = 2; + string price = 3 [ + (cosmos_proto.scalar) = "cosmos.Dec", + (gogoproto.customtype) = "cosmossdk.io/math.LegacyDec", + (gogoproto.nullable) = false + ]; +} + +// PendingSwap is a swap whose incoming leg has been escrowed but whose payout +// has not happened yet. +// +// It exists because the two legs are authorised by two different group policies +// and cannot be carried by one message. The entry is written by MsgSwap and +// consumed by MsgPaySwap, normally inside the same transaction; MsgAssertSwapSettled +// exists so a transaction that failed to consume it cannot commit. +message PendingSwap { + // address is the account whose funds are escrowed, and the account the payout + // is owed to. One pending swap per address. + string address = 1 [ (cosmos_proto.scalar) = "cosmos.AddressString" ]; + string from_denom = 2; + string to_denom = 3; + // escrowed is what left the address and sits in the module account. + cosmos.base.v1beta1.Coin escrowed = 4 [ (gogoproto.nullable) = false ]; +} diff --git a/proto/swap/tx.proto b/proto/swap/tx.proto new file mode 100644 index 0000000..838d167 --- /dev/null +++ b/proto/swap/tx.proto @@ -0,0 +1,143 @@ +syntax = "proto3"; +package swap; + +import "gogoproto/gogo.proto"; +import "cosmos_proto/cosmos.proto"; +import "cosmos/msg/v1/msg.proto"; +import "swap/params.proto"; + +option go_package = "github.com/peersyst/cbdc-node/x/swap/types"; + +// Msg defines the Msg service. +service Msg { + option (cosmos.msg.v1.service) = true; + + // Sets the exchange rate for an ordered denom pair, overwriting any rate + // already stored for that pair. + rpc SetPrice(MsgSetPrice) returns (MsgSetPriceResponse); + // Escrows the incoming leg of an exchange and records it as pending. + rpc Swap(MsgSwap) returns (MsgSwapResponse); + // Pays out the other leg and consumes the pending entry. Only the configured + // exchange address may call it. + rpc PaySwap(MsgPaySwap) returns (MsgPaySwapResponse); + // Refunds an escrowed swap that was never paid out. + rpc CancelSwap(MsgCancelSwap) returns (MsgCancelSwapResponse); + // Fails if the address still has a pending swap. Carried as the last message + // of a settlement transaction so a silently unsettled leg reverts the whole tx. + rpc AssertSwapSettled(MsgAssertSwapSettled) returns (MsgAssertSwapSettledResponse); + // Updates the module params. Only the governance authority can call it. + rpc UpdateParams(MsgUpdateParams) returns (MsgUpdateParamsResponse); +} + +// MsgSetPrice defines a message that sets the exchange rate for an ordered +// denom pair. +message MsgSetPrice { + option (cosmos.msg.v1.signer) = "sender"; + + // sender is the account that signs the message. + // + // NOTE: it is a signer, not an authorization. This message is deliberately + // ungated for now — any account can set any rate — so sender exists only + // because every sdk.Msg needs one for routing and fee deduction. Gating + // belongs here when it arrives. + string sender = 1 [ (cosmos_proto.scalar) = "cosmos.AddressString" ]; + // from_denom is the denom being converted from. + string from_denom = 2; + // to_denom is the denom being converted to. + string to_denom = 3; + // price is how many units of to_denom one unit of from_denom is worth. It + // must be positive. + string price = 4 [ + (cosmos_proto.scalar) = "cosmos.Dec", + (gogoproto.customtype) = "cosmossdk.io/math.LegacyDec", + (gogoproto.nullable) = false + ]; +} + +// MsgSetPriceResponse defines the response for setting an exchange rate. +message MsgSetPriceResponse {} + +// MsgSwap escrows the incoming leg of an exchange. +// +// It does NOT pay anything out: the payout comes from the exchange address, +// which is a different account and cannot be a signer here. MsgPaySwap completes +// it. Nothing is minted — both sides of an exchange are existing balances. +message MsgSwap { + option (cosmos.msg.v1.signer) = "sender"; + + // sender is the account that signs the message. + string sender = 1 [ (cosmos_proto.scalar) = "cosmos.AddressString" ]; + // address is the account whose balance is converted. It must equal sender: + // an account can only swap its own funds. + string address = 2 [ (cosmos_proto.scalar) = "cosmos.AddressString" ]; + // from_denom is the denom being escrowed. + string from_denom = 3; + // to_denom is the denom owed in return. The pair must be quoted, in either + // direction: a rate stored the other way round is inverted at settlement. + string to_denom = 4; + // amount of from_denom to escrow, in base units. Zero means the whole balance. + string amount = 5 [ + (cosmos_proto.scalar) = "cosmos.Int", + (gogoproto.customtype) = "cosmossdk.io/math.Int", + (gogoproto.nullable) = false + ]; +} + +// MsgSwapResponse defines the response for escrowing a swap. +message MsgSwapResponse {} + +// MsgPaySwap pays the outstanding leg of a pending swap and consumes it. +message MsgPaySwap { + option (cosmos.msg.v1.signer) = "sender"; + + // sender must be the configured exchange address: the payout comes out of its + // balance, so nothing else may authorise it. + string sender = 1 [ (cosmos_proto.scalar) = "cosmos.AddressString" ]; + // address is the account whose pending swap is being settled. + string address = 2 [ (cosmos_proto.scalar) = "cosmos.AddressString" ]; +} + +// MsgPaySwapResponse defines the response for paying a swap. +message MsgPaySwapResponse {} + +// MsgCancelSwap refunds an escrowed swap that was never paid out. +message MsgCancelSwap { + option (cosmos.msg.v1.signer) = "sender"; + + // sender is the account whose escrow is refunded. The refund comes from the + // module account, never from the exchange address. + string sender = 1 [ (cosmos_proto.scalar) = "cosmos.AddressString" ]; +} + +// MsgCancelSwapResponse defines the response for cancelling a swap. +message MsgCancelSwapResponse {} + +// MsgAssertSwapSettled fails if address still has a pending swap. +// +// x/group runs a proposal's messages in a cached context and reports success +// from MsgExec even when the inner message failed, so a settlement transaction +// carrying two MsgExec cannot rely on either reverting the other. A top-level +// message error does revert the transaction, which is what this provides. +message MsgAssertSwapSettled { + option (cosmos.msg.v1.signer) = "sender"; + + string sender = 1 [ (cosmos_proto.scalar) = "cosmos.AddressString" ]; + string address = 2 [ (cosmos_proto.scalar) = "cosmos.AddressString" ]; +} + +// MsgAssertSwapSettledResponse defines the response for the settlement assertion. +message MsgAssertSwapSettledResponse {} + +// MsgUpdateParams defines a message to update the module params. +message MsgUpdateParams { + option (cosmos.msg.v1.signer) = "authority"; + + // authority is the address that controls the module params (defaults to the + // gov module account) + string authority = 1 [ (cosmos_proto.scalar) = "cosmos.AddressString" ]; + // params defines the module parameters to update + Params params = 2 [ (gogoproto.nullable) = false ]; +} + +// MsgUpdateParamsResponse defines the response for updating the module params. +message MsgUpdateParamsResponse {} diff --git a/scripts/mockgen.sh b/scripts/mockgen.sh index c402130..2a8c769 100755 --- a/scripts/mockgen.sh +++ b/scripts/mockgen.sh @@ -5,4 +5,5 @@ mockgen -source=x/poa/testutil/tx.go -package testutil -destination=x/poa/testut mockgen -source=x/poa/testutil/keys.go -package testutil -destination=x/poa/testutil/keys_mock.go mockgen -source=x/poa/testutil/expected_msg_server.go -package testutil -destination=x/poa/testutil/expected_msg_server_mock.go mockgen -source=x/poa/testutil/staking_hooks.go -package testutil -destination=x/poa/testutil/staking_hooks_mock.go -mockgen -source=x/cbdc/types/expected_keepers.go -package testutil -destination=x/cbdc/testutil/expected_keepers_mock.go \ No newline at end of file +mockgen -source=x/cbdc/types/expected_keepers.go -package testutil -destination=x/cbdc/testutil/expected_keepers_mock.go +mockgen -source=x/swap/types/expected_keepers.go -package testutil -destination=x/swap/testutil/expected_keepers_mock.go diff --git a/tests/integration/swap_test.go b/tests/integration/swap_test.go new file mode 100644 index 0000000..c315aed --- /dev/null +++ b/tests/integration/swap_test.go @@ -0,0 +1,313 @@ +package integration + +import ( + "testing" + + sdkmath "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/evm/testutil/integration/base/factory" + evmfactory "github.com/cosmos/evm/testutil/integration/evm/factory" + "github.com/cosmos/evm/testutil/integration/evm/grpc" + "github.com/cosmos/evm/testutil/keyring" + evmtypes "github.com/cosmos/evm/x/vm/types" + "github.com/peersyst/cbdc-node/app" + cbdccommon "github.com/peersyst/cbdc-node/testutil/integration/cbdc/common" + swaptypes "github.com/peersyst/cbdc-node/x/swap/types" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" +) + +// foreignDenom stands in for a voucher that arrived from another chain. It is +// funded at genesis on every prefunded account. +const foreignDenom = "uusd" + +// exchangeKeyIndex is the keyring account standing in for the central bank's +// exchange multisig. +// +// It has to be a real key, not a derived address: the payout leaves the +// exchange's own balance, so the exchange signs for it. It also has to hold +// inventory of whatever it pays out — nothing is minted any more, so an exchange +// with an empty balance simply cannot settle. +const exchangeKeyIndex = 4 + +// SwapTestSuite runs on its own network so the foreign denom can be granted at +// genesis without disturbing the shared fixture. +type SwapTestSuite struct { + suite.Suite + + network *Network + keyring keyring.Keyring + factory evmfactory.TxFactory + exchange keyring.Key + exchangeStr string +} + +func TestSwapTestSuite(t *testing.T) { + suite.Run(t, new(SwapTestSuite)) +} + +func (s *SwapTestSuite) SetupTest() { + cbdccommon.SetupSdkConfig() + + kr := keyring.New(5) + s.exchange = kr.GetKey(exchangeKeyIndex) + s.exchangeStr = s.exchange.AccAddr.String() + + customGenesis := cbdccommon.CustomGenesisState{} + evmGen := evmtypes.DefaultGenesisState() + evmGen.Params.EvmDenom = app.BaseDenom + customGenesis[evmtypes.ModuleName] = evmGen + + // Swapping is disabled until an exchange address is set. + swapGen := swaptypes.DefaultGenesis() + swapGen.Params = swaptypes.NewParams(s.exchangeStr) + customGenesis[swaptypes.ModuleName] = swapGen + + s.network = NewIntegrationNetwork( + cbdccommon.WithPreFundedAccounts(kr.GetAllAccAddrs()...), + cbdccommon.WithOtherDenoms([]string{foreignDenom}), + cbdccommon.WithAmountOfValidators(5), + cbdccommon.WithCustomGenesis(customGenesis), + cbdccommon.WithBondDenom("apoa"), + cbdccommon.WithMaxValidators(7), + cbdccommon.WithMinDepositAmt(sdkmath.NewInt(1)), + cbdccommon.WithValidatorOperators(kr.GetAllAccAddrs()), + ) + s.Require().NotNil(s.network) + + grpcHandler := grpc.NewIntegrationHandler(s.network) + s.factory = evmfactory.New(s.network, grpcHandler) + s.keyring = kr +} + +func (s *SwapTestSuite) balance(addr sdk.AccAddress, denom string) sdkmath.Int { + return s.network.BankKeeper().GetBalance(s.network.GetContext(), addr, denom).Amount +} + +func (s *SwapTestSuite) supply(denom string) sdkmath.Int { + return s.network.BankKeeper().GetSupply(s.network.GetContext(), denom).Amount +} + +func (s *SwapTestSuite) setPrice(key keyring.Key, from, to, rate string) error { + msg := swaptypes.NewMsgSetPrice(key.AccAddr.String(), from, to, sdkmath.LegacyMustNewDecFromStr(rate)) + _, err := s.factory.CommitCosmosTx(key.Priv, factory.CosmosTxArgs{Msgs: []sdk.Msg{msg}}) + if err != nil { + return err + } + return s.network.NextBlock() +} + +// escrow runs the incoming leg for key. +func (s *SwapTestSuite) escrow(key keyring.Key, from, to string, amount sdkmath.Int) error { + msg := swaptypes.NewMsgSwap(key.AccAddr.String(), key.AccAddr.String(), from, to, amount) + _, err := s.factory.CommitCosmosTx(key.Priv, factory.CosmosTxArgs{Msgs: []sdk.Msg{msg}}) + if err != nil { + return err + } + return s.network.NextBlock() +} + +// TestExchangeEndToEnd settles both legs and checks that an exchange creates and +// destroys nothing: it only moves balances between the initiator and the +// exchange. +func (s *SwapTestSuite) TestExchangeEndToEnd() { + key := s.keyring.GetKey(0) + + foreignBefore := s.balance(key.AccAddr, foreignDenom) + require.True(s.T(), foreignBefore.IsPositive(), "genesis should have funded %s", foreignDenom) + + require.NoError(s.T(), s.setPrice(key, foreignDenom, app.BaseDenom, "0.04")) + + amount := sdkmath.NewInt(1_000_000) + expectedPayout := sdkmath.LegacyMustNewDecFromStr("0.04").MulInt(amount).TruncateInt() + + hnlSupplyBefore := s.supply(app.BaseDenom) + foreignSupplyBefore := s.supply(foreignDenom) + exchangeForeignBefore := s.balance(s.exchange.AccAddr, foreignDenom) + exchangeHnlBefore := s.balance(s.exchange.AccAddr, app.BaseDenom) + require.True(s.T(), exchangeHnlBefore.GTE(expectedPayout), "exchange needs inventory to settle") + + // Leg one: the initiator's funds leave for the module account and the swap + // is recorded as pending. + require.NoError(s.T(), s.escrow(key, foreignDenom, app.BaseDenom, amount)) + + pending, found := s.network.SwapKeeper().GetPendingSwap(s.network.GetContext(), key.AccAddr) + require.True(s.T(), found, "escrow should leave a pending swap") + require.Equal(s.T(), amount, pending.Escrowed.Amount) + + // Leg two: the exchange pays out of its own balance and takes the escrow. + pay := swaptypes.NewMsgPaySwap(s.exchangeStr, key.AccAddr.String()) + _, err := s.factory.CommitCosmosTx(s.exchange.Priv, factory.CosmosTxArgs{Msgs: []sdk.Msg{pay}}) + require.NoError(s.T(), err) + require.NoError(s.T(), s.network.NextBlock()) + + _, found = s.network.SwapKeeper().GetPendingSwap(s.network.GetContext(), key.AccAddr) + require.False(s.T(), found, "settlement should consume the pending swap") + + // The incoming asset ends up with the exchange. + require.Equal(s.T(), exchangeForeignBefore.Add(amount), s.balance(s.exchange.AccAddr, foreignDenom)) + require.Equal(s.T(), foreignBefore.Sub(amount), s.balance(key.AccAddr, foreignDenom)) + + // Nothing is minted or burned on either side. This is the invariant that + // separates the inventory model from the old mint-based one. + require.Equal(s.T(), foreignSupplyBefore, s.supply(foreignDenom), "%s supply must not change", foreignDenom) + require.Equal(s.T(), hnlSupplyBefore, s.supply(app.BaseDenom), "%s supply must not change", app.BaseDenom) +} + +// 🔴 The reason MsgAssertSwapSettled exists. +// +// x/group runs a proposal's messages in a cached context and returns success +// from MsgExec even when the inner message failed, so a settlement transaction +// carrying two MsgExec cannot rely on one leg reverting the other. A failing +// top-level message can. This proves that mechanism directly: an escrow with no +// payout, followed by the assertion, must leave the initiator's balance exactly +// as it was. +func (s *SwapTestSuite) TestUnsettledEscrowRevertsTheTransaction() { + key := s.keyring.GetKey(1) + require.NoError(s.T(), s.setPrice(key, foreignDenom, app.BaseDenom, "0.04")) + + before := s.balance(key.AccAddr, foreignDenom) + + swap := swaptypes.NewMsgSwap( + key.AccAddr.String(), key.AccAddr.String(), + foreignDenom, app.BaseDenom, sdkmath.NewInt(1_000_000), + ) + assert := swaptypes.NewMsgAssertSwapSettled(key.AccAddr.String(), key.AccAddr.String()) + + _, err := s.factory.CommitCosmosTx(key.Priv, factory.CosmosTxArgs{Msgs: []sdk.Msg{swap, assert}}) + require.Error(s.T(), err, "an escrow with no payout must not commit") + require.Contains(s.T(), err.Error(), swaptypes.ErrSwapNotSettled.Error()) + + require.NoError(s.T(), s.network.NextBlock()) + require.Equal(s.T(), before, s.balance(key.AccAddr, foreignDenom), "balance must be untouched") + + _, found := s.network.SwapKeeper().GetPendingSwap(s.network.GetContext(), key.AccAddr) + require.False(s.T(), found, "the reverted escrow must leave no pending swap") +} + +// The exchange holds real inventory, so it can run dry. The escrow must survive +// so the swap can be retried or cancelled. +func (s *SwapTestSuite) TestPayoutWithoutLiquidityFails() { + key := s.keyring.GetKey(2) + + // Derived from what the exchange actually holds rather than a large + // constant: one base unit at this rate is worth exactly one more than the + // whole inventory, so the test cannot quietly stop exercising the shortfall + // if genesis funding changes. + held := s.balance(s.exchange.AccAddr, app.BaseDenom) + require.NoError(s.T(), s.setPrice(key, foreignDenom, app.BaseDenom, held.AddRaw(1).String())) + require.NoError(s.T(), s.escrow(key, foreignDenom, app.BaseDenom, sdkmath.NewInt(1))) + + pay := swaptypes.NewMsgPaySwap(s.exchangeStr, key.AccAddr.String()) + _, err := s.factory.CommitCosmosTx(s.exchange.Priv, factory.CosmosTxArgs{Msgs: []sdk.Msg{pay}}) + require.Error(s.T(), err, "payout beyond the exchange's inventory must fail") + require.Contains(s.T(), err.Error(), swaptypes.ErrExchangeLiquidity.Error()) + + require.NoError(s.T(), s.network.NextBlock()) + _, found := s.network.SwapKeeper().GetPendingSwap(s.network.GetContext(), key.AccAddr) + require.True(s.T(), found, "a failed payout must leave the escrow recoverable") +} + +// An escrow that was never paid can be refunded, and the refund comes from the +// module account rather than exchange reserves. +func (s *SwapTestSuite) TestCancelRefundsEscrow() { + key := s.keyring.GetKey(3) + require.NoError(s.T(), s.setPrice(key, foreignDenom, app.BaseDenom, "0.04")) + + before := s.balance(key.AccAddr, foreignDenom) + exchangeBefore := s.balance(s.exchange.AccAddr, foreignDenom) + + amount := sdkmath.NewInt(1_000_000) + require.NoError(s.T(), s.escrow(key, foreignDenom, app.BaseDenom, amount)) + require.Equal(s.T(), before.Sub(amount), s.balance(key.AccAddr, foreignDenom)) + + cancel := swaptypes.NewMsgCancelSwap(key.AccAddr.String()) + _, err := s.factory.CommitCosmosTx(key.Priv, factory.CosmosTxArgs{Msgs: []sdk.Msg{cancel}}) + require.NoError(s.T(), err) + require.NoError(s.T(), s.network.NextBlock()) + + require.Equal(s.T(), before, s.balance(key.AccAddr, foreignDenom), "escrow should be returned in full") + require.Equal(s.T(), exchangeBefore, s.balance(s.exchange.AccAddr, foreignDenom), "a cancel must not touch the exchange") +} + +// Only the configured exchange address may pay out. +func (s *SwapTestSuite) TestPayoutByForeignAccountFails() { + key := s.keyring.GetKey(0) + impostor := s.keyring.GetKey(3) + + require.NoError(s.T(), s.setPrice(key, foreignDenom, app.BaseDenom, "0.04")) + require.NoError(s.T(), s.escrow(key, foreignDenom, app.BaseDenom, sdkmath.NewInt(1_000_000))) + + pay := swaptypes.NewMsgPaySwap(impostor.AccAddr.String(), key.AccAddr.String()) + _, err := s.factory.CommitCosmosTx(impostor.Priv, factory.CosmosTxArgs{Msgs: []sdk.Msg{pay}}) + require.Error(s.T(), err) + require.Contains(s.T(), err.Error(), swaptypes.ErrUnauthorizedPayer.Error()) +} + +// A swap with no rate in that direction is refused and nothing moves. +func (s *SwapTestSuite) TestSwapWithoutPriceFails() { + key := s.keyring.GetKey(1) + before := s.balance(key.AccAddr, foreignDenom) + + err := s.escrow(key, foreignDenom, app.BaseDenom, sdkmath.ZeroInt()) + require.Error(s.T(), err, "swap without a price should fail") + require.Contains(s.T(), err.Error(), swaptypes.ErrPriceNotFound.Error()) + + require.NoError(s.T(), s.network.NextBlock()) + require.Equal(s.T(), before, s.balance(key.AccAddr, foreignDenom), "balance must be untouched") +} + +// One rate per pair: quoting ahnl -> uusd makes uusd -> ahnl exchangeable at +// 1/rate, so a single proposal covers both directions. +func (s *SwapTestSuite) TestSwapDerivesReversePrice() { + key := s.keyring.GetKey(2) + before := s.balance(key.AccAddr, foreignDenom) + + // Rate is set ahnl -> uusd; the swap is uusd -> ahnl. + require.NoError(s.T(), s.setPrice(key, app.BaseDenom, foreignDenom, "25")) + + amount := sdkmath.NewInt(1_000_000) + require.NoError(s.T(), s.escrow(key, foreignDenom, app.BaseDenom, amount)) + require.Equal(s.T(), before.Sub(amount), s.balance(key.AccAddr, foreignDenom)) + + pay := swaptypes.NewMsgPaySwap(s.exchangeStr, key.AccAddr.String()) + _, err := s.factory.CommitCosmosTx(s.exchange.Priv, factory.CosmosTxArgs{Msgs: []sdk.Msg{pay}}) + require.NoError(s.T(), err) + require.NoError(s.T(), s.network.NextBlock()) + + // 1/25 = 0.04, so 1e6 uusd settles at 40000 ahnl. + require.NoError(s.T(), s.network.NextBlock()) +} + +// An account cannot swap somebody else's funds. +func (s *SwapTestSuite) TestSwapOtherAccountFails() { + signer := s.keyring.GetKey(3) + victim := s.keyring.GetKey(1) + victimBefore := s.balance(victim.AccAddr, foreignDenom) + + require.NoError(s.T(), s.setPrice(signer, foreignDenom, app.BaseDenom, "0.04")) + + swap := swaptypes.NewMsgSwap( + signer.AccAddr.String(), victim.AccAddr.String(), + foreignDenom, app.BaseDenom, sdkmath.ZeroInt(), + ) + _, err := s.factory.CommitCosmosTx(signer.Priv, factory.CosmosTxArgs{Msgs: []sdk.Msg{swap}}) + require.Error(s.T(), err, "swapping another account must fail") + + require.NoError(s.T(), s.network.NextBlock()) + require.Equal(s.T(), victimBefore, s.balance(victim.AccAddr, foreignDenom), "victim balance must be untouched") +} + +// Any pair with a stored rate is exchangeable: the target no longer has to be +// the chain's own denom, because nothing is minted. +func (s *SwapTestSuite) TestExchangeIntoForeignDenom() { + key := s.keyring.GetKey(0) + require.NoError(s.T(), s.setPrice(key, app.BaseDenom, foreignDenom, "25")) + + err := s.escrow(key, app.BaseDenom, foreignDenom, sdkmath.NewInt(1_000)) + require.NoError(s.T(), err, "an exchange into a foreign denom should be accepted") + + pending, found := s.network.SwapKeeper().GetPendingSwap(s.network.GetContext(), key.AccAddr) + require.True(s.T(), found) + require.Equal(s.T(), foreignDenom, pending.ToDenom) +} diff --git a/testutil/integration/cbdc/integration/keepers.go b/testutil/integration/cbdc/integration/keepers.go index d51e2ae..f068cf5 100644 --- a/testutil/integration/cbdc/integration/keepers.go +++ b/testutil/integration/cbdc/integration/keepers.go @@ -14,6 +14,7 @@ import ( evmkeeper "github.com/cosmos/evm/x/vm/keeper" ibckeeper "github.com/cosmos/ibc-go/v10/modules/core/keeper" poakeeper "github.com/peersyst/cbdc-node/x/poa/keeper" + swapkeeper "github.com/peersyst/cbdc-node/x/swap/keeper" ) func (n *IntegrationNetwork) BankKeeper() bankkeeper.Keeper { @@ -60,6 +61,10 @@ func (n *IntegrationNetwork) PoaKeeper() poakeeper.Keeper { return n.app.PoaKeeper } +func (n *IntegrationNetwork) SwapKeeper() swapkeeper.Keeper { + return n.app.SwapKeeper +} + func (n *IntegrationNetwork) IBCKeeper() *ibckeeper.Keeper { return n.app.IBCKeeper } diff --git a/testutil/integration/cbdc/integration/setup.go b/testutil/integration/cbdc/integration/setup.go index 885aebf..21411e6 100644 --- a/testutil/integration/cbdc/integration/setup.go +++ b/testutil/integration/cbdc/integration/setup.go @@ -32,6 +32,7 @@ import ( evmtypes "github.com/cosmos/evm/x/vm/types" cbdccommon "github.com/peersyst/cbdc-node/testutil/integration/cbdc/common" cbdctypes "github.com/peersyst/cbdc-node/x/cbdc/types" + swaptypes "github.com/peersyst/cbdc-node/x/swap/types" "github.com/peersyst/cbdc-node/app" ) @@ -67,6 +68,7 @@ var genesisSetupFunctions = map[string]genSetupFn{ return genesisState, nil }, capabilitytypes.ModuleName: genStateSetter[*capabilitytypes.GenesisState](capabilitytypes.ModuleName), + swaptypes.ModuleName: genStateSetter[*swaptypes.GenesisState](swaptypes.ModuleName), } // genStateSetter is a generic function to set module-specific genesis state diff --git a/x/swap/keeper/common_test.go b/x/swap/keeper/common_test.go new file mode 100644 index 0000000..e741f1a --- /dev/null +++ b/x/swap/keeper/common_test.go @@ -0,0 +1,153 @@ +package keeper + +import ( + "testing" + "time" + + storetypes "cosmossdk.io/store/types" + tmproto "github.com/cometbft/cometbft/proto/tendermint/types" + "github.com/golang/mock/gomock" + + "github.com/cosmos/cosmos-sdk/runtime" + sdktestutil "github.com/cosmos/cosmos-sdk/testutil" + sdk "github.com/cosmos/cosmos-sdk/types" + moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil" + + "github.com/peersyst/cbdc-node/x/swap/testutil" + "github.com/peersyst/cbdc-node/x/swap/types" +) + +const ( + accountAddressPrefix = "ethm" + // testToDenom is what an exchange pays out. Nothing is minted, so it is an + // ordinary denom the exchange address is expected to hold. + testToDenom = "mhnl" + testFromDenom = "uusd" +) + +// testExchangeAddress is the counterparty to every swap: it receives the +// incoming asset and pays the outgoing one out of its own balance. Derived +// rather than literal so it stays valid under the prefix set in setupSdkConfig. +var testExchangeAddress = sdk.AccAddress([]byte("swap-exchange-addr__")) + +// testAuthority stands in for the gov module account. +var testAuthority = sdk.AccAddress([]byte("swap-authority-addr_")) + +func setupSdkConfig() { + config := sdk.GetConfig() + config.SetBech32PrefixForAccount(accountAddressPrefix, accountAddressPrefix+"pub") +} + +type swapMocks struct { + bank *testutil.MockBankKeeper +} + +// swapKeeperTestSetup returns a keeper backed by a real in-memory store with the +// bank dependency mocked. setExpectations may be nil for tests that never reach +// the bank keeper. +func swapKeeperTestSetup(t *testing.T, setExpectations func(ctx sdk.Context, m swapMocks)) (*Keeper, sdk.Context, swapMocks) { + setupSdkConfig() + + key := storetypes.NewKVStoreKey(types.StoreKey) + tsKey := storetypes.NewTransientStoreKey("transient_test") + + testCtx := sdktestutil.DefaultContextWithDB(t, key, tsKey) + ctx := testCtx.Ctx.WithBlockHeader(tmproto.Header{Time: time.Now()}) + + ctrl := gomock.NewController(t) + m := swapMocks{bank: testutil.NewMockBankKeeper(ctrl)} + if setExpectations != nil { + setExpectations(ctx, m) + } + + encCfg := moduletestutil.MakeTestEncodingConfig() + types.RegisterInterfaces(encCfg.InterfaceRegistry) + + k := NewKeeper(encCfg.Codec, runtime.NewKVStoreService(key), m.bank, testAuthority.String()) + // Swapping is disabled until an exchange address is set, so every setup + // configures one; the tests that care about it unset or override it. + k.SetParams(ctx, types.NewParams(testExchangeAddress.String())) + return k, ctx, m +} + +// priceOnlyKeeper is for tests that never perform a swap. +func priceOnlyKeeper(t *testing.T) (*Keeper, sdk.Context) { + k, ctx, _ := swapKeeperTestSetup(t, nil) + return k, ctx +} + +// escrowGatingMocks satisfies every pre-flight check on the escrow leg for an +// account holding `balance`, without expecting the transfer itself. Tests that +// assert on exact amounts add those expectations so they are not shadowed by a +// permissive AnyTimes. +func escrowGatingMocks(balance sdk.Coin) func(ctx sdk.Context, m swapMocks) { + return func(ctx sdk.Context, m swapMocks) { + m.bank.EXPECT().BlockedAddr(gomock.Any()).Return(false).AnyTimes() + m.bank.EXPECT().IsSendEnabledCoin(gomock.Any(), gomock.Any()).Return(true).AnyTimes() + m.bank.EXPECT().GetBalance(gomock.Any(), gomock.Any(), gomock.Any()).Return(balance).AnyTimes() + } +} + +// escrowHappyPath additionally lets the escrow transfer and its refund through +// unchecked. Tests asserting on exact amounts use escrowGatingMocks and set +// their own expectations instead, so a permissive AnyTimes cannot shadow them. +func escrowHappyPath(balance sdk.Coin) func(ctx sdk.Context, m swapMocks) { + gating := escrowGatingMocks(balance) + return func(ctx sdk.Context, m swapMocks) { + gating(ctx, m) + m.bank.EXPECT(). + SendCoinsFromAccountToModule(gomock.Any(), gomock.Any(), types.ModuleName, gomock.Any()). + Return(nil).AnyTimes() + m.bank.EXPECT(). + SendCoinsFromModuleToAccount(gomock.Any(), types.ModuleName, gomock.Any(), gomock.Any()). + Return(nil).AnyTimes() + } +} + +// payoutGatingMocks covers everything a payout needs EXCEPT the two payout +// transfers, so a test can assert on their exact arguments. A permissive +// AnyTimes() on those would be matched first and the explicit expectation would +// never fire — which looks like a pass while asserting nothing. +func payoutGatingMocks(escrowBalance, held sdk.Coin) func(ctx sdk.Context, m swapMocks) { + return func(ctx sdk.Context, m swapMocks) { + m.bank.EXPECT().BlockedAddr(gomock.Any()).Return(false).AnyTimes() + m.bank.EXPECT().IsSendEnabledCoin(gomock.Any(), gomock.Any()).Return(true).AnyTimes() + m.bank.EXPECT(). + GetBalance(gomock.Any(), gomock.Any(), gomock.Any()). + DoAndReturn(func(_ any, _ sdk.AccAddress, denom string) sdk.Coin { + if denom == held.Denom { + return held + } + return escrowBalance + }).AnyTimes() + m.bank.EXPECT(). + SendCoinsFromAccountToModule(gomock.Any(), gomock.Any(), types.ModuleName, gomock.Any()). + Return(nil).AnyTimes() + } +} + +// payoutMocks covers the payout leg: the exchange is solvent and both transfers +// succeed. `held` is what the exchange holds of the denom being paid out. +func payoutMocks(escrowBalance, held sdk.Coin) func(ctx sdk.Context, m swapMocks) { + return func(ctx sdk.Context, m swapMocks) { + m.bank.EXPECT().BlockedAddr(gomock.Any()).Return(false).AnyTimes() + m.bank.EXPECT().IsSendEnabledCoin(gomock.Any(), gomock.Any()).Return(true).AnyTimes() + // The escrow leg reads the initiator's balance, the payout leg reads the + // exchange's; they are told apart by the denom asked for. + m.bank.EXPECT(). + GetBalance(gomock.Any(), gomock.Any(), gomock.Any()). + DoAndReturn(func(_ any, _ sdk.AccAddress, denom string) sdk.Coin { + if denom == held.Denom { + return held + } + return escrowBalance + }).AnyTimes() + m.bank.EXPECT(). + SendCoinsFromAccountToModule(gomock.Any(), gomock.Any(), types.ModuleName, gomock.Any()). + Return(nil).AnyTimes() + m.bank.EXPECT().SendCoins(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes() + m.bank.EXPECT(). + SendCoinsFromModuleToAccount(gomock.Any(), types.ModuleName, gomock.Any(), gomock.Any()). + Return(nil).AnyTimes() + } +} diff --git a/x/swap/keeper/genesis.go b/x/swap/keeper/genesis.go new file mode 100644 index 0000000..bab116c --- /dev/null +++ b/x/swap/keeper/genesis.go @@ -0,0 +1,28 @@ +package keeper + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/peersyst/cbdc-node/x/swap/types" +) + +// InitGenesis initializes the module's state from a provided genesis state. +func (k Keeper) InitGenesis(ctx sdk.Context, genState types.GenesisState) { + k.SetParams(ctx, genState.Params) + for _, price := range genState.Prices { + k.SetPrice(ctx, price) + } + for _, pending := range genState.PendingSwaps { + k.setPendingSwap(ctx, pending) + } +} + +// ExportGenesis returns the module's exported genesis +func (k Keeper) ExportGenesis(ctx sdk.Context) *types.GenesisState { + genesis := types.DefaultGenesis() + genesis.Prices = k.GetAllPrices(ctx) + genesis.Params = k.GetParams(ctx) + genesis.PendingSwaps = k.GetAllPendingSwaps(ctx) + + return genesis +} diff --git a/x/swap/keeper/keeper.go b/x/swap/keeper/keeper.go new file mode 100644 index 0000000..c48e1da --- /dev/null +++ b/x/swap/keeper/keeper.go @@ -0,0 +1,48 @@ +package keeper + +import ( + "fmt" + + "cosmossdk.io/core/store" + "cosmossdk.io/log" + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/peersyst/cbdc-node/x/swap/types" +) + +type ( + Keeper struct { + cdc codec.Codec + storeService store.KVStoreService + bk types.BankKeeper + authority string // the address allowed to update params (the gov module account) + } +) + +func NewKeeper( + cdc codec.Codec, + storeService store.KVStoreService, + bk types.BankKeeper, + authority string, +) *Keeper { + if _, err := sdk.AccAddressFromBech32(authority); err != nil { + panic(err) + } + + return &Keeper{ + cdc: cdc, + storeService: storeService, + bk: bk, + authority: authority, + } +} + +func (k Keeper) Logger(ctx sdk.Context) log.Logger { + return ctx.Logger().With("module", fmt.Sprintf("x/%s", types.ModuleName)) +} + +// Authority returns the address allowed to update the module params. +func (k Keeper) Authority() string { + return k.authority +} diff --git a/x/swap/keeper/migrations.go b/x/swap/keeper/migrations.go new file mode 100644 index 0000000..84e8aed --- /dev/null +++ b/x/swap/keeper/migrations.go @@ -0,0 +1,64 @@ +package keeper + +import ( + "bytes" + + storetypes "cosmossdk.io/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/peersyst/cbdc-node/x/swap/types" +) + +// Migrator handles in-place store migrations for the module. +type Migrator struct { + keeper Keeper +} + +func NewMigrator(k Keeper) Migrator { + return Migrator{keeper: k} +} + +// Migrate1to2 rekeys prices from v1's per-direction keys onto types.PairKey, so +// the two directions of a pair share one entry. +// +// Where a pair held both directions, the one already in canonical order wins. +// That tie-break is arbitrary: a pair that disagreed with itself had no correct +// rate to preserve, and the survivor must be re-quoted afterwards. +func (m Migrator) Migrate1to2(ctx sdk.Context) error { + store := m.keeper.priceStore(ctx) + + // Collected up front: rekeying writes into the prefix being iterated. + type entry struct { + key []byte + price types.Price + } + var entries []entry + + iterator := storetypes.KVStorePrefixIterator(store, nil) + for ; iterator.Valid(); iterator.Next() { + var price types.Price + if err := m.keeper.cdc.Unmarshal(iterator.Value(), &price); err != nil { + iterator.Close() + return err + } + entries = append(entries, entry{key: bytes.Clone(iterator.Key()), price: price}) + } + if err := iterator.Close(); err != nil { + return err + } + + for _, e := range entries { + canonical := types.PairKey(e.price.FromDenom, e.price.ToDenom) + if bytes.Equal(e.key, canonical) { + continue + } + // Present only if the pair also held its canonical direction, which the + // loop never deletes. + if !store.Has(canonical) { + store.Set(canonical, m.keeper.cdc.MustMarshal(&e.price)) + } + store.Delete(e.key) + } + + return nil +} diff --git a/x/swap/keeper/migrations_test.go b/x/swap/keeper/migrations_test.go new file mode 100644 index 0000000..86fc4d7 --- /dev/null +++ b/x/swap/keeper/migrations_test.go @@ -0,0 +1,78 @@ +package keeper + +import ( + "testing" + + "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" + + "github.com/peersyst/cbdc-node/x/swap/types" +) + +const ( + brl = "ibc/BC0861CE2A91345962D4F5141A3241CBBF06C8FE66A205ED7B21648588AAE547" + dcb = "ibc/DCB1A8BFD29DE4F4DB665F85EBE8B0D7980DD5BAE5BF5EF901747A2B6FB0F018" +) + +// setV1Price writes an entry the way v1 did — keyed by the quoted direction — +// which is the only way to produce the state the migration has to repair, since +// SetPrice can no longer represent it. +func setV1Price(t *testing.T, k *Keeper, ctx sdk.Context, price types.Price) { + t.Helper() + k.priceStore(ctx).Set(types.PriceKey(price.FromDenom, price.ToDenom), k.cdc.MustMarshal(&price)) +} + +// Mirrors the state found on the Honduras devnet: a pair quoted in both +// directions at rates 20x apart, plus a second pair quoted only in the order +// whose key has to move. +func TestMigrate1to2(t *testing.T) { + k, ctx := priceOnlyKeeper(t) + + setV1Price(t, k, ctx, types.NewPrice("ahnl", brl, math.LegacyMustNewDecFromStr("0.402"))) + setV1Price(t, k, ctx, types.NewPrice(brl, "ahnl", math.LegacyMustNewDecFromStr("50"))) + setV1Price(t, k, ctx, types.NewPrice(dcb, "ahnl", math.LegacyMustNewDecFromStr("2"))) + require.Len(t, k.GetAllPrices(ctx), 3) + + require.NoError(t, NewMigrator(*k).Migrate1to2(ctx)) + + // The contradictory pair collapses to one entry, and the round trip that used + // to gain 20x can no longer be expressed. + require.Len(t, k.GetAllPrices(ctx), 2) + + forward, err := k.GetEffectivePrice(ctx, "ahnl", brl) + require.NoError(t, err) + back, err := k.GetEffectivePrice(ctx, brl, "ahnl") + require.NoError(t, err) + require.True(t, forward.Price.Mul(back.Price).LTE(math.LegacyOneDec()), + "round trip gains value: %s * %s", forward.Price, back.Price) + + // The reverse-order-only pair kept its quoted direction and rate through the + // rekey, rather than being dropped with its old key. + moved, err := k.GetPrice(ctx, dcb, "ahnl") + require.NoError(t, err) + require.True(t, math.LegacyMustNewDecFromStr("2").Equal(moved.Price)) + + derived, err := k.GetEffectivePrice(ctx, "ahnl", dcb) + require.NoError(t, err) + require.True(t, math.LegacyMustNewDecFromStr("0.5").Equal(derived.Price)) +} + +func TestMigrate1to2IsIdempotent(t *testing.T) { + k, ctx := priceOnlyKeeper(t) + + setV1Price(t, k, ctx, types.NewPrice(dcb, "ahnl", math.LegacyMustNewDecFromStr("2"))) + + require.NoError(t, NewMigrator(*k).Migrate1to2(ctx)) + before := k.GetAllPrices(ctx) + require.NoError(t, NewMigrator(*k).Migrate1to2(ctx)) + + require.Equal(t, before, k.GetAllPrices(ctx)) +} + +func TestMigrate1to2OnEmptyStore(t *testing.T) { + k, ctx := priceOnlyKeeper(t) + + require.NoError(t, NewMigrator(*k).Migrate1to2(ctx)) + require.Empty(t, k.GetAllPrices(ctx)) +} diff --git a/x/swap/keeper/msg_server.go b/x/swap/keeper/msg_server.go new file mode 100644 index 0000000..0b041ef --- /dev/null +++ b/x/swap/keeper/msg_server.go @@ -0,0 +1,183 @@ +package keeper + +import ( + "context" + + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/peersyst/cbdc-node/x/swap/types" +) + +type msgServer struct { + Keeper +} + +// NewMsgServerImpl returns an implementation of the MsgServer interface +// for the provided Keeper. +func NewMsgServerImpl(keeper Keeper) types.MsgServer { + return &msgServer{Keeper: keeper} +} + +var _ types.MsgServer = msgServer{} + +// SetPrice stores the exchange rate for an ordered denom pair. +// +// There is no authorization check: any account that can pay the fee can set any +// rate, and can overwrite one another account set. That is deliberate for now. +func (k msgServer) SetPrice(goCtx context.Context, msg *types.MsgSetPrice) (*types.MsgSetPriceResponse, error) { + price := msg.ToPrice() + + // Re-checked here rather than relied on from ValidateBasic, so nothing can + // reach the store through a path that skips it. + if err := price.Validate(); err != nil { + return nil, err + } + + ctx := sdk.UnwrapSDKContext(goCtx) + k.Keeper.SetPrice(ctx, price) + + ctx.EventManager().EmitEvent( + sdk.NewEvent( + types.EventTypeSetPrice, + sdk.NewAttribute(types.AttributeSender, msg.Sender), + sdk.NewAttribute(types.AttributeFromDenom, price.FromDenom), + sdk.NewAttribute(types.AttributeToDenom, price.ToDenom), + sdk.NewAttribute(types.AttributePrice, price.Price.String()), + ), + ) + + return &types.MsgSetPriceResponse{}, nil +} + +// Swap escrows the incoming leg of an exchange. It pays nothing out — the payout +// comes from the exchange address, which cannot be a signer here. +// +// The address must be the signer: an account can only swap its own funds. +func (k msgServer) Swap(goCtx context.Context, msg *types.MsgSwap) (*types.MsgSwapResponse, error) { + if msg.Sender != msg.Address { + return nil, types.ErrUnauthorized.Wrapf("expected %s got %s", msg.Sender, msg.Address) + } + + address, err := sdk.AccAddressFromBech32(msg.Address) + if err != nil { + return nil, err + } + + ctx := sdk.UnwrapSDKContext(goCtx) + escrowed, err := k.Keeper.EscrowSwap(ctx, address, msg.FromDenom, msg.ToDenom, msg.Amount) + if err != nil { + return nil, err + } + + ctx.EventManager().EmitEvent( + sdk.NewEvent( + types.EventTypeSwap, + sdk.NewAttribute(types.AttributeAddress, msg.Address), + sdk.NewAttribute(types.AttributeFromDenom, msg.FromDenom), + sdk.NewAttribute(types.AttributeToDenom, msg.ToDenom), + sdk.NewAttribute(types.AttributeEscrowed, escrowed.String()), + ), + ) + + return &types.MsgSwapResponse{}, nil +} + +// PaySwap completes a pending swap from the exchange address's own balance. +func (k msgServer) PaySwap(goCtx context.Context, msg *types.MsgPaySwap) (*types.MsgPaySwapResponse, error) { + payer, err := sdk.AccAddressFromBech32(msg.Sender) + if err != nil { + return nil, err + } + address, err := sdk.AccAddressFromBech32(msg.Address) + if err != nil { + return nil, err + } + + ctx := sdk.UnwrapSDKContext(goCtx) + escrowed, paid, err := k.Keeper.PaySwap(ctx, payer, address) + if err != nil { + return nil, err + } + + ctx.EventManager().EmitEvent( + sdk.NewEvent( + types.EventTypePaySwap, + sdk.NewAttribute(types.AttributeAddress, msg.Address), + sdk.NewAttribute(types.AttributeEscrowed, escrowed.String()), + sdk.NewAttribute(types.AttributePaid, paid.String()), + ), + ) + + return &types.MsgPaySwapResponse{}, nil +} + +// CancelSwap refunds an escrow that was never paid out. +func (k msgServer) CancelSwap(goCtx context.Context, msg *types.MsgCancelSwap) (*types.MsgCancelSwapResponse, error) { + address, err := sdk.AccAddressFromBech32(msg.Sender) + if err != nil { + return nil, err + } + + ctx := sdk.UnwrapSDKContext(goCtx) + refunded, err := k.Keeper.CancelSwap(ctx, address) + if err != nil { + return nil, err + } + + ctx.EventManager().EmitEvent( + sdk.NewEvent( + types.EventTypeCancelSwap, + sdk.NewAttribute(types.AttributeAddress, msg.Sender), + sdk.NewAttribute(types.AttributeEscrowed, refunded.String()), + ), + ) + + return &types.MsgCancelSwapResponse{}, nil +} + +// AssertSwapSettled fails if the address still has an escrow awaiting payout. +// +// This is the only thing making a settlement transaction atomic. x/group runs a +// proposal's messages in a cached context and returns success from MsgExec even +// when the inner message failed, so two MsgExec in one transaction cannot revert +// each other. A failing top-level message can, which is what this is for. +func (k msgServer) AssertSwapSettled( + goCtx context.Context, + msg *types.MsgAssertSwapSettled, +) (*types.MsgAssertSwapSettledResponse, error) { + address, err := sdk.AccAddressFromBech32(msg.Address) + if err != nil { + return nil, err + } + + ctx := sdk.UnwrapSDKContext(goCtx) + if _, found := k.Keeper.GetPendingSwap(ctx, address); found { + return nil, types.ErrSwapNotSettled.Wrap(msg.Address) + } + + return &types.MsgAssertSwapSettledResponse{}, nil +} + +// UpdateParams sets the module params. Only the governance authority may call +// it — the exchange address decides where every future swap's collateral lands, +// so it is not left ungated the way SetPrice is. +func (k msgServer) UpdateParams(goCtx context.Context, msg *types.MsgUpdateParams) (*types.MsgUpdateParamsResponse, error) { + if msg.Authority != k.Keeper.Authority() { + return nil, types.ErrInvalidAuthority.Wrapf("expected %s got %s", k.Keeper.Authority(), msg.Authority) + } + if err := msg.Params.Validate(); err != nil { + return nil, err + } + + ctx := sdk.UnwrapSDKContext(goCtx) + k.Keeper.SetParams(ctx, msg.Params) + + ctx.EventManager().EmitEvent( + sdk.NewEvent( + types.EventTypeUpdateParams, + sdk.NewAttribute(types.AttributeExchangeAddress, msg.Params.ExchangeAddress), + ), + ) + + return &types.MsgUpdateParamsResponse{}, nil +} diff --git a/x/swap/keeper/msg_server_set_price_test.go b/x/swap/keeper/msg_server_set_price_test.go new file mode 100644 index 0000000..76cf11a --- /dev/null +++ b/x/swap/keeper/msg_server_set_price_test.go @@ -0,0 +1,121 @@ +package keeper + +import ( + "testing" + + "cosmossdk.io/math" + "github.com/stretchr/testify/require" + + "github.com/peersyst/cbdc-node/testutil/sample" + "github.com/peersyst/cbdc-node/x/swap/types" +) + +func TestMsgServerSetPrice(t *testing.T) { + k, ctx := priceOnlyKeeper(t) + srv := NewMsgServerImpl(*k) + rate := math.LegacyMustNewDecFromStr("0.04") + sender := sample.AccAddress() + + _, err := srv.SetPrice(ctx, types.NewMsgSetPrice(sender, "mhnl", "uusd", rate)) + require.NoError(t, err) + + got, err := k.GetPrice(ctx, "mhnl", "uusd") + require.NoError(t, err) + require.True(t, rate.Equal(got.Price)) +} + +func TestMsgServerSetPriceEmitsEvent(t *testing.T) { + k, ctx := priceOnlyKeeper(t) + srv := NewMsgServerImpl(*k) + sender := sample.AccAddress() + + _, err := srv.SetPrice(ctx, types.NewMsgSetPrice(sender, "mhnl", "uusd", math.LegacyMustNewDecFromStr("0.04"))) + require.NoError(t, err) + + var found bool + for _, ev := range ctx.EventManager().Events() { + if ev.Type != types.EventTypeSetPrice { + continue + } + found = true + attrs := map[string]string{} + for _, a := range ev.Attributes { + attrs[a.Key] = a.Value + } + require.Equal(t, sender, attrs[types.AttributeSender]) + require.Equal(t, "mhnl", attrs[types.AttributeFromDenom]) + require.Equal(t, "uusd", attrs[types.AttributeToDenom]) + require.Equal(t, "0.040000000000000000", attrs[types.AttributePrice]) + } + require.True(t, found, "expected a %s event", types.EventTypeSetPrice) +} + +// The message is deliberately ungated: any account can set a rate, and any +// other account can overwrite it. +func TestMsgServerSetPriceIsUngated(t *testing.T) { + k, ctx := priceOnlyKeeper(t) + srv := NewMsgServerImpl(*k) + updated := math.LegacyMustNewDecFromStr("0.05") + + _, err := srv.SetPrice(ctx, types.NewMsgSetPrice(sample.AccAddress(), "mhnl", "uusd", math.LegacyMustNewDecFromStr("0.04"))) + require.NoError(t, err) + + _, err = srv.SetPrice(ctx, types.NewMsgSetPrice(sample.AccAddress(), "mhnl", "uusd", updated)) + require.NoError(t, err) + + got, err := k.GetPrice(ctx, "mhnl", "uusd") + require.NoError(t, err) + require.True(t, updated.Equal(got.Price)) +} + +// The msg server re-validates rather than trusting ValidateBasic to have run. +func TestMsgServerSetPriceRejectsInvalid(t *testing.T) { + tt := []struct { + name string + msg *types.MsgSetPrice + expectErr error + }{ + { + name: "same denom", + msg: types.NewMsgSetPrice(sample.AccAddress(), "mhnl", "mhnl", math.LegacyMustNewDecFromStr("1")), + expectErr: types.ErrSameDenom, + }, + { + name: "zero price", + msg: types.NewMsgSetPrice(sample.AccAddress(), "mhnl", "uusd", math.LegacyZeroDec()), + expectErr: types.ErrInvalidPrice, + }, + { + name: "invalid denom", + msg: types.NewMsgSetPrice(sample.AccAddress(), "!!", "uusd", math.LegacyMustNewDecFromStr("1")), + expectErr: types.ErrInvalidDenom, + }, + } + + for _, tc := range tt { + t.Run(tc.name, func(t *testing.T) { + k, ctx := priceOnlyKeeper(t) + srv := NewMsgServerImpl(*k) + + _, err := srv.SetPrice(ctx, tc.msg) + require.ErrorIs(t, err, tc.expectErr) + require.Empty(t, k.GetAllPrices(ctx)) + }) + } +} + +func TestGenesisRoundTrip(t *testing.T) { + k, ctx := priceOnlyKeeper(t) + + genState := types.GenesisState{Prices: []types.Price{ + types.NewPrice("mhnl", "uusd", math.LegacyMustNewDecFromStr("0.04")), + types.NewPrice("mhnl", "ubrl", math.LegacyMustNewDecFromStr("0.4")), + }} + require.NoError(t, genState.Validate()) + + k.InitGenesis(ctx, genState) + exported := k.ExportGenesis(ctx) + + require.ElementsMatch(t, genState.Prices, exported.Prices) + require.NoError(t, exported.Validate()) +} diff --git a/x/swap/keeper/msg_server_swap_test.go b/x/swap/keeper/msg_server_swap_test.go new file mode 100644 index 0000000..5ca1eef --- /dev/null +++ b/x/swap/keeper/msg_server_swap_test.go @@ -0,0 +1,151 @@ +package keeper + +import ( + "testing" + + "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" + + "github.com/peersyst/cbdc-node/testutil/sample" + "github.com/peersyst/cbdc-node/x/swap/types" +) + +// eventAttrs returns the attributes of the first event of the given type. +func eventAttrs(t *testing.T, ctx sdk.Context, eventType string) map[string]string { + t.Helper() + for _, ev := range ctx.EventManager().Events() { + if ev.Type != eventType { + continue + } + attrs := map[string]string{} + for _, a := range ev.Attributes { + attrs[a.Key] = a.Value + } + return attrs + } + t.Fatalf("expected a %s event", eventType) + return nil +} + +func TestMsgServerSwapEscrows(t *testing.T) { + balance := sdk.NewCoin(testFromDenom, math.NewInt(100)) + k, ctx, _ := swapKeeperTestSetup(t, escrowHappyPath(balance)) + srv := NewMsgServerImpl(*k) + addr := sample.AccAddress() + setRate(k, ctx) + + _, err := srv.Swap(ctx, types.NewMsgSwap(addr, addr, testFromDenom, testToDenom, math.ZeroInt())) + require.NoError(t, err) + + attrs := eventAttrs(t, ctx, types.EventTypeSwap) + require.Equal(t, addr, attrs[types.AttributeAddress]) + require.Equal(t, "100"+testFromDenom, attrs[types.AttributeEscrowed]) + // Nothing is paid on this leg — the payout comes from the exchange. + require.NotContains(t, attrs, types.AttributePaid) +} + +// An account can only swap its own funds. +func TestMsgServerSwapRejectsOtherAddress(t *testing.T) { + balance := sdk.NewCoin(testFromDenom, math.NewInt(100)) + k, ctx, _ := swapKeeperTestSetup(t, escrowHappyPath(balance)) + srv := NewMsgServerImpl(*k) + setRate(k, ctx) + + msg := types.NewMsgSwap(sample.AccAddress(), sample.AccAddress(), testFromDenom, testToDenom, math.ZeroInt()) + _, err := srv.Swap(ctx, msg) + require.ErrorIs(t, err, types.ErrUnauthorized) +} + +func TestMsgServerSwapNoPrice(t *testing.T) { + balance := sdk.NewCoin(testFromDenom, math.NewInt(100)) + k, ctx, _ := swapKeeperTestSetup(t, escrowHappyPath(balance)) + srv := NewMsgServerImpl(*k) + addr := sample.AccAddress() + + _, err := srv.Swap(ctx, types.NewMsgSwap(addr, addr, testFromDenom, testToDenom, math.ZeroInt())) + require.ErrorIs(t, err, types.ErrPriceNotFound) +} + +func TestMsgServerPaySwap(t *testing.T) { + balance := sdk.NewCoin(testFromDenom, math.NewInt(100)) + k, ctx, _ := swapKeeperTestSetup(t, payoutMocks(balance, sdk.NewCoin(testToDenom, math.NewInt(1000)))) + srv := NewMsgServerImpl(*k) + addr := sample.AccAddress() + setRate(k, ctx) + + _, err := srv.Swap(ctx, types.NewMsgSwap(addr, addr, testFromDenom, testToDenom, math.ZeroInt())) + require.NoError(t, err) + + _, err = srv.PaySwap(ctx, types.NewMsgPaySwap(testExchangeAddress.String(), addr)) + require.NoError(t, err) + + attrs := eventAttrs(t, ctx, types.EventTypePaySwap) + require.Equal(t, addr, attrs[types.AttributeAddress]) + require.Equal(t, "100"+testFromDenom, attrs[types.AttributeEscrowed]) + require.Equal(t, "4"+testToDenom, attrs[types.AttributePaid]) +} + +// --------------------------------------------------------------------------- +// The settlement assertion +// --------------------------------------------------------------------------- + +// x/group reports success from MsgExec even when the inner message failed, so a +// settlement transaction cannot rely on one leg reverting the other. The assert +// is what turns an unpaid escrow into a transaction-level error. +func TestMsgServerAssertSwapSettledFailsWhilePending(t *testing.T) { + balance := sdk.NewCoin(testFromDenom, math.NewInt(100)) + k, ctx, _ := swapKeeperTestSetup(t, escrowHappyPath(balance)) + srv := NewMsgServerImpl(*k) + addr := sample.AccAddress() + setRate(k, ctx) + + _, err := srv.Swap(ctx, types.NewMsgSwap(addr, addr, testFromDenom, testToDenom, math.ZeroInt())) + require.NoError(t, err) + + _, err = srv.AssertSwapSettled(ctx, types.NewMsgAssertSwapSettled(addr, addr)) + require.ErrorIs(t, err, types.ErrSwapNotSettled) +} + +func TestMsgServerAssertSwapSettledPassesOncePaid(t *testing.T) { + balance := sdk.NewCoin(testFromDenom, math.NewInt(100)) + k, ctx, _ := swapKeeperTestSetup(t, payoutMocks(balance, sdk.NewCoin(testToDenom, math.NewInt(1000)))) + srv := NewMsgServerImpl(*k) + addr := sample.AccAddress() + setRate(k, ctx) + + _, err := srv.Swap(ctx, types.NewMsgSwap(addr, addr, testFromDenom, testToDenom, math.ZeroInt())) + require.NoError(t, err) + _, err = srv.PaySwap(ctx, types.NewMsgPaySwap(testExchangeAddress.String(), addr)) + require.NoError(t, err) + + _, err = srv.AssertSwapSettled(ctx, types.NewMsgAssertSwapSettled(addr, addr)) + require.NoError(t, err) +} + +// An address that never swapped has nothing outstanding. +func TestMsgServerAssertSwapSettledPassesWhenNoSwap(t *testing.T) { + k, ctx, _ := swapKeeperTestSetup(t, nil) + srv := NewMsgServerImpl(*k) + addr := sample.AccAddress() + + _, err := srv.AssertSwapSettled(ctx, types.NewMsgAssertSwapSettled(addr, addr)) + require.NoError(t, err) +} + +func TestMsgServerCancelSwapRefunds(t *testing.T) { + balance := sdk.NewCoin(testFromDenom, math.NewInt(100)) + k, ctx, _ := swapKeeperTestSetup(t, escrowHappyPath(balance)) + srv := NewMsgServerImpl(*k) + addr := sample.AccAddress() + setRate(k, ctx) + + _, err := srv.Swap(ctx, types.NewMsgSwap(addr, addr, testFromDenom, testToDenom, math.ZeroInt())) + require.NoError(t, err) + + _, err = srv.CancelSwap(ctx, types.NewMsgCancelSwap(addr)) + require.NoError(t, err) + + attrs := eventAttrs(t, ctx, types.EventTypeCancelSwap) + require.Equal(t, "100"+testFromDenom, attrs[types.AttributeEscrowed]) +} diff --git a/x/swap/keeper/params.go b/x/swap/keeper/params.go new file mode 100644 index 0000000..f804eef --- /dev/null +++ b/x/swap/keeper/params.go @@ -0,0 +1,45 @@ +package keeper + +import ( + "github.com/cosmos/cosmos-sdk/runtime" + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/peersyst/cbdc-node/x/swap/types" +) + +// GetParams returns the module's parameter set. An unset store returns the +// defaults, which leave swapping disabled. +func (k Keeper) GetParams(ctx sdk.Context) types.Params { + bz := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx)).Get(types.ParamsKey) + if bz == nil { + return types.DefaultParams() + } + + var params types.Params + k.cdc.MustUnmarshal(bz, ¶ms) + return params +} + +// SetParams writes the module's parameter set. +func (k Keeper) SetParams(ctx sdk.Context, params types.Params) { + runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx)).Set(types.ParamsKey, k.cdc.MustMarshal(¶ms)) +} + +// ExchangeAddress returns the address that receives the incoming asset on a +// swap. It returns types.ErrExchangeAddressUnset when no address is configured, +// which is the state a chain starts in — the address is an x/group policy that +// does not exist at genesis. +func (k Keeper) ExchangeAddress(ctx sdk.Context) (sdk.AccAddress, error) { + raw := k.GetParams(ctx).ExchangeAddress + if raw == "" { + return nil, types.ErrExchangeAddressUnset + } + + addr, err := sdk.AccAddressFromBech32(raw) + if err != nil { + // Params are validated on the way in, so this is a corrupt store rather + // than bad input. + return nil, types.ErrInvalidExchangeAddress.Wrapf("%s: %s", raw, err) + } + return addr, nil +} diff --git a/x/swap/keeper/price.go b/x/swap/keeper/price.go new file mode 100644 index 0000000..8a9d21f --- /dev/null +++ b/x/swap/keeper/price.go @@ -0,0 +1,83 @@ +package keeper + +import ( + "cosmossdk.io/math" + "cosmossdk.io/store/prefix" + storetypes "cosmossdk.io/store/types" + "github.com/cosmos/cosmos-sdk/runtime" + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/peersyst/cbdc-node/x/swap/types" +) + +// priceStore returns the store holding every Price, keyed by types.PairKey. +func (k Keeper) priceStore(ctx sdk.Context) prefix.Store { + return prefix.NewStore(runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx)), types.PriceKeyPrefix) +} + +// SetPrice writes the rate for a denom pair, replacing whatever was stored for +// that pair in EITHER direction: types.PairKey is shared by both orderings, so +// two contradictory rates for one pair cannot coexist. +func (k Keeper) SetPrice(ctx sdk.Context, price types.Price) { + k.priceStore(ctx).Set(types.PairKey(price.FromDenom, price.ToDenom), k.cdc.MustMarshal(&price)) +} + +// GetPrice returns the rate STORED for an ordered denom pair, with no +// derivation. It returns types.ErrPriceNotFound when that exact direction has no +// rate, including when only the reverse pair does. +// +// Callers settling an exchange want GetEffectivePrice; this is the raw read. +func (k Keeper) GetPrice(ctx sdk.Context, fromDenom, toDenom string) (types.Price, error) { + bz := k.priceStore(ctx).Get(types.PairKey(fromDenom, toDenom)) + if bz == nil { + return types.Price{}, types.ErrPriceNotFound.Wrapf("%s -> %s", fromDenom, toDenom) + } + + var price types.Price + k.cdc.MustUnmarshal(bz, &price) + // The entry is shared with the opposite direction; this read reports only the + // direction actually quoted. + if price.FromDenom != fromDenom || price.ToDenom != toDenom { + return types.Price{}, types.ErrPriceNotFound.Wrapf("%s -> %s", fromDenom, toDenom) + } + return price, nil +} + +// GetEffectivePrice returns the rate an exchange settles at, deriving the +// reverse direction when only one side of a pair is quoted. +// +// A pair is quoted in one direction and the opposite is 1/rate, so the halves +// cannot drift apart. The trade-off is that a derived inverse carries no spread: +// a stale rate is arbitrageable against the reserve until corrected. The division +// truncates, so a derived rate never pays out more than the exact inverse. +func (k Keeper) GetEffectivePrice(ctx sdk.Context, fromDenom, toDenom string) (types.Price, error) { + bz := k.priceStore(ctx).Get(types.PairKey(fromDenom, toDenom)) + if bz == nil { + return types.Price{}, types.ErrPriceNotFound.Wrapf("%s -> %s", fromDenom, toDenom) + } + + var quoted types.Price + k.cdc.MustUnmarshal(bz, "ed) + if quoted.FromDenom == fromDenom && quoted.ToDenom == toDenom { + return quoted, nil + } + if !quoted.Price.IsPositive() { + return types.Price{}, types.ErrInvalidPrice.Wrapf("%s -> %s", quoted.FromDenom, quoted.ToDenom) + } + + return types.NewPrice(fromDenom, toDenom, math.LegacyOneDec().QuoTruncate(quoted.Price)), nil +} + +// GetAllPrices returns every stored rate. +func (k Keeper) GetAllPrices(ctx sdk.Context) []types.Price { + iterator := storetypes.KVStorePrefixIterator(k.priceStore(ctx), nil) + defer iterator.Close() + + prices := []types.Price{} + for ; iterator.Valid(); iterator.Next() { + var price types.Price + k.cdc.MustUnmarshal(iterator.Value(), &price) + prices = append(prices, price) + } + return prices +} diff --git a/x/swap/keeper/price_test.go b/x/swap/keeper/price_test.go new file mode 100644 index 0000000..aa0665a --- /dev/null +++ b/x/swap/keeper/price_test.go @@ -0,0 +1,175 @@ +package keeper + +import ( + "testing" + + "cosmossdk.io/math" + "github.com/stretchr/testify/require" + + "github.com/peersyst/cbdc-node/x/swap/types" +) + +func TestSetGetPrice(t *testing.T) { + k, ctx := priceOnlyKeeper(t) + rate := math.LegacyMustNewDecFromStr("0.04") + + k.SetPrice(ctx, types.NewPrice("mhnl", "uusd", rate)) + + got, err := k.GetPrice(ctx, "mhnl", "uusd") + require.NoError(t, err) + require.Equal(t, "mhnl", got.FromDenom) + require.Equal(t, "uusd", got.ToDenom) + require.True(t, rate.Equal(got.Price)) +} + +func TestGetPriceNotFound(t *testing.T) { + k, ctx := priceOnlyKeeper(t) + + _, err := k.GetPrice(ctx, "mhnl", "uusd") + require.ErrorIs(t, err, types.ErrPriceNotFound) +} + +// GetPrice is the raw read: it reports only the direction that was quoted, even +// though the pair's single entry also answers for the opposite one. Inverting is +// GetEffectivePrice's job. +func TestGetPriceReverseNotDerived(t *testing.T) { + k, ctx := priceOnlyKeeper(t) + + k.SetPrice(ctx, types.NewPrice("mhnl", "uusd", math.LegacyMustNewDecFromStr("0.04"))) + + _, err := k.GetPrice(ctx, "uusd", "mhnl") + require.ErrorIs(t, err, types.ErrPriceNotFound) +} + +func TestSetPriceOverwrites(t *testing.T) { + k, ctx := priceOnlyKeeper(t) + updated := math.LegacyMustNewDecFromStr("0.05") + + k.SetPrice(ctx, types.NewPrice("mhnl", "uusd", math.LegacyMustNewDecFromStr("0.04"))) + k.SetPrice(ctx, types.NewPrice("mhnl", "uusd", updated)) + + got, err := k.GetPrice(ctx, "mhnl", "uusd") + require.NoError(t, err) + require.True(t, updated.Equal(got.Price)) + require.Len(t, k.GetAllPrices(ctx), 1) +} + +// Pairs whose denoms concatenate to the same string must not share a key. +// Without the length prefix in types.PriceKey these two would collide. +func TestPriceKeyNoCollision(t *testing.T) { + k, ctx := priceOnlyKeeper(t) + first := math.LegacyMustNewDecFromStr("1") + second := math.LegacyMustNewDecFromStr("2") + + k.SetPrice(ctx, types.NewPrice("abc", "defg", first)) + k.SetPrice(ctx, types.NewPrice("abcd", "efg", second)) + + got, err := k.GetPrice(ctx, "abc", "defg") + require.NoError(t, err) + require.True(t, first.Equal(got.Price)) + + got, err = k.GetPrice(ctx, "abcd", "efg") + require.NoError(t, err) + require.True(t, second.Equal(got.Price)) + + require.Len(t, k.GetAllPrices(ctx), 2) +} + +func TestGetAllPrices(t *testing.T) { + k, ctx := priceOnlyKeeper(t) + + require.Empty(t, k.GetAllPrices(ctx)) + + k.SetPrice(ctx, types.NewPrice("mhnl", "uusd", math.LegacyMustNewDecFromStr("0.04"))) + k.SetPrice(ctx, types.NewPrice("mhnl", "ubrl", math.LegacyMustNewDecFromStr("0.4"))) + + require.Len(t, k.GetAllPrices(ctx), 2) +} + +// Both orderings resolve to one store key, so re-quoting a pair the other way +// round replaces it. Without this, "mhnl -> uusd" at 0.04 could sit next to +// "uusd -> mhnl" at 50 and a round trip would gain value out of nothing. +func TestSetPriceReplacesReverseDirection(t *testing.T) { + k, ctx := priceOnlyKeeper(t) + reverse := math.LegacyMustNewDecFromStr("25") + + k.SetPrice(ctx, types.NewPrice("mhnl", "uusd", math.LegacyMustNewDecFromStr("0.04"))) + k.SetPrice(ctx, types.NewPrice("uusd", "mhnl", reverse)) + + require.Len(t, k.GetAllPrices(ctx), 1) + + _, err := k.GetPrice(ctx, "mhnl", "uusd") + require.ErrorIs(t, err, types.ErrPriceNotFound) + + got, err := k.GetPrice(ctx, "uusd", "mhnl") + require.NoError(t, err) + require.True(t, reverse.Equal(got.Price)) +} + +// The surviving entry is the only source for both legs, so the derived direction +// is exactly its inverse and a round trip cannot gain value. +func TestEffectivePriceRoundTripIsLossless(t *testing.T) { + k, ctx := priceOnlyKeeper(t) + + k.SetPrice(ctx, types.NewPrice("mhnl", "uusd", math.LegacyMustNewDecFromStr("0.04"))) + k.SetPrice(ctx, types.NewPrice("uusd", "mhnl", math.LegacyMustNewDecFromStr("50"))) + + forward, err := k.GetEffectivePrice(ctx, "mhnl", "uusd") + require.NoError(t, err) + back, err := k.GetEffectivePrice(ctx, "uusd", "mhnl") + require.NoError(t, err) + + // Truncation may lose a hair, but the product can never exceed one. + require.True(t, forward.Price.Mul(back.Price).LTE(math.LegacyOneDec()), + "round trip gains value: %s * %s", forward.Price, back.Price) +} + +// Pairs that merely share a denom are independent and both survive. +func TestSetPriceKeepsUnrelatedPairs(t *testing.T) { + k, ctx := priceOnlyKeeper(t) + + k.SetPrice(ctx, types.NewPrice("mhnl", "uusd", math.LegacyMustNewDecFromStr("0.04"))) + k.SetPrice(ctx, types.NewPrice("mhnl", "ubrl", math.LegacyMustNewDecFromStr("0.4"))) + + require.Len(t, k.GetAllPrices(ctx), 2) +} + +// The invariant is structural, not a rule SetPrice remembers to apply: the two +// orderings of a pair name the same key, so however many times a pair is quoted +// in either direction, the store holds exactly one entry for it. +func TestPairKeyIsOrderInsensitive(t *testing.T) { + require.Equal(t, + types.PairKey("mhnl", "uusd"), + types.PairKey("uusd", "mhnl"), + ) + require.NotEqual(t, + types.PairKey("mhnl", "uusd"), + types.PairKey("mhnl", "ubrl"), + ) +} + +func TestSetPriceNeverAccumulatesEntriesForOnePair(t *testing.T) { + k, ctx := priceOnlyKeeper(t) + + for i := range 5 { + rate := math.LegacyNewDec(int64(i + 1)) + k.SetPrice(ctx, types.NewPrice("mhnl", "uusd", rate)) + k.SetPrice(ctx, types.NewPrice("uusd", "mhnl", rate)) + require.Len(t, k.GetAllPrices(ctx), 1) + } +} + +// The raw read reports only the direction that was quoted, even though the entry +// answering it is shared with the opposite direction. +func TestGetPriceRejectsUnquotedDirection(t *testing.T) { + k, ctx := priceOnlyKeeper(t) + + k.SetPrice(ctx, types.NewPrice("uusd", "mhnl", math.LegacyMustNewDecFromStr("25"))) + + _, err := k.GetPrice(ctx, "mhnl", "uusd") + require.ErrorIs(t, err, types.ErrPriceNotFound) + + got, err := k.GetPrice(ctx, "uusd", "mhnl") + require.NoError(t, err) + require.True(t, math.LegacyMustNewDecFromStr("25").Equal(got.Price)) +} diff --git a/x/swap/keeper/query.go b/x/swap/keeper/query.go new file mode 100644 index 0000000..dcdea18 --- /dev/null +++ b/x/swap/keeper/query.go @@ -0,0 +1,19 @@ +package keeper + +import ( + "github.com/peersyst/cbdc-node/x/swap/types" +) + +var _ types.QueryServer = Querier{} + +// Querier is the gRPC query server for the swap module. It holds the keeper as +// an unexported field (rather than embedding it) so the read API stays +// decoupled from the keeper's state-mutating methods. +type Querier struct { + k Keeper +} + +// NewQuerier returns a Querier for the provided Keeper. +func NewQuerier(k Keeper) Querier { + return Querier{k: k} +} diff --git a/x/swap/keeper/query_params.go b/x/swap/keeper/query_params.go new file mode 100644 index 0000000..b44c5d3 --- /dev/null +++ b/x/swap/keeper/query_params.go @@ -0,0 +1,20 @@ +package keeper + +import ( + "context" + + sdk "github.com/cosmos/cosmos-sdk/types" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + "github.com/peersyst/cbdc-node/x/swap/types" +) + +func (q Querier) Params(goCtx context.Context, req *types.QueryParamsRequest) (*types.QueryParamsResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "invalid request") + } + ctx := sdk.UnwrapSDKContext(goCtx) + + return &types.QueryParamsResponse{Params: q.k.GetParams(ctx)}, nil +} diff --git a/x/swap/keeper/query_pending_swap.go b/x/swap/keeper/query_pending_swap.go new file mode 100644 index 0000000..5688bb8 --- /dev/null +++ b/x/swap/keeper/query_pending_swap.go @@ -0,0 +1,35 @@ +package keeper + +import ( + "context" + + sdk "github.com/cosmos/cosmos-sdk/types" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + "github.com/peersyst/cbdc-node/x/swap/types" +) + +// PendingSwap reports whether an address has an escrowed swap awaiting payout. +// +// Absence is the normal answer, not an error: a pending entry exists only +// between an exchange's two legs, usually within a single transaction. Finding +// one means a settlement was interrupted, which is exactly what a caller +// resuming settlement needs to know. +func (q Querier) PendingSwap( + goCtx context.Context, + req *types.QueryPendingSwapRequest, +) (*types.QueryPendingSwapResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "invalid request") + } + + address, err := sdk.AccAddressFromBech32(req.Address) + if err != nil { + return nil, status.Error(codes.InvalidArgument, err.Error()) + } + + ctx := sdk.UnwrapSDKContext(goCtx) + pending, found := q.k.GetPendingSwap(ctx, address) + return &types.QueryPendingSwapResponse{Found: found, PendingSwap: pending}, nil +} diff --git a/x/swap/keeper/query_price.go b/x/swap/keeper/query_price.go new file mode 100644 index 0000000..13f2b47 --- /dev/null +++ b/x/swap/keeper/query_price.go @@ -0,0 +1,48 @@ +package keeper + +import ( + "context" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/query" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + "github.com/peersyst/cbdc-node/x/swap/types" +) + +func (q Querier) Price(goCtx context.Context, req *types.QueryPriceRequest) (*types.QueryPriceResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "invalid request") + } + ctx := sdk.UnwrapSDKContext(goCtx) + + price, err := q.k.GetPrice(ctx, req.FromDenom, req.ToDenom) + if err != nil { + return nil, status.Error(codes.NotFound, err.Error()) + } + + return &types.QueryPriceResponse{Price: price}, nil +} + +func (q Querier) Prices(goCtx context.Context, req *types.QueryPricesRequest) (*types.QueryPricesResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "invalid request") + } + ctx := sdk.UnwrapSDKContext(goCtx) + + prices := []types.Price{} + pageRes, err := query.Paginate(q.k.priceStore(ctx), req.Pagination, func(_, value []byte) error { + var price types.Price + if err := q.k.cdc.Unmarshal(value, &price); err != nil { + return err + } + prices = append(prices, price) + return nil + }) + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + + return &types.QueryPricesResponse{Prices: prices, Pagination: pageRes}, nil +} diff --git a/x/swap/keeper/query_price_test.go b/x/swap/keeper/query_price_test.go new file mode 100644 index 0000000..fb07ccd --- /dev/null +++ b/x/swap/keeper/query_price_test.go @@ -0,0 +1,45 @@ +package keeper + +import ( + "testing" + + "cosmossdk.io/math" + "github.com/cosmos/cosmos-sdk/types/query" + "github.com/stretchr/testify/require" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + "github.com/peersyst/cbdc-node/x/swap/types" +) + +func TestQueryPrice(t *testing.T) { + k, ctx := priceOnlyKeeper(t) + q := NewQuerier(*k) + rate := math.LegacyMustNewDecFromStr("0.04") + + k.SetPrice(ctx, types.NewPrice("mhnl", "uusd", rate)) + + res, err := q.Price(ctx, &types.QueryPriceRequest{FromDenom: "mhnl", ToDenom: "uusd"}) + require.NoError(t, err) + require.True(t, rate.Equal(res.Price.Price)) + + _, err = q.Price(ctx, &types.QueryPriceRequest{FromDenom: "uusd", ToDenom: "mhnl"}) + require.Equal(t, codes.NotFound, status.Code(err)) +} + +func TestQueryPrices(t *testing.T) { + k, ctx := priceOnlyKeeper(t) + q := NewQuerier(*k) + + k.SetPrice(ctx, types.NewPrice("mhnl", "uusd", math.LegacyMustNewDecFromStr("0.04"))) + k.SetPrice(ctx, types.NewPrice("mhnl", "ubrl", math.LegacyMustNewDecFromStr("0.4"))) + + res, err := q.Prices(ctx, &types.QueryPricesRequest{}) + require.NoError(t, err) + require.Len(t, res.Prices, 2) + + paged, err := q.Prices(ctx, &types.QueryPricesRequest{Pagination: &query.PageRequest{Limit: 1, CountTotal: true}}) + require.NoError(t, err) + require.Len(t, paged.Prices, 1) + require.Equal(t, uint64(2), paged.Pagination.Total) +} diff --git a/x/swap/keeper/swap.go b/x/swap/keeper/swap.go new file mode 100644 index 0000000..9be15d7 --- /dev/null +++ b/x/swap/keeper/swap.go @@ -0,0 +1,210 @@ +package keeper + +import ( + "cosmossdk.io/math" + "cosmossdk.io/store/prefix" + storetypes "cosmossdk.io/store/types" + "github.com/cosmos/cosmos-sdk/runtime" + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/peersyst/cbdc-node/x/swap/types" +) + +// An exchange has two legs owned by two different accounts: the initiator's +// balance and the exchange address's. Neither can sign for the other, and +// x/group refuses a message whose signers are not all the executing policy, so +// one message cannot carry both. The legs are therefore separate messages joined +// by a PendingSwap entry, and MsgAssertSwapSettled is what stops a transaction +// committing with only one of them applied. +// +// Nothing is minted. Both sides are balances that already exist, which is why +// the exchange address running dry is a real failure rather than an impossibility. + +// pendingStore holds escrowed-but-unpaid swaps, keyed by owning address. +func (k Keeper) pendingStore(ctx sdk.Context) prefix.Store { + return prefix.NewStore(runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx)), types.PendingSwapKeyPrefix) +} + +// GetPendingSwap returns the escrowed swap awaiting payout for address. +func (k Keeper) GetPendingSwap(ctx sdk.Context, address sdk.AccAddress) (types.PendingSwap, bool) { + bz := k.pendingStore(ctx).Get(address.Bytes()) + if bz == nil { + return types.PendingSwap{}, false + } + + var pending types.PendingSwap + k.cdc.MustUnmarshal(bz, &pending) + return pending, true +} + +func (k Keeper) setPendingSwap(ctx sdk.Context, pending types.PendingSwap) { + addr := sdk.MustAccAddressFromBech32(pending.Address) + k.pendingStore(ctx).Set(addr.Bytes(), k.cdc.MustMarshal(&pending)) +} + +func (k Keeper) deletePendingSwap(ctx sdk.Context, address sdk.AccAddress) { + k.pendingStore(ctx).Delete(address.Bytes()) +} + +// GetAllPendingSwaps returns every escrowed swap awaiting payout. +func (k Keeper) GetAllPendingSwaps(ctx sdk.Context) []types.PendingSwap { + iterator := storetypes.KVStorePrefixIterator(k.pendingStore(ctx), nil) + defer iterator.Close() + + pending := []types.PendingSwap{} + for ; iterator.Valid(); iterator.Next() { + var p types.PendingSwap + k.cdc.MustUnmarshal(iterator.Value(), &p) + pending = append(pending, p) + } + return pending +} + +// EscrowSwap moves amount of fromDenom out of address and records the exchange +// as pending. amount zero means the whole balance. +// +// It deliberately does not look at whether the exchange address can afford the +// payout: that is checked where the payout happens, against the balance at that +// moment, so the two cannot disagree. +func (k Keeper) EscrowSwap( + ctx sdk.Context, + address sdk.AccAddress, + fromDenom, toDenom string, + amount math.Int, +) (sdk.Coin, error) { + if fromDenom == toDenom { + return sdk.Coin{}, types.ErrSameDenom.Wrap(fromDenom) + } + + exchange, err := k.ExchangeAddress(ctx) + if err != nil { + return sdk.Coin{}, err + } + // The exchange is the counterparty to every swap; swapping with itself would + // move nothing while still writing pending state. + if exchange.Equals(address) { + return sdk.Coin{}, types.ErrSwapWithExchange.Wrap(address.String()) + } + + // A rate must be quotable for this direction — stored outright, or + // derived from the reverse pair. + if _, err := k.GetEffectivePrice(ctx, fromDenom, toDenom); err != nil { + return sdk.Coin{}, err + } + + if _, found := k.GetPendingSwap(ctx, address); found { + return sdk.Coin{}, types.ErrPendingSwapExists.Wrap(address.String()) + } + + if k.bk.BlockedAddr(address) { + return sdk.Coin{}, types.ErrBlockedAddr.Wrap(address.String()) + } + + balance := k.bk.GetBalance(ctx, address, fromDenom) + if !balance.IsPositive() { + return sdk.Coin{}, types.ErrZeroBalance.Wrapf("%s has no %s", address, fromDenom) + } + + if amount.IsNil() || amount.IsZero() { + amount = balance.Amount + } + if amount.IsNegative() { + return sdk.Coin{}, types.ErrInsufficientAmount.Wrapf("negative amount %s", amount) + } + if amount.GT(balance.Amount) { + return sdk.Coin{}, types.ErrInsufficientAmount.Wrapf("%s%s requested, %s held", amount, fromDenom, balance.Amount) + } + + escrow := sdk.NewCoin(fromDenom, amount) + if !k.bk.IsSendEnabledCoin(ctx, escrow) { + return sdk.Coin{}, types.ErrSendDisabled.Wrap(escrow.Denom) + } + + // Into the module account rather than straight to the exchange, so a swap + // that is never paid out can be refunded without spending exchange reserves. + if err := k.bk.SendCoinsFromAccountToModule(ctx, address, types.ModuleName, sdk.NewCoins(escrow)); err != nil { + return sdk.Coin{}, err + } + + k.setPendingSwap(ctx, types.PendingSwap{ + Address: address.String(), + FromDenom: fromDenom, + ToDenom: toDenom, + Escrowed: escrow, + }) + + return escrow, nil +} + +// PaySwap completes a pending swap: it pays the owed denom from the exchange +// address and forwards the escrow to it. +// +// The rate is read here, not at escrow time, so the amount paid is the rate in +// force when the exchange actually settles. +func (k Keeper) PaySwap(ctx sdk.Context, payer, address sdk.AccAddress) (escrow, paid sdk.Coin, err error) { + exchange, err := k.ExchangeAddress(ctx) + if err != nil { + return escrow, paid, err + } + if !exchange.Equals(payer) { + return escrow, paid, types.ErrUnauthorizedPayer.Wrapf("expected %s got %s", exchange, payer) + } + + pending, found := k.GetPendingSwap(ctx, address) + if !found { + return escrow, paid, types.ErrNoPendingSwap.Wrap(address.String()) + } + escrow = pending.Escrowed + + price, err := k.GetEffectivePrice(ctx, pending.FromDenom, pending.ToDenom) + if err != nil { + return escrow, paid, err + } + + // Truncated, not rounded: the exchange never pays out more than the escrowed + // asset is worth. The remainder is at most one base unit of to_denom. + amount := price.Price.MulInt(escrow.Amount).TruncateInt() + if !amount.IsPositive() { + return escrow, paid, types.ErrDustAmount.Wrapf("%s at rate %s", escrow, price.Price) + } + paid = sdk.NewCoin(pending.ToDenom, amount) + + if !k.bk.IsSendEnabledCoin(ctx, paid) { + return escrow, paid, types.ErrSendDisabled.Wrap(paid.Denom) + } + + // The exchange holds real inventory, so this is a genuine failure mode and + // the whole point of the liquidity gate. + held := k.bk.GetBalance(ctx, exchange, paid.Denom) + if held.Amount.LT(paid.Amount) { + return escrow, paid, types.ErrExchangeLiquidity.Wrapf("needs %s, holds %s", paid, held) + } + + // Pay first, then take the escrow: if the payout fails there is nothing to + // unwind, and the pending entry survives for a retry or a cancel. + if err := k.bk.SendCoins(ctx, exchange, address, sdk.NewCoins(paid)); err != nil { + return escrow, paid, err + } + if err := k.bk.SendCoinsFromModuleToAccount(ctx, types.ModuleName, exchange, sdk.NewCoins(escrow)); err != nil { + return escrow, paid, err + } + + k.deletePendingSwap(ctx, address) + return escrow, paid, nil +} + +// CancelSwap refunds an escrow that was never paid out. The refund comes from +// the module account, so a cancel can never reach exchange reserves. +func (k Keeper) CancelSwap(ctx sdk.Context, address sdk.AccAddress) (sdk.Coin, error) { + pending, found := k.GetPendingSwap(ctx, address) + if !found { + return sdk.Coin{}, types.ErrNoPendingSwap.Wrap(address.String()) + } + + if err := k.bk.SendCoinsFromModuleToAccount(ctx, types.ModuleName, address, sdk.NewCoins(pending.Escrowed)); err != nil { + return sdk.Coin{}, err + } + + k.deletePendingSwap(ctx, address) + return pending.Escrowed, nil +} diff --git a/x/swap/keeper/swap_test.go b/x/swap/keeper/swap_test.go new file mode 100644 index 0000000..59f3f7a --- /dev/null +++ b/x/swap/keeper/swap_test.go @@ -0,0 +1,350 @@ +package keeper + +import ( + "testing" + + "cosmossdk.io/math" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/require" + + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/peersyst/cbdc-node/testutil/sample" + "github.com/peersyst/cbdc-node/x/swap/types" +) + +func testAddr(t *testing.T) sdk.AccAddress { + t.Helper() + addr, err := sdk.AccAddressFromBech32(sample.AccAddress()) + require.NoError(t, err) + return addr +} + +var testRate = math.LegacyMustNewDecFromStr("0.04") + +// setRate stores the rate for the pair the tests exchange over. +func setRate(k *Keeper, ctx sdk.Context) { + k.SetPrice(ctx, types.NewPrice(testFromDenom, testToDenom, testRate)) +} + +// --------------------------------------------------------------------------- +// Escrow leg +// --------------------------------------------------------------------------- + +func TestEscrowSwapWholeBalance(t *testing.T) { + balance := sdk.NewCoin(testFromDenom, math.NewInt(100)) + k, ctx, m := swapKeeperTestSetup(t, escrowGatingMocks(balance)) + addr := testAddr(t) + setRate(k, ctx) + + // Escrow goes to the module account, not to the exchange: an unpaid swap has + // to be refundable without touching exchange reserves. + m.bank.EXPECT(). + SendCoinsFromAccountToModule(gomock.Any(), addr, types.ModuleName, sdk.NewCoins(balance)). + Return(nil) + + escrowed, err := k.EscrowSwap(ctx, addr, testFromDenom, testToDenom, math.ZeroInt()) + require.NoError(t, err) + require.Equal(t, balance, escrowed) + + pending, found := k.GetPendingSwap(ctx, addr) + require.True(t, found) + require.Equal(t, balance, pending.Escrowed) + require.Equal(t, testToDenom, pending.ToDenom) +} + +func TestEscrowSwapPartialAmount(t *testing.T) { + balance := sdk.NewCoin(testFromDenom, math.NewInt(100)) + k, ctx, m := swapKeeperTestSetup(t, escrowGatingMocks(balance)) + addr := testAddr(t) + setRate(k, ctx) + + part := sdk.NewCoin(testFromDenom, math.NewInt(30)) + m.bank.EXPECT(). + SendCoinsFromAccountToModule(gomock.Any(), addr, types.ModuleName, sdk.NewCoins(part)). + Return(nil) + + escrowed, err := k.EscrowSwap(ctx, addr, testFromDenom, testToDenom, math.NewInt(30)) + require.NoError(t, err) + require.Equal(t, part, escrowed) +} + +func TestEscrowSwapAmountExceedsBalance(t *testing.T) { + k, ctx, _ := swapKeeperTestSetup(t, escrowGatingMocks(sdk.NewCoin(testFromDenom, math.NewInt(100)))) + setRate(k, ctx) + + _, err := k.EscrowSwap(ctx, testAddr(t), testFromDenom, testToDenom, math.NewInt(101)) + require.ErrorIs(t, err, types.ErrInsufficientAmount) +} + +func TestEscrowSwapNoPrice(t *testing.T) { + k, ctx, _ := swapKeeperTestSetup(t, escrowHappyPath(sdk.NewCoin(testFromDenom, math.NewInt(100)))) + + _, err := k.EscrowSwap(ctx, testAddr(t), testFromDenom, testToDenom, math.ZeroInt()) + require.ErrorIs(t, err, types.ErrPriceNotFound) +} + +// Only one direction of a pair is stored; the other is derived from it, so a +// reverse-only rate still makes the swap quotable. +func TestEscrowSwapDerivesReversePrice(t *testing.T) { + k, ctx, _ := swapKeeperTestSetup(t, escrowHappyPath(sdk.NewCoin(testFromDenom, math.NewInt(100)))) + k.SetPrice(ctx, types.NewPrice(testToDenom, testFromDenom, math.LegacyMustNewDecFromStr("25"))) + + _, err := k.EscrowSwap(ctx, testAddr(t), testFromDenom, testToDenom, math.ZeroInt()) + require.NoError(t, err) +} + +// With neither direction stored there is nothing to derive from. +func TestGetEffectivePriceWithoutEitherDirection(t *testing.T) { + k, ctx := priceOnlyKeeper(t) + + _, err := k.GetEffectivePrice(ctx, testFromDenom, testToDenom) + require.ErrorIs(t, err, types.ErrPriceNotFound) +} + +// A stored direction is used as-is; the reverse is 1/rate. +func TestGetEffectivePriceInverts(t *testing.T) { + k, ctx := priceOnlyKeeper(t) + k.SetPrice(ctx, types.NewPrice(testFromDenom, testToDenom, math.LegacyMustNewDecFromStr("2.5"))) + + direct, err := k.GetEffectivePrice(ctx, testFromDenom, testToDenom) + require.NoError(t, err) + require.True(t, math.LegacyMustNewDecFromStr("2.5").Equal(direct.Price)) + + inverse, err := k.GetEffectivePrice(ctx, testToDenom, testFromDenom) + require.NoError(t, err) + require.True(t, math.LegacyMustNewDecFromStr("0.4").Equal(inverse.Price), "got %s", inverse.Price) +} + +// An explicitly stored reverse wins over the derived one, so a pair can still +// be quoted asymmetrically by writing both directions. +func TestGetEffectivePricePrefersStoredDirection(t *testing.T) { + k, ctx := priceOnlyKeeper(t) + k.SetPrice(ctx, types.NewPrice(testFromDenom, testToDenom, math.LegacyMustNewDecFromStr("2.5"))) + k.SetPrice(ctx, types.NewPrice(testToDenom, testFromDenom, math.LegacyMustNewDecFromStr("0.3"))) + + inverse, err := k.GetEffectivePrice(ctx, testToDenom, testFromDenom) + require.NoError(t, err) + require.True(t, math.LegacyMustNewDecFromStr("0.3").Equal(inverse.Price), "stored rate must win over 1/rate") +} + +// Any pair with a stored rate is exchangeable now that nothing is minted; the +// target no longer has to be the chain's own denom. +func TestEscrowSwapAllowsForeignToDenom(t *testing.T) { + balance := sdk.NewCoin(testFromDenom, math.NewInt(100)) + k, ctx, _ := swapKeeperTestSetup(t, escrowHappyPath(balance)) + k.SetPrice(ctx, types.NewPrice(testFromDenom, "ueur", math.LegacyMustNewDecFromStr("0.9"))) + + escrowed, err := k.EscrowSwap(ctx, testAddr(t), testFromDenom, "ueur", math.ZeroInt()) + require.NoError(t, err) + require.Equal(t, balance, escrowed) +} + +func TestEscrowSwapZeroBalance(t *testing.T) { + k, ctx, _ := swapKeeperTestSetup(t, escrowGatingMocks(sdk.NewCoin(testFromDenom, math.ZeroInt()))) + setRate(k, ctx) + + _, err := k.EscrowSwap(ctx, testAddr(t), testFromDenom, testToDenom, math.ZeroInt()) + require.ErrorIs(t, err, types.ErrZeroBalance) +} + +func TestEscrowSwapBlockedAddr(t *testing.T) { + balance := sdk.NewCoin(testFromDenom, math.NewInt(100)) + k, ctx, _ := swapKeeperTestSetup(t, func(ctx sdk.Context, m swapMocks) { + m.bank.EXPECT().BlockedAddr(gomock.Any()).Return(true).AnyTimes() + m.bank.EXPECT().GetBalance(gomock.Any(), gomock.Any(), gomock.Any()).Return(balance).AnyTimes() + }) + setRate(k, ctx) + + _, err := k.EscrowSwap(ctx, testAddr(t), testFromDenom, testToDenom, math.ZeroInt()) + require.ErrorIs(t, err, types.ErrBlockedAddr) +} + +// Until governance names an exchange address there is no counterparty, so an +// exchange cannot be started at all. +func TestEscrowSwapRejectsUnsetExchangeAddress(t *testing.T) { + k, ctx, _ := swapKeeperTestSetup(t, escrowGatingMocks(sdk.NewCoin(testFromDenom, math.NewInt(100)))) + k.SetParams(ctx, types.DefaultParams()) + setRate(k, ctx) + + _, err := k.EscrowSwap(ctx, testAddr(t), testFromDenom, testToDenom, math.ZeroInt()) + require.ErrorIs(t, err, types.ErrExchangeAddressUnset) +} + +// The exchange is the counterparty to every swap; swapping with itself would +// move nothing while still writing pending state. +func TestEscrowSwapRejectsExchangeAsInitiator(t *testing.T) { + k, ctx, _ := swapKeeperTestSetup(t, escrowGatingMocks(sdk.NewCoin(testFromDenom, math.NewInt(100)))) + setRate(k, ctx) + + _, err := k.EscrowSwap(ctx, testExchangeAddress, testFromDenom, testToDenom, math.ZeroInt()) + require.ErrorIs(t, err, types.ErrSwapWithExchange) +} + +func TestEscrowSwapRejectsSecondPending(t *testing.T) { + balance := sdk.NewCoin(testFromDenom, math.NewInt(100)) + k, ctx, _ := swapKeeperTestSetup(t, escrowHappyPath(balance)) + addr := testAddr(t) + setRate(k, ctx) + + _, err := k.EscrowSwap(ctx, addr, testFromDenom, testToDenom, math.NewInt(10)) + require.NoError(t, err) + + _, err = k.EscrowSwap(ctx, addr, testFromDenom, testToDenom, math.NewInt(10)) + require.ErrorIs(t, err, types.ErrPendingSwapExists) +} + +// --------------------------------------------------------------------------- +// Payout leg +// --------------------------------------------------------------------------- + +func TestPaySwapSettles(t *testing.T) { + balance := sdk.NewCoin(testFromDenom, math.NewInt(100)) + held := sdk.NewCoin(testToDenom, math.NewInt(1000)) + k, ctx, m := swapKeeperTestSetup(t, payoutGatingMocks(balance, held)) + addr := testAddr(t) + setRate(k, ctx) + + _, err := k.EscrowSwap(ctx, addr, testFromDenom, testToDenom, math.ZeroInt()) + require.NoError(t, err) + + // 100 * 0.04 = 4 paid out of the exchange's own balance; the escrow then + // moves from the module account to the exchange. + paidCoin := sdk.NewCoin(testToDenom, math.NewInt(4)) + m.bank.EXPECT().SendCoins(gomock.Any(), testExchangeAddress, addr, sdk.NewCoins(paidCoin)).Return(nil) + m.bank.EXPECT(). + SendCoinsFromModuleToAccount(gomock.Any(), types.ModuleName, testExchangeAddress, sdk.NewCoins(balance)). + Return(nil) + + escrowed, paid, err := k.PaySwap(ctx, testExchangeAddress, addr) + require.NoError(t, err) + require.Equal(t, balance, escrowed) + require.Equal(t, paidCoin, paid) + + _, found := k.GetPendingSwap(ctx, addr) + require.False(t, found, "pending entry must be consumed") +} + +func TestPaySwapTruncates(t *testing.T) { + balance := sdk.NewCoin(testFromDenom, math.NewInt(101)) + k, ctx, _ := swapKeeperTestSetup(t, payoutMocks(balance, sdk.NewCoin(testToDenom, math.NewInt(1000)))) + addr := testAddr(t) + setRate(k, ctx) + + _, err := k.EscrowSwap(ctx, addr, testFromDenom, testToDenom, math.ZeroInt()) + require.NoError(t, err) + + _, paid, err := k.PaySwap(ctx, testExchangeAddress, addr) + require.NoError(t, err) + // 101 * 0.04 = 4.04, truncated to 4 — the exchange never overpays. + require.Equal(t, math.NewInt(4), paid.Amount) +} + +// The rate is read when the exchange settles, not when the swap was escrowed. +func TestPaySwapUsesRateAtSettlement(t *testing.T) { + balance := sdk.NewCoin(testFromDenom, math.NewInt(100)) + k, ctx, _ := swapKeeperTestSetup(t, payoutMocks(balance, sdk.NewCoin(testToDenom, math.NewInt(1000)))) + addr := testAddr(t) + setRate(k, ctx) + + _, err := k.EscrowSwap(ctx, addr, testFromDenom, testToDenom, math.ZeroInt()) + require.NoError(t, err) + + k.SetPrice(ctx, types.NewPrice(testFromDenom, testToDenom, math.LegacyMustNewDecFromStr("0.08"))) + + _, paid, err := k.PaySwap(ctx, testExchangeAddress, addr) + require.NoError(t, err) + require.Equal(t, math.NewInt(8), paid.Amount) +} + +// Only the configured exchange address may pay out — the payout leaves its +// balance, so nothing else can authorise it. +func TestPaySwapRejectsForeignPayer(t *testing.T) { + balance := sdk.NewCoin(testFromDenom, math.NewInt(100)) + k, ctx, _ := swapKeeperTestSetup(t, payoutMocks(balance, sdk.NewCoin(testToDenom, math.NewInt(1000)))) + addr := testAddr(t) + setRate(k, ctx) + + _, err := k.EscrowSwap(ctx, addr, testFromDenom, testToDenom, math.ZeroInt()) + require.NoError(t, err) + + _, _, err = k.PaySwap(ctx, testAddr(t), addr) + require.ErrorIs(t, err, types.ErrUnauthorizedPayer) +} + +// The exchange holds real inventory, so running dry is a genuine failure and the +// pending entry must survive for a retry or a cancel. +func TestPaySwapInsufficientLiquidity(t *testing.T) { + balance := sdk.NewCoin(testFromDenom, math.NewInt(100)) + k, ctx, _ := swapKeeperTestSetup(t, payoutMocks(balance, sdk.NewCoin(testToDenom, math.NewInt(3)))) + addr := testAddr(t) + setRate(k, ctx) + + _, err := k.EscrowSwap(ctx, addr, testFromDenom, testToDenom, math.ZeroInt()) + require.NoError(t, err) + + _, _, err = k.PaySwap(ctx, testExchangeAddress, addr) + require.ErrorIs(t, err, types.ErrExchangeLiquidity) + + _, found := k.GetPendingSwap(ctx, addr) + require.True(t, found, "a failed payout must leave the escrow recoverable") +} + +func TestPaySwapWithoutPending(t *testing.T) { + k, ctx, _ := swapKeeperTestSetup(t, payoutMocks(sdk.NewCoin(testFromDenom, math.NewInt(100)), sdk.NewCoin(testToDenom, math.NewInt(1000)))) + + _, _, err := k.PaySwap(ctx, testExchangeAddress, testAddr(t)) + require.ErrorIs(t, err, types.ErrNoPendingSwap) +} + +// A rate too small to pay one base unit is refused rather than settling for +// nothing. +func TestPaySwapDustAmount(t *testing.T) { + balance := sdk.NewCoin(testFromDenom, math.NewInt(10)) + k, ctx, _ := swapKeeperTestSetup(t, payoutMocks(balance, sdk.NewCoin(testToDenom, math.NewInt(1000)))) + addr := testAddr(t) + setRate(k, ctx) + + _, err := k.EscrowSwap(ctx, addr, testFromDenom, testToDenom, math.ZeroInt()) + require.NoError(t, err) + + // 10 * 0.04 = 0.4, truncates to 0 + _, _, err = k.PaySwap(ctx, testExchangeAddress, addr) + require.ErrorIs(t, err, types.ErrDustAmount) +} + +// --------------------------------------------------------------------------- +// Cancel +// --------------------------------------------------------------------------- + +func TestCancelSwapRefundsFromModuleAccount(t *testing.T) { + balance := sdk.NewCoin(testFromDenom, math.NewInt(100)) + k, ctx, m := swapKeeperTestSetup(t, escrowGatingMocks(balance)) + addr := testAddr(t) + setRate(k, ctx) + + m.bank.EXPECT(). + SendCoinsFromAccountToModule(gomock.Any(), addr, types.ModuleName, sdk.NewCoins(balance)). + Return(nil) + // The refund comes from the module account, never from exchange reserves. + m.bank.EXPECT(). + SendCoinsFromModuleToAccount(gomock.Any(), types.ModuleName, addr, sdk.NewCoins(balance)). + Return(nil) + + _, err := k.EscrowSwap(ctx, addr, testFromDenom, testToDenom, math.ZeroInt()) + require.NoError(t, err) + + refunded, err := k.CancelSwap(ctx, addr) + require.NoError(t, err) + require.Equal(t, balance, refunded) + + _, found := k.GetPendingSwap(ctx, addr) + require.False(t, found) +} + +func TestCancelSwapWithoutPending(t *testing.T) { + k, ctx, _ := swapKeeperTestSetup(t, nil) + + _, err := k.CancelSwap(ctx, testAddr(t)) + require.ErrorIs(t, err, types.ErrNoPendingSwap) +} diff --git a/x/swap/module.go b/x/swap/module.go new file mode 100644 index 0000000..0873740 --- /dev/null +++ b/x/swap/module.go @@ -0,0 +1,128 @@ +package swap + +import ( + "context" + "encoding/json" + "fmt" + + "cosmossdk.io/core/appmodule" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/codec" + cdctypes "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + "github.com/grpc-ecosystem/grpc-gateway/runtime" + + "github.com/peersyst/cbdc-node/x/swap/keeper" + "github.com/peersyst/cbdc-node/x/swap/types" +) + +var ( + _ module.AppModuleBasic = (*AppModule)(nil) + _ module.HasGenesis = (*AppModule)(nil) + _ appmodule.AppModule = (*AppModule)(nil) +) + +// ---------------------------------------------------------------------------- +// AppModuleBasic +// ---------------------------------------------------------------------------- + +type AppModuleBasic struct { + cdc codec.BinaryCodec +} + +func NewAppModuleBasic(cdc codec.BinaryCodec) AppModuleBasic { + return AppModuleBasic{cdc: cdc} +} + +func (AppModuleBasic) Name() string { + return types.ModuleName +} + +func (AppModuleBasic) RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { + types.RegisterCodec(cdc) +} + +func (a AppModuleBasic) RegisterInterfaces(reg cdctypes.InterfaceRegistry) { + types.RegisterInterfaces(reg) +} + +func (AppModuleBasic) DefaultGenesis(cdc codec.JSONCodec) json.RawMessage { + return cdc.MustMarshalJSON(types.DefaultGenesis()) +} + +func (AppModuleBasic) ValidateGenesis(cdc codec.JSONCodec, _ client.TxEncodingConfig, bz json.RawMessage) error { + var genState types.GenesisState + if err := cdc.UnmarshalJSON(bz, &genState); err != nil { + return fmt.Errorf("failed to unmarshal %s genesis state: %w", types.ModuleName, err) + } + return genState.Validate() +} + +func (AppModuleBasic) RegisterGRPCGatewayRoutes(clientCtx client.Context, mux *runtime.ServeMux) { + if err := types.RegisterQueryHandlerClient(context.Background(), mux, types.NewQueryClient(clientCtx)); err != nil { + panic(err) + } +} + +// NOTE: no GetTxCmd/GetQueryCmd. Unlike x/cbdc, none of this module's messages +// carry a Coin field, so autocli derives the commands without tripping over it. + +// ---------------------------------------------------------------------------- +// AppModule +// ---------------------------------------------------------------------------- + +type AppModule struct { + AppModuleBasic + keeper keeper.Keeper + ak types.AccountKeeper +} + +func NewAppModule( + cdc codec.Codec, + keeper keeper.Keeper, + ak types.AccountKeeper, +) AppModule { + return AppModule{ + AppModuleBasic: NewAppModuleBasic(cdc), + keeper: keeper, + ak: ak, + } +} + +func (am AppModule) RegisterServices(cfg module.Configurator) { + types.RegisterMsgServer(cfg.MsgServer(), keeper.NewMsgServerImpl(am.keeper)) + types.RegisterQueryServer(cfg.QueryServer(), keeper.NewQuerier(am.keeper)) + + // v1 keyed a price by its quoted direction, which let a pair hold two + // contradictory entries; v2 gives the pair a single slot. A chain carrying v1 + // prices must run this or the entries stored in the reverse order become + // unreachable. + if err := cfg.RegisterMigration(types.ModuleName, 1, keeper.NewMigrator(am.keeper).Migrate1to2); err != nil { + panic(err) + } +} + +func (am AppModule) RegisterInvariants(_ sdk.InvariantRegistry) {} + +func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, gs json.RawMessage) { + var genState types.GenesisState + cdc.MustUnmarshalJSON(gs, &genState) + + am.keeper.InitGenesis(ctx, genState) + + // To create the module account that holds escrowed assets + am.ak.GetModuleAccount(ctx, am.Name()) +} + +func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.RawMessage { + genState := am.keeper.ExportGenesis(ctx) + return cdc.MustMarshalJSON(genState) +} + +func (AppModule) ConsensusVersion() uint64 { return 2 } + +func (am AppModule) IsOnePerModuleType() {} + +func (am AppModule) IsAppModule() {} diff --git a/x/swap/testutil/expected_keepers_mock.go b/x/swap/testutil/expected_keepers_mock.go new file mode 100644 index 0000000..716963d --- /dev/null +++ b/x/swap/testutil/expected_keepers_mock.go @@ -0,0 +1,157 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: x/swap/types/expected_keepers.go + +// Package testutil is a generated GoMock package. +package testutil + +import ( + context "context" + reflect "reflect" + + types "github.com/cosmos/cosmos-sdk/types" + gomock "github.com/golang/mock/gomock" +) + +// MockAccountKeeper is a mock of AccountKeeper interface. +type MockAccountKeeper struct { + ctrl *gomock.Controller + recorder *MockAccountKeeperMockRecorder +} + +// MockAccountKeeperMockRecorder is the mock recorder for MockAccountKeeper. +type MockAccountKeeperMockRecorder struct { + mock *MockAccountKeeper +} + +// NewMockAccountKeeper creates a new mock instance. +func NewMockAccountKeeper(ctrl *gomock.Controller) *MockAccountKeeper { + mock := &MockAccountKeeper{ctrl: ctrl} + mock.recorder = &MockAccountKeeperMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockAccountKeeper) EXPECT() *MockAccountKeeperMockRecorder { + return m.recorder +} + +// GetModuleAccount mocks base method. +func (m *MockAccountKeeper) GetModuleAccount(ctx context.Context, moduleName string) types.ModuleAccountI { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetModuleAccount", ctx, moduleName) + ret0, _ := ret[0].(types.ModuleAccountI) + return ret0 +} + +// GetModuleAccount indicates an expected call of GetModuleAccount. +func (mr *MockAccountKeeperMockRecorder) GetModuleAccount(ctx, moduleName interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetModuleAccount", reflect.TypeOf((*MockAccountKeeper)(nil).GetModuleAccount), ctx, moduleName) +} + +// MockBankKeeper is a mock of BankKeeper interface. +type MockBankKeeper struct { + ctrl *gomock.Controller + recorder *MockBankKeeperMockRecorder +} + +// MockBankKeeperMockRecorder is the mock recorder for MockBankKeeper. +type MockBankKeeperMockRecorder struct { + mock *MockBankKeeper +} + +// NewMockBankKeeper creates a new mock instance. +func NewMockBankKeeper(ctrl *gomock.Controller) *MockBankKeeper { + mock := &MockBankKeeper{ctrl: ctrl} + mock.recorder = &MockBankKeeperMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockBankKeeper) EXPECT() *MockBankKeeperMockRecorder { + return m.recorder +} + +// BlockedAddr mocks base method. +func (m *MockBankKeeper) BlockedAddr(addr types.AccAddress) bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "BlockedAddr", addr) + ret0, _ := ret[0].(bool) + return ret0 +} + +// BlockedAddr indicates an expected call of BlockedAddr. +func (mr *MockBankKeeperMockRecorder) BlockedAddr(addr interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BlockedAddr", reflect.TypeOf((*MockBankKeeper)(nil).BlockedAddr), addr) +} + +// GetBalance mocks base method. +func (m *MockBankKeeper) GetBalance(ctx context.Context, addr types.AccAddress, denom string) types.Coin { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetBalance", ctx, addr, denom) + ret0, _ := ret[0].(types.Coin) + return ret0 +} + +// GetBalance indicates an expected call of GetBalance. +func (mr *MockBankKeeperMockRecorder) GetBalance(ctx, addr, denom interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBalance", reflect.TypeOf((*MockBankKeeper)(nil).GetBalance), ctx, addr, denom) +} + +// IsSendEnabledCoin mocks base method. +func (m *MockBankKeeper) IsSendEnabledCoin(ctx context.Context, coin types.Coin) bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IsSendEnabledCoin", ctx, coin) + ret0, _ := ret[0].(bool) + return ret0 +} + +// IsSendEnabledCoin indicates an expected call of IsSendEnabledCoin. +func (mr *MockBankKeeperMockRecorder) IsSendEnabledCoin(ctx, coin interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsSendEnabledCoin", reflect.TypeOf((*MockBankKeeper)(nil).IsSendEnabledCoin), ctx, coin) +} + +// SendCoins mocks base method. +func (m *MockBankKeeper) SendCoins(ctx context.Context, fromAddr, toAddr types.AccAddress, amt types.Coins) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SendCoins", ctx, fromAddr, toAddr, amt) + ret0, _ := ret[0].(error) + return ret0 +} + +// SendCoins indicates an expected call of SendCoins. +func (mr *MockBankKeeperMockRecorder) SendCoins(ctx, fromAddr, toAddr, amt interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendCoins", reflect.TypeOf((*MockBankKeeper)(nil).SendCoins), ctx, fromAddr, toAddr, amt) +} + +// SendCoinsFromAccountToModule mocks base method. +func (m *MockBankKeeper) SendCoinsFromAccountToModule(ctx context.Context, senderAddr types.AccAddress, recipientModule string, amt types.Coins) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SendCoinsFromAccountToModule", ctx, senderAddr, recipientModule, amt) + ret0, _ := ret[0].(error) + return ret0 +} + +// SendCoinsFromAccountToModule indicates an expected call of SendCoinsFromAccountToModule. +func (mr *MockBankKeeperMockRecorder) SendCoinsFromAccountToModule(ctx, senderAddr, recipientModule, amt interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendCoinsFromAccountToModule", reflect.TypeOf((*MockBankKeeper)(nil).SendCoinsFromAccountToModule), ctx, senderAddr, recipientModule, amt) +} + +// SendCoinsFromModuleToAccount mocks base method. +func (m *MockBankKeeper) SendCoinsFromModuleToAccount(ctx context.Context, senderModule string, recipientAddr types.AccAddress, amt types.Coins) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SendCoinsFromModuleToAccount", ctx, senderModule, recipientAddr, amt) + ret0, _ := ret[0].(error) + return ret0 +} + +// SendCoinsFromModuleToAccount indicates an expected call of SendCoinsFromModuleToAccount. +func (mr *MockBankKeeperMockRecorder) SendCoinsFromModuleToAccount(ctx, senderModule, recipientAddr, amt interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendCoinsFromModuleToAccount", reflect.TypeOf((*MockBankKeeper)(nil).SendCoinsFromModuleToAccount), ctx, senderModule, recipientAddr, amt) +} diff --git a/x/swap/types/codec.go b/x/swap/types/codec.go new file mode 100644 index 0000000..caaed6e --- /dev/null +++ b/x/swap/types/codec.go @@ -0,0 +1,41 @@ +package types + +import ( + "github.com/cosmos/cosmos-sdk/codec" + cdctypes "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/msgservice" +) + +func RegisterCodec(cdc *codec.LegacyAmino) { + cdc.RegisterConcrete(&MsgSetPrice{}, "swap/SetPrice", nil) + cdc.RegisterConcrete(&MsgSwap{}, "swap/Swap", nil) + cdc.RegisterConcrete(&MsgUpdateParams{}, "swap/UpdateParams", nil) + cdc.RegisterConcrete(&MsgPaySwap{}, "swap/PaySwap", nil) + cdc.RegisterConcrete(&MsgCancelSwap{}, "swap/CancelSwap", nil) + cdc.RegisterConcrete(&MsgAssertSwapSettled{}, "swap/AssertSwapSettled", nil) +} + +func RegisterInterfaces(registry cdctypes.InterfaceRegistry) { + registry.RegisterImplementations((*sdk.Msg)(nil), + &MsgSetPrice{}, + ) + registry.RegisterImplementations((*sdk.Msg)(nil), + &MsgSwap{}, + ) + registry.RegisterImplementations((*sdk.Msg)(nil), + &MsgUpdateParams{}, + ) + registry.RegisterImplementations((*sdk.Msg)(nil), + &MsgPaySwap{}, + &MsgCancelSwap{}, + &MsgAssertSwapSettled{}, + ) + + msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc) +} + +var ( + Amino = codec.NewLegacyAmino() + ModuleCdc = codec.NewProtoCodec(cdctypes.NewInterfaceRegistry()) +) diff --git a/x/swap/types/errors.go b/x/swap/types/errors.go new file mode 100644 index 0000000..07cc4cb --- /dev/null +++ b/x/swap/types/errors.go @@ -0,0 +1,33 @@ +package types + +// DONTCOVER + +import ( + sdkerrors "cosmossdk.io/errors" +) + +// x/swap module sentinel errors +var ( + ErrInvalidDenom = sdkerrors.Register(ModuleName, 1, "invalid denom") + ErrSameDenom = sdkerrors.Register(ModuleName, 2, "from and to denoms must differ") + ErrInvalidPrice = sdkerrors.Register(ModuleName, 3, "price must be positive") + ErrPriceNotFound = sdkerrors.Register(ModuleName, 4, "no price set for denom pair") + ErrDuplicatePair = sdkerrors.Register(ModuleName, 5, "duplicate denom pair") + ErrUnauthorized = sdkerrors.Register(ModuleName, 6, "address must match the signer") + ErrZeroBalance = sdkerrors.Register(ModuleName, 9, "address holds no balance in the from denom") + ErrDustAmount = sdkerrors.Register(ModuleName, 10, "converted amount rounds down to zero") + ErrBlockedAddr = sdkerrors.Register(ModuleName, 11, "address is blocked from receiving funds") + ErrSendDisabled = sdkerrors.Register(ModuleName, 12, "transfers are disabled for the denom") + + ErrExchangeAddressUnset = sdkerrors.Register(ModuleName, 13, "exchange address is not set") + ErrSwapWithExchange = sdkerrors.Register(ModuleName, 14, "the exchange address cannot swap with itself") + ErrInvalidAuthority = sdkerrors.Register(ModuleName, 15, "invalid authority") + ErrInvalidExchangeAddress = sdkerrors.Register(ModuleName, 16, "invalid exchange address") + + ErrPendingSwapExists = sdkerrors.Register(ModuleName, 17, "address already has a swap awaiting payout") + ErrNoPendingSwap = sdkerrors.Register(ModuleName, 18, "address has no swap awaiting payout") + ErrUnauthorizedPayer = sdkerrors.Register(ModuleName, 19, "only the exchange address may pay out a swap") + ErrSwapNotSettled = sdkerrors.Register(ModuleName, 20, "swap is still awaiting payout") + ErrInsufficientAmount = sdkerrors.Register(ModuleName, 21, "amount exceeds the available balance") + ErrExchangeLiquidity = sdkerrors.Register(ModuleName, 22, "exchange address holds too little of the requested denom") +) diff --git a/x/swap/types/events.go b/x/swap/types/events.go new file mode 100644 index 0000000..0f5908f --- /dev/null +++ b/x/swap/types/events.go @@ -0,0 +1,19 @@ +package types + +// swap module event types +const ( + EventTypeSetPrice = "set_price" + EventTypeSwap = "swap" + EventTypePaySwap = "pay_swap" + EventTypeCancelSwap = "cancel_swap" + EventTypeUpdateParams = "update_params" + + AttributeSender = "sender" + AttributeAddress = "address" + AttributeFromDenom = "from_denom" + AttributeToDenom = "to_denom" + AttributePrice = "price" + AttributeEscrowed = "escrowed" + AttributePaid = "paid" + AttributeExchangeAddress = "exchange_address" +) diff --git a/x/swap/types/expected_keepers.go b/x/swap/types/expected_keepers.go new file mode 100644 index 0000000..fb7c822 --- /dev/null +++ b/x/swap/types/expected_keepers.go @@ -0,0 +1,25 @@ +package types + +import ( + "context" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// AccountKeeper defines the expected account keeper, used to create the module +// account that holds escrowed assets. +type AccountKeeper interface { + GetModuleAccount(ctx context.Context, moduleName string) sdk.ModuleAccountI +} + +// BankKeeper defines the expected interface needed to move both sides of an +// exchange. Nothing here mints: an exchange is two transfers of balances that +// already exist. +type BankKeeper interface { + GetBalance(ctx context.Context, addr sdk.AccAddress, denom string) sdk.Coin + SendCoinsFromModuleToAccount(ctx context.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error + SendCoinsFromAccountToModule(ctx context.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error + SendCoins(ctx context.Context, fromAddr, toAddr sdk.AccAddress, amt sdk.Coins) error + BlockedAddr(addr sdk.AccAddress) bool + IsSendEnabledCoin(ctx context.Context, coin sdk.Coin) bool +} diff --git a/x/swap/types/genesis.go b/x/swap/types/genesis.go new file mode 100644 index 0000000..5edbe57 --- /dev/null +++ b/x/swap/types/genesis.go @@ -0,0 +1,33 @@ +package types + +import ( + errorsmod "cosmossdk.io/errors" +) + +// DefaultGenesis returns the default genesis state +func DefaultGenesis() *GenesisState { + return &GenesisState{ + Prices: []Price{}, + Params: DefaultParams(), + PendingSwaps: []PendingSwap{}, + } +} + +// Validate performs basic genesis state validation returning an error upon any +// failure. +func (gs GenesisState) Validate() error { + seen := make(map[string]struct{}, len(gs.Prices)) + for _, p := range gs.Prices { + if err := p.Validate(); err != nil { + return err + } + // Keyed on the unordered pair: a pair is quoted once and the reverse is + // derived, so the same two denoms in the opposite order is the same entry. + key := string(PairKey(p.FromDenom, p.ToDenom)) + if _, ok := seen[key]; ok { + return errorsmod.Wrapf(ErrDuplicatePair, "%s -> %s", p.FromDenom, p.ToDenom) + } + seen[key] = struct{}{} + } + return gs.Params.Validate() +} diff --git a/x/swap/types/genesis.pb.go b/x/swap/types/genesis.pb.go new file mode 100644 index 0000000..2c043b9 --- /dev/null +++ b/x/swap/types/genesis.pb.go @@ -0,0 +1,453 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: swap/genesis.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 + +// GenesisState defines the swap module's genesis state. +type GenesisState struct { + // prices is the full set of stored exchange rates. + Prices []Price `protobuf:"bytes,1,rep,name=prices,proto3" json:"prices"` + // params is the module's parameter set. + Params Params `protobuf:"bytes,2,opt,name=params,proto3" json:"params"` + // pending_swaps are escrowed legs awaiting payout. Normally empty: an entry + // lives only between MsgSwap and MsgPaySwap, usually within one transaction. + // Exported so an escrow that outlived a restart is not silently dropped. + PendingSwaps []PendingSwap `protobuf:"bytes,3,rep,name=pending_swaps,json=pendingSwaps,proto3" json:"pending_swaps"` +} + +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_3c485c51859e7f43, []int{0} +} +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) GetPrices() []Price { + if m != nil { + return m.Prices + } + return nil +} + +func (m *GenesisState) GetParams() Params { + if m != nil { + return m.Params + } + return Params{} +} + +func (m *GenesisState) GetPendingSwaps() []PendingSwap { + if m != nil { + return m.PendingSwaps + } + return nil +} + +func init() { + proto.RegisterType((*GenesisState)(nil), "swap.GenesisState") +} + +func init() { proto.RegisterFile("swap/genesis.proto", fileDescriptor_3c485c51859e7f43) } + +var fileDescriptor_3c485c51859e7f43 = []byte{ + // 256 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x2a, 0x2e, 0x4f, 0x2c, + 0xd0, 0x4f, 0x4f, 0xcd, 0x4b, 0x2d, 0xce, 0x2c, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, + 0x01, 0x89, 0x49, 0x89, 0xa4, 0xe7, 0xa7, 0xe7, 0x83, 0x05, 0xf4, 0x41, 0x2c, 0x88, 0x9c, 0x94, + 0x20, 0x58, 0x7d, 0x41, 0x62, 0x51, 0x62, 0x2e, 0x54, 0xb9, 0x14, 0x3f, 0x58, 0x08, 0x44, 0x40, + 0x04, 0x94, 0x16, 0x33, 0x72, 0xf1, 0xb8, 0x43, 0x4c, 0x0c, 0x2e, 0x49, 0x2c, 0x49, 0x15, 0xd2, + 0xe4, 0x62, 0x2b, 0x28, 0xca, 0x4c, 0x4e, 0x2d, 0x96, 0x60, 0x54, 0x60, 0xd6, 0xe0, 0x36, 0xe2, + 0xd6, 0x03, 0xab, 0x0e, 0x00, 0x89, 0x39, 0xb1, 0x9c, 0xb8, 0x27, 0xcf, 0x10, 0x04, 0x55, 0x20, + 0xa4, 0xc5, 0xc5, 0x06, 0x31, 0x5c, 0x82, 0x49, 0x81, 0x51, 0x83, 0xdb, 0x88, 0x07, 0xaa, 0x14, + 0x2c, 0x06, 0x57, 0x0b, 0xe6, 0x09, 0xd9, 0x70, 0xf1, 0x16, 0xa4, 0xe6, 0xa5, 0x64, 0xe6, 0xa5, + 0xc7, 0x83, 0x14, 0x15, 0x4b, 0x30, 0x83, 0x4d, 0x17, 0x84, 0x6a, 0x81, 0x48, 0x05, 0x97, 0x27, + 0x16, 0x40, 0xf5, 0xf1, 0x14, 0x20, 0x84, 0x8a, 0x9d, 0x5c, 0x4e, 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, 0x4a, 0x2b, 0x3d, 0xb3, 0x24, 0xa3, 0x34, 0x49, 0x2f, 0x39, 0x3f, 0x57, + 0xbf, 0x20, 0x35, 0xb5, 0xa8, 0xb8, 0xb2, 0xb8, 0x44, 0x3f, 0x39, 0x29, 0x25, 0x59, 0x37, 0x2f, + 0x3f, 0x25, 0x55, 0xbf, 0x02, 0xec, 0x57, 0xfd, 0x92, 0xca, 0x82, 0xd4, 0xe2, 0x24, 0x36, 0xb0, + 0x97, 0x8d, 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, 0x8e, 0xa3, 0xf7, 0x0e, 0x48, 0x01, 0x00, 0x00, +} + +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.PendingSwaps) > 0 { + for iNdEx := len(m.PendingSwaps) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.PendingSwaps[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 len(m.Prices) > 0 { + for iNdEx := len(m.Prices) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Prices[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 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 *GenesisState) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Prices) > 0 { + for _, e := range m.Prices { + l = e.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + } + l = m.Params.Size() + n += 1 + l + sovGenesis(uint64(l)) + if len(m.PendingSwaps) > 0 { + for _, e := range m.PendingSwaps { + 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 *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 != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Prices", 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.Prices = append(m.Prices, Price{}) + if err := m.Prices[len(m.Prices)-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 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 PendingSwaps", 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.PendingSwaps = append(m.PendingSwaps, PendingSwap{}) + if err := m.PendingSwaps[len(m.PendingSwaps)-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/x/swap/types/keys.go b/x/swap/types/keys.go new file mode 100644 index 0000000..65bb312 --- /dev/null +++ b/x/swap/types/keys.go @@ -0,0 +1,48 @@ +package types + +const ( + // ModuleName defines the module name + ModuleName = "swap" + + // StoreKey defines the primary module store key + StoreKey = ModuleName + + // RouterKey defines the module's message routing key + RouterKey = ModuleName +) + +// PriceKeyPrefix prefixes every stored Price. +var PriceKeyPrefix = []byte{0x01} + +// ParamsKey is the store key for the module's parameter set. +var ParamsKey = []byte{0x02} + +// PendingSwapKeyPrefix prefixes every escrowed-but-unpaid swap, keyed by the +// address that owns the escrow. One per address, so a second swap from the same +// account while one is outstanding is refused rather than silently queued. +var PendingSwapKeyPrefix = []byte{0x03} + +// PriceKey builds an order-sensitive key for a denom pair. Prices are stored +// under PairKey; this is its building block, and addresses the v1 layout that +// Migrator.Migrate1to2 reads. +// +// The length of fromDenom is written first so pairs cannot collide: without it +// ("ab", "cd") and ("abc", "d") would produce the same key. One byte suffices — +// sdk.ValidateDenom caps denoms at 128 chars. +func PriceKey(fromDenom, toDenom string) []byte { + key := make([]byte, 0, 1+len(fromDenom)+len(toDenom)) + key = append(key, byte(len(fromDenom))) + key = append(key, fromDenom...) + key = append(key, toDenom...) + return key +} + +// PairKey is the store key for a denom pair, identical for both orderings. That +// is what makes one rate per pair structural: a second entry for the same two +// denoms is unrepresentable. The quoted direction lives in the stored Price. +func PairKey(fromDenom, toDenom string) []byte { + if fromDenom > toDenom { + fromDenom, toDenom = toDenom, fromDenom + } + return PriceKey(fromDenom, toDenom) +} diff --git a/x/swap/types/message_pay_swap.go b/x/swap/types/message_pay_swap.go new file mode 100644 index 0000000..6d76274 --- /dev/null +++ b/x/swap/types/message_pay_swap.go @@ -0,0 +1,64 @@ +package types + +import ( + errorsmod "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +var ( + _ sdk.Msg = &MsgPaySwap{} + _ sdk.Msg = &MsgCancelSwap{} + _ sdk.Msg = &MsgAssertSwapSettled{} +) + +func NewMsgPaySwap(sender, address string) *MsgPaySwap { + return &MsgPaySwap{Sender: sender, Address: address} +} + +// ValidateBasic performs stateless validation of the payout leg. +// +// That the sender is actually the configured exchange address is stateful and +// enforced in the keeper — this only checks the addresses are well formed and +// that the exchange is not paying itself. +func (msg *MsgPaySwap) ValidateBasic() error { + if _, err := sdk.AccAddressFromBech32(msg.Sender); err != nil { + return errorsmod.Wrapf(err, "invalid sender address (%s)", msg.Sender) + } + if _, err := sdk.AccAddressFromBech32(msg.Address); err != nil { + return errorsmod.Wrapf(err, "invalid address (%s)", msg.Address) + } + if msg.Sender == msg.Address { + return errorsmod.Wrap(ErrSwapWithExchange, msg.Address) + } + return nil +} + +func NewMsgCancelSwap(sender string) *MsgCancelSwap { + return &MsgCancelSwap{Sender: sender} +} + +func (msg *MsgCancelSwap) ValidateBasic() error { + if _, err := sdk.AccAddressFromBech32(msg.Sender); err != nil { + return errorsmod.Wrapf(err, "invalid sender address (%s)", msg.Sender) + } + return nil +} + +func NewMsgAssertSwapSettled(sender, address string) *MsgAssertSwapSettled { + return &MsgAssertSwapSettled{Sender: sender, Address: address} +} + +// ValidateBasic performs stateless validation of the settlement assertion. +// +// The sender is only a signer: anyone may assert, because the assertion cannot +// change state and its only effect is to fail a transaction that left an escrow +// unpaid. +func (msg *MsgAssertSwapSettled) ValidateBasic() error { + if _, err := sdk.AccAddressFromBech32(msg.Sender); err != nil { + return errorsmod.Wrapf(err, "invalid sender address (%s)", msg.Sender) + } + if _, err := sdk.AccAddressFromBech32(msg.Address); err != nil { + return errorsmod.Wrapf(err, "invalid address (%s)", msg.Address) + } + return nil +} diff --git a/x/swap/types/message_set_price.go b/x/swap/types/message_set_price.go new file mode 100644 index 0000000..d67064a --- /dev/null +++ b/x/swap/types/message_set_price.go @@ -0,0 +1,34 @@ +package types + +import ( + errorsmod "cosmossdk.io/errors" + "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +var _ sdk.Msg = &MsgSetPrice{} + +func NewMsgSetPrice(sender, fromDenom, toDenom string, price math.LegacyDec) *MsgSetPrice { + return &MsgSetPrice{ + Sender: sender, + FromDenom: fromDenom, + ToDenom: toDenom, + Price: price, + } +} + +// ValidateBasic performs stateless validation of the message. +// +// The sender is only checked for well-formedness. There is deliberately no +// authorization check anywhere in this path: any account can set any rate. +func (msg *MsgSetPrice) ValidateBasic() error { + if _, err := sdk.AccAddressFromBech32(msg.Sender); err != nil { + return errorsmod.Wrapf(err, "invalid sender address (%s)", msg.Sender) + } + return msg.ToPrice().Validate() +} + +// ToPrice returns the Price this message stores. +func (msg *MsgSetPrice) ToPrice() Price { + return NewPrice(msg.FromDenom, msg.ToDenom, msg.Price) +} diff --git a/x/swap/types/message_set_price_test.go b/x/swap/types/message_set_price_test.go new file mode 100644 index 0000000..a17cbbf --- /dev/null +++ b/x/swap/types/message_set_price_test.go @@ -0,0 +1,157 @@ +package types + +import ( + "testing" + + "cosmossdk.io/math" + "github.com/peersyst/cbdc-node/testutil/sample" + "github.com/stretchr/testify/require" +) + +func TestMsgSetPrice_ValidateBasic(t *testing.T) { + rate := math.LegacyMustNewDecFromStr("0.04") + + tt := []struct { + name string + msg MsgSetPrice + expectErr bool + }{ + { + name: "valid", + msg: MsgSetPrice{Sender: sample.AccAddress(), FromDenom: "mhnl", ToDenom: "uusd", Price: rate}, + }, + { + name: "invalid sender", + msg: MsgSetPrice{Sender: "invalid", FromDenom: "mhnl", ToDenom: "uusd", Price: rate}, + expectErr: true, + }, + { + name: "invalid from denom", + msg: MsgSetPrice{Sender: sample.AccAddress(), FromDenom: "!!", ToDenom: "uusd", Price: rate}, + expectErr: true, + }, + { + name: "invalid to denom", + msg: MsgSetPrice{Sender: sample.AccAddress(), FromDenom: "mhnl", ToDenom: "!!", Price: rate}, + expectErr: true, + }, + { + name: "same denom", + msg: MsgSetPrice{Sender: sample.AccAddress(), FromDenom: "mhnl", ToDenom: "mhnl", Price: rate}, + expectErr: true, + }, + { + name: "zero price", + msg: MsgSetPrice{Sender: sample.AccAddress(), FromDenom: "mhnl", ToDenom: "uusd", Price: math.LegacyZeroDec()}, + expectErr: true, + }, + { + name: "negative price", + msg: MsgSetPrice{Sender: sample.AccAddress(), FromDenom: "mhnl", ToDenom: "uusd", Price: math.LegacyMustNewDecFromStr("-1")}, + expectErr: true, + }, + { + // a Dec left unset has a nil internal value; IsPositive would panic on it + name: "nil price", + msg: MsgSetPrice{Sender: sample.AccAddress(), FromDenom: "mhnl", ToDenom: "uusd"}, + expectErr: true, + }, + } + + for _, tc := range tt { + t.Run(tc.name, func(t *testing.T) { + err := tc.msg.ValidateBasic() + if tc.expectErr { + require.Error(t, err) + return + } + require.NoError(t, err) + }) + } +} + +func TestGenesisState_Validate(t *testing.T) { + rate := math.LegacyMustNewDecFromStr("0.04") + + t.Run("default is valid", func(t *testing.T) { + require.NoError(t, DefaultGenesis().Validate()) + }) + + // A pair is quoted once and the reverse is derived, so the same two denoms in + // the opposite order is the same entry, not a second one. Genesis carrying + // both would start the chain with two rates that can contradict each other. + t.Run("reverse direction of a pair rejected", func(t *testing.T) { + gs := GenesisState{Prices: []Price{ + NewPrice("mhnl", "uusd", rate), + NewPrice("uusd", "mhnl", math.LegacyMustNewDecFromStr("25")), + }} + require.ErrorIs(t, gs.Validate(), ErrDuplicatePair) + }) + + t.Run("unrelated pairs sharing a denom are allowed", func(t *testing.T) { + gs := GenesisState{Prices: []Price{ + NewPrice("mhnl", "uusd", rate), + NewPrice("mhnl", "ubrl", math.LegacyMustNewDecFromStr("0.4")), + }} + require.NoError(t, gs.Validate()) + }) + + t.Run("duplicate pair rejected", func(t *testing.T) { + gs := GenesisState{Prices: []Price{ + NewPrice("mhnl", "uusd", rate), + NewPrice("mhnl", "uusd", math.LegacyMustNewDecFromStr("0.05")), + }} + require.ErrorIs(t, gs.Validate(), ErrDuplicatePair) + }) + + t.Run("invalid entry rejected", func(t *testing.T) { + gs := GenesisState{Prices: []Price{NewPrice("mhnl", "mhnl", rate)}} + require.ErrorIs(t, gs.Validate(), ErrSameDenom) + }) +} + +func TestMsgSwap_ValidateBasic(t *testing.T) { + addr := sample.AccAddress() + + tt := []struct { + name string + msg MsgSwap + expectErr bool + }{ + { + name: "valid", + msg: MsgSwap{Sender: addr, Address: addr, FromDenom: "uusd", ToDenom: "mhnl"}, + }, + { + name: "address is not the signer", + msg: MsgSwap{Sender: addr, Address: sample.AccAddress(), FromDenom: "uusd", ToDenom: "mhnl"}, + expectErr: true, + }, + { + name: "invalid sender", + msg: MsgSwap{Sender: "invalid", Address: addr, FromDenom: "uusd", ToDenom: "mhnl"}, + expectErr: true, + }, + { + name: "invalid from denom", + msg: MsgSwap{Sender: addr, Address: addr, FromDenom: "!!", ToDenom: "mhnl"}, + expectErr: true, + }, + { + name: "same denom", + msg: MsgSwap{Sender: addr, Address: addr, FromDenom: "mhnl", ToDenom: "mhnl"}, + expectErr: true, + }, + } + + for _, tc := range tt { + t.Run(tc.name, func(t *testing.T) { + err := tc.msg.ValidateBasic() + if tc.expectErr { + require.Error(t, err) + return + } + require.NoError(t, err) + }) + } +} diff --git a/x/swap/types/message_swap.go b/x/swap/types/message_swap.go new file mode 100644 index 0000000..58fa182 --- /dev/null +++ b/x/swap/types/message_swap.go @@ -0,0 +1,56 @@ +package types + +import ( + errorsmod "cosmossdk.io/errors" + "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +var _ sdk.Msg = &MsgSwap{} + +// NewMsgSwap builds the escrow leg of an exchange. A zero or nil amount means +// the whole balance of fromDenom. +func NewMsgSwap(sender, address, fromDenom, toDenom string, amount math.Int) *MsgSwap { + if amount.IsNil() { + amount = math.ZeroInt() + } + return &MsgSwap{ + Sender: sender, + Address: address, + FromDenom: fromDenom, + ToDenom: toDenom, + Amount: amount, + } +} + +// ValidateBasic performs stateless validation of the message. +// +// Whether a rate exists for the pair, whether the account holds the amount, and +// whether the exchange can cover the other side are all stateful and stay in the +// keeper. +func (msg *MsgSwap) ValidateBasic() error { + if _, err := sdk.AccAddressFromBech32(msg.Sender); err != nil { + return errorsmod.Wrapf(err, "invalid sender address (%s)", msg.Sender) + } + if _, err := sdk.AccAddressFromBech32(msg.Address); err != nil { + return errorsmod.Wrapf(err, "invalid address (%s)", msg.Address) + } + if msg.Sender != msg.Address { + return errorsmod.Wrapf(ErrUnauthorized, "expected %s got %s", msg.Sender, msg.Address) + } + if err := sdk.ValidateDenom(msg.FromDenom); err != nil { + return errorsmod.Wrapf(ErrInvalidDenom, "from denom %q: %s", msg.FromDenom, err) + } + if err := sdk.ValidateDenom(msg.ToDenom); err != nil { + return errorsmod.Wrapf(ErrInvalidDenom, "to denom %q: %s", msg.ToDenom, err) + } + if msg.FromDenom == msg.ToDenom { + return errorsmod.Wrap(ErrSameDenom, msg.FromDenom) + } + // A nil Amount decodes from an absent field, and zero is the documented + // "whole balance" form; only a negative one is malformed. + if !msg.Amount.IsNil() && msg.Amount.IsNegative() { + return errorsmod.Wrapf(ErrInsufficientAmount, "negative amount %s", msg.Amount) + } + return nil +} diff --git a/x/swap/types/params.go b/x/swap/types/params.go new file mode 100644 index 0000000..65f6ed6 --- /dev/null +++ b/x/swap/types/params.go @@ -0,0 +1,34 @@ +package types + +import ( + errorsmod "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// NewParams creates a new Params instance. +func NewParams(exchangeAddress string) Params { + return Params{ExchangeAddress: exchangeAddress} +} + +// DefaultParams returns a default set of parameters. +// +// The exchange address is deliberately empty, which leaves swapping disabled: it +// is an x/group policy address that does not exist until the group is created, +// so no genesis can name it. A chain comes up able to hold rates and unable to +// convert, rather than converting into whatever address a placeholder named. +func DefaultParams() Params { + return NewParams("") +} + +// Validate performs stateless validation of the parameter set. +func (p Params) Validate() error { + // Empty is the default and means "swapping disabled" — a valid state, and + // the one every chain starts in. + if p.ExchangeAddress == "" { + return nil + } + if _, err := sdk.AccAddressFromBech32(p.ExchangeAddress); err != nil { + return errorsmod.Wrapf(ErrInvalidExchangeAddress, "%s: %s", p.ExchangeAddress, err) + } + return nil +} diff --git a/x/swap/types/params.pb.go b/x/swap/types/params.pb.go new file mode 100644 index 0000000..bb928bc --- /dev/null +++ b/x/swap/types/params.pb.go @@ -0,0 +1,329 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: swap/params.proto + +package types + +import ( + fmt "fmt" + _ "github.com/cosmos/cosmos-proto" + 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 + +// Params defines the parameters for the module. +type Params struct { + // exchange_address receives the incoming asset when a swap converts it. It + // holds the collateral backing every unit this module has minted. + // + // It is gov-controlled via MsgUpdateParams rather than fixed at genesis + // because it is an x/group policy address, which does not exist until the + // group is created — after the chain is already running. + // + // Empty, the default, disables swapping entirely: with nowhere to put the + // incoming asset there is no way to convert without minting against + // collateral the chain never took custody of. + ExchangeAddress string `protobuf:"bytes,1,opt,name=exchange_address,json=exchangeAddress,proto3" json:"exchange_address,omitempty"` +} + +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_2841fee6739ad578, []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) GetExchangeAddress() string { + if m != nil { + return m.ExchangeAddress + } + return "" +} + +func init() { + proto.RegisterType((*Params)(nil), "swap.Params") +} + +func init() { proto.RegisterFile("swap/params.proto", fileDescriptor_2841fee6739ad578) } + +var fileDescriptor_2841fee6739ad578 = []byte{ + // 193 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x2c, 0x2e, 0x4f, 0x2c, + 0xd0, 0x2f, 0x48, 0x2c, 0x4a, 0xcc, 0x2d, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x01, + 0x09, 0x49, 0x49, 0x26, 0xe7, 0x17, 0xe7, 0xe6, 0x17, 0xc7, 0x83, 0xc5, 0xf4, 0x21, 0x1c, 0x88, + 0x02, 0x25, 0x5f, 0x2e, 0xb6, 0x00, 0xb0, 0x06, 0x21, 0x67, 0x2e, 0x81, 0xd4, 0x8a, 0xe4, 0x8c, + 0xc4, 0xbc, 0xf4, 0xd4, 0xf8, 0xc4, 0x94, 0x94, 0xa2, 0xd4, 0xe2, 0x62, 0x09, 0x46, 0x05, 0x46, + 0x0d, 0x4e, 0x27, 0x89, 0x4b, 0x5b, 0x74, 0x45, 0xa0, 0xba, 0x1c, 0x21, 0x32, 0xc1, 0x25, 0x45, + 0x99, 0x79, 0xe9, 0x41, 0xfc, 0x30, 0x1d, 0x50, 0x61, 0x27, 0x97, 0x13, 0x8f, 0xe4, 0x18, 0x2f, + 0x3c, 0x92, 0x63, 0x7c, 0xf0, 0x48, 0x8e, 0x71, 0xc2, 0x63, 0x39, 0x86, 0x0b, 0x8f, 0xe5, 0x18, + 0x6e, 0x3c, 0x96, 0x63, 0x88, 0xd2, 0x4a, 0xcf, 0x2c, 0xc9, 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, + 0xd5, 0x2f, 0x48, 0x4d, 0x2d, 0x2a, 0xae, 0x2c, 0x2e, 0xd1, 0x4f, 0x4e, 0x4a, 0x49, 0xd6, 0xcd, + 0xcb, 0x4f, 0x49, 0xd5, 0xaf, 0xd0, 0x07, 0x3b, 0xbe, 0xa4, 0xb2, 0x20, 0xb5, 0x38, 0x89, 0x0d, + 0xec, 0x36, 0x63, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0x57, 0x2f, 0x0e, 0x11, 0xd1, 0x00, 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.ExchangeAddress) > 0 { + i -= len(m.ExchangeAddress) + copy(dAtA[i:], m.ExchangeAddress) + i = encodeVarintParams(dAtA, i, uint64(len(m.ExchangeAddress))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintParams(dAtA []byte, offset int, v uint64) int { + offset -= sovParams(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 + l = len(m.ExchangeAddress) + if l > 0 { + n += 1 + l + sovParams(uint64(l)) + } + return n +} + +func sovParams(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozParams(x uint64) (n int) { + return sovParams(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 ErrIntOverflowParams + } + 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 ExchangeAddress", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowParams + } + 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 ErrInvalidLengthParams + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthParams + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ExchangeAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipParams(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthParams + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipParams(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, ErrIntOverflowParams + } + 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, ErrIntOverflowParams + } + 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, ErrIntOverflowParams + } + 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, ErrInvalidLengthParams + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupParams + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthParams + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthParams = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowParams = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupParams = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/swap/types/price.go b/x/swap/types/price.go new file mode 100644 index 0000000..d999f48 --- /dev/null +++ b/x/swap/types/price.go @@ -0,0 +1,36 @@ +package types + +import ( + errorsmod "cosmossdk.io/errors" + "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +func NewPrice(fromDenom, toDenom string, price math.LegacyDec) Price { + return Price{ + FromDenom: fromDenom, + ToDenom: toDenom, + Price: price, + } +} + +// Validate performs stateless validation of an exchange rate. It is shared by +// MsgSetPrice.ValidateBasic and GenesisState.Validate so a rate can only enter +// the store through one set of rules. +func (p Price) Validate() error { + if err := sdk.ValidateDenom(p.FromDenom); err != nil { + return errorsmod.Wrapf(ErrInvalidDenom, "from denom %q: %s", p.FromDenom, err) + } + if err := sdk.ValidateDenom(p.ToDenom); err != nil { + return errorsmod.Wrapf(ErrInvalidDenom, "to denom %q: %s", p.ToDenom, err) + } + if p.FromDenom == p.ToDenom { + return errorsmod.Wrap(ErrSameDenom, p.FromDenom) + } + // A Dec that was never set decodes with a nil internal value, which would + // make IsPositive panic rather than return false. + if p.Price.IsNil() || !p.Price.IsPositive() { + return errorsmod.Wrapf(ErrInvalidPrice, "%s -> %s", p.FromDenom, p.ToDenom) + } + return nil +} diff --git a/x/swap/types/query.pb.go b/x/swap/types/query.pb.go new file mode 100644 index 0000000..c96131a --- /dev/null +++ b/x/swap/types/query.pb.go @@ -0,0 +1,1885 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: swap/query.proto + +package types + +import ( + context "context" + fmt "fmt" + query "github.com/cosmos/cosmos-sdk/types/query" + _ "github.com/cosmos/gogoproto/gogoproto" + grpc1 "github.com/cosmos/gogoproto/grpc" + proto "github.com/cosmos/gogoproto/proto" + _ "google.golang.org/genproto/googleapis/api/annotations" + 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 + +// QueryPendingSwapRequest is request type for the Query/PendingSwap RPC method. +type QueryPendingSwapRequest struct { + Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` +} + +func (m *QueryPendingSwapRequest) Reset() { *m = QueryPendingSwapRequest{} } +func (m *QueryPendingSwapRequest) String() string { return proto.CompactTextString(m) } +func (*QueryPendingSwapRequest) ProtoMessage() {} +func (*QueryPendingSwapRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_ba9b806334123578, []int{0} +} +func (m *QueryPendingSwapRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryPendingSwapRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryPendingSwapRequest.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 *QueryPendingSwapRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryPendingSwapRequest.Merge(m, src) +} +func (m *QueryPendingSwapRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryPendingSwapRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryPendingSwapRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryPendingSwapRequest proto.InternalMessageInfo + +func (m *QueryPendingSwapRequest) GetAddress() string { + if m != nil { + return m.Address + } + return "" +} + +// QueryPendingSwapResponse is response type for the Query/PendingSwap RPC method. +type QueryPendingSwapResponse struct { + // found is false when the address has nothing outstanding, which is the normal + // state — a pending swap exists only between an exchange's two legs. + Found bool `protobuf:"varint,1,opt,name=found,proto3" json:"found,omitempty"` + PendingSwap PendingSwap `protobuf:"bytes,2,opt,name=pending_swap,json=pendingSwap,proto3" json:"pending_swap"` +} + +func (m *QueryPendingSwapResponse) Reset() { *m = QueryPendingSwapResponse{} } +func (m *QueryPendingSwapResponse) String() string { return proto.CompactTextString(m) } +func (*QueryPendingSwapResponse) ProtoMessage() {} +func (*QueryPendingSwapResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_ba9b806334123578, []int{1} +} +func (m *QueryPendingSwapResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryPendingSwapResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryPendingSwapResponse.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 *QueryPendingSwapResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryPendingSwapResponse.Merge(m, src) +} +func (m *QueryPendingSwapResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryPendingSwapResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryPendingSwapResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryPendingSwapResponse proto.InternalMessageInfo + +func (m *QueryPendingSwapResponse) GetFound() bool { + if m != nil { + return m.Found + } + return false +} + +func (m *QueryPendingSwapResponse) GetPendingSwap() PendingSwap { + if m != nil { + return m.PendingSwap + } + return PendingSwap{} +} + +// QueryParamsRequest is request type for the Query/Params RPC method. +type QueryParamsRequest struct { +} + +func (m *QueryParamsRequest) Reset() { *m = QueryParamsRequest{} } +func (m *QueryParamsRequest) String() string { return proto.CompactTextString(m) } +func (*QueryParamsRequest) ProtoMessage() {} +func (*QueryParamsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_ba9b806334123578, []int{2} +} +func (m *QueryParamsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryParamsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryParamsRequest.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 *QueryParamsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryParamsRequest.Merge(m, src) +} +func (m *QueryParamsRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryParamsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryParamsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryParamsRequest proto.InternalMessageInfo + +// QueryParamsResponse is response type for the Query/Params RPC method. +type QueryParamsResponse struct { + Params Params `protobuf:"bytes,1,opt,name=params,proto3" json:"params"` +} + +func (m *QueryParamsResponse) Reset() { *m = QueryParamsResponse{} } +func (m *QueryParamsResponse) String() string { return proto.CompactTextString(m) } +func (*QueryParamsResponse) ProtoMessage() {} +func (*QueryParamsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_ba9b806334123578, []int{3} +} +func (m *QueryParamsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryParamsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryParamsResponse.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 *QueryParamsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryParamsResponse.Merge(m, src) +} +func (m *QueryParamsResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryParamsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryParamsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryParamsResponse proto.InternalMessageInfo + +func (m *QueryParamsResponse) GetParams() Params { + if m != nil { + return m.Params + } + return Params{} +} + +// QueryPriceRequest is request type for the Query/Price RPC method. +type QueryPriceRequest struct { + FromDenom string `protobuf:"bytes,1,opt,name=from_denom,json=fromDenom,proto3" json:"from_denom,omitempty"` + ToDenom string `protobuf:"bytes,2,opt,name=to_denom,json=toDenom,proto3" json:"to_denom,omitempty"` +} + +func (m *QueryPriceRequest) Reset() { *m = QueryPriceRequest{} } +func (m *QueryPriceRequest) String() string { return proto.CompactTextString(m) } +func (*QueryPriceRequest) ProtoMessage() {} +func (*QueryPriceRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_ba9b806334123578, []int{4} +} +func (m *QueryPriceRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryPriceRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryPriceRequest.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 *QueryPriceRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryPriceRequest.Merge(m, src) +} +func (m *QueryPriceRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryPriceRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryPriceRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryPriceRequest proto.InternalMessageInfo + +func (m *QueryPriceRequest) GetFromDenom() string { + if m != nil { + return m.FromDenom + } + return "" +} + +func (m *QueryPriceRequest) GetToDenom() string { + if m != nil { + return m.ToDenom + } + return "" +} + +// QueryPriceResponse is response type for the Query/Price RPC method. +type QueryPriceResponse struct { + Price Price `protobuf:"bytes,1,opt,name=price,proto3" json:"price"` +} + +func (m *QueryPriceResponse) Reset() { *m = QueryPriceResponse{} } +func (m *QueryPriceResponse) String() string { return proto.CompactTextString(m) } +func (*QueryPriceResponse) ProtoMessage() {} +func (*QueryPriceResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_ba9b806334123578, []int{5} +} +func (m *QueryPriceResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryPriceResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryPriceResponse.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 *QueryPriceResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryPriceResponse.Merge(m, src) +} +func (m *QueryPriceResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryPriceResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryPriceResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryPriceResponse proto.InternalMessageInfo + +func (m *QueryPriceResponse) GetPrice() Price { + if m != nil { + return m.Price + } + return Price{} +} + +// QueryPricesRequest is request type for the Query/Prices RPC method. +type QueryPricesRequest struct { + Pagination *query.PageRequest `protobuf:"bytes,1,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (m *QueryPricesRequest) Reset() { *m = QueryPricesRequest{} } +func (m *QueryPricesRequest) String() string { return proto.CompactTextString(m) } +func (*QueryPricesRequest) ProtoMessage() {} +func (*QueryPricesRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_ba9b806334123578, []int{6} +} +func (m *QueryPricesRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryPricesRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryPricesRequest.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 *QueryPricesRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryPricesRequest.Merge(m, src) +} +func (m *QueryPricesRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryPricesRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryPricesRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryPricesRequest proto.InternalMessageInfo + +func (m *QueryPricesRequest) GetPagination() *query.PageRequest { + if m != nil { + return m.Pagination + } + return nil +} + +// QueryPricesResponse is response type for the Query/Prices RPC method. +type QueryPricesResponse struct { + Prices []Price `protobuf:"bytes,1,rep,name=prices,proto3" json:"prices"` + Pagination *query.PageResponse `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (m *QueryPricesResponse) Reset() { *m = QueryPricesResponse{} } +func (m *QueryPricesResponse) String() string { return proto.CompactTextString(m) } +func (*QueryPricesResponse) ProtoMessage() {} +func (*QueryPricesResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_ba9b806334123578, []int{7} +} +func (m *QueryPricesResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryPricesResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryPricesResponse.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 *QueryPricesResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryPricesResponse.Merge(m, src) +} +func (m *QueryPricesResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryPricesResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryPricesResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryPricesResponse proto.InternalMessageInfo + +func (m *QueryPricesResponse) GetPrices() []Price { + if m != nil { + return m.Prices + } + return nil +} + +func (m *QueryPricesResponse) GetPagination() *query.PageResponse { + if m != nil { + return m.Pagination + } + return nil +} + +func init() { + proto.RegisterType((*QueryPendingSwapRequest)(nil), "swap.QueryPendingSwapRequest") + proto.RegisterType((*QueryPendingSwapResponse)(nil), "swap.QueryPendingSwapResponse") + proto.RegisterType((*QueryParamsRequest)(nil), "swap.QueryParamsRequest") + proto.RegisterType((*QueryParamsResponse)(nil), "swap.QueryParamsResponse") + proto.RegisterType((*QueryPriceRequest)(nil), "swap.QueryPriceRequest") + proto.RegisterType((*QueryPriceResponse)(nil), "swap.QueryPriceResponse") + proto.RegisterType((*QueryPricesRequest)(nil), "swap.QueryPricesRequest") + proto.RegisterType((*QueryPricesResponse)(nil), "swap.QueryPricesResponse") +} + +func init() { proto.RegisterFile("swap/query.proto", fileDescriptor_ba9b806334123578) } + +var fileDescriptor_ba9b806334123578 = []byte{ + // 579 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x94, 0xcf, 0x6b, 0x13, 0x41, + 0x14, 0xc7, 0xb3, 0xb1, 0x89, 0xed, 0x4b, 0xc1, 0x66, 0x1a, 0xe8, 0x76, 0xb1, 0x6b, 0xd9, 0x83, + 0xad, 0x01, 0x77, 0x68, 0x7a, 0x13, 0x3c, 0x58, 0x8a, 0x9e, 0x84, 0x1a, 0x3d, 0x88, 0x08, 0x65, + 0x92, 0x9d, 0x6e, 0x17, 0x9a, 0x9d, 0xe9, 0xce, 0xc4, 0x9a, 0xab, 0x37, 0x6f, 0x82, 0xff, 0x54, + 0x8f, 0x05, 0x2f, 0x9e, 0x44, 0x12, 0xff, 0x0b, 0x2f, 0x32, 0xf3, 0x66, 0xe9, 0xc6, 0x44, 0xbc, + 0x84, 0xec, 0xfb, 0xf1, 0xf9, 0x7e, 0xdf, 0xcc, 0xdb, 0x85, 0x0d, 0x75, 0xc5, 0x24, 0xbd, 0x1c, + 0xf3, 0x62, 0x12, 0xcb, 0x42, 0x68, 0x41, 0x56, 0x4c, 0x24, 0xe8, 0xa4, 0x22, 0x15, 0x36, 0x40, + 0xcd, 0x3f, 0xcc, 0x05, 0xf7, 0x53, 0x21, 0xd2, 0x0b, 0x4e, 0x99, 0xcc, 0x28, 0xcb, 0x73, 0xa1, + 0x99, 0xce, 0x44, 0xae, 0x5c, 0xb6, 0x3b, 0x14, 0x6a, 0x24, 0x14, 0x1d, 0x30, 0xc5, 0x11, 0x49, + 0x3f, 0x1c, 0x0c, 0xb8, 0x66, 0x07, 0x54, 0xb2, 0x34, 0xcb, 0x6d, 0xb1, 0xab, 0x6d, 0x5b, 0x5d, + 0xc9, 0x0a, 0x36, 0x2a, 0xdb, 0xef, 0xd9, 0x90, 0xf9, 0xc1, 0x40, 0x74, 0x08, 0x5b, 0xaf, 0x0c, + 0xe5, 0x84, 0xe7, 0x49, 0x96, 0xa7, 0xaf, 0xaf, 0x98, 0xec, 0xf3, 0xcb, 0x31, 0x57, 0x9a, 0xf8, + 0x70, 0x97, 0x25, 0x49, 0xc1, 0x95, 0xf2, 0xbd, 0x5d, 0x6f, 0x7f, 0xad, 0x5f, 0x3e, 0x46, 0x17, + 0xe0, 0x2f, 0x36, 0x29, 0x29, 0x72, 0xc5, 0x49, 0x07, 0x1a, 0x67, 0x62, 0x9c, 0x27, 0xb6, 0x67, + 0xb5, 0x8f, 0x0f, 0xe4, 0x09, 0xac, 0x4b, 0x2c, 0x3e, 0x35, 0xe2, 0x7e, 0x7d, 0xd7, 0xdb, 0x6f, + 0xf5, 0xda, 0xb1, 0x75, 0x52, 0xc1, 0x1c, 0xad, 0x5c, 0xff, 0x78, 0x50, 0xeb, 0xb7, 0xe4, 0x6d, + 0x28, 0xea, 0x00, 0x41, 0x35, 0x3b, 0x88, 0x73, 0x17, 0x3d, 0x83, 0xcd, 0xb9, 0xa8, 0x93, 0xef, + 0x42, 0x13, 0x07, 0xb6, 0xfa, 0xad, 0xde, 0xba, 0x93, 0xb0, 0x31, 0x47, 0x77, 0x15, 0xd1, 0x4b, + 0x68, 0x23, 0xa2, 0xc8, 0x86, 0xbc, 0x9c, 0x7a, 0x07, 0xe0, 0xac, 0x10, 0xa3, 0xd3, 0x84, 0xe7, + 0x62, 0xe4, 0x06, 0x5f, 0x33, 0x91, 0x63, 0x13, 0x20, 0xdb, 0xb0, 0xaa, 0x85, 0x4b, 0xd6, 0xf1, + 0x54, 0xb4, 0xb0, 0xa9, 0xe8, 0x69, 0xe9, 0x13, 0x71, 0xce, 0xd0, 0x1e, 0x34, 0xa4, 0x09, 0x38, + 0x3f, 0x2d, 0xe7, 0xc7, 0x84, 0x9c, 0x1d, 0xcc, 0x47, 0xef, 0xab, 0xed, 0xe5, 0x98, 0xe4, 0x39, + 0xc0, 0xed, 0xbd, 0x3a, 0xc6, 0xc3, 0x18, 0x97, 0x20, 0x36, 0x4b, 0x10, 0xe3, 0x5e, 0xb9, 0x25, + 0x88, 0x4f, 0x58, 0x5a, 0x8e, 0xd2, 0xaf, 0x74, 0x46, 0x9f, 0xbd, 0xf2, 0xbc, 0x1c, 0xde, 0xd9, + 0x7b, 0x04, 0x4d, 0x2b, 0x6f, 0xce, 0xeb, 0xce, 0x72, 0x7f, 0xae, 0x80, 0xbc, 0x98, 0xb3, 0x82, + 0x37, 0xb8, 0xf7, 0x5f, 0x2b, 0xa8, 0x53, 0xf5, 0xd2, 0xfb, 0x5d, 0x87, 0x86, 0xf5, 0x42, 0xde, + 0x40, 0xc3, 0x2a, 0x91, 0x2d, 0x94, 0x5d, 0xb8, 0x8e, 0xc0, 0x5f, 0x4c, 0x20, 0x31, 0xf2, 0x3f, + 0x7d, 0xfb, 0xf5, 0xb5, 0x4e, 0xc8, 0x06, 0x1d, 0x0e, 0x92, 0x21, 0xc5, 0x5d, 0xb7, 0xb0, 0xb7, + 0xd0, 0xc4, 0x29, 0xc9, 0x42, 0x77, 0x79, 0xae, 0xc1, 0xf6, 0x92, 0x8c, 0x03, 0x6f, 0x5b, 0xf0, + 0x26, 0x69, 0xff, 0x0d, 0x56, 0x96, 0x6c, 0x77, 0x67, 0x9e, 0x5c, 0x5d, 0xcc, 0x79, 0xf2, 0xdc, + 0x72, 0x2e, 0x27, 0x23, 0xef, 0x1c, 0x5a, 0x95, 0xd7, 0x80, 0xec, 0x54, 0x21, 0x0b, 0xaf, 0x66, + 0x10, 0xfe, 0x2b, 0xed, 0x84, 0x02, 0x2b, 0xd4, 0x21, 0xa4, 0x2a, 0x84, 0x75, 0x47, 0xc7, 0xd7, + 0xd3, 0xd0, 0xbb, 0x99, 0x86, 0xde, 0xcf, 0x69, 0xe8, 0x7d, 0x99, 0x85, 0xb5, 0x9b, 0x59, 0x58, + 0xfb, 0x3e, 0x0b, 0x6b, 0xef, 0xba, 0x69, 0xa6, 0xcf, 0xc7, 0x83, 0x78, 0x28, 0x46, 0x54, 0x72, + 0x5e, 0xa8, 0x89, 0xd2, 0x16, 0xf0, 0x38, 0x17, 0x09, 0xa7, 0x1f, 0x91, 0xa3, 0x27, 0x92, 0xab, + 0x41, 0xd3, 0x7e, 0x3e, 0x0e, 0xff, 0x04, 0x00, 0x00, 0xff, 0xff, 0x90, 0xd6, 0x5a, 0xc9, 0xdc, + 0x04, 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 + +// QueryClient is the client API for Query service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type QueryClient interface { + // Price queries the exchange rate stored for a single ordered denom pair. + // + // The denoms are query-string parameters rather than path segments because + // IBC voucher denoms contain a slash, which no path template can match. + Price(ctx context.Context, in *QueryPriceRequest, opts ...grpc.CallOption) (*QueryPriceResponse, error) + // Prices queries every stored exchange rate. + Prices(ctx context.Context, in *QueryPricesRequest, opts ...grpc.CallOption) (*QueryPricesResponse, error) + // Params queries the module's parameter set. + Params(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error) + // PendingSwap queries the escrowed-but-unpaid swap for an address, if any. + // + // The address is a query-string parameter for the same reason the price pair + // is: nothing about it is guaranteed path-safe. + PendingSwap(ctx context.Context, in *QueryPendingSwapRequest, opts ...grpc.CallOption) (*QueryPendingSwapResponse, error) +} + +type queryClient struct { + cc grpc1.ClientConn +} + +func NewQueryClient(cc grpc1.ClientConn) QueryClient { + return &queryClient{cc} +} + +func (c *queryClient) Price(ctx context.Context, in *QueryPriceRequest, opts ...grpc.CallOption) (*QueryPriceResponse, error) { + out := new(QueryPriceResponse) + err := c.cc.Invoke(ctx, "/swap.Query/Price", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) Prices(ctx context.Context, in *QueryPricesRequest, opts ...grpc.CallOption) (*QueryPricesResponse, error) { + out := new(QueryPricesResponse) + err := c.cc.Invoke(ctx, "/swap.Query/Prices", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) Params(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error) { + out := new(QueryParamsResponse) + err := c.cc.Invoke(ctx, "/swap.Query/Params", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) PendingSwap(ctx context.Context, in *QueryPendingSwapRequest, opts ...grpc.CallOption) (*QueryPendingSwapResponse, error) { + out := new(QueryPendingSwapResponse) + err := c.cc.Invoke(ctx, "/swap.Query/PendingSwap", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// QueryServer is the server API for Query service. +type QueryServer interface { + // Price queries the exchange rate stored for a single ordered denom pair. + // + // The denoms are query-string parameters rather than path segments because + // IBC voucher denoms contain a slash, which no path template can match. + Price(context.Context, *QueryPriceRequest) (*QueryPriceResponse, error) + // Prices queries every stored exchange rate. + Prices(context.Context, *QueryPricesRequest) (*QueryPricesResponse, error) + // Params queries the module's parameter set. + Params(context.Context, *QueryParamsRequest) (*QueryParamsResponse, error) + // PendingSwap queries the escrowed-but-unpaid swap for an address, if any. + // + // The address is a query-string parameter for the same reason the price pair + // is: nothing about it is guaranteed path-safe. + PendingSwap(context.Context, *QueryPendingSwapRequest) (*QueryPendingSwapResponse, error) +} + +// UnimplementedQueryServer can be embedded to have forward compatible implementations. +type UnimplementedQueryServer struct { +} + +func (*UnimplementedQueryServer) Price(ctx context.Context, req *QueryPriceRequest) (*QueryPriceResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Price not implemented") +} +func (*UnimplementedQueryServer) Prices(ctx context.Context, req *QueryPricesRequest) (*QueryPricesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Prices not implemented") +} +func (*UnimplementedQueryServer) Params(ctx context.Context, req *QueryParamsRequest) (*QueryParamsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Params not implemented") +} +func (*UnimplementedQueryServer) PendingSwap(ctx context.Context, req *QueryPendingSwapRequest) (*QueryPendingSwapResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method PendingSwap not implemented") +} + +func RegisterQueryServer(s grpc1.Server, srv QueryServer) { + s.RegisterService(&_Query_serviceDesc, srv) +} + +func _Query_Price_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryPriceRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).Price(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/swap.Query/Price", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).Price(ctx, req.(*QueryPriceRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_Prices_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryPricesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).Prices(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/swap.Query/Prices", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).Prices(ctx, req.(*QueryPricesRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_Params_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryParamsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).Params(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/swap.Query/Params", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).Params(ctx, req.(*QueryParamsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_PendingSwap_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryPendingSwapRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).PendingSwap(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/swap.Query/PendingSwap", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).PendingSwap(ctx, req.(*QueryPendingSwapRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var Query_serviceDesc = _Query_serviceDesc +var _Query_serviceDesc = grpc.ServiceDesc{ + ServiceName: "swap.Query", + HandlerType: (*QueryServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Price", + Handler: _Query_Price_Handler, + }, + { + MethodName: "Prices", + Handler: _Query_Prices_Handler, + }, + { + MethodName: "Params", + Handler: _Query_Params_Handler, + }, + { + MethodName: "PendingSwap", + Handler: _Query_PendingSwap_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "swap/query.proto", +} + +func (m *QueryPendingSwapRequest) 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 *QueryPendingSwapRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryPendingSwapRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Address) > 0 { + i -= len(m.Address) + copy(dAtA[i:], m.Address) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Address))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryPendingSwapResponse) 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 *QueryPendingSwapResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryPendingSwapResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.PendingSwap.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + if m.Found { + i-- + if m.Found { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *QueryParamsRequest) 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 *QueryParamsRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryParamsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *QueryParamsResponse) 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 *QueryParamsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryParamsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Params.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *QueryPriceRequest) 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 *QueryPriceRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryPriceRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ToDenom) > 0 { + i -= len(m.ToDenom) + copy(dAtA[i:], m.ToDenom) + i = encodeVarintQuery(dAtA, i, uint64(len(m.ToDenom))) + i-- + dAtA[i] = 0x12 + } + if len(m.FromDenom) > 0 { + i -= len(m.FromDenom) + copy(dAtA[i:], m.FromDenom) + i = encodeVarintQuery(dAtA, i, uint64(len(m.FromDenom))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryPriceResponse) 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 *QueryPriceResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryPriceResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Price.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *QueryPricesRequest) 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 *QueryPricesRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryPricesRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Pagination != nil { + { + size, err := m.Pagination.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryPricesResponse) 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 *QueryPricesResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryPricesResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Pagination != nil { + { + size, err := m.Pagination.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if len(m.Prices) > 0 { + for iNdEx := len(m.Prices) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Prices[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func encodeVarintQuery(dAtA []byte, offset int, v uint64) int { + offset -= sovQuery(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *QueryPendingSwapRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Address) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryPendingSwapResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Found { + n += 2 + } + l = m.PendingSwap.Size() + n += 1 + l + sovQuery(uint64(l)) + return n +} + +func (m *QueryParamsRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *QueryParamsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Params.Size() + n += 1 + l + sovQuery(uint64(l)) + return n +} + +func (m *QueryPriceRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.FromDenom) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = len(m.ToDenom) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryPriceResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Price.Size() + n += 1 + l + sovQuery(uint64(l)) + return n +} + +func (m *QueryPricesRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Pagination != nil { + l = m.Pagination.Size() + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryPricesResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Prices) > 0 { + for _, e := range m.Prices { + l = e.Size() + n += 1 + l + sovQuery(uint64(l)) + } + } + if m.Pagination != nil { + l = m.Pagination.Size() + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func sovQuery(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozQuery(x uint64) (n int) { + return sovQuery(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *QueryPendingSwapRequest) 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 ErrIntOverflowQuery + } + 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: QueryPendingSwapRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryPendingSwapRequest: 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 ErrIntOverflowQuery + } + 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 ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Address = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryPendingSwapResponse) 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 ErrIntOverflowQuery + } + 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: QueryPendingSwapResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryPendingSwapResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Found", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Found = bool(v != 0) + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PendingSwap", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.PendingSwap.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryParamsRequest) 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 ErrIntOverflowQuery + } + 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: QueryParamsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryParamsRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryParamsResponse) 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 ErrIntOverflowQuery + } + 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: QueryParamsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryParamsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + 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 ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Params.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryPriceRequest) 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 ErrIntOverflowQuery + } + 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: QueryPriceRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryPriceRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FromDenom", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + 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 ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.FromDenom = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ToDenom", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + 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 ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ToDenom = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryPriceResponse) 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 ErrIntOverflowQuery + } + 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: QueryPriceResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryPriceResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Price", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Price.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryPricesRequest) 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 ErrIntOverflowQuery + } + 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: QueryPricesRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryPricesRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pagination", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Pagination == nil { + m.Pagination = &query.PageRequest{} + } + if err := m.Pagination.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryPricesResponse) 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 ErrIntOverflowQuery + } + 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: QueryPricesResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryPricesResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Prices", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Prices = append(m.Prices, Price{}) + if err := m.Prices[len(m.Prices)-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 Pagination", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Pagination == nil { + m.Pagination = &query.PageResponse{} + } + if err := m.Pagination.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipQuery(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, ErrIntOverflowQuery + } + 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, ErrIntOverflowQuery + } + 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, ErrIntOverflowQuery + } + 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, ErrInvalidLengthQuery + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupQuery + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthQuery + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthQuery = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowQuery = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupQuery = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/swap/types/query.pb.gw.go b/x/swap/types/query.pb.gw.go new file mode 100644 index 0000000..1d596d4 --- /dev/null +++ b/x/swap/types/query.pb.gw.go @@ -0,0 +1,402 @@ +// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. +// source: swap/query.proto + +/* +Package types is a reverse proxy. + +It translates gRPC into RESTful JSON APIs. +*/ +package types + +import ( + "context" + "io" + "net/http" + + "github.com/golang/protobuf/descriptor" + "github.com/golang/protobuf/proto" + "github.com/grpc-ecosystem/grpc-gateway/runtime" + "github.com/grpc-ecosystem/grpc-gateway/utilities" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/grpclog" + "google.golang.org/grpc/metadata" + "google.golang.org/grpc/status" +) + +// Suppress "imported and not used" errors +var _ codes.Code +var _ io.Reader +var _ status.Status +var _ = runtime.String +var _ = utilities.NewDoubleArray +var _ = descriptor.ForMessage +var _ = metadata.Join + +var ( + filter_Query_Price_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + +func request_Query_Price_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryPriceRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_Price_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.Price(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_Price_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryPriceRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_Price_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.Price(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_Query_Prices_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + +func request_Query_Prices_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryPricesRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_Prices_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.Prices(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_Prices_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryPricesRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_Prices_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.Prices(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_Params_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryParamsRequest + var metadata runtime.ServerMetadata + + msg, err := client.Params(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_Params_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryParamsRequest + var metadata runtime.ServerMetadata + + msg, err := server.Params(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_Query_PendingSwap_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + +func request_Query_PendingSwap_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryPendingSwapRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_PendingSwap_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.PendingSwap(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_PendingSwap_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryPendingSwapRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_PendingSwap_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.PendingSwap(ctx, &protoReq) + return msg, metadata, err + +} + +// RegisterQueryHandlerServer registers the http handlers for service Query to "mux". +// UnaryRPC :call QueryServer directly. +// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. +// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterQueryHandlerFromEndpoint instead. +func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, server QueryServer) error { + + mux.Handle("GET", pattern_Query_Price_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_Price_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_Price_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_Prices_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_Prices_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_Prices_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_Params_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_Params_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_Params_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_PendingSwap_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_PendingSwap_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_PendingSwap_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +// RegisterQueryHandlerFromEndpoint is same as RegisterQueryHandler but +// automatically dials to "endpoint" and closes the connection when "ctx" gets done. +func RegisterQueryHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) { + conn, err := grpc.Dial(endpoint, opts...) + if err != nil { + return err + } + defer func() { + if err != nil { + if cerr := conn.Close(); cerr != nil { + grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) + } + return + } + go func() { + <-ctx.Done() + if cerr := conn.Close(); cerr != nil { + grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) + } + }() + }() + + return RegisterQueryHandler(ctx, mux, conn) +} + +// RegisterQueryHandler registers the http handlers for service Query to "mux". +// The handlers forward requests to the grpc endpoint over "conn". +func RegisterQueryHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { + return RegisterQueryHandlerClient(ctx, mux, NewQueryClient(conn)) +} + +// RegisterQueryHandlerClient registers the http handlers for service Query +// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "QueryClient". +// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "QueryClient" +// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in +// "QueryClient" to call the correct interceptors. +func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, client QueryClient) error { + + mux.Handle("GET", pattern_Query_Price_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_Price_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_Price_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_Prices_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_Prices_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_Prices_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_Params_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_Params_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_Params_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_PendingSwap_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_PendingSwap_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_PendingSwap_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +var ( + pattern_Query_Price_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"cbdc", "swap", "price"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_Prices_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"cbdc", "swap", "prices"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_Params_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"cbdc", "swap", "params"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_PendingSwap_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"cbdc", "swap", "pending"}, "", runtime.AssumeColonVerbOpt(false))) +) + +var ( + forward_Query_Price_0 = runtime.ForwardResponseMessage + + forward_Query_Prices_0 = runtime.ForwardResponseMessage + + forward_Query_Params_0 = runtime.ForwardResponseMessage + + forward_Query_PendingSwap_0 = runtime.ForwardResponseMessage +) diff --git a/x/swap/types/swap.pb.go b/x/swap/types/swap.pb.go new file mode 100644 index 0000000..3acea0d --- /dev/null +++ b/x/swap/types/swap.pb.go @@ -0,0 +1,772 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: swap/swap.proto + +package types + +import ( + cosmossdk_io_math "cosmossdk.io/math" + fmt "fmt" + _ "github.com/cosmos/cosmos-proto" + 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 + +// Price is a one-directional exchange rate: how many units of to_denom one unit +// of from_denom is worth. +// +// The pair is ordered. Setting from_denom -> to_denom says nothing about +// to_denom -> from_denom: the reverse rate is a separate entry that has to be +// set explicitly, and is never derived by inverting this one. That keeps the +// stored value exactly what the setter sent (no division rounding) and leaves +// room for asymmetric rates later. +type Price struct { + FromDenom string `protobuf:"bytes,1,opt,name=from_denom,json=fromDenom,proto3" json:"from_denom,omitempty"` + ToDenom string `protobuf:"bytes,2,opt,name=to_denom,json=toDenom,proto3" json:"to_denom,omitempty"` + Price cosmossdk_io_math.LegacyDec `protobuf:"bytes,3,opt,name=price,proto3,customtype=cosmossdk.io/math.LegacyDec" json:"price"` +} + +func (m *Price) Reset() { *m = Price{} } +func (m *Price) String() string { return proto.CompactTextString(m) } +func (*Price) ProtoMessage() {} +func (*Price) Descriptor() ([]byte, []int) { + return fileDescriptor_b4906e0bf1273377, []int{0} +} +func (m *Price) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Price) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Price.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 *Price) XXX_Merge(src proto.Message) { + xxx_messageInfo_Price.Merge(m, src) +} +func (m *Price) XXX_Size() int { + return m.Size() +} +func (m *Price) XXX_DiscardUnknown() { + xxx_messageInfo_Price.DiscardUnknown(m) +} + +var xxx_messageInfo_Price proto.InternalMessageInfo + +func (m *Price) GetFromDenom() string { + if m != nil { + return m.FromDenom + } + return "" +} + +func (m *Price) GetToDenom() string { + if m != nil { + return m.ToDenom + } + return "" +} + +// PendingSwap is a swap whose incoming leg has been escrowed but whose payout +// has not happened yet. +// +// It exists because the two legs are authorised by two different group policies +// and cannot be carried by one message. The entry is written by MsgSwap and +// consumed by MsgPaySwap, normally inside the same transaction; MsgAssertSwapSettled +// exists so a transaction that failed to consume it cannot commit. +type PendingSwap struct { + // address is the account whose funds are escrowed, and the account the payout + // is owed to. One pending swap per address. + Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` + FromDenom string `protobuf:"bytes,2,opt,name=from_denom,json=fromDenom,proto3" json:"from_denom,omitempty"` + ToDenom string `protobuf:"bytes,3,opt,name=to_denom,json=toDenom,proto3" json:"to_denom,omitempty"` + // escrowed is what left the address and sits in the module account. + Escrowed types.Coin `protobuf:"bytes,4,opt,name=escrowed,proto3" json:"escrowed"` +} + +func (m *PendingSwap) Reset() { *m = PendingSwap{} } +func (m *PendingSwap) String() string { return proto.CompactTextString(m) } +func (*PendingSwap) ProtoMessage() {} +func (*PendingSwap) Descriptor() ([]byte, []int) { + return fileDescriptor_b4906e0bf1273377, []int{1} +} +func (m *PendingSwap) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *PendingSwap) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_PendingSwap.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 *PendingSwap) XXX_Merge(src proto.Message) { + xxx_messageInfo_PendingSwap.Merge(m, src) +} +func (m *PendingSwap) XXX_Size() int { + return m.Size() +} +func (m *PendingSwap) XXX_DiscardUnknown() { + xxx_messageInfo_PendingSwap.DiscardUnknown(m) +} + +var xxx_messageInfo_PendingSwap proto.InternalMessageInfo + +func (m *PendingSwap) GetAddress() string { + if m != nil { + return m.Address + } + return "" +} + +func (m *PendingSwap) GetFromDenom() string { + if m != nil { + return m.FromDenom + } + return "" +} + +func (m *PendingSwap) GetToDenom() string { + if m != nil { + return m.ToDenom + } + return "" +} + +func (m *PendingSwap) GetEscrowed() types.Coin { + if m != nil { + return m.Escrowed + } + return types.Coin{} +} + +func init() { + proto.RegisterType((*Price)(nil), "swap.Price") + proto.RegisterType((*PendingSwap)(nil), "swap.PendingSwap") +} + +func init() { proto.RegisterFile("swap/swap.proto", fileDescriptor_b4906e0bf1273377) } + +var fileDescriptor_b4906e0bf1273377 = []byte{ + // 365 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x91, 0xbf, 0x6e, 0xe2, 0x40, + 0x10, 0xc6, 0xbd, 0xfc, 0x39, 0x60, 0x29, 0x4e, 0xb2, 0x28, 0x0c, 0xa7, 0x33, 0x88, 0x0a, 0x9d, + 0x84, 0x57, 0x70, 0x65, 0xaa, 0x38, 0x96, 0xd2, 0xa4, 0x40, 0xa6, 0x4b, 0x83, 0xec, 0xdd, 0x8d, + 0xb1, 0x22, 0x7b, 0x2c, 0xef, 0x26, 0x84, 0x57, 0x48, 0x95, 0x87, 0xa1, 0xc8, 0x23, 0x50, 0x22, + 0xaa, 0x28, 0x05, 0x8a, 0xe0, 0x45, 0xa2, 0xf5, 0x3a, 0x91, 0x92, 0x22, 0x8d, 0x35, 0xf3, 0xfd, + 0x3c, 0x3b, 0xf3, 0xcd, 0xe0, 0xdf, 0x62, 0x15, 0x64, 0x44, 0x7d, 0x9c, 0x2c, 0x07, 0x09, 0x66, + 0x4d, 0xc5, 0xbd, 0x4e, 0x04, 0x11, 0x14, 0x02, 0x51, 0x91, 0x66, 0xbd, 0x2e, 0x05, 0x91, 0x80, + 0x58, 0x68, 0xa0, 0x93, 0x12, 0xd9, 0x3a, 0x23, 0x61, 0x20, 0x38, 0xb9, 0x9f, 0x84, 0x5c, 0x06, + 0x13, 0x42, 0x21, 0x4e, 0x35, 0x1f, 0x3e, 0x22, 0x5c, 0x9f, 0xe5, 0x31, 0xe5, 0xe6, 0x5f, 0x8c, + 0x6f, 0x72, 0x48, 0x16, 0x8c, 0xa7, 0x90, 0x58, 0x68, 0x80, 0x46, 0x2d, 0xbf, 0xa5, 0x14, 0x4f, + 0x09, 0x66, 0x17, 0x37, 0x25, 0x94, 0xb0, 0x52, 0xc0, 0x86, 0x04, 0x8d, 0x2e, 0x71, 0x3d, 0x53, + 0x4f, 0x58, 0x55, 0xa5, 0xbb, 0x93, 0xed, 0xa1, 0x6f, 0xbc, 0x1e, 0xfa, 0x7f, 0x74, 0x6b, 0xc1, + 0x6e, 0x9d, 0x18, 0x48, 0x12, 0xc8, 0xa5, 0x73, 0xc5, 0xa3, 0x80, 0xae, 0x3d, 0x4e, 0xf7, 0x9b, + 0x31, 0x2e, 0xe7, 0xf4, 0x38, 0xf5, 0x75, 0xfd, 0xf0, 0x19, 0xe1, 0xf6, 0x8c, 0xa7, 0x2c, 0x4e, + 0xa3, 0xf9, 0x2a, 0xc8, 0xcc, 0x29, 0x6e, 0x04, 0x8c, 0xe5, 0x5c, 0x08, 0x3d, 0x8f, 0x6b, 0xed, + 0x37, 0xe3, 0x4e, 0x59, 0x77, 0xae, 0xc9, 0x5c, 0xe6, 0x71, 0x1a, 0xf9, 0x1f, 0x3f, 0x7e, 0xb3, + 0x51, 0xf9, 0xc9, 0x46, 0xf5, 0xab, 0x8d, 0x33, 0xdc, 0xe4, 0x82, 0xe6, 0xb0, 0xe2, 0xcc, 0xaa, + 0x0d, 0xd0, 0xa8, 0x3d, 0xed, 0x3a, 0x65, 0x2f, 0xb5, 0x3d, 0xa7, 0xdc, 0x9e, 0x73, 0x01, 0x71, + 0xea, 0xd6, 0x94, 0x49, 0xff, 0xb3, 0xc0, 0xf5, 0xb6, 0x47, 0x1b, 0xed, 0x8e, 0x36, 0x7a, 0x3b, + 0xda, 0xe8, 0xe9, 0x64, 0x1b, 0xbb, 0x93, 0x6d, 0xbc, 0x9c, 0x6c, 0xe3, 0xfa, 0x5f, 0x14, 0xcb, + 0xe5, 0x5d, 0xe8, 0x50, 0x48, 0x48, 0xc6, 0x79, 0x2e, 0xd6, 0x42, 0x12, 0x1a, 0x32, 0x3a, 0x4e, + 0x81, 0x71, 0xf2, 0x50, 0x1c, 0x99, 0xc8, 0x75, 0xc6, 0x45, 0xf8, 0xab, 0x38, 0xca, 0xff, 0xf7, + 0x00, 0x00, 0x00, 0xff, 0xff, 0x3e, 0x2f, 0x15, 0xf2, 0xfe, 0x01, 0x00, 0x00, +} + +func (m *Price) 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 *Price) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Price) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size := m.Price.Size() + i -= size + if _, err := m.Price.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintSwap(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + if len(m.ToDenom) > 0 { + i -= len(m.ToDenom) + copy(dAtA[i:], m.ToDenom) + i = encodeVarintSwap(dAtA, i, uint64(len(m.ToDenom))) + i-- + dAtA[i] = 0x12 + } + if len(m.FromDenom) > 0 { + i -= len(m.FromDenom) + copy(dAtA[i:], m.FromDenom) + i = encodeVarintSwap(dAtA, i, uint64(len(m.FromDenom))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *PendingSwap) 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 *PendingSwap) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *PendingSwap) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Escrowed.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSwap(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + if len(m.ToDenom) > 0 { + i -= len(m.ToDenom) + copy(dAtA[i:], m.ToDenom) + i = encodeVarintSwap(dAtA, i, uint64(len(m.ToDenom))) + i-- + dAtA[i] = 0x1a + } + if len(m.FromDenom) > 0 { + i -= len(m.FromDenom) + copy(dAtA[i:], m.FromDenom) + i = encodeVarintSwap(dAtA, i, uint64(len(m.FromDenom))) + i-- + dAtA[i] = 0x12 + } + if len(m.Address) > 0 { + i -= len(m.Address) + copy(dAtA[i:], m.Address) + i = encodeVarintSwap(dAtA, i, uint64(len(m.Address))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintSwap(dAtA []byte, offset int, v uint64) int { + offset -= sovSwap(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *Price) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.FromDenom) + if l > 0 { + n += 1 + l + sovSwap(uint64(l)) + } + l = len(m.ToDenom) + if l > 0 { + n += 1 + l + sovSwap(uint64(l)) + } + l = m.Price.Size() + n += 1 + l + sovSwap(uint64(l)) + return n +} + +func (m *PendingSwap) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Address) + if l > 0 { + n += 1 + l + sovSwap(uint64(l)) + } + l = len(m.FromDenom) + if l > 0 { + n += 1 + l + sovSwap(uint64(l)) + } + l = len(m.ToDenom) + if l > 0 { + n += 1 + l + sovSwap(uint64(l)) + } + l = m.Escrowed.Size() + n += 1 + l + sovSwap(uint64(l)) + return n +} + +func sovSwap(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozSwap(x uint64) (n int) { + return sovSwap(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *Price) 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 ErrIntOverflowSwap + } + 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: Price: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Price: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FromDenom", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSwap + } + 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 ErrInvalidLengthSwap + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSwap + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.FromDenom = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ToDenom", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSwap + } + 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 ErrInvalidLengthSwap + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSwap + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ToDenom = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Price", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSwap + } + 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 ErrInvalidLengthSwap + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSwap + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Price.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipSwap(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthSwap + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *PendingSwap) 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 ErrIntOverflowSwap + } + 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: PendingSwap: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PendingSwap: 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 ErrIntOverflowSwap + } + 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 ErrInvalidLengthSwap + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSwap + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Address = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FromDenom", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSwap + } + 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 ErrInvalidLengthSwap + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSwap + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.FromDenom = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ToDenom", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSwap + } + 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 ErrInvalidLengthSwap + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSwap + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ToDenom = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Escrowed", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSwap + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthSwap + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSwap + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Escrowed.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipSwap(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthSwap + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipSwap(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, ErrIntOverflowSwap + } + 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, ErrIntOverflowSwap + } + 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, ErrIntOverflowSwap + } + 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, ErrInvalidLengthSwap + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupSwap + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthSwap + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthSwap = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowSwap = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupSwap = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/swap/types/tx.pb.go b/x/swap/types/tx.pb.go new file mode 100644 index 0000000..82130c0 --- /dev/null +++ b/x/swap/types/tx.pb.go @@ -0,0 +1,2749 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: swap/tx.proto + +package types + +import ( + context "context" + cosmossdk_io_math "cosmossdk.io/math" + fmt "fmt" + _ "github.com/cosmos/cosmos-proto" + _ "github.com/cosmos/cosmos-sdk/types/msgservice" + _ "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 + +// MsgSetPrice defines a message that sets the exchange rate for an ordered +// denom pair. +type MsgSetPrice struct { + // sender is the account that signs the message. + // + // NOTE: it is a signer, not an authorization. This message is deliberately + // ungated for now — any account can set any rate — so sender exists only + // because every sdk.Msg needs one for routing and fee deduction. Gating + // belongs here when it arrives. + Sender string `protobuf:"bytes,1,opt,name=sender,proto3" json:"sender,omitempty"` + // from_denom is the denom being converted from. + FromDenom string `protobuf:"bytes,2,opt,name=from_denom,json=fromDenom,proto3" json:"from_denom,omitempty"` + // to_denom is the denom being converted to. + ToDenom string `protobuf:"bytes,3,opt,name=to_denom,json=toDenom,proto3" json:"to_denom,omitempty"` + // price is how many units of to_denom one unit of from_denom is worth. It + // must be positive. + Price cosmossdk_io_math.LegacyDec `protobuf:"bytes,4,opt,name=price,proto3,customtype=cosmossdk.io/math.LegacyDec" json:"price"` +} + +func (m *MsgSetPrice) Reset() { *m = MsgSetPrice{} } +func (m *MsgSetPrice) String() string { return proto.CompactTextString(m) } +func (*MsgSetPrice) ProtoMessage() {} +func (*MsgSetPrice) Descriptor() ([]byte, []int) { + return fileDescriptor_e6107e4e71e75faa, []int{0} +} +func (m *MsgSetPrice) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgSetPrice) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgSetPrice.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 *MsgSetPrice) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgSetPrice.Merge(m, src) +} +func (m *MsgSetPrice) XXX_Size() int { + return m.Size() +} +func (m *MsgSetPrice) XXX_DiscardUnknown() { + xxx_messageInfo_MsgSetPrice.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgSetPrice proto.InternalMessageInfo + +func (m *MsgSetPrice) GetSender() string { + if m != nil { + return m.Sender + } + return "" +} + +func (m *MsgSetPrice) GetFromDenom() string { + if m != nil { + return m.FromDenom + } + return "" +} + +func (m *MsgSetPrice) GetToDenom() string { + if m != nil { + return m.ToDenom + } + return "" +} + +// MsgSetPriceResponse defines the response for setting an exchange rate. +type MsgSetPriceResponse struct { +} + +func (m *MsgSetPriceResponse) Reset() { *m = MsgSetPriceResponse{} } +func (m *MsgSetPriceResponse) String() string { return proto.CompactTextString(m) } +func (*MsgSetPriceResponse) ProtoMessage() {} +func (*MsgSetPriceResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_e6107e4e71e75faa, []int{1} +} +func (m *MsgSetPriceResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgSetPriceResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgSetPriceResponse.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 *MsgSetPriceResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgSetPriceResponse.Merge(m, src) +} +func (m *MsgSetPriceResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgSetPriceResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgSetPriceResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgSetPriceResponse proto.InternalMessageInfo + +// MsgSwap escrows the incoming leg of an exchange. +// +// It does NOT pay anything out: the payout comes from the exchange address, +// which is a different account and cannot be a signer here. MsgPaySwap completes +// it. Nothing is minted — both sides of an exchange are existing balances. +type MsgSwap struct { + // sender is the account that signs the message. + Sender string `protobuf:"bytes,1,opt,name=sender,proto3" json:"sender,omitempty"` + // address is the account whose balance is converted. It must equal sender: + // an account can only swap its own funds. + Address string `protobuf:"bytes,2,opt,name=address,proto3" json:"address,omitempty"` + // from_denom is the denom being escrowed. + FromDenom string `protobuf:"bytes,3,opt,name=from_denom,json=fromDenom,proto3" json:"from_denom,omitempty"` + // to_denom is the denom owed in return. It must have a rate stored for the + // ordered pair (from_denom, to_denom); the reverse rate is never inverted. + ToDenom string `protobuf:"bytes,4,opt,name=to_denom,json=toDenom,proto3" json:"to_denom,omitempty"` + // amount of from_denom to escrow, in base units. Zero means the whole balance. + Amount cosmossdk_io_math.Int `protobuf:"bytes,5,opt,name=amount,proto3,customtype=cosmossdk.io/math.Int" json:"amount"` +} + +func (m *MsgSwap) Reset() { *m = MsgSwap{} } +func (m *MsgSwap) String() string { return proto.CompactTextString(m) } +func (*MsgSwap) ProtoMessage() {} +func (*MsgSwap) Descriptor() ([]byte, []int) { + return fileDescriptor_e6107e4e71e75faa, []int{2} +} +func (m *MsgSwap) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgSwap) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgSwap.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 *MsgSwap) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgSwap.Merge(m, src) +} +func (m *MsgSwap) XXX_Size() int { + return m.Size() +} +func (m *MsgSwap) XXX_DiscardUnknown() { + xxx_messageInfo_MsgSwap.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgSwap proto.InternalMessageInfo + +func (m *MsgSwap) GetSender() string { + if m != nil { + return m.Sender + } + return "" +} + +func (m *MsgSwap) GetAddress() string { + if m != nil { + return m.Address + } + return "" +} + +func (m *MsgSwap) GetFromDenom() string { + if m != nil { + return m.FromDenom + } + return "" +} + +func (m *MsgSwap) GetToDenom() string { + if m != nil { + return m.ToDenom + } + return "" +} + +// MsgSwapResponse defines the response for escrowing a swap. +type MsgSwapResponse struct { +} + +func (m *MsgSwapResponse) Reset() { *m = MsgSwapResponse{} } +func (m *MsgSwapResponse) String() string { return proto.CompactTextString(m) } +func (*MsgSwapResponse) ProtoMessage() {} +func (*MsgSwapResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_e6107e4e71e75faa, []int{3} +} +func (m *MsgSwapResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgSwapResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgSwapResponse.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 *MsgSwapResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgSwapResponse.Merge(m, src) +} +func (m *MsgSwapResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgSwapResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgSwapResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgSwapResponse proto.InternalMessageInfo + +// MsgPaySwap pays the outstanding leg of a pending swap and consumes it. +type MsgPaySwap struct { + // sender must be the configured exchange address: the payout comes out of its + // balance, so nothing else may authorise it. + Sender string `protobuf:"bytes,1,opt,name=sender,proto3" json:"sender,omitempty"` + // address is the account whose pending swap is being settled. + Address string `protobuf:"bytes,2,opt,name=address,proto3" json:"address,omitempty"` +} + +func (m *MsgPaySwap) Reset() { *m = MsgPaySwap{} } +func (m *MsgPaySwap) String() string { return proto.CompactTextString(m) } +func (*MsgPaySwap) ProtoMessage() {} +func (*MsgPaySwap) Descriptor() ([]byte, []int) { + return fileDescriptor_e6107e4e71e75faa, []int{4} +} +func (m *MsgPaySwap) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgPaySwap) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgPaySwap.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 *MsgPaySwap) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgPaySwap.Merge(m, src) +} +func (m *MsgPaySwap) XXX_Size() int { + return m.Size() +} +func (m *MsgPaySwap) XXX_DiscardUnknown() { + xxx_messageInfo_MsgPaySwap.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgPaySwap proto.InternalMessageInfo + +func (m *MsgPaySwap) GetSender() string { + if m != nil { + return m.Sender + } + return "" +} + +func (m *MsgPaySwap) GetAddress() string { + if m != nil { + return m.Address + } + return "" +} + +// MsgPaySwapResponse defines the response for paying a swap. +type MsgPaySwapResponse struct { +} + +func (m *MsgPaySwapResponse) Reset() { *m = MsgPaySwapResponse{} } +func (m *MsgPaySwapResponse) String() string { return proto.CompactTextString(m) } +func (*MsgPaySwapResponse) ProtoMessage() {} +func (*MsgPaySwapResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_e6107e4e71e75faa, []int{5} +} +func (m *MsgPaySwapResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgPaySwapResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgPaySwapResponse.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 *MsgPaySwapResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgPaySwapResponse.Merge(m, src) +} +func (m *MsgPaySwapResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgPaySwapResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgPaySwapResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgPaySwapResponse proto.InternalMessageInfo + +// MsgCancelSwap refunds an escrowed swap that was never paid out. +type MsgCancelSwap struct { + // sender is the account whose escrow is refunded. The refund comes from the + // module account, never from the exchange address. + Sender string `protobuf:"bytes,1,opt,name=sender,proto3" json:"sender,omitempty"` +} + +func (m *MsgCancelSwap) Reset() { *m = MsgCancelSwap{} } +func (m *MsgCancelSwap) String() string { return proto.CompactTextString(m) } +func (*MsgCancelSwap) ProtoMessage() {} +func (*MsgCancelSwap) Descriptor() ([]byte, []int) { + return fileDescriptor_e6107e4e71e75faa, []int{6} +} +func (m *MsgCancelSwap) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgCancelSwap) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgCancelSwap.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 *MsgCancelSwap) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgCancelSwap.Merge(m, src) +} +func (m *MsgCancelSwap) XXX_Size() int { + return m.Size() +} +func (m *MsgCancelSwap) XXX_DiscardUnknown() { + xxx_messageInfo_MsgCancelSwap.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgCancelSwap proto.InternalMessageInfo + +func (m *MsgCancelSwap) GetSender() string { + if m != nil { + return m.Sender + } + return "" +} + +// MsgCancelSwapResponse defines the response for cancelling a swap. +type MsgCancelSwapResponse struct { +} + +func (m *MsgCancelSwapResponse) Reset() { *m = MsgCancelSwapResponse{} } +func (m *MsgCancelSwapResponse) String() string { return proto.CompactTextString(m) } +func (*MsgCancelSwapResponse) ProtoMessage() {} +func (*MsgCancelSwapResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_e6107e4e71e75faa, []int{7} +} +func (m *MsgCancelSwapResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgCancelSwapResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgCancelSwapResponse.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 *MsgCancelSwapResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgCancelSwapResponse.Merge(m, src) +} +func (m *MsgCancelSwapResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgCancelSwapResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgCancelSwapResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgCancelSwapResponse proto.InternalMessageInfo + +// MsgAssertSwapSettled fails if address still has a pending swap. +// +// x/group runs a proposal's messages in a cached context and reports success +// from MsgExec even when the inner message failed, so a settlement transaction +// carrying two MsgExec cannot rely on either reverting the other. A top-level +// message error does revert the transaction, which is what this provides. +type MsgAssertSwapSettled struct { + Sender string `protobuf:"bytes,1,opt,name=sender,proto3" json:"sender,omitempty"` + Address string `protobuf:"bytes,2,opt,name=address,proto3" json:"address,omitempty"` +} + +func (m *MsgAssertSwapSettled) Reset() { *m = MsgAssertSwapSettled{} } +func (m *MsgAssertSwapSettled) String() string { return proto.CompactTextString(m) } +func (*MsgAssertSwapSettled) ProtoMessage() {} +func (*MsgAssertSwapSettled) Descriptor() ([]byte, []int) { + return fileDescriptor_e6107e4e71e75faa, []int{8} +} +func (m *MsgAssertSwapSettled) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgAssertSwapSettled) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgAssertSwapSettled.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 *MsgAssertSwapSettled) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgAssertSwapSettled.Merge(m, src) +} +func (m *MsgAssertSwapSettled) XXX_Size() int { + return m.Size() +} +func (m *MsgAssertSwapSettled) XXX_DiscardUnknown() { + xxx_messageInfo_MsgAssertSwapSettled.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgAssertSwapSettled proto.InternalMessageInfo + +func (m *MsgAssertSwapSettled) GetSender() string { + if m != nil { + return m.Sender + } + return "" +} + +func (m *MsgAssertSwapSettled) GetAddress() string { + if m != nil { + return m.Address + } + return "" +} + +// MsgAssertSwapSettledResponse defines the response for the settlement assertion. +type MsgAssertSwapSettledResponse struct { +} + +func (m *MsgAssertSwapSettledResponse) Reset() { *m = MsgAssertSwapSettledResponse{} } +func (m *MsgAssertSwapSettledResponse) String() string { return proto.CompactTextString(m) } +func (*MsgAssertSwapSettledResponse) ProtoMessage() {} +func (*MsgAssertSwapSettledResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_e6107e4e71e75faa, []int{9} +} +func (m *MsgAssertSwapSettledResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgAssertSwapSettledResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgAssertSwapSettledResponse.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 *MsgAssertSwapSettledResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgAssertSwapSettledResponse.Merge(m, src) +} +func (m *MsgAssertSwapSettledResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgAssertSwapSettledResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgAssertSwapSettledResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgAssertSwapSettledResponse proto.InternalMessageInfo + +// MsgUpdateParams defines a message to update the module params. +type MsgUpdateParams struct { + // authority is the address that controls the module params (defaults to the + // gov module account) + Authority string `protobuf:"bytes,1,opt,name=authority,proto3" json:"authority,omitempty"` + // params defines the module parameters to update + Params Params `protobuf:"bytes,2,opt,name=params,proto3" json:"params"` +} + +func (m *MsgUpdateParams) Reset() { *m = MsgUpdateParams{} } +func (m *MsgUpdateParams) String() string { return proto.CompactTextString(m) } +func (*MsgUpdateParams) ProtoMessage() {} +func (*MsgUpdateParams) Descriptor() ([]byte, []int) { + return fileDescriptor_e6107e4e71e75faa, []int{10} +} +func (m *MsgUpdateParams) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgUpdateParams) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgUpdateParams.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 *MsgUpdateParams) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgUpdateParams.Merge(m, src) +} +func (m *MsgUpdateParams) XXX_Size() int { + return m.Size() +} +func (m *MsgUpdateParams) XXX_DiscardUnknown() { + xxx_messageInfo_MsgUpdateParams.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgUpdateParams proto.InternalMessageInfo + +func (m *MsgUpdateParams) GetAuthority() string { + if m != nil { + return m.Authority + } + return "" +} + +func (m *MsgUpdateParams) GetParams() Params { + if m != nil { + return m.Params + } + return Params{} +} + +// MsgUpdateParamsResponse defines the response for updating the module params. +type MsgUpdateParamsResponse struct { +} + +func (m *MsgUpdateParamsResponse) Reset() { *m = MsgUpdateParamsResponse{} } +func (m *MsgUpdateParamsResponse) String() string { return proto.CompactTextString(m) } +func (*MsgUpdateParamsResponse) ProtoMessage() {} +func (*MsgUpdateParamsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_e6107e4e71e75faa, []int{11} +} +func (m *MsgUpdateParamsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgUpdateParamsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgUpdateParamsResponse.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 *MsgUpdateParamsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgUpdateParamsResponse.Merge(m, src) +} +func (m *MsgUpdateParamsResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgUpdateParamsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgUpdateParamsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgUpdateParamsResponse proto.InternalMessageInfo + +func init() { + proto.RegisterType((*MsgSetPrice)(nil), "swap.MsgSetPrice") + proto.RegisterType((*MsgSetPriceResponse)(nil), "swap.MsgSetPriceResponse") + proto.RegisterType((*MsgSwap)(nil), "swap.MsgSwap") + proto.RegisterType((*MsgSwapResponse)(nil), "swap.MsgSwapResponse") + proto.RegisterType((*MsgPaySwap)(nil), "swap.MsgPaySwap") + proto.RegisterType((*MsgPaySwapResponse)(nil), "swap.MsgPaySwapResponse") + proto.RegisterType((*MsgCancelSwap)(nil), "swap.MsgCancelSwap") + proto.RegisterType((*MsgCancelSwapResponse)(nil), "swap.MsgCancelSwapResponse") + proto.RegisterType((*MsgAssertSwapSettled)(nil), "swap.MsgAssertSwapSettled") + proto.RegisterType((*MsgAssertSwapSettledResponse)(nil), "swap.MsgAssertSwapSettledResponse") + proto.RegisterType((*MsgUpdateParams)(nil), "swap.MsgUpdateParams") + proto.RegisterType((*MsgUpdateParamsResponse)(nil), "swap.MsgUpdateParamsResponse") +} + +func init() { proto.RegisterFile("swap/tx.proto", fileDescriptor_e6107e4e71e75faa) } + +var fileDescriptor_e6107e4e71e75faa = []byte{ + // 657 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x55, 0x4f, 0x4f, 0x13, 0x41, + 0x1c, 0xed, 0xda, 0xd2, 0xc2, 0x0f, 0x50, 0xbb, 0xb4, 0xa1, 0x5d, 0x64, 0x31, 0x3d, 0x99, 0x2a, + 0xbb, 0x82, 0xd1, 0x18, 0x0e, 0x26, 0x40, 0x13, 0x43, 0x62, 0x0d, 0xd9, 0xc6, 0x8b, 0x17, 0x32, + 0xec, 0x8e, 0x4b, 0x23, 0xbb, 0xb3, 0xd9, 0x19, 0x84, 0x9e, 0x34, 0x26, 0x9a, 0x78, 0xf3, 0xa3, + 0x70, 0xe0, 0x43, 0x70, 0x93, 0x70, 0x32, 0x1e, 0x88, 0x81, 0x03, 0x1f, 0xc2, 0x8b, 0x99, 0x9d, + 0xe9, 0x6c, 0x97, 0x22, 0x26, 0x1c, 0xf4, 0xd4, 0xce, 0x7b, 0xbf, 0x3f, 0xef, 0xf7, 0x66, 0x76, + 0x06, 0x26, 0xe9, 0x2e, 0x8a, 0x6c, 0xb6, 0x67, 0x45, 0x31, 0x61, 0x44, 0x2f, 0xf0, 0xa5, 0x51, + 0xf1, 0x89, 0x4f, 0x12, 0xc0, 0xe6, 0xff, 0x04, 0x67, 0xd4, 0x5d, 0x42, 0x03, 0x42, 0x37, 0x04, + 0x21, 0x16, 0x92, 0x9a, 0x16, 0x2b, 0x3b, 0xa0, 0xbe, 0xfd, 0x6e, 0x81, 0xff, 0x48, 0xa2, 0x9c, + 0x94, 0x8f, 0x50, 0x8c, 0x02, 0x19, 0xdb, 0xf8, 0xa6, 0xc1, 0x78, 0x9b, 0xfa, 0x1d, 0xcc, 0xd6, + 0xe3, 0xae, 0x8b, 0xf5, 0x87, 0x50, 0xa4, 0x38, 0xf4, 0x70, 0x5c, 0xd3, 0xee, 0x6a, 0xf7, 0xc6, + 0x56, 0x6a, 0xc7, 0x07, 0xf3, 0x15, 0x59, 0x7d, 0xd9, 0xf3, 0x62, 0x4c, 0x69, 0x87, 0xc5, 0xdd, + 0xd0, 0x77, 0x64, 0x9c, 0x3e, 0x0b, 0xf0, 0x26, 0x26, 0xc1, 0x86, 0x87, 0x43, 0x12, 0xd4, 0x6e, + 0xf0, 0x2c, 0x67, 0x8c, 0x23, 0x2d, 0x0e, 0xe8, 0x75, 0x18, 0x65, 0x44, 0x92, 0xf9, 0x84, 0x2c, + 0x31, 0x22, 0xa8, 0xe7, 0x30, 0x12, 0xf1, 0xa6, 0xb5, 0x42, 0xd2, 0x6a, 0xe1, 0xf0, 0x64, 0x2e, + 0xf7, 0xe3, 0x64, 0x6e, 0x46, 0xb4, 0xa3, 0xde, 0x5b, 0xab, 0x4b, 0xec, 0x00, 0xb1, 0x2d, 0xeb, + 0x05, 0xf6, 0x91, 0xdb, 0x6b, 0x61, 0xf7, 0xf8, 0x60, 0x1e, 0xa4, 0x9a, 0x16, 0x76, 0x1d, 0x91, + 0xbf, 0x34, 0xfe, 0xf1, 0x7c, 0xbf, 0x29, 0xf5, 0x34, 0xaa, 0x30, 0x35, 0x30, 0x90, 0x83, 0x69, + 0x44, 0x42, 0x8a, 0x1b, 0xbf, 0x34, 0x28, 0x71, 0x7c, 0x17, 0x45, 0xd7, 0x18, 0x72, 0x11, 0x4a, + 0x48, 0x10, 0x62, 0xc2, 0x2b, 0x52, 0xfa, 0x81, 0x17, 0x8c, 0xc9, 0x5f, 0x65, 0x4c, 0x21, 0x6b, + 0xcc, 0x2a, 0x14, 0x51, 0x40, 0x76, 0x42, 0x56, 0x1b, 0x49, 0x9a, 0xdd, 0x97, 0xce, 0x54, 0x87, + 0x9d, 0x59, 0x0b, 0xd9, 0x80, 0x27, 0x6b, 0x21, 0x73, 0x64, 0x6a, 0xd6, 0x94, 0x32, 0xdc, 0x92, + 0xc3, 0x2b, 0x43, 0xde, 0x03, 0xb4, 0xa9, 0xbf, 0x8e, 0x7a, 0xff, 0xce, 0x92, 0xac, 0xa6, 0x0a, + 0xe8, 0xa9, 0x00, 0x25, 0xeb, 0x25, 0x4c, 0xb6, 0xa9, 0xbf, 0x8a, 0x42, 0x17, 0x6f, 0x5f, 0x4f, + 0x59, 0xb6, 0xcb, 0x34, 0x54, 0x33, 0xf5, 0x54, 0xa3, 0x2f, 0x1a, 0x54, 0xda, 0xd4, 0x5f, 0xa6, + 0x14, 0xc7, 0x8c, 0x33, 0x1d, 0xcc, 0xd8, 0x36, 0xf6, 0xfe, 0x87, 0x15, 0x26, 0xdc, 0xb9, 0x4c, + 0x8a, 0xd2, 0xfa, 0x49, 0x4b, 0xf6, 0xef, 0x55, 0xe4, 0x21, 0x86, 0xd7, 0x93, 0xef, 0x57, 0x7f, + 0x02, 0x63, 0x68, 0x87, 0x6d, 0x91, 0xb8, 0xcb, 0x7a, 0x7f, 0x55, 0x9a, 0x86, 0xea, 0x4d, 0x28, + 0x8a, 0x1b, 0x20, 0xd1, 0x3a, 0xbe, 0x38, 0x61, 0xf1, 0x5b, 0xc1, 0x12, 0x55, 0x57, 0x0a, 0xfc, + 0xa8, 0x39, 0x32, 0x62, 0xe9, 0x26, 0x17, 0x99, 0xe6, 0x36, 0xea, 0x30, 0x7d, 0x41, 0x46, 0x5f, + 0xe2, 0xe2, 0xe7, 0x3c, 0xe4, 0xdb, 0xd4, 0xd7, 0x9f, 0xc2, 0xa8, 0xba, 0x4c, 0xca, 0xa2, 0xf4, + 0xc0, 0xe7, 0x68, 0xd4, 0x87, 0xa0, 0x7e, 0x05, 0xfd, 0x01, 0x14, 0x92, 0x0d, 0x9f, 0x4c, 0x43, + 0xf8, 0xf5, 0x57, 0xcd, 0x2c, 0x55, 0xf4, 0x63, 0x28, 0xf5, 0xcf, 0xee, 0x6d, 0x15, 0x21, 0x11, + 0xa3, 0x76, 0x11, 0x51, 0x69, 0xcf, 0x00, 0x06, 0xce, 0xd6, 0x94, 0x8a, 0x4b, 0x41, 0x63, 0xe6, + 0x12, 0x50, 0xe5, 0x77, 0xa0, 0x3c, 0x7c, 0x62, 0x0c, 0x95, 0x31, 0xc4, 0x19, 0x8d, 0x3f, 0x73, + 0xaa, 0x68, 0x0b, 0x26, 0x32, 0x5b, 0x9b, 0x8e, 0x3c, 0x08, 0x1b, 0xb3, 0x97, 0xc2, 0xfd, 0x2a, + 0xc6, 0xc8, 0x87, 0xf3, 0xfd, 0xa6, 0xb6, 0xd2, 0x3a, 0x3c, 0x35, 0xb5, 0xa3, 0x53, 0x53, 0xfb, + 0x79, 0x6a, 0x6a, 0x5f, 0xcf, 0xcc, 0xdc, 0xd1, 0x99, 0x99, 0xfb, 0x7e, 0x66, 0xe6, 0x5e, 0x37, + 0xfd, 0x2e, 0xdb, 0xda, 0xd9, 0xb4, 0x5c, 0x12, 0xd8, 0x11, 0xc6, 0x31, 0xed, 0x51, 0x66, 0xbb, + 0x9b, 0x9e, 0x3b, 0x1f, 0x12, 0x0f, 0xdb, 0x7b, 0xb6, 0x78, 0x7d, 0x7a, 0x11, 0xa6, 0x9b, 0xc5, + 0xe4, 0x79, 0x78, 0xf4, 0x3b, 0x00, 0x00, 0xff, 0xff, 0x45, 0x40, 0x5e, 0x8e, 0x92, 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 { + // Sets the exchange rate for an ordered denom pair, overwriting any rate + // already stored for that pair. + SetPrice(ctx context.Context, in *MsgSetPrice, opts ...grpc.CallOption) (*MsgSetPriceResponse, error) + // Escrows the incoming leg of an exchange and records it as pending. + Swap(ctx context.Context, in *MsgSwap, opts ...grpc.CallOption) (*MsgSwapResponse, error) + // Pays out the other leg and consumes the pending entry. Only the configured + // exchange address may call it. + PaySwap(ctx context.Context, in *MsgPaySwap, opts ...grpc.CallOption) (*MsgPaySwapResponse, error) + // Refunds an escrowed swap that was never paid out. + CancelSwap(ctx context.Context, in *MsgCancelSwap, opts ...grpc.CallOption) (*MsgCancelSwapResponse, error) + // Fails if the address still has a pending swap. Carried as the last message + // of a settlement transaction so a silently unsettled leg reverts the whole tx. + AssertSwapSettled(ctx context.Context, in *MsgAssertSwapSettled, opts ...grpc.CallOption) (*MsgAssertSwapSettledResponse, error) + // Updates the module params. Only the governance authority can call it. + UpdateParams(ctx context.Context, in *MsgUpdateParams, opts ...grpc.CallOption) (*MsgUpdateParamsResponse, error) +} + +type msgClient struct { + cc grpc1.ClientConn +} + +func NewMsgClient(cc grpc1.ClientConn) MsgClient { + return &msgClient{cc} +} + +func (c *msgClient) SetPrice(ctx context.Context, in *MsgSetPrice, opts ...grpc.CallOption) (*MsgSetPriceResponse, error) { + out := new(MsgSetPriceResponse) + err := c.cc.Invoke(ctx, "/swap.Msg/SetPrice", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) Swap(ctx context.Context, in *MsgSwap, opts ...grpc.CallOption) (*MsgSwapResponse, error) { + out := new(MsgSwapResponse) + err := c.cc.Invoke(ctx, "/swap.Msg/Swap", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) PaySwap(ctx context.Context, in *MsgPaySwap, opts ...grpc.CallOption) (*MsgPaySwapResponse, error) { + out := new(MsgPaySwapResponse) + err := c.cc.Invoke(ctx, "/swap.Msg/PaySwap", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) CancelSwap(ctx context.Context, in *MsgCancelSwap, opts ...grpc.CallOption) (*MsgCancelSwapResponse, error) { + out := new(MsgCancelSwapResponse) + err := c.cc.Invoke(ctx, "/swap.Msg/CancelSwap", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) AssertSwapSettled(ctx context.Context, in *MsgAssertSwapSettled, opts ...grpc.CallOption) (*MsgAssertSwapSettledResponse, error) { + out := new(MsgAssertSwapSettledResponse) + err := c.cc.Invoke(ctx, "/swap.Msg/AssertSwapSettled", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) UpdateParams(ctx context.Context, in *MsgUpdateParams, opts ...grpc.CallOption) (*MsgUpdateParamsResponse, error) { + out := new(MsgUpdateParamsResponse) + err := c.cc.Invoke(ctx, "/swap.Msg/UpdateParams", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// MsgServer is the server API for Msg service. +type MsgServer interface { + // Sets the exchange rate for an ordered denom pair, overwriting any rate + // already stored for that pair. + SetPrice(context.Context, *MsgSetPrice) (*MsgSetPriceResponse, error) + // Escrows the incoming leg of an exchange and records it as pending. + Swap(context.Context, *MsgSwap) (*MsgSwapResponse, error) + // Pays out the other leg and consumes the pending entry. Only the configured + // exchange address may call it. + PaySwap(context.Context, *MsgPaySwap) (*MsgPaySwapResponse, error) + // Refunds an escrowed swap that was never paid out. + CancelSwap(context.Context, *MsgCancelSwap) (*MsgCancelSwapResponse, error) + // Fails if the address still has a pending swap. Carried as the last message + // of a settlement transaction so a silently unsettled leg reverts the whole tx. + AssertSwapSettled(context.Context, *MsgAssertSwapSettled) (*MsgAssertSwapSettledResponse, error) + // Updates the module params. Only the governance authority can call it. + UpdateParams(context.Context, *MsgUpdateParams) (*MsgUpdateParamsResponse, error) +} + +// UnimplementedMsgServer can be embedded to have forward compatible implementations. +type UnimplementedMsgServer struct { +} + +func (*UnimplementedMsgServer) SetPrice(ctx context.Context, req *MsgSetPrice) (*MsgSetPriceResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method SetPrice not implemented") +} +func (*UnimplementedMsgServer) Swap(ctx context.Context, req *MsgSwap) (*MsgSwapResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Swap not implemented") +} +func (*UnimplementedMsgServer) PaySwap(ctx context.Context, req *MsgPaySwap) (*MsgPaySwapResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method PaySwap not implemented") +} +func (*UnimplementedMsgServer) CancelSwap(ctx context.Context, req *MsgCancelSwap) (*MsgCancelSwapResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method CancelSwap not implemented") +} +func (*UnimplementedMsgServer) AssertSwapSettled(ctx context.Context, req *MsgAssertSwapSettled) (*MsgAssertSwapSettledResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method AssertSwapSettled not implemented") +} +func (*UnimplementedMsgServer) UpdateParams(ctx context.Context, req *MsgUpdateParams) (*MsgUpdateParamsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method UpdateParams not implemented") +} + +func RegisterMsgServer(s grpc1.Server, srv MsgServer) { + s.RegisterService(&_Msg_serviceDesc, srv) +} + +func _Msg_SetPrice_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgSetPrice) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).SetPrice(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/swap.Msg/SetPrice", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).SetPrice(ctx, req.(*MsgSetPrice)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_Swap_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgSwap) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).Swap(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/swap.Msg/Swap", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).Swap(ctx, req.(*MsgSwap)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_PaySwap_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgPaySwap) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).PaySwap(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/swap.Msg/PaySwap", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).PaySwap(ctx, req.(*MsgPaySwap)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_CancelSwap_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgCancelSwap) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).CancelSwap(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/swap.Msg/CancelSwap", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).CancelSwap(ctx, req.(*MsgCancelSwap)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_AssertSwapSettled_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgAssertSwapSettled) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).AssertSwapSettled(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/swap.Msg/AssertSwapSettled", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).AssertSwapSettled(ctx, req.(*MsgAssertSwapSettled)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_UpdateParams_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgUpdateParams) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).UpdateParams(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/swap.Msg/UpdateParams", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).UpdateParams(ctx, req.(*MsgUpdateParams)) + } + return interceptor(ctx, in, info, handler) +} + +var Msg_serviceDesc = _Msg_serviceDesc +var _Msg_serviceDesc = grpc.ServiceDesc{ + ServiceName: "swap.Msg", + HandlerType: (*MsgServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "SetPrice", + Handler: _Msg_SetPrice_Handler, + }, + { + MethodName: "Swap", + Handler: _Msg_Swap_Handler, + }, + { + MethodName: "PaySwap", + Handler: _Msg_PaySwap_Handler, + }, + { + MethodName: "CancelSwap", + Handler: _Msg_CancelSwap_Handler, + }, + { + MethodName: "AssertSwapSettled", + Handler: _Msg_AssertSwapSettled_Handler, + }, + { + MethodName: "UpdateParams", + Handler: _Msg_UpdateParams_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "swap/tx.proto", +} + +func (m *MsgSetPrice) 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 *MsgSetPrice) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgSetPrice) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size := m.Price.Size() + i -= size + if _, err := m.Price.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + if len(m.ToDenom) > 0 { + i -= len(m.ToDenom) + copy(dAtA[i:], m.ToDenom) + i = encodeVarintTx(dAtA, i, uint64(len(m.ToDenom))) + i-- + dAtA[i] = 0x1a + } + if len(m.FromDenom) > 0 { + i -= len(m.FromDenom) + copy(dAtA[i:], m.FromDenom) + i = encodeVarintTx(dAtA, i, uint64(len(m.FromDenom))) + 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 *MsgSetPriceResponse) 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 *MsgSetPriceResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgSetPriceResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgSwap) 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 *MsgSwap) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgSwap) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size := m.Amount.Size() + i -= size + if _, err := m.Amount.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + if len(m.ToDenom) > 0 { + i -= len(m.ToDenom) + copy(dAtA[i:], m.ToDenom) + i = encodeVarintTx(dAtA, i, uint64(len(m.ToDenom))) + i-- + dAtA[i] = 0x22 + } + if len(m.FromDenom) > 0 { + i -= len(m.FromDenom) + copy(dAtA[i:], m.FromDenom) + i = encodeVarintTx(dAtA, i, uint64(len(m.FromDenom))) + i-- + dAtA[i] = 0x1a + } + if len(m.Address) > 0 { + i -= len(m.Address) + copy(dAtA[i:], m.Address) + i = encodeVarintTx(dAtA, i, uint64(len(m.Address))) + 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 *MsgSwapResponse) 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 *MsgSwapResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgSwapResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgPaySwap) 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 *MsgPaySwap) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgPaySwap) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Address) > 0 { + i -= len(m.Address) + copy(dAtA[i:], m.Address) + i = encodeVarintTx(dAtA, i, uint64(len(m.Address))) + 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 *MsgPaySwapResponse) 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 *MsgPaySwapResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgPaySwapResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgCancelSwap) 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 *MsgCancelSwap) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgCancelSwap) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + 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 *MsgCancelSwapResponse) 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 *MsgCancelSwapResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgCancelSwapResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgAssertSwapSettled) 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 *MsgAssertSwapSettled) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgAssertSwapSettled) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Address) > 0 { + i -= len(m.Address) + copy(dAtA[i:], m.Address) + i = encodeVarintTx(dAtA, i, uint64(len(m.Address))) + 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 *MsgAssertSwapSettledResponse) 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 *MsgAssertSwapSettledResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgAssertSwapSettledResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgUpdateParams) 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 *MsgUpdateParams) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgUpdateParams) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Params.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + if len(m.Authority) > 0 { + i -= len(m.Authority) + copy(dAtA[i:], m.Authority) + i = encodeVarintTx(dAtA, i, uint64(len(m.Authority))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgUpdateParamsResponse) 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 *MsgUpdateParamsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgUpdateParamsResponse) 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 *MsgSetPrice) 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)) + } + l = len(m.FromDenom) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.ToDenom) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = m.Price.Size() + n += 1 + l + sovTx(uint64(l)) + return n +} + +func (m *MsgSetPriceResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgSwap) 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)) + } + l = len(m.Address) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.FromDenom) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.ToDenom) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = m.Amount.Size() + n += 1 + l + sovTx(uint64(l)) + return n +} + +func (m *MsgSwapResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgPaySwap) 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)) + } + l = len(m.Address) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgPaySwapResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgCancelSwap) 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)) + } + return n +} + +func (m *MsgCancelSwapResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgAssertSwapSettled) 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)) + } + l = len(m.Address) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgAssertSwapSettledResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgUpdateParams) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Authority) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = m.Params.Size() + n += 1 + l + sovTx(uint64(l)) + return n +} + +func (m *MsgUpdateParamsResponse) 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 *MsgSetPrice) 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: MsgSetPrice: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgSetPrice: 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 FromDenom", 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.FromDenom = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ToDenom", 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.ToDenom = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Price", 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.Price.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 *MsgSetPriceResponse) 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: MsgSetPriceResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgSetPriceResponse: 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 (m *MsgSwap) 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: MsgSwap: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgSwap: 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 Address", 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.Address = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FromDenom", 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.FromDenom = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ToDenom", 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.ToDenom = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Amount", 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.Amount.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 *MsgSwapResponse) 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: MsgSwapResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgSwapResponse: 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 (m *MsgPaySwap) 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: MsgPaySwap: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgPaySwap: 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 Address", 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.Address = 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 *MsgPaySwapResponse) 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: MsgPaySwapResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgPaySwapResponse: 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 (m *MsgCancelSwap) 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: MsgCancelSwap: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgCancelSwap: 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 + 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 *MsgCancelSwapResponse) 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: MsgCancelSwapResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgCancelSwapResponse: 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 (m *MsgAssertSwapSettled) 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: MsgAssertSwapSettled: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgAssertSwapSettled: 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 Address", 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.Address = 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 *MsgAssertSwapSettledResponse) 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: MsgAssertSwapSettledResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgAssertSwapSettledResponse: 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 (m *MsgUpdateParams) 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: MsgUpdateParams: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgUpdateParams: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Authority", 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.Authority = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + 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 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.Params.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 *MsgUpdateParamsResponse) 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: MsgUpdateParamsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgUpdateParamsResponse: 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") +) From 1482fe64cbf16f56506fcf8c902551c38963ba8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Luque?= Date: Mon, 24 Aug 2026 14:32:21 +0200 Subject: [PATCH 2/2] test(swap): assert the derived reverse rate, not just that it settles --- tests/integration/swap_test.go | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/tests/integration/swap_test.go b/tests/integration/swap_test.go index c315aed..32da5e9 100644 --- a/tests/integration/swap_test.go +++ b/tests/integration/swap_test.go @@ -257,26 +257,31 @@ func (s *SwapTestSuite) TestSwapWithoutPriceFails() { require.Equal(s.T(), before, s.balance(key.AccAddr, foreignDenom), "balance must be untouched") } -// One rate per pair: quoting ahnl -> uusd makes uusd -> ahnl exchangeable at -// 1/rate, so a single proposal covers both directions. +// One rate per pair: the opposite direction is derived as 1/rate at settlement. +// +// Settles INTO the foreign denom on purpose. Gas is paid in app.BaseDenom and +// dwarfs a payout of this size, so the native balance cannot carry an exact +// assertion — the foreign denom is untouched by fees and can. func (s *SwapTestSuite) TestSwapDerivesReversePrice() { key := s.keyring.GetKey(2) - before := s.balance(key.AccAddr, foreignDenom) - // Rate is set ahnl -> uusd; the swap is uusd -> ahnl. - require.NoError(s.T(), s.setPrice(key, app.BaseDenom, foreignDenom, "25")) + // Quoted uusd -> ahnl; the swap is the reverse, ahnl -> uusd, so the rate + // used is 1/0.04 = 25. + require.NoError(s.T(), s.setPrice(key, foreignDenom, app.BaseDenom, "0.04")) - amount := sdkmath.NewInt(1_000_000) - require.NoError(s.T(), s.escrow(key, foreignDenom, app.BaseDenom, amount)) - require.Equal(s.T(), before.Sub(amount), s.balance(key.AccAddr, foreignDenom)) + amount := sdkmath.NewInt(1_000) + foreignBefore := s.balance(key.AccAddr, foreignDenom) + require.NoError(s.T(), s.escrow(key, app.BaseDenom, foreignDenom, amount)) pay := swaptypes.NewMsgPaySwap(s.exchangeStr, key.AccAddr.String()) _, err := s.factory.CommitCosmosTx(s.exchange.Priv, factory.CosmosTxArgs{Msgs: []sdk.Msg{pay}}) require.NoError(s.T(), err) require.NoError(s.T(), s.network.NextBlock()) - // 1/25 = 0.04, so 1e6 uusd settles at 40000 ahnl. - require.NoError(s.T(), s.network.NextBlock()) + // 1/0.04 = 25, so 1000 ahnl settles at exactly 25000 uusd. Asserting the + // figure is the point: without it the derivation could return anything. + expected := sdkmath.NewInt(25_000) + require.Equal(s.T(), foreignBefore.Add(expected), s.balance(key.AccAddr, foreignDenom)) } // An account cannot swap somebody else's funds.