diff --git a/Makefile b/Makefile index 4a503572e..8e6f14a4d 100644 --- a/Makefile +++ b/Makefile @@ -18,6 +18,7 @@ DEPS_IBC_GO_VERSION := $(shell cat go.sum | grep 'github.com/cosmos/ibc-go' | gr DEPS_COSMOS_PROTO_VERSION := $(shell cat go.sum | grep 'github.com/cosmos/cosmos-proto' | grep -v -e 'go.mod' | tail -n 1 | awk '{ print $$2; }') DEPS_COSMOS_GOGOPROTO_VERSION := $(shell cat go.sum | grep 'github.com/cosmos/gogoproto' | grep -v -e 'go.mod' | tail -n 1 | awk '{ print $$2; }') DEPS_CONFIO_ICS23_VERSION := go/$(shell cat go.sum | grep 'github.com/confio/ics23/go' | grep -v -e 'go.mod' | tail -n 1 | awk '{ print $$2; }') +DEPS_COSMOS_ICS23 := go/$(shell cat go.sum | grep 'github.com/cosmos/ics23/go' | grep -v -e 'go.mod' | tail -n 1 | awk '{ print $$2; }') export GO111MODULE = on @@ -174,6 +175,8 @@ proto-gen: @go mod tidy proto-swagger-gen: + @echo "Downloading Protobuf dependencies" + @make proto-download-deps @echo "Generating Protobuf Swagger" $(protoCosmosImage) sh ./scripts/protoc-swagger-gen.sh @@ -257,4 +260,7 @@ proto-download-deps: mkdir -p "$(THIRD_PARTY_DIR)/confio/ics23" && \ curl -sSL https://raw.githubusercontent.com/confio/ics23/$(DEPS_CONFIO_ICS23_VERSION)/proofs.proto > "$(THIRD_PARTY_DIR)/proofs.proto" -.PHONY: proto-gen proto-swagger-gen proto-format proto-lint proto-download-deps + mkdir -p "$(THIRD_PARTY_DIR)/cosmos/ics23/v1" && \ + curl -sSL "https://raw.githubusercontent.com/cosmos/ics23/$(DEPS_COSMOS_ICS23)/proto/cosmos/ics23/v1/proofs.proto" > "$(THIRD_PARTY_DIR)/cosmos/ics23/v1/proofs.proto" + +.PHONY: proto-gen proto-swagger-gen proto-format proto-lint proto-download-deps \ No newline at end of file diff --git a/app/apptesting/test_suite.go b/app/apptesting/test_suite.go index 229500386..851ecee8a 100644 --- a/app/apptesting/test_suite.go +++ b/app/apptesting/test_suite.go @@ -3,6 +3,10 @@ package apptesting import ( "strings" + bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" + "github.com/dymensionxyz/dymension/v3/app/params" + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" + "github.com/cometbft/cometbft/libs/rand" codectypes "github.com/cosmos/cosmos-sdk/codec/types" "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" @@ -44,14 +48,13 @@ func (s *KeeperTestHelper) CreateDefaultRollapp() string { } func (s *KeeperTestHelper) CreateRollappByName(name string) { - alias := strings.NewReplacer("_", "", "-", "").Replace(name) // base it on rollappID to avoid alias conflicts msgCreateRollapp := rollapptypes.MsgCreateRollapp{ Creator: alice, RollappId: name, InitialSequencer: "*", Bech32Prefix: strings.ToLower(rand.Str(3)), GenesisChecksum: "1234567890abcdefg", - Alias: alias, + Alias: strings.ToLower(rand.Str(7)), VmType: rollapptypes.Rollapp_EVM, Metadata: &rollapptypes.RollappMetadata{ Website: "https://dymension.xyz", @@ -63,6 +66,8 @@ func (s *KeeperTestHelper) CreateRollappByName(name string) { }, } + s.FundForAliasRegistration(msgCreateRollapp) + msgServer := rollappkeeper.NewMsgServerImpl(*s.App.RollappKeeper) _, err := msgServer.CreateRollapp(s.Ctx, &msgCreateRollapp) s.Require().NoError(err) @@ -139,3 +144,25 @@ func (s *KeeperTestHelper) StateNotAltered() { newState := s.App.ExportState(s.Ctx) s.Require().Equal(oldState, newState) } + +func (s *KeeperTestHelper) FundForAliasRegistration(msgCreateRollApp rollapptypes.MsgCreateRollapp) { + err := FundForAliasRegistration(s.Ctx, s.App.BankKeeper, msgCreateRollApp) + s.Require().NoError(err) +} + +func FundForAliasRegistration( + ctx sdk.Context, + bankKeeper bankkeeper.Keeper, + msgCreateRollApp rollapptypes.MsgCreateRollapp, +) error { + if msgCreateRollApp.Alias == "" { + return nil + } + dymNsParams := dymnstypes.DefaultPriceParams() + aliasRegistrationCost := sdk.NewCoins(sdk.NewCoin( + params.BaseDenom, dymNsParams.GetAliasPrice(msgCreateRollApp.Alias), + )) + return bankutil.FundAccount( + bankKeeper, ctx, sdk.MustAccAddressFromBech32(msgCreateRollApp.Creator), aliasRegistrationCost, + ) +} diff --git a/app/keepers/keepers.go b/app/keepers/keepers.go index cc1f35fb7..012ad2357 100644 --- a/app/keepers/keepers.go +++ b/app/keepers/keepers.go @@ -77,6 +77,9 @@ import ( denommetadatamodule "github.com/dymensionxyz/dymension/v3/x/denommetadata" denommetadatamodulekeeper "github.com/dymensionxyz/dymension/v3/x/denommetadata/keeper" denommetadatamoduletypes "github.com/dymensionxyz/dymension/v3/x/denommetadata/types" + dymnsmodule "github.com/dymensionxyz/dymension/v3/x/dymns" + dymnskeeper "github.com/dymensionxyz/dymension/v3/x/dymns/keeper" + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" eibckeeper "github.com/dymensionxyz/dymension/v3/x/eibc/keeper" eibcmoduletypes "github.com/dymensionxyz/dymension/v3/x/eibc/types" incentiveskeeper "github.com/dymensionxyz/dymension/v3/x/incentives/keeper" @@ -144,6 +147,8 @@ type AppKeepers struct { DelayedAckKeeper delayedackkeeper.Keeper DenomMetadataKeeper *denommetadatamodulekeeper.Keeper + DymNSKeeper dymnskeeper.Keeper + // keys to access the substores keys map[string]*storetypes.KVStoreKey tkeys map[string]*storetypes.TransientStoreKey @@ -397,6 +402,15 @@ func (a *AppKeepers) InitKeepers( nil, ) + a.DymNSKeeper = dymnskeeper.NewKeeper( + appCodec, + a.keys[dymnstypes.StoreKey], + a.GetSubspace(dymnstypes.ModuleName), + a.BankKeeper, + a.RollappKeeper, + authtypes.NewModuleAddress(govtypes.ModuleName).String(), + ) + // Create Transfer Keepers a.TransferKeeper = ibctransferkeeper.NewKeeper( appCodec, @@ -434,6 +448,7 @@ func (a *AppKeepers) InitKeepers( AddRoute(streamermoduletypes.RouterKey, streamermodule.NewStreamerProposalHandler(a.StreamerKeeper)). AddRoute(rollappmoduletypes.RouterKey, rollappmodule.NewRollappProposalHandler(a.RollappKeeper)). AddRoute(denommetadatamoduletypes.RouterKey, denommetadatamodule.NewDenomMetadataProposalHandler(a.DenomMetadataKeeper)). + AddRoute(dymnstypes.RouterKey, dymnsmodule.NewDymNsProposalHandler(a.DymNSKeeper)). AddRoute(evmtypes.RouterKey, evm.NewEvmProposalHandler(a.EvmKeeper)) // Create evidence Keeper for to register the IBC light client misbehaviour evidence route @@ -542,6 +557,7 @@ func (a *AppKeepers) SetupHooks() { a.StreamerKeeper.Hooks(), a.TxFeesKeeper.Hooks(), a.DelayedAckKeeper.GetEpochHooks(), + a.DymNSKeeper.GetEpochHooks(), ), ) @@ -558,6 +574,7 @@ func (a *AppKeepers) SetupHooks() { a.SequencerKeeper.RollappHooks(), a.delayedAckMiddleware, a.StreamerKeeper.Hooks(), + a.DymNSKeeper.GetRollAppHooks(), )) } @@ -598,6 +615,7 @@ func initParamsKeeper(appCodec codec.BinaryCodec, legacyAmino *codec.LegacyAmino paramsKeeper.Subspace(denommetadatamoduletypes.ModuleName) paramsKeeper.Subspace(delayedacktypes.ModuleName) paramsKeeper.Subspace(eibcmoduletypes.ModuleName) + paramsKeeper.Subspace(dymnstypes.ModuleName) // ethermint subspaces paramsKeeper.Subspace(evmtypes.ModuleName) diff --git a/app/keepers/keys.go b/app/keepers/keys.go index f499487e7..723c3e392 100644 --- a/app/keepers/keys.go +++ b/app/keepers/keys.go @@ -21,6 +21,7 @@ import ( packetforwardtypes "github.com/cosmos/ibc-apps/middleware/packet-forward-middleware/v7/packetforward/types" ibctransfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" ibcexported "github.com/cosmos/ibc-go/v7/modules/core/exported" + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" evmtypes "github.com/evmos/ethermint/x/evm/types" feemarkettypes "github.com/evmos/ethermint/x/feemarket/types" epochstypes "github.com/osmosis-labs/osmosis/v15/x/epochs/types" @@ -118,6 +119,7 @@ var KVStoreKeys = sdk.NewKVStoreKeys( packetforwardtypes.StoreKey, delayedacktypes.StoreKey, eibcmoduletypes.StoreKey, + dymnstypes.StoreKey, // ethermint keys evmtypes.StoreKey, feemarkettypes.StoreKey, diff --git a/app/keepers/modules.go b/app/keepers/modules.go index c95d96acd..6957da3d1 100644 --- a/app/keepers/modules.go +++ b/app/keepers/modules.go @@ -52,6 +52,9 @@ import ( ibcclientclient "github.com/cosmos/ibc-go/v7/modules/core/02-client/client" ibcexported "github.com/cosmos/ibc-go/v7/modules/core/exported" ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" + dymnsmodule "github.com/dymensionxyz/dymension/v3/x/dymns" + dymnsmoduleclient "github.com/dymensionxyz/dymension/v3/x/dymns/client" + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" "github.com/evmos/ethermint/x/evm" evmclient "github.com/evmos/ethermint/x/evm/client" evmtypes "github.com/evmos/ethermint/x/evm/types" @@ -124,6 +127,8 @@ var ModuleBasics = module.NewBasicManager( rollappmoduleclient.SubmitFraudHandler, denommetadatamoduleclient.CreateDenomMetadataHandler, denommetadatamoduleclient.UpdateDenomMetadataHandler, + dymnsmoduleclient.MigrateChainIdsProposalHandler, + dymnsmoduleclient.UpdateAliasesProposalHandler, evmclient.UpdateVirtualFrontierBankContractProposalHandler, }), params.AppModuleBasic{}, @@ -144,6 +149,7 @@ var ModuleBasics = module.NewBasicManager( packetforward.AppModuleBasic{}, delayedack.AppModuleBasic{}, eibc.AppModuleBasic{}, + dymnsmodule.AppModuleBasic{}, // Ethermint modules evm.AppModuleBasic{}, @@ -195,6 +201,7 @@ func (a *AppKeepers) SetupModules( delayedackmodule.NewAppModule(appCodec, a.DelayedAckKeeper), denommetadatamodule.NewAppModule(a.DenomMetadataKeeper, *a.EvmKeeper, a.BankKeeper), eibcmodule.NewAppModule(appCodec, a.EIBCKeeper, a.AccountKeeper, a.BankKeeper), + dymnsmodule.NewAppModule(appCodec, a.DymNSKeeper), // Ethermint app modules evm.NewAppModule(a.EvmKeeper, a.AccountKeeper, a.BankKeeper, a.GetSubspace(evmtypes.ModuleName).WithKeyTable(evmtypes.ParamKeyTable())), @@ -242,6 +249,7 @@ var maccPerms = map[string][]string{ lockuptypes.ModuleName: {authtypes.Minter, authtypes.Burner}, incentivestypes.ModuleName: {authtypes.Minter, authtypes.Burner}, txfeestypes.ModuleName: {authtypes.Burner}, + dymnstypes.ModuleName: {authtypes.Minter, authtypes.Burner}, } var BeginBlockers = []string{ @@ -274,6 +282,7 @@ var BeginBlockers = []string{ denommetadatamoduletypes.ModuleName, delayedacktypes.ModuleName, eibcmoduletypes.ModuleName, + dymnstypes.ModuleName, lockuptypes.ModuleName, gammtypes.ModuleName, poolmanagertypes.ModuleName, @@ -311,6 +320,7 @@ var EndBlockers = []string{ denommetadatamoduletypes.ModuleName, delayedacktypes.ModuleName, eibcmoduletypes.ModuleName, + dymnstypes.ModuleName, epochstypes.ModuleName, lockuptypes.ModuleName, gammtypes.ModuleName, @@ -349,6 +359,7 @@ var InitGenesis = []string{ denommetadatamoduletypes.ModuleName, // must after `x/bank` to trigger hooks delayedacktypes.ModuleName, eibcmoduletypes.ModuleName, + dymnstypes.ModuleName, epochstypes.ModuleName, lockuptypes.ModuleName, gammtypes.ModuleName, diff --git a/docs/config.json b/docs/config.json index 005d5e46a..e62012d0a 100644 --- a/docs/config.json +++ b/docs/config.json @@ -7,7 +7,7 @@ }, "apis": [ { - "url": "./tmp-swagger-gen/dymension/delayedack/query.swagger.json", + "url": "./tmp-swagger-gen/dymensionxyz/dymension/delayedack/query.swagger.json", "operationIds": { "rename": { "Params": "DelayedAckParams" @@ -15,7 +15,7 @@ } }, { - "url": "./tmp-swagger-gen/dymension/eibc/query.swagger.json", + "url": "./tmp-swagger-gen/dymensionxyz/dymension/eibc/query.swagger.json", "operationIds": { "rename": { "Params": "EIbcParams" @@ -23,7 +23,15 @@ } }, { - "url": "./tmp-swagger-gen/dymension/rollapp/query.swagger.json", + "url": "./tmp-swagger-gen/dymensionxyz/dymension/dymns/query.swagger.json", + "operationIds": { + "rename": { + "Params": "DymNSParams" + } + } + }, + { + "url": "./tmp-swagger-gen/dymensionxyz/dymension/rollapp/query.swagger.json", "operationIds": { "rename": { "Params": "RollAppParams" @@ -31,7 +39,7 @@ } }, { - "url": "./tmp-swagger-gen/dymension/sequencer/query.swagger.json", + "url": "./tmp-swagger-gen/dymensionxyz/dymension/sequencer/query.swagger.json", "operationIds": { "rename": { "Params": "SequencerParams" @@ -39,7 +47,7 @@ } }, { - "url": "./tmp-swagger-gen/dymension/streamer/query.swagger.json", + "url": "./tmp-swagger-gen/dymensionxyz/dymension/streamer/query.swagger.json", "operationIds": { "rename": { "Params": "StreamerParams", @@ -65,7 +73,7 @@ } }, { - "url": "./tmp-swagger-gen/osmosis/gamm/v2/query.swagger.json", + "url": "./tmp-swagger-gen/dymensionxyz/dymension/gamm/v2/query.swagger.json", "operationIds": { "rename": { "SpotPrice": "SpotPriceV2" @@ -73,7 +81,7 @@ } }, { - "url": "./tmp-swagger-gen/osmosis/gamm/v1beta1/query.swagger.json", + "url": "./tmp-swagger-gen/dymensionxyz/dymension/gamm/v1beta1/query.swagger.json", "operationIds": { "rename": { "Params": "GammParams", @@ -83,10 +91,10 @@ } }, { - "url": "./tmp-swagger-gen/osmosis/epochs/query.swagger.json" + "url": "./tmp-swagger-gen/dymensionxyz/dymension/epochs/v1beta1/query.swagger.json" }, { - "url": "./tmp-swagger-gen/osmosis/incentives/query.swagger.json", + "url": "./tmp-swagger-gen/dymensionxyz/dymension/incentives/query.swagger.json", "operationIds": { "rename": { "Params": "IncentivesParams", @@ -95,7 +103,7 @@ } }, { - "url": "./tmp-swagger-gen/osmosis/lockup/query.swagger.json", + "url": "./tmp-swagger-gen/dymensionxyz/dymension/lockup/query.swagger.json", "operationIds": { "rename": { "Params": "LockupParams" @@ -103,7 +111,7 @@ } }, { - "url": "./tmp-swagger-gen/osmosis/txfees/v1beta1/query.swagger.json", + "url": "./tmp-swagger-gen/dymensionxyz/dymension/txfees/v1beta1/query.swagger.json", "operationIds": { "rename": { "Params": "TxFeesParams" diff --git a/docs/static/openapi.yml b/docs/static/openapi.yml index 5ba5805e3..d290fdc33 100644 --- a/docs/static/openapi.yml +++ b/docs/static/openapi.yml @@ -444,6 +444,28 @@ paths: type: string bridging_fee: type: string + delete_packets_epoch_limit: + type: integer + format: int32 + description: >- + `delete_packets_epoch_limit` is the hard limit of the + number of finalized rollapp packets + + that will be deleted from the store on every epoch end. + + As deleting finalized rollapp packets is meant to keep the + store from growing, + + it is more of a "nice to have" rather than a "must have" + feature, + + this is a way to limit the time it takes to do so, + + even if it means potentially causing the store to + temporarily grow by piling up packets + + that weren't deleted but rather "postponed", to subsequent + epochs. description: >- QueryParamsResponse is response type for the Query/Params RPC method. @@ -665,7 +687,7 @@ paths: e.g status/rollappid/packetProofHeight/packetDestinationChannel-PacketSequence - which gurantees uniqueness + which guarantees uniqueness tracking_packet_key: type: string description: >- @@ -715,8 +737,6 @@ paths: they pay price and receive fee + price recipient: type: string - is_fulfilled: - type: boolean tracking_packet_status: type: string enum: @@ -734,6 +754,11 @@ paths: - ON_TIMEOUT - UNDEFINED default: ON_RECV + fulfiller_address: + type: string + description: >- + fulfiller_address is the bech32-encoded address of the + account which fulfilled the order. description: >- QueryGetDemandOrderResponse is the response type for the Query/GetDemandOrder RPC method. @@ -962,7 +987,7 @@ paths: e.g status/rollappid/packetProofHeight/packetDestinationChannel-PacketSequence - which gurantees uniqueness + which guarantees uniqueness tracking_packet_key: type: string description: >- @@ -1014,8 +1039,6 @@ paths: because they pay price and receive fee + price recipient: type: string - is_fulfilled: - type: boolean tracking_packet_status: type: string enum: @@ -1033,6 +1056,11 @@ paths: - ON_TIMEOUT - UNDEFINED default: ON_RECV + fulfiller_address: + type: string + description: >- + fulfiller_address is the bech32-encoded address of the + account which fulfilled the order. title: A list of demand orders with the given status description: >- QueryDemandOrdersByStatusResponse is the response type for the @@ -1273,6 +1301,21 @@ paths: - FULFILLED - UNFULFILLED default: UNDEFINED + - name: fulfiller + description: optional fulfiller address. + in: query + required: false + type: string + - name: denom + description: optional denom. + in: query + required: false + type: string + - name: recipient + description: optional recipient address. + in: query + required: false + type: string tags: - Query /dymensionxyz/dymension/eibc/params: @@ -1494,292 +1537,91 @@ paths: } tags: - Query - /dymensionxyz/dymension/rollapp/eip155/{eip155}: + /dymensionxyz/dymension/dymns/alias/{alias}: get: - summary: Queries a Rollapp by index. - operationId: RollappByEIP155 + summary: >- + Alias queries the chain_id associated as well as the Sell-Order and + Buy-Order IDs relates to the alias. + operationId: Alias responses: '200': description: A successful response. schema: type: object properties: - rollapp: - type: object - properties: - rollappId: - type: string - description: >- - The unique identifier of the rollapp chain. - - The rollappId follows the same standard as cosmos - chain_id. - creator: - type: string - description: >- - creator is the bech32-encoded address of the rollapp - creator. - version: - type: string - format: uint64 - title: |- - version is the software and configuration version. - starts from 1 and increases by one on every MsgUpdateState - maxSequencers: - type: string - format: uint64 - description: maxSequencers is the maximum number of sequencers. - permissionedAddresses: - type: array - items: - type: string - description: >- - permissionedAddresses is a bech32-encoded address list of - the sequencers that are allowed to serve this rollappId. - - In the case of an empty list, the rollapp is considered - permissionless. - tokenMetadata: - type: array - items: - type: object - properties: - description: - type: string - denom_units: - type: array - items: - type: object - properties: - denom: - type: string - description: >- - denom represents the string name of the given - denom unit (e.g uatom). - exponent: - type: integer - format: int64 - description: >- - exponent represents power of 10 exponent that - one must - - raise the base_denom to in order to equal the - given DenomUnit's denom - - 1 denom = 10^exponent base_denom - - (e.g. with a base_denom of uatom, one can - create a DenomUnit of 'atom' with - - exponent = 6, thus: 1 atom = 10^6 uatom). - aliases: - type: array - items: - type: string - title: >- - aliases is a list of string aliases for the - given denom - description: >- - DenomUnit represents a struct that describes a - given - - denomination unit of the basic token. - title: >- - denom_units represents the list of DenomUnit's for a - given coin - base: - type: string - description: >- - base represents the base denom (should be the - DenomUnit with exponent = 0). - display: - type: string - description: |- - display indicates the suggested denom that should be - displayed in clients. - name: - type: string - description: 'Since: cosmos-sdk 0.43' - title: 'name defines the name of the token (eg: Cosmos Atom)' - symbol: - type: string - description: >- - symbol is the token symbol usually shown on - exchanges (eg: ATOM). This can - - be the same as the display. - - - Since: cosmos-sdk 0.43 - uri: - type: string - description: >- - URI to a document (on or off-chain) that contains - additional information. Optional. - - - Since: cosmos-sdk 0.46 - uri_hash: - type: string - description: >- - URIHash is a sha256 hash of a document pointed by - URI. It's used to verify that - - the document didn't change. Optional. - - - Since: cosmos-sdk 0.46 - description: |- - Metadata represents a struct that describes - a basic token. - title: >- - tokenMetadata is a list of TokenMetadata that are - registered on this rollapp - genesis_state: - title: >- - genesis_state is a partial repr of the state the hub can - expect the rollapp to be in upon genesis - type: object - properties: - genesis_accounts: - type: array - items: - type: object - properties: - amount: - title: >- - amount of coins to be sent to the genesis - address - type: object - properties: - denom: - type: string - amount: - type: string - description: >- - Coin defines a token with a denomination and an - amount. - - - NOTE: The amount field is an Int which - implements the custom method - - signatures required by gogoproto. - address: - type: string - title: >- - address is a bech-32 address of the genesis - account - title: >- - GenesisAccount is a struct for the genesis account - for the rollapp - title: genesis_accounts is a list of token allocations - is_genesis_event: - type: boolean - title: >- - is_genesis_event is a boolean that indicates if the - genesis event has occured - channel_id: - type: string - description: >- - channel_id will be set to the canonical IBC channel of the - rollapp. - frozen: - type: boolean - description: >- - frozen is a boolean that indicates if the rollapp is - frozen. - registeredDenoms: - type: array - items: - type: string - title: >- - registeredDenoms is a list of registered denom bases on - this rollapp - title: >- - Rollapp defines a rollapp object. First the RollApp is created - and then - - sequencers can be created and attached. The RollApp is - identified by rollappId - latestStateIndex: - description: Defines the index of the last rollapp UpdateState. - type: object - properties: - rollappId: - type: string - title: >- - rollappId is the rollapp that the sequencer belongs to and - asking to update - - it used to identify the what rollapp a StateInfo belongs - - The rollappId follows the same standard as cosmos chain_id - index: - type: string - format: uint64 - title: >- - index is a sequential increasing number, updating on each - - state update used for indexing to a specific state info, - the first index is 1 - title: >- - StateInfoIndex is the data used for indexing and retrieving a - StateInfo - - it updated and saved with every UpdateState in StateInfo. - - We use the this structure also for: - - 1. LatestStateInfoIndex which defines the rollapps' current - (latest) index of the last UpdateState - - 2. LatestFinalizedStateIndex which defines the rollapps' - current (latest) index of the latest StateInfo that was - finalized - latestFinalizedStateIndex: + chain_id: + type: string + title: chain_id associated with the alias + found_sell_order: + type: boolean description: >- - Defines the index of the last rollapp UpdateState that was - finalized. - type: object - properties: - rollappId: - type: string - title: >- - rollappId is the rollapp that the sequencer belongs to and - asking to update - - it used to identify the what rollapp a StateInfo belongs - - The rollappId follows the same standard as cosmos chain_id - index: - type: string - format: uint64 - title: >- - index is a sequential increasing number, updating on each - - state update used for indexing to a specific state info, - the first index is 1 - title: >- - StateInfoIndex is the data used for indexing and retrieving a - StateInfo - - it updated and saved with every UpdateState in StateInfo. - - We use the this structure also for: - - 1. LatestStateInfoIndex which defines the rollapps' current - (latest) index of the last UpdateState - - 2. LatestFinalizedStateIndex which defines the rollapps' - current (latest) index of the latest StateInfo that was - finalized - latestHeight: + found_sell_order is true if active Sell-Order is found for the + alias. + buy_order_ids: + type: array + items: + type: string + description: buy_order_ids is the list of Buy-Order IDs for the alias. + same_chain_aliases: + type: array + items: + type: string + description: >- + same_chain_aliases is the list of aliases for the same chain + that associated with the alias. + title: QueryAliasResponse + default: + description: An unexpected error response. + schema: + type: object + properties: + error: type: string - format: uint64 - latestFinalizedHeight: + code: + type: integer + format: int32 + message: type: string - format: uint64 + details: + type: array + items: + type: object + properties: + type_url: + type: string + value: + type: string + format: byte + parameters: + - name: alias + description: alias to query + in: path + required: true + type: string + tags: + - Query + /dymensionxyz/dymension/dymns/aliases: + get: + summary: Aliases queries all the aliases for a chain id or all chains. + operationId: Aliases + responses: + '200': + description: A successful response. + schema: + type: object + properties: + aliases_by_chain_id: + type: object + additionalProperties: + type: object + properties: + aliases: + type: array + items: + type: string + description: aliases is a list of alias, available for a RollApp. + description: MultipleAliases contains a list of alias. + description: aliases_by_chain_id is the map of aliases by chain id. default: description: An unexpected error response. schema: @@ -1803,26 +1645,94 @@ paths: type: string format: byte parameters: - - name: eip155 - in: path - required: true + - name: chain_id + description: chain_id to query alias for, empty for all chains. + in: query + required: false type: string - format: uint64 tags: - Query - /dymensionxyz/dymension/rollapp/latest_height/{rollappId}: + /dymensionxyz/dymension/dymns/buy_order/{id}: get: - summary: Queries a LatestHeight by rollapp-id. - operationId: LatestHeight + summary: BuyOrderById queries a Buy-Order by its id. + operationId: BuyOrderById responses: '200': description: A successful response. schema: type: object properties: - height: - type: string - format: uint64 + buy_order: + description: buy_order is the result. + type: object + properties: + id: + type: string + description: >- + id is the unique identifier of the order. Generated by the + module. + asset_id: + type: string + description: asset_id of the Dym-Name/Alias willing to buy. + asset_type: + title: >- + asset_type is type of the asset of the order, is + Dym-Name/Alias + type: string + enum: + - AT_UNKNOWN + - AT_DYM_NAME + - AT_ALIAS + default: AT_UNKNOWN + description: AssetType present type of the asset of the Buy/Sell order. + params: + type: array + items: + type: string + description: >- + params is the list of parameters of the Buy-Order. + + It is empty for asset type Dym-Name. + + It has one element for asset type Alias, which is the + rollapp_id to assigned for. + buyer: + type: string + description: >- + buyer is bech32 address of the account which placed the + order. + offer_price: + description: >- + offer_price is the amount of coins that buyer willing to + pay for the asset. + + This amount is deposited to the module account upon + placing the offer. + type: object + properties: + denom: + type: string + amount: + type: string + counterparty_offer_price: + description: >- + counterparty_offer_price is the price that the + Dym-Name/Alias owner is willing to sell for. + + This is used for counterparty price negotiation and for + information only. + + The transaction can only be executed when the owner + accepts the offer with exact offer_price. + type: object + properties: + denom: + type: string + amount: + type: string + description: >- + QueryBuyOrderByIdResponse is the response type for the + Query/BuyOrderById RPC method. default: description: An unexpected error response. schema: @@ -1846,60 +1756,110 @@ paths: type: string format: byte parameters: - - name: rollappId + - name: id + description: id of buy offer to query. in: path required: true type: string - - name: finalized - in: query - required: false - type: boolean tags: - Query - /dymensionxyz/dymension/rollapp/latest_state_index/{rollappId}: + /dymensionxyz/dymension/dymns/buy_orders_by_alias/{alias}: get: - summary: Queries a LatestStateIndex by rollapp-id. - operationId: LatestStateIndex + summary: BuyOrdersByAlias queries all the buy orders of an Alias. + operationId: BuyOrdersByAlias responses: '200': description: A successful response. schema: type: object properties: - stateIndex: - type: object - properties: - rollappId: - type: string - title: >- - rollappId is the rollapp that the sequencer belongs to and - asking to update + buy_orders: + type: array + items: + type: object + properties: + id: + type: string + description: >- + id is the unique identifier of the order. Generated by + the module. + asset_id: + type: string + description: asset_id of the Dym-Name/Alias willing to buy. + asset_type: + title: >- + asset_type is type of the asset of the order, is + Dym-Name/Alias + type: string + enum: + - AT_UNKNOWN + - AT_DYM_NAME + - AT_ALIAS + default: AT_UNKNOWN + description: >- + AssetType present type of the asset of the Buy/Sell + order. + params: + type: array + items: + type: string + description: >- + params is the list of parameters of the Buy-Order. - it used to identify the what rollapp a StateInfo belongs + It is empty for asset type Dym-Name. - The rollappId follows the same standard as cosmos chain_id - index: - type: string - format: uint64 - title: >- - index is a sequential increasing number, updating on each + It has one element for asset type Alias, which is the + rollapp_id to assigned for. + buyer: + type: string + description: >- + buyer is bech32 address of the account which placed the + order. + offer_price: + description: >- + offer_price is the amount of coins that buyer willing to + pay for the asset. - state update used for indexing to a specific state info, - the first index is 1 - title: >- - StateInfoIndex is the data used for indexing and retrieving a - StateInfo + This amount is deposited to the module account upon + placing the offer. + type: object + properties: + denom: + type: string + amount: + type: string + counterparty_offer_price: + description: >- + counterparty_offer_price is the price that the + Dym-Name/Alias owner is willing to sell for. - it updated and saved with every UpdateState in StateInfo. + This is used for counterparty price negotiation and for + information only. - We use the this structure also for: + The transaction can only be executed when the owner + accepts the offer with exact offer_price. + type: object + properties: + denom: + type: string + amount: + type: string + description: >- + BuyOrder defines an offer to buy a Dym-Name/Alias, placed by + buyer. - 1. LatestStateInfoIndex which defines the rollapps' current - (latest) index of the last UpdateState + Buyer will need to deposit the offer amount to the module + account. - 2. LatestFinalizedStateIndex which defines the rollapps' - current (latest) index of the latest StateInfo that was - finalized + When the owner of the Dym-Name/Alias accepts the offer, + deposited amount will be transferred to the owner. + + When the buyer cancels the offer, deposited amount will be + refunded to the buyer. + description: buy_orders of the input alias. + description: >- + QueryBuyOrdersByAliasResponse is the response type for the + Query/BuyOrdersByAlias RPC method. default: description: An unexpected error response. schema: @@ -1923,59 +1883,110 @@ paths: type: string format: byte parameters: - - name: rollappId + - name: alias + description: alias is the alias to query the buy offers placed for it. in: path required: true type: string - - name: finalized - in: query - required: false - type: boolean tags: - Query - /dymensionxyz/dymension/rollapp/params: + /dymensionxyz/dymension/dymns/buy_orders_by_dym_name/{name}: get: - summary: Parameters queries the parameters of the module. - operationId: RollAppParams + summary: BuyOrdersByDymName queries all the buy orders of a Dym-Name. + operationId: BuyOrdersByDymName responses: '200': description: A successful response. schema: type: object properties: - params: - description: params holds all the parameters of this module. - type: object - properties: - dispute_period_in_blocks: - type: string - format: uint64 - title: |- - dispute_period_in_blocks the number of blocks it takes - to change a status of a state from received to finalized. - during that period, any user could submit fraud proof - deployer_whitelist: - type: array - items: + buy_orders: + type: array + items: + type: object + properties: + id: + type: string + description: >- + id is the unique identifier of the order. Generated by + the module. + asset_id: + type: string + description: asset_id of the Dym-Name/Alias willing to buy. + asset_type: + title: >- + asset_type is type of the asset of the order, is + Dym-Name/Alias + type: string + enum: + - AT_UNKNOWN + - AT_DYM_NAME + - AT_ALIAS + default: AT_UNKNOWN + description: >- + AssetType present type of the asset of the Buy/Sell + order. + params: + type: array + items: + type: string + description: >- + params is the list of parameters of the Buy-Order. + + It is empty for asset type Dym-Name. + + It has one element for asset type Alias, which is the + rollapp_id to assigned for. + buyer: + type: string + description: >- + buyer is bech32 address of the account which placed the + order. + offer_price: + description: >- + offer_price is the amount of coins that buyer willing to + pay for the asset. + + This amount is deposited to the module account upon + placing the offer. type: object properties: - address: + denom: type: string - description: |- - address is a bech32-encoded address of the - accounts that are allowed to create a rollapp. - title: >- - deployer_whitelist is a list of the + amount: + type: string + counterparty_offer_price: + description: >- + counterparty_offer_price is the price that the + Dym-Name/Alias owner is willing to sell for. - accounts that are allowed to create a rollapp and maximum - number of rollapps. + This is used for counterparty price negotiation and for + information only. - In the case of an empty list, there are no restrictions - rollapps_enabled: - type: boolean + The transaction can only be executed when the owner + accepts the offer with exact offer_price. + type: object + properties: + denom: + type: string + amount: + type: string + description: >- + BuyOrder defines an offer to buy a Dym-Name/Alias, placed by + buyer. + + Buyer will need to deposit the offer amount to the module + account. + + When the owner of the Dym-Name/Alias accepts the offer, + deposited amount will be transferred to the owner. + + When the buyer cancels the offer, deposited amount will be + refunded to the buyer. + description: buy_orders placed for the Dym-Name. description: >- - QueryParamsResponse is response type for the Query/Params RPC - method. + QueryBuyOrdersByDymNameResponse is the response type for the + Query/BuyOrdersByDymName RPC method. default: description: An unexpected error response. schema: @@ -1998,139 +2009,246 @@ paths: value: type: string format: byte + parameters: + - name: name + description: name is the Dym-Name to query the buy offers placed for it. + in: path + required: true + type: string tags: - Query - /dymensionxyz/dymension/rollapp/rollapp: + /dymensionxyz/dymension/dymns/buy_orders_of_aliases_linked_to_rollapp/{rollapp_id}: get: - summary: Queries a list of Rollapp items. - operationId: RollappAll + summary: >- + BuyOrdersOfAliasesLinkedToRollApp queries all the buy orders of all + Aliases linked to a RollApp. + operationId: BuyOrdersOfAliasesLinkedToRollApp responses: '200': description: A successful response. schema: type: object properties: - rollapp: + buy_orders: type: array items: type: object properties: - rollappId: + id: + type: string + description: >- + id is the unique identifier of the order. Generated by + the module. + asset_id: + type: string + description: asset_id of the Dym-Name/Alias willing to buy. + asset_type: + title: >- + asset_type is type of the asset of the order, is + Dym-Name/Alias + type: string + enum: + - AT_UNKNOWN + - AT_DYM_NAME + - AT_ALIAS + default: AT_UNKNOWN + description: >- + AssetType present type of the asset of the Buy/Sell + order. + params: + type: array + items: + type: string + description: >- + params is the list of parameters of the Buy-Order. + + It is empty for asset type Dym-Name. + + It has one element for asset type Alias, which is the + rollapp_id to assigned for. + buyer: type: string description: >- - The unique identifier of the rollapp chain. + buyer is bech32 address of the account which placed the + order. + offer_price: + description: >- + offer_price is the amount of coins that buyer willing to + pay for the asset. - The rollappId follows the same standard as cosmos - chain_id. - latestStateIndex: + This amount is deposited to the module account upon + placing the offer. type: object properties: - rollappId: + denom: type: string - title: >- - rollappId is the rollapp that the sequencer belongs - to and asking to update - - it used to identify the what rollapp a StateInfo - belongs - - The rollappId follows the same standard as cosmos - chain_id - index: + amount: type: string - format: uint64 - title: >- - index is a sequential increasing number, updating on - each - - state update used for indexing to a specific state - info, the first index is 1 - title: >- - StateInfoIndex is the data used for indexing and - retrieving a StateInfo - - it updated and saved with every UpdateState in - StateInfo. - - We use the this structure also for: + counterparty_offer_price: + description: >- + counterparty_offer_price is the price that the + Dym-Name/Alias owner is willing to sell for. - 1. LatestStateInfoIndex which defines the rollapps' - current (latest) index of the last UpdateState + This is used for counterparty price negotiation and for + information only. - 2. LatestFinalizedStateIndex which defines the rollapps' - current (latest) index of the latest StateInfo that was - finalized - description: Defines the index of the last rollapp UpdateState. - latestFinalizedStateIndex: + The transaction can only be executed when the owner + accepts the offer with exact offer_price. type: object properties: - rollappId: + denom: type: string - title: >- - rollappId is the rollapp that the sequencer belongs - to and asking to update + amount: + type: string + description: >- + BuyOrder defines an offer to buy a Dym-Name/Alias, placed by + buyer. - it used to identify the what rollapp a StateInfo - belongs + Buyer will need to deposit the offer amount to the module + account. - The rollappId follows the same standard as cosmos - chain_id - index: - type: string - format: uint64 - title: >- - index is a sequential increasing number, updating on - each + When the owner of the Dym-Name/Alias accepts the offer, + deposited amount will be transferred to the owner. - state update used for indexing to a specific state - info, the first index is 1 + When the buyer cancels the offer, deposited amount will be + refunded to the buyer. + description: >- + buy_orders are all the buy orders of the aliases linked to the + input rollapp. + description: >- + QueryBuyOrdersOfAliasesLinkedToRollAppResponse is the response + type for the Query/BuyOrdersOfAliasesLinkedToRollApp RPC method. + default: + description: An unexpected error response. + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + value: + type: string + format: byte + parameters: + - name: rollapp_id + description: >- + rollapp_id is the rollapp to query all the buy offers of the aliases + linked to it + in: path + required: true + type: string + tags: + - Query + /dymensionxyz/dymension/dymns/buy_orders_of_dym_names_owned_by_account/{account}: + get: + summary: >- + BuyOrdersOfDymNamesOwnedByAccount queries all the buy orders of all + Dym-Names owned by an account. + operationId: BuyOrdersOfDymNamesOwnedByAccount + responses: + '200': + description: A successful response. + schema: + type: object + properties: + buy_orders: + type: array + items: + type: object + properties: + id: + type: string + description: >- + id is the unique identifier of the order. Generated by + the module. + asset_id: + type: string + description: asset_id of the Dym-Name/Alias willing to buy. + asset_type: title: >- - StateInfoIndex is the data used for indexing and - retrieving a StateInfo - - it updated and saved with every UpdateState in - StateInfo. + asset_type is type of the asset of the order, is + Dym-Name/Alias + type: string + enum: + - AT_UNKNOWN + - AT_DYM_NAME + - AT_ALIAS + default: AT_UNKNOWN + description: >- + AssetType present type of the asset of the Buy/Sell + order. + params: + type: array + items: + type: string + description: >- + params is the list of parameters of the Buy-Order. - We use the this structure also for: + It is empty for asset type Dym-Name. - 1. LatestStateInfoIndex which defines the rollapps' - current (latest) index of the last UpdateState + It has one element for asset type Alias, which is the + rollapp_id to assigned for. + buyer: + type: string + description: >- + buyer is bech32 address of the account which placed the + order. + offer_price: + description: >- + offer_price is the amount of coins that buyer willing to + pay for the asset. - 2. LatestFinalizedStateIndex which defines the rollapps' - current (latest) index of the latest StateInfo that was - finalized + This amount is deposited to the module account upon + placing the offer. + type: object + properties: + denom: + type: string + amount: + type: string + counterparty_offer_price: description: >- - Defines the index of the last rollapp UpdateState that - was finalized. - title: Rollapp summary is a compact representation of Rollapp - pagination: - type: object - properties: - next_key: - type: string - format: byte - description: |- - next_key is the key to be passed to PageRequest.key to - query the next page most efficiently. It will be empty if - there are no more results. - total: - type: string - format: uint64 - title: >- - total is total number of results available if - PageRequest.count_total + counterparty_offer_price is the price that the + Dym-Name/Alias owner is willing to sell for. - was set, its value is undefined otherwise - description: >- - PageResponse is to be embedded in gRPC response messages where - the + This is used for counterparty price negotiation and for + information only. - corresponding request message has used PageRequest. + The transaction can only be executed when the owner + accepts the offer with exact offer_price. + type: object + properties: + denom: + type: string + amount: + type: string + description: >- + BuyOrder defines an offer to buy a Dym-Name/Alias, placed by + buyer. - message SomeResponse { - repeated Bar results = 1; - PageResponse page = 2; - } + Buyer will need to deposit the offer amount to the module + account. + + When the owner of the Dym-Name/Alias accepts the offer, + deposited amount will be transferred to the owner. + + When the buyer cancels the offer, deposited amount will be + refunded to the buyer. + description: buy_orders of all the Dym-Names owned by the input account. + description: >- + QueryBuyOrdersOfDymNamesOwnedByAccountResponse is the response + type for the Query/BuyOrdersOfDymNamesOwnedByAccount RPC method. default: description: An unexpected error response. schema: @@ -2154,350 +2272,293 @@ paths: type: string format: byte parameters: - - name: pagination.key - description: |- - key is a value returned in PageResponse.next_key to begin - querying the next page most efficiently. Only one of offset or key - should be set. - in: query - required: false - type: string - format: byte - - name: pagination.offset + - name: account description: >- - offset is a numeric offset that can be used when key is unavailable. + account is the account address to query all the buy offers of the + Dym-Names owned by it. + in: path + required: true + type: string + tags: + - Query + /dymensionxyz/dymension/dymns/buy_orders_placed_by_account/{account}: + get: + summary: >- + BuyOrdersPlacedByAccount queries the all the buy orders placed by an + account. + operationId: BuyOrdersPlacedByAccount + responses: + '200': + description: A successful response. + schema: + type: object + properties: + buy_orders: + type: array + items: + type: object + properties: + id: + type: string + description: >- + id is the unique identifier of the order. Generated by + the module. + asset_id: + type: string + description: asset_id of the Dym-Name/Alias willing to buy. + asset_type: + title: >- + asset_type is type of the asset of the order, is + Dym-Name/Alias + type: string + enum: + - AT_UNKNOWN + - AT_DYM_NAME + - AT_ALIAS + default: AT_UNKNOWN + description: >- + AssetType present type of the asset of the Buy/Sell + order. + params: + type: array + items: + type: string + description: >- + params is the list of parameters of the Buy-Order. - It is less efficient than using key. Only one of offset or key - should + It is empty for asset type Dym-Name. - be set. - in: query - required: false - type: string - format: uint64 - - name: pagination.limit - description: >- - limit is the total number of results to be returned in the result - page. + It has one element for asset type Alias, which is the + rollapp_id to assigned for. + buyer: + type: string + description: >- + buyer is bech32 address of the account which placed the + order. + offer_price: + description: >- + offer_price is the amount of coins that buyer willing to + pay for the asset. - If left empty it will default to a value to be set by each app. - in: query - required: false - type: string - format: uint64 - - name: pagination.count_total - description: >- - count_total is set to true to indicate that the result set should - include + This amount is deposited to the module account upon + placing the offer. + type: object + properties: + denom: + type: string + amount: + type: string + counterparty_offer_price: + description: >- + counterparty_offer_price is the price that the + Dym-Name/Alias owner is willing to sell for. - a count of the total number of items available for pagination in - UIs. + This is used for counterparty price negotiation and for + information only. - count_total is only respected when offset is used. It is ignored - when key + The transaction can only be executed when the owner + accepts the offer with exact offer_price. + type: object + properties: + denom: + type: string + amount: + type: string + description: >- + BuyOrder defines an offer to buy a Dym-Name/Alias, placed by + buyer. - is set. - in: query - required: false - type: boolean - - name: pagination.reverse - description: >- - reverse is set to true if results are to be returned in the - descending order. + Buyer will need to deposit the offer amount to the module + account. + When the owner of the Dym-Name/Alias accepts the offer, + deposited amount will be transferred to the owner. - Since: cosmos-sdk 0.43 - in: query - required: false - type: boolean + When the buyer cancels the offer, deposited amount will be + refunded to the buyer. + description: offers are the Buy-Orders placed by the account. + description: >- + QueryBuyOrdersByAccountResponse is the response type for the + Query/BuyOrdersPlacedByAccount RPC method. + default: + description: An unexpected error response. + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + value: + type: string + format: byte + parameters: + - name: account + description: account is the account address to query the placed buy offers. + in: path + required: true + type: string tags: - Query - /dymensionxyz/dymension/rollapp/rollapp/{rollappId}: + /dymensionxyz/dymension/dymns/dym_name/{dym_name}: get: - summary: Queries a Rollapp by index. - operationId: Rollapp + summary: DymName queries a Dym-Name by its name. + operationId: DymName responses: '200': description: A successful response. schema: type: object properties: - rollapp: + dym_name: + description: dym_name is the Dym-Name queried for. type: object properties: - rollappId: + name: type: string - description: >- - The unique identifier of the rollapp chain. - - The rollappId follows the same standard as cosmos - chain_id. - creator: + description: name is the human-readable name of the Dym-Name. + owner: type: string description: >- - creator is the bech32-encoded address of the rollapp - creator. - version: + owner is the account address that owns the Dym-Name. Owner + has permission to transfer ownership. + controller: type: string - format: uint64 - title: |- - version is the software and configuration version. - starts from 1 and increases by one on every MsgUpdateState - maxSequencers: + description: >- + controller is the account address that has permission + update configuration for the Dym-Name. + + Default is the owner. Able to transfer control to another + account by the owner. + + Users can set Dym-Name owned by Cold-Wallet and controlled + by Hot-Wallet. + expire_at: type: string - format: uint64 - description: maxSequencers is the maximum number of sequencers. - permissionedAddresses: - type: array - items: - type: string + format: int64 description: >- - permissionedAddresses is a bech32-encoded address list of - the sequencers that are allowed to serve this rollappId. + expire_at is the UTC epoch represent the last effective + date of the Dym-Name, + + after which the Dym-Name is no longer valid. + + NOTE: Expired Dym-Names are not deleted from the store - In the case of an empty list, the rollapp is considered - permissionless. - tokenMetadata: + because iterating through store is very expensive because + expiry date must be checked every use. + configs: type: array items: type: object properties: - description: - type: string - denom_units: - type: array - items: - type: object - properties: - denom: - type: string - description: >- - denom represents the string name of the given - denom unit (e.g uatom). - exponent: - type: integer - format: int64 - description: >- - exponent represents power of 10 exponent that - one must - - raise the base_denom to in order to equal the - given DenomUnit's denom - - 1 denom = 10^exponent base_denom - - (e.g. with a base_denom of uatom, one can - create a DenomUnit of 'atom' with - - exponent = 6, thus: 1 atom = 10^6 uatom). - aliases: - type: array - items: - type: string - title: >- - aliases is a list of string aliases for the - given denom - description: >- - DenomUnit represents a struct that describes a - given - - denomination unit of the basic token. - title: >- - denom_units represents the list of DenomUnit's for a - given coin - base: - type: string + type: description: >- - base represents the base denom (should be the - DenomUnit with exponent = 0). - display: + type is the type of the Dym-Name configuration + (equals to Type in DNS). type: string - description: |- - display indicates the suggested denom that should be - displayed in clients. - name: - type: string - description: 'Since: cosmos-sdk 0.43' - title: 'name defines the name of the token (eg: Cosmos Atom)' - symbol: + enum: + - DCT_UNKNOWN + - DCT_NAME + default: DCT_UNKNOWN + chain_id: type: string description: >- - symbol is the token symbol usually shown on - exchanges (eg: ATOM). This can - - be the same as the display. - + chain_id is the chain-id of the Dym-Name + configuration (equals to top-level-domain). - Since: cosmos-sdk 0.43 - uri: + If empty, the configuration is for host chain + (Dymension Hub). + path: type: string description: >- - URI to a document (on or off-chain) that contains - additional information. Optional. - + path of the Dym-Name configuration (equals to Host + in DNS). - Since: cosmos-sdk 0.46 - uri_hash: + If the type of this config record is Name, it is the + Sub-Name of the Dym-Name Address. + value: type: string description: >- - URIHash is a sha256 hash of a document pointed by - URI. It's used to verify that + value of the Dym-Name configuration resolves to + (equals to Value in DNS). - the document didn't change. Optional. - - - Since: cosmos-sdk 0.46 - description: |- - Metadata represents a struct that describes - a basic token. - title: >- - tokenMetadata is a list of TokenMetadata that are - registered on this rollapp - genesis_state: - title: >- - genesis_state is a partial repr of the state the hub can - expect the rollapp to be in upon genesis - type: object - properties: - genesis_accounts: - type: array - items: - type: object - properties: - amount: - title: >- - amount of coins to be sent to the genesis - address - type: object - properties: - denom: - type: string - amount: - type: string - description: >- - Coin defines a token with a denomination and an - amount. - - - NOTE: The amount field is an Int which - implements the custom method + If the type of this config record is Name, it is the + address which the Dym-Name Address resolves to. + description: >- + DymNameConfig contains the resolution configuration for + the Dym-Name. - signatures required by gogoproto. - address: - type: string - title: >- - address is a bech-32 address of the genesis - account - title: >- - GenesisAccount is a struct for the genesis account - for the rollapp - title: genesis_accounts is a list of token allocations - is_genesis_event: - type: boolean - title: >- - is_genesis_event is a boolean that indicates if the - genesis event has occured - channel_id: + Each record is a resolution record, similar to DNS. + description: configs are configuration records for the Dym-Name. + contact: type: string - description: >- - channel_id will be set to the canonical IBC channel of the - rollapp. - frozen: - type: boolean - description: >- - frozen is a boolean that indicates if the rollapp is - frozen. - registeredDenoms: - type: array - items: + description: |- + contact is an optional information for the Dym-Name. + Convenient for retails users. + description: >- + QueryDymNameResponse is the response type for the Query/DymName + RPC method. + default: + description: An unexpected error response. + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: type: string - title: >- - registeredDenoms is a list of registered denom bases on - this rollapp - title: >- - Rollapp defines a rollapp object. First the RollApp is created - and then - - sequencers can be created and attached. The RollApp is - identified by rollappId - latestStateIndex: - description: Defines the index of the last rollapp UpdateState. - type: object - properties: - rollappId: - type: string - title: >- - rollappId is the rollapp that the sequencer belongs to and - asking to update - - it used to identify the what rollapp a StateInfo belongs - - The rollappId follows the same standard as cosmos chain_id - index: - type: string - format: uint64 - title: >- - index is a sequential increasing number, updating on each - - state update used for indexing to a specific state info, - the first index is 1 - title: >- - StateInfoIndex is the data used for indexing and retrieving a - StateInfo - - it updated and saved with every UpdateState in StateInfo. - - We use the this structure also for: - - 1. LatestStateInfoIndex which defines the rollapps' current - (latest) index of the last UpdateState - - 2. LatestFinalizedStateIndex which defines the rollapps' - current (latest) index of the latest StateInfo that was - finalized - latestFinalizedStateIndex: - description: >- - Defines the index of the last rollapp UpdateState that was - finalized. + value: + type: string + format: byte + parameters: + - name: dym_name + description: dym_name is the name of the Dym-Name to query. + in: path + required: true + type: string + tags: + - Query + /dymensionxyz/dymension/dymns/estimate_register_alias/{alias}: + get: + summary: EstimateRegisterAlias estimates the cost to register an Alias. + operationId: EstimateRegisterAlias + responses: + '200': + description: A successful response. + schema: + type: object + properties: + price: + description: price is the price to register the alias. type: object properties: - rollappId: + denom: type: string - title: >- - rollappId is the rollapp that the sequencer belongs to and - asking to update - - it used to identify the what rollapp a StateInfo belongs - - The rollappId follows the same standard as cosmos chain_id - index: + amount: type: string - format: uint64 - title: >- - index is a sequential increasing number, updating on each - - state update used for indexing to a specific state info, - the first index is 1 - title: >- - StateInfoIndex is the data used for indexing and retrieving a - StateInfo - - it updated and saved with every UpdateState in StateInfo. - - We use the this structure also for: - - 1. LatestStateInfoIndex which defines the rollapps' current - (latest) index of the last UpdateState - - 2. LatestFinalizedStateIndex which defines the rollapps' - current (latest) index of the latest StateInfo that was - finalized - latestHeight: - type: string - format: uint64 - latestFinalizedHeight: - type: string - format: uint64 + description: >- + EstimateRegisterAliasResponse is the response type for the + Query/EstimateRegisterAlias RPC method. default: description: An unexpected error response. schema: @@ -2521,128 +2582,68 @@ paths: type: string format: byte parameters: - - name: rollappId + - name: alias + description: alias to be registered. in: path required: true type: string + - name: rollapp_id + description: rollapp_id is the rollapp to link the alias to. + in: query + required: false + type: string + - name: owner + description: >- + owner is the bech32-encoded address of the account which owns the + order. + in: query + required: false + type: string tags: - Query - /dymensionxyz/dymension/rollapp/state_info/{rollappId}/{index}: + /dymensionxyz/dymension/dymns/estimate_register_name/{name}/{duration}: get: - summary: Queries a StateInfo by index. - operationId: StateInfo + summary: EstimateRegisterName estimates the cost to register a Dym-Name. + operationId: EstimateRegisterName responses: '200': description: A successful response. schema: type: object properties: - stateInfo: + first_year_price: + description: >- + first_year_price is the price to register the Dym-Name for the + first year. type: object properties: - stateInfoIndex: - type: object - properties: - rollappId: - type: string - title: >- - rollappId is the rollapp that the sequencer belongs to - and asking to update - - it used to identify the what rollapp a StateInfo - belongs - - The rollappId follows the same standard as cosmos - chain_id - index: - type: string - format: uint64 - title: >- - index is a sequential increasing number, updating on - each - - state update used for indexing to a specific state - info, the first index is 1 - title: >- - StateInfoIndex is the data used for indexing and - retrieving a StateInfo - - it updated and saved with every UpdateState in StateInfo. - - We use the this structure also for: - - 1. LatestStateInfoIndex which defines the rollapps' - current (latest) index of the last UpdateState - - 2. LatestFinalizedStateIndex which defines the rollapps' - current (latest) index of the latest StateInfo that was - finalized - sequencer: - type: string - title: >- - sequencer is the bech32-encoded address of the sequencer - sent the update - startHeight: + denom: type: string - format: uint64 - title: >- - startHeight is the block height of the first block in the - batch - numBlocks: + amount: type: string - format: uint64 - title: >- - numBlocks is the number of blocks included in this batch - update - DAPath: + extend_price: + description: >- + extend_price is the price to extend the Dym-Name registration + for another year. + type: object + properties: + denom: type: string - title: DAPath is the description of the location on the DA layer - version: + amount: type: string - format: uint64 - title: version is the version of the rollapp - creationHeight: + total_price: + description: >- + total_price is the total price to register the Dym-Name for + the specified duration. + type: object + properties: + denom: type: string - format: uint64 - title: >- - creationHeight is the height at which the UpdateState took - place - status: - title: status is the status of the state update + amount: type: string - enum: - - PENDING - - FINALIZED - - REVERTED - default: PENDING - BDs: - title: >- - BDs is a list of block description objects (one per block) - - the list must be ordered by height, starting from - startHeight to startHeight+numBlocks-1 - type: object - properties: - BD: - type: array - items: - type: object - properties: - height: - type: string - format: uint64 - title: height is the height of the block - stateRoot: - type: string - format: byte - title: >- - stateRoot is a 32 byte array of the hash of the - block (state root of the block) - description: >- - BlockDescriptor defines a single rollapp chain block - description. - description: BlockDescriptors defines list of BlockDescriptor. - description: StateInfo defines a rollapps' state. + description: >- + EstimateRegisterNameResponse is the response type for the + Query/EstimateRegisterName RPC method. default: description: An unexpected error response. schema: @@ -2666,61 +2667,139 @@ paths: type: string format: byte parameters: - - name: rollappId + - name: name + description: name is the Dym-Name to be registered. in: path required: true type: string - - name: index + - name: duration + description: duration is the number of years the Dym-Name will be registered for. in: path required: true type: string - format: uint64 - - name: height + format: int64 + - name: owner + description: >- + owner is the bech32-encoded address of the account which owns the + order. in: query required: false type: string - format: uint64 - - name: finalized - in: query - required: false - type: boolean tags: - Query - /dymensionxyz/dymension/sequencer/params: + /dymensionxyz/dymension/dymns/owned_by/{owner}: get: - summary: Parameters queries the parameters of the module. - operationId: SequencerParams + summary: DymNamesOwnedByAccount queries the Dym-Names owned by an account. + operationId: DymNamesOwnedByAccount responses: '200': description: A successful response. schema: type: object properties: - params: - description: params holds all the parameters of this module. - type: object - properties: - min_bond: - type: object - properties: - denom: - type: string - amount: - type: string - description: >- - Coin defines a token with a denomination and an amount. + dym_names: + type: array + items: + type: object + properties: + name: + type: string + description: name is the human-readable name of the Dym-Name. + owner: + type: string + description: >- + owner is the account address that owns the Dym-Name. + Owner has permission to transfer ownership. + controller: + type: string + description: >- + controller is the account address that has permission + update configuration for the Dym-Name. + Default is the owner. Able to transfer control to + another account by the owner. - NOTE: The amount field is an Int which implements the - custom method + Users can set Dym-Name owned by Cold-Wallet and + controlled by Hot-Wallet. + expire_at: + type: string + format: int64 + description: >- + expire_at is the UTC epoch represent the last effective + date of the Dym-Name, - signatures required by gogoproto. - unbonding_time: - type: string - description: unbonding_time is the time duration of unbonding. + after which the Dym-Name is no longer valid. + + NOTE: Expired Dym-Names are not deleted from the store + + because iterating through store is very expensive + because expiry date must be checked every use. + configs: + type: array + items: + type: object + properties: + type: + description: >- + type is the type of the Dym-Name configuration + (equals to Type in DNS). + type: string + enum: + - DCT_UNKNOWN + - DCT_NAME + default: DCT_UNKNOWN + chain_id: + type: string + description: >- + chain_id is the chain-id of the Dym-Name + configuration (equals to top-level-domain). + + If empty, the configuration is for host chain + (Dymension Hub). + path: + type: string + description: >- + path of the Dym-Name configuration (equals to Host + in DNS). + + If the type of this config record is Name, it is + the Sub-Name of the Dym-Name Address. + value: + type: string + description: >- + value of the Dym-Name configuration resolves to + (equals to Value in DNS). + + If the type of this config record is Name, it is + the address which the Dym-Name Address resolves + to. + description: >- + DymNameConfig contains the resolution configuration + for the Dym-Name. + + Each record is a resolution record, similar to DNS. + description: configs are configuration records for the Dym-Name. + contact: + type: string + description: |- + contact is an optional information for the Dym-Name. + Convenient for retails users. + description: >- + DymName defines a Dym-Name, the mainly purpose is to store + ownership and resolution information. + + Dym-Name is similar to DNS. It is a human-readable name that + maps to a chain address. + + One Dym-Name can have multiple configurations, each + configuration is a resolution record. + + Dym-Name is owned by an account, and is able to grant + permission to another account to control the Dym-Name. + description: dym_names defines the Dym-Names owned by the input account. description: >- - QueryParamsResponse is response type for the Query/Params RPC - method. + QueryDymNamesOwnedByAccountResponse is the response type for the + Query/DymNamesOwnedByAccount RPC method. default: description: An unexpected error response. schema: @@ -2740,70 +2819,1961 @@ paths: properties: type_url: type: string - description: >- - A URL/resource name that uniquely identifies the type of - the serialized - - protocol buffer message. This string must contain at - least - - one "/" character. The last segment of the URL's path - must represent + value: + type: string + format: byte + parameters: + - name: owner + description: >- + owner defines the address of the owner of the Dym-Names to query + for. + in: path + required: true + type: string + tags: + - Query + /dymensionxyz/dymension/dymns/params: + get: + summary: Params queries the parameters of the module. + operationId: DymNSParams + responses: + '200': + description: A successful response. + schema: + type: object + properties: + params: + description: params holds all the parameters of this module. + type: object + properties: + price: + description: >- + price defines setting for pricing of Dym-Name and + price-related parameters. + type: object + properties: + name_price_steps: + type: array + items: + type: string + description: >- + name_price_steps holds the price steps configuration + for Dym-Name registration, apply to the first year. - the fully qualified name of the type (as in + The price of Dym-Name is calculated based on the + number of letters. - `path/google.protobuf.Duration`). The name should be in - a canonical form + The first element is the price of 1 letter Dym-Name, + the last element is the price of N+ letters Dym-Name. - (e.g., leading "." is not accepted). + Minimum steps count allowed is 4, for 1/2/3/4+ letters + Dym-Name. + alias_price_steps: + type: array + items: + type: string + description: >- + alias_price_steps holds the price steps configuration + for Alias registration, one off payment. + The price of Alias is calculated based on the number + of letters. - In practice, teams usually precompile into the binary - all types that they + The first element is the price of 1 letter Alias, the + last element is the price of N+ letters Alias. - expect it to use in the context of Any. However, for - URLs which use the + Minimum steps count allowed is 4, for 1/2/3/4+ letters + Alias. + price_extends: + type: string + description: >- + price_extends is used to extends Dym-Name yearly, + after the one-off payment for the first year. + price_denom: + type: string + description: >- + price_denom is the required denomination of the + pricing setup and trading policy. + min_offer_price: + type: string + description: >- + min_offer_price is minimum price allowed to place an + offer. - scheme `http`, `https`, or no scheme, one can optionally - set up a type + Mostly used to prevent spamming and abusing store with + low price offers, - server that maps type URLs to message definitions as - follows: + so the value should not be so low. + chains: + description: chains defines setting for prioritized aliases mapping. + type: object + properties: + aliases_of_chain_ids: + type: array + items: + type: object + properties: + chain_id: + type: string + description: chain_id which owned the aliases. + aliases: + type: array + items: + type: string + title: >- + aliases is a set of aliases of the chain id for + UX improvement, + like we can do my-name@cosmos instead of + my-name@cosmoshub-4 + description: >- + AliasesOfChainId defines the multiple-aliases of a + chain id. + description: >- + aliases_of_chain_ids is set of chain-ids and their + corresponding aliases, - * If no scheme is provided, `https` is assumed. + used for UX improvement like we can do my-name@cosmos + instead of my-name@cosmoshub-4. - * An HTTP GET on the URL must yield a - [google.protobuf.Type][] - value in binary format, or produce an error. - * Applications are allowed to cache lookup results based - on the - URL, or have them precompiled into a binary to avoid any - lookup. Therefore, binary compatibility needs to be preserved - on changes to types. (Use versioned type names to manage - breaking changes.) - Note: this functionality is not currently available in - the official + This list is prioritized over Roll-App aliases - protobuf release, and it is not used for type URLs - beginning with + the reason is to allow the community able to have + control to fixes the potential problems with the + aliases. + misc: + description: misc is group of miscellaneous parameters. + type: object + properties: + end_epoch_hook_identifier: + type: string + description: >- + end_epoch_hook_identifier is the identifier of the end + epoch hook. + grace_period_duration: + type: string + description: >- + grace_period_duration is the amount of time that the + former owner of an expired Dym-Name - type.googleapis.com. + can renew it before completely lost. + sell_order_duration: + type: string + description: >- + sell_order_duration is the amount of time of a + Sell-Order from created to expired. + prohibit_sell_duration: + type: string + description: >- + prohibit_sell_duration is the amount of time, + + if the Sell-Order expiration date enters the range + before expiry of Dym-Name, the Sell-Order can not be + placed. + + The logic also applies when the owner accepts a + Buy-Order, the Dym-Name can not be sold. + + This is to prevent the new owner of Dym-Name to sell + it before the Dym-Name expires, + + therefore help reduce the risk of new owner of + Dym-Name not able to renew it on time. + + + For example: + Let say the configured Sell-Order duration is 7 days, the prohibit sell duration is 30 days and you owns a Dym-Name. + We assume your Dym-Name expiration date is Mar 9th, 2024. then 30 days before, from Feb 9th, 2024, + the Dym-Name is prohibited to be sold. + We have some cases: + - If the you places a Sell Order at Feb 2nd, 2024, the Sell-Order supposed to be expired at Feb 9th, 2024, + which is within the prohibited range, so the Sell-Order can not be placed. + - If the you places a Sell Order at Feb 1st, 2024, the Sell-Order supposed to be expired at Feb 8th, 2024, + which is outside the prohibited range, so the Sell-Order can be placed. + - If someone put an offer (Buy-Order) at Feb 1st, 2024, and you have some days to accept it, + exactly from the moment order placed, until end of Feb 8th, 2024, which is outside the prohibited range. + After that, from Feb 9th, it is prohibited to accept the Buy-Order. + enable_trading_name: + type: boolean + description: >- + enable_trading_name is the flag to enable trading of + Dym-Name. + To be used to stop trading of Dym-Name when needed. + enable_trading_alias: + type: boolean + description: >- + enable_trading_alias is the flag to enable trading of + Alias. - Schemes other than `http`, `https` (or the empty scheme) - might be + To be used in the future when Alias trading + implementation is ready - used with implementation specific semantics. + or disable trading of Alias when needed. + description: >- + QueryParamsResponse is response type for the Query/Params RPC + method. + default: + description: An unexpected error response. + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string value: type: string format: byte - description: >- - Must be a valid serialized protocol buffer of the above - specified type. + tags: + - Query + /dymensionxyz/dymension/dymns/resolve: + get: + summary: >- + ResolveDymNameAddresses resolves multiple Dym-Name Addresses to account + address of each pointing to. + description: |- + For example: + - "my-name@dym" => "dym1a..." + - "another.my-name@dym" => "dym1b..." + - "my-name@nim" => "nim1..." + - (extra format) "0x1234...6789@nim" => "nim1..." + - (extra format) "dym1a...@nim" => "nim1..." + operationId: ResolveDymNameAddresses + responses: + '200': + description: A successful response. + schema: + type: object + properties: + resolved_addresses: + type: array + items: + type: object + properties: + address: + type: string + description: address is the input Dym-Name address to resolve. + resolved_address: + type: string + description: resolved_address is the resolved account address. + error: + type: string + description: error is the error that occurred during the resolution. description: >- - `Any` contains an arbitrary serialized protocol buffer + ResultDymNameAddress defines the result of a single Dym-Name + address resolution. + description: >- + resolved_addresses defines the resolved addresses for each + input Dym-Name address. + description: >- + ResolveDymNameAddressesResponse is the response type for the + Query/ResolveDymNameAddresses RPC method. + default: + description: An unexpected error response. + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + value: + type: string + format: byte + parameters: + - name: addresses + description: addresses defines the Dym-Name addresses to resolve. + in: query + required: false + type: array + items: + type: string + collectionFormat: multi + tags: + - Query + /dymensionxyz/dymension/dymns/reverse_resolve: + get: + summary: >- + ReverseResolveAddress resolves multiple account addresses to Dym-Name + Addresses which point to each. + + This function may return multiple possible Dym-Name-Addresses those + point to each of the input address. + description: |- + For example: when we have "my-name@dym" resolves to "dym1a..." + so reverse resolve will return "my-name@dym" when input is "dym1a..." + operationId: ReverseResolveAddress + responses: + '200': + description: A successful response. + schema: + type: object + properties: + result: + type: object + additionalProperties: + type: object + properties: + candidates: + type: array + items: + type: string + description: >- + candidates are the Dym-Name addresses that the input + address resolves to. Take one of them. + error: + type: string + description: error is the error that occurred during the resolution. + description: >- + result defines the reverse resolution result for each input + address. + working_chain_id: + type: string + description: >- + working_chain_id is the chain id used for the reverse + resolution. + description: >- + ReverseResolveAddressResponse is the response type for the + Query/ReverseResolveAddress RPC method. + default: + description: An unexpected error response. + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + value: + type: string + format: byte + parameters: + - name: addresses + description: >- + addresses defines the addresses to reverse resolve. Can be both + bech32 and hex addresses. + in: query + required: false + type: array + items: + type: string + collectionFormat: multi + - name: working_chain_id + description: >- + working_chain_id defines the chain id to use for the reverse + resolution. + + Leave empty to use the host chain id. + in: query + required: false + type: string + tags: + - Query + /dymensionxyz/dymension/dymns/sell_order/{asset_id}: + get: + summary: SellOrder queries the active SO of a Dym-Name/Alias. + operationId: SellOrder + responses: + '200': + description: A successful response. + schema: + type: object + properties: + result: + description: result is the active Sell-Order for the Dym-Name/Alias. + type: object + properties: + asset_id: + type: string + description: asset_id is the Dym-Name/Alias being opened to be sold. + asset_type: + description: >- + asset_type is the type of the asset of the order, is + Dym-Name/Alias. + type: string + enum: + - AT_UNKNOWN + - AT_DYM_NAME + - AT_ALIAS + default: AT_UNKNOWN + expire_at: + type: string + format: int64 + title: expire_at is the last effective date of this SO + min_price: + description: >- + min_price is the minimum price that the owner is willing + to accept for the asset. + type: object + properties: + denom: + type: string + amount: + type: string + sell_price: + description: >- + sell_price is the price that the owner is willing to sell + the Dym-Name/Alias for, + + the SO will be closed when the price is met. + + If the sell price is zero, the SO will be closed when the + expire_at is reached and the highest bidder wins. + type: object + properties: + denom: + type: string + amount: + type: string + highest_bid: + description: >- + highest_bid is the highest bid on the SO, if any. Price + must be greater than or equal to the min_price. + type: object + properties: + bidder: + type: string + description: >- + bidder is the account address of the account which + placed the bid. + price: + description: price is the amount of coin offered by the bidder. + type: object + properties: + denom: + type: string + amount: + type: string + params: + type: array + items: + type: string + description: >- + params is the list of parameters of the bid. + + It is empty for asset type Dym-Name. + + It has one element for asset type Alias, which is the + rollapp_id to assigned for. + description: >- + QuerySellOrderResponse is the response type for the + Query/SellOrder RPC method. + default: + description: An unexpected error response. + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + value: + type: string + format: byte + parameters: + - name: asset_id + description: asset_id is the Dym-Name/Alias to query the active Sell-Order for. + in: path + required: true + type: string + - name: asset_type + description: asset_type can be either "Dym-Name" or "Alias". + in: query + required: false + type: string + tags: + - Query + /dymensionxyz/dymension/dymns/translate_alias/{alias_or_chain_id}: + get: + summary: >- + TranslateAliasOrChainIdToChainId tries to translate an alias/handle to a + chain id. + + If an alias/handle can not be translated to chain-id, it is treated as a + chain-id and returns. + operationId: TranslateAliasOrChainIdToChainId + responses: + '200': + description: A successful response. + schema: + type: object + properties: + chain_id: + type: string + description: >- + chain_id is the chain id that the alias or chain id translates + to. + description: >- + QueryTranslateAliasOrChainIdToChainIdResponse is the response type + for the Query/TranslateAliasOrChainIdToChainId RPC method. + default: + description: An unexpected error response. + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + value: + type: string + format: byte + parameters: + - name: alias_or_chain_id + description: alias_or_chain_id is the alias or chain id to translate. + in: path + required: true + type: string + tags: + - Query + /dymensionxyz/dymension/rollapp/eip155/{eip155}: + get: + summary: Queries a Rollapp by index. + operationId: RollappByEIP155 + responses: + '200': + description: A successful response. + schema: + type: object + properties: + rollapp: + type: object + properties: + rollapp_id: + type: string + description: >- + The unique identifier of the rollapp chain. + + The rollapp_id follows the same standard as cosmos + chain_id. + owner: + type: string + description: owner is the bech32-encoded address of the rollapp owner. + genesis_state: + title: >- + genesis_state is a partial repr of the state the hub can + expect the rollapp to be in upon genesis + type: object + properties: + transfers_enabled: + type: boolean + description: >- + If true, then full usage of the canonical ibc transfer + channel is enabled. + + Note: in v3.1.0 and prior this field marked the + completion of the 'genesis event' + + Keeping and renaming the field enables a seamless + upgrade + https://www.notion.so/dymension/ADR-x-Genesis-Bridge-Phase-2-89769aa551b5440b9ed403a101775ce1?pvs=4#89698384d815435b87393dbe45bc5a74 + + to the new genesis transfer protocol + + Note: if this field is false, ibc transfers may still + be allowed in one or either direction. + channel_id: + type: string + description: >- + channel_id will be set to the canonical IBC channel of the + rollapp. + frozen: + type: boolean + description: >- + frozen is a boolean that indicates if the rollapp is + frozen. + registeredDenoms: + type: array + items: + type: string + title: >- + registeredDenoms is a list of registered denom bases on + this rollapp + bech32_prefix: + type: string + title: unique bech32 prefix + genesis_checksum: + type: string + title: checksum used to verify integrity of the genesis file + metadata: + title: metadata is the rollapp metadata + type: object + properties: + website: + type: string + title: website is the rollapp website + description: + type: string + title: >- + description is the rollapp description. should be + limited to 512 chars + logo_data_uri: + type: string + title: >- + logo_data_uri is a base64 rep with a URI prefix to the + rollapp logo. Size limited + token_logo_data_uri: + type: string + title: >- + token_logo_data_uri is a URI to the native token logo. + Size limited + telegram: + type: string + title: telegram is the rollapp telegram link + x: + type: string + title: x is the rollapp twitter link + genesis_url: + type: string + title: genesis_url has the genesis file + display_name: + type: string + description: >- + display_name is a non semantic name for displaying on + gui etc. Size limited. + tagline: + type: string + description: >- + tagline is a non semantic tagline/catch-phrase. Size + limited. + initial_sequencer: + type: string + description: >- + initial_sequencer is an option to preset one or more + coma-separated bech32-encoded addresses of the + + sequencer(s) that are allowed to initially register and + serve for this rollapp. + + if left empty, no sequencer is allowed to register. + + if set to "*" any sequencer can register. + vm_type: + title: 'vm_type is the type of rollapp machine: EVM or WASM' + type: string + enum: + - Unspecified + - EVM + - WASM + default: Unspecified + sealed: + type: boolean + description: >- + sealed is a boolean that indicates if the immutable fields + are no longer updatable. + liveness_event_height: + type: string + format: int64 + title: >- + LivenessEventHeight is the height of an upcoming liveness + event (slash or jail) + + 0 means not set + last_state_update_height: + type: string + format: int64 + title: >- + The LastStateUpdateHeight HUB height when the last state + update was received + title: >- + Rollapp defines a rollapp object. First, the RollApp is + created and then + + sequencers can be created and attached. The RollApp is + identified by rollappId + summary: + type: object + properties: + rollappId: + type: string + description: >- + The unique identifier of the rollapp chain. + + The rollappId follows the same standard as cosmos + chain_id. + latestStateIndex: + description: Defines the index of the last rollapp UpdateState. + type: object + properties: + rollappId: + type: string + title: >- + rollappId is the rollapp that the sequencer belongs to + and asking to update + + it used to identify the what rollapp a StateInfo + belongs + + The rollappId follows the same standard as cosmos + chain_id + index: + type: string + format: uint64 + title: >- + index is a sequential increasing number, updating on + each + + state update used for indexing to a specific state + info, the first index is 1 + title: >- + StateInfoIndex is the data used for indexing and + retrieving a StateInfo + + it updated and saved with every UpdateState in StateInfo. + + We use the this structure also for: + + 1. LatestStateInfoIndex which defines the rollapps' + current (latest) index of the last UpdateState + + 2. LatestFinalizedStateIndex which defines the rollapps' + current (latest) index of the latest StateInfo that was + finalized + latestFinalizedStateIndex: + description: >- + Defines the index of the last rollapp UpdateState that was + finalized. + type: object + properties: + rollappId: + type: string + title: >- + rollappId is the rollapp that the sequencer belongs to + and asking to update + + it used to identify the what rollapp a StateInfo + belongs + + The rollappId follows the same standard as cosmos + chain_id + index: + type: string + format: uint64 + title: >- + index is a sequential increasing number, updating on + each + + state update used for indexing to a specific state + info, the first index is 1 + title: >- + StateInfoIndex is the data used for indexing and + retrieving a StateInfo + + it updated and saved with every UpdateState in StateInfo. + + We use the this structure also for: + + 1. LatestStateInfoIndex which defines the rollapps' + current (latest) index of the last UpdateState + + 2. LatestFinalizedStateIndex which defines the rollapps' + current (latest) index of the latest StateInfo that was + finalized + latestHeight: + type: string + format: uint64 + latestFinalizedHeight: + type: string + format: uint64 + title: Rollapp summary is a compact representation of Rollapp + default: + description: An unexpected error response. + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + value: + type: string + format: byte + parameters: + - name: eip155 + in: path + required: true + type: string + format: uint64 + tags: + - Query + /dymensionxyz/dymension/rollapp/latest_height/{rollappId}: + get: + summary: Queries a LatestHeight by rollapp-id. + operationId: LatestHeight + responses: + '200': + description: A successful response. + schema: + type: object + properties: + height: + type: string + format: uint64 + default: + description: An unexpected error response. + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + value: + type: string + format: byte + parameters: + - name: rollappId + in: path + required: true + type: string + - name: finalized + in: query + required: false + type: boolean + tags: + - Query + /dymensionxyz/dymension/rollapp/latest_state_index/{rollappId}: + get: + summary: Queries a LatestStateIndex by rollapp-id. + operationId: LatestStateIndex + responses: + '200': + description: A successful response. + schema: + type: object + properties: + stateIndex: + type: object + properties: + rollappId: + type: string + title: >- + rollappId is the rollapp that the sequencer belongs to and + asking to update + + it used to identify the what rollapp a StateInfo belongs + + The rollappId follows the same standard as cosmos chain_id + index: + type: string + format: uint64 + title: >- + index is a sequential increasing number, updating on each + + state update used for indexing to a specific state info, + the first index is 1 + title: >- + StateInfoIndex is the data used for indexing and retrieving a + StateInfo + + it updated and saved with every UpdateState in StateInfo. + + We use the this structure also for: + + 1. LatestStateInfoIndex which defines the rollapps' current + (latest) index of the last UpdateState + + 2. LatestFinalizedStateIndex which defines the rollapps' + current (latest) index of the latest StateInfo that was + finalized + default: + description: An unexpected error response. + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + value: + type: string + format: byte + parameters: + - name: rollappId + in: path + required: true + type: string + - name: finalized + in: query + required: false + type: boolean + tags: + - Query + /dymensionxyz/dymension/rollapp/params: + get: + summary: Parameters queries the parameters of the module. + operationId: RollAppParams + responses: + '200': + description: A successful response. + schema: + type: object + properties: + params: + description: params holds all the parameters of this module. + type: object + properties: + dispute_period_in_blocks: + type: string + format: uint64 + title: |- + dispute_period_in_blocks the number of blocks it takes + to change a status of a state from received to finalized. + during that period, any user could submit fraud proof + liveness_slash_blocks: + type: string + format: uint64 + title: >- + The time (num hub blocks) a sequencer has to post a block, + before he will be slashed + liveness_slash_interval: + type: string + format: uint64 + title: >- + The min gap (num hub blocks) between a sequence of slashes + if the sequencer continues to be down + liveness_jail_blocks: + type: string + format: uint64 + title: >- + The time (num hub blocks) a sequencer can be down after + which he will be jailed rather than slashed + description: >- + QueryParamsResponse is response type for the Query/Params RPC + method. + default: + description: An unexpected error response. + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + value: + type: string + format: byte + tags: + - Query + /dymensionxyz/dymension/rollapp/rollapp: + get: + summary: Queries a list of Rollapp items. + operationId: RollappAll + responses: + '200': + description: A successful response. + schema: + type: object + properties: + rollapp: + type: array + items: + type: object + properties: + rollapp: + type: object + properties: + rollapp_id: + type: string + description: >- + The unique identifier of the rollapp chain. + + The rollapp_id follows the same standard as cosmos + chain_id. + owner: + type: string + description: >- + owner is the bech32-encoded address of the rollapp + owner. + genesis_state: + title: >- + genesis_state is a partial repr of the state the hub + can expect the rollapp to be in upon genesis + type: object + properties: + transfers_enabled: + type: boolean + description: >- + If true, then full usage of the canonical ibc + transfer channel is enabled. + + Note: in v3.1.0 and prior this field marked the + completion of the 'genesis event' + + Keeping and renaming the field enables a + seamless upgrade + https://www.notion.so/dymension/ADR-x-Genesis-Bridge-Phase-2-89769aa551b5440b9ed403a101775ce1?pvs=4#89698384d815435b87393dbe45bc5a74 + + to the new genesis transfer protocol + + Note: if this field is false, ibc transfers may + still be allowed in one or either direction. + channel_id: + type: string + description: >- + channel_id will be set to the canonical IBC channel + of the rollapp. + frozen: + type: boolean + description: >- + frozen is a boolean that indicates if the rollapp is + frozen. + registeredDenoms: + type: array + items: + type: string + title: >- + registeredDenoms is a list of registered denom bases + on this rollapp + bech32_prefix: + type: string + title: unique bech32 prefix + genesis_checksum: + type: string + title: >- + checksum used to verify integrity of the genesis + file + metadata: + title: metadata is the rollapp metadata + type: object + properties: + website: + type: string + title: website is the rollapp website + description: + type: string + title: >- + description is the rollapp description. should + be limited to 512 chars + logo_data_uri: + type: string + title: >- + logo_data_uri is a base64 rep with a URI prefix + to the rollapp logo. Size limited + token_logo_data_uri: + type: string + title: >- + token_logo_data_uri is a URI to the native token + logo. Size limited + telegram: + type: string + title: telegram is the rollapp telegram link + x: + type: string + title: x is the rollapp twitter link + genesis_url: + type: string + title: genesis_url has the genesis file + display_name: + type: string + description: >- + display_name is a non semantic name for + displaying on gui etc. Size limited. + tagline: + type: string + description: >- + tagline is a non semantic tagline/catch-phrase. + Size limited. + initial_sequencer: + type: string + description: >- + initial_sequencer is an option to preset one or more + coma-separated bech32-encoded addresses of the + + sequencer(s) that are allowed to initially register + and serve for this rollapp. + + if left empty, no sequencer is allowed to register. + + if set to "*" any sequencer can register. + vm_type: + title: 'vm_type is the type of rollapp machine: EVM or WASM' + type: string + enum: + - Unspecified + - EVM + - WASM + default: Unspecified + sealed: + type: boolean + description: >- + sealed is a boolean that indicates if the immutable + fields are no longer updatable. + liveness_event_height: + type: string + format: int64 + title: >- + LivenessEventHeight is the height of an upcoming + liveness event (slash or jail) + + 0 means not set + last_state_update_height: + type: string + format: int64 + title: >- + The LastStateUpdateHeight HUB height when the last + state update was received + title: >- + Rollapp defines a rollapp object. First, the RollApp is + created and then + + sequencers can be created and attached. The RollApp is + identified by rollappId + summary: + type: object + properties: + rollappId: + type: string + description: >- + The unique identifier of the rollapp chain. + + The rollappId follows the same standard as cosmos + chain_id. + latestStateIndex: + description: Defines the index of the last rollapp UpdateState. + type: object + properties: + rollappId: + type: string + title: >- + rollappId is the rollapp that the sequencer + belongs to and asking to update + + it used to identify the what rollapp a StateInfo + belongs + + The rollappId follows the same standard as + cosmos chain_id + index: + type: string + format: uint64 + title: >- + index is a sequential increasing number, + updating on each + + state update used for indexing to a specific + state info, the first index is 1 + title: >- + StateInfoIndex is the data used for indexing and + retrieving a StateInfo + + it updated and saved with every UpdateState in + StateInfo. + + We use the this structure also for: + + 1. LatestStateInfoIndex which defines the rollapps' + current (latest) index of the last UpdateState + + 2. LatestFinalizedStateIndex which defines the + rollapps' current (latest) index of the latest + StateInfo that was finalized + latestFinalizedStateIndex: + description: >- + Defines the index of the last rollapp UpdateState + that was finalized. + type: object + properties: + rollappId: + type: string + title: >- + rollappId is the rollapp that the sequencer + belongs to and asking to update + + it used to identify the what rollapp a StateInfo + belongs + + The rollappId follows the same standard as + cosmos chain_id + index: + type: string + format: uint64 + title: >- + index is a sequential increasing number, + updating on each + + state update used for indexing to a specific + state info, the first index is 1 + title: >- + StateInfoIndex is the data used for indexing and + retrieving a StateInfo + + it updated and saved with every UpdateState in + StateInfo. + + We use the this structure also for: + + 1. LatestStateInfoIndex which defines the rollapps' + current (latest) index of the last UpdateState + + 2. LatestFinalizedStateIndex which defines the + rollapps' current (latest) index of the latest + StateInfo that was finalized + latestHeight: + type: string + format: uint64 + latestFinalizedHeight: + type: string + format: uint64 + title: Rollapp summary is a compact representation of Rollapp + pagination: + type: object + properties: + next_key: + type: string + format: byte + description: |- + next_key is the key to be passed to PageRequest.key to + query the next page most efficiently. It will be empty if + there are no more results. + total: + type: string + format: uint64 + title: >- + total is total number of results available if + PageRequest.count_total + + was set, its value is undefined otherwise + description: >- + PageResponse is to be embedded in gRPC response messages where + the + + corresponding request message has used PageRequest. + + message SomeResponse { + repeated Bar results = 1; + PageResponse page = 2; + } + default: + description: An unexpected error response. + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + value: + type: string + format: byte + parameters: + - name: pagination.key + description: |- + key is a value returned in PageResponse.next_key to begin + querying the next page most efficiently. Only one of offset or key + should be set. + in: query + required: false + type: string + format: byte + - name: pagination.offset + description: >- + offset is a numeric offset that can be used when key is unavailable. + + It is less efficient than using key. Only one of offset or key + should + + be set. + in: query + required: false + type: string + format: uint64 + - name: pagination.limit + description: >- + limit is the total number of results to be returned in the result + page. + + If left empty it will default to a value to be set by each app. + in: query + required: false + type: string + format: uint64 + - name: pagination.count_total + description: >- + count_total is set to true to indicate that the result set should + include + + a count of the total number of items available for pagination in + UIs. + + count_total is only respected when offset is used. It is ignored + when key + + is set. + in: query + required: false + type: boolean + - name: pagination.reverse + description: >- + reverse is set to true if results are to be returned in the + descending order. + + + Since: cosmos-sdk 0.43 + in: query + required: false + type: boolean + tags: + - Query + /dymensionxyz/dymension/rollapp/rollapp/{rollappId}: + get: + summary: Queries a Rollapp by index. + operationId: Rollapp + responses: + '200': + description: A successful response. + schema: + type: object + properties: + rollapp: + type: object + properties: + rollapp_id: + type: string + description: >- + The unique identifier of the rollapp chain. + + The rollapp_id follows the same standard as cosmos + chain_id. + owner: + type: string + description: owner is the bech32-encoded address of the rollapp owner. + genesis_state: + title: >- + genesis_state is a partial repr of the state the hub can + expect the rollapp to be in upon genesis + type: object + properties: + transfers_enabled: + type: boolean + description: >- + If true, then full usage of the canonical ibc transfer + channel is enabled. + + Note: in v3.1.0 and prior this field marked the + completion of the 'genesis event' + + Keeping and renaming the field enables a seamless + upgrade + https://www.notion.so/dymension/ADR-x-Genesis-Bridge-Phase-2-89769aa551b5440b9ed403a101775ce1?pvs=4#89698384d815435b87393dbe45bc5a74 + + to the new genesis transfer protocol + + Note: if this field is false, ibc transfers may still + be allowed in one or either direction. + channel_id: + type: string + description: >- + channel_id will be set to the canonical IBC channel of the + rollapp. + frozen: + type: boolean + description: >- + frozen is a boolean that indicates if the rollapp is + frozen. + registeredDenoms: + type: array + items: + type: string + title: >- + registeredDenoms is a list of registered denom bases on + this rollapp + bech32_prefix: + type: string + title: unique bech32 prefix + genesis_checksum: + type: string + title: checksum used to verify integrity of the genesis file + metadata: + title: metadata is the rollapp metadata + type: object + properties: + website: + type: string + title: website is the rollapp website + description: + type: string + title: >- + description is the rollapp description. should be + limited to 512 chars + logo_data_uri: + type: string + title: >- + logo_data_uri is a base64 rep with a URI prefix to the + rollapp logo. Size limited + token_logo_data_uri: + type: string + title: >- + token_logo_data_uri is a URI to the native token logo. + Size limited + telegram: + type: string + title: telegram is the rollapp telegram link + x: + type: string + title: x is the rollapp twitter link + genesis_url: + type: string + title: genesis_url has the genesis file + display_name: + type: string + description: >- + display_name is a non semantic name for displaying on + gui etc. Size limited. + tagline: + type: string + description: >- + tagline is a non semantic tagline/catch-phrase. Size + limited. + initial_sequencer: + type: string + description: >- + initial_sequencer is an option to preset one or more + coma-separated bech32-encoded addresses of the + + sequencer(s) that are allowed to initially register and + serve for this rollapp. + + if left empty, no sequencer is allowed to register. + + if set to "*" any sequencer can register. + vm_type: + title: 'vm_type is the type of rollapp machine: EVM or WASM' + type: string + enum: + - Unspecified + - EVM + - WASM + default: Unspecified + sealed: + type: boolean + description: >- + sealed is a boolean that indicates if the immutable fields + are no longer updatable. + liveness_event_height: + type: string + format: int64 + title: >- + LivenessEventHeight is the height of an upcoming liveness + event (slash or jail) + + 0 means not set + last_state_update_height: + type: string + format: int64 + title: >- + The LastStateUpdateHeight HUB height when the last state + update was received + title: >- + Rollapp defines a rollapp object. First, the RollApp is + created and then + + sequencers can be created and attached. The RollApp is + identified by rollappId + summary: + type: object + properties: + rollappId: + type: string + description: >- + The unique identifier of the rollapp chain. + + The rollappId follows the same standard as cosmos + chain_id. + latestStateIndex: + description: Defines the index of the last rollapp UpdateState. + type: object + properties: + rollappId: + type: string + title: >- + rollappId is the rollapp that the sequencer belongs to + and asking to update + + it used to identify the what rollapp a StateInfo + belongs + + The rollappId follows the same standard as cosmos + chain_id + index: + type: string + format: uint64 + title: >- + index is a sequential increasing number, updating on + each + + state update used for indexing to a specific state + info, the first index is 1 + title: >- + StateInfoIndex is the data used for indexing and + retrieving a StateInfo + + it updated and saved with every UpdateState in StateInfo. + + We use the this structure also for: + + 1. LatestStateInfoIndex which defines the rollapps' + current (latest) index of the last UpdateState + + 2. LatestFinalizedStateIndex which defines the rollapps' + current (latest) index of the latest StateInfo that was + finalized + latestFinalizedStateIndex: + description: >- + Defines the index of the last rollapp UpdateState that was + finalized. + type: object + properties: + rollappId: + type: string + title: >- + rollappId is the rollapp that the sequencer belongs to + and asking to update + + it used to identify the what rollapp a StateInfo + belongs + + The rollappId follows the same standard as cosmos + chain_id + index: + type: string + format: uint64 + title: >- + index is a sequential increasing number, updating on + each + + state update used for indexing to a specific state + info, the first index is 1 + title: >- + StateInfoIndex is the data used for indexing and + retrieving a StateInfo + + it updated and saved with every UpdateState in StateInfo. + + We use the this structure also for: + + 1. LatestStateInfoIndex which defines the rollapps' + current (latest) index of the last UpdateState + + 2. LatestFinalizedStateIndex which defines the rollapps' + current (latest) index of the latest StateInfo that was + finalized + latestHeight: + type: string + format: uint64 + latestFinalizedHeight: + type: string + format: uint64 + title: Rollapp summary is a compact representation of Rollapp + default: + description: An unexpected error response. + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + value: + type: string + format: byte + parameters: + - name: rollappId + in: path + required: true + type: string + tags: + - Query + /dymensionxyz/dymension/rollapp/state_info/{rollappId}/{index}: + get: + summary: Queries a StateInfo by index. + operationId: StateInfo + responses: + '200': + description: A successful response. + schema: + type: object + properties: + stateInfo: + type: object + properties: + stateInfoIndex: + type: object + properties: + rollappId: + type: string + title: >- + rollappId is the rollapp that the sequencer belongs to + and asking to update + + it used to identify the what rollapp a StateInfo + belongs + + The rollappId follows the same standard as cosmos + chain_id + index: + type: string + format: uint64 + title: >- + index is a sequential increasing number, updating on + each + + state update used for indexing to a specific state + info, the first index is 1 + title: >- + StateInfoIndex is the data used for indexing and + retrieving a StateInfo + + it updated and saved with every UpdateState in StateInfo. + + We use the this structure also for: + + 1. LatestStateInfoIndex which defines the rollapps' + current (latest) index of the last UpdateState + + 2. LatestFinalizedStateIndex which defines the rollapps' + current (latest) index of the latest StateInfo that was + finalized + sequencer: + type: string + title: >- + sequencer is the bech32-encoded address of the sequencer + sent the update + startHeight: + type: string + format: uint64 + title: >- + startHeight is the block height of the first block in the + batch + numBlocks: + type: string + format: uint64 + title: >- + numBlocks is the number of blocks included in this batch + update + DAPath: + type: string + title: DAPath is the description of the location on the DA layer + creationHeight: + type: string + format: uint64 + title: >- + creationHeight is the height at which the UpdateState took + place + status: + title: status is the status of the state update + type: string + enum: + - PENDING + - FINALIZED + - REVERTED + default: PENDING + BDs: + title: >- + BDs is a list of block description objects (one per block) + + the list must be ordered by height, starting from + startHeight to startHeight+numBlocks-1 + type: object + properties: + BD: + type: array + items: + type: object + properties: + height: + type: string + format: uint64 + title: height is the height of the block + stateRoot: + type: string + format: byte + title: >- + stateRoot is a 32 byte array of the hash of the + block (state root of the block) + description: >- + BlockDescriptor defines a single rollapp chain block + description. + description: BlockDescriptors defines list of BlockDescriptor. + description: StateInfo defines a rollapps' state. + default: + description: An unexpected error response. + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + value: + type: string + format: byte + parameters: + - name: rollappId + in: path + required: true + type: string + - name: index + in: path + required: true + type: string + format: uint64 + - name: height + in: query + required: false + type: string + format: uint64 + - name: finalized + in: query + required: false + type: boolean + tags: + - Query + /dymensionxyz/dymension/sequencer/params: + get: + summary: Parameters queries the parameters of the module. + operationId: SequencerParams + responses: + '200': + description: A successful response. + schema: + type: object + properties: + params: + description: params holds all the parameters of this module. + type: object + properties: + min_bond: + type: object + properties: + denom: + type: string + amount: + type: string + description: >- + Coin defines a token with a denomination and an amount. + + + NOTE: The amount field is an Int which implements the + custom method + + signatures required by gogoproto. + unbonding_time: + type: string + description: unbonding_time is the time duration of unbonding. + liveness_slash_multiplier: + type: string + description: >- + LivenessSlashMultiplier multiplies with the tokens of the + slashed sequencer to compute the burn amount. + description: >- + QueryParamsResponse is response type for the Query/Params RPC + method. + default: + description: An unexpected error response. + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of + the serialized + + protocol buffer message. This string must contain at + least + + one "/" character. The last segment of the URL's path + must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in + a canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary + all types that they + + expect it to use in the context of Any. However, for + URLs which use the + + scheme `http`, `https`, or no scheme, one can optionally + set up a type + + server that maps type URLs to message definitions as + follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based + on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in + the official + + protobuf release, and it is not used for type URLs + beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) + might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer message along with a URL that describes the type of the serialized message. @@ -2932,12 +4902,12 @@ paths: items: type: object properties: - sequencerAddress: + address: type: string description: >- - sequencerAddress is the bech32-encoded address of the - sequencer account which is the account that the message - was sent from. + address is the bech32-encoded address of the sequencer + account which is the account that the message was sent + from. dymintPubKey: type: object properties: @@ -3125,9 +5095,9 @@ paths: description: >- rollappId defines the rollapp to which the sequencer belongs. - description: + metadata: description: >- - description defines the descriptive terms for the + metadata defines the extra information for the sequencer. type: object properties: @@ -3136,22 +5106,73 @@ paths: description: >- moniker defines a human-readable name for the sequencer. - identity: + details: type: string - description: >- - identity defines an optional identity signature (ex. - UPort or Keybase). - website: + description: details define other optional details. + p2p_seeds: + type: array + items: + type: string + title: bootstrap nodes list + rpcs: + type: array + items: + type: string + title: RPCs list + evm_rpcs: + type: array + items: + type: string + title: evm RPCs list + rest_api_urls: + type: array + items: + type: string + title: REST API URLs + explorer_url: type: string - description: website defines an optional website link. - securityContact: + title: block explorer URL + genesis_urls: + type: array + items: + type: string + title: genesis URLs + contact_details: + title: contact details + type: object + properties: + website: + type: string + title: website URL + telegram: + type: string + title: telegram link + x: + type: string + title: twitter link + extra_data: type: string - description: >- - securityContact defines an optional email for - security contact. - details: + format: byte + title: json dump the sequencer can add (limited by size) + snapshots: + type: array + items: + type: object + properties: + snapshot_url: + type: string + title: the snapshot url + height: + type: string + format: uint64 + title: The snapshot height + checksum: + type: string + title: sha-256 checksum value for the snapshot file + title: snapshots of the sequencer + gas_price: type: string - description: details define other optional details. + title: gas_price defines the value for each gas unit jailed: type: boolean description: >- @@ -3508,12 +5529,12 @@ paths: sequencer: type: object properties: - sequencerAddress: + address: type: string description: >- - sequencerAddress is the bech32-encoded address of the - sequencer account which is the account that the message - was sent from. + address is the bech32-encoded address of the sequencer + account which is the account that the message was sent + from. dymintPubKey: type: object properties: @@ -3699,10 +5720,8 @@ paths: description: >- rollappId defines the rollapp to which the sequencer belongs. - description: - description: >- - description defines the descriptive terms for the - sequencer. + metadata: + description: metadata defines the extra information for the sequencer. type: object properties: moniker: @@ -3710,22 +5729,73 @@ paths: description: >- moniker defines a human-readable name for the sequencer. - identity: + details: type: string - description: >- - identity defines an optional identity signature (ex. - UPort or Keybase). - website: + description: details define other optional details. + p2p_seeds: + type: array + items: + type: string + title: bootstrap nodes list + rpcs: + type: array + items: + type: string + title: RPCs list + evm_rpcs: + type: array + items: + type: string + title: evm RPCs list + rest_api_urls: + type: array + items: + type: string + title: REST API URLs + explorer_url: type: string - description: website defines an optional website link. - securityContact: + title: block explorer URL + genesis_urls: + type: array + items: + type: string + title: genesis URLs + contact_details: + title: contact details + type: object + properties: + website: + type: string + title: website URL + telegram: + type: string + title: telegram link + x: + type: string + title: twitter link + extra_data: type: string - description: >- - securityContact defines an optional email for security - contact. - details: + format: byte + title: json dump the sequencer can add (limited by size) + snapshots: + type: array + items: + type: object + properties: + snapshot_url: + type: string + title: the snapshot url + height: + type: string + format: uint64 + title: The snapshot height + checksum: + type: string + title: sha-256 checksum value for the snapshot file + title: snapshots of the sequencer + gas_price: type: string - description: details define other optional details. + title: gas_price defines the value for each gas unit jailed: type: boolean description: >- @@ -4003,12 +6073,12 @@ paths: items: type: object properties: - sequencerAddress: + address: type: string description: >- - sequencerAddress is the bech32-encoded address of the - sequencer account which is the account that the message - was sent from. + address is the bech32-encoded address of the sequencer + account which is the account that the message was sent + from. dymintPubKey: type: object properties: @@ -4196,9 +6266,9 @@ paths: description: >- rollappId defines the rollapp to which the sequencer belongs. - description: + metadata: description: >- - description defines the descriptive terms for the + metadata defines the extra information for the sequencer. type: object properties: @@ -4207,22 +6277,73 @@ paths: description: >- moniker defines a human-readable name for the sequencer. - identity: + details: type: string - description: >- - identity defines an optional identity signature (ex. - UPort or Keybase). - website: + description: details define other optional details. + p2p_seeds: + type: array + items: + type: string + title: bootstrap nodes list + rpcs: + type: array + items: + type: string + title: RPCs list + evm_rpcs: + type: array + items: + type: string + title: evm RPCs list + rest_api_urls: + type: array + items: + type: string + title: REST API URLs + explorer_url: type: string - description: website defines an optional website link. - securityContact: + title: block explorer URL + genesis_urls: + type: array + items: + type: string + title: genesis URLs + contact_details: + title: contact details + type: object + properties: + website: + type: string + title: website URL + telegram: + type: string + title: telegram link + x: + type: string + title: twitter link + extra_data: type: string - description: >- - securityContact defines an optional email for - security contact. - details: + format: byte + title: json dump the sequencer can add (limited by size) + snapshots: + type: array + items: + type: object + properties: + snapshot_url: + type: string + title: the snapshot url + height: + type: string + format: uint64 + title: The snapshot height + checksum: + type: string + title: sha-256 checksum value for the snapshot file + title: snapshots of the sequencer + gas_price: type: string - description: details define other optional details. + title: gas_price defines the value for each gas unit jailed: type: boolean description: >- @@ -4501,12 +6622,12 @@ paths: items: type: object properties: - sequencerAddress: + address: type: string description: >- - sequencerAddress is the bech32-encoded address of the - sequencer account which is the account that the message - was sent from. + address is the bech32-encoded address of the sequencer + account which is the account that the message was sent + from. dymintPubKey: type: object properties: @@ -4694,9 +6815,9 @@ paths: description: >- rollappId defines the rollapp to which the sequencer belongs. - description: + metadata: description: >- - description defines the descriptive terms for the + metadata defines the extra information for the sequencer. type: object properties: @@ -4705,22 +6826,73 @@ paths: description: >- moniker defines a human-readable name for the sequencer. - identity: + details: type: string - description: >- - identity defines an optional identity signature (ex. - UPort or Keybase). - website: + description: details define other optional details. + p2p_seeds: + type: array + items: + type: string + title: bootstrap nodes list + rpcs: + type: array + items: + type: string + title: RPCs list + evm_rpcs: + type: array + items: + type: string + title: evm RPCs list + rest_api_urls: + type: array + items: + type: string + title: REST API URLs + explorer_url: type: string - description: website defines an optional website link. - securityContact: + title: block explorer URL + genesis_urls: + type: array + items: + type: string + title: genesis URLs + contact_details: + title: contact details + type: object + properties: + website: + type: string + title: website URL + telegram: + type: string + title: telegram link + x: + type: string + title: twitter link + extra_data: type: string - description: >- - securityContact defines an optional email for - security contact. - details: + format: byte + title: json dump the sequencer can add (limited by size) + snapshots: + type: array + items: + type: object + properties: + snapshot_url: + type: string + title: the snapshot url + height: + type: string + format: uint64 + title: The snapshot height + checksum: + type: string + title: sha-256 checksum value for the snapshot file + title: snapshots of the sequencer + gas_price: type: string - description: details define other optional details. + title: gas_price defines the value for each gas unit jailed: type: boolean description: >- @@ -5098,6 +7270,11 @@ paths: title: >- distributed_coins are coins that have been distributed already + sponsored: + type: boolean + description: >- + Sponsored indicates if the stream is based on the + sponsorship distribution. description: >- Stream is an object that stores and distributes yields to recipients who @@ -5371,6 +7548,11 @@ paths: title: >- distributed_coins are coins that have been distributed already + sponsored: + type: boolean + description: >- + Sponsored indicates if the stream is based on the + sponsorship distribution. description: >- Stream is an object that stores and distributes yields to recipients who @@ -5517,6 +7699,11 @@ paths: title: >- distributed_coins are coins that have been distributed already + sponsored: + type: boolean + description: >- + Sponsored indicates if the stream is based on the + sponsorship distribution. description: >- Stream is an object that stores and distributes yields to recipients who @@ -5742,6 +7929,11 @@ paths: title: >- distributed_coins are coins that have been distributed already + sponsored: + type: boolean + description: >- + Sponsored indicates if the stream is based on the + sponsorship distribution. description: >- Stream is an object that stores and distributes yields to recipients who @@ -18324,308 +20516,367 @@ paths: format: byte parameters: - name: denom - in: query - required: false - type: string - tags: - - Query - /ibc/apps/transfer/v1/channels/{channel_id}/ports/{port_id}/escrow_address: - get: - summary: >- - EscrowAddress returns the escrow address for a particular port and - channel id. - operationId: EscrowAddress - responses: - '200': - description: A successful response. - schema: - type: object - properties: - escrow_address: - type: string - title: the escrow account address - description: >- - QueryEscrowAddressResponse is the response type of the - EscrowAddress RPC method. - default: - description: An unexpected error response. - schema: - type: object - properties: - error: - type: string - code: - type: integer - format: int32 - message: - type: string - details: - type: array - items: - type: object - properties: - type_url: - type: string - value: - type: string - format: byte - parameters: - - name: channel_id - description: unique channel identifier - in: path - required: true - type: string - - name: port_id - description: unique port identifier - in: path - required: true - type: string - tags: - - Query - /ibc/apps/transfer/v1/denom_hashes/{trace}: - get: - summary: DenomHash queries a denomination hash information. - operationId: DenomHash - responses: - '200': - description: A successful response. - schema: - type: object - properties: - hash: - type: string - description: hash (in hex format) of the denomination trace information. - description: >- - QueryDenomHashResponse is the response type for the - Query/DenomHash RPC - - method. - default: - description: An unexpected error response. - schema: - type: object - properties: - error: - type: string - code: - type: integer - format: int32 - message: - type: string - details: - type: array - items: - type: object - properties: - type_url: - type: string - value: - type: string - format: byte - parameters: - - name: trace - description: The denomination trace ([port_id]/[channel_id])+/[denom] - in: path - required: true - type: string - tags: - - Query - /ibc/apps/transfer/v1/denom_traces: - get: - summary: DenomTraces queries all denomination traces. - operationId: DenomTraces - responses: - '200': - description: A successful response. - schema: - type: object - properties: - denom_traces: - type: array - items: - type: object - properties: - path: - type: string - description: >- - path defines the chain of port/channel identifiers used - for tracing the - - source of the fungible token. - base_denom: - type: string - description: base denomination of the relayed fungible token. - description: >- - DenomTrace contains the base denomination for ICS20 fungible - tokens and the - - source tracing information path. - description: denom_traces returns all denominations trace information. - pagination: - description: pagination defines the pagination in the response. - type: object - properties: - next_key: - type: string - format: byte - description: |- - next_key is the key to be passed to PageRequest.key to - query the next page most efficiently. It will be empty if - there are no more results. - total: - type: string - format: uint64 - title: >- - total is total number of results available if - PageRequest.count_total - - was set, its value is undefined otherwise - description: >- - QueryConnectionsResponse is the response type for the - Query/DenomTraces RPC - - method. - default: - description: An unexpected error response. - schema: - type: object - properties: - error: - type: string - code: - type: integer - format: int32 - message: - type: string - details: - type: array - items: - type: object - properties: - type_url: - type: string - value: - type: string - format: byte - parameters: - - name: pagination.key - description: |- - key is a value returned in PageResponse.next_key to begin - querying the next page most efficiently. Only one of offset or key - should be set. - in: query - required: false - type: string - format: byte - - name: pagination.offset - description: >- - offset is a numeric offset that can be used when key is unavailable. - - It is less efficient than using key. Only one of offset or key - should - - be set. - in: query - required: false - type: string - format: uint64 - - name: pagination.limit - description: >- - limit is the total number of results to be returned in the result - page. - - If left empty it will default to a value to be set by each app. - in: query - required: false - type: string - format: uint64 - - name: pagination.count_total - description: >- - count_total is set to true to indicate that the result set should - include - - a count of the total number of items available for pagination in - UIs. - - count_total is only respected when offset is used. It is ignored - when key - - is set. - in: query - required: false - type: boolean - - name: pagination.reverse - description: >- - reverse is set to true if results are to be returned in the - descending order. - - - Since: cosmos-sdk 0.43 - in: query - required: false - type: boolean - tags: - - Query - /ibc/apps/transfer/v1/denom_traces/{hash}: - get: - summary: DenomTrace queries a denomination trace information. - operationId: DenomTrace - responses: - '200': - description: A successful response. - schema: - type: object - properties: - denom_trace: - type: object - properties: - path: - type: string - description: >- - path defines the chain of port/channel identifiers used - for tracing the - - source of the fungible token. - base_denom: - type: string - description: base denomination of the relayed fungible token. - description: >- - DenomTrace contains the base denomination for ICS20 fungible - tokens and the - - source tracing information path. - description: >- - QueryDenomTraceResponse is the response type for the - Query/DenomTrace RPC - - method. - default: - description: An unexpected error response. - schema: - type: object - properties: - error: - type: string - code: - type: integer - format: int32 - message: - type: string - details: - type: array - items: - type: object - properties: - type_url: - type: string - value: - type: string - format: byte - parameters: - - name: hash - description: >- - hash (in hex format) or denom (full denom with ibc prefix) of the - denomination trace information. + in: query + required: false + type: string + tags: + - Query + /ibc/apps/transfer/v1/channels/{channel_id}/ports/{port_id}/escrow_address: + get: + summary: >- + EscrowAddress returns the escrow address for a particular port and + channel id. + operationId: EscrowAddress + responses: + '200': + description: A successful response. + schema: + type: object + properties: + escrow_address: + type: string + title: the escrow account address + description: >- + QueryEscrowAddressResponse is the response type of the + EscrowAddress RPC method. + default: + description: An unexpected error response. + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + value: + type: string + format: byte + parameters: + - name: channel_id + description: unique channel identifier + in: path + required: true + type: string + - name: port_id + description: unique port identifier + in: path + required: true + type: string + tags: + - Query + /ibc/apps/transfer/v1/denom_hashes/{trace}: + get: + summary: DenomHash queries a denomination hash information. + operationId: DenomHash + responses: + '200': + description: A successful response. + schema: + type: object + properties: + hash: + type: string + description: hash (in hex format) of the denomination trace information. + description: >- + QueryDenomHashResponse is the response type for the + Query/DenomHash RPC + + method. + default: + description: An unexpected error response. + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + value: + type: string + format: byte + parameters: + - name: trace + description: The denomination trace ([port_id]/[channel_id])+/[denom] + in: path + required: true + type: string + tags: + - Query + /ibc/apps/transfer/v1/denom_traces: + get: + summary: DenomTraces queries all denomination traces. + operationId: DenomTraces + responses: + '200': + description: A successful response. + schema: + type: object + properties: + denom_traces: + type: array + items: + type: object + properties: + path: + type: string + description: >- + path defines the chain of port/channel identifiers used + for tracing the + + source of the fungible token. + base_denom: + type: string + description: base denomination of the relayed fungible token. + description: >- + DenomTrace contains the base denomination for ICS20 fungible + tokens and the + + source tracing information path. + description: denom_traces returns all denominations trace information. + pagination: + description: pagination defines the pagination in the response. + type: object + properties: + next_key: + type: string + format: byte + description: |- + next_key is the key to be passed to PageRequest.key to + query the next page most efficiently. It will be empty if + there are no more results. + total: + type: string + format: uint64 + title: >- + total is total number of results available if + PageRequest.count_total + + was set, its value is undefined otherwise + description: >- + QueryConnectionsResponse is the response type for the + Query/DenomTraces RPC + + method. + default: + description: An unexpected error response. + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + value: + type: string + format: byte + parameters: + - name: pagination.key + description: |- + key is a value returned in PageResponse.next_key to begin + querying the next page most efficiently. Only one of offset or key + should be set. + in: query + required: false + type: string + format: byte + - name: pagination.offset + description: >- + offset is a numeric offset that can be used when key is unavailable. + + It is less efficient than using key. Only one of offset or key + should + + be set. + in: query + required: false + type: string + format: uint64 + - name: pagination.limit + description: >- + limit is the total number of results to be returned in the result + page. + + If left empty it will default to a value to be set by each app. + in: query + required: false + type: string + format: uint64 + - name: pagination.count_total + description: >- + count_total is set to true to indicate that the result set should + include + + a count of the total number of items available for pagination in + UIs. + + count_total is only respected when offset is used. It is ignored + when key + + is set. + in: query + required: false + type: boolean + - name: pagination.reverse + description: >- + reverse is set to true if results are to be returned in the + descending order. + + + Since: cosmos-sdk 0.43 + in: query + required: false + type: boolean + tags: + - Query + /ibc/apps/transfer/v1/denom_traces/{hash}: + get: + summary: DenomTrace queries a denomination trace information. + operationId: DenomTrace + responses: + '200': + description: A successful response. + schema: + type: object + properties: + denom_trace: + type: object + properties: + path: + type: string + description: >- + path defines the chain of port/channel identifiers used + for tracing the + + source of the fungible token. + base_denom: + type: string + description: base denomination of the relayed fungible token. + description: >- + DenomTrace contains the base denomination for ICS20 fungible + tokens and the + + source tracing information path. + description: >- + QueryDenomTraceResponse is the response type for the + Query/DenomTrace RPC + + method. + default: + description: An unexpected error response. + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + value: + type: string + format: byte + parameters: + - name: hash + description: >- + hash (in hex format) or denom (full denom with ibc prefix) of the + denomination trace information. + in: path + required: true + type: string + tags: + - Query + /ibc/apps/transfer/v1/denoms/{denom}/total_escrow: + get: + summary: >- + TotalEscrowForDenom returns the total amount of tokens in escrow based + on the denom. + operationId: TotalEscrowForDenom + responses: + '200': + description: A successful response. + schema: + type: object + properties: + amount: + type: object + properties: + denom: + type: string + amount: + type: string + description: >- + Coin defines a token with a denomination and an amount. + + + NOTE: The amount field is an Int which implements the custom + method + + signatures required by gogoproto. + description: >- + QueryTotalEscrowForDenomResponse is the response type for + TotalEscrowForDenom RPC method. + default: + description: An unexpected error response. + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + value: + type: string + format: byte + parameters: + - name: denom in: path required: true type: string @@ -21174,7 +23425,13 @@ paths: type: string description: >- allowed_clients defines the list of allowed client state - types. + types which can be created + + and interacted with. If a client type is removed from the + allowed clients list, usage + + of this client will be disabled until it is added again to + the list. description: >- QueryClientParamsResponse is the response type for the Query/ClientParams RPC @@ -27427,8 +29684,277 @@ paths: in: path required: true type: string - - name: packet_ack_sequences - description: list of acknowledgement sequences + - name: packet_ack_sequences + description: list of acknowledgement sequences + in: path + required: true + type: array + items: + type: string + format: uint64 + collectionFormat: csv + minItems: 1 + tags: + - Query + /ibc/core/channel/v1/channels/{channel_id}/ports/{port_id}/packet_commitments/{packet_commitment_sequences}/unreceived_packets: + get: + summary: >- + UnreceivedPackets returns all the unreceived IBC packets associated with + a + + channel and sequences. + operationId: UnreceivedPackets + responses: + '200': + description: A successful response. + schema: + type: object + properties: + sequences: + type: array + items: + type: string + format: uint64 + title: list of unreceived packet sequences + height: + title: query block height + type: object + properties: + revision_number: + type: string + format: uint64 + title: the revision that the client is currently on + revision_height: + type: string + format: uint64 + title: the height within the given revision + description: >- + Normally the RevisionHeight is incremented at each height + while keeping + + RevisionNumber the same. However some consensus algorithms may + choose to + + reset the height in certain conditions e.g. hard forks, + state-machine + + breaking changes In these cases, the RevisionNumber is + incremented so that + + height continues to be monitonically increasing even as the + RevisionHeight + + gets reset + title: |- + QueryUnreceivedPacketsResponse is the response type for the + Query/UnreceivedPacketCommitments RPC method + default: + description: An unexpected error response. + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of + the serialized + + protocol buffer message. This string must contain at + least + + one "/" character. The last segment of the URL's path + must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in + a canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary + all types that they + + expect it to use in the context of Any. However, for + URLs which use the + + scheme `http`, `https`, or no scheme, one can optionally + set up a type + + server that maps type URLs to message definitions as + follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based + on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in + the official + + protobuf release, and it is not used for type URLs + beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) + might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer + message along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values + in the form + + of utility functions or additional generated methods of the + Any type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + // or ... + if (any.isSameTypeAs(Foo.getDefaultInstance())) { + foo = any.unpack(Foo.getDefaultInstance()); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := anypb.New(foo) + if err != nil { + ... + } + ... + foo := &pb.Foo{} + if err := any.UnmarshalTo(foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by + default use + + 'type.googleapis.com/full.type.name' as the type URL and the + unpack + + methods only use the fully qualified type name after the + last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield + type + + name "y.z". + + + JSON + + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with + an + + additional field `@type` which contains the type URL. + Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom + JSON + + representation, that representation will be embedded adding + a field + + `value` which holds the custom JSON in addition to the + `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + parameters: + - name: channel_id + description: channel unique identifier + in: path + required: true + type: string + - name: port_id + description: port unique identifier + in: path + required: true + type: string + - name: packet_commitment_sequences + description: list of packet sequences in: path required: true type: array @@ -27439,28 +29965,26 @@ paths: minItems: 1 tags: - Query - /ibc/core/channel/v1/channels/{channel_id}/ports/{port_id}/packet_commitments/{packet_commitment_sequences}/unreceived_packets: + /ibc/core/channel/v1/channels/{channel_id}/ports/{port_id}/packet_commitments/{sequence}: get: - summary: >- - UnreceivedPackets returns all the unreceived IBC packets associated with - a - - channel and sequences. - operationId: UnreceivedPackets + summary: PacketCommitment queries a stored packet commitment hash. + operationId: PacketCommitment responses: '200': description: A successful response. schema: type: object properties: - sequences: - type: array - items: - type: string - format: uint64 - title: list of unreceived packet sequences - height: - title: query block height + commitment: + type: string + format: byte + title: packet associated with the request fields + proof: + type: string + format: byte + title: merkle proof of existence + proof_height: + title: height at which the proof was retrieved type: object properties: revision_number: @@ -27488,9 +30012,14 @@ paths: RevisionHeight gets reset - title: |- - QueryUnreceivedPacketsResponse is the response type for the - Query/UnreceivedPacketCommitments RPC method + title: >- + QueryPacketCommitmentResponse defines the client query response + for a packet + + which also includes a proof and the height from which the proof + was + + retrieved default: description: An unexpected error response. schema: @@ -27696,32 +30225,31 @@ paths: in: path required: true type: string - - name: packet_commitment_sequences - description: list of packet sequences + - name: sequence + description: packet sequence in: path required: true - type: array - items: - type: string - format: uint64 - collectionFormat: csv - minItems: 1 + type: string + format: uint64 tags: - Query - /ibc/core/channel/v1/channels/{channel_id}/ports/{port_id}/packet_commitments/{sequence}: + /ibc/core/channel/v1/channels/{channel_id}/ports/{port_id}/packet_receipts/{sequence}: get: - summary: PacketCommitment queries a stored packet commitment hash. - operationId: PacketCommitment + summary: >- + PacketReceipt queries if a given packet sequence has been received on + the + + queried chain + operationId: PacketReceipt responses: '200': description: A successful response. schema: type: object properties: - commitment: - type: string - format: byte - title: packet associated with the request fields + received: + type: boolean + title: success flag for if receipt exists proof: type: string format: byte @@ -27756,11 +30284,11 @@ paths: gets reset title: >- - QueryPacketCommitmentResponse defines the client query response - for a packet + QueryPacketReceiptResponse defines the client query response for a + packet - which also includes a proof and the height from which the proof - was + receipt which also includes a proof, and the height from which the + proof was retrieved default: @@ -27976,29 +30504,129 @@ paths: format: uint64 tags: - Query - /ibc/core/channel/v1/channels/{channel_id}/ports/{port_id}/packet_receipts/{sequence}: + /ibc/core/channel/v1/connections/{connection}/channels: get: - summary: >- - PacketReceipt queries if a given packet sequence has been received on - the - - queried chain - operationId: PacketReceipt + summary: |- + ConnectionChannels queries all the channels associated with a connection + end. + operationId: ConnectionChannels responses: '200': description: A successful response. schema: type: object properties: - received: - type: boolean - title: success flag for if receipt exists - proof: - type: string - format: byte - title: merkle proof of existence - proof_height: - title: height at which the proof was retrieved + channels: + type: array + items: + type: object + properties: + state: + title: current state of the channel end + type: string + enum: + - STATE_UNINITIALIZED_UNSPECIFIED + - STATE_INIT + - STATE_TRYOPEN + - STATE_OPEN + - STATE_CLOSED + default: STATE_UNINITIALIZED_UNSPECIFIED + description: >- + State defines if a channel is in one of the following + states: + + CLOSED, INIT, TRYOPEN, OPEN or UNINITIALIZED. + + - STATE_UNINITIALIZED_UNSPECIFIED: Default State + - STATE_INIT: A channel has just started the opening handshake. + - STATE_TRYOPEN: A channel has acknowledged the handshake step on the counterparty chain. + - STATE_OPEN: A channel has completed the handshake. Open channels are + ready to send and receive packets. + - STATE_CLOSED: A channel has been closed and can no longer be used to send or receive + packets. + ordering: + title: whether the channel is ordered or unordered + type: string + enum: + - ORDER_NONE_UNSPECIFIED + - ORDER_UNORDERED + - ORDER_ORDERED + default: ORDER_NONE_UNSPECIFIED + description: >- + - ORDER_NONE_UNSPECIFIED: zero-value for channel + ordering + - ORDER_UNORDERED: packets can be delivered in any order, which may differ from the order in + which they were sent. + - ORDER_ORDERED: packets are delivered exactly in the order which they were sent + counterparty: + title: counterparty channel end + type: object + properties: + port_id: + type: string + description: >- + port on the counterparty chain which owns the other + end of the channel. + channel_id: + type: string + title: channel end on the counterparty chain + connection_hops: + type: array + items: + type: string + title: >- + list of connection identifiers, in order, along which + packets sent on + + this channel will travel + version: + type: string + title: >- + opaque channel version, which is agreed upon during the + handshake + port_id: + type: string + title: port identifier + channel_id: + type: string + title: channel identifier + description: >- + IdentifiedChannel defines a channel with additional port and + channel + + identifier fields. + description: list of channels associated with a connection. + pagination: + title: pagination response + type: object + properties: + next_key: + type: string + format: byte + description: |- + next_key is the key to be passed to PageRequest.key to + query the next page most efficiently. It will be empty if + there are no more results. + total: + type: string + format: uint64 + title: >- + total is total number of results available if + PageRequest.count_total + + was set, its value is undefined otherwise + description: >- + PageResponse is to be embedded in gRPC response messages where + the + + corresponding request message has used PageRequest. + + message SomeResponse { + repeated Bar results = 1; + PageResponse page = 2; + } + height: + title: query block height type: object properties: revision_number: @@ -28026,14 +30654,9 @@ paths: RevisionHeight gets reset - title: >- - QueryPacketReceiptResponse defines the client query response for a - packet - - receipt which also includes a proof, and the height from which the - proof was - - retrieved + title: |- + QueryConnectionChannelsResponse is the Response type for the + Query/QueryConnectionChannels RPC method default: description: An unexpected error response. schema: @@ -28152,254 +30775,353 @@ paths: foo = any.unpack(Foo.getDefaultInstance()); } - Example 3: Pack and unpack a message in Python. + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := anypb.New(foo) + if err != nil { + ... + } + ... + foo := &pb.Foo{} + if err := any.UnmarshalTo(foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by + default use + + 'type.googleapis.com/full.type.name' as the type URL and the + unpack + + methods only use the fully qualified type name after the + last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield + type + + name "y.z". + + + JSON + + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with + an + + additional field `@type` which contains the type URL. + Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom + JSON + + representation, that representation will be embedded adding + a field + + `value` which holds the custom JSON in addition to the + `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + parameters: + - name: connection + description: connection unique identifier + in: path + required: true + type: string + - name: pagination.key + description: |- + key is a value returned in PageResponse.next_key to begin + querying the next page most efficiently. Only one of offset or key + should be set. + in: query + required: false + type: string + format: byte + - name: pagination.offset + description: >- + offset is a numeric offset that can be used when key is unavailable. + + It is less efficient than using key. Only one of offset or key + should + + be set. + in: query + required: false + type: string + format: uint64 + - name: pagination.limit + description: >- + limit is the total number of results to be returned in the result + page. + + If left empty it will default to a value to be set by each app. + in: query + required: false + type: string + format: uint64 + - name: pagination.count_total + description: >- + count_total is set to true to indicate that the result set should + include + + a count of the total number of items available for pagination in + UIs. + + count_total is only respected when offset is used. It is ignored + when key + + is set. + in: query + required: false + type: boolean + - name: pagination.reverse + description: >- + reverse is set to true if results are to be returned in the + descending order. + + + Since: cosmos-sdk 0.43 + in: query + required: false + type: boolean + tags: + - Query + /cosmos/auth/v1beta1/account_info/{address}: + get: + summary: AccountInfo queries account info which is common to all account types. + description: 'Since: cosmos-sdk 0.47' + operationId: AccountInfo + responses: + '200': + description: A successful response. + schema: + type: object + properties: + info: + description: info is the account info which is represented by BaseAccount. + type: object + properties: + address: + type: string + pub_key: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type + of the serialized + + protocol buffer message. This string must contain at + least + + one "/" character. The last segment of the URL's path + must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be + in a canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary + all types that they + + expect it to use in the context of Any. However, for + URLs which use the + + scheme `http`, `https`, or no scheme, one can + optionally set up a type + + server that maps type URLs to message definitions as + follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results + based on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in + the official + + protobuf release, and it is not used for type URLs + beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty + scheme) might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the + above specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer + message along with a + + URL that describes the type of the serialized message. - foo = Foo(...) - any = Any() - any.Pack(foo) - ... - if any.Is(Foo.DESCRIPTOR): - any.Unpack(foo) - ... - Example 4: Pack and unpack a message in Go + Protobuf library provides support to pack/unpack Any + values in the form - foo := &pb.Foo{...} - any, err := anypb.New(foo) - if err != nil { - ... - } - ... - foo := &pb.Foo{} - if err := any.UnmarshalTo(foo); err != nil { - ... - } + of utility functions or additional generated methods of + the Any type. - The pack methods provided by protobuf library will by - default use - 'type.googleapis.com/full.type.name' as the type URL and the - unpack + Example 1: Pack and unpack a message in C++. - methods only use the fully qualified type name after the - last '/' + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } - in the type URL, for example "foo.bar.com/x/y.z" will yield - type + Example 2: Pack and unpack a message in Java. - name "y.z". + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + // or ... + if (any.isSameTypeAs(Foo.getDefaultInstance())) { + foo = any.unpack(Foo.getDefaultInstance()); + } + Example 3: Pack and unpack a message in Python. - JSON + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + Example 4: Pack and unpack a message in Go - The JSON representation of an `Any` value uses the regular + foo := &pb.Foo{...} + any, err := anypb.New(foo) + if err != nil { + ... + } + ... + foo := &pb.Foo{} + if err := any.UnmarshalTo(foo); err != nil { + ... + } - representation of the deserialized, embedded message, with - an + The pack methods provided by protobuf library will by + default use - additional field `@type` which contains the type URL. - Example: + 'type.googleapis.com/full.type.name' as the type URL and + the unpack - package google.profile; - message Person { - string first_name = 1; - string last_name = 2; - } + methods only use the fully qualified type name after the + last '/' - { - "@type": "type.googleapis.com/google.profile.Person", - "firstName": , - "lastName": - } + in the type URL, for example "foo.bar.com/x/y.z" will + yield type - If the embedded message type is well-known and has a custom - JSON + name "y.z". - representation, that representation will be embedded adding - a field - `value` which holds the custom JSON in addition to the - `@type` + JSON - field. Example (for message [google.protobuf.Duration][]): - { - "@type": "type.googleapis.com/google.protobuf.Duration", - "value": "1.212s" - } - parameters: - - name: channel_id - description: channel unique identifier - in: path - required: true - type: string - - name: port_id - description: port unique identifier - in: path - required: true - type: string - - name: sequence - description: packet sequence - in: path - required: true - type: string - format: uint64 - tags: - - Query - /ibc/core/channel/v1/connections/{connection}/channels: - get: - summary: |- - ConnectionChannels queries all the channels associated with a connection - end. - operationId: ConnectionChannels - responses: - '200': - description: A successful response. - schema: - type: object - properties: - channels: - type: array - items: - type: object - properties: - state: - title: current state of the channel end - type: string - enum: - - STATE_UNINITIALIZED_UNSPECIFIED - - STATE_INIT - - STATE_TRYOPEN - - STATE_OPEN - - STATE_CLOSED - default: STATE_UNINITIALIZED_UNSPECIFIED - description: >- - State defines if a channel is in one of the following - states: + The JSON representation of an `Any` value uses the regular - CLOSED, INIT, TRYOPEN, OPEN or UNINITIALIZED. + representation of the deserialized, embedded message, with + an - - STATE_UNINITIALIZED_UNSPECIFIED: Default State - - STATE_INIT: A channel has just started the opening handshake. - - STATE_TRYOPEN: A channel has acknowledged the handshake step on the counterparty chain. - - STATE_OPEN: A channel has completed the handshake. Open channels are - ready to send and receive packets. - - STATE_CLOSED: A channel has been closed and can no longer be used to send or receive - packets. - ordering: - title: whether the channel is ordered or unordered - type: string - enum: - - ORDER_NONE_UNSPECIFIED - - ORDER_UNORDERED - - ORDER_ORDERED - default: ORDER_NONE_UNSPECIFIED - description: >- - - ORDER_NONE_UNSPECIFIED: zero-value for channel - ordering - - ORDER_UNORDERED: packets can be delivered in any order, which may differ from the order in - which they were sent. - - ORDER_ORDERED: packets are delivered exactly in the order which they were sent - counterparty: - title: counterparty channel end - type: object - properties: - port_id: - type: string - description: >- - port on the counterparty chain which owns the other - end of the channel. - channel_id: - type: string - title: channel end on the counterparty chain - connection_hops: - type: array - items: - type: string - title: >- - list of connection identifiers, in order, along which - packets sent on + additional field `@type` which contains the type URL. + Example: - this channel will travel - version: - type: string - title: >- - opaque channel version, which is agreed upon during the - handshake - port_id: - type: string - title: port identifier - channel_id: - type: string - title: channel identifier - description: >- - IdentifiedChannel defines a channel with additional port and - channel + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } - identifier fields. - description: list of channels associated with a connection. - pagination: - title: pagination response - type: object - properties: - next_key: - type: string - format: byte - description: |- - next_key is the key to be passed to PageRequest.key to - query the next page most efficiently. It will be empty if - there are no more results. - total: - type: string - format: uint64 - title: >- - total is total number of results available if - PageRequest.count_total + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } - was set, its value is undefined otherwise - description: >- - PageResponse is to be embedded in gRPC response messages where - the + If the embedded message type is well-known and has a + custom JSON - corresponding request message has used PageRequest. + representation, that representation will be embedded + adding a field - message SomeResponse { - repeated Bar results = 1; - PageResponse page = 2; - } - height: - title: query block height - type: object - properties: - revision_number: + `value` which holds the custom JSON in addition to the + `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + account_number: type: string format: uint64 - title: the revision that the client is currently on - revision_height: + sequence: type: string format: uint64 - title: the height within the given revision - description: >- - Normally the RevisionHeight is incremented at each height - while keeping - - RevisionNumber the same. However some consensus algorithms may - choose to - - reset the height in certain conditions e.g. hard forks, - state-machine - - breaking changes In these cases, the RevisionNumber is - incremented so that - - height continues to be monitonically increasing even as the - RevisionHeight + description: |- + QueryAccountInfoResponse is the Query/AccountInfo response type. - gets reset - title: |- - QueryConnectionChannelsResponse is the Response type for the - Query/QueryConnectionChannels RPC method + Since: cosmos-sdk 0.47 default: description: An unexpected error response. schema: @@ -28595,73 +31317,24 @@ paths: "value": "1.212s" } parameters: - - name: connection - description: connection unique identifier + - name: address + description: address is the account address string. in: path required: true type: string - - name: pagination.key - description: |- - key is a value returned in PageResponse.next_key to begin - querying the next page most efficiently. Only one of offset or key - should be set. - in: query - required: false - type: string - format: byte - - name: pagination.offset - description: >- - offset is a numeric offset that can be used when key is unavailable. - - It is less efficient than using key. Only one of offset or key - should - - be set. - in: query - required: false - type: string - format: uint64 - - name: pagination.limit - description: >- - limit is the total number of results to be returned in the result - page. - - If left empty it will default to a value to be set by each app. - in: query - required: false - type: string - format: uint64 - - name: pagination.count_total - description: >- - count_total is set to true to indicate that the result set should - include - - a count of the total number of items available for pagination in - UIs. - - count_total is only respected when offset is used. It is ignored - when key - - is set. - in: query - required: false - type: boolean - - name: pagination.reverse - description: >- - reverse is set to true if results are to be returned in the - descending order. - - - Since: cosmos-sdk 0.43 - in: query - required: false - type: boolean tags: - Query /cosmos/auth/v1beta1/accounts: get: - summary: Accounts returns all the existing accounts - description: 'Since: cosmos-sdk 0.43' + summary: Accounts returns all the existing accounts. + description: >- + When called from another module, this query might consume a high amount + of + + gas if the pagination field is incorrectly set. + + + Since: cosmos-sdk 0.43 operationId: Accounts responses: '200': @@ -29735,6 +32408,8 @@ paths: parameters: - name: id description: |- + Deprecated, use account_id instead + id is the account number of the address to be queried. This field should have been an uint64 (like all account numbers), and will be updated to uint64 in a future version of the auth query. @@ -29742,6 +32417,15 @@ paths: required: true type: string format: int64 + - name: account_id + description: |- + account_id is the account number of the address to be queried. + + Since: cosmos-sdk 0.47 + in: query + required: false + type: string + format: uint64 tags: - Query /cosmos/auth/v1beta1/bech32: @@ -32901,6 +35585,11 @@ paths: /cosmos/bank/v1beta1/balances/{address}: get: summary: AllBalances queries the balance of all coins for a single account. + description: >- + When called from another module, this query might consume a high amount + of + + gas if the pagination field is incorrectly set. operationId: AllBalances responses: '200': @@ -33106,7 +35795,14 @@ paths: token denomination. - description: 'Since: cosmos-sdk 0.46' + description: >- + When called from another module, this query might consume a high amount + of + + gas if the pagination field is incorrectly set. + + + Since: cosmos-sdk 0.46 operationId: DenomOwners responses: '200': @@ -33601,76 +36297,252 @@ paths: type: string format: byte parameters: - - name: denom - description: denom is the coin denom to query the metadata for. - in: path - required: true + - name: denom + description: denom is the coin denom to query the metadata for. + in: path + required: true + type: string + tags: + - Query + /cosmos/bank/v1beta1/params: + get: + summary: Params queries the parameters of x/bank module. + operationId: BankParams + responses: + '200': + description: A successful response. + schema: + type: object + properties: + params: + type: object + properties: + send_enabled: + type: array + items: + type: object + properties: + denom: + type: string + enabled: + type: boolean + description: >- + SendEnabled maps coin denom to a send_enabled status + (whether a denom is + + sendable). + description: >- + Deprecated: Use of SendEnabled in params is deprecated. + + For genesis, use the newly added send_enabled field in the + genesis object. + + Storage, lookup, and manipulation of this information is + now in the keeper. + + + As of cosmos-sdk 0.47, this only exists for backwards + compatibility of genesis files. + default_send_enabled: + type: boolean + description: Params defines the parameters for the bank module. + description: >- + QueryParamsResponse defines the response type for querying x/bank + parameters. + default: + description: An unexpected error response. + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + value: + type: string + format: byte + tags: + - Query + /cosmos/bank/v1beta1/send_enabled: + get: + summary: SendEnabled queries for SendEnabled entries. + description: >- + This query only returns denominations that have specific SendEnabled + settings. + + Any denomination that does not have a specific setting will use the + default + + params.default_send_enabled, and will not be returned by this query. + + + Since: cosmos-sdk 0.47 + operationId: SendEnabled + responses: + '200': + description: A successful response. + schema: + type: object + properties: + send_enabled: + type: array + items: + type: object + properties: + denom: + type: string + enabled: + type: boolean + description: >- + SendEnabled maps coin denom to a send_enabled status + (whether a denom is + + sendable). + pagination: + description: >- + pagination defines the pagination in the response. This field + is only + + populated if the denoms field in the request is empty. + type: object + properties: + next_key: + type: string + format: byte + description: |- + next_key is the key to be passed to PageRequest.key to + query the next page most efficiently. It will be empty if + there are no more results. + total: + type: string + format: uint64 + title: >- + total is total number of results available if + PageRequest.count_total + + was set, its value is undefined otherwise + description: >- + QuerySendEnabledResponse defines the RPC response of a SendEnable + query. + + + Since: cosmos-sdk 0.47 + default: + description: An unexpected error response. + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + value: + type: string + format: byte + parameters: + - name: denoms + description: >- + denoms is the specific denoms you want look up. Leave empty to get + all entries. + in: query + required: false + type: array + items: + type: string + collectionFormat: multi + - name: pagination.key + description: |- + key is a value returned in PageResponse.next_key to begin + querying the next page most efficiently. Only one of offset or key + should be set. + in: query + required: false type: string - tags: - - Query - /cosmos/bank/v1beta1/params: - get: - summary: Params queries the parameters of x/bank module. - operationId: BankParams - responses: - '200': - description: A successful response. - schema: - type: object - properties: - params: - type: object - properties: - send_enabled: - type: array - items: - type: object - properties: - denom: - type: string - enabled: - type: boolean - description: >- - SendEnabled maps coin denom to a send_enabled status - (whether a denom is + format: byte + - name: pagination.offset + description: >- + offset is a numeric offset that can be used when key is unavailable. - sendable). - default_send_enabled: - type: boolean - description: Params defines the parameters for the bank module. - description: >- - QueryParamsResponse defines the response type for querying x/bank - parameters. - default: - description: An unexpected error response. - schema: - type: object - properties: - error: - type: string - code: - type: integer - format: int32 - message: - type: string - details: - type: array - items: - type: object - properties: - type_url: - type: string - value: - type: string - format: byte + It is less efficient than using key. Only one of offset or key + should + + be set. + in: query + required: false + type: string + format: uint64 + - name: pagination.limit + description: >- + limit is the total number of results to be returned in the result + page. + + If left empty it will default to a value to be set by each app. + in: query + required: false + type: string + format: uint64 + - name: pagination.count_total + description: >- + count_total is set to true to indicate that the result set should + include + + a count of the total number of items available for pagination in + UIs. + + count_total is only respected when offset is used. It is ignored + when key + + is set. + in: query + required: false + type: boolean + - name: pagination.reverse + description: >- + reverse is set to true if results are to be returned in the + descending order. + + + Since: cosmos-sdk 0.43 + in: query + required: false + type: boolean tags: - Query /cosmos/bank/v1beta1/spendable_balances/{address}: get: - summary: |- - SpendableBalances queries the spenable balance of all coins for a single + summary: >- + SpendableBalances queries the spendable balance of all coins for a + single + account. - description: 'Since: cosmos-sdk 0.46' + description: >- + When called from another module, this query might consume a high amount + of + + gas if the pagination field is incorrectly set. + + + Since: cosmos-sdk 0.46 operationId: SpendableBalances responses: '200': @@ -33809,9 +36681,94 @@ paths: type: boolean tags: - Query + /cosmos/bank/v1beta1/spendable_balances/{address}/by_denom: + get: + summary: >- + SpendableBalanceByDenom queries the spendable balance of a single denom + for + + a single account. + description: >- + When called from another module, this query might consume a high amount + of + + gas if the pagination field is incorrectly set. + + + Since: cosmos-sdk 0.47 + operationId: SpendableBalanceByDenom + responses: + '200': + description: A successful response. + schema: + type: object + properties: + balance: + type: object + properties: + denom: + type: string + amount: + type: string + description: >- + Coin defines a token with a denomination and an amount. + + + NOTE: The amount field is an Int which implements the custom + method + + signatures required by gogoproto. + description: >- + QuerySpendableBalanceByDenomResponse defines the gRPC response + structure for + + querying an account's spendable balance for a specific denom. + + + Since: cosmos-sdk 0.47 + default: + description: An unexpected error response. + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + value: + type: string + format: byte + parameters: + - name: address + description: address is the address to query balances for. + in: path + required: true + type: string + - name: denom + description: denom is the coin denom to query balances for. + in: query + required: false + type: string + tags: + - Query /cosmos/bank/v1beta1/supply: get: summary: TotalSupply queries the total supply of all coins. + description: >- + When called from another module, this query might consume a high amount + of + + gas if the pagination field is incorrectly set. operationId: TotalSupply responses: '200': @@ -33948,6 +36905,11 @@ paths: /cosmos/bank/v1beta1/supply/by_denom: get: summary: SupplyOf queries the supply of a single coin. + description: >- + When called from another module, this query might consume a high amount + of + + gas if the pagination field is incorrectly set. operationId: SupplyOf responses: '200': @@ -34304,44 +37266,134 @@ paths: items: type: object properties: - type_url: + type_url: + type: string + value: + type: string + format: byte + parameters: + - name: delegator_address + description: delegator_address defines the delegator address to query for. + in: path + required: true + type: string + tags: + - Query + /cosmos/distribution/v1beta1/params: + get: + summary: Params queries params of the distribution module. + operationId: DistributionParams + responses: + '200': + description: A successful response. + schema: + type: object + properties: + params: + description: params defines the parameters of the module. + type: object + properties: + community_tax: + type: string + base_proposer_reward: + type: string + description: >- + Deprecated: The base_proposer_reward field is deprecated + and is no longer used + + in the x/distribution module's reward mechanism. + bonus_proposer_reward: + type: string + description: >- + Deprecated: The bonus_proposer_reward field is deprecated + and is no longer used + + in the x/distribution module's reward mechanism. + withdraw_addr_enabled: + type: boolean + description: >- + QueryParamsResponse is the response type for the Query/Params RPC + method. + default: + description: An unexpected error response. + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + value: + type: string + format: byte + tags: + - Query + /cosmos/distribution/v1beta1/validators/{validator_address}: + get: + summary: >- + ValidatorDistributionInfo queries validator commission and + self-delegation rewards for validator + operationId: ValidatorDistributionInfo + responses: + '200': + description: A successful response. + schema: + type: object + properties: + operator_address: + type: string + description: operator_address defines the validator operator address. + self_bond_rewards: + type: array + items: + type: object + properties: + denom: + type: string + amount: + type: string + description: >- + DecCoin defines a token with a denomination and a decimal + amount. + + + NOTE: The amount field is an Dec which implements the custom + method + + signatures required by gogoproto. + description: self_bond_rewards defines the self delegations rewards. + commission: + type: array + items: + type: object + properties: + denom: type: string - value: + amount: type: string - format: byte - parameters: - - name: delegator_address - description: delegator_address defines the delegator address to query for. - in: path - required: true - type: string - tags: - - Query - /cosmos/distribution/v1beta1/params: - get: - summary: Params queries params of the distribution module. - operationId: DistributionParams - responses: - '200': - description: A successful response. - schema: - type: object - properties: - params: - description: params defines the parameters of the module. - type: object - properties: - community_tax: - type: string - base_proposer_reward: - type: string - bonus_proposer_reward: - type: string - withdraw_addr_enabled: - type: boolean + description: >- + DecCoin defines a token with a denomination and a decimal + amount. + + + NOTE: The amount field is an Dec which implements the custom + method + + signatures required by gogoproto. + description: commission defines the commission the validator received. description: >- - QueryParamsResponse is the response type for the Query/Params RPC - method. + QueryValidatorDistributionInfoResponse is the response type for + the Query/ValidatorDistributionInfo RPC method. default: description: An unexpected error response. schema: @@ -34364,6 +37416,12 @@ paths: value: type: string format: byte + parameters: + - name: validator_address + description: validator_address defines the validator address to query for. + in: path + required: true + type: string tags: - Query /cosmos/distribution/v1beta1/validators/{validator_address}/commission: @@ -34377,7 +37435,7 @@ paths: type: object properties: commission: - description: commission defines the commision the validator received. + description: commission defines the commission the validator received. type: object properties: commission: @@ -34503,39 +37561,573 @@ paths: type: string tags: - Query - /cosmos/distribution/v1beta1/validators/{validator_address}/slashes: + /cosmos/distribution/v1beta1/validators/{validator_address}/slashes: + get: + summary: ValidatorSlashes queries slash events of a validator. + operationId: ValidatorSlashes + responses: + '200': + description: A successful response. + schema: + type: object + properties: + slashes: + type: array + items: + type: object + properties: + validator_period: + type: string + format: uint64 + fraction: + type: string + description: >- + ValidatorSlashEvent represents a validator slash event. + + Height is implicit within the store key. + + This is needed to calculate appropriate amount of staking + tokens + + for delegations which are withdrawn after a slash has + occurred. + description: slashes defines the slashes the validator received. + pagination: + description: pagination defines the pagination in the response. + type: object + properties: + next_key: + type: string + format: byte + description: |- + next_key is the key to be passed to PageRequest.key to + query the next page most efficiently. It will be empty if + there are no more results. + total: + type: string + format: uint64 + title: >- + total is total number of results available if + PageRequest.count_total + + was set, its value is undefined otherwise + description: |- + QueryValidatorSlashesResponse is the response type for the + Query/ValidatorSlashes RPC method. + default: + description: An unexpected error response. + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + value: + type: string + format: byte + parameters: + - name: validator_address + description: validator_address defines the validator address to query for. + in: path + required: true + type: string + - name: starting_height + description: >- + starting_height defines the optional starting height to query the + slashes. + in: query + required: false + type: string + format: uint64 + - name: ending_height + description: >- + starting_height defines the optional ending height to query the + slashes. + in: query + required: false + type: string + format: uint64 + - name: pagination.key + description: |- + key is a value returned in PageResponse.next_key to begin + querying the next page most efficiently. Only one of offset or key + should be set. + in: query + required: false + type: string + format: byte + - name: pagination.offset + description: >- + offset is a numeric offset that can be used when key is unavailable. + + It is less efficient than using key. Only one of offset or key + should + + be set. + in: query + required: false + type: string + format: uint64 + - name: pagination.limit + description: >- + limit is the total number of results to be returned in the result + page. + + If left empty it will default to a value to be set by each app. + in: query + required: false + type: string + format: uint64 + - name: pagination.count_total + description: >- + count_total is set to true to indicate that the result set should + include + + a count of the total number of items available for pagination in + UIs. + + count_total is only respected when offset is used. It is ignored + when key + + is set. + in: query + required: false + type: boolean + - name: pagination.reverse + description: >- + reverse is set to true if results are to be returned in the + descending order. + + + Since: cosmos-sdk 0.43 + in: query + required: false + type: boolean + tags: + - Query + /cosmos/feegrant/v1beta1/allowance/{granter}/{grantee}: + get: + summary: Allowance returns fee granted to the grantee by the granter. + operationId: Allowance + responses: + '200': + description: A successful response. + schema: + type: object + properties: + allowance: + description: allowance is a allowance granted for grantee by granter. + type: object + properties: + granter: + type: string + description: >- + granter is the address of the user granting an allowance + of their funds. + grantee: + type: string + description: >- + grantee is the address of the user being granted an + allowance of another user's funds. + allowance: + description: >- + allowance can be any of basic, periodic, allowed fee + allowance. + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type + of the serialized + + protocol buffer message. This string must contain at + least + + one "/" character. The last segment of the URL's path + must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be + in a canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary + all types that they + + expect it to use in the context of Any. However, for + URLs which use the + + scheme `http`, `https`, or no scheme, one can + optionally set up a type + + server that maps type URLs to message definitions as + follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results + based on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in + the official + + protobuf release, and it is not used for type URLs + beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty + scheme) might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the + above specified type. + title: >- + Grant is stored in the KVStore to record a grant with full + context + description: >- + QueryAllowanceResponse is the response type for the + Query/Allowance RPC method. + default: + description: An unexpected error response. + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of + the serialized + + protocol buffer message. This string must contain at + least + + one "/" character. The last segment of the URL's path + must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in + a canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary + all types that they + + expect it to use in the context of Any. However, for + URLs which use the + + scheme `http`, `https`, or no scheme, one can optionally + set up a type + + server that maps type URLs to message definitions as + follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based + on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in + the official + + protobuf release, and it is not used for type URLs + beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) + might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer + message along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values + in the form + + of utility functions or additional generated methods of the + Any type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + // or ... + if (any.isSameTypeAs(Foo.getDefaultInstance())) { + foo = any.unpack(Foo.getDefaultInstance()); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := anypb.New(foo) + if err != nil { + ... + } + ... + foo := &pb.Foo{} + if err := any.UnmarshalTo(foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by + default use + + 'type.googleapis.com/full.type.name' as the type URL and the + unpack + + methods only use the fully qualified type name after the + last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield + type + + name "y.z". + + + JSON + + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with + an + + additional field `@type` which contains the type URL. + Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom + JSON + + representation, that representation will be embedded adding + a field + + `value` which holds the custom JSON in addition to the + `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + parameters: + - name: granter + description: >- + granter is the address of the user granting an allowance of their + funds. + in: path + required: true + type: string + - name: grantee + description: >- + grantee is the address of the user being granted an allowance of + another user's funds. + in: path + required: true + type: string + tags: + - Query + /cosmos/feegrant/v1beta1/allowances/{grantee}: get: - summary: ValidatorSlashes queries slash events of a validator. - operationId: ValidatorSlashes + summary: Allowances returns all the grants for address. + operationId: Allowances responses: '200': description: A successful response. schema: type: object properties: - slashes: + allowances: type: array items: type: object properties: - validator_period: + granter: type: string - format: uint64 - fraction: + description: >- + granter is the address of the user granting an allowance + of their funds. + grantee: type: string - description: >- - ValidatorSlashEvent represents a validator slash event. + description: >- + grantee is the address of the user being granted an + allowance of another user's funds. + allowance: + description: >- + allowance can be any of basic, periodic, allowed fee + allowance. + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the + type of the serialized - Height is implicit within the store key. + protocol buffer message. This string must contain at + least - This is needed to calculate appropriate amount of staking - tokens + one "/" character. The last segment of the URL's + path must represent - for delegations which are withdrawn after a slash has - occurred. - description: slashes defines the slashes the validator received. + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be + in a canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the + binary all types that they + + expect it to use in the context of Any. However, for + URLs which use the + + scheme `http`, `https`, or no scheme, one can + optionally set up a type + + server that maps type URLs to message definitions as + follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results + based on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available + in the official + + protobuf release, and it is not used for type URLs + beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty + scheme) might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the + above specified type. + title: >- + Grant is stored in the KVStore to record a grant with full + context + description: allowances are allowance's granted for grantee by granter. pagination: - description: pagination defines the pagination in the response. + description: pagination defines an pagination for the response. type: object properties: next_key: @@ -34553,9 +38145,9 @@ paths: PageRequest.count_total was set, its value is undefined otherwise - description: |- - QueryValidatorSlashesResponse is the response type for the - Query/ValidatorSlashes RPC method. + description: >- + QueryAllowancesResponse is the response type for the + Query/Allowances RPC method. default: description: An unexpected error response. schema: @@ -34575,31 +38167,186 @@ paths: properties: type_url: type: string + description: >- + A URL/resource name that uniquely identifies the type of + the serialized + + protocol buffer message. This string must contain at + least + + one "/" character. The last segment of the URL's path + must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in + a canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary + all types that they + + expect it to use in the context of Any. However, for + URLs which use the + + scheme `http`, `https`, or no scheme, one can optionally + set up a type + + server that maps type URLs to message definitions as + follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based + on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in + the official + + protobuf release, and it is not used for type URLs + beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) + might be + + used with implementation specific semantics. value: type: string format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer + message along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values + in the form + + of utility functions or additional generated methods of the + Any type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + // or ... + if (any.isSameTypeAs(Foo.getDefaultInstance())) { + foo = any.unpack(Foo.getDefaultInstance()); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := anypb.New(foo) + if err != nil { + ... + } + ... + foo := &pb.Foo{} + if err := any.UnmarshalTo(foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by + default use + + 'type.googleapis.com/full.type.name' as the type URL and the + unpack + + methods only use the fully qualified type name after the + last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield + type + + name "y.z". + + + JSON + + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with + an + + additional field `@type` which contains the type URL. + Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom + JSON + + representation, that representation will be embedded adding + a field + + `value` which holds the custom JSON in addition to the + `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } parameters: - - name: validator_address - description: validator_address defines the validator address to query for. + - name: grantee in: path required: true type: string - - name: starting_height - description: >- - starting_height defines the optional starting height to query the - slashes. - in: query - required: false - type: string - format: uint64 - - name: ending_height - description: >- - starting_height defines the optional ending height to query the - slashes. - in: query - required: false - type: string - format: uint64 - name: pagination.key description: |- key is a value returned in PageResponse.next_key to begin @@ -34658,106 +38405,131 @@ paths: type: boolean tags: - Query - /cosmos/feegrant/v1beta1/allowance/{granter}/{grantee}: + /cosmos/feegrant/v1beta1/issued/{granter}: get: - summary: Allowance returns fee granted to the grantee by the granter. - operationId: Allowance + summary: AllowancesByGranter returns all the grants given by an address + description: 'Since: cosmos-sdk 0.46' + operationId: AllowancesByGranter responses: '200': description: A successful response. schema: type: object properties: - allowance: - description: allowance is a allowance granted for grantee by granter. - type: object - properties: - granter: - type: string - description: >- - granter is the address of the user granting an allowance - of their funds. - grantee: - type: string - description: >- - grantee is the address of the user being granted an - allowance of another user's funds. - allowance: - description: >- - allowance can be any of basic, periodic, allowed fee - allowance. - type: object - properties: - type_url: - type: string - description: >- - A URL/resource name that uniquely identifies the type - of the serialized + allowances: + type: array + items: + type: object + properties: + granter: + type: string + description: >- + granter is the address of the user granting an allowance + of their funds. + grantee: + type: string + description: >- + grantee is the address of the user being granted an + allowance of another user's funds. + allowance: + description: >- + allowance can be any of basic, periodic, allowed fee + allowance. + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the + type of the serialized - protocol buffer message. This string must contain at - least + protocol buffer message. This string must contain at + least - one "/" character. The last segment of the URL's path - must represent + one "/" character. The last segment of the URL's + path must represent - the fully qualified name of the type (as in + the fully qualified name of the type (as in - `path/google.protobuf.Duration`). The name should be - in a canonical form + `path/google.protobuf.Duration`). The name should be + in a canonical form - (e.g., leading "." is not accepted). + (e.g., leading "." is not accepted). - In practice, teams usually precompile into the binary - all types that they + In practice, teams usually precompile into the + binary all types that they - expect it to use in the context of Any. However, for - URLs which use the + expect it to use in the context of Any. However, for + URLs which use the - scheme `http`, `https`, or no scheme, one can - optionally set up a type + scheme `http`, `https`, or no scheme, one can + optionally set up a type - server that maps type URLs to message definitions as - follows: + server that maps type URLs to message definitions as + follows: - * If no scheme is provided, `https` is assumed. + * If no scheme is provided, `https` is assumed. - * An HTTP GET on the URL must yield a - [google.protobuf.Type][] - value in binary format, or produce an error. - * Applications are allowed to cache lookup results - based on the - URL, or have them precompiled into a binary to avoid any - lookup. Therefore, binary compatibility needs to be preserved - on changes to types. (Use versioned type names to manage - breaking changes.) + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results + based on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) - Note: this functionality is not currently available in - the official + Note: this functionality is not currently available + in the official - protobuf release, and it is not used for type URLs - beginning with + protobuf release, and it is not used for type URLs + beginning with - type.googleapis.com. + type.googleapis.com. - Schemes other than `http`, `https` (or the empty - scheme) might be + Schemes other than `http`, `https` (or the empty + scheme) might be - used with implementation specific semantics. - value: - type: string - format: byte - description: >- - Must be a valid serialized protocol buffer of the - above specified type. - title: >- - Grant is stored in the KVStore to record a grant with full - context + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the + above specified type. + title: >- + Grant is stored in the KVStore to record a grant with full + context + description: allowances that have been issued by the granter. + pagination: + description: pagination defines an pagination for the response. + type: object + properties: + next_key: + type: string + format: byte + description: |- + next_key is the key to be passed to PageRequest.key to + query the next page most efficiently. It will be empty if + there are no more results. + total: + type: string + format: uint64 + title: >- + total is total number of results available if + PageRequest.count_total + + was set, its value is undefined otherwise description: >- - QueryAllowanceResponse is the response type for the - Query/Allowance RPC method. + QueryAllowancesByGranterResponse is the response type for the + Query/AllowancesByGranter RPC method. + + + Since: cosmos-sdk 0.46 default: description: An unexpected error response. schema: @@ -34940,156 +38712,156 @@ paths: If the embedded message type is well-known and has a custom JSON - representation, that representation will be embedded adding - a field + representation, that representation will be embedded adding + a field + + `value` which holds the custom JSON in addition to the + `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + parameters: + - name: granter + in: path + required: true + type: string + - name: pagination.key + description: |- + key is a value returned in PageResponse.next_key to begin + querying the next page most efficiently. Only one of offset or key + should be set. + in: query + required: false + type: string + format: byte + - name: pagination.offset + description: >- + offset is a numeric offset that can be used when key is unavailable. + + It is less efficient than using key. Only one of offset or key + should + + be set. + in: query + required: false + type: string + format: uint64 + - name: pagination.limit + description: >- + limit is the total number of results to be returned in the result + page. + + If left empty it will default to a value to be set by each app. + in: query + required: false + type: string + format: uint64 + - name: pagination.count_total + description: >- + count_total is set to true to indicate that the result set should + include - `value` which holds the custom JSON in addition to the - `@type` + a count of the total number of items available for pagination in + UIs. - field. Example (for message [google.protobuf.Duration][]): + count_total is only respected when offset is used. It is ignored + when key - { - "@type": "type.googleapis.com/google.protobuf.Duration", - "value": "1.212s" - } - parameters: - - name: granter - description: >- - granter is the address of the user granting an allowance of their - funds. - in: path - required: true - type: string - - name: grantee + is set. + in: query + required: false + type: boolean + - name: pagination.reverse description: >- - grantee is the address of the user being granted an allowance of - another user's funds. - in: path - required: true - type: string + reverse is set to true if results are to be returned in the + descending order. + + + Since: cosmos-sdk 0.43 + in: query + required: false + type: boolean tags: - Query - /cosmos/feegrant/v1beta1/allowances/{grantee}: + /cosmos/gov/v1beta1/params/{params_type}: get: - summary: Allowances returns all the grants for address. - operationId: Allowances + summary: Params queries all parameters of the gov module. + operationId: GovParams responses: '200': description: A successful response. schema: type: object properties: - allowances: - type: array - items: - type: object - properties: - granter: - type: string - description: >- - granter is the address of the user granting an allowance - of their funds. - grantee: - type: string - description: >- - grantee is the address of the user being granted an - allowance of another user's funds. - allowance: - description: >- - allowance can be any of basic, periodic, allowed fee - allowance. + voting_params: + description: voting_params defines the parameters related to voting. + type: object + properties: + voting_period: + type: string + description: Duration of the voting period. + deposit_params: + description: deposit_params defines the parameters related to deposit. + type: object + properties: + min_deposit: + type: array + items: type: object properties: - type_url: + denom: type: string - description: >- - A URL/resource name that uniquely identifies the - type of the serialized - - protocol buffer message. This string must contain at - least - - one "/" character. The last segment of the URL's - path must represent - - the fully qualified name of the type (as in - - `path/google.protobuf.Duration`). The name should be - in a canonical form - - (e.g., leading "." is not accepted). - - - In practice, teams usually precompile into the - binary all types that they - - expect it to use in the context of Any. However, for - URLs which use the - - scheme `http`, `https`, or no scheme, one can - optionally set up a type - - server that maps type URLs to message definitions as - follows: - - - * If no scheme is provided, `https` is assumed. - - * An HTTP GET on the URL must yield a - [google.protobuf.Type][] - value in binary format, or produce an error. - * Applications are allowed to cache lookup results - based on the - URL, or have them precompiled into a binary to avoid any - lookup. Therefore, binary compatibility needs to be preserved - on changes to types. (Use versioned type names to manage - breaking changes.) - - Note: this functionality is not currently available - in the official - - protobuf release, and it is not used for type URLs - beginning with + amount: + type: string + description: >- + Coin defines a token with a denomination and an amount. - type.googleapis.com. + NOTE: The amount field is an Int which implements the + custom method - Schemes other than `http`, `https` (or the empty - scheme) might be + signatures required by gogoproto. + description: Minimum deposit for a proposal to enter voting period. + max_deposit_period: + type: string + description: >- + Maximum period for Atom holders to deposit on a proposal. + Initial value: 2 - used with implementation specific semantics. - value: - type: string - format: byte - description: >- - Must be a valid serialized protocol buffer of the - above specified type. - title: >- - Grant is stored in the KVStore to record a grant with full - context - description: allowances are allowance's granted for grantee by granter. - pagination: - description: pagination defines an pagination for the response. + months. + tally_params: + description: tally_params defines the parameters related to tally. type: object properties: - next_key: + quorum: type: string format: byte - description: |- - next_key is the key to be passed to PageRequest.key to - query the next page most efficiently. It will be empty if - there are no more results. - total: + description: >- + Minimum percentage of total stake needed to vote for a + result to be + + considered valid. + threshold: type: string - format: uint64 - title: >- - total is total number of results available if - PageRequest.count_total + format: byte + description: >- + Minimum proportion of Yes votes for proposal to pass. + Default value: 0.5. + veto_threshold: + type: string + format: byte + description: >- + Minimum value of Veto votes to Total votes ratio for + proposal to be - was set, its value is undefined otherwise + vetoed. Default value: 1/3. description: >- - QueryAllowancesResponse is the response type for the - Query/Allowances RPC method. + QueryParamsResponse is the response type for the Query/Params RPC + method. default: description: An unexpected error response. schema: @@ -35285,98 +39057,37 @@ paths: "value": "1.212s" } parameters: - - name: grantee - in: path - required: true - type: string - - name: pagination.key - description: |- - key is a value returned in PageResponse.next_key to begin - querying the next page most efficiently. Only one of offset or key - should be set. - in: query - required: false - type: string - format: byte - - name: pagination.offset - description: >- - offset is a numeric offset that can be used when key is unavailable. - - It is less efficient than using key. Only one of offset or key - should - - be set. - in: query - required: false - type: string - format: uint64 - - name: pagination.limit + - name: params_type description: >- - limit is the total number of results to be returned in the result - page. + params_type defines which parameters to query for, can be one of + "voting", - If left empty it will default to a value to be set by each app. - in: query - required: false + "tallying" or "deposit". + in: path + required: true type: string - format: uint64 - - name: pagination.count_total - description: >- - count_total is set to true to indicate that the result set should - include - - a count of the total number of items available for pagination in - UIs. - - count_total is only respected when offset is used. It is ignored - when key - - is set. - in: query - required: false - type: boolean - - name: pagination.reverse - description: >- - reverse is set to true if results are to be returned in the - descending order. - - - Since: cosmos-sdk 0.43 - in: query - required: false - type: boolean tags: - Query - /cosmos/feegrant/v1beta1/issued/{granter}: + /cosmos/gov/v1beta1/proposals: get: - summary: AllowancesByGranter returns all the grants given by an address - description: 'Since: cosmos-sdk 0.46' - operationId: AllowancesByGranter + summary: Proposals queries all proposals based on given status. + operationId: Proposals responses: '200': description: A successful response. schema: type: object properties: - allowances: + proposals: type: array items: type: object properties: - granter: - type: string - description: >- - granter is the address of the user granting an allowance - of their funds. - grantee: + proposal_id: type: string - description: >- - grantee is the address of the user being granted an - allowance of another user's funds. - allowance: - description: >- - allowance can be any of basic, periodic, allowed fee - allowance. + format: uint64 + description: proposal_id defines the unique id of the proposal. + content: type: object properties: type_url: @@ -35443,12 +39154,202 @@ paths: description: >- Must be a valid serialized protocol buffer of the above specified type. - title: >- - Grant is stored in the KVStore to record a grant with full - context - description: allowances that have been issued by the granter. + description: >- + `Any` contains an arbitrary serialized protocol buffer + message along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any + values in the form + + of utility functions or additional generated methods of + the Any type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + // or ... + if (any.isSameTypeAs(Foo.getDefaultInstance())) { + foo = any.unpack(Foo.getDefaultInstance()); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := anypb.New(foo) + if err != nil { + ... + } + ... + foo := &pb.Foo{} + if err := any.UnmarshalTo(foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by + default use + + 'type.googleapis.com/full.type.name' as the type URL and + the unpack + + methods only use the fully qualified type name after the + last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will + yield type + + name "y.z". + + + JSON + + + The JSON representation of an `Any` value uses the + regular + + representation of the deserialized, embedded message, + with an + + additional field `@type` which contains the type URL. + Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a + custom JSON + + representation, that representation will be embedded + adding a field + + `value` which holds the custom JSON in addition to the + `@type` + + field. Example (for message + [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + status: + description: status defines the proposal status. + type: string + enum: + - PROPOSAL_STATUS_UNSPECIFIED + - PROPOSAL_STATUS_DEPOSIT_PERIOD + - PROPOSAL_STATUS_VOTING_PERIOD + - PROPOSAL_STATUS_PASSED + - PROPOSAL_STATUS_REJECTED + - PROPOSAL_STATUS_FAILED + default: PROPOSAL_STATUS_UNSPECIFIED + final_tally_result: + description: >- + final_tally_result is the final tally result of the + proposal. When + + querying a proposal via gRPC, this field is not + populated until the + + proposal's voting period has ended. + type: object + properties: + 'yes': + type: string + description: yes is the number of yes votes on a proposal. + abstain: + type: string + description: >- + abstain is the number of abstain votes on a + proposal. + 'no': + type: string + description: no is the number of no votes on a proposal. + no_with_veto: + type: string + description: >- + no_with_veto is the number of no with veto votes on + a proposal. + submit_time: + type: string + format: date-time + description: submit_time is the time of proposal submission. + deposit_end_time: + type: string + format: date-time + description: deposit_end_time is the end time for deposition. + total_deposit: + type: array + items: + type: object + properties: + denom: + type: string + amount: + type: string + description: >- + Coin defines a token with a denomination and an + amount. + + + NOTE: The amount field is an Int which implements the + custom method + + signatures required by gogoproto. + description: total_deposit is the total deposit on the proposal. + voting_start_time: + type: string + format: date-time + description: >- + voting_start_time is the starting time to vote on a + proposal. + voting_end_time: + type: string + format: date-time + description: voting_end_time is the end time of voting on a proposal. + description: >- + Proposal defines the core field members of a governance + proposal. + description: proposals defines all the requested governance proposals. pagination: - description: pagination defines an pagination for the response. + description: pagination defines the pagination in the response. type: object properties: next_key: @@ -35467,11 +39368,10 @@ paths: was set, its value is undefined otherwise description: >- - QueryAllowancesByGranterResponse is the response type for the - Query/AllowancesByGranter RPC method. - + QueryProposalsResponse is the response type for the + Query/Proposals RPC - Since: cosmos-sdk 0.46 + method. default: description: An unexpected error response. schema: @@ -35667,9 +39567,41 @@ paths: "value": "1.212s" } parameters: - - name: granter - in: path - required: true + - name: proposal_status + description: |- + proposal_status defines the status of the proposals. + + - PROPOSAL_STATUS_UNSPECIFIED: PROPOSAL_STATUS_UNSPECIFIED defines the default proposal status. + - PROPOSAL_STATUS_DEPOSIT_PERIOD: PROPOSAL_STATUS_DEPOSIT_PERIOD defines a proposal status during the deposit + period. + - PROPOSAL_STATUS_VOTING_PERIOD: PROPOSAL_STATUS_VOTING_PERIOD defines a proposal status during the voting + period. + - PROPOSAL_STATUS_PASSED: PROPOSAL_STATUS_PASSED defines a proposal status of a proposal that has + passed. + - PROPOSAL_STATUS_REJECTED: PROPOSAL_STATUS_REJECTED defines a proposal status of a proposal that has + been rejected. + - PROPOSAL_STATUS_FAILED: PROPOSAL_STATUS_FAILED defines a proposal status of a proposal that has + failed. + in: query + required: false + type: string + enum: + - PROPOSAL_STATUS_UNSPECIFIED + - PROPOSAL_STATUS_DEPOSIT_PERIOD + - PROPOSAL_STATUS_VOTING_PERIOD + - PROPOSAL_STATUS_PASSED + - PROPOSAL_STATUS_REJECTED + - PROPOSAL_STATUS_FAILED + default: PROPOSAL_STATUS_UNSPECIFIED + - name: voter + description: voter defines the voter address for the proposals. + in: query + required: false + type: string + - name: depositor + description: depositor defines the deposit addresses from the proposals. + in: query + required: false type: string - name: pagination.key description: |- @@ -35729,28 +39661,248 @@ paths: type: boolean tags: - Query - /cosmos/gov/v1beta1/params/{params_type}: + /cosmos/gov/v1beta1/proposals/{proposal_id}: get: - summary: Params queries all parameters of the gov module. - operationId: GovParams + summary: Proposal queries proposal details based on ProposalID. + operationId: Proposal responses: '200': description: A successful response. schema: type: object properties: - voting_params: - description: voting_params defines the parameters related to voting. + proposal: type: object properties: - voting_period: + proposal_id: type: string - description: Length of the voting period. - deposit_params: - description: deposit_params defines the parameters related to deposit. - type: object - properties: - min_deposit: + format: uint64 + description: proposal_id defines the unique id of the proposal. + content: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type + of the serialized + + protocol buffer message. This string must contain at + least + + one "/" character. The last segment of the URL's path + must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be + in a canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary + all types that they + + expect it to use in the context of Any. However, for + URLs which use the + + scheme `http`, `https`, or no scheme, one can + optionally set up a type + + server that maps type URLs to message definitions as + follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results + based on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in + the official + + protobuf release, and it is not used for type URLs + beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty + scheme) might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the + above specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer + message along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any + values in the form + + of utility functions or additional generated methods of + the Any type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + // or ... + if (any.isSameTypeAs(Foo.getDefaultInstance())) { + foo = any.unpack(Foo.getDefaultInstance()); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := anypb.New(foo) + if err != nil { + ... + } + ... + foo := &pb.Foo{} + if err := any.UnmarshalTo(foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by + default use + + 'type.googleapis.com/full.type.name' as the type URL and + the unpack + + methods only use the fully qualified type name after the + last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will + yield type + + name "y.z". + + + JSON + + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with + an + + additional field `@type` which contains the type URL. + Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a + custom JSON + + representation, that representation will be embedded + adding a field + + `value` which holds the custom JSON in addition to the + `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + status: + description: status defines the proposal status. + type: string + enum: + - PROPOSAL_STATUS_UNSPECIFIED + - PROPOSAL_STATUS_DEPOSIT_PERIOD + - PROPOSAL_STATUS_VOTING_PERIOD + - PROPOSAL_STATUS_PASSED + - PROPOSAL_STATUS_REJECTED + - PROPOSAL_STATUS_FAILED + default: PROPOSAL_STATUS_UNSPECIFIED + final_tally_result: + description: >- + final_tally_result is the final tally result of the + proposal. When + + querying a proposal via gRPC, this field is not populated + until the + + proposal's voting period has ended. + type: object + properties: + 'yes': + type: string + description: yes is the number of yes votes on a proposal. + abstain: + type: string + description: abstain is the number of abstain votes on a proposal. + 'no': + type: string + description: no is the number of no votes on a proposal. + no_with_veto: + type: string + description: >- + no_with_veto is the number of no with veto votes on a + proposal. + submit_time: + type: string + format: date-time + description: submit_time is the time of proposal submission. + deposit_end_time: + type: string + format: date-time + description: deposit_end_time is the end time for deposition. + total_deposit: type: array items: type: object @@ -35767,40 +39919,23 @@ paths: custom method signatures required by gogoproto. - description: Minimum deposit for a proposal to enter voting period. - max_deposit_period: - type: string - description: >- - Maximum period for Atom holders to deposit on a proposal. - Initial value: 2 - months. - tally_params: - description: tally_params defines the parameters related to tally. - type: object - properties: - quorum: - type: string - format: byte - description: >- - Minimum percentage of total stake needed to vote for a - result to be - considered valid. - threshold: + description: total_deposit is the total deposit on the proposal. + voting_start_time: type: string - format: byte + format: date-time description: >- - Minimum proportion of Yes votes for proposal to pass. - Default value: 0.5. - veto_threshold: + voting_start_time is the starting time to vote on a + proposal. + voting_end_time: type: string - format: byte - description: >- - Minimum value of Veto votes to Total votes ratio for - proposal to be - vetoed. Default value: 1/3. + format: date-time + description: voting_end_time is the end time of voting on a proposal. + description: >- + Proposal defines the core field members of a governance + proposal. description: >- - QueryParamsResponse is the response type for the Query/Params RPC - method. + QueryProposalResponse is the response type for the Query/Proposal + RPC method. default: description: An unexpected error response. schema: @@ -35960,304 +40095,75 @@ paths: JSON - The JSON representation of an `Any` value uses the regular - - representation of the deserialized, embedded message, with - an - - additional field `@type` which contains the type URL. - Example: - - package google.profile; - message Person { - string first_name = 1; - string last_name = 2; - } - - { - "@type": "type.googleapis.com/google.profile.Person", - "firstName": , - "lastName": - } - - If the embedded message type is well-known and has a custom - JSON - - representation, that representation will be embedded adding - a field - - `value` which holds the custom JSON in addition to the - `@type` - - field. Example (for message [google.protobuf.Duration][]): - - { - "@type": "type.googleapis.com/google.protobuf.Duration", - "value": "1.212s" - } - parameters: - - name: params_type - description: >- - params_type defines which parameters to query for, can be one of - "voting", - - "tallying" or "deposit". - in: path - required: true - type: string - tags: - - Query - /cosmos/gov/v1beta1/proposals: - get: - summary: Proposals queries all proposals based on given status. - operationId: Proposals - responses: - '200': - description: A successful response. - schema: - type: object - properties: - proposals: - type: array - items: - type: object - properties: - proposal_id: - type: string - format: uint64 - content: - type: object - properties: - type_url: - type: string - description: >- - A URL/resource name that uniquely identifies the - type of the serialized - - protocol buffer message. This string must contain at - least - - one "/" character. The last segment of the URL's - path must represent - - the fully qualified name of the type (as in - - `path/google.protobuf.Duration`). The name should be - in a canonical form - - (e.g., leading "." is not accepted). - - - In practice, teams usually precompile into the - binary all types that they - - expect it to use in the context of Any. However, for - URLs which use the - - scheme `http`, `https`, or no scheme, one can - optionally set up a type - - server that maps type URLs to message definitions as - follows: - - - * If no scheme is provided, `https` is assumed. - - * An HTTP GET on the URL must yield a - [google.protobuf.Type][] - value in binary format, or produce an error. - * Applications are allowed to cache lookup results - based on the - URL, or have them precompiled into a binary to avoid any - lookup. Therefore, binary compatibility needs to be preserved - on changes to types. (Use versioned type names to manage - breaking changes.) - - Note: this functionality is not currently available - in the official - - protobuf release, and it is not used for type URLs - beginning with - - type.googleapis.com. - - - Schemes other than `http`, `https` (or the empty - scheme) might be - - used with implementation specific semantics. - value: - type: string - format: byte - description: >- - Must be a valid serialized protocol buffer of the - above specified type. - description: >- - `Any` contains an arbitrary serialized protocol buffer - message along with a - - URL that describes the type of the serialized message. - - - Protobuf library provides support to pack/unpack Any - values in the form - - of utility functions or additional generated methods of - the Any type. - - - Example 1: Pack and unpack a message in C++. - - Foo foo = ...; - Any any; - any.PackFrom(foo); - ... - if (any.UnpackTo(&foo)) { - ... - } - - Example 2: Pack and unpack a message in Java. - - Foo foo = ...; - Any any = Any.pack(foo); - ... - if (any.is(Foo.class)) { - foo = any.unpack(Foo.class); - } - // or ... - if (any.isSameTypeAs(Foo.getDefaultInstance())) { - foo = any.unpack(Foo.getDefaultInstance()); - } - - Example 3: Pack and unpack a message in Python. - - foo = Foo(...) - any = Any() - any.Pack(foo) - ... - if any.Is(Foo.DESCRIPTOR): - any.Unpack(foo) - ... - - Example 4: Pack and unpack a message in Go - - foo := &pb.Foo{...} - any, err := anypb.New(foo) - if err != nil { - ... - } - ... - foo := &pb.Foo{} - if err := any.UnmarshalTo(foo); err != nil { - ... - } - - The pack methods provided by protobuf library will by - default use - - 'type.googleapis.com/full.type.name' as the type URL and - the unpack - - methods only use the fully qualified type name after the - last '/' - - in the type URL, for example "foo.bar.com/x/y.z" will - yield type - - name "y.z". - - - JSON - - - The JSON representation of an `Any` value uses the - regular - - representation of the deserialized, embedded message, - with an - - additional field `@type` which contains the type URL. - Example: - - package google.profile; - message Person { - string first_name = 1; - string last_name = 2; - } - - { - "@type": "type.googleapis.com/google.profile.Person", - "firstName": , - "lastName": - } - - If the embedded message type is well-known and has a - custom JSON + The JSON representation of an `Any` value uses the regular - representation, that representation will be embedded - adding a field + representation of the deserialized, embedded message, with + an - `value` which holds the custom JSON in addition to the - `@type` + additional field `@type` which contains the type URL. + Example: - field. Example (for message - [google.protobuf.Duration][]): + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } - { - "@type": "type.googleapis.com/google.protobuf.Duration", - "value": "1.212s" - } - status: - type: string - enum: - - PROPOSAL_STATUS_UNSPECIFIED - - PROPOSAL_STATUS_DEPOSIT_PERIOD - - PROPOSAL_STATUS_VOTING_PERIOD - - PROPOSAL_STATUS_PASSED - - PROPOSAL_STATUS_REJECTED - - PROPOSAL_STATUS_FAILED - default: PROPOSAL_STATUS_UNSPECIFIED - description: >- - ProposalStatus enumerates the valid statuses of a - proposal. + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } - - PROPOSAL_STATUS_UNSPECIFIED: PROPOSAL_STATUS_UNSPECIFIED defines the default proposal status. - - PROPOSAL_STATUS_DEPOSIT_PERIOD: PROPOSAL_STATUS_DEPOSIT_PERIOD defines a proposal status during the deposit - period. - - PROPOSAL_STATUS_VOTING_PERIOD: PROPOSAL_STATUS_VOTING_PERIOD defines a proposal status during the voting - period. - - PROPOSAL_STATUS_PASSED: PROPOSAL_STATUS_PASSED defines a proposal status of a proposal that has - passed. - - PROPOSAL_STATUS_REJECTED: PROPOSAL_STATUS_REJECTED defines a proposal status of a proposal that has - been rejected. - - PROPOSAL_STATUS_FAILED: PROPOSAL_STATUS_FAILED defines a proposal status of a proposal that has - failed. - final_tally_result: - description: >- - final_tally_result is the final tally result of the - proposal. When + If the embedded message type is well-known and has a custom + JSON - querying a proposal via gRPC, this field is not - populated until the + representation, that representation will be embedded adding + a field - proposal's voting period has ended. - type: object - properties: - 'yes': - type: string - abstain: - type: string - 'no': - type: string - no_with_veto: - type: string - submit_time: + `value` which holds the custom JSON in addition to the + `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + parameters: + - name: proposal_id + description: proposal_id defines the unique id of the proposal. + in: path + required: true + type: string + format: uint64 + tags: + - Query + /cosmos/gov/v1beta1/proposals/{proposal_id}/deposits: + get: + summary: Deposits queries all deposits of a single proposal. + operationId: Deposits + responses: + '200': + description: A successful response. + schema: + type: object + properties: + deposits: + type: array + items: + type: object + properties: + proposal_id: type: string - format: date-time - deposit_end_time: + format: uint64 + description: proposal_id defines the unique id of the proposal. + depositor: type: string - format: date-time - total_deposit: + description: >- + depositor defines the deposit addresses from the + proposals. + amount: type: array items: type: object @@ -36275,15 +40181,13 @@ paths: custom method signatures required by gogoproto. - voting_start_time: - type: string - format: date-time - voting_end_time: - type: string - format: date-time + description: amount to be deposited by depositor. description: >- - Proposal defines the core field members of a governance + Deposit defines an amount deposited by an account address to + an active + proposal. + description: deposits defines the requested deposits. pagination: description: pagination defines the pagination in the response. type: object @@ -36304,10 +40208,8 @@ paths: was set, its value is undefined otherwise description: >- - QueryProposalsResponse is the response type for the - Query/Proposals RPC - - method. + QueryDepositsResponse is the response type for the Query/Deposits + RPC method. default: description: An unexpected error response. schema: @@ -36503,42 +40405,12 @@ paths: "value": "1.212s" } parameters: - - name: proposal_status - description: |- - proposal_status defines the status of the proposals. - - - PROPOSAL_STATUS_UNSPECIFIED: PROPOSAL_STATUS_UNSPECIFIED defines the default proposal status. - - PROPOSAL_STATUS_DEPOSIT_PERIOD: PROPOSAL_STATUS_DEPOSIT_PERIOD defines a proposal status during the deposit - period. - - PROPOSAL_STATUS_VOTING_PERIOD: PROPOSAL_STATUS_VOTING_PERIOD defines a proposal status during the voting - period. - - PROPOSAL_STATUS_PASSED: PROPOSAL_STATUS_PASSED defines a proposal status of a proposal that has - passed. - - PROPOSAL_STATUS_REJECTED: PROPOSAL_STATUS_REJECTED defines a proposal status of a proposal that has - been rejected. - - PROPOSAL_STATUS_FAILED: PROPOSAL_STATUS_FAILED defines a proposal status of a proposal that has - failed. - in: query - required: false - type: string - enum: - - PROPOSAL_STATUS_UNSPECIFIED - - PROPOSAL_STATUS_DEPOSIT_PERIOD - - PROPOSAL_STATUS_VOTING_PERIOD - - PROPOSAL_STATUS_PASSED - - PROPOSAL_STATUS_REJECTED - - PROPOSAL_STATUS_FAILED - default: PROPOSAL_STATUS_UNSPECIFIED - - name: voter - description: voter defines the voter address for the proposals. - in: query - required: false - type: string - - name: depositor - description: depositor defines the deposit addresses from the proposals. - in: query - required: false + - name: proposal_id + description: proposal_id defines the unique id of the proposal. + in: path + required: true type: string + format: uint64 - name: pagination.key description: |- key is a value returned in PageResponse.next_key to begin @@ -36597,280 +40469,294 @@ paths: type: boolean tags: - Query - /cosmos/gov/v1beta1/proposals/{proposal_id}: + /cosmos/gov/v1beta1/proposals/{proposal_id}/deposits/{depositor}: get: - summary: Proposal queries proposal details based on ProposalID. - operationId: Proposal + summary: >- + Deposit queries single deposit information based proposalID, + depositAddr. + operationId: Deposit responses: '200': description: A successful response. schema: type: object properties: - proposal: + deposit: type: object properties: proposal_id: type: string format: uint64 - content: - type: object - properties: - type_url: - type: string - description: >- - A URL/resource name that uniquely identifies the type - of the serialized + description: proposal_id defines the unique id of the proposal. + depositor: + type: string + description: >- + depositor defines the deposit addresses from the + proposals. + amount: + type: array + items: + type: object + properties: + denom: + type: string + amount: + type: string + description: >- + Coin defines a token with a denomination and an amount. - protocol buffer message. This string must contain at - least - one "/" character. The last segment of the URL's path - must represent + NOTE: The amount field is an Int which implements the + custom method - the fully qualified name of the type (as in + signatures required by gogoproto. + description: amount to be deposited by depositor. + description: >- + Deposit defines an amount deposited by an account address to + an active - `path/google.protobuf.Duration`). The name should be - in a canonical form + proposal. + description: >- + QueryDepositResponse is the response type for the Query/Deposit + RPC method. + default: + description: An unexpected error response. + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of + the serialized - (e.g., leading "." is not accepted). + protocol buffer message. This string must contain at + least + one "/" character. The last segment of the URL's path + must represent - In practice, teams usually precompile into the binary - all types that they + the fully qualified name of the type (as in - expect it to use in the context of Any. However, for - URLs which use the + `path/google.protobuf.Duration`). The name should be in + a canonical form - scheme `http`, `https`, or no scheme, one can - optionally set up a type + (e.g., leading "." is not accepted). - server that maps type URLs to message definitions as - follows: + In practice, teams usually precompile into the binary + all types that they - * If no scheme is provided, `https` is assumed. + expect it to use in the context of Any. However, for + URLs which use the - * An HTTP GET on the URL must yield a - [google.protobuf.Type][] - value in binary format, or produce an error. - * Applications are allowed to cache lookup results - based on the - URL, or have them precompiled into a binary to avoid any - lookup. Therefore, binary compatibility needs to be preserved - on changes to types. (Use versioned type names to manage - breaking changes.) + scheme `http`, `https`, or no scheme, one can optionally + set up a type - Note: this functionality is not currently available in - the official + server that maps type URLs to message definitions as + follows: - protobuf release, and it is not used for type URLs - beginning with - type.googleapis.com. + * If no scheme is provided, `https` is assumed. + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based + on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) - Schemes other than `http`, `https` (or the empty - scheme) might be + Note: this functionality is not currently available in + the official - used with implementation specific semantics. - value: - type: string - format: byte - description: >- - Must be a valid serialized protocol buffer of the - above specified type. - description: >- - `Any` contains an arbitrary serialized protocol buffer - message along with a + protobuf release, and it is not used for type URLs + beginning with - URL that describes the type of the serialized message. + type.googleapis.com. - Protobuf library provides support to pack/unpack Any - values in the form + Schemes other than `http`, `https` (or the empty scheme) + might be - of utility functions or additional generated methods of - the Any type. + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer + message along with a + URL that describes the type of the serialized message. - Example 1: Pack and unpack a message in C++. - Foo foo = ...; - Any any; - any.PackFrom(foo); - ... - if (any.UnpackTo(&foo)) { - ... - } + Protobuf library provides support to pack/unpack Any values + in the form - Example 2: Pack and unpack a message in Java. + of utility functions or additional generated methods of the + Any type. - Foo foo = ...; - Any any = Any.pack(foo); - ... - if (any.is(Foo.class)) { - foo = any.unpack(Foo.class); - } - // or ... - if (any.isSameTypeAs(Foo.getDefaultInstance())) { - foo = any.unpack(Foo.getDefaultInstance()); - } - Example 3: Pack and unpack a message in Python. + Example 1: Pack and unpack a message in C++. - foo = Foo(...) - any = Any() - any.Pack(foo) + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { ... - if any.Is(Foo.DESCRIPTOR): - any.Unpack(foo) - ... + } - Example 4: Pack and unpack a message in Go + Example 2: Pack and unpack a message in Java. - foo := &pb.Foo{...} - any, err := anypb.New(foo) - if err != nil { - ... - } - ... - foo := &pb.Foo{} - if err := any.UnmarshalTo(foo); err != nil { - ... - } + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + // or ... + if (any.isSameTypeAs(Foo.getDefaultInstance())) { + foo = any.unpack(Foo.getDefaultInstance()); + } - The pack methods provided by protobuf library will by - default use + Example 3: Pack and unpack a message in Python. - 'type.googleapis.com/full.type.name' as the type URL and - the unpack + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... - methods only use the fully qualified type name after the - last '/' + Example 4: Pack and unpack a message in Go - in the type URL, for example "foo.bar.com/x/y.z" will - yield type + foo := &pb.Foo{...} + any, err := anypb.New(foo) + if err != nil { + ... + } + ... + foo := &pb.Foo{} + if err := any.UnmarshalTo(foo); err != nil { + ... + } - name "y.z". + The pack methods provided by protobuf library will by + default use + 'type.googleapis.com/full.type.name' as the type URL and the + unpack - JSON + methods only use the fully qualified type name after the + last '/' + in the type URL, for example "foo.bar.com/x/y.z" will yield + type - The JSON representation of an `Any` value uses the regular + name "y.z". - representation of the deserialized, embedded message, with - an - additional field `@type` which contains the type URL. - Example: + JSON - package google.profile; - message Person { - string first_name = 1; - string last_name = 2; - } - { - "@type": "type.googleapis.com/google.profile.Person", - "firstName": , - "lastName": - } + The JSON representation of an `Any` value uses the regular - If the embedded message type is well-known and has a - custom JSON + representation of the deserialized, embedded message, with + an - representation, that representation will be embedded - adding a field + additional field `@type` which contains the type URL. + Example: - `value` which holds the custom JSON in addition to the - `@type` + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } - field. Example (for message [google.protobuf.Duration][]): + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } - { - "@type": "type.googleapis.com/google.protobuf.Duration", - "value": "1.212s" - } - status: - type: string - enum: - - PROPOSAL_STATUS_UNSPECIFIED - - PROPOSAL_STATUS_DEPOSIT_PERIOD - - PROPOSAL_STATUS_VOTING_PERIOD - - PROPOSAL_STATUS_PASSED - - PROPOSAL_STATUS_REJECTED - - PROPOSAL_STATUS_FAILED - default: PROPOSAL_STATUS_UNSPECIFIED - description: >- - ProposalStatus enumerates the valid statuses of a - proposal. + If the embedded message type is well-known and has a custom + JSON - - PROPOSAL_STATUS_UNSPECIFIED: PROPOSAL_STATUS_UNSPECIFIED defines the default proposal status. - - PROPOSAL_STATUS_DEPOSIT_PERIOD: PROPOSAL_STATUS_DEPOSIT_PERIOD defines a proposal status during the deposit - period. - - PROPOSAL_STATUS_VOTING_PERIOD: PROPOSAL_STATUS_VOTING_PERIOD defines a proposal status during the voting - period. - - PROPOSAL_STATUS_PASSED: PROPOSAL_STATUS_PASSED defines a proposal status of a proposal that has - passed. - - PROPOSAL_STATUS_REJECTED: PROPOSAL_STATUS_REJECTED defines a proposal status of a proposal that has - been rejected. - - PROPOSAL_STATUS_FAILED: PROPOSAL_STATUS_FAILED defines a proposal status of a proposal that has - failed. - final_tally_result: - description: >- - final_tally_result is the final tally result of the - proposal. When + representation, that representation will be embedded adding + a field - querying a proposal via gRPC, this field is not populated - until the + `value` which holds the custom JSON in addition to the + `@type` - proposal's voting period has ended. - type: object - properties: - 'yes': - type: string - abstain: - type: string - 'no': - type: string - no_with_veto: - type: string - submit_time: + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + parameters: + - name: proposal_id + description: proposal_id defines the unique id of the proposal. + in: path + required: true + type: string + format: uint64 + - name: depositor + description: depositor defines the deposit addresses from the proposals. + in: path + required: true + type: string + tags: + - Query + /cosmos/gov/v1beta1/proposals/{proposal_id}/tally: + get: + summary: TallyResult queries the tally of a proposal vote. + operationId: TallyResult + responses: + '200': + description: A successful response. + schema: + type: object + properties: + tally: + description: tally defines the requested tally. + type: object + properties: + 'yes': type: string - format: date-time - deposit_end_time: + description: yes is the number of yes votes on a proposal. + abstain: type: string - format: date-time - total_deposit: - type: array - items: - type: object - properties: - denom: - type: string - amount: - type: string - description: >- - Coin defines a token with a denomination and an amount. - - - NOTE: The amount field is an Int which implements the - custom method - - signatures required by gogoproto. - voting_start_time: + description: abstain is the number of abstain votes on a proposal. + 'no': type: string - format: date-time - voting_end_time: + description: no is the number of no votes on a proposal. + no_with_veto: type: string - format: date-time - description: >- - Proposal defines the core field members of a governance - proposal. + description: >- + no_with_veto is the number of no with veto votes on a + proposal. description: >- - QueryProposalResponse is the response type for the Query/Proposal + QueryTallyResultResponse is the response type for the Query/Tally RPC method. default: description: An unexpected error response. @@ -37075,17 +40961,17 @@ paths: format: uint64 tags: - Query - /cosmos/gov/v1beta1/proposals/{proposal_id}/deposits: + /cosmos/gov/v1beta1/proposals/{proposal_id}/votes: get: - summary: Deposits queries all deposits of a single proposal. - operationId: Deposits + summary: Votes queries votes of a given proposal. + operationId: Votes responses: '200': description: A successful response. schema: type: object properties: - deposits: + votes: type: array items: type: object @@ -37093,31 +40979,66 @@ paths: proposal_id: type: string format: uint64 - depositor: + description: proposal_id defines the unique id of the proposal. + voter: type: string - amount: + description: voter is the voter address of the proposal. + option: + description: >- + Deprecated: Prefer to use `options` instead. This field + is set in queries + + if and only if `len(options) == 1` and that option has + weight 1. In all + + other cases, this field will default to + VOTE_OPTION_UNSPECIFIED. + type: string + enum: + - VOTE_OPTION_UNSPECIFIED + - VOTE_OPTION_YES + - VOTE_OPTION_ABSTAIN + - VOTE_OPTION_NO + - VOTE_OPTION_NO_WITH_VETO + default: VOTE_OPTION_UNSPECIFIED + options: type: array items: type: object properties: - denom: + option: + description: >- + option defines the valid vote options, it must not + contain duplicate vote options. type: string - amount: + enum: + - VOTE_OPTION_UNSPECIFIED + - VOTE_OPTION_YES + - VOTE_OPTION_ABSTAIN + - VOTE_OPTION_NO + - VOTE_OPTION_NO_WITH_VETO + default: VOTE_OPTION_UNSPECIFIED + weight: type: string + description: >- + weight is the vote weight associated with the vote + option. description: >- - Coin defines a token with a denomination and an - amount. + WeightedVoteOption defines a unit of vote for vote + split. - NOTE: The amount field is an Int which implements the - custom method + Since: cosmos-sdk 0.43 + description: |- + options is the weighted vote options. - signatures required by gogoproto. + Since: cosmos-sdk 0.43 description: >- - Deposit defines an amount deposited by an account address to - an active + Vote defines a vote on a governance proposal. - proposal. + A Vote consists of a proposal ID, the voter, and the vote + option. + description: votes defines the queried votes. pagination: description: pagination defines the pagination in the response. type: object @@ -37138,8 +41059,8 @@ paths: was set, its value is undefined otherwise description: >- - QueryDepositsResponse is the response type for the Query/Deposits - RPC method. + QueryVotesResponse is the response type for the Query/Votes RPC + method. default: description: An unexpected error response. schema: @@ -37399,51 +41320,84 @@ paths: type: boolean tags: - Query - /cosmos/gov/v1beta1/proposals/{proposal_id}/deposits/{depositor}: + /cosmos/gov/v1beta1/proposals/{proposal_id}/votes/{voter}: get: - summary: >- - Deposit queries single deposit information based proposalID, - depositAddr. - operationId: Deposit + summary: Vote queries voted information based on proposalID, voterAddr. + operationId: Vote responses: '200': description: A successful response. schema: type: object properties: - deposit: + vote: type: object properties: proposal_id: type: string format: uint64 - depositor: + description: proposal_id defines the unique id of the proposal. + voter: type: string - amount: + description: voter is the voter address of the proposal. + option: + description: >- + Deprecated: Prefer to use `options` instead. This field is + set in queries + + if and only if `len(options) == 1` and that option has + weight 1. In all + + other cases, this field will default to + VOTE_OPTION_UNSPECIFIED. + type: string + enum: + - VOTE_OPTION_UNSPECIFIED + - VOTE_OPTION_YES + - VOTE_OPTION_ABSTAIN + - VOTE_OPTION_NO + - VOTE_OPTION_NO_WITH_VETO + default: VOTE_OPTION_UNSPECIFIED + options: type: array items: type: object properties: - denom: + option: + description: >- + option defines the valid vote options, it must not + contain duplicate vote options. type: string - amount: + enum: + - VOTE_OPTION_UNSPECIFIED + - VOTE_OPTION_YES + - VOTE_OPTION_ABSTAIN + - VOTE_OPTION_NO + - VOTE_OPTION_NO_WITH_VETO + default: VOTE_OPTION_UNSPECIFIED + weight: type: string + description: >- + weight is the vote weight associated with the vote + option. description: >- - Coin defines a token with a denomination and an amount. + WeightedVoteOption defines a unit of vote for vote + split. - NOTE: The amount field is an Int which implements the - custom method + Since: cosmos-sdk 0.43 + description: |- + options is the weighted vote options. - signatures required by gogoproto. + Since: cosmos-sdk 0.43 description: >- - Deposit defines an amount deposited by an account address to - an active + Vote defines a vote on a governance proposal. - proposal. + A Vote consists of a proposal ID, the voter, and the vote + option. description: >- - QueryDepositResponse is the response type for the Query/Deposit - RPC method. + QueryVoteResponse is the response type for the Query/Vote RPC + method. default: description: An unexpected error response. schema: @@ -37645,38 +41599,157 @@ paths: required: true type: string format: uint64 - - name: depositor - description: depositor defines the deposit addresses from the proposals. + - name: voter + description: voter defines the voter address for the proposals. in: path required: true type: string tags: - Query - /cosmos/gov/v1beta1/proposals/{proposal_id}/tally: + /cosmos/gov/v1/params/{params_type}: get: - summary: TallyResult queries the tally of a proposal vote. - operationId: TallyResult + summary: Params queries all parameters of the gov module. + operationId: GovV1Params responses: '200': description: A successful response. schema: type: object properties: - tally: - description: tally defines the requested tally. + voting_params: + description: |- + Deprecated: Prefer to use `params` instead. + voting_params defines the parameters related to voting. type: object properties: - 'yes': + voting_period: type: string - abstain: + description: Duration of the voting period. + deposit_params: + description: |- + Deprecated: Prefer to use `params` instead. + deposit_params defines the parameters related to deposit. + type: object + properties: + min_deposit: + type: array + items: + type: object + properties: + denom: + type: string + amount: + type: string + description: >- + Coin defines a token with a denomination and an amount. + + + NOTE: The amount field is an Int which implements the + custom method + + signatures required by gogoproto. + description: Minimum deposit for a proposal to enter voting period. + max_deposit_period: type: string - 'no': + description: >- + Maximum period for Atom holders to deposit on a proposal. + Initial value: 2 + + months. + tally_params: + description: |- + Deprecated: Prefer to use `params` instead. + tally_params defines the parameters related to tally. + type: object + properties: + quorum: type: string - no_with_veto: + description: >- + Minimum percentage of total stake needed to vote for a + result to be + + considered valid. + threshold: + type: string + description: >- + Minimum proportion of Yes votes for proposal to pass. + Default value: 0.5. + veto_threshold: + type: string + description: >- + Minimum value of Veto votes to Total votes ratio for + proposal to be + + vetoed. Default value: 1/3. + params: + description: |- + params defines all the paramaters of x/gov module. + + Since: cosmos-sdk 0.47 + type: object + properties: + min_deposit: + type: array + items: + type: object + properties: + denom: + type: string + amount: + type: string + description: >- + Coin defines a token with a denomination and an amount. + + + NOTE: The amount field is an Int which implements the + custom method + + signatures required by gogoproto. + description: Minimum deposit for a proposal to enter voting period. + max_deposit_period: + type: string + description: >- + Maximum period for Atom holders to deposit on a proposal. + Initial value: 2 + + months. + voting_period: + type: string + description: Duration of the voting period. + quorum: + type: string + description: >- + Minimum percentage of total stake needed to vote for a + result to be + considered valid. + threshold: + type: string + description: >- + Minimum proportion of Yes votes for proposal to pass. + Default value: 0.5. + veto_threshold: type: string + description: >- + Minimum value of Veto votes to Total votes ratio for + proposal to be + vetoed. Default value: 1/3. + min_initial_deposit_ratio: + type: string + description: >- + The ratio representing the proportion of the deposit value + that must be paid at proposal submission. + burn_vote_quorum: + type: boolean + title: burn deposits if a proposal does not meet quorum + burn_proposal_deposit_prevote: + type: boolean + title: burn deposits if the proposal does not enter voting period + burn_vote_veto: + type: boolean + title: burn deposits if quorum with vote type no_veto is met description: >- - QueryTallyResultResponse is the response type for the Query/Tally - RPC method. + QueryParamsResponse is the response type for the Query/Params RPC + method. default: description: An unexpected error response. schema: @@ -37784,178 +41857,407 @@ paths: Example 2: Pack and unpack a message in Java. - Foo foo = ...; - Any any = Any.pack(foo); - ... - if (any.is(Foo.class)) { - foo = any.unpack(Foo.class); - } - // or ... - if (any.isSameTypeAs(Foo.getDefaultInstance())) { - foo = any.unpack(Foo.getDefaultInstance()); - } + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + // or ... + if (any.isSameTypeAs(Foo.getDefaultInstance())) { + foo = any.unpack(Foo.getDefaultInstance()); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := anypb.New(foo) + if err != nil { + ... + } + ... + foo := &pb.Foo{} + if err := any.UnmarshalTo(foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by + default use + + 'type.googleapis.com/full.type.name' as the type URL and the + unpack + + methods only use the fully qualified type name after the + last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield + type + + name "y.z". + + + JSON + + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with + an + + additional field `@type` which contains the type URL. + Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom + JSON + + representation, that representation will be embedded adding + a field + + `value` which holds the custom JSON in addition to the + `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + parameters: + - name: params_type + description: >- + params_type defines which parameters to query for, can be one of + "voting", + + "tallying" or "deposit". + in: path + required: true + type: string + tags: + - Query + /cosmos/gov/v1/proposals: + get: + summary: Proposals queries all proposals based on given status. + operationId: GovV1Proposal + responses: + '200': + description: A successful response. + schema: + type: object + properties: + proposals: + type: array + items: + type: object + properties: + id: + type: string + format: uint64 + description: id defines the unique id of the proposal. + messages: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the + type of the serialized + + protocol buffer message. This string must contain + at least + + one "/" character. The last segment of the URL's + path must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should + be in a canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the + binary all types that they + + expect it to use in the context of Any. However, + for URLs which use the + + scheme `http`, `https`, or no scheme, one can + optionally set up a type + + server that maps type URLs to message definitions + as follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results + based on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently + available in the official + + protobuf release, and it is not used for type URLs + beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty + scheme) might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the + above specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer + message along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any + values in the form + + of utility functions or additional generated methods + of the Any type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + // or ... + if (any.isSameTypeAs(Foo.getDefaultInstance())) { + foo = any.unpack(Foo.getDefaultInstance()); + } - Example 3: Pack and unpack a message in Python. + Example 3: Pack and unpack a message in Python. - foo = Foo(...) - any = Any() - any.Pack(foo) - ... - if any.Is(Foo.DESCRIPTOR): - any.Unpack(foo) - ... + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... - Example 4: Pack and unpack a message in Go + Example 4: Pack and unpack a message in Go - foo := &pb.Foo{...} - any, err := anypb.New(foo) - if err != nil { - ... - } - ... - foo := &pb.Foo{} - if err := any.UnmarshalTo(foo); err != nil { - ... - } + foo := &pb.Foo{...} + any, err := anypb.New(foo) + if err != nil { + ... + } + ... + foo := &pb.Foo{} + if err := any.UnmarshalTo(foo); err != nil { + ... + } - The pack methods provided by protobuf library will by - default use + The pack methods provided by protobuf library will by + default use - 'type.googleapis.com/full.type.name' as the type URL and the - unpack + 'type.googleapis.com/full.type.name' as the type URL + and the unpack - methods only use the fully qualified type name after the - last '/' + methods only use the fully qualified type name after + the last '/' - in the type URL, for example "foo.bar.com/x/y.z" will yield - type + in the type URL, for example "foo.bar.com/x/y.z" will + yield type - name "y.z". + name "y.z". - JSON + JSON - The JSON representation of an `Any` value uses the regular + The JSON representation of an `Any` value uses the + regular - representation of the deserialized, embedded message, with - an + representation of the deserialized, embedded message, + with an - additional field `@type` which contains the type URL. - Example: + additional field `@type` which contains the type URL. + Example: - package google.profile; - message Person { - string first_name = 1; - string last_name = 2; - } + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } - { - "@type": "type.googleapis.com/google.profile.Person", - "firstName": , - "lastName": - } + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } - If the embedded message type is well-known and has a custom - JSON + If the embedded message type is well-known and has a + custom JSON - representation, that representation will be embedded adding - a field + representation, that representation will be embedded + adding a field - `value` which holds the custom JSON in addition to the - `@type` + `value` which holds the custom JSON in addition to the + `@type` - field. Example (for message [google.protobuf.Duration][]): + field. Example (for message + [google.protobuf.Duration][]): - { - "@type": "type.googleapis.com/google.protobuf.Duration", - "value": "1.212s" - } - parameters: - - name: proposal_id - description: proposal_id defines the unique id of the proposal. - in: path - required: true - type: string - format: uint64 - tags: - - Query - /cosmos/gov/v1beta1/proposals/{proposal_id}/votes: - get: - summary: Votes queries votes of a given proposal. - operationId: Votes - responses: - '200': - description: A successful response. - schema: - type: object - properties: - votes: - type: array - items: - type: object - properties: - proposal_id: - type: string - format: uint64 - voter: + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + description: >- + messages are the arbitrary messages to be executed if + the proposal passes. + status: + description: status defines the proposal status. type: string - option: + enum: + - PROPOSAL_STATUS_UNSPECIFIED + - PROPOSAL_STATUS_DEPOSIT_PERIOD + - PROPOSAL_STATUS_VOTING_PERIOD + - PROPOSAL_STATUS_PASSED + - PROPOSAL_STATUS_REJECTED + - PROPOSAL_STATUS_FAILED + default: PROPOSAL_STATUS_UNSPECIFIED + final_tally_result: description: >- - Deprecated: Prefer to use `options` instead. This field - is set in queries + final_tally_result is the final tally result of the + proposal. When - if and only if `len(options) == 1` and that option has - weight 1. In all + querying a proposal via gRPC, this field is not + populated until the - other cases, this field will default to - VOTE_OPTION_UNSPECIFIED. + proposal's voting period has ended. + type: object + properties: + yes_count: + type: string + description: yes_count is the number of yes votes on a proposal. + abstain_count: + type: string + description: >- + abstain_count is the number of abstain votes on a + proposal. + no_count: + type: string + description: no_count is the number of no votes on a proposal. + no_with_veto_count: + type: string + description: >- + no_with_veto_count is the number of no with veto + votes on a proposal. + submit_time: type: string - enum: - - VOTE_OPTION_UNSPECIFIED - - VOTE_OPTION_YES - - VOTE_OPTION_ABSTAIN - - VOTE_OPTION_NO - - VOTE_OPTION_NO_WITH_VETO - default: VOTE_OPTION_UNSPECIFIED - options: + format: date-time + description: submit_time is the time of proposal submission. + deposit_end_time: + type: string + format: date-time + description: deposit_end_time is the end time for deposition. + total_deposit: type: array items: type: object properties: - option: + denom: type: string - enum: - - VOTE_OPTION_UNSPECIFIED - - VOTE_OPTION_YES - - VOTE_OPTION_ABSTAIN - - VOTE_OPTION_NO - - VOTE_OPTION_NO_WITH_VETO - default: VOTE_OPTION_UNSPECIFIED - description: >- - VoteOption enumerates the valid vote options for a - given governance proposal. - - - VOTE_OPTION_UNSPECIFIED: VOTE_OPTION_UNSPECIFIED defines a no-op vote option. - - VOTE_OPTION_YES: VOTE_OPTION_YES defines a yes vote option. - - VOTE_OPTION_ABSTAIN: VOTE_OPTION_ABSTAIN defines an abstain vote option. - - VOTE_OPTION_NO: VOTE_OPTION_NO defines a no vote option. - - VOTE_OPTION_NO_WITH_VETO: VOTE_OPTION_NO_WITH_VETO defines a no with veto vote option. - weight: + amount: type: string description: >- - WeightedVoteOption defines a unit of vote for vote - split. + Coin defines a token with a denomination and an + amount. - Since: cosmos-sdk 0.43 - title: 'Since: cosmos-sdk 0.43' - description: >- - Vote defines a vote on a governance proposal. + NOTE: The amount field is an Int which implements the + custom method - A Vote consists of a proposal ID, the voter, and the vote - option. - description: votes defined the queried votes. + signatures required by gogoproto. + description: total_deposit is the total deposit on the proposal. + voting_start_time: + type: string + format: date-time + description: >- + voting_start_time is the starting time to vote on a + proposal. + voting_end_time: + type: string + format: date-time + description: voting_end_time is the end time of voting on a proposal. + metadata: + type: string + description: >- + metadata is any arbitrary metadata attached to the + proposal. + title: + type: string + description: 'Since: cosmos-sdk 0.47' + title: title is the title of the proposal + summary: + type: string + description: 'Since: cosmos-sdk 0.47' + title: summary is a short summary of the proposal + proposer: + type: string + description: 'Since: cosmos-sdk 0.47' + title: Proposer is the address of the proposal sumbitter + description: >- + Proposal defines the core field members of a governance + proposal. + description: proposals defines all the requested governance proposals. pagination: description: pagination defines the pagination in the response. type: object @@ -37976,7 +42278,9 @@ paths: was set, its value is undefined otherwise description: >- - QueryVotesResponse is the response type for the Query/Votes RPC + QueryProposalsResponse is the response type for the + Query/Proposals RPC + method. default: description: An unexpected error response. @@ -38173,12 +42477,42 @@ paths: "value": "1.212s" } parameters: - - name: proposal_id - description: proposal_id defines the unique id of the proposal. - in: path - required: true + - name: proposal_status + description: |- + proposal_status defines the status of the proposals. + + - PROPOSAL_STATUS_UNSPECIFIED: PROPOSAL_STATUS_UNSPECIFIED defines the default proposal status. + - PROPOSAL_STATUS_DEPOSIT_PERIOD: PROPOSAL_STATUS_DEPOSIT_PERIOD defines a proposal status during the deposit + period. + - PROPOSAL_STATUS_VOTING_PERIOD: PROPOSAL_STATUS_VOTING_PERIOD defines a proposal status during the voting + period. + - PROPOSAL_STATUS_PASSED: PROPOSAL_STATUS_PASSED defines a proposal status of a proposal that has + passed. + - PROPOSAL_STATUS_REJECTED: PROPOSAL_STATUS_REJECTED defines a proposal status of a proposal that has + been rejected. + - PROPOSAL_STATUS_FAILED: PROPOSAL_STATUS_FAILED defines a proposal status of a proposal that has + failed. + in: query + required: false + type: string + enum: + - PROPOSAL_STATUS_UNSPECIFIED + - PROPOSAL_STATUS_DEPOSIT_PERIOD + - PROPOSAL_STATUS_VOTING_PERIOD + - PROPOSAL_STATUS_PASSED + - PROPOSAL_STATUS_REJECTED + - PROPOSAL_STATUS_FAILED + default: PROPOSAL_STATUS_UNSPECIFIED + - name: voter + description: voter defines the voter address for the proposals. + in: query + required: false + type: string + - name: depositor + description: depositor defines the deposit addresses from the proposals. + in: query + required: false type: string - format: uint64 - name: pagination.key description: |- key is a value returned in PageResponse.next_key to begin @@ -38237,82 +42571,307 @@ paths: type: boolean tags: - Query - /cosmos/gov/v1beta1/proposals/{proposal_id}/votes/{voter}: + /cosmos/gov/v1/proposals/{proposal_id}: get: - summary: Vote queries voted information based on proposalID, voterAddr. - operationId: Vote + summary: Proposal queries proposal details based on ProposalID. + operationId: GovV1Proposal responses: '200': description: A successful response. schema: type: object properties: - vote: + proposal: type: object properties: - proposal_id: + id: type: string format: uint64 - voter: + description: id defines the unique id of the proposal. + messages: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the + type of the serialized + + protocol buffer message. This string must contain at + least + + one "/" character. The last segment of the URL's + path must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be + in a canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the + binary all types that they + + expect it to use in the context of Any. However, for + URLs which use the + + scheme `http`, `https`, or no scheme, one can + optionally set up a type + + server that maps type URLs to message definitions as + follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results + based on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available + in the official + + protobuf release, and it is not used for type URLs + beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty + scheme) might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the + above specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer + message along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any + values in the form + + of utility functions or additional generated methods of + the Any type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + // or ... + if (any.isSameTypeAs(Foo.getDefaultInstance())) { + foo = any.unpack(Foo.getDefaultInstance()); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := anypb.New(foo) + if err != nil { + ... + } + ... + foo := &pb.Foo{} + if err := any.UnmarshalTo(foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by + default use + + 'type.googleapis.com/full.type.name' as the type URL and + the unpack + + methods only use the fully qualified type name after the + last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will + yield type + + name "y.z". + + + JSON + + + The JSON representation of an `Any` value uses the + regular + + representation of the deserialized, embedded message, + with an + + additional field `@type` which contains the type URL. + Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a + custom JSON + + representation, that representation will be embedded + adding a field + + `value` which holds the custom JSON in addition to the + `@type` + + field. Example (for message + [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + description: >- + messages are the arbitrary messages to be executed if the + proposal passes. + status: + description: status defines the proposal status. type: string - option: + enum: + - PROPOSAL_STATUS_UNSPECIFIED + - PROPOSAL_STATUS_DEPOSIT_PERIOD + - PROPOSAL_STATUS_VOTING_PERIOD + - PROPOSAL_STATUS_PASSED + - PROPOSAL_STATUS_REJECTED + - PROPOSAL_STATUS_FAILED + default: PROPOSAL_STATUS_UNSPECIFIED + final_tally_result: description: >- - Deprecated: Prefer to use `options` instead. This field is - set in queries - - if and only if `len(options) == 1` and that option has - weight 1. In all + final_tally_result is the final tally result of the + proposal. When - other cases, this field will default to - VOTE_OPTION_UNSPECIFIED. + querying a proposal via gRPC, this field is not populated + until the + + proposal's voting period has ended. + type: object + properties: + yes_count: + type: string + description: yes_count is the number of yes votes on a proposal. + abstain_count: + type: string + description: >- + abstain_count is the number of abstain votes on a + proposal. + no_count: + type: string + description: no_count is the number of no votes on a proposal. + no_with_veto_count: + type: string + description: >- + no_with_veto_count is the number of no with veto votes + on a proposal. + submit_time: type: string - enum: - - VOTE_OPTION_UNSPECIFIED - - VOTE_OPTION_YES - - VOTE_OPTION_ABSTAIN - - VOTE_OPTION_NO - - VOTE_OPTION_NO_WITH_VETO - default: VOTE_OPTION_UNSPECIFIED - options: + format: date-time + description: submit_time is the time of proposal submission. + deposit_end_time: + type: string + format: date-time + description: deposit_end_time is the end time for deposition. + total_deposit: type: array items: type: object properties: - option: + denom: type: string - enum: - - VOTE_OPTION_UNSPECIFIED - - VOTE_OPTION_YES - - VOTE_OPTION_ABSTAIN - - VOTE_OPTION_NO - - VOTE_OPTION_NO_WITH_VETO - default: VOTE_OPTION_UNSPECIFIED - description: >- - VoteOption enumerates the valid vote options for a - given governance proposal. - - - VOTE_OPTION_UNSPECIFIED: VOTE_OPTION_UNSPECIFIED defines a no-op vote option. - - VOTE_OPTION_YES: VOTE_OPTION_YES defines a yes vote option. - - VOTE_OPTION_ABSTAIN: VOTE_OPTION_ABSTAIN defines an abstain vote option. - - VOTE_OPTION_NO: VOTE_OPTION_NO defines a no vote option. - - VOTE_OPTION_NO_WITH_VETO: VOTE_OPTION_NO_WITH_VETO defines a no with veto vote option. - weight: + amount: type: string description: >- - WeightedVoteOption defines a unit of vote for vote - split. + Coin defines a token with a denomination and an amount. - Since: cosmos-sdk 0.43 - title: 'Since: cosmos-sdk 0.43' - description: >- - Vote defines a vote on a governance proposal. + NOTE: The amount field is an Int which implements the + custom method - A Vote consists of a proposal ID, the voter, and the vote - option. + signatures required by gogoproto. + description: total_deposit is the total deposit on the proposal. + voting_start_time: + type: string + format: date-time + description: >- + voting_start_time is the starting time to vote on a + proposal. + voting_end_time: + type: string + format: date-time + description: voting_end_time is the end time of voting on a proposal. + metadata: + type: string + description: >- + metadata is any arbitrary metadata attached to the + proposal. + title: + type: string + description: 'Since: cosmos-sdk 0.47' + title: title is the title of the proposal + summary: + type: string + description: 'Since: cosmos-sdk 0.47' + title: summary is a short summary of the proposal + proposer: + type: string + description: 'Since: cosmos-sdk 0.47' + title: Proposer is the address of the proposal sumbitter + description: >- + Proposal defines the core field members of a governance + proposal. description: >- - QueryVoteResponse is the response type for the Query/Vote RPC - method. + QueryProposalResponse is the response type for the Query/Proposal + RPC method. default: description: An unexpected error response. schema: @@ -38514,82 +43073,79 @@ paths: required: true type: string format: uint64 - - name: voter - description: voter defines the voter address for the proposals. - in: path - required: true - type: string tags: - Query - /cosmos/gov/v1/params/{params_type}: + /cosmos/gov/v1/proposals/{proposal_id}/deposits: get: - summary: Params queries all parameters of the gov module. - operationId: GovV1Params + summary: Deposits queries all deposits of a single proposal. + operationId: GovV1Deposit responses: '200': description: A successful response. schema: type: object properties: - voting_params: - description: voting_params defines the parameters related to voting. - type: object - properties: - voting_period: - type: string - description: Length of the voting period. - deposit_params: - description: deposit_params defines the parameters related to deposit. - type: object - properties: - min_deposit: - type: array - items: - type: object - properties: - denom: - type: string - amount: - type: string + deposits: + type: array + items: + type: object + properties: + proposal_id: + type: string + format: uint64 + description: proposal_id defines the unique id of the proposal. + depositor: + type: string description: >- - Coin defines a token with a denomination and an amount. + depositor defines the deposit addresses from the + proposals. + amount: + type: array + items: + type: object + properties: + denom: + type: string + amount: + type: string + description: >- + Coin defines a token with a denomination and an + amount. - NOTE: The amount field is an Int which implements the - custom method + NOTE: The amount field is an Int which implements the + custom method - signatures required by gogoproto. - description: Minimum deposit for a proposal to enter voting period. - max_deposit_period: - type: string - description: >- - Maximum period for Atom holders to deposit on a proposal. - Initial value: 2 - months. - tally_params: - description: tally_params defines the parameters related to tally. + signatures required by gogoproto. + description: amount to be deposited by depositor. + description: >- + Deposit defines an amount deposited by an account address to + an active + + proposal. + description: deposits defines the requested deposits. + pagination: + description: pagination defines the pagination in the response. type: object properties: - quorum: - type: string - description: >- - Minimum percentage of total stake needed to vote for a - result to be - considered valid. - threshold: + next_key: type: string - description: >- - Minimum proportion of Yes votes for proposal to pass. - Default value: 0.5. - veto_threshold: + format: byte + description: |- + next_key is the key to be passed to PageRequest.key to + query the next page most efficiently. It will be empty if + there are no more results. + total: type: string - description: >- - Minimum value of Veto votes to Total votes ratio for - proposal to be - vetoed. Default value: 1/3. + format: uint64 + title: >- + total is total number of results available if + PageRequest.count_total + + was set, its value is undefined otherwise description: >- - QueryParamsResponse is the response type for the Query/Params RPC - method. + QueryDepositsResponse is the response type for the Query/Deposits + RPC method. default: description: An unexpected error response. schema: @@ -38785,325 +43341,120 @@ paths: "value": "1.212s" } parameters: - - name: params_type - description: >- - params_type defines which parameters to query for, can be one of - "voting", - - "tallying" or "deposit". + - name: proposal_id + description: proposal_id defines the unique id of the proposal. in: path required: true type: string - tags: - - Query - /cosmos/gov/v1/proposals: - get: - summary: Proposals queries all proposals based on given status. - operationId: GovV1Proposal - responses: - '200': - description: A successful response. - schema: - type: object - properties: - proposals: - type: array - items: - type: object - properties: - id: - type: string - format: uint64 - messages: - type: array - items: - type: object - properties: - type_url: - type: string - description: >- - A URL/resource name that uniquely identifies the - type of the serialized - - protocol buffer message. This string must contain - at least - - one "/" character. The last segment of the URL's - path must represent - - the fully qualified name of the type (as in - - `path/google.protobuf.Duration`). The name should - be in a canonical form - - (e.g., leading "." is not accepted). - - - In practice, teams usually precompile into the - binary all types that they - - expect it to use in the context of Any. However, - for URLs which use the - - scheme `http`, `https`, or no scheme, one can - optionally set up a type - - server that maps type URLs to message definitions - as follows: - - - * If no scheme is provided, `https` is assumed. - - * An HTTP GET on the URL must yield a - [google.protobuf.Type][] - value in binary format, or produce an error. - * Applications are allowed to cache lookup results - based on the - URL, or have them precompiled into a binary to avoid any - lookup. Therefore, binary compatibility needs to be preserved - on changes to types. (Use versioned type names to manage - breaking changes.) - - Note: this functionality is not currently - available in the official - - protobuf release, and it is not used for type URLs - beginning with - - type.googleapis.com. - - - Schemes other than `http`, `https` (or the empty - scheme) might be - - used with implementation specific semantics. - value: - type: string - format: byte - description: >- - Must be a valid serialized protocol buffer of the - above specified type. - description: >- - `Any` contains an arbitrary serialized protocol buffer - message along with a - - URL that describes the type of the serialized message. - - - Protobuf library provides support to pack/unpack Any - values in the form - - of utility functions or additional generated methods - of the Any type. - - - Example 1: Pack and unpack a message in C++. - - Foo foo = ...; - Any any; - any.PackFrom(foo); - ... - if (any.UnpackTo(&foo)) { - ... - } - - Example 2: Pack and unpack a message in Java. - - Foo foo = ...; - Any any = Any.pack(foo); - ... - if (any.is(Foo.class)) { - foo = any.unpack(Foo.class); - } - // or ... - if (any.isSameTypeAs(Foo.getDefaultInstance())) { - foo = any.unpack(Foo.getDefaultInstance()); - } - - Example 3: Pack and unpack a message in Python. - - foo = Foo(...) - any = Any() - any.Pack(foo) - ... - if any.Is(Foo.DESCRIPTOR): - any.Unpack(foo) - ... - - Example 4: Pack and unpack a message in Go - - foo := &pb.Foo{...} - any, err := anypb.New(foo) - if err != nil { - ... - } - ... - foo := &pb.Foo{} - if err := any.UnmarshalTo(foo); err != nil { - ... - } - - The pack methods provided by protobuf library will by - default use - - 'type.googleapis.com/full.type.name' as the type URL - and the unpack - - methods only use the fully qualified type name after - the last '/' - - in the type URL, for example "foo.bar.com/x/y.z" will - yield type - - name "y.z". - - - JSON - - - The JSON representation of an `Any` value uses the - regular - - representation of the deserialized, embedded message, - with an - - additional field `@type` which contains the type URL. - Example: - - package google.profile; - message Person { - string first_name = 1; - string last_name = 2; - } - - { - "@type": "type.googleapis.com/google.profile.Person", - "firstName": , - "lastName": - } - - If the embedded message type is well-known and has a - custom JSON - - representation, that representation will be embedded - adding a field - - `value` which holds the custom JSON in addition to the - `@type` + format: uint64 + - name: pagination.key + description: |- + key is a value returned in PageResponse.next_key to begin + querying the next page most efficiently. Only one of offset or key + should be set. + in: query + required: false + type: string + format: byte + - name: pagination.offset + description: >- + offset is a numeric offset that can be used when key is unavailable. - field. Example (for message - [google.protobuf.Duration][]): + It is less efficient than using key. Only one of offset or key + should - { - "@type": "type.googleapis.com/google.protobuf.Duration", - "value": "1.212s" - } - status: - type: string - enum: - - PROPOSAL_STATUS_UNSPECIFIED - - PROPOSAL_STATUS_DEPOSIT_PERIOD - - PROPOSAL_STATUS_VOTING_PERIOD - - PROPOSAL_STATUS_PASSED - - PROPOSAL_STATUS_REJECTED - - PROPOSAL_STATUS_FAILED - default: PROPOSAL_STATUS_UNSPECIFIED - description: >- - ProposalStatus enumerates the valid statuses of a - proposal. + be set. + in: query + required: false + type: string + format: uint64 + - name: pagination.limit + description: >- + limit is the total number of results to be returned in the result + page. - - PROPOSAL_STATUS_UNSPECIFIED: PROPOSAL_STATUS_UNSPECIFIED defines the default proposal status. - - PROPOSAL_STATUS_DEPOSIT_PERIOD: PROPOSAL_STATUS_DEPOSIT_PERIOD defines a proposal status during the deposit - period. - - PROPOSAL_STATUS_VOTING_PERIOD: PROPOSAL_STATUS_VOTING_PERIOD defines a proposal status during the voting - period. - - PROPOSAL_STATUS_PASSED: PROPOSAL_STATUS_PASSED defines a proposal status of a proposal that has - passed. - - PROPOSAL_STATUS_REJECTED: PROPOSAL_STATUS_REJECTED defines a proposal status of a proposal that has - been rejected. - - PROPOSAL_STATUS_FAILED: PROPOSAL_STATUS_FAILED defines a proposal status of a proposal that has - failed. - final_tally_result: - description: >- - final_tally_result is the final tally result of the - proposal. When + If left empty it will default to a value to be set by each app. + in: query + required: false + type: string + format: uint64 + - name: pagination.count_total + description: >- + count_total is set to true to indicate that the result set should + include - querying a proposal via gRPC, this field is not - populated until the + a count of the total number of items available for pagination in + UIs. - proposal's voting period has ended. - type: object - properties: - yes_count: - type: string - abstain_count: - type: string - no_count: - type: string - no_with_veto_count: - type: string - submit_time: - type: string - format: date-time - deposit_end_time: - type: string - format: date-time - total_deposit: - type: array - items: - type: object - properties: - denom: - type: string - amount: - type: string - description: >- - Coin defines a token with a denomination and an - amount. + count_total is only respected when offset is used. It is ignored + when key + is set. + in: query + required: false + type: boolean + - name: pagination.reverse + description: >- + reverse is set to true if results are to be returned in the + descending order. - NOTE: The amount field is an Int which implements the - custom method - signatures required by gogoproto. - voting_start_time: - type: string - format: date-time - voting_end_time: - type: string - format: date-time - metadata: - type: string - description: >- - metadata is any arbitrary metadata attached to the - proposal. - description: >- - Proposal defines the core field members of a governance - proposal. - pagination: - description: pagination defines the pagination in the response. + Since: cosmos-sdk 0.43 + in: query + required: false + type: boolean + tags: + - Query + /cosmos/gov/v1/proposals/{proposal_id}/deposits/{depositor}: + get: + summary: >- + Deposit queries single deposit information based proposalID, + depositAddr. + operationId: GovV1Deposit + responses: + '200': + description: A successful response. + schema: + type: object + properties: + deposit: type: object properties: - next_key: - type: string - format: byte - description: |- - next_key is the key to be passed to PageRequest.key to - query the next page most efficiently. It will be empty if - there are no more results. - total: + proposal_id: type: string format: uint64 - title: >- - total is total number of results available if - PageRequest.count_total + description: proposal_id defines the unique id of the proposal. + depositor: + type: string + description: >- + depositor defines the deposit addresses from the + proposals. + amount: + type: array + items: + type: object + properties: + denom: + type: string + amount: + type: string + description: >- + Coin defines a token with a denomination and an amount. - was set, its value is undefined otherwise - description: >- - QueryProposalsResponse is the response type for the - Query/Proposals RPC - method. + NOTE: The amount field is an Int which implements the + custom method + + signatures required by gogoproto. + description: amount to be deposited by depositor. + description: >- + Deposit defines an amount deposited by an account address to + an active + + proposal. + description: >- + QueryDepositResponse is the response type for the Query/Deposit + RPC method. default: description: An unexpected error response. schema: @@ -39299,383 +43650,51 @@ paths: "value": "1.212s" } parameters: - - name: proposal_status - description: |- - proposal_status defines the status of the proposals. - - - PROPOSAL_STATUS_UNSPECIFIED: PROPOSAL_STATUS_UNSPECIFIED defines the default proposal status. - - PROPOSAL_STATUS_DEPOSIT_PERIOD: PROPOSAL_STATUS_DEPOSIT_PERIOD defines a proposal status during the deposit - period. - - PROPOSAL_STATUS_VOTING_PERIOD: PROPOSAL_STATUS_VOTING_PERIOD defines a proposal status during the voting - period. - - PROPOSAL_STATUS_PASSED: PROPOSAL_STATUS_PASSED defines a proposal status of a proposal that has - passed. - - PROPOSAL_STATUS_REJECTED: PROPOSAL_STATUS_REJECTED defines a proposal status of a proposal that has - been rejected. - - PROPOSAL_STATUS_FAILED: PROPOSAL_STATUS_FAILED defines a proposal status of a proposal that has - failed. - in: query - required: false - type: string - enum: - - PROPOSAL_STATUS_UNSPECIFIED - - PROPOSAL_STATUS_DEPOSIT_PERIOD - - PROPOSAL_STATUS_VOTING_PERIOD - - PROPOSAL_STATUS_PASSED - - PROPOSAL_STATUS_REJECTED - - PROPOSAL_STATUS_FAILED - default: PROPOSAL_STATUS_UNSPECIFIED - - name: voter - description: voter defines the voter address for the proposals. - in: query - required: false + - name: proposal_id + description: proposal_id defines the unique id of the proposal. + in: path + required: true type: string + format: uint64 - name: depositor description: depositor defines the deposit addresses from the proposals. - in: query - required: false - type: string - - name: pagination.key - description: |- - key is a value returned in PageResponse.next_key to begin - querying the next page most efficiently. Only one of offset or key - should be set. - in: query - required: false - type: string - format: byte - - name: pagination.offset - description: >- - offset is a numeric offset that can be used when key is unavailable. - - It is less efficient than using key. Only one of offset or key - should - - be set. - in: query - required: false - type: string - format: uint64 - - name: pagination.limit - description: >- - limit is the total number of results to be returned in the result - page. - - If left empty it will default to a value to be set by each app. - in: query - required: false + in: path + required: true type: string - format: uint64 - - name: pagination.count_total - description: >- - count_total is set to true to indicate that the result set should - include - - a count of the total number of items available for pagination in - UIs. - - count_total is only respected when offset is used. It is ignored - when key - - is set. - in: query - required: false - type: boolean - - name: pagination.reverse - description: >- - reverse is set to true if results are to be returned in the - descending order. - - - Since: cosmos-sdk 0.43 - in: query - required: false - type: boolean tags: - Query - /cosmos/gov/v1/proposals/{proposal_id}: + /cosmos/gov/v1/proposals/{proposal_id}/tally: get: - summary: Proposal queries proposal details based on ProposalID. - operationId: GovV1Proposal + summary: TallyResult queries the tally of a proposal vote. + operationId: GovV1TallyResult responses: '200': description: A successful response. schema: type: object properties: - proposal: + tally: + description: tally defines the requested tally. type: object properties: - id: + yes_count: type: string - format: uint64 - messages: - type: array - items: - type: object - properties: - type_url: - type: string - description: >- - A URL/resource name that uniquely identifies the - type of the serialized - - protocol buffer message. This string must contain at - least - - one "/" character. The last segment of the URL's - path must represent - - the fully qualified name of the type (as in - - `path/google.protobuf.Duration`). The name should be - in a canonical form - - (e.g., leading "." is not accepted). - - - In practice, teams usually precompile into the - binary all types that they - - expect it to use in the context of Any. However, for - URLs which use the - - scheme `http`, `https`, or no scheme, one can - optionally set up a type - - server that maps type URLs to message definitions as - follows: - - - * If no scheme is provided, `https` is assumed. - - * An HTTP GET on the URL must yield a - [google.protobuf.Type][] - value in binary format, or produce an error. - * Applications are allowed to cache lookup results - based on the - URL, or have them precompiled into a binary to avoid any - lookup. Therefore, binary compatibility needs to be preserved - on changes to types. (Use versioned type names to manage - breaking changes.) - - Note: this functionality is not currently available - in the official - - protobuf release, and it is not used for type URLs - beginning with - - type.googleapis.com. - - - Schemes other than `http`, `https` (or the empty - scheme) might be - - used with implementation specific semantics. - value: - type: string - format: byte - description: >- - Must be a valid serialized protocol buffer of the - above specified type. - description: >- - `Any` contains an arbitrary serialized protocol buffer - message along with a - - URL that describes the type of the serialized message. - - - Protobuf library provides support to pack/unpack Any - values in the form - - of utility functions or additional generated methods of - the Any type. - - - Example 1: Pack and unpack a message in C++. - - Foo foo = ...; - Any any; - any.PackFrom(foo); - ... - if (any.UnpackTo(&foo)) { - ... - } - - Example 2: Pack and unpack a message in Java. - - Foo foo = ...; - Any any = Any.pack(foo); - ... - if (any.is(Foo.class)) { - foo = any.unpack(Foo.class); - } - // or ... - if (any.isSameTypeAs(Foo.getDefaultInstance())) { - foo = any.unpack(Foo.getDefaultInstance()); - } - - Example 3: Pack and unpack a message in Python. - - foo = Foo(...) - any = Any() - any.Pack(foo) - ... - if any.Is(Foo.DESCRIPTOR): - any.Unpack(foo) - ... - - Example 4: Pack and unpack a message in Go - - foo := &pb.Foo{...} - any, err := anypb.New(foo) - if err != nil { - ... - } - ... - foo := &pb.Foo{} - if err := any.UnmarshalTo(foo); err != nil { - ... - } - - The pack methods provided by protobuf library will by - default use - - 'type.googleapis.com/full.type.name' as the type URL and - the unpack - - methods only use the fully qualified type name after the - last '/' - - in the type URL, for example "foo.bar.com/x/y.z" will - yield type - - name "y.z". - - - JSON - - - The JSON representation of an `Any` value uses the - regular - - representation of the deserialized, embedded message, - with an - - additional field `@type` which contains the type URL. - Example: - - package google.profile; - message Person { - string first_name = 1; - string last_name = 2; - } - - { - "@type": "type.googleapis.com/google.profile.Person", - "firstName": , - "lastName": - } - - If the embedded message type is well-known and has a - custom JSON - - representation, that representation will be embedded - adding a field - - `value` which holds the custom JSON in addition to the - `@type` - - field. Example (for message - [google.protobuf.Duration][]): - - { - "@type": "type.googleapis.com/google.protobuf.Duration", - "value": "1.212s" - } - status: + description: yes_count is the number of yes votes on a proposal. + abstain_count: type: string - enum: - - PROPOSAL_STATUS_UNSPECIFIED - - PROPOSAL_STATUS_DEPOSIT_PERIOD - - PROPOSAL_STATUS_VOTING_PERIOD - - PROPOSAL_STATUS_PASSED - - PROPOSAL_STATUS_REJECTED - - PROPOSAL_STATUS_FAILED - default: PROPOSAL_STATUS_UNSPECIFIED description: >- - ProposalStatus enumerates the valid statuses of a + abstain_count is the number of abstain votes on a proposal. - - - PROPOSAL_STATUS_UNSPECIFIED: PROPOSAL_STATUS_UNSPECIFIED defines the default proposal status. - - PROPOSAL_STATUS_DEPOSIT_PERIOD: PROPOSAL_STATUS_DEPOSIT_PERIOD defines a proposal status during the deposit - period. - - PROPOSAL_STATUS_VOTING_PERIOD: PROPOSAL_STATUS_VOTING_PERIOD defines a proposal status during the voting - period. - - PROPOSAL_STATUS_PASSED: PROPOSAL_STATUS_PASSED defines a proposal status of a proposal that has - passed. - - PROPOSAL_STATUS_REJECTED: PROPOSAL_STATUS_REJECTED defines a proposal status of a proposal that has - been rejected. - - PROPOSAL_STATUS_FAILED: PROPOSAL_STATUS_FAILED defines a proposal status of a proposal that has - failed. - final_tally_result: - description: >- - final_tally_result is the final tally result of the - proposal. When - - querying a proposal via gRPC, this field is not populated - until the - - proposal's voting period has ended. - type: object - properties: - yes_count: - type: string - abstain_count: - type: string - no_count: - type: string - no_with_veto_count: - type: string - submit_time: - type: string - format: date-time - deposit_end_time: - type: string - format: date-time - total_deposit: - type: array - items: - type: object - properties: - denom: - type: string - amount: - type: string - description: >- - Coin defines a token with a denomination and an amount. - - - NOTE: The amount field is an Int which implements the - custom method - - signatures required by gogoproto. - voting_start_time: - type: string - format: date-time - voting_end_time: + no_count: type: string - format: date-time - metadata: + description: no_count is the number of no votes on a proposal. + no_with_veto_count: type: string description: >- - metadata is any arbitrary metadata attached to the - proposal. - description: >- - Proposal defines the core field members of a governance - proposal. + no_with_veto_count is the number of no with veto votes on + a proposal. description: >- - QueryProposalResponse is the response type for the Query/Proposal + QueryTallyResultResponse is the response type for the Query/Tally RPC method. default: description: An unexpected error response. @@ -39880,17 +43899,17 @@ paths: format: uint64 tags: - Query - /cosmos/gov/v1/proposals/{proposal_id}/deposits: + /cosmos/gov/v1/proposals/{proposal_id}/votes: get: - summary: Deposits queries all deposits of a single proposal. - operationId: GovV1Deposit + summary: Votes queries votes of a given proposal. + operationId: GovV1Votes responses: '200': description: A successful response. schema: type: object properties: - deposits: + votes: type: array items: type: object @@ -39898,31 +43917,47 @@ paths: proposal_id: type: string format: uint64 - depositor: + description: proposal_id defines the unique id of the proposal. + voter: type: string - amount: + description: voter is the voter address of the proposal. + options: type: array items: type: object properties: - denom: + option: + description: >- + option defines the valid vote options, it must not + contain duplicate vote options. type: string - amount: + enum: + - VOTE_OPTION_UNSPECIFIED + - VOTE_OPTION_YES + - VOTE_OPTION_ABSTAIN + - VOTE_OPTION_NO + - VOTE_OPTION_NO_WITH_VETO + default: VOTE_OPTION_UNSPECIFIED + weight: type: string + description: >- + weight is the vote weight associated with the vote + option. description: >- - Coin defines a token with a denomination and an - amount. - - - NOTE: The amount field is an Int which implements the - custom method - - signatures required by gogoproto. + WeightedVoteOption defines a unit of vote for vote + split. + description: options is the weighted vote options. + metadata: + type: string + description: >- + metadata is any arbitrary metadata to attached to the + vote. description: >- - Deposit defines an amount deposited by an account address to - an active + Vote defines a vote on a governance proposal. - proposal. + A Vote consists of a proposal ID, the voter, and the vote + option. + description: votes defines the queried votes. pagination: description: pagination defines the pagination in the response. type: object @@ -39943,8 +43978,8 @@ paths: was set, its value is undefined otherwise description: >- - QueryDepositsResponse is the response type for the Query/Deposits - RPC method. + QueryVotesResponse is the response type for the Query/Votes RPC + method. default: description: An unexpected error response. schema: @@ -40204,51 +44239,65 @@ paths: type: boolean tags: - Query - /cosmos/gov/v1/proposals/{proposal_id}/deposits/{depositor}: + /cosmos/gov/v1/proposals/{proposal_id}/votes/{voter}: get: - summary: >- - Deposit queries single deposit information based proposalID, - depositAddr. - operationId: GovV1Deposit + summary: Vote queries voted information based on proposalID, voterAddr. + operationId: GovV1Vote responses: '200': description: A successful response. schema: type: object properties: - deposit: + vote: type: object properties: proposal_id: type: string format: uint64 - depositor: + description: proposal_id defines the unique id of the proposal. + voter: type: string - amount: + description: voter is the voter address of the proposal. + options: type: array items: type: object properties: - denom: + option: + description: >- + option defines the valid vote options, it must not + contain duplicate vote options. type: string - amount: + enum: + - VOTE_OPTION_UNSPECIFIED + - VOTE_OPTION_YES + - VOTE_OPTION_ABSTAIN + - VOTE_OPTION_NO + - VOTE_OPTION_NO_WITH_VETO + default: VOTE_OPTION_UNSPECIFIED + weight: type: string + description: >- + weight is the vote weight associated with the vote + option. description: >- - Coin defines a token with a denomination and an amount. - - - NOTE: The amount field is an Int which implements the - custom method - - signatures required by gogoproto. + WeightedVoteOption defines a unit of vote for vote + split. + description: options is the weighted vote options. + metadata: + type: string + description: >- + metadata is any arbitrary metadata to attached to the + vote. description: >- - Deposit defines an amount deposited by an account address to - an active + Vote defines a vote on a governance proposal. - proposal. + A Vote consists of a proposal ID, the voter, and the vote + option. description: >- - QueryDepositResponse is the response type for the Query/Deposit - RPC method. + QueryVoteResponse is the response type for the Query/Vote RPC + method. default: description: An unexpected error response. schema: @@ -40450,38 +44499,46 @@ paths: required: true type: string format: uint64 - - name: depositor - description: depositor defines the deposit addresses from the proposals. + - name: voter + description: voter defines the voter address for the proposals. in: path required: true type: string tags: - Query - /cosmos/gov/v1/proposals/{proposal_id}/tally: + /cosmos/slashing/v1beta1/params: get: - summary: TallyResult queries the tally of a proposal vote. - operationId: GovV1TallyResult + summary: Params queries the parameters of slashing module + operationId: SlashingParams responses: '200': description: A successful response. schema: type: object properties: - tally: - description: tally defines the requested tally. + params: type: object properties: - yes_count: + signed_blocks_window: type: string - abstain_count: + format: int64 + min_signed_per_window: type: string - no_count: + format: byte + downtime_jail_duration: type: string - no_with_veto_count: + slash_fraction_double_sign: type: string - description: >- - QueryTallyResultResponse is the response type for the Query/Tally - RPC method. + format: byte + slash_fraction_downtime: + type: string + format: byte + description: >- + Params represents the parameters used for by the slashing + module. + title: >- + QueryParamsResponse is the response type for the Query/Params RPC + method default: description: An unexpected error response. schema: @@ -40501,249 +44558,356 @@ paths: properties: type_url: type: string - description: >- - A URL/resource name that uniquely identifies the type of - the serialized - - protocol buffer message. This string must contain at - least - - one "/" character. The last segment of the URL's path - must represent - - the fully qualified name of the type (as in - - `path/google.protobuf.Duration`). The name should be in - a canonical form - - (e.g., leading "." is not accepted). - - - In practice, teams usually precompile into the binary - all types that they - - expect it to use in the context of Any. However, for - URLs which use the - - scheme `http`, `https`, or no scheme, one can optionally - set up a type - - server that maps type URLs to message definitions as - follows: - - - * If no scheme is provided, `https` is assumed. - - * An HTTP GET on the URL must yield a - [google.protobuf.Type][] - value in binary format, or produce an error. - * Applications are allowed to cache lookup results based - on the - URL, or have them precompiled into a binary to avoid any - lookup. Therefore, binary compatibility needs to be preserved - on changes to types. (Use versioned type names to manage - breaking changes.) - - Note: this functionality is not currently available in - the official - - protobuf release, and it is not used for type URLs - beginning with - - type.googleapis.com. - - - Schemes other than `http`, `https` (or the empty scheme) - might be - - used with implementation specific semantics. value: type: string format: byte + tags: + - Query + /cosmos/slashing/v1beta1/signing_infos: + get: + summary: SigningInfos queries signing info of all validators + operationId: SigningInfos + responses: + '200': + description: A successful response. + schema: + type: object + properties: + info: + type: array + items: + type: object + properties: + address: + type: string + start_height: + type: string + format: int64 + title: >- + Height at which validator was first a candidate OR was + unjailed + index_offset: + type: string + format: int64 description: >- - Must be a valid serialized protocol buffer of the above - specified type. - description: >- - `Any` contains an arbitrary serialized protocol buffer - message along with a - - URL that describes the type of the serialized message. - - - Protobuf library provides support to pack/unpack Any values - in the form - - of utility functions or additional generated methods of the - Any type. - - - Example 1: Pack and unpack a message in C++. - - Foo foo = ...; - Any any; - any.PackFrom(foo); - ... - if (any.UnpackTo(&foo)) { - ... - } - - Example 2: Pack and unpack a message in Java. + Index which is incremented each time the validator was a + bonded - Foo foo = ...; - Any any = Any.pack(foo); - ... - if (any.is(Foo.class)) { - foo = any.unpack(Foo.class); - } - // or ... - if (any.isSameTypeAs(Foo.getDefaultInstance())) { - foo = any.unpack(Foo.getDefaultInstance()); - } + in a block and may have signed a precommit or not. This + in conjunction with the - Example 3: Pack and unpack a message in Python. + `SignedBlocksWindow` param determines the index in the + `MissedBlocksBitArray`. + jailed_until: + type: string + format: date-time + description: >- + Timestamp until which the validator is jailed due to + liveness downtime. + tombstoned: + type: boolean + description: >- + Whether or not a validator has been tombstoned (killed + out of validator set). It is set - foo = Foo(...) - any = Any() - any.Pack(foo) - ... - if any.Is(Foo.DESCRIPTOR): - any.Unpack(foo) - ... + once the validator commits an equivocation or for any + other configured misbehiavor. + missed_blocks_counter: + type: string + format: int64 + description: >- + A counter kept to avoid unnecessary array reads. - Example 4: Pack and unpack a message in Go + Note that `Sum(MissedBlocksBitArray)` always equals + `MissedBlocksCounter`. + description: >- + ValidatorSigningInfo defines a validator's signing info for + monitoring their - foo := &pb.Foo{...} - any, err := anypb.New(foo) - if err != nil { - ... - } - ... - foo := &pb.Foo{} - if err := any.UnmarshalTo(foo); err != nil { - ... - } + liveness activity. + title: info is the signing info of all validators + pagination: + type: object + properties: + next_key: + type: string + format: byte + description: |- + next_key is the key to be passed to PageRequest.key to + query the next page most efficiently. It will be empty if + there are no more results. + total: + type: string + format: uint64 + title: >- + total is total number of results available if + PageRequest.count_total - The pack methods provided by protobuf library will by - default use + was set, its value is undefined otherwise + description: >- + PageResponse is to be embedded in gRPC response messages where + the - 'type.googleapis.com/full.type.name' as the type URL and the - unpack + corresponding request message has used PageRequest. - methods only use the fully qualified type name after the - last '/' + message SomeResponse { + repeated Bar results = 1; + PageResponse page = 2; + } + title: >- + QuerySigningInfosResponse is the response type for the + Query/SigningInfos RPC - in the type URL, for example "foo.bar.com/x/y.z" will yield - type + method + default: + description: An unexpected error response. + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + value: + type: string + format: byte + parameters: + - name: pagination.key + description: |- + key is a value returned in PageResponse.next_key to begin + querying the next page most efficiently. Only one of offset or key + should be set. + in: query + required: false + type: string + format: byte + - name: pagination.offset + description: >- + offset is a numeric offset that can be used when key is unavailable. - name "y.z". + It is less efficient than using key. Only one of offset or key + should + be set. + in: query + required: false + type: string + format: uint64 + - name: pagination.limit + description: >- + limit is the total number of results to be returned in the result + page. - JSON + If left empty it will default to a value to be set by each app. + in: query + required: false + type: string + format: uint64 + - name: pagination.count_total + description: >- + count_total is set to true to indicate that the result set should + include + a count of the total number of items available for pagination in + UIs. - The JSON representation of an `Any` value uses the regular + count_total is only respected when offset is used. It is ignored + when key - representation of the deserialized, embedded message, with - an + is set. + in: query + required: false + type: boolean + - name: pagination.reverse + description: >- + reverse is set to true if results are to be returned in the + descending order. - additional field `@type` which contains the type URL. - Example: - package google.profile; - message Person { - string first_name = 1; - string last_name = 2; - } + Since: cosmos-sdk 0.43 + in: query + required: false + type: boolean + tags: + - Query + /cosmos/slashing/v1beta1/signing_infos/{cons_address}: + get: + summary: SigningInfo queries the signing info of given cons address + operationId: SigningInfo + responses: + '200': + description: A successful response. + schema: + type: object + properties: + val_signing_info: + type: object + properties: + address: + type: string + start_height: + type: string + format: int64 + title: >- + Height at which validator was first a candidate OR was + unjailed + index_offset: + type: string + format: int64 + description: >- + Index which is incremented each time the validator was a + bonded - { - "@type": "type.googleapis.com/google.profile.Person", - "firstName": , - "lastName": - } + in a block and may have signed a precommit or not. This in + conjunction with the - If the embedded message type is well-known and has a custom - JSON + `SignedBlocksWindow` param determines the index in the + `MissedBlocksBitArray`. + jailed_until: + type: string + format: date-time + description: >- + Timestamp until which the validator is jailed due to + liveness downtime. + tombstoned: + type: boolean + description: >- + Whether or not a validator has been tombstoned (killed out + of validator set). It is set - representation, that representation will be embedded adding - a field + once the validator commits an equivocation or for any + other configured misbehiavor. + missed_blocks_counter: + type: string + format: int64 + description: >- + A counter kept to avoid unnecessary array reads. - `value` which holds the custom JSON in addition to the - `@type` + Note that `Sum(MissedBlocksBitArray)` always equals + `MissedBlocksCounter`. + description: >- + ValidatorSigningInfo defines a validator's signing info for + monitoring their - field. Example (for message [google.protobuf.Duration][]): + liveness activity. + title: >- + val_signing_info is the signing info of requested val cons + address + title: >- + QuerySigningInfoResponse is the response type for the + Query/SigningInfo RPC - { - "@type": "type.googleapis.com/google.protobuf.Duration", - "value": "1.212s" - } + method + default: + description: An unexpected error response. + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + value: + type: string + format: byte parameters: - - name: proposal_id - description: proposal_id defines the unique id of the proposal. + - name: cons_address + description: cons_address is the address to query signing info of in: path required: true type: string - format: uint64 tags: - Query - /cosmos/gov/v1/proposals/{proposal_id}/votes: + /cosmos/staking/v1beta1/delegations/{delegator_addr}: get: - summary: Votes queries votes of a given proposal. - operationId: GovV1Votes + summary: >- + DelegatorDelegations queries all delegations of a given delegator + address. + description: >- + When called from another module, this query might consume a high amount + of + + gas if the pagination field is incorrectly set. + operationId: DelegatorDelegations responses: '200': description: A successful response. schema: type: object properties: - votes: + delegation_responses: type: array items: type: object properties: - proposal_id: - type: string - format: uint64 - voter: - type: string - options: - type: array - items: - type: object - properties: - option: - type: string - enum: - - VOTE_OPTION_UNSPECIFIED - - VOTE_OPTION_YES - - VOTE_OPTION_ABSTAIN - - VOTE_OPTION_NO - - VOTE_OPTION_NO_WITH_VETO - default: VOTE_OPTION_UNSPECIFIED - description: >- - VoteOption enumerates the valid vote options for a - given governance proposal. - - - VOTE_OPTION_UNSPECIFIED: VOTE_OPTION_UNSPECIFIED defines a no-op vote option. - - VOTE_OPTION_YES: VOTE_OPTION_YES defines a yes vote option. - - VOTE_OPTION_ABSTAIN: VOTE_OPTION_ABSTAIN defines an abstain vote option. - - VOTE_OPTION_NO: VOTE_OPTION_NO defines a no vote option. - - VOTE_OPTION_NO_WITH_VETO: VOTE_OPTION_NO_WITH_VETO defines a no with veto vote option. - weight: - type: string - description: >- - WeightedVoteOption defines a unit of vote for vote - split. - metadata: - type: string + delegation: + type: object + properties: + delegator_address: + type: string + description: >- + delegator_address is the bech32-encoded address of + the delegator. + validator_address: + type: string + description: >- + validator_address is the bech32-encoded address of + the validator. + shares: + type: string + description: shares define the delegation shares received. description: >- - metadata is any arbitrary metadata to attached to the - vote. + Delegation represents the bond with tokens held by an + account. It is + + owned by one delegator, and is associated with the + voting power of one + + validator. + balance: + type: object + properties: + denom: + type: string + amount: + type: string + description: >- + Coin defines a token with a denomination and an amount. + + + NOTE: The amount field is an Int which implements the + custom method + + signatures required by gogoproto. description: >- - Vote defines a vote on a governance proposal. + DelegationResponse is equivalent to Delegation except that + it contains a - A Vote consists of a proposal ID, the voter, and the vote - option. - description: votes defined the queried votes. + balance in addition to shares which is more suitable for + client responses. + description: >- + delegation_responses defines all the delegations' info of a + delegator. pagination: description: pagination defines the pagination in the response. type: object @@ -40763,9 +44927,9 @@ paths: PageRequest.count_total was set, its value is undefined otherwise - description: >- - QueryVotesResponse is the response type for the Query/Votes RPC - method. + description: |- + QueryDelegatorDelegationsResponse is response type for the + Query/DelegatorDelegations RPC method. default: description: An unexpected error response. schema: @@ -40961,12 +45125,11 @@ paths: "value": "1.212s" } parameters: - - name: proposal_id - description: proposal_id defines the unique id of the proposal. + - name: delegator_addr + description: delegator_addr defines the delegator address to query for. in: path required: true type: string - format: uint64 - name: pagination.key description: |- key is a value returned in PageResponse.next_key to begin @@ -41025,64 +45188,181 @@ paths: type: boolean tags: - Query - /cosmos/gov/v1/proposals/{proposal_id}/votes/{voter}: + /cosmos/staking/v1beta1/delegators/{delegator_addr}/redelegations: get: - summary: Vote queries voted information based on proposalID, voterAddr. - operationId: GovV1Vote + summary: Redelegations queries redelegations of given address. + description: >- + When called from another module, this query might consume a high amount + of + + gas if the pagination field is incorrectly set. + operationId: Redelegations responses: '200': description: A successful response. schema: type: object properties: - vote: - type: object - properties: - proposal_id: - type: string - format: uint64 - voter: - type: string - options: - type: array - items: + redelegation_responses: + type: array + items: + type: object + properties: + redelegation: type: object properties: - option: + delegator_address: type: string - enum: - - VOTE_OPTION_UNSPECIFIED - - VOTE_OPTION_YES - - VOTE_OPTION_ABSTAIN - - VOTE_OPTION_NO - - VOTE_OPTION_NO_WITH_VETO - default: VOTE_OPTION_UNSPECIFIED description: >- - VoteOption enumerates the valid vote options for a - given governance proposal. - - - VOTE_OPTION_UNSPECIFIED: VOTE_OPTION_UNSPECIFIED defines a no-op vote option. - - VOTE_OPTION_YES: VOTE_OPTION_YES defines a yes vote option. - - VOTE_OPTION_ABSTAIN: VOTE_OPTION_ABSTAIN defines an abstain vote option. - - VOTE_OPTION_NO: VOTE_OPTION_NO defines a no vote option. - - VOTE_OPTION_NO_WITH_VETO: VOTE_OPTION_NO_WITH_VETO defines a no with veto vote option. - weight: + delegator_address is the bech32-encoded address of + the delegator. + validator_src_address: + type: string + description: >- + validator_src_address is the validator redelegation + source operator address. + validator_dst_address: type: string + description: >- + validator_dst_address is the validator redelegation + destination operator address. + entries: + type: array + items: + type: object + properties: + creation_height: + type: string + format: int64 + description: >- + creation_height defines the height which the + redelegation took place. + completion_time: + type: string + format: date-time + description: >- + completion_time defines the unix time for + redelegation completion. + initial_balance: + type: string + description: >- + initial_balance defines the initial balance + when redelegation started. + shares_dst: + type: string + description: >- + shares_dst is the amount of + destination-validator shares created by + redelegation. + unbonding_id: + type: string + format: uint64 + title: >- + Incrementing id that uniquely identifies this + entry + unbonding_on_hold_ref_count: + type: string + format: int64 + title: >- + Strictly positive if this entry's unbonding + has been stopped by external modules + description: >- + RedelegationEntry defines a redelegation object + with relevant metadata. + description: entries are the redelegation entries. description: >- - WeightedVoteOption defines a unit of vote for vote - split. - metadata: + Redelegation contains the list of a particular + delegator's redelegating bonds + + from a particular source validator to a particular + destination validator. + entries: + type: array + items: + type: object + properties: + redelegation_entry: + type: object + properties: + creation_height: + type: string + format: int64 + description: >- + creation_height defines the height which the + redelegation took place. + completion_time: + type: string + format: date-time + description: >- + completion_time defines the unix time for + redelegation completion. + initial_balance: + type: string + description: >- + initial_balance defines the initial balance + when redelegation started. + shares_dst: + type: string + description: >- + shares_dst is the amount of + destination-validator shares created by + redelegation. + unbonding_id: + type: string + format: uint64 + title: >- + Incrementing id that uniquely identifies this + entry + unbonding_on_hold_ref_count: + type: string + format: int64 + title: >- + Strictly positive if this entry's unbonding + has been stopped by external modules + description: >- + RedelegationEntry defines a redelegation object + with relevant metadata. + balance: + type: string + description: >- + RedelegationEntryResponse is equivalent to a + RedelegationEntry except that it + + contains a balance in addition to shares which is more + suitable for client + + responses. + description: >- + RedelegationResponse is equivalent to a Redelegation except + that its entries + + contain a balance in addition to shares which is more + suitable for client + + responses. + pagination: + description: pagination defines the pagination in the response. + type: object + properties: + next_key: type: string - description: >- - metadata is any arbitrary metadata to attached to the - vote. - description: >- - Vote defines a vote on a governance proposal. + format: byte + description: |- + next_key is the key to be passed to PageRequest.key to + query the next page most efficiently. It will be empty if + there are no more results. + total: + type: string + format: uint64 + title: >- + total is total number of results available if + PageRequest.count_total - A Vote consists of a proposal ID, the voter, and the vote - option. + was set, its value is undefined otherwise description: >- - QueryVoteResponse is the response type for the Query/Vote RPC + QueryRedelegationsResponse is response type for the + Query/Redelegations RPC + method. default: description: An unexpected error response. @@ -41279,195 +45559,21 @@ paths: "value": "1.212s" } parameters: - - name: proposal_id - description: proposal_id defines the unique id of the proposal. + - name: delegator_addr + description: delegator_addr defines the delegator address to query for. in: path required: true type: string - format: uint64 - - name: voter - description: voter defines the voter address for the proposals. - in: path - required: true + - name: src_validator_addr + description: src_validator_addr defines the validator address to redelegate from. + in: query + required: false + type: string + - name: dst_validator_addr + description: dst_validator_addr defines the validator address to redelegate to. + in: query + required: false type: string - tags: - - Query - /cosmos/slashing/v1beta1/params: - get: - summary: Params queries the parameters of slashing module - operationId: SlashingParams - responses: - '200': - description: A successful response. - schema: - type: object - properties: - params: - type: object - properties: - signed_blocks_window: - type: string - format: int64 - min_signed_per_window: - type: string - format: byte - downtime_jail_duration: - type: string - slash_fraction_double_sign: - type: string - format: byte - slash_fraction_downtime: - type: string - format: byte - description: >- - Params represents the parameters used for by the slashing - module. - title: >- - QueryParamsResponse is the response type for the Query/Params RPC - method - default: - description: An unexpected error response. - schema: - type: object - properties: - error: - type: string - code: - type: integer - format: int32 - message: - type: string - details: - type: array - items: - type: object - properties: - type_url: - type: string - value: - type: string - format: byte - tags: - - Query - /cosmos/slashing/v1beta1/signing_infos: - get: - summary: SigningInfos queries signing info of all validators - operationId: SigningInfos - responses: - '200': - description: A successful response. - schema: - type: object - properties: - info: - type: array - items: - type: object - properties: - address: - type: string - start_height: - type: string - format: int64 - title: >- - Height at which validator was first a candidate OR was - unjailed - index_offset: - type: string - format: int64 - description: >- - Index which is incremented each time the validator was a - bonded - - in a block and may have signed a precommit or not. This - in conjunction with the - - `SignedBlocksWindow` param determines the index in the - `MissedBlocksBitArray`. - jailed_until: - type: string - format: date-time - description: >- - Timestamp until which the validator is jailed due to - liveness downtime. - tombstoned: - type: boolean - description: >- - Whether or not a validator has been tombstoned (killed - out of validator set). It is set - - once the validator commits an equivocation or for any - other configured misbehiavor. - missed_blocks_counter: - type: string - format: int64 - description: >- - A counter kept to avoid unnecessary array reads. - - Note that `Sum(MissedBlocksBitArray)` always equals - `MissedBlocksCounter`. - description: >- - ValidatorSigningInfo defines a validator's signing info for - monitoring their - - liveness activity. - title: info is the signing info of all validators - pagination: - type: object - properties: - next_key: - type: string - format: byte - description: |- - next_key is the key to be passed to PageRequest.key to - query the next page most efficiently. It will be empty if - there are no more results. - total: - type: string - format: uint64 - title: >- - total is total number of results available if - PageRequest.count_total - - was set, its value is undefined otherwise - description: >- - PageResponse is to be embedded in gRPC response messages where - the - - corresponding request message has used PageRequest. - - message SomeResponse { - repeated Bar results = 1; - PageResponse page = 2; - } - title: >- - QuerySigningInfosResponse is the response type for the - Query/SigningInfos RPC - - method - default: - description: An unexpected error response. - schema: - type: object - properties: - error: - type: string - code: - type: integer - format: int32 - message: - type: string - details: - type: array - items: - type: object - properties: - type_url: - type: string - value: - type: string - format: byte - parameters: - name: pagination.key description: |- key is a value returned in PageResponse.next_key to begin @@ -41509,186 +45615,105 @@ paths: count_total is only respected when offset is used. It is ignored when key - - is set. - in: query - required: false - type: boolean - - name: pagination.reverse - description: >- - reverse is set to true if results are to be returned in the - descending order. - - - Since: cosmos-sdk 0.43 - in: query - required: false - type: boolean - tags: - - Query - /cosmos/slashing/v1beta1/signing_infos/{cons_address}: - get: - summary: SigningInfo queries the signing info of given cons address - operationId: SigningInfo - responses: - '200': - description: A successful response. - schema: - type: object - properties: - val_signing_info: - type: object - properties: - address: - type: string - start_height: - type: string - format: int64 - title: >- - Height at which validator was first a candidate OR was - unjailed - index_offset: - type: string - format: int64 - description: >- - Index which is incremented each time the validator was a - bonded - - in a block and may have signed a precommit or not. This in - conjunction with the - - `SignedBlocksWindow` param determines the index in the - `MissedBlocksBitArray`. - jailed_until: - type: string - format: date-time - description: >- - Timestamp until which the validator is jailed due to - liveness downtime. - tombstoned: - type: boolean - description: >- - Whether or not a validator has been tombstoned (killed out - of validator set). It is set - - once the validator commits an equivocation or for any - other configured misbehiavor. - missed_blocks_counter: - type: string - format: int64 - description: >- - A counter kept to avoid unnecessary array reads. - - Note that `Sum(MissedBlocksBitArray)` always equals - `MissedBlocksCounter`. - description: >- - ValidatorSigningInfo defines a validator's signing info for - monitoring their - - liveness activity. - title: >- - val_signing_info is the signing info of requested val cons - address - title: >- - QuerySigningInfoResponse is the response type for the - Query/SigningInfo RPC - - method - default: - description: An unexpected error response. - schema: - type: object - properties: - error: - type: string - code: - type: integer - format: int32 - message: - type: string - details: - type: array - items: - type: object - properties: - type_url: - type: string - value: - type: string - format: byte - parameters: - - name: cons_address - description: cons_address is the address to query signing info of - in: path - required: true - type: string + + is set. + in: query + required: false + type: boolean + - name: pagination.reverse + description: >- + reverse is set to true if results are to be returned in the + descending order. + + + Since: cosmos-sdk 0.43 + in: query + required: false + type: boolean tags: - Query - /cosmos/staking/v1beta1/delegations/{delegator_addr}: + /cosmos/staking/v1beta1/delegators/{delegator_addr}/unbonding_delegations: get: summary: >- - DelegatorDelegations queries all delegations of a given delegator - address. - operationId: DelegatorDelegations + DelegatorUnbondingDelegations queries all unbonding delegations of a + given + + delegator address. + description: >- + When called from another module, this query might consume a high amount + of + + gas if the pagination field is incorrectly set. + operationId: DelegatorUnbondingDelegations responses: '200': description: A successful response. schema: type: object properties: - delegation_responses: + unbonding_responses: type: array items: type: object properties: - delegation: - type: object - properties: - delegator_address: - type: string - description: >- - delegator_address is the bech32-encoded address of - the delegator. - validator_address: - type: string - description: >- - validator_address is the bech32-encoded address of - the validator. - shares: - type: string - description: shares define the delegation shares received. + delegator_address: + type: string description: >- - Delegation represents the bond with tokens held by an - account. It is - - owned by one delegator, and is associated with the - voting power of one - - validator. - balance: - type: object - properties: - denom: - type: string - amount: - type: string + delegator_address is the bech32-encoded address of the + delegator. + validator_address: + type: string description: >- - Coin defines a token with a denomination and an amount. - - - NOTE: The amount field is an Int which implements the - custom method - - signatures required by gogoproto. + validator_address is the bech32-encoded address of the + validator. + entries: + type: array + items: + type: object + properties: + creation_height: + type: string + format: int64 + description: >- + creation_height is the height which the unbonding + took place. + completion_time: + type: string + format: date-time + description: >- + completion_time is the unix time for unbonding + completion. + initial_balance: + type: string + description: >- + initial_balance defines the tokens initially + scheduled to receive at completion. + balance: + type: string + description: >- + balance defines the tokens to receive at + completion. + unbonding_id: + type: string + format: uint64 + title: >- + Incrementing id that uniquely identifies this + entry + unbonding_on_hold_ref_count: + type: string + format: int64 + title: >- + Strictly positive if this entry's unbonding has + been stopped by external modules + description: >- + UnbondingDelegationEntry defines an unbonding object + with relevant metadata. + description: entries are the unbonding delegation entries. description: >- - DelegationResponse is equivalent to Delegation except that - it contains a + UnbondingDelegation stores all of a single delegator's + unbonding bonds - balance in addition to shares which is more suitable for - client responses. - description: >- - delegation_responses defines all the delegations' info of a - delegator. + for a single validator in an time-ordered list. pagination: description: pagination defines the pagination in the response. type: object @@ -41708,9 +45733,11 @@ paths: PageRequest.count_total was set, its value is undefined otherwise - description: |- - QueryDelegatorDelegationsResponse is response type for the - Query/DelegatorDelegations RPC method. + description: >- + QueryUnbondingDelegatorDelegationsResponse is response type for + the + + Query/UnbondingDelegatorDelegations RPC method. default: description: An unexpected error response. schema: @@ -41969,129 +45996,358 @@ paths: type: boolean tags: - Query - /cosmos/staking/v1beta1/delegators/{delegator_addr}/redelegations: + /cosmos/staking/v1beta1/delegators/{delegator_addr}/validators: get: - summary: Redelegations queries redelegations of given address. - operationId: Redelegations + summary: |- + DelegatorValidators queries all validators info for given delegator + address. + description: >- + When called from another module, this query might consume a high amount + of + + gas if the pagination field is incorrectly set. + operationId: DelegatorValidators responses: '200': description: A successful response. schema: type: object properties: - redelegation_responses: + validators: type: array items: type: object properties: - redelegation: + operator_address: + type: string + description: >- + operator_address defines the address of the validator's + operator; bech encoded in JSON. + consensus_pubkey: type: object properties: - delegator_address: + type_url: type: string description: >- - delegator_address is the bech32-encoded address of - the delegator. - validator_src_address: + A URL/resource name that uniquely identifies the + type of the serialized + + protocol buffer message. This string must contain at + least + + one "/" character. The last segment of the URL's + path must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be + in a canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the + binary all types that they + + expect it to use in the context of Any. However, for + URLs which use the + + scheme `http`, `https`, or no scheme, one can + optionally set up a type + + server that maps type URLs to message definitions as + follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results + based on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available + in the official + + protobuf release, and it is not used for type URLs + beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty + scheme) might be + + used with implementation specific semantics. + value: type: string + format: byte description: >- - validator_src_address is the validator redelegation - source operator address. - validator_dst_address: + Must be a valid serialized protocol buffer of the + above specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer + message along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any + values in the form + + of utility functions or additional generated methods of + the Any type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + // or ... + if (any.isSameTypeAs(Foo.getDefaultInstance())) { + foo = any.unpack(Foo.getDefaultInstance()); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := anypb.New(foo) + if err != nil { + ... + } + ... + foo := &pb.Foo{} + if err := any.UnmarshalTo(foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by + default use + + 'type.googleapis.com/full.type.name' as the type URL and + the unpack + + methods only use the fully qualified type name after the + last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will + yield type + + name "y.z". + + + JSON + + + The JSON representation of an `Any` value uses the + regular + + representation of the deserialized, embedded message, + with an + + additional field `@type` which contains the type URL. + Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a + custom JSON + + representation, that representation will be embedded + adding a field + + `value` which holds the custom JSON in addition to the + `@type` + + field. Example (for message + [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + jailed: + type: boolean + description: >- + jailed defined whether the validator has been jailed + from bonded status or not. + status: + description: >- + status is the validator status + (bonded/unbonding/unbonded). + type: string + enum: + - BOND_STATUS_UNSPECIFIED + - BOND_STATUS_UNBONDED + - BOND_STATUS_UNBONDING + - BOND_STATUS_BONDED + default: BOND_STATUS_UNSPECIFIED + tokens: + type: string + description: >- + tokens define the delegated tokens (incl. + self-delegation). + delegator_shares: + type: string + description: >- + delegator_shares defines total shares issued to a + validator's delegators. + description: + description: >- + description defines the description terms for the + validator. + type: object + properties: + moniker: type: string description: >- - validator_dst_address is the validator redelegation - destination operator address. - entries: - type: array - items: - type: object - properties: - creation_height: - type: string - format: int64 - description: >- - creation_height defines the height which the - redelegation took place. - completion_time: - type: string - format: date-time - description: >- - completion_time defines the unix time for - redelegation completion. - initial_balance: - type: string - description: >- - initial_balance defines the initial balance - when redelegation started. - shares_dst: - type: string - description: >- - shares_dst is the amount of - destination-validator shares created by - redelegation. - description: >- - RedelegationEntry defines a redelegation object - with relevant metadata. - description: entries are the redelegation entries. + moniker defines a human-readable name for the + validator. + identity: + type: string + description: >- + identity defines an optional identity signature (ex. + UPort or Keybase). + website: + type: string + description: website defines an optional website link. + security_contact: + type: string + description: >- + security_contact defines an optional email for + security contact. + details: + type: string + description: details define other optional details. + unbonding_height: + type: string + format: int64 description: >- - Redelegation contains the list of a particular - delegator's redelegating bonds + unbonding_height defines, if unbonding, the height at + which this validator has begun unbonding. + unbonding_time: + type: string + format: date-time + description: >- + unbonding_time defines, if unbonding, the min time for + the validator to complete unbonding. + commission: + description: commission defines the commission parameters. + type: object + properties: + commission_rates: + description: >- + commission_rates defines the initial commission + rates to be used for creating a validator. + type: object + properties: + rate: + type: string + description: >- + rate is the commission rate charged to + delegators, as a fraction. + max_rate: + type: string + description: >- + max_rate defines the maximum commission rate + which validator can ever charge, as a fraction. + max_change_rate: + type: string + description: >- + max_change_rate defines the maximum daily + increase of the validator commission, as a + fraction. + update_time: + type: string + format: date-time + description: >- + update_time is the last time the commission rate was + changed. + min_self_delegation: + type: string + description: >- + min_self_delegation is the validator's self declared + minimum self delegation. - from a particular source validator to a particular - destination validator. - entries: + + Since: cosmos-sdk 0.46 + unbonding_on_hold_ref_count: + type: string + format: int64 + title: >- + strictly positive if this validator's unbonding has been + stopped by external modules + unbonding_ids: type: array items: - type: object - properties: - redelegation_entry: - type: object - properties: - creation_height: - type: string - format: int64 - description: >- - creation_height defines the height which the - redelegation took place. - completion_time: - type: string - format: date-time - description: >- - completion_time defines the unix time for - redelegation completion. - initial_balance: - type: string - description: >- - initial_balance defines the initial balance - when redelegation started. - shares_dst: - type: string - description: >- - shares_dst is the amount of - destination-validator shares created by - redelegation. - description: >- - RedelegationEntry defines a redelegation object - with relevant metadata. - balance: - type: string - description: >- - RedelegationEntryResponse is equivalent to a - RedelegationEntry except that it + type: string + format: uint64 + title: >- + list of unbonding ids, each uniquely identifing an + unbonding of this validator + description: >- + Validator defines a validator, together with the total + amount of the - contains a balance in addition to shares which is more - suitable for client + Validator's bond shares and their exchange rate to coins. + Slashing results in - responses. - description: >- - RedelegationResponse is equivalent to a Redelegation except - that its entries + a decrease in the exchange rate, allowing correct + calculation of future - contain a balance in addition to shares which is more - suitable for client + undelegations without iterating over delegators. When coins + are delegated to - responses. + this validator, the validator is credited with a delegation + whose number of + + bond shares is based on the amount of coins delegated + divided by the current + + exchange rate. Voting power can be calculated as total + bonded shares + + multiplied by exchange rate. + description: validators defines the validators' info of a delegator. pagination: description: pagination defines the pagination in the response. type: object @@ -42111,11 +46367,9 @@ paths: PageRequest.count_total was set, its value is undefined otherwise - description: >- - QueryRedelegationsResponse is response type for the - Query/Redelegations RPC - - method. + description: |- + QueryDelegatorValidatorsResponse is response type for the + Query/DelegatorValidators RPC method. default: description: An unexpected error response. schema: @@ -42316,16 +46570,6 @@ paths: in: path required: true type: string - - name: src_validator_addr - description: src_validator_addr defines the validator address to redelegate from. - in: query - required: false - type: string - - name: dst_validator_addr - description: dst_validator_addr defines the validator address to redelegate to. - in: query - required: false - type: string - name: pagination.key description: |- key is a value returned in PageResponse.next_key to begin @@ -42384,95 +46628,350 @@ paths: type: boolean tags: - Query - /cosmos/staking/v1beta1/delegators/{delegator_addr}/unbonding_delegations: + /cosmos/staking/v1beta1/delegators/{delegator_addr}/validators/{validator_addr}: get: - summary: >- - DelegatorUnbondingDelegations queries all unbonding delegations of a - given - - delegator address. - operationId: DelegatorUnbondingDelegations + summary: |- + DelegatorValidator queries validator info for given delegator validator + pair. + operationId: DelegatorValidator responses: '200': description: A successful response. schema: type: object properties: - unbonding_responses: - type: array - items: - type: object - properties: - delegator_address: - type: string - description: >- - delegator_address is the bech32-encoded address of the - delegator. - validator_address: - type: string - description: >- - validator_address is the bech32-encoded address of the - validator. - entries: - type: array - items: + validator: + type: object + properties: + operator_address: + type: string + description: >- + operator_address defines the address of the validator's + operator; bech encoded in JSON. + consensus_pubkey: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type + of the serialized + + protocol buffer message. This string must contain at + least + + one "/" character. The last segment of the URL's path + must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be + in a canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary + all types that they + + expect it to use in the context of Any. However, for + URLs which use the + + scheme `http`, `https`, or no scheme, one can + optionally set up a type + + server that maps type URLs to message definitions as + follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results + based on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in + the official + + protobuf release, and it is not used for type URLs + beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty + scheme) might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the + above specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer + message along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any + values in the form + + of utility functions or additional generated methods of + the Any type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + // or ... + if (any.isSameTypeAs(Foo.getDefaultInstance())) { + foo = any.unpack(Foo.getDefaultInstance()); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := anypb.New(foo) + if err != nil { + ... + } + ... + foo := &pb.Foo{} + if err := any.UnmarshalTo(foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by + default use + + 'type.googleapis.com/full.type.name' as the type URL and + the unpack + + methods only use the fully qualified type name after the + last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will + yield type + + name "y.z". + + + JSON + + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with + an + + additional field `@type` which contains the type URL. + Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a + custom JSON + + representation, that representation will be embedded + adding a field + + `value` which holds the custom JSON in addition to the + `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + jailed: + type: boolean + description: >- + jailed defined whether the validator has been jailed from + bonded status or not. + status: + description: >- + status is the validator status + (bonded/unbonding/unbonded). + type: string + enum: + - BOND_STATUS_UNSPECIFIED + - BOND_STATUS_UNBONDED + - BOND_STATUS_UNBONDING + - BOND_STATUS_BONDED + default: BOND_STATUS_UNSPECIFIED + tokens: + type: string + description: >- + tokens define the delegated tokens (incl. + self-delegation). + delegator_shares: + type: string + description: >- + delegator_shares defines total shares issued to a + validator's delegators. + description: + description: >- + description defines the description terms for the + validator. + type: object + properties: + moniker: + type: string + description: >- + moniker defines a human-readable name for the + validator. + identity: + type: string + description: >- + identity defines an optional identity signature (ex. + UPort or Keybase). + website: + type: string + description: website defines an optional website link. + security_contact: + type: string + description: >- + security_contact defines an optional email for + security contact. + details: + type: string + description: details define other optional details. + unbonding_height: + type: string + format: int64 + description: >- + unbonding_height defines, if unbonding, the height at + which this validator has begun unbonding. + unbonding_time: + type: string + format: date-time + description: >- + unbonding_time defines, if unbonding, the min time for the + validator to complete unbonding. + commission: + description: commission defines the commission parameters. + type: object + properties: + commission_rates: + description: >- + commission_rates defines the initial commission rates + to be used for creating a validator. type: object properties: - creation_height: - type: string - format: int64 - description: >- - creation_height is the height which the unbonding - took place. - completion_time: + rate: type: string - format: date-time description: >- - completion_time is the unix time for unbonding - completion. - initial_balance: + rate is the commission rate charged to delegators, + as a fraction. + max_rate: type: string description: >- - initial_balance defines the tokens initially - scheduled to receive at completion. - balance: + max_rate defines the maximum commission rate which + validator can ever charge, as a fraction. + max_change_rate: type: string description: >- - balance defines the tokens to receive at - completion. + max_change_rate defines the maximum daily increase + of the validator commission, as a fraction. + update_time: + type: string + format: date-time description: >- - UnbondingDelegationEntry defines an unbonding object - with relevant metadata. - description: entries are the unbonding delegation entries. - description: >- - UnbondingDelegation stores all of a single delegator's - unbonding bonds - - for a single validator in an time-ordered list. - pagination: - description: pagination defines the pagination in the response. - type: object - properties: - next_key: + update_time is the last time the commission rate was + changed. + min_self_delegation: type: string - format: byte - description: |- - next_key is the key to be passed to PageRequest.key to - query the next page most efficiently. It will be empty if - there are no more results. - total: + description: >- + min_self_delegation is the validator's self declared + minimum self delegation. + + + Since: cosmos-sdk 0.46 + unbonding_on_hold_ref_count: type: string - format: uint64 + format: int64 title: >- - total is total number of results available if - PageRequest.count_total + strictly positive if this validator's unbonding has been + stopped by external modules + unbonding_ids: + type: array + items: + type: string + format: uint64 + title: >- + list of unbonding ids, each uniquely identifing an + unbonding of this validator + description: >- + Validator defines a validator, together with the total amount + of the - was set, its value is undefined otherwise - description: >- - QueryUnbondingDelegatorDelegationsResponse is response type for - the + Validator's bond shares and their exchange rate to coins. + Slashing results in - Query/UnbondingDelegatorDelegations RPC method. + a decrease in the exchange rate, allowing correct calculation + of future + + undelegations without iterating over delegators. When coins + are delegated to + + this validator, the validator is credited with a delegation + whose number of + + bond shares is based on the amount of coins delegated divided + by the current + + exchange rate. Voting power can be calculated as total bonded + shares + + multiplied by exchange rate. + description: |- + QueryDelegatorValidatorResponse response type for the + Query/DelegatorValidator RPC method. default: description: An unexpected error response. schema: @@ -42673,419 +47172,445 @@ paths: in: path required: true type: string - - name: pagination.key - description: |- - key is a value returned in PageResponse.next_key to begin - querying the next page most efficiently. Only one of offset or key - should be set. - in: query - required: false - type: string - format: byte - - name: pagination.offset - description: >- - offset is a numeric offset that can be used when key is unavailable. - - It is less efficient than using key. Only one of offset or key - should - - be set. - in: query - required: false - type: string - format: uint64 - - name: pagination.limit - description: >- - limit is the total number of results to be returned in the result - page. - - If left empty it will default to a value to be set by each app. - in: query - required: false + - name: validator_addr + description: validator_addr defines the validator address to query for. + in: path + required: true type: string - format: uint64 - - name: pagination.count_total - description: >- - count_total is set to true to indicate that the result set should - include - - a count of the total number of items available for pagination in - UIs. - - count_total is only respected when offset is used. It is ignored - when key - - is set. - in: query - required: false - type: boolean - - name: pagination.reverse - description: >- - reverse is set to true if results are to be returned in the - descending order. - - - Since: cosmos-sdk 0.43 - in: query - required: false - type: boolean tags: - Query - /cosmos/staking/v1beta1/delegators/{delegator_addr}/validators: + /cosmos/staking/v1beta1/historical_info/{height}: get: - summary: |- - DelegatorValidators queries all validators info for given delegator - address. - operationId: DelegatorValidators + summary: HistoricalInfo queries the historical info for given height. + operationId: HistoricalInfo responses: '200': - description: A successful response. - schema: - type: object - properties: - validators: - type: array - items: - type: object - properties: - operator_address: - type: string - description: >- - operator_address defines the address of the validator's - operator; bech encoded in JSON. - consensus_pubkey: + description: A successful response. + schema: + type: object + properties: + hist: + description: hist defines the historical info at the given height. + type: object + properties: + header: + type: object + properties: + version: + title: basic block info + type: object + properties: + block: + type: string + format: uint64 + app: + type: string + format: uint64 + description: >- + Consensus captures the consensus rules for processing + a block in the blockchain, + + including all blockchain data structures and the rules + of the application's + + state transition machine. + chain_id: + type: string + height: + type: string + format: int64 + time: + type: string + format: date-time + last_block_id: + title: prev block info + type: object + properties: + hash: + type: string + format: byte + part_set_header: + type: object + properties: + total: + type: integer + format: int64 + hash: + type: string + format: byte + title: PartsetHeader + last_commit_hash: + type: string + format: byte + title: hashes of block data + data_hash: + type: string + format: byte + validators_hash: + type: string + format: byte + title: hashes from the app output from the prev block + next_validators_hash: + type: string + format: byte + consensus_hash: + type: string + format: byte + app_hash: + type: string + format: byte + last_results_hash: + type: string + format: byte + evidence_hash: + type: string + format: byte + title: consensus info + proposer_address: + type: string + format: byte + description: Header defines the structure of a block header. + valset: + type: array + items: type: object properties: - type_url: + operator_address: type: string description: >- - A URL/resource name that uniquely identifies the - type of the serialized + operator_address defines the address of the + validator's operator; bech encoded in JSON. + consensus_pubkey: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the + type of the serialized - protocol buffer message. This string must contain at - least + protocol buffer message. This string must + contain at least - one "/" character. The last segment of the URL's - path must represent + one "/" character. The last segment of the URL's + path must represent - the fully qualified name of the type (as in + the fully qualified name of the type (as in - `path/google.protobuf.Duration`). The name should be - in a canonical form + `path/google.protobuf.Duration`). The name + should be in a canonical form - (e.g., leading "." is not accepted). + (e.g., leading "." is not accepted). - In practice, teams usually precompile into the - binary all types that they + In practice, teams usually precompile into the + binary all types that they - expect it to use in the context of Any. However, for - URLs which use the + expect it to use in the context of Any. However, + for URLs which use the - scheme `http`, `https`, or no scheme, one can - optionally set up a type + scheme `http`, `https`, or no scheme, one can + optionally set up a type - server that maps type URLs to message definitions as - follows: + server that maps type URLs to message + definitions as follows: - * If no scheme is provided, `https` is assumed. + * If no scheme is provided, `https` is assumed. - * An HTTP GET on the URL must yield a - [google.protobuf.Type][] - value in binary format, or produce an error. - * Applications are allowed to cache lookup results - based on the - URL, or have them precompiled into a binary to avoid any - lookup. Therefore, binary compatibility needs to be preserved - on changes to types. (Use versioned type names to manage - breaking changes.) + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup + results based on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) - Note: this functionality is not currently available - in the official + Note: this functionality is not currently + available in the official - protobuf release, and it is not used for type URLs - beginning with + protobuf release, and it is not used for type + URLs beginning with - type.googleapis.com. + type.googleapis.com. - Schemes other than `http`, `https` (or the empty - scheme) might be + Schemes other than `http`, `https` (or the empty + scheme) might be - used with implementation specific semantics. - value: - type: string - format: byte + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of + the above specified type. description: >- - Must be a valid serialized protocol buffer of the - above specified type. - description: >- - `Any` contains an arbitrary serialized protocol buffer - message along with a + `Any` contains an arbitrary serialized protocol + buffer message along with a - URL that describes the type of the serialized message. + URL that describes the type of the serialized + message. - Protobuf library provides support to pack/unpack Any - values in the form + Protobuf library provides support to pack/unpack Any + values in the form - of utility functions or additional generated methods of - the Any type. + of utility functions or additional generated methods + of the Any type. - Example 1: Pack and unpack a message in C++. + Example 1: Pack and unpack a message in C++. - Foo foo = ...; - Any any; - any.PackFrom(foo); - ... - if (any.UnpackTo(&foo)) { - ... - } + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } - Example 2: Pack and unpack a message in Java. + Example 2: Pack and unpack a message in Java. - Foo foo = ...; - Any any = Any.pack(foo); - ... - if (any.is(Foo.class)) { - foo = any.unpack(Foo.class); - } - // or ... - if (any.isSameTypeAs(Foo.getDefaultInstance())) { - foo = any.unpack(Foo.getDefaultInstance()); - } + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + // or ... + if (any.isSameTypeAs(Foo.getDefaultInstance())) { + foo = any.unpack(Foo.getDefaultInstance()); + } - Example 3: Pack and unpack a message in Python. + Example 3: Pack and unpack a message in Python. - foo = Foo(...) - any = Any() - any.Pack(foo) - ... - if any.Is(Foo.DESCRIPTOR): - any.Unpack(foo) - ... + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... - Example 4: Pack and unpack a message in Go + Example 4: Pack and unpack a message in Go - foo := &pb.Foo{...} - any, err := anypb.New(foo) - if err != nil { - ... - } - ... - foo := &pb.Foo{} - if err := any.UnmarshalTo(foo); err != nil { - ... - } + foo := &pb.Foo{...} + any, err := anypb.New(foo) + if err != nil { + ... + } + ... + foo := &pb.Foo{} + if err := any.UnmarshalTo(foo); err != nil { + ... + } - The pack methods provided by protobuf library will by - default use + The pack methods provided by protobuf library will + by default use - 'type.googleapis.com/full.type.name' as the type URL and - the unpack + 'type.googleapis.com/full.type.name' as the type URL + and the unpack - methods only use the fully qualified type name after the - last '/' + methods only use the fully qualified type name after + the last '/' - in the type URL, for example "foo.bar.com/x/y.z" will - yield type + in the type URL, for example "foo.bar.com/x/y.z" + will yield type - name "y.z". + name "y.z". - JSON + JSON - The JSON representation of an `Any` value uses the - regular + The JSON representation of an `Any` value uses the + regular - representation of the deserialized, embedded message, - with an + representation of the deserialized, embedded + message, with an - additional field `@type` which contains the type URL. - Example: + additional field `@type` which contains the type + URL. Example: - package google.profile; - message Person { - string first_name = 1; - string last_name = 2; - } + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } - { - "@type": "type.googleapis.com/google.profile.Person", - "firstName": , - "lastName": - } + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } - If the embedded message type is well-known and has a - custom JSON + If the embedded message type is well-known and has a + custom JSON - representation, that representation will be embedded - adding a field + representation, that representation will be embedded + adding a field - `value` which holds the custom JSON in addition to the - `@type` + `value` which holds the custom JSON in addition to + the `@type` - field. Example (for message - [google.protobuf.Duration][]): + field. Example (for message + [google.protobuf.Duration][]): - { - "@type": "type.googleapis.com/google.protobuf.Duration", - "value": "1.212s" - } - jailed: - type: boolean - description: >- - jailed defined whether the validator has been jailed - from bonded status or not. - status: - description: >- - status is the validator status - (bonded/unbonding/unbonded). - type: string - enum: - - BOND_STATUS_UNSPECIFIED - - BOND_STATUS_UNBONDED - - BOND_STATUS_UNBONDING - - BOND_STATUS_BONDED - default: BOND_STATUS_UNSPECIFIED - tokens: - type: string - description: >- - tokens define the delegated tokens (incl. - self-delegation). - delegator_shares: - type: string - description: >- - delegator_shares defines total shares issued to a - validator's delegators. - description: - description: >- - description defines the description terms for the - validator. - type: object - properties: - moniker: - type: string + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + jailed: + type: boolean description: >- - moniker defines a human-readable name for the - validator. - identity: - type: string + jailed defined whether the validator has been jailed + from bonded status or not. + status: description: >- - identity defines an optional identity signature (ex. - UPort or Keybase). - website: + status is the validator status + (bonded/unbonding/unbonded). type: string - description: website defines an optional website link. - security_contact: + enum: + - BOND_STATUS_UNSPECIFIED + - BOND_STATUS_UNBONDED + - BOND_STATUS_UNBONDING + - BOND_STATUS_BONDED + default: BOND_STATUS_UNSPECIFIED + tokens: type: string description: >- - security_contact defines an optional email for - security contact. - details: + tokens define the delegated tokens (incl. + self-delegation). + delegator_shares: type: string - description: details define other optional details. - unbonding_height: - type: string - format: int64 - description: >- - unbonding_height defines, if unbonding, the height at - which this validator has begun unbonding. - unbonding_time: - type: string - format: date-time - description: >- - unbonding_time defines, if unbonding, the min time for - the validator to complete unbonding. - commission: - description: commission defines the commission parameters. - type: object - properties: - commission_rates: description: >- - commission_rates defines the initial commission - rates to be used for creating a validator. + delegator_shares defines total shares issued to a + validator's delegators. + description: + description: >- + description defines the description terms for the + validator. type: object properties: - rate: + moniker: type: string description: >- - rate is the commission rate charged to - delegators, as a fraction. - max_rate: + moniker defines a human-readable name for the + validator. + identity: type: string description: >- - max_rate defines the maximum commission rate - which validator can ever charge, as a fraction. - max_change_rate: + identity defines an optional identity signature + (ex. UPort or Keybase). + website: + type: string + description: website defines an optional website link. + security_contact: type: string description: >- - max_change_rate defines the maximum daily - increase of the validator commission, as a - fraction. - update_time: + security_contact defines an optional email for + security contact. + details: + type: string + description: details define other optional details. + unbonding_height: + type: string + format: int64 + description: >- + unbonding_height defines, if unbonding, the height + at which this validator has begun unbonding. + unbonding_time: type: string format: date-time description: >- - update_time is the last time the commission rate was - changed. - min_self_delegation: - type: string - description: >- - min_self_delegation is the validator's self declared - minimum self delegation. + unbonding_time defines, if unbonding, the min time + for the validator to complete unbonding. + commission: + description: commission defines the commission parameters. + type: object + properties: + commission_rates: + description: >- + commission_rates defines the initial commission + rates to be used for creating a validator. + type: object + properties: + rate: + type: string + description: >- + rate is the commission rate charged to + delegators, as a fraction. + max_rate: + type: string + description: >- + max_rate defines the maximum commission rate + which validator can ever charge, as a + fraction. + max_change_rate: + type: string + description: >- + max_change_rate defines the maximum daily + increase of the validator commission, as a + fraction. + update_time: + type: string + format: date-time + description: >- + update_time is the last time the commission rate + was changed. + min_self_delegation: + type: string + description: >- + min_self_delegation is the validator's self declared + minimum self delegation. - Since: cosmos-sdk 0.46 - description: >- - Validator defines a validator, together with the total - amount of the + Since: cosmos-sdk 0.46 + unbonding_on_hold_ref_count: + type: string + format: int64 + title: >- + strictly positive if this validator's unbonding has + been stopped by external modules + unbonding_ids: + type: array + items: + type: string + format: uint64 + title: >- + list of unbonding ids, each uniquely identifing an + unbonding of this validator + description: >- + Validator defines a validator, together with the total + amount of the - Validator's bond shares and their exchange rate to coins. - Slashing results in + Validator's bond shares and their exchange rate to + coins. Slashing results in - a decrease in the exchange rate, allowing correct - calculation of future + a decrease in the exchange rate, allowing correct + calculation of future - undelegations without iterating over delegators. When coins - are delegated to + undelegations without iterating over delegators. When + coins are delegated to - this validator, the validator is credited with a delegation - whose number of + this validator, the validator is credited with a + delegation whose number of - bond shares is based on the amount of coins delegated - divided by the current + bond shares is based on the amount of coins delegated + divided by the current - exchange rate. Voting power can be calculated as total - bonded shares + exchange rate. Voting power can be calculated as total + bonded shares - multiplied by exchange rate. - description: validators defines the validators' info of a delegator. - pagination: - description: pagination defines the pagination in the response. - type: object - properties: - next_key: - type: string - format: byte - description: |- - next_key is the key to be passed to PageRequest.key to - query the next page most efficiently. It will be empty if - there are no more results. - total: - type: string - format: uint64 - title: >- - total is total number of results available if - PageRequest.count_total + multiplied by exchange rate. + description: >- + QueryHistoricalInfoResponse is response type for the + Query/HistoricalInfo RPC - was set, its value is undefined otherwise - description: |- - QueryDelegatorValidatorsResponse is response type for the - Query/DelegatorValidators RPC method. + method. default: description: An unexpected error response. schema: @@ -43281,399 +47806,273 @@ paths: "value": "1.212s" } parameters: - - name: delegator_addr - description: delegator_addr defines the delegator address to query for. + - name: height + description: height defines at which height to query the historical info. in: path required: true type: string - - name: pagination.key - description: |- - key is a value returned in PageResponse.next_key to begin - querying the next page most efficiently. Only one of offset or key - should be set. - in: query - required: false - type: string - format: byte - - name: pagination.offset - description: >- - offset is a numeric offset that can be used when key is unavailable. - - It is less efficient than using key. Only one of offset or key - should - - be set. - in: query - required: false - type: string - format: uint64 - - name: pagination.limit - description: >- - limit is the total number of results to be returned in the result - page. - - If left empty it will default to a value to be set by each app. - in: query - required: false - type: string - format: uint64 - - name: pagination.count_total - description: >- - count_total is set to true to indicate that the result set should - include - - a count of the total number of items available for pagination in - UIs. - - count_total is only respected when offset is used. It is ignored - when key - - is set. - in: query - required: false - type: boolean - - name: pagination.reverse - description: >- - reverse is set to true if results are to be returned in the - descending order. - - - Since: cosmos-sdk 0.43 - in: query - required: false - type: boolean + format: int64 tags: - Query - /cosmos/staking/v1beta1/delegators/{delegator_addr}/validators/{validator_addr}: + /cosmos/staking/v1beta1/params: get: - summary: |- - DelegatorValidator queries validator info for given delegator validator - pair. - operationId: DelegatorValidator + summary: Parameters queries the staking parameters. + operationId: StakingParams responses: '200': description: A successful response. schema: type: object properties: - validator: + params: + description: params holds all the parameters of this module. type: object properties: - operator_address: + unbonding_time: type: string + description: unbonding_time is the time duration of unbonding. + max_validators: + type: integer + format: int64 + description: max_validators is the maximum number of validators. + max_entries: + type: integer + format: int64 description: >- - operator_address defines the address of the validator's - operator; bech encoded in JSON. - consensus_pubkey: - type: object - properties: - type_url: - type: string - description: >- - A URL/resource name that uniquely identifies the type - of the serialized - - protocol buffer message. This string must contain at - least - - one "/" character. The last segment of the URL's path - must represent - - the fully qualified name of the type (as in - - `path/google.protobuf.Duration`). The name should be - in a canonical form - - (e.g., leading "." is not accepted). - - - In practice, teams usually precompile into the binary - all types that they - - expect it to use in the context of Any. However, for - URLs which use the - - scheme `http`, `https`, or no scheme, one can - optionally set up a type - - server that maps type URLs to message definitions as - follows: - - - * If no scheme is provided, `https` is assumed. - - * An HTTP GET on the URL must yield a - [google.protobuf.Type][] - value in binary format, or produce an error. - * Applications are allowed to cache lookup results - based on the - URL, or have them precompiled into a binary to avoid any - lookup. Therefore, binary compatibility needs to be preserved - on changes to types. (Use versioned type names to manage - breaking changes.) - - Note: this functionality is not currently available in - the official - - protobuf release, and it is not used for type URLs - beginning with - - type.googleapis.com. - - - Schemes other than `http`, `https` (or the empty - scheme) might be - - used with implementation specific semantics. - value: - type: string - format: byte - description: >- - Must be a valid serialized protocol buffer of the - above specified type. + max_entries is the max entries for either unbonding + delegation or redelegation (per pair/trio). + historical_entries: + type: integer + format: int64 description: >- - `Any` contains an arbitrary serialized protocol buffer - message along with a - - URL that describes the type of the serialized message. - - - Protobuf library provides support to pack/unpack Any - values in the form + historical_entries is the number of historical entries to + persist. + bond_denom: + type: string + description: bond_denom defines the bondable coin denomination. + min_commission_rate: + type: string + title: >- + min_commission_rate is the chain-wide minimum commission + rate that a validator can charge their delegators + description: >- + QueryParamsResponse is response type for the Query/Params RPC + method. + default: + description: An unexpected error response. + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of + the serialized - of utility functions or additional generated methods of - the Any type. + protocol buffer message. This string must contain at + least + one "/" character. The last segment of the URL's path + must represent - Example 1: Pack and unpack a message in C++. + the fully qualified name of the type (as in - Foo foo = ...; - Any any; - any.PackFrom(foo); - ... - if (any.UnpackTo(&foo)) { - ... - } + `path/google.protobuf.Duration`). The name should be in + a canonical form - Example 2: Pack and unpack a message in Java. + (e.g., leading "." is not accepted). - Foo foo = ...; - Any any = Any.pack(foo); - ... - if (any.is(Foo.class)) { - foo = any.unpack(Foo.class); - } - // or ... - if (any.isSameTypeAs(Foo.getDefaultInstance())) { - foo = any.unpack(Foo.getDefaultInstance()); - } - Example 3: Pack and unpack a message in Python. + In practice, teams usually precompile into the binary + all types that they - foo = Foo(...) - any = Any() - any.Pack(foo) - ... - if any.Is(Foo.DESCRIPTOR): - any.Unpack(foo) - ... + expect it to use in the context of Any. However, for + URLs which use the - Example 4: Pack and unpack a message in Go + scheme `http`, `https`, or no scheme, one can optionally + set up a type - foo := &pb.Foo{...} - any, err := anypb.New(foo) - if err != nil { - ... - } - ... - foo := &pb.Foo{} - if err := any.UnmarshalTo(foo); err != nil { - ... - } + server that maps type URLs to message definitions as + follows: - The pack methods provided by protobuf library will by - default use - 'type.googleapis.com/full.type.name' as the type URL and - the unpack + * If no scheme is provided, `https` is assumed. - methods only use the fully qualified type name after the - last '/' + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based + on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) - in the type URL, for example "foo.bar.com/x/y.z" will - yield type + Note: this functionality is not currently available in + the official - name "y.z". + protobuf release, and it is not used for type URLs + beginning with + type.googleapis.com. - JSON + Schemes other than `http`, `https` (or the empty scheme) + might be - The JSON representation of an `Any` value uses the regular + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer + message along with a - representation of the deserialized, embedded message, with - an + URL that describes the type of the serialized message. - additional field `@type` which contains the type URL. - Example: - package google.profile; - message Person { - string first_name = 1; - string last_name = 2; - } + Protobuf library provides support to pack/unpack Any values + in the form - { - "@type": "type.googleapis.com/google.profile.Person", - "firstName": , - "lastName": - } + of utility functions or additional generated methods of the + Any type. - If the embedded message type is well-known and has a - custom JSON - representation, that representation will be embedded - adding a field + Example 1: Pack and unpack a message in C++. - `value` which holds the custom JSON in addition to the - `@type` + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } - field. Example (for message [google.protobuf.Duration][]): + Example 2: Pack and unpack a message in Java. - { - "@type": "type.googleapis.com/google.protobuf.Duration", - "value": "1.212s" - } - jailed: - type: boolean - description: >- - jailed defined whether the validator has been jailed from - bonded status or not. - status: - description: >- - status is the validator status - (bonded/unbonding/unbonded). - type: string - enum: - - BOND_STATUS_UNSPECIFIED - - BOND_STATUS_UNBONDED - - BOND_STATUS_UNBONDING - - BOND_STATUS_BONDED - default: BOND_STATUS_UNSPECIFIED - tokens: - type: string - description: >- - tokens define the delegated tokens (incl. - self-delegation). - delegator_shares: - type: string - description: >- - delegator_shares defines total shares issued to a - validator's delegators. - description: - description: >- - description defines the description terms for the - validator. - type: object - properties: - moniker: - type: string - description: >- - moniker defines a human-readable name for the - validator. - identity: - type: string - description: >- - identity defines an optional identity signature (ex. - UPort or Keybase). - website: - type: string - description: website defines an optional website link. - security_contact: - type: string - description: >- - security_contact defines an optional email for - security contact. - details: - type: string - description: details define other optional details. - unbonding_height: - type: string - format: int64 - description: >- - unbonding_height defines, if unbonding, the height at - which this validator has begun unbonding. - unbonding_time: - type: string - format: date-time - description: >- - unbonding_time defines, if unbonding, the min time for the - validator to complete unbonding. - commission: - description: commission defines the commission parameters. - type: object - properties: - commission_rates: - description: >- - commission_rates defines the initial commission rates - to be used for creating a validator. - type: object - properties: - rate: - type: string - description: >- - rate is the commission rate charged to delegators, - as a fraction. - max_rate: - type: string - description: >- - max_rate defines the maximum commission rate which - validator can ever charge, as a fraction. - max_change_rate: - type: string - description: >- - max_change_rate defines the maximum daily increase - of the validator commission, as a fraction. - update_time: - type: string - format: date-time - description: >- - update_time is the last time the commission rate was - changed. - min_self_delegation: - type: string - description: >- - min_self_delegation is the validator's self declared - minimum self delegation. + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + // or ... + if (any.isSameTypeAs(Foo.getDefaultInstance())) { + foo = any.unpack(Foo.getDefaultInstance()); + } + Example 3: Pack and unpack a message in Python. - Since: cosmos-sdk 0.46 - description: >- - Validator defines a validator, together with the total amount - of the + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... - Validator's bond shares and their exchange rate to coins. - Slashing results in + Example 4: Pack and unpack a message in Go - a decrease in the exchange rate, allowing correct calculation - of future + foo := &pb.Foo{...} + any, err := anypb.New(foo) + if err != nil { + ... + } + ... + foo := &pb.Foo{} + if err := any.UnmarshalTo(foo); err != nil { + ... + } - undelegations without iterating over delegators. When coins - are delegated to + The pack methods provided by protobuf library will by + default use - this validator, the validator is credited with a delegation - whose number of + 'type.googleapis.com/full.type.name' as the type URL and the + unpack - bond shares is based on the amount of coins delegated divided - by the current + methods only use the fully qualified type name after the + last '/' - exchange rate. Voting power can be calculated as total bonded - shares + in the type URL, for example "foo.bar.com/x/y.z" will yield + type - multiplied by exchange rate. - description: |- - QueryDelegatorValidatorResponse response type for the - Query/DelegatorValidator RPC method. + name "y.z". + + + JSON + + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with + an + + additional field `@type` which contains the type URL. + Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom + JSON + + representation, that representation will be embedded adding + a field + + `value` which holds the custom JSON in addition to the + `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + tags: + - Query + /cosmos/staking/v1beta1/pool: + get: + summary: Pool queries the pool info. + operationId: Pool + responses: + '200': + description: A successful response. + schema: + type: object + properties: + pool: + description: pool defines the pool info. + type: object + properties: + not_bonded_tokens: + type: string + bonded_tokens: + type: string + description: QueryPoolResponse is response type for the Query/Pool RPC method. default: description: An unexpected error response. schema: @@ -43868,437 +48267,380 @@ paths: "@type": "type.googleapis.com/google.protobuf.Duration", "value": "1.212s" } - parameters: - - name: delegator_addr - description: delegator_addr defines the delegator address to query for. - in: path - required: true - type: string - - name: validator_addr - description: validator_addr defines the validator address to query for. - in: path - required: true - type: string tags: - Query - /cosmos/staking/v1beta1/historical_info/{height}: + /cosmos/staking/v1beta1/validators: get: - summary: HistoricalInfo queries the historical info for given height. - operationId: HistoricalInfo + summary: Validators queries all validators that match the given status. + description: >- + When called from another module, this query might consume a high amount + of + + gas if the pagination field is incorrectly set. + operationId: Validators responses: '200': description: A successful response. schema: type: object properties: - hist: - description: hist defines the historical info at the given height. - type: object - properties: - header: - type: object - properties: - version: - title: basic block info - type: object - properties: - block: - type: string - format: uint64 - app: - type: string - format: uint64 - description: >- - Consensus captures the consensus rules for processing - a block in the blockchain, - - including all blockchain data structures and the rules - of the application's - - state transition machine. - chain_id: - type: string - height: - type: string - format: int64 - time: - type: string - format: date-time - last_block_id: - title: prev block info - type: object - properties: - hash: - type: string - format: byte - part_set_header: - type: object - properties: - total: - type: integer - format: int64 - hash: - type: string - format: byte - title: PartsetHeader - last_commit_hash: - type: string - format: byte - title: hashes of block data - data_hash: - type: string - format: byte - validators_hash: - type: string - format: byte - title: hashes from the app output from the prev block - next_validators_hash: - type: string - format: byte - consensus_hash: - type: string - format: byte - app_hash: - type: string - format: byte - last_results_hash: - type: string - format: byte - evidence_hash: - type: string - format: byte - title: consensus info - proposer_address: - type: string - format: byte - description: Header defines the structure of a block header. - valset: - type: array - items: + validators: + type: array + items: + type: object + properties: + operator_address: + type: string + description: >- + operator_address defines the address of the validator's + operator; bech encoded in JSON. + consensus_pubkey: type: object properties: - operator_address: + type_url: type: string description: >- - operator_address defines the address of the - validator's operator; bech encoded in JSON. - consensus_pubkey: - type: object - properties: - type_url: - type: string - description: >- - A URL/resource name that uniquely identifies the - type of the serialized + A URL/resource name that uniquely identifies the + type of the serialized - protocol buffer message. This string must - contain at least + protocol buffer message. This string must contain at + least - one "/" character. The last segment of the URL's - path must represent + one "/" character. The last segment of the URL's + path must represent - the fully qualified name of the type (as in + the fully qualified name of the type (as in - `path/google.protobuf.Duration`). The name - should be in a canonical form + `path/google.protobuf.Duration`). The name should be + in a canonical form - (e.g., leading "." is not accepted). + (e.g., leading "." is not accepted). - In practice, teams usually precompile into the - binary all types that they + In practice, teams usually precompile into the + binary all types that they - expect it to use in the context of Any. However, - for URLs which use the + expect it to use in the context of Any. However, for + URLs which use the - scheme `http`, `https`, or no scheme, one can - optionally set up a type + scheme `http`, `https`, or no scheme, one can + optionally set up a type - server that maps type URLs to message - definitions as follows: + server that maps type URLs to message definitions as + follows: - * If no scheme is provided, `https` is assumed. + * If no scheme is provided, `https` is assumed. - * An HTTP GET on the URL must yield a - [google.protobuf.Type][] - value in binary format, or produce an error. - * Applications are allowed to cache lookup - results based on the - URL, or have them precompiled into a binary to avoid any - lookup. Therefore, binary compatibility needs to be preserved - on changes to types. (Use versioned type names to manage - breaking changes.) + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results + based on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) - Note: this functionality is not currently - available in the official + Note: this functionality is not currently available + in the official - protobuf release, and it is not used for type - URLs beginning with + protobuf release, and it is not used for type URLs + beginning with - type.googleapis.com. + type.googleapis.com. - Schemes other than `http`, `https` (or the empty - scheme) might be + Schemes other than `http`, `https` (or the empty + scheme) might be - used with implementation specific semantics. - value: - type: string - format: byte - description: >- - Must be a valid serialized protocol buffer of - the above specified type. + used with implementation specific semantics. + value: + type: string + format: byte description: >- - `Any` contains an arbitrary serialized protocol - buffer message along with a + Must be a valid serialized protocol buffer of the + above specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer + message along with a - URL that describes the type of the serialized - message. + URL that describes the type of the serialized message. - Protobuf library provides support to pack/unpack Any - values in the form + Protobuf library provides support to pack/unpack Any + values in the form - of utility functions or additional generated methods - of the Any type. + of utility functions or additional generated methods of + the Any type. - Example 1: Pack and unpack a message in C++. + Example 1: Pack and unpack a message in C++. - Foo foo = ...; - Any any; - any.PackFrom(foo); - ... - if (any.UnpackTo(&foo)) { - ... - } + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } - Example 2: Pack and unpack a message in Java. + Example 2: Pack and unpack a message in Java. - Foo foo = ...; - Any any = Any.pack(foo); - ... - if (any.is(Foo.class)) { - foo = any.unpack(Foo.class); - } - // or ... - if (any.isSameTypeAs(Foo.getDefaultInstance())) { - foo = any.unpack(Foo.getDefaultInstance()); - } + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + // or ... + if (any.isSameTypeAs(Foo.getDefaultInstance())) { + foo = any.unpack(Foo.getDefaultInstance()); + } - Example 3: Pack and unpack a message in Python. + Example 3: Pack and unpack a message in Python. - foo = Foo(...) - any = Any() - any.Pack(foo) - ... - if any.Is(Foo.DESCRIPTOR): - any.Unpack(foo) - ... + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... - Example 4: Pack and unpack a message in Go + Example 4: Pack and unpack a message in Go - foo := &pb.Foo{...} - any, err := anypb.New(foo) - if err != nil { - ... - } - ... - foo := &pb.Foo{} - if err := any.UnmarshalTo(foo); err != nil { - ... - } + foo := &pb.Foo{...} + any, err := anypb.New(foo) + if err != nil { + ... + } + ... + foo := &pb.Foo{} + if err := any.UnmarshalTo(foo); err != nil { + ... + } - The pack methods provided by protobuf library will - by default use + The pack methods provided by protobuf library will by + default use - 'type.googleapis.com/full.type.name' as the type URL - and the unpack + 'type.googleapis.com/full.type.name' as the type URL and + the unpack - methods only use the fully qualified type name after - the last '/' + methods only use the fully qualified type name after the + last '/' - in the type URL, for example "foo.bar.com/x/y.z" - will yield type + in the type URL, for example "foo.bar.com/x/y.z" will + yield type - name "y.z". + name "y.z". - JSON + JSON - The JSON representation of an `Any` value uses the - regular + The JSON representation of an `Any` value uses the + regular - representation of the deserialized, embedded - message, with an + representation of the deserialized, embedded message, + with an - additional field `@type` which contains the type - URL. Example: + additional field `@type` which contains the type URL. + Example: - package google.profile; - message Person { - string first_name = 1; - string last_name = 2; - } + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } - { - "@type": "type.googleapis.com/google.profile.Person", - "firstName": , - "lastName": - } + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } - If the embedded message type is well-known and has a - custom JSON + If the embedded message type is well-known and has a + custom JSON - representation, that representation will be embedded - adding a field + representation, that representation will be embedded + adding a field - `value` which holds the custom JSON in addition to - the `@type` + `value` which holds the custom JSON in addition to the + `@type` - field. Example (for message - [google.protobuf.Duration][]): + field. Example (for message + [google.protobuf.Duration][]): - { - "@type": "type.googleapis.com/google.protobuf.Duration", - "value": "1.212s" - } - jailed: - type: boolean + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + jailed: + type: boolean + description: >- + jailed defined whether the validator has been jailed + from bonded status or not. + status: + description: >- + status is the validator status + (bonded/unbonding/unbonded). + type: string + enum: + - BOND_STATUS_UNSPECIFIED + - BOND_STATUS_UNBONDED + - BOND_STATUS_UNBONDING + - BOND_STATUS_BONDED + default: BOND_STATUS_UNSPECIFIED + tokens: + type: string + description: >- + tokens define the delegated tokens (incl. + self-delegation). + delegator_shares: + type: string + description: >- + delegator_shares defines total shares issued to a + validator's delegators. + description: + description: >- + description defines the description terms for the + validator. + type: object + properties: + moniker: + type: string description: >- - jailed defined whether the validator has been jailed - from bonded status or not. - status: + moniker defines a human-readable name for the + validator. + identity: + type: string description: >- - status is the validator status - (bonded/unbonding/unbonded). + identity defines an optional identity signature (ex. + UPort or Keybase). + website: type: string - enum: - - BOND_STATUS_UNSPECIFIED - - BOND_STATUS_UNBONDED - - BOND_STATUS_UNBONDING - - BOND_STATUS_BONDED - default: BOND_STATUS_UNSPECIFIED - tokens: + description: website defines an optional website link. + security_contact: type: string description: >- - tokens define the delegated tokens (incl. - self-delegation). - delegator_shares: + security_contact defines an optional email for + security contact. + details: type: string + description: details define other optional details. + unbonding_height: + type: string + format: int64 + description: >- + unbonding_height defines, if unbonding, the height at + which this validator has begun unbonding. + unbonding_time: + type: string + format: date-time + description: >- + unbonding_time defines, if unbonding, the min time for + the validator to complete unbonding. + commission: + description: commission defines the commission parameters. + type: object + properties: + commission_rates: description: >- - delegator_shares defines total shares issued to a - validator's delegators. - description: - description: >- - description defines the description terms for the - validator. + commission_rates defines the initial commission + rates to be used for creating a validator. type: object properties: - moniker: + rate: type: string description: >- - moniker defines a human-readable name for the - validator. - identity: + rate is the commission rate charged to + delegators, as a fraction. + max_rate: type: string description: >- - identity defines an optional identity signature - (ex. UPort or Keybase). - website: - type: string - description: website defines an optional website link. - security_contact: + max_rate defines the maximum commission rate + which validator can ever charge, as a fraction. + max_change_rate: type: string description: >- - security_contact defines an optional email for - security contact. - details: - type: string - description: details define other optional details. - unbonding_height: - type: string - format: int64 - description: >- - unbonding_height defines, if unbonding, the height - at which this validator has begun unbonding. - unbonding_time: + max_change_rate defines the maximum daily + increase of the validator commission, as a + fraction. + update_time: type: string format: date-time description: >- - unbonding_time defines, if unbonding, the min time - for the validator to complete unbonding. - commission: - description: commission defines the commission parameters. - type: object - properties: - commission_rates: - description: >- - commission_rates defines the initial commission - rates to be used for creating a validator. - type: object - properties: - rate: - type: string - description: >- - rate is the commission rate charged to - delegators, as a fraction. - max_rate: - type: string - description: >- - max_rate defines the maximum commission rate - which validator can ever charge, as a - fraction. - max_change_rate: - type: string - description: >- - max_change_rate defines the maximum daily - increase of the validator commission, as a - fraction. - update_time: - type: string - format: date-time - description: >- - update_time is the last time the commission rate - was changed. - min_self_delegation: - type: string - description: >- - min_self_delegation is the validator's self declared - minimum self delegation. + update_time is the last time the commission rate was + changed. + min_self_delegation: + type: string + description: >- + min_self_delegation is the validator's self declared + minimum self delegation. - Since: cosmos-sdk 0.46 - description: >- - Validator defines a validator, together with the total - amount of the + Since: cosmos-sdk 0.46 + unbonding_on_hold_ref_count: + type: string + format: int64 + title: >- + strictly positive if this validator's unbonding has been + stopped by external modules + unbonding_ids: + type: array + items: + type: string + format: uint64 + title: >- + list of unbonding ids, each uniquely identifing an + unbonding of this validator + description: >- + Validator defines a validator, together with the total + amount of the - Validator's bond shares and their exchange rate to - coins. Slashing results in + Validator's bond shares and their exchange rate to coins. + Slashing results in - a decrease in the exchange rate, allowing correct - calculation of future + a decrease in the exchange rate, allowing correct + calculation of future - undelegations without iterating over delegators. When - coins are delegated to + undelegations without iterating over delegators. When coins + are delegated to - this validator, the validator is credited with a - delegation whose number of + this validator, the validator is credited with a delegation + whose number of - bond shares is based on the amount of coins delegated - divided by the current + bond shares is based on the amount of coins delegated + divided by the current - exchange rate. Voting power can be calculated as total - bonded shares + exchange rate. Voting power can be calculated as total + bonded shares - multiplied by exchange rate. - description: >- - QueryHistoricalInfoResponse is response type for the - Query/HistoricalInfo RPC + multiplied by exchange rate. + description: validators contains all the queried validators. + pagination: + description: pagination defines the pagination in the response. + type: object + properties: + next_key: + type: string + format: byte + description: |- + next_key is the key to be passed to PageRequest.key to + query the next page most efficiently. It will be empty if + there are no more results. + total: + type: string + format: uint64 + title: >- + total is total number of results available if + PageRequest.count_total - method. + was set, its value is undefined otherwise + title: >- + QueryValidatorsResponse is response type for the Query/Validators + RPC method default: description: An unexpected error response. schema: @@ -44494,273 +48836,411 @@ paths: "value": "1.212s" } parameters: - - name: height - description: height defines at which height to query the historical info. - in: path - required: true + - name: status + description: status enables to query for validators matching a given status. + in: query + required: false type: string - format: int64 + - name: pagination.key + description: |- + key is a value returned in PageResponse.next_key to begin + querying the next page most efficiently. Only one of offset or key + should be set. + in: query + required: false + type: string + format: byte + - name: pagination.offset + description: >- + offset is a numeric offset that can be used when key is unavailable. + + It is less efficient than using key. Only one of offset or key + should + + be set. + in: query + required: false + type: string + format: uint64 + - name: pagination.limit + description: >- + limit is the total number of results to be returned in the result + page. + + If left empty it will default to a value to be set by each app. + in: query + required: false + type: string + format: uint64 + - name: pagination.count_total + description: >- + count_total is set to true to indicate that the result set should + include + + a count of the total number of items available for pagination in + UIs. + + count_total is only respected when offset is used. It is ignored + when key + + is set. + in: query + required: false + type: boolean + - name: pagination.reverse + description: >- + reverse is set to true if results are to be returned in the + descending order. + + + Since: cosmos-sdk 0.43 + in: query + required: false + type: boolean tags: - Query - /cosmos/staking/v1beta1/params: + /cosmos/staking/v1beta1/validators/{validator_addr}: get: - summary: Parameters queries the staking parameters. - operationId: StakingParams + summary: Validator queries validator info for given validator address. + operationId: Validator responses: '200': description: A successful response. schema: type: object properties: - params: - description: params holds all the parameters of this module. + validator: type: object properties: - unbonding_time: + operator_address: type: string - description: unbonding_time is the time duration of unbonding. - max_validators: - type: integer - format: int64 - description: max_validators is the maximum number of validators. - max_entries: - type: integer - format: int64 - description: >- - max_entries is the max entries for either unbonding - delegation or redelegation (per pair/trio). - historical_entries: - type: integer - format: int64 description: >- - historical_entries is the number of historical entries to - persist. - bond_denom: - type: string - description: bond_denom defines the bondable coin denomination. - min_commission_rate: - type: string - title: >- - min_commission_rate is the chain-wide minimum commission - rate that a validator can charge their delegators - description: >- - QueryParamsResponse is response type for the Query/Params RPC - method. - default: - description: An unexpected error response. - schema: - type: object - properties: - error: - type: string - code: - type: integer - format: int32 - message: - type: string - details: - type: array - items: - type: object - properties: - type_url: - type: string - description: >- - A URL/resource name that uniquely identifies the type of - the serialized + operator_address defines the address of the validator's + operator; bech encoded in JSON. + consensus_pubkey: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type + of the serialized - protocol buffer message. This string must contain at - least + protocol buffer message. This string must contain at + least - one "/" character. The last segment of the URL's path - must represent + one "/" character. The last segment of the URL's path + must represent - the fully qualified name of the type (as in + the fully qualified name of the type (as in - `path/google.protobuf.Duration`). The name should be in - a canonical form + `path/google.protobuf.Duration`). The name should be + in a canonical form - (e.g., leading "." is not accepted). + (e.g., leading "." is not accepted). - In practice, teams usually precompile into the binary - all types that they + In practice, teams usually precompile into the binary + all types that they - expect it to use in the context of Any. However, for - URLs which use the + expect it to use in the context of Any. However, for + URLs which use the - scheme `http`, `https`, or no scheme, one can optionally - set up a type + scheme `http`, `https`, or no scheme, one can + optionally set up a type - server that maps type URLs to message definitions as - follows: + server that maps type URLs to message definitions as + follows: - * If no scheme is provided, `https` is assumed. + * If no scheme is provided, `https` is assumed. - * An HTTP GET on the URL must yield a - [google.protobuf.Type][] - value in binary format, or produce an error. - * Applications are allowed to cache lookup results based - on the - URL, or have them precompiled into a binary to avoid any - lookup. Therefore, binary compatibility needs to be preserved - on changes to types. (Use versioned type names to manage - breaking changes.) + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results + based on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) - Note: this functionality is not currently available in - the official + Note: this functionality is not currently available in + the official - protobuf release, and it is not used for type URLs - beginning with + protobuf release, and it is not used for type URLs + beginning with - type.googleapis.com. + type.googleapis.com. - Schemes other than `http`, `https` (or the empty scheme) - might be + Schemes other than `http`, `https` (or the empty + scheme) might be - used with implementation specific semantics. - value: - type: string - format: byte - description: >- - Must be a valid serialized protocol buffer of the above - specified type. - description: >- - `Any` contains an arbitrary serialized protocol buffer - message along with a + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the + above specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer + message along with a - URL that describes the type of the serialized message. + URL that describes the type of the serialized message. - Protobuf library provides support to pack/unpack Any values - in the form + Protobuf library provides support to pack/unpack Any + values in the form - of utility functions or additional generated methods of the - Any type. + of utility functions or additional generated methods of + the Any type. - Example 1: Pack and unpack a message in C++. + Example 1: Pack and unpack a message in C++. - Foo foo = ...; - Any any; - any.PackFrom(foo); - ... - if (any.UnpackTo(&foo)) { + Foo foo = ...; + Any any; + any.PackFrom(foo); ... - } + if (any.UnpackTo(&foo)) { + ... + } - Example 2: Pack and unpack a message in Java. + Example 2: Pack and unpack a message in Java. - Foo foo = ...; - Any any = Any.pack(foo); - ... - if (any.is(Foo.class)) { - foo = any.unpack(Foo.class); - } - // or ... - if (any.isSameTypeAs(Foo.getDefaultInstance())) { - foo = any.unpack(Foo.getDefaultInstance()); - } + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + // or ... + if (any.isSameTypeAs(Foo.getDefaultInstance())) { + foo = any.unpack(Foo.getDefaultInstance()); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := anypb.New(foo) + if err != nil { + ... + } + ... + foo := &pb.Foo{} + if err := any.UnmarshalTo(foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by + default use + + 'type.googleapis.com/full.type.name' as the type URL and + the unpack + + methods only use the fully qualified type name after the + last '/' - Example 3: Pack and unpack a message in Python. + in the type URL, for example "foo.bar.com/x/y.z" will + yield type - foo = Foo(...) - any = Any() - any.Pack(foo) - ... - if any.Is(Foo.DESCRIPTOR): - any.Unpack(foo) - ... + name "y.z". - Example 4: Pack and unpack a message in Go - foo := &pb.Foo{...} - any, err := anypb.New(foo) - if err != nil { - ... - } - ... - foo := &pb.Foo{} - if err := any.UnmarshalTo(foo); err != nil { - ... - } + JSON - The pack methods provided by protobuf library will by - default use - 'type.googleapis.com/full.type.name' as the type URL and the - unpack + The JSON representation of an `Any` value uses the regular - methods only use the fully qualified type name after the - last '/' + representation of the deserialized, embedded message, with + an - in the type URL, for example "foo.bar.com/x/y.z" will yield - type + additional field `@type` which contains the type URL. + Example: - name "y.z". + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } - JSON + If the embedded message type is well-known and has a + custom JSON + representation, that representation will be embedded + adding a field - The JSON representation of an `Any` value uses the regular + `value` which holds the custom JSON in addition to the + `@type` - representation of the deserialized, embedded message, with - an + field. Example (for message [google.protobuf.Duration][]): - additional field `@type` which contains the type URL. - Example: + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + jailed: + type: boolean + description: >- + jailed defined whether the validator has been jailed from + bonded status or not. + status: + description: >- + status is the validator status + (bonded/unbonding/unbonded). + type: string + enum: + - BOND_STATUS_UNSPECIFIED + - BOND_STATUS_UNBONDED + - BOND_STATUS_UNBONDING + - BOND_STATUS_BONDED + default: BOND_STATUS_UNSPECIFIED + tokens: + type: string + description: >- + tokens define the delegated tokens (incl. + self-delegation). + delegator_shares: + type: string + description: >- + delegator_shares defines total shares issued to a + validator's delegators. + description: + description: >- + description defines the description terms for the + validator. + type: object + properties: + moniker: + type: string + description: >- + moniker defines a human-readable name for the + validator. + identity: + type: string + description: >- + identity defines an optional identity signature (ex. + UPort or Keybase). + website: + type: string + description: website defines an optional website link. + security_contact: + type: string + description: >- + security_contact defines an optional email for + security contact. + details: + type: string + description: details define other optional details. + unbonding_height: + type: string + format: int64 + description: >- + unbonding_height defines, if unbonding, the height at + which this validator has begun unbonding. + unbonding_time: + type: string + format: date-time + description: >- + unbonding_time defines, if unbonding, the min time for the + validator to complete unbonding. + commission: + description: commission defines the commission parameters. + type: object + properties: + commission_rates: + description: >- + commission_rates defines the initial commission rates + to be used for creating a validator. + type: object + properties: + rate: + type: string + description: >- + rate is the commission rate charged to delegators, + as a fraction. + max_rate: + type: string + description: >- + max_rate defines the maximum commission rate which + validator can ever charge, as a fraction. + max_change_rate: + type: string + description: >- + max_change_rate defines the maximum daily increase + of the validator commission, as a fraction. + update_time: + type: string + format: date-time + description: >- + update_time is the last time the commission rate was + changed. + min_self_delegation: + type: string + description: >- + min_self_delegation is the validator's self declared + minimum self delegation. - package google.profile; - message Person { - string first_name = 1; - string last_name = 2; - } - { - "@type": "type.googleapis.com/google.profile.Person", - "firstName": , - "lastName": - } + Since: cosmos-sdk 0.46 + unbonding_on_hold_ref_count: + type: string + format: int64 + title: >- + strictly positive if this validator's unbonding has been + stopped by external modules + unbonding_ids: + type: array + items: + type: string + format: uint64 + title: >- + list of unbonding ids, each uniquely identifing an + unbonding of this validator + description: >- + Validator defines a validator, together with the total amount + of the - If the embedded message type is well-known and has a custom - JSON + Validator's bond shares and their exchange rate to coins. + Slashing results in - representation, that representation will be embedded adding - a field + a decrease in the exchange rate, allowing correct calculation + of future - `value` which holds the custom JSON in addition to the - `@type` + undelegations without iterating over delegators. When coins + are delegated to - field. Example (for message [google.protobuf.Duration][]): + this validator, the validator is credited with a delegation + whose number of - { - "@type": "type.googleapis.com/google.protobuf.Duration", - "value": "1.212s" - } - tags: - - Query - /cosmos/staking/v1beta1/pool: - get: - summary: Pool queries the pool info. - operationId: Pool - responses: - '200': - description: A successful response. - schema: - type: object - properties: - pool: - description: pool defines the pool info. - type: object - properties: - not_bonded_tokens: - type: string - bonded_tokens: - type: string - description: QueryPoolResponse is response type for the Query/Pool RPC method. + bond shares is based on the amount of coins delegated divided + by the current + + exchange rate. Voting power can be calculated as total bonded + shares + + multiplied by exchange rate. + title: >- + QueryValidatorResponse is response type for the Query/Validator + RPC method default: description: An unexpected error response. schema: @@ -44955,339 +49435,79 @@ paths: "@type": "type.googleapis.com/google.protobuf.Duration", "value": "1.212s" } + parameters: + - name: validator_addr + description: validator_addr defines the validator address to query for. + in: path + required: true + type: string tags: - Query - /cosmos/staking/v1beta1/validators: + /cosmos/staking/v1beta1/validators/{validator_addr}/delegations: get: - summary: Validators queries all validators that match the given status. - operationId: Validators - responses: - '200': - description: A successful response. - schema: - type: object - properties: - validators: - type: array - items: - type: object - properties: - operator_address: - type: string - description: >- - operator_address defines the address of the validator's - operator; bech encoded in JSON. - consensus_pubkey: - type: object - properties: - type_url: - type: string - description: >- - A URL/resource name that uniquely identifies the - type of the serialized - - protocol buffer message. This string must contain at - least - - one "/" character. The last segment of the URL's - path must represent - - the fully qualified name of the type (as in - - `path/google.protobuf.Duration`). The name should be - in a canonical form - - (e.g., leading "." is not accepted). - - - In practice, teams usually precompile into the - binary all types that they - - expect it to use in the context of Any. However, for - URLs which use the - - scheme `http`, `https`, or no scheme, one can - optionally set up a type - - server that maps type URLs to message definitions as - follows: - - - * If no scheme is provided, `https` is assumed. - - * An HTTP GET on the URL must yield a - [google.protobuf.Type][] - value in binary format, or produce an error. - * Applications are allowed to cache lookup results - based on the - URL, or have them precompiled into a binary to avoid any - lookup. Therefore, binary compatibility needs to be preserved - on changes to types. (Use versioned type names to manage - breaking changes.) - - Note: this functionality is not currently available - in the official - - protobuf release, and it is not used for type URLs - beginning with - - type.googleapis.com. - - - Schemes other than `http`, `https` (or the empty - scheme) might be - - used with implementation specific semantics. - value: - type: string - format: byte - description: >- - Must be a valid serialized protocol buffer of the - above specified type. - description: >- - `Any` contains an arbitrary serialized protocol buffer - message along with a - - URL that describes the type of the serialized message. - - - Protobuf library provides support to pack/unpack Any - values in the form - - of utility functions or additional generated methods of - the Any type. - - - Example 1: Pack and unpack a message in C++. - - Foo foo = ...; - Any any; - any.PackFrom(foo); - ... - if (any.UnpackTo(&foo)) { - ... - } - - Example 2: Pack and unpack a message in Java. - - Foo foo = ...; - Any any = Any.pack(foo); - ... - if (any.is(Foo.class)) { - foo = any.unpack(Foo.class); - } - // or ... - if (any.isSameTypeAs(Foo.getDefaultInstance())) { - foo = any.unpack(Foo.getDefaultInstance()); - } - - Example 3: Pack and unpack a message in Python. - - foo = Foo(...) - any = Any() - any.Pack(foo) - ... - if any.Is(Foo.DESCRIPTOR): - any.Unpack(foo) - ... - - Example 4: Pack and unpack a message in Go - - foo := &pb.Foo{...} - any, err := anypb.New(foo) - if err != nil { - ... - } - ... - foo := &pb.Foo{} - if err := any.UnmarshalTo(foo); err != nil { - ... - } - - The pack methods provided by protobuf library will by - default use - - 'type.googleapis.com/full.type.name' as the type URL and - the unpack - - methods only use the fully qualified type name after the - last '/' - - in the type URL, for example "foo.bar.com/x/y.z" will - yield type - - name "y.z". - - - JSON - - - The JSON representation of an `Any` value uses the - regular - - representation of the deserialized, embedded message, - with an - - additional field `@type` which contains the type URL. - Example: - - package google.profile; - message Person { - string first_name = 1; - string last_name = 2; - } - - { - "@type": "type.googleapis.com/google.profile.Person", - "firstName": , - "lastName": - } - - If the embedded message type is well-known and has a - custom JSON - - representation, that representation will be embedded - adding a field - - `value` which holds the custom JSON in addition to the - `@type` - - field. Example (for message - [google.protobuf.Duration][]): - - { - "@type": "type.googleapis.com/google.protobuf.Duration", - "value": "1.212s" - } - jailed: - type: boolean - description: >- - jailed defined whether the validator has been jailed - from bonded status or not. - status: - description: >- - status is the validator status - (bonded/unbonding/unbonded). - type: string - enum: - - BOND_STATUS_UNSPECIFIED - - BOND_STATUS_UNBONDED - - BOND_STATUS_UNBONDING - - BOND_STATUS_BONDED - default: BOND_STATUS_UNSPECIFIED - tokens: - type: string - description: >- - tokens define the delegated tokens (incl. - self-delegation). - delegator_shares: - type: string - description: >- - delegator_shares defines total shares issued to a - validator's delegators. - description: - description: >- - description defines the description terms for the - validator. - type: object - properties: - moniker: - type: string - description: >- - moniker defines a human-readable name for the - validator. - identity: - type: string - description: >- - identity defines an optional identity signature (ex. - UPort or Keybase). - website: - type: string - description: website defines an optional website link. - security_contact: - type: string - description: >- - security_contact defines an optional email for - security contact. - details: - type: string - description: details define other optional details. - unbonding_height: - type: string - format: int64 - description: >- - unbonding_height defines, if unbonding, the height at - which this validator has begun unbonding. - unbonding_time: - type: string - format: date-time - description: >- - unbonding_time defines, if unbonding, the min time for - the validator to complete unbonding. - commission: - description: commission defines the commission parameters. + summary: ValidatorDelegations queries delegate info for given validator. + description: >- + When called from another module, this query might consume a high amount + of + + gas if the pagination field is incorrectly set. + operationId: ValidatorDelegations + responses: + '200': + description: A successful response. + schema: + type: object + properties: + delegation_responses: + type: array + items: + type: object + properties: + delegation: type: object properties: - commission_rates: + delegator_address: + type: string description: >- - commission_rates defines the initial commission - rates to be used for creating a validator. - type: object - properties: - rate: - type: string - description: >- - rate is the commission rate charged to - delegators, as a fraction. - max_rate: - type: string - description: >- - max_rate defines the maximum commission rate - which validator can ever charge, as a fraction. - max_change_rate: - type: string - description: >- - max_change_rate defines the maximum daily - increase of the validator commission, as a - fraction. - update_time: + delegator_address is the bech32-encoded address of + the delegator. + validator_address: type: string - format: date-time description: >- - update_time is the last time the commission rate was - changed. - min_self_delegation: - type: string + validator_address is the bech32-encoded address of + the validator. + shares: + type: string + description: shares define the delegation shares received. description: >- - min_self_delegation is the validator's self declared - minimum self delegation. - - - Since: cosmos-sdk 0.46 - description: >- - Validator defines a validator, together with the total - amount of the - - Validator's bond shares and their exchange rate to coins. - Slashing results in + Delegation represents the bond with tokens held by an + account. It is - a decrease in the exchange rate, allowing correct - calculation of future + owned by one delegator, and is associated with the + voting power of one - undelegations without iterating over delegators. When coins - are delegated to + validator. + balance: + type: object + properties: + denom: + type: string + amount: + type: string + description: >- + Coin defines a token with a denomination and an amount. - this validator, the validator is credited with a delegation - whose number of - bond shares is based on the amount of coins delegated - divided by the current + NOTE: The amount field is an Int which implements the + custom method - exchange rate. Voting power can be calculated as total - bonded shares + signatures required by gogoproto. + description: >- + DelegationResponse is equivalent to Delegation except that + it contains a - multiplied by exchange rate. - description: validators contains all the queried validators. + balance in addition to shares which is more suitable for + client responses. pagination: description: pagination defines the pagination in the response. type: object @@ -45307,9 +49527,9 @@ paths: PageRequest.count_total was set, its value is undefined otherwise - title: >- - QueryValidatorsResponse is response type for the Query/Validators - RPC method + title: |- + QueryValidatorDelegationsResponse is response type for the + Query/ValidatorDelegations RPC method default: description: An unexpected error response. schema: @@ -45505,10 +49725,10 @@ paths: "value": "1.212s" } parameters: - - name: status - description: status enables to query for validators matching a given status. - in: query - required: false + - name: validator_addr + description: validator_addr defines the validator address to query for. + in: path + required: true type: string - name: pagination.key description: |- @@ -45568,334 +49788,348 @@ paths: type: boolean tags: - Query - /cosmos/staking/v1beta1/validators/{validator_addr}: + /cosmos/staking/v1beta1/validators/{validator_addr}/delegations/{delegator_addr}: get: - summary: Validator queries validator info for given validator address. - operationId: Validator + summary: Delegation queries delegate info for given validator delegator pair. + operationId: Delegation responses: '200': description: A successful response. schema: type: object properties: - validator: + delegation_response: type: object properties: - operator_address: - type: string - description: >- - operator_address defines the address of the validator's - operator; bech encoded in JSON. - consensus_pubkey: + delegation: type: object properties: - type_url: + delegator_address: type: string description: >- - A URL/resource name that uniquely identifies the type - of the serialized - - protocol buffer message. This string must contain at - least - - one "/" character. The last segment of the URL's path - must represent - - the fully qualified name of the type (as in - - `path/google.protobuf.Duration`). The name should be - in a canonical form - - (e.g., leading "." is not accepted). - - - In practice, teams usually precompile into the binary - all types that they - - expect it to use in the context of Any. However, for - URLs which use the - - scheme `http`, `https`, or no scheme, one can - optionally set up a type + delegator_address is the bech32-encoded address of the + delegator. + validator_address: + type: string + description: >- + validator_address is the bech32-encoded address of the + validator. + shares: + type: string + description: shares define the delegation shares received. + description: >- + Delegation represents the bond with tokens held by an + account. It is - server that maps type URLs to message definitions as - follows: + owned by one delegator, and is associated with the voting + power of one + validator. + balance: + type: object + properties: + denom: + type: string + amount: + type: string + description: >- + Coin defines a token with a denomination and an amount. - * If no scheme is provided, `https` is assumed. - * An HTTP GET on the URL must yield a - [google.protobuf.Type][] - value in binary format, or produce an error. - * Applications are allowed to cache lookup results - based on the - URL, or have them precompiled into a binary to avoid any - lookup. Therefore, binary compatibility needs to be preserved - on changes to types. (Use versioned type names to manage - breaking changes.) + NOTE: The amount field is an Int which implements the + custom method - Note: this functionality is not currently available in - the official + signatures required by gogoproto. + description: >- + DelegationResponse is equivalent to Delegation except that it + contains a - protobuf release, and it is not used for type URLs - beginning with + balance in addition to shares which is more suitable for + client responses. + description: >- + QueryDelegationResponse is response type for the Query/Delegation + RPC method. + default: + description: An unexpected error response. + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of + the serialized - type.googleapis.com. + protocol buffer message. This string must contain at + least + one "/" character. The last segment of the URL's path + must represent - Schemes other than `http`, `https` (or the empty - scheme) might be + the fully qualified name of the type (as in - used with implementation specific semantics. - value: - type: string - format: byte - description: >- - Must be a valid serialized protocol buffer of the - above specified type. - description: >- - `Any` contains an arbitrary serialized protocol buffer - message along with a + `path/google.protobuf.Duration`). The name should be in + a canonical form - URL that describes the type of the serialized message. + (e.g., leading "." is not accepted). - Protobuf library provides support to pack/unpack Any - values in the form + In practice, teams usually precompile into the binary + all types that they - of utility functions or additional generated methods of - the Any type. + expect it to use in the context of Any. However, for + URLs which use the + scheme `http`, `https`, or no scheme, one can optionally + set up a type - Example 1: Pack and unpack a message in C++. + server that maps type URLs to message definitions as + follows: - Foo foo = ...; - Any any; - any.PackFrom(foo); - ... - if (any.UnpackTo(&foo)) { - ... - } - Example 2: Pack and unpack a message in Java. + * If no scheme is provided, `https` is assumed. - Foo foo = ...; - Any any = Any.pack(foo); - ... - if (any.is(Foo.class)) { - foo = any.unpack(Foo.class); - } - // or ... - if (any.isSameTypeAs(Foo.getDefaultInstance())) { - foo = any.unpack(Foo.getDefaultInstance()); - } + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based + on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) - Example 3: Pack and unpack a message in Python. + Note: this functionality is not currently available in + the official - foo = Foo(...) - any = Any() - any.Pack(foo) - ... - if any.Is(Foo.DESCRIPTOR): - any.Unpack(foo) - ... + protobuf release, and it is not used for type URLs + beginning with - Example 4: Pack and unpack a message in Go + type.googleapis.com. - foo := &pb.Foo{...} - any, err := anypb.New(foo) - if err != nil { - ... - } - ... - foo := &pb.Foo{} - if err := any.UnmarshalTo(foo); err != nil { - ... - } - The pack methods provided by protobuf library will by - default use + Schemes other than `http`, `https` (or the empty scheme) + might be - 'type.googleapis.com/full.type.name' as the type URL and - the unpack + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer + message along with a - methods only use the fully qualified type name after the - last '/' + URL that describes the type of the serialized message. - in the type URL, for example "foo.bar.com/x/y.z" will - yield type - name "y.z". + Protobuf library provides support to pack/unpack Any values + in the form + of utility functions or additional generated methods of the + Any type. - JSON + Example 1: Pack and unpack a message in C++. - The JSON representation of an `Any` value uses the regular + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } - representation of the deserialized, embedded message, with - an + Example 2: Pack and unpack a message in Java. - additional field `@type` which contains the type URL. - Example: + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + // or ... + if (any.isSameTypeAs(Foo.getDefaultInstance())) { + foo = any.unpack(Foo.getDefaultInstance()); + } - package google.profile; - message Person { - string first_name = 1; - string last_name = 2; - } + Example 3: Pack and unpack a message in Python. - { - "@type": "type.googleapis.com/google.profile.Person", - "firstName": , - "lastName": - } + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... - If the embedded message type is well-known and has a - custom JSON + Example 4: Pack and unpack a message in Go - representation, that representation will be embedded - adding a field + foo := &pb.Foo{...} + any, err := anypb.New(foo) + if err != nil { + ... + } + ... + foo := &pb.Foo{} + if err := any.UnmarshalTo(foo); err != nil { + ... + } - `value` which holds the custom JSON in addition to the - `@type` + The pack methods provided by protobuf library will by + default use - field. Example (for message [google.protobuf.Duration][]): + 'type.googleapis.com/full.type.name' as the type URL and the + unpack - { - "@type": "type.googleapis.com/google.protobuf.Duration", - "value": "1.212s" - } - jailed: - type: boolean - description: >- - jailed defined whether the validator has been jailed from - bonded status or not. - status: - description: >- - status is the validator status - (bonded/unbonding/unbonded). - type: string - enum: - - BOND_STATUS_UNSPECIFIED - - BOND_STATUS_UNBONDED - - BOND_STATUS_UNBONDING - - BOND_STATUS_BONDED - default: BOND_STATUS_UNSPECIFIED - tokens: - type: string - description: >- - tokens define the delegated tokens (incl. - self-delegation). - delegator_shares: - type: string - description: >- - delegator_shares defines total shares issued to a - validator's delegators. - description: - description: >- - description defines the description terms for the - validator. - type: object - properties: - moniker: - type: string - description: >- - moniker defines a human-readable name for the - validator. - identity: - type: string - description: >- - identity defines an optional identity signature (ex. - UPort or Keybase). - website: - type: string - description: website defines an optional website link. - security_contact: - type: string - description: >- - security_contact defines an optional email for - security contact. - details: - type: string - description: details define other optional details. - unbonding_height: - type: string - format: int64 - description: >- - unbonding_height defines, if unbonding, the height at - which this validator has begun unbonding. - unbonding_time: - type: string - format: date-time - description: >- - unbonding_time defines, if unbonding, the min time for the - validator to complete unbonding. - commission: - description: commission defines the commission parameters. - type: object - properties: - commission_rates: - description: >- - commission_rates defines the initial commission rates - to be used for creating a validator. - type: object - properties: - rate: - type: string - description: >- - rate is the commission rate charged to delegators, - as a fraction. - max_rate: - type: string - description: >- - max_rate defines the maximum commission rate which - validator can ever charge, as a fraction. - max_change_rate: - type: string - description: >- - max_change_rate defines the maximum daily increase - of the validator commission, as a fraction. - update_time: - type: string - format: date-time - description: >- - update_time is the last time the commission rate was - changed. - min_self_delegation: - type: string - description: >- - min_self_delegation is the validator's self declared - minimum self delegation. + methods only use the fully qualified type name after the + last '/' + in the type URL, for example "foo.bar.com/x/y.z" will yield + type - Since: cosmos-sdk 0.46 - description: >- - Validator defines a validator, together with the total amount - of the + name "y.z". - Validator's bond shares and their exchange rate to coins. - Slashing results in - a decrease in the exchange rate, allowing correct calculation - of future + JSON - undelegations without iterating over delegators. When coins - are delegated to - this validator, the validator is credited with a delegation - whose number of + The JSON representation of an `Any` value uses the regular - bond shares is based on the amount of coins delegated divided - by the current + representation of the deserialized, embedded message, with + an - exchange rate. Voting power can be calculated as total bonded - shares + additional field `@type` which contains the type URL. + Example: - multiplied by exchange rate. - title: >- - QueryValidatorResponse is response type for the Query/Validator - RPC method + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom + JSON + + representation, that representation will be embedded adding + a field + + `value` which holds the custom JSON in addition to the + `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + parameters: + - name: validator_addr + description: validator_addr defines the validator address to query for. + in: path + required: true + type: string + - name: delegator_addr + description: delegator_addr defines the delegator address to query for. + in: path + required: true + type: string + tags: + - Query + /cosmos/staking/v1beta1/validators/{validator_addr}/delegations/{delegator_addr}/unbonding_delegation: + get: + summary: |- + UnbondingDelegation queries unbonding info for given validator delegator + pair. + operationId: UnbondingDelegation + responses: + '200': + description: A successful response. + schema: + type: object + properties: + unbond: + type: object + properties: + delegator_address: + type: string + description: >- + delegator_address is the bech32-encoded address of the + delegator. + validator_address: + type: string + description: >- + validator_address is the bech32-encoded address of the + validator. + entries: + type: array + items: + type: object + properties: + creation_height: + type: string + format: int64 + description: >- + creation_height is the height which the unbonding + took place. + completion_time: + type: string + format: date-time + description: >- + completion_time is the unix time for unbonding + completion. + initial_balance: + type: string + description: >- + initial_balance defines the tokens initially + scheduled to receive at completion. + balance: + type: string + description: balance defines the tokens to receive at completion. + unbonding_id: + type: string + format: uint64 + title: Incrementing id that uniquely identifies this entry + unbonding_on_hold_ref_count: + type: string + format: int64 + title: >- + Strictly positive if this entry's unbonding has been + stopped by external modules + description: >- + UnbondingDelegationEntry defines an unbonding object + with relevant metadata. + description: entries are the unbonding delegation entries. + description: >- + UnbondingDelegation stores all of a single delegator's + unbonding bonds + + for a single validator in an time-ordered list. + description: >- + QueryDelegationResponse is response type for the + Query/UnbondingDelegation + + RPC method. default: description: An unexpected error response. schema: @@ -46096,68 +50330,93 @@ paths: in: path required: true type: string + - name: delegator_addr + description: delegator_addr defines the delegator address to query for. + in: path + required: true + type: string tags: - Query - /cosmos/staking/v1beta1/validators/{validator_addr}/delegations: + /cosmos/staking/v1beta1/validators/{validator_addr}/unbonding_delegations: get: - summary: ValidatorDelegations queries delegate info for given validator. - operationId: ValidatorDelegations + summary: >- + ValidatorUnbondingDelegations queries unbonding delegations of a + validator. + description: >- + When called from another module, this query might consume a high amount + of + + gas if the pagination field is incorrectly set. + operationId: ValidatorUnbondingDelegations responses: '200': description: A successful response. schema: type: object properties: - delegation_responses: + unbonding_responses: type: array items: type: object properties: - delegation: - type: object - properties: - delegator_address: - type: string - description: >- - delegator_address is the bech32-encoded address of - the delegator. - validator_address: - type: string - description: >- - validator_address is the bech32-encoded address of - the validator. - shares: - type: string - description: shares define the delegation shares received. + delegator_address: + type: string description: >- - Delegation represents the bond with tokens held by an - account. It is - - owned by one delegator, and is associated with the - voting power of one - - validator. - balance: - type: object - properties: - denom: - type: string - amount: - type: string + delegator_address is the bech32-encoded address of the + delegator. + validator_address: + type: string description: >- - Coin defines a token with a denomination and an amount. - - - NOTE: The amount field is an Int which implements the - custom method - - signatures required by gogoproto. + validator_address is the bech32-encoded address of the + validator. + entries: + type: array + items: + type: object + properties: + creation_height: + type: string + format: int64 + description: >- + creation_height is the height which the unbonding + took place. + completion_time: + type: string + format: date-time + description: >- + completion_time is the unix time for unbonding + completion. + initial_balance: + type: string + description: >- + initial_balance defines the tokens initially + scheduled to receive at completion. + balance: + type: string + description: >- + balance defines the tokens to receive at + completion. + unbonding_id: + type: string + format: uint64 + title: >- + Incrementing id that uniquely identifies this + entry + unbonding_on_hold_ref_count: + type: string + format: int64 + title: >- + Strictly positive if this entry's unbonding has + been stopped by external modules + description: >- + UnbondingDelegationEntry defines an unbonding object + with relevant metadata. + description: entries are the unbonding delegation entries. description: >- - DelegationResponse is equivalent to Delegation except that - it contains a + UnbondingDelegation stores all of a single delegator's + unbonding bonds - balance in addition to shares which is more suitable for - client responses. + for a single validator in an time-ordered list. pagination: description: pagination defines the pagination in the response. type: object @@ -46177,9 +50436,11 @@ paths: PageRequest.count_total was set, its value is undefined otherwise - title: |- - QueryValidatorDelegationsResponse is response type for the - Query/ValidatorDelegations RPC method + description: >- + QueryValidatorUnbondingDelegationsResponse is response type for + the + + Query/ValidatorUnbondingDelegations RPC method. default: description: An unexpected error response. schema: @@ -46438,67 +50699,249 @@ paths: type: boolean tags: - Query - /cosmos/staking/v1beta1/validators/{validator_addr}/delegations/{delegator_addr}: - get: - summary: Delegation queries delegate info for given validator delegator pair. - operationId: Delegation + /cosmos/tx/v1beta1/decode: + post: + summary: TxDecode decodes the transaction. + description: 'Since: cosmos-sdk 0.47' + operationId: TxDecode responses: '200': description: A successful response. + schema: + $ref: '#/definitions/cosmos.tx.v1beta1.TxDecodeResponse' + default: + description: An unexpected error response. schema: type: object properties: - delegation_response: - type: object - properties: - delegation: - type: object - properties: - delegator_address: - type: string - description: >- - delegator_address is the bech32-encoded address of the - delegator. - validator_address: - type: string - description: >- - validator_address is the bech32-encoded address of the - validator. - shares: - type: string - description: shares define the delegation shares received. - description: >- - Delegation represents the bond with tokens held by an - account. It is + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of + the serialized - owned by one delegator, and is associated with the voting - power of one + protocol buffer message. This string must contain at + least - validator. - balance: - type: object - properties: - denom: - type: string - amount: - type: string - description: >- - Coin defines a token with a denomination and an amount. + one "/" character. The last segment of the URL's path + must represent + the fully qualified name of the type (as in - NOTE: The amount field is an Int which implements the - custom method + `path/google.protobuf.Duration`). The name should be in + a canonical form - signatures required by gogoproto. - description: >- - DelegationResponse is equivalent to Delegation except that it - contains a + (e.g., leading "." is not accepted). - balance in addition to shares which is more suitable for - client responses. + + In practice, teams usually precompile into the binary + all types that they + + expect it to use in the context of Any. However, for + URLs which use the + + scheme `http`, `https`, or no scheme, one can optionally + set up a type + + server that maps type URLs to message definitions as + follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based + on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in + the official + + protobuf release, and it is not used for type URLs + beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) + might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer + message along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values + in the form + + of utility functions or additional generated methods of the + Any type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + // or ... + if (any.isSameTypeAs(Foo.getDefaultInstance())) { + foo = any.unpack(Foo.getDefaultInstance()); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := anypb.New(foo) + if err != nil { + ... + } + ... + foo := &pb.Foo{} + if err := any.UnmarshalTo(foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by + default use + + 'type.googleapis.com/full.type.name' as the type URL and the + unpack + + methods only use the fully qualified type name after the + last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield + type + + name "y.z". + + + JSON + + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with + an + + additional field `@type` which contains the type URL. + Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom + JSON + + representation, that representation will be embedded adding + a field + + `value` which holds the custom JSON in addition to the + `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + parameters: + - name: body + in: body + required: true + schema: + type: object + properties: + tx_bytes: + type: string + format: byte + description: tx_bytes is the raw transaction. + description: |- + TxDecodeRequest is the request type for the Service.TxDecode + RPC method. + + Since: cosmos-sdk 0.47 + tags: + - Service + /cosmos/tx/v1beta1/decode/amino: + post: + summary: TxDecodeAmino decodes an Amino transaction from encoded bytes to JSON. + description: 'Since: cosmos-sdk 0.47' + operationId: TxDecodeAmino + responses: + '200': + description: A successful response. + schema: + type: object + properties: + amino_json: + type: string description: >- - QueryDelegationResponse is response type for the Query/Delegation + TxDecodeAminoResponse is the response type for the + Service.TxDecodeAmino + RPC method. + + + Since: cosmos-sdk 0.47 default: description: An unexpected error response. schema: @@ -46694,82 +51137,45 @@ paths: "value": "1.212s" } parameters: - - name: validator_addr - description: validator_addr defines the validator address to query for. - in: path - required: true - type: string - - name: delegator_addr - description: delegator_addr defines the delegator address to query for. - in: path + - name: body + in: body required: true - type: string + schema: + type: object + properties: + amino_binary: + type: string + format: byte + description: >- + TxDecodeAminoRequest is the request type for the + Service.TxDecodeAmino + + RPC method. + + + Since: cosmos-sdk 0.47 tags: - - Query - /cosmos/staking/v1beta1/validators/{validator_addr}/delegations/{delegator_addr}/unbonding_delegation: - get: - summary: |- - UnbondingDelegation queries unbonding info for given validator delegator - pair. - operationId: UnbondingDelegation + - Service + /cosmos/tx/v1beta1/encode: + post: + summary: TxEncode encodes the transaction. + description: 'Since: cosmos-sdk 0.47' + operationId: TxEncode responses: '200': description: A successful response. schema: type: object properties: - unbond: - type: object - properties: - delegator_address: - type: string - description: >- - delegator_address is the bech32-encoded address of the - delegator. - validator_address: - type: string - description: >- - validator_address is the bech32-encoded address of the - validator. - entries: - type: array - items: - type: object - properties: - creation_height: - type: string - format: int64 - description: >- - creation_height is the height which the unbonding - took place. - completion_time: - type: string - format: date-time - description: >- - completion_time is the unix time for unbonding - completion. - initial_balance: - type: string - description: >- - initial_balance defines the tokens initially - scheduled to receive at completion. - balance: - type: string - description: balance defines the tokens to receive at completion. - description: >- - UnbondingDelegationEntry defines an unbonding object - with relevant metadata. - description: entries are the unbonding delegation entries. - description: >- - UnbondingDelegation stores all of a single delegator's - unbonding bonds - - for a single validator in an time-ordered list. - description: >- - QueryDelegationResponse is response type for the - Query/UnbondingDelegation + tx_bytes: + type: string + format: byte + description: tx_bytes is the encoded transaction bytes. + description: |- + TxEncodeResponse is the response type for the + Service.TxEncode method. - RPC method. + Since: cosmos-sdk 0.47 default: description: An unexpected error response. schema: @@ -46965,105 +51371,35 @@ paths: "value": "1.212s" } parameters: - - name: validator_addr - description: validator_addr defines the validator address to query for. - in: path - required: true - type: string - - name: delegator_addr - description: delegator_addr defines the delegator address to query for. - in: path + - name: body + in: body required: true - type: string + schema: + $ref: '#/definitions/cosmos.tx.v1beta1.TxEncodeRequest' tags: - - Query - /cosmos/staking/v1beta1/validators/{validator_addr}/unbonding_delegations: - get: - summary: >- - ValidatorUnbondingDelegations queries unbonding delegations of a - validator. - operationId: ValidatorUnbondingDelegations + - Service + /cosmos/tx/v1beta1/encode/amino: + post: + summary: TxEncodeAmino encodes an Amino transaction from JSON to encoded bytes. + description: 'Since: cosmos-sdk 0.47' + operationId: TxEncodeAmino responses: '200': description: A successful response. schema: type: object properties: - unbonding_responses: - type: array - items: - type: object - properties: - delegator_address: - type: string - description: >- - delegator_address is the bech32-encoded address of the - delegator. - validator_address: - type: string - description: >- - validator_address is the bech32-encoded address of the - validator. - entries: - type: array - items: - type: object - properties: - creation_height: - type: string - format: int64 - description: >- - creation_height is the height which the unbonding - took place. - completion_time: - type: string - format: date-time - description: >- - completion_time is the unix time for unbonding - completion. - initial_balance: - type: string - description: >- - initial_balance defines the tokens initially - scheduled to receive at completion. - balance: - type: string - description: >- - balance defines the tokens to receive at - completion. - description: >- - UnbondingDelegationEntry defines an unbonding object - with relevant metadata. - description: entries are the unbonding delegation entries. - description: >- - UnbondingDelegation stores all of a single delegator's - unbonding bonds + amino_binary: + type: string + format: byte + description: >- + TxEncodeAminoResponse is the response type for the + Service.TxEncodeAmino - for a single validator in an time-ordered list. - pagination: - description: pagination defines the pagination in the response. - type: object - properties: - next_key: - type: string - format: byte - description: |- - next_key is the key to be passed to PageRequest.key to - query the next page most efficiently. It will be empty if - there are no more results. - total: - type: string - format: uint64 - title: >- - total is total number of results available if - PageRequest.count_total + RPC method. - was set, its value is undefined otherwise - description: >- - QueryValidatorUnbondingDelegationsResponse is response type for - the - Query/ValidatorUnbondingDelegations RPC method. + Since: cosmos-sdk 0.47 default: description: An unexpected error response. schema: @@ -47259,69 +51595,24 @@ paths: "value": "1.212s" } parameters: - - name: validator_addr - description: validator_addr defines the validator address to query for. - in: path + - name: body + in: body required: true - type: string - - name: pagination.key - description: |- - key is a value returned in PageResponse.next_key to begin - querying the next page most efficiently. Only one of offset or key - should be set. - in: query - required: false - type: string - format: byte - - name: pagination.offset - description: >- - offset is a numeric offset that can be used when key is unavailable. - - It is less efficient than using key. Only one of offset or key - should - - be set. - in: query - required: false - type: string - format: uint64 - - name: pagination.limit - description: >- - limit is the total number of results to be returned in the result - page. - - If left empty it will default to a value to be set by each app. - in: query - required: false - type: string - format: uint64 - - name: pagination.count_total - description: >- - count_total is set to true to indicate that the result set should - include - - a count of the total number of items available for pagination in - UIs. - - count_total is only respected when offset is used. It is ignored - when key + schema: + type: object + properties: + amino_json: + type: string + description: >- + TxEncodeAminoRequest is the request type for the + Service.TxEncodeAmino - is set. - in: query - required: false - type: boolean - - name: pagination.reverse - description: >- - reverse is set to true if results are to be returned in the - descending order. + RPC method. - Since: cosmos-sdk 0.43 - in: query - required: false - type: boolean + Since: cosmos-sdk 0.47 tags: - - Query + - Service /cosmos/tx/v1beta1/simulate: post: summary: Simulate simulates executing a transaction for estimating gas usage. @@ -47383,10 +51674,8 @@ paths: properties: key: type: string - format: byte value: type: string - format: byte index: type: boolean description: >- @@ -48400,10 +52689,8 @@ paths: properties: key: type: string - format: byte value: type: string - format: byte index: type: boolean description: >- @@ -48658,8 +52945,9 @@ paths: TxService.Broadcast RPC method. - BROADCAST_MODE_UNSPECIFIED: zero-value for mode ordering - - BROADCAST_MODE_BLOCK: BROADCAST_MODE_BLOCK defines a tx broadcasting mode where the client waits for - the tx to be committed in a block. + - BROADCAST_MODE_BLOCK: DEPRECATED: use BROADCAST_MODE_SYNC instead, + BROADCAST_MODE_BLOCK is not supported by the SDK from v0.47.x + onwards. - BROADCAST_MODE_SYNC: BROADCAST_MODE_SYNC defines a tx broadcasting mode where the client waits for a CheckTx execution response only. - BROADCAST_MODE_ASYNC: BROADCAST_MODE_ASYNC defines a tx broadcasting mode where the client returns @@ -49155,11 +53443,12 @@ paths: get: summary: >- ABCIQuery defines a query handler that supports ABCI queries directly to + the - the application, bypassing Tendermint completely. The ABCI query must + application, bypassing Tendermint completely. The ABCI query must + contain - contain a valid and supported path, including app, custom, p2p, and - store. + a valid and supported path, including app, custom, p2p, and store. description: 'Since: cosmos-sdk 0.46' operationId: ABCIQuery responses: @@ -49204,24 +53493,20 @@ paths: ProofOp defines an operation used for calculating Merkle root. The data could - be arbitrary format, providing nessecary data for + be arbitrary format, providing necessary data for example neighbouring node hash. Note: This type is a duplicate of the ProofOp proto type - defined in - - Tendermint. + defined in Tendermint. description: >- ProofOps is Merkle proof defined by the list of ProofOps. Note: This type is a duplicate of the ProofOps proto type - defined in - - Tendermint. + defined in Tendermint. height: type: string format: int64 @@ -49229,9 +53514,7 @@ paths: type: string description: >- ABCIQueryResponse defines the response structure for the ABCIQuery - gRPC - - query. + gRPC query. Note: This type is a duplicate of the ResponseQuery proto type @@ -50570,9 +54853,7 @@ paths: field converted to bech32 string. description: >- GetLatestBlockResponse is the response type for the - Query/GetLatestBlock RPC - - method. + Query/GetLatestBlock RPC method. default: description: An unexpected error response. schema: @@ -51886,9 +56167,7 @@ paths: field converted to bech32 string. description: >- GetBlockByHeightResponse is the response type for the - Query/GetBlockByHeight - - RPC method. + Query/GetBlockByHeight RPC method. default: description: An unexpected error response. schema: @@ -52172,9 +56451,218 @@ paths: description: VersionInfo is the type for the GetNodeInfoResponse message. description: >- GetNodeInfoResponse is the response type for the Query/GetNodeInfo - RPC + RPC method. + default: + description: An unexpected error response. + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of + the serialized - method. + protocol buffer message. This string must contain at + least + + one "/" character. The last segment of the URL's path + must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in + a canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary + all types that they + + expect it to use in the context of Any. However, for + URLs which use the + + scheme `http`, `https`, or no scheme, one can optionally + set up a type + + server that maps type URLs to message definitions as + follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based + on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in + the official + + protobuf release, and it is not used for type URLs + beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) + might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer + message along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values + in the form + + of utility functions or additional generated methods of the + Any type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + // or ... + if (any.isSameTypeAs(Foo.getDefaultInstance())) { + foo = any.unpack(Foo.getDefaultInstance()); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := anypb.New(foo) + if err != nil { + ... + } + ... + foo := &pb.Foo{} + if err := any.UnmarshalTo(foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by + default use + + 'type.googleapis.com/full.type.name' as the type URL and the + unpack + + methods only use the fully qualified type name after the + last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield + type + + name "y.z". + + + JSON + + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with + an + + additional field `@type` which contains the type URL. + Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom + JSON + + representation, that representation will be embedded adding + a field + + `value` which holds the custom JSON in addition to the + `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + tags: + - Service + /cosmos/base/tendermint/v1beta1/syncing: + get: + summary: GetSyncing queries node syncing. + operationId: GetSyncing + responses: + '200': + description: A successful response. + schema: + type: object + properties: + syncing: + type: boolean + description: >- + GetSyncingResponse is the response type for the Query/GetSyncing + RPC method. default: description: An unexpected error response. schema: @@ -52316,76 +56804,292 @@ paths: ... } - The pack methods provided by protobuf library will by - default use + The pack methods provided by protobuf library will by + default use + + 'type.googleapis.com/full.type.name' as the type URL and the + unpack + + methods only use the fully qualified type name after the + last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield + type + + name "y.z". + + + JSON + + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with + an + + additional field `@type` which contains the type URL. + Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom + JSON + + representation, that representation will be embedded adding + a field + + `value` which holds the custom JSON in addition to the + `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + tags: + - Service + /cosmos/base/tendermint/v1beta1/validatorsets/latest: + get: + summary: GetLatestValidatorSet queries latest validator-set. + operationId: GetLatestValidatorSet + responses: + '200': + description: A successful response. + schema: + type: object + properties: + block_height: + type: string + format: int64 + validators: + type: array + items: + type: object + properties: + address: + type: string + pub_key: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the + type of the serialized + + protocol buffer message. This string must contain at + least + + one "/" character. The last segment of the URL's + path must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be + in a canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the + binary all types that they + + expect it to use in the context of Any. However, for + URLs which use the + + scheme `http`, `https`, or no scheme, one can + optionally set up a type + + server that maps type URLs to message definitions as + follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results + based on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available + in the official + + protobuf release, and it is not used for type URLs + beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty + scheme) might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the + above specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer + message along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any + values in the form + + of utility functions or additional generated methods of + the Any type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + // or ... + if (any.isSameTypeAs(Foo.getDefaultInstance())) { + foo = any.unpack(Foo.getDefaultInstance()); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := anypb.New(foo) + if err != nil { + ... + } + ... + foo := &pb.Foo{} + if err := any.UnmarshalTo(foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by + default use - 'type.googleapis.com/full.type.name' as the type URL and the - unpack + 'type.googleapis.com/full.type.name' as the type URL and + the unpack - methods only use the fully qualified type name after the - last '/' + methods only use the fully qualified type name after the + last '/' - in the type URL, for example "foo.bar.com/x/y.z" will yield - type + in the type URL, for example "foo.bar.com/x/y.z" will + yield type - name "y.z". + name "y.z". - JSON + JSON - The JSON representation of an `Any` value uses the regular + The JSON representation of an `Any` value uses the + regular - representation of the deserialized, embedded message, with - an + representation of the deserialized, embedded message, + with an - additional field `@type` which contains the type URL. - Example: + additional field `@type` which contains the type URL. + Example: - package google.profile; - message Person { - string first_name = 1; - string last_name = 2; - } + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } - { - "@type": "type.googleapis.com/google.profile.Person", - "firstName": , - "lastName": - } + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } - If the embedded message type is well-known and has a custom - JSON + If the embedded message type is well-known and has a + custom JSON - representation, that representation will be embedded adding - a field + representation, that representation will be embedded + adding a field - `value` which holds the custom JSON in addition to the - `@type` + `value` which holds the custom JSON in addition to the + `@type` - field. Example (for message [google.protobuf.Duration][]): + field. Example (for message + [google.protobuf.Duration][]): - { - "@type": "type.googleapis.com/google.protobuf.Duration", - "value": "1.212s" - } - tags: - - Service - /cosmos/base/tendermint/v1beta1/syncing: - get: - summary: GetSyncing queries node syncing. - operationId: GetSyncing - responses: - '200': - description: A successful response. - schema: - type: object - properties: - syncing: - type: boolean + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + voting_power: + type: string + format: int64 + proposer_priority: + type: string + format: int64 + description: Validator is the type for the validator-set. + pagination: + description: pagination defines an pagination for the response. + type: object + properties: + next_key: + type: string + format: byte + description: |- + next_key is the key to be passed to PageRequest.key to + query the next page most efficiently. It will be empty if + there are no more results. + total: + type: string + format: uint64 + title: >- + total is total number of results available if + PageRequest.count_total + + was set, its value is undefined otherwise description: >- - GetSyncingResponse is the response type for the Query/GetSyncing - RPC method. + GetLatestValidatorSetResponse is the response type for the + Query/GetValidatorSetByHeight RPC method. default: description: An unexpected error response. schema: @@ -52580,12 +57284,69 @@ paths: "@type": "type.googleapis.com/google.protobuf.Duration", "value": "1.212s" } + parameters: + - name: pagination.key + description: |- + key is a value returned in PageResponse.next_key to begin + querying the next page most efficiently. Only one of offset or key + should be set. + in: query + required: false + type: string + format: byte + - name: pagination.offset + description: >- + offset is a numeric offset that can be used when key is unavailable. + + It is less efficient than using key. Only one of offset or key + should + + be set. + in: query + required: false + type: string + format: uint64 + - name: pagination.limit + description: >- + limit is the total number of results to be returned in the result + page. + + If left empty it will default to a value to be set by each app. + in: query + required: false + type: string + format: uint64 + - name: pagination.count_total + description: >- + count_total is set to true to indicate that the result set should + include + + a count of the total number of items available for pagination in + UIs. + + count_total is only respected when offset is used. It is ignored + when key + + is set. + in: query + required: false + type: boolean + - name: pagination.reverse + description: >- + reverse is set to true if results are to be returned in the + descending order. + + + Since: cosmos-sdk 0.43 + in: query + required: false + type: boolean tags: - Service - /cosmos/base/tendermint/v1beta1/validatorsets/latest: + /cosmos/base/tendermint/v1beta1/validatorsets/{height}: get: - summary: GetLatestValidatorSet queries latest validator-set. - operationId: GetLatestValidatorSet + summary: GetValidatorSetByHeight queries validator-set at a given height. + operationId: GetValidatorSetByHeight responses: '200': description: A successful response. @@ -52810,8 +57571,8 @@ paths: PageRequest.count_total was set, its value is undefined otherwise - description: |- - GetLatestValidatorSetResponse is the response type for the + description: >- + GetValidatorSetByHeightResponse is the response type for the Query/GetValidatorSetByHeight RPC method. default: description: An unexpected error response. @@ -53008,6 +57769,11 @@ paths: "value": "1.212s" } parameters: + - name: height + in: path + required: true + type: string + format: int64 - name: pagination.key description: |- key is a value returned in PageResponse.next_key to begin @@ -53066,660 +57832,901 @@ paths: type: boolean tags: - Service - /cosmos/base/tendermint/v1beta1/validatorsets/{height}: - get: - summary: GetValidatorSetByHeight queries validator-set at a given height. - operationId: GetValidatorSetByHeight - responses: - '200': - description: A successful response. - schema: +definitions: + cosmos.base.query.v1beta1.PageRequest: + type: object + properties: + key: + type: string + format: byte + description: |- + key is a value returned in PageResponse.next_key to begin + querying the next page most efficiently. Only one of offset or key + should be set. + offset: + type: string + format: uint64 + description: |- + offset is a numeric offset that can be used when key is unavailable. + It is less efficient than using key. Only one of offset or key should + be set. + limit: + type: string + format: uint64 + description: >- + limit is the total number of results to be returned in the result + page. + + If left empty it will default to a value to be set by each app. + count_total: + type: boolean + description: >- + count_total is set to true to indicate that the result set should + include + + a count of the total number of items available for pagination in UIs. + + count_total is only respected when offset is used. It is ignored when + key + + is set. + reverse: + type: boolean + description: >- + reverse is set to true if results are to be returned in the descending + order. + + + Since: cosmos-sdk 0.43 + description: |- + message SomeRequest { + Foo some_parameter = 1; + PageRequest pagination = 2; + } + title: |- + PageRequest is to be embedded in gRPC request messages for efficient + pagination. Ex: + cosmos.base.query.v1beta1.PageResponse: + type: object + properties: + next_key: + type: string + format: byte + description: |- + next_key is the key to be passed to PageRequest.key to + query the next page most efficiently. It will be empty if + there are no more results. + total: + type: string + format: uint64 + title: |- + total is total number of results available if PageRequest.count_total + was set, its value is undefined otherwise + description: |- + PageResponse is to be embedded in gRPC response messages where the + corresponding request message has used PageRequest. + + message SomeResponse { + repeated Bar results = 1; + PageResponse page = 2; + } + dymensionxyz.dymension.common.RollappPacket: + type: object + properties: + rollapp_id: + type: string + packet: + type: object + properties: + sequence: + type: string + format: uint64 + description: >- + number corresponds to the order of sends and receives, where a + Packet + + with an earlier sequence number must be sent and received before a + Packet + + with a later sequence number. + source_port: + type: string + description: identifies the port on the sending chain. + source_channel: + type: string + description: identifies the channel end on the sending chain. + destination_port: + type: string + description: identifies the port on the receiving chain. + destination_channel: + type: string + description: identifies the channel end on the receiving chain. + data: + type: string + format: byte + title: actual opaque bytes transferred directly to the application module + timeout_height: + title: block height after which the packet times out type: object properties: - block_height: + revision_number: type: string - format: int64 - validators: - type: array - items: - type: object - properties: - address: - type: string - pub_key: - type: object - properties: - type_url: - type: string - description: >- - A URL/resource name that uniquely identifies the - type of the serialized + format: uint64 + title: the revision that the client is currently on + revision_height: + type: string + format: uint64 + title: the height within the given revision + description: >- + Normally the RevisionHeight is incremented at each height while + keeping - protocol buffer message. This string must contain at - least + RevisionNumber the same. However some consensus algorithms may + choose to - one "/" character. The last segment of the URL's - path must represent + reset the height in certain conditions e.g. hard forks, + state-machine - the fully qualified name of the type (as in + breaking changes In these cases, the RevisionNumber is incremented + so that - `path/google.protobuf.Duration`). The name should be - in a canonical form + height continues to be monitonically increasing even as the + RevisionHeight - (e.g., leading "." is not accepted). + gets reset + timeout_timestamp: + type: string + format: uint64 + title: block timestamp (in nanoseconds) after which the packet times out + title: >- + Packet defines a type that carries data across different chains + through IBC + acknowledgement: + type: string + format: byte + status: + type: string + enum: + - PENDING + - FINALIZED + - REVERTED + default: PENDING + ProofHeight: + type: string + format: uint64 + relayer: + type: string + format: byte + type: + type: string + enum: + - ON_RECV + - ON_ACK + - ON_TIMEOUT + - UNDEFINED + default: ON_RECV + error: + type: string + title: stores the result of onAck, onTimeout or onRecv/writeAck + original_transfer_target: + type: string + title: >- + who was the original person who gets the money (recipient of ics20 + transfer) of the packet? + dymensionxyz.dymension.common.RollappPacket.Type: + type: string + enum: + - ON_RECV + - ON_ACK + - ON_TIMEOUT + - UNDEFINED + default: ON_RECV + dymensionxyz.dymension.common.Status: + type: string + enum: + - PENDING + - FINALIZED + - REVERTED + default: PENDING + dymensionxyz.dymension.delayedack.Params: + type: object + properties: + epoch_identifier: + type: string + bridging_fee: + type: string + delete_packets_epoch_limit: + type: integer + format: int32 + description: >- + `delete_packets_epoch_limit` is the hard limit of the number of + finalized rollapp packets + + that will be deleted from the store on every epoch end. + + As deleting finalized rollapp packets is meant to keep the store from + growing, + + it is more of a "nice to have" rather than a "must have" feature, + + this is a way to limit the time it takes to do so, + + even if it means potentially causing the store to temporarily grow by + piling up packets + + that weren't deleted but rather "postponed", to subsequent epochs. + description: Params defines the parameters for the module. + dymensionxyz.dymension.delayedack.QueryParamsResponse: + type: object + properties: + params: + description: params holds all the parameters of this module. + type: object + properties: + epoch_identifier: + type: string + bridging_fee: + type: string + delete_packets_epoch_limit: + type: integer + format: int32 + description: >- + `delete_packets_epoch_limit` is the hard limit of the number of + finalized rollapp packets + + that will be deleted from the store on every epoch end. + + As deleting finalized rollapp packets is meant to keep the store + from growing, + + it is more of a "nice to have" rather than a "must have" feature, + + this is a way to limit the time it takes to do so, + + even if it means potentially causing the store to temporarily grow + by piling up packets + + that weren't deleted but rather "postponed", to subsequent epochs. + description: QueryParamsResponse is response type for the Query/Params RPC method. + dymensionxyz.dymension.delayedack.QueryRollappPacketListResponse: + type: object + properties: + rollappPackets: + type: array + items: + type: object + properties: + rollapp_id: + type: string + packet: + type: object + properties: + sequence: + type: string + format: uint64 + description: >- + number corresponds to the order of sends and receives, where + a Packet + + with an earlier sequence number must be sent and received + before a Packet + + with a later sequence number. + source_port: + type: string + description: identifies the port on the sending chain. + source_channel: + type: string + description: identifies the channel end on the sending chain. + destination_port: + type: string + description: identifies the port on the receiving chain. + destination_channel: + type: string + description: identifies the channel end on the receiving chain. + data: + type: string + format: byte + title: >- + actual opaque bytes transferred directly to the application + module + timeout_height: + title: block height after which the packet times out + type: object + properties: + revision_number: + type: string + format: uint64 + title: the revision that the client is currently on + revision_height: + type: string + format: uint64 + title: the height within the given revision + description: >- + Normally the RevisionHeight is incremented at each height + while keeping + + RevisionNumber the same. However some consensus algorithms + may choose to + + reset the height in certain conditions e.g. hard forks, + state-machine + breaking changes In these cases, the RevisionNumber is + incremented so that - In practice, teams usually precompile into the - binary all types that they + height continues to be monitonically increasing even as the + RevisionHeight - expect it to use in the context of Any. However, for - URLs which use the + gets reset + timeout_timestamp: + type: string + format: uint64 + title: >- + block timestamp (in nanoseconds) after which the packet + times out + title: >- + Packet defines a type that carries data across different chains + through IBC + acknowledgement: + type: string + format: byte + status: + type: string + enum: + - PENDING + - FINALIZED + - REVERTED + default: PENDING + ProofHeight: + type: string + format: uint64 + relayer: + type: string + format: byte + type: + type: string + enum: + - ON_RECV + - ON_ACK + - ON_TIMEOUT + - UNDEFINED + default: ON_RECV + error: + type: string + title: stores the result of onAck, onTimeout or onRecv/writeAck + original_transfer_target: + type: string + title: >- + who was the original person who gets the money (recipient of + ics20 transfer) of the packet? + pagination: + type: object + properties: + next_key: + type: string + format: byte + description: |- + next_key is the key to be passed to PageRequest.key to + query the next page most efficiently. It will be empty if + there are no more results. + total: + type: string + format: uint64 + title: >- + total is total number of results available if + PageRequest.count_total - scheme `http`, `https`, or no scheme, one can - optionally set up a type + was set, its value is undefined otherwise + description: |- + PageResponse is to be embedded in gRPC response messages where the + corresponding request message has used PageRequest. - server that maps type URLs to message definitions as - follows: + message SomeResponse { + repeated Bar results = 1; + PageResponse page = 2; + } + google.protobuf.Any: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of the + serialized + protocol buffer message. This string must contain at least - * If no scheme is provided, `https` is assumed. + one "/" character. The last segment of the URL's path must represent - * An HTTP GET on the URL must yield a - [google.protobuf.Type][] - value in binary format, or produce an error. - * Applications are allowed to cache lookup results - based on the - URL, or have them precompiled into a binary to avoid any - lookup. Therefore, binary compatibility needs to be preserved - on changes to types. (Use versioned type names to manage - breaking changes.) + the fully qualified name of the type (as in - Note: this functionality is not currently available - in the official + `path/google.protobuf.Duration`). The name should be in a canonical + form - protobuf release, and it is not used for type URLs - beginning with + (e.g., leading "." is not accepted). - type.googleapis.com. + In practice, teams usually precompile into the binary all types that + they - Schemes other than `http`, `https` (or the empty - scheme) might be + expect it to use in the context of Any. However, for URLs which use + the - used with implementation specific semantics. - value: - type: string - format: byte - description: >- - Must be a valid serialized protocol buffer of the - above specified type. - description: >- - `Any` contains an arbitrary serialized protocol buffer - message along with a + scheme `http`, `https`, or no scheme, one can optionally set up a type - URL that describes the type of the serialized message. + server that maps type URLs to message definitions as follows: - Protobuf library provides support to pack/unpack Any - values in the form + * If no scheme is provided, `https` is assumed. - of utility functions or additional generated methods of - the Any type. + * An HTTP GET on the URL must yield a [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + Note: this functionality is not currently available in the official - Example 1: Pack and unpack a message in C++. + protobuf release, and it is not used for type URLs beginning with - Foo foo = ...; - Any any; - any.PackFrom(foo); - ... - if (any.UnpackTo(&foo)) { - ... - } + type.googleapis.com. - Example 2: Pack and unpack a message in Java. - Foo foo = ...; - Any any = Any.pack(foo); - ... - if (any.is(Foo.class)) { - foo = any.unpack(Foo.class); - } - // or ... - if (any.isSameTypeAs(Foo.getDefaultInstance())) { - foo = any.unpack(Foo.getDefaultInstance()); - } + Schemes other than `http`, `https` (or the empty scheme) might be - Example 3: Pack and unpack a message in Python. + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above specified + type. + description: >- + `Any` contains an arbitrary serialized protocol buffer message along with + a - foo = Foo(...) - any = Any() - any.Pack(foo) - ... - if any.Is(Foo.DESCRIPTOR): - any.Unpack(foo) - ... + URL that describes the type of the serialized message. - Example 4: Pack and unpack a message in Go - foo := &pb.Foo{...} - any, err := anypb.New(foo) - if err != nil { - ... - } - ... - foo := &pb.Foo{} - if err := any.UnmarshalTo(foo); err != nil { - ... - } + Protobuf library provides support to pack/unpack Any values in the form - The pack methods provided by protobuf library will by - default use + of utility functions or additional generated methods of the Any type. - 'type.googleapis.com/full.type.name' as the type URL and - the unpack - methods only use the fully qualified type name after the - last '/' + Example 1: Pack and unpack a message in C++. - in the type URL, for example "foo.bar.com/x/y.z" will - yield type + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } - name "y.z". + Example 2: Pack and unpack a message in Java. + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + // or ... + if (any.isSameTypeAs(Foo.getDefaultInstance())) { + foo = any.unpack(Foo.getDefaultInstance()); + } - JSON + Example 3: Pack and unpack a message in Python. + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... - The JSON representation of an `Any` value uses the - regular + Example 4: Pack and unpack a message in Go - representation of the deserialized, embedded message, - with an + foo := &pb.Foo{...} + any, err := anypb.New(foo) + if err != nil { + ... + } + ... + foo := &pb.Foo{} + if err := any.UnmarshalTo(foo); err != nil { + ... + } - additional field `@type` which contains the type URL. - Example: + The pack methods provided by protobuf library will by default use - package google.profile; - message Person { - string first_name = 1; - string last_name = 2; - } + 'type.googleapis.com/full.type.name' as the type URL and the unpack - { - "@type": "type.googleapis.com/google.profile.Person", - "firstName": , - "lastName": - } + methods only use the fully qualified type name after the last '/' - If the embedded message type is well-known and has a - custom JSON + in the type URL, for example "foo.bar.com/x/y.z" will yield type - representation, that representation will be embedded - adding a field + name "y.z". - `value` which holds the custom JSON in addition to the - `@type` - field. Example (for message - [google.protobuf.Duration][]): + JSON - { - "@type": "type.googleapis.com/google.protobuf.Duration", - "value": "1.212s" - } - voting_power: - type: string - format: int64 - proposer_priority: - type: string - format: int64 - description: Validator is the type for the validator-set. - pagination: - description: pagination defines an pagination for the response. - type: object - properties: - next_key: - type: string - format: byte - description: |- - next_key is the key to be passed to PageRequest.key to - query the next page most efficiently. It will be empty if - there are no more results. - total: - type: string - format: uint64 - title: >- - total is total number of results available if - PageRequest.count_total - was set, its value is undefined otherwise - description: |- - GetValidatorSetByHeightResponse is the response type for the - Query/GetValidatorSetByHeight RPC method. - default: - description: An unexpected error response. - schema: - type: object - properties: - error: - type: string - code: - type: integer - format: int32 - message: - type: string - details: - type: array - items: - type: object - properties: - type_url: - type: string - description: >- - A URL/resource name that uniquely identifies the type of - the serialized + The JSON representation of an `Any` value uses the regular - protocol buffer message. This string must contain at - least + representation of the deserialized, embedded message, with an - one "/" character. The last segment of the URL's path - must represent + additional field `@type` which contains the type URL. Example: - the fully qualified name of the type (as in + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } - `path/google.protobuf.Duration`). The name should be in - a canonical form + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } - (e.g., leading "." is not accepted). + If the embedded message type is well-known and has a custom JSON + representation, that representation will be embedded adding a field - In practice, teams usually precompile into the binary - all types that they + `value` which holds the custom JSON in addition to the `@type` - expect it to use in the context of Any. However, for - URLs which use the + field. Example (for message [google.protobuf.Duration][]): - scheme `http`, `https`, or no scheme, one can optionally - set up a type + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + grpc.gateway.runtime.Error: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of the + serialized - server that maps type URLs to message definitions as - follows: + protocol buffer message. This string must contain at least + one "/" character. The last segment of the URL's path must + represent - * If no scheme is provided, `https` is assumed. + the fully qualified name of the type (as in - * An HTTP GET on the URL must yield a - [google.protobuf.Type][] - value in binary format, or produce an error. - * Applications are allowed to cache lookup results based - on the - URL, or have them precompiled into a binary to avoid any - lookup. Therefore, binary compatibility needs to be preserved - on changes to types. (Use versioned type names to manage - breaking changes.) + `path/google.protobuf.Duration`). The name should be in a + canonical form - Note: this functionality is not currently available in - the official + (e.g., leading "." is not accepted). - protobuf release, and it is not used for type URLs - beginning with - type.googleapis.com. + In practice, teams usually precompile into the binary all types + that they + expect it to use in the context of Any. However, for URLs which + use the - Schemes other than `http`, `https` (or the empty scheme) - might be + scheme `http`, `https`, or no scheme, one can optionally set up + a type - used with implementation specific semantics. - value: - type: string - format: byte - description: >- - Must be a valid serialized protocol buffer of the above - specified type. - description: >- - `Any` contains an arbitrary serialized protocol buffer - message along with a + server that maps type URLs to message definitions as follows: - URL that describes the type of the serialized message. + * If no scheme is provided, `https` is assumed. - Protobuf library provides support to pack/unpack Any values - in the form + * An HTTP GET on the URL must yield a [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) - of utility functions or additional generated methods of the - Any type. + Note: this functionality is not currently available in the + official + protobuf release, and it is not used for type URLs beginning + with - Example 1: Pack and unpack a message in C++. + type.googleapis.com. - Foo foo = ...; - Any any; - any.PackFrom(foo); - ... - if (any.UnpackTo(&foo)) { - ... - } - Example 2: Pack and unpack a message in Java. + Schemes other than `http`, `https` (or the empty scheme) might + be - Foo foo = ...; - Any any = Any.pack(foo); - ... - if (any.is(Foo.class)) { - foo = any.unpack(Foo.class); - } - // or ... - if (any.isSameTypeAs(Foo.getDefaultInstance())) { - foo = any.unpack(Foo.getDefaultInstance()); - } + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer message along + with a - Example 3: Pack and unpack a message in Python. + URL that describes the type of the serialized message. - foo = Foo(...) - any = Any() - any.Pack(foo) - ... - if any.Is(Foo.DESCRIPTOR): - any.Unpack(foo) - ... - Example 4: Pack and unpack a message in Go + Protobuf library provides support to pack/unpack Any values in the + form - foo := &pb.Foo{...} - any, err := anypb.New(foo) - if err != nil { - ... - } - ... - foo := &pb.Foo{} - if err := any.UnmarshalTo(foo); err != nil { - ... - } + of utility functions or additional generated methods of the Any + type. - The pack methods provided by protobuf library will by - default use - 'type.googleapis.com/full.type.name' as the type URL and the - unpack + Example 1: Pack and unpack a message in C++. - methods only use the fully qualified type name after the - last '/' + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } - in the type URL, for example "foo.bar.com/x/y.z" will yield - type + Example 2: Pack and unpack a message in Java. - name "y.z". + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + // or ... + if (any.isSameTypeAs(Foo.getDefaultInstance())) { + foo = any.unpack(Foo.getDefaultInstance()); + } + Example 3: Pack and unpack a message in Python. - JSON + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + Example 4: Pack and unpack a message in Go - The JSON representation of an `Any` value uses the regular + foo := &pb.Foo{...} + any, err := anypb.New(foo) + if err != nil { + ... + } + ... + foo := &pb.Foo{} + if err := any.UnmarshalTo(foo); err != nil { + ... + } - representation of the deserialized, embedded message, with - an + The pack methods provided by protobuf library will by default use - additional field `@type` which contains the type URL. - Example: + 'type.googleapis.com/full.type.name' as the type URL and the unpack - package google.profile; - message Person { - string first_name = 1; - string last_name = 2; - } + methods only use the fully qualified type name after the last '/' - { - "@type": "type.googleapis.com/google.profile.Person", - "firstName": , - "lastName": - } + in the type URL, for example "foo.bar.com/x/y.z" will yield type - If the embedded message type is well-known and has a custom - JSON + name "y.z". - representation, that representation will be embedded adding - a field - `value` which holds the custom JSON in addition to the - `@type` + JSON - field. Example (for message [google.protobuf.Duration][]): - { - "@type": "type.googleapis.com/google.protobuf.Duration", - "value": "1.212s" - } - parameters: - - name: height - in: path - required: true - type: string - format: int64 - - name: pagination.key - description: |- - key is a value returned in PageResponse.next_key to begin - querying the next page most efficiently. Only one of offset or key - should be set. - in: query - required: false - type: string - format: byte - - name: pagination.offset - description: >- - offset is a numeric offset that can be used when key is unavailable. + The JSON representation of an `Any` value uses the regular - It is less efficient than using key. Only one of offset or key - should + representation of the deserialized, embedded message, with an - be set. - in: query - required: false - type: string - format: uint64 - - name: pagination.limit - description: >- - limit is the total number of results to be returned in the result - page. + additional field `@type` which contains the type URL. Example: - If left empty it will default to a value to be set by each app. - in: query - required: false - type: string - format: uint64 - - name: pagination.count_total - description: >- - count_total is set to true to indicate that the result set should - include + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } - a count of the total number of items available for pagination in - UIs. + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } - count_total is only respected when offset is used. It is ignored - when key + If the embedded message type is well-known and has a custom JSON - is set. - in: query - required: false - type: boolean - - name: pagination.reverse - description: >- - reverse is set to true if results are to be returned in the - descending order. + representation, that representation will be embedded adding a field + `value` which holds the custom JSON in addition to the `@type` - Since: cosmos-sdk 0.43 - in: query - required: false - type: boolean - tags: - - Service -definitions: - cosmos.base.query.v1beta1.PageRequest: + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + ibc.core.channel.v1.Packet: type: object properties: - key: - type: string - format: byte - description: |- - key is a value returned in PageResponse.next_key to begin - querying the next page most efficiently. Only one of offset or key - should be set. - offset: - type: string - format: uint64 - description: |- - offset is a numeric offset that can be used when key is unavailable. - It is less efficient than using key. Only one of offset or key should - be set. - limit: + sequence: type: string format: uint64 description: >- - limit is the total number of results to be returned in the result - page. + number corresponds to the order of sends and receives, where a Packet - If left empty it will default to a value to be set by each app. - count_total: - type: boolean + with an earlier sequence number must be sent and received before a + Packet + + with a later sequence number. + source_port: + type: string + description: identifies the port on the sending chain. + source_channel: + type: string + description: identifies the channel end on the sending chain. + destination_port: + type: string + description: identifies the port on the receiving chain. + destination_channel: + type: string + description: identifies the channel end on the receiving chain. + data: + type: string + format: byte + title: actual opaque bytes transferred directly to the application module + timeout_height: + title: block height after which the packet times out + type: object + properties: + revision_number: + type: string + format: uint64 + title: the revision that the client is currently on + revision_height: + type: string + format: uint64 + title: the height within the given revision description: >- - count_total is set to true to indicate that the result set should - include + Normally the RevisionHeight is incremented at each height while + keeping - a count of the total number of items available for pagination in UIs. + RevisionNumber the same. However some consensus algorithms may choose + to - count_total is only respected when offset is used. It is ignored when - key + reset the height in certain conditions e.g. hard forks, state-machine - is set. - reverse: - type: boolean - description: >- - reverse is set to true if results are to be returned in the descending - order. + breaking changes In these cases, the RevisionNumber is incremented so + that + height continues to be monitonically increasing even as the + RevisionHeight - Since: cosmos-sdk 0.43 - description: |- - message SomeRequest { - Foo some_parameter = 1; - PageRequest pagination = 2; - } - title: |- - PageRequest is to be embedded in gRPC request messages for efficient - pagination. Ex: - cosmos.base.query.v1beta1.PageResponse: + gets reset + timeout_timestamp: + type: string + format: uint64 + title: block timestamp (in nanoseconds) after which the packet times out + title: >- + Packet defines a type that carries data across different chains through + IBC + ibc.core.client.v1.Height: type: object properties: - next_key: + revision_number: type: string - format: byte - description: |- - next_key is the key to be passed to PageRequest.key to - query the next page most efficiently. It will be empty if - there are no more results. - total: + format: uint64 + title: the revision that the client is currently on + revision_height: type: string format: uint64 - title: |- - total is total number of results available if PageRequest.count_total - was set, its value is undefined otherwise + title: the height within the given revision description: |- - PageResponse is to be embedded in gRPC response messages where the - corresponding request message has used PageRequest. + Normally the RevisionHeight is incremented at each height while keeping + RevisionNumber the same. However some consensus algorithms may choose to + reset the height in certain conditions e.g. hard forks, state-machine + breaking changes In these cases, the RevisionNumber is incremented so that + height continues to be monitonically increasing even as the RevisionHeight + gets reset + title: >- + Height is a monotonically increasing data type - message SomeResponse { - repeated Bar results = 1; - PageResponse page = 2; - } - dymensionxyz.dymension.common.RollappPacket: + that can be compared against another Height for the purposes of updating + and + + freezing clients + cosmos.base.v1beta1.Coin: type: object properties: - rollapp_id: + denom: type: string - packet: - type: object - properties: - sequence: - type: string - format: uint64 - description: >- - number corresponds to the order of sends and receives, where a - Packet - - with an earlier sequence number must be sent and received before a - Packet - - with a later sequence number. - source_port: - type: string - description: identifies the port on the sending chain. - source_channel: - type: string - description: identifies the channel end on the sending chain. - destination_port: - type: string - description: identifies the port on the receiving chain. - destination_channel: - type: string - description: identifies the channel end on the receiving chain. - data: - type: string - format: byte - title: actual opaque bytes transferred directly to the application module - timeout_height: - title: block height after which the packet times out - type: object - properties: - revision_number: - type: string - format: uint64 - title: the revision that the client is currently on - revision_height: - type: string - format: uint64 - title: the height within the given revision - description: >- - Normally the RevisionHeight is incremented at each height while - keeping - - RevisionNumber the same. However some consensus algorithms may - choose to + amount: + type: string + description: |- + Coin defines a token with a denomination and an amount. - reset the height in certain conditions e.g. hard forks, - state-machine + NOTE: The amount field is an Int which implements the custom method + signatures required by gogoproto. + dymensionxyz.dymension.eibc.DemandOrder: + type: object + properties: + id: + type: string + title: >- + id is a hash of the form generated by GetRollappPacketKey, - breaking changes In these cases, the RevisionNumber is incremented - so that + e.g + status/rollappid/packetProofHeight/packetDestinationChannel-PacketSequence + which guarantees uniqueness + tracking_packet_key: + type: string + description: |- + tracking_packet_key is the key of the packet that is being tracked. + This key can change depends on the packet status. + price: + type: array + items: + type: object + properties: + denom: + type: string + amount: + type: string + description: |- + Coin defines a token with a denomination and an amount. - height continues to be monitonically increasing even as the - RevisionHeight + NOTE: The amount field is an Int which implements the custom method + signatures required by gogoproto. + title: >- + price is the amount that the fulfiller sends to original eibc transfer + recipient + fee: + type: array + items: + type: object + properties: + denom: + type: string + amount: + type: string + description: |- + Coin defines a token with a denomination and an amount. - gets reset - timeout_timestamp: - type: string - format: uint64 - title: block timestamp (in nanoseconds) after which the packet times out + NOTE: The amount field is an Int which implements the custom method + signatures required by gogoproto. title: >- - Packet defines a type that carries data across different chains - through IBC - acknowledgement: + fee is the effective profit made by the fulfiller because they pay + price and receive fee + price + recipient: type: string - format: byte - status: + tracking_packet_status: type: string enum: - PENDING - FINALIZED - REVERTED default: PENDING - ProofHeight: - type: string - format: uint64 - relayer: + rollapp_id: type: string - format: byte type: type: string enum: @@ -53728,145 +58735,102 @@ definitions: - ON_TIMEOUT - UNDEFINED default: ON_RECV - error: - type: string - title: stores the result of onAck, onTimeout or onRecv/writeAck - original_transfer_target: + fulfiller_address: type: string - title: >- - who was the original person who gets the money (recipient of ics20 - transfer) of the packet? - dymensionxyz.dymension.common.RollappPacket.Type: + description: >- + fulfiller_address is the bech32-encoded address of the account which + fulfilled the order. + dymensionxyz.dymension.eibc.FulfillmentState: type: string enum: - - ON_RECV - - ON_ACK - - ON_TIMEOUT - UNDEFINED - default: ON_RECV - dymensionxyz.dymension.common.Status: - type: string - enum: - - PENDING - - FINALIZED - - REVERTED - default: PENDING - dymensionxyz.dymension.delayedack.Params: + - FULFILLED + - UNFULFILLED + default: UNDEFINED + dymensionxyz.dymension.eibc.Params: type: object properties: epoch_identifier: type: string - bridging_fee: + timeout_fee: + type: string + errack_fee: type: string description: Params defines the parameters for the module. - dymensionxyz.dymension.delayedack.QueryParamsResponse: - type: object - properties: - params: - description: params holds all the parameters of this module. - type: object - properties: - epoch_identifier: - type: string - bridging_fee: - type: string - description: QueryParamsResponse is response type for the Query/Params RPC method. - dymensionxyz.dymension.delayedack.QueryRollappPacketListResponse: + dymensionxyz.dymension.eibc.QueryDemandOrdersByStatusResponse: type: object properties: - rollappPackets: + demand_orders: type: array items: type: object properties: - rollapp_id: + id: type: string - packet: - type: object - properties: - sequence: - type: string - format: uint64 - description: >- - number corresponds to the order of sends and receives, where - a Packet + title: >- + id is a hash of the form generated by GetRollappPacketKey, - with an earlier sequence number must be sent and received - before a Packet + e.g + status/rollappid/packetProofHeight/packetDestinationChannel-PacketSequence + which guarantees uniqueness + tracking_packet_key: + type: string + description: >- + tracking_packet_key is the key of the packet that is being + tracked. - with a later sequence number. - source_port: - type: string - description: identifies the port on the sending chain. - source_channel: - type: string - description: identifies the channel end on the sending chain. - destination_port: - type: string - description: identifies the port on the receiving chain. - destination_channel: - type: string - description: identifies the channel end on the receiving chain. - data: - type: string - format: byte - title: >- - actual opaque bytes transferred directly to the application - module - timeout_height: - title: block height after which the packet times out - type: object - properties: - revision_number: - type: string - format: uint64 - title: the revision that the client is currently on - revision_height: - type: string - format: uint64 - title: the height within the given revision - description: >- - Normally the RevisionHeight is incremented at each height - while keeping + This key can change depends on the packet status. + price: + type: array + items: + type: object + properties: + denom: + type: string + amount: + type: string + description: >- + Coin defines a token with a denomination and an amount. - RevisionNumber the same. However some consensus algorithms - may choose to - reset the height in certain conditions e.g. hard forks, - state-machine + NOTE: The amount field is an Int which implements the custom + method - breaking changes In these cases, the RevisionNumber is - incremented so that + signatures required by gogoproto. + title: >- + price is the amount that the fulfiller sends to original eibc + transfer recipient + fee: + type: array + items: + type: object + properties: + denom: + type: string + amount: + type: string + description: >- + Coin defines a token with a denomination and an amount. - height continues to be monitonically increasing even as the - RevisionHeight - gets reset - timeout_timestamp: - type: string - format: uint64 - title: >- - block timestamp (in nanoseconds) after which the packet - times out + NOTE: The amount field is an Int which implements the custom + method + + signatures required by gogoproto. title: >- - Packet defines a type that carries data across different chains - through IBC - acknowledgement: + fee is the effective profit made by the fulfiller because they + pay price and receive fee + price + recipient: type: string - format: byte - status: + tracking_packet_status: type: string enum: - PENDING - FINALIZED - REVERTED default: PENDING - ProofHeight: - type: string - format: uint64 - relayer: + rollapp_id: type: string - format: byte type: type: string enum: @@ -53875,756 +58839,1863 @@ definitions: - ON_TIMEOUT - UNDEFINED default: ON_RECV - error: - type: string - title: stores the result of onAck, onTimeout or onRecv/writeAck - original_transfer_target: + fulfiller_address: type: string - title: >- - who was the original person who gets the money (recipient of - ics20 transfer) of the packet? - pagination: + description: >- + fulfiller_address is the bech32-encoded address of the account + which fulfilled the order. + title: A list of demand orders with the given status + description: >- + QueryDemandOrdersByStatusResponse is the response type for the + Query/GetDemandOrdersByStatus RPC method. + dymensionxyz.dymension.eibc.QueryGetDemandOrderResponse: + type: object + properties: + demand_order: + title: demand order with the given id type: object properties: - next_key: - type: string - format: byte - description: |- - next_key is the key to be passed to PageRequest.key to - query the next page most efficiently. It will be empty if - there are no more results. - total: + id: type: string - format: uint64 title: >- - total is total number of results available if - PageRequest.count_total - - was set, its value is undefined otherwise - description: |- - PageResponse is to be embedded in gRPC response messages where the - corresponding request message has used PageRequest. - - message SomeResponse { - repeated Bar results = 1; - PageResponse page = 2; - } - google.protobuf.Any: - type: object - properties: - type_url: - type: string - description: >- - A URL/resource name that uniquely identifies the type of the - serialized - - protocol buffer message. This string must contain at least - - one "/" character. The last segment of the URL's path must represent - - the fully qualified name of the type (as in - - `path/google.protobuf.Duration`). The name should be in a canonical - form - - (e.g., leading "." is not accepted). - - - In practice, teams usually precompile into the binary all types that - they - - expect it to use in the context of Any. However, for URLs which use - the - - scheme `http`, `https`, or no scheme, one can optionally set up a type - - server that maps type URLs to message definitions as follows: - + id is a hash of the form generated by GetRollappPacketKey, - * If no scheme is provided, `https` is assumed. + e.g + status/rollappid/packetProofHeight/packetDestinationChannel-PacketSequence + which guarantees uniqueness + tracking_packet_key: + type: string + description: >- + tracking_packet_key is the key of the packet that is being + tracked. - * An HTTP GET on the URL must yield a [google.protobuf.Type][] - value in binary format, or produce an error. - * Applications are allowed to cache lookup results based on the - URL, or have them precompiled into a binary to avoid any - lookup. Therefore, binary compatibility needs to be preserved - on changes to types. (Use versioned type names to manage - breaking changes.) + This key can change depends on the packet status. + price: + type: array + items: + type: object + properties: + denom: + type: string + amount: + type: string + description: >- + Coin defines a token with a denomination and an amount. - Note: this functionality is not currently available in the official - protobuf release, and it is not used for type URLs beginning with + NOTE: The amount field is an Int which implements the custom + method - type.googleapis.com. + signatures required by gogoproto. + title: >- + price is the amount that the fulfiller sends to original eibc + transfer recipient + fee: + type: array + items: + type: object + properties: + denom: + type: string + amount: + type: string + description: >- + Coin defines a token with a denomination and an amount. - Schemes other than `http`, `https` (or the empty scheme) might be + NOTE: The amount field is an Int which implements the custom + method - used with implementation specific semantics. - value: + signatures required by gogoproto. + title: >- + fee is the effective profit made by the fulfiller because they pay + price and receive fee + price + recipient: + type: string + tracking_packet_status: + type: string + enum: + - PENDING + - FINALIZED + - REVERTED + default: PENDING + rollapp_id: + type: string + type: + type: string + enum: + - ON_RECV + - ON_ACK + - ON_TIMEOUT + - UNDEFINED + default: ON_RECV + fulfiller_address: + type: string + description: >- + fulfiller_address is the bech32-encoded address of the account + which fulfilled the order. + description: >- + QueryGetDemandOrderResponse is the response type for the + Query/GetDemandOrder RPC method. + dymensionxyz.dymension.eibc.QueryParamsResponse: + type: object + properties: + params: + description: params holds all the parameters of this module. + type: object + properties: + epoch_identifier: + type: string + timeout_fee: + type: string + errack_fee: + type: string + description: QueryParamsResponse is response type for the Query/Params RPC method. + dymensionxyz.dymension.dymns.AliasesOfChainId: + type: object + properties: + chain_id: type: string - format: byte + description: chain_id which owned the aliases. + aliases: + type: array + items: + type: string + title: |- + aliases is a set of aliases of the chain id for UX improvement, + like we can do my-name@cosmos instead of my-name@cosmoshub-4 + description: AliasesOfChainId defines the multiple-aliases of a chain id. + dymensionxyz.dymension.dymns.AssetType: + type: string + enum: + - AT_UNKNOWN + - AT_DYM_NAME + - AT_ALIAS + default: AT_UNKNOWN + description: AssetType present type of the asset of the Buy/Sell order. + dymensionxyz.dymension.dymns.BuyOrder: + type: object + properties: + id: + type: string + description: id is the unique identifier of the order. Generated by the module. + asset_id: + type: string + description: asset_id of the Dym-Name/Alias willing to buy. + asset_type: + title: asset_type is type of the asset of the order, is Dym-Name/Alias + type: string + enum: + - AT_UNKNOWN + - AT_DYM_NAME + - AT_ALIAS + default: AT_UNKNOWN + description: AssetType present type of the asset of the Buy/Sell order. + params: + type: array + items: + type: string description: >- - Must be a valid serialized protocol buffer of the above specified - type. - description: >- - `Any` contains an arbitrary serialized protocol buffer message along with - a - - URL that describes the type of the serialized message. - - - Protobuf library provides support to pack/unpack Any values in the form - - of utility functions or additional generated methods of the Any type. - - - Example 1: Pack and unpack a message in C++. + params is the list of parameters of the Buy-Order. - Foo foo = ...; - Any any; - any.PackFrom(foo); - ... - if (any.UnpackTo(&foo)) { - ... - } - - Example 2: Pack and unpack a message in Java. - - Foo foo = ...; - Any any = Any.pack(foo); - ... - if (any.is(Foo.class)) { - foo = any.unpack(Foo.class); - } - // or ... - if (any.isSameTypeAs(Foo.getDefaultInstance())) { - foo = any.unpack(Foo.getDefaultInstance()); - } - - Example 3: Pack and unpack a message in Python. - - foo = Foo(...) - any = Any() - any.Pack(foo) - ... - if any.Is(Foo.DESCRIPTOR): - any.Unpack(foo) - ... - - Example 4: Pack and unpack a message in Go + It is empty for asset type Dym-Name. - foo := &pb.Foo{...} - any, err := anypb.New(foo) - if err != nil { - ... - } - ... - foo := &pb.Foo{} - if err := any.UnmarshalTo(foo); err != nil { - ... - } - - The pack methods provided by protobuf library will by default use - - 'type.googleapis.com/full.type.name' as the type URL and the unpack - - methods only use the fully qualified type name after the last '/' + It has one element for asset type Alias, which is the rollapp_id to + assigned for. + buyer: + type: string + description: buyer is bech32 address of the account which placed the order. + offer_price: + description: >- + offer_price is the amount of coins that buyer willing to pay for the + asset. - in the type URL, for example "foo.bar.com/x/y.z" will yield type + This amount is deposited to the module account upon placing the offer. + type: object + properties: + denom: + type: string + amount: + type: string + counterparty_offer_price: + description: >- + counterparty_offer_price is the price that the Dym-Name/Alias owner is + willing to sell for. - name "y.z". + This is used for counterparty price negotiation and for information + only. + The transaction can only be executed when the owner accepts the offer + with exact offer_price. + type: object + properties: + denom: + type: string + amount: + type: string + description: >- + BuyOrder defines an offer to buy a Dym-Name/Alias, placed by buyer. - JSON + Buyer will need to deposit the offer amount to the module account. + When the owner of the Dym-Name/Alias accepts the offer, deposited amount + will be transferred to the owner. - The JSON representation of an `Any` value uses the regular + When the buyer cancels the offer, deposited amount will be refunded to the + buyer. + dymensionxyz.dymension.dymns.ChainsParams: + type: object + properties: + aliases_of_chain_ids: + type: array + items: + type: object + properties: + chain_id: + type: string + description: chain_id which owned the aliases. + aliases: + type: array + items: + type: string + title: |- + aliases is a set of aliases of the chain id for UX improvement, + like we can do my-name@cosmos instead of my-name@cosmoshub-4 + description: AliasesOfChainId defines the multiple-aliases of a chain id. + description: >- + aliases_of_chain_ids is set of chain-ids and their corresponding + aliases, - representation of the deserialized, embedded message, with an + used for UX improvement like we can do my-name@cosmos instead of + my-name@cosmoshub-4. - additional field `@type` which contains the type URL. Example: - package google.profile; - message Person { - string first_name = 1; - string last_name = 2; - } + This list is prioritized over Roll-App aliases - { - "@type": "type.googleapis.com/google.profile.Person", - "firstName": , - "lastName": - } + the reason is to allow the community able to have control to fixes the + potential problems with the aliases. + description: ChainsParams defines setting for prioritized aliases mapping. + dymensionxyz.dymension.dymns.DymName: + type: object + properties: + name: + type: string + description: name is the human-readable name of the Dym-Name. + owner: + type: string + description: >- + owner is the account address that owns the Dym-Name. Owner has + permission to transfer ownership. + controller: + type: string + description: >- + controller is the account address that has permission update + configuration for the Dym-Name. - If the embedded message type is well-known and has a custom JSON + Default is the owner. Able to transfer control to another account by + the owner. - representation, that representation will be embedded adding a field + Users can set Dym-Name owned by Cold-Wallet and controlled by + Hot-Wallet. + expire_at: + type: string + format: int64 + description: >- + expire_at is the UTC epoch represent the last effective date of the + Dym-Name, - `value` which holds the custom JSON in addition to the `@type` + after which the Dym-Name is no longer valid. - field. Example (for message [google.protobuf.Duration][]): + NOTE: Expired Dym-Names are not deleted from the store - { - "@type": "type.googleapis.com/google.protobuf.Duration", - "value": "1.212s" - } - grpc.gateway.runtime.Error: - type: object - properties: - error: - type: string - code: - type: integer - format: int32 - message: - type: string - details: + because iterating through store is very expensive because expiry date + must be checked every use. + configs: type: array items: type: object properties: - type_url: + type: + description: >- + type is the type of the Dym-Name configuration (equals to Type + in DNS). + type: string + enum: + - DCT_UNKNOWN + - DCT_NAME + default: DCT_UNKNOWN + chain_id: type: string description: >- - A URL/resource name that uniquely identifies the type of the - serialized - - protocol buffer message. This string must contain at least - - one "/" character. The last segment of the URL's path must - represent + chain_id is the chain-id of the Dym-Name configuration (equals + to top-level-domain). - the fully qualified name of the type (as in + If empty, the configuration is for host chain (Dymension Hub). + path: + type: string + description: >- + path of the Dym-Name configuration (equals to Host in DNS). - `path/google.protobuf.Duration`). The name should be in a - canonical form + If the type of this config record is Name, it is the Sub-Name of + the Dym-Name Address. + value: + type: string + description: >- + value of the Dym-Name configuration resolves to (equals to Value + in DNS). - (e.g., leading "." is not accepted). + If the type of this config record is Name, it is the address + which the Dym-Name Address resolves to. + description: >- + DymNameConfig contains the resolution configuration for the + Dym-Name. + Each record is a resolution record, similar to DNS. + description: configs are configuration records for the Dym-Name. + contact: + type: string + description: |- + contact is an optional information for the Dym-Name. + Convenient for retails users. + description: >- + DymName defines a Dym-Name, the mainly purpose is to store ownership and + resolution information. - In practice, teams usually precompile into the binary all types - that they + Dym-Name is similar to DNS. It is a human-readable name that maps to a + chain address. - expect it to use in the context of Any. However, for URLs which - use the + One Dym-Name can have multiple configurations, each configuration is a + resolution record. - scheme `http`, `https`, or no scheme, one can optionally set up - a type + Dym-Name is owned by an account, and is able to grant permission to + another account to control the Dym-Name. + dymensionxyz.dymension.dymns.DymNameConfig: + type: object + properties: + type: + description: >- + type is the type of the Dym-Name configuration (equals to Type in + DNS). + type: string + enum: + - DCT_UNKNOWN + - DCT_NAME + default: DCT_UNKNOWN + chain_id: + type: string + description: >- + chain_id is the chain-id of the Dym-Name configuration (equals to + top-level-domain). - server that maps type URLs to message definitions as follows: + If empty, the configuration is for host chain (Dymension Hub). + path: + type: string + description: >- + path of the Dym-Name configuration (equals to Host in DNS). + If the type of this config record is Name, it is the Sub-Name of the + Dym-Name Address. + value: + type: string + description: >- + value of the Dym-Name configuration resolves to (equals to Value in + DNS). - * If no scheme is provided, `https` is assumed. + If the type of this config record is Name, it is the address which the + Dym-Name Address resolves to. + description: |- + DymNameConfig contains the resolution configuration for the Dym-Name. + Each record is a resolution record, similar to DNS. + dymensionxyz.dymension.dymns.DymNameConfigType: + type: string + enum: + - DCT_UNKNOWN + - DCT_NAME + default: DCT_UNKNOWN + description: |- + DymNameConfigType specifies the type of the Dym-Name configuration. + Currently only supports Name, similar to DNS. + dymensionxyz.dymension.dymns.EstimateRegisterAliasResponse: + type: object + properties: + price: + description: price is the price to register the alias. + type: object + properties: + denom: + type: string + amount: + type: string + description: >- + EstimateRegisterAliasResponse is the response type for the + Query/EstimateRegisterAlias RPC method. + dymensionxyz.dymension.dymns.EstimateRegisterNameResponse: + type: object + properties: + first_year_price: + description: >- + first_year_price is the price to register the Dym-Name for the first + year. + type: object + properties: + denom: + type: string + amount: + type: string + extend_price: + description: >- + extend_price is the price to extend the Dym-Name registration for + another year. + type: object + properties: + denom: + type: string + amount: + type: string + total_price: + description: >- + total_price is the total price to register the Dym-Name for the + specified duration. + type: object + properties: + denom: + type: string + amount: + type: string + description: >- + EstimateRegisterNameResponse is the response type for the + Query/EstimateRegisterName RPC method. + dymensionxyz.dymension.dymns.MiscParams: + type: object + properties: + end_epoch_hook_identifier: + type: string + description: end_epoch_hook_identifier is the identifier of the end epoch hook. + grace_period_duration: + type: string + description: >- + grace_period_duration is the amount of time that the former owner of + an expired Dym-Name - * An HTTP GET on the URL must yield a [google.protobuf.Type][] - value in binary format, or produce an error. - * Applications are allowed to cache lookup results based on the - URL, or have them precompiled into a binary to avoid any - lookup. Therefore, binary compatibility needs to be preserved - on changes to types. (Use versioned type names to manage - breaking changes.) + can renew it before completely lost. + sell_order_duration: + type: string + description: >- + sell_order_duration is the amount of time of a Sell-Order from created + to expired. + prohibit_sell_duration: + type: string + description: >- + prohibit_sell_duration is the amount of time, + + if the Sell-Order expiration date enters the range before expiry of + Dym-Name, the Sell-Order can not be placed. + + The logic also applies when the owner accepts a Buy-Order, the + Dym-Name can not be sold. + + This is to prevent the new owner of Dym-Name to sell it before the + Dym-Name expires, + + therefore help reduce the risk of new owner of Dym-Name not able to + renew it on time. + + + For example: + Let say the configured Sell-Order duration is 7 days, the prohibit sell duration is 30 days and you owns a Dym-Name. + We assume your Dym-Name expiration date is Mar 9th, 2024. then 30 days before, from Feb 9th, 2024, + the Dym-Name is prohibited to be sold. + We have some cases: + - If the you places a Sell Order at Feb 2nd, 2024, the Sell-Order supposed to be expired at Feb 9th, 2024, + which is within the prohibited range, so the Sell-Order can not be placed. + - If the you places a Sell Order at Feb 1st, 2024, the Sell-Order supposed to be expired at Feb 8th, 2024, + which is outside the prohibited range, so the Sell-Order can be placed. + - If someone put an offer (Buy-Order) at Feb 1st, 2024, and you have some days to accept it, + exactly from the moment order placed, until end of Feb 8th, 2024, which is outside the prohibited range. + After that, from Feb 9th, it is prohibited to accept the Buy-Order. + enable_trading_name: + type: boolean + description: |- + enable_trading_name is the flag to enable trading of Dym-Name. + To be used to stop trading of Dym-Name when needed. + enable_trading_alias: + type: boolean + description: |- + enable_trading_alias is the flag to enable trading of Alias. + To be used in the future when Alias trading implementation is ready + or disable trading of Alias when needed. + description: MiscParams defines group of miscellaneous parameters. + dymensionxyz.dymension.dymns.MultipleAliases: + type: object + properties: + aliases: + type: array + items: + type: string + description: aliases is a list of alias, available for a RollApp. + description: MultipleAliases contains a list of alias. + dymensionxyz.dymension.dymns.Params: + type: object + properties: + price: + description: >- + price defines setting for pricing of Dym-Name and price-related + parameters. + type: object + properties: + name_price_steps: + type: array + items: + type: string + description: >- + name_price_steps holds the price steps configuration for Dym-Name + registration, apply to the first year. - Note: this functionality is not currently available in the - official + The price of Dym-Name is calculated based on the number of + letters. - protobuf release, and it is not used for type URLs beginning - with + The first element is the price of 1 letter Dym-Name, the last + element is the price of N+ letters Dym-Name. - type.googleapis.com. + Minimum steps count allowed is 4, for 1/2/3/4+ letters Dym-Name. + alias_price_steps: + type: array + items: + type: string + description: >- + alias_price_steps holds the price steps configuration for Alias + registration, one off payment. + The price of Alias is calculated based on the number of letters. - Schemes other than `http`, `https` (or the empty scheme) might - be + The first element is the price of 1 letter Alias, the last element + is the price of N+ letters Alias. - used with implementation specific semantics. - value: - type: string - format: byte - description: >- - Must be a valid serialized protocol buffer of the above - specified type. - description: >- - `Any` contains an arbitrary serialized protocol buffer message along - with a + Minimum steps count allowed is 4, for 1/2/3/4+ letters Alias. + price_extends: + type: string + description: >- + price_extends is used to extends Dym-Name yearly, after the + one-off payment for the first year. + price_denom: + type: string + description: >- + price_denom is the required denomination of the pricing setup and + trading policy. + min_offer_price: + type: string + description: >- + min_offer_price is minimum price allowed to place an offer. - URL that describes the type of the serialized message. + Mostly used to prevent spamming and abusing store with low price + offers, + so the value should not be so low. + chains: + description: chains defines setting for prioritized aliases mapping. + type: object + properties: + aliases_of_chain_ids: + type: array + items: + type: object + properties: + chain_id: + type: string + description: chain_id which owned the aliases. + aliases: + type: array + items: + type: string + title: >- + aliases is a set of aliases of the chain id for UX + improvement, - Protobuf library provides support to pack/unpack Any values in the - form + like we can do my-name@cosmos instead of my-name@cosmoshub-4 + description: AliasesOfChainId defines the multiple-aliases of a chain id. + description: >- + aliases_of_chain_ids is set of chain-ids and their corresponding + aliases, - of utility functions or additional generated methods of the Any - type. + used for UX improvement like we can do my-name@cosmos instead of + my-name@cosmoshub-4. - Example 1: Pack and unpack a message in C++. + This list is prioritized over Roll-App aliases - Foo foo = ...; - Any any; - any.PackFrom(foo); - ... - if (any.UnpackTo(&foo)) { - ... - } + the reason is to allow the community able to have control to fixes + the potential problems with the aliases. + misc: + description: misc is group of miscellaneous parameters. + type: object + properties: + end_epoch_hook_identifier: + type: string + description: end_epoch_hook_identifier is the identifier of the end epoch hook. + grace_period_duration: + type: string + description: >- + grace_period_duration is the amount of time that the former owner + of an expired Dym-Name - Example 2: Pack and unpack a message in Java. + can renew it before completely lost. + sell_order_duration: + type: string + description: >- + sell_order_duration is the amount of time of a Sell-Order from + created to expired. + prohibit_sell_duration: + type: string + description: >- + prohibit_sell_duration is the amount of time, + + if the Sell-Order expiration date enters the range before expiry + of Dym-Name, the Sell-Order can not be placed. + + The logic also applies when the owner accepts a Buy-Order, the + Dym-Name can not be sold. + + This is to prevent the new owner of Dym-Name to sell it before the + Dym-Name expires, + + therefore help reduce the risk of new owner of Dym-Name not able + to renew it on time. + + + For example: + Let say the configured Sell-Order duration is 7 days, the prohibit sell duration is 30 days and you owns a Dym-Name. + We assume your Dym-Name expiration date is Mar 9th, 2024. then 30 days before, from Feb 9th, 2024, + the Dym-Name is prohibited to be sold. + We have some cases: + - If the you places a Sell Order at Feb 2nd, 2024, the Sell-Order supposed to be expired at Feb 9th, 2024, + which is within the prohibited range, so the Sell-Order can not be placed. + - If the you places a Sell Order at Feb 1st, 2024, the Sell-Order supposed to be expired at Feb 8th, 2024, + which is outside the prohibited range, so the Sell-Order can be placed. + - If someone put an offer (Buy-Order) at Feb 1st, 2024, and you have some days to accept it, + exactly from the moment order placed, until end of Feb 8th, 2024, which is outside the prohibited range. + After that, from Feb 9th, it is prohibited to accept the Buy-Order. + enable_trading_name: + type: boolean + description: |- + enable_trading_name is the flag to enable trading of Dym-Name. + To be used to stop trading of Dym-Name when needed. + enable_trading_alias: + type: boolean + description: >- + enable_trading_alias is the flag to enable trading of Alias. - Foo foo = ...; - Any any = Any.pack(foo); - ... - if (any.is(Foo.class)) { - foo = any.unpack(Foo.class); - } - // or ... - if (any.isSameTypeAs(Foo.getDefaultInstance())) { - foo = any.unpack(Foo.getDefaultInstance()); - } + To be used in the future when Alias trading implementation is + ready - Example 3: Pack and unpack a message in Python. + or disable trading of Alias when needed. + description: Params defines the parameters for the module. + dymensionxyz.dymension.dymns.PriceParams: + type: object + properties: + name_price_steps: + type: array + items: + type: string + description: >- + name_price_steps holds the price steps configuration for Dym-Name + registration, apply to the first year. - foo = Foo(...) - any = Any() - any.Pack(foo) - ... - if any.Is(Foo.DESCRIPTOR): - any.Unpack(foo) - ... + The price of Dym-Name is calculated based on the number of letters. - Example 4: Pack and unpack a message in Go + The first element is the price of 1 letter Dym-Name, the last element + is the price of N+ letters Dym-Name. - foo := &pb.Foo{...} - any, err := anypb.New(foo) - if err != nil { - ... - } - ... - foo := &pb.Foo{} - if err := any.UnmarshalTo(foo); err != nil { - ... - } + Minimum steps count allowed is 4, for 1/2/3/4+ letters Dym-Name. + alias_price_steps: + type: array + items: + type: string + description: >- + alias_price_steps holds the price steps configuration for Alias + registration, one off payment. - The pack methods provided by protobuf library will by default use + The price of Alias is calculated based on the number of letters. - 'type.googleapis.com/full.type.name' as the type URL and the unpack + The first element is the price of 1 letter Alias, the last element is + the price of N+ letters Alias. - methods only use the fully qualified type name after the last '/' + Minimum steps count allowed is 4, for 1/2/3/4+ letters Alias. + price_extends: + type: string + description: >- + price_extends is used to extends Dym-Name yearly, after the one-off + payment for the first year. + price_denom: + type: string + description: >- + price_denom is the required denomination of the pricing setup and + trading policy. + min_offer_price: + type: string + description: >- + min_offer_price is minimum price allowed to place an offer. - in the type URL, for example "foo.bar.com/x/y.z" will yield type + Mostly used to prevent spamming and abusing store with low price + offers, - name "y.z". + so the value should not be so low. + description: PriceParams defines the pricing of Dym-Name and price-related parameters. + dymensionxyz.dymension.dymns.QueryAliasResponse: + type: object + properties: + chain_id: + type: string + title: chain_id associated with the alias + found_sell_order: + type: boolean + description: found_sell_order is true if active Sell-Order is found for the alias. + buy_order_ids: + type: array + items: + type: string + description: buy_order_ids is the list of Buy-Order IDs for the alias. + same_chain_aliases: + type: array + items: + type: string + description: >- + same_chain_aliases is the list of aliases for the same chain that + associated with the alias. + title: QueryAliasResponse + dymensionxyz.dymension.dymns.QueryAliasesResponse: + type: object + properties: + aliases_by_chain_id: + type: object + additionalProperties: + type: object + properties: + aliases: + type: array + items: + type: string + description: aliases is a list of alias, available for a RollApp. + description: MultipleAliases contains a list of alias. + description: aliases_by_chain_id is the map of aliases by chain id. + dymensionxyz.dymension.dymns.QueryBuyOrderByIdResponse: + type: object + properties: + buy_order: + description: buy_order is the result. + type: object + properties: + id: + type: string + description: id is the unique identifier of the order. Generated by the module. + asset_id: + type: string + description: asset_id of the Dym-Name/Alias willing to buy. + asset_type: + title: asset_type is type of the asset of the order, is Dym-Name/Alias + type: string + enum: + - AT_UNKNOWN + - AT_DYM_NAME + - AT_ALIAS + default: AT_UNKNOWN + description: AssetType present type of the asset of the Buy/Sell order. + params: + type: array + items: + type: string + description: >- + params is the list of parameters of the Buy-Order. + It is empty for asset type Dym-Name. - JSON + It has one element for asset type Alias, which is the rollapp_id + to assigned for. + buyer: + type: string + description: buyer is bech32 address of the account which placed the order. + offer_price: + description: >- + offer_price is the amount of coins that buyer willing to pay for + the asset. + This amount is deposited to the module account upon placing the + offer. + type: object + properties: + denom: + type: string + amount: + type: string + counterparty_offer_price: + description: >- + counterparty_offer_price is the price that the Dym-Name/Alias + owner is willing to sell for. - The JSON representation of an `Any` value uses the regular + This is used for counterparty price negotiation and for + information only. - representation of the deserialized, embedded message, with an + The transaction can only be executed when the owner accepts the + offer with exact offer_price. + type: object + properties: + denom: + type: string + amount: + type: string + description: >- + QueryBuyOrderByIdResponse is the response type for the Query/BuyOrderById + RPC method. + dymensionxyz.dymension.dymns.QueryBuyOrdersByAliasResponse: + type: object + properties: + buy_orders: + type: array + items: + type: object + properties: + id: + type: string + description: >- + id is the unique identifier of the order. Generated by the + module. + asset_id: + type: string + description: asset_id of the Dym-Name/Alias willing to buy. + asset_type: + title: asset_type is type of the asset of the order, is Dym-Name/Alias + type: string + enum: + - AT_UNKNOWN + - AT_DYM_NAME + - AT_ALIAS + default: AT_UNKNOWN + description: AssetType present type of the asset of the Buy/Sell order. + params: + type: array + items: + type: string + description: >- + params is the list of parameters of the Buy-Order. - additional field `@type` which contains the type URL. Example: + It is empty for asset type Dym-Name. - package google.profile; - message Person { - string first_name = 1; - string last_name = 2; - } + It has one element for asset type Alias, which is the rollapp_id + to assigned for. + buyer: + type: string + description: buyer is bech32 address of the account which placed the order. + offer_price: + description: >- + offer_price is the amount of coins that buyer willing to pay for + the asset. - { - "@type": "type.googleapis.com/google.profile.Person", - "firstName": , - "lastName": - } + This amount is deposited to the module account upon placing the + offer. + type: object + properties: + denom: + type: string + amount: + type: string + counterparty_offer_price: + description: >- + counterparty_offer_price is the price that the Dym-Name/Alias + owner is willing to sell for. - If the embedded message type is well-known and has a custom JSON + This is used for counterparty price negotiation and for + information only. - representation, that representation will be embedded adding a field + The transaction can only be executed when the owner accepts the + offer with exact offer_price. + type: object + properties: + denom: + type: string + amount: + type: string + description: >- + BuyOrder defines an offer to buy a Dym-Name/Alias, placed by buyer. - `value` which holds the custom JSON in addition to the `@type` + Buyer will need to deposit the offer amount to the module account. - field. Example (for message [google.protobuf.Duration][]): + When the owner of the Dym-Name/Alias accepts the offer, deposited + amount will be transferred to the owner. - { - "@type": "type.googleapis.com/google.protobuf.Duration", - "value": "1.212s" - } - ibc.core.channel.v1.Packet: + When the buyer cancels the offer, deposited amount will be refunded + to the buyer. + description: buy_orders of the input alias. + description: >- + QueryBuyOrdersByAliasResponse is the response type for the + Query/BuyOrdersByAlias RPC method. + dymensionxyz.dymension.dymns.QueryBuyOrdersByDymNameResponse: type: object properties: - sequence: - type: string - format: uint64 - description: >- - number corresponds to the order of sends and receives, where a Packet + buy_orders: + type: array + items: + type: object + properties: + id: + type: string + description: >- + id is the unique identifier of the order. Generated by the + module. + asset_id: + type: string + description: asset_id of the Dym-Name/Alias willing to buy. + asset_type: + title: asset_type is type of the asset of the order, is Dym-Name/Alias + type: string + enum: + - AT_UNKNOWN + - AT_DYM_NAME + - AT_ALIAS + default: AT_UNKNOWN + description: AssetType present type of the asset of the Buy/Sell order. + params: + type: array + items: + type: string + description: >- + params is the list of parameters of the Buy-Order. - with an earlier sequence number must be sent and received before a - Packet + It is empty for asset type Dym-Name. - with a later sequence number. - source_port: - type: string - description: identifies the port on the sending chain. - source_channel: - type: string - description: identifies the channel end on the sending chain. - destination_port: - type: string - description: identifies the port on the receiving chain. - destination_channel: - type: string - description: identifies the channel end on the receiving chain. - data: - type: string - format: byte - title: actual opaque bytes transferred directly to the application module - timeout_height: - title: block height after which the packet times out - type: object - properties: - revision_number: - type: string - format: uint64 - title: the revision that the client is currently on - revision_height: - type: string - format: uint64 - title: the height within the given revision - description: >- - Normally the RevisionHeight is incremented at each height while - keeping + It has one element for asset type Alias, which is the rollapp_id + to assigned for. + buyer: + type: string + description: buyer is bech32 address of the account which placed the order. + offer_price: + description: >- + offer_price is the amount of coins that buyer willing to pay for + the asset. - RevisionNumber the same. However some consensus algorithms may choose - to + This amount is deposited to the module account upon placing the + offer. + type: object + properties: + denom: + type: string + amount: + type: string + counterparty_offer_price: + description: >- + counterparty_offer_price is the price that the Dym-Name/Alias + owner is willing to sell for. - reset the height in certain conditions e.g. hard forks, state-machine + This is used for counterparty price negotiation and for + information only. - breaking changes In these cases, the RevisionNumber is incremented so - that + The transaction can only be executed when the owner accepts the + offer with exact offer_price. + type: object + properties: + denom: + type: string + amount: + type: string + description: >- + BuyOrder defines an offer to buy a Dym-Name/Alias, placed by buyer. - height continues to be monitonically increasing even as the - RevisionHeight + Buyer will need to deposit the offer amount to the module account. - gets reset - timeout_timestamp: - type: string - format: uint64 - title: block timestamp (in nanoseconds) after which the packet times out - title: >- - Packet defines a type that carries data across different chains through - IBC - ibc.core.client.v1.Height: + When the owner of the Dym-Name/Alias accepts the offer, deposited + amount will be transferred to the owner. + + When the buyer cancels the offer, deposited amount will be refunded + to the buyer. + description: buy_orders placed for the Dym-Name. + description: >- + QueryBuyOrdersByDymNameResponse is the response type for the + Query/BuyOrdersByDymName RPC method. + dymensionxyz.dymension.dymns.QueryBuyOrdersOfAliasesLinkedToRollAppResponse: type: object properties: - revision_number: - type: string - format: uint64 - title: the revision that the client is currently on - revision_height: - type: string - format: uint64 - title: the height within the given revision - description: |- - Normally the RevisionHeight is incremented at each height while keeping - RevisionNumber the same. However some consensus algorithms may choose to - reset the height in certain conditions e.g. hard forks, state-machine - breaking changes In these cases, the RevisionNumber is incremented so that - height continues to be monitonically increasing even as the RevisionHeight - gets reset - title: >- - Height is a monotonically increasing data type + buy_orders: + type: array + items: + type: object + properties: + id: + type: string + description: >- + id is the unique identifier of the order. Generated by the + module. + asset_id: + type: string + description: asset_id of the Dym-Name/Alias willing to buy. + asset_type: + title: asset_type is type of the asset of the order, is Dym-Name/Alias + type: string + enum: + - AT_UNKNOWN + - AT_DYM_NAME + - AT_ALIAS + default: AT_UNKNOWN + description: AssetType present type of the asset of the Buy/Sell order. + params: + type: array + items: + type: string + description: >- + params is the list of parameters of the Buy-Order. - that can be compared against another Height for the purposes of updating - and + It is empty for asset type Dym-Name. - freezing clients - cosmos.base.v1beta1.Coin: - type: object - properties: - denom: - type: string - amount: - type: string - description: |- - Coin defines a token with a denomination and an amount. + It has one element for asset type Alias, which is the rollapp_id + to assigned for. + buyer: + type: string + description: buyer is bech32 address of the account which placed the order. + offer_price: + description: >- + offer_price is the amount of coins that buyer willing to pay for + the asset. - NOTE: The amount field is an Int which implements the custom method - signatures required by gogoproto. - dymensionxyz.dymension.eibc.DemandOrder: + This amount is deposited to the module account upon placing the + offer. + type: object + properties: + denom: + type: string + amount: + type: string + counterparty_offer_price: + description: >- + counterparty_offer_price is the price that the Dym-Name/Alias + owner is willing to sell for. + + This is used for counterparty price negotiation and for + information only. + + The transaction can only be executed when the owner accepts the + offer with exact offer_price. + type: object + properties: + denom: + type: string + amount: + type: string + description: >- + BuyOrder defines an offer to buy a Dym-Name/Alias, placed by buyer. + + Buyer will need to deposit the offer amount to the module account. + + When the owner of the Dym-Name/Alias accepts the offer, deposited + amount will be transferred to the owner. + + When the buyer cancels the offer, deposited amount will be refunded + to the buyer. + description: >- + buy_orders are all the buy orders of the aliases linked to the input + rollapp. + description: >- + QueryBuyOrdersOfAliasesLinkedToRollAppResponse is the response type for + the Query/BuyOrdersOfAliasesLinkedToRollApp RPC method. + dymensionxyz.dymension.dymns.QueryBuyOrdersOfDymNamesOwnedByAccountResponse: type: object properties: - id: - type: string - title: >- - id is a hash of the form generated by GetRollappPacketKey, - - e.g - status/rollappid/packetProofHeight/packetDestinationChannel-PacketSequence - which gurantees uniqueness - tracking_packet_key: - type: string - description: |- - tracking_packet_key is the key of the packet that is being tracked. - This key can change depends on the packet status. - price: + buy_orders: type: array items: type: object properties: - denom: + id: type: string - amount: + description: >- + id is the unique identifier of the order. Generated by the + module. + asset_id: type: string - description: |- - Coin defines a token with a denomination and an amount. + description: asset_id of the Dym-Name/Alias willing to buy. + asset_type: + title: asset_type is type of the asset of the order, is Dym-Name/Alias + type: string + enum: + - AT_UNKNOWN + - AT_DYM_NAME + - AT_ALIAS + default: AT_UNKNOWN + description: AssetType present type of the asset of the Buy/Sell order. + params: + type: array + items: + type: string + description: >- + params is the list of parameters of the Buy-Order. - NOTE: The amount field is an Int which implements the custom method - signatures required by gogoproto. - title: >- - price is the amount that the fulfiller sends to original eibc transfer - recipient - fee: + It is empty for asset type Dym-Name. + + It has one element for asset type Alias, which is the rollapp_id + to assigned for. + buyer: + type: string + description: buyer is bech32 address of the account which placed the order. + offer_price: + description: >- + offer_price is the amount of coins that buyer willing to pay for + the asset. + + This amount is deposited to the module account upon placing the + offer. + type: object + properties: + denom: + type: string + amount: + type: string + counterparty_offer_price: + description: >- + counterparty_offer_price is the price that the Dym-Name/Alias + owner is willing to sell for. + + This is used for counterparty price negotiation and for + information only. + + The transaction can only be executed when the owner accepts the + offer with exact offer_price. + type: object + properties: + denom: + type: string + amount: + type: string + description: >- + BuyOrder defines an offer to buy a Dym-Name/Alias, placed by buyer. + + Buyer will need to deposit the offer amount to the module account. + + When the owner of the Dym-Name/Alias accepts the offer, deposited + amount will be transferred to the owner. + + When the buyer cancels the offer, deposited amount will be refunded + to the buyer. + description: buy_orders of all the Dym-Names owned by the input account. + description: >- + QueryBuyOrdersOfDymNamesOwnedByAccountResponse is the response type for + the Query/BuyOrdersOfDymNamesOwnedByAccount RPC method. + dymensionxyz.dymension.dymns.QueryBuyOrdersPlacedByAccountResponse: + type: object + properties: + buy_orders: type: array items: type: object properties: - denom: + id: type: string - amount: + description: >- + id is the unique identifier of the order. Generated by the + module. + asset_id: type: string - description: |- - Coin defines a token with a denomination and an amount. + description: asset_id of the Dym-Name/Alias willing to buy. + asset_type: + title: asset_type is type of the asset of the order, is Dym-Name/Alias + type: string + enum: + - AT_UNKNOWN + - AT_DYM_NAME + - AT_ALIAS + default: AT_UNKNOWN + description: AssetType present type of the asset of the Buy/Sell order. + params: + type: array + items: + type: string + description: >- + params is the list of parameters of the Buy-Order. - NOTE: The amount field is an Int which implements the custom method - signatures required by gogoproto. - title: >- - fee is the effective profit made by the fulfiller because they pay - price and receive fee + price - recipient: - type: string - is_fulfilled: - type: boolean - tracking_packet_status: - type: string - enum: - - PENDING - - FINALIZED - - REVERTED - default: PENDING - rollapp_id: - type: string - type: - type: string - enum: - - ON_RECV - - ON_ACK - - ON_TIMEOUT - - UNDEFINED - default: ON_RECV - dymensionxyz.dymension.eibc.FulfillmentState: - type: string - enum: - - UNDEFINED - - FULFILLED - - UNFULFILLED - default: UNDEFINED - dymensionxyz.dymension.eibc.Params: + It is empty for asset type Dym-Name. + + It has one element for asset type Alias, which is the rollapp_id + to assigned for. + buyer: + type: string + description: buyer is bech32 address of the account which placed the order. + offer_price: + description: >- + offer_price is the amount of coins that buyer willing to pay for + the asset. + + This amount is deposited to the module account upon placing the + offer. + type: object + properties: + denom: + type: string + amount: + type: string + counterparty_offer_price: + description: >- + counterparty_offer_price is the price that the Dym-Name/Alias + owner is willing to sell for. + + This is used for counterparty price negotiation and for + information only. + + The transaction can only be executed when the owner accepts the + offer with exact offer_price. + type: object + properties: + denom: + type: string + amount: + type: string + description: >- + BuyOrder defines an offer to buy a Dym-Name/Alias, placed by buyer. + + Buyer will need to deposit the offer amount to the module account. + + When the owner of the Dym-Name/Alias accepts the offer, deposited + amount will be transferred to the owner. + + When the buyer cancels the offer, deposited amount will be refunded + to the buyer. + description: offers are the Buy-Orders placed by the account. + description: >- + QueryBuyOrdersByAccountResponse is the response type for the + Query/BuyOrdersPlacedByAccount RPC method. + dymensionxyz.dymension.dymns.QueryDymNameResponse: type: object properties: - epoch_identifier: - type: string - timeout_fee: - type: string - errack_fee: - type: string - description: Params defines the parameters for the module. - dymensionxyz.dymension.eibc.QueryDemandOrdersByStatusResponse: + dym_name: + description: dym_name is the Dym-Name queried for. + type: object + properties: + name: + type: string + description: name is the human-readable name of the Dym-Name. + owner: + type: string + description: >- + owner is the account address that owns the Dym-Name. Owner has + permission to transfer ownership. + controller: + type: string + description: >- + controller is the account address that has permission update + configuration for the Dym-Name. + + Default is the owner. Able to transfer control to another account + by the owner. + + Users can set Dym-Name owned by Cold-Wallet and controlled by + Hot-Wallet. + expire_at: + type: string + format: int64 + description: >- + expire_at is the UTC epoch represent the last effective date of + the Dym-Name, + + after which the Dym-Name is no longer valid. + + NOTE: Expired Dym-Names are not deleted from the store + + because iterating through store is very expensive because expiry + date must be checked every use. + configs: + type: array + items: + type: object + properties: + type: + description: >- + type is the type of the Dym-Name configuration (equals to + Type in DNS). + type: string + enum: + - DCT_UNKNOWN + - DCT_NAME + default: DCT_UNKNOWN + chain_id: + type: string + description: >- + chain_id is the chain-id of the Dym-Name configuration + (equals to top-level-domain). + + If empty, the configuration is for host chain (Dymension + Hub). + path: + type: string + description: >- + path of the Dym-Name configuration (equals to Host in DNS). + + If the type of this config record is Name, it is the + Sub-Name of the Dym-Name Address. + value: + type: string + description: >- + value of the Dym-Name configuration resolves to (equals to + Value in DNS). + + If the type of this config record is Name, it is the address + which the Dym-Name Address resolves to. + description: >- + DymNameConfig contains the resolution configuration for the + Dym-Name. + + Each record is a resolution record, similar to DNS. + description: configs are configuration records for the Dym-Name. + contact: + type: string + description: |- + contact is an optional information for the Dym-Name. + Convenient for retails users. + description: >- + QueryDymNameResponse is the response type for the Query/DymName RPC + method. + dymensionxyz.dymension.dymns.QueryDymNamesOwnedByAccountResponse: type: object properties: - demand_orders: + dym_names: type: array items: type: object properties: - id: + name: type: string - title: >- - id is a hash of the form generated by GetRollappPacketKey, + description: name is the human-readable name of the Dym-Name. + owner: + type: string + description: >- + owner is the account address that owns the Dym-Name. Owner has + permission to transfer ownership. + controller: + type: string + description: >- + controller is the account address that has permission update + configuration for the Dym-Name. - e.g - status/rollappid/packetProofHeight/packetDestinationChannel-PacketSequence - which gurantees uniqueness - tracking_packet_key: + Default is the owner. Able to transfer control to another + account by the owner. + + Users can set Dym-Name owned by Cold-Wallet and controlled by + Hot-Wallet. + expire_at: type: string + format: int64 description: >- - tracking_packet_key is the key of the packet that is being - tracked. + expire_at is the UTC epoch represent the last effective date of + the Dym-Name, - This key can change depends on the packet status. - price: + after which the Dym-Name is no longer valid. + + NOTE: Expired Dym-Names are not deleted from the store + + because iterating through store is very expensive because expiry + date must be checked every use. + configs: type: array items: type: object properties: - denom: + type: + description: >- + type is the type of the Dym-Name configuration (equals to + Type in DNS). type: string - amount: + enum: + - DCT_UNKNOWN + - DCT_NAME + default: DCT_UNKNOWN + chain_id: + type: string + description: >- + chain_id is the chain-id of the Dym-Name configuration + (equals to top-level-domain). + + If empty, the configuration is for host chain (Dymension + Hub). + path: + type: string + description: >- + path of the Dym-Name configuration (equals to Host in + DNS). + + If the type of this config record is Name, it is the + Sub-Name of the Dym-Name Address. + value: type: string + description: >- + value of the Dym-Name configuration resolves to (equals to + Value in DNS). + + If the type of this config record is Name, it is the + address which the Dym-Name Address resolves to. description: >- - Coin defines a token with a denomination and an amount. + DymNameConfig contains the resolution configuration for the + Dym-Name. + Each record is a resolution record, similar to DNS. + description: configs are configuration records for the Dym-Name. + contact: + type: string + description: |- + contact is an optional information for the Dym-Name. + Convenient for retails users. + description: >- + DymName defines a Dym-Name, the mainly purpose is to store ownership + and resolution information. - NOTE: The amount field is an Int which implements the custom - method + Dym-Name is similar to DNS. It is a human-readable name that maps to + a chain address. - signatures required by gogoproto. - title: >- - price is the amount that the fulfiller sends to original eibc - transfer recipient - fee: - type: array - items: + One Dym-Name can have multiple configurations, each configuration is + a resolution record. + + Dym-Name is owned by an account, and is able to grant permission to + another account to control the Dym-Name. + description: dym_names defines the Dym-Names owned by the input account. + description: >- + QueryDymNamesOwnedByAccountResponse is the response type for the + Query/DymNamesOwnedByAccount RPC method. + dymensionxyz.dymension.dymns.QueryParamsResponse: + type: object + properties: + params: + description: params holds all the parameters of this module. + type: object + properties: + price: + description: >- + price defines setting for pricing of Dym-Name and price-related + parameters. + type: object + properties: + name_price_steps: + type: array + items: + type: string + description: >- + name_price_steps holds the price steps configuration for + Dym-Name registration, apply to the first year. + + The price of Dym-Name is calculated based on the number of + letters. + + The first element is the price of 1 letter Dym-Name, the last + element is the price of N+ letters Dym-Name. + + Minimum steps count allowed is 4, for 1/2/3/4+ letters + Dym-Name. + alias_price_steps: + type: array + items: + type: string + description: >- + alias_price_steps holds the price steps configuration for + Alias registration, one off payment. + + The price of Alias is calculated based on the number of + letters. + + The first element is the price of 1 letter Alias, the last + element is the price of N+ letters Alias. + + Minimum steps count allowed is 4, for 1/2/3/4+ letters Alias. + price_extends: + type: string + description: >- + price_extends is used to extends Dym-Name yearly, after the + one-off payment for the first year. + price_denom: + type: string + description: >- + price_denom is the required denomination of the pricing setup + and trading policy. + min_offer_price: + type: string + description: >- + min_offer_price is minimum price allowed to place an offer. + + Mostly used to prevent spamming and abusing store with low + price offers, + + so the value should not be so low. + chains: + description: chains defines setting for prioritized aliases mapping. + type: object + properties: + aliases_of_chain_ids: + type: array + items: + type: object + properties: + chain_id: + type: string + description: chain_id which owned the aliases. + aliases: + type: array + items: + type: string + title: >- + aliases is a set of aliases of the chain id for UX + improvement, + + like we can do my-name@cosmos instead of + my-name@cosmoshub-4 + description: AliasesOfChainId defines the multiple-aliases of a chain id. + description: >- + aliases_of_chain_ids is set of chain-ids and their + corresponding aliases, + + used for UX improvement like we can do my-name@cosmos instead + of my-name@cosmoshub-4. + + + This list is prioritized over Roll-App aliases + + the reason is to allow the community able to have control to + fixes the potential problems with the aliases. + misc: + description: misc is group of miscellaneous parameters. + type: object + properties: + end_epoch_hook_identifier: + type: string + description: >- + end_epoch_hook_identifier is the identifier of the end epoch + hook. + grace_period_duration: + type: string + description: >- + grace_period_duration is the amount of time that the former + owner of an expired Dym-Name + + can renew it before completely lost. + sell_order_duration: + type: string + description: >- + sell_order_duration is the amount of time of a Sell-Order from + created to expired. + prohibit_sell_duration: + type: string + description: >- + prohibit_sell_duration is the amount of time, + + if the Sell-Order expiration date enters the range before + expiry of Dym-Name, the Sell-Order can not be placed. + + The logic also applies when the owner accepts a Buy-Order, the + Dym-Name can not be sold. + + This is to prevent the new owner of Dym-Name to sell it before + the Dym-Name expires, + + therefore help reduce the risk of new owner of Dym-Name not + able to renew it on time. + + + For example: + Let say the configured Sell-Order duration is 7 days, the prohibit sell duration is 30 days and you owns a Dym-Name. + We assume your Dym-Name expiration date is Mar 9th, 2024. then 30 days before, from Feb 9th, 2024, + the Dym-Name is prohibited to be sold. + We have some cases: + - If the you places a Sell Order at Feb 2nd, 2024, the Sell-Order supposed to be expired at Feb 9th, 2024, + which is within the prohibited range, so the Sell-Order can not be placed. + - If the you places a Sell Order at Feb 1st, 2024, the Sell-Order supposed to be expired at Feb 8th, 2024, + which is outside the prohibited range, so the Sell-Order can be placed. + - If someone put an offer (Buy-Order) at Feb 1st, 2024, and you have some days to accept it, + exactly from the moment order placed, until end of Feb 8th, 2024, which is outside the prohibited range. + After that, from Feb 9th, it is prohibited to accept the Buy-Order. + enable_trading_name: + type: boolean + description: |- + enable_trading_name is the flag to enable trading of Dym-Name. + To be used to stop trading of Dym-Name when needed. + enable_trading_alias: + type: boolean + description: >- + enable_trading_alias is the flag to enable trading of Alias. + + To be used in the future when Alias trading implementation is + ready + + or disable trading of Alias when needed. + description: QueryParamsResponse is response type for the Query/Params RPC method. + dymensionxyz.dymension.dymns.QuerySellOrderResponse: + type: object + properties: + result: + description: result is the active Sell-Order for the Dym-Name/Alias. + type: object + properties: + asset_id: + type: string + description: asset_id is the Dym-Name/Alias being opened to be sold. + asset_type: + description: >- + asset_type is the type of the asset of the order, is + Dym-Name/Alias. + type: string + enum: + - AT_UNKNOWN + - AT_DYM_NAME + - AT_ALIAS + default: AT_UNKNOWN + expire_at: + type: string + format: int64 + title: expire_at is the last effective date of this SO + min_price: + description: >- + min_price is the minimum price that the owner is willing to accept + for the asset. + type: object + properties: + denom: + type: string + amount: + type: string + sell_price: + description: >- + sell_price is the price that the owner is willing to sell the + Dym-Name/Alias for, + + the SO will be closed when the price is met. + + If the sell price is zero, the SO will be closed when the + expire_at is reached and the highest bidder wins. + type: object + properties: + denom: + type: string + amount: + type: string + highest_bid: + description: >- + highest_bid is the highest bid on the SO, if any. Price must be + greater than or equal to the min_price. + type: object + properties: + bidder: + type: string + description: >- + bidder is the account address of the account which placed the + bid. + price: + description: price is the amount of coin offered by the bidder. type: object properties: denom: type: string amount: type: string + params: + type: array + items: + type: string description: >- - Coin defines a token with a denomination and an amount. - + params is the list of parameters of the bid. - NOTE: The amount field is an Int which implements the custom - method + It is empty for asset type Dym-Name. - signatures required by gogoproto. - title: >- - fee is the effective profit made by the fulfiller because they - pay price and receive fee + price - recipient: + It has one element for asset type Alias, which is the + rollapp_id to assigned for. + description: >- + QuerySellOrderResponse is the response type for the Query/SellOrder RPC + method. + dymensionxyz.dymension.dymns.QueryTranslateAliasOrChainIdToChainIdResponse: + type: object + properties: + chain_id: + type: string + description: chain_id is the chain id that the alias or chain id translates to. + description: >- + QueryTranslateAliasOrChainIdToChainIdResponse is the response type for the + Query/TranslateAliasOrChainIdToChainId RPC method. + dymensionxyz.dymension.dymns.ResolveDymNameAddressesResponse: + type: object + properties: + resolved_addresses: + type: array + items: + type: object + properties: + address: type: string - is_fulfilled: - type: boolean - tracking_packet_status: + description: address is the input Dym-Name address to resolve. + resolved_address: type: string - enum: - - PENDING - - FINALIZED - - REVERTED - default: PENDING - rollapp_id: + description: resolved_address is the resolved account address. + error: type: string - type: + description: error is the error that occurred during the resolution. + description: >- + ResultDymNameAddress defines the result of a single Dym-Name address + resolution. + description: >- + resolved_addresses defines the resolved addresses for each input + Dym-Name address. + description: >- + ResolveDymNameAddressesResponse is the response type for the + Query/ResolveDymNameAddresses RPC method. + dymensionxyz.dymension.dymns.ResultDymNameAddress: + type: object + properties: + address: + type: string + description: address is the input Dym-Name address to resolve. + resolved_address: + type: string + description: resolved_address is the resolved account address. + error: + type: string + description: error is the error that occurred during the resolution. + description: >- + ResultDymNameAddress defines the result of a single Dym-Name address + resolution. + dymensionxyz.dymension.dymns.ReverseResolveAddressResponse: + type: object + properties: + result: + type: object + additionalProperties: + type: object + properties: + candidates: + type: array + items: + type: string + description: >- + candidates are the Dym-Name addresses that the input address + resolves to. Take one of them. + error: type: string - enum: - - ON_RECV - - ON_ACK - - ON_TIMEOUT - - UNDEFINED - default: ON_RECV - title: A list of demand orders with the given status + description: error is the error that occurred during the resolution. + description: result defines the reverse resolution result for each input address. + working_chain_id: + type: string + description: working_chain_id is the chain id used for the reverse resolution. description: >- - QueryDemandOrdersByStatusResponse is the response type for the - Query/GetDemandOrdersByStatus RPC method. - dymensionxyz.dymension.eibc.QueryGetDemandOrderResponse: + ReverseResolveAddressResponse is the response type for the + Query/ReverseResolveAddress RPC method. + dymensionxyz.dymension.dymns.ReverseResolveAddressResult: type: object properties: - demand_order: - title: demand order with the given id + candidates: + type: array + items: + type: string + description: >- + candidates are the Dym-Name addresses that the input address resolves + to. Take one of them. + error: + type: string + description: error is the error that occurred during the resolution. + dymensionxyz.dymension.dymns.SellOrder: + type: object + properties: + asset_id: + type: string + description: asset_id is the Dym-Name/Alias being opened to be sold. + asset_type: + description: asset_type is the type of the asset of the order, is Dym-Name/Alias. + type: string + enum: + - AT_UNKNOWN + - AT_DYM_NAME + - AT_ALIAS + default: AT_UNKNOWN + expire_at: + type: string + format: int64 + title: expire_at is the last effective date of this SO + min_price: + description: >- + min_price is the minimum price that the owner is willing to accept for + the asset. type: object properties: - id: + denom: type: string - title: >- - id is a hash of the form generated by GetRollappPacketKey, - - e.g - status/rollappid/packetProofHeight/packetDestinationChannel-PacketSequence - which gurantees uniqueness - tracking_packet_key: + amount: type: string - description: >- - tracking_packet_key is the key of the packet that is being - tracked. + sell_price: + description: >- + sell_price is the price that the owner is willing to sell the + Dym-Name/Alias for, - This key can change depends on the packet status. + the SO will be closed when the price is met. + + If the sell price is zero, the SO will be closed when the expire_at is + reached and the highest bidder wins. + type: object + properties: + denom: + type: string + amount: + type: string + highest_bid: + description: >- + highest_bid is the highest bid on the SO, if any. Price must be + greater than or equal to the min_price. + type: object + properties: + bidder: + type: string + description: bidder is the account address of the account which placed the bid. price: + description: price is the amount of coin offered by the bidder. + type: object + properties: + denom: + type: string + amount: + type: string + params: type: array items: - type: object - properties: - denom: - type: string - amount: - type: string - description: >- - Coin defines a token with a denomination and an amount. + type: string + description: >- + params is the list of parameters of the bid. + It is empty for asset type Dym-Name. - NOTE: The amount field is an Int which implements the custom - method + It has one element for asset type Alias, which is the rollapp_id + to assigned for. + description: >- + SellOrder defines a sell order, placed by owner, to sell a Dym-Name/Alias. - signatures required by gogoproto. - title: >- - price is the amount that the fulfiller sends to original eibc - transfer recipient - fee: - type: array - items: - type: object - properties: - denom: - type: string - amount: - type: string - description: >- - Coin defines a token with a denomination and an amount. + Sell-Order has an expiry date. + After expiry date, if no one has placed a bid, this Sell-Order will be + closed, no change. - NOTE: The amount field is an Int which implements the custom - method + If there is a bid, the highest bid will win, and the Dym-Name/Alias + ownership will be transferred to the winner. - signatures required by gogoproto. - title: >- - fee is the effective profit made by the fulfiller because they pay - price and receive fee + price - recipient: - type: string - is_fulfilled: - type: boolean - tracking_packet_status: - type: string - enum: - - PENDING - - FINALIZED - - REVERTED - default: PENDING - rollapp_id: - type: string - type: - type: string - enum: - - ON_RECV - - ON_ACK - - ON_TIMEOUT - - UNDEFINED - default: ON_RECV - description: >- - QueryGetDemandOrderResponse is the response type for the - Query/GetDemandOrder RPC method. - dymensionxyz.dymension.eibc.QueryParamsResponse: + If the bid matches the sell price, the Dym-Name/Alias ownership will be + transferred to the bidder immediately. + dymensionxyz.dymension.dymns.SellOrderBid: type: object properties: - params: - description: params holds all the parameters of this module. + bidder: + type: string + description: bidder is the account address of the account which placed the bid. + price: + description: price is the amount of coin offered by the bidder. type: object properties: - epoch_identifier: - type: string - timeout_fee: + denom: type: string - errack_fee: + amount: type: string - description: QueryParamsResponse is response type for the Query/Params RPC method. + params: + type: array + items: + type: string + description: >- + params is the list of parameters of the bid. + + It is empty for asset type Dym-Name. + + It has one element for asset type Alias, which is the rollapp_id to + assigned for. + description: SellOrderBid defines a bid placed by an account on a Sell-Order. dymensionxyz.dymension.rollapp.BlockDescriptor: type: object properties: @@ -54659,62 +60730,6 @@ definitions: root of the block) description: BlockDescriptor defines a single rollapp chain block description. description: BlockDescriptors defines list of BlockDescriptor. - dymensionxyz.dymension.rollapp.DenomUnit: - type: object - properties: - denom: - type: string - description: denom represents the string name of the given denom unit (e.g uatom). - exponent: - type: integer - format: int64 - description: >- - exponent represents power of 10 exponent that one must - - raise the base_denom to in order to equal the given DenomUnit's denom - - 1 denom = 10^exponent base_denom - - (e.g. with a base_denom of uatom, one can create a DenomUnit of 'atom' - with - - exponent = 6, thus: 1 atom = 10^6 uatom). - aliases: - type: array - items: - type: string - title: aliases is a list of string aliases for the given denom - description: |- - DenomUnit represents a struct that describes a given - denomination unit of the basic token. - dymensionxyz.dymension.rollapp.DeployerParams: - type: object - properties: - address: - type: string - description: |- - address is a bech32-encoded address of the - accounts that are allowed to create a rollapp. - dymensionxyz.dymension.rollapp.GenesisAccount: - type: object - properties: - amount: - title: amount of coins to be sent to the genesis address - type: object - properties: - denom: - type: string - amount: - type: string - description: |- - Coin defines a token with a denomination and an amount. - - NOTE: The amount field is an Int which implements the custom method - signatures required by gogoproto. - address: - type: string - title: address is a bech-32 address of the genesis account - title: GenesisAccount is a struct for the genesis account for the rollapp dymensionxyz.dymension.rollapp.Params: type: object properties: @@ -54725,25 +60740,24 @@ definitions: dispute_period_in_blocks the number of blocks it takes to change a status of a state from received to finalized. during that period, any user could submit fraud proof - deployer_whitelist: - type: array - items: - type: object - properties: - address: - type: string - description: |- - address is a bech32-encoded address of the - accounts that are allowed to create a rollapp. + liveness_slash_blocks: + type: string + format: uint64 title: >- - deployer_whitelist is a list of the - - accounts that are allowed to create a rollapp and maximum number of - rollapps. - - In the case of an empty list, there are no restrictions - rollapps_enabled: - type: boolean + The time (num hub blocks) a sequencer has to post a block, before he + will be slashed + liveness_slash_interval: + type: string + format: uint64 + title: >- + The min gap (num hub blocks) between a sequence of slashes if the + sequencer continues to be down + liveness_jail_blocks: + type: string + format: uint64 + title: >- + The time (num hub blocks) a sequencer can be down after which he will + be jailed rather than slashed description: Params defines the parameters for the module. dymensionxyz.dymension.rollapp.QueryAllRollappResponse: type: object @@ -54753,82 +60767,238 @@ definitions: items: type: object properties: - rollappId: - type: string - description: |- - The unique identifier of the rollapp chain. - The rollappId follows the same standard as cosmos chain_id. - latestStateIndex: + rollapp: type: object properties: - rollappId: + rollapp_id: + type: string + description: |- + The unique identifier of the rollapp chain. + The rollapp_id follows the same standard as cosmos chain_id. + owner: + type: string + description: owner is the bech32-encoded address of the rollapp owner. + genesis_state: + title: >- + genesis_state is a partial repr of the state the hub can + expect the rollapp to be in upon genesis + type: object + properties: + transfers_enabled: + type: boolean + description: >- + If true, then full usage of the canonical ibc transfer + channel is enabled. + + Note: in v3.1.0 and prior this field marked the + completion of the 'genesis event' + + Keeping and renaming the field enables a seamless + upgrade + https://www.notion.so/dymension/ADR-x-Genesis-Bridge-Phase-2-89769aa551b5440b9ed403a101775ce1?pvs=4#89698384d815435b87393dbe45bc5a74 + + to the new genesis transfer protocol + + Note: if this field is false, ibc transfers may still be + allowed in one or either direction. + channel_id: + type: string + description: >- + channel_id will be set to the canonical IBC channel of the + rollapp. + frozen: + type: boolean + description: frozen is a boolean that indicates if the rollapp is frozen. + registeredDenoms: + type: array + items: + type: string + title: >- + registeredDenoms is a list of registered denom bases on this + rollapp + bech32_prefix: + type: string + title: unique bech32 prefix + genesis_checksum: + type: string + title: checksum used to verify integrity of the genesis file + metadata: + title: metadata is the rollapp metadata + type: object + properties: + website: + type: string + title: website is the rollapp website + description: + type: string + title: >- + description is the rollapp description. should be + limited to 512 chars + logo_data_uri: + type: string + title: >- + logo_data_uri is a base64 rep with a URI prefix to the + rollapp logo. Size limited + token_logo_data_uri: + type: string + title: >- + token_logo_data_uri is a URI to the native token logo. + Size limited + telegram: + type: string + title: telegram is the rollapp telegram link + x: + type: string + title: x is the rollapp twitter link + genesis_url: + type: string + title: genesis_url has the genesis file + display_name: + type: string + description: >- + display_name is a non semantic name for displaying on + gui etc. Size limited. + tagline: + type: string + description: >- + tagline is a non semantic tagline/catch-phrase. Size + limited. + initial_sequencer: + type: string + description: >- + initial_sequencer is an option to preset one or more + coma-separated bech32-encoded addresses of the + + sequencer(s) that are allowed to initially register and + serve for this rollapp. + + if left empty, no sequencer is allowed to register. + + if set to "*" any sequencer can register. + vm_type: + title: 'vm_type is the type of rollapp machine: EVM or WASM' + type: string + enum: + - Unspecified + - EVM + - WASM + default: Unspecified + sealed: + type: boolean + description: >- + sealed is a boolean that indicates if the immutable fields + are no longer updatable. + liveness_event_height: + type: string + format: int64 + title: >- + LivenessEventHeight is the height of an upcoming liveness + event (slash or jail) + + 0 means not set + last_state_update_height: type: string + format: int64 title: >- - rollappId is the rollapp that the sequencer belongs to and - asking to update + The LastStateUpdateHeight HUB height when the last state + update was received + title: >- + Rollapp defines a rollapp object. First, the RollApp is created + and then + + sequencers can be created and attached. The RollApp is + identified by rollappId + summary: + type: object + properties: + rollappId: + type: string + description: |- + The unique identifier of the rollapp chain. + The rollappId follows the same standard as cosmos chain_id. + latestStateIndex: + description: Defines the index of the last rollapp UpdateState. + type: object + properties: + rollappId: + type: string + title: >- + rollappId is the rollapp that the sequencer belongs to + and asking to update - it used to identify the what rollapp a StateInfo belongs + it used to identify the what rollapp a StateInfo belongs - The rollappId follows the same standard as cosmos chain_id - index: - type: string - format: uint64 + The rollappId follows the same standard as cosmos + chain_id + index: + type: string + format: uint64 + title: >- + index is a sequential increasing number, updating on + each + + state update used for indexing to a specific state info, + the first index is 1 title: >- - index is a sequential increasing number, updating on each + StateInfoIndex is the data used for indexing and retrieving + a StateInfo - state update used for indexing to a specific state info, the - first index is 1 - title: >- - StateInfoIndex is the data used for indexing and retrieving a - StateInfo + it updated and saved with every UpdateState in StateInfo. - it updated and saved with every UpdateState in StateInfo. + We use the this structure also for: - We use the this structure also for: + 1. LatestStateInfoIndex which defines the rollapps' current + (latest) index of the last UpdateState - 1. LatestStateInfoIndex which defines the rollapps' current - (latest) index of the last UpdateState + 2. LatestFinalizedStateIndex which defines the rollapps' + current (latest) index of the latest StateInfo that was + finalized + latestFinalizedStateIndex: + description: >- + Defines the index of the last rollapp UpdateState that was + finalized. + type: object + properties: + rollappId: + type: string + title: >- + rollappId is the rollapp that the sequencer belongs to + and asking to update - 2. LatestFinalizedStateIndex which defines the rollapps' current - (latest) index of the latest StateInfo that was finalized - description: Defines the index of the last rollapp UpdateState. - latestFinalizedStateIndex: - type: object - properties: - rollappId: - type: string - title: >- - rollappId is the rollapp that the sequencer belongs to and - asking to update + it used to identify the what rollapp a StateInfo belongs - it used to identify the what rollapp a StateInfo belongs + The rollappId follows the same standard as cosmos + chain_id + index: + type: string + format: uint64 + title: >- + index is a sequential increasing number, updating on + each - The rollappId follows the same standard as cosmos chain_id - index: - type: string - format: uint64 + state update used for indexing to a specific state info, + the first index is 1 title: >- - index is a sequential increasing number, updating on each - - state update used for indexing to a specific state info, the - first index is 1 - title: >- - StateInfoIndex is the data used for indexing and retrieving a - StateInfo + StateInfoIndex is the data used for indexing and retrieving + a StateInfo - it updated and saved with every UpdateState in StateInfo. + it updated and saved with every UpdateState in StateInfo. - We use the this structure also for: + We use the this structure also for: - 1. LatestStateInfoIndex which defines the rollapps' current - (latest) index of the last UpdateState + 1. LatestStateInfoIndex which defines the rollapps' current + (latest) index of the last UpdateState - 2. LatestFinalizedStateIndex which defines the rollapps' current - (latest) index of the latest StateInfo that was finalized - description: >- - Defines the index of the last rollapp UpdateState that was - finalized. - title: Rollapp summary is a compact representation of Rollapp + 2. LatestFinalizedStateIndex which defines the rollapps' + current (latest) index of the latest StateInfo that was + finalized + latestHeight: + type: string + format: uint64 + latestFinalizedHeight: + type: string + format: uint64 + title: Rollapp summary is a compact representation of Rollapp pagination: type: object properties: @@ -54903,166 +61073,36 @@ definitions: rollapp: type: object properties: - rollappId: + rollapp_id: type: string description: |- The unique identifier of the rollapp chain. - The rollappId follows the same standard as cosmos chain_id. - creator: - type: string - description: creator is the bech32-encoded address of the rollapp creator. - version: - type: string - format: uint64 - title: |- - version is the software and configuration version. - starts from 1 and increases by one on every MsgUpdateState - maxSequencers: + The rollapp_id follows the same standard as cosmos chain_id. + owner: type: string - format: uint64 - description: maxSequencers is the maximum number of sequencers. - permissionedAddresses: - type: array - items: - type: string - description: >- - permissionedAddresses is a bech32-encoded address list of the - sequencers that are allowed to serve this rollappId. - - In the case of an empty list, the rollapp is considered - permissionless. - tokenMetadata: - type: array - items: - type: object - properties: - description: - type: string - denom_units: - type: array - items: - type: object - properties: - denom: - type: string - description: >- - denom represents the string name of the given denom - unit (e.g uatom). - exponent: - type: integer - format: int64 - description: >- - exponent represents power of 10 exponent that one must - - raise the base_denom to in order to equal the given - DenomUnit's denom - - 1 denom = 10^exponent base_denom - - (e.g. with a base_denom of uatom, one can create a - DenomUnit of 'atom' with - - exponent = 6, thus: 1 atom = 10^6 uatom). - aliases: - type: array - items: - type: string - title: >- - aliases is a list of string aliases for the given - denom - description: |- - DenomUnit represents a struct that describes a given - denomination unit of the basic token. - title: >- - denom_units represents the list of DenomUnit's for a given - coin - base: - type: string - description: >- - base represents the base denom (should be the DenomUnit with - exponent = 0). - display: - type: string - description: |- - display indicates the suggested denom that should be - displayed in clients. - name: - type: string - description: 'Since: cosmos-sdk 0.43' - title: 'name defines the name of the token (eg: Cosmos Atom)' - symbol: - type: string - description: >- - symbol is the token symbol usually shown on exchanges (eg: - ATOM). This can - - be the same as the display. - - - Since: cosmos-sdk 0.43 - uri: - type: string - description: >- - URI to a document (on or off-chain) that contains additional - information. Optional. - - - Since: cosmos-sdk 0.46 - uri_hash: - type: string - description: >- - URIHash is a sha256 hash of a document pointed by URI. It's - used to verify that - - the document didn't change. Optional. - - - Since: cosmos-sdk 0.46 - description: |- - Metadata represents a struct that describes - a basic token. - title: >- - tokenMetadata is a list of TokenMetadata that are registered on - this rollapp + description: owner is the bech32-encoded address of the rollapp owner. genesis_state: title: >- genesis_state is a partial repr of the state the hub can expect the rollapp to be in upon genesis type: object properties: - genesis_accounts: - type: array - items: - type: object - properties: - amount: - title: amount of coins to be sent to the genesis address - type: object - properties: - denom: - type: string - amount: - type: string - description: >- - Coin defines a token with a denomination and an amount. + transfers_enabled: + type: boolean + description: >- + If true, then full usage of the canonical ibc transfer channel + is enabled. + Note: in v3.1.0 and prior this field marked the completion of + the 'genesis event' - NOTE: The amount field is an Int which implements the - custom method + Keeping and renaming the field enables a seamless upgrade + https://www.notion.so/dymension/ADR-x-Genesis-Bridge-Phase-2-89769aa551b5440b9ed403a101775ce1?pvs=4#89698384d815435b87393dbe45bc5a74 - signatures required by gogoproto. - address: - type: string - title: address is a bech-32 address of the genesis account - title: >- - GenesisAccount is a struct for the genesis account for the - rollapp - title: genesis_accounts is a list of token allocations - is_genesis_event: - type: boolean - title: >- - is_genesis_event is a boolean that indicates if the genesis - event has occured + to the new genesis transfer protocol + + Note: if this field is false, ibc transfers may still be + allowed in one or either direction. channel_id: type: string description: >- @@ -55078,86 +61118,181 @@ definitions: title: >- registeredDenoms is a list of registered denom bases on this rollapp + bech32_prefix: + type: string + title: unique bech32 prefix + genesis_checksum: + type: string + title: checksum used to verify integrity of the genesis file + metadata: + title: metadata is the rollapp metadata + type: object + properties: + website: + type: string + title: website is the rollapp website + description: + type: string + title: >- + description is the rollapp description. should be limited to + 512 chars + logo_data_uri: + type: string + title: >- + logo_data_uri is a base64 rep with a URI prefix to the rollapp + logo. Size limited + token_logo_data_uri: + type: string + title: >- + token_logo_data_uri is a URI to the native token logo. Size + limited + telegram: + type: string + title: telegram is the rollapp telegram link + x: + type: string + title: x is the rollapp twitter link + genesis_url: + type: string + title: genesis_url has the genesis file + display_name: + type: string + description: >- + display_name is a non semantic name for displaying on gui etc. + Size limited. + tagline: + type: string + description: tagline is a non semantic tagline/catch-phrase. Size limited. + initial_sequencer: + type: string + description: >- + initial_sequencer is an option to preset one or more + coma-separated bech32-encoded addresses of the + + sequencer(s) that are allowed to initially register and serve for + this rollapp. + + if left empty, no sequencer is allowed to register. + + if set to "*" any sequencer can register. + vm_type: + title: 'vm_type is the type of rollapp machine: EVM or WASM' + type: string + enum: + - Unspecified + - EVM + - WASM + default: Unspecified + sealed: + type: boolean + description: >- + sealed is a boolean that indicates if the immutable fields are no + longer updatable. + liveness_event_height: + type: string + format: int64 + title: >- + LivenessEventHeight is the height of an upcoming liveness event + (slash or jail) + + 0 means not set + last_state_update_height: + type: string + format: int64 + title: >- + The LastStateUpdateHeight HUB height when the last state update + was received title: >- - Rollapp defines a rollapp object. First the RollApp is created and + Rollapp defines a rollapp object. First, the RollApp is created and then sequencers can be created and attached. The RollApp is identified by rollappId - latestStateIndex: - description: Defines the index of the last rollapp UpdateState. + summary: type: object properties: rollappId: type: string - title: >- - rollappId is the rollapp that the sequencer belongs to and asking - to update + description: |- + The unique identifier of the rollapp chain. + The rollappId follows the same standard as cosmos chain_id. + latestStateIndex: + description: Defines the index of the last rollapp UpdateState. + type: object + properties: + rollappId: + type: string + title: >- + rollappId is the rollapp that the sequencer belongs to and + asking to update - it used to identify the what rollapp a StateInfo belongs + it used to identify the what rollapp a StateInfo belongs - The rollappId follows the same standard as cosmos chain_id - index: - type: string - format: uint64 + The rollappId follows the same standard as cosmos chain_id + index: + type: string + format: uint64 + title: >- + index is a sequential increasing number, updating on each + + state update used for indexing to a specific state info, the + first index is 1 title: >- - index is a sequential increasing number, updating on each + StateInfoIndex is the data used for indexing and retrieving a + StateInfo - state update used for indexing to a specific state info, the first - index is 1 - title: >- - StateInfoIndex is the data used for indexing and retrieving a - StateInfo + it updated and saved with every UpdateState in StateInfo. - it updated and saved with every UpdateState in StateInfo. + We use the this structure also for: - We use the this structure also for: + 1. LatestStateInfoIndex which defines the rollapps' current + (latest) index of the last UpdateState - 1. LatestStateInfoIndex which defines the rollapps' current (latest) - index of the last UpdateState + 2. LatestFinalizedStateIndex which defines the rollapps' current + (latest) index of the latest StateInfo that was finalized + latestFinalizedStateIndex: + description: >- + Defines the index of the last rollapp UpdateState that was + finalized. + type: object + properties: + rollappId: + type: string + title: >- + rollappId is the rollapp that the sequencer belongs to and + asking to update - 2. LatestFinalizedStateIndex which defines the rollapps' current - (latest) index of the latest StateInfo that was finalized - latestFinalizedStateIndex: - description: Defines the index of the last rollapp UpdateState that was finalized. - type: object - properties: - rollappId: - type: string - title: >- - rollappId is the rollapp that the sequencer belongs to and asking - to update + it used to identify the what rollapp a StateInfo belongs - it used to identify the what rollapp a StateInfo belongs + The rollappId follows the same standard as cosmos chain_id + index: + type: string + format: uint64 + title: >- + index is a sequential increasing number, updating on each - The rollappId follows the same standard as cosmos chain_id - index: - type: string - format: uint64 + state update used for indexing to a specific state info, the + first index is 1 title: >- - index is a sequential increasing number, updating on each - - state update used for indexing to a specific state info, the first - index is 1 - title: >- - StateInfoIndex is the data used for indexing and retrieving a - StateInfo + StateInfoIndex is the data used for indexing and retrieving a + StateInfo - it updated and saved with every UpdateState in StateInfo. + it updated and saved with every UpdateState in StateInfo. - We use the this structure also for: + We use the this structure also for: - 1. LatestStateInfoIndex which defines the rollapps' current (latest) - index of the last UpdateState + 1. LatestStateInfoIndex which defines the rollapps' current + (latest) index of the last UpdateState - 2. LatestFinalizedStateIndex which defines the rollapps' current - (latest) index of the latest StateInfo that was finalized - latestHeight: - type: string - format: uint64 - latestFinalizedHeight: - type: string - format: uint64 + 2. LatestFinalizedStateIndex which defines the rollapps' current + (latest) index of the latest StateInfo that was finalized + latestHeight: + type: string + format: uint64 + latestFinalizedHeight: + type: string + format: uint64 + title: Rollapp summary is a compact representation of Rollapp dymensionxyz.dymension.rollapp.QueryGetStateInfoResponse: type: object properties: @@ -55213,10 +61348,6 @@ definitions: DAPath: type: string title: DAPath is the description of the location on the DA layer - version: - type: string - format: uint64 - title: version is the version of the rollapp creationHeight: type: string format: uint64 @@ -55271,185 +61402,58 @@ definitions: dispute_period_in_blocks the number of blocks it takes to change a status of a state from received to finalized. during that period, any user could submit fraud proof - deployer_whitelist: - type: array - items: - type: object - properties: - address: - type: string - description: |- - address is a bech32-encoded address of the - accounts that are allowed to create a rollapp. + liveness_slash_blocks: + type: string + format: uint64 title: >- - deployer_whitelist is a list of the - - accounts that are allowed to create a rollapp and maximum number - of rollapps. - - In the case of an empty list, there are no restrictions - rollapps_enabled: - type: boolean + The time (num hub blocks) a sequencer has to post a block, before + he will be slashed + liveness_slash_interval: + type: string + format: uint64 + title: >- + The min gap (num hub blocks) between a sequence of slashes if the + sequencer continues to be down + liveness_jail_blocks: + type: string + format: uint64 + title: >- + The time (num hub blocks) a sequencer can be down after which he + will be jailed rather than slashed description: QueryParamsResponse is response type for the Query/Params RPC method. dymensionxyz.dymension.rollapp.Rollapp: type: object properties: - rollappId: + rollapp_id: type: string description: |- The unique identifier of the rollapp chain. - The rollappId follows the same standard as cosmos chain_id. - creator: - type: string - description: creator is the bech32-encoded address of the rollapp creator. - version: - type: string - format: uint64 - title: |- - version is the software and configuration version. - starts from 1 and increases by one on every MsgUpdateState - maxSequencers: + The rollapp_id follows the same standard as cosmos chain_id. + owner: type: string - format: uint64 - description: maxSequencers is the maximum number of sequencers. - permissionedAddresses: - type: array - items: - type: string - description: >- - permissionedAddresses is a bech32-encoded address list of the - sequencers that are allowed to serve this rollappId. - - In the case of an empty list, the rollapp is considered - permissionless. - tokenMetadata: - type: array - items: - type: object - properties: - description: - type: string - denom_units: - type: array - items: - type: object - properties: - denom: - type: string - description: >- - denom represents the string name of the given denom unit - (e.g uatom). - exponent: - type: integer - format: int64 - description: >- - exponent represents power of 10 exponent that one must - - raise the base_denom to in order to equal the given - DenomUnit's denom - - 1 denom = 10^exponent base_denom - - (e.g. with a base_denom of uatom, one can create a - DenomUnit of 'atom' with - - exponent = 6, thus: 1 atom = 10^6 uatom). - aliases: - type: array - items: - type: string - title: aliases is a list of string aliases for the given denom - description: |- - DenomUnit represents a struct that describes a given - denomination unit of the basic token. - title: denom_units represents the list of DenomUnit's for a given coin - base: - type: string - description: >- - base represents the base denom (should be the DenomUnit with - exponent = 0). - display: - type: string - description: |- - display indicates the suggested denom that should be - displayed in clients. - name: - type: string - description: 'Since: cosmos-sdk 0.43' - title: 'name defines the name of the token (eg: Cosmos Atom)' - symbol: - type: string - description: >- - symbol is the token symbol usually shown on exchanges (eg: - ATOM). This can - - be the same as the display. - - - Since: cosmos-sdk 0.43 - uri: - type: string - description: >- - URI to a document (on or off-chain) that contains additional - information. Optional. - - - Since: cosmos-sdk 0.46 - uri_hash: - type: string - description: >- - URIHash is a sha256 hash of a document pointed by URI. It's used - to verify that - - the document didn't change. Optional. - - - Since: cosmos-sdk 0.46 - description: |- - Metadata represents a struct that describes - a basic token. - title: >- - tokenMetadata is a list of TokenMetadata that are registered on this - rollapp + description: owner is the bech32-encoded address of the rollapp owner. genesis_state: title: >- genesis_state is a partial repr of the state the hub can expect the rollapp to be in upon genesis type: object properties: - genesis_accounts: - type: array - items: - type: object - properties: - amount: - title: amount of coins to be sent to the genesis address - type: object - properties: - denom: - type: string - amount: - type: string - description: >- - Coin defines a token with a denomination and an amount. + transfers_enabled: + type: boolean + description: >- + If true, then full usage of the canonical ibc transfer channel is + enabled. + Note: in v3.1.0 and prior this field marked the completion of the + 'genesis event' - NOTE: The amount field is an Int which implements the custom - method + Keeping and renaming the field enables a seamless upgrade + https://www.notion.so/dymension/ADR-x-Genesis-Bridge-Phase-2-89769aa551b5440b9ed403a101775ce1?pvs=4#89698384d815435b87393dbe45bc5a74 - signatures required by gogoproto. - address: - type: string - title: address is a bech-32 address of the genesis account - title: >- - GenesisAccount is a struct for the genesis account for the - rollapp - title: genesis_accounts is a list of token allocations - is_genesis_event: - type: boolean - title: >- - is_genesis_event is a boolean that indicates if the genesis event - has occured + to the new genesis transfer protocol + + Note: if this field is false, ibc transfers may still be allowed + in one or either direction. channel_id: type: string description: channel_id will be set to the canonical IBC channel of the rollapp. @@ -55461,48 +61465,158 @@ definitions: items: type: string title: registeredDenoms is a list of registered denom bases on this rollapp + bech32_prefix: + type: string + title: unique bech32 prefix + genesis_checksum: + type: string + title: checksum used to verify integrity of the genesis file + metadata: + title: metadata is the rollapp metadata + type: object + properties: + website: + type: string + title: website is the rollapp website + description: + type: string + title: >- + description is the rollapp description. should be limited to 512 + chars + logo_data_uri: + type: string + title: >- + logo_data_uri is a base64 rep with a URI prefix to the rollapp + logo. Size limited + token_logo_data_uri: + type: string + title: >- + token_logo_data_uri is a URI to the native token logo. Size + limited + telegram: + type: string + title: telegram is the rollapp telegram link + x: + type: string + title: x is the rollapp twitter link + genesis_url: + type: string + title: genesis_url has the genesis file + display_name: + type: string + description: >- + display_name is a non semantic name for displaying on gui etc. + Size limited. + tagline: + type: string + description: tagline is a non semantic tagline/catch-phrase. Size limited. + initial_sequencer: + type: string + description: >- + initial_sequencer is an option to preset one or more coma-separated + bech32-encoded addresses of the + + sequencer(s) that are allowed to initially register and serve for this + rollapp. + + if left empty, no sequencer is allowed to register. + + if set to "*" any sequencer can register. + vm_type: + title: 'vm_type is the type of rollapp machine: EVM or WASM' + type: string + enum: + - Unspecified + - EVM + - WASM + default: Unspecified + sealed: + type: boolean + description: >- + sealed is a boolean that indicates if the immutable fields are no + longer updatable. + liveness_event_height: + type: string + format: int64 + title: >- + LivenessEventHeight is the height of an upcoming liveness event (slash + or jail) + + 0 means not set + last_state_update_height: + type: string + format: int64 + title: >- + The LastStateUpdateHeight HUB height when the last state update was + received title: >- - Rollapp defines a rollapp object. First the RollApp is created and then + Rollapp defines a rollapp object. First, the RollApp is created and then sequencers can be created and attached. The RollApp is identified by rollappId + dymensionxyz.dymension.rollapp.Rollapp.VMType: + type: string + enum: + - Unspecified + - EVM + - WASM + default: Unspecified dymensionxyz.dymension.rollapp.RollappGenesisState: type: object properties: - genesis_accounts: - type: array - items: - type: object - properties: - amount: - title: amount of coins to be sent to the genesis address - type: object - properties: - denom: - type: string - amount: - type: string - description: >- - Coin defines a token with a denomination and an amount. + transfers_enabled: + type: boolean + description: >- + If true, then full usage of the canonical ibc transfer channel is + enabled. + Note: in v3.1.0 and prior this field marked the completion of the + 'genesis event' - NOTE: The amount field is an Int which implements the custom - method + Keeping and renaming the field enables a seamless upgrade + https://www.notion.so/dymension/ADR-x-Genesis-Bridge-Phase-2-89769aa551b5440b9ed403a101775ce1?pvs=4#89698384d815435b87393dbe45bc5a74 - signatures required by gogoproto. - address: - type: string - title: address is a bech-32 address of the genesis account - title: GenesisAccount is a struct for the genesis account for the rollapp - title: genesis_accounts is a list of token allocations - is_genesis_event: - type: boolean - title: >- - is_genesis_event is a boolean that indicates if the genesis event has - occured + to the new genesis transfer protocol + + Note: if this field is false, ibc transfers may still be allowed in + one or either direction. title: >- RollappGenesisState is a partial repr of the state the hub can expect the rollapp to be in upon genesis + dymensionxyz.dymension.rollapp.RollappMetadata: + type: object + properties: + website: + type: string + title: website is the rollapp website + description: + type: string + title: description is the rollapp description. should be limited to 512 chars + logo_data_uri: + type: string + title: >- + logo_data_uri is a base64 rep with a URI prefix to the rollapp logo. + Size limited + token_logo_data_uri: + type: string + title: token_logo_data_uri is a URI to the native token logo. Size limited + telegram: + type: string + title: telegram is the rollapp telegram link + x: + type: string + title: x is the rollapp twitter link + genesis_url: + type: string + title: genesis_url has the genesis file + display_name: + type: string + description: >- + display_name is a non semantic name for displaying on gui etc. Size + limited. + tagline: + type: string + description: tagline is a non semantic tagline/catch-phrase. Size limited. dymensionxyz.dymension.rollapp.RollappSummary: type: object properties: @@ -55512,6 +61626,7 @@ definitions: The unique identifier of the rollapp chain. The rollappId follows the same standard as cosmos chain_id. latestStateIndex: + description: Defines the index of the last rollapp UpdateState. type: object properties: rollappId: @@ -55544,8 +61659,8 @@ definitions: 2. LatestFinalizedStateIndex which defines the rollapps' current (latest) index of the latest StateInfo that was finalized - description: Defines the index of the last rollapp UpdateState. latestFinalizedStateIndex: + description: Defines the index of the last rollapp UpdateState that was finalized. type: object properties: rollappId: @@ -55578,7 +61693,12 @@ definitions: 2. LatestFinalizedStateIndex which defines the rollapps' current (latest) index of the latest StateInfo that was finalized - description: Defines the index of the last rollapp UpdateState that was finalized. + latestHeight: + type: string + format: uint64 + latestFinalizedHeight: + type: string + format: uint64 title: Rollapp summary is a compact representation of Rollapp dymensionxyz.dymension.rollapp.StateInfo: type: object @@ -55632,10 +61752,6 @@ definitions: DAPath: type: string title: DAPath is the description of the location on the DA layer - version: - type: string - format: uint64 - title: version is the version of the rollapp creationHeight: type: string format: uint64 @@ -55708,111 +61824,18 @@ definitions: 2. LatestFinalizedStateIndex which defines the rollapps' current (latest) index of the latest StateInfo that was finalized - dymensionxyz.dymension.rollapp.TokenMetadata: + dymensionxyz.dymension.sequencer.ContactDetails: type: object properties: - description: - type: string - denom_units: - type: array - items: - type: object - properties: - denom: - type: string - description: >- - denom represents the string name of the given denom unit (e.g - uatom). - exponent: - type: integer - format: int64 - description: >- - exponent represents power of 10 exponent that one must - - raise the base_denom to in order to equal the given DenomUnit's - denom - - 1 denom = 10^exponent base_denom - - (e.g. with a base_denom of uatom, one can create a DenomUnit of - 'atom' with - - exponent = 6, thus: 1 atom = 10^6 uatom). - aliases: - type: array - items: - type: string - title: aliases is a list of string aliases for the given denom - description: |- - DenomUnit represents a struct that describes a given - denomination unit of the basic token. - title: denom_units represents the list of DenomUnit's for a given coin - base: - type: string - description: >- - base represents the base denom (should be the DenomUnit with exponent - = 0). - display: - type: string - description: |- - display indicates the suggested denom that should be - displayed in clients. - name: - type: string - description: 'Since: cosmos-sdk 0.43' - title: 'name defines the name of the token (eg: Cosmos Atom)' - symbol: - type: string - description: >- - symbol is the token symbol usually shown on exchanges (eg: ATOM). This - can - - be the same as the display. - - - Since: cosmos-sdk 0.43 - uri: - type: string - description: >- - URI to a document (on or off-chain) that contains additional - information. Optional. - - - Since: cosmos-sdk 0.46 - uri_hash: - type: string - description: >- - URIHash is a sha256 hash of a document pointed by URI. It's used to - verify that - - the document didn't change. Optional. - - - Since: cosmos-sdk 0.46 - description: |- - Metadata represents a struct that describes - a basic token. - dymensionxyz.dymension.sequencer.Description: - type: object - properties: - moniker: - type: string - description: moniker defines a human-readable name for the sequencer. - identity: - type: string - description: >- - identity defines an optional identity signature (ex. UPort or - Keybase). website: type: string - description: website defines an optional website link. - securityContact: + title: website URL + telegram: type: string - description: securityContact defines an optional email for security contact. - details: + title: telegram link + x: type: string - description: details define other optional details. - description: Description defines a sequencer description. + title: twitter link dymensionxyz.dymension.sequencer.OperatingStatus: type: string enum: @@ -55844,6 +61867,11 @@ definitions: unbonding_time: type: string description: unbonding_time is the time duration of unbonding. + liveness_slash_multiplier: + type: string + description: >- + LivenessSlashMultiplier multiplies with the tokens of the slashed + sequencer to compute the burn amount. description: Params defines the parameters for the module. dymensionxyz.dymension.sequencer.QueryGetSequencerResponse: type: object @@ -55851,11 +61879,11 @@ definitions: sequencer: type: object properties: - sequencerAddress: + address: type: string description: >- - sequencerAddress is the bech32-encoded address of the sequencer - account which is the account that the message was sent from. + address is the bech32-encoded address of the sequencer account + which is the account that the message was sent from. dymintPubKey: type: object properties: @@ -56029,29 +62057,80 @@ definitions: rollappId: type: string description: rollappId defines the rollapp to which the sequencer belongs. - description: - description: description defines the descriptive terms for the sequencer. + metadata: + description: metadata defines the extra information for the sequencer. type: object properties: moniker: type: string description: moniker defines a human-readable name for the sequencer. - identity: + details: type: string - description: >- - identity defines an optional identity signature (ex. UPort or - Keybase). - website: + description: details define other optional details. + p2p_seeds: + type: array + items: + type: string + title: bootstrap nodes list + rpcs: + type: array + items: + type: string + title: RPCs list + evm_rpcs: + type: array + items: + type: string + title: evm RPCs list + rest_api_urls: + type: array + items: + type: string + title: REST API URLs + explorer_url: type: string - description: website defines an optional website link. - securityContact: + title: block explorer URL + genesis_urls: + type: array + items: + type: string + title: genesis URLs + contact_details: + title: contact details + type: object + properties: + website: + type: string + title: website URL + telegram: + type: string + title: telegram link + x: + type: string + title: twitter link + extra_data: type: string - description: >- - securityContact defines an optional email for security - contact. - details: + format: byte + title: json dump the sequencer can add (limited by size) + snapshots: + type: array + items: + type: object + properties: + snapshot_url: + type: string + title: the snapshot url + height: + type: string + format: uint64 + title: The snapshot height + checksum: + type: string + title: sha-256 checksum value for the snapshot file + title: snapshots of the sequencer + gas_price: type: string - description: details define other optional details. + title: gas_price defines the value for each gas unit jailed: type: boolean description: >- @@ -56112,11 +62191,11 @@ definitions: items: type: object properties: - sequencerAddress: + address: type: string description: >- - sequencerAddress is the bech32-encoded address of the sequencer - account which is the account that the message was sent from. + address is the bech32-encoded address of the sequencer account + which is the account that the message was sent from. dymintPubKey: type: object properties: @@ -56294,29 +62373,80 @@ definitions: rollappId: type: string description: rollappId defines the rollapp to which the sequencer belongs. - description: - description: description defines the descriptive terms for the sequencer. + metadata: + description: metadata defines the extra information for the sequencer. type: object properties: moniker: type: string description: moniker defines a human-readable name for the sequencer. - identity: + details: type: string - description: >- - identity defines an optional identity signature (ex. UPort - or Keybase). - website: + description: details define other optional details. + p2p_seeds: + type: array + items: + type: string + title: bootstrap nodes list + rpcs: + type: array + items: + type: string + title: RPCs list + evm_rpcs: + type: array + items: + type: string + title: evm RPCs list + rest_api_urls: + type: array + items: + type: string + title: REST API URLs + explorer_url: type: string - description: website defines an optional website link. - securityContact: + title: block explorer URL + genesis_urls: + type: array + items: + type: string + title: genesis URLs + contact_details: + title: contact details + type: object + properties: + website: + type: string + title: website URL + telegram: + type: string + title: telegram link + x: + type: string + title: twitter link + extra_data: type: string - description: >- - securityContact defines an optional email for security - contact. - details: + format: byte + title: json dump the sequencer can add (limited by size) + snapshots: + type: array + items: + type: object + properties: + snapshot_url: + type: string + title: the snapshot url + height: + type: string + format: uint64 + title: The snapshot height + checksum: + type: string + title: sha-256 checksum value for the snapshot file + title: snapshots of the sequencer + gas_price: type: string - description: details define other optional details. + title: gas_price defines the value for each gas unit jailed: type: boolean description: >- @@ -56377,11 +62507,11 @@ definitions: items: type: object properties: - sequencerAddress: + address: type: string description: >- - sequencerAddress is the bech32-encoded address of the sequencer - account which is the account that the message was sent from. + address is the bech32-encoded address of the sequencer account + which is the account that the message was sent from. dymintPubKey: type: object properties: @@ -56559,29 +62689,80 @@ definitions: rollappId: type: string description: rollappId defines the rollapp to which the sequencer belongs. - description: - description: description defines the descriptive terms for the sequencer. + metadata: + description: metadata defines the extra information for the sequencer. type: object properties: moniker: type: string description: moniker defines a human-readable name for the sequencer. - identity: + details: type: string - description: >- - identity defines an optional identity signature (ex. UPort - or Keybase). - website: + description: details define other optional details. + p2p_seeds: + type: array + items: + type: string + title: bootstrap nodes list + rpcs: + type: array + items: + type: string + title: RPCs list + evm_rpcs: + type: array + items: + type: string + title: evm RPCs list + rest_api_urls: + type: array + items: + type: string + title: REST API URLs + explorer_url: type: string - description: website defines an optional website link. - securityContact: + title: block explorer URL + genesis_urls: + type: array + items: + type: string + title: genesis URLs + contact_details: + title: contact details + type: object + properties: + website: + type: string + title: website URL + telegram: + type: string + title: telegram link + x: + type: string + title: twitter link + extra_data: type: string - description: >- - securityContact defines an optional email for security - contact. - details: + format: byte + title: json dump the sequencer can add (limited by size) + snapshots: + type: array + items: + type: object + properties: + snapshot_url: + type: string + title: the snapshot url + height: + type: string + format: uint64 + title: The snapshot height + checksum: + type: string + title: sha-256 checksum value for the snapshot file + title: snapshots of the sequencer + gas_price: type: string - description: details define other optional details. + title: gas_price defines the value for each gas unit jailed: type: boolean description: >- @@ -56659,6 +62840,11 @@ definitions: unbonding_time: type: string description: unbonding_time is the time duration of unbonding. + liveness_slash_multiplier: + type: string + description: >- + LivenessSlashMultiplier multiplies with the tokens of the slashed + sequencer to compute the burn amount. description: QueryParamsResponse is response type for the Query/Params RPC method. dymensionxyz.dymension.sequencer.QuerySequencersResponse: type: object @@ -56668,11 +62854,11 @@ definitions: items: type: object properties: - sequencerAddress: + address: type: string description: >- - sequencerAddress is the bech32-encoded address of the sequencer - account which is the account that the message was sent from. + address is the bech32-encoded address of the sequencer account + which is the account that the message was sent from. dymintPubKey: type: object properties: @@ -56850,29 +63036,80 @@ definitions: rollappId: type: string description: rollappId defines the rollapp to which the sequencer belongs. - description: - description: description defines the descriptive terms for the sequencer. + metadata: + description: metadata defines the extra information for the sequencer. type: object properties: moniker: type: string description: moniker defines a human-readable name for the sequencer. - identity: + details: type: string - description: >- - identity defines an optional identity signature (ex. UPort - or Keybase). - website: + description: details define other optional details. + p2p_seeds: + type: array + items: + type: string + title: bootstrap nodes list + rpcs: + type: array + items: + type: string + title: RPCs list + evm_rpcs: + type: array + items: + type: string + title: evm RPCs list + rest_api_urls: + type: array + items: + type: string + title: REST API URLs + explorer_url: type: string - description: website defines an optional website link. - securityContact: + title: block explorer URL + genesis_urls: + type: array + items: + type: string + title: genesis URLs + contact_details: + title: contact details + type: object + properties: + website: + type: string + title: website URL + telegram: + type: string + title: telegram link + x: + type: string + title: twitter link + extra_data: type: string - description: >- - securityContact defines an optional email for security - contact. - details: + format: byte + title: json dump the sequencer can add (limited by size) + snapshots: + type: array + items: + type: object + properties: + snapshot_url: + type: string + title: the snapshot url + height: + type: string + format: uint64 + title: The snapshot height + checksum: + type: string + title: sha-256 checksum value for the snapshot file + title: snapshots of the sequencer + gas_price: type: string - description: details define other optional details. + title: gas_price defines the value for each gas unit jailed: type: boolean description: >- @@ -56954,11 +63191,11 @@ definitions: dymensionxyz.dymension.sequencer.Sequencer: type: object properties: - sequencerAddress: + address: type: string description: >- - sequencerAddress is the bech32-encoded address of the sequencer - account which is the account that the message was sent from. + address is the bech32-encoded address of the sequencer account which + is the account that the message was sent from. dymintPubKey: type: object properties: @@ -57126,27 +63363,80 @@ definitions: rollappId: type: string description: rollappId defines the rollapp to which the sequencer belongs. - description: - description: description defines the descriptive terms for the sequencer. + metadata: + description: metadata defines the extra information for the sequencer. type: object properties: moniker: type: string description: moniker defines a human-readable name for the sequencer. - identity: + details: type: string - description: >- - identity defines an optional identity signature (ex. UPort or - Keybase). - website: + description: details define other optional details. + p2p_seeds: + type: array + items: + type: string + title: bootstrap nodes list + rpcs: + type: array + items: + type: string + title: RPCs list + evm_rpcs: + type: array + items: + type: string + title: evm RPCs list + rest_api_urls: + type: array + items: + type: string + title: REST API URLs + explorer_url: type: string - description: website defines an optional website link. - securityContact: + title: block explorer URL + genesis_urls: + type: array + items: + type: string + title: genesis URLs + contact_details: + title: contact details + type: object + properties: + website: + type: string + title: website URL + telegram: + type: string + title: telegram link + x: + type: string + title: twitter link + extra_data: type: string - description: securityContact defines an optional email for security contact. - details: + format: byte + title: json dump the sequencer can add (limited by size) + snapshots: + type: array + items: + type: object + properties: + snapshot_url: + type: string + title: the snapshot url + height: + type: string + format: uint64 + title: The snapshot height + checksum: + type: string + title: sha-256 checksum value for the snapshot file + title: snapshots of the sequencer + gas_price: type: string - description: details define other optional details. + title: gas_price defines the value for each gas unit jailed: type: boolean description: >- @@ -57196,6 +63486,93 @@ definitions: (sequencerAddress). The sequencer could be attached to only one rollapp (rollappId). + dymensionxyz.dymension.sequencer.SequencerMetadata: + type: object + properties: + moniker: + type: string + description: moniker defines a human-readable name for the sequencer. + details: + type: string + description: details define other optional details. + p2p_seeds: + type: array + items: + type: string + title: bootstrap nodes list + rpcs: + type: array + items: + type: string + title: RPCs list + evm_rpcs: + type: array + items: + type: string + title: evm RPCs list + rest_api_urls: + type: array + items: + type: string + title: REST API URLs + explorer_url: + type: string + title: block explorer URL + genesis_urls: + type: array + items: + type: string + title: genesis URLs + contact_details: + title: contact details + type: object + properties: + website: + type: string + title: website URL + telegram: + type: string + title: telegram link + x: + type: string + title: twitter link + extra_data: + type: string + format: byte + title: json dump the sequencer can add (limited by size) + snapshots: + type: array + items: + type: object + properties: + snapshot_url: + type: string + title: the snapshot url + height: + type: string + format: uint64 + title: The snapshot height + checksum: + type: string + title: sha-256 checksum value for the snapshot file + title: snapshots of the sequencer + gas_price: + type: string + title: gas_price defines the value for each gas unit + description: Metadata defines rollapp/sequencer extra information. + dymensionxyz.dymension.sequencer.SnapshotInfo: + type: object + properties: + snapshot_url: + type: string + title: the snapshot url + height: + type: string + format: uint64 + title: The snapshot height + checksum: + type: string + title: sha-256 checksum value for the snapshot file dymensionxyz.dymension.streamer.ActiveStreamsResponse: type: object properties: @@ -57289,6 +63666,11 @@ definitions: signatures required by gogoproto. title: distributed_coins are coins that have been distributed already + sponsored: + type: boolean + description: >- + Sponsored indicates if the stream is based on the sponsorship + distribution. description: >- Stream is an object that stores and distributes yields to recipients who @@ -57448,6 +63830,11 @@ definitions: NOTE: The amount field is an Int which implements the custom method signatures required by gogoproto. title: distributed_coins are coins that have been distributed already + sponsored: + type: boolean + description: >- + Sponsored indicates if the stream is based on the sponsorship + distribution. description: >- Stream is an object that stores and distributes yields to recipients who @@ -57546,6 +63933,11 @@ definitions: signatures required by gogoproto. title: distributed_coins are coins that have been distributed already + sponsored: + type: boolean + description: >- + Sponsored indicates if the stream is based on the sponsorship + distribution. description: >- Stream is an object that stores and distributes yields to recipients who @@ -57648,6 +64040,11 @@ definitions: signatures required by gogoproto. title: distributed_coins are coins that have been distributed already + sponsored: + type: boolean + description: >- + Sponsored indicates if the stream is based on the sponsorship + distribution. description: >- Stream is an object that stores and distributes yields to recipients who @@ -57777,6 +64174,11 @@ definitions: signatures required by gogoproto. title: distributed_coins are coins that have been distributed already + sponsored: + type: boolean + description: >- + Sponsored indicates if the stream is based on the sponsorship + distribution. description: >- Stream is an object that stores and distributes yields to recipients who @@ -62447,6 +68849,24 @@ definitions: chain. description: QueryParamsResponse is the response type for the Query/Params RPC method. + ibc.applications.transfer.v1.QueryTotalEscrowForDenomResponse: + type: object + properties: + amount: + type: object + properties: + denom: + type: string + amount: + type: string + description: |- + Coin defines a token with a denomination and an amount. + + NOTE: The amount field is an Int which implements the custom method + signatures required by gogoproto. + description: >- + QueryTotalEscrowForDenomResponse is the response type for + TotalEscrowForDenom RPC method. ibc.core.client.v1.ConsensusStateWithHeight: type: object properties: @@ -62829,7 +69249,14 @@ definitions: type: array items: type: string - description: allowed_clients defines the list of allowed client state types. + description: >- + allowed_clients defines the list of allowed client state types which + can be created + + and interacted with. If a client type is removed from the allowed + clients list, usage + + of this client will be disabled until it is added again to the list. description: Params defines the set of IBC light client parameters. ibc.core.client.v1.QueryClientParamsResponse: type: object @@ -62842,7 +69269,15 @@ definitions: type: array items: type: string - description: allowed_clients defines the list of allowed client state types. + description: >- + allowed_clients defines the list of allowed client state types + which can be created + + and interacted with. If a client type is removed from the allowed + clients list, usage + + of this client will be disabled until it is added again to the + list. description: >- QueryClientParamsResponse is the response type for the Query/ClientParams RPC @@ -66649,6 +73084,189 @@ definitions: Since: cosmos-sdk 0.46 + cosmos.auth.v1beta1.BaseAccount: + type: object + properties: + address: + type: string + pub_key: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of the + serialized + + protocol buffer message. This string must contain at least + + one "/" character. The last segment of the URL's path must + represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in a + canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary all types + that they + + expect it to use in the context of Any. However, for URLs which + use the + + scheme `http`, `https`, or no scheme, one can optionally set up a + type + + server that maps type URLs to message definitions as follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in the + official + + protobuf release, and it is not used for type URLs beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above specified + type. + description: >- + `Any` contains an arbitrary serialized protocol buffer message along + with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values in the + form + + of utility functions or additional generated methods of the Any type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + // or ... + if (any.isSameTypeAs(Foo.getDefaultInstance())) { + foo = any.unpack(Foo.getDefaultInstance()); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := anypb.New(foo) + if err != nil { + ... + } + ... + foo := &pb.Foo{} + if err := any.UnmarshalTo(foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by default use + + 'type.googleapis.com/full.type.name' as the type URL and the unpack + + methods only use the fully qualified type name after the last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield type + + name "y.z". + + + JSON + + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with an + + additional field `@type` which contains the type URL. Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom JSON + + representation, that representation will be embedded adding a field + + `value` which holds the custom JSON in addition to the `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + account_number: + type: string + format: uint64 + sequence: + type: string + format: uint64 + description: >- + BaseAccount defines a base account type. It contains all the necessary + fields + + for basic account functionality. Any custom account type should extend + this + + type for additional functionality (e.g. vesting). cosmos.auth.v1beta1.Bech32PrefixResponse: type: object properties: @@ -66686,6 +73304,195 @@ definitions: title: >- QueryAccountAddressByIDResponse is the response type for AccountAddressByID rpc method + cosmos.auth.v1beta1.QueryAccountInfoResponse: + type: object + properties: + info: + description: info is the account info which is represented by BaseAccount. + type: object + properties: + address: + type: string + pub_key: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of the + serialized + + protocol buffer message. This string must contain at least + + one "/" character. The last segment of the URL's path must + represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in a + canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary all + types that they + + expect it to use in the context of Any. However, for URLs + which use the + + scheme `http`, `https`, or no scheme, one can optionally set + up a type + + server that maps type URLs to message definitions as follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based on + the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in the + official + + protobuf release, and it is not used for type URLs beginning + with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) might + be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer message + along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values in the + form + + of utility functions or additional generated methods of the Any + type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + // or ... + if (any.isSameTypeAs(Foo.getDefaultInstance())) { + foo = any.unpack(Foo.getDefaultInstance()); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := anypb.New(foo) + if err != nil { + ... + } + ... + foo := &pb.Foo{} + if err := any.UnmarshalTo(foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by default use + + 'type.googleapis.com/full.type.name' as the type URL and the + unpack + + methods only use the fully qualified type name after the last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield type + + name "y.z". + + + JSON + + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with an + + additional field `@type` which contains the type URL. Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom JSON + + representation, that representation will be embedded adding a + field + + `value` which holds the custom JSON in addition to the `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + account_number: + type: string + format: uint64 + sequence: + type: string + format: uint64 + description: |- + QueryAccountInfoResponse is the Query/AccountInfo response type. + + Since: cosmos-sdk 0.47 cosmos.auth.v1beta1.QueryAccountResponse: type: object properties: @@ -68590,6 +75397,18 @@ definitions: denom is sendable). + description: >- + Deprecated: Use of SendEnabled in params is deprecated. + + For genesis, use the newly added send_enabled field in the genesis + object. + + Storage, lookup, and manipulation of this information is now in the + keeper. + + + As of cosmos-sdk 0.47, this only exists for backwards compatibility of + genesis files. default_send_enabled: type: boolean description: Params defines the parameters for the bank module. @@ -68943,12 +75762,89 @@ definitions: denom is sendable). + description: >- + Deprecated: Use of SendEnabled in params is deprecated. + + For genesis, use the newly added send_enabled field in the genesis + object. + + Storage, lookup, and manipulation of this information is now in + the keeper. + + + As of cosmos-sdk 0.47, this only exists for backwards + compatibility of genesis files. default_send_enabled: type: boolean description: Params defines the parameters for the bank module. description: >- QueryParamsResponse defines the response type for querying x/bank parameters. + cosmos.bank.v1beta1.QuerySendEnabledResponse: + type: object + properties: + send_enabled: + type: array + items: + type: object + properties: + denom: + type: string + enabled: + type: boolean + description: >- + SendEnabled maps coin denom to a send_enabled status (whether a + denom is + + sendable). + pagination: + description: |- + pagination defines the pagination in the response. This field is only + populated if the denoms field in the request is empty. + type: object + properties: + next_key: + type: string + format: byte + description: |- + next_key is the key to be passed to PageRequest.key to + query the next page most efficiently. It will be empty if + there are no more results. + total: + type: string + format: uint64 + title: >- + total is total number of results available if + PageRequest.count_total + + was set, its value is undefined otherwise + description: |- + QuerySendEnabledResponse defines the RPC response of a SendEnable query. + + Since: cosmos-sdk 0.47 + cosmos.bank.v1beta1.QuerySpendableBalanceByDenomResponse: + type: object + properties: + balance: + type: object + properties: + denom: + type: string + amount: + type: string + description: |- + Coin defines a token with a denomination and an amount. + + NOTE: The amount field is an Int which implements the custom method + signatures required by gogoproto. + description: >- + QuerySpendableBalanceByDenomResponse defines the gRPC response structure + for + + querying an account's spendable balance for a specific denom. + + + Since: cosmos-sdk 0.47 cosmos.bank.v1beta1.QuerySpendableBalancesResponse: type: object properties: @@ -69108,8 +76004,18 @@ definitions: type: string base_proposer_reward: type: string + description: >- + Deprecated: The base_proposer_reward field is deprecated and is no + longer used + + in the x/distribution module's reward mechanism. bonus_proposer_reward: type: string + description: >- + Deprecated: The bonus_proposer_reward field is deprecated and is no + longer used + + in the x/distribution module's reward mechanism. withdraw_addr_enabled: type: boolean description: Params defines the set of params for the distribution module. @@ -69238,8 +76144,18 @@ definitions: type: string base_proposer_reward: type: string + description: >- + Deprecated: The base_proposer_reward field is deprecated and is no + longer used + + in the x/distribution module's reward mechanism. bonus_proposer_reward: type: string + description: >- + Deprecated: The bonus_proposer_reward field is deprecated and is + no longer used + + in the x/distribution module's reward mechanism. withdraw_addr_enabled: type: boolean description: QueryParamsResponse is the response type for the Query/Params RPC method. @@ -69247,7 +76163,7 @@ definitions: type: object properties: commission: - description: commission defines the commision the validator received. + description: commission defines the commission the validator received. type: object properties: commission: @@ -69271,6 +76187,45 @@ definitions: title: |- QueryValidatorCommissionResponse is the response type for the Query/ValidatorCommission RPC method + cosmos.distribution.v1beta1.QueryValidatorDistributionInfoResponse: + type: object + properties: + operator_address: + type: string + description: operator_address defines the validator operator address. + self_bond_rewards: + type: array + items: + type: object + properties: + denom: + type: string + amount: + type: string + description: |- + DecCoin defines a token with a denomination and a decimal amount. + + NOTE: The amount field is an Dec which implements the custom method + signatures required by gogoproto. + description: self_bond_rewards defines the self delegations rewards. + commission: + type: array + items: + type: object + properties: + denom: + type: string + amount: + type: string + description: |- + DecCoin defines a token with a denomination and a decimal amount. + + NOTE: The amount field is an Dec which implements the custom method + signatures required by gogoproto. + description: commission defines the commission the validator received. + description: >- + QueryValidatorDistributionInfoResponse is the response type for the + Query/ValidatorDistributionInfo RPC method. cosmos.distribution.v1beta1.QueryValidatorOutstandingRewardsResponse: type: object properties: @@ -69786,8 +76741,10 @@ definitions: proposal_id: type: string format: uint64 + description: proposal_id defines the unique id of the proposal. depositor: type: string + description: depositor defines the deposit addresses from the proposals. amount: type: array items: @@ -69802,6 +76759,7 @@ definitions: NOTE: The amount field is an Int which implements the custom method signatures required by gogoproto. + description: amount to be deposited by depositor. description: |- Deposit defines an amount deposited by an account address to an active proposal. @@ -69828,7 +76786,8 @@ definitions: description: >- Maximum period for Atom holders to deposit on a proposal. Initial value: 2 - months. + + months. description: DepositParams defines the params for deposits on governance proposals. cosmos.gov.v1beta1.Proposal: type: object @@ -69836,6 +76795,7 @@ definitions: proposal_id: type: string format: uint64 + description: proposal_id defines the unique id of the proposal. content: type: object properties: @@ -70001,6 +76961,7 @@ definitions: "value": "1.212s" } status: + description: status defines the proposal status. type: string enum: - PROPOSAL_STATUS_UNSPECIFIED @@ -70010,20 +76971,6 @@ definitions: - PROPOSAL_STATUS_REJECTED - PROPOSAL_STATUS_FAILED default: PROPOSAL_STATUS_UNSPECIFIED - description: |- - ProposalStatus enumerates the valid statuses of a proposal. - - - PROPOSAL_STATUS_UNSPECIFIED: PROPOSAL_STATUS_UNSPECIFIED defines the default proposal status. - - PROPOSAL_STATUS_DEPOSIT_PERIOD: PROPOSAL_STATUS_DEPOSIT_PERIOD defines a proposal status during the deposit - period. - - PROPOSAL_STATUS_VOTING_PERIOD: PROPOSAL_STATUS_VOTING_PERIOD defines a proposal status during the voting - period. - - PROPOSAL_STATUS_PASSED: PROPOSAL_STATUS_PASSED defines a proposal status of a proposal that has - passed. - - PROPOSAL_STATUS_REJECTED: PROPOSAL_STATUS_REJECTED defines a proposal status of a proposal that has - been rejected. - - PROPOSAL_STATUS_FAILED: PROPOSAL_STATUS_FAILED defines a proposal status of a proposal that has - failed. final_tally_result: description: |- final_tally_result is the final tally result of the proposal. When @@ -70033,18 +76980,24 @@ definitions: properties: 'yes': type: string + description: yes is the number of yes votes on a proposal. abstain: type: string + description: abstain is the number of abstain votes on a proposal. 'no': type: string + description: no is the number of no votes on a proposal. no_with_veto: type: string + description: no_with_veto is the number of no with veto votes on a proposal. submit_time: type: string format: date-time + description: submit_time is the time of proposal submission. deposit_end_time: type: string format: date-time + description: deposit_end_time is the end time for deposition. total_deposit: type: array items: @@ -70059,12 +77012,15 @@ definitions: NOTE: The amount field is an Int which implements the custom method signatures required by gogoproto. + description: total_deposit is the total deposit on the proposal. voting_start_time: type: string format: date-time + description: voting_start_time is the starting time to vote on a proposal. voting_end_time: type: string format: date-time + description: voting_end_time is the end time of voting on a proposal. description: Proposal defines the core field members of a governance proposal. cosmos.gov.v1beta1.ProposalStatus: type: string @@ -70099,8 +77055,10 @@ definitions: proposal_id: type: string format: uint64 + description: proposal_id defines the unique id of the proposal. depositor: type: string + description: depositor defines the deposit addresses from the proposals. amount: type: array items: @@ -70118,6 +77076,7 @@ definitions: method signatures required by gogoproto. + description: amount to be deposited by depositor. description: |- Deposit defines an amount deposited by an account address to an active proposal. @@ -70135,8 +77094,10 @@ definitions: proposal_id: type: string format: uint64 + description: proposal_id defines the unique id of the proposal. depositor: type: string + description: depositor defines the deposit addresses from the proposals. amount: type: array items: @@ -70154,11 +77115,13 @@ definitions: method signatures required by gogoproto. + description: amount to be deposited by depositor. description: >- Deposit defines an amount deposited by an account address to an active proposal. + description: deposits defines the requested deposits. pagination: description: pagination defines the pagination in the response. type: object @@ -70190,7 +77153,7 @@ definitions: properties: voting_period: type: string - description: Length of the voting period. + description: Duration of the voting period. deposit_params: description: deposit_params defines the parameters related to deposit. type: object @@ -70218,7 +77181,8 @@ definitions: description: >- Maximum period for Atom holders to deposit on a proposal. Initial value: 2 - months. + + months. tally_params: description: tally_params defines the parameters related to tally. type: object @@ -70229,7 +77193,8 @@ definitions: description: >- Minimum percentage of total stake needed to vote for a result to be - considered valid. + + considered valid. threshold: type: string format: byte @@ -70242,7 +77207,8 @@ definitions: description: >- Minimum value of Veto votes to Total votes ratio for proposal to be - vetoed. Default value: 1/3. + + vetoed. Default value: 1/3. description: QueryParamsResponse is the response type for the Query/Params RPC method. cosmos.gov.v1beta1.QueryProposalResponse: type: object @@ -70253,6 +77219,7 @@ definitions: proposal_id: type: string format: uint64 + description: proposal_id defines the unique id of the proposal. content: type: object properties: @@ -70424,6 +77391,7 @@ definitions: "value": "1.212s" } status: + description: status defines the proposal status. type: string enum: - PROPOSAL_STATUS_UNSPECIFIED @@ -70433,20 +77401,6 @@ definitions: - PROPOSAL_STATUS_REJECTED - PROPOSAL_STATUS_FAILED default: PROPOSAL_STATUS_UNSPECIFIED - description: |- - ProposalStatus enumerates the valid statuses of a proposal. - - - PROPOSAL_STATUS_UNSPECIFIED: PROPOSAL_STATUS_UNSPECIFIED defines the default proposal status. - - PROPOSAL_STATUS_DEPOSIT_PERIOD: PROPOSAL_STATUS_DEPOSIT_PERIOD defines a proposal status during the deposit - period. - - PROPOSAL_STATUS_VOTING_PERIOD: PROPOSAL_STATUS_VOTING_PERIOD defines a proposal status during the voting - period. - - PROPOSAL_STATUS_PASSED: PROPOSAL_STATUS_PASSED defines a proposal status of a proposal that has - passed. - - PROPOSAL_STATUS_REJECTED: PROPOSAL_STATUS_REJECTED defines a proposal status of a proposal that has - been rejected. - - PROPOSAL_STATUS_FAILED: PROPOSAL_STATUS_FAILED defines a proposal status of a proposal that has - failed. final_tally_result: description: >- final_tally_result is the final tally result of the proposal. When @@ -70459,18 +77413,26 @@ definitions: properties: 'yes': type: string + description: yes is the number of yes votes on a proposal. abstain: type: string + description: abstain is the number of abstain votes on a proposal. 'no': type: string + description: no is the number of no votes on a proposal. no_with_veto: type: string + description: >- + no_with_veto is the number of no with veto votes on a + proposal. submit_time: type: string format: date-time + description: submit_time is the time of proposal submission. deposit_end_time: type: string format: date-time + description: deposit_end_time is the end time for deposition. total_deposit: type: array items: @@ -70488,12 +77450,15 @@ definitions: method signatures required by gogoproto. + description: total_deposit is the total deposit on the proposal. voting_start_time: type: string format: date-time + description: voting_start_time is the starting time to vote on a proposal. voting_end_time: type: string format: date-time + description: voting_end_time is the end time of voting on a proposal. description: Proposal defines the core field members of a governance proposal. description: >- QueryProposalResponse is the response type for the Query/Proposal RPC @@ -70509,6 +77474,7 @@ definitions: proposal_id: type: string format: uint64 + description: proposal_id defines the unique id of the proposal. content: type: object properties: @@ -70684,6 +77650,7 @@ definitions: "value": "1.212s" } status: + description: status defines the proposal status. type: string enum: - PROPOSAL_STATUS_UNSPECIFIED @@ -70693,20 +77660,6 @@ definitions: - PROPOSAL_STATUS_REJECTED - PROPOSAL_STATUS_FAILED default: PROPOSAL_STATUS_UNSPECIFIED - description: |- - ProposalStatus enumerates the valid statuses of a proposal. - - - PROPOSAL_STATUS_UNSPECIFIED: PROPOSAL_STATUS_UNSPECIFIED defines the default proposal status. - - PROPOSAL_STATUS_DEPOSIT_PERIOD: PROPOSAL_STATUS_DEPOSIT_PERIOD defines a proposal status during the deposit - period. - - PROPOSAL_STATUS_VOTING_PERIOD: PROPOSAL_STATUS_VOTING_PERIOD defines a proposal status during the voting - period. - - PROPOSAL_STATUS_PASSED: PROPOSAL_STATUS_PASSED defines a proposal status of a proposal that has - passed. - - PROPOSAL_STATUS_REJECTED: PROPOSAL_STATUS_REJECTED defines a proposal status of a proposal that has - been rejected. - - PROPOSAL_STATUS_FAILED: PROPOSAL_STATUS_FAILED defines a proposal status of a proposal that has - failed. final_tally_result: description: >- final_tally_result is the final tally result of the proposal. @@ -70720,18 +77673,26 @@ definitions: properties: 'yes': type: string + description: yes is the number of yes votes on a proposal. abstain: type: string + description: abstain is the number of abstain votes on a proposal. 'no': type: string + description: no is the number of no votes on a proposal. no_with_veto: type: string + description: >- + no_with_veto is the number of no with veto votes on a + proposal. submit_time: type: string format: date-time + description: submit_time is the time of proposal submission. deposit_end_time: type: string format: date-time + description: deposit_end_time is the end time for deposition. total_deposit: type: array items: @@ -70749,13 +77710,17 @@ definitions: method signatures required by gogoproto. + description: total_deposit is the total deposit on the proposal. voting_start_time: type: string format: date-time + description: voting_start_time is the starting time to vote on a proposal. voting_end_time: type: string format: date-time + description: voting_end_time is the end time of voting on a proposal. description: Proposal defines the core field members of a governance proposal. + description: proposals defines all the requested governance proposals. pagination: description: pagination defines the pagination in the response. type: object @@ -70787,12 +77752,16 @@ definitions: properties: 'yes': type: string + description: yes is the number of yes votes on a proposal. abstain: type: string + description: abstain is the number of abstain votes on a proposal. 'no': type: string + description: no is the number of no votes on a proposal. no_with_veto: type: string + description: no_with_veto is the number of no with veto votes on a proposal. description: >- QueryTallyResultResponse is the response type for the Query/Tally RPC method. @@ -70805,8 +77774,10 @@ definitions: proposal_id: type: string format: uint64 + description: proposal_id defines the unique id of the proposal. voter: type: string + description: voter is the voter address of the proposal. option: description: >- Deprecated: Prefer to use `options` instead. This field is set in @@ -70830,6 +77801,9 @@ definitions: type: object properties: option: + description: >- + option defines the valid vote options, it must not contain + duplicate vote options. type: string enum: - VOTE_OPTION_UNSPECIFIED @@ -70838,22 +77812,17 @@ definitions: - VOTE_OPTION_NO - VOTE_OPTION_NO_WITH_VETO default: VOTE_OPTION_UNSPECIFIED - description: >- - VoteOption enumerates the valid vote options for a given - governance proposal. - - - VOTE_OPTION_UNSPECIFIED: VOTE_OPTION_UNSPECIFIED defines a no-op vote option. - - VOTE_OPTION_YES: VOTE_OPTION_YES defines a yes vote option. - - VOTE_OPTION_ABSTAIN: VOTE_OPTION_ABSTAIN defines an abstain vote option. - - VOTE_OPTION_NO: VOTE_OPTION_NO defines a no vote option. - - VOTE_OPTION_NO_WITH_VETO: VOTE_OPTION_NO_WITH_VETO defines a no with veto vote option. weight: type: string + description: weight is the vote weight associated with the vote option. description: |- WeightedVoteOption defines a unit of vote for vote split. Since: cosmos-sdk 0.43 - title: 'Since: cosmos-sdk 0.43' + description: |- + options is the weighted vote options. + + Since: cosmos-sdk 0.43 description: |- Vote defines a vote on a governance proposal. A Vote consists of a proposal ID, the voter, and the vote option. @@ -70869,8 +77838,10 @@ definitions: proposal_id: type: string format: uint64 + description: proposal_id defines the unique id of the proposal. voter: type: string + description: voter is the voter address of the proposal. option: description: >- Deprecated: Prefer to use `options` instead. This field is set @@ -70894,6 +77865,9 @@ definitions: type: object properties: option: + description: >- + option defines the valid vote options, it must not contain + duplicate vote options. type: string enum: - VOTE_OPTION_UNSPECIFIED @@ -70902,26 +77876,21 @@ definitions: - VOTE_OPTION_NO - VOTE_OPTION_NO_WITH_VETO default: VOTE_OPTION_UNSPECIFIED - description: >- - VoteOption enumerates the valid vote options for a given - governance proposal. - - - VOTE_OPTION_UNSPECIFIED: VOTE_OPTION_UNSPECIFIED defines a no-op vote option. - - VOTE_OPTION_YES: VOTE_OPTION_YES defines a yes vote option. - - VOTE_OPTION_ABSTAIN: VOTE_OPTION_ABSTAIN defines an abstain vote option. - - VOTE_OPTION_NO: VOTE_OPTION_NO defines a no vote option. - - VOTE_OPTION_NO_WITH_VETO: VOTE_OPTION_NO_WITH_VETO defines a no with veto vote option. weight: type: string + description: weight is the vote weight associated with the vote option. description: |- WeightedVoteOption defines a unit of vote for vote split. Since: cosmos-sdk 0.43 - title: 'Since: cosmos-sdk 0.43' + description: |- + options is the weighted vote options. + + Since: cosmos-sdk 0.43 description: |- Vote defines a vote on a governance proposal. A Vote consists of a proposal ID, the voter, and the vote option. - description: votes defined the queried votes. + description: votes defines the queried votes. pagination: description: pagination defines the pagination in the response. type: object @@ -70950,7 +77919,7 @@ definitions: format: byte description: |- Minimum percentage of total stake needed to vote for a result to be - considered valid. + considered valid. threshold: type: string format: byte @@ -70962,19 +77931,23 @@ definitions: format: byte description: |- Minimum value of Veto votes to Total votes ratio for proposal to be - vetoed. Default value: 1/3. + vetoed. Default value: 1/3. description: TallyParams defines the params for tallying votes on governance proposals. cosmos.gov.v1beta1.TallyResult: type: object properties: 'yes': type: string + description: yes is the number of yes votes on a proposal. abstain: type: string + description: abstain is the number of abstain votes on a proposal. 'no': type: string + description: no is the number of no votes on a proposal. no_with_veto: type: string + description: no_with_veto is the number of no with veto votes on a proposal. description: TallyResult defines a standard tally for a governance proposal. cosmos.gov.v1beta1.Vote: type: object @@ -70982,8 +77955,10 @@ definitions: proposal_id: type: string format: uint64 + description: proposal_id defines the unique id of the proposal. voter: type: string + description: voter is the voter address of the proposal. option: description: >- Deprecated: Prefer to use `options` instead. This field is set in @@ -71007,6 +77982,9 @@ definitions: type: object properties: option: + description: >- + option defines the valid vote options, it must not contain + duplicate vote options. type: string enum: - VOTE_OPTION_UNSPECIFIED @@ -71015,22 +77993,17 @@ definitions: - VOTE_OPTION_NO - VOTE_OPTION_NO_WITH_VETO default: VOTE_OPTION_UNSPECIFIED - description: >- - VoteOption enumerates the valid vote options for a given - governance proposal. - - - VOTE_OPTION_UNSPECIFIED: VOTE_OPTION_UNSPECIFIED defines a no-op vote option. - - VOTE_OPTION_YES: VOTE_OPTION_YES defines a yes vote option. - - VOTE_OPTION_ABSTAIN: VOTE_OPTION_ABSTAIN defines an abstain vote option. - - VOTE_OPTION_NO: VOTE_OPTION_NO defines a no vote option. - - VOTE_OPTION_NO_WITH_VETO: VOTE_OPTION_NO_WITH_VETO defines a no with veto vote option. weight: type: string + description: weight is the vote weight associated with the vote option. description: |- WeightedVoteOption defines a unit of vote for vote split. Since: cosmos-sdk 0.43 - title: 'Since: cosmos-sdk 0.43' + description: |- + options is the weighted vote options. + + Since: cosmos-sdk 0.43 description: |- Vote defines a vote on a governance proposal. A Vote consists of a proposal ID, the voter, and the vote option. @@ -71057,12 +78030,15 @@ definitions: properties: voting_period: type: string - description: Length of the voting period. + description: Duration of the voting period. description: VotingParams defines the params for voting on governance proposals. cosmos.gov.v1beta1.WeightedVoteOption: type: object properties: option: + description: >- + option defines the valid vote options, it must not contain duplicate + vote options. type: string enum: - VOTE_OPTION_UNSPECIFIED @@ -71071,17 +78047,9 @@ definitions: - VOTE_OPTION_NO - VOTE_OPTION_NO_WITH_VETO default: VOTE_OPTION_UNSPECIFIED - description: >- - VoteOption enumerates the valid vote options for a given governance - proposal. - - - VOTE_OPTION_UNSPECIFIED: VOTE_OPTION_UNSPECIFIED defines a no-op vote option. - - VOTE_OPTION_YES: VOTE_OPTION_YES defines a yes vote option. - - VOTE_OPTION_ABSTAIN: VOTE_OPTION_ABSTAIN defines an abstain vote option. - - VOTE_OPTION_NO: VOTE_OPTION_NO defines a no vote option. - - VOTE_OPTION_NO_WITH_VETO: VOTE_OPTION_NO_WITH_VETO defines a no with veto vote option. weight: type: string + description: weight is the vote weight associated with the vote option. description: |- WeightedVoteOption defines a unit of vote for vote split. @@ -71092,8 +78060,10 @@ definitions: proposal_id: type: string format: uint64 + description: proposal_id defines the unique id of the proposal. depositor: type: string + description: depositor defines the deposit addresses from the proposals. amount: type: array items: @@ -71108,6 +78078,7 @@ definitions: NOTE: The amount field is an Int which implements the custom method signatures required by gogoproto. + description: amount to be deposited by depositor. description: |- Deposit defines an amount deposited by an account address to an active proposal. @@ -71134,14 +78105,77 @@ definitions: description: >- Maximum period for Atom holders to deposit on a proposal. Initial value: 2 - months. + + months. description: DepositParams defines the params for deposits on governance proposals. + cosmos.gov.v1.Params: + type: object + properties: + min_deposit: + type: array + items: + type: object + properties: + denom: + type: string + amount: + type: string + description: |- + Coin defines a token with a denomination and an amount. + + NOTE: The amount field is an Int which implements the custom method + signatures required by gogoproto. + description: Minimum deposit for a proposal to enter voting period. + max_deposit_period: + type: string + description: >- + Maximum period for Atom holders to deposit on a proposal. Initial + value: 2 + + months. + voting_period: + type: string + description: Duration of the voting period. + quorum: + type: string + description: |- + Minimum percentage of total stake needed to vote for a result to be + considered valid. + threshold: + type: string + description: >- + Minimum proportion of Yes votes for proposal to pass. Default value: + 0.5. + veto_threshold: + type: string + description: |- + Minimum value of Veto votes to Total votes ratio for proposal to be + vetoed. Default value: 1/3. + min_initial_deposit_ratio: + type: string + description: >- + The ratio representing the proportion of the deposit value that must + be paid at proposal submission. + burn_vote_quorum: + type: boolean + title: burn deposits if a proposal does not meet quorum + burn_proposal_deposit_prevote: + type: boolean + title: burn deposits if the proposal does not enter voting period + burn_vote_veto: + type: boolean + title: burn deposits if quorum with vote type no_veto is met + description: |- + Params defines the parameters for the x/gov module. + + Since: cosmos-sdk 0.47 cosmos.gov.v1.Proposal: type: object properties: id: type: string format: uint64 + description: id defines the unique id of the proposal. messages: type: array items: @@ -71311,7 +78345,11 @@ definitions: "@type": "type.googleapis.com/google.protobuf.Duration", "value": "1.212s" } + description: >- + messages are the arbitrary messages to be executed if the proposal + passes. status: + description: status defines the proposal status. type: string enum: - PROPOSAL_STATUS_UNSPECIFIED @@ -71321,20 +78359,6 @@ definitions: - PROPOSAL_STATUS_REJECTED - PROPOSAL_STATUS_FAILED default: PROPOSAL_STATUS_UNSPECIFIED - description: |- - ProposalStatus enumerates the valid statuses of a proposal. - - - PROPOSAL_STATUS_UNSPECIFIED: PROPOSAL_STATUS_UNSPECIFIED defines the default proposal status. - - PROPOSAL_STATUS_DEPOSIT_PERIOD: PROPOSAL_STATUS_DEPOSIT_PERIOD defines a proposal status during the deposit - period. - - PROPOSAL_STATUS_VOTING_PERIOD: PROPOSAL_STATUS_VOTING_PERIOD defines a proposal status during the voting - period. - - PROPOSAL_STATUS_PASSED: PROPOSAL_STATUS_PASSED defines a proposal status of a proposal that has - passed. - - PROPOSAL_STATUS_REJECTED: PROPOSAL_STATUS_REJECTED defines a proposal status of a proposal that has - been rejected. - - PROPOSAL_STATUS_FAILED: PROPOSAL_STATUS_FAILED defines a proposal status of a proposal that has - failed. final_tally_result: description: |- final_tally_result is the final tally result of the proposal. When @@ -71344,18 +78368,26 @@ definitions: properties: yes_count: type: string + description: yes_count is the number of yes votes on a proposal. abstain_count: type: string + description: abstain_count is the number of abstain votes on a proposal. no_count: type: string + description: no_count is the number of no votes on a proposal. no_with_veto_count: type: string + description: >- + no_with_veto_count is the number of no with veto votes on a + proposal. submit_time: type: string format: date-time + description: submit_time is the time of proposal submission. deposit_end_time: type: string format: date-time + description: deposit_end_time is the end time for deposition. total_deposit: type: array items: @@ -71370,15 +78402,30 @@ definitions: NOTE: The amount field is an Int which implements the custom method signatures required by gogoproto. + description: total_deposit is the total deposit on the proposal. voting_start_time: type: string format: date-time + description: voting_start_time is the starting time to vote on a proposal. voting_end_time: type: string format: date-time + description: voting_end_time is the end time of voting on a proposal. metadata: type: string description: metadata is any arbitrary metadata attached to the proposal. + title: + type: string + description: 'Since: cosmos-sdk 0.47' + title: title is the title of the proposal + summary: + type: string + description: 'Since: cosmos-sdk 0.47' + title: summary is a short summary of the proposal + proposer: + type: string + description: 'Since: cosmos-sdk 0.47' + title: Proposer is the address of the proposal sumbitter description: Proposal defines the core field members of a governance proposal. cosmos.gov.v1.ProposalStatus: type: string @@ -71413,8 +78460,10 @@ definitions: proposal_id: type: string format: uint64 + description: proposal_id defines the unique id of the proposal. depositor: type: string + description: depositor defines the deposit addresses from the proposals. amount: type: array items: @@ -71432,6 +78481,7 @@ definitions: method signatures required by gogoproto. + description: amount to be deposited by depositor. description: |- Deposit defines an amount deposited by an account address to an active proposal. @@ -71449,8 +78499,10 @@ definitions: proposal_id: type: string format: uint64 + description: proposal_id defines the unique id of the proposal. depositor: type: string + description: depositor defines the deposit addresses from the proposals. amount: type: array items: @@ -71468,11 +78520,13 @@ definitions: method signatures required by gogoproto. + description: amount to be deposited by depositor. description: >- Deposit defines an amount deposited by an account address to an active proposal. + description: deposits defines the requested deposits. pagination: description: pagination defines the pagination in the response. type: object @@ -71499,14 +78553,18 @@ definitions: type: object properties: voting_params: - description: voting_params defines the parameters related to voting. + description: |- + Deprecated: Prefer to use `params` instead. + voting_params defines the parameters related to voting. type: object properties: voting_period: type: string - description: Length of the voting period. + description: Duration of the voting period. deposit_params: - description: deposit_params defines the parameters related to deposit. + description: |- + Deprecated: Prefer to use `params` instead. + deposit_params defines the parameters related to deposit. type: object properties: min_deposit: @@ -71532,11 +78590,68 @@ definitions: description: >- Maximum period for Atom holders to deposit on a proposal. Initial value: 2 - months. + + months. tally_params: - description: tally_params defines the parameters related to tally. + description: |- + Deprecated: Prefer to use `params` instead. + tally_params defines the parameters related to tally. type: object properties: + quorum: + type: string + description: >- + Minimum percentage of total stake needed to vote for a result to + be + + considered valid. + threshold: + type: string + description: >- + Minimum proportion of Yes votes for proposal to pass. Default + value: 0.5. + veto_threshold: + type: string + description: >- + Minimum value of Veto votes to Total votes ratio for proposal to + be + + vetoed. Default value: 1/3. + params: + description: |- + params defines all the paramaters of x/gov module. + + Since: cosmos-sdk 0.47 + type: object + properties: + min_deposit: + type: array + items: + type: object + properties: + denom: + type: string + amount: + type: string + description: >- + Coin defines a token with a denomination and an amount. + + + NOTE: The amount field is an Int which implements the custom + method + + signatures required by gogoproto. + description: Minimum deposit for a proposal to enter voting period. + max_deposit_period: + type: string + description: >- + Maximum period for Atom holders to deposit on a proposal. Initial + value: 2 + + months. + voting_period: + type: string + description: Duration of the voting period. quorum: type: string description: >- @@ -71554,6 +78669,20 @@ definitions: Minimum value of Veto votes to Total votes ratio for proposal to be vetoed. Default value: 1/3. + min_initial_deposit_ratio: + type: string + description: >- + The ratio representing the proportion of the deposit value that + must be paid at proposal submission. + burn_vote_quorum: + type: boolean + title: burn deposits if a proposal does not meet quorum + burn_proposal_deposit_prevote: + type: boolean + title: burn deposits if the proposal does not enter voting period + burn_vote_veto: + type: boolean + title: burn deposits if quorum with vote type no_veto is met description: QueryParamsResponse is the response type for the Query/Params RPC method. cosmos.gov.v1.QueryProposalResponse: type: object @@ -71564,6 +78693,7 @@ definitions: id: type: string format: uint64 + description: id defines the unique id of the proposal. messages: type: array items: @@ -71740,7 +78870,11 @@ definitions: "@type": "type.googleapis.com/google.protobuf.Duration", "value": "1.212s" } + description: >- + messages are the arbitrary messages to be executed if the proposal + passes. status: + description: status defines the proposal status. type: string enum: - PROPOSAL_STATUS_UNSPECIFIED @@ -71750,20 +78884,6 @@ definitions: - PROPOSAL_STATUS_REJECTED - PROPOSAL_STATUS_FAILED default: PROPOSAL_STATUS_UNSPECIFIED - description: |- - ProposalStatus enumerates the valid statuses of a proposal. - - - PROPOSAL_STATUS_UNSPECIFIED: PROPOSAL_STATUS_UNSPECIFIED defines the default proposal status. - - PROPOSAL_STATUS_DEPOSIT_PERIOD: PROPOSAL_STATUS_DEPOSIT_PERIOD defines a proposal status during the deposit - period. - - PROPOSAL_STATUS_VOTING_PERIOD: PROPOSAL_STATUS_VOTING_PERIOD defines a proposal status during the voting - period. - - PROPOSAL_STATUS_PASSED: PROPOSAL_STATUS_PASSED defines a proposal status of a proposal that has - passed. - - PROPOSAL_STATUS_REJECTED: PROPOSAL_STATUS_REJECTED defines a proposal status of a proposal that has - been rejected. - - PROPOSAL_STATUS_FAILED: PROPOSAL_STATUS_FAILED defines a proposal status of a proposal that has - failed. final_tally_result: description: >- final_tally_result is the final tally result of the proposal. When @@ -71776,18 +78896,26 @@ definitions: properties: yes_count: type: string + description: yes_count is the number of yes votes on a proposal. abstain_count: type: string + description: abstain_count is the number of abstain votes on a proposal. no_count: type: string + description: no_count is the number of no votes on a proposal. no_with_veto_count: type: string + description: >- + no_with_veto_count is the number of no with veto votes on a + proposal. submit_time: type: string format: date-time + description: submit_time is the time of proposal submission. deposit_end_time: type: string format: date-time + description: deposit_end_time is the end time for deposition. total_deposit: type: array items: @@ -71805,15 +78933,30 @@ definitions: method signatures required by gogoproto. + description: total_deposit is the total deposit on the proposal. voting_start_time: type: string format: date-time + description: voting_start_time is the starting time to vote on a proposal. voting_end_time: type: string format: date-time + description: voting_end_time is the end time of voting on a proposal. metadata: type: string description: metadata is any arbitrary metadata attached to the proposal. + title: + type: string + description: 'Since: cosmos-sdk 0.47' + title: title is the title of the proposal + summary: + type: string + description: 'Since: cosmos-sdk 0.47' + title: summary is a short summary of the proposal + proposer: + type: string + description: 'Since: cosmos-sdk 0.47' + title: Proposer is the address of the proposal sumbitter description: Proposal defines the core field members of a governance proposal. description: >- QueryProposalResponse is the response type for the Query/Proposal RPC @@ -71829,6 +78972,7 @@ definitions: id: type: string format: uint64 + description: id defines the unique id of the proposal. messages: type: array items: @@ -72007,7 +79151,11 @@ definitions: "@type": "type.googleapis.com/google.protobuf.Duration", "value": "1.212s" } + description: >- + messages are the arbitrary messages to be executed if the + proposal passes. status: + description: status defines the proposal status. type: string enum: - PROPOSAL_STATUS_UNSPECIFIED @@ -72017,20 +79165,6 @@ definitions: - PROPOSAL_STATUS_REJECTED - PROPOSAL_STATUS_FAILED default: PROPOSAL_STATUS_UNSPECIFIED - description: |- - ProposalStatus enumerates the valid statuses of a proposal. - - - PROPOSAL_STATUS_UNSPECIFIED: PROPOSAL_STATUS_UNSPECIFIED defines the default proposal status. - - PROPOSAL_STATUS_DEPOSIT_PERIOD: PROPOSAL_STATUS_DEPOSIT_PERIOD defines a proposal status during the deposit - period. - - PROPOSAL_STATUS_VOTING_PERIOD: PROPOSAL_STATUS_VOTING_PERIOD defines a proposal status during the voting - period. - - PROPOSAL_STATUS_PASSED: PROPOSAL_STATUS_PASSED defines a proposal status of a proposal that has - passed. - - PROPOSAL_STATUS_REJECTED: PROPOSAL_STATUS_REJECTED defines a proposal status of a proposal that has - been rejected. - - PROPOSAL_STATUS_FAILED: PROPOSAL_STATUS_FAILED defines a proposal status of a proposal that has - failed. final_tally_result: description: >- final_tally_result is the final tally result of the proposal. @@ -72044,18 +79178,26 @@ definitions: properties: yes_count: type: string + description: yes_count is the number of yes votes on a proposal. abstain_count: type: string + description: abstain_count is the number of abstain votes on a proposal. no_count: type: string + description: no_count is the number of no votes on a proposal. no_with_veto_count: type: string + description: >- + no_with_veto_count is the number of no with veto votes on a + proposal. submit_time: type: string format: date-time + description: submit_time is the time of proposal submission. deposit_end_time: type: string format: date-time + description: deposit_end_time is the end time for deposition. total_deposit: type: array items: @@ -72073,16 +79215,32 @@ definitions: method signatures required by gogoproto. + description: total_deposit is the total deposit on the proposal. voting_start_time: type: string format: date-time + description: voting_start_time is the starting time to vote on a proposal. voting_end_time: type: string format: date-time + description: voting_end_time is the end time of voting on a proposal. metadata: type: string description: metadata is any arbitrary metadata attached to the proposal. + title: + type: string + description: 'Since: cosmos-sdk 0.47' + title: title is the title of the proposal + summary: + type: string + description: 'Since: cosmos-sdk 0.47' + title: summary is a short summary of the proposal + proposer: + type: string + description: 'Since: cosmos-sdk 0.47' + title: Proposer is the address of the proposal sumbitter description: Proposal defines the core field members of a governance proposal. + description: proposals defines all the requested governance proposals. pagination: description: pagination defines the pagination in the response. type: object @@ -72114,12 +79272,18 @@ definitions: properties: yes_count: type: string + description: yes_count is the number of yes votes on a proposal. abstain_count: type: string + description: abstain_count is the number of abstain votes on a proposal. no_count: type: string + description: no_count is the number of no votes on a proposal. no_with_veto_count: type: string + description: >- + no_with_veto_count is the number of no with veto votes on a + proposal. description: >- QueryTallyResultResponse is the response type for the Query/Tally RPC method. @@ -72132,14 +79296,19 @@ definitions: proposal_id: type: string format: uint64 + description: proposal_id defines the unique id of the proposal. voter: type: string + description: voter is the voter address of the proposal. options: type: array items: type: object properties: option: + description: >- + option defines the valid vote options, it must not contain + duplicate vote options. type: string enum: - VOTE_OPTION_UNSPECIFIED @@ -72148,18 +79317,11 @@ definitions: - VOTE_OPTION_NO - VOTE_OPTION_NO_WITH_VETO default: VOTE_OPTION_UNSPECIFIED - description: >- - VoteOption enumerates the valid vote options for a given - governance proposal. - - - VOTE_OPTION_UNSPECIFIED: VOTE_OPTION_UNSPECIFIED defines a no-op vote option. - - VOTE_OPTION_YES: VOTE_OPTION_YES defines a yes vote option. - - VOTE_OPTION_ABSTAIN: VOTE_OPTION_ABSTAIN defines an abstain vote option. - - VOTE_OPTION_NO: VOTE_OPTION_NO defines a no vote option. - - VOTE_OPTION_NO_WITH_VETO: VOTE_OPTION_NO_WITH_VETO defines a no with veto vote option. weight: type: string + description: weight is the vote weight associated with the vote option. description: WeightedVoteOption defines a unit of vote for vote split. + description: options is the weighted vote options. metadata: type: string description: metadata is any arbitrary metadata to attached to the vote. @@ -72178,14 +79340,19 @@ definitions: proposal_id: type: string format: uint64 + description: proposal_id defines the unique id of the proposal. voter: type: string + description: voter is the voter address of the proposal. options: type: array items: type: object properties: option: + description: >- + option defines the valid vote options, it must not contain + duplicate vote options. type: string enum: - VOTE_OPTION_UNSPECIFIED @@ -72194,25 +79361,18 @@ definitions: - VOTE_OPTION_NO - VOTE_OPTION_NO_WITH_VETO default: VOTE_OPTION_UNSPECIFIED - description: >- - VoteOption enumerates the valid vote options for a given - governance proposal. - - - VOTE_OPTION_UNSPECIFIED: VOTE_OPTION_UNSPECIFIED defines a no-op vote option. - - VOTE_OPTION_YES: VOTE_OPTION_YES defines a yes vote option. - - VOTE_OPTION_ABSTAIN: VOTE_OPTION_ABSTAIN defines an abstain vote option. - - VOTE_OPTION_NO: VOTE_OPTION_NO defines a no vote option. - - VOTE_OPTION_NO_WITH_VETO: VOTE_OPTION_NO_WITH_VETO defines a no with veto vote option. weight: type: string + description: weight is the vote weight associated with the vote option. description: WeightedVoteOption defines a unit of vote for vote split. + description: options is the weighted vote options. metadata: type: string description: metadata is any arbitrary metadata to attached to the vote. description: |- Vote defines a vote on a governance proposal. A Vote consists of a proposal ID, the voter, and the vote option. - description: votes defined the queried votes. + description: votes defines the queried votes. pagination: description: pagination defines the pagination in the response. type: object @@ -72240,7 +79400,7 @@ definitions: type: string description: |- Minimum percentage of total stake needed to vote for a result to be - considered valid. + considered valid. threshold: type: string description: >- @@ -72250,19 +79410,23 @@ definitions: type: string description: |- Minimum value of Veto votes to Total votes ratio for proposal to be - vetoed. Default value: 1/3. + vetoed. Default value: 1/3. description: TallyParams defines the params for tallying votes on governance proposals. cosmos.gov.v1.TallyResult: type: object properties: yes_count: type: string + description: yes_count is the number of yes votes on a proposal. abstain_count: type: string + description: abstain_count is the number of abstain votes on a proposal. no_count: type: string + description: no_count is the number of no votes on a proposal. no_with_veto_count: type: string + description: no_with_veto_count is the number of no with veto votes on a proposal. description: TallyResult defines a standard tally for a governance proposal. cosmos.gov.v1.Vote: type: object @@ -72270,14 +79434,19 @@ definitions: proposal_id: type: string format: uint64 + description: proposal_id defines the unique id of the proposal. voter: type: string + description: voter is the voter address of the proposal. options: type: array items: type: object properties: option: + description: >- + option defines the valid vote options, it must not contain + duplicate vote options. type: string enum: - VOTE_OPTION_UNSPECIFIED @@ -72286,18 +79455,11 @@ definitions: - VOTE_OPTION_NO - VOTE_OPTION_NO_WITH_VETO default: VOTE_OPTION_UNSPECIFIED - description: >- - VoteOption enumerates the valid vote options for a given - governance proposal. - - - VOTE_OPTION_UNSPECIFIED: VOTE_OPTION_UNSPECIFIED defines a no-op vote option. - - VOTE_OPTION_YES: VOTE_OPTION_YES defines a yes vote option. - - VOTE_OPTION_ABSTAIN: VOTE_OPTION_ABSTAIN defines an abstain vote option. - - VOTE_OPTION_NO: VOTE_OPTION_NO defines a no vote option. - - VOTE_OPTION_NO_WITH_VETO: VOTE_OPTION_NO_WITH_VETO defines a no with veto vote option. weight: type: string + description: weight is the vote weight associated with the vote option. description: WeightedVoteOption defines a unit of vote for vote split. + description: options is the weighted vote options. metadata: type: string description: metadata is any arbitrary metadata to attached to the vote. @@ -72327,12 +79489,15 @@ definitions: properties: voting_period: type: string - description: Length of the voting period. + description: Duration of the voting period. description: VotingParams defines the params for voting on governance proposals. cosmos.gov.v1.WeightedVoteOption: type: object properties: option: + description: >- + option defines the valid vote options, it must not contain duplicate + vote options. type: string enum: - VOTE_OPTION_UNSPECIFIED @@ -72341,17 +79506,9 @@ definitions: - VOTE_OPTION_NO - VOTE_OPTION_NO_WITH_VETO default: VOTE_OPTION_UNSPECIFIED - description: >- - VoteOption enumerates the valid vote options for a given governance - proposal. - - - VOTE_OPTION_UNSPECIFIED: VOTE_OPTION_UNSPECIFIED defines a no-op vote option. - - VOTE_OPTION_YES: VOTE_OPTION_YES defines a yes vote option. - - VOTE_OPTION_ABSTAIN: VOTE_OPTION_ABSTAIN defines an abstain vote option. - - VOTE_OPTION_NO: VOTE_OPTION_NO defines a no vote option. - - VOTE_OPTION_NO_WITH_VETO: VOTE_OPTION_NO_WITH_VETO defines a no with veto vote option. weight: type: string + description: weight is the vote weight associated with the vote option. description: WeightedVoteOption defines a unit of vote for vote split. cosmos.slashing.v1beta1.Params: type: object @@ -73074,6 +80231,20 @@ definitions: Since: cosmos-sdk 0.46 + unbonding_on_hold_ref_count: + type: string + format: int64 + title: >- + strictly positive if this validator's unbonding has been stopped + by external modules + unbonding_ids: + type: array + items: + type: string + format: uint64 + title: >- + list of unbonding ids, each uniquely identifing an unbonding of + this validator description: >- Validator defines a validator, together with the total amount of the @@ -73133,7 +80304,7 @@ definitions: title: >- min_commission_rate is the chain-wide minimum commission rate that a validator can charge their delegators - description: Params defines the parameters for the staking module. + description: Params defines the parameters for the x/staking module. cosmos.staking.v1beta1.Pool: type: object properties: @@ -73315,6 +80486,16 @@ definitions: balance: type: string description: balance defines the tokens to receive at completion. + unbonding_id: + type: string + format: uint64 + title: Incrementing id that uniquely identifies this entry + unbonding_on_hold_ref_count: + type: string + format: int64 + title: >- + Strictly positive if this entry's unbonding has been + stopped by external modules description: >- UnbondingDelegationEntry defines an unbonding object with relevant metadata. @@ -73621,6 +80802,20 @@ definitions: Since: cosmos-sdk 0.46 + unbonding_on_hold_ref_count: + type: string + format: int64 + title: >- + strictly positive if this validator's unbonding has been stopped + by external modules + unbonding_ids: + type: array + items: + type: string + format: uint64 + title: >- + list of unbonding ids, each uniquely identifing an unbonding of + this validator description: >- Validator defines a validator, together with the total amount of the @@ -73928,6 +81123,20 @@ definitions: Since: cosmos-sdk 0.46 + unbonding_on_hold_ref_count: + type: string + format: int64 + title: >- + strictly positive if this validator's unbonding has been stopped + by external modules + unbonding_ids: + type: array + items: + type: string + format: uint64 + title: >- + list of unbonding ids, each uniquely identifing an unbonding of + this validator description: >- Validator defines a validator, together with the total amount of the @@ -74342,6 +81551,20 @@ definitions: Since: cosmos-sdk 0.46 + unbonding_on_hold_ref_count: + type: string + format: int64 + title: >- + strictly positive if this validator's unbonding has been + stopped by external modules + unbonding_ids: + type: array + items: + type: string + format: uint64 + title: >- + list of unbonding ids, each uniquely identifing an unbonding + of this validator description: >- Validator defines a validator, together with the total amount of the @@ -74468,6 +81691,16 @@ definitions: description: >- shares_dst is the amount of destination-validator shares created by redelegation. + unbonding_id: + type: string + format: uint64 + title: Incrementing id that uniquely identifies this entry + unbonding_on_hold_ref_count: + type: string + format: int64 + title: >- + Strictly positive if this entry's unbonding has been + stopped by external modules description: >- RedelegationEntry defines a redelegation object with relevant metadata. @@ -74508,6 +81741,16 @@ definitions: description: >- shares_dst is the amount of destination-validator shares created by redelegation. + unbonding_id: + type: string + format: uint64 + title: Incrementing id that uniquely identifies this entry + unbonding_on_hold_ref_count: + type: string + format: int64 + title: >- + Strictly positive if this entry's unbonding has been + stopped by external modules description: >- RedelegationEntry defines a redelegation object with relevant metadata. @@ -74588,6 +81831,16 @@ definitions: balance: type: string description: balance defines the tokens to receive at completion. + unbonding_id: + type: string + format: uint64 + title: Incrementing id that uniquely identifies this entry + unbonding_on_hold_ref_count: + type: string + format: int64 + title: >- + Strictly positive if this entry's unbonding has been stopped + by external modules description: >- UnbondingDelegationEntry defines an unbonding object with relevant metadata. @@ -74948,6 +82201,20 @@ definitions: Since: cosmos-sdk 0.46 + unbonding_on_hold_ref_count: + type: string + format: int64 + title: >- + strictly positive if this validator's unbonding has been stopped + by external modules + unbonding_ids: + type: array + items: + type: string + format: uint64 + title: >- + list of unbonding ids, each uniquely identifing an unbonding of + this validator description: >- Validator defines a validator, together with the total amount of the @@ -75011,6 +82278,16 @@ definitions: balance: type: string description: balance defines the tokens to receive at completion. + unbonding_id: + type: string + format: uint64 + title: Incrementing id that uniquely identifies this entry + unbonding_on_hold_ref_count: + type: string + format: int64 + title: >- + Strictly positive if this entry's unbonding has been + stopped by external modules description: >- UnbondingDelegationEntry defines an unbonding object with relevant metadata. @@ -75325,6 +82602,20 @@ definitions: Since: cosmos-sdk 0.46 + unbonding_on_hold_ref_count: + type: string + format: int64 + title: >- + strictly positive if this validator's unbonding has been stopped + by external modules + unbonding_ids: + type: array + items: + type: string + format: uint64 + title: >- + list of unbonding ids, each uniquely identifing an unbonding of + this validator description: >- Validator defines a validator, together with the total amount of the @@ -75412,6 +82703,16 @@ definitions: description: >- shares_dst is the amount of destination-validator shares created by redelegation. + unbonding_id: + type: string + format: uint64 + title: Incrementing id that uniquely identifies this entry + unbonding_on_hold_ref_count: + type: string + format: int64 + title: >- + Strictly positive if this entry's unbonding has been stopped by + external modules description: >- RedelegationEntry defines a redelegation object with relevant metadata. @@ -75440,6 +82741,16 @@ definitions: description: >- shares_dst is the amount of destination-validator shares created by redelegation. + unbonding_id: + type: string + format: uint64 + title: Incrementing id that uniquely identifies this entry + unbonding_on_hold_ref_count: + type: string + format: int64 + title: >- + Strictly positive if this entry's unbonding has been stopped by + external modules description: RedelegationEntry defines a redelegation object with relevant metadata. cosmos.staking.v1beta1.RedelegationEntryResponse: type: object @@ -75467,6 +82778,16 @@ definitions: description: >- shares_dst is the amount of destination-validator shares created by redelegation. + unbonding_id: + type: string + format: uint64 + title: Incrementing id that uniquely identifies this entry + unbonding_on_hold_ref_count: + type: string + format: int64 + title: >- + Strictly positive if this entry's unbonding has been stopped by + external modules description: >- RedelegationEntry defines a redelegation object with relevant metadata. @@ -75525,6 +82846,16 @@ definitions: description: >- shares_dst is the amount of destination-validator shares created by redelegation. + unbonding_id: + type: string + format: uint64 + title: Incrementing id that uniquely identifies this entry + unbonding_on_hold_ref_count: + type: string + format: int64 + title: >- + Strictly positive if this entry's unbonding has been stopped + by external modules description: >- RedelegationEntry defines a redelegation object with relevant metadata. @@ -75565,6 +82896,16 @@ definitions: description: >- shares_dst is the amount of destination-validator shares created by redelegation. + unbonding_id: + type: string + format: uint64 + title: Incrementing id that uniquely identifies this entry + unbonding_on_hold_ref_count: + type: string + format: int64 + title: >- + Strictly positive if this entry's unbonding has been stopped + by external modules description: >- RedelegationEntry defines a redelegation object with relevant metadata. @@ -75615,6 +82956,16 @@ definitions: balance: type: string description: balance defines the tokens to receive at completion. + unbonding_id: + type: string + format: uint64 + title: Incrementing id that uniquely identifies this entry + unbonding_on_hold_ref_count: + type: string + format: int64 + title: >- + Strictly positive if this entry's unbonding has been stopped by + external modules description: >- UnbondingDelegationEntry defines an unbonding object with relevant metadata. @@ -75641,6 +82992,16 @@ definitions: balance: type: string description: balance defines the tokens to receive at completion. + unbonding_id: + type: string + format: uint64 + title: Incrementing id that uniquely identifies this entry + unbonding_on_hold_ref_count: + type: string + format: int64 + title: >- + Strictly positive if this entry's unbonding has been stopped by + external modules description: >- UnbondingDelegationEntry defines an unbonding object with relevant metadata. @@ -75908,6 +83269,20 @@ definitions: Since: cosmos-sdk 0.46 + unbonding_on_hold_ref_count: + type: string + format: int64 + title: >- + strictly positive if this validator's unbonding has been stopped by + external modules + unbonding_ids: + type: array + items: + type: string + format: uint64 + title: >- + list of unbonding ids, each uniquely identifing an unbonding of this + validator description: >- Validator defines a validator, together with the total amount of the @@ -76143,10 +83518,8 @@ definitions: properties: key: type: string - format: byte value: type: string - format: byte index: type: boolean description: >- @@ -76630,10 +84003,8 @@ definitions: properties: key: type: string - format: byte value: type: string - format: byte index: type: boolean description: >- @@ -76854,8 +84225,8 @@ definitions: method. - BROADCAST_MODE_UNSPECIFIED: zero-value for mode ordering - - BROADCAST_MODE_BLOCK: BROADCAST_MODE_BLOCK defines a tx broadcasting mode where the client waits for - the tx to be committed in a block. + - BROADCAST_MODE_BLOCK: DEPRECATED: use BROADCAST_MODE_SYNC instead, + BROADCAST_MODE_BLOCK is not supported by the SDK from v0.47.x onwards. - BROADCAST_MODE_SYNC: BROADCAST_MODE_SYNC defines a tx broadcasting mode where the client waits for a CheckTx execution response only. - BROADCAST_MODE_ASYNC: BROADCAST_MODE_ASYNC defines a tx broadcasting mode where the client returns @@ -76880,8 +84251,8 @@ definitions: RPC method. - BROADCAST_MODE_UNSPECIFIED: zero-value for mode ordering - - BROADCAST_MODE_BLOCK: BROADCAST_MODE_BLOCK defines a tx broadcasting mode where the client waits for - the tx to be committed in a block. + - BROADCAST_MODE_BLOCK: DEPRECATED: use BROADCAST_MODE_SYNC instead, + BROADCAST_MODE_BLOCK is not supported by the SDK from v0.47.x onwards. - BROADCAST_MODE_SYNC: BROADCAST_MODE_SYNC defines a tx broadcasting mode where the client waits for a CheckTx execution response only. - BROADCAST_MODE_ASYNC: BROADCAST_MODE_ASYNC defines a tx broadcasting mode where the client returns @@ -77170,10 +84541,8 @@ definitions: properties: key: type: string - format: byte value: type: string - format: byte index: type: boolean description: >- @@ -78124,10 +85493,8 @@ definitions: properties: key: type: string - format: byte value: type: string - format: byte index: type: boolean description: >- @@ -78452,10 +85819,8 @@ definitions: properties: key: type: string - format: byte value: type: string - format: byte index: type: boolean description: >- @@ -78969,10 +86334,8 @@ definitions: properties: key: type: string - format: byte value: type: string - format: byte index: type: boolean description: >- @@ -80364,6 +87727,94 @@ definitions: and can't be handled, they will be ignored description: TxBody is the body of a transaction that all signers sign over. + cosmos.tx.v1beta1.TxDecodeAminoRequest: + type: object + properties: + amino_binary: + type: string + format: byte + description: |- + TxDecodeAminoRequest is the request type for the Service.TxDecodeAmino + RPC method. + + Since: cosmos-sdk 0.47 + cosmos.tx.v1beta1.TxDecodeAminoResponse: + type: object + properties: + amino_json: + type: string + description: |- + TxDecodeAminoResponse is the response type for the Service.TxDecodeAmino + RPC method. + + Since: cosmos-sdk 0.47 + cosmos.tx.v1beta1.TxDecodeRequest: + type: object + properties: + tx_bytes: + type: string + format: byte + description: tx_bytes is the raw transaction. + description: |- + TxDecodeRequest is the request type for the Service.TxDecode + RPC method. + + Since: cosmos-sdk 0.47 + cosmos.tx.v1beta1.TxDecodeResponse: + type: object + properties: + tx: + $ref: '#/definitions/cosmos.tx.v1beta1.Tx' + description: tx is the decoded transaction. + description: |- + TxDecodeResponse is the response type for the + Service.TxDecode method. + + Since: cosmos-sdk 0.47 + cosmos.tx.v1beta1.TxEncodeAminoRequest: + type: object + properties: + amino_json: + type: string + description: |- + TxEncodeAminoRequest is the request type for the Service.TxEncodeAmino + RPC method. + + Since: cosmos-sdk 0.47 + cosmos.tx.v1beta1.TxEncodeAminoResponse: + type: object + properties: + amino_binary: + type: string + format: byte + description: |- + TxEncodeAminoResponse is the response type for the Service.TxEncodeAmino + RPC method. + + Since: cosmos-sdk 0.47 + cosmos.tx.v1beta1.TxEncodeRequest: + type: object + properties: + tx: + $ref: '#/definitions/cosmos.tx.v1beta1.Tx' + description: tx is the transaction to encode. + description: |- + TxEncodeRequest is the request type for the Service.TxEncode + RPC method. + + Since: cosmos-sdk 0.47 + cosmos.tx.v1beta1.TxEncodeResponse: + type: object + properties: + tx_bytes: + type: string + format: byte + description: tx_bytes is the encoded transaction bytes. + description: |- + TxEncodeResponse is the response type for the + Service.TxEncode method. + + Since: cosmos-sdk 0.47 tendermint.abci.Event: type: object properties: @@ -80376,10 +87827,8 @@ definitions: properties: key: type: string - format: byte value: type: string - format: byte index: type: boolean description: EventAttribute is a single key-value pair, associated with an event. @@ -80395,10 +87844,8 @@ definitions: properties: key: type: string - format: byte value: type: string - format: byte index: type: boolean description: EventAttribute is a single key-value pair, associated with an event. @@ -82637,19 +90084,18 @@ definitions: ProofOp defines an operation used for calculating Merkle root. The data could - be arbitrary format, providing nessecary data for example + be arbitrary format, providing necessary data for example neighbouring node hash. Note: This type is a duplicate of the ProofOp proto type defined - in - - Tendermint. - description: |- + in Tendermint. + description: >- ProofOps is Merkle proof defined by the list of ProofOps. + Note: This type is a duplicate of the ProofOps proto type defined in Tendermint. height: @@ -82657,11 +90103,13 @@ definitions: format: int64 codespace: type: string - description: |- + description: >- ABCIQueryResponse defines the response structure for the ABCIQuery gRPC query. + Note: This type is a duplicate of the ResponseQuery proto type defined in + Tendermint. cosmos.base.tendermint.v1beta1.Block: type: object @@ -84288,9 +91736,7 @@ definitions: field converted to bech32 string. description: >- GetBlockByHeightResponse is the response type for the - Query/GetBlockByHeight - - RPC method. + Query/GetBlockByHeight RPC method. cosmos.base.tendermint.v1beta1.GetLatestBlockResponse: type: object properties: @@ -85381,9 +92827,7 @@ definitions: field converted to bech32 string. description: >- GetLatestBlockResponse is the response type for the Query/GetLatestBlock - RPC - - method. + RPC method. cosmos.base.tendermint.v1beta1.GetLatestValidatorSetResponse: type: object properties: @@ -85597,7 +93041,7 @@ definitions: PageRequest.count_total was set, its value is undefined otherwise - description: |- + description: >- GetLatestValidatorSetResponse is the response type for the Query/GetValidatorSetByHeight RPC method. cosmos.base.tendermint.v1beta1.GetNodeInfoResponse: @@ -85672,7 +93116,7 @@ definitions: type: string title: 'Since: cosmos-sdk 0.43' description: VersionInfo is the type for the GetNodeInfoResponse message. - description: |- + description: >- GetNodeInfoResponse is the response type for the Query/GetNodeInfo RPC method. cosmos.base.tendermint.v1beta1.GetSyncingResponse: @@ -85896,7 +93340,7 @@ definitions: PageRequest.count_total was set, its value is undefined otherwise - description: |- + description: >- GetValidatorSetByHeightResponse is the response type for the Query/GetValidatorSetByHeight RPC method. cosmos.base.tendermint.v1beta1.Header: @@ -86011,14 +93455,13 @@ definitions: ProofOp defines an operation used for calculating Merkle root. The data could - be arbitrary format, providing nessecary data for example neighbouring + be arbitrary format, providing necessary data for example neighbouring node hash. Note: This type is a duplicate of the ProofOp proto type defined in - Tendermint. cosmos.base.tendermint.v1beta1.ProofOps: type: object @@ -86040,18 +93483,18 @@ definitions: ProofOp defines an operation used for calculating Merkle root. The data could - be arbitrary format, providing nessecary data for example + be arbitrary format, providing necessary data for example neighbouring node hash. Note: This type is a duplicate of the ProofOp proto type defined in - Tendermint. - description: |- + description: >- ProofOps is Merkle proof defined by the list of ProofOps. + Note: This type is a duplicate of the ProofOps proto type defined in Tendermint. cosmos.base.tendermint.v1beta1.Validator: diff --git a/ibctesting/utils_test.go b/ibctesting/utils_test.go index 4d2ca00ba..e1b3a9a27 100644 --- a/ibctesting/utils_test.go +++ b/ibctesting/utils_test.go @@ -3,8 +3,11 @@ package ibctesting_test import ( "bytes" "encoding/json" + "strings" "testing" + tmrand "github.com/cometbft/cometbft/libs/rand" + cometbftproto "github.com/cometbft/cometbft/proto/tendermint/types" cometbfttypes "github.com/cometbft/cometbft/types" cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" @@ -106,7 +109,6 @@ func (s *utilSuite) rollappMsgServer() rollapptypes.MsgServer { func (s *utilSuite) SetupTest() { s.coordinator = ibctesting.NewCoordinator(s.T(), 2) // initializes test chains s.coordinator.Chains[rollappChainID()] = s.newTestChainWithSingleValidator(s.T(), s.coordinator, rollappChainID()) - // s.fundSenderAccount() // TODO: enable after x/dymns hooks are wired } func (s *utilSuite) fundSenderAccount() { @@ -126,7 +128,7 @@ func (s *utilSuite) createRollapp(transfersEnabled bool, channelID *string) { s.hubChain().SenderAccount.GetAddress().String(), "eth", "somechecksum", - "Rollapp", + strings.ToLower(tmrand.Str(7)), rollapptypes.Rollapp_EVM, &rollapptypes.RollappMetadata{ @@ -138,7 +140,13 @@ func (s *utilSuite) createRollapp(transfersEnabled bool, channelID *string) { X: "https://x.dymension.xyz", }, ) - _, err := s.hubChain().SendMsgs(msgCreateRollapp) + + err := apptesting.FundForAliasRegistration( + s.hubCtx(), s.hubApp().BankKeeper, *msgCreateRollapp, + ) + s.Require().NoError(err) + + _, err = s.hubChain().SendMsgs(msgCreateRollapp) s.Require().NoError(err) // message committed if channelID != nil { a := s.hubApp() diff --git a/proto/dymensionxyz/dymension/dymns/alias.proto b/proto/dymensionxyz/dymension/dymns/alias.proto new file mode 100644 index 000000000..764c1d09e --- /dev/null +++ b/proto/dymensionxyz/dymension/dymns/alias.proto @@ -0,0 +1,12 @@ +syntax = "proto3"; +package dymensionxyz.dymension.dymns; + +import "gogoproto/gogo.proto"; + +option go_package = "github.com/dymensionxyz/dymension/v3/x/dymns/types"; + +// MultipleAliases contains a list of alias. +message MultipleAliases { + // aliases is a list of alias, available for a RollApp. + repeated string aliases = 1; +} \ No newline at end of file diff --git a/proto/dymensionxyz/dymension/dymns/dym_name.proto b/proto/dymensionxyz/dymension/dymns/dym_name.proto new file mode 100644 index 000000000..ccc0ca8c2 --- /dev/null +++ b/proto/dymensionxyz/dymension/dymns/dym_name.proto @@ -0,0 +1,69 @@ +syntax = "proto3"; +package dymensionxyz.dymension.dymns; + +import "gogoproto/gogo.proto"; +import "cosmos/base/v1beta1/coin.proto"; + +option go_package = "github.com/dymensionxyz/dymension/v3/x/dymns/types"; + +// DymName defines a Dym-Name, the mainly purpose is to store ownership and resolution information. +// Dym-Name is similar to DNS. It is a human-readable name that maps to a chain address. +// One Dym-Name can have multiple configurations, each configuration is a resolution record. +// Dym-Name is owned by an account, and is able to grant permission to another account to control the Dym-Name. +message DymName { + // name is the human-readable name of the Dym-Name. + string name = 1; + + // owner is the account address that owns the Dym-Name. Owner has permission to transfer ownership. + string owner = 2; + + // controller is the account address that has permission update configuration for the Dym-Name. + // Default is the owner. Able to transfer control to another account by the owner. + // Users can set Dym-Name owned by Cold-Wallet and controlled by Hot-Wallet. + string controller = 3; + + // expire_at is the UTC epoch represent the last effective date of the Dym-Name, + // after which the Dym-Name is no longer valid. + // NOTE: Expired Dym-Names are not deleted from the store + // because iterating through store is very expensive because expiry date must be checked every use. + int64 expire_at = 4; + + // configs are configuration records for the Dym-Name. + repeated DymNameConfig configs = 5 [(gogoproto.nullable) = false]; + + // contact is an optional information for the Dym-Name. + // Convenient for retails users. + string contact = 6; +} + +// DymNameConfigType specifies the type of the Dym-Name configuration. +// Currently only supports Name, similar to DNS. +enum DymNameConfigType { + DCT_UNKNOWN = 0; + DCT_NAME = 1; +} + +// DymNameConfig contains the resolution configuration for the Dym-Name. +// Each record is a resolution record, similar to DNS. +message DymNameConfig { + // type is the type of the Dym-Name configuration (equals to Type in DNS). + DymNameConfigType type = 1; + + // chain_id is the chain-id of the Dym-Name configuration (equals to top-level-domain). + // If empty, the configuration is for host chain (Dymension Hub). + string chain_id = 2; + + // path of the Dym-Name configuration (equals to Host in DNS). + // If the type of this config record is Name, it is the Sub-Name of the Dym-Name Address. + string path = 3; + + // value of the Dym-Name configuration resolves to (equals to Value in DNS). + // If the type of this config record is Name, it is the address which the Dym-Name Address resolves to. + string value = 4; +} + +// ReverseLookupDymNames contains a list of Dym-Names for reverse lookup. +message ReverseLookupDymNames { + // dym_names is a list of name of the Dym-Names linked to the reverse-lookup record. + repeated string dym_names = 1; +} \ No newline at end of file diff --git a/proto/dymensionxyz/dymension/dymns/genesis.proto b/proto/dymensionxyz/dymension/dymns/genesis.proto new file mode 100644 index 000000000..e8a8c3a8a --- /dev/null +++ b/proto/dymensionxyz/dymension/dymns/genesis.proto @@ -0,0 +1,32 @@ +syntax = "proto3"; +package dymensionxyz.dymension.dymns; + +import "gogoproto/gogo.proto"; +import "dymensionxyz/dymension/dymns/params.proto"; +import "dymensionxyz/dymension/dymns/dym_name.proto"; +import "dymensionxyz/dymension/dymns/market.proto"; + +option go_package = "github.com/dymensionxyz/dymension/v3/x/dymns/types"; + +// GenesisState defines the DymNS module's genesis state. +message GenesisState { + // params defines all the parameters of the module. + Params params = 1 [(gogoproto.nullable) = false]; + + // dym_names defines all the dym names in the genesis state. + repeated DymName dym_names = 2 [(gogoproto.nullable) = false]; + + // sell_order_bids are records which used to refund the bid amount to the bidder + // of the Sell-Orders which was not finished during genesis export + repeated SellOrderBid sell_order_bids = 3 [(gogoproto.nullable) = false]; + + // buy_orders are records which used to refund the bid amount to the bidder + // of the Buy-Order which was not finished during genesis export + repeated BuyOrder buy_orders = 4 [(gogoproto.nullable) = false]; + + // aliases_of_rollapps defines all the aliases of all RollApps. + repeated AliasesOfChainId aliases_of_rollapps = 5 [ + (gogoproto.moretags) = "yaml:\"aliases_of_rollapps\"", + (gogoproto.nullable) = false + ]; +} diff --git a/proto/dymensionxyz/dymension/dymns/gov.proto b/proto/dymensionxyz/dymension/dymns/gov.proto new file mode 100644 index 000000000..b7efae986 --- /dev/null +++ b/proto/dymensionxyz/dymension/dymns/gov.proto @@ -0,0 +1,54 @@ +syntax = "proto3"; +package dymensionxyz.dymension.dymns; + +import "gogoproto/gogo.proto"; + +option go_package = "github.com/dymensionxyz/dymension/v3/x/dymns/types"; + +// MigrateChainIdsProposal defines a proposal to replace chain-id in module params and configurations. +// This proposal is used when the chain-id of a connected network changes. +// It will look up and replace the chain-id in module params and all configurations of all non-expired Dym-Names. +message MigrateChainIdsProposal { + // title of the proposal + string title = 1; + + // description of the proposal + string description = 2; + + // replacements is set of chain-id replacements + repeated MigrateChainId replacement = 3 [(gogoproto.nullable) = false]; +} + +// MigrateChainId defines a chain-id replacement. +message MigrateChainId { + // previous_chain_id is the chain-id to be replaced + string previous_chain_id = 1; + + // new_chain_id is the new chain-id to replace with + string new_chain_id = 2; +} + +// UpdateAliasesProposal defines a proposal to update the aliases associated with chain-ids in module params. +message UpdateAliasesProposal { + // title of the proposal + string title = 1; + + // description of the proposal + string description = 2; + + // add is set of aliases to be mapped to chain-ids + repeated UpdateAlias add = 3 [(gogoproto.nullable) = false]; + + // remove is set of aliases to remove mapping from chain-ids + repeated UpdateAlias remove = 4 [(gogoproto.nullable) = false]; +} + +// UpdateAlias defines an alias to chain-id mapping. +// It can be used to add or remove alias to chain-id mapping. +message UpdateAlias { + // chain_id is the chain-id to take action on + string chain_id = 1; + + // alias is the alias to be mapped to chain-id or removed + string alias = 2; +} \ No newline at end of file diff --git a/proto/dymensionxyz/dymension/dymns/market.proto b/proto/dymensionxyz/dymension/dymns/market.proto new file mode 100644 index 000000000..6981aa70a --- /dev/null +++ b/proto/dymensionxyz/dymension/dymns/market.proto @@ -0,0 +1,108 @@ +syntax = "proto3"; +package dymensionxyz.dymension.dymns; + +import "gogoproto/gogo.proto"; +import "cosmos/base/v1beta1/coin.proto"; + +option go_package = "github.com/dymensionxyz/dymension/v3/x/dymns/types"; + +// SellOrder defines a sell order, placed by owner, to sell a Dym-Name/Alias. +// Sell-Order has an expiry date. +// After expiry date, if no one has placed a bid, this Sell-Order will be closed, no change. +// If there is a bid, the highest bid will win, and the Dym-Name/Alias ownership will be transferred to the winner. +// If the bid matches the sell price, the Dym-Name/Alias ownership will be transferred to the bidder immediately. +message SellOrder { + // asset_id is the Dym-Name/Alias being opened to be sold. + string asset_id = 1; + + // asset_type is the type of the asset of the order, is Dym-Name/Alias. + AssetType asset_type = 2; + + // expire_at is the last effective date of this SO + int64 expire_at = 3; + + // min_price is the minimum price that the owner is willing to accept for the asset. + cosmos.base.v1beta1.Coin min_price = 4 [(gogoproto.nullable) = false]; + + // sell_price is the price that the owner is willing to sell the Dym-Name/Alias for, + // the SO will be completed when the price is met, ownership transferred. + // If the sell price is zero, the SO will be closed when the expire_at is reached and the highest bidder wins. + cosmos.base.v1beta1.Coin sell_price = 5; + + // highest_bid is the highest bid on the SO, if any. Price must be greater than or equal to the min_price. + SellOrderBid highest_bid = 6; +} + +// ActiveSellOrdersExpiration contains list of active SOs, store expiration date mapped by asset identity. +// Used by hook to find out expired SO instead of iterating through all records. +message ActiveSellOrdersExpiration { + repeated ActiveSellOrdersExpirationRecord records = 1 [(gogoproto.nullable) = false]; +} + +// ActiveSellOrdersExpirationRecord contains the expiration date of an active Sell-Order. +message ActiveSellOrdersExpirationRecord { + // asset_id is the Dym-Name/Alias being opened to be sold. + string asset_id = 1; + + // expire_at is the last effective date of this Sell-Order. + int64 expire_at = 2; +} + +// SellOrderBid defines a bid placed by an account on a Sell-Order. +message SellOrderBid { + // bidder is the account address of the account which placed the bid. + string bidder = 1; + + // price is the amount of coin offered by the bidder. + cosmos.base.v1beta1.Coin price = 2 [(gogoproto.nullable) = false]; + + // params is the list of parameters of the bid. + // It is empty for asset type Dym-Name. + // It has one element for asset type Alias, which is the rollapp_id to assigned for. + repeated string params = 3; +} + +// BuyOrder defines an offer to buy a Dym-Name/Alias, placed by buyer. +// Buyer will need to deposit the offer amount to the module account. +// When the owner of the Dym-Name/Alias accepts the offer, deposited amount will be transferred to the owner. +// When the buyer cancels the offer, deposited amount will be refunded to the buyer. +message BuyOrder { + // id is the unique identifier of the order. Generated by the module. + string id = 1; + + // asset_id of the Dym-Name/Alias willing to buy. + string asset_id = 2; + + // asset_type is type of the asset of the order, is Dym-Name/Alias + AssetType asset_type = 3; + + // params is the list of parameters of the Buy-Order. + // It is empty for asset type Dym-Name. + // It has one element for asset type Alias, which is the rollapp_id to assigned for. + repeated string params = 4; + + // buyer is bech32 address of the account which placed the order. + string buyer = 5; + + // offer_price is the amount of coins that buyer willing to pay for the asset. + // This amount is deposited to the module account upon placing the offer. + cosmos.base.v1beta1.Coin offer_price = 6 [(gogoproto.nullable) = false]; + + // counterparty_offer_price is the price that the Dym-Name/Alias owner is willing to sell for. + // This is used for counterparty price negotiation and for information only. + // The transaction can only be executed when the owner accepts the offer with exact offer_price. + cosmos.base.v1beta1.Coin counterparty_offer_price = 7; +} + +// ReverseLookupBuyOrderIds contains a list of Buy-Orders IDs for reverse lookup. +message ReverseLookupBuyOrderIds { + // order_ids is a list of Buy-Order IDs of the Buy-Orders linked to the reverse-lookup record. + repeated string order_ids = 1; +} + +// AssetType present type of the asset of the Buy/Sell order. +enum AssetType { + AT_UNKNOWN = 0; + AT_DYM_NAME = 1; + AT_ALIAS = 2; +} diff --git a/proto/dymensionxyz/dymension/dymns/params.proto b/proto/dymensionxyz/dymension/dymns/params.proto new file mode 100644 index 000000000..12a78b7bb --- /dev/null +++ b/proto/dymensionxyz/dymension/dymns/params.proto @@ -0,0 +1,133 @@ +syntax = "proto3"; +package dymensionxyz.dymension.dymns; + +import "gogoproto/gogo.proto"; +import "cosmos/base/v1beta1/coin.proto"; +import "google/protobuf/duration.proto"; + +option go_package = "github.com/dymensionxyz/dymension/v3/x/dymns/types"; + +// Params defines the parameters for the module. +message Params { + // price defines setting for pricing of Dym-Name and price-related parameters. + PriceParams price = 1 [ + (gogoproto.moretags) = "yaml:\"price\"", + (gogoproto.nullable) = false + ]; + + // chains defines setting for prioritized aliases mapping. + ChainsParams chains = 2 [ + (gogoproto.moretags) = "yaml:\"chains\"", + (gogoproto.nullable) = false + ]; + + // misc is group of miscellaneous parameters. + MiscParams misc = 3 [ + (gogoproto.moretags) = "yaml:\"misc\"", + (gogoproto.nullable) = false + ]; +} + +// PriceParams defines the pricing of Dym-Name and price-related parameters. +message PriceParams { + // name_price_steps holds the price steps configuration for Dym-Name registration, apply to the first year. + // The price of Dym-Name is calculated based on the number of letters. + // The first element is the price of 1 letter Dym-Name, the last element is the price of N+ letters Dym-Name. + // Minimum steps count allowed is 4, for 1/2/3/4+ letters Dym-Name. + repeated string name_price_steps = 1 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", + (gogoproto.moretags) = "yaml:\"name_price_steps\"", + (gogoproto.nullable) = false + ]; + + + // alias_price_steps holds the price steps configuration for Alias registration, one off payment. + // The price of Alias is calculated based on the number of letters. + // The first element is the price of 1 letter Alias, the last element is the price of N+ letters Alias. + // Minimum steps count allowed is 4, for 1/2/3/4+ letters Alias. + repeated string alias_price_steps = 2 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", + (gogoproto.moretags) = "yaml:\"alias_price_steps\"", + (gogoproto.nullable) = false + ]; + + // price_extends is used to extends Dym-Name yearly, after the one-off payment for the first year. + string price_extends = 3 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", + (gogoproto.moretags) = "yaml:\"price_extends\"", + (gogoproto.nullable) = false + ]; + + // price_denom is the required denomination of the pricing setup and trading policy. + string price_denom = 4 [ + (gogoproto.moretags) = "yaml:\"price_denom\"" + ]; + + // min_offer_price is minimum price allowed to place an offer. + // Mostly used to prevent spamming and abusing store with low price offers, + // so the value should not be so low. + string min_offer_price = 5 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", + (gogoproto.moretags) = "yaml:\"min_offer_price\"", + (gogoproto.nullable) = false + ]; +} + +// ChainsParams defines setting for prioritized aliases mapping. +message ChainsParams { + // aliases_of_chain_ids is set of chain-ids and their corresponding aliases, + // used for UX improvement like we can do my-name@cosmos instead of my-name@cosmoshub-4. + // + // This list is prioritized over Roll-App aliases + // the reason is to allow the community able to have control to fixes the potential problems with the aliases. + repeated AliasesOfChainId aliases_of_chain_ids = 1 [ + (gogoproto.moretags) = "yaml:\"aliases_of_chain_ids\"", + (gogoproto.nullable) = false + ]; +} + +// AliasesOfChainId defines the multiple-aliases of a chain id. +message AliasesOfChainId { + // chain_id which owned the aliases. + string chain_id = 1 [ + (gogoproto.moretags) = "yaml:\"chain_id\"" + ]; + + // aliases is a set of aliases of the chain id for UX improvement, + // like we can do my-name@cosmos instead of my-name@cosmoshub-4 + repeated string aliases = 2 [ + (gogoproto.moretags) = "yaml:\"aliases\"" + ]; +} + +// MiscParams defines group of miscellaneous parameters. +message MiscParams { + // end_epoch_hook_identifier is the identifier of the end epoch hook. + string end_epoch_hook_identifier = 1 [ + (gogoproto.moretags) = "yaml:\"end_epoch_hook_identifier\"" + ]; + + // grace_period_duration is the amount of time that the former owner of an expired Dym-Name + // can renew it before completely lost. + google.protobuf.Duration grace_period_duration = 2 [ + (gogoproto.nullable) = false, + (gogoproto.stdduration) = true, + (gogoproto.moretags) = "yaml:\"grace_period_duration\"" + ]; + + // sell_order_duration is the amount of time of a Sell-Order from created to expired. + google.protobuf.Duration sell_order_duration = 3 [ + (gogoproto.nullable) = false, + (gogoproto.stdduration) = true, + (gogoproto.moretags) = "yaml:\"sell_order_duration\"" + ]; + + // enable_trading_name is the flag to enable trading of Dym-Name. + // To be used to stop trading of Dym-Name when needed. + bool enable_trading_name = 4; + + // enable_trading_alias is the flag to enable trading of Alias. + // To be used in the future when Alias trading implementation is ready + // or disable trading of Alias when needed. + bool enable_trading_alias = 5; +} diff --git a/proto/dymensionxyz/dymension/dymns/query.proto b/proto/dymensionxyz/dymension/dymns/query.proto new file mode 100644 index 000000000..4b7deec04 --- /dev/null +++ b/proto/dymensionxyz/dymension/dymns/query.proto @@ -0,0 +1,384 @@ +syntax = "proto3"; +package dymensionxyz.dymension.dymns; + +import "gogoproto/gogo.proto"; +import "google/api/annotations.proto"; +import "cosmos/base/v1beta1/coin.proto"; +import "dymensionxyz/dymension/dymns/params.proto"; +import "dymensionxyz/dymension/dymns/dym_name.proto"; +import "dymensionxyz/dymension/dymns/market.proto"; +import "dymensionxyz/dymension/dymns/alias.proto"; + +option go_package = "github.com/dymensionxyz/dymension/v3/x/dymns/types"; + +// Query defines the gRPC querier service. +service Query { + // Params queries the parameters of the module. + rpc Params(QueryParamsRequest) returns (QueryParamsResponse) { + option (google.api.http).get = "/dymensionxyz/dymension/dymns/params"; + } + + // DymName queries a Dym-Name by its name. + rpc DymName(QueryDymNameRequest) returns (QueryDymNameResponse) { + option (google.api.http).get = "/dymensionxyz/dymension/dymns/dym_name/{dym_name}"; + } + + // Alias queries the chain_id associated as well as the Sell-Order and Buy-Order IDs relates to the alias. + rpc Alias(QueryAliasRequest) returns (QueryAliasResponse) { + option (google.api.http).get = "/dymensionxyz/dymension/dymns/alias/{alias}"; + } + + // Aliases queries all the aliases for a chain id or all chains. + rpc Aliases(QueryAliasesRequest) returns (QueryAliasesResponse) { + option (google.api.http).get = "/dymensionxyz/dymension/dymns/aliases"; + } + + // ResolveDymNameAddresses resolves multiple Dym-Name Addresses to account address of each pointing to. + // + // For example: + // - "my-name@dym" => "dym1a..." + // - "another.my-name@dym" => "dym1b..." + // - "my-name@nim" => "nim1..." + // - (extra format) "0x1234...6789@nim" => "nim1..." + // - (extra format) "dym1a...@nim" => "nim1..." + rpc ResolveDymNameAddresses(ResolveDymNameAddressesRequest) returns (ResolveDymNameAddressesResponse) { + option (google.api.http).get = "/dymensionxyz/dymension/dymns/resolve"; + } + + // DymNamesOwnedByAccount queries the Dym-Names owned by an account. + rpc DymNamesOwnedByAccount(QueryDymNamesOwnedByAccountRequest) returns (QueryDymNamesOwnedByAccountResponse) { + option (google.api.http).get = "/dymensionxyz/dymension/dymns/owned_by/{owner}"; + } + + // SellOrder queries the active SO of a Dym-Name/Alias. + rpc SellOrder(QuerySellOrderRequest) returns (QuerySellOrderResponse) { + option (google.api.http).get = "/dymensionxyz/dymension/dymns/sell_order/{asset_id}"; + } + + // EstimateRegisterName estimates the cost to register a Dym-Name. + rpc EstimateRegisterName(EstimateRegisterNameRequest) returns (EstimateRegisterNameResponse) { + option (google.api.http).get = "/dymensionxyz/dymension/dymns/estimate_register_name/{name}/{duration}"; + } + + // EstimateRegisterAlias estimates the cost to register an Alias. + rpc EstimateRegisterAlias(EstimateRegisterAliasRequest) returns (EstimateRegisterAliasResponse) { + option (google.api.http).get = "/dymensionxyz/dymension/dymns/estimate_register_alias/{alias}"; + } + + // ReverseResolveAddress resolves multiple account addresses to Dym-Name Addresses which point to each. + // This function may return multiple possible Dym-Name-Addresses those point to each of the input address. + // + // For example: when we have "my-name@dym" resolves to "dym1a..." + // so reverse resolve will return "my-name@dym" when input is "dym1a..." + rpc ReverseResolveAddress(ReverseResolveAddressRequest) returns (ReverseResolveAddressResponse) { + option (google.api.http).get = "/dymensionxyz/dymension/dymns/reverse_resolve"; + } + + // TranslateAliasOrChainIdToChainId tries to translate an alias/handle to a chain id. + // If an alias/handle can not be translated to chain-id, it is treated as a chain-id and returns. + rpc TranslateAliasOrChainIdToChainId(QueryTranslateAliasOrChainIdToChainIdRequest) returns (QueryTranslateAliasOrChainIdToChainIdResponse) { + option (google.api.http).get = "/dymensionxyz/dymension/dymns/translate_alias/{alias_or_chain_id}"; + } + + // BuyOrderById queries a Buy-Order by its id. + rpc BuyOrderById(QueryBuyOrderByIdRequest) returns (QueryBuyOrderByIdResponse) { + option (google.api.http).get = "/dymensionxyz/dymension/dymns/buy_order/{id}"; + } + + // BuyOrdersPlacedByAccount queries the all the buy orders placed by an account. + rpc BuyOrdersPlacedByAccount(QueryBuyOrdersPlacedByAccountRequest) returns (QueryBuyOrdersPlacedByAccountResponse) { + option (google.api.http).get = "/dymensionxyz/dymension/dymns/buy_orders_placed_by_account/{account}"; + } + + // BuyOrdersByDymName queries all the buy orders of a Dym-Name. + rpc BuyOrdersByDymName(QueryBuyOrdersByDymNameRequest) returns (QueryBuyOrdersByDymNameResponse) { + option (google.api.http).get = "/dymensionxyz/dymension/dymns/buy_orders_by_dym_name/{name}"; + } + + // BuyOrdersOfDymNamesOwnedByAccount queries all the buy orders of all Dym-Names owned by an account. + rpc BuyOrdersOfDymNamesOwnedByAccount(QueryBuyOrdersOfDymNamesOwnedByAccountRequest) returns (QueryBuyOrdersOfDymNamesOwnedByAccountResponse) { + option (google.api.http).get = "/dymensionxyz/dymension/dymns/buy_orders_of_dym_names_owned_by_account/{account}"; + } + + // BuyOrdersByAlias queries all the buy orders of an Alias. + rpc BuyOrdersByAlias(QueryBuyOrdersByAliasRequest) returns (QueryBuyOrdersByAliasResponse) { + option (google.api.http).get = "/dymensionxyz/dymension/dymns/buy_orders_by_alias/{alias}"; + } + + // BuyOrdersOfAliasesLinkedToRollApp queries all the buy orders of all Aliases linked to a RollApp. + rpc BuyOrdersOfAliasesLinkedToRollApp(QueryBuyOrdersOfAliasesLinkedToRollAppRequest) returns (QueryBuyOrdersOfAliasesLinkedToRollAppResponse) { + option (google.api.http).get = "/dymensionxyz/dymension/dymns/buy_orders_of_aliases_linked_to_rollapp/{rollapp_id}"; + } +} + +// 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 holds all the parameters of this module. + Params params = 1 [(gogoproto.nullable) = false]; +} + +// QueryDymNameRequest is the request type for the Query/DymName RPC method. +message QueryDymNameRequest { + option (gogoproto.equal) = false; + + // dym_name is the name of the Dym-Name to query. + string dym_name = 1; +} + +// QueryDymNameResponse is the response type for the Query/DymName RPC method. +message QueryDymNameResponse { + // dym_name is the Dym-Name queried for. + DymName dym_name = 1; +} + +// QueryAliasRequest is the request type for the Query/QueryAlias RPC method. +message QueryAliasRequest { + option (gogoproto.equal) = false; + + // alias to query + string alias = 1; +} + +// QueryAliasResponse +message QueryAliasResponse { + // chain_id associated with the alias + string chain_id = 1; + + // found_sell_order is true if active Sell-Order is found for the alias. + bool found_sell_order = 2; + + // buy_order_ids is the list of Buy-Order IDs for the alias. + repeated string buy_order_ids = 3; + + // same_chain_aliases is the list of aliases for the same chain that associated with the alias. + repeated string same_chain_aliases = 4; +} + +message QueryAliasesRequest { + option (gogoproto.equal) = false; + + // chain_id to query alias for, empty for all chains. + string chain_id = 1; +} + +message QueryAliasesResponse { + // aliases_by_chain_id is the map of aliases by chain id. + map aliases_by_chain_id = 1 [(gogoproto.nullable) = false]; +} + +// ResolveDymNameAddressesRequest is the request type for the Query/ResolveDymNameAddresses RPC method. +message ResolveDymNameAddressesRequest { + option (gogoproto.equal) = false; + + // addresses defines the Dym-Name addresses to resolve. + repeated string addresses = 1; +} + +// ResultDymNameAddress defines the result of a single Dym-Name address resolution. +message ResultDymNameAddress { + // address is the input Dym-Name address to resolve. + string address = 1; + + // resolved_address is the resolved account address. + string resolved_address = 2; + + // error is the error that occurred during the resolution. + string error = 3; +} + +// ResolveDymNameAddressesResponse is the response type for the Query/ResolveDymNameAddresses RPC method. +message ResolveDymNameAddressesResponse { + // resolved_addresses defines the resolved addresses for each input Dym-Name address. + repeated ResultDymNameAddress resolved_addresses = 1 [(gogoproto.nullable) = false]; +} + +// QueryDymNamesOwnedByAccountRequest is the request type for the Query/DymNamesOwnedByAccount RPC method. +message QueryDymNamesOwnedByAccountRequest { + option (gogoproto.equal) = false; + + // owner defines the address of the owner of the Dym-Names to query for. + string owner = 1; +} + +// QueryDymNamesOwnedByAccountResponse is the response type for the Query/DymNamesOwnedByAccount RPC method. +message QueryDymNamesOwnedByAccountResponse { + // dym_names defines the Dym-Names owned by the input account. + repeated DymName dym_names = 1 [(gogoproto.nullable) = false]; +} + +// QuerySellOrderRequest is the request type for the Query/SellOrder RPC method. +message QuerySellOrderRequest { + option (gogoproto.equal) = false; + + // asset_id is the Dym-Name/Alias to query the active Sell-Order for. + string asset_id = 1; + + // asset_type can be either "Dym-Name" or "Alias". + string asset_type = 2; +} + +// QuerySellOrderResponse is the response type for the Query/SellOrder RPC method. +message QuerySellOrderResponse { + // result is the active Sell-Order for the Dym-Name/Alias. + SellOrder result = 1 [(gogoproto.nullable) = false]; +} + +// EstimateRegisterNameRequest is the request type for the Query/EstimateRegisterName RPC method. +message EstimateRegisterNameRequest { + option (gogoproto.equal) = false; + + // name is the Dym-Name to be registered. + string name = 1; + + // duration is the number of years the Dym-Name will be registered for. + int64 duration = 2; + + // owner is the bech32-encoded address of the account which owns the order. + string owner = 3; +} + +// EstimateRegisterNameResponse is the response type for the Query/EstimateRegisterName RPC method. +message EstimateRegisterNameResponse { + // first_year_price is the price to register the Dym-Name for the first year. + cosmos.base.v1beta1.Coin first_year_price = 1 [(gogoproto.nullable) = false]; + + // extend_price is the price to extend the Dym-Name registration for another year. + cosmos.base.v1beta1.Coin extend_price = 2 [(gogoproto.nullable) = false]; + + // total_price is the total price to register the Dym-Name for the specified duration. + cosmos.base.v1beta1.Coin total_price = 3 [(gogoproto.nullable) = false]; +} + +// EstimateRegisterAliasRequest is the request type for the Query/EstimateRegisterAlias RPC method. +message EstimateRegisterAliasRequest { + option (gogoproto.equal) = false; + + // alias to be registered. + string alias = 1; + + // rollapp_id is the rollapp to link the alias to. + string rollapp_id = 2; + + // owner is the bech32-encoded address of the account which owns the order. + string owner = 3; +} + +// EstimateRegisterAliasResponse is the response type for the Query/EstimateRegisterAlias RPC method. +message EstimateRegisterAliasResponse { + // price is the price to register the alias. + cosmos.base.v1beta1.Coin price = 1 [(gogoproto.nullable) = false]; +} + +// ReverseResolveAddressRequest is the request type for the Query/ReverseResolveAddress RPC method. +message ReverseResolveAddressRequest { + // addresses defines the addresses to reverse resolve. Can be both bech32 and hex addresses. + repeated string addresses = 1; + + // working_chain_id defines the chain id to use for the reverse resolution. + // Leave empty to use the host chain id. + string working_chain_id = 2; +} + +// ReverseResolveAddressResponse is the response type for the Query/ReverseResolveAddress RPC method. +message ReverseResolveAddressResponse { + // result defines the reverse resolution result for each input address. + map result = 1 [(gogoproto.nullable) = false]; + + // working_chain_id is the chain id used for the reverse resolution. + string working_chain_id = 2; +} + +message ReverseResolveAddressResult { + // candidates are the Dym-Name addresses that the input address resolves to. Take one of them. + repeated string candidates = 1; + + // error is the error that occurred during the resolution. + string error = 2; +} + +// QueryTranslateAliasOrChainIdToChainIdRequest is the request type for the Query/TranslateAliasOrChainIdToChainId RPC method. +message QueryTranslateAliasOrChainIdToChainIdRequest { + // alias_or_chain_id is the alias or chain id to translate. + string alias_or_chain_id = 1; +} + +// QueryTranslateAliasOrChainIdToChainIdResponse is the response type for the Query/TranslateAliasOrChainIdToChainId RPC method. +message QueryTranslateAliasOrChainIdToChainIdResponse { + // chain_id is the chain id that the alias or chain id translates to. + string chain_id = 1; +} + +// QueryBuyOrderByIdRequest is the request type for the Query/BuyOrderById RPC method. +message QueryBuyOrderByIdRequest { + // id of buy offer to query. + string id = 1; +} + +// QueryBuyOrderByIdResponse is the response type for the Query/BuyOrderById RPC method. +message QueryBuyOrderByIdResponse { + // buy_order is the result. + BuyOrder buy_order = 1 [(gogoproto.nullable) = false]; +} + +// QueryBuyOrdersByAccountRequest is the request type for the Query/BuyOrdersPlacedByAccount RPC method. +message QueryBuyOrdersPlacedByAccountRequest { + // account is the account address to query the placed buy offers. + string account = 1; +} + +// QueryBuyOrdersByAccountResponse is the response type for the Query/BuyOrdersPlacedByAccount RPC method. +message QueryBuyOrdersPlacedByAccountResponse { + // offers are the Buy-Orders placed by the account. + repeated BuyOrder buy_orders = 1 [(gogoproto.nullable) = false]; +} + +// QueryBuyOrdersByDymNameRequest is the request type for the Query/BuyOrdersByDymName RPC method. +message QueryBuyOrdersByDymNameRequest { + // name is the Dym-Name to query the buy offers placed for it. + string name = 1; +} + +// QueryBuyOrdersByDymNameResponse is the response type for the Query/BuyOrdersByDymName RPC method. +message QueryBuyOrdersByDymNameResponse { + // buy_orders placed for the Dym-Name. + repeated BuyOrder buy_orders = 1 [(gogoproto.nullable) = false]; +} + +// QueryBuyOrdersOfDymNamesOwnedByAccountRequest is the request type for the Query/BuyOrdersOfDymNamesOwnedByAccount RPC method. +message QueryBuyOrdersOfDymNamesOwnedByAccountRequest { + // account is the account address to query all the buy offers of the Dym-Names owned by it. + string account = 1; +} + +// QueryBuyOrdersOfDymNamesOwnedByAccountResponse is the response type for the Query/BuyOrdersOfDymNamesOwnedByAccount RPC method. +message QueryBuyOrdersOfDymNamesOwnedByAccountResponse { + // buy_orders of all the Dym-Names owned by the input account. + repeated BuyOrder buy_orders = 1 [(gogoproto.nullable) = false]; +} + +// QueryBuyOrdersByAliasRequest is the request type for the Query/BuyOrdersByAlias RPC method. +message QueryBuyOrdersByAliasRequest { + // alias is the alias to query the buy offers placed for it. + string alias = 1; +} + +// QueryBuyOrdersByAliasResponse is the response type for the Query/BuyOrdersByAlias RPC method. +message QueryBuyOrdersByAliasResponse { + // buy_orders of the input alias. + repeated BuyOrder buy_orders = 1 [(gogoproto.nullable) = false]; +} + +// QueryBuyOrdersOfAliasesLinkedToRollAppRequest is the request type for the Query/BuyOrdersOfAliasesLinkedToRollApp RPC method. +message QueryBuyOrdersOfAliasesLinkedToRollAppRequest { + // rollapp_id is the rollapp to query all the buy offers of the aliases linked to it + string rollapp_id = 1; +} + +// QueryBuyOrdersOfAliasesLinkedToRollAppResponse is the response type for the Query/BuyOrdersOfAliasesLinkedToRollApp RPC method. +message QueryBuyOrdersOfAliasesLinkedToRollAppResponse { + // buy_orders are all the buy orders of the aliases linked to the input rollapp. + repeated BuyOrder buy_orders = 1 [(gogoproto.nullable) = false]; +} diff --git a/proto/dymensionxyz/dymension/dymns/tx.proto b/proto/dymensionxyz/dymension/dymns/tx.proto new file mode 100644 index 000000000..7c9281e66 --- /dev/null +++ b/proto/dymensionxyz/dymension/dymns/tx.proto @@ -0,0 +1,331 @@ +syntax = "proto3"; +package dymensionxyz.dymension.dymns; + +import "gogoproto/gogo.proto"; +import "cosmos/base/v1beta1/coin.proto"; +import "cosmos/msg/v1/msg.proto"; +import "cosmos_proto/cosmos.proto"; +import "dymensionxyz/dymension/dymns/market.proto"; +import "dymensionxyz/dymension/dymns/params.proto"; + +option go_package = "github.com/dymensionxyz/dymension/v3/x/dymns/types"; + +// Msg defines the Msg service. +service Msg { + // RegisterName is message handler, handles registration of a new Dym-Name + // or extends the ownership duration of an existing Dym-Name. + rpc RegisterName(MsgRegisterName) returns (MsgRegisterNameResponse) {} + // RegisterAlias is message handler, handles registration of a new Alias for an existing RollApp. + rpc RegisterAlias(MsgRegisterAlias) returns (MsgRegisterAliasResponse) {} + // TransferDymNameOwnership is message handler, + // handles transfer of ownership of a Dym-Name, performed by the owner. + rpc TransferDymNameOwnership(MsgTransferDymNameOwnership) returns (MsgTransferDymNameOwnershipResponse) {} + // SetController is message handler, + // handles setting a controller for a Dym-Name, performed by the owner. + rpc SetController(MsgSetController) returns (MsgSetControllerResponse) {} + // UpdateResolveAddress is message handler, + // handles updating Dym-Name-Address resolution configuration, performed by the controller. + rpc UpdateResolveAddress(MsgUpdateResolveAddress) returns (MsgUpdateResolveAddressResponse) {} + // UpdateDetails is message handler, + // handles updating Dym-Name details, performed by the controller. + rpc UpdateDetails(MsgUpdateDetails) returns (MsgUpdateDetailsResponse) {} + + // PlaceSellOrder is message handler, + // handles creating a Sell-Order that advertise a Dym-Name/Alias is for sale, performed by the owner. + rpc PlaceSellOrder(MsgPlaceSellOrder) returns (MsgPlaceSellOrderResponse) {} + // CancelSellOrder is message handler, + // handles canceling Sell-Order, performed by the owner. + // This will stop the advertisement and remove the Dym-Name/Alias sale from the market. + // Can only be performed if no one has placed a bid on the asset. + rpc CancelSellOrder(MsgCancelSellOrder) returns (MsgCancelSellOrderResponse) {} + // PurchaseOrder is message handler, + // handles purchasing a Dym-Name/Alias from a Sell-Order, performed by the buyer. + rpc PurchaseOrder(MsgPurchaseOrder) returns (MsgPurchaseOrderResponse) {} + + // PlaceBuyOrder is message handler, + // handles creating an offer to buy a Dym-Name/Alias, performed by the buyer. + rpc PlaceBuyOrder(MsgPlaceBuyOrder) returns (MsgPlaceBuyOrderResponse) {} + // CancelBuyOrder is message handler, + // handles canceling a Buy-Order, performed by the buyer who placed the offer. + rpc CancelBuyOrder(MsgCancelBuyOrder) returns (MsgCancelBuyOrderResponse) {} + // AcceptBuyOrder is message handler, + // handles accepting a Buy-Order or raising the amount for negotiation, + // performed by the owner of the asset. + rpc AcceptBuyOrder(MsgAcceptBuyOrder) returns (MsgAcceptBuyOrderResponse) {} + + // UpdateParams is used for updating module params. + rpc UpdateParams(MsgUpdateParams) returns (MsgUpdateParamsResponse); +} + +// MsgRegisterName defines the message used for user to register or extends ownership duration of a Dym-Name. +message MsgRegisterName { + option (cosmos.msg.v1.signer) = "owner"; + // name is the Dym-Name to be registered. + string name = 1; + + // owner is the account address of the account which owns the order. + string owner = 2; + + // duration is the number of years the Dym-Name will be registered for. + int64 duration = 3; + + // confirm_payment is used to ensure user acknowledge of the amount coin that the user must pay. + // If the amount mis-match with the actual payment, the transaction will be rejected. + cosmos.base.v1beta1.Coin confirm_payment = 4 [(gogoproto.nullable) = false]; + + // contact defines an optional contact information for the Dym-Name. + string contact = 5; +} + +// MsgRegisterNameResponse defines the response for the name registration. +message MsgRegisterNameResponse {} + +// MsgRegisterAlias defines the message used for user to register a new Alias for their owned RollApp. +message MsgRegisterAlias { + option (cosmos.msg.v1.signer) = "owner"; + // alias to be registered. + string alias = 1; + + // rollapp_id is RollApp ID that the new Alias to be assigned for. + string rollapp_id = 2; + + // owner is the account address of the RollApp which owns the Alias. + string owner = 3; + + // confirm_payment is used to ensure user acknowledge of the amount coin that the user must pay. + // If the amount mis-match with the actual payment, the transaction will be rejected. + cosmos.base.v1beta1.Coin confirm_payment = 4 [(gogoproto.nullable) = false]; +} + +// MsgRegisterAliasResponse defines the response for the alias registration. +message MsgRegisterAliasResponse {} + +// MsgTransferDymNameOwnership defines the message used for user to transfer ownership of a Dym-Name. +message MsgTransferDymNameOwnership { + option (cosmos.msg.v1.signer) = "owner"; + + // name is the Dym-Name to be transferred ownership. + string name = 1; + + // owner is the account address of the account which is currently owner of the Dym-Name. + string owner = 2; + + // new_owner is the account address of the next account which will own the Dym-Name. + string new_owner = 3; +} + +// MsgTransferDymNameOwnershipResponse defines the response for the name transfer. +message MsgTransferDymNameOwnershipResponse {} + +// MsgSetController defines the message used for user to set a controller for a Dym-Name. +message MsgSetController { + option (cosmos.msg.v1.signer) = "owner"; + + // name is the Dym-Name to change controller. + string name = 1; + + // owner is the account address of the account which is currently owner of the Dym-Name. + string owner = 2; + + // controller is the account address of the account which will be the new controller of the Dym-Name. + string controller = 3; +} + +// MsgSetControllerResponse defines the response for the name controller setting. +message MsgSetControllerResponse {} + +// MsgUpdateResolveAddress defines the message used for user to update the resolve address of a Dym-Name. +message MsgUpdateResolveAddress { + option (cosmos.msg.v1.signer) = "controller"; + // name is the Dym-Name to be updated by controller. + string name = 1; + + // controller is the account address of the account which has permission to update the Dym-Name. + string controller = 2; + + // chain_id is an optional field, chain-based mapping + string chain_id = 3; + + // sub_name is an optional field, sub-domain-like mapping + string sub_name = 4; + + // resolve_to is the address that this config will resolve to. + // Leave it empty to remove the resolve address. + string resolve_to = 5; +} + +// MsgUpdateResolveAddressResponse defines the response for the name resolve address update. +message MsgUpdateResolveAddressResponse {} + +// MsgUpdateDetails defines the message used for user to update the details of a Dym-Name. +message MsgUpdateDetails { + option (cosmos.msg.v1.signer) = "controller"; + + // name is the Dym-Name to be updated details. + string name = 1; + + // controller is the bech32-encoded address of the account which has permission to update details of the Dym-Name. + string controller = 2; + + // contact is an optional field, contact information of the Dym-Name. + string contact = 3; + + // clear_configs is an optional field, set to true to clear the current configuration. + bool clear_configs = 4; +} + +// MsgUpdateDetailsResponse defines the response for the name details update. +message MsgUpdateDetailsResponse {} + +// MsgPlaceSellOrder defines the message used for user to put a Dym-Name/Alias for sale. +message MsgPlaceSellOrder { + option (cosmos.msg.v1.signer) = "owner"; + + // asset_id is the Dym-Name/Alias to be opened for sell. + string asset_id = 1; + + // asset_type is the type of the asset of the order, is Dym-Name/Alias. + AssetType asset_type = 2; + + // owner is the bech32-encoded address of the account which owns the order. + string owner = 3; + + // min_price is the minimum price that buyer must pay for the Dym-Name. + cosmos.base.v1beta1.Coin min_price = 4 [(gogoproto.nullable) = false]; + + // sell_price is the price that buyer must pay for the Dym-Name to immediately own it. + // Leaving this field empty/zero means + // the Dym-Name is not for immediate purchase and must wait until the Sell-Order expired. + cosmos.base.v1beta1.Coin sell_price = 5; +} + +// MsgPlaceSellOrderResponse defines the response after placed the Sell-Order. +message MsgPlaceSellOrderResponse {} + +// MsgCancelSellOrder defines the message used for user to cancel a Sell-Order. +message MsgCancelSellOrder { + option (cosmos.msg.v1.signer) = "owner"; + + // asset_id is the Dym-Name/Alias to cancel selling. + string asset_id = 1; + + // asset_type is the type of the asset of the order, is Dym-Name/Alias. + AssetType asset_type = 2; + + // owner is the bech32-encoded address of the account which owns the Dym-Name as well as the order. + string owner = 3; +} + +// MsgCancelSellOrderResponse defines the response for the Sell-Order cancellation. +message MsgCancelSellOrderResponse {} + +// MsgPurchaseOrder defines the message used for user to bid/purchase a Sell-Order. +message MsgPurchaseOrder { + option (cosmos.msg.v1.signer) = "buyer"; + + // asset_id is the Dym-Name/Alias to be purchased for. + string asset_id = 1; + + // asset_type is the type of the asset of the order, is Dym-Name/Alias. + AssetType asset_type = 2; + + // params is the list of parameters of the bid. + // It is empty for asset type Dym-Name. + // It has one element for asset type Alias, which is the rollapp_id to assigned for. + repeated string params = 3; + + // buyer is the account address of the account which is purchasing the Dym-Name. + string buyer = 4; + + // offer is the price that buyer is willing to pay for the Dym-Name. + cosmos.base.v1beta1.Coin offer = 5 [(gogoproto.nullable) = false]; +} + +// MsgPurchaseOrderResponse defines the response for the purchase order. +message MsgPurchaseOrderResponse {} + +// MsgPlaceBuyOrder defines the message used for user to place an offer, to buy a Dym-Name. +message MsgPlaceBuyOrder { + option (cosmos.msg.v1.signer) = "buyer"; + // asset_id is the Dym-Name/Alias wishing to buy. + string asset_id = 1; + + // asset_type is the type of the asset of the order, is Dym-Name/Alias. + AssetType asset_type = 2; + + // params is the list of parameters of the offer. + // It is empty for asset type Dym-Name. + // It has one element for asset type Alias, which is the rollapp_id to assigned for. + repeated string params = 3; + + // buyer is the account address of the account which is purchasing the Dym-Name. + string buyer = 4; + + // continue_order_id is the optional field, if the buyer wants to extends an existing offer. + string continue_order_id = 5; + + // offer is the price that buyer is willing to pay for the Dym-Name. + cosmos.base.v1beta1.Coin offer = 6 [(gogoproto.nullable) = false]; +} + +// MsgPlaceBuyOrderResponse defines the response after placed the Buy-Order. +message MsgPlaceBuyOrderResponse { + // order_id is the unique identifier of the new generated Buy-Order. + string order_id = 1; +} + +// MsgCancelBuyOrder defines the message used for user to cancel a Buy-Order. +message MsgCancelBuyOrder { + option (cosmos.msg.v1.signer) = "buyer"; + // order_id is the unique identifier of the Buy-Order. + string order_id = 1; + + // buyer is the account address of the account which is purchasing the Dym-Name. + string buyer = 2; +} + +// MsgCancelBuyOrderResponse defines the response for the Buy-Order cancellation. +message MsgCancelBuyOrderResponse {} + +// MsgAcceptBuyOrder defines the message used for user to accept a Buy-Order. +message MsgAcceptBuyOrder { + option (cosmos.msg.v1.signer) = "owner"; + + // order_id is the unique identifier of the Buy-Order. + string order_id = 1; + + // owner is the account address of the account which owns the Dym-Name. + // And is the only one who can accept the offer. + string owner = 2; + + // min_accept is the minimum price that the owner is willing to accept for the Dym-Name. + // If this amount matches the offer, the Dym-Name will be transferred to the buyer. + // If the offer is lower than this amount, this information will be updated into offer record to inform the buyer. + cosmos.base.v1beta1.Coin min_accept = 3 [(gogoproto.nullable) = false]; +} + +// MsgAcceptBuyOrderResponse defines the response for the Buy-Order acceptance. +message MsgAcceptBuyOrderResponse { + // accepted is the flag to indicate if the offer is accepted (price matched). + bool accepted = 1; +} + +// MsgUpdateParams allows to update module params. +message MsgUpdateParams { + option (cosmos.msg.v1.signer) = "authority"; + + // authority is the address that controls the module. + string authority = 1 [ (cosmos_proto.scalar) = "cosmos.AddressString" ]; + + // new_price_params is the optional update new price params if provided. + PriceParams new_price_params = 2; + + // new_chains_params is the optional update new chains params if provided. + // Not recommended, consider using a dedicated gov proposal for this instead of update params by this way. + ChainsParams new_chains_params = 3; + + // new_misc_params is the optional update new misc params if provided. + MiscParams new_misc_params = 4; +} + +message MsgUpdateParamsResponse {} \ No newline at end of file diff --git a/scripts/init_config.sh b/scripts/init_config.sh index c10e6f8b1..da9bf6dc0 100644 --- a/scripts/init_config.sh +++ b/scripts/init_config.sh @@ -46,6 +46,7 @@ set_EVM_params set_bank_denom_metadata set_epochs_params set_incentives_params +set_dymns_params # Setup genesis account and transaction dymd keys add "$KEY_NAME" --keyring-backend test diff --git a/scripts/protoc-swagger-gen.sh b/scripts/protoc-swagger-gen.sh index a022a175f..75366634f 100755 --- a/scripts/protoc-swagger-gen.sh +++ b/scripts/protoc-swagger-gen.sh @@ -10,7 +10,7 @@ printf "version: v1\nname: buf.build/dymensionxyz/dymension\n" > "$SWAGGER_DIR/p cp ./proto/buf.gen.swagger.yaml "$SWAGGER_DIR/proto/buf.gen.swagger.yaml" # copy existing proto files -cp -r ./proto/dymension "$SWAGGER_DIR/proto" +cp -r ./proto/dymensionxyz "$SWAGGER_DIR/proto" # create temporary folder to store intermediate results from `buf generate` mkdir -p ./tmp-swagger-gen diff --git a/scripts/set_params.sh b/scripts/set_params.sh index 93634f5a4..51c8806b1 100644 --- a/scripts/set_params.sh +++ b/scripts/set_params.sh @@ -21,4 +21,5 @@ set_misc_params set_EVM_params set_bank_denom_metadata set_epochs_params -set_incentives_params \ No newline at end of file +set_incentives_params +set_dymns_params \ No newline at end of file diff --git a/scripts/setup_local.sh b/scripts/setup_local.sh index 55f7b2d05..ebd9e85ac 100755 --- a/scripts/setup_local.sh +++ b/scripts/setup_local.sh @@ -94,6 +94,7 @@ set_EVM_params set_bank_denom_metadata set_epochs_params set_incentives_params +set_dymns_params echo "Enable monitoring? (Y/n) " read -r answer diff --git a/scripts/src/genesis_config_commands.sh b/scripts/src/genesis_config_commands.sh index 691594166..dcae54ff3 100644 --- a/scripts/src/genesis_config_commands.sh +++ b/scripts/src/genesis_config_commands.sh @@ -37,7 +37,7 @@ set_consenus_params() { # MaxGas: 10000000, // ten million echo "setting consensus params" jq '.consensus_params["block"]["max_bytes"] = "4194304"' "$GENESIS_FILE" > "$tmp" && mv "$tmp" "$GENESIS_FILE" - jq '.consensus_params["block"]["max_gas"] = "10000000"' "$GENESIS_FILE" > "$tmp" && mv "$tmp" "$GENESIS_FILE" + jq '.consensus_params["block"]["max_gas"] = "40000000"' "$GENESIS_FILE" > "$tmp" && mv "$tmp" "$GENESIS_FILE" } set_EVM_params() { @@ -111,4 +111,10 @@ enable_monitoring() { sed -ie 's/enabled-unsafe-cors.*$/enabled-unsafe-cors = true/' "$APP_CONFIG_FILE" sed -ie 's/enable-unsafe-cors.*$/enabled-unsafe-cors = true/' "$APP_CONFIG_FILE" sed -ie 's/cors_allowed_origins.*$/cors_allowed_origins = ["*"]/' "$TENDERMINT_CONFIG_FILE" +} + +# this set_dymns_params function should only be used on local-net for development purpose +set_dymns_params() { + echo "setting dymns params" + jq '.app_state.dymns.params.misc.sell_order_duration = "2m"' "$GENESIS_FILE" > "$tmp" && mv "$tmp" "$GENESIS_FILE" } \ No newline at end of file diff --git a/scripts/start_localnode.sh b/scripts/start_localnode.sh index 46c933841..c91d373fd 100755 --- a/scripts/start_localnode.sh +++ b/scripts/start_localnode.sh @@ -67,6 +67,7 @@ set_EVM_params set_bank_denom_metadata set_epochs_params set_incentives_params +set_dymns_params dymd keys add "$KEY_NAME" --keyring-backend test dymd add-genesis-account "$(dymd keys show "$KEY_NAME" -a --keyring-backend test)" "$TOKEN_AMOUNT" diff --git a/scripts/start_node_debug.sh b/scripts/start_node_debug.sh index 07c10ce10..cb5043ebd 100755 --- a/scripts/start_node_debug.sh +++ b/scripts/start_node_debug.sh @@ -67,6 +67,7 @@ set_EVM_params set_bank_denom_metadata set_epochs_params set_incentives_params +set_dymns_params dymd keys add "$KEY_NAME" --keyring-backend test dymd add-genesis-account "$(dymd keys show "$KEY_NAME" -a --keyring-backend test)" "$TOKEN_AMOUNT" diff --git a/testutil/keeper/dymns.go b/testutil/keeper/dymns.go new file mode 100644 index 000000000..ad95fef54 --- /dev/null +++ b/testutil/keeper/dymns.go @@ -0,0 +1,116 @@ +package keeper + +import ( + "testing" + "time" + + authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + "github.com/dymensionxyz/dymension/v3/app/params" + + tmdb "github.com/cometbft/cometbft-db" + "github.com/cometbft/cometbft/libs/log" + tmproto "github.com/cometbft/cometbft/proto/tendermint/types" + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/store" + storetypes "github.com/cosmos/cosmos-sdk/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" + typesparams "github.com/cosmos/cosmos-sdk/x/params/types" + "github.com/stretchr/testify/require" + + dymnskeeper "github.com/dymensionxyz/dymension/v3/x/dymns/keeper" + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" + rollappkeeper "github.com/dymensionxyz/dymension/v3/x/rollapp/keeper" + rollapptypes "github.com/dymensionxyz/dymension/v3/x/rollapp/types" +) + +func DymNSKeeper(t testing.TB) (dymnskeeper.Keeper, dymnstypes.BankKeeper, rollappkeeper.Keeper, sdk.Context) { + dymNsStoreKey := sdk.NewKVStoreKey(dymnstypes.StoreKey) + dymNsMemStoreKey := storetypes.NewMemoryStoreKey(dymnstypes.MemStoreKey) + + authStoreKey := sdk.NewKVStoreKey(authtypes.StoreKey) + + bankStoreKey := sdk.NewKVStoreKey(banktypes.StoreKey) + + rollappStoreKey := sdk.NewKVStoreKey(rollapptypes.StoreKey) + rollappMemStoreKey := storetypes.NewMemoryStoreKey(rollapptypes.MemStoreKey) + + db := tmdb.NewMemDB() + stateStore := store.NewCommitMultiStore(db) + stateStore.MountStoreWithDB(dymNsStoreKey, storetypes.StoreTypeIAVL, db) + stateStore.MountStoreWithDB(dymNsMemStoreKey, storetypes.StoreTypeMemory, nil) + stateStore.MountStoreWithDB(authStoreKey, storetypes.StoreTypeIAVL, db) + stateStore.MountStoreWithDB(bankStoreKey, storetypes.StoreTypeIAVL, db) + stateStore.MountStoreWithDB(rollappStoreKey, storetypes.StoreTypeIAVL, db) + stateStore.MountStoreWithDB(rollappMemStoreKey, storetypes.StoreTypeMemory, nil) + require.NoError(t, stateStore.LoadLatestVersion()) + + registry := codectypes.NewInterfaceRegistry() + cdc := codec.NewProtoCodec(registry) + + dymNSParamsSubspace := typesparams.NewSubspace(cdc, + dymnstypes.Amino, + dymNsStoreKey, + dymNsMemStoreKey, + "DymNSParams", + ) + + rollappParamsSubspace := typesparams.NewSubspace(cdc, + rollapptypes.Amino, + rollappStoreKey, + rollappMemStoreKey, + "RollappParams", + ) + + authKeeper := authkeeper.NewAccountKeeper( + cdc, + authStoreKey, + authtypes.ProtoBaseAccount, + map[string][]string{ + banktypes.ModuleName: {authtypes.Minter, authtypes.Burner}, + dymnstypes.ModuleName: {authtypes.Minter, authtypes.Burner}, + }, + params.AccountAddressPrefix, + authtypes.NewModuleAddress(govtypes.ModuleName).String(), + ) + authtypes.RegisterInterfaces(registry) + + bankKeeper := bankkeeper.NewBaseKeeper( + cdc, + bankStoreKey, + authKeeper, + map[string]bool{}, + authtypes.NewModuleAddress(govtypes.ModuleName).String(), + ) + banktypes.RegisterInterfaces(registry) + + rollappKeeper := rollappkeeper.NewKeeper( + cdc, + rollappStoreKey, + rollappParamsSubspace, + nil, nil, nil, + ) + + k := dymnskeeper.NewKeeper(cdc, + dymNsStoreKey, + dymNSParamsSubspace, + bankKeeper, + rollappKeeper, + authtypes.NewModuleAddress(govtypes.ModuleName).String(), + ) + + ctx := sdk.NewContext(stateStore, tmproto.Header{}, false, log.NewNopLogger()) + ctx = ctx.WithBlockTime(time.Now().UTC()) + + // Initialize params + moduleParams := dymnstypes.DefaultParams() + moduleParams.Chains.AliasesOfChainIds = nil + err := k.SetParams(ctx, moduleParams) + require.NoError(t, err) + + return k, bankKeeper, *rollappKeeper, ctx +} diff --git a/x/dymns/client/cli/constants.go b/x/dymns/client/cli/constants.go new file mode 100644 index 000000000..5bc9964b0 --- /dev/null +++ b/x/dymns/client/cli/constants.go @@ -0,0 +1,15 @@ +package cli + +const ( + // maxDymBuyValueInteractingCLI is a hard limit for the number of Dym values can be used for Bidding/Offer... in a single CLI command. + // It was designed to reduce the risk of input wrong number. + // If working with higher DYM amount, please go to dApp. + maxDymBuyValueInteractingCLI = 10_000 + + // maxDymSellValueInteractingCLI is a hard limit for the number of Dym values can be used for Selling in a single CLI command. + // It was designed to reduce the mistake that user input an unexpected high number. + maxDymSellValueInteractingCLI = 1_000_000 + + // adymToDymMultiplier is used in CLI to convert `adym` to `DYM`. + adymToDymMultiplier = 1e18 +) diff --git a/x/dymns/client/cli/query.go b/x/dymns/client/cli/query.go new file mode 100644 index 000000000..f6bbe65e4 --- /dev/null +++ b/x/dymns/client/cli/query.go @@ -0,0 +1,35 @@ +package cli + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/cosmos/cosmos-sdk/client" + + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" +) + +// GetQueryCmd returns the cli query commands for this module +func GetQueryCmd() *cobra.Command { + // Group DymNS queries under a subcommand + cmd := &cobra.Command{ + Use: dymnstypes.ModuleName, + Short: fmt.Sprintf("Querying commands for the %s module", dymnstypes.ModuleName), + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + + cmd.AddCommand( + CmdQueryParams(), + CmdQueryDymName(), + CmdQueryAlias(), + CmdQuerySellOrder(), + CmdQueryBuyOrder(), + CmdQueryResolveDymNameAddress(), + CmdQueryReverseResolveDymNameAddress(), + ) + + return cmd +} diff --git a/x/dymns/client/cli/query_alias.go b/x/dymns/client/cli/query_alias.go new file mode 100644 index 000000000..c42d2f2f9 --- /dev/null +++ b/x/dymns/client/cli/query_alias.go @@ -0,0 +1,52 @@ +package cli + +import ( + "fmt" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/version" + dymnsutils "github.com/dymensionxyz/dymension/v3/x/dymns/utils" + "github.com/spf13/cobra" + + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" +) + +// CmdQueryAlias is the CLI command for querying Alias information +func CmdQueryAlias() *cobra.Command { + cmd := &cobra.Command{ + Use: "alias [alias]", + Short: "Get alias (aka handle) information", + Example: fmt.Sprintf("%s q %s alias myname", version.AppName, dymnstypes.ModuleName), + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + alias := args[0] + + if !dymnsutils.IsValidAlias(alias) { + return fmt.Errorf("input is not a valid alias: %s", alias) + } + + clientCtx := client.GetClientContextFromCmd(cmd) + queryClient := dymnstypes.NewQueryClient(clientCtx) + + res, err := queryClient.Alias(cmd.Context(), &dymnstypes.QueryAliasRequest{ + Alias: alias, + }) + if err != nil { + return fmt.Errorf("failed to fetch information of '%s': %w", alias, err) + } + + if res == nil || res.ChainId == "" { + fmt.Println("Alias is not registered.") + return nil + } + + fmt.Printf("Alias '%s' is being used by '%s'", alias, res.ChainId) + return nil + }, + } + + flags.AddQueryFlagsToCmd(cmd) + + return cmd +} diff --git a/x/dymns/client/cli/query_buy_order.go b/x/dymns/client/cli/query_buy_order.go new file mode 100644 index 000000000..0a6309889 --- /dev/null +++ b/x/dymns/client/cli/query_buy_order.go @@ -0,0 +1,238 @@ +package cli + +import ( + "context" + "fmt" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/version" + dymnsutils "github.com/dymensionxyz/dymension/v3/x/dymns/utils" + "github.com/spf13/cobra" + + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" +) + +const ( + flagTargetType = "target-type" + + targetTypeById = "offer-id" + targetTypeBuyer = "buyer" + targetTypeOwner = "owner" + targetTypeDymName = "name" + targetTypeAlias = "alias" + targetTypeRollApp = "rollapp" +) + +// CmdQueryBuyOrder is the CLI command for querying Buy-Orders a Dym-Name +func CmdQueryBuyOrder() *cobra.Command { + cmd := &cobra.Command{ + Use: "buy-order [target]", + Aliases: []string{"offer"}, + Short: fmt.Sprintf("Get list of Buy-Orders corresponding to the offer-id/buyer/owner/name/alias/rollapp, provided by the flag --%s", flagTargetType), + Example: fmt.Sprintf( + `%s q %s offer 1 --%s=%s +%s q %s offer dym1buyer --%s=%s +%s q %s offer dym1owner --%s=%s +%s q %s offer my-name --%s=%s +%s q %s offer dym --%s=%s +%s q %s offer rollapp_1-1 --%s=%s +`, + version.AppName, dymnstypes.ModuleName, flagTargetType, targetTypeById, + version.AppName, dymnstypes.ModuleName, flagTargetType, targetTypeBuyer, + version.AppName, dymnstypes.ModuleName, flagTargetType, targetTypeOwner, + version.AppName, dymnstypes.ModuleName, flagTargetType, targetTypeDymName, + version.AppName, dymnstypes.ModuleName, flagTargetType, targetTypeAlias, + version.AppName, dymnstypes.ModuleName, flagTargetType, targetTypeRollApp, + ), + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + targetType, err := cmd.Flags().GetString(flagTargetType) + if err != nil { + return err + } + + if targetType == "" { + return fmt.Errorf("flag --%s is required", flagTargetType) + } + + var offers []dymnstypes.BuyOrder + + clientCtx := client.GetClientContextFromCmd(cmd) + queryClient := dymnstypes.NewQueryClient(clientCtx) + queryCtx := cmd.Context() + + switch targetType { + case targetTypeById: + var offer *dymnstypes.BuyOrder + offer, err = queryOfferById(queryClient, queryCtx, args[0]) + if err == nil && offer != nil { + offers = append(offers, *offer) + } + case targetTypeBuyer: + offers, err = queryOffersPlacedByBuyer(queryClient, queryCtx, args[0]) + case targetTypeOwner: + offers, err = queryOffersOfDymNamesOwnedByOwner(queryClient, queryCtx, args[0]) + case targetTypeDymName: + offers, err = queryOffersByDymName(queryClient, queryCtx, args[0]) + case targetTypeAlias: + offers, err = queryOffersByAlias(queryClient, queryCtx, args[0]) + case targetTypeRollApp: + offers, err = queryOffersOfAliasesLinkedToRollApp(queryClient, queryCtx, args[0]) + default: + return fmt.Errorf("invalid target type: %s", targetType) + } + + if err != nil { + return err + } + + if len(offers) == 0 { + fmt.Println("No offers found") + return nil + } + + for i, offer := range offers { + if i > 0 { + fmt.Println("___") + } + if err := printBuyOrder(offer); err != nil { + fmt.Printf("Bad offer: %s\n", err.Error()) + fmt.Println("Raw: ", offer) + } + } + + return nil + }, + } + + flags.AddQueryFlagsToCmd(cmd) + + cmd.Flags().String(flagTargetType, "", fmt.Sprintf("Target type to query for, one of: %s/%s/%s/%s/%s/%s", targetTypeById, targetTypeBuyer, targetTypeOwner, targetTypeDymName, targetTypeAlias, targetTypeRollApp)) + + return cmd +} + +func printBuyOrder(offer dymnstypes.BuyOrder) error { + if err := offer.Validate(); err != nil { + return err + } + fmt.Printf("ID: %s\n", offer.Id) + fmt.Printf(" Buyer: %s\n", offer.Buyer) + fmt.Printf(" Type: %s\n", offer.AssetType.PrettyName()) + if offer.AssetType == dymnstypes.TypeName { + fmt.Printf(" Dym-Name: %s\n", offer.AssetId) + } else if offer.AssetType == dymnstypes.TypeAlias { + fmt.Printf(" Alias: %s\n", offer.AssetId) + fmt.Printf(" For RollApp: %s\n", offer.Params[0]) + } + fmt.Printf(" Offer Price: %s\n", offer.OfferPrice) + if estAmt, ok := toEstimatedCoinAmount(offer.OfferPrice); ok { + fmt.Printf(" (~ %s)\n", estAmt) + } + fmt.Printf(" Counterparty Offer Price: ") + if offer.CounterpartyOfferPrice != nil { + fmt.Printf("%s\n", *offer.CounterpartyOfferPrice) + if estAmt, ok := toEstimatedCoinAmount(*offer.CounterpartyOfferPrice); ok { + fmt.Printf(" (~ %s)\n", estAmt) + } + } else { + fmt.Println("None") + } + return nil +} + +// queryOfferById fetches a Buy-Order by its ID +func queryOfferById(queryClient dymnstypes.QueryClient, ctx context.Context, orderId string) (*dymnstypes.BuyOrder, error) { + if !dymnstypes.IsValidBuyOrderId(orderId) { + return nil, fmt.Errorf("input is not a valid Buy-Order ID: %s", orderId) + } + + res, err := queryClient.BuyOrderById(ctx, &dymnstypes.QueryBuyOrderByIdRequest{ + Id: orderId, + }) + if err != nil { + return nil, fmt.Errorf("failed to fetch buy offer by ID '%s': %w", orderId, err) + } + + return &res.BuyOrder, nil +} + +// queryOffersPlacedByBuyer fetches Buy-Orders placed by a buyer +func queryOffersPlacedByBuyer(queryClient dymnstypes.QueryClient, ctx context.Context, buyer string) ([]dymnstypes.BuyOrder, error) { + if !dymnsutils.IsValidBech32AccountAddress(buyer, true) { + return nil, fmt.Errorf("input buyer address '%s' is not a valid bech32 account address", buyer) + } + + res, err := queryClient.BuyOrdersPlacedByAccount(ctx, &dymnstypes.QueryBuyOrdersPlacedByAccountRequest{ + Account: buyer, + }) + if err != nil { + return nil, fmt.Errorf("failed to fetch Buy-Orders placed by buyer '%s': %w", buyer, err) + } + + return res.BuyOrders, nil +} + +// queryOffersOfDymNamesOwnedByOwner fetches all Buy-Orders of all Dym-Names owned by an owner +func queryOffersOfDymNamesOwnedByOwner(queryClient dymnstypes.QueryClient, ctx context.Context, owner string) ([]dymnstypes.BuyOrder, error) { + if !dymnsutils.IsValidBech32AccountAddress(owner, true) { + return nil, fmt.Errorf("input owner address is not a valid bech32 account address: %s", owner) + } + + res, err := queryClient.BuyOrdersOfDymNamesOwnedByAccount(ctx, &dymnstypes.QueryBuyOrdersOfDymNamesOwnedByAccountRequest{ + Account: owner, + }) + if err != nil { + return nil, fmt.Errorf("failed to fetch Buy-Orders of Dym-Names owned by '%s': %w", owner, err) + } + + return res.BuyOrders, nil +} + +// queryOffersOfAliasesLinkedToRollApp fetches all Buy-Orders of all Aliases linked to a RollApp +func queryOffersOfAliasesLinkedToRollApp(queryClient dymnstypes.QueryClient, ctx context.Context, rollAppId string) ([]dymnstypes.BuyOrder, error) { + if !dymnsutils.IsValidChainIdFormat(rollAppId) { + return nil, fmt.Errorf("input RollApp ID is invalid: %s", rollAppId) + } + + res, err := queryClient.BuyOrdersOfAliasesLinkedToRollApp(ctx, &dymnstypes.QueryBuyOrdersOfAliasesLinkedToRollAppRequest{ + RollappId: rollAppId, + }) + if err != nil { + return nil, fmt.Errorf("failed to fetch Buy-Orders of aliases linked to '%s': %w", rollAppId, err) + } + + return res.BuyOrders, nil +} + +// queryOffersByDymName fetches all Buy-Orders of a Dym-Name +func queryOffersByDymName(queryClient dymnstypes.QueryClient, ctx context.Context, dymName string) ([]dymnstypes.BuyOrder, error) { + if !dymnsutils.IsValidDymName(dymName) { + return nil, fmt.Errorf("input is not a valid Dym-Name: %s", dymName) + } + + res, err := queryClient.BuyOrdersByDymName(ctx, &dymnstypes.QueryBuyOrdersByDymNameRequest{ + Name: dymName, + }) + if err != nil { + return nil, fmt.Errorf("failed to fetch Buy-Orders of Dym-Name '%s': %w", dymName, err) + } + + return res.BuyOrders, nil +} + +func queryOffersByAlias(queryClient dymnstypes.QueryClient, ctx context.Context, alias string) ([]dymnstypes.BuyOrder, error) { + if !dymnsutils.IsValidAlias(alias) { + return nil, fmt.Errorf("input is not a valid alias: %s", alias) + } + + res, err := queryClient.BuyOrdersByAlias(ctx, &dymnstypes.QueryBuyOrdersByAliasRequest{ + Alias: alias, + }) + if err != nil { + return nil, fmt.Errorf("failed to fetch Buy-Orders of Alias '%s': %w", alias, err) + } + + return res.BuyOrders, nil +} diff --git a/x/dymns/client/cli/query_dym_name.go b/x/dymns/client/cli/query_dym_name.go new file mode 100644 index 000000000..be6cb68a0 --- /dev/null +++ b/x/dymns/client/cli/query_dym_name.go @@ -0,0 +1,51 @@ +package cli + +import ( + "fmt" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/version" + dymnsutils "github.com/dymensionxyz/dymension/v3/x/dymns/utils" + "github.com/spf13/cobra" + + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" +) + +// CmdQueryDymName is the CLI command for querying Dym-Name information +func CmdQueryDymName() *cobra.Command { + cmd := &cobra.Command{ + Use: "dym-name [Dym-Name]", + Aliases: []string{"name"}, + Short: "Get Dym-Name information", + Example: fmt.Sprintf("%s q %s dym-name myname", version.AppName, dymnstypes.ModuleName), + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + dymName := args[0] + + if !dymnsutils.IsValidDymName(dymName) { + return fmt.Errorf("input is not a valid Dym-Name: %s", dymName) + } + + clientCtx := client.GetClientContextFromCmd(cmd) + queryClient := dymnstypes.NewQueryClient(clientCtx) + + res, err := queryClient.DymName(cmd.Context(), &dymnstypes.QueryDymNameRequest{ + DymName: dymName, + }) + if err != nil { + return fmt.Errorf("failed to fetch information of '%s': %w", dymName, err) + } + + if res == nil || res.DymName == nil { + return fmt.Errorf("Dym-Name is not registered or expired: %s", dymName) + } + + return clientCtx.PrintProto(res.DymName) + }, + } + + flags.AddQueryFlagsToCmd(cmd) + + return cmd +} diff --git a/x/dymns/client/cli/query_params.go b/x/dymns/client/cli/query_params.go new file mode 100644 index 000000000..3c9028ab6 --- /dev/null +++ b/x/dymns/client/cli/query_params.go @@ -0,0 +1,33 @@ +package cli + +import ( + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" + "github.com/spf13/cobra" +) + +// CmdQueryParams is the CLI command for querying the parameters of the module +func CmdQueryParams() *cobra.Command { + cmd := &cobra.Command{ + Use: "params", + Short: "shows the parameters of the module", + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx := client.GetClientContextFromCmd(cmd) + + queryClient := dymnstypes.NewQueryClient(clientCtx) + + res, err := queryClient.Params(cmd.Context(), &dymnstypes.QueryParamsRequest{}) + if err != nil { + return err + } + + return clientCtx.PrintProto(&res.Params) + }, + } + + flags.AddQueryFlagsToCmd(cmd) + + return cmd +} diff --git a/x/dymns/client/cli/query_resolve.go b/x/dymns/client/cli/query_resolve.go new file mode 100644 index 000000000..1c63c88cf --- /dev/null +++ b/x/dymns/client/cli/query_resolve.go @@ -0,0 +1,60 @@ +package cli + +import ( + "fmt" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/version" + "github.com/spf13/cobra" + + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" +) + +// CmdQueryResolveDymNameAddress is the CLI command for resolving one or multiple Dym-Name-Addresss +func CmdQueryResolveDymNameAddress() *cobra.Command { + cmd := &cobra.Command{ + Use: "resolve-dym-name-address [Dym-Name-Address...]", + Aliases: []string{"resolve", "r"}, + Short: "Resolve Dym-Name-Address", + Example: fmt.Sprintf( + "%s q %s resolve myname@dym", + version.AppName, dymnstypes.ModuleName, + ), + Args: cobra.MinimumNArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx := client.GetClientContextFromCmd(cmd) + queryClient := dymnstypes.NewQueryClient(clientCtx) + + res, err := queryClient.ResolveDymNameAddresses(cmd.Context(), &dymnstypes.ResolveDymNameAddressesRequest{ + Addresses: args, + }) + if err != nil { + return fmt.Errorf("failed to resolve: %w", err) + } + + if res == nil || len(res.ResolvedAddresses) == 0 { + return fmt.Errorf("no result found for the provided Dym-Name addresses. They might not be configured or might have expired") + } + + for i, resolvedAddress := range res.ResolvedAddresses { + if i > 0 { + fmt.Println() + } + + fmt.Printf("%s\n", resolvedAddress.Address) + if resolvedAddress.Error != "" { + fmt.Printf(" => Error: %s\n", resolvedAddress.Error) + } else { + fmt.Printf(" => %s\n", resolvedAddress.ResolvedAddress) + } + } + + return nil + }, + } + + flags.AddQueryFlagsToCmd(cmd) + + return cmd +} diff --git a/x/dymns/client/cli/query_reverse_resolve.go b/x/dymns/client/cli/query_reverse_resolve.go new file mode 100644 index 000000000..c05728ed5 --- /dev/null +++ b/x/dymns/client/cli/query_reverse_resolve.go @@ -0,0 +1,73 @@ +package cli + +import ( + "fmt" + "strings" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/version" + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" + "github.com/spf13/cobra" +) + +const ( + flagWorkingChainId = "working-chain-id" +) + +// CmdQueryReverseResolveDymNameAddress is the CLI command for reverse-resolving Dym-Name-Address from an address. +// Reverse resolving means: given an address, find the Dym-Name-Address that resolves to it. +func CmdQueryReverseResolveDymNameAddress() *cobra.Command { + cmd := &cobra.Command{ + Use: "reverse-resolve-dym-name-address [Bech32 Address/0x Address]", + Aliases: []string{"reverse-resolve", "rr"}, + Short: "Reverse-resolve Dym-Name-Address from bech32 or 0x address", + Example: fmt.Sprintf( + "%s q %s reverse-resolve dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + version.AppName, dymnstypes.ModuleName, + ), + Args: cobra.MinimumNArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + workingChainId, err := cmd.Flags().GetString(flagWorkingChainId) + if err != nil { + return err + } + + clientCtx := client.GetClientContextFromCmd(cmd) + queryClient := dymnstypes.NewQueryClient(clientCtx) + + res, err := queryClient.ReverseResolveAddress(cmd.Context(), &dymnstypes.ReverseResolveAddressRequest{ + Addresses: args, + WorkingChainId: workingChainId, + }) + if err != nil { + return fmt.Errorf("failed to resolve: %w", err) + } + + if res == nil || len(res.Result) == 0 { + return fmt.Errorf("no result found for the provided Dym-Name addresses. They might not be configured or might have expired") + } + + fmt.Println("Working chain-id:", res.WorkingChainId) + + for query, result := range res.Result { + fmt.Printf("%s\n", query) + if result.Error != "" { + fmt.Printf(" => Error: %s\n", result.Error) + } else if len(result.Candidates) == 0 { + fmt.Printf(" => (no result)\n") + } else { + fmt.Printf(" => %s\n", strings.Join(result.Candidates, ", ")) + } + } + + return nil + }, + } + + flags.AddQueryFlagsToCmd(cmd) + + cmd.Flags().String(flagWorkingChainId, "", "Working Chain ID") + + return cmd +} diff --git a/x/dymns/client/cli/query_sell_order.go b/x/dymns/client/cli/query_sell_order.go new file mode 100644 index 000000000..1cc3dba61 --- /dev/null +++ b/x/dymns/client/cli/query_sell_order.go @@ -0,0 +1,78 @@ +package cli + +import ( + "fmt" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/version" + dymnsutils "github.com/dymensionxyz/dymension/v3/x/dymns/utils" + "github.com/spf13/cobra" + + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" +) + +// CmdQuerySellOrder is the CLI command for querying the current active Sell Order of a Dym-Name +func CmdQuerySellOrder() *cobra.Command { + targetSellOrderAssetTypeDymName := dymnstypes.TypeName.PrettyName() + targetSellOrderAssetTypeAlias := dymnstypes.TypeAlias.PrettyName() + + cmd := &cobra.Command{ + Use: "sell-order [Dym-Name/Alias]", + Aliases: []string{"so", "sellorder"}, + Short: "Get current active Sell Order of a Dym-Name/Alias.", + Example: fmt.Sprintf( + `%s q %s sell-order my-name --%s=%s +%s q %s sell-order dym --%s=%s`, + version.AppName, dymnstypes.ModuleName, flagTargetType, targetSellOrderAssetTypeDymName, + version.AppName, dymnstypes.ModuleName, flagTargetType, targetSellOrderAssetTypeAlias, + ), + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + input := args[0] + + targetType, err := cmd.Flags().GetString(flagTargetType) + if err != nil { + return err + } + if targetType == "" { + return fmt.Errorf("flag --%s is required", flagTargetType) + } + + clientCtx := client.GetClientContextFromCmd(cmd) + queryClient := dymnstypes.NewQueryClient(clientCtx) + switch targetType { + case targetSellOrderAssetTypeDymName: + if !dymnsutils.IsValidDymName(input) { + return fmt.Errorf("input is not a valid Dym-Name: %s", input) + } + case targetSellOrderAssetTypeAlias: + if !dymnsutils.IsValidAlias(input) { + return fmt.Errorf("input is not a valid Alias: %s", input) + } + default: + return fmt.Errorf("invalid target type: %s", targetType) + } + + res, err := queryClient.SellOrder(cmd.Context(), &dymnstypes.QuerySellOrderRequest{ + AssetId: input, + AssetType: targetType, + }) + if err != nil { + return fmt.Errorf("failed to fetch Sell Order of '%s': %w", input, err) + } + + if res == nil { + return fmt.Errorf("no active Sell Order of '%s'", input) + } + + return clientCtx.PrintProto(&res.Result) + }, + } + + flags.AddQueryFlagsToCmd(cmd) + + cmd.Flags().String(flagTargetType, targetSellOrderAssetTypeDymName, fmt.Sprintf("Target type to query for, one of: %s/%s", targetSellOrderAssetTypeDymName, targetSellOrderAssetTypeAlias)) + + return cmd +} diff --git a/x/dymns/client/cli/test_proposals/mcid_invalid_update_chain_id_proposal_test.json b/x/dymns/client/cli/test_proposals/mcid_invalid_update_chain_id_proposal_test.json new file mode 100644 index 000000000..324919d73 --- /dev/null +++ b/x/dymns/client/cli/test_proposals/mcid_invalid_update_chain_id_proposal_test.json @@ -0,0 +1,6 @@ +{ + "replacement": [{ + "previous_chain_id": 1, + "new_chain_id": "cosmoshub-4" + }] +} \ No newline at end of file diff --git a/x/dymns/client/cli/test_proposals/mcid_update_multiple_chain_ids_proposal_test.json b/x/dymns/client/cli/test_proposals/mcid_update_multiple_chain_ids_proposal_test.json new file mode 100644 index 000000000..7f22730fd --- /dev/null +++ b/x/dymns/client/cli/test_proposals/mcid_update_multiple_chain_ids_proposal_test.json @@ -0,0 +1,9 @@ +{ + "replacement": [{ + "previous_chain_id": "cosmoshub-3", + "new_chain_id": "cosmoshub-4" + },{ + "previous_chain_id": "columbus-4", + "new_chain_id": "columbus-5" + }] +} \ No newline at end of file diff --git a/x/dymns/client/cli/test_proposals/mcid_update_single_chain_id_proposal_test.json b/x/dymns/client/cli/test_proposals/mcid_update_single_chain_id_proposal_test.json new file mode 100644 index 000000000..32ba8b932 --- /dev/null +++ b/x/dymns/client/cli/test_proposals/mcid_update_single_chain_id_proposal_test.json @@ -0,0 +1,6 @@ +{ + "replacement": [{ + "previous_chain_id": "cosmoshub-3", + "new_chain_id": "cosmoshub-4" + }] +} \ No newline at end of file diff --git a/x/dymns/client/cli/test_proposals/sample_update_params.json b/x/dymns/client/cli/test_proposals/sample_update_params.json new file mode 100644 index 000000000..4daa790da --- /dev/null +++ b/x/dymns/client/cli/test_proposals/sample_update_params.json @@ -0,0 +1,19 @@ +{ + "messages": [ + { + "@type": "/dymensionxyz.dymension.dymns.MsgUpdateParams", + "authority": "dym10d07y265gmmuvt4z0w9aw880jnsr700jgllrna", + "new_price_params":{ + "name_price_steps": ["1000000000000000000000","100000000000000000000","10000000000000000000","1000000000000000000"], + "alias_price_steps": ["1000000000000000000000","100000000000000000000","10000000000000000000","1000000000000000000"], + "price_extends": "1000000000000000000", + "price_denom": "adym", + "min_offer_price": "1000000000000000000" + } + } + ], + "metadata": "", + "deposit": "1000000000000000000adym", + "title": "Proposal Title", + "summary": "Use this command to submit: dymd tx gov submit-proposal sample_update_params.json --from hub-user --gas auto --gas-prices 20000000000adym --yes" +} \ No newline at end of file diff --git a/x/dymns/client/cli/test_proposals/uac_invalid_update_aliases_proposal_test.json b/x/dymns/client/cli/test_proposals/uac_invalid_update_aliases_proposal_test.json new file mode 100644 index 000000000..922658338 --- /dev/null +++ b/x/dymns/client/cli/test_proposals/uac_invalid_update_aliases_proposal_test.json @@ -0,0 +1,6 @@ +{ + "add": [{ + "chain_id": 1, + "alias": "dym" + }] +} \ No newline at end of file diff --git a/x/dymns/client/cli/test_proposals/uac_update_aliases_multiple_add_remove_proposal_test.json b/x/dymns/client/cli/test_proposals/uac_update_aliases_multiple_add_remove_proposal_test.json new file mode 100644 index 000000000..e4d7409df --- /dev/null +++ b/x/dymns/client/cli/test_proposals/uac_update_aliases_multiple_add_remove_proposal_test.json @@ -0,0 +1,16 @@ +{ + "add": [{ + "chain_id": "dymension_1100-1", + "alias": "dym" + },{ + "chain_id": "blumbus_111-1", + "alias": "bb" + }], + "remove": [{ + "chain_id": "dymension_1100-1", + "alias": "dymension" + },{ + "chain_id": "froopyland_111-1", + "alias": "fl" + }] +} \ No newline at end of file diff --git a/x/dymns/client/cli/test_proposals/uac_update_aliases_single_add_proposal_test.json b/x/dymns/client/cli/test_proposals/uac_update_aliases_single_add_proposal_test.json new file mode 100644 index 000000000..f493ddefa --- /dev/null +++ b/x/dymns/client/cli/test_proposals/uac_update_aliases_single_add_proposal_test.json @@ -0,0 +1,6 @@ +{ + "add": [{ + "chain_id": "dymension_1100-1", + "alias": "dym" + }] +} \ No newline at end of file diff --git a/x/dymns/client/cli/tx.go b/x/dymns/client/cli/tx.go new file mode 100644 index 000000000..39a764529 --- /dev/null +++ b/x/dymns/client/cli/tx.go @@ -0,0 +1,37 @@ +package cli + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/cosmos/cosmos-sdk/client" + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" +) + +// GetTxCmd returns the transaction commands for this module +func GetTxCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: dymnstypes.ModuleName, + Short: fmt.Sprintf("%s transactions subcommands", dymnstypes.ModuleName), + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + + cmd.AddCommand( + NewRegisterDymNameTxCmd(), + NewRegisterAliasTxCmd(), + NewUpdateResolveDymNameAddressTxCmd(), + NewUpdateDetailsTxCmd(), + NewPlaceDymNameSellOrderTxCmd(), + NewPlaceAliasSellOrderTxCmd(), + NewPlaceBidOnDymNameOrderTxCmd(), + NewPlaceBidOnAliasOrderTxCmd(), + NewOfferBuyDymNameTxCmd(), + NewOfferBuyAliasTxCmd(), + NewAcceptBuyOrderTxCmd(), + ) + + return cmd +} diff --git a/x/dymns/client/cli/tx_accept_offer.go b/x/dymns/client/cli/tx_accept_offer.go new file mode 100644 index 000000000..d9ef80d9c --- /dev/null +++ b/x/dymns/client/cli/tx_accept_offer.go @@ -0,0 +1,80 @@ +package cli + +import ( + "fmt" + "strconv" + "strings" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/client/tx" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/version" + "github.com/dymensionxyz/dymension/v3/app/params" + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" + "github.com/spf13/cobra" +) + +// NewAcceptBuyOrderTxCmd is the CLI command for accepting a Buy-Order of a Dym-Name/Alias/Handle. +// or offer-back to raise the price. +func NewAcceptBuyOrderTxCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: fmt.Sprintf("accept-offer [offer-id] [amount] %s", params.DisplayDenom), + Short: "Accept a Buy-Order for your Dym-Name/Alias/Handle", + Example: fmt.Sprintf( + "$ %s tx %s accept-offer 1 50 %s --%s owner", + version.AppName, dymnstypes.ModuleName, + params.DisplayDenom, + flags.FlagFrom, + ), + Args: cobra.ExactArgs(3), + RunE: func(cmd *cobra.Command, args []string) (err error) { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + + orderId := args[0] + if !dymnstypes.IsValidBuyOrderId(orderId) { + return fmt.Errorf("input is not a valid Buy-Order ID: %s", orderId) + } + + amount, err := strconv.ParseInt(args[1], 10, 64) + if err != nil || amount < 1 { + return fmt.Errorf("amount must be a positive number") + } + + denom := args[2] + if !strings.EqualFold(denom, params.DisplayDenom) { + return fmt.Errorf("denom must be %s", strings.ToUpper(params.DisplayDenom)) + } + + owner := clientCtx.GetFromAddress().String() + if owner == "" { + return fmt.Errorf("flag --%s is required", flags.FlagFrom) + } + + queryClient := dymnstypes.NewQueryClient(clientCtx) + + resParams, err := queryClient.Params(cmd.Context(), &dymnstypes.QueryParamsRequest{}) + if err != nil { + return err + } + + msg := &dymnstypes.MsgAcceptBuyOrder{ + OrderId: orderId, + Owner: owner, + MinAccept: sdk.Coin{ + Denom: resParams.Params.Price.PriceDenom, + Amount: sdk.NewInt(amount).MulRaw(adymToDymMultiplier), + }, + } + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + }, + } + + flags.AddTxFlagsToCmd(cmd) + + return cmd +} diff --git a/x/dymns/client/cli/tx_bid_alias.go b/x/dymns/client/cli/tx_bid_alias.go new file mode 100644 index 000000000..795cca5e9 --- /dev/null +++ b/x/dymns/client/cli/tx_bid_alias.go @@ -0,0 +1,74 @@ +package cli + +import ( + "fmt" + "strconv" + "strings" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/client/tx" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/version" + "github.com/dymensionxyz/dymension/v3/app/params" + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" + dymnsutils "github.com/dymensionxyz/dymension/v3/x/dymns/utils" + "github.com/spf13/cobra" +) + +// NewPlaceBidOnAliasOrderTxCmd is the CLI command for placing a bid on a Alias/Handle Sell-Order. +func NewPlaceBidOnAliasOrderTxCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: fmt.Sprintf("bid-alias [Alias] [amount] %s", params.DisplayDenom), + Aliases: []string{"bid-handle"}, + Short: "place a bid on a Alias/Handle Sell-Order", + Example: fmt.Sprintf( + "$ %s tx %s bid-alias dym 100 %s --%s sequencer", + version.AppName, dymnstypes.ModuleName, params.DisplayDenom, flags.FlagFrom, + ), + Args: cobra.ExactArgs(3), + RunE: func(cmd *cobra.Command, args []string) (err error) { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + + alias := args[0] + if !dymnsutils.IsValidAlias(alias) { + return fmt.Errorf("invalid alias: %s", alias) + } + amount, err := strconv.ParseUint(args[1], 10, 64) + if err != nil || amount < 1 { + return fmt.Errorf("amount must be a positive number") + } + if amount > maxDymBuyValueInteractingCLI { + return fmt.Errorf( + "excess maximum bid value, you should go to dApp. To prevent mistakenly in input, the maximum amount allowed via CLI is: %d %s", + maxDymBuyValueInteractingCLI, params.DisplayDenom, + ) + } + denom := args[2] + if !strings.EqualFold(denom, params.DisplayDenom) { + return fmt.Errorf("denom must be: %s", strings.ToUpper(params.DisplayDenom)) + } + + buyer := clientCtx.GetFromAddress().String() + if buyer == "" { + return fmt.Errorf("flag --%s is required", flags.FlagFrom) + } + + msg := &dymnstypes.MsgPurchaseOrder{ + AssetId: alias, + AssetType: dymnstypes.TypeAlias, + Offer: sdk.NewCoin(params.BaseDenom, sdk.NewInt(int64(amount)).MulRaw(adymToDymMultiplier)), + Buyer: buyer, + } + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + }, + } + + flags.AddTxFlagsToCmd(cmd) + + return cmd +} diff --git a/x/dymns/client/cli/tx_bid_name.go b/x/dymns/client/cli/tx_bid_name.go new file mode 100644 index 000000000..bc60c283a --- /dev/null +++ b/x/dymns/client/cli/tx_bid_name.go @@ -0,0 +1,74 @@ +package cli + +import ( + "fmt" + "strconv" + "strings" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/client/tx" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/version" + "github.com/dymensionxyz/dymension/v3/app/params" + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" + dymnsutils "github.com/dymensionxyz/dymension/v3/x/dymns/utils" + "github.com/spf13/cobra" +) + +// NewPlaceBidOnDymNameOrderTxCmd is the CLI command for placing a bid on a Dym-Name Sell-Order. +func NewPlaceBidOnDymNameOrderTxCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: fmt.Sprintf("bid-name [Dym-Name] [amount] %s", params.DisplayDenom), + Aliases: []string{"bid"}, + Short: "place a bid on a Dym-Name Sell-Order", + Example: fmt.Sprintf( + "$ %s tx %s bid-name myname 100 %s --%s hub-user", + version.AppName, dymnstypes.ModuleName, params.DisplayDenom, flags.FlagFrom, + ), + Args: cobra.ExactArgs(3), + RunE: func(cmd *cobra.Command, args []string) (err error) { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + + dymName := args[0] + if !dymnsutils.IsValidDymName(dymName) { + return fmt.Errorf("invalid Dym-Name: %s", dymName) + } + amount, err := strconv.ParseUint(args[1], 10, 64) + if err != nil || amount < 1 { + return fmt.Errorf("amount must be a positive number") + } + if amount > maxDymBuyValueInteractingCLI { + return fmt.Errorf( + "excess maximum bid value, you should go to dApp. To prevent mistakenly in input, the maximum amount allowed via CLI is: %d %s", + maxDymBuyValueInteractingCLI, params.DisplayDenom, + ) + } + denom := args[2] + if !strings.EqualFold(denom, params.DisplayDenom) { + return fmt.Errorf("denom must be: %s", strings.ToUpper(params.DisplayDenom)) + } + + buyer := clientCtx.GetFromAddress().String() + if buyer == "" { + return fmt.Errorf("flag --%s is required", flags.FlagFrom) + } + + msg := &dymnstypes.MsgPurchaseOrder{ + AssetId: dymName, + AssetType: dymnstypes.TypeName, + Offer: sdk.NewCoin(params.BaseDenom, sdk.NewInt(int64(amount)).MulRaw(adymToDymMultiplier)), + Buyer: buyer, + } + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + }, + } + + flags.AddTxFlagsToCmd(cmd) + + return cmd +} diff --git a/x/dymns/client/cli/tx_gov.go b/x/dymns/client/cli/tx_gov.go new file mode 100644 index 000000000..bbb21fb19 --- /dev/null +++ b/x/dymns/client/cli/tx_gov.go @@ -0,0 +1,217 @@ +package cli + +import ( + "fmt" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/tx" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/version" + "github.com/cosmos/cosmos-sdk/x/gov/client/cli" + govv1beta1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" + "github.com/dymensionxyz/dymension/v3/app/params" + "github.com/dymensionxyz/dymension/v3/utils" + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" + "github.com/spf13/cobra" +) + +// NewMigrateChainIdsCmd implements the command to submit a proposal that migrate chain-ids in module params +// as well as Dym-Names configurations (of non-expired) those contain chain-ids. +func NewMigrateChainIdsCmd() *cobra.Command { + cmdCode := "migrate-chain-id" + cmd := &cobra.Command{ + Use: fmt.Sprintf("%s PROPOSAL_FILE", cmdCode), + Args: cobra.ExactArgs(1), + Short: "Submit a proposal that migrate chain-ids in params and Dym-Names configurations.", + Long: "Submit a proposal that migrate chain-ids in params and Dym-Names configurations. The proposal details must be provided via a JSON file.", + Example: fmt.Sprintf(`$ %s tx gov submit-legacy-proposal %s proposal_file.json --from= + +Sample proposal file content: +// all fields are required +{ + "replacement": [{ + "previous_chain_id": "cosmoshub-3", + "new_chain_id": "cosmoshub-4" + },{ + "previous_chain_id": "columbus-4", + "new_chain_id": "columbus-5" + }] +}`, + version.AppName, + cmdCode, + ), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + + title, err := cmd.Flags().GetString(cli.FlagTitle) + if err != nil { + return err + } + + description, err := cmd.Flags().GetString(cli.FlagDescription) + if err != nil { + return err + } + + depositStr, err := cmd.Flags().GetString(cli.FlagDeposit) + if err != nil { + return err + } + + deposit, err := sdk.ParseCoinsNormalized(depositStr) + if err != nil { + return err + } + + proposal, err := parseMigrateChainIdsProposal(args[0]) + if err != nil { + return err + } + + from := clientCtx.GetFromAddress() + + content := dymnstypes.NewMigrateChainIdsProposal(title, description, proposal.Replacement...) + + msg, err := govv1beta1.NewMsgSubmitProposal(content, deposit, from) + if err != nil { + return err + } + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + }, + } + + cmd.Flags().String(cli.FlagTitle, "", "title of proposal") + if err := cmd.MarkFlagRequired(cli.FlagTitle); err != nil { + panic(err) + } + + cmd.Flags().String(cli.FlagDescription, "", "description of proposal") + if err := cmd.MarkFlagRequired(cli.FlagDescription); err != nil { + panic(err) + } + + cmd.Flags().String(cli.FlagDeposit, "1000"+params.BaseDenom, "deposit of proposal") + if err := cmd.MarkFlagRequired(cli.FlagDeposit); err != nil { + panic(err) + } + + return cmd +} + +// NewUpdateAliasesCmd implements the command to submit a proposal that update alias for chain-ids. +func NewUpdateAliasesCmd() *cobra.Command { + cmdCode := "update-aliases" + cmd := &cobra.Command{ + Use: fmt.Sprintf("%s PROPOSAL_FILE", cmdCode), + Args: cobra.ExactArgs(1), + Short: "Submit a proposal that update alias for chain-ids.", + Long: `Submit a proposal that update alias for chain-ids. The proposal details must be provided via a JSON file.`, + Example: fmt.Sprintf(`$ %s tx gov submit-legacy-proposal %s proposal_file.json --from= + +Sample proposal file content: +// all fields are required +{ + "add": [{ + "chain_id": "dymension_1100-1", + "alias": "dym" + },{ + "chain_id": "blumbus_111-1", + "alias": "blumbus" + }], + "remove": [{ + "chain_id": "dymension_1100-1", + "alias": "dymension" + }] +}`, + version.AppName, + cmdCode, + ), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + + title, err := cmd.Flags().GetString(cli.FlagTitle) + if err != nil { + return err + } + + description, err := cmd.Flags().GetString(cli.FlagDescription) + if err != nil { + return err + } + + depositStr, err := cmd.Flags().GetString(cli.FlagDeposit) + if err != nil { + return err + } + + deposit, err := sdk.ParseCoinsNormalized(depositStr) + if err != nil { + return err + } + + proposal, err := parseUpdateAliasesProposal(args[0]) + if err != nil { + return err + } + + from := clientCtx.GetFromAddress() + + content := dymnstypes.NewUpdateAliasesProposal(title, description, proposal.Add, proposal.Remove) + + msg, err := govv1beta1.NewMsgSubmitProposal(content, deposit, from) + if err != nil { + return err + } + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + }, + } + + cmd.Flags().String(cli.FlagTitle, "", "title of proposal") + if err := cmd.MarkFlagRequired(cli.FlagTitle); err != nil { + panic(err) + } + + cmd.Flags().String(cli.FlagDescription, "", "description of proposal") + if err := cmd.MarkFlagRequired(cli.FlagDescription); err != nil { + panic(err) + } + + cmd.Flags().String(cli.FlagDeposit, "1000"+params.BaseDenom, "deposit of proposal") + if err := cmd.MarkFlagRequired(cli.FlagDeposit); err != nil { + panic(err) + } + + return cmd +} + +// parseMigrateChainIdsProposal reads and parses proposal for NewMigrateChainIdsCmd from a JSON file. +func parseMigrateChainIdsProposal(metadataFile string) (*dymnstypes.MigrateChainIdsProposal, error) { + var proposal dymnstypes.MigrateChainIdsProposal + + err := utils.ParseJsonFromFile(metadataFile, &proposal) + if err != nil { + return nil, err + } + + return &proposal, nil +} + +// parseUpdateAliasesProposal reads and parses proposal for NewUpdateAliasesCmd from a JSON file. +func parseUpdateAliasesProposal(metadataFile string) (*dymnstypes.UpdateAliasesProposal, error) { + var proposal dymnstypes.UpdateAliasesProposal + + err := utils.ParseJsonFromFile(metadataFile, &proposal) + if err != nil { + return nil, err + } + + return &proposal, nil +} diff --git a/x/dymns/client/cli/tx_gov_test.go b/x/dymns/client/cli/tx_gov_test.go new file mode 100644 index 000000000..31bb1475b --- /dev/null +++ b/x/dymns/client/cli/tx_gov_test.go @@ -0,0 +1,146 @@ +package cli + +import ( + "testing" + + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" + "github.com/stretchr/testify/require" +) + +func Test_parseMigrateChainIdsProposal(t *testing.T) { + //goland:noinspection SpellCheckingInspection + testCases := []struct { + name string + metadataFile string + wantErr bool + want []dymnstypes.MigrateChainId + }{ + { + name: "fail - invalid file name", + metadataFile: "", + wantErr: true, + }, + { + name: "fail - invalid content", + metadataFile: "test_proposals/mcid_invalid_update_chain_id_proposal_test.json", + wantErr: true, + }, + { + name: "pass - update single", + metadataFile: "test_proposals/mcid_update_single_chain_id_proposal_test.json", + wantErr: false, + want: []dymnstypes.MigrateChainId{ + { + PreviousChainId: "cosmoshub-3", + NewChainId: "cosmoshub-4", + }, + }, + }, + { + name: "pass - update multiple", + metadataFile: "test_proposals/mcid_update_multiple_chain_ids_proposal_test.json", + wantErr: false, + want: []dymnstypes.MigrateChainId{ + { + PreviousChainId: "cosmoshub-3", + NewChainId: "cosmoshub-4", + }, + { + PreviousChainId: "columbus-4", + NewChainId: "columbus-5", + }, + }, + }, + } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + proposal, err := parseMigrateChainIdsProposal(tc.metadataFile) + if tc.wantErr { + require.Error(t, err) + return + } + + require.NoError(t, err) + require.Len(t, proposal.Replacement, len(tc.want)) + require.Equal(t, tc.want, proposal.Replacement) + }) + } +} + +func Test_parseUpdateAliasesProposal(t *testing.T) { + testCases := []struct { + name string + metadataFile string + wantErr bool + wantAdd []dymnstypes.UpdateAlias + wantRemove []dymnstypes.UpdateAlias + }{ + { + name: "fail - invalid file name", + metadataFile: "", + wantErr: true, + }, + { + name: "fail - invalid content", + metadataFile: "test_proposals/uac_invalid_update_aliases_proposal_test.json", + wantErr: true, + }, + { + name: "pass - update single", + metadataFile: "test_proposals/uac_update_aliases_single_add_proposal_test.json", + wantErr: false, + wantAdd: []dymnstypes.UpdateAlias{ + { + ChainId: "dymension_1100-1", + Alias: "dym", + }, + }, + }, + { + name: "pass - update multiple", + metadataFile: "test_proposals/uac_update_aliases_multiple_add_remove_proposal_test.json", + wantErr: false, + wantAdd: []dymnstypes.UpdateAlias{ + { + ChainId: "dymension_1100-1", + Alias: "dym", + }, + { + ChainId: "blumbus_111-1", + Alias: "bb", + }, + }, + wantRemove: []dymnstypes.UpdateAlias{ + { + ChainId: "dymension_1100-1", + Alias: "dymension", + }, + { + ChainId: "froopyland_111-1", + Alias: "fl", + }, + }, + }, + } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + proposal, err := parseUpdateAliasesProposal(tc.metadataFile) + if tc.wantErr { + require.Error(t, err) + return + } + + require.NoError(t, err) + + require.Len(t, proposal.Add, len(tc.wantAdd)) + if len(tc.wantAdd) > 0 { + require.Equal(t, tc.wantAdd, proposal.Add) + } + + require.Len(t, proposal.Remove, len(tc.wantRemove)) + if len(tc.wantRemove) > 0 { + require.Equal(t, tc.wantRemove, proposal.Remove) + } + }) + } +} diff --git a/x/dymns/client/cli/tx_offer_alias.go b/x/dymns/client/cli/tx_offer_alias.go new file mode 100644 index 000000000..4aafd11bf --- /dev/null +++ b/x/dymns/client/cli/tx_offer_alias.go @@ -0,0 +1,99 @@ +package cli + +import ( + "fmt" + "strconv" + "strings" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/client/tx" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/version" + "github.com/dymensionxyz/dymension/v3/app/params" + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" + dymnsutils "github.com/dymensionxyz/dymension/v3/x/dymns/utils" + "github.com/spf13/cobra" +) + +// NewOfferBuyAliasTxCmd is the CLI command for creating an offer to buy an Alias/Handle. +func NewOfferBuyAliasTxCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: fmt.Sprintf("offer-alias [Alias/Handle] [amount] %s", params.DisplayDenom), + Aliases: []string{"offer-handle"}, + Short: "Create an offer to buy an Alias/Handle", + Example: fmt.Sprintf( + "$ %s tx %s offer dym 50 %s --%s sequencer", + version.AppName, dymnstypes.ModuleName, + params.DisplayDenom, + flags.FlagFrom, + ), + Args: cobra.ExactArgs(3), + RunE: func(cmd *cobra.Command, args []string) (err error) { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + + alias := args[0] + if !dymnsutils.IsValidAlias(alias) { + return fmt.Errorf("input is not a valid alias: %s", alias) + } + + amount, err := strconv.ParseUint(args[1], 10, 64) + if err != nil || amount < 1 { + return fmt.Errorf("amount must be a positive number") + } + + if amount > maxDymBuyValueInteractingCLI { + return fmt.Errorf( + "excess maximum offer value, you should go to dApp. To prevent mistakenly in input, the maximum amount allowed via CLI is: %d %s", + maxDymBuyValueInteractingCLI, params.DisplayDenom, + ) + } + denom := args[2] + if !strings.EqualFold(denom, params.DisplayDenom) { + return fmt.Errorf("denom must be %s", strings.ToUpper(params.DisplayDenom)) + } + + buyer := clientCtx.GetFromAddress().String() + if buyer == "" { + return fmt.Errorf("flag --%s is required", flags.FlagFrom) + } + + continueOrderId, err := cmd.Flags().GetString(flagContinueOrderId) + if err != nil { + return err + } + if continueOrderId != "" && !dymnstypes.IsValidBuyOrderId(continueOrderId) { + return fmt.Errorf("invalid continue offer id") + } + + queryClient := dymnstypes.NewQueryClient(clientCtx) + + resParams, err := queryClient.Params(cmd.Context(), &dymnstypes.QueryParamsRequest{}) + if err != nil { + return err + } + + msg := &dymnstypes.MsgPlaceBuyOrder{ + AssetId: alias, + AssetType: dymnstypes.TypeAlias, + Buyer: buyer, + ContinueOrderId: continueOrderId, + Offer: sdk.Coin{ + Denom: resParams.Params.Price.PriceDenom, + Amount: sdk.NewInt(int64(amount)).MulRaw(adymToDymMultiplier), + }, + } + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + }, + } + + flags.AddTxFlagsToCmd(cmd) + + cmd.Flags().String(flagContinueOrderId, "", "if provided, will raise offer value of an existing offer") + + return cmd +} diff --git a/x/dymns/client/cli/tx_offer_name.go b/x/dymns/client/cli/tx_offer_name.go new file mode 100644 index 000000000..dc6998ff0 --- /dev/null +++ b/x/dymns/client/cli/tx_offer_name.go @@ -0,0 +1,103 @@ +package cli + +import ( + "fmt" + "strconv" + "strings" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/client/tx" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/version" + "github.com/dymensionxyz/dymension/v3/app/params" + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" + dymnsutils "github.com/dymensionxyz/dymension/v3/x/dymns/utils" + "github.com/spf13/cobra" +) + +const ( + flagContinueOrderId = "continue-order-id" +) + +// NewOfferBuyDymNameTxCmd is the CLI command for creating an offer to buy a Dym-Name. +func NewOfferBuyDymNameTxCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: fmt.Sprintf("offer-name [Dym-Name] [amount] %s", params.DisplayDenom), + Aliases: []string{"offer"}, + Short: "Create an offer to buy a Dym-Name", + Example: fmt.Sprintf( + "$ %s tx %s offer myname 50 %s --%s hub-user", + version.AppName, dymnstypes.ModuleName, + params.DisplayDenom, + flags.FlagFrom, + ), + Args: cobra.ExactArgs(3), + RunE: func(cmd *cobra.Command, args []string) (err error) { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + + dymName := args[0] + if !dymnsutils.IsValidDymName(dymName) { + return fmt.Errorf("input is not a valid Dym-Name: %s", dymName) + } + + amount, err := strconv.ParseUint(args[1], 10, 64) + if err != nil || amount < 1 { + return fmt.Errorf("amount must be a positive number") + } + + if amount > maxDymBuyValueInteractingCLI { + return fmt.Errorf( + "excess maximum offer value, you should go to dApp. To prevent mistakenly in input, the maximum amount allowed via CLI is: %d %s", + maxDymBuyValueInteractingCLI, params.DisplayDenom, + ) + } + denom := args[2] + if !strings.EqualFold(denom, params.DisplayDenom) { + return fmt.Errorf("denom must be %s", strings.ToUpper(params.DisplayDenom)) + } + + buyer := clientCtx.GetFromAddress().String() + if buyer == "" { + return fmt.Errorf("flag --%s is required", flags.FlagFrom) + } + + continueOrderId, err := cmd.Flags().GetString(flagContinueOrderId) + if err != nil { + return err + } + if continueOrderId != "" && !dymnstypes.IsValidBuyOrderId(continueOrderId) { + return fmt.Errorf("invalid continue buy-order id: %s", continueOrderId) + } + + queryClient := dymnstypes.NewQueryClient(clientCtx) + + resParams, err := queryClient.Params(cmd.Context(), &dymnstypes.QueryParamsRequest{}) + if err != nil { + return err + } + + msg := &dymnstypes.MsgPlaceBuyOrder{ + AssetId: dymName, + AssetType: dymnstypes.TypeName, + Buyer: buyer, + ContinueOrderId: continueOrderId, + Offer: sdk.Coin{ + Denom: resParams.Params.Price.PriceDenom, + Amount: sdk.NewInt(int64(amount)).MulRaw(adymToDymMultiplier), + }, + } + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + }, + } + + flags.AddTxFlagsToCmd(cmd) + + cmd.Flags().String(flagContinueOrderId, "", "if provided, will raise offer value of an existing offer") + + return cmd +} diff --git a/x/dymns/client/cli/tx_register_alias.go b/x/dymns/client/cli/tx_register_alias.go new file mode 100644 index 000000000..b248bf988 --- /dev/null +++ b/x/dymns/client/cli/tx_register_alias.go @@ -0,0 +1,98 @@ +package cli + +import ( + "fmt" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/client/tx" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/version" + "github.com/dymensionxyz/dymension/v3/app/params" + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" + dymnsutils "github.com/dymensionxyz/dymension/v3/x/dymns/utils" + "github.com/spf13/cobra" +) + +// NewRegisterAliasTxCmd is the CLI command for registering a new Alias for an owned RollApp. +func NewRegisterAliasTxCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "register-alias [Alias/Handle] [RollApp ID]", + Aliases: []string{"register-handle"}, + Short: "Register a new Alias/Handle for the owned Roll App", + Example: fmt.Sprintf( + "$ %s tx %s register-alias rolx rollappx_1-1 --confirm-payment 15000000000000000000%s --%s sequencer", + version.AppName, dymnstypes.ModuleName, + params.BaseDenom, + flags.FlagFrom, + ), + Args: cobra.ExactArgs(2), + RunE: func(cmd *cobra.Command, args []string) (err error) { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + + alias := args[0] + if !dymnsutils.IsValidAlias(alias) { + return fmt.Errorf("input is not a valid alias: %s", alias) + } + + rollAppId := args[1] + if !dymnsutils.IsValidChainIdFormat(rollAppId) { + return fmt.Errorf("input is not a valid RollApp ID: %s", rollAppId) + } + + rollAppOwnerAsBuyer := clientCtx.GetFromAddress().String() + + if rollAppOwnerAsBuyer == "" { + return fmt.Errorf("flag --%s is required", flags.FlagFrom) + } + + confirmPaymentStr, err := cmd.Flags().GetString(flagConfirmPayment) + if err != nil { + return err + } + if confirmPaymentStr == "" { + // mode query to get the estimated payment amount + queryClient := dymnstypes.NewQueryClient(clientCtx) + + resEst, err := queryClient.EstimateRegisterAlias(cmd.Context(), &dymnstypes.EstimateRegisterAliasRequest{ + Alias: alias, + RollappId: rollAppId, + Owner: rollAppOwnerAsBuyer, + }) + if err != nil { + return fmt.Errorf("failed to estimate registration fee for '%s': %w", alias, err) + } + + fmt.Println("Estimated registration fee: ", resEst.Price) + if estAmt, ok := toEstimatedCoinAmount(resEst.Price); ok { + fmt.Printf(" (~ %s)\n", estAmt) + } + + fmt.Printf("Supplying flag '--%s=%s' to submit the registration\n", flagConfirmPayment, resEst.Price.String()) + + return nil + } + + confirmPayment, err := sdk.ParseCoinNormalized(confirmPaymentStr) + if err != nil { + return fmt.Errorf("invalid confirm payment: %w", err) + } + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), &dymnstypes.MsgRegisterAlias{ + Alias: alias, + RollappId: rollAppId, + Owner: rollAppOwnerAsBuyer, + ConfirmPayment: confirmPayment, + }) + }, + } + + flags.AddTxFlagsToCmd(cmd) + + cmd.Flags().String(flagConfirmPayment, "", "confirm payment for the Alias/Handle registration, without this flag, the command will query the estimated payment amount") + + return cmd +} diff --git a/x/dymns/client/cli/tx_register_name.go b/x/dymns/client/cli/tx_register_name.go new file mode 100644 index 000000000..298750154 --- /dev/null +++ b/x/dymns/client/cli/tx_register_name.go @@ -0,0 +1,148 @@ +package cli + +import ( + "fmt" + "strings" + + sdkmath "cosmossdk.io/math" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/client/tx" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/version" + "github.com/dymensionxyz/dymension/v3/app/params" + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" + dymnsutils "github.com/dymensionxyz/dymension/v3/x/dymns/utils" + "github.com/spf13/cobra" +) + +const ( + flagYears = "years" + flagConfirmPayment = "confirm-payment" + flagContact = "contact" +) + +// NewRegisterDymNameTxCmd is the CLI command for registering a new Dym-Name or extending the duration of an owned Dym-Name. +func NewRegisterDymNameTxCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "register-name [Dym-Name]", + Aliases: []string{"register"}, + Short: "Register a new Dym-Name or Extends the duration of an owned Dym-Name.", + Example: fmt.Sprintf( + "$ %s tx %s register myname --years 3 --confirm-payment 15000000000000000000%s --%s hub-user", + version.AppName, dymnstypes.ModuleName, + params.BaseDenom, + flags.FlagFrom, + ), + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) (err error) { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + + dymName := args[0] + if !dymnsutils.IsValidDymName(dymName) { + return fmt.Errorf("input is not a valid Dym-Name: %s", dymName) + } + + years, err := cmd.Flags().GetInt64(flagYears) + if err != nil { + return err + } + if years < 1 { + return fmt.Errorf("years must be greater than 0, specify by flag --%s", flagYears) + } + + buyer := clientCtx.GetFromAddress().String() + + if buyer == "" { + return fmt.Errorf("flag --%s is required", flags.FlagFrom) + } + + confirmPaymentStr, err := cmd.Flags().GetString(flagConfirmPayment) + if err != nil { + return err + } + if confirmPaymentStr == "" { + // mode query to get the estimated payment amount + queryClient := dymnstypes.NewQueryClient(clientCtx) + + resEst, err := queryClient.EstimateRegisterName(cmd.Context(), &dymnstypes.EstimateRegisterNameRequest{ + Name: dymName, + Duration: years, + Owner: buyer, + }) + if err != nil { + return fmt.Errorf("failed to estimate registration/renew fee for '%s': %w", dymName, err) + } + + fmt.Println("Estimated payment amount:") + if resEst.FirstYearPrice.IsNil() || resEst.FirstYearPrice.IsZero() { + fmt.Println("- Registration fee: None") + } else { + fmt.Println("- Registration fee + first year fee: ", resEst.FirstYearPrice) + if estAmt, ok := toEstimatedCoinAmount(resEst.FirstYearPrice); ok { + fmt.Printf(" (~ %s)\n", estAmt) + } + } + fmt.Print("- Extends duration fee: ") + if resEst.ExtendPrice.IsNil() || resEst.ExtendPrice.IsZero() { + fmt.Println("None") + } else { + fmt.Println(resEst.ExtendPrice) + if estAmt, ok := toEstimatedCoinAmount(resEst.ExtendPrice); ok { + fmt.Printf(" (~ %s)\n", estAmt) + } + } + fmt.Println("- Total fee: ", resEst.TotalPrice) + if estAmt, ok := toEstimatedCoinAmount(resEst.TotalPrice); ok { + fmt.Printf(" (~ %s)\n", estAmt) + } + + fmt.Printf("Supplying flag '--%s=%s' to submit the registration\n", flagConfirmPayment, resEst.TotalPrice.String()) + + return nil + } + + confirmPayment, err := sdk.ParseCoinNormalized(confirmPaymentStr) + if err != nil { + return fmt.Errorf("invalid confirm payment: %w", err) + } + + contact, err := cmd.Flags().GetString(flagContact) + if err != nil { + return err + } + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), &dymnstypes.MsgRegisterName{ + Name: dymName, + Duration: years, + Owner: buyer, + ConfirmPayment: confirmPayment, + Contact: contact, + }) + }, + } + + flags.AddTxFlagsToCmd(cmd) + + cmd.Flags().Int64(flagYears, 0, "number of years to register the Dym-Name for") + cmd.Flags().String(flagConfirmPayment, "", "confirm payment for the Dym-Name registration, without this flag, the command will query the estimated payment amount") + cmd.Flags().String(flagContact, "", "contact information for the Dym-Name") + + return cmd +} + +func toEstimatedAmount(amount sdkmath.Int) string { + return fmt.Sprintf("%s %s", amount.QuoRaw(adymToDymMultiplier), strings.ToUpper(params.DisplayDenom)) +} + +func toEstimatedCoinAmount(amount sdk.Coin) (estimatedAmount string, success bool) { + if amount.Denom == params.BaseDenom { + return toEstimatedAmount(amount.Amount), true + } else { + return amount.String(), false + } +} diff --git a/x/dymns/client/cli/tx_sell_alias.go b/x/dymns/client/cli/tx_sell_alias.go new file mode 100644 index 000000000..f9ef0e6b7 --- /dev/null +++ b/x/dymns/client/cli/tx_sell_alias.go @@ -0,0 +1,101 @@ +package cli + +import ( + "fmt" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/client/tx" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/version" + "github.com/dymensionxyz/dymension/v3/app/params" + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" + dymnsutils "github.com/dymensionxyz/dymension/v3/x/dymns/utils" + "github.com/spf13/cobra" +) + +// NewPlaceAliasSellOrderTxCmd is the CLI command for creating a Sell-Order to sell an Alias/Handle. +func NewPlaceAliasSellOrderTxCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "sell-alias [Alias/Handle]", + Aliases: []string{"sell-handle"}, + Short: "Create a sell-order to sell Alias/Handle of a RollApp you owned", + Long: fmt.Sprintf(`Create a sell-order to sell Alias/Handle of a RollApp you owned. Flag --%s indicate the starting price of the Alias/Handle, and flag --%s indicate the immediately sell price of the Alias/Handle. If immediately sell price is not supplied or the highest bid does not reaching this amount, auction can only be ended when the sell-order expired.`, flagMinPrice, flagImmediatelySellPrice), + Example: fmt.Sprintf( + "$ %s tx %s sell-alias dym --%s 50 [--%s 100] --%s sequencer", + version.AppName, dymnstypes.ModuleName, + flagMinPrice, flagImmediatelySellPrice, + flags.FlagFrom, + ), + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) (err error) { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + + alias := args[0] + if !dymnsutils.IsValidAlias(alias) { + return fmt.Errorf("input is not a valid Dym-Name: %s", alias) + } + + minPriceDym, err := cmd.Flags().GetUint64(flagMinPrice) + if err != nil { + return fmt.Errorf("error reading flag --%s: %w", flagMinPrice, err) + } + sellPriceDym, err := cmd.Flags().GetUint64(flagImmediatelySellPrice) + if err != nil { + return fmt.Errorf("error reading flag --%s: %w", flagImmediatelySellPrice, err) + } + + if minPriceDym < 1 { + return fmt.Errorf("--%s must be a positive number", flagMinPrice) + } + + if sellPriceDym > 0 && sellPriceDym < minPriceDym { + return fmt.Errorf("--%s must be greater than or equal to --%s", flagImmediatelySellPrice, flagMinPrice) + } + + if minPriceDym > maxDymSellValueInteractingCLI || sellPriceDym > maxDymSellValueInteractingCLI { + return fmt.Errorf("price is too high, over %d %s", maxDymSellValueInteractingCLI, params.DisplayDenom) + } + + seller := clientCtx.GetFromAddress().String() + if seller == "" { + return fmt.Errorf("flag --%s is required", flags.FlagFrom) + } + + queryClient := dymnstypes.NewQueryClient(clientCtx) + + resParams, err := queryClient.Params(cmd.Context(), &dymnstypes.QueryParamsRequest{}) + if err != nil { + return err + } + + var sellPrice *sdk.Coin + if sellPriceDym > 0 { + sellPrice = &sdk.Coin{ + Denom: resParams.Params.Price.PriceDenom, + Amount: sdk.NewInt(int64(sellPriceDym)).MulRaw(adymToDymMultiplier), + } + } + + msg := &dymnstypes.MsgPlaceSellOrder{ + AssetId: alias, + AssetType: dymnstypes.TypeAlias, + MinPrice: sdk.NewCoin(resParams.Params.Price.PriceDenom, sdk.NewInt(int64(minPriceDym)).MulRaw(adymToDymMultiplier)), + SellPrice: sellPrice, + Owner: seller, + } + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + }, + } + + flags.AddTxFlagsToCmd(cmd) + + cmd.Flags().Uint64(flagMinPrice, 0, "minimum price to sell the Alias/Handle") + cmd.Flags().Uint64(flagImmediatelySellPrice, 0, "immediately sell price of the Alias/Handle, when someone placed a bid on it that matching the immediately sell price, auction stopped and the Alias/Handle will be sold immediately, otherwise the Alias/Handle will be sold to the highest bidder when the sell-order expired") + + return cmd +} diff --git a/x/dymns/client/cli/tx_sell_name.go b/x/dymns/client/cli/tx_sell_name.go new file mode 100644 index 000000000..d36c108d7 --- /dev/null +++ b/x/dymns/client/cli/tx_sell_name.go @@ -0,0 +1,106 @@ +package cli + +import ( + "fmt" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/client/tx" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/version" + "github.com/dymensionxyz/dymension/v3/app/params" + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" + dymnsutils "github.com/dymensionxyz/dymension/v3/x/dymns/utils" + "github.com/spf13/cobra" +) + +const ( + flagMinPrice = "min-price" + flagImmediatelySellPrice = "immediately-sell-price" +) + +// NewPlaceDymNameSellOrderTxCmd is the CLI command for creating a Sell-Order to sell a Dym-Name. +func NewPlaceDymNameSellOrderTxCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "sell-name [Dym-Name]", + Aliases: []string{"sell"}, + Short: "Create a sell-order to sell your Dym-Name", + Long: fmt.Sprintf(`Create a sell-order to sell your Dym-Name. Flag --%s indicate the starting price of the Dym-Name, and flag --%s indicate the immediately sell price of the Dym-Name. If immediately sell price is not supplied or the highest bid does not reaching this amount, auction can only be ended when the sell-order expired.`, flagMinPrice, flagImmediatelySellPrice), + Example: fmt.Sprintf( + "$ %s tx %s sell-name myname --%s 50 [--%s 100] --%s hub-user", + version.AppName, dymnstypes.ModuleName, + flagMinPrice, flagImmediatelySellPrice, + flags.FlagFrom, + ), + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) (err error) { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + + dymName := args[0] + if !dymnsutils.IsValidDymName(dymName) { + return fmt.Errorf("input is not a valid Dym-Name: %s", dymName) + } + + minPriceDym, err := cmd.Flags().GetUint64(flagMinPrice) + if err != nil { + return fmt.Errorf("error reading flag --%s: %w", flagMinPrice, err) + } + sellPriceDym, err := cmd.Flags().GetUint64(flagImmediatelySellPrice) + if err != nil { + return fmt.Errorf("error reading flag --%s: %w", flagImmediatelySellPrice, err) + } + + if minPriceDym < 1 { + return fmt.Errorf("--%s must be a positive number", flagMinPrice) + } + + if sellPriceDym > 0 && sellPriceDym < minPriceDym { + return fmt.Errorf("--%s must be greater than or equal to --%s", flagImmediatelySellPrice, flagMinPrice) + } + + if minPriceDym > maxDymSellValueInteractingCLI || sellPriceDym > maxDymSellValueInteractingCLI { + return fmt.Errorf("price is too high, over %d %s", maxDymSellValueInteractingCLI, params.DisplayDenom) + } + + seller := clientCtx.GetFromAddress().String() + if seller == "" { + return fmt.Errorf("flag --%s is required", flags.FlagFrom) + } + + queryClient := dymnstypes.NewQueryClient(clientCtx) + + resParams, err := queryClient.Params(cmd.Context(), &dymnstypes.QueryParamsRequest{}) + if err != nil { + return err + } + + var sellPrice *sdk.Coin + if sellPriceDym > 0 { + sellPrice = &sdk.Coin{ + Denom: resParams.Params.Price.PriceDenom, + Amount: sdk.NewInt(int64(sellPriceDym)).MulRaw(adymToDymMultiplier), + } + } + + msg := &dymnstypes.MsgPlaceSellOrder{ + AssetId: dymName, + AssetType: dymnstypes.TypeName, + MinPrice: sdk.NewCoin(resParams.Params.Price.PriceDenom, sdk.NewInt(int64(minPriceDym)).MulRaw(adymToDymMultiplier)), + SellPrice: sellPrice, + Owner: seller, + } + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + }, + } + + flags.AddTxFlagsToCmd(cmd) + + cmd.Flags().Uint64(flagMinPrice, 0, "minimum price to sell the Dym-Name") + cmd.Flags().Uint64(flagImmediatelySellPrice, 0, "immediately sell price of the Dym-Name, when someone placed a bid on it that matching the immediately sell price, auction stopped and the Dym-Name will be sold immediately, otherwise the Dym-Name will be sold to the highest bidder when the sell-order expired") + + return cmd +} diff --git a/x/dymns/client/cli/tx_update_details.go b/x/dymns/client/cli/tx_update_details.go new file mode 100644 index 000000000..7e0def7fd --- /dev/null +++ b/x/dymns/client/cli/tx_update_details.go @@ -0,0 +1,68 @@ +package cli + +import ( + "fmt" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/client/tx" + "github.com/cosmos/cosmos-sdk/version" + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" + "github.com/spf13/cobra" +) + +const ( + flagClearConfigs = "clear-configs" +) + +// NewUpdateDetailsTxCmd is the CLI command for updating the details of a Dym-Name. +func NewUpdateDetailsTxCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: fmt.Sprintf("update-details [Dym-Name] --%s [--%s]", flagContact, flagClearConfigs), + Short: "Configure resolve Dym-Name address. 2nd arg if empty means to remove the configuration.", + Example: fmt.Sprintf( + "$ %s tx %s update-details myname --%s contact@example.com --%s hub-user [--%s]", + version.AppName, dymnstypes.ModuleName, flagContact, flags.FlagFrom, flagClearConfigs, + ), + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) (err error) { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + + dymName := args[0] + + controller := clientCtx.GetFromAddress().String() + + if controller == "" { + return fmt.Errorf("flag --%s is required", flags.FlagFrom) + } + + contact, err := cmd.Flags().GetString(flagContact) + if err != nil { + return err + } + clearConfigs, err := cmd.Flags().GetBool(flagClearConfigs) + if err != nil { + return err + } + + msg := &dymnstypes.MsgUpdateDetails{ + Name: dymName, + Controller: controller, + Contact: contact, + ClearConfigs: clearConfigs, + } + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + }, + } + + flags.AddTxFlagsToCmd(cmd) + + cmd.Flags().String(flagContact, dymnstypes.DoNotModifyDesc, "New contact details for the Dym-Name") + cmd.Flags().Bool(flagClearConfigs, false, "Clear all the current resolution configurations for the Dym-Name") + + return cmd +} diff --git a/x/dymns/client/cli/tx_update_resolve_address.go b/x/dymns/client/cli/tx_update_resolve_address.go new file mode 100644 index 000000000..e524a9342 --- /dev/null +++ b/x/dymns/client/cli/tx_update_resolve_address.go @@ -0,0 +1,85 @@ +package cli + +import ( + "fmt" + "time" + + "cosmossdk.io/errors" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/client/tx" + "github.com/cosmos/cosmos-sdk/version" + dymnskeeper "github.com/dymensionxyz/dymension/v3/x/dymns/keeper" + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" + dymnsutils "github.com/dymensionxyz/dymension/v3/x/dymns/utils" + "github.com/spf13/cobra" +) + +// NewUpdateResolveDymNameAddressTxCmd returns the CLI command for +// updating the address resolution configuration for a Dym-Name. +func NewUpdateResolveDymNameAddressTxCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "resolve [Dym-Name address] [?resolve to]", + Short: "Configure resolve Dym-Name address. 2nd arg if empty means to remove the configuration.", + Example: fmt.Sprintf( + "$ %s tx %s resolve bonded.staking@dym dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue --%s hub-user", + version.AppName, dymnstypes.ModuleName, flags.FlagFrom, + ), + Args: cobra.RangeArgs(1, 2), + RunE: func(cmd *cobra.Command, args []string) (err error) { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + + var dymNameAddress, resolveTo string + dymNameAddress = args[0] + if len(args) > 1 { + resolveTo = args[1] + } + + queryClient := dymnstypes.NewQueryClient(clientCtx) + + subName, dymName, chainIdOrAlias, err := dymnskeeper.ParseDymNameAddress(dymNameAddress) + if err != nil { + return errors.Wrap(err, "failed to parse input Dym-Name-Address") + } + + respTranslateChainId, err := queryClient.TranslateAliasOrChainIdToChainId(cmd.Context(), &dymnstypes.QueryTranslateAliasOrChainIdToChainIdRequest{ + AliasOrChainId: chainIdOrAlias, + }) + if err != nil || respTranslateChainId.ChainId == "" { + return errors.Wrapf(err, "failed to translate alias to chain-id: %s", chainIdOrAlias) + } + + chainId := respTranslateChainId.ChainId + fmt.Printf("Translated '%s' => '%s'\n", chainIdOrAlias, chainId) + time.Sleep(5 * time.Second) + + if !dymnsutils.IsValidChainIdFormat(chainId) { + return fmt.Errorf("input chain-id '%s' is not a valid chain-id", chainId) + } + + controller := clientCtx.GetFromAddress().String() + + if controller == "" { + return fmt.Errorf("flag --%s is required", flags.FlagFrom) + } + + msg := &dymnstypes.MsgUpdateResolveAddress{ + Name: dymName, + ChainId: chainId, + SubName: subName, + ResolveTo: resolveTo, + Controller: controller, + } + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + }, + } + + flags.AddTxFlagsToCmd(cmd) + + return cmd +} diff --git a/x/dymns/client/proposal_handler.go b/x/dymns/client/proposal_handler.go new file mode 100644 index 000000000..1c9e6bf99 --- /dev/null +++ b/x/dymns/client/proposal_handler.go @@ -0,0 +1,14 @@ +package client + +import ( + govclient "github.com/cosmos/cosmos-sdk/x/gov/client" + "github.com/dymensionxyz/dymension/v3/x/dymns/client/cli" +) + +var ( + // MigrateChainIdsProposalHandler is the proposal handler for migrating chain-ids + // in module params and configurations of non-expired Dym-Names. + MigrateChainIdsProposalHandler = govclient.NewProposalHandler(cli.NewMigrateChainIdsCmd) + // UpdateAliasesProposalHandler is the proposal handler for updating aliases of chain-ids. + UpdateAliasesProposalHandler = govclient.NewProposalHandler(cli.NewUpdateAliasesCmd) +) diff --git a/x/dymns/genesis.go b/x/dymns/genesis.go new file mode 100644 index 000000000..0e398f263 --- /dev/null +++ b/x/dymns/genesis.go @@ -0,0 +1,93 @@ +package dymns + +import ( + "time" + + sdk "github.com/cosmos/cosmos-sdk/types" + dymnskeeper "github.com/dymensionxyz/dymension/v3/x/dymns/keeper" + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" +) + +// InitGenesis initializes the module's state from a provided genesis state. +func InitGenesis(ctx sdk.Context, k dymnskeeper.Keeper, genState dymnstypes.GenesisState) { + mustNoError(k.SetParams(ctx, genState.Params)) + for _, dymName := range genState.DymNames { + mustNoError(k.SetDymName(ctx, dymName)) + mustNoError(k.AfterDymNameOwnerChanged(ctx, dymName.Name)) + mustNoError(k.AfterDymNameConfigChanged(ctx, dymName.Name)) + } + for _, bid := range genState.SellOrderBids { + mustNoError(k.GenesisRefundBid(ctx, bid)) + } + for _, offer := range genState.BuyOrders { + mustNoError(k.GenesisRefundBuyOrder(ctx, offer)) + } + for _, aliasesOfRollApp := range genState.AliasesOfRollapps { + for _, alias := range aliasesOfRollApp.Aliases { + mustNoError(k.SetAliasForRollAppId(ctx, aliasesOfRollApp.ChainId, alias)) + } + } +} + +// mustNoError is used when an action, which returns an error, must be run successfully without error. +// During genesis initialization, we must ensure that all actions are run successfully. +func mustNoError(err error) { + if err != nil { + panic(err) + } +} + +// ExportGenesis returns the module's exported genesis +func ExportGenesis(ctx sdk.Context, k dymnskeeper.Keeper) *dymnstypes.GenesisState { + // Note: during genesis export, the context does not contain chain-id and time. + if ctx.BlockTime().Unix() <= 0 { + // Since the implementation relies on context time, we need to set it to an actual value. + // The Export-Genesis action supposed to be called by a specific person, + // on local machine so using time.Now() is fine. + ctx = ctx.WithBlockTime(time.Now().UTC()) + } + + // Collect Dym-Names records so that we can add back later. + // We supposed to collect only Non-Expired Dym-Names to save the genesis & store size, + // but we also need to support those Dym-Names owners which their Dym-Name are expired but within grace period. + params := k.GetParams(ctx) + collectExpiredDymNamesExpiredFromEpoch := ctx.BlockTime().Add(-1 * params.Misc.GracePeriodDuration).Unix() + + dymNames := k.GetAllDymNames(ctx) + var nonExpiredDymNameAndWithinGracePeriod []dymnstypes.DymName + for _, dymName := range dymNames { + if dymName.ExpireAt < collectExpiredDymNamesExpiredFromEpoch { + continue + } + nonExpiredDymNameAndWithinGracePeriod = append(nonExpiredDymNameAndWithinGracePeriod, dymName) + } + + // Collect bidders of active Sell-Orders so that we can refund them later. + var nonRefundedBids []dymnstypes.SellOrderBid + for _, bid := range k.GetAllSellOrders(ctx) { + if bid.HighestBid == nil { + continue + } + // we ignore check expiry here because as long as Sell Orders exists, the highest bid not processed yet. + nonRefundedBids = append(nonRefundedBids, *bid.HighestBid) + } + + // Collect buyers of active Buy-Orders so that we can refund them later. + var nonRefundedBuyOrders []dymnstypes.BuyOrder + for _, offer := range k.GetAllBuyOrders(ctx) { + truncatedOffer := offer + truncatedOffer.CounterpartyOfferPrice = nil + nonRefundedBuyOrders = append(nonRefundedBuyOrders, truncatedOffer) + } + + // Collect aliases of RollApps so that we can add back later. + aliasesOfRollApps := k.GetAllRollAppsWithAliases(ctx) + + return &dymnstypes.GenesisState{ + Params: params, + DymNames: nonExpiredDymNameAndWithinGracePeriod, + SellOrderBids: nonRefundedBids, + BuyOrders: nonRefundedBuyOrders, + AliasesOfRollapps: aliasesOfRollApps, + } +} diff --git a/x/dymns/genesis_test.go b/x/dymns/genesis_test.go new file mode 100644 index 000000000..0d6ee1e8f --- /dev/null +++ b/x/dymns/genesis_test.go @@ -0,0 +1,468 @@ +package dymns_test + +import ( + "testing" + "time" + + rollapptypes "github.com/dymensionxyz/dymension/v3/x/rollapp/types" + + "github.com/dymensionxyz/sdk-utils/utils/uptr" + + "github.com/dymensionxyz/dymension/v3/testutil/sample" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/dymensionxyz/dymension/v3/app/params" + testkeeper "github.com/dymensionxyz/dymension/v3/testutil/keeper" + "github.com/dymensionxyz/dymension/v3/x/dymns" + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" + "github.com/stretchr/testify/require" +) + +//goland:noinspection SpellCheckingInspection +func TestExportThenInitGenesis(t *testing.T) { + now := time.Now().UTC() + + oldKeeper, _, oldRollAppKeeper, oldCtx := testkeeper.DymNSKeeper(t) + oldCtx = oldCtx.WithBlockTime(now) + moduleParams := oldKeeper.GetParams(oldCtx) + moduleParams.Misc.GracePeriodDuration = 30 * 24 * time.Hour + require.NoError(t, oldKeeper.SetParams(oldCtx, moduleParams)) + + type rollapp struct { + rollAppId string + owner string + alias string + } + + // Setup genesis state + owner1 := sample.AccAddress() + owner2 := sample.AccAddress() + anotherAccount := sample.AccAddress() + + bidder1 := sample.AccAddress() + bidder2 := sample.AccAddress() + bidder3 := sample.AccAddress() + + buyer1 := sample.AccAddress() + buyer2 := sample.AccAddress() + buyer3 := sample.AccAddress() + buyer4 := sample.AccAddress() + buyer5 := sample.AccAddress() + + rollApp1 := rollapp{ + rollAppId: "rollapp_1-1", + owner: sample.AccAddress(), + alias: "alias", + } + rollApp2 := rollapp{ + rollAppId: "rollapp_2-2", + owner: sample.AccAddress(), + alias: "cosmos", + } + rollApp3 := rollapp{ + rollAppId: "rollapp_3-3", + owner: sample.AccAddress(), + alias: "", + } + + dymName1 := dymnstypes.DymName{ + Name: "my-name", + Owner: owner1, + Controller: owner1, + ExpireAt: now.Add(time.Hour).Unix(), + Configs: []dymnstypes.DymNameConfig{ + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Path: "pseudo", + Value: anotherAccount, + }, + }, + } + require.NoError(t, oldKeeper.SetDymName(oldCtx, dymName1)) + + dymName2 := dymnstypes.DymName{ + Name: "light", + Owner: owner2, + Controller: owner2, + ExpireAt: now.Add(time.Hour).Unix(), + } + require.NoError(t, oldKeeper.SetDymName(oldCtx, dymName2)) + + dymName3JustExpired := dymnstypes.DymName{ + Name: "just-expired", + Owner: owner2, + Controller: owner2, + ExpireAt: now.Add(-time.Second).Unix(), + } + require.NoError(t, oldKeeper.SetDymName(oldCtx, dymName3JustExpired)) + + dymName4LongExpired := dymnstypes.DymName{ + Name: "long-expired", + Owner: owner1, + Controller: owner1, + ExpireAt: now.Add(-moduleParams.Misc.GracePeriodDuration - time.Second).Unix(), + } + require.NoError(t, oldKeeper.SetDymName(oldCtx, dymName4LongExpired)) + + so1 := dymnstypes.SellOrder{ + AssetId: dymName1.Name, + AssetType: dymnstypes.TypeName, + ExpireAt: 1, + MinPrice: testCoin(100), + SellPrice: uptr.To(testCoin(300)), + HighestBid: &dymnstypes.SellOrderBid{ + Bidder: bidder1, + Price: testCoin(200), + }, + } + require.NoError(t, oldKeeper.SetSellOrder(oldCtx, so1)) + + so2 := dymnstypes.SellOrder{ + AssetId: dymName2.Name, + AssetType: dymnstypes.TypeName, + ExpireAt: 1, + MinPrice: testCoin(100), + SellPrice: uptr.To(testCoin(900)), + HighestBid: &dymnstypes.SellOrderBid{ + Bidder: bidder2, + Price: testCoin(800), + }, + } + require.NoError(t, oldKeeper.SetSellOrder(oldCtx, so2)) + + so3 := dymnstypes.SellOrder{ + AssetId: dymName3JustExpired.Name, + AssetType: dymnstypes.TypeName, + ExpireAt: 1, + MinPrice: testCoin(100), + SellPrice: uptr.To(testCoin(200)), + } + require.NoError(t, oldKeeper.SetSellOrder(oldCtx, so3)) + + so4 := dymnstypes.SellOrder{ + AssetId: rollApp1.alias, + AssetType: dymnstypes.TypeAlias, + ExpireAt: 1, + MinPrice: testCoin(100), + SellPrice: uptr.To(testCoin(900)), + HighestBid: &dymnstypes.SellOrderBid{ + Bidder: bidder3, + Price: testCoin(777), + Params: []string{rollApp2.rollAppId}, + }, + } + require.NoError(t, oldKeeper.SetSellOrder(oldCtx, so4)) + + so5 := dymnstypes.SellOrder{ + AssetId: rollApp2.alias, + AssetType: dymnstypes.TypeAlias, + ExpireAt: 1, + MinPrice: testCoin(100), + } + require.NoError(t, oldKeeper.SetSellOrder(oldCtx, so5)) + + offer1 := dymnstypes.BuyOrder{ + Id: "101", + AssetId: dymName1.Name, + AssetType: dymnstypes.TypeName, + Buyer: buyer1, + OfferPrice: testCoin(100), + } + require.NoError(t, oldKeeper.SetBuyOrder(oldCtx, offer1)) + + offer2 := dymnstypes.BuyOrder{ + Id: "102", + AssetId: dymName2.Name, + AssetType: dymnstypes.TypeName, + Buyer: buyer2, + OfferPrice: testCoin(200), + } + require.NoError(t, oldKeeper.SetBuyOrder(oldCtx, offer2)) + + offer3OfExpired := dymnstypes.BuyOrder{ + Id: "103", + AssetId: dymName3JustExpired.Name, + AssetType: dymnstypes.TypeName, + Buyer: buyer3, + OfferPrice: testCoin(300), + } + require.NoError(t, oldKeeper.SetBuyOrder(oldCtx, offer3OfExpired)) + + offer4 := dymnstypes.BuyOrder{ + Id: "204", + AssetId: rollApp1.alias, + AssetType: dymnstypes.TypeAlias, + Params: []string{rollApp2.rollAppId}, + Buyer: buyer4, + OfferPrice: testCoin(333), + } + require.NoError(t, oldKeeper.SetBuyOrder(oldCtx, offer4)) + + offer5 := dymnstypes.BuyOrder{ + Id: "205", + AssetId: rollApp1.alias, + AssetType: dymnstypes.TypeAlias, + Params: []string{rollApp3.rollAppId}, + Buyer: buyer5, + OfferPrice: testCoin(555), + } + require.NoError(t, oldKeeper.SetBuyOrder(oldCtx, offer5)) + + for _, ra := range []rollapp{rollApp1, rollApp2, rollApp3} { + oldRollAppKeeper.SetRollapp(oldCtx, rollapptypes.Rollapp{ + RollappId: ra.rollAppId, + Owner: ra.owner, + }) + if ra.alias != "" { + require.NoError(t, oldKeeper.SetAliasForRollAppId(oldCtx, ra.rollAppId, ra.alias)) + } + } + + // Export genesis state + genState := dymns.ExportGenesis(oldCtx, oldKeeper) + + t.Run("params should be exported correctly", func(t *testing.T) { + require.Equal(t, oldKeeper.GetParams(oldCtx), genState.Params) + }) + + t.Run("dym-names should be exported correctly", func(t *testing.T) { + require.Len(t, genState.DymNames, 3) + require.Contains(t, genState.DymNames, dymName1) + require.Contains(t, genState.DymNames, dymName2) + + // Expired Dym-Names + // which less than grace period should be included + require.Contains(t, genState.DymNames, dymName3JustExpired) + // which passed grace period should not be included + require.NotContains(t, genState.DymNames, dymName4LongExpired) + }) + + t.Run("sell orders's non-refunded bids should be exported correctly", func(t *testing.T) { + require.Len(t, genState.SellOrderBids, 3) + require.Contains(t, genState.SellOrderBids, *so1.HighestBid) + require.Contains(t, genState.SellOrderBids, *so2.HighestBid) + require.Contains(t, genState.SellOrderBids, *so4.HighestBid) + // Expired sell order should not be exported + }) + + t.Run("buy offers should be exported correctly", func(t *testing.T) { + require.Len(t, genState.BuyOrders, 5) + require.Contains(t, genState.BuyOrders, offer1) + require.Contains(t, genState.BuyOrders, offer2) + require.Contains( + t, genState.BuyOrders, offer3OfExpired, + "offer should be exported even if the dym-name is expired", + ) + require.Contains(t, genState.BuyOrders, offer4) + require.Contains(t, genState.BuyOrders, offer5) + }) + + t.Run("aliases of rollapps should be exported correctly", func(t *testing.T) { + require.Len(t, genState.AliasesOfRollapps, 2) + require.Contains(t, genState.AliasesOfRollapps, dymnstypes.AliasesOfChainId{ + ChainId: rollApp1.rollAppId, + Aliases: []string{rollApp1.alias}, + }) + require.Contains(t, genState.AliasesOfRollapps, dymnstypes.AliasesOfChainId{ + ChainId: rollApp2.rollAppId, + Aliases: []string{rollApp2.alias}, + }) + }) + + // Init genesis state + + genState.Params.Misc.EndEpochHookIdentifier = "week" // Change the epoch identifier to test if it is imported correctly + genState.Params.Misc.SellOrderDuration = 9999 * time.Hour + + newDymNsKeeper, newBankKeeper, newRollAppKeeper, newCtx := testkeeper.DymNSKeeper(t) + newCtx = newCtx.WithBlockTime(now) + + for _, ra := range []rollapp{rollApp1, rollApp2, rollApp3} { + newRollAppKeeper.SetRollapp(newCtx, rollapptypes.Rollapp{ + RollappId: ra.rollAppId, + Owner: ra.owner, + }) + } + + dymns.InitGenesis(newCtx, newDymNsKeeper, *genState) + + t.Run("params should be imported correctly", func(t *testing.T) { + importedParams := newDymNsKeeper.GetParams(newCtx) + require.Equal(t, genState.Params, importedParams) + require.Equal(t, "week", importedParams.Misc.EndEpochHookIdentifier) + require.Equal(t, 9999*time.Hour, importedParams.Misc.SellOrderDuration) + }) + + t.Run("Dym-Names should be imported correctly", func(t *testing.T) { + require.Len(t, newDymNsKeeper.GetAllNonExpiredDymNames(newCtx), 2) + require.Len(t, newDymNsKeeper.GetAllDymNames(newCtx), 3) + + require.Equal(t, &dymName1, newDymNsKeeper.GetDymName(newCtx, dymName1.Name)) + require.Equal(t, &dymName2, newDymNsKeeper.GetDymName(newCtx, dymName2.Name)) + require.Equal(t, &dymName3JustExpired, newDymNsKeeper.GetDymName(newCtx, dymName3JustExpired.Name)) + require.Nil(t, newDymNsKeeper.GetDymName(newCtx, dymName4LongExpired.Name)) + }) + + t.Run("reverse lookup should be created correctly", func(t *testing.T) { + owned, err := newDymNsKeeper.GetDymNamesOwnedBy(newCtx, owner1) + require.NoError(t, err) + require.Len(t, owned, 1) + + owned, err = newDymNsKeeper.GetDymNamesOwnedBy(newCtx, owner2) + require.NoError(t, err) + require.Len(t, owned, 1) + + names, err := newDymNsKeeper.GetDymNamesContainsConfiguredAddress(newCtx, owner1) + require.NoError(t, err) + require.Len(t, names, 1) + + names, err = newDymNsKeeper.GetDymNamesContainsConfiguredAddress(newCtx, owner2) + require.NoError(t, err) + require.Len(t, names, 1) + + names, err = newDymNsKeeper.GetDymNamesContainsConfiguredAddress(newCtx, anotherAccount) + require.NoError(t, err) + require.Len(t, names, 1) + + names, err = newDymNsKeeper.GetDymNamesContainsFallbackAddress(newCtx, sdk.MustAccAddressFromBech32(owner1).Bytes()) + require.NoError(t, err) + require.Len(t, names, 1) + + names, err = newDymNsKeeper.GetDymNamesContainsFallbackAddress(newCtx, sdk.MustAccAddressFromBech32(owner2).Bytes()) + require.NoError(t, err) + require.Len(t, names, 1) + + names, err = newDymNsKeeper.GetDymNamesContainsFallbackAddress(newCtx, sdk.MustAccAddressFromBech32(anotherAccount).Bytes()) + require.NoError(t, err) + require.Empty(t, names, 0) + }) + + t.Run("sell orders's non-refunded bids should be refunded correctly", func(t *testing.T) { + require.Equal(t, + testCoin(200), + newBankKeeper.GetBalance(newCtx, sdk.MustAccAddressFromBech32(bidder1), params.BaseDenom), + ) + require.Equal(t, + testCoin(800), + newBankKeeper.GetBalance(newCtx, sdk.MustAccAddressFromBech32(bidder2), params.BaseDenom), + ) + require.Equal(t, + testCoin(777), + newBankKeeper.GetBalance(newCtx, sdk.MustAccAddressFromBech32(bidder3), params.BaseDenom), + ) + }) + + t.Run("non-refunded buy-offers should be refunded correctly", func(t *testing.T) { + require.Equal(t, + testCoin(100), + newBankKeeper.GetBalance(newCtx, sdk.MustAccAddressFromBech32(buyer1), params.BaseDenom), + ) + require.Equal(t, + testCoin(200), + newBankKeeper.GetBalance(newCtx, sdk.MustAccAddressFromBech32(buyer2), params.BaseDenom), + ) + require.Equal(t, + testCoin(300), + newBankKeeper.GetBalance(newCtx, sdk.MustAccAddressFromBech32(buyer3), params.BaseDenom), + ) + require.Equal(t, + testCoin(333), + newBankKeeper.GetBalance(newCtx, sdk.MustAccAddressFromBech32(buyer4), params.BaseDenom), + ) + require.Equal(t, + testCoin(555), + newBankKeeper.GetBalance(newCtx, sdk.MustAccAddressFromBech32(buyer5), params.BaseDenom), + ) + }) + + t.Run("aliases of RollApps should be linked correctly", func(t *testing.T) { + rollApp1Aliases := newDymNsKeeper.GetAliasesOfRollAppId(newCtx, rollApp1.rollAppId) + require.ElementsMatch(t, []string{rollApp1.alias}, rollApp1Aliases) + + rollApp2Aliases := newDymNsKeeper.GetAliasesOfRollAppId(newCtx, rollApp2.rollAppId) + require.ElementsMatch(t, []string{rollApp2.alias}, rollApp2Aliases) + + rollApp3Aliases := newDymNsKeeper.GetAliasesOfRollAppId(newCtx, rollApp3.rollAppId) + require.Empty(t, rollApp3Aliases) + }) + + // Init genesis state but with invalid input + newDymNsKeeper, newBankKeeper, _, newCtx = testkeeper.DymNSKeeper(t) + + t.Run("fail - invalid params", func(t *testing.T) { + require.Panics(t, func() { + dymns.InitGenesis(newCtx, newDymNsKeeper, dymnstypes.GenesisState{ + Params: dymnstypes.Params{ + Price: dymnstypes.PriceParams{}, // empty + Misc: dymnstypes.MiscParams{}, // empty + }, + }) + }) + }) + + t.Run("fail - invalid dym-name", func(t *testing.T) { + require.Panics(t, func() { + dymns.InitGenesis(newCtx, newDymNsKeeper, dymnstypes.GenesisState{ + Params: dymnstypes.DefaultParams(), + DymNames: []dymnstypes.DymName{ + {}, // empty content + }, + }) + }) + }) + + t.Run("fail - invalid highest bid", func(t *testing.T) { + require.Panics(t, func() { + dymns.InitGenesis(newCtx, newDymNsKeeper, dymnstypes.GenesisState{ + Params: dymnstypes.DefaultParams(), + SellOrderBids: []dymnstypes.SellOrderBid{ + {}, // empty content + }, + }) + }) + }) + + t.Run("fail - invalid offer", func(t *testing.T) { + require.Panics(t, func() { + dymns.InitGenesis(newCtx, newDymNsKeeper, dymnstypes.GenesisState{ + Params: dymnstypes.DefaultParams(), + BuyOrders: []dymnstypes.BuyOrder{ + {}, // empty content + }, + }) + }) + }) + + t.Run("fail - invalid aliases of RollApps", func(t *testing.T) { + require.Panics(t, func() { + dymns.InitGenesis(newCtx, newDymNsKeeper, dymnstypes.GenesisState{ + Params: dymnstypes.DefaultParams(), + AliasesOfRollapps: []dymnstypes.AliasesOfChainId{ + { + ChainId: "rollapp_1-1", + Aliases: []string{"@invalid"}, + }, + }, + }) + }) + + require.Panics(t, func() { + dymns.InitGenesis(newCtx, newDymNsKeeper, dymnstypes.GenesisState{ + Params: dymnstypes.DefaultParams(), + AliasesOfRollapps: []dymnstypes.AliasesOfChainId{ + { + ChainId: "@@@", + Aliases: []string{"alias"}, + }, + }, + }) + }) + }) +} + +func testCoin(amount int64) sdk.Coin { + return sdk.Coin{ + Denom: params.BaseDenom, + Amount: sdk.NewInt(amount), + } +} diff --git a/x/dymns/keeper/alias.go b/x/dymns/keeper/alias.go new file mode 100644 index 000000000..8f1f1920e --- /dev/null +++ b/x/dymns/keeper/alias.go @@ -0,0 +1,288 @@ +package keeper + +import ( + "slices" + + errorsmod "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" + dymnsutils "github.com/dymensionxyz/dymension/v3/x/dymns/utils" + "github.com/dymensionxyz/gerr-cosmos/gerrc" +) + +// GetRollAppIdByAlias returns the RollApp ID which is linked to the input alias. +func (k Keeper) GetRollAppIdByAlias(ctx sdk.Context, alias string) (rollAppId string, found bool) { + defer func() { + found = rollAppId != "" + }() + + store := ctx.KVStore(k.storeKey) + key := dymnstypes.AliasToRollAppIdRvlKey(alias) + bz := store.Get(key) + if bz != nil { + rollAppId = string(bz) + } + + return +} + +// GetAliasByRollAppId returns the first alias (in case RollApp has multiple aliases) linked to the RollApp ID. +func (k Keeper) GetAliasByRollAppId(ctx sdk.Context, rollAppId string) (alias string, found bool) { + if !k.IsRollAppId(ctx, rollAppId) { + return + } + + defer func() { + found = alias != "" + }() + + store := ctx.KVStore(k.storeKey) + key := dymnstypes.RollAppIdToAliasesKey(rollAppId) + bz := store.Get(key) + if bz != nil { + var multipleAliases dymnstypes.MultipleAliases + k.cdc.MustUnmarshal(bz, &multipleAliases) + alias = multipleAliases.Aliases[0] + } + + return +} + +// SetAliasForRollAppId assigns the usage of an alias to a RollApp. +func (k Keeper) SetAliasForRollAppId(ctx sdk.Context, rollAppId, alias string) error { + if !dymnsutils.IsValidAlias(alias) { + return errorsmod.Wrapf(gerrc.ErrInvalidArgument, "invalid alias: %s", alias) + } + + if !k.IsRollAppId(ctx, rollAppId) { + return errorsmod.Wrapf(gerrc.ErrInvalidArgument, "not a RollApp chain-id: %s", rollAppId) + } + + store := ctx.KVStore(k.storeKey) + keyR2A := dymnstypes.RollAppIdToAliasesKey(rollAppId) + keyA2R := dymnstypes.AliasToRollAppIdRvlKey(alias) + + // ensure the alias is not being used by another RollApp + if bz := store.Get(keyA2R); bz != nil { + return errorsmod.Wrapf(gerrc.ErrAlreadyExists, "alias currently being in used by: %s", string(bz)) + } + + // one RollApp can have multiple aliases, append to the existing list + var multipleAliases dymnstypes.MultipleAliases + if bz := store.Get(keyR2A); bz != nil { + k.cdc.MustUnmarshal(bz, &multipleAliases) + } + multipleAliases.Aliases = append(multipleAliases.Aliases, alias) + + store.Set(keyR2A, k.cdc.MustMarshal(&multipleAliases)) + store.Set(keyA2R, []byte(rollAppId)) + + return nil +} + +// GetAliasesOfRollAppId returns all aliases linked to a RollApp. +// Notes: the result does not exclude aliases reserved in params. +func (k Keeper) GetAliasesOfRollAppId(ctx sdk.Context, rollAppId string) []string { + store := ctx.KVStore(k.storeKey) + keyR2A := dymnstypes.RollAppIdToAliasesKey(rollAppId) + + var multipleAliases dymnstypes.MultipleAliases + if bz := store.Get(keyR2A); bz != nil { + k.cdc.MustUnmarshal(bz, &multipleAliases) + } + + return multipleAliases.Aliases +} + +// GetEffectiveAliasesByChainId returns all effective aliases by chain-id. +// Effective means: if an alias is reserved in params, it will be excluded from the result if the chain-id is a RollApp. +func (k Keeper) GetEffectiveAliasesByChainId(ctx sdk.Context, chainId string) []string { + var effectiveAliases []string + + // check if there is a mapping in params + for _, aliasesOfChainId := range k.ChainsParams(ctx).AliasesOfChainIds { + if aliasesOfChainId.ChainId != chainId { + continue + } + effectiveAliases = aliasesOfChainId.Aliases + break + } + + if k.IsRollAppId(ctx, chainId) { + aliasesOfRollApp := k.GetAliasesOfRollAppId(ctx, chainId) + + // If the chain-id is a RollApp, must exclude the aliases which being reserved in params. + // Please read the `processActiveAliasSellOrders` method (hooks.go) for more information. + reservedAliases := k.GetAllAliasAndChainIdInParams(ctx) + aliasesOfRollApp = slices.DeleteFunc(aliasesOfRollApp, func(a string) bool { + _, found := reservedAliases[a] + return found + }) + + effectiveAliases = append(effectiveAliases, aliasesOfRollApp...) + } + + return effectiveAliases +} + +// RemoveAliasFromRollAppId removes the linking of an existing alias from a RollApp. +func (k Keeper) RemoveAliasFromRollAppId(ctx sdk.Context, rollAppId, alias string) error { + if !dymnsutils.IsValidAlias(alias) { + return errorsmod.Wrapf(gerrc.ErrInvalidArgument, "invalid alias: %s", alias) + } + + if !k.IsRollAppId(ctx, rollAppId) { + return errorsmod.Wrapf(gerrc.ErrInvalidArgument, "not a RollApp chain-id: %s", rollAppId) + } + + store := ctx.KVStore(k.storeKey) + keyR2A := dymnstypes.RollAppIdToAliasesKey(rollAppId) + keyA2R := dymnstypes.AliasToRollAppIdRvlKey(alias) + + // ensure the alias is being used by the RollApp + bzRollAppId := store.Get(keyA2R) + if bzRollAppId == nil { + return errorsmod.Wrapf(gerrc.ErrNotFound, "alias not found: %s", alias) + } else if string(bzRollAppId) != rollAppId { + return errorsmod.Wrapf(gerrc.ErrPermissionDenied, "alias currently being in used by: %s", string(bzRollAppId)) + } + + // load the existing aliases of the RollApp + var multipleAliases dymnstypes.MultipleAliases + if bz := store.Get(keyR2A); bz != nil { + k.cdc.MustUnmarshal(bz, &multipleAliases) + } + + // remove the alias from the RollApp alias list + originalAliasesCount := len(multipleAliases.Aliases) + multipleAliases.Aliases = slices.DeleteFunc(multipleAliases.Aliases, func(a string) bool { + return a == alias + }) + if len(multipleAliases.Aliases) == originalAliasesCount { + return errorsmod.Wrapf(gerrc.ErrNotFound, "alias not found: %s", alias) + } + + // update store + + // if no alias left, remove the key, otherwise update new list + if len(multipleAliases.Aliases) == 0 { + store.Delete(keyR2A) + } else { + store.Set(keyR2A, k.cdc.MustMarshal(&multipleAliases)) + } + + // remove the alias to RollAppId mapping + store.Delete(keyA2R) + + return nil +} + +// MoveAliasToRollAppId moves the linking of an existing alias from a RollApp to another RollApp. +func (k Keeper) MoveAliasToRollAppId(ctx sdk.Context, srcRollAppId, alias, dstRollAppId string) error { + if !dymnsutils.IsValidAlias(alias) { + return errorsmod.Wrapf(gerrc.ErrInvalidArgument, "invalid alias: %s", alias) + } + + if !k.IsRollAppId(ctx, srcRollAppId) { + return errorsmod.Wrapf(gerrc.ErrInvalidArgument, "source RollApp does not exists: %s", srcRollAppId) + } + + if !k.IsRollAppId(ctx, dstRollAppId) { + return errorsmod.Wrapf(gerrc.ErrInvalidArgument, "destination RollApp does not exists: %s", dstRollAppId) + } + + inUsedByRollApp, found := k.GetRollAppIdByAlias(ctx, alias) + if !found { + return errorsmod.Wrapf(gerrc.ErrNotFound, "alias not found: %s", alias) + } + + if inUsedByRollApp != srcRollAppId { + return errorsmod.Wrapf(gerrc.ErrPermissionDenied, "source RollApp mis-match: %s", inUsedByRollApp) + } + + // remove the existing link + + if err := k.RemoveAliasFromRollAppId(ctx, srcRollAppId, alias); err != nil { + return err + } + + // set the new link + + return k.SetAliasForRollAppId(ctx, dstRollAppId, alias) +} + +// GetAllAliasAndChainIdInParams returns all aliases and chain-ids in the params. +// Note: this method returns a map so the iteration is non-deterministic, +// any implementation should not rely on the order of the result. +func (k Keeper) GetAllAliasAndChainIdInParams(ctx sdk.Context) map[string]struct{} { + result := make(map[string]struct{}) + for _, aliasesOfChainId := range k.ChainsParams(ctx).AliasesOfChainIds { + result[aliasesOfChainId.ChainId] = struct{}{} + for _, a := range aliasesOfChainId.Aliases { + result[a] = struct{}{} + } + } + + return result +} + +// IsAliasPresentsInParamsAsAliasOrChainId returns true if the alias presents in the params. +// Extra check if it collision with chain-ids there. +func (k Keeper) IsAliasPresentsInParamsAsAliasOrChainId(ctx sdk.Context, alias string) bool { + _, found := k.GetAllAliasAndChainIdInParams(ctx)[alias] + return found +} + +// SetDefaultAliasForRollApp move the alias into the first place, so it can be used as default alias in resolution. +func (k Keeper) SetDefaultAliasForRollApp(ctx sdk.Context, rollAppId, alias string) error { + // load the existing aliases of the RollApp from store + existingAliases := k.GetAliasesOfRollAppId(ctx, rollAppId) + + // swap the alias to the first place + existingIndex := -1 + for i, existingAlias := range existingAliases { + if alias == existingAlias { + existingIndex = i + break + } + } + + if existingIndex < 0 { + return errorsmod.Wrapf(gerrc.ErrNotFound, "alias is not linked to the RollApp: %s", alias) + } + + if existingIndex == 0 { // no need to do anything + return nil + } + + existingAliases[0], existingAliases[existingIndex] = existingAliases[existingIndex], existingAliases[0] + + // update the new list into store + + store := ctx.KVStore(k.storeKey) + keyR2A := dymnstypes.RollAppIdToAliasesKey(rollAppId) + store.Set(keyR2A, k.cdc.MustMarshal(&dymnstypes.MultipleAliases{Aliases: existingAliases})) + + return nil +} + +// GetAllRollAppsWithAliases returns all RollApp IDs which have aliases and their aliases. +func (k Keeper) GetAllRollAppsWithAliases(ctx sdk.Context) (list []dymnstypes.AliasesOfChainId) { + store := ctx.KVStore(k.storeKey) + + iterator := sdk.KVStorePrefixIterator(store, dymnstypes.KeyPrefixRollAppIdToAliases) + defer func() { + _ = iterator.Close() + }() + + for ; iterator.Valid(); iterator.Next() { + var multipleAliases dymnstypes.MultipleAliases + k.cdc.MustUnmarshal(iterator.Value(), &multipleAliases) + list = append(list, dymnstypes.AliasesOfChainId{ + ChainId: string(iterator.Key()[len(dymnstypes.KeyPrefixRollAppIdToAliases):]), + Aliases: multipleAliases.Aliases, + }) + } + + return list +} diff --git a/x/dymns/keeper/alias_test.go b/x/dymns/keeper/alias_test.go new file mode 100644 index 000000000..1337ed22a --- /dev/null +++ b/x/dymns/keeper/alias_test.go @@ -0,0 +1,1127 @@ +package keeper_test + +import ( + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" + rollapptypes "github.com/dymensionxyz/dymension/v3/x/rollapp/types" +) + +func (s *KeeperTestSuite) TestKeeper_GetSetAliasForRollAppId() { + rollApp1 := *newRollApp("rollapp_1-1").WithAlias("al1") + rollApp2 := *newRollApp("rolling_2-2").WithAlias("al2") + rollApp3NotExists := *newRollApp("nah_0-0").WithAlias("al3") + + for i, ra := range []rollapp{rollApp1, rollApp2} { + s.rollAppKeeper.SetRollapp(s.ctx, rollapptypes.Rollapp{ + RollappId: ra.rollAppId, + Owner: testAddr(uint64(i)).bech32(), + }) + } + + s.Run("set - can set", func() { + s.Require().True(s.dymNsKeeper.IsRollAppId(s.ctx, rollApp1.rollAppId), "must be a RollApp, just not set alias") + + err := s.dymNsKeeper.SetAliasForRollAppId(s.ctx, rollApp1.rollAppId, rollApp1.alias) + s.Require().NoError(err) + + alias, found := s.dymNsKeeper.GetAliasByRollAppId(s.ctx, rollApp1.rollAppId) + s.Equal(rollApp1.alias, alias) + s.True(found) + + rollAppId, found := s.dymNsKeeper.GetRollAppIdByAlias(s.ctx, rollApp1.alias) + s.Equal(rollApp1.rollAppId, rollAppId) + s.True(found) + }) + + s.Run("set - can NOT set if alias is being in-used by another RollApp", func() { + rollAppId, found := s.dymNsKeeper.GetRollAppIdByAlias(s.ctx, rollApp1.alias) + s.Equal(rollApp1.rollAppId, rollAppId) + s.True(found) + + err := s.dymNsKeeper.SetAliasForRollAppId(s.ctx, rollApp2.rollAppId, rollApp1.alias) + s.Require().ErrorContains(err, "alias currently being in used by:") + }) + + s.Run("set - reject bad chain-id", func() { + err := s.dymNsKeeper.SetAliasForRollAppId(s.ctx, "bad@", "alias") + s.Error(err) + }) + + s.Run("set - reject bad alias", func() { + s.Require().True(s.dymNsKeeper.IsRollAppId(s.ctx, rollApp2.rollAppId), "must be a RollApp") + + err := s.dymNsKeeper.SetAliasForRollAppId(s.ctx, rollApp2.rollAppId, "") + s.Require().ErrorContains(err, "invalid alias") + + err = s.dymNsKeeper.SetAliasForRollAppId(s.ctx, rollApp2.rollAppId, "@") + s.Require().ErrorContains(err, "invalid alias") + }) + + s.Run("get - of existing RollApp but no alias set", func() { + s.Require().True(s.dymNsKeeper.IsRollAppId(s.ctx, rollApp2.rollAppId), "must be a RollApp, just not set alias") + + alias, found := s.dymNsKeeper.GetAliasByRollAppId(s.ctx, rollApp2.rollAppId) + s.Empty(alias) + s.False(found) + + rollAppId, found := s.dymNsKeeper.GetRollAppIdByAlias(s.ctx, rollApp2.alias) + s.Empty(rollAppId) + s.False(found) + }) + + s.Run("set - non-exists RollApp returns error", func() { + s.Require().False(s.dymNsKeeper.IsRollAppId(s.ctx, rollApp3NotExists.rollAppId)) + + err := s.dymNsKeeper.SetAliasForRollAppId(s.ctx, rollApp3NotExists.rollAppId, rollApp3NotExists.alias) + s.Require().ErrorContains(err, "not a RollApp") + }) + + s.Run("get - non-exists RollApp returns empty", func() { + s.Require().False(s.dymNsKeeper.IsRollAppId(s.ctx, rollApp3NotExists.rollAppId)) + + alias, found := s.dymNsKeeper.GetAliasByRollAppId(s.ctx, rollApp3NotExists.rollAppId) + s.Empty(alias) + s.False(found) + + rollAppId, found := s.dymNsKeeper.GetRollAppIdByAlias(s.ctx, rollApp3NotExists.alias) + s.Empty(rollAppId) + s.False(found) + }) + + s.Run("set/get - can set multiple alias to a single Roll-App", func() { + s.RefreshContext() + + type testCase struct { + rollAppId string + aliases []string + } + + testcases := []testCase{ + { + rollAppId: "rollapp_1-1", + aliases: []string{"one", "two", "three"}, + }, + { + rollAppId: "rollapp_2-2", + aliases: []string{"four", "five"}, + }, + } + + for _, tc := range testcases { + s.rollAppKeeper.SetRollapp(s.ctx, rollapptypes.Rollapp{ + RollappId: tc.rollAppId, + Owner: testAddr(0).bech32(), + }) + } + + for _, tc := range testcases { + for _, alias := range tc.aliases { + err := s.dymNsKeeper.SetAliasForRollAppId(s.ctx, tc.rollAppId, alias) + s.Require().NoError(err) + } + } + + for _, tc := range testcases { + for _, alias := range tc.aliases { + rollAppId, found := s.dymNsKeeper.GetRollAppIdByAlias(s.ctx, alias) + s.Equal(tc.rollAppId, rollAppId) + s.True(found) + } + + alias, found := s.dymNsKeeper.GetAliasByRollAppId(s.ctx, tc.rollAppId) + s.True(found) + s.Contains(tc.aliases, alias) + s.Equal(alias, tc.aliases[0], "should returns the first one added") + } + }) +} + +func (s *KeeperTestSuite) TestKeeper_GetAliasesOfRollAppId() { + rollApp1 := *newRollApp("rollapp_1-1").WithAlias("one").WithAlias("more").WithAlias("alias") + rollApp2 := *newRollApp("rollapp_2-2").WithAlias("two") + rollApp3NoAlias := *newRollApp("rollapp_3-3") + + s.persistRollApp(rollApp1) + s.persistRollApp(rollApp2) + s.persistRollApp(rollApp3NoAlias) + + aliases := s.dymNsKeeper.GetAliasesOfRollAppId(s.ctx, rollApp1.rollAppId) + s.Require().Equal([]string{"one", "more", "alias"}, aliases) + + aliases = s.dymNsKeeper.GetAliasesOfRollAppId(s.ctx, rollApp2.rollAppId) + s.Require().Equal([]string{"two"}, aliases) + + aliases = s.dymNsKeeper.GetAliasesOfRollAppId(s.ctx, rollApp3NoAlias.rollAppId) + s.Require().Empty(aliases) +} + +func (s *KeeperTestSuite) TestKeeper_GetEffectiveAliasesByChainId() { + rollApp1 := *newRollApp("rollapp_1-1").WithAlias("one").WithAlias("two") + rollApp2 := *newRollApp("rollapp_2-2").WithAlias("three") + rollApp3WithoutAlias := *newRollApp("rollapp_3-1") + + tests := []struct { + name string + paramsAliasesByChainId []dymnstypes.AliasesOfChainId + rollApps []rollapp + chainId string + wantErr bool + wantErrContains string + want []string + }{ + { + name: "pass - can returns, case in params", + paramsAliasesByChainId: []dymnstypes.AliasesOfChainId{ + { + ChainId: "dymension_1100-1", + Aliases: []string{"dym", "dymension"}, + }, + { + ChainId: "blumbus_111-1", + Aliases: []string{"blumbus"}, + }, + }, + rollApps: []rollapp{rollApp1, rollApp2, rollApp3WithoutAlias}, + chainId: "dymension_1100-1", + wantErr: false, + want: []string{"dym", "dymension"}, + }, + { + name: "pass - can returns, case in RollApp", + paramsAliasesByChainId: []dymnstypes.AliasesOfChainId{ + { + ChainId: "dymension_1100-1", + Aliases: []string{"dym", "dymension"}, + }, + { + ChainId: "blumbus_111-1", + Aliases: []string{"blumbus"}, + }, + }, + rollApps: []rollapp{rollApp1, rollApp2, rollApp3WithoutAlias}, + chainId: rollApp1.rollAppId, + wantErr: false, + want: rollApp1.aliases, + }, + { + name: "pass - if an alias of a RollApp is reserved in params, exclude it", + paramsAliasesByChainId: []dymnstypes.AliasesOfChainId{ + { + ChainId: "dymension_1100-1", + Aliases: []string{ + "dym", + rollApp1.aliases[0], // reserved + }, + }, + }, + rollApps: []rollapp{rollApp1}, + chainId: rollApp1.rollAppId, + wantErr: false, + want: rollApp1.aliases[1:], // ignore the reserved one + }, + { + name: "pass - if an alias of a RollApp is reserved in params, exclude it, if not any remaining alias, skip it", + paramsAliasesByChainId: []dymnstypes.AliasesOfChainId{ + { + ChainId: "dymension_1100-1", + Aliases: rollApp1.aliases, + }, + }, + rollApps: []rollapp{rollApp1}, + chainId: rollApp1.rollAppId, + wantErr: false, + want: nil, // totally excluded + }, + { + name: "pass - if a RollApp ID presents in both params and local mapped alias, merge result", + paramsAliasesByChainId: []dymnstypes.AliasesOfChainId{ + { + ChainId: "dymension_1100-1", + Aliases: []string{"dym"}, + }, + { + ChainId: rollApp1.rollAppId, + Aliases: []string{"more", "alias"}, + }, + }, + rollApps: []rollapp{rollApp1, rollApp2, rollApp3WithoutAlias}, + chainId: rollApp1.rollAppId, + wantErr: false, + want: append( // merged + []string{"more", "alias"}, // respect params, put it on head + rollApp1.aliases..., + ), + }, + { + name: "pass - if a chain does not have alias (both params and RollApp), returns empty", + paramsAliasesByChainId: []dymnstypes.AliasesOfChainId{ + { + ChainId: "dymension_1100-1", + Aliases: []string{"dym"}, + }, + { + ChainId: "blumbus_111-1", + Aliases: nil, + }, + }, + rollApps: []rollapp{rollApp1, rollApp3WithoutAlias}, + chainId: "blumbus_111-1", + wantErr: false, + want: nil, + }, + } + for _, tt := range tests { + s.Run(tt.name, func() { + s.RefreshContext() + + s.updateModuleParams(func(moduleParams dymnstypes.Params) dymnstypes.Params { + moduleParams.Chains.AliasesOfChainIds = tt.paramsAliasesByChainId + return moduleParams + }) + + for _, rollApp := range tt.rollApps { + s.persistRollApp(rollApp) + } + + aliases := s.dymNsKeeper.GetEffectiveAliasesByChainId(s.ctx, tt.chainId) + + if len(tt.want) == 0 { + s.Empty(aliases) + } else { + s.Equal(tt.want, aliases) + } + }) + } +} + +func (s *KeeperTestSuite) TestKeeper_RemoveAliasFromRollAppId() { + rollApp1 := *newRollApp("rollapp_1-1").WithAlias("al1") + rollApp2 := *newRollApp("rolling_2-2").WithAlias("al2") + rollApp3 := *newRollApp("rollapp_3-3").WithAlias("al3") + rollApp4NoAlias := *newRollApp("noa_4-4") + rollApp5NotExists := *newRollApp("nah_5-5").WithAlias("al5") + + const aliasOne = "one" + const aliasTwo = "two" + const unusedAlias = "unused" + + tests := []struct { + name string + addRollApps []rollapp + preRunFunc func(*KeeperTestSuite) + inputRollAppId string + inputAlias string + wantErr bool + wantErrContains string + afterTestFunc func(*KeeperTestSuite) + }{ + { + name: "pass - can remove", + addRollApps: []rollapp{rollApp1}, + preRunFunc: func(s *KeeperTestSuite) { + s.requireRollApp(rollApp1.rollAppId).HasAlias(rollApp1.alias) + }, + inputRollAppId: rollApp1.rollAppId, + inputAlias: rollApp1.alias, + wantErr: false, + afterTestFunc: func(s *KeeperTestSuite) { + s.requireRollApp(rollApp1.rollAppId).HasNoAlias() + s.requireAlias(rollApp1.alias).NotInUse() + }, + }, + { + name: "pass - can remove among multiple records", + addRollApps: []rollapp{rollApp1, rollApp2, rollApp3}, + preRunFunc: func(s *KeeperTestSuite) { + s.requireRollApp(rollApp1.rollAppId).HasAlias(rollApp1.alias) + s.requireRollApp(rollApp2.rollAppId).HasAlias(rollApp2.alias) + s.requireRollApp(rollApp3.rollAppId).HasAlias(rollApp3.alias) + }, + inputRollAppId: rollApp2.rollAppId, + inputAlias: rollApp2.alias, + wantErr: false, + afterTestFunc: func(s *KeeperTestSuite) { + s.requireRollApp(rollApp2.rollAppId).HasNoAlias() + s.requireAlias(rollApp2.alias).NotInUse() + + // other records remain unchanged + s.requireRollApp(rollApp1.rollAppId).HasAlias(rollApp1.alias) + s.requireRollApp(rollApp3.rollAppId).HasAlias(rollApp3.alias) + }, + }, + { + name: "fail - reject if input RollApp ID is empty", + addRollApps: []rollapp{rollApp1}, + preRunFunc: func(s *KeeperTestSuite) { + s.requireRollApp(rollApp1.rollAppId).HasAlias(rollApp1.alias) + }, + inputRollAppId: "", + inputAlias: rollApp1.alias, + wantErr: true, + wantErrContains: "not a RollApp", + afterTestFunc: func(s *KeeperTestSuite) { + // record remains unchanged + s.requireRollApp(rollApp1.rollAppId).HasAlias(rollApp1.alias) + }, + }, + { + name: "fail - reject if input RollApp ID is not exists", + addRollApps: []rollapp{rollApp1}, + preRunFunc: func(s *KeeperTestSuite) { + s.Require().False(s.dymNsKeeper.IsRollAppId(s.ctx, rollApp5NotExists.rollAppId)) + + s.requireRollApp(rollApp1.rollAppId).HasAlias(rollApp1.alias) + }, + inputRollAppId: rollApp5NotExists.rollAppId, + inputAlias: rollApp5NotExists.alias, + wantErr: true, + wantErrContains: "not a RollApp", + afterTestFunc: func(s *KeeperTestSuite) { + // other records remain unchanged + s.requireRollApp(rollApp1.rollAppId).HasAlias(rollApp1.alias) + }, + }, + { + name: "fail - reject if input Alias is empty", + addRollApps: []rollapp{rollApp1}, + preRunFunc: func(s *KeeperTestSuite) { + s.requireRollApp(rollApp1.rollAppId).HasAlias(rollApp1.alias) + }, + inputRollAppId: rollApp1.rollAppId, + inputAlias: "", + wantErr: true, + wantErrContains: "invalid alias", + afterTestFunc: func(s *KeeperTestSuite) { + // record remains unchanged + s.requireRollApp(rollApp1.rollAppId).HasAlias(rollApp1.alias) + }, + }, + { + name: "fail - reject if input Alias is malformed", + addRollApps: []rollapp{rollApp1}, + preRunFunc: func(s *KeeperTestSuite) { + s.requireRollApp(rollApp1.rollAppId).HasAlias(rollApp1.alias) + }, + inputRollAppId: rollApp1.rollAppId, + inputAlias: "@", + wantErr: true, + wantErrContains: "invalid alias", + afterTestFunc: func(s *KeeperTestSuite) { + // record remains unchanged + s.requireRollApp(rollApp1.rollAppId).HasAlias(rollApp1.alias) + }, + }, + { + name: "fail - reject if Roll App has no alias linked", + addRollApps: []rollapp{rollApp4NoAlias}, + preRunFunc: func(s *KeeperTestSuite) { + s.requireRollApp(rollApp4NoAlias.rollAppId).HasNoAlias() + }, + inputRollAppId: rollApp4NoAlias.rollAppId, + inputAlias: aliasOne, + wantErr: true, + wantErrContains: "alias not found", + afterTestFunc: func(s *KeeperTestSuite) { + // record remains unchanged + s.requireRollApp(rollApp4NoAlias.rollAppId).HasNoAlias() + }, + }, + { + name: "fail - reject if Roll App has no alias linked and input alias linked to another Roll App", + addRollApps: []rollapp{rollApp1, rollApp4NoAlias}, + preRunFunc: func(s *KeeperTestSuite) { + s.requireRollApp(rollApp1.rollAppId).HasAlias(rollApp1.alias) + s.requireRollApp(rollApp4NoAlias.rollAppId).HasNoAlias() + }, + inputRollAppId: rollApp4NoAlias.rollAppId, + inputAlias: rollApp1.alias, + wantErr: true, + wantErrContains: "alias currently being in used by:", + afterTestFunc: func(s *KeeperTestSuite) { + // records remain unchanged + s.requireRollApp(rollApp1.rollAppId).HasAlias(rollApp1.alias) + s.requireRollApp(rollApp4NoAlias.rollAppId).HasNoAlias() + }, + }, + { + name: "fail - reject if remove alias linked to another Roll App", + addRollApps: []rollapp{rollApp1, rollApp2}, + preRunFunc: func(s *KeeperTestSuite) { + s.requireRollApp(rollApp1.rollAppId).HasAlias(rollApp1.alias) + s.requireRollApp(rollApp2.rollAppId).HasAlias(rollApp2.alias) + }, + inputRollAppId: rollApp1.rollAppId, + inputAlias: rollApp2.alias, + wantErr: true, + wantErrContains: "alias currently being in used by", + afterTestFunc: func(s *KeeperTestSuite) { + // records remain unchanged + s.requireRollApp(rollApp1.rollAppId).HasAlias(rollApp1.alias) + s.requireRollApp(rollApp2.rollAppId).HasAlias(rollApp2.alias) + }, + }, + { + name: "fail - reject if input alias does not link to any Roll App", + addRollApps: []rollapp{rollApp1}, + preRunFunc: func(s *KeeperTestSuite) { + s.requireRollApp(rollApp1.rollAppId).HasAlias(rollApp1.alias) + }, + inputRollAppId: rollApp1.rollAppId, + inputAlias: unusedAlias, + wantErr: true, + wantErrContains: "alias not found", + afterTestFunc: func(s *KeeperTestSuite) { + // records remain unchanged + s.requireRollApp(rollApp1.rollAppId).HasAlias(rollApp1.alias) + }, + }, + { + name: "pass - remove alias correctly among multiple aliases linked to a Roll App", + addRollApps: []rollapp{rollApp1}, + preRunFunc: func(s *KeeperTestSuite) { + s.Require().NoError(s.dymNsKeeper.SetAliasForRollAppId(s.ctx, rollApp1.rollAppId, aliasOne)) + s.Require().NoError(s.dymNsKeeper.SetAliasForRollAppId(s.ctx, rollApp1.rollAppId, aliasTwo)) + + s.requireRollApp(rollApp1.rollAppId).HasAlias(rollApp1.alias, aliasOne, aliasTwo) + }, + inputRollAppId: rollApp1.rollAppId, + inputAlias: aliasOne, + wantErr: false, + afterTestFunc: func(s *KeeperTestSuite) { + s.requireRollApp(rollApp1.rollAppId).HasAlias(rollApp1.alias, aliasTwo) + s.requireAlias(aliasOne).NotInUse() + }, + }, + } + for _, tt := range tests { + s.Run(tt.name, func() { + s.RefreshContext() + + for _, ra := range tt.addRollApps { + s.persistRollApp(ra) + } + + if tt.preRunFunc != nil { + tt.preRunFunc(s) + } + + err := s.dymNsKeeper.RemoveAliasFromRollAppId(s.ctx, tt.inputRollAppId, tt.inputAlias) + + defer func() { + if s.T().Failed() { + return + } + if tt.afterTestFunc != nil { + tt.afterTestFunc(s) + } + }() + + if tt.wantErr { + s.Require().ErrorContains(err, tt.wantErrContains) + return + } + + s.Require().NoError(err) + }) + } +} + +func (s *KeeperTestSuite) TestKeeper_MoveAliasToRollAppId() { + rollApp1 := *newRollApp("rollapp_1-1").WithAlias("al1") + rollApp2 := *newRollApp("rolling_2-2").WithAlias("al2") + rollApp3WithoutAlias := *newRollApp("rollapp_3-3") + rollApp4WithoutAlias := *newRollApp("rollapp_4-4") + + tests := []struct { + name string + rollapps []rollapp + srcRollAppId string + alias string + dstRollAppId string + preTestFunc func(*KeeperTestSuite) + wantErr bool + wantErrContains string + afterTestFunc func(*KeeperTestSuite) + }{ + { + name: "pass - can move", + rollapps: []rollapp{rollApp1, rollApp3WithoutAlias}, + srcRollAppId: rollApp1.rollAppId, + alias: rollApp1.alias, + dstRollAppId: rollApp3WithoutAlias.rollAppId, + preTestFunc: func(s *KeeperTestSuite) { + s.requireRollApp(rollApp1.rollAppId).HasOnlyAlias(rollApp1.alias) + s.requireRollApp(rollApp3WithoutAlias.rollAppId).HasNoAlias() + }, + wantErr: false, + afterTestFunc: func(s *KeeperTestSuite) { + s.requireRollApp(rollApp1.rollAppId).HasNoAlias() + s.requireRollApp(rollApp3WithoutAlias.rollAppId).HasOnlyAlias(rollApp1.alias) + }, + }, + { + name: "pass - can move to RollApp with existing Alias", + rollapps: []rollapp{rollApp1, rollApp2}, + srcRollAppId: rollApp1.rollAppId, + alias: rollApp1.alias, + dstRollAppId: rollApp2.rollAppId, + preTestFunc: func(s *KeeperTestSuite) { + s.requireRollApp(rollApp1.rollAppId).HasOnlyAlias(rollApp1.alias) + s.requireRollApp(rollApp2.rollAppId).HasOnlyAlias(rollApp2.alias) + }, + wantErr: false, + afterTestFunc: func(s *KeeperTestSuite) { + s.requireRollApp(rollApp1.rollAppId).HasNoAlias() + + // now 2 aliases are linked to roll app 2 + s.requireRollApp(rollApp2.rollAppId).HasAlias(rollApp1.alias, rollApp2.alias) + }, + }, + { + name: "pass - can move to RollApp with existing multiple Aliases", + rollapps: []rollapp{rollApp1, rollApp2}, + srcRollAppId: rollApp1.rollAppId, + alias: rollApp1.alias, + dstRollAppId: rollApp2.rollAppId, + preTestFunc: func(s *KeeperTestSuite) { + s.requireRollApp(rollApp1.rollAppId).HasOnlyAlias(rollApp1.alias) + s.requireRollApp(rollApp2.rollAppId).HasOnlyAlias(rollApp2.alias) + + // add another alias to roll app 2 + err := s.dymNsKeeper.SetAliasForRollAppId(s.ctx, rollApp2.rollAppId, "new") + s.Require().NoError(err) + }, + wantErr: false, + afterTestFunc: func(s *KeeperTestSuite) { + s.requireRollApp(rollApp1.rollAppId).HasNoAlias() + // now 3 aliases are linked to roll app 2 + s.requireRollApp(rollApp2.rollAppId).HasAlias(rollApp1.alias, rollApp2.alias, "new") + }, + }, + { + name: "pass - can move to RollApp without alias", + rollapps: []rollapp{rollApp1, rollApp3WithoutAlias}, + srcRollAppId: rollApp1.rollAppId, + alias: rollApp1.alias, + dstRollAppId: rollApp3WithoutAlias.rollAppId, + preTestFunc: func(s *KeeperTestSuite) { + s.requireRollApp(rollApp1.rollAppId).HasOnlyAlias(rollApp1.alias) + s.requireRollApp(rollApp3WithoutAlias.rollAppId).HasNoAlias() + }, + wantErr: false, + afterTestFunc: func(s *KeeperTestSuite) { + s.requireRollApp(rollApp1.rollAppId).HasNoAlias() + s.requireRollApp(rollApp3WithoutAlias.rollAppId).HasOnlyAlias(rollApp1.alias) + }, + }, + { + name: "fail - source RollApp has no alias linked", + rollapps: []rollapp{rollApp3WithoutAlias, rollApp4WithoutAlias}, + srcRollAppId: rollApp3WithoutAlias.rollAppId, + alias: "alias", + dstRollAppId: rollApp4WithoutAlias.rollAppId, + preTestFunc: func(s *KeeperTestSuite) { + s.requireRollApp(rollApp3WithoutAlias.rollAppId).HasNoAlias() + s.requireRollApp(rollApp4WithoutAlias.rollAppId).HasNoAlias() + }, + wantErr: true, + wantErrContains: "alias not found", + afterTestFunc: func(s *KeeperTestSuite) { + s.requireRollApp(rollApp3WithoutAlias.rollAppId).HasNoAlias() + s.requireRollApp(rollApp4WithoutAlias.rollAppId).HasNoAlias() + }, + }, + { + name: "fail - source RollApp has no alias linked, move alias of another", + rollapps: []rollapp{rollApp1, rollApp3WithoutAlias, rollApp4WithoutAlias}, + srcRollAppId: rollApp3WithoutAlias.rollAppId, + alias: rollApp1.alias, + dstRollAppId: rollApp4WithoutAlias.rollAppId, + preTestFunc: func(s *KeeperTestSuite) { + s.requireRollApp(rollApp1.rollAppId).HasOnlyAlias(rollApp1.alias) + s.requireRollApp(rollApp3WithoutAlias.rollAppId).HasNoAlias() + s.requireRollApp(rollApp4WithoutAlias.rollAppId).HasNoAlias() + }, + wantErr: true, + wantErrContains: "permission denied", + afterTestFunc: func(s *KeeperTestSuite) { + s.requireRollApp(rollApp1.rollAppId).HasOnlyAlias(rollApp1.alias) + s.requireRollApp(rollApp3WithoutAlias.rollAppId).HasNoAlias() + s.requireRollApp(rollApp4WithoutAlias.rollAppId).HasNoAlias() + }, + }, + { + name: "fail - move alias in-used by another RollApp", + rollapps: []rollapp{rollApp1, rollApp2, rollApp3WithoutAlias}, + srcRollAppId: rollApp1.rollAppId, + alias: rollApp2.alias, + dstRollAppId: rollApp3WithoutAlias.rollAppId, + preTestFunc: func(s *KeeperTestSuite) { + s.requireRollApp(rollApp1.rollAppId).HasOnlyAlias(rollApp1.alias) + s.requireRollApp(rollApp2.rollAppId).HasOnlyAlias(rollApp2.alias) + s.requireRollApp(rollApp3WithoutAlias.rollAppId).HasNoAlias() + }, + wantErr: true, + wantErrContains: "permission denied", + afterTestFunc: func(s *KeeperTestSuite) { + s.requireRollApp(rollApp1.rollAppId).HasOnlyAlias(rollApp1.alias) + s.requireRollApp(rollApp2.rollAppId).HasOnlyAlias(rollApp2.alias) + s.requireRollApp(rollApp3WithoutAlias.rollAppId).HasNoAlias() + }, + }, + { + name: "fail - source RollApp ID is malformed", + rollapps: []rollapp{rollApp3WithoutAlias}, + srcRollAppId: "@bad", + alias: "alias", + dstRollAppId: rollApp3WithoutAlias.rollAppId, + preTestFunc: func(s *KeeperTestSuite) { + s.requireRollApp(rollApp3WithoutAlias.rollAppId).HasNoAlias() + s.requireAlias("alias").NotInUse() + }, + wantErr: true, + wantErrContains: "source RollApp does not exists", + afterTestFunc: func(s *KeeperTestSuite) { + s.requireRollApp(rollApp3WithoutAlias.rollAppId).HasNoAlias() + s.requireAlias("alias").NotInUse() + }, + }, + { + name: "fail - bad alias", + rollapps: []rollapp{rollApp1, rollApp2}, + srcRollAppId: rollApp1.rollAppId, + alias: "@bad", + dstRollAppId: rollApp2.rollAppId, + preTestFunc: func(s *KeeperTestSuite) { + s.requireRollApp(rollApp1.rollAppId).HasOnlyAlias(rollApp1.alias) + s.requireRollApp(rollApp2.rollAppId).HasOnlyAlias(rollApp2.alias) + }, + wantErr: true, + wantErrContains: "invalid alias", + afterTestFunc: func(s *KeeperTestSuite) { + s.requireRollApp(rollApp1.rollAppId).HasOnlyAlias(rollApp1.alias) + s.requireRollApp(rollApp2.rollAppId).HasOnlyAlias(rollApp2.alias) + }, + }, + { + name: "fail - destination RollApp ID is malformed", + rollapps: []rollapp{rollApp1}, + srcRollAppId: rollApp1.rollAppId, + alias: rollApp1.alias, + dstRollAppId: "@bad", + preTestFunc: func(s *KeeperTestSuite) { + s.requireRollApp(rollApp1.rollAppId).HasOnlyAlias(rollApp1.alias) + }, + wantErr: true, + wantErrContains: "destination RollApp does not exists", + afterTestFunc: func(s *KeeperTestSuite) { + s.requireRollApp(rollApp1.rollAppId).HasOnlyAlias(rollApp1.alias) + }, + }, + } + for _, tt := range tests { + s.Run(tt.name, func() { + s.RefreshContext() + + for _, ra := range tt.rollapps { + s.persistRollApp(ra) + } + + if tt.preTestFunc != nil { + tt.preTestFunc(s) + } + + err := s.dymNsKeeper.MoveAliasToRollAppId(s.ctx, tt.srcRollAppId, tt.alias, tt.dstRollAppId) + + defer func() { + if s.T().Failed() { + return + } + if tt.afterTestFunc != nil { + tt.afterTestFunc(s) + } + }() + + if tt.wantErr { + s.Require().ErrorContains(err, tt.wantErrContains) + return + } + + s.Require().NoError(err) + }) + } +} + +func (s *KeeperTestSuite) TestKeeper_GetAllAliasAndChainIdInParams() { + tests := []struct { + name string + setup func() []dymnstypes.AliasesOfChainId + want map[string]struct{} + }{ + { + name: "returns empty when params empty", + setup: func() []dymnstypes.AliasesOfChainId { + return nil + }, + want: map[string]struct{}{}, + }, + { + name: "returns alias and chain-id, single record, no alias", + setup: func() []dymnstypes.AliasesOfChainId { + return []dymnstypes.AliasesOfChainId{ + { + ChainId: "dymension_1100-1", + Aliases: nil, + }, + } + }, + want: map[string]struct{}{ + "dymension_1100-1": {}, + }, + }, + { + name: "returns alias and chain-id, single record, single alias", + setup: func() []dymnstypes.AliasesOfChainId { + return []dymnstypes.AliasesOfChainId{ + { + ChainId: "dymension_1100-1", + Aliases: []string{"dym"}, + }, + } + }, + want: map[string]struct{}{ + "dymension_1100-1": {}, + "dym": {}, + }, + }, + { + name: "returns alias and chain-id, single record, multiple aliases", + setup: func() []dymnstypes.AliasesOfChainId { + return []dymnstypes.AliasesOfChainId{ + { + ChainId: "dymension_1100-1", + Aliases: []string{"dym", "dymension"}, + }, + } + }, + want: map[string]struct{}{ + "dymension_1100-1": {}, + "dym": {}, + "dymension": {}, + }, + }, + { + name: "returns alias and chain-id, multiple record, multiple aliases", + setup: func() []dymnstypes.AliasesOfChainId { + return []dymnstypes.AliasesOfChainId{ + { + ChainId: "dymension_1100-1", + Aliases: []string{"dym", "dymension"}, + }, + { + ChainId: "blumbus_111-1", + Aliases: []string{"bb", "blumbus"}, + }, + } + }, + want: map[string]struct{}{ + "dymension_1100-1": {}, + "dym": {}, + "dymension": {}, + "blumbus_111-1": {}, + "bb": {}, + "blumbus": {}, + }, + }, + } + for _, tt := range tests { + s.Run(tt.name, func() { + s.RefreshContext() + + aliasesOfChainIds := tt.setup() + s.updateModuleParams(func(moduleParams dymnstypes.Params) dymnstypes.Params { + moduleParams.Chains.AliasesOfChainIds = aliasesOfChainIds + return moduleParams + }) + + got := s.dymNsKeeper.GetAllAliasAndChainIdInParams(s.ctx) + if len(tt.want) == 0 { + s.Require().Empty(got) + return + } + + s.Require().Equal(tt.want, got) + }) + } +} + +func (s *KeeperTestSuite) TestKeeper_IsAliasPresentsInParamsAsAliasOrChainId() { + tests := []struct { + name string + preRunFunc func(s *KeeperTestSuite) + alias string + want bool + }{ + { + name: "alias mapped in params", + preRunFunc: func(s *KeeperTestSuite) { + s.updateModuleParams(func(params dymnstypes.Params) dymnstypes.Params { + params.Chains.AliasesOfChainIds = []dymnstypes.AliasesOfChainId{ + { + ChainId: "dymension_100-1", + Aliases: []string{"dym"}, + }, + } + return params + }) + }, + alias: "dym", + want: true, + }, + { + name: "alias as chain-id in params", + preRunFunc: func(s *KeeperTestSuite) { + s.updateModuleParams(func(params dymnstypes.Params) dymnstypes.Params { + params.Chains.AliasesOfChainIds = []dymnstypes.AliasesOfChainId{ + { + ChainId: "dymension", + Aliases: []string{"dym"}, + }, + } + return params + }) + }, + alias: "dymension", + want: true, + }, + { + name: "alias not in params", + preRunFunc: func(s *KeeperTestSuite) { + s.updateModuleParams(func(params dymnstypes.Params) dymnstypes.Params { + params.Chains.AliasesOfChainIds = []dymnstypes.AliasesOfChainId{ + { + ChainId: "dymension", + Aliases: []string{"dym"}, + }, + } + return params + }) + }, + alias: "alias", + want: false, + }, + { + name: "alias is used by RollApp", + preRunFunc: func(s *KeeperTestSuite) { + rollApp := newRollApp("rollapp_1-1").WithAlias("alias") + s.persistRollApp(*rollApp) + + s.requireRollApp(rollApp.rollAppId).HasAlias("alias") + }, + alias: "alias", + want: false, + }, + } + for _, tt := range tests { + s.Run(tt.name, func() { + if tt.preRunFunc != nil { + tt.preRunFunc(s) + } + + got := s.dymNsKeeper.IsAliasPresentsInParamsAsAliasOrChainId(s.ctx, tt.alias) + s.Require().Equal(tt.want, got) + }) + } +} + +func (s *KeeperTestSuite) TestKeeper_SetDefaultAliasForRollApp() { + const rollAppId = "rollapp_1-1" + + const anotherRollAppId = "rollapp_2-2" + const anotherRollAppAlias = "another" + + tests := []struct { + name string + rollAppId string + skipCreateRolApp bool + existingAliases []string + moveAlias string + wantErr bool + wantErrContains string + wantAliases []string + }{ + { + name: "pass - can set", + rollAppId: rollAppId, + existingAliases: []string{"alias", "default"}, + moveAlias: "default", + wantErr: false, + wantAliases: []string{"default", "alias"}, + }, + { + name: "pass - can set among multiple aliases", + rollAppId: rollAppId, + existingAliases: []string{"alias", "default", "of", "rollapp"}, + moveAlias: "default", + wantErr: false, + wantAliases: []string{"default", "alias", "of", "rollapp"}, + }, + { + name: "pass - can set default to default", + rollAppId: rollAppId, + existingAliases: []string{"default", "alias", "here"}, + moveAlias: "default", + wantErr: false, + wantAliases: []string{"default", "alias", "here"}, // unchanged + }, + { + name: "pass - can set default even when only one alias", + rollAppId: rollAppId, + existingAliases: []string{"default"}, + moveAlias: "default", + wantErr: false, + wantAliases: []string{"default"}, + }, + { + name: "fail - reject invalid RollApp ID", + rollAppId: "@@@", + skipCreateRolApp: true, + existingAliases: nil, + moveAlias: "default", + wantErr: true, + wantErrContains: "alias is not linked to the RollApp", + wantAliases: nil, + }, + { + name: "fail - reject invalid input Alias", + rollAppId: rollAppId, + existingAliases: []string{"alias", "default"}, + moveAlias: "@@@", + wantErr: true, + wantErrContains: "alias is not linked to the RollApp", + wantAliases: []string{"alias", "default"}, + }, + { + name: "fail - reject alias belong to another", + rollAppId: rollAppId, + existingAliases: []string{"alias", "default"}, + moveAlias: anotherRollAppAlias, + wantErr: true, + wantErrContains: "alias is not linked to the RollApp", + wantAliases: []string{"alias", "default"}, + }, + { + name: "fail - reject alias that not exists", + rollAppId: rollAppId, + existingAliases: []string{"alias", "default"}, + moveAlias: "void", + wantErr: true, + wantErrContains: "alias is not linked to the RollApp", + wantAliases: []string{"alias", "default"}, + }, + } + for _, tt := range tests { + s.Run(tt.name, func() { + s.RefreshContext() + + if !tt.skipCreateRolApp { + if tt.rollAppId != "" { + ra := newRollApp(tt.rollAppId) + for _, existingAlias := range tt.existingAliases { + ra = ra.WithAlias(existingAlias) + } + s.persistRollApp(*ra) + } else { + s.Require().Empty(tt.existingAliases, "bad setup") + } + + s.persistRollApp( + *newRollApp(anotherRollAppId).WithAlias(anotherRollAppAlias), + ) + } + + err := s.dymNsKeeper.SetDefaultAliasForRollApp(s.ctx, tt.rollAppId, tt.moveAlias) + + defer func() { + if tt.rollAppId == "" || s.T().Failed() { + return + } + aliasesAfter := s.dymNsKeeper.GetAliasesOfRollAppId(s.ctx, tt.rollAppId) + if len(tt.wantAliases) == 0 { + s.Empty(aliasesAfter) + } else { + s.Equal(tt.wantAliases, aliasesAfter) + } + }() + + if tt.wantErr { + s.Require().ErrorContains(err, tt.wantErrContains) + return + } + + s.Require().NoError(err) + }) + } +} + +func (s *KeeperTestSuite) TestKeeper_GetAllRollAppsWithAliases() { + rollApp1 := *newRollApp("rollapp_1-1").WithAlias("al1").WithAlias("al2") + rollApp2 := *newRollApp("rollapp_2-2").WithAlias("al3") + + tests := []struct { + name string + preRunFunc func(s *KeeperTestSuite) + want []dymnstypes.AliasesOfChainId + }{ + { + name: "if empty, returns empty", + preRunFunc: nil, + want: nil, + }, + { + name: "can return RollApp with aliases", + preRunFunc: func(s *KeeperTestSuite) { + s.persistRollApp(rollApp1) + }, + want: []dymnstypes.AliasesOfChainId{ + {ChainId: rollApp1.rollAppId, Aliases: rollApp1.aliases}, + }, + }, + { + name: "can return RollApp with alias", + preRunFunc: func(s *KeeperTestSuite) { + s.persistRollApp(rollApp2) + }, + want: []dymnstypes.AliasesOfChainId{ + {ChainId: rollApp2.rollAppId, Aliases: rollApp2.aliases}, + }, + }, + { + name: "can return all RollApps with aliases", + preRunFunc: func(s *KeeperTestSuite) { + s.persistRollApp(rollApp1) + s.persistRollApp(rollApp2) + }, + want: []dymnstypes.AliasesOfChainId{ + {ChainId: rollApp1.rollAppId, Aliases: rollApp1.aliases}, + {ChainId: rollApp2.rollAppId, Aliases: rollApp2.aliases}, + }, + }, + } + for _, tt := range tests { + s.Run(tt.name, func() { + s.RefreshContext() + + if tt.preRunFunc != nil { + tt.preRunFunc(s) + } + + got := s.dymNsKeeper.GetAllRollAppsWithAliases(s.ctx) + if len(tt.want) == 0 { + s.Require().Empty(got) + return + } + + s.Require().Equal(tt.want, got) + }) + } +} diff --git a/x/dymns/keeper/buy_order.go b/x/dymns/keeper/buy_order.go new file mode 100644 index 000000000..57ab5f2f0 --- /dev/null +++ b/x/dymns/keeper/buy_order.go @@ -0,0 +1,145 @@ +package keeper + +import ( + errorsmod "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" + "github.com/dymensionxyz/gerr-cosmos/gerrc" +) + +// IncreaseBuyOrdersCountAndGet increases the all-time Buy-Order records count and returns the updated value. +func (k Keeper) IncreaseBuyOrdersCountAndGet(ctx sdk.Context) uint64 { + countFromStore := k.GetCountBuyOrders(ctx) + newCount := countFromStore + 1 + + if newCount < countFromStore { + // this case can happen only if all-time total count of Buy-Order records exceeds uint64. + // It's not likely to happens. + panic("overflow") + } + + k.SetCountBuyOrders(ctx, newCount) + + return newCount +} + +// GetCountBuyOrders returns the all-time Buy-Order records count from the KVStore. +// Note: do not use this function. This function should only be used in IncreaseBuyOrdersCountAndGet and test. +func (k Keeper) GetCountBuyOrders(ctx sdk.Context) uint64 { + store := ctx.KVStore(k.storeKey) + bz := store.Get(dymnstypes.KeyCountBuyOrders) + return sdk.BigEndianToUint64(bz) +} + +// SetCountBuyOrders sets the all-time Buy-Order records count into the KVStore. +// Note: do not use this function. This function should only be used in IncreaseBuyOrdersCountAndGet and test. +func (k Keeper) SetCountBuyOrders(ctx sdk.Context, value uint64) { + store := ctx.KVStore(k.storeKey) + store.Set(dymnstypes.KeyCountBuyOrders, sdk.Uint64ToBigEndian(value)) +} + +// GetAllBuyOrders returns all Buy-Order records from the KVStore. +// No filter is applied. +// Store iterator is expensive so this function should be used only in Genesis and for testing purpose. +func (k Keeper) GetAllBuyOrders(ctx sdk.Context) (list []dymnstypes.BuyOrder) { + store := ctx.KVStore(k.storeKey) + + iterator := sdk.KVStorePrefixIterator(store, dymnstypes.KeyPrefixBuyOrder) + defer func() { + _ = iterator.Close() // nolint: errcheck + }() + + for ; iterator.Valid(); iterator.Next() { + var offer dymnstypes.BuyOrder + k.cdc.MustUnmarshal(iterator.Value(), &offer) + list = append(list, offer) + } + + return +} + +// GetBuyOrder retrieves the Buy-Order from the KVStore. +// If the Buy-Order does not exist, nil is returned. +func (k Keeper) GetBuyOrder(ctx sdk.Context, orderId string) *dymnstypes.BuyOrder { + if !dymnstypes.IsValidBuyOrderId(orderId) { + panic("invalid Buy-Order ID") + } + + store := ctx.KVStore(k.storeKey) + offerKey := dymnstypes.BuyOrderKey(orderId) + + bz := store.Get(offerKey) + if bz == nil { + return nil + } + + var offer dymnstypes.BuyOrder + k.cdc.MustUnmarshal(bz, &offer) + + return &offer +} + +// InsertNewBuyOrder assigns ID and insert new Buy-Order record into the KVStore. +// The method will panic if the ID of the Buy-Order is not empty. +func (k Keeper) InsertNewBuyOrder(ctx sdk.Context, buyOrder dymnstypes.BuyOrder) (dymnstypes.BuyOrder, error) { + if buyOrder.Id != "" { + // wrong call, consider use SetBuyOrder instead + panic("ID of the buy order must be empty") + } + + // generate new order ID + + count := k.IncreaseBuyOrdersCountAndGet(ctx) + newOrderId := dymnstypes.CreateBuyOrderId(buyOrder.AssetType, count) + + // check if the ID is already used + + existingRecord := k.GetBuyOrder(ctx, newOrderId) + if existingRecord != nil { + return buyOrder, errorsmod.Wrapf( + gerrc.ErrAlreadyExists, "Buy-Order ID already exists: %s", newOrderId, + ) + } + + // assign the ID to the new record and persist + buyOrder.Id = newOrderId + if err := k.SetBuyOrder(ctx, buyOrder); err != nil { + return buyOrder, err + } + + return buyOrder, nil +} + +// SetBuyOrder stores the Buy-Order into the KVStore. +func (k Keeper) SetBuyOrder(ctx sdk.Context, offer dymnstypes.BuyOrder) error { + if err := offer.Validate(); err != nil { + return err + } + + if len(offer.Params) == 0 { + offer.Params = nil + } + + store := ctx.KVStore(k.storeKey) + offerKey := dymnstypes.BuyOrderKey(offer.Id) + bz := k.cdc.MustMarshal(&offer) + store.Set(offerKey, bz) + + ctx.EventManager().EmitEvent(offer.GetSdkEvent(dymnstypes.AttributeValueBoActionNameSet)) + + return nil +} + +// DeleteBuyOrder deletes the Buy-Order from the KVStore. +func (k Keeper) DeleteBuyOrder(ctx sdk.Context, orderId string) { + offer := k.GetBuyOrder(ctx, orderId) + if offer == nil { + return + } + + store := ctx.KVStore(k.storeKey) + offerKey := dymnstypes.BuyOrderKey(orderId) + store.Delete(offerKey) + + ctx.EventManager().EmitEvent(offer.GetSdkEvent(dymnstypes.AttributeValueBoActionNameDelete)) +} diff --git a/x/dymns/keeper/buy_order_reverse_lookup.go b/x/dymns/keeper/buy_order_reverse_lookup.go new file mode 100644 index 000000000..a6e2afbc5 --- /dev/null +++ b/x/dymns/keeper/buy_order_reverse_lookup.go @@ -0,0 +1,269 @@ +package keeper + +import ( + errorsmod "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" + dymnsutils "github.com/dymensionxyz/dymension/v3/x/dymns/utils" + "github.com/dymensionxyz/gerr-cosmos/gerrc" +) + +// AddReverseMappingBuyerToBuyOrderRecord stores the reverse mapping from buyers to their Buy-Order IDs into the KVStore. +// This reverse mapping should help to find all Buy-Orders that placed by the account address. +// This should be called when a new Buy-Order is created. +func (k Keeper) AddReverseMappingBuyerToBuyOrderRecord(ctx sdk.Context, buyer, orderId string) error { + accAddr, err := sdk.AccAddressFromBech32(buyer) + if err != nil { + return errorsmod.Wrapf(gerrc.ErrInvalidArgument, "invalid buyer address: %s", buyer) + } + + if !dymnstypes.IsValidBuyOrderId(orderId) { + return errorsmod.Wrapf(gerrc.ErrInvalidArgument, "invalid Buy-Order ID: %s", orderId) + } + + key := dymnstypes.BuyerToOrderIdsRvlKey(accAddr) + + return k.GenericAddReverseLookupBuyOrderIdsRecord(ctx, key, orderId) +} + +// GetBuyOrdersByBuyer returns all Buy-Orders placed by the account address, +// by taking advantage of the reverse mapping from buyer to Buy-Order IDs. +func (k Keeper) GetBuyOrdersByBuyer( + ctx sdk.Context, buyer string, +) ([]dymnstypes.BuyOrder, error) { + accAddr, err := sdk.AccAddressFromBech32(buyer) + if err != nil { + return nil, errorsmod.Wrapf(gerrc.ErrInvalidArgument, "invalid buyer address: %s", buyer) + } + + // load the reverse mapping record + + key := dymnstypes.BuyerToOrderIdsRvlKey(accAddr) + existingOrderIds := k.GenericGetReverseLookupBuyOrderIdsRecord(ctx, key) + + // load the buy orders follow the order IDs from the reverse mapping + + var buyOrders []dymnstypes.BuyOrder + for _, orderId := range existingOrderIds.OrderIds { + buyOrder := k.GetBuyOrder(ctx, orderId) + + // invalid records will be skipped + + if buyOrder == nil { + // Buy Order not found, skip + k.Logger(ctx).Error( + "buy-order in the reverse-lookup could not be found.", + "buyer", buyer, "order-id", orderId, + "method", "GetBuyOrdersByBuyer", + ) + continue + } + + if buyOrder.Buyer != buyer { + // buyer of the Buy Order mismatch, skip + k.Logger(ctx).Error( + "buy-order in the reverse-lookup has different buyer.", + "input-buyer", buyer, "buy-order-buyer", buyOrder.Buyer, "order-id", orderId, + "method", "GetBuyOrdersByBuyer", + ) + continue + } + + buyOrders = append(buyOrders, *buyOrder) + } + + return buyOrders, nil +} + +// RemoveReverseMappingBuyerToBuyOrder removes a reverse mapping from buyer to a Buy-Order ID. +// This should be called after the Buy-Order is removed. +func (k Keeper) RemoveReverseMappingBuyerToBuyOrder(ctx sdk.Context, buyer, orderId string) error { + accAddr, err := sdk.AccAddressFromBech32(buyer) + if err != nil { + return errorsmod.Wrapf(gerrc.ErrInvalidArgument, "invalid buyer address: %s", buyer) + } + + if !dymnstypes.IsValidBuyOrderId(orderId) { + return errorsmod.Wrapf(gerrc.ErrInvalidArgument, "invalid Buy-Order ID: %s", orderId) + } + + key := dymnstypes.BuyerToOrderIdsRvlKey(accAddr) + + return k.GenericRemoveReverseLookupBuyOrderIdRecord(ctx, key, orderId) +} + +// AddReverseMappingAssetIdToBuyOrder add a reverse mapping from Dym-Name/Alias to the Buy-Order ID which placed for it. +// This reverse mapping should help to find all Buy-Orders that placed for the assets. +// This should be called when a new Buy-Order is created. +func (k Keeper) AddReverseMappingAssetIdToBuyOrder(ctx sdk.Context, assetId string, assetType dymnstypes.AssetType, orderId string) error { + var key []byte + + switch assetType { + case dymnstypes.TypeName: + if !dymnsutils.IsValidDymName(assetId) { + return errorsmod.Wrapf(gerrc.ErrInvalidArgument, "invalid Dym-Name: %s", assetId) + } + key = dymnstypes.DymNameToBuyOrderIdsRvlKey(assetId) + case dymnstypes.TypeAlias: + if !dymnsutils.IsValidAlias(assetId) { + return errorsmod.Wrapf(gerrc.ErrInvalidArgument, "invalid Alias: %s", assetId) + } + key = dymnstypes.AliasToBuyOrderIdsRvlKey(assetId) + default: + return errorsmod.Wrapf(gerrc.ErrInvalidArgument, "invalid asset type: %s", assetType) + } + + if !dymnstypes.IsValidBuyOrderId(orderId) { + return errorsmod.Wrapf(gerrc.ErrInvalidArgument, "invalid Buy-Order ID: %s", orderId) + } + + return k.GenericAddReverseLookupBuyOrderIdsRecord(ctx, key, orderId) +} + +// GetBuyOrdersOfDymName returns all Buy-Orders that placed for the Dym-Name +// by taking advantage of the reverse mapping from asset to Buy-Order IDs. +func (k Keeper) GetBuyOrdersOfDymName( + ctx sdk.Context, name string, +) ([]dymnstypes.BuyOrder, error) { + if !dymnsutils.IsValidDymName(name) { + return nil, errorsmod.Wrapf(gerrc.ErrInvalidArgument, "invalid Dym-Name: %s", name) + } + + // load the reverse mapping record + + key := dymnstypes.DymNameToBuyOrderIdsRvlKey(name) + orderIds := k.GenericGetReverseLookupBuyOrderIdsRecord(ctx, key) + + // load the buy orders follow the order IDs from the reverse mapping + + var buyOrders []dymnstypes.BuyOrder + for _, orderId := range orderIds.OrderIds { + bo := k.GetBuyOrder(ctx, orderId) + + if bo == nil { + // not found, skip + continue + } + + buyOrders = append(buyOrders, *bo) + } + + return buyOrders, nil +} + +// GetBuyOrdersOfAlias returns all Buy-Orders that placed for the Alias +// by taking advantage of the reverse mapping from asset to Buy-Order IDs. +func (k Keeper) GetBuyOrdersOfAlias( + ctx sdk.Context, alias string, +) ([]dymnstypes.BuyOrder, error) { + if !dymnsutils.IsValidAlias(alias) { + return nil, errorsmod.Wrapf(gerrc.ErrInvalidArgument, "invalid Alias: %s", alias) + } + + // load the reverse mapping record + + key := dymnstypes.AliasToBuyOrderIdsRvlKey(alias) + orderIds := k.GenericGetReverseLookupBuyOrderIdsRecord(ctx, key) + + // load the buy orders follow the order IDs from the reverse mapping + + var buyOrders []dymnstypes.BuyOrder + for _, orderId := range orderIds.OrderIds { + buyOrder := k.GetBuyOrder(ctx, orderId) + if buyOrder == nil { + // not found, skip + continue + } + buyOrders = append(buyOrders, *buyOrder) + } + + return buyOrders, nil +} + +// RemoveReverseMappingAssetIdToBuyOrder removes reverse mapping from Dym-Name/Alias to Buy-Order which placed for it. +// This should be called after the Buy-Order is removed. +func (k Keeper) RemoveReverseMappingAssetIdToBuyOrder(ctx sdk.Context, assetId string, assetType dymnstypes.AssetType, orderId string) error { + var key []byte + + if !dymnstypes.IsValidBuyOrderId(orderId) { + return errorsmod.Wrapf(gerrc.ErrInvalidArgument, "invalid Buy-Order ID: %s", orderId) + } + + // determine the key based on the asset type + + switch assetType { + case dymnstypes.TypeName: + if !dymnsutils.IsValidDymName(assetId) { + return errorsmod.Wrapf(gerrc.ErrInvalidArgument, "invalid Dym-Name: %s", assetId) + } + key = dymnstypes.DymNameToBuyOrderIdsRvlKey(assetId) + case dymnstypes.TypeAlias: + if !dymnsutils.IsValidAlias(assetId) { + return errorsmod.Wrapf(gerrc.ErrInvalidArgument, "invalid Alias: %s", assetId) + } + key = dymnstypes.AliasToBuyOrderIdsRvlKey(assetId) + default: + return errorsmod.Wrapf(gerrc.ErrInvalidArgument, "invalid asset type: %s", assetType) + } + + // remove the record + + return k.GenericRemoveReverseLookupBuyOrderIdRecord(ctx, key, orderId) +} + +// GenericAddReverseLookupBuyOrderIdsRecord is a utility method that help to add a reverse lookup record for Buy-Order ID. +func (k Keeper) GenericAddReverseLookupBuyOrderIdsRecord(ctx sdk.Context, key []byte, orderId string) error { + return k.GenericAddReverseLookupRecord( + ctx, + key, orderId, + func(list []string) []byte { + record := dymnstypes.ReverseLookupBuyOrderIds{ + OrderIds: list, + } + return k.cdc.MustMarshal(&record) + }, + func(bz []byte) []string { + var record dymnstypes.ReverseLookupBuyOrderIds + k.cdc.MustUnmarshal(bz, &record) + return record.OrderIds + }, + ) +} + +// GenericGetReverseLookupBuyOrderIdsRecord is a utility method that help to get a reverse lookup record for Buy-Order IDs. +func (k Keeper) GenericGetReverseLookupBuyOrderIdsRecord( + ctx sdk.Context, key []byte, +) dymnstypes.ReverseLookupBuyOrderIds { + dymNames := k.GenericGetReverseLookupRecord( + ctx, + key, + func(bz []byte) []string { + var record dymnstypes.ReverseLookupBuyOrderIds + k.cdc.MustUnmarshal(bz, &record) + return record.OrderIds + }, + ) + + return dymnstypes.ReverseLookupBuyOrderIds{ + OrderIds: dymNames, + } +} + +// GenericRemoveReverseLookupBuyOrderIdRecord is a utility method that help to remove a reverse lookup record for Buy-Order ID. +func (k Keeper) GenericRemoveReverseLookupBuyOrderIdRecord(ctx sdk.Context, key []byte, orderId string) error { + return k.GenericRemoveReverseLookupRecord( + ctx, + key, orderId, + func(list []string) []byte { + record := dymnstypes.ReverseLookupBuyOrderIds{ + OrderIds: list, + } + return k.cdc.MustMarshal(&record) + }, + func(bz []byte) []string { + var record dymnstypes.ReverseLookupBuyOrderIds + k.cdc.MustUnmarshal(bz, &record) + return record.OrderIds + }, + ) +} diff --git a/x/dymns/keeper/buy_order_reverse_lookup_test.go b/x/dymns/keeper/buy_order_reverse_lookup_test.go new file mode 100644 index 000000000..f6d5cde84 --- /dev/null +++ b/x/dymns/keeper/buy_order_reverse_lookup_test.go @@ -0,0 +1,739 @@ +package keeper_test + +import ( + "time" + + "golang.org/x/exp/slices" + + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" +) + +func (s *KeeperTestSuite) TestKeeper_GetAddReverseMappingBuyerToPlacedBuyOrder() { + buyer1a := testAddr(1).bech32() + buyer2a := testAddr(2).bech32() + buyer3a := testAddr(3).bech32() + someoneA := testAddr(4).bech32() + + s.Require().Error( + s.dymNsKeeper.AddReverseMappingBuyerToBuyOrderRecord(s.ctx, "0x", "101"), + "should not allow invalid buyer address", + ) + + s.Require().Error( + s.dymNsKeeper.AddReverseMappingBuyerToBuyOrderRecord(s.ctx, buyer1a, "@"), + "should not allow invalid offer ID", + ) + + _, err := s.dymNsKeeper.GetBuyOrdersByBuyer(s.ctx, "0x") + s.Require().Error(err, "should not allow invalid buyer address") + + offer1 := dymnstypes.BuyOrder{ + Id: "101", + AssetId: "a", + AssetType: dymnstypes.TypeName, + Buyer: buyer1a, + OfferPrice: s.coin(1), + CounterpartyOfferPrice: nil, + } + s.Require().NoError(s.dymNsKeeper.SetBuyOrder(s.ctx, offer1)) + err = s.dymNsKeeper.AddReverseMappingBuyerToBuyOrderRecord(s.ctx, buyer1a, offer1.Id) + s.Require().NoError(err) + + offer2 := dymnstypes.BuyOrder{ + Id: "202", + AssetId: "alias", + AssetType: dymnstypes.TypeAlias, + Params: []string{"rollapp_1-1"}, + Buyer: buyer2a, + OfferPrice: s.coin(1), + CounterpartyOfferPrice: nil, + } + s.Require().NoError(s.dymNsKeeper.SetBuyOrder(s.ctx, offer2)) + err = s.dymNsKeeper.AddReverseMappingBuyerToBuyOrderRecord(s.ctx, buyer2a, offer2.Id) + s.Require().NoError(err) + + offer3 := dymnstypes.BuyOrder{ + Id: "103", + AssetId: "c", + AssetType: dymnstypes.TypeName, + Buyer: buyer2a, + OfferPrice: s.coin(1), + CounterpartyOfferPrice: nil, + } + s.Require().NoError(s.dymNsKeeper.SetBuyOrder(s.ctx, offer3)) + err = s.dymNsKeeper.AddReverseMappingBuyerToBuyOrderRecord(s.ctx, buyer2a, offer3.Id) + s.Require().NoError(err) + + offer4 := dymnstypes.BuyOrder{ + Id: "204", + AssetId: "salas", + AssetType: dymnstypes.TypeAlias, + Params: []string{"rollapp_2-2"}, + Buyer: buyer3a, + OfferPrice: s.coin(1), + CounterpartyOfferPrice: nil, + } + s.Require().NoError(s.dymNsKeeper.SetBuyOrder(s.ctx, offer4)) + err = s.dymNsKeeper.AddReverseMappingBuyerToBuyOrderRecord(s.ctx, buyer3a, offer4.Id) + s.Require().NoError(err) + + s.Require().NoError( + s.dymNsKeeper.AddReverseMappingBuyerToBuyOrderRecord(s.ctx, buyer2a, "103721461"), + "no check non-existing offer record", + ) + + s.Run("no error if duplicated ID", func() { + for i := 0; i < 3; i++ { + s.Require().NoError(s.dymNsKeeper.AddReverseMappingBuyerToBuyOrderRecord(s.ctx, buyer2a, offer2.Id)) + } + }) + + placedBy1, err1 := s.dymNsKeeper.GetBuyOrdersByBuyer(s.ctx, buyer1a) + s.Require().NoError(err1) + s.Require().Len(placedBy1, 1) + + placedBy2, err2 := s.dymNsKeeper.GetBuyOrdersByBuyer(s.ctx, buyer2a) + s.Require().NoError(err2) + s.Require().NotEqual(3, len(placedBy2), "should not include non-existing offers") + s.Require().Len(placedBy2, 2) + + placedBy3, err3 := s.dymNsKeeper.GetBuyOrdersByBuyer(s.ctx, buyer3a) + s.Require().NoError(err3) + s.Require().Len(placedBy3, 1) + + placedByNonExists, err3 := s.dymNsKeeper.GetDymNamesOwnedBy(s.ctx, someoneA) + s.Require().NoError(err3) + s.Require().Len(placedByNonExists, 0) + + s.Require().NoError( + s.dymNsKeeper.AddReverseMappingBuyerToBuyOrderRecord(s.ctx, buyer2a, offer1.Id), + "no error if offer placed by another buyer", + ) + s.Require().NoError( + s.dymNsKeeper.AddReverseMappingBuyerToBuyOrderRecord(s.ctx, buyer2a, offer4.Id), + "no error if offer placed by another buyer", + ) + placedBy2, err2 = s.dymNsKeeper.GetBuyOrdersByBuyer(s.ctx, buyer2a) + s.Require().NoError(err2) + s.Require().Len(placedBy2, 2, "should not include offers placed by another buyer") +} + +func (s *KeeperTestSuite) TestKeeper_RemoveReverseMappingBuyerToPlacedBuyOrder() { + buyerA := testAddr(1).bech32() + someoneA := testAddr(2).bech32() + + s.Require().Error( + s.dymNsKeeper.RemoveReverseMappingBuyerToBuyOrder(s.ctx, "0x", "101"), + "should not allow invalid buyer address", + ) + + s.Require().Error( + s.dymNsKeeper.RemoveReverseMappingBuyerToBuyOrder(s.ctx, buyerA, "@"), + "should not allow invalid offer ID", + ) + + offer1 := dymnstypes.BuyOrder{ + Id: "101", + AssetId: "my-name", + AssetType: dymnstypes.TypeName, + Buyer: buyerA, + OfferPrice: s.coin(1), + CounterpartyOfferPrice: nil, + } + s.Require().NoError(s.dymNsKeeper.SetBuyOrder(s.ctx, offer1)) + s.Require().NoError(s.dymNsKeeper.AddReverseMappingBuyerToBuyOrderRecord(s.ctx, buyerA, offer1.Id)) + + offer2 := dymnstypes.BuyOrder{ + Id: "202", + AssetId: "alias", + AssetType: dymnstypes.TypeAlias, + Params: []string{"rollapp_1-1"}, + Buyer: buyerA, + OfferPrice: s.coin(1), + CounterpartyOfferPrice: nil, + } + s.Require().NoError(s.dymNsKeeper.SetBuyOrder(s.ctx, offer2)) + s.Require().NoError(s.dymNsKeeper.AddReverseMappingBuyerToBuyOrderRecord(s.ctx, buyerA, offer2.Id)) + + s.Require().NoError( + s.dymNsKeeper.RemoveReverseMappingBuyerToBuyOrder(s.ctx, someoneA, offer1.Id), + "no error if buyer non-exists", + ) + + placedByBuyer, err := s.dymNsKeeper.GetBuyOrdersByBuyer(s.ctx, buyerA) + s.Require().NoError(err) + s.Require().Len(placedByBuyer, 2, "existing data must be kept") + + s.Require().NoError( + s.dymNsKeeper.RemoveReverseMappingBuyerToBuyOrder(s.ctx, buyerA, "10138132187"), + "no error if not placed order", + ) + + placedByBuyer, err = s.dymNsKeeper.GetBuyOrdersByBuyer(s.ctx, buyerA) + s.Require().NoError(err) + s.Require().Len(placedByBuyer, 2, "existing data must be kept") + + s.Require().NoError(s.dymNsKeeper.RemoveReverseMappingBuyerToBuyOrder(s.ctx, buyerA, offer1.Id)) + placedByBuyer, err = s.dymNsKeeper.GetBuyOrdersByBuyer(s.ctx, buyerA) + s.Require().NoError(err) + s.Require().Len(placedByBuyer, 1) + s.Require().Equal(offer2.Id, placedByBuyer[0].Id) + + s.Require().NoError(s.dymNsKeeper.RemoveReverseMappingBuyerToBuyOrder(s.ctx, buyerA, offer2.Id)) + placedByBuyer, err = s.dymNsKeeper.GetBuyOrdersByBuyer(s.ctx, buyerA) + s.Require().NoError(err) + s.Require().Len(placedByBuyer, 0) +} + +func (s *KeeperTestSuite) TestKeeper_AddReverseMappingAssetIdToBuyOrder_Generic() { + supportedAssetTypes := []dymnstypes.AssetType{ + dymnstypes.TypeName, dymnstypes.TypeAlias, + } + + s.Run("pass - can add", func() { + s.RefreshContext() + + const assetId = "asset" + + for _, assetType := range supportedAssetTypes { + err := s.dymNsKeeper.AddReverseMappingAssetIdToBuyOrder(s.ctx, assetId, assetType, dymnstypes.CreateBuyOrderId(assetType, 1)) + s.Require().NoError(err) + } + + s.Require().NotEmpty(s.dymNsKeeper.GenericGetReverseLookupBuyOrderIdsRecord(s.ctx, dymnstypes.DymNameToBuyOrderIdsRvlKey(assetId)).OrderIds) + s.Require().NotEmpty(s.dymNsKeeper.GenericGetReverseLookupBuyOrderIdsRecord(s.ctx, dymnstypes.AliasToBuyOrderIdsRvlKey(assetId)).OrderIds) + }) + + s.Run("pass - can add without collision across asset types", func() { + s.RefreshContext() + + const assetId = "asset" + + for _, assetType := range supportedAssetTypes { + err := s.dymNsKeeper.AddReverseMappingAssetIdToBuyOrder(s.ctx, assetId, assetType, dymnstypes.CreateBuyOrderId(assetType, 1)) + s.Require().NoError(err) + } + + err := s.dymNsKeeper.AddReverseMappingAssetIdToBuyOrder(s.ctx, assetId, dymnstypes.TypeName, dymnstypes.CreateBuyOrderId(dymnstypes.TypeName, 2)) + s.Require().NoError(err) + + err = s.dymNsKeeper.AddReverseMappingAssetIdToBuyOrder(s.ctx, assetId, dymnstypes.TypeAlias, dymnstypes.CreateBuyOrderId(dymnstypes.TypeAlias, 3)) + s.Require().NoError(err) + + nameOffers := s.dymNsKeeper.GenericGetReverseLookupBuyOrderIdsRecord(s.ctx, dymnstypes.DymNameToBuyOrderIdsRvlKey(assetId)) + s.Require().Len(nameOffers.OrderIds, 2) + aliasOffers := s.dymNsKeeper.GenericGetReverseLookupBuyOrderIdsRecord(s.ctx, dymnstypes.AliasToBuyOrderIdsRvlKey(assetId)) + s.Require().Len(aliasOffers.OrderIds, 2) + + s.Require().NotEqual(nameOffers.OrderIds, aliasOffers.OrderIds, "data must be persisted separately") + + s.Require().Equal(dymnstypes.CreateBuyOrderId(dymnstypes.TypeName, 1), nameOffers.OrderIds[0]) + s.Require().Equal(dymnstypes.CreateBuyOrderId(dymnstypes.TypeName, 2), nameOffers.OrderIds[1]) + s.Require().Equal(dymnstypes.CreateBuyOrderId(dymnstypes.TypeAlias, 1), aliasOffers.OrderIds[0]) + s.Require().Equal(dymnstypes.CreateBuyOrderId(dymnstypes.TypeAlias, 3), aliasOffers.OrderIds[1]) + }) + + s.Run("fail - should reject invalid offer id", func() { + s.RefreshContext() + + for _, assetType := range supportedAssetTypes { + s.Require().ErrorContains(s.dymNsKeeper.AddReverseMappingAssetIdToBuyOrder(s.ctx, "asset", assetType, "@"), + "invalid Buy-Order ID", + ) + } + }) + + s.Run("fail - should reject invalid asset id", func() { + s.RefreshContext() + + for _, assetType := range supportedAssetTypes { + var wantErrContains string + switch assetType { + case dymnstypes.TypeName: + wantErrContains = "invalid Dym-Name" + case dymnstypes.TypeAlias: + wantErrContains = "invalid Alias" + default: + s.T().Fatalf("unsupported asset type: %s", assetType) + } + s.Require().ErrorContains( + s.dymNsKeeper.AddReverseMappingAssetIdToBuyOrder(s.ctx, "@", assetType, dymnstypes.CreateBuyOrderId(assetType, 1)), + wantErrContains, + ) + } + }) + + s.Run("fail - should reject invalid asset type", func() { + s.RefreshContext() + + s.Require().ErrorContains( + s.dymNsKeeper.AddReverseMappingAssetIdToBuyOrder(s.ctx, "@", dymnstypes.AssetType_AT_UNKNOWN, "101"), + "invalid asset type", + ) + + for i := int32(0); i < 99; i++ { + assetType := dymnstypes.AssetType(i) + + if slices.Contains(supportedAssetTypes, dymnstypes.AssetType(i)) { + continue + } + + s.Require().ErrorContains( + s.dymNsKeeper.AddReverseMappingAssetIdToBuyOrder(s.ctx, "@", assetType, "101"), + "invalid asset type", + ) + } + }) +} + +func (s *KeeperTestSuite) TestKeeper_GetAddReverseMappingAssetIdToBuyOrder_Type_DymName() { + _, err := s.dymNsKeeper.GetBuyOrdersOfDymName(s.ctx, "@") + s.Require().Error( + err, + "fail - should reject invalid Dym-Name", + ) + + ownerA := testAddr(1).bech32() + buyerA := testAddr(2).bech32() + + dymName1 := dymnstypes.DymName{ + Name: "a", + Owner: ownerA, + Controller: ownerA, + ExpireAt: time.Now().UTC().Add(time.Hour).Unix(), + } + s.Require().NoError(s.dymNsKeeper.SetDymName(s.ctx, dymName1)) + + offer11 := dymnstypes.BuyOrder{ + Id: "1011", + AssetId: dymName1.Name, + AssetType: dymnstypes.TypeName, + Buyer: buyerA, + OfferPrice: s.coin(1), + } + s.Require().NoError(s.dymNsKeeper.SetBuyOrder(s.ctx, offer11)) + + offer12 := dymnstypes.BuyOrder{ + Id: "1012", + AssetId: dymName1.Name, + AssetType: dymnstypes.TypeName, + Buyer: buyerA, + OfferPrice: s.coin(1), + } + s.Require().NoError(s.dymNsKeeper.SetBuyOrder(s.ctx, offer12)) + + s.Require().NoError(s.dymNsKeeper.AddReverseMappingAssetIdToBuyOrder(s.ctx, dymName1.Name, dymnstypes.TypeName, offer11.Id)) + + s.Require().NoError(s.dymNsKeeper.AddReverseMappingAssetIdToBuyOrder(s.ctx, dymName1.Name, dymnstypes.TypeName, offer12.Id)) + + dymName2 := dymnstypes.DymName{ + Name: "b", + Owner: ownerA, + Controller: ownerA, + ExpireAt: time.Now().UTC().Add(time.Hour).Unix(), + } + s.Require().NoError(s.dymNsKeeper.SetDymName(s.ctx, dymName2)) + + offer2 := dymnstypes.BuyOrder{ + Id: "102", + AssetId: dymName2.Name, + AssetType: dymnstypes.TypeName, + Buyer: buyerA, + OfferPrice: s.coin(1), + } + s.Require().NoError(s.dymNsKeeper.SetBuyOrder(s.ctx, offer2)) + + s.Require().NoError(s.dymNsKeeper.AddReverseMappingAssetIdToBuyOrder(s.ctx, dymName2.Name, dymnstypes.TypeName, offer2.Id)) + + s.Require().NoError( + s.dymNsKeeper.AddReverseMappingAssetIdToBuyOrder(s.ctx, dymName1.Name, dymnstypes.TypeName, "1012356215631"), + "no check non-existing offer id", + ) + + s.Run("no error if duplicated name", func() { + for i := 0; i < 3; i++ { + s.Require().NoError(s.dymNsKeeper.AddReverseMappingAssetIdToBuyOrder(s.ctx, dymName2.Name, dymnstypes.TypeName, offer2.Id)) + } + }) + + linked1, err1 := s.dymNsKeeper.GetBuyOrdersOfDymName(s.ctx, dymName1.Name) + s.Require().NoError(err1) + s.Require().Len(linked1, 2) + s.Require().Equal(offer11.Id, linked1[0].Id) + s.Require().Equal(offer12.Id, linked1[1].Id) + + linked2, err2 := s.dymNsKeeper.GetBuyOrdersOfDymName(s.ctx, dymName2.Name) + s.Require().NoError(err2) + s.Require().NotEqual(2, len(linked2), "should not include non-existing offers") + s.Require().Len(linked2, 1) + s.Require().Equal(offer2.Id, linked2[0].Id) + + linkedByNotExists, err3 := s.dymNsKeeper.GetBuyOrdersOfDymName(s.ctx, "non-exists") + s.Require().NoError(err3) + s.Require().Len(linkedByNotExists, 0) + + s.Run("should be linked regardless of expired Dym-Name", func() { + dymName1.ExpireAt = time.Now().UTC().Add(-time.Hour).Unix() + s.Require().NoError(s.dymNsKeeper.SetDymName(s.ctx, dymName1)) + + linked1, err1 = s.dymNsKeeper.GetBuyOrdersOfDymName(s.ctx, dymName1.Name) + s.Require().NoError(err1) + s.Require().Len(linked1, 2) + s.Require().Equal(offer11.Id, linked1[0].Id) + s.Require().Equal(offer12.Id, linked1[1].Id) + }) +} + +func (s *KeeperTestSuite) TestKeeper_GetAddReverseMappingAssetIdToBuyOrder_Type_Alias() { + _, err := s.dymNsKeeper.GetBuyOrdersOfAlias(s.ctx, "@") + s.Require().Error( + err, + "fail - should reject invalid Alias", + ) + + const alias1 = "one" + + const dstRollAppId = "rollapp_3-2" + + buyerA := testAddr(1).bech32() + + offer11 := dymnstypes.BuyOrder{ + Id: dymnstypes.CreateBuyOrderId(dymnstypes.TypeAlias, 11), + AssetId: alias1, + AssetType: dymnstypes.TypeAlias, + Params: []string{dstRollAppId}, + Buyer: buyerA, + OfferPrice: s.coin(1), + } + s.Require().NoError(s.dymNsKeeper.SetBuyOrder(s.ctx, offer11)) + + offer12 := dymnstypes.BuyOrder{ + Id: dymnstypes.CreateBuyOrderId(dymnstypes.TypeAlias, 12), + AssetId: alias1, + AssetType: dymnstypes.TypeAlias, + Params: []string{dstRollAppId}, + Buyer: buyerA, + OfferPrice: s.coin(1), + } + s.Require().NoError(s.dymNsKeeper.SetBuyOrder(s.ctx, offer12)) + + s.Require().NoError(s.dymNsKeeper.AddReverseMappingAssetIdToBuyOrder(s.ctx, alias1, dymnstypes.TypeAlias, offer11.Id)) + + s.Require().NoError(s.dymNsKeeper.AddReverseMappingAssetIdToBuyOrder(s.ctx, alias1, dymnstypes.TypeAlias, offer12.Id)) + + const alias2 = "two" + + offer2 := dymnstypes.BuyOrder{ + Id: dymnstypes.CreateBuyOrderId(dymnstypes.TypeAlias, 2), + AssetId: alias2, + AssetType: dymnstypes.TypeAlias, + Params: []string{dstRollAppId}, + Buyer: buyerA, + OfferPrice: s.coin(1), + } + s.Require().NoError(s.dymNsKeeper.SetBuyOrder(s.ctx, offer2)) + + s.Require().NoError(s.dymNsKeeper.AddReverseMappingAssetIdToBuyOrder(s.ctx, alias2, dymnstypes.TypeAlias, offer2.Id)) + + s.Require().NoError( + s.dymNsKeeper.AddReverseMappingAssetIdToBuyOrder(s.ctx, alias2, dymnstypes.TypeAlias, "2012356215631"), + "no check non-existing offer id", + ) + + s.Run("no error if duplicated name", func() { + for i := 0; i < 3; i++ { + s.Require().NoError(s.dymNsKeeper.AddReverseMappingAssetIdToBuyOrder(s.ctx, alias2, dymnstypes.TypeAlias, offer2.Id)) + } + }) + + linked1, err1 := s.dymNsKeeper.GetBuyOrdersOfAlias(s.ctx, alias1) + s.Require().NoError(err1) + s.Require().Len(linked1, 2) + s.Require().Equal(offer11.Id, linked1[0].Id) + s.Require().Equal(offer12.Id, linked1[1].Id) + + linked2, err2 := s.dymNsKeeper.GetBuyOrdersOfAlias(s.ctx, alias2) + s.Require().NoError(err2) + s.Require().NotEqual(2, len(linked2), "should not include non-existing offers") + s.Require().Len(linked2, 1) + s.Require().Equal(offer2.Id, linked2[0].Id) + + linkedByNotExists, err3 := s.dymNsKeeper.GetBuyOrdersOfAlias(s.ctx, "nah") + s.Require().NoError(err3) + s.Require().Len(linkedByNotExists, 0) +} + +func (s *KeeperTestSuite) TestKeeper_RemoveReverseMappingAssetIdToBuyOrder_Generic() { + supportedAssetTypes := []dymnstypes.AssetType{ + dymnstypes.TypeName, dymnstypes.TypeAlias, + } + + s.Run("pass - can remove", func() { + s.RefreshContext() + + for _, assetType := range supportedAssetTypes { + err := s.dymNsKeeper.AddReverseMappingAssetIdToBuyOrder(s.ctx, "asset", assetType, dymnstypes.CreateBuyOrderId(assetType, 1)) + s.Require().NoError(err) + } + + for _, assetType := range supportedAssetTypes { + err := s.dymNsKeeper.RemoveReverseMappingAssetIdToBuyOrder(s.ctx, "asset", assetType, dymnstypes.CreateBuyOrderId(assetType, 1)) + s.Require().NoError(err) + } + }) + + s.Run("pass - can remove of non-exists", func() { + s.RefreshContext() + + for _, assetType := range supportedAssetTypes { + err := s.dymNsKeeper.RemoveReverseMappingAssetIdToBuyOrder(s.ctx, "asset", assetType, dymnstypes.CreateBuyOrderId(assetType, 1)) + s.Require().NoError(err) + } + }) + + s.Run("pass - can remove without collision across asset types", func() { + s.RefreshContext() + + const assetId = "asset" + + for _, assetType := range supportedAssetTypes { + err := s.dymNsKeeper.AddReverseMappingAssetIdToBuyOrder(s.ctx, assetId, assetType, dymnstypes.CreateBuyOrderId(assetType, 1)) + s.Require().NoError(err) + } + + err := s.dymNsKeeper.RemoveReverseMappingAssetIdToBuyOrder(s.ctx, assetId, dymnstypes.TypeName, dymnstypes.CreateBuyOrderId(dymnstypes.TypeName, 1)) + s.Require().NoError(err) + + s.Require().Empty(s.dymNsKeeper.GenericGetReverseLookupBuyOrderIdsRecord(s.ctx, dymnstypes.DymNameToBuyOrderIdsRvlKey(assetId)).OrderIds) + s.Require().NotEmpty(s.dymNsKeeper.GenericGetReverseLookupBuyOrderIdsRecord(s.ctx, dymnstypes.AliasToBuyOrderIdsRvlKey(assetId)).OrderIds) + }) + + s.Run("fail - should reject invalid offer id", func() { + s.RefreshContext() + + for _, assetType := range supportedAssetTypes { + s.Require().ErrorContains( + s.dymNsKeeper.RemoveReverseMappingAssetIdToBuyOrder(s.ctx, "asset", assetType, "@"), + "invalid Buy-Order ID", + ) + } + }) + + s.Run("fail - should reject invalid asset id", func() { + s.RefreshContext() + + for _, assetType := range supportedAssetTypes { + var wantErrContains string + switch assetType { + case dymnstypes.TypeName: + wantErrContains = "invalid Dym-Name" + case dymnstypes.TypeAlias: + wantErrContains = "invalid Alias" + default: + s.T().Fatalf("unsupported asset type: %s", assetType) + } + s.Require().ErrorContains( + s.dymNsKeeper.RemoveReverseMappingAssetIdToBuyOrder(s.ctx, "@", assetType, dymnstypes.CreateBuyOrderId(assetType, 1)), + wantErrContains, + ) + } + }) + + s.Run("fail - should reject invalid asset type", func() { + s.RefreshContext() + + s.Require().ErrorContains( + s.dymNsKeeper.RemoveReverseMappingAssetIdToBuyOrder(s.ctx, "@", dymnstypes.AssetType_AT_UNKNOWN, "101"), + "invalid asset type", + ) + + for i := int32(0); i < 99; i++ { + assetType := dymnstypes.AssetType(i) + + if slices.Contains(supportedAssetTypes, dymnstypes.AssetType(i)) { + continue + } + + s.Require().ErrorContains( + s.dymNsKeeper.RemoveReverseMappingAssetIdToBuyOrder(s.ctx, "@", assetType, "101"), + "invalid asset type", + ) + } + }) +} + +func (s *KeeperTestSuite) TestKeeper_RemoveReverseMappingAssetIdToBuyOrder_Type_DymName() { + ownerA := testAddr(1).bech32() + buyerA := testAddr(2).bech32() + + dymName1 := dymnstypes.DymName{ + Name: "a", + Owner: ownerA, + Controller: ownerA, + ExpireAt: time.Now().UTC().Add(time.Hour).Unix(), + } + s.Require().NoError(s.dymNsKeeper.SetDymName(s.ctx, dymName1)) + + offer11 := dymnstypes.BuyOrder{ + Id: "1011", + AssetId: dymName1.Name, + AssetType: dymnstypes.TypeName, + Buyer: buyerA, + OfferPrice: s.coin(1), + } + s.Require().NoError(s.dymNsKeeper.SetBuyOrder(s.ctx, offer11)) + + offer12 := dymnstypes.BuyOrder{ + Id: "1012", + AssetId: dymName1.Name, + AssetType: dymnstypes.TypeName, + Buyer: buyerA, + OfferPrice: s.coin(1), + } + s.Require().NoError(s.dymNsKeeper.SetBuyOrder(s.ctx, offer12)) + + s.Require().NoError(s.dymNsKeeper.AddReverseMappingAssetIdToBuyOrder(s.ctx, dymName1.Name, dymnstypes.TypeName, offer11.Id)) + + s.Require().NoError(s.dymNsKeeper.AddReverseMappingAssetIdToBuyOrder(s.ctx, dymName1.Name, dymnstypes.TypeName, offer12.Id)) + + dymName2 := dymnstypes.DymName{ + Name: "b", + Owner: ownerA, + Controller: ownerA, + ExpireAt: time.Now().UTC().Add(time.Hour).Unix(), + } + s.Require().NoError(s.dymNsKeeper.SetDymName(s.ctx, dymName2)) + + offer2 := dymnstypes.BuyOrder{ + Id: "102", + AssetId: dymName2.Name, + AssetType: dymnstypes.TypeName, + Buyer: buyerA, + OfferPrice: s.coin(1), + } + s.Require().NoError(s.dymNsKeeper.SetBuyOrder(s.ctx, offer2)) + + s.Require().NoError(s.dymNsKeeper.AddReverseMappingAssetIdToBuyOrder(s.ctx, dymName2.Name, dymnstypes.TypeName, offer2.Id)) + + s.Run("no error if remove a record that not linked", func() { + linked, _ := s.dymNsKeeper.GetBuyOrdersOfDymName(s.ctx, dymName1.Name) + s.Require().Len(linked, 2) + + s.Require().NoError(s.dymNsKeeper.RemoveReverseMappingAssetIdToBuyOrder(s.ctx, dymName1.Name, dymnstypes.TypeName, offer2.Id)) + + linked, err := s.dymNsKeeper.GetBuyOrdersOfDymName(s.ctx, dymName1.Name) + s.Require().NoError(err) + s.Require().Len(linked, 2, "existing data must be kept") + }) + + s.Run("no error if element is not in the list", func() { + s.Require().NoError(s.dymNsKeeper.RemoveReverseMappingAssetIdToBuyOrder(s.ctx, dymName1.Name, dymnstypes.TypeName, "10218362184621")) + + linked, err := s.dymNsKeeper.GetBuyOrdersOfDymName(s.ctx, dymName1.Name) + s.Require().NoError(err) + s.Require().Len(linked, 2, "existing data must be kept") + }) + + s.Run("remove correctly", func() { + s.Require().NoError(s.dymNsKeeper.RemoveReverseMappingAssetIdToBuyOrder(s.ctx, dymName1.Name, dymnstypes.TypeName, offer11.Id)) + + linked, err := s.dymNsKeeper.GetBuyOrdersOfDymName(s.ctx, dymName1.Name) + s.Require().NoError(err) + s.Require().Len(linked, 1) + s.Require().Equal(offer12.Id, linked[0].Id) + + s.Require().NoError(s.dymNsKeeper.RemoveReverseMappingAssetIdToBuyOrder(s.ctx, dymName1.Name, dymnstypes.TypeName, offer12.Id)) + + linked, err = s.dymNsKeeper.GetBuyOrdersOfDymName(s.ctx, dymName1.Name) + s.Require().NoError(err) + s.Require().Empty(linked) + }) + + linked, err := s.dymNsKeeper.GetBuyOrdersOfDymName(s.ctx, dymName2.Name) + s.Require().NoError(err) + s.Require().Len(linked, 1) +} + +func (s *KeeperTestSuite) TestKeeper_RemoveReverseMappingAssetIdToBuyOrder_Type_Alias() { + buyerA := testAddr(1).bech32() + + const alias1 = "one" + + const dstRollAppId = "rollapp_3-2" + + offer11 := dymnstypes.BuyOrder{ + Id: dymnstypes.CreateBuyOrderId(dymnstypes.TypeAlias, 11), + AssetId: alias1, + AssetType: dymnstypes.TypeAlias, + Params: []string{dstRollAppId}, + Buyer: buyerA, + OfferPrice: s.coin(1), + } + s.Require().NoError(s.dymNsKeeper.SetBuyOrder(s.ctx, offer11)) + + offer12 := dymnstypes.BuyOrder{ + Id: dymnstypes.CreateBuyOrderId(dymnstypes.TypeAlias, 12), + AssetId: alias1, + AssetType: dymnstypes.TypeAlias, + Params: []string{dstRollAppId}, + Buyer: buyerA, + OfferPrice: s.coin(1), + } + s.Require().NoError(s.dymNsKeeper.SetBuyOrder(s.ctx, offer12)) + + s.Require().NoError(s.dymNsKeeper.AddReverseMappingAssetIdToBuyOrder(s.ctx, alias1, dymnstypes.TypeAlias, offer11.Id)) + + s.Require().NoError(s.dymNsKeeper.AddReverseMappingAssetIdToBuyOrder(s.ctx, alias1, dymnstypes.TypeAlias, offer12.Id)) + + const alias2 = "two" + + offer2 := dymnstypes.BuyOrder{ + Id: dymnstypes.CreateBuyOrderId(dymnstypes.TypeAlias, 2), + AssetId: alias2, + AssetType: dymnstypes.TypeAlias, + Params: []string{dstRollAppId}, + Buyer: buyerA, + OfferPrice: s.coin(1), + } + s.Require().NoError(s.dymNsKeeper.SetBuyOrder(s.ctx, offer2)) + + s.Require().NoError(s.dymNsKeeper.AddReverseMappingAssetIdToBuyOrder(s.ctx, alias2, dymnstypes.TypeAlias, offer2.Id)) + + s.Run("no error if remove a record that not linked", func() { + linked, _ := s.dymNsKeeper.GetBuyOrdersOfAlias(s.ctx, alias1) + s.Require().Len(linked, 2) + + s.Require().NoError(s.dymNsKeeper.RemoveReverseMappingAssetIdToBuyOrder(s.ctx, alias1, dymnstypes.TypeAlias, offer2.Id)) + + linked, err := s.dymNsKeeper.GetBuyOrdersOfAlias(s.ctx, alias1) + s.Require().NoError(err) + s.Require().Len(linked, 2, "existing data must be kept") + }) + + s.Run("no error if element is not in the list", func() { + s.Require().NoError(s.dymNsKeeper.RemoveReverseMappingAssetIdToBuyOrder(s.ctx, alias1, dymnstypes.TypeAlias, "20218362184621")) + + linked, err := s.dymNsKeeper.GetBuyOrdersOfAlias(s.ctx, alias1) + s.Require().NoError(err) + s.Require().Len(linked, 2, "existing data must be kept") + }) + + s.Run("remove correctly", func() { + s.Require().NoError(s.dymNsKeeper.RemoveReverseMappingAssetIdToBuyOrder(s.ctx, alias1, dymnstypes.TypeAlias, offer11.Id)) + + linked, err := s.dymNsKeeper.GetBuyOrdersOfAlias(s.ctx, alias1) + s.Require().NoError(err) + s.Require().Len(linked, 1) + s.Require().Equal(offer12.Id, linked[0].Id) + + s.Require().NoError(s.dymNsKeeper.RemoveReverseMappingAssetIdToBuyOrder(s.ctx, alias1, dymnstypes.TypeAlias, offer12.Id)) + + linked, err = s.dymNsKeeper.GetBuyOrdersOfAlias(s.ctx, alias1) + s.Require().NoError(err) + s.Require().Empty(linked) + }) + + linked, err := s.dymNsKeeper.GetBuyOrdersOfAlias(s.ctx, alias2) + s.Require().NoError(err) + s.Require().Len(linked, 1) +} diff --git a/x/dymns/keeper/buy_order_test.go b/x/dymns/keeper/buy_order_test.go new file mode 100644 index 000000000..f9c0f6a0a --- /dev/null +++ b/x/dymns/keeper/buy_order_test.go @@ -0,0 +1,592 @@ +package keeper_test + +import ( + "math" + + sdk "github.com/cosmos/cosmos-sdk/types" + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" +) + +func (s *KeeperTestSuite) TestKeeper_IncreaseBuyOrdersCountAndGet() { + s.Require().Zero(s.dymNsKeeper.GetCountBuyOrders(s.ctx)) + + count := s.dymNsKeeper.IncreaseBuyOrdersCountAndGet(s.ctx) + s.Require().Equal(uint64(1), count) + s.Require().Equal(uint64(1), s.dymNsKeeper.GetCountBuyOrders(s.ctx)) + + count = s.dymNsKeeper.IncreaseBuyOrdersCountAndGet(s.ctx) + s.Require().Equal(uint64(2), count) + s.Require().Equal(uint64(2), s.dymNsKeeper.GetCountBuyOrders(s.ctx)) + + count = s.dymNsKeeper.IncreaseBuyOrdersCountAndGet(s.ctx) + s.Require().Equal(uint64(3), count) + s.Require().Equal(uint64(3), s.dymNsKeeper.GetCountBuyOrders(s.ctx)) + + s.dymNsKeeper.SetCountBuyOrders(s.ctx, math.MaxUint64-1) + + count = s.dymNsKeeper.IncreaseBuyOrdersCountAndGet(s.ctx) + s.Require().Equal(uint64(math.MaxUint64), count) + s.Require().Equal(uint64(math.MaxUint64), s.dymNsKeeper.GetCountBuyOrders(s.ctx)) + + s.Require().Panics(func() { + s.dymNsKeeper.IncreaseBuyOrdersCountAndGet(s.ctx) + }, "expect panic on overflow when increasing count of all-time buy offer records greater than uint64") +} + +func (s *KeeperTestSuite) TestKeeper_GetSetInsertNewBuyOrder() { + buyerA := testAddr(1).bech32() + + supportedAssetTypes := []dymnstypes.AssetType{ + dymnstypes.TypeName, dymnstypes.TypeAlias, + } + + s.Run("get non-exists offer should returns nil", func() { + offer := s.dymNsKeeper.GetBuyOrder(s.ctx, "10183418") + s.Require().Nil(offer) + }) + + s.Run("should returns error when set empty ID offer", func() { + for _, assetType := range supportedAssetTypes { + s.Run(assetType.PrettyName(), func() { + s.RefreshContext() + + var params []string + if assetType == dymnstypes.TypeAlias { + params = []string{"rollapp_1-1"} + } + + err := s.dymNsKeeper.SetBuyOrder(s.ctx, dymnstypes.BuyOrder{ + Id: "", + AssetId: "asset", + AssetType: assetType, + Params: params, + Buyer: buyerA, + OfferPrice: s.coin(1), + }) + s.Require().ErrorContains(err, "ID of offer is empty") + }) + } + }) + + s.Run("can set and can get", func() { + s.RefreshContext() + + offer1 := dymnstypes.BuyOrder{ + Id: "101", + AssetId: "my-name", + AssetType: dymnstypes.TypeName, + Buyer: buyerA, + OfferPrice: s.coin(1), + } + + err := s.dymNsKeeper.SetBuyOrder(s.ctx, offer1) + s.Require().NoError(err) + + offerGot1 := s.dymNsKeeper.GetBuyOrder(s.ctx, offer1.Id) + s.Require().NotNil(offerGot1) + + s.Require().Equal(offer1, *offerGot1) + + offer2 := dymnstypes.BuyOrder{ + Id: "202", + AssetId: "alias", + AssetType: dymnstypes.TypeAlias, + Params: []string{"rollapp_1-1"}, + Buyer: buyerA, + OfferPrice: s.coin(1), + } + + err = s.dymNsKeeper.SetBuyOrder(s.ctx, offer2) + s.Require().NoError(err) + + offerGot2 := s.dymNsKeeper.GetBuyOrder(s.ctx, offer2.Id) + s.Require().NotNil(offerGot2) + + s.Require().Equal(offer2, *offerGot2) + + // previous record should not be effected + + offerGot1 = s.dymNsKeeper.GetBuyOrder(s.ctx, offer1.Id) + s.Require().NotNil(offerGot1) + + s.Require().NotEqual(*offerGot1, *offerGot2) + s.Require().Equal(offer1, *offerGot1) + }) + + s.Run("set omits params if empty", func() { + s.RefreshContext() + + offer1 := dymnstypes.BuyOrder{ + Id: "101", + AssetId: "my-name", + AssetType: dymnstypes.TypeName, + Params: []string{}, + Buyer: buyerA, + OfferPrice: s.coin(1), + } + + err := s.dymNsKeeper.SetBuyOrder(s.ctx, offer1) + s.Require().NoError(err) + + offerGot1 := s.dymNsKeeper.GetBuyOrder(s.ctx, offer1.Id) + s.Require().NotNil(offerGot1) + + s.Require().Nil(offerGot1.Params) + }) + + s.Run("should panic when insert non-empty ID offer", func() { + for _, assetType := range supportedAssetTypes { + s.Run(assetType.PrettyName(), func() { + s.RefreshContext() + + var params []string + if assetType == dymnstypes.TypeAlias { + params = []string{"rollapp_1-1"} + } + + s.Require().Panics(func() { + _, _ = s.dymNsKeeper.InsertNewBuyOrder(s.ctx, dymnstypes.BuyOrder{ + Id: dymnstypes.CreateBuyOrderId(assetType, 1), + AssetId: "asset", + AssetType: assetType, + Params: params, + Buyer: buyerA, + OfferPrice: s.coin(1), + }) + }) + }) + } + }) + + s.Run("can insert", func() { + s.RefreshContext() + + offer1a := dymnstypes.BuyOrder{ + Id: "", + AssetId: "my-name", + AssetType: dymnstypes.TypeName, + Buyer: buyerA, + OfferPrice: s.coin(1), + } + + offer1b, err := s.dymNsKeeper.InsertNewBuyOrder(s.ctx, offer1a) + s.Require().NoError(err) + s.Require().Equal("101", offer1b.Id) + + offerGot1 := s.dymNsKeeper.GetBuyOrder(s.ctx, "101") + s.Require().NotNil(offerGot1) + + offer1a.Id = offer1b.Id + s.Require().Equal(offer1a, *offerGot1) + + offer2a := dymnstypes.BuyOrder{ + Id: "", + AssetId: "alias", + AssetType: dymnstypes.TypeAlias, + Params: []string{"rollapp_1-1"}, + Buyer: buyerA, + OfferPrice: s.coin(1), + } + + offer2b, err := s.dymNsKeeper.InsertNewBuyOrder(s.ctx, offer2a) + s.Require().NoError(err) + s.Require().Equal("202", offer2b.Id) + + offerGot2 := s.dymNsKeeper.GetBuyOrder(s.ctx, "202") + s.Require().NotNil(offerGot2) + + offer2a.Id = offer2b.Id + s.Require().Equal(offer2a, *offerGot2) + + // previous record should not be effected + + offerGot1 = s.dymNsKeeper.GetBuyOrder(s.ctx, "101") + s.Require().NotNil(offerGot1) + + s.Require().NotEqual(*offerGot1, *offerGot2) + s.Require().Equal(offer1a, *offerGot1) + }) + + s.Run("can not insert duplicated", func() { + for _, assetType := range supportedAssetTypes { + s.Run(assetType.PrettyName(), func() { + s.RefreshContext() + + s.dymNsKeeper.SetCountBuyOrders(s.ctx, 1) + const nextId uint64 = 2 + + var params []string + if assetType == dymnstypes.TypeAlias { + params = []string{"rollapp_1-1"} + } + + existing := dymnstypes.BuyOrder{ + Id: dymnstypes.CreateBuyOrderId(assetType, nextId), + AssetId: "asset", + AssetType: assetType, + Params: params, + Buyer: buyerA, + OfferPrice: s.coin(1), + } + + err := s.dymNsKeeper.SetBuyOrder(s.ctx, existing) + s.Require().NoError(err) + + offer := dymnstypes.BuyOrder{ + Id: "", + AssetId: "asset", + AssetType: assetType, + Params: params, + Buyer: buyerA, + OfferPrice: s.coin(1), + } + + _, err = s.dymNsKeeper.InsertNewBuyOrder(s.ctx, offer) + s.Require().Error(err) + s.Require().Contains(err.Error(), "Buy-Order ID already exists") + }) + } + }) + + s.Run("should automatically fill ID when insert", func() { + for _, assetType := range supportedAssetTypes { + s.Run(assetType.PrettyName(), func() { + s.RefreshContext() + + var params []string + if assetType == dymnstypes.TypeAlias { + params = []string{"rollapp_1-1"} + } + + offer1 := dymnstypes.BuyOrder{ + Id: "", + AssetId: "one", + AssetType: assetType, + Params: params, + Buyer: buyerA, + OfferPrice: s.coin(1), + } + + offer, err := s.dymNsKeeper.InsertNewBuyOrder(s.ctx, offer1) + s.Require().NoError(err) + + wantId1 := dymnstypes.CreateBuyOrderId(assetType, 1) + s.Require().Equal(wantId1, offer.Id) + + offerGot := s.dymNsKeeper.GetBuyOrder(s.ctx, wantId1) + s.Require().NotNil(offerGot) + + offer1.Id = wantId1 + s.Require().Equal(offer1, *offerGot) + + s.dymNsKeeper.SetCountBuyOrders(s.ctx, 99) + + offer2 := dymnstypes.BuyOrder{ + Id: "", + AssetId: "two", + AssetType: assetType, + Params: params, + Buyer: buyerA, + OfferPrice: s.coin(1), + } + offer, err = s.dymNsKeeper.InsertNewBuyOrder(s.ctx, offer2) + s.Require().NoError(err) + + wantId2 := dymnstypes.CreateBuyOrderId(assetType, 100) + s.Require().Equal(wantId2, offer.Id) + + offerGot = s.dymNsKeeper.GetBuyOrder(s.ctx, wantId2) + s.Require().NotNil(offerGot) + + offer2.Id = wantId2 + s.Require().Equal(offer2, *offerGot) + }) + } + }) + + s.Run("can delete", func() { + s.RefreshContext() + + var err error + + offer1 := dymnstypes.BuyOrder{ + Id: "101", + AssetId: "a", + AssetType: dymnstypes.TypeName, + Buyer: buyerA, + OfferPrice: s.coin(1), + } + err = s.dymNsKeeper.SetBuyOrder(s.ctx, offer1) + s.Require().NoError(err) + + offer2 := dymnstypes.BuyOrder{ + Id: "202", + AssetId: "b", + AssetType: dymnstypes.TypeAlias, + Params: []string{"rollapp_1-1"}, + Buyer: buyerA, + OfferPrice: s.coin(2), + } + err = s.dymNsKeeper.SetBuyOrder(s.ctx, offer2) + s.Require().NoError(err) + + offer3 := dymnstypes.BuyOrder{ + Id: "103", + AssetId: "c", + AssetType: dymnstypes.TypeName, + Buyer: buyerA, + OfferPrice: s.coin(3), + } + err = s.dymNsKeeper.SetBuyOrder(s.ctx, offer3) + s.Require().NoError(err) + + offer4 := dymnstypes.BuyOrder{ + Id: "204", + AssetId: "d", + AssetType: dymnstypes.TypeAlias, + Params: []string{"rollapp_2-2"}, + Buyer: buyerA, + OfferPrice: s.coin(4), + } + err = s.dymNsKeeper.SetBuyOrder(s.ctx, offer4) + s.Require().NoError(err) + + s.Require().NotNil(s.dymNsKeeper.GetBuyOrder(s.ctx, offer1.Id)) + s.Require().NotNil(s.dymNsKeeper.GetBuyOrder(s.ctx, offer2.Id)) + s.Require().NotNil(s.dymNsKeeper.GetBuyOrder(s.ctx, offer3.Id)) + s.Require().NotNil(s.dymNsKeeper.GetBuyOrder(s.ctx, offer4.Id)) + + s.dymNsKeeper.DeleteBuyOrder(s.ctx, offer2.Id) + s.Require().NotNil(s.dymNsKeeper.GetBuyOrder(s.ctx, offer1.Id)) + s.Require().Nil(s.dymNsKeeper.GetBuyOrder(s.ctx, offer2.Id)) + s.Require().NotNil(s.dymNsKeeper.GetBuyOrder(s.ctx, offer3.Id)) + s.Require().NotNil(s.dymNsKeeper.GetBuyOrder(s.ctx, offer4.Id)) + + s.dymNsKeeper.DeleteBuyOrder(s.ctx, offer4.Id) + s.Require().NotNil(s.dymNsKeeper.GetBuyOrder(s.ctx, offer1.Id)) + s.Require().Nil(s.dymNsKeeper.GetBuyOrder(s.ctx, offer2.Id)) + s.Require().NotNil(s.dymNsKeeper.GetBuyOrder(s.ctx, offer3.Id)) + s.Require().Nil(s.dymNsKeeper.GetBuyOrder(s.ctx, offer4.Id)) + + s.dymNsKeeper.DeleteBuyOrder(s.ctx, offer3.Id) + s.Require().NotNil(s.dymNsKeeper.GetBuyOrder(s.ctx, offer1.Id)) + s.Require().Nil(s.dymNsKeeper.GetBuyOrder(s.ctx, offer2.Id)) + s.Require().Nil(s.dymNsKeeper.GetBuyOrder(s.ctx, offer3.Id)) + s.Require().Nil(s.dymNsKeeper.GetBuyOrder(s.ctx, offer4.Id)) + }) + + s.Run("delete non-existing will not panics", func() { + s.RefreshContext() + + s.dymNsKeeper.DeleteBuyOrder(s.ctx, "1099999") + s.dymNsKeeper.DeleteBuyOrder(s.ctx, "2099999") + }) + + s.Run("event should be fired on set/insert offer", func() { + tests := []struct { + name string + offer dymnstypes.BuyOrder + setFunc func(offer dymnstypes.BuyOrder, s *KeeperTestSuite) + }{ + { + name: "set offer type Dym-Name", + offer: dymnstypes.BuyOrder{ + Id: "101", + AssetId: "my-name", + AssetType: dymnstypes.TypeName, + Buyer: buyerA, + OfferPrice: s.coin(1), + }, + setFunc: func(offer dymnstypes.BuyOrder, s *KeeperTestSuite) { + err := s.dymNsKeeper.SetBuyOrder(s.ctx, offer) + s.Require().NoError(err) + }, + }, + { + name: "set offer type Alias", + offer: dymnstypes.BuyOrder{ + Id: "201", + AssetId: "alias", + AssetType: dymnstypes.TypeAlias, + Params: []string{"rollapp_1-1"}, + Buyer: buyerA, + OfferPrice: s.coin(1), + }, + setFunc: func(offer dymnstypes.BuyOrder, s *KeeperTestSuite) { + err := s.dymNsKeeper.SetBuyOrder(s.ctx, offer) + s.Require().NoError(err) + }, + }, + } + for _, tt := range tests { + s.Run(tt.name, func() { + s.RefreshContext() + + tt.setFunc(tt.offer, s) + + events := s.ctx.EventManager().Events() + s.Require().NotEmpty(events) + + for _, event := range events { + if event.Type != dymnstypes.EventTypeBuyOrder { + continue + } + + var actionName string + for _, attr := range event.Attributes { + if attr.Key == dymnstypes.AttributeKeyBoActionName { + actionName = attr.Value + } + } + s.Require().NotEmpty(actionName, "event attr action name could not be found") + s.Require().Equalf( + actionName, dymnstypes.AttributeValueBoActionNameSet, + "event attr action name should be `%s`", dymnstypes.AttributeValueBoActionNameSet, + ) + return + } + + s.T().Errorf("event %s not found", dymnstypes.EventTypeBuyOrder) + }) + } + }) + + s.Run("event should be fired on delete offer", func() { + for _, assetType := range supportedAssetTypes { + s.Run(assetType.PrettyName(), func() { + s.RefreshContext() + + var params []string + if assetType == dymnstypes.TypeAlias { + params = []string{"rollapp_1-1"} + } + + offer := dymnstypes.BuyOrder{ + Id: dymnstypes.CreateBuyOrderId(assetType, 1), + AssetId: "asset", + AssetType: assetType, + Params: params, + Buyer: buyerA, + OfferPrice: s.coin(1), + } + + err := s.dymNsKeeper.SetBuyOrder(s.ctx, offer) + s.Require().NoError(err) + + s.ctx = s.ctx.WithEventManager(sdk.NewEventManager()) + + s.dymNsKeeper.DeleteBuyOrder(s.ctx, offer.Id) + + events := s.ctx.EventManager().Events() + s.Require().NotEmpty(events) + + for _, event := range events { + if event.Type != dymnstypes.EventTypeBuyOrder { + continue + } + + var actionName string + for _, attr := range event.Attributes { + if attr.Key == dymnstypes.AttributeKeyBoActionName { + actionName = attr.Value + } + } + s.Require().NotEmpty(actionName, "event attr action name could not be found") + s.Require().Equalf( + actionName, dymnstypes.AttributeValueBoActionNameDelete, + "event attr action name should be `%s`", dymnstypes.AttributeValueBoActionNameDelete, + ) + return + } + + s.T().Errorf("event %s not found", dymnstypes.EventTypeBuyOrder) + }) + } + }) + + s.Run("get - should panic if input Buy-Order ID is malformed", func() { + s.Require().Panics(func() { + _ = s.dymNsKeeper.GetBuyOrder(s.ctx, "invalid") + }) + }) +} + +func (s *KeeperTestSuite) TestKeeper_GetAllBuyOrders() { + buyerA := testAddr(1).bech32() + + offer1 := dymnstypes.BuyOrder{ + Id: "101", + AssetId: "a", + AssetType: dymnstypes.TypeName, + Buyer: buyerA, + OfferPrice: s.coin(1), + } + err := s.dymNsKeeper.SetBuyOrder(s.ctx, offer1) + s.Require().NoError(err) + + offers := s.dymNsKeeper.GetAllBuyOrders(s.ctx) + s.Require().Len(offers, 1) + s.Require().Equal(offer1, offers[0]) + + offer2 := dymnstypes.BuyOrder{ + Id: "202", + AssetId: "b", + AssetType: dymnstypes.TypeAlias, + Params: []string{"rollapp_1-1"}, + Buyer: buyerA, + OfferPrice: s.coin(1), + } + err = s.dymNsKeeper.SetBuyOrder(s.ctx, offer2) + s.Require().NoError(err) + + offers = s.dymNsKeeper.GetAllBuyOrders(s.ctx) + s.Require().Len(offers, 2) + s.Require().Equal([]dymnstypes.BuyOrder{offer1, offer2}, offers) + + offer3 := dymnstypes.BuyOrder{ + Id: "103", + AssetId: "a", + AssetType: dymnstypes.TypeName, + Buyer: buyerA, + OfferPrice: s.coin(1), + } + err = s.dymNsKeeper.SetBuyOrder(s.ctx, offer3) + s.Require().NoError(err) + + offers = s.dymNsKeeper.GetAllBuyOrders(s.ctx) + s.Require().Len(offers, 3) + s.Require().Equal([]dymnstypes.BuyOrder{ + offer1, offer3, // <= Dym-Name Buy-Order should be sorted first + offer2, // <= Alias Buy-Order should be sorted second + // because of store branched by asset type + }, offers) + + offer4 := dymnstypes.BuyOrder{ + Id: "204", + AssetId: "b", + AssetType: dymnstypes.TypeAlias, + Params: []string{"rollapp_2-2"}, + Buyer: buyerA, + OfferPrice: s.coin(1), + } + err = s.dymNsKeeper.SetBuyOrder(s.ctx, offer4) + s.Require().NoError(err) + + offers = s.dymNsKeeper.GetAllBuyOrders(s.ctx) + s.Require().Len(offers, 4) + s.Require().Equal([]dymnstypes.BuyOrder{offer1, offer3, offer2, offer4}, offers) + + offer5 := dymnstypes.BuyOrder{ + Id: "105", + AssetId: "b", + AssetType: dymnstypes.TypeName, + Buyer: buyerA, + OfferPrice: s.coin(3), + } + err = s.dymNsKeeper.SetBuyOrder(s.ctx, offer5) + s.Require().NoError(err) + + offers = s.dymNsKeeper.GetAllBuyOrders(s.ctx) + s.Require().Len(offers, 5) + s.Require().Equal([]dymnstypes.BuyOrder{offer1, offer3, offer5, offer2, offer4}, offers) +} diff --git a/x/dymns/keeper/dym_name.go b/x/dymns/keeper/dym_name.go new file mode 100644 index 000000000..bf662fdee --- /dev/null +++ b/x/dymns/keeper/dym_name.go @@ -0,0 +1,942 @@ +package keeper + +import ( + "strings" + + errorsmod "cosmossdk.io/errors" + "github.com/dymensionxyz/gerr-cosmos/gerrc" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/bech32" + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" + dymnsutils "github.com/dymensionxyz/dymension/v3/x/dymns/utils" +) + +// SetDymName stores a Dym-Name into the KVStore. +// +// Important Note: +// 1. Must call BeforeDymNameOwnerChanged and AfterDymNameOwnerChanged before and after calling this function when updating owner. +// 2. Must call BeforeDymNameConfigChanged and AfterDymNameConfigChanged before and after calling this function when updating configuration. +func (k Keeper) SetDymName(ctx sdk.Context, dymName dymnstypes.DymName) error { + if err := dymName.Validate(); err != nil { + return err + } + + // persist record + store := ctx.KVStore(k.storeKey) + dymNameKey := dymnstypes.DymNameKey(dymName.Name) + bz := k.cdc.MustMarshal(&dymName) + store.Set(dymNameKey, bz) + ctx.EventManager().EmitEvent(dymName.GetSdkEvent()) + + return nil +} + +// BeforeDymNameOwnerChanged must be called before updating the owner of a Dym-Name. +// This function will remove the reverse mapping from the old owner to the Dym-Name. +func (k Keeper) BeforeDymNameOwnerChanged(ctx sdk.Context, name string) error { + // reload record from store to respect the existing owner + dymName := k.GetDymName(ctx, name) + if dymName == nil { + return nil + } + + return k.RemoveReverseMappingOwnerToOwnedDymName(ctx, dymName.Owner, dymName.Name) +} + +// AfterDymNameOwnerChanged must be called after the owner of a Dym-Name is changed. +// This function will add the reverse mapping from the new owner to the Dym-Name. +// The reverse mapping present for the ownership reference. +func (k Keeper) AfterDymNameOwnerChanged(ctx sdk.Context, name string) error { + // reload record from store to respect the latest owner + dymName := k.GetDymName(ctx, name) + + if dymName == nil { + return errorsmod.Wrapf(gerrc.ErrNotFound, "Dym-Name: %s", name) + } + + return k.AddReverseMappingOwnerToOwnedDymName(ctx, dymName.Owner, name) +} + +// BeforeDymNameConfigChanged must be called before updating the configuration of a Dym-Name. +// This function will remove the reverse mapping from the configured addresses and fallback addresses to the Dym-Name. +func (k Keeper) BeforeDymNameConfigChanged(ctx sdk.Context, name string) error { + // reload record from store to respect the existing configuration + dymName := k.GetDymName(ctx, name) + + if dymName == nil { + return nil + } + + configuredAddresses, fallbackAddresses := dymName.GetAddressesForReverseMapping() + for _, configuredAddress := range dymnsutils.GetSortedStringKeys(configuredAddresses) { + if err := k.RemoveReverseMappingConfiguredAddressToDymName(ctx, configuredAddress, name); err != nil { + return err + } + } + for _, fallbackAddress := range dymnsutils.GetSortedStringKeys(fallbackAddresses) { + bz := dymnsutils.GetBytesFromHexAddress(fallbackAddress) + if err := k.RemoveReverseMappingFallbackAddressToDymName(ctx, bz, name); err != nil { + return err + } + } + + return nil +} + +// AfterDymNameConfigChanged must be called after the configuration of a Dym-Name is changed. +// This function will add the reverse mapping from the configured addresses and fallback addresses to the Dym-Name. +func (k Keeper) AfterDymNameConfigChanged(ctx sdk.Context, name string) error { + // reload record from store to ensure the latest configuration is persisted + dymName := k.GetDymName(ctx, name) + + if dymName == nil { + return errorsmod.Wrapf(gerrc.ErrNotFound, "Dym-Name: %s", name) + } + + // build list of configured addresses and fallback addresses those used for reverse mapping + configuredAddresses, fallbackAddresses := dymName.GetAddressesForReverseMapping() + + // set reverse mapping for each of the configured addresses and fallback addresses + // to the Dym-Name which contains them + for _, configuredAddress := range dymnsutils.GetSortedStringKeys(configuredAddresses) { + if err := k.AddReverseMappingConfiguredAddressToDymName(ctx, configuredAddress, name); err != nil { + return err + } + } + for _, fallbackAddrAsHex := range dymnsutils.GetSortedStringKeys(fallbackAddresses) { + bz := dymnsutils.GetBytesFromHexAddress(fallbackAddrAsHex) + if err := k.AddReverseMappingFallbackAddressToDymName(ctx, bz, name); err != nil { + return err + } + } + + return nil +} + +// GetDymName returns a Dym-Name from the KVStore. +func (k Keeper) GetDymName(ctx sdk.Context, name string) *dymnstypes.DymName { + store := ctx.KVStore(k.storeKey) + dymNameKey := dymnstypes.DymNameKey(name) + + bz := store.Get(dymNameKey) + if bz == nil { + return nil + } + + var dymName dymnstypes.DymName + k.cdc.MustUnmarshal(bz, &dymName) + + return &dymName +} + +// GetDymNameWithExpirationCheck returns a Dym-Name from the KVStore, if the Dym-Name is not expired. +// Returns nil if Dym-Name does not exist or is expired. +func (k Keeper) GetDymNameWithExpirationCheck(ctx sdk.Context, name string) *dymnstypes.DymName { + // Legacy TODO DymNS: always use this on queries + dymName := k.GetDymName(ctx, name) + if dymName == nil { + return nil + } + + if dymName.IsExpiredAtCtx(ctx) { + return nil + } + + return dymName +} + +// DeleteDymName removes a Dym-Name from the KVStore. +// This function will remove the Dym-Name record as well as the existing reverse mappings records. +func (k Keeper) DeleteDymName(ctx sdk.Context, name string) error { + // the input is the name, not the record to respect the existing owner and configuration + + if err := k.BeforeDymNameOwnerChanged(ctx, name); err != nil { + return err + } + + if err := k.BeforeDymNameConfigChanged(ctx, name); err != nil { + return err + } + + store := ctx.KVStore(k.storeKey) + dymNameKey := dymnstypes.DymNameKey(name) + store.Delete(dymNameKey) + + return nil +} + +// GetAllNonExpiredDymNames returns all non-expired Dym-Names from the KVStore. +// This function will filter out all expired Dym-Names based on the time of the context. +// Store iterator is expensive so usage should be considered carefully. +func (k Keeper) GetAllNonExpiredDymNames(ctx sdk.Context) (list []dymnstypes.DymName) { + store := ctx.KVStore(k.storeKey) + + iterator := sdk.KVStorePrefixIterator(store, dymnstypes.KeyPrefixDymName) + defer func() { + _ = iterator.Close() + }() + + for ; iterator.Valid(); iterator.Next() { + var dymName dymnstypes.DymName + k.cdc.MustUnmarshal(iterator.Value(), &dymName) + + if dymName.IsExpiredAtCtx(ctx) { + continue + } + + list = append(list, dymName) + } + + return +} + +// GetAllDymNames returns all Dym-Names from the KVStore. +// No filter applied, to be used in genesis export. +// Store iterator is expensive so usage should be used in Genesis and for testing purpose. +func (k Keeper) GetAllDymNames(ctx sdk.Context) (list []dymnstypes.DymName) { + store := ctx.KVStore(k.storeKey) + + iterator := sdk.KVStorePrefixIterator(store, dymnstypes.KeyPrefixDymName) + defer func() { + _ = iterator.Close() + }() + + for ; iterator.Valid(); iterator.Next() { + var dymName dymnstypes.DymName + k.cdc.MustUnmarshal(iterator.Value(), &dymName) + list = append(list, dymName) + } + + return list +} + +// PruneDymName removes a Dym-Name from the KVStore, as well as all related records. +func (k Keeper) PruneDymName(ctx sdk.Context, name string) error { + // remove SO (force, ignore active SO) + k.DeleteSellOrder(ctx, name, dymnstypes.TypeName) + + dymName := k.GetDymName(ctx, name) + if dymName == nil { + return nil + } + + // remove config + // This seems not necessary because we are going to remove the record anyway, + // but just let it here to clear the business logic + dymName.Configs = nil // all configuration should be removed + dymName.Owner = "" // no one owns it anyone + dymName.Controller = "" // no one controls it anyone + + return k.DeleteDymName(ctx, name) +} + +// ResolveByDymNameAddress resolves a Dym-Name-Address into an output address. +// +// For example: +// - "my-name@dym" => "dym1a..." +// - "another.my-name@dym" => "dym1b..." +// - "my-name@nim" => "nim1..." +// - (extra format) "0x1234...6789@nim" => "nim1..." +// - (extra format) "dym1a...@nim" => "nim1..." +func (k Keeper) ResolveByDymNameAddress(ctx sdk.Context, dymNameAddress string) (outputAddress string, err error) { + // Split the input address into its components: + // - Sub-Name + // - Dym-Name + // - Chain-ID or Alias (after @ or the last dot) + subName, name, chainIdOrAlias, parseErr := ParseDymNameAddress(dymNameAddress) + if parseErr != nil { + ctx.Logger().Debug("failed to parse Dym-Name address.", "dym-name-address", dymNameAddress, "error", parseErr) + err = parseErr + return + } + + dymName := k.GetDymNameWithExpirationCheck(ctx, name) + if dymName == nil { + err = errorsmod.Wrapf(gerrc.ErrNotFound, "Dym-Name: %s", name) + + // Dym-Name not found, in this case, there are 3 possible reasons: + // 1. Dym-Name does not exist + // 2. Dym-Name was expired + // 3. Resolve extra format 0x1234...6789@nim and dym1...@nim + // First two cases, stop here. + // If it is the third case, the extra format, we need to resolve it. + + // The extra format does not support sub-name + if subName != "" { + return + } + + // call method to resolve the extra format + outputAddressFromExtraFormat, success := k.resolveByDymNameAddressInExtraFormat(ctx, name, chainIdOrAlias) + if !success { + return + } + + outputAddress = outputAddressFromExtraFormat + err = nil + return + } + + defer func() { + // if no result, we need to return the Not Found error + if outputAddress == "" { + err = errorsmod.Wrapf(gerrc.ErrNotFound, "no resolution found") + return + } + }() + + tryResolveFromConfig := func(lookupChainIdConfig string) (value string, found bool) { + if lookupChainIdConfig == ctx.ChainID() { + // Dym-Name configuration in store does not persist the chain-id if it is the host chain + lookupChainIdConfig = "" + } + + // do filter + for _, config := range dymName.Configs { + if config.Type != dymnstypes.DymNameConfigType_DCT_NAME { + // skip non-Name config records + continue + } + + if config.ChainId != lookupChainIdConfig { + // skip if chain-id does not match + continue + } + + if config.Path != subName { + // skip if sub-name does not match + continue + } + + // returns the address of the configuration + return config.Value, true + } + + return "", false + } + + // first attempt to resolve, to see if the chain-id/alias is user-configured + var found bool + outputAddress, found = tryResolveFromConfig(chainIdOrAlias) + if found { + // have result, skip wasting resource + return + } + // end of first attempt + + // if that handle part is alias, try to translate it into chain-id, + // if not able to translate, we assume it is chain-id + var resolvedToChainId string + if chainId, success := k.tryResolveChainIdOrAliasToChainId(ctx, chainIdOrAlias); success { + resolvedToChainId = chainId + } else { + // treat it as chain-id + resolvedToChainId = chainIdOrAlias + } + + // second attempt to resolve + outputAddress, found = tryResolveFromConfig(resolvedToChainId) + if found { + // have result, skip wasting resource + return + } + // end of second attempt + + // no more attempts to resolve from config + + // try fallback + + if subName != "" { + // fallback does not apply for sub-name + return + } + + // now the address surely look like this: "my-name@dym" or "my-name@some-chain" + + // if the chain-id is host-chain, we do fallback to the owner + if resolvedToChainId == ctx.ChainID() { + outputAddress = dymName.Owner + return + } + + // not the host chain, try to resolve if is a RollApp + + if isRollAppId := k.IsRollAppId(ctx, resolvedToChainId); !isRollAppId { + // fallback does not apply for non-RollApp + return + } + + rollAppBech32Prefix, found := k.GetRollAppBech32Prefix(ctx, resolvedToChainId) + if !found { + // fallback does not apply for RollApp does not have this metadata + return + } + + // we convert the owner address or the address in default config to RollApp-based address + resolveToAddress := dymName.Owner + for _, config := range dymnstypes.DymNameConfigs(dymName.Configs).DefaultNameConfigs(true) { + resolveToAddress = config.Value + break + } + + // convert the address to RollApp-based address based on the RollApp bech32 prefix + accAddr := sdk.MustAccAddressFromBech32(resolveToAddress) + rollAppBasedBech32Addr, convertErr := bech32.ConvertAndEncode(rollAppBech32Prefix, accAddr) + if convertErr != nil { + err = errorsmod.Wrapf( + gerrc.ErrUnknown, + "failed to convert address to RollApp-based address: %s", resolveToAddress, + ) + return + } + + outputAddress = rollAppBasedBech32Addr + return +} + +// resolveByDymNameAddressInExtraFormat resolves a Dym-Name-Address in extra format. +// Supported extra formats: +// 1. @rollapp +// 2. @rollapp +func (k Keeper) resolveByDymNameAddressInExtraFormat(ctx sdk.Context, anyAddress, chainIdOrAlias string) (outputAddress string, success bool) { + // convert the input address to AccAddress + var accAddr sdk.AccAddress + if dymnsutils.IsValidHexAddress(anyAddress) { + accAddr = dymnsutils.GetBytesFromHexAddress(anyAddress) + } else if dymnsutils.IsValidBech32AccountAddress(anyAddress, false) { + _, bz, errDecode := bech32.DecodeAndConvert(anyAddress) + if errDecode != nil { + // silent error on purpose, just skip it + return + } + + accAddr = bz + } else { + // neither hex address nor bech32 account address + return + } + + // If that handle part is alias, try to translate it into chain-id. + // If not able to translate, this chain is unknown, we skip + chainId, resolveSuccess := k.tryResolveChainIdOrAliasToChainId(ctx, chainIdOrAlias) + if !resolveSuccess { + // not a known chain-id or alias + return + } + + // only accept resolve for host chain or RollApp + + // if the chain-id is host-chain, we do bech32 conversion to host-chain bech32 format + if chainId == ctx.ChainID() { + // is host chain + outputAddress = accAddr.String() + success = true + return + } + + if !k.IsRollAppId(ctx, chainId) { + // don't do bech32 conversion for non-RollApp because we don't know bech32 prefix + return + } + + bech32Prefix, found := k.GetRollAppBech32Prefix(ctx, chainId) + if !found { + // no bech32 prefix configured for this RollApp + return + } + + // convert the address to RollApp-based address based on the RollApp bech32 prefix + rollAppBasedBech32Addr, convertErr := bech32.ConvertAndEncode(bech32Prefix, accAddr) + if convertErr != nil { + return + } + + outputAddress = rollAppBasedBech32Addr + success = true + return +} + +// tryResolveChainIdOrAliasToChainId accept input that might be chain-id or alias, +// then try to resolve it to chain-id. +// Returns the resolved chain-id and a boolean indicating success. +func (k Keeper) tryResolveChainIdOrAliasToChainId(ctx sdk.Context, chainIdOrAlias string) (resolvedToChainId string, success bool) { + if chainIdOrAlias == ctx.ChainID() { + return chainIdOrAlias, true + } + + // first try to resolve from module params because it is the first priority + chainsParams := k.ChainsParams(ctx) + if len(chainsParams.AliasesOfChainIds) > 0 { + for _, record := range chainsParams.AliasesOfChainIds { + if chainIdOrAlias == record.ChainId { + // it is chain-id + return record.ChainId, true + } + + for _, alias := range record.Aliases { + if alias == chainIdOrAlias { + // it is alias, then returns the chain-id it mapped to + return record.ChainId, true + } + } + } + } + + if isRollAppId := k.IsRollAppId(ctx, chainIdOrAlias); isRollAppId { + // it is RollApp + return chainIdOrAlias, true + } + + if rollAppId, found := k.GetRollAppIdByAlias(ctx, chainIdOrAlias); found { + // it is an alias linked to RollApp then return the RollApp ID + return rollAppId, true + } + + return +} + +// ParseDymNameAddress parses a Dym-Name address into its components. +// +// The components are: +// 1. Sub-Name is the part before the last '.' before the '@'. +// 2. Dym-Name is the part before the last '@'. +// 3. Chain-ID or Alias is the part after the last '@'. +// +// The '@' can replaced with '.'. +func ParseDymNameAddress( + dymNameAddress string, +) ( + subName string, dymName string, chainIdOrAlias string, err error, +) { + // normalize the input address + dymNameAddress = strings.ToLower(strings.TrimSpace(dymNameAddress)) + + // last dot/@ split the name and the handle part + lastDotIndex := strings.LastIndex(dymNameAddress, ".") + lastAtIndex := strings.LastIndex(dymNameAddress, "@") + + // check if the last dot/@ is misplaced + if lastAtIndex > -1 && lastDotIndex > -1 { + if lastDotIndex > lastAtIndex { + // do not accept '.' at chain-id/alias part + err = errorsmod.Wrap(dymnstypes.ErrBadDymNameAddress, "misplaced '.'") + return + } + } + + // check if multiple '@' in the input Dym-Name-Address, then it's invalid + firstAtIndex := strings.IndexRune(dymNameAddress, '@') + if firstAtIndex > -1 { + if firstAtIndex != lastAtIndex { + err = errorsmod.Wrap(dymnstypes.ErrBadDymNameAddress, "multiple '@' found") + return + } + } + + // Dym-Name-Address does not start with dot/@ + firstDotIndex := strings.IndexRune(dymNameAddress, '.') + if firstDotIndex == 0 || firstAtIndex == 0 { + err = dymnstypes.ErrBadDymNameAddress + return + } + + // Dym-Name-Address does not end with dot/@ + if lastCharIdx := len(dymNameAddress) - 1; firstDotIndex == lastCharIdx || + firstAtIndex == lastCharIdx || + lastDotIndex == lastCharIdx || + lastAtIndex == lastCharIdx { + err = dymnstypes.ErrBadDymNameAddress + return + } + + // Dym-Name-Address does not contain consecutive dot/@ + if strings.Contains(strings.ReplaceAll(strings.ReplaceAll(dymNameAddress, ".", "|"), "@", "|"), "||") { + err = dymnstypes.ErrBadDymNameAddress + return + } + + // chop the input address into chunks + chunks := strings.FieldsFunc(dymNameAddress, func(r rune) bool { + return r == '.' || r == '@' + }) + for i, chunk := range chunks { + normalizedChunk := strings.TrimSpace(chunk) + if normalizedChunk != chunk { + // chunk part can not have leading/trailing spaces + err = dymnstypes.ErrBadDymNameAddress + return + } + chunks[i] = normalizedChunk + } + + // check if the input address is well-formed + if len(chunks) == 1 { + // seems like only Dym-Name part, without chain-id/alias,... reject it + err = dymnstypes.ErrBadDymNameAddress + return + } + + // identify the components + + chainIdOrAlias = chunks[len(chunks)-1] // handle is the last part + dymName = chunks[len(chunks)-2] // name is the part before the handle + + // if there are more than 2 parts, then the first parts are sub-name parts + if len(chunks) > 2 { + subNameParts := chunks[:len(chunks)-2] + for _, subNamePart := range subNameParts { + // validate each sub-name part + if !dymnsutils.IsValidDymName(subNamePart) { + err = errorsmod.Wrapf( + dymnstypes.ErrBadDymNameAddress, + "Sub-Dym-Name part is not well-formed: %s", subNamePart, + ) + return + } + } + subName = strings.Join(subNameParts, ".") + } + + // validate handle part + if !dymnsutils.IsValidChainIdFormat(chainIdOrAlias) && + !dymnsutils.IsValidAlias(chainIdOrAlias) { + err = errorsmod.Wrapf(dymnstypes.ErrBadDymNameAddress, "chain-id/alias is not well-formed: %s", chainIdOrAlias) + return + } + + if subName == "" { + // when no sub-name, we have 2 valid formats that won't pass Dym-Name validation, + // so here we make exception for them: + + // 0x1234...6789@chain + if dymnsutils.IsValidHexAddress(dymName) { + return + } + + // dym1a...@chain + if dymnsutils.IsValidBech32AccountAddress(dymName, false) { + return + } + } + + // validate Dym-Name part + if !dymnsutils.IsValidDymName(dymName) { + err = errorsmod.Wrapf(dymnstypes.ErrBadDymNameAddress, "Dym-Name is not well-formed: %s", dymName) + return + } + + return +} + +// ReverseResolveDymNameAddress resolves an address into a Dym-Name-Address which points to it. +// This function may return multiple possible Dym-Name-Addresses those point to the input address. +// +// For example: when we have "my-name@dym" resolves to "dym1a..." +// so reverse resolve will return "my-name@dym" when input is "dym1a..." +func (k Keeper) ReverseResolveDymNameAddress(ctx sdk.Context, inputAddress, workingChainId string) (outputDymNameAddresses dymnstypes.ReverseResolvedDymNameAddresses, err error) { + // quick check to ensure we don't waste resource on playing around with invalid input + if !dymnsutils.PossibleAccountRegardlessChain(inputAddress) { + return nil, errorsmod.Wrapf(gerrc.ErrInvalidArgument, "not supported address format: %s", inputAddress) + } + if !dymnsutils.IsValidChainIdFormat(workingChainId) { + return nil, errorsmod.Wrapf(gerrc.ErrInvalidArgument, "invalid chain-id format: %s", workingChainId) + } + + isBech32Addr := dymnsutils.IsValidBech32AccountAddress(inputAddress, false) + is0xAddr := dymnsutils.IsValidHexAddress(inputAddress) + + workingChainIdIsHostChain := workingChainId == ctx.ChainID() + workingChainIdIsRollApp := !workingChainIdIsHostChain && k.IsRollAppId(ctx, workingChainId) + + // conditionally normalize input address based on the working chain-id + // - If the chain is host-chain or RollApp, then the address is case-insensitive, so we convert it to lowercase. + // - If the chain is not host-chain or RollApp, if the input address is hex format, then treat the chain is case-insensitive address and convert it to lowercase. + // - Otherwise we don't know if the chain is case-insensitive address or not, so we keep it as is. + + if workingChainIdIsHostChain || workingChainIdIsRollApp { + if !isBech32Addr && !is0xAddr { + return nil, errorsmod.Wrapf( + gerrc.ErrInvalidArgument, + "not supported address format in host-chain and RollApp: %s", inputAddress, + ) + } + + // on host-chain and RollApps, guarantee of case-insensitive address + inputAddress = strings.ToLower(inputAddress) + } else if dymnsutils.IsValidHexAddress(inputAddress) { + // if the address is hex format, then treat the chain is case-insensitive address, + // like Ethereum, where the address is case-insensitive and checksum address contains mixed case + inputAddress = strings.ToLower(inputAddress) + } + + defer func() { + // replace chain-id with alias if possible for the results + outputDymNameAddresses = outputDymNameAddresses.Distinct() + outputDymNameAddresses = k.ReplaceChainIdWithAliasIfPossible(ctx, outputDymNameAddresses) + outputDymNameAddresses.Sort() + }() + + if is0xAddr { + hexAddr := inputAddress + + // call the method which in-charges reverse-resolve the hex address + outputDymNameAddresses, err = k.reverseResolveDymNameAddressUsingHexAddress( + ctx, + hexAddr, + workingChainId, + workingChainIdIsHostChain, workingChainIdIsRollApp, + ) + if err != nil { + return nil, err + } + + if len(outputDymNameAddresses) > 0 { + // there is at least one result, can stop here + return + } + + // there is no matching result + + // give it a try using fallback lookup + + return k.fallbackReverseResolveDymNameAddress( + ctx, + dymnsutils.GetBytesFromHexAddress(hexAddr), + workingChainId, + workingChainIdIsHostChain, workingChainIdIsRollApp, + ) + } + + // try lookup using configured address + + outputDymNameAddresses, err = k.reverseResolveDymNameAddressUsingConfiguredAddress( + ctx, + inputAddress, + workingChainId, + ) + if err != nil { + return nil, err + } + + if len(outputDymNameAddresses) > 0 { + // there is at least one result, can stop here + return + } + + // There is no matching result from lookup by configured-address, + // we are going to give it one more try using fallback-lookup. + // Only host-chain and RollApp are supported for fallback lookup because they use HD-Path 60, + // while other chains are ignored for safety purpose, as we don't know if they use the same HD-Path. + + if !workingChainIdIsHostChain && !workingChainIdIsRollApp { + return + } + + if !isBech32Addr { + // not a bech32 address, we can't do fallback lookup + return + } + + bech32Addr := inputAddress + + _, bz, err2 := bech32.DecodeAndConvert(bech32Addr) + if err2 != nil { + // this can not happen because we have checked the format before + panic("failed to decode bech32 address: " + bech32Addr) + } + + // call the method which in-charges reverse-resolve the fallback address + + return k.fallbackReverseResolveDymNameAddress( + ctx, + bz, + workingChainId, + workingChainIdIsHostChain, workingChainIdIsRollApp, + ) +} + +// reverseResolveDymNameAddressUsingConfiguredAddress resolves the input address into a Dym-Name-Address +// which points to it. +func (k Keeper) reverseResolveDymNameAddressUsingConfiguredAddress( + ctx sdk.Context, + inputAddress, + workingChainId string, +) (outputDymNameAddresses dymnstypes.ReverseResolvedDymNameAddresses, err error) { + // find all the Dym-Names those contain the input address in configuration, + // iterate through the configuration records to find out the Dym-Name-Address + dymNames, err1 := k.GetDymNamesContainsConfiguredAddress(ctx, inputAddress) + if err1 != nil { + return nil, err1 + } + + for _, dymName := range dymNames { + configuredAddresses, _ := dymName.GetAddressesForReverseMapping() + configs := configuredAddresses[inputAddress] + outputDymNameAddresses = outputDymNameAddresses.AppendConfigs(ctx, dymName, + configs, func(address dymnstypes.ReverseResolvedDymNameAddress) bool { + return address.ChainIdOrAlias == workingChainId + }, + ) + } + + return +} + +// reverseResolveDymNameAddressUsingHexAddress resolves the input hex address into a Dym-Name-Address +// which points to its corresponding bech32 address. +func (k Keeper) reverseResolveDymNameAddressUsingHexAddress( + ctx sdk.Context, + hexAddr, + workingChainId string, + workingChainIdIsHostChain, workingChainIdIsRollApp bool, +) (outputDymNameAddresses dymnstypes.ReverseResolvedDymNameAddresses, err error) { + if !dymnsutils.IsValidHexAddress(hexAddr) { + return nil, errorsmod.Wrapf(gerrc.ErrInvalidArgument, "invalid hex address: %s", hexAddr) + } + + bzAddr := dymnsutils.GetBytesFromHexAddress(hexAddr) + + // When the input address is a hex address, + // we can assume the query comes from chain that uses HD-Path 60, + // Only host-chain and RollApp are using HD-Path 60 and support reverse lookup, + // while other chains are ignored for safety purpose because we not sure the HD-Path they used. + + // But first, let try to convert it into a bech32 address to see if any result is available. + var bech32Hrp string + if workingChainIdIsHostChain { + bech32Hrp = sdk.GetConfig().GetBech32AccountAddrPrefix() + } else if rollappBech32Hrp, found := k.GetRollAppBech32Prefix(ctx, workingChainId); found { + bech32Hrp = rollappBech32Hrp + } + + { + lookupKey := hexAddr + if bech32Hrp != "" { + // found bech32 prefix configured for this chain-id + lookupKey = sdk.MustBech32ifyAddressBytes(bech32Hrp, bzAddr) + } + + dymNames, err1 := k.GetDymNamesContainsConfiguredAddress(ctx, lookupKey) + if err1 != nil { + return nil, err1 + } + + for _, dymName := range dymNames { + configuredAddresses, _ := dymName.GetAddressesForReverseMapping() + configs := configuredAddresses[lookupKey] + outputDymNameAddresses = outputDymNameAddresses.AppendConfigs(ctx, dymName, + configs, func(address dymnstypes.ReverseResolvedDymNameAddress) bool { + return address.ChainIdOrAlias == workingChainId + }, + ) + } + + if len(outputDymNameAddresses) > 0 { + // there is at least one result, can stop here + return + } + } + + // there is no matching result + + // give it a try using fallback lookup + + return k.fallbackReverseResolveDymNameAddress( + ctx, + bzAddr, + workingChainId, + workingChainIdIsHostChain, workingChainIdIsRollApp, + ) +} + +// fallbackReverseResolveDymNameAddress performs a fallback lookup for reverse resolve Dym-Name-Address. +// Fallback mechanism is used in reverse-lookup, to find possible Dym-Name-Address from account address in bytes, +// compatible for host-chain and RollApps only. +// The other chains are not supported because of HD-Path might be different. +func (k Keeper) fallbackReverseResolveDymNameAddress( + ctx sdk.Context, + bzAddr []byte, + workingChainId string, + workingChainIdIsHostChain, workingChainIdIsRollApp bool, +) (outputDymNameAddresses dymnstypes.ReverseResolvedDymNameAddresses, err error) { + // only do fallback lookup for host-chain and RollApp + if !workingChainIdIsHostChain && !workingChainIdIsRollApp { + return + } + + fallbackAddr := dymnstypes.FallbackAddress(bzAddr) + + // find the Dym-Names those contain the fallback address, + // look through the configuration records to find out the Dym-Name-Address + + dymNames, err2 := k.GetDymNamesContainsFallbackAddress(ctx, fallbackAddr) + if err2 != nil { + return nil, err2 + } + for _, dymName := range dymNames { + _, fallbackAddresses := dymName.GetAddressesForReverseMapping() + configs := fallbackAddresses[fallbackAddr.String()] + + // only accept fallback for the case of default config + for range dymnstypes.DymNameConfigs(configs).DefaultNameConfigs(true) { + outputDymNameAddresses = append(outputDymNameAddresses, dymnstypes.ReverseResolvedDymNameAddress{ + SubName: "", + Name: dymName.Name, + ChainIdOrAlias: workingChainId, // fallback + }) + + break // just take the first one + } + } + + return +} + +// ReplaceChainIdWithAliasIfPossible called before returns result for reverse-resolve, +// this replaces the chain-id with alias if possible for more pretty Dym-Name-Address. +func (k Keeper) ReplaceChainIdWithAliasIfPossible(ctx sdk.Context, reverseResolvedRecords dymnstypes.ReverseResolvedDymNameAddresses) []dymnstypes.ReverseResolvedDymNameAddress { + if len(reverseResolvedRecords) < 1 { + return reverseResolvedRecords + } + + resolvedCache := make(map[string]string) + // Describe usage of Go Map: used for caching purpose, no iteration. + + for i, reverseResolvedRecord := range reverseResolvedRecords { + chainIdOrAlias := reverseResolvedRecord.ChainIdOrAlias + + if chainIdOrAlias == "" { + // reverse-resolve record might was built from Dym-Name configuration + // so the chain-id information might be empty if it for the host chain, + // then at this place, we put it back + chainIdOrAlias = ctx.ChainID() + reverseResolvedRecords[i].ChainIdOrAlias = chainIdOrAlias + } + + // check cache first + if resolvedTo, found := resolvedCache[chainIdOrAlias]; found { + if resolvedTo != chainIdOrAlias { + reverseResolvedRecords[i].ChainIdOrAlias = resolvedTo + } + continue + } + + // get all aliases for the chain-id + aliases := k.GetEffectiveAliasesByChainId(ctx, chainIdOrAlias) + + if len(aliases) < 1 { + // no alias found, no need to replace + resolvedCache[chainIdOrAlias] = chainIdOrAlias // cache it + continue + } + + // use the default alias + + defaultAlias := aliases[0] + reverseResolvedRecords[i].ChainIdOrAlias = defaultAlias + resolvedCache[chainIdOrAlias] = defaultAlias // cache it + } + + return reverseResolvedRecords +} diff --git a/x/dymns/keeper/dym_name_reverse_lookup.go b/x/dymns/keeper/dym_name_reverse_lookup.go new file mode 100644 index 000000000..9f5b71d52 --- /dev/null +++ b/x/dymns/keeper/dym_name_reverse_lookup.go @@ -0,0 +1,287 @@ +package keeper + +import ( + "strings" + + dymnsutils "github.com/dymensionxyz/dymension/v3/x/dymns/utils" + + errorsmod "cosmossdk.io/errors" + "github.com/dymensionxyz/gerr-cosmos/gerrc" + + sdk "github.com/cosmos/cosmos-sdk/types" + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" +) + +// AddReverseMappingOwnerToOwnedDymName add a reverse mapping from the owner to owned Dym-Name into the KVStore. +// This reverse mapping should help to find all Dym-Names that owned by the account address. +// This should be called when a new Dym-Name is created and after the owner is updated. +func (k Keeper) AddReverseMappingOwnerToOwnedDymName(ctx sdk.Context, owner, name string) error { + accAddr, err := sdk.AccAddressFromBech32(owner) + if err != nil { + return errorsmod.Wrap(gerrc.ErrInvalidArgument, owner) + } + + dymNamesOwnedByAccountKey := dymnstypes.DymNamesOwnedByAccountRvlKey(accAddr) + + return k.GenericAddReverseLookupDymNamesRecord(ctx, dymNamesOwnedByAccountKey, name) +} + +// GetDymNamesOwnedBy returns all Dym-Names owned by the account address. +// The Dym-Names are filtered by the owner and excluded expired Dym-Name using the time from context. +// The action done by reverse mapping from owner to owned Dym-Name. +func (k Keeper) GetDymNamesOwnedBy( + ctx sdk.Context, owner string, +) ([]dymnstypes.DymName, error) { + accAddr, err := sdk.AccAddressFromBech32(owner) + if err != nil { + return nil, errorsmod.Wrap(gerrc.ErrInvalidArgument, owner) + } + + // load the reverse mapping record + dymNamesOwnedByAccountKey := dymnstypes.DymNamesOwnedByAccountRvlKey(accAddr) + existingOwnedDymNames := k.GenericGetReverseLookupDymNamesRecord(ctx, dymNamesOwnedByAccountKey) + + // load the Dym-Names follow the names from the reverse mapping + + var dymNames []dymnstypes.DymName + for _, owned := range existingOwnedDymNames.DymNames { + dymName := k.GetDymNameWithExpirationCheck(ctx, owned) + + // invalid records will be skipped + + if dymName == nil { + // Dym-Name not found or expired, skip + continue + } + + if dymName.Owner != owner { + // Dym-Name owner mismatch, skip + continue + } + + dymNames = append(dymNames, *dymName) + } + + return dymNames, nil +} + +// RemoveReverseMappingOwnerToOwnedDymName removes a reverse mapping from owner to owned Dym-Name from the KVStore. +// This should be called before the Dym-Name is deleted or before the owner is updated. +func (k Keeper) RemoveReverseMappingOwnerToOwnedDymName(ctx sdk.Context, owner, name string) error { + accAddr, err := sdk.AccAddressFromBech32(owner) + if err != nil { + return errorsmod.Wrapf(gerrc.ErrInvalidArgument, "owner is not a valid bech32 account address: %s", owner) + } + + dymNamesOwnedByAccountKey := dymnstypes.DymNamesOwnedByAccountRvlKey(accAddr) + + return k.GenericRemoveReverseLookupDymNamesRecord(ctx, dymNamesOwnedByAccountKey, name) +} + +// AddReverseMappingConfiguredAddressToDymName add a reverse mapping from configured address to Dym-Name +// which contains the configuration, into the KVStore. +// This reverse mapping should help to find all Dym-Names that contains the configured address. +// This should be called when a new Dym-Name is created and after the configuration is updated. +func (k Keeper) AddReverseMappingConfiguredAddressToDymName(ctx sdk.Context, configuredAddress, name string) error { + configuredAddress = normalizeConfiguredAddressForReverseMapping(configuredAddress) + if err := validateConfiguredAddressForReverseMapping(configuredAddress); err != nil { + return err + } + + return k.GenericAddReverseLookupDymNamesRecord( + ctx, + dymnstypes.ConfiguredAddressToDymNamesIncludeRvlKey(configuredAddress), + name, + ) +} + +// GetDymNamesContainsConfiguredAddress returns all Dym-Names that contains the configured address. +// The action done by reverse mapping from configured address to Dym-Name. +// The Dym-Names are filtered by the configured address and excluded expired Dym-Name using the time from context. +func (k Keeper) GetDymNamesContainsConfiguredAddress( + ctx sdk.Context, configuredAddress string, +) ([]dymnstypes.DymName, error) { + // normalize the configured address following rule and validate it + configuredAddress = normalizeConfiguredAddressForReverseMapping(configuredAddress) + if err := validateConfiguredAddressForReverseMapping(configuredAddress); err != nil { + return nil, err + } + + // load the reverse mapping record + key := dymnstypes.ConfiguredAddressToDymNamesIncludeRvlKey(configuredAddress) + currentDymNamesContainsConfiguredAddress := k.GenericGetReverseLookupDymNamesRecord(ctx, key) + + // load the Dym-Names follow the names from the reverse mapping + + var dymNames []dymnstypes.DymName + for _, name := range currentDymNamesContainsConfiguredAddress.DymNames { + dymName := k.GetDymNameWithExpirationCheck(ctx, name) + + if dymName == nil { + // Dym-Name not found or expired, skip + continue + } + + dymNames = append(dymNames, *dymName) + } + + return dymNames, nil +} + +// RemoveReverseMappingConfiguredAddressToDymName removes reverse mapping from configured address +// to Dym-Names which contains it from the KVStore. +// This should be called before the Dym-Name is deleted or before the configuration is updated. +func (k Keeper) RemoveReverseMappingConfiguredAddressToDymName(ctx sdk.Context, configuredAddress, name string) error { + configuredAddress = normalizeConfiguredAddressForReverseMapping(configuredAddress) + if err := validateConfiguredAddressForReverseMapping(configuredAddress); err != nil { + return err + } + + return k.GenericRemoveReverseLookupDymNamesRecord( + ctx, + dymnstypes.ConfiguredAddressToDymNamesIncludeRvlKey(configuredAddress), + name, + ) +} + +// validateConfiguredAddressForReverseMapping validates the configured address for reverse mapping. +func validateConfiguredAddressForReverseMapping(configuredAddress string) error { + if configuredAddress == "" { + return errorsmod.Wrap(gerrc.ErrInvalidArgument, "configured address cannot be blank") + } + return nil +} + +// normalizeConfiguredAddressForReverseMapping normalizes the configured address for reverse mapping +// before putting it into the KVStore. +func normalizeConfiguredAddressForReverseMapping(configuredAddress string) string { + configuredAddress = strings.TrimSpace(configuredAddress) + + if dymnsutils.IsValidHexAddress(configuredAddress) { + // if the address is hex format, then treat the chain is case-insensitive address, + // like Ethereum, where the address is case-insensitive and checksum address contains mixed case + configuredAddress = strings.ToLower(configuredAddress) + } + + return configuredAddress +} + +// AddReverseMappingFallbackAddressToDymName add a reverse mapping +// from fallback address to Dym-Name which contains the fallback address, into the KVStore. +// This reverse mapping should help to find all Dym-Names that contains the fallback address. +// This should be called when a new Dym-Name is created and after the configuration is updated. +func (k Keeper) AddReverseMappingFallbackAddressToDymName(ctx sdk.Context, fallbackAddr dymnstypes.FallbackAddress, name string) error { + if err := fallbackAddr.ValidateBasic(); err != nil { + return err + } + + return k.GenericAddReverseLookupDymNamesRecord( + ctx, + dymnstypes.FallbackAddressToDymNamesIncludeRvlKey(fallbackAddr), + name, + ) +} + +// GetDymNamesContainsFallbackAddress returns all Dym-Names +// that contains the fallback address. +// The action done by reverse mapping from fallback address to Dym-Name. +// The Dym-Names are filtered by the fallback address and excluded expired Dym-Name using the time from context. +func (k Keeper) GetDymNamesContainsFallbackAddress( + ctx sdk.Context, fallbackAddr dymnstypes.FallbackAddress, +) ([]dymnstypes.DymName, error) { + if err := fallbackAddr.ValidateBasic(); err != nil { + return nil, err + } + + // load the reverse mapping record + key := dymnstypes.FallbackAddressToDymNamesIncludeRvlKey(fallbackAddr) + currentDymNamesContainsFallbackAddress := k.GenericGetReverseLookupDymNamesRecord(ctx, key) + + // load the Dym-Names follow the names from the reverse mapping + + var dymNames []dymnstypes.DymName + for _, name := range currentDymNamesContainsFallbackAddress.DymNames { + dymName := k.GetDymNameWithExpirationCheck(ctx, name) + + if dymName == nil { + // dym-name not found or expired, skip + continue + } + + dymNames = append(dymNames, *dymName) + } + + return dymNames, nil +} + +// RemoveReverseMappingFallbackAddressToDymName removes reverse mapping +// from fallback address to Dym-Names which contains it from the KVStore. +// This should be called before the Dym-Name is deleted or before the configuration is updated. +func (k Keeper) RemoveReverseMappingFallbackAddressToDymName(ctx sdk.Context, fallbackAddr dymnstypes.FallbackAddress, name string) error { + if err := fallbackAddr.ValidateBasic(); err != nil { + return err + } + + return k.GenericRemoveReverseLookupDymNamesRecord( + ctx, + dymnstypes.FallbackAddressToDymNamesIncludeRvlKey(fallbackAddr), + name, + ) +} + +// GenericAddReverseLookupDymNamesRecord is a utility method that help to add a reverse lookup record for Dym-Names. +func (k Keeper) GenericAddReverseLookupDymNamesRecord(ctx sdk.Context, key []byte, name string) error { + return k.GenericAddReverseLookupRecord( + ctx, + key, name, + func(list []string) []byte { + record := dymnstypes.ReverseLookupDymNames{ + DymNames: list, + } + return k.cdc.MustMarshal(&record) + }, + func(bz []byte) []string { + var record dymnstypes.ReverseLookupDymNames + k.cdc.MustUnmarshal(bz, &record) + return record.DymNames + }, + ) +} + +// GenericGetReverseLookupDymNamesRecord is a utility method that help to get a reverse lookup record for Dym-Names. +func (k Keeper) GenericGetReverseLookupDymNamesRecord( + ctx sdk.Context, key []byte, +) dymnstypes.ReverseLookupDymNames { + dymNames := k.GenericGetReverseLookupRecord( + ctx, + key, + func(bz []byte) []string { + var record dymnstypes.ReverseLookupDymNames + k.cdc.MustUnmarshal(bz, &record) + return record.DymNames + }, + ) + + return dymnstypes.ReverseLookupDymNames{ + DymNames: dymNames, + } +} + +// GenericRemoveReverseLookupDymNamesRecord is a utility method that help to remove a reverse lookup record for Dym-Names. +func (k Keeper) GenericRemoveReverseLookupDymNamesRecord(ctx sdk.Context, key []byte, name string) error { + return k.GenericRemoveReverseLookupRecord( + ctx, + key, name, + func(list []string) []byte { + record := dymnstypes.ReverseLookupDymNames{ + DymNames: list, + } + return k.cdc.MustMarshal(&record) + }, + func(bz []byte) []string { + var record dymnstypes.ReverseLookupDymNames + k.cdc.MustUnmarshal(bz, &record) + return record.DymNames + }, + ) +} diff --git a/x/dymns/keeper/dym_name_reverse_lookup_test.go b/x/dymns/keeper/dym_name_reverse_lookup_test.go new file mode 100644 index 000000000..461b68aab --- /dev/null +++ b/x/dymns/keeper/dym_name_reverse_lookup_test.go @@ -0,0 +1,668 @@ +package keeper_test + +import ( + "strings" + "time" + + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" +) + +func (s *KeeperTestSuite) TestKeeper_GetAddReverseMappingOwnerToOwnedDymName() { + s.Run("should not allow invalid owner address", func() { + s.Require().Error(s.dymNsKeeper.AddReverseMappingOwnerToOwnedDymName(s.ctx, "0x", "a")) + + _, err := s.dymNsKeeper.GetDymNamesOwnedBy(s.ctx, "0x") + s.Require().Error(err) + }) + + owner1a := testAddr(1).bech32() + owner2a := testAddr(2).bech32() + notOwnerA := testAddr(3).bech32() + + dymName11 := dymnstypes.DymName{ + Name: "n11", + Owner: owner1a, + Controller: owner1a, + ExpireAt: time.Now().UTC().Add(time.Hour).Unix(), + } + s.Require().NoError(s.dymNsKeeper.SetDymName(s.ctx, dymName11)) + + dymName21 := dymnstypes.DymName{ + Name: "n21", + Owner: owner2a, + Controller: owner2a, + ExpireAt: time.Now().UTC().Add(time.Hour).Unix(), + } + s.Require().NoError(s.dymNsKeeper.SetDymName(s.ctx, dymName21)) + + dymName22 := dymnstypes.DymName{ + Name: "n22", + Owner: owner2a, + Controller: owner2a, + ExpireAt: time.Now().UTC().Add(time.Hour).Unix(), + } + s.Require().NoError(s.dymNsKeeper.SetDymName(s.ctx, dymName22)) + + s.Run("can add", func() { + var err error + + err = s.dymNsKeeper.AddReverseMappingOwnerToOwnedDymName(s.ctx, owner1a, dymName11.Name) + s.Require().NoError(err) + + err = s.dymNsKeeper.AddReverseMappingOwnerToOwnedDymName(s.ctx, owner2a, dymName21.Name) + s.Require().NoError(err) + + err = s.dymNsKeeper.AddReverseMappingOwnerToOwnedDymName(s.ctx, owner2a, dymName22.Name) + s.Require().NoError(err) + }) + + s.Run("can add non-existing dym-name", func() { + err := s.dymNsKeeper.AddReverseMappingOwnerToOwnedDymName(s.ctx, owner2a, "not-exists") + s.Require().NoError(err) + }) + + s.Run("no error when adding duplicated name", func() { + for i := 0; i < 3; i++ { + err := s.dymNsKeeper.AddReverseMappingOwnerToOwnedDymName(s.ctx, owner2a, dymName21.Name) + s.Require().NoError(err) + } + }) + + tests := []struct { + name string + owner string + preRun func() + want []string + }{ + { + name: "get - returns correctly", + owner: owner1a, + want: []string{dymName11.Name}, + }, + { + name: "get - returns correctly", + owner: owner2a, + want: []string{dymName21.Name, dymName22.Name}, + }, + { + name: "get - returns empty if account not owned any Dym-Name", + owner: notOwnerA, + want: nil, + }, + { + name: "get - result not include not-owned Dym-Name", + owner: owner2a, + preRun: func() { + s.Require().NoError( + s.dymNsKeeper.AddReverseMappingOwnerToOwnedDymName(s.ctx, owner2a, dymName11.Name), + "no error if dym-name owned by another owner", + ) + s.Require().NoError( + s.dymNsKeeper.AddReverseMappingOwnerToOwnedDymName(s.ctx, owner2a, "non-existence"), + "no error if dym-name owned by another owner", + ) + }, + want: []string{dymName21.Name, dymName22.Name}, + }, + } + for _, tt := range tests { + s.Run(tt.name, func() { + if tt.preRun != nil { + tt.preRun() + } + + ownedDymNames, err := s.dymNsKeeper.GetDymNamesOwnedBy(s.ctx, tt.owner) + s.Require().NoError(err) + + s.requireDymNameList(ownedDymNames, tt.want) + }) + } +} + +func (s *KeeperTestSuite) TestKeeper_RemoveReverseMappingOwnerToOwnedDymName() { + s.Require().Error( + s.dymNsKeeper.RemoveReverseMappingOwnerToOwnedDymName(s.ctx, "0x", "a"), + "should not allow invalid owner address", + ) + + owner1a := testAddr(1).bech32() + owner2a := testAddr(2).bech32() + notOwnerA := testAddr(3).bech32() + + dymName11 := dymnstypes.DymName{ + Name: "n11", + Owner: owner1a, + Controller: owner1a, + ExpireAt: time.Now().UTC().Add(time.Hour).Unix(), + } + s.setDymNameWithFunctionsAfter(dymName11) + + dymName12 := dymnstypes.DymName{ + Name: "n12", + Owner: owner1a, + Controller: owner1a, + ExpireAt: time.Now().UTC().Add(time.Hour).Unix(), + } + s.setDymNameWithFunctionsAfter(dymName12) + + dymName21 := dymnstypes.DymName{ + Name: "n21", + Owner: owner2a, + Controller: owner2a, + ExpireAt: time.Now().UTC().Add(time.Hour).Unix(), + } + s.setDymNameWithFunctionsAfter(dymName21) + + s.Require().NoError( + s.dymNsKeeper.RemoveReverseMappingOwnerToOwnedDymName(s.ctx, notOwnerA, dymName11.Name), + "no error if owner non-exists", + ) + + s.Require().NoError( + s.dymNsKeeper.RemoveReverseMappingOwnerToOwnedDymName(s.ctx, owner1a, dymName21.Name), + "no error if not owned dym-name", + ) + ownedBy, err := s.dymNsKeeper.GetDymNamesOwnedBy(s.ctx, owner1a) + s.Require().NoError(err) + + // existing data must be kept + s.requireDymNameList(ownedBy, []string{dymName11.Name, dymName12.Name}) + + s.Require().NoError( + s.dymNsKeeper.RemoveReverseMappingOwnerToOwnedDymName(s.ctx, owner1a, "not-exists"), + "no error if not-exists dym-name", + ) + ownedBy, err = s.dymNsKeeper.GetDymNamesOwnedBy(s.ctx, owner1a) + s.Require().NoError(err) + // existing data must be kept + s.requireDymNameList(ownedBy, []string{dymName11.Name, dymName12.Name}) + + s.Require().NoError( + s.dymNsKeeper.RemoveReverseMappingOwnerToOwnedDymName(s.ctx, owner1a, dymName11.Name), + ) + ownedBy, err = s.dymNsKeeper.GetDymNamesOwnedBy(s.ctx, owner1a) + s.Require().NoError(err) + s.requireDymNameList(ownedBy, []string{dymName12.Name}) + + s.Require().NoError( + s.dymNsKeeper.RemoveReverseMappingOwnerToOwnedDymName(s.ctx, owner1a, dymName12.Name), + ) + ownedBy, err = s.dymNsKeeper.GetDymNamesOwnedBy(s.ctx, owner1a) + s.Require().NoError(err) + s.Require().Len(ownedBy, 0) +} + +func (s *KeeperTestSuite) TestKeeper_GetAddReverseMappingConfiguredAddressToDymName() { + s.Run("fail - should reject blank address", func() { + s.Require().Error(s.dymNsKeeper.AddReverseMappingConfiguredAddressToDymName(s.ctx, " ", "a")) + + _, err := s.dymNsKeeper.GetDymNamesContainsConfiguredAddress(s.ctx, " ") + s.Require().Error(err) + }) + + owner1a := testAddr(1).bech32() + owner2a := testAddr(2).bech32() + anotherA := testAddr(3).bech32() + icaA := testICAddr(4).bech32() + someoneA := testAddr(5).bech32() + + dymName11 := dymnstypes.DymName{ + Name: "n11", + Owner: owner1a, + Controller: owner1a, + ExpireAt: time.Now().UTC().Add(time.Hour).Unix(), + Configs: []dymnstypes.DymNameConfig{{ + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Value: anotherA, + }}, + } + s.Require().NoError(s.dymNsKeeper.SetDymName(s.ctx, dymName11)) + err := s.dymNsKeeper.AddReverseMappingConfiguredAddressToDymName(s.ctx, anotherA, dymName11.Name) + s.Require().NoError(err) + + dymName21 := dymnstypes.DymName{ + Name: "n21", + Owner: owner2a, + Controller: owner2a, + ExpireAt: time.Now().UTC().Add(time.Hour).Unix(), + } + s.Require().NoError(s.dymNsKeeper.SetDymName(s.ctx, dymName21)) + err = s.dymNsKeeper.AddReverseMappingConfiguredAddressToDymName(s.ctx, owner2a, dymName21.Name) + s.Require().NoError(err) + + dymName22 := dymnstypes.DymName{ + Name: "n22", + Owner: owner2a, + Controller: owner2a, + ExpireAt: time.Now().UTC().Add(time.Hour).Unix(), + Configs: []dymnstypes.DymNameConfig{{ + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Value: anotherA, + }}, + } + s.Require().NoError(s.dymNsKeeper.SetDymName(s.ctx, dymName22)) + err = s.dymNsKeeper.AddReverseMappingConfiguredAddressToDymName(s.ctx, anotherA, dymName22.Name) + s.Require().NoError(err) + + s.Require().NoError( + s.dymNsKeeper.AddReverseMappingConfiguredAddressToDymName(s.ctx, anotherA, "not-exists"), + "no check non-existing dym-name", + ) + + s.Run("no error if duplicated name", func() { + for i := 0; i < 3; i++ { + s.Require().NoError( + s.dymNsKeeper.AddReverseMappingConfiguredAddressToDymName(s.ctx, owner2a, dymName21.Name), + ) + } + }) + + linked1, err1 := s.dymNsKeeper.GetDymNamesContainsConfiguredAddress(s.ctx, anotherA) + s.Require().NoError(err1) + s.Require().Len(linked1, 2) + s.requireDymNameList(linked1, []string{dymName11.Name, dymName22.Name}) + + linked2, err2 := s.dymNsKeeper.GetDymNamesContainsConfiguredAddress(s.ctx, owner2a) + s.Require().NoError(err2) + s.Require().NotEqual(2, len(linked2), "should not include non-existing dym-name") + s.Require().Len(linked2, 1) + s.Require().Equal(dymName21.Name, linked2[0].Name) + + linkedByNotExists, err3 := s.dymNsKeeper.GetDymNamesContainsConfiguredAddress(s.ctx, someoneA) + s.Require().NoError(err3) + s.Require().Len(linkedByNotExists, 0) + + s.Run("allow Interchain Account (32 bytes)", func() { + s.Require().NoError( + s.dymNsKeeper.AddReverseMappingConfiguredAddressToDymName(s.ctx, icaA, dymName11.Name), + ) + + linked3, err := s.dymNsKeeper.GetDymNamesContainsConfiguredAddress(s.ctx, icaA) + s.Require().NoError(err) + s.Require().Len(linked3, 1) + s.Require().Equal(dymName11.Name, linked3[0].Name) + }) + + s.Run("insert and get must be case-sensitive", func() { + addr1 := strings.ToLower(owner1a) + addr2 := strings.ToUpper(owner1a) + addr3 := strings.ToLower(owner1a[:10]) + strings.ToUpper(owner1a[10:20]) + strings.ToLower(owner1a[20:]) + + dymName := dymnstypes.DymName{ + Name: "my-name", + Owner: owner1a, + Controller: owner1a, + ExpireAt: s.ctx.BlockTime().Add(time.Hour).Unix(), + } + s.Require().NoError(s.dymNsKeeper.SetDymName(s.ctx, dymName)) + + err = s.dymNsKeeper.AddReverseMappingConfiguredAddressToDymName(s.ctx, addr1, dymName.Name) + s.Require().NoError(err) + err = s.dymNsKeeper.AddReverseMappingConfiguredAddressToDymName(s.ctx, addr2, dymName.Name) + s.Require().NoError(err) + err = s.dymNsKeeper.AddReverseMappingConfiguredAddressToDymName(s.ctx, addr3, dymName.Name) + s.Require().NoError(err) + + linked1, err1 := s.dymNsKeeper.GetDymNamesContainsConfiguredAddress(s.ctx, addr1) + s.Require().NoError(err1) + s.requireDymNameList(linked1, []string{dymName.Name}) + + linked2, err2 := s.dymNsKeeper.GetDymNamesContainsConfiguredAddress(s.ctx, addr2) + s.Require().NoError(err2) + s.requireDymNameList(linked2, []string{dymName.Name}) + + linked3, err3 := s.dymNsKeeper.GetDymNamesContainsConfiguredAddress(s.ctx, addr3) + s.Require().NoError(err3) + s.requireDymNameList(linked3, []string{dymName.Name}) + }) +} + +func (s *KeeperTestSuite) TestKeeper_RemoveReverseMappingConfiguredAddressToDymName() { + s.Require().Error( + s.dymNsKeeper.RemoveReverseMappingConfiguredAddressToDymName(s.ctx, " ", "a"), + "should not allow blank address", + ) + + ownerA := testAddr(1).bech32() + anotherA := testAddr(2).bech32() + icaA := testICAddr(3).bech32() + someoneA := testAddr(4).bech32() + + dymName1 := dymnstypes.DymName{ + Name: "a", + Owner: ownerA, + Controller: ownerA, + ExpireAt: time.Now().UTC().Add(time.Hour).Unix(), + Configs: []dymnstypes.DymNameConfig{{ + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Value: anotherA, + }}, + } + s.Require().NoError(s.dymNsKeeper.SetDymName(s.ctx, dymName1)) + err := s.dymNsKeeper.AddReverseMappingConfiguredAddressToDymName(s.ctx, anotherA, dymName1.Name) + s.Require().NoError(err) + + dymName2 := dymnstypes.DymName{ + Name: "b", + Owner: ownerA, + Controller: ownerA, + ExpireAt: time.Now().UTC().Add(time.Hour).Unix(), + Configs: []dymnstypes.DymNameConfig{{ + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Value: anotherA, + }}, + } + s.Require().NoError(s.dymNsKeeper.SetDymName(s.ctx, dymName2)) + err = s.dymNsKeeper.AddReverseMappingConfiguredAddressToDymName(s.ctx, anotherA, dymName2.Name) + s.Require().NoError(err) + + s.Require().NoError( + s.dymNsKeeper.RemoveReverseMappingConfiguredAddressToDymName(s.ctx, someoneA, dymName2.Name), + "no error if record not exists", + ) + + linked, err := s.dymNsKeeper.GetDymNamesContainsConfiguredAddress(s.ctx, anotherA) + s.Require().NoError(err) + s.Require().Len(linked, 2, "existing data must be kept") + + s.Run("no error if element is not in the list", func() { + s.Require().NoError( + s.dymNsKeeper.RemoveReverseMappingConfiguredAddressToDymName(s.ctx, anotherA, "not-exists"), + ) + linked, err = s.dymNsKeeper.GetDymNamesContainsConfiguredAddress(s.ctx, anotherA) + s.Require().NoError(err) + s.Require().Len(linked, 2, "existing data must be kept") + }) + + s.Run("remove correctly", func() { + s.Require().NoError( + s.dymNsKeeper.RemoveReverseMappingConfiguredAddressToDymName(s.ctx, anotherA, dymName1.Name), + ) + + linked, err = s.dymNsKeeper.GetDymNamesContainsConfiguredAddress(s.ctx, anotherA) + s.Require().NoError(err) + s.Require().Len(linked, 1) + s.Require().Equal(dymName2.Name, linked[0].Name) + + s.Require().NoError( + s.dymNsKeeper.RemoveReverseMappingConfiguredAddressToDymName(s.ctx, anotherA, dymName2.Name), + ) + + linked, err = s.dymNsKeeper.GetDymNamesContainsConfiguredAddress(s.ctx, anotherA) + s.Require().NoError(err) + s.Require().Empty(linked) + }) + + s.Run("remove correctly with Interchain Account (32 bytes)", func() { + s.Require().NoError( + s.dymNsKeeper.AddReverseMappingConfiguredAddressToDymName(s.ctx, icaA, dymName1.Name), + ) + + linked3, err := s.dymNsKeeper.GetDymNamesContainsConfiguredAddress(s.ctx, icaA) + s.Require().NoError(err) + s.Require().Len(linked3, 1) + + s.Require().NoError( + s.dymNsKeeper.RemoveReverseMappingConfiguredAddressToDymName(s.ctx, icaA, dymName1.Name), + ) + + linked, err = s.dymNsKeeper.GetDymNamesContainsConfiguredAddress(s.ctx, icaA) + s.Require().NoError(err) + s.Require().Empty(linked) + }) + + s.Run("remove must be case-sensitive", func() { + addr1 := strings.ToLower(ownerA) + addr2 := strings.ToUpper(ownerA) + addr3 := strings.ToLower(ownerA[:10]) + strings.ToUpper(ownerA[10:20]) + strings.ToLower(ownerA[20:]) + + dymName := dymnstypes.DymName{ + Name: "my-name", + Owner: ownerA, + Controller: ownerA, + ExpireAt: s.ctx.BlockTime().Add(time.Hour).Unix(), + } + s.Require().NoError(s.dymNsKeeper.SetDymName(s.ctx, dymName)) + + err = s.dymNsKeeper.AddReverseMappingConfiguredAddressToDymName(s.ctx, addr1, dymName.Name) + s.Require().NoError(err) + err = s.dymNsKeeper.AddReverseMappingConfiguredAddressToDymName(s.ctx, addr2, dymName.Name) + s.Require().NoError(err) + err = s.dymNsKeeper.AddReverseMappingConfiguredAddressToDymName(s.ctx, addr3, dymName.Name) + s.Require().NoError(err) + + linked1, err1 := s.dymNsKeeper.GetDymNamesContainsConfiguredAddress(s.ctx, addr1) + s.Require().NoError(err1) + s.requireDymNameList(linked1, []string{dymName.Name}) + + linked2, err2 := s.dymNsKeeper.GetDymNamesContainsConfiguredAddress(s.ctx, addr2) + s.Require().NoError(err2) + s.requireDymNameList(linked2, []string{dymName.Name}) + + linked3, err3 := s.dymNsKeeper.GetDymNamesContainsConfiguredAddress(s.ctx, addr3) + s.Require().NoError(err3) + s.requireDymNameList(linked3, []string{dymName.Name}) + + err = s.dymNsKeeper.RemoveReverseMappingConfiguredAddressToDymName(s.ctx, addr3, dymName.Name) + s.Require().NoError(err) + + linked3, err3 = s.dymNsKeeper.GetDymNamesContainsConfiguredAddress(s.ctx, addr3) + s.Require().NoError(err3) + s.Require().Empty(linked3) + }) +} + +func (s *KeeperTestSuite) TestKeeper_GetAddReverseMappingFallbackAddressToDymName() { + for size := 0; size <= 128; size++ { + if size == 20 || size == 32 { + continue // two valid size + } + + addr := make([]byte, size) + + s.Require().Errorf( + s.dymNsKeeper.AddReverseMappingFallbackAddressToDymName(s.ctx, addr, "a"), + "should not allow %d bytes address", size, + ) + + _, err := s.dymNsKeeper.GetDymNamesContainsFallbackAddress(s.ctx, addr) + s.Require().Errorf( + err, + "should not allow %d bytes address", size, + ) + } + + owner1Acc := testAddr(1) + owner2Acc := testAddr(2) + anotherAcc := testAddr(3) + icaAcc := testICAddr(4) + + dymName11 := dymnstypes.DymName{ + Name: "n11", + Owner: owner1Acc.bech32(), + Controller: owner1Acc.bech32(), + ExpireAt: time.Now().UTC().Add(time.Hour).Unix(), + Configs: []dymnstypes.DymNameConfig{{ + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Value: anotherAcc.bech32(), + }}, + } + s.Require().NoError(s.dymNsKeeper.SetDymName(s.ctx, dymName11)) + err := s.dymNsKeeper.AddReverseMappingFallbackAddressToDymName(s.ctx, anotherAcc.bytes(), dymName11.Name) + s.Require().NoError(err) + + dymName21 := dymnstypes.DymName{ + Name: "n21", + Owner: owner2Acc.bech32(), + Controller: owner2Acc.bech32(), + ExpireAt: time.Now().UTC().Add(time.Hour).Unix(), + } + s.Require().NoError(s.dymNsKeeper.SetDymName(s.ctx, dymName21)) + err = s.dymNsKeeper.AddReverseMappingFallbackAddressToDymName( + s.ctx, + owner2Acc.bytes(), + dymName21.Name, + ) + s.Require().NoError(err) + + dymName22 := dymnstypes.DymName{ + Name: "n22", + Owner: owner2Acc.bech32(), + Controller: owner2Acc.bech32(), + ExpireAt: time.Now().UTC().Add(time.Hour).Unix(), + Configs: []dymnstypes.DymNameConfig{{ + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Value: anotherAcc.bech32(), + }}, + } + s.Require().NoError(s.dymNsKeeper.SetDymName(s.ctx, dymName22)) + err = s.dymNsKeeper.AddReverseMappingFallbackAddressToDymName(s.ctx, anotherAcc.bytes(), dymName22.Name) + s.Require().NoError(err) + + s.Require().NoError( + s.dymNsKeeper.AddReverseMappingFallbackAddressToDymName(s.ctx, anotherAcc.bytes(), "not-exists"), + "no check non-existing dym-name", + ) + + s.Run("no error if duplicated name", func() { + for i := 0; i < 3; i++ { + s.Require().NoError( + s.dymNsKeeper.AddReverseMappingFallbackAddressToDymName(s.ctx, owner2Acc.bytes(), dymName21.Name), + ) + } + }) + + linked1, err1 := s.dymNsKeeper.GetDymNamesContainsFallbackAddress(s.ctx, anotherAcc.bytes()) + s.Require().NoError(err1) + s.Require().Len(linked1, 2) + s.requireDymNameList(linked1, []string{dymName11.Name, dymName22.Name}) + + linked2, err2 := s.dymNsKeeper.GetDymNamesContainsFallbackAddress(s.ctx, owner2Acc.bytes()) + s.Require().NoError(err2) + s.Require().NotEqual(2, len(linked2), "should not include non-existing dym-name") + s.Require().Len(linked2, 1) + s.Require().Equal(dymName21.Name, linked2[0].Name) + + linkedByNotExists, err3 := s.dymNsKeeper.GetDymNamesContainsFallbackAddress( + s.ctx, + make([]byte, 20), + ) + s.Require().NoError(err3) + s.Require().Len(linkedByNotExists, 0) + + s.Run("allow Interchain Account (32 bytes)", func() { + s.Require().NoError( + s.dymNsKeeper.AddReverseMappingFallbackAddressToDymName(s.ctx, icaAcc.bytes(), dymName11.Name), + ) + + linked3, err := s.dymNsKeeper.GetDymNamesContainsFallbackAddress(s.ctx, icaAcc.bytes()) + s.Require().NoError(err) + s.Require().Len(linked3, 1) + s.Require().Equal(dymName11.Name, linked3[0].Name) + }) +} + +func (s *KeeperTestSuite) TestKeeper_RemoveReverseMappingFallbackAddressToDymName() { + for size := 0; size <= 128; size++ { + if size == 20 || size == 32 { + continue // two valid size + } + + bz := make([]byte, size) + + s.Require().Errorf( + s.dymNsKeeper.RemoveReverseMappingFallbackAddressToDymName(s.ctx, bz, "a"), + "should not allow %d bytes address", size, + ) + } + + ownerAcc := testAddr(1) + anotherAcc := testAddr(2) + someoneAcc := testAddr(3) + icaAcc := testICAddr(4) + + dymName1 := dymnstypes.DymName{ + Name: "a", + Owner: ownerAcc.bech32(), + Controller: ownerAcc.bech32(), + ExpireAt: time.Now().UTC().Add(time.Hour).Unix(), + Configs: []dymnstypes.DymNameConfig{{ + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Value: anotherAcc.bech32(), + }}, + } + s.Require().NoError(s.dymNsKeeper.SetDymName(s.ctx, dymName1)) + err := s.dymNsKeeper.AddReverseMappingFallbackAddressToDymName(s.ctx, anotherAcc.bytes(), dymName1.Name) + s.Require().NoError(err) + + dymName2 := dymnstypes.DymName{ + Name: "b", + Owner: ownerAcc.bech32(), + Controller: ownerAcc.bech32(), + ExpireAt: time.Now().UTC().Add(time.Hour).Unix(), + Configs: []dymnstypes.DymNameConfig{{ + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Value: anotherAcc.bech32(), + }}, + } + s.Require().NoError(s.dymNsKeeper.SetDymName(s.ctx, dymName2)) + err = s.dymNsKeeper.AddReverseMappingFallbackAddressToDymName(s.ctx, anotherAcc.bytes(), dymName2.Name) + s.Require().NoError(err) + + s.Require().NoError( + s.dymNsKeeper.RemoveReverseMappingFallbackAddressToDymName(s.ctx, + someoneAcc.bytes(), + dymName2.Name, + ), + "no error if record not exists", + ) + + linked, err := s.dymNsKeeper.GetDymNamesContainsFallbackAddress(s.ctx, anotherAcc.bytes()) + s.Require().NoError(err) + s.Require().Len(linked, 2, "existing data must be kept") + + s.Run("no error if element is not in the list", func() { + s.Require().NoError( + s.dymNsKeeper.RemoveReverseMappingFallbackAddressToDymName(s.ctx, anotherAcc.bytes(), "not-in-list"), + ) + linked, err = s.dymNsKeeper.GetDymNamesContainsFallbackAddress(s.ctx, anotherAcc.bytes()) + s.Require().NoError(err) + s.Require().Len(linked, 2, "existing data must be kept") + }) + + s.Run("remove correctly", func() { + s.Require().NoError( + s.dymNsKeeper.RemoveReverseMappingFallbackAddressToDymName(s.ctx, anotherAcc.bytes(), dymName1.Name), + ) + + linked, err = s.dymNsKeeper.GetDymNamesContainsFallbackAddress(s.ctx, anotherAcc.bytes()) + s.Require().NoError(err) + s.Require().Len(linked, 1) + s.Require().Equal(dymName2.Name, linked[0].Name) + + s.Require().NoError( + s.dymNsKeeper.RemoveReverseMappingFallbackAddressToDymName(s.ctx, anotherAcc.bytes(), dymName2.Name), + ) + + linked, err = s.dymNsKeeper.GetDymNamesContainsFallbackAddress(s.ctx, anotherAcc.bytes()) + s.Require().NoError(err) + s.Require().Empty(linked) + }) + + s.Run("allow Interchain Account (32 bytes)", func() { + s.Require().NoError( + s.dymNsKeeper.AddReverseMappingFallbackAddressToDymName(s.ctx, icaAcc.bytes(), dymName1.Name), + ) + + linked3, err := s.dymNsKeeper.GetDymNamesContainsFallbackAddress(s.ctx, icaAcc.bytes()) + s.Require().NoError(err) + s.Require().Len(linked3, 1) + + s.Require().NoError( + s.dymNsKeeper.RemoveReverseMappingFallbackAddressToDymName(s.ctx, icaAcc.bytes(), dymName1.Name), + ) + linked3, err = s.dymNsKeeper.GetDymNamesContainsFallbackAddress(s.ctx, icaAcc.bytes()) + s.Require().NoError(err) + s.Require().Empty(linked3) + }) +} diff --git a/x/dymns/keeper/dym_name_test.go b/x/dymns/keeper/dym_name_test.go new file mode 100644 index 000000000..ee602b910 --- /dev/null +++ b/x/dymns/keeper/dym_name_test.go @@ -0,0 +1,3505 @@ +package keeper_test + +import ( + "strings" + "time" + "unicode" + + sdk "github.com/cosmos/cosmos-sdk/types" + dymnskeeper "github.com/dymensionxyz/dymension/v3/x/dymns/keeper" + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" + rollapptypes "github.com/dymensionxyz/dymension/v3/x/rollapp/types" + "github.com/ethereum/go-ethereum/common" +) + +func (s *KeeperTestSuite) TestKeeper_GetSetDeleteDymName() { + ownerA := testAddr(1).bech32() + + dymName := dymnstypes.DymName{ + Name: "a", + Owner: ownerA, + Controller: ownerA, + ExpireAt: 1, + Configs: []dymnstypes.DymNameConfig{{ + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Path: "www", + Value: ownerA, + }}, + } + + s.setDymNameWithFunctionsAfter(dymName) + + s.Run("event should be fired", func() { + events := s.ctx.EventManager().Events() + s.Require().NotEmpty(events) + + for _, event := range events { + if event.Type == dymnstypes.EventTypeSetDymName { + return + } + } + + s.T().Errorf("event %s not found", dymnstypes.EventTypeSetDymName) + }) + + s.Run("Dym-Name should be equals to original", func() { + s.Require().Equal(dymName, *s.dymNsKeeper.GetDymName(s.ctx, dymName.Name)) + }) + + s.Run("delete", func() { + err := s.dymNsKeeper.DeleteDymName(s.ctx, dymName.Name) + s.Require().NoError(err) + s.Require().Nil(s.dymNsKeeper.GetDymName(s.ctx, dymName.Name)) + + s.Run("reverse mapping should be deleted, check by key", func() { + ownedBy := s.dymNsKeeper.GenericGetReverseLookupDymNamesRecord(s.ctx, + dymnstypes.DymNamesOwnedByAccountRvlKey(sdk.MustAccAddressFromBech32(ownerA)), + ) + s.Require().NoError(err) + s.Require().Empty(ownedBy, "reverse mapping should be removed") + + dymNames := s.dymNsKeeper.GenericGetReverseLookupDymNamesRecord(s.ctx, + dymnstypes.ConfiguredAddressToDymNamesIncludeRvlKey(ownerA), + ) + s.Require().NoError(err) + s.Require().Empty(dymNames, "reverse mapping should be removed") + + dymNames = s.dymNsKeeper.GenericGetReverseLookupDymNamesRecord(s.ctx, + dymnstypes.FallbackAddressToDymNamesIncludeRvlKey(dymnstypes.FallbackAddress(sdk.MustAccAddressFromBech32(ownerA))), + ) + s.Require().NoError(err) + s.Require().Empty(dymNames, "reverse mapping should be removed") + }) + + s.Run("reverse mapping should be deleted, check by get", func() { + ownedBy, err := s.dymNsKeeper.GetDymNamesOwnedBy(s.ctx, ownerA) + s.Require().NoError(err) + s.Require().Empty(ownedBy, "reverse mapping should be removed") + + dymNames, err := s.dymNsKeeper.GetDymNamesContainsConfiguredAddress(s.ctx, ownerA) + s.Require().NoError(err) + s.Require().Empty(dymNames, "reverse mapping should be removed") + + dymNames, err = s.dymNsKeeper.GetDymNamesContainsFallbackAddress(s.ctx, sdk.MustAccAddressFromBech32(ownerA).Bytes()) + s.Require().NoError(err) + s.Require().Empty(dymNames, "reverse mapping should be removed") + }) + }) + + s.Run("can not set invalid Dym-Name", func() { + s.Require().Error(s.dymNsKeeper.SetDymName(s.ctx, dymnstypes.DymName{})) + }) + + s.Run("get returns nil if non-exists", func() { + s.Require().Nil(s.dymNsKeeper.GetDymName(s.ctx, "non-exists")) + }) + + s.Run("delete a non-exists Dym-Name should be ok", func() { + err := s.dymNsKeeper.DeleteDymName(s.ctx, "non-exists") + s.Require().NoError(err) + }) +} + +func (s *KeeperTestSuite) TestKeeper_BeforeAfterDymNameOwnerChanged() { + s.Run("BeforeDymNameOwnerChanged can be called on non-existing Dym-Name without error", func() { + s.Require().NoError(s.dymNsKeeper.BeforeDymNameOwnerChanged(s.ctx, "non-exists")) + }) + + s.Run("AfterDymNameOwnerChanged should returns error when calling on non-existing Dym-Name", func() { + err := s.dymNsKeeper.AfterDymNameOwnerChanged(s.ctx, "non-exists") + s.Require().Error(err) + s.Require().Contains(err.Error(), "Dym-Name: non-exists: not found") + }) + + ownerA := testAddr(1).bech32() + + dymName := dymnstypes.DymName{ + Name: "a", + Owner: ownerA, + Controller: ownerA, + ExpireAt: time.Now().Add(time.Hour).Unix(), + Configs: []dymnstypes.DymNameConfig{{ + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Path: "www", + Value: ownerA, + }}, + } + + s.Run("BeforeDymNameOwnerChanged will remove the reverse mapping owned-by", func() { + s.setDymNameWithFunctionsAfter(dymName) + + owned, err := s.dymNsKeeper.GetDymNamesOwnedBy(s.ctx, ownerA) + s.Require().NoError(err) + s.Require().Len(owned, 1) + + s.Require().NoError(s.dymNsKeeper.BeforeDymNameOwnerChanged(s.ctx, dymName.Name)) + + owned, err = s.dymNsKeeper.GetDymNamesOwnedBy(s.ctx, ownerA) + s.Require().NoError(err) + s.Require().Empty(owned) + }) + + s.Run("after run BeforeDymNameOwnerChanged, Dym-Name must be kept", func() { + s.setDymNameWithFunctionsAfter(dymName) + + s.Require().NoError(s.dymNsKeeper.BeforeDymNameOwnerChanged(s.ctx, dymName.Name)) + + s.Require().Equal(dymName, *s.dymNsKeeper.GetDymName(s.ctx, dymName.Name)) + }) + + s.Run("AfterDymNameOwnerChanged will add the reverse mapping owned-by", func() { + s.Require().NoError(s.dymNsKeeper.SetDymName(s.ctx, dymName)) + + owned, err := s.dymNsKeeper.GetDymNamesOwnedBy(s.ctx, ownerA) + s.Require().NoError(err) + s.Require().Empty(owned) + + s.Require().NoError(s.dymNsKeeper.AfterDymNameOwnerChanged(s.ctx, dymName.Name)) + + owned, err = s.dymNsKeeper.GetDymNamesOwnedBy(s.ctx, ownerA) + s.Require().NoError(err) + s.Require().Len(owned, 1) + }) + + s.Run("after run AfterDymNameOwnerChanged, Dym-Name must be kept", func() { + s.Require().NoError(s.dymNsKeeper.SetDymName(s.ctx, dymName)) + + s.Require().NoError(s.dymNsKeeper.AfterDymNameOwnerChanged(s.ctx, dymName.Name)) + + s.Require().Equal(dymName, *s.dymNsKeeper.GetDymName(s.ctx, dymName.Name)) + }) +} + +func (s *KeeperTestSuite) TestKeeper_BeforeAfterDymNameConfigChanged() { + s.Run("BeforeDymNameConfigChanged can be called on non-existing Dym-Name without error", func() { + s.Require().NoError(s.dymNsKeeper.BeforeDymNameConfigChanged(s.ctx, "non-exists")) + }) + + s.Run("AfterDymNameConfigChanged should returns error when calling on non-existing Dym-Name", func() { + err := s.dymNsKeeper.AfterDymNameConfigChanged(s.ctx, "non-exists") + s.Require().Error(err) + s.Require().Contains(err.Error(), "Dym-Name: non-exists: not found") + }) + + ownerAcc := testAddr(1) + controllerAcc := testAddr(2) + icaAcc := testICAddr(3) + + dymName := dymnstypes.DymName{ + Name: "a", + Owner: ownerAcc.bech32(), + Controller: controllerAcc.bech32(), + ExpireAt: time.Now().Add(time.Hour).Unix(), + Configs: []dymnstypes.DymNameConfig{ + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Path: "controller", + Value: controllerAcc.bech32(), + }, { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Path: "ica", + Value: icaAcc.bech32(), + }, + }, + } + + s.Run("BeforeDymNameConfigChanged will remove the reverse mapping address", func() { + // do setup test + + s.setDymNameWithFunctionsAfter(dymName) + + s.requireConfiguredAddress(ownerAcc.bech32()).mappedDymNames(dymName.Name) + s.requireConfiguredAddress(controllerAcc.bech32()).mappedDymNames(dymName.Name) + s.requireConfiguredAddress(icaAcc.bech32()).mappedDymNames(dymName.Name) + s.requireFallbackAddress(ownerAcc.bytes()).mappedDymNames(dymName.Name) + s.requireFallbackAddress(controllerAcc.bytes()).notMappedToAnyDymName() + s.requireFallbackAddress(icaAcc.bytes()).notMappedToAnyDymName() + + // do test + + s.Require().NoError(s.dymNsKeeper.BeforeDymNameConfigChanged(s.ctx, dymName.Name)) + + s.requireConfiguredAddress(ownerAcc.bech32()).notMappedToAnyDymName() + s.requireConfiguredAddress(controllerAcc.bech32()).notMappedToAnyDymName() + s.requireConfiguredAddress(icaAcc.bech32()).notMappedToAnyDymName() + s.requireFallbackAddress(ownerAcc.bytes()).notMappedToAnyDymName() + s.requireFallbackAddress(controllerAcc.bytes()).notMappedToAnyDymName() + s.requireFallbackAddress(icaAcc.bytes()).notMappedToAnyDymName() + }) + + s.Run("after run BeforeDymNameConfigChanged, Dym-Name must be kept", func() { + s.setDymNameWithFunctionsAfter(dymName) + + s.Require().NoError(s.dymNsKeeper.BeforeDymNameConfigChanged(s.ctx, dymName.Name)) + + s.Require().Equal(dymName, *s.dymNsKeeper.GetDymName(s.ctx, dymName.Name)) + }) + + s.Run("AfterDymNameConfigChanged will add the reverse mapping address", func() { + // do setup test + + s.Require().NoError(s.dymNsKeeper.SetDymName(s.ctx, dymName)) + + s.requireConfiguredAddress(ownerAcc.bech32()).notMappedToAnyDymName() + s.requireConfiguredAddress(controllerAcc.bech32()).notMappedToAnyDymName() + s.requireConfiguredAddress(icaAcc.bech32()).notMappedToAnyDymName() + s.requireFallbackAddress(ownerAcc.bytes()).notMappedToAnyDymName() + s.requireFallbackAddress(controllerAcc.bytes()).notMappedToAnyDymName() + s.requireFallbackAddress(icaAcc.bytes()).notMappedToAnyDymName() + + // do test + + s.Require().NoError(s.dymNsKeeper.AfterDymNameConfigChanged(s.ctx, dymName.Name)) + + s.requireConfiguredAddress(ownerAcc.bech32()).mappedDymNames(dymName.Name) + s.requireConfiguredAddress(controllerAcc.bech32()).mappedDymNames(dymName.Name) + s.requireConfiguredAddress(icaAcc.bech32()).mappedDymNames(dymName.Name) + s.requireFallbackAddress(ownerAcc.bytes()).mappedDymNames(dymName.Name) + s.requireFallbackAddress(controllerAcc.bytes()).notMappedToAnyDymName() + s.requireFallbackAddress(icaAcc.bytes()).notMappedToAnyDymName() + }) + + s.Run("after run AfterDymNameConfigChanged, Dym-Name must be kept", func() { + s.Require().NoError(s.dymNsKeeper.SetDymName(s.ctx, dymName)) + + s.Require().NoError(s.dymNsKeeper.AfterDymNameConfigChanged(s.ctx, dymName.Name)) + + s.Require().Equal(dymName, *s.dymNsKeeper.GetDymName(s.ctx, dymName.Name)) + }) +} + +func (s *KeeperTestSuite) TestKeeper_GetDymNameWithExpirationCheck() { + now := time.Now().UTC() + + s.ctx = s.ctx.WithBlockTime(now) + + s.Run("returns nil if not exists", func() { + s.Require().Nil(s.dymNsKeeper.GetDymNameWithExpirationCheck(s.ctx, "non-exists")) + }) + + ownerA := testAddr(1).bech32() + + dymName := dymnstypes.DymName{ + Name: "a", + Owner: ownerA, + Controller: ownerA, + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{{ + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Path: "www", + Value: ownerA, + }}, + } + + err := s.dymNsKeeper.SetDymName(s.ctx, dymName) + s.Require().NoError(err) + + s.Run("returns if not expired", func() { + s.Require().NotNil(s.dymNsKeeper.GetDymNameWithExpirationCheck(s.ctx, dymName.Name)) + }) + + s.Run("returns nil if expired", func() { + dymName.ExpireAt = s.ctx.BlockTime().Unix() - 1000 + s.Require().NoError(s.dymNsKeeper.SetDymName(s.ctx, dymName)) + s.Require().Nil(s.dymNsKeeper.GetDymNameWithExpirationCheck( + s.ctx.WithBlockTime(time.Unix(dymName.ExpireAt+1, 0)), dymName.Name, + )) + }) +} + +func (s *KeeperTestSuite) TestKeeper_GetAllDymNamesAndNonExpiredDymNames() { + now := time.Now().UTC() + + s.ctx = s.ctx.WithBlockTime(now) + + owner1a := testAddr(1).bech32() + owner2a := testAddr(2).bech32() + owner3a := testAddr(3).bech32() + + dymName1 := dymnstypes.DymName{ + Name: "a", + Owner: owner1a, + Controller: owner1a, + ExpireAt: s.now.Add(time.Hour).Unix(), + Configs: []dymnstypes.DymNameConfig{{ + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Path: "www", + Value: owner1a, + }}, + } + s.Require().NoError(s.dymNsKeeper.SetDymName(s.ctx, dymName1)) + + dymName2 := dymnstypes.DymName{ + Name: "b", + Owner: owner2a, + Controller: owner2a, + ExpireAt: s.now.Add(time.Hour).Unix(), + Configs: []dymnstypes.DymNameConfig{{ + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Path: "www", + Value: owner2a, + }}, + } + s.Require().NoError(s.dymNsKeeper.SetDymName(s.ctx, dymName2)) + + dymName3 := dymnstypes.DymName{ + Name: "c", + Owner: owner3a, + Controller: owner3a, + ExpireAt: s.now.Add(-time.Hour).Unix(), + Configs: []dymnstypes.DymNameConfig{{ + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Path: "www", + Value: owner3a, + }}, + } + s.Require().NoError(s.dymNsKeeper.SetDymName(s.ctx, dymName3)) + + listNonExpired := s.dymNsKeeper.GetAllNonExpiredDymNames(s.ctx) + s.Require().Len(listNonExpired, 2) + s.Require().Contains(listNonExpired, dymName1) + s.Require().Contains(listNonExpired, dymName2) + s.Require().NotContains(listNonExpired, dymName3, "should not include expired Dym-Name") + + listAll := s.dymNsKeeper.GetAllDymNames(s.ctx) + s.Require().Len(listAll, 3) + s.Require().Contains(listAll, dymName1) + s.Require().Contains(listAll, dymName2) + s.Require().Contains(listAll, dymName3, "should include expired Dym-Name") +} + +func (s *KeeperTestSuite) TestKeeper_GetDymNamesOwnedBy() { + now := time.Now().UTC() + + s.ctx = s.ctx.WithBlockTime(now) + + owner1a := testAddr(1).bech32() + owner2a := testAddr(2).bech32() + + dymName11 := dymnstypes.DymName{ + Name: "n11", + Owner: owner1a, + Controller: owner1a, + ExpireAt: s.now.Add(time.Hour).Unix(), + } + s.setDymNameWithFunctionsAfter(dymName11) + + dymName12 := dymnstypes.DymName{ + Name: "n12", + Owner: owner1a, + Controller: owner1a, + ExpireAt: s.now.Add(time.Hour).Unix(), + } + s.setDymNameWithFunctionsAfter(dymName12) + + dymName21 := dymnstypes.DymName{ + Name: "n21", + Owner: owner2a, + Controller: owner2a, + ExpireAt: s.now.Add(time.Hour).Unix(), + } + s.setDymNameWithFunctionsAfter(dymName21) + + s.Run("returns owned Dym-Names", func() { + ownedBy, err := s.dymNsKeeper.GetDymNamesOwnedBy(s.ctx, owner1a) + s.Require().NoError(err) + s.requireDymNameList(ownedBy, []string{dymName11.Name, dymName12.Name}) + }) + + s.Run("returns owned Dym-Names with filtered expiration", func() { + dymName12.ExpireAt = s.now.Add(-time.Hour).Unix() + s.setDymNameWithFunctionsAfter(dymName12) + + ownedBy, err := s.dymNsKeeper.GetDymNamesOwnedBy(s.ctx, owner1a) + s.Require().NoError(err) + s.requireDymNameList(ownedBy, []string{dymName11.Name}) + }) +} + +func (s *KeeperTestSuite) TestKeeper_PruneDymName() { + now := time.Now().UTC() + + s.ctx = s.ctx.WithBlockTime(now) + + s.Run("prune non-exists Dym-Name should be ok", func() { + s.Require().NoError(s.dymNsKeeper.PruneDymName(s.ctx, "non-exists")) + }) + + ownerA := testAddr(1).bech32() + + dymName1 := dymnstypes.DymName{ + Name: "a", + Owner: ownerA, + Controller: ownerA, + ExpireAt: s.now.Add(time.Hour).Unix(), + } + + s.Run("able to prune non-expired Dym-Name", func() { + s.setDymNameWithFunctionsAfter(dymName1) + s.Require().NotNil(s.dymNsKeeper.GetDymName(s.ctx, dymName1.Name)) + + s.Require().NoError(s.dymNsKeeper.PruneDymName(s.ctx, dymName1.Name)) + s.Require().Nil(s.dymNsKeeper.GetDymName(s.ctx, dymName1.Name)) + }) + + // re-setup record + s.setDymNameWithFunctionsAfter(dymName1) + s.Require().NotNil(s.dymNsKeeper.GetDymName(s.ctx, dymName1.Name)) + owned, err := s.dymNsKeeper.GetDymNamesOwnedBy(s.ctx, dymName1.Owner) + s.Require().NoError(err) + s.Require().Len(owned, 1) + + // setup active SO + so := dymnstypes.SellOrder{ + AssetId: dymName1.Name, + AssetType: dymnstypes.TypeName, + ExpireAt: s.now.Add(time.Hour).Unix(), + MinPrice: s.coin(100), + } + err = s.dymNsKeeper.SetSellOrder(s.ctx, so) + s.Require().NoError(err) + s.Require().NotNil(s.dymNsKeeper.GetSellOrder(s.ctx, dymName1.Name, dymnstypes.TypeName)) + + // prune + err = s.dymNsKeeper.PruneDymName(s.ctx, dymName1.Name) + s.Require().NoError(err) + + s.Require().Nil(s.dymNsKeeper.GetDymName(s.ctx, dymName1.Name), "Dym-Name should be removed") + + owned, err = s.dymNsKeeper.GetDymNamesOwnedBy(s.ctx, dymName1.Owner) + s.Require().NoError(err) + s.Require().Empty(owned, "reserve mapping should be removed") + + s.Require().Nil(s.dymNsKeeper.GetSellOrder(s.ctx, dymName1.Name, dymnstypes.TypeName), "active SO should be removed") +} + +//goland:noinspection SpellCheckingInspection +func (s *KeeperTestSuite) TestKeeper_ResolveByDymNameAddress() { + addr1a := testAddr(1).bech32() + + addr2Acc := testAddr(2) + addr2a := addr2Acc.bech32() + + addr3a := testAddr(3).bech32() + + generalSetupAlias := func(s *KeeperTestSuite) { + s.updateModuleParams(func(moduleParams dymnstypes.Params) dymnstypes.Params { + moduleParams.Chains.AliasesOfChainIds = []dymnstypes.AliasesOfChainId{ + { + ChainId: s.chainId, + Aliases: []string{"dym", "dymension"}, + }, + { + ChainId: "blumbus_111-1", + Aliases: []string{"bb", "blumbus"}, + }, + { + ChainId: "froopyland_100-1", + Aliases: nil, + }, + } + return moduleParams + }) + } + + tests := []struct { + name string + dymName *dymnstypes.DymName + preSetup func(*KeeperTestSuite) + dymNameAddress string + wantError bool + wantErrContains string + wantOutputAddress string + postTest func(*KeeperTestSuite) + }{ + { + name: "success, no sub name, chain-id", + dymName: &dymnstypes.DymName{ + Name: "a", + Owner: addr1a, + Controller: addr2a, + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{{ + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Value: addr3a, + }}, + }, + dymNameAddress: "a.dymension_1100-1", + wantOutputAddress: addr3a, + }, + { + name: "success, no sub name, chain-id, @", + dymName: &dymnstypes.DymName{ + Name: "a", + Owner: addr1a, + Controller: addr2a, + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{{ + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Value: addr3a, + }}, + }, + dymNameAddress: "a@dymension_1100-1", + wantOutputAddress: addr3a, + }, + { + name: "success, sub name, chain-id", + dymName: &dymnstypes.DymName{ + Name: "a", + Owner: addr1a, + Controller: addr2a, + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{{ + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Path: "b", + Value: addr3a, + }}, + }, + dymNameAddress: "b.a.dymension_1100-1", + wantOutputAddress: addr3a, + }, + { + name: "success, sub name, chain-id, @", + dymName: &dymnstypes.DymName{ + Name: "a", + Owner: addr1a, + Controller: addr2a, + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{{ + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Path: "b", + Value: addr3a, + }}, + }, + dymNameAddress: "b.a@dymension_1100-1", + wantOutputAddress: addr3a, + }, + { + name: "success, multi-sub name, chain-id", + dymName: &dymnstypes.DymName{ + Name: "a", + Owner: addr1a, + Controller: addr2a, + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{{ + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Path: "c.b", + Value: addr3a, + }}, + }, + dymNameAddress: "c.b.a.dymension_1100-1", + wantOutputAddress: addr3a, + }, + { + name: "success, multi-sub name, chain-id, @", + dymName: &dymnstypes.DymName{ + Name: "a", + Owner: addr1a, + Controller: addr2a, + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{{ + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Path: "c.b", + Value: addr3a, + }}, + }, + dymNameAddress: "c.b.a@dymension_1100-1", + wantOutputAddress: addr3a, + }, + { + name: "success, no sub name, alias", + dymName: &dymnstypes.DymName{ + Name: "a", + Owner: addr1a, + Controller: addr2a, + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{{ + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Value: addr3a, + }}, + }, + preSetup: generalSetupAlias, + dymNameAddress: "a.dym", + wantOutputAddress: addr3a, + }, + { + name: "success, no sub name, alias, @", + dymName: &dymnstypes.DymName{ + Name: "a", + Owner: addr1a, + Controller: addr2a, + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{{ + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Value: addr3a, + }}, + }, + preSetup: generalSetupAlias, + dymNameAddress: "a@dym", + wantOutputAddress: addr3a, + }, + { + name: "success, sub name, alias", + dymName: &dymnstypes.DymName{ + Name: "a", + Owner: addr1a, + Controller: addr2a, + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{{ + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Path: "b", + Value: addr3a, + }}, + }, + preSetup: generalSetupAlias, + dymNameAddress: "b.a.dym", + wantOutputAddress: addr3a, + }, + { + name: "success, sub name, alias, @", + dymName: &dymnstypes.DymName{ + Name: "a", + Owner: addr1a, + Controller: addr2a, + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{{ + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Path: "b", + Value: addr3a, + }}, + }, + preSetup: generalSetupAlias, + dymNameAddress: "b.a@dym", + wantOutputAddress: addr3a, + }, + { + name: "success, multi-sub name, alias", + dymName: &dymnstypes.DymName{ + Name: "a", + Owner: addr1a, + Controller: addr2a, + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{{ + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Path: "c.b", + Value: addr3a, + }}, + }, + preSetup: generalSetupAlias, + dymNameAddress: "c.b.a.dym", + wantOutputAddress: addr3a, + }, + { + name: "success, match multiple alias", + dymName: &dymnstypes.DymName{ + Name: "a", + Owner: addr1a, + Controller: addr2a, + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{{ + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Path: "", + Value: addr2a, + }, { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Path: "c.b", + Value: addr3a, + }}, + }, + preSetup: generalSetupAlias, + dymNameAddress: "c.b.a.dymension", + wantOutputAddress: addr3a, + postTest: func(s *KeeperTestSuite) { + var outputAddr string + var err error + + outputAddr, err = s.dymNsKeeper.ResolveByDymNameAddress(s.ctx, "c.b.a.dym") + s.Require().NoError(err) + s.Require().Equal(addr3a, outputAddr) + }, + }, + { + name: "success, multi-sub name, alias, @", + dymName: &dymnstypes.DymName{ + Name: "a", + Owner: addr1a, + Controller: addr2a, + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{{ + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Path: "c.b", + Value: addr3a, + }}, + }, + preSetup: generalSetupAlias, + dymNameAddress: "c.b.a@dym", + wantOutputAddress: addr3a, + }, + { + name: "success, multi-sub config, chain-id", + dymName: &dymnstypes.DymName{ + Name: "a", + Owner: addr1a, + Controller: addr2a, + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{{ + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Path: "c.b", + Value: addr3a, + }, { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Path: "b", + Value: addr2a, + }, { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Path: "", + Value: addr1a, + }}, + }, + preSetup: nil, + dymNameAddress: "c.b.a.dymension_1100-1", + wantOutputAddress: addr3a, + postTest: func(s *KeeperTestSuite) { + var outputAddr string + var err error + + outputAddr, err = s.dymNsKeeper.ResolveByDymNameAddress(s.ctx, "b.a.dymension_1100-1") + s.Require().NoError(err) + s.Require().Equal(addr2a, outputAddr) + + outputAddr, err = s.dymNsKeeper.ResolveByDymNameAddress(s.ctx, "b.a@dymension_1100-1") + s.Require().NoError(err) + s.Require().Equal(addr2a, outputAddr) + + outputAddr, err = s.dymNsKeeper.ResolveByDymNameAddress(s.ctx, "b.a@dymension_1100-1") + s.Require().NoError(err) + s.Require().Equal(addr2a, outputAddr) + + outputAddr, err = s.dymNsKeeper.ResolveByDymNameAddress(s.ctx, "a@dymension_1100-1") + s.Require().NoError(err) + s.Require().Equal(addr1a, outputAddr) + + _, err = s.dymNsKeeper.ResolveByDymNameAddress(s.ctx, "a@dym") + s.Require().Error(err) + s.Require().Contains(err.Error(), "no resolution found") + + _, err = s.dymNsKeeper.ResolveByDymNameAddress(s.ctx, "non-exists.a@dymension_1100-1") + s.Require().Error(err) + s.Require().Contains(err.Error(), "no resolution found") + }, + }, + { + name: "success, multi-sub config, alias", + dymName: &dymnstypes.DymName{ + Name: "a", + Owner: addr1a, + Controller: addr2a, + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{{ + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Path: "c.b", + Value: addr3a, + }, { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Path: "b", + Value: addr2a, + }, { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Path: "", + Value: addr1a, + }}, + }, + preSetup: generalSetupAlias, + dymNameAddress: "c.b.a@dym", + wantOutputAddress: addr3a, + postTest: func(s *KeeperTestSuite) { + var outputAddr string + var err error + + outputAddr, err = s.dymNsKeeper.ResolveByDymNameAddress(s.ctx, "b.a.dym") + s.Require().NoError(err) + s.Require().Equal(addr2a, outputAddr) + + outputAddr, err = s.dymNsKeeper.ResolveByDymNameAddress(s.ctx, "b.a.dymension_1100-1") + s.Require().NoError(err) + s.Require().Equal(addr2a, outputAddr) + + outputAddr, err = s.dymNsKeeper.ResolveByDymNameAddress(s.ctx, "b.a@dymension_1100-1") + s.Require().NoError(err) + s.Require().Equal(addr2a, outputAddr) + + outputAddr, err = s.dymNsKeeper.ResolveByDymNameAddress(s.ctx, "b.a@dym") + s.Require().NoError(err) + s.Require().Equal(addr2a, outputAddr) + + outputAddr, err = s.dymNsKeeper.ResolveByDymNameAddress(s.ctx, "a@dym") + s.Require().NoError(err) + s.Require().Equal(addr1a, outputAddr) + + _, err = s.dymNsKeeper.ResolveByDymNameAddress(s.ctx, "non-exists.a@dym") + s.Require().Error(err) + s.Require().Contains(err.Error(), "no resolution found") + }, + }, + { + name: "success, alias of RollApp", + dymName: &dymnstypes.DymName{ + Name: "a", + Owner: addr1a, + Controller: addr2a, + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{{ + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "nim_1122-1", + Value: addr2Acc.bech32C("nim"), + }, { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "nim_1122-1", + Path: "b", + Value: addr2Acc.bech32C("nim"), + }}, + }, + preSetup: func(s *KeeperTestSuite) { + s.persistRollApp(*newRollApp("nim_1122-1").WithBech32("nim").WithAlias("nim")) + }, + dymNameAddress: "a@nim", + wantOutputAddress: addr2Acc.bech32C("nim"), + postTest: func(s *KeeperTestSuite) { + // should be able to resolve if multiple aliases attached to the same RollApp + + aliases := []string{"nim1", "nim2", "nim3"} + for _, alias := range aliases { + s.Require().NoError(s.dymNsKeeper.SetAliasForRollAppId(s.ctx, "nim_1122-1", alias)) + } + + for _, alias := range aliases { + outputAddr, err := s.dymNsKeeper.ResolveByDymNameAddress(s.ctx, "a@"+alias) + s.Require().NoError(err) + s.Require().Equal(addr2Acc.bech32C("nim"), outputAddr) + + outputAddr, err = s.dymNsKeeper.ResolveByDymNameAddress(s.ctx, "b.a@"+alias) + s.Require().NoError(err) + s.Require().Equal(addr2Acc.bech32C("nim"), outputAddr) + } + }, + }, + { + name: "lookup through multiple sub-domains", + dymName: &dymnstypes.DymName{ + Name: "a", + Owner: addr1a, + Controller: addr2a, + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{{ + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Path: "b", + Value: addr3a, + }, { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Path: "", + Value: addr3a, + }}, + }, + preSetup: func(s *KeeperTestSuite) { + dymNameB := dymnstypes.DymName{ + Name: "b", + Owner: addr1a, + Controller: addr2a, + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{{ + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Path: "b", + Value: addr2a, + }, { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Path: "", + Value: addr2a, + }}, + } + s.Require().NoError(s.dymNsKeeper.SetDymName(s.ctx, dymNameB)) + }, + dymNameAddress: "b.a.dymension_1100-1", + wantOutputAddress: addr3a, + postTest: func(s *KeeperTestSuite) { + var outputAddr string + var err error + + outputAddr, err = s.dymNsKeeper.ResolveByDymNameAddress(s.ctx, "b.dymension_1100-1") + s.Require().NoError(err) + s.Require().Equal(addr2a, outputAddr) + + outputAddr, err = s.dymNsKeeper.ResolveByDymNameAddress(s.ctx, "b@dymension_1100-1") + s.Require().NoError(err) + s.Require().Equal(addr2a, outputAddr) + + outputAddr, err = s.dymNsKeeper.ResolveByDymNameAddress(s.ctx, "b.b.dymension_1100-1") + s.Require().NoError(err) + s.Require().Equal(addr2a, outputAddr) + }, + }, + { + name: "matching by chain-id, no alias", + dymName: &dymnstypes.DymName{ + Name: "a", + Owner: addr1a, + Controller: addr2a, + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{{ + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "b", + Value: addr2a, + }, { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "", + Value: addr2a, + }, { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "blumbus_111-1", + Path: "b", + Value: addr3a, + }, { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "blumbus_111-1", + Path: "", + Value: addr3a, + }}, + }, + dymNameAddress: "a.blumbus_111-1", + wantOutputAddress: addr3a, + postTest: func(s *KeeperTestSuite) { + var outputAddr string + var err error + + outputAddr, err = s.dymNsKeeper.ResolveByDymNameAddress(s.ctx, "a.blumbus_111-1") + s.Require().NoError(err) + s.Require().Equal(addr3a, outputAddr) + + _, err = s.dymNsKeeper.ResolveByDymNameAddress(s.ctx, "a@bb") + s.Require().Error(err) + + _, err = s.dymNsKeeper.ResolveByDymNameAddress(s.ctx, "a@blumbus") + s.Require().Error(err) + + outputAddr, err = s.dymNsKeeper.ResolveByDymNameAddress(s.ctx, "a.dymension_1100-1") + s.Require().NoError(err) + s.Require().Equal(addr2a, outputAddr) + + _, err = s.dymNsKeeper.ResolveByDymNameAddress(s.ctx, "a.dym") + s.Require().Error(err) + + _, err = s.dymNsKeeper.ResolveByDymNameAddress(s.ctx, "a.dymension") + s.Require().Error(err) + }, + }, + { + name: "matching by chain-id", + dymName: &dymnstypes.DymName{ + Name: "a", + Owner: addr1a, + Controller: addr2a, + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{{ + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "b", + Value: addr2a, + }, { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "", + Value: addr2a, + }, { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "blumbus_111-1", + Path: "b", + Value: addr3a, + }, { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "blumbus_111-1", + Path: "", + Value: addr3a, + }}, + }, + preSetup: generalSetupAlias, + dymNameAddress: "a.blumbus_111-1", + wantOutputAddress: addr3a, + postTest: func(s *KeeperTestSuite) { + var outputAddr string + var err error + + outputAddr, err = s.dymNsKeeper.ResolveByDymNameAddress(s.ctx, "a.blumbus_111-1") + s.Require().NoError(err) + s.Require().Equal(addr3a, outputAddr) + + outputAddr, err = s.dymNsKeeper.ResolveByDymNameAddress(s.ctx, "a@bb") + s.Require().NoError(err) + s.Require().Equal(addr3a, outputAddr) + + outputAddr, err = s.dymNsKeeper.ResolveByDymNameAddress(s.ctx, "a@blumbus") + s.Require().NoError(err) + s.Require().Equal(addr3a, outputAddr) + + outputAddr, err = s.dymNsKeeper.ResolveByDymNameAddress(s.ctx, "a.dymension_1100-1") + s.Require().NoError(err) + s.Require().Equal(addr2a, outputAddr) + + outputAddr, err = s.dymNsKeeper.ResolveByDymNameAddress(s.ctx, "a.dym") + s.Require().NoError(err) + s.Require().Equal(addr2a, outputAddr) + + outputAddr, err = s.dymNsKeeper.ResolveByDymNameAddress(s.ctx, "a.dymension") + s.Require().NoError(err) + s.Require().Equal(addr2a, outputAddr) + }, + }, + { + name: "not configured sub-name", + dymName: &dymnstypes.DymName{ + Name: "a", + Owner: addr1a, + Controller: addr2a, + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{{ + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Path: "c.b", + Value: addr3a, + }, { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Path: "", + Value: addr2a, + }}, + }, + dymNameAddress: "b.a.dymension_1100-1", + wantError: true, + wantErrContains: "no resolution found", + }, + { + name: "when Dym-Name does not exists", + dymName: &dymnstypes.DymName{ + Name: "a", + Owner: addr1a, + Controller: addr2a, + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{{ + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Path: "", + Value: addr3a, + }}, + }, + dymNameAddress: "b@dym", + wantError: true, + wantErrContains: "Dym-Name: b: not found", + }, + { + name: "resolve to owner when no Dym-Name config", + dymName: &dymnstypes.DymName{ + Name: "a", + Owner: addr1a, + Controller: addr2a, + ExpireAt: s.now.Unix() + 100, + Configs: nil, + }, + dymNameAddress: "a.dymension_1100-1", + wantError: false, + wantOutputAddress: addr1a, + }, + { + name: "resolve to non-bech32/non-hex", + dymName: &dymnstypes.DymName{ + Name: "a", + Owner: addr1a, + Controller: addr2a, + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{ + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "another", + Path: "", + Value: "X-avax1tzdcgj4ehsvhhgpl7zylwpw0gl2rxcg4r5afk5", + }, + }, + }, + dymNameAddress: "a.another", + wantError: false, + wantOutputAddress: "X-avax1tzdcgj4ehsvhhgpl7zylwpw0gl2rxcg4r5afk5", + }, + { + name: "resolve to non-bech32/non-hex, with sub-name", + dymName: &dymnstypes.DymName{ + Name: "a", + Owner: addr1a, + Controller: addr2a, + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{ + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "another", + Path: "sub1", + Value: "X-avax1tzdcgj4ehsvhhgpl7zylwpw0gl2rxcg4r5afk5", + }, + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "another", + Path: "sub2", + Value: "Ae2tdPwUPEZFSi1cTyL1ZL6bgixhc2vSy5heg6Zg9uP7PpumkAJ82Qprt8b", + }, + }, + }, + dymNameAddress: "sub2.a.another", + wantError: false, + wantOutputAddress: "Ae2tdPwUPEZFSi1cTyL1ZL6bgixhc2vSy5heg6Zg9uP7PpumkAJ82Qprt8b", + postTest: func(s *KeeperTestSuite) { + list, err := s.dymNsKeeper.ReverseResolveDymNameAddress(s.ctx, "Ae2tdPwUPEZFSi1cTyL1ZL6bgixhc2vSy5heg6Zg9uP7PpumkAJ82Qprt8b", "another") + s.Require().NoError(err) + s.Require().Len(list, 1) + s.Require().Equal("sub2.a@another", list[0].String()) + + list, err = s.dymNsKeeper.ReverseResolveDymNameAddress(s.ctx, "X-avax1tzdcgj4ehsvhhgpl7zylwpw0gl2rxcg4r5afk5", "another") + s.Require().NoError(err) + s.Require().Len(list, 1) + s.Require().Equal("sub1.a@another", list[0].String()) + }, + }, + { + name: "resolve to owner when no default (without sub-name) Dym-Name config", + dymName: &dymnstypes.DymName{ + Name: "a", + Owner: addr1a, + Controller: addr2a, + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{ + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Path: "sub", + Value: addr3a, + }, + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "blumbus_111-1", + Path: "", + Value: addr2a, + }, + }, + }, + preSetup: generalSetupAlias, + dymNameAddress: "a.dymension_1100-1", + wantError: false, + wantOutputAddress: addr1a, + postTest: func(s *KeeperTestSuite) { + outputAddr, err := s.dymNsKeeper.ResolveByDymNameAddress(s.ctx, "sub.a.dym") + s.Require().NoError(err) + s.Require().Equal(addr3a, outputAddr) + + _, err = s.dymNsKeeper.ResolveByDymNameAddress(s.ctx, "non-exists.a.dym") + s.Require().Error(err) + s.Require().Contains(err.Error(), "no resolution found") + + outputAddr, err = s.dymNsKeeper.ResolveByDymNameAddress(s.ctx, "a@bb") + s.Require().NoError(err) + s.Require().Equal(addr2a, outputAddr) + }, + }, + { + name: "do not fallback for sub-name", + dymName: &dymnstypes.DymName{ + Name: "a", + Owner: addr1a, + Controller: addr2a, + ExpireAt: s.now.Unix() + 100, + Configs: nil, + }, + dymNameAddress: "sub.a.dymension_1100-1", + wantError: true, + wantErrContains: "no resolution found", + postTest: func(s *KeeperTestSuite) { + outputAddr, err := s.dymNsKeeper.ResolveByDymNameAddress(s.ctx, "a.dymension_1100-1") + s.Require().NoError(err, "should fallback if not sub-name") + s.Require().Equal(addr1a, outputAddr) + }, + }, + { + name: "should not resolve for expired Dym-Name", + dymName: &dymnstypes.DymName{ + Name: "a", + Owner: addr1a, + Controller: addr2a, + ExpireAt: s.now.Unix() - 1, + Configs: []dymnstypes.DymNameConfig{{ + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Path: "", + Value: addr3a, + }}, + }, + dymNameAddress: "a.dymension_1100-1", + wantError: true, + wantErrContains: "Dym-Name: a: not found", + }, + { + name: "should not resolve for expired Dym-Name", + dymName: &dymnstypes.DymName{ + Name: "a", + Owner: addr1a, + Controller: addr2a, + ExpireAt: s.now.Unix() - 1, + Configs: []dymnstypes.DymNameConfig{{ + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Path: "", + Value: addr3a, + }}, + }, + dymNameAddress: "a.a.dymension_1100-1", + wantError: true, + wantErrContains: "Dym-Name: a: not found", + }, + { + name: "should not resolve if input addr is invalid", + dymName: &dymnstypes.DymName{ + Name: "a", + Owner: addr1a, + Controller: addr2a, + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{{ + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Path: "", + Value: addr3a, + }}, + }, + dymNameAddress: "a@a.dymension_1100-1", + wantError: true, + wantErrContains: dymnstypes.ErrBadDymNameAddress.Error(), + }, + { + name: "if alias collision with configured record, priority configuration", + dymName: &dymnstypes.DymName{ + Name: "a", + Owner: addr1a, + Controller: addr2a, + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{ + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "blumbus_111-1", + Path: "", + Value: addr2a, + }, + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "blumbus", + Path: "", + Value: addr3a, + }, + }, + }, + preSetup: func(s *KeeperTestSuite) { + params := s.dymNsKeeper.GetParams(s.ctx) + params.Chains.AliasesOfChainIds = []dymnstypes.AliasesOfChainId{ + { + ChainId: "blumbus_111-1", + Aliases: []string{"blumbus"}, + }, + } + err := s.dymNsKeeper.SetParams(s.ctx, params) + s.Require().NoError(err) + }, + dymNameAddress: "a.blumbus", + wantError: false, + wantOutputAddress: addr3a, + postTest: func(s *KeeperTestSuite) { + outputAddr, err := s.dymNsKeeper.ResolveByDymNameAddress(s.ctx, "a@blumbus_111-1") + s.Require().NoError(err) + s.Require().Equal(addr2a, outputAddr) + }, + }, + { + name: "resolve extra format 0x1234...6789@dym", + dymName: nil, + preSetup: generalSetupAlias, + dymNameAddress: "0x1234567890123456789012345678901234567890@dymension_1100-1", + wantError: false, + wantOutputAddress: "dym1zg69v7yszg69v7yszg69v7yszg69v7ys8xdv96", + postTest: func(s *KeeperTestSuite) { + outputAddr, err := s.dymNsKeeper.ResolveByDymNameAddress(s.ctx, "0x1234567890123456789012345678901234567890.dym") + s.Require().NoError(err) + s.Require().Equal("dym1zg69v7yszg69v7yszg69v7yszg69v7ys8xdv96", outputAddr) + }, + }, + { + name: "resolve extra format 0x1234...6789@dym, do not resolve if chain-id is unknown", + dymName: nil, + preSetup: generalSetupAlias, + dymNameAddress: "0x1234567890123456789012345678901234567890@unknown-1", + wantError: true, + wantErrContains: "Dym-Name: 0x1234567890123456789012345678901234567890: not found", + wantOutputAddress: "", + }, + { + name: "resolve extra format 0x1234...6789@dym, do not resolve if chain-id is not RollApp, even tho alias was defined", + dymName: nil, + preSetup: func(s *KeeperTestSuite) { + params := s.dymNsKeeper.GetParams(s.ctx) + params.Chains.AliasesOfChainIds = []dymnstypes.AliasesOfChainId{ + { + ChainId: "blumbus_111-1", + Aliases: []string{"blumbus"}, + }, + } + err := s.dymNsKeeper.SetParams(s.ctx, params) + s.Require().NoError(err) + }, + dymNameAddress: "0x1234567890123456789012345678901234567890@blumbus", + wantError: true, + wantErrContains: "Dym-Name: 0x1234567890123456789012345678901234567890: not found", + wantOutputAddress: "", + }, + { + name: "resolve extra format 0x1234...6789@dym, Interchain Account", + dymName: nil, + preSetup: generalSetupAlias, + dymNameAddress: "0x1234567890123456789012345678901234567890123456789012345678901234@dymension_1100-1", + wantError: false, + wantOutputAddress: "dym1zg69v7yszg69v7yszg69v7yszg69v7yszg69v7yszg69v7yszg6qrz80ul", + postTest: func(s *KeeperTestSuite) { + outputAddr, err := s.dymNsKeeper.ResolveByDymNameAddress(s.ctx, "0x1234567890123456789012345678901234567890123456789012345678901234.dym") + s.Require().NoError(err) + s.Require().Equal("dym1zg69v7yszg69v7yszg69v7yszg69v7yszg69v7yszg69v7yszg6qrz80ul", outputAddr) + }, + }, + { + name: "resolve extra format nim1...@dym, cross bech32 format", + dymName: nil, + preSetup: generalSetupAlias, + dymNameAddress: "nim1zg69v7yszg69v7yszg69v7yszg69v7yspkhdt9@dymension_1100-1", + wantError: false, + wantOutputAddress: "dym1zg69v7yszg69v7yszg69v7yszg69v7ys8xdv96", + postTest: func(s *KeeperTestSuite) { + outputAddr, err := s.dymNsKeeper.ResolveByDymNameAddress(s.ctx, "nim1zg69v7yszg69v7yszg69v7yszg69v7yspkhdt9.dym") + s.Require().NoError(err) + s.Require().Equal("dym1zg69v7yszg69v7yszg69v7yszg69v7ys8xdv96", outputAddr) + }, + }, + { + name: "fallback resolve follow default config", + dymName: &dymnstypes.DymName{ + Name: "a", + Owner: addr1a, + Controller: addr1a, + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{ + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Value: addr2Acc.bech32(), + }, + }, + }, + preSetup: func(s *KeeperTestSuite) { + s.persistRollApp( + *newRollApp("nim_1122-1").WithBech32("nim").WithAlias("nim"), + ) + }, + dymNameAddress: "a@nim", + wantError: false, + wantOutputAddress: addr2Acc.bech32C("nim"), + postTest: nil, + }, + { + name: "resolve extra format 0x1234...6789@nim (RollApp)", + dymName: nil, + preSetup: func(s *KeeperTestSuite) { + s.persistRollApp(*newRollApp("nim_1122-1").WithBech32("nim").WithAlias("nim")) + }, + dymNameAddress: "0x1234567890123456789012345678901234567890@nim_1122-1", + wantError: false, + wantOutputAddress: "nim1zg69v7yszg69v7yszg69v7yszg69v7yspkhdt9", + postTest: func(s *KeeperTestSuite) { + outputAddr, err := s.dymNsKeeper.ResolveByDymNameAddress(s.ctx, "0x1234567890123456789012345678901234567890.nim") + s.Require().NoError(err) + s.Require().Equal("nim1zg69v7yszg69v7yszg69v7yszg69v7yspkhdt9", outputAddr) + }, + }, + { + name: "resolve extra format 0x1234...6789@nim1 (RollApp), alternative alias among multiple aliases for RollApp", + dymName: nil, + preSetup: func(s *KeeperTestSuite) { + s.persistRollApp(*newRollApp("nim_1122-1").WithBech32("nim").WithAlias("nim")) + s.Require().NoError(s.dymNsKeeper.SetAliasForRollAppId(s.ctx, "nim_1122-1", "nim1")) + s.Require().NoError(s.dymNsKeeper.SetAliasForRollAppId(s.ctx, "nim_1122-1", "nim2")) + }, + dymNameAddress: "0x1234567890123456789012345678901234567890@nim1", + wantError: false, + wantOutputAddress: "nim1zg69v7yszg69v7yszg69v7yszg69v7yspkhdt9", + postTest: func(s *KeeperTestSuite) { + outputAddr, err := s.dymNsKeeper.ResolveByDymNameAddress(s.ctx, "0x1234567890123456789012345678901234567890.nim") + s.Require().NoError(err) + s.Require().Equal("nim1zg69v7yszg69v7yszg69v7yszg69v7yspkhdt9", outputAddr) + }, + }, + { + name: "resolve extra format dym1...@nim (RollApp), cross bech32 format", + dymName: nil, + preSetup: func(s *KeeperTestSuite) { + s.persistRollApp(*newRollApp("nim_1122-1").WithBech32("nim").WithAlias("nim")) + }, + dymNameAddress: "dym1zg69v7yszg69v7yszg69v7yszg69v7ys8xdv96@nim_1122-1", + wantError: false, + wantOutputAddress: "nim1zg69v7yszg69v7yszg69v7yszg69v7yspkhdt9", + postTest: func(s *KeeperTestSuite) { + outputAddr, err := s.dymNsKeeper.ResolveByDymNameAddress(s.ctx, "dym1zg69v7yszg69v7yszg69v7yszg69v7ys8xdv96.nim") + s.Require().NoError(err) + s.Require().Equal("nim1zg69v7yszg69v7yszg69v7yszg69v7yspkhdt9", outputAddr) + }, + }, + { + name: "resolve extra format dym1...@nim1 (RollApp), cross bech32 format, alternative alias among multiple aliases for RollApp", + dymName: nil, + preSetup: func(s *KeeperTestSuite) { + s.persistRollApp(*newRollApp("nim_1122-1").WithBech32("nim").WithAlias("nim")) + s.Require().NoError(s.dymNsKeeper.SetAliasForRollAppId(s.ctx, "nim_1122-1", "nim1")) + s.Require().NoError(s.dymNsKeeper.SetAliasForRollAppId(s.ctx, "nim_1122-1", "nim2")) + }, + dymNameAddress: "dym1zg69v7yszg69v7yszg69v7yszg69v7ys8xdv96@nim1", + wantError: false, + wantOutputAddress: "nim1zg69v7yszg69v7yszg69v7yszg69v7yspkhdt9", + postTest: func(s *KeeperTestSuite) { + outputAddr, err := s.dymNsKeeper.ResolveByDymNameAddress(s.ctx, "dym1zg69v7yszg69v7yszg69v7yszg69v7ys8xdv96.nim") + s.Require().NoError(err) + s.Require().Equal("nim1zg69v7yszg69v7yszg69v7yszg69v7yspkhdt9", outputAddr) + }, + }, + { + name: "try resolve extra format dym1...@rollapp, cross bech32 format, but RollApp does not have bech32 configured", + dymName: nil, + preSetup: func(s *KeeperTestSuite) { + s.persistRollApp( + *newRollApp("rollapp_1-1"), + ) + }, + dymNameAddress: "dym1zg69v7yszg69v7yszg69v7yszg69v7ys8xdv96@rollapp_1-1", + wantError: true, + wantErrContains: "not found", + }, + } + for _, tt := range tests { + s.Run(tt.name, func() { + s.RefreshContext() + + if tt.preSetup != nil { + tt.preSetup(s) + } + + if tt.dymName != nil { + s.setDymNameWithFunctionsAfter(*tt.dymName) + } + + outputAddress, err := s.dymNsKeeper.ResolveByDymNameAddress(s.ctx, tt.dymNameAddress) + + defer func() { + if s.T().Failed() { + return + } + + if tt.postTest != nil { + tt.postTest(s) + } + }() + + if tt.wantError { + s.Require().NotEmpty(tt.wantErrContains, "mis-configured test case") + s.Require().Error(err) + s.Require().Contains(err.Error(), tt.wantErrContains) + return + } + + s.Require().NoError(err) + s.Require().Equal(tt.wantOutputAddress, outputAddress) + }) + } + + s.Run("mixed tests", func() { + s.RefreshContext() + + bech32Addr := func(no uint64) string { + return testAddr(no).bech32() + } + + // setup alias + moduleParams := s.dymNsKeeper.GetParams(s.ctx) + moduleParams.Chains.AliasesOfChainIds = []dymnstypes.AliasesOfChainId{ + { + ChainId: s.chainId, + Aliases: []string{"dym"}, + }, + { + ChainId: "blumbus_111-1", + Aliases: []string{"bb"}, + }, + { + ChainId: "froopyland_100-1", + Aliases: nil, + }, + { + ChainId: "cosmoshub-4", + Aliases: []string{"cosmos"}, + }, + } + s.Require().NoError(s.dymNsKeeper.SetParams(s.ctx, moduleParams)) + + // setup Dym-Names + dymName1 := dymnstypes.DymName{ + Name: "name1", + Owner: bech32Addr(1), + Controller: bech32Addr(2), + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{ + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "s1", + Value: bech32Addr(3), + }, + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "s2", + Value: bech32Addr(4), + }, + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "a.s5", + Value: bech32Addr(5), + }, + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "blumbus_111-1", + Path: "b", + Value: bech32Addr(6), + }, + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "blumbus_111-1", + Path: "c.b", + Value: bech32Addr(7), + }, + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "juno-1", + Path: "", + Value: bech32Addr(8), + }, + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "juno-1", + Path: "a.b.c", + Value: bech32Addr(9), + }, + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "cosmoshub-4", + Path: "", + Value: bech32Addr(10), + }, + }, + } + s.Require().NoError(s.dymNsKeeper.SetDymName(s.ctx, dymName1)) + + dymName2 := dymnstypes.DymName{ + Name: "name2", + Owner: bech32Addr(100), + Controller: bech32Addr(101), + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{ + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "s1", + Value: bech32Addr(103), + }, + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "s2", + Value: bech32Addr(104), + }, + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "a.s5", + Value: bech32Addr(105), + }, + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "blumbus_111-1", + Path: "b", + Value: bech32Addr(106), + }, + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "blumbus_111-1", + Path: "c.b", + Value: bech32Addr(107), + }, + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "juno-1", + Path: "", + Value: bech32Addr(108), + }, + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "juno-1", + Path: "a.b.c", + Value: bech32Addr(109), + }, + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "froopyland_100-1", + Path: "a", + Value: bech32Addr(110), + }, + }, + } + s.Require().NoError(s.dymNsKeeper.SetDymName(s.ctx, dymName2)) + + dymName3 := dymnstypes.DymName{ + Name: "name3", + Owner: bech32Addr(200), + Controller: bech32Addr(201), + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{ + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "s1", + Value: bech32Addr(203), + }, + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "s2", + Value: bech32Addr(204), + }, + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "a.s5", + Value: bech32Addr(205), + }, + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "blumbus_111-1", + Path: "b", + Value: bech32Addr(206), + }, + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "blumbus_111-1", + Path: "c.b", + Value: bech32Addr(207), + }, + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "juno-1", + Path: "", + Value: bech32Addr(208), + }, + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "juno-1", + Path: "a.b.c", + Value: bech32Addr(209), + }, + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "froopyland_100-1", + Path: "a", + Value: bech32Addr(210), + }, + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "cosmoshub-4", + Path: "a", + Value: bech32Addr(211), + }, + }, + } + s.Require().NoError(s.dymNsKeeper.SetDymName(s.ctx, dymName3)) + + dymName4 := dymnstypes.DymName{ + Name: "name4", + Owner: "dym1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqp7vezn", + Controller: bech32Addr(301), + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{ + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "s1", + Value: bech32Addr(302), + }, + }, + } + s.Require().NoError(s.dymNsKeeper.SetDymName(s.ctx, dymName4)) + + rollAppNim := rollapptypes.Rollapp{ + RollappId: "nim_1122-1", + Owner: bech32Addr(1122), + } + s.persistRollApp(*newRollApp(rollAppNim.RollappId).WithBech32("nim").WithAlias("nim")) + rollAppNim, found := s.rollAppKeeper.GetRollapp(s.ctx, rollAppNim.RollappId) + s.Require().True(found) + + tc := func(name, chainIdOrAlias string) input { + return newInputTestcase(name, chainIdOrAlias, s) + } + + tc("name1", s.chainId).WithSubName("s1").RequireResolveTo(bech32Addr(3)) + tc("name1", "dym").WithSubName("s1").RequireResolveTo(bech32Addr(3)) + tc("name1", s.chainId).WithSubName("s2").RequireResolveTo(bech32Addr(4)) + tc("name1", "dym").WithSubName("s2").RequireResolveTo(bech32Addr(4)) + tc("name1", s.chainId).WithSubName("a.s5").RequireResolveTo(bech32Addr(5)) + tc("name1", "dym").WithSubName("a.s5").RequireResolveTo(bech32Addr(5)) + tc("name1", s.chainId).WithSubName("none").RequireNotResolve() + tc("name1", "dym").WithSubName("none").RequireNotResolve() + tc("name1", "blumbus_111-1").WithSubName("b").RequireResolveTo(bech32Addr(6)) + tc("name1", "bb").WithSubName("b").RequireResolveTo(bech32Addr(6)) + tc("name1", "blumbus_111-1").WithSubName("c.b").RequireResolveTo(bech32Addr(7)) + tc("name1", "bb").WithSubName("c.b").RequireResolveTo(bech32Addr(7)) + tc("name1", "blumbus_111-1").WithSubName("none").RequireNotResolve() + tc("name1", "bb").WithSubName("none").RequireNotResolve() + tc("name1", "juno-1").RequireResolveTo(bech32Addr(8)) + tc("name1", "juno-1").WithSubName("a.b.c").RequireResolveTo(bech32Addr(9)) + tc("name1", "juno-1").WithSubName("none").RequireNotResolve() + tc("name1", "cosmoshub-4").RequireResolveTo(bech32Addr(10)) + tc("name1", "cosmos").RequireResolveTo(bech32Addr(10)) + + tc("name2", s.chainId).WithSubName("s1").RequireResolveTo(bech32Addr(103)) + tc("name2", "dym").WithSubName("s1").RequireResolveTo(bech32Addr(103)) + tc("name2", s.chainId).WithSubName("s2").RequireResolveTo(bech32Addr(104)) + tc("name2", "dym").WithSubName("s2").RequireResolveTo(bech32Addr(104)) + tc("name2", s.chainId).WithSubName("a.s5").RequireResolveTo(bech32Addr(105)) + tc("name2", "dym").WithSubName("a.s5").RequireResolveTo(bech32Addr(105)) + tc("name2", s.chainId).WithSubName("none").RequireNotResolve() + tc("name2", "dym").WithSubName("none").RequireNotResolve() + tc("name2", "blumbus_111-1").WithSubName("b").RequireResolveTo(bech32Addr(106)) + tc("name2", "bb").WithSubName("b").RequireResolveTo(bech32Addr(106)) + tc("name2", "blumbus_111-1").WithSubName("c.b").RequireResolveTo(bech32Addr(107)) + tc("name2", "bb").WithSubName("c.b").RequireResolveTo(bech32Addr(107)) + tc("name2", "blumbus_111-1").WithSubName("none").RequireNotResolve() + tc("name2", "bb").WithSubName("none").RequireNotResolve() + tc("name2", "juno-1").RequireResolveTo(bech32Addr(108)) + tc("name2", "juno-1").WithSubName("a.b.c").RequireResolveTo(bech32Addr(109)) + tc("name2", "juno-1").WithSubName("none").RequireNotResolve() + tc("name2", "froopyland_100-1").WithSubName("a").RequireResolveTo(bech32Addr(110)) + tc("name2", "froopyland").WithSubName("a").RequireNotResolve() + tc("name2", "cosmoshub-4").RequireNotResolve() + tc("name2", "cosmoshub-4").WithSubName("a").RequireNotResolve() + + tc("name3", s.chainId).WithSubName("s1").RequireResolveTo(bech32Addr(203)) + tc("name3", "dym").WithSubName("s1").RequireResolveTo(bech32Addr(203)) + tc("name3", s.chainId).WithSubName("s2").RequireResolveTo(bech32Addr(204)) + tc("name3", "dym").WithSubName("s2").RequireResolveTo(bech32Addr(204)) + tc("name3", s.chainId).WithSubName("a.s5").RequireResolveTo(bech32Addr(205)) + tc("name3", "dym").WithSubName("a.s5").RequireResolveTo(bech32Addr(205)) + tc("name3", s.chainId).WithSubName("none").RequireNotResolve() + tc("name3", "dym").WithSubName("none").RequireNotResolve() + tc("name3", "blumbus_111-1").WithSubName("b").RequireResolveTo(bech32Addr(206)) + tc("name3", "bb").WithSubName("b").RequireResolveTo(bech32Addr(206)) + tc("name3", "blumbus_111-1").WithSubName("c.b").RequireResolveTo(bech32Addr(207)) + tc("name3", "bb").WithSubName("c.b").RequireResolveTo(bech32Addr(207)) + tc("name3", "blumbus_111-1").WithSubName("none").RequireNotResolve() + tc("name3", "bb").WithSubName("none").RequireNotResolve() + tc("name3", "juno-1").RequireResolveTo(bech32Addr(208)) + tc("name3", "juno-1").WithSubName("a.b.c").RequireResolveTo(bech32Addr(209)) + tc("name3", "juno-1").WithSubName("none").RequireNotResolve() + tc("name3", "froopyland_100-1").WithSubName("a").RequireResolveTo(bech32Addr(210)) + tc("name3", "froopyland").WithSubName("a").RequireNotResolve() + tc("name3", "cosmoshub-4").RequireNotResolve() + tc("name3", "cosmos").WithSubName("a").RequireResolveTo(bech32Addr(211)) + + tc("name4", s.chainId).WithSubName("s1").RequireResolveTo(bech32Addr(302)) + tc("name4", "dym").WithSubName("s1").RequireResolveTo(bech32Addr(302)) + tc("name4", s.chainId).WithSubName("none").RequireNotResolve() + tc("name4", "dym").WithSubName("none").RequireNotResolve() + tc("name4", s.chainId).RequireResolveTo("dym1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqp7vezn") + tc("name4", "dym").RequireResolveTo("dym1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqp7vezn") + tc("name4", rollAppNim.RollappId).RequireResolveTo( + "nim1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq8wkcvv", + ) + }) +} + +type input struct { + s *KeeperTestSuite + // + name string + chainIdOrAlias string + subName string +} + +func newInputTestcase(name, chainIdOrAlias string, s *KeeperTestSuite) input { + return input{name: name, chainIdOrAlias: chainIdOrAlias, s: s} +} + +func (m input) WithSubName(subName string) input { + m.subName = subName + return m +} + +func (m input) buildDymNameAddrsCases() []string { + var dymNameAddrs []string + func() { + dymNameAddr := m.name + "." + m.chainIdOrAlias + if len(m.subName) > 0 { + dymNameAddr = m.subName + "." + dymNameAddr + } + dymNameAddrs = append(dymNameAddrs, dymNameAddr) + }() + func() { + dymNameAddr := m.name + "@" + m.chainIdOrAlias + if len(m.subName) > 0 { + dymNameAddr = m.subName + "." + dymNameAddr + } + dymNameAddrs = append(dymNameAddrs, dymNameAddr) + }() + return dymNameAddrs +} + +func (m input) RequireNotResolve() { + for _, dymNameAddr := range m.buildDymNameAddrsCases() { + _, err := m.s.dymNsKeeper.ResolveByDymNameAddress(m.s.ctx, dymNameAddr) + m.s.Require().Error(err) + } +} + +func (m input) RequireResolveTo(wantAddr string) { + for _, dymNameAddr := range m.buildDymNameAddrsCases() { + gotAddr, err := m.s.dymNsKeeper.ResolveByDymNameAddress(m.s.ctx, dymNameAddr) + m.s.Require().NoError(err) + m.s.Require().Equal(wantAddr, gotAddr) + } +} + +//goland:noinspection SpellCheckingInspection +func (s *KeeperTestSuite) Test_ParseDymNameAddress() { + tests := []struct { + name string + dymNameAddress string + wantErr bool + wantErrContains string + wantSubName string + wantDymName string + wantChainIdOrAlias string + }{ + { + name: "pass - valid input, no sub-name, chain-id, @", + dymNameAddress: "a@dymension_1100-1", + wantDymName: "a", + wantChainIdOrAlias: "dymension_1100-1", + }, + { + name: "pass - valid input, no sub-name, chain-id", + dymNameAddress: "a.dymension_1100-1", + wantDymName: "a", + wantChainIdOrAlias: "dymension_1100-1", + }, + { + name: "pass - valid input, no sub-name, alias, @", + dymNameAddress: "a@dym", + wantDymName: "a", + wantChainIdOrAlias: "dym", + }, + { + name: "pass - valid input, no sub-name, alias", + dymNameAddress: "a.dym", + wantDymName: "a", + wantChainIdOrAlias: "dym", + }, + { + name: "pass - valid input, sub-name, chain-id, @", + dymNameAddress: "b.a@dymension_1100-1", + wantSubName: "b", + wantDymName: "a", + wantChainIdOrAlias: "dymension_1100-1", + }, + { + name: "pass - valid input, sub-name, chain-id", + dymNameAddress: "b.a.dymension_1100-1", + wantSubName: "b", + wantDymName: "a", + wantChainIdOrAlias: "dymension_1100-1", + }, + { + name: "pass - valid input, sub-name, alias, @", + dymNameAddress: "b.a@dym", + wantSubName: "b", + wantDymName: "a", + wantChainIdOrAlias: "dym", + }, + { + name: "pass - valid input, sub-name, alias", + dymNameAddress: "b.a.dym", + wantSubName: "b", + wantDymName: "a", + wantChainIdOrAlias: "dym", + }, + { + name: "pass - valid input, multi-sub-name, chain-id, @", + dymNameAddress: "c.b.a@dymension_1100-1", + wantSubName: "c.b", + wantDymName: "a", + wantChainIdOrAlias: "dymension_1100-1", + }, + { + name: "pass - valid input, multi-sub-name, chain-id", + dymNameAddress: "c.b.a.dymension_1100-1", + wantSubName: "c.b", + wantDymName: "a", + wantChainIdOrAlias: "dymension_1100-1", + }, + { + name: "pass - valid input, multi-sub-name, alias, @", + dymNameAddress: "c.b.a@dym", + wantSubName: "c.b", + wantDymName: "a", + wantChainIdOrAlias: "dym", + }, + { + name: "pass - valid input, multi-sub-name, alias", + dymNameAddress: "c.b.a.dym", + wantSubName: "c.b", + wantDymName: "a", + wantChainIdOrAlias: "dym", + }, + { + name: "fail - invalid '.' after '@', no sub-name", + dymNameAddress: "a@dymension_1100-1.dym", + wantErr: true, + wantErrContains: "misplaced '.'", + }, + { + name: "fail - invalid '.' after '@', sub-name", + dymNameAddress: "a.b@dymension_1100-1.dym", + wantErr: true, + wantErrContains: "misplaced '.'", + }, + { + name: "fail - invalid '.' after '@', multi-sub-name", + dymNameAddress: "a.b.c@dymension_1100-1.dym", + wantErr: true, + wantErrContains: "misplaced '.'", + }, + { + name: "fail - missing chain-id/alias, @", + dymNameAddress: "a@", + wantErr: true, + wantErrContains: dymnstypes.ErrBadDymNameAddress.Error(), + }, + { + name: "fail - missing chain-id/alias", + dymNameAddress: "a", + wantErr: true, + wantErrContains: dymnstypes.ErrBadDymNameAddress.Error(), + }, + { + name: "fail - missing chain-id/alias", + dymNameAddress: "a.", + wantErr: true, + wantErrContains: dymnstypes.ErrBadDymNameAddress.Error(), + }, + { + name: "fail - not accept space, no sub-name", + dymNameAddress: "a .dym", + wantErr: true, + wantErrContains: dymnstypes.ErrBadDymNameAddress.Error(), + }, + { + name: "fail - not accept space, sub-name", + dymNameAddress: "b .a.dym", + wantErr: true, + wantErrContains: dymnstypes.ErrBadDymNameAddress.Error(), + }, + { + name: "fail - not accept space, multi-sub-name", + dymNameAddress: "c.b .a.dym", + wantErr: true, + wantErrContains: dymnstypes.ErrBadDymNameAddress.Error(), + }, + { + name: "fail - invalid chain-id/alias, @", + dymNameAddress: "a@-dym", + wantErr: true, + wantErrContains: "chain-id/alias is not well-formed", + }, + { + name: "fail - invalid chain-id/alias", + dymNameAddress: "a.-dym", + wantErr: true, + wantErrContains: "chain-id/alias is not well-formed", + }, + { + name: "fail - invalid Dym-Name, @", + dymNameAddress: "-a@dym", + wantErr: true, + wantErrContains: "Dym-Name is not well-formed", + }, + { + name: "fail - invalid Dym-Name", + dymNameAddress: "-a.dym", + wantErr: true, + wantErrContains: "Dym-Name is not well-formed", + }, + { + name: "fail - invalid sub-Dym-Name, @", + dymNameAddress: "-b.a@dym", + wantErr: true, + wantErrContains: "Sub-Dym-Name part is not well-formed", + }, + { + name: "fail - invalid sub-Dym-Name", + dymNameAddress: "-b.a.dym", + wantErr: true, + wantErrContains: "Sub-Dym-Name part is not well-formed", + }, + { + name: "fail - invalid multi-sub-Dym-Name, @", + dymNameAddress: "c-.b.a@dym", + wantErr: true, + wantErrContains: "Sub-Dym-Name part is not well-formed", + }, + { + name: "fail - invalid multi-sub-Dym-Name", + dymNameAddress: "c-.b.a.dym", + wantErr: true, + wantErrContains: "Sub-Dym-Name part is not well-formed", + }, + { + name: "fail - blank path", + dymNameAddress: "b. .a.dym", + wantErr: true, + wantErrContains: dymnstypes.ErrBadDymNameAddress.Error(), + }, + { + name: "fail - do not accept continuous dot", + dymNameAddress: "b..a.dym", + wantErr: true, + wantErrContains: dymnstypes.ErrBadDymNameAddress.Error(), + }, + { + name: "fail - do not accept continuous '@'", + dymNameAddress: "a@@dym", + wantErr: true, + wantErrContains: "multiple '@' found", + }, + { + name: "fail - do not accept continuous '@'", + dymNameAddress: "b.a@@dym", + wantErr: true, + wantErrContains: "multiple '@' found", + }, + { + name: "fail - do not accept multiple '@'", + dymNameAddress: "b@a@dym", + wantErr: true, + wantErrContains: "multiple '@' found", + }, + { + name: "fail - do not accept multiple '@'", + dymNameAddress: "@a@dym", + wantErr: true, + wantErrContains: "multiple '@' found", + }, + { + name: "fail - do not accept multiple '@'", + dymNameAddress: "@a.b@dym", + wantErr: true, + wantErrContains: "multiple '@' found", + }, + { + name: "fail - do not accept multiple '@'", + dymNameAddress: "a@b@dym", + wantErr: true, + wantErrContains: "multiple '@' found", + }, + { + name: "fail - bad name", + dymNameAddress: "a.@dym", + wantErr: true, + wantErrContains: dymnstypes.ErrBadDymNameAddress.Error(), + }, + { + name: "fail - bad name", + dymNameAddress: "a.b.@dym", + wantErr: true, + wantErrContains: dymnstypes.ErrBadDymNameAddress.Error(), + }, + { + name: "fail - bad name", + dymNameAddress: "a.b@.dym", + wantErr: true, + wantErrContains: dymnstypes.ErrBadDymNameAddress.Error(), + }, + { + name: "fail - bad name", + dymNameAddress: "a.b.@.dym", + wantErr: true, + wantErrContains: dymnstypes.ErrBadDymNameAddress.Error(), + }, + { + name: "fail - bad name", + dymNameAddress: ".b.a.dym", + wantErr: true, + wantErrContains: dymnstypes.ErrBadDymNameAddress.Error(), + }, + { + name: "fail - bad name", + dymNameAddress: ".b.a@dym", + wantErr: true, + wantErrContains: dymnstypes.ErrBadDymNameAddress.Error(), + }, + { + name: "fail - empty input", + dymNameAddress: "", + wantErr: true, + wantErrContains: dymnstypes.ErrBadDymNameAddress.Error(), + }, + { + name: "pass - allow hex address pattern", + dymNameAddress: "0x1234567890123456789012345678901234567890@dym", + wantErr: false, + wantSubName: "", + wantDymName: "0x1234567890123456789012345678901234567890", + wantChainIdOrAlias: "dym", + }, + { + name: "pass - allow 32 bytes hex address pattern", + dymNameAddress: "0x1234567890123456789012345678901234567890123456789012345678901234@dym", + wantErr: false, + wantSubName: "", + wantDymName: "0x1234567890123456789012345678901234567890123456789012345678901234", + wantChainIdOrAlias: "dym", + }, + { + name: "fail - reject non-20 or 32 bytes hex address pattern, case 19 bytes", + dymNameAddress: "0x123456789012345678901234567890123456789@dym", + wantErr: true, + wantErrContains: dymnstypes.ErrBadDymNameAddress.Error(), + }, + { + name: "fail - reject non-20 or 32 bytes hex address pattern, case 21 bytes", + dymNameAddress: "0x12345678901234567890123456789012345678901@dym", + wantErr: true, + wantErrContains: dymnstypes.ErrBadDymNameAddress.Error(), + }, + { + name: "fail - reject non-20 or 32 bytes hex address pattern, case 31 bytes", + dymNameAddress: "0x123456789012345678901234567890123456789012345678901234567890123@dym", + wantErr: true, + wantErrContains: dymnstypes.ErrBadDymNameAddress.Error(), + }, + { + name: "fail - reject non-20 or 32 bytes hex address pattern, case 33 bytes", + dymNameAddress: "0x12345678901234567890123456789012345678901234567890123456789012345@dym", + wantErr: true, + wantErrContains: dymnstypes.ErrBadDymNameAddress.Error(), + }, + { + name: "pass - allow valid bech32 address pattern", + dymNameAddress: "dym1zg69v7yszg69v7yszg69v7yszg69v7ys8xdv96@dym", + wantErr: false, + wantSubName: "", + wantDymName: "dym1zg69v7yszg69v7yszg69v7yszg69v7ys8xdv96", + wantChainIdOrAlias: "dym", + }, + { + name: "pass - allow valid bech32 address pattern, Interchain Account", + dymNameAddress: "dym1zg69v7yszg69v7yszg69v7yszg69v7yszg69v7yszg69v7yszg6qrz80ul@dym", + wantErr: false, + wantSubName: "", + wantDymName: "dym1zg69v7yszg69v7yszg69v7yszg69v7yszg69v7yszg69v7yszg6qrz80ul", + wantChainIdOrAlias: "dym", + }, + { + name: "fail - reject invalid bech32 address pattern", + dymNameAddress: "dym1zzzzzzzzzz69v7yszg69v7yszg69v7ys8xdv96@dym", + wantErr: true, + wantErrContains: dymnstypes.ErrBadDymNameAddress.Error(), + }, + { + name: "fail - reject invalid bech32 address pattern, Interchain Account", + dymNameAddress: "dym1zzzzzzzzzg69v7yszg69v7yszg69v7yszg69v7yszg69v7yszg6qrz80ul@dym", + wantErr: true, + wantErrContains: dymnstypes.ErrBadDymNameAddress.Error(), + }, + } + for _, tt := range tests { + s.Run(tt.name, func() { + gotSubName, gotDymName, gotChainIdOrAlias, err := dymnskeeper.ParseDymNameAddress(tt.dymNameAddress) + if tt.wantErr { + s.Require().NotEmpty(tt.wantErrContains, "mis-configured test case") + s.Require().Error(err) + s.Require().Contains(err.Error(), tt.wantErrContains) + + // cross-check ResolveByDymNameAddress + + _, err2 := s.dymNsKeeper.ResolveByDymNameAddress(s.ctx, tt.dymNameAddress) + s.Require().NotNil(err2, "when invalid address passed in, ResolveByDymNameAddress should return false") + return + } + + s.Require().NoError(err) + s.Require().Equal(tt.wantSubName, gotSubName) + s.Require().Equal(tt.wantDymName, gotDymName) + s.Require().Equal(tt.wantChainIdOrAlias, gotChainIdOrAlias) + }) + } +} + +//goland:noinspection SpellCheckingInspection +func (s *KeeperTestSuite) TestKeeper_ReverseResolveDymNameAddress() { + const rollAppId1 = "rollapp_1-1" + const rollApp1Bech32 = "nim" + const rollAppId2 = "rollapp_2-2" + const rollApp2Bech32 = "man" + const rollApp2Alias = "ral" + + ownerAcc := testAddr(1) + anotherAcc := testAddr(2) + icaAcc := testICAddr(3) + + tests := []struct { + name string + dymNames []dymnstypes.DymName + additionalSetup func(*KeeperTestSuite) + inputAddress string + workingChainId string + wantErr bool + wantErrContains string + want dymnstypes.ReverseResolvedDymNameAddresses + }{ + { + name: "pass - can resolve bech32 on host-chain", + dymNames: newDN("a", ownerAcc.bech32()). + exp(s.now, +1). + cfgN("", "b", ownerAcc.bech32()). + cfgN("blumbus_111-1", "bb", ownerAcc.bech32()). + buildSlice(), + additionalSetup: nil, + inputAddress: ownerAcc.bech32(), + workingChainId: s.chainId, + wantErr: false, + want: dymnstypes.ReverseResolvedDymNameAddresses{ + { + SubName: "", + Name: "a", + ChainIdOrAlias: s.chainId, + }, + { + SubName: "b", + Name: "a", + ChainIdOrAlias: s.chainId, + }, + }, + }, + { + name: "pass - can resolve bech32 on RollApp", + dymNames: newDN("a", ownerAcc.bech32()). + exp(s.now, +1). + cfgN(rollAppId1, "", ownerAcc.bech32C("ra")). + buildSlice(), + additionalSetup: nil, + inputAddress: ownerAcc.bech32(), + workingChainId: rollAppId1, + wantErr: false, + want: dymnstypes.ReverseResolvedDymNameAddresses{ + { + SubName: "", + Name: "a", + ChainIdOrAlias: rollAppId1, + }, + }, + }, + { + name: "pass - can resolve case-insensitive bech32 on host-chain", + dymNames: newDN("a", ownerAcc.bech32()). + exp(s.now, +1). + cfgN("", "b", ownerAcc.bech32()). + cfgN("blumbus_111-1", "bb", ownerAcc.bech32()). + buildSlice(), + additionalSetup: nil, + inputAddress: swapCase(ownerAcc.bech32()), + workingChainId: s.chainId, + wantErr: false, + want: dymnstypes.ReverseResolvedDymNameAddresses{ + { + SubName: "", + Name: "a", + ChainIdOrAlias: s.chainId, + }, + { + SubName: "b", + Name: "a", + ChainIdOrAlias: s.chainId, + }, + }, + }, + { + name: "pass - can resolve case-insensitive bech32 on Roll-App", + dymNames: newDN("a", ownerAcc.bech32()). + exp(s.now, +1). + cfgN(rollAppId1, "", ownerAcc.bech32C("ra")). + buildSlice(), + additionalSetup: nil, + inputAddress: swapCase(ownerAcc.bech32C("ra")), + workingChainId: rollAppId1, + wantErr: false, + want: dymnstypes.ReverseResolvedDymNameAddresses{ + { + SubName: "", + Name: "a", + ChainIdOrAlias: rollAppId1, + }, + }, + }, + { + name: "pass - case-sensitive resolve bech32 on non-host-chain/non-Roll-App", + dymNames: newDN("a", ownerAcc.bech32()). + exp(s.now, +1). + cfgN("", "b", ownerAcc.bech32()). + cfgN("blumbus_111-1", "bb", ownerAcc.bech32()). + buildSlice(), + additionalSetup: nil, + inputAddress: ownerAcc.bech32(), + workingChainId: "blumbus_111-1", + wantErr: false, + want: dymnstypes.ReverseResolvedDymNameAddresses{ + { + SubName: "bb", + Name: "a", + ChainIdOrAlias: "blumbus_111-1", + }, + }, + }, + { + name: "pass - case-sensitive resolve bech32 on non-host-chain/non-Roll-App", + dymNames: newDN("a", ownerAcc.bech32()). + exp(s.now, +1). + cfgN("", "b", ownerAcc.bech32()). + cfgN("blumbus_111-1", "bb", ownerAcc.bech32()). + buildSlice(), + additionalSetup: nil, + inputAddress: swapCase(ownerAcc.bech32()), + workingChainId: "blumbus_111-1", + wantErr: false, + want: nil, + }, + { + name: "pass - can resolve ICA bech32 on host-chain", + dymNames: newDN("a", ownerAcc.bech32()). + exp(s.now, +1). + cfgN("", "b", ownerAcc.bech32()). + cfgN("", "ica", icaAcc.bech32()). + cfgN("blumbus_111-1", "bb", ownerAcc.bech32()). + buildSlice(), + additionalSetup: nil, + inputAddress: icaAcc.bech32(), + workingChainId: s.chainId, + wantErr: false, + want: dymnstypes.ReverseResolvedDymNameAddresses{ + { + SubName: "ica", + Name: "a", + ChainIdOrAlias: s.chainId, + }, + }, + }, + { + name: "pass - can resolve ICA bech32 on RollApp", + dymNames: newDN("a", ownerAcc.bech32()). + exp(s.now, +1). + cfgN("", "b", ownerAcc.bech32()). + cfgN(rollAppId1, "ica", icaAcc.bech32()). + buildSlice(), + additionalSetup: nil, + inputAddress: icaAcc.bech32(), + workingChainId: rollAppId1, + wantErr: false, + want: dymnstypes.ReverseResolvedDymNameAddresses{ + { + SubName: "ica", + Name: "a", + ChainIdOrAlias: rollAppId1, + }, + }, + }, + { + name: "pass - can resolve case-insensitive ICA bech32 on host-chain", + dymNames: newDN("a", ownerAcc.bech32()). + exp(s.now, +1). + cfgN("", "b", ownerAcc.bech32()). + cfgN("", "ica", icaAcc.bech32()). + cfgN("blumbus_111-1", "bb", ownerAcc.bech32()). + buildSlice(), + additionalSetup: nil, + inputAddress: swapCase(icaAcc.bech32()), + workingChainId: s.chainId, + wantErr: false, + want: dymnstypes.ReverseResolvedDymNameAddresses{ + { + SubName: "ica", + Name: "a", + ChainIdOrAlias: s.chainId, + }, + }, + }, + { + name: "pass - can resolve case-insensitive ICA bech32 on RollApp", + dymNames: newDN("a", ownerAcc.bech32()). + exp(s.now, +1). + cfgN("", "b", ownerAcc.bech32()). + cfgN(rollAppId1, "ica", icaAcc.bech32()). + buildSlice(), + additionalSetup: nil, + inputAddress: swapCase(icaAcc.bech32()), + workingChainId: rollAppId1, + wantErr: false, + want: dymnstypes.ReverseResolvedDymNameAddresses{ + { + SubName: "ica", + Name: "a", + ChainIdOrAlias: rollAppId1, + }, + }, + }, + { + name: "pass - case-sensitive resolve ICA bech32 on non-host-chain/non-RollApp", + dymNames: newDN("a", ownerAcc.bech32()). + exp(s.now, +1). + cfgN("blumbus_111-1", "ica", icaAcc.bech32()). + buildSlice(), + additionalSetup: nil, + inputAddress: icaAcc.bech32(), + workingChainId: "blumbus_111-1", + wantErr: false, + want: dymnstypes.ReverseResolvedDymNameAddresses{ + { + SubName: "ica", + Name: "a", + ChainIdOrAlias: "blumbus_111-1", + }, + }, + }, + { + name: "pass - case-sensitive resolve ICA bech32 on non-host-chain/non-RollApp", + dymNames: newDN("a", ownerAcc.bech32()). + exp(s.now, +1). + cfgN("blumbus_111-1", "ica", icaAcc.bech32()). + buildSlice(), + additionalSetup: nil, + inputAddress: swapCase(icaAcc.bech32()), + workingChainId: "blumbus_111-1", + wantErr: false, + want: nil, + }, + + { + name: "pass - case-sensitive resolve other address on non-host-chain/non-RollApp", + dymNames: newDN("a", ownerAcc.bech32()). + exp(s.now, +1). + cfgN("another", "", "X-avax1tzdcgj4ehsvhhgpl7zylwpw0gl2rxcg4r5afk5"). + buildSlice(), + additionalSetup: nil, + inputAddress: "X-avax1tzdcgj4ehsvhhgpl7zylwpw0gl2rxcg4r5afk5", + workingChainId: "another", + wantErr: false, + want: dymnstypes.ReverseResolvedDymNameAddresses{ + { + SubName: "", + Name: "a", + ChainIdOrAlias: "another", + }, + }, + }, + { + name: "pass - case-sensitive resolve other address on non-host-chain/non-RollApp", + dymNames: newDN("a", ownerAcc.bech32()). + exp(s.now, +1). + cfgN("another", "", "X-avax1tzdcgj4ehsvhhgpl7zylwpw0gl2rxcg4r5afk5"). + buildSlice(), + additionalSetup: nil, + inputAddress: swapCase("X-avax1tzdcgj4ehsvhhgpl7zylwpw0gl2rxcg4r5afk5"), + workingChainId: "another", + wantErr: false, + want: nil, + }, + { + name: "pass - only take records matching input chain-id", + dymNames: newDN("a", ownerAcc.bech32()). + exp(s.now, +1). + cfgN("", "b", ownerAcc.bech32()). + cfgN("blumbus_111-1", "bb", ownerAcc.bech32()). + buildSlice(), + additionalSetup: nil, + inputAddress: ownerAcc.bech32(), + workingChainId: "blumbus_111-1", + wantErr: false, + want: dymnstypes.ReverseResolvedDymNameAddresses{ + { + SubName: "bb", + Name: "a", + ChainIdOrAlias: "blumbus_111-1", + }, + }, + }, + { + name: "pass - if no result, return empty without error", + dymNames: newDN("a", ownerAcc.bech32()). + exp(s.now, +1). + buildSlice(), + additionalSetup: nil, + inputAddress: anotherAcc.bech32(), + workingChainId: s.chainId, + wantErr: false, + want: nil, + }, + { + name: "pass - lookup by hex on host chain", + dymNames: newDN("a", ownerAcc.bech32()). + exp(s.now, +1). + cfgN("", "b", ownerAcc.bech32()). + cfgN("blumbus_111-1", "bb", ownerAcc.bech32()). + buildSlice(), + additionalSetup: nil, + inputAddress: ownerAcc.hexStr(), + workingChainId: s.chainId, + wantErr: false, + want: dymnstypes.ReverseResolvedDymNameAddresses{ + { + SubName: "", + Name: "a", + ChainIdOrAlias: s.chainId, + }, + { + SubName: "b", + Name: "a", + ChainIdOrAlias: s.chainId, + }, + }, + }, + { + name: "pass - lookup by hex on host chain, uppercase address", + dymNames: newDN("a", ownerAcc.bech32()). + exp(s.now, +1). + cfgN("", "b", ownerAcc.bech32()). + cfgN("blumbus_111-1", "bb", ownerAcc.bech32()). + buildSlice(), + additionalSetup: nil, + inputAddress: strings.ToUpper(ownerAcc.hexStr()), + workingChainId: s.chainId, + wantErr: false, + want: dymnstypes.ReverseResolvedDymNameAddresses{ + { + SubName: "", + Name: "a", + ChainIdOrAlias: s.chainId, + }, + { + SubName: "b", + Name: "a", + ChainIdOrAlias: s.chainId, + }, + }, + }, + { + name: "pass - lookup by hex on host chain, checksum address", + dymNames: newDN("a", ownerAcc.bech32()). + exp(s.now, +1). + cfgN("", "b", ownerAcc.bech32()). + cfgN("blumbus_111-1", "bb", ownerAcc.bech32()). + buildSlice(), + additionalSetup: nil, + inputAddress: common.BytesToAddress(ownerAcc.bytes()).String(), + workingChainId: s.chainId, + wantErr: false, + want: dymnstypes.ReverseResolvedDymNameAddresses{ + { + SubName: "", + Name: "a", + ChainIdOrAlias: s.chainId, + }, + { + SubName: "b", + Name: "a", + ChainIdOrAlias: s.chainId, + }, + }, + }, + { + name: "pass - lookup ICA by hex on host chain", + dymNames: newDN("a", ownerAcc.bech32()). + exp(s.now, +1). + cfgN("", "b", ownerAcc.bech32()). + cfgN("", "ica", icaAcc.bech32()). + cfgN("blumbus_111-1", "bb", ownerAcc.bech32()). + buildSlice(), + additionalSetup: nil, + inputAddress: icaAcc.hexStr(), + workingChainId: s.chainId, + wantErr: false, + want: dymnstypes.ReverseResolvedDymNameAddresses{ + { + SubName: "ica", + Name: "a", + ChainIdOrAlias: s.chainId, + }, + }, + }, + { + name: "pass - lookup by hex on RollApp with bech32 prefix mapped, find out the matching configuration", + dymNames: newDN("a", ownerAcc.bech32()). + exp(s.now, +1). + cfgN("", "b", ownerAcc.bech32()). + cfgN("blumbus_111-1", "bb", ownerAcc.bech32()). + cfgN(rollAppId1, "ra", anotherAcc.bech32C(rollApp1Bech32)). + buildSlice(), + inputAddress: anotherAcc.hexStr(), + workingChainId: rollAppId1, + wantErr: false, + want: dymnstypes.ReverseResolvedDymNameAddresses{ + { + // when bech32 found from mapped by chain-id, + // we convert the hex address into bech32 + // and perform lookup, so we should find out + // the existing configuration + SubName: "ra", + Name: "a", + ChainIdOrAlias: rollAppId1, + }, + }, + }, + { + name: "pass - lookup by hex on RollApp with bech32 prefix mapped, but matching configuration of corresponding address so we do fallback lookup", + dymNames: newDN("a", ownerAcc.bech32()). + exp(s.now, +1). + cfgN("", "b", ownerAcc.bech32()). + cfgN("blumbus_111-1", "bb", ownerAcc.bech32()). + buildSlice(), + inputAddress: ownerAcc.hexStr(), + workingChainId: rollAppId1, + wantErr: false, + want: dymnstypes.ReverseResolvedDymNameAddresses{ + { + SubName: "", // fallback lookup does not have Path => SubName + Name: "a", + ChainIdOrAlias: rollAppId1, + }, + }, + }, + { + name: "pass - lookup by hex on RollApp with bech32 prefix mapped, find out the matching configuration", + dymNames: newDN("a", ownerAcc.bech32()). + exp(s.now, +1). + cfgN("", "b", ownerAcc.bech32()). + cfgN("blumbus_111-1", "bb", ownerAcc.bech32()). + cfgN(rollAppId1, "ra", ownerAcc.bech32C(rollApp1Bech32)). + buildSlice(), + inputAddress: ownerAcc.hexStr(), + workingChainId: rollAppId1, + wantErr: false, + want: dymnstypes.ReverseResolvedDymNameAddresses{ + { + // when bech32 found from mapped by chain-id, + // we convert the hex address into bech32 + // and perform lookup, so we should find out + // the existing configuration + SubName: "ra", + Name: "a", + ChainIdOrAlias: rollAppId1, + }, + }, + }, + { + name: "pass - skip lookup by hex after first try (direct match) if working-chain-id is Neither host-chain nor RollApp, by bech32", + dymNames: newDN("a", ownerAcc.bech32()). + exp(s.now, +1). + cfgN("", "", ownerAcc.bech32()). + buildSlice(), + inputAddress: anotherAcc.bech32(), + workingChainId: "cosmoshub-4", + wantErr: false, + want: nil, + }, + { + name: "pass - skip lookup by hex if working-chain-id is Neither host-chain nor RollApp, by hex", + dymNames: newDN("a", ownerAcc.bech32()). + exp(s.now, +1). + cfgN("", "", ownerAcc.bech32()). + buildSlice(), + inputAddress: ownerAcc.hexStr(), + workingChainId: "cosmoshub-4", + wantErr: false, + want: nil, + }, + { + name: "pass - find result from multiple Dym-Names matched, by bech32", + dymNames: []dymnstypes.DymName{ + newDN("a", ownerAcc.bech32()). + exp(s.now, +1). + cfgN("", "b", ownerAcc.bech32()). + build(), + newDN("b", ownerAcc.bech32()). + exp(s.now, +1). + build(), + }, + additionalSetup: nil, + inputAddress: ownerAcc.bech32(), + workingChainId: s.chainId, + wantErr: false, + want: dymnstypes.ReverseResolvedDymNameAddresses{ + { + SubName: "", + Name: "a", + ChainIdOrAlias: s.chainId, + }, + { + SubName: "", + Name: "b", + ChainIdOrAlias: s.chainId, + }, + { + SubName: "b", + Name: "a", + ChainIdOrAlias: s.chainId, + }, + }, + }, + { + name: "pass - find result from multiple Dym-Names matched, by hex", + dymNames: []dymnstypes.DymName{ + newDN("a", ownerAcc.bech32()). + exp(s.now, +1). + cfgN("", "b", ownerAcc.bech32()). + build(), + newDN("b", ownerAcc.bech32()). + exp(s.now, +1). + build(), + }, + additionalSetup: nil, + inputAddress: ownerAcc.hexStr(), + workingChainId: s.chainId, + wantErr: false, + want: dymnstypes.ReverseResolvedDymNameAddresses{ + { + SubName: "", + Name: "a", + ChainIdOrAlias: s.chainId, + }, + { + SubName: "", + Name: "b", + ChainIdOrAlias: s.chainId, + }, + { + SubName: "b", + Name: "a", + ChainIdOrAlias: s.chainId, + }, + }, + }, + { + name: "pass - result is sorted", + dymNames: []dymnstypes.DymName{ + newDN("a", ownerAcc.bech32()). + exp(s.now, +1). + cfgN("", "b", ownerAcc.bech32()). + build(), + newDN("b", ownerAcc.bech32()). + exp(s.now, +1). + cfgN("", "b", ownerAcc.bech32()). + build(), + }, + additionalSetup: nil, + inputAddress: ownerAcc.bech32(), + workingChainId: s.chainId, + wantErr: false, + want: dymnstypes.ReverseResolvedDymNameAddresses{ + { + SubName: "", + Name: "a", + ChainIdOrAlias: s.chainId, + }, + { + SubName: "", + Name: "b", + ChainIdOrAlias: s.chainId, + }, + { + SubName: "b", + Name: "a", + ChainIdOrAlias: s.chainId, + }, + { + SubName: "b", + Name: "b", + ChainIdOrAlias: s.chainId, + }, + }, + }, + { + name: "pass - result not contains expired Dym-Name, by bech32", + dymNames: []dymnstypes.DymName{ + newDN("a", ownerAcc.bech32()). + exp(s.now, -1). + cfgN("", "b", ownerAcc.bech32()). + build(), + newDN("b", ownerAcc.bech32()). + exp(s.now, +1). + build(), + }, + additionalSetup: nil, + inputAddress: ownerAcc.bech32(), + workingChainId: s.chainId, + wantErr: false, + want: dymnstypes.ReverseResolvedDymNameAddresses{ + { + SubName: "", + Name: "b", + ChainIdOrAlias: s.chainId, + }, + }, + }, + { + name: "pass - result not contains expired Dym-Name, by hex", + dymNames: []dymnstypes.DymName{ + newDN("a", ownerAcc.bech32()). + exp(s.now, -1). + cfgN("", "b", ownerAcc.bech32()). + build(), + newDN("b", ownerAcc.bech32()). + exp(s.now, +1). + build(), + }, + additionalSetup: nil, + inputAddress: ownerAcc.hexStr(), + workingChainId: s.chainId, + wantErr: false, + want: dymnstypes.ReverseResolvedDymNameAddresses{ + { + SubName: "", + Name: "b", + ChainIdOrAlias: s.chainId, + }, + }, + }, + { + name: "fail - reject empty input address", + dymNames: newDN("a", ownerAcc.bech32()).buildSlice(), + inputAddress: "", + workingChainId: s.chainId, + wantErr: true, + wantErrContains: "not supported address format", + }, + { + name: "fail - reject bad input address", + dymNames: newDN("a", ownerAcc.bech32()).buildSlice(), + inputAddress: "0xdym1", + workingChainId: s.chainId, + wantErr: true, + wantErrContains: "not supported address format", + }, + { + name: "fail - reject empty working-chain-id", + dymNames: newDN("a", ownerAcc.bech32()).buildSlice(), + inputAddress: ownerAcc.bech32(), + workingChainId: "", + wantErr: true, + wantErrContains: "invalid chain-id format", + }, + { + name: "fail - reject bad working-chain-id", + dymNames: newDN("a", ownerAcc.bech32()).buildSlice(), + inputAddress: ownerAcc.bech32(), + workingChainId: "@", + wantErr: true, + wantErrContains: "invalid chain-id format", + }, + { + name: "pass - should not include the Dym-Name that mistakenly linked to Dym-Name that does not correct config relates to the account, by bech32", + dymNames: []dymnstypes.DymName{ + newDN("a", ownerAcc.bech32()). + exp(s.now, +1). + build(), + newDN("b", ownerAcc.bech32()). + exp(s.now, +1). + build(), + newDN("c", anotherAcc.bech32()). + exp(s.now, +1). + build(), + }, + additionalSetup: func(s *KeeperTestSuite) { + err := s.dymNsKeeper.AddReverseMappingConfiguredAddressToDymName(s.ctx, ownerAcc.bech32(), "c") + s.Require().NoError(err) + err = s.dymNsKeeper.AddReverseMappingFallbackAddressToDymName(s.ctx, common.HexToAddress(ownerAcc.hexStr()).Bytes(), "c") + s.Require().NoError(err) + }, + inputAddress: ownerAcc.bech32(), + workingChainId: s.chainId, + wantErr: false, + want: dymnstypes.ReverseResolvedDymNameAddresses{ + { + SubName: "", + Name: "a", + ChainIdOrAlias: s.chainId, + }, + { + SubName: "", + Name: "b", + ChainIdOrAlias: s.chainId, + }, + }, + }, + { + name: "pass - should not include the Dym-Name that mistakenly linked to Dym-Name that does not correct config relates to the account, by hex", + dymNames: []dymnstypes.DymName{ + newDN("a", ownerAcc.bech32()). + exp(s.now, +1). + build(), + newDN("b", ownerAcc.bech32()). + exp(s.now, +1). + build(), + newDN("c", anotherAcc.bech32()). + exp(s.now, +1). + build(), + }, + additionalSetup: func(s *KeeperTestSuite) { + err := s.dymNsKeeper.AddReverseMappingConfiguredAddressToDymName(s.ctx, ownerAcc.bech32(), "c") + s.Require().NoError(err) + err = s.dymNsKeeper.AddReverseMappingFallbackAddressToDymName(s.ctx, common.HexToAddress(ownerAcc.hexStr()).Bytes(), "c") + s.Require().NoError(err) + }, + inputAddress: ownerAcc.hexStr(), + workingChainId: s.chainId, + wantErr: false, + want: dymnstypes.ReverseResolvedDymNameAddresses{ + { + SubName: "", + Name: "a", + ChainIdOrAlias: s.chainId, + }, + { + SubName: "", + Name: "b", + ChainIdOrAlias: s.chainId, + }, + }, + }, + { + name: "pass - should not include the Dym-Name that mistakenly linked to Dym-Name that does not correct config relates to the account, by bech32", + dymNames: []dymnstypes.DymName{ + newDN("a", ownerAcc.bech32()). + exp(s.now, +1). + build(), + }, + additionalSetup: func(s *KeeperTestSuite) { + err := s.dymNsKeeper.AddReverseMappingConfiguredAddressToDymName(s.ctx, anotherAcc.bech32(), "a") + s.Require().NoError(err) + err = s.dymNsKeeper.AddReverseMappingFallbackAddressToDymName(s.ctx, common.HexToAddress(anotherAcc.hexStr()).Bytes(), "a") + s.Require().NoError(err) + }, + inputAddress: anotherAcc.bech32(), + workingChainId: s.chainId, + wantErr: false, + want: nil, + }, + { + name: "pass - should not include the Dym-Name that mistakenly linked to Dym-Name that does not correct config relates to the account, by hex", + dymNames: []dymnstypes.DymName{ + newDN("a", ownerAcc.bech32()). + exp(s.now, +1). + build(), + }, + additionalSetup: func(s *KeeperTestSuite) { + err := s.dymNsKeeper.AddReverseMappingConfiguredAddressToDymName(s.ctx, anotherAcc.bech32(), "a") + s.Require().NoError(err) + err = s.dymNsKeeper.AddReverseMappingFallbackAddressToDymName(s.ctx, common.HexToAddress(anotherAcc.hexStr()).Bytes(), "a") + s.Require().NoError(err) + }, + inputAddress: anotherAcc.hexStr(), + workingChainId: s.chainId, + wantErr: false, + want: nil, + }, + { + name: "pass - matching by hex if bech32 is not found, on host chain", + dymNames: newDN("a", ownerAcc.bech32()). + exp(s.now, +1). + buildSlice(), + additionalSetup: nil, + inputAddress: ownerAcc.bech32C(rollApp1Bech32), + workingChainId: s.chainId, + wantErr: false, + want: dymnstypes.ReverseResolvedDymNameAddresses{ + { + SubName: "", + Name: "a", + ChainIdOrAlias: s.chainId, + }, + }, + }, + { + name: "pass - matching by hex if bech32 is not found, on RollApp", + dymNames: newDN("a", ownerAcc.bech32()). + exp(s.now, +1). + buildSlice(), + additionalSetup: nil, + inputAddress: ownerAcc.bech32C(rollApp1Bech32), + workingChainId: rollAppId1, + wantErr: false, + want: dymnstypes.ReverseResolvedDymNameAddresses{ + { + SubName: "", + Name: "a", + ChainIdOrAlias: rollAppId1, + }, + }, + }, + { + name: "pass - alias is used if available, by bech32, alias from params", + dymNames: newDN("a", ownerAcc.bech32()). + exp(s.now, +1). + cfgN("", "b", ownerAcc.bech32()). + cfgN("blumbus_111-1", "bb", ownerAcc.bech32()). + buildSlice(), + additionalSetup: func(s *KeeperTestSuite) { + moduleParams := s.dymNsKeeper.GetParams(s.ctx) + moduleParams.Chains.AliasesOfChainIds = []dymnstypes.AliasesOfChainId{ + { + ChainId: s.chainId, + Aliases: []string{"dym", "dymension"}, + }, + } + s.Require().NoError(s.dymNsKeeper.SetParams(s.ctx, moduleParams)) + }, + inputAddress: ownerAcc.bech32(), + workingChainId: s.chainId, + wantErr: false, + want: dymnstypes.ReverseResolvedDymNameAddresses{ + { + SubName: "", + Name: "a", + ChainIdOrAlias: "dym", // alias is used instead of chain-id + }, + { + SubName: "b", + Name: "a", + ChainIdOrAlias: "dym", + }, + }, + }, + { + name: "pass - alias is used if available, by bech32, alias from RollApp", + dymNames: newDN("a", ownerAcc.bech32()). + exp(s.now, +1). + cfgN("", "b", ownerAcc.bech32()). + cfgN("blumbus_111-1", "bb", ownerAcc.bech32()). + cfgN(rollAppId2, "", ownerAcc.bech32C(rollApp2Bech32)). + cfgN(rollAppId2, "b", ownerAcc.bech32C(rollApp2Bech32)). + buildSlice(), + additionalSetup: func(s *KeeperTestSuite) { + }, + inputAddress: ownerAcc.bech32C(rollApp2Bech32), + workingChainId: rollAppId2, + wantErr: false, + want: dymnstypes.ReverseResolvedDymNameAddresses{ + { + SubName: "", + Name: "a", + ChainIdOrAlias: rollApp2Alias, // alias is used instead of chain-id + }, + { + SubName: "b", + Name: "a", + ChainIdOrAlias: rollApp2Alias, + }, + }, + }, + { + name: "pass - alias is used if available, by hex, alias from params", + dymNames: newDN("a", ownerAcc.bech32()). + exp(s.now, +1). + cfgN("", "b", ownerAcc.bech32()). + cfgN("blumbus_111-1", "bb", ownerAcc.bech32()). + cfgN(rollAppId2, "", ownerAcc.bech32C(rollApp2Bech32)). + buildSlice(), + additionalSetup: func(s *KeeperTestSuite) { + moduleParams := s.dymNsKeeper.GetParams(s.ctx) + moduleParams.Chains.AliasesOfChainIds = []dymnstypes.AliasesOfChainId{ + { + ChainId: s.chainId, + Aliases: []string{"dym", "dymension"}, + }, + } + s.Require().NoError(s.dymNsKeeper.SetParams(s.ctx, moduleParams)) + }, + inputAddress: ownerAcc.hexStr(), + workingChainId: s.chainId, + wantErr: false, + want: dymnstypes.ReverseResolvedDymNameAddresses{ + { + SubName: "", + Name: "a", + ChainIdOrAlias: "dym", // alias is used instead of chain-id + }, + { + SubName: "b", + Name: "a", + ChainIdOrAlias: "dym", + }, + }, + }, + { + name: "pass - alias is used if available, by hex, alias from RollApp", + dymNames: newDN("a", ownerAcc.bech32()). + exp(s.now, +1). + cfgN("", "b", ownerAcc.bech32()). + cfgN("blumbus_111-1", "bb", ownerAcc.bech32()). + cfgN(rollAppId2, "", ownerAcc.bech32C(rollApp2Bech32)). + cfgN(rollAppId2, "b", ownerAcc.bech32C(rollApp2Bech32)). + buildSlice(), + additionalSetup: func(s *KeeperTestSuite) { + }, + inputAddress: ownerAcc.hexStr(), + workingChainId: rollAppId2, + wantErr: false, + want: dymnstypes.ReverseResolvedDymNameAddresses{ + { + SubName: "", + Name: "a", + ChainIdOrAlias: rollApp2Alias, // alias is used instead of chain-id + }, + { + SubName: "b", + Name: "a", + ChainIdOrAlias: rollApp2Alias, + }, + }, + }, + } + for _, tt := range tests { + s.Run(tt.name, func() { + s.RefreshContext() + + s.persistRollApp( + *newRollApp(rollAppId1).WithBech32(rollApp1Bech32), + *newRollApp(rollAppId2).WithBech32(rollApp2Bech32).WithAlias(rollApp2Alias), + ) + + for _, dymName := range tt.dymNames { + s.setDymNameWithFunctionsAfter(dymName) + } + + if tt.additionalSetup != nil { + tt.additionalSetup(s) + } + + s.Require().True(s.dymNsKeeper.IsRollAppId(s.ctx, rollAppId1), "bad-setup") + + got, err := s.dymNsKeeper.ReverseResolveDymNameAddress(s.ctx, tt.inputAddress, tt.workingChainId) + if tt.wantErr { + s.Require().NotEmpty(tt.wantErrContains, "mis-configured test case") + s.Require().Error(err) + s.Require().Contains(err.Error(), tt.wantErrContains) + return + } + + s.Require().NoError(err) + s.Require().Equal(tt.want, got) + }) + } +} + +func (s *KeeperTestSuite) TestKeeper_ReplaceChainIdWithAliasIfPossible() { + moduleParams := s.dymNsKeeper.GetParams(s.ctx) + moduleParams.Chains.AliasesOfChainIds = []dymnstypes.AliasesOfChainId{ + { + ChainId: s.chainId, + Aliases: []string{"dym", "dymension"}, + }, + { + ChainId: "blumbus_111-1", + Aliases: []string{"bb"}, + }, + { + ChainId: "froopyland_100-1", + Aliases: nil, + }, + { + ChainId: "another-1", + Aliases: []string{"another"}, + }, + } + s.Require().NoError(s.dymNsKeeper.SetParams(s.ctx, moduleParams)) + + s.rollAppKeeper.SetRollapp(s.ctx, rollapptypes.Rollapp{ + RollappId: "rollapp_1-1", + Owner: testAddr(1).bech32(), + }) + s.Require().True(s.dymNsKeeper.IsRollAppId(s.ctx, "rollapp_1-1")) + s.Require().NoError(s.dymNsKeeper.SetAliasForRollAppId(s.ctx, "rollapp_1-1", "ra1")) + + s.rollAppKeeper.SetRollapp(s.ctx, rollapptypes.Rollapp{ + RollappId: "rollapp_2-2", + Owner: testAddr(2).bech32(), + }) + s.Require().True(s.dymNsKeeper.IsRollAppId(s.ctx, "rollapp_2-2")) + + s.rollAppKeeper.SetRollapp(s.ctx, rollapptypes.Rollapp{ + RollappId: "rollapp_3-3", + Owner: testAddr(3).bech32(), + }) + s.Require().True(s.dymNsKeeper.IsRollAppId(s.ctx, "rollapp_3-3")) + + s.rollAppKeeper.SetRollapp(s.ctx, rollapptypes.Rollapp{ + RollappId: "rollapp_4-4", + Owner: testAddr(4).bech32(), + }) + s.Require().True(s.dymNsKeeper.IsRollAppId(s.ctx, "rollapp_4-4")) + s.Require().NoError(s.dymNsKeeper.SetAliasForRollAppId(s.ctx, "rollapp_4-4", "another")) + + s.Run("can replace from params", func() { + input := []dymnstypes.ReverseResolvedDymNameAddress{ + { + SubName: "a", + Name: "b", + ChainIdOrAlias: s.chainId, + }, + { + SubName: "a", + Name: "b", + ChainIdOrAlias: "blumbus_111-1", + }, + { + SubName: "", + Name: "z", + ChainIdOrAlias: "blumbus_111-1", + }, + { + SubName: "a", + Name: "b", + ChainIdOrAlias: "froopyland_100-1", + }, + { + SubName: "", + Name: "a", + ChainIdOrAlias: "froopyland_100-1", + }, + } + + s.Require().Equal( + []dymnstypes.ReverseResolvedDymNameAddress{ + { + SubName: "a", + Name: "b", + ChainIdOrAlias: "dym", + }, + { + SubName: "a", + Name: "b", + ChainIdOrAlias: "bb", + }, + { + SubName: "", + Name: "z", + ChainIdOrAlias: "bb", + }, + { + SubName: "a", + Name: "b", + ChainIdOrAlias: "froopyland_100-1", + }, + { + SubName: "", + Name: "a", + ChainIdOrAlias: "froopyland_100-1", + }, + }, + s.dymNsKeeper.ReplaceChainIdWithAliasIfPossible(s.ctx, input), + ) + }) + + s.Run("ful-fill with host-chain-id if empty", func() { + input := []dymnstypes.ReverseResolvedDymNameAddress{ + { + SubName: "a", + Name: "b", + ChainIdOrAlias: "", // empty + }, + } + s.Require().Equal( + []dymnstypes.ReverseResolvedDymNameAddress{ + { + SubName: "a", + Name: "b", + ChainIdOrAlias: "dym", + }, + }, + s.dymNsKeeper.ReplaceChainIdWithAliasIfPossible(s.ctx, input), + ) + }) + + s.Run("mapping correct alias for RollApp by ID", func() { + input := []dymnstypes.ReverseResolvedDymNameAddress{ + { + SubName: "a", + Name: "b", + ChainIdOrAlias: "rollapp_1-1", + }, + { + Name: "a", + ChainIdOrAlias: "rollapp_2-2", + }, + { + Name: "b", + ChainIdOrAlias: "rollapp_3-3", + }, + } + s.Require().Equal( + []dymnstypes.ReverseResolvedDymNameAddress{ + { + SubName: "a", + Name: "b", + ChainIdOrAlias: "ra1", + }, + { + Name: "a", + ChainIdOrAlias: "rollapp_2-2", + }, + { + Name: "b", + ChainIdOrAlias: "rollapp_3-3", + }, + }, + s.dymNsKeeper.ReplaceChainIdWithAliasIfPossible(s.ctx, input), + ) + }) + + s.Run("mapping correct alias for RollApp by ID, when RollApp has multiple alias", func() { + s.Require().NoError(s.dymNsKeeper.SetAliasForRollAppId(s.ctx, "rollapp_1-1", "ral12")) + s.Require().NoError(s.dymNsKeeper.SetAliasForRollAppId(s.ctx, "rollapp_1-1", "ral13")) + s.Require().NoError(s.dymNsKeeper.SetAliasForRollAppId(s.ctx, "rollapp_1-1", "ral14")) + + input := []dymnstypes.ReverseResolvedDymNameAddress{ + { + SubName: "a", + Name: "b", + ChainIdOrAlias: "rollapp_1-1", + }, + } + s.Require().Equal( + []dymnstypes.ReverseResolvedDymNameAddress{ + { + SubName: "a", + Name: "b", + ChainIdOrAlias: "ra1", + }, + }, + s.dymNsKeeper.ReplaceChainIdWithAliasIfPossible(s.ctx, input), + ) + }) + + s.Run("mixed replacement from both params and RolApp alias", func() { + input := []dymnstypes.ReverseResolvedDymNameAddress{ + { + SubName: "a", + Name: "b", + ChainIdOrAlias: "rollapp_1-1", + }, + { + Name: "a", + ChainIdOrAlias: "rollapp_2-2", + }, + { + SubName: "a", + Name: "b", + ChainIdOrAlias: "", + }, + { + SubName: "a", + Name: "c", + ChainIdOrAlias: s.chainId, + }, + } + s.Require().Equal( + []dymnstypes.ReverseResolvedDymNameAddress{ + { + SubName: "a", + Name: "b", + ChainIdOrAlias: "ra1", + }, + { + Name: "a", + ChainIdOrAlias: "rollapp_2-2", + }, + { + SubName: "a", + Name: "b", + ChainIdOrAlias: "dym", + }, + { + SubName: "a", + Name: "c", + ChainIdOrAlias: "dym", + }, + }, + s.dymNsKeeper.ReplaceChainIdWithAliasIfPossible(s.ctx, input), + ) + }) + + s.Run("do not use Roll-App alias if occupied in Params", func() { + input := []dymnstypes.ReverseResolvedDymNameAddress{ + { + SubName: "a", + Name: "b", + ChainIdOrAlias: "rollapp_4-4", + }, + { + SubName: "a", + Name: "b", + ChainIdOrAlias: "another-1", + }, + } + s.Require().Equal( + []dymnstypes.ReverseResolvedDymNameAddress{ + { + SubName: "a", + Name: "b", + ChainIdOrAlias: "rollapp_4-4", // keep as is, even tho it has alias + }, + { + SubName: "a", + Name: "b", + ChainIdOrAlias: "another", + }, + }, + s.dymNsKeeper.ReplaceChainIdWithAliasIfPossible(s.ctx, input), + ) + }) + + s.Run("allow passing empty", func() { + s.Require().Empty(s.dymNsKeeper.ReplaceChainIdWithAliasIfPossible(s.ctx, nil)) + s.Require().Empty(s.dymNsKeeper.ReplaceChainIdWithAliasIfPossible(s.ctx, []dymnstypes.ReverseResolvedDymNameAddress{})) + }) +} + +func swapCase(s string) string { + return strings.Map(func(r rune) rune { + switch { + case unicode.IsLower(r): + return unicode.ToUpper(r) + case unicode.IsUpper(r): + return unicode.ToLower(r) + } + return r + }, s) +} diff --git a/x/dymns/keeper/generic_reverse_lookup.go b/x/dymns/keeper/generic_reverse_lookup.go new file mode 100644 index 000000000..74e9db232 --- /dev/null +++ b/x/dymns/keeper/generic_reverse_lookup.go @@ -0,0 +1,87 @@ +package keeper + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" +) + +// GenericAddReverseLookupRecord is a utility method that help to add a reverse lookup record. +func (k Keeper) GenericAddReverseLookupRecord( + ctx sdk.Context, + key []byte, newElement string, + marshaller func([]string) []byte, + unMarshaller func([]byte) []string, +) error { + modifiedRecord := dymnstypes.StringList{ + newElement, + } + + store := ctx.KVStore(k.storeKey) + bz := store.Get(key) + if bz != nil { + existingRecord := unMarshaller(bz) + + modifiedRecord = dymnstypes.StringList(existingRecord).Combine( + modifiedRecord, + ) + + if len(modifiedRecord) == len(existingRecord) { + // no new mapping to add + return nil + } + } + + modifiedRecord = modifiedRecord.Sort() + + bz = marshaller(modifiedRecord) + store.Set(key, bz) + + return nil +} + +// GenericGetReverseLookupRecord is a utility method that help to get a reverse lookup record. +func (k Keeper) GenericGetReverseLookupRecord( + ctx sdk.Context, key []byte, + unMarshaller func([]byte) []string, +) (result []string) { + store := ctx.KVStore(k.storeKey) + + bz := store.Get(key) + if bz != nil { + result = unMarshaller(bz) + } + + return +} + +// GenericRemoveReverseLookupRecord is a utility method that help to remove a reverse lookup record. +func (k Keeper) GenericRemoveReverseLookupRecord( + ctx sdk.Context, + key []byte, elementToRemove string, + marshaller func([]string) []byte, + unMarshaller func([]byte) []string, +) error { + store := ctx.KVStore(k.storeKey) + bz := store.Get(key) + if bz == nil { + // no mapping to remove + return nil + } + + existingRecord := unMarshaller(bz) + + modifiedRecord := dymnstypes.StringList(existingRecord).Exclude([]string{elementToRemove}) + + if len(modifiedRecord) == 0 { + // no more, remove record + store.Delete(key) + return nil + } + + modifiedRecord = modifiedRecord.Sort() + + bz = marshaller(modifiedRecord) + store.Set(key, bz) + + return nil +} diff --git a/x/dymns/keeper/generic_reverse_lookup_test.go b/x/dymns/keeper/generic_reverse_lookup_test.go new file mode 100644 index 000000000..790405103 --- /dev/null +++ b/x/dymns/keeper/generic_reverse_lookup_test.go @@ -0,0 +1,283 @@ +package keeper_test + +import ( + "fmt" + "sort" + + sdk "github.com/cosmos/cosmos-sdk/types" + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" +) + +var keyTestReverseLookup = []byte("test-reverse-lookup") + +func (s *KeeperTestSuite) TestKeeper_GenericAddGetRemoveReverseLookupRecord() { + codec := s.cdc + + type testEntity struct { + getter func(ctx sdk.Context, key []byte, s *KeeperTestSuite) []string + adder func(ctx sdk.Context, key []byte, value string, s *KeeperTestSuite) + remover func(ctx sdk.Context, key []byte, value string, s *KeeperTestSuite) + } + + pseudoMarshaller := func(list []string) []byte { + return codec.MustMarshal(&dymnstypes.ReverseLookupDymNames{ + DymNames: list, + }) + } + pseudoUnMarshaller := func(bz []byte) []string { + var record dymnstypes.ReverseLookupDymNames + codec.MustUnmarshal(bz, &record) + return record.DymNames + } + + genericTE := testEntity{ + getter: func(ctx sdk.Context, key []byte, s *KeeperTestSuite) []string { + return s.dymNsKeeper.GenericGetReverseLookupRecord(ctx, key, pseudoUnMarshaller) + }, + adder: func(ctx sdk.Context, key []byte, value string, s *KeeperTestSuite) { + err := s.dymNsKeeper.GenericAddReverseLookupRecord( + ctx, + key, value, + pseudoMarshaller, pseudoUnMarshaller, + ) + s.NoError(err) + }, + remover: func(ctx sdk.Context, key []byte, value string, s *KeeperTestSuite) { + err := s.dymNsKeeper.GenericRemoveReverseLookupRecord( + ctx, + key, value, + pseudoMarshaller, pseudoUnMarshaller, + ) + s.NoError(err) + }, + } + + dymNameTE := testEntity{ + getter: func(ctx sdk.Context, key []byte, s *KeeperTestSuite) []string { + return s.dymNsKeeper.GenericGetReverseLookupDymNamesRecord(ctx, key).DymNames + }, + adder: func(ctx sdk.Context, key []byte, value string, s *KeeperTestSuite) { + err := s.dymNsKeeper.GenericAddReverseLookupDymNamesRecord(ctx, key, value) + s.NoError(err) + }, + remover: func(ctx sdk.Context, key []byte, value string, s *KeeperTestSuite) { + err := s.dymNsKeeper.GenericRemoveReverseLookupDymNamesRecord(ctx, key, value) + s.NoError(err) + }, + } + + boIdsTE := testEntity{ + getter: func(ctx sdk.Context, key []byte, s *KeeperTestSuite) []string { + return s.dymNsKeeper.GenericGetReverseLookupBuyOrderIdsRecord(ctx, key).OrderIds + }, + adder: func(ctx sdk.Context, key []byte, value string, s *KeeperTestSuite) { + err := s.dymNsKeeper.GenericAddReverseLookupBuyOrderIdsRecord(ctx, key, value) + s.NoError(err) + }, + remover: func(ctx sdk.Context, key []byte, value string, s *KeeperTestSuite) { + err := s.dymNsKeeper.GenericRemoveReverseLookupBuyOrderIdRecord(ctx, key, value) + s.NoError(err) + }, + } + + tests := []struct { + name string + testFunc func(te testEntity, ctx sdk.Context, s *KeeperTestSuite) + }{ + { + name: "add - should able to add new record", + testFunc: func(te testEntity, ctx sdk.Context, s *KeeperTestSuite) { + records := te.getter(ctx, keyTestReverseLookup, s) + s.Empty(records) + + te.adder(ctx, keyTestReverseLookup, "test", s) + + records = te.getter(ctx, keyTestReverseLookup, s) + s.Equal([]string{"test"}, records) + }, + }, + { + name: "add - should able to append new record", + testFunc: func(te testEntity, ctx sdk.Context, s *KeeperTestSuite) { + te.adder(ctx, keyTestReverseLookup, "test", s) + + records := te.getter(ctx, keyTestReverseLookup, s) + s.Equal([]string{"test"}, records) + + te.adder(ctx, keyTestReverseLookup, "test2", s) + + records = te.getter(ctx, keyTestReverseLookup, s) + s.Equal([]string{"test", "test2"}, records) + }, + }, + { + name: "add - should able to add duplicated record but not persist into store", + testFunc: func(te testEntity, ctx sdk.Context, s *KeeperTestSuite) { + te.adder(ctx, keyTestReverseLookup, "test", s) + te.adder(ctx, keyTestReverseLookup, "test2", s) + + records := te.getter(ctx, keyTestReverseLookup, s) + s.Equal([]string{"test", "test2"}, records) + + te.adder(ctx, keyTestReverseLookup, "test2", s) // duplicated + + records = te.getter(ctx, keyTestReverseLookup, s) + s.Equal([]string{"test", "test2"}, records) // still the same + }, + }, + { + name: "add - list must be sorted before persist", + testFunc: func(te testEntity, ctx sdk.Context, s *KeeperTestSuite) { + te.adder(ctx, keyTestReverseLookup, "test3", s) + te.adder(ctx, keyTestReverseLookup, "test", s) + + records := te.getter(ctx, keyTestReverseLookup, s) + s.Equal([]string{"test", "test3"}, records) + + te.adder(ctx, keyTestReverseLookup, "test2", s) + + records = te.getter(ctx, keyTestReverseLookup, s) + s.Equal([]string{"test", "test2", "test3"}, records) + }, + }, + { + name: "get - returns empty when getting non-exist record", + testFunc: func(te testEntity, ctx sdk.Context, s *KeeperTestSuite) { + records := te.getter(ctx, keyTestReverseLookup, s) + s.Empty(records) + }, + }, + { + name: "get - returns correct list of records", + testFunc: func(te testEntity, ctx sdk.Context, s *KeeperTestSuite) { + te.adder(ctx, keyTestReverseLookup, "test3", s) + te.adder(ctx, keyTestReverseLookup, "test2", s) + te.adder(ctx, keyTestReverseLookup, "test1", s) + + records := te.getter(ctx, keyTestReverseLookup, s) + s.Equal([]string{"test1", "test2", "test3"}, records) + }, + }, + { + name: "get - result is ordered", + testFunc: func(te testEntity, ctx sdk.Context, s *KeeperTestSuite) { + te.adder(ctx, keyTestReverseLookup, "test3", s) + te.adder(ctx, keyTestReverseLookup, "test2", s) + te.adder(ctx, keyTestReverseLookup, "test1", s) + + records := te.getter(ctx, keyTestReverseLookup, s) + s.Equal([]string{"test1", "test2", "test3"}, records) + }, + }, + { + name: "remove - able to remove non-existing record without error", + testFunc: func(te testEntity, ctx sdk.Context, s *KeeperTestSuite) { + te.remover(ctx, keyTestReverseLookup, "test3", s) + }, + }, + { + name: "remove - able to remove record not-existing in the list without error", + testFunc: func(te testEntity, ctx sdk.Context, s *KeeperTestSuite) { + te.adder(ctx, keyTestReverseLookup, "test1", s) + te.adder(ctx, keyTestReverseLookup, "test2", s) + records := te.getter(ctx, keyTestReverseLookup, s) + s.Equal([]string{"test1", "test2"}, records) + + te.remover(ctx, keyTestReverseLookup, "test3", s) + + records = te.getter(ctx, keyTestReverseLookup, s) + s.Equal([]string{"test1", "test2"}, records) + }, + }, + { + name: "remove - able to remove record from single element list", + testFunc: func(te testEntity, ctx sdk.Context, s *KeeperTestSuite) { + te.adder(ctx, keyTestReverseLookup, "test", s) + records := te.getter(ctx, keyTestReverseLookup, s) + s.Equal([]string{"test"}, records) + + te.remover(ctx, keyTestReverseLookup, "test", s) + + records = te.getter(ctx, keyTestReverseLookup, s) + s.Empty(records) + }, + }, + { + name: "remove - able to remove record from multiple elements list", + testFunc: func(te testEntity, ctx sdk.Context, s *KeeperTestSuite) { + te.adder(ctx, keyTestReverseLookup, "test1", s) + te.adder(ctx, keyTestReverseLookup, "test2", s) + te.adder(ctx, keyTestReverseLookup, "test3", s) + records := te.getter(ctx, keyTestReverseLookup, s) + s.Equal([]string{"test1", "test2", "test3"}, records) + + te.remover(ctx, keyTestReverseLookup, "test2", s) + + records = te.getter(ctx, keyTestReverseLookup, s) + s.Equal([]string{"test1", "test3"}, records) + }, + }, + { + name: "remove - list must be sorted before persist", + testFunc: func(te testEntity, ctx sdk.Context, s *KeeperTestSuite) { + for no := 100; no <= 999; no++ { + te.adder(ctx, keyTestReverseLookup, fmt.Sprintf("test%d", no), s) + } + + records := te.getter(ctx, keyTestReverseLookup, s) + s.Len(records, 900) + + te.remover(ctx, keyTestReverseLookup, "test500", s) + + records = te.getter(ctx, keyTestReverseLookup, s) + s.Len(records, 899) + + s.True(sort.StringsAreSorted(records)) + }, + }, + { + name: "mix - no collision of records between different keys shares the same head/tail", + testFunc: func(te testEntity, ctx sdk.Context, s *KeeperTestSuite) { + key1 := []byte{0x1, 0x2, 0x3, 0x4} + key2 := append(key1, 0x5, 0x6) // share head + key3 := append([]byte{0x5, 0x6}, key1...) // share tail + + te.adder(ctx, key1, "11", s) + te.adder(ctx, key2, "21", s) + te.adder(ctx, key3, "31", s) + + s.Equal([]string{"11"}, te.getter(ctx, key1, s)) + s.Equal([]string{"21"}, te.getter(ctx, key2, s)) + s.Equal([]string{"31"}, te.getter(ctx, key3, s)) + + te.adder(ctx, key1, "12", s) + te.adder(ctx, key2, "22", s) + te.adder(ctx, key3, "32", s) + + s.Equal([]string{"11", "12"}, te.getter(ctx, key1, s)) + s.Equal([]string{"21", "22"}, te.getter(ctx, key2, s)) + s.Equal([]string{"31", "32"}, te.getter(ctx, key3, s)) + + te.remover(ctx, key1, "11", s) + te.remover(ctx, key2, "21", s) + te.remover(ctx, key3, "31", s) + + s.Equal([]string{"12"}, te.getter(ctx, key1, s)) + s.Equal([]string{"22"}, te.getter(ctx, key2, s)) + s.Equal([]string{"32"}, te.getter(ctx, key3, s)) + }, + }, + } + for _, tt := range tests { + s.Run(tt.name, func() { + branchedCtx1, _ := s.ctx.CacheContext() + tt.testFunc(genericTE, branchedCtx1, s) + + branchedCtx2, _ := s.ctx.CacheContext() + tt.testFunc(dymNameTE, branchedCtx2, s) + + branchedCtx3, _ := s.ctx.CacheContext() + tt.testFunc(boIdsTE, branchedCtx3, s) + }) + } +} diff --git a/x/dymns/keeper/grpc_query.go b/x/dymns/keeper/grpc_query.go new file mode 100644 index 000000000..9ed6be0dd --- /dev/null +++ b/x/dymns/keeper/grpc_query.go @@ -0,0 +1,574 @@ +package keeper + +import ( + "context" + "slices" + + errorsmod "cosmossdk.io/errors" + + sdk "github.com/cosmos/cosmos-sdk/types" + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" + dymnsutils "github.com/dymensionxyz/dymension/v3/x/dymns/utils" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +var _ dymnstypes.QueryServer = queryServer{} + +type queryServer struct { + Keeper +} + +// NewQueryServerImpl returns an implementation of the QueryServer interface +func NewQueryServerImpl(keeper Keeper) dymnstypes.QueryServer { + return &queryServer{Keeper: keeper} +} + +// Params queries the parameters of the module. +func (q queryServer) Params(goCtx context.Context, _ *dymnstypes.QueryParamsRequest) (*dymnstypes.QueryParamsResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + params := q.GetParams(ctx) + + return &dymnstypes.QueryParamsResponse{Params: params}, nil +} + +// DymName queries a Dym-Name by its name. +func (q queryServer) DymName(goCtx context.Context, req *dymnstypes.QueryDymNameRequest) (*dymnstypes.QueryDymNameResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "invalid request") + } + + ctx := sdk.UnwrapSDKContext(goCtx) + + dymName := q.GetDymNameWithExpirationCheck(ctx, req.DymName) + + return &dymnstypes.QueryDymNameResponse{DymName: dymName}, nil +} + +// ResolveDymNameAddresses resolves multiple Dym-Name Addresses to account address of each pointing to. +// +// For example: +// - "my-name@dym" => "dym1a..." +// - "another.my-name@dym" => "dym1b..." +// - "my-name@nim" => "nim1..." +// - (extra format) "0x1234...6789@nim" => "nim1..." +// - (extra format) "dym1a...@nim" => "nim1..." +func (q queryServer) ResolveDymNameAddresses(goCtx context.Context, req *dymnstypes.ResolveDymNameAddressesRequest) (*dymnstypes.ResolveDymNameAddressesResponse, error) { + if req == nil || len(req.Addresses) == 0 { + return nil, status.Error(codes.InvalidArgument, "invalid request") + } + + if addrCount := len(req.Addresses); addrCount > dymnstypes.LimitMaxElementsInApiRequest { + return nil, status.Errorf(codes.InvalidArgument, "too many input addresses: %d > %d", addrCount, dymnstypes.LimitMaxElementsInApiRequest) + } + + // There is a phishing attack vector like this: dym1.....@dym + // With the current implementation, it is limited to 20 characters per name/sub-name + // so, it is easier to recognize: dym1234.5678@dym + + ctx := sdk.UnwrapSDKContext(goCtx) + + var result []dymnstypes.ResultDymNameAddress + for _, address := range req.Addresses { + resolvedAddress, err := q.ResolveByDymNameAddress(ctx, address) + + r := dymnstypes.ResultDymNameAddress{ + Address: address, + } + + if err != nil { + r.Error = err.Error() + } else { + r.ResolvedAddress = resolvedAddress + } + + result = append(result, r) + } + + return &dymnstypes.ResolveDymNameAddressesResponse{ + ResolvedAddresses: result, + }, nil +} + +// DymNamesOwnedByAccount queries the Dym-Names owned by an account. +func (q queryServer) DymNamesOwnedByAccount(goCtx context.Context, req *dymnstypes.QueryDymNamesOwnedByAccountRequest) (*dymnstypes.QueryDymNamesOwnedByAccountResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "invalid request") + } + + ctx := sdk.UnwrapSDKContext(goCtx) + + dymNames, err := q.GetDymNamesOwnedBy(ctx, req.Owner) + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + + return &dymnstypes.QueryDymNamesOwnedByAccountResponse{ + DymNames: dymNames, + }, nil +} + +// SellOrder queries the active SO of a Dym-Name/Alias. +func (q queryServer) SellOrder(goCtx context.Context, req *dymnstypes.QuerySellOrderRequest) (*dymnstypes.QuerySellOrderResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "invalid request") + } + + var assetType dymnstypes.AssetType + switch req.AssetType { + case dymnstypes.TypeName.PrettyName(): + if !dymnsutils.IsValidDymName(req.AssetId) { + return nil, status.Errorf(codes.InvalidArgument, "invalid Dym-Name: %s", req.AssetId) + } + assetType = dymnstypes.TypeName + case dymnstypes.TypeAlias.PrettyName(): + if !dymnsutils.IsValidAlias(req.AssetId) { + return nil, status.Errorf(codes.InvalidArgument, "invalid alias: %s", req.AssetId) + } + assetType = dymnstypes.TypeAlias + default: + return nil, status.Errorf(codes.InvalidArgument, "invalid asset type: %s", req.AssetType) + } + + ctx := sdk.UnwrapSDKContext(goCtx) + so := q.GetSellOrder(ctx, req.AssetId, assetType) + if so == nil { + return nil, status.Errorf(codes.NotFound, "no active Sell Order for %s '%s' at this moment", assetType.PrettyName(), req.AssetId) + } + + return &dymnstypes.QuerySellOrderResponse{ + Result: *so, + }, nil +} + +// EstimateRegisterName estimates the cost to register a Dym-Name. +func (q queryServer) EstimateRegisterName(goCtx context.Context, req *dymnstypes.EstimateRegisterNameRequest) (*dymnstypes.EstimateRegisterNameResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "invalid request") + } + + if !dymnsutils.IsValidDymName(req.Name) { + return nil, status.Errorf(codes.InvalidArgument, "invalid dym name: %s", req.Name) + } + + if req.Duration < 1 { + return nil, status.Error(codes.InvalidArgument, "duration must be at least 1 year") + } + + ctx := sdk.UnwrapSDKContext(goCtx) + + existingDymNameRecord := q.GetDymName(ctx, req.Name) // can be nil if not registered before + + if existingDymNameRecord != nil && existingDymNameRecord.Owner != req.Owner { + // check take-over permission + if !existingDymNameRecord.IsExpiredAtCtx(ctx) { + return nil, status.Errorf( + codes.PermissionDenied, + "you are not the owner of '%s'", req.Name, + ) + } + + // we ignore the grace period since this is just an estimation + } + + estimation := EstimateRegisterName( + q.PriceParams(ctx), + req.Name, + existingDymNameRecord, + req.Owner, + req.Duration, + ) + return &estimation, nil +} + +// EstimateRegisterAlias estimates the cost to register an Alias. +func (q queryServer) EstimateRegisterAlias(goCtx context.Context, req *dymnstypes.EstimateRegisterAliasRequest) (*dymnstypes.EstimateRegisterAliasResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "invalid request") + } + + if !dymnsutils.IsValidAlias(req.Alias) { + return nil, status.Errorf(codes.InvalidArgument, "invalid alias: %s", req.Alias) + } + + ctx := sdk.UnwrapSDKContext(goCtx) + + if req.RollappId != "" && req.Owner != "" { + rollApp, found := q.rollappKeeper.GetRollapp(ctx, req.RollappId) + if !found { + return nil, status.Errorf(codes.NotFound, "RollApp not found: %s", req.RollappId) + } + + if rollApp.Owner != req.Owner { + return nil, status.Errorf(codes.PermissionDenied, "not the owner of the RollApp") + } + } + + if !q.CanUseAliasForNewRegistration(ctx, req.Alias) { + return nil, status.Errorf(codes.AlreadyExists, "alias already taken: %s", req.Alias) + } + + estimation := EstimateRegisterAlias(req.Alias, q.PriceParams(ctx)) + + return &estimation, nil +} + +// ReverseResolveAddress resolves multiple account addresses to Dym-Name Addresses which point to each. +// This function may return multiple possible Dym-Name-Addresses those point to each of the input address. +// +// For example: when we have "my-name@dym" resolves to "dym1a..." +// so reverse resolve will return "my-name@dym" when input is "dym1a..." +func (q queryServer) ReverseResolveAddress(goCtx context.Context, req *dymnstypes.ReverseResolveAddressRequest) (*dymnstypes.ReverseResolveAddressResponse, error) { + if req == nil || len(req.Addresses) == 0 { + return nil, status.Error(codes.InvalidArgument, "invalid request") + } + + if addrCount := len(req.Addresses); addrCount > dymnstypes.LimitMaxElementsInApiRequest { + return nil, status.Errorf(codes.InvalidArgument, "too many input addresses: %d > %d", addrCount, dymnstypes.LimitMaxElementsInApiRequest) + } + + ctx := sdk.UnwrapSDKContext(goCtx) + + workingChainId := req.WorkingChainId + if workingChainId == "" { + workingChainId = ctx.ChainID() + } + + result := make(map[string]dymnstypes.ReverseResolveAddressResult) + // Describe usage of Go Map: non-consensus state, for querying purpose only. + + addErrorResult := func(address string, err error) { + result[address] = dymnstypes.ReverseResolveAddressResult{ + Error: err.Error(), + } + } + + addResult := func(address string, candidates []dymnstypes.ReverseResolvedDymNameAddress) { + dymNameAddress := make([]string, 0, len(candidates)) + for _, candidate := range candidates { + dymNameAddress = append(dymNameAddress, candidate.String()) + } + + result[address] = dymnstypes.ReverseResolveAddressResult{ + Candidates: dymNameAddress, + } + } + + for _, address := range req.Addresses { + if !dymnsutils.PossibleAccountRegardlessChain(address) { + // Simply ignore invalid address. + // Invalid address is not included here to prevent wasting resources due to bad requests. + continue + } + + candidates, err := q.ReverseResolveDymNameAddress(ctx, address, workingChainId) + if err != nil { + addErrorResult(address, err) + continue + } + + addResult(address, candidates) + } + + return &dymnstypes.ReverseResolveAddressResponse{ + Result: result, + WorkingChainId: workingChainId, + }, nil +} + +// TranslateAliasOrChainIdToChainId tries to translate an alias/handle to a chain id. +// If an alias/handle can not be translated to chain-id, it is treated as a chain-id and returns. +func (q queryServer) TranslateAliasOrChainIdToChainId(goCtx context.Context, req *dymnstypes.QueryTranslateAliasOrChainIdToChainIdRequest) (*dymnstypes.QueryTranslateAliasOrChainIdToChainIdResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "invalid request") + } + + if req.AliasOrChainId == "" { + return nil, status.Error(codes.InvalidArgument, "empty alias or chain id") + } + + ctx := sdk.UnwrapSDKContext(goCtx) + + resolvedToChainId, success := q.tryResolveChainIdOrAliasToChainId(ctx, req.AliasOrChainId) + if !success { + resolvedToChainId = req.AliasOrChainId + } + + return &dymnstypes.QueryTranslateAliasOrChainIdToChainIdResponse{ + ChainId: resolvedToChainId, + }, nil +} + +// BuyOrderById queries a Buy-Order by its id. +func (q queryServer) BuyOrderById(goCtx context.Context, req *dymnstypes.QueryBuyOrderByIdRequest) (*dymnstypes.QueryBuyOrderByIdResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "invalid request") + } + + if !dymnstypes.IsValidBuyOrderId(req.Id) { + return nil, status.Errorf(codes.InvalidArgument, "invalid Buy-Order ID: %s", req.Id) + } + + ctx := sdk.UnwrapSDKContext(goCtx) + + buyOrder := q.GetBuyOrder(ctx, req.Id) + if buyOrder == nil { + return nil, status.Error(codes.NotFound, "buy order not found") + } + + return &dymnstypes.QueryBuyOrderByIdResponse{ + BuyOrder: *buyOrder, + }, nil +} + +// BuyOrdersPlacedByAccount queries the all the buy orders placed by an account. +func (q queryServer) BuyOrdersPlacedByAccount(goCtx context.Context, req *dymnstypes.QueryBuyOrdersPlacedByAccountRequest) (*dymnstypes.QueryBuyOrdersPlacedByAccountResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "invalid request") + } + + ctx := sdk.UnwrapSDKContext(goCtx) + + buyOrders, err := q.GetBuyOrdersByBuyer(ctx, req.Account) + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + + return &dymnstypes.QueryBuyOrdersPlacedByAccountResponse{ + BuyOrders: buyOrders, + }, nil +} + +// BuyOrdersByDymName queries all the buy orders of a Dym-Name. +func (q queryServer) BuyOrdersByDymName(goCtx context.Context, req *dymnstypes.QueryBuyOrdersByDymNameRequest) (*dymnstypes.QueryBuyOrdersByDymNameResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "invalid request") + } + + ctx := sdk.UnwrapSDKContext(goCtx) + + buyOrders, err := q.GetBuyOrdersOfDymName(ctx, req.Name) + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + + return &dymnstypes.QueryBuyOrdersByDymNameResponse{ + BuyOrders: buyOrders, + }, nil +} + +// BuyOrdersOfDymNamesOwnedByAccount queries all the buy orders of all Dym-Names owned by an account. +func (q queryServer) BuyOrdersOfDymNamesOwnedByAccount(goCtx context.Context, req *dymnstypes.QueryBuyOrdersOfDymNamesOwnedByAccountRequest) (*dymnstypes.QueryBuyOrdersOfDymNamesOwnedByAccountResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "invalid request") + } + + ctx := sdk.UnwrapSDKContext(goCtx) + + ownedDymNames, err := q.GetDymNamesOwnedBy(ctx, req.Account) + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + + buyOrders := make([]dymnstypes.BuyOrder, 0) + for _, dymName := range ownedDymNames { + buyOrdersOfDymName, err := q.GetBuyOrdersOfDymName(ctx, dymName.Name) + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + + buyOrders = append(buyOrders, buyOrdersOfDymName...) + } + + return &dymnstypes.QueryBuyOrdersOfDymNamesOwnedByAccountResponse{ + BuyOrders: buyOrders, + }, nil +} + +// Alias queries the chain_id associated as well as the Sell-Order and Buy-Order IDs relates to the alias. +func (q queryServer) Alias(goCtx context.Context, req *dymnstypes.QueryAliasRequest) (*dymnstypes.QueryAliasResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "invalid request") + } + + ctx := sdk.UnwrapSDKContext(goCtx) + + chainId, success := q.tryResolveChainIdOrAliasToChainId(ctx, req.Alias) + if !success { + return nil, status.Errorf(codes.NotFound, "alias not found: %s", req.Alias) + } + + if chainId == req.Alias { + return nil, status.Errorf(codes.NotFound, "alias not found: %s", req.Alias) + } + + var foundSellOrder bool + var buyOrderIds []string + var aliasesOfSameChain []string + + if q.IsAliasPresentsInParamsAsAliasOrChainId(ctx, req.Alias) { + for _, aliasesOfChainId := range q.ChainsParams(ctx).AliasesOfChainIds { + if chainId != aliasesOfChainId.ChainId { + continue + } + + aliasesOfSameChain = aliasesOfChainId.Aliases + break + } + } else { + foundSellOrder = q.GetSellOrder(ctx, req.Alias, dymnstypes.TypeAlias) != nil + + aliasToBuyOrderIdsRvlKey := dymnstypes.AliasToBuyOrderIdsRvlKey(req.Alias) + buyOrderIds = q.GenericGetReverseLookupBuyOrderIdsRecord(ctx, aliasToBuyOrderIdsRvlKey).OrderIds + + aliasesOfSameChain = q.GetAliasesOfRollAppId(ctx, chainId) + } + + if len(aliasesOfSameChain) > 0 { + // exclude the alias itself + aliasesOfSameChain = slices.DeleteFunc(aliasesOfSameChain, func(alias string) bool { + return alias == req.Alias + }) + } + + return &dymnstypes.QueryAliasResponse{ + ChainId: chainId, + FoundSellOrder: foundSellOrder, + BuyOrderIds: buyOrderIds, + SameChainAliases: aliasesOfSameChain, + }, nil +} + +// Aliases queries all the aliases for a chain id or all chains. +func (q queryServer) Aliases(goCtx context.Context, req *dymnstypes.QueryAliasesRequest) (*dymnstypes.QueryAliasesResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "invalid request") + } + + if req.ChainId != "" && !dymnsutils.IsValidChainIdFormat(req.ChainId) { + return nil, status.Errorf(codes.InvalidArgument, "invalid chain id: %s", req.ChainId) + } + + ctx := sdk.UnwrapSDKContext(goCtx) + + if req.ChainId != "" { + aliases := q.GetEffectiveAliasesByChainId(ctx, req.ChainId) + resp := dymnstypes.QueryAliasesResponse{} + if len(aliases) > 0 { + resp.AliasesByChainId = map[string]dymnstypes.MultipleAliases{ + req.ChainId: { + Aliases: aliases, + }, + } + } + return &resp, nil + } + + aliasesByChainId := make(map[string]dymnstypes.MultipleAliases) + for _, aliasesOfChainId := range q.ChainsParams(ctx).AliasesOfChainIds { + if len(aliasesOfChainId.Aliases) < 1 { + continue + } + aliasesByChainId[aliasesOfChainId.ChainId] = dymnstypes.MultipleAliases{ + Aliases: aliasesOfChainId.Aliases, + } + } + + rollAppsWithAliases := q.GetAllRollAppsWithAliases(ctx) + if len(rollAppsWithAliases) > 0 { + reservedAliases := q.GetAllAliasAndChainIdInParams(ctx) + + for _, rollAppWithAliases := range rollAppsWithAliases { + aliases := rollAppWithAliases.Aliases + + // Remove the preserved aliases from record. + // Please read the `processActiveAliasSellOrders` method (hooks.go) for more information. + aliases = slices.DeleteFunc(aliases, func(a string) bool { + _, found := reservedAliases[a] + return found + }) + + if len(aliases) < 1 { + continue + } + + existingAliases := aliasesByChainId[rollAppWithAliases.ChainId] + aliasesByChainId[rollAppWithAliases.ChainId] = dymnstypes.MultipleAliases{ + Aliases: append(existingAliases.Aliases, aliases...), + } + } + } + + return &dymnstypes.QueryAliasesResponse{ + AliasesByChainId: aliasesByChainId, + }, nil +} + +// BuyOrdersByAlias queries all the buy orders of an Alias. +func (q queryServer) BuyOrdersByAlias(goCtx context.Context, req *dymnstypes.QueryBuyOrdersByAliasRequest) (*dymnstypes.QueryBuyOrdersByAliasResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "invalid request") + } + + if !dymnsutils.IsValidAlias(req.Alias) { + return nil, status.Errorf(codes.InvalidArgument, "invalid alias: %s", req.Alias) + } + + ctx := sdk.UnwrapSDKContext(goCtx) + + var buyOrders []dymnstypes.BuyOrder + if !q.IsAliasPresentsInParamsAsAliasOrChainId(ctx, req.Alias) { + // We ignore the aliases which presents in the params because they are prohibited from trading. + // Please read the `processActiveAliasSellOrders` method (hooks.go) for more information. + + var err error + buyOrders, err = q.GetBuyOrdersOfAlias(ctx, req.Alias) + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + } + + return &dymnstypes.QueryBuyOrdersByAliasResponse{ + BuyOrders: buyOrders, + }, nil +} + +// BuyOrdersOfAliasesLinkedToRollApp queries all the buy orders of all Aliases linked to a RollApp. +func (q queryServer) BuyOrdersOfAliasesLinkedToRollApp(goCtx context.Context, req *dymnstypes.QueryBuyOrdersOfAliasesLinkedToRollAppRequest) (*dymnstypes.QueryBuyOrdersOfAliasesLinkedToRollAppResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "invalid request") + } + + if !dymnsutils.IsValidChainIdFormat(req.RollappId) { + return nil, status.Errorf(codes.InvalidArgument, "invalid RollApp ID: %s", req.RollappId) + } + + ctx := sdk.UnwrapSDKContext(goCtx) + + if !q.IsRollAppId(ctx, req.RollappId) { + return nil, status.Errorf(codes.NotFound, "RollApp not found: %s", req.RollappId) + } + + var allBuyOrders []dymnstypes.BuyOrder + + aliases := q.GetAliasesOfRollAppId(ctx, req.RollappId) + for _, alias := range aliases { + if q.IsAliasPresentsInParamsAsAliasOrChainId(ctx, alias) { + // ignore + // Please read the `processActiveAliasSellOrders` method (hooks.go) for more information. + continue + } + + buyOrders, err := q.GetBuyOrdersOfAlias(ctx, alias) + if err != nil { + return nil, status.Error(codes.Internal, errorsmod.Wrapf(err, "alias: %s", alias).Error()) + } + + allBuyOrders = append(allBuyOrders, buyOrders...) + } + + return &dymnstypes.QueryBuyOrdersOfAliasesLinkedToRollAppResponse{ + BuyOrders: allBuyOrders, + }, nil +} diff --git a/x/dymns/keeper/grpc_query_test.go b/x/dymns/keeper/grpc_query_test.go new file mode 100644 index 000000000..ad68d874a --- /dev/null +++ b/x/dymns/keeper/grpc_query_test.go @@ -0,0 +1,3463 @@ +package keeper_test + +import ( + "fmt" + "reflect" + "sort" + "time" + + sdkmath "cosmossdk.io/math" + + sdk "github.com/cosmos/cosmos-sdk/types" + dymnskeeper "github.com/dymensionxyz/dymension/v3/x/dymns/keeper" + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" +) + +func (s *KeeperTestSuite) Test_queryServer_Params() { + params := s.dymNsKeeper.GetParams(s.ctx) + params.Misc.SellOrderDuration += time.Hour + err := s.dymNsKeeper.SetParams(s.ctx, params) + s.Require().NoError(err) + + queryServer := dymnskeeper.NewQueryServerImpl(s.dymNsKeeper) + + resp, err := queryServer.Params(sdk.WrapSDKContext(s.ctx), &dymnstypes.QueryParamsRequest{}) + s.Require().NoError(err) + s.Require().NotNil(resp) + s.Require().Equal(params, resp.Params) +} + +func (s *KeeperTestSuite) Test_queryServer_DymName() { + s.Run("Dym-Name not found", func() { + queryServer := dymnskeeper.NewQueryServerImpl(s.dymNsKeeper) + resp, err := queryServer.DymName(sdk.WrapSDKContext(s.ctx), &dymnstypes.QueryDymNameRequest{ + DymName: "not-exists", + }) + s.Require().NoError(err) + s.Require().Nil(resp.DymName) + }) + + ownerA := testAddr(1).bech32() + + tests := []struct { + name string + dymName *dymnstypes.DymName + queryName string + wantResult bool + }{ + { + name: "correct record", + dymName: &dymnstypes.DymName{ + Name: "a", + Owner: ownerA, + Controller: ownerA, + ExpireAt: s.now.Unix() + 99, + Configs: []dymnstypes.DymNameConfig{ + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Value: ownerA, + }, + }, + }, + queryName: "a", + wantResult: true, + }, + { + name: "NOT expired record only", + dymName: &dymnstypes.DymName{ + Name: "a", + Owner: ownerA, + Controller: ownerA, + ExpireAt: s.now.Unix() + 99, + Configs: []dymnstypes.DymNameConfig{ + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Value: ownerA, + }, + }, + }, + queryName: "a", + wantResult: true, + }, + { + name: "return nil for expired record", + dymName: &dymnstypes.DymName{ + Name: "a", + Owner: ownerA, + Controller: ownerA, + ExpireAt: s.now.Unix() - 1, + Configs: []dymnstypes.DymNameConfig{ + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Value: ownerA, + }, + }, + }, + queryName: "a", + wantResult: false, + }, + { + name: "return nil if not found", + dymName: nil, + queryName: "non-exists", + wantResult: false, + }, + } + for _, tt := range tests { + s.Run(tt.name, func() { + s.RefreshContext() + + if tt.dymName != nil { + err := s.dymNsKeeper.SetDymName(s.ctx, *tt.dymName) + s.Require().NoError(err) + } + + queryServer := dymnskeeper.NewQueryServerImpl(s.dymNsKeeper) + resp, err := queryServer.DymName(sdk.WrapSDKContext(s.ctx), &dymnstypes.QueryDymNameRequest{ + DymName: tt.queryName, + }) + s.Require().NoError(err, "should never returns error") + s.Require().NotNil(resp, "should never returns nil response") + + if !tt.wantResult { + s.Require().Nil(resp.DymName) + return + } + + s.Require().NotNil(resp.DymName) + s.Require().Equal(*tt.dymName, *resp.DymName) + }) + } + + s.Run("reject nil request", func() { + s.RefreshContext() + + queryServer := dymnskeeper.NewQueryServerImpl(s.dymNsKeeper) + resp, err := queryServer.DymName(sdk.WrapSDKContext(s.ctx), nil) + s.Require().Error(err) + s.Require().Nil(resp) + }) +} + +func (s *KeeperTestSuite) Test_queryServer_ResolveDymNameAddresses() { + addr1a := testAddr(1).bech32() + addr2a := testAddr(2).bech32() + addr3a := testAddr(3).bech32() + + dymNameA := dymnstypes.DymName{ + Name: "a", + Owner: addr1a, + Controller: addr2a, + ExpireAt: s.now.Unix() + 99, + Configs: []dymnstypes.DymNameConfig{{ + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Value: addr1a, + }}, + } + s.Require().NoError(s.dymNsKeeper.SetDymName(s.ctx, dymNameA)) + + dymNameB := dymnstypes.DymName{ + Name: "b", + Owner: addr1a, + Controller: addr2a, + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{{ + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Value: addr2a, + }}, + } + s.Require().NoError(s.dymNsKeeper.SetDymName(s.ctx, dymNameB)) + + dymNameC := dymnstypes.DymName{ + Name: "c", + Owner: addr1a, + Controller: addr2a, + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{{ + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Value: addr3a, + }}, + } + s.Require().NoError(s.dymNsKeeper.SetDymName(s.ctx, dymNameC)) + + dymNameD := dymnstypes.DymName{ + Name: "d", + Owner: addr1a, + Controller: addr2a, + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{ + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Path: "sub", + Value: addr3a, + }, + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "blumbus_111-1", + Path: "", + Value: addr3a, + }, + }, + } + s.Require().NoError(s.dymNsKeeper.SetDymName(s.ctx, dymNameD)) + + queryServer := dymnskeeper.NewQueryServerImpl(s.dymNsKeeper) + + resp, err := queryServer.ResolveDymNameAddresses(sdk.WrapSDKContext(s.ctx), &dymnstypes.ResolveDymNameAddressesRequest{ + Addresses: []string{ + "a.dymension_1100-1", + "b.dymension_1100-1", + "c.dymension_1100-1", + "a.blumbus_111-1", + }, + }) + s.Require().NoError(err) + s.Require().NotNil(resp) + s.Require().Len(resp.ResolvedAddresses, 4) + + s.Require().Equal(addr1a, resp.ResolvedAddresses[0].ResolvedAddress) + s.Require().Equal(addr2a, resp.ResolvedAddresses[1].ResolvedAddress) + s.Require().Equal(addr3a, resp.ResolvedAddresses[2].ResolvedAddress) + s.Require().Empty(resp.ResolvedAddresses[3].ResolvedAddress) + s.Require().NotEmpty(resp.ResolvedAddresses[3].Error) + + s.Run("reject nil request", func() { + resp, err := queryServer.ResolveDymNameAddresses(sdk.WrapSDKContext(s.ctx), nil) + s.Require().Error(err) + s.Require().Nil(resp) + }) + + s.Run("reject empty request", func() { + resp, err := queryServer.ResolveDymNameAddresses( + sdk.WrapSDKContext(s.ctx), + &dymnstypes.ResolveDymNameAddressesRequest{}, + ) + s.Require().Error(err) + s.Require().Nil(resp) + }) + + s.Run("resolves default to owner if no config of default (without sub-name)", func() { + resp, err := queryServer.ResolveDymNameAddresses( + sdk.WrapSDKContext(s.ctx), + &dymnstypes.ResolveDymNameAddressesRequest{ + Addresses: []string{"d.dymension_1100-1", "d.blumbus_111-1"}, + }, + ) + s.Require().NoError(err) + s.Require().NotNil(resp) + s.Require().Len(resp.ResolvedAddresses, 2) + s.Require().Equal(addr1a, resp.ResolvedAddresses[0].ResolvedAddress) + s.Require().Equal(addr3a, resp.ResolvedAddresses[1].ResolvedAddress) + }) + + s.Run("should limit number of input", func() { + resp, err := queryServer.ResolveDymNameAddresses( + sdk.WrapSDKContext(s.ctx), + &dymnstypes.ResolveDymNameAddressesRequest{ + Addresses: make([]string, 101), + }, + ) + s.Require().ErrorContains(err, "too many input addresses") + s.Require().Nil(resp) + }) +} + +func (s *KeeperTestSuite) Test_queryServer_DymNamesOwnedByAccount() { + addr1a := testAddr(1).bech32() + addr2a := testAddr(2).bech32() + addr3a := testAddr(3).bech32() + + dymNameA := dymnstypes.DymName{ + Name: "a", + Owner: addr1a, + Controller: addr2a, + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{{ + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Value: addr1a, + }}, + } + s.setDymNameWithFunctionsAfter(dymNameA) + + dymNameB := dymnstypes.DymName{ + Name: "b", + Owner: addr1a, + Controller: addr2a, + ExpireAt: s.now.Unix() + 100, + } + s.setDymNameWithFunctionsAfter(dymNameB) + + dymNameCExpired := dymnstypes.DymName{ + Name: "c", + Owner: addr1a, + Controller: addr2a, + ExpireAt: s.now.Unix() - 1, + Configs: []dymnstypes.DymNameConfig{{ + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Value: addr3a, + }}, + } + s.setDymNameWithFunctionsAfter(dymNameCExpired) + + dymNameD := dymnstypes.DymName{ + Name: "d", + Owner: addr3a, + Controller: addr3a, + ExpireAt: s.now.Unix() + 100, + } + s.setDymNameWithFunctionsAfter(dymNameD) + + queryServer := dymnskeeper.NewQueryServerImpl(s.dymNsKeeper) + resp, err := queryServer.DymNamesOwnedByAccount(sdk.WrapSDKContext(s.ctx), &dymnstypes.QueryDymNamesOwnedByAccountRequest{ + Owner: addr1a, + }) + s.Require().NoError(err) + s.Require().NotNil(resp) + s.Require().Len(resp.DymNames, 2) + s.Require().True(resp.DymNames[0].Name == dymNameA.Name || resp.DymNames[1].Name == dymNameA.Name) + s.Require().True(resp.DymNames[0].Name == dymNameB.Name || resp.DymNames[1].Name == dymNameB.Name) + + s.Run("reject nil request", func() { + resp, err := queryServer.DymNamesOwnedByAccount(sdk.WrapSDKContext(s.ctx), nil) + s.Require().Error(err) + s.Require().Nil(resp) + }) + + s.Run("reject invalid request", func() { + resp, err := queryServer.DymNamesOwnedByAccount(sdk.WrapSDKContext(s.ctx), &dymnstypes.QueryDymNamesOwnedByAccountRequest{ + Owner: "x", + }) + s.Require().Error(err) + s.Require().Nil(resp) + }) +} + +func (s *KeeperTestSuite) Test_queryServer_SellOrder() { + addr1a := testAddr(1).bech32() + addr2a := testAddr(2).bech32() + + dymNameA := dymnstypes.DymName{ + Name: "asset", + Owner: addr1a, + Controller: addr2a, + ExpireAt: s.now.Add(time.Hour).Unix(), + } + dymNameB := dymnstypes.DymName{ + Name: "mood", + Owner: addr1a, + Controller: addr2a, + ExpireAt: s.now.Add(time.Hour).Unix(), + } + + rollAppC := newRollApp("central_1-1").WithAlias("asset") + rollAppD := newRollApp("donut_2-1").WithAlias("donut") + + soDymNameA := s.newDymNameSellOrder(dymNameA.Name).WithMinPrice(100).Build() + soAliasRollAppC := s.newAliasSellOrder(rollAppC.alias).WithMinPrice(100).Build() + + tests := []struct { + name string + req *dymnstypes.QuerySellOrderRequest + preRunFunc func(s *KeeperTestSuite) + wantErr bool + wantErrContains string + wantSellOrder *dymnstypes.SellOrder + }{ + { + name: "pass - returns correct order, type Dym Name", + preRunFunc: func(s *KeeperTestSuite) { + err := s.dymNsKeeper.SetSellOrder(s.ctx, soDymNameA) + s.Require().NoError(err) + }, + req: &dymnstypes.QuerySellOrderRequest{ + AssetId: dymNameA.Name, + AssetType: dymnstypes.TypeName.PrettyName(), + }, + wantErr: false, + wantSellOrder: &soDymNameA, + }, + { + name: "pass - returns correct order, type Alias", + preRunFunc: func(s *KeeperTestSuite) { + err := s.dymNsKeeper.SetSellOrder(s.ctx, soAliasRollAppC) + s.Require().NoError(err) + }, + req: &dymnstypes.QuerySellOrderRequest{ + AssetId: rollAppC.alias, + AssetType: dymnstypes.TypeAlias.PrettyName(), + }, + wantErr: false, + wantSellOrder: &soAliasRollAppC, + }, + { + name: "pass - returns correct order of same asset-id with multiple asset types", + preRunFunc: func(s *KeeperTestSuite) { + s.Require().Equal(soDymNameA.AssetId, soAliasRollAppC.AssetId, "Dym-Name and Alias must be the same for this test") + + err := s.dymNsKeeper.SetSellOrder(s.ctx, soDymNameA) + s.Require().NoError(err) + + err = s.dymNsKeeper.SetSellOrder(s.ctx, soAliasRollAppC) + s.Require().NoError(err) + }, + req: &dymnstypes.QuerySellOrderRequest{ + AssetId: dymNameA.Name, + AssetType: dymnstypes.TypeName.PrettyName(), + }, + wantErr: false, + wantSellOrder: &soDymNameA, + }, + { + name: "pass - returns correct order of same asset-id with multiple asset types", + preRunFunc: func(s *KeeperTestSuite) { + s.Require().Equal(soDymNameA.AssetId, soAliasRollAppC.AssetId, "Dym-Name and Alias must be the same for this test") + + err := s.dymNsKeeper.SetSellOrder(s.ctx, soDymNameA) + s.Require().NoError(err) + + err = s.dymNsKeeper.SetSellOrder(s.ctx, soAliasRollAppC) + s.Require().NoError(err) + }, + req: &dymnstypes.QuerySellOrderRequest{ + AssetId: rollAppC.alias, + AssetType: dymnstypes.TypeAlias.PrettyName(), + }, + wantErr: false, + wantSellOrder: &soAliasRollAppC, + }, + { + name: "fail - reject nil request", + req: nil, + wantErr: true, + wantErrContains: "invalid request", + }, + { + name: "fail - reject bad Dym-Name request", + req: &dymnstypes.QuerySellOrderRequest{ + AssetId: "$$$", + AssetType: dymnstypes.TypeName.PrettyName(), + }, + wantErr: true, + wantErrContains: "invalid Dym-Name", + }, + { + name: "fail - reject bad Alias request", + req: &dymnstypes.QuerySellOrderRequest{ + AssetId: "$$$", + AssetType: dymnstypes.TypeAlias.PrettyName(), + }, + wantErr: true, + wantErrContains: "invalid alias", + }, + { + name: "fail - reject unknown asset type", + req: &dymnstypes.QuerySellOrderRequest{ + AssetId: "asset", + AssetType: "pseudo", + }, + wantErr: true, + wantErrContains: "invalid asset type", + }, + { + name: "fail - reject if not found, type Dym Name", + preRunFunc: nil, + req: &dymnstypes.QuerySellOrderRequest{ + AssetId: dymNameB.Name, + AssetType: dymnstypes.TypeName.PrettyName(), + }, + wantErr: true, + wantErrContains: "no active Sell Order for Dym-Name", + }, + { + name: "fail - reject if not found, type Alias", + preRunFunc: nil, + req: &dymnstypes.QuerySellOrderRequest{ + AssetId: rollAppD.alias, + AssetType: dymnstypes.TypeAlias.PrettyName(), + }, + wantErr: true, + wantErrContains: "no active Sell Order for Alias", + }, + } + for _, tt := range tests { + s.Run(tt.name, func() { + s.RefreshContext() + + if tt.preRunFunc != nil { + tt.preRunFunc(s) + } + + resp, err := dymnskeeper.NewQueryServerImpl(s.dymNsKeeper).SellOrder(sdk.WrapSDKContext(s.ctx), tt.req) + if tt.wantErr { + s.Require().ErrorContains(err, tt.wantErrContains) + return + } + + s.Require().NoError(err) + s.Require().NotNil(resp) + s.Require().Equal(*tt.wantSellOrder, resp.Result) + }) + } +} + +func (s *KeeperTestSuite) Test_queryServer_EstimateRegisterName() { + const denom = "atom" + const price1L int64 = 9 + const price2L int64 = 8 + const price3L int64 = 7 + const price4L int64 = 6 + const price5PlusL int64 = 5 + const extendsPrice int64 = 4 + + // the number values used in this test will be multiplied by this value + priceMultiplier := sdk.NewInt(1e18) + + s.updateModuleParams(func(params dymnstypes.Params) dymnstypes.Params { + params.Price.PriceDenom = denom + params.Price.NamePriceSteps = []sdkmath.Int{ + sdkmath.NewInt(price1L).Mul(priceMultiplier), + sdkmath.NewInt(price2L).Mul(priceMultiplier), + sdkmath.NewInt(price3L).Mul(priceMultiplier), + sdkmath.NewInt(price4L).Mul(priceMultiplier), + sdkmath.NewInt(price5PlusL).Mul(priceMultiplier), + } + params.Price.PriceExtends = sdk.NewInt(extendsPrice).Mul(priceMultiplier) + params.Misc.GracePeriodDuration = 30 * 24 * time.Hour + + return params + }) + s.SaveCurrentContext() + + buyerA := testAddr(1).bech32() + previousOwnerA := testAddr(2).bech32() + + tests := []struct { + name string + dymName string + existingDymName *dymnstypes.DymName + newOwner string + duration int64 + wantErr bool + wantErrContains string + wantFirstYearPrice int64 + wantExtendPrice int64 + }{ + { + name: "pass - new registration, 1 letter, 1 year", + dymName: "a", + existingDymName: nil, + newOwner: buyerA, + duration: 1, + wantFirstYearPrice: price1L, + wantExtendPrice: 0, + }, + { + name: "pass - new registration, empty buyer", + dymName: "a", + existingDymName: nil, + newOwner: "", + duration: 1, + wantFirstYearPrice: price1L, + wantExtendPrice: 0, + }, + { + name: "pass - new registration, 1 letter, 2 years", + dymName: "a", + existingDymName: nil, + newOwner: buyerA, + duration: 2, + wantFirstYearPrice: price1L, + wantExtendPrice: extendsPrice, + }, + { + name: "pass - new registration, 1 letter, N years", + dymName: "a", + existingDymName: nil, + newOwner: buyerA, + duration: 99, + wantFirstYearPrice: price1L, + wantExtendPrice: extendsPrice * (99 - 1), + }, + { + name: "pass - new registration, 6 letters, 1 year", + dymName: "bridge", + existingDymName: nil, + newOwner: buyerA, + duration: 1, + wantFirstYearPrice: price5PlusL, + wantExtendPrice: 0, + }, + { + name: "pass - new registration, 6 letters, 2 years", + dymName: "bridge", + existingDymName: nil, + newOwner: buyerA, + duration: 2, + wantFirstYearPrice: price5PlusL, + wantExtendPrice: extendsPrice, + }, + { + name: "pass - new registration, 5+ letters, N years", + dymName: "my-name", + existingDymName: nil, + newOwner: buyerA, + duration: 99, + wantFirstYearPrice: price5PlusL, + wantExtendPrice: extendsPrice * (99 - 1), + }, + { + name: "pass - extends same owner, 1 letter, 1 year", + dymName: "a", + existingDymName: &dymnstypes.DymName{ + Name: "a", + Owner: buyerA, + Controller: buyerA, + ExpireAt: s.now.Unix() + 100, + }, + newOwner: buyerA, + duration: 1, + wantFirstYearPrice: 0, + wantExtendPrice: extendsPrice, + }, + { + name: "pass - extends same owner, 1 letter, 2 years", + dymName: "a", + existingDymName: &dymnstypes.DymName{ + Name: "a", + Owner: buyerA, + Controller: buyerA, + ExpireAt: s.now.Unix() + 100, + }, + newOwner: buyerA, + duration: 2, + wantFirstYearPrice: 0, + wantExtendPrice: extendsPrice * 2, + }, + { + name: "pass - extends same owner, 1 letter, N years", + dymName: "a", + existingDymName: &dymnstypes.DymName{ + Name: "a", + Owner: buyerA, + Controller: buyerA, + ExpireAt: s.now.Unix() + 100, + }, + newOwner: buyerA, + duration: 99, + wantFirstYearPrice: 0, + wantExtendPrice: extendsPrice * 99, + }, + { + name: "pass - extends same owner, 6 letters, 1 year", + dymName: "bridge", + existingDymName: &dymnstypes.DymName{ + Name: "bridge", + Owner: buyerA, + Controller: buyerA, + ExpireAt: s.now.Unix() + 100, + }, + newOwner: buyerA, + duration: 1, + wantFirstYearPrice: 0, + wantExtendPrice: extendsPrice, + }, + { + name: "pass - extends same owner, 6 letters, 2 years", + dymName: "bridge", + existingDymName: &dymnstypes.DymName{ + Name: "bridge", + Owner: buyerA, + Controller: buyerA, + ExpireAt: s.now.Unix() + 100, + }, + newOwner: buyerA, + duration: 2, + wantFirstYearPrice: 0, + wantExtendPrice: extendsPrice * 2, + }, + { + name: "pass - extends same owner, 5+ letters, N years", + dymName: "bridge", + existingDymName: &dymnstypes.DymName{ + Name: "bridge", + Owner: buyerA, + Controller: buyerA, + ExpireAt: s.now.Unix() + 100, + }, + newOwner: buyerA, + duration: 99, + wantFirstYearPrice: 0, + wantExtendPrice: extendsPrice * 99, + }, + { + name: "pass - extends expired, same owner, 5+ letters, 2 years", + dymName: "my-name", + existingDymName: &dymnstypes.DymName{ + Name: "my-name", + Owner: buyerA, + Controller: buyerA, + ExpireAt: s.now.Unix() - 1, + }, + newOwner: buyerA, + duration: 2, + wantFirstYearPrice: 0, + wantExtendPrice: extendsPrice * 2, + }, + { + name: "pass - extends expired, empty buyer, treat as take over", + dymName: "bridge", + existingDymName: &dymnstypes.DymName{ + Name: "bridge", + Owner: buyerA, + Controller: buyerA, + ExpireAt: s.now.Unix() - 1, + }, + newOwner: "", + duration: 2, + wantFirstYearPrice: 5, + wantExtendPrice: extendsPrice, + }, + { + name: "pass - take-over, 1 letter, 1 year", + dymName: "a", + existingDymName: &dymnstypes.DymName{ + Name: "a", + Owner: previousOwnerA, + Controller: previousOwnerA, + ExpireAt: s.now.Unix() - 1, + }, + newOwner: buyerA, + duration: 1, + wantFirstYearPrice: price1L, + wantExtendPrice: 0, + }, + { + name: "pass - take-over, 1 letter, 3 years", + dymName: "a", + existingDymName: &dymnstypes.DymName{ + Name: "a", + Owner: previousOwnerA, + Controller: previousOwnerA, + ExpireAt: s.now.Unix() - 1, + }, + newOwner: buyerA, + duration: 3, + wantFirstYearPrice: price1L, + wantExtendPrice: extendsPrice * 2, + }, + { + name: "pass - take-over, 6 letters, 1 year", + dymName: "bridge", + existingDymName: &dymnstypes.DymName{ + Name: "bridge", + Owner: previousOwnerA, + Controller: previousOwnerA, + ExpireAt: s.now.Unix() - 1, + }, + newOwner: buyerA, + duration: 1, + wantFirstYearPrice: price5PlusL, + wantExtendPrice: 0, + }, + { + name: "pass - take-over, 6 letters, 3 years", + dymName: "bridge", + existingDymName: &dymnstypes.DymName{ + Name: "bridge", + Owner: previousOwnerA, + Controller: previousOwnerA, + ExpireAt: s.now.Unix() - 1, + }, + newOwner: buyerA, + duration: 3, + wantFirstYearPrice: price5PlusL, + wantExtendPrice: extendsPrice * 2, + }, + { + name: "pass - new registration, 2 letters", + dymName: "aa", + existingDymName: nil, + newOwner: buyerA, + duration: 3, + wantFirstYearPrice: price2L, + wantExtendPrice: extendsPrice * 2, + }, + { + name: "pass - new registration, 3 letters", + dymName: "aaa", + existingDymName: nil, + newOwner: buyerA, + duration: 3, + wantFirstYearPrice: price3L, + wantExtendPrice: extendsPrice * 2, + }, + { + name: "pass - new registration, 4 letters", + dymName: "less", + existingDymName: nil, + newOwner: buyerA, + duration: 3, + wantFirstYearPrice: price4L, + wantExtendPrice: extendsPrice * 2, + }, + { + name: "pass - new registration, 5 letters", + dymName: "angel", + existingDymName: nil, + newOwner: buyerA, + duration: 3, + wantFirstYearPrice: price5PlusL, + wantExtendPrice: extendsPrice * 2, + }, + { + name: "fail - reject invalid Dym-Name", + dymName: "-a-", + existingDymName: nil, + newOwner: buyerA, + duration: 2, + wantErr: true, + wantErrContains: "invalid dym name", + }, + { + name: "fail - reject invalid duration", + dymName: "a", + existingDymName: nil, + newOwner: buyerA, + duration: 0, + wantErr: true, + wantErrContains: "duration must be at least 1 year", + }, + { + name: "fail - reject estimation for Dym-Name owned by another and not expired", + dymName: "a", + existingDymName: &dymnstypes.DymName{ + Name: "a", + Owner: previousOwnerA, + Controller: previousOwnerA, + ExpireAt: s.now.Unix() + 100, + }, + newOwner: buyerA, + duration: 1, + wantErr: true, + wantErrContains: "you are not the owner", + }, + { + name: "fail - reject estimation for Dym-Name owned by another and not expired, empty buyer", + dymName: "a", + existingDymName: &dymnstypes.DymName{ + Name: "a", + Owner: previousOwnerA, + Controller: previousOwnerA, + ExpireAt: s.now.Unix() + 100, + }, + newOwner: "", + duration: 1, + wantErr: true, + wantErrContains: "you are not the owner", + }, + { + name: "pass - allow estimation for take-over, regardless grace period", + dymName: "a", + existingDymName: &dymnstypes.DymName{ + Name: "a", + Owner: previousOwnerA, + Controller: previousOwnerA, + ExpireAt: s.now.Unix() - 1, // still in grace period + }, + newOwner: buyerA, + duration: 3, + wantErr: false, + wantFirstYearPrice: price1L, + wantExtendPrice: extendsPrice * 2, + }, + { + name: "pass - allow estimation for take-over, regardless grace period, empty buyer", + dymName: "a", + existingDymName: &dymnstypes.DymName{ + Name: "a", + Owner: previousOwnerA, + Controller: previousOwnerA, + ExpireAt: s.now.Unix() - 1, // still in grace period + }, + newOwner: "", + duration: 3, + wantErr: false, + wantFirstYearPrice: price1L, + wantExtendPrice: extendsPrice * 2, + }, + } + for _, tt := range tests { + s.Run(tt.name, func() { + s.RefreshContext() + + s.Require().Positive(s.dymNsKeeper.MiscParams(s.ctx).GracePeriodDuration, "bad setup, must have grace period") + + if tt.existingDymName != nil { + err := s.dymNsKeeper.SetDymName(s.ctx, *tt.existingDymName) + s.Require().NoError(err) + } + + queryServer := dymnskeeper.NewQueryServerImpl(s.dymNsKeeper) + + resp, err := queryServer.EstimateRegisterName(sdk.WrapSDKContext(s.ctx), &dymnstypes.EstimateRegisterNameRequest{ + Name: tt.dymName, + Duration: tt.duration, + Owner: tt.newOwner, + }) + + if tt.wantErr { + s.Require().ErrorContains(err, tt.wantErrContains) + s.Nil(resp) + return + } + + s.Require().NoError(err) + s.Require().NotNil(resp) + + s.Equal(sdk.NewInt(tt.wantFirstYearPrice).Mul(priceMultiplier).String(), resp.FirstYearPrice.Amount.String()) + s.Equal(sdk.NewInt(tt.wantExtendPrice).Mul(priceMultiplier).String(), resp.ExtendPrice.Amount.String()) + s.Equal( + sdk.NewInt(tt.wantFirstYearPrice+tt.wantExtendPrice).Mul(priceMultiplier).String(), + resp.TotalPrice.Amount.String(), + "total price must be equals to sum of first year and extend price", + ) + s.Equal(denom, resp.FirstYearPrice.Denom) + s.Equal(denom, resp.ExtendPrice.Denom) + s.Equal(denom, resp.TotalPrice.Denom) + }) + } + + s.Run("reject nil request", func() { + queryServer := dymnskeeper.NewQueryServerImpl(s.dymNsKeeper) + resp, err := queryServer.EstimateRegisterName(sdk.WrapSDKContext(s.ctx), nil) + s.Require().ErrorContains(err, "invalid request") + s.Require().Nil(resp) + }) +} + +func (s *KeeperTestSuite) Test_queryServer_EstimateRegisterAlias() { + const denom = "atom" + const price1L int64 = 9 + const price2L int64 = 8 + const price3L int64 = 7 + const price4L int64 = 6 + const price5PlusL int64 = 5 + + // the number values used in this test will be multiplied by this value + priceMultiplier := sdk.NewInt(1e18) + + s.updateModuleParams(func(params dymnstypes.Params) dymnstypes.Params { + params.Price.PriceDenom = denom + params.Price.AliasPriceSteps = []sdkmath.Int{ + sdkmath.NewInt(price1L).Mul(priceMultiplier), + sdkmath.NewInt(price2L).Mul(priceMultiplier), + sdkmath.NewInt(price3L).Mul(priceMultiplier), + sdkmath.NewInt(price4L).Mul(priceMultiplier), + sdkmath.NewInt(price5PlusL).Mul(priceMultiplier), + } + params.Misc.GracePeriodDuration = 30 * 24 * time.Hour + + return params + }) + s.SaveCurrentContext() + + rollAppOwner := testAddr(1).bech32() + notTheOwnerOfRollApp := testAddr(2).bech32() + + const existingRollAppId = "rollapp_1-1" + const existingAlias = "occupied" + const notExistingRollAppId = "nad_0-0" + + tests := []struct { + name string + alias string + rollAppId string + owner string + preRunFunc func(s *KeeperTestSuite) + wantErr bool + wantErrContains string + wantPrice int64 + }{ + { + name: "pass - can estimate", + alias: "a", + rollAppId: existingRollAppId, + owner: rollAppOwner, + wantPrice: price1L, + }, + { + name: "pass - can estimate, 1 letter", + alias: "a", + rollAppId: existingRollAppId, + owner: rollAppOwner, + wantPrice: price1L, + }, + { + name: "pass - can estimate without RollApp-ID and owner", + alias: "a", + rollAppId: "", + owner: "", + wantPrice: price1L, + }, + { + name: "pass - can estimate, 2 letters", + alias: "oh", + rollAppId: existingRollAppId, + owner: rollAppOwner, + wantPrice: price2L, + }, + { + name: "pass - can estimate, 3 letters", + alias: "dog", + rollAppId: existingRollAppId, + owner: rollAppOwner, + wantPrice: price3L, + }, + { + name: "pass - can estimate, 4 letters", + alias: "pool", + rollAppId: existingRollAppId, + owner: rollAppOwner, + wantPrice: price4L, + }, + { + name: "pass - can estimate, 5 letters", + alias: "sword", + rollAppId: existingRollAppId, + owner: rollAppOwner, + wantPrice: price5PlusL, + }, + { + name: "pass - can estimate, 6 letters", + alias: "bridge", + rollAppId: existingRollAppId, + owner: rollAppOwner, + wantPrice: price5PlusL, + }, + { + name: "pass - can estimate, 5+ letters", + alias: "wholesome", + rollAppId: existingRollAppId, + owner: rollAppOwner, + wantPrice: price5PlusL, + }, + { + name: "fail - reject invalid alias", + alias: "-a-", + rollAppId: existingRollAppId, + owner: rollAppOwner, + wantErr: true, + wantErrContains: "invalid alias", + }, + { + name: "fail - reject invalid RollApp ID", + alias: "a", + rollAppId: "@rollapp", + owner: rollAppOwner, + wantErr: true, + wantErrContains: "RollApp not found", + }, + { + name: "fail - reject Roll App does not exist", + alias: "a", + rollAppId: notExistingRollAppId, + owner: rollAppOwner, + wantErr: true, + wantErrContains: "RollApp not found", + }, + { + name: "fail - reject not the owner of RollApp ID", + alias: "a", + rollAppId: existingRollAppId, + owner: notTheOwnerOfRollApp, + wantErr: true, + wantErrContains: "not the owner of the RollApp", + }, + { + name: "fail - reject alias that already taken", + alias: existingAlias, + rollAppId: existingRollAppId, + owner: rollAppOwner, + wantErr: true, + wantErrContains: "alias already taken", + }, + { + name: "fail - reject alias that reserved in params", + alias: "dym", + rollAppId: existingRollAppId, + owner: rollAppOwner, + preRunFunc: func(s *KeeperTestSuite) { + s.updateModuleParams(func(moduleParams dymnstypes.Params) dymnstypes.Params { + moduleParams.Chains.AliasesOfChainIds = []dymnstypes.AliasesOfChainId{ + { + ChainId: s.chainId, + Aliases: []string{"dym"}, + }, + } + return moduleParams + }) + }, + wantErr: true, + wantErrContains: "alias already taken", + }, + } + for _, tt := range tests { + s.Run(tt.name, func() { + s.RefreshContext() + + s.persistRollApp( + *newRollApp(existingRollAppId).WithOwner(rollAppOwner).WithAlias(existingAlias), + ) + + if tt.preRunFunc != nil { + tt.preRunFunc(s) + } + + queryServer := dymnskeeper.NewQueryServerImpl(s.dymNsKeeper) + + resp, err := queryServer.EstimateRegisterAlias(sdk.WrapSDKContext(s.ctx), &dymnstypes.EstimateRegisterAliasRequest{ + Alias: tt.alias, + RollappId: tt.rollAppId, + Owner: tt.owner, + }) + + if tt.wantErr { + s.Require().ErrorContains(err, tt.wantErrContains) + s.Nil(resp) + return + } + + s.Require().NoError(err) + s.Require().NotNil(resp) + + s.Equal(sdk.NewInt(tt.wantPrice).Mul(priceMultiplier).String(), resp.Price.Amount.String()) + s.Equal(denom, resp.Price.Denom) + }) + } + + s.Run("reject nil request", func() { + queryServer := dymnskeeper.NewQueryServerImpl(s.dymNsKeeper) + resp, err := queryServer.EstimateRegisterAlias(sdk.WrapSDKContext(s.ctx), nil) + s.Require().ErrorContains(err, "invalid request") + s.Require().Nil(resp) + }) +} + +func (s *KeeperTestSuite) Test_queryServer_ReverseResolveAddress() { + const nimChainId = "nim_1122-1" + + s.updateModuleParams(func(moduleParams dymnstypes.Params) dymnstypes.Params { + moduleParams.Chains.AliasesOfChainIds = []dymnstypes.AliasesOfChainId{ + { + ChainId: s.chainId, + Aliases: []string{"dym"}, + }, + { + ChainId: nimChainId, + Aliases: []string{"nim"}, + }, + } + return moduleParams + }) + s.SaveCurrentContext() + + s.Run("reject nil request", func() { + queryServer := dymnskeeper.NewQueryServerImpl(s.dymNsKeeper) + + resp, err := queryServer.ReverseResolveAddress(sdk.WrapSDKContext(s.ctx), nil) + s.Require().Error(err) + s.Require().Nil(resp) + }) + + s.Run("reject empty request", func() { + queryServer := dymnskeeper.NewQueryServerImpl(s.dymNsKeeper) + + resp, err := queryServer.ReverseResolveAddress(sdk.WrapSDKContext(s.ctx), &dymnstypes.ReverseResolveAddressRequest{ + Addresses: []string{}, + }) + s.Require().Error(err) + s.Require().Nil(resp) + }) + + ownerAcc := testAddr(1) + anotherAcc := testAddr(2) + icaAcc := testICAddr(3) + cosmosAcc := testAddr(4) + //goland:noinspection SpellCheckingInspection + bitcoinAddr := "12higDjoCCNXSA95xZMWUdPvXNmkAduhWv" + + tests := []struct { + name string + dymNames []dymnstypes.DymName + addresses []string + workingChainId string + wantErr bool + wantErrContains string + wantResult map[string]dymnstypes.ReverseResolveAddressResult + wantWorkingChainId string + }{ + { + name: "pass - mixed addresses type", + dymNames: []dymnstypes.DymName{ + { + Name: "a", + Owner: ownerAcc.bech32(), + Controller: ownerAcc.bech32(), + ExpireAt: s.now.Unix() + 100, + }, + }, + addresses: []string{ownerAcc.bech32(), ownerAcc.hexStr()}, + wantErr: false, + wantResult: map[string]dymnstypes.ReverseResolveAddressResult{ + ownerAcc.bech32(): { + Candidates: []string{"a@dym"}, + }, + ownerAcc.hexStr(): { + Candidates: []string{"a@dym"}, + }, + }, + wantWorkingChainId: s.chainId, + }, + { + name: "pass - ignore bad input address", + dymNames: []dymnstypes.DymName{ + { + Name: "a", + Owner: ownerAcc.bech32(), + Controller: ownerAcc.bech32(), + ExpireAt: s.now.Unix() + 100, + }, + }, + addresses: []string{ownerAcc.bech32(), ownerAcc.hexStr(), "@", string(make([]rune, 1000))}, + wantErr: false, + wantResult: map[string]dymnstypes.ReverseResolveAddressResult{ + ownerAcc.bech32(): { + Candidates: []string{"a@dym"}, + }, + ownerAcc.hexStr(): { + Candidates: []string{"a@dym"}, + }, + }, + wantWorkingChainId: s.chainId, + }, + { + name: "pass - working =-chain-id if empty is host-chain", + dymNames: nil, + addresses: []string{ownerAcc.bech32()}, + wantErr: false, + wantResult: map[string]dymnstypes.ReverseResolveAddressResult{ + ownerAcc.bech32(): { + Candidates: []string{}, + }, + }, + wantWorkingChainId: s.chainId, + }, + { + name: "pass - multiple addresses", + dymNames: []dymnstypes.DymName{ + { + Name: "a", + Owner: ownerAcc.bech32(), + Controller: ownerAcc.bech32(), + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{ + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "another.account", + Value: anotherAcc.bech32(), + }, + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "cosmoshub-4", + Path: "", + Value: cosmosAcc.bech32(), + }, + }, + }, + }, + addresses: []string{ + ownerAcc.bech32(), + anotherAcc.bech32(), + cosmosAcc.bech32(), + }, + workingChainId: s.chainId, + wantErr: false, + wantResult: map[string]dymnstypes.ReverseResolveAddressResult{ + ownerAcc.bech32(): { + Candidates: []string{"a@dym"}, + }, + anotherAcc.bech32(): { + Candidates: []string{"another.account.a@dym"}, + }, + cosmosAcc.bech32(): { + Candidates: []string{}, + }, + }, + wantWorkingChainId: s.chainId, + }, + { + name: "pass - only find on matching chain", + dymNames: []dymnstypes.DymName{ + { + Name: "a", + Owner: ownerAcc.bech32(), + Controller: ownerAcc.bech32(), + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{ + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "another.account", + Value: anotherAcc.bech32(), + }, + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "cosmoshub-4", + Path: "", + Value: cosmosAcc.bech32(), + }, + }, + }, + }, + addresses: []string{ + ownerAcc.bech32(), + anotherAcc.bech32(), + cosmosAcc.bech32(), + }, + workingChainId: "cosmoshub-4", + wantErr: false, + wantResult: map[string]dymnstypes.ReverseResolveAddressResult{ + ownerAcc.bech32(): { + Candidates: []string{}, + }, + anotherAcc.bech32(): { + Candidates: []string{}, + }, + cosmosAcc.bech32(): { + Candidates: []string{"a@cosmoshub-4"}, + }, + }, + wantWorkingChainId: "cosmoshub-4", + }, + { + name: "pass - multi-level sub-name", + dymNames: []dymnstypes.DymName{ + { + Name: "a", + Owner: ownerAcc.bech32(), + Controller: ownerAcc.bech32(), + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{ + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "a.b.c.d", + Value: ownerAcc.bech32(), + }, + }, + }, + }, + addresses: []string{ownerAcc.bech32()}, + workingChainId: s.chainId, + wantErr: false, + wantResult: map[string]dymnstypes.ReverseResolveAddressResult{ + ownerAcc.bech32(): { + Candidates: []string{"a@dym", "a.b.c.d.a@dym"}, + }, + }, + wantWorkingChainId: s.chainId, + }, + { + name: "pass - each address match multiple result", + dymNames: []dymnstypes.DymName{ + { + Name: "a", + Owner: ownerAcc.bech32(), + Controller: ownerAcc.bech32(), + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{ + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "a.b.c.d", + Value: ownerAcc.bech32(), + }, + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "another", + Value: anotherAcc.bech32(), + }, + }, + }, + { + Name: "b", + Owner: ownerAcc.bech32(), + Controller: ownerAcc.bech32(), + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{ + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "e.f.g.h", + Value: ownerAcc.bech32(), + }, + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "another", + Value: anotherAcc.bech32(), + }, + }, + }, + { + Name: "c", + Owner: anotherAcc.bech32(), + Controller: anotherAcc.bech32(), + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{ + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "d", + Value: ownerAcc.bech32(), + }, + }, + }, + }, + addresses: []string{ownerAcc.bech32(), anotherAcc.hexStr()}, + wantErr: false, + wantResult: map[string]dymnstypes.ReverseResolveAddressResult{ + ownerAcc.bech32(): { + Candidates: []string{"a@dym", "b@dym", "d.c@dym", "a.b.c.d.a@dym", "e.f.g.h.b@dym"}, + }, + anotherAcc.hexStr(): { + Candidates: []string{"c@dym", "another.a@dym", "another.b@dym"}, + }, + }, + wantWorkingChainId: s.chainId, + }, + { + name: "pass - alias not mapped if no alias", + dymNames: []dymnstypes.DymName{ + { + Name: "a", + Owner: ownerAcc.bech32(), + Controller: ownerAcc.bech32(), + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{ + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "cosmoshub-4", + Path: "", + Value: cosmosAcc.bech32(), + }, + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: nimChainId, + Path: "", + Value: ownerAcc.bech32(), + }, + }, + }, + }, + addresses: []string{cosmosAcc.bech32(), ownerAcc.bech32()}, + workingChainId: "cosmoshub-4", + wantErr: false, + wantResult: map[string]dymnstypes.ReverseResolveAddressResult{ + cosmosAcc.bech32(): { + Candidates: []string{"a@cosmoshub-4"}, + }, + ownerAcc.bech32(): { + Candidates: []string{}, + }, + }, + wantWorkingChainId: "cosmoshub-4", + }, + { + name: "pass - support ICA address", + dymNames: []dymnstypes.DymName{ + { + Name: "a", + Owner: ownerAcc.bech32(), + Controller: ownerAcc.bech32(), + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{ + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "ica", + Value: icaAcc.bech32(), + }, + }, + }, + { + Name: "ica", + Owner: icaAcc.bech32(), + Controller: icaAcc.bech32(), + ExpireAt: s.now.Unix() + 100, + }, + }, + addresses: []string{icaAcc.bech32(), icaAcc.hexStr()}, + wantErr: false, + wantResult: map[string]dymnstypes.ReverseResolveAddressResult{ + icaAcc.bech32(): { + Candidates: []string{"ica@dym", "ica.a@dym"}, + }, + icaAcc.hexStr(): { + Candidates: []string{"ica@dym", "ica.a@dym"}, + }, + }, + wantWorkingChainId: s.chainId, + }, + { + name: "pass - chains neither host-chain nor RollApp should not support reverse-resolve hex address", + dymNames: []dymnstypes.DymName{ + { + Name: "a", + Owner: ownerAcc.bech32(), + Controller: ownerAcc.bech32(), + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{ + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "cosmoshub-4", + Path: "", + Value: cosmosAcc.bech32(), + }, + }, + }, + }, + addresses: []string{cosmosAcc.bech32(), cosmosAcc.hexStr()}, + workingChainId: "cosmoshub-4", + wantErr: false, + wantResult: map[string]dymnstypes.ReverseResolveAddressResult{ + cosmosAcc.bech32(): { + Candidates: []string{"a@cosmoshub-4"}, + }, + cosmosAcc.hexStr(): { + Candidates: []string{}, + }, + }, + wantWorkingChainId: "cosmoshub-4", + }, + { + name: "pass - returns empty for non-reverse-resolvable address", + dymNames: []dymnstypes.DymName{ + { + Name: "a", + Owner: ownerAcc.bech32(), + Controller: ownerAcc.bech32(), + ExpireAt: s.now.Unix() + 100, + }, + }, + addresses: []string{anotherAcc.bech32(), anotherAcc.hexStr()}, + wantErr: false, + wantResult: map[string]dymnstypes.ReverseResolveAddressResult{ + anotherAcc.bech32(): { + Candidates: []string{}, + }, + anotherAcc.hexStr(): { + Candidates: []string{}, + }, + }, + wantWorkingChainId: s.chainId, + }, + { + name: "pass - reverse-resolve bitcoin address (neither bech32 nor hex address)", + dymNames: []dymnstypes.DymName{ + { + Name: "a", + Owner: ownerAcc.bech32(), + Controller: ownerAcc.bech32(), + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{ + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "bitcoin", + Value: bitcoinAddr, + }, + }, + }, + }, + addresses: []string{bitcoinAddr}, + workingChainId: "bitcoin", + wantErr: false, + wantResult: map[string]dymnstypes.ReverseResolveAddressResult{ + bitcoinAddr: { + Candidates: []string{"a@bitcoin"}, + }, + }, + wantWorkingChainId: "bitcoin", + }, + { + name: "fail - should limit the number of input addresses", + addresses: make([]string, 101), + workingChainId: s.chainId, + wantErr: true, + wantErrContains: "too many input addresses", + }, + } + for _, tt := range tests { + s.Run(tt.name, func() { + s.RefreshContext() + + for _, dymName := range tt.dymNames { + s.setDymNameWithFunctionsAfter(dymName) + } + + queryServer := dymnskeeper.NewQueryServerImpl(s.dymNsKeeper) + + resp, err := queryServer.ReverseResolveAddress(sdk.WrapSDKContext(s.ctx), &dymnstypes.ReverseResolveAddressRequest{ + Addresses: tt.addresses, + WorkingChainId: tt.workingChainId, + }) + + if tt.wantErr { + s.Require().NotEmpty(tt.wantErrContains, "mis-configured test case") + s.Require().Error(err) + s.Require().Contains(err.Error(), tt.wantErrContains) + s.Require().Nil(resp) + return + } + + s.Require().NoError(err) + s.Require().NotNil(resp) + if !reflect.DeepEqual(tt.wantResult, resp.Result) { + s.T().Errorf("got = %v, want %v", resp.Result, tt.wantResult) + } + s.Require().Equal(tt.wantWorkingChainId, resp.WorkingChainId) + }) + } +} + +func (s *KeeperTestSuite) Test_queryServer_TranslateAliasOrChainIdToChainId() { + registeredAlias := map[string]string{ + s.chainId: "dym", + "nim_1122-1": "nim", + } + + s.updateModuleParams(func(moduleParams dymnstypes.Params) dymnstypes.Params { + for chainIdHasAlias, alias := range registeredAlias { + moduleParams.Chains.AliasesOfChainIds = append(moduleParams.Chains.AliasesOfChainIds, dymnstypes.AliasesOfChainId{ + ChainId: chainIdHasAlias, + Aliases: []string{alias}, + }) + } + return moduleParams + }) + s.SaveCurrentContext() + + s.Run("reject nil request", func() { + queryServer := dymnskeeper.NewQueryServerImpl(s.dymNsKeeper) + + resp, err := queryServer.TranslateAliasOrChainIdToChainId(sdk.WrapSDKContext(s.ctx), nil) + s.Require().Error(err) + s.Require().Nil(resp) + }) + + s.Run("reject empty request", func() { + queryServer := dymnskeeper.NewQueryServerImpl(s.dymNsKeeper) + + resp, err := queryServer.TranslateAliasOrChainIdToChainId(sdk.WrapSDKContext(s.ctx), &dymnstypes.QueryTranslateAliasOrChainIdToChainIdRequest{ + AliasOrChainId: "", + }) + s.Require().Error(err) + s.Require().Nil(resp) + }) + + s.Run("resolve alias to chain-id", func() { + s.RefreshContext() + + queryServer := dymnskeeper.NewQueryServerImpl(s.dymNsKeeper) + + for chainIdHasAlias, alias := range registeredAlias { + resp, err := queryServer.TranslateAliasOrChainIdToChainId(sdk.WrapSDKContext(s.ctx), &dymnstypes.QueryTranslateAliasOrChainIdToChainIdRequest{ + AliasOrChainId: alias, + }) + s.Require().NoError(err) + s.Require().NotNil(resp) + s.Require().Equal(chainIdHasAlias, resp.ChainId) + } + }) + + s.Run("resolve chain-id to chain-id", func() { + s.RefreshContext() + + queryServer := dymnskeeper.NewQueryServerImpl(s.dymNsKeeper) + + for chainIdHasAlias := range registeredAlias { + resp, err := queryServer.TranslateAliasOrChainIdToChainId(sdk.WrapSDKContext(s.ctx), &dymnstypes.QueryTranslateAliasOrChainIdToChainIdRequest{ + AliasOrChainId: chainIdHasAlias, + }) + s.Require().NoError(err) + s.Require().NotNil(resp) + s.Require().Equal(chainIdHasAlias, resp.ChainId) + } + }) + + s.Run("treat unknown-chain-id as chain-id", func() { + s.RefreshContext() + + queryServer := dymnskeeper.NewQueryServerImpl(s.dymNsKeeper) + + for _, unknownChainId := range []string{ + "aaa", "bbb", "ccc", "ddd", "eee", + } { + resp, err := queryServer.TranslateAliasOrChainIdToChainId(sdk.WrapSDKContext(s.ctx), &dymnstypes.QueryTranslateAliasOrChainIdToChainIdRequest{ + AliasOrChainId: unknownChainId, + }) + s.Require().NoError(err) + s.Require().NotNil(resp) + s.Require().Equal(unknownChainId, resp.ChainId) + } + }) +} + +func (s *KeeperTestSuite) Test_queryServer_BuyOrderById() { + s.Run("reject nil request", func() { + queryServer := dymnskeeper.NewQueryServerImpl(s.dymNsKeeper) + + resp, err := queryServer.BuyOrderById(sdk.WrapSDKContext(s.ctx), nil) + s.Require().Error(err) + s.Require().Nil(resp) + }) + + buyerA := testAddr(1).bech32() + + tests := []struct { + name string + buyOrders []dymnstypes.BuyOrder + buyOrderId string + wantErr bool + wantErrContains string + wantOffer dymnstypes.BuyOrder + }{ + { + name: "pass - can return", + buyOrders: []dymnstypes.BuyOrder{ + { + Id: "101", + AssetId: "a", + AssetType: dymnstypes.TypeName, + Buyer: buyerA, + OfferPrice: s.coin(1), + }, + }, + buyOrderId: "101", + wantErr: false, + wantOffer: dymnstypes.BuyOrder{ + Id: "101", + AssetId: "a", + AssetType: dymnstypes.TypeName, + Buyer: buyerA, + OfferPrice: s.coin(1), + }, + }, + { + name: "pass - can return among multiple records", + buyOrders: []dymnstypes.BuyOrder{ + { + Id: "101", + AssetId: "a", + AssetType: dymnstypes.TypeName, + Buyer: buyerA, + OfferPrice: s.coin(1), + }, + { + Id: "102", + AssetId: "a", + AssetType: dymnstypes.TypeName, + Buyer: buyerA, + OfferPrice: s.coin(2), + }, + { + Id: "103", + AssetId: "b", + AssetType: dymnstypes.TypeName, + Buyer: buyerA, + OfferPrice: s.coin(3), + }, + }, + buyOrderId: "102", + wantErr: false, + wantOffer: dymnstypes.BuyOrder{ + Id: "102", + AssetId: "a", + AssetType: dymnstypes.TypeName, + Buyer: buyerA, + OfferPrice: s.coin(2), + }, + }, + { + name: "fail - return error if not found", + buyOrders: []dymnstypes.BuyOrder{ + { + Id: "101", + AssetId: "a", + AssetType: dymnstypes.TypeName, + Buyer: buyerA, + OfferPrice: s.coin(1), + }, + { + Id: "102", + AssetId: "a", + AssetType: dymnstypes.TypeName, + Buyer: buyerA, + OfferPrice: s.coin(2), + }, + }, + buyOrderId: "103", + wantErr: true, + wantErrContains: "buy order not found", + }, + { + name: "fail - reject empty offer-id", + buyOrders: []dymnstypes.BuyOrder{ + { + Id: "101", + AssetId: "a", + AssetType: dymnstypes.TypeName, + Buyer: buyerA, + OfferPrice: s.coin(1), + }, + }, + buyOrderId: "", + wantErr: true, + wantErrContains: "invalid Buy-Order ID", + }, + { + name: "fail - reject bad offer-id", + buyOrders: []dymnstypes.BuyOrder{ + { + Id: "101", + AssetId: "a", + AssetType: dymnstypes.TypeName, + Buyer: buyerA, + OfferPrice: s.coin(1), + }, + }, + buyOrderId: "@", + wantErr: true, + wantErrContains: "invalid Buy-Order ID", + }, + } + for _, tt := range tests { + s.Run(tt.name, func() { + s.RefreshContext() + + for _, offer := range tt.buyOrders { + err := s.dymNsKeeper.SetBuyOrder(s.ctx, offer) + s.Require().NoError(err) + } + + queryServer := dymnskeeper.NewQueryServerImpl(s.dymNsKeeper) + + resp, err := queryServer.BuyOrderById(sdk.WrapSDKContext(s.ctx), &dymnstypes.QueryBuyOrderByIdRequest{ + Id: tt.buyOrderId, + }) + + if tt.wantErr { + s.Require().ErrorContains(err, tt.wantErrContains) + s.Require().Nil(resp) + return + } + + s.Require().NoError(err) + s.Require().NotNil(resp) + + s.Require().Equal(tt.wantOffer, resp.BuyOrder) + }) + } +} + +func (s *KeeperTestSuite) Test_queryServer_BuyOrdersPlacedByAccount() { + s.Run("reject nil request", func() { + queryServer := dymnskeeper.NewQueryServerImpl(s.dymNsKeeper) + + resp, err := queryServer.BuyOrdersPlacedByAccount(sdk.WrapSDKContext(s.ctx), nil) + s.Require().Error(err) + s.Require().Nil(resp) + }) + + buyerA := testAddr(1).bech32() + anotherA := testAddr(2).bech32() + + tests := []struct { + name string + dymNames []dymnstypes.DymName + offers []dymnstypes.BuyOrder + account string + wantErr bool + wantOffers []dymnstypes.BuyOrder + }{ + { + name: "pass - can return", + offers: []dymnstypes.BuyOrder{ + { + Id: "101", + AssetId: "a", + AssetType: dymnstypes.TypeName, + Buyer: buyerA, + OfferPrice: s.coin(1), + }, + }, + account: buyerA, + wantErr: false, + wantOffers: []dymnstypes.BuyOrder{ + { + Id: "101", + AssetId: "a", + AssetType: dymnstypes.TypeName, + Buyer: buyerA, + OfferPrice: s.coin(1), + }, + }, + }, + { + name: "pass - returns all records made by account", + offers: []dymnstypes.BuyOrder{ + { + Id: "101", + AssetId: "a", + AssetType: dymnstypes.TypeName, + Buyer: buyerA, + OfferPrice: s.coin(1), + }, + { + Id: "102", + AssetId: "b", + AssetType: dymnstypes.TypeName, + Buyer: buyerA, + OfferPrice: s.coin(2), + }, + { + Id: "103", + AssetId: "c", + AssetType: dymnstypes.TypeName, + Buyer: anotherA, // should exclude this + OfferPrice: s.coin(3), + }, + { + Id: "104", + AssetId: "d", + AssetType: dymnstypes.TypeName, + Buyer: buyerA, + OfferPrice: s.coin(4), + }, + }, + account: buyerA, + wantErr: false, + wantOffers: []dymnstypes.BuyOrder{ + { + Id: "101", + AssetId: "a", + AssetType: dymnstypes.TypeName, + Buyer: buyerA, + OfferPrice: s.coin(1), + }, + { + Id: "102", + AssetId: "b", + AssetType: dymnstypes.TypeName, + Buyer: buyerA, + OfferPrice: s.coin(2), + }, + { + Id: "104", + AssetId: "d", + AssetType: dymnstypes.TypeName, + Buyer: buyerA, + OfferPrice: s.coin(4), + }, + }, + }, + { + name: "pass - return empty if no match", + offers: []dymnstypes.BuyOrder{ + { + Id: "101", + AssetId: "a", + AssetType: dymnstypes.TypeName, + Buyer: anotherA, + OfferPrice: s.coin(1), + }, + { + Id: "102", + AssetId: "a", + AssetType: dymnstypes.TypeName, + Buyer: anotherA, + OfferPrice: s.coin(1), + }, + }, + account: buyerA, + wantErr: false, + wantOffers: nil, + }, + { + name: "fail - reject empty account", + offers: []dymnstypes.BuyOrder{ + { + Id: "101", + AssetId: "a", + AssetType: dymnstypes.TypeName, + Buyer: buyerA, + OfferPrice: s.coin(1), + }, + }, + account: "", + wantErr: true, + }, + { + name: "fail - reject bad account", + offers: []dymnstypes.BuyOrder{ + { + Id: "101", + AssetId: "a", + AssetType: dymnstypes.TypeName, + Buyer: buyerA, + OfferPrice: s.coin(1), + }, + }, + account: "0x1", + wantErr: true, + }, + } + for _, tt := range tests { + s.Run(tt.name, func() { + s.RefreshContext() + + for _, dymName := range tt.dymNames { + err := s.dymNsKeeper.SetDymName(s.ctx, dymName) + s.Require().NoError(err) + } + + for _, offer := range tt.offers { + err := s.dymNsKeeper.SetBuyOrder(s.ctx, offer) + s.Require().NoError(err) + + err = s.dymNsKeeper.AddReverseMappingAssetIdToBuyOrder(s.ctx, offer.AssetId, offer.AssetType, offer.Id) + s.Require().NoError(err) + + err = s.dymNsKeeper.AddReverseMappingBuyerToBuyOrderRecord(s.ctx, offer.Buyer, offer.Id) + s.Require().NoError(err) + } + + queryServer := dymnskeeper.NewQueryServerImpl(s.dymNsKeeper) + + resp, err := queryServer.BuyOrdersPlacedByAccount(sdk.WrapSDKContext(s.ctx), &dymnstypes.QueryBuyOrdersPlacedByAccountRequest{ + Account: tt.account, + }) + + if tt.wantErr { + s.Require().Error(err) + s.Require().Nil(resp) + return + } + + s.Require().NoError(err) + s.Require().NotNil(resp) + + sort.Slice(tt.wantOffers, func(i, j int) bool { + return tt.wantOffers[i].Id < tt.wantOffers[j].Id + }) + sort.Slice(resp.BuyOrders, func(i, j int) bool { + return resp.BuyOrders[i].Id < resp.BuyOrders[j].Id + }) + + s.Require().Equal(tt.wantOffers, resp.BuyOrders) + }) + } +} + +func (s *KeeperTestSuite) Test_queryServer_BuyOrdersByDymName() { + s.Run("reject nil request", func() { + queryServer := dymnskeeper.NewQueryServerImpl(s.dymNsKeeper) + + resp, err := queryServer.BuyOrdersByDymName(sdk.WrapSDKContext(s.ctx), nil) + s.Require().Error(err) + s.Require().Nil(resp) + }) + + buyerA := testAddr(1).bech32() + ownerA := testAddr(2).bech32() + anotherA := testAddr(3).bech32() + + tests := []struct { + name string + dymNames []dymnstypes.DymName + offers []dymnstypes.BuyOrder + dymName string + wantErr bool + wantOffers []dymnstypes.BuyOrder + }{ + { + name: "pass - can return", + dymNames: []dymnstypes.DymName{ + { + Name: "a", + Owner: ownerA, + Controller: ownerA, + ExpireAt: s.now.Unix() + 100, + }, + }, + offers: []dymnstypes.BuyOrder{ + { + Id: "101", + AssetId: "a", + AssetType: dymnstypes.TypeName, + Buyer: buyerA, + OfferPrice: s.coin(1), + }, + }, + dymName: "a", + wantErr: false, + wantOffers: []dymnstypes.BuyOrder{ + { + Id: "101", + AssetId: "a", + AssetType: dymnstypes.TypeName, + Buyer: buyerA, + OfferPrice: s.coin(1), + }, + }, + }, + { + name: "pass - returns all records by corresponding Dym-Name", + dymNames: []dymnstypes.DymName{ + { + Name: "a", + Owner: ownerA, + Controller: ownerA, + ExpireAt: s.now.Unix() + 100, + }, + { + Name: "b", + Owner: ownerA, + Controller: ownerA, + ExpireAt: s.now.Unix() + 100, + }, + }, + offers: []dymnstypes.BuyOrder{ + { + Id: "101", + AssetId: "a", + AssetType: dymnstypes.TypeName, + Buyer: buyerA, + OfferPrice: s.coin(1), + }, + { + Id: "102", + AssetId: "a", + AssetType: dymnstypes.TypeName, + Buyer: anotherA, + OfferPrice: s.coin(2), + }, + { + Id: "103", + AssetId: "b", + AssetType: dymnstypes.TypeName, + Buyer: anotherA, + OfferPrice: s.coin(3), + }, + }, + dymName: "a", + wantErr: false, + wantOffers: []dymnstypes.BuyOrder{ + { + Id: "101", + AssetId: "a", + AssetType: dymnstypes.TypeName, + Buyer: buyerA, + OfferPrice: s.coin(1), + }, + { + Id: "102", + AssetId: "a", + AssetType: dymnstypes.TypeName, + Buyer: anotherA, + OfferPrice: s.coin(2), + }, + }, + }, + { + name: "pass - return empty if no match", + dymNames: []dymnstypes.DymName{ + { + Name: "a", + Owner: ownerA, + Controller: ownerA, + ExpireAt: s.now.Unix() + 100, + }, + { + Name: "b", + Owner: ownerA, + Controller: ownerA, + ExpireAt: s.now.Unix() + 100, + }, + }, + offers: []dymnstypes.BuyOrder{ + { + Id: "101", + AssetId: "a", + AssetType: dymnstypes.TypeName, + Buyer: buyerA, + OfferPrice: s.coin(1), + }, + { + Id: "102", + AssetId: "a", + AssetType: dymnstypes.TypeName, + Buyer: anotherA, + OfferPrice: s.coin(2), + }, + { + Id: "103", + AssetId: "b", + AssetType: dymnstypes.TypeName, + Buyer: anotherA, + OfferPrice: s.coin(3), + }, + }, + dymName: "c", + wantErr: false, + wantOffers: nil, + }, + { + name: "fail - reject empty Dym-Name", + dymNames: []dymnstypes.DymName{ + { + Name: "a", + Owner: ownerA, + Controller: ownerA, + ExpireAt: s.now.Unix() + 100, + }, + }, + offers: []dymnstypes.BuyOrder{ + { + Id: "101", + AssetId: "a", + AssetType: dymnstypes.TypeName, + Buyer: buyerA, + OfferPrice: s.coin(1), + }, + }, + dymName: "", + wantErr: true, + }, + { + name: "fail - reject bad Dym-Name", + dymNames: []dymnstypes.DymName{ + { + Name: "a", + Owner: ownerA, + Controller: ownerA, + ExpireAt: s.now.Unix() + 100, + }, + }, + offers: []dymnstypes.BuyOrder{ + { + Id: "101", + AssetId: "a", + AssetType: dymnstypes.TypeName, + Buyer: buyerA, + OfferPrice: s.coin(1), + }, + }, + dymName: "@", + wantErr: true, + }, + } + for _, tt := range tests { + s.Run(tt.name, func() { + s.RefreshContext() + + for _, dymName := range tt.dymNames { + err := s.dymNsKeeper.SetDymName(s.ctx, dymName) + s.Require().NoError(err) + } + + for _, offer := range tt.offers { + err := s.dymNsKeeper.SetBuyOrder(s.ctx, offer) + s.Require().NoError(err) + + err = s.dymNsKeeper.AddReverseMappingAssetIdToBuyOrder(s.ctx, offer.AssetId, offer.AssetType, offer.Id) + s.Require().NoError(err) + + err = s.dymNsKeeper.AddReverseMappingBuyerToBuyOrderRecord(s.ctx, offer.Buyer, offer.Id) + s.Require().NoError(err) + } + + queryServer := dymnskeeper.NewQueryServerImpl(s.dymNsKeeper) + + resp, err := queryServer.BuyOrdersByDymName(sdk.WrapSDKContext(s.ctx), &dymnstypes.QueryBuyOrdersByDymNameRequest{ + Name: tt.dymName, + }) + + if tt.wantErr { + s.Require().Error(err) + s.Require().Nil(resp) + return + } + + s.Require().NoError(err) + s.Require().NotNil(resp) + + sort.Slice(tt.wantOffers, func(i, j int) bool { + return tt.wantOffers[i].Id < tt.wantOffers[j].Id + }) + sort.Slice(resp.BuyOrders, func(i, j int) bool { + return resp.BuyOrders[i].Id < resp.BuyOrders[j].Id + }) + + s.Require().Equal(tt.wantOffers, resp.BuyOrders) + }) + } +} + +func (s *KeeperTestSuite) Test_queryServer_BuyOrdersOfDymNamesOwnedByAccount() { + s.Run("reject nil request", func() { + queryServer := dymnskeeper.NewQueryServerImpl(s.dymNsKeeper) + + resp, err := queryServer.BuyOrdersOfDymNamesOwnedByAccount(sdk.WrapSDKContext(s.ctx), nil) + s.Require().Error(err) + s.Require().Nil(resp) + }) + + buyerA := testAddr(1).bech32() + ownerA := testAddr(2).bech32() + anotherA := testAddr(3).bech32() + + tests := []struct { + name string + dymNames []dymnstypes.DymName + offers []dymnstypes.BuyOrder + owner string + wantErr bool + wantOffers []dymnstypes.BuyOrder + }{ + { + name: "pass - can return", + dymNames: []dymnstypes.DymName{ + { + Name: "a", + Owner: ownerA, + Controller: ownerA, + ExpireAt: s.now.Unix() + 100, + }, + }, + offers: []dymnstypes.BuyOrder{ + { + Id: "101", + AssetId: "a", + AssetType: dymnstypes.TypeName, + Buyer: buyerA, + OfferPrice: s.coin(1), + }, + }, + owner: ownerA, + wantErr: false, + wantOffers: []dymnstypes.BuyOrder{ + { + Id: "101", + AssetId: "a", + AssetType: dymnstypes.TypeName, + Buyer: buyerA, + OfferPrice: s.coin(1), + }, + }, + }, + { + name: "pass - returns all records by corresponding Dym-Name", + dymNames: []dymnstypes.DymName{ + { + Name: "a", + Owner: ownerA, + Controller: ownerA, + ExpireAt: s.now.Unix() + 100, + }, + { + Name: "b", + Owner: ownerA, + Controller: ownerA, + ExpireAt: s.now.Unix() + 100, + }, + { + Name: "c", + Owner: anotherA, + Controller: anotherA, + ExpireAt: s.now.Unix() + 100, + }, + }, + offers: []dymnstypes.BuyOrder{ + { + Id: "101", + AssetId: "a", + AssetType: dymnstypes.TypeName, + Buyer: buyerA, + OfferPrice: s.coin(1), + }, + { + Id: "102", + AssetId: "a", + AssetType: dymnstypes.TypeName, + Buyer: anotherA, + OfferPrice: s.coin(2), + }, + { + Id: "103", + AssetId: "b", + AssetType: dymnstypes.TypeName, + Buyer: anotherA, + OfferPrice: s.coin(3), + }, + { + Id: "104", + AssetId: "c", + AssetType: dymnstypes.TypeName, + Buyer: ownerA, + OfferPrice: s.coin(3), + }, + }, + owner: ownerA, + wantErr: false, + wantOffers: []dymnstypes.BuyOrder{ + { + Id: "101", + AssetId: "a", + AssetType: dymnstypes.TypeName, + Buyer: buyerA, + OfferPrice: s.coin(1), + }, + { + Id: "102", + AssetId: "a", + AssetType: dymnstypes.TypeName, + Buyer: anotherA, + OfferPrice: s.coin(2), + }, + { + Id: "103", + AssetId: "b", + AssetType: dymnstypes.TypeName, + Buyer: anotherA, + OfferPrice: s.coin(3), + }, + }, + }, + { + name: "pass - return empty if no match", + dymNames: []dymnstypes.DymName{ + { + Name: "a", + Owner: ownerA, + Controller: ownerA, + ExpireAt: s.now.Unix() + 100, + }, + { + Name: "b", + Owner: ownerA, + Controller: ownerA, + ExpireAt: s.now.Unix() + 100, + }, + }, + offers: []dymnstypes.BuyOrder{ + { + Id: "101", + AssetId: "a", + AssetType: dymnstypes.TypeName, + Buyer: buyerA, + OfferPrice: s.coin(1), + }, + { + Id: "102", + AssetId: "a", + AssetType: dymnstypes.TypeName, + Buyer: anotherA, + OfferPrice: s.coin(2), + }, + { + Id: "103", + AssetId: "b", + AssetType: dymnstypes.TypeName, + Buyer: anotherA, + OfferPrice: s.coin(3), + }, + }, + owner: anotherA, + wantErr: false, + wantOffers: []dymnstypes.BuyOrder{}, + }, + { + name: "fail - reject empty account", + dymNames: []dymnstypes.DymName{ + { + Name: "a", + Owner: ownerA, + Controller: ownerA, + ExpireAt: s.now.Unix() + 100, + }, + }, + offers: []dymnstypes.BuyOrder{ + { + Id: "101", + AssetId: "a", + AssetType: dymnstypes.TypeName, + Buyer: buyerA, + OfferPrice: s.coin(1), + }, + }, + owner: "", + wantErr: true, + }, + { + name: "fail - reject bad account", + dymNames: []dymnstypes.DymName{ + { + Name: "a", + Owner: ownerA, + Controller: ownerA, + ExpireAt: s.now.Unix() + 100, + }, + }, + offers: []dymnstypes.BuyOrder{ + { + Id: "101", + AssetId: "a", + AssetType: dymnstypes.TypeName, + Buyer: buyerA, + OfferPrice: s.coin(1), + }, + }, + owner: "0x1", + wantErr: true, + }, + } + for _, tt := range tests { + s.Run(tt.name, func() { + s.RefreshContext() + + for _, dymName := range tt.dymNames { + s.setDymNameWithFunctionsAfter(dymName) + } + + for _, offer := range tt.offers { + err := s.dymNsKeeper.SetBuyOrder(s.ctx, offer) + s.Require().NoError(err) + + err = s.dymNsKeeper.AddReverseMappingAssetIdToBuyOrder(s.ctx, offer.AssetId, offer.AssetType, offer.Id) + s.Require().NoError(err) + + err = s.dymNsKeeper.AddReverseMappingBuyerToBuyOrderRecord(s.ctx, offer.Buyer, offer.Id) + s.Require().NoError(err) + } + + queryServer := dymnskeeper.NewQueryServerImpl(s.dymNsKeeper) + + resp, err := queryServer.BuyOrdersOfDymNamesOwnedByAccount(sdk.WrapSDKContext(s.ctx), &dymnstypes.QueryBuyOrdersOfDymNamesOwnedByAccountRequest{ + Account: tt.owner, + }) + + if tt.wantErr { + s.Require().Error(err) + s.Require().Nil(resp) + return + } + + s.Require().NoError(err) + s.Require().NotNil(resp) + + sort.Slice(tt.wantOffers, func(i, j int) bool { + return tt.wantOffers[i].Id < tt.wantOffers[j].Id + }) + sort.Slice(resp.BuyOrders, func(i, j int) bool { + return resp.BuyOrders[i].Id < resp.BuyOrders[j].Id + }) + + s.Require().Equal(tt.wantOffers, resp.BuyOrders) + }) + } +} + +func (s *KeeperTestSuite) Test_queryServer_Alias() { + rollApp1 := newRollApp("rollapp_1-1").WithOwner(testAddr(1).bech32()).WithAlias("one") + rollApp2 := newRollApp("rollapp_2-2").WithAlias("two") + rollApp3 := newRollApp("rollapp_3-1").WithAlias("three").WithAlias("another").WithAlias("alias") + + tests := []struct { + name string + rollApps []rollapp + preRunFunc func(s *KeeperTestSuite) + req *dymnstypes.QueryAliasRequest + wantErr bool + wantErrContains string + wantChainId string + wantFoundSellOrder bool + wantBuyOrderIds []string + wantSameChainAliases []string + }{ + { + name: "pass - can return alias of mapping in params", + rollApps: nil, + preRunFunc: func(s *KeeperTestSuite) { + s.updateModuleParams(func(params dymnstypes.Params) dymnstypes.Params { + params.Chains.AliasesOfChainIds = []dymnstypes.AliasesOfChainId{ + { + ChainId: "dymension_1100-1", + Aliases: []string{"dym"}, + }, + } + return params + }) + }, + req: &dymnstypes.QueryAliasRequest{Alias: "dym"}, + wantErr: false, + wantChainId: "dymension_1100-1", + wantFoundSellOrder: false, + wantBuyOrderIds: nil, + wantSameChainAliases: nil, + }, + { + name: "pass - can return alias of mapping in params, even if there are multiple mappings", + rollApps: nil, + preRunFunc: func(s *KeeperTestSuite) { + s.updateModuleParams(func(params dymnstypes.Params) dymnstypes.Params { + params.Chains.AliasesOfChainIds = []dymnstypes.AliasesOfChainId{ + { + ChainId: "dymension_1100-1", + Aliases: []string{"dym", "dymension"}, + }, + { + ChainId: "blumbus_111-1", + Aliases: []string{"blumbus"}, + }, + } + return params + }) + }, + req: &dymnstypes.QueryAliasRequest{Alias: "dymension"}, + wantErr: false, + wantChainId: "dymension_1100-1", + wantFoundSellOrder: false, + wantBuyOrderIds: nil, + wantSameChainAliases: []string{"dym"}, + }, + { + name: "pass - can return alias of mapping in params, also returns the other aliases mapped", + rollApps: nil, + preRunFunc: func(s *KeeperTestSuite) { + s.updateModuleParams(func(params dymnstypes.Params) dymnstypes.Params { + params.Chains.AliasesOfChainIds = []dymnstypes.AliasesOfChainId{ + { + ChainId: "dymension_1100-1", + Aliases: []string{"more", "dym", "dymension"}, + }, + } + return params + }) + }, + req: &dymnstypes.QueryAliasRequest{Alias: "dymension"}, + wantErr: false, + wantChainId: "dymension_1100-1", + wantFoundSellOrder: false, + wantBuyOrderIds: nil, + wantSameChainAliases: []string{"more", "dym"}, + }, + { + name: "pass - if alias is mapped both in params and RollApp alias, priority params", + rollApps: nil, + preRunFunc: func(s *KeeperTestSuite) { + s.updateModuleParams(func(params dymnstypes.Params) dymnstypes.Params { + params.Chains.AliasesOfChainIds = []dymnstypes.AliasesOfChainId{ + { + ChainId: "dymension_1100-1", + Aliases: []string{"dym", "more", "dymension"}, + }, + } + return params + }) + + s.persistRollApp( + *newRollApp("dym_1-1").WithAlias("dym"), + ) + + s.Require().True(s.dymNsKeeper.IsRollAppId(s.ctx, "dym_1-1")) + }, + req: &dymnstypes.QueryAliasRequest{Alias: "dym"}, + wantErr: false, + wantChainId: "dymension_1100-1", + wantFoundSellOrder: false, + wantBuyOrderIds: nil, + wantSameChainAliases: []string{"more", "dymension"}, + }, + { + name: "pass - returns Sell/Buy orders info if alias is mapped in RollApp alias", + rollApps: nil, + preRunFunc: func(s *KeeperTestSuite) { + s.persistRollApp(*rollApp1) + s.persistRollApp(*rollApp2) + + aliasSellOrder := s.newAliasSellOrder(rollApp1.alias).WithMinPrice(100).Build() + + err := s.dymNsKeeper.SetSellOrder(s.ctx, aliasSellOrder) + s.Require().NoError(err) + + aliasBuyOrder1 := s.newAliasBuyOrder(rollApp2.owner, rollApp1.alias, rollApp2.rollAppId). + WithID(dymnstypes.CreateBuyOrderId(dymnstypes.TypeAlias, 1)). + Build() + s.setBuyOrderWithFunctionsAfter(aliasBuyOrder1) + + aliasBuyOrder2 := s.newAliasBuyOrder(rollApp2.owner, rollApp1.alias, rollApp2.rollAppId). + WithID(dymnstypes.CreateBuyOrderId(dymnstypes.TypeAlias, 2)). + Build() + s.setBuyOrderWithFunctionsAfter(aliasBuyOrder2) + }, + req: &dymnstypes.QueryAliasRequest{Alias: rollApp1.alias}, + wantErr: false, + wantChainId: rollApp1.rollAppId, + wantFoundSellOrder: true, + wantBuyOrderIds: []string{ + dymnstypes.CreateBuyOrderId(dymnstypes.TypeAlias, 1), + dymnstypes.CreateBuyOrderId(dymnstypes.TypeAlias, 2), + }, + wantSameChainAliases: nil, + }, + { + name: "pass - include the other aliases of the same RollApp to response", + rollApps: nil, + preRunFunc: func(s *KeeperTestSuite) { + s.persistRollApp(*rollApp3) + }, + req: &dymnstypes.QueryAliasRequest{Alias: rollApp3.alias}, + wantErr: false, + wantChainId: rollApp3.rollAppId, + wantFoundSellOrder: false, + wantBuyOrderIds: nil, + wantSameChainAliases: []string{"another", "alias"}, + }, + { + name: "pass - if alias is mapped both in params and RollApp alias, priority params, ignore Sell/Buy orders", + rollApps: nil, + preRunFunc: func(s *KeeperTestSuite) { + rollApp := newRollApp("dym_3-3").WithOwner(testAddr(3).bech32()).WithAlias("dym") + s.persistRollApp(*rollApp) + + aliasSellOrder := s.newAliasSellOrder("dym").WithMinPrice(100).Build() + aliasBuyOrder := s.newAliasBuyOrder(rollApp1.owner, "dym", rollApp1.rollAppId).Build() + + err := s.dymNsKeeper.SetSellOrder(s.ctx, aliasSellOrder) + s.Require().NoError(err) + _, err = s.dymNsKeeper.InsertNewBuyOrder(s.ctx, aliasBuyOrder) + s.Require().NoError(err) + + s.updateModuleParams(func(params dymnstypes.Params) dymnstypes.Params { + params.Chains.AliasesOfChainIds = []dymnstypes.AliasesOfChainId{ + { + ChainId: "dymension_1100-1", + Aliases: []string{"dym"}, + }, + } + return params + }) + }, + req: &dymnstypes.QueryAliasRequest{Alias: "dym"}, + wantErr: false, + wantChainId: "dymension_1100-1", + wantFoundSellOrder: false, + wantBuyOrderIds: nil, + }, + { + name: "fail - reject nil request", + req: nil, + wantErr: true, + wantErrContains: "invalid request", + }, + { + name: "fail - returns error if not found", + req: &dymnstypes.QueryAliasRequest{Alias: "void"}, + wantErr: true, + wantErrContains: "not found", + }, + { + name: "fail - if input was detected as a chain-id returns as not found", + req: &dymnstypes.QueryAliasRequest{Alias: s.chainId}, + wantErr: true, + wantErrContains: "not found", + }, + { + name: "fail - if input was detected as a RollApp ID returns as not found", + preRunFunc: func(s *KeeperTestSuite) { + s.persistRollApp(*rollApp1) + }, + req: &dymnstypes.QueryAliasRequest{Alias: rollApp1.rollAppId}, + wantErr: true, + wantErrContains: "not found", + }, + } + for _, tt := range tests { + s.Run(tt.name, func() { + s.RefreshContext() + + for _, rollApp := range tt.rollApps { + s.persistRollApp(rollApp) + } + + if tt.preRunFunc != nil { + tt.preRunFunc(s) + } + + resp, err := dymnskeeper.NewQueryServerImpl(s.dymNsKeeper).Alias(sdk.WrapSDKContext(s.ctx), tt.req) + + if tt.wantErr { + s.Require().Error(err) + s.Require().Nil(resp) + s.Require().Contains(err.Error(), tt.wantErrContains) + return + } + + s.Require().NoError(err) + s.Require().NotNil(resp) + + s.Equal(tt.wantChainId, resp.ChainId) + s.Equal(tt.wantFoundSellOrder, resp.FoundSellOrder) + + if len(tt.wantBuyOrderIds) == 0 { + s.Empty(resp.BuyOrderIds) + } else { + sort.Strings(tt.wantBuyOrderIds) + sort.Strings(resp.BuyOrderIds) + s.Equal(tt.wantBuyOrderIds, resp.BuyOrderIds) + } + + if len(tt.wantSameChainAliases) == 0 { + s.Empty(resp.SameChainAliases) + } else { + sort.Strings(tt.wantSameChainAliases) + sort.Strings(resp.SameChainAliases) + s.Equal(tt.wantSameChainAliases, resp.SameChainAliases) + } + }) + } +} + +func (s *KeeperTestSuite) Test_queryServer_BuyOrdersByAlias() { + rollApp1 := *newRollApp("rollapp_1-1").WithOwner(testAddr(1).bech32()).WithAlias("one") + rollApp2 := *newRollApp("rollapp_2-2").WithAlias("two") + + aliasBuyOrder1 := s.newAliasBuyOrder(rollApp2.owner, rollApp1.alias, rollApp2.rollAppId). + WithID(dymnstypes.CreateBuyOrderId(dymnstypes.TypeAlias, 1)). + Build() + aliasBuyOrder2 := s.newAliasBuyOrder(rollApp2.owner, rollApp1.alias, rollApp2.rollAppId). + WithID(dymnstypes.CreateBuyOrderId(dymnstypes.TypeAlias, 2)). + Build() + + tests := []struct { + name string + rollapp []rollapp + buyOrders []dymnstypes.BuyOrder + preRunFunc func(s *KeeperTestSuite) + req *dymnstypes.QueryBuyOrdersByAliasRequest + wantErr bool + wantErrContains string + wantBuyOrderIds []string + }{ + { + name: "pass - can buy buy orders of the alias", + rollapp: []rollapp{rollApp1, rollApp2}, + buyOrders: []dymnstypes.BuyOrder{aliasBuyOrder1, aliasBuyOrder2}, + req: &dymnstypes.QueryBuyOrdersByAliasRequest{Alias: rollApp1.alias}, + wantErr: false, + wantBuyOrderIds: []string{ + aliasBuyOrder1.Id, aliasBuyOrder2.Id, + }, + }, + { + name: "pass - returns empty if alias present in params as alias of a chain", + rollapp: []rollapp{rollApp1, rollApp2}, + buyOrders: []dymnstypes.BuyOrder{aliasBuyOrder1, aliasBuyOrder2}, + preRunFunc: func(s *KeeperTestSuite) { + s.updateModuleParams(func(params dymnstypes.Params) dymnstypes.Params { + params.Chains.AliasesOfChainIds = []dymnstypes.AliasesOfChainId{ + { + ChainId: "some-chain", + Aliases: []string{rollApp1.alias}, + }, + } + return params + }) + }, + req: &dymnstypes.QueryBuyOrdersByAliasRequest{Alias: rollApp1.alias}, + wantErr: false, + wantBuyOrderIds: nil, + }, + { + name: "pass - returns empty if alias present in params as a chain-id", + rollapp: []rollapp{rollApp1, rollApp2}, + buyOrders: []dymnstypes.BuyOrder{aliasBuyOrder1, aliasBuyOrder2}, + preRunFunc: func(s *KeeperTestSuite) { + s.updateModuleParams(func(params dymnstypes.Params) dymnstypes.Params { + params.Chains.AliasesOfChainIds = []dymnstypes.AliasesOfChainId{ + { + ChainId: rollApp1.alias, + Aliases: nil, + }, + } + return params + }) + }, + req: &dymnstypes.QueryBuyOrdersByAliasRequest{Alias: rollApp1.alias}, + wantErr: false, + wantBuyOrderIds: nil, + }, + { + name: "fail - reject nil request", + req: nil, + wantErr: true, + wantErrContains: "invalid request", + wantBuyOrderIds: nil, + }, + { + name: "fail - reject bad alias", + req: &dymnstypes.QueryBuyOrdersByAliasRequest{Alias: "@@@"}, + wantErr: true, + wantErrContains: "invalid alias", + wantBuyOrderIds: nil, + }, + } + for _, tt := range tests { + s.Run(tt.name, func() { + s.RefreshContext() + + for _, rollapp := range tt.rollapp { + s.persistRollApp(rollapp) + } + for _, offer := range tt.buyOrders { + s.setBuyOrderWithFunctionsAfter(offer) + } + + if tt.preRunFunc != nil { + tt.preRunFunc(s) + } + + resp, err := dymnskeeper.NewQueryServerImpl(s.dymNsKeeper).BuyOrdersByAlias(sdk.WrapSDKContext(s.ctx), tt.req) + + if tt.wantErr { + s.Require().Error(err) + s.Require().Nil(resp) + s.Require().Contains(err.Error(), tt.wantErrContains) + return + } + + s.Require().NoError(err) + s.Require().NotNil(resp) + + if len(tt.wantBuyOrderIds) == 0 { + s.Empty(resp.BuyOrders) + } else { + var responseBuyOrderIds []string + for _, offer := range resp.BuyOrders { + responseBuyOrderIds = append(responseBuyOrderIds, offer.Id) + } + + sort.Strings(tt.wantBuyOrderIds) + sort.Strings(responseBuyOrderIds) + + s.Equal(tt.wantBuyOrderIds, responseBuyOrderIds) + } + }) + } +} + +//goland:noinspection GoSnakeCaseUsage +func (s *KeeperTestSuite) Test_queryServer_BuyOffersOfAliasesLinkedToRollApp() { + rollApp1 := *newRollApp("rollapp_1-1").WithAlias("one") + rollApp2 := *newRollApp("rollapp_2-2").WithAlias("another") + + aliasBuyOffer1_ra1_alias1 := s.newAliasBuyOrder(rollApp2.owner, rollApp1.alias, rollApp2.rollAppId). + WithID(dymnstypes.CreateBuyOrderId(dymnstypes.TypeAlias, 1)). + Build() + aliasBuyOffer2_ra1_alias1 := s.newAliasBuyOrder(rollApp2.owner, rollApp1.alias, rollApp2.rollAppId). + WithID(dymnstypes.CreateBuyOrderId(dymnstypes.TypeAlias, 2)). + Build() + + tests := []struct { + name string + rollapp []rollapp + buyOffers []dymnstypes.BuyOrder + preRunFunc func(s *KeeperTestSuite) + req *dymnstypes.QueryBuyOrdersOfAliasesLinkedToRollAppRequest + wantErr bool + wantErrContains string + wantBuyOrderIds []string + }{ + { + name: "pass - can returns if there is Buy Order", + rollapp: []rollapp{rollApp1, rollApp2}, + buyOffers: []dymnstypes.BuyOrder{aliasBuyOffer1_ra1_alias1}, + preRunFunc: nil, + req: &dymnstypes.QueryBuyOrdersOfAliasesLinkedToRollAppRequest{ + RollappId: rollApp1.rollAppId, + }, + wantErr: false, + wantBuyOrderIds: []string{aliasBuyOffer1_ra1_alias1.Id}, + }, + { + name: "pass - can empty if there is No buy order", + rollapp: []rollapp{rollApp1, rollApp2}, + buyOffers: nil, + preRunFunc: nil, + req: &dymnstypes.QueryBuyOrdersOfAliasesLinkedToRollAppRequest{ + RollappId: rollApp2.rollAppId, + }, + wantErr: false, + wantBuyOrderIds: []string{}, + }, + { + name: "pass - return multiple if there are many buy orders", + rollapp: []rollapp{rollApp1, rollApp2}, + buyOffers: []dymnstypes.BuyOrder{aliasBuyOffer1_ra1_alias1, aliasBuyOffer2_ra1_alias1}, + preRunFunc: nil, + req: &dymnstypes.QueryBuyOrdersOfAliasesLinkedToRollAppRequest{ + RollappId: rollApp1.rollAppId, + }, + wantErr: false, + wantBuyOrderIds: []string{ + aliasBuyOffer1_ra1_alias1.Id, aliasBuyOffer2_ra1_alias1.Id, + }, + }, + { + name: "pass - return multiple if there are buy orders associated with different aliases of the same RollApp", + rollapp: []rollapp{rollApp1, rollApp2}, + buyOffers: []dymnstypes.BuyOrder{aliasBuyOffer1_ra1_alias1, aliasBuyOffer2_ra1_alias1}, + preRunFunc: func(s *KeeperTestSuite) { + const alias2 = "more" + const alias3 = "alias" + + s.Require().NoError( + s.dymNsKeeper.SetAliasForRollAppId(s.ctx, rollApp1.rollAppId, alias2), + ) + s.Require().NoError( + s.dymNsKeeper.SetAliasForRollAppId(s.ctx, rollApp1.rollAppId, alias3), + ) + s.requireRollApp(rollApp1.rollAppId).HasAlias( + rollApp1.alias, alias2, alias3, + ) + + aliasBuyOffer3_ra1_alias2 := s.newAliasBuyOrder(rollApp2.owner, alias2, rollApp2.rollAppId). + WithID(dymnstypes.CreateBuyOrderId(dymnstypes.TypeAlias, 3)). + Build() + s.setBuyOrderWithFunctionsAfter(aliasBuyOffer3_ra1_alias2) + + aliasBuyOffer4_ra1_alias3 := s.newAliasBuyOrder(rollApp2.owner, alias3, rollApp2.rollAppId). + WithID(dymnstypes.CreateBuyOrderId(dymnstypes.TypeAlias, 4)). + Build() + s.setBuyOrderWithFunctionsAfter(aliasBuyOffer4_ra1_alias3) + }, + req: &dymnstypes.QueryBuyOrdersOfAliasesLinkedToRollAppRequest{ + RollappId: rollApp1.rollAppId, + }, + wantErr: false, + wantBuyOrderIds: []string{ + aliasBuyOffer1_ra1_alias1.Id, + aliasBuyOffer2_ra1_alias1.Id, + dymnstypes.CreateBuyOrderId(dymnstypes.TypeAlias, 3), + dymnstypes.CreateBuyOrderId(dymnstypes.TypeAlias, 4), + }, + }, + { + name: "pass - exclude buy orders of aliases which presents in params as chain-alias", + rollapp: []rollapp{rollApp1, rollApp2}, + buyOffers: []dymnstypes.BuyOrder{aliasBuyOffer1_ra1_alias1, aliasBuyOffer2_ra1_alias1}, + preRunFunc: func(s *KeeperTestSuite) { + const alias2 = "more" + const alias3 = "alias" + + s.Require().NoError( + s.dymNsKeeper.SetAliasForRollAppId(s.ctx, rollApp1.rollAppId, alias2), + ) + s.Require().NoError( + s.dymNsKeeper.SetAliasForRollAppId(s.ctx, rollApp1.rollAppId, alias3), + ) + s.requireRollApp(rollApp1.rollAppId).HasAlias( + rollApp1.alias, alias2, alias3, + ) + + aliasBuyOffer3_ra1_alias2 := s.newAliasBuyOrder(rollApp2.owner, alias2, rollApp2.rollAppId). + WithID(dymnstypes.CreateBuyOrderId(dymnstypes.TypeAlias, 3)). + Build() + s.setBuyOrderWithFunctionsAfter(aliasBuyOffer3_ra1_alias2) + + aliasBuyOffer4_ra1_alias3 := s.newAliasBuyOrder(rollApp2.owner, alias3, rollApp2.rollAppId). + WithID(dymnstypes.CreateBuyOrderId(dymnstypes.TypeAlias, 4)). + Build() + s.setBuyOrderWithFunctionsAfter(aliasBuyOffer4_ra1_alias3) + + s.updateModuleParams(func(params dymnstypes.Params) dymnstypes.Params { + params.Chains.AliasesOfChainIds = []dymnstypes.AliasesOfChainId{ + { + ChainId: "some-chain", + Aliases: []string{alias2}, + }, + } + return params + }) + }, + req: &dymnstypes.QueryBuyOrdersOfAliasesLinkedToRollAppRequest{ + RollappId: rollApp1.rollAppId, + }, + wantErr: false, + wantBuyOrderIds: []string{ + aliasBuyOffer1_ra1_alias1.Id, + aliasBuyOffer2_ra1_alias1.Id, + dymnstypes.CreateBuyOrderId(dymnstypes.TypeAlias, 4), + }, + }, + { + name: "fail - reject nil request", + req: nil, + wantErr: true, + wantErrContains: "invalid request", + wantBuyOrderIds: nil, + }, + { + name: "fail - reject bad RollApp ID", + req: &dymnstypes.QueryBuyOrdersOfAliasesLinkedToRollAppRequest{ + RollappId: "@@@", + }, + wantErr: true, + wantErrContains: "invalid RollApp ID", + wantBuyOrderIds: nil, + }, + { + name: "fail - reject if RollApp does not exists", + rollapp: []rollapp{rollApp1}, + req: &dymnstypes.QueryBuyOrdersOfAliasesLinkedToRollAppRequest{ + RollappId: "nah_0-0", + }, + wantErr: true, + wantErrContains: "RollApp not found", + wantBuyOrderIds: nil, + }, + } + for _, tt := range tests { + s.Run(tt.name, func() { + s.RefreshContext() + + for _, rollapp := range tt.rollapp { + s.persistRollApp(rollapp) + } + for _, offer := range tt.buyOffers { + s.setBuyOrderWithFunctionsAfter(offer) + } + + if tt.preRunFunc != nil { + tt.preRunFunc(s) + } + + resp, err := dymnskeeper.NewQueryServerImpl(s.dymNsKeeper).BuyOrdersOfAliasesLinkedToRollApp(sdk.WrapSDKContext(s.ctx), tt.req) + + if tt.wantErr { + s.Require().Error(err) + s.Require().Nil(resp) + s.Require().Contains(err.Error(), tt.wantErrContains) + return + } + + s.Require().NoError(err) + s.Require().NotNil(resp) + + if len(tt.wantBuyOrderIds) == 0 { + s.Empty(resp.BuyOrders) + } else { + var responseBuyOrderIds []string + for _, offer := range resp.BuyOrders { + responseBuyOrderIds = append(responseBuyOrderIds, offer.Id) + } + + sort.Strings(tt.wantBuyOrderIds) + sort.Strings(responseBuyOrderIds) + + s.Equal(tt.wantBuyOrderIds, responseBuyOrderIds) + } + }) + } +} + +func (s *KeeperTestSuite) Test_queryServer_Aliases() { + rollApp1 := *newRollApp("rollapp_1-1").WithAlias("one").WithAlias("two") + rollApp2 := *newRollApp("rollapp_2-2").WithAlias("three") + rollApp3WithoutAlias := *newRollApp("rollapp_3-1") + + tests := []struct { + name string + paramsAliasesByChainId []dymnstypes.AliasesOfChainId + rollApps []rollapp + chainId string + wantErr bool + wantErrContains string + want map[string]dymnstypes.MultipleAliases + }{ + { + name: "pass - can returns all chains aliases", + paramsAliasesByChainId: []dymnstypes.AliasesOfChainId{ + { + ChainId: "dymension_1100-1", + Aliases: []string{"dym", "dymension"}, + }, + { + ChainId: "blumbus_111-1", + Aliases: []string{"blumbus"}, + }, + }, + rollApps: []rollapp{rollApp1, rollApp2, rollApp3WithoutAlias}, + chainId: "", + wantErr: false, + want: map[string]dymnstypes.MultipleAliases{ + "dymension_1100-1": {Aliases: []string{"dym", "dymension"}}, + "blumbus_111-1": {Aliases: []string{"blumbus"}}, + rollApp1.rollAppId: {Aliases: rollApp1.aliases}, + rollApp2.rollAppId: {Aliases: rollApp2.aliases}, + }, + }, + { + name: "pass - can returns corresponding chain when filter, case in params", + paramsAliasesByChainId: []dymnstypes.AliasesOfChainId{ + { + ChainId: "dymension_1100-1", + Aliases: []string{"dym", "dymension"}, + }, + { + ChainId: "blumbus_111-1", + Aliases: []string{"blumbus"}, + }, + }, + rollApps: []rollapp{rollApp1, rollApp2, rollApp3WithoutAlias}, + chainId: "dymension_1100-1", + wantErr: false, + want: map[string]dymnstypes.MultipleAliases{ + "dymension_1100-1": {Aliases: []string{"dym", "dymension"}}, + }, + }, + { + name: "pass - can returns corresponding chain when filter, case in RollApp", + paramsAliasesByChainId: []dymnstypes.AliasesOfChainId{ + { + ChainId: "dymension_1100-1", + Aliases: []string{"dym", "dymension"}, + }, + { + ChainId: "blumbus_111-1", + Aliases: []string{"blumbus"}, + }, + }, + rollApps: []rollapp{rollApp1, rollApp2, rollApp3WithoutAlias}, + chainId: rollApp1.rollAppId, + wantErr: false, + want: map[string]dymnstypes.MultipleAliases{ + rollApp1.rollAppId: {Aliases: rollApp1.aliases}, + }, + }, + { + name: "pass - if an alias of a RollApp is reserved in params, exclude it", + paramsAliasesByChainId: []dymnstypes.AliasesOfChainId{ + { + ChainId: "dymension_1100-1", + Aliases: []string{ + "dym", + rollApp1.aliases[0], // reserved + }, + }, + }, + rollApps: []rollapp{rollApp1, rollApp2}, + chainId: "", + wantErr: false, + want: map[string]dymnstypes.MultipleAliases{ + "dymension_1100-1": { + Aliases: []string{ + "dym", + rollApp1.aliases[0], + }, + }, + rollApp1.rollAppId: { + Aliases: rollApp1.aliases[1:], // ignore the reserved one + }, + rollApp2.rollAppId: { + Aliases: rollApp2.aliases, + }, + }, + }, + { + name: "pass - if an alias of a RollApp is reserved in params, exclude it", + paramsAliasesByChainId: []dymnstypes.AliasesOfChainId{ + { + ChainId: "dymension_1100-1", + Aliases: []string{ + "dym", + rollApp1.aliases[0], // reserved + }, + }, + }, + rollApps: []rollapp{rollApp1}, + chainId: rollApp1.rollAppId, + wantErr: false, + want: map[string]dymnstypes.MultipleAliases{ + rollApp1.rollAppId: { + Aliases: rollApp1.aliases[1:], // ignore the reserved one + }, + }, + }, + { + name: "pass - if an alias of a RollApp is reserved in params, exclude it, if not any remaining alias, skip it", + paramsAliasesByChainId: []dymnstypes.AliasesOfChainId{ + { + ChainId: "dymension_1100-1", + Aliases: rollApp1.aliases, + }, + }, + rollApps: []rollapp{rollApp1}, + chainId: "", + wantErr: false, + want: map[string]dymnstypes.MultipleAliases{ + "dymension_1100-1": {Aliases: rollApp1.aliases}, + }, + }, + { + name: "pass - if an alias of a RollApp is reserved in params, exclude it, if not any remaining alias, skip it", + paramsAliasesByChainId: []dymnstypes.AliasesOfChainId{ + { + ChainId: "dymension_1100-1", + Aliases: rollApp1.aliases, + }, + }, + rollApps: []rollapp{rollApp1}, + chainId: rollApp1.rollAppId, + wantErr: false, + want: map[string]dymnstypes.MultipleAliases{}, // totally excluded + }, + { + name: "pass - if a RollApp ID presents in both params and local mapped alias, merge result", + paramsAliasesByChainId: []dymnstypes.AliasesOfChainId{ + { + ChainId: "dymension_1100-1", + Aliases: []string{"dym"}, + }, + { + ChainId: rollApp1.rollAppId, + Aliases: []string{"more", "alias"}, + }, + }, + rollApps: []rollapp{rollApp1, rollApp2, rollApp3WithoutAlias}, + chainId: "", + wantErr: false, + want: map[string]dymnstypes.MultipleAliases{ + "dymension_1100-1": {Aliases: []string{"dym"}}, + rollApp1.rollAppId: { + Aliases: append( // merged + []string{"more", "alias"}, // respect params, put it on head + rollApp1.aliases...), + }, + rollApp2.rollAppId: {Aliases: rollApp2.aliases}, + }, + }, + { + name: "pass - if a RollApp ID presents in both params and local mapped alias, merge result", + paramsAliasesByChainId: []dymnstypes.AliasesOfChainId{ + { + ChainId: "dymension_1100-1", + Aliases: []string{"dym"}, + }, + { + ChainId: rollApp1.rollAppId, + Aliases: []string{"more", "alias"}, + }, + }, + rollApps: []rollapp{rollApp1, rollApp2, rollApp3WithoutAlias}, + chainId: rollApp1.rollAppId, + wantErr: false, + want: map[string]dymnstypes.MultipleAliases{ + rollApp1.rollAppId: { + Aliases: append( // merged + []string{"more", "alias"}, // respect params, put it on head + rollApp1.aliases...), + }, + }, + }, + { + name: "pass - if a chain does not have alias (both params and RollApp), exclude from result", + paramsAliasesByChainId: []dymnstypes.AliasesOfChainId{ + { + ChainId: "dymension_1100-1", + Aliases: []string{"dym"}, + }, + { + ChainId: "blumbus_111-1", + Aliases: nil, + }, + }, + rollApps: []rollapp{rollApp1, rollApp3WithoutAlias}, + chainId: "", + wantErr: false, + want: map[string]dymnstypes.MultipleAliases{ + "dymension_1100-1": {Aliases: []string{"dym"}}, + rollApp1.rollAppId: {Aliases: rollApp1.aliases}, + }, + }, + { + name: "fail - reject bad chain-id in request", + paramsAliasesByChainId: nil, + rollApps: nil, + chainId: "@@@", + wantErr: true, + wantErrContains: "invalid chain id", + want: nil, + }, + } + for _, tt := range tests { + s.Run(tt.name, func() { + s.RefreshContext() + + s.updateModuleParams(func(moduleParams dymnstypes.Params) dymnstypes.Params { + moduleParams.Chains.AliasesOfChainIds = tt.paramsAliasesByChainId + return moduleParams + }) + + for _, rollApp := range tt.rollApps { + s.persistRollApp(rollApp) + } + + resp, err := dymnskeeper.NewQueryServerImpl(s.dymNsKeeper).Aliases(sdk.WrapSDKContext(s.ctx), &dymnstypes.QueryAliasesRequest{ + ChainId: tt.chainId, + }) + if tt.wantErr { + s.Require().ErrorContains(err, tt.wantErrContains) + s.Require().Nil(resp) + return + } + + s.Require().NoError(err) + s.Require().NotNil(resp) + + if len(tt.want) == 0 { + s.Empty(resp.AliasesByChainId) + } else if !s.True(reflect.DeepEqual(tt.want, resp.AliasesByChainId)) { + fmt.Println("Maps are not equals") + fmt.Println(" Expected:") + for chainId, aliases := range tt.want { + fmt.Printf(" %s: %v\n", chainId, aliases) + } + fmt.Println(" Got:") + for chainId, aliases := range resp.AliasesByChainId { + fmt.Printf(" %s: %v\n", chainId, aliases) + } + } + }) + } + + s.Run("fail - reject nil request", func() { + resp, err := dymnskeeper.NewQueryServerImpl(s.dymNsKeeper).Aliases(sdk.WrapSDKContext(s.ctx), nil) + s.Require().Error(err) + s.Require().Nil(resp) + s.Require().Contains(err.Error(), "invalid request") + }) +} diff --git a/x/dymns/keeper/hooks.go b/x/dymns/keeper/hooks.go new file mode 100644 index 000000000..c94ee63e1 --- /dev/null +++ b/x/dymns/keeper/hooks.go @@ -0,0 +1,369 @@ +package keeper + +import ( + "errors" + + dymnsutils "github.com/dymensionxyz/dymension/v3/x/dymns/utils" + + "github.com/cometbft/cometbft/libs/log" + + errorsmod "cosmossdk.io/errors" + + "github.com/dymensionxyz/gerr-cosmos/gerrc" + + "github.com/osmosis-labs/osmosis/v15/osmoutils" + + sdk "github.com/cosmos/cosmos-sdk/types" + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" + rollapptypes "github.com/dymensionxyz/dymension/v3/x/rollapp/types" + epochstypes "github.com/osmosis-labs/osmosis/v15/x/epochs/types" +) + +/* -------------------------------------------------------------------------- */ +/* x/epochs hooks */ +/* -------------------------------------------------------------------------- */ + +var _ epochstypes.EpochHooks = epochHooks{} + +type epochHooks struct { + Keeper +} + +// GetEpochHooks returns the epoch hooks for the module. +func (k Keeper) GetEpochHooks() epochstypes.EpochHooks { + return epochHooks{ + Keeper: k, + } +} + +// BeforeEpochStart is the epoch start hook. +func (e epochHooks) BeforeEpochStart(_ sdk.Context, _ string, _ int64) error { + return nil +} + +// AfterEpochEnd is the epoch end hook. +func (e epochHooks) AfterEpochEnd(ctx sdk.Context, epochIdentifier string, epochNumber int64) error { + miscParams := e.MiscParams(ctx) + + if epochIdentifier != miscParams.EndEpochHookIdentifier { + return nil + } + + logger := e.Logger(ctx).With("hook", "After-Epoch-End", "epoch-number", epochNumber, "epoch-identifier", epochIdentifier) + + if miscParams.EnableTradingName { + if err := e.processActiveDymNameSellOrders(ctx, logger); err != nil { + return err + } + } + + if miscParams.EnableTradingAlias { + if err := e.processActiveAliasSellOrders(ctx, logger); err != nil { + return err + } + } + + return nil +} + +// processActiveDymNameSellOrders process the finished Dym-Name Sell-Orders. +// Sell-Order will be deleted. If the Sell-Order has a winner, the Dym-Name ownership will be transferred. +func (e epochHooks) processActiveDymNameSellOrders(ctx sdk.Context, logger log.Logger) error { + activeSellOrdersExpiration := e.Keeper.GetActiveSellOrdersExpiration(ctx, dymnstypes.TypeName) + + finishedSOs := e.getFinishedSellOrders(ctx, activeSellOrdersExpiration, dymnstypes.TypeName, logger) + + if len(finishedSOs) < 1 { + return nil + } + + logger.Info("processing finished SOs.", "count", len(finishedSOs)) + + for _, so := range finishedSOs { + // each order should be processed in a branched context, if error, discard the state change + // and process next order, to prevent chain reaction when an individual order failed to process + errApplyStateChange := osmoutils.ApplyFuncIfNoError(ctx, func(ctx sdk.Context) error { + if so.HighestBid == nil { + e.DeleteSellOrder(ctx, so.AssetId, dymnstypes.TypeName) + return nil + } + + if err := e.CompleteDymNameSellOrder(ctx, so.AssetId); err != nil { + return err + } + + return nil + }) + + if errApplyStateChange == nil { + activeSellOrdersExpiration.Remove(so.AssetId) + } else { + logger.Error( + "failed to process finished sell order.", "asset-id", so.AssetId, + "bid", so.HighestBid != nil, + "error", errApplyStateChange, + ) + } + } + + if err := e.SetActiveSellOrdersExpiration(ctx, activeSellOrdersExpiration, dymnstypes.TypeName); err != nil { + return errorsmod.Wrap(errors.Join(gerrc.ErrInternal, err), "failed to update active SO expiry") + } + + return nil +} + +// processActiveAliasSellOrders process the finished Alias Sell-Orders. +// Sell-Order will be deleted. +// If the Sell-Order has a winner, the Alias linking will be updated. +// Sell-Orders for the aliases that are prohibited to trade will be force cancelled, +// please read the code body for more information about what it is. +func (e epochHooks) processActiveAliasSellOrders(ctx sdk.Context, logger log.Logger) error { + activeSellOrdersExpiration := e.Keeper.GetActiveSellOrdersExpiration(ctx, dymnstypes.TypeAlias) + + finishedSOs := e.getFinishedSellOrders(ctx, activeSellOrdersExpiration, dymnstypes.TypeAlias, logger) + + if len(finishedSOs) < 1 { + return nil + } + + logger.Info("processing finished SOs.", "count", len(finishedSOs)) + + prohibitedToTradeAliases := e.GetAllAliasAndChainIdInParams(ctx) + + for _, so := range finishedSOs { + // each order should be processed in a branched context, if error, discard the state change + // and process next order, to prevent chain reaction when an individual order failed to process + errApplyStateChange := osmoutils.ApplyFuncIfNoError(ctx, func(ctx sdk.Context) error { + if so.HighestBid == nil { + e.DeleteSellOrder(ctx, so.AssetId, dymnstypes.TypeAlias) + return nil + } + + /** + For the Sell-Orders which the assets are prohibited to trade, + the Sell-Order will be force cancelled and the bids will be refunded. + + Why some aliases are prohibited to trade? And what are they? + In module params, there is a list of alias mapping for some external well-known chains. + So those aliases are considered as reserved for the external chains, + therefor trading is not allowed. + + Why can someone own a prohibited alias? + An alias can be bought before the reservation was made. + But when the alias becomes reserved for the external well-known chains, + the alias will be prohibited to trade. + + Why can someone place a Sell-Order for the prohibited alias? + When a Sell-Order created before the reservation was made. + */ + if _, forceCancel := prohibitedToTradeAliases[so.AssetId]; forceCancel { + // Sell-Order will be force cancelled and refund bids if any, + // when the alias is prohibited to trade + if err := e.RefundBid(ctx, *so.HighestBid, dymnstypes.TypeAlias); err != nil { + return err + } + e.DeleteSellOrder(ctx, so.AssetId, dymnstypes.TypeAlias) + return nil + } + + if err := e.CompleteAliasSellOrder(ctx, so.AssetId); err != nil { + return err + } + + return nil + }) + + if errApplyStateChange == nil { + activeSellOrdersExpiration.Remove(so.AssetId) + } else { + _, forceCancel := prohibitedToTradeAliases[so.AssetId] + logger.Error( + "failed to process finished sell order.", "asset-id", so.AssetId, + "bid", so.HighestBid != nil, "prohibited", forceCancel, + "error", errApplyStateChange, + ) + } + } + + if err := e.SetActiveSellOrdersExpiration(ctx, activeSellOrdersExpiration, dymnstypes.TypeAlias); err != nil { + return errorsmod.Wrap(errors.Join(gerrc.ErrInternal, err), "failed to update active SO expiry") + } + + return nil +} + +// getFinishedSellOrders returns the finished Sell-Orders for the asset type. +// Finished Sell-Orders are the Sell-Orders that have expired. +// Expired sell-orders can either have a bid or not. In both cases we consider them as `finished`. +func (e epochHooks) getFinishedSellOrders( + ctx sdk.Context, + activeSellOrdersExpiration *dymnstypes.ActiveSellOrdersExpiration, assetType dymnstypes.AssetType, + logger log.Logger, +) (finishedSellOrders []dymnstypes.SellOrder) { + blockEpochUTC := ctx.BlockTime().Unix() + + for _, record := range activeSellOrdersExpiration.Records { + if record.ExpireAt > blockEpochUTC { + // skip not expired ones + continue + } + + so := e.GetSellOrder(ctx, record.AssetId, assetType) + + if so == nil { + logger.Error( + "invalid entry on Active Sell Order Expiration records: Sell Order not found.", + "asset-id", record.AssetId, "asset-type", assetType.PrettyName(), + ) + // ignore the invalid entries for now, invariant will catch it + continue + } + + if !so.HasFinished(blockEpochUTC) { + logger.Error( + "invalid entry on Active Sell Order Expiration records: Sell Order not yet finished.", + "asset-id", record.AssetId, "asset-type", assetType.PrettyName(), + "record-expiry", record.ExpireAt, "actual-expiry", so.ExpireAt, + ) + // ignore the invalid entries for now, invariant will catch it + continue + } + + finishedSellOrders = append(finishedSellOrders, *so) + } + + return +} + +/* -------------------------------------------------------------------------- */ +/* x/rollapp hooks */ +/* -------------------------------------------------------------------------- */ + +// GetRollAppHooks returns the RollApp hooks struct. +func (k Keeper) GetRollAppHooks() rollapptypes.RollappHooks { + return rollappHooks{ + Keeper: k, + } +} + +type rollappHooks struct { + Keeper +} + +var _ rollapptypes.RollappHooks = rollappHooks{} + +func (h rollappHooks) RollappCreated(ctx sdk.Context, rollappID, alias string, creatorAddr sdk.AccAddress) error { + if alias == "" { + return nil + } + + // ensure RollApp record is set + if !h.Keeper.IsRollAppId(ctx, rollappID) { + return errorsmod.Wrapf(gerrc.ErrInvalidArgument, "not a RollApp chain-id: %s", rollappID) + } + + if !dymnsutils.IsValidAlias(alias) { + return errorsmod.Wrapf(gerrc.ErrInvalidArgument, "invalid alias format: %s", alias) + } + + if !h.Keeper.CanUseAliasForNewRegistration(ctx, alias) { + return errorsmod.Wrapf(gerrc.ErrAlreadyExists, "alias already in use or preserved: %s", alias) + } + + priceParams := h.Keeper.PriceParams(ctx) + + aliasCost := sdk.NewCoins( + sdk.NewCoin( + priceParams.PriceDenom, priceParams.GetAliasPrice(alias), + ), + ) + + return h.Keeper.registerAliasForRollApp(ctx, rollappID, creatorAddr, alias, aliasCost) +} + +func (h rollappHooks) BeforeUpdateState(_ sdk.Context, _ string, _ string, _ bool) error { + return nil +} + +func (h rollappHooks) AfterStateFinalized(_ sdk.Context, _ string, _ *rollapptypes.StateInfo) error { + return nil +} + +func (h rollappHooks) FraudSubmitted(_ sdk.Context, _ string, _ uint64, _ string) error { + return nil +} + +// FutureRollappHooks is temporary added to handle future hooks that not available yet. +type FutureRollappHooks interface { + // OnRollAppIdChanged is called when a RollApp's ID is changed, typically due to fraud submission. + // It migrates all aliases and Dym-Names associated with the previous RollApp ID to the new one. + // This function executes step by step in a branched context to prevent side effects, and any errors + // during execution will result in the state changes being discarded. + // + // Parameters: + // - ctx: The SDK context + // - previousRollAppId: The original ID of the RollApp + // - newRollAppId: The new ID assigned to the RollApp + OnRollAppIdChanged(ctx sdk.Context, previousRollAppId, newRollAppId string) + // Just a pseudo method signature, the actual method signature might be different. + + // TODO DymNS: connect to the actual implementation when the hooks are available. + // The implementation of OnRollAppIdChanged assume that both of the RollApp records are exists in the x/rollapp store. +} + +var _ FutureRollappHooks = rollappHooks{} + +func (k Keeper) GetFutureRollAppHooks() FutureRollappHooks { + return rollappHooks{ + Keeper: k, + } +} + +// OnRollAppIdChanged implements FutureRollappHooks. +func (h rollappHooks) OnRollAppIdChanged(ctx sdk.Context, previousRollAppId, newRollAppId string) { + logger := h.Logger(ctx).With( + "old-rollapp-id", previousRollAppId, "new-rollapp-id", newRollAppId, + ) + + logger.Info("begin DymNS hook on RollApp ID changed.") + + // Due to the critical nature reason of the hook, + // each step will be done in branched context and drop if error, to prevent any side effects. + + if err := osmoutils.ApplyFuncIfNoError(ctx, func(ctx sdk.Context) error { + aliasesLinkedToPreviousRollApp := h.GetAliasesOfRollAppId(ctx, previousRollAppId) + if len(aliasesLinkedToPreviousRollApp) == 0 { + return nil + } + + for _, alias := range aliasesLinkedToPreviousRollApp { + if err := h.MoveAliasToRollAppId(ctx, previousRollAppId, alias, newRollAppId); err != nil { + return errorsmod.Wrapf(errors.Join(gerrc.ErrInternal, err), "failed to migrate alias: %s", alias) + } + } + + // now priority the first alias from previous RollApp, because users are already familiar with it. + return h.SetDefaultAliasForRollApp(ctx, newRollAppId, aliasesLinkedToPreviousRollApp[0]) + }); err != nil { + logger.Error("aborted alias migration.", "error", err) + return + } + + if err := osmoutils.ApplyFuncIfNoError(ctx, func(ctx sdk.Context) error { + previousChainIdsToNewChainId := map[string]string{ + previousRollAppId: newRollAppId, + } + + if err := h.migrateChainIdsInDymNames(ctx, previousChainIdsToNewChainId); err != nil { + return errorsmod.Wrapf(errors.Join(gerrc.ErrInternal, err), "failed to migrate chain-ids in Dym-Names") + } + + return nil + }); err != nil { + logger.Error("aborted chain-id migration in Dym-Names configurations.", "error", err) + return + } + + logger.Info("finished DymNS hook on RollApp ID changed.") +} diff --git a/x/dymns/keeper/hooks_test.go b/x/dymns/keeper/hooks_test.go new file mode 100644 index 000000000..f2126dfd6 --- /dev/null +++ b/x/dymns/keeper/hooks_test.go @@ -0,0 +1,2537 @@ +package keeper_test + +import ( + "time" + + sdkmath "cosmossdk.io/math" + + rollapptypes "github.com/dymensionxyz/dymension/v3/x/rollapp/types" + + sdk "github.com/cosmos/cosmos-sdk/types" + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" +) + +func (s *KeeperTestSuite) Test_epochHooks_BeforeEpochStart() { + s.Run("should do nothing", func() { + originalGas := s.ctx.GasMeter().GasConsumed() + + err := s.dymNsKeeper.GetEpochHooks().BeforeEpochStart( + s.ctx, "hour", 1, + ) + s.Require().NoError(err) + + s.Require().Equal(originalGas, s.ctx.GasMeter().GasConsumed()) + }) +} + +//goland:noinspection GoSnakeCaseUsage +func (s *KeeperTestSuite) Test_epochHooks_AfterEpochEnd() { + s.Run("should do something even nothing to do", func() { + s.RefreshContext() + + moduleParams := s.moduleParams() + + originalGas := s.ctx.GasMeter().GasConsumed() + + err := s.dymNsKeeper.GetEpochHooks().AfterEpochEnd( + s.ctx, + moduleParams.Misc.EndEpochHookIdentifier, 1, + ) + s.Require().NoError(err) + + // gas should be changed because it should at least reading the params to check epoch identifier + s.Require().Less(originalGas, s.ctx.GasMeter().GasConsumed(), "should do something") + }) + + s.Run("process active mixed Dym-Name and alias Sell-Orders", func() { + s.RefreshContext() + + dymNameOwner := testAddr(1).bech32() + dymNameBuyer := testAddr(2).bech32() + + creator1_asOwner := testAddr(3).bech32() + creator2_asBuyer := testAddr(4).bech32() + + dymName1 := dymnstypes.DymName{ + Name: "my-name", + Owner: dymNameOwner, + Controller: dymNameOwner, + ExpireAt: s.now.Add(2 * 365 * 24 * time.Hour).Unix(), + } + err := s.dymNsKeeper.SetDymName(s.ctx, dymName1) + s.Require().NoError(err) + + rollApp1_asSrc := *newRollApp("rollapp_1-1").WithOwner(creator1_asOwner).WithAlias("one") + s.persistRollApp(rollApp1_asSrc) + s.requireRollApp(rollApp1_asSrc.rollAppId).HasAlias("one") + rollApp2_asDst := *newRollApp("rollapp_2-2").WithOwner(creator2_asBuyer) + s.persistRollApp(rollApp2_asDst) + s.requireRollApp(rollApp2_asDst.rollAppId).HasNoAlias() + + const dymNameOrderPrice = 100 + const aliasOrderPrice = 200 + + s.mintToModuleAccount(dymNameOrderPrice + aliasOrderPrice + 1) + + dymNameSO := s.newDymNameSellOrder(dymName1.Name). + WithMinPrice(dymNameOrderPrice). + WithDymNameBid(dymNameBuyer, dymNameOrderPrice). + Expired().Build() + err = s.dymNsKeeper.SetSellOrder(s.ctx, dymNameSO) + s.Require().NoError(err) + err = s.dymNsKeeper.SetActiveSellOrdersExpiration(s.ctx, &dymnstypes.ActiveSellOrdersExpiration{ + Records: []dymnstypes.ActiveSellOrdersExpirationRecord{ + { + AssetId: dymNameSO.AssetId, + ExpireAt: dymNameSO.ExpireAt, + }, + }, + }, dymnstypes.TypeName) + s.Require().NoError(err) + + aliasSO := s.newAliasSellOrder(rollApp1_asSrc.alias). + WithMinPrice(aliasOrderPrice). + WithAliasBid(rollApp2_asDst.owner, aliasOrderPrice, rollApp2_asDst.rollAppId). + Expired().Build() + err = s.dymNsKeeper.SetSellOrder(s.ctx, aliasSO) + s.Require().NoError(err) + err = s.dymNsKeeper.SetActiveSellOrdersExpiration(s.ctx, &dymnstypes.ActiveSellOrdersExpiration{ + Records: []dymnstypes.ActiveSellOrdersExpirationRecord{ + { + AssetId: aliasSO.AssetId, + ExpireAt: aliasSO.ExpireAt, + }, + }, + }, dymnstypes.TypeAlias) + s.Require().NoError(err) + + moduleParams := s.moduleParams() + + err = s.dymNsKeeper.GetEpochHooks().AfterEpochEnd(s.ctx, moduleParams.Misc.EndEpochHookIdentifier, 1) + s.Require().NoError(err) + + s.Nil(s.dymNsKeeper.GetSellOrder(s.ctx, dymName1.Name, dymnstypes.TypeName)) + s.Nil(s.dymNsKeeper.GetSellOrder(s.ctx, rollApp1_asSrc.alias, dymnstypes.TypeAlias)) + + s.Equal(int64(1), s.moduleBalance()) + s.Equal(int64(dymNameOrderPrice), s.balance(dymNameOwner)) + s.Equal(int64(aliasOrderPrice), s.balance(rollApp1_asSrc.owner)) + + laterDymName := s.dymNsKeeper.GetDymName(s.ctx, dymName1.Name) + if s.NotNil(laterDymName) { + s.Equal(dymNameBuyer, laterDymName.Owner) + s.Equal(dymNameBuyer, laterDymName.Controller) + } + + s.requireRollApp(rollApp1_asSrc.rollAppId).HasNoAlias() + s.requireRollApp(rollApp2_asDst.rollAppId).HasAlias("one") + }) + + s.Run("should not process Dym-Name SO if trading is disabled", func() { + s.RefreshContext() + + dymNameOwner := testAddr(1).bech32() + dymNameBuyer := testAddr(2).bech32() + + dymName1 := dymnstypes.DymName{ + Name: "my-name", + Owner: dymNameOwner, + Controller: dymNameOwner, + ExpireAt: s.now.Add(2 * 365 * 24 * time.Hour).Unix(), + } + err := s.dymNsKeeper.SetDymName(s.ctx, dymName1) + s.Require().NoError(err) + + const dymNameOrderPrice = 100 + + s.mintToModuleAccount(dymNameOrderPrice + 1) + + dymNameSO := s.newDymNameSellOrder(dymName1.Name). + WithMinPrice(dymNameOrderPrice). + WithDymNameBid(dymNameBuyer, dymNameOrderPrice). + Expired().Build() + err = s.dymNsKeeper.SetSellOrder(s.ctx, dymNameSO) + s.Require().NoError(err) + err = s.dymNsKeeper.SetActiveSellOrdersExpiration(s.ctx, &dymnstypes.ActiveSellOrdersExpiration{ + Records: []dymnstypes.ActiveSellOrdersExpirationRecord{ + { + AssetId: dymNameSO.AssetId, + ExpireAt: dymNameSO.ExpireAt, + }, + }, + }, dymnstypes.TypeName) + s.Require().NoError(err) + + s.updateModuleParams(func(p dymnstypes.Params) dymnstypes.Params { + p.Misc.EnableTradingName = false + return p + }) + + moduleParams := s.moduleParams() + + err = s.dymNsKeeper.GetEpochHooks().AfterEpochEnd(s.ctx, moduleParams.Misc.EndEpochHookIdentifier, 1) + s.Require().NoError(err) + + // the SellOrder should still be there + s.NotNil(s.dymNsKeeper.GetSellOrder(s.ctx, dymName1.Name, dymnstypes.TypeName)) + + // re-enable and test again to make sure it not processes just because trading was disabled + s.updateModuleParams(func(p dymnstypes.Params) dymnstypes.Params { + p.Misc.EnableTradingName = true + return p + }) + + err = s.dymNsKeeper.GetEpochHooks().AfterEpochEnd(s.ctx, moduleParams.Misc.EndEpochHookIdentifier, 1) + s.Require().NoError(err) + + s.Nil(s.dymNsKeeper.GetSellOrder(s.ctx, dymName1.Name, dymnstypes.TypeName)) + }) + + s.Run("should not process Alias SO if trading is disabled", func() { + s.RefreshContext() + + creator1_asOwner := testAddr(3).bech32() + creator2_asBuyer := testAddr(4).bech32() + + rollApp1_asSrc := *newRollApp("rollapp_1-1").WithOwner(creator1_asOwner).WithAlias("one") + s.persistRollApp(rollApp1_asSrc) + s.requireRollApp(rollApp1_asSrc.rollAppId).HasAlias("one") + rollApp2_asDst := *newRollApp("rollapp_2-2").WithOwner(creator2_asBuyer) + s.persistRollApp(rollApp2_asDst) + s.requireRollApp(rollApp2_asDst.rollAppId).HasNoAlias() + + const aliasOrderPrice = 200 + + s.mintToModuleAccount(aliasOrderPrice + 1) + + aliasSO := s.newAliasSellOrder(rollApp1_asSrc.alias). + WithMinPrice(aliasOrderPrice). + WithAliasBid(rollApp2_asDst.owner, aliasOrderPrice, rollApp2_asDst.rollAppId). + Expired().Build() + err := s.dymNsKeeper.SetSellOrder(s.ctx, aliasSO) + s.Require().NoError(err) + err = s.dymNsKeeper.SetActiveSellOrdersExpiration(s.ctx, &dymnstypes.ActiveSellOrdersExpiration{ + Records: []dymnstypes.ActiveSellOrdersExpirationRecord{ + { + AssetId: aliasSO.AssetId, + ExpireAt: aliasSO.ExpireAt, + }, + }, + }, dymnstypes.TypeAlias) + s.Require().NoError(err) + + s.updateModuleParams(func(p dymnstypes.Params) dymnstypes.Params { + p.Misc.EnableTradingAlias = false + return p + }) + + moduleParams := s.moduleParams() + + err = s.dymNsKeeper.GetEpochHooks().AfterEpochEnd(s.ctx, moduleParams.Misc.EndEpochHookIdentifier, 1) + s.Require().NoError(err) + + // the SellOrder should still be there + s.NotNil(s.dymNsKeeper.GetSellOrder(s.ctx, rollApp1_asSrc.alias, dymnstypes.TypeAlias)) + + // re-enable and test again to make sure it not processes just because trading was disabled + s.updateModuleParams(func(p dymnstypes.Params) dymnstypes.Params { + p.Misc.EnableTradingAlias = true + return p + }) + + err = s.dymNsKeeper.GetEpochHooks().AfterEpochEnd(s.ctx, moduleParams.Misc.EndEpochHookIdentifier, 1) + s.Require().NoError(err) + + s.Nil(s.dymNsKeeper.GetSellOrder(s.ctx, rollApp1_asSrc.alias, dymnstypes.TypeAlias)) + }) +} + +func (s *KeeperTestSuite) Test_epochHooks_AfterEpochEnd_processActiveDymNameSellOrders() { + ownerAcc := testAddr(1) + ownerA := ownerAcc.bech32() + + bidderAcc := testAddr(2) + bidderA := bidderAcc.bech32() + + dymNameA := dymnstypes.DymName{ + Name: "a", + Owner: ownerA, + Controller: ownerA, + ExpireAt: s.now.Unix() + 100, + } + + dymNameB := dymnstypes.DymName{ + Name: "b", + Owner: ownerA, + Controller: ownerA, + ExpireAt: s.now.Unix() + 100, + } + + dymNameC := dymnstypes.DymName{ + Name: "c", + Owner: ownerA, + Controller: ownerA, + ExpireAt: s.now.Unix() + 100, + } + + dymNameD := dymnstypes.DymName{ + Name: "d", + Owner: ownerA, + Controller: ownerA, + ExpireAt: s.now.Unix() + 100, + } + + originalDymNameA := dymNameA + originalDymNameB := dymNameB + originalDymNameC := dymNameC + originalDymNameD := dymNameD + + coin100 := s.coin(100) + coin200 := s.coin(200) + + soExpiredEpoch := s.now.Unix() - 1 + soNotExpiredEpoch := s.now.Add(time.Hour).Unix() + + const soExpired = true + const soNotExpired = false + genSo := func( + dymName dymnstypes.DymName, + expired bool, sellPrice *sdk.Coin, highestBid *dymnstypes.SellOrderBid, + ) dymnstypes.SellOrder { + return dymnstypes.SellOrder{ + AssetId: dymName.Name, + AssetType: dymnstypes.TypeName, + ExpireAt: func() int64 { + if expired { + return soExpiredEpoch + } + return soNotExpiredEpoch + }(), + MinPrice: coin100, + SellPrice: sellPrice, + HighestBid: highestBid, + } + } + + tests := []struct { + name string + dymNames []dymnstypes.DymName + sellOrders []dymnstypes.SellOrder + expiryByDymName []dymnstypes.ActiveSellOrdersExpirationRecord + preMintModuleBalance int64 + customEpochIdentifier string + beforeHookTestFunc func(*KeeperTestSuite) + wantErr bool + wantErrContains string + wantExpiryByDymName []dymnstypes.ActiveSellOrdersExpirationRecord + afterHookTestFunc func(*KeeperTestSuite) + }{ + { + name: "pass - simple process expired SO", + dymNames: []dymnstypes.DymName{dymNameA, dymNameB, dymNameC, dymNameD}, + sellOrders: []dymnstypes.SellOrder{genSo(dymNameA, soExpired, &coin200, nil)}, + expiryByDymName: []dymnstypes.ActiveSellOrdersExpirationRecord{ + { + AssetId: dymNameA.Name, + ExpireAt: soExpiredEpoch, + }, + }, + preMintModuleBalance: 200, + beforeHookTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerA).mappedDymNames(dymNameA.Name, dymNameB.Name, dymNameC.Name, dymNameD.Name) + s.requireConfiguredAddress(bidderA).notMappedToAnyDymName() + s.requireFallbackAddress(ownerAcc.fallback()).mappedDymNames(dymNameA.Name, dymNameB.Name, dymNameC.Name, dymNameD.Name) + s.requireFallbackAddress(bidderAcc.fallback()).notMappedToAnyDymName() + }, + wantErr: false, + wantExpiryByDymName: nil, + afterHookTestFunc: func(s *KeeperTestSuite) { + s.requireDymName(dymNameA.Name). + noActiveSO(). + mustEquals(originalDymNameA) + + s.Require().EqualValues(200, s.moduleBalance()) + + s.EqualValues(0, s.balance(dymNameA.Owner)) + + s.requireConfiguredAddress(ownerA).mappedDymNames(dymNameA.Name, dymNameB.Name, dymNameC.Name, dymNameD.Name) + s.requireConfiguredAddress(bidderA).notMappedToAnyDymName() + s.requireFallbackAddress(ownerAcc.fallback()).mappedDymNames(dymNameA.Name, dymNameB.Name, dymNameC.Name, dymNameD.Name) + s.requireFallbackAddress(bidderAcc.fallback()).notMappedToAnyDymName() + }, + }, + { + name: "pass - simple process expired & completed SO", + dymNames: []dymnstypes.DymName{dymNameA}, + sellOrders: []dymnstypes.SellOrder{genSo(dymNameA, soExpired, &coin200, &dymnstypes.SellOrderBid{ + Bidder: bidderA, + Price: coin200, + })}, + expiryByDymName: []dymnstypes.ActiveSellOrdersExpirationRecord{ + { + AssetId: dymNameA.Name, + ExpireAt: soExpiredEpoch, + }, + }, + preMintModuleBalance: 200, + beforeHookTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerA).mappedDymNames(dymNameA.Name) + s.requireConfiguredAddress(bidderA).notMappedToAnyDymName() + s.requireFallbackAddress(ownerAcc.fallback()).mappedDymNames(dymNameA.Name) + s.requireFallbackAddress(bidderAcc.fallback()).notMappedToAnyDymName() + }, + wantErr: false, + wantExpiryByDymName: nil, + afterHookTestFunc: func(s *KeeperTestSuite) { + s.requireDymName(dymNameA.Name). + noActiveSO(). + ownerChangedTo(bidderA). + expiryEquals(originalDymNameA.ExpireAt) + + s.Require().EqualValues(0, s.moduleBalance()) // 200 should be transferred to previous owner + + s.Require().EqualValues(200, s.balance(dymNameA.Owner)) // previous owner should earn from bid + + s.requireConfiguredAddress(ownerA).notMappedToAnyDymName() + s.requireConfiguredAddress(bidderA).mappedDymNames(dymNameA.Name) + s.requireFallbackAddress(ownerAcc.fallback()).notMappedToAnyDymName() + s.requireFallbackAddress(bidderAcc.fallback()).mappedDymNames(dymNameA.Name) + }, + }, + { + name: "pass - simple process expired & completed SO, match by min price", + dymNames: []dymnstypes.DymName{dymNameA}, + sellOrders: []dymnstypes.SellOrder{genSo(dymNameA, soExpired, &coin200, &dymnstypes.SellOrderBid{ + Bidder: bidderA, + Price: coin100, + })}, + expiryByDymName: []dymnstypes.ActiveSellOrdersExpirationRecord{ + { + AssetId: dymNameA.Name, + ExpireAt: soExpiredEpoch, + }, + }, + preMintModuleBalance: 250, + beforeHookTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerA).mappedDymNames(dymNameA.Name) + s.requireConfiguredAddress(bidderA).notMappedToAnyDymName() + s.requireFallbackAddress(ownerAcc.fallback()).mappedDymNames(dymNameA.Name) + s.requireFallbackAddress(bidderAcc.fallback()).notMappedToAnyDymName() + }, + wantErr: false, + wantExpiryByDymName: nil, + afterHookTestFunc: func(s *KeeperTestSuite) { + s.requireDymName(dymNameA.Name). + noActiveSO(). + ownerChangedTo(bidderA). + expiryEquals(originalDymNameA.ExpireAt) + + s.Require().EqualValues(150, s.moduleBalance()) // 100 should be transferred to previous owner + + s.Require().EqualValues(100, s.balance(dymNameA.Owner)) // previous owner should earn from bid + + s.requireConfiguredAddress(ownerA).notMappedToAnyDymName() + s.requireConfiguredAddress(bidderA).mappedDymNames(dymNameA.Name) + s.requireFallbackAddress(ownerAcc.fallback()).notMappedToAnyDymName() + s.requireFallbackAddress(bidderAcc.fallback()).mappedDymNames(dymNameA.Name) + }, + }, + { + name: "pass - process multiple - mixed SOs", + dymNames: []dymnstypes.DymName{dymNameA, dymNameB, dymNameC, dymNameD}, + sellOrders: []dymnstypes.SellOrder{ + genSo(dymNameA, soExpired, nil, nil), + genSo(dymNameB, soNotExpired, &coin200, &dymnstypes.SellOrderBid{ + // not completed + Bidder: bidderA, + Price: coin100, + }), + genSo(dymNameC, soExpired, &coin200, &dymnstypes.SellOrderBid{ + Bidder: bidderA, + Price: coin200, + }), + genSo(dymNameD, soExpired, &coin200, &dymnstypes.SellOrderBid{ + // completed by min price + Bidder: bidderA, + Price: coin100, + }), + }, + expiryByDymName: []dymnstypes.ActiveSellOrdersExpirationRecord{ + { + AssetId: dymNameA.Name, + ExpireAt: soExpiredEpoch, + }, + { + AssetId: dymNameB.Name, + ExpireAt: soNotExpiredEpoch, + }, + { + AssetId: dymNameC.Name, + ExpireAt: soExpiredEpoch, + }, + { + AssetId: dymNameD.Name, + ExpireAt: soExpiredEpoch, + }, + }, + preMintModuleBalance: 450, + beforeHookTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerA).mappedDymNames(dymNameA.Name, dymNameB.Name, dymNameC.Name, dymNameD.Name) + s.requireConfiguredAddress(bidderA).notMappedToAnyDymName() + s.requireFallbackAddress(ownerAcc.fallback()).mappedDymNames(dymNameA.Name, dymNameB.Name, dymNameC.Name, dymNameD.Name) + s.requireFallbackAddress(bidderAcc.fallback()).notMappedToAnyDymName() + }, + wantErr: false, + wantExpiryByDymName: []dymnstypes.ActiveSellOrdersExpirationRecord{ + { + AssetId: dymNameB.Name, + ExpireAt: soNotExpiredEpoch, + }, + }, + afterHookTestFunc: func(s *KeeperTestSuite) { + // SO for Dym-Name A is expired without any bid/winner + s.requireDymName(dymNameA.Name). + noActiveSO(). + mustEquals(originalDymNameA) + + // SO for Dym-Name B not yet finished + soB := s.dymNsKeeper.GetSellOrder(s.ctx, dymNameB.Name, dymnstypes.TypeName) + s.Require().NotNil(soB) + s.requireDymName(dymNameB.Name). + mustEquals(originalDymNameB) + + // SO for Dym-Name C is completed with winner + s.requireDymName(dymNameC.Name). + noActiveSO(). + ownerChangedTo(bidderA). + expiryEquals(originalDymNameC.ExpireAt) + + // SO for Dym-Name D is completed with winner + s.requireDymName(dymNameD.Name). + noActiveSO(). + ownerChangedTo(bidderA). + expiryEquals(originalDymNameD.ExpireAt) + + s.Require().EqualValues(150, s.moduleBalance()) + + s.Require().EqualValues(300, s.balance(ownerA)) // 200 from SO C, 100 from SO D + + s.requireConfiguredAddress(ownerA).mappedDymNames(dymNameA.Name, dymNameB.Name) + s.requireConfiguredAddress(bidderA).mappedDymNames(dymNameC.Name, dymNameD.Name) + s.requireFallbackAddress(ownerAcc.fallback()).mappedDymNames(dymNameA.Name, dymNameB.Name) + s.requireFallbackAddress(bidderAcc.fallback()).mappedDymNames(dymNameC.Name, dymNameD.Name) + }, + }, + { + name: "pass - should do nothing if invalid epoch identifier", + dymNames: []dymnstypes.DymName{dymNameA, dymNameB, dymNameC, dymNameD}, + sellOrders: []dymnstypes.SellOrder{ + genSo(dymNameA, soExpired, nil, nil), + genSo(dymNameB, soNotExpired, &coin200, &dymnstypes.SellOrderBid{ + // not completed + Bidder: bidderA, + Price: coin100, + }), + genSo(dymNameC, soExpired, &coin200, &dymnstypes.SellOrderBid{ + Bidder: bidderA, + Price: coin200, + }), + genSo(dymNameD, soExpired, &coin200, &dymnstypes.SellOrderBid{ + // completed by min price + Bidder: bidderA, + Price: coin100, + }), + }, + expiryByDymName: []dymnstypes.ActiveSellOrdersExpirationRecord{ + { + AssetId: dymNameA.Name, + ExpireAt: soExpiredEpoch, + }, + { + AssetId: dymNameB.Name, + ExpireAt: soNotExpiredEpoch, + }, + { + AssetId: dymNameC.Name, + ExpireAt: soExpiredEpoch, + }, + { + AssetId: dymNameD.Name, + ExpireAt: soExpiredEpoch, + }, + }, + preMintModuleBalance: 450, + customEpochIdentifier: "another", + beforeHookTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerA).mappedDymNames(dymNameA.Name, dymNameB.Name, dymNameC.Name, dymNameD.Name) + s.requireConfiguredAddress(bidderA).notMappedToAnyDymName() + s.requireFallbackAddress(ownerAcc.fallback()).mappedDymNames(dymNameA.Name, dymNameB.Name, dymNameC.Name, dymNameD.Name) + s.requireFallbackAddress(bidderAcc.fallback()).notMappedToAnyDymName() + }, + wantErr: false, + wantExpiryByDymName: []dymnstypes.ActiveSellOrdersExpirationRecord{ + { + AssetId: dymNameA.Name, + ExpireAt: soExpiredEpoch, + }, + { + AssetId: dymNameB.Name, + ExpireAt: soNotExpiredEpoch, + }, + { + AssetId: dymNameC.Name, + ExpireAt: soExpiredEpoch, + }, + { + AssetId: dymNameD.Name, + ExpireAt: soExpiredEpoch, + }, + }, + afterHookTestFunc: func(s *KeeperTestSuite) { + s.requireDymName(dymNameA.Name).mustEquals(originalDymNameA) + s.requireDymName(dymNameB.Name).mustEquals(originalDymNameB) + s.requireDymName(dymNameC.Name).mustEquals(originalDymNameC) + s.requireDymName(dymNameD.Name).mustEquals(originalDymNameD) + + s.Require().EqualValues(450, s.moduleBalance()) + + s.Require().EqualValues(0, s.balance(ownerA)) + + s.requireConfiguredAddress(ownerA).mappedDymNames(dymNameA.Name, dymNameB.Name, dymNameC.Name, dymNameD.Name) + s.requireConfiguredAddress(bidderA).notMappedToAnyDymName() + s.requireFallbackAddress(ownerAcc.fallback()).mappedDymNames(dymNameA.Name, dymNameB.Name, dymNameC.Name, dymNameD.Name) + s.requireFallbackAddress(bidderAcc.fallback()).notMappedToAnyDymName() + }, + }, + { + name: "pass - keep expiry reference to non-exists SO, later invariants will catch it", + dymNames: []dymnstypes.DymName{dymNameA, dymNameB}, + sellOrders: []dymnstypes.SellOrder{ + genSo(dymNameA, soExpired, nil, nil), + // no SO for Dym-Name B + }, + expiryByDymName: []dymnstypes.ActiveSellOrdersExpirationRecord{ + { + AssetId: dymNameA.Name, + ExpireAt: soExpiredEpoch, + }, + { + // no SO for Dym-Name B but still have reference + AssetId: dymNameB.Name, + ExpireAt: soExpiredEpoch, + }, + }, + beforeHookTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerA).mappedDymNames(dymNameA.Name, dymNameB.Name) + s.requireConfiguredAddress(bidderA).notMappedToAnyDymName() + s.requireFallbackAddress(ownerAcc.fallback()).mappedDymNames(dymNameA.Name, dymNameB.Name) + s.requireFallbackAddress(bidderAcc.fallback()).notMappedToAnyDymName() + }, + wantErr: false, + wantExpiryByDymName: []dymnstypes.ActiveSellOrdersExpirationRecord{ + // removed reference to Dym-Name A because of processed + { + // the reference to non-exists existing SO should be kept + AssetId: dymNameB.Name, + ExpireAt: soExpiredEpoch, + }, + }, + afterHookTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerA).mappedDymNames(dymNameA.Name, dymNameB.Name) + s.requireConfiguredAddress(bidderA).notMappedToAnyDymName() + s.requireFallbackAddress(ownerAcc.fallback()).mappedDymNames(dymNameA.Name, dymNameB.Name) + s.requireFallbackAddress(bidderAcc.fallback()).notMappedToAnyDymName() + }, + }, + { + name: "pass - ignore expiry if in-correct, later invariants will catch it", + dymNames: []dymnstypes.DymName{dymNameA, dymNameB}, + sellOrders: []dymnstypes.SellOrder{ + genSo(dymNameA, soExpired, nil, nil), + genSo(dymNameB, soNotExpired, nil, nil), // SO not expired + }, + expiryByDymName: []dymnstypes.ActiveSellOrdersExpirationRecord{ + { + AssetId: dymNameA.Name, + ExpireAt: soExpiredEpoch, + }, + { + // incorrect, SO not expired + AssetId: dymNameB.Name, + ExpireAt: soExpiredEpoch, + }, + }, + beforeHookTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerA).mappedDymNames(dymNameA.Name, dymNameB.Name) + s.requireConfiguredAddress(bidderA).notMappedToAnyDymName() + s.requireFallbackAddress(ownerAcc.fallback()).mappedDymNames(dymNameA.Name, dymNameB.Name) + s.requireFallbackAddress(bidderAcc.fallback()).notMappedToAnyDymName() + }, + wantErr: false, + wantExpiryByDymName: []dymnstypes.ActiveSellOrdersExpirationRecord{ + { + AssetId: dymNameB.Name, + ExpireAt: soExpiredEpoch, // incorrect still kept, later invariant will catch it + }, + }, + afterHookTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerA).mappedDymNames(dymNameA.Name, dymNameB.Name) + s.requireConfiguredAddress(bidderA).notMappedToAnyDymName() + s.requireFallbackAddress(ownerAcc.fallback()).mappedDymNames(dymNameA.Name, dymNameB.Name) + s.requireFallbackAddress(bidderAcc.fallback()).notMappedToAnyDymName() + }, + }, + { + name: "pass - ignore processing SO when error occurs", + dymNames: []dymnstypes.DymName{dymNameA}, + sellOrders: []dymnstypes.SellOrder{ + genSo(dymNameA, soExpired, nil, &dymnstypes.SellOrderBid{ + Bidder: bidderA, + Price: coin100, + }), + }, + expiryByDymName: []dymnstypes.ActiveSellOrdersExpirationRecord{ + { + AssetId: dymNameA.Name, + ExpireAt: soExpiredEpoch, + }, + }, + preMintModuleBalance: 1, // not enough balance + beforeHookTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerA).mappedDymNames(dymNameA.Name) + s.requireConfiguredAddress(bidderA).notMappedToAnyDymName() + s.requireFallbackAddress(ownerAcc.fallback()).mappedDymNames(dymNameA.Name) + s.requireFallbackAddress(bidderAcc.fallback()).notMappedToAnyDymName() + }, + wantErr: false, + wantExpiryByDymName: []dymnstypes.ActiveSellOrdersExpirationRecord{ + { + AssetId: dymNameA.Name, + ExpireAt: soExpiredEpoch, + }, + }, + afterHookTestFunc: func(s *KeeperTestSuite) { + // unchanged + + s.requireConfiguredAddress(ownerA).mappedDymNames(dymNameA.Name) + s.requireConfiguredAddress(bidderA).notMappedToAnyDymName() + s.requireFallbackAddress(ownerAcc.fallback()).mappedDymNames(dymNameA.Name) + s.requireFallbackAddress(bidderAcc.fallback()).notMappedToAnyDymName() + + s.requireDymName(dymNameA.Name).mustHaveActiveSO() + + s.EqualValues(1, s.moduleBalance()) + }, + }, + { + name: "pass - ignore processing SO when error occurs, one pass one fail", + dymNames: []dymnstypes.DymName{dymNameA, dymNameB}, + sellOrders: []dymnstypes.SellOrder{ + genSo(dymNameA, soExpired, nil, &dymnstypes.SellOrderBid{ + Bidder: bidderA, + Price: coin100, + }), + genSo(dymNameB, soExpired, nil, &dymnstypes.SellOrderBid{ + Bidder: bidderA, + Price: coin100, + }), + }, + expiryByDymName: []dymnstypes.ActiveSellOrdersExpirationRecord{ + { + AssetId: dymNameA.Name, + ExpireAt: soExpiredEpoch, + }, + { + AssetId: dymNameB.Name, + ExpireAt: soExpiredEpoch, + }, + }, + preMintModuleBalance: 101, // just enough process the first SO + beforeHookTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerA).mappedDymNames( + dymNameA.Name, dymNameB.Name, + ) + s.requireConfiguredAddress(bidderA).notMappedToAnyDymName() + }, + wantErr: false, + wantExpiryByDymName: []dymnstypes.ActiveSellOrdersExpirationRecord{ + { + AssetId: dymNameB.Name, + ExpireAt: soExpiredEpoch, + }, + }, + afterHookTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerA).mappedDymNames( + dymNameB.Name, + ) + s.requireConfiguredAddress(bidderA).mappedDymNames( + dymNameA.Name, + ) + + s.requireDymName(dymNameA.Name).noActiveSO() + s.requireDymName(dymNameB.Name).mustHaveActiveSO() + + s.EqualValues(1, s.moduleBalance()) + s.EqualValues(100, s.balance(ownerA)) + }, + }, + { + name: "pass - ignore processing SO when error occurs, one fail one pass", + dymNames: []dymnstypes.DymName{dymNameA, dymNameB}, + sellOrders: []dymnstypes.SellOrder{ + genSo(dymNameA, soExpired, nil, &dymnstypes.SellOrderBid{ + Bidder: bidderA, + Price: coin200, + }), + genSo(dymNameB, soExpired, nil, &dymnstypes.SellOrderBid{ + Bidder: bidderA, + Price: coin100, + }), + }, + expiryByDymName: []dymnstypes.ActiveSellOrdersExpirationRecord{ + { + AssetId: dymNameA.Name, + ExpireAt: soExpiredEpoch, + }, + { + AssetId: dymNameB.Name, + ExpireAt: soExpiredEpoch, + }, + }, + preMintModuleBalance: 101, // just enough process the second SO + beforeHookTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerA).mappedDymNames( + dymNameA.Name, dymNameB.Name, + ) + s.requireConfiguredAddress(bidderA).notMappedToAnyDymName() + }, + wantErr: false, + wantExpiryByDymName: []dymnstypes.ActiveSellOrdersExpirationRecord{ + { + AssetId: dymNameA.Name, + ExpireAt: soExpiredEpoch, + }, + }, + afterHookTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerA).mappedDymNames( + dymNameA.Name, + ) + s.requireConfiguredAddress(bidderA).mappedDymNames( + dymNameB.Name, + ) + + s.requireDymName(dymNameA.Name).mustHaveActiveSO() + s.requireDymName(dymNameB.Name).noActiveSO() + + s.EqualValues(1, s.moduleBalance()) + s.EqualValues(100, s.balance(ownerA)) + }, + }, + } + for _, tt := range tests { + s.Run(tt.name, func() { + s.Require().NotNil(tt.beforeHookTestFunc, "mis-configured test case") + s.Require().NotNil(tt.afterHookTestFunc, "mis-configured test case") + + s.RefreshContext() + + if tt.preMintModuleBalance > 0 { + s.mintToModuleAccount(tt.preMintModuleBalance) + } + + err := s.dymNsKeeper.SetActiveSellOrdersExpiration(s.ctx, &dymnstypes.ActiveSellOrdersExpiration{ + Records: tt.expiryByDymName, + }, dymnstypes.TypeName) + s.Require().NoError(err) + + for _, dymName := range tt.dymNames { + s.setDymNameWithFunctionsAfter(dymName) + } + + for _, so := range tt.sellOrders { + err = s.dymNsKeeper.SetSellOrder(s.ctx, so) + s.Require().NoError(err) + } + + moduleParams := s.dymNsKeeper.GetParams(s.ctx) + + useEpochIdentifier := moduleParams.Misc.EndEpochHookIdentifier + if tt.customEpochIdentifier != "" { + useEpochIdentifier = tt.customEpochIdentifier + } + + tt.beforeHookTestFunc(s) + + err = s.dymNsKeeper.GetEpochHooks().AfterEpochEnd(s.ctx, useEpochIdentifier, 1) + + defer func() { + if s.T().Failed() { + return + } + + tt.afterHookTestFunc(s) + + aSoe := s.dymNsKeeper.GetActiveSellOrdersExpiration(s.ctx, dymnstypes.TypeName) + if len(tt.wantExpiryByDymName) == 0 { + s.Require().Empty(aSoe.Records) + } else { + s.Require().Equal(tt.wantExpiryByDymName, aSoe.Records) + } + }() + + if tt.wantErr { + s.Require().NotEmpty(tt.wantErrContains, "mis-configured test case") + s.Require().Error(err) + s.Require().Contains(err.Error(), tt.wantErrContains) + + return + } + + s.Require().NoError(err) + }) + } +} + +//goland:noinspection GoSnakeCaseUsage +func (s *KeeperTestSuite) Test_epochHooks_AfterEpochEnd_processActiveAliasSellOrders() { + creator_1_asOwner := testAddr(1).bech32() + creator_2_asBidder := testAddr(2).bech32() + + rollApp_1_byOwner_asSrc := *newRollApp("rollapp_1-1").WithAlias("one").WithOwner(creator_1_asOwner) + rollApp_2_byBuyer_asDst := *newRollApp("rollapp_2-2").WithOwner(creator_2_asBidder) + rollApp_3_byOwner_asSrc := *newRollApp("rollapp_3-1").WithAlias("three").WithOwner(creator_1_asOwner) + rollApp_4_byOwner_asSrc := *newRollApp("rollapp_4-1").WithAlias("four").WithOwner(creator_1_asOwner) + rollApp_5_byOwner_asSrc := *newRollApp("rollapp_5-1").WithAlias("five").WithOwner(creator_1_asOwner) + + const aliasProhibitedTrading = "prohibited" + + const minPrice = 100 + const soExpiredEpoch = 1 + soNotExpiredEpoch := s.now.Add(time.Hour).Unix() + + tests := []struct { + name string + rollApps []rollapp + sellOrders []dymnstypes.SellOrder + expiryByAlias []dymnstypes.ActiveSellOrdersExpirationRecord + preMintModuleBalance int64 + customEpochIdentifier string + beforeHookTestFunc func(s *KeeperTestSuite) + wantErr bool + wantErrContains string + wantExpiryByAlias []dymnstypes.ActiveSellOrdersExpirationRecord + afterHookTestFunc func(s *KeeperTestSuite) + }{ + { + name: "pass - simple process expired SO without bid", + rollApps: []rollapp{rollApp_1_byOwner_asSrc}, + sellOrders: []dymnstypes.SellOrder{ + s.newAliasSellOrder(rollApp_1_byOwner_asSrc.alias). + WithMinPrice(minPrice).WithSellPrice(200). + WithExpiry(soExpiredEpoch). + Build(), + }, + expiryByAlias: []dymnstypes.ActiveSellOrdersExpirationRecord{ + { + AssetId: rollApp_1_byOwner_asSrc.alias, + ExpireAt: soExpiredEpoch, + }, + }, + preMintModuleBalance: 200, + beforeHookTestFunc: func(s *KeeperTestSuite) { + s.requireRollApp(rollApp_1_byOwner_asSrc.rollAppId).HasAlias(rollApp_1_byOwner_asSrc.alias) + }, + wantErr: false, + wantExpiryByAlias: nil, + afterHookTestFunc: func(s *KeeperTestSuite) { + s.requireAlias(rollApp_1_byOwner_asSrc.alias).noActiveSO() + + // unchanged + + s.Equal(int64(200), s.moduleBalance()) + s.Zero(s.balance(rollApp_1_byOwner_asSrc.owner)) + + s.requireRollApp(rollApp_1_byOwner_asSrc.rollAppId).HasAlias(rollApp_1_byOwner_asSrc.alias) + s.requireRollApp(rollApp_2_byBuyer_asDst.rollAppId).HasNoAlias() + }, + }, + { + name: "pass - simple process expired & completed SO", + rollApps: []rollapp{rollApp_1_byOwner_asSrc, rollApp_2_byBuyer_asDst}, + sellOrders: []dymnstypes.SellOrder{ + s.newAliasSellOrder(rollApp_1_byOwner_asSrc.alias). + WithMinPrice(minPrice). + WithSellPrice(200). + WithExpiry(soExpiredEpoch). + WithAliasBid(rollApp_2_byBuyer_asDst.owner, 200, rollApp_2_byBuyer_asDst.rollAppId). + Build(), + }, + expiryByAlias: []dymnstypes.ActiveSellOrdersExpirationRecord{ + { + AssetId: rollApp_1_byOwner_asSrc.alias, + ExpireAt: soExpiredEpoch, + }, + }, + preMintModuleBalance: 200, + beforeHookTestFunc: func(s *KeeperTestSuite) { + s.requireRollApp(rollApp_1_byOwner_asSrc.rollAppId).HasAlias(rollApp_1_byOwner_asSrc.alias) + s.requireRollApp(rollApp_2_byBuyer_asDst.rollAppId).HasNoAlias() + }, + wantErr: false, + wantExpiryByAlias: nil, + afterHookTestFunc: func(s *KeeperTestSuite) { + s.requireRollApp(rollApp_1_byOwner_asSrc.rollAppId).HasNoAlias() + s.requireRollApp(rollApp_2_byBuyer_asDst.rollAppId).HasAlias(rollApp_1_byOwner_asSrc.alias) + + s.requireAlias(rollApp_1_byOwner_asSrc.alias).noActiveSO() + + s.Zero(s.moduleBalance()) // should be transferred to previous owner + s.Equal(int64(200), s.balance(rollApp_1_byOwner_asSrc.owner)) // previous owner should earn from bid + }, + }, + { + name: "pass - simple process expired & completed SO, match by min price", + rollApps: []rollapp{rollApp_1_byOwner_asSrc, rollApp_2_byBuyer_asDst}, + sellOrders: []dymnstypes.SellOrder{ + s.newAliasSellOrder(rollApp_1_byOwner_asSrc.alias). + WithMinPrice(minPrice). + WithSellPrice(200). + WithExpiry(soExpiredEpoch). + WithAliasBid(rollApp_2_byBuyer_asDst.owner, minPrice, rollApp_2_byBuyer_asDst.rollAppId). + Build(), + }, + expiryByAlias: []dymnstypes.ActiveSellOrdersExpirationRecord{ + { + AssetId: rollApp_1_byOwner_asSrc.alias, + ExpireAt: soExpiredEpoch, + }, + }, + preMintModuleBalance: 250, + beforeHookTestFunc: func(s *KeeperTestSuite) { + s.requireRollApp(rollApp_1_byOwner_asSrc.rollAppId).HasAlias(rollApp_1_byOwner_asSrc.alias) + s.requireRollApp(rollApp_2_byBuyer_asDst.rollAppId).HasNoAlias() + }, + wantErr: false, + wantExpiryByAlias: nil, + afterHookTestFunc: func(s *KeeperTestSuite) { + s.requireRollApp(rollApp_1_byOwner_asSrc.rollAppId).HasNoAlias() + s.requireRollApp(rollApp_2_byBuyer_asDst.rollAppId).HasAlias(rollApp_1_byOwner_asSrc.alias) + + s.requireAlias(rollApp_1_byOwner_asSrc.alias).noActiveSO() + + s.Equal(int64(250-minPrice), s.moduleBalance()) // should be transferred to previous owner + s.Equal(int64(minPrice), s.balance(rollApp_1_byOwner_asSrc.owner)) // previous owner should earn from bid + }, + }, + { + name: "pass - refunds records that alias presents in params", + rollApps: []rollapp{ + rollApp_1_byOwner_asSrc, rollApp_2_byBuyer_asDst, + }, + sellOrders: []dymnstypes.SellOrder{ + s.newAliasSellOrder(aliasProhibitedTrading). + WithMinPrice(minPrice). + WithSellPrice(200). + WithExpiry(soExpiredEpoch). + WithAliasBid(rollApp_2_byBuyer_asDst.owner, minPrice, rollApp_2_byBuyer_asDst.rollAppId). + Build(), + }, + expiryByAlias: []dymnstypes.ActiveSellOrdersExpirationRecord{ + { + AssetId: aliasProhibitedTrading, + ExpireAt: soExpiredEpoch, + }, + }, + preMintModuleBalance: 500, + beforeHookTestFunc: func(s *KeeperTestSuite) { + err := s.dymNsKeeper.SetAliasForRollAppId(s.ctx, rollApp_1_byOwner_asSrc.rollAppId, aliasProhibitedTrading) + s.NoError(err) + + s.requireRollApp(rollApp_1_byOwner_asSrc.rollAppId).HasAlias( + rollApp_1_byOwner_asSrc.alias, aliasProhibitedTrading, + ) + s.requireRollApp(rollApp_2_byBuyer_asDst.rollAppId).HasNoAlias() + + s.updateModuleParams(func(p dymnstypes.Params) dymnstypes.Params { + p.Chains.AliasesOfChainIds = append(p.Chains.AliasesOfChainIds, dymnstypes.AliasesOfChainId{ + ChainId: "some-chain", + Aliases: []string{aliasProhibitedTrading}, + }) + return p + }) + }, + wantErr: false, + wantExpiryByAlias: []dymnstypes.ActiveSellOrdersExpirationRecord{}, + afterHookTestFunc: func(s *KeeperTestSuite) { + s.Nil(s.dymNsKeeper.GetSellOrder(s.ctx, aliasProhibitedTrading, dymnstypes.TypeAlias)) + + // refunded + s.Equal(int64(500-minPrice), s.moduleBalance()) + s.Equal(int64(minPrice), s.balance(rollApp_2_byBuyer_asDst.owner)) + }, + }, + { + name: "pass - failed to refunds records that alias presents in params will keep the data as is", + rollApps: []rollapp{ + rollApp_1_byOwner_asSrc, rollApp_2_byBuyer_asDst, + }, + sellOrders: []dymnstypes.SellOrder{ + s.newAliasSellOrder(aliasProhibitedTrading). + WithMinPrice(minPrice). + WithSellPrice(200). + WithExpiry(soExpiredEpoch). + WithAliasBid(rollApp_2_byBuyer_asDst.owner, minPrice, rollApp_2_byBuyer_asDst.rollAppId). + Build(), + }, + expiryByAlias: []dymnstypes.ActiveSellOrdersExpirationRecord{ + { + AssetId: aliasProhibitedTrading, + ExpireAt: soExpiredEpoch, + }, + }, + preMintModuleBalance: 1, + beforeHookTestFunc: func(s *KeeperTestSuite) { + err := s.dymNsKeeper.SetAliasForRollAppId(s.ctx, rollApp_1_byOwner_asSrc.rollAppId, aliasProhibitedTrading) + s.NoError(err) + + s.requireRollApp(rollApp_1_byOwner_asSrc.rollAppId).HasAlias( + rollApp_1_byOwner_asSrc.alias, aliasProhibitedTrading, + ) + s.requireRollApp(rollApp_2_byBuyer_asDst.rollAppId).HasNoAlias() + + s.updateModuleParams(func(p dymnstypes.Params) dymnstypes.Params { + p.Chains.AliasesOfChainIds = append(p.Chains.AliasesOfChainIds, dymnstypes.AliasesOfChainId{ + ChainId: "some-chain", + Aliases: []string{aliasProhibitedTrading}, + }) + return p + }) + }, + wantErr: false, + wantExpiryByAlias: []dymnstypes.ActiveSellOrdersExpirationRecord{ + { + AssetId: aliasProhibitedTrading, + ExpireAt: soExpiredEpoch, + }, + }, + afterHookTestFunc: func(s *KeeperTestSuite) { + s.NotNil(s.dymNsKeeper.GetSellOrder(s.ctx, aliasProhibitedTrading, dymnstypes.TypeAlias)) + + s.Equal(int64(1), s.moduleBalance()) + s.Zero(s.balance(rollApp_2_byBuyer_asDst.owner)) + }, + }, + { + name: "pass - process multiple - mixed SOs", + rollApps: []rollapp{ + rollApp_1_byOwner_asSrc, rollApp_2_byBuyer_asDst, rollApp_3_byOwner_asSrc, rollApp_4_byOwner_asSrc, rollApp_5_byOwner_asSrc, + }, + sellOrders: []dymnstypes.SellOrder{ + // expired SO without bid + s.newAliasSellOrder(rollApp_1_byOwner_asSrc.alias). + WithMinPrice(minPrice).WithSellPrice(200). + WithExpiry(soExpiredEpoch). + Build(), + // not yet finished + s.newAliasSellOrder(rollApp_3_byOwner_asSrc.alias). + WithMinPrice(minPrice). + WithSellPrice(200). + WithExpiry(soNotExpiredEpoch). + WithAliasBid(rollApp_2_byBuyer_asDst.owner, minPrice, rollApp_2_byBuyer_asDst.rollAppId). + Build(), + // completed by matching sell-price + s.newAliasSellOrder(rollApp_4_byOwner_asSrc.alias). + WithMinPrice(minPrice). + WithSellPrice(200). + WithExpiry(soExpiredEpoch). + WithAliasBid(rollApp_2_byBuyer_asDst.owner, 200, rollApp_2_byBuyer_asDst.rollAppId). + Build(), + // completed by min price + s.newAliasSellOrder(rollApp_5_byOwner_asSrc.alias). + WithMinPrice(minPrice). + WithSellPrice(200). + WithExpiry(soExpiredEpoch). + WithAliasBid(rollApp_2_byBuyer_asDst.owner, minPrice, rollApp_2_byBuyer_asDst.rollAppId). + Build(), + // completed by min price, but prohibited trading because presents in module params + s.newAliasSellOrder(aliasProhibitedTrading). + WithMinPrice(minPrice). + WithSellPrice(200). + WithExpiry(soExpiredEpoch). + WithAliasBid(rollApp_2_byBuyer_asDst.owner, minPrice, rollApp_2_byBuyer_asDst.rollAppId). + Build(), + }, + expiryByAlias: []dymnstypes.ActiveSellOrdersExpirationRecord{ + { + AssetId: rollApp_1_byOwner_asSrc.alias, + ExpireAt: soExpiredEpoch, + }, + { + AssetId: rollApp_3_byOwner_asSrc.alias, + ExpireAt: soNotExpiredEpoch, + }, + { + AssetId: rollApp_4_byOwner_asSrc.alias, + ExpireAt: soExpiredEpoch, + }, + { + AssetId: rollApp_5_byOwner_asSrc.alias, + ExpireAt: soExpiredEpoch, + }, + { + AssetId: aliasProhibitedTrading, + ExpireAt: soExpiredEpoch, + }, + }, + preMintModuleBalance: 450, + beforeHookTestFunc: func(s *KeeperTestSuite) { + err := s.dymNsKeeper.SetAliasForRollAppId(s.ctx, rollApp_1_byOwner_asSrc.rollAppId, aliasProhibitedTrading) + s.NoError(err) + + s.requireRollApp(rollApp_1_byOwner_asSrc.rollAppId).HasAlias( + rollApp_1_byOwner_asSrc.alias, aliasProhibitedTrading, + ) + s.requireRollApp(rollApp_2_byBuyer_asDst.rollAppId).HasNoAlias() + s.requireRollApp(rollApp_3_byOwner_asSrc.rollAppId).HasAlias(rollApp_3_byOwner_asSrc.alias) + s.requireRollApp(rollApp_4_byOwner_asSrc.rollAppId).HasAlias(rollApp_4_byOwner_asSrc.alias) + s.requireRollApp(rollApp_5_byOwner_asSrc.rollAppId).HasAlias(rollApp_5_byOwner_asSrc.alias) + + s.updateModuleParams(func(p dymnstypes.Params) dymnstypes.Params { + p.Chains.AliasesOfChainIds = append(p.Chains.AliasesOfChainIds, dymnstypes.AliasesOfChainId{ + ChainId: "some-chain", + Aliases: []string{aliasProhibitedTrading}, + }) + return p + }) + }, + wantErr: false, + wantExpiryByAlias: []dymnstypes.ActiveSellOrdersExpirationRecord{ + { + AssetId: rollApp_3_byOwner_asSrc.alias, + ExpireAt: soNotExpiredEpoch, + }, + }, + afterHookTestFunc: func(s *KeeperTestSuite) { + // SO for alias 1 is expired without any bid/winner + s.requireRollApp(rollApp_1_byOwner_asSrc.rollAppId).HasAlias(rollApp_1_byOwner_asSrc.alias) + s.requireAlias(rollApp_1_byOwner_asSrc.alias).noActiveSO() + + // SO of the prohibited alias should be removed + s.Nil(s.dymNsKeeper.GetSellOrder(s.ctx, aliasProhibitedTrading, dymnstypes.TypeAlias)) + + // SO for alias 3 not yet finished + s.requireRollApp(rollApp_3_byOwner_asSrc.rollAppId).HasAlias(rollApp_3_byOwner_asSrc.alias) + s.NotNil(s.dymNsKeeper.GetSellOrder(s.ctx, rollApp_3_byOwner_asSrc.alias, dymnstypes.TypeAlias)) + + // SO for alias 4 is completed with winner + s.requireRollApp(rollApp_4_byOwner_asSrc.rollAppId).HasNoAlias() + s.requireAlias(rollApp_4_byOwner_asSrc.alias).noActiveSO() + + // SO for alias 5 is completed with winner + s.requireRollApp(rollApp_5_byOwner_asSrc.rollAppId).HasNoAlias() + s.requireAlias(rollApp_5_byOwner_asSrc.alias).noActiveSO() + + // aliases moved to RollApps of the winner + s.requireRollApp(rollApp_2_byBuyer_asDst.rollAppId). + HasAlias(rollApp_4_byOwner_asSrc.alias, rollApp_5_byOwner_asSrc.alias) + + s.Equal(int64(50), s.moduleBalance()) + s.Equal(int64(300), s.balance(creator_1_asOwner)) // price from 2 completed SO + s.Equal(int64(100), s.balance(creator_2_asBidder)) // refunded from prohibited trading SO + }, + }, + { + name: "pass - should do nothing if invalid epoch identifier", + rollApps: []rollapp{ + rollApp_1_byOwner_asSrc, rollApp_2_byBuyer_asDst, rollApp_3_byOwner_asSrc, rollApp_4_byOwner_asSrc, rollApp_5_byOwner_asSrc, + }, + sellOrders: []dymnstypes.SellOrder{ + // expired SO without bid + s.newAliasSellOrder(rollApp_1_byOwner_asSrc.alias). + WithMinPrice(minPrice).WithSellPrice(200). + WithExpiry(soExpiredEpoch). + Build(), + // not yet finished + s.newAliasSellOrder(rollApp_3_byOwner_asSrc.alias). + WithMinPrice(minPrice). + WithSellPrice(200). + WithExpiry(soNotExpiredEpoch). + WithAliasBid(rollApp_2_byBuyer_asDst.owner, minPrice, rollApp_2_byBuyer_asDst.rollAppId). + Build(), + // completed by matching sell-price + s.newAliasSellOrder(rollApp_4_byOwner_asSrc.alias). + WithMinPrice(minPrice). + WithSellPrice(200). + WithExpiry(soExpiredEpoch). + WithAliasBid(rollApp_2_byBuyer_asDst.owner, 200, rollApp_2_byBuyer_asDst.rollAppId). + Build(), + // completed by min price + s.newAliasSellOrder(rollApp_5_byOwner_asSrc.alias). + WithMinPrice(minPrice). + WithSellPrice(200). + WithExpiry(soExpiredEpoch). + WithAliasBid(rollApp_2_byBuyer_asDst.owner, minPrice, rollApp_2_byBuyer_asDst.rollAppId). + Build(), + }, + expiryByAlias: []dymnstypes.ActiveSellOrdersExpirationRecord{ + { + AssetId: rollApp_1_byOwner_asSrc.alias, + ExpireAt: soExpiredEpoch, + }, + { + AssetId: rollApp_3_byOwner_asSrc.alias, + ExpireAt: soNotExpiredEpoch, + }, + { + AssetId: rollApp_4_byOwner_asSrc.alias, + ExpireAt: soExpiredEpoch, + }, + { + AssetId: rollApp_5_byOwner_asSrc.alias, + ExpireAt: soExpiredEpoch, + }, + }, + customEpochIdentifier: "another", + beforeHookTestFunc: func(s *KeeperTestSuite) { + s.requireRollApp(rollApp_1_byOwner_asSrc.rollAppId).HasAlias(rollApp_1_byOwner_asSrc.alias) + s.requireRollApp(rollApp_2_byBuyer_asDst.rollAppId).HasNoAlias() + s.requireRollApp(rollApp_3_byOwner_asSrc.rollAppId).HasAlias(rollApp_3_byOwner_asSrc.alias) + s.requireRollApp(rollApp_4_byOwner_asSrc.rollAppId).HasAlias(rollApp_4_byOwner_asSrc.alias) + s.requireRollApp(rollApp_5_byOwner_asSrc.rollAppId).HasAlias(rollApp_5_byOwner_asSrc.alias) + }, + wantErr: false, + wantExpiryByAlias: []dymnstypes.ActiveSellOrdersExpirationRecord{ + // deep unchanged but order changed due to sorting + { + AssetId: rollApp_5_byOwner_asSrc.alias, + ExpireAt: soExpiredEpoch, + }, + { + AssetId: rollApp_4_byOwner_asSrc.alias, + ExpireAt: soExpiredEpoch, + }, + { + AssetId: rollApp_1_byOwner_asSrc.alias, + ExpireAt: soExpiredEpoch, + }, + { + AssetId: rollApp_3_byOwner_asSrc.alias, + ExpireAt: soNotExpiredEpoch, + }, + }, + afterHookTestFunc: func(s *KeeperTestSuite) { + // unchanged + + s.requireRollApp(rollApp_1_byOwner_asSrc.rollAppId).HasAlias(rollApp_1_byOwner_asSrc.alias) + s.requireRollApp(rollApp_2_byBuyer_asDst.rollAppId).HasNoAlias() + s.requireRollApp(rollApp_3_byOwner_asSrc.rollAppId).HasAlias(rollApp_3_byOwner_asSrc.alias) + s.requireRollApp(rollApp_4_byOwner_asSrc.rollAppId).HasAlias(rollApp_4_byOwner_asSrc.alias) + s.requireRollApp(rollApp_5_byOwner_asSrc.rollAppId).HasAlias(rollApp_5_byOwner_asSrc.alias) + }, + }, + { + name: "pass - should ignore expiry reference to non-exists SO, invariants will catch it later", + rollApps: []rollapp{rollApp_1_byOwner_asSrc, rollApp_3_byOwner_asSrc}, + sellOrders: []dymnstypes.SellOrder{ + s.newAliasSellOrder(rollApp_1_byOwner_asSrc.alias). + WithMinPrice(minPrice). + WithSellPrice(300). + WithExpiry(soExpiredEpoch). + Build(), + // no SO for alias of rollapp 3 + }, + expiryByAlias: []dymnstypes.ActiveSellOrdersExpirationRecord{ + { + AssetId: rollApp_1_byOwner_asSrc.alias, + ExpireAt: soExpiredEpoch, + }, + { + // no SO for alias of RollApp 3 but still have reference + AssetId: rollApp_3_byOwner_asSrc.alias, + ExpireAt: soExpiredEpoch, + }, + }, + beforeHookTestFunc: func(s *KeeperTestSuite) { + s.requireRollApp(rollApp_1_byOwner_asSrc.rollAppId).HasAlias(rollApp_1_byOwner_asSrc.alias) + s.requireRollApp(rollApp_3_byOwner_asSrc.rollAppId).HasAlias(rollApp_3_byOwner_asSrc.alias) + }, + wantErr: false, + wantExpiryByAlias: []dymnstypes.ActiveSellOrdersExpirationRecord{ + // removed reference to alias of RollApp 1 because of processed + { + // reference to the non-existing SO will be kept and later catch by invariants + AssetId: rollApp_3_byOwner_asSrc.alias, + ExpireAt: soExpiredEpoch, + }, + }, + afterHookTestFunc: func(s *KeeperTestSuite) { + // unchanged + s.requireRollApp(rollApp_1_byOwner_asSrc.rollAppId).HasAlias(rollApp_1_byOwner_asSrc.alias) + s.requireRollApp(rollApp_3_byOwner_asSrc.rollAppId).HasAlias(rollApp_3_byOwner_asSrc.alias) + }, + }, + { + name: "pass - ignore incorrect expiry, later invariants will catch it", + rollApps: []rollapp{rollApp_1_byOwner_asSrc, rollApp_2_byBuyer_asDst, rollApp_3_byOwner_asSrc}, + sellOrders: []dymnstypes.SellOrder{ + s.newAliasSellOrder(rollApp_1_byOwner_asSrc.alias). + WithMinPrice(minPrice). + WithExpiry(soExpiredEpoch). + Build(), + s.newAliasSellOrder(rollApp_3_byOwner_asSrc.alias). + WithMinPrice(minPrice). + WithExpiry(soNotExpiredEpoch). // SO not expired + Build(), + }, + expiryByAlias: []dymnstypes.ActiveSellOrdersExpirationRecord{ + { + AssetId: rollApp_1_byOwner_asSrc.alias, + ExpireAt: soExpiredEpoch, + }, + { + // incorrect, SO not expired + AssetId: rollApp_3_byOwner_asSrc.alias, + ExpireAt: soExpiredEpoch, + }, + }, + beforeHookTestFunc: func(s *KeeperTestSuite) { + }, + wantErr: false, + wantExpiryByAlias: []dymnstypes.ActiveSellOrdersExpirationRecord{ + // removed reference to alias of RollApp 1 because of processed + // reference to alias of RollApp 3 was kept because not expired + { + AssetId: rollApp_3_byOwner_asSrc.alias, + ExpireAt: soExpiredEpoch, // still keep the incorrect expiry + }, + }, + afterHookTestFunc: func(s *KeeperTestSuite) { + }, + }, + { + name: "pass - ignore processing SO when error occurs", + rollApps: []rollapp{rollApp_1_byOwner_asSrc, rollApp_2_byBuyer_asDst}, + sellOrders: []dymnstypes.SellOrder{ + s.newAliasSellOrder(rollApp_1_byOwner_asSrc.alias). + WithMinPrice(minPrice). + WithExpiry(soExpiredEpoch). + WithAliasBid(creator_2_asBidder, minPrice, rollApp_2_byBuyer_asDst.rollAppId). + Build(), + }, + expiryByAlias: []dymnstypes.ActiveSellOrdersExpirationRecord{ + { + AssetId: rollApp_1_byOwner_asSrc.alias, + ExpireAt: soExpiredEpoch, + }, + }, + preMintModuleBalance: 1, // not enough balance + beforeHookTestFunc: func(s *KeeperTestSuite) { + s.requireRollApp(rollApp_1_byOwner_asSrc.rollAppId).HasAlias(rollApp_1_byOwner_asSrc.alias) + s.requireRollApp(rollApp_2_byBuyer_asDst.rollAppId).HasNoAlias() + }, + wantErr: false, + wantExpiryByAlias: []dymnstypes.ActiveSellOrdersExpirationRecord{ + { + AssetId: rollApp_1_byOwner_asSrc.alias, + ExpireAt: soExpiredEpoch, + }, + }, + afterHookTestFunc: func(s *KeeperTestSuite) { + // unchanged + s.requireRollApp(rollApp_1_byOwner_asSrc.rollAppId).HasAlias(rollApp_1_byOwner_asSrc.alias) + s.requireRollApp(rollApp_2_byBuyer_asDst.rollAppId).HasNoAlias() + s.requireAlias(rollApp_1_byOwner_asSrc.alias).mustHaveActiveSO() + }, + }, + { + name: "pass - ignore processing SO when error occurs, one pass one fail", + rollApps: []rollapp{rollApp_1_byOwner_asSrc, rollApp_2_byBuyer_asDst, rollApp_3_byOwner_asSrc}, + sellOrders: []dymnstypes.SellOrder{ + s.newAliasSellOrder(rollApp_1_byOwner_asSrc.alias). + WithMinPrice(minPrice). + WithExpiry(soExpiredEpoch). + WithAliasBid(creator_2_asBidder, minPrice, rollApp_2_byBuyer_asDst.rollAppId). + Build(), + s.newAliasSellOrder(rollApp_3_byOwner_asSrc.alias). + WithMinPrice(minPrice). + WithExpiry(soExpiredEpoch). + WithAliasBid(creator_2_asBidder, minPrice, rollApp_2_byBuyer_asDst.rollAppId). + Build(), + }, + expiryByAlias: []dymnstypes.ActiveSellOrdersExpirationRecord{ + { + AssetId: rollApp_1_byOwner_asSrc.alias, + ExpireAt: soExpiredEpoch, + }, + { + AssetId: rollApp_3_byOwner_asSrc.alias, + ExpireAt: soExpiredEpoch, + }, + }, + preMintModuleBalance: minPrice + 1, // just enough for the first SO + beforeHookTestFunc: func(s *KeeperTestSuite) { + s.requireRollApp(rollApp_1_byOwner_asSrc.rollAppId).HasAlias(rollApp_1_byOwner_asSrc.alias) + s.requireRollApp(rollApp_2_byBuyer_asDst.rollAppId).HasNoAlias() + s.requireRollApp(rollApp_3_byOwner_asSrc.rollAppId).HasAlias(rollApp_3_byOwner_asSrc.alias) + }, + wantErr: false, + wantExpiryByAlias: []dymnstypes.ActiveSellOrdersExpirationRecord{ + { + AssetId: rollApp_3_byOwner_asSrc.alias, + ExpireAt: soExpiredEpoch, + }, + }, + afterHookTestFunc: func(s *KeeperTestSuite) { + s.requireRollApp(rollApp_1_byOwner_asSrc.rollAppId).HasNoAlias() + s.requireRollApp(rollApp_2_byBuyer_asDst.rollAppId).HasAlias(rollApp_1_byOwner_asSrc.alias) + s.requireRollApp(rollApp_3_byOwner_asSrc.rollAppId).HasAlias(rollApp_3_byOwner_asSrc.alias) + + s.requireAlias(rollApp_1_byOwner_asSrc.alias).noActiveSO() + s.requireAlias(rollApp_3_byOwner_asSrc.alias).mustHaveActiveSO() + + s.EqualValues(1, s.moduleBalance()) + s.EqualValues(minPrice, s.balance(rollApp_1_byOwner_asSrc.owner)) + }, + }, + { + name: "pass - ignore processing SO when error occurs, one fail one pass", + rollApps: []rollapp{rollApp_1_byOwner_asSrc, rollApp_2_byBuyer_asDst, rollApp_3_byOwner_asSrc}, + sellOrders: []dymnstypes.SellOrder{ + s.newAliasSellOrder(rollApp_1_byOwner_asSrc.alias). + WithMinPrice(minPrice). + WithExpiry(soExpiredEpoch). + WithAliasBid(creator_2_asBidder, minPrice*2, rollApp_2_byBuyer_asDst.rollAppId). + Build(), + s.newAliasSellOrder(rollApp_3_byOwner_asSrc.alias). + WithMinPrice(minPrice). + WithExpiry(soExpiredEpoch). + WithAliasBid(creator_2_asBidder, minPrice, rollApp_2_byBuyer_asDst.rollAppId). + Build(), + }, + expiryByAlias: []dymnstypes.ActiveSellOrdersExpirationRecord{ + { + AssetId: rollApp_1_byOwner_asSrc.alias, + ExpireAt: soExpiredEpoch, + }, + { + AssetId: rollApp_3_byOwner_asSrc.alias, + ExpireAt: soExpiredEpoch, + }, + }, + preMintModuleBalance: minPrice + 1, // just enough for the second SO + beforeHookTestFunc: func(s *KeeperTestSuite) { + s.requireRollApp(rollApp_1_byOwner_asSrc.rollAppId).HasAlias(rollApp_1_byOwner_asSrc.alias) + s.requireRollApp(rollApp_2_byBuyer_asDst.rollAppId).HasNoAlias() + s.requireRollApp(rollApp_3_byOwner_asSrc.rollAppId).HasAlias(rollApp_3_byOwner_asSrc.alias) + }, + wantErr: false, + wantExpiryByAlias: []dymnstypes.ActiveSellOrdersExpirationRecord{ + { + AssetId: rollApp_1_byOwner_asSrc.alias, + ExpireAt: soExpiredEpoch, + }, + }, + afterHookTestFunc: func(s *KeeperTestSuite) { + s.requireRollApp(rollApp_1_byOwner_asSrc.rollAppId).HasAlias(rollApp_1_byOwner_asSrc.alias) + s.requireRollApp(rollApp_2_byBuyer_asDst.rollAppId).HasAlias(rollApp_3_byOwner_asSrc.alias) + s.requireRollApp(rollApp_3_byOwner_asSrc.rollAppId).HasNoAlias() + + s.requireAlias(rollApp_1_byOwner_asSrc.alias).mustHaveActiveSO() + s.requireAlias(rollApp_3_byOwner_asSrc.alias).noActiveSO() + + s.EqualValues(1, s.moduleBalance()) + s.EqualValues(minPrice, s.balance(rollApp_1_byOwner_asSrc.owner)) + }, + }, + } + for _, tt := range tests { + s.Run(tt.name, func() { + s.RefreshContext() + + s.Require().NotNil(tt.beforeHookTestFunc, "mis-configured test case") + s.Require().NotNil(tt.afterHookTestFunc, "mis-configured test case") + + if tt.preMintModuleBalance > 0 { + s.mintToModuleAccount(tt.preMintModuleBalance) + } + + err := s.dymNsKeeper.SetActiveSellOrdersExpiration(s.ctx, &dymnstypes.ActiveSellOrdersExpiration{ + Records: tt.expiryByAlias, + }, dymnstypes.TypeAlias) + s.Require().NoError(err) + + for _, rollApp := range tt.rollApps { + s.persistRollApp(rollApp) + } + + for _, so := range tt.sellOrders { + err = s.dymNsKeeper.SetSellOrder(s.ctx, so) + s.Require().NoError(err) + } + + useEpochIdentifier := s.moduleParams().Misc.EndEpochHookIdentifier + if tt.customEpochIdentifier != "" { + useEpochIdentifier = tt.customEpochIdentifier + } + + tt.beforeHookTestFunc(s) + + err = s.dymNsKeeper.GetEpochHooks().AfterEpochEnd(s.ctx, useEpochIdentifier, 1) + + defer func() { + tt.afterHookTestFunc(s) + + aSoe := s.dymNsKeeper.GetActiveSellOrdersExpiration(s.ctx, dymnstypes.TypeAlias) + if len(tt.wantExpiryByAlias) == 0 { + s.Empty(aSoe.Records) + } else { + s.Equal(tt.wantExpiryByAlias, aSoe.Records) + } + }() + + if tt.wantErr { + s.Require().ErrorContains(err, tt.wantErrContains) + + return + } + + s.Require().NoError(err) + }) + } +} + +func (s *KeeperTestSuite) Test_rollappHooks_RollappCreated() { + const price1L = 9 + const price2L = 8 + const price3L = 7 + const price4L = 6 + const price5L = 5 + const price6L = 4 + const price7PL = 3 + + // the number values used in this test will be multiplied by this value + priceMultiplier := sdk.NewInt(1e18) + + s.updateModuleParams(func(moduleParams dymnstypes.Params) dymnstypes.Params { + moduleParams.Price.AliasPriceSteps = []sdkmath.Int{ + sdk.NewInt(price1L).Mul(priceMultiplier), + sdk.NewInt(price2L).Mul(priceMultiplier), + sdk.NewInt(price3L).Mul(priceMultiplier), + sdk.NewInt(price4L).Mul(priceMultiplier), + sdk.NewInt(price5L).Mul(priceMultiplier), + sdk.NewInt(price6L).Mul(priceMultiplier), + sdk.NewInt(price7PL).Mul(priceMultiplier), + } + return moduleParams + }) + s.SaveCurrentContext() + + creatorAccAddr := sdk.AccAddress(testAddr(1).bytes()) + dymNameOwnerAcc := testAddr(2) + anotherAcc := testAddr(3) + + tests := []struct { + name string + addRollApps []string + preRunSetup func(s *KeeperTestSuite) + originalCreatorBalance int64 + originalModuleBalance int64 + rollAppId string + alias string + wantErr bool + wantErrContains string + wantSuccess bool + wantLaterCreatorBalance int64 + postTest func(s *KeeperTestSuite) + }{ + { + name: "pass - register without problem", + addRollApps: []string{"rollapp_1-1"}, + originalCreatorBalance: price5L + 2, + originalModuleBalance: 1, + rollAppId: "rollapp_1-1", + alias: "alias", + wantErr: false, + wantSuccess: true, + wantLaterCreatorBalance: 2, + }, + { + name: "pass - mapping RollApp ID <=> Alias should be set", + addRollApps: []string{"rollapp_1-1"}, + originalCreatorBalance: price5L, + rollAppId: "rollapp_1-1", + alias: "alias", + wantErr: false, + wantSuccess: true, + postTest: func(s *KeeperTestSuite) { + alias, found := s.dymNsKeeper.GetAliasByRollAppId(s.ctx, "rollapp_1-1") + s.True(found) + s.Equal("alias", alias) + + rollAppId, found := s.dymNsKeeper.GetRollAppIdByAlias(s.ctx, "alias") + s.True(found) + s.Equal("rollapp_1-1", rollAppId) + }, + }, + { + name: "pass - if input alias is empty, do nothing", + addRollApps: []string{"rollapp_1-1"}, + originalCreatorBalance: 0, + rollAppId: "rollapp_1-1", + alias: "", + wantErr: false, + wantSuccess: true, + wantLaterCreatorBalance: 0, + postTest: func(s *KeeperTestSuite) { + // mapping should not be created + + alias, found := s.dymNsKeeper.GetAliasByRollAppId(s.ctx, "rollapp_1-1") + s.False(found) + s.Empty(alias) + + rollAppId, found := s.dymNsKeeper.GetRollAppIdByAlias(s.ctx, "alias") + s.False(found) + s.Empty(rollAppId) + }, + }, + { + name: "pass - Alias cost subtracted from creator and burned", + addRollApps: []string{"rollapp_1-1"}, + originalCreatorBalance: price5L + 10, + originalModuleBalance: 1, + rollAppId: "rollapp_1-1", + alias: "alias", + wantErr: false, + wantSuccess: true, + wantLaterCreatorBalance: 10, + }, + { + name: "pass - charges correct price for Alias based on length, 1 char", + addRollApps: []string{"rollapp_1-1"}, + originalCreatorBalance: price1L + 10, + rollAppId: "rollapp_1-1", + alias: "a", + wantErr: false, + wantSuccess: true, + wantLaterCreatorBalance: 10, + }, + { + name: "pass - charges correct price for Alias based on length, 2 chars", + addRollApps: []string{"rollapp_1-1"}, + originalCreatorBalance: price2L + 10, + rollAppId: "rollapp_1-1", + alias: "ab", + wantErr: false, + wantSuccess: true, + wantLaterCreatorBalance: 10, + }, + { + name: "pass - charges correct price for Alias based on length, 3 chars", + addRollApps: []string{"rollapp_1-1"}, + originalCreatorBalance: price3L + 10, + rollAppId: "rollapp_1-1", + alias: "dog", + wantErr: false, + wantSuccess: true, + wantLaterCreatorBalance: 10, + }, + { + name: "pass - charges correct price for Alias based on length, 4 chars", + addRollApps: []string{"rollapp_1-1"}, + originalCreatorBalance: price4L + 10, + rollAppId: "rollapp_1-1", + alias: "pool", + wantErr: false, + wantSuccess: true, + wantLaterCreatorBalance: 10, + }, + { + name: "pass - charges correct price for Alias based on length, 5 chars", + addRollApps: []string{"rollapp_1-1"}, + originalCreatorBalance: price5L + 10, + rollAppId: "rollapp_1-1", + alias: "angel", + wantErr: false, + wantSuccess: true, + wantLaterCreatorBalance: 10, + }, + { + name: "pass - charges correct price for Alias based on length, 6 chars", + addRollApps: []string{"rollapp_1-1"}, + originalCreatorBalance: price6L + 10, + rollAppId: "rollapp_1-1", + alias: "bridge", + wantErr: false, + wantSuccess: true, + wantLaterCreatorBalance: 10, + }, + { + name: "pass - charges correct price for Alias based on length, 7 chars", + addRollApps: []string{"rollapp_1-1"}, + originalCreatorBalance: price7PL + 10, + rollAppId: "rollapp_1-1", + alias: "academy", + wantErr: false, + wantSuccess: true, + wantLaterCreatorBalance: 10, + }, + { + name: "pass - charges correct price for Alias based on length, 7+ chars", + addRollApps: []string{"rollapp_1-1"}, + originalCreatorBalance: price7PL + 10, + rollAppId: "rollapp_1-1", + alias: "dymension", + wantErr: false, + wantSuccess: true, + wantLaterCreatorBalance: 10, + }, + { + name: "fail - RollApp not exists", + addRollApps: nil, + originalCreatorBalance: price1L, + rollAppId: "nad_0-0", + alias: "alias", + wantErr: true, + wantErrContains: "not a RollApp chain-id", + wantSuccess: false, + wantLaterCreatorBalance: price1L, + }, + { + name: "fail - mapping should not be created", + addRollApps: nil, + originalCreatorBalance: price1L, + rollAppId: "nad_0-0", + alias: "alias", + wantErr: true, + wantErrContains: "not", + wantSuccess: false, + wantLaterCreatorBalance: price1L, + postTest: func(s *KeeperTestSuite) { + _, found := s.dymNsKeeper.GetAliasByRollAppId(s.ctx, "nad_0-0") + s.False(found) + + _, found = s.dymNsKeeper.GetRollAppIdByAlias(s.ctx, "alias") + s.False(found) + }, + }, + { + name: "fail - reject bad alias", + addRollApps: []string{"rollapp_1-1"}, + originalCreatorBalance: price1L, + rollAppId: "rollapp_1-1", + alias: "@@@", + wantErr: true, + wantErrContains: "invalid alias format", + wantSuccess: false, + wantLaterCreatorBalance: price1L, + }, + { + name: "pass - can register if alias is not used", + addRollApps: []string{"rollapp_1-1", "rollapp_2-2"}, + preRunSetup: func(s *KeeperTestSuite) { + s.updateModuleParams(func(moduleParams dymnstypes.Params) dymnstypes.Params { + moduleParams.Chains.AliasesOfChainIds = []dymnstypes.AliasesOfChainId{ + { + ChainId: "dymension_1100-1", + Aliases: []string{"dym"}, + }, + } + return moduleParams + }) + + err := s.dymNsKeeper.SetAliasForRollAppId(s.ctx, "rollapp_2-2", "ra2") + s.NoError(err) + }, + originalCreatorBalance: price5L + 2, + originalModuleBalance: 1, + rollAppId: "rollapp_1-1", + alias: "alias", + wantErr: false, + wantSuccess: true, + wantLaterCreatorBalance: 2, + postTest: func(s *KeeperTestSuite) { + alias, found := s.dymNsKeeper.GetAliasByRollAppId(s.ctx, "rollapp_1-1") + s.True(found) + s.Equal("alias", alias) + + rollAppId, found := s.dymNsKeeper.GetRollAppIdByAlias(s.ctx, "alias") + s.True(found) + s.Equal("rollapp_1-1", rollAppId) + }, + }, + { + name: "fail - reject if alias is presents as chain-id in params", + addRollApps: []string{"rollapp_1-1", "rollapp_2-2"}, + preRunSetup: func(s *KeeperTestSuite) { + s.updateModuleParams(func(moduleParams dymnstypes.Params) dymnstypes.Params { + moduleParams.Chains.AliasesOfChainIds = []dymnstypes.AliasesOfChainId{ + { + ChainId: "bridge", + Aliases: []string{"b"}, + }, + } + return moduleParams + }) + + err := s.dymNsKeeper.SetAliasForRollAppId(s.ctx, "rollapp_2-2", "ra2") + s.NoError(err) + }, + originalCreatorBalance: price1L, + originalModuleBalance: 1, + rollAppId: "rollapp_1-1", + alias: "bridge", + wantErr: true, + wantErrContains: "alias already in use or preserved", + wantSuccess: false, + wantLaterCreatorBalance: price1L, + postTest: func(s *KeeperTestSuite) { + alias, found := s.dymNsKeeper.GetAliasByRollAppId(s.ctx, "rollapp_1-1") + s.False(found) + s.Empty(alias) + + rollAppId, found := s.dymNsKeeper.GetRollAppIdByAlias(s.ctx, "bridge") + s.False(found) + s.Empty(rollAppId) + }, + }, + { + name: "fail - reject if alias is presents as alias of a chain-id in params", + addRollApps: []string{"rollapp_1-1", "rollapp_2-2"}, + preRunSetup: func(s *KeeperTestSuite) { + s.updateModuleParams(func(moduleParams dymnstypes.Params) dymnstypes.Params { + moduleParams.Chains.AliasesOfChainIds = []dymnstypes.AliasesOfChainId{ + { + ChainId: "dymension_1100-1", + Aliases: []string{"dym"}, + }, + } + return moduleParams + }) + + err := s.dymNsKeeper.SetAliasForRollAppId(s.ctx, "rollapp_2-2", "ra2") + s.NoError(err) + }, + originalCreatorBalance: price1L, + originalModuleBalance: 1, + rollAppId: "rollapp_1-1", + alias: "dym", + wantErr: true, + wantErrContains: "alias already in use or preserved", + wantSuccess: false, + wantLaterCreatorBalance: price1L, + postTest: func(s *KeeperTestSuite) { + alias, found := s.dymNsKeeper.GetAliasByRollAppId(s.ctx, "rollapp_1-1") + s.False(found) + s.Empty(alias) + + rollAppId, found := s.dymNsKeeper.GetRollAppIdByAlias(s.ctx, "dym") + s.False(found) + s.Empty(rollAppId) + }, + }, + { + name: "fail - reject if alias is a RollApp-ID", + addRollApps: []string{"rollapp_1-1", "rollapp_2-2"}, + preRunSetup: func(s *KeeperTestSuite) { + s.pureSetRollApp(rollapptypes.Rollapp{ + RollappId: "rollapp", + Owner: creatorAccAddr.String(), + }) + + s.updateModuleParams(func(moduleParams dymnstypes.Params) dymnstypes.Params { + moduleParams.Chains.AliasesOfChainIds = []dymnstypes.AliasesOfChainId{ + { + ChainId: "dymension_1100-1", + Aliases: []string{"dym"}, + }, + } + return moduleParams + }) + + err := s.dymNsKeeper.SetAliasForRollAppId(s.ctx, "rollapp_2-2", "ra2") + s.Require().NoError(err) + }, + originalCreatorBalance: price1L, + originalModuleBalance: 1, + rollAppId: "rollapp_1-1", + alias: "rollapp", + wantErr: true, + wantErrContains: "alias already in use or preserved", + wantSuccess: false, + wantLaterCreatorBalance: price1L, + postTest: func(s *KeeperTestSuite) { + alias, found := s.dymNsKeeper.GetAliasByRollAppId(s.ctx, "rollapp_1-1") + s.False(found) + s.Empty(alias) + + rollAppId, found := s.dymNsKeeper.GetRollAppIdByAlias(s.ctx, "rollapp") + s.False(found) + s.Empty(rollAppId) + }, + }, + { + name: "fail - reject if alias used by another RollApp", + addRollApps: []string{"rollapp_1-1", "rollapp_2-2"}, + preRunSetup: func(s *KeeperTestSuite) { + s.updateModuleParams(func(moduleParams dymnstypes.Params) dymnstypes.Params { + moduleParams.Chains.AliasesOfChainIds = []dymnstypes.AliasesOfChainId{ + { + ChainId: "dymension_1100-1", + Aliases: []string{"dym"}, + }, + } + return moduleParams + }) + + err := s.dymNsKeeper.SetAliasForRollAppId(s.ctx, "rollapp_2-2", "alias") + s.Require().NoError(err) + }, + originalCreatorBalance: price1L, + originalModuleBalance: 1, + rollAppId: "rollapp_1-1", + alias: "alias", + wantErr: true, + wantErrContains: "alias already in use or preserved", + wantSuccess: false, + wantLaterCreatorBalance: price1L, + postTest: func(s *KeeperTestSuite) { + alias, found := s.dymNsKeeper.GetAliasByRollAppId(s.ctx, "rollapp_1-1") + s.False(found) + s.Empty(alias) + + rollAppId, found := s.dymNsKeeper.GetRollAppIdByAlias(s.ctx, "alias") + s.True(found) + s.Equal("rollapp_2-2", rollAppId) + }, + }, + { + name: "fail - reject if creator does not have enough balance to pay the fee", + addRollApps: []string{"rollapp_1-1"}, + originalCreatorBalance: 1, + originalModuleBalance: 1, + rollAppId: "rollapp_1-1", + alias: "alias", + wantErr: true, + wantErrContains: "insufficient funds", + wantSuccess: false, + wantLaterCreatorBalance: 1, + }, + { + name: "pass - can resolve address using alias", + addRollApps: []string{"rollapp_1-1"}, + preRunSetup: nil, + originalCreatorBalance: price5L, + rollAppId: "rollapp_1-1", + alias: "alias", + wantErr: false, + wantSuccess: true, + postTest: func(s *KeeperTestSuite) { + dymName := dymnstypes.DymName{ + Name: "my-name", + Owner: dymNameOwnerAcc.bech32(), + Controller: dymNameOwnerAcc.bech32(), + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{ + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "rollapp_1-1", + Value: dymNameOwnerAcc.bech32(), + }, + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "rollapp_1-1", + Path: "sub", + Value: anotherAcc.bech32(), + }, + }, + } + s.setDymNameWithFunctionsAfter(dymName) + + outputAddr, err := s.dymNsKeeper.ResolveByDymNameAddress(s.ctx, "my-name@rollapp_1-1") + s.Require().NoError(err) + s.Equal(dymNameOwnerAcc.bech32(), outputAddr) + + outputAddr, err = s.dymNsKeeper.ResolveByDymNameAddress(s.ctx, "my-name@alias") + s.Require().NoError(err) + s.Equal(dymNameOwnerAcc.bech32(), outputAddr) + + outputAddr, err = s.dymNsKeeper.ResolveByDymNameAddress(s.ctx, "sub.my-name@alias") + s.Require().NoError(err) + s.Equal(anotherAcc.bech32(), outputAddr) + + outputs, err := s.dymNsKeeper.ReverseResolveDymNameAddress(s.ctx, anotherAcc.bech32(), "rollapp_1-1") + s.Require().NoError(err) + s.Require().NotEmpty(outputs) + s.Equal("sub.my-name@alias", outputs[0].String()) + }, + }, + } + for _, tt := range tests { + s.Run(tt.name, func() { + s.Require().NotEqual(tt.wantSuccess, tt.wantErr, "mis-configured test case") + + s.RefreshContext() + + if tt.originalCreatorBalance > 0 { + s.mintToAccount2(creatorAccAddr.String(), sdk.NewInt(tt.originalCreatorBalance).Mul(priceMultiplier)) + } + + if tt.originalModuleBalance > 0 { + s.mintToModuleAccount2(sdk.NewInt(tt.originalModuleBalance).Mul(priceMultiplier)) + } + + for _, rollAppId := range tt.addRollApps { + s.rollAppKeeper.SetRollapp(s.ctx, rollapptypes.Rollapp{ + RollappId: rollAppId, + Owner: creatorAccAddr.String(), + }) + } + + if tt.preRunSetup != nil { + tt.preRunSetup(s) + } + + err := s.dymNsKeeper.GetRollAppHooks().RollappCreated(s.ctx, tt.rollAppId, tt.alias, creatorAccAddr) + + defer func() { + if s.T().Failed() { + return + } + + laterModuleBalance := s.moduleBalance2() + s.Equal( + sdk.NewInt(tt.originalModuleBalance).Mul(priceMultiplier), + laterModuleBalance, + "module balance should not be changed regardless of success because of burn", + ) + + if tt.postTest != nil { + tt.postTest(s) + } + }() + + if tt.wantErr { + s.Require().ErrorContains(err, tt.wantErrContains) + return + } + + s.Require().NoError(err) + + laterCreatorBalance := s.balance2(creatorAccAddr.String()) + s.Equal( + sdk.NewInt(tt.wantLaterCreatorBalance).Mul(priceMultiplier), + laterCreatorBalance, + "creator balance mismatch", + ) + + // event should be fired + func() { + if tt.alias == "" { + return + } + + events := s.ctx.EventManager().Events() + s.Require().NotEmpty(events) + + for _, event := range events { + if event.Type == dymnstypes.EventTypeSell { + return + } + } + + s.T().Errorf("event %s not found", dymnstypes.EventTypeSell) + }() + }) + } + + s.Run("if alias is empty, do nothing", func() { + originalTxGas := s.ctx.GasMeter().GasConsumed() + + err := s.dymNsKeeper.GetRollAppHooks().RollappCreated(s.ctx, "rollapp_1-1", "", creatorAccAddr) + s.Require().NoError(err) + + s.Equal(originalTxGas, s.ctx.GasMeter().GasConsumed(), "should not consume gas") + }) +} + +func (s *KeeperTestSuite) Test_rollappHooks_OnRollAppIdChanged() { + const previousRollAppId = "rollapp_1-1" + const newRollAppId = "rollapp_1-2" + + const name1 = "name1" + const name2 = "name2" + + user1Acc := testAddr(1) + user2Acc := testAddr(2) + user3Acc := testAddr(3) + user4Acc := testAddr(4) + user5Acc := testAddr(5) + + genRollApp := func(rollAppId string, aliases ...string) *rollapp { + ra := newRollApp(rollAppId) + for _, alias := range aliases { + _ = ra.WithAlias(alias) + } + return ra + } + + tests := []struct { + name string + setupFunc func(s *KeeperTestSuite) + testFunc func(s *KeeperTestSuite) + }{ + { + name: "pass - normal migration, with alias, with Dym-Name", + setupFunc: func(s *KeeperTestSuite) { + s.persistRollApp(*genRollApp(previousRollAppId, "alias")) + s.persistRollApp(*genRollApp(newRollAppId)) + + s.requireRollApp(previousRollAppId).HasAlias("alias") + s.requireRollApp(newRollAppId).HasNoAlias() + + s.setDymNameWithFunctionsAfter( + newDN(name1, user1Acc.bech32()). + cfgN(previousRollAppId, "", user2Acc.bech32()). + build(), + ) + + outputAddr, err := s.dymNsKeeper.ResolveByDymNameAddress(s.ctx, name1+"@"+previousRollAppId) + s.NoError(err) + s.Equal(user2Acc.bech32(), outputAddr) + + outputDymNameAddrs, err := s.dymNsKeeper.ReverseResolveDymNameAddress(s.ctx, user2Acc.bech32(), previousRollAppId) + s.NoError(err) + s.NotEmpty(outputDymNameAddrs) + + _, err = s.dymNsKeeper.ResolveByDymNameAddress(s.ctx, name1+"@"+newRollAppId) + s.ErrorContains(err, "not found") + + outputDymNameAddrs, err = s.dymNsKeeper.ReverseResolveDymNameAddress(s.ctx, user2Acc.bech32(), newRollAppId) + s.NoError(err) + s.Empty(outputDymNameAddrs) + }, + testFunc: func(s *KeeperTestSuite) { + s.requireRollApp(previousRollAppId).HasNoAlias() + s.requireRollApp(newRollAppId).HasAlias("alias") + + // + + _, err := s.dymNsKeeper.ResolveByDymNameAddress(s.ctx, name1+"@"+previousRollAppId) + s.ErrorContains(err, "not found") + + outputDymNameAddrs, err := s.dymNsKeeper.ReverseResolveDymNameAddress(s.ctx, user2Acc.bech32(), previousRollAppId) + s.NoError(err) + s.Empty(outputDymNameAddrs) + + // + + outputAddr, err := s.dymNsKeeper.ResolveByDymNameAddress(s.ctx, name1+"@"+newRollAppId) + s.NoError(err) + s.Equal(user2Acc.bech32(), outputAddr) + + outputDymNameAddrs, err = s.dymNsKeeper.ReverseResolveDymNameAddress(s.ctx, user2Acc.bech32(), newRollAppId) + s.NoError(err) + s.NotEmpty(outputDymNameAddrs) + }, + }, + { + name: "pass - normal migration, with multiple aliases, with multiple Dym-Names", + setupFunc: func(s *KeeperTestSuite) { + s.persistRollApp(*genRollApp(previousRollAppId, "one", "two", "three")) + s.persistRollApp(*genRollApp(newRollAppId)) + + s.requireRollApp(previousRollAppId).HasAlias("one", "two", "three") + s.requireRollApp(newRollAppId).HasNoAlias() + + s.setDymNameWithFunctionsAfter( + newDN(name1, user1Acc.bech32()). + cfgN(previousRollAppId, "", user2Acc.bech32()). + build(), + ) + + s.setDymNameWithFunctionsAfter( + newDN(name2, user1Acc.bech32()). + cfgN(previousRollAppId, "", user2Acc.bech32()). + cfgN(previousRollAppId, "sub2", user2Acc.bech32()). + cfgN(previousRollAppId, "sub3", user3Acc.bech32()). + cfgN("", "", user4Acc.bech32()). + cfgN("", "sub5", user5Acc.bech32()). + build(), + ) + + // + + outputAddr, err := s.dymNsKeeper.ResolveByDymNameAddress(s.ctx, name1+"@"+previousRollAppId) + s.NoError(err) + s.Equal(user2Acc.bech32(), outputAddr) + + outputAddr, err = s.dymNsKeeper.ResolveByDymNameAddress(s.ctx, name2+"@"+previousRollAppId) + s.NoError(err) + s.Equal(user2Acc.bech32(), outputAddr) + + outputDymNameAddrs, err := s.dymNsKeeper.ReverseResolveDymNameAddress(s.ctx, user2Acc.bech32(), previousRollAppId) + s.NoError(err) + s.Len(outputDymNameAddrs, 3) // 1 from name1, 2 from name2 + + // + + outputAddr, err = s.dymNsKeeper.ResolveByDymNameAddress(s.ctx, "sub2."+name2+"@"+previousRollAppId) + s.NoError(err) + s.Equal(user2Acc.bech32(), outputAddr) + + // + + outputAddr, err = s.dymNsKeeper.ResolveByDymNameAddress(s.ctx, "sub3."+name2+"@"+previousRollAppId) + s.NoError(err) + s.Equal(user3Acc.bech32(), outputAddr) + + outputDymNameAddrs, err = s.dymNsKeeper.ReverseResolveDymNameAddress(s.ctx, user3Acc.bech32(), previousRollAppId) + s.NoError(err) + s.Len(outputDymNameAddrs, 1) + + // + + outputAddr, err = s.dymNsKeeper.ResolveByDymNameAddress(s.ctx, name2+"@"+s.chainId) + s.NoError(err) + s.Equal(user4Acc.bech32(), outputAddr) + + outputDymNameAddrs, err = s.dymNsKeeper.ReverseResolveDymNameAddress(s.ctx, user4Acc.bech32(), s.chainId) + s.NoError(err) + s.Len(outputDymNameAddrs, 1) + + outputDymNameAddrs, err = s.dymNsKeeper.ReverseResolveDymNameAddress(s.ctx, user4Acc.bech32(), previousRollAppId) + s.NoError(err) + if s.Len(outputDymNameAddrs, 1) { + s.Equal(name2+"@one", outputDymNameAddrs[0].String()) // result of fallback lookup + } + + // + + outputAddr, err = s.dymNsKeeper.ResolveByDymNameAddress(s.ctx, "sub5."+name2+"@"+s.chainId) + s.NoError(err) + s.Equal(user5Acc.bech32(), outputAddr) + + outputDymNameAddrs, err = s.dymNsKeeper.ReverseResolveDymNameAddress(s.ctx, user5Acc.bech32(), s.chainId) + s.NoError(err) + s.Len(outputDymNameAddrs, 1) + + outputDymNameAddrs, err = s.dymNsKeeper.ReverseResolveDymNameAddress(s.ctx, user5Acc.bech32(), previousRollAppId) + s.NoError(err) + s.Empty(outputDymNameAddrs) // no fallback lookup because it's a sub-name + }, + testFunc: func(s *KeeperTestSuite) { + s.requireRollApp(previousRollAppId).HasNoAlias() + s.requireRollApp(newRollAppId).HasAlias("one", "two", "three") + + // + + outputAddr, err := s.dymNsKeeper.ResolveByDymNameAddress(s.ctx, name1+"@"+newRollAppId) + s.NoError(err) + s.Equal(user2Acc.bech32(), outputAddr) + + outputAddr, err = s.dymNsKeeper.ResolveByDymNameAddress(s.ctx, name2+"@"+newRollAppId) + s.NoError(err) + s.Equal(user2Acc.bech32(), outputAddr) + + outputDymNameAddrs, err := s.dymNsKeeper.ReverseResolveDymNameAddress(s.ctx, user2Acc.bech32(), newRollAppId) + s.NoError(err) + s.Len(outputDymNameAddrs, 3) // 1 from name1, 2 from name2 + + // + + outputAddr, err = s.dymNsKeeper.ResolveByDymNameAddress(s.ctx, "sub2."+name2+"@"+newRollAppId) + s.NoError(err) + s.Equal(user2Acc.bech32(), outputAddr) + + // + + outputAddr, err = s.dymNsKeeper.ResolveByDymNameAddress(s.ctx, "sub3."+name2+"@"+newRollAppId) + s.NoError(err) + s.Equal(user3Acc.bech32(), outputAddr) + + outputDymNameAddrs, err = s.dymNsKeeper.ReverseResolveDymNameAddress(s.ctx, user3Acc.bech32(), newRollAppId) + s.NoError(err) + s.Len(outputDymNameAddrs, 1) + + // + + outputAddr, err = s.dymNsKeeper.ResolveByDymNameAddress(s.ctx, name2+"@"+s.chainId) + s.NoError(err) + s.Equal(user4Acc.bech32(), outputAddr) + + outputDymNameAddrs, err = s.dymNsKeeper.ReverseResolveDymNameAddress(s.ctx, user4Acc.bech32(), s.chainId) + s.NoError(err) + s.Len(outputDymNameAddrs, 1) + + outputDymNameAddrs, err = s.dymNsKeeper.ReverseResolveDymNameAddress(s.ctx, user4Acc.bech32(), newRollAppId) + s.NoError(err) + if s.Len(outputDymNameAddrs, 1) { + s.Equal(name2+"@one", outputDymNameAddrs[0].String()) // result of fallback lookup + } + + // + + outputAddr, err = s.dymNsKeeper.ResolveByDymNameAddress(s.ctx, "sub5."+name2+"@"+s.chainId) + s.NoError(err) + s.Equal(user5Acc.bech32(), outputAddr) + + outputDymNameAddrs, err = s.dymNsKeeper.ReverseResolveDymNameAddress(s.ctx, user5Acc.bech32(), s.chainId) + s.NoError(err) + s.Len(outputDymNameAddrs, 1) + + outputDymNameAddrs, err = s.dymNsKeeper.ReverseResolveDymNameAddress(s.ctx, user5Acc.bech32(), newRollAppId) + s.NoError(err) + s.Empty(outputDymNameAddrs) // no fallback lookup because it's a sub-name + }, + }, + { + name: "fail - when migrate alias failed, should not change anything", + setupFunc: func(s *KeeperTestSuite) { + s.persistRollApp(*genRollApp(previousRollAppId, "alias")) + s.persistRollApp(*genRollApp(newRollAppId)) + + s.requireRollApp(previousRollAppId).HasAlias("alias") + s.requireRollApp(newRollAppId).HasNoAlias() + + s.setDymNameWithFunctionsAfter( + newDN(name1, user1Acc.bech32()). + cfgN(previousRollAppId, "", user2Acc.bech32()). + build(), + ) + + outputAddr, err := s.dymNsKeeper.ResolveByDymNameAddress(s.ctx, name1+"@"+previousRollAppId) + s.NoError(err) + s.Equal(user2Acc.bech32(), outputAddr) + + outputDymNameAddrs, err := s.dymNsKeeper.ReverseResolveDymNameAddress(s.ctx, user2Acc.bech32(), previousRollAppId) + s.NoError(err) + s.NotEmpty(outputDymNameAddrs) + + _, err = s.dymNsKeeper.ResolveByDymNameAddress(s.ctx, name1+"@"+newRollAppId) + s.ErrorContains(err, "not found") + + outputDymNameAddrs, err = s.dymNsKeeper.ReverseResolveDymNameAddress(s.ctx, user2Acc.bech32(), newRollAppId) + s.NoError(err) + s.Empty(outputDymNameAddrs) + + // delete the rollapp to make the migration fail + s.rollAppKeeper.RemoveRollapp(s.ctx, previousRollAppId) + + s.Require().False(s.dymNsKeeper.IsRollAppId(s.ctx, previousRollAppId)) + }, + testFunc: func(s *KeeperTestSuite) { + // unchanged + + s.requireRollApp(newRollAppId).HasNoAlias() + + outputAddr, err := s.dymNsKeeper.ResolveByDymNameAddress(s.ctx, name1+"@"+previousRollAppId) + s.NoError(err) + s.Equal(user2Acc.bech32(), outputAddr) + + outputDymNameAddrs, err := s.dymNsKeeper.ReverseResolveDymNameAddress(s.ctx, user2Acc.bech32(), previousRollAppId) + s.NoError(err) + s.NotEmpty(outputDymNameAddrs) + + _, err = s.dymNsKeeper.ResolveByDymNameAddress(s.ctx, name1+"@"+newRollAppId) + s.ErrorContains(err, "not found") + + outputDymNameAddrs, err = s.dymNsKeeper.ReverseResolveDymNameAddress(s.ctx, user2Acc.bech32(), newRollAppId) + s.NoError(err) + s.Empty(outputDymNameAddrs) + }, + }, + { + name: "pass - when the new RollApp has existing aliases, merge them", + setupFunc: func(s *KeeperTestSuite) { + s.persistRollApp(*genRollApp(previousRollAppId, "one", "two", "three")) + s.persistRollApp(*genRollApp(newRollAppId, "four", "five", "six")) + + s.setDymNameWithFunctionsAfter( + newDN(name1, user1Acc.bech32()). + cfgN(previousRollAppId, "", user2Acc.bech32()). + build(), + ) + + // + + s.requireRollApp(previousRollAppId).HasAlias("one", "two", "three") + s.requireRollApp(newRollAppId).HasAlias("four", "five", "six") + + outputAddr, err := s.dymNsKeeper.ResolveByDymNameAddress(s.ctx, name1+"@"+previousRollAppId) + s.NoError(err) + s.Equal(user2Acc.bech32(), outputAddr) + }, + testFunc: func(s *KeeperTestSuite) { + s.requireRollApp(previousRollAppId).HasNoAlias() + s.requireRollApp(newRollAppId).HasAlias("one", "two", "three", "four", "five", "six") + + // others + + outputAddr, err := s.dymNsKeeper.ResolveByDymNameAddress(s.ctx, name1+"@"+newRollAppId) + s.NoError(err) + s.Equal(user2Acc.bech32(), outputAddr) + }, + }, + { + name: "pass - when previous RollApp has no alias", + setupFunc: func(s *KeeperTestSuite) { + s.persistRollApp(*genRollApp(previousRollAppId)) + s.persistRollApp(*genRollApp(newRollAppId, "new")) + + s.setDymNameWithFunctionsAfter( + newDN(name1, user1Acc.bech32()). + cfgN(previousRollAppId, "", user2Acc.bech32()). + build(), + ) + + // + + s.requireRollApp(previousRollAppId).HasNoAlias() + s.requireRollApp(newRollAppId).HasAlias("new") + + outputAddr, err := s.dymNsKeeper.ResolveByDymNameAddress(s.ctx, name1+"@"+previousRollAppId) + s.NoError(err) + s.Equal(user2Acc.bech32(), outputAddr) + }, + testFunc: func(s *KeeperTestSuite) { + s.requireRollApp(previousRollAppId).HasNoAlias() + s.requireRollApp(newRollAppId).HasAlias("new") + + // others + + outputAddr, err := s.dymNsKeeper.ResolveByDymNameAddress(s.ctx, name1+"@"+newRollAppId) + s.NoError(err) + s.Equal(user2Acc.bech32(), outputAddr) + }, + }, + { + name: "pass - when the new RollApp has existing aliases, priority previous default alias", + setupFunc: func(s *KeeperTestSuite) { + s.persistRollApp(*genRollApp(previousRollAppId, "old", "journey")) + s.persistRollApp(*genRollApp(newRollAppId, "new")) + + s.requireRollApp(previousRollAppId).HasAlias("old", "journey") + s.requireRollApp(newRollAppId).HasAlias("new") + }, + testFunc: func(s *KeeperTestSuite) { + s.requireRollApp(previousRollAppId).HasNoAlias() + s.requireRollApp(newRollAppId).HasAliasesWithOrder("old", "new", "journey") + }, + }, + } + for _, tt := range tests { + s.Run(tt.name, func() { + s.RefreshContext() + + if tt.setupFunc != nil { + tt.setupFunc(s) + } + + s.dymNsKeeper.GetFutureRollAppHooks().OnRollAppIdChanged(s.ctx, previousRollAppId, newRollAppId) + + if tt.testFunc != nil { + tt.testFunc(s) + } + }) + } +} diff --git a/x/dymns/keeper/invariants.go b/x/dymns/keeper/invariants.go new file mode 100644 index 000000000..83e72622f --- /dev/null +++ b/x/dymns/keeper/invariants.go @@ -0,0 +1,61 @@ +package keeper + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" +) + +const ( + sellOrderExpirationInvariantName = "sell-order-expiration" +) + +// RegisterInvariants registers the DymNS module invariants +func RegisterInvariants(ir sdk.InvariantRegistry, k Keeper) { + ir.RegisterRoute(dymnstypes.ModuleName, sellOrderExpirationInvariantName, SellOrderExpirationInvariant(k)) +} + +// SellOrderExpirationInvariant checks that the records in the `ActiveSellOrdersExpiration` records are valid. +// The `ActiveSellOrdersExpiration` is used in hook and records are fetch from the store frequently, +// so we need to make sure the records are correct. +func SellOrderExpirationInvariant(k Keeper) sdk.Invariant { + return func(ctx sdk.Context) (string, bool) { + var ( + broken bool + msg string + ) + + blockEpochUTC := ctx.BlockTime().Unix() + + for _, assetType := range []dymnstypes.AssetType{dymnstypes.TypeName, dymnstypes.TypeAlias} { + activeSellOrdersExpiration := k.GetActiveSellOrdersExpiration(ctx, assetType) + for _, record := range activeSellOrdersExpiration.Records { + if record.ExpireAt > blockEpochUTC { + // skip not expired ones. + // Why we skip expired ones? Because the hook only process the expired ones, + // so it is not necessary to check the not expired ones, to reduce store read. + continue + } + + sellOrder := k.GetSellOrder(ctx, record.AssetId, assetType) + if sellOrder == nil { + msg += fmt.Sprintf( + "sell order not found: assetId(%s), assetType(%s), expiry(%d)\n", + record.AssetId, assetType.PrettyName(), + record.ExpireAt, + ) + broken = true + } else if sellOrder.ExpireAt != record.ExpireAt { + msg += fmt.Sprintf( + "sell order expiration mismatch: assetId(%s), assetType(%s), expiry(%d) != actual(%d)\n", + record.AssetId, assetType.PrettyName(), + record.ExpireAt, sellOrder.ExpireAt, + ) + broken = true + } + } + } + return sdk.FormatInvariant(dymnstypes.ModuleName, sellOrderExpirationInvariantName, msg), broken + } +} diff --git a/x/dymns/keeper/invariants_test.go b/x/dymns/keeper/invariants_test.go new file mode 100644 index 000000000..65119746d --- /dev/null +++ b/x/dymns/keeper/invariants_test.go @@ -0,0 +1,202 @@ +package keeper_test + +import ( + dymnskeeper "github.com/dymensionxyz/dymension/v3/x/dymns/keeper" + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" +) + +func (s *KeeperTestSuite) TestInvariants() { + tests := []struct { + name string + nameASoe []dymnstypes.ActiveSellOrdersExpirationRecord + aliasASoe []dymnstypes.ActiveSellOrdersExpirationRecord + sellOrders []dymnstypes.SellOrder + wantBroken bool + wantMsgContains string + }{ + { + name: "pass - all empty", + wantBroken: false, + }, + { + name: "pass - all correct", + nameASoe: []dymnstypes.ActiveSellOrdersExpirationRecord{ + { + AssetId: "name1", + ExpireAt: 1, + }, + { + AssetId: "name2", + ExpireAt: 2, + }, + }, + aliasASoe: []dymnstypes.ActiveSellOrdersExpirationRecord{ + { + AssetId: "alias1", + ExpireAt: 3, + }, + { + AssetId: "alias2", + ExpireAt: 4, + }, + }, + sellOrders: []dymnstypes.SellOrder{ + s.newDymNameSellOrder("name1").WithExpiry(1).WithMinPrice(1).Build(), + s.newDymNameSellOrder("name2").WithExpiry(2).WithMinPrice(2).Build(), + s.newAliasSellOrder("alias1").WithExpiry(3).WithMinPrice(3).Build(), + s.newAliasSellOrder("alias2").WithExpiry(4).WithMinPrice(4).Build(), + }, + wantBroken: false, + }, + { + name: "fail - missing a Dym-Name Sell-Order", + nameASoe: []dymnstypes.ActiveSellOrdersExpirationRecord{ + { + AssetId: "name1", + ExpireAt: 1, + }, + { + AssetId: "name2", + ExpireAt: 2, + }, + }, + aliasASoe: []dymnstypes.ActiveSellOrdersExpirationRecord{ + { + AssetId: "alias1", + ExpireAt: 3, + }, + { + AssetId: "alias2", + ExpireAt: 4, + }, + }, + sellOrders: []dymnstypes.SellOrder{ + s.newDymNameSellOrder("name1").WithExpiry(1).WithMinPrice(1).Build(), + s.newAliasSellOrder("alias1").WithExpiry(3).WithMinPrice(3).Build(), + s.newAliasSellOrder("alias2").WithExpiry(4).WithMinPrice(4).Build(), + }, + wantBroken: true, + wantMsgContains: "sell order not found", + }, + { + name: "fail - missing a Alias sell order", + nameASoe: []dymnstypes.ActiveSellOrdersExpirationRecord{ + { + AssetId: "name1", + ExpireAt: 1, + }, + { + AssetId: "name2", + ExpireAt: 2, + }, + }, + aliasASoe: []dymnstypes.ActiveSellOrdersExpirationRecord{ + { + AssetId: "alias1", + ExpireAt: 3, + }, + { + AssetId: "alias2", + ExpireAt: 4, + }, + }, + sellOrders: []dymnstypes.SellOrder{ + s.newDymNameSellOrder("name1").WithExpiry(1).WithMinPrice(1).Build(), + s.newDymNameSellOrder("name2").WithExpiry(2).WithMinPrice(2).Build(), + s.newAliasSellOrder("alias1").WithExpiry(3).WithMinPrice(3).Build(), + }, + wantBroken: true, + wantMsgContains: "sell order not found", + }, + { + name: "fail - mis-match expiry of a Dym-Name sell order", + nameASoe: []dymnstypes.ActiveSellOrdersExpirationRecord{ + { + AssetId: "name1", + ExpireAt: 1, + }, + { + AssetId: "name2", + ExpireAt: 999, + }, + }, + aliasASoe: []dymnstypes.ActiveSellOrdersExpirationRecord{ + { + AssetId: "alias1", + ExpireAt: 3, + }, + { + AssetId: "alias2", + ExpireAt: 4, + }, + }, + sellOrders: []dymnstypes.SellOrder{ + s.newDymNameSellOrder("name1").WithExpiry(1).WithMinPrice(1).Build(), + s.newDymNameSellOrder("name2").WithExpiry(2).WithMinPrice(2).Build(), + s.newAliasSellOrder("alias1").WithExpiry(3).WithMinPrice(3).Build(), + s.newAliasSellOrder("alias2").WithExpiry(4).WithMinPrice(4).Build(), + }, + wantBroken: true, + wantMsgContains: "sell order expiration mismatch", + }, + { + name: "fail - mis-match expiry of a Alias sell order", + nameASoe: []dymnstypes.ActiveSellOrdersExpirationRecord{ + { + AssetId: "name1", + ExpireAt: 1, + }, + { + AssetId: "name2", + ExpireAt: 2, + }, + }, + aliasASoe: []dymnstypes.ActiveSellOrdersExpirationRecord{ + { + AssetId: "alias1", + ExpireAt: 3, + }, + { + AssetId: "alias2", + ExpireAt: 99, + }, + }, + sellOrders: []dymnstypes.SellOrder{ + s.newDymNameSellOrder("name1").WithExpiry(1).WithMinPrice(1).Build(), + s.newDymNameSellOrder("name2").WithExpiry(2).WithMinPrice(2).Build(), + s.newAliasSellOrder("alias1").WithExpiry(3).WithMinPrice(3).Build(), + s.newAliasSellOrder("alias2").WithExpiry(4).WithMinPrice(4).Build(), + }, + wantBroken: true, + wantMsgContains: "sell order expiration mismatch", + }, + } + for _, tt := range tests { + s.Run(tt.name, func() { + s.RefreshContext() + + err := s.dymNsKeeper.SetActiveSellOrdersExpiration(s.ctx, &dymnstypes.ActiveSellOrdersExpiration{ + Records: tt.nameASoe, + }, dymnstypes.TypeName) + s.Require().NoError(err) + err = s.dymNsKeeper.SetActiveSellOrdersExpiration(s.ctx, &dymnstypes.ActiveSellOrdersExpiration{ + Records: tt.aliasASoe, + }, dymnstypes.TypeAlias) + s.Require().NoError(err) + + for _, so := range tt.sellOrders { + err := s.dymNsKeeper.SetSellOrder(s.ctx, so) + s.Require().NoError(err) + } + + msg, broken := dymnskeeper.SellOrderExpirationInvariant(s.dymNsKeeper)(s.ctx) + if tt.wantBroken { + s.Require().True(broken) + s.Require().NotEmpty(tt.wantMsgContains, "bad setup") + s.Require().Contains(msg, tt.wantMsgContains) + } else { + s.Require().False(broken) + } + }) + } +} diff --git a/x/dymns/keeper/keeper.go b/x/dymns/keeper/keeper.go new file mode 100644 index 000000000..4c50eacef --- /dev/null +++ b/x/dymns/keeper/keeper.go @@ -0,0 +1,53 @@ +package keeper + +import ( + "fmt" + + "github.com/cosmos/cosmos-sdk/codec" + storetypes "github.com/cosmos/cosmos-sdk/store/types" + paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" + + "github.com/cometbft/cometbft/libs/log" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// Keeper of the DymNS store +type Keeper struct { + authority string // authority is the x/gov module account + + cdc codec.BinaryCodec + storeKey storetypes.StoreKey + paramStore paramtypes.Subspace + bankKeeper dymnstypes.BankKeeper + rollappKeeper dymnstypes.RollAppKeeper +} + +// NewKeeper returns a new instance of the DymNS keeper +func NewKeeper( + cdc codec.BinaryCodec, + key storetypes.StoreKey, + ps paramtypes.Subspace, + bk dymnstypes.BankKeeper, + rk dymnstypes.RollAppKeeper, + authority string, +) Keeper { + // set KeyTable if it has not already been set + if !ps.HasKeyTable() { + ps = ps.WithKeyTable(dymnstypes.ParamKeyTable()) + } + return Keeper{ + authority: authority, + + cdc: cdc, + storeKey: key, + paramStore: ps, + bankKeeper: bk, + rollappKeeper: rk, + } +} + +// Logger returns a module-specific logger. +func (k *Keeper) Logger(ctx sdk.Context) log.Logger { + return ctx.Logger().With("module", fmt.Sprintf("x/%s", dymnstypes.ModuleName)) +} diff --git a/x/dymns/keeper/keeper_suite_test.go b/x/dymns/keeper/keeper_suite_test.go new file mode 100644 index 000000000..a87c9c29d --- /dev/null +++ b/x/dymns/keeper/keeper_suite_test.go @@ -0,0 +1,785 @@ +package keeper_test + +import ( + "slices" + "sort" + "testing" + "time" + + "github.com/dymensionxyz/sdk-utils/utils/uptr" + + tmdb "github.com/cometbft/cometbft-db" + "github.com/cometbft/cometbft/libs/log" + tmproto "github.com/cometbft/cometbft/proto/tendermint/types" + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/store" + "github.com/cosmos/cosmos-sdk/store/prefix" + storetypes "github.com/cosmos/cosmos-sdk/store/types" + authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + typesparams "github.com/cosmos/cosmos-sdk/x/params/types" + "github.com/dymensionxyz/dymension/v3/app/params" + + sdkmath "cosmossdk.io/math" + + sdk "github.com/cosmos/cosmos-sdk/types" + dymnskeeper "github.com/dymensionxyz/dymension/v3/x/dymns/keeper" + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" + rollappkeeper "github.com/dymensionxyz/dymension/v3/x/rollapp/keeper" + rollapptypes "github.com/dymensionxyz/dymension/v3/x/rollapp/types" + "github.com/stretchr/testify/suite" +) + +func init() { + params.SetAddressPrefixes() +} + +type KeeperTestSuite struct { + suite.Suite + + savedCtx sdk.Context + ctx sdk.Context + + chainId string + now time.Time + + cdc codec.BinaryCodec + + dymNsKeeper dymnskeeper.Keeper + rollAppKeeper rollappkeeper.Keeper + bankKeeper dymnstypes.BankKeeper + + rollappStoreKey storetypes.StoreKey +} + +func TestKeeperTestSuite(t *testing.T) { + suite.Run(t, new(KeeperTestSuite)) +} + +func (s *KeeperTestSuite) SetupSuite() { +} + +func (s *KeeperTestSuite) SetupTest() { + var ctx sdk.Context + + var cdc codec.BinaryCodec + + var dk dymnskeeper.Keeper + var bk dymnstypes.BankKeeper + var rk *rollappkeeper.Keeper + + var rollappStoreKey storetypes.StoreKey + + { + // initialization + dymNsStoreKey := sdk.NewKVStoreKey(dymnstypes.StoreKey) + dymNsMemStoreKey := storetypes.NewMemoryStoreKey(dymnstypes.MemStoreKey) + + authStoreKey := sdk.NewKVStoreKey(authtypes.StoreKey) + + bankStoreKey := sdk.NewKVStoreKey(banktypes.StoreKey) + + rollappStoreKey = sdk.NewKVStoreKey(rollapptypes.StoreKey) + rollappMemStoreKey := storetypes.NewMemoryStoreKey(rollapptypes.MemStoreKey) + + db := tmdb.NewMemDB() + stateStore := store.NewCommitMultiStore(db) + stateStore.MountStoreWithDB(dymNsStoreKey, storetypes.StoreTypeIAVL, db) + stateStore.MountStoreWithDB(dymNsMemStoreKey, storetypes.StoreTypeMemory, nil) + stateStore.MountStoreWithDB(authStoreKey, storetypes.StoreTypeIAVL, db) + stateStore.MountStoreWithDB(bankStoreKey, storetypes.StoreTypeIAVL, db) + stateStore.MountStoreWithDB(rollappStoreKey, storetypes.StoreTypeIAVL, db) + stateStore.MountStoreWithDB(rollappMemStoreKey, storetypes.StoreTypeMemory, nil) + s.Require().NoError(stateStore.LoadLatestVersion()) + + registry := codectypes.NewInterfaceRegistry() + cdc = codec.NewProtoCodec(registry) + + dymNSParamsSubspace := typesparams.NewSubspace(cdc, + dymnstypes.Amino, + dymNsStoreKey, + dymNsMemStoreKey, + "DymNSParams", + ) + + rollappParamsSubspace := typesparams.NewSubspace(cdc, + rollapptypes.Amino, + rollappStoreKey, + rollappMemStoreKey, + "RollappParams", + ) + + authKeeper := authkeeper.NewAccountKeeper( + cdc, + authStoreKey, + authtypes.ProtoBaseAccount, + map[string][]string{ + banktypes.ModuleName: {authtypes.Minter, authtypes.Burner}, + dymnstypes.ModuleName: {authtypes.Minter, authtypes.Burner}, + }, + params.AccountAddressPrefix, + authtypes.NewModuleAddress(govtypes.ModuleName).String(), + ) + authtypes.RegisterInterfaces(registry) + + bk = bankkeeper.NewBaseKeeper( + cdc, + bankStoreKey, + authKeeper, + map[string]bool{}, + authtypes.NewModuleAddress(govtypes.ModuleName).String(), + ) + banktypes.RegisterInterfaces(registry) + + rk = rollappkeeper.NewKeeper( + cdc, + rollappStoreKey, + rollappParamsSubspace, + nil, nil, nil, + ) + + dk = dymnskeeper.NewKeeper(cdc, + dymNsStoreKey, + dymNSParamsSubspace, + bk, + rk, + authtypes.NewModuleAddress(govtypes.ModuleName).String(), + ) + + ctx = sdk.NewContext(stateStore, tmproto.Header{}, false, log.NewNopLogger()) + + s.Require().NoError(dk.SetParams(ctx, dymnstypes.DefaultParams())) + } + + const chainId = "dymension_1100-1" + + // set + s.chainId = chainId + s.now = time.Now().UTC() + s.savedCtx = sdk.Context{} + s.ctx = ctx.WithBlockTime(s.now).WithChainID(chainId) + s.cdc = cdc + s.dymNsKeeper = dk + s.rollAppKeeper = *rk + s.bankKeeper = bk + s.rollappStoreKey = rollappStoreKey + + // custom + s.updateModuleParams(func(moduleParams dymnstypes.Params) dymnstypes.Params { + moduleParams.Chains.AliasesOfChainIds = nil + // force enable trading + moduleParams.Misc.EnableTradingName = true + moduleParams.Misc.EnableTradingAlias = true + + return moduleParams + }) + + // others + + s.SaveCurrentContext() +} + +func (s *KeeperTestSuite) AfterTest(_, _ string) { +} + +// SaveCurrentContext saves the current context and convert current context into a branch context. +// This is useful when you want to set up a context and reuse multiple times. +// This is less expensive than call SetupTest. +func (s *KeeperTestSuite) SaveCurrentContext() { + s.savedCtx = s.ctx + s.RefreshContext() +} + +// RefreshContext clear any change to the current context and use a new copy of the saved context. +func (s *KeeperTestSuite) RefreshContext() { + if s.savedCtx.ChainID() == "" { + panic("saved context not set") + } + s.ctx, _ = s.savedCtx.CacheContext() + if gasMeter := s.ctx.GasMeter(); gasMeter != nil { + gasMeter.RefundGas(gasMeter.GasConsumed(), "reset gas meter") + } + if blockGasMeter := s.ctx.BlockGasMeter(); blockGasMeter != nil { + blockGasMeter.RefundGas(blockGasMeter.GasConsumed(), "reset block gas meter") + } +} + +func (s *KeeperTestSuite) priceDenom() string { + return s.dymNsKeeper.GetParams(s.ctx).Price.PriceDenom +} + +func (s *KeeperTestSuite) mintToModuleAccount(amount int64) { + err := s.bankKeeper.MintCoins(s.ctx, + dymnstypes.ModuleName, + sdk.Coins{sdk.NewCoin(s.priceDenom(), sdk.NewInt(amount))}, + ) + s.Require().NoError(err) +} + +func (s *KeeperTestSuite) mintToModuleAccount2(amount sdkmath.Int) { + err := s.bankKeeper.MintCoins(s.ctx, + dymnstypes.ModuleName, + sdk.Coins{sdk.NewCoin(s.priceDenom(), amount)}, + ) + s.Require().NoError(err) +} + +func (s *KeeperTestSuite) mintToAccount(bech32Account string, amount int64) { + s.mintToModuleAccount(amount) + err := s.bankKeeper.SendCoinsFromModuleToAccount(s.ctx, + dymnstypes.ModuleName, + sdk.MustAccAddressFromBech32(bech32Account), + sdk.Coins{sdk.NewCoin(s.priceDenom(), sdk.NewInt(amount))}, + ) + s.Require().NoError(err) +} + +func (s *KeeperTestSuite) mintToAccount2(bech32Account string, amount sdkmath.Int) { + s.mintToModuleAccount2(amount) + err := s.bankKeeper.SendCoinsFromModuleToAccount(s.ctx, + dymnstypes.ModuleName, + sdk.MustAccAddressFromBech32(bech32Account), + sdk.Coins{sdk.NewCoin(s.priceDenom(), amount)}, + ) + s.Require().NoError(err) +} + +func (s *KeeperTestSuite) balance(bech32Account string) int64 { + return s.bankKeeper.GetBalance(s.ctx, + sdk.MustAccAddressFromBech32(bech32Account), + s.priceDenom(), + ).Amount.Int64() +} + +func (s *KeeperTestSuite) balance2(bech32Account string) sdkmath.Int { + return s.bankKeeper.GetBalance(s.ctx, + sdk.MustAccAddressFromBech32(bech32Account), + s.priceDenom(), + ).Amount +} + +func (s *KeeperTestSuite) moduleBalance() int64 { + return s.balance(dymNsModuleAccAddr.String()) +} + +func (s *KeeperTestSuite) moduleBalance2() sdkmath.Int { + return s.balance2(dymNsModuleAccAddr.String()) +} + +func (s *KeeperTestSuite) persistRollApp(ras ...rollapp) { + for _, ra := range ras { + s.rollAppKeeper.SetRollapp(s.ctx, rollapptypes.Rollapp{ + RollappId: ra.rollAppId, + Owner: ra.owner, + Bech32Prefix: ra.bech32, + }) + + if ra.alias != "" { + if len(ra.aliases) == 0 { + panic("must provide aliases if alias is set") + } else if !slices.Contains(ra.aliases, ra.alias) { + panic("alias must be in aliases") + } + } + + for _, alias := range ra.aliases { + err := s.dymNsKeeper.SetAliasForRollAppId(s.ctx, ra.rollAppId, alias) + s.Require().NoError(err) + } + } +} + +// pureSetRollApp persists a rollapp without any side effects and all checking was skipped. +// Used to persist some invalid rollapp for testing. +func (s *KeeperTestSuite) pureSetRollApp(ra rollapptypes.Rollapp) { + _store := prefix.NewStore(s.ctx.KVStore(s.rollappStoreKey), rollapptypes.KeyPrefix(rollapptypes.RollappKeyPrefix)) + b := s.cdc.MustMarshal(&ra) + _store.Set(rollapptypes.RollappKey( + ra.RollappId, + ), b) + + s.Require().True(s.dymNsKeeper.IsRollAppId(s.ctx, ra.RollappId)) +} + +func (s *KeeperTestSuite) moduleParams() dymnstypes.Params { + return s.dymNsKeeper.GetParams(s.ctx) +} + +func (s *KeeperTestSuite) updateModuleParams(f func(dymnstypes.Params) dymnstypes.Params) { + moduleParams := s.moduleParams() + moduleParams = f(moduleParams) + err := s.dymNsKeeper.SetParams(s.ctx, moduleParams) + s.Require().NoError(err) +} + +func (s *KeeperTestSuite) setBuyOrderWithFunctionsAfter(buyOrder dymnstypes.BuyOrder) { + err := s.dymNsKeeper.SetBuyOrder(s.ctx, buyOrder) + s.Require().NoError(err) + + err = s.dymNsKeeper.AddReverseMappingAssetIdToBuyOrder(s.ctx, + buyOrder.AssetId, buyOrder.AssetType, buyOrder.Id, + ) + s.Require().NoError(err) +} + +func (s *KeeperTestSuite) setDymNameWithFunctionsAfter(dymName dymnstypes.DymName) { + s.Require().NoError(s.dymNsKeeper.SetDymName(s.ctx, dymName)) + s.Require().NoError(s.dymNsKeeper.AfterDymNameOwnerChanged(s.ctx, dymName.Name)) + s.Require().NoError(s.dymNsKeeper.AfterDymNameConfigChanged(s.ctx, dymName.Name)) +} + +func (s *KeeperTestSuite) requireDymNameList(dymNames []dymnstypes.DymName, wantNames []string) { + var gotNames []string + for _, dymName := range dymNames { + gotNames = append(gotNames, dymName.Name) + } + + sort.Strings(gotNames) + sort.Strings(wantNames) + + if len(wantNames) == 0 { + wantNames = nil + } + + s.Require().Equal(wantNames, gotNames) +} + +func (s *KeeperTestSuite) coin(amount int64) sdk.Coin { + return sdk.NewCoin(s.priceDenom(), sdk.NewInt(amount)) +} + +// + +type rollapp struct { + rollAppId string + owner string + bech32 string + alias string + aliases []string +} + +func newRollApp(rollAppId string) *rollapp { + return &rollapp{ + rollAppId: rollAppId, + owner: testAddr(0).bech32(), + bech32: "", + aliases: nil, + } +} + +func (r *rollapp) WithOwner(owner string) *rollapp { + r.owner = owner + return r +} + +func (r *rollapp) WithBech32(bech32 string) *rollapp { + r.bech32 = bech32 + return r +} + +func (r *rollapp) WithAlias(alias string) *rollapp { + r.aliases = append(r.aliases, alias) + if r.alias == "" { + r.alias = alias + } + return r +} + +// + +type dymNameBuilder struct { + name string + owner string + controller string + expireAt int64 + configs []dymnstypes.DymNameConfig +} + +func newDN(name, owner string) *dymNameBuilder { + return &dymNameBuilder{ + name: name, + owner: owner, + controller: owner, + expireAt: time.Now().Unix() + 10, + configs: nil, + } +} + +func (m *dymNameBuilder) exp(now time.Time, offset int64) *dymNameBuilder { + m.expireAt = now.Unix() + offset + return m +} + +func (m *dymNameBuilder) cfgN(chainId, subName, resolveTo string) *dymNameBuilder { + m.configs = append(m.configs, dymnstypes.DymNameConfig{ + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: chainId, + Path: subName, + Value: resolveTo, + }) + return m +} + +func (m *dymNameBuilder) build() dymnstypes.DymName { + return dymnstypes.DymName{ + Name: m.name, + Owner: m.owner, + Controller: m.controller, + ExpireAt: m.expireAt, + Configs: m.configs, + } +} + +func (m *dymNameBuilder) buildSlice() []dymnstypes.DymName { + return []dymnstypes.DymName{m.build()} +} + +// + +type reqRollApp struct { + s *KeeperTestSuite + rollAppId string +} + +func (s *KeeperTestSuite) requireRollApp(rollAppId string) *reqRollApp { + return &reqRollApp{ + s: s, + rollAppId: rollAppId, + } +} + +func (m reqRollApp) HasAlias(aliases ...string) { + if len(aliases) == 0 { + panic("must provide at least one alias") + } + for _, alias := range aliases { + rollAppId, found := m.s.dymNsKeeper.GetRollAppIdByAlias(m.s.ctx, alias) + m.s.Require().True(found) + m.s.Require().Equal(m.rollAppId, rollAppId) + } +} + +func (m reqRollApp) HasAliasesWithOrder(aliases ...string) { + if len(aliases) < 2 { + panic("must provide at least two aliases") + } + existingAliases := m.s.dymNsKeeper.GetAliasesOfRollAppId(m.s.ctx, m.rollAppId) + m.s.Require().Equal(aliases, existingAliases) +} + +func (m reqRollApp) HasOnlyAlias(alias string) { + list := m.s.dymNsKeeper.GetAliasesOfRollAppId(m.s.ctx, m.rollAppId) + m.s.Require().Len(list, 1) + m.s.Require().Equal(alias, list[0]) +} + +func (m reqRollApp) HasNoAlias() { + alias, found := m.s.dymNsKeeper.GetAliasByRollAppId(m.s.ctx, m.rollAppId) + m.s.Require().Falsef(found, "got: %v", m.s.dymNsKeeper.GetAliasesOfRollAppId(m.s.ctx, m.rollAppId)) + m.s.Require().Empty(alias) +} + +// + +type reqAlias struct { + s *KeeperTestSuite + alias string +} + +func (s *KeeperTestSuite) requireAlias(alias string) *reqAlias { + return &reqAlias{ + s: s, + alias: alias, + } +} + +func (m reqAlias) NotInUse() { + gotRollAppId, found := m.s.dymNsKeeper.GetRollAppIdByAlias(m.s.ctx, m.alias) + m.s.Require().False(found, "got: %s", gotRollAppId) + m.s.Require().Empty(gotRollAppId) +} + +func (m reqAlias) LinkedToRollApp(rollAppId string) { + gotRollAppId, found := m.s.dymNsKeeper.GetRollAppIdByAlias(m.s.ctx, m.alias) + m.s.Require().True(found) + m.s.Require().Equal(rollAppId, gotRollAppId) +} + +func (m reqAlias) mustHaveActiveSO() reqAlias { + so := m.s.dymNsKeeper.GetSellOrder(m.s.ctx, m.alias, dymnstypes.TypeAlias) + m.s.Require().NotNil(so) + return m +} + +func (m reqAlias) noActiveSO() reqAlias { + so := m.s.dymNsKeeper.GetSellOrder(m.s.ctx, m.alias, dymnstypes.TypeAlias) + m.s.Require().Nil(so) + return m +} + +// + +type sellOrderBuilder struct { + s *KeeperTestSuite + // + assetId string + assetType dymnstypes.AssetType + expiry int64 + minPrice int64 + sellPrice *int64 + // bid + bidder string + bidAmount int64 + params []string +} + +func (s *KeeperTestSuite) newDymNameSellOrder(dymName string) *sellOrderBuilder { + return s.newSellOrder(dymName, dymnstypes.TypeName) +} + +func (s *KeeperTestSuite) newAliasSellOrder(alias string) *sellOrderBuilder { + return s.newSellOrder(alias, dymnstypes.TypeAlias) +} + +func (s *KeeperTestSuite) newSellOrder(assetId string, assetType dymnstypes.AssetType) *sellOrderBuilder { + return &sellOrderBuilder{ + s: s, + assetId: assetId, + assetType: assetType, + expiry: s.now.Add(time.Second).Unix(), + minPrice: 0, + } +} + +func (b *sellOrderBuilder) WithMinPrice(minPrice int64) *sellOrderBuilder { + b.minPrice = minPrice + return b +} + +func (b *sellOrderBuilder) Expired() *sellOrderBuilder { + return b.WithExpiry(b.s.now.Add(-time.Second).Unix()) +} + +func (b *sellOrderBuilder) WithExpiry(epoch int64) *sellOrderBuilder { + b.expiry = epoch + return b +} + +func (b *sellOrderBuilder) WithSellPrice(sellPrice int64) *sellOrderBuilder { + b.sellPrice = &sellPrice + return b +} + +func (b *sellOrderBuilder) WithDymNameBid(bidder string, bidAmount int64) *sellOrderBuilder { + b.bidder = bidder + b.bidAmount = bidAmount + return b +} + +func (b *sellOrderBuilder) WithAliasBid(bidder string, bidAmount int64, rollAppId string) *sellOrderBuilder { + b.bidder = bidder + b.bidAmount = bidAmount + b.params = []string{rollAppId} + return b +} + +func (b *sellOrderBuilder) BuildP() *dymnstypes.SellOrder { + so := b.Build() + return &so +} + +func (b *sellOrderBuilder) Build() dymnstypes.SellOrder { + so := dymnstypes.SellOrder{ + AssetId: b.assetId, + AssetType: b.assetType, + ExpireAt: b.expiry, + MinPrice: b.s.coin(b.minPrice), + SellPrice: nil, + HighestBid: nil, + } + + if b.sellPrice != nil { + so.SellPrice = uptr.To(b.s.coin(*b.sellPrice)) + } + + if b.bidder != "" { + so.HighestBid = &dymnstypes.SellOrderBid{ + Bidder: b.bidder, + Price: b.s.coin(b.bidAmount), + Params: b.params, + } + } + + return so +} + +// + +type buyOrderBuilder struct { + s *KeeperTestSuite + // + id string + assetId string + assetType dymnstypes.AssetType + buyer string + offerPrice int64 + params []string +} + +func (s *KeeperTestSuite) newAliasBuyOrder(buyer, alias, rollAppId string) *buyOrderBuilder { + ob := s.newBuyOrder(buyer, alias, dymnstypes.TypeAlias) + ob.params = []string{rollAppId} + return ob +} + +func (s *KeeperTestSuite) newBuyOrder(buyer, assetId string, assetType dymnstypes.AssetType) *buyOrderBuilder { + return &buyOrderBuilder{ + s: s, + assetId: assetId, + assetType: assetType, + buyer: buyer, + offerPrice: 1, + params: nil, + } +} + +func (b *buyOrderBuilder) WithID(id string) *buyOrderBuilder { + b.id = id + return b +} + +func (b *buyOrderBuilder) WithOfferPrice(p int64) *buyOrderBuilder { + b.offerPrice = p + return b +} + +func (b *buyOrderBuilder) BuildP() *dymnstypes.BuyOrder { + bo := b.Build() + return &bo +} + +func (b *buyOrderBuilder) Build() dymnstypes.BuyOrder { + bo := dymnstypes.BuyOrder{ + Id: b.id, + AssetId: b.assetId, + AssetType: b.assetType, + Params: b.params, + Buyer: b.buyer, + OfferPrice: b.s.coin(b.offerPrice), + } + + return bo +} + +// + +type reqConfiguredAddr struct { + s *KeeperTestSuite + cfgAddr string +} + +func (s *KeeperTestSuite) requireConfiguredAddress(cfgAddr string) *reqConfiguredAddr { + return &reqConfiguredAddr{ + s: s, + cfgAddr: cfgAddr, + } +} + +func (m reqConfiguredAddr) mappedDymNames(names ...string) { + if len(names) == 0 { + panic("must provide at least one name") + } + + dymNames, err := m.s.dymNsKeeper.GetDymNamesContainsConfiguredAddress(m.s.ctx, m.cfgAddr) + m.s.Require().NoError(err) + m.s.requireDymNameList(dymNames, names) +} + +func (m reqConfiguredAddr) notMappedToAnyDymName() { + dymNames, err := m.s.dymNsKeeper.GetDymNamesContainsConfiguredAddress(m.s.ctx, m.cfgAddr) + m.s.Require().NoError(err) + m.s.Require().Empty(dymNames) +} + +// + +type reqFallbackAddr struct { + s *KeeperTestSuite + fbAddr dymnstypes.FallbackAddress +} + +func (s *KeeperTestSuite) requireFallbackAddress(fbAddr dymnstypes.FallbackAddress) *reqFallbackAddr { + return &reqFallbackAddr{ + s: s, + fbAddr: fbAddr, + } +} + +func (m reqFallbackAddr) mappedDymNames(names ...string) { + if len(names) == 0 { + panic("must provide at least one name") + } + + dymNames, err := m.s.dymNsKeeper.GetDymNamesContainsFallbackAddress(m.s.ctx, m.fbAddr) + m.s.Require().NoError(err) + m.s.requireDymNameList(dymNames, names) +} + +func (m reqFallbackAddr) notMappedToAnyDymName() { + dymNames, err := m.s.dymNsKeeper.GetDymNamesContainsFallbackAddress(m.s.ctx, m.fbAddr) + m.s.Require().NoError(err, "got: %v", dymNames) + m.s.Require().Empty(dymNames) +} + +// + +type reqDymNameS struct { + s *KeeperTestSuite + dymName string +} + +func (s *KeeperTestSuite) requireDymName(dymName string) *reqDymNameS { + return &reqDymNameS{ + s: s, + dymName: dymName, + } +} + +func (m reqDymNameS) mustHaveActiveSO() reqDymNameS { + so := m.s.dymNsKeeper.GetSellOrder(m.s.ctx, m.dymName, dymnstypes.TypeName) + m.s.Require().NotNil(so) + return m +} + +func (m reqDymNameS) noActiveSO() reqDymNameS { + so := m.s.dymNsKeeper.GetSellOrder(m.s.ctx, m.dymName, dymnstypes.TypeName) + m.s.Require().Nil(so) + return m +} + +func (m reqDymNameS) mustEquals(another dymnstypes.DymName) reqDymNameS { + dymName := m.s.dymNsKeeper.GetDymName(m.s.ctx, m.dymName) + m.s.Require().NotNil(dymName) + m.s.Require().Equal(another, *dymName) + return m +} + +func (m reqDymNameS) ownerChangedTo(newOwner string) reqDymNameS { + dymName := m.s.dymNsKeeper.GetDymName(m.s.ctx, m.dymName) + m.s.Require().NotNil(dymName) + m.s.Require().Equal(newOwner, dymName.Owner) + m.s.Require().Equal(newOwner, dymName.Controller, "controller must be updated to new owner when owner changed") + m.s.Require().Empty(dymName.Configs, "configs must be cleared when owner changed") + return m +} + +func (m reqDymNameS) expiryEquals(expiry int64) reqDymNameS { + dymName := m.s.dymNsKeeper.GetDymName(m.s.ctx, m.dymName) + m.s.Require().NotNil(dymName) + m.s.Require().Equal(expiry, dymName.ExpireAt) + return m +} diff --git a/x/dymns/keeper/msg_server.go b/x/dymns/keeper/msg_server.go new file mode 100644 index 000000000..66732c9b3 --- /dev/null +++ b/x/dymns/keeper/msg_server.go @@ -0,0 +1,31 @@ +package keeper + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" +) + +var _ dymnstypes.MsgServer = msgServer{} + +type msgServer struct { + Keeper +} + +// NewMsgServerImpl returns an implementation of the MsgServer interface +// for the provided Keeper. +func NewMsgServerImpl(keeper Keeper) dymnstypes.MsgServer { + return &msgServer{Keeper: keeper} +} + +// consumeMinimumGas consumes the minimum gas +// if the consumed gas during tx is less than the minimum gas. +func consumeMinimumGas(ctx sdk.Context, minimumGas uint64, actionName string) { + if minimumGas > 0 { + if consumedGas := ctx.GasMeter().GasConsumed(); consumedGas < minimumGas { + // we only consume the gas that is needed to reach the target minimum gas + gasToConsume := minimumGas - consumedGas + + ctx.GasMeter().ConsumeGas(gasToConsume, actionName) + } + } +} diff --git a/x/dymns/keeper/msg_server_accept_buy_order.go b/x/dymns/keeper/msg_server_accept_buy_order.go new file mode 100644 index 000000000..b241536e3 --- /dev/null +++ b/x/dymns/keeper/msg_server_accept_buy_order.go @@ -0,0 +1,266 @@ +package keeper + +import ( + "context" + + errorsmod "cosmossdk.io/errors" + "github.com/dymensionxyz/gerr-cosmos/gerrc" + + sdk "github.com/cosmos/cosmos-sdk/types" + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" + rollapptypes "github.com/dymensionxyz/dymension/v3/x/rollapp/types" +) + +// AcceptBuyOrder is message handler, +// handles accepting a Buy-Order or raising the amount for negotiation, +// performed by the owner of the asset. +func (k msgServer) AcceptBuyOrder(goCtx context.Context, msg *dymnstypes.MsgAcceptBuyOrder) (*dymnstypes.MsgAcceptBuyOrderResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + if err := msg.ValidateBasic(); err != nil { + return nil, err + } + + // get the Buy-Order record from store + + bo := k.GetBuyOrder(ctx, msg.OrderId) + if bo == nil { + return nil, errorsmod.Wrapf(gerrc.ErrNotFound, "Buy-Order: %s", msg.OrderId) + } + + miscParams := k.MiscParams(ctx) + + var resp *dymnstypes.MsgAcceptBuyOrderResponse + var err error + + // process the Buy-Order based on the asset type + + if bo.AssetType == dymnstypes.TypeName { + resp, err = k.processAcceptBuyOrderWithAssetTypeDymName(ctx, msg, *bo, miscParams) + } else if bo.AssetType == dymnstypes.TypeAlias { + resp, err = k.processAcceptBuyOrderWithAssetTypeAlias(ctx, msg, *bo, miscParams) + } else { + err = errorsmod.Wrapf(gerrc.ErrInvalidArgument, "invalid asset type: %s", bo.AssetType) + } + if err != nil { + return nil, err + } + + // charge protocol fee + consumeMinimumGas(ctx, dymnstypes.OpGasUpdateBuyOrder, "AcceptBuyOrder") + + return resp, nil +} + +// processAcceptBuyOrderWithAssetTypeDymName handles the message handled by AcceptBuyOrder, type Dym-Name. +func (k msgServer) processAcceptBuyOrderWithAssetTypeDymName( + ctx sdk.Context, + msg *dymnstypes.MsgAcceptBuyOrder, offer dymnstypes.BuyOrder, miscParams dymnstypes.MiscParams, +) (*dymnstypes.MsgAcceptBuyOrderResponse, error) { + if !miscParams.EnableTradingName { + return nil, errorsmod.Wrapf(gerrc.ErrFailedPrecondition, "trading of Dym-Name is disabled") + } + + dymName, err := k.validateAcceptBuyOrderWithAssetTypeDymName(ctx, msg, offer) + if err != nil { + return nil, err + } + + var accepted bool + + if msg.MinAccept.IsLT(offer.OfferPrice) { + // this was checked earlier so this won't happen, + // but I keep this here to easier to understand of all-cases of comparison + panic("min-accept is less than offer price") + } else if msg.MinAccept.IsEqual(offer.OfferPrice) { + accepted = true + + // check active SO + sellOrder := k.GetSellOrder(ctx, offer.AssetId, offer.AssetType) + if sellOrder != nil { + return nil, errorsmod.Wrapf(gerrc.ErrPermissionDenied, "must cancel the sell order first") + } + + // take the offer + if err := k.bankKeeper.SendCoinsFromModuleToAccount( + ctx, + dymnstypes.ModuleName, + sdk.MustAccAddressFromBech32(dymName.Owner), + sdk.Coins{offer.OfferPrice}, + ); err != nil { + return nil, err + } + + if err := k.removeBuyOrder(ctx, offer); err != nil { + return nil, err + } + + if err := k.transferDymNameOwnership(ctx, *dymName, offer.Buyer); err != nil { + return nil, err + } + } else { + accepted = false + + offer.CounterpartyOfferPrice = &msg.MinAccept + if err := k.SetBuyOrder(ctx, offer); err != nil { + return nil, err + } + } + + return &dymnstypes.MsgAcceptBuyOrderResponse{ + Accepted: accepted, + }, nil +} + +// validateAcceptBuyOrderWithAssetTypeDymName handles validation for the message handled by AcceptBuyOrder, type Dym-Name. +func (k msgServer) validateAcceptBuyOrderWithAssetTypeDymName( + ctx sdk.Context, + msg *dymnstypes.MsgAcceptBuyOrder, bo dymnstypes.BuyOrder, +) (*dymnstypes.DymName, error) { + dymName := k.GetDymNameWithExpirationCheck(ctx, bo.AssetId) + if dymName == nil { + return nil, errorsmod.Wrapf(gerrc.ErrNotFound, "Dym-Name: %s", bo.AssetId) + } + + if dymName.Owner != msg.Owner { + return nil, errorsmod.Wrapf(gerrc.ErrPermissionDenied, "not the owner of the Dym-Name") + } + + if bo.Buyer == msg.Owner { + return nil, errorsmod.Wrapf(gerrc.ErrPermissionDenied, "cannot accept own offer") + } + + if msg.MinAccept.Denom != bo.OfferPrice.Denom { + return nil, errorsmod.Wrapf( + gerrc.ErrInvalidArgument, + "denom must be the same as the offer price: %s", bo.OfferPrice.Denom, + ) + } + + if msg.MinAccept.IsLT(bo.OfferPrice) { + return nil, errorsmod.Wrapf( + gerrc.ErrInvalidArgument, + "amount must be greater than or equals to the offer price: %s", bo.OfferPrice.Denom, + ) + } + + return dymName, nil +} + +// processAcceptBuyOrderWithAssetTypeAlias handles the message handled by AcceptBuyOrder, type Alias. +func (k msgServer) processAcceptBuyOrderWithAssetTypeAlias( + ctx sdk.Context, + msg *dymnstypes.MsgAcceptBuyOrder, offer dymnstypes.BuyOrder, miscParams dymnstypes.MiscParams, +) (*dymnstypes.MsgAcceptBuyOrderResponse, error) { + if !miscParams.EnableTradingAlias { + return nil, errorsmod.Wrap(gerrc.ErrPermissionDenied, "trading of Alias is disabled") + } + + if k.IsAliasPresentsInParamsAsAliasOrChainId(ctx, offer.AssetId) { + // Please read the `processActiveAliasSellOrders` method (hooks.go) for more information. + + return nil, errorsmod.Wrapf(gerrc.ErrPermissionDenied, + "prohibited to trade aliases which is reserved for chain-id or alias in module params: %s", offer.AssetId, + ) + } + + existingRollAppUsingAlias, err := k.validateAcceptBuyOrderWithAssetTypeAlias(ctx, msg, offer) + if err != nil { + return nil, err + } + + destinationRollAppId := offer.Params[0] + if !k.IsRollAppId(ctx, destinationRollAppId) { + return nil, errorsmod.Wrapf(gerrc.ErrInvalidArgument, "invalid destination Roll-App ID: %s", destinationRollAppId) + } + + var accepted bool + + if msg.MinAccept.IsLT(offer.OfferPrice) { + // this was checked earlier so this won't happen, + // but I keep this here to easier to understand of all-cases of comparison + panic("min-accept is less than offer price") + } else if msg.MinAccept.IsEqual(offer.OfferPrice) { + accepted = true + + // check active SO + sellOrder := k.GetSellOrder(ctx, offer.AssetId, offer.AssetType) + if sellOrder != nil { + return nil, errorsmod.Wrapf(gerrc.ErrPermissionDenied, "must cancel the sell order first") + } + + // take the offer + if err := k.bankKeeper.SendCoinsFromModuleToAccount( + ctx, + dymnstypes.ModuleName, + sdk.MustAccAddressFromBech32(existingRollAppUsingAlias.Owner), + sdk.Coins{offer.OfferPrice}, + ); err != nil { + return nil, err + } + + if err := k.removeBuyOrder(ctx, offer); err != nil { + return nil, err + } + + if err := k.MoveAliasToRollAppId(ctx, + existingRollAppUsingAlias.RollappId, // source Roll-App ID + offer.AssetId, // alias + destinationRollAppId, // destination Roll-App ID + ); err != nil { + return nil, err + } + } else { + accepted = false + + offer.CounterpartyOfferPrice = &msg.MinAccept + if err := k.SetBuyOrder(ctx, offer); err != nil { + return nil, err + } + } + + return &dymnstypes.MsgAcceptBuyOrderResponse{ + Accepted: accepted, + }, nil +} + +// validateAcceptBuyOrderWithAssetTypeAlias handles validation for the message handled by AcceptBuyOrder, type Alias. +func (k msgServer) validateAcceptBuyOrderWithAssetTypeAlias( + ctx sdk.Context, + msg *dymnstypes.MsgAcceptBuyOrder, bo dymnstypes.BuyOrder, +) (*rollapptypes.Rollapp, error) { + existingRollAppIdUsingAlias, found := k.GetRollAppIdByAlias(ctx, bo.AssetId) + if !found { + return nil, errorsmod.Wrapf(gerrc.ErrNotFound, "alias is not in-used: %s", bo.AssetId) + } + + if !k.IsRollAppCreator(ctx, existingRollAppIdUsingAlias, msg.Owner) { + return nil, errorsmod.Wrapf(gerrc.ErrPermissionDenied, "not the owner of the RollApp") + } + + existingRollAppUsingAlias, found := k.rollappKeeper.GetRollapp(ctx, existingRollAppIdUsingAlias) + if !found { + // this can not happen as the previous check already ensures the Roll-App exists + panic("roll-app not found: " + existingRollAppIdUsingAlias) + } + + if bo.Buyer == msg.Owner { + return nil, errorsmod.Wrapf(gerrc.ErrPermissionDenied, "cannot accept own offer") + } + + if msg.MinAccept.Denom != bo.OfferPrice.Denom { + return nil, errorsmod.Wrapf( + gerrc.ErrInvalidArgument, + "denom must be the same as the offer price: %s", bo.OfferPrice.Denom, + ) + } + + if msg.MinAccept.IsLT(bo.OfferPrice) { + return nil, errorsmod.Wrapf( + gerrc.ErrInvalidArgument, + "amount must be greater than or equals to the offer price: %s", bo.OfferPrice.Denom, + ) + } + + return &existingRollAppUsingAlias, nil +} diff --git a/x/dymns/keeper/msg_server_accept_buy_order_test.go b/x/dymns/keeper/msg_server_accept_buy_order_test.go new file mode 100644 index 000000000..e56ac2d30 --- /dev/null +++ b/x/dymns/keeper/msg_server_accept_buy_order_test.go @@ -0,0 +1,1547 @@ +package keeper_test + +import ( + "fmt" + "time" + + sdkmath "cosmossdk.io/math" + + "github.com/dymensionxyz/sdk-utils/utils/uptr" + + "github.com/dymensionxyz/gerr-cosmos/gerrc" + + sdk "github.com/cosmos/cosmos-sdk/types" + dymnskeeper "github.com/dymensionxyz/dymension/v3/x/dymns/keeper" + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" + rollapptypes "github.com/dymensionxyz/dymension/v3/x/rollapp/types" +) + +func (s *KeeperTestSuite) Test_msgServer_AcceptBuyOrder_Type_DymName() { + const minOfferPrice = 5 + + // the number values used in this test will be multiplied by this value + priceMultiplier := sdk.NewInt(1e18) + + minOfferPriceCoin := sdk.NewCoin(s.priceDenom(), sdk.NewInt(minOfferPrice).Mul(priceMultiplier)) + + buyerA := testAddr(1).bech32() + ownerA := testAddr(2).bech32() + anotherOwnerA := testAddr(3).bech32() + + s.updateModuleParams(func(moduleParams dymnstypes.Params) dymnstypes.Params { + moduleParams.Price.MinOfferPrice = minOfferPriceCoin.Amount + // force enable trading + moduleParams.Misc.EnableTradingName = true + moduleParams.Misc.EnableTradingAlias = true + return moduleParams + }) + s.SaveCurrentContext() + + s.Run("reject if message not pass validate basic", func() { + _, err := dymnskeeper.NewMsgServerImpl(s.dymNsKeeper).AcceptBuyOrder(s.ctx, &dymnstypes.MsgAcceptBuyOrder{}) + s.Require().ErrorContains(err, gerrc.ErrInvalidArgument.Error()) + }) + + dymName := &dymnstypes.DymName{ + Name: "a", + Owner: ownerA, + Controller: ownerA, + ExpireAt: s.now.Unix() + 100, + } + + sameDymNameButOwnedByAnother := &dymnstypes.DymName{ + Name: dymName.Name, + Owner: anotherOwnerA, + Controller: anotherOwnerA, + ExpireAt: dymName.ExpireAt, + } + + offer := &dymnstypes.BuyOrder{ + Id: "101", + AssetId: dymName.Name, + AssetType: dymnstypes.TypeName, + Buyer: buyerA, + OfferPrice: minOfferPriceCoin, + } + + tests := []struct { + name string + existingDymName *dymnstypes.DymName + existingOffer *dymnstypes.BuyOrder + buyOrderId string + owner string + minAccept sdk.Coin + originalModuleBalance sdkmath.Int + originalOwnerBalance sdkmath.Int + preRunSetupFunc func(s *KeeperTestSuite) + wantErr bool + wantErrContains string + wantLaterOffer *dymnstypes.BuyOrder + wantLaterDymName *dymnstypes.DymName + wantLaterModuleBalance sdkmath.Int + wantLaterOwnerBalance sdkmath.Int + wantMinConsumeGas sdk.Gas + afterTestFunc func(s *KeeperTestSuite) + }{ + { + name: "pass - can accept offer (match)", + existingDymName: dymName, + existingOffer: offer, + buyOrderId: offer.Id, + owner: dymName.Owner, + minAccept: offer.OfferPrice, + originalModuleBalance: offer.OfferPrice.Amount, + originalOwnerBalance: sdk.NewInt(0), + preRunSetupFunc: nil, + wantErr: false, + wantLaterOffer: nil, + wantLaterDymName: &dymnstypes.DymName{ + Name: dymName.Name, + Owner: offer.Buyer, + Controller: offer.Buyer, + ExpireAt: dymName.ExpireAt, + }, + wantLaterModuleBalance: sdkmath.ZeroInt(), + wantLaterOwnerBalance: offer.OfferPrice.Amount, + wantMinConsumeGas: dymnstypes.OpGasUpdateBuyOrder, + }, + { + name: "pass - after match offer, reverse records of the offer are removed", + existingDymName: dymName, + existingOffer: offer, + buyOrderId: offer.Id, + owner: dymName.Owner, + minAccept: offer.OfferPrice, + originalModuleBalance: offer.OfferPrice.Amount, + originalOwnerBalance: sdk.NewInt(0), + preRunSetupFunc: func(s *KeeperTestSuite) { + key := dymnstypes.DymNameToBuyOrderIdsRvlKey(dymName.Name) + orderIds := s.dymNsKeeper.GenericGetReverseLookupBuyOrderIdsRecord(s.ctx, key) + s.Require().Equal([]string{offer.Id}, orderIds.OrderIds) + + key = dymnstypes.BuyerToOrderIdsRvlKey(sdk.MustAccAddressFromBech32(offer.Buyer)) + orderIds = s.dymNsKeeper.GenericGetReverseLookupBuyOrderIdsRecord(s.ctx, key) + s.Require().Equal([]string{offer.Id}, orderIds.OrderIds) + }, + wantErr: false, + wantLaterOffer: nil, + wantLaterDymName: &dymnstypes.DymName{ + Name: dymName.Name, + Owner: offer.Buyer, + Controller: offer.Buyer, + ExpireAt: dymName.ExpireAt, + }, + wantLaterModuleBalance: sdkmath.ZeroInt(), + wantLaterOwnerBalance: offer.OfferPrice.Amount, + wantMinConsumeGas: dymnstypes.OpGasUpdateBuyOrder, + afterTestFunc: func(s *KeeperTestSuite) { + key := dymnstypes.DymNameToBuyOrderIdsRvlKey(dymName.Name) + orderIds := s.dymNsKeeper.GenericGetReverseLookupBuyOrderIdsRecord(s.ctx, key) + s.Require().Empty(orderIds.OrderIds) + + key = dymnstypes.BuyerToOrderIdsRvlKey(sdk.MustAccAddressFromBech32(offer.Buyer)) + orderIds = s.dymNsKeeper.GenericGetReverseLookupBuyOrderIdsRecord(s.ctx, key) + s.Require().Empty(orderIds.OrderIds) + }, + }, + { + name: "pass - after match offer, reverse records of the Dym-Name are updated", + existingDymName: dymName, + existingOffer: offer, + buyOrderId: offer.Id, + owner: dymName.Owner, + minAccept: offer.OfferPrice, + originalModuleBalance: offer.OfferPrice.Amount, + originalOwnerBalance: sdk.NewInt(0), + preRunSetupFunc: func(s *KeeperTestSuite) { + // reverse record still linked to owner before transaction + key := dymnstypes.ConfiguredAddressToDymNamesIncludeRvlKey(dymName.Owner) + dymNames := s.dymNsKeeper.GenericGetReverseLookupDymNamesRecord(s.ctx, key) + s.Require().Equal([]string{dymName.Name}, dymNames.DymNames) + + key = dymnstypes.FallbackAddressToDymNamesIncludeRvlKey(dymnstypes.FallbackAddress(sdk.MustAccAddressFromBech32(dymName.Owner))) + dymNames = s.dymNsKeeper.GenericGetReverseLookupDymNamesRecord(s.ctx, key) + s.Require().Equal([]string{dymName.Name}, dymNames.DymNames) + + key = dymnstypes.DymNamesOwnedByAccountRvlKey(sdk.MustAccAddressFromBech32(dymName.Owner)) + dymNames = s.dymNsKeeper.GenericGetReverseLookupDymNamesRecord(s.ctx, key) + s.Require().Equal([]string{dymName.Name}, dymNames.DymNames) + + // no reverse record for buyer (the later owner) before transaction + key = dymnstypes.ConfiguredAddressToDymNamesIncludeRvlKey(offer.Buyer) + dymNames = s.dymNsKeeper.GenericGetReverseLookupDymNamesRecord(s.ctx, key) + s.Require().Empty(dymNames.DymNames) + + key = dymnstypes.FallbackAddressToDymNamesIncludeRvlKey(dymnstypes.FallbackAddress(sdk.MustAccAddressFromBech32(offer.Buyer))) + dymNames = s.dymNsKeeper.GenericGetReverseLookupDymNamesRecord(s.ctx, key) + s.Require().Empty(dymNames.DymNames) + + key = dymnstypes.DymNamesOwnedByAccountRvlKey(sdk.MustAccAddressFromBech32(offer.Buyer)) + dymNames = s.dymNsKeeper.GenericGetReverseLookupDymNamesRecord(s.ctx, key) + s.Require().Empty(dymNames.DymNames) + }, + wantErr: false, + wantLaterOffer: nil, + wantLaterDymName: &dymnstypes.DymName{ + Name: dymName.Name, + Owner: offer.Buyer, + Controller: offer.Buyer, + ExpireAt: dymName.ExpireAt, + }, + wantLaterModuleBalance: sdkmath.ZeroInt(), + wantLaterOwnerBalance: offer.OfferPrice.Amount, + wantMinConsumeGas: dymnstypes.OpGasUpdateBuyOrder, + afterTestFunc: func(s *KeeperTestSuite) { + // reverse record to later owner (buyer) are created after transaction + key := dymnstypes.ConfiguredAddressToDymNamesIncludeRvlKey(offer.Buyer) + dymNames := s.dymNsKeeper.GenericGetReverseLookupDymNamesRecord(s.ctx, key) + s.Require().Equal([]string{dymName.Name}, dymNames.DymNames) + + key = dymnstypes.FallbackAddressToDymNamesIncludeRvlKey(dymnstypes.FallbackAddress(sdk.MustAccAddressFromBech32(offer.Buyer))) + dymNames = s.dymNsKeeper.GenericGetReverseLookupDymNamesRecord(s.ctx, key) + s.Require().Equal([]string{dymName.Name}, dymNames.DymNames) + + key = dymnstypes.DymNamesOwnedByAccountRvlKey(sdk.MustAccAddressFromBech32(offer.Buyer)) + dymNames = s.dymNsKeeper.GenericGetReverseLookupDymNamesRecord(s.ctx, key) + s.Require().Equal([]string{dymName.Name}, dymNames.DymNames) + + // reverse record to previous owner are removed after transaction + key = dymnstypes.ConfiguredAddressToDymNamesIncludeRvlKey(dymName.Owner) + dymNames = s.dymNsKeeper.GenericGetReverseLookupDymNamesRecord(s.ctx, key) + s.Require().Empty(dymNames.DymNames) + + key = dymnstypes.FallbackAddressToDymNamesIncludeRvlKey(dymnstypes.FallbackAddress(sdk.MustAccAddressFromBech32(dymName.Owner))) + dymNames = s.dymNsKeeper.GenericGetReverseLookupDymNamesRecord(s.ctx, key) + s.Require().Empty(dymNames.DymNames) + + key = dymnstypes.DymNamesOwnedByAccountRvlKey(sdk.MustAccAddressFromBech32(dymName.Owner)) + dymNames = s.dymNsKeeper.GenericGetReverseLookupDymNamesRecord(s.ctx, key) + s.Require().Empty(dymNames.DymNames) + }, + }, + { + name: "pass - (negotiation) when price not match offer price, raise the counterparty offer price", + existingDymName: dymName, + existingOffer: offer, + buyOrderId: offer.Id, + owner: dymName.Owner, + minAccept: offer.OfferPrice.AddAmount(sdk.NewInt(1)), + originalModuleBalance: sdkmath.OneInt().Mul(priceMultiplier), + originalOwnerBalance: sdkmath.NewInt(2).Mul(priceMultiplier), + preRunSetupFunc: nil, + wantErr: false, + wantLaterOffer: &dymnstypes.BuyOrder{ + Id: offer.Id, + AssetId: offer.AssetId, + AssetType: dymnstypes.TypeName, + Buyer: offer.Buyer, + OfferPrice: offer.OfferPrice, + CounterpartyOfferPrice: func() *sdk.Coin { + coin := offer.OfferPrice.AddAmount(sdk.NewInt(1)) + return &coin + }(), + }, + wantLaterDymName: dymName, + wantLaterModuleBalance: sdkmath.OneInt().Mul(priceMultiplier), + wantLaterOwnerBalance: sdkmath.NewInt(2).Mul(priceMultiplier), + wantMinConsumeGas: dymnstypes.OpGasUpdateBuyOrder, + }, + { + name: "pass - after put negotiation price, reverse records of the offer are preserved", + existingDymName: dymName, + existingOffer: offer, + buyOrderId: offer.Id, + owner: dymName.Owner, + minAccept: offer.OfferPrice.AddAmount(sdk.NewInt(1)), + originalModuleBalance: sdkmath.OneInt().Mul(priceMultiplier), + originalOwnerBalance: sdkmath.NewInt(2).Mul(priceMultiplier), + preRunSetupFunc: func(s *KeeperTestSuite) { + key := dymnstypes.DymNameToBuyOrderIdsRvlKey(dymName.Name) + orderIds := s.dymNsKeeper.GenericGetReverseLookupBuyOrderIdsRecord(s.ctx, key) + s.Require().Equal([]string{offer.Id}, orderIds.OrderIds) + + key = dymnstypes.BuyerToOrderIdsRvlKey(sdk.MustAccAddressFromBech32(offer.Buyer)) + orderIds = s.dymNsKeeper.GenericGetReverseLookupBuyOrderIdsRecord(s.ctx, key) + s.Require().Equal([]string{offer.Id}, orderIds.OrderIds) + }, + wantErr: false, + wantLaterOffer: &dymnstypes.BuyOrder{ + Id: offer.Id, + AssetId: offer.AssetId, + AssetType: dymnstypes.TypeName, + Buyer: offer.Buyer, + OfferPrice: offer.OfferPrice, + CounterpartyOfferPrice: func() *sdk.Coin { + coin := offer.OfferPrice.AddAmount(sdk.NewInt(1)) + return &coin + }(), + }, + wantLaterModuleBalance: sdkmath.OneInt().Mul(priceMultiplier), + wantLaterOwnerBalance: sdkmath.NewInt(2).Mul(priceMultiplier), + wantMinConsumeGas: dymnstypes.OpGasUpdateBuyOrder, + afterTestFunc: func(s *KeeperTestSuite) { + key := dymnstypes.DymNameToBuyOrderIdsRvlKey(dymName.Name) + orderIds := s.dymNsKeeper.GenericGetReverseLookupBuyOrderIdsRecord(s.ctx, key) + s.Require().Equal([]string{offer.Id}, orderIds.OrderIds) + + key = dymnstypes.BuyerToOrderIdsRvlKey(sdk.MustAccAddressFromBech32(offer.Buyer)) + orderIds = s.dymNsKeeper.GenericGetReverseLookupBuyOrderIdsRecord(s.ctx, key) + s.Require().Equal([]string{offer.Id}, orderIds.OrderIds) + }, + }, + { + name: "pass - after put negotiation price, reverse records of the Dym-Name are preserved", + existingDymName: dymName, + existingOffer: offer, + buyOrderId: offer.Id, + owner: dymName.Owner, + minAccept: offer.OfferPrice.AddAmount(sdk.NewInt(1)), + originalModuleBalance: sdkmath.OneInt().Mul(priceMultiplier), + originalOwnerBalance: sdkmath.NewInt(2).Mul(priceMultiplier), + preRunSetupFunc: func(s *KeeperTestSuite) { + key := dymnstypes.ConfiguredAddressToDymNamesIncludeRvlKey(dymName.Owner) + dymNames := s.dymNsKeeper.GenericGetReverseLookupDymNamesRecord(s.ctx, key) + s.Require().Equal([]string{dymName.Name}, dymNames.DymNames) + + key = dymnstypes.FallbackAddressToDymNamesIncludeRvlKey(dymnstypes.FallbackAddress(sdk.MustAccAddressFromBech32(dymName.Owner))) + dymNames = s.dymNsKeeper.GenericGetReverseLookupDymNamesRecord(s.ctx, key) + s.Require().Equal([]string{dymName.Name}, dymNames.DymNames) + + key = dymnstypes.DymNamesOwnedByAccountRvlKey(sdk.MustAccAddressFromBech32(dymName.Owner)) + dymNames = s.dymNsKeeper.GenericGetReverseLookupDymNamesRecord(s.ctx, key) + s.Require().Equal([]string{dymName.Name}, dymNames.DymNames) + + key = dymnstypes.ConfiguredAddressToDymNamesIncludeRvlKey(offer.Buyer) + dymNames = s.dymNsKeeper.GenericGetReverseLookupDymNamesRecord(s.ctx, key) + s.Require().Empty(dymNames.DymNames) + + key = dymnstypes.FallbackAddressToDymNamesIncludeRvlKey(dymnstypes.FallbackAddress(sdk.MustAccAddressFromBech32(offer.Buyer))) + dymNames = s.dymNsKeeper.GenericGetReverseLookupDymNamesRecord(s.ctx, key) + s.Require().Empty(dymNames.DymNames) + + key = dymnstypes.DymNamesOwnedByAccountRvlKey(sdk.MustAccAddressFromBech32(offer.Buyer)) + dymNames = s.dymNsKeeper.GenericGetReverseLookupDymNamesRecord(s.ctx, key) + s.Require().Empty(dymNames.DymNames) + }, + wantErr: false, + wantLaterOffer: &dymnstypes.BuyOrder{ + Id: offer.Id, + AssetId: offer.AssetId, + AssetType: dymnstypes.TypeName, + Buyer: offer.Buyer, + OfferPrice: offer.OfferPrice, + CounterpartyOfferPrice: func() *sdk.Coin { + coin := offer.OfferPrice.AddAmount(sdk.NewInt(1)) + return &coin + }(), + }, + wantLaterModuleBalance: sdkmath.OneInt().Mul(priceMultiplier), + wantLaterOwnerBalance: sdkmath.NewInt(2).Mul(priceMultiplier), + wantMinConsumeGas: dymnstypes.OpGasUpdateBuyOrder, + afterTestFunc: func(s *KeeperTestSuite) { + key := dymnstypes.ConfiguredAddressToDymNamesIncludeRvlKey(dymName.Owner) + dymNames := s.dymNsKeeper.GenericGetReverseLookupDymNamesRecord(s.ctx, key) + s.Require().Equal([]string{dymName.Name}, dymNames.DymNames) + + key = dymnstypes.FallbackAddressToDymNamesIncludeRvlKey(dymnstypes.FallbackAddress(sdk.MustAccAddressFromBech32(dymName.Owner))) + dymNames = s.dymNsKeeper.GenericGetReverseLookupDymNamesRecord(s.ctx, key) + s.Require().Equal([]string{dymName.Name}, dymNames.DymNames) + + key = dymnstypes.DymNamesOwnedByAccountRvlKey(sdk.MustAccAddressFromBech32(dymName.Owner)) + dymNames = s.dymNsKeeper.GenericGetReverseLookupDymNamesRecord(s.ctx, key) + s.Require().Equal([]string{dymName.Name}, dymNames.DymNames) + + key = dymnstypes.ConfiguredAddressToDymNamesIncludeRvlKey(offer.Buyer) + dymNames = s.dymNsKeeper.GenericGetReverseLookupDymNamesRecord(s.ctx, key) + s.Require().Empty(dymNames.DymNames) + + key = dymnstypes.FallbackAddressToDymNamesIncludeRvlKey(dymnstypes.FallbackAddress(sdk.MustAccAddressFromBech32(offer.Buyer))) + dymNames = s.dymNsKeeper.GenericGetReverseLookupDymNamesRecord(s.ctx, key) + s.Require().Empty(dymNames.DymNames) + + key = dymnstypes.DymNamesOwnedByAccountRvlKey(sdk.MustAccAddressFromBech32(offer.Buyer)) + dymNames = s.dymNsKeeper.GenericGetReverseLookupDymNamesRecord(s.ctx, key) + s.Require().Empty(dymNames.DymNames) + }, + }, + { + name: "fail - can NOT accept offer when trading Dym-Name is disabled", + existingDymName: dymName, + existingOffer: offer, + buyOrderId: offer.Id, + owner: dymName.Owner, + minAccept: offer.OfferPrice, + originalModuleBalance: offer.OfferPrice.Amount, + originalOwnerBalance: sdk.NewInt(0), + preRunSetupFunc: func(s *KeeperTestSuite) { + s.updateModuleParams(func(moduleParams dymnstypes.Params) dymnstypes.Params { + moduleParams.Misc.EnableTradingName = false + return moduleParams + }) + }, + wantErr: true, + wantErrContains: "trading of Dym-Name is disabled", + wantLaterOffer: offer, + wantLaterDymName: dymName, + wantLaterModuleBalance: offer.OfferPrice.Amount, + wantLaterOwnerBalance: sdk.NewInt(0), + wantMinConsumeGas: 1, + }, + { + name: "fail - offer not found", + existingDymName: dymName, + existingOffer: nil, + buyOrderId: "101", + owner: ownerA, + minAccept: minOfferPriceCoin, + originalModuleBalance: sdkmath.NewInt(1).Mul(priceMultiplier), + originalOwnerBalance: sdkmath.NewInt(2).Mul(priceMultiplier), + wantErr: true, + wantErrContains: "Buy-Order: 101: not found", + wantLaterOffer: nil, + wantLaterDymName: dymName, + wantLaterModuleBalance: sdkmath.OneInt().Mul(priceMultiplier), + wantLaterOwnerBalance: sdkmath.NewInt(2).Mul(priceMultiplier), + wantMinConsumeGas: 1, + }, + { + name: "fail - offer not found", + existingDymName: dymName, + existingOffer: offer, + buyOrderId: "10673264823", + owner: ownerA, + minAccept: minOfferPriceCoin, + originalModuleBalance: sdkmath.NewInt(1).Mul(priceMultiplier), + originalOwnerBalance: sdkmath.NewInt(2).Mul(priceMultiplier), + wantErr: true, + wantErrContains: "Buy-Order: 10673264823: not found", + wantLaterOffer: offer, + wantLaterDymName: dymName, + wantLaterModuleBalance: sdkmath.OneInt().Mul(priceMultiplier), + wantLaterOwnerBalance: sdkmath.NewInt(2).Mul(priceMultiplier), + wantMinConsumeGas: 1, + }, + { + name: "fail - Dym-Name not found", + existingDymName: nil, + existingOffer: offer, + buyOrderId: offer.Id, + owner: ownerA, + minAccept: offer.OfferPrice, + originalModuleBalance: sdkmath.NewInt(1).Mul(priceMultiplier), + originalOwnerBalance: sdkmath.NewInt(2).Mul(priceMultiplier), + wantErr: true, + wantErrContains: fmt.Sprintf("Dym-Name: %s: not found", offer.AssetId), + wantLaterOffer: offer, + wantLaterDymName: nil, + wantLaterModuleBalance: sdkmath.OneInt().Mul(priceMultiplier), + wantLaterOwnerBalance: sdkmath.NewInt(2).Mul(priceMultiplier), + wantMinConsumeGas: 1, + }, + { + name: "fail - expired Dym-Name", + existingDymName: func() *dymnstypes.DymName { + return &dymnstypes.DymName{ + Name: dymName.Name, + Owner: dymName.Owner, + Controller: dymName.Controller, + ExpireAt: s.now.Unix() - 1, + } + }(), + existingOffer: offer, + buyOrderId: offer.Id, + owner: dymName.Owner, + minAccept: offer.OfferPrice, + originalModuleBalance: sdkmath.NewInt(1).Mul(priceMultiplier), + originalOwnerBalance: sdkmath.NewInt(2).Mul(priceMultiplier), + wantErr: true, + wantErrContains: fmt.Sprintf("Dym-Name: %s: not found", offer.AssetId), + wantLaterOffer: offer, + wantLaterModuleBalance: sdkmath.OneInt().Mul(priceMultiplier), + wantLaterOwnerBalance: sdkmath.NewInt(2).Mul(priceMultiplier), + wantMinConsumeGas: 1, + }, + { + name: "fail - can not accept offer of Dym-Name owned by another", + existingDymName: sameDymNameButOwnedByAnother, + existingOffer: offer, + buyOrderId: offer.Id, + owner: ownerA, + minAccept: offer.OfferPrice, + originalModuleBalance: sdkmath.NewInt(1).Mul(priceMultiplier), + originalOwnerBalance: sdkmath.NewInt(2).Mul(priceMultiplier), + wantErr: true, + wantErrContains: "not the owner of the Dym-Name", + wantLaterDymName: sameDymNameButOwnedByAnother, + wantLaterOffer: offer, + wantLaterModuleBalance: sdkmath.OneInt().Mul(priceMultiplier), + wantLaterOwnerBalance: sdkmath.NewInt(2).Mul(priceMultiplier), + wantMinConsumeGas: 1, + }, + { + name: "fail - can not accept own offer", + existingDymName: dymName, + existingOffer: func() *dymnstypes.BuyOrder { + return &dymnstypes.BuyOrder{ + Id: "101", + AssetId: dymName.Name, + AssetType: dymnstypes.TypeName, + Buyer: ownerA, + OfferPrice: minOfferPriceCoin, + } + }(), + buyOrderId: "101", + owner: ownerA, + minAccept: minOfferPriceCoin, + originalModuleBalance: sdkmath.OneInt().Mul(priceMultiplier), + originalOwnerBalance: sdkmath.NewInt(2).Mul(priceMultiplier), + wantErr: true, + wantErrContains: "cannot accept own offer", + wantLaterDymName: dymName, + wantLaterOffer: func() *dymnstypes.BuyOrder { + return &dymnstypes.BuyOrder{ + Id: "101", + AssetId: dymName.Name, + AssetType: dymnstypes.TypeName, + Buyer: ownerA, + OfferPrice: minOfferPriceCoin, + } + }(), + wantLaterModuleBalance: sdkmath.OneInt().Mul(priceMultiplier), + wantLaterOwnerBalance: sdkmath.NewInt(2).Mul(priceMultiplier), + wantMinConsumeGas: 1, + }, + { + name: "fail - offer price denom != accept price denom", + existingDymName: dymName, + existingOffer: func() *dymnstypes.BuyOrder { + return &dymnstypes.BuyOrder{ + Id: "101", + AssetId: dymName.Name, + AssetType: dymnstypes.TypeName, + Buyer: buyerA, + OfferPrice: sdk.Coin{ + Denom: s.priceDenom(), + Amount: sdk.NewInt(minOfferPrice).Mul(priceMultiplier), + }, + } + }(), + buyOrderId: "101", + owner: ownerA, + minAccept: sdk.Coin{ + Denom: "u" + s.priceDenom(), + Amount: sdk.NewInt(minOfferPrice).Mul(priceMultiplier), + }, + originalModuleBalance: sdkmath.OneInt().Mul(priceMultiplier), + originalOwnerBalance: sdkmath.NewInt(2).Mul(priceMultiplier), + wantErr: true, + wantErrContains: "denom must be the same as the offer price", + wantLaterDymName: dymName, + wantLaterOffer: func() *dymnstypes.BuyOrder { + // unchanged + return &dymnstypes.BuyOrder{ + Id: "101", + AssetId: dymName.Name, + AssetType: dymnstypes.TypeName, + Buyer: buyerA, + OfferPrice: sdk.Coin{ + Denom: s.priceDenom(), + Amount: sdk.NewInt(minOfferPrice).Mul(priceMultiplier), + }, + } + }(), + wantLaterModuleBalance: sdkmath.OneInt().Mul(priceMultiplier), + wantLaterOwnerBalance: sdkmath.NewInt(2).Mul(priceMultiplier), + wantMinConsumeGas: 1, + }, + { + name: "fail - accept price lower than offer price", + existingDymName: dymName, + existingOffer: func() *dymnstypes.BuyOrder { + return &dymnstypes.BuyOrder{ + Id: "101", + AssetId: dymName.Name, + AssetType: dymnstypes.TypeName, + Buyer: buyerA, + OfferPrice: minOfferPriceCoin.AddAmount(sdk.NewInt(2)), + } + }(), + buyOrderId: "101", + owner: ownerA, + minAccept: minOfferPriceCoin, + originalModuleBalance: sdkmath.OneInt().Mul(priceMultiplier), + originalOwnerBalance: sdkmath.NewInt(2).Mul(priceMultiplier), + wantErr: true, + wantErrContains: "amount must be greater than or equals to the offer price", + wantLaterDymName: dymName, + wantLaterOffer: func() *dymnstypes.BuyOrder { + return &dymnstypes.BuyOrder{ + Id: "101", + AssetId: dymName.Name, + AssetType: dymnstypes.TypeName, + Buyer: buyerA, + OfferPrice: minOfferPriceCoin.AddAmount(sdk.NewInt(2)), + } + }(), + wantLaterModuleBalance: sdkmath.OneInt().Mul(priceMultiplier), + wantLaterOwnerBalance: sdkmath.NewInt(2).Mul(priceMultiplier), + wantMinConsumeGas: 1, + }, + { + name: "fail - prohibited to accept offer if a Sell-Order is active", + existingDymName: dymName, + existingOffer: offer, + buyOrderId: offer.Id, + owner: dymName.Owner, + minAccept: offer.OfferPrice, + originalModuleBalance: offer.OfferPrice.Amount, + originalOwnerBalance: sdk.NewInt(0), + preRunSetupFunc: func(s *KeeperTestSuite) { + err := s.dymNsKeeper.SetSellOrder( + s.ctx, + s.newDymNameSellOrder(dymName.Name). + WithMinPrice(1). + WithExpiry(s.now.Add(time.Hour).Unix()). + Build(), + ) + s.Require().NoError(err) + }, + wantErr: true, + wantErrContains: "must cancel the sell order first", + wantLaterOffer: offer, + wantLaterDymName: dymName, + wantLaterModuleBalance: offer.OfferPrice.Amount, + wantLaterOwnerBalance: sdk.NewInt(0), + wantMinConsumeGas: 1, + }, + { + name: "fail - prohibited to accept offer if a Sell-Order is active, regardless the SO is expired", + existingDymName: dymName, + existingOffer: offer, + buyOrderId: offer.Id, + owner: dymName.Owner, + minAccept: offer.OfferPrice, + originalModuleBalance: offer.OfferPrice.Amount, + originalOwnerBalance: sdk.NewInt(0), + preRunSetupFunc: func(s *KeeperTestSuite) { + err := s.dymNsKeeper.SetSellOrder( + s.ctx, + s.newDymNameSellOrder(dymName.Name). + WithMinPrice(1). + Expired(). + Build(), + ) + s.Require().NoError(err) + }, + wantErr: true, + wantErrContains: "must cancel the sell order first", + wantLaterOffer: offer, + wantLaterDymName: dymName, + wantLaterModuleBalance: offer.OfferPrice.Amount, + wantLaterOwnerBalance: sdk.NewInt(0), + wantMinConsumeGas: 1, + }, + { + name: "pass - can negotiate when a Sell-Order is active", + existingDymName: dymName, + existingOffer: offer, + buyOrderId: offer.Id, + owner: dymName.Owner, + minAccept: offer.OfferPrice.AddAmount(sdk.NewInt(1)), + originalModuleBalance: sdkmath.OneInt().Mul(priceMultiplier), + originalOwnerBalance: sdkmath.NewInt(2).Mul(priceMultiplier), + preRunSetupFunc: func(s *KeeperTestSuite) { + err := s.dymNsKeeper.SetSellOrder( + s.ctx, + s.newDymNameSellOrder(dymName.Name). + WithMinPrice(1). + Expired(). + Build(), + ) + s.Require().NoError(err) + }, + wantErr: false, + wantLaterOffer: &dymnstypes.BuyOrder{ + Id: offer.Id, + AssetId: offer.AssetId, + AssetType: dymnstypes.TypeName, + Buyer: offer.Buyer, + OfferPrice: offer.OfferPrice, + CounterpartyOfferPrice: func() *sdk.Coin { + coin := offer.OfferPrice.AddAmount(sdk.NewInt(1)) + return &coin + }(), + }, + wantLaterDymName: dymName, + wantLaterModuleBalance: sdkmath.OneInt().Mul(priceMultiplier), + wantLaterOwnerBalance: sdkmath.NewInt(2).Mul(priceMultiplier), + wantMinConsumeGas: dymnstypes.OpGasUpdateBuyOrder, + }, + } + for _, tt := range tests { + s.Run(tt.name, func() { + s.RefreshContext() + + if tt.originalModuleBalance.IsPositive() { + s.mintToModuleAccount2(tt.originalModuleBalance) + } + + if tt.originalOwnerBalance.IsPositive() { + s.mintToAccount2(tt.owner, tt.originalOwnerBalance) + } + + if tt.existingDymName != nil { + s.setDymNameWithFunctionsAfter(*tt.existingDymName) + } + + if tt.existingOffer != nil { + err := s.dymNsKeeper.SetBuyOrder(s.ctx, *tt.existingOffer) + s.Require().NoError(err) + + err = s.dymNsKeeper.AddReverseMappingBuyerToBuyOrderRecord(s.ctx, tt.existingOffer.Buyer, tt.existingOffer.Id) + s.Require().NoError(err) + + err = s.dymNsKeeper.AddReverseMappingAssetIdToBuyOrder(s.ctx, tt.existingOffer.AssetId, tt.existingOffer.AssetType, tt.existingOffer.Id) + s.Require().NoError(err) + } + + if tt.preRunSetupFunc != nil { + tt.preRunSetupFunc(s) + } + + resp, err := dymnskeeper.NewMsgServerImpl(s.dymNsKeeper).AcceptBuyOrder(s.ctx, &dymnstypes.MsgAcceptBuyOrder{ + OrderId: tt.buyOrderId, + Owner: tt.owner, + MinAccept: tt.minAccept, + }) + + defer func() { + if s.T().Failed() { + return + } + + if tt.wantLaterOffer != nil { + laterOffer := s.dymNsKeeper.GetBuyOrder(s.ctx, tt.wantLaterOffer.Id) + s.Require().NotNil(laterOffer) + s.Require().Equal(*tt.wantLaterOffer, *laterOffer) + } else { + laterOffer := s.dymNsKeeper.GetBuyOrder(s.ctx, tt.buyOrderId) + s.Require().Nil(laterOffer) + } + + laterModuleBalance := s.moduleBalance2() + s.Equal(tt.wantLaterModuleBalance.String(), laterModuleBalance.String()) + + laterBuyerBalance := s.balance2(tt.owner) + s.Equal(tt.wantLaterOwnerBalance.String(), laterBuyerBalance.String()) + + s.Less(tt.wantMinConsumeGas, s.ctx.GasMeter().GasConsumed()) + + if tt.wantLaterDymName != nil { + laterDymName := s.dymNsKeeper.GetDymName(s.ctx, tt.wantLaterDymName.Name) + s.Require().NotNil(laterDymName) + s.Require().Equal(*tt.wantLaterDymName, *laterDymName) + } + + if tt.afterTestFunc != nil { + tt.afterTestFunc(s) + } + }() + + if tt.wantErr { + s.Require().ErrorContains(err, tt.wantErrContains) + return + } + + s.Require().NoError(err) + s.NotNil(resp) + }) + } +} + +//goland:noinspection GoSnakeCaseUsage +func (s *KeeperTestSuite) Test_msgServer_AcceptBuyOrder_Type_Alias() { + const minOfferPrice = 5 + + // the number values used in this test will be multiplied by this value + priceMultiplier := sdk.NewInt(1e18) + + minOfferPriceCoin := sdk.NewCoin(s.priceDenom(), sdk.NewInt(minOfferPrice).Mul(priceMultiplier)) + + creator_1_asOwner := testAddr(1).bech32() + creator_2_asBuyer := testAddr(2).bech32() + anotherAcc := testAddr(3) + + rollApp_One_By1_SingleAlias := *newRollApp("rollapp_1-1"). + WithOwner(creator_1_asOwner). + WithAlias("one1") + rollApp_Two_By2_SingleAlias := *newRollApp("rollapp_2-2"). + WithOwner(creator_2_asBuyer). + WithAlias("two1") + rollApp_Three_By1_MultipleAliases := *newRollApp("rollapp_3-1"). + WithOwner(creator_1_asOwner). + WithAlias("three1").WithAlias("three2") + rollApp_Four_By2_MultipleAliases := *newRollApp("rollapp_4-2"). + WithOwner(creator_2_asBuyer). + WithAlias("four1").WithAlias("four2").WithAlias("four3") + + s.updateModuleParams(func(moduleParams dymnstypes.Params) dymnstypes.Params { + moduleParams.Price.MinOfferPrice = minOfferPriceCoin.Amount + // force enable trading + moduleParams.Misc.EnableTradingName = true + moduleParams.Misc.EnableTradingAlias = true + return moduleParams + }) + s.SaveCurrentContext() + + s.Run("reject if message not pass validate basic", func() { + _, err := dymnskeeper.NewMsgServerImpl(s.dymNsKeeper).AcceptBuyOrder(s.ctx, &dymnstypes.MsgAcceptBuyOrder{}) + s.Require().ErrorContains(err, gerrc.ErrInvalidArgument.Error()) + }) + + offerAliasOfRollAppOne := &dymnstypes.BuyOrder{ + Id: dymnstypes.CreateBuyOrderId(dymnstypes.TypeAlias, 1), + AssetId: rollApp_One_By1_SingleAlias.aliases[0], + AssetType: dymnstypes.TypeAlias, + Params: []string{rollApp_Two_By2_SingleAlias.rollAppId}, + Buyer: rollApp_Two_By2_SingleAlias.owner, + OfferPrice: minOfferPriceCoin, + } + + offerNonExistingAlias := &dymnstypes.BuyOrder{ + Id: dymnstypes.CreateBuyOrderId(dymnstypes.TypeAlias, 2), + AssetId: "nah", + AssetType: dymnstypes.TypeAlias, + Params: []string{rollApp_Two_By2_SingleAlias.rollAppId}, + Buyer: rollApp_Two_By2_SingleAlias.owner, + OfferPrice: minOfferPriceCoin, + } + + offerAliasForNonExistingRollApp := &dymnstypes.BuyOrder{ + Id: dymnstypes.CreateBuyOrderId(dymnstypes.TypeAlias, 1), + AssetId: rollApp_One_By1_SingleAlias.aliases[0], + AssetType: dymnstypes.TypeAlias, + Params: []string{"nah_0-0"}, + Buyer: creator_2_asBuyer, + OfferPrice: minOfferPriceCoin, + } + + tests := []struct { + name string + existingRollApps []rollapp + existingOffer *dymnstypes.BuyOrder + buyOrderId string + owner string + minAccept sdk.Coin + originalModuleBalance sdkmath.Int + originalOwnerBalance sdkmath.Int + preRunSetupFunc func(s *KeeperTestSuite) + wantErr bool + wantErrContains string + wantLaterOffer *dymnstypes.BuyOrder + wantLaterRollApps []rollapp + wantLaterModuleBalance sdkmath.Int + wantLaterOwnerBalance sdkmath.Int + wantMinConsumeGas sdk.Gas + afterTestFunc func(s *KeeperTestSuite) + }{ + { + name: "pass - can accept offer (match)", + existingRollApps: []rollapp{rollApp_One_By1_SingleAlias, rollApp_Two_By2_SingleAlias}, + existingOffer: offerAliasOfRollAppOne, + buyOrderId: offerAliasOfRollAppOne.Id, + owner: rollApp_One_By1_SingleAlias.owner, + minAccept: offerAliasOfRollAppOne.OfferPrice, + originalModuleBalance: offerAliasOfRollAppOne.OfferPrice.Amount, + originalOwnerBalance: sdk.NewInt(0), + preRunSetupFunc: nil, + wantErr: false, + wantLaterOffer: nil, + wantLaterRollApps: []rollapp{ + { + rollAppId: rollApp_One_By1_SingleAlias.rollAppId, + aliases: []string{}, + }, + { + rollAppId: rollApp_Two_By2_SingleAlias.rollAppId, + aliases: append(rollApp_Two_By2_SingleAlias.aliases, offerAliasOfRollAppOne.AssetId), + }, + }, + wantLaterModuleBalance: sdk.NewInt(0), + wantLaterOwnerBalance: offerAliasOfRollAppOne.OfferPrice.Amount, + wantMinConsumeGas: dymnstypes.OpGasUpdateBuyOrder, + }, + { + name: "pass - after match offer, reverse records of the offer are removed", + existingRollApps: []rollapp{rollApp_One_By1_SingleAlias, rollApp_Two_By2_SingleAlias}, + existingOffer: offerAliasOfRollAppOne, + buyOrderId: offerAliasOfRollAppOne.Id, + owner: rollApp_One_By1_SingleAlias.owner, + minAccept: offerAliasOfRollAppOne.OfferPrice, + originalModuleBalance: offerAliasOfRollAppOne.OfferPrice.Amount, + originalOwnerBalance: sdk.NewInt(0), + preRunSetupFunc: func(s *KeeperTestSuite) { + key := dymnstypes.AliasToBuyOrderIdsRvlKey(offerAliasOfRollAppOne.AssetId) + orderIds := s.dymNsKeeper.GenericGetReverseLookupBuyOrderIdsRecord(s.ctx, key) + s.Require().Equal([]string{offerAliasOfRollAppOne.Id}, orderIds.OrderIds) + + key = dymnstypes.BuyerToOrderIdsRvlKey(sdk.MustAccAddressFromBech32(offerAliasOfRollAppOne.Buyer)) + orderIds = s.dymNsKeeper.GenericGetReverseLookupBuyOrderIdsRecord(s.ctx, key) + s.Require().Equal([]string{offerAliasOfRollAppOne.Id}, orderIds.OrderIds) + }, + wantErr: false, + wantLaterOffer: nil, + wantLaterModuleBalance: sdk.NewInt(0), + wantLaterOwnerBalance: offerAliasOfRollAppOne.OfferPrice.Amount, + wantMinConsumeGas: dymnstypes.OpGasUpdateBuyOrder, + afterTestFunc: func(s *KeeperTestSuite) { + key := dymnstypes.DymNameToBuyOrderIdsRvlKey(offerAliasOfRollAppOne.AssetId) + orderIds := s.dymNsKeeper.GenericGetReverseLookupBuyOrderIdsRecord(s.ctx, key) + s.Require().Empty(orderIds.OrderIds) + + key = dymnstypes.BuyerToOrderIdsRvlKey(sdk.MustAccAddressFromBech32(offerAliasOfRollAppOne.Buyer)) + orderIds = s.dymNsKeeper.GenericGetReverseLookupBuyOrderIdsRecord(s.ctx, key) + s.Require().Empty(orderIds.OrderIds) + }, + }, + { + name: "pass - after match offer, linking between RollApps and the alias are updated", + existingRollApps: []rollapp{rollApp_One_By1_SingleAlias, rollApp_Two_By2_SingleAlias}, + existingOffer: offerAliasOfRollAppOne, + buyOrderId: offerAliasOfRollAppOne.Id, + owner: rollApp_One_By1_SingleAlias.owner, + minAccept: offerAliasOfRollAppOne.OfferPrice, + originalModuleBalance: offerAliasOfRollAppOne.OfferPrice.Amount, + originalOwnerBalance: sdk.NewInt(0), + preRunSetupFunc: func(s *KeeperTestSuite) { + s.requireAlias(offerAliasOfRollAppOne.AssetId). + LinkedToRollApp(rollApp_One_By1_SingleAlias.rollAppId) + }, + wantErr: false, + wantLaterOffer: nil, + wantLaterRollApps: []rollapp{ + { + rollAppId: rollApp_One_By1_SingleAlias.rollAppId, + aliases: []string{}, + }, + { + rollAppId: rollApp_Two_By2_SingleAlias.rollAppId, + aliases: append(rollApp_Two_By2_SingleAlias.aliases, offerAliasOfRollAppOne.AssetId), + }, + }, + wantLaterModuleBalance: sdk.NewInt(0), + wantLaterOwnerBalance: offerAliasOfRollAppOne.OfferPrice.Amount, + wantMinConsumeGas: dymnstypes.OpGasUpdateBuyOrder, + afterTestFunc: func(s *KeeperTestSuite) { + s.requireAlias(offerAliasOfRollAppOne.AssetId). + LinkedToRollApp(rollApp_Two_By2_SingleAlias.rollAppId) // changed + + s.requireRollApp(rollApp_One_By1_SingleAlias.rollAppId). + HasNoAlias() // link removed from previous RollApp + }, + }, + { + name: "pass - (negotiation) when price not match offer price, raise the counterparty offer price", + existingRollApps: []rollapp{rollApp_One_By1_SingleAlias, rollApp_Two_By2_SingleAlias}, + existingOffer: offerAliasOfRollAppOne, + buyOrderId: offerAliasOfRollAppOne.Id, + owner: rollApp_One_By1_SingleAlias.owner, + minAccept: offerAliasOfRollAppOne.OfferPrice.AddAmount(sdk.NewInt(1)), + originalModuleBalance: sdk.NewInt(1), + originalOwnerBalance: sdk.NewInt(2), + preRunSetupFunc: nil, + wantErr: false, + wantLaterOffer: &dymnstypes.BuyOrder{ + Id: offerAliasOfRollAppOne.Id, + AssetId: offerAliasOfRollAppOne.AssetId, + AssetType: offerAliasOfRollAppOne.AssetType, + Params: offerAliasOfRollAppOne.Params, + Buyer: offerAliasOfRollAppOne.Buyer, + OfferPrice: offerAliasOfRollAppOne.OfferPrice, + CounterpartyOfferPrice: uptr.To(offerAliasOfRollAppOne.OfferPrice.AddAmount(sdk.NewInt(1))), + }, + wantLaterRollApps: []rollapp{rollApp_One_By1_SingleAlias, rollApp_Two_By2_SingleAlias}, + wantLaterModuleBalance: sdk.NewInt(1), + wantLaterOwnerBalance: sdk.NewInt(2), + wantMinConsumeGas: dymnstypes.OpGasUpdateBuyOrder, + }, + { + name: "pass - after put negotiation price, reverse records of the offer are preserved", + existingRollApps: []rollapp{rollApp_One_By1_SingleAlias, rollApp_Two_By2_SingleAlias}, + existingOffer: offerAliasOfRollAppOne, + buyOrderId: offerAliasOfRollAppOne.Id, + owner: rollApp_One_By1_SingleAlias.owner, + minAccept: offerAliasOfRollAppOne.OfferPrice.AddAmount(sdk.NewInt(1)), + originalModuleBalance: sdk.NewInt(1), + originalOwnerBalance: sdk.NewInt(2), + preRunSetupFunc: func(s *KeeperTestSuite) { + key := dymnstypes.AliasToBuyOrderIdsRvlKey(offerAliasOfRollAppOne.AssetId) + orderIds := s.dymNsKeeper.GenericGetReverseLookupBuyOrderIdsRecord(s.ctx, key) + s.Require().Equal([]string{offerAliasOfRollAppOne.Id}, orderIds.OrderIds) + + key = dymnstypes.BuyerToOrderIdsRvlKey(sdk.MustAccAddressFromBech32(offerAliasOfRollAppOne.Buyer)) + orderIds = s.dymNsKeeper.GenericGetReverseLookupBuyOrderIdsRecord(s.ctx, key) + s.Require().Equal([]string{offerAliasOfRollAppOne.Id}, orderIds.OrderIds) + }, + wantErr: false, + wantLaterOffer: &dymnstypes.BuyOrder{ + Id: offerAliasOfRollAppOne.Id, + AssetId: offerAliasOfRollAppOne.AssetId, + AssetType: offerAliasOfRollAppOne.AssetType, + Params: offerAliasOfRollAppOne.Params, + Buyer: offerAliasOfRollAppOne.Buyer, + OfferPrice: offerAliasOfRollAppOne.OfferPrice, + CounterpartyOfferPrice: uptr.To(offerAliasOfRollAppOne.OfferPrice.AddAmount(sdk.NewInt(1))), + }, + wantLaterModuleBalance: sdk.NewInt(1), + wantLaterOwnerBalance: sdk.NewInt(2), + wantMinConsumeGas: dymnstypes.OpGasUpdateBuyOrder, + afterTestFunc: func(s *KeeperTestSuite) { + // the same as before + + key := dymnstypes.AliasToBuyOrderIdsRvlKey(offerAliasOfRollAppOne.AssetId) + orderIds := s.dymNsKeeper.GenericGetReverseLookupBuyOrderIdsRecord(s.ctx, key) + s.Require().Equal([]string{offerAliasOfRollAppOne.Id}, orderIds.OrderIds) + + key = dymnstypes.BuyerToOrderIdsRvlKey(sdk.MustAccAddressFromBech32(offerAliasOfRollAppOne.Buyer)) + orderIds = s.dymNsKeeper.GenericGetReverseLookupBuyOrderIdsRecord(s.ctx, key) + s.Require().Equal([]string{offerAliasOfRollAppOne.Id}, orderIds.OrderIds) + }, + }, + { + name: "pass - after put negotiation price, original linking between RollApp and alias are preserved", + existingRollApps: []rollapp{rollApp_One_By1_SingleAlias, rollApp_Two_By2_SingleAlias}, + existingOffer: offerAliasOfRollAppOne, + buyOrderId: offerAliasOfRollAppOne.Id, + owner: rollApp_One_By1_SingleAlias.owner, + minAccept: offerAliasOfRollAppOne.OfferPrice.AddAmount(sdk.NewInt(1)), + originalModuleBalance: sdk.NewInt(1), + originalOwnerBalance: sdk.NewInt(2), + preRunSetupFunc: func(s *KeeperTestSuite) { + s.requireAlias(offerAliasOfRollAppOne.AssetId).LinkedToRollApp(rollApp_One_By1_SingleAlias.rollAppId) + }, + wantErr: false, + wantLaterOffer: &dymnstypes.BuyOrder{ + Id: offerAliasOfRollAppOne.Id, + AssetId: offerAliasOfRollAppOne.AssetId, + AssetType: offerAliasOfRollAppOne.AssetType, + Params: offerAliasOfRollAppOne.Params, + Buyer: offerAliasOfRollAppOne.Buyer, + OfferPrice: offerAliasOfRollAppOne.OfferPrice, + CounterpartyOfferPrice: uptr.To(offerAliasOfRollAppOne.OfferPrice.AddAmount(sdk.NewInt(1))), + }, + wantLaterModuleBalance: sdk.NewInt(1), + wantLaterOwnerBalance: sdk.NewInt(2), + wantMinConsumeGas: dymnstypes.OpGasUpdateBuyOrder, + afterTestFunc: func(s *KeeperTestSuite) { + // unchanged + s.requireAlias(offerAliasOfRollAppOne.AssetId).LinkedToRollApp(rollApp_One_By1_SingleAlias.rollAppId) + }, + }, + { + name: "fail - not accept offer if alias presents in params", + existingRollApps: []rollapp{ + rollApp_One_By1_SingleAlias, + rollApp_Two_By2_SingleAlias, + }, + existingOffer: offerAliasOfRollAppOne, + buyOrderId: offerAliasOfRollAppOne.Id, + owner: rollApp_One_By1_SingleAlias.owner, + minAccept: offerAliasOfRollAppOne.OfferPrice, + originalModuleBalance: offerAliasOfRollAppOne.OfferPrice.Amount, + originalOwnerBalance: sdk.NewInt(0), + preRunSetupFunc: func(s *KeeperTestSuite) { + s.updateModuleParams(func(moduleParams dymnstypes.Params) dymnstypes.Params { + moduleParams.Chains.AliasesOfChainIds = []dymnstypes.AliasesOfChainId{ + { + ChainId: "some-chain", + Aliases: []string{offerAliasOfRollAppOne.AssetId}, + }, + } + return moduleParams + }) + }, + wantErr: true, + wantErrContains: "prohibited to trade aliases which is reserved for chain-id or alias in module params", + wantLaterOffer: offerAliasOfRollAppOne, + wantLaterRollApps: []rollapp{ + rollApp_One_By1_SingleAlias, + rollApp_Two_By2_SingleAlias, + }, + wantLaterModuleBalance: offerAliasOfRollAppOne.OfferPrice.Amount, + wantLaterOwnerBalance: sdk.NewInt(0), + wantMinConsumeGas: 1, + }, + { + name: "fail - can NOT accept offer when trading Alias is disabled", + existingRollApps: []rollapp{rollApp_One_By1_SingleAlias, rollApp_Two_By2_SingleAlias}, + existingOffer: offerAliasOfRollAppOne, + buyOrderId: offerAliasOfRollAppOne.Id, + owner: rollApp_One_By1_SingleAlias.owner, + minAccept: offerAliasOfRollAppOne.OfferPrice, + originalModuleBalance: offerAliasOfRollAppOne.OfferPrice.Amount, + originalOwnerBalance: sdk.NewInt(0), + preRunSetupFunc: func(s *KeeperTestSuite) { + s.updateModuleParams(func(moduleParams dymnstypes.Params) dymnstypes.Params { + moduleParams.Misc.EnableTradingAlias = false + return moduleParams + }) + }, + wantErr: true, + wantErrContains: "trading of Alias is disabled", + wantLaterOffer: offerAliasOfRollAppOne, + wantLaterRollApps: []rollapp{rollApp_One_By1_SingleAlias, rollApp_Two_By2_SingleAlias}, + wantLaterModuleBalance: offerAliasOfRollAppOne.OfferPrice.Amount, + wantLaterOwnerBalance: sdk.NewInt(0), + wantMinConsumeGas: 1, + }, + { + name: "fail - offer not found", + existingRollApps: []rollapp{rollApp_One_By1_SingleAlias, rollApp_Two_By2_SingleAlias}, + existingOffer: nil, + buyOrderId: "201", + owner: rollApp_One_By1_SingleAlias.owner, + minAccept: minOfferPriceCoin, + originalModuleBalance: sdk.NewInt(1), + originalOwnerBalance: sdk.NewInt(2), + wantErr: true, + wantErrContains: "Buy-Order: 201: not found", + wantLaterOffer: nil, + wantLaterRollApps: []rollapp{rollApp_One_By1_SingleAlias, rollApp_Two_By2_SingleAlias}, + wantLaterModuleBalance: sdk.NewInt(1), + wantLaterOwnerBalance: sdk.NewInt(2), + wantMinConsumeGas: 1, + }, + { + name: "fail - offer not found", + existingRollApps: []rollapp{rollApp_One_By1_SingleAlias, rollApp_Two_By2_SingleAlias}, + existingOffer: offerAliasOfRollAppOne, + buyOrderId: "20673264823", + owner: rollApp_One_By1_SingleAlias.owner, + minAccept: minOfferPriceCoin, + originalModuleBalance: sdk.NewInt(1), + originalOwnerBalance: sdk.NewInt(2), + wantErr: true, + wantErrContains: "Buy-Order: 20673264823: not found", + wantLaterOffer: offerAliasOfRollAppOne, + wantLaterRollApps: []rollapp{rollApp_One_By1_SingleAlias, rollApp_Two_By2_SingleAlias}, + wantLaterModuleBalance: sdk.NewInt(1), + wantLaterOwnerBalance: sdk.NewInt(2), + wantMinConsumeGas: 1, + }, + { + name: "fail - Alias not found", + existingRollApps: []rollapp{rollApp_One_By1_SingleAlias, rollApp_Two_By2_SingleAlias}, + existingOffer: offerNonExistingAlias, // offer non-existing alias + buyOrderId: offerNonExistingAlias.Id, + owner: rollApp_One_By1_SingleAlias.owner, + minAccept: offerNonExistingAlias.OfferPrice, + originalModuleBalance: sdk.NewInt(1), + originalOwnerBalance: sdk.NewInt(2), + wantErr: true, + wantErrContains: "alias is not in-used", + wantLaterOffer: offerNonExistingAlias, + wantLaterRollApps: []rollapp{rollApp_One_By1_SingleAlias, rollApp_Two_By2_SingleAlias}, + wantLaterModuleBalance: sdk.NewInt(1), + wantLaterOwnerBalance: sdk.NewInt(2), + wantMinConsumeGas: 1, + }, + { + name: "fail - destination RollApp not exists", + existingRollApps: []rollapp{rollApp_One_By1_SingleAlias, rollApp_Two_By2_SingleAlias}, + existingOffer: offerAliasForNonExistingRollApp, // offer for non-existing RollApp + buyOrderId: offerAliasForNonExistingRollApp.Id, + owner: rollApp_One_By1_SingleAlias.owner, + minAccept: offerAliasForNonExistingRollApp.OfferPrice, + originalModuleBalance: sdk.NewInt(1), + originalOwnerBalance: sdk.NewInt(2), + wantErr: true, + wantErrContains: "invalid destination Roll-App ID", + wantLaterOffer: offerAliasForNonExistingRollApp, + wantLaterRollApps: []rollapp{rollApp_One_By1_SingleAlias, rollApp_Two_By2_SingleAlias}, + wantLaterModuleBalance: sdk.NewInt(1), + wantLaterOwnerBalance: sdk.NewInt(2), + wantMinConsumeGas: 1, + }, + { + name: "fail - can not accept offer of Alias owned by another", + existingRollApps: []rollapp{rollApp_One_By1_SingleAlias, rollApp_Two_By2_SingleAlias}, + existingOffer: offerAliasOfRollAppOne, + buyOrderId: offerAliasOfRollAppOne.Id, + owner: anotherAcc.bech32(), + minAccept: offerAliasOfRollAppOne.OfferPrice, + originalModuleBalance: sdk.NewInt(1), + originalOwnerBalance: sdk.NewInt(2), + wantErr: true, + wantErrContains: "not the owner of the RollApp", + wantLaterRollApps: []rollapp{rollApp_One_By1_SingleAlias, rollApp_Two_By2_SingleAlias}, + wantLaterOffer: offerAliasOfRollAppOne, + wantLaterModuleBalance: sdk.NewInt(1), + wantLaterOwnerBalance: sdk.NewInt(2), + wantMinConsumeGas: 1, + }, + { + name: "fail - can not accept own offer", + existingRollApps: []rollapp{rollApp_One_By1_SingleAlias, rollApp_Two_By2_SingleAlias}, + existingOffer: func() *dymnstypes.BuyOrder { + return &dymnstypes.BuyOrder{ + Id: offerAliasOfRollAppOne.Id, + AssetId: offerAliasOfRollAppOne.AssetId, + AssetType: offerAliasOfRollAppOne.AssetType, + Params: offerAliasOfRollAppOne.Params, + Buyer: creator_1_asOwner, + OfferPrice: minOfferPriceCoin, + } + }(), + buyOrderId: offerAliasOfRollAppOne.Id, + owner: creator_1_asOwner, + minAccept: minOfferPriceCoin, + originalModuleBalance: sdk.NewInt(1), + originalOwnerBalance: sdk.NewInt(2), + wantErr: true, + wantErrContains: "cannot accept own offer", + wantLaterRollApps: []rollapp{rollApp_One_By1_SingleAlias, rollApp_Two_By2_SingleAlias}, + wantLaterOffer: func() *dymnstypes.BuyOrder { + return &dymnstypes.BuyOrder{ + Id: offerAliasOfRollAppOne.Id, + AssetId: offerAliasOfRollAppOne.AssetId, + AssetType: offerAliasOfRollAppOne.AssetType, + Params: offerAliasOfRollAppOne.Params, + Buyer: creator_1_asOwner, + OfferPrice: minOfferPriceCoin, + } + }(), + wantLaterModuleBalance: sdk.NewInt(1), + wantLaterOwnerBalance: sdk.NewInt(2), + wantMinConsumeGas: 1, + }, + { + name: "fail - offer price denom != accept price denom", + existingRollApps: []rollapp{rollApp_One_By1_SingleAlias, rollApp_Two_By2_SingleAlias}, + existingOffer: func() *dymnstypes.BuyOrder { + return &dymnstypes.BuyOrder{ + Id: offerAliasOfRollAppOne.Id, + AssetId: offerAliasOfRollAppOne.AssetId, + AssetType: offerAliasOfRollAppOne.AssetType, + Params: offerAliasOfRollAppOne.Params, + Buyer: offerAliasOfRollAppOne.Buyer, + OfferPrice: sdk.Coin{ + Denom: s.priceDenom(), + Amount: minOfferPriceCoin.Amount, + }, + } + }(), + buyOrderId: offerAliasOfRollAppOne.Id, + owner: rollApp_One_By1_SingleAlias.owner, + minAccept: sdk.Coin{ + Denom: "u" + s.priceDenom(), + Amount: minOfferPriceCoin.Amount, + }, + originalModuleBalance: sdk.NewInt(1), + originalOwnerBalance: sdk.NewInt(2), + wantErr: true, + wantErrContains: "denom must be the same as the offer price", + wantLaterRollApps: []rollapp{rollApp_One_By1_SingleAlias, rollApp_Two_By2_SingleAlias}, + wantLaterOffer: func() *dymnstypes.BuyOrder { + // unchanged + return &dymnstypes.BuyOrder{ + Id: offerAliasOfRollAppOne.Id, + AssetId: offerAliasOfRollAppOne.AssetId, + AssetType: offerAliasOfRollAppOne.AssetType, + Params: offerAliasOfRollAppOne.Params, + Buyer: offerAliasOfRollAppOne.Buyer, + OfferPrice: sdk.Coin{ + Denom: s.priceDenom(), + Amount: minOfferPriceCoin.Amount, + }, + } + }(), + wantLaterModuleBalance: sdk.NewInt(1), + wantLaterOwnerBalance: sdk.NewInt(2), + wantMinConsumeGas: 1, + }, + { + name: "fail - accept price lower than offer price", + existingRollApps: []rollapp{rollApp_One_By1_SingleAlias, rollApp_Two_By2_SingleAlias}, + existingOffer: func() *dymnstypes.BuyOrder { + return &dymnstypes.BuyOrder{ + Id: offerAliasOfRollAppOne.Id, + AssetId: offerAliasOfRollAppOne.AssetId, + AssetType: offerAliasOfRollAppOne.AssetType, + Params: offerAliasOfRollAppOne.Params, + Buyer: offerAliasOfRollAppOne.Buyer, + OfferPrice: minOfferPriceCoin.AddAmount(sdk.NewInt(2)), + } + }(), + buyOrderId: offerAliasOfRollAppOne.Id, + owner: rollApp_One_By1_SingleAlias.owner, + minAccept: minOfferPriceCoin, + originalModuleBalance: sdk.NewInt(1), + originalOwnerBalance: sdk.NewInt(2), + wantErr: true, + wantErrContains: "amount must be greater than or equals to the offer price", + wantLaterRollApps: []rollapp{rollApp_One_By1_SingleAlias, rollApp_Two_By2_SingleAlias}, + wantLaterOffer: func() *dymnstypes.BuyOrder { + return &dymnstypes.BuyOrder{ + Id: offerAliasOfRollAppOne.Id, + AssetId: offerAliasOfRollAppOne.AssetId, + AssetType: offerAliasOfRollAppOne.AssetType, + Params: offerAliasOfRollAppOne.Params, + Buyer: offerAliasOfRollAppOne.Buyer, + OfferPrice: minOfferPriceCoin.AddAmount(sdk.NewInt(2)), + } + }(), + wantLaterModuleBalance: sdk.NewInt(1), + wantLaterOwnerBalance: sdk.NewInt(2), + wantMinConsumeGas: 1, + }, + { + name: "pass - accept offer transfer alias from One-Alias-RollApp to Multiple-Alias-RollApp", + existingRollApps: []rollapp{rollApp_One_By1_SingleAlias, rollApp_Four_By2_MultipleAliases}, + existingOffer: func() *dymnstypes.BuyOrder { + return &dymnstypes.BuyOrder{ + Id: "201", + AssetId: rollApp_One_By1_SingleAlias.aliases[0], + AssetType: dymnstypes.TypeAlias, + Params: []string{rollApp_Four_By2_MultipleAliases.rollAppId}, + Buyer: creator_2_asBuyer, + OfferPrice: minOfferPriceCoin, + } + }(), + buyOrderId: "201", + owner: rollApp_One_By1_SingleAlias.owner, + minAccept: offerAliasOfRollAppOne.OfferPrice, + originalModuleBalance: offerAliasOfRollAppOne.OfferPrice.Amount, + originalOwnerBalance: sdk.NewInt(0), + preRunSetupFunc: nil, + wantErr: false, + wantLaterOffer: nil, + wantLaterRollApps: []rollapp{ + { + rollAppId: rollApp_One_By1_SingleAlias.rollAppId, + aliases: []string{}, + }, + { + rollAppId: rollApp_Four_By2_MultipleAliases.rollAppId, + aliases: append(rollApp_Four_By2_MultipleAliases.aliases, offerAliasOfRollAppOne.AssetId), + }, + }, + wantLaterModuleBalance: sdk.NewInt(0), + wantLaterOwnerBalance: offerAliasOfRollAppOne.OfferPrice.Amount, + wantMinConsumeGas: dymnstypes.OpGasUpdateBuyOrder, + }, + { + name: "pass - accept offer transfer alias from Multiple-Alias-RollApp to One-Alias-RollApp", + existingRollApps: []rollapp{rollApp_Three_By1_MultipleAliases, rollApp_Two_By2_SingleAlias}, + existingOffer: func() *dymnstypes.BuyOrder { + return &dymnstypes.BuyOrder{ + Id: "201", + AssetId: rollApp_Three_By1_MultipleAliases.aliases[0], + AssetType: dymnstypes.TypeAlias, + Params: []string{rollApp_Two_By2_SingleAlias.rollAppId}, + Buyer: creator_2_asBuyer, + OfferPrice: minOfferPriceCoin, + } + }(), + buyOrderId: "201", + owner: rollApp_One_By1_SingleAlias.owner, + minAccept: minOfferPriceCoin, + originalModuleBalance: minOfferPriceCoin.Amount, + originalOwnerBalance: sdk.NewInt(0), + preRunSetupFunc: nil, + wantErr: false, + wantLaterOffer: nil, + wantLaterRollApps: []rollapp{ + { + rollAppId: rollApp_Three_By1_MultipleAliases.rollAppId, + aliases: rollApp_Three_By1_MultipleAliases.aliases[1:], + }, + { + rollAppId: rollApp_Two_By2_SingleAlias.rollAppId, + aliases: append(rollApp_Two_By2_SingleAlias.aliases, rollApp_Three_By1_MultipleAliases.aliases[0]), + }, + }, + wantLaterModuleBalance: sdk.NewInt(0), + wantLaterOwnerBalance: minOfferPriceCoin.Amount, + wantMinConsumeGas: dymnstypes.OpGasUpdateBuyOrder, + }, + { + name: "fail - prohibit to accept offer when a Sell-Order is active", + existingRollApps: []rollapp{rollApp_One_By1_SingleAlias, rollApp_Two_By2_SingleAlias}, + existingOffer: offerAliasOfRollAppOne, + buyOrderId: offerAliasOfRollAppOne.Id, + owner: rollApp_One_By1_SingleAlias.owner, + minAccept: offerAliasOfRollAppOne.OfferPrice, + originalModuleBalance: offerAliasOfRollAppOne.OfferPrice.Amount, + originalOwnerBalance: sdk.NewInt(0), + preRunSetupFunc: func(s *KeeperTestSuite) { + err := s.dymNsKeeper.SetSellOrder( + s.ctx, + s.newAliasSellOrder(offerAliasOfRollAppOne.AssetId). + WithExpiry(s.now.Add(time.Hour).Unix()). + WithMinPrice(1). + Build(), + ) + s.Require().NoError(err) + }, + wantErr: true, + wantErrContains: "must cancel the sell order first", + wantLaterOffer: offerAliasOfRollAppOne, + wantLaterRollApps: []rollapp{rollApp_One_By1_SingleAlias, rollApp_Two_By2_SingleAlias}, + wantLaterModuleBalance: offerAliasOfRollAppOne.OfferPrice.Amount, + wantLaterOwnerBalance: sdk.NewInt(0), + wantMinConsumeGas: 1, + }, + { + name: "fail - prohibit to accept offer when a Sell-Order is active, regardless the SO is expired", + existingRollApps: []rollapp{rollApp_One_By1_SingleAlias, rollApp_Two_By2_SingleAlias}, + existingOffer: offerAliasOfRollAppOne, + buyOrderId: offerAliasOfRollAppOne.Id, + owner: rollApp_One_By1_SingleAlias.owner, + minAccept: offerAliasOfRollAppOne.OfferPrice, + originalModuleBalance: offerAliasOfRollAppOne.OfferPrice.Amount, + originalOwnerBalance: sdk.NewInt(0), + preRunSetupFunc: func(s *KeeperTestSuite) { + err := s.dymNsKeeper.SetSellOrder( + s.ctx, + s.newAliasSellOrder(offerAliasOfRollAppOne.AssetId). + Expired(). + WithMinPrice(1). + Build(), + ) + s.Require().NoError(err) + }, + wantErr: true, + wantErrContains: "must cancel the sell order first", + wantLaterOffer: offerAliasOfRollAppOne, + wantLaterRollApps: []rollapp{rollApp_One_By1_SingleAlias, rollApp_Two_By2_SingleAlias}, + wantLaterModuleBalance: offerAliasOfRollAppOne.OfferPrice.Amount, + wantLaterOwnerBalance: sdk.NewInt(0), + wantMinConsumeGas: 1, + }, + { + name: "pass - can negotiate when a Sell-Order is active", + existingRollApps: []rollapp{rollApp_One_By1_SingleAlias, rollApp_Two_By2_SingleAlias}, + existingOffer: offerAliasOfRollAppOne, + buyOrderId: offerAliasOfRollAppOne.Id, + owner: rollApp_One_By1_SingleAlias.owner, + minAccept: offerAliasOfRollAppOne.OfferPrice.AddAmount(sdk.NewInt(1)), + originalModuleBalance: sdk.NewInt(1), + originalOwnerBalance: sdk.NewInt(2), + preRunSetupFunc: func(s *KeeperTestSuite) { + err := s.dymNsKeeper.SetSellOrder( + s.ctx, + s.newAliasSellOrder(offerAliasOfRollAppOne.AssetId). + Expired(). + WithMinPrice(1). + Build(), + ) + s.Require().NoError(err) + }, + wantErr: false, + wantLaterOffer: &dymnstypes.BuyOrder{ + Id: offerAliasOfRollAppOne.Id, + AssetId: offerAliasOfRollAppOne.AssetId, + AssetType: offerAliasOfRollAppOne.AssetType, + Params: offerAliasOfRollAppOne.Params, + Buyer: offerAliasOfRollAppOne.Buyer, + OfferPrice: offerAliasOfRollAppOne.OfferPrice, + CounterpartyOfferPrice: uptr.To(offerAliasOfRollAppOne.OfferPrice.AddAmount(sdk.NewInt(1))), + }, + wantLaterRollApps: []rollapp{rollApp_One_By1_SingleAlias, rollApp_Two_By2_SingleAlias}, + wantLaterModuleBalance: sdk.NewInt(1), + wantLaterOwnerBalance: sdk.NewInt(2), + wantMinConsumeGas: dymnstypes.OpGasUpdateBuyOrder, + }, + } + for _, tt := range tests { + s.Run(tt.name, func() { + s.RefreshContext() + + if tt.originalModuleBalance.IsPositive() { + s.mintToModuleAccount2(tt.originalModuleBalance) + } + + if tt.originalOwnerBalance.IsPositive() { + s.mintToAccount2(tt.owner, tt.originalOwnerBalance) + } + + for _, rollApp := range tt.existingRollApps { + s.rollAppKeeper.SetRollapp(s.ctx, rollapptypes.Rollapp{ + RollappId: rollApp.rollAppId, + Owner: rollApp.owner, + }) + for _, alias := range rollApp.aliases { + err := s.dymNsKeeper.SetAliasForRollAppId(s.ctx, rollApp.rollAppId, alias) + s.Require().NoError(err) + } + } + + if tt.existingOffer != nil { + err := s.dymNsKeeper.SetBuyOrder(s.ctx, *tt.existingOffer) + s.Require().NoError(err) + + err = s.dymNsKeeper.AddReverseMappingBuyerToBuyOrderRecord(s.ctx, tt.existingOffer.Buyer, tt.existingOffer.Id) + s.Require().NoError(err) + + err = s.dymNsKeeper.AddReverseMappingAssetIdToBuyOrder(s.ctx, tt.existingOffer.AssetId, tt.existingOffer.AssetType, tt.existingOffer.Id) + s.Require().NoError(err) + } + + if tt.preRunSetupFunc != nil { + tt.preRunSetupFunc(s) + } + + resp, err := dymnskeeper.NewMsgServerImpl(s.dymNsKeeper).AcceptBuyOrder(s.ctx, &dymnstypes.MsgAcceptBuyOrder{ + OrderId: tt.buyOrderId, + Owner: tt.owner, + MinAccept: tt.minAccept, + }) + + defer func() { + if s.T().Failed() { + return + } + + if tt.wantLaterOffer != nil { + laterOffer := s.dymNsKeeper.GetBuyOrder(s.ctx, tt.wantLaterOffer.Id) + s.Require().NotNil(laterOffer) + s.Require().Equal(*tt.wantLaterOffer, *laterOffer) + } else { + laterOffer := s.dymNsKeeper.GetBuyOrder(s.ctx, tt.buyOrderId) + s.Require().Nil(laterOffer) + } + + laterModuleBalance := s.moduleBalance2() + s.Require().Equal(tt.wantLaterModuleBalance.String(), laterModuleBalance.String()) + + laterBuyerBalance := s.balance2(tt.owner) + s.Require().Equal(tt.wantLaterOwnerBalance.String(), laterBuyerBalance.String()) + + s.Less(tt.wantMinConsumeGas, s.ctx.GasMeter().GasConsumed()) + + for _, wantLaterRollApp := range tt.wantLaterRollApps { + rollApp, found := s.rollAppKeeper.GetRollapp(s.ctx, wantLaterRollApp.rollAppId) + s.Require().True(found) + if len(wantLaterRollApp.aliases) == 0 { + s.requireRollApp(rollApp.RollappId).HasNoAlias() + } else { + s.requireRollApp(rollApp.RollappId).HasAlias(wantLaterRollApp.aliases...) + } + } + + if tt.afterTestFunc != nil { + tt.afterTestFunc(s) + } + }() + + if tt.wantErr { + s.Require().ErrorContains(err, tt.wantErrContains) + return + } + + s.Require().NoError(err) + s.Require().NotNil(resp) + }) + } +} diff --git a/x/dymns/keeper/msg_server_cancel_buy_order.go b/x/dymns/keeper/msg_server_cancel_buy_order.go new file mode 100644 index 000000000..eef165336 --- /dev/null +++ b/x/dymns/keeper/msg_server_cancel_buy_order.go @@ -0,0 +1,93 @@ +package keeper + +import ( + "context" + + errorsmod "cosmossdk.io/errors" + "github.com/dymensionxyz/gerr-cosmos/gerrc" + + sdk "github.com/cosmos/cosmos-sdk/types" + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" +) + +// CancelBuyOrder is message handler, +// handles canceling a Buy-Order, performed by the buyer who placed the offer. +func (k msgServer) CancelBuyOrder(goCtx context.Context, msg *dymnstypes.MsgCancelBuyOrder) (*dymnstypes.MsgCancelBuyOrderResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + if err := msg.ValidateBasic(); err != nil { + return nil, err + } + + // get the Buy-Order record from store + + bo := k.GetBuyOrder(ctx, msg.OrderId) + if bo == nil { + return nil, errorsmod.Wrapf(gerrc.ErrNotFound, "Buy-Order ID: %s", msg.OrderId) + } + + var resp *dymnstypes.MsgCancelBuyOrderResponse + var err error + + // process the Buy-Order based on the asset type + + if bo.AssetType == dymnstypes.TypeName || bo.AssetType == dymnstypes.TypeAlias { + resp, err = k.processCancelBuyOrder(ctx, msg, *bo) + } else { + err = errorsmod.Wrapf(gerrc.ErrInvalidArgument, "invalid asset type: %s", bo.AssetType) + } + if err != nil { + return nil, err + } + + // charge protocol fee + consumeMinimumGas(ctx, dymnstypes.OpGasCloseBuyOrder, "CancelBuyOrder") + + return resp, nil +} + +// processCancelBuyOrder handles the message handled by CancelBuyOrder, type Dym-Name/Alias. +func (k msgServer) processCancelBuyOrder( + ctx sdk.Context, + msg *dymnstypes.MsgCancelBuyOrder, offer dymnstypes.BuyOrder, +) (*dymnstypes.MsgCancelBuyOrderResponse, error) { + if err := k.validateCancelBuyOrder(ctx, msg, offer); err != nil { + return nil, err + } + + if err := k.RefundBuyOrder(ctx, offer); err != nil { + return nil, err + } + + if err := k.removeBuyOrder(ctx, offer); err != nil { + return nil, err + } + + return &dymnstypes.MsgCancelBuyOrderResponse{}, nil +} + +// validateCancelBuyOrder handles validation for the message handled by CancelBuyOrder, type Dym-Name/Alias. +func (k msgServer) validateCancelBuyOrder(_ sdk.Context, msg *dymnstypes.MsgCancelBuyOrder, offer dymnstypes.BuyOrder) error { + if offer.Buyer != msg.Buyer { + return errorsmod.Wrap(gerrc.ErrPermissionDenied, "not the owner of the offer") + } + + return nil +} + +// removeBuyOrder removes the Buy-Order from the store and the reverse mappings. +func (k msgServer) removeBuyOrder(ctx sdk.Context, offer dymnstypes.BuyOrder) error { + k.DeleteBuyOrder(ctx, offer.Id) + + err := k.RemoveReverseMappingBuyerToBuyOrder(ctx, offer.Buyer, offer.Id) + if err != nil { + return err + } + + err = k.RemoveReverseMappingAssetIdToBuyOrder(ctx, offer.AssetId, offer.AssetType, offer.Id) + if err != nil { + return err + } + + return nil +} diff --git a/x/dymns/keeper/msg_server_cancel_buy_order_test.go b/x/dymns/keeper/msg_server_cancel_buy_order_test.go new file mode 100644 index 000000000..669648ab3 --- /dev/null +++ b/x/dymns/keeper/msg_server_cancel_buy_order_test.go @@ -0,0 +1,707 @@ +package keeper_test + +import ( + sdkmath "cosmossdk.io/math" + rollapptypes "github.com/dymensionxyz/dymension/v3/x/rollapp/types" + + "github.com/dymensionxyz/gerr-cosmos/gerrc" + + sdk "github.com/cosmos/cosmos-sdk/types" + dymnskeeper "github.com/dymensionxyz/dymension/v3/x/dymns/keeper" + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" +) + +func (s *KeeperTestSuite) Test_msgServer_CancelBuyOrder_DymName() { + const minOfferPrice = 5 + + // the number values used in this test will be multiplied by this value + priceMultiplier := sdk.NewInt(1e18) + + minOfferPriceCoin := sdk.NewCoin(s.priceDenom(), sdk.NewInt(minOfferPrice).Mul(priceMultiplier)) + + buyerA := testAddr(1).bech32() + anotherBuyerA := testAddr(2).bech32() + ownerA := testAddr(3).bech32() + + s.updateModuleParams(func(moduleParams dymnstypes.Params) dymnstypes.Params { + moduleParams.Price.MinOfferPrice = minOfferPriceCoin.Amount + // force enable trading + moduleParams.Misc.EnableTradingName = true + moduleParams.Misc.EnableTradingAlias = true + return moduleParams + }) + s.SaveCurrentContext() + + s.Run("reject if message not pass validate basic", func() { + _, err := dymnskeeper.NewMsgServerImpl(s.dymNsKeeper).CancelBuyOrder(s.ctx, &dymnstypes.MsgCancelBuyOrder{}) + s.Require().ErrorContains(err, gerrc.ErrInvalidArgument.Error()) + }) + + dymName := &dymnstypes.DymName{ + Name: "a", + Owner: ownerA, + Controller: ownerA, + ExpireAt: s.now.Unix() + 100, + } + + offer := &dymnstypes.BuyOrder{ + Id: "101", + AssetId: dymName.Name, + AssetType: dymnstypes.TypeName, + Buyer: buyerA, + OfferPrice: minOfferPriceCoin, + } + + offerByAnother := &dymnstypes.BuyOrder{ + Id: "10999", + AssetId: dymName.Name, + AssetType: dymnstypes.TypeName, + Buyer: anotherBuyerA, + OfferPrice: minOfferPriceCoin, + } + + tests := []struct { + name string + existingDymName *dymnstypes.DymName + existingOffer *dymnstypes.BuyOrder + buyOrderId string + buyer string + originalModuleBalance sdkmath.Int + originalBuyerBalance sdkmath.Int + preRunSetupFunc func(s *KeeperTestSuite) + wantErr bool + wantErrContains string + wantLaterOffer *dymnstypes.BuyOrder + wantLaterModuleBalance sdkmath.Int + wantLaterBuyerBalance sdkmath.Int + wantMinConsumeGas sdk.Gas + afterTestFunc func(s *KeeperTestSuite) + }{ + { + name: "pass - can cancel offer", + existingDymName: dymName, + existingOffer: offer, + buyOrderId: offer.Id, + buyer: offer.Buyer, + originalModuleBalance: offer.OfferPrice.Amount, + originalBuyerBalance: sdk.NewInt(0), + wantErr: false, + wantLaterOffer: nil, + wantLaterModuleBalance: sdk.NewInt(0), + wantLaterBuyerBalance: offer.OfferPrice.Amount, + wantMinConsumeGas: dymnstypes.OpGasCloseBuyOrder, + }, + { + name: "pass - cancel offer will refund the buyer", + existingDymName: dymName, + existingOffer: offer, + buyOrderId: offer.Id, + buyer: offer.Buyer, + originalModuleBalance: offer.OfferPrice.Amount.AddRaw(1), + originalBuyerBalance: sdk.NewInt(2), + wantErr: false, + wantLaterOffer: nil, + wantLaterModuleBalance: sdk.NewInt(1), + wantLaterBuyerBalance: offer.OfferPrice.Amount.AddRaw(2), + wantMinConsumeGas: dymnstypes.OpGasCloseBuyOrder, + }, + { + name: "pass - cancel offer will remove the offer record", + existingDymName: dymName, + existingOffer: offer, + buyOrderId: offer.Id, + buyer: offer.Buyer, + originalModuleBalance: offer.OfferPrice.Amount, + originalBuyerBalance: sdk.NewInt(0), + preRunSetupFunc: func(s *KeeperTestSuite) { + s.Require().NotNil(s.dymNsKeeper.GetBuyOrder(s.ctx, offer.Id)) + }, + wantErr: false, + wantLaterOffer: nil, + wantLaterModuleBalance: sdk.NewInt(0), + wantLaterBuyerBalance: offer.OfferPrice.Amount, + wantMinConsumeGas: dymnstypes.OpGasCloseBuyOrder, + afterTestFunc: func(s *KeeperTestSuite) { + s.Require().Nil(s.dymNsKeeper.GetBuyOrder(s.ctx, offer.Id)) + }, + }, + { + name: "pass - cancel offer will remove reverse mapping records", + existingDymName: dymName, + existingOffer: offer, + buyOrderId: offer.Id, + buyer: offer.Buyer, + originalModuleBalance: offer.OfferPrice.Amount, + originalBuyerBalance: sdk.NewInt(0), + preRunSetupFunc: func(s *KeeperTestSuite) { + buyOrders, err := s.dymNsKeeper.GetBuyOrdersByBuyer(s.ctx, offer.Buyer) + s.Require().NoError(err) + s.Require().Len(buyOrders, 1) + + buyOrders, err = s.dymNsKeeper.GetBuyOrdersOfDymName(s.ctx, offer.AssetId) + s.Require().NoError(err) + s.Require().Len(buyOrders, 1) + }, + wantErr: false, + wantLaterOffer: nil, + wantLaterModuleBalance: sdk.NewInt(0), + wantLaterBuyerBalance: offer.OfferPrice.Amount, + wantMinConsumeGas: dymnstypes.OpGasCloseBuyOrder, + afterTestFunc: func(s *KeeperTestSuite) { + buyOrders, err := s.dymNsKeeper.GetBuyOrdersByBuyer(s.ctx, offer.Buyer) + s.Require().NoError(err) + s.Require().Empty(buyOrders) + + buyOrders, err = s.dymNsKeeper.GetBuyOrdersOfDymName(s.ctx, offer.AssetId) + s.Require().NoError(err) + s.Require().Empty(buyOrders) + }, + }, + { + name: "pass - can cancel offer when trading Dym-Name is disabled", + existingDymName: dymName, + existingOffer: offer, + buyOrderId: offer.Id, + buyer: offer.Buyer, + originalModuleBalance: offer.OfferPrice.Amount, + originalBuyerBalance: sdk.NewInt(0), + preRunSetupFunc: func(s *KeeperTestSuite) { + s.updateModuleParams(func(moduleParams dymnstypes.Params) dymnstypes.Params { + moduleParams.Misc.EnableTradingName = false + return moduleParams + }) + }, + wantErr: false, + wantLaterOffer: nil, + wantLaterModuleBalance: sdk.NewInt(0), + wantLaterBuyerBalance: offer.OfferPrice.Amount, + wantMinConsumeGas: dymnstypes.OpGasCloseBuyOrder, + }, + { + name: "fail - cannot cancel non-existing offer", + existingDymName: dymName, + existingOffer: nil, + buyOrderId: "102142142", + buyer: buyerA, + originalModuleBalance: sdk.NewInt(1), + originalBuyerBalance: sdk.NewInt(2), + wantErr: true, + wantErrContains: "Buy-Order ID: 102142142: not found", + wantLaterOffer: nil, + wantLaterModuleBalance: sdk.NewInt(1), + wantLaterBuyerBalance: sdk.NewInt(2), + wantMinConsumeGas: 1, + }, + { + name: "fail - cannot cancel non-existing offer", + existingDymName: dymName, + existingOffer: offer, + buyOrderId: "102142142", + buyer: offer.Buyer, + originalModuleBalance: sdk.NewInt(1), + originalBuyerBalance: sdk.NewInt(2), + wantErr: true, + wantErrContains: "Buy-Order ID: 102142142: not found", + wantLaterOffer: nil, + wantLaterModuleBalance: sdk.NewInt(1), + wantLaterBuyerBalance: sdk.NewInt(2), + wantMinConsumeGas: 1, + }, + { + name: "fail - cannot cancel offer with different buyer", + existingDymName: dymName, + existingOffer: offerByAnother, + buyOrderId: "10999", + buyer: buyerA, + originalModuleBalance: sdk.NewInt(1), + originalBuyerBalance: sdk.NewInt(2), + wantErr: true, + wantErrContains: "not the owner of the offer", + wantLaterOffer: offerByAnother, + wantLaterModuleBalance: sdk.NewInt(1), + wantLaterBuyerBalance: sdk.NewInt(2), + wantMinConsumeGas: 1, + }, + { + name: "fail - can not cancel if module account does not have enough balance to refund", + existingDymName: dymName, + existingOffer: offer, + buyOrderId: offer.Id, + buyer: buyerA, + originalModuleBalance: sdk.NewInt(0), + originalBuyerBalance: sdk.NewInt(2), + wantErr: true, + wantErrContains: "insufficient funds", + wantLaterOffer: offer, + wantLaterModuleBalance: sdk.NewInt(0), + wantLaterBuyerBalance: sdk.NewInt(2), + wantMinConsumeGas: 1, + }, + } + for _, tt := range tests { + s.Run(tt.name, func() { + s.RefreshContext() + + if tt.originalModuleBalance.IsPositive() { + s.mintToModuleAccount2(tt.originalModuleBalance) + } + + if tt.originalBuyerBalance.IsPositive() { + s.mintToAccount2(tt.buyer, tt.originalBuyerBalance) + } + + if tt.existingDymName != nil { + err := s.dymNsKeeper.SetDymName(s.ctx, *tt.existingDymName) + s.Require().NoError(err) + } + + if tt.existingOffer != nil { + err := s.dymNsKeeper.SetBuyOrder(s.ctx, *tt.existingOffer) + s.Require().NoError(err) + + err = s.dymNsKeeper.AddReverseMappingBuyerToBuyOrderRecord(s.ctx, tt.existingOffer.Buyer, tt.existingOffer.Id) + s.Require().NoError(err) + + err = s.dymNsKeeper.AddReverseMappingAssetIdToBuyOrder(s.ctx, tt.existingOffer.AssetId, tt.existingOffer.AssetType, tt.existingOffer.Id) + s.Require().NoError(err) + } + + if tt.preRunSetupFunc != nil { + tt.preRunSetupFunc(s) + } + + resp, err := dymnskeeper.NewMsgServerImpl(s.dymNsKeeper).CancelBuyOrder(s.ctx, &dymnstypes.MsgCancelBuyOrder{ + OrderId: tt.buyOrderId, + Buyer: tt.buyer, + }) + + defer func() { + if s.T().Failed() { + return + } + + if tt.wantLaterOffer != nil { + laterOffer := s.dymNsKeeper.GetBuyOrder(s.ctx, tt.wantLaterOffer.Id) + s.Require().NotNil(laterOffer) + s.Require().Equal(*tt.wantLaterOffer, *laterOffer) + } else { + laterOffer := s.dymNsKeeper.GetBuyOrder(s.ctx, tt.buyOrderId) + s.Require().Nil(laterOffer) + } + + laterModuleBalance := s.moduleBalance2() + s.Require().Equal(tt.wantLaterModuleBalance.String(), laterModuleBalance.String()) + + laterBuyerBalance := s.balance2(tt.buyer) + s.Require().Equal(tt.wantLaterBuyerBalance.String(), laterBuyerBalance.String()) + + s.Less(tt.wantMinConsumeGas, s.ctx.GasMeter().GasConsumed()) + + if tt.existingDymName != nil { + originalDymName := *tt.existingDymName + laterDymName := s.dymNsKeeper.GetDymName(s.ctx, originalDymName.Name) + s.Require().NotNil(laterDymName) + s.Require().Equal(originalDymName, *laterDymName, "Dym-Name record should not be changed") + } + + if tt.afterTestFunc != nil { + tt.afterTestFunc(s) + } + }() + + if tt.wantErr { + s.Require().ErrorContains(err, tt.wantErrContains) + s.Nil(resp) + return + } + + s.Require().NoError(err) + s.Require().NotNil(resp) + }) + } +} + +//goland:noinspection GoSnakeCaseUsage +func (s *KeeperTestSuite) Test_msgServer_CancelBuyOrder_Alias() { + const minOfferPrice = 5 + + // the number values used in this test will be multiplied by this value + priceMultiplier := sdk.NewInt(1e18) + + minOfferPriceCoin := sdk.NewCoin(s.priceDenom(), sdk.NewInt(minOfferPrice).Mul(priceMultiplier)) + + creator_1_asOwner := testAddr(1).bech32() + creator_2_asBuyer := testAddr(2).bech32() + creator_3_asAnotherBuyer := testAddr(3).bech32() + + s.updateModuleParams(func(moduleParams dymnstypes.Params) dymnstypes.Params { + moduleParams.Price.MinOfferPrice = minOfferPriceCoin.Amount + // force enable trading + moduleParams.Misc.EnableTradingName = true + moduleParams.Misc.EnableTradingAlias = true + return moduleParams + }) + s.SaveCurrentContext() + + s.Run("reject if message not pass validate basic", func() { + _, err := dymnskeeper.NewMsgServerImpl(s.dymNsKeeper).CancelBuyOrder(s.ctx, &dymnstypes.MsgCancelBuyOrder{}) + s.Require().ErrorContains(err, gerrc.ErrInvalidArgument.Error()) + }) + + type rollapp struct { + rollAppId string + creator string + aliases []string + } + + rollApp_One_By1 := rollapp{ + rollAppId: "rollapp_1-1", + creator: creator_1_asOwner, + aliases: []string{"one"}, + } + rollApp_Two_By2 := rollapp{ + rollAppId: "rollapp_2-2", + creator: creator_2_asBuyer, + aliases: []string{"two"}, + } + rollApp_Three_By3 := rollapp{ + rollAppId: "rollapp_3-3", + creator: creator_3_asAnotherBuyer, + aliases: []string{}, + } + + offerAliasOfRollAppOne := &dymnstypes.BuyOrder{ + Id: dymnstypes.CreateBuyOrderId(dymnstypes.TypeAlias, 1), + AssetId: rollApp_One_By1.aliases[0], + AssetType: dymnstypes.TypeAlias, + Params: []string{rollApp_Two_By2.rollAppId}, + Buyer: rollApp_Two_By2.creator, + OfferPrice: minOfferPriceCoin, + } + + offerAliasOfRollAppOneByAnother := &dymnstypes.BuyOrder{ + Id: dymnstypes.CreateBuyOrderId(dymnstypes.TypeAlias, 2), + AssetId: rollApp_One_By1.aliases[0], + AssetType: dymnstypes.TypeAlias, + Params: []string{rollApp_Three_By3.rollAppId}, + Buyer: rollApp_Three_By3.creator, + OfferPrice: minOfferPriceCoin, + } + + tests := []struct { + name string + existingRollApps []rollapp + existingOffer *dymnstypes.BuyOrder + buyOrderId string + buyer string + originalModuleBalance sdkmath.Int + originalBuyerBalance sdkmath.Int + preRunSetupFunc func(s *KeeperTestSuite) + wantErr bool + wantErrContains string + wantLaterOffer *dymnstypes.BuyOrder + wantLaterModuleBalance sdkmath.Int + wantLaterBuyerBalance sdkmath.Int + wantMinConsumeGas sdk.Gas + afterTestFunc func(s *KeeperTestSuite) + }{ + { + name: "pass - can cancel offer", + existingRollApps: []rollapp{rollApp_One_By1, rollApp_Two_By2}, + existingOffer: offerAliasOfRollAppOne, + buyOrderId: offerAliasOfRollAppOne.Id, + buyer: offerAliasOfRollAppOne.Buyer, + originalModuleBalance: offerAliasOfRollAppOne.OfferPrice.Amount, + originalBuyerBalance: sdk.NewInt(0), + wantErr: false, + wantLaterOffer: nil, + wantLaterModuleBalance: sdk.NewInt(0), + wantLaterBuyerBalance: offerAliasOfRollAppOne.OfferPrice.Amount, + wantMinConsumeGas: dymnstypes.OpGasCloseBuyOrder, + }, + { + name: "pass - cancel offer will refund the buyer", + existingRollApps: []rollapp{rollApp_One_By1, rollApp_Two_By2}, + existingOffer: offerAliasOfRollAppOne, + buyOrderId: offerAliasOfRollAppOne.Id, + buyer: offerAliasOfRollAppOne.Buyer, + originalModuleBalance: offerAliasOfRollAppOne.OfferPrice.Amount.AddRaw(1), + originalBuyerBalance: sdk.NewInt(2), + wantErr: false, + wantLaterOffer: nil, + wantLaterModuleBalance: sdk.NewInt(1), + wantLaterBuyerBalance: offerAliasOfRollAppOne.OfferPrice.Amount.AddRaw(2), + wantMinConsumeGas: dymnstypes.OpGasCloseBuyOrder, + }, + { + name: "pass - cancel offer will remove the offer record", + existingRollApps: []rollapp{rollApp_One_By1, rollApp_Two_By2}, + existingOffer: offerAliasOfRollAppOne, + buyOrderId: offerAliasOfRollAppOne.Id, + buyer: offerAliasOfRollAppOne.Buyer, + originalModuleBalance: offerAliasOfRollAppOne.OfferPrice.Amount, + originalBuyerBalance: sdk.NewInt(0), + preRunSetupFunc: func(s *KeeperTestSuite) { + s.Require().NotNil(s.dymNsKeeper.GetBuyOrder(s.ctx, offerAliasOfRollAppOne.Id)) + }, + wantErr: false, + wantLaterOffer: nil, + wantLaterModuleBalance: sdk.NewInt(0), + wantLaterBuyerBalance: offerAliasOfRollAppOne.OfferPrice.Amount, + wantMinConsumeGas: dymnstypes.OpGasCloseBuyOrder, + afterTestFunc: func(s *KeeperTestSuite) { + s.Require().Nil(s.dymNsKeeper.GetBuyOrder(s.ctx, offerAliasOfRollAppOne.Id)) + }, + }, + { + name: "pass - cancel offer will remove reverse mapping records", + existingRollApps: []rollapp{rollApp_One_By1, rollApp_Two_By2}, + existingOffer: offerAliasOfRollAppOne, + buyOrderId: offerAliasOfRollAppOne.Id, + buyer: offerAliasOfRollAppOne.Buyer, + originalModuleBalance: offerAliasOfRollAppOne.OfferPrice.Amount, + originalBuyerBalance: sdk.NewInt(0), + preRunSetupFunc: func(s *KeeperTestSuite) { + orderIds, err := s.dymNsKeeper.GetBuyOrdersByBuyer(s.ctx, offerAliasOfRollAppOne.Buyer) + s.Require().NoError(err) + s.Require().Len(orderIds, 1) + + orderIds, err = s.dymNsKeeper.GetBuyOrdersOfAlias(s.ctx, offerAliasOfRollAppOne.AssetId) + s.Require().NoError(err) + s.Require().Len(orderIds, 1) + }, + wantErr: false, + wantLaterOffer: nil, + wantLaterModuleBalance: sdk.NewInt(0), + wantLaterBuyerBalance: offerAliasOfRollAppOne.OfferPrice.Amount, + wantMinConsumeGas: dymnstypes.OpGasCloseBuyOrder, + afterTestFunc: func(s *KeeperTestSuite) { + orderIds, err := s.dymNsKeeper.GetBuyOrdersByBuyer(s.ctx, offerAliasOfRollAppOne.Buyer) + s.Require().NoError(err) + s.Require().Empty(orderIds) + + orderIds, err = s.dymNsKeeper.GetBuyOrdersOfAlias(s.ctx, offerAliasOfRollAppOne.AssetId) + s.Require().NoError(err) + s.Require().Empty(orderIds) + }, + }, + { + name: "pass - cancel offer will NOT remove reverse mapping records of other offers", + existingRollApps: []rollapp{rollApp_One_By1, rollApp_Two_By2}, + existingOffer: offerAliasOfRollAppOne, + buyOrderId: offerAliasOfRollAppOne.Id, + buyer: offerAliasOfRollAppOne.Buyer, + originalModuleBalance: offerAliasOfRollAppOne.OfferPrice.Amount, + originalBuyerBalance: sdk.NewInt(0), + preRunSetupFunc: func(s *KeeperTestSuite) { + err := s.dymNsKeeper.SetBuyOrder(s.ctx, *offerAliasOfRollAppOneByAnother) + s.Require().NoError(err) + + err = s.dymNsKeeper.AddReverseMappingBuyerToBuyOrderRecord( + s.ctx, + offerAliasOfRollAppOneByAnother.Buyer, + offerAliasOfRollAppOneByAnother.Id, + ) + s.Require().NoError(err) + + err = s.dymNsKeeper.AddReverseMappingAssetIdToBuyOrder( + s.ctx, + offerAliasOfRollAppOneByAnother.AssetId, offerAliasOfRollAppOneByAnother.AssetType, + offerAliasOfRollAppOneByAnother.Id, + ) + s.Require().NoError(err) + + orderIds, err := s.dymNsKeeper.GetBuyOrdersByBuyer(s.ctx, offerAliasOfRollAppOne.Buyer) + s.Require().NoError(err) + s.Require().Len(orderIds, 1) + + orderIds, err = s.dymNsKeeper.GetBuyOrdersOfAlias(s.ctx, offerAliasOfRollAppOne.AssetId) + s.Require().NoError(err) + s.Require().Len(orderIds, 2) + }, + wantErr: false, + wantLaterOffer: nil, + wantLaterModuleBalance: sdk.NewInt(0), + wantLaterBuyerBalance: offerAliasOfRollAppOne.OfferPrice.Amount, + wantMinConsumeGas: dymnstypes.OpGasCloseBuyOrder, + afterTestFunc: func(s *KeeperTestSuite) { + orderIds, err := s.dymNsKeeper.GetBuyOrdersByBuyer(s.ctx, offerAliasOfRollAppOne.Buyer) + s.Require().NoError(err) + s.Require().Empty(orderIds) + + orderIds, err = s.dymNsKeeper.GetBuyOrdersOfAlias(s.ctx, offerAliasOfRollAppOne.AssetId) + s.Require().NoError(err) + s.Require().Len(orderIds, 1) + s.Require().Equal(offerAliasOfRollAppOneByAnother.Id, orderIds[0].Id) + }, + }, + { + name: "pass - can cancel offer when trading Alias is disabled", + existingRollApps: []rollapp{rollApp_One_By1, rollApp_Two_By2}, + existingOffer: offerAliasOfRollAppOne, + buyOrderId: offerAliasOfRollAppOne.Id, + buyer: offerAliasOfRollAppOne.Buyer, + originalModuleBalance: offerAliasOfRollAppOne.OfferPrice.Amount, + originalBuyerBalance: sdk.NewInt(0), + preRunSetupFunc: func(s *KeeperTestSuite) { + s.updateModuleParams(func(moduleParams dymnstypes.Params) dymnstypes.Params { + moduleParams.Misc.EnableTradingAlias = false + return moduleParams + }) + }, + wantErr: false, + wantLaterOffer: nil, + wantLaterModuleBalance: sdk.NewInt(0), + wantLaterBuyerBalance: offerAliasOfRollAppOne.OfferPrice.Amount, + wantMinConsumeGas: dymnstypes.OpGasCloseBuyOrder, + }, + { + name: "fail - cannot cancel non-existing offer", + existingRollApps: []rollapp{rollApp_One_By1, rollApp_Two_By2}, + existingOffer: nil, + buyOrderId: "202142142", + buyer: creator_2_asBuyer, + originalModuleBalance: sdk.NewInt(1), + originalBuyerBalance: sdk.NewInt(2), + wantErr: true, + wantErrContains: "Buy-Order ID: 202142142: not found", + wantLaterOffer: nil, + wantLaterModuleBalance: sdk.NewInt(1), + wantLaterBuyerBalance: sdk.NewInt(2), + wantMinConsumeGas: 1, + }, + { + name: "fail - cannot cancel non-existing offer", + existingRollApps: []rollapp{rollApp_One_By1, rollApp_Two_By2}, + existingOffer: offerAliasOfRollAppOne, + buyOrderId: "202142142", + buyer: offerAliasOfRollAppOne.Buyer, + originalModuleBalance: sdk.NewInt(1), + originalBuyerBalance: sdk.NewInt(2), + wantErr: true, + wantErrContains: "Buy-Order ID: 202142142: not found", + wantLaterOffer: nil, + wantLaterModuleBalance: sdk.NewInt(1), + wantLaterBuyerBalance: sdk.NewInt(2), + wantMinConsumeGas: 1, + }, + { + name: "fail - cannot cancel offer with different buyer", + existingRollApps: []rollapp{rollApp_One_By1, rollApp_Two_By2}, + existingOffer: offerAliasOfRollAppOneByAnother, + buyOrderId: offerAliasOfRollAppOneByAnother.Id, + buyer: creator_2_asBuyer, + originalModuleBalance: sdk.NewInt(1), + originalBuyerBalance: sdk.NewInt(2), + wantErr: true, + wantErrContains: "not the owner of the offer", + wantLaterOffer: offerAliasOfRollAppOneByAnother, + wantLaterModuleBalance: sdk.NewInt(1), + wantLaterBuyerBalance: sdk.NewInt(2), + wantMinConsumeGas: 1, + }, + { + name: "fail - can not cancel if module account does not have enough balance to refund", + existingRollApps: []rollapp{rollApp_One_By1, rollApp_Two_By2}, + existingOffer: offerAliasOfRollAppOne, + buyOrderId: offerAliasOfRollAppOne.Id, + buyer: creator_2_asBuyer, + originalModuleBalance: sdk.NewInt(0), + originalBuyerBalance: sdk.NewInt(2), + wantErr: true, + wantErrContains: "insufficient funds", + wantLaterOffer: offerAliasOfRollAppOne, + wantLaterModuleBalance: sdk.NewInt(0), + wantLaterBuyerBalance: sdk.NewInt(2), + wantMinConsumeGas: 1, + }, + } + for _, tt := range tests { + s.Run(tt.name, func() { + s.RefreshContext() + + if tt.originalModuleBalance.IsPositive() { + s.mintToModuleAccount2(tt.originalModuleBalance) + } + + if tt.originalBuyerBalance.IsPositive() { + s.mintToAccount2(tt.buyer, tt.originalBuyerBalance) + } + + for _, rollApp := range tt.existingRollApps { + s.rollAppKeeper.SetRollapp(s.ctx, rollapptypes.Rollapp{ + RollappId: rollApp.rollAppId, + Owner: rollApp.creator, + }) + for _, alias := range rollApp.aliases { + err := s.dymNsKeeper.SetAliasForRollAppId(s.ctx, rollApp.rollAppId, alias) + s.Require().NoError(err) + } + } + + if tt.existingOffer != nil { + err := s.dymNsKeeper.SetBuyOrder(s.ctx, *tt.existingOffer) + s.Require().NoError(err) + + err = s.dymNsKeeper.AddReverseMappingBuyerToBuyOrderRecord(s.ctx, tt.existingOffer.Buyer, tt.existingOffer.Id) + s.Require().NoError(err) + + err = s.dymNsKeeper.AddReverseMappingAssetIdToBuyOrder(s.ctx, tt.existingOffer.AssetId, tt.existingOffer.AssetType, tt.existingOffer.Id) + s.Require().NoError(err) + } + + if tt.preRunSetupFunc != nil { + tt.preRunSetupFunc(s) + } + + resp, err := dymnskeeper.NewMsgServerImpl(s.dymNsKeeper).CancelBuyOrder(s.ctx, &dymnstypes.MsgCancelBuyOrder{ + OrderId: tt.buyOrderId, + Buyer: tt.buyer, + }) + + defer func() { + if s.T().Failed() { + return + } + + if tt.wantLaterOffer != nil { + laterOffer := s.dymNsKeeper.GetBuyOrder(s.ctx, tt.wantLaterOffer.Id) + s.Require().NotNil(laterOffer) + s.Require().Equal(*tt.wantLaterOffer, *laterOffer) + } else { + laterOffer := s.dymNsKeeper.GetBuyOrder(s.ctx, tt.buyOrderId) + s.Require().Nil(laterOffer) + } + + laterModuleBalance := s.moduleBalance2() + s.Require().Equal(tt.wantLaterModuleBalance.String(), laterModuleBalance.String()) + + laterBuyerBalance := s.balance2(tt.buyer) + s.Require().Equal(tt.wantLaterBuyerBalance.String(), laterBuyerBalance.String()) + + s.Less(tt.wantMinConsumeGas, s.ctx.GasMeter().GasConsumed()) + + for _, rollApp := range tt.existingRollApps { + if len(rollApp.aliases) == 0 { + s.requireRollApp(rollApp.rollAppId).HasNoAlias() + } else { + s.requireRollApp(rollApp.rollAppId).HasAlias(rollApp.aliases...) + } + } + + if tt.afterTestFunc != nil { + tt.afterTestFunc(s) + } + }() + + if tt.wantErr { + s.Require().ErrorContains(err, tt.wantErrContains) + s.Nil(resp) + return + } + + s.Require().NoError(err) + s.Require().NotNil(resp) + }) + } +} diff --git a/x/dymns/keeper/msg_server_cancel_sell_order.go b/x/dymns/keeper/msg_server_cancel_sell_order.go new file mode 100644 index 000000000..a6b1d29cc --- /dev/null +++ b/x/dymns/keeper/msg_server_cancel_sell_order.go @@ -0,0 +1,140 @@ +package keeper + +import ( + "context" + + errorsmod "cosmossdk.io/errors" + "github.com/dymensionxyz/gerr-cosmos/gerrc" + + sdk "github.com/cosmos/cosmos-sdk/types" + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" +) + +// CancelSellOrder is message handler, +// handles canceling Sell-Order, performed by the owner. +// This will stop the advertisement and remove the Dym-Name/Alias sale from the market. +// Can only be performed if no one has placed a bid on the asset. +func (k msgServer) CancelSellOrder(goCtx context.Context, msg *dymnstypes.MsgCancelSellOrder) (*dymnstypes.MsgCancelSellOrderResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + if err := msg.ValidateBasic(); err != nil { + return nil, err + } + + var resp *dymnstypes.MsgCancelSellOrderResponse + var err error + + // process the Sell-Order based on the asset type + + if msg.AssetType == dymnstypes.TypeName { + resp, err = k.processCancelSellOrderWithAssetTypeDymName(ctx, msg) + } else if msg.AssetType == dymnstypes.TypeAlias { + resp, err = k.processCancelSellOrderWithAssetTypeAlias(ctx, msg) + } else { + err = errorsmod.Wrapf(gerrc.ErrInvalidArgument, "invalid asset type: %s", msg.AssetType) + } + if err != nil { + return nil, err + } + + // charge protocol fee + consumeMinimumGas(ctx, dymnstypes.OpGasCloseSellOrder, "CancelSellOrder") + + return resp, nil +} + +// processCancelSellOrderWithAssetTypeDymName handles the message handled by CancelSellOrder, type Dym-Name. +func (k msgServer) processCancelSellOrderWithAssetTypeDymName( + ctx sdk.Context, msg *dymnstypes.MsgCancelSellOrder, +) (*dymnstypes.MsgCancelSellOrderResponse, error) { + if err := k.validateCancelSellOrderWithAssetTypeDymName(ctx, msg); err != nil { + return nil, err + } + + k.DeleteSellOrder(ctx, msg.AssetId, msg.AssetType) + + aSoe := k.GetActiveSellOrdersExpiration(ctx, msg.AssetType) + aSoe.Remove(msg.AssetId) + if err := k.SetActiveSellOrdersExpiration(ctx, aSoe, msg.AssetType); err != nil { + return nil, err + } + + return &dymnstypes.MsgCancelSellOrderResponse{}, nil +} + +// validateCancelSellOrderWithAssetTypeDymName handles validation for the message handled by CancelSellOrder, type Dym-Name. +func (k msgServer) validateCancelSellOrderWithAssetTypeDymName( + ctx sdk.Context, msg *dymnstypes.MsgCancelSellOrder, +) error { + dymName := k.GetDymName(ctx, msg.AssetId) + if dymName == nil { + return errorsmod.Wrapf(gerrc.ErrNotFound, "Dym-Name: %s", msg.AssetId) + } + + if dymName.Owner != msg.Owner { + return errorsmod.Wrap(gerrc.ErrPermissionDenied, "not the owner of the Dym-Name") + } + + so := k.GetSellOrder(ctx, msg.AssetId, msg.AssetType) + if so == nil { + return errorsmod.Wrapf(gerrc.ErrNotFound, "Sell-Order: %s", msg.AssetId) + } + + if so.HasExpiredAtCtx(ctx) { + return errorsmod.Wrap(gerrc.ErrFailedPrecondition, "cannot cancel an expired order") + } + + if so.HighestBid != nil { + return errorsmod.Wrap(gerrc.ErrFailedPrecondition, "cannot cancel once bid placed") + } + + return nil +} + +// processCancelSellOrderWithAssetTypeAlias handles the message handled by CancelSellOrder, type Alias. +func (k msgServer) processCancelSellOrderWithAssetTypeAlias( + ctx sdk.Context, msg *dymnstypes.MsgCancelSellOrder, +) (*dymnstypes.MsgCancelSellOrderResponse, error) { + if err := k.validateCancelSellOrderWithAssetTypeAlias(ctx, msg); err != nil { + return nil, err + } + + k.DeleteSellOrder(ctx, msg.AssetId, msg.AssetType) + + aSoe := k.GetActiveSellOrdersExpiration(ctx, msg.AssetType) + aSoe.Remove(msg.AssetId) + if err := k.SetActiveSellOrdersExpiration(ctx, aSoe, msg.AssetType); err != nil { + return nil, err + } + + return &dymnstypes.MsgCancelSellOrderResponse{}, nil +} + +// validateCancelSellOrderWithAssetTypeAlias handles validation for the message handled by CancelSellOrder, type Alias. +func (k msgServer) validateCancelSellOrderWithAssetTypeAlias( + ctx sdk.Context, msg *dymnstypes.MsgCancelSellOrder, +) error { + existingRollAppIdUsingAlias, found := k.GetRollAppIdByAlias(ctx, msg.AssetId) + if !found { + return errorsmod.Wrapf(gerrc.ErrNotFound, "alias is not in-used: %s", msg.AssetId) + } + + if !k.IsRollAppCreator(ctx, existingRollAppIdUsingAlias, msg.Owner) { + return errorsmod.Wrapf(gerrc.ErrPermissionDenied, "not the owner of the RollApp") + } + + so := k.GetSellOrder(ctx, msg.AssetId, msg.AssetType) + if so == nil { + return errorsmod.Wrapf(gerrc.ErrNotFound, "Sell-Order: %s", msg.AssetId) + } + + if so.HasExpiredAtCtx(ctx) { + return errorsmod.Wrap(gerrc.ErrFailedPrecondition, "cannot cancel an expired order") + } + + if so.HighestBid != nil { + return errorsmod.Wrap(gerrc.ErrFailedPrecondition, "cannot cancel once bid placed") + } + + return nil +} diff --git a/x/dymns/keeper/msg_server_cancel_sell_order_test.go b/x/dymns/keeper/msg_server_cancel_sell_order_test.go new file mode 100644 index 000000000..2a9c9f5e1 --- /dev/null +++ b/x/dymns/keeper/msg_server_cancel_sell_order_test.go @@ -0,0 +1,486 @@ +package keeper_test + +import ( + "fmt" + + "github.com/dymensionxyz/sdk-utils/utils/uptr" + + "github.com/dymensionxyz/gerr-cosmos/gerrc" + + sdk "github.com/cosmos/cosmos-sdk/types" + dymnskeeper "github.com/dymensionxyz/dymension/v3/x/dymns/keeper" + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" +) + +func (s *KeeperTestSuite) Test_msgServer_CancelSellOrder_DymName() { + msgServer := dymnskeeper.NewMsgServerImpl(s.dymNsKeeper) + + ownerA := testAddr(1).bech32() + notOwnerA := testAddr(2).bech32() + bidderA := testAddr(3).bech32() + + dymName1 := dymnstypes.DymName{ + Name: "a", + Owner: ownerA, + Controller: ownerA, + ExpireAt: s.now.Unix() + 100, + } + err := s.dymNsKeeper.SetDymName(s.ctx, dymName1) + s.Require().NoError(err) + + dymName2 := dymnstypes.DymName{ + Name: "b", + Owner: ownerA, + Controller: ownerA, + ExpireAt: s.now.Unix() + 100, + } + err = s.dymNsKeeper.SetDymName(s.ctx, dymName2) + s.Require().NoError(err) + + dymNames := s.dymNsKeeper.GetAllNonExpiredDymNames(s.ctx) + s.Require().Len(dymNames, 2) + + s.Run("do not process message that not pass basic validation", func() { + resp, err := msgServer.CancelSellOrder(sdk.WrapSDKContext(s.ctx), &dymnstypes.MsgCancelSellOrder{ + AssetId: "abc", + Owner: "0x1", // invalid owner + }) + + s.Require().ErrorContains(err, gerrc.ErrInvalidArgument.Error()) + + s.Require().Nil(resp) + }) + + s.Run("do not process message that refer to non-existing Dym-Name", func() { + resp, err := msgServer.CancelSellOrder(sdk.WrapSDKContext(s.ctx), &dymnstypes.MsgCancelSellOrder{ + AssetId: "not-exists", + AssetType: dymnstypes.TypeName, + Owner: ownerA, + }) + s.Require().Error(err) + s.Require().Nil(resp) + s.Require().Contains(err.Error(), "Dym-Name: not-exists: not found") + }) + + s.Run("do not process message that type is Unknown", func() { + resp, err := msgServer.CancelSellOrder(sdk.WrapSDKContext(s.ctx), &dymnstypes.MsgCancelSellOrder{ + AssetId: "asset", + AssetType: dymnstypes.AssetType_AT_UNKNOWN, + Owner: ownerA, + }) + s.Require().Error(err) + s.Require().Nil(resp) + s.Require().Contains(err.Error(), "invalid asset type") + }) + + s.Run("do not process that owner does not match", func() { + so11 := dymnstypes.SellOrder{ + AssetId: dymName1.Name, + AssetType: dymnstypes.TypeName, + ExpireAt: s.now.Unix() + 100, + MinPrice: s.coin(100), + SellPrice: uptr.To(s.coin(300)), + } + err = s.dymNsKeeper.SetSellOrder(s.ctx, so11) + s.Require().NoError(err) + + defer func() { + s.dymNsKeeper.DeleteSellOrder(s.ctx, so11.AssetId, dymnstypes.TypeName) + }() + + resp, err := msgServer.CancelSellOrder(sdk.WrapSDKContext(s.ctx), &dymnstypes.MsgCancelSellOrder{ + AssetId: so11.AssetId, + AssetType: dymnstypes.TypeName, + Owner: notOwnerA, + }) + s.Require().Error(err) + s.Require().Nil(resp) + s.Require().Contains(err.Error(), "not the owner of the Dym-Name") + }) + + s.Run("do not process for Dym-Name that does not have any SO", func() { + resp, err := msgServer.CancelSellOrder(sdk.WrapSDKContext(s.ctx), &dymnstypes.MsgCancelSellOrder{ + AssetId: dymName1.Name, + AssetType: dymnstypes.TypeName, + Owner: ownerA, + }) + s.Require().Error(err) + s.Require().Nil(resp) + s.Require().Contains(err.Error(), fmt.Sprintf("Sell-Order: %s: not found", dymName1.Name)) + }) + + s.Run("can not cancel expired", func() { + so11 := dymnstypes.SellOrder{ + AssetId: dymName1.Name, + AssetType: dymnstypes.TypeName, + ExpireAt: 1, + MinPrice: s.coin(100), + SellPrice: uptr.To(s.coin(300)), + } + err = s.dymNsKeeper.SetSellOrder(s.ctx, so11) + s.Require().NoError(err) + + defer func() { + s.dymNsKeeper.DeleteSellOrder(s.ctx, so11.AssetId, dymnstypes.TypeName) + }() + + resp, err := msgServer.CancelSellOrder(sdk.WrapSDKContext(s.ctx), &dymnstypes.MsgCancelSellOrder{ + AssetId: so11.AssetId, + AssetType: dymnstypes.TypeName, + Owner: ownerA, + }) + s.Require().Error(err) + s.Require().Nil(resp) + s.Require().Contains(err.Error(), "cannot cancel an expired order") + }) + + s.Run("can not cancel once bid placed", func() { + so11 := dymnstypes.SellOrder{ + AssetId: dymName1.Name, + AssetType: dymnstypes.TypeName, + ExpireAt: s.now.Unix() + 100, + MinPrice: s.coin(100), + HighestBid: &dymnstypes.SellOrderBid{ + Bidder: bidderA, + Price: s.coin(300), + }, + } + err = s.dymNsKeeper.SetSellOrder(s.ctx, so11) + s.Require().NoError(err) + + defer func() { + s.dymNsKeeper.DeleteSellOrder(s.ctx, so11.AssetId, dymnstypes.TypeName) + }() + + resp, err := msgServer.CancelSellOrder(sdk.WrapSDKContext(s.ctx), &dymnstypes.MsgCancelSellOrder{ + AssetId: so11.AssetId, + AssetType: dymnstypes.TypeName, + Owner: ownerA, + }) + s.Require().Error(err) + s.Require().Nil(resp) + s.Require().Contains(err.Error(), "cannot cancel once bid placed") + }) + + s.Run("can will remove the active SO expiration mapping record", func() { + aSoe := s.dymNsKeeper.GetActiveSellOrdersExpiration(s.ctx, dymnstypes.TypeName) + + so11 := dymnstypes.SellOrder{ + AssetId: dymName1.Name, + AssetType: dymnstypes.TypeName, + ExpireAt: s.now.Unix() + 100, + MinPrice: s.coin(100), + } + err = s.dymNsKeeper.SetSellOrder(s.ctx, so11) + s.Require().NoError(err) + aSoe.Add(so11.AssetId, so11.ExpireAt) + + so12 := dymnstypes.SellOrder{ + AssetId: dymName2.Name, + AssetType: dymnstypes.TypeName, + ExpireAt: s.now.Unix() + 100, + MinPrice: s.coin(100), + } + err = s.dymNsKeeper.SetSellOrder(s.ctx, so12) + s.Require().NoError(err) + aSoe.Add(so12.AssetId, so12.ExpireAt) + + err = s.dymNsKeeper.SetActiveSellOrdersExpiration(s.ctx, aSoe, dymnstypes.TypeName) + s.Require().NoError(err) + + defer func() { + s.dymNsKeeper.DeleteSellOrder(s.ctx, so11.AssetId, dymnstypes.TypeName) + s.dymNsKeeper.DeleteSellOrder(s.ctx, so12.AssetId, dymnstypes.TypeName) + }() + + resp, err := msgServer.CancelSellOrder(sdk.WrapSDKContext(s.ctx), &dymnstypes.MsgCancelSellOrder{ + AssetId: so11.AssetId, + AssetType: dymnstypes.TypeName, + Owner: ownerA, + }) + s.Require().NoError(err) + s.Require().NotNil(resp) + + s.Require().Nil(s.dymNsKeeper.GetSellOrder(s.ctx, so11.AssetId, dymnstypes.TypeName), "SO should be removed from active") + + aSoe = s.dymNsKeeper.GetActiveSellOrdersExpiration(s.ctx, dymnstypes.TypeName) + + allNames := make(map[string]bool) + for _, record := range aSoe.Records { + allNames[record.AssetId] = true + } + s.Require().NotContains(allNames, so11.AssetId) + s.Require().Contains(allNames, so12.AssetId) + }) + + s.Run("can cancel if satisfied conditions", func() { + moduleParams := s.dymNsKeeper.GetParams(s.ctx) + moduleParams.Misc.EnableTradingName = false // allowed to cancel even if trading is disabled + s.Require().NoError(s.dymNsKeeper.SetParams(s.ctx, moduleParams)) + + so11 := dymnstypes.SellOrder{ + AssetId: dymName1.Name, + AssetType: dymnstypes.TypeName, + ExpireAt: s.now.Unix() + 100, + MinPrice: s.coin(100), + } + err = s.dymNsKeeper.SetSellOrder(s.ctx, so11) + s.Require().NoError(err) + + so12 := dymnstypes.SellOrder{ + AssetId: dymName2.Name, + AssetType: dymnstypes.TypeName, + ExpireAt: s.now.Unix() + 100, + MinPrice: s.coin(100), + } + err = s.dymNsKeeper.SetSellOrder(s.ctx, so12) + s.Require().NoError(err) + + defer func() { + s.dymNsKeeper.DeleteSellOrder(s.ctx, so11.AssetId, dymnstypes.TypeName) + s.dymNsKeeper.DeleteSellOrder(s.ctx, so12.AssetId, dymnstypes.TypeName) + }() + + resp, err := msgServer.CancelSellOrder(sdk.WrapSDKContext(s.ctx), &dymnstypes.MsgCancelSellOrder{ + AssetId: so11.AssetId, + AssetType: dymnstypes.TypeName, + Owner: ownerA, + }) + s.Require().NoError(err) + s.Require().NotNil(resp) + + s.Require().Nil(s.dymNsKeeper.GetSellOrder(s.ctx, so11.AssetId, dymnstypes.TypeName), "SO should be removed from active") + s.Require().NotNil(s.dymNsKeeper.GetSellOrder(s.ctx, dymName2.Name, dymnstypes.TypeName), "other records remaining as-is") + + s.GreaterOrEqual( + s.ctx.GasMeter().GasConsumed(), dymnstypes.OpGasCloseSellOrder, + "should consume params gas", + ) + }) +} + +//goland:noinspection GoSnakeCaseUsage +func (s *KeeperTestSuite) Test_msgServer_CancelSellOrder_Alias() { + msgServer := dymnskeeper.NewMsgServerImpl(s.dymNsKeeper) + + ownerA := testAddr(1).bech32() + anotherA := testAddr(2).bech32() + bidderA := testAddr(3).bech32() + + rollapp_1_ofOwner := *newRollApp("rollapp_1-1").WithOwner(ownerA).WithAlias("one") + rollapp_2_ofOwner := *newRollApp("rollapp_2-1").WithOwner(ownerA).WithAlias("two") + rollapp_3_ofAnother := *newRollApp("rollapp_3-1").WithOwner(anotherA).WithAlias("three") + rollapp_4_ofBidder := *newRollApp("rollapp_4-2").WithOwner(bidderA) + for _, ra := range []rollapp{rollapp_1_ofOwner, rollapp_2_ofOwner, rollapp_3_ofAnother, rollapp_4_ofBidder} { + s.persistRollApp(ra) + } + + s.Run("do not process message that not pass basic validation", func() { + resp, err := msgServer.CancelSellOrder(sdk.WrapSDKContext(s.ctx), &dymnstypes.MsgCancelSellOrder{ + AssetId: rollapp_1_ofOwner.alias, + Owner: "0x1", // invalid owner + }) + + s.Require().ErrorContains(err, gerrc.ErrInvalidArgument.Error()) + s.Require().Nil(resp) + }) + + s.Run("do not process message that refer to non-existing alias", func() { + resp, err := msgServer.CancelSellOrder(sdk.WrapSDKContext(s.ctx), &dymnstypes.MsgCancelSellOrder{ + AssetId: "void", + AssetType: dymnstypes.TypeAlias, + Owner: ownerA, + }) + s.Require().Error(err) + s.Require().Nil(resp) + s.Require().Contains(err.Error(), "alias is not in-used: void: not found") + }) + + s.Run("do not process for Alias that does not have any SO", func() { + resp, err := msgServer.CancelSellOrder(sdk.WrapSDKContext(s.ctx), &dymnstypes.MsgCancelSellOrder{ + AssetId: rollapp_1_ofOwner.alias, + AssetType: dymnstypes.TypeAlias, + Owner: ownerA, + }) + s.Require().Error(err) + s.Require().Nil(resp) + s.Require().Contains(err.Error(), fmt.Sprintf("Sell-Order: %s: not found", rollapp_1_ofOwner.alias)) + }) + + s.Run("do not process that owner does not match", func() { + so11 := dymnstypes.SellOrder{ + AssetId: rollapp_1_ofOwner.alias, + AssetType: dymnstypes.TypeAlias, + ExpireAt: s.now.Unix() + 100, + MinPrice: s.coin(100), + SellPrice: uptr.To(s.coin(300)), + } + err := s.dymNsKeeper.SetSellOrder(s.ctx, so11) + s.Require().NoError(err) + + defer func() { + s.dymNsKeeper.DeleteSellOrder(s.ctx, so11.AssetId, dymnstypes.TypeAlias) + }() + + resp, err := msgServer.CancelSellOrder(sdk.WrapSDKContext(s.ctx), &dymnstypes.MsgCancelSellOrder{ + AssetId: so11.AssetId, + AssetType: dymnstypes.TypeAlias, + Owner: anotherA, + }) + s.Require().Error(err) + s.Require().Nil(resp) + s.Require().Contains(err.Error(), "not the owner of the RollApp") + }) + + s.Run("can not cancel expired order", func() { + so11 := dymnstypes.SellOrder{ + AssetId: rollapp_1_ofOwner.alias, + AssetType: dymnstypes.TypeAlias, + ExpireAt: 1, + MinPrice: s.coin(100), + SellPrice: uptr.To(s.coin(300)), + } + err := s.dymNsKeeper.SetSellOrder(s.ctx, so11) + s.Require().NoError(err) + + defer func() { + s.dymNsKeeper.DeleteSellOrder(s.ctx, so11.AssetId, dymnstypes.TypeAlias) + }() + + resp, err := msgServer.CancelSellOrder(sdk.WrapSDKContext(s.ctx), &dymnstypes.MsgCancelSellOrder{ + AssetId: so11.AssetId, + AssetType: dymnstypes.TypeAlias, + Owner: ownerA, + }) + s.Require().Error(err) + s.Require().Nil(resp) + s.Require().Contains(err.Error(), "cannot cancel an expired order") + }) + + s.Run("can not cancel once bid placed", func() { + so11 := dymnstypes.SellOrder{ + AssetId: rollapp_1_ofOwner.alias, + AssetType: dymnstypes.TypeAlias, + ExpireAt: s.now.Unix() + 100, + MinPrice: s.coin(100), + HighestBid: &dymnstypes.SellOrderBid{ + Bidder: bidderA, + Price: s.coin(300), + Params: []string{rollapp_4_ofBidder.rollAppId}, + }, + } + err := s.dymNsKeeper.SetSellOrder(s.ctx, so11) + s.Require().NoError(err) + + defer func() { + s.dymNsKeeper.DeleteSellOrder(s.ctx, so11.AssetId, dymnstypes.TypeAlias) + }() + + resp, err := msgServer.CancelSellOrder(sdk.WrapSDKContext(s.ctx), &dymnstypes.MsgCancelSellOrder{ + AssetId: so11.AssetId, + AssetType: dymnstypes.TypeAlias, + Owner: ownerA, + }) + s.Require().Error(err) + s.Require().Nil(resp) + s.Require().Contains(err.Error(), "cannot cancel once bid placed") + }) + + s.Run("cancellation will remove the active SO expiration mapping record", func() { + aSoe := s.dymNsKeeper.GetActiveSellOrdersExpiration(s.ctx, dymnstypes.TypeAlias) + + so11 := dymnstypes.SellOrder{ + AssetId: rollapp_1_ofOwner.alias, + AssetType: dymnstypes.TypeAlias, + ExpireAt: s.now.Unix() + 100, + MinPrice: s.coin(100), + } + err := s.dymNsKeeper.SetSellOrder(s.ctx, so11) + s.Require().NoError(err) + aSoe.Add(so11.AssetId, so11.ExpireAt) + + so12 := dymnstypes.SellOrder{ + AssetId: rollapp_2_ofOwner.alias, + AssetType: dymnstypes.TypeAlias, + ExpireAt: s.now.Unix() + 100, + MinPrice: s.coin(100), + } + err = s.dymNsKeeper.SetSellOrder(s.ctx, so12) + s.Require().NoError(err) + aSoe.Add(so12.AssetId, so12.ExpireAt) + + err = s.dymNsKeeper.SetActiveSellOrdersExpiration(s.ctx, aSoe, dymnstypes.TypeAlias) + s.Require().NoError(err) + + defer func() { + s.dymNsKeeper.DeleteSellOrder(s.ctx, so11.AssetId, dymnstypes.TypeAlias) + s.dymNsKeeper.DeleteSellOrder(s.ctx, so12.AssetId, dymnstypes.TypeAlias) + }() + + resp, err := msgServer.CancelSellOrder(sdk.WrapSDKContext(s.ctx), &dymnstypes.MsgCancelSellOrder{ + AssetId: so11.AssetId, + AssetType: dymnstypes.TypeAlias, + Owner: ownerA, + }) + s.Require().NoError(err) + s.Require().NotNil(resp) + + s.Require().Nil(s.dymNsKeeper.GetSellOrder(s.ctx, so11.AssetId, dymnstypes.TypeAlias), "SO should be removed from active") + + aSoe = s.dymNsKeeper.GetActiveSellOrdersExpiration(s.ctx, dymnstypes.TypeAlias) + + allAliases := make(map[string]bool) + for _, record := range aSoe.Records { + allAliases[record.AssetId] = true + } + s.Require().NotContains(allAliases, so11.AssetId) + s.Require().Contains(allAliases, so12.AssetId) + }) + + s.Run("can cancel if satisfied conditions", func() { + moduleParams := s.dymNsKeeper.GetParams(s.ctx) + moduleParams.Misc.EnableTradingAlias = false // allowed to cancel even if trading is disabled + s.Require().NoError(s.dymNsKeeper.SetParams(s.ctx, moduleParams)) + + so11 := dymnstypes.SellOrder{ + AssetId: rollapp_1_ofOwner.alias, + AssetType: dymnstypes.TypeAlias, + ExpireAt: s.now.Unix() + 100, + MinPrice: s.coin(100), + } + err := s.dymNsKeeper.SetSellOrder(s.ctx, so11) + s.Require().NoError(err) + + so12 := dymnstypes.SellOrder{ + AssetId: rollapp_2_ofOwner.alias, + AssetType: dymnstypes.TypeAlias, + ExpireAt: s.now.Unix() + 100, + MinPrice: s.coin(100), + } + err = s.dymNsKeeper.SetSellOrder(s.ctx, so12) + s.Require().NoError(err) + + defer func() { + s.dymNsKeeper.DeleteSellOrder(s.ctx, so11.AssetId, dymnstypes.TypeAlias) + s.dymNsKeeper.DeleteSellOrder(s.ctx, so12.AssetId, dymnstypes.TypeAlias) + }() + + resp, err := msgServer.CancelSellOrder(sdk.WrapSDKContext(s.ctx), &dymnstypes.MsgCancelSellOrder{ + AssetId: so11.AssetId, + AssetType: dymnstypes.TypeAlias, + Owner: ownerA, + }) + s.Require().NoError(err) + s.Require().NotNil(resp) + + s.Require().Nil(s.dymNsKeeper.GetSellOrder(s.ctx, so11.AssetId, dymnstypes.TypeAlias), "SO should be removed from active") + s.Require().NotNil(s.dymNsKeeper.GetSellOrder(s.ctx, so12.AssetId, dymnstypes.TypeAlias), "other records remaining as-is") + + s.GreaterOrEqual( + s.ctx.GasMeter().GasConsumed(), dymnstypes.OpGasCloseSellOrder, + "should consume params gas", + ) + + s.requireAlias(rollapp_1_ofOwner.alias).LinkedToRollApp(rollapp_1_ofOwner.rollAppId) + }) +} diff --git a/x/dymns/keeper/msg_server_place_buy_order.go b/x/dymns/keeper/msg_server_place_buy_order.go new file mode 100644 index 000000000..d0702343b --- /dev/null +++ b/x/dymns/keeper/msg_server_place_buy_order.go @@ -0,0 +1,336 @@ +package keeper + +import ( + "context" + + errorsmod "cosmossdk.io/errors" + "github.com/dymensionxyz/gerr-cosmos/gerrc" + + sdk "github.com/cosmos/cosmos-sdk/types" + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" +) + +// PlaceBuyOrder is message handler, +// handles creating an offer to buy a Dym-Name/Alias, performed by the buyer. +func (k msgServer) PlaceBuyOrder(goCtx context.Context, msg *dymnstypes.MsgPlaceBuyOrder) (*dymnstypes.MsgPlaceBuyOrderResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + if err := msg.ValidateBasic(); err != nil { + return nil, err + } + + priceParams := k.PriceParams(ctx) + miscParams := k.MiscParams(ctx) + + var resp *dymnstypes.MsgPlaceBuyOrderResponse + var err error + + // process the Buy-Order based on the asset type + + if msg.AssetType == dymnstypes.TypeName { + resp, err = k.placeBuyOrderWithAssetTypeDymName(ctx, msg, priceParams, miscParams) + } else if msg.AssetType == dymnstypes.TypeAlias { + resp, err = k.placeBuyOrderWithAssetTypeAlias(ctx, msg, priceParams, miscParams) + } else { + err = errorsmod.Wrapf(gerrc.ErrInvalidArgument, "invalid asset type: %s", msg.AssetType) + } + if err != nil { + return nil, err + } + + // compute protocol fee and charge + var minimumTxGasRequired sdk.Gas + if msg.ContinueOrderId != "" { + minimumTxGasRequired = dymnstypes.OpGasUpdateBuyOrder + } else { + minimumTxGasRequired = dymnstypes.OpGasPutBuyOrder + } + consumeMinimumGas(ctx, minimumTxGasRequired, "PlaceBuyOrder") + + return resp, nil +} + +// placeBuyOrderWithAssetTypeDymName handles the message handled by PlaceBuyOrder, type Dym-Name. +func (k msgServer) placeBuyOrderWithAssetTypeDymName( + ctx sdk.Context, + msg *dymnstypes.MsgPlaceBuyOrder, priceParams dymnstypes.PriceParams, miscParams dymnstypes.MiscParams, +) (*dymnstypes.MsgPlaceBuyOrderResponse, error) { + if !miscParams.EnableTradingName { + return nil, errorsmod.Wrapf(gerrc.ErrFailedPrecondition, "trading of Dym-Name is disabled") + } + + existingOffer, err := k.validatePlaceBuyOrderWithAssetTypeDymName(ctx, msg, priceParams) + if err != nil { + return nil, err + } + + var offer dymnstypes.BuyOrder + var deposit sdk.Coin + + if existingOffer != nil { + deposit = msg.Offer.Sub(existingOffer.OfferPrice) + + offer = *existingOffer + offer.OfferPrice = msg.Offer + + if err := k.SetBuyOrder(ctx, offer); err != nil { + return nil, err + } + } else { + deposit = msg.Offer + + offer = dymnstypes.BuyOrder{ + Id: "", // will be auto-generated + AssetId: msg.AssetId, + AssetType: dymnstypes.TypeName, + Params: msg.Params, + Buyer: msg.Buyer, + OfferPrice: msg.Offer, + } + + offer, err = k.InsertNewBuyOrder(ctx, offer) + if err != nil { + return nil, err + } + + err = k.AddReverseMappingBuyerToBuyOrderRecord(ctx, msg.Buyer, offer.Id) + if err != nil { + return nil, err + } + + err = k.AddReverseMappingAssetIdToBuyOrder(ctx, msg.AssetId, offer.AssetType, offer.Id) + if err != nil { + return nil, err + } + } + + if err := k.bankKeeper.SendCoinsFromAccountToModule(ctx, + sdk.MustAccAddressFromBech32(msg.Buyer), + dymnstypes.ModuleName, + sdk.NewCoins(deposit), + ); err != nil { + return nil, err + } + + return &dymnstypes.MsgPlaceBuyOrderResponse{ + OrderId: offer.Id, + }, nil +} + +// validatePlaceBuyOrderWithAssetTypeDymName handles validation for the message handled by PlaceBuyOrder, type Dym-Name. +func (k msgServer) validatePlaceBuyOrderWithAssetTypeDymName( + ctx sdk.Context, + msg *dymnstypes.MsgPlaceBuyOrder, priceParams dymnstypes.PriceParams, +) (existingOffer *dymnstypes.BuyOrder, err error) { + dymName := k.GetDymNameWithExpirationCheck(ctx, msg.AssetId) + if dymName == nil { + err = errorsmod.Wrapf(gerrc.ErrNotFound, "Dym-Name: %s", msg.AssetId) + return + } + if dymName.Owner == msg.Buyer { + err = errorsmod.Wrapf(gerrc.ErrInvalidArgument, "cannot buy own Dym-Name") + return + } + + if msg.Offer.Denom != priceParams.PriceDenom { + err = errorsmod.Wrapf(gerrc.ErrInvalidArgument, + "invalid offer denomination, only accept %s", priceParams.PriceDenom, + ) + return + } + if msg.Offer.Amount.LT(priceParams.MinOfferPrice) { + err = errorsmod.Wrapf(gerrc.ErrInvalidArgument, + "offer price must be greater than or equal to %s", priceParams.MinOfferPrice.String(), + ) + return + } + + if msg.ContinueOrderId != "" { + existingOffer = k.GetBuyOrder(ctx, msg.ContinueOrderId) + if existingOffer == nil { + err = errorsmod.Wrapf(gerrc.ErrNotFound, "Buy-Order ID: %s", msg.ContinueOrderId) + return + } + if existingOffer.Buyer != msg.Buyer { + err = errorsmod.Wrap(gerrc.ErrPermissionDenied, "not the owner of the offer") + return + } + if existingOffer.AssetId != msg.AssetId { + err = errorsmod.Wrap(gerrc.ErrInvalidArgument, "Dym-Name mismatch with existing offer") + return + } + if existingOffer.AssetType != msg.AssetType { + err = errorsmod.Wrap(gerrc.ErrInvalidArgument, "asset type mismatch with existing offer") + return + } + if existingOffer.OfferPrice.Denom != msg.Offer.Denom { + err = errorsmod.Wrapf( + gerrc.ErrInvalidArgument, + "offer denomination mismatch with existing offer: %s != %s", msg.Offer.Denom, existingOffer.OfferPrice.Denom, + ) + return + } + if msg.Offer.IsLTE(existingOffer.OfferPrice) { + err = errorsmod.Wrapf( + gerrc.ErrInvalidArgument, + "offer price must be greater than existing offer price %s", existingOffer.OfferPrice.String(), + ) + return + } + } + + return +} + +// placeBuyOrderWithAssetTypeAlias handles the message handled by PlaceBuyOrder, type Alias. +func (k msgServer) placeBuyOrderWithAssetTypeAlias( + ctx sdk.Context, + msg *dymnstypes.MsgPlaceBuyOrder, priceParams dymnstypes.PriceParams, miscParams dymnstypes.MiscParams, +) (*dymnstypes.MsgPlaceBuyOrderResponse, error) { + if !miscParams.EnableTradingAlias { + return nil, errorsmod.Wrapf(gerrc.ErrFailedPrecondition, "trading of Alias is disabled") + } + + existingOffer, err := k.validatePlaceBuyOrderWithAssetTypeAlias(ctx, msg, priceParams) + if err != nil { + return nil, err + } + + var offer dymnstypes.BuyOrder + var deposit sdk.Coin + + if existingOffer != nil { + deposit = msg.Offer.Sub(existingOffer.OfferPrice) + + offer = *existingOffer + offer.OfferPrice = msg.Offer + + if err := k.SetBuyOrder(ctx, offer); err != nil { + return nil, err + } + } else { + deposit = msg.Offer + + offer = dymnstypes.BuyOrder{ + Id: "", // will be auto-generated + AssetId: msg.AssetId, + AssetType: dymnstypes.TypeAlias, + Params: msg.Params, + Buyer: msg.Buyer, + OfferPrice: msg.Offer, + } + + offer, err = k.InsertNewBuyOrder(ctx, offer) + if err != nil { + return nil, err + } + + err = k.AddReverseMappingBuyerToBuyOrderRecord(ctx, msg.Buyer, offer.Id) + if err != nil { + return nil, err + } + + err = k.AddReverseMappingAssetIdToBuyOrder(ctx, msg.AssetId, offer.AssetType, offer.Id) + if err != nil { + return nil, err + } + } + + if err := k.bankKeeper.SendCoinsFromAccountToModule(ctx, + sdk.MustAccAddressFromBech32(msg.Buyer), + dymnstypes.ModuleName, + sdk.NewCoins(deposit), + ); err != nil { + return nil, err + } + + return &dymnstypes.MsgPlaceBuyOrderResponse{ + OrderId: offer.Id, + }, nil +} + +// validatePlaceBuyOrderWithAssetTypeAlias handles validation for the message handled by PlaceBuyOrder, type Alias. +func (k msgServer) validatePlaceBuyOrderWithAssetTypeAlias( + ctx sdk.Context, + msg *dymnstypes.MsgPlaceBuyOrder, priceParams dymnstypes.PriceParams, +) (existingOffer *dymnstypes.BuyOrder, err error) { + destinationRollAppId := msg.Params[0] + + if !k.IsRollAppId(ctx, destinationRollAppId) { + err = errorsmod.Wrapf(gerrc.ErrInvalidArgument, "destination Roll-App does not exists: %s", destinationRollAppId) + return + } + + if !k.IsRollAppCreator(ctx, destinationRollAppId, msg.Buyer) { + err = errorsmod.Wrapf(gerrc.ErrPermissionDenied, "not the owner of the RollApp: %s", destinationRollAppId) + return + } + + existingRollAppIdUsingAlias, found := k.GetRollAppIdByAlias(ctx, msg.AssetId) + if !found { + err = errorsmod.Wrapf(gerrc.ErrNotFound, "alias is not in-used: %s", msg.AssetId) + return + } + + if destinationRollAppId == existingRollAppIdUsingAlias { + err = errorsmod.Wrap(gerrc.ErrInvalidArgument, "destination Roll-App ID is the same as the source") + return + } + + if k.IsAliasPresentsInParamsAsAliasOrChainId(ctx, msg.AssetId) { + // Please read the `processActiveAliasSellOrders` method (hooks.go) for more information. + err = errorsmod.Wrapf(gerrc.ErrPermissionDenied, + "prohibited to trade aliases which is reserved for chain-id or alias in module params: %s", msg.AssetId, + ) + return + } + + if msg.Offer.Denom != priceParams.PriceDenom { + err = errorsmod.Wrapf(gerrc.ErrInvalidArgument, + "invalid offer denomination, only accept %s", priceParams.PriceDenom, + ) + return + } + if msg.Offer.Amount.LT(priceParams.MinOfferPrice) { + err = errorsmod.Wrapf(gerrc.ErrInvalidArgument, + "offer price must be greater than or equal to %s", priceParams.MinOfferPrice.String(), + ) + return + } + + if msg.ContinueOrderId != "" { + existingOffer = k.GetBuyOrder(ctx, msg.ContinueOrderId) + if existingOffer == nil { + err = errorsmod.Wrapf(gerrc.ErrNotFound, "Buy-Order ID: %s", msg.ContinueOrderId) + return + } + if existingOffer.Buyer != msg.Buyer { + err = errorsmod.Wrap(gerrc.ErrPermissionDenied, "not the owner of the offer") + return + } + if existingOffer.AssetId != msg.AssetId { + err = errorsmod.Wrap(gerrc.ErrInvalidArgument, "alias mismatch with existing offer") + return + } + if existingOffer.AssetType != msg.AssetType { + err = errorsmod.Wrap(gerrc.ErrInvalidArgument, "asset type mismatch with existing offer") + return + } + if existingOffer.OfferPrice.Denom != msg.Offer.Denom { + err = errorsmod.Wrapf( + gerrc.ErrInvalidArgument, + "offer denomination mismatch with existing offer: %s != %s", msg.Offer.Denom, existingOffer.OfferPrice.Denom, + ) + return + } + if msg.Offer.IsLTE(existingOffer.OfferPrice) { + err = errorsmod.Wrapf( + gerrc.ErrInvalidArgument, + "offer price must be greater than existing offer price %s", existingOffer.OfferPrice.String(), + ) + return + } + } + + return +} diff --git a/x/dymns/keeper/msg_server_place_buy_order_test.go b/x/dymns/keeper/msg_server_place_buy_order_test.go new file mode 100644 index 000000000..93ce0e397 --- /dev/null +++ b/x/dymns/keeper/msg_server_place_buy_order_test.go @@ -0,0 +1,1951 @@ +package keeper_test + +import ( + "time" + + sdkmath "cosmossdk.io/math" + "github.com/dymensionxyz/sdk-utils/utils/uptr" + + rollapptypes "github.com/dymensionxyz/dymension/v3/x/rollapp/types" + + "github.com/dymensionxyz/gerr-cosmos/gerrc" + + sdk "github.com/cosmos/cosmos-sdk/types" + dymnskeeper "github.com/dymensionxyz/dymension/v3/x/dymns/keeper" + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" +) + +func (s *KeeperTestSuite) Test_msgServer_PlaceBuyOrder_DymName() { + const minOfferPrice = 5 + + // the number values used in this test will be multiplied by this value + priceMultiplier := sdk.NewInt(1e18) + + minOfferPriceCoin := sdk.NewCoin(s.priceDenom(), sdk.NewInt(minOfferPrice).Mul(priceMultiplier)) + + ownerA := testAddr(1).bech32() + buyerA := testAddr(2).bech32() + anotherBuyerA := testAddr(3).bech32() + + s.updateModuleParams(func(moduleParams dymnstypes.Params) dymnstypes.Params { + moduleParams.Price.MinOfferPrice = minOfferPriceCoin.Amount + // force enable trading + moduleParams.Misc.EnableTradingName = true + moduleParams.Misc.EnableTradingAlias = true + return moduleParams + }) + s.SaveCurrentContext() + + s.Run("reject if message not pass validate basic", func() { + _, err := dymnskeeper.NewMsgServerImpl(s.dymNsKeeper).PlaceBuyOrder(s.ctx, &dymnstypes.MsgPlaceBuyOrder{}) + s.Require().ErrorContains(err, gerrc.ErrInvalidArgument.Error()) + }) + + dymName := &dymnstypes.DymName{ + Name: "a", + Owner: ownerA, + Controller: ownerA, + ExpireAt: s.now.Add(9 * 365 * 24 * time.Hour).Unix(), + } + + tests := []struct { + name string + existingDymName *dymnstypes.DymName + existingOffer *dymnstypes.BuyOrder + dymName string + buyer string + offer sdk.Coin + existingBuyOrderId string + originalModuleBalance sdkmath.Int + originalBuyerBalance sdkmath.Int + originalAnotherBuyerBalance sdkmath.Int + preRunSetupFunc func(s *KeeperTestSuite) + wantErr bool + wantErrContains string + wantBuyOrderId string + wantLaterOffer *dymnstypes.BuyOrder + wantLaterModuleBalance sdkmath.Int + wantLaterBuyerBalance sdkmath.Int + wantMinConsumeGas sdk.Gas + afterTestFunc func(s *KeeperTestSuite) + }{ + { + name: "pass - can place offer", + existingDymName: dymName, + existingOffer: nil, + dymName: dymName.Name, + buyer: buyerA, + offer: minOfferPriceCoin, + existingBuyOrderId: "", + originalModuleBalance: sdk.NewInt(5), + originalBuyerBalance: minOfferPriceCoin.Amount.AddRaw(2), + wantErr: false, + wantBuyOrderId: "101", + wantLaterOffer: &dymnstypes.BuyOrder{ + Id: "101", + AssetId: dymName.Name, + AssetType: dymnstypes.TypeName, + Buyer: buyerA, + OfferPrice: minOfferPriceCoin, + }, + wantLaterModuleBalance: minOfferPriceCoin.Amount.AddRaw(5), + wantLaterBuyerBalance: sdk.NewInt(2), + wantMinConsumeGas: dymnstypes.OpGasPutBuyOrder, + }, + { + name: "pass - can extends offer", + existingDymName: dymName, + existingOffer: &dymnstypes.BuyOrder{ + Id: "102", + AssetId: dymName.Name, + AssetType: dymnstypes.TypeName, + Buyer: buyerA, + OfferPrice: minOfferPriceCoin, + CounterpartyOfferPrice: nil, + }, + dymName: dymName.Name, + buyer: buyerA, + offer: minOfferPriceCoin.AddAmount(sdk.NewInt(1)), + existingBuyOrderId: "102", + originalModuleBalance: sdk.NewInt(0), + originalBuyerBalance: sdk.NewInt(1), + wantErr: false, + wantBuyOrderId: "102", + wantLaterOffer: &dymnstypes.BuyOrder{ + Id: "102", + AssetId: dymName.Name, + AssetType: dymnstypes.TypeName, + Buyer: buyerA, + OfferPrice: minOfferPriceCoin.AddAmount(sdk.NewInt(1)), + }, + wantLaterModuleBalance: sdk.NewInt(1), + wantLaterBuyerBalance: sdk.NewInt(0), + wantMinConsumeGas: dymnstypes.OpGasUpdateBuyOrder, + }, + { + name: "fail - can NOT extends offer of type mis-match", + existingDymName: dymName, + existingOffer: &dymnstypes.BuyOrder{ + Id: "202", + AssetId: dymName.Name, + AssetType: dymnstypes.TypeAlias, + Params: []string{"rollapp_1-1"}, + Buyer: buyerA, + OfferPrice: minOfferPriceCoin, + }, + dymName: dymName.Name, + buyer: buyerA, + offer: minOfferPriceCoin.AddAmount(sdk.NewInt(1)), + existingBuyOrderId: "202", + originalModuleBalance: sdk.NewInt(0), + originalBuyerBalance: sdk.NewInt(1), + wantErr: true, + wantErrContains: "asset type mismatch with existing offer", + wantBuyOrderId: "102", + wantLaterOffer: &dymnstypes.BuyOrder{ + Id: "202", + AssetId: dymName.Name, + AssetType: dymnstypes.TypeAlias, + Params: []string{"rollapp_1-1"}, + Buyer: buyerA, + OfferPrice: minOfferPriceCoin, + }, + wantLaterModuleBalance: sdk.NewInt(0), + wantLaterBuyerBalance: sdk.NewInt(1), + wantMinConsumeGas: 1, + }, + { + name: "pass - can extends offer with counterparty offer", + existingDymName: dymName, + existingOffer: &dymnstypes.BuyOrder{ + Id: "102", + AssetId: dymName.Name, + AssetType: dymnstypes.TypeName, + Buyer: buyerA, + OfferPrice: minOfferPriceCoin, + CounterpartyOfferPrice: uptr.To(minOfferPriceCoin.AddAmount(sdk.NewInt(3))), + }, + dymName: dymName.Name, + buyer: buyerA, + offer: minOfferPriceCoin.AddAmount(sdk.NewInt(1)), + existingBuyOrderId: "102", + originalModuleBalance: sdk.NewInt(1), + originalBuyerBalance: sdk.NewInt(2), + wantErr: false, + wantBuyOrderId: "102", + wantLaterOffer: &dymnstypes.BuyOrder{ + Id: "102", + AssetId: dymName.Name, + AssetType: dymnstypes.TypeName, + Buyer: buyerA, + OfferPrice: minOfferPriceCoin.AddAmount(sdk.NewInt(1)), + CounterpartyOfferPrice: uptr.To(minOfferPriceCoin.AddAmount(sdk.NewInt(3))), + }, + wantLaterModuleBalance: sdk.NewInt(2), + wantLaterBuyerBalance: sdk.NewInt(1), + wantMinConsumeGas: dymnstypes.OpGasUpdateBuyOrder, + }, + { + name: "pass - can extends offer with offer equals to counterparty offer", + existingDymName: dymName, + existingOffer: &dymnstypes.BuyOrder{ + Id: "102", + AssetId: dymName.Name, + AssetType: dymnstypes.TypeName, + Buyer: buyerA, + OfferPrice: minOfferPriceCoin, + CounterpartyOfferPrice: uptr.To(minOfferPriceCoin.AddAmount(sdk.NewInt(3))), + }, + dymName: dymName.Name, + buyer: buyerA, + offer: minOfferPriceCoin.AddAmount(sdk.NewInt(3)), + existingBuyOrderId: "102", + originalModuleBalance: sdk.NewInt(1), + originalBuyerBalance: sdk.NewInt(5), + wantErr: false, + wantBuyOrderId: "102", + wantLaterOffer: &dymnstypes.BuyOrder{ + Id: "102", + AssetId: dymName.Name, + AssetType: dymnstypes.TypeName, + Buyer: buyerA, + OfferPrice: minOfferPriceCoin.AddAmount(sdk.NewInt(3)), // updated + CounterpartyOfferPrice: uptr.To(minOfferPriceCoin.AddAmount(sdk.NewInt(3))), // unchanged + }, + wantLaterModuleBalance: sdk.NewInt(4), + wantLaterBuyerBalance: sdk.NewInt(2), + wantMinConsumeGas: dymnstypes.OpGasUpdateBuyOrder, + }, + { + name: "pass - can extends offer with offer greater than counterparty offer", + existingDymName: dymName, + existingOffer: &dymnstypes.BuyOrder{ + Id: "102", + AssetId: dymName.Name, + AssetType: dymnstypes.TypeName, + Buyer: buyerA, + OfferPrice: minOfferPriceCoin, + CounterpartyOfferPrice: uptr.To(minOfferPriceCoin.AddAmount(sdk.NewInt(3))), + }, + dymName: dymName.Name, + buyer: buyerA, + offer: minOfferPriceCoin.AddAmount(sdk.NewInt(4)), + existingBuyOrderId: "102", + originalModuleBalance: sdk.NewInt(2), + originalBuyerBalance: sdk.NewInt(5), + wantErr: false, + wantBuyOrderId: "102", + wantLaterOffer: &dymnstypes.BuyOrder{ + Id: "102", + AssetId: dymName.Name, + AssetType: dymnstypes.TypeName, + Buyer: buyerA, + OfferPrice: minOfferPriceCoin.AddAmount(sdk.NewInt(4)), + CounterpartyOfferPrice: uptr.To(minOfferPriceCoin.AddAmount(sdk.NewInt(3))), + }, + wantLaterModuleBalance: sdk.NewInt(6), + wantLaterBuyerBalance: sdk.NewInt(1), + wantMinConsumeGas: dymnstypes.OpGasUpdateBuyOrder, + }, + { + name: "pass - extends an existing offer only take the extra amount instead of all", + existingDymName: dymName, + existingOffer: &dymnstypes.BuyOrder{ + Id: "101", + AssetId: dymName.Name, + AssetType: dymnstypes.TypeName, + Buyer: buyerA, + OfferPrice: minOfferPriceCoin, + CounterpartyOfferPrice: nil, + }, + dymName: dymName.Name, + buyer: buyerA, + offer: minOfferPriceCoin.AddAmount(sdk.NewInt(2)), + existingBuyOrderId: "101", + originalModuleBalance: sdk.NewInt(5), + originalBuyerBalance: sdk.NewInt(3), + wantErr: false, + wantBuyOrderId: "101", + wantLaterOffer: &dymnstypes.BuyOrder{ + Id: "101", + AssetId: dymName.Name, + AssetType: dymnstypes.TypeName, + Buyer: buyerA, + OfferPrice: minOfferPriceCoin.AddAmount(sdk.NewInt(2)), + }, + wantLaterModuleBalance: sdk.NewInt(7), + wantLaterBuyerBalance: sdk.NewInt(1), + wantMinConsumeGas: dymnstypes.OpGasUpdateBuyOrder, + }, + { + name: "fail - can NOT place offer if trading Dym-Name is disabled", + existingDymName: dymName, + existingOffer: nil, + dymName: dymName.Name, + buyer: buyerA, + offer: minOfferPriceCoin, + existingBuyOrderId: "", + originalModuleBalance: sdk.NewInt(5), + originalBuyerBalance: minOfferPriceCoin.Amount.AddRaw(2), + preRunSetupFunc: func(s *KeeperTestSuite) { + moduleParams := s.dymNsKeeper.GetParams(s.ctx) + moduleParams.Misc.EnableTradingName = false + err := s.dymNsKeeper.SetParams(s.ctx, moduleParams) + s.Require().NoError(err) + }, + wantErr: true, + wantErrContains: "trading of Dym-Name is disabled", + wantBuyOrderId: "", + wantLaterOffer: nil, + wantLaterModuleBalance: sdk.NewInt(5), + wantLaterBuyerBalance: minOfferPriceCoin.Amount.AddRaw(2), + wantMinConsumeGas: 1, + }, + { + name: "fail - reject offer for non-existing Dym-Name", + existingDymName: nil, + dymName: "non-exists", + buyer: buyerA, + offer: minOfferPriceCoin, + originalModuleBalance: sdk.NewInt(1), + originalBuyerBalance: minOfferPriceCoin.Amount, + wantErr: true, + wantErrContains: "Dym-Name: non-exists: not found", + wantLaterModuleBalance: sdk.NewInt(1), + wantLaterBuyerBalance: minOfferPriceCoin.Amount, + wantMinConsumeGas: 1, + afterTestFunc: func(s *KeeperTestSuite) { + s.Require().Empty(s.dymNsKeeper.GetAllBuyOrders(s.ctx)) + }, + }, + { + name: "fail - reject offer for expired Dym-Name", + existingDymName: &dymnstypes.DymName{ + Name: "expired", + Owner: ownerA, + Controller: ownerA, + ExpireAt: s.now.Unix() - 1, + }, + dymName: "expired", + buyer: buyerA, + offer: minOfferPriceCoin, + originalModuleBalance: sdk.NewInt(1), + originalBuyerBalance: minOfferPriceCoin.Amount, + wantErr: true, + wantErrContains: "Dym-Name: expired: not found", + wantLaterModuleBalance: sdk.NewInt(1), + wantLaterBuyerBalance: minOfferPriceCoin.Amount, + wantMinConsumeGas: 1, + afterTestFunc: func(s *KeeperTestSuite) { + s.Require().Empty(s.dymNsKeeper.GetAllBuyOrders(s.ctx)) + }, + }, + { + name: "fail - can not offer own Dym-Name", + existingDymName: dymName, + dymName: dymName.Name, + buyer: dymName.Owner, + offer: minOfferPriceCoin, + originalModuleBalance: sdk.NewInt(1), + originalBuyerBalance: minOfferPriceCoin.Amount, + wantErr: true, + wantErrContains: "cannot buy own Dym-Name", + wantLaterModuleBalance: sdk.NewInt(1), + wantLaterBuyerBalance: minOfferPriceCoin.Amount, + wantMinConsumeGas: 1, + afterTestFunc: func(s *KeeperTestSuite) { + s.Require().Empty(s.dymNsKeeper.GetAllBuyOrders(s.ctx)) + }, + }, + { + name: "fail - offer denom must match params", + existingDymName: dymName, + dymName: dymName.Name, + buyer: buyerA, + offer: sdk.Coin{ + Denom: "u" + s.priceDenom(), + Amount: sdk.NewInt(minOfferPrice), + }, + originalModuleBalance: sdk.NewInt(1), + originalBuyerBalance: minOfferPriceCoin.Amount, + wantErr: true, + wantErrContains: "invalid offer denomination, only accept", + wantLaterModuleBalance: sdk.NewInt(1), + wantLaterBuyerBalance: minOfferPriceCoin.Amount, + wantMinConsumeGas: 1, + afterTestFunc: func(s *KeeperTestSuite) { + s.Require().Empty(s.dymNsKeeper.GetAllBuyOrders(s.ctx)) + }, + }, + { + name: "fail - offer price can not lower than min defined in params", + existingDymName: dymName, + dymName: dymName.Name, + buyer: buyerA, + offer: minOfferPriceCoin.SubAmount(sdk.NewInt(1)), + originalModuleBalance: sdk.NewInt(1), + originalBuyerBalance: minOfferPriceCoin.Amount, + wantErr: true, + wantErrContains: "offer price must be greater than or equal to", + wantLaterModuleBalance: sdk.NewInt(1), + wantLaterBuyerBalance: minOfferPriceCoin.Amount, + wantMinConsumeGas: 1, + afterTestFunc: func(s *KeeperTestSuite) { + s.Require().Empty(s.dymNsKeeper.GetAllBuyOrders(s.ctx)) + }, + }, + { + name: "pass - if NOT continue offer, create another and charges full offer price", + existingDymName: dymName, + existingOffer: &dymnstypes.BuyOrder{ + Id: "101", + AssetId: dymName.Name, + AssetType: dymnstypes.TypeName, + Buyer: buyerA, + OfferPrice: minOfferPriceCoin, + CounterpartyOfferPrice: nil, + }, + dymName: dymName.Name, + buyer: buyerA, + offer: minOfferPriceCoin.AddAmount(sdk.NewInt(1)), + existingBuyOrderId: "", + originalModuleBalance: minOfferPriceCoin.Amount, + originalBuyerBalance: minOfferPriceCoin.Amount.AddRaw(2), + preRunSetupFunc: func(s *KeeperTestSuite) { + s.dymNsKeeper.SetCountBuyOrders(s.ctx, 1) + }, + wantErr: false, + wantBuyOrderId: "102", + wantLaterOffer: &dymnstypes.BuyOrder{ + Id: "102", + AssetId: dymName.Name, + AssetType: dymnstypes.TypeName, + Buyer: buyerA, + OfferPrice: minOfferPriceCoin.AddAmount(sdk.NewInt(1)), + }, + wantLaterModuleBalance: minOfferPriceCoin.Amount.Add(minOfferPriceCoin.Amount.AddRaw(1)), + wantLaterBuyerBalance: sdk.NewInt(1), + wantMinConsumeGas: dymnstypes.OpGasPutBuyOrder, + }, + { + name: "fail - continue a non-existing offer", + existingDymName: dymName, + existingOffer: &dymnstypes.BuyOrder{ + Id: "101", + AssetId: dymName.Name, + AssetType: dymnstypes.TypeName, + Buyer: buyerA, + OfferPrice: minOfferPriceCoin, + CounterpartyOfferPrice: nil, + }, + dymName: dymName.Name, + buyer: buyerA, + offer: minOfferPriceCoin.AddAmount(sdk.NewInt(1)), + existingBuyOrderId: "102", + originalModuleBalance: sdk.NewInt(1), + originalBuyerBalance: minOfferPriceCoin.Amount.AddRaw(2), + preRunSetupFunc: func(s *KeeperTestSuite) { + s.dymNsKeeper.SetCountBuyOrders(s.ctx, 1) + }, + wantErr: true, + wantErrContains: "Buy-Order ID: 102: not found", + wantLaterOffer: &dymnstypes.BuyOrder{ + Id: "101", + AssetId: dymName.Name, + AssetType: dymnstypes.TypeName, + Buyer: buyerA, + OfferPrice: minOfferPriceCoin, + }, + wantLaterModuleBalance: sdk.NewInt(1), + wantLaterBuyerBalance: minOfferPriceCoin.Amount.AddRaw(2), + wantMinConsumeGas: 1, + afterTestFunc: func(s *KeeperTestSuite) { + s.Require().Len(s.dymNsKeeper.GetAllBuyOrders(s.ctx), 1) + s.Require().Nil(s.dymNsKeeper.GetBuyOrder(s.ctx, "102")) + }, + }, + { + name: "fail - continue an existing offer but not yours", + existingDymName: dymName, + existingOffer: &dymnstypes.BuyOrder{ + Id: "101", + AssetId: dymName.Name, + AssetType: dymnstypes.TypeName, + Buyer: anotherBuyerA, // not the buyer + OfferPrice: minOfferPriceCoin, + CounterpartyOfferPrice: nil, + }, + dymName: dymName.Name, + buyer: buyerA, // not the existing offer's buyer + offer: minOfferPriceCoin.AddAmount(sdk.NewInt(1)), + existingBuyOrderId: "101", + originalModuleBalance: sdk.NewInt(1), + originalBuyerBalance: minOfferPriceCoin.Amount.AddRaw(2), + preRunSetupFunc: func(s *KeeperTestSuite) { + s.dymNsKeeper.SetCountBuyOrders(s.ctx, 1) + }, + wantErr: true, + wantErrContains: "not the owner of the offer", + wantLaterOffer: &dymnstypes.BuyOrder{ + Id: "101", + AssetId: dymName.Name, + AssetType: dymnstypes.TypeName, + Buyer: anotherBuyerA, + OfferPrice: minOfferPriceCoin, + }, + wantLaterModuleBalance: sdk.NewInt(1), + wantLaterBuyerBalance: minOfferPriceCoin.Amount.AddRaw(2), + wantMinConsumeGas: 1, + afterTestFunc: func(s *KeeperTestSuite) { + s.Require().Len(s.dymNsKeeper.GetAllBuyOrders(s.ctx), 1) + s.Require().Nil(s.dymNsKeeper.GetBuyOrder(s.ctx, "102")) + }, + }, + { + name: "fail - continue an existing offer but the Dym-Name mismatch", + existingDymName: dymName, + existingOffer: &dymnstypes.BuyOrder{ + Id: "101", + AssetId: "another-name", + AssetType: dymnstypes.TypeName, + Buyer: buyerA, + OfferPrice: minOfferPriceCoin, + CounterpartyOfferPrice: nil, + }, + dymName: dymName.Name, + buyer: buyerA, + offer: minOfferPriceCoin.AddAmount(sdk.NewInt(1)), + existingBuyOrderId: "101", + originalModuleBalance: sdk.NewInt(1), + originalBuyerBalance: minOfferPriceCoin.Amount.AddRaw(2), + preRunSetupFunc: func(s *KeeperTestSuite) { + s.dymNsKeeper.SetCountBuyOrders(s.ctx, 1) + }, + wantErr: true, + wantErrContains: "Dym-Name mismatch with existing offer", + wantLaterOffer: &dymnstypes.BuyOrder{ + Id: "101", + AssetId: "another-name", + AssetType: dymnstypes.TypeName, + Buyer: buyerA, + OfferPrice: minOfferPriceCoin, + }, + wantLaterModuleBalance: sdk.NewInt(1), + wantLaterBuyerBalance: minOfferPriceCoin.Amount.AddRaw(2), + wantMinConsumeGas: 1, + afterTestFunc: func(s *KeeperTestSuite) { + s.Require().Len(s.dymNsKeeper.GetAllBuyOrders(s.ctx), 1) + s.Require().Nil(s.dymNsKeeper.GetBuyOrder(s.ctx, "102")) + }, + }, + { + name: "fail - continue an existing offer but mis-match offer denom", + existingDymName: dymName, + existingOffer: &dymnstypes.BuyOrder{ + Id: "101", + AssetId: dymName.Name, + AssetType: dymnstypes.TypeName, + Buyer: buyerA, + OfferPrice: sdk.Coin{ + Denom: "u" + s.priceDenom(), + Amount: sdk.NewInt(minOfferPrice), + }, + CounterpartyOfferPrice: nil, + }, + dymName: dymName.Name, + buyer: buyerA, + offer: minOfferPriceCoin.AddAmount(sdk.NewInt(1)), + existingBuyOrderId: "101", + originalModuleBalance: sdk.NewInt(1), + originalBuyerBalance: minOfferPriceCoin.Amount.AddRaw(2), + preRunSetupFunc: func(s *KeeperTestSuite) { + s.dymNsKeeper.SetCountBuyOrders(s.ctx, 1) + }, + wantErr: true, + wantErrContains: "offer denomination mismatch with existing offer", + wantLaterOffer: &dymnstypes.BuyOrder{ + Id: "101", + AssetId: dymName.Name, + AssetType: dymnstypes.TypeName, + Buyer: buyerA, + OfferPrice: sdk.Coin{ + Denom: "u" + s.priceDenom(), + Amount: sdk.NewInt(minOfferPrice), + }, + }, + wantLaterModuleBalance: sdk.NewInt(1), + wantLaterBuyerBalance: minOfferPriceCoin.Amount.AddRaw(2), + wantMinConsumeGas: 1, + afterTestFunc: func(s *KeeperTestSuite) { + s.Require().Len(s.dymNsKeeper.GetAllBuyOrders(s.ctx), 1) + s.Require().Nil(s.dymNsKeeper.GetBuyOrder(s.ctx, "102")) + }, + }, + { + name: "fail - continue an existing offer but new offer less than previous", + existingDymName: dymName, + existingOffer: &dymnstypes.BuyOrder{ + Id: "101", + AssetId: dymName.Name, + AssetType: dymnstypes.TypeName, + Buyer: buyerA, + OfferPrice: minOfferPriceCoin.AddAmount(sdk.NewInt(2)), + CounterpartyOfferPrice: nil, + }, + dymName: dymName.Name, + buyer: buyerA, + offer: minOfferPriceCoin.AddAmount(sdk.NewInt(1)), // less + existingBuyOrderId: "101", + originalModuleBalance: sdk.NewInt(1), + originalBuyerBalance: minOfferPriceCoin.Amount.AddRaw(2), + preRunSetupFunc: func(s *KeeperTestSuite) { + s.dymNsKeeper.SetCountBuyOrders(s.ctx, 1) + }, + wantErr: true, + wantErrContains: "offer price must be greater than existing offer price", + wantLaterOffer: &dymnstypes.BuyOrder{ + Id: "101", + AssetId: dymName.Name, + AssetType: dymnstypes.TypeName, + Buyer: buyerA, + OfferPrice: minOfferPriceCoin.AddAmount(sdk.NewInt(2)), // keep + }, + wantLaterModuleBalance: sdk.NewInt(1), + wantLaterBuyerBalance: minOfferPriceCoin.Amount.AddRaw(2), + wantMinConsumeGas: 1, + afterTestFunc: func(s *KeeperTestSuite) { + s.Require().Len(s.dymNsKeeper.GetAllBuyOrders(s.ctx), 1) + s.Require().Nil(s.dymNsKeeper.GetBuyOrder(s.ctx, "102")) + }, + }, + { + name: "fail - continue an existing offer but new offer equals to previous", + existingDymName: dymName, + existingOffer: &dymnstypes.BuyOrder{ + Id: "101", + AssetId: dymName.Name, + AssetType: dymnstypes.TypeName, + Buyer: buyerA, + OfferPrice: minOfferPriceCoin.AddAmount(sdk.NewInt(2)), + CounterpartyOfferPrice: nil, + }, + dymName: dymName.Name, + buyer: buyerA, + offer: minOfferPriceCoin.AddAmount(sdk.NewInt(2)), // same + existingBuyOrderId: "101", + originalModuleBalance: sdk.NewInt(1), + originalBuyerBalance: minOfferPriceCoin.Amount.AddRaw(2), + preRunSetupFunc: func(s *KeeperTestSuite) { + s.dymNsKeeper.SetCountBuyOrders(s.ctx, 1) + }, + wantErr: true, + wantErrContains: "offer price must be greater than existing offer price", + wantLaterOffer: &dymnstypes.BuyOrder{ + Id: "101", + AssetId: dymName.Name, + AssetType: dymnstypes.TypeName, + Buyer: buyerA, + OfferPrice: minOfferPriceCoin.AddAmount(sdk.NewInt(2)), + }, + wantLaterModuleBalance: sdk.NewInt(1), + wantLaterBuyerBalance: minOfferPriceCoin.Amount.AddRaw(2), + wantMinConsumeGas: 1, + afterTestFunc: func(s *KeeperTestSuite) { + s.Require().Len(s.dymNsKeeper.GetAllBuyOrders(s.ctx), 1) + s.Require().Nil(s.dymNsKeeper.GetBuyOrder(s.ctx, "102")) + }, + }, + { + name: "pass - reverse record added after successful offer", + existingDymName: dymName, + existingOffer: nil, + dymName: dymName.Name, + buyer: buyerA, + offer: minOfferPriceCoin, + existingBuyOrderId: "", + originalModuleBalance: sdk.NewInt(5), + originalBuyerBalance: minOfferPriceCoin.Amount.AddRaw(2), + originalAnotherBuyerBalance: minOfferPriceCoin.Amount, + wantErr: false, + wantBuyOrderId: "101", + wantLaterOffer: &dymnstypes.BuyOrder{ + Id: "101", + AssetId: dymName.Name, + AssetType: dymnstypes.TypeName, + Buyer: buyerA, + OfferPrice: minOfferPriceCoin, + }, + wantLaterModuleBalance: minOfferPriceCoin.Amount.AddRaw(5), + wantLaterBuyerBalance: sdk.NewInt(2), + wantMinConsumeGas: dymnstypes.OpGasPutBuyOrder, + afterTestFunc: func(s *KeeperTestSuite) { + key := dymnstypes.DymNameToBuyOrderIdsRvlKey(dymName.Name) + orderIds := s.dymNsKeeper.GenericGetReverseLookupBuyOrderIdsRecord(s.ctx, key) + s.Equal([]string{"101"}, orderIds.OrderIds) + + offers, err := s.dymNsKeeper.GetBuyOrdersOfDymName(s.ctx, dymName.Name) + s.Require().NoError(err) + s.Equal("101", offers[0].Id) + + key = dymnstypes.BuyerToOrderIdsRvlKey(sdk.MustAccAddressFromBech32(buyerA)) + orderIds = s.dymNsKeeper.GenericGetReverseLookupBuyOrderIdsRecord(s.ctx, key) + s.Equal([]string{"101"}, orderIds.OrderIds) + + offers, err = s.dymNsKeeper.GetBuyOrdersByBuyer(s.ctx, buyerA) + s.Require().NoError(err) + s.Equal("101", offers[0].Id) + + resp, err := dymnskeeper.NewMsgServerImpl(s.dymNsKeeper).PlaceBuyOrder(s.ctx, &dymnstypes.MsgPlaceBuyOrder{ + AssetId: dymName.Name, + AssetType: dymnstypes.TypeName, + Buyer: anotherBuyerA, + Offer: minOfferPriceCoin, + }) + s.Require().NoError(err) + s.Require().NotNil(resp) + s.Equal("102", resp.OrderId) + + key = dymnstypes.BuyerToOrderIdsRvlKey(sdk.MustAccAddressFromBech32(anotherBuyerA)) + orderIds = s.dymNsKeeper.GenericGetReverseLookupBuyOrderIdsRecord(s.ctx, key) + s.Equal([]string{"102"}, orderIds.OrderIds) + + key = dymnstypes.BuyerToOrderIdsRvlKey(sdk.MustAccAddressFromBech32(buyerA)) + orderIds = s.dymNsKeeper.GenericGetReverseLookupBuyOrderIdsRecord(s.ctx, key) + s.Equal([]string{"101"}, orderIds.OrderIds) + + key = dymnstypes.DymNameToBuyOrderIdsRvlKey(dymName.Name) + orderIds = s.dymNsKeeper.GenericGetReverseLookupBuyOrderIdsRecord(s.ctx, key) + s.Equal([]string{"101", "102"}, orderIds.OrderIds) + }, + }, + { + name: "pass - reverse record added after successful offer extends", + existingDymName: dymName, + existingOffer: &dymnstypes.BuyOrder{ + Id: "102", + AssetId: dymName.Name, + AssetType: dymnstypes.TypeName, + Buyer: buyerA, + OfferPrice: minOfferPriceCoin, + CounterpartyOfferPrice: nil, + }, + dymName: dymName.Name, + buyer: buyerA, + offer: minOfferPriceCoin.AddAmount(sdk.NewInt(1)), + existingBuyOrderId: "102", + originalModuleBalance: sdk.NewInt(0), + originalBuyerBalance: sdk.NewInt(1), + originalAnotherBuyerBalance: minOfferPriceCoin.Amount, + preRunSetupFunc: func(s *KeeperTestSuite) { + s.dymNsKeeper.SetCountBuyOrders(s.ctx, 2) + + err := s.dymNsKeeper.AddReverseMappingBuyerToBuyOrderRecord(s.ctx, buyerA, "102") + s.Require().NoError(err) + + err = s.dymNsKeeper.AddReverseMappingAssetIdToBuyOrder(s.ctx, dymName.Name, dymnstypes.TypeName, "102") + s.Require().NoError(err) + }, + wantErr: false, + wantBuyOrderId: "102", + wantLaterOffer: &dymnstypes.BuyOrder{ + Id: "102", + AssetId: dymName.Name, + AssetType: dymnstypes.TypeName, + Buyer: buyerA, + OfferPrice: minOfferPriceCoin.AddAmount(sdk.NewInt(1)), + }, + wantLaterModuleBalance: sdk.NewInt(1), + wantLaterBuyerBalance: sdk.NewInt(0), + wantMinConsumeGas: dymnstypes.OpGasUpdateBuyOrder, + afterTestFunc: func(s *KeeperTestSuite) { + key := dymnstypes.DymNameToBuyOrderIdsRvlKey(dymName.Name) + orderIds := s.dymNsKeeper.GenericGetReverseLookupBuyOrderIdsRecord(s.ctx, key) + s.Equal([]string{"102"}, orderIds.OrderIds) + + offers, err := s.dymNsKeeper.GetBuyOrdersOfDymName(s.ctx, dymName.Name) + s.Require().NoError(err) + s.Equal("102", offers[0].Id) + + key = dymnstypes.BuyerToOrderIdsRvlKey(sdk.MustAccAddressFromBech32(buyerA)) + orderIds = s.dymNsKeeper.GenericGetReverseLookupBuyOrderIdsRecord(s.ctx, key) + s.Equal([]string{"102"}, orderIds.OrderIds) + + offers, err = s.dymNsKeeper.GetBuyOrdersByBuyer(s.ctx, buyerA) + s.Require().NoError(err) + s.Equal("102", offers[0].Id) + + resp, err := dymnskeeper.NewMsgServerImpl(s.dymNsKeeper).PlaceBuyOrder(s.ctx, &dymnstypes.MsgPlaceBuyOrder{ + AssetId: dymName.Name, + AssetType: dymnstypes.TypeName, + Buyer: anotherBuyerA, + Offer: minOfferPriceCoin, + }) + s.Require().NoError(err) + s.Require().NotNil(resp) + s.Equal("103", resp.OrderId) + + key = dymnstypes.BuyerToOrderIdsRvlKey(sdk.MustAccAddressFromBech32(anotherBuyerA)) + orderIds = s.dymNsKeeper.GenericGetReverseLookupBuyOrderIdsRecord(s.ctx, key) + s.Equal([]string{"103"}, orderIds.OrderIds) + + key = dymnstypes.BuyerToOrderIdsRvlKey(sdk.MustAccAddressFromBech32(buyerA)) + orderIds = s.dymNsKeeper.GenericGetReverseLookupBuyOrderIdsRecord(s.ctx, key) + s.Equal([]string{"102"}, orderIds.OrderIds) + + key = dymnstypes.DymNameToBuyOrderIdsRvlKey(dymName.Name) + orderIds = s.dymNsKeeper.GenericGetReverseLookupBuyOrderIdsRecord(s.ctx, key) + s.Equal([]string{"102", "103"}, orderIds.OrderIds) + }, + }, + } + for _, tt := range tests { + s.Run(tt.name, func() { + s.RefreshContext() + + if tt.originalModuleBalance.IsPositive() { + s.mintToModuleAccount2(tt.originalModuleBalance) + } + + if tt.originalBuyerBalance.IsPositive() { + s.mintToAccount2(tt.buyer, tt.originalBuyerBalance) + } + + if !tt.originalAnotherBuyerBalance.IsNil() && tt.originalAnotherBuyerBalance.IsPositive() { + s.mintToAccount2(anotherBuyerA, tt.originalAnotherBuyerBalance) + } + + if tt.existingDymName != nil { + err := s.dymNsKeeper.SetDymName(s.ctx, *tt.existingDymName) + s.Require().NoError(err) + } + + if tt.existingOffer != nil { + err := s.dymNsKeeper.SetBuyOrder(s.ctx, *tt.existingOffer) + s.Require().NoError(err) + } + + if tt.preRunSetupFunc != nil { + tt.preRunSetupFunc(s) + } + + resp, err := dymnskeeper.NewMsgServerImpl(s.dymNsKeeper).PlaceBuyOrder(s.ctx, &dymnstypes.MsgPlaceBuyOrder{ + AssetId: tt.dymName, + AssetType: dymnstypes.TypeName, + Buyer: tt.buyer, + ContinueOrderId: tt.existingBuyOrderId, + Offer: tt.offer, + }) + + defer func() { + if s.T().Failed() { + return + } + + if tt.wantLaterOffer != nil { + laterOffer := s.dymNsKeeper.GetBuyOrder(s.ctx, tt.wantLaterOffer.Id) + s.Require().NotNil(laterOffer) + s.Equal(*tt.wantLaterOffer, *laterOffer) + } + + laterModuleBalance := s.moduleBalance2() + s.Equal(tt.wantLaterModuleBalance.String(), laterModuleBalance.String()) + + laterBuyerBalance := s.balance2(tt.buyer) + s.Equal(tt.wantLaterBuyerBalance.String(), laterBuyerBalance.String()) + + s.Less(tt.wantMinConsumeGas, s.ctx.GasMeter().GasConsumed()) + + if tt.existingDymName != nil { + originalDymName := *tt.existingDymName + laterDymName := s.dymNsKeeper.GetDymName(s.ctx, tt.dymName) + s.Require().NotNil(laterDymName) + s.Equal(originalDymName, *laterDymName, "Dym-Name record should not be changed") + } + + if tt.afterTestFunc != nil { + tt.afterTestFunc(s) + } + }() + + if tt.wantErr { + s.Require().ErrorContains(err, tt.wantErrContains) + s.Require().Nil(resp) + return + } + + s.Require().NoError(err) + s.Require().NotNil(resp) + s.Equal(tt.wantBuyOrderId, resp.OrderId) + }) + } +} + +//goland:noinspection GoSnakeCaseUsage +func (s *KeeperTestSuite) Test_msgServer_PlaceBuyOrder_Alias() { + const minOfferPrice = 5 + + // the number values used in this test will be multiplied by this value + priceMultiplier := sdk.NewInt(1e18) + + minOfferPriceCoin := sdk.NewCoin(s.priceDenom(), sdk.NewInt(minOfferPrice).Mul(priceMultiplier)) + + creator_1_asOwner := testAddr(1).bech32() + creator_2_asBuyer := testAddr(2).bech32() + creator_3_asAnotherBuyer := testAddr(3).bech32() + + s.updateModuleParams(func(moduleParams dymnstypes.Params) dymnstypes.Params { + moduleParams.Price.MinOfferPrice = minOfferPriceCoin.Amount + // force enable trading + moduleParams.Misc.EnableTradingName = true + moduleParams.Misc.EnableTradingAlias = true + return moduleParams + }) + s.SaveCurrentContext() + + type rollapp struct { + rollAppID string + creator string + alias string + } + + rollApp_1_by1_asSrc := rollapp{ + rollAppID: "rollapp_1-1", + creator: creator_1_asOwner, + alias: "one", + } + rollApp_2_by2_asDest := rollapp{ + rollAppID: "rollapp_2-1", + creator: creator_2_asBuyer, + alias: "two", + } + rollApp_3_by3_asDest_noAlias := rollapp{ + rollAppID: "rollapp_3-1", + creator: creator_3_asAnotherBuyer, + alias: "", + } + rollApp_4_by1_asDest_noAlias := rollapp{ + rollAppID: "rollapp_4-2", + creator: creator_1_asOwner, + alias: "", + } + + tests := []struct { + name string + existingRollApps []rollapp + existingOffer *dymnstypes.BuyOrder + alias string + buyer string + dstRollAppId string // destination RollApp ID + offer sdk.Coin + existingBuyOrderId string + originalModuleBalance sdkmath.Int + originalBuyerBalance sdkmath.Int + originalAnotherBuyerBalance sdkmath.Int + preRunSetupFunc func(s *KeeperTestSuite) + wantErr bool + wantErrContains string + wantBuyOrderId string + wantLaterOffer *dymnstypes.BuyOrder + wantLaterModuleBalance sdkmath.Int + wantLaterBuyerBalance sdkmath.Int + wantMinConsumeGas sdk.Gas + afterTestFunc func(s *KeeperTestSuite) + }{ + { + name: "pass - can place offer", + existingRollApps: []rollapp{rollApp_1_by1_asSrc, rollApp_2_by2_asDest}, + existingOffer: nil, + alias: rollApp_1_by1_asSrc.alias, + buyer: rollApp_2_by2_asDest.creator, + dstRollAppId: rollApp_2_by2_asDest.rollAppID, + offer: minOfferPriceCoin, + existingBuyOrderId: "", + originalModuleBalance: sdk.NewInt(5), + originalBuyerBalance: minOfferPriceCoin.Amount.AddRaw(2), + wantErr: false, + wantBuyOrderId: "201", + wantLaterOffer: &dymnstypes.BuyOrder{ + Id: "201", + AssetId: rollApp_1_by1_asSrc.alias, + AssetType: dymnstypes.TypeAlias, + Params: []string{rollApp_2_by2_asDest.rollAppID}, + Buyer: rollApp_2_by2_asDest.creator, + OfferPrice: minOfferPriceCoin, + }, + wantLaterModuleBalance: minOfferPriceCoin.Amount.AddRaw(5), + wantLaterBuyerBalance: sdk.NewInt(2), + wantMinConsumeGas: dymnstypes.OpGasPutBuyOrder, + }, + { + name: "fail - can not place offer of alias which presents in params", + existingRollApps: []rollapp{rollApp_1_by1_asSrc, rollApp_2_by2_asDest}, + existingOffer: nil, + alias: rollApp_1_by1_asSrc.alias, + buyer: rollApp_2_by2_asDest.creator, + dstRollAppId: rollApp_2_by2_asDest.rollAppID, + offer: minOfferPriceCoin, + existingBuyOrderId: "", + originalModuleBalance: sdk.NewInt(5), + originalBuyerBalance: minOfferPriceCoin.Amount.AddRaw(2), + preRunSetupFunc: func(s *KeeperTestSuite) { + moduleParams := s.dymNsKeeper.GetParams(s.ctx) + moduleParams.Chains.AliasesOfChainIds = []dymnstypes.AliasesOfChainId{ + { + ChainId: "some-chain", + Aliases: []string{rollApp_1_by1_asSrc.alias}, + }, + } + err := s.dymNsKeeper.SetParams(s.ctx, moduleParams) + s.Require().NoError(err) + }, + wantErr: true, + wantErrContains: "prohibited to trade aliases which is reserved for chain-id or alias in module params", + wantLaterOffer: nil, + wantLaterModuleBalance: sdk.NewInt(5), + wantLaterBuyerBalance: minOfferPriceCoin.Amount.AddRaw(2), + wantMinConsumeGas: 1, + }, + { + name: "pass - can extends offer", + existingRollApps: []rollapp{rollApp_1_by1_asSrc, rollApp_2_by2_asDest}, + existingOffer: &dymnstypes.BuyOrder{ + Id: "202", + AssetId: rollApp_1_by1_asSrc.alias, + AssetType: dymnstypes.TypeAlias, + Params: []string{rollApp_2_by2_asDest.rollAppID}, + Buyer: rollApp_2_by2_asDest.creator, + OfferPrice: minOfferPriceCoin, + CounterpartyOfferPrice: nil, + }, + alias: rollApp_1_by1_asSrc.alias, + buyer: rollApp_2_by2_asDest.creator, + dstRollAppId: rollApp_2_by2_asDest.rollAppID, + offer: minOfferPriceCoin.AddAmount(sdk.NewInt(1)), + existingBuyOrderId: "202", + originalModuleBalance: sdk.NewInt(0), + originalBuyerBalance: sdk.NewInt(1), + wantErr: false, + wantBuyOrderId: "202", + wantLaterOffer: &dymnstypes.BuyOrder{ + Id: "202", + AssetId: rollApp_1_by1_asSrc.alias, + AssetType: dymnstypes.TypeAlias, + Params: []string{rollApp_2_by2_asDest.rollAppID}, + Buyer: rollApp_2_by2_asDest.creator, + OfferPrice: minOfferPriceCoin.AddAmount(sdk.NewInt(1)), + }, + wantLaterModuleBalance: sdk.NewInt(1), + wantLaterBuyerBalance: sdk.NewInt(0), + wantMinConsumeGas: dymnstypes.OpGasUpdateBuyOrder, + }, + { + name: "fail - can NOT extend offer of alias which presents in params", + existingRollApps: []rollapp{rollApp_1_by1_asSrc, rollApp_2_by2_asDest}, + existingOffer: &dymnstypes.BuyOrder{ + Id: "202", + AssetId: rollApp_1_by1_asSrc.alias, + AssetType: dymnstypes.TypeAlias, + Params: []string{rollApp_2_by2_asDest.rollAppID}, + Buyer: rollApp_2_by2_asDest.creator, + OfferPrice: minOfferPriceCoin, + CounterpartyOfferPrice: nil, + }, + alias: rollApp_1_by1_asSrc.alias, + buyer: rollApp_2_by2_asDest.creator, + dstRollAppId: rollApp_2_by2_asDest.rollAppID, + offer: minOfferPriceCoin.AddAmount(sdk.NewInt(1)), + existingBuyOrderId: "202", + originalModuleBalance: sdk.NewInt(0), + originalBuyerBalance: sdk.NewInt(1), + preRunSetupFunc: func(s *KeeperTestSuite) { + moduleParams := s.dymNsKeeper.GetParams(s.ctx) + moduleParams.Chains.AliasesOfChainIds = []dymnstypes.AliasesOfChainId{ + { + ChainId: "some-chain", + Aliases: []string{rollApp_1_by1_asSrc.alias}, + }, + } + err := s.dymNsKeeper.SetParams(s.ctx, moduleParams) + s.Require().NoError(err) + }, + wantErr: true, + wantErrContains: "prohibited to trade aliases which is reserved for chain-id or alias in module params", + wantBuyOrderId: "202", + wantLaterOffer: &dymnstypes.BuyOrder{ + Id: "202", + AssetId: rollApp_1_by1_asSrc.alias, + AssetType: dymnstypes.TypeAlias, + Params: []string{rollApp_2_by2_asDest.rollAppID}, + Buyer: rollApp_2_by2_asDest.creator, + OfferPrice: minOfferPriceCoin, + }, + wantLaterModuleBalance: sdk.NewInt(0), + wantLaterBuyerBalance: sdk.NewInt(1), + wantMinConsumeGas: 1, + }, + { + name: "fail - can NOT extends offer of type mis-match", + existingRollApps: []rollapp{rollApp_1_by1_asSrc, rollApp_2_by2_asDest}, + existingOffer: &dymnstypes.BuyOrder{ + Id: "102", + AssetId: rollApp_1_by1_asSrc.alias, + AssetType: dymnstypes.TypeName, + Buyer: rollApp_2_by2_asDest.creator, + OfferPrice: minOfferPriceCoin, + }, + alias: rollApp_1_by1_asSrc.alias, + buyer: rollApp_2_by2_asDest.creator, + dstRollAppId: rollApp_2_by2_asDest.rollAppID, + offer: minOfferPriceCoin.AddAmount(sdk.NewInt(1)), + existingBuyOrderId: "102", + originalModuleBalance: sdk.NewInt(0), + originalBuyerBalance: sdk.NewInt(1), + wantErr: true, + wantErrContains: "asset type mismatch with existing offer", + wantBuyOrderId: "102", + wantLaterOffer: &dymnstypes.BuyOrder{ + Id: "102", + AssetId: rollApp_1_by1_asSrc.alias, + AssetType: dymnstypes.TypeName, + Buyer: rollApp_2_by2_asDest.creator, + OfferPrice: minOfferPriceCoin, + }, + wantLaterModuleBalance: sdk.NewInt(0), + wantLaterBuyerBalance: sdk.NewInt(1), + wantMinConsumeGas: 1, + }, + { + name: "pass - can extends offer with counterparty offer", + existingRollApps: []rollapp{rollApp_1_by1_asSrc, rollApp_2_by2_asDest}, + existingOffer: &dymnstypes.BuyOrder{ + Id: "202", + AssetId: rollApp_1_by1_asSrc.alias, + AssetType: dymnstypes.TypeAlias, + Params: []string{rollApp_2_by2_asDest.rollAppID}, + Buyer: rollApp_2_by2_asDest.creator, + OfferPrice: minOfferPriceCoin, + CounterpartyOfferPrice: uptr.To(minOfferPriceCoin.AddAmount(sdk.NewInt(3))), + }, + alias: rollApp_1_by1_asSrc.alias, + buyer: rollApp_2_by2_asDest.creator, + dstRollAppId: rollApp_2_by2_asDest.rollAppID, + offer: minOfferPriceCoin.AddAmount(sdk.NewInt(1)), + existingBuyOrderId: "202", + originalModuleBalance: sdk.NewInt(1), + originalBuyerBalance: sdk.NewInt(6), + wantErr: false, + wantBuyOrderId: "202", + wantLaterOffer: &dymnstypes.BuyOrder{ + Id: "202", + AssetId: rollApp_1_by1_asSrc.alias, + AssetType: dymnstypes.TypeAlias, + Params: []string{rollApp_2_by2_asDest.rollAppID}, + Buyer: rollApp_2_by2_asDest.creator, + OfferPrice: minOfferPriceCoin.AddAmount(sdk.NewInt(1)), + CounterpartyOfferPrice: uptr.To(minOfferPriceCoin.AddAmount(sdk.NewInt(3))), + }, + wantLaterModuleBalance: sdk.NewInt(2), + wantLaterBuyerBalance: sdk.NewInt(5), + wantMinConsumeGas: dymnstypes.OpGasUpdateBuyOrder, + }, + { + name: "pass - can extends offer with offer equals to counterparty offer", + existingRollApps: []rollapp{rollApp_1_by1_asSrc, rollApp_2_by2_asDest}, + existingOffer: &dymnstypes.BuyOrder{ + Id: "202", + AssetId: rollApp_1_by1_asSrc.alias, + AssetType: dymnstypes.TypeAlias, + Params: []string{rollApp_2_by2_asDest.rollAppID}, + Buyer: rollApp_2_by2_asDest.creator, + OfferPrice: minOfferPriceCoin, + CounterpartyOfferPrice: uptr.To(minOfferPriceCoin.AddAmount(sdk.NewInt(3))), + }, + alias: rollApp_1_by1_asSrc.alias, + buyer: rollApp_2_by2_asDest.creator, + dstRollAppId: rollApp_2_by2_asDest.rollAppID, + offer: minOfferPriceCoin.AddAmount(sdk.NewInt(3)), + existingBuyOrderId: "202", + originalModuleBalance: sdk.NewInt(2), + originalBuyerBalance: sdk.NewInt(6), + wantErr: false, + wantBuyOrderId: "202", + wantLaterOffer: &dymnstypes.BuyOrder{ + Id: "202", + AssetId: rollApp_1_by1_asSrc.alias, + AssetType: dymnstypes.TypeAlias, + Params: []string{rollApp_2_by2_asDest.rollAppID}, + Buyer: rollApp_2_by2_asDest.creator, + OfferPrice: minOfferPriceCoin.AddAmount(sdk.NewInt(3)), + CounterpartyOfferPrice: uptr.To(minOfferPriceCoin.AddAmount(sdk.NewInt(3))), + }, + wantLaterModuleBalance: sdk.NewInt(5), + wantLaterBuyerBalance: sdk.NewInt(3), + wantMinConsumeGas: dymnstypes.OpGasUpdateBuyOrder, + }, + { + name: "pass - can extends offer with offer greater than counterparty offer", + existingRollApps: []rollapp{rollApp_1_by1_asSrc, rollApp_2_by2_asDest}, + existingOffer: &dymnstypes.BuyOrder{ + Id: "202", + AssetId: rollApp_1_by1_asSrc.alias, + AssetType: dymnstypes.TypeAlias, + Params: []string{rollApp_2_by2_asDest.rollAppID}, + Buyer: rollApp_2_by2_asDest.creator, + OfferPrice: minOfferPriceCoin, + CounterpartyOfferPrice: uptr.To(minOfferPriceCoin.AddAmount(sdk.NewInt(3))), + }, + alias: rollApp_1_by1_asSrc.alias, + buyer: rollApp_2_by2_asDest.creator, + dstRollAppId: rollApp_2_by2_asDest.rollAppID, + offer: minOfferPriceCoin.AddAmount(sdk.NewInt(5)), + existingBuyOrderId: "202", + originalModuleBalance: sdk.NewInt(1), + originalBuyerBalance: sdk.NewInt(7), + wantErr: false, + wantBuyOrderId: "202", + wantLaterOffer: &dymnstypes.BuyOrder{ + Id: "202", + AssetId: rollApp_1_by1_asSrc.alias, + AssetType: dymnstypes.TypeAlias, + Params: []string{rollApp_2_by2_asDest.rollAppID}, + Buyer: rollApp_2_by2_asDest.creator, + OfferPrice: minOfferPriceCoin.AddAmount(sdk.NewInt(5)), + CounterpartyOfferPrice: uptr.To(minOfferPriceCoin.AddAmount(sdk.NewInt(3))), + }, + wantLaterModuleBalance: sdk.NewInt(6), + wantLaterBuyerBalance: sdk.NewInt(2), + wantMinConsumeGas: dymnstypes.OpGasUpdateBuyOrder, + }, + { + name: "pass - extends an existing offer only take the extra amount instead of all", + existingRollApps: []rollapp{rollApp_1_by1_asSrc, rollApp_2_by2_asDest}, + existingOffer: &dymnstypes.BuyOrder{ + Id: "201", + AssetId: rollApp_1_by1_asSrc.alias, + AssetType: dymnstypes.TypeAlias, + Params: []string{rollApp_2_by2_asDest.rollAppID}, + Buyer: rollApp_2_by2_asDest.creator, + OfferPrice: minOfferPriceCoin, + CounterpartyOfferPrice: nil, + }, + alias: rollApp_1_by1_asSrc.alias, + buyer: rollApp_2_by2_asDest.creator, + dstRollAppId: rollApp_2_by2_asDest.rollAppID, + offer: minOfferPriceCoin.AddAmount(sdk.NewInt(2)), + existingBuyOrderId: "201", + originalModuleBalance: sdk.NewInt(5), + originalBuyerBalance: sdk.NewInt(3), + wantErr: false, + wantBuyOrderId: "201", + wantLaterOffer: &dymnstypes.BuyOrder{ + Id: "201", + AssetId: rollApp_1_by1_asSrc.alias, + AssetType: dymnstypes.TypeAlias, + Params: []string{rollApp_2_by2_asDest.rollAppID}, + Buyer: rollApp_2_by2_asDest.creator, + OfferPrice: minOfferPriceCoin.AddAmount(sdk.NewInt(2)), + }, + wantLaterModuleBalance: sdk.NewInt(7), + wantLaterBuyerBalance: sdk.NewInt(1), + wantMinConsumeGas: dymnstypes.OpGasUpdateBuyOrder, + }, + { + name: "fail - can NOT place offer if trading Alias is disabled", + existingRollApps: []rollapp{rollApp_1_by1_asSrc, rollApp_2_by2_asDest}, + existingOffer: nil, + alias: rollApp_1_by1_asSrc.alias, + buyer: rollApp_2_by2_asDest.creator, + dstRollAppId: rollApp_2_by2_asDest.rollAppID, + offer: minOfferPriceCoin, + existingBuyOrderId: "", + originalModuleBalance: sdk.NewInt(5), + originalBuyerBalance: minOfferPriceCoin.Amount.AddRaw(2), + preRunSetupFunc: func(s *KeeperTestSuite) { + moduleParams := s.dymNsKeeper.GetParams(s.ctx) + moduleParams.Misc.EnableTradingAlias = false + err := s.dymNsKeeper.SetParams(s.ctx, moduleParams) + s.Require().NoError(err) + }, + wantErr: true, + wantErrContains: "trading of Alias is disabled", + wantBuyOrderId: "", + wantLaterOffer: nil, + wantLaterModuleBalance: sdk.NewInt(5), + wantLaterBuyerBalance: minOfferPriceCoin.Amount.AddRaw(2), + wantMinConsumeGas: 1, + }, + { + name: "fail - reject offer for non-existing Alias", + existingRollApps: []rollapp{rollApp_1_by1_asSrc, rollApp_2_by2_asDest}, + alias: "void", + buyer: rollApp_2_by2_asDest.creator, + dstRollAppId: rollApp_2_by2_asDest.rollAppID, + offer: minOfferPriceCoin, + originalModuleBalance: sdk.NewInt(1), + originalBuyerBalance: minOfferPriceCoin.Amount, + wantErr: true, + wantErrContains: "alias is not in-used: void: not found", + wantLaterModuleBalance: sdk.NewInt(1), + wantLaterBuyerBalance: minOfferPriceCoin.Amount, + wantMinConsumeGas: 1, + afterTestFunc: func(s *KeeperTestSuite) { + s.Require().Empty(s.dymNsKeeper.GetAllBuyOrders(s.ctx)) + }, + }, + { + name: "pass - can place offer buy own alias, different RollApp", + existingRollApps: []rollapp{rollApp_1_by1_asSrc, rollApp_4_by1_asDest_noAlias}, + existingOffer: nil, + alias: rollApp_1_by1_asSrc.alias, + buyer: rollApp_1_by1_asSrc.creator, + dstRollAppId: rollApp_4_by1_asDest_noAlias.rollAppID, + offer: minOfferPriceCoin, + existingBuyOrderId: "", + originalModuleBalance: sdk.NewInt(5), + originalBuyerBalance: minOfferPriceCoin.Amount.AddRaw(2), + wantErr: false, + wantBuyOrderId: "201", + wantLaterOffer: &dymnstypes.BuyOrder{ + Id: "201", + AssetId: rollApp_1_by1_asSrc.alias, + AssetType: dymnstypes.TypeAlias, + Params: []string{rollApp_4_by1_asDest_noAlias.rollAppID}, + Buyer: rollApp_1_by1_asSrc.creator, + OfferPrice: minOfferPriceCoin, + }, + wantLaterModuleBalance: minOfferPriceCoin.Amount.AddRaw(5), + wantLaterBuyerBalance: sdk.NewInt(2), + wantMinConsumeGas: dymnstypes.OpGasPutBuyOrder, + }, + { + name: "fail - offer denom must match params", + existingRollApps: []rollapp{rollApp_1_by1_asSrc, rollApp_2_by2_asDest}, + alias: rollApp_1_by1_asSrc.alias, + buyer: rollApp_2_by2_asDest.creator, + dstRollAppId: rollApp_2_by2_asDest.rollAppID, + offer: sdk.Coin{ + Denom: "u" + s.priceDenom(), + Amount: sdk.NewInt(minOfferPrice), + }, + originalModuleBalance: sdk.NewInt(1), + originalBuyerBalance: minOfferPriceCoin.Amount, + wantErr: true, + wantErrContains: "invalid offer denomination, only accept", + wantLaterModuleBalance: sdk.NewInt(1), + wantLaterBuyerBalance: minOfferPriceCoin.Amount, + wantMinConsumeGas: 1, + afterTestFunc: func(s *KeeperTestSuite) { + s.Require().Empty(s.dymNsKeeper.GetAllBuyOrders(s.ctx)) + }, + }, + { + name: "fail - offer price can not lower than min defined in params", + existingRollApps: []rollapp{rollApp_1_by1_asSrc, rollApp_2_by2_asDest}, + alias: rollApp_1_by1_asSrc.alias, + buyer: rollApp_2_by2_asDest.creator, + dstRollAppId: rollApp_2_by2_asDest.rollAppID, + offer: minOfferPriceCoin.SubAmount(sdk.NewInt(1)), + originalModuleBalance: sdk.NewInt(1), + originalBuyerBalance: minOfferPriceCoin.Amount, + wantErr: true, + wantErrContains: "offer price must be greater than or equal to", + wantLaterModuleBalance: sdk.NewInt(1), + wantLaterBuyerBalance: minOfferPriceCoin.Amount, + wantMinConsumeGas: 1, + afterTestFunc: func(s *KeeperTestSuite) { + s.Require().Empty(s.dymNsKeeper.GetAllBuyOrders(s.ctx)) + }, + }, + { + name: "pass - if NOT continue offer, create another and charges full offer price", + existingRollApps: []rollapp{rollApp_1_by1_asSrc, rollApp_2_by2_asDest}, + existingOffer: &dymnstypes.BuyOrder{ + Id: "201", + AssetId: rollApp_1_by1_asSrc.alias, + AssetType: dymnstypes.TypeAlias, + Params: []string{rollApp_2_by2_asDest.rollAppID}, + Buyer: rollApp_2_by2_asDest.creator, + OfferPrice: minOfferPriceCoin, + CounterpartyOfferPrice: nil, + }, + alias: rollApp_1_by1_asSrc.alias, + buyer: rollApp_2_by2_asDest.creator, + dstRollAppId: rollApp_2_by2_asDest.rollAppID, + offer: minOfferPriceCoin.AddAmount(sdk.NewInt(1)), + existingBuyOrderId: "", + originalModuleBalance: sdk.NewInt(2), + originalBuyerBalance: minOfferPriceCoin.Amount.AddRaw(2), + preRunSetupFunc: func(s *KeeperTestSuite) { + s.dymNsKeeper.SetCountBuyOrders(s.ctx, 1) + }, + wantErr: false, + wantBuyOrderId: "202", + wantLaterOffer: &dymnstypes.BuyOrder{ + Id: "202", + AssetId: rollApp_1_by1_asSrc.alias, + AssetType: dymnstypes.TypeAlias, + Params: []string{rollApp_2_by2_asDest.rollAppID}, + Buyer: rollApp_2_by2_asDest.creator, + OfferPrice: minOfferPriceCoin.AddAmount(sdk.NewInt(1)), + }, + wantLaterModuleBalance: minOfferPriceCoin.Amount.AddRaw(1).AddRaw(2), + wantLaterBuyerBalance: sdk.NewInt(1), + wantMinConsumeGas: dymnstypes.OpGasPutBuyOrder, + }, + { + name: "fail - continue a non-existing offer", + existingRollApps: []rollapp{rollApp_1_by1_asSrc, rollApp_2_by2_asDest}, + existingOffer: &dymnstypes.BuyOrder{ + Id: "201", + AssetId: rollApp_1_by1_asSrc.alias, + AssetType: dymnstypes.TypeAlias, + Params: []string{rollApp_2_by2_asDest.rollAppID}, + Buyer: rollApp_2_by2_asDest.creator, + OfferPrice: minOfferPriceCoin, + CounterpartyOfferPrice: nil, + }, + alias: rollApp_1_by1_asSrc.alias, + buyer: rollApp_2_by2_asDest.creator, + dstRollAppId: rollApp_2_by2_asDest.rollAppID, + offer: minOfferPriceCoin.AddAmount(sdk.NewInt(1)), + existingBuyOrderId: "202", + originalModuleBalance: sdk.NewInt(1), + originalBuyerBalance: minOfferPriceCoin.Amount.AddRaw(2), + preRunSetupFunc: func(s *KeeperTestSuite) { + s.dymNsKeeper.SetCountBuyOrders(s.ctx, 1) + }, + wantErr: true, + wantErrContains: "Buy-Order ID: 202: not found", + wantLaterOffer: &dymnstypes.BuyOrder{ + Id: "201", + AssetId: rollApp_1_by1_asSrc.alias, + AssetType: dymnstypes.TypeAlias, + Params: []string{rollApp_2_by2_asDest.rollAppID}, + Buyer: rollApp_2_by2_asDest.creator, + OfferPrice: minOfferPriceCoin, + }, + wantLaterModuleBalance: sdk.NewInt(1), + wantLaterBuyerBalance: minOfferPriceCoin.Amount.AddRaw(2), + wantMinConsumeGas: 1, + afterTestFunc: func(s *KeeperTestSuite) { + s.Require().Len(s.dymNsKeeper.GetAllBuyOrders(s.ctx), 1) + s.Require().Nil(s.dymNsKeeper.GetBuyOrder(s.ctx, "202")) + }, + }, + { + name: "fail - continue an existing offer but not yours", + existingRollApps: []rollapp{rollApp_1_by1_asSrc, rollApp_2_by2_asDest}, + existingOffer: &dymnstypes.BuyOrder{ + Id: "201", + AssetId: rollApp_1_by1_asSrc.alias, + AssetType: dymnstypes.TypeAlias, + Params: []string{rollApp_2_by2_asDest.rollAppID}, + Buyer: creator_3_asAnotherBuyer, // not the buyer + OfferPrice: minOfferPriceCoin, + CounterpartyOfferPrice: nil, + }, + alias: rollApp_1_by1_asSrc.alias, + buyer: rollApp_2_by2_asDest.creator, // not the existing offer's buyer + dstRollAppId: rollApp_2_by2_asDest.rollAppID, + offer: minOfferPriceCoin.AddAmount(sdk.NewInt(1)), + existingBuyOrderId: "201", + originalModuleBalance: sdk.NewInt(1), + originalBuyerBalance: minOfferPriceCoin.Amount.AddRaw(2), + preRunSetupFunc: func(s *KeeperTestSuite) { + s.dymNsKeeper.SetCountBuyOrders(s.ctx, 1) + }, + wantErr: true, + wantErrContains: "not the owner of the offer", + wantLaterOffer: &dymnstypes.BuyOrder{ + Id: "201", + AssetId: rollApp_1_by1_asSrc.alias, + AssetType: dymnstypes.TypeAlias, + Params: []string{rollApp_2_by2_asDest.rollAppID}, + Buyer: creator_3_asAnotherBuyer, + OfferPrice: minOfferPriceCoin, + }, + wantLaterModuleBalance: sdk.NewInt(1), + wantLaterBuyerBalance: minOfferPriceCoin.Amount.AddRaw(2), + wantMinConsumeGas: 1, + afterTestFunc: func(s *KeeperTestSuite) { + s.Require().Len(s.dymNsKeeper.GetAllBuyOrders(s.ctx), 1) + s.Require().Nil(s.dymNsKeeper.GetBuyOrder(s.ctx, "202")) + }, + }, + { + name: "fail - continue an existing offer but the Alias mismatch", + existingRollApps: []rollapp{rollApp_1_by1_asSrc, rollApp_2_by2_asDest}, + existingOffer: &dymnstypes.BuyOrder{ + Id: "201", + AssetId: "another", + AssetType: dymnstypes.TypeAlias, + Params: []string{rollApp_2_by2_asDest.rollAppID}, + Buyer: rollApp_2_by2_asDest.creator, + OfferPrice: minOfferPriceCoin, + CounterpartyOfferPrice: nil, + }, + alias: rollApp_1_by1_asSrc.alias, + buyer: rollApp_2_by2_asDest.creator, + dstRollAppId: rollApp_2_by2_asDest.rollAppID, + offer: minOfferPriceCoin.AddAmount(sdk.NewInt(1)), + existingBuyOrderId: "201", + originalModuleBalance: sdk.NewInt(1), + originalBuyerBalance: minOfferPriceCoin.Amount.AddRaw(2), + preRunSetupFunc: func(s *KeeperTestSuite) { + s.dymNsKeeper.SetCountBuyOrders(s.ctx, 1) + }, + wantErr: true, + wantErrContains: "alias mismatch with existing offer", + wantLaterOffer: &dymnstypes.BuyOrder{ + Id: "201", + AssetId: "another", + AssetType: dymnstypes.TypeAlias, + Params: []string{rollApp_2_by2_asDest.rollAppID}, + Buyer: rollApp_2_by2_asDest.creator, + OfferPrice: minOfferPriceCoin, + }, + wantLaterModuleBalance: sdk.NewInt(1), + wantLaterBuyerBalance: minOfferPriceCoin.Amount.AddRaw(2), + wantMinConsumeGas: 1, + afterTestFunc: func(s *KeeperTestSuite) { + s.Require().Len(s.dymNsKeeper.GetAllBuyOrders(s.ctx), 1) + s.Require().Nil(s.dymNsKeeper.GetBuyOrder(s.ctx, "202")) + }, + }, + { + name: "fail - continue an existing offer but mis-match offer denom", + existingRollApps: []rollapp{rollApp_1_by1_asSrc, rollApp_2_by2_asDest}, + existingOffer: &dymnstypes.BuyOrder{ + Id: "201", + AssetId: rollApp_1_by1_asSrc.alias, + AssetType: dymnstypes.TypeAlias, + Params: []string{rollApp_2_by2_asDest.rollAppID}, + Buyer: rollApp_2_by2_asDest.creator, + OfferPrice: sdk.Coin{ + Denom: "u" + s.priceDenom(), + Amount: sdk.NewInt(minOfferPrice), + }, + CounterpartyOfferPrice: nil, + }, + alias: rollApp_1_by1_asSrc.alias, + buyer: rollApp_2_by2_asDest.creator, + dstRollAppId: rollApp_2_by2_asDest.rollAppID, + offer: minOfferPriceCoin.AddAmount(sdk.NewInt(1)), + existingBuyOrderId: "201", + originalModuleBalance: sdk.NewInt(1), + originalBuyerBalance: minOfferPriceCoin.Amount.AddRaw(2), + preRunSetupFunc: func(s *KeeperTestSuite) { + s.dymNsKeeper.SetCountBuyOrders(s.ctx, 1) + }, + wantErr: true, + wantErrContains: "offer denomination mismatch with existing offer", + wantLaterOffer: &dymnstypes.BuyOrder{ + Id: "201", + AssetId: rollApp_1_by1_asSrc.alias, + AssetType: dymnstypes.TypeAlias, + Params: []string{rollApp_2_by2_asDest.rollAppID}, + Buyer: rollApp_2_by2_asDest.creator, + OfferPrice: sdk.Coin{ + Denom: "u" + s.priceDenom(), + Amount: sdk.NewInt(minOfferPrice), + }, + }, + wantLaterModuleBalance: sdk.NewInt(1), + wantLaterBuyerBalance: minOfferPriceCoin.Amount.AddRaw(2), + wantMinConsumeGas: 1, + afterTestFunc: func(s *KeeperTestSuite) { + s.Require().Len(s.dymNsKeeper.GetAllBuyOrders(s.ctx), 1) + s.Require().Nil(s.dymNsKeeper.GetBuyOrder(s.ctx, "202")) + }, + }, + { + name: "fail - continue an existing offer but new offer less than previous", + existingRollApps: []rollapp{rollApp_1_by1_asSrc, rollApp_2_by2_asDest}, + existingOffer: &dymnstypes.BuyOrder{ + Id: "201", + AssetId: rollApp_1_by1_asSrc.alias, + AssetType: dymnstypes.TypeAlias, + Params: []string{rollApp_2_by2_asDest.rollAppID}, + Buyer: rollApp_2_by2_asDest.creator, + OfferPrice: minOfferPriceCoin.AddAmount(sdk.NewInt(2)), + CounterpartyOfferPrice: nil, + }, + alias: rollApp_1_by1_asSrc.alias, + buyer: rollApp_2_by2_asDest.creator, + dstRollAppId: rollApp_2_by2_asDest.rollAppID, + offer: minOfferPriceCoin.AddAmount(sdk.NewInt(1)), // less + existingBuyOrderId: "201", + originalModuleBalance: sdk.NewInt(1), + originalBuyerBalance: minOfferPriceCoin.Amount.AddRaw(2), + preRunSetupFunc: func(s *KeeperTestSuite) { + s.dymNsKeeper.SetCountBuyOrders(s.ctx, 1) + }, + wantErr: true, + wantErrContains: "offer price must be greater than existing offer price", + wantLaterOffer: &dymnstypes.BuyOrder{ + Id: "201", + AssetId: rollApp_1_by1_asSrc.alias, + AssetType: dymnstypes.TypeAlias, + Params: []string{rollApp_2_by2_asDest.rollAppID}, + Buyer: rollApp_2_by2_asDest.creator, + OfferPrice: minOfferPriceCoin.AddAmount(sdk.NewInt(2)), // keep + }, + wantLaterModuleBalance: sdk.NewInt(1), + wantLaterBuyerBalance: minOfferPriceCoin.Amount.AddRaw(2), + wantMinConsumeGas: 1, + afterTestFunc: func(s *KeeperTestSuite) { + s.Require().Len(s.dymNsKeeper.GetAllBuyOrders(s.ctx), 1) + s.Require().Nil(s.dymNsKeeper.GetBuyOrder(s.ctx, "202")) + }, + }, + { + name: "fail - continue an existing offer but new offer equals to previous", + existingRollApps: []rollapp{rollApp_1_by1_asSrc, rollApp_2_by2_asDest}, + existingOffer: &dymnstypes.BuyOrder{ + Id: "201", + AssetId: rollApp_1_by1_asSrc.alias, + AssetType: dymnstypes.TypeAlias, + Params: []string{rollApp_2_by2_asDest.rollAppID}, + Buyer: rollApp_2_by2_asDest.creator, + OfferPrice: minOfferPriceCoin.AddAmount(sdk.NewInt(2)), + CounterpartyOfferPrice: nil, + }, + alias: rollApp_1_by1_asSrc.alias, + buyer: rollApp_2_by2_asDest.creator, + dstRollAppId: rollApp_2_by2_asDest.rollAppID, + offer: minOfferPriceCoin.AddAmount(sdk.NewInt(2)), // same + existingBuyOrderId: "201", + originalModuleBalance: sdk.NewInt(1), + originalBuyerBalance: minOfferPriceCoin.Amount.AddRaw(2), + preRunSetupFunc: func(s *KeeperTestSuite) { + s.dymNsKeeper.SetCountBuyOrders(s.ctx, 1) + }, + wantErr: true, + wantErrContains: "offer price must be greater than existing offer price", + wantLaterOffer: &dymnstypes.BuyOrder{ + Id: "201", + AssetId: rollApp_1_by1_asSrc.alias, + AssetType: dymnstypes.TypeAlias, + Params: []string{rollApp_2_by2_asDest.rollAppID}, + Buyer: rollApp_2_by2_asDest.creator, + OfferPrice: minOfferPriceCoin.AddAmount(sdk.NewInt(2)), + }, + wantLaterModuleBalance: sdk.NewInt(1), + wantLaterBuyerBalance: minOfferPriceCoin.Amount.AddRaw(2), + wantMinConsumeGas: 1, + afterTestFunc: func(s *KeeperTestSuite) { + s.Require().Len(s.dymNsKeeper.GetAllBuyOrders(s.ctx), 1) + s.Require().Nil(s.dymNsKeeper.GetBuyOrder(s.ctx, "202")) + }, + }, + { + name: "fail - destination RollApp is not found", + existingRollApps: []rollapp{rollApp_1_by1_asSrc}, + alias: rollApp_1_by1_asSrc.alias, + buyer: creator_2_asBuyer, + dstRollAppId: "nah_0-0", + offer: minOfferPriceCoin, + originalModuleBalance: sdk.NewInt(1), + originalBuyerBalance: minOfferPriceCoin.Amount.AddRaw(2), + preRunSetupFunc: func(s *KeeperTestSuite) { + }, + wantErr: true, + wantErrContains: "destination Roll-App does not exists", + wantBuyOrderId: "201", + wantLaterOffer: nil, + wantLaterModuleBalance: sdk.NewInt(1), + wantLaterBuyerBalance: minOfferPriceCoin.Amount.AddRaw(2), + wantMinConsumeGas: 1, + afterTestFunc: func(s *KeeperTestSuite) { + s.Require().Empty(s.dymNsKeeper.GetAllBuyOrders(s.ctx)) + s.Require().Nil(s.dymNsKeeper.GetBuyOrder(s.ctx, "201")) + }, + }, + { + name: "fail - destination RollApp is not found", + existingRollApps: []rollapp{rollApp_1_by1_asSrc, rollApp_3_by3_asDest_noAlias}, + alias: rollApp_1_by1_asSrc.alias, + buyer: creator_2_asBuyer, + dstRollAppId: rollApp_3_by3_asDest_noAlias.rollAppID, + offer: minOfferPriceCoin, + originalModuleBalance: sdk.NewInt(1), + originalBuyerBalance: minOfferPriceCoin.Amount.AddRaw(2), + preRunSetupFunc: func(s *KeeperTestSuite) { + }, + wantErr: true, + wantErrContains: "not the owner of the RollApp", + wantBuyOrderId: "201", + wantLaterOffer: nil, + wantLaterModuleBalance: sdk.NewInt(1), + wantLaterBuyerBalance: minOfferPriceCoin.Amount.AddRaw(2), + wantMinConsumeGas: 1, + afterTestFunc: func(s *KeeperTestSuite) { + s.Require().Empty(s.dymNsKeeper.GetAllBuyOrders(s.ctx)) + s.Require().Nil(s.dymNsKeeper.GetBuyOrder(s.ctx, "201")) + }, + }, + { + name: "fail - destination RollApp is the same as source", + existingRollApps: []rollapp{rollApp_1_by1_asSrc}, + alias: rollApp_1_by1_asSrc.alias, + buyer: rollApp_1_by1_asSrc.creator, + dstRollAppId: rollApp_1_by1_asSrc.rollAppID, + offer: minOfferPriceCoin, + originalModuleBalance: sdk.NewInt(1), + originalBuyerBalance: minOfferPriceCoin.Amount.AddRaw(2), + preRunSetupFunc: func(s *KeeperTestSuite) { + }, + wantErr: true, + wantErrContains: "destination Roll-App ID is the same as the source", + wantBuyOrderId: "201", + wantLaterOffer: nil, + wantLaterModuleBalance: sdk.NewInt(1), + wantLaterBuyerBalance: minOfferPriceCoin.Amount.AddRaw(2), + wantMinConsumeGas: 1, + afterTestFunc: func(s *KeeperTestSuite) { + s.Require().Empty(s.dymNsKeeper.GetAllBuyOrders(s.ctx)) + s.Require().Nil(s.dymNsKeeper.GetBuyOrder(s.ctx, "201")) + }, + }, + { + name: "pass - reverse record added after successful offer", + existingRollApps: []rollapp{rollApp_1_by1_asSrc, rollApp_2_by2_asDest, rollApp_3_by3_asDest_noAlias}, + existingOffer: nil, + alias: rollApp_1_by1_asSrc.alias, + buyer: rollApp_2_by2_asDest.creator, + dstRollAppId: rollApp_2_by2_asDest.rollAppID, + offer: minOfferPriceCoin, + existingBuyOrderId: "", + originalModuleBalance: sdk.NewInt(5), + originalBuyerBalance: minOfferPriceCoin.Amount.AddRaw(2), + originalAnotherBuyerBalance: minOfferPriceCoin.Amount, + wantErr: false, + wantBuyOrderId: "201", + wantLaterOffer: &dymnstypes.BuyOrder{ + Id: "201", + AssetId: rollApp_1_by1_asSrc.alias, + AssetType: dymnstypes.TypeAlias, + Params: []string{rollApp_2_by2_asDest.rollAppID}, + Buyer: rollApp_2_by2_asDest.creator, + OfferPrice: minOfferPriceCoin, + }, + wantLaterModuleBalance: minOfferPriceCoin.Amount.AddRaw(5), + wantLaterBuyerBalance: sdk.NewInt(2), + wantMinConsumeGas: dymnstypes.OpGasPutBuyOrder, + afterTestFunc: func(s *KeeperTestSuite) { + key := dymnstypes.AliasToBuyOrderIdsRvlKey(rollApp_1_by1_asSrc.alias) + orderIds := s.dymNsKeeper.GenericGetReverseLookupBuyOrderIdsRecord(s.ctx, key) + s.Equal([]string{"201"}, orderIds.OrderIds) + + offers, err := s.dymNsKeeper.GetBuyOrdersOfAlias(s.ctx, rollApp_1_by1_asSrc.alias) + s.Require().NoError(err) + s.Equal("201", offers[0].Id) + + key = dymnstypes.BuyerToOrderIdsRvlKey(sdk.MustAccAddressFromBech32(rollApp_2_by2_asDest.creator)) + orderIds = s.dymNsKeeper.GenericGetReverseLookupBuyOrderIdsRecord(s.ctx, key) + s.Equal([]string{"201"}, orderIds.OrderIds) + + offers, err = s.dymNsKeeper.GetBuyOrdersByBuyer(s.ctx, rollApp_2_by2_asDest.creator) + s.Require().NoError(err) + s.Equal("201", offers[0].Id) + + resp, err := dymnskeeper.NewMsgServerImpl(s.dymNsKeeper).PlaceBuyOrder(s.ctx, &dymnstypes.MsgPlaceBuyOrder{ + AssetId: rollApp_1_by1_asSrc.alias, + AssetType: dymnstypes.TypeAlias, + Params: []string{rollApp_3_by3_asDest_noAlias.rollAppID}, + Buyer: rollApp_3_by3_asDest_noAlias.creator, + Offer: minOfferPriceCoin, + }) + s.Require().NoError(err) + s.Require().NotNil(resp) + s.Equal("202", resp.OrderId) + + key = dymnstypes.BuyerToOrderIdsRvlKey(sdk.MustAccAddressFromBech32(rollApp_3_by3_asDest_noAlias.creator)) + orderIds = s.dymNsKeeper.GenericGetReverseLookupBuyOrderIdsRecord(s.ctx, key) + s.Equal([]string{"202"}, orderIds.OrderIds) + + key = dymnstypes.BuyerToOrderIdsRvlKey(sdk.MustAccAddressFromBech32(rollApp_2_by2_asDest.creator)) + orderIds = s.dymNsKeeper.GenericGetReverseLookupBuyOrderIdsRecord(s.ctx, key) + s.Equal([]string{"201"}, orderIds.OrderIds) + + key = dymnstypes.AliasToBuyOrderIdsRvlKey(rollApp_1_by1_asSrc.alias) + orderIds = s.dymNsKeeper.GenericGetReverseLookupBuyOrderIdsRecord(s.ctx, key) + s.Equal([]string{"201", "202"}, orderIds.OrderIds) + }, + }, + { + name: "pass - reverse record added after successful offer extends", + existingRollApps: []rollapp{rollApp_1_by1_asSrc, rollApp_2_by2_asDest, rollApp_3_by3_asDest_noAlias}, + existingOffer: &dymnstypes.BuyOrder{ + Id: "202", + AssetId: rollApp_1_by1_asSrc.alias, + AssetType: dymnstypes.TypeAlias, + Params: []string{rollApp_2_by2_asDest.rollAppID}, + Buyer: rollApp_2_by2_asDest.creator, + OfferPrice: minOfferPriceCoin, + CounterpartyOfferPrice: nil, + }, + alias: rollApp_1_by1_asSrc.alias, + buyer: rollApp_2_by2_asDest.creator, + dstRollAppId: rollApp_2_by2_asDest.rollAppID, + offer: minOfferPriceCoin.AddAmount(sdk.NewInt(1)), + existingBuyOrderId: "202", + originalModuleBalance: sdk.NewInt(0), + originalBuyerBalance: sdk.NewInt(1), + originalAnotherBuyerBalance: minOfferPriceCoin.Amount, + preRunSetupFunc: func(s *KeeperTestSuite) { + s.dymNsKeeper.SetCountBuyOrders(s.ctx, 2) + + err := s.dymNsKeeper.AddReverseMappingBuyerToBuyOrderRecord(s.ctx, rollApp_2_by2_asDest.creator, "202") + s.Require().NoError(err) + + err = s.dymNsKeeper.AddReverseMappingAssetIdToBuyOrder(s.ctx, rollApp_1_by1_asSrc.alias, dymnstypes.TypeAlias, "202") + s.Require().NoError(err) + }, + wantErr: false, + wantBuyOrderId: "202", + wantLaterOffer: &dymnstypes.BuyOrder{ + Id: "202", + AssetId: rollApp_1_by1_asSrc.alias, + AssetType: dymnstypes.TypeAlias, + Params: []string{rollApp_2_by2_asDest.rollAppID}, + Buyer: rollApp_2_by2_asDest.creator, + OfferPrice: minOfferPriceCoin.AddAmount(sdk.NewInt(1)), + }, + wantLaterModuleBalance: sdk.NewInt(1), + wantLaterBuyerBalance: sdk.NewInt(0), + wantMinConsumeGas: dymnstypes.OpGasUpdateBuyOrder, + afterTestFunc: func(s *KeeperTestSuite) { + key := dymnstypes.AliasToBuyOrderIdsRvlKey(rollApp_1_by1_asSrc.alias) + orderIds := s.dymNsKeeper.GenericGetReverseLookupBuyOrderIdsRecord(s.ctx, key) + s.Equal([]string{"202"}, orderIds.OrderIds) + + offers, err := s.dymNsKeeper.GetBuyOrdersOfAlias(s.ctx, rollApp_1_by1_asSrc.alias) + s.Require().NoError(err) + s.Equal("202", offers[0].Id) + + key = dymnstypes.BuyerToOrderIdsRvlKey(sdk.MustAccAddressFromBech32(rollApp_2_by2_asDest.creator)) + orderIds = s.dymNsKeeper.GenericGetReverseLookupBuyOrderIdsRecord(s.ctx, key) + s.Equal([]string{"202"}, orderIds.OrderIds) + + offers, err = s.dymNsKeeper.GetBuyOrdersByBuyer(s.ctx, rollApp_2_by2_asDest.creator) + s.Require().NoError(err) + s.Equal("202", offers[0].Id) + + resp, err := dymnskeeper.NewMsgServerImpl(s.dymNsKeeper).PlaceBuyOrder(s.ctx, &dymnstypes.MsgPlaceBuyOrder{ + AssetId: rollApp_1_by1_asSrc.alias, + AssetType: dymnstypes.TypeAlias, + Params: []string{rollApp_3_by3_asDest_noAlias.rollAppID}, + Buyer: rollApp_3_by3_asDest_noAlias.creator, + Offer: minOfferPriceCoin, + }) + s.Require().NoError(err) + s.Require().NotNil(resp) + s.Equal("203", resp.OrderId) + + key = dymnstypes.BuyerToOrderIdsRvlKey(sdk.MustAccAddressFromBech32(rollApp_3_by3_asDest_noAlias.creator)) + orderIds = s.dymNsKeeper.GenericGetReverseLookupBuyOrderIdsRecord(s.ctx, key) + s.Equal([]string{"203"}, orderIds.OrderIds) + + key = dymnstypes.BuyerToOrderIdsRvlKey(sdk.MustAccAddressFromBech32(rollApp_2_by2_asDest.creator)) + orderIds = s.dymNsKeeper.GenericGetReverseLookupBuyOrderIdsRecord(s.ctx, key) + s.Equal([]string{"202"}, orderIds.OrderIds) + + key = dymnstypes.AliasToBuyOrderIdsRvlKey(rollApp_1_by1_asSrc.alias) + orderIds = s.dymNsKeeper.GenericGetReverseLookupBuyOrderIdsRecord(s.ctx, key) + s.Equal([]string{"202", "203"}, orderIds.OrderIds) + }, + }, + } + for _, tt := range tests { + s.Run(tt.name, func() { + s.RefreshContext() + + if tt.originalModuleBalance.IsPositive() { + s.mintToModuleAccount2(tt.originalModuleBalance) + } + + if tt.originalBuyerBalance.IsPositive() { + s.mintToAccount2(tt.buyer, tt.originalBuyerBalance) + } + + if !tt.originalAnotherBuyerBalance.IsNil() && tt.originalAnotherBuyerBalance.IsPositive() { + s.mintToAccount2(creator_3_asAnotherBuyer, tt.originalAnotherBuyerBalance) + } + + for _, existingRollApp := range tt.existingRollApps { + s.rollAppKeeper.SetRollapp(s.ctx, rollapptypes.Rollapp{ + RollappId: existingRollApp.rollAppID, + Owner: existingRollApp.creator, + }) + if existingRollApp.alias != "" { + err := s.dymNsKeeper.SetAliasForRollAppId(s.ctx, existingRollApp.rollAppID, existingRollApp.alias) + s.Require().NoError(err) + } + } + + if tt.existingOffer != nil { + err := s.dymNsKeeper.SetBuyOrder(s.ctx, *tt.existingOffer) + s.Require().NoError(err) + } + + if tt.preRunSetupFunc != nil { + tt.preRunSetupFunc(s) + } + + resp, err := dymnskeeper.NewMsgServerImpl(s.dymNsKeeper).PlaceBuyOrder(s.ctx, &dymnstypes.MsgPlaceBuyOrder{ + AssetId: tt.alias, + AssetType: dymnstypes.TypeAlias, + Buyer: tt.buyer, + Params: []string{tt.dstRollAppId}, + ContinueOrderId: tt.existingBuyOrderId, + Offer: tt.offer, + }) + + defer func() { + if s.T().Failed() { + return + } + + if tt.wantLaterOffer != nil { + laterOffer := s.dymNsKeeper.GetBuyOrder(s.ctx, tt.wantLaterOffer.Id) + s.Require().NotNil(laterOffer) + s.Equal(*tt.wantLaterOffer, *laterOffer) + } + + laterModuleBalance := s.moduleBalance2() + s.Equal(tt.wantLaterModuleBalance.String(), laterModuleBalance.String()) + + laterBuyerBalance := s.balance2(tt.buyer) + s.Equal(tt.wantLaterBuyerBalance.String(), laterBuyerBalance.String()) + + s.Less(tt.wantMinConsumeGas, s.ctx.GasMeter().GasConsumed()) + + for _, existingRollApp := range tt.existingRollApps { + rollApp, found := s.rollAppKeeper.GetRollapp(s.ctx, existingRollApp.rollAppID) + s.Require().True(found) + s.Equal(existingRollApp.creator, rollApp.Owner) + if existingRollApp.alias != "" { + s.requireRollApp(existingRollApp.rollAppID).HasAlias(existingRollApp.alias) + } else { + s.requireRollApp(existingRollApp.rollAppID).HasNoAlias() + } + } + + if tt.afterTestFunc != nil { + tt.afterTestFunc(s) + } + }() + + if tt.wantErr { + s.Require().ErrorContains(err, tt.wantErrContains) + s.Require().Nil(resp) + return + } + + s.Require().NoError(err) + s.Require().NotNil(resp) + s.Equal(tt.wantBuyOrderId, resp.OrderId) + }) + } +} diff --git a/x/dymns/keeper/msg_server_place_sell_order.go b/x/dymns/keeper/msg_server_place_sell_order.go new file mode 100644 index 000000000..8f102cfcd --- /dev/null +++ b/x/dymns/keeper/msg_server_place_sell_order.go @@ -0,0 +1,205 @@ +package keeper + +import ( + "context" + + "github.com/dymensionxyz/gerr-cosmos/gerrc" + + errorsmod "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" +) + +// PlaceSellOrder is message handler, +// handles creating a Sell-Order that advertise a Dym-Name/Alias is for sale, performed by the owner. +func (k msgServer) PlaceSellOrder(goCtx context.Context, msg *dymnstypes.MsgPlaceSellOrder) (*dymnstypes.MsgPlaceSellOrderResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + if err := msg.ValidateBasic(); err != nil { + return nil, err + } + + priceParams := k.PriceParams(ctx) + miscParams := k.MiscParams(ctx) + + var resp *dymnstypes.MsgPlaceSellOrderResponse + var err error + + // process the Sell-Order based on the asset type + + if msg.AssetType == dymnstypes.TypeName { + resp, err = k.processPlaceSellOrderWithAssetTypeDymName(ctx, msg, priceParams, miscParams) + } else if msg.AssetType == dymnstypes.TypeAlias { + resp, err = k.processPlaceSellOrderWithAssetTypeAlias(ctx, msg, priceParams, miscParams) + } else { + err = errorsmod.Wrapf(gerrc.ErrInvalidArgument, "invalid asset type: %s", msg.AssetType) + } + if err != nil { + return nil, err + } + + // Charge protocol fee. + // The protocol fee mechanism is used to prevent spamming to the network. + consumeMinimumGas(ctx, dymnstypes.OpGasPlaceSellOrder, "PlaceSellOrder") + + return resp, nil +} + +// processPlaceSellOrderWithAssetTypeDymName handles the message handled by PlaceSellOrder, type Dym-Name. +func (k msgServer) processPlaceSellOrderWithAssetTypeDymName( + ctx sdk.Context, + msg *dymnstypes.MsgPlaceSellOrder, priceParams dymnstypes.PriceParams, miscParams dymnstypes.MiscParams, +) (*dymnstypes.MsgPlaceSellOrderResponse, error) { + if !miscParams.EnableTradingName { + return nil, errorsmod.Wrapf(gerrc.ErrFailedPrecondition, "trading of Dym-Name is disabled") + } + + dymName, err := k.validatePlaceSellOrderWithAssetTypeDymName(ctx, msg, priceParams) + if err != nil { + return nil, err + } + + so := msg.ToSellOrder() + so.ExpireAt = ctx.BlockTime().Add(miscParams.SellOrderDuration).Unix() + + if so.ExpireAt >= dymName.ExpireAt { + return nil, errorsmod.Wrap( + gerrc.ErrPermissionDenied, "the remaining time of the Dym-Name is too short", + ) + } + + if err := so.Validate(); err != nil { + panic(errorsmod.Wrap(err, "un-expected invalid state of created SO")) + } + + if err := k.SetSellOrder(ctx, so); err != nil { + return nil, err + } + + aSoe := k.GetActiveSellOrdersExpiration(ctx, so.AssetType) + aSoe.Add(so.AssetId, so.ExpireAt) + if err := k.SetActiveSellOrdersExpiration(ctx, aSoe, so.AssetType); err != nil { + return nil, err + } + + return &dymnstypes.MsgPlaceSellOrderResponse{}, nil +} + +// validatePlaceSellOrderWithAssetTypeDymName handles validation for message handled by PlaceSellOrder, type Dym-Name. +func (k msgServer) validatePlaceSellOrderWithAssetTypeDymName( + ctx sdk.Context, + msg *dymnstypes.MsgPlaceSellOrder, priceParams dymnstypes.PriceParams, +) (*dymnstypes.DymName, error) { + dymName := k.GetDymName(ctx, msg.AssetId) + if dymName == nil { + return nil, errorsmod.Wrapf(gerrc.ErrNotFound, "Dym-Name: %s", msg.AssetId) + } + + if dymName.Owner != msg.Owner { + return nil, errorsmod.Wrap(gerrc.ErrPermissionDenied, "not the owner of the Dym-Name") + } + + if dymName.IsExpiredAtCtx(ctx) { + return nil, errorsmod.Wrap(gerrc.ErrUnauthenticated, "Dym-Name is already expired") + } + + existingActiveSo := k.GetSellOrder(ctx, dymName.Name, msg.AssetType) + if existingActiveSo != nil { + if existingActiveSo.HasFinishedAtCtx(ctx) { + return nil, errorsmod.Wrap( + gerrc.ErrAlreadyExists, + "an active expired/completed Sell-Order already exists for the Dym-Name, must wait until processed", + ) + } + return nil, errorsmod.Wrap(gerrc.ErrAlreadyExists, "an active Sell-Order already exists for the Dym-Name") + } + + if msg.MinPrice.Denom != priceParams.PriceDenom { + return nil, errorsmod.Wrapf( + gerrc.ErrInvalidArgument, + "the only denom allowed as price: %s", priceParams.PriceDenom, + ) + } + + return dymName, nil +} + +// processPlaceSellOrderWithAssetTypeAlias handles the message handled by PlaceSellOrder, type Alias. +func (k msgServer) processPlaceSellOrderWithAssetTypeAlias( + ctx sdk.Context, + msg *dymnstypes.MsgPlaceSellOrder, priceParams dymnstypes.PriceParams, miscParams dymnstypes.MiscParams, +) (*dymnstypes.MsgPlaceSellOrderResponse, error) { + if !miscParams.EnableTradingAlias { + return nil, errorsmod.Wrapf(gerrc.ErrFailedPrecondition, "trading of Alias is disabled") + } + + err := k.validatePlaceSellOrderWithAssetTypeAlias(ctx, msg, priceParams) + if err != nil { + return nil, err + } + + so := msg.ToSellOrder() + so.ExpireAt = ctx.BlockTime().Add(miscParams.SellOrderDuration).Unix() + + if err := so.Validate(); err != nil { + panic(errorsmod.Wrap(err, "un-expected invalid state of created SO")) + } + + if err := k.SetSellOrder(ctx, so); err != nil { + return nil, err + } + + aSoe := k.GetActiveSellOrdersExpiration(ctx, so.AssetType) + aSoe.Add(so.AssetId, so.ExpireAt) + if err := k.SetActiveSellOrdersExpiration(ctx, aSoe, so.AssetType); err != nil { + return nil, err + } + + return &dymnstypes.MsgPlaceSellOrderResponse{}, nil +} + +// validatePlaceSellOrderWithAssetTypeAlias handles validation for message handled by PlaceSellOrder, type Alias. +func (k msgServer) validatePlaceSellOrderWithAssetTypeAlias( + ctx sdk.Context, + msg *dymnstypes.MsgPlaceSellOrder, priceParams dymnstypes.PriceParams, +) error { + alias := msg.AssetId + + if k.IsAliasPresentsInParamsAsAliasOrChainId(ctx, msg.AssetId) { + // Please read the `processActiveAliasSellOrders` method (hooks.go) for more information. + return errorsmod.Wrapf(gerrc.ErrPermissionDenied, + "prohibited to trade aliases which is reserved for chain-id or alias in module params: %s", msg.AssetId, + ) + } + + sourceRollAppId, found := k.GetRollAppIdByAlias(ctx, alias) + if !found { + return errorsmod.Wrapf(gerrc.ErrNotFound, "alias: %s", alias) + } + + if !k.IsRollAppCreator(ctx, sourceRollAppId, msg.Owner) { + return errorsmod.Wrapf(gerrc.ErrPermissionDenied, + "not the owner of the RollApp using the alias: %s", sourceRollAppId, + ) + } + + existingActiveSo := k.GetSellOrder(ctx, alias, msg.AssetType) + if existingActiveSo != nil { + if existingActiveSo.HasFinishedAtCtx(ctx) { + return errorsmod.Wrap( + gerrc.ErrAlreadyExists, + "an active expired/completed Sell-Order already exists for the Alias, must wait until processed", + ) + } + return errorsmod.Wrap(gerrc.ErrAlreadyExists, "an active Sell-Order already exists for the Alias") + } + + if msg.MinPrice.Denom != priceParams.PriceDenom { + return errorsmod.Wrapf( + gerrc.ErrInvalidArgument, + "the only denom allowed as price: %s", priceParams.PriceDenom, + ) + } + + return nil +} diff --git a/x/dymns/keeper/msg_server_place_sell_order_test.go b/x/dymns/keeper/msg_server_place_sell_order_test.go new file mode 100644 index 000000000..262629445 --- /dev/null +++ b/x/dymns/keeper/msg_server_place_sell_order_test.go @@ -0,0 +1,599 @@ +package keeper_test + +import ( + "fmt" + "time" + + "github.com/dymensionxyz/sdk-utils/utils/uptr" + + rollapptypes "github.com/dymensionxyz/dymension/v3/x/rollapp/types" + + "github.com/dymensionxyz/gerr-cosmos/gerrc" + + sdk "github.com/cosmos/cosmos-sdk/types" + dymnskeeper "github.com/dymensionxyz/dymension/v3/x/dymns/keeper" + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" +) + +func (s *KeeperTestSuite) Test_msgServer_PlaceSellOrder_DymName() { + const daysSellOrderDuration = 7 + + s.updateModuleParams(func(moduleParams dymnstypes.Params) dymnstypes.Params { + moduleParams.Misc.SellOrderDuration = daysSellOrderDuration * 24 * time.Hour + return moduleParams + }) + s.SaveCurrentContext() + + s.Run("reject if message not pass validate basic", func() { + _, err := dymnskeeper.NewMsgServerImpl(s.dymNsKeeper).PlaceSellOrder(s.ctx, &dymnstypes.MsgPlaceSellOrder{}) + + s.Require().ErrorContains(err, gerrc.ErrInvalidArgument.Error()) + }) + + const name = "my-name" + + ownerA := testAddr(1).bech32() + notOwnerA := testAddr(2).bech32() + bidderA := testAddr(3).bech32() + + coin100 := s.coin(100) + coin200 := s.coin(200) + coin300 := s.coin(300) + + tests := []struct { + name string + withoutDymName bool + existingSo *dymnstypes.SellOrder + dymNameExpiryOffsetDays int64 + customOwner string + customDymNameOwner string + minPrice sdk.Coin + sellPrice *sdk.Coin + preRunSetup func(*KeeperTestSuite) + wantErr bool + wantErrContains string + }{ + { + name: "fail - Dym-Name does not exists", + withoutDymName: true, + minPrice: coin100, + wantErr: true, + wantErrContains: fmt.Sprintf("Dym-Name: %s: not found", name), + }, + { + name: "fail - wrong owner", + customOwner: ownerA, + customDymNameOwner: notOwnerA, + minPrice: coin100, + wantErr: true, + wantErrContains: "not the owner of the Dym-Name", + }, + { + name: "fail - expired Dym-Name", + withoutDymName: false, + existingSo: nil, + dymNameExpiryOffsetDays: -1, + minPrice: coin100, + wantErr: true, + wantErrContains: "Dym-Name is already expired", + }, + { + name: "fail - reject SO if the expiry pass Dym-Name expiry", + withoutDymName: false, + existingSo: nil, + dymNameExpiryOffsetDays: daysSellOrderDuration - 1, + minPrice: coin100, + wantErr: true, + wantErrContains: "the remaining time of the Dym-Name is too short", + }, + { + name: "fail - existing active SO, not finished", + existingSo: &dymnstypes.SellOrder{ + AssetType: dymnstypes.TypeName, + ExpireAt: s.now.Add(time.Hour).Unix(), + MinPrice: coin100, + SellPrice: &coin200, + }, + minPrice: coin100, + wantErr: true, + wantErrContains: "an active Sell-Order already exists", + }, + { + name: "fail - existing active SO, expired", + existingSo: &dymnstypes.SellOrder{ + AssetType: dymnstypes.TypeName, + ExpireAt: s.now.Add(-1 * time.Hour).Unix(), + MinPrice: coin100, + SellPrice: &coin200, + }, + minPrice: coin100, + wantErr: true, + wantErrContains: "an active expired/completed Sell-Order already exists ", + }, + { + name: "fail - existing active SO, not expired, completed", + existingSo: &dymnstypes.SellOrder{ + AssetType: dymnstypes.TypeName, + ExpireAt: s.now.Add(time.Hour).Unix(), + MinPrice: coin100, + SellPrice: &coin200, + HighestBid: &dymnstypes.SellOrderBid{ + Bidder: bidderA, + Price: coin200, + }, + }, + minPrice: coin100, + wantErr: true, + wantErrContains: "an active expired/completed Sell-Order already exists ", + }, + { + name: "fail - existing active SO, expired, completed", + existingSo: &dymnstypes.SellOrder{ + AssetType: dymnstypes.TypeName, + ExpireAt: s.now.Add(-1 * time.Hour).Unix(), + MinPrice: coin100, + SellPrice: &coin200, + HighestBid: &dymnstypes.SellOrderBid{ + Bidder: bidderA, + Price: coin200, + }, + }, + minPrice: coin100, + wantErr: true, + wantErrContains: "an active expired/completed Sell-Order already exists", + }, + { + name: "fail - not allowed denom", + minPrice: sdk.NewInt64Coin("u"+s.priceDenom(), 100), + wantErr: true, + wantErrContains: fmt.Sprintf("the only denom allowed as price: %s", s.priceDenom()), + }, + { + name: "pass - successfully place Dym-Name Sell-Order, without sell price", + dymNameExpiryOffsetDays: 9999, + minPrice: coin100, + sellPrice: nil, + }, + { + name: "pass - successfully place Dym-Name Sell-Order, without sell price", + dymNameExpiryOffsetDays: 9999, + minPrice: coin100, + sellPrice: uptr.To(s.coin(0)), + }, + { + name: "pass - successfully place Dym-Name Sell-Order, with sell price", + dymNameExpiryOffsetDays: 9999, + minPrice: coin100, + sellPrice: &coin300, + }, + { + name: "pass - successfully place Dym-Name Sell-Order, with sell price equals to min-price", + dymNameExpiryOffsetDays: 9999, + minPrice: coin100, + sellPrice: &coin100, + }, + { + name: "fail - can NOT place Dym-Name Sell-Order, when Dym-Name trading is disabled", + dymNameExpiryOffsetDays: 9999, + minPrice: coin100, + sellPrice: nil, + preRunSetup: func(*KeeperTestSuite) { + s.updateModuleParams(func(moduleParams dymnstypes.Params) dymnstypes.Params { + moduleParams.Misc.EnableTradingName = false + return moduleParams + }) + }, + wantErr: true, + wantErrContains: "trading of Dym-Name is disabled", + }, + } + for _, tt := range tests { + s.Run(tt.name, func() { + s.RefreshContext() + + useDymNameOwner := ownerA + if tt.customDymNameOwner != "" { + useDymNameOwner = tt.customDymNameOwner + } + useDymNameExpiry := s.ctx.BlockTime().Add( + time.Hour * 24 * time.Duration(tt.dymNameExpiryOffsetDays), + ).Unix() + + if !tt.withoutDymName { + dymName := dymnstypes.DymName{ + Name: name, + Owner: useDymNameOwner, + Controller: useDymNameOwner, + ExpireAt: useDymNameExpiry, + } + err := s.dymNsKeeper.SetDymName(s.ctx, dymName) + s.Require().NoError(err) + } + + if tt.existingSo != nil { + tt.existingSo.AssetId = name + err := s.dymNsKeeper.SetSellOrder(s.ctx, *tt.existingSo) + s.Require().NoError(err) + } + + useOwner := ownerA + if tt.customOwner != "" { + useOwner = tt.customOwner + } + msg := &dymnstypes.MsgPlaceSellOrder{ + AssetId: name, + AssetType: dymnstypes.TypeName, + MinPrice: tt.minPrice, + SellPrice: tt.sellPrice, + Owner: useOwner, + } + + if tt.preRunSetup != nil { + tt.preRunSetup(s) + } + + resp, err := dymnskeeper.NewMsgServerImpl(s.dymNsKeeper).PlaceSellOrder(s.ctx, msg) + moduleParams := s.dymNsKeeper.GetParams(s.ctx) + + defer func() { + laterDymName := s.dymNsKeeper.GetDymName(s.ctx, name) + if tt.withoutDymName { + s.Require().Nil(laterDymName) + return + } + + s.Require().NotNil(laterDymName) + s.Require().Equal(dymnstypes.DymName{ + Name: name, + Owner: useDymNameOwner, + Controller: useDymNameOwner, + ExpireAt: useDymNameExpiry, + }, *laterDymName, "Dym-Name record should not be changed in any case") + }() + + if tt.wantErr { + s.Require().NotEmpty(tt.wantErrContains, "mis-configured test case") + s.Require().Error(err) + s.Require().Contains(err.Error(), tt.wantErrContains) + + s.Require().Nil(resp) + + so := s.dymNsKeeper.GetSellOrder(s.ctx, name, dymnstypes.TypeName) + if tt.existingSo != nil { + s.Require().NotNil(so) + s.Require().Equal(*tt.existingSo, *so) + } else { + s.Require().Nil(so) + } + + s.Require().Less( + s.ctx.GasMeter().GasConsumed(), dymnstypes.OpGasPlaceSellOrder, + "should not consume params gas on failed operation", + ) + return + } + + s.Require().NoError(err) + s.Require().NotNil(resp) + + so := s.dymNsKeeper.GetSellOrder(s.ctx, name, dymnstypes.TypeName) + s.Require().NotNil(so) + + expectedSo := dymnstypes.SellOrder{ + AssetId: name, + AssetType: dymnstypes.TypeName, + ExpireAt: s.ctx.BlockTime().Add(moduleParams.Misc.SellOrderDuration).Unix(), + MinPrice: msg.MinPrice, + SellPrice: msg.SellPrice, + HighestBid: nil, + } + if !expectedSo.HasSetSellPrice() { + expectedSo.SellPrice = nil + } + + s.Require().Nil(so.HighestBid, "highest bid should not be set") + + s.Require().Equal(expectedSo, *so) + + s.Require().GreaterOrEqual( + s.ctx.GasMeter().GasConsumed(), dymnstypes.OpGasPlaceSellOrder, + "should consume params gas", + ) + + aSoe := s.dymNsKeeper.GetActiveSellOrdersExpiration(s.ctx, dymnstypes.TypeName) + + var found bool + for _, record := range aSoe.Records { + if record.AssetId == name { + found = true + s.Require().Equal(expectedSo.ExpireAt, record.ExpireAt) + break + } + } + + s.Require().True(found) + }) + } +} + +func (s *KeeperTestSuite) Test_msgServer_PlaceSellOrder_Alias() { + const daysSellOrderDuration = 7 + denom := s.coin(0).Denom + + s.updateModuleParams(func(moduleParams dymnstypes.Params) dymnstypes.Params { + moduleParams.Misc.SellOrderDuration = daysSellOrderDuration * 24 * time.Hour + return moduleParams + }) + s.SaveCurrentContext() + + const srcRollAppId = "rollapp_1-1" + const alias = "alias" + const dstRollAppId = "rollapp_2-2" + + ownerA := testAddr(1).bech32() + notOwnerA := testAddr(2).bech32() + bidderA := testAddr(3).bech32() + + coin100 := s.coin(100) + coin200 := s.coin(200) + coin300 := s.coin(300) + + tests := []struct { + name string + withoutAlias bool + existingSo *dymnstypes.SellOrder + customOwner string + customRollAppOwner string + minPrice sdk.Coin + sellPrice *sdk.Coin + preRunSetup func(*KeeperTestSuite) + wantErr bool + wantErrContains string + }{ + { + name: "fail - alias does not exists", + withoutAlias: true, + minPrice: coin100, + wantErr: true, + wantErrContains: "alias: alias: not found", + }, + { + name: "fail - wrong owner", + customOwner: ownerA, + customRollAppOwner: notOwnerA, + minPrice: coin100, + wantErr: true, + wantErrContains: "not the owner of the RollApp using the alias", + }, + { + name: "fail - existing active SO, not finished", + existingSo: &dymnstypes.SellOrder{ + AssetType: dymnstypes.TypeAlias, + ExpireAt: s.now.Add(time.Hour).Unix(), + MinPrice: coin100, + SellPrice: &coin200, + }, + minPrice: coin100, + wantErr: true, + wantErrContains: "an active Sell-Order already exists", + }, + { + name: "fail - existing active SO, expired", + existingSo: &dymnstypes.SellOrder{ + AssetType: dymnstypes.TypeAlias, + ExpireAt: s.now.Add(-1 * time.Hour).Unix(), + MinPrice: coin100, + SellPrice: &coin200, + }, + minPrice: coin100, + wantErr: true, + wantErrContains: "an active expired/completed Sell-Order already exists ", + }, + { + name: "fail - existing active SO, not expired, completed", + existingSo: &dymnstypes.SellOrder{ + AssetType: dymnstypes.TypeAlias, + ExpireAt: s.now.Add(time.Hour).Unix(), + MinPrice: coin100, + SellPrice: &coin200, + HighestBid: &dymnstypes.SellOrderBid{ + Bidder: bidderA, + Price: coin200, + Params: []string{dstRollAppId}, + }, + }, + minPrice: coin100, + wantErr: true, + wantErrContains: "an active expired/completed Sell-Order already exists ", + }, + { + name: "fail - existing active SO, expired, completed", + existingSo: &dymnstypes.SellOrder{ + AssetType: dymnstypes.TypeAlias, + ExpireAt: s.now.Add(-1 * time.Hour).Unix(), + MinPrice: coin100, + SellPrice: &coin200, + HighestBid: &dymnstypes.SellOrderBid{ + Bidder: bidderA, + Price: coin200, + Params: []string{dstRollAppId}, + }, + }, + minPrice: coin100, + wantErr: true, + wantErrContains: "an active expired/completed Sell-Order already exists", + }, + { + name: "fail - not allowed denom", + minPrice: sdk.NewInt64Coin("u"+denom, 100), + wantErr: true, + wantErrContains: fmt.Sprintf("the only denom allowed as price: %s", denom), + }, + { + name: "pass - successfully place Alias Sell-Order, without sell price", + minPrice: coin100, + sellPrice: nil, + }, + { + name: "fail - can NOT place sell order if alias which present in params", + minPrice: coin100, + sellPrice: nil, + preRunSetup: func(*KeeperTestSuite) { + moduleParams := s.dymNsKeeper.GetParams(s.ctx) + moduleParams.Chains.AliasesOfChainIds = []dymnstypes.AliasesOfChainId{ + { + ChainId: "some-chain", + Aliases: []string{alias}, + }, + } + err := s.dymNsKeeper.SetParams(s.ctx, moduleParams) + s.Require().NoError(err) + }, + wantErr: true, + wantErrContains: "prohibited to trade aliases which is reserved for chain-id or alias in module params", + }, + { + name: "pass - successfully place Alias Sell-Order, without sell price", + minPrice: coin100, + sellPrice: uptr.To(s.coin(0)), + }, + { + name: "pass - successfully place Alias Sell-Order, with sell price", + minPrice: coin100, + sellPrice: &coin300, + }, + { + name: "pass - successfully place Alias Sell-Order, with sell price equals to min-price", + minPrice: coin100, + sellPrice: &coin100, + }, + { + name: "fail - can NOT place Alias Sell-Order, when Alias trading is disabled", + minPrice: coin100, + sellPrice: nil, + preRunSetup: func(*KeeperTestSuite) { + moduleParams := s.dymNsKeeper.GetParams(s.ctx) + moduleParams.Misc.EnableTradingAlias = false + err := s.dymNsKeeper.SetParams(s.ctx, moduleParams) + s.Require().NoError(err) + }, + wantErr: true, + wantErrContains: "trading of Alias is disabled", + }, + } + for _, tt := range tests { + s.Run(tt.name, func() { + s.RefreshContext() + + useRollAppOwner := ownerA + if tt.customRollAppOwner != "" { + useRollAppOwner = tt.customRollAppOwner + } + + s.rollAppKeeper.SetRollapp(s.ctx, rollapptypes.Rollapp{ + RollappId: srcRollAppId, + Owner: useRollAppOwner, + }) + if !tt.withoutAlias { + err := s.dymNsKeeper.SetAliasForRollAppId(s.ctx, srcRollAppId, alias) + s.Require().NoError(err) + } + + if tt.existingSo != nil { + tt.existingSo.AssetId = alias + err := s.dymNsKeeper.SetSellOrder(s.ctx, *tt.existingSo) + s.Require().NoError(err) + } + + useOwner := ownerA + if tt.customOwner != "" { + useOwner = tt.customOwner + } + msg := &dymnstypes.MsgPlaceSellOrder{ + AssetId: alias, + AssetType: dymnstypes.TypeAlias, + MinPrice: tt.minPrice, + SellPrice: tt.sellPrice, + Owner: useOwner, + } + + if tt.preRunSetup != nil { + tt.preRunSetup(s) + } + + resp, err := dymnskeeper.NewMsgServerImpl(s.dymNsKeeper).PlaceSellOrder(s.ctx, msg) + moduleParams := s.dymNsKeeper.GetParams(s.ctx) + + defer func() { + if tt.withoutAlias { + s.requireAlias(alias).NotInUse() + s.requireRollApp(srcRollAppId).HasNoAlias() + } else { + s.requireAlias(alias).LinkedToRollApp(srcRollAppId) + } + }() + + if tt.wantErr { + s.Require().NotEmpty(tt.wantErrContains, "mis-configured test case") + s.Require().Error(err) + s.Require().Contains(err.Error(), tt.wantErrContains) + + s.Require().Nil(resp) + + so := s.dymNsKeeper.GetSellOrder(s.ctx, alias, dymnstypes.TypeAlias) + if tt.existingSo != nil { + s.Require().NotNil(so) + s.Require().Equal(*tt.existingSo, *so) + } else { + s.Require().Nil(so) + } + + s.Require().Less( + s.ctx.GasMeter().GasConsumed(), dymnstypes.OpGasPlaceSellOrder, + "should not consume params gas on failed operation", + ) + return + } + + s.Require().NoError(err) + s.Require().NotNil(resp) + + so := s.dymNsKeeper.GetSellOrder(s.ctx, alias, dymnstypes.TypeAlias) + s.Require().NotNil(so) + + expectedSo := dymnstypes.SellOrder{ + AssetId: alias, + AssetType: dymnstypes.TypeAlias, + ExpireAt: s.ctx.BlockTime().Add(moduleParams.Misc.SellOrderDuration).Unix(), + MinPrice: msg.MinPrice, + SellPrice: msg.SellPrice, + HighestBid: nil, + } + if !expectedSo.HasSetSellPrice() { + expectedSo.SellPrice = nil + } + + s.Require().Nil(so.HighestBid, "highest bid should not be set") + + s.Require().Equal(expectedSo, *so) + + s.Require().GreaterOrEqual( + s.ctx.GasMeter().GasConsumed(), dymnstypes.OpGasPlaceSellOrder, + "should consume params gas", + ) + + aSoe := s.dymNsKeeper.GetActiveSellOrdersExpiration(s.ctx, dymnstypes.TypeAlias) + + var found bool + for _, record := range aSoe.Records { + if record.AssetId == alias { + found = true + s.Require().Equal(expectedSo.ExpireAt, record.ExpireAt) + break + } + } + + s.Require().True(found) + }) + } +} diff --git a/x/dymns/keeper/msg_server_purchase_order.go b/x/dymns/keeper/msg_server_purchase_order.go new file mode 100644 index 000000000..35fcfd2cb --- /dev/null +++ b/x/dymns/keeper/msg_server_purchase_order.go @@ -0,0 +1,274 @@ +package keeper + +import ( + "context" + + errorsmod "cosmossdk.io/errors" + "github.com/dymensionxyz/gerr-cosmos/gerrc" + + sdk "github.com/cosmos/cosmos-sdk/types" + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" +) + +// PurchaseOrder is message handler, +// handles purchasing a Dym-Name/Alias from a Sell-Order, performed by the buyer. +func (k msgServer) PurchaseOrder(goCtx context.Context, msg *dymnstypes.MsgPurchaseOrder) (*dymnstypes.MsgPurchaseOrderResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + if err := msg.ValidateBasic(); err != nil { + return nil, err + } + + miscParams := k.MiscParams(ctx) + + var resp *dymnstypes.MsgPurchaseOrderResponse + var err error + + // process the purchase order based on the asset type + + if msg.AssetType == dymnstypes.TypeName { + resp, err = k.processPurchaseOrderWithAssetTypeDymName(ctx, msg, miscParams) + } else if msg.AssetType == dymnstypes.TypeAlias { + resp, err = k.processPurchaseOrderWithAssetTypeAlias(ctx, msg, miscParams) + } else { + err = errorsmod.Wrapf(gerrc.ErrInvalidArgument, "invalid asset type: %s", msg.AssetType) + } + if err != nil { + return nil, err + } + + // charge protocol fee + consumeMinimumGas(ctx, dymnstypes.OpGasPlaceBidOnSellOrder, "PurchaseOrder") + + return resp, nil +} + +// processPurchaseOrderWithAssetTypeDymName handles the message handled by PurchaseOrder, type Dym-Name. +func (k msgServer) processPurchaseOrderWithAssetTypeDymName( + ctx sdk.Context, + msg *dymnstypes.MsgPurchaseOrder, miscParams dymnstypes.MiscParams, +) (*dymnstypes.MsgPurchaseOrderResponse, error) { + if !miscParams.EnableTradingName { + return nil, errorsmod.Wrapf(gerrc.ErrFailedPrecondition, "trading of Dym-Name is disabled") + } + + dymName, so, err := k.validatePurchaseOrderWithAssetTypeDymName(ctx, msg) + if err != nil { + return nil, err + } + + if so.HighestBid != nil { + // refund previous bidder + if err := k.RefundBid(ctx, *so.HighestBid, so.AssetType); err != nil { + return nil, err + } + } + + // deduct offer price from buyer's account + if err := k.bankKeeper.SendCoinsFromAccountToModule( + ctx, + sdk.MustAccAddressFromBech32(msg.Buyer), + dymnstypes.ModuleName, + sdk.Coins{msg.Offer}, + ); err != nil { + return nil, err + } + + // record new highest bid + so.HighestBid = &dymnstypes.SellOrderBid{ + Bidder: msg.Buyer, + Price: msg.Offer, + Params: msg.Params, + } + + // after highest bid updated, update SO to store to reflect the new state + if err := k.SetSellOrder(ctx, *so); err != nil { + return nil, err + } + + // try to complete the purchase + + if so.HasFinishedAtCtx(ctx) { + if err := k.CompleteDymNameSellOrder(ctx, dymName.Name); err != nil { + return nil, err + } + } + + return &dymnstypes.MsgPurchaseOrderResponse{}, nil +} + +// validatePurchaseOrderWithAssetTypeDymName handles validation for the message handled by PurchaseOrder, type Dym-Name. +func (k msgServer) validatePurchaseOrderWithAssetTypeDymName(ctx sdk.Context, msg *dymnstypes.MsgPurchaseOrder) (*dymnstypes.DymName, *dymnstypes.SellOrder, error) { + dymName := k.GetDymName(ctx, msg.AssetId) + if dymName == nil { + return nil, nil, errorsmod.Wrapf(gerrc.ErrNotFound, "Dym-Name: %s", msg.AssetId) + } + + if dymName.Owner == msg.Buyer { + return nil, nil, errorsmod.Wrap(gerrc.ErrPermissionDenied, "cannot purchase your own dym name") + } + + so := k.GetSellOrder(ctx, msg.AssetId, msg.AssetType) + if so == nil { + return nil, nil, errorsmod.Wrapf(gerrc.ErrNotFound, "Sell-Order: %s", msg.AssetId) + } + + if so.HasExpiredAtCtx(ctx) { + return nil, nil, errorsmod.Wrap(gerrc.ErrFailedPrecondition, "cannot purchase an expired order") + } + + if so.HasFinishedAtCtx(ctx) { + return nil, nil, errorsmod.Wrap(gerrc.ErrFailedPrecondition, "cannot purchase a completed order") + } + + if msg.Offer.Denom != so.MinPrice.Denom { + return nil, nil, errorsmod.Wrapf(gerrc.ErrInvalidArgument, + "offer denom does not match the order denom: %s != %s", + msg.Offer.Denom, so.MinPrice.Denom, + ) + } + + if msg.Offer.IsLT(so.MinPrice) { + return nil, nil, errorsmod.Wrap(gerrc.ErrInvalidArgument, "offer is lower than minimum price") + } + + if so.HasSetSellPrice() { + if !msg.Offer.IsLTE(*so.SellPrice) { // overpaid protection + return nil, nil, errorsmod.Wrap(gerrc.ErrInvalidArgument, "offer is higher than sell price") + } + } + + if so.HighestBid != nil { + if msg.Offer.IsLTE(so.HighestBid.Price) { + return nil, nil, errorsmod.Wrap( + gerrc.ErrInvalidArgument, + "new offer must be higher than current highest bid", + ) + } + } + + return dymName, so, nil +} + +// processPurchaseOrderWithAssetTypeAlias handles the message handled by PurchaseOrder, type Alias. +func (k msgServer) processPurchaseOrderWithAssetTypeAlias( + ctx sdk.Context, + msg *dymnstypes.MsgPurchaseOrder, miscParams dymnstypes.MiscParams, +) (*dymnstypes.MsgPurchaseOrderResponse, error) { + if !miscParams.EnableTradingAlias { + return nil, errorsmod.Wrapf(gerrc.ErrFailedPrecondition, "trading of Alias is disabled") + } + + so, err := k.validatePurchaseOrderWithAssetTypeAlias(ctx, msg) + if err != nil { + return nil, err + } + + if so.HighestBid != nil { + // refund previous bidder + if err := k.RefundBid(ctx, *so.HighestBid, so.AssetType); err != nil { + return nil, err + } + } + + // deduct offer price from buyer's account + if err := k.bankKeeper.SendCoinsFromAccountToModule( + ctx, + sdk.MustAccAddressFromBech32(msg.Buyer), + dymnstypes.ModuleName, + sdk.Coins{msg.Offer}, + ); err != nil { + return nil, err + } + + // record new highest bid + so.HighestBid = &dymnstypes.SellOrderBid{ + Bidder: msg.Buyer, + Price: msg.Offer, + Params: msg.Params, + } + + // after highest bid updated, update SO to store to reflect the new state + if err := k.SetSellOrder(ctx, *so); err != nil { + return nil, err + } + + // try to complete the purchase + if so.HasFinishedAtCtx(ctx) { + if err := k.CompleteAliasSellOrder(ctx, so.AssetId); err != nil { + return nil, err + } + } + + return &dymnstypes.MsgPurchaseOrderResponse{}, nil +} + +// validatePurchaseOrderWithAssetTypeAlias handles validation for the message handled by PurchaseOrder, type Alias. +func (k msgServer) validatePurchaseOrderWithAssetTypeAlias(ctx sdk.Context, msg *dymnstypes.MsgPurchaseOrder) (*dymnstypes.SellOrder, error) { + destinationRollAppId := msg.Params[0] + + if !k.IsRollAppId(ctx, destinationRollAppId) { + return nil, errorsmod.Wrapf(gerrc.ErrInvalidArgument, "destination Roll-App does not exists: %s", destinationRollAppId) + } + + if !k.IsRollAppCreator(ctx, destinationRollAppId, msg.Buyer) { + return nil, errorsmod.Wrapf(gerrc.ErrPermissionDenied, "not the owner of the RollApp: %s", destinationRollAppId) + } + + existingRollAppIdUsingAlias, found := k.GetRollAppIdByAlias(ctx, msg.AssetId) + if !found { + return nil, errorsmod.Wrapf(gerrc.ErrNotFound, "alias not owned by any RollApp: %s", msg.AssetId) + } + + if destinationRollAppId == existingRollAppIdUsingAlias { + return nil, errorsmod.Wrap(gerrc.ErrInvalidArgument, "destination Roll-App ID is the same as the source") + } + + if k.IsAliasPresentsInParamsAsAliasOrChainId(ctx, msg.AssetId) { + // Please read the `processActiveAliasSellOrders` method (hooks.go) for more information. + return nil, errorsmod.Wrapf(gerrc.ErrPermissionDenied, + "prohibited to trade aliases which is reserved for chain-id or alias in module params: %s", msg.AssetId, + ) + } + + so := k.GetSellOrder(ctx, msg.AssetId, msg.AssetType) + if so == nil { + return nil, errorsmod.Wrapf(gerrc.ErrNotFound, "Sell-Order: %s", msg.AssetId) + } + + if so.HasExpiredAtCtx(ctx) { + return nil, errorsmod.Wrap(gerrc.ErrFailedPrecondition, "cannot purchase an expired order") + } + + if so.HasFinishedAtCtx(ctx) { + return nil, errorsmod.Wrap(gerrc.ErrFailedPrecondition, "cannot purchase a completed order") + } + + if msg.Offer.Denom != so.MinPrice.Denom { + return nil, errorsmod.Wrapf(gerrc.ErrInvalidArgument, + "offer denom does not match the order denom: %s != %s", + msg.Offer.Denom, so.MinPrice.Denom, + ) + } + + if msg.Offer.IsLT(so.MinPrice) { + return nil, errorsmod.Wrap(gerrc.ErrInvalidArgument, "offer is lower than minimum price") + } + + if so.HasSetSellPrice() { + if !msg.Offer.IsLTE(*so.SellPrice) { // overpaid protection + return nil, errorsmod.Wrap(gerrc.ErrInvalidArgument, "offer is higher than sell price") + } + } + + if so.HighestBid != nil { + if msg.Offer.IsLTE(so.HighestBid.Price) { + return nil, errorsmod.Wrap( + gerrc.ErrInvalidArgument, + "new offer must be higher than current highest bid", + ) + } + } + + return so, nil +} diff --git a/x/dymns/keeper/msg_server_purchase_order_test.go b/x/dymns/keeper/msg_server_purchase_order_test.go new file mode 100644 index 000000000..fa2911efd --- /dev/null +++ b/x/dymns/keeper/msg_server_purchase_order_test.go @@ -0,0 +1,1246 @@ +package keeper_test + +import ( + "fmt" + + "github.com/dymensionxyz/sdk-utils/utils/uptr" + + "github.com/dymensionxyz/gerr-cosmos/gerrc" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/dymensionxyz/dymension/v3/app/params" + dymnskeeper "github.com/dymensionxyz/dymension/v3/x/dymns/keeper" + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" +) + +func (s *KeeperTestSuite) Test_msgServer_PurchaseOrder_DymName() { + s.Run("reject if message not pass validate basic", func() { + _, err := dymnskeeper.NewMsgServerImpl(s.dymNsKeeper).PurchaseOrder(s.ctx, &dymnstypes.MsgPurchaseOrder{ + AssetType: dymnstypes.TypeName, + }) + + s.Require().ErrorContains(err, gerrc.ErrInvalidArgument.Error()) + }) + + s.Run("reject if message asset type is Unknown", func() { + _, err := dymnskeeper.NewMsgServerImpl(s.dymNsKeeper).PurchaseOrder(s.ctx, &dymnstypes.MsgPurchaseOrder{ + AssetId: "asset", + AssetType: dymnstypes.AssetType_AT_UNKNOWN, + Buyer: testAddr(0).bech32(), + Offer: s.coin(1), + }) + + s.Require().ErrorContains(err, gerrc.ErrInvalidArgument.Error()) + }) + + ownerA := testAddr(1).bech32() + buyerA := testAddr(2).bech32() + previousBidderA := testAddr(3).bech32() + + originalDymNameExpiry := s.now.Unix() + 100 + dymName := dymnstypes.DymName{ + Name: "my-name", + Owner: ownerA, + Controller: ownerA, + ExpireAt: s.now.Unix() + 100, + } + + const ownerOriginalBalance int64 = 1000 + const buyerOriginalBalance int64 = 500 + const previousBidderOriginalBalance int64 = 400 + const minPrice int64 = 100 + tests := []struct { + name string + withoutDymName bool + withoutSellOrder bool + expiredSellOrder bool + sellPrice int64 + previousBid int64 + skipPreMintModuleAccount bool + overrideBuyerOriginalBalance int64 + customBuyer string + newBid int64 + customBidDenom string + wantOwnershipChanged bool + wantErr bool + wantErrContains string + wantOwnerBalanceLater int64 + wantBuyerBalanceLater int64 + wantPreviousBidderBalanceLater int64 + }{ + { + name: "fail - Dym-Name does not exists, SO does not exists", + withoutDymName: true, + withoutSellOrder: true, + newBid: 100, + wantErr: true, + wantErrContains: fmt.Sprintf("Dym-Name: %s: not found", dymName.Name), + wantOwnerBalanceLater: ownerOriginalBalance, + wantBuyerBalanceLater: buyerOriginalBalance, + wantPreviousBidderBalanceLater: previousBidderOriginalBalance, + }, + { + name: "fail - Dym-Name does not exists, SO exists", + withoutDymName: true, + withoutSellOrder: false, + newBid: 100, + wantErr: true, + wantErrContains: fmt.Sprintf("Dym-Name: %s: not found", dymName.Name), + wantOwnerBalanceLater: ownerOriginalBalance, + wantBuyerBalanceLater: buyerOriginalBalance, + wantPreviousBidderBalanceLater: previousBidderOriginalBalance, + }, + { + name: "fail - Dym-Name exists, SO does not exists", + withoutDymName: false, + withoutSellOrder: true, + newBid: 100, + wantErr: true, + wantErrContains: fmt.Sprintf("Sell-Order: %s: not found", dymName.Name), + wantOwnerBalanceLater: ownerOriginalBalance, + wantBuyerBalanceLater: buyerOriginalBalance, + wantPreviousBidderBalanceLater: previousBidderOriginalBalance, + }, + { + name: "fail - self-purchase is not allowed", + customBuyer: ownerA, + newBid: 100, + wantErr: true, + wantErrContains: "cannot purchase your own dym name", + wantOwnerBalanceLater: ownerOriginalBalance, + wantBuyerBalanceLater: buyerOriginalBalance, + wantPreviousBidderBalanceLater: previousBidderOriginalBalance, + }, + { + name: "fail - invalid buyer address", + customBuyer: "invalidAddress", + newBid: 100, + wantErr: true, + wantErrContains: "buyer is not a valid bech32 account address", + wantOwnerBalanceLater: ownerOriginalBalance, + wantBuyerBalanceLater: buyerOriginalBalance, + wantPreviousBidderBalanceLater: previousBidderOriginalBalance, + }, + { + name: "fail - purchase an expired order, no bid", + expiredSellOrder: true, + newBid: 100, + wantErr: true, + wantErrContains: "cannot purchase an expired order", + wantOwnerBalanceLater: ownerOriginalBalance, + wantBuyerBalanceLater: buyerOriginalBalance, + wantPreviousBidderBalanceLater: previousBidderOriginalBalance, + }, + { + name: "fail - purchase a completed order, expired, with bid, without sell price", + expiredSellOrder: true, + sellPrice: 0, + previousBid: 200, + newBid: 300, + wantErr: true, + wantErrContains: "cannot purchase an expired order", + wantOwnerBalanceLater: ownerOriginalBalance, + wantBuyerBalanceLater: buyerOriginalBalance, + wantPreviousBidderBalanceLater: previousBidderOriginalBalance, + }, + { + name: "fail - purchase a completed order, expired, with sell price, with bid under sell price", + expiredSellOrder: true, + sellPrice: 300, + previousBid: 200, + newBid: 300, + wantErr: true, + wantErrContains: "cannot purchase an expired order", + wantOwnerBalanceLater: ownerOriginalBalance, + wantBuyerBalanceLater: buyerOriginalBalance, + wantPreviousBidderBalanceLater: previousBidderOriginalBalance, + }, + { + name: "fail - purchase a completed order, expired, with sell price, with bid = sell price", + expiredSellOrder: true, + sellPrice: 300, + previousBid: 300, + newBid: 300, + wantErr: true, + wantErrContains: "cannot purchase an expired order", + wantOwnerBalanceLater: ownerOriginalBalance, + wantBuyerBalanceLater: buyerOriginalBalance, + wantPreviousBidderBalanceLater: previousBidderOriginalBalance, + }, + { + name: "fail - purchase a completed order, not expired, fail because previous bid matches sell price", + expiredSellOrder: false, + sellPrice: 300, + previousBid: 300, + newBid: 300, + wantErr: true, + wantErrContains: "cannot purchase a completed order", + wantOwnerBalanceLater: ownerOriginalBalance, + wantBuyerBalanceLater: buyerOriginalBalance, + wantPreviousBidderBalanceLater: previousBidderOriginalBalance, + }, + { + name: "fail - purchase order, not expired, fail because lower than previous bid", + expiredSellOrder: false, + sellPrice: 300, + previousBid: 200, + newBid: 200 - 1, + wantErr: true, + wantErrContains: "new offer must be higher than current highest bid", + wantOwnerBalanceLater: ownerOriginalBalance, + wantBuyerBalanceLater: buyerOriginalBalance, + wantPreviousBidderBalanceLater: previousBidderOriginalBalance, + }, + { + name: "fail - purchase order, not expired, fail because equals to previous bid", + expiredSellOrder: false, + sellPrice: 300, + previousBid: 200, + newBid: 200, + wantErr: true, + wantErrContains: "new offer must be higher than current highest bid", + wantOwnerBalanceLater: ownerOriginalBalance, + wantBuyerBalanceLater: buyerOriginalBalance, + wantPreviousBidderBalanceLater: previousBidderOriginalBalance, + }, + { + name: "fail - purchase a completed order, expired, bid equals to previous bid", + expiredSellOrder: true, + sellPrice: 300, + previousBid: 200, + newBid: 200, + wantErr: true, + wantErrContains: "cannot purchase an expired order", + wantOwnerBalanceLater: ownerOriginalBalance, + wantBuyerBalanceLater: buyerOriginalBalance, + wantPreviousBidderBalanceLater: previousBidderOriginalBalance, + }, + { + name: "fail - purchase a completed order, expired, bid lower than previous bid", + expiredSellOrder: true, + sellPrice: 300, + previousBid: 200, + newBid: 200 - 1, + wantErr: true, + wantErrContains: "cannot purchase an expired order", + wantOwnerBalanceLater: ownerOriginalBalance, + wantBuyerBalanceLater: buyerOriginalBalance, + wantPreviousBidderBalanceLater: previousBidderOriginalBalance, + }, + { + name: "fail - mis-match denom", + expiredSellOrder: false, + newBid: 200, + customBidDenom: "u" + params.BaseDenom, + wantErr: true, + wantErrContains: "offer denom does not match the order denom", + wantOwnerBalanceLater: ownerOriginalBalance, + wantBuyerBalanceLater: buyerOriginalBalance, + wantPreviousBidderBalanceLater: previousBidderOriginalBalance, + }, + { + name: "fail - offer lower than min-price", + expiredSellOrder: false, + newBid: minPrice - 1, + wantErr: true, + wantErrContains: "offer is lower than minimum price", + wantOwnerBalanceLater: ownerOriginalBalance, + wantBuyerBalanceLater: buyerOriginalBalance, + wantPreviousBidderBalanceLater: previousBidderOriginalBalance, + }, + { + name: "fail - zero bid amount", + expiredSellOrder: false, + newBid: 0, + wantErr: true, + wantErrContains: "offer must be positive", + wantOwnerBalanceLater: ownerOriginalBalance, + wantBuyerBalanceLater: buyerOriginalBalance, + wantPreviousBidderBalanceLater: previousBidderOriginalBalance, + }, + { + name: "fail - offer higher than sell-price", + expiredSellOrder: false, + sellPrice: 300, + newBid: 300 + 1, + wantErr: true, + wantErrContains: "offer is higher than sell price", + wantOwnerBalanceLater: ownerOriginalBalance, + wantBuyerBalanceLater: buyerOriginalBalance, + wantPreviousBidderBalanceLater: previousBidderOriginalBalance, + }, + { + name: "fail - offer equals to previous bid, no sell price", + expiredSellOrder: false, + previousBid: 200, + newBid: 200, + wantErr: true, + wantErrContains: "new offer must be higher than current highest bid", + wantOwnerBalanceLater: ownerOriginalBalance, + wantBuyerBalanceLater: buyerOriginalBalance, + wantPreviousBidderBalanceLater: previousBidderOriginalBalance, + }, + { + name: "fail - offer lower than previous bid, no sell price", + expiredSellOrder: false, + previousBid: 200, + newBid: 200 - 1, + wantErr: true, + wantErrContains: "new offer must be higher than current highest bid", + wantOwnerBalanceLater: ownerOriginalBalance, + wantBuyerBalanceLater: buyerOriginalBalance, + wantPreviousBidderBalanceLater: previousBidderOriginalBalance, + }, + { + name: "fail - offer equals to previous bid, has sell price", + expiredSellOrder: false, + sellPrice: 300, + previousBid: 200, + newBid: 200, + wantErr: true, + wantErrContains: "new offer must be higher than current highest bid", + wantOwnerBalanceLater: ownerOriginalBalance, + wantBuyerBalanceLater: buyerOriginalBalance, + wantPreviousBidderBalanceLater: previousBidderOriginalBalance, + }, + { + name: "fail - offer lower than previous bid, has sell price", + expiredSellOrder: false, + sellPrice: 300, + previousBid: 200, + newBid: 200 - 1, + wantErr: true, + wantErrContains: "new offer must be higher than current highest bid", + wantOwnerBalanceLater: ownerOriginalBalance, + wantBuyerBalanceLater: buyerOriginalBalance, + wantPreviousBidderBalanceLater: previousBidderOriginalBalance, + }, + { + name: "pass - place bid, = min price, no previous bid, no sell price", + expiredSellOrder: false, + newBid: minPrice, + wantOwnershipChanged: false, + wantOwnerBalanceLater: ownerOriginalBalance, + wantBuyerBalanceLater: buyerOriginalBalance - minPrice, + wantPreviousBidderBalanceLater: previousBidderOriginalBalance, + }, + { + name: "pass - place bid, greater than previous bid, no sell price", + expiredSellOrder: false, + previousBid: minPrice, + newBid: minPrice + 1, + wantOwnershipChanged: false, + wantOwnerBalanceLater: ownerOriginalBalance, + wantBuyerBalanceLater: buyerOriginalBalance - (minPrice + 1), + wantPreviousBidderBalanceLater: previousBidderOriginalBalance + minPrice, // refund + }, + { + name: "fail - failed to refund previous bid", + expiredSellOrder: false, + previousBid: minPrice, + skipPreMintModuleAccount: true, + newBid: minPrice + 1, + wantOwnershipChanged: false, + wantErr: true, + wantErrContains: "insufficient funds", + wantOwnerBalanceLater: ownerOriginalBalance, + wantBuyerBalanceLater: buyerOriginalBalance, + wantPreviousBidderBalanceLater: previousBidderOriginalBalance, + }, + { + name: "fail - insufficient buyer funds", + expiredSellOrder: false, + overrideBuyerOriginalBalance: 1, + newBid: minPrice + 1, + wantOwnershipChanged: false, + wantErr: true, + wantErrContains: "insufficient funds", + wantOwnerBalanceLater: ownerOriginalBalance, + wantBuyerBalanceLater: 1, + wantPreviousBidderBalanceLater: previousBidderOriginalBalance, + }, + { + name: "pass - place bid, greater than previous bid, under sell price", + expiredSellOrder: false, + sellPrice: 300, + previousBid: minPrice, + newBid: 300 - 1, + wantOwnershipChanged: false, + wantOwnerBalanceLater: ownerOriginalBalance, + wantBuyerBalanceLater: buyerOriginalBalance - (300 - 1), + wantPreviousBidderBalanceLater: previousBidderOriginalBalance + minPrice, // refund + }, + { + name: "pass - place bid, greater than previous bid, equals sell price, transfer ownership", + expiredSellOrder: false, + sellPrice: 300, + previousBid: minPrice, + newBid: 300, + wantOwnershipChanged: true, + wantOwnerBalanceLater: ownerOriginalBalance + 300, + wantBuyerBalanceLater: buyerOriginalBalance - 300, + wantPreviousBidderBalanceLater: previousBidderOriginalBalance + minPrice, // refund + }, + { + name: "pass - refund previous bidder", + expiredSellOrder: false, + previousBid: minPrice, + newBid: 200, + wantOwnershipChanged: false, + wantOwnerBalanceLater: ownerOriginalBalance, + wantBuyerBalanceLater: buyerOriginalBalance - 200, + wantPreviousBidderBalanceLater: previousBidderOriginalBalance + minPrice, // refund + }, + } + for _, tt := range tests { + s.Run(tt.name, func() { + s.RefreshContext() + + useOwnerOriginalBalance := ownerOriginalBalance + useBuyerOriginalBalance := buyerOriginalBalance + if tt.overrideBuyerOriginalBalance > 0 { + useBuyerOriginalBalance = tt.overrideBuyerOriginalBalance + } + usePreviousBidderOriginalBalance := previousBidderOriginalBalance + + s.mintToAccount(ownerA, useOwnerOriginalBalance) + s.mintToAccount(buyerA, useBuyerOriginalBalance) + s.mintToAccount(previousBidderA, usePreviousBidderOriginalBalance) + + dymName.Configs = []dymnstypes.DymNameConfig{ + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Value: ownerA, + }, + } + + if !tt.withoutDymName { + s.setDymNameWithFunctionsAfter(dymName) + } + + so := dymnstypes.SellOrder{ + AssetId: dymName.Name, + AssetType: dymnstypes.TypeName, + MinPrice: s.coin(minPrice), + } + + if tt.expiredSellOrder { + so.ExpireAt = s.now.Unix() - 1 + } else { + so.ExpireAt = s.now.Unix() + 100 + } + + s.Require().GreaterOrEqual(tt.sellPrice, int64(0), "bad setup") + if tt.sellPrice > 0 { + so.SellPrice = uptr.To(s.coin(tt.sellPrice)) + } + + s.Require().GreaterOrEqual(tt.previousBid, int64(0), "bad setup") + if tt.previousBid > 0 { + so.HighestBid = &dymnstypes.SellOrderBid{ + Bidder: previousBidderA, + Price: s.coin(tt.previousBid), + } + + // mint coin to module account because we charged bidder before update SO + if !tt.skipPreMintModuleAccount { + err := s.bankKeeper.MintCoins(s.ctx, dymnstypes.ModuleName, sdk.NewCoins(so.HighestBid.Price)) + s.Require().NoError(err) + } + } + + if !tt.withoutSellOrder { + err := s.dymNsKeeper.SetSellOrder(s.ctx, so) + s.Require().NoError(err) + } + + // test + + s.Require().GreaterOrEqual(tt.newBid, int64(0), "mis-configured test case") + useBuyer := buyerA + if tt.customBuyer != "" { + useBuyer = tt.customBuyer + } + useDenom := params.BaseDenom + if tt.customBidDenom != "" { + useDenom = tt.customBidDenom + } + resp, errPurchaseName := dymnskeeper.NewMsgServerImpl(s.dymNsKeeper).PurchaseOrder(s.ctx, &dymnstypes.MsgPurchaseOrder{ + AssetId: dymName.Name, + AssetType: dymnstypes.TypeName, + Offer: sdk.NewInt64Coin(useDenom, tt.newBid), + Buyer: useBuyer, + }) + laterDymName := s.dymNsKeeper.GetDymName(s.ctx, dymName.Name) + if !tt.withoutDymName { + s.Require().NotNil(laterDymName) + s.Require().Equal(dymName.Name, laterDymName.Name, "name should not be changed") + s.Require().Equal(originalDymNameExpiry, laterDymName.ExpireAt, "expiry should not be changed") + } + + laterSo := s.dymNsKeeper.GetSellOrder(s.ctx, dymName.Name, dymnstypes.TypeName) + laterOwnerBalance := s.balance(ownerA) + laterBuyerBalance := s.balance(buyerA) + laterPreviousBidderBalance := s.balance(previousBidderA) + laterDymNamesOwnedByOwner, err := s.dymNsKeeper.GetDymNamesOwnedBy(s.ctx, ownerA) + s.Require().NoError(err) + laterDymNamesOwnedByBuyer, err := s.dymNsKeeper.GetDymNamesOwnedBy(s.ctx, buyerA) + s.Require().NoError(err) + laterDymNamesOwnedByPreviousBidder, err := s.dymNsKeeper.GetDymNamesOwnedBy(s.ctx, previousBidderA) + s.Require().NoError(err) + + s.Require().Equal(tt.wantOwnerBalanceLater, laterOwnerBalance, "owner balance mis-match") + s.Require().Equal(tt.wantBuyerBalanceLater, laterBuyerBalance, "buyer balance mis-match") + s.Require().Equal(tt.wantPreviousBidderBalanceLater, laterPreviousBidderBalance, "previous bidder balance mis-match") + + s.Require().Empty(laterDymNamesOwnedByPreviousBidder, "no reverse record should be made for previous bidder") + + if tt.wantErr { + s.Require().Error(errPurchaseName, "action should be failed") + s.Require().NotEmpty(tt.wantErrContains, "mis-configured test case") + s.Require().Contains(errPurchaseName.Error(), tt.wantErrContains) + s.Require().Nil(resp) + + s.Require().False(tt.wantOwnershipChanged, "mis-configured test case") + + s.Require().Less( + s.ctx.GasMeter().GasConsumed(), dymnstypes.OpGasPlaceBidOnSellOrder, + "should not consume params gas on failed operation", + ) + } else { + s.Require().NoError(errPurchaseName, "action should be successful") + s.Require().NotNil(resp) + + s.Require().GreaterOrEqual( + s.ctx.GasMeter().GasConsumed(), dymnstypes.OpGasPlaceBidOnSellOrder, + "should consume params gas", + ) + } + + if tt.wantOwnershipChanged { + s.Require().False(tt.withoutDymName, "mis-configured test case") + s.Require().False(tt.withoutSellOrder, "mis-configured test case") + + s.Require().Nil(laterSo, "SO should be deleted") + + s.Require().Equal(buyerA, laterDymName.Owner, "ownership should be changed") + s.Require().Equal(buyerA, laterDymName.Controller, "controller should be changed") + s.Require().Empty(laterDymName.Configs, "configs should be cleared") + s.Require().Empty(laterDymNamesOwnedByOwner, "reverse record should be removed") + s.Require().Len(laterDymNamesOwnedByBuyer, 1, "reverse record should be added") + } else { + if tt.withoutDymName { + s.Require().Nil(laterDymName) + s.Require().Empty(laterDymNamesOwnedByOwner) + s.Require().Empty(laterDymNamesOwnedByBuyer) + } else { + s.Require().Equal(ownerA, laterDymName.Owner, "ownership should not be changed") + s.Require().Equal(ownerA, laterDymName.Controller, "controller should not be changed") + s.Require().NotEmpty(laterDymName.Configs, "configs should be kept") + s.Require().Equal(dymName.Configs, laterDymName.Configs, "configs not be changed") + s.Require().Len(laterDymNamesOwnedByOwner, 1, "reverse record should be kept") + s.Require().Empty(laterDymNamesOwnedByBuyer, "reverse record should not be added") + } + + if tt.withoutSellOrder { + s.Require().Nil(laterSo) + } else { + s.Require().NotNil(laterSo, "SO should not be deleted") + } + } + }) + } + + s.Run("reject purchase order when trading is disabled", func() { + s.RefreshContext() + + s.updateModuleParams(func(moduleParams dymnstypes.Params) dymnstypes.Params { + moduleParams.Misc.EnableTradingName = false + return moduleParams + }) + + _, err := dymnskeeper.NewMsgServerImpl(s.dymNsKeeper).PurchaseOrder(s.ctx, &dymnstypes.MsgPurchaseOrder{ + AssetId: "my-name", + AssetType: dymnstypes.TypeName, + Offer: s.coin(100), + Buyer: buyerA, + }) + s.Require().ErrorContains(err, "unmet precondition") + }) +} + +//goland:noinspection GoSnakeCaseUsage +func (s *KeeperTestSuite) Test_msgServer_PurchaseOrder_Alias() { + s.Run("reject if message not pass validate basic", func() { + _, err := dymnskeeper.NewMsgServerImpl(s.dymNsKeeper).PurchaseOrder(s.ctx, &dymnstypes.MsgPurchaseOrder{ + AssetType: dymnstypes.TypeAlias, + }) + s.Require().ErrorContains(err, gerrc.ErrInvalidArgument.Error()) + }) + + creator_1_asOwner := testAddr(1).bech32() + creator_2_asBuyer := testAddr(2).bech32() + creator_3_asAnotherBuyer := testAddr(3).bech32() + + rollApp_1_byOwner_asSrc := *newRollApp("rollapp_1-1").WithAlias("alias").WithOwner(creator_1_asOwner) + rollApp_2_byBuyer_asDst := *newRollApp("rollapp_2-2").WithOwner(creator_2_asBuyer) + rollApp_3_byAnotherBuyer_asDst := *newRollApp("rollapp_3-2").WithOwner(creator_3_asAnotherBuyer) + rollApp_4_byOwner_asDst := *newRollApp("rollapp_4-2").WithOwner(creator_1_asOwner) + + const originalBalanceCreator1 int64 = 1000 + const originalBalanceCreator2 int64 = 500 + const originalBalanceCreator3 int64 = 400 + const minPrice int64 = 100 + + msg := func(buyer string, offer int64, assetId, dstRollAppId string) dymnstypes.MsgPurchaseOrder { + return dymnstypes.MsgPurchaseOrder{ + AssetId: assetId, + AssetType: dymnstypes.TypeAlias, + Params: []string{dstRollAppId}, + Offer: s.coin(offer), + Buyer: buyer, + } + } + + tests := []struct { + name string + rollApps []rollapp + sellOrder *dymnstypes.SellOrder + sourceRollAppId string + skipPreMintModuleAccount bool + overrideOriginalBalanceCreator2 int64 + msg dymnstypes.MsgPurchaseOrder + preRunFunc func(s *KeeperTestSuite) + wantCompleted bool + wantErr bool + wantErrContains string + wantLaterBalanceCreator1 int64 + wantLaterBalanceCreator2 int64 + wantLaterBalanceCreator3 int64 + }{ + { + name: "fail - source Alias/RollApp does not exists, SO does not exists", + rollApps: []rollapp{rollApp_2_byBuyer_asDst}, + sellOrder: nil, + msg: msg( + creator_2_asBuyer, 100, + "void", rollApp_2_byBuyer_asDst.rollAppId, + ), + wantCompleted: false, + wantErr: true, + wantErrContains: "alias not owned by any RollApp", + }, + { + name: "fail - destination RollApp does not exists, SO does not exists", + rollApps: nil, + sellOrder: nil, + msg: msg( + creator_2_asBuyer, 100, + "void", rollApp_2_byBuyer_asDst.rollAppId, + ), + wantCompleted: false, + wantErr: true, + wantErrContains: "destination Roll-App does not exists", + }, + { + name: "fail - source Alias/RollApp does not exists, SO exists", + rollApps: []rollapp{rollApp_2_byBuyer_asDst}, + sellOrder: s.newAliasSellOrder("void"). + WithMinPrice(minPrice). + BuildP(), + msg: msg( + creator_2_asBuyer, minPrice, + "void", rollApp_2_byBuyer_asDst.rollAppId, + ), + wantErr: true, + wantErrContains: "alias not owned by any RollApp", + }, + { + name: "fail - destination Alias/RollApp does not exists, SO exists", + rollApps: nil, + sellOrder: s.newAliasSellOrder("void"). + WithMinPrice(minPrice). + BuildP(), + msg: msg( + creator_2_asBuyer, minPrice, + "void", rollApp_2_byBuyer_asDst.rollAppId, + ), + wantErr: true, + wantErrContains: "destination Roll-App does not exists", + }, + { + name: "fail - Alias/RollApp exists, SO does not exists", + rollApps: []rollapp{rollApp_1_byOwner_asSrc, rollApp_2_byBuyer_asDst}, + sellOrder: nil, + msg: msg( + creator_2_asBuyer, 100, + rollApp_1_byOwner_asSrc.alias, rollApp_2_byBuyer_asDst.rollAppId, + ), + wantErr: true, + wantErrContains: fmt.Sprintf("Sell-Order: %s: not found", rollApp_1_byOwner_asSrc.alias), + }, + { + name: "pass - self-purchase is allowed", + rollApps: []rollapp{rollApp_1_byOwner_asSrc, rollApp_4_byOwner_asDst}, + sellOrder: s.newAliasSellOrder(rollApp_1_byOwner_asSrc.alias). + WithMinPrice(minPrice). + BuildP(), + msg: msg( + creator_1_asOwner, minPrice, + rollApp_1_byOwner_asSrc.alias, rollApp_4_byOwner_asDst.rollAppId, + ), + wantErr: false, + wantCompleted: false, + wantLaterBalanceCreator1: originalBalanceCreator1 - minPrice, + wantLaterBalanceCreator2: originalBalanceCreator2, + wantLaterBalanceCreator3: originalBalanceCreator3, + }, + { + name: "pass - self-purchase is allowed, complete order", + rollApps: []rollapp{rollApp_1_byOwner_asSrc, rollApp_4_byOwner_asDst}, + sellOrder: s.newAliasSellOrder(rollApp_1_byOwner_asSrc.alias). + WithMinPrice(minPrice). + WithSellPrice(200). + BuildP(), + sourceRollAppId: rollApp_1_byOwner_asSrc.rollAppId, + msg: msg( + creator_1_asOwner, 200, + rollApp_1_byOwner_asSrc.alias, rollApp_4_byOwner_asDst.rollAppId, + ), + wantErr: false, + wantCompleted: true, + wantLaterBalanceCreator1: originalBalanceCreator1, // unchanged + wantLaterBalanceCreator2: originalBalanceCreator2, + wantLaterBalanceCreator3: originalBalanceCreator3, + }, + { + name: "fail - invalid buyer address", + rollApps: []rollapp{rollApp_1_byOwner_asSrc, rollApp_2_byBuyer_asDst}, + sellOrder: s.newAliasSellOrder(rollApp_1_byOwner_asSrc.alias).WithMinPrice(minPrice).BuildP(), + msg: msg( + "invalidAddress", minPrice, + rollApp_1_byOwner_asSrc.alias, rollApp_2_byBuyer_asDst.rollAppId, + ), + wantErr: true, + wantErrContains: "buyer is not a valid bech32 account address", + }, + { + name: "fail - buyer is not the owner of the destination RollApp", + rollApps: []rollapp{rollApp_1_byOwner_asSrc, rollApp_3_byAnotherBuyer_asDst}, + sellOrder: s.newAliasSellOrder(rollApp_1_byOwner_asSrc.alias).WithMinPrice(minPrice). + Expired(). + BuildP(), + msg: msg( + creator_2_asBuyer, 200, + rollApp_1_byOwner_asSrc.alias, rollApp_3_byAnotherBuyer_asDst.rollAppId, + ), + wantErr: true, + wantErrContains: "not the owner of the RollApp", + }, + { + name: "fail - destination RollApp is the same as source", + rollApps: []rollapp{rollApp_1_byOwner_asSrc}, + sellOrder: s.newAliasSellOrder(rollApp_1_byOwner_asSrc.alias).WithMinPrice(minPrice). + Expired(). + BuildP(), + msg: msg( + creator_1_asOwner, 200, + rollApp_1_byOwner_asSrc.alias, rollApp_1_byOwner_asSrc.rollAppId, + ), + wantErr: true, + wantErrContains: "destination Roll-App ID is the same as the source", + }, + { + name: "fail - purchase an expired order, no bid", + rollApps: []rollapp{rollApp_1_byOwner_asSrc, rollApp_2_byBuyer_asDst}, + sellOrder: s.newAliasSellOrder(rollApp_1_byOwner_asSrc.alias).WithMinPrice(minPrice). + Expired(). + BuildP(), + msg: msg( + creator_2_asBuyer, 200, + rollApp_1_byOwner_asSrc.alias, rollApp_2_byBuyer_asDst.rollAppId, + ), + wantErr: true, + wantErrContains: "cannot purchase an expired order", + }, + { + name: "fail - purchase a completed order, expired, with bid, without sell price", + rollApps: []rollapp{rollApp_1_byOwner_asSrc, rollApp_2_byBuyer_asDst, rollApp_3_byAnotherBuyer_asDst}, + sellOrder: s.newAliasSellOrder(rollApp_1_byOwner_asSrc.alias). + WithMinPrice(minPrice). + Expired(). + WithAliasBid(creator_3_asAnotherBuyer, 150, rollApp_3_byAnotherBuyer_asDst.rollAppId). + BuildP(), + msg: msg( + creator_2_asBuyer, 200, + rollApp_1_byOwner_asSrc.alias, rollApp_2_byBuyer_asDst.rollAppId, + ), + wantErr: true, + wantErrContains: "cannot purchase an expired order", + }, + { + name: "fail - purchase a completed order, expired, with sell price, with bid under sell price", + rollApps: []rollapp{rollApp_1_byOwner_asSrc, rollApp_2_byBuyer_asDst, rollApp_3_byAnotherBuyer_asDst}, + sellOrder: s.newAliasSellOrder(rollApp_1_byOwner_asSrc.alias). + WithMinPrice(minPrice). + WithSellPrice(300). + Expired(). + WithAliasBid(creator_3_asAnotherBuyer, 150, rollApp_3_byAnotherBuyer_asDst.rollAppId). + BuildP(), + msg: msg( + creator_2_asBuyer, 200, + rollApp_1_byOwner_asSrc.alias, rollApp_2_byBuyer_asDst.rollAppId, + ), + wantErr: true, + wantErrContains: "cannot purchase an expired order", + }, + { + name: "fail - purchase a completed order, expired, with sell price, with bid = sell price", + rollApps: []rollapp{rollApp_1_byOwner_asSrc, rollApp_2_byBuyer_asDst, rollApp_3_byAnotherBuyer_asDst}, + sellOrder: s.newAliasSellOrder(rollApp_1_byOwner_asSrc.alias). + WithMinPrice(minPrice). + WithSellPrice(300). + Expired(). + WithAliasBid(creator_3_asAnotherBuyer, 300 /*equals sell price*/, rollApp_3_byAnotherBuyer_asDst.rollAppId). + BuildP(), + msg: msg( + creator_2_asBuyer, 300, /*equals sell price*/ + rollApp_1_byOwner_asSrc.alias, rollApp_2_byBuyer_asDst.rollAppId, + ), + wantErr: true, + wantErrContains: "cannot purchase an expired order", + }, + { + name: "fail - purchase a completed order, not expired, fail because previous bid matches sell price", + rollApps: []rollapp{rollApp_1_byOwner_asSrc, rollApp_2_byBuyer_asDst, rollApp_3_byAnotherBuyer_asDst}, + sellOrder: s.newAliasSellOrder(rollApp_1_byOwner_asSrc.alias). + WithMinPrice(minPrice). + WithSellPrice(300). + WithAliasBid(creator_3_asAnotherBuyer, 300 /*equals sell price*/, rollApp_3_byAnotherBuyer_asDst.rollAppId). + BuildP(), + msg: msg( + creator_2_asBuyer, 300, /*equals sell price*/ + rollApp_1_byOwner_asSrc.alias, rollApp_2_byBuyer_asDst.rollAppId, + ), + wantErr: true, + wantErrContains: "cannot purchase a completed order", + }, + { + name: "fail - purchase order, not expired, fail because lower than previous bid, with sell-price", + rollApps: []rollapp{rollApp_1_byOwner_asSrc, rollApp_2_byBuyer_asDst, rollApp_3_byAnotherBuyer_asDst}, + sellOrder: s.newAliasSellOrder(rollApp_1_byOwner_asSrc.alias). + WithMinPrice(minPrice). + WithSellPrice(300). + WithAliasBid(creator_3_asAnotherBuyer, 250, rollApp_3_byAnotherBuyer_asDst.rollAppId). + BuildP(), + msg: msg( + creator_2_asBuyer, 200, + rollApp_1_byOwner_asSrc.alias, rollApp_2_byBuyer_asDst.rollAppId, + ), + wantErr: true, + wantErrContains: "new offer must be higher than current highest bid", + }, + { + name: "fail - purchase order, not expired, fail because lower than previous bid, without sell-price", + rollApps: []rollapp{rollApp_1_byOwner_asSrc, rollApp_2_byBuyer_asDst, rollApp_3_byAnotherBuyer_asDst}, + sellOrder: s.newAliasSellOrder(rollApp_1_byOwner_asSrc.alias). + WithMinPrice(minPrice). + // without sell-price + WithAliasBid(creator_3_asAnotherBuyer, 200, rollApp_3_byAnotherBuyer_asDst.rollAppId). + BuildP(), + msg: msg( + creator_2_asBuyer, 150, + rollApp_1_byOwner_asSrc.alias, rollApp_2_byBuyer_asDst.rollAppId, + ), + wantErr: true, + wantErrContains: "new offer must be higher than current highest bid", + }, + { + name: "fail - purchase order, expired, fail because lower than previous bid, with sell-price", + rollApps: []rollapp{rollApp_1_byOwner_asSrc, rollApp_2_byBuyer_asDst, rollApp_3_byAnotherBuyer_asDst}, + sellOrder: s.newAliasSellOrder(rollApp_1_byOwner_asSrc.alias). + WithMinPrice(minPrice). + WithSellPrice(300). + Expired(). + WithAliasBid(creator_3_asAnotherBuyer, 300, rollApp_3_byAnotherBuyer_asDst.rollAppId). + BuildP(), + msg: msg( + creator_2_asBuyer, 200, + rollApp_1_byOwner_asSrc.alias, rollApp_2_byBuyer_asDst.rollAppId, + ), + wantErr: true, + wantErrContains: "cannot purchase an expired order", + }, + { + name: "fail - purchase order, expired, fail because lower than previous bid, without sell-price", + rollApps: []rollapp{rollApp_1_byOwner_asSrc, rollApp_2_byBuyer_asDst, rollApp_3_byAnotherBuyer_asDst}, + sellOrder: s.newAliasSellOrder(rollApp_1_byOwner_asSrc.alias). + WithMinPrice(minPrice). + // without sell-price + Expired(). + WithAliasBid(creator_3_asAnotherBuyer, 200, rollApp_3_byAnotherBuyer_asDst.rollAppId). + BuildP(), + msg: msg( + creator_2_asBuyer, 150, + rollApp_1_byOwner_asSrc.alias, rollApp_2_byBuyer_asDst.rollAppId, + ), + wantErr: true, + wantErrContains: "cannot purchase an expired order", + }, + { + name: "fail - purchase order, not expired, fail because equals to previous bid", + rollApps: []rollapp{rollApp_1_byOwner_asSrc, rollApp_2_byBuyer_asDst, rollApp_3_byAnotherBuyer_asDst}, + sellOrder: s.newAliasSellOrder(rollApp_1_byOwner_asSrc.alias). + WithMinPrice(minPrice). + WithSellPrice(300). + WithAliasBid(creator_3_asAnotherBuyer, 200, rollApp_3_byAnotherBuyer_asDst.rollAppId). + BuildP(), + msg: msg( + creator_2_asBuyer, 200, + rollApp_1_byOwner_asSrc.alias, rollApp_2_byBuyer_asDst.rollAppId, + ), + wantErr: true, + wantErrContains: "new offer must be higher than current highest bid", + }, + { + name: "fail - purchase a completed order, expired, bid equals to previous bid", + rollApps: []rollapp{rollApp_1_byOwner_asSrc, rollApp_2_byBuyer_asDst, rollApp_3_byAnotherBuyer_asDst}, + sellOrder: s.newAliasSellOrder(rollApp_1_byOwner_asSrc.alias). + WithMinPrice(minPrice). + WithSellPrice(300). + Expired(). + WithAliasBid(creator_3_asAnotherBuyer, 200, rollApp_3_byAnotherBuyer_asDst.rollAppId). + BuildP(), + msg: msg( + creator_2_asBuyer, 200, + rollApp_1_byOwner_asSrc.alias, rollApp_2_byBuyer_asDst.rollAppId, + ), + wantErr: true, + wantErrContains: "cannot purchase an expired order", + }, + { + name: "fail - mis-match denom", + rollApps: []rollapp{rollApp_1_byOwner_asSrc, rollApp_2_byBuyer_asDst}, + sellOrder: s.newAliasSellOrder(rollApp_1_byOwner_asSrc.alias). + WithMinPrice(minPrice).WithSellPrice(300). + BuildP(), + msg: func() dymnstypes.MsgPurchaseOrder { + msg := msg( + creator_2_asBuyer, 200, + rollApp_1_byOwner_asSrc.alias, rollApp_2_byBuyer_asDst.rollAppId, + ) + msg.Offer = sdk.Coin{ + Denom: "u" + params.BaseDenom, + Amount: msg.Offer.Amount, + } + return msg + }(), + wantErr: true, + wantErrContains: "offer denom does not match the order denom", + }, + { + name: "fail - offer lower than min-price", + rollApps: []rollapp{rollApp_1_byOwner_asSrc, rollApp_2_byBuyer_asDst}, + sellOrder: s.newAliasSellOrder(rollApp_1_byOwner_asSrc.alias). + WithMinPrice(minPrice). + BuildP(), + msg: msg( + creator_2_asBuyer, 1, // very low + rollApp_1_byOwner_asSrc.alias, rollApp_2_byBuyer_asDst.rollAppId, + ), + wantErr: true, + wantErrContains: "offer is lower than minimum price", + }, + { + name: "fail - zero bid amount", + rollApps: []rollapp{rollApp_1_byOwner_asSrc, rollApp_2_byBuyer_asDst}, + sellOrder: s.newAliasSellOrder(rollApp_1_byOwner_asSrc.alias). + WithMinPrice(minPrice). + BuildP(), + msg: msg( + creator_2_asBuyer, 0, + rollApp_1_byOwner_asSrc.alias, rollApp_2_byBuyer_asDst.rollAppId, + ), + wantErr: true, + wantErrContains: "offer must be positive", + }, + { + name: "fail - offer higher than sell-price", + rollApps: []rollapp{rollApp_1_byOwner_asSrc, rollApp_2_byBuyer_asDst}, + sellOrder: s.newAliasSellOrder(rollApp_1_byOwner_asSrc.alias). + WithMinPrice(minPrice). + WithSellPrice(300). + BuildP(), + msg: msg( + creator_2_asBuyer, 300+1, + rollApp_1_byOwner_asSrc.alias, rollApp_2_byBuyer_asDst.rollAppId, + ), + wantErr: true, + wantErrContains: "offer is higher than sell price", + }, + { + name: "pass - place bid, = min price, no previous bid, no sell price", + rollApps: []rollapp{rollApp_1_byOwner_asSrc, rollApp_2_byBuyer_asDst}, + sellOrder: s.newAliasSellOrder(rollApp_1_byOwner_asSrc.alias). + WithMinPrice(minPrice). + BuildP(), + sourceRollAppId: rollApp_1_byOwner_asSrc.rollAppId, + msg: msg( + creator_2_asBuyer, minPrice, + rollApp_1_byOwner_asSrc.alias, rollApp_2_byBuyer_asDst.rollAppId, + ), + wantErr: false, + wantCompleted: false, + wantLaterBalanceCreator1: originalBalanceCreator1, + wantLaterBalanceCreator2: originalBalanceCreator2 - minPrice, + wantLaterBalanceCreator3: originalBalanceCreator3, + }, + { + name: "fail - can not purchase if alias is presents in params", + rollApps: []rollapp{rollApp_1_byOwner_asSrc, rollApp_2_byBuyer_asDst}, + sellOrder: s.newAliasSellOrder(rollApp_1_byOwner_asSrc.alias). + WithMinPrice(minPrice). + BuildP(), + sourceRollAppId: rollApp_1_byOwner_asSrc.rollAppId, + msg: msg( + creator_2_asBuyer, minPrice, + rollApp_1_byOwner_asSrc.alias, rollApp_2_byBuyer_asDst.rollAppId, + ), + preRunFunc: func(s *KeeperTestSuite) { + s.updateModuleParams(func(p dymnstypes.Params) dymnstypes.Params { + p.Chains.AliasesOfChainIds = []dymnstypes.AliasesOfChainId{ + { + ChainId: "some-chain", + Aliases: []string{rollApp_1_byOwner_asSrc.alias}, + }, + } + return p + }) + }, + wantErr: true, + wantErrContains: "prohibited to trade aliases which is reserved for chain-id or alias in module params", + wantCompleted: false, + }, + { + name: "pass - place bid, greater than previous bid, no sell price", + rollApps: []rollapp{rollApp_1_byOwner_asSrc, rollApp_2_byBuyer_asDst}, + sellOrder: s.newAliasSellOrder(rollApp_1_byOwner_asSrc.alias). + WithMinPrice(minPrice). + WithAliasBid(creator_3_asAnotherBuyer, 200, rollApp_3_byAnotherBuyer_asDst.rollAppId). + BuildP(), + sourceRollAppId: rollApp_1_byOwner_asSrc.rollAppId, + msg: msg( + creator_2_asBuyer, 250, + rollApp_1_byOwner_asSrc.alias, rollApp_2_byBuyer_asDst.rollAppId, + ), + wantErr: false, + wantCompleted: false, + wantLaterBalanceCreator1: originalBalanceCreator1, + wantLaterBalanceCreator2: originalBalanceCreator2 - 250, + wantLaterBalanceCreator3: originalBalanceCreator3 + 200, // refund + }, + { + name: "fail - failed to refund previous bid", + rollApps: []rollapp{rollApp_1_byOwner_asSrc, rollApp_2_byBuyer_asDst}, + sellOrder: s.newAliasSellOrder(rollApp_1_byOwner_asSrc.alias). + WithMinPrice(minPrice). + WithAliasBid(creator_3_asAnotherBuyer, 200, rollApp_3_byAnotherBuyer_asDst.rollAppId). + BuildP(), + msg: msg( + creator_2_asBuyer, 250, + rollApp_1_byOwner_asSrc.alias, rollApp_2_byBuyer_asDst.rollAppId, + ), + skipPreMintModuleAccount: true, + wantErr: true, + wantErrContains: "insufficient funds", + }, + { + name: "fail - insufficient buyer funds", + rollApps: []rollapp{rollApp_1_byOwner_asSrc, rollApp_2_byBuyer_asDst}, + sellOrder: s.newAliasSellOrder(rollApp_1_byOwner_asSrc.alias). + WithMinPrice(minPrice). + WithAliasBid(creator_3_asAnotherBuyer, 200, rollApp_3_byAnotherBuyer_asDst.rollAppId). + BuildP(), + msg: msg( + creator_2_asBuyer, 250, + rollApp_1_byOwner_asSrc.alias, rollApp_2_byBuyer_asDst.rollAppId, + ), + overrideOriginalBalanceCreator2: 1, + wantErr: true, + wantErrContains: "insufficient funds", + }, + { + name: "pass - place bid, greater than previous bid, under sell price", + rollApps: []rollapp{rollApp_1_byOwner_asSrc, rollApp_2_byBuyer_asDst}, + sellOrder: s.newAliasSellOrder(rollApp_1_byOwner_asSrc.alias). + WithMinPrice(minPrice). + WithSellPrice(300). + WithAliasBid(creator_3_asAnotherBuyer, minPrice, rollApp_3_byAnotherBuyer_asDst.rollAppId). + BuildP(), + msg: msg( + creator_2_asBuyer, 250, + rollApp_1_byOwner_asSrc.alias, rollApp_2_byBuyer_asDst.rollAppId, + ), + wantErr: false, + wantCompleted: false, + wantLaterBalanceCreator1: originalBalanceCreator1, + wantLaterBalanceCreator2: originalBalanceCreator2 - 250, // charge bid + wantLaterBalanceCreator3: originalBalanceCreator3 + minPrice, // refund + }, + { + name: "pass - place bid, greater than previous bid, equals sell price, transfer ownership", + rollApps: []rollapp{rollApp_1_byOwner_asSrc, rollApp_2_byBuyer_asDst}, + sellOrder: s.newAliasSellOrder(rollApp_1_byOwner_asSrc.alias). + WithMinPrice(minPrice). + WithSellPrice(300). + WithAliasBid(creator_3_asAnotherBuyer, minPrice, rollApp_3_byAnotherBuyer_asDst.rollAppId). + BuildP(), + sourceRollAppId: rollApp_1_byOwner_asSrc.rollAppId, + msg: msg( + creator_2_asBuyer, 300, + rollApp_1_byOwner_asSrc.alias, rollApp_2_byBuyer_asDst.rollAppId, + ), + wantErr: false, + wantCompleted: true, + wantLaterBalanceCreator1: originalBalanceCreator1 + 300, // transfer sale + wantLaterBalanceCreator2: originalBalanceCreator2 - 300, // charge bid + wantLaterBalanceCreator3: originalBalanceCreator3 + minPrice, // refund + }, + { + name: "pass - if any bid before, later bid higher, refund previous bidder", + rollApps: []rollapp{rollApp_1_byOwner_asSrc, rollApp_2_byBuyer_asDst}, + sellOrder: s.newAliasSellOrder(rollApp_1_byOwner_asSrc.alias). + WithMinPrice(minPrice). + WithSellPrice(300). + WithAliasBid(creator_3_asAnotherBuyer, minPrice, rollApp_3_byAnotherBuyer_asDst.rollAppId). + BuildP(), + msg: msg( + creator_2_asBuyer, 200, + rollApp_1_byOwner_asSrc.alias, rollApp_2_byBuyer_asDst.rollAppId, + ), + wantErr: false, + wantCompleted: false, + wantLaterBalanceCreator1: originalBalanceCreator1, + wantLaterBalanceCreator2: originalBalanceCreator2 - 200, // charge bid + wantLaterBalanceCreator3: originalBalanceCreator3 + minPrice, // refund + }, + } + for _, tt := range tests { + s.Run(tt.name, func() { + s.RefreshContext() + + useOriginalBalanceCreator1 := originalBalanceCreator1 + useOriginalBalanceCreator2 := originalBalanceCreator2 + if tt.overrideOriginalBalanceCreator2 > 0 { + useOriginalBalanceCreator2 = tt.overrideOriginalBalanceCreator2 + } + useOriginalBalanceCreator3 := originalBalanceCreator3 + + s.mintToAccount(creator_1_asOwner, useOriginalBalanceCreator1) + s.mintToAccount(creator_2_asBuyer, useOriginalBalanceCreator2) + s.mintToAccount(creator_3_asAnotherBuyer, useOriginalBalanceCreator3) + + for _, rollApp := range tt.rollApps { + s.persistRollApp(rollApp) + } + + if tt.sellOrder != nil { + s.Require().Equal(tt.sellOrder.AssetId, tt.msg.AssetId, "bad setup") + + err := s.dymNsKeeper.SetSellOrder(s.ctx, *tt.sellOrder) + s.Require().NoError(err) + + if tt.sellOrder.HighestBid != nil { + if !tt.skipPreMintModuleAccount { + s.mintToModuleAccount(tt.sellOrder.HighestBid.Price.Amount.Int64()) + } + } + } + + if tt.preRunFunc != nil { + tt.preRunFunc(s) + } + + // test + + resp, errPurchaseName := dymnskeeper.NewMsgServerImpl(s.dymNsKeeper).PurchaseOrder(s.ctx, &tt.msg) + + for _, ra := range tt.rollApps { + s.True(s.dymNsKeeper.IsRollAppId(s.ctx, ra.rollAppId)) + } + + laterSo := s.dymNsKeeper.GetSellOrder(s.ctx, tt.msg.AssetId, dymnstypes.TypeAlias) + + if tt.wantErr { + s.Require().ErrorContains(errPurchaseName, tt.wantErrContains) + s.Nil(resp) + s.Require().False(tt.wantCompleted, "mis-configured test case") + s.Less( + s.ctx.GasMeter().GasConsumed(), dymnstypes.OpGasPlaceBidOnSellOrder, + "should not consume params gas on failed operation", + ) + + s.Zero(tt.wantLaterBalanceCreator1, "bad setup, won't check balance on error") + s.Zero(tt.wantLaterBalanceCreator2, "bad setup, won't check balance on error") + s.Zero(tt.wantLaterBalanceCreator3, "bad setup, won't check balance on error") + } else { + s.NotNil(resp) + s.GreaterOrEqual( + s.ctx.GasMeter().GasConsumed(), dymnstypes.OpGasPlaceBidOnSellOrder, + "should consume params gas", + ) + + s.Equal(tt.wantLaterBalanceCreator1, s.balance(creator_1_asOwner), "owner balance mis-match") + s.Equal(tt.wantLaterBalanceCreator2, s.balance(creator_2_asBuyer), "buyer balance mis-match") + s.Equal(tt.wantLaterBalanceCreator3, s.balance(creator_3_asAnotherBuyer), "previous bidder balance mis-match") + } + + destinationRollAppId := tt.msg.Params[0] + if tt.wantCompleted { + s.Require().NotEmpty(tt.sourceRollAppId, "mis-configured test case") + + s.Require().NotEmpty(tt.rollApps, "mis-configured test case") + s.Require().NotNil(tt.sellOrder, "mis-configured test case") + + s.Nil(laterSo, "SO should be deleted") + + s.requireRollApp(tt.sourceRollAppId).HasNoAlias() + s.requireRollApp(destinationRollAppId).HasAlias(tt.msg.AssetId) + } else { + if len(tt.rollApps) > 0 { + for _, ra := range tt.rollApps { + if ra.alias != "" { + s.requireRollApp(ra.rollAppId).HasAlias(ra.alias) + } else { + s.requireRollApp(ra.rollAppId).HasNoAlias() + } + } + } + + if tt.sellOrder != nil { + s.NotNil(laterSo, "SO should not be deleted") + } else { + s.Nil(laterSo, "SO should not exists") + } + } + }) + } + + s.Run("reject purchase order when trading is disabled", func() { + s.RefreshContext() + + moduleParams := s.dymNsKeeper.GetParams(s.ctx) + moduleParams.Misc.EnableTradingAlias = false + err := s.dymNsKeeper.SetParams(s.ctx, moduleParams) + s.Require().NoError(err) + + _, err = dymnskeeper.NewMsgServerImpl(s.dymNsKeeper).PurchaseOrder(s.ctx, &dymnstypes.MsgPurchaseOrder{ + AssetId: "alias", + AssetType: dymnstypes.TypeAlias, + Params: []string{rollApp_2_byBuyer_asDst.rollAppId}, + Offer: s.coin(100), + Buyer: creator_2_asBuyer, + }) + s.Require().ErrorContains(err, "trading of Alias is disabled") + }) +} diff --git a/x/dymns/keeper/msg_server_register_alias.go b/x/dymns/keeper/msg_server_register_alias.go new file mode 100644 index 000000000..4272b3041 --- /dev/null +++ b/x/dymns/keeper/msg_server_register_alias.go @@ -0,0 +1,116 @@ +package keeper + +import ( + "context" + "errors" + + errorsmod "cosmossdk.io/errors" + + "github.com/dymensionxyz/gerr-cosmos/gerrc" + + sdk "github.com/cosmos/cosmos-sdk/types" + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" +) + +// RegisterAlias is message handler, handles registration of a new Alias for an existing RollApp. +func (k msgServer) RegisterAlias(goCtx context.Context, msg *dymnstypes.MsgRegisterAlias) (*dymnstypes.MsgRegisterAliasResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + err := k.validateRegisterAlias(ctx, msg) + if err != nil { + return nil, err + } + + priceParams := k.PriceParams(ctx) + + registrationCost := sdk.NewCoin( + priceParams.PriceDenom, priceParams.GetAliasPrice(msg.Alias), + ) + + if !registrationCost.Equal(msg.ConfirmPayment) { + return nil, errorsmod.Wrapf( + gerrc.ErrInvalidArgument, + "actual payment is is different with provided by user: %s != %s", registrationCost.String(), msg.ConfirmPayment, + ) + } + + // At this place we don't do compare actual payment with estimated payment calculated by EstimateRegisterAlias + // because in-case there is different between them, it would prevent RollApp owners to registration. + + if err := k.registerAliasForRollApp( + ctx, + msg.RollappId, sdk.MustAccAddressFromBech32(msg.Owner), + msg.Alias, + sdk.NewCoins(registrationCost), + ); err != nil { + return nil, errorsmod.Wrap( + errors.Join(gerrc.ErrInternal, err), "failed to register alias for RollApp", + ) + } + + return &dymnstypes.MsgRegisterAliasResponse{}, nil +} + +// validateRegisterAlias handles validation for the message handled by RegisterAlias. +func (k msgServer) validateRegisterAlias(ctx sdk.Context, msg *dymnstypes.MsgRegisterAlias) error { + if err := msg.ValidateBasic(); err != nil { + return err + } + + rollApp, found := k.rollappKeeper.GetRollapp(ctx, msg.RollappId) + if !found { + return errorsmod.Wrapf(gerrc.ErrNotFound, "RollApp: %s", msg.RollappId) + } + + if rollApp.Owner != msg.Owner { + return errorsmod.Wrapf(gerrc.ErrPermissionDenied, "not the owner of the RollApp") + } + + if !k.CanUseAliasForNewRegistration(ctx, msg.Alias) { + return errorsmod.Wrapf(gerrc.ErrAlreadyExists, "alias already in use or preserved: %s", msg.Alias) + } + + return nil +} + +func (k Keeper) registerAliasForRollApp( + ctx sdk.Context, + rollAppId string, owner sdk.AccAddress, + alias string, + registrationFee sdk.Coins, +) error { + if err := k.bankKeeper.SendCoinsFromAccountToModule(ctx, + owner, + dymnstypes.ModuleName, + registrationFee, + ); err != nil { + return err + } + + if err := k.bankKeeper.BurnCoins(ctx, dymnstypes.ModuleName, registrationFee); err != nil { + return err + } + + if err := k.SetAliasForRollAppId(ctx, rollAppId, alias); err != nil { + return errorsmod.Wrap(gerrc.ErrInternal, "failed to set alias for RollApp") + } + + ctx.EventManager().EmitEvent(sdk.NewEvent( + dymnstypes.EventTypeSell, + sdk.NewAttribute(dymnstypes.AttributeKeySellAssetType, dymnstypes.TypeAlias.PrettyName()), + sdk.NewAttribute(dymnstypes.AttributeKeySellName, alias), + sdk.NewAttribute(dymnstypes.AttributeKeySellPrice, registrationFee.String()), + sdk.NewAttribute(dymnstypes.AttributeKeySellTo, rollAppId), + )) + + return nil +} + +// EstimateRegisterAlias is a function to estimate the cost of registering an alias. +func EstimateRegisterAlias( + alias string, priceParams dymnstypes.PriceParams, +) dymnstypes.EstimateRegisterAliasResponse { + return dymnstypes.EstimateRegisterAliasResponse{ + Price: sdk.NewCoin(priceParams.PriceDenom, priceParams.GetAliasPrice(alias)), + } +} diff --git a/x/dymns/keeper/msg_server_register_alias_test.go b/x/dymns/keeper/msg_server_register_alias_test.go new file mode 100644 index 000000000..dfc5225ed --- /dev/null +++ b/x/dymns/keeper/msg_server_register_alias_test.go @@ -0,0 +1,426 @@ +package keeper_test + +import ( + "fmt" + + sdkmath "cosmossdk.io/math" + + sdk "github.com/cosmos/cosmos-sdk/types" + dymnskeeper "github.com/dymensionxyz/dymension/v3/x/dymns/keeper" + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" + dymnsutils "github.com/dymensionxyz/dymension/v3/x/dymns/utils" + "github.com/dymensionxyz/gerr-cosmos/gerrc" +) + +func (s *KeeperTestSuite) Test_msgServer_RegisterAlias() { + denom := s.coin(0).Denom + const price1L = 6 + const price2L = 5 + const price3L = 4 + const price4L = 3 + const price5PlusL = 2 + + // the number values used in this test will be multiplied by this value + priceMultiplier := sdk.NewInt(1e18) + + rollApp1 := *newRollApp("rollapp_1-1").WithOwner(testAddr(1).bech32()).WithBech32("one").WithAlias("one") + rollApp2 := *newRollApp("rollapp_2-2").WithOwner(testAddr(2).bech32()).WithBech32("two").WithAlias("two") + rollApp3WithoutAlias := *newRollApp("rollapp_3-3").WithBech32("three").WithOwner(testAddr(3).bech32()) + + buyerNotOwnedAnyRollApp := testAddr(4).bech32() + + s.persistRollApp(rollApp1) + s.persistRollApp(rollApp2) + s.persistRollApp(rollApp3WithoutAlias) + + const reservedAliasInParams = "reserved" + + const freeAlias8L = "accepted" + + const originalModuleBalance int64 = 88 + + s.updateModuleParams(func(moduleParams dymnstypes.Params) dymnstypes.Params { + moduleParams.Price.AliasPriceSteps = []sdkmath.Int{ + sdk.NewInt(price1L).Mul(priceMultiplier), + sdk.NewInt(price2L).Mul(priceMultiplier), + sdk.NewInt(price3L).Mul(priceMultiplier), + sdk.NewInt(price4L).Mul(priceMultiplier), + sdk.NewInt(price5PlusL).Mul(priceMultiplier), + } + moduleParams.Price.PriceDenom = denom + // reserved + moduleParams.Chains.AliasesOfChainIds = []dymnstypes.AliasesOfChainId{ + { + ChainId: "famous_9999-9", + Aliases: []string{reservedAliasInParams}, + }, + } + + return moduleParams + }) + + s.mintToModuleAccount(originalModuleBalance) + + dymNameOwnerAcc := testAddr(5) + dymName := dymnstypes.DymName{ + Name: "my-name", + Owner: dymNameOwnerAcc.bech32(), + Controller: dymNameOwnerAcc.bech32(), + ExpireAt: s.now.Unix() + 100, + } + s.setDymNameWithFunctionsAfter(dymName) + + s.SaveCurrentContext() + + s.Run("reject if message not pass validate basic", func() { + _, err := dymnskeeper.NewMsgServerImpl(s.dymNsKeeper).RegisterAlias(s.ctx, &dymnstypes.MsgRegisterAlias{}) + s.Require().ErrorContains(err, gerrc.ErrInvalidArgument.Error()) + }) + + tests := []struct { + name string + msg dymnstypes.MsgRegisterAlias + buyerBalance int64 + preRunSetup func(s *KeeperTestSuite) + wantErr bool + wantErrContains string + wantLaterAliasLinkedTo string + wantLaterBuyerBalance int64 + wantLaterAliasesOwnedByRollApp []string + }{ + { + name: "pass - can register", + msg: dymnstypes.MsgRegisterAlias{ + Alias: freeAlias8L, + RollappId: rollApp1.rollAppId, + Owner: rollApp1.owner, + ConfirmPayment: sdk.NewCoin(denom, sdk.NewInt(price5PlusL)), + }, + buyerBalance: price5PlusL + 1, + wantErr: false, + wantLaterAliasLinkedTo: rollApp1.rollAppId, + wantLaterBuyerBalance: 1, + wantLaterAliasesOwnedByRollApp: append(rollApp1.aliases, freeAlias8L), + }, + { + name: "pass - can register, 2L", + msg: dymnstypes.MsgRegisterAlias{ + Alias: "oh", + RollappId: rollApp1.rollAppId, + Owner: rollApp1.owner, + ConfirmPayment: sdk.NewCoin(denom, sdk.NewInt(price2L)), + }, + buyerBalance: price2L + 1, + wantErr: false, + wantLaterAliasLinkedTo: rollApp1.rollAppId, + wantLaterBuyerBalance: 1, + wantLaterAliasesOwnedByRollApp: append(rollApp1.aliases, "oh"), + }, + { + name: "pass - can register to RollApp that not owned any alias", + msg: dymnstypes.MsgRegisterAlias{ + Alias: "oh", + RollappId: rollApp3WithoutAlias.rollAppId, + Owner: rollApp3WithoutAlias.owner, + ConfirmPayment: sdk.NewCoin(denom, sdk.NewInt(price2L)), + }, + buyerBalance: price2L + 1, + wantErr: false, + wantLaterAliasLinkedTo: rollApp3WithoutAlias.rollAppId, + wantLaterBuyerBalance: 1, + wantLaterAliasesOwnedByRollApp: []string{"oh"}, + }, + { + name: "pass - deduct correct amount of balance", + msg: dymnstypes.MsgRegisterAlias{ + Alias: "oh", + RollappId: rollApp3WithoutAlias.rollAppId, + Owner: rollApp3WithoutAlias.owner, + ConfirmPayment: sdk.NewCoin(denom, sdk.NewInt(price2L)), + }, + buyerBalance: price1L, + wantErr: false, + wantLaterAliasLinkedTo: rollApp3WithoutAlias.rollAppId, + wantLaterBuyerBalance: price1L - price2L, + wantLaterAliasesOwnedByRollApp: []string{"oh"}, + }, + { + name: "fail - reject if buyer does not have enough balance to register", + msg: dymnstypes.MsgRegisterAlias{ + Alias: freeAlias8L, + RollappId: rollApp1.rollAppId, + Owner: rollApp1.owner, + ConfirmPayment: sdk.NewCoin(denom, sdk.NewInt(price5PlusL)), + }, + buyerBalance: price5PlusL - 1, + wantErr: true, + wantErrContains: "insufficient funds", + wantLaterAliasLinkedTo: "", + wantLaterBuyerBalance: price5PlusL - 1, + wantLaterAliasesOwnedByRollApp: rollApp1.aliases, + }, + { + name: "fail - reject bad request", + msg: dymnstypes.MsgRegisterAlias{}, + wantErr: true, + wantErrContains: gerrc.ErrInvalidArgument.Error(), + }, + { + name: "fail - reject alias occupied by another", + msg: dymnstypes.MsgRegisterAlias{ + Alias: rollApp2.alias, + RollappId: rollApp1.rollAppId, + Owner: rollApp1.owner, + ConfirmPayment: sdk.NewCoin(denom, s.moduleParams().Price.GetAliasPrice(rollApp2.alias)), + }, + buyerBalance: price1L, + wantErr: true, + wantErrContains: "alias already in use or preserved", + wantLaterAliasLinkedTo: rollApp2.rollAppId, + wantLaterBuyerBalance: price1L, + wantLaterAliasesOwnedByRollApp: rollApp1.aliases, + }, + { + name: "fail - reject alias reserved in params", + msg: dymnstypes.MsgRegisterAlias{ + Alias: reservedAliasInParams, + RollappId: rollApp1.rollAppId, + Owner: rollApp1.owner, + ConfirmPayment: sdk.NewCoin(denom, s.moduleParams().Price.GetAliasPrice(reservedAliasInParams)), + }, + buyerBalance: price1L, + wantErr: true, + wantErrContains: "alias already in use or preserved", + wantLaterAliasLinkedTo: "", + wantLaterBuyerBalance: price1L, + wantLaterAliasesOwnedByRollApp: rollApp1.aliases, + }, + { + name: "fail - reject if RollApp not found", + msg: dymnstypes.MsgRegisterAlias{ + Alias: freeAlias8L, + RollappId: "nah_0-0", + Owner: buyerNotOwnedAnyRollApp, + ConfirmPayment: sdk.NewCoin(denom, sdk.NewInt(price5PlusL)), + }, + buyerBalance: price1L, + wantErr: true, + wantErrContains: "not found", + wantLaterAliasLinkedTo: "", + wantLaterBuyerBalance: price1L, + wantLaterAliasesOwnedByRollApp: []string{}, + }, + { + name: "fail - don't charge if tx failed", + msg: dymnstypes.MsgRegisterAlias{ + Alias: freeAlias8L, + RollappId: "nah_0-0", + Owner: buyerNotOwnedAnyRollApp, + ConfirmPayment: sdk.NewCoin(denom, sdk.NewInt(price5PlusL)), + }, + buyerBalance: price1L, + wantErr: true, + wantErrContains: "not found", + wantLaterBuyerBalance: price1L, + }, + { + name: "fail - reject if not owner of the RollApp", + msg: dymnstypes.MsgRegisterAlias{ + Alias: freeAlias8L, + RollappId: rollApp1.rollAppId, + Owner: buyerNotOwnedAnyRollApp, + ConfirmPayment: sdk.NewCoin(denom, sdk.NewInt(price5PlusL)), + }, + buyerBalance: price1L, + wantErr: true, + wantErrContains: "not the owner of the RollApp", + wantLaterAliasLinkedTo: "", + wantLaterBuyerBalance: price1L, + wantLaterAliasesOwnedByRollApp: rollApp1.aliases, + }, + { + name: "fail - reject if confirm payment does not match the actual amount", + msg: dymnstypes.MsgRegisterAlias{ + Alias: freeAlias8L, + RollappId: rollApp1.rollAppId, + Owner: rollApp1.owner, + ConfirmPayment: sdk.NewCoin(denom, sdk.NewInt(price1L)), + }, + buyerBalance: price1L, + wantErr: true, + wantErrContains: "actual payment is is different with provided by user", + wantLaterAliasLinkedTo: "", + wantLaterBuyerBalance: price1L, + wantLaterAliasesOwnedByRollApp: rollApp1.aliases, + }, + } + for _, tt := range tests { + s.Run(tt.name, func() { + s.RefreshContext() + + if tt.buyerBalance > 0 { + s.Require().NotEmpty(tt.msg.Owner) + s.mintToAccount2(tt.msg.Owner, sdk.NewInt(tt.buyerBalance).Mul(priceMultiplier)) + } + + if tt.preRunSetup != nil { + tt.preRunSetup(s) + } + + if !tt.msg.ConfirmPayment.IsNil() { + tt.msg.ConfirmPayment = sdk.NewCoin(denom, sdk.NewInt(tt.msg.ConfirmPayment.Amount.Int64()).Mul(priceMultiplier)) + } + _, err := dymnskeeper.NewMsgServerImpl(s.dymNsKeeper).RegisterAlias(s.ctx, &tt.msg) + + defer func() { + if s.T().Failed() { + return + } + + s.Equal(originalModuleBalance, s.moduleBalance(), "module balance should not change because of burn") + + if tt.msg.Owner != "" && dymnsutils.IsValidBech32AccountAddress(tt.msg.Owner, true) { + laterBuyerBalance := s.balance2(tt.msg.Owner) + s.Equal( + sdk.NewInt(tt.wantLaterBuyerBalance).Mul(priceMultiplier).String(), + laterBuyerBalance.String(), + ) + } + }() + + defer func() { + rollAppId, found := s.dymNsKeeper.GetRollAppIdByAlias(s.ctx, tt.msg.Alias) + if tt.wantLaterAliasLinkedTo == "" { + s.Falsef(found, "alias should not be linked to any RollApp but got: %s", rollAppId) + } else { + s.True(found, "alias should be linked to a RollApp") + s.Equal(tt.wantLaterAliasLinkedTo, rollAppId, "alias should be linked to the RollApp") + } + + if dymnsutils.IsValidAlias(tt.msg.Alias) { + if s.dymNsKeeper.IsRollAppId(s.ctx, tt.msg.RollappId) { + if len(tt.wantLaterAliasesOwnedByRollApp) == 0 { + s.requireRollApp(tt.msg.RollappId).HasNoAlias() + } else { + s.requireRollApp(tt.msg.RollappId).HasAlias(tt.wantLaterAliasesOwnedByRollApp...) + } + } + } + }() + + if tt.wantErr { + s.Require().ErrorContains(err, tt.wantErrContains) + return + } + + s.Require().NoError(err) + + rollApp, found := s.rollAppKeeper.GetRollapp(s.ctx, tt.msg.RollappId) + s.Require().True(found) + + outputAddr, err := s.dymNsKeeper.ResolveByDymNameAddress(s.ctx, fmt.Sprintf("%s@%s", dymName.Name, tt.msg.Alias)) + s.Require().NoError(err) + s.Equal(dymNameOwnerAcc.bech32C(rollApp.Bech32Prefix), outputAddr, "resolution should be correct") + + if len(tt.wantLaterAliasesOwnedByRollApp) == 1 { + outputDNA, err := s.dymNsKeeper.ReverseResolveDymNameAddress(s.ctx, dymNameOwnerAcc.bech32C(rollApp.Bech32Prefix), rollApp.RollappId) + s.Require().NoError(err) + s.Require().NotEmpty(outputDNA, "should have value") + s.Equal(fmt.Sprintf("%s@%s", dymName.Name, tt.msg.Alias), outputDNA[0].String(), "reverse resolution should be correct") + } + }) + } + + s.Run("pass - new alias should be appended to the tails of the list", func() { + s.RefreshContext() + + s.mintToAccount2(rollApp1.owner, sdk.NewInt(price1L).Mul(priceMultiplier)) + + _, err := dymnskeeper.NewMsgServerImpl(s.dymNsKeeper).RegisterAlias(s.ctx, &dymnstypes.MsgRegisterAlias{ + Alias: freeAlias8L, + RollappId: rollApp1.rollAppId, + Owner: rollApp1.owner, + ConfirmPayment: sdk.NewCoin(denom, sdk.NewInt(price5PlusL).Mul(priceMultiplier)), + }) + s.Require().NoError(err) + + s.requireRollApp(rollApp1.rollAppId).HasAliasesWithOrder(append(rollApp1.aliases, freeAlias8L)...) + }) +} + +func (s *KeeperTestSuite) TestEstimateRegisterAlias() { + const denom = "atom" + const price1L int64 = 9 + const price2L int64 = 8 + const price3L int64 = 7 + const price4L int64 = 6 + const price5PlusL int64 = 5 + + // the number values used in this test will be multiplied by this value + priceMultiplier := sdk.NewInt(1e18) + + priceParams := dymnstypes.DefaultPriceParams() + priceParams.PriceDenom = denom + priceParams.AliasPriceSteps = []sdkmath.Int{ + sdk.NewInt(price1L).Mul(priceMultiplier), + sdk.NewInt(price2L).Mul(priceMultiplier), + sdk.NewInt(price3L).Mul(priceMultiplier), + sdk.NewInt(price4L).Mul(priceMultiplier), + sdk.NewInt(price5PlusL).Mul(priceMultiplier), + } + + tests := []struct { + name string + alias string + wantPrice int64 + }{ + { + name: "1 letter", + alias: "a", + wantPrice: price1L, + }, + { + name: "2 letters", + alias: "oh", + wantPrice: price2L, + }, + { + name: "3 letters", + alias: "dog", + wantPrice: price3L, + }, + { + name: "4 letters", + alias: "pool", + wantPrice: price4L, + }, + { + name: "5 letters", + alias: "human", + wantPrice: price5PlusL, + }, + { + name: "6 letters", + alias: "planet", + wantPrice: price5PlusL, + }, + { + name: "5+ letters", + alias: "universe", + wantPrice: price5PlusL, + }, + } + for _, tt := range tests { + s.Run(tt.name, func() { + got := dymnskeeper.EstimateRegisterAlias( + tt.alias, + priceParams, + ) + s.Equal( + sdkmath.NewInt(tt.wantPrice).Mul(priceMultiplier).String(), + got.Price.Amount.String(), + ) + s.Equal(denom, got.Price.Denom) + }) + } +} diff --git a/x/dymns/keeper/msg_server_register_name.go b/x/dymns/keeper/msg_server_register_name.go new file mode 100644 index 000000000..a0117a9a3 --- /dev/null +++ b/x/dymns/keeper/msg_server_register_name.go @@ -0,0 +1,243 @@ +package keeper + +import ( + "context" + "time" + + errorsmod "cosmossdk.io/errors" + "github.com/dymensionxyz/gerr-cosmos/gerrc" + + sdkmath "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" +) + +// RegisterName is message handler, handles registration of a new Dym-Name +// or extends the ownership duration of an existing Dym-Name. +func (k msgServer) RegisterName(goCtx context.Context, msg *dymnstypes.MsgRegisterName) (*dymnstypes.MsgRegisterNameResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + dymName, err := k.validateRegisterName(ctx, msg) + if err != nil { + return nil, err + } + + priceParams := k.PriceParams(ctx) + + addDurationInSeconds := 86400 * 365 * msg.Duration + + firstYearPrice := priceParams.GetFirstYearDymNamePrice(msg.Name) + + var prunePreviousDymNameRecord bool + var totalCost sdk.Coin + if dymName == nil { + // register new + prunePreviousDymNameRecord = true + + dymName = &dymnstypes.DymName{ + Name: msg.Name, + Owner: msg.Owner, + Controller: msg.Owner, + ExpireAt: ctx.BlockTime().Unix() + addDurationInSeconds, + Configs: nil, + Contact: msg.Contact, + } + + totalCost = sdk.NewCoin( + priceParams.PriceDenom, + firstYearPrice.Add( // first year has different price + priceParams.PriceExtends.Mul( + sdkmath.NewInt( + msg.Duration-1, // subtract first year + ), + ), + ), + ) + } else if dymName.Owner == msg.Owner { + if dymName.IsExpiredAtCtx(ctx) { + // renew + prunePreviousDymNameRecord = true + + dymName = &dymnstypes.DymName{ + Name: msg.Name, + Owner: msg.Owner, + Controller: msg.Owner, + ExpireAt: ctx.BlockTime().Unix() + addDurationInSeconds, + Configs: nil, + Contact: msg.Contact, + } + } else { + // extends + prunePreviousDymNameRecord = false + + // just add duration, no need to change any existing configuration + dymName.ExpireAt += addDurationInSeconds + + if msg.Contact != "" { + // update contact if provided + dymName.Contact = msg.Contact + } + } + + totalCost = sdk.NewCoin( + priceParams.PriceDenom, + priceParams.PriceExtends.Mul( + sdkmath.NewInt(msg.Duration), + ), + ) + } else { + // take over + prunePreviousDymNameRecord = true + + dymName = &dymnstypes.DymName{ + Name: msg.Name, + Owner: msg.Owner, + Controller: msg.Owner, + ExpireAt: ctx.BlockTime().Unix() + addDurationInSeconds, + Configs: nil, + Contact: msg.Contact, + } + + totalCost = sdk.NewCoin( + priceParams.PriceDenom, + firstYearPrice.Add( // first year has different price + priceParams.PriceExtends.Mul( + sdkmath.NewInt( + msg.Duration-1, // subtract first year + ), + ), + ), + ) + } + + if !totalCost.IsPositive() { + panic(errorsmod.Wrapf(gerrc.ErrFault, "total cost is not positive: %s", totalCost.String())) + } + + if !totalCost.Equal(msg.ConfirmPayment) { + return nil, errorsmod.Wrapf( + gerrc.ErrInvalidArgument, + "actual payment is is different with provided by user: %s != %s", totalCost.String(), msg.ConfirmPayment, + ) + } + + // At this place we don't do compare actual payment with estimated payment calculated by EstimateRegisterName + // because in-case there is different between them, it would prevent user to registration/renew. + + if err := k.bankKeeper.SendCoinsFromAccountToModule(ctx, + sdk.MustAccAddressFromBech32(msg.Owner), + dymnstypes.ModuleName, + sdk.NewCoins(totalCost), + ); err != nil { + return nil, err + } + + if err := k.bankKeeper.BurnCoins(ctx, dymnstypes.ModuleName, sdk.NewCoins(totalCost)); err != nil { + return nil, err + } + + if prunePreviousDymNameRecord { + if err := k.PruneDymName(ctx, msg.Name); err != nil { + return nil, err + } + } + + if err := k.SetDymName(ctx, *dymName); err != nil { + return nil, err + } + + if err := k.AfterDymNameOwnerChanged(ctx, dymName.Name); err != nil { + return nil, err + } + + if err := k.AfterDymNameConfigChanged(ctx, dymName.Name); err != nil { + return nil, err + } + + ctx.EventManager().EmitEvent(sdk.NewEvent( + dymnstypes.EventTypeSell, + sdk.NewAttribute(dymnstypes.AttributeKeySellAssetType, dymnstypes.TypeName.PrettyName()), + sdk.NewAttribute(dymnstypes.AttributeKeySellName, dymName.Name), + sdk.NewAttribute(dymnstypes.AttributeKeySellPrice, totalCost.String()), + sdk.NewAttribute(dymnstypes.AttributeKeySellTo, msg.Owner), + )) + + return &dymnstypes.MsgRegisterNameResponse{}, nil +} + +// validateRegisterName handles validation for the message handled by RegisterName. +func (k msgServer) validateRegisterName(ctx sdk.Context, msg *dymnstypes.MsgRegisterName) (*dymnstypes.DymName, error) { + if err := msg.ValidateBasic(); err != nil { + return nil, err + } + + miscParams := k.MiscParams(ctx) + + dymName := k.GetDymName(ctx, msg.Name) + if dymName != nil { + if dymName.Owner == msg.Owner { + // just renew or extends + } else { + if !dymName.IsExpiredAtCtx(ctx) { + return nil, gerrc.ErrUnauthenticated + } + + // take over + + // check grace period. + // Grace period is the time period after the Dym-Name expired + // that the previous owner can re-purchase the Dym-Name and no one else can take over. + // This follow domain specification to prevent user mistake. + dymNameCanBeTakeOverAfterEpoch := dymName.ExpireAt + int64(miscParams.GracePeriodDuration.Seconds()) + + if ctx.BlockTime().Unix() < dymNameCanBeTakeOverAfterEpoch { + // still in grace period + return nil, errorsmod.Wrapf( + gerrc.ErrFailedPrecondition, + "can be taken over after: %s", time.Unix(dymNameCanBeTakeOverAfterEpoch, 0).UTC().Format(time.DateTime), + ) + } + + // allowed to take over + } + } + + return dymName, nil +} + +// EstimateRegisterName returns the estimated amount of coins required to register a new Dym-Name +// or extends the ownership duration of an existing Dym-Name. +func EstimateRegisterName( + priceParams dymnstypes.PriceParams, + name string, + existingDymName *dymnstypes.DymName, + newOwner string, + duration int64, +) dymnstypes.EstimateRegisterNameResponse { + var newFirstYearPrice, extendsPrice sdkmath.Int + + if existingDymName != nil && existingDymName.Owner == newOwner { + // Dym-Name exists and just renew or extends by the same owner + + newFirstYearPrice = sdk.ZeroInt() // regardless of expired or not, we don't charge this + extendsPrice = priceParams.PriceExtends.Mul( + sdkmath.NewInt(duration), + ) + } else { + // new registration or take over + newFirstYearPrice = priceParams.GetFirstYearDymNamePrice(name) // charge based on name length for the first year + if duration > 1 { + extendsPrice = priceParams.PriceExtends.Mul( + sdkmath.NewInt(duration - 1), // subtract first year, which has different price + ) + } else { + extendsPrice = sdk.ZeroInt() + } + } + + return dymnstypes.EstimateRegisterNameResponse{ + FirstYearPrice: sdk.NewCoin(priceParams.PriceDenom, newFirstYearPrice), + ExtendPrice: sdk.NewCoin(priceParams.PriceDenom, extendsPrice), + TotalPrice: sdk.NewCoin(priceParams.PriceDenom, newFirstYearPrice.Add(extendsPrice)), + } +} diff --git a/x/dymns/keeper/msg_server_register_name_test.go b/x/dymns/keeper/msg_server_register_name_test.go new file mode 100644 index 000000000..1129f0e94 --- /dev/null +++ b/x/dymns/keeper/msg_server_register_name_test.go @@ -0,0 +1,1014 @@ +package keeper_test + +import ( + "time" + + "github.com/dymensionxyz/sdk-utils/utils/uptr" + + sdkmath "cosmossdk.io/math" + + "github.com/dymensionxyz/gerr-cosmos/gerrc" + + sdk "github.com/cosmos/cosmos-sdk/types" + dymnskeeper "github.com/dymensionxyz/dymension/v3/x/dymns/keeper" + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" +) + +func (s *KeeperTestSuite) Test_msgServer_RegisterName() { + denom := s.coin(0).Denom + const firstYearPrice1L = 6 + const firstYearPrice2L = 5 + const firstYearPrice3L = 4 + const firstYearPrice4L = 3 + const firstYearPrice5PlusL = 2 + const extendsPrice = 1 + const gracePeriod = 30 + + // the number values used in this test will be multiplied by this value + priceMultiplier := sdk.NewInt(1e18) + + buyerA := testAddr(1).bech32() + previousOwnerA := testAddr(2).bech32() + anotherA := testAddr(3).bech32() + + s.updateModuleParams(func(moduleParams dymnstypes.Params) dymnstypes.Params { + moduleParams.Price.NamePriceSteps = []sdkmath.Int{ + sdk.NewInt(firstYearPrice1L).Mul(priceMultiplier), + sdk.NewInt(firstYearPrice2L).Mul(priceMultiplier), + sdk.NewInt(firstYearPrice3L).Mul(priceMultiplier), + sdk.NewInt(firstYearPrice4L).Mul(priceMultiplier), + sdk.NewInt(firstYearPrice5PlusL).Mul(priceMultiplier), + } + moduleParams.Price.PriceExtends = sdk.NewInt(extendsPrice).Mul(priceMultiplier) + moduleParams.Price.PriceDenom = denom + // misc + moduleParams.Misc.GracePeriodDuration = gracePeriod * 24 * time.Hour + + return moduleParams + }) + s.SaveCurrentContext() + + s.Run("reject if message not pass validate basic", func() { + _, err := dymnskeeper.NewMsgServerImpl(s.dymNsKeeper).RegisterName(s.ctx, &dymnstypes.MsgRegisterName{}) + s.Require().ErrorContains(err, gerrc.ErrInvalidArgument.Error()) + }) + + const originalModuleBalance int64 = 88 + + tests := []struct { + name string + buyer string + originalBalance int64 + duration int64 + confirmPayment sdk.Coin + contact string + customDymName string + existingDymName *dymnstypes.DymName + setupActiveSellOrder bool + preRunSetup func(s *KeeperTestSuite) + wantLaterDymName *dymnstypes.DymName + wantErr bool + wantErrContains string + wantLaterBalance int64 + wantPruneSellOrder bool + }{ + { + name: "pass - can register, new Dym-Name", + buyer: buyerA, + originalBalance: firstYearPrice5PlusL + extendsPrice + 3, + duration: 2, + confirmPayment: s.coin(firstYearPrice5PlusL + extendsPrice), + contact: "contact@example.com", + wantLaterDymName: &dymnstypes.DymName{ + Owner: buyerA, + Controller: buyerA, + ExpireAt: s.now.Unix() + 86400*365*2, + Contact: "contact@example.com", + }, + wantLaterBalance: 3, + }, + { + name: "fail - not allow to takeover a non-expired Dym-Name", + buyer: buyerA, + originalBalance: 1, + duration: 2, + confirmPayment: s.coin(firstYearPrice5PlusL + extendsPrice), + contact: "contact@example.com", + existingDymName: &dymnstypes.DymName{ + Owner: previousOwnerA, + Controller: previousOwnerA, + ExpireAt: s.now.Add(time.Hour).Unix(), + Contact: "existing@example.com", + }, + wantLaterDymName: &dymnstypes.DymName{ + Owner: previousOwnerA, + Controller: previousOwnerA, + ExpireAt: s.now.Add(time.Hour).Unix(), + Contact: "existing@example.com", + }, + wantErr: true, + wantErrContains: "unauthenticated", + wantLaterBalance: 1, + }, + { + name: "fail - not allow to takeover an expired Dym-Name which in grace period", + buyer: buyerA, + originalBalance: 1, + duration: 2, + confirmPayment: s.coin(firstYearPrice5PlusL + extendsPrice), + existingDymName: &dymnstypes.DymName{ + Owner: previousOwnerA, + Controller: previousOwnerA, + ExpireAt: s.now.Unix() - 1, + }, + wantLaterDymName: &dymnstypes.DymName{ + Owner: previousOwnerA, + Controller: previousOwnerA, + ExpireAt: s.now.Unix() - 1, + }, + wantErr: true, + wantErrContains: "can be taken over after", + wantLaterBalance: 1, + }, + { + name: "fail - not enough balance to pay for the Dym-Name", + buyer: buyerA, + originalBalance: 1, + duration: 2, + confirmPayment: s.coin(firstYearPrice5PlusL + extendsPrice), + wantErr: true, + wantErrContains: "insufficient funds", + wantLaterBalance: 1, + }, + { + name: "fail - mis-match confirm payment", + buyer: buyerA, + originalBalance: firstYearPrice5PlusL + extendsPrice + 3, + duration: 2, + confirmPayment: s.coin(1), + wantErr: true, + wantErrContains: "actual payment is is different with provided by user", + wantLaterBalance: firstYearPrice5PlusL + extendsPrice + 3, + }, + { + name: "pass - deduct balance for new Dym-Name, 5+ letters, multiple years", + buyer: buyerA, + originalBalance: firstYearPrice5PlusL + extendsPrice*2 + 3, + duration: 3, + confirmPayment: s.coin(firstYearPrice5PlusL + extendsPrice*2), + wantLaterDymName: &dymnstypes.DymName{ + Owner: buyerA, + Controller: buyerA, + ExpireAt: s.now.Unix() + 86400*365*3, + }, + wantLaterBalance: 3, + }, + { + name: "pass - deduct balance for new Dym-Name, 5+ letters, 1 year", + buyer: buyerA, + originalBalance: firstYearPrice5PlusL + 3, + duration: 1, + confirmPayment: s.coin(firstYearPrice5PlusL), + wantLaterDymName: &dymnstypes.DymName{ + Owner: buyerA, + Controller: buyerA, + ExpireAt: s.now.Unix() + 86400*365, + }, + wantLaterBalance: 3, + }, + { + name: "pass - deduct balance for new Dym-Name, 4 letters, multiple years", + buyer: buyerA, + customDymName: "kids", + originalBalance: firstYearPrice4L + extendsPrice + 3, + duration: 2, + confirmPayment: s.coin(firstYearPrice4L + extendsPrice), + wantLaterDymName: &dymnstypes.DymName{ + Owner: buyerA, + Controller: buyerA, + ExpireAt: s.now.Unix() + 86400*365*2, + }, + wantLaterBalance: 3, + }, + { + name: "pass - deduct balance for new Dym-Name, 4 letters, 1 year", + buyer: buyerA, + customDymName: "kids", + originalBalance: firstYearPrice4L + 3, + duration: 1, + confirmPayment: s.coin(firstYearPrice4L), + wantLaterDymName: &dymnstypes.DymName{ + Owner: buyerA, + Controller: buyerA, + ExpireAt: s.now.Unix() + 86400*365, + }, + wantLaterBalance: 3, + }, + { + name: "pass - deduct balance for new Dym-Name, 3 letters, multiple years", + buyer: buyerA, + customDymName: "abc", + originalBalance: firstYearPrice3L + extendsPrice + 3, + duration: 2, + confirmPayment: s.coin(firstYearPrice3L + extendsPrice), + wantLaterDymName: &dymnstypes.DymName{ + Owner: buyerA, + Controller: buyerA, + ExpireAt: s.now.Unix() + 86400*365*2, + }, + wantLaterBalance: 3, + }, + { + name: "pass - deduct balance for new Dym-Name, 3 letters, 1 year", + buyer: buyerA, + customDymName: "abc", + originalBalance: firstYearPrice3L + 3, + duration: 1, + confirmPayment: s.coin(firstYearPrice3L), + wantLaterDymName: &dymnstypes.DymName{ + Owner: buyerA, + Controller: buyerA, + ExpireAt: s.now.Unix() + 86400*365, + }, + wantLaterBalance: 3, + }, + { + name: "pass - deduct balance for new Dym-Name, 2 letters, multiple years", + buyer: buyerA, + customDymName: "ab", + originalBalance: firstYearPrice2L + extendsPrice + 3, + duration: 2, + confirmPayment: s.coin(firstYearPrice2L + extendsPrice), + wantLaterDymName: &dymnstypes.DymName{ + Owner: buyerA, + Controller: buyerA, + ExpireAt: s.now.Unix() + 86400*365*2, + }, + wantLaterBalance: 3, + }, + { + name: "pass - deduct balance for new Dym-Name, 2 letters, 1 year", + buyer: buyerA, + customDymName: "ab", + originalBalance: firstYearPrice2L + 3, + duration: 1, + confirmPayment: s.coin(firstYearPrice2L), + wantLaterDymName: &dymnstypes.DymName{ + Owner: buyerA, + Controller: buyerA, + ExpireAt: s.now.Unix() + 86400*365, + }, + wantLaterBalance: 3, + }, + { + name: "pass - deduct balance for new Dym-Name, 1 letter, multiple years", + buyer: buyerA, + customDymName: "a", + originalBalance: firstYearPrice1L + extendsPrice + 3, + duration: 2, + confirmPayment: s.coin(firstYearPrice1L + extendsPrice), + wantLaterDymName: &dymnstypes.DymName{ + Owner: buyerA, + Controller: buyerA, + ExpireAt: s.now.Unix() + 86400*365*2, + }, + wantLaterBalance: 3, + }, + { + name: "pass - deduct balance for new Dym-Name, 1 letter, 1 year", + buyer: buyerA, + customDymName: "a", + originalBalance: firstYearPrice1L + 3, + duration: 1, + confirmPayment: s.coin(firstYearPrice1L), + wantLaterDymName: &dymnstypes.DymName{ + Owner: buyerA, + Controller: buyerA, + ExpireAt: s.now.Unix() + 86400*365, + }, + wantLaterBalance: 3, + }, + { + name: "pass - can extend owned Dym-Name, not expired", + buyer: buyerA, + originalBalance: extendsPrice*2 + 3, + duration: 2, + confirmPayment: s.coin(extendsPrice * 2), + existingDymName: &dymnstypes.DymName{ + Owner: buyerA, + Controller: buyerA, + ExpireAt: s.now.Unix() + 100, + }, + wantLaterDymName: &dymnstypes.DymName{ + Owner: buyerA, + Controller: buyerA, + ExpireAt: s.now.Unix() + 100 + 86400*365*2, + }, + wantLaterBalance: 3, + }, + { + name: "pass - when extend owned non-expired Dym-Name, keep config", + buyer: buyerA, + originalBalance: extendsPrice*2 + 3, + duration: 2, + confirmPayment: s.coin(extendsPrice * 2), + existingDymName: &dymnstypes.DymName{ + Owner: buyerA, + Controller: buyerA, + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{{ + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Value: buyerA, + }}, + Contact: "existing@example.com", + }, + setupActiveSellOrder: true, + wantLaterDymName: &dymnstypes.DymName{ + Owner: buyerA, + Controller: buyerA, + ExpireAt: s.now.Unix() + 100 + 86400*365*2, + Configs: []dymnstypes.DymNameConfig{{ + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Value: buyerA, + }}, + Contact: "existing@example.com", + }, + wantLaterBalance: 3, + wantPruneSellOrder: false, + }, + { + name: "pass - when extend owned non-expired Dym-Name, keep config, update contact if provided", + buyer: buyerA, + originalBalance: extendsPrice*2 + 3, + duration: 2, + confirmPayment: s.coin(extendsPrice * 2), + contact: "new-contact@example.com", + existingDymName: &dymnstypes.DymName{ + Owner: buyerA, + Controller: buyerA, + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{{ + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Value: buyerA, + }}, + Contact: "existing@example.com", + }, + setupActiveSellOrder: true, + wantLaterDymName: &dymnstypes.DymName{ + Owner: buyerA, + Controller: buyerA, + ExpireAt: s.now.Unix() + 100 + 86400*365*2, + Configs: []dymnstypes.DymNameConfig{{ + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Value: buyerA, + }}, + Contact: "new-contact@example.com", + }, + wantLaterBalance: 3, + wantPruneSellOrder: false, + }, + { + name: "pass - can renew owned Dym-Name, expired", + buyer: buyerA, + originalBalance: extendsPrice*2 + 3, + duration: 2, + confirmPayment: s.coin(extendsPrice * 2), + existingDymName: &dymnstypes.DymName{ + Owner: buyerA, + Controller: buyerA, + ExpireAt: 1, + }, + wantLaterDymName: &dymnstypes.DymName{ + Owner: buyerA, + Controller: buyerA, + ExpireAt: s.now.Unix() + 86400*365*2, + }, + wantLaterBalance: 3, + }, + { + name: "pass - can renew owned Dym-Name, expired, update contact if provided", + buyer: buyerA, + originalBalance: extendsPrice*2 + 3, + duration: 2, + confirmPayment: s.coin(extendsPrice * 2), + contact: "new-contact@example.com", + existingDymName: &dymnstypes.DymName{ + Owner: buyerA, + Controller: buyerA, + ExpireAt: 1, + }, + wantLaterDymName: &dymnstypes.DymName{ + Owner: buyerA, + Controller: buyerA, + ExpireAt: s.now.Unix() + 86400*365*2, + Contact: "new-contact@example.com", + }, + wantLaterBalance: 3, + }, + { + name: "pass - when renew previously-owned expired Dym-Name, reset config", + buyer: buyerA, + originalBalance: extendsPrice*2 + 3, + duration: 2, + confirmPayment: s.coin(extendsPrice * 2), + existingDymName: &dymnstypes.DymName{ + Owner: buyerA, + Controller: buyerA, + ExpireAt: 5, + Configs: []dymnstypes.DymNameConfig{{ + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Value: buyerA, + }}, + }, + setupActiveSellOrder: true, + wantLaterDymName: &dymnstypes.DymName{ + Owner: buyerA, + Controller: buyerA, + ExpireAt: s.now.Unix() + 86400*365*2, + Configs: nil, + }, + wantLaterBalance: 3, + wantPruneSellOrder: true, + }, + { + name: "pass - when renew previously-owned expired Dym-Name, reset config, update contact if provided", + buyer: buyerA, + originalBalance: extendsPrice*2 + 3, + duration: 2, + confirmPayment: s.coin(extendsPrice * 2), + contact: "new-contact@example.com", + existingDymName: &dymnstypes.DymName{ + Owner: buyerA, + Controller: buyerA, + ExpireAt: 5, + Configs: []dymnstypes.DymNameConfig{{ + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Value: buyerA, + }}, + }, + setupActiveSellOrder: true, + wantLaterDymName: &dymnstypes.DymName{ + Owner: buyerA, + Controller: buyerA, + ExpireAt: s.now.Unix() + 86400*365*2, + Configs: nil, + Contact: "new-contact@example.com", + }, + wantLaterBalance: 3, + wantPruneSellOrder: true, + }, + { + name: "pass - can take over an expired Dym-Name after grace period has passed", + buyer: buyerA, + originalBalance: firstYearPrice5PlusL + extendsPrice + 3, + duration: 2, + confirmPayment: s.coin(firstYearPrice5PlusL + extendsPrice), + existingDymName: &dymnstypes.DymName{ + Owner: previousOwnerA, + Controller: previousOwnerA, + ExpireAt: 1, + }, + wantLaterDymName: &dymnstypes.DymName{ + Owner: buyerA, + Controller: buyerA, + ExpireAt: s.now.Unix() + 86400*365*2, + }, + wantLaterBalance: 3, + }, + { + name: "pass - take over an expired when ownership changed, reset config", + buyer: buyerA, + originalBalance: firstYearPrice5PlusL + extendsPrice + 3, + duration: 2, + confirmPayment: s.coin(firstYearPrice5PlusL + extendsPrice), + existingDymName: &dymnstypes.DymName{ + Owner: previousOwnerA, + Controller: previousOwnerA, + ExpireAt: 1, + Configs: []dymnstypes.DymNameConfig{{ + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Value: buyerA, + }}, + Contact: "old-contact@example.com", + }, + wantLaterDymName: &dymnstypes.DymName{ + Owner: buyerA, + Controller: buyerA, + ExpireAt: s.now.Unix() + 86400*365*2, + Configs: nil, + }, + wantLaterBalance: 3, + }, + { + name: "pass - take over an expired when ownership changed, reset config, update contact if provided", + buyer: buyerA, + originalBalance: firstYearPrice5PlusL + extendsPrice + 3, + duration: 2, + confirmPayment: s.coin(firstYearPrice5PlusL + extendsPrice), + contact: "new-contact@example.com", + existingDymName: &dymnstypes.DymName{ + Owner: previousOwnerA, + Controller: previousOwnerA, + ExpireAt: 1, + Configs: []dymnstypes.DymNameConfig{{ + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Value: buyerA, + }}, + Contact: "old-contact@example.com", + }, + wantLaterDymName: &dymnstypes.DymName{ + Owner: buyerA, + Controller: buyerA, + ExpireAt: s.now.Unix() + 86400*365*2, + Configs: nil, + Contact: "new-contact@example.com", + }, + wantLaterBalance: 3, + }, + { + name: "fail - not enough balance to take over an expired Dym-Name after grace period has passed", + buyer: buyerA, + originalBalance: 1, + duration: 2, + confirmPayment: s.coin(firstYearPrice5PlusL + extendsPrice), + existingDymName: &dymnstypes.DymName{ + Owner: previousOwnerA, + Controller: previousOwnerA, + ExpireAt: 3, + }, + wantLaterDymName: &dymnstypes.DymName{ + Owner: previousOwnerA, + Controller: previousOwnerA, + ExpireAt: 3, + }, + wantErr: true, + wantErrContains: "insufficient funds", + wantLaterBalance: 1, + }, + } + for _, tt := range tests { + s.Run(tt.name, func() { + s.RefreshContext() + + s.mintToModuleAccount2(sdk.NewInt(originalModuleBalance).Mul(priceMultiplier)) + + if tt.originalBalance > 0 { + s.mintToAccount2(tt.buyer, sdk.NewInt(tt.originalBalance).Mul(priceMultiplier)) + } + + useRecordName := "my-name" + if tt.customDymName != "" { + useRecordName = tt.customDymName + } + + if tt.existingDymName != nil { + tt.existingDymName.Name = useRecordName + err := s.dymNsKeeper.SetDymName(s.ctx, *tt.existingDymName) + s.Require().NoError(err) + + if tt.setupActiveSellOrder { + so := dymnstypes.SellOrder{ + AssetId: useRecordName, + AssetType: dymnstypes.TypeName, + ExpireAt: tt.existingDymName.ExpireAt - 1, + MinPrice: s.coin(1), + SellPrice: uptr.To(s.coin(2)), + HighestBid: &dymnstypes.SellOrderBid{ + Bidder: anotherA, + Price: s.coin(2), + }, + } + err = s.dymNsKeeper.SetSellOrder(s.ctx, so) + s.Require().NoError(err) + } + } else { + s.Require().False(tt.setupActiveSellOrder, "bad setup testcase") + } + if tt.wantLaterDymName != nil { + tt.wantLaterDymName.Name = useRecordName + } + + if tt.preRunSetup != nil { + tt.preRunSetup(s) + } + + resp, err := dymnskeeper.NewMsgServerImpl(s.dymNsKeeper).RegisterName(s.ctx, &dymnstypes.MsgRegisterName{ + Name: useRecordName, + Duration: tt.duration, + Owner: tt.buyer, + ConfirmPayment: sdk.NewCoin(tt.confirmPayment.Denom, tt.confirmPayment.Amount.Mul(priceMultiplier)), + Contact: tt.contact, + }) + laterDymName := s.dymNsKeeper.GetDymName(s.ctx, useRecordName) + + defer func() { + laterBalance := s.balance2(tt.buyer) + s.Equal( + sdk.NewInt(tt.wantLaterBalance).Mul(priceMultiplier).String(), + laterBalance.String(), + ) + }() + + if tt.wantErr { + s.Require().ErrorContains(err, tt.wantErrContains) + s.Nil(resp) + + defer func() { + laterModuleBalance := s.moduleBalance2() + s.Equal( + sdk.NewInt(originalModuleBalance).Mul(priceMultiplier).String(), + laterModuleBalance.String(), + "module account balance should not be changed", + ) + }() + + if tt.existingDymName != nil { + s.Equal(tt.existingDymName.Name, laterDymName.Name, "should not change existing record") + s.Require().NotNil(tt.wantLaterDymName, "bad setup testcase") + s.Equal(*tt.wantLaterDymName, *laterDymName) + } else { + s.Nil(laterDymName) + s.Nil(tt.wantLaterDymName, "bad setup testcase") + } + + if tt.setupActiveSellOrder { + s.NotNil( + s.dymNsKeeper.GetSellOrder(s.ctx, useRecordName, dymnstypes.TypeName), + "sell order must be kept", + ) + } + return + } + + s.Require().NoError(err) + s.NotNil(resp) + + defer func() { + laterModuleBalance := s.moduleBalance2() + s.Equal( + sdk.NewInt(originalModuleBalance).Mul(priceMultiplier).String(), laterModuleBalance.String(), + "token should be burned", + ) + }() + + s.NotNil(laterDymName) + s.NotNil(tt.wantLaterDymName, "bad setup testcase") + s.Equal(*tt.wantLaterDymName, *laterDymName) + + if tt.setupActiveSellOrder { + if tt.wantPruneSellOrder { + s.Nil( + s.dymNsKeeper.GetSellOrder(s.ctx, useRecordName, dymnstypes.TypeName), + "sell order must be pruned", + ) + + if tt.existingDymName.Owner != laterDymName.Owner { + ownedByPreviousOwner, err := s.dymNsKeeper.GetDymNamesOwnedBy(s.ctx, tt.existingDymName.Owner) + s.Require().NoError(err) + s.Empty(ownedByPreviousOwner, "reverse mapping should be removed") + + mappedDymNamesByPreviousOwner, err := s.dymNsKeeper.GetDymNamesContainsConfiguredAddress(s.ctx, tt.existingDymName.Owner) + s.Require().NoError(err) + s.Empty(mappedDymNamesByPreviousOwner, "reverse mapping should be removed") + + mappedDymNamesByPreviousOwner, err = s.dymNsKeeper.GetDymNamesContainsFallbackAddress(s.ctx, + sdk.MustAccAddressFromBech32(tt.existingDymName.Owner).Bytes(), + ) + s.Require().NoError(err) + s.Empty(mappedDymNamesByPreviousOwner, "reverse mapping should be removed") + } + } else { + s.NotNil(s.dymNsKeeper.GetSellOrder(s.ctx, useRecordName, dymnstypes.TypeName), "sell order must be kept") + } + } else { + s.False(tt.wantPruneSellOrder, "bad setup testcase") + } + + ownedByBuyer, err := s.dymNsKeeper.GetDymNamesOwnedBy(s.ctx, tt.buyer) + s.Require().NoError(err) + s.Len(ownedByBuyer, 1, "reverse mapping should be set") + s.Equal(useRecordName, ownedByBuyer[0].Name) + + mappedDymNamesByBuyer, err := s.dymNsKeeper.GetDymNamesContainsConfiguredAddress(s.ctx, tt.buyer) + s.Require().NoError(err) + s.Len(mappedDymNamesByBuyer, 1, "reverse mapping should be set") + s.Equal(useRecordName, mappedDymNamesByBuyer[0].Name) + + mappedDymNamesByBuyer, err = s.dymNsKeeper.GetDymNamesContainsFallbackAddress(s.ctx, + sdk.MustAccAddressFromBech32(tt.buyer).Bytes(), + ) + s.Require().NoError(err) + s.Len(mappedDymNamesByBuyer, 1, "reverse mapping should be set") + s.Equal(useRecordName, mappedDymNamesByBuyer[0].Name) + }) + } +} + +func (s *KeeperTestSuite) TestEstimateRegisterName() { + const denom = "atom" + const price1L int64 = 9 + const price2L int64 = 8 + const price3L int64 = 7 + const price4L int64 = 6 + const price5PlusL int64 = 5 + const extendsPrice int64 = 4 + + // the number values used in this test will be multiplied by this value + priceMultiplier := sdk.NewInt(1e18) + + priceParams := dymnstypes.DefaultPriceParams() + priceParams.PriceDenom = denom + priceParams.NamePriceSteps = []sdkmath.Int{ + sdk.NewInt(price1L).Mul(priceMultiplier), + sdk.NewInt(price2L).Mul(priceMultiplier), + sdk.NewInt(price3L).Mul(priceMultiplier), + sdk.NewInt(price4L).Mul(priceMultiplier), + sdk.NewInt(price5PlusL).Mul(priceMultiplier), + } + priceParams.PriceExtends = sdk.NewInt(extendsPrice).Mul(priceMultiplier) + + buyerA := testAddr(1).bech32() + previousOwnerA := testAddr(2).bech32() + + tests := []struct { + name string + dymName string + existingDymName *dymnstypes.DymName + newOwner string + duration int64 + wantFirstYearPrice int64 + wantExtendPrice int64 + }{ + { + name: "new registration, 1 letter, 1 year", + dymName: "a", + existingDymName: nil, + newOwner: buyerA, + duration: 1, + wantFirstYearPrice: price1L, + wantExtendPrice: 0, + }, + { + name: "new registration, 1 letter, 2 years", + dymName: "a", + existingDymName: nil, + newOwner: buyerA, + duration: 2, + wantFirstYearPrice: price1L, + wantExtendPrice: extendsPrice, + }, + { + name: "new registration, 1 letter, N years", + dymName: "a", + existingDymName: nil, + newOwner: buyerA, + duration: 99, + wantFirstYearPrice: price1L, + wantExtendPrice: extendsPrice * (99 - 1), + }, + { + name: "new registration, 6 letters, 1 year", + dymName: "bridge", + existingDymName: nil, + newOwner: buyerA, + duration: 1, + wantFirstYearPrice: price5PlusL, + wantExtendPrice: 0, + }, + { + name: "new registration, 6 letters, 2 years", + dymName: "bridge", + existingDymName: nil, + newOwner: buyerA, + duration: 2, + wantFirstYearPrice: price5PlusL, + wantExtendPrice: extendsPrice, + }, + { + name: "new registration, 5+ letters, N years", + dymName: "central", + existingDymName: nil, + newOwner: buyerA, + duration: 99, + wantFirstYearPrice: price5PlusL, + wantExtendPrice: extendsPrice * (99 - 1), + }, + { + name: "extends same owner, 1 letter, 1 year", + dymName: "a", + existingDymName: &dymnstypes.DymName{ + Name: "a", + Owner: buyerA, + Controller: buyerA, + ExpireAt: s.now.Unix() + 100, + }, + newOwner: buyerA, + duration: 1, + wantFirstYearPrice: 0, + wantExtendPrice: extendsPrice, + }, + { + name: "extends same owner, 1 letter, 2 years", + dymName: "a", + existingDymName: &dymnstypes.DymName{ + Name: "a", + Owner: buyerA, + Controller: buyerA, + ExpireAt: s.now.Unix() + 100, + }, + newOwner: buyerA, + duration: 2, + wantFirstYearPrice: 0, + wantExtendPrice: extendsPrice * 2, + }, + { + name: "extends same owner, 1 letter, N years", + dymName: "a", + existingDymName: &dymnstypes.DymName{ + Name: "a", + Owner: buyerA, + Controller: buyerA, + ExpireAt: s.now.Unix() + 100, + }, + newOwner: buyerA, + duration: 99, + wantFirstYearPrice: 0, + wantExtendPrice: extendsPrice * 99, + }, + { + name: "extends same owner, 6 letters, 1 year", + dymName: "bridge", + existingDymName: &dymnstypes.DymName{ + Name: "bridge", + Owner: buyerA, + Controller: buyerA, + ExpireAt: s.now.Unix() + 100, + }, + newOwner: buyerA, + duration: 1, + wantFirstYearPrice: 0, + wantExtendPrice: extendsPrice, + }, + { + name: "extends same owner, 6 letters, 2 years", + dymName: "bridge", + existingDymName: &dymnstypes.DymName{ + Name: "bridge", + Owner: buyerA, + Controller: buyerA, + ExpireAt: s.now.Unix() + 100, + }, + newOwner: buyerA, + duration: 2, + wantFirstYearPrice: 0, + wantExtendPrice: extendsPrice * 2, + }, + { + name: "extends same owner, 5+ letters, N years", + dymName: "central", + existingDymName: &dymnstypes.DymName{ + Name: "central", + Owner: buyerA, + Controller: buyerA, + ExpireAt: s.now.Unix() + 100, + }, + newOwner: buyerA, + duration: 99, + wantFirstYearPrice: 0, + wantExtendPrice: extendsPrice * 99, + }, + { + name: "extends expired, same owner, 5+ letters, 2 years", + dymName: "central", + existingDymName: &dymnstypes.DymName{ + Name: "central", + Owner: buyerA, + Controller: buyerA, + ExpireAt: s.now.Unix() - 1, + }, + newOwner: buyerA, + duration: 2, + wantFirstYearPrice: 0, + wantExtendPrice: extendsPrice * 2, + }, + { + name: "take-over, 1 letter, 1 year", + dymName: "a", + existingDymName: &dymnstypes.DymName{ + Name: "a", + Owner: previousOwnerA, + Controller: previousOwnerA, + ExpireAt: s.now.Unix() - 1, + }, + newOwner: buyerA, + duration: 1, + wantFirstYearPrice: price1L, + wantExtendPrice: 0, + }, + { + name: "take-over, 1 letter, 3 years", + dymName: "a", + existingDymName: &dymnstypes.DymName{ + Name: "a", + Owner: previousOwnerA, + Controller: previousOwnerA, + ExpireAt: s.now.Unix() - 1, + }, + newOwner: buyerA, + duration: 3, + wantFirstYearPrice: price1L, + wantExtendPrice: extendsPrice * 2, + }, + { + name: "take-over, 6 letters, 1 year", + dymName: "bridge", + existingDymName: &dymnstypes.DymName{ + Name: "bridge", + Owner: previousOwnerA, + Controller: previousOwnerA, + ExpireAt: s.now.Unix() - 1, + }, + newOwner: buyerA, + duration: 1, + wantFirstYearPrice: price5PlusL, + wantExtendPrice: 0, + }, + { + name: "take-over, 6 letters, 3 years", + dymName: "bridge", + existingDymName: &dymnstypes.DymName{ + Name: "bridge", + Owner: previousOwnerA, + Controller: previousOwnerA, + ExpireAt: s.now.Unix() - 1, + }, + newOwner: buyerA, + duration: 3, + wantFirstYearPrice: price5PlusL, + wantExtendPrice: extendsPrice * 2, + }, + { + name: "new registration, 2 letters", + dymName: "aa", + existingDymName: nil, + newOwner: buyerA, + duration: 3, + wantFirstYearPrice: price2L, + wantExtendPrice: extendsPrice * 2, + }, + { + name: "new registration, 3 letters", + dymName: "aaa", + existingDymName: nil, + newOwner: buyerA, + duration: 3, + wantFirstYearPrice: price3L, + wantExtendPrice: extendsPrice * 2, + }, + { + name: "new registration, 4 letters", + dymName: "geek", + existingDymName: nil, + newOwner: buyerA, + duration: 3, + wantFirstYearPrice: price4L, + wantExtendPrice: extendsPrice * 2, + }, + { + name: "new registration, 5 letters", + dymName: "human", + existingDymName: nil, + newOwner: buyerA, + duration: 3, + wantFirstYearPrice: price5PlusL, + wantExtendPrice: extendsPrice * 2, + }, + } + for _, tt := range tests { + s.Run(tt.name, func() { + got := dymnskeeper.EstimateRegisterName( + priceParams, + tt.dymName, + tt.existingDymName, + tt.newOwner, + tt.duration, + ) + s.Equal( + sdkmath.NewInt(tt.wantFirstYearPrice).Mul(priceMultiplier).String(), + got.FirstYearPrice.Amount.String(), + ) + s.Equal( + sdkmath.NewInt(tt.wantExtendPrice).Mul(priceMultiplier).String(), + got.ExtendPrice.Amount.String(), + ) + s.Equal( + sdkmath.NewInt(tt.wantFirstYearPrice+tt.wantExtendPrice).Mul(priceMultiplier).String(), + got.TotalPrice.Amount.String(), + "total price must be equals to sum of first year and extend price", + ) + s.Equal(denom, got.FirstYearPrice.Denom) + s.Equal(denom, got.ExtendPrice.Denom) + s.Equal(denom, got.TotalPrice.Denom) + }) + } +} diff --git a/x/dymns/keeper/msg_server_set_controller.go b/x/dymns/keeper/msg_server_set_controller.go new file mode 100644 index 000000000..6e91b61e7 --- /dev/null +++ b/x/dymns/keeper/msg_server_set_controller.go @@ -0,0 +1,55 @@ +package keeper + +import ( + "context" + + errorsmod "cosmossdk.io/errors" + "github.com/dymensionxyz/gerr-cosmos/gerrc" + + sdk "github.com/cosmos/cosmos-sdk/types" + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" +) + +// SetController is message handler, +// handles setting a controller for a Dym-Name, performed by the owner. +func (k msgServer) SetController(goCtx context.Context, msg *dymnstypes.MsgSetController) (*dymnstypes.MsgSetControllerResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + dymName, err := k.validateSetController(ctx, msg) + if err != nil { + return nil, err + } + + dymName.Controller = msg.Controller + if err := k.SetDymName(ctx, *dymName); err != nil { + return nil, err + } + + return &dymnstypes.MsgSetControllerResponse{}, nil +} + +// validateSetController handles validation for message handled by SetController +func (k msgServer) validateSetController(ctx sdk.Context, msg *dymnstypes.MsgSetController) (*dymnstypes.DymName, error) { + if err := msg.ValidateBasic(); err != nil { + return nil, err + } + + dymName := k.GetDymName(ctx, msg.Name) + if dymName == nil { + return nil, errorsmod.Wrapf(gerrc.ErrNotFound, "Dym-Name: %s", msg.Name) + } + + if dymName.Owner != msg.Owner { + return nil, errorsmod.Wrap(gerrc.ErrPermissionDenied, "not the owner of the Dym-Name") + } + + if dymName.IsExpiredAtCtx(ctx) { + return nil, errorsmod.Wrap(gerrc.ErrUnauthenticated, "Dym-Name is already expired") + } + + if dymName.Controller == msg.Controller { + return nil, errorsmod.Wrap(gerrc.ErrInvalidArgument, "controller already set") + } + + return dymName, nil +} diff --git a/x/dymns/keeper/msg_server_set_controller_test.go b/x/dymns/keeper/msg_server_set_controller_test.go new file mode 100644 index 000000000..7ff93d7b4 --- /dev/null +++ b/x/dymns/keeper/msg_server_set_controller_test.go @@ -0,0 +1,142 @@ +package keeper_test + +import ( + "github.com/dymensionxyz/gerr-cosmos/gerrc" + + dymnskeeper "github.com/dymensionxyz/dymension/v3/x/dymns/keeper" + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" +) + +func (s *KeeperTestSuite) Test_msgServer_SetController() { + s.Run("reject if message not pass validate basic", func() { + _, err := dymnskeeper.NewMsgServerImpl(s.dymNsKeeper).SetController(s.ctx, &dymnstypes.MsgSetController{}) + s.Require().ErrorContains(err, gerrc.ErrInvalidArgument.Error()) + }) + + ownerA := testAddr(1).bech32() + controllerA := testAddr(2).bech32() + notOwnerA := testAddr(3).bech32() + + tests := []struct { + name string + dymName *dymnstypes.DymName + recordName string + wantErr bool + wantErrContains string + }{ + { + name: "fail - reject if Dym-Name not found", + recordName: "a", + wantErr: true, + wantErrContains: "Dym-Name: a: not found", + }, + { + name: "fail - reject if not owned", + dymName: &dymnstypes.DymName{ + Name: "a", + Owner: notOwnerA, + Controller: notOwnerA, + ExpireAt: s.now.Unix() + 100, + }, + recordName: "a", + wantErr: true, + wantErrContains: "not the owner of the Dym-Name", + }, + { + name: "fail - reject if not new controller is the same as previous controller", + dymName: &dymnstypes.DymName{ + Name: "a", + Owner: ownerA, + Controller: controllerA, + ExpireAt: s.now.Unix() + 100, + }, + recordName: "a", + wantErr: true, + wantErrContains: "controller already set", + }, + { + name: "fail - reject if Dym-Name is already expired", + dymName: &dymnstypes.DymName{ + Name: "a", + Owner: ownerA, + Controller: controllerA, + ExpireAt: s.now.Unix() - 1, + }, + recordName: "a", + wantErr: true, + wantErrContains: "Dym-Name is already expired", + }, + { + name: "pass - accept if new controller is different from previous controller", + dymName: &dymnstypes.DymName{ + Name: "a", + Owner: ownerA, + Controller: ownerA, + ExpireAt: s.now.Unix() + 100, + }, + recordName: "a", + }, + { + name: "pass - changing controller will not change configs", + dymName: &dymnstypes.DymName{ + Name: "a", + Owner: ownerA, + Controller: ownerA, + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{{ + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Value: ownerA, + }}, + }, + recordName: "a", + }, + } + for _, tt := range tests { + s.Run(tt.name, func() { + s.RefreshContext() + + if tt.dymName != nil { + err := s.dymNsKeeper.SetDymName(s.ctx, *tt.dymName) + s.Require().NoError(err) + } + + resp, err := dymnskeeper.NewMsgServerImpl(s.dymNsKeeper).SetController(s.ctx, &dymnstypes.MsgSetController{ + Name: tt.recordName, + Controller: controllerA, + Owner: ownerA, + }) + if tt.wantErr { + s.Require().NotEmpty(tt.wantErrContains, "mis-configured test case") + s.Require().Error(err) + s.Require().Contains(err.Error(), tt.wantErrContains) + + s.Require().Nil(resp) + + laterDymName := s.dymNsKeeper.GetDymName(s.ctx, tt.recordName) + + if tt.dymName != nil { + s.Require().Equal(*tt.dymName, *laterDymName) + } else { + s.Require().Nil(laterDymName) + } + + return + } + + s.Require().NoError(err) + + s.Require().NotNil(resp) + + s.Require().NotNil(tt.dymName, "mis-configured test case") + + laterDymName := s.dymNsKeeper.GetDymName(s.ctx, tt.recordName) + s.Require().NotNil(laterDymName) + + s.Require().Equal(controllerA, laterDymName.Controller) + s.Require().Equal(ownerA, laterDymName.Owner) + + s.Require().Equal(tt.dymName.ExpireAt, laterDymName.ExpireAt) + s.Require().Equal(tt.dymName.Configs, laterDymName.Configs) + }) + } +} diff --git a/x/dymns/keeper/msg_server_test.go b/x/dymns/keeper/msg_server_test.go new file mode 100644 index 000000000..6ffeb3e47 --- /dev/null +++ b/x/dymns/keeper/msg_server_test.go @@ -0,0 +1,16 @@ +package keeper + +import ( + "testing" + "time" + + "github.com/stretchr/testify/require" +) + +func TestTime(t *testing.T) { + require.Equal( + t, + time.Now().Unix(), time.Now().UTC().Unix(), + "if mis-match, 100% sure will causes AppHash", + ) +} diff --git a/x/dymns/keeper/msg_server_transfer_ownership.go b/x/dymns/keeper/msg_server_transfer_ownership.go new file mode 100644 index 000000000..17d8dae5e --- /dev/null +++ b/x/dymns/keeper/msg_server_transfer_ownership.go @@ -0,0 +1,92 @@ +package keeper + +import ( + "context" + + errorsmod "cosmossdk.io/errors" + "github.com/dymensionxyz/gerr-cosmos/gerrc" + + sdk "github.com/cosmos/cosmos-sdk/types" + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" +) + +// TransferDymNameOwnership is message handler, +// handles transfer of ownership of a Dym-Name, performed by the owner. +func (k msgServer) TransferDymNameOwnership(goCtx context.Context, msg *dymnstypes.MsgTransferDymNameOwnership) (*dymnstypes.MsgTransferDymNameOwnershipResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + dymName, err := k.validateTransferDymNameOwnership(ctx, msg) + if err != nil { + return nil, err + } + + if err := k.transferDymNameOwnership(ctx, *dymName, msg.NewOwner); err != nil { + return nil, err + } + + return &dymnstypes.MsgTransferDymNameOwnershipResponse{}, nil +} + +// validateTransferDymNameOwnership handles validation for message handled by TransferDymNameOwnership +func (k msgServer) validateTransferDymNameOwnership(ctx sdk.Context, msg *dymnstypes.MsgTransferDymNameOwnership) (*dymnstypes.DymName, error) { + if err := msg.ValidateBasic(); err != nil { + return nil, err + } + + dymName := k.GetDymName(ctx, msg.Name) + if dymName == nil { + return nil, errorsmod.Wrapf(gerrc.ErrNotFound, "Dym-Name: %s", msg.Name) + } + + if dymName.Owner != msg.Owner { + return nil, errorsmod.Wrap(gerrc.ErrPermissionDenied, "not the owner of the Dym-Name") + } + + if dymName.IsExpiredAtCtx(ctx) { + return nil, errorsmod.Wrap(gerrc.ErrUnauthenticated, "Dym-Name is already expired") + } + + so := k.GetSellOrder(ctx, msg.Name, dymnstypes.TypeName) + if so != nil { + // by ignoring SO, can fall into case that SO not completed/lost funds of bidder,... + + return nil, errorsmod.Wrap( + gerrc.ErrFailedPrecondition, + "can not transfer ownership while there is an active Sell Order", + ) + } + + return dymName, nil +} + +// transferDymNameOwnership transfers ownership of a Dym-Name to a new owner. +func (k Keeper) transferDymNameOwnership(ctx sdk.Context, dymName dymnstypes.DymName, newOwner string) error { + if err := k.PruneDymName(ctx, dymName.Name); err != nil { + return err + } + + newDymNameRecord := dymnstypes.DymName{ + Name: dymName.Name, + Owner: newOwner, // transfer ownership + Controller: newOwner, // transfer controller + ExpireAt: dymName.ExpireAt, // keep the same expiration date + Configs: nil, // clear configs + Contact: "", // clear contact + } + + if err := k.SetDymName(ctx, newDymNameRecord); err != nil { + return err + } + + // we call this because the owner was changed + if err := k.AfterDymNameOwnerChanged(ctx, newDymNameRecord.Name); err != nil { + return err + } + + // we call this because the config was cleared + if err := k.AfterDymNameConfigChanged(ctx, newDymNameRecord.Name); err != nil { + return err + } + + return nil +} diff --git a/x/dymns/keeper/msg_server_transfer_ownership_test.go b/x/dymns/keeper/msg_server_transfer_ownership_test.go new file mode 100644 index 000000000..5f23b45c6 --- /dev/null +++ b/x/dymns/keeper/msg_server_transfer_ownership_test.go @@ -0,0 +1,284 @@ +package keeper_test + +import ( + "fmt" + + "github.com/dymensionxyz/sdk-utils/utils/uptr" + + "github.com/dymensionxyz/gerr-cosmos/gerrc" + + sdk "github.com/cosmos/cosmos-sdk/types" + dymnskeeper "github.com/dymensionxyz/dymension/v3/x/dymns/keeper" + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" +) + +func (s *KeeperTestSuite) Test_msgServer_TransferDymNameOwnership() { + s.Run("reject if message not pass validate basic", func() { + _, err := dymnskeeper.NewMsgServerImpl(s.dymNsKeeper).TransferDymNameOwnership(s.ctx, &dymnstypes.MsgTransferDymNameOwnership{}) + s.Require().ErrorContains(err, gerrc.ErrInvalidArgument.Error()) + }) + + ownerA := testAddr(1).bech32() + newOwnerA := testAddr(2).bech32() + anotherA := testAddr(3).bech32() + bidderA := testAddr(4).bech32() + + const recordName = "my-name" + + tests := []struct { + name string + dymName *dymnstypes.DymName + sellOrder *dymnstypes.SellOrder + customNewOwner string + wantErr bool + wantErrContains string + }{ + { + name: "fail - Dym-Name does not exists", + dymName: nil, + wantErr: true, + wantErrContains: fmt.Sprintf("Dym-Name: %s: not found", recordName), + }, + { + name: "fail - reject if not owned", + dymName: &dymnstypes.DymName{ + Owner: anotherA, + Controller: anotherA, + ExpireAt: s.now.Unix() + 100, + }, + wantErr: true, + wantErrContains: "not the owner of the Dym-Name", + }, + { + name: "fail - reject if Dym-Name expired", + dymName: &dymnstypes.DymName{ + Owner: ownerA, + Controller: ownerA, + ExpireAt: s.now.Unix() - 1, + }, + wantErr: true, + wantErrContains: "Dym-Name is already expired", + }, + { + name: "fail - reject if new owner is the same as current owner", + dymName: &dymnstypes.DymName{ + Owner: ownerA, + Controller: ownerA, + ExpireAt: s.now.Unix() + 100, + }, + customNewOwner: ownerA, + wantErr: true, + wantErrContains: "new owner must be different from the current owner", + }, + { + name: "fail - reject if Sell Order exists, expired SO", + dymName: &dymnstypes.DymName{ + Owner: ownerA, + Controller: ownerA, + ExpireAt: s.now.Unix() + 100, + }, + sellOrder: &dymnstypes.SellOrder{ + AssetType: dymnstypes.TypeName, + ExpireAt: 1, + MinPrice: s.coin(100), + }, + wantErr: true, + wantErrContains: "can not transfer ownership while there is an active Sell Order", + }, + { + name: "fail - reject if Sell Order exists, not finished SO", + dymName: &dymnstypes.DymName{ + Owner: ownerA, + Controller: ownerA, + ExpireAt: s.now.Unix() + 100, + }, + sellOrder: &dymnstypes.SellOrder{ + AssetType: dymnstypes.TypeName, + ExpireAt: s.now.Unix() + 100, + MinPrice: s.coin(100), + }, + wantErr: true, + wantErrContains: "can not transfer ownership while there is an active Sell Order", + }, + { + name: "fail - reject if Sell Order exists, not finished SO", + dymName: &dymnstypes.DymName{ + Owner: ownerA, + Controller: ownerA, + ExpireAt: s.now.Unix() + 100, + }, + sellOrder: &dymnstypes.SellOrder{ + AssetType: dymnstypes.TypeName, + ExpireAt: s.now.Unix() + 100, + MinPrice: s.coin(100), + HighestBid: &dymnstypes.SellOrderBid{ + Bidder: bidderA, + Price: s.coin(200), + }, + }, + wantErr: true, + wantErrContains: "can not transfer ownership while there is an active Sell Order", + }, + { + name: "fail - reject if Sell Order exists, completed SO", + dymName: &dymnstypes.DymName{ + Owner: ownerA, + Controller: ownerA, + ExpireAt: s.now.Unix() + 100, + }, + sellOrder: &dymnstypes.SellOrder{ + AssetType: dymnstypes.TypeName, + ExpireAt: s.now.Unix() + 100, + MinPrice: s.coin(100), + SellPrice: uptr.To(s.coin(200)), + HighestBid: &dymnstypes.SellOrderBid{ + Bidder: bidderA, + Price: s.coin(200), + }, + }, + wantErr: true, + wantErrContains: "can not transfer ownership while there is an active Sell Order", + }, + { + name: "pass - can transfer ownership", + dymName: &dymnstypes.DymName{ + Owner: ownerA, + Controller: ownerA, + ExpireAt: s.now.Unix() + 100, + }, + }, + { + name: "pass - can transfer ownership", + dymName: &dymnstypes.DymName{ + Owner: ownerA, + Controller: ownerA, + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{{ + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "a", + Value: anotherA, + }}, + }, + }, + } + for _, tt := range tests { + s.Run(tt.name, func() { + s.RefreshContext() + + if tt.dymName != nil { + if tt.dymName.Name == "" { + tt.dymName.Name = recordName + } + s.setDymNameWithFunctionsAfter(*tt.dymName) + } + + if tt.sellOrder != nil { + s.Require().NotNil(tt.dymName, "bad test setup") + tt.sellOrder.AssetId = recordName + s.Require().NoError(s.dymNsKeeper.SetSellOrder(s.ctx, *tt.sellOrder)) + } + + useNewOwner := newOwnerA + if tt.customNewOwner != "" { + useNewOwner = tt.customNewOwner + } + + msg := &dymnstypes.MsgTransferDymNameOwnership{ + Name: recordName, + Owner: ownerA, + NewOwner: useNewOwner, + } + resp, err := dymnskeeper.NewMsgServerImpl(s.dymNsKeeper).TransferDymNameOwnership(s.ctx, msg) + laterDymName := s.dymNsKeeper.GetDymName(s.ctx, recordName) + + if tt.dymName != nil { + s.Require().NotNil(laterDymName) + } else { + s.Require().Nil(laterDymName) + } + + if tt.wantErr { + s.Require().NotEmpty(tt.wantErrContains, "mis-configured test case") + s.Require().Error(err) + s.Require().Contains(err.Error(), tt.wantErrContains) + + s.Require().Nil(resp) + + if tt.dymName != nil { + s.Require().Equal(*tt.dymName, *laterDymName, "Dym-Name should not be changed") + + if tt.dymName.ExpireAt > s.now.Unix() { + list, err := s.dymNsKeeper.GetDymNamesOwnedBy(s.ctx, tt.dymName.Owner) + // GetDymNamesOwnedBy does not return expired Dym-Names + s.Require().NoError(err) + s.Require().Len(list, 1, "reverse mapping should be kept") + + names, err := s.dymNsKeeper.GetDymNamesContainsConfiguredAddress(s.ctx, tt.dymName.Owner) + s.Require().NoError(err) + s.Require().Len(names, 1, "reverse mapping should be kept") + + names, err = s.dymNsKeeper.GetDymNamesContainsFallbackAddress(s.ctx, + sdk.MustAccAddressFromBech32(tt.dymName.Owner).Bytes(), + ) + s.Require().NoError(err) + s.Require().Len(names, 1, "reverse mapping should be kept") + } + } + return + } + + s.Require().NotNil(tt.dymName, "bad test setup") + + s.Require().NoError(err) + + s.Require().NotNil(resp) + + previousOwner := ownerA + + s.Require().NotNil(laterDymName) + + s.Require().Equal( + tt.dymName.ExpireAt, laterDymName.ExpireAt, + "expiration date should not be changed", + ) + + wantLaterDymName := dymnstypes.DymName{ + Name: recordName, + Owner: useNewOwner, + Controller: useNewOwner, + ExpireAt: tt.dymName.ExpireAt, + Configs: nil, + } + s.Require().Equal(wantLaterDymName, *laterDymName) + + list, err := s.dymNsKeeper.GetDymNamesOwnedBy(s.ctx, previousOwner) + s.Require().NoError(err) + s.Require().Empty(list, "reverse mapping of previous owner should be removed") + + names, err := s.dymNsKeeper.GetDymNamesContainsConfiguredAddress(s.ctx, previousOwner) + s.Require().NoError(err) + s.Require().Empty(names, "reverse mapping of previous owner should be removed") + + names, err = s.dymNsKeeper.GetDymNamesContainsFallbackAddress(s.ctx, + sdk.MustAccAddressFromBech32(previousOwner).Bytes(), + ) + s.Require().NoError(err) + s.Require().Empty(names, "reverse mapping of previous owner should be removed") + + list, err = s.dymNsKeeper.GetDymNamesOwnedBy(s.ctx, useNewOwner) + s.Require().NoError(err) + s.Require().Len(list, 1, "reverse mapping of new owner should be added") + + names, err = s.dymNsKeeper.GetDymNamesContainsConfiguredAddress(s.ctx, useNewOwner) + s.Require().NoError(err) + s.Require().Len(names, 1, "reverse mapping of new owner should be added") + + names, err = s.dymNsKeeper.GetDymNamesContainsFallbackAddress(s.ctx, + sdk.MustAccAddressFromBech32(useNewOwner).Bytes(), + ) + s.Require().NoError(err) + s.Require().Len(names, 1, "reverse mapping of new owner should be added") + }) + } +} diff --git a/x/dymns/keeper/msg_server_update_details.go b/x/dymns/keeper/msg_server_update_details.go new file mode 100644 index 000000000..b11946741 --- /dev/null +++ b/x/dymns/keeper/msg_server_update_details.go @@ -0,0 +1,95 @@ +package keeper + +import ( + "context" + + errorsmod "cosmossdk.io/errors" + "github.com/dymensionxyz/gerr-cosmos/gerrc" + + sdk "github.com/cosmos/cosmos-sdk/types" + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" +) + +// UpdateDetails is message handler, +// handles updating Dym-Name details, performed by the controller. +func (k msgServer) UpdateDetails(goCtx context.Context, msg *dymnstypes.MsgUpdateDetails) (*dymnstypes.MsgUpdateDetailsResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + dymName, err := k.validateUpdateDetails(ctx, msg) + if err != nil { + return nil, err + } + + var minimumTxGasRequired sdk.Gas + + if msg.Contact == dymnstypes.DoNotModifyDesc { + minimumTxGasRequired = 0 + // dymName.Contact remaining unchanged + } else if msg.Contact != "" { + minimumTxGasRequired = dymnstypes.OpGasUpdateContact + dymName.Contact = msg.Contact + } else { + minimumTxGasRequired = 0 + dymName.Contact = "" + } + + shouldClearConfigs := msg.ClearConfigs && len(dymName.Configs) > 0 + + if shouldClearConfigs { + dymName.Configs = nil + + if err := k.BeforeDymNameConfigChanged(ctx, dymName.Name); err != nil { + return nil, err + } + + if err := k.SetDymName(ctx, *dymName); err != nil { + return nil, err + } + + if err := k.AfterDymNameConfigChanged(ctx, dymName.Name); err != nil { + return nil, err + } + } else { + if err := k.SetDymName(ctx, *dymName); err != nil { + return nil, err + } + } + + // charge protocol fee + consumeMinimumGas(ctx, minimumTxGasRequired, "UpdateDetails") + + return &dymnstypes.MsgUpdateDetailsResponse{}, nil +} + +// validateUpdateDetails handles validation for message handled by UpdateDetails +func (k msgServer) validateUpdateDetails(ctx sdk.Context, msg *dymnstypes.MsgUpdateDetails) (*dymnstypes.DymName, error) { + if err := msg.ValidateBasic(); err != nil { + return nil, err + } + + dymName := k.GetDymName(ctx, msg.Name) + if dymName == nil { + return nil, errorsmod.Wrapf(gerrc.ErrNotFound, "Dym-Name: %s", msg.Name) + } + + if dymName.IsExpiredAtCtx(ctx) { + return nil, errorsmod.Wrap(gerrc.ErrUnauthenticated, "Dym-Name is already expired") + } + + if dymName.Controller != msg.Controller { + if dymName.Owner == msg.Controller { + return nil, errorsmod.Wrapf( + gerrc.ErrPermissionDenied, + "please use controller account '%s' to configure", dymName.Controller, + ) + } + + return nil, gerrc.ErrPermissionDenied + } + + if msg.Contact == dymnstypes.DoNotModifyDesc && msg.ClearConfigs && len(dymName.Configs) == 0 { + return nil, errorsmod.Wrap(gerrc.ErrInvalidArgument, "no existing config to clear") + } + + return dymName, nil +} diff --git a/x/dymns/keeper/msg_server_update_details_test.go b/x/dymns/keeper/msg_server_update_details_test.go new file mode 100644 index 000000000..2b0d7d9d3 --- /dev/null +++ b/x/dymns/keeper/msg_server_update_details_test.go @@ -0,0 +1,665 @@ +package keeper_test + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/dymensionxyz/dymension/v3/app/params" + dymnskeeper "github.com/dymensionxyz/dymension/v3/x/dymns/keeper" + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" + "github.com/dymensionxyz/gerr-cosmos/gerrc" +) + +func (s *KeeperTestSuite) Test_msgServer_UpdateDetails() { + ownerAcc := testAddr(1) + ownerA := ownerAcc.bech32() + + controllerAcc := testAddr(2) + controllerA := controllerAcc.bech32() + + anotherAcc := testAddr(3) + + const recordName = "my-name" + + params.SetAddressPrefixes() + + tests := []struct { + name string + dymName *dymnstypes.DymName + msg *dymnstypes.MsgUpdateDetails + preTestFunc func(s *KeeperTestSuite) + wantErr bool + wantErrContains string + wantDymName *dymnstypes.DymName + wantMinGasConsumed sdk.Gas + postTestFunc func(s *KeeperTestSuite) + }{ + { + name: "fail - reject if message not pass validate basic", + dymName: &dymnstypes.DymName{ + Owner: ownerA, + Controller: controllerA, + ExpireAt: s.now.Unix() + 100, + }, + msg: &dymnstypes.MsgUpdateDetails{}, + preTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerA).mappedDymNames(recordName) + s.requireConfiguredAddress(controllerA).notMappedToAnyDymName() + s.requireFallbackAddress(ownerAcc.fallback()).mappedDymNames(recordName) + s.requireFallbackAddress(controllerAcc.fallback()).notMappedToAnyDymName() + }, + wantErr: true, + wantErrContains: gerrc.ErrInvalidArgument.Error(), + wantDymName: &dymnstypes.DymName{ + Owner: ownerA, + Controller: controllerA, + ExpireAt: s.now.Unix() + 100, + }, + postTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerA).mappedDymNames(recordName) + s.requireConfiguredAddress(controllerA).notMappedToAnyDymName() + s.requireFallbackAddress(ownerAcc.fallback()).mappedDymNames(recordName) + s.requireFallbackAddress(controllerAcc.fallback()).notMappedToAnyDymName() + }, + }, + { + name: "fail - Dym-Name does not exists", + dymName: nil, + preTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerA).notMappedToAnyDymName() + s.requireConfiguredAddress(controllerA).notMappedToAnyDymName() + s.requireFallbackAddress(ownerAcc.fallback()).notMappedToAnyDymName() + s.requireFallbackAddress(controllerAcc.fallback()).notMappedToAnyDymName() + }, + msg: &dymnstypes.MsgUpdateDetails{ + Contact: "contact@example.com", + Controller: controllerA, + }, + wantErr: true, + wantErrContains: fmt.Sprintf("Dym-Name: %s: not found", recordName), + wantDymName: nil, + postTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerA).notMappedToAnyDymName() + s.requireConfiguredAddress(controllerA).notMappedToAnyDymName() + s.requireFallbackAddress(ownerAcc.fallback()).notMappedToAnyDymName() + s.requireFallbackAddress(controllerAcc.fallback()).notMappedToAnyDymName() + }, + }, + { + name: "fail - reject if Dym-Name expired", + dymName: &dymnstypes.DymName{ + Owner: ownerA, + Controller: controllerA, + ExpireAt: s.now.Unix() - 1, + }, + preTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerA).notMappedToAnyDymName() + s.requireConfiguredAddress(controllerA).notMappedToAnyDymName() + s.requireFallbackAddress(ownerAcc.fallback()).notMappedToAnyDymName() + s.requireFallbackAddress(controllerAcc.fallback()).notMappedToAnyDymName() + }, + msg: &dymnstypes.MsgUpdateDetails{ + Contact: "contact@example.com", + Controller: controllerA, + }, + wantErr: true, + wantErrContains: "Dym-Name is already expired", + wantDymName: &dymnstypes.DymName{ + Owner: ownerA, + Controller: controllerA, + ExpireAt: s.now.Unix() - 1, + }, + postTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerA).notMappedToAnyDymName() + s.requireConfiguredAddress(controllerA).notMappedToAnyDymName() + s.requireFallbackAddress(ownerAcc.fallback()).notMappedToAnyDymName() + s.requireFallbackAddress(controllerAcc.fallback()).notMappedToAnyDymName() + }, + }, + { + name: "fail - reject if sender is neither owner nor controller", + dymName: &dymnstypes.DymName{ + Owner: ownerA, + Controller: controllerA, + ExpireAt: s.now.Unix() + 100, + }, + preTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerA).mappedDymNames(recordName) + s.requireConfiguredAddress(controllerA).notMappedToAnyDymName() + s.requireFallbackAddress(ownerAcc.fallback()).mappedDymNames(recordName) + s.requireFallbackAddress(controllerAcc.fallback()).notMappedToAnyDymName() + }, + msg: &dymnstypes.MsgUpdateDetails{ + Contact: "contact@example.com", + Controller: anotherAcc.bech32(), + }, + wantErr: true, + wantErrContains: "permission denied", + wantDymName: &dymnstypes.DymName{ + Owner: ownerA, + Controller: controllerA, + ExpireAt: s.now.Unix() + 100, + }, + postTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerA).mappedDymNames(recordName) + s.requireConfiguredAddress(controllerA).notMappedToAnyDymName() + s.requireFallbackAddress(ownerAcc.fallback()).mappedDymNames(recordName) + s.requireFallbackAddress(controllerAcc.fallback()).notMappedToAnyDymName() + }, + }, + { + name: "fail - reject if sender is owner but not controller", + dymName: &dymnstypes.DymName{ + Owner: ownerA, + Controller: controllerA, + ExpireAt: s.now.Unix() + 100, + }, + preTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerA).mappedDymNames(recordName) + s.requireConfiguredAddress(controllerA).notMappedToAnyDymName() + s.requireFallbackAddress(ownerAcc.fallback()).mappedDymNames(recordName) + s.requireFallbackAddress(controllerAcc.fallback()).notMappedToAnyDymName() + }, + msg: &dymnstypes.MsgUpdateDetails{ + Contact: "contact@example.com", + Controller: ownerA, + }, + wantErr: true, + wantErrContains: "please use controller account", + wantDymName: &dymnstypes.DymName{ + Owner: ownerA, + Controller: controllerA, + ExpireAt: s.now.Unix() + 100, + }, + postTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerA).mappedDymNames(recordName) + s.requireConfiguredAddress(controllerA).notMappedToAnyDymName() + s.requireFallbackAddress(ownerAcc.fallback()).mappedDymNames(recordName) + s.requireFallbackAddress(controllerAcc.fallback()).notMappedToAnyDymName() + }, + }, + { + name: "fail - reject if contact is not valid", + dymName: &dymnstypes.DymName{ + Owner: ownerA, + Controller: controllerA, + ExpireAt: s.now.Unix() + 100, + }, + preTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerA).mappedDymNames(recordName) + s.requireConfiguredAddress(controllerA).notMappedToAnyDymName() + s.requireFallbackAddress(ownerAcc.fallback()).mappedDymNames(recordName) + s.requireFallbackAddress(controllerAcc.fallback()).notMappedToAnyDymName() + }, + msg: &dymnstypes.MsgUpdateDetails{ + Contact: "123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901", + Controller: controllerA, + }, + wantErr: true, + wantErrContains: "contact is too long; max length", + wantDymName: &dymnstypes.DymName{ + Owner: ownerA, + Controller: controllerA, + ExpireAt: s.now.Unix() + 100, + }, + postTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerA).mappedDymNames(recordName) + s.requireConfiguredAddress(controllerA).notMappedToAnyDymName() + s.requireFallbackAddress(ownerAcc.fallback()).mappedDymNames(recordName) + s.requireFallbackAddress(controllerAcc.fallback()).notMappedToAnyDymName() + }, + }, + { + name: "pass - can update contact", + dymName: &dymnstypes.DymName{ + Owner: ownerA, + Controller: controllerA, + ExpireAt: s.now.Unix() + 100, + Contact: "old-contact@example.com", + }, + preTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerA).mappedDymNames(recordName) + s.requireConfiguredAddress(controllerA).notMappedToAnyDymName() + s.requireFallbackAddress(ownerAcc.fallback()).mappedDymNames(recordName) + s.requireFallbackAddress(controllerAcc.fallback()).notMappedToAnyDymName() + }, + msg: &dymnstypes.MsgUpdateDetails{ + Contact: "new-contact@example.com", + Controller: controllerA, + }, + wantErr: false, + wantDymName: &dymnstypes.DymName{ + Owner: ownerA, + Controller: controllerA, + ExpireAt: s.now.Unix() + 100, + Contact: "new-contact@example.com", + }, + wantMinGasConsumed: dymnstypes.OpGasUpdateContact, + postTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerA).mappedDymNames(recordName) + s.requireConfiguredAddress(controllerA).notMappedToAnyDymName() + s.requireFallbackAddress(ownerAcc.fallback()).mappedDymNames(recordName) + s.requireFallbackAddress(controllerAcc.fallback()).notMappedToAnyDymName() + }, + }, + { + name: "pass - can remove contact", + dymName: &dymnstypes.DymName{ + Owner: ownerA, + Controller: controllerA, + ExpireAt: s.now.Unix() + 100, + Contact: "old-contact@example.com", + }, + preTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerA).mappedDymNames(recordName) + s.requireConfiguredAddress(controllerA).notMappedToAnyDymName() + s.requireFallbackAddress(ownerAcc.fallback()).mappedDymNames(recordName) + s.requireFallbackAddress(controllerAcc.fallback()).notMappedToAnyDymName() + }, + msg: &dymnstypes.MsgUpdateDetails{ + Contact: "", // clear + Controller: controllerA, + }, + wantErr: false, + wantDymName: &dymnstypes.DymName{ + Owner: ownerA, + Controller: controllerA, + ExpireAt: s.now.Unix() + 100, + Contact: "", // cleared + }, + wantMinGasConsumed: 1, + postTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerA).mappedDymNames(recordName) + s.requireConfiguredAddress(controllerA).notMappedToAnyDymName() + s.requireFallbackAddress(ownerAcc.fallback()).mappedDymNames(recordName) + s.requireFallbackAddress(controllerAcc.fallback()).notMappedToAnyDymName() + }, + }, + { + name: "pass - can remove contact & configs", + dymName: &dymnstypes.DymName{ + Owner: ownerA, + Controller: controllerA, + ExpireAt: s.now.Unix() + 100, + Contact: "contact@example.com", + Configs: []dymnstypes.DymNameConfig{ + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Value: controllerA, + }, + }, + }, + preTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerA).notMappedToAnyDymName() + s.requireConfiguredAddress(controllerA).mappedDymNames(recordName) + s.requireFallbackAddress(ownerAcc.fallback()).notMappedToAnyDymName() + s.requireFallbackAddress(controllerAcc.fallback()).mappedDymNames(recordName) + }, + msg: &dymnstypes.MsgUpdateDetails{ + Contact: "", + ClearConfigs: true, + Controller: controllerA, + }, + wantErr: false, + wantDymName: &dymnstypes.DymName{ + Owner: ownerA, + Controller: controllerA, + ExpireAt: s.now.Unix() + 100, + Contact: "", + }, + wantMinGasConsumed: 1, + postTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerA).mappedDymNames(recordName) + s.requireConfiguredAddress(controllerA).notMappedToAnyDymName() + s.requireFallbackAddress(ownerAcc.fallback()).mappedDymNames(recordName) + s.requireFallbackAddress(controllerAcc.fallback()).notMappedToAnyDymName() + }, + }, + { + name: "pass - can update contact & remove configs", + dymName: &dymnstypes.DymName{ + Owner: ownerA, + Controller: controllerA, + ExpireAt: s.now.Unix() + 100, + Contact: "old-contact@example.com", + Configs: []dymnstypes.DymNameConfig{ + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Value: controllerA, + }, + }, + }, + preTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerA).notMappedToAnyDymName() + s.requireConfiguredAddress(controllerA).mappedDymNames(recordName) + s.requireFallbackAddress(ownerAcc.fallback()).notMappedToAnyDymName() + s.requireFallbackAddress(controllerAcc.fallback()).mappedDymNames(recordName) + }, + msg: &dymnstypes.MsgUpdateDetails{ + Contact: "new-contact@example.com", + ClearConfigs: true, + Controller: controllerA, + }, + wantErr: false, + wantDymName: &dymnstypes.DymName{ + Owner: ownerA, + Controller: controllerA, + ExpireAt: s.now.Unix() + 100, + Contact: "new-contact@example.com", + }, + wantMinGasConsumed: dymnstypes.OpGasUpdateContact, + postTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerA).mappedDymNames(recordName) + s.requireConfiguredAddress(controllerA).notMappedToAnyDymName() + s.requireFallbackAddress(ownerAcc.fallback()).mappedDymNames(recordName) + s.requireFallbackAddress(controllerAcc.fallback()).notMappedToAnyDymName() + }, + }, + { + name: "pass - expiry not changed", + dymName: &dymnstypes.DymName{ + Owner: ownerA, + Controller: controllerA, + ExpireAt: s.now.Unix() + 99, + }, + preTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerA).mappedDymNames(recordName) + s.requireConfiguredAddress(controllerA).notMappedToAnyDymName() + s.requireFallbackAddress(ownerAcc.fallback()).mappedDymNames(recordName) + s.requireFallbackAddress(controllerAcc.fallback()).notMappedToAnyDymName() + }, + msg: &dymnstypes.MsgUpdateDetails{ + Contact: "contact@example.com", + Controller: controllerA, + }, + wantErr: false, + wantDymName: &dymnstypes.DymName{ + Owner: ownerA, + Controller: controllerA, + ExpireAt: s.now.Unix() + 99, + Contact: "contact@example.com", + }, + wantMinGasConsumed: dymnstypes.OpGasUpdateContact, + postTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerA).mappedDymNames(recordName) + s.requireConfiguredAddress(controllerA).notMappedToAnyDymName() + s.requireFallbackAddress(ownerAcc.fallback()).mappedDymNames(recordName) + s.requireFallbackAddress(controllerAcc.fallback()).notMappedToAnyDymName() + }, + }, + { + name: "pass - configs should not be changed when update contact and not clear config", + dymName: &dymnstypes.DymName{ + Owner: ownerA, + Controller: controllerA, + ExpireAt: s.now.Unix() + 100, + Contact: "old-contact@example.com", + Configs: []dymnstypes.DymNameConfig{ + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Value: controllerA, + }, + }, + }, + preTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerA).notMappedToAnyDymName() + s.requireConfiguredAddress(controllerA).mappedDymNames(recordName) + s.requireFallbackAddress(ownerAcc.fallback()).notMappedToAnyDymName() + s.requireFallbackAddress(controllerAcc.fallback()).mappedDymNames(recordName) + }, + msg: &dymnstypes.MsgUpdateDetails{ + Contact: "new-contact@example.com", + ClearConfigs: false, + Controller: controllerA, + }, + wantErr: false, + wantDymName: &dymnstypes.DymName{ + Owner: ownerA, + Controller: controllerA, + ExpireAt: s.now.Unix() + 100, + Contact: "new-contact@example.com", + Configs: []dymnstypes.DymNameConfig{ + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Value: controllerA, + }, + }, + }, + wantMinGasConsumed: dymnstypes.OpGasUpdateContact, + postTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerA).notMappedToAnyDymName() + s.requireConfiguredAddress(controllerA).mappedDymNames(recordName) + s.requireFallbackAddress(ownerAcc.fallback()).notMappedToAnyDymName() + s.requireFallbackAddress(controllerAcc.fallback()).mappedDymNames(recordName) + }, + }, + { + name: "pass - reverse mapping record should be updated accordingly", + dymName: &dymnstypes.DymName{ + Owner: ownerA, + Controller: controllerA, + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{ + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "", + Value: controllerA, + }, + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "nim_1122-1", + Path: "a", + Value: anotherAcc.bech32C("nim"), + }, + }, + }, + preTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerA).notMappedToAnyDymName() + s.requireConfiguredAddress(controllerA).mappedDymNames(recordName) + s.requireConfiguredAddress(anotherAcc.bech32C("nim")).mappedDymNames(recordName) + s.requireFallbackAddress(ownerAcc.fallback()).notMappedToAnyDymName() + s.requireFallbackAddress(controllerAcc.fallback()).mappedDymNames(recordName) + s.requireFallbackAddress(anotherAcc.fallback()).notMappedToAnyDymName() + }, + msg: &dymnstypes.MsgUpdateDetails{ + ClearConfigs: true, + Controller: controllerA, + }, + wantErr: false, + wantDymName: &dymnstypes.DymName{ + Owner: ownerA, + Controller: controllerA, + ExpireAt: s.now.Unix() + 100, + }, + wantMinGasConsumed: 1, + postTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerA).mappedDymNames(recordName) + s.requireConfiguredAddress(controllerA).notMappedToAnyDymName() + s.requireConfiguredAddress(anotherAcc.bech32C("nim")).notMappedToAnyDymName() + s.requireFallbackAddress(ownerAcc.fallback()).mappedDymNames(recordName) + s.requireFallbackAddress(controllerAcc.fallback()).notMappedToAnyDymName() + s.requireFallbackAddress(anotherAcc.fallback()).notMappedToAnyDymName() + }, + }, + { + name: "pass - when contact is [do-not-modify], do not update contact", + dymName: &dymnstypes.DymName{ + Owner: ownerA, + Controller: controllerA, + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{ + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "", + Value: controllerA, + }, + }, + Contact: "contact@example.com", + }, + preTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerA).notMappedToAnyDymName() + s.requireConfiguredAddress(controllerA).mappedDymNames(recordName) + s.requireFallbackAddress(ownerAcc.fallback()).notMappedToAnyDymName() + s.requireFallbackAddress(controllerAcc.fallback()).mappedDymNames(recordName) + }, + msg: &dymnstypes.MsgUpdateDetails{ + Contact: dymnstypes.DoNotModifyDesc, + ClearConfigs: true, + Controller: controllerA, + }, + wantErr: false, + wantDymName: &dymnstypes.DymName{ + Owner: ownerA, + Controller: controllerA, + ExpireAt: s.now.Unix() + 100, + Contact: "contact@example.com", // keep + }, + wantMinGasConsumed: 1, + postTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerA).mappedDymNames(recordName) + s.requireConfiguredAddress(controllerA).notMappedToAnyDymName() + s.requireFallbackAddress(ownerAcc.fallback()).mappedDymNames(recordName) + s.requireFallbackAddress(controllerAcc.fallback()).notMappedToAnyDymName() + }, + }, + { + name: "fail - reject message that neither update contact nor clear config", + dymName: &dymnstypes.DymName{ + Owner: ownerA, + Controller: controllerA, + ExpireAt: s.now.Unix() + 100, + Contact: "contact@example.com", + }, + preTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerA).mappedDymNames(recordName) + s.requireConfiguredAddress(controllerA).notMappedToAnyDymName() + s.requireFallbackAddress(ownerAcc.fallback()).mappedDymNames(recordName) + s.requireFallbackAddress(controllerAcc.fallback()).notMappedToAnyDymName() + }, + msg: &dymnstypes.MsgUpdateDetails{ + Contact: dymnstypes.DoNotModifyDesc, + ClearConfigs: false, + Controller: controllerA, + }, + wantErr: true, + wantErrContains: "message neither clears configs nor updates contact information", + wantDymName: &dymnstypes.DymName{ + Owner: ownerA, + Controller: controllerA, + ExpireAt: s.now.Unix() + 100, + Contact: "contact@example.com", + }, + wantMinGasConsumed: 1, + postTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerA).mappedDymNames(recordName) + s.requireConfiguredAddress(controllerA).notMappedToAnyDymName() + s.requireFallbackAddress(ownerAcc.fallback()).mappedDymNames(recordName) + s.requireFallbackAddress(controllerAcc.fallback()).notMappedToAnyDymName() + }, + }, + { + name: "fail - reject message that not update contact and clear config but no config to clear", + dymName: &dymnstypes.DymName{ + Owner: ownerA, + Controller: controllerA, + ExpireAt: s.now.Unix() + 100, + Contact: "contact@example.com", + }, + preTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerA).mappedDymNames(recordName) + s.requireConfiguredAddress(controllerA).notMappedToAnyDymName() + s.requireFallbackAddress(ownerAcc.fallback()).mappedDymNames(recordName) + s.requireFallbackAddress(controllerAcc.fallback()).notMappedToAnyDymName() + }, + msg: &dymnstypes.MsgUpdateDetails{ + Contact: dymnstypes.DoNotModifyDesc, + ClearConfigs: true, + Controller: controllerA, + }, + wantErr: true, + wantErrContains: "no existing config to clear", + wantDymName: &dymnstypes.DymName{ + Owner: ownerA, + Controller: controllerA, + ExpireAt: s.now.Unix() + 100, + Contact: "contact@example.com", + }, + wantMinGasConsumed: 1, + postTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerA).mappedDymNames(recordName) + s.requireConfiguredAddress(controllerA).notMappedToAnyDymName() + s.requireFallbackAddress(ownerAcc.fallback()).mappedDymNames(recordName) + s.requireFallbackAddress(controllerAcc.fallback()).notMappedToAnyDymName() + }, + }, + } + for _, tt := range tests { + s.Run(tt.name, func() { + s.Require().NotNil(tt.preTestFunc) + s.Require().NotNil(tt.postTestFunc) + + s.RefreshContext() + + if tt.dymName != nil { + if tt.dymName.Name == "" { + tt.dymName.Name = recordName + } + err := s.dymNsKeeper.SetDymName(s.ctx, *tt.dymName) + s.Require().NoError(err) + s.Require().NoError(s.dymNsKeeper.AfterDymNameOwnerChanged(s.ctx, tt.dymName.Name)) + s.Require().NoError(s.dymNsKeeper.AfterDymNameConfigChanged(s.ctx, tt.dymName.Name)) + } + if tt.wantDymName != nil && tt.wantDymName.Name == "" { + tt.wantDymName.Name = recordName + } + + tt.preTestFunc(s) + + tt.msg.Name = recordName + resp, err := dymnskeeper.NewMsgServerImpl(s.dymNsKeeper).UpdateDetails(s.ctx, tt.msg) + laterDymName := s.dymNsKeeper.GetDymName(s.ctx, tt.msg.Name) + + defer func() { + if tt.wantMinGasConsumed > 0 { + s.Require().GreaterOrEqual( + s.ctx.GasMeter().GasConsumed(), tt.wantMinGasConsumed, + "should consume at least %d gas", tt.wantMinGasConsumed, + ) + } + + if !s.T().Failed() { + tt.postTestFunc(s) + } + }() + + if tt.wantErr { + s.Require().NotEmpty(tt.wantErrContains, "mis-configured test case") + s.Require().Error(err) + s.Require().Contains(err.Error(), tt.wantErrContains) + s.Require().Nil(resp) + + if tt.wantDymName != nil { + s.Require().Equal(*tt.wantDymName, *laterDymName) + + owned, err := s.dymNsKeeper.GetDymNamesOwnedBy(s.ctx, laterDymName.Owner) + s.Require().NoError(err) + if laterDymName.ExpireAt >= s.now.Unix() { + s.Require().Len(owned, 1) + } else { + s.Require().Empty(owned) + } + } else { + s.Require().Nil(laterDymName) + } + return + } + + s.Require().NoError(err) + s.Require().NotNil(resp) + s.Require().NotNil(laterDymName) + s.Require().Equal(*tt.wantDymName, *laterDymName) + }) + } +} diff --git a/x/dymns/keeper/msg_server_update_params.go b/x/dymns/keeper/msg_server_update_params.go new file mode 100644 index 000000000..162203afd --- /dev/null +++ b/x/dymns/keeper/msg_server_update_params.go @@ -0,0 +1,44 @@ +package keeper + +import ( + "context" + + errorsmod "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" + "github.com/dymensionxyz/gerr-cosmos/gerrc" +) + +func (k msgServer) UpdateParams(goCtx context.Context, msg *dymnstypes.MsgUpdateParams) (*dymnstypes.MsgUpdateParamsResponse, error) { + err := msg.ValidateBasic() + if err != nil { + return nil, err + } + + ctx := sdk.UnwrapSDKContext(goCtx) + + if msg.Authority != k.authority { + return nil, errorsmod.Wrap(gerrc.ErrUnauthenticated, "only the gov module can update params") + } + + moduleParams := k.GetParams(ctx) + + if msg.NewPriceParams != nil { + moduleParams.Price = *msg.NewPriceParams + } + + if msg.NewChainsParams != nil { + moduleParams.Chains = *msg.NewChainsParams + } + + if msg.NewMiscParams != nil { + moduleParams.Misc = *msg.NewMiscParams + } + + err = k.SetParams(ctx, moduleParams) + if err != nil { + return nil, err + } + + return &dymnstypes.MsgUpdateParamsResponse{}, nil +} diff --git a/x/dymns/keeper/msg_server_update_params_test.go b/x/dymns/keeper/msg_server_update_params_test.go new file mode 100644 index 000000000..f1a20fffc --- /dev/null +++ b/x/dymns/keeper/msg_server_update_params_test.go @@ -0,0 +1,167 @@ +package keeper_test + +import ( + "reflect" + + sdkmath "cosmossdk.io/math" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + dymnskeeper "github.com/dymensionxyz/dymension/v3/x/dymns/keeper" + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" +) + +func (s *KeeperTestSuite) Test_msgServer_UpdateParams() { + govModuleAccount := authtypes.NewModuleAddress(govtypes.ModuleName).String() + + updatedPriceParams := func() dymnstypes.PriceParams { + updated := dymnstypes.DefaultPriceParams() + updated.AliasPriceSteps = append([]sdkmath.Int{ + updated.AliasPriceSteps[0].AddRaw(1), + }, updated.AliasPriceSteps...) + return updated + }() + updatedChainsParams := func() dymnstypes.ChainsParams { + updated := dymnstypes.DefaultChainsParams() + updated.AliasesOfChainIds = append(updated.AliasesOfChainIds, dymnstypes.AliasesOfChainId{ + ChainId: "pseudo_1-1", + Aliases: []string{"pseudo"}, + }) + return updated + }() + updatedMiscParams := func() dymnstypes.MiscParams { + updated := dymnstypes.DefaultMiscParams() + updated.GracePeriodDuration = updated.GracePeriodDuration * 2 + return updated + }() + + tests := []struct { + name string + msg *dymnstypes.MsgUpdateParams + wantErr bool + wantErrContains string + }{ + { + name: "pass - can update", + msg: &dymnstypes.MsgUpdateParams{ + Authority: govModuleAccount, + NewPriceParams: &updatedPriceParams, + NewChainsParams: &updatedChainsParams, + NewMiscParams: &updatedMiscParams, + }, + wantErr: false, + }, + { + name: "fail - reject if not from gov module", + msg: &dymnstypes.MsgUpdateParams{ + Authority: dymNsModuleAccAddr.String(), + NewPriceParams: nil, + NewChainsParams: &dymnstypes.ChainsParams{ + AliasesOfChainIds: []dymnstypes.AliasesOfChainId{ + { + ChainId: "pseudo_1-1", + Aliases: []string{"pseudo"}, + }, + }, + }, + NewMiscParams: nil, + }, + wantErr: true, + wantErrContains: "only the gov module can update params", + }, + { + name: "pass - can update price params", + msg: &dymnstypes.MsgUpdateParams{ + Authority: govModuleAccount, + NewPriceParams: &updatedPriceParams, + NewChainsParams: nil, + NewMiscParams: nil, + }, + wantErr: false, + }, + { + name: "pass - can update chains params", + msg: &dymnstypes.MsgUpdateParams{ + Authority: govModuleAccount, + NewPriceParams: nil, + NewChainsParams: &updatedChainsParams, + NewMiscParams: nil, + }, + wantErr: false, + }, + { + name: "pass - can update price params", + msg: &dymnstypes.MsgUpdateParams{ + Authority: govModuleAccount, + NewPriceParams: nil, + NewChainsParams: nil, + NewMiscParams: &updatedMiscParams, + }, + wantErr: false, + }, + { + name: "fail - can not update if all params are nil", + msg: &dymnstypes.MsgUpdateParams{ + Authority: govModuleAccount, + NewPriceParams: nil, + NewChainsParams: nil, + NewMiscParams: nil, + }, + wantErr: true, + wantErrContains: "at least one of the new params must be provided", + }, + { + name: "fail - can not update if any params is invalid", + msg: &dymnstypes.MsgUpdateParams{ + Authority: govModuleAccount, + NewPriceParams: &dymnstypes.PriceParams{}, // invalid + NewChainsParams: &updatedChainsParams, + NewMiscParams: &updatedMiscParams, + }, + wantErr: true, + wantErrContains: "invalid argument", + }, + } + for _, tt := range tests { + s.Run(tt.name, func() { + s.RefreshContext() + + // use default params + s.updateModuleParams(func(_ dymnstypes.Params) dymnstypes.Params { + return dymnstypes.DefaultParams() + }) + + _, err := dymnskeeper.NewMsgServerImpl(s.dymNsKeeper).UpdateParams(s.ctx, tt.msg) + + laterModuleParams := s.dymNsKeeper.GetParams(s.ctx) + + if tt.wantErr { + s.Require().ErrorContains(err, tt.wantErrContains) + s.Require().True(reflect.DeepEqual(dymnstypes.DefaultParams(), laterModuleParams), "params should not be updated") + return + } + + s.Require().NoError(err) + + expectNewModuleParams := dymnstypes.DefaultParams() + if tt.msg.NewPriceParams != nil { + expectNewModuleParams.Price = *tt.msg.NewPriceParams + } + if tt.msg.NewChainsParams != nil { + expectNewModuleParams.Chains = *tt.msg.NewChainsParams + } + if tt.msg.NewMiscParams != nil { + expectNewModuleParams.Misc = *tt.msg.NewMiscParams + } + s.Require().Falsef( + reflect.DeepEqual(dymnstypes.DefaultParams(), expectNewModuleParams), + "bad setup testcase, must provide altered params to test", + ) + s.Require().Truef( + reflect.DeepEqual(expectNewModuleParams, laterModuleParams), + `params should be updated +want: %v +got: %v`, expectNewModuleParams, laterModuleParams, + ) + }) + } +} diff --git a/x/dymns/keeper/msg_server_update_resolve_address.go b/x/dymns/keeper/msg_server_update_resolve_address.go new file mode 100644 index 000000000..d0078a8f4 --- /dev/null +++ b/x/dymns/keeper/msg_server_update_resolve_address.go @@ -0,0 +1,161 @@ +package keeper + +import ( + "context" + "strings" + + errorsmod "cosmossdk.io/errors" + "github.com/dymensionxyz/gerr-cosmos/gerrc" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/bech32" + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" + dymnsutils "github.com/dymensionxyz/dymension/v3/x/dymns/utils" +) + +// UpdateResolveAddress is message handler, +// handles updating Dym-Name-Address resolution configuration, performed by the controller. +func (k msgServer) UpdateResolveAddress(goCtx context.Context, msg *dymnstypes.MsgUpdateResolveAddress) (*dymnstypes.MsgUpdateResolveAddressResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + dymName, err := k.validateUpdateResolveAddress(ctx, msg) + if err != nil { + return nil, err + } + + _, newConfig := msg.GetDymNameConfig() + if newConfig.ChainId == ctx.ChainID() { + newConfig.ChainId = "" + } + newConfigIdentity := newConfig.GetIdentity() + + if newConfig.ChainId == "" || k.IsRollAppId(ctx, newConfig.ChainId) { + // guarantee of case-insensitive on host and RollApps, + // so we do normalize input + newConfig.Value = strings.ToLower(newConfig.Value) + } else if dymnsutils.IsValidHexAddress(newConfig.Value) { + // if the address is hex format, then treat the chain is case-insensitive address, + // like Ethereum, where the address is case-insensitive and checksum address contains mixed case + newConfig.Value = strings.ToLower(newConfig.Value) + } + + var minimumTxGasRequired sdk.Gas + + existingConfigCount := len(dymName.Configs) + if newConfig.IsDelete() { + minimumTxGasRequired = 0 // do not charge for delete + + foundSameConfigIdAtIdx := -1 + for i, config := range dymName.Configs { + if config.GetIdentity() == newConfigIdentity { + foundSameConfigIdAtIdx = i + break + } + } + + if foundSameConfigIdAtIdx < 0 { + // no-config case also falls into this branch + + // do nothing + } else { + dymName.Configs = append( + dymName.Configs[:foundSameConfigIdAtIdx], + dymName.Configs[foundSameConfigIdAtIdx+1:]..., + ) + } + } else { + minimumTxGasRequired = dymnstypes.OpGasConfig + + if existingConfigCount > 0 { + var foundSameConfigId bool + for i, config := range dymName.Configs { + if config.GetIdentity() == newConfigIdentity { + dymName.Configs[i] = newConfig + foundSameConfigId = true + break + } + } + if !foundSameConfigId { + dymName.Configs = append(dymName.Configs, newConfig) + } + } else { + dymName.Configs = []dymnstypes.DymNameConfig{newConfig} + } + } + + if err := k.BeforeDymNameConfigChanged(ctx, dymName.Name); err != nil { + return nil, err + } + + if err := k.SetDymName(ctx, *dymName); err != nil { + return nil, err + } + + if err := k.AfterDymNameConfigChanged(ctx, dymName.Name); err != nil { + return nil, err + } + + // Charge protocol fee. + // The protocol fee mechanism is used to prevent spamming to the network. + consumeMinimumGas(ctx, minimumTxGasRequired, "UpdateResolveAddress") + + return &dymnstypes.MsgUpdateResolveAddressResponse{}, nil +} + +// validateUpdateResolveAddress handles validation for message handled by UpdateResolveAddress +func (k msgServer) validateUpdateResolveAddress(ctx sdk.Context, msg *dymnstypes.MsgUpdateResolveAddress) (*dymnstypes.DymName, error) { + if err := msg.ValidateBasic(); err != nil { + return nil, err + } + + dymName := k.GetDymName(ctx, msg.Name) + if dymName == nil { + return nil, errorsmod.Wrapf(gerrc.ErrNotFound, "Dym-Name: %s", msg.Name) + } + + if dymName.IsExpiredAtCtx(ctx) { + return nil, errorsmod.Wrap(gerrc.ErrUnauthenticated, "Dym-Name is already expired") + } + + if dymName.Controller != msg.Controller { + if dymName.Owner == msg.Controller { + return nil, errorsmod.Wrapf(gerrc.ErrPermissionDenied, + "please use controller account '%s' to configure", dymName.Controller, + ) + } + + return nil, gerrc.ErrPermissionDenied + } + + if msg.ResolveTo != "" { + if msg.ChainId == "" || msg.ChainId == ctx.ChainID() { + if !dymnsutils.IsValidBech32AccountAddress(msg.ResolveTo, true) { + return nil, errorsmod.Wrap( + gerrc.ErrInvalidArgument, + "resolve address must be a valid bech32 account address on host chain", + ) + } + } else if k.IsRollAppId(ctx, msg.ChainId) { + if !dymnsutils.IsValidBech32AccountAddress(msg.ResolveTo, false) { + return nil, errorsmod.Wrap( + gerrc.ErrInvalidArgument, + "resolve address must be a valid bech32 account address on RollApp", + ) + } + if bech32Prefix, found := k.GetRollAppBech32Prefix(ctx, msg.ChainId); found { + hrp, _, err := bech32.DecodeAndConvert(msg.ResolveTo) + if err != nil { + panic("unreachable") + } + if hrp != bech32Prefix { + return nil, errorsmod.Wrapf( + gerrc.ErrInvalidArgument, + "resolve address must be a valid bech32 account address on RollApps: %s", bech32Prefix, + ) + } + } + } + } + + return dymName, nil +} diff --git a/x/dymns/keeper/msg_server_update_resolve_address_test.go b/x/dymns/keeper/msg_server_update_resolve_address_test.go new file mode 100644 index 000000000..1a4b74a81 --- /dev/null +++ b/x/dymns/keeper/msg_server_update_resolve_address_test.go @@ -0,0 +1,2029 @@ +package keeper_test + +import ( + cryptorand "crypto/rand" + "fmt" + "math" + "math/big" + "strings" + "time" + + "github.com/dymensionxyz/gerr-cosmos/gerrc" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/dymensionxyz/dymension/v3/app/params" + dymnskeeper "github.com/dymensionxyz/dymension/v3/x/dymns/keeper" + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" + dymnsutils "github.com/dymensionxyz/dymension/v3/x/dymns/utils" + rollapptypes "github.com/dymensionxyz/dymension/v3/x/rollapp/types" +) + +//goland:noinspection SpellCheckingInspection +var nonHostChainBech32InputSet = []string{ + "dym1fl48vsnmsdzcv8", // host-chain prefix but invalid bech32 format + "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38xuuuu", // host-chain prefix but invalid bech32 checksum + testAddr(func() uint64 { + n, _ := cryptorand.Int(cryptorand.Reader, big.NewInt(math.MaxInt64)) + return n.Uint64() + }() + 9471274174).bech32C("another"), + "4BDtRc8Ym9wGFyEBzDWMSZ7iuUcNJ1ssiRkU6LjQgHURD4PGAMsZnzxAz2SGmNhinLxPF111N41bTHQBiu6QTmaZwKngDWrH", + "t1Rv4exT7bqhZqi2j7xz8bUHDMxwosrjADU", + "zs1z7rejlpsa98s2rrrfkwmaxu53e4ue0ulcrw0h4x5g8jl04tak0d3mm47vdtahatqrlkngh9sly", + "zcU1Cd6zYyZCd2VJF8yKgmzjxdiiU1rgTTjEwoN1CGUWCziPkUTXUjXmX7TMqdMNsTfuiGN1jQoVN4kGxUR4sAPN4XZ7pxb", + "XpLM8qBMd7CqukVzKXkQWuQJmgrAFb87Qr", + "0x7f533b5fbf6ef86c3b7df76cc27fc67744a9a760", + "2UEQTE5QDNXPI7M3TU44G6SYKLFWLPQO7EBZM7K7MHMQQMFI4QJPLHQFHM", + "ALGO-2UEQTE5QDNXPI7M3TU44G6SYKLFWLPQO7EBZM7K7MHMQQMFI4QJPLHQFHM", + "0.0.123", + "0.0.0", + "0.0.123-vfmkw", + "LMHEFMwRsQ3nHDfb9zZqynLHxjuJ2hgyyW", + "MC2JYMPVWaxqUb9qUkUbjtUwoNMo1tPaLF", + "ltc1qhzjptwpym9afcdjhs7jcz6fd0jma0l0rc0e5yr", + "ltc1qzvcgmntglcuv4smv3lzj6k8szcvsrmvk0phrr9wfq8w493r096ssm2fgsw", + "qrvax3jgtwqssnkpctlqdl0rq7rjn0l0hgny8pt0hp", + "bitcoincash:qrvax3jgtwqssnkpctlqdl0rq7rjn0l0hgny8pt0hp", + "D7wbmbjBWG5HPkT6d4gh6SdQPp6z25vcF2", + "0xBe588061d20fe359E69D78824EC45EA98C87069A", + "NVeu7XqbZ6WiL1prhChC1jMWgicuWtneDP", + "ALuhj3QNoxvAnMZsA2oKP5UxYsBmRwjwHL", + "tz1YWK1gDPQx9N1Jh4JnmVre7xN6xhGGM4uC", + "tz3T8djchG5FDwt7H6wEUU3sRFJwonYPqMJe", + "KT1S5hgipNSTFehZo7v81gq6fcLChbRwptqy", + "rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz", + "XV5sbjUmgPpvXv4ixFWZ5ptAYZ6PD28Sq49uo34VyjnmK5H", + "7EcDhSYGxXyscszYEp35KHN8vvw3svAuLKTzXwCFLtV", + "414450cf8c8b6a8229b7f628e36b3a658e84441b6f", + "TGCRkw1Vq759FBCrwxkZGgqZbRX1WkBHSu", + "xdc64b3b0a417775cfb441ed064611bf79826649c0f", + "0x64b3b0a417775cfb441ed064611bf79826649c0f", + "GBH4TZYZ4IRCPO44CBOLFUHULU2WGALXTAVESQA6432MBJMABBB4GIYI", + "jed*stellar.org", + "maria@gmail.com*stellar.org", + "bc1qeklep85ntjz4605drds6aww9u0qr46qzrv5xswd35uhjuj8ahfcqgf6hak", + "bc1pxwww0ct9ue7e8tdnlmug5m2tamfn7q06sahstg39ys4c9f3340qqxrdu9k", + "bc1prwgcpptoxrpfl5go81wpd5qlsig5yt4g7urb45e", + "bc1qwqdg6squsna38e46795at95yu9atm8azzmyvckulcc7kytlcckxswvvzej", + "0x3cA8ac240F6ebeA8684b3E629A8e8C1f0E3bC0Ff", + "X-avax1tzdcgj4ehsvhhgpl7zylwpw0gl2rxcg4r5afk5", + "Ae2tdPwUPEZFSi1cTyL1ZL6bgixhc2vSy5heg6Zg9uP7PpumkAJ82Qprt8b", + "DdzFFzCqrhsfZHjaBunVySZBU8i9Zom7Gujham6Jz8scCcAdkDmEbD9XSdXKdBiPoa1fjgL4ksGjQXD8ZkSNHGJfT25ieA9rWNCSA5qc", + "addr1q8gg2r3vf9zggn48g7m8vx62rwf6warcs4k7ej8mdzmqmesj30jz7psduyk6n4n2qrud2xlv9fgj53n6ds3t8cs4fvzs05yzmz", + "1a1LcBX6hGPKg5aQ6DXZpAHCCzWjckhea4sz3P1PvL3oc4F", + "HNZata7iMYWmk5RvZRTiAsSDhV8366zq2YGb3tLH5Upf74F", + "5CdiCGvTEuzut954STAXRfL8Lazs3KCZa5LPpkPeqqJXdTHp", + "0x192c3c7e5789b461fbf1c7f614ba5eed0b22efc507cda60a5e7fda8e046bcdce", + "0x0380d46a00e427d89f35d78b4eacb4270bd5ecfd10b64662dcfe31eb117fc62c68", + "04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f", + "11111111111111111111BZbvjr", + "1111111111111111111114oLvT2", + "12higDjoCCNXSA95xZMWUdPvXNmkAduhWv", + "342ftSRCvFHfCeFFBuz4xwbeqnDw6BGUey", + "bc1q34aq5drpuwy3wgl9lhup9892qp6svr8ldzyy7c", +} + +var nonBech32InputSet []string + +func init() { + for _, input := range nonHostChainBech32InputSet { + if !dymnsutils.IsValidBech32AccountAddress(input, false) { + nonBech32InputSet = append(nonBech32InputSet, input) + } + } +} + +func (s *KeeperTestSuite) Test_msgServer_UpdateResolveAddress() { + ownerAcc := testAddr(1) + controllerAcc := testAddr(2) + anotherAcc := testAddr(14) + _32BytesAcc := testAddr(15) + + const recordName = "my-name" + + const rollAppId = "ra_9999-1" + + //goland:noinspection SpellCheckingInspection + nonBech32NonHexUpperCaseA := strings.ToUpper("X-avax1tzdcgj4ehsvhhgpl7zylwpw0gl2rxcg4r5afk5") + + params.SetAddressPrefixes() + + tests := []struct { + name string + dymName *dymnstypes.DymName + msg *dymnstypes.MsgUpdateResolveAddress + preTestFunc func(s *KeeperTestSuite) + wantErr bool + wantErrContains string + wantDymName *dymnstypes.DymName + wantMinGasConsumed sdk.Gas + postTestFunc func(s *KeeperTestSuite) + }{ + { + name: "fail - reject if message not pass validate basic", + dymName: &dymnstypes.DymName{ + Owner: ownerAcc.bech32(), + Controller: controllerAcc.bech32(), + ExpireAt: s.now.Unix() + 100, + }, + msg: &dymnstypes.MsgUpdateResolveAddress{}, + preTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerAcc.bech32()).mappedDymNames(recordName) + s.requireConfiguredAddress(controllerAcc.bech32()).notMappedToAnyDymName() + s.requireFallbackAddress(ownerAcc.fallback()).mappedDymNames(recordName) + s.requireFallbackAddress(controllerAcc.fallback()).notMappedToAnyDymName() + }, + wantErr: true, + wantErrContains: gerrc.ErrInvalidArgument.Error(), + wantDymName: &dymnstypes.DymName{ + Owner: ownerAcc.bech32(), + Controller: controllerAcc.bech32(), + ExpireAt: s.now.Unix() + 100, + }, + postTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerAcc.bech32()).mappedDymNames(recordName) + s.requireConfiguredAddress(controllerAcc.bech32()).notMappedToAnyDymName() + s.requireFallbackAddress(ownerAcc.fallback()).mappedDymNames(recordName) + s.requireFallbackAddress(controllerAcc.fallback()).notMappedToAnyDymName() + }, + }, + { + name: "fail - Dym-Name does not exists", + dymName: nil, + preTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerAcc.bech32()).notMappedToAnyDymName() + s.requireConfiguredAddress(controllerAcc.bech32()).notMappedToAnyDymName() + s.requireFallbackAddress(ownerAcc.fallback()).notMappedToAnyDymName() + s.requireFallbackAddress(controllerAcc.fallback()).notMappedToAnyDymName() + }, + msg: &dymnstypes.MsgUpdateResolveAddress{ + ResolveTo: ownerAcc.bech32(), + Controller: controllerAcc.bech32(), + }, + wantErr: true, + wantErrContains: fmt.Sprintf("Dym-Name: %s: not found", recordName), + wantDymName: nil, + postTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerAcc.bech32()).notMappedToAnyDymName() + s.requireConfiguredAddress(controllerAcc.bech32()).notMappedToAnyDymName() + s.requireFallbackAddress(ownerAcc.fallback()).notMappedToAnyDymName() + s.requireFallbackAddress(controllerAcc.fallback()).notMappedToAnyDymName() + }, + }, + { + name: "fail - reject if Dym-Name expired", + dymName: &dymnstypes.DymName{ + Owner: ownerAcc.bech32(), + Controller: controllerAcc.bech32(), + ExpireAt: s.now.Unix() - 1, + }, + preTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerAcc.bech32()).notMappedToAnyDymName() + s.requireConfiguredAddress(controllerAcc.bech32()).notMappedToAnyDymName() + s.requireFallbackAddress(ownerAcc.fallback()).notMappedToAnyDymName() + s.requireFallbackAddress(controllerAcc.fallback()).notMappedToAnyDymName() + }, + msg: &dymnstypes.MsgUpdateResolveAddress{ + ResolveTo: ownerAcc.bech32(), + Controller: controllerAcc.bech32(), + }, + wantErr: true, + wantErrContains: "Dym-Name is already expired", + wantDymName: &dymnstypes.DymName{ + Owner: ownerAcc.bech32(), + Controller: controllerAcc.bech32(), + ExpireAt: s.now.Unix() - 1, + }, + postTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerAcc.bech32()).notMappedToAnyDymName() + s.requireConfiguredAddress(controllerAcc.bech32()).notMappedToAnyDymName() + s.requireFallbackAddress(ownerAcc.fallback()).notMappedToAnyDymName() + s.requireFallbackAddress(controllerAcc.fallback()).notMappedToAnyDymName() + }, + }, + { + name: "fail - reject if sender is neither owner nor controller", + dymName: &dymnstypes.DymName{ + Owner: ownerAcc.bech32(), + Controller: controllerAcc.bech32(), + ExpireAt: s.now.Unix() + 100, + }, + preTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerAcc.bech32()).mappedDymNames(recordName) + s.requireConfiguredAddress(controllerAcc.bech32()).notMappedToAnyDymName() + s.requireFallbackAddress(ownerAcc.fallback()).mappedDymNames(recordName) + s.requireFallbackAddress(controllerAcc.fallback()).notMappedToAnyDymName() + }, + msg: &dymnstypes.MsgUpdateResolveAddress{ + ResolveTo: ownerAcc.bech32(), + Controller: anotherAcc.bech32(), + }, + wantErr: true, + wantErrContains: "permission denied", + wantDymName: &dymnstypes.DymName{ + Owner: ownerAcc.bech32(), + Controller: controllerAcc.bech32(), + ExpireAt: s.now.Unix() + 100, + }, + postTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerAcc.bech32()).mappedDymNames(recordName) + s.requireConfiguredAddress(controllerAcc.bech32()).notMappedToAnyDymName() + s.requireFallbackAddress(ownerAcc.fallback()).mappedDymNames(recordName) + s.requireFallbackAddress(controllerAcc.fallback()).notMappedToAnyDymName() + }, + }, + { + name: "fail - reject if sender is owner but not controller", + dymName: &dymnstypes.DymName{ + Owner: ownerAcc.bech32(), + Controller: controllerAcc.bech32(), + ExpireAt: s.now.Unix() + 100, + }, + preTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerAcc.bech32()).mappedDymNames(recordName) + s.requireConfiguredAddress(controllerAcc.bech32()).notMappedToAnyDymName() + s.requireFallbackAddress(ownerAcc.fallback()).mappedDymNames(recordName) + s.requireFallbackAddress(controllerAcc.fallback()).notMappedToAnyDymName() + }, + msg: &dymnstypes.MsgUpdateResolveAddress{ + ResolveTo: ownerAcc.bech32(), + Controller: ownerAcc.bech32(), + }, + wantErr: true, + wantErrContains: "please use controller account", + wantDymName: &dymnstypes.DymName{ + Owner: ownerAcc.bech32(), + Controller: controllerAcc.bech32(), + ExpireAt: s.now.Unix() + 100, + }, + postTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerAcc.bech32()).mappedDymNames(recordName) + s.requireConfiguredAddress(controllerAcc.bech32()).notMappedToAnyDymName() + s.requireFallbackAddress(ownerAcc.fallback()).mappedDymNames(recordName) + s.requireFallbackAddress(controllerAcc.fallback()).notMappedToAnyDymName() + }, + }, + { + name: "fail - reject if config is not valid", + dymName: &dymnstypes.DymName{ + Owner: ownerAcc.bech32(), + Controller: controllerAcc.bech32(), + ExpireAt: s.now.Unix() + 100, + }, + preTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerAcc.bech32()).mappedDymNames(recordName) + s.requireConfiguredAddress(controllerAcc.bech32()).notMappedToAnyDymName() + s.requireFallbackAddress(ownerAcc.fallback()).mappedDymNames(recordName) + s.requireFallbackAddress(controllerAcc.fallback()).notMappedToAnyDymName() + }, + msg: &dymnstypes.MsgUpdateResolveAddress{ + ResolveTo: "0x1", + Controller: controllerAcc.bech32(), + }, + wantErr: true, + wantErrContains: "config is invalid:", + wantDymName: &dymnstypes.DymName{ + Owner: ownerAcc.bech32(), + Controller: controllerAcc.bech32(), + ExpireAt: s.now.Unix() + 100, + }, + postTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerAcc.bech32()).mappedDymNames(recordName) + s.requireConfiguredAddress(controllerAcc.bech32()).notMappedToAnyDymName() + s.requireFallbackAddress(ownerAcc.fallback()).mappedDymNames(recordName) + s.requireFallbackAddress(controllerAcc.fallback()).notMappedToAnyDymName() + }, + }, + { + name: "fail - reject if config is not valid. Only accept lowercase", + dymName: &dymnstypes.DymName{ + Owner: ownerAcc.bech32(), + Controller: controllerAcc.bech32(), + ExpireAt: s.now.Unix() + 100, + }, + preTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerAcc.bech32()).mappedDymNames(recordName) + s.requireConfiguredAddress(controllerAcc.bech32()).notMappedToAnyDymName() + s.requireFallbackAddress(ownerAcc.fallback()).mappedDymNames(recordName) + s.requireFallbackAddress(controllerAcc.fallback()).notMappedToAnyDymName() + }, + msg: &dymnstypes.MsgUpdateResolveAddress{ + SubName: "SUB", // upper-case is not accepted + ResolveTo: ownerAcc.bech32(), + Controller: controllerAcc.bech32(), + }, + wantErr: true, + wantErrContains: "config is invalid:", + wantDymName: &dymnstypes.DymName{ + Owner: ownerAcc.bech32(), + Controller: controllerAcc.bech32(), + ExpireAt: s.now.Unix() + 100, + }, + postTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerAcc.bech32()).mappedDymNames(recordName) + s.requireConfiguredAddress(controllerAcc.bech32()).notMappedToAnyDymName() + s.requireFallbackAddress(ownerAcc.fallback()).mappedDymNames(recordName) + s.requireFallbackAddress(controllerAcc.fallback()).notMappedToAnyDymName() + }, + }, + { + name: "pass - can update", + dymName: &dymnstypes.DymName{ + Owner: ownerAcc.bech32(), + Controller: controllerAcc.bech32(), + ExpireAt: s.now.Unix() + 100, + }, + preTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerAcc.bech32()).mappedDymNames(recordName) + s.requireConfiguredAddress(controllerAcc.bech32()).notMappedToAnyDymName() + s.requireFallbackAddress(ownerAcc.fallback()).mappedDymNames(recordName) + s.requireFallbackAddress(controllerAcc.fallback()).notMappedToAnyDymName() + }, + msg: &dymnstypes.MsgUpdateResolveAddress{ + ResolveTo: ownerAcc.bech32(), + Controller: controllerAcc.bech32(), + }, + wantErr: false, + wantDymName: &dymnstypes.DymName{ + Owner: ownerAcc.bech32(), + Controller: controllerAcc.bech32(), + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{ + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "", + Value: ownerAcc.bech32(), + }, + }, + }, + wantMinGasConsumed: dymnstypes.OpGasConfig, + postTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerAcc.bech32()).mappedDymNames(recordName) + s.requireConfiguredAddress(controllerAcc.bech32()).notMappedToAnyDymName() + s.requireFallbackAddress(ownerAcc.fallback()).mappedDymNames(recordName) + s.requireFallbackAddress(controllerAcc.fallback()).notMappedToAnyDymName() + }, + }, + { + name: "pass - address on RollApp automatically lowercase", + dymName: &dymnstypes.DymName{ + Owner: ownerAcc.bech32(), + Controller: controllerAcc.bech32(), + ExpireAt: s.now.Unix() + 100, + }, + preTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(anotherAcc.bech32C("rol")).notMappedToAnyDymName() + s.requireFallbackAddress(anotherAcc.fallback()).notMappedToAnyDymName() + + s.rollAppKeeper.SetRollapp(s.ctx, rollapptypes.Rollapp{ + RollappId: rollAppId, + Owner: anotherAcc.bech32(), + }) + }, + msg: &dymnstypes.MsgUpdateResolveAddress{ + ChainId: rollAppId, + ResolveTo: strings.ToUpper(anotherAcc.bech32C("rol")), // upper-cased + Controller: controllerAcc.bech32(), + }, + wantErr: false, + wantDymName: &dymnstypes.DymName{ + Owner: ownerAcc.bech32(), + Controller: controllerAcc.bech32(), + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{ + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: rollAppId, + Path: "", + Value: anotherAcc.bech32C("rol"), + }, + }, + }, + wantMinGasConsumed: dymnstypes.OpGasConfig, + postTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(anotherAcc.bech32C("rol")).mappedDymNames(recordName) + s.requireFallbackAddress(anotherAcc.fallback()).notMappedToAnyDymName() + }, + }, + { + name: "pass - keep case-sensitive address on non-host/non-RollApp", + dymName: &dymnstypes.DymName{ + Owner: ownerAcc.bech32(), + Controller: controllerAcc.bech32(), + ExpireAt: s.now.Unix() + 100, + }, + preTestFunc: func(s *KeeperTestSuite) { + s.Require().Equal( + nonBech32NonHexUpperCaseA, strings.ToUpper(nonBech32NonHexUpperCaseA), + "bad setup, this address must be upper-cased, to be used in this testcase", + ) + + s.requireConfiguredAddress(nonBech32NonHexUpperCaseA).notMappedToAnyDymName() + }, + msg: &dymnstypes.MsgUpdateResolveAddress{ + ChainId: "another", + ResolveTo: nonBech32NonHexUpperCaseA, // this address is neither bech32 nor hex + Controller: controllerAcc.bech32(), + }, + wantErr: false, + wantDymName: &dymnstypes.DymName{ + Owner: ownerAcc.bech32(), + Controller: controllerAcc.bech32(), + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{ + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "another", + Path: "", + Value: nonBech32NonHexUpperCaseA, + }, + }, + }, + wantMinGasConsumed: dymnstypes.OpGasConfig, + postTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(nonBech32NonHexUpperCaseA).mappedDymNames(recordName) + }, + }, + { + name: "pass - add new record if not exists", + dymName: &dymnstypes.DymName{ + Owner: ownerAcc.bech32(), + Controller: controllerAcc.bech32(), + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{ + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Path: "a", + Value: ownerAcc.bech32(), + }, + }, + }, + preTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerAcc.bech32()).mappedDymNames(recordName) + s.requireConfiguredAddress(controllerAcc.bech32()).notMappedToAnyDymName() + s.requireFallbackAddress(ownerAcc.fallback()).mappedDymNames(recordName) + s.requireFallbackAddress(controllerAcc.fallback()).notMappedToAnyDymName() + }, + msg: &dymnstypes.MsgUpdateResolveAddress{ + ResolveTo: ownerAcc.bech32(), + Controller: controllerAcc.bech32(), + }, + wantErr: false, + wantDymName: &dymnstypes.DymName{ + Owner: ownerAcc.bech32(), + Controller: controllerAcc.bech32(), + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{ + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Path: "a", + Value: ownerAcc.bech32(), + }, + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Path: "", + Value: ownerAcc.bech32(), + }, + }, + }, + wantMinGasConsumed: dymnstypes.OpGasConfig, + postTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerAcc.bech32()).mappedDymNames(recordName) + s.requireConfiguredAddress(controllerAcc.bech32()).notMappedToAnyDymName() + s.requireFallbackAddress(ownerAcc.fallback()).mappedDymNames(recordName) + s.requireFallbackAddress(controllerAcc.fallback()).notMappedToAnyDymName() + }, + }, + { + name: "pass - override record if exists", + dymName: &dymnstypes.DymName{ + Owner: ownerAcc.bech32(), + Controller: controllerAcc.bech32(), + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{ + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Path: "a", + Value: ownerAcc.bech32(), + }, + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Path: "", + Value: controllerAcc.bech32(), + }, + }, + }, + preTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerAcc.bech32()).mappedDymNames(recordName) + s.requireConfiguredAddress(controllerAcc.bech32()).mappedDymNames(recordName) + s.requireFallbackAddress(ownerAcc.fallback()).notMappedToAnyDymName() + s.requireFallbackAddress(controllerAcc.fallback()).mappedDymNames(recordName) + }, + msg: &dymnstypes.MsgUpdateResolveAddress{ + ResolveTo: ownerAcc.bech32(), + Controller: controllerAcc.bech32(), + }, + wantErr: false, + wantDymName: &dymnstypes.DymName{ + Owner: ownerAcc.bech32(), + Controller: controllerAcc.bech32(), + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{ + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Path: "a", + Value: ownerAcc.bech32(), + }, + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Path: "", + Value: ownerAcc.bech32(), + }, + }, + }, + wantMinGasConsumed: dymnstypes.OpGasConfig, + postTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerAcc.bech32()).mappedDymNames(recordName) + s.requireConfiguredAddress(controllerAcc.bech32()).notMappedToAnyDymName() + s.requireFallbackAddress(ownerAcc.fallback()).mappedDymNames(recordName) + s.requireFallbackAddress(controllerAcc.fallback()).notMappedToAnyDymName() + }, + }, + { + name: "pass - remove record if new resolve to empty, single-config", + dymName: &dymnstypes.DymName{ + Owner: ownerAcc.bech32(), + Controller: controllerAcc.bech32(), + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{ + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Path: "a", + Value: ownerAcc.bech32(), + }, + }, + }, + preTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerAcc.bech32()).mappedDymNames(recordName) + s.requireConfiguredAddress(controllerAcc.bech32()).notMappedToAnyDymName() + s.requireFallbackAddress(ownerAcc.fallback()).mappedDymNames(recordName) + s.requireFallbackAddress(controllerAcc.fallback()).notMappedToAnyDymName() + }, + msg: &dymnstypes.MsgUpdateResolveAddress{ + ResolveTo: "", + SubName: "a", + Controller: controllerAcc.bech32(), + }, + wantErr: false, + wantDymName: &dymnstypes.DymName{ + Owner: ownerAcc.bech32(), + Controller: controllerAcc.bech32(), + ExpireAt: s.now.Unix() + 100, + Configs: nil, + }, + wantMinGasConsumed: 1, + postTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerAcc.bech32()).mappedDymNames(recordName) + s.requireConfiguredAddress(controllerAcc.bech32()).notMappedToAnyDymName() + s.requireFallbackAddress(ownerAcc.fallback()).mappedDymNames(recordName) + s.requireFallbackAddress(controllerAcc.fallback()).notMappedToAnyDymName() + }, + }, + { + name: "pass - remove record if new resolve to empty, single-config, not match any", + dymName: &dymnstypes.DymName{ + Owner: ownerAcc.bech32(), + Controller: controllerAcc.bech32(), + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{ + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Path: "a", + Value: ownerAcc.bech32(), + }, + }, + }, + preTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerAcc.bech32()).mappedDymNames(recordName) + s.requireConfiguredAddress(controllerAcc.bech32()).notMappedToAnyDymName() + s.requireFallbackAddress(ownerAcc.fallback()).mappedDymNames(recordName) + s.requireFallbackAddress(controllerAcc.fallback()).notMappedToAnyDymName() + }, + msg: &dymnstypes.MsgUpdateResolveAddress{ + ResolveTo: "", + SubName: "non-exists", + Controller: controllerAcc.bech32(), + }, + wantErr: false, + wantDymName: &dymnstypes.DymName{ + Owner: ownerAcc.bech32(), + Controller: controllerAcc.bech32(), + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{ + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Path: "a", + Value: ownerAcc.bech32(), + }, + }, + }, + wantMinGasConsumed: 1, + postTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerAcc.bech32()).mappedDymNames(recordName) + s.requireConfiguredAddress(controllerAcc.bech32()).notMappedToAnyDymName() + s.requireFallbackAddress(ownerAcc.fallback()).mappedDymNames(recordName) + s.requireFallbackAddress(controllerAcc.fallback()).notMappedToAnyDymName() + }, + }, + { + name: "pass - remove record if new resolve to empty, multi-config, first", + dymName: &dymnstypes.DymName{ + Owner: ownerAcc.bech32(), + Controller: controllerAcc.bech32(), + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{ + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Path: "a", + Value: ownerAcc.bech32(), + }, + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Path: "", + Value: controllerAcc.bech32(), + }, + }, + }, + preTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerAcc.bech32()).mappedDymNames(recordName) + s.requireConfiguredAddress(controllerAcc.bech32()).mappedDymNames(recordName) + s.requireFallbackAddress(ownerAcc.fallback()).notMappedToAnyDymName() + s.requireFallbackAddress(controllerAcc.fallback()).mappedDymNames(recordName) + }, + msg: &dymnstypes.MsgUpdateResolveAddress{ + ResolveTo: "", + SubName: "a", + Controller: controllerAcc.bech32(), + }, + wantErr: false, + wantDymName: &dymnstypes.DymName{ + Owner: ownerAcc.bech32(), + Controller: controllerAcc.bech32(), + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{ + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Path: "", + Value: controllerAcc.bech32(), + }, + }, + }, + wantMinGasConsumed: 1, + postTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerAcc.bech32()).notMappedToAnyDymName() + s.requireConfiguredAddress(controllerAcc.bech32()).mappedDymNames(recordName) + s.requireFallbackAddress(ownerAcc.fallback()).notMappedToAnyDymName() + s.requireFallbackAddress(controllerAcc.fallback()).mappedDymNames(recordName) + }, + }, + { + name: "pass - remove record if new resolve to empty, multi-configs, last", + dymName: &dymnstypes.DymName{ + Owner: ownerAcc.bech32(), + Controller: controllerAcc.bech32(), + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{ + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Path: "a", + Value: ownerAcc.bech32(), + }, + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Path: "", + Value: controllerAcc.bech32(), + }, + }, + }, + preTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerAcc.bech32()).mappedDymNames(recordName) + s.requireConfiguredAddress(controllerAcc.bech32()).mappedDymNames(recordName) + s.requireFallbackAddress(ownerAcc.fallback()).notMappedToAnyDymName() + s.requireFallbackAddress(controllerAcc.fallback()).mappedDymNames(recordName) + }, + msg: &dymnstypes.MsgUpdateResolveAddress{ + ResolveTo: "", + Controller: controllerAcc.bech32(), + }, + wantErr: false, + wantDymName: &dymnstypes.DymName{ + Owner: ownerAcc.bech32(), + Controller: controllerAcc.bech32(), + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{ + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Path: "a", + Value: ownerAcc.bech32(), + }, + }, + }, + wantMinGasConsumed: 1, + postTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerAcc.bech32()).mappedDymNames(recordName) + s.requireConfiguredAddress(controllerAcc.bech32()).notMappedToAnyDymName() + s.requireFallbackAddress(ownerAcc.fallback()).mappedDymNames(recordName) + s.requireFallbackAddress(controllerAcc.fallback()).notMappedToAnyDymName() + }, + }, + { + name: "pass - remove record if new resolve to empty, multi-config, not any of existing", + dymName: &dymnstypes.DymName{ + Owner: ownerAcc.bech32(), + Controller: controllerAcc.bech32(), + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{ + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Path: "a", + Value: ownerAcc.bech32(), + }, + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Path: "", + Value: controllerAcc.bech32(), + }, + }, + }, + preTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerAcc.bech32()).mappedDymNames(recordName) + s.requireConfiguredAddress(controllerAcc.bech32()).mappedDymNames(recordName) + s.requireFallbackAddress(ownerAcc.fallback()).notMappedToAnyDymName() + s.requireFallbackAddress(controllerAcc.fallback()).mappedDymNames(recordName) + }, + msg: &dymnstypes.MsgUpdateResolveAddress{ + ResolveTo: "", + SubName: "non-exists", + Controller: controllerAcc.bech32(), + }, + wantErr: false, + wantDymName: &dymnstypes.DymName{ + Owner: ownerAcc.bech32(), + Controller: controllerAcc.bech32(), + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{ + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Path: "a", + Value: ownerAcc.bech32(), + }, + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Path: "", + Value: controllerAcc.bech32(), + }, + }, + }, + wantMinGasConsumed: 1, + postTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerAcc.bech32()).mappedDymNames(recordName) + s.requireConfiguredAddress(controllerAcc.bech32()).mappedDymNames(recordName) + s.requireFallbackAddress(ownerAcc.fallback()).notMappedToAnyDymName() + s.requireFallbackAddress(controllerAcc.fallback()).mappedDymNames(recordName) + }, + }, + { + name: "pass - expiry not changed", + dymName: &dymnstypes.DymName{ + Owner: ownerAcc.bech32(), + Controller: controllerAcc.bech32(), + ExpireAt: s.now.Unix() + 99, + }, + preTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerAcc.bech32()).mappedDymNames(recordName) + s.requireConfiguredAddress(controllerAcc.bech32()).notMappedToAnyDymName() + s.requireFallbackAddress(ownerAcc.fallback()).mappedDymNames(recordName) + s.requireFallbackAddress(controllerAcc.fallback()).notMappedToAnyDymName() + }, + msg: &dymnstypes.MsgUpdateResolveAddress{ + ResolveTo: ownerAcc.bech32(), + Controller: controllerAcc.bech32(), + }, + wantErr: false, + wantDymName: &dymnstypes.DymName{ + Owner: ownerAcc.bech32(), + Controller: controllerAcc.bech32(), + ExpireAt: s.now.Unix() + 99, + Configs: []dymnstypes.DymNameConfig{ + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Value: ownerAcc.bech32(), + }, + }, + }, + wantMinGasConsumed: dymnstypes.OpGasConfig, + postTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerAcc.bech32()).mappedDymNames(recordName) + s.requireConfiguredAddress(controllerAcc.bech32()).notMappedToAnyDymName() + s.requireFallbackAddress(ownerAcc.fallback()).mappedDymNames(recordName) + s.requireFallbackAddress(controllerAcc.fallback()).notMappedToAnyDymName() + }, + }, + { + name: "pass - chain-id automatically removed from record if is host chain-id", + dymName: &dymnstypes.DymName{ + Owner: ownerAcc.bech32(), + Controller: controllerAcc.bech32(), + ExpireAt: s.now.Unix() + 100, + }, + preTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerAcc.bech32()).mappedDymNames(recordName) + s.requireConfiguredAddress(controllerAcc.bech32()).notMappedToAnyDymName() + s.requireFallbackAddress(ownerAcc.fallback()).mappedDymNames(recordName) + s.requireFallbackAddress(controllerAcc.fallback()).notMappedToAnyDymName() + }, + msg: &dymnstypes.MsgUpdateResolveAddress{ + ChainId: s.chainId, + SubName: "a", + ResolveTo: ownerAcc.bech32(), + Controller: controllerAcc.bech32(), + }, + wantErr: false, + wantDymName: &dymnstypes.DymName{ + Owner: ownerAcc.bech32(), + Controller: controllerAcc.bech32(), + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{ + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "", // empty + Path: "a", + Value: ownerAcc.bech32(), + }, + }, + }, + wantMinGasConsumed: dymnstypes.OpGasConfig, + postTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerAcc.bech32()).mappedDymNames(recordName) + s.requireConfiguredAddress(controllerAcc.bech32()).notMappedToAnyDymName() + s.requireFallbackAddress(ownerAcc.fallback()).mappedDymNames(recordName) + s.requireFallbackAddress(controllerAcc.fallback()).notMappedToAnyDymName() + }, + }, + { + name: "pass - chain-id automatically removed from record if is host chain-id", + dymName: &dymnstypes.DymName{ + Owner: ownerAcc.bech32(), + Controller: controllerAcc.bech32(), + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{ + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "", // originally empty + Path: "a", + Value: controllerAcc.bech32(), + }, + }, + }, + preTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerAcc.bech32()).mappedDymNames(recordName) + s.requireConfiguredAddress(controllerAcc.bech32()).mappedDymNames(recordName) + s.requireFallbackAddress(ownerAcc.fallback()).mappedDymNames(recordName) + s.requireFallbackAddress(controllerAcc.fallback()).notMappedToAnyDymName() + }, + msg: &dymnstypes.MsgUpdateResolveAddress{ + ChainId: s.chainId, + SubName: "a", + ResolveTo: ownerAcc.bech32(), + Controller: controllerAcc.bech32(), + }, + wantErr: false, + wantDymName: &dymnstypes.DymName{ + Owner: ownerAcc.bech32(), + Controller: controllerAcc.bech32(), + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{ + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "", // empty + Path: "a", + Value: ownerAcc.bech32(), + }, + }, + }, + wantMinGasConsumed: dymnstypes.OpGasConfig, + postTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerAcc.bech32()).mappedDymNames(recordName) + s.requireConfiguredAddress(controllerAcc.bech32()).notMappedToAnyDymName() + s.requireFallbackAddress(ownerAcc.fallback()).mappedDymNames(recordName) + s.requireFallbackAddress(controllerAcc.fallback()).notMappedToAnyDymName() + }, + }, + { + name: "pass - chain-id recorded if is NOT host chain-id", + dymName: &dymnstypes.DymName{ + Owner: ownerAcc.bech32(), + Controller: controllerAcc.bech32(), + ExpireAt: s.now.Unix() + 100, + }, + msg: &dymnstypes.MsgUpdateResolveAddress{ + ChainId: "blumbus_100-1", + SubName: "a", + ResolveTo: ownerAcc.bech32(), + Controller: controllerAcc.bech32(), + }, + preTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerAcc.bech32()).mappedDymNames(recordName) + s.requireConfiguredAddress(controllerAcc.bech32()).notMappedToAnyDymName() + s.requireFallbackAddress(ownerAcc.fallback()).mappedDymNames(recordName) + s.requireFallbackAddress(controllerAcc.fallback()).notMappedToAnyDymName() + }, + wantErr: false, + wantDymName: &dymnstypes.DymName{ + Owner: ownerAcc.bech32(), + Controller: controllerAcc.bech32(), + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{ + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "blumbus_100-1", + Path: "a", + Value: ownerAcc.bech32(), + }, + }, + }, + wantMinGasConsumed: dymnstypes.OpGasConfig, + postTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerAcc.bech32()).mappedDymNames(recordName) + s.requireConfiguredAddress(controllerAcc.bech32()).notMappedToAnyDymName() + s.requireFallbackAddress(ownerAcc.fallback()).mappedDymNames(recordName) + s.requireFallbackAddress(controllerAcc.fallback()).notMappedToAnyDymName() + }, + }, + { + name: "pass - do not override record with different chain-id", + dymName: &dymnstypes.DymName{ + Owner: ownerAcc.bech32(), + Controller: controllerAcc.bech32(), + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{ + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "a", + Value: ownerAcc.bech32(), + }, + }, + }, + preTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerAcc.bech32()).mappedDymNames(recordName) + s.requireConfiguredAddress(controllerAcc.bech32()).notMappedToAnyDymName() + s.requireFallbackAddress(ownerAcc.fallback()).mappedDymNames(recordName) + s.requireFallbackAddress(controllerAcc.fallback()).notMappedToAnyDymName() + }, + msg: &dymnstypes.MsgUpdateResolveAddress{ + ChainId: "blumbus_100-1", + SubName: "a", + ResolveTo: ownerAcc.bech32(), + Controller: controllerAcc.bech32(), + }, + wantErr: false, + wantDymName: &dymnstypes.DymName{ + Owner: ownerAcc.bech32(), + Controller: controllerAcc.bech32(), + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{ + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "a", + Value: ownerAcc.bech32(), + }, + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "blumbus_100-1", + Path: "a", + Value: ownerAcc.bech32(), + }, + }, + }, + wantMinGasConsumed: dymnstypes.OpGasConfig, + postTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerAcc.bech32()).mappedDymNames(recordName) + s.requireConfiguredAddress(controllerAcc.bech32()).notMappedToAnyDymName() + s.requireFallbackAddress(ownerAcc.fallback()).mappedDymNames(recordName) + s.requireFallbackAddress(controllerAcc.fallback()).notMappedToAnyDymName() + }, + }, + { + name: "pass - do not override record with different chain-id", + dymName: &dymnstypes.DymName{ + Owner: ownerAcc.bech32(), + Controller: controllerAcc.bech32(), + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{ + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "a", + Value: controllerAcc.bech32(), + }, + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "blumbus_100-1", + Path: "a", + Value: controllerAcc.bech32(), + }, + }, + }, + preTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerAcc.bech32()).mappedDymNames(recordName) + s.requireConfiguredAddress(controllerAcc.bech32()).mappedDymNames(recordName) + s.requireFallbackAddress(ownerAcc.fallback()).mappedDymNames(recordName) + s.requireFallbackAddress(controllerAcc.fallback()).notMappedToAnyDymName() + }, + msg: &dymnstypes.MsgUpdateResolveAddress{ + ChainId: "blumbus_100-1", + SubName: "a", + ResolveTo: ownerAcc.bech32(), + Controller: controllerAcc.bech32(), + }, + wantErr: false, + wantDymName: &dymnstypes.DymName{ + Owner: ownerAcc.bech32(), + Controller: controllerAcc.bech32(), + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{ + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "a", + Value: controllerAcc.bech32(), + }, + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "blumbus_100-1", + Path: "a", + Value: ownerAcc.bech32(), + }, + }, + }, + wantMinGasConsumed: dymnstypes.OpGasConfig, + postTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerAcc.bech32()).mappedDymNames(recordName) + s.requireConfiguredAddress(controllerAcc.bech32()).mappedDymNames(recordName) + s.requireFallbackAddress(ownerAcc.fallback()).mappedDymNames(recordName) + s.requireFallbackAddress(controllerAcc.fallback()).notMappedToAnyDymName() + }, + }, + { + name: "pass - if input is 20 bytes, hex address, lower-case when persist", + dymName: &dymnstypes.DymName{ + Owner: ownerAcc.bech32(), + Controller: controllerAcc.bech32(), + ExpireAt: s.now.Unix() + 100, + }, + preTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(anotherAcc.checksumHex()).notMappedToAnyDymName() + s.requireFallbackAddress(anotherAcc.fallback()).notMappedToAnyDymName() + + s.Require().NotEqual(strings.ToLower(anotherAcc.hexStr()), anotherAcc.checksumHex()) + }, + msg: &dymnstypes.MsgUpdateResolveAddress{ + ChainId: "ethereum", + ResolveTo: anotherAcc.checksumHex(), + Controller: controllerAcc.bech32(), + }, + wantErr: false, + wantDymName: &dymnstypes.DymName{ + Owner: ownerAcc.bech32(), + Controller: controllerAcc.bech32(), + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{ + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "ethereum", + Value: strings.ToLower(anotherAcc.checksumHex()), // lower-cased + }, + }, + }, + wantMinGasConsumed: dymnstypes.OpGasConfig, + postTestFunc: func(s *KeeperTestSuite) { + // should be able to search case-insensitive + s.requireConfiguredAddress(anotherAcc.checksumHex()).mappedDymNames(recordName) + s.requireConfiguredAddress(strings.ToLower(anotherAcc.checksumHex())).mappedDymNames(recordName) + s.requireConfiguredAddress("0x" + strings.ToUpper(anotherAcc.checksumHex()[2:])).mappedDymNames(recordName) + + s.requireFallbackAddress(anotherAcc.fallback()).notMappedToAnyDymName() + }, + }, + { + name: "pass - if input is 32 bytes, hex address, lower-case when persist", + dymName: &dymnstypes.DymName{ + Owner: ownerAcc.bech32(), + Controller: controllerAcc.bech32(), + ExpireAt: s.now.Unix() + 100, + }, + preTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress("0x" + strings.ToLower(_32BytesAcc.hexStr())[2:]).notMappedToAnyDymName() + s.requireConfiguredAddress("0x" + strings.ToUpper(_32BytesAcc.hexStr())[2:]).notMappedToAnyDymName() + s.requireFallbackAddress(_32BytesAcc.fallback()).notMappedToAnyDymName() + }, + msg: &dymnstypes.MsgUpdateResolveAddress{ + ChainId: "another", + ResolveTo: "0x" + strings.ToUpper(_32BytesAcc.hexStr()[2:]), // upper-cased + Controller: controllerAcc.bech32(), + }, + wantErr: false, + wantDymName: &dymnstypes.DymName{ + Owner: ownerAcc.bech32(), + Controller: controllerAcc.bech32(), + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{ + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "another", + Value: "0x" + strings.ToLower(_32BytesAcc.hexStr()[2:]), // lower-cased + }, + }, + }, + wantMinGasConsumed: dymnstypes.OpGasConfig, + postTestFunc: func(s *KeeperTestSuite) { + // should be able to search case-insensitive + lowerCased := "0x" + strings.ToLower(_32BytesAcc.hexStr()[2:]) + s.requireConfiguredAddress(lowerCased).mappedDymNames(recordName) + upperCased := "0x" + strings.ToUpper(_32BytesAcc.hexStr()[2:]) + s.requireConfiguredAddress(upperCased).mappedDymNames(recordName) + + s.requireFallbackAddress(_32BytesAcc.fallback()).notMappedToAnyDymName() + }, + }, + { + name: "pass - if input is 20 bytes, WITHOUT 0x hex address, keep case-sensitive when persist", + dymName: &dymnstypes.DymName{ + Owner: ownerAcc.bech32(), + Controller: controllerAcc.bech32(), + ExpireAt: s.now.Unix() + 100, + }, + preTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(strings.ToUpper(anotherAcc.hexStr())[2:]).notMappedToAnyDymName() + s.requireFallbackAddress(anotherAcc.fallback()).notMappedToAnyDymName() + }, + msg: &dymnstypes.MsgUpdateResolveAddress{ + ChainId: "another", + ResolveTo: strings.ToUpper(anotherAcc.hexStr()[2:]), // removed 0x part and upper-cased + Controller: controllerAcc.bech32(), + }, + wantErr: false, + wantDymName: &dymnstypes.DymName{ + Owner: ownerAcc.bech32(), + Controller: controllerAcc.bech32(), + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{ + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "another", + Value: strings.ToUpper(anotherAcc.hexStr()[2:]), // keep as is + }, + }, + }, + wantMinGasConsumed: dymnstypes.OpGasConfig, + postTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(strings.ToUpper(anotherAcc.hexStr()[2:])).mappedDymNames(recordName) + s.requireConfiguredAddress(strings.ToLower(anotherAcc.hexStr())[2:]).notMappedToAnyDymName() + + s.requireFallbackAddress(anotherAcc.fallback()).notMappedToAnyDymName() + + // dont returns for similar address (+0x) + s.requireConfiguredAddress(anotherAcc.hexStr()).notMappedToAnyDymName() + s.requireConfiguredAddress(anotherAcc.checksumHex()).notMappedToAnyDymName() + }, + }, + { + name: "pass - if input is 32 bytes, WITHOUT 0x hex address, keep case-sensitive when persist", + dymName: &dymnstypes.DymName{ + Owner: ownerAcc.bech32(), + Controller: controllerAcc.bech32(), + ExpireAt: s.now.Unix() + 100, + }, + preTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(strings.ToUpper(_32BytesAcc.hexStr())[2:]).notMappedToAnyDymName() + s.requireFallbackAddress(_32BytesAcc.fallback()).notMappedToAnyDymName() + }, + msg: &dymnstypes.MsgUpdateResolveAddress{ + ChainId: "another", + ResolveTo: strings.ToUpper(_32BytesAcc.hexStr()[2:]), // removed 0x part and upper-cased + Controller: controllerAcc.bech32(), + }, + wantErr: false, + wantDymName: &dymnstypes.DymName{ + Owner: ownerAcc.bech32(), + Controller: controllerAcc.bech32(), + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{ + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "another", + Value: strings.ToUpper(_32BytesAcc.hexStr()[2:]), // keep as is + }, + }, + }, + wantMinGasConsumed: dymnstypes.OpGasConfig, + postTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(strings.ToUpper(_32BytesAcc.hexStr()[2:])).mappedDymNames(recordName) + s.requireConfiguredAddress(strings.ToLower(_32BytesAcc.hexStr())[2:]).notMappedToAnyDymName() + + s.requireFallbackAddress(_32BytesAcc.fallback()).notMappedToAnyDymName() + + // dont returns for similar address (+0x) + s.requireConfiguredAddress(_32BytesAcc.hexStr()).notMappedToAnyDymName() + s.requireConfiguredAddress("0x" + strings.ToUpper(_32BytesAcc.hexStr())[2:]).notMappedToAnyDymName() + }, + }, + { + name: "fail - reject if address is not corresponding bech32 on host chain if target chain is host chain, case empty chain-id", + dymName: &dymnstypes.DymName{ + Owner: ownerAcc.bech32(), + Controller: controllerAcc.bech32(), + ExpireAt: s.now.Unix() + 100, + }, + preTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerAcc.bech32()).mappedDymNames(recordName) + s.requireConfiguredAddress(controllerAcc.bech32()).notMappedToAnyDymName() + s.requireFallbackAddress(ownerAcc.fallback()).mappedDymNames(recordName) + s.requireFallbackAddress(controllerAcc.fallback()).notMappedToAnyDymName() + }, + msg: &dymnstypes.MsgUpdateResolveAddress{ + ChainId: "", + SubName: "a", + ResolveTo: ownerAcc.bech32C("nim"), // owner but with nim prefix + Controller: controllerAcc.bech32(), + }, + wantErr: true, + wantErrContains: "resolve address must be a valid bech32 account address on host chain", + wantDymName: &dymnstypes.DymName{ + Owner: ownerAcc.bech32(), + Controller: controllerAcc.bech32(), + ExpireAt: s.now.Unix() + 100, + }, + wantMinGasConsumed: 1, + postTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerAcc.bech32()).mappedDymNames(recordName) + s.requireConfiguredAddress(controllerAcc.bech32()).notMappedToAnyDymName() + s.requireFallbackAddress(ownerAcc.fallback()).mappedDymNames(recordName) + s.requireFallbackAddress(controllerAcc.fallback()).notMappedToAnyDymName() + }, + }, + { + name: "fail - reject if address is not corresponding bech32 on host chain if target chain is host chain, case use chain-id in request", + dymName: &dymnstypes.DymName{ + Owner: ownerAcc.bech32(), + Controller: controllerAcc.bech32(), + ExpireAt: s.now.Unix() + 100, + }, + preTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerAcc.bech32()).mappedDymNames(recordName) + s.requireConfiguredAddress(controllerAcc.bech32()).notMappedToAnyDymName() + s.requireFallbackAddress(ownerAcc.fallback()).mappedDymNames(recordName) + s.requireFallbackAddress(controllerAcc.fallback()).notMappedToAnyDymName() + }, + msg: &dymnstypes.MsgUpdateResolveAddress{ + ChainId: s.chainId, + SubName: "a", + ResolveTo: ownerAcc.bech32C("nim"), // owner but with nim prefix + Controller: controllerAcc.bech32(), + }, + wantErr: true, + wantErrContains: "resolve address must be a valid bech32 account address on host chain", + wantDymName: &dymnstypes.DymName{ + Owner: ownerAcc.bech32(), + Controller: controllerAcc.bech32(), + ExpireAt: s.now.Unix() + 100, + }, + wantMinGasConsumed: 1, + postTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerAcc.bech32()).mappedDymNames(recordName) + s.requireConfiguredAddress(controllerAcc.bech32()).notMappedToAnyDymName() + s.requireConfiguredAddress(ownerAcc.bech32C("nim")).notMappedToAnyDymName() + s.requireFallbackAddress(ownerAcc.fallback()).mappedDymNames(recordName) + s.requireFallbackAddress(controllerAcc.fallback()).notMappedToAnyDymName() + }, + }, + { + name: "fail - reject if address is not corresponding bech32 on host chain if target chain is host chain, case dym prefix but valoper, not acc addr", + dymName: &dymnstypes.DymName{ + Owner: ownerAcc.bech32(), + Controller: controllerAcc.bech32(), + ExpireAt: s.now.Unix() + 100, + }, + preTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerAcc.bech32()).mappedDymNames(recordName) + s.requireConfiguredAddress(controllerAcc.bech32()).notMappedToAnyDymName() + s.requireFallbackAddress(ownerAcc.fallback()).mappedDymNames(recordName) + s.requireFallbackAddress(controllerAcc.fallback()).notMappedToAnyDymName() + }, + msg: &dymnstypes.MsgUpdateResolveAddress{ + ChainId: "", + SubName: "a", + ResolveTo: ownerAcc.bech32Valoper(), // owner but with valoper prefix + Controller: controllerAcc.bech32(), + }, + wantErr: true, + wantErrContains: "resolve address must be a valid bech32 account address on host chain", + wantDymName: &dymnstypes.DymName{ + Owner: ownerAcc.bech32(), + Controller: controllerAcc.bech32(), + ExpireAt: s.now.Unix() + 100, + }, + wantMinGasConsumed: 1, + postTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerAcc.bech32()).mappedDymNames(recordName) + s.requireConfiguredAddress(controllerAcc.bech32()).notMappedToAnyDymName() + s.requireConfiguredAddress(ownerAcc.bech32Valoper()).notMappedToAnyDymName() + s.requireFallbackAddress(ownerAcc.fallback()).mappedDymNames(recordName) + s.requireFallbackAddress(controllerAcc.fallback()).notMappedToAnyDymName() + }, + }, + { + name: "fail - reject if address is not corresponding bech32 if target chain is RollApp", + dymName: &dymnstypes.DymName{ + Owner: ownerAcc.bech32(), + Controller: controllerAcc.bech32(), + ExpireAt: s.now.Unix() + 100, + }, + preTestFunc: func(s *KeeperTestSuite) { + s.rollAppKeeper.SetRollapp(s.ctx, rollapptypes.Rollapp{ + RollappId: rollAppId, + Owner: anotherAcc.bech32(), + }) + }, + msg: &dymnstypes.MsgUpdateResolveAddress{ + ChainId: rollAppId, + SubName: "a", + ResolveTo: ownerAcc.hexStr(), // wrong format + Controller: controllerAcc.bech32(), + }, + wantErr: true, + wantErrContains: "resolve address must be a valid bech32 account address on RollApp", + wantDymName: &dymnstypes.DymName{ + Owner: ownerAcc.bech32(), + Controller: controllerAcc.bech32(), + ExpireAt: s.now.Unix() + 100, + }, + wantMinGasConsumed: 1, + postTestFunc: func(s *KeeperTestSuite) {}, + }, + { + name: "fail - reject if address is not corresponding bech32 if target chain is RollApp", + dymName: &dymnstypes.DymName{ + Owner: ownerAcc.bech32(), + Controller: controllerAcc.bech32(), + ExpireAt: s.now.Unix() + 100, + }, + preTestFunc: func(s *KeeperTestSuite) { + s.persistRollApp( + *newRollApp("nim_1122-1").WithBech32("nim").WithAlias("nim"), + ) + }, + msg: &dymnstypes.MsgUpdateResolveAddress{ + ChainId: "nim_1122-1", + SubName: "a", + ResolveTo: ownerAcc.bech32C("ma"), // wrong bech32 prefix + Controller: controllerAcc.bech32(), + }, + wantErr: true, + wantErrContains: "resolve address must be a valid bech32 account address on RollApps", + wantDymName: &dymnstypes.DymName{ + Owner: ownerAcc.bech32(), + Controller: controllerAcc.bech32(), + ExpireAt: s.now.Unix() + 100, + }, + wantMinGasConsumed: 1, + postTestFunc: func(s *KeeperTestSuite) {}, + }, + { + name: "pass - accept if address is corresponding bech32 if target chain is RollApp", + dymName: &dymnstypes.DymName{ + Owner: ownerAcc.bech32(), + Controller: controllerAcc.bech32(), + ExpireAt: s.now.Unix() + 100, + }, + preTestFunc: func(s *KeeperTestSuite) { + s.rollAppKeeper.SetRollapp(s.ctx, rollapptypes.Rollapp{ + RollappId: "nim_1122-1", + Owner: anotherAcc.bech32(), + }) + }, + msg: &dymnstypes.MsgUpdateResolveAddress{ + ChainId: "nim_1122-1", + SubName: "a", + ResolveTo: ownerAcc.bech32C("nim"), + Controller: controllerAcc.bech32(), + }, + wantErr: false, + wantDymName: &dymnstypes.DymName{ + Owner: ownerAcc.bech32(), + Controller: controllerAcc.bech32(), + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{ + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "nim_1122-1", + Path: "a", + Value: ownerAcc.bech32C("nim"), + }, + }, + }, + wantMinGasConsumed: 1, + postTestFunc: func(s *KeeperTestSuite) {}, + }, + { + name: "pass - reverse mapping record should be updated accordingly", + dymName: &dymnstypes.DymName{ + Owner: ownerAcc.bech32(), + Controller: controllerAcc.bech32(), + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{ + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "", + Value: controllerAcc.bech32(), + }, + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "nim_1122-1", + Path: "a", + Value: anotherAcc.bech32C("nim"), + }, + }, + }, + preTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerAcc.bech32()).notMappedToAnyDymName() + s.requireConfiguredAddress(controllerAcc.bech32()).mappedDymNames(recordName) + s.requireConfiguredAddress(anotherAcc.bech32C("nim")).mappedDymNames(recordName) + s.requireFallbackAddress(ownerAcc.fallback()).notMappedToAnyDymName() + s.requireFallbackAddress(controllerAcc.fallback()).mappedDymNames(recordName) + s.requireFallbackAddress(anotherAcc.fallback()).notMappedToAnyDymName() + }, + msg: &dymnstypes.MsgUpdateResolveAddress{ + ChainId: "", + SubName: "", + ResolveTo: ownerAcc.bech32(), + Controller: controllerAcc.bech32(), + }, + wantErr: false, + wantDymName: &dymnstypes.DymName{ + Owner: ownerAcc.bech32(), + Controller: controllerAcc.bech32(), + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{ + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "", + Value: ownerAcc.bech32(), + }, + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "nim_1122-1", + Path: "a", + Value: anotherAcc.bech32C("nim"), + }, + }, + }, + wantMinGasConsumed: dymnstypes.OpGasConfig, + postTestFunc: func(s *KeeperTestSuite) { + s.requireConfiguredAddress(ownerAcc.bech32()).mappedDymNames(recordName) + s.requireConfiguredAddress(controllerAcc.bech32()).notMappedToAnyDymName() + s.requireConfiguredAddress(anotherAcc.bech32C("nim")).mappedDymNames(recordName) + s.requireFallbackAddress(ownerAcc.fallback()).mappedDymNames(recordName) + s.requireFallbackAddress(controllerAcc.fallback()).notMappedToAnyDymName() + s.requireFallbackAddress(anotherAcc.fallback()).notMappedToAnyDymName() + }, + }, + } + for _, tt := range tests { + s.Run(tt.name, func() { + s.Require().NotNil(tt.preTestFunc) + s.Require().NotNil(tt.postTestFunc) + + s.RefreshContext() + + if tt.dymName != nil { + if tt.dymName.Name == "" { + tt.dymName.Name = recordName + } + err := s.dymNsKeeper.SetDymName(s.ctx, *tt.dymName) + s.Require().NoError(err) + s.Require().NoError(s.dymNsKeeper.AfterDymNameOwnerChanged(s.ctx, tt.dymName.Name)) + s.Require().NoError(s.dymNsKeeper.AfterDymNameConfigChanged(s.ctx, tt.dymName.Name)) + } + if tt.wantDymName != nil && tt.wantDymName.Name == "" { + tt.wantDymName.Name = recordName + } + + tt.preTestFunc(s) + + tt.msg.Name = recordName + resp, err := dymnskeeper.NewMsgServerImpl(s.dymNsKeeper).UpdateResolveAddress(s.ctx, tt.msg) + laterDymName := s.dymNsKeeper.GetDymName(s.ctx, tt.msg.Name) + + defer func() { + if tt.wantMinGasConsumed > 0 { + s.Require().GreaterOrEqual( + s.ctx.GasMeter().GasConsumed(), tt.wantMinGasConsumed, + "should consume at least %d gas", tt.wantMinGasConsumed, + ) + } + + if !s.T().Failed() { + tt.postTestFunc(s) + } + }() + + if tt.wantErr { + s.Require().NotEmpty(tt.wantErrContains, "mis-configured test case") + s.Require().Error(err) + s.Require().Contains(err.Error(), tt.wantErrContains) + s.Require().Nil(resp) + + if tt.wantDymName != nil { + s.Require().Equal(*tt.wantDymName, *laterDymName) + + owned, err := s.dymNsKeeper.GetDymNamesOwnedBy(s.ctx, laterDymName.Owner) + s.Require().NoError(err) + if laterDymName.ExpireAt >= s.now.Unix() { + s.Require().Len(owned, 1) + } else { + s.Require().Empty(owned) + } + } else { + s.Require().Nil(laterDymName) + } + return + } + + s.Require().NoError(err) + s.Require().NotNil(resp) + s.Require().NotNil(laterDymName) + s.Require().Equal(*tt.wantDymName, *laterDymName) + }) + } + + for _, input := range nonHostChainBech32InputSet { + s.Run("non-bech32/non-hex on non-host/non-RollApp chain: "+input, func() { + s.RefreshContext() + + const anotherChainId = "another" + + dymName := dymnstypes.DymName{ + Name: "a", + Owner: ownerAcc.bech32(), + Controller: controllerAcc.bech32(), + ExpireAt: s.now.Unix() + 100, + } + s.Require().NoError(s.dymNsKeeper.SetDymName(s.ctx, dymName)) + + resp, err := dymnskeeper.NewMsgServerImpl(s.dymNsKeeper).UpdateResolveAddress(s.ctx, &dymnstypes.MsgUpdateResolveAddress{ + Name: dymName.Name, + Controller: dymName.Controller, + ChainId: anotherChainId, + SubName: "", + ResolveTo: input, + }) + s.Require().NoError(err) + s.Require().NotNil(resp) + + wantRecordedValue := input + if dymnsutils.IsValidHexAddress(input) { + // if input is hex, lower-case it regardless chain-id + wantRecordedValue = strings.ToLower(input) + } + + laterDymName := s.dymNsKeeper.GetDymName(s.ctx, dymName.Name) + s.Require().NotNil(laterDymName) + s.Require().Equal(dymnstypes.DymName{ + Name: dymName.Name, + Owner: dymName.Owner, + Controller: dymName.Controller, + ExpireAt: dymName.ExpireAt, + Configs: []dymnstypes.DymNameConfig{ + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: anotherChainId, + Path: "", + Value: wantRecordedValue, + }, + }, + }, *laterDymName) + + dymNameAddress := fmt.Sprintf("%s@%s", dymName.Name, anotherChainId) + outputAddress, err := s.dymNsKeeper.ResolveByDymNameAddress(s.ctx, dymNameAddress) + s.Require().NoError(err) + s.Require().Equal(wantRecordedValue, outputAddress) + + list, err := s.dymNsKeeper.ReverseResolveDymNameAddress(s.ctx, input, anotherChainId) + s.Require().NoError(err) + s.Require().Len(list, 1) + s.Require().Equal(dymNameAddress, list[0].String()) + + list, err = s.dymNsKeeper.ReverseResolveDymNameAddress(s.ctx, input, s.chainId) + s.Require().True(err != nil || len(list) == 0) + }) + } +} + +func (s *KeeperTestSuite) Test_msgServer_UpdateResolveAddress_ReverseMapping() { + ownerAcc := testAddr(1) + anotherAcc := testAddr(2) + + const rollappChainId = "rollapp_1-1" + const rollAppBech32 = "rol" + const externalChainId = "awesome" + const name = "my-name" + const subName = "sub" + + params.SetAddressPrefixes() + + const ( + tcCfgAddr = iota + tcFallbackAddr + tcResolveAddr + tcReverseResolveAddr + ) + type tc struct { + _type int + input string + want any + } + testMapCfgAddrToDymName := func(input string, wantMapped bool) tc { + return tc{_type: tcCfgAddr, input: input, want: wantMapped} + } + testMapFallbackAddrToDymName := func(input string, wantMapped bool) tc { + return tc{_type: tcFallbackAddr, input: input, want: wantMapped} + } + testResolveAddr := func(input, want string) tc { + return tc{_type: tcResolveAddr, input: input, want: want} + } + testReverseResolveAddr := func(input, want string) tc { + return tc{_type: tcReverseResolveAddr, input: input, want: want} + } + + type testStruct struct { + name string + inputResolveTo string + multipleInputResolveTo []string + hostChain bool + rollapp bool + rollappWithBech32 bool + externalChain bool + useSubName bool + wantReject bool + tests []tc + } + + tests := []testStruct{ + { + name: "bech32 on host-chain, without sub-name", + inputResolveTo: anotherAcc.bech32(), + hostChain: true, + useSubName: false, + tests: []tc{ + testMapCfgAddrToDymName(anotherAcc.bech32(), true), + testMapFallbackAddrToDymName(anotherAcc.hexStr(), true), // cuz host-chain and default config + testResolveAddr(name+"@"+s.chainId, anotherAcc.bech32()), + testReverseResolveAddr(anotherAcc.bech32(), name+"@"+s.chainId), + testReverseResolveAddr(anotherAcc.hexStr(), name+"@"+s.chainId), + }, + }, + { + name: "bech32 on host-chain, with sub-name", + inputResolveTo: anotherAcc.bech32(), + hostChain: true, + useSubName: true, + tests: []tc{ + testMapCfgAddrToDymName(anotherAcc.bech32(), true), + testMapFallbackAddrToDymName(anotherAcc.hexStr(), false), // cuz sub-name, not default config + testResolveAddr(subName+"."+name+"@"+s.chainId, anotherAcc.bech32()), + testReverseResolveAddr(anotherAcc.bech32(), subName+"."+name+"@"+s.chainId), + + testReverseResolveAddr(anotherAcc.hexStr(), subName+"."+name+"@"+s.chainId), + // reverse-resolve-able cuz it's host-chain or RollApp with bech32 configured + }, + }, + { + name: "bech32 on RollApp, without sub-name, without bech32 prefix cfg", + inputResolveTo: anotherAcc.bech32(), + rollapp: true, + rollappWithBech32: false, + useSubName: false, + tests: []tc{ + testMapCfgAddrToDymName(anotherAcc.bech32(), true), + testMapFallbackAddrToDymName(anotherAcc.hexStr(), false), // cuz not host-chain + testResolveAddr(name+"@"+rollappChainId, anotherAcc.bech32()), + testReverseResolveAddr(anotherAcc.bech32(), name+"@"+rollappChainId), + testReverseResolveAddr(anotherAcc.hexStr(), ""), + }, + }, + { + name: "bech32 on RollApp, with sub-name, without bech32 prefix cfg", + inputResolveTo: anotherAcc.bech32(), + rollapp: true, + rollappWithBech32: false, + useSubName: true, + tests: []tc{ + testMapCfgAddrToDymName(anotherAcc.bech32(), true), + testMapFallbackAddrToDymName(anotherAcc.hexStr(), false), // cuz not host-chain + testResolveAddr(subName+"."+name+"@"+rollappChainId, anotherAcc.bech32()), + testReverseResolveAddr(anotherAcc.bech32(), subName+"."+name+"@"+rollappChainId), + testReverseResolveAddr(anotherAcc.hexStr(), ""), + }, + }, + { + name: "bech32 on RollApp, without sub-name, with bech32 prefix cfg", + inputResolveTo: anotherAcc.bech32C(rollAppBech32), + rollapp: true, + rollappWithBech32: true, + useSubName: false, + tests: []tc{ + testMapCfgAddrToDymName(anotherAcc.bech32C(rollAppBech32), true), + testMapFallbackAddrToDymName(anotherAcc.hexStr(), false), // cuz not host-chain + testResolveAddr(name+"@"+rollappChainId, anotherAcc.bech32C(rollAppBech32)), + testReverseResolveAddr(anotherAcc.bech32C(rollAppBech32), name+"@"+rollappChainId), + testReverseResolveAddr(anotherAcc.hexStr(), name+"@"+rollappChainId), // cuz it's RollApp with bech32 prefix configured + }, + }, + { + name: "bech32 on RollApp with sub-name, with bech32 prefix cfg", + inputResolveTo: anotherAcc.bech32C(rollAppBech32), + rollapp: true, + rollappWithBech32: true, + useSubName: true, + tests: []tc{ + testMapCfgAddrToDymName(anotherAcc.bech32C(rollAppBech32), true), + testMapFallbackAddrToDymName(anotherAcc.hexStr(), false), // cuz not host-chain + testResolveAddr(subName+"."+name+"@"+rollappChainId, anotherAcc.bech32C(rollAppBech32)), + testReverseResolveAddr(anotherAcc.bech32C(rollAppBech32), subName+"."+name+"@"+rollappChainId), + testReverseResolveAddr(anotherAcc.hexStr(), subName+"."+name+"@"+rollappChainId), // cuz it's RollApp with bech32 prefix configured + }, + }, + { + name: "bech32 on external-chain, without sub-name", + inputResolveTo: anotherAcc.bech32(), + externalChain: true, + useSubName: false, + tests: []tc{ + testMapCfgAddrToDymName(anotherAcc.bech32(), true), + testMapFallbackAddrToDymName(anotherAcc.hexStr(), false), // cuz not host-chain + testResolveAddr(name+"@"+externalChainId, anotherAcc.bech32()), + testReverseResolveAddr(anotherAcc.bech32(), name+"@"+externalChainId), + testReverseResolveAddr(anotherAcc.hexStr(), ""), + }, + }, + { + name: "bech32 on external-chain, with sub-name", + inputResolveTo: anotherAcc.bech32(), + externalChain: true, + useSubName: true, + tests: []tc{ + testMapCfgAddrToDymName(anotherAcc.bech32(), true), + testMapFallbackAddrToDymName(anotherAcc.hexStr(), false), // cuz not host-chain + testResolveAddr(subName+"."+name+"@"+externalChainId, anotherAcc.bech32()), + testReverseResolveAddr(anotherAcc.bech32(), subName+"."+name+"@"+externalChainId), + testReverseResolveAddr(anotherAcc.hexStr(), ""), + }, + }, + { + name: "non-bech32 on host-chain, without sub-name", + inputResolveTo: anotherAcc.hexStr(), + multipleInputResolveTo: nonHostChainBech32InputSet, + hostChain: true, + useSubName: false, + wantReject: true, // host-chain requires bech32 as input + }, + { + name: "non-bech32 on host-chain, with sub-name", + inputResolveTo: anotherAcc.hexStr(), + multipleInputResolveTo: nonHostChainBech32InputSet, + hostChain: true, + useSubName: true, + wantReject: true, // host-chain, requires bech32 as input + }, + { + name: "non-bech32 on RollApp, without sub-name", + inputResolveTo: anotherAcc.hexStr(), + multipleInputResolveTo: nonBech32InputSet, + rollapp: true, + useSubName: false, + wantReject: true, // RollApp requires bech32 as input + }, + { + name: "non-bech32 on RollApp, with sub-name", + inputResolveTo: anotherAcc.hexStr(), + multipleInputResolveTo: nonBech32InputSet, + rollapp: true, + useSubName: true, + wantReject: true, // RollApp requires bech32 as input + }, + { + name: "hex on external chain, without sub-name", + inputResolveTo: anotherAcc.hexStr(), + externalChain: true, + useSubName: false, + tests: []tc{ + testMapCfgAddrToDymName(anotherAcc.hexStr(), true), + testMapFallbackAddrToDymName(anotherAcc.hexStr(), false), // cuz not host-chain, not default config + testResolveAddr(name+"@"+externalChainId, anotherAcc.hexStr()), + testReverseResolveAddr(anotherAcc.hexStr(), name+"@"+externalChainId), + testReverseResolveAddr(anotherAcc.bech32(), ""), // cuz input is hex + }, + }, + { + name: "hex on external chain, with sub-name", + inputResolveTo: anotherAcc.hexStr(), + externalChain: true, + useSubName: true, + tests: []tc{ + testMapCfgAddrToDymName(anotherAcc.hexStr(), true), + testMapFallbackAddrToDymName(anotherAcc.hexStr(), false), // cuz not host-chain, not default config + testResolveAddr(subName+"."+name+"@"+externalChainId, anotherAcc.hexStr()), + testReverseResolveAddr(anotherAcc.hexStr(), subName+"."+name+"@"+externalChainId), + testReverseResolveAddr(anotherAcc.bech32(), ""), // cuz input is hex + }, + }, + } + + // build test cases from non-bech32 set + for _, input := range nonHostChainBech32InputSet { + if dymnsutils.IsValidHexAddress(input) { + continue + } + tests = append( + tests, + testStruct{ + name: fmt.Sprintf("non-bech32 on external chain, without sub-name: %s", input), + inputResolveTo: input, + externalChain: true, + useSubName: false, + tests: []tc{ + testMapCfgAddrToDymName(input, true), + testResolveAddr(name+"@"+externalChainId, input), + testReverseResolveAddr(input, name+"@"+externalChainId), + }, + }, + testStruct{ + name: fmt.Sprintf("non-bech32 on external chain, with sub-name: %s", input), + inputResolveTo: input, + externalChain: true, + useSubName: true, + tests: []tc{ + testMapCfgAddrToDymName(input, true), + testResolveAddr(subName+"."+name+"@"+externalChainId, input), + testReverseResolveAddr(input, subName+"."+name+"@"+externalChainId), + }, + }, + ) + } + + for _, tt := range tests { + s.Run(tt.name, func() { + bti := func(b bool) int { + if b { + return 1 + } + return 0 + } + s.Require().Equal( + 1, bti(tt.hostChain)+bti(tt.rollapp)+bti(tt.externalChain), + "at least one and only one flag is allowed", + ) + if len(tt.multipleInputResolveTo) > 0 { + s.Require().True(tt.wantReject, "multiple input resolve-to only be used with want-reject") + } + + s.RefreshContext() + + dymName := dymnstypes.DymName{ + Name: name, + Owner: ownerAcc.bech32(), + Controller: ownerAcc.bech32(), + ExpireAt: s.ctx.BlockTime().Add(time.Second).Unix(), + } + s.setDymNameWithFunctionsAfter(dymName) + + if tt.rollapp { + bech32Prefix := "" + if tt.rollappWithBech32 { + bech32Prefix = rollAppBech32 + } + + s.persistRollApp( + *newRollApp(rollappChainId).WithBech32(bech32Prefix), + ) + } + + var useContextChainId string + if tt.hostChain { + useContextChainId = s.chainId + } else if tt.rollapp { + useContextChainId = rollappChainId + } else { + useContextChainId = externalChainId + } + + var useSubName string + if tt.useSubName { + useSubName = subName + } + + msg := &dymnstypes.MsgUpdateResolveAddress{ + Name: dymName.Name, + Controller: ownerAcc.bech32(), + ChainId: useContextChainId, + SubName: useSubName, + ResolveTo: tt.inputResolveTo, + } + + resp, err := dymnskeeper.NewMsgServerImpl(s.dymNsKeeper).UpdateResolveAddress(s.ctx, msg) + if tt.wantReject { + s.Require().Error(err) + + for _, input := range tt.multipleInputResolveTo { + msg.ResolveTo = input + _, err := dymnskeeper.NewMsgServerImpl(s.dymNsKeeper).UpdateResolveAddress(s.ctx, msg) + s.Require().Errorf(err, "input: %s", input) + } + return + } + + s.Require().NoError(err) + s.Require().NotNil(resp) + + { + // check Dym-Name record + + laterDymName := s.dymNsKeeper.GetDymName(s.ctx, dymName.Name) + s.Require().NotNil(laterDymName) + + wantDymName := dymName + wantDymName.Configs = []dymnstypes.DymNameConfig{ + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: msg.ChainId, + Path: msg.SubName, + Value: msg.ResolveTo, + }, + } + if tt.hostChain { + wantDymName.Configs[0].ChainId = "" + } + s.Require().Equal(wantDymName, *laterDymName) + } + + s.Require().NotEmpty(tt.tests) + + for _, tc := range tt.tests { + switch tc._type { + case tcCfgAddr: + list, err := s.dymNsKeeper.GetDymNamesContainsConfiguredAddress(s.ctx, tc.input) + s.Require().NoError(err) + if tc.want.(bool) { + s.requireDymNameList(list, []string{dymName.Name}) + } else { + s.Require().Empty(list) + } + case tcFallbackAddr: + list, err := s.dymNsKeeper.GetDymNamesContainsFallbackAddress(s.ctx, dymnsutils.GetBytesFromHexAddress(tc.input)) + s.Require().NoError(err) + if tc.want.(bool) { + s.requireDymNameList(list, []string{dymName.Name}) + } else { + s.Require().Empty(list) + } + case tcResolveAddr: + outputAddr, err := s.dymNsKeeper.ResolveByDymNameAddress(s.ctx, tc.input) + if tc.want.(string) == "" { + s.Require().Error(err) + s.Require().Empty(outputAddr) + } else { + s.Require().NoError(err) + s.Require().Equal(tc.want.(string), outputAddr) + } + case tcReverseResolveAddr: + candidates, err := s.dymNsKeeper.ReverseResolveDymNameAddress(s.ctx, tc.input, useContextChainId) + if tc.want.(string) == "" { + s.Require().NoError(err) + s.Require().Empty(candidates) + } else { + s.Require().NoError(err) + s.Require().NotEmptyf(candidates, "want %s", tc.want.(string)) + s.Require().Equal(tc.want.(string), candidates[0].String()) + } + default: + s.T().Fatalf("unknown test case type: %d", tc._type) + } + } + }) + } +} diff --git a/x/dymns/keeper/params.go b/x/dymns/keeper/params.go new file mode 100644 index 000000000..0087fc242 --- /dev/null +++ b/x/dymns/keeper/params.go @@ -0,0 +1,67 @@ +package keeper + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" + dymnsutils "github.com/dymensionxyz/dymension/v3/x/dymns/utils" +) + +// GetParams get all parameters as types.Params +func (k Keeper) GetParams(ctx sdk.Context) dymnstypes.Params { + return dymnstypes.NewParams( + k.PriceParams(ctx), + k.ChainsParams(ctx), + k.MiscParams(ctx), + ) +} + +// SetParams set the params +func (k Keeper) SetParams(ctx sdk.Context, params dymnstypes.Params) error { + if err := params.Validate(); err != nil { + return err + } + k.paramStore.SetParamSet(ctx, ¶ms) + return nil +} + +// PriceParams returns the price params +func (k Keeper) PriceParams(ctx sdk.Context) (res dymnstypes.PriceParams) { + k.paramStore.Get(ctx, dymnstypes.KeyPriceParams, &res) + return +} + +// ChainsParams returns the chains params +func (k Keeper) ChainsParams(ctx sdk.Context) (res dymnstypes.ChainsParams) { + k.paramStore.Get(ctx, dymnstypes.KeyChainsParams, &res) + return +} + +// MiscParams returns the miscellaneous params +func (k Keeper) MiscParams(ctx sdk.Context) (res dymnstypes.MiscParams) { + k.paramStore.Get(ctx, dymnstypes.KeyMiscParams, &res) + return +} + +// CanUseAliasForNewRegistration checks if the alias can be used for a new alias registration. +// +// It returns False when +// - The format is invalid. +// - The alias is exists in the params, mapped by a chain-id or registered to a Roll-App. +// - The alias is equals to a known chain-id, from the params. +func (k Keeper) CanUseAliasForNewRegistration(ctx sdk.Context, aliasCandidate string) (can bool) { + if !dymnsutils.IsValidAlias(aliasCandidate) { + return false + } + + if k.IsAliasPresentsInParamsAsAliasOrChainId(ctx, aliasCandidate) { + // Please read the `processActiveAliasSellOrders` method (hooks.go) for more information. + return false + } + + if isRollAppId := k.IsRollAppId(ctx, aliasCandidate); isRollAppId { + return false + } + + _, foundRollAppIdFromAlias := k.GetRollAppIdByAlias(ctx, aliasCandidate) + return !foundRollAppIdFromAlias +} diff --git a/x/dymns/keeper/params_test.go b/x/dymns/keeper/params_test.go new file mode 100644 index 000000000..1f27e7056 --- /dev/null +++ b/x/dymns/keeper/params_test.go @@ -0,0 +1,235 @@ +package keeper_test + +import ( + "time" + + rollapptypes "github.com/dymensionxyz/dymension/v3/x/rollapp/types" + + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" +) + +func (s *KeeperTestSuite) TestGetSetParams() { + params := dymnstypes.DefaultParams() + + err := s.dymNsKeeper.SetParams(s.ctx, params) + s.Require().NoError(err) + + s.Require().Equal(params, s.dymNsKeeper.GetParams(s.ctx)) + + s.Run("can not set invalid params", func() { + params := dymnstypes.DefaultParams() + params.Misc.EndEpochHookIdentifier = "" + s.Require().Error(s.dymNsKeeper.SetParams(s.ctx, params)) + }) + + s.Run("can not set invalid params", func() { + params := dymnstypes.DefaultParams() + params.Price.PriceDenom = "" + s.Require().Error(s.dymNsKeeper.SetParams(s.ctx, params)) + }) + + s.Run("can not set invalid params", func() { + params := dymnstypes.DefaultParams() + params.Chains.AliasesOfChainIds = []dymnstypes.AliasesOfChainId{ + { + ChainId: "@", + Aliases: nil, + }, + } + s.Require().Error(s.dymNsKeeper.SetParams(s.ctx, params)) + }) + + s.Run("can not set invalid params", func() { + params := dymnstypes.DefaultParams() + params.Misc.GracePeriodDuration = -999 * time.Hour + s.Require().Error(s.dymNsKeeper.SetParams(s.ctx, params)) + }) +} + +func (s *KeeperTestSuite) TestKeeper_CanUseAliasForNewRegistration() { + tests := []struct { + name string + alias string + preSetup func(s *KeeperTestSuite) + want bool + }{ + { + name: "pass - can check", + alias: "a", + want: true, + }, + { + name: "fail - reject bad alias", + alias: "@", + want: false, + }, + { + name: "pass - returns as free if neither in Params or Roll-App", + alias: "free", + preSetup: func(s *KeeperTestSuite) { + s.updateModuleParams(func(params dymnstypes.Params) dymnstypes.Params { + params.Chains.AliasesOfChainIds = []dymnstypes.AliasesOfChainId{ + { + ChainId: "dymension_1100-1", + Aliases: []string{"dym"}, + }, + } + return params + }) + + s.persistRollApp( + *newRollApp("rollapp_1-1").WithAlias("ra"), + ) + + s.requireRollApp("rollapp_1-1").HasAlias("ra") + }, + want: true, + }, + { + name: "pass - returns as free if no params, no Roll-App", + alias: "free", + want: true, + }, + { + name: "pass - returns as NOT free if reserved in Params", + alias: "dymension", + preSetup: func(s *KeeperTestSuite) { + s.updateModuleParams(func(params dymnstypes.Params) dymnstypes.Params { + params.Chains.AliasesOfChainIds = []dymnstypes.AliasesOfChainId{ + { + ChainId: "dymension_1100-1", + Aliases: []string{"dym", "dymension"}, + }, + } + return params + }) + + s.persistRollApp( + *newRollApp("rollapp_1-1").WithAlias("ra"), + ) + + s.requireRollApp("rollapp_1-1").HasAlias("ra") + }, + want: false, + }, + { + name: "pass - returns as NOT free if reserved in Params as chain-id, without alias", + alias: "zeta", + preSetup: func(s *KeeperTestSuite) { + s.updateModuleParams(func(params dymnstypes.Params) dymnstypes.Params { + params.Chains.AliasesOfChainIds = []dymnstypes.AliasesOfChainId{ + { + ChainId: "zeta", + Aliases: nil, + }, + } + return params + }) + }, + want: false, + }, + { + name: "pass - returns as NOT free if reserved in RollApp", + alias: "ra", + preSetup: func(s *KeeperTestSuite) { + s.updateModuleParams(func(params dymnstypes.Params) dymnstypes.Params { + params.Chains.AliasesOfChainIds = []dymnstypes.AliasesOfChainId{ + { + ChainId: "dymension_1100-1", + Aliases: []string{"dym", "dymension"}, + }, + } + return params + }) + + s.persistRollApp( + *newRollApp("rollapp_1-1").WithAlias("ra"), + ) + + s.requireRollApp("rollapp_1-1").HasAlias("ra") + }, + want: false, + }, + { + name: "pass - returns as NOT free if reserved in RollApp, which owned multiple aliases", + alias: "two", + preSetup: func(s *KeeperTestSuite) { + s.persistRollApp( + *newRollApp("rollapp_1-1").WithAlias("one"), + ) + + err := s.dymNsKeeper.SetAliasForRollAppId(s.ctx, "rollapp_1-1", "two") + s.Require().NoError(err) + + err = s.dymNsKeeper.SetAliasForRollAppId(s.ctx, "rollapp_1-1", "three") + s.Require().NoError(err) + + s.requireRollApp("rollapp_1-1").HasAlias("one", "two", "three") + }, + want: false, + }, + { + name: "pass - returns as NOT free if reserved in both Params and RollApp", + alias: "dym", + preSetup: func(s *KeeperTestSuite) { + s.updateModuleParams(func(params dymnstypes.Params) dymnstypes.Params { + params.Chains.AliasesOfChainIds = []dymnstypes.AliasesOfChainId{ + { + ChainId: "dymension_1100-1", + Aliases: []string{"dym", "dymension"}, + }, + } + return params + }) + + s.persistRollApp( + *newRollApp("dymension_1-1").WithAlias("dym"), + ) + + s.requireRollApp("dymension_1-1").HasAlias("dym") + }, + want: false, + }, + { + name: "pass - returns as NOT free if it is a Chain-ID in params mapping", + alias: "bridge", + preSetup: func(s *KeeperTestSuite) { + s.updateModuleParams(func(params dymnstypes.Params) dymnstypes.Params { + params.Chains.AliasesOfChainIds = []dymnstypes.AliasesOfChainId{ + { + ChainId: "bridge", + Aliases: []string{"b"}, + }, + } + return params + }) + }, + want: false, + }, + { + name: "pass - returns as NOT free if it is a RollApp-ID", + alias: "bridge", + preSetup: func(s *KeeperTestSuite) { + s.pureSetRollApp(rollapptypes.Rollapp{ + RollappId: "bridge", + Owner: testAddr(1).bech32(), + }) + err := s.dymNsKeeper.SetAliasForRollAppId(s.ctx, "bridge", "b") + s.Require().NoError(err) + }, + want: false, + }, + } + for _, tt := range tests { + s.Run(tt.name, func() { + s.RefreshContext() + + if tt.preSetup != nil { + tt.preSetup(s) + } + + can := s.dymNsKeeper.CanUseAliasForNewRegistration(s.ctx, tt.alias) + s.Require().Equal(tt.want, can) + }) + } +} diff --git a/x/dymns/keeper/proposal.go b/x/dymns/keeper/proposal.go new file mode 100644 index 000000000..adea37860 --- /dev/null +++ b/x/dymns/keeper/proposal.go @@ -0,0 +1,219 @@ +package keeper + +import ( + "errors" + + errorsmod "cosmossdk.io/errors" + + sdk "github.com/cosmos/cosmos-sdk/types" + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" + dymnsutils "github.com/dymensionxyz/dymension/v3/x/dymns/utils" + "github.com/dymensionxyz/gerr-cosmos/gerrc" +) + +// MigrateChainIds called by GOV handler, migrate chain-ids in module params +// as well as Dym-Names configurations (of non-expired) those contain chain-ids. +func (k Keeper) MigrateChainIds(ctx sdk.Context, replacement []dymnstypes.MigrateChainId) error { + previousChainIdsToNewChainId := make(map[string]string) + // Describe usage of Go Map: only used for mapping previous chain id to new chain id + // and should not be used for other purposes as well as iteration. + + for _, r := range replacement { + previousChainIdsToNewChainId[r.PreviousChainId] = r.NewChainId + } + + if err := k.migrateChainIdsInParams(ctx, previousChainIdsToNewChainId); err != nil { + return err + } + + if err := k.migrateChainIdsInDymNames(ctx, previousChainIdsToNewChainId); err != nil { + return err + } + + return nil +} + +// migrateChainIdsInParams migrates chain-ids in module params. +func (k Keeper) migrateChainIdsInParams(ctx sdk.Context, previousChainIdsToNewChainId map[string]string) error { + params := k.GetParams(ctx) + + if len(params.Chains.AliasesOfChainIds) > 0 { + existingAliasesOfChainIds := make(map[string]dymnstypes.AliasesOfChainId) + // Describe usage of Go Map: only used for mapping chain id to the alias configuration of chain id + // and should not be used for other purposes as well as iteration. + + for _, record := range params.Chains.AliasesOfChainIds { + existingAliasesOfChainIds[record.ChainId] = record + } + + newAliasesByChainId := make([]dymnstypes.AliasesOfChainId, 0) + for _, record := range params.Chains.AliasesOfChainIds { + chainId := record.ChainId + aliases := record.Aliases + + if newChainId, isPreviousChainId := previousChainIdsToNewChainId[chainId]; isPreviousChainId { + if _, foundDeclared := existingAliasesOfChainIds[newChainId]; foundDeclared { + // we don't override, we keep the aliases of the new chain id + + // ignore and remove the aliases of the previous chain id + } else { + newAliasesByChainId = append(newAliasesByChainId, dymnstypes.AliasesOfChainId{ + ChainId: newChainId, + Aliases: aliases, + }) + } + } else { + newAliasesByChainId = append(newAliasesByChainId, dymnstypes.AliasesOfChainId{ + ChainId: chainId, + Aliases: aliases, + }) + } + } + params.Chains.AliasesOfChainIds = newAliasesByChainId + } + + if err := k.SetParams(ctx, params); err != nil { + return errorsmod.Wrapf(errors.Join(gerrc.ErrInternal, err), "failed to update params") + } + + return nil +} + +// migrateChainIdsInDymNames migrates chain-ids in non-expired Dym-Names configurations. +func (k Keeper) migrateChainIdsInDymNames(ctx sdk.Context, previousChainIdsToNewChainId map[string]string) error { + // We only migrate for Dym-Names that not expired to reduce IO needed. + + nonExpiredDymNames := k.GetAllNonExpiredDymNames(ctx) + + for _, dymName := range nonExpiredDymNames { + newConfigs := make([]dymnstypes.DymNameConfig, len(dymName.Configs)) + var anyConfigUpdated bool + for i, config := range dymName.Configs { + if config.ChainId != "" { + if newChainId, isPreviousChainId := previousChainIdsToNewChainId[config.ChainId]; isPreviousChainId { + config.ChainId = newChainId + anyConfigUpdated = true + } + } + + newConfigs[i] = config + } + + if !anyConfigUpdated { + // Skip migration for this Dym-Name if nothing updated to reduce IO. + continue + } + + dymName.Configs = newConfigs + + if err := dymName.Validate(); err != nil { + k.Logger(ctx).Error( + "failed to migrate chain ids for Dym-Name, ignored", + "dymName", dymName.Name, + "error", err, + ) + // Skip migration for this Dym-Name. + // We don't want to break the migration process for other Dym-Names. + // The replacement should be done later by the owner. + continue + } + + // From here, any step can procedures dirty state, so we need to abort the migration + + // We do not call BeforeDymNameConfigChanged and AfterDymNameConfigChanged + // here because we only change the chain id, which does not affect any data + // that need to be updated in those methods, so we can skip them to reduce IO. + // Reverse-resolve records are re-computed in runtime anyway. + + if err := k.SetDymName(ctx, dymName); err != nil { + return errorsmod.Wrapf( + errors.Join(gerrc.ErrInternal, err), + "failed to migrate chain ids for Dym-Name: %s", dymName.Name, + ) + } + + k.Logger(ctx).Info("migrated chain ids for Dym-Name.", "dymName", dymName.Name) + } + + return nil +} + +// UpdateAliases called by GOV handler, update aliases of chain-ids in module params. +func (k Keeper) UpdateAliases(ctx sdk.Context, add, remove []dymnstypes.UpdateAlias) error { + params := k.GetParams(ctx) + + chainIdToAliasConfig := make(map[string]map[string]bool) + // Describe usage of Go Map: used to map from chain id to alias configuration. + // This map is used to quickly find the alias configuration of a chain id. + // Data should be sorted before persist. + + for _, record := range params.Chains.AliasesOfChainIds { + aliasesPerChainId := make(map[string]bool) + for _, alias := range record.Aliases { + aliasesPerChainId[alias] = true + } + chainIdToAliasConfig[record.ChainId] = aliasesPerChainId + } + + if len(add) > 0 { + for _, record := range add { + chainId := record.ChainId + alias := record.Alias + + existingAliases, foundExistingChainId := chainIdToAliasConfig[chainId] + if !foundExistingChainId { + existingAliases = make(map[string]bool) + } + + _, foundAlias := existingAliases[alias] + if foundAlias { + return errorsmod.Wrapf(gerrc.ErrAlreadyExists, "alias %s already mapped to chain-id %s in params", alias, chainId) + } + + existingAliases[alias] = true + chainIdToAliasConfig[chainId] = existingAliases + } + } + + if len(remove) > 0 { + for _, record := range remove { + chainId := record.ChainId + alias := record.Alias + + aliasesPerChainId, foundExistingChainId := chainIdToAliasConfig[chainId] + if !foundExistingChainId { + return errorsmod.Wrapf(gerrc.ErrNotFound, "chain id not found to remove: %s", chainId) + } + + _, foundAlias := aliasesPerChainId[alias] + if !foundAlias { + return errorsmod.Wrapf(gerrc.ErrNotFound, "alias does not exists in params: %s", alias) + } + + delete(aliasesPerChainId, alias) + if len(aliasesPerChainId) == 0 { + delete(chainIdToAliasConfig, chainId) + } + } + } + + // build new params + // Note: data must be sorted before persist + + sortedChainIds := dymnsutils.GetSortedStringKeys(chainIdToAliasConfig) + + var newAliasesOfChainIds []dymnstypes.AliasesOfChainId + for _, chainId := range sortedChainIds { + newAliasesOfChainIds = append(newAliasesOfChainIds, dymnstypes.AliasesOfChainId{ + ChainId: chainId, + Aliases: dymnsutils.GetSortedStringKeys(chainIdToAliasConfig[chainId]), + }) + } + params.Chains.AliasesOfChainIds = newAliasesOfChainIds + + if err := k.SetParams(ctx, params); err != nil { + return errorsmod.Wrap(errors.Join(gerrc.ErrInternal, err), "failed to update params") + } + + return nil +} diff --git a/x/dymns/keeper/proposal_test.go b/x/dymns/keeper/proposal_test.go new file mode 100644 index 000000000..563e185a5 --- /dev/null +++ b/x/dymns/keeper/proposal_test.go @@ -0,0 +1,939 @@ +package keeper_test + +import ( + "reflect" + + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" +) + +func (s *KeeperTestSuite) TestKeeper_MigrateChainIds() { + addr1a := testAddr(1).bech32() + addr2a := testAddr(2).bech32() + cosmos3A := testAddr(3).bech32C("cosmos") + + tests := []struct { + name string + dymNames []dymnstypes.DymName + replacement []dymnstypes.MigrateChainId + chainsAliasParams []dymnstypes.AliasesOfChainId + additionalSetup func(s *KeeperTestSuite) + wantErr bool + wantErrContains string + wantDymNames []dymnstypes.DymName + wantChainsAliasParams []dymnstypes.AliasesOfChainId + }{ + { + name: "pass - can migrate", + dymNames: []dymnstypes.DymName{ + { + Name: "a", + Owner: addr1a, + Controller: addr1a, + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{{ + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "cosmoshub-3", + Path: "", + Value: cosmos3A, + }}, + }, + { + Name: "b", + Owner: addr1a, + Controller: addr1a, + ExpireAt: s.now.Unix() + 100, + }, + }, + replacement: []dymnstypes.MigrateChainId{ + { + PreviousChainId: "cosmoshub-3", + NewChainId: "cosmoshub-4", + }, + { + PreviousChainId: "blumbus_111-1", + NewChainId: "blumbus_111-2", + }, + }, + chainsAliasParams: []dymnstypes.AliasesOfChainId{ + { + ChainId: "cosmoshub-3", + Aliases: []string{"cosmos"}, + }, + }, + additionalSetup: nil, + wantErr: false, + wantDymNames: []dymnstypes.DymName{ + { + Name: "a", + Owner: addr1a, + Controller: addr1a, + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{{ + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "cosmoshub-4", + Path: "", + Value: cosmos3A, + }}, + }, + { + Name: "b", + Owner: addr1a, + Controller: addr1a, + ExpireAt: s.now.Unix() + 100, + }, + }, + wantChainsAliasParams: []dymnstypes.AliasesOfChainId{ + { + ChainId: "cosmoshub-4", + Aliases: []string{"cosmos"}, + }, + }, + }, + { + name: "pass - can migrate params alias chain-id", + replacement: []dymnstypes.MigrateChainId{ + { + PreviousChainId: "cosmoshub-3", + NewChainId: "cosmoshub-4", + }, + { + PreviousChainId: "blumbus_111-1", + NewChainId: "blumbus_111-2", + }, + }, + chainsAliasParams: []dymnstypes.AliasesOfChainId{ + { + ChainId: "cosmoshub-3", + Aliases: []string{"cosmos"}, + }, + { + ChainId: "blumbus_111-1", + Aliases: []string{"bb"}, + }, + { + ChainId: s.chainId, + Aliases: []string{"dym"}, + }, + }, + wantErr: false, + wantChainsAliasParams: []dymnstypes.AliasesOfChainId{ + { + ChainId: "cosmoshub-4", + Aliases: []string{"cosmos"}, + }, + { + ChainId: "blumbus_111-2", + Aliases: []string{"bb"}, + }, + { + ChainId: s.chainId, + Aliases: []string{"dym"}, + }, + }, + }, + { + name: "pass - can Dym-Name", + dymNames: []dymnstypes.DymName{ + { + Name: "a", + Owner: addr1a, + Controller: addr1a, + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{{ + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "cosmoshub-3", + Path: "", + Value: cosmos3A, + }}, + }, + { + Name: "b", + Owner: addr1a, + Controller: addr1a, + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{ + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "cosmoshub-3", + Path: "", + Value: cosmos3A, + }, + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "blumbus_111-1", + Path: "", + Value: addr2a, + }, + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "froopyland_100-1", + Path: "", + Value: addr2a, + }, + }, + }, + }, + replacement: []dymnstypes.MigrateChainId{ + { + PreviousChainId: "cosmoshub-3", + NewChainId: "cosmoshub-4", + }, + { + PreviousChainId: "blumbus_111-1", + NewChainId: "blumbus_111-2", + }, + }, + wantErr: false, + wantDymNames: []dymnstypes.DymName{ + { + Name: "a", + Owner: addr1a, + Controller: addr1a, + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{{ + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "cosmoshub-4", + Path: "", + Value: cosmos3A, + }}, + }, + { + Name: "b", + Owner: addr1a, + Controller: addr1a, + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{ + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "cosmoshub-4", + Path: "", + Value: cosmos3A, + }, + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "blumbus_111-2", + Path: "", + Value: addr2a, + }, + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "froopyland_100-1", + Path: "", + Value: addr2a, + }, + }, + }, + }, + }, + { + name: "pass - ignore expired Dym-Name", + dymNames: []dymnstypes.DymName{ + { + Name: "a", + Owner: addr1a, + Controller: addr1a, + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{{ + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "cosmoshub-3", + Path: "", + Value: cosmos3A, + }}, + }, + { + Name: "b", + Owner: addr1a, + Controller: addr1a, + ExpireAt: s.now.Unix() - 1, + Configs: []dymnstypes.DymNameConfig{{ + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "cosmoshub-3", + Path: "", + Value: cosmos3A, + }}, + }, + }, + replacement: []dymnstypes.MigrateChainId{ + { + PreviousChainId: "cosmoshub-3", + NewChainId: "cosmoshub-4", + }, + }, + wantErr: false, + wantDymNames: []dymnstypes.DymName{ + { + Name: "a", + Owner: addr1a, + Controller: addr1a, + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{{ + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "cosmoshub-4", + Path: "", + Value: cosmos3A, + }}, + }, + { + Name: "b", + Owner: addr1a, + Controller: addr1a, + ExpireAt: s.now.Unix() - 1, + Configs: []dymnstypes.DymNameConfig{{ + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "cosmoshub-3", // keep + Path: "", + Value: cosmos3A, + }}, + }, + }, + }, + { + name: "fail - should stop if can not migrate params", + dymNames: []dymnstypes.DymName{ + { + Name: "a", + Owner: addr1a, + Controller: addr1a, + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{{ + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "blumbus_111-1", + Path: "", + Value: addr1a, + }}, + }, + }, + replacement: []dymnstypes.MigrateChainId{ + { + PreviousChainId: "blumbus_111-1", + NewChainId: "dym", // collision with alias + }, + }, + chainsAliasParams: []dymnstypes.AliasesOfChainId{ + { + ChainId: s.chainId, + Aliases: []string{"dym"}, // collision with new chain-id + }, + { + ChainId: "blumbus_111-1", + Aliases: []string{"bb"}, + }, + }, + wantErr: true, + wantErrContains: "chain ID and alias must unique among all, found duplicated", + wantDymNames: []dymnstypes.DymName{ + { + Name: "a", + Owner: addr1a, + Controller: addr1a, + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{{ + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "blumbus_111-1", // not updated + Path: "", + Value: addr1a, + }}, + }, + }, + wantChainsAliasParams: []dymnstypes.AliasesOfChainId{ + // not changed + { + ChainId: s.chainId, + Aliases: []string{"dym"}, + }, + { + ChainId: "blumbus_111-1", + Aliases: []string{"bb"}, + }, + }, + }, + { + name: "fail - should stop if new params does not valid", + replacement: []dymnstypes.MigrateChainId{ + { + PreviousChainId: "blumbus_111-1", + NewChainId: "dym", // collision with alias + }, + }, + chainsAliasParams: []dymnstypes.AliasesOfChainId{ + { + ChainId: s.chainId, + Aliases: []string{"dym"}, // collision with new chain-id + }, + { + ChainId: "blumbus_111-1", + Aliases: []string{"bb"}, + }, + }, + wantErr: true, + wantErrContains: "chain ID and alias must unique among all, found duplicated", + wantChainsAliasParams: []dymnstypes.AliasesOfChainId{ + // not changed + { + ChainId: s.chainId, + Aliases: []string{"dym"}, + }, + { + ChainId: "blumbus_111-1", + Aliases: []string{"bb"}, + }, + }, + }, + { + name: "pass - should complete even tho nothing to update", + dymNames: nil, + replacement: []dymnstypes.MigrateChainId{ + { + PreviousChainId: "cosmoshub-3", + NewChainId: "cosmoshub-4", + }, + }, + wantErr: false, + }, + { + name: "pass - skip migrate alias if new chain-id present, just remove", + replacement: []dymnstypes.MigrateChainId{ + { + PreviousChainId: "cosmoshub-3", + NewChainId: "cosmoshub-4", + }, + }, + chainsAliasParams: []dymnstypes.AliasesOfChainId{ + { + ChainId: s.chainId, + Aliases: []string{"dym"}, + }, + { + ChainId: "cosmoshub-3", + Aliases: []string{"cosmos3"}, + }, + { + ChainId: "cosmoshub-4", + Aliases: []string{"cosmos4"}, + }, + }, + wantErr: false, + wantChainsAliasParams: []dymnstypes.AliasesOfChainId{ + { + ChainId: s.chainId, + Aliases: []string{"dym"}, + }, + { + ChainId: "cosmoshub-4", + Aliases: []string{"cosmos4"}, + }, + }, + }, + { + name: "pass - skip migrate Dym-Name if new record does not pass validation", + dymNames: []dymnstypes.DymName{ + { + Name: "a", + Owner: addr1a, + Controller: addr1a, + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{ + // migrate this will cause non-unique config + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "cosmoshub-3", + Path: "", + Value: cosmos3A, + }, + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "cosmoshub-4", + Path: "", + Value: cosmos3A, + }, + }, + }, + { + Name: "b", + Owner: addr1a, + Controller: addr1a, + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{ + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "cosmoshub-3", + Path: "", + Value: cosmos3A, + }, + }, + }, + { + Name: "c", + Owner: addr1a, + Controller: addr1a, + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{ + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "cosmoshub-4", + Path: "", + Value: cosmos3A, + }, + }, + }, + }, + replacement: []dymnstypes.MigrateChainId{ + { + PreviousChainId: "cosmoshub-3", + NewChainId: "cosmoshub-4", + }, + }, + wantErr: false, + wantDymNames: []dymnstypes.DymName{ + { + Name: "a", + Owner: addr1a, + Controller: addr1a, + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{ + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "cosmoshub-3", // keep + Path: "", + Value: cosmos3A, + }, + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "cosmoshub-4", + Path: "", + Value: cosmos3A, + }, + }, + }, + { + Name: "b", + Owner: addr1a, + Controller: addr1a, + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{ + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "cosmoshub-4", // migrated + Path: "", + Value: cosmos3A, + }, + }, + }, + { + Name: "c", + Owner: addr1a, + Controller: addr1a, + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{ + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "cosmoshub-4", + Path: "", + Value: cosmos3A, + }, + }, + }, + }, + }, + } + for _, tt := range tests { + s.Run(tt.name, func() { + s.RefreshContext() + + s.updateModuleParams(func(moduleParams dymnstypes.Params) dymnstypes.Params { + moduleParams.Chains.AliasesOfChainIds = tt.chainsAliasParams + return moduleParams + }) + + for _, dymName := range tt.dymNames { + s.setDymNameWithFunctionsAfter(dymName) + } + + if tt.additionalSetup != nil { + tt.additionalSetup(s) + } + + err := s.dymNsKeeper.MigrateChainIds(s.ctx, tt.replacement) + + defer func() { + laterModuleParams := s.moduleParams() + if len(tt.wantChainsAliasParams) > 0 || len(laterModuleParams.Chains.AliasesOfChainIds) > 0 { + if !reflect.DeepEqual(tt.wantChainsAliasParams, laterModuleParams.Chains.AliasesOfChainIds) { + s.T().Errorf("alias: want %v, got %v", tt.wantChainsAliasParams, laterModuleParams.Chains.AliasesOfChainIds) + } + } + }() + + defer func() { + for _, wantDymName := range tt.wantDymNames { + laterDymName := s.dymNsKeeper.GetDymName(s.ctx, wantDymName.Name) + s.Require().NotNil(laterDymName) + if !reflect.DeepEqual(wantDymName.Configs, laterDymName.Configs) { + s.T().Errorf("dym name config: want %v, got %v", wantDymName.Configs, laterDymName.Configs) + } + if !reflect.DeepEqual(wantDymName, *laterDymName) { + s.T().Errorf("dym name: want %v, got %v", wantDymName, *laterDymName) + } + } + }() + + if tt.wantErr { + s.Require().NotEmpty(tt.wantErrContains, "mis-configured test case") + s.Require().Error(err) + s.Require().Contains(err.Error(), tt.wantErrContains) + return + } + + s.Require().NoError(err) + }) + } +} + +func (s *KeeperTestSuite) TestKeeper_UpdateAliases() { + tests := []struct { + name string + chainsAliasParams []dymnstypes.AliasesOfChainId + add []dymnstypes.UpdateAlias + remove []dymnstypes.UpdateAlias + wantErr bool + wantErrContains string + wantChainsAliasParams []dymnstypes.AliasesOfChainId + }{ + { + name: "pass - can migrate", + chainsAliasParams: []dymnstypes.AliasesOfChainId{ + { + ChainId: "dymension_1100-1", + Aliases: []string{"dymension"}, + }, + { + ChainId: "blumbus_111-1", + Aliases: []string{"blumbus"}, + }, + { + ChainId: "froopyland_100-1", + Aliases: []string{"fl"}, + }, + }, + add: []dymnstypes.UpdateAlias{ + { + ChainId: "dymension_1100-1", + Alias: "dym", + }, + { + ChainId: "blumbus_111-1", + Alias: "bb", + }, + { + ChainId: "froopyland_100-1", + Alias: "frl", + }, + }, + remove: []dymnstypes.UpdateAlias{ + { + ChainId: "blumbus_111-1", + Alias: "blumbus", + }, + { + ChainId: "froopyland_100-1", + Alias: "fl", + }, + }, + wantErr: false, + wantChainsAliasParams: []dymnstypes.AliasesOfChainId{ + { + ChainId: "blumbus_111-1", + Aliases: []string{"bb"}, + }, + { + ChainId: "dymension_1100-1", + Aliases: []string{"dym", "dymension"}, + }, + { + ChainId: "froopyland_100-1", + Aliases: []string{"frl"}, + }, + }, + }, + { + name: "pass - records are sorted asc by chain-id", + chainsAliasParams: []dymnstypes.AliasesOfChainId{ + { + ChainId: "dymension_1100-1", + Aliases: []string{}, + }, + { + ChainId: "blumbus_111-1", + Aliases: []string{"blumbus"}, + }, + { + ChainId: "froopyland_100-1", + Aliases: []string{"fl"}, + }, + }, + add: []dymnstypes.UpdateAlias{ + { + ChainId: "dymension_1100-1", + Alias: "dym", + }, + { + ChainId: "froopyland_100-1", + Alias: "frl", + }, + }, + wantErr: false, + wantChainsAliasParams: []dymnstypes.AliasesOfChainId{ + { + ChainId: "blumbus_111-1", + Aliases: []string{"blumbus"}, + }, + { + ChainId: "dymension_1100-1", + Aliases: []string{"dym"}, + }, + { + ChainId: "froopyland_100-1", + Aliases: []string{"fl", "frl"}, + }, + }, + }, + { + name: "pass - aliases are sorted asc", + chainsAliasParams: []dymnstypes.AliasesOfChainId{ + { + ChainId: "dymension_1100-1", + Aliases: []string{"d5", "d3"}, + }, + { + ChainId: "blumbus_111-1", + Aliases: []string{"b2"}, + }, + }, + add: []dymnstypes.UpdateAlias{ + { + ChainId: "dymension_1100-1", + Alias: "d4", + }, + { + ChainId: "dymension_1100-1", + Alias: "d1", + }, + { + ChainId: "dymension_1100-1", + Alias: "d2", + }, + { + ChainId: "blumbus_111-1", + Alias: "b4", + }, + { + ChainId: "blumbus_111-1", + Alias: "b1", + }, + { + ChainId: "blumbus_111-1", + Alias: "b3", + }, + }, + remove: []dymnstypes.UpdateAlias{ + { + ChainId: "dymension_1100-1", + Alias: "d5", + }, + }, + wantErr: false, + wantChainsAliasParams: []dymnstypes.AliasesOfChainId{ + { + ChainId: "blumbus_111-1", + Aliases: []string{"b1", "b2", "b3", "b4"}, + }, + { + ChainId: "dymension_1100-1", + Aliases: []string{"d1", "d2", "d3", "d4"}, + }, + }, + }, + { + name: "fail - adding existing alias of same chain-id", + chainsAliasParams: []dymnstypes.AliasesOfChainId{ + { + ChainId: "dymension_1100-1", + Aliases: []string{"d1", "d2"}, + }, + { + ChainId: "blumbus_111-1", + Aliases: []string{"b1"}, + }, + }, + add: []dymnstypes.UpdateAlias{ + { + ChainId: "dymension_1100-1", + Alias: "d1", + }, + { + ChainId: "dymension_1100-1", + Alias: "d3", + }, + }, + wantErr: true, + wantErrContains: "alias d1 already mapped to chain-id dymension_1100-1 in params", + wantChainsAliasParams: []dymnstypes.AliasesOfChainId{ + { + ChainId: "dymension_1100-1", + Aliases: []string{"d1", "d2"}, + }, + { + ChainId: "blumbus_111-1", + Aliases: []string{"b1"}, + }, + }, + }, + { + name: "fail - removing non-existing chain-id", + chainsAliasParams: []dymnstypes.AliasesOfChainId{ + { + ChainId: "dymension_1100-1", + Aliases: []string{"d1"}, + }, + }, + add: []dymnstypes.UpdateAlias{ + { + ChainId: "dymension_1100-1", + Alias: "d2", + }, + }, + remove: []dymnstypes.UpdateAlias{ + { + ChainId: "blumbus_111-1", + Alias: "b1", + }, + }, + wantErr: true, + wantErrContains: "not found to remove", + wantChainsAliasParams: []dymnstypes.AliasesOfChainId{ + { + ChainId: "dymension_1100-1", + Aliases: []string{"d1"}, + }, + }, + }, + { + name: "fail - removing non-existing alias of chain-id", + chainsAliasParams: []dymnstypes.AliasesOfChainId{ + { + ChainId: "dymension_1100-1", + Aliases: []string{"d1", "d2"}, + }, + { + ChainId: "blumbus_111-1", + Aliases: []string{"b1"}, + }, + }, + remove: []dymnstypes.UpdateAlias{ + { + ChainId: "dymension_1100-1", + Alias: "d3", + }, + }, + wantErr: true, + wantErrContains: "alias does not exists in params: d3: not found", + wantChainsAliasParams: []dymnstypes.AliasesOfChainId{ + { + ChainId: "dymension_1100-1", + Aliases: []string{"d1", "d2"}, + }, + { + ChainId: "blumbus_111-1", + Aliases: []string{"b1"}, + }, + }, + }, + { + name: "fail - do not update if params validation failed", + chainsAliasParams: []dymnstypes.AliasesOfChainId{ + { + ChainId: "dymension_1100-1", + Aliases: []string{"dym"}, + }, + }, + add: []dymnstypes.UpdateAlias{ + { + ChainId: "blumbus_111-1", + Alias: "dym", // collision with existing alias + }, + }, + wantErr: true, + wantErrContains: "chain ID and alias must unique among all", + wantChainsAliasParams: []dymnstypes.AliasesOfChainId{ + { + ChainId: "dymension_1100-1", + Aliases: []string{"dym"}, + }, + }, + }, + { + name: "pass - remove records that no more alias", + chainsAliasParams: []dymnstypes.AliasesOfChainId{ + { + ChainId: "dymension_1100-1", + Aliases: []string{"dym", "dymension"}, + }, + { + ChainId: "blumbus_111-1", + Aliases: []string{"blumbus"}, + }, + }, + remove: []dymnstypes.UpdateAlias{ + { + ChainId: "dymension_1100-1", + Alias: "dymension", + }, + { + ChainId: "blumbus_111-1", + Alias: "blumbus", + }, + }, + wantErr: false, + wantChainsAliasParams: []dymnstypes.AliasesOfChainId{ + { + ChainId: "dymension_1100-1", + Aliases: []string{"dym"}, + }, + }, + }, + } + for _, tt := range tests { + s.Run(tt.name, func() { + s.RefreshContext() + + s.updateModuleParams(func(moduleParams dymnstypes.Params) dymnstypes.Params { + moduleParams.Chains.AliasesOfChainIds = tt.chainsAliasParams + return moduleParams + }) + + err := s.dymNsKeeper.UpdateAliases(s.ctx, tt.add, tt.remove) + + laterModuleParams := s.moduleParams() + defer func() { + if s.T().Failed() { + return + } + + if len(tt.wantChainsAliasParams) == 0 { + s.Require().Empty(laterModuleParams.Chains.AliasesOfChainIds) + } else { + s.Require().Equal(tt.wantChainsAliasParams, laterModuleParams.Chains.AliasesOfChainIds) + } + }() + + if tt.wantErr { + s.Require().NotEmpty(tt.wantErrContains, "mis-configured test case") + s.Require().Error(err) + s.Require().Contains(err.Error(), tt.wantErrContains) + return + } + + s.Require().NoError(err) + }) + } +} diff --git a/x/dymns/keeper/refund.go b/x/dymns/keeper/refund.go new file mode 100644 index 000000000..acdb38bbe --- /dev/null +++ b/x/dymns/keeper/refund.go @@ -0,0 +1,103 @@ +package keeper + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" +) + +// GenesisRefundBid refunds the bid in genesis initialization. +// This action will mint coins to the module account and send coins to the bidder. +// The reason for minting is that the module account has no balance during genesis initialization. +func (k Keeper) GenesisRefundBid(ctx sdk.Context, soBid dymnstypes.SellOrderBid) error { + soBid.Params = nil // treat it as refund name orders + return k.refundBid(ctx, soBid, dymnstypes.TypeName, true) +} + +// RefundBid refunds the bid. +// This action will send coins from module account to the bidder. +func (k Keeper) RefundBid(ctx sdk.Context, soBid dymnstypes.SellOrderBid, assetType dymnstypes.AssetType) error { + return k.refundBid(ctx, soBid, assetType, false) +} + +// refundBid refunds the bid. +// Depends on the genesis flag, this action will mint coins to the module account and send coins to the bidder. +func (k Keeper) refundBid(ctx sdk.Context, soBid dymnstypes.SellOrderBid, assetType dymnstypes.AssetType, genesis bool) error { + if err := soBid.Validate(assetType); err != nil { + return err + } + + if genesis { + // During genesis initialization progress, the module account has no balance, so we mint coins. + // Otherwise, the module account should have enough balance to refund the bid. + if err := k.bankKeeper.MintCoins(ctx, dymnstypes.ModuleName, sdk.Coins{soBid.Price}); err != nil { + return err + } + } + + if err := k.bankKeeper.SendCoinsFromModuleToAccount( + ctx, + dymnstypes.ModuleName, + sdk.MustAccAddressFromBech32(soBid.Bidder), + sdk.Coins{soBid.Price}, + ); err != nil { + return err + } + + ctx.EventManager().EmitEvent( + sdk.NewEvent( + dymnstypes.EventTypeSoRefundBid, + sdk.NewAttribute(dymnstypes.AttributeKeySoRefundBidder, soBid.Bidder), + sdk.NewAttribute(dymnstypes.AttributeKeySoRefundAmount, soBid.Price.String()), + ), + ) + + return nil +} + +// GenesisRefundBuyOrder refunds the buy orders in genesis initialization. +// This action will mint coins to the module account and send coins to the buyer. +// The reason for minting is that the module account has no balance during genesis initialization. +func (k Keeper) GenesisRefundBuyOrder(ctx sdk.Context, offer dymnstypes.BuyOrder) error { + return k.refundBuyOrder(ctx, offer, true) +} + +// RefundBuyOrder refunds the deposited amount to the buy order. +// This action will send coins from module account to the buyer. +func (k Keeper) RefundBuyOrder(ctx sdk.Context, offer dymnstypes.BuyOrder) error { + return k.refundBuyOrder(ctx, offer, false) +} + +// refundBuyOrder refunds the buy order. +// Depends on the genesis flag, this action will mint coins to the module account and send coins to the buyer. +func (k Keeper) refundBuyOrder(ctx sdk.Context, offer dymnstypes.BuyOrder, genesis bool) error { + if err := offer.Validate(); err != nil { + return err + } + + if genesis { + // During genesis initialization progress, the module account has no balance, so we mint coins. + // Otherwise, the module account should have enough balance to refund the offer. + if err := k.bankKeeper.MintCoins(ctx, dymnstypes.ModuleName, sdk.Coins{offer.OfferPrice}); err != nil { + return err + } + } + + if err := k.bankKeeper.SendCoinsFromModuleToAccount( + ctx, + dymnstypes.ModuleName, + sdk.MustAccAddressFromBech32(offer.Buyer), + sdk.Coins{offer.OfferPrice}, + ); err != nil { + return err + } + + ctx.EventManager().EmitEvent( + sdk.NewEvent( + dymnstypes.EventTypeBoRefundOffer, + sdk.NewAttribute(dymnstypes.AttributeKeyBoRefundBuyer, offer.Buyer), + sdk.NewAttribute(dymnstypes.AttributeKeyBoRefundAmount, offer.OfferPrice.String()), + ), + ) + + return nil +} diff --git a/x/dymns/keeper/refund_test.go b/x/dymns/keeper/refund_test.go new file mode 100644 index 000000000..3c38db1ba --- /dev/null +++ b/x/dymns/keeper/refund_test.go @@ -0,0 +1,259 @@ +package keeper_test + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" +) + +func (s *KeeperTestSuite) TestKeeper_RefundBid() { + bidderA := testAddr(1).bech32() + + tests := []struct { + name string + refundToAccount string + refundAmount sdk.Coin + fundModuleAccountBalance sdk.Coin + genesis bool + wantErr bool + wantErrContains string + }{ + { + name: "pass - refund bid", + refundToAccount: bidderA, + refundAmount: s.coin(100), + fundModuleAccountBalance: s.coin(150), + genesis: false, + }, + { + name: "pass - refund bid genesis", + refundToAccount: bidderA, + refundAmount: s.coin(100), + fundModuleAccountBalance: s.coin(0), // no need balance, will mint + genesis: true, + wantErr: false, + }, + { + name: "fail - refund bid normally but module account has no balance", + refundToAccount: bidderA, + refundAmount: s.coin(100), + fundModuleAccountBalance: s.coin(0), + genesis: false, + wantErr: true, + wantErrContains: "insufficient funds", + }, + { + name: "fail - refund bid normally but module account does not have enough balance", + refundToAccount: bidderA, + refundAmount: s.coin(100), + fundModuleAccountBalance: s.coin(50), + genesis: false, + wantErr: true, + wantErrContains: "insufficient funds", + }, + { + name: "fail - bad bidder", + refundToAccount: "0x1", + refundAmount: s.coin(100), + fundModuleAccountBalance: s.coin(100), + wantErr: true, + wantErrContains: "SO bidder is not a valid bech32 account address", + }, + } + for _, tt := range tests { + s.Run(tt.name, func() { + s.RefreshContext() + + if !tt.fundModuleAccountBalance.IsNil() { + if !tt.fundModuleAccountBalance.IsZero() { + err := s.bankKeeper.MintCoins(s.ctx, dymnstypes.ModuleName, sdk.Coins{tt.fundModuleAccountBalance}) + s.Require().NoError(err) + } + } + + soBid := dymnstypes.SellOrderBid{ + Bidder: tt.refundToAccount, + Price: tt.refundAmount, + } + + var err error + if tt.genesis { + err = s.dymNsKeeper.GenesisRefundBid(s.ctx, soBid) + } else { + err = s.dymNsKeeper.RefundBid(s.ctx, soBid, dymnstypes.TypeName) + } + + if tt.wantErr { + s.Require().NotEmpty(tt.wantErrContains, "mis-configured test case") + s.Require().Error(err) + s.Require().Contains(err.Error(), tt.wantErrContains) + return + } + + s.Require().NoError(err) + + laterBidderBalance := s.balance2(tt.refundToAccount) + s.Require().Equal(tt.refundAmount.Amount.String(), laterBidderBalance.String()) + + laterDymNsModuleBalance := s.moduleBalance2() + if tt.genesis { + s.Require().True(laterDymNsModuleBalance.IsZero()) + } else { + s.Require().Equal( + tt.fundModuleAccountBalance.Sub(tt.refundAmount).Amount.String(), + laterDymNsModuleBalance.String(), + ) + } + + // event should be fired + events := s.ctx.EventManager().Events() + s.Require().NotEmpty(events) + + var found bool + for _, event := range events { + if event.Type == dymnstypes.EventTypeSoRefundBid { + found = true + break + } + } + + if !found { + s.T().Errorf("event %s not found", dymnstypes.EventTypeSoRefundBid) + } + }) + } +} + +func (s *KeeperTestSuite) TestKeeper_RefundBuyOrder() { + buyerA := testAddr(1).bech32() + + supportedAssetTypes := []dymnstypes.AssetType{ + dymnstypes.TypeName, dymnstypes.TypeAlias, + } + + tests := []struct { + name string + refundToAccount string + refundAmount sdk.Coin + fundModuleAccountBalance sdk.Coin + genesis bool + wantErr bool + wantErrContains string + }{ + { + name: "pass - refund offer", + refundToAccount: buyerA, + refundAmount: s.coin(100), + fundModuleAccountBalance: s.coin(150), + genesis: false, + }, + { + name: "pass - refund offer genesis", + refundToAccount: buyerA, + refundAmount: s.coin(100), + fundModuleAccountBalance: s.coin(0), // no need balance, will mint + genesis: true, + wantErr: false, + }, + { + name: "fail - refund offer normally but module account has no balance", + refundToAccount: buyerA, + refundAmount: s.coin(100), + fundModuleAccountBalance: s.coin(0), + genesis: false, + wantErr: true, + wantErrContains: "insufficient funds", + }, + { + name: "fail - refund offer normally but module account does not have enough balance", + refundToAccount: buyerA, + refundAmount: s.coin(100), + fundModuleAccountBalance: s.coin(50), + genesis: false, + wantErr: true, + wantErrContains: "insufficient funds", + }, + { + name: "fail - bad offer buyer address", + refundToAccount: "0x1", + refundAmount: s.coin(100), + fundModuleAccountBalance: s.coin(100), + wantErr: true, + wantErrContains: "buyer is not a valid bech32 account address", + }, + } + for _, tt := range tests { + s.Run(tt.name, func() { + for _, assetType := range supportedAssetTypes { + s.Run(assetType.PrettyName(), func() { + s.RefreshContext() + + if !tt.fundModuleAccountBalance.IsNil() { + if !tt.fundModuleAccountBalance.IsZero() { + err := s.bankKeeper.MintCoins(s.ctx, dymnstypes.ModuleName, sdk.Coins{tt.fundModuleAccountBalance}) + s.Require().NoError(err) + } + } + + var orderParams []string + if assetType == dymnstypes.TypeAlias { + orderParams = []string{"rollapp_1-1"} + } + + offer := dymnstypes.BuyOrder{ + Id: dymnstypes.CreateBuyOrderId(assetType, 1), + AssetId: "asset", + AssetType: assetType, + Params: orderParams, + Buyer: tt.refundToAccount, + OfferPrice: tt.refundAmount, + } + + var err error + if tt.genesis { + err = s.dymNsKeeper.GenesisRefundBuyOrder(s.ctx, offer) + } else { + err = s.dymNsKeeper.RefundBuyOrder(s.ctx, offer) + } + + if tt.wantErr { + s.Require().NotEmpty(tt.wantErrContains, "mis-configured test case") + s.Require().Error(err) + s.Require().Contains(err.Error(), tt.wantErrContains) + return + } + + s.Require().NoError(err) + + laterBidderBalance := s.balance2(tt.refundToAccount) + s.Require().Equal(tt.refundAmount.Amount.String(), laterBidderBalance.String()) + + laterDymNsModuleBalance := s.moduleBalance2() + if tt.genesis { + s.Require().True(laterDymNsModuleBalance.IsZero()) + } else { + s.Require().Equal( + tt.fundModuleAccountBalance.Sub(tt.refundAmount).Amount.String(), + laterDymNsModuleBalance.String(), + ) + } + + // event should be fired + events := s.ctx.EventManager().Events() + s.Require().NotEmpty(events) + + var found bool + for _, event := range events { + if event.Type == dymnstypes.EventTypeBoRefundOffer { + found = true + break + } + } + + if !found { + s.T().Errorf("event %s not found", dymnstypes.EventTypeBoRefundOffer) + } + }) + } + }) + } +} diff --git a/x/dymns/keeper/rollapp.go b/x/dymns/keeper/rollapp.go new file mode 100644 index 000000000..b75160a82 --- /dev/null +++ b/x/dymns/keeper/rollapp.go @@ -0,0 +1,27 @@ +package keeper + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// IsRollAppId checks if the chain-id is a RollApp-Id. +func (k Keeper) IsRollAppId(ctx sdk.Context, chainId string) bool { + _, found := k.rollappKeeper.GetRollapp(ctx, chainId) + return found +} + +// IsRollAppCreator returns true if the input bech32 address is the creator of the RollApp. +func (k Keeper) IsRollAppCreator(ctx sdk.Context, rollAppId, account string) bool { + rollApp, found := k.rollappKeeper.GetRollapp(ctx, rollAppId) + return found && rollApp.Owner == account +} + +// GetRollAppBech32Prefix returns the Bech32 prefix of the RollApp by the chain-id. +func (k Keeper) GetRollAppBech32Prefix(ctx sdk.Context, chainId string) (bech32Prefix string, found bool) { + rollApp, found := k.rollappKeeper.GetRollapp(ctx, chainId) + if found && len(rollApp.Bech32Prefix) > 0 { + return rollApp.Bech32Prefix, true + } + + return "", false +} diff --git a/x/dymns/keeper/rollapp_test.go b/x/dymns/keeper/rollapp_test.go new file mode 100644 index 000000000..2021d001a --- /dev/null +++ b/x/dymns/keeper/rollapp_test.go @@ -0,0 +1,196 @@ +package keeper_test + +import ( + rollapptypes "github.com/dymensionxyz/dymension/v3/x/rollapp/types" +) + +func (s *KeeperTestSuite) TestKeeper_IsRollAppId() { + s.rollAppKeeper.SetRollapp(s.ctx, rollapptypes.Rollapp{ + RollappId: "rollapp_1-1", + Owner: testAddr(1).bech32(), + }) + + s.rollAppKeeper.SetRollapp(s.ctx, rollapptypes.Rollapp{ + RollappId: "rolling_2-2", + Owner: testAddr(2).bech32(), + }) + + tests := []struct { + rollAppId string + wantIsRollApp bool + }{ + { + rollAppId: "rollapp_1-1", + wantIsRollApp: true, + }, + { + rollAppId: "rolling_2-2", + wantIsRollApp: true, + }, + { + rollAppId: "rollapp_1-11", + wantIsRollApp: false, + }, + { + rollAppId: "rollapp_11-1", + wantIsRollApp: false, + }, + { + rollAppId: "rollapp_11-11", + wantIsRollApp: false, + }, + { + rollAppId: "rollapp_1-2", + wantIsRollApp: false, + }, + { + rollAppId: "rollapp_2-1", + wantIsRollApp: false, + }, + { + rollAppId: "rolling_1-1", + wantIsRollApp: false, + }, + } + for _, tt := range tests { + s.Run(tt.rollAppId, func() { + gotIsRollApp := s.dymNsKeeper.IsRollAppId(s.ctx, tt.rollAppId) + s.Require().Equal(tt.wantIsRollApp, gotIsRollApp) + }) + } +} + +func (s *KeeperTestSuite) TestKeeper_IsRollAppCreator() { + acc1 := testAddr(1) + acc2 := testAddr(2) + + tests := []struct { + name string + rollApp *rollapptypes.Rollapp + rollAppId string + account string + want bool + }{ + { + name: "pass - is creator", + rollApp: &rollapptypes.Rollapp{ + RollappId: "rollapp_1-1", + Owner: acc1.bech32(), + }, + rollAppId: "rollapp_1-1", + account: acc1.bech32(), + want: true, + }, + { + name: "fail - rollapp does not exists", + rollApp: &rollapptypes.Rollapp{ + RollappId: "rollapp_1-1", + Owner: acc1.bech32(), + }, + rollAppId: "nah_2-2", + account: acc1.bech32(), + want: false, + }, + { + name: "fail - is NOT creator", + rollApp: &rollapptypes.Rollapp{ + RollappId: "rollapp_1-1", + Owner: acc1.bech32(), + }, + rollAppId: "rollapp_1-1", + account: acc2.bech32(), + want: false, + }, + { + name: "fail - creator but in different bech32 format is not accepted", + rollApp: &rollapptypes.Rollapp{ + RollappId: "rollapp_1-1", + Owner: acc1.bech32(), + }, + rollAppId: "rollapp_1-1", + account: acc1.bech32C("nim"), + want: false, + }, + } + for _, tt := range tests { + s.Run(tt.name, func() { + s.RefreshContext() + + if tt.rollApp != nil { + s.rollAppKeeper.SetRollapp(s.ctx, *tt.rollApp) + } + + got := s.dymNsKeeper.IsRollAppCreator(s.ctx, tt.rollAppId, tt.account) + s.Require().Equal(tt.want, got) + }) + } + + s.Run("pass - can detect among multiple RollApps of same owned", func() { + s.RefreshContext() + + rollAppABy1 := rollapptypes.Rollapp{ + RollappId: "rollapp_1-1", + Owner: acc1.bech32(), + } + rollAppBBy1 := rollapptypes.Rollapp{ + RollappId: "rollapp_2-2", + Owner: acc1.bech32(), + } + rollAppCBy2 := rollapptypes.Rollapp{ + RollappId: "rollapp_3-3", + Owner: acc2.bech32(), + } + rollAppDBy2 := rollapptypes.Rollapp{ + RollappId: "rollapp_4-4", + Owner: acc2.bech32(), + } + + s.rollAppKeeper.SetRollapp(s.ctx, rollAppABy1) + s.rollAppKeeper.SetRollapp(s.ctx, rollAppBBy1) + s.rollAppKeeper.SetRollapp(s.ctx, rollAppCBy2) + s.rollAppKeeper.SetRollapp(s.ctx, rollAppDBy2) + + s.Require().True(s.dymNsKeeper.IsRollAppCreator(s.ctx, rollAppABy1.RollappId, acc1.bech32())) + s.Require().True(s.dymNsKeeper.IsRollAppCreator(s.ctx, rollAppBBy1.RollappId, acc1.bech32())) + s.Require().True(s.dymNsKeeper.IsRollAppCreator(s.ctx, rollAppCBy2.RollappId, acc2.bech32())) + s.Require().True(s.dymNsKeeper.IsRollAppCreator(s.ctx, rollAppDBy2.RollappId, acc2.bech32())) + + s.Require().False(s.dymNsKeeper.IsRollAppCreator(s.ctx, rollAppABy1.RollappId, acc2.bech32())) + s.Require().False(s.dymNsKeeper.IsRollAppCreator(s.ctx, rollAppBBy1.RollappId, acc2.bech32())) + s.Require().False(s.dymNsKeeper.IsRollAppCreator(s.ctx, rollAppCBy2.RollappId, acc1.bech32())) + s.Require().False(s.dymNsKeeper.IsRollAppCreator(s.ctx, rollAppDBy2.RollappId, acc1.bech32())) + }) +} + +func (s *KeeperTestSuite) TestKeeper_GetRollAppBech32Prefix() { + rollApp1 := rollapptypes.Rollapp{ + RollappId: "rollapp_1-1", + Owner: testAddr(0).bech32(), + Bech32Prefix: "one", + } + rollApp2 := rollapptypes.Rollapp{ + RollappId: "rolling_2-2", + Owner: testAddr(0).bech32(), + Bech32Prefix: "two", + } + rollApp3NonExists := rollapptypes.Rollapp{ + RollappId: "nah_3-3", + Owner: testAddr(0).bech32(), + Bech32Prefix: "nah", + } + + s.rollAppKeeper.SetRollapp(s.ctx, rollApp1) + s.rollAppKeeper.SetRollapp(s.ctx, rollApp2) + + bech32, found := s.dymNsKeeper.GetRollAppBech32Prefix(s.ctx, rollApp1.RollappId) + s.True(found) + s.Equal("one", bech32) + + bech32, found = s.dymNsKeeper.GetRollAppBech32Prefix(s.ctx, rollApp2.RollappId) + s.True(found) + s.Equal("two", bech32) + + bech32, found = s.dymNsKeeper.GetRollAppBech32Prefix(s.ctx, rollApp3NonExists.RollappId) + s.False(found) + s.Empty(bech32) +} diff --git a/x/dymns/keeper/sell_order.go b/x/dymns/keeper/sell_order.go new file mode 100644 index 000000000..552effaa9 --- /dev/null +++ b/x/dymns/keeper/sell_order.go @@ -0,0 +1,149 @@ +package keeper + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" +) + +// SetSellOrder stores a Sell-Order into the KVStore. +func (k Keeper) SetSellOrder(ctx sdk.Context, so dymnstypes.SellOrder) error { + if err := so.Validate(); err != nil { + return err + } + + // micro optimize to save space + if !so.HasSetSellPrice() { + so.SellPrice = nil + } + if so.HighestBid != nil && len(so.HighestBid.Params) == 0 { + so.HighestBid.Params = nil + } + + // persist record + store := ctx.KVStore(k.storeKey) + soKey := dymnstypes.SellOrderKey(so.AssetId, so.AssetType) + bz := k.cdc.MustMarshal(&so) + store.Set(soKey, bz) + + ctx.EventManager().EmitEvent(so.GetSdkEvent(dymnstypes.AttributeValueSoActionNameSet)) + + return nil +} + +// GetSellOrder retrieves active Sell-Order of the corresponding Dym-Name/Alias from the KVStore. +// If the Sell-Order does not exist, nil is returned. +func (k Keeper) GetSellOrder(ctx sdk.Context, + assetId string, assetType dymnstypes.AssetType, +) *dymnstypes.SellOrder { + store := ctx.KVStore(k.storeKey) + soKey := dymnstypes.SellOrderKey(assetId, assetType) + + bz := store.Get(soKey) + if bz == nil { + return nil + } + + var so dymnstypes.SellOrder + k.cdc.MustUnmarshal(bz, &so) + + return &so +} + +// DeleteSellOrder deletes the Sell-Order from the KVStore. +func (k Keeper) DeleteSellOrder(ctx sdk.Context, assetId string, assetType dymnstypes.AssetType) { + so := k.GetSellOrder(ctx, assetId, assetType) + if so == nil { + return + } + + store := ctx.KVStore(k.storeKey) + soKey := dymnstypes.SellOrderKey(assetId, assetType) + store.Delete(soKey) + + ctx.EventManager().EmitEvent(so.GetSdkEvent(dymnstypes.AttributeValueSoActionNameDelete)) +} + +// GetAllSellOrders returns all active Sell-Orders from the KVStore. +// No filter is applied. +// Store iterator is expensive so this function should be used only in Genesis and for testing purpose. +func (k Keeper) GetAllSellOrders(ctx sdk.Context) (list []dymnstypes.SellOrder) { + store := ctx.KVStore(k.storeKey) + + iterator := sdk.KVStorePrefixIterator(store, dymnstypes.KeyPrefixSellOrder) + defer func() { + _ = iterator.Close() // nolint: errcheck + }() + + for ; iterator.Valid(); iterator.Next() { + var so dymnstypes.SellOrder + k.cdc.MustUnmarshal(iterator.Value(), &so) + list = append(list, so) + } + + return list +} + +// SetActiveSellOrdersExpiration stores the expiration of the active Sell-Orders records into the KVStore. +// When a Sell-Order is created, it has an expiration time, later be processed by the batch processing in `x/epochs` hooks, +// instead of iterating through all the Sell-Orders records in store, we store the expiration of the active Sell-Orders, +// so that we can easily find the expired Sell-Orders and conditionally load them to process. +// This expiration list should be maintained accordingly to the Sell-Order CRUD. +func (k Keeper) SetActiveSellOrdersExpiration(ctx sdk.Context, + activeSellOrdersExpiration *dymnstypes.ActiveSellOrdersExpiration, assetType dymnstypes.AssetType, +) error { + activeSellOrdersExpiration.Sort() + + if err := activeSellOrdersExpiration.Validate(); err != nil { + return err + } + + // use key according to asset type + var key []byte + switch assetType { + case dymnstypes.TypeName: + key = dymnstypes.KeyActiveSellOrdersExpirationOfDymName + case dymnstypes.TypeAlias: + key = dymnstypes.KeyActiveSellOrdersExpirationOfAlias + default: + panic("invalid asset type: " + assetType.PrettyName()) + } + + // persist record + store := ctx.KVStore(k.storeKey) + bz := k.cdc.MustMarshal(activeSellOrdersExpiration) + store.Set(key, bz) + return nil +} + +// GetActiveSellOrdersExpiration retrieves the expiration of the active Sell-Orders records +// of the corresponding asset type. +// For more information, see SetActiveSellOrdersExpiration. +func (k Keeper) GetActiveSellOrdersExpiration(ctx sdk.Context, + assetType dymnstypes.AssetType, +) *dymnstypes.ActiveSellOrdersExpiration { + store := ctx.KVStore(k.storeKey) + + // use key according to asset type + var key []byte + switch assetType { + case dymnstypes.TypeName: + key = dymnstypes.KeyActiveSellOrdersExpirationOfDymName + case dymnstypes.TypeAlias: + key = dymnstypes.KeyActiveSellOrdersExpirationOfAlias + default: + panic("invalid asset type: " + assetType.PrettyName()) + } + + var activeSellOrdersExpiration dymnstypes.ActiveSellOrdersExpiration + + bz := store.Get(key) + if bz != nil { + k.cdc.MustUnmarshal(bz, &activeSellOrdersExpiration) + } + + if activeSellOrdersExpiration.Records == nil { + activeSellOrdersExpiration.Records = make([]dymnstypes.ActiveSellOrdersExpirationRecord, 0) + } + + return &activeSellOrdersExpiration +} diff --git a/x/dymns/keeper/sell_order_alias.go b/x/dymns/keeper/sell_order_alias.go new file mode 100644 index 000000000..c322bd9ec --- /dev/null +++ b/x/dymns/keeper/sell_order_alias.go @@ -0,0 +1,77 @@ +package keeper + +import ( + errorsmod "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" + "github.com/dymensionxyz/gerr-cosmos/gerrc" +) + +// CompleteAliasSellOrder completes the active sell order of the Alias, +// give value to the previous owner, and assign alias usage to destination RollApp. +// +// Sell-Order is considered completed when: +// - There is a bid placed, and the Sell-Order has expired. +// - There is a bid placed, and it matches the sell price. +func (k Keeper) CompleteAliasSellOrder(ctx sdk.Context, name string) error { + so := k.GetSellOrder(ctx, name, dymnstypes.TypeAlias) + if so == nil { + return errorsmod.Wrapf(gerrc.ErrNotFound, "Sell-Order: %s", name) + } + + if !so.HasFinishedAtCtx(ctx) { + return errorsmod.Wrap(gerrc.ErrFailedPrecondition, "Sell-Order has not finished yet") + } + + // the SO can be expired at this point, + // in case the highest bid is lower than sell price or no sell price is set, + // so the order is expired, but no logic to complete the SO, then will be completed via hooks + + // validation + + if so.HighestBid == nil { + return errorsmod.Wrap(gerrc.ErrFailedPrecondition, "no bid placed") + } + + existingRollAppIdUsingAlias, found := k.GetRollAppIdByAlias(ctx, so.AssetId) + if !found { + return errorsmod.Wrapf(gerrc.ErrNotFound, "alias not owned by any RollApp: %s", so.AssetId) + } + + existingRollAppUsingAlias, found := k.rollappKeeper.GetRollapp(ctx, existingRollAppIdUsingAlias) + if !found { + return errorsmod.Wrapf(gerrc.ErrNotFound, "RollApp: %s", existingRollAppIdUsingAlias) + } + + destinationRollAppId := so.HighestBid.Params[0] + if !k.IsRollAppId(ctx, destinationRollAppId) { + return errorsmod.Wrapf(gerrc.ErrInvalidArgument, "destination Roll-App does not exists: %s", destinationRollAppId) + } + + // complete the Sell-Order + + // bid placed by the bidder will be transferred to the previous owner + if err := k.bankKeeper.SendCoinsFromModuleToAccount( + ctx, + dymnstypes.ModuleName, + sdk.MustAccAddressFromBech32(existingRollAppUsingAlias.Owner), + sdk.Coins{so.HighestBid.Price}, + ); err != nil { + return err + } + + // remove SO record + k.DeleteSellOrder(ctx, so.AssetId, so.AssetType) + + // unlink from source RollApp + if err := k.RemoveAliasFromRollAppId(ctx, existingRollAppIdUsingAlias, so.AssetId); err != nil { + return err + } + + // link to destination RollApp + if err := k.SetAliasForRollAppId(ctx, destinationRollAppId, so.AssetId); err != nil { + return err + } + + return nil +} diff --git a/x/dymns/keeper/sell_order_alias_test.go b/x/dymns/keeper/sell_order_alias_test.go new file mode 100644 index 000000000..7afd9492f --- /dev/null +++ b/x/dymns/keeper/sell_order_alias_test.go @@ -0,0 +1,402 @@ +package keeper_test + +import ( + "fmt" + + "github.com/dymensionxyz/sdk-utils/utils/uptr" + + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" +) + +//goland:noinspection GoSnakeCaseUsage +func (s *KeeperTestSuite) TestKeeper_CompleteAliasSellOrder() { + creator_1_asOwner := testAddr(1).bech32() + creator_2_asBuyer := testAddr(2).bech32() + + rollApp_1_asSrc := *newRollApp("rollapp_1-1").WithOwner(creator_1_asOwner).WithAlias("alias") + rollApp_2_asSrc := *newRollApp("rollapp_2-1").WithOwner(creator_1_asOwner) + rollApp_3_asDst := *newRollApp("rollapp_3-2").WithOwner(creator_2_asBuyer) + rollApp_4_asDst_byOwner := *newRollApp("rollapp_4-1").WithOwner(creator_1_asOwner).WithAlias("exists") + + s.Run("alias not found", func() { + s.RefreshContext() + + s.persistRollApp(rollApp_2_asSrc) + s.persistRollApp(rollApp_3_asDst) + + so := dymnstypes.SellOrder{ + AssetId: "void", + AssetType: dymnstypes.TypeAlias, + ExpireAt: s.now.Unix() + 100, + MinPrice: s.coin(100), + SellPrice: uptr.To(s.coin(200)), + HighestBid: &dymnstypes.SellOrderBid{ + Bidder: rollApp_3_asDst.owner, + Price: s.coin(200), + Params: []string{rollApp_3_asDst.rollAppId}, + }, + } + err := s.dymNsKeeper.SetSellOrder(s.ctx, so) + s.Require().NoError(err) + + s.Require().ErrorContains(s.dymNsKeeper.CompleteAliasSellOrder(s.ctx, so.AssetId), "alias not owned by any RollApp: void: not found") + }) + + s.Run("destination Roll-App not found", func() { + s.RefreshContext() + + s.persistRollApp(rollApp_1_asSrc) + + const sellPrice = 200 + + so := dymnstypes.SellOrder{ + AssetId: rollApp_1_asSrc.alias, + AssetType: dymnstypes.TypeAlias, + ExpireAt: s.now.Unix() + 100, + MinPrice: s.coin(100), + SellPrice: uptr.To(s.coin(sellPrice)), + HighestBid: &dymnstypes.SellOrderBid{ + Bidder: rollApp_3_asDst.owner, + Price: s.coin(sellPrice), + Params: []string{"nah_0-0"}, + }, + } + err := s.dymNsKeeper.SetSellOrder(s.ctx, so) + s.Require().NoError(err) + + s.Require().ErrorContains(s.dymNsKeeper.CompleteAliasSellOrder(s.ctx, so.AssetId), "destination Roll-App does not exists") + }) + + s.Run("SO not found", func() { + s.RefreshContext() + + s.persistRollApp(rollApp_1_asSrc) + s.persistRollApp(rollApp_3_asDst) + + s.Require().ErrorContains( + s.dymNsKeeper.CompleteAliasSellOrder(s.ctx, rollApp_1_asSrc.alias), + fmt.Sprintf("Sell-Order: %s: not found", rollApp_1_asSrc.alias), + ) + }) + + s.Run("SO not yet completed, no bidder", func() { + s.RefreshContext() + + s.persistRollApp(rollApp_1_asSrc) + s.persistRollApp(rollApp_3_asDst) + + so := dymnstypes.SellOrder{ + AssetId: rollApp_1_asSrc.alias, + AssetType: dymnstypes.TypeAlias, + ExpireAt: s.now.Unix() + 100, + MinPrice: s.coin(100), + } + err := s.dymNsKeeper.SetSellOrder(s.ctx, so) + s.Require().NoError(err) + + s.Require().ErrorContains( + s.dymNsKeeper.CompleteAliasSellOrder(s.ctx, so.AssetId), + "Sell-Order has not finished yet", + ) + }) + + s.Run("SO has bidder but not yet completed", func() { + s.RefreshContext() + + s.persistRollApp(rollApp_1_asSrc) + s.persistRollApp(rollApp_3_asDst) + + so := dymnstypes.SellOrder{ + AssetId: rollApp_1_asSrc.alias, + AssetType: dymnstypes.TypeAlias, + ExpireAt: s.now.Unix() + 100, + MinPrice: s.coin(100), + SellPrice: uptr.To(s.coin(300)), + HighestBid: &dymnstypes.SellOrderBid{ + Bidder: rollApp_3_asDst.owner, + Price: s.coin(200), // lower than sell price + Params: []string{rollApp_3_asDst.rollAppId}, + }, + } + err := s.dymNsKeeper.SetSellOrder(s.ctx, so) + s.Require().NoError(err) + + s.Require().ErrorContains( + s.dymNsKeeper.CompleteAliasSellOrder(s.ctx, so.AssetId), + "Sell-Order has not finished yet", + ) + }) + + s.Run("SO expired without bidder", func() { + s.RefreshContext() + + s.persistRollApp(rollApp_1_asSrc) + s.persistRollApp(rollApp_3_asDst) + + so := dymnstypes.SellOrder{ + AssetId: rollApp_1_asSrc.alias, + AssetType: dymnstypes.TypeAlias, + ExpireAt: s.now.Unix() - 1, + MinPrice: s.coin(100), + SellPrice: uptr.To(s.coin(300)), + } + err := s.dymNsKeeper.SetSellOrder(s.ctx, so) + s.Require().NoError(err) + + s.Require().ErrorContains(s.dymNsKeeper.CompleteAliasSellOrder(s.ctx, so.AssetId), "no bid placed") + }) + + s.Run("SO without sell price, with bid, finished by expiry", func() { + s.RefreshContext() + + s.persistRollApp(rollApp_1_asSrc) + s.persistRollApp(rollApp_3_asDst) + + so := dymnstypes.SellOrder{ + AssetId: rollApp_1_asSrc.alias, + AssetType: dymnstypes.TypeAlias, + ExpireAt: s.now.Unix() + 100, + MinPrice: s.coin(100), + HighestBid: &dymnstypes.SellOrderBid{ + Bidder: rollApp_3_asDst.owner, + Price: s.coin(200), + Params: []string{rollApp_3_asDst.rollAppId}, + }, + } + err := s.dymNsKeeper.SetSellOrder(s.ctx, so) + s.Require().NoError(err) + + s.Require().ErrorContains(s.dymNsKeeper.CompleteAliasSellOrder(s.ctx, so.AssetId), "Sell-Order has not finished yet") + }) + + const ownerOriginalBalance int64 = 1000 + const buyerOriginalBalance int64 = 500 + tests := []struct { + name string + expiredSO bool + sellPrice int64 + bid int64 + wantErr bool + wantErrContains string + wantOwnerBalanceLater int64 + }{ + { + name: "pass - completed, expired, no sell price", + expiredSO: true, + sellPrice: 0, + bid: 200, + wantErr: false, + wantOwnerBalanceLater: ownerOriginalBalance + 200, + }, + { + name: "pass - completed, expired, under sell price", + expiredSO: true, + sellPrice: 300, + bid: 200, + wantErr: false, + wantOwnerBalanceLater: ownerOriginalBalance + 200, + }, + { + name: "pass - completed, expired, equals sell price", + expiredSO: true, + sellPrice: 300, + bid: 300, + wantErr: false, + wantOwnerBalanceLater: ownerOriginalBalance + 300, + }, + { + name: "pass - completed by sell-price met, not expired", + expiredSO: false, + sellPrice: 300, + bid: 300, + wantErr: false, + wantOwnerBalanceLater: ownerOriginalBalance + 300, + }, + { + name: "fail - expired without bid, no sell price", + expiredSO: true, + sellPrice: 0, + bid: 0, + wantErr: true, + wantErrContains: "no bid placed", + wantOwnerBalanceLater: ownerOriginalBalance, + }, + { + name: "fail - expired without bid, with sell price", + expiredSO: true, + sellPrice: 300, + bid: 0, + wantErr: true, + wantErrContains: "no bid placed", + wantOwnerBalanceLater: ownerOriginalBalance, + }, + { + name: "fail - not expired but bid under sell price", + expiredSO: false, + sellPrice: 300, + bid: 200, + wantErr: true, + wantErrContains: "Sell-Order has not finished yet", + wantOwnerBalanceLater: ownerOriginalBalance, + }, + { + name: "fail - not expired has bid, no sell price", + expiredSO: false, + sellPrice: 0, + bid: 200, + wantErr: true, + wantErrContains: "Sell-Order has not finished yet", + wantOwnerBalanceLater: ownerOriginalBalance, + }, + } + for _, tt := range tests { + s.Run(tt.name, func() { + // setup execution context + s.RefreshContext() + + s.mintToAccount(creator_1_asOwner, ownerOriginalBalance) + s.mintToAccount(creator_2_asBuyer, buyerOriginalBalance) + + s.persistRollApp(rollApp_1_asSrc) + s.persistRollApp(rollApp_3_asDst) + + so := dymnstypes.SellOrder{ + AssetId: rollApp_1_asSrc.alias, + AssetType: dymnstypes.TypeAlias, + MinPrice: s.coin(100), + } + + if tt.expiredSO { + so.ExpireAt = s.now.Unix() - 1 + } else { + so.ExpireAt = s.now.Unix() + 1 + } + + s.Require().GreaterOrEqual(tt.sellPrice, int64(0), "bad setup") + so.SellPrice = uptr.To(s.coin(tt.sellPrice)) + + s.Require().GreaterOrEqual(tt.bid, int64(0), "bad setup") + if tt.bid > 0 { + so.HighestBid = &dymnstypes.SellOrderBid{ + Bidder: rollApp_3_asDst.owner, + Price: s.coin(tt.bid), + Params: []string{rollApp_3_asDst.rollAppId}, + } + + // mint coin to module account because we charged buyer before update SO + s.mintToModuleAccount2(so.HighestBid.Price.Amount) + } + err := s.dymNsKeeper.SetSellOrder(s.ctx, so) + s.Require().NoError(err) + + rollApp1, found := s.rollAppKeeper.GetRollapp(s.ctx, rollApp_1_asSrc.rollAppId) + s.Require().True(found) + rollApp2, found := s.rollAppKeeper.GetRollapp(s.ctx, rollApp_3_asDst.rollAppId) + s.Require().True(found) + + // test + + errCompleteSellOrder := s.dymNsKeeper.CompleteAliasSellOrder(s.ctx, so.AssetId) + + laterSo := s.dymNsKeeper.GetSellOrder(s.ctx, so.AssetId, dymnstypes.TypeAlias) + + laterOwnerBalance := s.balance(creator_1_asOwner) + laterBuyerBalance := s.balance(creator_2_asBuyer) + + laterAliasOfRollApp1, _ := s.dymNsKeeper.GetAliasByRollAppId(s.ctx, rollApp_1_asSrc.rollAppId) + laterAliasOfRollApp2, _ := s.dymNsKeeper.GetAliasByRollAppId(s.ctx, rollApp_3_asDst.rollAppId) + + laterAliasLinkedToRollAppId, found := s.dymNsKeeper.GetRollAppIdByAlias(s.ctx, rollApp_1_asSrc.alias) + s.Require().True(found) + + laterRollApp1, found := s.rollAppKeeper.GetRollapp(s.ctx, rollApp_1_asSrc.rollAppId) + s.Require().True(found, "rollapp should be kept") + s.Require().Equal(rollApp1, laterRollApp1, "rollapp should not be changed") + laterRollApp2, found := s.rollAppKeeper.GetRollapp(s.ctx, rollApp_3_asDst.rollAppId) + s.Require().True(found, "rollapp should be kept") + s.Require().Equal(rollApp2, laterRollApp2, "rollapp should not be changed") + + if tt.wantErr { + s.Require().Error(errCompleteSellOrder, "action should be failed") + s.Require().NotEmpty(tt.wantErrContains, "mis-configured test case") + s.Require().Contains(errCompleteSellOrder.Error(), tt.wantErrContains) + + s.Require().NotNil(laterSo, "SO should not be deleted") + + s.Require().Equal(rollApp_1_asSrc.alias, laterAliasOfRollApp1, "alias should be kept") + s.Require().Equal(rollApp_1_asSrc.rollAppId, laterAliasLinkedToRollAppId, "alias should be kept") + s.Require().Empty(laterAliasOfRollApp2, "should not be linked to RollApp 2") + + s.Require().Equal(ownerOriginalBalance, laterOwnerBalance, "owner balance should not be changed") + s.Require().Equal(tt.wantOwnerBalanceLater, laterOwnerBalance, "owner balance mis-match") + s.Require().Equal(buyerOriginalBalance, laterBuyerBalance, "buyer balance should not be changed") + return + } + + s.Require().NoError(errCompleteSellOrder, "action should be successful") + + s.Require().Nil(laterSo, "SO should be deleted") + + s.Require().Empty(laterAliasOfRollApp1, "should not be linked to RollApp 1 anymore") + s.Require().Equal(rollApp_3_asDst.rollAppId, laterAliasLinkedToRollAppId, "alias should be linked to RollApp 2") + + s.Require().Equal(tt.wantOwnerBalanceLater, laterOwnerBalance, "owner balance mis-match") + s.Require().Equal(buyerOriginalBalance, laterBuyerBalance, "buyer balance should not be changed") + }) + } + + s.Run("if buyer is owner, can still process", func() { + s.RefreshContext() + + const ownerOriginalBalance = 100 + const moduleAccountOriginalBalance = 1000 + const offerValue = 300 + + s.mintToModuleAccount(moduleAccountOriginalBalance) + s.mintToAccount(creator_1_asOwner, ownerOriginalBalance) + + s.persistRollApp(rollApp_1_asSrc) + s.persistRollApp(rollApp_4_asDst_byOwner) + + so := dymnstypes.SellOrder{ + AssetId: rollApp_1_asSrc.alias, + AssetType: dymnstypes.TypeAlias, + ExpireAt: s.now.Unix() + 100, + MinPrice: s.coin(100), + SellPrice: uptr.To(s.coin(offerValue)), + HighestBid: &dymnstypes.SellOrderBid{ + Bidder: rollApp_4_asDst_byOwner.owner, + Price: s.coin(offerValue), + Params: []string{rollApp_4_asDst_byOwner.rollAppId}, + }, + } + + err := s.dymNsKeeper.SetSellOrder(s.ctx, so) + s.Require().NoError(err) + + err = s.dymNsKeeper.CompleteAliasSellOrder(s.ctx, so.AssetId) + s.Require().NoError(err) + + // Alias should be transferred as normal + laterAliasOfRollApp1, _ := s.dymNsKeeper.GetAliasByRollAppId(s.ctx, rollApp_1_asSrc.rollAppId) + laterAliasOfRollApp3, _ := s.dymNsKeeper.GetAliasByRollAppId(s.ctx, rollApp_4_asDst_byOwner.rollAppId) + laterAliasLinkedToRollAppId, _ := s.dymNsKeeper.GetRollAppIdByAlias(s.ctx, so.AssetId) + + s.Require().Empty(laterAliasOfRollApp1, "should not be linked to RollApp 1 anymore") + s.Require().Equal(rollApp_4_asDst_byOwner.alias, laterAliasOfRollApp3, "alias should be linked to RollApp 3") + s.Require().Equal(rollApp_4_asDst_byOwner.rollAppId, laterAliasLinkedToRollAppId, "alias should be linked to RollApp 3") + + // ensure all existing alias are linked to the correct RollApp + for _, alias := range []string{rollApp_4_asDst_byOwner.alias, so.AssetId} { + s.requireAlias(alias).LinkedToRollApp(rollApp_4_asDst_byOwner.rollAppId) + } + + // owner receives the offer amount because owner also the buyer + laterOwnerBalance := s.balance(creator_1_asOwner) + s.Require().Equal(int64(offerValue+ownerOriginalBalance), laterOwnerBalance) + + // SO records should be processed as normal + laterSo := s.dymNsKeeper.GetSellOrder(s.ctx, so.AssetId, dymnstypes.TypeAlias) + s.Require().Nil(laterSo, "SO should be deleted") + }) +} diff --git a/x/dymns/keeper/sell_order_name.go b/x/dymns/keeper/sell_order_name.go new file mode 100644 index 000000000..2d747835d --- /dev/null +++ b/x/dymns/keeper/sell_order_name.go @@ -0,0 +1,97 @@ +package keeper + +import ( + errorsmod "cosmossdk.io/errors" + + sdk "github.com/cosmos/cosmos-sdk/types" + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" + "github.com/dymensionxyz/gerr-cosmos/gerrc" +) + +// CompleteDymNameSellOrder completes the active sell order of the Dym-Name, +// give value to the previous owner, and transfer ownership to new owner. +// +// Sell-Order is considered completed when: +// - There is a bid placed, and the Sell-Order has expired. +// - There is a bid placed, and it matches the sell price. +func (k Keeper) CompleteDymNameSellOrder(ctx sdk.Context, name string) error { + dymName := k.GetDymName(ctx, name) + if dymName == nil { + return errorsmod.Wrapf(gerrc.ErrNotFound, "Dym-Name: %s", name) + } + + // here we don't check Dym-Name expiration, because it can not happen, + // and there is a grace period for the owner to renew the Dym-Name in case bad things happen + + so := k.GetSellOrder(ctx, name, dymnstypes.TypeName) + if so == nil { + return errorsmod.Wrapf(gerrc.ErrNotFound, "Sell-Order: %s", name) + } + + if !so.HasFinishedAtCtx(ctx) { + return errorsmod.Wrap(gerrc.ErrFailedPrecondition, "Sell-Order has not finished yet") + } + + // the SO can be expired at this point, + // in case the highest bid is lower than sell price or no sell price is set, + // so the order is expired, but no logic to complete the SO, then will be completed via hooks + + if so.HighestBid == nil { + return errorsmod.Wrap(gerrc.ErrFailedPrecondition, "no bid placed") + } + + newOwner := so.HighestBid.Bidder + + // complete the Sell-Order + + previousOwner := dymName.Owner + + // bid placed by the bidder will be transferred to the previous owner + + if err := k.bankKeeper.SendCoinsFromModuleToAccount( + ctx, + dymnstypes.ModuleName, + sdk.MustAccAddressFromBech32(previousOwner), + sdk.Coins{so.HighestBid.Price}, + ); err != nil { + return err + } + + // remove SO record + k.DeleteSellOrder(ctx, so.AssetId, so.AssetType) + + // transfer ownership + + // remove the existing reverse mapping + + if err := k.BeforeDymNameOwnerChanged(ctx, dymName.Name); err != nil { + return err + } + + if err := k.BeforeDymNameConfigChanged(ctx, dymName.Name); err != nil { + return err + } + + // update Dym records to prevent any potential mistake + dymName.Owner = newOwner // ownership transfer + dymName.Controller = newOwner // new owner becomes the controller + dymName.Configs = nil // clear all configs + dymName.Contact = "" // clear contact + + // persist updated DymName + if err := k.SetDymName(ctx, *dymName); err != nil { + return err + } + + // update reverse mapping + + if err := k.AfterDymNameOwnerChanged(ctx, dymName.Name); err != nil { + return err + } + + if err := k.AfterDymNameConfigChanged(ctx, dymName.Name); err != nil { + return err + } + + return nil +} diff --git a/x/dymns/keeper/sell_order_name_test.go b/x/dymns/keeper/sell_order_name_test.go new file mode 100644 index 000000000..8763123c2 --- /dev/null +++ b/x/dymns/keeper/sell_order_name_test.go @@ -0,0 +1,388 @@ +package keeper_test + +import ( + "fmt" + + "github.com/dymensionxyz/sdk-utils/utils/uptr" + + sdk "github.com/cosmos/cosmos-sdk/types" + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" +) + +func (s *KeeperTestSuite) TestKeeper_CompleteDymNameSellOrder() { + ownerA := testAddr(1).bech32() + buyerA := testAddr(2).bech32() + const contactEmail = "contact@example.com" + + dymName := dymnstypes.DymName{ + Name: "a", + Owner: ownerA, + Controller: ownerA, + ExpireAt: s.now.Unix() + 100, + Contact: contactEmail, + } + + s.Run("Dym-Name not found", func() { + s.Require().ErrorContains( + s.dymNsKeeper.CompleteDymNameSellOrder(s.ctx, "non-exists"), + "Dym-Name: non-exists: not found", + ) + }) + + s.Run("SO not found", func() { + err := s.dymNsKeeper.SetDymName(s.ctx, dymName) + s.Require().NoError(err) + + s.Require().ErrorContains( + s.dymNsKeeper.CompleteDymNameSellOrder(s.ctx, dymName.Name), + fmt.Sprintf("Sell-Order: %s: not found", dymName.Name), + ) + }) + + s.Run("SO not yet completed, no bidder", func() { + s.RefreshContext() + + err := s.dymNsKeeper.SetDymName(s.ctx, dymName) + s.Require().NoError(err) + + so := dymnstypes.SellOrder{ + AssetId: dymName.Name, + AssetType: dymnstypes.TypeName, + ExpireAt: s.now.Unix() + 100, + MinPrice: s.coin(100), + } + err = s.dymNsKeeper.SetSellOrder(s.ctx, so) + s.Require().NoError(err) + + s.Require().ErrorContains(s.dymNsKeeper.CompleteDymNameSellOrder(s.ctx, dymName.Name), "Sell-Order has not finished yet") + }) + + s.Run("SO has bidder but not yet completed", func() { + s.RefreshContext() + + err := s.dymNsKeeper.SetDymName(s.ctx, dymName) + s.Require().NoError(err) + + so := dymnstypes.SellOrder{ + AssetId: dymName.Name, + AssetType: dymnstypes.TypeName, + ExpireAt: s.now.Unix() + 100, + MinPrice: s.coin(100), + SellPrice: uptr.To(s.coin(300)), + HighestBid: &dymnstypes.SellOrderBid{ + Bidder: buyerA, + Price: s.coin(200), // lower than sell price + }, + } + err = s.dymNsKeeper.SetSellOrder(s.ctx, so) + s.Require().NoError(err) + + s.Require().ErrorContains(s.dymNsKeeper.CompleteDymNameSellOrder(s.ctx, dymName.Name), "Sell-Order has not finished yet") + }) + + s.Run("SO expired without bidder", func() { + s.RefreshContext() + + err := s.dymNsKeeper.SetDymName(s.ctx, dymName) + s.Require().NoError(err) + + so := dymnstypes.SellOrder{ + AssetId: dymName.Name, + AssetType: dymnstypes.TypeName, + ExpireAt: s.now.Unix() - 1, + MinPrice: s.coin(100), + SellPrice: uptr.To(s.coin(300)), + } + err = s.dymNsKeeper.SetSellOrder(s.ctx, so) + s.Require().NoError(err) + + s.Require().ErrorContains(s.dymNsKeeper.CompleteDymNameSellOrder(s.ctx, dymName.Name), "no bid placed") + }) + + s.Run("SO without sell price, with bid, finished by expiry", func() { + s.RefreshContext() + + err := s.dymNsKeeper.SetDymName(s.ctx, dymName) + s.Require().NoError(err) + + so := dymnstypes.SellOrder{ + AssetId: dymName.Name, + AssetType: dymnstypes.TypeName, + ExpireAt: s.now.Unix() + 100, + MinPrice: s.coin(100), + HighestBid: &dymnstypes.SellOrderBid{ + Bidder: buyerA, + Price: s.coin(200), + }, + } + err = s.dymNsKeeper.SetSellOrder(s.ctx, so) + s.Require().NoError(err) + + s.Require().ErrorContains(s.dymNsKeeper.CompleteDymNameSellOrder(s.ctx, dymName.Name), "Sell-Order has not finished yet") + }) + + const ownerOriginalBalance int64 = 1000 + const buyerOriginalBalance int64 = 500 + tests := []struct { + name string + expiredSO bool + sellPrice int64 + bid int64 + wantErr bool + wantErrContains string + wantOwnerBalanceLater int64 + }{ + { + name: "pass - completed, expired, no sell price", + expiredSO: true, + sellPrice: 0, + bid: 200, + wantErr: false, + wantOwnerBalanceLater: ownerOriginalBalance + 200, + }, + { + name: "pass - completed, expired, under sell price", + expiredSO: true, + sellPrice: 300, + bid: 200, + wantErr: false, + wantOwnerBalanceLater: ownerOriginalBalance + 200, + }, + { + name: "pass - completed, expired, equals sell price", + expiredSO: true, + sellPrice: 300, + bid: 300, + wantErr: false, + wantOwnerBalanceLater: ownerOriginalBalance + 300, + }, + { + name: "pass - completed by sell-price met, not expired", + expiredSO: false, + sellPrice: 300, + bid: 300, + wantErr: false, + wantOwnerBalanceLater: ownerOriginalBalance + 300, + }, + { + name: "fail - expired without bid, no sell price", + expiredSO: true, + sellPrice: 0, + bid: 0, + wantErr: true, + wantErrContains: "no bid placed", + wantOwnerBalanceLater: ownerOriginalBalance, + }, + { + name: "fail - expired without bid, with sell price", + expiredSO: true, + sellPrice: 300, + bid: 0, + wantErr: true, + wantErrContains: "no bid placed", + wantOwnerBalanceLater: ownerOriginalBalance, + }, + { + name: "fail - not expired but bid under sell price", + expiredSO: false, + sellPrice: 300, + bid: 200, + wantErr: true, + wantErrContains: "Sell-Order has not finished yet", + wantOwnerBalanceLater: ownerOriginalBalance, + }, + { + name: "fail - not expired has bid, no sell price", + expiredSO: false, + sellPrice: 0, + bid: 200, + wantErr: true, + wantErrContains: "Sell-Order has not finished yet", + wantOwnerBalanceLater: ownerOriginalBalance, + }, + } + for _, tt := range tests { + s.Run(tt.name, func() { + // setup execution context + s.RefreshContext() + + s.mintToAccount(ownerA, ownerOriginalBalance) + s.mintToAccount(buyerA, buyerOriginalBalance) + + dymName.Configs = []dymnstypes.DymNameConfig{ + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + Value: ownerA, + }, + } + s.setDymNameWithFunctionsAfter(dymName) + + so := dymnstypes.SellOrder{ + AssetId: dymName.Name, + AssetType: dymnstypes.TypeName, + MinPrice: s.coin(100), + } + + if tt.expiredSO { + so.ExpireAt = s.now.Unix() - 1 + } else { + so.ExpireAt = s.now.Unix() + 1 + } + + s.Require().GreaterOrEqual(tt.sellPrice, int64(0), "bad setup") + so.SellPrice = uptr.To(s.coin(tt.sellPrice)) + + s.Require().GreaterOrEqual(tt.bid, int64(0), "bad setup") + if tt.bid > 0 { + so.HighestBid = &dymnstypes.SellOrderBid{ + Bidder: buyerA, + Price: s.coin(tt.bid), + } + + // mint coin to module account because we charged buyer before update SO + err := s.bankKeeper.MintCoins(s.ctx, dymnstypes.ModuleName, sdk.NewCoins(so.HighestBid.Price)) + s.Require().NoError(err) + } + err := s.dymNsKeeper.SetSellOrder(s.ctx, so) + s.Require().NoError(err) + + // test + + errCompleteSellOrder := s.dymNsKeeper.CompleteDymNameSellOrder(s.ctx, dymName.Name) + laterDymName := s.dymNsKeeper.GetDymName(s.ctx, dymName.Name) + s.Require().NotNil(laterDymName) + laterSo := s.dymNsKeeper.GetSellOrder(s.ctx, dymName.Name, dymnstypes.TypeName) + laterOwnerBalance := s.balance(ownerA) + laterBuyerBalance := s.balance(buyerA) + laterDymNamesOwnedByOwner, err := s.dymNsKeeper.GetDymNamesOwnedBy(s.ctx, ownerA) + s.Require().NoError(err) + laterDymNamesOwnedByBuyer, err := s.dymNsKeeper.GetDymNamesOwnedBy(s.ctx, buyerA) + s.Require().NoError(err) + laterConfiguredAddressOwnerDymNames, err := s.dymNsKeeper.GetDymNamesContainsConfiguredAddress(s.ctx, ownerA) + s.Require().NoError(err) + laterConfiguredAddressBuyerDymNames, err := s.dymNsKeeper.GetDymNamesContainsConfiguredAddress(s.ctx, buyerA) + s.Require().NoError(err) + laterFallbackAddressOwnerDymNames, err := s.dymNsKeeper.GetDymNamesContainsFallbackAddress(s.ctx, sdk.MustAccAddressFromBech32(ownerA).Bytes()) + s.Require().NoError(err) + laterFallbackAddressBuyerDymNames, err := s.dymNsKeeper.GetDymNamesContainsFallbackAddress(s.ctx, sdk.MustAccAddressFromBech32(buyerA).Bytes()) + s.Require().NoError(err) + + s.Require().Equal(dymName.Name, laterDymName.Name, "name should not be changed") + s.Require().Equal(dymName.ExpireAt, laterDymName.ExpireAt, "expiry should not be changed") + + if tt.wantErr { + s.Require().Error(errCompleteSellOrder, "action should be failed") + s.Require().NotEmpty(tt.wantErrContains, "mis-configured test case") + s.Require().Contains(errCompleteSellOrder.Error(), tt.wantErrContains) + + s.Require().NotNil(laterSo, "SO should not be deleted") + + s.Require().Equal(ownerA, laterDymName.Owner, "ownership should not be changed") + s.Require().Equal(ownerA, laterDymName.Controller, "controller should not be changed") + s.Require().NotEmpty(laterDymName.Configs, "configs should be kept") + s.Require().Equal(dymName.Configs, laterDymName.Configs, "configs not be changed") + s.Require().Equal(contactEmail, dymName.Contact, "contact should not be changed") + s.Require().Len(laterDymNamesOwnedByOwner, 1, "reverse record should be kept") + s.Require().Empty(laterDymNamesOwnedByBuyer, "reverse record should not be added") + s.Require().Len(laterConfiguredAddressOwnerDymNames, 1, "reverse record should be kept") + s.Require().Empty(laterConfiguredAddressBuyerDymNames, "reverse record should not be added") + s.Require().Len(laterFallbackAddressOwnerDymNames, 1, "reverse record should be kept") + s.Require().Empty(laterFallbackAddressBuyerDymNames, "reverse record should not be added") + + s.Require().Equal(ownerOriginalBalance, laterOwnerBalance, "owner balance should not be changed") + s.Require().Equal(tt.wantOwnerBalanceLater, laterOwnerBalance, "owner balance mis-match") + s.Require().Equal(buyerOriginalBalance, laterBuyerBalance, "buyer balance should not be changed") + return + } + + s.Require().NoError(errCompleteSellOrder, "action should be successful") + + s.Require().Nil(laterSo, "SO should be deleted") + + s.Require().Equal(buyerA, laterDymName.Owner, "ownership should be changed") + s.Require().Equal(buyerA, laterDymName.Controller, "controller should be changed") + s.Require().Empty(laterDymName.Configs, "configs should be cleared") + s.Require().Empty(laterDymName.Contact, "contact should be cleared") + s.Require().Empty(laterDymNamesOwnedByOwner, "reverse record should be removed") + s.Require().Len(laterDymNamesOwnedByBuyer, 1, "reverse record should be added") + s.Require().Empty(laterConfiguredAddressOwnerDymNames, "reverse record should be removed") + s.Require().Len(laterConfiguredAddressBuyerDymNames, 1, "reverse record should be added") + s.Require().Empty(laterFallbackAddressOwnerDymNames, "reverse record should be removed") + s.Require().Len(laterFallbackAddressBuyerDymNames, 1, "reverse record should be added") + + s.Require().Equal(tt.wantOwnerBalanceLater, laterOwnerBalance, "owner balance mis-match") + s.Require().Equal(buyerOriginalBalance, laterBuyerBalance, "buyer balance should not be changed") + }) + } + + s.Run("if buyer is owner, can still process", func() { + s.RefreshContext() + + const ownerOriginalBalance = 100 + const moduleAccountOriginalBalance = 1000 + const offerValue = 300 + + s.mintToModuleAccount(moduleAccountOriginalBalance) + s.mintToAccount(ownerA, ownerOriginalBalance) + + dymName := dymnstypes.DymName{ + Name: "a", + Owner: ownerA, + Controller: testAddr(3).bech32(), + ExpireAt: s.now.Unix() + 100, + Contact: contactEmail, + } + s.Require().NotEqual(ownerA, dymName.Controller, "bad setup") + s.setDymNameWithFunctionsAfter(dymName) + + so := dymnstypes.SellOrder{ + AssetId: dymName.Name, + AssetType: dymnstypes.TypeName, + ExpireAt: s.now.Unix() + 100, + MinPrice: s.coin(100), + SellPrice: uptr.To(s.coin(offerValue)), + HighestBid: &dymnstypes.SellOrderBid{ + Bidder: ownerA, + Price: s.coin(offerValue), + }, + } + + err := s.dymNsKeeper.SetSellOrder(s.ctx, so) + s.Require().NoError(err) + + err = s.dymNsKeeper.CompleteDymNameSellOrder(s.ctx, dymName.Name) + s.Require().NoError(err) + + // Dym-Name should be updated as normal + laterDymName := s.dymNsKeeper.GetDymName(s.ctx, dymName.Name) + s.Require().NotNil(laterDymName) + s.Require().Equal(ownerA, laterDymName.Owner, "ownership should be kept because buyer and owner is the same") + s.Require().Equal(ownerA, laterDymName.Controller, "controller should be changed to owner as standard") + s.Require().Empty(laterDymName.Configs, "configs should be cleared") + s.Require().Empty(laterDymName.Contact, "contact should be cleared") + + // owner receives the offer amount because owner also the buyer + laterOwnerBalance := s.balance(ownerA) + s.Require().Equal(int64(offerValue+ownerOriginalBalance), laterOwnerBalance) + + // reverse records should be kept + laterDymNamesOwnedByOwner, err := s.dymNsKeeper.GetDymNamesOwnedBy(s.ctx, ownerA) + s.Require().NoError(err) + s.Require().Len(laterDymNamesOwnedByOwner, 1) + s.Require().Equal(dymName.Name, laterDymNamesOwnedByOwner[0].Name) + + laterConfiguredAddressOwnerDymNames, err := s.dymNsKeeper.GetDymNamesContainsConfiguredAddress(s.ctx, ownerA) + s.Require().NoError(err) + s.Require().Len(laterConfiguredAddressOwnerDymNames, 1) + s.Require().Equal(dymName.Name, laterConfiguredAddressOwnerDymNames[0].Name) + + laterFallbackAddressOwnerDymNames, err := s.dymNsKeeper.GetDymNamesContainsFallbackAddress(s.ctx, sdk.MustAccAddressFromBech32(ownerA).Bytes()) + s.Require().NoError(err) + s.Require().Len(laterFallbackAddressOwnerDymNames, 1) + s.Require().Equal(dymName.Name, laterFallbackAddressOwnerDymNames[0].Name) + + // SO records should be processed as normal + laterSo := s.dymNsKeeper.GetSellOrder(s.ctx, dymName.Name, dymnstypes.TypeName) + s.Require().Nil(laterSo, "SO should be deleted") + }) +} diff --git a/x/dymns/keeper/sell_order_test.go b/x/dymns/keeper/sell_order_test.go new file mode 100644 index 000000000..0d2b9d169 --- /dev/null +++ b/x/dymns/keeper/sell_order_test.go @@ -0,0 +1,475 @@ +package keeper_test + +import ( + cryptorand "crypto/rand" + "fmt" + "math/big" + + "github.com/dymensionxyz/sdk-utils/utils/uptr" + + sdk "github.com/cosmos/cosmos-sdk/types" + + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" +) + +func (s *KeeperTestSuite) TestKeeper_GetSetSellOrder() { + supportedAssetTypes := []dymnstypes.AssetType{ + dymnstypes.TypeName, dymnstypes.TypeAlias, + } + + s.Run("reject invalid SO", func() { + err := s.dymNsKeeper.SetSellOrder(s.ctx, dymnstypes.SellOrder{}) + s.Require().Error(err) + }) + + s.Run("can set", func() { + for _, assetType := range supportedAssetTypes { + s.Run(assetType.PrettyName(), func() { + s.RefreshContext() + + so := dymnstypes.SellOrder{ + AssetId: "asset", + AssetType: assetType, + ExpireAt: 1, + MinPrice: s.coin(100), + } + err := s.dymNsKeeper.SetSellOrder(s.ctx, so) + s.Require().NoError(err) + }) + } + }) + + s.Run("event should be fired on set", func() { + for _, assetType := range supportedAssetTypes { + s.Run(assetType.PrettyName(), func() { + s.RefreshContext() + + so := dymnstypes.SellOrder{ + AssetId: "asset", + AssetType: assetType, + ExpireAt: 1, + MinPrice: s.coin(100), + } + err := s.dymNsKeeper.SetSellOrder(s.ctx, so) + s.Require().NoError(err) + + events := s.ctx.EventManager().Events() + s.Require().NotEmpty(events) + + func(events sdk.Events) { + for _, event := range events { + if event.Type != dymnstypes.EventTypeSellOrder { + continue + } + + var actionName string + for _, attr := range event.Attributes { + if attr.Key == dymnstypes.AttributeKeySoActionName { + actionName = attr.Value + } + } + s.Require().NotEmpty(actionName, "event attr action name could not be found") + s.Require().Equalf( + actionName, dymnstypes.AttributeValueSoActionNameSet, + "event attr action name should be `%s`", dymnstypes.AttributeValueSoActionNameSet, + ) + return + } + + s.T().Errorf("event %s not found", dymnstypes.EventTypeSellOrder) + }(events) + }) + } + }) + + s.Run("can not set Sell-Order with unknown type", func() { + s.RefreshContext() + + err := s.dymNsKeeper.SetSellOrder(s.ctx, dymnstypes.SellOrder{ + AssetId: "asset", + AssetType: dymnstypes.AssetType_AT_UNKNOWN, + ExpireAt: 1, + MinPrice: s.coin(100), + }) + s.Require().ErrorContains(err, "invalid SO type") + }) + + s.Run("non-exists returns nil", func() { + s.RefreshContext() + + for _, assetType := range supportedAssetTypes { + s.Require().Nil(s.dymNsKeeper.GetSellOrder(s.ctx, "asset", assetType)) + } + }) + + s.Run("omit Sell Price if zero", func() { + for _, sellPrice := range []*sdk.Coin{nil, uptr.To(s.coin(0)), {}} { + for _, assetType := range supportedAssetTypes { + s.Run(assetType.PrettyName(), func() { + s.RefreshContext() + + err := s.dymNsKeeper.SetSellOrder(s.ctx, dymnstypes.SellOrder{ + AssetId: "asset", + AssetType: assetType, + ExpireAt: 1, + MinPrice: s.coin(100), + SellPrice: sellPrice, + }) + s.Require().NoError(err) + + s.Require().Nil(s.dymNsKeeper.GetSellOrder(s.ctx, "asset", assetType).SellPrice) + }) + } + } + }) + + s.Run("omit Bid params if empty", func() { + s.RefreshContext() + + err := s.dymNsKeeper.SetSellOrder(s.ctx, dymnstypes.SellOrder{ + AssetId: "asset", + AssetType: dymnstypes.TypeName, + ExpireAt: 1, + MinPrice: s.coin(100), + HighestBid: &dymnstypes.SellOrderBid{ + Bidder: testAddr(1).bech32(), + Price: s.coin(200), + Params: []string{}, + }, + }) + s.Require().NoError(err) + + s.Require().Nil(s.dymNsKeeper.GetSellOrder(s.ctx, "asset", dymnstypes.TypeName).HighestBid.Params) + }) + + s.Run("get returns correct inserted record, regardless type", func() { + s.RefreshContext() + + var sellOrders []dymnstypes.SellOrder + + const seed int = 100 + + for i := 0; i < seed; i++ { + for _, assetType := range supportedAssetTypes { + + so := dymnstypes.SellOrder{ + AssetId: fmt.Sprintf("dog%d", i), // same asset id for all types + AssetType: assetType, + ExpireAt: 1 + int64(i), + MinPrice: s.coin(int64(seed + i)), + } + err := s.dymNsKeeper.SetSellOrder(s.ctx, so) + s.Require().NoError(err) + + sellOrders = append(sellOrders, so) + } + } + + for _, so := range sellOrders { + got := s.dymNsKeeper.GetSellOrder(s.ctx, so.AssetId, so.AssetType) + s.Require().NotNil(got) + s.Require().Equal(so, *got) + } + }) +} + +func (s *KeeperTestSuite) TestKeeper_DeleteSellOrder() { + supportedAssetTypes := []dymnstypes.AssetType{ + dymnstypes.TypeName, dymnstypes.TypeAlias, + } + + s.Run("can delete", func() { + for _, assetType := range supportedAssetTypes { + s.Run(assetType.PrettyName(), func() { + s.RefreshContext() + + so := dymnstypes.SellOrder{ + AssetId: "asset", + AssetType: assetType, + ExpireAt: 1, + MinPrice: s.coin(1), + } + + err := s.dymNsKeeper.SetSellOrder(s.ctx, so) + s.Require().NoError(err) + + s.Require().NotNil(s.dymNsKeeper.GetSellOrder(s.ctx, so.AssetId, so.AssetType)) + + s.dymNsKeeper.DeleteSellOrder(s.ctx, so.AssetId, so.AssetType) + + s.Require().Nil(s.dymNsKeeper.GetSellOrder(s.ctx, so.AssetId, so.AssetType)) + }) + } + }) + + s.Run("event should be fired upon deletion", func() { + for _, assetType := range supportedAssetTypes { + s.Run(assetType.PrettyName(), func() { + s.RefreshContext() + + so := dymnstypes.SellOrder{ + AssetId: "asset", + AssetType: assetType, + ExpireAt: 1, + MinPrice: s.coin(1), + } + + err := s.dymNsKeeper.SetSellOrder(s.ctx, so) + s.Require().NoError(err) + + s.dymNsKeeper.DeleteSellOrder(s.ctx, so.AssetId, so.AssetType) + + events := s.ctx.EventManager().Events() + s.Require().NotEmpty(events) + + func(events sdk.Events) { + for _, event := range events { + if event.Type != dymnstypes.EventTypeSellOrder { + continue + } + + var actionName string + for _, attr := range event.Attributes { + if attr.Key == dymnstypes.AttributeKeySoActionName { + actionName = attr.Value + } + } + s.Require().NotEmpty(actionName, "event attr action name could not be found") + s.Require().Equalf( + actionName, dymnstypes.AttributeValueSoActionNameSet, + "event attr action name should be `%s`", dymnstypes.AttributeValueSoActionNameDelete, + ) + return + } + + s.T().Errorf("event %s not found", dymnstypes.EventTypeSellOrder) + }(events) + }) + } + }) + + s.Run("delete remove the correct record regardless type", func() { + s.RefreshContext() + + type testCase struct { + so dymnstypes.SellOrder + deleted bool + } + + var testCases []*testCase + + // build testcases + const seed int = 100 + for i := 0; i < seed; i++ { + for j, assetType := range supportedAssetTypes { + so := dymnstypes.SellOrder{ + AssetId: fmt.Sprintf("dog%03d%d", i, j), + AssetType: assetType, + ExpireAt: 1, + MinPrice: s.coin(1), + } + + err := s.dymNsKeeper.SetSellOrder(s.ctx, so) + s.Require().NoError(err) + + s.Require().NotNil(s.dymNsKeeper.GetSellOrder(s.ctx, so.AssetId, so.AssetType)) + + testCases = append(testCases, &testCase{so: so, deleted: false}) + } + } + + s.Require().Len(testCases, seed*len(supportedAssetTypes)) + s.Require().Len(s.dymNsKeeper.GetAllSellOrders(s.ctx), len(testCases)) + + // test delete + for i, tc := range testCases { + s.dymNsKeeper.DeleteSellOrder(s.ctx, tc.so.AssetId, tc.so.AssetType) + tc.deleted = true + s.Require().Nil(s.dymNsKeeper.GetSellOrder(s.ctx, tc.so.AssetId, tc.so.AssetType)) + + s.Require().Len(s.dymNsKeeper.GetAllSellOrders(s.ctx), len(testCases)-(i+1)) + + for _, tc2 := range testCases { + if tc2.deleted { + s.Require().Nil(s.dymNsKeeper.GetSellOrder(s.ctx, tc2.so.AssetId, tc2.so.AssetType)) + } else { + s.Require().NotNil(s.dymNsKeeper.GetSellOrder(s.ctx, tc2.so.AssetId, tc2.so.AssetType)) + } + } + } + }) +} + +func (s *KeeperTestSuite) TestKeeper_GetSetActiveSellOrdersExpiration() { + s.RefreshContext() + + supportedAssetTypes := []dymnstypes.AssetType{ + dymnstypes.TypeName, dymnstypes.TypeAlias, + } + + s.Run("get", func() { + for _, assetType := range supportedAssetTypes { + aSoe := s.dymNsKeeper.GetActiveSellOrdersExpiration(s.ctx, assetType) + s.Require().Empty(aSoe.Records, "default list must be empty") + s.Require().NotNil(aSoe.Records, "list must be initialized") + } + }) + + s.Run("set", func() { + for _, assetType := range supportedAssetTypes { + aSoe := &dymnstypes.ActiveSellOrdersExpiration{ + Records: []dymnstypes.ActiveSellOrdersExpirationRecord{ + { + AssetId: "hello", + ExpireAt: 123, + }, + { + AssetId: "world", + ExpireAt: 456, + }, + }, + } + err := s.dymNsKeeper.SetActiveSellOrdersExpiration(s.ctx, aSoe, assetType) + s.Require().NoError(err) + + aSoe = s.dymNsKeeper.GetActiveSellOrdersExpiration(s.ctx, assetType) + s.Require().Len(aSoe.Records, 2) + s.Require().Equal("hello", aSoe.Records[0].AssetId) + s.Require().Equal(int64(123), aSoe.Records[0].ExpireAt) + s.Require().Equal("world", aSoe.Records[1].AssetId) + s.Require().Equal(int64(456), aSoe.Records[1].ExpireAt) + } + }) + + s.Run("must automatically sort when set", func() { + for _, assetType := range supportedAssetTypes { + err := s.dymNsKeeper.SetActiveSellOrdersExpiration(s.ctx, &dymnstypes.ActiveSellOrdersExpiration{ + Records: []dymnstypes.ActiveSellOrdersExpirationRecord{ + { + AssetId: "bbb", + ExpireAt: 456, + }, + { + AssetId: "aaa", + ExpireAt: 123, + }, + }, + }, assetType) + s.Require().NoError(err) + + aSoe := s.dymNsKeeper.GetActiveSellOrdersExpiration(s.ctx, assetType) + s.Require().Len(aSoe.Records, 2) + + s.Require().Equal("aaa", aSoe.Records[0].AssetId) + s.Require().Equal(int64(123), aSoe.Records[0].ExpireAt) + s.Require().Equal("bbb", aSoe.Records[1].AssetId) + s.Require().Equal(int64(456), aSoe.Records[1].ExpireAt) + } + }) + + s.Run("can not set if set is not valid", func() { + for _, assetType := range supportedAssetTypes { + // not unique + err := s.dymNsKeeper.SetActiveSellOrdersExpiration(s.ctx, &dymnstypes.ActiveSellOrdersExpiration{ + Records: []dymnstypes.ActiveSellOrdersExpirationRecord{ + { + AssetId: "dup", + ExpireAt: 456, + }, + { + AssetId: "dup", + ExpireAt: 123, + }, + }, + }, assetType) + s.Require().Error(err) + + // zero expiry + err = s.dymNsKeeper.SetActiveSellOrdersExpiration(s.ctx, &dymnstypes.ActiveSellOrdersExpiration{ + Records: []dymnstypes.ActiveSellOrdersExpirationRecord{ + { + AssetId: "alice", + ExpireAt: -1, + }, + { + AssetId: "bob", + ExpireAt: 0, + }, + }, + }, assetType) + s.Require().Error(err) + } + }) + + s.Run("each asset type persists separately", func() { + err := s.dymNsKeeper.SetActiveSellOrdersExpiration(s.ctx, &dymnstypes.ActiveSellOrdersExpiration{ + Records: []dymnstypes.ActiveSellOrdersExpirationRecord{ + { + AssetId: "asset", + ExpireAt: 1, + }, + }, + }, dymnstypes.TypeName) + s.Require().NoError(err) + + err = s.dymNsKeeper.SetActiveSellOrdersExpiration(s.ctx, &dymnstypes.ActiveSellOrdersExpiration{ + Records: []dymnstypes.ActiveSellOrdersExpirationRecord{ + { + AssetId: "asset", + ExpireAt: 2, + }, + }, + }, dymnstypes.TypeAlias) + s.Require().NoError(err) + + listName := s.dymNsKeeper.GetActiveSellOrdersExpiration(s.ctx, dymnstypes.TypeName) + s.Require().Len(listName.Records, 1) + s.Require().Equal("asset", listName.Records[0].AssetId) + s.Require().Equal(int64(1), listName.Records[0].ExpireAt) + + listAlias := s.dymNsKeeper.GetActiveSellOrdersExpiration(s.ctx, dymnstypes.TypeAlias) + s.Require().Len(listAlias.Records, 1) + s.Require().Equal("asset", listAlias.Records[0].AssetId) + s.Require().Equal(int64(2), listAlias.Records[0].ExpireAt) + }) +} + +func (s *KeeperTestSuite) TestKeeper_GetAllSellOrders() { + supportedAssetTypes := []dymnstypes.AssetType{ + dymnstypes.TypeName, dymnstypes.TypeAlias, + } + + s.RefreshContext() + + var sellOrders []dymnstypes.SellOrder + + n, err := cryptorand.Int(cryptorand.Reader, big.NewInt(1000)) + s.Require().NoError(err) + + seed := 200 + int(n.Int64()) + + for i := 0; i < seed; i++ { + for j, assetType := range supportedAssetTypes { + so := dymnstypes.SellOrder{ + AssetId: fmt.Sprintf("dog%03d%d", i, j), + AssetType: assetType, + ExpireAt: 1, + MinPrice: s.coin(1), + } + err := s.dymNsKeeper.SetSellOrder(s.ctx, so) + s.Require().NoError(err) + + sellOrders = append(sellOrders, so) + } + } + + s.Require().Len(sellOrders, seed*len(supportedAssetTypes)) + + allSellOrders := s.dymNsKeeper.GetAllSellOrders(s.ctx) + + s.Require().Len(allSellOrders, len(sellOrders), "should returns all inserted records") + + for _, so := range sellOrders { + s.Require().Contains(allSellOrders, so) + } +} diff --git a/x/dymns/keeper/showcase_test.go b/x/dymns/keeper/showcase_test.go new file mode 100644 index 000000000..7d97af88b --- /dev/null +++ b/x/dymns/keeper/showcase_test.go @@ -0,0 +1,1158 @@ +package keeper_test + +import ( + "sort" + "strings" + "time" + + dymnsutils "github.com/dymensionxyz/dymension/v3/x/dymns/utils" + + sdk "github.com/cosmos/cosmos-sdk/types" + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" + rollapptypes "github.com/dymensionxyz/dymension/v3/x/rollapp/types" + "github.com/ethereum/go-ethereum/common" +) + +/** +This file contains showcases of how the DymNS config, + resolve from Dym-Name-Address to address (forward-resolve), + reverse-resolve from address to Dym-Name-Address, + and how alias working in DymNS module. + +Why resolve Dym-Name-Address to address? + This is the main feature of DymNS, it allows users to resolve Dym-Name-Address to the configured address. + For example: resolve "my-name@dym" to "dym1fl48..." + +Why reverse-resolve address to Dym-Name-Address? + This is the feature that allows users to find the Dym-Name-Address from the address, + mostly used in UI to show the Dym-Name-Address to the user or integrated into other services like Block Explorer. + For example: reverse-resolve "dym1fl48..." to "my-name@dym" +*/ + +//goland:noinspection SpellCheckingInspection +func (s *KeeperTestSuite) TestKeeper_NewRegistration() { + /** + This show how Dym-Name record look like in store, after new registration + And basic resolve Dym-Name-Address & reverse-resolve address to the Dym-Name-Address + */ + + sc := setupShowcase(s) + + s.Require().Equal("dymension", sc.s.ctx.ChainID()) // our chain-id is "dymension" + + dymNameExpirationDate := sc.s.now.Add(365 * 24 * time.Hour) + + sc. + newDymName( + // name of the Dym-Name + "my-name", + // the owner of the Dym-Name + "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + ). + withExpiry(dymNameExpirationDate). + save() + + s.Run("this show how the new Dym-Name record look like after new registration", func() { + sc.requireDymName("my-name").equals( + dymnstypes.DymName{ + Name: "my-name", + Owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + Controller: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", // default controller is the owner + ExpireAt: dymNameExpirationDate.Unix(), + Configs: nil, // default no config + Contact: "", // default no contact + }, + ) + }) + + s.Run("this show how resolve Dym-Name-Address look like for brand-new Dym-Name", func() { + // resolve "my-name@dymension" to "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue" + sc. + requireResolveDymNameAddress("my-name@dymension"). + Equals("dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue") + }) + + s.Run("this show how reverse-resolve to a Dym-Name-Address look like for brand-new Dym-Name", func() { + // resolve "my-name@dymension" to "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue" + // and reverse-resolve is to resolve from "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue" back to "my-name@dymension" + + sc. + requireReverseResolve("dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue").forChainId("dymension"). + equals("my-name@dymension") + + // reverse lookup from 0x address + ownerIn0xFormat := common.BytesToAddress(sdk.MustAccAddressFromBech32("dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue")).String() + s.Require().Equal("0x4feA76427B8345861e80A3540a8a9D936FD39391", ownerIn0xFormat) + sc. + requireReverseResolve(ownerIn0xFormat).forChainId("dymension"). + equals("my-name@dymension") + // reverse lookup from 0x address has some limitation, I'll provide more details at later parts + }) + + s.Run("this show how resolve address across all RollApps", func() { + dymName := sc.requireDymName("my-name").get() + ownerAccAddr := sdk.MustAccAddressFromBech32(dymName.Owner) + + // register RollApps with their accounts bech32 prefix + rollAppONE := sc.newRollApp("one_1-1").withBech32Prefix("one").save() + rollAppTWO := sc.newRollApp("two_2-2").withBech32Prefix("two").save() + rollAppWithoutBech32 := sc.newRollApp("nob_3-3").withoutBech32Prefix().save() + + // convert owner address to RollApp's bech32 prefix + ownerWithONEPrefix := sdk.MustBech32ifyAddressBytes("one", ownerAccAddr) + s.Require().Equal("one1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3668wjg", ownerWithONEPrefix) + ownerWithTWOPrefix := sdk.MustBech32ifyAddressBytes("two", ownerAccAddr) + s.Require().Equal("two1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3y4eefh", ownerWithTWOPrefix) + + sc. + requireResolveDymNameAddress("my-name@" + rollAppONE.RollappId). + Equals(ownerWithONEPrefix) + + sc. + requireResolveDymNameAddress("my-name@" + rollAppTWO.RollappId). + Equals(ownerWithTWOPrefix) + + // do not resolve for RollApp without bech32 prefix + sc. + requireResolveDymNameAddress("my-name@" + rollAppWithoutBech32.RollappId). + NoResult() + }) + + s.Run("when Dym-Name is expired, resolution won't work in both ways", func() { + dymName := sc.requireDymName("my-name").get() + dymName.ExpireAt = sc.s.now.Add(-1 * time.Hour).Unix() + sc.requireDymName("my-name").update(*dymName) + + // resolve address don't work anymore + sc. + requireResolveDymNameAddress("my-name@dymension"). + NoResult() + + // reverse-resolve address don't work anymore + sc. + requireReverseResolve("dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue").forChainId("dymension"). + NoResult() + }) + + // Friendly notes for non-technical readers: + // _________________________________________ + testAccount := sc.newTestAccount() // this creates a test account, which support procedure multiple address format + // _________________________________________ + _ = testAccount.bech32() // this will output the address in bech32 format with "dym" prefix + // look like this: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue" + // _________________________________________ + _ = testAccount.bech32C("rol") // this will output the address in bech32 format with "rol" prefix + // look like this: "rol1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3n0r7hx" + // _________________________________________ + _ = testAccount.hexStr() // this will output the address in 0x format + // look like this: "0x4fea76427b8345861e80a3540a8a9d936fd39391" + // _________________________________________ + _ = testAccount.checksumHex() // this will output the address in 0x format, with checksum + // look like this: "0x4feA76427B8345861e80A3540a8a9D936FD39391" // similar hex but with mixed case for checksum + // _________________________________________ + // 4 formats of the same address, but in different format + // will be used many times later. +} + +//goland:noinspection SpellCheckingInspection +func (s *KeeperTestSuite) TestKeeper_DefaultDymNameConfiguration() { + /** + This adds more information about the default resolution + and how things change after update the default resolution + + Default resolution is config where chain-id = host-chain, no sub-name. + + * limit to the default only * + */ + + sc := setupShowcase(s) + + s.Require().Equal("dymension", sc.s.ctx.ChainID()) // our chain-id is "dymension" + + owner := "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue" + + sc. + newDymName( + "my-name", // name of the Dym-Name + owner, // the owner of the Dym-Name + ). + save() + + s.Run("default resolution", func() { + // by default, there is no configuration for configuration + dymName := sc.requireDymName("my-name").get() + s.Require().Empty(dymName.Configs) // no config record + + // so when query Dym-Name-Address, it will resolve to the Owner + sc. + requireResolveDymNameAddress("my-name@dymension"). + Equals(owner) + // so as reverse-resolve can find Dym-Name-Address from the Owner address + sc. + requireReverseResolve(owner).forChainId("dymension"). + equals("my-name@dymension") + // reverse-resolve using 0x address + sc. + requireReverseResolve("0x4feA76427B8345861e80A3540a8a9D936FD39391"). // still owner, just in 0x format + forChainId("dymension"). + equals("my-name@dymension") + + randomTestAccount := sc.newTestAccount() + // reverse-resolve other addresses will return no result at this point + sc.requireReverseResolve(randomTestAccount.bech32()).forChainId("dymension").NoResult() + + // why? Underthe hood, Dym-Name without any config equals with the one have a default config, which look like this + dymName.Configs = []dymnstypes.DymNameConfig{ + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "", // empty for host-chain + Path: "", + Value: owner, + }, + } + s.Require().Len(dymName.Configs, 1) // ignore this line + }) + + s.Run("this is how things changed after we change the default resolution", func() { + dymName := sc.requireDymName("my-name").get() + + // create a new test account + notTheOwner := sc.newTestAccount() + + dymName.Configs = []dymnstypes.DymNameConfig{ + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "", // empty for host-chain + Path: "", + Value: notTheOwner.bech32(), // changed to another account + }, + } + + sc.requireDymName("my-name").update(*dymName) + + // reverse-resolve no longer returns result for search of Dym-Name-Address using the Owner address + sc.requireReverseResolve(owner).forChainId("dymension").NoResult() + + // Dym-Name-Address now resolves to the new account + sc. + requireResolveDymNameAddress("my-name@dymension"). + Equals(notTheOwner.bech32()) // look + sc. + requireResolveDymNameAddress("my-name@dymension"). + NotEquals(owner) // NO longer the owner + + // Reverse-resolve will return the Dym-Name-Address when lookup by the new account + sc. + requireReverseResolve(notTheOwner.bech32()). + forChainId("dymension"). + equals("my-name@dymension") + // and 0x + sc. + requireReverseResolve(notTheOwner.hexStr() /* 0x */). + forChainId("dymension"). + equals("my-name@dymension") + }) +} + +//goland:noinspection SpellCheckingInspection +func (s *KeeperTestSuite) TestKeeper_DymNameConfiguration() { + /** + This show how Dym-Name record look like in store, after update configuration resolution + And resolve Dym-Name-Address & reverse-resolve address to the Dym-Name-Address in a more complex way + */ + + sc := setupShowcase(s) + + s.Require().Equal("dymension", sc.s.ctx.ChainID()) // our chain-id is "dymension" + + owner := sc.newTestAccount() // account 1 + + dymName := sc. + newDymName( + "my-name", + owner.bech32(), + ). + save() + + const rollAppId = "rollapp_1-1" + const rollAppBech32Prefix = "rol" + _ = sc. + newRollApp(rollAppId). + withBech32Prefix(rollAppBech32Prefix). + save() + + // update the resolution configuration + // vvvvvvv please PAY ATTENTION on this section vvvvvvv + /// account 2 + anotherUser2 := sc.newTestAccount() + updateDymName(dymName).resolveTo(anotherUser2.bech32()).onChain("dymension").withSubName("sub-name-host").add() + sc.addLaterTest(func() { + sc.requireResolveDymNameAddress("sub-name-host.my-name@dymension").Equals(anotherUser2.bech32()) + // on host-chain, configured address is case-insensitive + sc.requireReverseResolve( + anotherUser2.bech32(), + swapCase(anotherUser2.bech32()), + ).forChainId("dymension").equals("sub-name-host.my-name@dymension") + // able to reverse-lookup using 0x address on host-chain + _0xAddr := anotherUser2.hexStr() + sc.requireReverseResolve( + _0xAddr, + swapCase(_0xAddr), + ).forChainId("dymension").equals("sub-name-host.my-name@dymension") + }) + /// account 3 + anotherUser3 := sc.newTestAccount() + updateDymName(dymName).resolveTo(anotherUser3.bech32C("cosmos")).onChain("cosmoshub-4").add() + sc.addLaterTest(func() { + sc.requireResolveDymNameAddress("my-name@cosmoshub-4").Equals(anotherUser3.bech32C("cosmos")) + sc.requireReverseResolve(anotherUser3.bech32C("cosmos")).forChainId("cosmoshub-4").equals("my-name@cosmoshub-4") + // on non-host and non-RollApp, configured address is case-sensitive + swappedCase := swapCase(anotherUser3.bech32C("cosmos")) + sc.requireReverseResolve(swappedCase).forChainId("cosmoshub-4").NoResult() + // not able to reverse-lookup using 0x address on non-host chain + _0xAddr := anotherUser3.hexStr() + sc.requireReverseResolve(_0xAddr).forChainId("cosmoshub-4").NoResult() + }) + /// account 4 + anotherUser4 := sc.newTestAccount() + updateDymName(dymName).resolveTo(anotherUser4.bech32C("cosmos")).onChain("cosmoshub-4").withSubName("sub-name").add() + sc.addLaterTest(func() { + sc.requireResolveDymNameAddress("sub-name.my-name@cosmoshub-4").Equals(anotherUser4.bech32C("cosmos")) + sc.requireReverseResolve(anotherUser4.bech32C("cosmos")).forChainId("cosmoshub-4").equals("sub-name.my-name@cosmoshub-4") + }) + /// account 5 + anotherUser5 := sc.newTestAccount() + updateDymName(dymName).resolveTo(anotherUser5.bech32C(rollAppBech32Prefix)).onChain(rollAppId).add() + sc.addLaterTest(func() { + sc.requireResolveDymNameAddress("my-name@" + rollAppId).Equals(anotherUser5.bech32C(rollAppBech32Prefix)) + // on RollApp, configured address is case-insensitive + sc.requireReverseResolve( + anotherUser5.bech32C(rollAppBech32Prefix), + swapCase(anotherUser5.bech32C(rollAppBech32Prefix)), + ).forChainId(rollAppId).equals("my-name@" + rollAppId) + // able to reverse-lookup using 0x address on RollApp + _0xAddr := anotherUser5.hexStr() + sc.requireReverseResolve( + _0xAddr, + swapCase(_0xAddr), + ).forChainId(rollAppId).equals("my-name@" + rollAppId) + }) + /// account 6 + anotherUser6 := sc.newTestAccount() + updateDymName(dymName).resolveTo(anotherUser6.bech32C(rollAppBech32Prefix)).withSubName("sub-rol").onChain(rollAppId).add() + sc.addLaterTest(func() { + sc.requireResolveDymNameAddress("sub-rol.my-name@" + rollAppId).Equals(anotherUser6.bech32C(rollAppBech32Prefix)) + sc.requireReverseResolve(anotherUser6.bech32C(rollAppBech32Prefix)).forChainId(rollAppId).equals("sub-rol.my-name@" + rollAppId) + _0xAddr := anotherUser6.hexStr() + sc.requireReverseResolve(_0xAddr).forChainId(rollAppId).equals("sub-rol.my-name@" + rollAppId) + }) + /// account 7 is a Bitcoin address + anotherUser7 := "12higDjoCCNXSA95xZMWUdPvXNmkAduhWv" + updateDymName(dymName).resolveTo(anotherUser7).onChain("bitcoin").add() + sc.addLaterTest(func() { + sc.requireResolveDymNameAddress("my-name@bitcoin").Equals(anotherUser7) + sc.requireReverseResolve(anotherUser7).forChainId("bitcoin").equals("my-name@bitcoin") + // on non-host and non-RollApp, configured address is case-sensitive + swappedCase := swapCase(anotherUser7) + sc.requireReverseResolve(swappedCase).forChainId("bitcoin").NoResult() + }) + /// account 8 is an Ethereum checksum address (mixed case) + anotherUser8WithChecksum := "0x4838B106FCe9647Bdf1E7877BF73cE8B0BAD5f97" + updateDymName(dymName).resolveTo(anotherUser8WithChecksum).onChain("ethereum").add() + sc.addLaterTest(func() { + /* + Just above, we know that, on non-host and non-RollApp, configured address is case-sensitive + But there is an exception: if the address is welformed a hex address, start with 0x, + then treat the chain is case-insensitive address. + */ + + lowercase := strings.ToLower(anotherUser8WithChecksum) + uppercase := "0x" + strings.ToUpper(anotherUser8WithChecksum[2:]) + swappedCase := "0x" + swapCase(anotherUser8WithChecksum[2:]) + sc.requireResolveDymNameAddress("my-name@ethereum").Equals(lowercase) + sc.requireReverseResolve( + anotherUser8WithChecksum, + lowercase, + uppercase, + swappedCase, + ).forChainId("ethereum").equals("my-name@ethereum") + }) + // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + sc.requireDymName("my-name").update(*dymName) + + s.Run("this show how the new Dym-Name record look like after updated", func() { + sc.requireDymName("my-name").equals( + dymnstypes.DymName{ + Name: "my-name", + Owner: dymName.Owner, // unchanged + Controller: dymName.Controller, // unchanged + ExpireAt: dymName.ExpireAt, // unchanged + Contact: "", + Configs: []dymnstypes.DymNameConfig{ + // Note: there is no default configuration record here, + // therefore my-name@dymension will resolve to the owner (account 1) + + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "sub-name-host", + Value: anotherUser2.bech32(), // account 2 + }, + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "cosmoshub-4", + Path: "", + Value: anotherUser3.bech32C("cosmos"), // account 3 + }, + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "cosmoshub-4", + Path: "sub-name", + Value: anotherUser4.bech32C("cosmos"), // account 4 + }, + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: rollAppId, + Path: "", + Value: anotherUser5.bech32C(rollAppBech32Prefix), // account 5 + }, + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: rollAppId, + Path: "sub-rol", + Value: anotherUser6.bech32C(rollAppBech32Prefix), // account 6 + }, + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "bitcoin", + Path: "", + Value: anotherUser7, // account 7 + }, + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "ethereum", + Path: "", + Value: strings.ToLower(anotherUser8WithChecksum), // account 8 + // the value is lowercased, + // even tho the original value is mixed-case and the chain is neither host-chain nor RollApp + // because it welformed as hex address, start with 0x + }, + }, + }, + ) + }) + + s.Run("resolve and reverse-resolve after updated", func() { + // now we run the pending tests + sc.runPendingTests() + }) +} + +//goland:noinspection SpellCheckingInspection +func (s *KeeperTestSuite) TestKeeper_Alias() { + /** + This show details about Alias: working with alias and how it affects the resolution + */ + + sc := setupShowcase(s) + + s.Require().Equal("dymension", sc.s.ctx.ChainID()) // our chain-id is "dymension" + + // register some aliases + sc.registerAlias("dym").forChainId("dymension") + sc.registerAlias("btc").forChainId("bitcoin") + sc.registerAlias("eth").forChainId("ethereum") + + _ = sc.newRollApp("one_1-1").withBech32Prefix("one").withAlias("one").save() + + owner := sc.newTestAccount() + ethereumAddr := "0x4838B106FCe9647Bdf1E7877BF73cE8B0BAD5f97" + bitcoinAddr := "12higDjoCCNXSA95xZMWUdPvXNmkAduhWv" + + // register a Dym-Name + dymName := sc. + newDymName( + "my-name", + owner.bech32(), + ). + save() + // add some configuration + updateDymName(dymName).resolveTo(bitcoinAddr).onChain("bitcoin").add() + updateDymName(dymName).resolveTo(ethereumAddr).onChain("ethereum").add() + sc.requireDymName("my-name").update(*dymName) + + s.Run("resolve address", func() { + // general resolution using chain-id + sc. + requireResolveDymNameAddress("my-name@dymension"). + Equals(owner.bech32()) + sc. + requireResolveDymNameAddress("my-name@one_1-1"). + Equals(owner.bech32C("one")) + sc. + requireResolveDymNameAddress("my-name@bitcoin"). + Equals(bitcoinAddr) + sc. + requireResolveDymNameAddress("my-name@ethereum"). + Equals(strings.ToLower(ethereumAddr)) + + // resolution using alias works + sc. + requireResolveDymNameAddress("my-name@dym"). + Equals(owner.bech32()) + sc. + requireResolveDymNameAddress("my-name@one"). + Equals(owner.bech32C("one")) + sc. + requireResolveDymNameAddress("my-name@btc"). + Equals(bitcoinAddr) + sc. + requireResolveDymNameAddress("my-name@eth"). + Equals(strings.ToLower(ethereumAddr)) + }) + + s.Run("reverse-resolve address", func() { + // if a chain has alias configured then reverse-resolve will use the alias instead of the chain-id + sc. + requireReverseResolve(owner.bech32()). + forChainId("dymension"). + equals("my-name@dym") // alias "dym" is used instead of "dymension" + sc. + requireReverseResolve(owner.bech32C("one")). + forChainId("one_1-1"). + equals("my-name@one") // alias "one" is used instead of "one_1-1" + sc. + requireReverseResolve(bitcoinAddr). + forChainId("bitcoin"). + equals("my-name@btc") // alias "btc" is used instead of "bitcoin" + sc. + requireReverseResolve(ethereumAddr). + forChainId("ethereum"). + equals("my-name@eth") // alias "eth" is used instead of "ethereum" + }) + + s.Run("when alias is defined in params, it has priority over RollApp", func() { + // this RollApp is uses the alias "cosmos" which supposed to belong to Cosmos Hub + _ = sc. + newRollApp("unexpected_2-2"). + withAlias("cosmos"). // NOOO... + withBech32Prefix("unexpected").save() + + // add some resolution configuration for testing purpose + updateDymName(dymName).resolveTo(owner.bech32C("cosmos")).onChain("cosmoshub-4").add() + sc.requireDymName("my-name").update(*dymName) + + // now we see how it looks like + sc. + requireResolveDymNameAddress("my-name@cosmos"). + Equals(owner.bech32C("unexpected")) // that bad + + // to protect users, we take over the alias "cosmos" and give it to Cosmos Hub + sc.registerAlias("cosmos").forChainId("cosmoshub-4") // <= this put into module params + + // now we see how it looks like, again + sc. + requireResolveDymNameAddress("my-name@cosmos"). + Equals(owner.bech32C("cosmos")) // that's it + }) + + s.Run("Reverse-resolve when alias is defined in params, it has priority over RollApp", func() { + // this RollApp is uses the alias "injective" which supposed to belong to Injective + _ = sc. + newRollApp("unintended_3-3"). + withAlias("injective"). // NOOO... + withBech32Prefix("unintended").save() + + // add some resolution configuration for testing purpose + updateDymName(dymName).resolveTo(owner.bech32C("unintended")).onChain("unintended_3-3").add() + updateDymName(dymName).resolveTo(owner.bech32C("inj")).onChain("injective-1").add() + sc.requireDymName("my-name").update(*dymName) + sc.requireResolveDymNameAddress("my-name@injective").Equals(owner.bech32C("unintended")) // that bad + + sc. + requireReverseResolve(owner.bech32C("unintended")). + forChainId("unintended_3-3"). + equals("my-name@injective") // the alias is used in reverse-resolve + // later, we want reverse-resolve won't use the alias for the RollApp + // => "my-name@unintended_3-3" + + sc. + requireReverseResolve(owner.bech32C("inj")). + forChainId("injective-1"). + equals("my-name@injective-1") // no alias is used in reverse-resolve + // later, we want reverse-resolve will use the alias for Injective + // => "my-name@injective" + + // to protect users, we take over the alias "injective" and give it to Injective + sc.registerAlias("injective").forChainId("injective-1") // <= this put into module params + sc.requireResolveDymNameAddress("my-name@injective").Equals(owner.bech32C("inj")) + + // now test again + sc. + requireReverseResolve(owner.bech32C("unintended")). + forChainId("unintended_3-3"). + equals("my-name@unintended_3-3") // RollApp no longer use the alias + sc. + requireReverseResolve(owner.bech32C("inj")). + forChainId("injective-1"). + equals("my-name@injective") // alias is used in reverse-resolve + }) +} + +//goland:noinspection SpellCheckingInspection +func (s *KeeperTestSuite) TestKeeper_ResolveExtraFormat() { + /** + This show additional details about Extra Format resolution for the Dym-Name-Address. + Extra formats: + - nim1...@dym + - 0x.....@dym + + These formats are not the default resolution, but they are supported. + It converts the address to another-chain-based bech32 format like: + - nim1...@dym => dym1... + - 0x.....@dym => dym1... + - dym1...@nim => nim1... + - 0x.....@nim => nim1... + + In this mode, no Dym-Name is required, and the resolution is based on the chain-id + and the input address must be in the correct format and no Sub-Name. + The result is expected to be the same as the input address, with prefix changed corresponding to the chain-id. + */ + + sc := setupShowcase(s) + + s.Require().Equal("dymension", sc.s.ctx.ChainID()) // our chain-id is "dymension" + sc.registerAlias("dym").forChainId("dymension") + + rollApp1 := sc.newRollApp("one_1-1").withBech32Prefix("one").withAlias("one").save() + rollApp2 := sc.newRollApp("two_2-2").withBech32Prefix("two").withAlias("two").save() + rollAppWithoutBech32 := sc.newRollApp("nob_3-3").withoutBech32Prefix().save() + + testAccount := sc.newTestAccount() + + s.Run("resolve hex to chain-based bech32", func() { + // 0x...@dymension + sc.requireResolveDymNameAddress(testAccount.hexStr() + "@dymension"). + Equals(testAccount.bech32()) + + // 0x...@one_1-1 + sc.requireResolveDymNameAddress(testAccount.hexStr() + "@" + rollApp1.RollappId). + Equals(testAccount.bech32C("one")) + + // 0x...@two_2-2 + sc.requireResolveDymNameAddress(testAccount.hexStr() + "@" + rollApp2.RollappId). + Equals(testAccount.bech32C("two")) + + // 0x...@nob_3-3 + sc.requireResolveDymNameAddress(testAccount.hexStr() + "@" + rollAppWithoutBech32.RollappId). + NoResult() // won't work for RollApp without bech32 prefix because we don't know bech32 prefix to cast + + // also works with alias + sc.requireResolveDymNameAddress(testAccount.hexStr() + "@dym"). + Equals(testAccount.bech32()) + sc.requireResolveDymNameAddress(testAccount.hexStr() + "@one"). + Equals(testAccount.bech32C("one")) + }) + + s.Run("resolve bech32 to chain-based bech32", func() { + // kava1...@dymension + sc.requireResolveDymNameAddress(testAccount.bech32C("kava") + "@dymension"). + Equals(testAccount.bech32()) + + // dym1...@one_1-1 + sc.requireResolveDymNameAddress(testAccount.bech32() + "@" + rollApp1.RollappId). + Equals(testAccount.bech32C("one")) + + // whatever1...@two_2-2 + sc.requireResolveDymNameAddress(testAccount.bech32C("whatever") + "@" + rollApp2.RollappId). + Equals(testAccount.bech32C("two")) + + // also works with alias + sc.requireResolveDymNameAddress(testAccount.bech32C("kava") + "@dym"). + Equals(testAccount.bech32()) + sc.requireResolveDymNameAddress(testAccount.bech32() + "@two"). + Equals(testAccount.bech32C("two")) + }) + + sc.requireResolveDymNameAddress("otherNotWelformedAddress@dymension"). + WillError() + + s.Run("when alias is defined in params, it has priority over RollApp", func() { + _ = sc. + newRollApp("unintended_3-3"). + withAlias("injective"). + withBech32Prefix("unintended"). + save() + + sc.requireResolveDymNameAddress(testAccount.hexStr() + "@injective"). + Equals(testAccount.bech32C("unintended")) + + sc.registerAlias("injective").forChainId("injective-1") // <= this put into module params + + sc.requireResolveDymNameAddress(testAccount.hexStr() + "@injective"). + NoResult() + // because Injective is not the RollApp anymore. + // It neither the host-chain nor RollApp, so it should not resolve + }) +} + +//goland:noinspection SpellCheckingInspection +func (s *KeeperTestSuite) TestKeeper_ReverseResolve() { + /** + This show advanced details about Reverse-Resolve: + working with reverse-resolve and some vector affects the resolution + */ + + sc := setupShowcase(s) + + s.Require().Equal("dymension", sc.s.ctx.ChainID()) // our chain-id is "dymension" + + owner := sc.newTestAccount() + + // register a Dym-Name + _ = sc. + newDymName( + "my-name", + owner.bech32(), + ). + save() + + // register a RollApp + _ = sc.newRollApp("one_1-1").withBech32Prefix("one").save() + + s.Run("normal reverse-resolve", func() { + sc. + requireReverseResolve(owner.bech32()). + forChainId("dymension"). + equals("my-name@dymension") + sc. + requireReverseResolve(owner.bech32C("any")). + forChainId("dymension"). + equals("my-name@dymension") + + // reverse-resolve using 0x address + sc. + requireReverseResolve(owner.hexStr()). + forChainId("dymension"). + equals("my-name@dymension") + + // reverse-resolve using bech32 for RollApp Dym-Name-Address + sc. + requireReverseResolve(owner.bech32()). + forChainId("one_1-1"). + equals("my-name@one_1-1") + + // reverse-resolve using any bech32 for RollApp Dym-Name-Address + sc. + requireReverseResolve(owner.bech32C("any")). + forChainId("one_1-1"). + equals("my-name@one_1-1") + }) + + s.Run("normal reverse-resolve NOT work for chains those are NEITHER host-chain nor RollApp", func() { + sc. + requireReverseResolve(owner.bech32()). + forChainId("injective-1"). + NoResult() // won't work + sc. + requireReverseResolve(owner.hexStr()). + forChainId("injective-1"). + NoResult() // won't work + sc. + requireReverseResolve(owner.bech32()). + forChainId("cosmoshub-4"). + NoResult() // won't work + sc. + requireReverseResolve(owner.hexStr()). + forChainId("cosmoshub-4"). + NoResult() // won't work + // the main reason for those do not work is we don't know if the chain is coin-type-60 or not, + // so we can not blindly reverse-resolve the address. + + // Cosmos Hub is a good example, it is coin-type-118, + // so we CAN NOT convert from both bech32 & 0x format of Dymension to Cosmos Hub address. + // Because if user send funds to those address, it will be lost. + + // And that's why we Do Not support reverse-resolve for chains those are NEITHER host-chain nor RollApp. + }) + + s.Run("when alias is registered, reverse resolve will use the alias instead of chain-id", func() { + sc.newRollApp("two_2-2").withAlias("two").save() + sc. + requireReverseResolve(owner.hexStr()). + forChainId("two_2-2"). + equals("my-name@two") + }) +} + +/* -------------------------------------------------------------------------- */ +/* setup area, no need to read */ +/* -------------------------------------------------------------------------- */ + +func setupShowcase(s *KeeperTestSuite) *showcaseSetup { + const chainId = "dymension" + + s.ctx = s.ctx.WithChainID(chainId) + + scs := &showcaseSetup{ + s: s, + recentTestAccountNo: 0, + dymNameOwner: ta{}, + laterTests: nil, + } + + scs.dymNameOwner = scs.newTestAccount() + + return scs +} + +type showcaseSetup struct { + s *KeeperTestSuite + + recentTestAccountNo uint64 + dymNameOwner ta + + laterTests []func() +} + +func (m *showcaseSetup) newTestAccount() ta { + m.recentTestAccountNo++ + return testAddr(m.recentTestAccountNo) +} + +func (m *showcaseSetup) requireDymName(name string) reqDymName { + return reqDymName{ + scs: m, + name: name, + } +} + +func (m *showcaseSetup) addLaterTest(laterTest func()) { + m.laterTests = append(m.laterTests, laterTest) +} + +func (m *showcaseSetup) runPendingTests() { + defer func() { + m.laterTests = nil // clear + }() + for _, laterTest := range m.laterTests { + laterTest() + } +} + +// + +type reqDymName struct { + scs *showcaseSetup + name string +} + +func (m *showcaseSetup) newDymName(name string, owner string) *configureDymName { + return &configureDymName{ + scs: m, + name: name, + owner: owner, + controller: owner, + expiry: m.s.now.Add(365 * 24 * time.Hour), + configs: nil, + } +} + +func (m reqDymName) equals(otherDymName dymnstypes.DymName) { + dymName := m.scs.s.dymNsKeeper.GetDymName(m.scs.s.ctx, m.name) + m.scs.s.Require().NotNil(dymName) + m.scs.s.Require().Equal(otherDymName, *dymName) +} + +func (m reqDymName) get() *dymnstypes.DymName { + return m.scs.s.dymNsKeeper.GetDymName(m.scs.s.ctx, m.name) +} + +func (m reqDymName) MustHasConfig(filter func(cfg dymnstypes.DymNameConfig) bool) { + dymName := m.get() + var anyMatch bool + for _, cfg := range dymName.Configs { + if filter(cfg) { + anyMatch = true + break + } + } + m.scs.s.Require().True(anyMatch) +} + +func (m reqDymName) NotHaveConfig(filter func(cfg dymnstypes.DymNameConfig) bool) { + dymName := m.get() + for _, cfg := range dymName.Configs { + m.scs.s.Require().False(filter(cfg)) + } +} + +func (m reqDymName) update(dymName dymnstypes.DymName) { + m.scs.s.Require().Equal(m.name, dymName.Name, "passed wrong Dym-Name") + + for i, config := range dymName.Configs { + if config.ChainId == m.scs.s.ctx.ChainID() { + config.ChainId = "" + dymName.Configs[i] = config + } + } + + m.scs.s.setDymNameWithFunctionsAfter(dymName) +} + +// + +type configureDymName struct { + scs *showcaseSetup + name string + owner string + controller string + expiry time.Time + configs []dymnstypes.DymNameConfig +} + +func (m *configureDymName) withExpiry(expiry time.Time) *configureDymName { + m.expiry = expiry + return m +} + +func (m configureDymName) build() dymnstypes.DymName { + return dymnstypes.DymName{ + Name: m.name, + Owner: m.owner, + Controller: m.controller, + ExpireAt: m.expiry.Unix(), + Configs: m.configs, + Contact: "", + } +} + +func (m configureDymName) save() *dymnstypes.DymName { + dymName := m.build() + m.scs.s.setDymNameWithFunctionsAfter(dymName) + + record := m.scs.s.dymNsKeeper.GetDymName(m.scs.s.ctx, dymName.Name) + m.scs.s.Require().NotNil(record) + return record +} + +// + +type configureRollApp struct { + scs *showcaseSetup + rollAppId string + alias string + bech32Prefix string +} + +func (m *showcaseSetup) newRollApp(rollAppId string) *configureRollApp { + return &configureRollApp{ + scs: m, + rollAppId: rollAppId, + } +} + +func (m *configureRollApp) withAlias(alias string) *configureRollApp { + m.alias = alias + return m +} + +func (m *configureRollApp) withBech32Prefix(bech32Prefix string) *configureRollApp { + m.bech32Prefix = bech32Prefix + return m +} + +func (m *configureRollApp) withoutBech32Prefix() *configureRollApp { + m.bech32Prefix = "" + return m +} + +func (m configureRollApp) save() *rollapptypes.Rollapp { + m.scs.s.persistRollApp( + rollapp{ + rollAppId: m.rollAppId, + owner: testAddr(0).bech32(), + bech32: m.bech32Prefix, + alias: m.alias, + aliases: func() []string { + if m.alias == "" { + return nil + } + return []string{m.alias} + }(), + }, + ) + + rollApp, found := m.scs.s.rollAppKeeper.GetRollapp(m.scs.s.ctx, m.rollAppId) + m.scs.s.Require().True(found) + return &rollApp +} + +// + +type reqResolveDymNameAddress struct { + scs *showcaseSetup + dymNameAddress string +} + +func (m *showcaseSetup) requireResolveDymNameAddress(dymNameAddress string) reqResolveDymNameAddress { + return reqResolveDymNameAddress{ + scs: m, + dymNameAddress: dymNameAddress, + } +} + +func (m reqResolveDymNameAddress) Equals(want string) { + outputAddress, err := m.scs.s.dymNsKeeper.ResolveByDymNameAddress(m.scs.s.ctx, m.dymNameAddress) + m.scs.s.Require().NoError(err) + m.scs.s.Require().Equal(want, outputAddress) +} + +func (m reqResolveDymNameAddress) NotEquals(want string) { + outputAddress, err := m.scs.s.dymNsKeeper.ResolveByDymNameAddress(m.scs.s.ctx, m.dymNameAddress) + m.scs.s.Require().NoError(err) + m.scs.s.Require().NotEqual(want, outputAddress) +} + +func (m reqResolveDymNameAddress) NoResult() { + outputAddress, err := m.scs.s.dymNsKeeper.ResolveByDymNameAddress(m.scs.s.ctx, m.dymNameAddress) + if err != nil { + m.scs.s.Require().ErrorContains(err, "not found") + } else { + m.scs.s.Require().Empty(outputAddress) + } +} + +func (m reqResolveDymNameAddress) WillError() { + _, err := m.scs.s.dymNsKeeper.ResolveByDymNameAddress(m.scs.s.ctx, m.dymNameAddress) + m.scs.s.Require().Error(err) +} + +// + +type reqReverseResolveDymNameAddress struct { + scs *showcaseSetup + workingChainId string + addresses []string +} + +func (m *showcaseSetup) requireReverseResolve(addresses ...string) *reqReverseResolveDymNameAddress { + return &reqReverseResolveDymNameAddress{ + scs: m, + workingChainId: m.s.ctx.ChainID(), + addresses: addresses, + } +} + +func (m *reqReverseResolveDymNameAddress) forChainId(workingChainId string) *reqReverseResolveDymNameAddress { + m.workingChainId = workingChainId + return m +} + +func (m reqReverseResolveDymNameAddress) equals(wantMany ...string) { + for _, address := range m.addresses { + m.scs.s.Run("reverse-resolve for "+address, func() { + list, err := m.scs.s.dymNsKeeper.ReverseResolveDymNameAddress(m.scs.s.ctx, address, m.workingChainId) + m.scs.s.Require().NoError(err) + + var dymNameAddresses []string + for _, dna := range list { + dymNameAddresses = append(dymNameAddresses, dna.String()) + } + + sort.Strings(dymNameAddresses) + sort.Strings(wantMany) + + m.scs.s.Require().Equal(wantMany, dymNameAddresses) + }) + } +} + +func (m reqReverseResolveDymNameAddress) NoResult() { + for _, address := range m.addresses { + m.scs.s.Run("reverse-resolve for "+address, func() { + list, err := m.scs.s.dymNsKeeper.ReverseResolveDymNameAddress(m.scs.s.ctx, address, m.workingChainId) + m.scs.s.Require().NoError(err) + m.scs.s.Require().Empty(list) + }) + } +} + +// + +type udtDymName struct { + dymName *dymnstypes.DymName +} + +func updateDymName(dymName *dymnstypes.DymName) *udtDymName { + return &udtDymName{dymName: dymName} +} + +func (m *udtDymName) resolveTo(value string) *udtDymNameConfigResolveTo { + return &udtDymNameConfigResolveTo{ + udtDymName: m, + value: value, + } +} + +// + +type udtDymNameConfigResolveTo struct { + udtDymName *udtDymName + chainId string + subName string + value string +} + +func (m *udtDymNameConfigResolveTo) onChain(chainId string) *udtDymNameConfigResolveTo { + m.chainId = chainId + return m +} + +func (m *udtDymNameConfigResolveTo) withSubName(subName string) *udtDymNameConfigResolveTo { + m.subName = subName + return m +} + +func (m *udtDymNameConfigResolveTo) add() { + value := m.value + + if dymnsutils.IsValidHexAddress(value) { + value = strings.ToLower(value) + } + + m.udtDymName.dymName.Configs = append(m.udtDymName.dymName.Configs, dymnstypes.DymNameConfig{ + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: m.chainId, + Path: m.subName, + Value: value, + }) +} + +// + +type regAlias struct { + scs *showcaseSetup + alias string +} + +func (m *showcaseSetup) registerAlias(alias string) regAlias { + return regAlias{ + scs: m, + alias: alias, + } +} + +func (m regAlias) forChainId(chainId string) { + m.scs.s.Require().NotEmpty(m.alias) + m.scs.s.Require().NotEmpty(chainId) + + moduleParams := m.scs.s.dymNsKeeper.GetParams(m.scs.s.ctx) + moduleParams.Chains.AliasesOfChainIds = append(moduleParams.Chains.AliasesOfChainIds, dymnstypes.AliasesOfChainId{ + ChainId: chainId, + Aliases: []string{m.alias}, + }) + + err := m.scs.s.dymNsKeeper.SetParams(m.scs.s.ctx, moduleParams) + m.scs.s.Require().NoError(err) +} diff --git a/x/dymns/keeper/util_test.go b/x/dymns/keeper/util_test.go new file mode 100644 index 000000000..216a66d89 --- /dev/null +++ b/x/dymns/keeper/util_test.go @@ -0,0 +1,67 @@ +package keeper_test + +import ( + "github.com/ethereum/go-ethereum/common" + + sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/dymensionxyz/dymension/v3/app/params" + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" + dymnsutils "github.com/dymensionxyz/dymension/v3/x/dymns/utils" +) + +var dymNsModuleAccAddr = authtypes.NewModuleAddress(dymnstypes.ModuleName) + +// ta stands for test-address, a simple wrapper for generating account for testing purpose. +// Usage is short, memorable, easy to type. +// The generated address is predictable, deterministic, supports output multiple formats. +type ta struct { + bz []byte +} + +// testAddr creates a general 20-bytes address from seed. +func testAddr(no uint64) ta { + bz1 := sdk.Uint64ToBigEndian(no) + bz2 := make([]byte, 20) + copy(bz2, bz1) + return ta{bz: bz2} +} + +// testICAddr creates a 32-bytes address of Interchain Account from seed. +func testICAddr(no uint64) ta { + bz1 := sdk.Uint64ToBigEndian(no) + bz2 := make([]byte, 32) + copy(bz2, bz1) + return ta{bz: bz2} +} + +func (a ta) bytes() []byte { + return a.bz +} + +func (a ta) bech32() string { + return a.bech32C(params.AccountAddressPrefix) +} + +func (a ta) bech32Valoper() string { + return a.bech32C(params.AccountAddressPrefix + "valoper") +} + +func (a ta) bech32C(customHrp string) string { + return sdk.MustBech32ifyAddressBytes(customHrp, a.bz) +} + +func (a ta) fallback() dymnstypes.FallbackAddress { + return a.bz +} + +func (a ta) hexStr() string { + return dymnsutils.GetHexAddressFromBytes(a.bz) +} + +func (a ta) checksumHex() string { + if len(a.bz) != 20 { + panic("invalid call") + } + return common.BytesToAddress(a.bz).Hex() +} diff --git a/x/dymns/module.go b/x/dymns/module.go new file mode 100644 index 000000000..93b2d95dc --- /dev/null +++ b/x/dymns/module.go @@ -0,0 +1,145 @@ +package dymns + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/grpc-ecosystem/grpc-gateway/runtime" + "github.com/spf13/cobra" + + abci "github.com/cometbft/cometbft/abci/types" + + "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" + dymnsclient "github.com/dymensionxyz/dymension/v3/x/dymns/client/cli" + dymnskeeper "github.com/dymensionxyz/dymension/v3/x/dymns/keeper" + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" +) + +var ( + _ module.AppModule = AppModule{} + _ module.AppModuleBasic = AppModuleBasic{} +) + +// ---------------------------------------------------------------------------- +// AppModuleBasic +// ---------------------------------------------------------------------------- + +// AppModuleBasic implements the AppModuleBasic interface that defines the independent methods a Cosmos SDK module needs to implement. +type AppModuleBasic struct { + cdc codec.BinaryCodec +} + +// NewAppModuleBasic creates a new AppModuleBasic object +func NewAppModuleBasic(cdc codec.BinaryCodec) AppModuleBasic { + return AppModuleBasic{cdc: cdc} +} + +// Name returns the name of the module as a string +func (AppModuleBasic) Name() string { + return dymnstypes.ModuleName +} + +// RegisterLegacyAminoCodec registers the amino codec for the module, which is used to marshal and unmarshal structs to/from []byte in order to persist them in the module's KVStore +func (AppModuleBasic) RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { + dymnstypes.RegisterCodec(cdc) +} + +// RegisterInterfaces registers a module's interface types and their concrete implementations as proto.Message +func (a AppModuleBasic) RegisterInterfaces(reg cdctypes.InterfaceRegistry) { + dymnstypes.RegisterInterfaces(reg) +} + +// DefaultGenesis returns a default GenesisState for the module, marshalled to json.RawMessage. The default GenesisState need to be defined by the module developer and is primarily used for testing +func (AppModuleBasic) DefaultGenesis(cdc codec.JSONCodec) json.RawMessage { + return cdc.MustMarshalJSON(dymnstypes.DefaultGenesis()) +} + +// ValidateGenesis used to validate the GenesisState, given in its json.RawMessage form +func (AppModuleBasic) ValidateGenesis(cdc codec.JSONCodec, _ client.TxEncodingConfig, bz json.RawMessage) error { + var genState dymnstypes.GenesisState + if err := cdc.UnmarshalJSON(bz, &genState); err != nil { + return fmt.Errorf("failed to unmarshal %s genesis state: %w", dymnstypes.ModuleName, err) + } + return genState.Validate() +} + +// RegisterGRPCGatewayRoutes registers the gRPC Gateway routes for the module +func (AppModuleBasic) RegisterGRPCGatewayRoutes(clientCtx client.Context, mux *runtime.ServeMux) { + _ = dymnstypes.RegisterQueryHandlerClient(context.Background(), mux, dymnstypes.NewQueryClient(clientCtx)) +} + +// GetTxCmd returns the root Tx command for the module. The subcommands of this root command are used by end-users to generate new transactions containing messages defined in the module +func (a AppModuleBasic) GetTxCmd() *cobra.Command { + return dymnsclient.GetTxCmd() +} + +// GetQueryCmd returns the root query command for the module. The subcommands of this root command are used by end-users to generate new queries to the subset of the state defined by the module +func (AppModuleBasic) GetQueryCmd() *cobra.Command { + return dymnsclient.GetQueryCmd() +} + +// ---------------------------------------------------------------------------- +// AppModule +// ---------------------------------------------------------------------------- + +// AppModule implements the AppModule interface that defines the inter-dependent methods that modules need to implement +type AppModule struct { + AppModuleBasic + + keeper dymnskeeper.Keeper +} + +// NewAppModule creates a new AppModule object +func NewAppModule( + cdc codec.Codec, + keeper dymnskeeper.Keeper, +) AppModule { + return AppModule{ + AppModuleBasic: NewAppModuleBasic(cdc), + keeper: keeper, + } +} + +// RegisterServices registers a gRPC query service to respond to the module-specific gRPC queries +func (am AppModule) RegisterServices(cfg module.Configurator) { + dymnstypes.RegisterMsgServer(cfg.MsgServer(), dymnskeeper.NewMsgServerImpl(am.keeper)) + dymnstypes.RegisterQueryServer(cfg.QueryServer(), dymnskeeper.NewQueryServerImpl(am.keeper)) +} + +// RegisterInvariants registers the module's invariants. +func (am AppModule) RegisterInvariants(ir sdk.InvariantRegistry) { + dymnskeeper.RegisterInvariants(ir, am.keeper) +} + +// InitGenesis performs the module's genesis initialization. It returns no validator updates. +func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, gs json.RawMessage) []abci.ValidatorUpdate { + var genState dymnstypes.GenesisState + // Initialize global index to index in genesis state + cdc.MustUnmarshalJSON(gs, &genState) + + InitGenesis(ctx, am.keeper, genState) + + return []abci.ValidatorUpdate{} +} + +// ExportGenesis returns the module's exported genesis state as raw JSON bytes. +func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.RawMessage { + genState := ExportGenesis(ctx, am.keeper) + return cdc.MustMarshalJSON(genState) +} + +// ConsensusVersion is a sequence number for state-breaking change of the module. It should be incremented on each consensus-breaking change introduced by the module. To avoid wrong/empty versions, the initial version should be set to 1 +func (AppModule) ConsensusVersion() uint64 { return 1 } + +// BeginBlock contains the logic that is automatically triggered at the beginning of each block +func (am AppModule) BeginBlock(_ sdk.Context, _ abci.RequestBeginBlock) {} + +// EndBlock contains the logic that is automatically triggered at the end of each block +func (am AppModule) EndBlock(_ sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate { + return []abci.ValidatorUpdate{} +} diff --git a/x/dymns/module_simulation.go b/x/dymns/module_simulation.go new file mode 100644 index 000000000..4cca97630 --- /dev/null +++ b/x/dymns/module_simulation.go @@ -0,0 +1,43 @@ +package dymns + +import ( + "github.com/cosmos/cosmos-sdk/baseapp" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + simtypes "github.com/cosmos/cosmos-sdk/types/simulation" + "github.com/cosmos/cosmos-sdk/x/simulation" + "github.com/dymensionxyz/dymension/v3/app/params" + "github.com/dymensionxyz/dymension/v3/testutil/sample" + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" +) + +// avoid unused import issue +var ( + _ = sample.AccAddress + _ = params.StakePerAccount + _ = simulation.MsgEntryKind + _ = baseapp.Paramspace +) + +// GenerateGenesisState creates a randomized GenState of the module +func (AppModule) GenerateGenesisState(simState *module.SimulationState) { + eibcGenesis := dymnstypes.GenesisState{ + Params: dymnstypes.DefaultParams(), + } + simState.GenState[dymnstypes.ModuleName] = simState.Cdc.MustMarshalJSON(&eibcGenesis) +} + +// ProposalContents doesn't return any content functions for governance proposals +func (AppModule) ProposalContents(_ module.SimulationState) []simtypes.WeightedProposalContent { + return nil +} + +// RegisterStoreDecoder registers a decoder +func (am AppModule) RegisterStoreDecoder(_ sdk.StoreDecoderRegistry) {} + +// WeightedOperations returns the all the gov module operations with their respective weights. +func (am AppModule) WeightedOperations(_ module.SimulationState) []simtypes.WeightedOperation { + operations := make([]simtypes.WeightedOperation, 0) + + return operations +} diff --git a/x/dymns/proposal_handler.go b/x/dymns/proposal_handler.go new file mode 100644 index 000000000..6535a65b5 --- /dev/null +++ b/x/dymns/proposal_handler.go @@ -0,0 +1,64 @@ +package dymns + +import ( + errorsmod "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + errortypes "github.com/cosmos/cosmos-sdk/types/errors" + govv1beta1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" + dymnskeeper "github.com/dymensionxyz/dymension/v3/x/dymns/keeper" + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" +) + +// NewDymNsProposalHandler creates a governance handler to manage new proposal types. +func NewDymNsProposalHandler(dk dymnskeeper.Keeper) govv1beta1.Handler { + return func(ctx sdk.Context, content govv1beta1.Content) error { + switch c := content.(type) { + case *dymnstypes.MigrateChainIdsProposal: + return handleMigrateChainIdsProposal(ctx, dk, c) + case *dymnstypes.UpdateAliasesProposal: + return handleUpdateAliasesProposal(ctx, dk, c) + default: + return errorsmod.Wrapf( + errortypes.ErrUnknownRequest, + "unrecognized %s proposal content type: %T", dymnstypes.ModuleName, c, + ) + } + } +} + +// handleMigrateChainIdsProposal handles the proposal to migrate chain-ids in module params +// as well as Dym-Names configurations (of non-expired) those contain chain-ids. +func handleMigrateChainIdsProposal( + ctx sdk.Context, + dk dymnskeeper.Keeper, + p *dymnstypes.MigrateChainIdsProposal, +) error { + if err := p.ValidateBasic(); err != nil { + return err + } + + err := dk.MigrateChainIds(ctx, p.Replacement) + if err != nil { + return err + } + + return nil +} + +// handleUpdateAliasesProposal handles the proposal to update aliases of chain-ids in module params. +func handleUpdateAliasesProposal( + ctx sdk.Context, + dk dymnskeeper.Keeper, + p *dymnstypes.UpdateAliasesProposal, +) error { + if err := p.ValidateBasic(); err != nil { + return err + } + + err := dk.UpdateAliases(ctx, p.Add, p.Remove) + if err != nil { + return err + } + + return nil +} diff --git a/x/dymns/proposal_handler_test.go b/x/dymns/proposal_handler_test.go new file mode 100644 index 000000000..f92337128 --- /dev/null +++ b/x/dymns/proposal_handler_test.go @@ -0,0 +1,254 @@ +package dymns_test + +import ( + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" + testkeeper "github.com/dymensionxyz/dymension/v3/testutil/keeper" + "github.com/dymensionxyz/dymension/v3/x/dymns" + dymnskeeper "github.com/dymensionxyz/dymension/v3/x/dymns/keeper" + dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" + "github.com/stretchr/testify/require" +) + +func Test_ProposalHandler(t *testing.T) { + dk, _, _, ctx := testkeeper.DymNSKeeper(t) + proposalHandler := dymns.NewDymNsProposalHandler(dk) + + t.Run("pass - can process proposal", func(t *testing.T) { + err := proposalHandler(ctx, &dymnstypes.MigrateChainIdsProposal{ + Title: "T", + Description: "D", + Replacement: []dymnstypes.MigrateChainId{ + { + PreviousChainId: "cosmoshub-3", + NewChainId: "cosmoshub-4", + }, + }, + }) + require.NoError(t, err) + }) + + t.Run("pass - can process proposal", func(t *testing.T) { + moduleParams := dk.GetParams(ctx) + moduleParams.Chains.AliasesOfChainIds = []dymnstypes.AliasesOfChainId{{ + ChainId: "dymension_1100-1", + Aliases: []string{"dymension"}, + }} + require.NoError(t, dk.SetParams(ctx, moduleParams)) + + err := proposalHandler(ctx, &dymnstypes.UpdateAliasesProposal{ + Title: "T", + Description: "D", + Add: []dymnstypes.UpdateAlias{ + { + ChainId: "dymension_1100-1", + Alias: "dym", + }, + }, + Remove: []dymnstypes.UpdateAlias{ + { + ChainId: "dymension_1100-1", + Alias: "dymension", + }, + }, + }) + require.NoError(t, err) + }) + + t.Run("fail - can not process unknown proposal", func(t *testing.T) { + //goland:noinspection GoDeprecation + err := proposalHandler(ctx, &distrtypes.CommunityPoolSpendProposal{}) + require.Error(t, err) + require.Contains(t, err.Error(), "unrecognized dymns proposal content type") + }) +} + +func Test_ProposalHandler_MigrateChainIdsProposal(t *testing.T) { + tests := []struct { + name string + additionalSetup func(ctx sdk.Context, dk dymnskeeper.Keeper) + proposal dymnstypes.MigrateChainIdsProposal + wantErr bool + wantErrContains string + }{ + { + name: "pass - migration successfully", + proposal: dymnstypes.MigrateChainIdsProposal{ + Title: "T", + Description: "D", + Replacement: []dymnstypes.MigrateChainId{ + { + PreviousChainId: "cosmoshub-3", + NewChainId: "cosmoshub-4", + }, + }, + }, + wantErr: false, + }, + { + name: "fail - reject invalid proposal content", + proposal: dymnstypes.MigrateChainIdsProposal{ + Title: "T", + Description: "D", + Replacement: []dymnstypes.MigrateChainId{ + { + PreviousChainId: "", + NewChainId: "cosmoshub-4", + }, + }, + }, + wantErr: true, + wantErrContains: "previous chain id cannot be empty", + }, + { + name: "fail - returns error if migration failed", + additionalSetup: func(ctx sdk.Context, dk dymnskeeper.Keeper) { + moduleParams := dk.GetParams(ctx) + moduleParams.Chains.AliasesOfChainIds = []dymnstypes.AliasesOfChainId{ + { + ChainId: "dymension_1100-1", + Aliases: []string{"dym"}, + }, + { + ChainId: "blumbus_111-1", + Aliases: []string{"bb"}, + }, + } + require.NoError(t, dk.SetParams(ctx, moduleParams)) + }, + proposal: dymnstypes.MigrateChainIdsProposal{ + Title: "T", + Description: "D", + Replacement: []dymnstypes.MigrateChainId{ + { + PreviousChainId: "blumbus_111-1", + NewChainId: "dym", // collision with alias of dymension_1100-1 + }, + }, + }, + wantErr: true, + wantErrContains: "chain ID and alias must unique among all", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + dk, _, _, ctx := testkeeper.DymNSKeeper(t) + + if tt.additionalSetup != nil { + tt.additionalSetup(ctx, dk) + } + + proposalHandler := dymns.NewDymNsProposalHandler(dk) + + err := proposalHandler(ctx, &tt.proposal) + if tt.wantErr { + require.NotEmpty(t, tt.wantErrContains, "mis-configured test case") + require.Error(t, err) + require.Contains(t, err.Error(), tt.wantErrContains) + return + } + + require.NoError(t, err) + }) + } +} + +func Test_ProposalHandler_UpdateAliasesProposal(t *testing.T) { + tests := []struct { + name string + additionalSetup func(ctx sdk.Context, dk dymnskeeper.Keeper) + proposal dymnstypes.UpdateAliasesProposal + wantErr bool + wantErrContains string + }{ + { + name: "pass - migration successfully", + proposal: dymnstypes.UpdateAliasesProposal{ + Title: "T", + Description: "D", + Add: []dymnstypes.UpdateAlias{ + { + ChainId: "dymension_1100-1", + Alias: "dym", + }, + { + ChainId: "blumbus_111-1", + Alias: "bb", + }, + { + ChainId: "froopyland_100-1", + Alias: "frl", + }, + { + ChainId: "froopyland_100-1", + Alias: "fl", + }, + }, + }, + wantErr: false, + }, + { + name: "fail - reject invalid proposal content", + proposal: dymnstypes.UpdateAliasesProposal{ + Title: "T", + Description: "D", + Remove: []dymnstypes.UpdateAlias{ + { + ChainId: "", + Alias: "dym", + }, + }, + }, + wantErr: true, + wantErrContains: "chain id cannot be empty", + }, + { + name: "fail - returns error if migration failed", + additionalSetup: func(ctx sdk.Context, dk dymnskeeper.Keeper) { + moduleParams := dk.GetParams(ctx) + moduleParams.Chains.AliasesOfChainIds = []dymnstypes.AliasesOfChainId{ + { + ChainId: "dymension_1100-1", + Aliases: []string{"dym"}, + }, + } + require.NoError(t, dk.SetParams(ctx, moduleParams)) + }, + proposal: dymnstypes.UpdateAliasesProposal{ + Title: "T", + Description: "D", + Add: []dymnstypes.UpdateAlias{ + { + ChainId: "blumbus_111-1", // collision with alias of dymension_1100-1 + Alias: "dym", + }, + }, + }, + wantErr: true, + wantErrContains: "chain ID and alias must unique among all", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + dk, _, _, ctx := testkeeper.DymNSKeeper(t) + + if tt.additionalSetup != nil { + tt.additionalSetup(ctx, dk) + } + + proposalHandler := dymns.NewDymNsProposalHandler(dk) + + err := proposalHandler(ctx, &tt.proposal) + if tt.wantErr { + require.NotEmpty(t, tt.wantErrContains, "mis-configured test case") + require.Error(t, err) + require.Contains(t, err.Error(), tt.wantErrContains) + return + } + + require.NoError(t, err) + }) + } +} diff --git a/x/dymns/types/address.go b/x/dymns/types/address.go new file mode 100644 index 000000000..bc9c314e8 --- /dev/null +++ b/x/dymns/types/address.go @@ -0,0 +1,29 @@ +package types + +import ( + "strings" + + errorsmod "cosmossdk.io/errors" + dymnsutils "github.com/dymensionxyz/dymension/v3/x/dymns/utils" + "github.com/dymensionxyz/gerr-cosmos/gerrc" +) + +// FallbackAddress is used for reverse lookup using fallback mechanism. +// Fallback mechanism is used in reverse-lookup, to find possible Dym-Name-Addresses from account address in bytes, +// compatible for host-chain and RollApps only. +// The other chains are not supported because of HD-Path might be different. +type FallbackAddress []byte + +// ValidateBasic performs basic validation for the FallbackAddress. +func (m FallbackAddress) ValidateBasic() error { + if length := len(m); length != 20 && length != 32 { + return errorsmod.Wrapf(gerrc.ErrInvalidArgument, "fallback address must be 20 or 32 bytes, got: %d", length) + } + + return nil +} + +// String returns the fallback-address in lowercase hex format. +func (m FallbackAddress) String() string { + return strings.ToLower(dymnsutils.GetHexAddressFromBytes(m)) +} diff --git a/x/dymns/types/address_test.go b/x/dymns/types/address_test.go new file mode 100644 index 000000000..03bb7bcfb --- /dev/null +++ b/x/dymns/types/address_test.go @@ -0,0 +1,30 @@ +package types + +import ( + "strings" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/require" +) + +func TestFallbackAddress_ValidateBasic(t *testing.T) { + require.NoError(t, FallbackAddress(make([]byte, 20)).ValidateBasic()) + require.NoError(t, FallbackAddress(make([]byte, 32)).ValidateBasic()) + + for i := 0; i < 120; i++ { + if i == 20 || i == 32 { + continue + } + + require.Error(t, FallbackAddress(make([]byte, i)).ValidateBasic()) + } +} + +func TestFallbackAddress_String(t *testing.T) { + t.Run("output must be lowercase", func(t *testing.T) { + bz := make([]byte, 20) + copy(bz, []byte{0xab, 0xcd, 0xef}) + require.Equal(t, strings.ToLower(common.BytesToAddress(bz).String()), FallbackAddress(bz).String()) + }) +} diff --git a/x/dymns/types/alias.pb.go b/x/dymns/types/alias.pb.go new file mode 100644 index 000000000..f87b50a28 --- /dev/null +++ b/x/dymns/types/alias.pb.go @@ -0,0 +1,324 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: dymensionxyz/dymension/dymns/alias.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 + +// MultipleAliases contains a list of alias. +type MultipleAliases struct { + // aliases is a list of alias, available for a RollApp. + Aliases []string `protobuf:"bytes,1,rep,name=aliases,proto3" json:"aliases,omitempty"` +} + +func (m *MultipleAliases) Reset() { *m = MultipleAliases{} } +func (m *MultipleAliases) String() string { return proto.CompactTextString(m) } +func (*MultipleAliases) ProtoMessage() {} +func (*MultipleAliases) Descriptor() ([]byte, []int) { + return fileDescriptor_602110efd70c10a3, []int{0} +} +func (m *MultipleAliases) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MultipleAliases) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MultipleAliases.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 *MultipleAliases) XXX_Merge(src proto.Message) { + xxx_messageInfo_MultipleAliases.Merge(m, src) +} +func (m *MultipleAliases) XXX_Size() int { + return m.Size() +} +func (m *MultipleAliases) XXX_DiscardUnknown() { + xxx_messageInfo_MultipleAliases.DiscardUnknown(m) +} + +var xxx_messageInfo_MultipleAliases proto.InternalMessageInfo + +func (m *MultipleAliases) GetAliases() []string { + if m != nil { + return m.Aliases + } + return nil +} + +func init() { + proto.RegisterType((*MultipleAliases)(nil), "dymensionxyz.dymension.dymns.MultipleAliases") +} + +func init() { + proto.RegisterFile("dymensionxyz/dymension/dymns/alias.proto", fileDescriptor_602110efd70c10a3) +} + +var fileDescriptor_602110efd70c10a3 = []byte{ + // 171 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xd2, 0x48, 0xa9, 0xcc, 0x4d, + 0xcd, 0x2b, 0xce, 0xcc, 0xcf, 0xab, 0xa8, 0xac, 0xd2, 0x87, 0x73, 0x40, 0xac, 0xbc, 0x62, 0xfd, + 0xc4, 0x9c, 0xcc, 0xc4, 0x62, 0xbd, 0x82, 0xa2, 0xfc, 0x92, 0x7c, 0x21, 0x19, 0x64, 0x95, 0x7a, + 0x70, 0x8e, 0x1e, 0x58, 0xa5, 0x94, 0x48, 0x7a, 0x7e, 0x7a, 0x3e, 0x58, 0xa1, 0x3e, 0x88, 0x05, + 0xd1, 0xa3, 0xa4, 0xcd, 0xc5, 0xef, 0x5b, 0x9a, 0x53, 0x92, 0x59, 0x90, 0x93, 0xea, 0x08, 0x32, + 0x2a, 0xb5, 0x58, 0x48, 0x82, 0x8b, 0x3d, 0x11, 0xc2, 0x94, 0x60, 0x54, 0x60, 0xd6, 0xe0, 0x0c, + 0x82, 0x71, 0x9d, 0x7c, 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, 0xca, 0x28, + 0x3d, 0xb3, 0x24, 0xa3, 0x34, 0x49, 0x2f, 0x39, 0x3f, 0x57, 0x1f, 0x87, 0x7b, 0xcb, 0x8c, 0xf5, + 0x2b, 0xa0, 0x8e, 0x2e, 0xa9, 0x2c, 0x48, 0x2d, 0x4e, 0x62, 0x03, 0xbb, 0xc0, 0x18, 0x10, 0x00, + 0x00, 0xff, 0xff, 0xcb, 0x2c, 0x54, 0x2b, 0xe1, 0x00, 0x00, 0x00, +} + +func (m *MultipleAliases) 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 *MultipleAliases) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MultipleAliases) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Aliases) > 0 { + for iNdEx := len(m.Aliases) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Aliases[iNdEx]) + copy(dAtA[i:], m.Aliases[iNdEx]) + i = encodeVarintAlias(dAtA, i, uint64(len(m.Aliases[iNdEx]))) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func encodeVarintAlias(dAtA []byte, offset int, v uint64) int { + offset -= sovAlias(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *MultipleAliases) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Aliases) > 0 { + for _, s := range m.Aliases { + l = len(s) + n += 1 + l + sovAlias(uint64(l)) + } + } + return n +} + +func sovAlias(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozAlias(x uint64) (n int) { + return sovAlias(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *MultipleAliases) 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 ErrIntOverflowAlias + } + 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: MultipleAliases: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MultipleAliases: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Aliases", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAlias + } + 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 ErrInvalidLengthAlias + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAlias + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Aliases = append(m.Aliases, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipAlias(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthAlias + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipAlias(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, ErrIntOverflowAlias + } + 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, ErrIntOverflowAlias + } + 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, ErrIntOverflowAlias + } + 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, ErrInvalidLengthAlias + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupAlias + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthAlias + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthAlias = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowAlias = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupAlias = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/dymns/types/buy_offer.go b/x/dymns/types/buy_offer.go new file mode 100644 index 000000000..895aa0e8a --- /dev/null +++ b/x/dymns/types/buy_offer.go @@ -0,0 +1,161 @@ +package types + +import ( + "fmt" + "strconv" + "strings" + + errorsmod "cosmossdk.io/errors" + sdkmath "cosmossdk.io/math" + + sdk "github.com/cosmos/cosmos-sdk/types" + dymnsutils "github.com/dymensionxyz/dymension/v3/x/dymns/utils" + "github.com/dymensionxyz/gerr-cosmos/gerrc" +) + +// HasCounterpartyOfferPrice returns true if the offer has a raise-offer request from the Dym-Name owner. +func (m *BuyOrder) HasCounterpartyOfferPrice() bool { + return m.CounterpartyOfferPrice != nil && !m.CounterpartyOfferPrice.Amount.IsNil() && !m.CounterpartyOfferPrice.IsZero() +} + +// Validate performs basic validation for the BuyOrder. +func (m *BuyOrder) Validate() error { + if m == nil { + return errorsmod.Wrap(gerrc.ErrInvalidArgument, "offer is nil") + } + + if m.Id == "" { + return errorsmod.Wrap(gerrc.ErrInvalidArgument, "ID of offer is empty") + } + + if !IsValidBuyOrderId(m.Id) { + return errorsmod.Wrap(gerrc.ErrInvalidArgument, "ID of offer is not a valid offer id") + } + + switch m.AssetType { + case TypeName: + if !strings.HasPrefix(m.Id, BuyOrderIdTypeDymNamePrefix) { + return errorsmod.Wrap( + gerrc.ErrInvalidArgument, + "mismatch type of Buy-Order ID prefix and type", + ) + } + + if m.AssetId == "" { + return errorsmod.Wrap(gerrc.ErrInvalidArgument, "Dym-Name of offer is empty") + } + + if !dymnsutils.IsValidDymName(m.AssetId) { + return errorsmod.Wrap(gerrc.ErrInvalidArgument, "Dym-Name of offer is not a valid dym name") + } + case TypeAlias: + if !strings.HasPrefix(m.Id, BuyOrderIdTypeAliasPrefix) { + return errorsmod.Wrap( + gerrc.ErrInvalidArgument, + "mismatch type of Buy-Order ID prefix and type", + ) + } + + if m.AssetId == "" { + return errorsmod.Wrap(gerrc.ErrInvalidArgument, "alias of offer is empty") + } + + if !dymnsutils.IsValidAlias(m.AssetId) { + return errorsmod.Wrap(gerrc.ErrInvalidArgument, "alias of offer is not a valid alias") + } + default: + return errorsmod.Wrapf(gerrc.ErrInvalidArgument, "invalid asset type: %s", m.AssetType) + } + + if err := ValidateOrderParams(m.Params, m.AssetType); err != nil { + return err + } + + if !dymnsutils.IsValidBech32AccountAddress(m.Buyer, true) { + return errorsmod.Wrap(gerrc.ErrInvalidArgument, "buyer is not a valid bech32 account address") + } + + if m.OfferPrice.Amount.IsNil() || m.OfferPrice.IsZero() { + return errorsmod.Wrap(gerrc.ErrInvalidArgument, "offer price is zero") + } else if m.OfferPrice.IsNegative() { + return errorsmod.Wrap(gerrc.ErrInvalidArgument, "offer price is negative") + } else if err := m.OfferPrice.Validate(); err != nil { + return errorsmod.Wrapf(gerrc.ErrInvalidArgument, "offer price is invalid: %v", err) + } + + if m.HasCounterpartyOfferPrice() { + if m.CounterpartyOfferPrice.IsNegative() { + return errorsmod.Wrap(gerrc.ErrInvalidArgument, "counterparty offer price is negative") + } else if err := m.CounterpartyOfferPrice.Validate(); err != nil { + return errorsmod.Wrapf(gerrc.ErrInvalidArgument, "counterparty offer price is invalid: %v", err) + } + + if m.CounterpartyOfferPrice.Denom != m.OfferPrice.Denom { + return errorsmod.Wrap( + gerrc.ErrInvalidArgument, + "counterparty offer price denom is different from offer price denom", + ) + } + } + + return nil +} + +// GetSdkEvent returns the sdk event contains information of BuyOrder record. +// Fired when BuyOrder record is set into store. +func (m BuyOrder) GetSdkEvent(actionName string) sdk.Event { + var attrCounterpartyOfferPrice sdk.Attribute + if m.CounterpartyOfferPrice != nil { + attrCounterpartyOfferPrice = sdk.NewAttribute(AttributeKeyBoCounterpartyOfferPrice, m.CounterpartyOfferPrice.String()) + } else { + attrCounterpartyOfferPrice = sdk.NewAttribute(AttributeKeyBoCounterpartyOfferPrice, "") + } + + return sdk.NewEvent( + EventTypeBuyOrder, + sdk.NewAttribute(AttributeKeyBoId, m.Id), + sdk.NewAttribute(AttributeKeyBoAssetId, m.AssetId), + sdk.NewAttribute(AttributeKeyBoAssetType, m.AssetType.PrettyName()), + sdk.NewAttribute(AttributeKeyBoBuyer, m.Buyer), + sdk.NewAttribute(AttributeKeyBoOfferPrice, m.OfferPrice.String()), + attrCounterpartyOfferPrice, + sdk.NewAttribute(AttributeKeyBoActionName, actionName), + ) +} + +// IsValidBuyOrderId returns true if the given string is a valid ID for a Buy-Order record. +func IsValidBuyOrderId(id string) bool { + if len(id) < 3 { + return false + } + switch id[:2] { + case BuyOrderIdTypeDymNamePrefix: + case BuyOrderIdTypeAliasPrefix: + default: + return false + } + + ui, err := strconv.ParseUint(id[2:], 10, 64) + return err == nil && ui > 0 +} + +// CreateBuyOrderId creates a new BuyOrder ID from the given parameters. +func CreateBuyOrderId(_type AssetType, i uint64) string { + var prefix string + switch _type { + case TypeName: + prefix = BuyOrderIdTypeDymNamePrefix + case TypeAlias: + prefix = BuyOrderIdTypeAliasPrefix + default: + panic(fmt.Sprintf("unknown buy asset type: %d", _type)) + } + + buyOrderId := prefix + sdkmath.NewIntFromUint64(i).String() + + if !IsValidBuyOrderId(buyOrderId) { + panic("bad input parameters for creating buy order id") + } + + return buyOrderId +} diff --git a/x/dymns/types/buy_offer_test.go b/x/dymns/types/buy_offer_test.go new file mode 100644 index 000000000..e44059e46 --- /dev/null +++ b/x/dymns/types/buy_offer_test.go @@ -0,0 +1,614 @@ +package types + +import ( + "math" + "testing" + + "github.com/dymensionxyz/sdk-utils/utils/uptr" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/dymensionxyz/dymension/v3/app/params" + "github.com/stretchr/testify/require" +) + +func TestBuyOrder_HasCounterpartyOfferPrice(t *testing.T) { + require.False(t, (&BuyOrder{ + CounterpartyOfferPrice: nil, + }).HasCounterpartyOfferPrice()) + require.False(t, (&BuyOrder{ + CounterpartyOfferPrice: &sdk.Coin{}, + }).HasCounterpartyOfferPrice()) + require.False(t, (&BuyOrder{ + CounterpartyOfferPrice: uptr.To(testCoin(0)), + }).HasCounterpartyOfferPrice()) + require.True(t, (&BuyOrder{ + CounterpartyOfferPrice: uptr.To(testCoin(1)), + }).HasCounterpartyOfferPrice()) +} + +func TestBuyOrder_Validate(t *testing.T) { + t.Run("nil obj", func(t *testing.T) { + m := (*BuyOrder)(nil) + require.Error(t, m.Validate()) + }) + + //goland:noinspection SpellCheckingInspection + tests := []struct { + name string + orderId string + assetId string + assetType AssetType + params []string + buyer string + offerPrice sdk.Coin + counterpartyOfferPrice *sdk.Coin + wantErr bool + wantErrContains string + }{ + { + name: "pass - (Name) valid offer", + orderId: "101", + assetId: "my-name", + assetType: TypeName, + params: nil, + buyer: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + offerPrice: testCoin(1), + counterpartyOfferPrice: nil, + }, + { + name: "pass - (Alias) valid offer", + orderId: "201", + assetId: "alias", + assetType: TypeAlias, + params: []string{"rollapp_1-1"}, + buyer: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + offerPrice: testCoin(1), + counterpartyOfferPrice: nil, + }, + { + name: "pass - valid offer with counterparty offer price", + orderId: "101", + assetId: "my-name", + assetType: TypeName, + params: nil, + buyer: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + offerPrice: testCoin(1), + counterpartyOfferPrice: uptr.To(testCoin(2)), + }, + { + name: "pass - valid offer without counterparty offer price", + orderId: "101", + assetId: "my-name", + assetType: TypeName, + params: nil, + buyer: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + offerPrice: testCoin(1), + counterpartyOfferPrice: nil, + }, + { + name: "fail - empty offer ID", + orderId: "", + assetId: "my-name", + assetType: TypeName, + params: nil, + buyer: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + offerPrice: testCoin(1), + wantErr: true, + wantErrContains: "ID of offer is empty", + }, + { + name: "fail - offer ID prefix not match type, case Dym-Name", + orderId: CreateBuyOrderId(TypeAlias, 1), + assetId: "my-name", + assetType: TypeName, + params: nil, + buyer: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + offerPrice: testCoin(1), + wantErr: true, + wantErrContains: "mismatch type of Buy-Order ID prefix and type", + }, + { + name: "fail - offer ID prefix not match type, case Alias", + orderId: CreateBuyOrderId(TypeName, 1), + assetId: "my-name", + assetType: TypeAlias, + params: []string{"rollapp_1-1"}, + buyer: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + offerPrice: testCoin(1), + wantErr: true, + wantErrContains: "mismatch type of Buy-Order ID prefix and type", + }, + { + name: "fail - bad offer ID", + orderId: "@", + assetId: "my-name", + assetType: TypeName, + params: nil, + buyer: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + offerPrice: testCoin(1), + wantErr: true, + wantErrContains: "ID of offer is not a valid offer id", + }, + { + name: "fail - (Name) empty name", + orderId: "101", + assetId: "", + assetType: TypeName, + params: nil, + buyer: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + offerPrice: testCoin(1), + wantErr: true, + wantErrContains: "Dym-Name of offer is empty", + }, + { + name: "fail - (Alias) empty alias", + orderId: "201", + assetId: "", + assetType: TypeAlias, + params: []string{"rollapp_1-1"}, + buyer: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + offerPrice: testCoin(1), + wantErr: true, + wantErrContains: "alias of offer is empty", + }, + { + name: "fail - (Name) bad name", + orderId: "101", + assetId: "@", + assetType: TypeName, + params: nil, + buyer: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + offerPrice: testCoin(1), + wantErr: true, + wantErrContains: "Dym-Name of offer is not a valid dym name", + }, + { + name: "fail - (Alias) bad name", + orderId: "201", + assetId: "bad-alias", + assetType: TypeAlias, + params: []string{"rollapp_1-1"}, + buyer: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + offerPrice: testCoin(1), + wantErr: true, + wantErrContains: "alias of offer is not a valid alias", + }, + { + name: "fail - (Name) reject non-empty params", + orderId: "101", + assetId: "my-name", + assetType: TypeName, + params: []string{"non-empty"}, + buyer: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + offerPrice: testCoin(1), + wantErr: true, + wantErrContains: "not accept order params for asset type", + }, + { + name: "fail - (Alias) reject empty params", + orderId: "201", + assetId: "alias", + assetType: TypeAlias, + params: nil, + buyer: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + offerPrice: testCoin(1), + wantErr: true, + wantErrContains: "expect 1 order param of RollApp ID", + }, + { + name: "fail - (Alias) reject bad params", + orderId: "201", + assetId: "alias", + assetType: TypeAlias, + params: []string{"@chain"}, + buyer: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + offerPrice: testCoin(1), + wantErr: true, + wantErrContains: "invalid RollApp ID format", + }, + { + name: "fail - bad buyer", + orderId: "101", + assetId: "my-name", + assetType: TypeName, + params: nil, + buyer: "0x1", + offerPrice: testCoin(1), + wantErr: true, + wantErrContains: "buyer is not a valid bech32 account address", + }, + { + name: "fail - offer price is zero", + orderId: "101", + assetId: "my-name", + assetType: TypeName, + params: nil, + buyer: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + offerPrice: testCoin(0), + wantErr: true, + wantErrContains: "offer price is zero", + }, + { + name: "fail - offer price is empty", + orderId: "101", + assetId: "my-name", + assetType: TypeName, + params: nil, + buyer: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + offerPrice: sdk.Coin{}, + wantErr: true, + wantErrContains: "offer price is zero", + }, + { + name: "fail - offer price is negative", + orderId: "101", + assetId: "my-name", + assetType: TypeName, + params: nil, + buyer: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + offerPrice: testCoin(-1), + wantErr: true, + wantErrContains: "offer price is negative", + }, + { + name: "fail - offer price is invalid", + orderId: "101", + assetId: "my-name", + assetType: TypeName, + params: nil, + buyer: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + offerPrice: sdk.Coin{ + Denom: "-", + Amount: sdk.OneInt(), + }, + wantErr: true, + wantErrContains: "offer price is invalid", + }, + { + name: "pass - counter-party offer price is zero", + orderId: "101", + assetId: "my-name", + assetType: TypeName, + params: nil, + buyer: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + offerPrice: testCoin(1), + counterpartyOfferPrice: uptr.To(testCoin(0)), + }, + { + name: "pass - counter-party offer price is empty", + orderId: "101", + assetId: "my-name", + assetType: TypeName, + params: nil, + buyer: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + offerPrice: testCoin(1), + counterpartyOfferPrice: &sdk.Coin{}, + }, + { + name: "fail - counter-party offer price is negative", + orderId: "101", + assetId: "my-name", + assetType: TypeName, + params: nil, + buyer: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + offerPrice: testCoin(1), + counterpartyOfferPrice: uptr.To(testCoin(-1)), + wantErr: true, + wantErrContains: "counterparty offer price is negative", + }, + { + name: "fail - counter-party offer price is invalid", + orderId: "101", + assetId: "my-name", + assetType: TypeName, + params: nil, + buyer: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + offerPrice: testCoin(1), + counterpartyOfferPrice: &sdk.Coin{ + Denom: "-", + Amount: sdk.OneInt(), + }, + wantErr: true, + wantErrContains: "counterparty offer price is invalid", + }, + { + name: "pass - counterparty offer price can be less than offer price", + orderId: "101", + assetId: "my-name", + assetType: TypeName, + params: nil, + buyer: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + offerPrice: testCoin(2), + counterpartyOfferPrice: uptr.To(testCoin(1)), + wantErr: false, + }, + { + name: "pass - counterparty offer price can be equals to offer price", + orderId: "101", + assetId: "my-name", + assetType: TypeName, + params: nil, + buyer: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + offerPrice: testCoin(2), + counterpartyOfferPrice: uptr.To(testCoin(2)), + wantErr: false, + }, + { + name: "pass - counterparty offer price can be greater than offer price", + orderId: "101", + assetId: "my-name", + assetType: TypeName, + params: nil, + buyer: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + offerPrice: testCoin(2), + counterpartyOfferPrice: uptr.To(testCoin(3)), + wantErr: false, + }, + { + name: "fail - counterparty offer price denom must match offer price denom", + orderId: "101", + assetId: "my-name", + assetType: TypeName, + params: nil, + buyer: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + offerPrice: testCoin(1), + counterpartyOfferPrice: uptr.To(sdk.NewInt64Coin("u"+params.BaseDenom, 2)), + wantErr: true, + wantErrContains: "counterparty offer price denom is different from offer price denom", + }, + { + name: "fail - reject unknown asset type", + orderId: "101", + assetId: "asset", + assetType: AssetType_AT_UNKNOWN, + buyer: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + offerPrice: testCoin(1), + wantErr: true, + wantErrContains: "invalid asset type", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + m := &BuyOrder{ + Id: tt.orderId, + AssetId: tt.assetId, + AssetType: tt.assetType, + Params: tt.params, + Buyer: tt.buyer, + OfferPrice: tt.offerPrice, + CounterpartyOfferPrice: tt.counterpartyOfferPrice, + } + + err := m.Validate() + if tt.wantErr { + require.NotEmpty(t, tt.wantErrContains, "mis-configured test case") + require.Error(t, err) + require.Contains(t, err.Error(), tt.wantErrContains) + return + } + + require.NoError(t, err) + }) + } +} + +//goland:noinspection SpellCheckingInspection +func TestBuyOrder_GetSdkEvent(t *testing.T) { + t.Run("all fields", func(t *testing.T) { + event := BuyOrder{ + Id: "1", + AssetId: "a", + AssetType: TypeName, + Buyer: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + OfferPrice: testCoin(1), + CounterpartyOfferPrice: uptr.To(testCoin(2)), + }.GetSdkEvent("action-name") + requireEventEquals(t, event, + EventTypeBuyOrder, + AttributeKeyBoId, "1", + AttributeKeyBoAssetId, "a", + AttributeKeyBoAssetType, TypeName.PrettyName(), + AttributeKeyBoBuyer, "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + AttributeKeyBoOfferPrice, "1"+params.BaseDenom, + AttributeKeyBoCounterpartyOfferPrice, "2"+params.BaseDenom, + AttributeKeyBoActionName, "action-name", + ) + }) + + t.Run("BO type Alias", func(t *testing.T) { + event := BuyOrder{ + Id: "1", + AssetId: "a", + AssetType: TypeAlias, + Buyer: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + OfferPrice: testCoin(1), + CounterpartyOfferPrice: uptr.To(testCoin(2)), + }.GetSdkEvent("action-name") + require.NotNil(t, event) + require.Equal(t, EventTypeBuyOrder, event.Type) + require.Len(t, event.Attributes, 7) + require.Equal(t, AttributeKeyBoAssetType, event.Attributes[2].Key) + require.Equal(t, TypeAlias.PrettyName(), event.Attributes[2].Value) + }) + + t.Run("no counterparty offer price", func(t *testing.T) { + event := BuyOrder{ + Id: "1", + AssetId: "a", + AssetType: TypeName, + Buyer: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + OfferPrice: testCoin(1), + CounterpartyOfferPrice: nil, + }.GetSdkEvent("action-name") + requireEventEquals(t, event, + EventTypeBuyOrder, + AttributeKeyBoId, "1", + AttributeKeyBoAssetId, "a", + AttributeKeyBoAssetType, TypeName.PrettyName(), + AttributeKeyBoBuyer, "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + AttributeKeyBoOfferPrice, "1"+params.BaseDenom, + AttributeKeyBoCounterpartyOfferPrice, "", + AttributeKeyBoActionName, "action-name", + ) + }) +} + +func TestIsValidBuyOrderId(t *testing.T) { + tests := []struct { + name string + id string + wantValid bool + }{ + { + name: "pass - positive number", + id: "101", + wantValid: true, + }, + { + name: "pass - positive number", + id: "201", + wantValid: true, + }, + { + name: "fail - reject zero", + id: "100", + wantValid: false, + }, + { + name: "fail - reject zero", + id: "200", + wantValid: false, + }, + { + name: "fail - reject empty", + id: "", + wantValid: false, + }, + { + name: "fail - reject 1 char", + id: "1", + wantValid: false, + }, + { + name: "fail - reject 2 chars", + id: "10", + wantValid: false, + }, + { + name: "fail - reject negative", + id: "10-1", + wantValid: false, + }, + { + name: "fail - reject negative", + id: "20-1", + wantValid: false, + }, + { + name: "fail - reject non-numeric", + id: "10a", + wantValid: false, + }, + { + name: "fail - reject non-numeric", + id: "20a", + wantValid: false, + }, + { + name: "pass - maximum is max uint64", + id: "10" + "18446744073709551615", + wantValid: true, + }, + { + name: "pass - maximum is max uint64", + id: "20" + "18446744073709551615", + wantValid: true, + }, + { + name: "fail - reject out-of-bound uint64", + id: "10" + "18446744073709551616", // max uint64 + 1 + wantValid: false, + }, + { + name: "fail - reject out-of-bound uint64", + id: "20" + "18446744073709551616", // max uint64 + 1 + wantValid: false, + }, + { + name: "fail - reject unrecognized prefix", + id: "OO1", + wantValid: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + require.Equal(t, tt.wantValid, IsValidBuyOrderId(tt.id)) + }) + } +} + +func TestCreateBuyOrderId(t *testing.T) { + tests := []struct { + name string + _type AssetType + i uint64 + want string + wantPanic bool + }{ + { + name: "pass - type Dym-Name", + _type: TypeName, + i: 1, + want: "101", + }, + { + name: "pass - type Alias", + _type: TypeAlias, + i: 1, + want: "201", + }, + { + name: "pass - type Dym-Name, max uint64", + _type: TypeName, + i: math.MaxUint64, + want: "10" + "18446744073709551615", + }, + { + name: "pass - type Alias, max uint64", + _type: TypeAlias, + i: math.MaxUint64, + want: "20" + "18446744073709551615", + }, + { + name: "fail - reject unknown type", + _type: AssetType_AT_UNKNOWN, + i: 1, + wantPanic: true, + }, + { + name: "fail - reject bad input number", + _type: TypeName, + i: 0, + wantPanic: true, + }, + { + name: "fail - reject bad input number", + _type: TypeAlias, + i: 0, + wantPanic: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if tt.wantPanic { + require.Panics(t, func() { + _ = CreateBuyOrderId(tt._type, tt.i) + }) + return + } + got := CreateBuyOrderId(tt._type, tt.i) + require.Equal(t, tt.want, got) + require.True(t, IsValidBuyOrderId(got)) + }) + } +} diff --git a/x/dymns/types/codec.go b/x/dymns/types/codec.go new file mode 100644 index 000000000..177534bf5 --- /dev/null +++ b/x/dymns/types/codec.go @@ -0,0 +1,49 @@ +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" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" +) + +// RegisterCodec registers the necessary types and interfaces for the module +func RegisterCodec(cdc *codec.LegacyAmino) { + cdc.RegisterConcrete(&MsgRegisterName{}, "dymns/RegisterName", nil) + cdc.RegisterConcrete(&MsgTransferDymNameOwnership{}, "dymns/TransferDymNameOwnership", nil) + cdc.RegisterConcrete(&MsgSetController{}, "dymns/SetController", nil) + cdc.RegisterConcrete(&MsgUpdateResolveAddress{}, "dymns/UpdateResolveAddress", nil) + cdc.RegisterConcrete(&MsgUpdateDetails{}, "dymns/UpdateDetails", nil) + cdc.RegisterConcrete(&MsgPlaceSellOrder{}, "dymns/PlaceSellOrder", nil) + cdc.RegisterConcrete(&MsgCancelSellOrder{}, "dymns/CancelSellOrder", nil) + cdc.RegisterConcrete(&MsgPurchaseOrder{}, "dymns/PurchaseName", nil) +} + +// RegisterInterfaces registers implementations by its interface, for the module +func RegisterInterfaces(registry cdctypes.InterfaceRegistry) { + registry.RegisterImplementations( + (*sdk.Msg)(nil), + &MsgRegisterName{}, + &MsgTransferDymNameOwnership{}, + &MsgSetController{}, + &MsgUpdateResolveAddress{}, + &MsgUpdateDetails{}, + &MsgPlaceSellOrder{}, + &MsgCancelSellOrder{}, + &MsgPurchaseOrder{}, + ) + + registry.RegisterImplementations( + (*govtypes.Content)(nil), + &MigrateChainIdsProposal{}, + &UpdateAliasesProposal{}, + ) + + msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc) +} + +var ( + Amino = codec.NewLegacyAmino() + ModuleCdc = codec.NewProtoCodec(cdctypes.NewInterfaceRegistry()) +) diff --git a/x/dymns/types/codec_test.go b/x/dymns/types/codec_test.go new file mode 100644 index 000000000..bdb14b698 --- /dev/null +++ b/x/dymns/types/codec_test.go @@ -0,0 +1,25 @@ +package types + +import ( + "testing" + + "github.com/cosmos/cosmos-sdk/codec" + cdctypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/stretchr/testify/require" +) + +func TestRegisterCodec(t *testing.T) { + t.Run("can register codec", func(t *testing.T) { + require.NotPanics(t, func() { + RegisterCodec(codec.NewLegacyAmino()) + }) + }) +} + +func TestRegisterInterfaces(t *testing.T) { + t.Run("can register interfaces", func(t *testing.T) { + require.NotPanics(t, func() { + RegisterInterfaces(cdctypes.NewInterfaceRegistry()) + }) + }) +} diff --git a/x/dymns/types/constants.go b/x/dymns/types/constants.go new file mode 100644 index 000000000..b0ab5b994 --- /dev/null +++ b/x/dymns/types/constants.go @@ -0,0 +1,70 @@ +package types + +import ( + sdkmath "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +const ( + // MaxDymNameContactLength is the maximum length allowed for Dym-Name contact. + MaxDymNameContactLength = 140 + + // MaxConfigSize is the maximum size allowed for number Dym-Name configuration per Dym-Name. + // This is another layer protects spamming the chain with large data. + MaxConfigSize = 100 + + // MinDymNamePriceStepsCount is the minimum number of price steps required for Dym-Name price. + MinDymNamePriceStepsCount = 4 + + // MinAliasPriceStepsCount is the minimum number of price steps required for Alias price. + MinAliasPriceStepsCount +) + +// MinPriceValue is the minimum value allowed for price configuration. +var MinPriceValue = sdkmath.NewInt(1e18) + +const ( + // OpGasPlaceSellOrder is the gas consumed when a Dym-Name owner creates a Sell-Order for selling Dym-Name. + OpGasPlaceSellOrder sdk.Gas = 25_000_000 + // OpGasCloseSellOrder is the gas consumed when Dym-Name owner closes Sell-Order. + OpGasCloseSellOrder sdk.Gas = 5_000_000 + + // OpGasPlaceBidOnSellOrder is the gas consumed when a buyer bids on a Dym-Name Sell-Order. + OpGasPlaceBidOnSellOrder sdk.Gas = 20_000_000 + + // OpGasConfig is the gas consumed when Dym-Name controller updating Dym-Name configuration, + // We charge this high amount of gas for extra permanent data + // needed to be stored like reverse lookup record. + // We do not charge this fee on Delete operation. + OpGasConfig sdk.Gas = 35_000_000 + + // OpGasUpdateContact is the gas consumed when Dym-Name controller updating Dym-Name contact. + // We do not charge this fee on clear Contact operation. + OpGasUpdateContact sdk.Gas = 1_000_000 + + // OpGasPutBuyOrder is the gas consumed when a buyer placing a buy order, offer to buy Dym-Name. + OpGasPutBuyOrder sdk.Gas = 25_000_000 + + // OpGasUpdateBuyOrder is the gas consumed when the buyer who placed the buy order, + // updating the offer to buy Dym-Name. + OpGasUpdateBuyOrder sdk.Gas = 20_000_000 + + // OpGasCloseBuyOrder is the gas consumed when the buyer who placed the buy order, + // closing the offer to buy Dym-Name. + OpGasCloseBuyOrder sdk.Gas = 5_000_000 +) + +const ( + // DoNotModifyDesc is a constant used in flags to indicate that description field should not be updated + DoNotModifyDesc = "[do-not-modify]" +) + +const ( + BuyOrderIdTypeDymNamePrefix = "10" + BuyOrderIdTypeAliasPrefix = "20" +) + +const ( + // LimitMaxElementsInApiRequest is the maximum number of elements allowed in a single API request. + LimitMaxElementsInApiRequest = 100 +) diff --git a/x/dymns/types/dym_name.go b/x/dymns/types/dym_name.go new file mode 100644 index 000000000..e5825248c --- /dev/null +++ b/x/dymns/types/dym_name.go @@ -0,0 +1,320 @@ +package types + +import ( + "fmt" + "sort" + "strings" + + errorsmod "cosmossdk.io/errors" + + "github.com/dymensionxyz/gerr-cosmos/gerrc" + + sdk "github.com/cosmos/cosmos-sdk/types" + dymnsutils "github.com/dymensionxyz/dymension/v3/x/dymns/utils" +) + +// Validate checks if the DymName record is valid. +func (m *DymName) Validate() error { + if m == nil { + return errorsmod.Wrap(gerrc.ErrInvalidArgument, "dym name is nil") + } + if m.Name == "" { + return errorsmod.Wrap(gerrc.ErrInvalidArgument, "name is empty") + } + if !dymnsutils.IsValidDymName(m.Name) { + return errorsmod.Wrap(gerrc.ErrInvalidArgument, "name is not a valid dym name") + } + if m.Owner == "" { + return errorsmod.Wrap(gerrc.ErrInvalidArgument, "owner is empty") + } + if !dymnsutils.IsValidBech32AccountAddress(m.Owner, true) { + return errorsmod.Wrapf(gerrc.ErrInvalidArgument, "owner is not a valid bech32 account address: %s", m.Owner) + } + if m.Controller == "" { + return errorsmod.Wrap(gerrc.ErrInvalidArgument, "controller is empty") + } + if !dymnsutils.IsValidBech32AccountAddress(m.Controller, true) { + return errorsmod.Wrap(gerrc.ErrInvalidArgument, "controller is not a valid bech32 account address") + } + if m.ExpireAt == 0 { + return errorsmod.Wrap(gerrc.ErrInvalidArgument, "expiry is empty") + } + + if len(m.Configs) > MaxConfigSize { + return errorsmod.Wrapf( + gerrc.ErrResourceExhausted, + "maximum number of configs allowed: %d", MaxConfigSize, + ) + } + + uniqueConfig := make(map[string]bool) + // Describe usage of Go Map: only used for validation + for _, config := range m.Configs { + if err := config.Validate(); err != nil { + return err + } + + configIdentity := config.GetIdentity() + if _, duplicated := uniqueConfig[configIdentity]; duplicated { + return errorsmod.Wrapf( + gerrc.ErrInvalidArgument, "dym name config is not unique: %s", configIdentity, + ) + } + uniqueConfig[configIdentity] = true + } + + if len(m.Contact) > MaxDymNameContactLength { + return errorsmod.Wrapf( + gerrc.ErrInvalidArgument, + "invalid contact length; got: %d, max: %d", len(m.Contact), MaxDymNameContactLength, + ) + } + + return nil +} + +// Validate checks if the DymNameConfig record is valid. +func (m *DymNameConfig) Validate() error { + if m == nil { + return errorsmod.Wrap(gerrc.ErrInvalidArgument, "dym name config is nil") + } + + if m.ChainId == "" { + // ok to be empty + } else if !dymnsutils.IsValidChainIdFormat(m.ChainId) { + return errorsmod.Wrap( + gerrc.ErrInvalidArgument, + "dym name config chain id must be a valid chain id format", + ) + } + + if m.Path == "" { + // ok to be empty + } else if !dymnsutils.IsValidSubDymName(m.Path) { + return errorsmod.Wrap(gerrc.ErrInvalidArgument, "dym name config path must be a valid dym name") + } + + if m.Type == DymNameConfigType_DCT_NAME { + if m.ChainId == "" { + if m.Value != strings.ToLower(m.Value) { + return errorsmod.Wrap(gerrc.ErrInvalidArgument, "dym name config value on host-chain must be lowercase") + } + } + + if !m.IsDelete() { + if m.ChainId == "" { + if !dymnsutils.IsValidBech32AccountAddress(m.Value, false) { + return errorsmod.Wrap( + gerrc.ErrInvalidArgument, + "dym name config value must be a valid bech32 account address", + ) + } + } else { + if !dymnsutils.PossibleAccountRegardlessChain(m.Value) { + return errorsmod.Wrapf( + gerrc.ErrInvalidArgument, + "dym name config value: %s", m.Value, + ) + } + } + } + } else { + return errorsmod.Wrapf( + gerrc.ErrInvalidArgument, + "Dym-Name config type must be: %s", DymNameConfigType_DCT_NAME.String(), + ) + } + + return nil +} + +// Validate checks if the ReverseLookupDymNames record is valid. +func (m *ReverseLookupDymNames) Validate() error { + if m == nil { + return errorsmod.Wrap(gerrc.ErrInvalidArgument, "reverse lookup record is nil") + } + + for _, name := range m.DymNames { + if !dymnsutils.IsValidDymName(name) { + return errorsmod.Wrapf(gerrc.ErrInvalidArgument, "invalid dym name: %s", name) + } + } + + return nil +} + +// IsExpiredAtCtx returns true if the Dym-Name is expired at the given context. +// It compares the expiry with the block time in context. +func (m DymName) IsExpiredAtCtx(ctx sdk.Context) bool { + return m.ExpireAt < ctx.BlockTime().Unix() +} + +// GetSdkEvent returns the sdk event contains information of Dym-Name. +// Fired when Dym-Name record is set into store. +func (m DymName) GetSdkEvent() sdk.Event { + return sdk.NewEvent( + EventTypeSetDymName, + sdk.NewAttribute(AttributeKeyDymName, m.Name), + sdk.NewAttribute(AttributeKeyDymNameOwner, m.Owner), + sdk.NewAttribute(AttributeKeyDymNameController, m.Controller), + sdk.NewAttribute(AttributeKeyDymNameExpiryEpoch, fmt.Sprintf("%d", m.ExpireAt)), + sdk.NewAttribute(AttributeKeyDymNameConfigCount, fmt.Sprintf("%d", len(m.Configs))), + sdk.NewAttribute(AttributeKeyDymNameHasContactDetails, fmt.Sprintf("%t", m.Contact != "")), + ) +} + +// GetIdentity returns the unique identity of the DymNameConfig record. +// Used for uniqueness check. +func (m DymNameConfig) GetIdentity() string { + return strings.ToLower(fmt.Sprintf("%s|%s|%s", m.Type, m.ChainId, m.Path)) +} + +// IsDefaultNameConfig checks if the config is a default name config, satisfy the following conditions: +// - Type is NAME +// - ChainId is empty (means host chain) +// - Path is empty (means root Dym-Name) +func (m DymNameConfig) IsDefaultNameConfig() bool { + return m.Type == DymNameConfigType_DCT_NAME && + m.ChainId == "" && + m.Path == "" +} + +// IsDelete checks if the config is a delete operation. +// A delete operation is when the value is empty. +func (m DymNameConfig) IsDelete() bool { + return m.Value == "" +} + +// DymNameConfigs is a list of DymNameConfig records. +// Used to add some operations on the list. +type DymNameConfigs []DymNameConfig + +// DefaultNameConfigs returns a list of default name configs. +// It returns a list instead of a single record with purpose is to negotiate case +// where both add and delete operations are present. +func (m DymNameConfigs) DefaultNameConfigs(dropEmptyValueConfigs bool) DymNameConfigs { + var defaultConfigs DymNameConfigs + for _, config := range m { + if config.IsDefaultNameConfig() { + if dropEmptyValueConfigs { + if config.Value == "" { + continue + } + } + defaultConfigs = append(defaultConfigs, config) + } + } + return defaultConfigs +} + +// GetAddressesForReverseMapping parses the Dym-Name configuration and returns a map of addresses to their configurations. +func (m *DymName) GetAddressesForReverseMapping() ( + configuredAddressesToConfigs map[string][]DymNameConfig, + fallbackAddressesToConfigs map[string][]DymNameConfig, + // Describe usage of Go Map: used to mapping each address to its configuration, + // caller should have responsibility to handle the result and aware of iterating over map can cause non-determinism +) { + if err := m.Validate(); err != nil { + // should validate before calling this method + panic(err) + } + + configuredAddressesToConfigs = make(map[string][]DymNameConfig) + fallbackAddressesToConfigs = make(map[string][]DymNameConfig) + + addConfiguredAddress := func(address string, config DymNameConfig) { + configuredAddressesToConfigs[address] = append(configuredAddressesToConfigs[address], config) + } + + addFallbackAddress := func(fallbackAddr FallbackAddress, config DymNameConfig) { + strAddr := fallbackAddr.String() + fallbackAddressesToConfigs[strAddr] = append(fallbackAddressesToConfigs[strAddr], config) + } + + var nameConfigs []DymNameConfig + for _, config := range m.Configs { + if config.Type == DymNameConfigType_DCT_NAME { + nameConfigs = append(nameConfigs, config) + } + } + + var defaultConfig *DymNameConfig + for i, config := range nameConfigs { + if config.IsDefaultNameConfig() { + if config.Value == "" { + config.Value = m.Owner + nameConfigs[i] = config + } + + defaultConfig = &config + break + } + } + + if defaultConfig == nil { + // add a fake record to be used to generate default address + nameConfigs = append(nameConfigs, DymNameConfig{ + Type: DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "", + Value: m.Owner, + }) + } + + for _, config := range nameConfigs { + if config.Value == "" { + continue + } + + if config.IsDefaultNameConfig() { + // default config is for host chain only so value must be valid bech32 + accAddr, err := sdk.AccAddressFromBech32(config.Value) + if err != nil { + // should not happen as configuration should be validated before calling this method + panic(err) + } + + addConfiguredAddress(config.Value, config) + addFallbackAddress(FallbackAddress(accAddr), config) + + continue + } + + addConfiguredAddress(config.Value, config) + + // note: this config is not a default config, is not a part of fallback mechanism, + // so we don't add fallback address for this config + } + + return +} + +// Distinct returns a new list of dym names with duplicates removed. +// Result will be sorted. +func (m ReverseLookupDymNames) Distinct() (distinct ReverseLookupDymNames) { + return ReverseLookupDymNames{ + DymNames: StringList(m.DymNames).Distinct(), + } +} + +// Combine merges the dym names from the current list and the other list. +// Result will be sorted distinct. +func (m ReverseLookupDymNames) Combine(other ReverseLookupDymNames) ReverseLookupDymNames { + return ReverseLookupDymNames{ + DymNames: StringList(m.DymNames).Combine(other.DymNames), + }.Distinct() +} + +// Exclude removes the dym names from the current list that are in the toBeExcluded list. +// Result will be sorted distinct. +func (m ReverseLookupDymNames) Exclude(toBeExcluded ReverseLookupDymNames) (afterExcluded ReverseLookupDymNames) { + return ReverseLookupDymNames{ + DymNames: StringList(m.DymNames).Exclude(toBeExcluded.DymNames), + }.Distinct() +} + +// Sort sorts the dym names in the list. +func (m ReverseLookupDymNames) Sort() ReverseLookupDymNames { + sort.Strings(m.DymNames) + return m +} diff --git a/x/dymns/types/dym_name.pb.go b/x/dymns/types/dym_name.pb.go new file mode 100644 index 000000000..16d6f1e82 --- /dev/null +++ b/x/dymns/types/dym_name.pb.go @@ -0,0 +1,1122 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: dymensionxyz/dymension/dymns/dym_name.proto + +package types + +import ( + fmt "fmt" + _ "github.com/cosmos/cosmos-sdk/types" + _ "github.com/cosmos/gogoproto/gogoproto" + proto "github.com/cosmos/gogoproto/proto" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// DymNameConfigType specifies the type of the Dym-Name configuration. +// Currently only supports Name, similar to DNS. +type DymNameConfigType int32 + +const ( + DymNameConfigType_DCT_UNKNOWN DymNameConfigType = 0 + DymNameConfigType_DCT_NAME DymNameConfigType = 1 +) + +var DymNameConfigType_name = map[int32]string{ + 0: "DCT_UNKNOWN", + 1: "DCT_NAME", +} + +var DymNameConfigType_value = map[string]int32{ + "DCT_UNKNOWN": 0, + "DCT_NAME": 1, +} + +func (x DymNameConfigType) String() string { + return proto.EnumName(DymNameConfigType_name, int32(x)) +} + +func (DymNameConfigType) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_463436600bef60e6, []int{0} +} + +// DymName defines a Dym-Name, the mainly purpose is to store ownership and resolution information. +// Dym-Name is similar to DNS. It is a human-readable name that maps to a chain address. +// One Dym-Name can have multiple configurations, each configuration is a resolution record. +// Dym-Name is owned by an account, and is able to grant permission to another account to control the Dym-Name. +type DymName struct { + // name is the human-readable name of the Dym-Name. + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + // owner is the account address that owns the Dym-Name. Owner has permission to transfer ownership. + Owner string `protobuf:"bytes,2,opt,name=owner,proto3" json:"owner,omitempty"` + // controller is the account address that has permission update configuration for the Dym-Name. + // Default is the owner. Able to transfer control to another account by the owner. + // Users can set Dym-Name owned by Cold-Wallet and controlled by Hot-Wallet. + Controller string `protobuf:"bytes,3,opt,name=controller,proto3" json:"controller,omitempty"` + // expire_at is the UTC epoch represent the last effective date of the Dym-Name, + // after which the Dym-Name is no longer valid. + // NOTE: Expired Dym-Names are not deleted from the store + // because iterating through store is very expensive because expiry date must be checked every use. + ExpireAt int64 `protobuf:"varint,4,opt,name=expire_at,json=expireAt,proto3" json:"expire_at,omitempty"` + // configs are configuration records for the Dym-Name. + Configs []DymNameConfig `protobuf:"bytes,5,rep,name=configs,proto3" json:"configs"` + // contact is an optional information for the Dym-Name. + // Convenient for retails users. + Contact string `protobuf:"bytes,6,opt,name=contact,proto3" json:"contact,omitempty"` +} + +func (m *DymName) Reset() { *m = DymName{} } +func (m *DymName) String() string { return proto.CompactTextString(m) } +func (*DymName) ProtoMessage() {} +func (*DymName) Descriptor() ([]byte, []int) { + return fileDescriptor_463436600bef60e6, []int{0} +} +func (m *DymName) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *DymName) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_DymName.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 *DymName) XXX_Merge(src proto.Message) { + xxx_messageInfo_DymName.Merge(m, src) +} +func (m *DymName) XXX_Size() int { + return m.Size() +} +func (m *DymName) XXX_DiscardUnknown() { + xxx_messageInfo_DymName.DiscardUnknown(m) +} + +var xxx_messageInfo_DymName proto.InternalMessageInfo + +func (m *DymName) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *DymName) GetOwner() string { + if m != nil { + return m.Owner + } + return "" +} + +func (m *DymName) GetController() string { + if m != nil { + return m.Controller + } + return "" +} + +func (m *DymName) GetExpireAt() int64 { + if m != nil { + return m.ExpireAt + } + return 0 +} + +func (m *DymName) GetConfigs() []DymNameConfig { + if m != nil { + return m.Configs + } + return nil +} + +func (m *DymName) GetContact() string { + if m != nil { + return m.Contact + } + return "" +} + +// DymNameConfig contains the resolution configuration for the Dym-Name. +// Each record is a resolution record, similar to DNS. +type DymNameConfig struct { + // type is the type of the Dym-Name configuration (equals to Type in DNS). + Type DymNameConfigType `protobuf:"varint,1,opt,name=type,proto3,enum=dymensionxyz.dymension.dymns.DymNameConfigType" json:"type,omitempty"` + // chain_id is the chain-id of the Dym-Name configuration (equals to top-level-domain). + // If empty, the configuration is for host chain (Dymension Hub). + ChainId string `protobuf:"bytes,2,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` + // path of the Dym-Name configuration (equals to Host in DNS). + // If the type of this config record is Name, it is the Sub-Name of the Dym-Name Address. + Path string `protobuf:"bytes,3,opt,name=path,proto3" json:"path,omitempty"` + // value of the Dym-Name configuration resolves to (equals to Value in DNS). + // If the type of this config record is Name, it is the address which the Dym-Name Address resolves to. + Value string `protobuf:"bytes,4,opt,name=value,proto3" json:"value,omitempty"` +} + +func (m *DymNameConfig) Reset() { *m = DymNameConfig{} } +func (m *DymNameConfig) String() string { return proto.CompactTextString(m) } +func (*DymNameConfig) ProtoMessage() {} +func (*DymNameConfig) Descriptor() ([]byte, []int) { + return fileDescriptor_463436600bef60e6, []int{1} +} +func (m *DymNameConfig) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *DymNameConfig) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_DymNameConfig.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 *DymNameConfig) XXX_Merge(src proto.Message) { + xxx_messageInfo_DymNameConfig.Merge(m, src) +} +func (m *DymNameConfig) XXX_Size() int { + return m.Size() +} +func (m *DymNameConfig) XXX_DiscardUnknown() { + xxx_messageInfo_DymNameConfig.DiscardUnknown(m) +} + +var xxx_messageInfo_DymNameConfig proto.InternalMessageInfo + +func (m *DymNameConfig) GetType() DymNameConfigType { + if m != nil { + return m.Type + } + return DymNameConfigType_DCT_UNKNOWN +} + +func (m *DymNameConfig) GetChainId() string { + if m != nil { + return m.ChainId + } + return "" +} + +func (m *DymNameConfig) GetPath() string { + if m != nil { + return m.Path + } + return "" +} + +func (m *DymNameConfig) GetValue() string { + if m != nil { + return m.Value + } + return "" +} + +// ReverseLookupDymNames contains a list of Dym-Names for reverse lookup. +type ReverseLookupDymNames struct { + // dym_names is a list of name of the Dym-Names linked to the reverse-lookup record. + DymNames []string `protobuf:"bytes,1,rep,name=dym_names,json=dymNames,proto3" json:"dym_names,omitempty"` +} + +func (m *ReverseLookupDymNames) Reset() { *m = ReverseLookupDymNames{} } +func (m *ReverseLookupDymNames) String() string { return proto.CompactTextString(m) } +func (*ReverseLookupDymNames) ProtoMessage() {} +func (*ReverseLookupDymNames) Descriptor() ([]byte, []int) { + return fileDescriptor_463436600bef60e6, []int{2} +} +func (m *ReverseLookupDymNames) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ReverseLookupDymNames) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ReverseLookupDymNames.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 *ReverseLookupDymNames) XXX_Merge(src proto.Message) { + xxx_messageInfo_ReverseLookupDymNames.Merge(m, src) +} +func (m *ReverseLookupDymNames) XXX_Size() int { + return m.Size() +} +func (m *ReverseLookupDymNames) XXX_DiscardUnknown() { + xxx_messageInfo_ReverseLookupDymNames.DiscardUnknown(m) +} + +var xxx_messageInfo_ReverseLookupDymNames proto.InternalMessageInfo + +func (m *ReverseLookupDymNames) GetDymNames() []string { + if m != nil { + return m.DymNames + } + return nil +} + +func init() { + proto.RegisterEnum("dymensionxyz.dymension.dymns.DymNameConfigType", DymNameConfigType_name, DymNameConfigType_value) + proto.RegisterType((*DymName)(nil), "dymensionxyz.dymension.dymns.DymName") + proto.RegisterType((*DymNameConfig)(nil), "dymensionxyz.dymension.dymns.DymNameConfig") + proto.RegisterType((*ReverseLookupDymNames)(nil), "dymensionxyz.dymension.dymns.ReverseLookupDymNames") +} + +func init() { + proto.RegisterFile("dymensionxyz/dymension/dymns/dym_name.proto", fileDescriptor_463436600bef60e6) +} + +var fileDescriptor_463436600bef60e6 = []byte{ + // 436 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x52, 0xdd, 0x6e, 0xd3, 0x30, + 0x14, 0x8e, 0x69, 0xb7, 0xb6, 0x1e, 0x3f, 0xc3, 0x1a, 0x92, 0x19, 0xc8, 0x54, 0xbd, 0x8a, 0x98, + 0x14, 0x6b, 0x19, 0x2f, 0xb0, 0x75, 0x5c, 0xa0, 0x8d, 0x20, 0x45, 0x43, 0x48, 0xdc, 0x44, 0x4e, + 0x62, 0xd2, 0x88, 0xc6, 0x8e, 0x62, 0x37, 0x34, 0x3c, 0x05, 0xb7, 0xbc, 0xd1, 0x2e, 0x77, 0x07, + 0x57, 0x08, 0xb5, 0x2f, 0x82, 0xec, 0xa4, 0x53, 0x11, 0x02, 0x89, 0x9b, 0xe8, 0x7c, 0xdf, 0x39, + 0x9f, 0xcf, 0x39, 0x5f, 0x0e, 0x3c, 0x4a, 0x9b, 0x82, 0x0b, 0x95, 0x4b, 0xb1, 0x6c, 0x3e, 0xd3, + 0x5b, 0x60, 0x22, 0xa1, 0xcc, 0x37, 0x12, 0xac, 0xe0, 0x5e, 0x59, 0x49, 0x2d, 0xd1, 0xd3, 0xed, + 0x62, 0xef, 0x16, 0x78, 0xb6, 0xf8, 0xf0, 0x20, 0x93, 0x99, 0xb4, 0x85, 0xd4, 0x44, 0xad, 0xe6, + 0x90, 0x24, 0x52, 0x15, 0x52, 0xd1, 0x98, 0x29, 0x4e, 0xeb, 0xe3, 0x98, 0x6b, 0x76, 0x4c, 0x13, + 0x99, 0x8b, 0x36, 0x3f, 0xf9, 0x06, 0xe0, 0xe0, 0xbc, 0x29, 0x02, 0x56, 0x70, 0x84, 0x60, 0xdf, + 0x74, 0xc3, 0x60, 0x0c, 0xdc, 0x51, 0x68, 0x63, 0x74, 0x00, 0x77, 0xe4, 0x27, 0xc1, 0x2b, 0x7c, + 0xc7, 0x92, 0x2d, 0x40, 0x04, 0xc2, 0x44, 0x0a, 0x5d, 0xc9, 0xf9, 0x9c, 0x57, 0xb8, 0x67, 0x53, + 0x5b, 0x0c, 0x7a, 0x02, 0x47, 0x7c, 0x59, 0xe6, 0x15, 0x8f, 0x98, 0xc6, 0xfd, 0x31, 0x70, 0x7b, + 0xe1, 0xb0, 0x25, 0x4e, 0x35, 0xba, 0x80, 0x83, 0x44, 0x8a, 0x0f, 0x79, 0xa6, 0xf0, 0xce, 0xb8, + 0xe7, 0xee, 0xf9, 0x47, 0xde, 0xbf, 0x16, 0xf3, 0xba, 0xf1, 0xa6, 0x56, 0x73, 0xd6, 0xbf, 0xfe, + 0xf1, 0xcc, 0x09, 0x37, 0x2f, 0x20, 0x6c, 0x1f, 0xd3, 0x2c, 0xd1, 0x78, 0xd7, 0x8e, 0xb1, 0x81, + 0x93, 0xaf, 0x00, 0xde, 0xfb, 0x4d, 0x8a, 0xa6, 0xb0, 0xaf, 0x9b, 0xb2, 0xdd, 0xef, 0xbe, 0x4f, + 0xff, 0xa3, 0xeb, 0x55, 0x53, 0xf2, 0xd0, 0x8a, 0xd1, 0x63, 0x38, 0x4c, 0x66, 0x2c, 0x17, 0x51, + 0x9e, 0x76, 0x9e, 0x0c, 0x2c, 0x7e, 0x95, 0x1a, 0xff, 0x4a, 0xa6, 0x67, 0x9d, 0x1f, 0x36, 0x36, + 0xfe, 0xd5, 0x6c, 0xbe, 0xe0, 0xd6, 0x85, 0x51, 0xd8, 0x82, 0xc9, 0x0b, 0xf8, 0x28, 0xe4, 0x35, + 0xaf, 0x14, 0xbf, 0x94, 0xf2, 0xe3, 0xa2, 0xec, 0x9a, 0x29, 0x63, 0xdc, 0xe6, 0xa7, 0x2b, 0x0c, + 0xc6, 0x3d, 0x77, 0x14, 0x0e, 0xd3, 0x2e, 0xf9, 0xdc, 0x87, 0x0f, 0xff, 0x98, 0x0a, 0x3d, 0x80, + 0x7b, 0xe7, 0xd3, 0xab, 0xe8, 0x6d, 0x70, 0x11, 0xbc, 0x79, 0x17, 0xec, 0x3b, 0xe8, 0x2e, 0x1c, + 0x1a, 0x22, 0x38, 0x7d, 0xfd, 0x72, 0x1f, 0x9c, 0x5d, 0x5e, 0xaf, 0x08, 0xb8, 0x59, 0x11, 0xf0, + 0x73, 0x45, 0xc0, 0x97, 0x35, 0x71, 0x6e, 0xd6, 0xc4, 0xf9, 0xbe, 0x26, 0xce, 0x7b, 0x3f, 0xcb, + 0xf5, 0x6c, 0x11, 0x7b, 0x89, 0x2c, 0xe8, 0x5f, 0xae, 0xb0, 0x3e, 0xa1, 0xcb, 0xee, 0x14, 0xcd, + 0xee, 0x2a, 0xde, 0xb5, 0x47, 0x73, 0xf2, 0x2b, 0x00, 0x00, 0xff, 0xff, 0x28, 0x32, 0x1e, 0x91, + 0xb7, 0x02, 0x00, 0x00, +} + +func (m *DymName) 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 *DymName) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *DymName) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Contact) > 0 { + i -= len(m.Contact) + copy(dAtA[i:], m.Contact) + i = encodeVarintDymName(dAtA, i, uint64(len(m.Contact))) + i-- + dAtA[i] = 0x32 + } + if len(m.Configs) > 0 { + for iNdEx := len(m.Configs) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Configs[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintDymName(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + } + } + if m.ExpireAt != 0 { + i = encodeVarintDymName(dAtA, i, uint64(m.ExpireAt)) + i-- + dAtA[i] = 0x20 + } + if len(m.Controller) > 0 { + i -= len(m.Controller) + copy(dAtA[i:], m.Controller) + i = encodeVarintDymName(dAtA, i, uint64(len(m.Controller))) + i-- + dAtA[i] = 0x1a + } + if len(m.Owner) > 0 { + i -= len(m.Owner) + copy(dAtA[i:], m.Owner) + i = encodeVarintDymName(dAtA, i, uint64(len(m.Owner))) + i-- + dAtA[i] = 0x12 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = encodeVarintDymName(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *DymNameConfig) 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 *DymNameConfig) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *DymNameConfig) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Value) > 0 { + i -= len(m.Value) + copy(dAtA[i:], m.Value) + i = encodeVarintDymName(dAtA, i, uint64(len(m.Value))) + i-- + dAtA[i] = 0x22 + } + if len(m.Path) > 0 { + i -= len(m.Path) + copy(dAtA[i:], m.Path) + i = encodeVarintDymName(dAtA, i, uint64(len(m.Path))) + i-- + dAtA[i] = 0x1a + } + if len(m.ChainId) > 0 { + i -= len(m.ChainId) + copy(dAtA[i:], m.ChainId) + i = encodeVarintDymName(dAtA, i, uint64(len(m.ChainId))) + i-- + dAtA[i] = 0x12 + } + if m.Type != 0 { + i = encodeVarintDymName(dAtA, i, uint64(m.Type)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *ReverseLookupDymNames) 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 *ReverseLookupDymNames) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ReverseLookupDymNames) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.DymNames) > 0 { + for iNdEx := len(m.DymNames) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.DymNames[iNdEx]) + copy(dAtA[i:], m.DymNames[iNdEx]) + i = encodeVarintDymName(dAtA, i, uint64(len(m.DymNames[iNdEx]))) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func encodeVarintDymName(dAtA []byte, offset int, v uint64) int { + offset -= sovDymName(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *DymName) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Name) + if l > 0 { + n += 1 + l + sovDymName(uint64(l)) + } + l = len(m.Owner) + if l > 0 { + n += 1 + l + sovDymName(uint64(l)) + } + l = len(m.Controller) + if l > 0 { + n += 1 + l + sovDymName(uint64(l)) + } + if m.ExpireAt != 0 { + n += 1 + sovDymName(uint64(m.ExpireAt)) + } + if len(m.Configs) > 0 { + for _, e := range m.Configs { + l = e.Size() + n += 1 + l + sovDymName(uint64(l)) + } + } + l = len(m.Contact) + if l > 0 { + n += 1 + l + sovDymName(uint64(l)) + } + return n +} + +func (m *DymNameConfig) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Type != 0 { + n += 1 + sovDymName(uint64(m.Type)) + } + l = len(m.ChainId) + if l > 0 { + n += 1 + l + sovDymName(uint64(l)) + } + l = len(m.Path) + if l > 0 { + n += 1 + l + sovDymName(uint64(l)) + } + l = len(m.Value) + if l > 0 { + n += 1 + l + sovDymName(uint64(l)) + } + return n +} + +func (m *ReverseLookupDymNames) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.DymNames) > 0 { + for _, s := range m.DymNames { + l = len(s) + n += 1 + l + sovDymName(uint64(l)) + } + } + return n +} + +func sovDymName(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozDymName(x uint64) (n int) { + return sovDymName(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *DymName) 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 ErrIntOverflowDymName + } + 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: DymName: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: DymName: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDymName + } + 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 ErrInvalidLengthDymName + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDymName + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Owner", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDymName + } + 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 ErrInvalidLengthDymName + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDymName + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Owner = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Controller", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDymName + } + 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 ErrInvalidLengthDymName + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDymName + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Controller = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ExpireAt", wireType) + } + m.ExpireAt = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDymName + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ExpireAt |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Configs", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDymName + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthDymName + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthDymName + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Configs = append(m.Configs, DymNameConfig{}) + if err := m.Configs[len(m.Configs)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Contact", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDymName + } + 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 ErrInvalidLengthDymName + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDymName + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Contact = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipDymName(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthDymName + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *DymNameConfig) 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 ErrIntOverflowDymName + } + 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: DymNameConfig: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: DymNameConfig: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) + } + m.Type = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDymName + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Type |= DymNameConfigType(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ChainId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDymName + } + 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 ErrInvalidLengthDymName + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDymName + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ChainId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDymName + } + 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 ErrInvalidLengthDymName + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDymName + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Path = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Value", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDymName + } + 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 ErrInvalidLengthDymName + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDymName + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Value = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipDymName(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthDymName + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ReverseLookupDymNames) 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 ErrIntOverflowDymName + } + 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: ReverseLookupDymNames: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ReverseLookupDymNames: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DymNames", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDymName + } + 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 ErrInvalidLengthDymName + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDymName + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.DymNames = append(m.DymNames, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipDymName(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthDymName + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipDymName(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, ErrIntOverflowDymName + } + 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, ErrIntOverflowDymName + } + 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, ErrIntOverflowDymName + } + 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, ErrInvalidLengthDymName + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupDymName + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthDymName + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthDymName = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowDymName = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupDymName = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/dymns/types/dym_name_test.go b/x/dymns/types/dym_name_test.go new file mode 100644 index 000000000..8c7d8fa06 --- /dev/null +++ b/x/dymns/types/dym_name_test.go @@ -0,0 +1,1722 @@ +package types + +import ( + "fmt" + "reflect" + "testing" + "time" + + dymnsutils "github.com/dymensionxyz/dymension/v3/x/dymns/utils" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" +) + +//goland:noinspection SpellCheckingInspection +func TestDymName_Validate(t *testing.T) { + t.Run("nil obj", func(t *testing.T) { + m := (*DymName)(nil) + require.Error(t, m.Validate()) + }) + + tests := []struct { + name string + dymName string + owner string + controller string + expireAt int64 + configs []DymNameConfig + contact string + wantErr bool + wantErrContains string + }{ + { + name: "pass - valid dym name", + dymName: "my-name", + owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + controller: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + expireAt: time.Now().Unix(), + configs: []DymNameConfig{ + { + Type: DymNameConfigType_DCT_NAME, + Path: "", + Value: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + }, + { + Type: DymNameConfigType_DCT_NAME, + Path: "www", + Value: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + }, + }, + contact: "contact@example.com", + }, + { + name: "fail - empty name", + dymName: "", + owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + controller: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + expireAt: time.Now().Unix(), + configs: []DymNameConfig{ + { + Type: DymNameConfigType_DCT_NAME, + Path: "", + Value: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + }, + { + Type: DymNameConfigType_DCT_NAME, + Path: "www", + Value: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + }, + }, + wantErr: true, + wantErrContains: "name is empty", + }, + { + name: "fail - bad name", + dymName: "-a", + owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + controller: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + expireAt: time.Now().Unix(), + configs: []DymNameConfig{ + { + Type: DymNameConfigType_DCT_NAME, + Path: "", + Value: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + }, + { + Type: DymNameConfigType_DCT_NAME, + Path: "www", + Value: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + }, + }, + wantErr: true, + wantErrContains: "name is not a valid dym name", + }, + { + name: "fail - empty owner", + dymName: "my-name", + owner: "", + controller: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + expireAt: time.Now().Unix(), + configs: []DymNameConfig{ + { + Type: DymNameConfigType_DCT_NAME, + Path: "", + Value: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + }, + { + Type: DymNameConfigType_DCT_NAME, + Path: "www", + Value: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + }, + }, + wantErr: true, + wantErrContains: "owner is empty", + }, + { + name: "fail - bad owner", + dymName: "my-name", + owner: "dym1fl48vsnmsdzcv85q5", + controller: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + expireAt: time.Now().Unix(), + configs: []DymNameConfig{ + { + Type: DymNameConfigType_DCT_NAME, + Path: "", + Value: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + }, + { + Type: DymNameConfigType_DCT_NAME, + Path: "www", + Value: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + }, + }, + wantErr: true, + wantErrContains: "owner is not a valid bech32 account address", + }, + { + name: "fail - empty controller", + dymName: "my-name", + owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + controller: "", + expireAt: time.Now().Unix(), + configs: []DymNameConfig{ + { + Type: DymNameConfigType_DCT_NAME, + Path: "", + Value: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + }, + { + Type: DymNameConfigType_DCT_NAME, + Path: "www", + Value: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + }, + }, + wantErr: true, + wantErrContains: "controller is empty", + }, + { + name: "fail - bad controller", + dymName: "my-name", + owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + controller: "dym1fl48vsnmsdzcv85q5", + expireAt: time.Now().Unix(), + configs: []DymNameConfig{ + { + Type: DymNameConfigType_DCT_NAME, + Path: "", + Value: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + }, + { + Type: DymNameConfigType_DCT_NAME, + Path: "www", + Value: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + }, + }, + wantErr: true, + wantErrContains: "controller is not a valid bech32 account address", + }, + { + name: "fail - empty expire at", + dymName: "my-name", + owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + controller: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + expireAt: 0, + configs: []DymNameConfig{ + { + Type: DymNameConfigType_DCT_NAME, + Path: "", + Value: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + }, + { + Type: DymNameConfigType_DCT_NAME, + Path: "www", + Value: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + }, + }, + wantErr: true, + wantErrContains: "expiry is empty", + }, + { + name: "pass - valid dym name without config", + dymName: "my-name", + owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + controller: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + expireAt: time.Now().Unix(), + }, + { + name: "fail - bad config, value must be valid bech32 account address when chain-id is empty", + dymName: "my-name", + owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + controller: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + expireAt: time.Now().Unix(), + configs: []DymNameConfig{ + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "", + Value: "dym1fl48vsnmsdzcv85q5d2", + }, + }, + wantErr: true, + wantErrContains: "dym name config value must be a valid bech32 account address", + }, + { + name: "fail - bad config, value must be possible account address when chain-id is not empty", + dymName: "my-name", + owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + controller: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + expireAt: time.Now().Unix(), + configs: []DymNameConfig{ + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "nim_1122-1", + Path: "", + Value: "@", + }, + }, + wantErr: true, + wantErrContains: "dym name config value", + }, + { + name: "fail - duplicate config", + dymName: "my-name", + owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + controller: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + expireAt: time.Now().Unix(), + configs: []DymNameConfig{ + { + Type: DymNameConfigType_DCT_NAME, + Path: "www", + Value: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + }, + { + Type: DymNameConfigType_DCT_NAME, + Path: "www", + Value: "dym1tygms3xhhs3yv487phx3dw4a95jn7t7lnxec2d", + }, + }, + wantErr: true, + wantErrContains: "dym name config is not unique", + }, + { + name: "pass - contact is optional, provided", + dymName: "my-name", + owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + controller: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + expireAt: time.Now().Unix(), + contact: "contact@example.com", + }, + { + name: "pass - contact is optional, not provided", + dymName: "my-name", + owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + controller: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + expireAt: time.Now().Unix(), + contact: "", + }, + { + name: "fail - bad contact, too long", + dymName: "my-name", + owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + controller: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + expireAt: time.Now().Unix(), + contact: "123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901", + wantErr: true, + wantErrContains: "invalid contact length", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + m := &DymName{ + Name: tt.dymName, + Owner: tt.owner, + Controller: tt.controller, + ExpireAt: tt.expireAt, + Configs: tt.configs, + Contact: tt.contact, + } + err := m.Validate() + if tt.wantErr { + require.NotEmpty(t, tt.wantErrContains, "mis-configured test") + require.Error(t, err) + require.Contains(t, err.Error(), tt.wantErrContains) + return + } + + require.NoError(t, err) + }) + } + + t.Run("maximum number of config", func(t *testing.T) { + m := &DymName{ + Name: "a", + Owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + Controller: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + ExpireAt: 1, + } + + for i := 0; i < MaxConfigSize+1; i++ { + m.Configs = append(m.Configs, DymNameConfig{ + Type: DymNameConfigType_DCT_NAME, + Path: fmt.Sprintf("s%d", i), + Value: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + }) + } + + err := m.Validate() + require.Error(t, err) + require.Contains(t, err.Error(), "maximum number of configs allowed") + }) +} + +func TestDymNameConfig_Validate(t *testing.T) { + t.Run("nil obj", func(t *testing.T) { + m := (*DymNameConfig)(nil) + require.Error(t, m.Validate()) + }) + + //goland:noinspection SpellCheckingInspection + tests := []struct { + name string + Type DymNameConfigType + ChainId string + Path string + Value string + wantErr bool + wantErrContains string + }{ + { + name: "pass - valid name config", + Type: DymNameConfigType_DCT_NAME, + ChainId: "dymension_1100-1", + Path: "abc", + Value: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + }, + { + name: "pass - valid name config with multi-level path", + Type: DymNameConfigType_DCT_NAME, + Path: "abc.def", + Value: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + }, + { + name: "pass - valid name config with empty path", + Type: DymNameConfigType_DCT_NAME, + ChainId: "dymension_1100-1", + Path: "", + Value: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + }, + { + name: "pass - valid name config with empty chain-id", + Type: DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "abc", + Value: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + }, + { + name: "pass - valid name config with empty chain-id and path", + Type: DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "", + Value: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + }, + { + name: "fail - not accept hex address value on host-chain", + Type: DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "", + Value: "0x1234567890123456789012345678901234567890", + wantErr: true, + wantErrContains: "must be a valid bech32 account address", + }, + { + name: "fail - not accept hex address value on host-chain", + Type: DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "a", + Value: "0x1234567890123456789012345678901234567890", + wantErr: true, + wantErrContains: "must be a valid bech32 account address", + }, + { + name: "fail - not accept other address value on host-chain", + Type: DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "", + Value: "t1Rv4exT7bqhZqi2j7xz8bUHDMxwosrjADU", + wantErr: true, + wantErrContains: "dym name config value on host-chain must be lowercase", + }, + { + name: "fail - not accept other address value on host-chain", + Type: DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "a", + Value: "t1Rv4exT7bqhZqi2j7xz8bUHDMxwosrjADU", + wantErr: true, + wantErrContains: "dym name config value on host-chain must be lowercase", + }, + { + name: "pass - accept hex address value on non-host-chain", + Type: DymNameConfigType_DCT_NAME, + ChainId: "another", + Path: "", + Value: "0x1234567890123456789012345678901234567890", + wantErr: false, + }, + { + name: "pass - accept hex address value on non-host-chain", + Type: DymNameConfigType_DCT_NAME, + ChainId: "another", + Path: "a", + Value: "0x1234567890123456789012345678901234567890", + wantErr: false, + }, + { + name: "pass - accept other address value on non-host-chain", + Type: DymNameConfigType_DCT_NAME, + ChainId: "another", + Path: "", + Value: "t1Rv4exT7bqhZqi2j7xz8bUHDMxwosrjADU", + wantErr: false, + }, + { + name: "pass - accept other address value on non-host-chain", + Type: DymNameConfigType_DCT_NAME, + ChainId: "another", + Path: "a", + Value: "t1Rv4exT7bqhZqi2j7xz8bUHDMxwosrjADU", + wantErr: false, + }, + { + name: "fail - reject bad address value on non-host-chain", + Type: DymNameConfigType_DCT_NAME, + ChainId: "another", + Path: "", + Value: "@@", + wantErr: true, + wantErrContains: "dym name config value: @@", + }, + { + name: "fail - reject bad address value on non-host-chain", + Type: DymNameConfigType_DCT_NAME, + ChainId: "another", + Path: "a", + Value: "@@", + wantErr: true, + wantErrContains: "dym name config value: @@", + }, + { + name: "fail - not accept unknown type", + Type: DymNameConfigType_DCT_UNKNOWN, + ChainId: "", + Path: "", + Value: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + wantErr: true, + wantErrContains: "Dym-Name config type must be", + }, + { + name: "fail - bad chain-id", + Type: DymNameConfigType_DCT_NAME, + ChainId: "dymension_", + Path: "abc", + Value: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + wantErr: true, + wantErrContains: "dym name config chain id must be a valid chain id format", + }, + { + name: "fail - bad path", + Type: DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "-a", + Value: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + wantErr: true, + wantErrContains: "dym name config path must be a valid dym name", + }, + { + name: "fail - bad multi-level path", + Type: DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "a.b.", + Value: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + wantErr: true, + wantErrContains: "dym name config path must be a valid dym name", + }, + { + name: "pass - value can be empty", + Type: DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "a", + Value: "", + }, + { + name: "pass - value can be empty", + Type: DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "", + Value: "", + }, + { + name: "fail - bad value on host-chain", + Type: DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "a", + Value: "0x01", + wantErr: true, + wantErrContains: "dym name config value must be a valid bech32 account address", + }, + { + name: "fail - reject value not normalized on host-chain", + Type: DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "", + Value: "Dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + wantErr: true, + wantErrContains: "must be lowercase", + }, + { + name: "fail - reject value not normalized on host-chain", + Type: DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "a", + Value: "Dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + wantErr: true, + wantErrContains: "must be lowercase", + }, + { + name: "pass - allow not-lowercased on non-host-chain", + Type: DymNameConfigType_DCT_NAME, + ChainId: "another", + Path: "", + Value: "t1Rv4exT7bqhZqi2j7xz8bUHDMxwosrjADU", + wantErr: false, + }, + { + name: "pass - allow not-lowercased on non-host-chain", + Type: DymNameConfigType_DCT_NAME, + ChainId: "another", + Path: "a", + Value: "t1Rv4exT7bqhZqi2j7xz8bUHDMxwosrjADU", + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + m := &DymNameConfig{ + Type: tt.Type, + ChainId: tt.ChainId, + Path: tt.Path, + Value: tt.Value, + } + + err := m.Validate() + if tt.wantErr { + require.NotEmpty(t, tt.wantErrContains, "mis-configured test") + require.Error(t, err) + require.Contains(t, err.Error(), tt.wantErrContains) + } else { + require.NoError(t, err) + } + }) + } +} + +func TestReverseLookupDymNames_Validate(t *testing.T) { + t.Run("nil obj", func(t *testing.T) { + m := (*ReverseLookupDymNames)(nil) + require.Error(t, m.Validate()) + }) + + tests := []struct { + name string + DymNames []string + wantErr bool + wantErrContains string + }{ + { + name: "pass - valid reverse lookup record", + DymNames: []string{"my-name", "not-bonded-pool"}, + }, + { + name: "pass - allow empty", + DymNames: []string{}, + }, + { + name: "fail - bad dym name", + DymNames: []string{"my-name", "-not-bonded-pool"}, + wantErr: true, + wantErrContains: "invalid dym name:", + }, + { + name: "fail - bad dym name", + DymNames: []string{"-a"}, + wantErr: true, + wantErrContains: "invalid dym name:", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + m := &ReverseLookupDymNames{ + DymNames: tt.DymNames, + } + + err := m.Validate() + if tt.wantErr { + require.NotEmpty(t, tt.wantErrContains, "mis-configured test") + require.Error(t, err) + require.Contains(t, err.Error(), tt.wantErrContains) + } else { + require.NoError(t, err) + } + }) + } +} + +func TestDymName_IsExpiredAt(t *testing.T) { + now := time.Now() + tests := []struct { + name string + contextTime time.Time + wantExpired bool + }{ + { + name: "future", + contextTime: now.Add(-time.Second), + wantExpired: false, + }, + { + name: "past", + contextTime: now.Add(+time.Second), + wantExpired: true, + }, + { + name: "present", + contextTime: now, + wantExpired: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ctx := sdk.Context{}.WithBlockTime(tt.contextTime) + require.Equal(t, tt.wantExpired, DymName{ + ExpireAt: now.Unix(), + }.IsExpiredAtCtx(ctx)) + }) + } +} + +func TestDymName_GetSdkEvent(t *testing.T) { + event := DymName{ + Name: "a", + Owner: "b", + Controller: "c", + ExpireAt: time.Date(2024, 0o1, 0o2, 0o3, 0o4, 0o5, 0, time.UTC).Unix(), + Configs: []DymNameConfig{{}, {}}, + Contact: "contact@example.com", + }.GetSdkEvent() + require.NotNil(t, event) + require.Equal(t, EventTypeSetDymName, event.Type) + require.Len(t, event.Attributes, 6) + require.Equal(t, AttributeKeyDymName, event.Attributes[0].Key) + require.Equal(t, "a", event.Attributes[0].Value) + require.Equal(t, AttributeKeyDymNameOwner, event.Attributes[1].Key) + require.Equal(t, "b", event.Attributes[1].Value) + require.Equal(t, AttributeKeyDymNameController, event.Attributes[2].Key) + require.Equal(t, "c", event.Attributes[2].Value) + require.Equal(t, AttributeKeyDymNameExpiryEpoch, event.Attributes[3].Key) + require.Equal(t, "1704164645", event.Attributes[3].Value) + require.Equal(t, AttributeKeyDymNameConfigCount, event.Attributes[4].Key) + require.Equal(t, "2", event.Attributes[4].Value) + require.Equal(t, AttributeKeyDymNameHasContactDetails, event.Attributes[5].Key) + require.Equal(t, "true", event.Attributes[5].Value) +} + +func TestDymNameConfig_GetIdentity(t *testing.T) { + tests := []struct { + name string + _type DymNameConfigType + chainId string + path string + value string + want string + }{ + { + name: "combination of Type & Chain Id & Path, exclude Value", + _type: DymNameConfigType_DCT_NAME, + chainId: "1", + path: "2", + value: "3", + want: "dct_name|1|2", + }, + { + name: "combination of Type & Chain Id & Path, exclude Value", + _type: DymNameConfigType_DCT_NAME, + chainId: "1", + path: "2", + value: "", + want: "dct_name|1|2", + }, + { + name: "normalize material fields", + _type: DymNameConfigType_DCT_NAME, + chainId: "AaA", + path: "bBb", + value: "", + want: "dct_name|aaa|bbb", + }, + { + name: "use String() of type", + _type: DymNameConfigType_DCT_UNKNOWN, + chainId: "1", + path: "2", + want: "dct_unknown|1|2", + }, + { + name: "use String() of type", + _type: DymNameConfigType_DCT_NAME, + chainId: "1", + path: "2", + want: "dct_name|1|2", + }, + { + name: "respect empty chain-id", + _type: DymNameConfigType_DCT_NAME, + chainId: "", + path: "2", + want: "dct_name||2", + }, + { + name: "respect empty path", + _type: DymNameConfigType_DCT_NAME, + chainId: "1", + path: "", + want: "dct_name|1|", + }, + { + name: "respect empty chain-id and path", + _type: DymNameConfigType_DCT_NAME, + chainId: "", + path: "", + want: "dct_name||", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + m := DymNameConfig{ + Type: tt._type, + ChainId: tt.chainId, + Path: tt.path, + Value: tt.value, + } + require.Equal(t, tt.want, m.GetIdentity()) + }) + } + + t.Run("normalize material fields", func(t *testing.T) { + require.Equal(t, DymNameConfig{ + ChainId: "AaA", + Path: "bBb", + Value: "123", + }.GetIdentity(), DymNameConfig{ + ChainId: "aAa", + Path: "BbB", + Value: "456", + }.GetIdentity()) + }) +} + +func TestDymNameConfig_IsDelete(t *testing.T) { + require.True(t, DymNameConfig{ + Value: "", + }.IsDelete(), "if value is empty then it's delete") + require.False(t, DymNameConfig{ + Value: "1", + }.IsDelete(), "if value is not empty then it's not delete") +} + +//goland:noinspection SpellCheckingInspection +func TestDymName_GetAddressesForReverseMapping(t *testing.T) { + const dymName = "a" + const ownerBech32 = "dym1zg69v7yszg69v7yszg69v7yszg69v7ys8xdv96" + const ownerBech32AtNim = "nim1zg69v7yszg69v7yszg69v7yszg69v7yspkhdt9" + const ownerHex = "0x1234567890123456789012345678901234567890" + const bondedPoolBech32 = "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue" + const bondedPoolHex = "0x4fea76427b8345861e80a3540a8a9d936fd39391" + + const icaBech32 = "dym1zg69v7yszg69v7yszg69v7yszg69v7yszg69v7yszg69v7yszg6qrz80ul" + const icaBech32AtNim = "nim1zg69v7yszg69v7yszg69v7yszg69v7yszg69v7yszg69v7yszg6qe9zz9m" + const icaHex = "0x1234567890123456789012345678901234567890123456789012345678901234" + + tests := []struct { + name string + configs []DymNameConfig + wantPanic bool + wantConfiguredAddresses map[string][]DymNameConfig + wantFallbackAddresses map[string][]DymNameConfig + }{ + { + name: "pass", + configs: []DymNameConfig{ + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "", + Value: ownerBech32, + }, + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "nim_1122-1", + Path: "", + Value: ownerBech32AtNim, + }, + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "my-name", + Value: bondedPoolBech32, + }, + }, + wantConfiguredAddresses: map[string][]DymNameConfig{ + ownerBech32: { + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "", + Value: ownerBech32, + }, + }, + ownerBech32AtNim: { + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "nim_1122-1", + Path: "", + Value: ownerBech32AtNim, + }, + }, + bondedPoolBech32: { + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "my-name", + Value: bondedPoolBech32, + }, + }, + }, + wantFallbackAddresses: map[string][]DymNameConfig{ + ownerHex: { + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "", + Value: ownerBech32, + }, + }, + }, + }, + { + name: "pass - fallback address is parsed correctly", + configs: []DymNameConfig{ + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "", + Value: ownerBech32, + }, + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "my-name", + Value: bondedPoolBech32, + }, + }, + wantConfiguredAddresses: map[string][]DymNameConfig{ + ownerBech32: { + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "", + Value: ownerBech32, + }, + }, + bondedPoolBech32: { + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "my-name", + Value: bondedPoolBech32, + }, + }, + }, + wantFallbackAddresses: map[string][]DymNameConfig{ + ownerHex: { + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "", + Value: ownerBech32, + }, + }, + }, + }, + { + name: "pass - configured bech32 address is kept as is", + configs: []DymNameConfig{ + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "", + Value: ownerBech32, + }, + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "nim_1122-1", + Path: "", + Value: ownerBech32AtNim, // not dym1, it's nim1 + }, + }, + wantConfiguredAddresses: map[string][]DymNameConfig{ + ownerBech32: { + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "", + Value: ownerBech32, + }, + }, + ownerBech32AtNim: { + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "nim_1122-1", + Path: "", + Value: ownerBech32AtNim, + }, + }, + }, + wantFallbackAddresses: map[string][]DymNameConfig{ + ownerHex: { + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "", + Value: ownerBech32, + }, + }, + }, + }, + { + name: "pass - able to detect default config address when not configured", + configs: []DymNameConfig{ + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "my-name", + Value: bondedPoolBech32, + }, + // not include default config + }, + wantConfiguredAddresses: map[string][]DymNameConfig{ + ownerBech32: { // default config resolved to owner + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "", + Value: ownerBech32, + }, + }, + bondedPoolBech32: { + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "my-name", + Value: bondedPoolBech32, + }, + }, + }, + wantFallbackAddresses: map[string][]DymNameConfig{ + ownerHex: { // default config resolved to owner + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "", + Value: ownerBech32, + }, + }, + }, + }, + { + name: "pass - respect default config when it is not owner", + configs: []DymNameConfig{ + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "", + Value: bondedPoolBech32, // not the owner + }, + }, + wantConfiguredAddresses: map[string][]DymNameConfig{ + bondedPoolBech32: { // respect + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "", + Value: bondedPoolBech32, + }, + }, + }, + wantFallbackAddresses: map[string][]DymNameConfig{ + bondedPoolHex: { // respect + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "", + Value: bondedPoolBech32, + }, + }, + }, + }, + { + name: "pass - respect default config when it is not owner", + configs: []DymNameConfig{ + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "", + Value: bondedPoolBech32, // not the owner + }, + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "a", + Value: bondedPoolBech32, + }, + }, + wantConfiguredAddresses: map[string][]DymNameConfig{ + bondedPoolBech32: { // respect + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "", + Value: bondedPoolBech32, + }, + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "a", + Value: bondedPoolBech32, + }, + }, + }, + wantFallbackAddresses: map[string][]DymNameConfig{ + bondedPoolHex: { // respect + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "", + Value: bondedPoolBech32, + }, + }, + }, + }, + { + name: "pass - respect default config when it is not owner", + configs: []DymNameConfig{ + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "", + Value: bondedPoolBech32, // not owner + }, + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "nim_1122-1", + Path: "", + Value: ownerBech32AtNim, // but this is owner, in different bech32 prefix + }, + }, + wantConfiguredAddresses: map[string][]DymNameConfig{ + ownerBech32AtNim: { + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "nim_1122-1", + Path: "", + Value: ownerBech32AtNim, + }, + }, + bondedPoolBech32: { + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "", + Value: bondedPoolBech32, + }, + }, + }, + wantFallbackAddresses: map[string][]DymNameConfig{ + bondedPoolHex: { + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "", + Value: bondedPoolBech32, + }, + }, + }, + }, + { + name: "pass - non-default config will not have fallback records", + configs: []DymNameConfig{ + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "", + Value: ownerBech32, + }, + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "cosmoshub-4", + Path: "", + Value: "cosmos1tygms3xhhs3yv487phx3dw4a95jn7t7lpm470r", + }, + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "my-name", + Value: bondedPoolBech32, + }, + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "blumbus_111-1", + Path: "", + Value: bondedPoolBech32, + }, + }, + wantConfiguredAddresses: map[string][]DymNameConfig{ + ownerBech32: { + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "", + Value: ownerBech32, + }, + }, + "cosmos1tygms3xhhs3yv487phx3dw4a95jn7t7lpm470r": { + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "cosmoshub-4", + Path: "", + Value: "cosmos1tygms3xhhs3yv487phx3dw4a95jn7t7lpm470r", + }, + }, + bondedPoolBech32: { + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "my-name", + Value: bondedPoolBech32, + }, + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "blumbus_111-1", + Path: "", + Value: bondedPoolBech32, + }, + }, + }, + wantFallbackAddresses: map[string][]DymNameConfig{ + ownerHex: { + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "", + Value: ownerBech32, + }, + }, + }, + }, + { + name: "fail - not accept malformed config", + configs: []DymNameConfig{{}}, + wantPanic: true, + }, + { + name: "fail - not accept malformed config, not bech32 address value", + configs: []DymNameConfig{{ + Type: DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "a", + Value: "0x1234567890123456789012345678901234567890", + }}, + wantPanic: true, + }, + { + name: "fail - not accept malformed config, default config is not bech32 address of host", + configs: []DymNameConfig{{ + Type: DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "", + Value: ownerBech32AtNim, + }}, + wantPanic: true, + }, + { + name: "fail - not accept malformed config, not valid bech32 address", + configs: []DymNameConfig{{ + Type: DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "a", + Value: ownerBech32 + "a", + }}, + wantPanic: true, + }, + { + name: "fail - not accept malformed config, default config is not bech32 address of host", + configs: []DymNameConfig{{ + Type: DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "", + Value: ownerBech32 + "a", + }}, + wantPanic: true, + }, + { + name: "pass - ignore empty value config", + configs: []DymNameConfig{ + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "", + Value: ownerBech32, + }, + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "nim_1122-1", + Path: "", + Value: ownerBech32AtNim, + }, + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "my-name", + Value: "", // empty value + }, + }, + wantConfiguredAddresses: map[string][]DymNameConfig{ + ownerBech32: { + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "", + Value: ownerBech32, + }, + }, + ownerBech32AtNim: { + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "nim_1122-1", + Path: "", + Value: ownerBech32AtNim, + }, + }, + }, + wantFallbackAddresses: map[string][]DymNameConfig{ + ownerHex: { + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "", + Value: ownerBech32, + }, + }, + }, + }, + { + name: "pass - ignore empty value default config", + configs: []DymNameConfig{ + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "", + Value: "", // empty value + }, + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "nim_1122-1", + Path: "", + Value: ownerBech32AtNim, + }, + }, + wantConfiguredAddresses: map[string][]DymNameConfig{ + ownerBech32: { + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "", + Value: ownerBech32, // detected & automatically filled default config + }, + }, + ownerBech32AtNim: { + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "nim_1122-1", + Path: "", + Value: ownerBech32AtNim, + }, + }, + }, + wantFallbackAddresses: map[string][]DymNameConfig{ + ownerHex: { + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "", + Value: ownerBech32, // detected & automatically filled default config + }, + }, + }, + }, + { + name: "pass - allow Interchain Account", + configs: []DymNameConfig{ + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "", + Value: icaBech32, + }, + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "nim_1122-1", + Path: "ica", + Value: icaBech32AtNim, + }, + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "nim_1122-1", + Path: "", + Value: ownerBech32AtNim, + }, + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "my-name", + Value: bondedPoolBech32, + }, + }, + wantConfiguredAddresses: map[string][]DymNameConfig{ + ownerBech32AtNim: { + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "nim_1122-1", + Path: "", + Value: ownerBech32AtNim, + }, + }, + bondedPoolBech32: { + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "my-name", + Value: bondedPoolBech32, + }, + }, + icaBech32: { + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "", + Value: icaBech32, + }, + }, + icaBech32AtNim: { + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "nim_1122-1", + Path: "ica", + Value: icaBech32AtNim, + }, + }, + }, + wantFallbackAddresses: map[string][]DymNameConfig{ + icaHex: { + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "", + Value: icaBech32, + }, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + m := &DymName{ + Name: dymName, + Owner: ownerBech32, + Controller: ownerBech32, + ExpireAt: 1, + Configs: tt.configs, + } + + if tt.wantPanic { + require.Panics(t, func() { + _, _ = m.GetAddressesForReverseMapping() + }) + + return + } + + gotConfiguredAddresses, gotFallbackAddresses := m.GetAddressesForReverseMapping() + + if !reflect.DeepEqual(tt.wantConfiguredAddresses, gotConfiguredAddresses) { + t.Errorf("gotConfiguredAddresses = %v, want %v", gotConfiguredAddresses, tt.wantConfiguredAddresses) + } + if !reflect.DeepEqual(tt.wantFallbackAddresses, gotFallbackAddresses) { + t.Errorf("gotFallbackAddresses = %v, want %v", gotFallbackAddresses, tt.wantFallbackAddresses) + } + + if len(gotFallbackAddresses) > 0 { + require.Len(t, gotFallbackAddresses, 1, "there is only one default config, therefor only one fallback address") + } + for fba, cfgs := range gotFallbackAddresses { + require.True(t, dymnsutils.IsValidHexAddress(fba)) + for _, cfg := range cfgs { + require.True(t, cfg.IsDefaultNameConfig()) + require.NotEmpty(t, cfg.Value != "") + } + } + }) + } +} + +func TestDymNameConfig_IsDefaultNameConfig(t *testing.T) { + tests := []struct { + name string + _type DymNameConfigType + chainId string + path string + value string + want bool + }{ + { + name: "default name config", + _type: DymNameConfigType_DCT_NAME, + chainId: "", + path: "", + value: "x", + want: true, + }, + { + name: "default name config, value can be empty", + _type: DymNameConfigType_DCT_NAME, + chainId: "", + path: "", + value: "", + want: true, + }, + { + name: "config with type != name is not default name config", + _type: DymNameConfigType_DCT_UNKNOWN, + chainId: "", + path: "", + value: "x", + want: false, + }, + { + name: "config with type != name is not default name config", + _type: DymNameConfigType_DCT_UNKNOWN, + chainId: "", + path: "", + value: "", + want: false, + }, + { + name: "config with type != name is not default name config", + _type: DymNameConfigType_DCT_UNKNOWN, + chainId: "", + path: "x", + value: "", + want: false, + }, + { + name: "non-empty chain-id is not default name config", + _type: DymNameConfigType_DCT_NAME, + chainId: "x", + path: "", + value: "x", + want: false, + }, + { + name: "non-empty path is not default name config", + _type: DymNameConfigType_DCT_NAME, + chainId: "", + path: "x", + value: "x", + want: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + m := DymNameConfig{ + Type: tt._type, + ChainId: tt.chainId, + Path: tt.path, + Value: tt.value, + } + require.Equal(t, tt.want, m.IsDefaultNameConfig()) + }) + } +} + +func TestDymNameConfigs_DefaultNameConfigs(t *testing.T) { + tests := []struct { + name string + m DymNameConfigs + dropEmptyValueConfigs bool + want DymNameConfigs + }{ + { + name: "pass", + m: []DymNameConfig{ + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "b", + Path: "b", + Value: "b", + }, + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "", + Value: "a", + }, + }, + want: []DymNameConfig{ + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "", + Value: "a", + }, + }, + }, + { + name: "pass - empty", + m: []DymNameConfig{}, + want: DymNameConfigs{}, + }, + { + name: "pass - nil", + m: nil, + want: DymNameConfigs{}, + }, + { + name: "pass - none", + m: []DymNameConfig{ + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "b", + Path: "b", + Value: "b", + }, + }, + want: DymNameConfigs{}, + }, + { + name: "pass - multiple of more than one", + m: []DymNameConfig{ + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "", + Value: "a", + }, + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "b", + Path: "b", + Value: "b", + }, + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "", + Value: "c", + }, + }, + want: []DymNameConfig{ + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "", + Value: "a", + }, + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "", + Value: "c", + }, + }, + }, + { + name: "pass - name config only", + m: []DymNameConfig{ + { + Type: DymNameConfigType_DCT_UNKNOWN, + ChainId: "", + Path: "", + Value: "a", + }, + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "b", + Path: "b", + Value: "b", + }, + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "", + Value: "c", + }, + }, + want: []DymNameConfig{ + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "", + Value: "c", + }, + }, + }, + { + name: "pass - drop empty value configs", + m: []DymNameConfig{ + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "", + Value: "a", + }, + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "", + Value: "", + }, + }, + dropEmptyValueConfigs: true, + want: []DymNameConfig{ + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "", + Value: "a", + }, + }, + }, + { + name: "pass - drop empty value configs", + m: []DymNameConfig{ + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "", + Value: "a", + }, + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "", + Value: "", + }, + }, + dropEmptyValueConfigs: false, + want: []DymNameConfig{ + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "", + Value: "a", + }, + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "", + Value: "", + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := tt.m.DefaultNameConfigs(tt.dropEmptyValueConfigs) + if len(tt.want) == 0 { + require.Empty(t, got) + } else { + require.Equal(t, tt.want, got) + } + }) + } +} diff --git a/x/dymns/types/errors.go b/x/dymns/types/errors.go new file mode 100644 index 000000000..2e0561b5d --- /dev/null +++ b/x/dymns/types/errors.go @@ -0,0 +1,8 @@ +package types + +import ( + errorsmod "cosmossdk.io/errors" + "github.com/dymensionxyz/gerr-cosmos/gerrc" +) + +var ErrBadDymNameAddress = errorsmod.Wrap(gerrc.ErrInvalidArgument, "Dym-Name address is invalid") diff --git a/x/dymns/types/expected_keepers.go b/x/dymns/types/expected_keepers.go new file mode 100644 index 000000000..10066b107 --- /dev/null +++ b/x/dymns/types/expected_keepers.go @@ -0,0 +1,21 @@ +package types + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + rollapptypes "github.com/dymensionxyz/dymension/v3/x/rollapp/types" +) + +// BankKeeper defines the expected x/bank keeper +type BankKeeper interface { + SendCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error + SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error + MintCoins(ctx sdk.Context, moduleName string, amt sdk.Coins) error + BurnCoins(ctx sdk.Context, moduleName string, amt sdk.Coins) error + GetBalance(ctx sdk.Context, addr sdk.AccAddress, denom string) sdk.Coin +} + +// RollAppKeeper defines the expected x/rollapp keeper +type RollAppKeeper interface { + GetRollapp(ctx sdk.Context, rollappId string) (val rollapptypes.Rollapp, found bool) + SetRollapp(ctx sdk.Context, rollapp rollapptypes.Rollapp) +} diff --git a/x/dymns/types/genesis.go b/x/dymns/types/genesis.go new file mode 100644 index 000000000..889b82e5f --- /dev/null +++ b/x/dymns/types/genesis.go @@ -0,0 +1,48 @@ +package types + +import ( + "errors" + + errorsmod "cosmossdk.io/errors" + + "github.com/dymensionxyz/gerr-cosmos/gerrc" +) + +// DefaultGenesis returns the default genesis state +func DefaultGenesis() *GenesisState { + return &GenesisState{ + Params: DefaultParams(), + } +} + +// Validate checks if the GenesisState is valid. +func (m GenesisState) Validate() error { + if err := (&m.Params).Validate(); err != nil { + return errorsmod.Wrapf(gerrc.ErrInvalidArgument, "params: %v", err) + } + + for _, dymName := range m.DymNames { + if err := dymName.Validate(); err != nil { + return errorsmod.Wrapf(gerrc.ErrInvalidArgument, "Dym-Name '%s': %v", dymName.Name, err) + } + } + + for _, soBid := range m.SellOrderBids { + soBid.Params = nil // treat it as refund name orders + if err := soBid.Validate(TypeName); err != nil { + return errorsmod.Wrapf(gerrc.ErrInvalidArgument, "Sell-Order-Bid by '%s': %v", soBid.Bidder, err) + } + } + + for _, bo := range m.BuyOrders { + if err := bo.Validate(); err != nil { + return errorsmod.Wrapf(gerrc.ErrInvalidArgument, "Buy-Order by '%s': %v", bo.Buyer, err) + } + } + + if err := validateAliasesOfChainIds(m.AliasesOfRollapps); err != nil { + return errorsmod.Wrapf(errors.Join(gerrc.ErrInvalidArgument, err), "alias of chain-id") + } + + return nil +} diff --git a/x/dymns/types/genesis.pb.go b/x/dymns/types/genesis.pb.go new file mode 100644 index 000000000..75696cdc8 --- /dev/null +++ b/x/dymns/types/genesis.pb.go @@ -0,0 +1,590 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: dymensionxyz/dymension/dymns/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 DymNS module's genesis state. +type GenesisState struct { + // params defines all the parameters of the module. + Params Params `protobuf:"bytes,1,opt,name=params,proto3" json:"params"` + // dym_names defines all the dym names in the genesis state. + DymNames []DymName `protobuf:"bytes,2,rep,name=dym_names,json=dymNames,proto3" json:"dym_names"` + // sell_order_bids are records which used to refund the bid amount to the bidder + // of the Sell-Orders which was not finished during genesis export + SellOrderBids []SellOrderBid `protobuf:"bytes,3,rep,name=sell_order_bids,json=sellOrderBids,proto3" json:"sell_order_bids"` + // buy_orders are records which used to refund the bid amount to the bidder + // of the Buy-Order which was not finished during genesis export + BuyOrders []BuyOrder `protobuf:"bytes,4,rep,name=buy_orders,json=buyOrders,proto3" json:"buy_orders"` + // aliases_of_rollapps defines all the aliases of all RollApps. + AliasesOfRollapps []AliasesOfChainId `protobuf:"bytes,5,rep,name=aliases_of_rollapps,json=aliasesOfRollapps,proto3" json:"aliases_of_rollapps" yaml:"aliases_of_rollapps"` +} + +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_3a8fb43714238c1e, []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) GetParams() Params { + if m != nil { + return m.Params + } + return Params{} +} + +func (m *GenesisState) GetDymNames() []DymName { + if m != nil { + return m.DymNames + } + return nil +} + +func (m *GenesisState) GetSellOrderBids() []SellOrderBid { + if m != nil { + return m.SellOrderBids + } + return nil +} + +func (m *GenesisState) GetBuyOrders() []BuyOrder { + if m != nil { + return m.BuyOrders + } + return nil +} + +func (m *GenesisState) GetAliasesOfRollapps() []AliasesOfChainId { + if m != nil { + return m.AliasesOfRollapps + } + return nil +} + +func init() { + proto.RegisterType((*GenesisState)(nil), "dymensionxyz.dymension.dymns.GenesisState") +} + +func init() { + proto.RegisterFile("dymensionxyz/dymension/dymns/genesis.proto", fileDescriptor_3a8fb43714238c1e) +} + +var fileDescriptor_3a8fb43714238c1e = []byte{ + // 387 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0xd2, 0xb1, 0x6a, 0xdb, 0x40, + 0x18, 0x07, 0x70, 0xa9, 0x76, 0x4d, 0x7d, 0x6e, 0x29, 0x55, 0x3b, 0x08, 0x51, 0x64, 0x23, 0xda, + 0xe2, 0xba, 0x20, 0x81, 0xbd, 0x75, 0xab, 0x1a, 0x48, 0x42, 0x42, 0x1c, 0xec, 0x25, 0x64, 0x11, + 0xa7, 0xe8, 0x2c, 0x8b, 0xdc, 0xe9, 0x84, 0x3e, 0x39, 0xf8, 0x32, 0xe6, 0x09, 0xf2, 0x58, 0x1e, + 0x3d, 0x66, 0x32, 0xc1, 0x7e, 0x83, 0x3c, 0x41, 0x90, 0x4e, 0x36, 0x1e, 0x12, 0x91, 0xed, 0xbe, + 0xe3, 0xff, 0xff, 0x49, 0x07, 0x1f, 0xea, 0x05, 0x82, 0x91, 0x18, 0x22, 0x1e, 0xcf, 0xc5, 0xad, + 0xb3, 0x1b, 0xf2, 0x53, 0x0c, 0x4e, 0x48, 0x62, 0x02, 0x11, 0xd8, 0x49, 0xca, 0x33, 0xae, 0x7d, + 0xdf, 0xcf, 0xda, 0xbb, 0xc1, 0x2e, 0xb2, 0xc6, 0xb7, 0x90, 0x87, 0xbc, 0x08, 0x3a, 0xf9, 0x49, + 0x76, 0x8c, 0xdf, 0x95, 0x7e, 0x82, 0x53, 0xcc, 0x4a, 0xde, 0xf8, 0x53, 0x19, 0x0d, 0x04, 0xf3, + 0x62, 0xcc, 0xc8, 0x9b, 0x5c, 0x86, 0xd3, 0x6b, 0x92, 0xc9, 0xa8, 0xb5, 0xa8, 0xa1, 0x8f, 0x87, + 0xf2, 0x21, 0xe3, 0x0c, 0x67, 0x44, 0x73, 0x51, 0x43, 0x7e, 0x58, 0x57, 0x3b, 0x6a, 0xb7, 0xd5, + 0xff, 0x61, 0x57, 0x3d, 0xcc, 0x3e, 0x2f, 0xb2, 0x6e, 0x7d, 0xb1, 0x6a, 0x2b, 0xa3, 0xb2, 0xa9, + 0x1d, 0xa1, 0xe6, 0xf6, 0x8f, 0x40, 0x7f, 0xd7, 0xa9, 0x75, 0x5b, 0xfd, 0x9f, 0xd5, 0xcc, 0x81, + 0x60, 0x67, 0x98, 0x91, 0xd2, 0xf9, 0x10, 0xc8, 0x11, 0xb4, 0x0b, 0xf4, 0x19, 0x08, 0xa5, 0x1e, + 0x4f, 0x03, 0x92, 0x7a, 0x7e, 0x14, 0x80, 0x5e, 0x2b, 0xbc, 0x5e, 0xb5, 0x37, 0x26, 0x94, 0x0e, + 0xf3, 0x8e, 0x1b, 0x05, 0x25, 0xfa, 0x09, 0xf6, 0xee, 0x40, 0x3b, 0x41, 0xc8, 0x9f, 0x09, 0x09, + 0x83, 0x5e, 0x2f, 0xd0, 0x5f, 0xd5, 0xa8, 0x3b, 0x13, 0xb2, 0x2f, 0xc1, 0xa6, 0x5f, 0xce, 0xa0, + 0xdd, 0xa9, 0xe8, 0x2b, 0xa6, 0x11, 0x06, 0x02, 0x1e, 0x9f, 0x78, 0x29, 0xa7, 0x14, 0x27, 0x09, + 0xe8, 0xef, 0x0b, 0xd6, 0xae, 0x66, 0xff, 0xc9, 0xe2, 0x70, 0xf2, 0x7f, 0x8a, 0xa3, 0xf8, 0x38, + 0x70, 0xad, 0x9c, 0x7f, 0x5a, 0xb5, 0x0d, 0x81, 0x19, 0xfd, 0x6b, 0xbd, 0x00, 0x5b, 0xa3, 0x2f, + 0x78, 0xdb, 0x1a, 0x95, 0x77, 0xee, 0xe9, 0x62, 0x6d, 0xaa, 0xcb, 0xb5, 0xa9, 0x3e, 0xae, 0x4d, + 0xf5, 0x7e, 0x63, 0x2a, 0xcb, 0x8d, 0xa9, 0x3c, 0x6c, 0x4c, 0xe5, 0xb2, 0x1f, 0x46, 0xd9, 0x74, + 0xe6, 0xdb, 0x57, 0x9c, 0x39, 0xaf, 0xac, 0xc6, 0xcd, 0xc0, 0x99, 0x97, 0xfb, 0x91, 0x89, 0x84, + 0x80, 0xdf, 0x28, 0xf6, 0x63, 0xf0, 0x1c, 0x00, 0x00, 0xff, 0xff, 0x1c, 0x41, 0xcc, 0x1f, 0x04, + 0x03, 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.AliasesOfRollapps) > 0 { + for iNdEx := len(m.AliasesOfRollapps) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.AliasesOfRollapps[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + } + } + if len(m.BuyOrders) > 0 { + for iNdEx := len(m.BuyOrders) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.BuyOrders[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + } + if len(m.SellOrderBids) > 0 { + for iNdEx := len(m.SellOrderBids) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.SellOrderBids[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + } + if len(m.DymNames) > 0 { + for iNdEx := len(m.DymNames) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.DymNames[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + { + size, err := m.Params.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 + l = m.Params.Size() + n += 1 + l + sovGenesis(uint64(l)) + if len(m.DymNames) > 0 { + for _, e := range m.DymNames { + l = e.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + } + if len(m.SellOrderBids) > 0 { + for _, e := range m.SellOrderBids { + l = e.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + } + if len(m.BuyOrders) > 0 { + for _, e := range m.BuyOrders { + l = e.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + } + if len(m.AliasesOfRollapps) > 0 { + for _, e := range m.AliasesOfRollapps { + 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 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 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DymNames", 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.DymNames = append(m.DymNames, DymName{}) + if err := m.DymNames[len(m.DymNames)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SellOrderBids", 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.SellOrderBids = append(m.SellOrderBids, SellOrderBid{}) + if err := m.SellOrderBids[len(m.SellOrderBids)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BuyOrders", 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.BuyOrders = append(m.BuyOrders, BuyOrder{}) + if err := m.BuyOrders[len(m.BuyOrders)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AliasesOfRollapps", 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.AliasesOfRollapps = append(m.AliasesOfRollapps, AliasesOfChainId{}) + if err := m.AliasesOfRollapps[len(m.AliasesOfRollapps)-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/dymns/types/genesis_test.go b/x/dymns/types/genesis_test.go new file mode 100644 index 000000000..024d49627 --- /dev/null +++ b/x/dymns/types/genesis_test.go @@ -0,0 +1,147 @@ +package types + +import ( + "testing" + "time" + + "github.com/dymensionxyz/dymension/v3/app/params" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" +) + +func TestDefaultGenesis(t *testing.T) { + defaultGenesis := DefaultGenesis() + require.NotNil(t, defaultGenesis) + require.NoError(t, defaultGenesis.Validate()) +} + +//goland:noinspection SpellCheckingInspection +func TestGenesisState_Validate(t *testing.T) { + defaultGenesis := DefaultGenesis() + require.NoError(t, defaultGenesis.Validate()) + + t.Run("pass - valid genesis", func(t *testing.T) { + require.NoError(t, (GenesisState{ + Params: DefaultParams(), + DymNames: []DymName{ + { + Name: "my-name", + Owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + Controller: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + ExpireAt: time.Now().Unix(), + }, + }, + SellOrderBids: []SellOrderBid{ + { + // this bid from a SO of type Dym-Name + Bidder: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + Price: sdk.Coin{ + Denom: params.BaseDenom, + Amount: sdk.OneInt(), + }, + }, + { + // this bid from a SO of type Alias + Bidder: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + Price: sdk.Coin{ + Denom: params.BaseDenom, + Amount: sdk.OneInt(), + }, + Params: []string{"rollapp_1-1"}, + }, + }, + BuyOrders: []BuyOrder{ + { + Id: "101", + AssetId: "my-name", + AssetType: TypeName, + Buyer: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + OfferPrice: sdk.Coin{ + Denom: params.BaseDenom, + Amount: sdk.OneInt(), + }, + }, + { + Id: "202", + AssetId: "alias", + AssetType: TypeAlias, + Params: []string{"rollapp_1-1"}, + Buyer: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + OfferPrice: sdk.Coin{ + Denom: params.BaseDenom, + Amount: sdk.OneInt(), + }, + }, + }, + AliasesOfRollapps: []AliasesOfChainId{ + { + ChainId: "rollapp_1-1", + Aliases: []string{"alias"}, + }, + }, + }).Validate()) + }) + + t.Run("fail - invalid params", func(t *testing.T) { + require.Error(t, (GenesisState{ + Params: Params{ + Price: DefaultPriceParams(), + Misc: MiscParams{ + EndEpochHookIdentifier: "invalid", + }, + }, + }).Validate()) + + require.Error(t, (GenesisState{ + Params: Params{ + Price: PriceParams{}, + Misc: MiscParams{ + EndEpochHookIdentifier: "invalid", + }, + }, + }).Validate()) + }) + + t.Run("fail - invalid dym names", func(t *testing.T) { + require.Error(t, (GenesisState{ + Params: DefaultParams(), + DymNames: []DymName{ + { + Name: "", + }, + }, + }).Validate()) + }) + + t.Run("fail - invalid bid", func(t *testing.T) { + require.Error(t, (GenesisState{ + Params: DefaultParams(), + SellOrderBids: []SellOrderBid{ + { + Bidder: "", + }, + }, + }).Validate()) + }) + + t.Run("fail - invalid buy offer", func(t *testing.T) { + require.Error(t, (GenesisState{ + Params: DefaultParams(), + BuyOrders: []BuyOrder{ + { + Buyer: "", + }, + }, + }).Validate()) + }) + + t.Run("fail - invalid aliases of RollApps", func(t *testing.T) { + require.Error(t, (GenesisState{ + Params: DefaultParams(), + AliasesOfRollapps: []AliasesOfChainId{ + {}, + }, + }).Validate()) + }) +} diff --git a/x/dymns/types/gov.pb.go b/x/dymns/types/gov.pb.go new file mode 100644 index 000000000..27ce66af9 --- /dev/null +++ b/x/dymns/types/gov.pb.go @@ -0,0 +1,1249 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: dymensionxyz/dymension/dymns/gov.proto + +package types + +import ( + fmt "fmt" + _ "github.com/cosmos/gogoproto/gogoproto" + proto "github.com/cosmos/gogoproto/proto" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// MigrateChainIdsProposal defines a proposal to replace chain-id in module params and configurations. +// This proposal is used when the chain-id of a connected network changes. +// It will look up and replace the chain-id in module params and all configurations of all non-expired Dym-Names. +type MigrateChainIdsProposal struct { + // title of the proposal + Title string `protobuf:"bytes,1,opt,name=title,proto3" json:"title,omitempty"` + // description of the proposal + Description string `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty"` + // replacements is set of chain-id replacements + Replacement []MigrateChainId `protobuf:"bytes,3,rep,name=replacement,proto3" json:"replacement"` +} + +func (m *MigrateChainIdsProposal) Reset() { *m = MigrateChainIdsProposal{} } +func (m *MigrateChainIdsProposal) String() string { return proto.CompactTextString(m) } +func (*MigrateChainIdsProposal) ProtoMessage() {} +func (*MigrateChainIdsProposal) Descriptor() ([]byte, []int) { + return fileDescriptor_b28b7e3a40fa0a74, []int{0} +} +func (m *MigrateChainIdsProposal) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MigrateChainIdsProposal) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MigrateChainIdsProposal.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 *MigrateChainIdsProposal) XXX_Merge(src proto.Message) { + xxx_messageInfo_MigrateChainIdsProposal.Merge(m, src) +} +func (m *MigrateChainIdsProposal) XXX_Size() int { + return m.Size() +} +func (m *MigrateChainIdsProposal) XXX_DiscardUnknown() { + xxx_messageInfo_MigrateChainIdsProposal.DiscardUnknown(m) +} + +var xxx_messageInfo_MigrateChainIdsProposal proto.InternalMessageInfo + +func (m *MigrateChainIdsProposal) GetTitle() string { + if m != nil { + return m.Title + } + return "" +} + +func (m *MigrateChainIdsProposal) GetDescription() string { + if m != nil { + return m.Description + } + return "" +} + +func (m *MigrateChainIdsProposal) GetReplacement() []MigrateChainId { + if m != nil { + return m.Replacement + } + return nil +} + +// MigrateChainId defines a chain-id replacement. +type MigrateChainId struct { + // previous_chain_id is the chain-id to be replaced + PreviousChainId string `protobuf:"bytes,1,opt,name=previous_chain_id,json=previousChainId,proto3" json:"previous_chain_id,omitempty"` + // new_chain_id is the new chain-id to replace with + NewChainId string `protobuf:"bytes,2,opt,name=new_chain_id,json=newChainId,proto3" json:"new_chain_id,omitempty"` +} + +func (m *MigrateChainId) Reset() { *m = MigrateChainId{} } +func (m *MigrateChainId) String() string { return proto.CompactTextString(m) } +func (*MigrateChainId) ProtoMessage() {} +func (*MigrateChainId) Descriptor() ([]byte, []int) { + return fileDescriptor_b28b7e3a40fa0a74, []int{1} +} +func (m *MigrateChainId) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MigrateChainId) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MigrateChainId.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 *MigrateChainId) XXX_Merge(src proto.Message) { + xxx_messageInfo_MigrateChainId.Merge(m, src) +} +func (m *MigrateChainId) XXX_Size() int { + return m.Size() +} +func (m *MigrateChainId) XXX_DiscardUnknown() { + xxx_messageInfo_MigrateChainId.DiscardUnknown(m) +} + +var xxx_messageInfo_MigrateChainId proto.InternalMessageInfo + +func (m *MigrateChainId) GetPreviousChainId() string { + if m != nil { + return m.PreviousChainId + } + return "" +} + +func (m *MigrateChainId) GetNewChainId() string { + if m != nil { + return m.NewChainId + } + return "" +} + +// UpdateAliasesProposal defines a proposal to update the aliases associated with chain-ids in module params. +type UpdateAliasesProposal struct { + // title of the proposal + Title string `protobuf:"bytes,1,opt,name=title,proto3" json:"title,omitempty"` + // description of the proposal + Description string `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty"` + // add is set of aliases to be mapped to chain-ids + Add []UpdateAlias `protobuf:"bytes,3,rep,name=add,proto3" json:"add"` + // remove is set of aliases to remove mapping from chain-ids + Remove []UpdateAlias `protobuf:"bytes,4,rep,name=remove,proto3" json:"remove"` +} + +func (m *UpdateAliasesProposal) Reset() { *m = UpdateAliasesProposal{} } +func (m *UpdateAliasesProposal) String() string { return proto.CompactTextString(m) } +func (*UpdateAliasesProposal) ProtoMessage() {} +func (*UpdateAliasesProposal) Descriptor() ([]byte, []int) { + return fileDescriptor_b28b7e3a40fa0a74, []int{2} +} +func (m *UpdateAliasesProposal) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *UpdateAliasesProposal) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_UpdateAliasesProposal.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 *UpdateAliasesProposal) XXX_Merge(src proto.Message) { + xxx_messageInfo_UpdateAliasesProposal.Merge(m, src) +} +func (m *UpdateAliasesProposal) XXX_Size() int { + return m.Size() +} +func (m *UpdateAliasesProposal) XXX_DiscardUnknown() { + xxx_messageInfo_UpdateAliasesProposal.DiscardUnknown(m) +} + +var xxx_messageInfo_UpdateAliasesProposal proto.InternalMessageInfo + +func (m *UpdateAliasesProposal) GetTitle() string { + if m != nil { + return m.Title + } + return "" +} + +func (m *UpdateAliasesProposal) GetDescription() string { + if m != nil { + return m.Description + } + return "" +} + +func (m *UpdateAliasesProposal) GetAdd() []UpdateAlias { + if m != nil { + return m.Add + } + return nil +} + +func (m *UpdateAliasesProposal) GetRemove() []UpdateAlias { + if m != nil { + return m.Remove + } + return nil +} + +// UpdateAlias defines an alias to chain-id mapping. +// It can be used to add or remove alias to chain-id mapping. +type UpdateAlias struct { + // chain_id is the chain-id to take action on + ChainId string `protobuf:"bytes,1,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` + // alias is the alias to be mapped to chain-id or removed + Alias string `protobuf:"bytes,2,opt,name=alias,proto3" json:"alias,omitempty"` +} + +func (m *UpdateAlias) Reset() { *m = UpdateAlias{} } +func (m *UpdateAlias) String() string { return proto.CompactTextString(m) } +func (*UpdateAlias) ProtoMessage() {} +func (*UpdateAlias) Descriptor() ([]byte, []int) { + return fileDescriptor_b28b7e3a40fa0a74, []int{3} +} +func (m *UpdateAlias) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *UpdateAlias) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_UpdateAlias.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 *UpdateAlias) XXX_Merge(src proto.Message) { + xxx_messageInfo_UpdateAlias.Merge(m, src) +} +func (m *UpdateAlias) XXX_Size() int { + return m.Size() +} +func (m *UpdateAlias) XXX_DiscardUnknown() { + xxx_messageInfo_UpdateAlias.DiscardUnknown(m) +} + +var xxx_messageInfo_UpdateAlias proto.InternalMessageInfo + +func (m *UpdateAlias) GetChainId() string { + if m != nil { + return m.ChainId + } + return "" +} + +func (m *UpdateAlias) GetAlias() string { + if m != nil { + return m.Alias + } + return "" +} + +func init() { + proto.RegisterType((*MigrateChainIdsProposal)(nil), "dymensionxyz.dymension.dymns.MigrateChainIdsProposal") + proto.RegisterType((*MigrateChainId)(nil), "dymensionxyz.dymension.dymns.MigrateChainId") + proto.RegisterType((*UpdateAliasesProposal)(nil), "dymensionxyz.dymension.dymns.UpdateAliasesProposal") + proto.RegisterType((*UpdateAlias)(nil), "dymensionxyz.dymension.dymns.UpdateAlias") +} + +func init() { + proto.RegisterFile("dymensionxyz/dymension/dymns/gov.proto", fileDescriptor_b28b7e3a40fa0a74) +} + +var fileDescriptor_b28b7e3a40fa0a74 = []byte{ + // 373 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0x4b, 0xa9, 0xcc, 0x4d, + 0xcd, 0x2b, 0xce, 0xcc, 0xcf, 0xab, 0xa8, 0xac, 0xd2, 0x87, 0x73, 0x40, 0xac, 0xbc, 0x62, 0xfd, + 0xf4, 0xfc, 0x32, 0xbd, 0x82, 0xa2, 0xfc, 0x92, 0x7c, 0x21, 0x19, 0x64, 0x75, 0x7a, 0x70, 0x8e, + 0x1e, 0x58, 0x9d, 0x94, 0x48, 0x7a, 0x7e, 0x7a, 0x3e, 0x58, 0xa1, 0x3e, 0x88, 0x05, 0xd1, 0xa3, + 0xb4, 0x9c, 0x91, 0x4b, 0xdc, 0x37, 0x33, 0xbd, 0x28, 0xb1, 0x24, 0xd5, 0x39, 0x23, 0x31, 0x33, + 0xcf, 0x33, 0xa5, 0x38, 0xa0, 0x28, 0xbf, 0x20, 0xbf, 0x38, 0x31, 0x47, 0x48, 0x84, 0x8b, 0xb5, + 0x24, 0xb3, 0x24, 0x27, 0x55, 0x82, 0x51, 0x81, 0x51, 0x83, 0x33, 0x08, 0xc2, 0x11, 0x52, 0xe0, + 0xe2, 0x4e, 0x49, 0x2d, 0x4e, 0x2e, 0xca, 0x2c, 0x28, 0xc9, 0xcc, 0xcf, 0x93, 0x60, 0x02, 0xcb, + 0x21, 0x0b, 0x09, 0x85, 0x70, 0x71, 0x17, 0xa5, 0x16, 0xe4, 0x24, 0x26, 0xa7, 0xe6, 0xa6, 0xe6, + 0x95, 0x48, 0x30, 0x2b, 0x30, 0x6b, 0x70, 0x1b, 0xe9, 0xe8, 0xe1, 0x73, 0x9d, 0x1e, 0xaa, 0x1b, + 0x9c, 0x58, 0x4e, 0xdc, 0x93, 0x67, 0x08, 0x42, 0x36, 0x46, 0x29, 0x8e, 0x8b, 0x0f, 0x55, 0x91, + 0x90, 0x16, 0x97, 0x60, 0x41, 0x51, 0x6a, 0x59, 0x66, 0x7e, 0x69, 0x71, 0x7c, 0x32, 0x48, 0x2c, + 0x3e, 0x33, 0x05, 0xea, 0x56, 0x7e, 0x98, 0x04, 0x4c, 0xad, 0x02, 0x17, 0x4f, 0x5e, 0x6a, 0x39, + 0x42, 0x19, 0xc4, 0xd9, 0x5c, 0x79, 0xa9, 0xe5, 0x50, 0x15, 0x4a, 0xb7, 0x19, 0xb9, 0x44, 0x43, + 0x0b, 0x52, 0x12, 0x4b, 0x52, 0x1d, 0x73, 0x32, 0x13, 0x8b, 0x53, 0x29, 0x0f, 0x07, 0x47, 0x2e, + 0xe6, 0xc4, 0x94, 0x14, 0xa8, 0xff, 0x35, 0xf1, 0xfb, 0x1f, 0xc9, 0x66, 0xa8, 0xe7, 0x41, 0x7a, + 0x85, 0xdc, 0xb9, 0xd8, 0x8a, 0x52, 0x73, 0xf3, 0xcb, 0x52, 0x25, 0x58, 0xc8, 0x33, 0x05, 0xaa, + 0x5d, 0xc9, 0x8e, 0x8b, 0x1b, 0x49, 0x52, 0x48, 0x92, 0x8b, 0x03, 0x2d, 0xc4, 0xd8, 0x93, 0xa1, + 0x21, 0x25, 0xc2, 0xc5, 0x9a, 0x08, 0x52, 0x03, 0xf5, 0x11, 0x84, 0xe3, 0xe4, 0x73, 0xe2, 0x91, + 0x1c, 0xe3, 0x85, 0x47, 0x72, 0x8c, 0x0f, 0x1e, 0xc9, 0x31, 0x4e, 0x78, 0x2c, 0xc7, 0x70, 0xe1, + 0xb1, 0x1c, 0xc3, 0x8d, 0xc7, 0x72, 0x0c, 0x51, 0x46, 0xe9, 0x99, 0x25, 0x19, 0xa5, 0x49, 0x7a, + 0xc9, 0xf9, 0xb9, 0xfa, 0x38, 0x12, 0x6a, 0x99, 0xb1, 0x7e, 0x05, 0x34, 0xb5, 0x96, 0x54, 0x16, + 0xa4, 0x16, 0x27, 0xb1, 0x81, 0x13, 0x9f, 0x31, 0x20, 0x00, 0x00, 0xff, 0xff, 0x25, 0xbf, 0x85, + 0xa0, 0xda, 0x02, 0x00, 0x00, +} + +func (m *MigrateChainIdsProposal) 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 *MigrateChainIdsProposal) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MigrateChainIdsProposal) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Replacement) > 0 { + for iNdEx := len(m.Replacement) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Replacement[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGov(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + } + if len(m.Description) > 0 { + i -= len(m.Description) + copy(dAtA[i:], m.Description) + i = encodeVarintGov(dAtA, i, uint64(len(m.Description))) + i-- + dAtA[i] = 0x12 + } + if len(m.Title) > 0 { + i -= len(m.Title) + copy(dAtA[i:], m.Title) + i = encodeVarintGov(dAtA, i, uint64(len(m.Title))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MigrateChainId) 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 *MigrateChainId) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MigrateChainId) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.NewChainId) > 0 { + i -= len(m.NewChainId) + copy(dAtA[i:], m.NewChainId) + i = encodeVarintGov(dAtA, i, uint64(len(m.NewChainId))) + i-- + dAtA[i] = 0x12 + } + if len(m.PreviousChainId) > 0 { + i -= len(m.PreviousChainId) + copy(dAtA[i:], m.PreviousChainId) + i = encodeVarintGov(dAtA, i, uint64(len(m.PreviousChainId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *UpdateAliasesProposal) 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 *UpdateAliasesProposal) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *UpdateAliasesProposal) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Remove) > 0 { + for iNdEx := len(m.Remove) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Remove[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGov(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + } + if len(m.Add) > 0 { + for iNdEx := len(m.Add) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Add[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGov(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + } + if len(m.Description) > 0 { + i -= len(m.Description) + copy(dAtA[i:], m.Description) + i = encodeVarintGov(dAtA, i, uint64(len(m.Description))) + i-- + dAtA[i] = 0x12 + } + if len(m.Title) > 0 { + i -= len(m.Title) + copy(dAtA[i:], m.Title) + i = encodeVarintGov(dAtA, i, uint64(len(m.Title))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *UpdateAlias) 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 *UpdateAlias) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *UpdateAlias) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Alias) > 0 { + i -= len(m.Alias) + copy(dAtA[i:], m.Alias) + i = encodeVarintGov(dAtA, i, uint64(len(m.Alias))) + i-- + dAtA[i] = 0x12 + } + if len(m.ChainId) > 0 { + i -= len(m.ChainId) + copy(dAtA[i:], m.ChainId) + i = encodeVarintGov(dAtA, i, uint64(len(m.ChainId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintGov(dAtA []byte, offset int, v uint64) int { + offset -= sovGov(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *MigrateChainIdsProposal) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Title) + if l > 0 { + n += 1 + l + sovGov(uint64(l)) + } + l = len(m.Description) + if l > 0 { + n += 1 + l + sovGov(uint64(l)) + } + if len(m.Replacement) > 0 { + for _, e := range m.Replacement { + l = e.Size() + n += 1 + l + sovGov(uint64(l)) + } + } + return n +} + +func (m *MigrateChainId) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.PreviousChainId) + if l > 0 { + n += 1 + l + sovGov(uint64(l)) + } + l = len(m.NewChainId) + if l > 0 { + n += 1 + l + sovGov(uint64(l)) + } + return n +} + +func (m *UpdateAliasesProposal) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Title) + if l > 0 { + n += 1 + l + sovGov(uint64(l)) + } + l = len(m.Description) + if l > 0 { + n += 1 + l + sovGov(uint64(l)) + } + if len(m.Add) > 0 { + for _, e := range m.Add { + l = e.Size() + n += 1 + l + sovGov(uint64(l)) + } + } + if len(m.Remove) > 0 { + for _, e := range m.Remove { + l = e.Size() + n += 1 + l + sovGov(uint64(l)) + } + } + return n +} + +func (m *UpdateAlias) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ChainId) + if l > 0 { + n += 1 + l + sovGov(uint64(l)) + } + l = len(m.Alias) + if l > 0 { + n += 1 + l + sovGov(uint64(l)) + } + return n +} + +func sovGov(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozGov(x uint64) (n int) { + return sovGov(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *MigrateChainIdsProposal) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGov + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MigrateChainIdsProposal: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MigrateChainIdsProposal: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Title", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGov + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGov + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGov + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Title = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Description", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGov + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGov + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGov + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Description = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Replacement", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGov + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGov + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGov + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Replacement = append(m.Replacement, MigrateChainId{}) + if err := m.Replacement[len(m.Replacement)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGov(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGov + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MigrateChainId) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGov + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MigrateChainId: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MigrateChainId: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PreviousChainId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGov + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGov + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGov + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.PreviousChainId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NewChainId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGov + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGov + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGov + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.NewChainId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGov(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGov + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *UpdateAliasesProposal) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGov + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: UpdateAliasesProposal: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: UpdateAliasesProposal: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Title", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGov + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGov + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGov + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Title = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Description", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGov + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGov + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGov + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Description = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Add", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGov + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGov + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGov + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Add = append(m.Add, UpdateAlias{}) + if err := m.Add[len(m.Add)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Remove", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGov + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGov + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGov + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Remove = append(m.Remove, UpdateAlias{}) + if err := m.Remove[len(m.Remove)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGov(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGov + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *UpdateAlias) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGov + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: UpdateAlias: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: UpdateAlias: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ChainId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGov + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGov + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGov + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ChainId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Alias", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGov + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGov + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGov + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Alias = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGov(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGov + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipGov(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowGov + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowGov + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowGov + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthGov + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupGov + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthGov + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthGov = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowGov = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupGov = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/dymns/types/gov_migrate_chain_id.go b/x/dymns/types/gov_migrate_chain_id.go new file mode 100644 index 000000000..447865a16 --- /dev/null +++ b/x/dymns/types/gov_migrate_chain_id.go @@ -0,0 +1,65 @@ +package types + +import ( + "strings" + + errorsmod "cosmossdk.io/errors" + "github.com/dymensionxyz/gerr-cosmos/gerrc" + + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" + dymnsutils "github.com/dymensionxyz/dymension/v3/x/dymns/utils" +) + +// ValidateBasic performs basic validation for the MigrateChainIdsProposal. +func (m *MigrateChainIdsProposal) ValidateBasic() error { + if len(m.Replacement) == 0 { + return errorsmod.Wrap(gerrc.ErrInvalidArgument, "replacement cannot be empty") + } + + uniqueChainIds := make(map[string]bool) + // Describe usage of Go Map: only used for validation + for _, r := range m.Replacement { + if err := r.ValidateBasic(); err != nil { + return err + } + + normalizedPreviousChainId := strings.ToLower(r.PreviousChainId) + normalizedNewChainId := strings.ToLower(r.NewChainId) + + if _, found := uniqueChainIds[normalizedPreviousChainId]; found { + return errorsmod.Wrapf(gerrc.ErrInvalidArgument, "duplicate chain id: %s", r.PreviousChainId) + } + uniqueChainIds[normalizedPreviousChainId] = true + + if _, found := uniqueChainIds[normalizedNewChainId]; found { + return errorsmod.Wrapf(gerrc.ErrInvalidArgument, "duplicate chain id: %s", r.NewChainId) + } + uniqueChainIds[normalizedNewChainId] = true + } + + return v1beta1.ValidateAbstract(m) +} + +// ValidateBasic performs basic validation for the MigrateChainId operation. +func (m MigrateChainId) ValidateBasic() error { + if m.PreviousChainId == "" { + return govtypes.ErrInvalidProposalContent.Wrap("previous chain id cannot be empty") + } + if !dymnsutils.IsValidChainIdFormat(m.PreviousChainId) { + return govtypes.ErrInvalidProposalContent.Wrapf("previous chain id is not well-formed: %s", m.PreviousChainId) + } + + if m.NewChainId == "" { + return govtypes.ErrInvalidProposalContent.Wrap("new chain id cannot be empty") + } + if !dymnsutils.IsValidChainIdFormat(m.NewChainId) { + return govtypes.ErrInvalidProposalContent.Wrapf("new chain id is not well-formed: %s", m.NewChainId) + } + + if strings.EqualFold(m.PreviousChainId, m.NewChainId) { + return govtypes.ErrInvalidProposalContent.Wrap("previous chain id and new chain id cannot be the same") + } + + return nil +} diff --git a/x/dymns/types/gov_migrate_chain_id_test.go b/x/dymns/types/gov_migrate_chain_id_test.go new file mode 100644 index 000000000..ed251709a --- /dev/null +++ b/x/dymns/types/gov_migrate_chain_id_test.go @@ -0,0 +1,266 @@ +package types + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestMigrateChainIdsProposal_ValidateBasic(t *testing.T) { + tests := []struct { + name string + title string + description string + replacement []MigrateChainId + wantErr bool + wantErrContains string + }{ + { + name: "pass - valid, single", + title: "T", + description: "D", + replacement: []MigrateChainId{ + { + PreviousChainId: "cosmoshub-3", + NewChainId: "cosmoshub-4", + }, + }, + wantErr: false, + }, + { + name: "pass - valid, multiple", + title: "T", + description: "D", + replacement: []MigrateChainId{ + { + PreviousChainId: "cosmoshub-3", + NewChainId: "cosmoshub-4", + }, + { + PreviousChainId: "columbus-4", + NewChainId: "columbus-5", + }, + }, + wantErr: false, + }, + { + name: "fail - reject empty replacement", + title: "T", + description: "D", + replacement: []MigrateChainId{}, + wantErr: true, + wantErrContains: "replacement cannot be empty", + }, + { + name: "fail - reject empty replacement", + title: "T", + description: "D", + replacement: nil, + wantErr: true, + wantErrContains: "replacement cannot be empty", + }, + { + name: "fail - reject empty title", + title: "", + description: "D", + replacement: []MigrateChainId{ + { + PreviousChainId: "cosmoshub-3", + NewChainId: "cosmoshub-4", + }, + }, + wantErr: true, + wantErrContains: "proposal title cannot be blank", + }, + { + name: "fail - reject empty description", + title: "T", + description: "", + replacement: []MigrateChainId{ + { + PreviousChainId: "cosmoshub-3", + NewChainId: "cosmoshub-4", + }, + }, + wantErr: true, + wantErrContains: "proposal description cannot be blank", + }, + { + name: "fail - reject invalid replacement", + title: "T", + description: "D", + replacement: []MigrateChainId{ + { + PreviousChainId: "", + NewChainId: "cosmoshub-4", + }, + }, + wantErr: true, + wantErrContains: "previous chain id cannot be empty", + }, + { + name: "fail - reject duplicate replacement", + title: "T", + description: "D", + replacement: []MigrateChainId{ + { + PreviousChainId: "cosmoshub-3", + NewChainId: "cosmoshub-4", + }, + { + PreviousChainId: "cosmoshub-3", + NewChainId: "cosmoshub-5", + }, + }, + wantErr: true, + wantErrContains: "duplicate chain id", + }, + { + name: "fail - reject duplicate replacement", + title: "T", + description: "D", + replacement: []MigrateChainId{ + { + PreviousChainId: "cosmoshub-3", + NewChainId: "cosmoshub-4", + }, + { + PreviousChainId: "cosmoshub-2", + NewChainId: "cosmoshub-4", + }, + }, + wantErr: true, + wantErrContains: "duplicate chain id", + }, + { + name: "fail - reject duplicate replacement", + title: "T", + description: "D", + replacement: []MigrateChainId{ + { + PreviousChainId: "cosmoshub-3", + NewChainId: "cosmoshub-4", + }, + { + PreviousChainId: "cosmoshub-2", + NewChainId: "cosmoshub-3", + }, + }, + wantErr: true, + wantErrContains: "duplicate chain id", + }, + { + name: "fail - reject duplicate replacement", + title: "T", + description: "D", + replacement: []MigrateChainId{ + { + PreviousChainId: "cosmoshub-3", + NewChainId: "cosmoshub-4", + }, + { + PreviousChainId: "cosmoshub-4", + NewChainId: "cosmoshub-5", + }, + }, + wantErr: true, + wantErrContains: "duplicate chain id", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + m := MigrateChainIdsProposal{ + Title: tt.title, + Description: tt.description, + Replacement: tt.replacement, + } + + err := m.ValidateBasic() + if tt.wantErr { + require.NotEmpty(t, tt.wantErrContains, "mis-configured test case") + require.Error(t, err) + require.Contains(t, err.Error(), tt.wantErrContains) + return + } + + require.NoError(t, err) + }) + } +} + +func TestMigrateChainId_ValidateBasic(t *testing.T) { + tests := []struct { + name string + previousChainId string + newChainId string + wantErr bool + wantErrContains string + }{ + { + name: "pass - valid", + previousChainId: "cosmoshub-3", + newChainId: "cosmoshub-4", + wantErr: false, + }, + { + name: "fail - not allow empty previous chain-id", + previousChainId: "", + newChainId: "cosmoshub-4", + wantErr: true, + wantErrContains: "previous chain id cannot be empty", + }, + { + name: "fail - not allow empty new chain-id", + previousChainId: "cosmoshub-3", + newChainId: "", + wantErr: true, + wantErrContains: "new chain id cannot be empty", + }, + { + name: "fail - chain-id cannot be the same", + previousChainId: "cosmoshub-3", + newChainId: "cosmoshub-3", + wantErr: true, + wantErrContains: "previous chain id and new chain id cannot be the same", + }, + { + name: "fail - chain-id cannot be the same, case insensitive", + previousChainId: "CosmosHub-3", + newChainId: "cosmoshub-3", + wantErr: true, + wantErrContains: "chain id is not well-formed", + }, + { + name: "fail - reject invalid previous chain-id", + previousChainId: "cosmoshub@3", + newChainId: "cosmoshub-4", + wantErr: true, + wantErrContains: "previous chain id is not well-formed:", + }, + { + name: "fail - reject invalid new chain-id", + previousChainId: "cosmoshub-3", + newChainId: "cosmoshub@4", + wantErr: true, + wantErrContains: "new chain id is not well-formed:", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + m := MigrateChainId{ + PreviousChainId: tt.previousChainId, + NewChainId: tt.newChainId, + } + + err := m.ValidateBasic() + if tt.wantErr { + require.NotEmpty(t, tt.wantErrContains, "mis-configured test case") + require.Error(t, err) + require.Contains(t, err.Error(), tt.wantErrContains) + return + } + + require.NoError(t, err) + }) + } +} diff --git a/x/dymns/types/gov_update_aliases.go b/x/dymns/types/gov_update_aliases.go new file mode 100644 index 000000000..5297d67e9 --- /dev/null +++ b/x/dymns/types/gov_update_aliases.go @@ -0,0 +1,62 @@ +package types + +import ( + "fmt" + + errorsmod "cosmossdk.io/errors" + + "github.com/dymensionxyz/gerr-cosmos/gerrc" + + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" + dymnsutils "github.com/dymensionxyz/dymension/v3/x/dymns/utils" +) + +// ValidateBasic performs basic validation for the UpdateAliasesProposal. +func (m *UpdateAliasesProposal) ValidateBasic() error { + if len(m.Add) == 0 && len(m.Remove) == 0 { + return errorsmod.Wrap(gerrc.ErrInvalidArgument, "update list can not be empty") + } + + uniquePairs := make(map[string]bool) + // Describe usage of Go Map: only used for validation + + for _, r := range append(m.Add, m.Remove...) { + if err := r.ValidateBasic(); err != nil { + return err + } + + pairId := fmt.Sprintf("%s|%s", r.ChainId, r.Alias) + if _, found := uniquePairs[pairId]; found { + return errorsmod.Wrapf(gerrc.ErrInvalidArgument, "duplicate chain id and alias pair: %s", pairId) + } + uniquePairs[pairId] = true + } + + return v1beta1.ValidateAbstract(m) +} + +// ValidateBasic performs basic validation for the UpdateAlias operation. +func (m *UpdateAlias) ValidateBasic() error { + if m.ChainId == "" { + return govtypes.ErrInvalidProposalContent.Wrap("chain id cannot be empty") + } + + if !dymnsutils.IsValidChainIdFormat(m.ChainId) { + return errorsmod.Wrapf(gerrc.ErrInvalidArgument, "chain id is not well-formed: %s", m.ChainId) + } + + if m.Alias == "" { + return govtypes.ErrInvalidProposalContent.Wrap("alias cannot be empty") + } + + if !dymnsutils.IsValidAlias(m.Alias) { + return errorsmod.Wrapf(gerrc.ErrInvalidArgument, "alias is not well-formed: %s", m.Alias) + } + + if m.ChainId == m.Alias { + return errorsmod.Wrap(gerrc.ErrInvalidArgument, "chain id and alias cannot be the same") + } + + return nil +} diff --git a/x/dymns/types/gov_update_aliases_test.go b/x/dymns/types/gov_update_aliases_test.go new file mode 100644 index 000000000..e50e02dd0 --- /dev/null +++ b/x/dymns/types/gov_update_aliases_test.go @@ -0,0 +1,364 @@ +package types + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestUpdateAliasesProposal_ValidateBasic(t *testing.T) { + tests := []struct { + name string + title string + description string + add []UpdateAlias + remove []UpdateAlias + wantErr bool + wantErrContains string + }{ + { + name: "pass - valid, single add", + title: "T", + description: "D", + add: []UpdateAlias{ + { + ChainId: "dymension_1100-1", + Alias: "dym", + }, + }, + wantErr: false, + }, + { + name: "pass - valid, multiple add", + title: "T", + description: "D", + add: []UpdateAlias{ + { + ChainId: "dymension_1100-1", + Alias: "dym", + }, + { + ChainId: "blumbus_111-1", + Alias: "blumbus", + }, + }, + wantErr: false, + }, + { + name: "pass - valid, multiple add of same chain id", + title: "T", + description: "D", + add: []UpdateAlias{ + { + ChainId: "dymension_1100-1", + Alias: "dym", + }, + { + ChainId: "dymension_1100-1", + Alias: "dymension", + }, + }, + wantErr: false, + }, + { + name: "pass - valid, single remove", + title: "T", + description: "D", + remove: []UpdateAlias{ + { + ChainId: "dymension_1100-1", + Alias: "dym", + }, + }, + wantErr: false, + }, + { + name: "pass - valid, multiple remove", + title: "T", + description: "D", + remove: []UpdateAlias{ + { + ChainId: "dymension_1100-1", + Alias: "dym", + }, + { + ChainId: "blumbus_111-1", + Alias: "blumbus", + }, + }, + wantErr: false, + }, + { + name: "pass - valid, multiple remove of same chain id", + title: "T", + description: "D", + remove: []UpdateAlias{ + { + ChainId: "dymension_1100-1", + Alias: "dym", + }, + { + ChainId: "dymension_1100-1", + Alias: "dymension", + }, + }, + wantErr: false, + }, + { + name: "pass - valid, multiple add and remove", + title: "T", + description: "D", + add: []UpdateAlias{ + { + ChainId: "dymension_1100-1", + Alias: "dym1", + }, + { + ChainId: "blumbus_111-1", + Alias: "blumbus", + }, + }, + remove: []UpdateAlias{ + { + ChainId: "froopyland_111-1", + Alias: "fl", + }, + }, + wantErr: false, + }, + { + name: "pass - valid, multiple add and remove of same chain id", + title: "T", + description: "D", + add: []UpdateAlias{ + { + ChainId: "dymension_1100-1", + Alias: "dym1", + }, + { + ChainId: "dymension_1100-1", + Alias: "dym2", + }, + }, + remove: []UpdateAlias{ + { + ChainId: "dymension_1100-1", + Alias: "dym3", + }, + { + ChainId: "dymension_1100-1", + Alias: "dym4", + }, + }, + wantErr: false, + }, + { + name: "fail - reject empty title", + title: "", + description: "D", + add: []UpdateAlias{ + { + ChainId: "dymension_1100-1", + Alias: "dym", + }, + }, + wantErr: true, + wantErrContains: "proposal title cannot be blank", + }, + { + name: "fail - reject empty description", + title: "T", + description: "", + add: []UpdateAlias{ + { + ChainId: "dymension_1100-1", + Alias: "dym", + }, + }, + wantErr: true, + wantErrContains: "proposal description cannot be blank", + }, + { + name: "fail - reject empty proposal", + title: "T", + description: "D", + wantErr: true, + wantErrContains: "update list can not be empty", + }, + { + name: "fail - reject non-unique combination of chain id and alias", + title: "T", + description: "D", + add: []UpdateAlias{ + { + ChainId: "dymension_1100-1", + Alias: "dym1", + }, + { + ChainId: "blumbus_111-1", + Alias: "blumbus", + }, + }, + remove: []UpdateAlias{ + { + ChainId: "froopyland_111-1", + Alias: "fl", + }, + { + ChainId: "dymension_1100-1", + Alias: "dym1", + }, + }, + wantErr: true, + wantErrContains: "duplicate chain id and alias pair", + }, + { + name: "fail - reject non-unique combination of chain id and alias", + title: "T", + description: "D", + add: []UpdateAlias{ + { + ChainId: "dymension_1100-1", + Alias: "dym1", + }, + { + ChainId: "blumbus_111-1", + Alias: "blumbus", + }, + { + ChainId: "dymension_1100-1", + Alias: "dym1", + }, + }, + remove: []UpdateAlias{ + { + ChainId: "froopyland_111-1", + Alias: "fl", + }, + }, + wantErr: true, + wantErrContains: "duplicate chain id and alias pair", + }, + { + name: "fail - reject record that does not pass validation", + title: "T", + description: "", + add: []UpdateAlias{ + { + ChainId: "dymension_1100-1", + Alias: "dym", + }, + { + ChainId: "Blumbus_111-1", // bad chain id + Alias: "bb", + }, + }, + wantErr: true, + wantErrContains: "chain id is not well-formed", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + m := UpdateAliasesProposal{ + Title: tt.title, + Description: tt.description, + Add: tt.add, + Remove: tt.remove, + } + + err := m.ValidateBasic() + if tt.wantErr { + require.NotEmpty(t, tt.wantErrContains, "mis-configured test case") + require.Error(t, err) + require.Contains(t, err.Error(), tt.wantErrContains) + return + } + + require.NoError(t, err) + }) + } +} + +func TestUpdateAlias_ValidateBasic(t *testing.T) { + tests := []struct { + name string + chainId string + alias string + wantErr bool + wantErrContains string + }{ + { + name: "pass - valid", + chainId: "dymension_1100-1", + alias: "dym", + wantErr: false, + }, + { + name: "fail - chain-id can not be empty", + chainId: "", + alias: "dym", + wantErr: true, + wantErrContains: "chain id cannot be empty", + }, + { + name: "fail - chain-id must be well-formed", + chainId: "@dym", + alias: "dym", + wantErr: true, + wantErrContains: "chain id is not well-formed", + }, + { + name: "fail - chain-id must be well-formed", + chainId: "Dymension_1100-1", + alias: "dym", + wantErr: true, + wantErrContains: "chain id is not well-formed", + }, + { + name: "fail - alias can not be empty", + chainId: "dymension_1100-1", + alias: "", + wantErr: true, + wantErrContains: "alias cannot be empty", + }, + { + name: "fail - alias must be well-formed", + chainId: "dymension_1100-1", + alias: "@dym", + wantErr: true, + wantErrContains: "alias is not well-formed", + }, + { + name: "fail - alias must be well-formed", + chainId: "dymension_1100-1", + alias: "Dym", + wantErr: true, + wantErrContains: "alias is not well-formed", + }, + { + name: "fail - chain-id and alias can not be the same", + chainId: "dymension", + alias: "dymension", + wantErr: true, + wantErrContains: "chain id and alias cannot be the same", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + m := &UpdateAlias{ + ChainId: tt.chainId, + Alias: tt.alias, + } + + err := m.ValidateBasic() + if tt.wantErr { + require.NotEmpty(t, tt.wantErrContains, "mis-configured test case") + require.Error(t, err) + require.Contains(t, err.Error(), tt.wantErrContains) + return + } + + require.NoError(t, err) + }) + } +} diff --git a/x/dymns/types/keys.go b/x/dymns/types/keys.go new file mode 100644 index 000000000..3cc3310d4 --- /dev/null +++ b/x/dymns/types/keys.go @@ -0,0 +1,155 @@ +package types + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" +) + +const ( + // ModuleName defines the module name + ModuleName = "dymns" + + // StoreKey defines the primary module store key + StoreKey = ModuleName + + // RouterKey defines the module's message routing key + RouterKey = ModuleName + + // MemStoreKey defines the in-memory store key + MemStoreKey = "mem_" + ModuleName +) + +// prefix bytes for the DymNS persistent store. +const ( + prefixDymName = iota + 1 + prefixRvlDymNamesOwnedByAccount // reverse lookup store + prefixRvlConfiguredAddressToDymNamesInclude // reverse lookup store + prefixRvlFallbackAddressToDymNamesInclude // reverse lookup store + prefixSellOrder + prefixActiveSellOrdersExpiration + prefixCountBuyOrders + prefixBuyOrder + prefixRvlBuyerToBuyOrderIds // reverse lookup store + prefixRvlAssetIdToBuyOrderIds // reverse lookup store + prefixRollAppIdToAliases + prefixRvlAliasToRollAppId // reverse lookup store +) + +const ( + // partialStoreAssetTypeDymName is a part of the store key prefix for the SellOrder records of Dym-Name + partialStoreAssetTypeDymName = iota + + // partialStoreAssetTypeAlias is a part of the store key prefix for the SellOrder records of Alias + partialStoreAssetTypeAlias +) + +var ( + // KeyPrefixDymName is the key prefix for the DymName records + KeyPrefixDymName = []byte{prefixDymName} + + // KeyPrefixRvlDymNamesOwnedByAccount is the key prefix for the reverse lookup for Dym-Names owned by an account + KeyPrefixRvlDymNamesOwnedByAccount = []byte{prefixRvlDymNamesOwnedByAccount} + + // KeyPrefixRvlConfiguredAddressToDymNamesInclude is the key prefix for the reverse lookup for Dym-Names that contain the configured address (bech32) + KeyPrefixRvlConfiguredAddressToDymNamesInclude = []byte{prefixRvlConfiguredAddressToDymNamesInclude} + + // KeyPrefixRvlFallbackAddressToDymNamesInclude is the key prefix for the reverse lookup address for Dym-Names using fallback mechanism + KeyPrefixRvlFallbackAddressToDymNamesInclude = []byte{prefixRvlFallbackAddressToDymNamesInclude} + + // KeyPrefixSellOrder is the key prefix for the active SellOrder records of both type DymName/Alias + KeyPrefixSellOrder = []byte{prefixSellOrder} + + // KeyPrefixDymNameSellOrder is the key prefix for the active SellOrder records of type DymName + KeyPrefixDymNameSellOrder = []byte{prefixSellOrder, partialStoreAssetTypeDymName} + + // KeyPrefixAliasSellOrder is the key prefix for the active SellOrder records of type Alias + KeyPrefixAliasSellOrder = []byte{prefixSellOrder, partialStoreAssetTypeAlias} + + // KeyPrefixBuyOrder is the key prefix for the active BuyOrder records regardless asset type DymName/Alias + KeyPrefixBuyOrder = []byte{prefixBuyOrder} + + // KeyPrefixRvlBuyerToBuyOrderIds is the key prefix for the reverse lookup for BuyOrder IDs by the buyer + KeyPrefixRvlBuyerToBuyOrderIds = []byte{prefixRvlBuyerToBuyOrderIds} + + // KeyPrefixRvlDymNameToBuyOrderIds is the key prefix for the reverse lookup for BuyOrder IDs by the DymName + KeyPrefixRvlDymNameToBuyOrderIds = []byte{prefixRvlAssetIdToBuyOrderIds, partialStoreAssetTypeDymName} + + // KeyPrefixRvlAliasToBuyOrderIds is the key prefix for the reverse lookup for BuyOrder IDs by the Alias + KeyPrefixRvlAliasToBuyOrderIds = []byte{prefixRvlAssetIdToBuyOrderIds, partialStoreAssetTypeAlias} + + // KeyPrefixRollAppIdToAliases is the key prefix for the Roll-App ID to Alias records + KeyPrefixRollAppIdToAliases = []byte{prefixRollAppIdToAliases} + + // KeyPrefixRvlAliasToRollAppId is the key prefix for the reverse lookup for Alias to Roll-App ID records + KeyPrefixRvlAliasToRollAppId = []byte{prefixRvlAliasToRollAppId} +) + +var ( + KeyActiveSellOrdersExpirationOfDymName = []byte{prefixActiveSellOrdersExpiration, partialStoreAssetTypeDymName} + + KeyActiveSellOrdersExpirationOfAlias = []byte{prefixActiveSellOrdersExpiration, partialStoreAssetTypeAlias} + + // KeyCountBuyOrders is the key for the count of all-time buy orders + KeyCountBuyOrders = []byte{prefixCountBuyOrders} +) + +// DymNameKey returns a key for specific Dym-Name +func DymNameKey(name string) []byte { + return append(KeyPrefixDymName, []byte(name)...) +} + +// DymNamesOwnedByAccountRvlKey returns a key for reverse lookup for Dym-Names owned by an account +func DymNamesOwnedByAccountRvlKey(owner sdk.AccAddress) []byte { + return append(KeyPrefixRvlDymNamesOwnedByAccount, owner.Bytes()...) +} + +// ConfiguredAddressToDymNamesIncludeRvlKey returns a key for reverse lookup for Dym-Names that contain the configured address +func ConfiguredAddressToDymNamesIncludeRvlKey(address string) []byte { + return append(KeyPrefixRvlConfiguredAddressToDymNamesInclude, []byte(address)...) +} + +// FallbackAddressToDymNamesIncludeRvlKey returns the key for the reverse lookup address for Dym-Names using fallback mechanism +func FallbackAddressToDymNamesIncludeRvlKey(fallbackAddr FallbackAddress) []byte { + return append(KeyPrefixRvlFallbackAddressToDymNamesInclude, fallbackAddr...) +} + +// SellOrderKey returns a key for the active Sell-Order of the Dym-Name/Alias +func SellOrderKey(assetId string, assetType AssetType) []byte { + switch assetType { + case TypeName: + return append(KeyPrefixDymNameSellOrder, []byte(assetId)...) + case TypeAlias: + return append(KeyPrefixAliasSellOrder, []byte(assetId)...) + default: + panic("invalid asset type: " + assetType.PrettyName()) + } +} + +// BuyOrderKey returns a key for the active Buy-Order of the Dym-Name/Alias +func BuyOrderKey(orderId string) []byte { + return append(KeyPrefixBuyOrder, []byte(orderId)...) +} + +// BuyerToOrderIdsRvlKey returns a key for reverse lookup for Buy-Order IDs by the buyer +func BuyerToOrderIdsRvlKey(bzHexAddr []byte) []byte { + return append(KeyPrefixRvlBuyerToBuyOrderIds, bzHexAddr...) +} + +// DymNameToBuyOrderIdsRvlKey returns a key for reverse lookup for Buy-Order IDs by the Dym-Name +func DymNameToBuyOrderIdsRvlKey(dymName string) []byte { + return append(KeyPrefixRvlDymNameToBuyOrderIds, []byte(dymName)...) +} + +// AliasToBuyOrderIdsRvlKey returns a key for reverse lookup for Buy-Order IDs by the Alias +func AliasToBuyOrderIdsRvlKey(alias string) []byte { + return append(KeyPrefixRvlAliasToBuyOrderIds, []byte(alias)...) +} + +// RollAppIdToAliasesKey returns a key for the Roll-App ID to list of alias records +func RollAppIdToAliasesKey(rollAppId string) []byte { + return append(KeyPrefixRollAppIdToAliases, []byte(rollAppId)...) +} + +// AliasToRollAppIdRvlKey returns a key for reverse lookup for Alias to Roll-App ID records +func AliasToRollAppIdRvlKey(alias string) []byte { + return append(KeyPrefixRvlAliasToRollAppId, []byte(alias)...) +} diff --git a/x/dymns/types/keys_test.go b/x/dymns/types/keys_test.go new file mode 100644 index 000000000..7c8950b90 --- /dev/null +++ b/x/dymns/types/keys_test.go @@ -0,0 +1,84 @@ +package types + +import ( + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" +) + +func TestStorePrefixes(t *testing.T) { + t.Run("ensure key prefixes are not mistakenly modified", func(t *testing.T) { + require.Equal(t, []byte{0x01}, KeyPrefixDymName, "do not change it, will break the app") + require.Equal(t, []byte{0x02}, KeyPrefixRvlDymNamesOwnedByAccount, "do not change it, will break the app") + require.Equal(t, []byte{0x03}, KeyPrefixRvlConfiguredAddressToDymNamesInclude, "do not change it, will break the app") + require.Equal(t, []byte{0x04}, KeyPrefixRvlFallbackAddressToDymNamesInclude, "do not change it, will break the app") + require.Equal(t, []byte{0x05}, KeyPrefixSellOrder, "do not change it, will break the app") + require.Equal(t, []byte{0x05, partialStoreAssetTypeDymName}, KeyPrefixDymNameSellOrder, "do not change it, will break the app") + require.Equal(t, []byte{0x05, partialStoreAssetTypeAlias}, KeyPrefixAliasSellOrder, "do not change it, will break the app") + require.Equal(t, []byte{0x08}, KeyPrefixBuyOrder, "do not change it, will break the app") + require.Equal(t, []byte{0x09}, KeyPrefixRvlBuyerToBuyOrderIds, "do not change it, will break the app") + require.Equal(t, []byte{0x0A, partialStoreAssetTypeDymName}, KeyPrefixRvlDymNameToBuyOrderIds, "do not change it, will break the app") + require.Equal(t, []byte{0x0A, partialStoreAssetTypeAlias}, KeyPrefixRvlAliasToBuyOrderIds, "do not change it, will break the app") + require.Equal(t, []byte{0x0B}, KeyPrefixRollAppIdToAliases, "do not change it, will break the app") + require.Equal(t, []byte{0x0C}, KeyPrefixRvlAliasToRollAppId, "do not change it, will break the app") + }) + + t.Run("ensure keys are not mistakenly modified", func(t *testing.T) { + require.Equal(t, []byte{0x06, partialStoreAssetTypeDymName}, KeyActiveSellOrdersExpirationOfDymName, "do not change it, will break the app") + require.Equal(t, []byte{0x06, partialStoreAssetTypeAlias}, KeyActiveSellOrdersExpirationOfAlias, "do not change it, will break the app") + require.Equal(t, []byte{0x07}, KeyCountBuyOrders, "do not change it, will break the app") + }) + + t.Run("ensure partitioned keys are not mistakenly modified", func(t *testing.T) { + require.Equal(t, byte(0x00), byte(partialStoreAssetTypeDymName), "do not change it, will break the app") + require.Equal(t, byte(0x01), byte(partialStoreAssetTypeAlias), "do not change it, will break the app") + }) +} + +//goland:noinspection SpellCheckingInspection +func TestKeys(t *testing.T) { + for _, dymName := range []string{"a", "b", "my-name"} { + t.Run(dymName, func(t *testing.T) { + require.Equal(t, append(KeyPrefixDymName, []byte(dymName)...), DymNameKey(dymName)) + require.Equal(t, append(KeyPrefixDymNameSellOrder, []byte(dymName)...), SellOrderKey(dymName, TypeName)) + require.Equal(t, append(KeyPrefixRvlDymNameToBuyOrderIds, []byte(dymName)...), DymNameToBuyOrderIdsRvlKey(dymName)) + }) + } + + for _, alias := range []string{"a", "b", "alias"} { + t.Run(alias, func(t *testing.T) { + require.Equal(t, append(KeyPrefixAliasSellOrder, []byte(alias)...), SellOrderKey(alias, TypeAlias)) + require.Equal(t, append(KeyPrefixRvlAliasToBuyOrderIds, []byte(alias)...), AliasToBuyOrderIdsRvlKey(alias)) + }) + } + + for _, bech32Address := range []string{ + "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + "dym1gtcunp63a3aqypr250csar4devn8fjpqulq8d4", + "dym1tygms3xhhs3yv487phx3dw4a95jn7t7lnxec2d", + } { + t.Run(bech32Address, func(t *testing.T) { + accAddr := sdk.MustAccAddressFromBech32(bech32Address) + require.Equal(t, append(KeyPrefixRvlDymNamesOwnedByAccount, accAddr.Bytes()...), DymNamesOwnedByAccountRvlKey(accAddr)) + require.Equal(t, append(KeyPrefixRvlConfiguredAddressToDymNamesInclude, []byte(bech32Address)...), ConfiguredAddressToDymNamesIncludeRvlKey(bech32Address)) + require.Equal(t, append(KeyPrefixRvlFallbackAddressToDymNamesInclude, accAddr.Bytes()...), FallbackAddressToDymNamesIncludeRvlKey(FallbackAddress(accAddr))) + require.Equal(t, append(KeyPrefixRvlBuyerToBuyOrderIds, accAddr.Bytes()...), BuyerToOrderIdsRvlKey(accAddr.Bytes())) + }) + } + + for _, input := range []string{ + "888", + "aaa", + "@@@", + } { + t.Run(input, func(t *testing.T) { + require.Equal(t, append(KeyPrefixBuyOrder, []byte(input)...), BuyOrderKey(input)) + require.Equal(t, append(KeyPrefixRvlAliasToRollAppId, []byte(input)...), AliasToRollAppIdRvlKey(input)) + }) + } + + t.Run("should panics of getting Sell-Order related keys if asset type is invalid", func(t *testing.T) { + require.Panics(t, func() { _ = SellOrderKey("asset", AssetType_AT_UNKNOWN) }) + }) +} diff --git a/x/dymns/types/market.go b/x/dymns/types/market.go new file mode 100644 index 000000000..b6cc008ff --- /dev/null +++ b/x/dymns/types/market.go @@ -0,0 +1,55 @@ +package types + +import ( + errorsmod "cosmossdk.io/errors" + dymnsutils "github.com/dymensionxyz/dymension/v3/x/dymns/utils" + "github.com/dymensionxyz/gerr-cosmos/gerrc" +) + +const ( + // TypeName is an alias variable of AssetType_AT_DYM_NAME + TypeName = AssetType_AT_DYM_NAME + + // TypeAlias is an alias variable of AssetType_AT_ALIAS + TypeAlias = AssetType_AT_ALIAS +) + +var assetTypePrettyName = map[AssetType]string{ + AssetType_AT_DYM_NAME: "Dym-Name", + AssetType_AT_ALIAS: "Alias", +} + +func (x AssetType) PrettyName() string { + if s, ok := assetTypePrettyName[x]; ok { + return s + } + return "Unknown" +} + +func ValidateOrderParams(params []string, assetType AssetType) error { + switch assetType { + case AssetType_AT_DYM_NAME: + if len(params) != 0 { + return errorsmod.Wrapf(gerrc.ErrInvalidArgument, + "not accept order params for asset type: %s", assetType.PrettyName(), + ) + } + return nil + case AssetType_AT_ALIAS: + if len(params) != 1 { + return errorsmod.Wrapf(gerrc.ErrInvalidArgument, + "expect 1 order param of RollApp ID for asset type: %s", assetType.PrettyName(), + ) + } + if !dymnsutils.IsValidChainIdFormat(params[0]) { + return errorsmod.Wrapf(gerrc.ErrInvalidArgument, + "invalid RollApp ID format: %s", params[0], + ) + } + return nil + default: + return errorsmod.Wrapf(gerrc.ErrInvalidArgument, + "unknown asset type: %s", assetType, + ) + } +} diff --git a/x/dymns/types/market.pb.go b/x/dymns/types/market.pb.go new file mode 100644 index 000000000..cde0488f6 --- /dev/null +++ b/x/dymns/types/market.pb.go @@ -0,0 +1,1976 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: dymensionxyz/dymension/dymns/market.proto + +package types + +import ( + fmt "fmt" + 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 + +// AssetType present type of the asset of the Buy/Sell order. +type AssetType int32 + +const ( + AssetType_AT_UNKNOWN AssetType = 0 + AssetType_AT_DYM_NAME AssetType = 1 + AssetType_AT_ALIAS AssetType = 2 +) + +var AssetType_name = map[int32]string{ + 0: "AT_UNKNOWN", + 1: "AT_DYM_NAME", + 2: "AT_ALIAS", +} + +var AssetType_value = map[string]int32{ + "AT_UNKNOWN": 0, + "AT_DYM_NAME": 1, + "AT_ALIAS": 2, +} + +func (x AssetType) String() string { + return proto.EnumName(AssetType_name, int32(x)) +} + +func (AssetType) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_ddf761d4919b968f, []int{0} +} + +// SellOrder defines a sell order, placed by owner, to sell a Dym-Name/Alias. +// Sell-Order has an expiry date. +// After expiry date, if no one has placed a bid, this Sell-Order will be closed, no change. +// If there is a bid, the highest bid will win, and the Dym-Name/Alias ownership will be transferred to the winner. +// If the bid matches the sell price, the Dym-Name/Alias ownership will be transferred to the bidder immediately. +type SellOrder struct { + // asset_id is the Dym-Name/Alias being opened to be sold. + AssetId string `protobuf:"bytes,1,opt,name=asset_id,json=assetId,proto3" json:"asset_id,omitempty"` + // asset_type is the type of the asset of the order, is Dym-Name/Alias. + AssetType AssetType `protobuf:"varint,2,opt,name=asset_type,json=assetType,proto3,enum=dymensionxyz.dymension.dymns.AssetType" json:"asset_type,omitempty"` + // expire_at is the last effective date of this SO + ExpireAt int64 `protobuf:"varint,3,opt,name=expire_at,json=expireAt,proto3" json:"expire_at,omitempty"` + // min_price is the minimum price that the owner is willing to accept for the asset. + MinPrice types.Coin `protobuf:"bytes,4,opt,name=min_price,json=minPrice,proto3" json:"min_price"` + // sell_price is the price that the owner is willing to sell the Dym-Name/Alias for, + // the SO will be completed when the price is met, ownership transferred. + // If the sell price is zero, the SO will be closed when the expire_at is reached and the highest bidder wins. + SellPrice *types.Coin `protobuf:"bytes,5,opt,name=sell_price,json=sellPrice,proto3" json:"sell_price,omitempty"` + // highest_bid is the highest bid on the SO, if any. Price must be greater than or equal to the min_price. + HighestBid *SellOrderBid `protobuf:"bytes,6,opt,name=highest_bid,json=highestBid,proto3" json:"highest_bid,omitempty"` +} + +func (m *SellOrder) Reset() { *m = SellOrder{} } +func (m *SellOrder) String() string { return proto.CompactTextString(m) } +func (*SellOrder) ProtoMessage() {} +func (*SellOrder) Descriptor() ([]byte, []int) { + return fileDescriptor_ddf761d4919b968f, []int{0} +} +func (m *SellOrder) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SellOrder) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SellOrder.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 *SellOrder) XXX_Merge(src proto.Message) { + xxx_messageInfo_SellOrder.Merge(m, src) +} +func (m *SellOrder) XXX_Size() int { + return m.Size() +} +func (m *SellOrder) XXX_DiscardUnknown() { + xxx_messageInfo_SellOrder.DiscardUnknown(m) +} + +var xxx_messageInfo_SellOrder proto.InternalMessageInfo + +func (m *SellOrder) GetAssetId() string { + if m != nil { + return m.AssetId + } + return "" +} + +func (m *SellOrder) GetAssetType() AssetType { + if m != nil { + return m.AssetType + } + return AssetType_AT_UNKNOWN +} + +func (m *SellOrder) GetExpireAt() int64 { + if m != nil { + return m.ExpireAt + } + return 0 +} + +func (m *SellOrder) GetMinPrice() types.Coin { + if m != nil { + return m.MinPrice + } + return types.Coin{} +} + +func (m *SellOrder) GetSellPrice() *types.Coin { + if m != nil { + return m.SellPrice + } + return nil +} + +func (m *SellOrder) GetHighestBid() *SellOrderBid { + if m != nil { + return m.HighestBid + } + return nil +} + +// ActiveSellOrdersExpiration contains list of active SOs, store expiration date mapped by asset identity. +// Used by hook to find out expired SO instead of iterating through all records. +type ActiveSellOrdersExpiration struct { + Records []ActiveSellOrdersExpirationRecord `protobuf:"bytes,1,rep,name=records,proto3" json:"records"` +} + +func (m *ActiveSellOrdersExpiration) Reset() { *m = ActiveSellOrdersExpiration{} } +func (m *ActiveSellOrdersExpiration) String() string { return proto.CompactTextString(m) } +func (*ActiveSellOrdersExpiration) ProtoMessage() {} +func (*ActiveSellOrdersExpiration) Descriptor() ([]byte, []int) { + return fileDescriptor_ddf761d4919b968f, []int{1} +} +func (m *ActiveSellOrdersExpiration) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ActiveSellOrdersExpiration) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ActiveSellOrdersExpiration.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 *ActiveSellOrdersExpiration) XXX_Merge(src proto.Message) { + xxx_messageInfo_ActiveSellOrdersExpiration.Merge(m, src) +} +func (m *ActiveSellOrdersExpiration) XXX_Size() int { + return m.Size() +} +func (m *ActiveSellOrdersExpiration) XXX_DiscardUnknown() { + xxx_messageInfo_ActiveSellOrdersExpiration.DiscardUnknown(m) +} + +var xxx_messageInfo_ActiveSellOrdersExpiration proto.InternalMessageInfo + +func (m *ActiveSellOrdersExpiration) GetRecords() []ActiveSellOrdersExpirationRecord { + if m != nil { + return m.Records + } + return nil +} + +// ActiveSellOrdersExpirationRecord contains the expiration date of an active Sell-Order. +type ActiveSellOrdersExpirationRecord struct { + // asset_id is the Dym-Name/Alias being opened to be sold. + AssetId string `protobuf:"bytes,1,opt,name=asset_id,json=assetId,proto3" json:"asset_id,omitempty"` + // expire_at is the last effective date of this Sell-Order. + ExpireAt int64 `protobuf:"varint,2,opt,name=expire_at,json=expireAt,proto3" json:"expire_at,omitempty"` +} + +func (m *ActiveSellOrdersExpirationRecord) Reset() { *m = ActiveSellOrdersExpirationRecord{} } +func (m *ActiveSellOrdersExpirationRecord) String() string { return proto.CompactTextString(m) } +func (*ActiveSellOrdersExpirationRecord) ProtoMessage() {} +func (*ActiveSellOrdersExpirationRecord) Descriptor() ([]byte, []int) { + return fileDescriptor_ddf761d4919b968f, []int{2} +} +func (m *ActiveSellOrdersExpirationRecord) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ActiveSellOrdersExpirationRecord) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ActiveSellOrdersExpirationRecord.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 *ActiveSellOrdersExpirationRecord) XXX_Merge(src proto.Message) { + xxx_messageInfo_ActiveSellOrdersExpirationRecord.Merge(m, src) +} +func (m *ActiveSellOrdersExpirationRecord) XXX_Size() int { + return m.Size() +} +func (m *ActiveSellOrdersExpirationRecord) XXX_DiscardUnknown() { + xxx_messageInfo_ActiveSellOrdersExpirationRecord.DiscardUnknown(m) +} + +var xxx_messageInfo_ActiveSellOrdersExpirationRecord proto.InternalMessageInfo + +func (m *ActiveSellOrdersExpirationRecord) GetAssetId() string { + if m != nil { + return m.AssetId + } + return "" +} + +func (m *ActiveSellOrdersExpirationRecord) GetExpireAt() int64 { + if m != nil { + return m.ExpireAt + } + return 0 +} + +// SellOrderBid defines a bid placed by an account on a Sell-Order. +type SellOrderBid struct { + // bidder is the account address of the account which placed the bid. + Bidder string `protobuf:"bytes,1,opt,name=bidder,proto3" json:"bidder,omitempty"` + // price is the amount of coin offered by the bidder. + Price types.Coin `protobuf:"bytes,2,opt,name=price,proto3" json:"price"` + // params is the list of parameters of the bid. + // It is empty for asset type Dym-Name. + // It has one element for asset type Alias, which is the rollapp_id to assigned for. + Params []string `protobuf:"bytes,3,rep,name=params,proto3" json:"params,omitempty"` +} + +func (m *SellOrderBid) Reset() { *m = SellOrderBid{} } +func (m *SellOrderBid) String() string { return proto.CompactTextString(m) } +func (*SellOrderBid) ProtoMessage() {} +func (*SellOrderBid) Descriptor() ([]byte, []int) { + return fileDescriptor_ddf761d4919b968f, []int{3} +} +func (m *SellOrderBid) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SellOrderBid) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SellOrderBid.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 *SellOrderBid) XXX_Merge(src proto.Message) { + xxx_messageInfo_SellOrderBid.Merge(m, src) +} +func (m *SellOrderBid) XXX_Size() int { + return m.Size() +} +func (m *SellOrderBid) XXX_DiscardUnknown() { + xxx_messageInfo_SellOrderBid.DiscardUnknown(m) +} + +var xxx_messageInfo_SellOrderBid proto.InternalMessageInfo + +func (m *SellOrderBid) GetBidder() string { + if m != nil { + return m.Bidder + } + return "" +} + +func (m *SellOrderBid) GetPrice() types.Coin { + if m != nil { + return m.Price + } + return types.Coin{} +} + +func (m *SellOrderBid) GetParams() []string { + if m != nil { + return m.Params + } + return nil +} + +// BuyOrder defines an offer to buy a Dym-Name/Alias, placed by buyer. +// Buyer will need to deposit the offer amount to the module account. +// When the owner of the Dym-Name/Alias accepts the offer, deposited amount will be transferred to the owner. +// When the buyer cancels the offer, deposited amount will be refunded to the buyer. +type BuyOrder struct { + // id is the unique identifier of the order. Generated by the module. + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + // asset_id of the Dym-Name/Alias willing to buy. + AssetId string `protobuf:"bytes,2,opt,name=asset_id,json=assetId,proto3" json:"asset_id,omitempty"` + // asset_type is type of the asset of the order, is Dym-Name/Alias + AssetType AssetType `protobuf:"varint,3,opt,name=asset_type,json=assetType,proto3,enum=dymensionxyz.dymension.dymns.AssetType" json:"asset_type,omitempty"` + // params is the list of parameters of the Buy-Order. + // It is empty for asset type Dym-Name. + // It has one element for asset type Alias, which is the rollapp_id to assigned for. + Params []string `protobuf:"bytes,4,rep,name=params,proto3" json:"params,omitempty"` + // buyer is bech32 address of the account which placed the order. + Buyer string `protobuf:"bytes,5,opt,name=buyer,proto3" json:"buyer,omitempty"` + // offer_price is the amount of coins that buyer willing to pay for the asset. + // This amount is deposited to the module account upon placing the offer. + OfferPrice types.Coin `protobuf:"bytes,6,opt,name=offer_price,json=offerPrice,proto3" json:"offer_price"` + // counterparty_offer_price is the price that the Dym-Name/Alias owner is willing to sell for. + // This is used for counterparty price negotiation and for information only. + // The transaction can only be executed when the owner accepts the offer with exact offer_price. + CounterpartyOfferPrice *types.Coin `protobuf:"bytes,7,opt,name=counterparty_offer_price,json=counterpartyOfferPrice,proto3" json:"counterparty_offer_price,omitempty"` +} + +func (m *BuyOrder) Reset() { *m = BuyOrder{} } +func (m *BuyOrder) String() string { return proto.CompactTextString(m) } +func (*BuyOrder) ProtoMessage() {} +func (*BuyOrder) Descriptor() ([]byte, []int) { + return fileDescriptor_ddf761d4919b968f, []int{4} +} +func (m *BuyOrder) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *BuyOrder) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_BuyOrder.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 *BuyOrder) XXX_Merge(src proto.Message) { + xxx_messageInfo_BuyOrder.Merge(m, src) +} +func (m *BuyOrder) XXX_Size() int { + return m.Size() +} +func (m *BuyOrder) XXX_DiscardUnknown() { + xxx_messageInfo_BuyOrder.DiscardUnknown(m) +} + +var xxx_messageInfo_BuyOrder proto.InternalMessageInfo + +func (m *BuyOrder) GetId() string { + if m != nil { + return m.Id + } + return "" +} + +func (m *BuyOrder) GetAssetId() string { + if m != nil { + return m.AssetId + } + return "" +} + +func (m *BuyOrder) GetAssetType() AssetType { + if m != nil { + return m.AssetType + } + return AssetType_AT_UNKNOWN +} + +func (m *BuyOrder) GetParams() []string { + if m != nil { + return m.Params + } + return nil +} + +func (m *BuyOrder) GetBuyer() string { + if m != nil { + return m.Buyer + } + return "" +} + +func (m *BuyOrder) GetOfferPrice() types.Coin { + if m != nil { + return m.OfferPrice + } + return types.Coin{} +} + +func (m *BuyOrder) GetCounterpartyOfferPrice() *types.Coin { + if m != nil { + return m.CounterpartyOfferPrice + } + return nil +} + +// ReverseLookupBuyOrderIds contains a list of Buy-Orders IDs for reverse lookup. +type ReverseLookupBuyOrderIds struct { + // order_ids is a list of Buy-Order IDs of the Buy-Orders linked to the reverse-lookup record. + OrderIds []string `protobuf:"bytes,1,rep,name=order_ids,json=orderIds,proto3" json:"order_ids,omitempty"` +} + +func (m *ReverseLookupBuyOrderIds) Reset() { *m = ReverseLookupBuyOrderIds{} } +func (m *ReverseLookupBuyOrderIds) String() string { return proto.CompactTextString(m) } +func (*ReverseLookupBuyOrderIds) ProtoMessage() {} +func (*ReverseLookupBuyOrderIds) Descriptor() ([]byte, []int) { + return fileDescriptor_ddf761d4919b968f, []int{5} +} +func (m *ReverseLookupBuyOrderIds) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ReverseLookupBuyOrderIds) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ReverseLookupBuyOrderIds.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 *ReverseLookupBuyOrderIds) XXX_Merge(src proto.Message) { + xxx_messageInfo_ReverseLookupBuyOrderIds.Merge(m, src) +} +func (m *ReverseLookupBuyOrderIds) XXX_Size() int { + return m.Size() +} +func (m *ReverseLookupBuyOrderIds) XXX_DiscardUnknown() { + xxx_messageInfo_ReverseLookupBuyOrderIds.DiscardUnknown(m) +} + +var xxx_messageInfo_ReverseLookupBuyOrderIds proto.InternalMessageInfo + +func (m *ReverseLookupBuyOrderIds) GetOrderIds() []string { + if m != nil { + return m.OrderIds + } + return nil +} + +func init() { + proto.RegisterEnum("dymensionxyz.dymension.dymns.AssetType", AssetType_name, AssetType_value) + proto.RegisterType((*SellOrder)(nil), "dymensionxyz.dymension.dymns.SellOrder") + proto.RegisterType((*ActiveSellOrdersExpiration)(nil), "dymensionxyz.dymension.dymns.ActiveSellOrdersExpiration") + proto.RegisterType((*ActiveSellOrdersExpirationRecord)(nil), "dymensionxyz.dymension.dymns.ActiveSellOrdersExpirationRecord") + proto.RegisterType((*SellOrderBid)(nil), "dymensionxyz.dymension.dymns.SellOrderBid") + proto.RegisterType((*BuyOrder)(nil), "dymensionxyz.dymension.dymns.BuyOrder") + proto.RegisterType((*ReverseLookupBuyOrderIds)(nil), "dymensionxyz.dymension.dymns.ReverseLookupBuyOrderIds") +} + +func init() { + proto.RegisterFile("dymensionxyz/dymension/dymns/market.proto", fileDescriptor_ddf761d4919b968f) +} + +var fileDescriptor_ddf761d4919b968f = []byte{ + // 626 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x54, 0x4f, 0x4f, 0xdb, 0x4e, + 0x10, 0x8d, 0x1d, 0x08, 0xf1, 0x04, 0xf1, 0x43, 0x2b, 0x84, 0x0c, 0xbf, 0xca, 0xb5, 0x72, 0x69, + 0xca, 0xc1, 0x16, 0x41, 0x55, 0xab, 0xaa, 0xaa, 0xea, 0xb4, 0x54, 0x42, 0x40, 0xa8, 0x4c, 0xaa, + 0xaa, 0x1c, 0x6a, 0xf9, 0xcf, 0x12, 0x56, 0xc4, 0x5e, 0x6b, 0x77, 0x13, 0xe1, 0xaa, 0x1f, 0xa2, + 0x9f, 0xa9, 0x27, 0x8e, 0x1c, 0x7b, 0xaa, 0x2a, 0xf8, 0x22, 0x95, 0xbd, 0xc6, 0x0d, 0x07, 0x12, + 0xd4, 0xdb, 0x8c, 0x3d, 0xef, 0x69, 0xde, 0xbc, 0xa7, 0x85, 0xa7, 0x51, 0x16, 0xe3, 0x84, 0x13, + 0x9a, 0x5c, 0x64, 0x5f, 0xed, 0xaa, 0xc9, 0xab, 0x84, 0xdb, 0xb1, 0xcf, 0xce, 0xb1, 0xb0, 0x52, + 0x46, 0x05, 0x45, 0x8f, 0xa6, 0x47, 0xad, 0xaa, 0xb1, 0x8a, 0xd1, 0xcd, 0xb5, 0x21, 0x1d, 0xd2, + 0x62, 0xd0, 0xce, 0x2b, 0x89, 0xd9, 0x34, 0x42, 0xca, 0x63, 0xca, 0xed, 0xc0, 0xe7, 0xd8, 0x9e, + 0x6c, 0x07, 0x58, 0xf8, 0xdb, 0x76, 0x48, 0x49, 0x22, 0xff, 0xb7, 0xaf, 0x54, 0xd0, 0x8e, 0xf1, + 0x68, 0x74, 0xc4, 0x22, 0xcc, 0xd0, 0x06, 0x34, 0x7d, 0xce, 0xb1, 0xf0, 0x48, 0xa4, 0x2b, 0xa6, + 0xd2, 0xd1, 0xdc, 0xa5, 0xa2, 0xdf, 0x8b, 0xd0, 0x7b, 0x00, 0xf9, 0x4b, 0x64, 0x29, 0xd6, 0x55, + 0x53, 0xe9, 0xac, 0x74, 0x9f, 0x58, 0xb3, 0x36, 0xb2, 0x9c, 0x7c, 0x7e, 0x90, 0xa5, 0xd8, 0xd5, + 0xfc, 0xdb, 0x12, 0xfd, 0x0f, 0x1a, 0xbe, 0x48, 0x09, 0xc3, 0x9e, 0x2f, 0xf4, 0xba, 0xa9, 0x74, + 0xea, 0x6e, 0x53, 0x7e, 0x70, 0x04, 0x7a, 0x05, 0x5a, 0x4c, 0x12, 0x2f, 0x65, 0x24, 0xc4, 0xfa, + 0x82, 0xa9, 0x74, 0x5a, 0xdd, 0x0d, 0x4b, 0x2a, 0xb0, 0x72, 0x05, 0x56, 0xa9, 0xc0, 0x7a, 0x4b, + 0x49, 0xd2, 0x5b, 0xb8, 0xfc, 0xf5, 0xb8, 0xe6, 0x36, 0x63, 0x92, 0x7c, 0xc8, 0x01, 0xe8, 0x05, + 0x00, 0xc7, 0xa3, 0x51, 0x09, 0x5f, 0x9c, 0x03, 0x77, 0xb5, 0x7c, 0x58, 0x22, 0xf7, 0xa1, 0x75, + 0x46, 0x86, 0x67, 0x98, 0x0b, 0x2f, 0x20, 0x91, 0xde, 0x28, 0xa0, 0x5b, 0xb3, 0xd5, 0x55, 0x57, + 0xeb, 0x91, 0xc8, 0x85, 0x12, 0xde, 0x23, 0x51, 0xfb, 0x1b, 0x6c, 0x3a, 0xa1, 0x20, 0x13, 0x5c, + 0x4d, 0xf0, 0xdd, 0x5c, 0xa0, 0x2f, 0x08, 0x4d, 0xd0, 0x17, 0x58, 0x62, 0x38, 0xa4, 0x2c, 0xe2, + 0xba, 0x62, 0xd6, 0x3b, 0xad, 0xee, 0xeb, 0x39, 0x47, 0xbc, 0x97, 0xca, 0x2d, 0x68, 0xca, 0x2b, + 0xdc, 0x92, 0xb6, 0x4f, 0xc0, 0x9c, 0x07, 0x99, 0x65, 0xf3, 0x1d, 0x7b, 0xd4, 0xbb, 0xf6, 0xb4, + 0xc7, 0xb0, 0x3c, 0xad, 0x1a, 0xad, 0x43, 0x23, 0x20, 0x51, 0x84, 0x59, 0xc9, 0x52, 0x76, 0xe8, + 0x19, 0x2c, 0x4a, 0x0f, 0xd4, 0x87, 0x59, 0x28, 0xa7, 0x73, 0xba, 0xd4, 0x67, 0x7e, 0xcc, 0xf5, + 0xba, 0x59, 0xcf, 0xe9, 0x64, 0xd7, 0xfe, 0xa1, 0x42, 0xb3, 0x37, 0xce, 0x64, 0x44, 0x57, 0x40, + 0xad, 0xb6, 0x56, 0xc9, 0x5d, 0x2d, 0xea, 0xac, 0xc8, 0xd6, 0xff, 0x39, 0xb2, 0x7f, 0xf7, 0x5a, + 0x98, 0xde, 0x0b, 0xad, 0xc1, 0x62, 0x30, 0xce, 0x30, 0x2b, 0xa2, 0xa6, 0xb9, 0xb2, 0x41, 0x6f, + 0xa0, 0x45, 0x4f, 0x4f, 0x31, 0x2b, 0x63, 0xd8, 0x78, 0xd8, 0x09, 0xa0, 0xc0, 0xc8, 0x34, 0x1e, + 0x83, 0x1e, 0xd2, 0x71, 0x22, 0x30, 0x4b, 0x7d, 0x26, 0x32, 0x6f, 0x9a, 0x6e, 0x69, 0x5e, 0xaa, + 0xd7, 0xa7, 0xa1, 0x47, 0x15, 0x69, 0xfb, 0x39, 0xe8, 0x2e, 0x9e, 0x60, 0xc6, 0xf1, 0x01, 0xa5, + 0xe7, 0xe3, 0xf4, 0xf6, 0xa0, 0x7b, 0x11, 0xcf, 0x4d, 0xa7, 0x79, 0xed, 0x91, 0x32, 0x95, 0x9a, + 0xdb, 0xa4, 0xe5, 0xcf, 0xad, 0x97, 0xa0, 0x55, 0x57, 0x41, 0x2b, 0x00, 0xce, 0xc0, 0xfb, 0xd8, + 0xdf, 0xef, 0x1f, 0x7d, 0xea, 0xaf, 0xd6, 0xd0, 0x7f, 0xd0, 0x72, 0x06, 0xde, 0xbb, 0xcf, 0x87, + 0x5e, 0xdf, 0x39, 0xdc, 0x5d, 0x55, 0xd0, 0x32, 0x34, 0x9d, 0x81, 0xe7, 0x1c, 0xec, 0x39, 0xc7, + 0xab, 0x6a, 0xef, 0xe0, 0xf2, 0xda, 0x50, 0xae, 0xae, 0x0d, 0xe5, 0xf7, 0xb5, 0xa1, 0x7c, 0xbf, + 0x31, 0x6a, 0x57, 0x37, 0x46, 0xed, 0xe7, 0x8d, 0x51, 0x3b, 0xe9, 0x0e, 0x89, 0x38, 0x1b, 0x07, + 0x56, 0x48, 0x63, 0xfb, 0x9e, 0x17, 0x70, 0xb2, 0x63, 0x5f, 0x94, 0xcf, 0x60, 0x6e, 0x20, 0x0f, + 0x1a, 0xc5, 0x93, 0xb5, 0xf3, 0x27, 0x00, 0x00, 0xff, 0xff, 0x62, 0x23, 0xac, 0x0b, 0x33, 0x05, + 0x00, 0x00, +} + +func (m *SellOrder) 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 *SellOrder) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SellOrder) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.HighestBid != nil { + { + size, err := m.HighestBid.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMarket(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x32 + } + if m.SellPrice != nil { + { + size, err := m.SellPrice.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMarket(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + } + { + size, err := m.MinPrice.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMarket(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + if m.ExpireAt != 0 { + i = encodeVarintMarket(dAtA, i, uint64(m.ExpireAt)) + i-- + dAtA[i] = 0x18 + } + if m.AssetType != 0 { + i = encodeVarintMarket(dAtA, i, uint64(m.AssetType)) + i-- + dAtA[i] = 0x10 + } + if len(m.AssetId) > 0 { + i -= len(m.AssetId) + copy(dAtA[i:], m.AssetId) + i = encodeVarintMarket(dAtA, i, uint64(len(m.AssetId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ActiveSellOrdersExpiration) 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 *ActiveSellOrdersExpiration) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ActiveSellOrdersExpiration) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Records) > 0 { + for iNdEx := len(m.Records) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Records[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMarket(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *ActiveSellOrdersExpirationRecord) 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 *ActiveSellOrdersExpirationRecord) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ActiveSellOrdersExpirationRecord) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.ExpireAt != 0 { + i = encodeVarintMarket(dAtA, i, uint64(m.ExpireAt)) + i-- + dAtA[i] = 0x10 + } + if len(m.AssetId) > 0 { + i -= len(m.AssetId) + copy(dAtA[i:], m.AssetId) + i = encodeVarintMarket(dAtA, i, uint64(len(m.AssetId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *SellOrderBid) 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 *SellOrderBid) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SellOrderBid) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Params) > 0 { + for iNdEx := len(m.Params) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Params[iNdEx]) + copy(dAtA[i:], m.Params[iNdEx]) + i = encodeVarintMarket(dAtA, i, uint64(len(m.Params[iNdEx]))) + i-- + dAtA[i] = 0x1a + } + } + { + size, err := m.Price.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMarket(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + if len(m.Bidder) > 0 { + i -= len(m.Bidder) + copy(dAtA[i:], m.Bidder) + i = encodeVarintMarket(dAtA, i, uint64(len(m.Bidder))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *BuyOrder) 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 *BuyOrder) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *BuyOrder) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.CounterpartyOfferPrice != nil { + { + size, err := m.CounterpartyOfferPrice.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMarket(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x3a + } + { + size, err := m.OfferPrice.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMarket(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x32 + if len(m.Buyer) > 0 { + i -= len(m.Buyer) + copy(dAtA[i:], m.Buyer) + i = encodeVarintMarket(dAtA, i, uint64(len(m.Buyer))) + i-- + dAtA[i] = 0x2a + } + if len(m.Params) > 0 { + for iNdEx := len(m.Params) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Params[iNdEx]) + copy(dAtA[i:], m.Params[iNdEx]) + i = encodeVarintMarket(dAtA, i, uint64(len(m.Params[iNdEx]))) + i-- + dAtA[i] = 0x22 + } + } + if m.AssetType != 0 { + i = encodeVarintMarket(dAtA, i, uint64(m.AssetType)) + i-- + dAtA[i] = 0x18 + } + if len(m.AssetId) > 0 { + i -= len(m.AssetId) + copy(dAtA[i:], m.AssetId) + i = encodeVarintMarket(dAtA, i, uint64(len(m.AssetId))) + i-- + dAtA[i] = 0x12 + } + if len(m.Id) > 0 { + i -= len(m.Id) + copy(dAtA[i:], m.Id) + i = encodeVarintMarket(dAtA, i, uint64(len(m.Id))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ReverseLookupBuyOrderIds) 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 *ReverseLookupBuyOrderIds) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ReverseLookupBuyOrderIds) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.OrderIds) > 0 { + for iNdEx := len(m.OrderIds) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.OrderIds[iNdEx]) + copy(dAtA[i:], m.OrderIds[iNdEx]) + i = encodeVarintMarket(dAtA, i, uint64(len(m.OrderIds[iNdEx]))) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func encodeVarintMarket(dAtA []byte, offset int, v uint64) int { + offset -= sovMarket(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *SellOrder) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.AssetId) + if l > 0 { + n += 1 + l + sovMarket(uint64(l)) + } + if m.AssetType != 0 { + n += 1 + sovMarket(uint64(m.AssetType)) + } + if m.ExpireAt != 0 { + n += 1 + sovMarket(uint64(m.ExpireAt)) + } + l = m.MinPrice.Size() + n += 1 + l + sovMarket(uint64(l)) + if m.SellPrice != nil { + l = m.SellPrice.Size() + n += 1 + l + sovMarket(uint64(l)) + } + if m.HighestBid != nil { + l = m.HighestBid.Size() + n += 1 + l + sovMarket(uint64(l)) + } + return n +} + +func (m *ActiveSellOrdersExpiration) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Records) > 0 { + for _, e := range m.Records { + l = e.Size() + n += 1 + l + sovMarket(uint64(l)) + } + } + return n +} + +func (m *ActiveSellOrdersExpirationRecord) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.AssetId) + if l > 0 { + n += 1 + l + sovMarket(uint64(l)) + } + if m.ExpireAt != 0 { + n += 1 + sovMarket(uint64(m.ExpireAt)) + } + return n +} + +func (m *SellOrderBid) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Bidder) + if l > 0 { + n += 1 + l + sovMarket(uint64(l)) + } + l = m.Price.Size() + n += 1 + l + sovMarket(uint64(l)) + if len(m.Params) > 0 { + for _, s := range m.Params { + l = len(s) + n += 1 + l + sovMarket(uint64(l)) + } + } + return n +} + +func (m *BuyOrder) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Id) + if l > 0 { + n += 1 + l + sovMarket(uint64(l)) + } + l = len(m.AssetId) + if l > 0 { + n += 1 + l + sovMarket(uint64(l)) + } + if m.AssetType != 0 { + n += 1 + sovMarket(uint64(m.AssetType)) + } + if len(m.Params) > 0 { + for _, s := range m.Params { + l = len(s) + n += 1 + l + sovMarket(uint64(l)) + } + } + l = len(m.Buyer) + if l > 0 { + n += 1 + l + sovMarket(uint64(l)) + } + l = m.OfferPrice.Size() + n += 1 + l + sovMarket(uint64(l)) + if m.CounterpartyOfferPrice != nil { + l = m.CounterpartyOfferPrice.Size() + n += 1 + l + sovMarket(uint64(l)) + } + return n +} + +func (m *ReverseLookupBuyOrderIds) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.OrderIds) > 0 { + for _, s := range m.OrderIds { + l = len(s) + n += 1 + l + sovMarket(uint64(l)) + } + } + return n +} + +func sovMarket(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozMarket(x uint64) (n int) { + return sovMarket(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *SellOrder) 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 ErrIntOverflowMarket + } + 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: SellOrder: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SellOrder: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AssetId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMarket + } + 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 ErrInvalidLengthMarket + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthMarket + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.AssetId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field AssetType", wireType) + } + m.AssetType = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMarket + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.AssetType |= AssetType(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ExpireAt", wireType) + } + m.ExpireAt = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMarket + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ExpireAt |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field MinPrice", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMarket + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMarket + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMarket + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.MinPrice.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SellPrice", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMarket + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMarket + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMarket + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.SellPrice == nil { + m.SellPrice = &types.Coin{} + } + if err := m.SellPrice.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field HighestBid", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMarket + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMarket + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMarket + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.HighestBid == nil { + m.HighestBid = &SellOrderBid{} + } + if err := m.HighestBid.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipMarket(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthMarket + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ActiveSellOrdersExpiration) 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 ErrIntOverflowMarket + } + 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: ActiveSellOrdersExpiration: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ActiveSellOrdersExpiration: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Records", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMarket + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMarket + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMarket + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Records = append(m.Records, ActiveSellOrdersExpirationRecord{}) + if err := m.Records[len(m.Records)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipMarket(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthMarket + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ActiveSellOrdersExpirationRecord) 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 ErrIntOverflowMarket + } + 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: ActiveSellOrdersExpirationRecord: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ActiveSellOrdersExpirationRecord: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AssetId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMarket + } + 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 ErrInvalidLengthMarket + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthMarket + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.AssetId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ExpireAt", wireType) + } + m.ExpireAt = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMarket + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ExpireAt |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipMarket(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthMarket + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *SellOrderBid) 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 ErrIntOverflowMarket + } + 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: SellOrderBid: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SellOrderBid: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Bidder", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMarket + } + 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 ErrInvalidLengthMarket + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthMarket + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Bidder = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + 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 ErrIntOverflowMarket + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMarket + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMarket + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Price.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Params", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMarket + } + 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 ErrInvalidLengthMarket + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthMarket + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Params = append(m.Params, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipMarket(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthMarket + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *BuyOrder) 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 ErrIntOverflowMarket + } + 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: BuyOrder: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: BuyOrder: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMarket + } + 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 ErrInvalidLengthMarket + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthMarket + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Id = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AssetId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMarket + } + 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 ErrInvalidLengthMarket + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthMarket + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.AssetId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field AssetType", wireType) + } + m.AssetType = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMarket + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.AssetType |= AssetType(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Params", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMarket + } + 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 ErrInvalidLengthMarket + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthMarket + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Params = append(m.Params, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Buyer", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMarket + } + 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 ErrInvalidLengthMarket + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthMarket + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Buyer = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field OfferPrice", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMarket + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMarket + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMarket + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.OfferPrice.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CounterpartyOfferPrice", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMarket + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMarket + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMarket + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.CounterpartyOfferPrice == nil { + m.CounterpartyOfferPrice = &types.Coin{} + } + if err := m.CounterpartyOfferPrice.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipMarket(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthMarket + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ReverseLookupBuyOrderIds) 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 ErrIntOverflowMarket + } + 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: ReverseLookupBuyOrderIds: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ReverseLookupBuyOrderIds: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field OrderIds", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMarket + } + 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 ErrInvalidLengthMarket + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthMarket + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.OrderIds = append(m.OrderIds, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipMarket(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthMarket + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipMarket(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, ErrIntOverflowMarket + } + 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, ErrIntOverflowMarket + } + 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, ErrIntOverflowMarket + } + 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, ErrInvalidLengthMarket + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupMarket + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthMarket + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthMarket = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowMarket = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupMarket = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/dymns/types/market_test.go b/x/dymns/types/market_test.go new file mode 100644 index 000000000..1014e8eba --- /dev/null +++ b/x/dymns/types/market_test.go @@ -0,0 +1,97 @@ +package types + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestValidateOrderParams(t *testing.T) { + tests := []struct { + name string + params []string + assetType AssetType + wantErr bool + wantErrContains string + }{ + { + name: "pass - no params for Dym-Name order", + params: nil, + assetType: TypeName, + wantErr: false, + }, + { + name: "pass - no params for Dym-Name order", + params: []string{}, + assetType: TypeName, + wantErr: false, + }, + { + name: "fail - reject if has params for Dym-Name order", + params: []string{"one"}, + assetType: TypeName, + wantErr: true, + wantErrContains: "not accept order params for asset type:", + }, + { + name: "fail - reject if has params for Dym-Name order", + params: []string{"one", "two", "three"}, + assetType: TypeName, + wantErr: true, + wantErrContains: "not accept order params for asset type:", + }, + { + name: "pass - one params for Alias order", + params: []string{"rollapp_1-1"}, + assetType: TypeAlias, + wantErr: false, + }, + { + name: "fail - reject empty params for Alias order", + params: nil, + assetType: TypeAlias, + wantErr: true, + wantErrContains: "expect 1 order param of RollApp ID for asset type:", + }, + { + name: "fail - reject empty params for Alias order", + params: []string{}, + assetType: TypeAlias, + wantErr: true, + wantErrContains: "expect 1 order param of RollApp ID for asset type:", + }, + { + name: "fail - reject bad chain-id as params for Alias order", + params: []string{"@bad"}, + assetType: TypeAlias, + wantErr: true, + wantErrContains: "invalid RollApp ID format:", + }, + { + name: "fail - reject bad chain-id as params for Alias order", + params: []string{""}, + assetType: TypeAlias, + wantErr: true, + wantErrContains: "invalid RollApp ID format:", + }, + { + name: "fail - reject unknown asset type", + assetType: AssetType_AT_UNKNOWN, + wantErr: true, + wantErrContains: "unknown asset type:", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := ValidateOrderParams(tt.params, tt.assetType) + if tt.wantErr { + require.NotEmpty(t, tt.wantErrContains, "mis-configured test case") + require.Error(t, err) + require.Contains(t, err.Error(), tt.wantErrContains) + return + } + + require.NoError(t, err) + }) + } +} diff --git a/x/dymns/types/msg_accept_buy_order.go b/x/dymns/types/msg_accept_buy_order.go new file mode 100644 index 000000000..67b90d0b0 --- /dev/null +++ b/x/dymns/types/msg_accept_buy_order.go @@ -0,0 +1,53 @@ +package types + +import ( + errorsmod "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/dymensionxyz/gerr-cosmos/gerrc" +) + +var _ sdk.Msg = &MsgAcceptBuyOrder{} + +// ValidateBasic performs basic validation for the MsgAcceptBuyOrder. +func (m *MsgAcceptBuyOrder) ValidateBasic() error { + if !IsValidBuyOrderId(m.OrderId) { + return errorsmod.Wrap(gerrc.ErrInvalidArgument, "offer id is not a valid buy name offer id") + } + + if _, err := sdk.AccAddressFromBech32(m.Owner); err != nil { + return errorsmod.Wrap(gerrc.ErrInvalidArgument, "owner is not a valid bech32 account address") + } + + if !m.MinAccept.IsValid() { + return errorsmod.Wrap(gerrc.ErrInvalidArgument, "invalid min-accept amount") + } else if !m.MinAccept.IsPositive() { + return errorsmod.Wrap(gerrc.ErrInvalidArgument, "min-accept amount must be positive") + } + + return nil +} + +// GetSigners returns the required signers for the MsgAcceptBuyOrder. +func (m *MsgAcceptBuyOrder) GetSigners() []sdk.AccAddress { + owner, err := sdk.AccAddressFromBech32(m.Owner) + if err != nil { + panic(err) + } + return []sdk.AccAddress{owner} +} + +// Route returns the message router key for the MsgAcceptBuyOrder. +func (m *MsgAcceptBuyOrder) Route() string { + return RouterKey +} + +// Type returns the message type for the MsgAcceptBuyOrder. +func (m *MsgAcceptBuyOrder) Type() string { + return TypeMsgAcceptBuyOrder +} + +// GetSignBytes returns the raw bytes for the MsgAcceptBuyOrder. +func (m *MsgAcceptBuyOrder) GetSignBytes() []byte { + bz := ModuleCdc.MustMarshalJSON(m) + return sdk.MustSortJSON(bz) +} diff --git a/x/dymns/types/msg_accept_buy_order_test.go b/x/dymns/types/msg_accept_buy_order_test.go new file mode 100644 index 000000000..ff4b49bdf --- /dev/null +++ b/x/dymns/types/msg_accept_buy_order_test.go @@ -0,0 +1,91 @@ +package types + +import ( + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/dymensionxyz/dymension/v3/app/params" + "github.com/stretchr/testify/require" +) + +//goland:noinspection SpellCheckingInspection +func TestMsgAcceptBuyOrder_ValidateBasic(t *testing.T) { + tests := []struct { + name string + buyOrderId string + owner string + minAccept sdk.Coin + wantErr bool + wantErrContains string + }{ + { + name: "pass - valid", + buyOrderId: "101", + owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + minAccept: testCoin(1), + wantErr: false, + }, + { + name: "fail - reject bad offer id", + buyOrderId: "@", + owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + minAccept: testCoin(1), + wantErr: true, + wantErrContains: "offer id is not a valid buy name offer id", + }, + { + name: "fail - reject bad owner", + buyOrderId: "101", + owner: "x", + minAccept: testCoin(1), + wantErr: true, + wantErrContains: "owner is not a valid bech32 account address", + }, + { + name: "fail - reject empty coin", + buyOrderId: "101", + owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + minAccept: sdk.Coin{}, + wantErr: true, + wantErrContains: "invalid min-accept amount", + }, + { + name: "fail - reject zero coin", + buyOrderId: "101", + owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + minAccept: testCoin(0), + wantErr: true, + wantErrContains: "min-accept amount must be positive", + }, + { + name: "fail - reject negative coin", + buyOrderId: "101", + owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + minAccept: sdk.Coin{ + Denom: params.BaseDenom, + Amount: sdk.NewInt(-1), + }, + wantErr: true, + wantErrContains: "invalid min-accept amount", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + m := &MsgAcceptBuyOrder{ + OrderId: tt.buyOrderId, + Owner: tt.owner, + MinAccept: tt.minAccept, + } + + err := m.ValidateBasic() + if tt.wantErr { + require.NotEmpty(t, tt.wantErrContains, "mis-configured test case") + require.Error(t, err) + require.Contains(t, err.Error(), tt.wantErrContains) + return + } + + require.NoError(t, err) + }) + } +} diff --git a/x/dymns/types/msg_cancel_buy_order.go b/x/dymns/types/msg_cancel_buy_order.go new file mode 100644 index 000000000..65a17ba52 --- /dev/null +++ b/x/dymns/types/msg_cancel_buy_order.go @@ -0,0 +1,47 @@ +package types + +import ( + errorsmod "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/dymensionxyz/gerr-cosmos/gerrc" +) + +var _ sdk.Msg = &MsgCancelBuyOrder{} + +// ValidateBasic performs basic validation for the MsgCancelBuyOrder. +func (m *MsgCancelBuyOrder) ValidateBasic() error { + if !IsValidBuyOrderId(m.OrderId) { + return errorsmod.Wrap(gerrc.ErrInvalidArgument, "offer id is not a valid buy name offer id") + } + + if _, err := sdk.AccAddressFromBech32(m.Buyer); err != nil { + return errorsmod.Wrap(gerrc.ErrInvalidArgument, "buyer is not a valid bech32 account address") + } + + return nil +} + +// GetSigners returns the required signers for the MsgCancelBuyOrder. +func (m *MsgCancelBuyOrder) GetSigners() []sdk.AccAddress { + buyer, err := sdk.AccAddressFromBech32(m.Buyer) + if err != nil { + panic(err) + } + return []sdk.AccAddress{buyer} +} + +// Route returns the message router key for the MsgCancelBuyOrder. +func (m *MsgCancelBuyOrder) Route() string { + return RouterKey +} + +// Type returns the message type for the MsgCancelBuyOrder. +func (m *MsgCancelBuyOrder) Type() string { + return TypeMsgCancelBuyOrder +} + +// GetSignBytes returns the raw bytes for the MsgCancelBuyOrder. +func (m *MsgCancelBuyOrder) GetSignBytes() []byte { + bz := ModuleCdc.MustMarshalJSON(m) + return sdk.MustSortJSON(bz) +} diff --git a/x/dymns/types/msg_cancel_buy_order_test.go b/x/dymns/types/msg_cancel_buy_order_test.go new file mode 100644 index 000000000..26829d5ee --- /dev/null +++ b/x/dymns/types/msg_cancel_buy_order_test.go @@ -0,0 +1,57 @@ +package types + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +//goland:noinspection SpellCheckingInspection +func TestMsgCancelBuyOrder_ValidateBasic(t *testing.T) { + tests := []struct { + name string + orderId string + buyer string + wantErr bool + wantErrContains string + }{ + { + name: "pass - valid", + orderId: "101", + buyer: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + wantErr: false, + }, + { + name: "fail - bad offer id", + orderId: "@", + buyer: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + wantErr: true, + wantErrContains: "offer id is not a valid buy name offer id", + }, + { + name: "fail - bad buyer", + orderId: "101", + buyer: "dym1fl48vsnmsdzcv85", + wantErr: true, + wantErrContains: "buyer is not a valid bech32 account address", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + m := &MsgCancelBuyOrder{ + OrderId: tt.orderId, + Buyer: tt.buyer, + } + + err := m.ValidateBasic() + if tt.wantErr { + require.NotEmpty(t, tt.wantErrContains, "mis-configured test case") + require.Error(t, err) + require.Contains(t, err.Error(), tt.wantErrContains) + return + } + + require.NoError(t, err) + }) + } +} diff --git a/x/dymns/types/msg_cancel_sell_order.go b/x/dymns/types/msg_cancel_sell_order.go new file mode 100644 index 000000000..cd4b1048b --- /dev/null +++ b/x/dymns/types/msg_cancel_sell_order.go @@ -0,0 +1,56 @@ +package types + +import ( + errorsmod "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + dymnsutils "github.com/dymensionxyz/dymension/v3/x/dymns/utils" + "github.com/dymensionxyz/gerr-cosmos/gerrc" +) + +var _ sdk.Msg = &MsgCancelSellOrder{} + +// ValidateBasic performs basic validation for the MsgCancelSellOrder. +func (m *MsgCancelSellOrder) ValidateBasic() error { + if m.AssetType == TypeName { + if !dymnsutils.IsValidDymName(m.AssetId) { + return errorsmod.Wrapf(gerrc.ErrInvalidArgument, "name is not a valid dym name: %s", m.AssetId) + } + } else if m.AssetType == TypeAlias { + if !dymnsutils.IsValidAlias(m.AssetId) { + return errorsmod.Wrapf(gerrc.ErrInvalidArgument, "alias is not a valid alias: %s", m.AssetId) + } + } else { + return errorsmod.Wrapf(gerrc.ErrInvalidArgument, "invalid asset type: %s", m.AssetType) + } + + if _, err := sdk.AccAddressFromBech32(m.Owner); err != nil { + return errorsmod.Wrap(gerrc.ErrInvalidArgument, "owner is not a valid bech32 account address") + } + + return nil +} + +// GetSigners returns the required signers for the MsgCancelSellOrder. +func (m *MsgCancelSellOrder) GetSigners() []sdk.AccAddress { + owner, err := sdk.AccAddressFromBech32(m.Owner) + if err != nil { + panic(err) + } + return []sdk.AccAddress{owner} +} + +// Route returns the message router key for the MsgCancelSellOrder. +func (m *MsgCancelSellOrder) Route() string { + return RouterKey +} + +// Type returns the message type for the MsgCancelSellOrder. +func (m *MsgCancelSellOrder) Type() string { + return TypeMsgCancelSellOrder +} + +// GetSignBytes returns the raw bytes for the MsgCancelSellOrder. +func (m *MsgCancelSellOrder) GetSignBytes() []byte { + bz := ModuleCdc.MustMarshalJSON(m) + return sdk.MustSortJSON(bz) +} diff --git a/x/dymns/types/msg_cancel_sell_order_test.go b/x/dymns/types/msg_cancel_sell_order_test.go new file mode 100644 index 000000000..df2cbf590 --- /dev/null +++ b/x/dymns/types/msg_cancel_sell_order_test.go @@ -0,0 +1,114 @@ +package types + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestMsgCancelSellOrder_ValidateBasic(t *testing.T) { + //goland:noinspection SpellCheckingInspection + tests := []struct { + name string + assetId string + assetType AssetType + owner string + wantErr bool + wantErrContains string + }{ + { + name: "pass - (Name) valid", + assetId: "my-name", + assetType: TypeName, + owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + }, + { + name: "pass - (Alias) valid", + assetId: "alias", + assetType: TypeAlias, + owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + }, + { + name: "fail - (Name) not allow empty name", + assetId: "", + assetType: TypeName, + owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + wantErr: true, + wantErrContains: "name is not a valid dym name", + }, + { + name: "fail - (Alias) not allow empty alias", + assetId: "", + assetType: TypeAlias, + owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + wantErr: true, + wantErrContains: "alias is not a valid alias", + }, + { + name: "fail - (Name) not allow invalid name", + assetId: "-my-name", + assetType: TypeName, + owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + wantErr: true, + wantErrContains: "name is not a valid dym name", + }, + { + name: "fail - (Alias) not allow invalid alias", + assetId: "bad-alias", + assetType: TypeAlias, + owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + wantErr: true, + wantErrContains: "alias is not a valid alias", + }, + { + name: "fail - invalid owner", + assetId: "my-name", + assetType: TypeName, + owner: "dym1fl48vsnmsdzcv85q5", + wantErr: true, + wantErrContains: "owner is not a valid bech32 account address", + }, + { + name: "fail - missing owner", + assetId: "my-name", + assetType: TypeName, + owner: "", + wantErr: true, + wantErrContains: "owner is not a valid bech32 account address", + }, + { + name: "fail - owner must be dym1", + assetId: "my-name", + assetType: TypeName, + owner: "nim1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3pklgjx", + wantErr: true, + wantErrContains: "owner is not a valid bech32 account address", + }, + { + name: "fail - not supported asset type", + assetId: "asset", + assetType: AssetType_AT_UNKNOWN, + owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + wantErr: true, + wantErrContains: "invalid asset type", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + m := &MsgCancelSellOrder{ + AssetId: tt.assetId, + AssetType: tt.assetType, + Owner: tt.owner, + } + + err := m.ValidateBasic() + if tt.wantErr { + require.NotEmpty(t, tt.wantErrContains, "mis-configured test case") + require.Error(t, err) + require.Contains(t, err.Error(), tt.wantErrContains) + } else { + require.NoError(t, err) + } + }) + } +} diff --git a/x/dymns/types/msg_place_buy_order.go b/x/dymns/types/msg_place_buy_order.go new file mode 100644 index 000000000..e938bcffb --- /dev/null +++ b/x/dymns/types/msg_place_buy_order.go @@ -0,0 +1,72 @@ +package types + +import ( + errorsmod "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + dymnsutils "github.com/dymensionxyz/dymension/v3/x/dymns/utils" + "github.com/dymensionxyz/gerr-cosmos/gerrc" +) + +var _ sdk.Msg = &MsgPlaceBuyOrder{} + +// ValidateBasic performs basic validation for the MsgPlaceBuyOrder. +func (m *MsgPlaceBuyOrder) ValidateBasic() error { + if m.AssetType == TypeName { + if !dymnsutils.IsValidDymName(m.AssetId) { + return errorsmod.Wrapf(gerrc.ErrInvalidArgument, "name is not a valid dym name: %s", m.AssetId) + } + } else if m.AssetType == TypeAlias { + if !dymnsutils.IsValidAlias(m.AssetId) { + return errorsmod.Wrapf(gerrc.ErrInvalidArgument, "alias is not a valid alias: %s", m.AssetId) + } + } else { + return errorsmod.Wrapf(gerrc.ErrInvalidArgument, "invalid asset type: %s", m.AssetType) + } + + if err := ValidateOrderParams(m.Params, m.AssetType); err != nil { + return err + } + + if _, err := sdk.AccAddressFromBech32(m.Buyer); err != nil { + return errorsmod.Wrap(gerrc.ErrInvalidArgument, "buyer is not a valid bech32 account address") + } + + if m.ContinueOrderId != "" && !IsValidBuyOrderId(m.ContinueOrderId) { + return errorsmod.Wrapf(gerrc.ErrInvalidArgument, + "continue offer id is not a valid offer id: %s", m.ContinueOrderId, + ) + } + + if !m.Offer.IsValid() { + return errorsmod.Wrap(gerrc.ErrInvalidArgument, "invalid offer amount") + } else if !m.Offer.IsPositive() { + return errorsmod.Wrap(gerrc.ErrInvalidArgument, "offer amount must be positive") + } + + return nil +} + +// GetSigners returns the required signers for the MsgPlaceBuyOrder. +func (m *MsgPlaceBuyOrder) GetSigners() []sdk.AccAddress { + buyer, err := sdk.AccAddressFromBech32(m.Buyer) + if err != nil { + panic(err) + } + return []sdk.AccAddress{buyer} +} + +// Route returns the message router key for the MsgPlaceBuyOrder. +func (m *MsgPlaceBuyOrder) Route() string { + return RouterKey +} + +// Type returns the message type for the MsgPlaceBuyOrder. +func (m *MsgPlaceBuyOrder) Type() string { + return TypeMsgPlaceBuyOrder +} + +// GetSignBytes returns the raw bytes for the MsgPlaceBuyOrder. +func (m *MsgPlaceBuyOrder) GetSignBytes() []byte { + bz := ModuleCdc.MustMarshalJSON(m) + return sdk.MustSortJSON(bz) +} diff --git a/x/dymns/types/msg_place_buy_order_test.go b/x/dymns/types/msg_place_buy_order_test.go new file mode 100644 index 000000000..08d6ca20f --- /dev/null +++ b/x/dymns/types/msg_place_buy_order_test.go @@ -0,0 +1,194 @@ +package types + +import ( + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/dymensionxyz/dymension/v3/app/params" + "github.com/stretchr/testify/require" +) + +//goland:noinspection SpellCheckingInspection +func TestMsgPlaceBuyOrder_ValidateBasic(t *testing.T) { + tests := []struct { + name string + assetId string + assetType AssetType + params []string + buyer string + continueOrderId string + offer sdk.Coin + wantErr bool + wantErrContains string + }{ + { + name: "pass - (Name) valid", + assetId: "my-name", + assetType: TypeName, + params: nil, + buyer: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + continueOrderId: "", + offer: testCoin(1), + wantErr: false, + }, + { + name: "pass - (Alias) valid", + assetId: "alias", + assetType: TypeAlias, + params: []string{"rollapp_1-1"}, + buyer: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + continueOrderId: "", + offer: testCoin(1), + wantErr: false, + }, + { + name: "pass - (Name) valid, continue offer", + assetId: "my-name", + assetType: TypeName, + buyer: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + continueOrderId: "101", + offer: testCoin(1), + wantErr: false, + }, + { + name: "pass - (Alias) valid, continue offer", + assetId: "alias", + assetType: TypeAlias, + params: []string{"rollapp_1-1"}, + buyer: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + continueOrderId: "101", + offer: testCoin(1), + wantErr: false, + }, + { + name: "fail - (Name) reject bad Dym-Name format", + assetId: "@", + assetType: TypeName, + buyer: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + offer: testCoin(1), + wantErr: true, + wantErrContains: "name is not a valid dym name", + }, + { + name: "fail - (Alias) reject bad alias format", + assetId: "bad-alias", + assetType: TypeAlias, + params: []string{"rollapp_1-1"}, + buyer: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + offer: testCoin(1), + wantErr: true, + wantErrContains: "alias is not a valid alias", + }, + { + name: "fail - (Name) reject bad params", + assetId: "my-name", + assetType: TypeName, + params: []string{"not-empty"}, + buyer: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + offer: testCoin(1), + wantErr: true, + wantErrContains: "not accept order params for asset type", + }, + { + name: "fail - (Alias) reject empty params", + assetId: "alias", + assetType: TypeAlias, + params: nil, + buyer: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + offer: testCoin(1), + wantErr: true, + wantErrContains: "expect 1 order param of RollApp ID for asset type", + }, + { + name: "fail - (Alias) reject bad params", + assetId: "alias", + assetType: TypeAlias, + params: []string{"@chain-id"}, + buyer: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + offer: testCoin(1), + wantErr: true, + wantErrContains: "invalid RollApp ID format", + }, + { + name: "fail - bad buyer", + assetId: "my-name", + assetType: TypeName, + buyer: "dym1fl48vsnmsdzcv85", + offer: testCoin(1), + wantErr: true, + wantErrContains: "buyer is not a valid bech32 account address", + }, + { + name: "fail - offer ID", + assetId: "my-name", + assetType: TypeName, + buyer: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + continueOrderId: "@", + offer: testCoin(1), + wantErr: true, + wantErrContains: "continue offer id is not a valid offer id", + }, + { + name: "fail - empty offer", + assetId: "my-name", + assetType: TypeName, + buyer: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + offer: sdk.Coin{}, + wantErr: true, + wantErrContains: "invalid offer amount", + }, + { + name: "fail - zero offer", + assetId: "my-name", + assetType: TypeName, + buyer: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + offer: testCoin(0), + wantErr: true, + wantErrContains: "offer amount must be positive", + }, + { + name: "fail - negative offer", + assetId: "my-name", + assetType: TypeName, + buyer: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + offer: sdk.Coin{ + Denom: params.BaseDenom, + Amount: sdk.NewInt(-1), + }, + wantErr: true, + wantErrContains: "invalid offer amount", + }, + { + name: "fail - reject unknown asset type", + assetId: "asset", + assetType: AssetType_AT_UNKNOWN, + buyer: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + offer: testCoin(1), + wantErr: true, + wantErrContains: "invalid asset type", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + m := &MsgPlaceBuyOrder{ + AssetId: tt.assetId, + AssetType: tt.assetType, + Params: tt.params, + Buyer: tt.buyer, + ContinueOrderId: tt.continueOrderId, + Offer: tt.offer, + } + + err := m.ValidateBasic() + + if tt.wantErr { + require.NotEmpty(t, tt.wantErrContains, "mis-configured test case") + require.Error(t, err) + require.Contains(t, err.Error(), tt.wantErrContains) + return + } + + require.NoError(t, err) + }) + } +} diff --git a/x/dymns/types/msg_place_sell_order.go b/x/dymns/types/msg_place_sell_order.go new file mode 100644 index 000000000..474e2b764 --- /dev/null +++ b/x/dymns/types/msg_place_sell_order.go @@ -0,0 +1,82 @@ +package types + +import ( + errorsmod "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + dymnsutils "github.com/dymensionxyz/dymension/v3/x/dymns/utils" + "github.com/dymensionxyz/gerr-cosmos/gerrc" +) + +var _ sdk.Msg = &MsgPlaceSellOrder{} + +// ValidateBasic performs basic validation for the MsgPlaceSellOrder. +func (m *MsgPlaceSellOrder) ValidateBasic() error { + if m.AssetType == TypeName { + if !dymnsutils.IsValidDymName(m.AssetId) { + return errorsmod.Wrapf(gerrc.ErrInvalidArgument, "name is not a valid dym name: %s", m.AssetId) + } + } else if m.AssetType == TypeAlias { + if !dymnsutils.IsValidAlias(m.AssetId) { + return errorsmod.Wrapf(gerrc.ErrInvalidArgument, "alias is not a valid alias: %s", m.AssetId) + } + } else { + return errorsmod.Wrapf(gerrc.ErrInvalidArgument, "invalid asset type: %s", m.AssetType) + } + + so := m.ToSellOrder() + + // put a dummy expire at to validate, as zero expire at is invalid, + // and we don't have context of time at this point + so.ExpireAt = 1 + + if err := so.Validate(); err != nil { + return errorsmod.Wrapf(gerrc.ErrInvalidArgument, "invalid order: %v", err) + } + + if _, err := sdk.AccAddressFromBech32(m.Owner); err != nil { + return errorsmod.Wrap(gerrc.ErrInvalidArgument, "owner is not a valid bech32 account address") + } + + return nil +} + +// ToSellOrder converts the MsgPlaceSellOrder to a SellOrder. +func (m *MsgPlaceSellOrder) ToSellOrder() SellOrder { + so := SellOrder{ + AssetId: m.AssetId, + AssetType: m.AssetType, + MinPrice: m.MinPrice, + SellPrice: m.SellPrice, + } + + if !so.HasSetSellPrice() { + so.SellPrice = nil + } + + return so +} + +// GetSigners returns the required signers for the MsgPlaceSellOrder. +func (m *MsgPlaceSellOrder) GetSigners() []sdk.AccAddress { + owner, err := sdk.AccAddressFromBech32(m.Owner) + if err != nil { + panic(err) + } + return []sdk.AccAddress{owner} +} + +// Route returns the message router key for the MsgPlaceSellOrder. +func (m *MsgPlaceSellOrder) Route() string { + return RouterKey +} + +// Type returns the message type for the MsgPlaceSellOrder. +func (m *MsgPlaceSellOrder) Type() string { + return TypeMsgPlaceSellOrder +} + +// GetSignBytes returns the raw bytes for the MsgPlaceSellOrder. +func (m *MsgPlaceSellOrder) GetSignBytes() []byte { + bz := ModuleCdc.MustMarshalJSON(m) + return sdk.MustSortJSON(bz) +} diff --git a/x/dymns/types/msg_place_sell_order_test.go b/x/dymns/types/msg_place_sell_order_test.go new file mode 100644 index 000000000..5fe744e1d --- /dev/null +++ b/x/dymns/types/msg_place_sell_order_test.go @@ -0,0 +1,337 @@ +package types + +import ( + "testing" + + "github.com/dymensionxyz/sdk-utils/utils/uptr" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/dymensionxyz/dymension/v3/app/params" + "github.com/stretchr/testify/require" +) + +func TestMsgPlaceSellOrder_ValidateBasic(t *testing.T) { + //goland:noinspection SpellCheckingInspection + tests := []struct { + name string + assetId string + assetType AssetType + minPrice sdk.Coin + sellPrice *sdk.Coin + owner string + wantErr bool + wantErrContains string + }{ + { + name: "pass - (Name) valid sell order", + assetId: "my-name", + assetType: TypeName, + minPrice: testCoin(1), + sellPrice: uptr.To(testCoin(1)), + owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + }, + { + name: "pass - (Alias) valid sell order", + assetId: "alias", + assetType: TypeAlias, + minPrice: testCoin(1), + sellPrice: uptr.To(testCoin(1)), + owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + }, + { + name: "pass - (Name) valid sell order without bid", + assetId: "my-name", + assetType: TypeName, + minPrice: testCoin(1), + sellPrice: uptr.To(testCoin(1)), + owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + }, + { + name: "pass - (Alias) valid sell order without bid", + assetId: "alias", + assetType: TypeAlias, + minPrice: testCoin(1), + sellPrice: uptr.To(testCoin(1)), + owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + }, + { + name: "pass - (Name) valid sell order without setting sell price", + assetId: "my-name", + assetType: TypeName, + minPrice: testCoin(1), + owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + }, + { + name: "pass - (Alias) valid sell order without setting sell price", + assetId: "alias", + assetType: TypeAlias, + minPrice: testCoin(1), + owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + }, + { + name: "fail - (Name) empty name", + assetId: "", + assetType: TypeName, + minPrice: testCoin(1), + owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + wantErr: true, + wantErrContains: "name is not a valid dym name", + }, + { + name: "fail - (Alias) empty alias", + assetId: "", + assetType: TypeAlias, + minPrice: testCoin(1), + owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + wantErr: true, + wantErrContains: "alias is not a valid alias", + }, + { + name: "fail - (Name) bad name", + assetId: "-my-name", + assetType: TypeName, + minPrice: testCoin(1), + owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + wantErr: true, + wantErrContains: "name is not a valid dym name", + }, + { + name: "fail - (Alias) bad alias", + assetId: "bad-alias", + assetType: TypeAlias, + minPrice: testCoin(1), + owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + wantErr: true, + wantErrContains: "alias is not a valid alias", + }, + { + name: "fail - min price is zero", + assetId: "my-name", + assetType: TypeName, + minPrice: testCoin(0), + owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + wantErr: true, + wantErrContains: "SO min price is zero", + }, + { + name: "fail - min price is empty", + assetId: "my-name", + assetType: TypeName, + minPrice: sdk.Coin{}, + owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + wantErr: true, + wantErrContains: "SO min price is zero", + }, + { + name: "fail - min price is negative", + assetId: "my-name", + assetType: TypeName, + minPrice: sdk.Coin{ + Denom: params.BaseDenom, + Amount: sdk.NewInt(-1), + }, + owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + wantErr: true, + wantErrContains: "SO min price is negative", + }, + { + name: "fail - min price is invalid", + assetId: "my-name", + assetType: TypeName, + minPrice: sdk.Coin{ + Denom: "-", + Amount: sdk.OneInt(), + }, + owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + wantErr: true, + wantErrContains: "SO min price is invalid", + }, + { + name: "fail - sell price is negative", + assetId: "my-name", + assetType: TypeName, + minPrice: testCoin(1), + sellPrice: uptr.To(testCoin(-1)), + owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + wantErr: true, + wantErrContains: "SO sell price is negative", + }, + { + name: "fail - sell price is invalid", + assetId: "my-name", + assetType: TypeName, + minPrice: testCoin(1), + sellPrice: &sdk.Coin{ + Denom: "-", + Amount: sdk.OneInt(), + }, + owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + wantErr: true, + wantErrContains: "SO sell price is invalid", + }, + { + name: "fail - sell price is less than min price", + assetId: "my-name", + assetType: TypeName, + minPrice: testCoin(2), + sellPrice: uptr.To(testCoin(1)), + owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + wantErr: true, + wantErrContains: "SO sell price is less than min price", + }, + { + name: "fail - sell price denom must match min price denom", + assetId: "my-name", + assetType: TypeName, + minPrice: testCoin(2), + sellPrice: uptr.To(sdk.NewCoin("u"+params.BaseDenom, sdk.OneInt())), + owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + wantErr: true, + wantErrContains: "SO sell price denom is different from min price denom", + }, + { + name: "fail - missing owner", + assetId: "my-name", + assetType: TypeName, + minPrice: testCoin(2), + owner: "", + wantErr: true, + wantErrContains: "owner is not a valid bech32 account address", + }, + { + name: "fail - invalid owner", + assetId: "my-name", + assetType: TypeName, + minPrice: testCoin(2), + owner: "dym1fl48vsnmsdzcv85", + wantErr: true, + wantErrContains: "owner is not a valid bech32 account address", + }, + { + name: "fail - owner must be dym1", + assetId: "my-name", + assetType: TypeName, + minPrice: testCoin(2), + owner: "nim1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3pklgjx", + wantErr: true, + wantErrContains: "owner is not a valid bech32 account address", + }, + { + name: "fail - reject unknown asset type", + assetId: "asset", + assetType: AssetType_AT_UNKNOWN, + minPrice: testCoin(2), + owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + wantErr: true, + wantErrContains: "invalid asset type", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + m := &MsgPlaceSellOrder{ + AssetId: tt.assetId, + AssetType: tt.assetType, + MinPrice: tt.minPrice, + SellPrice: tt.sellPrice, + Owner: tt.owner, + } + + err := m.ValidateBasic() + if tt.wantErr { + require.NotEmpty(t, tt.wantErrContains, "mis-configured test case") + require.Error(t, err) + require.Contains(t, err.Error(), tt.wantErrContains) + } else { + require.NoError(t, err) + } + }) + } +} + +func TestMsgPlaceSellOrder_ToSellOrder(t *testing.T) { + validMinPrice := testCoin(1) + validSellPrice := testCoin(1) + + tests := []struct { + name string + assetId string + assetType AssetType + minPrice sdk.Coin + sellPrice *sdk.Coin + Owner string + want SellOrder + }{ + { + name: "normal Dym-Name sell order", + assetId: "my-name", + assetType: TypeName, + minPrice: validMinPrice, + sellPrice: &validSellPrice, + Owner: "", + want: SellOrder{ + AssetId: "my-name", + AssetType: TypeName, + MinPrice: validMinPrice, + SellPrice: &validSellPrice, + }, + }, + { + name: "normal Alias sell order", + assetId: "alias", + assetType: TypeAlias, + minPrice: validMinPrice, + sellPrice: &validSellPrice, + Owner: "", + want: SellOrder{ + AssetId: "alias", + AssetType: TypeAlias, + MinPrice: validMinPrice, + SellPrice: &validSellPrice, + }, + }, + { + name: "without sell price", + assetId: "my-name", + assetType: TypeName, + minPrice: validMinPrice, + sellPrice: nil, + Owner: "", + want: SellOrder{ + AssetId: "my-name", + AssetType: TypeName, + MinPrice: validMinPrice, + SellPrice: nil, + }, + }, + { + name: "without sell price, auto omit zero sell price", + assetId: "my-name", + assetType: TypeName, + minPrice: validMinPrice, + sellPrice: uptr.To(sdk.NewCoin(validMinPrice.Denom, sdk.ZeroInt())), + Owner: "", + want: SellOrder{ + AssetId: "my-name", + AssetType: TypeName, + MinPrice: validMinPrice, + SellPrice: nil, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + m := &MsgPlaceSellOrder{ + AssetId: tt.assetId, + AssetType: tt.assetType, + MinPrice: tt.minPrice, + SellPrice: tt.sellPrice, + Owner: tt.Owner, + } + + so := m.ToSellOrder() + require.Equal(t, tt.want, so) + require.Zero(t, so.ExpireAt) + require.Nil(t, so.HighestBid) + }) + } +} diff --git a/x/dymns/types/msg_purchase_order.go b/x/dymns/types/msg_purchase_order.go new file mode 100644 index 000000000..eff8d0059 --- /dev/null +++ b/x/dymns/types/msg_purchase_order.go @@ -0,0 +1,66 @@ +package types + +import ( + errorsmod "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + dymnsutils "github.com/dymensionxyz/dymension/v3/x/dymns/utils" + "github.com/dymensionxyz/gerr-cosmos/gerrc" +) + +var _ sdk.Msg = &MsgPurchaseOrder{} + +// ValidateBasic performs basic validation for the MsgPurchaseOrder. +func (m *MsgPurchaseOrder) ValidateBasic() error { + if m.AssetType == TypeName { + if !dymnsutils.IsValidDymName(m.AssetId) { + return errorsmod.Wrapf(gerrc.ErrInvalidArgument, "name is not a valid dym name: %s", m.AssetId) + } + } else if m.AssetType == TypeAlias { + if !dymnsutils.IsValidAlias(m.AssetId) { + return errorsmod.Wrapf(gerrc.ErrInvalidArgument, "alias is not a valid alias: %s", m.AssetId) + } + } else { + return errorsmod.Wrapf(gerrc.ErrInvalidArgument, "invalid asset type: %s", m.AssetType) + } + + if err := ValidateOrderParams(m.Params, m.AssetType); err != nil { + return err + } + + if !m.Offer.IsValid() { + return errorsmod.Wrap(gerrc.ErrInvalidArgument, "invalid offer") + } else if !m.Offer.IsPositive() { + return errorsmod.Wrap(gerrc.ErrInvalidArgument, "offer must be positive") + } + + if _, err := sdk.AccAddressFromBech32(m.Buyer); err != nil { + return errorsmod.Wrap(gerrc.ErrInvalidArgument, "buyer is not a valid bech32 account address") + } + + return nil +} + +// GetSigners returns the required signers for the MsgPurchaseOrder. +func (m *MsgPurchaseOrder) GetSigners() []sdk.AccAddress { + buyer, err := sdk.AccAddressFromBech32(m.Buyer) + if err != nil { + panic(err) + } + return []sdk.AccAddress{buyer} +} + +// Route returns the message router key for the MsgPurchaseOrder. +func (m *MsgPurchaseOrder) Route() string { + return RouterKey +} + +// Type returns the message type for the MsgPurchaseOrder. +func (m *MsgPurchaseOrder) Type() string { + return TypeMsgPurchaseOrder +} + +// GetSignBytes returns the raw bytes for the MsgPurchaseOrder. +func (m *MsgPurchaseOrder) GetSignBytes() []byte { + bz := ModuleCdc.MustMarshalJSON(m) + return sdk.MustSortJSON(bz) +} diff --git a/x/dymns/types/msg_purchase_order_test.go b/x/dymns/types/msg_purchase_order_test.go new file mode 100644 index 000000000..1ba218fe1 --- /dev/null +++ b/x/dymns/types/msg_purchase_order_test.go @@ -0,0 +1,214 @@ +package types + +import ( + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/dymensionxyz/dymension/v3/app/params" + "github.com/stretchr/testify/require" +) + +func TestMsgPurchaseOrder_ValidateBasic(t *testing.T) { + validOffer := testCoin(100) + + //goland:noinspection SpellCheckingInspection + tests := []struct { + name string + assetId string + assetType AssetType + params []string + offer sdk.Coin + buyer string + wantErr bool + wantErrContains string + }{ + { + name: "pass - (Name) valid", + assetId: "my-name", + assetType: TypeName, + offer: validOffer, + buyer: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + }, + { + name: "pass - (Alias) valid", + assetId: "alias", + assetType: TypeAlias, + params: []string{"rollapp_1-1"}, + offer: validOffer, + buyer: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + }, + { + name: "fail - (Name) reject empty name", + assetId: "", + assetType: TypeName, + offer: validOffer, + buyer: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + wantErr: true, + wantErrContains: "name is not a valid dym name", + }, + { + name: "fail - (Alias) reject empty alias", + assetId: "", + assetType: TypeAlias, + params: []string{"rollapp_1-1"}, + offer: validOffer, + buyer: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + wantErr: true, + wantErrContains: "alias is not a valid alias", + }, + { + name: "fail - (Name) bad name", + assetId: "-my-name", + assetType: TypeName, + offer: validOffer, + buyer: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + wantErr: true, + wantErrContains: "name is not a valid dym name", + }, + { + name: "fail - (Alias) bad alias", + assetId: "bad-alias", + assetType: TypeAlias, + params: []string{"rollapp_1-1"}, + offer: validOffer, + buyer: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + wantErr: true, + wantErrContains: "alias is not a valid alias", + }, + { + name: "fail - (Name) reject non-empty params", + assetId: "my-name", + assetType: TypeName, + params: []string{"one"}, + offer: validOffer, + buyer: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + wantErr: true, + wantErrContains: "not accept order params for asset type", + }, + { + name: "fail - (Alias) reject empty params", + assetId: "alias", + assetType: TypeAlias, + params: nil, + offer: validOffer, + buyer: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + wantErr: true, + wantErrContains: "expect 1 order param of RollApp ID for asset type", + }, + { + name: "fail - (Alias) reject bad params", + assetId: "alias", + assetType: TypeAlias, + params: []string{"-not-chain-id-"}, + offer: validOffer, + buyer: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + wantErr: true, + wantErrContains: "invalid RollApp ID format", + }, + { + name: "fail - (Name) missing offer", + assetId: "my-name", + assetType: TypeName, + buyer: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + wantErr: true, + wantErrContains: "invalid offer", + }, + { + name: "fail - (Alias) missing offer", + assetId: "alias", + params: []string{"rollapp_1-1"}, + assetType: TypeAlias, + buyer: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + wantErr: true, + wantErrContains: "invalid offer", + }, + { + name: "fail - (Name) offer can not be zero", + assetId: "my-name", + assetType: TypeName, + offer: testCoin(0), + buyer: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + wantErr: true, + wantErrContains: "offer must be positive", + }, + { + name: "fail - (Alias) offer can not be zero", + assetId: "alias", + assetType: TypeAlias, + params: []string{"rollapp_1-1"}, + offer: testCoin(0), + buyer: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + wantErr: true, + wantErrContains: "offer must be positive", + }, + { + name: "fail - offer can not be negative", + assetId: "my-name", + assetType: TypeName, + offer: sdk.Coin{ + Denom: params.BaseDenom, + Amount: sdk.NewInt(-1), + }, + buyer: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + wantErr: true, + wantErrContains: "invalid offer", + }, + { + name: "fail - missing buyer", + assetId: "my-name", + assetType: TypeName, + offer: validOffer, + buyer: "", + wantErr: true, + wantErrContains: "buyer is not a valid bech32 account address", + }, + { + name: "fail - invalid buyer", + assetId: "my-name", + assetType: TypeName, + offer: validOffer, + buyer: "dym1fl48vsnmsdzcv", + wantErr: true, + wantErrContains: "buyer is not a valid bech32 account address", + }, + { + name: "fail - buyer must be dym1", + assetId: "my-name", + assetType: TypeName, + offer: validOffer, + buyer: "nim1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3pklgjx", + wantErr: true, + wantErrContains: "buyer is not a valid bech32 account address", + }, + { + name: "fail - reject unknown asset type", + assetId: "asset", + assetType: AssetType_AT_UNKNOWN, + offer: validOffer, + buyer: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + wantErr: true, + wantErrContains: "invalid asset type", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + m := &MsgPurchaseOrder{ + AssetId: tt.assetId, + AssetType: tt.assetType, + Params: tt.params, + Offer: tt.offer, + Buyer: tt.buyer, + } + + err := m.ValidateBasic() + if tt.wantErr { + require.NotEmpty(t, tt.wantErrContains, "mis-configured test case") + require.Error(t, err) + require.Contains(t, err.Error(), tt.wantErrContains) + return + } + + require.NoError(t, err) + }) + } +} diff --git a/x/dymns/types/msg_register_alias.go b/x/dymns/types/msg_register_alias.go new file mode 100644 index 000000000..fe4a49c8d --- /dev/null +++ b/x/dymns/types/msg_register_alias.go @@ -0,0 +1,65 @@ +package types + +import ( + errorsmod "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + dymnsutils "github.com/dymensionxyz/dymension/v3/x/dymns/utils" + "github.com/dymensionxyz/gerr-cosmos/gerrc" +) + +var _ sdk.Msg = &MsgRegisterAlias{} + +// ValidateBasic performs basic validation for the MsgRegisterAlias. +func (m *MsgRegisterAlias) ValidateBasic() error { + if len(m.Alias) > dymnsutils.MaxAliasLength { + return errorsmod.Wrapf( + gerrc.ErrInvalidArgument, + "alias is too long, maximum %d characters", dymnsutils.MaxAliasLength, + ) + } + + if !dymnsutils.IsValidAlias(m.Alias) { + return errorsmod.Wrap(gerrc.ErrInvalidArgument, "alias is not a valid alias format") + } + + if !dymnsutils.IsValidChainIdFormat(m.RollappId) { + return errorsmod.Wrap(gerrc.ErrInvalidArgument, "RollApp ID is not a valid chain id format") + } + + if _, err := sdk.AccAddressFromBech32(m.Owner); err != nil { + return errorsmod.Wrapf(gerrc.ErrInvalidArgument, "owner is not a valid bech32 account address: %s", m.Owner) + } + + if m.ConfirmPayment.IsNil() || m.ConfirmPayment.IsZero() { + return errorsmod.Wrap(gerrc.ErrInvalidArgument, "confirm payment is not set") + } else if err := m.ConfirmPayment.Validate(); err != nil { + return errorsmod.Wrapf(gerrc.ErrInvalidArgument, "invalid confirm payment: %v", err) + } + + return nil +} + +// GetSigners returns the required signers for the MsgRegisterAlias. +func (m *MsgRegisterAlias) GetSigners() []sdk.AccAddress { + owner, err := sdk.AccAddressFromBech32(m.Owner) + if err != nil { + panic(err) + } + return []sdk.AccAddress{owner} +} + +// Route returns the message router key for the MsgRegisterAlias. +func (m *MsgRegisterAlias) Route() string { + return RouterKey +} + +// Type returns the message type for the MsgRegisterAlias. +func (m *MsgRegisterAlias) Type() string { + return TypeMsgRegisterAlias +} + +// GetSignBytes returns the raw bytes for the MsgRegisterAlias. +func (m *MsgRegisterAlias) GetSignBytes() []byte { + bz := ModuleCdc.MustMarshalJSON(m) + return sdk.MustSortJSON(bz) +} diff --git a/x/dymns/types/msg_register_alias_test.go b/x/dymns/types/msg_register_alias_test.go new file mode 100644 index 000000000..272f7fef4 --- /dev/null +++ b/x/dymns/types/msg_register_alias_test.go @@ -0,0 +1,166 @@ +package types + +import ( + "fmt" + "testing" + + sdkmath "cosmossdk.io/math" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/dymensionxyz/dymension/v3/app/params" + dymnsutils "github.com/dymensionxyz/dymension/v3/x/dymns/utils" + "github.com/stretchr/testify/require" +) + +//goland:noinspection SpellCheckingInspection +func TestMsgRegisterAlias_ValidateBasic(t *testing.T) { + tests := []struct { + name string + alias string + rollAppId string + owner string + confirmPayment sdk.Coin + wantErr bool + wantErrContains string + }{ + { + name: "pass - valid", + alias: "a", + rollAppId: "rollapp_1-1", + owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + confirmPayment: testCoin(1), + }, + { + name: "fail - missing alias", + alias: "", + rollAppId: "rollapp_1-1", + owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + confirmPayment: testCoin(1), + wantErr: true, + wantErrContains: "alias is not a valid alias format", + }, + { + name: "fail - alias is too long", + alias: "123456789012345678901234567890123", + rollAppId: "rollapp_1-1", + owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + confirmPayment: testCoin(1), + wantErr: true, + wantErrContains: fmt.Sprintf("alias is too long, maximum %d characters", dymnsutils.MaxAliasLength), + }, + { + name: "fail - invalid alias", + alias: "-a", + rollAppId: "rollapp_1-1", + owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + confirmPayment: testCoin(1), + wantErr: true, + wantErrContains: "alias is not a valid alias format", + }, + { + name: "fail - empty RollApp ID", + alias: "a", + rollAppId: "", + owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + confirmPayment: testCoin(1), + wantErr: true, + wantErrContains: "RollApp ID is not a valid chain id format", + }, + { + name: "fail - bad RollApp ID", + alias: "a", + rollAppId: "-RollApp", + owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + confirmPayment: testCoin(1), + wantErr: true, + wantErrContains: "RollApp ID is not a valid chain id format", + }, + { + name: "fail - empty owner", + alias: "a", + rollAppId: "rollapp_1-1", + owner: "", + confirmPayment: testCoin(1), + wantErr: true, + wantErrContains: "owner is not a valid bech32 account address", + }, + { + name: "fail - invalid owner", + alias: "a", + rollAppId: "rollapp_1-1", + owner: "dym1fl48vsnmsdzcv85q5d2q4", + confirmPayment: testCoin(1), + wantErr: true, + wantErrContains: "owner is not a valid bech32 account address", + }, + { + name: "fail - owner must be dym1", + alias: "a", + rollAppId: "rollapp_1-1", + owner: "nim1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3pklgjx", + confirmPayment: testCoin(1), + wantErr: true, + wantErrContains: "owner is not a valid bech32 account address", + }, + { + name: "fail - confirm-payment not set", + alias: "a", + rollAppId: "rollapp_1-1", + owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + confirmPayment: sdk.Coin{}, + wantErr: true, + wantErrContains: "confirm payment is not set", + }, + { + name: "fail - confirm-payment is zero", + alias: "a", + rollAppId: "rollapp_1-1", + owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + confirmPayment: testCoin(0), + wantErr: true, + wantErrContains: "confirm payment is not set", + }, + { + name: "fail - confirm-payment is negative", + alias: "a", + rollAppId: "rollapp_1-1", + owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + confirmPayment: sdk.Coin{ + Denom: params.BaseDenom, + Amount: sdkmath.NewInt(-1), + }, + wantErr: true, + wantErrContains: "negative coin amount", + }, + { + name: "fail - confirm-payment without denom", + alias: "a", + rollAppId: "rollapp_1-1", + owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + confirmPayment: sdk.Coin{ + Denom: "", + Amount: sdk.OneInt(), + }, + wantErr: true, + wantErrContains: "invalid denom", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + m := &MsgRegisterAlias{ + Alias: tt.alias, + RollappId: tt.rollAppId, + Owner: tt.owner, + ConfirmPayment: tt.confirmPayment, + } + + err := m.ValidateBasic() + if tt.wantErr { + require.ErrorContains(t, err, tt.wantErrContains) + return + } + + require.NoError(t, err) + }) + } +} diff --git a/x/dymns/types/msg_register_name.go b/x/dymns/types/msg_register_name.go new file mode 100644 index 000000000..de6ec7425 --- /dev/null +++ b/x/dymns/types/msg_register_name.go @@ -0,0 +1,69 @@ +package types + +import ( + errorsmod "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + dymnsutils "github.com/dymensionxyz/dymension/v3/x/dymns/utils" + "github.com/dymensionxyz/gerr-cosmos/gerrc" +) + +var _ sdk.Msg = &MsgRegisterName{} + +// ValidateBasic performs basic validation for the MsgRegisterName. +func (m *MsgRegisterName) ValidateBasic() error { + if len(m.Name) > dymnsutils.MaxDymNameLength { + return errorsmod.Wrapf( + gerrc.ErrInvalidArgument, + "name is too long, maximum %d characters", dymnsutils.MaxDymNameLength, + ) + } + + if !dymnsutils.IsValidDymName(m.Name) { + return errorsmod.Wrap(gerrc.ErrInvalidArgument, "name is not a valid dym name") + } + + if m.Duration < 1 { + return errorsmod.Wrap(gerrc.ErrInvalidArgument, "duration must be at least 1 year") + } + + if _, err := sdk.AccAddressFromBech32(m.Owner); err != nil { + return errorsmod.Wrapf(gerrc.ErrInvalidArgument, "owner is not a valid bech32 account address: %s", m.Owner) + } + + if m.ConfirmPayment.IsNil() || m.ConfirmPayment.IsZero() { + return errorsmod.Wrap(gerrc.ErrInvalidArgument, "confirm payment is not set") + } else if err := m.ConfirmPayment.Validate(); err != nil { + return errorsmod.Wrapf(gerrc.ErrInvalidArgument, "invalid confirm payment: %v", err) + } + + if len(m.Contact) > MaxDymNameContactLength { + return errorsmod.Wrapf(gerrc.ErrInvalidArgument, "invalid contact length; got: %d, max: %d", len(m.Contact), MaxDymNameContactLength) + } + + return nil +} + +// GetSigners returns the required signers for the MsgRegisterName. +func (m *MsgRegisterName) GetSigners() []sdk.AccAddress { + owner, err := sdk.AccAddressFromBech32(m.Owner) + if err != nil { + panic(err) + } + return []sdk.AccAddress{owner} +} + +// Route returns the message router key for the MsgRegisterName. +func (m *MsgRegisterName) Route() string { + return RouterKey +} + +// Type returns the message type for the MsgRegisterName. +func (m *MsgRegisterName) Type() string { + return TypeMsgRegisterName +} + +// GetSignBytes returns the raw bytes for the MsgRegisterName. +func (m *MsgRegisterName) GetSignBytes() []byte { + bz := ModuleCdc.MustMarshalJSON(m) + return sdk.MustSortJSON(bz) +} diff --git a/x/dymns/types/msg_register_name_test.go b/x/dymns/types/msg_register_name_test.go new file mode 100644 index 000000000..4f70a8307 --- /dev/null +++ b/x/dymns/types/msg_register_name_test.go @@ -0,0 +1,186 @@ +package types + +import ( + "fmt" + "testing" + + sdkmath "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/dymensionxyz/dymension/v3/app/params" + dymnsutils "github.com/dymensionxyz/dymension/v3/x/dymns/utils" + "github.com/stretchr/testify/require" +) + +//goland:noinspection SpellCheckingInspection +func TestMsgRegisterName_ValidateBasic(t *testing.T) { + tests := []struct { + name string + dymName string + duration int64 + owner string + confirmPayment sdk.Coin + contact string + wantErr bool + wantErrContains string + }{ + { + name: "pass - valid 1 yr", + dymName: "a", + duration: 1, + owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + confirmPayment: testCoin(1), + contact: "contact@example.com", + }, + { + name: "pass - valid 1+ yrs", + dymName: "a", + duration: 5, + owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + confirmPayment: testCoin(1), + }, + { + name: "fail - missing name", + dymName: "", + duration: 5, + owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + confirmPayment: testCoin(1), + wantErr: true, + wantErrContains: "name is not a valid dym name", + }, + { + name: "fail - name is too long", + dymName: "123456789012345678901", + duration: 5, + owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + confirmPayment: testCoin(1), + wantErr: true, + wantErrContains: fmt.Sprintf("name is too long, maximum %d characters", dymnsutils.MaxDymNameLength), + }, + { + name: "fail - invalid name", + dymName: "-a", + duration: 5, + owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + confirmPayment: testCoin(1), + wantErr: true, + wantErrContains: "name is not a valid dym name", + }, + { + name: "fail - zero duration", + dymName: "a", + duration: 0, + owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + confirmPayment: testCoin(1), + wantErr: true, + wantErrContains: "duration must be at least 1 year", + }, + { + name: "fail - negative duration", + dymName: "a", + duration: -1, + owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + confirmPayment: testCoin(1), + wantErr: true, + wantErrContains: "duration must be at least 1 year", + }, + { + name: "fail - empty owner", + dymName: "a", + duration: 1, + owner: "", + confirmPayment: testCoin(1), + wantErr: true, + wantErrContains: "owner is not a valid bech32 account address", + }, + { + name: "fail - invalid owner", + dymName: "a", + duration: 1, + owner: "dym1fl48vsnmsdzcv85q5d2q4", + confirmPayment: testCoin(1), + wantErr: true, + wantErrContains: "owner is not a valid bech32 account address", + }, + { + name: "fail - owner must be dym1", + dymName: "a", + duration: 1, + owner: "nim1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3pklgjx", + confirmPayment: testCoin(1), + wantErr: true, + wantErrContains: "owner is not a valid bech32 account address", + }, + { + name: "fail - confirm-payment not set", + dymName: "a", + duration: 1, + owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + confirmPayment: sdk.Coin{}, + wantErr: true, + wantErrContains: "confirm payment is not set", + }, + { + name: "fail - confirm-payment is zero", + dymName: "a", + duration: 1, + owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + confirmPayment: testCoin(0), + wantErr: true, + wantErrContains: "confirm payment is not set", + }, + { + name: "fail - confirm-payment is negative", + dymName: "a", + duration: 1, + owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + confirmPayment: sdk.Coin{ + Denom: params.BaseDenom, + Amount: sdkmath.NewInt(-1), + }, + wantErr: true, + wantErrContains: "negative coin amount", + }, + { + name: "fail - confirm-payment without denom", + dymName: "a", + duration: 1, + owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + confirmPayment: sdk.Coin{ + Denom: "", + Amount: sdk.OneInt(), + }, + wantErr: true, + wantErrContains: "invalid denom", + }, + { + name: "fail - contact too long", + dymName: "a", + duration: 1, + owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + confirmPayment: testCoin(1), + contact: "123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901", + wantErr: true, + wantErrContains: "invalid contact length", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + m := &MsgRegisterName{ + Name: tt.dymName, + Duration: tt.duration, + Owner: tt.owner, + ConfirmPayment: tt.confirmPayment, + Contact: tt.contact, + } + + err := m.ValidateBasic() + if tt.wantErr { + require.NotEmpty(t, tt.wantErrContains, "mis-configured test case") + require.Error(t, err) + require.Contains(t, err.Error(), tt.wantErrContains) + } else { + require.NoError(t, err) + } + }) + } +} diff --git a/x/dymns/types/msg_set_controller.go b/x/dymns/types/msg_set_controller.go new file mode 100644 index 000000000..ffdcc16e9 --- /dev/null +++ b/x/dymns/types/msg_set_controller.go @@ -0,0 +1,52 @@ +package types + +import ( + errorsmod "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + dymnsutils "github.com/dymensionxyz/dymension/v3/x/dymns/utils" + "github.com/dymensionxyz/gerr-cosmos/gerrc" +) + +var _ sdk.Msg = &MsgSetController{} + +// ValidateBasic performs basic validation for the MsgSetController. +func (m *MsgSetController) ValidateBasic() error { + if !dymnsutils.IsValidDymName(m.Name) { + return errorsmod.Wrap(gerrc.ErrInvalidArgument, "name is not a valid dym name") + } + + if _, err := sdk.AccAddressFromBech32(m.Controller); err != nil { + return errorsmod.Wrap(gerrc.ErrInvalidArgument, "controller is not a valid bech32 account address") + } + + if _, err := sdk.AccAddressFromBech32(m.Owner); err != nil { + return errorsmod.Wrap(gerrc.ErrInvalidArgument, "owner is not a valid bech32 account address") + } + + return nil +} + +// GetSigners returns the required signers for the MsgSetController. +func (m *MsgSetController) GetSigners() []sdk.AccAddress { + owner, err := sdk.AccAddressFromBech32(m.Owner) + if err != nil { + panic(err) + } + return []sdk.AccAddress{owner} +} + +// Route returns the message router key for the MsgSetController. +func (m *MsgSetController) Route() string { + return RouterKey +} + +// Type returns the message type for the MsgSetController. +func (m *MsgSetController) Type() string { + return TypeMsgSetController +} + +// GetSignBytes returns the raw bytes for the MsgSetController. +func (m *MsgSetController) GetSignBytes() []byte { + bz := ModuleCdc.MustMarshalJSON(m) + return sdk.MustSortJSON(bz) +} diff --git a/x/dymns/types/msg_set_controller_test.go b/x/dymns/types/msg_set_controller_test.go new file mode 100644 index 000000000..291df3a72 --- /dev/null +++ b/x/dymns/types/msg_set_controller_test.go @@ -0,0 +1,112 @@ +package types + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestMsgSetController_ValidateBasic(t *testing.T) { + //goland:noinspection SpellCheckingInspection + tests := []struct { + name string + dymName string + controller string + owner string + wantErr bool + wantErrContains string + }{ + { + name: "pass - valid", + dymName: "a", + controller: "dym1tygms3xhhs3yv487phx3dw4a95jn7t7lnxec2d", + owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + }, + { + name: "pass - controller and owner can be the same", + dymName: "a", + controller: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + }, + { + name: "pass - controller and owner can be the different", + dymName: "a", + controller: "dym1tygms3xhhs3yv487phx3dw4a95jn7t7lnxec2d", + owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + }, + { + name: "fail - missing name", + dymName: "", + controller: "dym1tygms3xhhs3yv487phx3dw4a95jn7t7lnxec2d", + owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + wantErr: true, + wantErrContains: "name is not a valid dym name", + }, + { + name: "fail - missing controller", + dymName: "a", + controller: "", + owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + wantErr: true, + wantErrContains: "controller is not a valid bech32 account address", + }, + { + name: "fail - invalid controller", + dymName: "a", + controller: "dym1tygms3xhhs3yv487phx", + owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + wantErr: true, + wantErrContains: "controller is not a valid bech32 account address", + }, + { + name: "fail - missing owner", + dymName: "a", + controller: "dym1tygms3xhhs3yv487phx3dw4a95jn7t7lnxec2d", + owner: "", + wantErr: true, + wantErrContains: "owner is not a valid bech32 account address", + }, + { + name: "fail - invalid owner", + dymName: "a", + controller: "dym1tygms3xhhs3yv487phx3dw4a95jn7t7lnxec2d", + owner: "dym1fl48vsnmsdzcv85q5d2", + wantErr: true, + wantErrContains: "owner is not a valid bech32 account address", + }, + { + name: "fail - controller must be dym1", + dymName: "a", + controller: "nim1tygms3xhhs3yv487phx3dw4a95jn7t7l4kreyj", + owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + wantErr: true, + wantErrContains: "controller is not a valid bech32 account address", + }, + { + name: "fail - owner must be dym1", + dymName: "a", + controller: "dym1tygms3xhhs3yv487phx3dw4a95jn7t7lnxec2d", + owner: "nim1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3pklgjx", + wantErr: true, + wantErrContains: "owner is not a valid bech32 account address", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + m := &MsgSetController{ + Name: tt.dymName, + Controller: tt.controller, + Owner: tt.owner, + } + + err := m.ValidateBasic() + if tt.wantErr { + require.NotEmpty(t, tt.wantErrContains, "mis-configured test case") + require.Error(t, err) + require.Contains(t, err.Error(), tt.wantErrContains) + } else { + require.NoError(t, err) + } + }) + } +} diff --git a/x/dymns/types/msg_transfer_ownership.go b/x/dymns/types/msg_transfer_ownership.go new file mode 100644 index 000000000..55eb6d14a --- /dev/null +++ b/x/dymns/types/msg_transfer_ownership.go @@ -0,0 +1,59 @@ +package types + +import ( + "strings" + + errorsmod "cosmossdk.io/errors" + "github.com/dymensionxyz/gerr-cosmos/gerrc" + + sdk "github.com/cosmos/cosmos-sdk/types" + dymnsutils "github.com/dymensionxyz/dymension/v3/x/dymns/utils" +) + +var _ sdk.Msg = &MsgTransferDymNameOwnership{} + +// ValidateBasic performs basic validation for the MsgTransferDymNameOwnership. +func (m *MsgTransferDymNameOwnership) ValidateBasic() error { + if !dymnsutils.IsValidDymName(m.Name) { + return errorsmod.Wrap(gerrc.ErrInvalidArgument, "name is not a valid dym name") + } + + if _, err := sdk.AccAddressFromBech32(m.NewOwner); err != nil { + return errorsmod.Wrap(gerrc.ErrInvalidArgument, "new owner is not a valid bech32 account address") + } + + if _, err := sdk.AccAddressFromBech32(m.Owner); err != nil { + return errorsmod.Wrap(gerrc.ErrInvalidArgument, "owner is not a valid bech32 account address") + } + + if strings.EqualFold(m.NewOwner, m.Owner) { + return errorsmod.Wrap(gerrc.ErrInvalidArgument, "new owner must be different from the current owner") + } + + return nil +} + +// GetSigners returns the required signers for the MsgTransferDymNameOwnership. +func (m *MsgTransferDymNameOwnership) GetSigners() []sdk.AccAddress { + owner, err := sdk.AccAddressFromBech32(m.Owner) + if err != nil { + panic(err) + } + return []sdk.AccAddress{owner} +} + +// Route returns the message router key for the MsgTransferDymNameOwnership. +func (m *MsgTransferDymNameOwnership) Route() string { + return RouterKey +} + +// Type returns the message type for the MsgTransferDymNameOwnership. +func (m *MsgTransferDymNameOwnership) Type() string { + return TypeMsgTransferDymNameOwnership +} + +// GetSignBytes returns the raw bytes for the MsgTransferDymNameOwnership. +func (m *MsgTransferDymNameOwnership) GetSignBytes() []byte { + bz := ModuleCdc.MustMarshalJSON(m) + return sdk.MustSortJSON(bz) +} diff --git a/x/dymns/types/msg_transfer_ownership_test.go b/x/dymns/types/msg_transfer_ownership_test.go new file mode 100644 index 000000000..4e33c5d2b --- /dev/null +++ b/x/dymns/types/msg_transfer_ownership_test.go @@ -0,0 +1,108 @@ +package types + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestMsgTransferDymNameOwnership_ValidateBasic(t *testing.T) { + //goland:noinspection SpellCheckingInspection + tests := []struct { + name string + dymName string + newOwner string + owner string + wantErr bool + wantErrContains string + }{ + { + name: "pass - valid", + dymName: "a", + newOwner: "dym1tygms3xhhs3yv487phx3dw4a95jn7t7lnxec2d", + owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + }, + { + name: "fail - new owner and owner can not be the same", + dymName: "a", + newOwner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + wantErr: true, + wantErrContains: "new owner must be different from the current owner", + }, + { + name: "fail - missing name", + dymName: "", + newOwner: "dym1tygms3xhhs3yv487phx3dw4a95jn7t7lnxec2d", + owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + wantErr: true, + wantErrContains: "name is not a valid dym name", + }, + { + name: "fail - missing new owner", + dymName: "a", + newOwner: "", + owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + wantErr: true, + wantErrContains: "new owner is not a valid bech32 account address", + }, + { + name: "fail - invalid new owner", + dymName: "a", + newOwner: "dym1tygms3xhhs3yv487phx", + owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + wantErr: true, + wantErrContains: "new owner is not a valid bech32 account address", + }, + { + name: "fail - missing owner", + dymName: "a", + newOwner: "dym1tygms3xhhs3yv487phx3dw4a95jn7t7lnxec2d", + owner: "", + wantErr: true, + wantErrContains: "owner is not a valid bech32 account address", + }, + { + name: "fail - invalid owner", + dymName: "a", + newOwner: "dym1tygms3xhhs3yv487phx3dw4a95jn7t7lnxec2d", + owner: "dym1fl48vsnmsdzcv85q5d2", + wantErr: true, + wantErrContains: "owner is not a valid bech32 account address", + }, + { + name: "fail - new owner must be dym1", + dymName: "a", + newOwner: "nim1tygms3xhhs3yv487phx3dw4a95jn7t7l4kreyj", + owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + wantErr: true, + wantErrContains: "new owner is not a valid bech32 account address", + }, + { + name: "fail - owner must be dym1", + dymName: "a", + newOwner: "dym1tygms3xhhs3yv487phx3dw4a95jn7t7lnxec2d", + owner: "nim1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3pklgjx", + wantErr: true, + wantErrContains: "owner is not a valid bech32 account address", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + m := &MsgTransferDymNameOwnership{ + Name: tt.dymName, + NewOwner: tt.newOwner, + Owner: tt.owner, + } + + err := m.ValidateBasic() + if tt.wantErr { + require.NotEmpty(t, tt.wantErrContains, "mis-configured test case") + require.Error(t, err) + require.Contains(t, err.Error(), tt.wantErrContains) + } else { + require.NoError(t, err) + } + }) + } +} diff --git a/x/dymns/types/msg_update_details.go b/x/dymns/types/msg_update_details.go new file mode 100644 index 000000000..375a3180b --- /dev/null +++ b/x/dymns/types/msg_update_details.go @@ -0,0 +1,58 @@ +package types + +import ( + errorsmod "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + dymnsutils "github.com/dymensionxyz/dymension/v3/x/dymns/utils" + "github.com/dymensionxyz/gerr-cosmos/gerrc" +) + +var _ sdk.Msg = &MsgUpdateDetails{} + +// ValidateBasic performs basic validation for the MsgUpdateDetails. +func (m *MsgUpdateDetails) ValidateBasic() error { + if !dymnsutils.IsValidDymName(m.Name) { + return errorsmod.Wrap(gerrc.ErrInvalidArgument, "name is not a valid dym name") + } + + if m.Contact != DoNotModifyDesc { + if len(m.Contact) > MaxDymNameContactLength { + return errorsmod.Wrapf(gerrc.ErrInvalidArgument, "contact is too long; max length: %d", MaxDymNameContactLength) + } + } + + if _, err := sdk.AccAddressFromBech32(m.Controller); err != nil { + return errorsmod.Wrap(gerrc.ErrInvalidArgument, "controller is not a valid bech32 account address") + } + + if m.Contact == DoNotModifyDesc && !m.ClearConfigs { + return errorsmod.Wrap(gerrc.ErrInvalidArgument, "message neither clears configs nor updates contact information") + } + + return nil +} + +// GetSigners returns the required signers for the MsgUpdateDetails. +func (m *MsgUpdateDetails) GetSigners() []sdk.AccAddress { + controller, err := sdk.AccAddressFromBech32(m.Controller) + if err != nil { + panic(err) + } + return []sdk.AccAddress{controller} +} + +// Route returns the message router key for the MsgUpdateDetails. +func (m *MsgUpdateDetails) Route() string { + return RouterKey +} + +// Type returns the message type for the MsgUpdateDetails. +func (m *MsgUpdateDetails) Type() string { + return TypeMsgUpdateDetails +} + +// GetSignBytes returns the raw bytes for the MsgUpdateDetails. +func (m *MsgUpdateDetails) GetSignBytes() []byte { + bz := ModuleCdc.MustMarshalJSON(m) + return sdk.MustSortJSON(bz) +} diff --git a/x/dymns/types/msg_update_details_test.go b/x/dymns/types/msg_update_details_test.go new file mode 100644 index 000000000..87f1342cb --- /dev/null +++ b/x/dymns/types/msg_update_details_test.go @@ -0,0 +1,102 @@ +package types + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestMsgUpdateDetails_ValidateBasic(t *testing.T) { + //goland:noinspection SpellCheckingInspection + tests := []struct { + name string + dymName string + controller string + contact string + clearConfigs bool + wantErr bool + wantErrContains string + }{ + { + name: "pass - valid", + dymName: "a", + controller: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + contact: "contact@example.com", + clearConfigs: false, + }, + { + name: "pass - valid", + dymName: "a", + controller: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + contact: "", + clearConfigs: false, + }, + { + name: "pass - valid", + dymName: "a", + controller: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + contact: "", + clearConfigs: true, + }, + { + name: "pass - valid", + dymName: "a", + controller: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + contact: "contact@example.com", + clearConfigs: true, + }, + { + name: "fail - reject contact too long", + dymName: "a", + controller: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + contact: "123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901", + clearConfigs: true, + wantErr: true, + wantErrContains: "contact is too long", + }, + { + name: "fail - reject bad Dym-Name", + dymName: "a@", + controller: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + contact: "", + wantErr: true, + wantErrContains: "name is not a valid dym name", + }, + { + name: "fail - reject bad controller", + dymName: "a", + controller: "dym1fl48vsnmsdzcv85q", + wantErr: true, + wantErrContains: "controller is not a valid bech32 account address", + }, + { + name: "fail - reject message that does nothing", + dymName: "a", + controller: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + contact: DoNotModifyDesc, + clearConfigs: false, + wantErr: true, + wantErrContains: "message neither clears configs nor updates contact information", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + m := &MsgUpdateDetails{ + Name: tt.dymName, + Controller: tt.controller, + Contact: tt.contact, + ClearConfigs: tt.clearConfigs, + } + + err := m.ValidateBasic() + if tt.wantErr { + require.NotEmpty(t, tt.wantErrContains, "mis-configured test case") + require.Error(t, err) + require.Contains(t, err.Error(), tt.wantErrContains) + return + } + + require.NoError(t, err) + }) + } +} diff --git a/x/dymns/types/msg_update_params.go b/x/dymns/types/msg_update_params.go new file mode 100644 index 000000000..7d470a61f --- /dev/null +++ b/x/dymns/types/msg_update_params.go @@ -0,0 +1,65 @@ +package types + +import ( + "errors" + + errorsmod "cosmossdk.io/errors" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/dymensionxyz/gerr-cosmos/gerrc" +) + +var _ sdk.Msg = &MsgUpdateParams{} + +// ValidateBasic performs basic validation for the MsgUpdateParams. +func (m *MsgUpdateParams) ValidateBasic() error { + _, err := sdk.AccAddressFromBech32(m.Authority) + if err != nil { + return errorsmod.Wrapf( + errors.Join(gerrc.ErrInvalidArgument, err), + "authority is not a valid bech32 address: %s", m.Authority, + ) + } + + if m.NewPriceParams == nil && m.NewChainsParams == nil && m.NewMiscParams == nil { + return errorsmod.Wrap(gerrc.ErrInvalidArgument, "at least one of the new params must be provided") + } + + if m.NewPriceParams != nil { + if err := m.NewPriceParams.Validate(); err != nil { + return errorsmod.Wrapf( + errors.Join(gerrc.ErrInvalidArgument, err), + "failed to validate new price params", + ) + } + } + + if m.NewChainsParams != nil { + if err := m.NewChainsParams.Validate(); err != nil { + return errorsmod.Wrapf( + errors.Join(gerrc.ErrInvalidArgument, err), + "failed to validate new chains params", + ) + } + } + + if m.NewMiscParams != nil { + if err := m.NewMiscParams.Validate(); err != nil { + return errorsmod.Wrapf( + errors.Join(gerrc.ErrInvalidArgument, err), + "failed to validate new misc params", + ) + } + } + + return nil +} + +// GetSigners returns the required signers for the MsgUpdateParams. +func (m *MsgUpdateParams) GetSigners() []sdk.AccAddress { + authority, err := sdk.AccAddressFromBech32(m.Authority) + if err != nil { + panic(err) + } + return []sdk.AccAddress{authority} +} diff --git a/x/dymns/types/msg_update_params_test.go b/x/dymns/types/msg_update_params_test.go new file mode 100644 index 000000000..d230859ae --- /dev/null +++ b/x/dymns/types/msg_update_params_test.go @@ -0,0 +1,133 @@ +package types + +import ( + "fmt" + "testing" + + "github.com/dymensionxyz/dymension/v3/testutil/sample" + "github.com/stretchr/testify/require" +) + +func TestMsgUpdateParams_ValidateBasic(t *testing.T) { + defaultPriceParams := DefaultPriceParams() + defaultChainsParams := DefaultChainsParams() + defaultMiscParams := DefaultMiscParams() + + trueAndFalse := []bool{true, false} + for _, updatePriceParams := range trueAndFalse { + for _, updateChainsParams := range trueAndFalse { + for _, updateMiscParams := range trueAndFalse { + if !updatePriceParams && !updateChainsParams && !updateMiscParams { + continue + } + + t.Run( + fmt.Sprintf( + "pass - all valid, random params provided: %t|%t|%t", + updatePriceParams, + updateChainsParams, + updateMiscParams, + ), + func(t *testing.T) { + m := &MsgUpdateParams{ + Authority: sample.AccAddress(), + } + + if updatePriceParams { + m.NewPriceParams = &defaultPriceParams + } + + if updateChainsParams { + m.NewChainsParams = &defaultChainsParams + } + + if updateMiscParams { + m.NewMiscParams = &defaultMiscParams + } + + err := m.ValidateBasic() + require.NoError(t, err) + }) + } + } + } + + tests := []struct { + name string + authority string + newPriceParams *PriceParams + newChainsParams *ChainsParams + newMiscParams *MiscParams + wantErr bool + wantErrContains string + }{ + { + name: "fail - not update any params", + authority: sample.AccAddress(), + newPriceParams: nil, + newChainsParams: nil, + newMiscParams: nil, + wantErr: true, + wantErrContains: "at least one of the new params must be provided", + }, + { + name: "fail - bad authority", + authority: "0x1", + newPriceParams: &defaultPriceParams, + newChainsParams: nil, + newMiscParams: nil, + wantErr: true, + wantErrContains: "authority is not a valid bech32 address", + }, + { + name: "fail - bad price params", + authority: sample.AccAddress(), + newPriceParams: &PriceParams{}, + newChainsParams: nil, + newMiscParams: nil, + wantErr: true, + wantErrContains: "failed to validate new price params", + }, + { + name: "fail - bad chain params", + authority: sample.AccAddress(), + newPriceParams: nil, + newChainsParams: &ChainsParams{ + AliasesOfChainIds: []AliasesOfChainId{ + { + ChainId: "@@@", + }, + }, + }, + newMiscParams: nil, + wantErr: true, + wantErrContains: "failed to validate new chains params", + }, + { + name: "fail - bad misc params", + authority: sample.AccAddress(), + newPriceParams: nil, + newChainsParams: nil, + newMiscParams: &MiscParams{}, + wantErr: true, + wantErrContains: "failed to validate new misc params", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + m := &MsgUpdateParams{ + Authority: tt.authority, + NewPriceParams: tt.newPriceParams, + NewChainsParams: tt.newChainsParams, + NewMiscParams: tt.newMiscParams, + } + err := m.ValidateBasic() + if tt.wantErr { + require.ErrorContains(t, err, tt.wantErrContains) + return + } + + require.NoError(t, err) + }) + } +} diff --git a/x/dymns/types/msg_update_resolve_address.go b/x/dymns/types/msg_update_resolve_address.go new file mode 100644 index 000000000..1836e7ff8 --- /dev/null +++ b/x/dymns/types/msg_update_resolve_address.go @@ -0,0 +1,78 @@ +package types + +import ( + errorsmod "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + dymnsutils "github.com/dymensionxyz/dymension/v3/x/dymns/utils" + "github.com/dymensionxyz/gerr-cosmos/gerrc" +) + +var _ sdk.Msg = &MsgUpdateResolveAddress{} + +// ValidateBasic performs basic validation for the MsgUpdateResolveAddress. +func (m *MsgUpdateResolveAddress) ValidateBasic() error { + if !dymnsutils.IsValidDymName(m.Name) { + return errorsmod.Wrap(gerrc.ErrInvalidArgument, "name is not a valid dym name") + } + + if len(m.SubName) > dymnsutils.MaxDymNameLength { + return errorsmod.Wrap(gerrc.ErrInvalidArgument, "sub name is too long") + } + + _, config := m.GetDymNameConfig() + if err := config.Validate(); err != nil { + return errorsmod.Wrapf(gerrc.ErrInvalidArgument, "config is invalid: %v", err) + } + + if m.ChainId == "" { + if m.ResolveTo != "" { + if !dymnsutils.IsValidBech32AccountAddress(m.ResolveTo, true) { + return errorsmod.Wrap( + gerrc.ErrInvalidArgument, + "resolve address must be a valid bech32 account address on host chain", + ) + } + } + } + + if !dymnsutils.IsValidBech32AccountAddress(m.Controller, true) { + return errorsmod.Wrap(gerrc.ErrInvalidArgument, "controller is not a valid bech32 account address") + } + + return nil +} + +// GetDymNameConfig casts MsgUpdateResolveAddress into DymNameConfig. +func (m *MsgUpdateResolveAddress) GetDymNameConfig() (name string, config DymNameConfig) { + return m.Name, DymNameConfig{ + Type: DymNameConfigType_DCT_NAME, + ChainId: m.ChainId, + Path: m.SubName, + Value: m.ResolveTo, + } +} + +// GetSigners returns the required signers for the MsgUpdateResolveAddress. +func (m *MsgUpdateResolveAddress) GetSigners() []sdk.AccAddress { + controller, err := sdk.AccAddressFromBech32(m.Controller) + if err != nil { + panic(err) + } + return []sdk.AccAddress{controller} +} + +// Route returns the message router key for the MsgUpdateResolveAddress. +func (m *MsgUpdateResolveAddress) Route() string { + return RouterKey +} + +// Type returns the message type for the MsgUpdateResolveAddress. +func (m *MsgUpdateResolveAddress) Type() string { + return TypeMsgUpdateResolveAddress +} + +// GetSignBytes returns the raw bytes for the MsgUpdateResolveAddress. +func (m *MsgUpdateResolveAddress) GetSignBytes() []byte { + bz := ModuleCdc.MustMarshalJSON(m) + return sdk.MustSortJSON(bz) +} diff --git a/x/dymns/types/msg_update_resolve_address_test.go b/x/dymns/types/msg_update_resolve_address_test.go new file mode 100644 index 000000000..d78e291f2 --- /dev/null +++ b/x/dymns/types/msg_update_resolve_address_test.go @@ -0,0 +1,237 @@ +package types + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestMsgUpdateResolveAddress_ValidateBasic(t *testing.T) { + //goland:noinspection SpellCheckingInspection + tests := []struct { + name string + dymName string + chainId string + subName string + resolveTo string + controller string + wantErr bool + wantErrContains string + }{ + { + name: "pass - valid", + dymName: "a", + chainId: "dymension_1100-1", + subName: "abc", + resolveTo: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + controller: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + }, + { + name: "fail - missing dym-name", + dymName: "", + resolveTo: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + controller: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + wantErr: true, + wantErrContains: "name is not a valid dym name", + }, + { + name: "fail - bad dym-name", + dymName: "", + resolveTo: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + controller: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + wantErr: true, + wantErrContains: "name is not a valid dym name", + }, + { + name: "pass - valid config resolve with multi-level sub-name", + dymName: "a", + subName: "abc.def", + resolveTo: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + controller: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + }, + { + name: "pass - valid config resolve without sub-name", + dymName: "a", + chainId: "dymension_1100-1", + subName: "", + resolveTo: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + controller: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + }, + { + name: "pass - valid config resolve with empty chain-id", + dymName: "a", + chainId: "", + subName: "abc", + resolveTo: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + controller: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + }, + { + name: "pass - valid config resolve with empty chain-id and sub-name", + dymName: "a", + chainId: "", + subName: "", + resolveTo: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + controller: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + }, + { + name: "fail - bad chain-id", + dymName: "a", + chainId: "dymension_", + subName: "abc", + resolveTo: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + controller: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + wantErr: true, + wantErrContains: "dym name config chain id must be a valid chain id format", + }, + { + name: "fail - bad sub-name", + dymName: "a", + chainId: "", + subName: "-a", + resolveTo: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + controller: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + wantErr: true, + wantErrContains: "dym name config path must be a valid dym name", + }, + { + name: "fail - bad sub-name, too long", + dymName: "a", + chainId: "", + subName: "123456789012345678901", + resolveTo: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + controller: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + wantErr: true, + wantErrContains: "sub name is too long", + }, + { + name: "fail - bad multi-level sub-name", + dymName: "a", + chainId: "", + subName: "a.b.", + resolveTo: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + controller: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + wantErr: true, + wantErrContains: "dym name config path must be a valid dym name", + }, + { + name: "pass - resolve to can be empty to allow delete", + dymName: "a", + chainId: "", + subName: "a", + resolveTo: "", + controller: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + }, + { + name: "pass - resolve to can be empty to allow delete", + dymName: "a", + chainId: "", + subName: "", + resolveTo: "", + controller: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + }, + { + name: "fail - bad resolve to", + dymName: "a", + chainId: "", + subName: "a", + resolveTo: "0x01", + controller: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + wantErr: true, + wantErrContains: "dym name config value must be a valid bech32 account address", + }, + { + name: "fail - resolve must be dym1 format if chain-id is empty", + dymName: "a", + chainId: "", + resolveTo: "nim1tygms3xhhs3yv487phx3dw4a95jn7t7l4kreyj", + controller: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + wantErr: true, + wantErrContains: "resolve address must be a valid bech32 account address on host chain", + }, + { + name: "pass - resolve to can be non-dym1 format if chain-id is not empty", + dymName: "a", + chainId: "nim_1122-1", + resolveTo: "nim1tygms3xhhs3yv487phx3dw4a95jn7t7l4kreyj", + controller: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + }, + { + name: "fail - controller must be dym1", + dymName: "a", + resolveTo: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + controller: "nim1tygms3xhhs3yv487phx3dw4a95jn7t7l4kreyj", + wantErr: true, + wantErrContains: "controller is not a valid bech32 account address", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + m := &MsgUpdateResolveAddress{ + Name: tt.dymName, + ChainId: tt.chainId, + SubName: tt.subName, + ResolveTo: tt.resolveTo, + Controller: tt.controller, + } + + err := m.ValidateBasic() + if tt.wantErr { + require.NotEmpty(t, tt.wantErrContains, "mis-configured test case") + require.Error(t, err) + require.Contains(t, err.Error(), tt.wantErrContains) + } else { + require.NoError(t, err) + } + }) + } +} + +func TestMsgUpdateResolveAddress_GetDymNameConfig(t *testing.T) { + tests := []struct { + name string + DymName string + ChainId string + SubName string + ResolveTo string + Controller string + wantName string + wantConfig DymNameConfig + }{ + { + name: "assigned correctly", + DymName: "a", + ChainId: "dymension", + SubName: "sub", + ResolveTo: "r", + Controller: "c", + wantName: "a", + wantConfig: DymNameConfig{ + Type: DymNameConfigType_DCT_NAME, + ChainId: "dymension", + Path: "sub", + Value: "r", + }, + }, + { + name: "all empty", + wantConfig: DymNameConfig{ + Type: DymNameConfigType_DCT_NAME, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + m := &MsgUpdateResolveAddress{ + Name: tt.DymName, + ChainId: tt.ChainId, + SubName: tt.SubName, + ResolveTo: tt.ResolveTo, + Controller: tt.Controller, + } + + gotName, gotConfig := m.GetDymNameConfig() + require.Equal(t, tt.wantName, gotName) + require.Equal(t, tt.wantConfig, gotConfig) + }) + } +} diff --git a/x/dymns/types/msgs.go b/x/dymns/types/msgs.go new file mode 100644 index 000000000..ca2b299ab --- /dev/null +++ b/x/dymns/types/msgs.go @@ -0,0 +1,31 @@ +package types + +// These constants define the types of messages those are used in this module. +const ( + // TypeMsgRegisterName is type for MsgRegisterName. + TypeMsgRegisterName = "register_name" + // TypeMsgRegisterAlias is type for MsgRegisterAlias. + TypeMsgRegisterAlias = "register_alias" + // TypeMsgTransferDymNameOwnership is type for MsgTransferDymNameOwnership. + TypeMsgTransferDymNameOwnership = "transfer_dym_name_ownership" + // TypeMsgSetController is type for MsgSetController. + TypeMsgSetController = "set_controller" + // TypeMsgUpdateResolveAddress is type for MsgUpdateResolveAddress. + TypeMsgUpdateResolveAddress = "update_resolve_address" + // TypeMsgUpdateDetails is type for MsgUpdateDetails. + TypeMsgUpdateDetails = "update_details" + + // TypeMsgPlaceSellOrder is type for MsgPlaceSellOrder. + TypeMsgPlaceSellOrder = "place_sell_order" + // TypeMsgCancelSellOrder is type for MsgCancelSellOrder. + TypeMsgCancelSellOrder = "cancel_sell_order" + // TypeMsgPurchaseOrder is type for MsgPurchaseOrder. + TypeMsgPurchaseOrder = "purchase_order" + + // TypeMsgPlaceBuyOrder is type for MsgPlaceBuyOrder. + TypeMsgPlaceBuyOrder = "place_buy_order" + // TypeMsgCancelBuyOrder is type for MsgCancelBuyOrder. + TypeMsgCancelBuyOrder = "cancel_buy_order" + // TypeMsgAcceptBuyOrder is type for MsgAcceptBuyOrder. + TypeMsgAcceptBuyOrder = "accept_buy_order" +) diff --git a/x/dymns/types/msgs_test.go b/x/dymns/types/msgs_test.go new file mode 100644 index 000000000..37c768028 --- /dev/null +++ b/x/dymns/types/msgs_test.go @@ -0,0 +1,119 @@ +package types + +import ( + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth/migrations/legacytx" + "github.com/stretchr/testify/require" +) + +func TestMsgs_Signers(t *testing.T) { + t.Run("get signers", func(t *testing.T) { + //goland:noinspection GoDeprecation,SpellCheckingInspection + msgs := []sdk.Msg{ + &MsgRegisterName{ + Owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + }, + &MsgRegisterAlias{ + Owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + }, + &MsgTransferDymNameOwnership{ + Owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + }, + &MsgSetController{ + Owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + }, + &MsgUpdateResolveAddress{ + Controller: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + }, + &MsgUpdateDetails{ + Controller: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + }, + &MsgPlaceSellOrder{ + Owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + }, + &MsgCancelSellOrder{ + Owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + }, + &MsgPurchaseOrder{ + Buyer: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + }, + &MsgPlaceBuyOrder{ + Buyer: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + }, + &MsgCancelBuyOrder{ + Buyer: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + }, + &MsgAcceptBuyOrder{ + Owner: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + }, + } + + for _, msg := range msgs { + require.Len(t, msg.GetSigners(), 1) + } + }) + + t.Run("bad signers should panic", func(t *testing.T) { + msgs := []sdk.Msg{ + &MsgRegisterName{}, + &MsgRegisterAlias{}, + &MsgTransferDymNameOwnership{}, + &MsgSetController{}, + &MsgUpdateResolveAddress{}, + &MsgUpdateDetails{}, + &MsgPlaceSellOrder{}, + &MsgCancelSellOrder{}, + &MsgPurchaseOrder{}, + &MsgPlaceBuyOrder{}, + &MsgCancelBuyOrder{}, + &MsgAcceptBuyOrder{}, + } + + for _, msg := range msgs { + require.Panics(t, func() { + _ = msg.GetSigners() + }) + } + }) +} + +func TestMsgs_ImplementLegacyMsg(t *testing.T) { + //goland:noinspection GoDeprecation + msgs := []legacytx.LegacyMsg{ + &MsgRegisterName{}, + &MsgRegisterAlias{}, + &MsgTransferDymNameOwnership{}, + &MsgSetController{}, + &MsgUpdateResolveAddress{}, + &MsgUpdateDetails{}, + &MsgPlaceSellOrder{}, + &MsgCancelSellOrder{}, + &MsgPurchaseOrder{}, + &MsgPlaceBuyOrder{}, + &MsgCancelBuyOrder{}, + &MsgAcceptBuyOrder{}, + } + + for _, msg := range msgs { + require.Equal(t, RouterKey, msg.Route()) + require.NotEmpty(t, msg.Type()) + require.NotEmpty(t, msg.GetSignBytes()) + } +} + +func TestMsgs_Type(t *testing.T) { + require.Equal(t, "register_name", (&MsgRegisterName{}).Type()) + require.Equal(t, "register_alias", (&MsgRegisterAlias{}).Type()) + require.Equal(t, "transfer_dym_name_ownership", (&MsgTransferDymNameOwnership{}).Type()) + require.Equal(t, "set_controller", (&MsgSetController{}).Type()) + require.Equal(t, "update_resolve_address", (&MsgUpdateResolveAddress{}).Type()) + require.Equal(t, "update_details", (&MsgUpdateDetails{}).Type()) + require.Equal(t, "place_sell_order", (&MsgPlaceSellOrder{}).Type()) + require.Equal(t, "cancel_sell_order", (&MsgCancelSellOrder{}).Type()) + require.Equal(t, "purchase_order", (&MsgPurchaseOrder{}).Type()) + require.Equal(t, "place_buy_order", (&MsgPlaceBuyOrder{}).Type()) + require.Equal(t, "cancel_buy_order", (&MsgCancelBuyOrder{}).Type()) + require.Equal(t, "accept_buy_order", (&MsgAcceptBuyOrder{}).Type()) +} diff --git a/x/dymns/types/params.go b/x/dymns/types/params.go new file mode 100644 index 000000000..07896218b --- /dev/null +++ b/x/dymns/types/params.go @@ -0,0 +1,425 @@ +package types + +import ( + "errors" + "fmt" + "time" + + errorsmod "cosmossdk.io/errors" + + "github.com/dymensionxyz/gerr-cosmos/gerrc" + + sdkmath "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" + paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" + "github.com/dymensionxyz/dymension/v3/app/params" + dymnsutils "github.com/dymensionxyz/dymension/v3/x/dymns/utils" +) + +var _ paramtypes.ParamSet = (*Params)(nil) + +var ( + // KeyPriceParams is the key for the price params + KeyPriceParams = []byte("PriceParams") + + // KeyChainsParams is the key for the chains params + KeyChainsParams = []byte("ChainsParams") + + // KeyMiscParams is the key for the misc params + KeyMiscParams = []byte("MiscParams") +) + +const ( + defaultEndEpochHookIdentifier = "hour" +) + +// ParamKeyTable the param key table for launch module +func ParamKeyTable() paramtypes.KeyTable { + return paramtypes.NewKeyTable().RegisterParamSet(&Params{}) +} + +// DefaultParams returns a default set of parameters +func DefaultParams() Params { + return NewParams( + DefaultPriceParams(), + DefaultChainsParams(), + DefaultMiscParams(), + ) +} + +// DefaultPriceParams returns a default set of price parameters +func DefaultPriceParams() PriceParams { + return PriceParams{ + NamePriceSteps: []sdkmath.Int{ + sdk.NewInt(5000 /* DYM */).MulRaw(1e18), + sdk.NewInt(2500 /* DYM */).MulRaw(1e18), + sdk.NewInt(1000 /* DYM */).MulRaw(1e18), + sdk.NewInt(100 /* DYM */).MulRaw(1e18), + sdk.NewInt(5 /* DYM */).MulRaw(1e18), + }, + + AliasPriceSteps: []sdkmath.Int{ + sdk.NewInt(5000 /* DYM */).MulRaw(1e18), + sdk.NewInt(1000 /* DYM */).MulRaw(1e18), + sdk.NewInt(250 /* DYM */).MulRaw(1e18), + sdk.NewInt(100 /* DYM */).MulRaw(1e18), + sdk.NewInt(25 /* DYM */).MulRaw(1e18), + sdk.NewInt(10 /* DYM */).MulRaw(1e18), + sdk.NewInt(5 /* DYM */).MulRaw(1e18), + }, + + PriceExtends: sdk.NewInt(5 /* DYM */).MulRaw(1e18), + PriceDenom: params.BaseDenom, + MinOfferPrice: sdk.NewInt(10 /* DYM */).MulRaw(1e18), + } +} + +// DefaultChainsParams returns a default set of chains configuration +func DefaultChainsParams() ChainsParams { + return ChainsParams{ + AliasesOfChainIds: []AliasesOfChainId{ + { + ChainId: "dymension_1100-1", + Aliases: []string{"dym", "dymension"}, + }, + { + ChainId: "blumbus_111-1", + Aliases: []string{"blumbus"}, + }, + { + ChainId: "cosmoshub-4", + Aliases: []string{"cosmos"}, + }, + { + ChainId: "osmosis-1", + Aliases: []string{"osmosis"}, + }, + { + ChainId: "axelar-dojo-1", + Aliases: []string{"axelar"}, + }, + { + ChainId: "stride-1", + Aliases: []string{"stride"}, + }, + { + ChainId: "kava_2222-10", + Aliases: []string{"kava"}, + }, + { + ChainId: "evmos_9001-2", + Aliases: []string{"evmos"}, + }, + { + ChainId: "dymension_100-1", + Aliases: []string{"test"}, + }, + // reserves alias for non Cosmos-SDK chains + // TODO DymNS: review the list + { + ChainId: "bitcoin", + Aliases: []string{"btc"}, + }, + { + ChainId: "ethereum", + Aliases: []string{"eth", "ether"}, + }, + { + ChainId: "solana", + Aliases: []string{"sol"}, + }, + { + ChainId: "avalanche", + Aliases: []string{"avax"}, + }, + { + ChainId: "polygon", + Aliases: []string{"matic"}, + }, + { + ChainId: "polkadot", + Aliases: []string{"dot"}, + }, + }, + } +} + +// DefaultMiscParams returns a default set of misc parameters +func DefaultMiscParams() MiscParams { + return MiscParams{ + EndEpochHookIdentifier: defaultEndEpochHookIdentifier, + GracePeriodDuration: 30 * 24 * time.Hour, + SellOrderDuration: 3 * 24 * time.Hour, + EnableTradingName: true, + EnableTradingAlias: true, + } +} + +// NewParams creates a new Params object from given parameters +func NewParams( + price PriceParams, chains ChainsParams, misc MiscParams, +) Params { + return Params{ + Price: price, + Chains: chains, + Misc: misc, + } +} + +// ParamSetPairs get the params.ParamSet +func (m *Params) ParamSetPairs() paramtypes.ParamSetPairs { + return paramtypes.ParamSetPairs{ + paramtypes.NewParamSetPair(KeyPriceParams, &m.Price, validatePriceParams), + paramtypes.NewParamSetPair(KeyChainsParams, &m.Chains, validateChainsParams), + paramtypes.NewParamSetPair(KeyMiscParams, &m.Misc, validateMiscParams), + } +} + +// Validate checks that the parameters have valid values. +func (m *Params) Validate() error { + if err := m.Price.Validate(); err != nil { + return errorsmod.Wrapf(gerrc.ErrInvalidArgument, "price params: %v", err) + } + if err := m.Chains.Validate(); err != nil { + return errorsmod.Wrapf(gerrc.ErrInvalidArgument, "chains params: %v", err) + } + if err := m.Misc.Validate(); err != nil { + return errorsmod.Wrapf(gerrc.ErrInvalidArgument, "misc params: %v", err) + } + return nil +} + +// Validate checks that the PriceParams have valid values. +func (m PriceParams) Validate() error { + return validatePriceParams(m) +} + +// GetFirstYearDymNamePrice returns the price for the first year of a Dym-Name registration. +func (m PriceParams) GetFirstYearDymNamePrice(name string) sdkmath.Int { + return getElementAtIndexOrLast(m.NamePriceSteps, len(name)-1) +} + +// GetAliasPrice returns the one-off-payment price for an Alias registration. +func (m PriceParams) GetAliasPrice(alias string) sdkmath.Int { + return getElementAtIndexOrLast(m.AliasPriceSteps, len(alias)-1) +} + +// getElementAtIndexOrLast returns the element at the given index or the last element if the index is out of bounds. +func getElementAtIndexOrLast(elements []sdkmath.Int, index int) sdkmath.Int { + if index >= len(elements) { + return elements[len(elements)-1] + } + return elements[index] +} + +// Validate checks that the ChainsParams have valid values. +func (m ChainsParams) Validate() error { + return validateChainsParams(m) +} + +// Validate checks that the MiscParams have valid values. +func (m MiscParams) Validate() error { + return validateMiscParams(m) +} + +// validateEpochIdentifier checks if the given epoch identifier is valid. +func validateEpochIdentifier(i interface{}) error { + v, ok := i.(string) + if !ok { + return fmt.Errorf("invalid parameter type: %T", i) + } + if len(v) == 0 { + return fmt.Errorf("epoch identifier cannot be empty") + } + switch v { + case "hour", "day", "week": + default: + return fmt.Errorf("invalid epoch identifier: %s", v) + } + return nil +} + +// validatePriceParams checks if the given PriceParams are valid. +func validatePriceParams(i interface{}) error { + m, ok := i.(PriceParams) + if !ok { + return errorsmod.Wrapf(gerrc.ErrInvalidArgument, "invalid parameter type: %T", i) + } + + if err := validateNamePriceParams(m); err != nil { + return err + } + + if err := validateAliasPriceParams(m); err != nil { + return err + } + + if m.PriceDenom == "" { + return errorsmod.Wrap(gerrc.ErrInvalidArgument, "price denom cannot be empty") + } + + if err := (sdk.Coin{ + Denom: m.PriceDenom, + Amount: sdk.ZeroInt(), + }).Validate(); err != nil { + return errorsmod.Wrapf(gerrc.ErrInvalidArgument, "invalid price denom: %s", m.PriceDenom) + } + + if m.MinOfferPrice.IsNil() || m.MinOfferPrice.LT(MinPriceValue) { + return errorsmod.Wrapf(gerrc.ErrInvalidArgument, "min-offer-price is must be at least %s%s", MinPriceValue, m.PriceDenom) + } + + return nil +} + +// validateNamePriceParams checks if Dym-Name price in the given PriceParams are valid. +func validateNamePriceParams(m PriceParams) error { + if len(m.NamePriceSteps) < MinDymNamePriceStepsCount { + return errorsmod.Wrapf( + gerrc.ErrInvalidArgument, + "Dym-Name price steps must have at least %d steps", MinDymNamePriceStepsCount, + ) + } + + for i, namePriceStep := range m.NamePriceSteps { + if namePriceStep.IsNil() || namePriceStep.LT(MinPriceValue) { + return errorsmod.Wrapf(gerrc.ErrInvalidArgument, + "Dym-Name price step at index %d must be at least %s%s", i, MinPriceValue, m.PriceDenom, + ) + } + } + + if m.PriceExtends.IsNil() || m.PriceExtends.LT(MinPriceValue) { + return errorsmod.Wrapf(gerrc.ErrInvalidArgument, + "Dym-Name yearly extends price must be at least %s%s", MinPriceValue, m.PriceDenom, + ) + } + + for i := 0; i < len(m.NamePriceSteps)-1; i++ { + left := m.NamePriceSteps[i] + right := m.NamePriceSteps[i+1] + if left.LTE(right) { + return errorsmod.Wrapf(gerrc.ErrInvalidArgument, + "previous Dym-Name price step must be greater than the next step at: %d", i, + ) + } + } + + lastPriceStep := m.NamePriceSteps[len(m.NamePriceSteps)-1] + if lastPriceStep.LT(m.PriceExtends) { + return errorsmod.Wrapf( + gerrc.ErrInvalidArgument, + "Dym-Name price step for the first year must be greater or equals to the yearly extends price: %s < %s", + lastPriceStep, m.PriceExtends, + ) + } + + return nil +} + +// validateAliasPriceParams checks if Alias price in the given PriceParams are valid. +func validateAliasPriceParams(m PriceParams) error { + if len(m.AliasPriceSteps) < MinAliasPriceStepsCount { + return errorsmod.Wrapf( + gerrc.ErrInvalidArgument, + "alias price steps must have at least %d steps", MinAliasPriceStepsCount, + ) + } + + for i, aliasPriceStep := range m.AliasPriceSteps { + if aliasPriceStep.IsNil() || aliasPriceStep.LT(MinPriceValue) { + return errorsmod.Wrapf(gerrc.ErrInvalidArgument, + "alias price step at index %d must be at least %s%s", i, MinPriceValue, m.PriceDenom, + ) + } + } + + for i := 0; i < len(m.AliasPriceSteps)-1; i++ { + left := m.AliasPriceSteps[i] + right := m.AliasPriceSteps[i+1] + if left.LTE(right) { + return errorsmod.Wrapf(gerrc.ErrInvalidArgument, + "previous alias price step must be greater than the next step at: %d", i, + ) + } + } + + return nil +} + +// validateChainsParams checks if the given ChainsParams are valid. +func validateChainsParams(i interface{}) error { + m, ok := i.(ChainsParams) + if !ok { + return errorsmod.Wrapf(gerrc.ErrInvalidArgument, "invalid parameter type: %T", i) + } + + if err := validateAliasesOfChainIds(m.AliasesOfChainIds); err != nil { + return errorsmod.Wrapf(errors.Join(gerrc.ErrInvalidArgument, err), "alias of chain-id") + } + + return nil +} + +func validateAliasesOfChainIds(aliasesOfChainIds []AliasesOfChainId) error { + uniqueChainIdAliasAmongAliasConfig := make(map[string]bool) + // Describe usage of Go Map: only used for validation + for _, record := range aliasesOfChainIds { + chainID := record.ChainId + aliases := record.Aliases + if len(chainID) < 3 { + return fmt.Errorf("chain ID must be at least 3 characters: %s", chainID) + } + + if !dymnsutils.IsValidChainIdFormat(chainID) { + return fmt.Errorf("chain ID is not well-formed: %s", chainID) + } + + if _, ok := uniqueChainIdAliasAmongAliasConfig[chainID]; ok { + return fmt.Errorf( + "chain ID and alias must unique among all, found duplicated: %s", chainID, + ) + } + uniqueChainIdAliasAmongAliasConfig[chainID] = true + + for _, alias := range aliases { + if !dymnsutils.IsValidAlias(alias) { + return fmt.Errorf( + "alias is not well-formed: %s", alias, + ) + } + + if _, ok := uniqueChainIdAliasAmongAliasConfig[alias]; ok { + return fmt.Errorf( + "chain ID and alias must unique among all, found duplicated: %s", alias, + ) + } + uniqueChainIdAliasAmongAliasConfig[alias] = true + } + } + + return nil +} + +// validateMiscParams checks if the given MiscParams are valid. +func validateMiscParams(i interface{}) error { + m, ok := i.(MiscParams) + if !ok { + return errorsmod.Wrapf(gerrc.ErrInvalidArgument, "invalid parameter type: %T", i) + } + + if err := validateEpochIdentifier(m.EndEpochHookIdentifier); err != nil { + return errorsmod.Wrapf(gerrc.ErrInvalidArgument, "end epoch hook identifier: %v", err) + } + + const minGracePeriodDuration = 30 * 24 * time.Hour + if m.GracePeriodDuration < minGracePeriodDuration { + return errorsmod.Wrapf(gerrc.ErrInvalidArgument, "grace period duration cannot be less than: %s", minGracePeriodDuration) + } + + if m.SellOrderDuration <= 0 { + return errorsmod.Wrap(gerrc.ErrInvalidArgument, "Sell Orders duration can not be zero") + } + + return nil +} diff --git a/x/dymns/types/params.pb.go b/x/dymns/types/params.pb.go new file mode 100644 index 000000000..33fd3ec9b --- /dev/null +++ b/x/dymns/types/params.pb.go @@ -0,0 +1,1647 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: dymensionxyz/dymension/dymns/params.proto + +package types + +import ( + fmt "fmt" + _ "github.com/cosmos/cosmos-sdk/types" + github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" + _ "github.com/cosmos/gogoproto/gogoproto" + proto "github.com/cosmos/gogoproto/proto" + github_com_cosmos_gogoproto_types "github.com/cosmos/gogoproto/types" + _ "google.golang.org/protobuf/types/known/durationpb" + io "io" + math "math" + math_bits "math/bits" + time "time" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf +var _ = time.Kitchen + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// Params defines the parameters for the module. +type Params struct { + // price defines setting for pricing of Dym-Name and price-related parameters. + Price PriceParams `protobuf:"bytes,1,opt,name=price,proto3" json:"price" yaml:"price"` + // chains defines setting for prioritized aliases mapping. + Chains ChainsParams `protobuf:"bytes,2,opt,name=chains,proto3" json:"chains" yaml:"chains"` + // misc is group of miscellaneous parameters. + Misc MiscParams `protobuf:"bytes,3,opt,name=misc,proto3" json:"misc" yaml:"misc"` +} + +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_6097ac65688a2490, []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) GetPrice() PriceParams { + if m != nil { + return m.Price + } + return PriceParams{} +} + +func (m *Params) GetChains() ChainsParams { + if m != nil { + return m.Chains + } + return ChainsParams{} +} + +func (m *Params) GetMisc() MiscParams { + if m != nil { + return m.Misc + } + return MiscParams{} +} + +// PriceParams defines the pricing of Dym-Name and price-related parameters. +type PriceParams struct { + // name_price_steps holds the price steps configuration for Dym-Name registration, apply to the first year. + // The price of Dym-Name is calculated based on the number of letters. + // The first element is the price of 1 letter Dym-Name, the last element is the price of N+ letters Dym-Name. + // Minimum steps count allowed is 4, for 1/2/3/4+ letters Dym-Name. + NamePriceSteps []github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,1,rep,name=name_price_steps,json=namePriceSteps,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"name_price_steps" yaml:"name_price_steps"` + // alias_price_steps holds the price steps configuration for Alias registration, one off payment. + // The price of Alias is calculated based on the number of letters. + // The first element is the price of 1 letter Alias, the last element is the price of N+ letters Alias. + // Minimum steps count allowed is 4, for 1/2/3/4+ letters Alias. + AliasPriceSteps []github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,2,rep,name=alias_price_steps,json=aliasPriceSteps,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"alias_price_steps" yaml:"alias_price_steps"` + // price_extends is used to extends Dym-Name yearly, after the one-off payment for the first year. + PriceExtends github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,3,opt,name=price_extends,json=priceExtends,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"price_extends" yaml:"price_extends"` + // price_denom is the required denomination of the pricing setup and trading policy. + PriceDenom string `protobuf:"bytes,4,opt,name=price_denom,json=priceDenom,proto3" json:"price_denom,omitempty" yaml:"price_denom"` + // min_offer_price is minimum price allowed to place an offer. + // Mostly used to prevent spamming and abusing store with low price offers, + // so the value should not be so low. + MinOfferPrice github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,5,opt,name=min_offer_price,json=minOfferPrice,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"min_offer_price" yaml:"min_offer_price"` +} + +func (m *PriceParams) Reset() { *m = PriceParams{} } +func (m *PriceParams) String() string { return proto.CompactTextString(m) } +func (*PriceParams) ProtoMessage() {} +func (*PriceParams) Descriptor() ([]byte, []int) { + return fileDescriptor_6097ac65688a2490, []int{1} +} +func (m *PriceParams) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *PriceParams) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_PriceParams.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 *PriceParams) XXX_Merge(src proto.Message) { + xxx_messageInfo_PriceParams.Merge(m, src) +} +func (m *PriceParams) XXX_Size() int { + return m.Size() +} +func (m *PriceParams) XXX_DiscardUnknown() { + xxx_messageInfo_PriceParams.DiscardUnknown(m) +} + +var xxx_messageInfo_PriceParams proto.InternalMessageInfo + +func (m *PriceParams) GetPriceDenom() string { + if m != nil { + return m.PriceDenom + } + return "" +} + +// ChainsParams defines setting for prioritized aliases mapping. +type ChainsParams struct { + // aliases_of_chain_ids is set of chain-ids and their corresponding aliases, + // used for UX improvement like we can do my-name@cosmos instead of my-name@cosmoshub-4. + // + // This list is prioritized over Roll-App aliases + // the reason is to allow the community able to have control to fixes the potential problems with the aliases. + AliasesOfChainIds []AliasesOfChainId `protobuf:"bytes,1,rep,name=aliases_of_chain_ids,json=aliasesOfChainIds,proto3" json:"aliases_of_chain_ids" yaml:"aliases_of_chain_ids"` +} + +func (m *ChainsParams) Reset() { *m = ChainsParams{} } +func (m *ChainsParams) String() string { return proto.CompactTextString(m) } +func (*ChainsParams) ProtoMessage() {} +func (*ChainsParams) Descriptor() ([]byte, []int) { + return fileDescriptor_6097ac65688a2490, []int{2} +} +func (m *ChainsParams) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ChainsParams) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ChainsParams.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 *ChainsParams) XXX_Merge(src proto.Message) { + xxx_messageInfo_ChainsParams.Merge(m, src) +} +func (m *ChainsParams) XXX_Size() int { + return m.Size() +} +func (m *ChainsParams) XXX_DiscardUnknown() { + xxx_messageInfo_ChainsParams.DiscardUnknown(m) +} + +var xxx_messageInfo_ChainsParams proto.InternalMessageInfo + +func (m *ChainsParams) GetAliasesOfChainIds() []AliasesOfChainId { + if m != nil { + return m.AliasesOfChainIds + } + return nil +} + +// AliasesOfChainId defines the multiple-aliases of a chain id. +type AliasesOfChainId struct { + // chain_id which owned the aliases. + ChainId string `protobuf:"bytes,1,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty" yaml:"chain_id"` + // aliases is a set of aliases of the chain id for UX improvement, + // like we can do my-name@cosmos instead of my-name@cosmoshub-4 + Aliases []string `protobuf:"bytes,2,rep,name=aliases,proto3" json:"aliases,omitempty" yaml:"aliases"` +} + +func (m *AliasesOfChainId) Reset() { *m = AliasesOfChainId{} } +func (m *AliasesOfChainId) String() string { return proto.CompactTextString(m) } +func (*AliasesOfChainId) ProtoMessage() {} +func (*AliasesOfChainId) Descriptor() ([]byte, []int) { + return fileDescriptor_6097ac65688a2490, []int{3} +} +func (m *AliasesOfChainId) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *AliasesOfChainId) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_AliasesOfChainId.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 *AliasesOfChainId) XXX_Merge(src proto.Message) { + xxx_messageInfo_AliasesOfChainId.Merge(m, src) +} +func (m *AliasesOfChainId) XXX_Size() int { + return m.Size() +} +func (m *AliasesOfChainId) XXX_DiscardUnknown() { + xxx_messageInfo_AliasesOfChainId.DiscardUnknown(m) +} + +var xxx_messageInfo_AliasesOfChainId proto.InternalMessageInfo + +func (m *AliasesOfChainId) GetChainId() string { + if m != nil { + return m.ChainId + } + return "" +} + +func (m *AliasesOfChainId) GetAliases() []string { + if m != nil { + return m.Aliases + } + return nil +} + +// MiscParams defines group of miscellaneous parameters. +type MiscParams struct { + // end_epoch_hook_identifier is the identifier of the end epoch hook. + EndEpochHookIdentifier string `protobuf:"bytes,1,opt,name=end_epoch_hook_identifier,json=endEpochHookIdentifier,proto3" json:"end_epoch_hook_identifier,omitempty" yaml:"end_epoch_hook_identifier"` + // grace_period_duration is the amount of time that the former owner of an expired Dym-Name + // can renew it before completely lost. + GracePeriodDuration time.Duration `protobuf:"bytes,2,opt,name=grace_period_duration,json=gracePeriodDuration,proto3,stdduration" json:"grace_period_duration" yaml:"grace_period_duration"` + // sell_order_duration is the amount of time of a Sell-Order from created to expired. + SellOrderDuration time.Duration `protobuf:"bytes,3,opt,name=sell_order_duration,json=sellOrderDuration,proto3,stdduration" json:"sell_order_duration" yaml:"sell_order_duration"` + // enable_trading_name is the flag to enable trading of Dym-Name. + // To be used to stop trading of Dym-Name when needed. + EnableTradingName bool `protobuf:"varint,4,opt,name=enable_trading_name,json=enableTradingName,proto3" json:"enable_trading_name,omitempty"` + // enable_trading_alias is the flag to enable trading of Alias. + // To be used in the future when Alias trading implementation is ready + // or disable trading of Alias when needed. + EnableTradingAlias bool `protobuf:"varint,5,opt,name=enable_trading_alias,json=enableTradingAlias,proto3" json:"enable_trading_alias,omitempty"` +} + +func (m *MiscParams) Reset() { *m = MiscParams{} } +func (m *MiscParams) String() string { return proto.CompactTextString(m) } +func (*MiscParams) ProtoMessage() {} +func (*MiscParams) Descriptor() ([]byte, []int) { + return fileDescriptor_6097ac65688a2490, []int{4} +} +func (m *MiscParams) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MiscParams) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MiscParams.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 *MiscParams) XXX_Merge(src proto.Message) { + xxx_messageInfo_MiscParams.Merge(m, src) +} +func (m *MiscParams) XXX_Size() int { + return m.Size() +} +func (m *MiscParams) XXX_DiscardUnknown() { + xxx_messageInfo_MiscParams.DiscardUnknown(m) +} + +var xxx_messageInfo_MiscParams proto.InternalMessageInfo + +func (m *MiscParams) GetEndEpochHookIdentifier() string { + if m != nil { + return m.EndEpochHookIdentifier + } + return "" +} + +func (m *MiscParams) GetGracePeriodDuration() time.Duration { + if m != nil { + return m.GracePeriodDuration + } + return 0 +} + +func (m *MiscParams) GetSellOrderDuration() time.Duration { + if m != nil { + return m.SellOrderDuration + } + return 0 +} + +func (m *MiscParams) GetEnableTradingName() bool { + if m != nil { + return m.EnableTradingName + } + return false +} + +func (m *MiscParams) GetEnableTradingAlias() bool { + if m != nil { + return m.EnableTradingAlias + } + return false +} + +func init() { + proto.RegisterType((*Params)(nil), "dymensionxyz.dymension.dymns.Params") + proto.RegisterType((*PriceParams)(nil), "dymensionxyz.dymension.dymns.PriceParams") + proto.RegisterType((*ChainsParams)(nil), "dymensionxyz.dymension.dymns.ChainsParams") + proto.RegisterType((*AliasesOfChainId)(nil), "dymensionxyz.dymension.dymns.AliasesOfChainId") + proto.RegisterType((*MiscParams)(nil), "dymensionxyz.dymension.dymns.MiscParams") +} + +func init() { + proto.RegisterFile("dymensionxyz/dymension/dymns/params.proto", fileDescriptor_6097ac65688a2490) +} + +var fileDescriptor_6097ac65688a2490 = []byte{ + // 794 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x95, 0xcf, 0x6f, 0xe3, 0x44, + 0x14, 0xc7, 0xe3, 0xa6, 0xdb, 0x6d, 0x27, 0xed, 0x76, 0x33, 0xc9, 0x96, 0x6c, 0x59, 0xc5, 0xd5, + 0x80, 0x56, 0x59, 0x04, 0x36, 0xdb, 0x3d, 0x20, 0x71, 0x23, 0xec, 0xa2, 0x0d, 0x02, 0x5a, 0x0c, + 0x1c, 0xe0, 0x32, 0x72, 0xec, 0x49, 0x32, 0x4a, 0x3c, 0x63, 0x3c, 0x6e, 0x69, 0x38, 0x73, 0x45, + 0x42, 0x48, 0x48, 0xfc, 0x2f, 0xfc, 0x03, 0x3d, 0xf6, 0x88, 0x38, 0x18, 0xd4, 0xfe, 0x07, 0xf9, + 0x0b, 0x90, 0xdf, 0x8c, 0x53, 0x27, 0xb4, 0x41, 0x3d, 0x25, 0x6f, 0xde, 0x7b, 0x9f, 0xef, 0xfc, + 0x78, 0xef, 0x19, 0x3d, 0x0b, 0xa7, 0x11, 0x13, 0x8a, 0x4b, 0x71, 0x36, 0xfd, 0xd1, 0x9d, 0x1b, + 0xf9, 0x3f, 0xa1, 0xdc, 0xd8, 0x4f, 0xfc, 0x48, 0x39, 0x71, 0x22, 0x53, 0x89, 0x9f, 0x94, 0x43, + 0x9d, 0xb9, 0xe1, 0x40, 0xe8, 0x7e, 0x73, 0x28, 0x87, 0x12, 0x02, 0xdd, 0xfc, 0x9f, 0xce, 0xd9, + 0x6f, 0x07, 0x52, 0x45, 0x52, 0xb9, 0x7d, 0x5f, 0x31, 0xf7, 0xf4, 0x79, 0x9f, 0xa5, 0xfe, 0x73, + 0x37, 0x90, 0x5c, 0x14, 0xfe, 0xa1, 0x94, 0xc3, 0x09, 0x73, 0xc1, 0xea, 0x9f, 0x0c, 0xdc, 0xf0, + 0x24, 0xf1, 0xd3, 0x9c, 0x0a, 0x2b, 0xe4, 0xe7, 0x35, 0xb4, 0x71, 0x0c, 0x9b, 0xc0, 0xdf, 0xa0, + 0x7b, 0x71, 0xc2, 0x03, 0xd6, 0xb2, 0x0e, 0xac, 0x4e, 0xed, 0xf0, 0x99, 0xb3, 0x6a, 0x3b, 0xce, + 0x71, 0x1e, 0xaa, 0x33, 0xbb, 0xcd, 0xf3, 0xcc, 0xae, 0xcc, 0x32, 0x7b, 0x7b, 0xea, 0x47, 0x93, + 0x0f, 0x09, 0x50, 0x88, 0xa7, 0x69, 0xf8, 0x5b, 0xb4, 0x11, 0x8c, 0x7c, 0x2e, 0x54, 0x6b, 0x0d, + 0xb8, 0xef, 0xac, 0xe6, 0x7e, 0x0c, 0xb1, 0x06, 0xfc, 0xc8, 0x80, 0x77, 0x34, 0x58, 0x73, 0x88, + 0x67, 0x80, 0xf8, 0x4b, 0xb4, 0x1e, 0x71, 0x15, 0xb4, 0xaa, 0x00, 0xee, 0xac, 0x06, 0x7f, 0xce, + 0x55, 0x60, 0xb0, 0x0d, 0x83, 0xad, 0x69, 0x6c, 0xce, 0x20, 0x1e, 0xa0, 0xc8, 0xaf, 0xeb, 0xa8, + 0x56, 0x3a, 0x1a, 0x56, 0xe8, 0xa1, 0xf0, 0x23, 0x46, 0xe1, 0x2c, 0x54, 0xa5, 0x2c, 0x56, 0x2d, + 0xeb, 0xa0, 0xda, 0xd9, 0xea, 0xf6, 0x72, 0xc8, 0x5f, 0x99, 0xfd, 0x74, 0xc8, 0xd3, 0xd1, 0x49, + 0xdf, 0x09, 0x64, 0xe4, 0x9a, 0xc7, 0xd0, 0x3f, 0xef, 0xa9, 0x70, 0xec, 0xa6, 0xd3, 0x98, 0x29, + 0xa7, 0x27, 0xd2, 0x59, 0x66, 0xbf, 0xa1, 0xe5, 0x96, 0x79, 0xc4, 0x7b, 0x90, 0x2f, 0x81, 0xea, + 0x57, 0xf9, 0x02, 0x3e, 0x45, 0x75, 0x7f, 0xc2, 0x7d, 0xb5, 0xa0, 0xba, 0x06, 0xaa, 0x9f, 0xde, + 0x59, 0xb5, 0xa5, 0x55, 0xff, 0x03, 0x24, 0xde, 0x2e, 0xac, 0x95, 0x74, 0xc7, 0x68, 0x47, 0x07, + 0xb0, 0xb3, 0x94, 0x89, 0x50, 0xc1, 0xc5, 0x6e, 0x75, 0x3f, 0xb9, 0xb3, 0x66, 0xb3, 0x54, 0x08, + 0x05, 0x8c, 0x78, 0xdb, 0x60, 0xbf, 0xd2, 0x26, 0xfe, 0x00, 0xd5, 0xb4, 0x3f, 0x64, 0x42, 0x46, + 0xad, 0x75, 0x90, 0xda, 0x9b, 0x65, 0x36, 0x2e, 0x27, 0x83, 0x93, 0x78, 0x08, 0xac, 0x97, 0xb9, + 0x81, 0x63, 0xb4, 0x1b, 0x71, 0x41, 0xe5, 0x60, 0xc0, 0x12, 0x7d, 0xa0, 0xd6, 0x3d, 0x48, 0x7e, + 0x7d, 0xe7, 0x7d, 0xee, 0x15, 0x05, 0xb0, 0x80, 0x23, 0xde, 0x4e, 0xc4, 0xc5, 0x51, 0xbe, 0x00, + 0x97, 0x43, 0x7e, 0xb3, 0xd0, 0x76, 0xb9, 0x2e, 0xf1, 0x4f, 0x16, 0x6a, 0xc2, 0xe5, 0x31, 0x45, + 0xe5, 0x80, 0x42, 0x39, 0x52, 0x1e, 0xea, 0xd2, 0xa8, 0x1d, 0x3a, 0xab, 0x2b, 0xf1, 0x23, 0x9d, + 0x79, 0x34, 0x00, 0x66, 0x2f, 0xec, 0xbe, 0x65, 0xea, 0xf1, 0xcd, 0xd2, 0x53, 0x2d, 0x91, 0x89, + 0x57, 0xf7, 0x97, 0xd2, 0x14, 0x89, 0xd1, 0xc3, 0x65, 0x16, 0x76, 0xd0, 0x66, 0x91, 0x04, 0x8d, + 0xbc, 0xd5, 0x6d, 0xcc, 0x32, 0x7b, 0xb7, 0xd4, 0x40, 0x94, 0x87, 0xc4, 0xbb, 0x1f, 0x98, 0xf8, + 0x77, 0xd1, 0x7d, 0x03, 0x36, 0x15, 0x86, 0x67, 0x99, 0xfd, 0x60, 0x61, 0x23, 0xc4, 0x2b, 0x42, + 0xc8, 0x1f, 0x55, 0x84, 0xae, 0x1b, 0x09, 0x53, 0xf4, 0x98, 0x89, 0x90, 0xb2, 0x58, 0x06, 0x23, + 0x3a, 0x92, 0x72, 0x4c, 0x79, 0xc8, 0x44, 0xca, 0x07, 0x9c, 0x25, 0x46, 0xfd, 0xed, 0x59, 0x66, + 0x1f, 0x68, 0xdc, 0xad, 0xa1, 0xc4, 0xdb, 0x63, 0x22, 0x7c, 0x95, 0xbb, 0x5e, 0x4b, 0x39, 0xee, + 0xcd, 0x1d, 0xf8, 0x07, 0xf4, 0x68, 0x98, 0xf8, 0x01, 0xa3, 0x31, 0x4b, 0xb8, 0x0c, 0x69, 0x31, + 0xbd, 0xcc, 0x2c, 0x79, 0xec, 0xe8, 0xf1, 0xe6, 0x14, 0xe3, 0xcd, 0x79, 0x69, 0x02, 0xba, 0x1d, + 0x73, 0xa7, 0x4f, 0xb4, 0xf6, 0x8d, 0x14, 0xf2, 0xfb, 0xdf, 0xb6, 0xe5, 0x35, 0xc0, 0x77, 0x0c, + 0xae, 0x22, 0x1d, 0x7f, 0x8f, 0x1a, 0x8a, 0x4d, 0x26, 0x54, 0x26, 0x21, 0x4b, 0xae, 0x65, 0xab, + 0xff, 0x27, 0xfb, 0xd4, 0xc8, 0xee, 0x6b, 0xd9, 0x1b, 0x18, 0x5a, 0xb4, 0x9e, 0x7b, 0x8e, 0x72, + 0xc7, 0x5c, 0xd2, 0x41, 0x0d, 0x26, 0xfc, 0xfe, 0x84, 0xd1, 0x34, 0xf1, 0x43, 0x2e, 0x86, 0x34, + 0x1f, 0x0b, 0xd0, 0x18, 0x9b, 0x5e, 0x5d, 0xbb, 0xbe, 0xd6, 0x9e, 0x2f, 0xfc, 0x88, 0xe1, 0xf7, + 0x51, 0x73, 0x29, 0x1e, 0x5e, 0x09, 0x9a, 0x61, 0xd3, 0xc3, 0x0b, 0x09, 0x50, 0x26, 0xdd, 0xcf, + 0xce, 0x2f, 0xdb, 0xd6, 0xc5, 0x65, 0xdb, 0xfa, 0xe7, 0xb2, 0x6d, 0xfd, 0x72, 0xd5, 0xae, 0x5c, + 0x5c, 0xb5, 0x2b, 0x7f, 0x5e, 0xb5, 0x2b, 0xdf, 0x1d, 0x96, 0x5a, 0xe6, 0x96, 0x0f, 0xd6, 0xe9, + 0x0b, 0xf7, 0xcc, 0x7c, 0xb5, 0xa0, 0x85, 0xfa, 0x1b, 0x70, 0xfa, 0x17, 0xff, 0x06, 0x00, 0x00, + 0xff, 0xff, 0x7f, 0x92, 0xf0, 0x4d, 0xe2, 0x06, 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 + { + size, err := m.Misc.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintParams(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + { + size, err := m.Chains.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintParams(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + { + size, err := m.Price.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintParams(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *PriceParams) 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 *PriceParams) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *PriceParams) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size := m.MinOfferPrice.Size() + i -= size + if _, err := m.MinOfferPrice.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintParams(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + if len(m.PriceDenom) > 0 { + i -= len(m.PriceDenom) + copy(dAtA[i:], m.PriceDenom) + i = encodeVarintParams(dAtA, i, uint64(len(m.PriceDenom))) + i-- + dAtA[i] = 0x22 + } + { + size := m.PriceExtends.Size() + i -= size + if _, err := m.PriceExtends.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintParams(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + if len(m.AliasPriceSteps) > 0 { + for iNdEx := len(m.AliasPriceSteps) - 1; iNdEx >= 0; iNdEx-- { + { + size := m.AliasPriceSteps[iNdEx].Size() + i -= size + if _, err := m.AliasPriceSteps[iNdEx].MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintParams(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if len(m.NamePriceSteps) > 0 { + for iNdEx := len(m.NamePriceSteps) - 1; iNdEx >= 0; iNdEx-- { + { + size := m.NamePriceSteps[iNdEx].Size() + i -= size + if _, err := m.NamePriceSteps[iNdEx].MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintParams(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *ChainsParams) 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 *ChainsParams) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ChainsParams) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.AliasesOfChainIds) > 0 { + for iNdEx := len(m.AliasesOfChainIds) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.AliasesOfChainIds[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintParams(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *AliasesOfChainId) 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 *AliasesOfChainId) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *AliasesOfChainId) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Aliases) > 0 { + for iNdEx := len(m.Aliases) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Aliases[iNdEx]) + copy(dAtA[i:], m.Aliases[iNdEx]) + i = encodeVarintParams(dAtA, i, uint64(len(m.Aliases[iNdEx]))) + i-- + dAtA[i] = 0x12 + } + } + if len(m.ChainId) > 0 { + i -= len(m.ChainId) + copy(dAtA[i:], m.ChainId) + i = encodeVarintParams(dAtA, i, uint64(len(m.ChainId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MiscParams) 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 *MiscParams) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MiscParams) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.EnableTradingAlias { + i-- + if m.EnableTradingAlias { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x28 + } + if m.EnableTradingName { + i-- + if m.EnableTradingName { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x20 + } + n4, err4 := github_com_cosmos_gogoproto_types.StdDurationMarshalTo(m.SellOrderDuration, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdDuration(m.SellOrderDuration):]) + if err4 != nil { + return 0, err4 + } + i -= n4 + i = encodeVarintParams(dAtA, i, uint64(n4)) + i-- + dAtA[i] = 0x1a + n5, err5 := github_com_cosmos_gogoproto_types.StdDurationMarshalTo(m.GracePeriodDuration, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdDuration(m.GracePeriodDuration):]) + if err5 != nil { + return 0, err5 + } + i -= n5 + i = encodeVarintParams(dAtA, i, uint64(n5)) + i-- + dAtA[i] = 0x12 + if len(m.EndEpochHookIdentifier) > 0 { + i -= len(m.EndEpochHookIdentifier) + copy(dAtA[i:], m.EndEpochHookIdentifier) + i = encodeVarintParams(dAtA, i, uint64(len(m.EndEpochHookIdentifier))) + 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 = m.Price.Size() + n += 1 + l + sovParams(uint64(l)) + l = m.Chains.Size() + n += 1 + l + sovParams(uint64(l)) + l = m.Misc.Size() + n += 1 + l + sovParams(uint64(l)) + return n +} + +func (m *PriceParams) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.NamePriceSteps) > 0 { + for _, e := range m.NamePriceSteps { + l = e.Size() + n += 1 + l + sovParams(uint64(l)) + } + } + if len(m.AliasPriceSteps) > 0 { + for _, e := range m.AliasPriceSteps { + l = e.Size() + n += 1 + l + sovParams(uint64(l)) + } + } + l = m.PriceExtends.Size() + n += 1 + l + sovParams(uint64(l)) + l = len(m.PriceDenom) + if l > 0 { + n += 1 + l + sovParams(uint64(l)) + } + l = m.MinOfferPrice.Size() + n += 1 + l + sovParams(uint64(l)) + return n +} + +func (m *ChainsParams) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.AliasesOfChainIds) > 0 { + for _, e := range m.AliasesOfChainIds { + l = e.Size() + n += 1 + l + sovParams(uint64(l)) + } + } + return n +} + +func (m *AliasesOfChainId) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ChainId) + if l > 0 { + n += 1 + l + sovParams(uint64(l)) + } + if len(m.Aliases) > 0 { + for _, s := range m.Aliases { + l = len(s) + n += 1 + l + sovParams(uint64(l)) + } + } + return n +} + +func (m *MiscParams) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.EndEpochHookIdentifier) + if l > 0 { + n += 1 + l + sovParams(uint64(l)) + } + l = github_com_cosmos_gogoproto_types.SizeOfStdDuration(m.GracePeriodDuration) + n += 1 + l + sovParams(uint64(l)) + l = github_com_cosmos_gogoproto_types.SizeOfStdDuration(m.SellOrderDuration) + n += 1 + l + sovParams(uint64(l)) + if m.EnableTradingName { + n += 2 + } + if m.EnableTradingAlias { + n += 2 + } + 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 Price", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowParams + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthParams + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthParams + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Price.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Chains", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowParams + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthParams + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthParams + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Chains.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Misc", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowParams + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthParams + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthParams + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Misc.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + 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 (m *PriceParams) 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: PriceParams: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PriceParams: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NamePriceSteps", 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 + } + var v github_com_cosmos_cosmos_sdk_types.Int + m.NamePriceSteps = append(m.NamePriceSteps, v) + if err := m.NamePriceSteps[len(m.NamePriceSteps)-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 AliasPriceSteps", 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 + } + var v github_com_cosmos_cosmos_sdk_types.Int + m.AliasPriceSteps = append(m.AliasPriceSteps, v) + if err := m.AliasPriceSteps[len(m.AliasPriceSteps)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PriceExtends", 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 + } + if err := m.PriceExtends.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PriceDenom", 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.PriceDenom = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field MinOfferPrice", 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 + } + if err := m.MinOfferPrice.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + 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 (m *ChainsParams) 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: ChainsParams: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ChainsParams: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AliasesOfChainIds", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowParams + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthParams + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthParams + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.AliasesOfChainIds = append(m.AliasesOfChainIds, AliasesOfChainId{}) + if err := m.AliasesOfChainIds[len(m.AliasesOfChainIds)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + 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 (m *AliasesOfChainId) 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: AliasesOfChainId: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: AliasesOfChainId: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ChainId", 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.ChainId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Aliases", 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.Aliases = append(m.Aliases, 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 (m *MiscParams) 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: MiscParams: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MiscParams: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field EndEpochHookIdentifier", 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.EndEpochHookIdentifier = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field GracePeriodDuration", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowParams + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthParams + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthParams + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := github_com_cosmos_gogoproto_types.StdDurationUnmarshal(&m.GracePeriodDuration, dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SellOrderDuration", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowParams + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthParams + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthParams + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := github_com_cosmos_gogoproto_types.StdDurationUnmarshal(&m.SellOrderDuration, dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field EnableTradingName", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowParams + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.EnableTradingName = bool(v != 0) + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field EnableTradingAlias", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowParams + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.EnableTradingAlias = bool(v != 0) + 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/dymns/types/params_test.go b/x/dymns/types/params_test.go new file mode 100644 index 000000000..072404640 --- /dev/null +++ b/x/dymns/types/params_test.go @@ -0,0 +1,559 @@ +package types + +import ( + "fmt" + "testing" + "time" + + sdkmath "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" +) + +func TestParamKeyTable(t *testing.T) { + m := ParamKeyTable() + require.NotNil(t, m) +} + +func TestDefaultParams(t *testing.T) { + moduleParams := DefaultParams() + require.NoError(t, (&moduleParams).Validate()) +} + +func TestNewParams(t *testing.T) { + moduleParams := NewParams( + PriceParams{ + PriceDenom: "a", + }, + ChainsParams{ + AliasesOfChainIds: []AliasesOfChainId{ + { + ChainId: "dymension_1100-1", + Aliases: []string{"dym", "dymension"}, + }, + }, + }, + MiscParams{ + EndEpochHookIdentifier: "c", + GracePeriodDuration: 666 * time.Hour, + SellOrderDuration: 333 * time.Hour, + }, + ) + require.Equal(t, "a", moduleParams.Price.PriceDenom) + require.Len(t, moduleParams.Chains.AliasesOfChainIds, 1) + require.Equal(t, "dymension_1100-1", moduleParams.Chains.AliasesOfChainIds[0].ChainId) + require.Len(t, moduleParams.Chains.AliasesOfChainIds[0].Aliases, 2) + require.Equal(t, "c", moduleParams.Misc.EndEpochHookIdentifier) + require.Equal(t, 666.0, moduleParams.Misc.GracePeriodDuration.Hours()) + require.Equal(t, 333.0, moduleParams.Misc.SellOrderDuration.Hours()) +} + +func TestDefaultPriceParams(t *testing.T) { + priceParams := DefaultPriceParams() + require.NoError(t, priceParams.Validate()) + + t.Run("ensure setting is correct", func(t *testing.T) { + i, ok := sdk.NewIntFromString("5" + "000000000000000000") + require.True(t, ok) + require.Equal(t, i, priceParams.NamePriceSteps[4]) + }) + + t.Run("ensure price setting is at least 1 DYM", func(t *testing.T) { + oneDym, ok := sdk.NewIntFromString("1" + "000000000000000000") + require.True(t, ok) + if oneDym.GT(priceParams.NamePriceSteps[4]) { + require.Fail(t, "price should be at least 1 DYM") + } + if oneDym.GT(priceParams.PriceExtends) { + require.Fail(t, "price should be at least 1 DYM") + } + }) +} + +func TestDefaultChainsParams(t *testing.T) { + require.NoError(t, DefaultChainsParams().Validate()) +} + +func TestDefaultMiscParams(t *testing.T) { + require.NoError(t, DefaultMiscParams().Validate()) +} + +func TestParams_ParamSetPairs(t *testing.T) { + moduleParams := DefaultParams() + paramSetPairs := (&moduleParams).ParamSetPairs() + require.Len(t, paramSetPairs, 3) +} + +func TestParams_Validate(t *testing.T) { + moduleParams := DefaultParams() + require.NoError(t, (&moduleParams).Validate()) + + moduleParams = DefaultParams() + moduleParams.Price.MinOfferPrice = sdk.ZeroInt() + require.Error(t, (&moduleParams).Validate()) + + moduleParams = DefaultParams() + moduleParams.Chains.AliasesOfChainIds = []AliasesOfChainId{{ChainId: "@"}} + require.Error(t, (&moduleParams).Validate()) + + moduleParams = DefaultParams() + moduleParams.Misc.SellOrderDuration = 0 + require.Error(t, (&moduleParams).Validate()) +} + +func TestPriceParams_Validate(t *testing.T) { + t.Run("pass - default must be valid", func(t *testing.T) { + defaultPriceParams := DefaultPriceParams() + + // copy to ensure no new fields are added + validPriceParams := PriceParams{ + NamePriceSteps: defaultPriceParams.NamePriceSteps, + AliasPriceSteps: defaultPriceParams.AliasPriceSteps, + PriceExtends: defaultPriceParams.PriceExtends, + PriceDenom: defaultPriceParams.PriceDenom, + MinOfferPrice: defaultPriceParams.MinOfferPrice, + } + + require.NoError(t, validPriceParams.Validate()) + }) + + t.Run("fail - price steps must be ordered descending", func(t *testing.T) { + for i := 0; i < len(DefaultPriceParams().NamePriceSteps)-1; i++ { + priceParams := DefaultPriceParams() + priceParams.NamePriceSteps[i], priceParams.NamePriceSteps[i+1] = priceParams.NamePriceSteps[i+1], priceParams.NamePriceSteps[i] + require.ErrorContains(t, + priceParams.Validate(), + fmt.Sprintf("previous Dym-Name price step must be greater than the next step at: %d", i), + ) + } + + for i := 0; i < len(DefaultPriceParams().AliasPriceSteps)-1; i++ { + priceParams := DefaultPriceParams() + priceParams.AliasPriceSteps[i], priceParams.AliasPriceSteps[i+1] = priceParams.AliasPriceSteps[i+1], priceParams.AliasPriceSteps[i] + require.ErrorContains(t, + priceParams.Validate(), + fmt.Sprintf("previous alias price step must be greater than the next step at: %d", i), + ) + } + }) + + t.Run("mix - minimum price step count", func(t *testing.T) { + defaultPriceParams := DefaultPriceParams() + + for size := 0; size <= (MinDymNamePriceStepsCount+MinAliasPriceStepsCount)*2; size++ { + priceSteps := make([]sdkmath.Int, size) + for i := 0; i < size; i++ { + priceSteps[i] = sdk.NewInt(int64(1000 - i)).MulRaw(1e18) + } + + m1 := defaultPriceParams + m1.NamePriceSteps = priceSteps + if size >= MinDymNamePriceStepsCount { + require.NoError(t, m1.Validate()) + } else { + require.ErrorContains(t, m1.Validate(), "price steps must have at least") + } + + m2 := defaultPriceParams + m2.AliasPriceSteps = priceSteps + if size >= MinAliasPriceStepsCount { + require.NoError(t, m2.Validate()) + } else { + require.ErrorContains(t, m2.Validate(), "price steps must have at least") + } + } + + require.GreaterOrEqual(t, MinDymNamePriceStepsCount, 3, "why it so low?") + require.GreaterOrEqual(t, MinAliasPriceStepsCount, 3, "why it so low?") + }) + + t.Run("fail - price denom", func(t *testing.T) { + m := DefaultPriceParams() + + m.PriceDenom = "" + require.ErrorContains(t, m.Validate(), "price denom cannot be empty") + + for _, denom := range []string{"-", "--", "0"} { + m.PriceDenom = denom + require.ErrorContains(t, m.Validate(), "invalid price denom") + } + }) + + t.Run("fail - price is too low", func(t *testing.T) { + defaultPriceParams := DefaultPriceParams() + + type tc struct { + name string + modifier func(PriceParams, sdkmath.Int) PriceParams + } + + tests := []tc{ + { + name: "price extends", + modifier: func(p PriceParams, v sdkmath.Int) PriceParams { + p.PriceExtends = v + return p + }, + }, + { + name: "min offer price", + modifier: func(p PriceParams, v sdkmath.Int) PriceParams { + p.MinOfferPrice = v + return p + }, + }, + } + + for i := 0; i < len(defaultPriceParams.NamePriceSteps); i++ { + tests = append(tests, tc{ + name: fmt.Sprintf("name price steps [%d]", i), + modifier: func(p PriceParams, v sdkmath.Int) PriceParams { + p.NamePriceSteps[i] = v + return p + }, + }) + } + + for i := 0; i < len(defaultPriceParams.AliasPriceSteps); i++ { + tests = append(tests, tc{ + name: fmt.Sprintf("alias price steps [%d]", i), + modifier: func(p PriceParams, v sdkmath.Int) PriceParams { + p.AliasPriceSteps[i] = v + return p + }, + }) + } + + for _, test := range tests { + for _, badPrice := range []sdkmath.Int{{}, sdkmath.NewInt(-1), sdkmath.ZeroInt(), MinPriceValue.Sub(sdkmath.NewInt(1))} { + t.Run(fmt.Sprintf("%s with v = %v", test.name, badPrice), func(t *testing.T) { + p := test.modifier(DefaultPriceParams(), badPrice) + err := (&p).Validate() + require.Error(t, err) + require.Contains(t, err.Error(), "must be at least") + }) + } + } + }) + + t.Run("fail - yearly extends price can not be higher than last step price", func(t *testing.T) { + defaultPriceParams := DefaultPriceParams() + defaultPriceParams.PriceExtends = defaultPriceParams.NamePriceSteps[len(defaultPriceParams.NamePriceSteps)-1].AddRaw(1) + + require.ErrorContains( + t, defaultPriceParams.Validate(), + "Dym-Name price step for the first year must be greater or equals to the yearly extends price", + ) + }) + + t.Run("fail - invalid type", func(t *testing.T) { + require.Error(t, validatePriceParams("hello world")) + require.Error(t, validatePriceParams(&PriceParams{}), "not accept pointer") + }) +} + +func TestPriceParams_GetPrice(t *testing.T) { + priceParams := DefaultPriceParams() + + t.Run("for name length <= number of price steps, use the corresponding price step", func(t *testing.T) { + runes := make([]rune, 0) + for length := 1; length <= len(priceParams.NamePriceSteps); length++ { + runes = append(runes, 'a') + require.Equal(t, priceParams.NamePriceSteps[length-1], priceParams.GetFirstYearDymNamePrice(string(runes))) + } + }) + + t.Run("for name length >= number of price steps, use the last price step", func(t *testing.T) { + wantPrice := priceParams.NamePriceSteps[len(priceParams.NamePriceSteps)-1] + + runes := make([]rune, len(priceParams.NamePriceSteps)) + for i := range runes { + runes[i] = 'a' + } + require.Equal(t, wantPrice, priceParams.GetFirstYearDymNamePrice(string(runes))) + + for extraLettersCount := 1; extraLettersCount < 1000; extraLettersCount++ { + runes = append(runes, 'a') + require.Equal(t, wantPrice, priceParams.GetFirstYearDymNamePrice(string(runes))) + } + }) + + t.Run("for alias length <= number of price steps, use the corresponding price step", func(t *testing.T) { + runes := make([]rune, 0) + for length := 1; length <= len(priceParams.AliasPriceSteps); length++ { + runes = append(runes, 'a') + require.Equal(t, priceParams.AliasPriceSteps[length-1], priceParams.GetAliasPrice(string(runes))) + } + }) + + t.Run("for alias length >= number of price steps, use the last price step", func(t *testing.T) { + wantPrice := priceParams.AliasPriceSteps[len(priceParams.AliasPriceSteps)-1] + + runes := make([]rune, len(priceParams.AliasPriceSteps)) + for i := range runes { + runes[i] = 'a' + } + require.Equal(t, wantPrice, priceParams.GetAliasPrice(string(runes))) + + for extraLettersCount := 1; extraLettersCount < 1000; extraLettersCount++ { + runes = append(runes, 'a') + require.Equal(t, wantPrice, priceParams.GetAliasPrice(string(runes))) + } + }) +} + +//goland:noinspection SpellCheckingInspection +func TestChainsParams_Validate(t *testing.T) { + tests := []struct { + name string + modifier func(params ChainsParams) ChainsParams + wantErr bool + wantErrContains string + }{ + { + name: "pass - default is valid", + modifier: func(p ChainsParams) ChainsParams { return p }, + }, + { + name: "pass - alias: empty is valid", + modifier: func(p ChainsParams) ChainsParams { + p.AliasesOfChainIds = nil + return p + }, + }, + { + name: "pass - alias: empty alias of chain is valid", + modifier: func(p ChainsParams) ChainsParams { + p.AliasesOfChainIds = []AliasesOfChainId{ + {ChainId: "dymension_1100-1", Aliases: nil}, + } + return p + }, + }, + { + name: "pass - alias: valid and correct alias", + modifier: func(p ChainsParams) ChainsParams { + p.AliasesOfChainIds = []AliasesOfChainId{ + {ChainId: "blumbus_111-1", Aliases: []string{"bb", "blumbus"}}, + {ChainId: "dymension_1100-1", Aliases: []string{"dym"}}, + } + return p + }, + }, + { + name: "fail - alias: chain_id and alias must be unique among all, case alias & alias", + modifier: func(p ChainsParams) ChainsParams { + p.AliasesOfChainIds = []AliasesOfChainId{ + {ChainId: "dymension_1100-1", Aliases: []string{"dym"}}, + {ChainId: "blumbus_111-1", Aliases: []string{"dym", "blumbus"}}, + } + return p + }, + wantErr: true, + wantErrContains: "chain ID and alias must unique among all", + }, + { + name: "fail - alias: chain_id and alias must be unique among all, case chain-id & alias", + modifier: func(p ChainsParams) ChainsParams { + p.AliasesOfChainIds = []AliasesOfChainId{ + {ChainId: "dymension_1100-1", Aliases: []string{"dym", "dymension"}}, + {ChainId: "blumbus_111-1", Aliases: []string{"blumbus", "cosmoshub"}}, + {ChainId: "cosmoshub", Aliases: []string{"cosmos"}}, + } + return p + }, + wantErr: true, + wantErrContains: "chain ID and alias must unique among all", + }, + { + name: "fail - alias: reject if chain-id format is bad", + modifier: func(p ChainsParams) ChainsParams { + p.AliasesOfChainIds = []AliasesOfChainId{ + {ChainId: "dymension@", Aliases: []string{"dym"}}, + {ChainId: "blumbus_111-1", Aliases: []string{"blumbus"}}, + } + return p + }, + wantErr: true, + wantErrContains: "is not well-formed", + }, + { + name: "fail - alias: reject if chain-id format is bad", + modifier: func(p ChainsParams) ChainsParams { + p.AliasesOfChainIds = []AliasesOfChainId{ + {ChainId: "d", Aliases: []string{"dym"}}, + } + return p + }, + wantErr: true, + wantErrContains: "must be at least 3 characters", + }, + { + name: "fail - alias: reject if alias format is bad", + modifier: func(p ChainsParams) ChainsParams { + p.AliasesOfChainIds = []AliasesOfChainId{ + {ChainId: "dymension_1100-1", Aliases: []string{"dym-dym"}}, + {ChainId: "blumbus_111-1", Aliases: []string{"blumbus"}}, + } + return p + }, + wantErr: true, + wantErrContains: "is not well-formed", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := tt.modifier(DefaultChainsParams()).Validate() + if tt.wantErr { + require.NotEmpty(t, tt.wantErrContains, "mis-configured test case") + require.Error(t, err) + require.Contains(t, err.Error(), tt.wantErrContains) + return + } + + require.NoError(t, err) + }) + } + + t.Run("fail - invalid type", func(t *testing.T) { + require.Error(t, validateChainsParams("hello world")) + require.Error(t, validateChainsParams(&ChainsParams{}), "not accept pointer") + }) +} + +func TestMiscParams_Validate(t *testing.T) { + tests := []struct { + name string + modifier func(MiscParams) MiscParams + wantErr bool + wantErrContains string + }{ + { + name: "pass - default is valid", + modifier: func(p MiscParams) MiscParams { return p }, + }, + { + name: "pass - minimum allowed", + modifier: func(p MiscParams) MiscParams { + p.GracePeriodDuration = 30 * 24 * time.Hour + p.SellOrderDuration = 1 * time.Nanosecond + return p + }, + }, + { + name: "pass - end epoch hour is valid", + modifier: func(p MiscParams) MiscParams { + p.EndEpochHookIdentifier = "hour" + return p + }, + }, + { + name: "pass - end epoch day is valid", + modifier: func(p MiscParams) MiscParams { + p.EndEpochHookIdentifier = "day" + return p + }, + }, + { + name: "pass - end epoch week is valid", + modifier: func(p MiscParams) MiscParams { + p.EndEpochHookIdentifier = "week" + return p + }, + }, + { + name: "fail - end other epoch is invalid", + modifier: func(p MiscParams) MiscParams { + p.EndEpochHookIdentifier = "invalid" + return p + }, + wantErr: true, + wantErrContains: "invalid epoch identifier: invalid", + }, + { + name: "fail - grace period can not lower than 30 days", + modifier: func(p MiscParams) MiscParams { + p.GracePeriodDuration = 30*24*time.Hour - time.Nanosecond + return p + }, + wantErr: true, + wantErrContains: "grace period duration cannot be less than", + }, + { + name: "fail - days SO duration can not be zero", + modifier: func(p MiscParams) MiscParams { + p.SellOrderDuration = 0 + return p + }, + wantErr: true, + wantErrContains: "Sell Orders duration can not be zero", + }, + { + name: "fail - days SO duration can not be negative", + modifier: func(p MiscParams) MiscParams { + p.SellOrderDuration = -1 * time.Nanosecond + return p + }, + wantErr: true, + wantErrContains: "Sell Orders duration can not be zero", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := tt.modifier(DefaultMiscParams()).Validate() + if tt.wantErr { + require.NotEmpty(t, tt.wantErrContains, "mis-configured test case") + require.Error(t, err) + require.Contains(t, err.Error(), tt.wantErrContains) + return + } + + require.NoError(t, err) + }) + } + + t.Run("fail - invalid type", func(t *testing.T) { + require.Error(t, validateMiscParams("hello world")) + require.Error(t, validateMiscParams(&MiscParams{}), "not accept pointer") + }) +} + +func Test_validateEpochIdentifier(t *testing.T) { + tests := []struct { + name string + i interface{} + wantErr bool + }{ + { + name: "pass - 'hour' is valid", + i: "hour", + }, + { + name: "pass - 'day' is valid", + i: "day", + }, + { + name: "pass - 'week' is valid", + i: "week", + }, + { + name: "fail - empty", + i: "", + wantErr: true, + }, + { + name: "fail - not string", + i: 1, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if tt.wantErr { + require.Error(t, validateEpochIdentifier(tt.i)) + } else { + require.NoError(t, validateEpochIdentifier(tt.i)) + } + }) + } +} diff --git a/x/dymns/types/proposal.go b/x/dymns/types/proposal.go new file mode 100644 index 000000000..14ca98fdc --- /dev/null +++ b/x/dymns/types/proposal.go @@ -0,0 +1,66 @@ +package types + +import ( + govcdc "github.com/cosmos/cosmos-sdk/x/gov/codec" + "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" +) + +const ( + // ProposalTypeMigrateChainIdsProposal defines the type for MigrateChainIdsProposal + ProposalTypeMigrateChainIdsProposal string = "MigrateChainIdsProposal" + // ProposalTypeUpdateAliasesProposal defines the type for UpdateAliasesProposal + ProposalTypeUpdateAliasesProposal string = "UpdateAliasesProposal" +) + +// Implements Proposal Interface +var ( + _ v1beta1.Content = &MigrateChainIdsProposal{} + _ v1beta1.Content = &UpdateAliasesProposal{} +) + +func init() { + v1beta1.RegisterProposalType(ProposalTypeMigrateChainIdsProposal) + govcdc.ModuleCdc.Amino.RegisterConcrete(&MigrateChainIdsProposal{}, "dymns/"+ProposalTypeMigrateChainIdsProposal, nil) + + v1beta1.RegisterProposalType(ProposalTypeUpdateAliasesProposal) + govcdc.ModuleCdc.Amino.RegisterConcrete(&UpdateAliasesProposal{}, "dymns/"+ProposalTypeUpdateAliasesProposal, nil) +} + +// NewMigrateChainIdsProposal returns new instance of MigrateChainIdsProposal +func NewMigrateChainIdsProposal(title, description string, replacement ...MigrateChainId) v1beta1.Content { + return &MigrateChainIdsProposal{ + Title: title, + Description: description, + Replacement: replacement, + } +} + +// ProposalRoute returns router key for this proposal +func (*MigrateChainIdsProposal) ProposalRoute() string { + return RouterKey +} + +// ProposalType returns proposal type for this proposal +func (*MigrateChainIdsProposal) ProposalType() string { + return ProposalTypeMigrateChainIdsProposal +} + +// NewUpdateAliasesProposal returns new instance of UpdateAliasesProposal +func NewUpdateAliasesProposal(title, description string, add, remove []UpdateAlias) v1beta1.Content { + return &UpdateAliasesProposal{ + Title: title, + Description: description, + Add: add, + Remove: remove, + } +} + +// ProposalRoute returns router key for this proposal +func (*UpdateAliasesProposal) ProposalRoute() string { + return RouterKey +} + +// ProposalType returns proposal type for this proposal +func (*UpdateAliasesProposal) ProposalType() string { + return ProposalTypeUpdateAliasesProposal +} diff --git a/x/dymns/types/proposal_test.go b/x/dymns/types/proposal_test.go new file mode 100644 index 000000000..5db7e4a07 --- /dev/null +++ b/x/dymns/types/proposal_test.go @@ -0,0 +1,43 @@ +package types + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestNewMigrateChainIdsProposal(t *testing.T) { + const title = "title" + const description = "description" + const prevChainId = "cosmoshub-3" + const newChainId = "cosmoshub-4" + got := NewMigrateChainIdsProposal(title, description, MigrateChainId{ + PreviousChainId: prevChainId, + NewChainId: newChainId, + }) + + require.Equal(t, title, got.GetTitle()) + require.Equal(t, description, got.GetDescription()) + + require.Equal(t, RouterKey, got.ProposalRoute()) + require.Equal(t, ProposalTypeMigrateChainIdsProposal, got.ProposalType()) +} + +func TestNewUpdateAliasesProposal(t *testing.T) { + const title = "title" + const description = "description" + const chainId = "cosmoshub-3" + const alias = "cosmoshub-4" + got := NewUpdateAliasesProposal(title, description, []UpdateAlias{ + { + ChainId: chainId, + Alias: alias, + }, + }, nil) + + require.Equal(t, title, got.GetTitle()) + require.Equal(t, description, got.GetDescription()) + + require.Equal(t, RouterKey, got.ProposalRoute()) + require.Equal(t, ProposalTypeUpdateAliasesProposal, got.ProposalType()) +} diff --git a/x/dymns/types/query.pb.go b/x/dymns/types/query.pb.go new file mode 100644 index 000000000..d7b63811e --- /dev/null +++ b/x/dymns/types/query.pb.go @@ -0,0 +1,8253 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: dymensionxyz/dymension/dymns/query.proto + +package types + +import ( + context "context" + fmt "fmt" + types "github.com/cosmos/cosmos-sdk/types" + _ "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 + +// 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_c9fbab881fb7aa6c, []int{0} +} +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 holds all the parameters of this module. + 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_c9fbab881fb7aa6c, []int{1} +} +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{} +} + +// QueryDymNameRequest is the request type for the Query/DymName RPC method. +type QueryDymNameRequest struct { + // dym_name is the name of the Dym-Name to query. + DymName string `protobuf:"bytes,1,opt,name=dym_name,json=dymName,proto3" json:"dym_name,omitempty"` +} + +func (m *QueryDymNameRequest) Reset() { *m = QueryDymNameRequest{} } +func (m *QueryDymNameRequest) String() string { return proto.CompactTextString(m) } +func (*QueryDymNameRequest) ProtoMessage() {} +func (*QueryDymNameRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_c9fbab881fb7aa6c, []int{2} +} +func (m *QueryDymNameRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryDymNameRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryDymNameRequest.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 *QueryDymNameRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryDymNameRequest.Merge(m, src) +} +func (m *QueryDymNameRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryDymNameRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryDymNameRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryDymNameRequest proto.InternalMessageInfo + +func (m *QueryDymNameRequest) GetDymName() string { + if m != nil { + return m.DymName + } + return "" +} + +// QueryDymNameResponse is the response type for the Query/DymName RPC method. +type QueryDymNameResponse struct { + // dym_name is the Dym-Name queried for. + DymName *DymName `protobuf:"bytes,1,opt,name=dym_name,json=dymName,proto3" json:"dym_name,omitempty"` +} + +func (m *QueryDymNameResponse) Reset() { *m = QueryDymNameResponse{} } +func (m *QueryDymNameResponse) String() string { return proto.CompactTextString(m) } +func (*QueryDymNameResponse) ProtoMessage() {} +func (*QueryDymNameResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_c9fbab881fb7aa6c, []int{3} +} +func (m *QueryDymNameResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryDymNameResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryDymNameResponse.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 *QueryDymNameResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryDymNameResponse.Merge(m, src) +} +func (m *QueryDymNameResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryDymNameResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryDymNameResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryDymNameResponse proto.InternalMessageInfo + +func (m *QueryDymNameResponse) GetDymName() *DymName { + if m != nil { + return m.DymName + } + return nil +} + +// QueryAliasRequest is the request type for the Query/QueryAlias RPC method. +type QueryAliasRequest struct { + // alias to query + Alias string `protobuf:"bytes,1,opt,name=alias,proto3" json:"alias,omitempty"` +} + +func (m *QueryAliasRequest) Reset() { *m = QueryAliasRequest{} } +func (m *QueryAliasRequest) String() string { return proto.CompactTextString(m) } +func (*QueryAliasRequest) ProtoMessage() {} +func (*QueryAliasRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_c9fbab881fb7aa6c, []int{4} +} +func (m *QueryAliasRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryAliasRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryAliasRequest.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 *QueryAliasRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryAliasRequest.Merge(m, src) +} +func (m *QueryAliasRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryAliasRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryAliasRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryAliasRequest proto.InternalMessageInfo + +func (m *QueryAliasRequest) GetAlias() string { + if m != nil { + return m.Alias + } + return "" +} + +// QueryAliasResponse +type QueryAliasResponse struct { + // chain_id associated with the alias + ChainId string `protobuf:"bytes,1,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` + // found_sell_order is true if active Sell-Order is found for the alias. + FoundSellOrder bool `protobuf:"varint,2,opt,name=found_sell_order,json=foundSellOrder,proto3" json:"found_sell_order,omitempty"` + // buy_order_ids is the list of Buy-Order IDs for the alias. + BuyOrderIds []string `protobuf:"bytes,3,rep,name=buy_order_ids,json=buyOrderIds,proto3" json:"buy_order_ids,omitempty"` + // same_chain_aliases is the list of aliases for the same chain that associated with the alias. + SameChainAliases []string `protobuf:"bytes,4,rep,name=same_chain_aliases,json=sameChainAliases,proto3" json:"same_chain_aliases,omitempty"` +} + +func (m *QueryAliasResponse) Reset() { *m = QueryAliasResponse{} } +func (m *QueryAliasResponse) String() string { return proto.CompactTextString(m) } +func (*QueryAliasResponse) ProtoMessage() {} +func (*QueryAliasResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_c9fbab881fb7aa6c, []int{5} +} +func (m *QueryAliasResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryAliasResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryAliasResponse.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 *QueryAliasResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryAliasResponse.Merge(m, src) +} +func (m *QueryAliasResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryAliasResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryAliasResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryAliasResponse proto.InternalMessageInfo + +func (m *QueryAliasResponse) GetChainId() string { + if m != nil { + return m.ChainId + } + return "" +} + +func (m *QueryAliasResponse) GetFoundSellOrder() bool { + if m != nil { + return m.FoundSellOrder + } + return false +} + +func (m *QueryAliasResponse) GetBuyOrderIds() []string { + if m != nil { + return m.BuyOrderIds + } + return nil +} + +func (m *QueryAliasResponse) GetSameChainAliases() []string { + if m != nil { + return m.SameChainAliases + } + return nil +} + +type QueryAliasesRequest struct { + // chain_id to query alias for, empty for all chains. + ChainId string `protobuf:"bytes,1,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` +} + +func (m *QueryAliasesRequest) Reset() { *m = QueryAliasesRequest{} } +func (m *QueryAliasesRequest) String() string { return proto.CompactTextString(m) } +func (*QueryAliasesRequest) ProtoMessage() {} +func (*QueryAliasesRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_c9fbab881fb7aa6c, []int{6} +} +func (m *QueryAliasesRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryAliasesRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryAliasesRequest.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 *QueryAliasesRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryAliasesRequest.Merge(m, src) +} +func (m *QueryAliasesRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryAliasesRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryAliasesRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryAliasesRequest proto.InternalMessageInfo + +func (m *QueryAliasesRequest) GetChainId() string { + if m != nil { + return m.ChainId + } + return "" +} + +type QueryAliasesResponse struct { + // aliases_by_chain_id is the map of aliases by chain id. + AliasesByChainId map[string]MultipleAliases `protobuf:"bytes,1,rep,name=aliases_by_chain_id,json=aliasesByChainId,proto3" json:"aliases_by_chain_id" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (m *QueryAliasesResponse) Reset() { *m = QueryAliasesResponse{} } +func (m *QueryAliasesResponse) String() string { return proto.CompactTextString(m) } +func (*QueryAliasesResponse) ProtoMessage() {} +func (*QueryAliasesResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_c9fbab881fb7aa6c, []int{7} +} +func (m *QueryAliasesResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryAliasesResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryAliasesResponse.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 *QueryAliasesResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryAliasesResponse.Merge(m, src) +} +func (m *QueryAliasesResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryAliasesResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryAliasesResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryAliasesResponse proto.InternalMessageInfo + +func (m *QueryAliasesResponse) GetAliasesByChainId() map[string]MultipleAliases { + if m != nil { + return m.AliasesByChainId + } + return nil +} + +// ResolveDymNameAddressesRequest is the request type for the Query/ResolveDymNameAddresses RPC method. +type ResolveDymNameAddressesRequest struct { + // addresses defines the Dym-Name addresses to resolve. + Addresses []string `protobuf:"bytes,1,rep,name=addresses,proto3" json:"addresses,omitempty"` +} + +func (m *ResolveDymNameAddressesRequest) Reset() { *m = ResolveDymNameAddressesRequest{} } +func (m *ResolveDymNameAddressesRequest) String() string { return proto.CompactTextString(m) } +func (*ResolveDymNameAddressesRequest) ProtoMessage() {} +func (*ResolveDymNameAddressesRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_c9fbab881fb7aa6c, []int{8} +} +func (m *ResolveDymNameAddressesRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ResolveDymNameAddressesRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ResolveDymNameAddressesRequest.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 *ResolveDymNameAddressesRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_ResolveDymNameAddressesRequest.Merge(m, src) +} +func (m *ResolveDymNameAddressesRequest) XXX_Size() int { + return m.Size() +} +func (m *ResolveDymNameAddressesRequest) XXX_DiscardUnknown() { + xxx_messageInfo_ResolveDymNameAddressesRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_ResolveDymNameAddressesRequest proto.InternalMessageInfo + +func (m *ResolveDymNameAddressesRequest) GetAddresses() []string { + if m != nil { + return m.Addresses + } + return nil +} + +// ResultDymNameAddress defines the result of a single Dym-Name address resolution. +type ResultDymNameAddress struct { + // address is the input Dym-Name address to resolve. + Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` + // resolved_address is the resolved account address. + ResolvedAddress string `protobuf:"bytes,2,opt,name=resolved_address,json=resolvedAddress,proto3" json:"resolved_address,omitempty"` + // error is the error that occurred during the resolution. + Error string `protobuf:"bytes,3,opt,name=error,proto3" json:"error,omitempty"` +} + +func (m *ResultDymNameAddress) Reset() { *m = ResultDymNameAddress{} } +func (m *ResultDymNameAddress) String() string { return proto.CompactTextString(m) } +func (*ResultDymNameAddress) ProtoMessage() {} +func (*ResultDymNameAddress) Descriptor() ([]byte, []int) { + return fileDescriptor_c9fbab881fb7aa6c, []int{9} +} +func (m *ResultDymNameAddress) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ResultDymNameAddress) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ResultDymNameAddress.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 *ResultDymNameAddress) XXX_Merge(src proto.Message) { + xxx_messageInfo_ResultDymNameAddress.Merge(m, src) +} +func (m *ResultDymNameAddress) XXX_Size() int { + return m.Size() +} +func (m *ResultDymNameAddress) XXX_DiscardUnknown() { + xxx_messageInfo_ResultDymNameAddress.DiscardUnknown(m) +} + +var xxx_messageInfo_ResultDymNameAddress proto.InternalMessageInfo + +func (m *ResultDymNameAddress) GetAddress() string { + if m != nil { + return m.Address + } + return "" +} + +func (m *ResultDymNameAddress) GetResolvedAddress() string { + if m != nil { + return m.ResolvedAddress + } + return "" +} + +func (m *ResultDymNameAddress) GetError() string { + if m != nil { + return m.Error + } + return "" +} + +// ResolveDymNameAddressesResponse is the response type for the Query/ResolveDymNameAddresses RPC method. +type ResolveDymNameAddressesResponse struct { + // resolved_addresses defines the resolved addresses for each input Dym-Name address. + ResolvedAddresses []ResultDymNameAddress `protobuf:"bytes,1,rep,name=resolved_addresses,json=resolvedAddresses,proto3" json:"resolved_addresses"` +} + +func (m *ResolveDymNameAddressesResponse) Reset() { *m = ResolveDymNameAddressesResponse{} } +func (m *ResolveDymNameAddressesResponse) String() string { return proto.CompactTextString(m) } +func (*ResolveDymNameAddressesResponse) ProtoMessage() {} +func (*ResolveDymNameAddressesResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_c9fbab881fb7aa6c, []int{10} +} +func (m *ResolveDymNameAddressesResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ResolveDymNameAddressesResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ResolveDymNameAddressesResponse.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 *ResolveDymNameAddressesResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_ResolveDymNameAddressesResponse.Merge(m, src) +} +func (m *ResolveDymNameAddressesResponse) XXX_Size() int { + return m.Size() +} +func (m *ResolveDymNameAddressesResponse) XXX_DiscardUnknown() { + xxx_messageInfo_ResolveDymNameAddressesResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_ResolveDymNameAddressesResponse proto.InternalMessageInfo + +func (m *ResolveDymNameAddressesResponse) GetResolvedAddresses() []ResultDymNameAddress { + if m != nil { + return m.ResolvedAddresses + } + return nil +} + +// QueryDymNamesOwnedByAccountRequest is the request type for the Query/DymNamesOwnedByAccount RPC method. +type QueryDymNamesOwnedByAccountRequest struct { + // owner defines the address of the owner of the Dym-Names to query for. + Owner string `protobuf:"bytes,1,opt,name=owner,proto3" json:"owner,omitempty"` +} + +func (m *QueryDymNamesOwnedByAccountRequest) Reset() { *m = QueryDymNamesOwnedByAccountRequest{} } +func (m *QueryDymNamesOwnedByAccountRequest) String() string { return proto.CompactTextString(m) } +func (*QueryDymNamesOwnedByAccountRequest) ProtoMessage() {} +func (*QueryDymNamesOwnedByAccountRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_c9fbab881fb7aa6c, []int{11} +} +func (m *QueryDymNamesOwnedByAccountRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryDymNamesOwnedByAccountRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryDymNamesOwnedByAccountRequest.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 *QueryDymNamesOwnedByAccountRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryDymNamesOwnedByAccountRequest.Merge(m, src) +} +func (m *QueryDymNamesOwnedByAccountRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryDymNamesOwnedByAccountRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryDymNamesOwnedByAccountRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryDymNamesOwnedByAccountRequest proto.InternalMessageInfo + +func (m *QueryDymNamesOwnedByAccountRequest) GetOwner() string { + if m != nil { + return m.Owner + } + return "" +} + +// QueryDymNamesOwnedByAccountResponse is the response type for the Query/DymNamesOwnedByAccount RPC method. +type QueryDymNamesOwnedByAccountResponse struct { + // dym_names defines the Dym-Names owned by the input account. + DymNames []DymName `protobuf:"bytes,1,rep,name=dym_names,json=dymNames,proto3" json:"dym_names"` +} + +func (m *QueryDymNamesOwnedByAccountResponse) Reset() { *m = QueryDymNamesOwnedByAccountResponse{} } +func (m *QueryDymNamesOwnedByAccountResponse) String() string { return proto.CompactTextString(m) } +func (*QueryDymNamesOwnedByAccountResponse) ProtoMessage() {} +func (*QueryDymNamesOwnedByAccountResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_c9fbab881fb7aa6c, []int{12} +} +func (m *QueryDymNamesOwnedByAccountResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryDymNamesOwnedByAccountResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryDymNamesOwnedByAccountResponse.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 *QueryDymNamesOwnedByAccountResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryDymNamesOwnedByAccountResponse.Merge(m, src) +} +func (m *QueryDymNamesOwnedByAccountResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryDymNamesOwnedByAccountResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryDymNamesOwnedByAccountResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryDymNamesOwnedByAccountResponse proto.InternalMessageInfo + +func (m *QueryDymNamesOwnedByAccountResponse) GetDymNames() []DymName { + if m != nil { + return m.DymNames + } + return nil +} + +// QuerySellOrderRequest is the request type for the Query/SellOrder RPC method. +type QuerySellOrderRequest struct { + // asset_id is the Dym-Name/Alias to query the active Sell-Order for. + AssetId string `protobuf:"bytes,1,opt,name=asset_id,json=assetId,proto3" json:"asset_id,omitempty"` + // asset_type can be either "Dym-Name" or "Alias". + AssetType string `protobuf:"bytes,2,opt,name=asset_type,json=assetType,proto3" json:"asset_type,omitempty"` +} + +func (m *QuerySellOrderRequest) Reset() { *m = QuerySellOrderRequest{} } +func (m *QuerySellOrderRequest) String() string { return proto.CompactTextString(m) } +func (*QuerySellOrderRequest) ProtoMessage() {} +func (*QuerySellOrderRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_c9fbab881fb7aa6c, []int{13} +} +func (m *QuerySellOrderRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QuerySellOrderRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QuerySellOrderRequest.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 *QuerySellOrderRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QuerySellOrderRequest.Merge(m, src) +} +func (m *QuerySellOrderRequest) XXX_Size() int { + return m.Size() +} +func (m *QuerySellOrderRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QuerySellOrderRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QuerySellOrderRequest proto.InternalMessageInfo + +func (m *QuerySellOrderRequest) GetAssetId() string { + if m != nil { + return m.AssetId + } + return "" +} + +func (m *QuerySellOrderRequest) GetAssetType() string { + if m != nil { + return m.AssetType + } + return "" +} + +// QuerySellOrderResponse is the response type for the Query/SellOrder RPC method. +type QuerySellOrderResponse struct { + // result is the active Sell-Order for the Dym-Name/Alias. + Result SellOrder `protobuf:"bytes,1,opt,name=result,proto3" json:"result"` +} + +func (m *QuerySellOrderResponse) Reset() { *m = QuerySellOrderResponse{} } +func (m *QuerySellOrderResponse) String() string { return proto.CompactTextString(m) } +func (*QuerySellOrderResponse) ProtoMessage() {} +func (*QuerySellOrderResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_c9fbab881fb7aa6c, []int{14} +} +func (m *QuerySellOrderResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QuerySellOrderResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QuerySellOrderResponse.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 *QuerySellOrderResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QuerySellOrderResponse.Merge(m, src) +} +func (m *QuerySellOrderResponse) XXX_Size() int { + return m.Size() +} +func (m *QuerySellOrderResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QuerySellOrderResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QuerySellOrderResponse proto.InternalMessageInfo + +func (m *QuerySellOrderResponse) GetResult() SellOrder { + if m != nil { + return m.Result + } + return SellOrder{} +} + +// EstimateRegisterNameRequest is the request type for the Query/EstimateRegisterName RPC method. +type EstimateRegisterNameRequest struct { + // name is the Dym-Name to be registered. + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + // duration is the number of years the Dym-Name will be registered for. + Duration int64 `protobuf:"varint,2,opt,name=duration,proto3" json:"duration,omitempty"` + // owner is the bech32-encoded address of the account which owns the order. + Owner string `protobuf:"bytes,3,opt,name=owner,proto3" json:"owner,omitempty"` +} + +func (m *EstimateRegisterNameRequest) Reset() { *m = EstimateRegisterNameRequest{} } +func (m *EstimateRegisterNameRequest) String() string { return proto.CompactTextString(m) } +func (*EstimateRegisterNameRequest) ProtoMessage() {} +func (*EstimateRegisterNameRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_c9fbab881fb7aa6c, []int{15} +} +func (m *EstimateRegisterNameRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *EstimateRegisterNameRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_EstimateRegisterNameRequest.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 *EstimateRegisterNameRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_EstimateRegisterNameRequest.Merge(m, src) +} +func (m *EstimateRegisterNameRequest) XXX_Size() int { + return m.Size() +} +func (m *EstimateRegisterNameRequest) XXX_DiscardUnknown() { + xxx_messageInfo_EstimateRegisterNameRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_EstimateRegisterNameRequest proto.InternalMessageInfo + +func (m *EstimateRegisterNameRequest) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *EstimateRegisterNameRequest) GetDuration() int64 { + if m != nil { + return m.Duration + } + return 0 +} + +func (m *EstimateRegisterNameRequest) GetOwner() string { + if m != nil { + return m.Owner + } + return "" +} + +// EstimateRegisterNameResponse is the response type for the Query/EstimateRegisterName RPC method. +type EstimateRegisterNameResponse struct { + // first_year_price is the price to register the Dym-Name for the first year. + FirstYearPrice types.Coin `protobuf:"bytes,1,opt,name=first_year_price,json=firstYearPrice,proto3" json:"first_year_price"` + // extend_price is the price to extend the Dym-Name registration for another year. + ExtendPrice types.Coin `protobuf:"bytes,2,opt,name=extend_price,json=extendPrice,proto3" json:"extend_price"` + // total_price is the total price to register the Dym-Name for the specified duration. + TotalPrice types.Coin `protobuf:"bytes,3,opt,name=total_price,json=totalPrice,proto3" json:"total_price"` +} + +func (m *EstimateRegisterNameResponse) Reset() { *m = EstimateRegisterNameResponse{} } +func (m *EstimateRegisterNameResponse) String() string { return proto.CompactTextString(m) } +func (*EstimateRegisterNameResponse) ProtoMessage() {} +func (*EstimateRegisterNameResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_c9fbab881fb7aa6c, []int{16} +} +func (m *EstimateRegisterNameResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *EstimateRegisterNameResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_EstimateRegisterNameResponse.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 *EstimateRegisterNameResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_EstimateRegisterNameResponse.Merge(m, src) +} +func (m *EstimateRegisterNameResponse) XXX_Size() int { + return m.Size() +} +func (m *EstimateRegisterNameResponse) XXX_DiscardUnknown() { + xxx_messageInfo_EstimateRegisterNameResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_EstimateRegisterNameResponse proto.InternalMessageInfo + +func (m *EstimateRegisterNameResponse) GetFirstYearPrice() types.Coin { + if m != nil { + return m.FirstYearPrice + } + return types.Coin{} +} + +func (m *EstimateRegisterNameResponse) GetExtendPrice() types.Coin { + if m != nil { + return m.ExtendPrice + } + return types.Coin{} +} + +func (m *EstimateRegisterNameResponse) GetTotalPrice() types.Coin { + if m != nil { + return m.TotalPrice + } + return types.Coin{} +} + +// EstimateRegisterAliasRequest is the request type for the Query/EstimateRegisterAlias RPC method. +type EstimateRegisterAliasRequest struct { + // alias to be registered. + Alias string `protobuf:"bytes,1,opt,name=alias,proto3" json:"alias,omitempty"` + // rollapp_id is the rollapp to link the alias to. + RollappId string `protobuf:"bytes,2,opt,name=rollapp_id,json=rollappId,proto3" json:"rollapp_id,omitempty"` + // owner is the bech32-encoded address of the account which owns the order. + Owner string `protobuf:"bytes,3,opt,name=owner,proto3" json:"owner,omitempty"` +} + +func (m *EstimateRegisterAliasRequest) Reset() { *m = EstimateRegisterAliasRequest{} } +func (m *EstimateRegisterAliasRequest) String() string { return proto.CompactTextString(m) } +func (*EstimateRegisterAliasRequest) ProtoMessage() {} +func (*EstimateRegisterAliasRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_c9fbab881fb7aa6c, []int{17} +} +func (m *EstimateRegisterAliasRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *EstimateRegisterAliasRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_EstimateRegisterAliasRequest.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 *EstimateRegisterAliasRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_EstimateRegisterAliasRequest.Merge(m, src) +} +func (m *EstimateRegisterAliasRequest) XXX_Size() int { + return m.Size() +} +func (m *EstimateRegisterAliasRequest) XXX_DiscardUnknown() { + xxx_messageInfo_EstimateRegisterAliasRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_EstimateRegisterAliasRequest proto.InternalMessageInfo + +func (m *EstimateRegisterAliasRequest) GetAlias() string { + if m != nil { + return m.Alias + } + return "" +} + +func (m *EstimateRegisterAliasRequest) GetRollappId() string { + if m != nil { + return m.RollappId + } + return "" +} + +func (m *EstimateRegisterAliasRequest) GetOwner() string { + if m != nil { + return m.Owner + } + return "" +} + +// EstimateRegisterAliasResponse is the response type for the Query/EstimateRegisterAlias RPC method. +type EstimateRegisterAliasResponse struct { + // price is the price to register the alias. + Price types.Coin `protobuf:"bytes,1,opt,name=price,proto3" json:"price"` +} + +func (m *EstimateRegisterAliasResponse) Reset() { *m = EstimateRegisterAliasResponse{} } +func (m *EstimateRegisterAliasResponse) String() string { return proto.CompactTextString(m) } +func (*EstimateRegisterAliasResponse) ProtoMessage() {} +func (*EstimateRegisterAliasResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_c9fbab881fb7aa6c, []int{18} +} +func (m *EstimateRegisterAliasResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *EstimateRegisterAliasResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_EstimateRegisterAliasResponse.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 *EstimateRegisterAliasResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_EstimateRegisterAliasResponse.Merge(m, src) +} +func (m *EstimateRegisterAliasResponse) XXX_Size() int { + return m.Size() +} +func (m *EstimateRegisterAliasResponse) XXX_DiscardUnknown() { + xxx_messageInfo_EstimateRegisterAliasResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_EstimateRegisterAliasResponse proto.InternalMessageInfo + +func (m *EstimateRegisterAliasResponse) GetPrice() types.Coin { + if m != nil { + return m.Price + } + return types.Coin{} +} + +// ReverseResolveAddressRequest is the request type for the Query/ReverseResolveAddress RPC method. +type ReverseResolveAddressRequest struct { + // addresses defines the addresses to reverse resolve. Can be both bech32 and hex addresses. + Addresses []string `protobuf:"bytes,1,rep,name=addresses,proto3" json:"addresses,omitempty"` + // working_chain_id defines the chain id to use for the reverse resolution. + // Leave empty to use the host chain id. + WorkingChainId string `protobuf:"bytes,2,opt,name=working_chain_id,json=workingChainId,proto3" json:"working_chain_id,omitempty"` +} + +func (m *ReverseResolveAddressRequest) Reset() { *m = ReverseResolveAddressRequest{} } +func (m *ReverseResolveAddressRequest) String() string { return proto.CompactTextString(m) } +func (*ReverseResolveAddressRequest) ProtoMessage() {} +func (*ReverseResolveAddressRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_c9fbab881fb7aa6c, []int{19} +} +func (m *ReverseResolveAddressRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ReverseResolveAddressRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ReverseResolveAddressRequest.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 *ReverseResolveAddressRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_ReverseResolveAddressRequest.Merge(m, src) +} +func (m *ReverseResolveAddressRequest) XXX_Size() int { + return m.Size() +} +func (m *ReverseResolveAddressRequest) XXX_DiscardUnknown() { + xxx_messageInfo_ReverseResolveAddressRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_ReverseResolveAddressRequest proto.InternalMessageInfo + +func (m *ReverseResolveAddressRequest) GetAddresses() []string { + if m != nil { + return m.Addresses + } + return nil +} + +func (m *ReverseResolveAddressRequest) GetWorkingChainId() string { + if m != nil { + return m.WorkingChainId + } + return "" +} + +// ReverseResolveAddressResponse is the response type for the Query/ReverseResolveAddress RPC method. +type ReverseResolveAddressResponse struct { + // result defines the reverse resolution result for each input address. + Result map[string]ReverseResolveAddressResult `protobuf:"bytes,1,rep,name=result,proto3" json:"result" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + // working_chain_id is the chain id used for the reverse resolution. + WorkingChainId string `protobuf:"bytes,2,opt,name=working_chain_id,json=workingChainId,proto3" json:"working_chain_id,omitempty"` +} + +func (m *ReverseResolveAddressResponse) Reset() { *m = ReverseResolveAddressResponse{} } +func (m *ReverseResolveAddressResponse) String() string { return proto.CompactTextString(m) } +func (*ReverseResolveAddressResponse) ProtoMessage() {} +func (*ReverseResolveAddressResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_c9fbab881fb7aa6c, []int{20} +} +func (m *ReverseResolveAddressResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ReverseResolveAddressResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ReverseResolveAddressResponse.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 *ReverseResolveAddressResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_ReverseResolveAddressResponse.Merge(m, src) +} +func (m *ReverseResolveAddressResponse) XXX_Size() int { + return m.Size() +} +func (m *ReverseResolveAddressResponse) XXX_DiscardUnknown() { + xxx_messageInfo_ReverseResolveAddressResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_ReverseResolveAddressResponse proto.InternalMessageInfo + +func (m *ReverseResolveAddressResponse) GetResult() map[string]ReverseResolveAddressResult { + if m != nil { + return m.Result + } + return nil +} + +func (m *ReverseResolveAddressResponse) GetWorkingChainId() string { + if m != nil { + return m.WorkingChainId + } + return "" +} + +type ReverseResolveAddressResult struct { + // candidates are the Dym-Name addresses that the input address resolves to. Take one of them. + Candidates []string `protobuf:"bytes,1,rep,name=candidates,proto3" json:"candidates,omitempty"` + // error is the error that occurred during the resolution. + Error string `protobuf:"bytes,2,opt,name=error,proto3" json:"error,omitempty"` +} + +func (m *ReverseResolveAddressResult) Reset() { *m = ReverseResolveAddressResult{} } +func (m *ReverseResolveAddressResult) String() string { return proto.CompactTextString(m) } +func (*ReverseResolveAddressResult) ProtoMessage() {} +func (*ReverseResolveAddressResult) Descriptor() ([]byte, []int) { + return fileDescriptor_c9fbab881fb7aa6c, []int{21} +} +func (m *ReverseResolveAddressResult) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ReverseResolveAddressResult) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ReverseResolveAddressResult.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 *ReverseResolveAddressResult) XXX_Merge(src proto.Message) { + xxx_messageInfo_ReverseResolveAddressResult.Merge(m, src) +} +func (m *ReverseResolveAddressResult) XXX_Size() int { + return m.Size() +} +func (m *ReverseResolveAddressResult) XXX_DiscardUnknown() { + xxx_messageInfo_ReverseResolveAddressResult.DiscardUnknown(m) +} + +var xxx_messageInfo_ReverseResolveAddressResult proto.InternalMessageInfo + +func (m *ReverseResolveAddressResult) GetCandidates() []string { + if m != nil { + return m.Candidates + } + return nil +} + +func (m *ReverseResolveAddressResult) GetError() string { + if m != nil { + return m.Error + } + return "" +} + +// QueryTranslateAliasOrChainIdToChainIdRequest is the request type for the Query/TranslateAliasOrChainIdToChainId RPC method. +type QueryTranslateAliasOrChainIdToChainIdRequest struct { + // alias_or_chain_id is the alias or chain id to translate. + AliasOrChainId string `protobuf:"bytes,1,opt,name=alias_or_chain_id,json=aliasOrChainId,proto3" json:"alias_or_chain_id,omitempty"` +} + +func (m *QueryTranslateAliasOrChainIdToChainIdRequest) Reset() { + *m = QueryTranslateAliasOrChainIdToChainIdRequest{} +} +func (m *QueryTranslateAliasOrChainIdToChainIdRequest) String() string { + return proto.CompactTextString(m) +} +func (*QueryTranslateAliasOrChainIdToChainIdRequest) ProtoMessage() {} +func (*QueryTranslateAliasOrChainIdToChainIdRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_c9fbab881fb7aa6c, []int{22} +} +func (m *QueryTranslateAliasOrChainIdToChainIdRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryTranslateAliasOrChainIdToChainIdRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryTranslateAliasOrChainIdToChainIdRequest.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 *QueryTranslateAliasOrChainIdToChainIdRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryTranslateAliasOrChainIdToChainIdRequest.Merge(m, src) +} +func (m *QueryTranslateAliasOrChainIdToChainIdRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryTranslateAliasOrChainIdToChainIdRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryTranslateAliasOrChainIdToChainIdRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryTranslateAliasOrChainIdToChainIdRequest proto.InternalMessageInfo + +func (m *QueryTranslateAliasOrChainIdToChainIdRequest) GetAliasOrChainId() string { + if m != nil { + return m.AliasOrChainId + } + return "" +} + +// QueryTranslateAliasOrChainIdToChainIdResponse is the response type for the Query/TranslateAliasOrChainIdToChainId RPC method. +type QueryTranslateAliasOrChainIdToChainIdResponse struct { + // chain_id is the chain id that the alias or chain id translates to. + ChainId string `protobuf:"bytes,1,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` +} + +func (m *QueryTranslateAliasOrChainIdToChainIdResponse) Reset() { + *m = QueryTranslateAliasOrChainIdToChainIdResponse{} +} +func (m *QueryTranslateAliasOrChainIdToChainIdResponse) String() string { + return proto.CompactTextString(m) +} +func (*QueryTranslateAliasOrChainIdToChainIdResponse) ProtoMessage() {} +func (*QueryTranslateAliasOrChainIdToChainIdResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_c9fbab881fb7aa6c, []int{23} +} +func (m *QueryTranslateAliasOrChainIdToChainIdResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryTranslateAliasOrChainIdToChainIdResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryTranslateAliasOrChainIdToChainIdResponse.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 *QueryTranslateAliasOrChainIdToChainIdResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryTranslateAliasOrChainIdToChainIdResponse.Merge(m, src) +} +func (m *QueryTranslateAliasOrChainIdToChainIdResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryTranslateAliasOrChainIdToChainIdResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryTranslateAliasOrChainIdToChainIdResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryTranslateAliasOrChainIdToChainIdResponse proto.InternalMessageInfo + +func (m *QueryTranslateAliasOrChainIdToChainIdResponse) GetChainId() string { + if m != nil { + return m.ChainId + } + return "" +} + +// QueryBuyOrderByIdRequest is the request type for the Query/BuyOrderById RPC method. +type QueryBuyOrderByIdRequest struct { + // id of buy offer to query. + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` +} + +func (m *QueryBuyOrderByIdRequest) Reset() { *m = QueryBuyOrderByIdRequest{} } +func (m *QueryBuyOrderByIdRequest) String() string { return proto.CompactTextString(m) } +func (*QueryBuyOrderByIdRequest) ProtoMessage() {} +func (*QueryBuyOrderByIdRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_c9fbab881fb7aa6c, []int{24} +} +func (m *QueryBuyOrderByIdRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryBuyOrderByIdRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryBuyOrderByIdRequest.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 *QueryBuyOrderByIdRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryBuyOrderByIdRequest.Merge(m, src) +} +func (m *QueryBuyOrderByIdRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryBuyOrderByIdRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryBuyOrderByIdRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryBuyOrderByIdRequest proto.InternalMessageInfo + +func (m *QueryBuyOrderByIdRequest) GetId() string { + if m != nil { + return m.Id + } + return "" +} + +// QueryBuyOrderByIdResponse is the response type for the Query/BuyOrderById RPC method. +type QueryBuyOrderByIdResponse struct { + // buy_order is the result. + BuyOrder BuyOrder `protobuf:"bytes,1,opt,name=buy_order,json=buyOrder,proto3" json:"buy_order"` +} + +func (m *QueryBuyOrderByIdResponse) Reset() { *m = QueryBuyOrderByIdResponse{} } +func (m *QueryBuyOrderByIdResponse) String() string { return proto.CompactTextString(m) } +func (*QueryBuyOrderByIdResponse) ProtoMessage() {} +func (*QueryBuyOrderByIdResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_c9fbab881fb7aa6c, []int{25} +} +func (m *QueryBuyOrderByIdResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryBuyOrderByIdResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryBuyOrderByIdResponse.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 *QueryBuyOrderByIdResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryBuyOrderByIdResponse.Merge(m, src) +} +func (m *QueryBuyOrderByIdResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryBuyOrderByIdResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryBuyOrderByIdResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryBuyOrderByIdResponse proto.InternalMessageInfo + +func (m *QueryBuyOrderByIdResponse) GetBuyOrder() BuyOrder { + if m != nil { + return m.BuyOrder + } + return BuyOrder{} +} + +// QueryBuyOrdersByAccountRequest is the request type for the Query/BuyOrdersPlacedByAccount RPC method. +type QueryBuyOrdersPlacedByAccountRequest struct { + // account is the account address to query the placed buy offers. + Account string `protobuf:"bytes,1,opt,name=account,proto3" json:"account,omitempty"` +} + +func (m *QueryBuyOrdersPlacedByAccountRequest) Reset() { *m = QueryBuyOrdersPlacedByAccountRequest{} } +func (m *QueryBuyOrdersPlacedByAccountRequest) String() string { return proto.CompactTextString(m) } +func (*QueryBuyOrdersPlacedByAccountRequest) ProtoMessage() {} +func (*QueryBuyOrdersPlacedByAccountRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_c9fbab881fb7aa6c, []int{26} +} +func (m *QueryBuyOrdersPlacedByAccountRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryBuyOrdersPlacedByAccountRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryBuyOrdersPlacedByAccountRequest.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 *QueryBuyOrdersPlacedByAccountRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryBuyOrdersPlacedByAccountRequest.Merge(m, src) +} +func (m *QueryBuyOrdersPlacedByAccountRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryBuyOrdersPlacedByAccountRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryBuyOrdersPlacedByAccountRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryBuyOrdersPlacedByAccountRequest proto.InternalMessageInfo + +func (m *QueryBuyOrdersPlacedByAccountRequest) GetAccount() string { + if m != nil { + return m.Account + } + return "" +} + +// QueryBuyOrdersByAccountResponse is the response type for the Query/BuyOrdersPlacedByAccount RPC method. +type QueryBuyOrdersPlacedByAccountResponse struct { + // offers are the Buy-Orders placed by the account. + BuyOrders []BuyOrder `protobuf:"bytes,1,rep,name=buy_orders,json=buyOrders,proto3" json:"buy_orders"` +} + +func (m *QueryBuyOrdersPlacedByAccountResponse) Reset() { *m = QueryBuyOrdersPlacedByAccountResponse{} } +func (m *QueryBuyOrdersPlacedByAccountResponse) String() string { return proto.CompactTextString(m) } +func (*QueryBuyOrdersPlacedByAccountResponse) ProtoMessage() {} +func (*QueryBuyOrdersPlacedByAccountResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_c9fbab881fb7aa6c, []int{27} +} +func (m *QueryBuyOrdersPlacedByAccountResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryBuyOrdersPlacedByAccountResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryBuyOrdersPlacedByAccountResponse.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 *QueryBuyOrdersPlacedByAccountResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryBuyOrdersPlacedByAccountResponse.Merge(m, src) +} +func (m *QueryBuyOrdersPlacedByAccountResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryBuyOrdersPlacedByAccountResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryBuyOrdersPlacedByAccountResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryBuyOrdersPlacedByAccountResponse proto.InternalMessageInfo + +func (m *QueryBuyOrdersPlacedByAccountResponse) GetBuyOrders() []BuyOrder { + if m != nil { + return m.BuyOrders + } + return nil +} + +// QueryBuyOrdersByDymNameRequest is the request type for the Query/BuyOrdersByDymName RPC method. +type QueryBuyOrdersByDymNameRequest struct { + // name is the Dym-Name to query the buy offers placed for it. + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` +} + +func (m *QueryBuyOrdersByDymNameRequest) Reset() { *m = QueryBuyOrdersByDymNameRequest{} } +func (m *QueryBuyOrdersByDymNameRequest) String() string { return proto.CompactTextString(m) } +func (*QueryBuyOrdersByDymNameRequest) ProtoMessage() {} +func (*QueryBuyOrdersByDymNameRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_c9fbab881fb7aa6c, []int{28} +} +func (m *QueryBuyOrdersByDymNameRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryBuyOrdersByDymNameRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryBuyOrdersByDymNameRequest.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 *QueryBuyOrdersByDymNameRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryBuyOrdersByDymNameRequest.Merge(m, src) +} +func (m *QueryBuyOrdersByDymNameRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryBuyOrdersByDymNameRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryBuyOrdersByDymNameRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryBuyOrdersByDymNameRequest proto.InternalMessageInfo + +func (m *QueryBuyOrdersByDymNameRequest) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +// QueryBuyOrdersByDymNameResponse is the response type for the Query/BuyOrdersByDymName RPC method. +type QueryBuyOrdersByDymNameResponse struct { + // buy_orders placed for the Dym-Name. + BuyOrders []BuyOrder `protobuf:"bytes,1,rep,name=buy_orders,json=buyOrders,proto3" json:"buy_orders"` +} + +func (m *QueryBuyOrdersByDymNameResponse) Reset() { *m = QueryBuyOrdersByDymNameResponse{} } +func (m *QueryBuyOrdersByDymNameResponse) String() string { return proto.CompactTextString(m) } +func (*QueryBuyOrdersByDymNameResponse) ProtoMessage() {} +func (*QueryBuyOrdersByDymNameResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_c9fbab881fb7aa6c, []int{29} +} +func (m *QueryBuyOrdersByDymNameResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryBuyOrdersByDymNameResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryBuyOrdersByDymNameResponse.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 *QueryBuyOrdersByDymNameResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryBuyOrdersByDymNameResponse.Merge(m, src) +} +func (m *QueryBuyOrdersByDymNameResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryBuyOrdersByDymNameResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryBuyOrdersByDymNameResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryBuyOrdersByDymNameResponse proto.InternalMessageInfo + +func (m *QueryBuyOrdersByDymNameResponse) GetBuyOrders() []BuyOrder { + if m != nil { + return m.BuyOrders + } + return nil +} + +// QueryBuyOrdersOfDymNamesOwnedByAccountRequest is the request type for the Query/BuyOrdersOfDymNamesOwnedByAccount RPC method. +type QueryBuyOrdersOfDymNamesOwnedByAccountRequest struct { + // account is the account address to query all the buy offers of the Dym-Names owned by it. + Account string `protobuf:"bytes,1,opt,name=account,proto3" json:"account,omitempty"` +} + +func (m *QueryBuyOrdersOfDymNamesOwnedByAccountRequest) Reset() { + *m = QueryBuyOrdersOfDymNamesOwnedByAccountRequest{} +} +func (m *QueryBuyOrdersOfDymNamesOwnedByAccountRequest) String() string { + return proto.CompactTextString(m) +} +func (*QueryBuyOrdersOfDymNamesOwnedByAccountRequest) ProtoMessage() {} +func (*QueryBuyOrdersOfDymNamesOwnedByAccountRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_c9fbab881fb7aa6c, []int{30} +} +func (m *QueryBuyOrdersOfDymNamesOwnedByAccountRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryBuyOrdersOfDymNamesOwnedByAccountRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryBuyOrdersOfDymNamesOwnedByAccountRequest.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 *QueryBuyOrdersOfDymNamesOwnedByAccountRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryBuyOrdersOfDymNamesOwnedByAccountRequest.Merge(m, src) +} +func (m *QueryBuyOrdersOfDymNamesOwnedByAccountRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryBuyOrdersOfDymNamesOwnedByAccountRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryBuyOrdersOfDymNamesOwnedByAccountRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryBuyOrdersOfDymNamesOwnedByAccountRequest proto.InternalMessageInfo + +func (m *QueryBuyOrdersOfDymNamesOwnedByAccountRequest) GetAccount() string { + if m != nil { + return m.Account + } + return "" +} + +// QueryBuyOrdersOfDymNamesOwnedByAccountResponse is the response type for the Query/BuyOrdersOfDymNamesOwnedByAccount RPC method. +type QueryBuyOrdersOfDymNamesOwnedByAccountResponse struct { + // buy_orders of all the Dym-Names owned by the input account. + BuyOrders []BuyOrder `protobuf:"bytes,1,rep,name=buy_orders,json=buyOrders,proto3" json:"buy_orders"` +} + +func (m *QueryBuyOrdersOfDymNamesOwnedByAccountResponse) Reset() { + *m = QueryBuyOrdersOfDymNamesOwnedByAccountResponse{} +} +func (m *QueryBuyOrdersOfDymNamesOwnedByAccountResponse) String() string { + return proto.CompactTextString(m) +} +func (*QueryBuyOrdersOfDymNamesOwnedByAccountResponse) ProtoMessage() {} +func (*QueryBuyOrdersOfDymNamesOwnedByAccountResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_c9fbab881fb7aa6c, []int{31} +} +func (m *QueryBuyOrdersOfDymNamesOwnedByAccountResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryBuyOrdersOfDymNamesOwnedByAccountResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryBuyOrdersOfDymNamesOwnedByAccountResponse.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 *QueryBuyOrdersOfDymNamesOwnedByAccountResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryBuyOrdersOfDymNamesOwnedByAccountResponse.Merge(m, src) +} +func (m *QueryBuyOrdersOfDymNamesOwnedByAccountResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryBuyOrdersOfDymNamesOwnedByAccountResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryBuyOrdersOfDymNamesOwnedByAccountResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryBuyOrdersOfDymNamesOwnedByAccountResponse proto.InternalMessageInfo + +func (m *QueryBuyOrdersOfDymNamesOwnedByAccountResponse) GetBuyOrders() []BuyOrder { + if m != nil { + return m.BuyOrders + } + return nil +} + +// QueryBuyOrdersByAliasRequest is the request type for the Query/BuyOrdersByAlias RPC method. +type QueryBuyOrdersByAliasRequest struct { + // alias is the alias to query the buy offers placed for it. + Alias string `protobuf:"bytes,1,opt,name=alias,proto3" json:"alias,omitempty"` +} + +func (m *QueryBuyOrdersByAliasRequest) Reset() { *m = QueryBuyOrdersByAliasRequest{} } +func (m *QueryBuyOrdersByAliasRequest) String() string { return proto.CompactTextString(m) } +func (*QueryBuyOrdersByAliasRequest) ProtoMessage() {} +func (*QueryBuyOrdersByAliasRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_c9fbab881fb7aa6c, []int{32} +} +func (m *QueryBuyOrdersByAliasRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryBuyOrdersByAliasRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryBuyOrdersByAliasRequest.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 *QueryBuyOrdersByAliasRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryBuyOrdersByAliasRequest.Merge(m, src) +} +func (m *QueryBuyOrdersByAliasRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryBuyOrdersByAliasRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryBuyOrdersByAliasRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryBuyOrdersByAliasRequest proto.InternalMessageInfo + +func (m *QueryBuyOrdersByAliasRequest) GetAlias() string { + if m != nil { + return m.Alias + } + return "" +} + +// QueryBuyOrdersByAliasResponse is the response type for the Query/BuyOrdersByAlias RPC method. +type QueryBuyOrdersByAliasResponse struct { + // buy_orders of the input alias. + BuyOrders []BuyOrder `protobuf:"bytes,1,rep,name=buy_orders,json=buyOrders,proto3" json:"buy_orders"` +} + +func (m *QueryBuyOrdersByAliasResponse) Reset() { *m = QueryBuyOrdersByAliasResponse{} } +func (m *QueryBuyOrdersByAliasResponse) String() string { return proto.CompactTextString(m) } +func (*QueryBuyOrdersByAliasResponse) ProtoMessage() {} +func (*QueryBuyOrdersByAliasResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_c9fbab881fb7aa6c, []int{33} +} +func (m *QueryBuyOrdersByAliasResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryBuyOrdersByAliasResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryBuyOrdersByAliasResponse.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 *QueryBuyOrdersByAliasResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryBuyOrdersByAliasResponse.Merge(m, src) +} +func (m *QueryBuyOrdersByAliasResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryBuyOrdersByAliasResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryBuyOrdersByAliasResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryBuyOrdersByAliasResponse proto.InternalMessageInfo + +func (m *QueryBuyOrdersByAliasResponse) GetBuyOrders() []BuyOrder { + if m != nil { + return m.BuyOrders + } + return nil +} + +// QueryBuyOrdersOfAliasesLinkedToRollAppRequest is the request type for the Query/BuyOrdersOfAliasesLinkedToRollApp RPC method. +type QueryBuyOrdersOfAliasesLinkedToRollAppRequest struct { + // rollapp_id is the rollapp to query all the buy offers of the aliases linked to it + RollappId string `protobuf:"bytes,1,opt,name=rollapp_id,json=rollappId,proto3" json:"rollapp_id,omitempty"` +} + +func (m *QueryBuyOrdersOfAliasesLinkedToRollAppRequest) Reset() { + *m = QueryBuyOrdersOfAliasesLinkedToRollAppRequest{} +} +func (m *QueryBuyOrdersOfAliasesLinkedToRollAppRequest) String() string { + return proto.CompactTextString(m) +} +func (*QueryBuyOrdersOfAliasesLinkedToRollAppRequest) ProtoMessage() {} +func (*QueryBuyOrdersOfAliasesLinkedToRollAppRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_c9fbab881fb7aa6c, []int{34} +} +func (m *QueryBuyOrdersOfAliasesLinkedToRollAppRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryBuyOrdersOfAliasesLinkedToRollAppRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryBuyOrdersOfAliasesLinkedToRollAppRequest.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 *QueryBuyOrdersOfAliasesLinkedToRollAppRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryBuyOrdersOfAliasesLinkedToRollAppRequest.Merge(m, src) +} +func (m *QueryBuyOrdersOfAliasesLinkedToRollAppRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryBuyOrdersOfAliasesLinkedToRollAppRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryBuyOrdersOfAliasesLinkedToRollAppRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryBuyOrdersOfAliasesLinkedToRollAppRequest proto.InternalMessageInfo + +func (m *QueryBuyOrdersOfAliasesLinkedToRollAppRequest) GetRollappId() string { + if m != nil { + return m.RollappId + } + return "" +} + +// QueryBuyOrdersOfAliasesLinkedToRollAppResponse is the response type for the Query/BuyOrdersOfAliasesLinkedToRollApp RPC method. +type QueryBuyOrdersOfAliasesLinkedToRollAppResponse struct { + // buy_orders are all the buy orders of the aliases linked to the input rollapp. + BuyOrders []BuyOrder `protobuf:"bytes,1,rep,name=buy_orders,json=buyOrders,proto3" json:"buy_orders"` +} + +func (m *QueryBuyOrdersOfAliasesLinkedToRollAppResponse) Reset() { + *m = QueryBuyOrdersOfAliasesLinkedToRollAppResponse{} +} +func (m *QueryBuyOrdersOfAliasesLinkedToRollAppResponse) String() string { + return proto.CompactTextString(m) +} +func (*QueryBuyOrdersOfAliasesLinkedToRollAppResponse) ProtoMessage() {} +func (*QueryBuyOrdersOfAliasesLinkedToRollAppResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_c9fbab881fb7aa6c, []int{35} +} +func (m *QueryBuyOrdersOfAliasesLinkedToRollAppResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryBuyOrdersOfAliasesLinkedToRollAppResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryBuyOrdersOfAliasesLinkedToRollAppResponse.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 *QueryBuyOrdersOfAliasesLinkedToRollAppResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryBuyOrdersOfAliasesLinkedToRollAppResponse.Merge(m, src) +} +func (m *QueryBuyOrdersOfAliasesLinkedToRollAppResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryBuyOrdersOfAliasesLinkedToRollAppResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryBuyOrdersOfAliasesLinkedToRollAppResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryBuyOrdersOfAliasesLinkedToRollAppResponse proto.InternalMessageInfo + +func (m *QueryBuyOrdersOfAliasesLinkedToRollAppResponse) GetBuyOrders() []BuyOrder { + if m != nil { + return m.BuyOrders + } + return nil +} + +func init() { + proto.RegisterType((*QueryParamsRequest)(nil), "dymensionxyz.dymension.dymns.QueryParamsRequest") + proto.RegisterType((*QueryParamsResponse)(nil), "dymensionxyz.dymension.dymns.QueryParamsResponse") + proto.RegisterType((*QueryDymNameRequest)(nil), "dymensionxyz.dymension.dymns.QueryDymNameRequest") + proto.RegisterType((*QueryDymNameResponse)(nil), "dymensionxyz.dymension.dymns.QueryDymNameResponse") + proto.RegisterType((*QueryAliasRequest)(nil), "dymensionxyz.dymension.dymns.QueryAliasRequest") + proto.RegisterType((*QueryAliasResponse)(nil), "dymensionxyz.dymension.dymns.QueryAliasResponse") + proto.RegisterType((*QueryAliasesRequest)(nil), "dymensionxyz.dymension.dymns.QueryAliasesRequest") + proto.RegisterType((*QueryAliasesResponse)(nil), "dymensionxyz.dymension.dymns.QueryAliasesResponse") + proto.RegisterMapType((map[string]MultipleAliases)(nil), "dymensionxyz.dymension.dymns.QueryAliasesResponse.AliasesByChainIdEntry") + proto.RegisterType((*ResolveDymNameAddressesRequest)(nil), "dymensionxyz.dymension.dymns.ResolveDymNameAddressesRequest") + proto.RegisterType((*ResultDymNameAddress)(nil), "dymensionxyz.dymension.dymns.ResultDymNameAddress") + proto.RegisterType((*ResolveDymNameAddressesResponse)(nil), "dymensionxyz.dymension.dymns.ResolveDymNameAddressesResponse") + proto.RegisterType((*QueryDymNamesOwnedByAccountRequest)(nil), "dymensionxyz.dymension.dymns.QueryDymNamesOwnedByAccountRequest") + proto.RegisterType((*QueryDymNamesOwnedByAccountResponse)(nil), "dymensionxyz.dymension.dymns.QueryDymNamesOwnedByAccountResponse") + proto.RegisterType((*QuerySellOrderRequest)(nil), "dymensionxyz.dymension.dymns.QuerySellOrderRequest") + proto.RegisterType((*QuerySellOrderResponse)(nil), "dymensionxyz.dymension.dymns.QuerySellOrderResponse") + proto.RegisterType((*EstimateRegisterNameRequest)(nil), "dymensionxyz.dymension.dymns.EstimateRegisterNameRequest") + proto.RegisterType((*EstimateRegisterNameResponse)(nil), "dymensionxyz.dymension.dymns.EstimateRegisterNameResponse") + proto.RegisterType((*EstimateRegisterAliasRequest)(nil), "dymensionxyz.dymension.dymns.EstimateRegisterAliasRequest") + proto.RegisterType((*EstimateRegisterAliasResponse)(nil), "dymensionxyz.dymension.dymns.EstimateRegisterAliasResponse") + proto.RegisterType((*ReverseResolveAddressRequest)(nil), "dymensionxyz.dymension.dymns.ReverseResolveAddressRequest") + proto.RegisterType((*ReverseResolveAddressResponse)(nil), "dymensionxyz.dymension.dymns.ReverseResolveAddressResponse") + proto.RegisterMapType((map[string]ReverseResolveAddressResult)(nil), "dymensionxyz.dymension.dymns.ReverseResolveAddressResponse.ResultEntry") + proto.RegisterType((*ReverseResolveAddressResult)(nil), "dymensionxyz.dymension.dymns.ReverseResolveAddressResult") + proto.RegisterType((*QueryTranslateAliasOrChainIdToChainIdRequest)(nil), "dymensionxyz.dymension.dymns.QueryTranslateAliasOrChainIdToChainIdRequest") + proto.RegisterType((*QueryTranslateAliasOrChainIdToChainIdResponse)(nil), "dymensionxyz.dymension.dymns.QueryTranslateAliasOrChainIdToChainIdResponse") + proto.RegisterType((*QueryBuyOrderByIdRequest)(nil), "dymensionxyz.dymension.dymns.QueryBuyOrderByIdRequest") + proto.RegisterType((*QueryBuyOrderByIdResponse)(nil), "dymensionxyz.dymension.dymns.QueryBuyOrderByIdResponse") + proto.RegisterType((*QueryBuyOrdersPlacedByAccountRequest)(nil), "dymensionxyz.dymension.dymns.QueryBuyOrdersPlacedByAccountRequest") + proto.RegisterType((*QueryBuyOrdersPlacedByAccountResponse)(nil), "dymensionxyz.dymension.dymns.QueryBuyOrdersPlacedByAccountResponse") + proto.RegisterType((*QueryBuyOrdersByDymNameRequest)(nil), "dymensionxyz.dymension.dymns.QueryBuyOrdersByDymNameRequest") + proto.RegisterType((*QueryBuyOrdersByDymNameResponse)(nil), "dymensionxyz.dymension.dymns.QueryBuyOrdersByDymNameResponse") + proto.RegisterType((*QueryBuyOrdersOfDymNamesOwnedByAccountRequest)(nil), "dymensionxyz.dymension.dymns.QueryBuyOrdersOfDymNamesOwnedByAccountRequest") + proto.RegisterType((*QueryBuyOrdersOfDymNamesOwnedByAccountResponse)(nil), "dymensionxyz.dymension.dymns.QueryBuyOrdersOfDymNamesOwnedByAccountResponse") + proto.RegisterType((*QueryBuyOrdersByAliasRequest)(nil), "dymensionxyz.dymension.dymns.QueryBuyOrdersByAliasRequest") + proto.RegisterType((*QueryBuyOrdersByAliasResponse)(nil), "dymensionxyz.dymension.dymns.QueryBuyOrdersByAliasResponse") + proto.RegisterType((*QueryBuyOrdersOfAliasesLinkedToRollAppRequest)(nil), "dymensionxyz.dymension.dymns.QueryBuyOrdersOfAliasesLinkedToRollAppRequest") + proto.RegisterType((*QueryBuyOrdersOfAliasesLinkedToRollAppResponse)(nil), "dymensionxyz.dymension.dymns.QueryBuyOrdersOfAliasesLinkedToRollAppResponse") +} + +func init() { + proto.RegisterFile("dymensionxyz/dymension/dymns/query.proto", fileDescriptor_c9fbab881fb7aa6c) +} + +var fileDescriptor_c9fbab881fb7aa6c = []byte{ + // 1899 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x59, 0xcf, 0x73, 0x1b, 0x49, + 0x15, 0xf6, 0xc8, 0x71, 0x62, 0x3d, 0x2d, 0xc6, 0xe9, 0x75, 0x16, 0x47, 0xeb, 0x28, 0x61, 0x48, + 0x76, 0x1d, 0x36, 0xd6, 0x24, 0x72, 0x12, 0x48, 0x42, 0x0a, 0x5b, 0x4e, 0x96, 0x78, 0x13, 0xe2, + 0xa0, 0x75, 0xc1, 0x66, 0x2f, 0x53, 0x23, 0x4d, 0x5b, 0x3b, 0x95, 0xd1, 0xb4, 0x32, 0x3d, 0x72, + 0x32, 0xa8, 0x74, 0xe1, 0x40, 0x01, 0x27, 0xaa, 0xb8, 0x50, 0x70, 0x80, 0x13, 0x97, 0x3d, 0x52, + 0xfc, 0x09, 0x14, 0x7b, 0xa2, 0xb6, 0x8a, 0xe2, 0xc7, 0x05, 0x8a, 0x4a, 0x38, 0x70, 0xa3, 0xf8, + 0x0f, 0xb6, 0xa6, 0xe7, 0xf5, 0x68, 0x46, 0x19, 0xcd, 0x8c, 0xb2, 0x9b, 0x93, 0xbb, 0x7b, 0xfa, + 0xbd, 0xfe, 0xbe, 0xd7, 0xdd, 0xef, 0xf5, 0x27, 0xc3, 0xba, 0xe9, 0xf7, 0xa8, 0xc3, 0x2d, 0xe6, + 0x3c, 0xf5, 0x7f, 0xa8, 0x45, 0x9d, 0xa0, 0xe5, 0x70, 0xed, 0xf1, 0x80, 0xba, 0x7e, 0xbd, 0xef, + 0x32, 0x8f, 0x91, 0xb5, 0xf8, 0xcc, 0x7a, 0xd4, 0xa9, 0x8b, 0x99, 0xd5, 0x95, 0x2e, 0xeb, 0x32, + 0x31, 0x51, 0x0b, 0x5a, 0xa1, 0x4d, 0x75, 0xad, 0xcb, 0x58, 0xd7, 0xa6, 0x9a, 0xd1, 0xb7, 0x34, + 0xc3, 0x71, 0x98, 0x67, 0x78, 0x16, 0x73, 0x38, 0x7e, 0xad, 0x75, 0x18, 0xef, 0x31, 0xae, 0xb5, + 0x0d, 0x4e, 0xb5, 0xc3, 0x4b, 0x6d, 0xea, 0x19, 0x97, 0xb4, 0x0e, 0xb3, 0x1c, 0xfc, 0x7e, 0x3e, + 0x13, 0x5b, 0xdf, 0x70, 0x8d, 0x9e, 0x74, 0xf5, 0x4e, 0xe6, 0x54, 0xd3, 0xef, 0xe9, 0x8e, 0xd1, + 0xa3, 0x85, 0xfc, 0xf6, 0x0c, 0xf7, 0x11, 0xf5, 0x70, 0x6a, 0x76, 0x78, 0x0c, 0xdb, 0x32, 0x10, + 0x81, 0xba, 0x02, 0xe4, 0x7b, 0x41, 0xb4, 0x1e, 0x08, 0x58, 0x2d, 0xfa, 0x78, 0x40, 0xb9, 0xa7, + 0x3e, 0x84, 0xd7, 0x13, 0xa3, 0xbc, 0xcf, 0x1c, 0x4e, 0x49, 0x13, 0x8e, 0x86, 0xf0, 0x57, 0x95, + 0x33, 0xca, 0x7a, 0xa5, 0x71, 0xb6, 0x9e, 0x15, 0xdc, 0x7a, 0x68, 0xdd, 0x3c, 0xf2, 0xc9, 0xbf, + 0x4e, 0xcf, 0xb5, 0xd0, 0x52, 0xbd, 0x8a, 0xae, 0x6f, 0xf9, 0xbd, 0xfb, 0x46, 0x8f, 0xe2, 0x8a, + 0xe4, 0x24, 0x2c, 0x4a, 0xba, 0xc2, 0x79, 0xb9, 0x75, 0xcc, 0x0c, 0x67, 0x5c, 0x3f, 0xf2, 0xdf, + 0xdf, 0x9e, 0x9e, 0x53, 0x3f, 0x80, 0x95, 0xa4, 0x1d, 0x62, 0xda, 0x9a, 0x30, 0xac, 0x34, 0xce, + 0x65, 0xa3, 0x92, 0x0e, 0xa4, 0x7f, 0x55, 0x83, 0xe3, 0xc2, 0xf3, 0x76, 0x10, 0x16, 0x89, 0x67, + 0x05, 0x16, 0x44, 0x98, 0x10, 0x4c, 0xd8, 0x41, 0x28, 0x1f, 0x2b, 0x18, 0x34, 0xb4, 0x40, 0x24, + 0x27, 0x61, 0xb1, 0xf3, 0x91, 0x61, 0x39, 0xba, 0x65, 0x4a, 0x0a, 0xa2, 0xbf, 0x6b, 0x92, 0x75, + 0x58, 0x3e, 0x60, 0x03, 0xc7, 0xd4, 0x39, 0xb5, 0x6d, 0x9d, 0xb9, 0x26, 0x75, 0x57, 0x4b, 0x67, + 0x94, 0xf5, 0xc5, 0xd6, 0x92, 0x18, 0x7f, 0x9f, 0xda, 0xf6, 0x5e, 0x30, 0x4a, 0x54, 0xf8, 0x52, + 0x7b, 0xe0, 0x87, 0x53, 0x74, 0xcb, 0xe4, 0xab, 0xf3, 0x67, 0xe6, 0xd7, 0xcb, 0xad, 0x4a, 0x7b, + 0xe0, 0x8b, 0x09, 0xbb, 0x26, 0x27, 0x17, 0x80, 0x70, 0xa3, 0x47, 0xf5, 0x70, 0x35, 0x81, 0x8c, + 0xf2, 0xd5, 0x23, 0x62, 0xe2, 0x72, 0xf0, 0x65, 0x27, 0xf8, 0xb0, 0x1d, 0x8e, 0x47, 0x01, 0xc7, + 0x7e, 0x2c, 0xe0, 0x53, 0xd0, 0x22, 0xcb, 0x9f, 0x96, 0x30, 0xe2, 0x91, 0x21, 0xf2, 0x1c, 0xc1, + 0xeb, 0xb8, 0xa6, 0xde, 0xf6, 0xf5, 0x98, 0x93, 0xf9, 0xf5, 0x4a, 0xe3, 0x4e, 0x76, 0xf0, 0xd3, + 0x1c, 0xd6, 0xb1, 0xdf, 0xf4, 0x77, 0x42, 0x00, 0xb7, 0x1d, 0xcf, 0xf5, 0xf1, 0xd8, 0x2c, 0x1b, + 0x13, 0x1f, 0xab, 0x2e, 0x9c, 0x48, 0x35, 0x20, 0xcb, 0x30, 0xff, 0x88, 0xfa, 0x48, 0x26, 0x68, + 0x92, 0x1d, 0x58, 0x38, 0x34, 0xec, 0x01, 0x15, 0xb1, 0xae, 0x34, 0x36, 0xb2, 0xb1, 0x7d, 0x77, + 0x60, 0x7b, 0x56, 0xdf, 0xa6, 0x12, 0x5e, 0x68, 0x7b, 0xbd, 0xf4, 0x4d, 0x45, 0xbd, 0x05, 0xb5, + 0x16, 0xe5, 0xcc, 0x3e, 0xa4, 0x78, 0x7a, 0xb6, 0x4d, 0xd3, 0xa5, 0x3c, 0x16, 0xce, 0x35, 0x28, + 0x1b, 0x72, 0x4c, 0x84, 0xa2, 0xdc, 0x1a, 0x0f, 0x60, 0x44, 0x1f, 0xc3, 0x4a, 0x8b, 0xf2, 0x81, + 0xed, 0x25, 0x9d, 0x90, 0x55, 0x38, 0x86, 0x53, 0xe5, 0x4e, 0x60, 0x97, 0x9c, 0x87, 0x65, 0x37, + 0x5c, 0xd7, 0xd4, 0xe5, 0x94, 0x92, 0x98, 0xf2, 0x65, 0x39, 0x2e, 0x9d, 0xac, 0xc0, 0x02, 0x75, + 0x5d, 0xe6, 0xae, 0xce, 0x87, 0x07, 0x56, 0x74, 0xd4, 0x9f, 0x29, 0x70, 0x7a, 0x2a, 0x72, 0xdc, + 0xcf, 0x2e, 0x90, 0xc9, 0x45, 0x90, 0x43, 0xa5, 0xd1, 0xc8, 0x0e, 0x59, 0x1a, 0x1d, 0xdc, 0xb8, + 0xe3, 0x13, 0x00, 0x29, 0x57, 0xb7, 0x40, 0x8d, 0x5f, 0x61, 0xbe, 0xf7, 0xc4, 0xa1, 0x66, 0xd3, + 0xdf, 0xee, 0x74, 0xd8, 0xc0, 0xf1, 0x62, 0x37, 0x8f, 0x3d, 0x71, 0xa8, 0x2b, 0x6f, 0x9e, 0xe8, + 0x60, 0x04, 0x19, 0x7c, 0x2d, 0xd3, 0x03, 0x32, 0xba, 0x03, 0x65, 0x99, 0x13, 0x24, 0x91, 0x62, + 0x49, 0x01, 0xb1, 0x2f, 0x62, 0x6a, 0xe0, 0xea, 0x0f, 0xe0, 0x84, 0x58, 0x30, 0xba, 0xa0, 0xb1, + 0xeb, 0x63, 0x70, 0x4e, 0xbd, 0xd8, 0xf5, 0x11, 0xfd, 0x5d, 0x93, 0x9c, 0x02, 0x08, 0x3f, 0x79, + 0x7e, 0x9f, 0xe2, 0x76, 0x95, 0xc5, 0xc8, 0xbe, 0xdf, 0x97, 0xe9, 0x4c, 0x87, 0x37, 0x26, 0x1d, + 0x23, 0xf8, 0xdb, 0x70, 0xd4, 0x15, 0x61, 0xc5, 0x74, 0xf6, 0x76, 0x36, 0xf2, 0xc8, 0x81, 0xcc, + 0xb3, 0xa1, 0xb1, 0x6a, 0xc1, 0x9b, 0xb7, 0xb9, 0x67, 0xf5, 0x0c, 0x8f, 0xb6, 0x68, 0xd7, 0xe2, + 0x1e, 0x75, 0xe3, 0xf9, 0x96, 0xc0, 0x91, 0x58, 0xae, 0x15, 0x6d, 0x52, 0x85, 0x45, 0x73, 0xe0, + 0x8a, 0x5a, 0x27, 0x60, 0xcf, 0xb7, 0xa2, 0xfe, 0x78, 0x57, 0xe6, 0x5f, 0xdc, 0x95, 0xff, 0x29, + 0xb0, 0x96, 0xbe, 0x16, 0x52, 0xda, 0x85, 0xe5, 0x03, 0xcb, 0xe5, 0x9e, 0xee, 0x53, 0xc3, 0xd5, + 0xfb, 0xae, 0xd5, 0x91, 0xb9, 0xfa, 0x64, 0x3d, 0x2c, 0xa6, 0xf5, 0xa0, 0x98, 0xd6, 0xb1, 0x98, + 0xd6, 0x77, 0x98, 0xe5, 0x20, 0x9d, 0x25, 0x61, 0xf8, 0x90, 0x1a, 0xee, 0x83, 0xc0, 0x8c, 0x34, + 0xe1, 0x35, 0xfa, 0xd4, 0xa3, 0x8e, 0x89, 0x6e, 0x4a, 0xc5, 0xdc, 0x54, 0x42, 0xa3, 0xd0, 0xc7, + 0x16, 0x54, 0x3c, 0xe6, 0x19, 0x36, 0xba, 0x98, 0x2f, 0xe6, 0x02, 0x84, 0x8d, 0xf0, 0xa0, 0xb2, + 0x17, 0x09, 0xe7, 0x57, 0x8f, 0xe0, 0x60, 0xb8, 0xcc, 0xb6, 0x8d, 0x7e, 0x3f, 0x38, 0x35, 0x78, + 0x30, 0x70, 0x64, 0xd7, 0xcc, 0x0c, 0xf1, 0xf7, 0xe1, 0xd4, 0x94, 0x05, 0x31, 0xc4, 0x57, 0x60, + 0x61, 0xa6, 0xb8, 0x86, 0xb3, 0xd5, 0x03, 0x58, 0x6b, 0xd1, 0x43, 0xea, 0x72, 0x8a, 0x59, 0x02, + 0x6f, 0x6b, 0xa1, 0xb4, 0x16, 0x94, 0xb5, 0x27, 0xcc, 0x7d, 0x64, 0x39, 0xdd, 0x71, 0x19, 0x08, + 0x69, 0x2d, 0xe1, 0x38, 0x26, 0x68, 0xf5, 0x77, 0x25, 0x38, 0x35, 0x65, 0x21, 0x24, 0x40, 0x63, + 0xc7, 0x3e, 0xb8, 0xb0, 0xdf, 0xc9, 0xcb, 0x3c, 0x19, 0xce, 0x30, 0x2f, 0xc5, 0xeb, 0x08, 0x3a, + 0x2f, 0x0e, 0xb9, 0xea, 0x41, 0x25, 0xe6, 0x26, 0xa5, 0xba, 0xec, 0x25, 0xab, 0xcb, 0xb5, 0x97, + 0x03, 0x3c, 0xb0, 0xbd, 0x78, 0xa5, 0x79, 0x1f, 0xde, 0xcc, 0x98, 0x49, 0x6a, 0x00, 0x1d, 0xc3, + 0x31, 0x2d, 0xd3, 0xf0, 0xa2, 0x0d, 0x89, 0x8d, 0x8c, 0xab, 0x40, 0x29, 0x5e, 0x05, 0x1e, 0xc2, + 0x05, 0x91, 0x6c, 0xf6, 0x5d, 0xc3, 0xe1, 0xb6, 0xe1, 0x85, 0x25, 0x6e, 0xcf, 0x45, 0xaa, 0xfb, + 0x0c, 0x1b, 0x72, 0xd7, 0xcf, 0xc3, 0x71, 0x71, 0x62, 0x75, 0xe6, 0xea, 0x13, 0x8f, 0x84, 0x25, + 0x23, 0x61, 0xaa, 0xbe, 0x07, 0x1b, 0x05, 0x5d, 0xe7, 0xbe, 0x92, 0xd4, 0xaf, 0xc3, 0xaa, 0xf0, + 0xd5, 0xc4, 0xb7, 0x4e, 0xd3, 0x1f, 0x43, 0x5a, 0x82, 0x52, 0x64, 0x50, 0xb2, 0x4c, 0xf5, 0x00, + 0x4e, 0xa6, 0xcc, 0x8d, 0xf2, 0x4d, 0x39, 0x7a, 0x44, 0xe1, 0x85, 0x78, 0x2b, 0x7b, 0x77, 0x22, + 0x37, 0x58, 0x00, 0xe4, 0x73, 0x4b, 0xdd, 0x82, 0xb3, 0x89, 0x75, 0xf8, 0x03, 0xdb, 0xe8, 0xa4, + 0x54, 0xad, 0xa0, 0x86, 0x87, 0x23, 0x51, 0x39, 0x08, 0xbb, 0xaa, 0x07, 0xe7, 0x72, 0x3c, 0x20, + 0xea, 0xbb, 0x00, 0x11, 0x6a, 0x59, 0xb6, 0x66, 0x83, 0x5d, 0x96, 0xb0, 0xb9, 0x7a, 0x19, 0x6a, + 0xc9, 0x55, 0x9b, 0x93, 0x2f, 0xee, 0x94, 0x0a, 0xa0, 0x3a, 0x70, 0x7a, 0xaa, 0xd5, 0xab, 0x40, + 0xb9, 0x8b, 0xa7, 0x27, 0x5a, 0x6f, 0xef, 0x20, 0xfb, 0x71, 0x30, 0x3d, 0xcc, 0x23, 0xa8, 0x17, + 0x75, 0xf5, 0x6a, 0xe2, 0xbd, 0x36, 0x19, 0xb9, 0xfc, 0x8a, 0xa0, 0xda, 0x70, 0x6a, 0x8a, 0xd5, + 0xab, 0xc0, 0x78, 0xff, 0xc5, 0x68, 0xe3, 0x5b, 0xf7, 0x9e, 0xe5, 0x3c, 0xa2, 0xe6, 0x3e, 0x6b, + 0x31, 0xdb, 0xde, 0xee, 0xf7, 0x25, 0xe8, 0x64, 0xc1, 0x52, 0x26, 0x0a, 0x56, 0x5a, 0xc8, 0xa7, + 0xf9, 0x7b, 0x05, 0x74, 0x1a, 0x3f, 0x59, 0x83, 0x05, 0xb1, 0x3e, 0xf9, 0xb5, 0x02, 0x47, 0x43, + 0xb1, 0x49, 0x2e, 0x16, 0xd0, 0x1f, 0x09, 0xad, 0x5b, 0xbd, 0x34, 0x83, 0x45, 0x48, 0x43, 0xbd, + 0xf0, 0xa3, 0xbf, 0xfc, 0xe7, 0x17, 0xa5, 0xb7, 0xc8, 0x59, 0xad, 0x80, 0xd4, 0x27, 0x1f, 0x2b, + 0x70, 0x0c, 0x8f, 0x22, 0x29, 0xb2, 0x58, 0xf2, 0x9e, 0x56, 0x1b, 0xb3, 0x98, 0x20, 0xc0, 0x6b, + 0x02, 0xe0, 0x26, 0xb9, 0xa4, 0x15, 0xfa, 0x81, 0x41, 0x1b, 0xca, 0xd6, 0x88, 0xfc, 0x46, 0x81, + 0x05, 0xb1, 0x8b, 0x44, 0x2b, 0x2a, 0xe5, 0x24, 0xd2, 0x8b, 0xc5, 0x0d, 0x10, 0xe7, 0xa6, 0xc0, + 0xb9, 0x41, 0xde, 0xd1, 0xf2, 0x7f, 0xb0, 0xd0, 0x86, 0xe2, 0x8f, 0x40, 0x78, 0x0c, 0xcf, 0x59, + 0xa1, 0x78, 0x26, 0x85, 0x6f, 0xa1, 0x78, 0x4e, 0x28, 0x54, 0x75, 0x43, 0xe0, 0x7c, 0x9b, 0x9c, + 0x2b, 0x80, 0x93, 0x72, 0xf2, 0x47, 0x05, 0xbe, 0x32, 0x45, 0x75, 0x91, 0x6f, 0xe5, 0x2a, 0xaa, + 0x0c, 0x99, 0x59, 0xbd, 0xf9, 0x92, 0xd6, 0xb3, 0xf1, 0x40, 0xe9, 0x46, 0xfe, 0xaa, 0xc0, 0x1b, + 0xe9, 0x49, 0x94, 0x6c, 0x15, 0x3f, 0x95, 0xe9, 0xa9, 0xbc, 0xba, 0xfd, 0x39, 0x3c, 0x20, 0x9d, + 0xab, 0x82, 0xce, 0x45, 0x52, 0xcf, 0xa6, 0x13, 0x3c, 0xa4, 0x4d, 0xbd, 0xed, 0x6b, 0x43, 0xf1, + 0xa4, 0x1e, 0x91, 0xdf, 0x2b, 0x50, 0x1e, 0xff, 0xe4, 0xb2, 0x59, 0x00, 0xc8, 0xa4, 0xfe, 0xab, + 0x5e, 0x9e, 0xcd, 0x08, 0x01, 0xdf, 0x10, 0x80, 0xaf, 0x90, 0xcd, 0x6c, 0xc0, 0xe3, 0x5f, 0x89, + 0xb4, 0xa1, 0x54, 0x99, 0x23, 0xf2, 0x4f, 0x05, 0x56, 0xd2, 0x64, 0x16, 0xc9, 0x79, 0x79, 0x66, + 0xc8, 0xc0, 0xea, 0xf5, 0x97, 0x31, 0x45, 0x32, 0xf7, 0x05, 0x99, 0x3b, 0xe4, 0xdd, 0x6c, 0x32, + 0x14, 0x7d, 0xe8, 0x2e, 0x3a, 0xc1, 0x94, 0x23, 0xd2, 0x8d, 0x36, 0x94, 0x0a, 0x73, 0x44, 0xfe, + 0xae, 0xc0, 0x89, 0x54, 0x91, 0x43, 0x66, 0x44, 0x99, 0x48, 0x4a, 0x37, 0x5e, 0xca, 0x16, 0x29, + 0xde, 0x16, 0x14, 0xbf, 0x4d, 0x6e, 0xce, 0x4a, 0x31, 0x99, 0xb1, 0xfe, 0xa4, 0xc0, 0x89, 0xd4, + 0x57, 0x7d, 0x1e, 0xb3, 0x2c, 0x6d, 0x96, 0xc7, 0x2c, 0x53, 0x21, 0xa9, 0x57, 0x04, 0x33, 0x8d, + 0x6c, 0xe4, 0x65, 0x02, 0xe1, 0x44, 0x97, 0x19, 0xe1, 0xc7, 0x25, 0x38, 0x93, 0xf7, 0xd4, 0x27, + 0xef, 0x15, 0xb8, 0x1b, 0x05, 0xa5, 0x48, 0xf5, 0xee, 0x17, 0xe2, 0x0b, 0x49, 0xef, 0x0a, 0xd2, + 0x3b, 0x64, 0x3b, 0x9b, 0xb4, 0x27, 0xfd, 0x25, 0xb6, 0x31, 0x2e, 0x86, 0x46, 0xe4, 0x0f, 0x0a, + 0xbc, 0x16, 0xd7, 0x1e, 0xe4, 0x6a, 0x01, 0xa0, 0x29, 0xc2, 0xa6, 0xfa, 0x8d, 0x99, 0xed, 0x90, + 0xcc, 0x65, 0x41, 0xa6, 0x4e, 0x2e, 0x64, 0x93, 0x89, 0xde, 0x5b, 0xda, 0x30, 0xc0, 0xfd, 0x7f, + 0x05, 0x56, 0xa7, 0x29, 0x11, 0xd2, 0x9c, 0x01, 0xcb, 0x14, 0x21, 0x54, 0xdd, 0xf9, 0x5c, 0x3e, + 0x90, 0xdb, 0x3d, 0xc1, 0xed, 0x5d, 0x72, 0xab, 0x20, 0x37, 0xae, 0xf7, 0x85, 0x27, 0xbd, 0xed, + 0xeb, 0x28, 0x08, 0xb4, 0x21, 0x36, 0x46, 0xe4, 0x6f, 0x0a, 0x90, 0x17, 0x15, 0x4d, 0x5e, 0x25, + 0xce, 0x96, 0x4f, 0x79, 0x95, 0x38, 0x47, 0x46, 0xa9, 0x3b, 0x82, 0xe1, 0x4d, 0x72, 0xa3, 0x30, + 0xc3, 0xb6, 0xaf, 0x8f, 0xdf, 0x6b, 0xe1, 0x5b, 0xed, 0x97, 0x25, 0xf8, 0x6a, 0xae, 0xde, 0x21, + 0x77, 0x67, 0x41, 0x9a, 0x23, 0xc0, 0xaa, 0xf7, 0xbe, 0x18, 0x67, 0x18, 0x85, 0x0f, 0x44, 0x14, + 0x5a, 0xe4, 0x41, 0xe1, 0x28, 0xb0, 0x83, 0x28, 0x0a, 0x5c, 0x97, 0x85, 0x3d, 0x65, 0xcf, 0xff, + 0xac, 0xc0, 0xf2, 0xa4, 0xaa, 0xca, 0xcb, 0xb6, 0x59, 0x02, 0x2e, 0x2f, 0xdb, 0x66, 0xca, 0x38, + 0x75, 0x5b, 0xf0, 0xbc, 0x41, 0xae, 0xcd, 0xb2, 0xdb, 0xc9, 0x1a, 0xf2, 0xab, 0xe4, 0x5e, 0xa7, + 0x0b, 0xad, 0x59, 0xf7, 0x3a, 0x53, 0xfe, 0xcd, 0xba, 0xd7, 0xd9, 0xda, 0x4f, 0xfd, 0x50, 0xc4, + 0x60, 0x9f, 0xb4, 0x66, 0xd9, 0x6b, 0xf9, 0x8f, 0x26, 0x5b, 0x38, 0xd5, 0x3d, 0xa6, 0xa3, 0xfc, + 0xd4, 0x86, 0x63, 0x65, 0x3a, 0x6a, 0xde, 0xfb, 0xe4, 0x59, 0x4d, 0xf9, 0xf4, 0x59, 0x4d, 0xf9, + 0xf7, 0xb3, 0x9a, 0xf2, 0xf3, 0xe7, 0xb5, 0xb9, 0x4f, 0x9f, 0xd7, 0xe6, 0xfe, 0xf1, 0xbc, 0x36, + 0xf7, 0x61, 0xa3, 0x6b, 0x79, 0x1f, 0x0d, 0xda, 0xf5, 0x0e, 0xeb, 0x4d, 0x5b, 0xf7, 0x70, 0x53, + 0x7b, 0x2a, 0x33, 0xbf, 0xdf, 0xa7, 0xbc, 0x7d, 0x54, 0xfc, 0x6b, 0x74, 0xf3, 0xb3, 0x00, 0x00, + 0x00, 0xff, 0xff, 0x97, 0xbd, 0xfa, 0x07, 0x65, 0x1e, 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 { + // Params queries the parameters of the module. + Params(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error) + // DymName queries a Dym-Name by its name. + DymName(ctx context.Context, in *QueryDymNameRequest, opts ...grpc.CallOption) (*QueryDymNameResponse, error) + // Alias queries the chain_id associated as well as the Sell-Order and Buy-Order IDs relates to the alias. + Alias(ctx context.Context, in *QueryAliasRequest, opts ...grpc.CallOption) (*QueryAliasResponse, error) + // Aliases queries all the aliases for a chain id or all chains. + Aliases(ctx context.Context, in *QueryAliasesRequest, opts ...grpc.CallOption) (*QueryAliasesResponse, error) + // ResolveDymNameAddresses resolves multiple Dym-Name Addresses to account address of each pointing to. + // + // For example: + // - "my-name@dym" => "dym1a..." + // - "another.my-name@dym" => "dym1b..." + // - "my-name@nim" => "nim1..." + // - (extra format) "0x1234...6789@nim" => "nim1..." + // - (extra format) "dym1a...@nim" => "nim1..." + ResolveDymNameAddresses(ctx context.Context, in *ResolveDymNameAddressesRequest, opts ...grpc.CallOption) (*ResolveDymNameAddressesResponse, error) + // DymNamesOwnedByAccount queries the Dym-Names owned by an account. + DymNamesOwnedByAccount(ctx context.Context, in *QueryDymNamesOwnedByAccountRequest, opts ...grpc.CallOption) (*QueryDymNamesOwnedByAccountResponse, error) + // SellOrder queries the active SO of a Dym-Name/Alias. + SellOrder(ctx context.Context, in *QuerySellOrderRequest, opts ...grpc.CallOption) (*QuerySellOrderResponse, error) + // EstimateRegisterName estimates the cost to register a Dym-Name. + EstimateRegisterName(ctx context.Context, in *EstimateRegisterNameRequest, opts ...grpc.CallOption) (*EstimateRegisterNameResponse, error) + // EstimateRegisterAlias estimates the cost to register an Alias. + EstimateRegisterAlias(ctx context.Context, in *EstimateRegisterAliasRequest, opts ...grpc.CallOption) (*EstimateRegisterAliasResponse, error) + // ReverseResolveAddress resolves multiple account addresses to Dym-Name Addresses which point to each. + // This function may return multiple possible Dym-Name-Addresses those point to each of the input address. + // + // For example: when we have "my-name@dym" resolves to "dym1a..." + // so reverse resolve will return "my-name@dym" when input is "dym1a..." + ReverseResolveAddress(ctx context.Context, in *ReverseResolveAddressRequest, opts ...grpc.CallOption) (*ReverseResolveAddressResponse, error) + // TranslateAliasOrChainIdToChainId tries to translate an alias/handle to a chain id. + // If an alias/handle can not be translated to chain-id, it is treated as a chain-id and returns. + TranslateAliasOrChainIdToChainId(ctx context.Context, in *QueryTranslateAliasOrChainIdToChainIdRequest, opts ...grpc.CallOption) (*QueryTranslateAliasOrChainIdToChainIdResponse, error) + // BuyOrderById queries a Buy-Order by its id. + BuyOrderById(ctx context.Context, in *QueryBuyOrderByIdRequest, opts ...grpc.CallOption) (*QueryBuyOrderByIdResponse, error) + // BuyOrdersPlacedByAccount queries the all the buy orders placed by an account. + BuyOrdersPlacedByAccount(ctx context.Context, in *QueryBuyOrdersPlacedByAccountRequest, opts ...grpc.CallOption) (*QueryBuyOrdersPlacedByAccountResponse, error) + // BuyOrdersByDymName queries all the buy orders of a Dym-Name. + BuyOrdersByDymName(ctx context.Context, in *QueryBuyOrdersByDymNameRequest, opts ...grpc.CallOption) (*QueryBuyOrdersByDymNameResponse, error) + // BuyOrdersOfDymNamesOwnedByAccount queries all the buy orders of all Dym-Names owned by an account. + BuyOrdersOfDymNamesOwnedByAccount(ctx context.Context, in *QueryBuyOrdersOfDymNamesOwnedByAccountRequest, opts ...grpc.CallOption) (*QueryBuyOrdersOfDymNamesOwnedByAccountResponse, error) + // BuyOrdersByAlias queries all the buy orders of an Alias. + BuyOrdersByAlias(ctx context.Context, in *QueryBuyOrdersByAliasRequest, opts ...grpc.CallOption) (*QueryBuyOrdersByAliasResponse, error) + // BuyOrdersOfAliasesLinkedToRollApp queries all the buy orders of all Aliases linked to a RollApp. + BuyOrdersOfAliasesLinkedToRollApp(ctx context.Context, in *QueryBuyOrdersOfAliasesLinkedToRollAppRequest, opts ...grpc.CallOption) (*QueryBuyOrdersOfAliasesLinkedToRollAppResponse, error) +} + +type queryClient struct { + cc grpc1.ClientConn +} + +func NewQueryClient(cc grpc1.ClientConn) QueryClient { + return &queryClient{cc} +} + +func (c *queryClient) Params(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error) { + out := new(QueryParamsResponse) + err := c.cc.Invoke(ctx, "/dymensionxyz.dymension.dymns.Query/Params", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) DymName(ctx context.Context, in *QueryDymNameRequest, opts ...grpc.CallOption) (*QueryDymNameResponse, error) { + out := new(QueryDymNameResponse) + err := c.cc.Invoke(ctx, "/dymensionxyz.dymension.dymns.Query/DymName", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) Alias(ctx context.Context, in *QueryAliasRequest, opts ...grpc.CallOption) (*QueryAliasResponse, error) { + out := new(QueryAliasResponse) + err := c.cc.Invoke(ctx, "/dymensionxyz.dymension.dymns.Query/Alias", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) Aliases(ctx context.Context, in *QueryAliasesRequest, opts ...grpc.CallOption) (*QueryAliasesResponse, error) { + out := new(QueryAliasesResponse) + err := c.cc.Invoke(ctx, "/dymensionxyz.dymension.dymns.Query/Aliases", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) ResolveDymNameAddresses(ctx context.Context, in *ResolveDymNameAddressesRequest, opts ...grpc.CallOption) (*ResolveDymNameAddressesResponse, error) { + out := new(ResolveDymNameAddressesResponse) + err := c.cc.Invoke(ctx, "/dymensionxyz.dymension.dymns.Query/ResolveDymNameAddresses", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) DymNamesOwnedByAccount(ctx context.Context, in *QueryDymNamesOwnedByAccountRequest, opts ...grpc.CallOption) (*QueryDymNamesOwnedByAccountResponse, error) { + out := new(QueryDymNamesOwnedByAccountResponse) + err := c.cc.Invoke(ctx, "/dymensionxyz.dymension.dymns.Query/DymNamesOwnedByAccount", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) SellOrder(ctx context.Context, in *QuerySellOrderRequest, opts ...grpc.CallOption) (*QuerySellOrderResponse, error) { + out := new(QuerySellOrderResponse) + err := c.cc.Invoke(ctx, "/dymensionxyz.dymension.dymns.Query/SellOrder", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) EstimateRegisterName(ctx context.Context, in *EstimateRegisterNameRequest, opts ...grpc.CallOption) (*EstimateRegisterNameResponse, error) { + out := new(EstimateRegisterNameResponse) + err := c.cc.Invoke(ctx, "/dymensionxyz.dymension.dymns.Query/EstimateRegisterName", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) EstimateRegisterAlias(ctx context.Context, in *EstimateRegisterAliasRequest, opts ...grpc.CallOption) (*EstimateRegisterAliasResponse, error) { + out := new(EstimateRegisterAliasResponse) + err := c.cc.Invoke(ctx, "/dymensionxyz.dymension.dymns.Query/EstimateRegisterAlias", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) ReverseResolveAddress(ctx context.Context, in *ReverseResolveAddressRequest, opts ...grpc.CallOption) (*ReverseResolveAddressResponse, error) { + out := new(ReverseResolveAddressResponse) + err := c.cc.Invoke(ctx, "/dymensionxyz.dymension.dymns.Query/ReverseResolveAddress", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) TranslateAliasOrChainIdToChainId(ctx context.Context, in *QueryTranslateAliasOrChainIdToChainIdRequest, opts ...grpc.CallOption) (*QueryTranslateAliasOrChainIdToChainIdResponse, error) { + out := new(QueryTranslateAliasOrChainIdToChainIdResponse) + err := c.cc.Invoke(ctx, "/dymensionxyz.dymension.dymns.Query/TranslateAliasOrChainIdToChainId", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) BuyOrderById(ctx context.Context, in *QueryBuyOrderByIdRequest, opts ...grpc.CallOption) (*QueryBuyOrderByIdResponse, error) { + out := new(QueryBuyOrderByIdResponse) + err := c.cc.Invoke(ctx, "/dymensionxyz.dymension.dymns.Query/BuyOrderById", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) BuyOrdersPlacedByAccount(ctx context.Context, in *QueryBuyOrdersPlacedByAccountRequest, opts ...grpc.CallOption) (*QueryBuyOrdersPlacedByAccountResponse, error) { + out := new(QueryBuyOrdersPlacedByAccountResponse) + err := c.cc.Invoke(ctx, "/dymensionxyz.dymension.dymns.Query/BuyOrdersPlacedByAccount", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) BuyOrdersByDymName(ctx context.Context, in *QueryBuyOrdersByDymNameRequest, opts ...grpc.CallOption) (*QueryBuyOrdersByDymNameResponse, error) { + out := new(QueryBuyOrdersByDymNameResponse) + err := c.cc.Invoke(ctx, "/dymensionxyz.dymension.dymns.Query/BuyOrdersByDymName", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) BuyOrdersOfDymNamesOwnedByAccount(ctx context.Context, in *QueryBuyOrdersOfDymNamesOwnedByAccountRequest, opts ...grpc.CallOption) (*QueryBuyOrdersOfDymNamesOwnedByAccountResponse, error) { + out := new(QueryBuyOrdersOfDymNamesOwnedByAccountResponse) + err := c.cc.Invoke(ctx, "/dymensionxyz.dymension.dymns.Query/BuyOrdersOfDymNamesOwnedByAccount", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) BuyOrdersByAlias(ctx context.Context, in *QueryBuyOrdersByAliasRequest, opts ...grpc.CallOption) (*QueryBuyOrdersByAliasResponse, error) { + out := new(QueryBuyOrdersByAliasResponse) + err := c.cc.Invoke(ctx, "/dymensionxyz.dymension.dymns.Query/BuyOrdersByAlias", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) BuyOrdersOfAliasesLinkedToRollApp(ctx context.Context, in *QueryBuyOrdersOfAliasesLinkedToRollAppRequest, opts ...grpc.CallOption) (*QueryBuyOrdersOfAliasesLinkedToRollAppResponse, error) { + out := new(QueryBuyOrdersOfAliasesLinkedToRollAppResponse) + err := c.cc.Invoke(ctx, "/dymensionxyz.dymension.dymns.Query/BuyOrdersOfAliasesLinkedToRollApp", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// QueryServer is the server API for Query service. +type QueryServer interface { + // Params queries the parameters of the module. + Params(context.Context, *QueryParamsRequest) (*QueryParamsResponse, error) + // DymName queries a Dym-Name by its name. + DymName(context.Context, *QueryDymNameRequest) (*QueryDymNameResponse, error) + // Alias queries the chain_id associated as well as the Sell-Order and Buy-Order IDs relates to the alias. + Alias(context.Context, *QueryAliasRequest) (*QueryAliasResponse, error) + // Aliases queries all the aliases for a chain id or all chains. + Aliases(context.Context, *QueryAliasesRequest) (*QueryAliasesResponse, error) + // ResolveDymNameAddresses resolves multiple Dym-Name Addresses to account address of each pointing to. + // + // For example: + // - "my-name@dym" => "dym1a..." + // - "another.my-name@dym" => "dym1b..." + // - "my-name@nim" => "nim1..." + // - (extra format) "0x1234...6789@nim" => "nim1..." + // - (extra format) "dym1a...@nim" => "nim1..." + ResolveDymNameAddresses(context.Context, *ResolveDymNameAddressesRequest) (*ResolveDymNameAddressesResponse, error) + // DymNamesOwnedByAccount queries the Dym-Names owned by an account. + DymNamesOwnedByAccount(context.Context, *QueryDymNamesOwnedByAccountRequest) (*QueryDymNamesOwnedByAccountResponse, error) + // SellOrder queries the active SO of a Dym-Name/Alias. + SellOrder(context.Context, *QuerySellOrderRequest) (*QuerySellOrderResponse, error) + // EstimateRegisterName estimates the cost to register a Dym-Name. + EstimateRegisterName(context.Context, *EstimateRegisterNameRequest) (*EstimateRegisterNameResponse, error) + // EstimateRegisterAlias estimates the cost to register an Alias. + EstimateRegisterAlias(context.Context, *EstimateRegisterAliasRequest) (*EstimateRegisterAliasResponse, error) + // ReverseResolveAddress resolves multiple account addresses to Dym-Name Addresses which point to each. + // This function may return multiple possible Dym-Name-Addresses those point to each of the input address. + // + // For example: when we have "my-name@dym" resolves to "dym1a..." + // so reverse resolve will return "my-name@dym" when input is "dym1a..." + ReverseResolveAddress(context.Context, *ReverseResolveAddressRequest) (*ReverseResolveAddressResponse, error) + // TranslateAliasOrChainIdToChainId tries to translate an alias/handle to a chain id. + // If an alias/handle can not be translated to chain-id, it is treated as a chain-id and returns. + TranslateAliasOrChainIdToChainId(context.Context, *QueryTranslateAliasOrChainIdToChainIdRequest) (*QueryTranslateAliasOrChainIdToChainIdResponse, error) + // BuyOrderById queries a Buy-Order by its id. + BuyOrderById(context.Context, *QueryBuyOrderByIdRequest) (*QueryBuyOrderByIdResponse, error) + // BuyOrdersPlacedByAccount queries the all the buy orders placed by an account. + BuyOrdersPlacedByAccount(context.Context, *QueryBuyOrdersPlacedByAccountRequest) (*QueryBuyOrdersPlacedByAccountResponse, error) + // BuyOrdersByDymName queries all the buy orders of a Dym-Name. + BuyOrdersByDymName(context.Context, *QueryBuyOrdersByDymNameRequest) (*QueryBuyOrdersByDymNameResponse, error) + // BuyOrdersOfDymNamesOwnedByAccount queries all the buy orders of all Dym-Names owned by an account. + BuyOrdersOfDymNamesOwnedByAccount(context.Context, *QueryBuyOrdersOfDymNamesOwnedByAccountRequest) (*QueryBuyOrdersOfDymNamesOwnedByAccountResponse, error) + // BuyOrdersByAlias queries all the buy orders of an Alias. + BuyOrdersByAlias(context.Context, *QueryBuyOrdersByAliasRequest) (*QueryBuyOrdersByAliasResponse, error) + // BuyOrdersOfAliasesLinkedToRollApp queries all the buy orders of all Aliases linked to a RollApp. + BuyOrdersOfAliasesLinkedToRollApp(context.Context, *QueryBuyOrdersOfAliasesLinkedToRollAppRequest) (*QueryBuyOrdersOfAliasesLinkedToRollAppResponse, error) +} + +// UnimplementedQueryServer can be embedded to have forward compatible implementations. +type UnimplementedQueryServer struct { +} + +func (*UnimplementedQueryServer) Params(ctx context.Context, req *QueryParamsRequest) (*QueryParamsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Params not implemented") +} +func (*UnimplementedQueryServer) DymName(ctx context.Context, req *QueryDymNameRequest) (*QueryDymNameResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method DymName not implemented") +} +func (*UnimplementedQueryServer) Alias(ctx context.Context, req *QueryAliasRequest) (*QueryAliasResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Alias not implemented") +} +func (*UnimplementedQueryServer) Aliases(ctx context.Context, req *QueryAliasesRequest) (*QueryAliasesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Aliases not implemented") +} +func (*UnimplementedQueryServer) ResolveDymNameAddresses(ctx context.Context, req *ResolveDymNameAddressesRequest) (*ResolveDymNameAddressesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ResolveDymNameAddresses not implemented") +} +func (*UnimplementedQueryServer) DymNamesOwnedByAccount(ctx context.Context, req *QueryDymNamesOwnedByAccountRequest) (*QueryDymNamesOwnedByAccountResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method DymNamesOwnedByAccount not implemented") +} +func (*UnimplementedQueryServer) SellOrder(ctx context.Context, req *QuerySellOrderRequest) (*QuerySellOrderResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method SellOrder not implemented") +} +func (*UnimplementedQueryServer) EstimateRegisterName(ctx context.Context, req *EstimateRegisterNameRequest) (*EstimateRegisterNameResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method EstimateRegisterName not implemented") +} +func (*UnimplementedQueryServer) EstimateRegisterAlias(ctx context.Context, req *EstimateRegisterAliasRequest) (*EstimateRegisterAliasResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method EstimateRegisterAlias not implemented") +} +func (*UnimplementedQueryServer) ReverseResolveAddress(ctx context.Context, req *ReverseResolveAddressRequest) (*ReverseResolveAddressResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ReverseResolveAddress not implemented") +} +func (*UnimplementedQueryServer) TranslateAliasOrChainIdToChainId(ctx context.Context, req *QueryTranslateAliasOrChainIdToChainIdRequest) (*QueryTranslateAliasOrChainIdToChainIdResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method TranslateAliasOrChainIdToChainId not implemented") +} +func (*UnimplementedQueryServer) BuyOrderById(ctx context.Context, req *QueryBuyOrderByIdRequest) (*QueryBuyOrderByIdResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method BuyOrderById not implemented") +} +func (*UnimplementedQueryServer) BuyOrdersPlacedByAccount(ctx context.Context, req *QueryBuyOrdersPlacedByAccountRequest) (*QueryBuyOrdersPlacedByAccountResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method BuyOrdersPlacedByAccount not implemented") +} +func (*UnimplementedQueryServer) BuyOrdersByDymName(ctx context.Context, req *QueryBuyOrdersByDymNameRequest) (*QueryBuyOrdersByDymNameResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method BuyOrdersByDymName not implemented") +} +func (*UnimplementedQueryServer) BuyOrdersOfDymNamesOwnedByAccount(ctx context.Context, req *QueryBuyOrdersOfDymNamesOwnedByAccountRequest) (*QueryBuyOrdersOfDymNamesOwnedByAccountResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method BuyOrdersOfDymNamesOwnedByAccount not implemented") +} +func (*UnimplementedQueryServer) BuyOrdersByAlias(ctx context.Context, req *QueryBuyOrdersByAliasRequest) (*QueryBuyOrdersByAliasResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method BuyOrdersByAlias not implemented") +} +func (*UnimplementedQueryServer) BuyOrdersOfAliasesLinkedToRollApp(ctx context.Context, req *QueryBuyOrdersOfAliasesLinkedToRollAppRequest) (*QueryBuyOrdersOfAliasesLinkedToRollAppResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method BuyOrdersOfAliasesLinkedToRollApp not implemented") +} + +func RegisterQueryServer(s grpc1.Server, srv QueryServer) { + s.RegisterService(&_Query_serviceDesc, srv) +} + +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: "/dymensionxyz.dymension.dymns.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_DymName_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryDymNameRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).DymName(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/dymensionxyz.dymension.dymns.Query/DymName", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).DymName(ctx, req.(*QueryDymNameRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_Alias_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryAliasRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).Alias(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/dymensionxyz.dymension.dymns.Query/Alias", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).Alias(ctx, req.(*QueryAliasRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_Aliases_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryAliasesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).Aliases(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/dymensionxyz.dymension.dymns.Query/Aliases", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).Aliases(ctx, req.(*QueryAliasesRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_ResolveDymNameAddresses_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ResolveDymNameAddressesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).ResolveDymNameAddresses(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/dymensionxyz.dymension.dymns.Query/ResolveDymNameAddresses", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).ResolveDymNameAddresses(ctx, req.(*ResolveDymNameAddressesRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_DymNamesOwnedByAccount_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryDymNamesOwnedByAccountRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).DymNamesOwnedByAccount(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/dymensionxyz.dymension.dymns.Query/DymNamesOwnedByAccount", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).DymNamesOwnedByAccount(ctx, req.(*QueryDymNamesOwnedByAccountRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_SellOrder_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QuerySellOrderRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).SellOrder(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/dymensionxyz.dymension.dymns.Query/SellOrder", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).SellOrder(ctx, req.(*QuerySellOrderRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_EstimateRegisterName_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(EstimateRegisterNameRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).EstimateRegisterName(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/dymensionxyz.dymension.dymns.Query/EstimateRegisterName", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).EstimateRegisterName(ctx, req.(*EstimateRegisterNameRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_EstimateRegisterAlias_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(EstimateRegisterAliasRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).EstimateRegisterAlias(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/dymensionxyz.dymension.dymns.Query/EstimateRegisterAlias", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).EstimateRegisterAlias(ctx, req.(*EstimateRegisterAliasRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_ReverseResolveAddress_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ReverseResolveAddressRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).ReverseResolveAddress(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/dymensionxyz.dymension.dymns.Query/ReverseResolveAddress", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).ReverseResolveAddress(ctx, req.(*ReverseResolveAddressRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_TranslateAliasOrChainIdToChainId_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryTranslateAliasOrChainIdToChainIdRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).TranslateAliasOrChainIdToChainId(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/dymensionxyz.dymension.dymns.Query/TranslateAliasOrChainIdToChainId", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).TranslateAliasOrChainIdToChainId(ctx, req.(*QueryTranslateAliasOrChainIdToChainIdRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_BuyOrderById_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryBuyOrderByIdRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).BuyOrderById(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/dymensionxyz.dymension.dymns.Query/BuyOrderById", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).BuyOrderById(ctx, req.(*QueryBuyOrderByIdRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_BuyOrdersPlacedByAccount_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryBuyOrdersPlacedByAccountRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).BuyOrdersPlacedByAccount(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/dymensionxyz.dymension.dymns.Query/BuyOrdersPlacedByAccount", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).BuyOrdersPlacedByAccount(ctx, req.(*QueryBuyOrdersPlacedByAccountRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_BuyOrdersByDymName_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryBuyOrdersByDymNameRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).BuyOrdersByDymName(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/dymensionxyz.dymension.dymns.Query/BuyOrdersByDymName", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).BuyOrdersByDymName(ctx, req.(*QueryBuyOrdersByDymNameRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_BuyOrdersOfDymNamesOwnedByAccount_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryBuyOrdersOfDymNamesOwnedByAccountRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).BuyOrdersOfDymNamesOwnedByAccount(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/dymensionxyz.dymension.dymns.Query/BuyOrdersOfDymNamesOwnedByAccount", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).BuyOrdersOfDymNamesOwnedByAccount(ctx, req.(*QueryBuyOrdersOfDymNamesOwnedByAccountRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_BuyOrdersByAlias_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryBuyOrdersByAliasRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).BuyOrdersByAlias(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/dymensionxyz.dymension.dymns.Query/BuyOrdersByAlias", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).BuyOrdersByAlias(ctx, req.(*QueryBuyOrdersByAliasRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_BuyOrdersOfAliasesLinkedToRollApp_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryBuyOrdersOfAliasesLinkedToRollAppRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).BuyOrdersOfAliasesLinkedToRollApp(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/dymensionxyz.dymension.dymns.Query/BuyOrdersOfAliasesLinkedToRollApp", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).BuyOrdersOfAliasesLinkedToRollApp(ctx, req.(*QueryBuyOrdersOfAliasesLinkedToRollAppRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _Query_serviceDesc = grpc.ServiceDesc{ + ServiceName: "dymensionxyz.dymension.dymns.Query", + HandlerType: (*QueryServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Params", + Handler: _Query_Params_Handler, + }, + { + MethodName: "DymName", + Handler: _Query_DymName_Handler, + }, + { + MethodName: "Alias", + Handler: _Query_Alias_Handler, + }, + { + MethodName: "Aliases", + Handler: _Query_Aliases_Handler, + }, + { + MethodName: "ResolveDymNameAddresses", + Handler: _Query_ResolveDymNameAddresses_Handler, + }, + { + MethodName: "DymNamesOwnedByAccount", + Handler: _Query_DymNamesOwnedByAccount_Handler, + }, + { + MethodName: "SellOrder", + Handler: _Query_SellOrder_Handler, + }, + { + MethodName: "EstimateRegisterName", + Handler: _Query_EstimateRegisterName_Handler, + }, + { + MethodName: "EstimateRegisterAlias", + Handler: _Query_EstimateRegisterAlias_Handler, + }, + { + MethodName: "ReverseResolveAddress", + Handler: _Query_ReverseResolveAddress_Handler, + }, + { + MethodName: "TranslateAliasOrChainIdToChainId", + Handler: _Query_TranslateAliasOrChainIdToChainId_Handler, + }, + { + MethodName: "BuyOrderById", + Handler: _Query_BuyOrderById_Handler, + }, + { + MethodName: "BuyOrdersPlacedByAccount", + Handler: _Query_BuyOrdersPlacedByAccount_Handler, + }, + { + MethodName: "BuyOrdersByDymName", + Handler: _Query_BuyOrdersByDymName_Handler, + }, + { + MethodName: "BuyOrdersOfDymNamesOwnedByAccount", + Handler: _Query_BuyOrdersOfDymNamesOwnedByAccount_Handler, + }, + { + MethodName: "BuyOrdersByAlias", + Handler: _Query_BuyOrdersByAlias_Handler, + }, + { + MethodName: "BuyOrdersOfAliasesLinkedToRollApp", + Handler: _Query_BuyOrdersOfAliasesLinkedToRollApp_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "dymensionxyz/dymension/dymns/query.proto", +} + +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 *QueryDymNameRequest) 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 *QueryDymNameRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryDymNameRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.DymName) > 0 { + i -= len(m.DymName) + copy(dAtA[i:], m.DymName) + i = encodeVarintQuery(dAtA, i, uint64(len(m.DymName))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryDymNameResponse) 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 *QueryDymNameResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryDymNameResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.DymName != nil { + { + size, err := m.DymName.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 *QueryAliasRequest) 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 *QueryAliasRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryAliasRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Alias) > 0 { + i -= len(m.Alias) + copy(dAtA[i:], m.Alias) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Alias))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryAliasResponse) 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 *QueryAliasResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryAliasResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.SameChainAliases) > 0 { + for iNdEx := len(m.SameChainAliases) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.SameChainAliases[iNdEx]) + copy(dAtA[i:], m.SameChainAliases[iNdEx]) + i = encodeVarintQuery(dAtA, i, uint64(len(m.SameChainAliases[iNdEx]))) + i-- + dAtA[i] = 0x22 + } + } + if len(m.BuyOrderIds) > 0 { + for iNdEx := len(m.BuyOrderIds) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.BuyOrderIds[iNdEx]) + copy(dAtA[i:], m.BuyOrderIds[iNdEx]) + i = encodeVarintQuery(dAtA, i, uint64(len(m.BuyOrderIds[iNdEx]))) + i-- + dAtA[i] = 0x1a + } + } + if m.FoundSellOrder { + i-- + if m.FoundSellOrder { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x10 + } + if len(m.ChainId) > 0 { + i -= len(m.ChainId) + copy(dAtA[i:], m.ChainId) + i = encodeVarintQuery(dAtA, i, uint64(len(m.ChainId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryAliasesRequest) 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 *QueryAliasesRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryAliasesRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ChainId) > 0 { + i -= len(m.ChainId) + copy(dAtA[i:], m.ChainId) + i = encodeVarintQuery(dAtA, i, uint64(len(m.ChainId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryAliasesResponse) 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 *QueryAliasesResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryAliasesResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.AliasesByChainId) > 0 { + for k := range m.AliasesByChainId { + v := m.AliasesByChainId[k] + baseI := i + { + size, err := (&v).MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + i -= len(k) + copy(dAtA[i:], k) + i = encodeVarintQuery(dAtA, i, uint64(len(k))) + i-- + dAtA[i] = 0xa + i = encodeVarintQuery(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *ResolveDymNameAddressesRequest) 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 *ResolveDymNameAddressesRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ResolveDymNameAddressesRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Addresses) > 0 { + for iNdEx := len(m.Addresses) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Addresses[iNdEx]) + copy(dAtA[i:], m.Addresses[iNdEx]) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Addresses[iNdEx]))) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *ResultDymNameAddress) 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 *ResultDymNameAddress) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ResultDymNameAddress) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Error) > 0 { + i -= len(m.Error) + copy(dAtA[i:], m.Error) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Error))) + i-- + dAtA[i] = 0x1a + } + if len(m.ResolvedAddress) > 0 { + i -= len(m.ResolvedAddress) + copy(dAtA[i:], m.ResolvedAddress) + i = encodeVarintQuery(dAtA, i, uint64(len(m.ResolvedAddress))) + i-- + dAtA[i] = 0x12 + } + 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 *ResolveDymNameAddressesResponse) 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 *ResolveDymNameAddressesResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ResolveDymNameAddressesResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ResolvedAddresses) > 0 { + for iNdEx := len(m.ResolvedAddresses) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.ResolvedAddresses[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 (m *QueryDymNamesOwnedByAccountRequest) 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 *QueryDymNamesOwnedByAccountRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryDymNamesOwnedByAccountRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Owner) > 0 { + i -= len(m.Owner) + copy(dAtA[i:], m.Owner) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Owner))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryDymNamesOwnedByAccountResponse) 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 *QueryDymNamesOwnedByAccountResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryDymNamesOwnedByAccountResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.DymNames) > 0 { + for iNdEx := len(m.DymNames) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.DymNames[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 (m *QuerySellOrderRequest) 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 *QuerySellOrderRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QuerySellOrderRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.AssetType) > 0 { + i -= len(m.AssetType) + copy(dAtA[i:], m.AssetType) + i = encodeVarintQuery(dAtA, i, uint64(len(m.AssetType))) + i-- + dAtA[i] = 0x12 + } + if len(m.AssetId) > 0 { + i -= len(m.AssetId) + copy(dAtA[i:], m.AssetId) + i = encodeVarintQuery(dAtA, i, uint64(len(m.AssetId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QuerySellOrderResponse) 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 *QuerySellOrderResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QuerySellOrderResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Result.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 *EstimateRegisterNameRequest) 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 *EstimateRegisterNameRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *EstimateRegisterNameRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Owner) > 0 { + i -= len(m.Owner) + copy(dAtA[i:], m.Owner) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Owner))) + i-- + dAtA[i] = 0x1a + } + if m.Duration != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.Duration)) + i-- + dAtA[i] = 0x10 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *EstimateRegisterNameResponse) 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 *EstimateRegisterNameResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *EstimateRegisterNameResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.TotalPrice.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + { + size, err := m.ExtendPrice.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + { + size, err := m.FirstYearPrice.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 *EstimateRegisterAliasRequest) 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 *EstimateRegisterAliasRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *EstimateRegisterAliasRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Owner) > 0 { + i -= len(m.Owner) + copy(dAtA[i:], m.Owner) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Owner))) + i-- + dAtA[i] = 0x1a + } + if len(m.RollappId) > 0 { + i -= len(m.RollappId) + copy(dAtA[i:], m.RollappId) + i = encodeVarintQuery(dAtA, i, uint64(len(m.RollappId))) + i-- + dAtA[i] = 0x12 + } + if len(m.Alias) > 0 { + i -= len(m.Alias) + copy(dAtA[i:], m.Alias) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Alias))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *EstimateRegisterAliasResponse) 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 *EstimateRegisterAliasResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *EstimateRegisterAliasResponse) 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 *ReverseResolveAddressRequest) 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 *ReverseResolveAddressRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ReverseResolveAddressRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.WorkingChainId) > 0 { + i -= len(m.WorkingChainId) + copy(dAtA[i:], m.WorkingChainId) + i = encodeVarintQuery(dAtA, i, uint64(len(m.WorkingChainId))) + i-- + dAtA[i] = 0x12 + } + if len(m.Addresses) > 0 { + for iNdEx := len(m.Addresses) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Addresses[iNdEx]) + copy(dAtA[i:], m.Addresses[iNdEx]) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Addresses[iNdEx]))) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *ReverseResolveAddressResponse) 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 *ReverseResolveAddressResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ReverseResolveAddressResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.WorkingChainId) > 0 { + i -= len(m.WorkingChainId) + copy(dAtA[i:], m.WorkingChainId) + i = encodeVarintQuery(dAtA, i, uint64(len(m.WorkingChainId))) + i-- + dAtA[i] = 0x12 + } + if len(m.Result) > 0 { + for k := range m.Result { + v := m.Result[k] + baseI := i + { + size, err := (&v).MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + i -= len(k) + copy(dAtA[i:], k) + i = encodeVarintQuery(dAtA, i, uint64(len(k))) + i-- + dAtA[i] = 0xa + i = encodeVarintQuery(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *ReverseResolveAddressResult) 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 *ReverseResolveAddressResult) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ReverseResolveAddressResult) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Error) > 0 { + i -= len(m.Error) + copy(dAtA[i:], m.Error) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Error))) + i-- + dAtA[i] = 0x12 + } + if len(m.Candidates) > 0 { + for iNdEx := len(m.Candidates) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Candidates[iNdEx]) + copy(dAtA[i:], m.Candidates[iNdEx]) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Candidates[iNdEx]))) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *QueryTranslateAliasOrChainIdToChainIdRequest) 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 *QueryTranslateAliasOrChainIdToChainIdRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryTranslateAliasOrChainIdToChainIdRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.AliasOrChainId) > 0 { + i -= len(m.AliasOrChainId) + copy(dAtA[i:], m.AliasOrChainId) + i = encodeVarintQuery(dAtA, i, uint64(len(m.AliasOrChainId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryTranslateAliasOrChainIdToChainIdResponse) 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 *QueryTranslateAliasOrChainIdToChainIdResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryTranslateAliasOrChainIdToChainIdResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ChainId) > 0 { + i -= len(m.ChainId) + copy(dAtA[i:], m.ChainId) + i = encodeVarintQuery(dAtA, i, uint64(len(m.ChainId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryBuyOrderByIdRequest) 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 *QueryBuyOrderByIdRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryBuyOrderByIdRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Id) > 0 { + i -= len(m.Id) + copy(dAtA[i:], m.Id) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Id))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryBuyOrderByIdResponse) 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 *QueryBuyOrderByIdResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryBuyOrderByIdResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.BuyOrder.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 *QueryBuyOrdersPlacedByAccountRequest) 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 *QueryBuyOrdersPlacedByAccountRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryBuyOrdersPlacedByAccountRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Account) > 0 { + i -= len(m.Account) + copy(dAtA[i:], m.Account) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Account))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryBuyOrdersPlacedByAccountResponse) 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 *QueryBuyOrdersPlacedByAccountResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryBuyOrdersPlacedByAccountResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.BuyOrders) > 0 { + for iNdEx := len(m.BuyOrders) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.BuyOrders[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 (m *QueryBuyOrdersByDymNameRequest) 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 *QueryBuyOrdersByDymNameRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryBuyOrdersByDymNameRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryBuyOrdersByDymNameResponse) 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 *QueryBuyOrdersByDymNameResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryBuyOrdersByDymNameResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.BuyOrders) > 0 { + for iNdEx := len(m.BuyOrders) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.BuyOrders[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 (m *QueryBuyOrdersOfDymNamesOwnedByAccountRequest) 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 *QueryBuyOrdersOfDymNamesOwnedByAccountRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryBuyOrdersOfDymNamesOwnedByAccountRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Account) > 0 { + i -= len(m.Account) + copy(dAtA[i:], m.Account) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Account))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryBuyOrdersOfDymNamesOwnedByAccountResponse) 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 *QueryBuyOrdersOfDymNamesOwnedByAccountResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryBuyOrdersOfDymNamesOwnedByAccountResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.BuyOrders) > 0 { + for iNdEx := len(m.BuyOrders) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.BuyOrders[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 (m *QueryBuyOrdersByAliasRequest) 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 *QueryBuyOrdersByAliasRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryBuyOrdersByAliasRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Alias) > 0 { + i -= len(m.Alias) + copy(dAtA[i:], m.Alias) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Alias))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryBuyOrdersByAliasResponse) 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 *QueryBuyOrdersByAliasResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryBuyOrdersByAliasResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.BuyOrders) > 0 { + for iNdEx := len(m.BuyOrders) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.BuyOrders[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 (m *QueryBuyOrdersOfAliasesLinkedToRollAppRequest) 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 *QueryBuyOrdersOfAliasesLinkedToRollAppRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryBuyOrdersOfAliasesLinkedToRollAppRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.RollappId) > 0 { + i -= len(m.RollappId) + copy(dAtA[i:], m.RollappId) + i = encodeVarintQuery(dAtA, i, uint64(len(m.RollappId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryBuyOrdersOfAliasesLinkedToRollAppResponse) 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 *QueryBuyOrdersOfAliasesLinkedToRollAppResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryBuyOrdersOfAliasesLinkedToRollAppResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.BuyOrders) > 0 { + for iNdEx := len(m.BuyOrders) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.BuyOrders[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 *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 *QueryDymNameRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.DymName) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryDymNameResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.DymName != nil { + l = m.DymName.Size() + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryAliasRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Alias) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryAliasResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ChainId) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + if m.FoundSellOrder { + n += 2 + } + if len(m.BuyOrderIds) > 0 { + for _, s := range m.BuyOrderIds { + l = len(s) + n += 1 + l + sovQuery(uint64(l)) + } + } + if len(m.SameChainAliases) > 0 { + for _, s := range m.SameChainAliases { + l = len(s) + n += 1 + l + sovQuery(uint64(l)) + } + } + return n +} + +func (m *QueryAliasesRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ChainId) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryAliasesResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.AliasesByChainId) > 0 { + for k, v := range m.AliasesByChainId { + _ = k + _ = v + l = v.Size() + mapEntrySize := 1 + len(k) + sovQuery(uint64(len(k))) + 1 + l + sovQuery(uint64(l)) + n += mapEntrySize + 1 + sovQuery(uint64(mapEntrySize)) + } + } + return n +} + +func (m *ResolveDymNameAddressesRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Addresses) > 0 { + for _, s := range m.Addresses { + l = len(s) + n += 1 + l + sovQuery(uint64(l)) + } + } + return n +} + +func (m *ResultDymNameAddress) 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)) + } + l = len(m.ResolvedAddress) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = len(m.Error) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *ResolveDymNameAddressesResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.ResolvedAddresses) > 0 { + for _, e := range m.ResolvedAddresses { + l = e.Size() + n += 1 + l + sovQuery(uint64(l)) + } + } + return n +} + +func (m *QueryDymNamesOwnedByAccountRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Owner) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryDymNamesOwnedByAccountResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.DymNames) > 0 { + for _, e := range m.DymNames { + l = e.Size() + n += 1 + l + sovQuery(uint64(l)) + } + } + return n +} + +func (m *QuerySellOrderRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.AssetId) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = len(m.AssetType) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QuerySellOrderResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Result.Size() + n += 1 + l + sovQuery(uint64(l)) + return n +} + +func (m *EstimateRegisterNameRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Name) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + if m.Duration != 0 { + n += 1 + sovQuery(uint64(m.Duration)) + } + l = len(m.Owner) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *EstimateRegisterNameResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.FirstYearPrice.Size() + n += 1 + l + sovQuery(uint64(l)) + l = m.ExtendPrice.Size() + n += 1 + l + sovQuery(uint64(l)) + l = m.TotalPrice.Size() + n += 1 + l + sovQuery(uint64(l)) + return n +} + +func (m *EstimateRegisterAliasRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Alias) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = len(m.RollappId) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = len(m.Owner) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *EstimateRegisterAliasResponse) 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 *ReverseResolveAddressRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Addresses) > 0 { + for _, s := range m.Addresses { + l = len(s) + n += 1 + l + sovQuery(uint64(l)) + } + } + l = len(m.WorkingChainId) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *ReverseResolveAddressResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Result) > 0 { + for k, v := range m.Result { + _ = k + _ = v + l = v.Size() + mapEntrySize := 1 + len(k) + sovQuery(uint64(len(k))) + 1 + l + sovQuery(uint64(l)) + n += mapEntrySize + 1 + sovQuery(uint64(mapEntrySize)) + } + } + l = len(m.WorkingChainId) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *ReverseResolveAddressResult) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Candidates) > 0 { + for _, s := range m.Candidates { + l = len(s) + n += 1 + l + sovQuery(uint64(l)) + } + } + l = len(m.Error) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryTranslateAliasOrChainIdToChainIdRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.AliasOrChainId) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryTranslateAliasOrChainIdToChainIdResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ChainId) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryBuyOrderByIdRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Id) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryBuyOrderByIdResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.BuyOrder.Size() + n += 1 + l + sovQuery(uint64(l)) + return n +} + +func (m *QueryBuyOrdersPlacedByAccountRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Account) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryBuyOrdersPlacedByAccountResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.BuyOrders) > 0 { + for _, e := range m.BuyOrders { + l = e.Size() + n += 1 + l + sovQuery(uint64(l)) + } + } + return n +} + +func (m *QueryBuyOrdersByDymNameRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Name) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryBuyOrdersByDymNameResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.BuyOrders) > 0 { + for _, e := range m.BuyOrders { + l = e.Size() + n += 1 + l + sovQuery(uint64(l)) + } + } + return n +} + +func (m *QueryBuyOrdersOfDymNamesOwnedByAccountRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Account) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryBuyOrdersOfDymNamesOwnedByAccountResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.BuyOrders) > 0 { + for _, e := range m.BuyOrders { + l = e.Size() + n += 1 + l + sovQuery(uint64(l)) + } + } + return n +} + +func (m *QueryBuyOrdersByAliasRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Alias) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryBuyOrdersByAliasResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.BuyOrders) > 0 { + for _, e := range m.BuyOrders { + l = e.Size() + n += 1 + l + sovQuery(uint64(l)) + } + } + return n +} + +func (m *QueryBuyOrdersOfAliasesLinkedToRollAppRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.RollappId) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryBuyOrdersOfAliasesLinkedToRollAppResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.BuyOrders) > 0 { + for _, e := range m.BuyOrders { + l = e.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 *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 *QueryDymNameRequest) 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: QueryDymNameRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryDymNameRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DymName", 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.DymName = 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 *QueryDymNameResponse) 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: QueryDymNameResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryDymNameResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DymName", 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.DymName == nil { + m.DymName = &DymName{} + } + if err := m.DymName.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 *QueryAliasRequest) 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: QueryAliasRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryAliasRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Alias", 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.Alias = 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 *QueryAliasResponse) 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: QueryAliasResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryAliasResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ChainId", 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.ChainId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field FoundSellOrder", 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.FoundSellOrder = bool(v != 0) + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BuyOrderIds", 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.BuyOrderIds = append(m.BuyOrderIds, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SameChainAliases", 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.SameChainAliases = append(m.SameChainAliases, 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 *QueryAliasesRequest) 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: QueryAliasesRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryAliasesRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ChainId", 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.ChainId = 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 *QueryAliasesResponse) 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: QueryAliasesResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryAliasesResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AliasesByChainId", 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.AliasesByChainId == nil { + m.AliasesByChainId = make(map[string]MultipleAliases) + } + var mapkey string + mapvalue := &MultipleAliases{} + for iNdEx < postIndex { + entryPreIndex := 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) + if fieldNum == 1 { + var stringLenmapkey uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapkey |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return ErrInvalidLengthQuery + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return ErrInvalidLengthQuery + } + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var mapmsglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + mapmsglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if mapmsglen < 0 { + return ErrInvalidLengthQuery + } + postmsgIndex := iNdEx + mapmsglen + if postmsgIndex < 0 { + return ErrInvalidLengthQuery + } + if postmsgIndex > l { + return io.ErrUnexpectedEOF + } + mapvalue = &MultipleAliases{} + if err := mapvalue.Unmarshal(dAtA[iNdEx:postmsgIndex]); err != nil { + return err + } + iNdEx = postmsgIndex + } else { + iNdEx = entryPreIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.AliasesByChainId[mapkey] = *mapvalue + 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 *ResolveDymNameAddressesRequest) 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: ResolveDymNameAddressesRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ResolveDymNameAddressesRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Addresses", 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.Addresses = append(m.Addresses, 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 *ResultDymNameAddress) 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: ResultDymNameAddress: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ResultDymNameAddress: 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 + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ResolvedAddress", 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.ResolvedAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Error", 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.Error = 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 *ResolveDymNameAddressesResponse) 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: ResolveDymNameAddressesResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ResolveDymNameAddressesResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ResolvedAddresses", 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.ResolvedAddresses = append(m.ResolvedAddresses, ResultDymNameAddress{}) + if err := m.ResolvedAddresses[len(m.ResolvedAddresses)-1].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 *QueryDymNamesOwnedByAccountRequest) 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: QueryDymNamesOwnedByAccountRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryDymNamesOwnedByAccountRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Owner", 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.Owner = 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 *QueryDymNamesOwnedByAccountResponse) 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: QueryDymNamesOwnedByAccountResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryDymNamesOwnedByAccountResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DymNames", 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.DymNames = append(m.DymNames, DymName{}) + if err := m.DymNames[len(m.DymNames)-1].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 *QuerySellOrderRequest) 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: QuerySellOrderRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QuerySellOrderRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AssetId", 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.AssetId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AssetType", 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.AssetType = 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 *QuerySellOrderResponse) 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: QuerySellOrderResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QuerySellOrderResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Result", 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.Result.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 *EstimateRegisterNameRequest) 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: EstimateRegisterNameRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: EstimateRegisterNameRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", 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.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Duration", wireType) + } + m.Duration = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Duration |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Owner", 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.Owner = 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 *EstimateRegisterNameResponse) 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: EstimateRegisterNameResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: EstimateRegisterNameResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FirstYearPrice", 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.FirstYearPrice.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ExtendPrice", 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.ExtendPrice.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TotalPrice", 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.TotalPrice.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 *EstimateRegisterAliasRequest) 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: EstimateRegisterAliasRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: EstimateRegisterAliasRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Alias", 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.Alias = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field RollappId", 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.RollappId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Owner", 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.Owner = 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 *EstimateRegisterAliasResponse) 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: EstimateRegisterAliasResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: EstimateRegisterAliasResponse: 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 *ReverseResolveAddressRequest) 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: ReverseResolveAddressRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ReverseResolveAddressRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Addresses", 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.Addresses = append(m.Addresses, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field WorkingChainId", 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.WorkingChainId = 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 *ReverseResolveAddressResponse) 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: ReverseResolveAddressResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ReverseResolveAddressResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Result", 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.Result == nil { + m.Result = make(map[string]ReverseResolveAddressResult) + } + var mapkey string + mapvalue := &ReverseResolveAddressResult{} + for iNdEx < postIndex { + entryPreIndex := 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) + if fieldNum == 1 { + var stringLenmapkey uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapkey |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return ErrInvalidLengthQuery + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return ErrInvalidLengthQuery + } + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var mapmsglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + mapmsglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if mapmsglen < 0 { + return ErrInvalidLengthQuery + } + postmsgIndex := iNdEx + mapmsglen + if postmsgIndex < 0 { + return ErrInvalidLengthQuery + } + if postmsgIndex > l { + return io.ErrUnexpectedEOF + } + mapvalue = &ReverseResolveAddressResult{} + if err := mapvalue.Unmarshal(dAtA[iNdEx:postmsgIndex]); err != nil { + return err + } + iNdEx = postmsgIndex + } else { + iNdEx = entryPreIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.Result[mapkey] = *mapvalue + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field WorkingChainId", 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.WorkingChainId = 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 *ReverseResolveAddressResult) 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: ReverseResolveAddressResult: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ReverseResolveAddressResult: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Candidates", 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.Candidates = append(m.Candidates, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Error", 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.Error = 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 *QueryTranslateAliasOrChainIdToChainIdRequest) 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: QueryTranslateAliasOrChainIdToChainIdRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryTranslateAliasOrChainIdToChainIdRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AliasOrChainId", 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.AliasOrChainId = 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 *QueryTranslateAliasOrChainIdToChainIdResponse) 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: QueryTranslateAliasOrChainIdToChainIdResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryTranslateAliasOrChainIdToChainIdResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ChainId", 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.ChainId = 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 *QueryBuyOrderByIdRequest) 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: QueryBuyOrderByIdRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryBuyOrderByIdRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Id", 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.Id = 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 *QueryBuyOrderByIdResponse) 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: QueryBuyOrderByIdResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryBuyOrderByIdResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BuyOrder", 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.BuyOrder.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 *QueryBuyOrdersPlacedByAccountRequest) 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: QueryBuyOrdersPlacedByAccountRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryBuyOrdersPlacedByAccountRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Account", 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.Account = 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 *QueryBuyOrdersPlacedByAccountResponse) 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: QueryBuyOrdersPlacedByAccountResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryBuyOrdersPlacedByAccountResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BuyOrders", 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.BuyOrders = append(m.BuyOrders, BuyOrder{}) + if err := m.BuyOrders[len(m.BuyOrders)-1].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 *QueryBuyOrdersByDymNameRequest) 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: QueryBuyOrdersByDymNameRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryBuyOrdersByDymNameRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", 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.Name = 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 *QueryBuyOrdersByDymNameResponse) 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: QueryBuyOrdersByDymNameResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryBuyOrdersByDymNameResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BuyOrders", 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.BuyOrders = append(m.BuyOrders, BuyOrder{}) + if err := m.BuyOrders[len(m.BuyOrders)-1].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 *QueryBuyOrdersOfDymNamesOwnedByAccountRequest) 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: QueryBuyOrdersOfDymNamesOwnedByAccountRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryBuyOrdersOfDymNamesOwnedByAccountRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Account", 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.Account = 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 *QueryBuyOrdersOfDymNamesOwnedByAccountResponse) 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: QueryBuyOrdersOfDymNamesOwnedByAccountResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryBuyOrdersOfDymNamesOwnedByAccountResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BuyOrders", 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.BuyOrders = append(m.BuyOrders, BuyOrder{}) + if err := m.BuyOrders[len(m.BuyOrders)-1].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 *QueryBuyOrdersByAliasRequest) 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: QueryBuyOrdersByAliasRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryBuyOrdersByAliasRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Alias", 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.Alias = 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 *QueryBuyOrdersByAliasResponse) 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: QueryBuyOrdersByAliasResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryBuyOrdersByAliasResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BuyOrders", 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.BuyOrders = append(m.BuyOrders, BuyOrder{}) + if err := m.BuyOrders[len(m.BuyOrders)-1].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 *QueryBuyOrdersOfAliasesLinkedToRollAppRequest) 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: QueryBuyOrdersOfAliasesLinkedToRollAppRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryBuyOrdersOfAliasesLinkedToRollAppRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field RollappId", 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.RollappId = 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 *QueryBuyOrdersOfAliasesLinkedToRollAppResponse) 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: QueryBuyOrdersOfAliasesLinkedToRollAppResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryBuyOrdersOfAliasesLinkedToRollAppResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BuyOrders", 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.BuyOrders = append(m.BuyOrders, BuyOrder{}) + if err := m.BuyOrders[len(m.BuyOrders)-1].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/dymns/types/query.pb.gw.go b/x/dymns/types/query.pb.gw.go new file mode 100644 index 000000000..7aa246390 --- /dev/null +++ b/x/dymns/types/query.pb.gw.go @@ -0,0 +1,1791 @@ +// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. +// source: dymensionxyz/dymension/dymns/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 + +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 + +} + +func request_Query_DymName_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryDymNameRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["dym_name"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "dym_name") + } + + protoReq.DymName, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "dym_name", err) + } + + msg, err := client.DymName(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_DymName_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryDymNameRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["dym_name"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "dym_name") + } + + protoReq.DymName, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "dym_name", err) + } + + msg, err := server.DymName(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_Alias_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryAliasRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["alias"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "alias") + } + + protoReq.Alias, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "alias", err) + } + + msg, err := client.Alias(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_Alias_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryAliasRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["alias"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "alias") + } + + protoReq.Alias, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "alias", err) + } + + msg, err := server.Alias(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_Query_Aliases_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + +func request_Query_Aliases_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryAliasesRequest + 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_Aliases_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.Aliases(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_Aliases_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryAliasesRequest + 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_Aliases_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.Aliases(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_Query_ResolveDymNameAddresses_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + +func request_Query_ResolveDymNameAddresses_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq ResolveDymNameAddressesRequest + 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_ResolveDymNameAddresses_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.ResolveDymNameAddresses(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_ResolveDymNameAddresses_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq ResolveDymNameAddressesRequest + 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_ResolveDymNameAddresses_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.ResolveDymNameAddresses(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_DymNamesOwnedByAccount_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryDymNamesOwnedByAccountRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["owner"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "owner") + } + + protoReq.Owner, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "owner", err) + } + + msg, err := client.DymNamesOwnedByAccount(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_DymNamesOwnedByAccount_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryDymNamesOwnedByAccountRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["owner"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "owner") + } + + protoReq.Owner, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "owner", err) + } + + msg, err := server.DymNamesOwnedByAccount(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_Query_SellOrder_0 = &utilities.DoubleArray{Encoding: map[string]int{"asset_id": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}} +) + +func request_Query_SellOrder_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QuerySellOrderRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["asset_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "asset_id") + } + + protoReq.AssetId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "asset_id", err) + } + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_SellOrder_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.SellOrder(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_SellOrder_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QuerySellOrderRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["asset_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "asset_id") + } + + protoReq.AssetId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "asset_id", err) + } + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_SellOrder_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.SellOrder(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_Query_EstimateRegisterName_0 = &utilities.DoubleArray{Encoding: map[string]int{"name": 0, "duration": 1}, Base: []int{1, 1, 2, 0, 0}, Check: []int{0, 1, 1, 2, 3}} +) + +func request_Query_EstimateRegisterName_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq EstimateRegisterNameRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["name"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "name") + } + + protoReq.Name, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "name", err) + } + + val, ok = pathParams["duration"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "duration") + } + + protoReq.Duration, err = runtime.Int64(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "duration", err) + } + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_EstimateRegisterName_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.EstimateRegisterName(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_EstimateRegisterName_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq EstimateRegisterNameRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["name"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "name") + } + + protoReq.Name, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "name", err) + } + + val, ok = pathParams["duration"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "duration") + } + + protoReq.Duration, err = runtime.Int64(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "duration", err) + } + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_EstimateRegisterName_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.EstimateRegisterName(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_Query_EstimateRegisterAlias_0 = &utilities.DoubleArray{Encoding: map[string]int{"alias": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}} +) + +func request_Query_EstimateRegisterAlias_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq EstimateRegisterAliasRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["alias"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "alias") + } + + protoReq.Alias, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "alias", err) + } + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_EstimateRegisterAlias_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.EstimateRegisterAlias(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_EstimateRegisterAlias_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq EstimateRegisterAliasRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["alias"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "alias") + } + + protoReq.Alias, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "alias", err) + } + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_EstimateRegisterAlias_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.EstimateRegisterAlias(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_Query_ReverseResolveAddress_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + +func request_Query_ReverseResolveAddress_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq ReverseResolveAddressRequest + 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_ReverseResolveAddress_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.ReverseResolveAddress(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_ReverseResolveAddress_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq ReverseResolveAddressRequest + 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_ReverseResolveAddress_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.ReverseResolveAddress(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_TranslateAliasOrChainIdToChainId_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryTranslateAliasOrChainIdToChainIdRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["alias_or_chain_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "alias_or_chain_id") + } + + protoReq.AliasOrChainId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "alias_or_chain_id", err) + } + + msg, err := client.TranslateAliasOrChainIdToChainId(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_TranslateAliasOrChainIdToChainId_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryTranslateAliasOrChainIdToChainIdRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["alias_or_chain_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "alias_or_chain_id") + } + + protoReq.AliasOrChainId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "alias_or_chain_id", err) + } + + msg, err := server.TranslateAliasOrChainIdToChainId(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_BuyOrderById_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryBuyOrderByIdRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id") + } + + protoReq.Id, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err) + } + + msg, err := client.BuyOrderById(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_BuyOrderById_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryBuyOrderByIdRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id") + } + + protoReq.Id, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err) + } + + msg, err := server.BuyOrderById(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_BuyOrdersPlacedByAccount_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryBuyOrdersPlacedByAccountRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["account"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "account") + } + + protoReq.Account, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "account", err) + } + + msg, err := client.BuyOrdersPlacedByAccount(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_BuyOrdersPlacedByAccount_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryBuyOrdersPlacedByAccountRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["account"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "account") + } + + protoReq.Account, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "account", err) + } + + msg, err := server.BuyOrdersPlacedByAccount(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_BuyOrdersByDymName_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryBuyOrdersByDymNameRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["name"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "name") + } + + protoReq.Name, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "name", err) + } + + msg, err := client.BuyOrdersByDymName(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_BuyOrdersByDymName_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryBuyOrdersByDymNameRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["name"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "name") + } + + protoReq.Name, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "name", err) + } + + msg, err := server.BuyOrdersByDymName(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_BuyOrdersOfDymNamesOwnedByAccount_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryBuyOrdersOfDymNamesOwnedByAccountRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["account"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "account") + } + + protoReq.Account, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "account", err) + } + + msg, err := client.BuyOrdersOfDymNamesOwnedByAccount(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_BuyOrdersOfDymNamesOwnedByAccount_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryBuyOrdersOfDymNamesOwnedByAccountRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["account"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "account") + } + + protoReq.Account, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "account", err) + } + + msg, err := server.BuyOrdersOfDymNamesOwnedByAccount(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_BuyOrdersByAlias_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryBuyOrdersByAliasRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["alias"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "alias") + } + + protoReq.Alias, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "alias", err) + } + + msg, err := client.BuyOrdersByAlias(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_BuyOrdersByAlias_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryBuyOrdersByAliasRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["alias"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "alias") + } + + protoReq.Alias, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "alias", err) + } + + msg, err := server.BuyOrdersByAlias(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_BuyOrdersOfAliasesLinkedToRollApp_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryBuyOrdersOfAliasesLinkedToRollAppRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["rollapp_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "rollapp_id") + } + + protoReq.RollappId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "rollapp_id", err) + } + + msg, err := client.BuyOrdersOfAliasesLinkedToRollApp(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_BuyOrdersOfAliasesLinkedToRollApp_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryBuyOrdersOfAliasesLinkedToRollAppRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["rollapp_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "rollapp_id") + } + + protoReq.RollappId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "rollapp_id", err) + } + + msg, err := server.BuyOrdersOfAliasesLinkedToRollApp(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_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_DymName_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_DymName_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_DymName_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_Alias_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_Alias_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_Alias_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_Aliases_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_Aliases_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_Aliases_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_ResolveDymNameAddresses_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_ResolveDymNameAddresses_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_ResolveDymNameAddresses_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_DymNamesOwnedByAccount_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_DymNamesOwnedByAccount_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_DymNamesOwnedByAccount_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_SellOrder_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_SellOrder_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_SellOrder_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_EstimateRegisterName_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_EstimateRegisterName_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_EstimateRegisterName_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_EstimateRegisterAlias_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_EstimateRegisterAlias_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_EstimateRegisterAlias_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_ReverseResolveAddress_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_ReverseResolveAddress_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_ReverseResolveAddress_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_TranslateAliasOrChainIdToChainId_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_TranslateAliasOrChainIdToChainId_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_TranslateAliasOrChainIdToChainId_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_BuyOrderById_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_BuyOrderById_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_BuyOrderById_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_BuyOrdersPlacedByAccount_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_BuyOrdersPlacedByAccount_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_BuyOrdersPlacedByAccount_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_BuyOrdersByDymName_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_BuyOrdersByDymName_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_BuyOrdersByDymName_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_BuyOrdersOfDymNamesOwnedByAccount_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_BuyOrdersOfDymNamesOwnedByAccount_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_BuyOrdersOfDymNamesOwnedByAccount_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_BuyOrdersByAlias_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_BuyOrdersByAlias_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_BuyOrdersByAlias_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_BuyOrdersOfAliasesLinkedToRollApp_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_BuyOrdersOfAliasesLinkedToRollApp_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_BuyOrdersOfAliasesLinkedToRollApp_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_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_DymName_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_DymName_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_DymName_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_Alias_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_Alias_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_Alias_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_Aliases_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_Aliases_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_Aliases_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_ResolveDymNameAddresses_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_ResolveDymNameAddresses_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_ResolveDymNameAddresses_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_DymNamesOwnedByAccount_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_DymNamesOwnedByAccount_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_DymNamesOwnedByAccount_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_SellOrder_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_SellOrder_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_SellOrder_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_EstimateRegisterName_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_EstimateRegisterName_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_EstimateRegisterName_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_EstimateRegisterAlias_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_EstimateRegisterAlias_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_EstimateRegisterAlias_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_ReverseResolveAddress_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_ReverseResolveAddress_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_ReverseResolveAddress_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_TranslateAliasOrChainIdToChainId_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_TranslateAliasOrChainIdToChainId_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_TranslateAliasOrChainIdToChainId_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_BuyOrderById_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_BuyOrderById_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_BuyOrderById_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_BuyOrdersPlacedByAccount_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_BuyOrdersPlacedByAccount_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_BuyOrdersPlacedByAccount_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_BuyOrdersByDymName_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_BuyOrdersByDymName_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_BuyOrdersByDymName_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_BuyOrdersOfDymNamesOwnedByAccount_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_BuyOrdersOfDymNamesOwnedByAccount_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_BuyOrdersOfDymNamesOwnedByAccount_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_BuyOrdersByAlias_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_BuyOrdersByAlias_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_BuyOrdersByAlias_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_BuyOrdersOfAliasesLinkedToRollApp_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_BuyOrdersOfAliasesLinkedToRollApp_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_BuyOrdersOfAliasesLinkedToRollApp_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +var ( + pattern_Query_Params_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"dymensionxyz", "dymension", "dymns", "params"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_DymName_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 3}, []string{"dymensionxyz", "dymension", "dymns", "dym_name"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_Alias_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 3}, []string{"dymensionxyz", "dymension", "dymns", "alias"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_Aliases_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"dymensionxyz", "dymension", "dymns", "aliases"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_ResolveDymNameAddresses_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"dymensionxyz", "dymension", "dymns", "resolve"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_DymNamesOwnedByAccount_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"dymensionxyz", "dymension", "dymns", "owned_by", "owner"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_SellOrder_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"dymensionxyz", "dymension", "dymns", "sell_order", "asset_id"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_EstimateRegisterName_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 1, 0, 4, 1, 5, 5}, []string{"dymensionxyz", "dymension", "dymns", "estimate_register_name", "name", "duration"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_EstimateRegisterAlias_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"dymensionxyz", "dymension", "dymns", "estimate_register_alias", "alias"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_ReverseResolveAddress_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"dymensionxyz", "dymension", "dymns", "reverse_resolve"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_TranslateAliasOrChainIdToChainId_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"dymensionxyz", "dymension", "dymns", "translate_alias", "alias_or_chain_id"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_BuyOrderById_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"dymensionxyz", "dymension", "dymns", "buy_order", "id"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_BuyOrdersPlacedByAccount_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"dymensionxyz", "dymension", "dymns", "buy_orders_placed_by_account", "account"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_BuyOrdersByDymName_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"dymensionxyz", "dymension", "dymns", "buy_orders_by_dym_name", "name"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_BuyOrdersOfDymNamesOwnedByAccount_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"dymensionxyz", "dymension", "dymns", "buy_orders_of_dym_names_owned_by_account", "account"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_BuyOrdersByAlias_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"dymensionxyz", "dymension", "dymns", "buy_orders_by_alias", "alias"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_BuyOrdersOfAliasesLinkedToRollApp_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"dymensionxyz", "dymension", "dymns", "buy_orders_of_aliases_linked_to_rollapp", "rollapp_id"}, "", runtime.AssumeColonVerbOpt(false))) +) + +var ( + forward_Query_Params_0 = runtime.ForwardResponseMessage + + forward_Query_DymName_0 = runtime.ForwardResponseMessage + + forward_Query_Alias_0 = runtime.ForwardResponseMessage + + forward_Query_Aliases_0 = runtime.ForwardResponseMessage + + forward_Query_ResolveDymNameAddresses_0 = runtime.ForwardResponseMessage + + forward_Query_DymNamesOwnedByAccount_0 = runtime.ForwardResponseMessage + + forward_Query_SellOrder_0 = runtime.ForwardResponseMessage + + forward_Query_EstimateRegisterName_0 = runtime.ForwardResponseMessage + + forward_Query_EstimateRegisterAlias_0 = runtime.ForwardResponseMessage + + forward_Query_ReverseResolveAddress_0 = runtime.ForwardResponseMessage + + forward_Query_TranslateAliasOrChainIdToChainId_0 = runtime.ForwardResponseMessage + + forward_Query_BuyOrderById_0 = runtime.ForwardResponseMessage + + forward_Query_BuyOrdersPlacedByAccount_0 = runtime.ForwardResponseMessage + + forward_Query_BuyOrdersByDymName_0 = runtime.ForwardResponseMessage + + forward_Query_BuyOrdersOfDymNamesOwnedByAccount_0 = runtime.ForwardResponseMessage + + forward_Query_BuyOrdersByAlias_0 = runtime.ForwardResponseMessage + + forward_Query_BuyOrdersOfAliasesLinkedToRollApp_0 = runtime.ForwardResponseMessage +) diff --git a/x/dymns/types/reverse_lookup_offer_ids.go b/x/dymns/types/reverse_lookup_offer_ids.go new file mode 100644 index 000000000..0eebe68bc --- /dev/null +++ b/x/dymns/types/reverse_lookup_offer_ids.go @@ -0,0 +1,33 @@ +package types + +import "sort" + +// Distinct returns a new list of offer-ids with duplicates removed. +// Result will be sorted. +func (m ReverseLookupBuyOrderIds) Distinct() (distinct ReverseLookupBuyOrderIds) { + return ReverseLookupBuyOrderIds{ + OrderIds: StringList(m.OrderIds).Distinct(), + } +} + +// Combine merges the offer-ids from the current list and the other list. +// Result will be sorted distinct. +func (m ReverseLookupBuyOrderIds) Combine(other ReverseLookupBuyOrderIds) ReverseLookupBuyOrderIds { + return ReverseLookupBuyOrderIds{ + OrderIds: StringList(m.OrderIds).Combine(other.OrderIds), + }.Distinct() +} + +// Exclude removes the offer-ids from the current list that are in the toBeExcluded list. +// Result will be sorted distinct. +func (m ReverseLookupBuyOrderIds) Exclude(toBeExcluded ReverseLookupBuyOrderIds) (afterExcluded ReverseLookupBuyOrderIds) { + return ReverseLookupBuyOrderIds{ + OrderIds: StringList(m.OrderIds).Exclude(toBeExcluded.OrderIds), + }.Distinct() +} + +// Sort sorts the offer-ids in the list. +func (m ReverseLookupBuyOrderIds) Sort() ReverseLookupBuyOrderIds { + sort.Strings(m.OrderIds) + return m +} diff --git a/x/dymns/types/reverse_resolved_dym_name_address.go b/x/dymns/types/reverse_resolved_dym_name_address.go new file mode 100644 index 000000000..1af0522d9 --- /dev/null +++ b/x/dymns/types/reverse_resolved_dym_name_address.go @@ -0,0 +1,99 @@ +package types + +import ( + "sort" + "strings" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// ReverseResolvedDymNameAddress is a struct that contains the reverse-resolved Dym-Name-Address components. +type ReverseResolvedDymNameAddress struct { + SubName string + Name string + ChainIdOrAlias string +} + +// ReverseResolvedDymNameAddresses is a list of ReverseResolvedDymNameAddress. +// Used to add some operations on the list. +type ReverseResolvedDymNameAddresses []ReverseResolvedDymNameAddress + +// String returns the string representation of the ReverseResolvedDymNameAddress. +// It returns the string in the format of "subname.name@chainIdOrAlias". +func (m ReverseResolvedDymNameAddress) String() string { + var sb strings.Builder + if m.SubName != "" { + sb.WriteString(m.SubName) + sb.WriteString(".") + } + sb.WriteString(m.Name) + sb.WriteString("@") + sb.WriteString(m.ChainIdOrAlias) + return sb.String() +} + +// Sort sorts the ReverseResolvedDymNameAddress in the list. +func (m ReverseResolvedDymNameAddresses) Sort() { + if len(m) > 0 { + sort.Slice(m, func(i, j int) bool { + addr1 := m[i].String() + addr2 := m[j].String() + + if len(addr1) < len(addr2) { + return true + } + + if len(addr1) > len(addr2) { + return false + } + + return strings.Compare(addr1, addr2) < 0 + }) + } +} + +// Distinct returns a new list of ReverseResolvedDymNameAddress with duplicates removed. +func (m ReverseResolvedDymNameAddresses) Distinct() (distinct ReverseResolvedDymNameAddresses) { + if len(m) < 1 { + return m + } + + unique := make(map[string]ReverseResolvedDymNameAddress) + // Describe usage of Go Map: used to store unique addresses, later result will be sorted + defer func() { + distinct.Sort() + }() + + for _, addr := range m { + unique[addr.String()] = addr + } + + for _, addr := range unique { + distinct = append(distinct, addr) + } + + return +} + +func (m ReverseResolvedDymNameAddresses) AppendConfigs(ctx sdk.Context, dymName DymName, configs []DymNameConfig, filter func(ReverseResolvedDymNameAddress) bool) ReverseResolvedDymNameAddresses { + result := m[:] + + for _, config := range configs { + record := ReverseResolvedDymNameAddress{ + SubName: config.Path, + Name: dymName.Name, + ChainIdOrAlias: config.ChainId, + } + if config.ChainId == "" { + record.ChainIdOrAlias = ctx.ChainID() + } + + if filter != nil && !filter(record) { + continue + } + + result = append(result, record) + } + + return result +} diff --git a/x/dymns/types/reverse_resolved_dym_name_address_test.go b/x/dymns/types/reverse_resolved_dym_name_address_test.go new file mode 100644 index 000000000..ca8c5080d --- /dev/null +++ b/x/dymns/types/reverse_resolved_dym_name_address_test.go @@ -0,0 +1,468 @@ +package types + +import ( + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" +) + +func TestReverseResolvedDymNameAddress_StringFormat(t *testing.T) { + tests := []struct { + name string + subName string + dymName string + chainIdOrAlias string + want string + }{ + { + name: "normal case", + subName: "", + dymName: "a", + chainIdOrAlias: "b", + want: "a@b", + }, + { + name: "normal case with sub-name", + subName: "c", + dymName: "a", + chainIdOrAlias: "b", + want: "c.a@b", + }, + { + name: "normal case with multi-sub-name", + subName: "c.d", + dymName: "a", + chainIdOrAlias: "b", + want: "c.d.a@b", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + m := ReverseResolvedDymNameAddress{ + SubName: tt.subName, + Name: tt.dymName, + ChainIdOrAlias: tt.chainIdOrAlias, + } + require.Equal(t, tt.want, m.String()) + }) + } +} + +func TestReverseResolvedDymNameAddresses_Sort(t *testing.T) { + t.Run("allow passing empty", func(t *testing.T) { + var m ReverseResolvedDymNameAddresses + m.Sort() + require.Empty(t, m) + }) + + input := ReverseResolvedDymNameAddresses{ + { + SubName: "geek", + Name: "aa", + }, + { + SubName: "a", + Name: "b", + }, + { + SubName: "a", + Name: "a", + }, + { + SubName: "a", + Name: "z", + }, + { + SubName: "a", + Name: "zz", + }, + } + + input.Sort() + + output := input + + require.Equal(t, ReverseResolvedDymNameAddresses{ + { + SubName: "a", + Name: "a", + }, + { + SubName: "a", + Name: "b", + }, + { + SubName: "a", + Name: "z", + }, + { + SubName: "a", + Name: "zz", + }, + { + SubName: "geek", + Name: "aa", + }, + }, output, "first by length, then by nature comparison") +} + +func TestReverseResolvedDymNameAddresses_Distinct(t *testing.T) { + tests := []struct { + name string + m ReverseResolvedDymNameAddresses + want ReverseResolvedDymNameAddresses + }{ + { + name: "distinct", + m: []ReverseResolvedDymNameAddress{{Name: "a"}, {Name: "b"}, {Name: "a"}}, + want: []ReverseResolvedDymNameAddress{{Name: "a"}, {Name: "b"}}, + }, + { + name: "distinct", + m: []ReverseResolvedDymNameAddress{{Name: "a"}, {Name: "a"}}, + want: []ReverseResolvedDymNameAddress{{Name: "a"}}, + }, + { + name: "already distinct", + m: []ReverseResolvedDymNameAddress{{Name: "a"}}, + want: []ReverseResolvedDymNameAddress{{Name: "a"}}, + }, + { + name: "input is empty", + m: []ReverseResolvedDymNameAddress{}, + want: []ReverseResolvedDymNameAddress{}, + }, + { + name: "input is nil", + m: nil, + want: nil, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := tt.m.Distinct() + tt.want.Sort() + got.Sort() + require.Equal(t, tt.want, got) + }) + } + + t.Run("sorted after distinct", func(t *testing.T) { + original := ReverseResolvedDymNameAddresses{ + { + SubName: "geek", + Name: "aa", + }, + { + SubName: "a", + Name: "b", + }, + { + SubName: "a", + Name: "a", + }, + { + SubName: "a", + Name: "z", + }, + { + SubName: "a", + Name: "zz", + }, + } + + duplicated := append(original, original...) + duplicated = append(duplicated, original...) + + afterDistinct := duplicated.Distinct() + + require.Equal(t, ReverseResolvedDymNameAddresses{ + { + SubName: "a", + Name: "a", + }, + { + SubName: "a", + Name: "b", + }, + { + SubName: "a", + Name: "z", + }, + { + SubName: "a", + Name: "zz", + }, + { + SubName: "geek", + Name: "aa", + }, + }, afterDistinct, "must be sorted after distinct") + }) +} + +func TestReverseResolvedDymNameAddresses_AppendConfigs(t *testing.T) { + tests := []struct { + name string + ctx sdk.Context + input ReverseResolvedDymNameAddresses + dymName DymName + configs []DymNameConfig + filter func(ReverseResolvedDymNameAddress) bool + want ReverseResolvedDymNameAddresses + }{ + { + name: "can append without filter", + ctx: sdk.Context{}.WithChainID("dymension_1100-1"), + input: ReverseResolvedDymNameAddresses{ + { + SubName: "a", + Name: "b", + ChainIdOrAlias: "c", + }, + }, + dymName: DymName{ + Name: "my-name", + }, + configs: []DymNameConfig{ + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "c1", + Path: "sub1", + Value: "v1", + }, + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "c2", + Path: "sub2", + Value: "v2", + }, + }, + filter: func(address ReverseResolvedDymNameAddress) bool { + return true + }, + want: ReverseResolvedDymNameAddresses{ + { + SubName: "a", + Name: "b", + ChainIdOrAlias: "c", + }, + { + SubName: "sub1", + Name: "my-name", + ChainIdOrAlias: "c1", + }, + { + SubName: "sub2", + Name: "my-name", + ChainIdOrAlias: "c2", + }, + }, + }, + { + name: "accept nil input list", + ctx: sdk.Context{}.WithChainID("dymension_1100-1"), + input: nil, + dymName: DymName{ + Name: "my-name", + }, + configs: []DymNameConfig{ + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "c1", + Path: "sub1", + Value: "v1", + }, + }, + filter: func(address ReverseResolvedDymNameAddress) bool { + return true + }, + want: ReverseResolvedDymNameAddresses{ + { + SubName: "sub1", + Name: "my-name", + ChainIdOrAlias: "c1", + }, + }, + }, + { + name: "accept nil input list and nil config list", + ctx: sdk.Context{}.WithChainID("dymension_1100-1"), + input: nil, + dymName: DymName{ + Name: "my-name", + }, + configs: nil, + filter: func(address ReverseResolvedDymNameAddress) bool { + return true + }, + want: nil, + }, + { + name: "when filter is nil, take all", + ctx: sdk.Context{}, + input: ReverseResolvedDymNameAddresses{ + { + SubName: "a", + Name: "b", + ChainIdOrAlias: "c", + }, + }, + dymName: DymName{ + Name: "my-name", + }, + configs: []DymNameConfig{ + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "c1", + Path: "sub1", + Value: "v1", + }, + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "c2", + Path: "sub2", + Value: "v2", + }, + }, + filter: nil, + want: ReverseResolvedDymNameAddresses{ + { + SubName: "a", + Name: "b", + ChainIdOrAlias: "c", + }, + { + SubName: "sub1", + Name: "my-name", + ChainIdOrAlias: "c1", + }, + { + SubName: "sub2", + Name: "my-name", + ChainIdOrAlias: "c2", + }, + }, + }, + { + name: "use chain-id from context for configs that has empty chain-id", + ctx: sdk.Context{}.WithChainID("dymension_1100-1"), + input: ReverseResolvedDymNameAddresses{ + { + SubName: "a", + Name: "b", + ChainIdOrAlias: "c", + }, + }, + dymName: DymName{ + Name: "my-name", + }, + configs: []DymNameConfig{ + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "sub1", + Value: "v1", + }, + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "c2", + Path: "sub2", + Value: "v2", + }, + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "sub3", + Value: "v3", + }, + }, + filter: func(address ReverseResolvedDymNameAddress) bool { + return true + }, + want: ReverseResolvedDymNameAddresses{ + { + SubName: "a", + Name: "b", + ChainIdOrAlias: "c", + }, + { + SubName: "sub1", + Name: "my-name", + ChainIdOrAlias: "dymension_1100-1", + }, + { + SubName: "sub2", + Name: "my-name", + ChainIdOrAlias: "c2", + }, + { + SubName: "sub3", + Name: "my-name", + ChainIdOrAlias: "dymension_1100-1", + }, + }, + }, + { + name: "filter should work", + ctx: sdk.Context{}.WithChainID("dymension_1100-1"), + input: ReverseResolvedDymNameAddresses{ + { + SubName: "a", + Name: "b", + ChainIdOrAlias: "dymension_1100-1", + }, + }, + dymName: DymName{ + Name: "my-name", + }, + configs: []DymNameConfig{ + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "sub1", + Value: "v1", + }, + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "c2", + Path: "sub2", + Value: "v2", + }, + { + Type: DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "sub3", + Value: "v3", + }, + }, + filter: func(address ReverseResolvedDymNameAddress) bool { + return address.ChainIdOrAlias == "dymension_1100-1" + }, + want: ReverseResolvedDymNameAddresses{ + { + SubName: "a", + Name: "b", + ChainIdOrAlias: "dymension_1100-1", + }, + { + SubName: "sub1", + Name: "my-name", + ChainIdOrAlias: "dymension_1100-1", + }, + { + SubName: "sub3", + Name: "my-name", + ChainIdOrAlias: "dymension_1100-1", + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := tt.input.AppendConfigs(tt.ctx, tt.dymName, tt.configs, tt.filter) + require.Equal(t, tt.want, got) + }) + } +} diff --git a/x/dymns/types/sell_order.go b/x/dymns/types/sell_order.go new file mode 100644 index 000000000..c5e822159 --- /dev/null +++ b/x/dymns/types/sell_order.go @@ -0,0 +1,256 @@ +package types + +import ( + "fmt" + "slices" + "sort" + + errorsmod "cosmossdk.io/errors" + + "github.com/dymensionxyz/gerr-cosmos/gerrc" + + sdk "github.com/cosmos/cosmos-sdk/types" + dymnsutils "github.com/dymensionxyz/dymension/v3/x/dymns/utils" +) + +// GetIdentity returns the unique identity of the SO +func (m *SellOrder) GetIdentity() string { + return fmt.Sprintf("%s|%d|%d", m.AssetId, m.AssetType, m.ExpireAt) +} + +// HasSetSellPrice returns true if the sell price is set +func (m *SellOrder) HasSetSellPrice() bool { + return m.SellPrice != nil && !m.SellPrice.Amount.IsNil() && !m.SellPrice.IsZero() +} + +// HasExpiredAtCtx returns true if the SO has expired at given context +func (m *SellOrder) HasExpiredAtCtx(ctx sdk.Context) bool { + return m.HasExpired(ctx.BlockTime().Unix()) +} + +// HasExpired returns true if the SO has expired at given epoch +func (m *SellOrder) HasExpired(nowEpoch int64) bool { + return m.ExpireAt < nowEpoch +} + +// HasFinishedAtCtx returns true if the SO has expired or completed at given context +func (m *SellOrder) HasFinishedAtCtx(ctx sdk.Context) bool { + return m.HasFinished(ctx.BlockTime().Unix()) +} + +// HasFinished returns true if the SO has expired or completed at given epoch +func (m *SellOrder) HasFinished(nowEpoch int64) bool { + if m.HasExpired(nowEpoch) { + return true + } + + if !m.HasSetSellPrice() { + // when no sell price is set, must wait until completed auction + return false + } + + // complete condition: bid >= sell price + + if m.HighestBid == nil { + return false + } + + return m.HighestBid.Price.IsGTE(*m.SellPrice) +} + +// Validate performs basic validation for the SellOrder. +func (m *SellOrder) Validate() error { + if m == nil { + return errorsmod.Wrap(gerrc.ErrInvalidArgument, "SO is nil") + } + + if m.AssetType == TypeName { + if m.AssetId == "" { + return errorsmod.Wrap(gerrc.ErrInvalidArgument, "Dym-Name of SO is empty") + } + + if !dymnsutils.IsValidDymName(m.AssetId) { + return errorsmod.Wrap(gerrc.ErrInvalidArgument, "Dym-Name of SO is not a valid dym name") + } + } else if m.AssetType == TypeAlias { + if m.AssetId == "" { + return errorsmod.Wrap(gerrc.ErrInvalidArgument, "alias of SO is empty") + } + + if !dymnsutils.IsValidAlias(m.AssetId) { + return errorsmod.Wrap(gerrc.ErrInvalidArgument, "alias of SO is not a valid alias") + } + } else { + return errorsmod.Wrapf(gerrc.ErrInvalidArgument, "invalid SO type: %s", m.AssetType) + } + + if m.ExpireAt == 0 { + return errorsmod.Wrap(gerrc.ErrInvalidArgument, "SO expiry is empty") + } + + if m.MinPrice.Amount.IsNil() || m.MinPrice.IsZero() { + return errorsmod.Wrap(gerrc.ErrInvalidArgument, "SO min price is zero") + } else if m.MinPrice.IsNegative() { + return errorsmod.Wrap(gerrc.ErrInvalidArgument, "SO min price is negative") + } else if err := m.MinPrice.Validate(); err != nil { + return errorsmod.Wrapf(gerrc.ErrInvalidArgument, "SO min price is invalid: %v", err) + } + + if m.HasSetSellPrice() { + if m.SellPrice.IsNegative() { + return errorsmod.Wrap(gerrc.ErrInvalidArgument, "SO sell price is negative") + } else if err := m.SellPrice.Validate(); err != nil { + return errorsmod.Wrapf(gerrc.ErrInvalidArgument, "SO sell price is invalid: %v", err) + } + + if m.SellPrice.Denom != m.MinPrice.Denom { + return errorsmod.Wrap(gerrc.ErrInvalidArgument, "SO sell price denom is different from min price denom") + } + + if m.SellPrice.IsLT(m.MinPrice) { + return errorsmod.Wrap(gerrc.ErrInvalidArgument, "SO sell price is less than min price") + } + } + + if m.HighestBid == nil { + // valid, means no bid yet + } else if err := m.HighestBid.Validate(m.AssetType); err != nil { + return errorsmod.Wrapf(gerrc.ErrInvalidArgument, "SO highest bid is invalid: %v", err) + } else if m.HighestBid.Price.IsLT(m.MinPrice) { + return errorsmod.Wrap(gerrc.ErrInvalidArgument, "SO highest bid price is less than min price") + } else if m.HasSetSellPrice() && m.SellPrice.IsLT(m.HighestBid.Price) { + return errorsmod.Wrap(gerrc.ErrInvalidArgument, "SO sell price is less than highest bid price") + } + + return nil +} + +// Validate performs basic validation for the SellOrderBid. +func (m *SellOrderBid) Validate(assetType AssetType) error { + if m == nil { + return errorsmod.Wrap(gerrc.ErrInvalidArgument, "SO bid is nil") + } + + if m.Bidder == "" { + return errorsmod.Wrap(gerrc.ErrInvalidArgument, "SO bidder is empty") + } + + if !dymnsutils.IsValidBech32AccountAddress(m.Bidder, true) { + return errorsmod.Wrap(gerrc.ErrInvalidArgument, "SO bidder is not a valid bech32 account address") + } + + if m.Price.Amount.IsNil() || m.Price.IsZero() { + return errorsmod.Wrap(gerrc.ErrInvalidArgument, "SO bid price is zero") + } else if m.Price.IsNegative() { + return errorsmod.Wrap(gerrc.ErrInvalidArgument, "SO bid price is negative") + } else if err := m.Price.Validate(); err != nil { + return errorsmod.Wrapf(gerrc.ErrInvalidArgument, "SO bid price is invalid: %v", err) + } + + if err := ValidateOrderParams(m.Params, assetType); err != nil { + return err + } + + return nil +} + +// GetSdkEvent returns the sdk event contains information of Sell-Order record. +// Fired when Sell-Order record is set into store. +func (m SellOrder) GetSdkEvent(actionName string) sdk.Event { + var sellPrice sdk.Coin + if m.HasSetSellPrice() { + sellPrice = *m.SellPrice + } else { + sellPrice = sdk.NewCoin(m.MinPrice.Denom, sdk.ZeroInt()) + } + + var attrHighestBidder, attrHighestBidPrice sdk.Attribute + if m.HighestBid != nil { + attrHighestBidder = sdk.NewAttribute(AttributeKeySoHighestBidder, m.HighestBid.Bidder) + attrHighestBidPrice = sdk.NewAttribute(AttributeKeySoHighestBidPrice, m.HighestBid.Price.String()) + } else { + attrHighestBidder = sdk.NewAttribute(AttributeKeySoHighestBidder, "") + attrHighestBidPrice = sdk.NewAttribute(AttributeKeySoHighestBidPrice, sdk.NewCoin(m.MinPrice.Denom, sdk.ZeroInt()).String()) + } + + return sdk.NewEvent( + EventTypeSellOrder, + sdk.NewAttribute(AttributeKeySoAssetId, m.AssetId), + sdk.NewAttribute(AttributeKeySoAssetType, m.AssetType.PrettyName()), + sdk.NewAttribute(AttributeKeySoExpiryEpoch, fmt.Sprintf("%d", m.ExpireAt)), + sdk.NewAttribute(AttributeKeySoMinPrice, m.MinPrice.String()), + sdk.NewAttribute(AttributeKeySoSellPrice, sellPrice.String()), + attrHighestBidder, + attrHighestBidPrice, + sdk.NewAttribute(AttributeKeySoActionName, actionName), + ) +} + +func (m ActiveSellOrdersExpiration) Validate() error { + if len(m.Records) > 0 { + uniqueName := make(map[string]bool) + // Describe usage of Go Map: only used for validation + allNames := make([]string, len(m.Records)) + + for i, record := range m.Records { + if record.ExpireAt < 1 { + return errorsmod.Wrap(gerrc.ErrInvalidArgument, "active SO expiry is empty") + } + + if _, duplicated := uniqueName[record.AssetId]; duplicated { + return errorsmod.Wrapf(gerrc.ErrInvalidArgument, "active SO is not unique: %s", record.AssetId) + } + + uniqueName[record.AssetId] = true + allNames[i] = record.AssetId + } + + if !sort.StringsAreSorted(allNames) { + return errorsmod.Wrap(gerrc.ErrInvalidArgument, "active SO names are not sorted") + } + } + + return nil +} + +func (m *ActiveSellOrdersExpiration) Sort() { + if len(m.Records) < 2 { + return + } + + sort.Slice(m.Records, func(i, j int) bool { + return m.Records[i].AssetId < m.Records[j].AssetId + }) +} + +func (m *ActiveSellOrdersExpiration) Add(assetId string, expiry int64) { + newRecord := ActiveSellOrdersExpirationRecord{AssetId: assetId, ExpireAt: expiry} + + if len(m.Records) < 1 { + m.Records = []ActiveSellOrdersExpirationRecord{newRecord} + return + } + + foundAtIdx := -1 + + for i, record := range m.Records { + if record.AssetId == assetId { + foundAtIdx = i + break + } + } + + if foundAtIdx > -1 { + m.Records[foundAtIdx].ExpireAt = expiry + } else { + m.Records = append(m.Records, newRecord) + } + + m.Sort() +} + +func (m *ActiveSellOrdersExpiration) Remove(assetId string) { + m.Records = slices.DeleteFunc(m.Records, func(r ActiveSellOrdersExpirationRecord) bool { + return r.AssetId == assetId + }) +} diff --git a/x/dymns/types/sell_order_test.go b/x/dymns/types/sell_order_test.go new file mode 100644 index 000000000..210e3d835 --- /dev/null +++ b/x/dymns/types/sell_order_test.go @@ -0,0 +1,1019 @@ +package types + +import ( + "testing" + "time" + + "github.com/dymensionxyz/sdk-utils/utils/uptr" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/dymensionxyz/dymension/v3/app/params" + "github.com/stretchr/testify/require" +) + +func TestSellOrder_GetIdentity(t *testing.T) { + nameSo := &SellOrder{ + AssetId: "my-name", + AssetType: TypeName, + ExpireAt: 1234, + } + require.Equal(t, "my-name|1|1234", nameSo.GetIdentity()) + aliasSo := &SellOrder{ + AssetId: "alias", + AssetType: TypeAlias, + ExpireAt: 1234, + } + require.Equal(t, "alias|2|1234", aliasSo.GetIdentity()) +} + +func TestSellOrder_HasSetSellPrice(t *testing.T) { + require.False(t, (&SellOrder{ + SellPrice: nil, + }).HasSetSellPrice()) + require.False(t, (&SellOrder{ + SellPrice: &sdk.Coin{}, + }).HasSetSellPrice()) + require.False(t, (&SellOrder{ + SellPrice: uptr.To(testCoin(0)), + }).HasSetSellPrice()) + require.True(t, (&SellOrder{ + SellPrice: uptr.To(testCoin(1)), + }).HasSetSellPrice()) +} + +func TestSellOrder_HasExpiredAtCtx(t *testing.T) { + const epoch int64 = 2 + ctx := sdk.Context{}.WithBlockTime(time.Unix(2, 0)) + require.True(t, (&SellOrder{ + ExpireAt: epoch - 1, + }).HasExpiredAtCtx(ctx)) + require.False(t, (&SellOrder{ + ExpireAt: epoch + 1, + }).HasExpiredAtCtx(ctx)) + require.False(t, (&SellOrder{ + ExpireAt: epoch, + }).HasExpiredAtCtx(ctx), "SO expires after expires at") +} + +func TestSellOrder_HasExpired(t *testing.T) { + const epoch int64 = 2 + require.True(t, (&SellOrder{ + ExpireAt: epoch - 1, + }).HasExpired(epoch)) + require.False(t, (&SellOrder{ + ExpireAt: epoch + 1, + }).HasExpired(epoch)) + require.False(t, (&SellOrder{ + ExpireAt: epoch, + }).HasExpired(epoch), "SO expires after expires at") +} + +func TestSellOrder_HasFinished(t *testing.T) { + oneCoin := testCoin(1) + threeCoin := testCoin(3) + zeroCoin := testCoin(0) + + now := time.Now().UTC() + + tests := []struct { + name string + expireAt int64 + sellPrice *sdk.Coin + highestBid *SellOrderBid + wantFinished bool + }{ + { + name: "expired, without sell-price, without bid", + expireAt: now.Unix() - 1, + sellPrice: &zeroCoin, + highestBid: nil, + wantFinished: true, + }, + { + name: "expired, without sell-price, without bid", + expireAt: now.Unix() - 1, + sellPrice: nil, + highestBid: nil, + wantFinished: true, + }, + { + name: "expired, + sell-price, without bid", + expireAt: now.Unix() - 1, + sellPrice: &threeCoin, + highestBid: nil, + wantFinished: true, + }, + { + name: "expired, + sell-price, + bid (under sell-price)", + expireAt: now.Unix() - 1, + sellPrice: &threeCoin, + highestBid: &SellOrderBid{ + Bidder: "x", + Price: oneCoin, + }, + wantFinished: true, + }, + { + name: "expired, + sell-price, + bid (= sell-price)", + expireAt: now.Unix() - 1, + sellPrice: &threeCoin, + highestBid: &SellOrderBid{ + Bidder: "x", + Price: threeCoin, + }, + wantFinished: true, + }, + { + name: "not expired, without sell-price, without bid", + expireAt: now.Unix() + 1, + sellPrice: &zeroCoin, + highestBid: nil, + wantFinished: false, + }, + { + name: "not expired, without sell-price, without bid", + expireAt: now.Unix() + 1, + sellPrice: nil, + highestBid: nil, + wantFinished: false, + }, + { + name: "not expired, + sell-price, without bid", + expireAt: now.Unix() + 1, + sellPrice: &threeCoin, + highestBid: nil, + wantFinished: false, + }, + { + name: "not expired, + sell-price, + bid (under sell-price)", + expireAt: now.Unix() + 1, + sellPrice: &threeCoin, + highestBid: &SellOrderBid{ + Bidder: "x", + Price: oneCoin, + }, + wantFinished: false, + }, + { + name: "not expired, + sell-price, + bid (= sell-price)", + expireAt: now.Unix() + 1, + sellPrice: &threeCoin, + highestBid: &SellOrderBid{ + Bidder: "x", + Price: threeCoin, + }, + wantFinished: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + m := &SellOrder{ + AssetId: "a", + ExpireAt: tt.expireAt, + MinPrice: oneCoin, + SellPrice: tt.sellPrice, + HighestBid: tt.highestBid, + } + + for _, assetType := range []AssetType{TypeName, TypeAlias} { + m.AssetType = assetType + require.Equal(t, tt.wantFinished, m.HasFinishedAtCtx( + sdk.Context{}.WithBlockTime(now), + )) + require.Equal(t, tt.wantFinished, m.HasFinished(now.Unix())) + } + }) + } +} + +func TestSellOrder_Validate(t *testing.T) { + t.Run("nil obj", func(t *testing.T) { + m := (*SellOrder)(nil) + require.Error(t, m.Validate()) + }) + + //goland:noinspection SpellCheckingInspection + tests := []struct { + name string + dymName string + _type AssetType + expireAt int64 + minPrice sdk.Coin + sellPrice *sdk.Coin + highestBid *SellOrderBid + wantErr bool + wantErrContains string + }{ + { + name: "pass - (Name) valid sell order", + dymName: "my-name", + _type: TypeName, + expireAt: time.Now().Unix(), + minPrice: testCoin(1), + sellPrice: uptr.To(testCoin(1)), + highestBid: &SellOrderBid{ + Bidder: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + Price: testCoin(1), + }, + }, + { + name: "pass - (Alias) valid sell order", + dymName: "alias", + _type: TypeAlias, + expireAt: time.Now().Unix(), + minPrice: testCoin(1), + sellPrice: uptr.To(testCoin(1)), + highestBid: &SellOrderBid{ + Bidder: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + Price: testCoin(1), + Params: []string{"rollapp_1-1"}, + }, + }, + { + name: "fail - (Alias) reject invalid bid", + dymName: "alias", + _type: TypeAlias, + expireAt: time.Now().Unix(), + minPrice: testCoin(1), + sellPrice: uptr.To(testCoin(1)), + highestBid: &SellOrderBid{ + Bidder: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + Price: testCoin(1), + Params: nil, // empty + }, + wantErr: true, + wantErrContains: "SO highest bid is invalid", + }, + { + name: "pass - (Name) valid sell order without bid", + dymName: "my-name", + _type: TypeName, + expireAt: time.Now().Unix(), + minPrice: testCoin(1), + sellPrice: uptr.To(testCoin(1)), + }, + { + name: "pass - (Alias) valid sell order without bid", + dymName: "alias", + _type: TypeAlias, + expireAt: time.Now().Unix(), + minPrice: testCoin(1), + sellPrice: uptr.To(testCoin(1)), + }, + { + name: "pass - (Name) valid sell order without setting sell price", + dymName: "my-name", + _type: TypeName, + expireAt: time.Now().Unix(), + minPrice: testCoin(1), + sellPrice: nil, + }, + { + name: "pass - (Alias) valid sell order without setting sell price", + dymName: "alias", + _type: TypeAlias, + expireAt: time.Now().Unix(), + minPrice: testCoin(1), + sellPrice: nil, + }, + { + name: "fail - (Name) reject empty name", + dymName: "", + _type: TypeName, + expireAt: time.Now().Unix(), + minPrice: testCoin(1), + wantErr: true, + wantErrContains: "Dym-Name of SO is empty", + }, + { + name: "fail - (Alias) reject empty alias", + dymName: "", + _type: TypeAlias, + expireAt: time.Now().Unix(), + minPrice: testCoin(1), + wantErr: true, + wantErrContains: "alias of SO is empty", + }, + { + name: "fail - reject unknown type", + dymName: "asset", + _type: AssetType_AT_UNKNOWN, + expireAt: time.Now().Unix(), + minPrice: testCoin(1), + wantErr: true, + wantErrContains: "invalid SO type", + }, + { + name: "fail - (Name) reject bad name", + dymName: "-my-name", + _type: TypeName, + expireAt: time.Now().Unix(), + minPrice: testCoin(1), + wantErr: true, + wantErrContains: "Dym-Name of SO is not a valid dym name", + }, + { + name: "fail - (Alias) reject bad alias", + dymName: "bad-alias", + _type: TypeAlias, + expireAt: time.Now().Unix(), + minPrice: testCoin(1), + wantErr: true, + wantErrContains: "alias of SO is not a valid alias", + }, + { + name: "fail - empty time", + dymName: "my-name", + _type: TypeName, + expireAt: 0, + minPrice: testCoin(1), + wantErr: true, + wantErrContains: "SO expiry is empty", + }, + { + name: "fail - min price is zero", + dymName: "my-name", + _type: TypeName, + expireAt: time.Now().Unix(), + minPrice: testCoin(0), + wantErr: true, + wantErrContains: "SO min price is zero", + }, + { + name: "fail - min price is empty", + dymName: "my-name", + _type: TypeName, + expireAt: time.Now().Unix(), + minPrice: sdk.Coin{}, + wantErr: true, + wantErrContains: "SO min price is zero", + }, + { + name: "fail - min price is negative", + dymName: "my-name", + _type: TypeName, + expireAt: time.Now().Unix(), + minPrice: testCoin(-1), + wantErr: true, + wantErrContains: "SO min price is negative", + }, + { + name: "fail - min price is invalid", + dymName: "my-name", + _type: TypeName, + expireAt: time.Now().Unix(), + minPrice: sdk.Coin{ + Denom: "-", + Amount: sdk.OneInt(), + }, + wantErr: true, + wantErrContains: "SO min price is invalid", + }, + { + name: "fail - sell price is negative", + dymName: "my-name", + _type: TypeName, + expireAt: time.Now().Unix(), + minPrice: testCoin(1), + sellPrice: uptr.To(testCoin(-1)), + wantErr: true, + wantErrContains: "SO sell price is negative", + }, + { + name: "fail - sell price is invalid", + dymName: "my-name", + _type: TypeName, + expireAt: time.Now().Unix(), + minPrice: testCoin(1), + sellPrice: &sdk.Coin{ + Denom: "-", + Amount: sdk.OneInt(), + }, + wantErr: true, + wantErrContains: "SO sell price is invalid", + }, + { + name: "fail - sell price is less than min price", + dymName: "my-name", + _type: TypeName, + expireAt: time.Now().Unix(), + minPrice: testCoin(2), + sellPrice: uptr.To(testCoin(1)), + wantErr: true, + wantErrContains: "SO sell price is less than min price", + }, + { + name: "fail - sell price denom must match min price denom", + dymName: "my-name", + _type: TypeName, + expireAt: time.Now().Unix(), + minPrice: testCoin(1), + sellPrice: uptr.To(sdk.NewInt64Coin("u"+params.BaseDenom, 2)), + wantErr: true, + wantErrContains: "SO sell price denom is different from min price denom", + }, + { + name: "fail - invalid highest bid", + dymName: "my-name", + _type: TypeName, + expireAt: time.Now().Unix(), + minPrice: testCoin(1), + sellPrice: uptr.To(testCoin(1)), + highestBid: &SellOrderBid{ + Bidder: "0x1", + Price: testCoin(1), + }, + wantErr: true, + wantErrContains: "SO bidder is not a valid bech32 account address", + }, + { + name: "fail - highest bid < min price", + dymName: "my-name", + _type: TypeName, + expireAt: time.Now().Unix(), + minPrice: testCoin(2), + sellPrice: uptr.To(testCoin(3)), + highestBid: &SellOrderBid{ + Bidder: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + Price: testCoin(1), + }, + wantErr: true, + wantErrContains: "SO highest bid price is less than min price", + }, + { + name: "fail - highest bid > sell price", + dymName: "my-name", + _type: TypeName, + expireAt: time.Now().Unix(), + minPrice: testCoin(2), + sellPrice: uptr.To(testCoin(3)), + highestBid: &SellOrderBid{ + Bidder: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + Price: testCoin(4), + }, + wantErr: true, + wantErrContains: "SO sell price is less than highest bid price", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + m := &SellOrder{ + AssetId: tt.dymName, + AssetType: tt._type, + ExpireAt: tt.expireAt, + MinPrice: tt.minPrice, + SellPrice: tt.sellPrice, + HighestBid: tt.highestBid, + } + + err := m.Validate() + if tt.wantErr { + require.NotEmpty(t, tt.wantErrContains, "mis-configured test case") + require.Error(t, err) + require.Contains(t, err.Error(), tt.wantErrContains) + return + } + + require.NoError(t, err) + }) + } +} + +func TestSellOrderBid_Validate(t *testing.T) { + t.Run("nil obj", func(t *testing.T) { + m := (*SellOrderBid)(nil) + require.Error(t, m.Validate(TypeName)) + require.Error(t, m.Validate(TypeAlias)) + }) + + //goland:noinspection SpellCheckingInspection + tests := []struct { + name string + bidder string + price sdk.Coin + params []string + assetType AssetType + wantErr bool + wantErrContains string + }{ + { + name: "pass - (Name) valid sell order bid", + bidder: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + params: nil, + assetType: TypeName, + price: testCoin(1), + }, + { + name: "pass - (Alias) valid sell order bid", + bidder: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + params: []string{"rollapp_1-1"}, + assetType: TypeAlias, + price: testCoin(1), + }, + { + name: "fail - empty bidder", + bidder: "", + price: testCoin(1), + params: nil, + assetType: TypeName, + wantErr: true, + wantErrContains: "SO bidder is empty", + }, + { + name: "fail - bad bidder", + bidder: "0x1", + price: testCoin(1), + params: nil, + assetType: TypeName, + wantErr: true, + wantErrContains: "SO bidder is not a valid bech32 account address", + }, + { + name: "fail - zero price", + bidder: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + price: testCoin(0), + params: nil, + assetType: TypeName, + wantErr: true, + wantErrContains: "SO bid price is zero", + }, + { + name: "fail - zero price", + bidder: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + price: sdk.Coin{}, + params: nil, + assetType: TypeName, + wantErr: true, + wantErrContains: "SO bid price is zero", + }, + { + name: "fail - negative price", + bidder: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + price: sdk.Coin{ + Denom: params.BaseDenom, + Amount: sdk.NewInt(-1), + }, + params: nil, + assetType: TypeName, + wantErr: true, + wantErrContains: "SO bid price is negative", + }, + { + name: "fail - invalid price", + bidder: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + price: sdk.Coin{ + Denom: "-", + Amount: sdk.OneInt(), + }, + params: nil, + assetType: TypeName, + wantErr: true, + wantErrContains: "SO bid price is invalid", + }, + { + name: "fail - (Name) bad params", + bidder: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + price: testCoin(1), + params: []string{"non-empty"}, + assetType: TypeName, + wantErr: true, + wantErrContains: "not accept order params for asset type", + }, + { + name: "fail - (Alias) bad params", + bidder: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + price: testCoin(1), + params: nil, + assetType: TypeAlias, + wantErr: true, + wantErrContains: "expect 1 order param of RollApp ID", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + m := &SellOrderBid{ + Bidder: tt.bidder, + Price: tt.price, + Params: tt.params, + } + err := m.Validate(tt.assetType) + if tt.wantErr { + require.NotEmpty(t, tt.wantErrContains, "mis-configured test case") + require.Error(t, err) + require.Contains(t, err.Error(), tt.wantErrContains) + return + } + + require.NoError(t, err) + }) + } +} + +func TestSellOrder_GetSdkEvent(t *testing.T) { + t.Run("all fields", func(t *testing.T) { + event := SellOrder{ + AssetId: "a", + AssetType: TypeName, + ExpireAt: 123456, + MinPrice: testCoin(1), + SellPrice: uptr.To(testCoin(3)), + HighestBid: &SellOrderBid{ + Bidder: "d", + Price: testCoin(2), + }, + }.GetSdkEvent("action-name") + requireEventEquals(t, event, + EventTypeSellOrder, + AttributeKeySoAssetId, "a", + AttributeKeySoAssetType, TypeName.PrettyName(), + AttributeKeySoExpiryEpoch, "123456", + AttributeKeySoMinPrice, "1"+params.BaseDenom, + AttributeKeySoSellPrice, "3"+params.BaseDenom, + AttributeKeySoHighestBidder, "d", + AttributeKeySoHighestBidPrice, "2"+params.BaseDenom, + AttributeKeySoActionName, "action-name", + ) + }) + + t.Run("SO type alias", func(t *testing.T) { + event := SellOrder{ + AssetId: "a", + AssetType: TypeAlias, + ExpireAt: 123456, + MinPrice: testCoin(1), + SellPrice: uptr.To(testCoin(3)), + HighestBid: &SellOrderBid{ + Bidder: "d", + Price: testCoin(2), + }, + }.GetSdkEvent("action-name") + require.NotNil(t, event) + require.Equal(t, EventTypeSellOrder, event.Type) + require.Len(t, event.Attributes, 8) + require.Equal(t, AttributeKeySoAssetType, event.Attributes[1].Key) + require.Equal(t, TypeAlias.PrettyName(), event.Attributes[1].Value) + }) + + t.Run("no sell-price", func(t *testing.T) { + event := SellOrder{ + AssetId: "a", + AssetType: TypeName, + ExpireAt: 123456, + MinPrice: testCoin(1), + HighestBid: &SellOrderBid{ + Bidder: "d", + Price: testCoin(2), + }, + }.GetSdkEvent("action-name") + requireEventEquals(t, event, + EventTypeSellOrder, + AttributeKeySoAssetId, "a", + AttributeKeySoAssetType, TypeName.PrettyName(), + AttributeKeySoExpiryEpoch, "123456", + AttributeKeySoMinPrice, "1"+params.BaseDenom, + AttributeKeySoSellPrice, "0"+params.BaseDenom, + AttributeKeySoHighestBidder, "d", + AttributeKeySoHighestBidPrice, "2"+params.BaseDenom, + AttributeKeySoActionName, "action-name", + ) + }) + + t.Run("no highest bid", func(t *testing.T) { + event := SellOrder{ + AssetId: "a", + AssetType: TypeName, + ExpireAt: 123456, + MinPrice: testCoin(1), + SellPrice: uptr.To(testCoin(3)), + }.GetSdkEvent("action-name") + requireEventEquals(t, event, + EventTypeSellOrder, + AttributeKeySoAssetId, "a", + AttributeKeySoAssetType, TypeName.PrettyName(), + AttributeKeySoExpiryEpoch, "123456", + AttributeKeySoMinPrice, "1"+params.BaseDenom, + AttributeKeySoSellPrice, "3"+params.BaseDenom, + AttributeKeySoHighestBidder, "", + AttributeKeySoHighestBidPrice, "0"+params.BaseDenom, + AttributeKeySoActionName, "action-name", + ) + }) +} + +func TestActiveSellOrdersExpiration_Validate(t *testing.T) { + tests := []struct { + name string + records []ActiveSellOrdersExpirationRecord + wantErr bool + wantErrContains string + }{ + { + name: "pass", + records: []ActiveSellOrdersExpirationRecord{ + {AssetId: "a", ExpireAt: 2}, {AssetId: "b", ExpireAt: 1}, + }, + wantErr: false, + }, + { + name: "fail - name must be unique", + records: []ActiveSellOrdersExpirationRecord{ + {AssetId: "a", ExpireAt: 2}, {AssetId: "a", ExpireAt: 1}, + }, + wantErr: true, + wantErrContains: "active SO is not unique", + }, + { + name: "pass - expire at can be duplicated", + records: []ActiveSellOrdersExpirationRecord{ + {AssetId: "a", ExpireAt: 2}, {AssetId: "b", ExpireAt: 2}, + }, + wantErr: false, + }, + { + name: "fail - expire at must be > 0", + records: []ActiveSellOrdersExpirationRecord{ + {AssetId: "a", ExpireAt: 0}, {AssetId: "b", ExpireAt: -1}, + }, + wantErr: true, + wantErrContains: "active SO expiry is empty", + }, + { + name: "fail - must be sorted", + records: []ActiveSellOrdersExpirationRecord{ + {AssetId: "b", ExpireAt: 1}, {AssetId: "a", ExpireAt: 1}, + }, + wantErr: true, + wantErrContains: "active SO names are not sorted", + }, + { + name: "pass - empty list", + records: []ActiveSellOrdersExpirationRecord{}, + wantErr: false, + }, + { + name: "pass - nil list", + records: nil, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + m := ActiveSellOrdersExpiration{ + Records: tt.records, + } + + err := m.Validate() + + if tt.wantErr { + require.NotEmpty(t, tt.wantErrContains, "mis-configured test case") + require.Error(t, err) + require.Contains(t, err.Error(), tt.wantErrContains) + return + } + + require.NoError(t, err) + }) + } +} + +func TestActiveSellOrdersExpiration_Sort(t *testing.T) { + tests := []struct { + name string + records []ActiveSellOrdersExpirationRecord + want []ActiveSellOrdersExpirationRecord + }{ + { + name: "can sort", + records: []ActiveSellOrdersExpirationRecord{ + {AssetId: "b", ExpireAt: 2}, {AssetId: "a", ExpireAt: 2}, + }, + want: []ActiveSellOrdersExpirationRecord{ + {AssetId: "a", ExpireAt: 2}, {AssetId: "b", ExpireAt: 2}, + }, + }, + { + name: "sort by name asc", + records: []ActiveSellOrdersExpirationRecord{ + {AssetId: "b", ExpireAt: 1}, {AssetId: "a", ExpireAt: 2}, {AssetId: "c", ExpireAt: 3}, + }, + want: []ActiveSellOrdersExpirationRecord{ + {AssetId: "a", ExpireAt: 2}, {AssetId: "b", ExpireAt: 1}, {AssetId: "c", ExpireAt: 3}, + }, + }, + { + name: "can sort one", + records: []ActiveSellOrdersExpirationRecord{ + {AssetId: "a", ExpireAt: 2}, + }, + want: []ActiveSellOrdersExpirationRecord{ + {AssetId: "a", ExpireAt: 2}, + }, + }, + { + name: "empty list", + records: []ActiveSellOrdersExpirationRecord{}, + want: []ActiveSellOrdersExpirationRecord{}, + }, + { + name: "nil list", + records: nil, + want: nil, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + m := ActiveSellOrdersExpiration{ + Records: tt.records, + } + + m.Sort() + + require.Equal(t, tt.want, m.Records) + }) + } +} + +func TestActiveSellOrdersExpiration_Add(t *testing.T) { + tests := []struct { + name string + existing []ActiveSellOrdersExpirationRecord + addName string + addExpiry int64 + want []ActiveSellOrdersExpirationRecord + }{ + { + name: "can add", + existing: []ActiveSellOrdersExpirationRecord{{AssetId: "a", ExpireAt: 1}}, + addName: "b", + addExpiry: 2, + want: []ActiveSellOrdersExpirationRecord{ + {AssetId: "a", ExpireAt: 1}, {AssetId: "b", ExpireAt: 2}, + }, + }, + { + name: "add will perform sort", + existing: []ActiveSellOrdersExpirationRecord{{AssetId: "b", ExpireAt: 1}}, + addName: "a", + addExpiry: 2, + want: []ActiveSellOrdersExpirationRecord{ + {AssetId: "a", ExpireAt: 2}, {AssetId: "b", ExpireAt: 1}, + }, + }, + { + name: "add can override existing", + existing: []ActiveSellOrdersExpirationRecord{{AssetId: "b", ExpireAt: 1}}, + addName: "b", + addExpiry: 2, + want: []ActiveSellOrdersExpirationRecord{ + {AssetId: "b", ExpireAt: 2}, + }, + }, + { + name: "add can override existing", + existing: []ActiveSellOrdersExpirationRecord{ + {AssetId: "b", ExpireAt: 1}, {AssetId: "c", ExpireAt: 1}, {AssetId: "d", ExpireAt: 1}, + }, + addName: "c", + addExpiry: 2, + want: []ActiveSellOrdersExpirationRecord{ + {AssetId: "b", ExpireAt: 1}, {AssetId: "c", ExpireAt: 2}, {AssetId: "d", ExpireAt: 1}, + }, + }, + { + name: "can add to nil", + existing: nil, + addName: "a", + addExpiry: 1, + want: []ActiveSellOrdersExpirationRecord{ + {AssetId: "a", ExpireAt: 1}, + }, + }, + { + name: "can add to empty", + existing: []ActiveSellOrdersExpirationRecord{}, + addName: "a", + addExpiry: 1, + want: []ActiveSellOrdersExpirationRecord{ + {AssetId: "a", ExpireAt: 1}, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + m := &ActiveSellOrdersExpiration{ + Records: tt.existing, + } + m.Add(tt.addName, tt.addExpiry) + + require.Equal(t, tt.want, m.Records) + }) + } +} + +func TestActiveSellOrdersExpiration_Remove(t *testing.T) { + tests := []struct { + name string + existing []ActiveSellOrdersExpirationRecord + removeName string + want []ActiveSellOrdersExpirationRecord + }{ + { + name: "can remove", + existing: []ActiveSellOrdersExpirationRecord{ + {AssetId: "a", ExpireAt: 1}, {AssetId: "b", ExpireAt: 1}, + }, + removeName: "a", + want: []ActiveSellOrdersExpirationRecord{ + {AssetId: "b", ExpireAt: 1}, + }, + }, + { + name: "remove the last one", + existing: []ActiveSellOrdersExpirationRecord{ + {AssetId: "a", ExpireAt: 1}, + }, + removeName: "a", + want: []ActiveSellOrdersExpirationRecord{}, + }, + { + name: "remove in head", + existing: []ActiveSellOrdersExpirationRecord{ + {AssetId: "a", ExpireAt: 1}, {AssetId: "b", ExpireAt: 1}, {AssetId: "c", ExpireAt: 1}, + }, + removeName: "a", + want: []ActiveSellOrdersExpirationRecord{ + {AssetId: "b", ExpireAt: 1}, {AssetId: "c", ExpireAt: 1}, + }, + }, + { + name: "remove in middle", + existing: []ActiveSellOrdersExpirationRecord{ + {AssetId: "a", ExpireAt: 1}, {AssetId: "b", ExpireAt: 1}, {AssetId: "c", ExpireAt: 1}, + }, + removeName: "b", + want: []ActiveSellOrdersExpirationRecord{ + {AssetId: "a", ExpireAt: 1}, {AssetId: "c", ExpireAt: 1}, + }, + }, + { + name: "remove in tails", + existing: []ActiveSellOrdersExpirationRecord{ + {AssetId: "a", ExpireAt: 1}, {AssetId: "b", ExpireAt: 1}, {AssetId: "c", ExpireAt: 1}, + }, + removeName: "c", + want: []ActiveSellOrdersExpirationRecord{ + {AssetId: "a", ExpireAt: 1}, {AssetId: "b", ExpireAt: 1}, + }, + }, + { + name: "remove keep order", + existing: []ActiveSellOrdersExpirationRecord{ + {AssetId: "c", ExpireAt: 1}, {AssetId: "b", ExpireAt: 1}, {AssetId: "a", ExpireAt: 1}, + }, + removeName: "b", + want: []ActiveSellOrdersExpirationRecord{ + {AssetId: "c", ExpireAt: 1}, {AssetId: "a", ExpireAt: 1}, + }, + }, + { + name: "can remove from nil", + existing: nil, + removeName: "a", + want: nil, + }, + { + name: "can remove from empty", + existing: []ActiveSellOrdersExpirationRecord{}, + removeName: "a", + want: []ActiveSellOrdersExpirationRecord{}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + m := &ActiveSellOrdersExpiration{ + Records: tt.existing, + } + m.Remove(tt.removeName) + + require.Equal(t, tt.want, m.Records) + }) + } +} + +func requireEventEquals(t *testing.T, event sdk.Event, wantType string, wantAttributePairs ...string) { + require.NotNil(t, event) + require.True(t, len(wantAttributePairs)%2 == 0, "size of expected attr pairs must be even") + require.Equal(t, wantType, event.Type) + require.Len(t, event.Attributes, len(wantAttributePairs)/2) + for i := 0; i < len(wantAttributePairs); i += 2 { + require.Equal(t, wantAttributePairs[i], event.Attributes[i/2].Key) + require.Equal(t, wantAttributePairs[i+1], event.Attributes[i/2].Value) + } +} + +func testCoin(amount int64) sdk.Coin { + return sdk.Coin{ + Denom: params.BaseDenom, + Amount: sdk.NewInt(amount), + } +} diff --git a/x/dymns/types/string_list.go b/x/dymns/types/string_list.go new file mode 100644 index 000000000..6276f6ac7 --- /dev/null +++ b/x/dymns/types/string_list.go @@ -0,0 +1,75 @@ +package types + +import "slices" + +// TODO: move this to a sdk-utils package + +// StringList is a list of strings. +// Used to add some operations on the list. +type StringList []string + +// Distinct returns a new list with duplicates removed. +// Result will be sorted. +func (m StringList) Distinct() (distinct StringList) { + uniqueElements := make(map[string]bool) + // Describe usage of Go Map: used to store unique elements, later result will be sorted + defer func() { + distinct.Sort() + }() + + for _, ele := range m { + uniqueElements[ele] = true + } + + distinctElements := make([]string, 0, len(uniqueElements)) + for name := range uniqueElements { + distinctElements = append(distinctElements, name) + } + + distinct = distinctElements + + return +} + +// Combine merges the elements from the current list and the other list. +// Result will be sorted distinct. +func (m StringList) Combine(other StringList) StringList { + return append(m, other...).Distinct() +} + +// Exclude removes the elements from the current list that are in the toBeExcluded list. +// Result will be sorted distinct. +func (m StringList) Exclude(toBeExcluded StringList) (afterExcluded StringList) { + var excludedElements map[string]bool + // Describe usage of Go Map: used to store unique elements, later result will be sorted + defer func() { + afterExcluded.Sort() + }() + + if len(toBeExcluded) > 0 { + excludedElements = make(map[string]bool) + + for _, element := range toBeExcluded { + excludedElements[element] = true + } + + filteredElements := make([]string, 0, len(m)) + for _, element := range m { + if !excludedElements[element] { + filteredElements = append(filteredElements, element) + } + } + + afterExcluded = StringList(filteredElements).Distinct() + } else { + afterExcluded = m + } + + return +} + +// Sort sorts the elements in the list. +func (m StringList) Sort() StringList { + slices.Sort(m) + return m +} diff --git a/x/dymns/types/string_list_test.go b/x/dymns/types/string_list_test.go new file mode 100644 index 000000000..73514a017 --- /dev/null +++ b/x/dymns/types/string_list_test.go @@ -0,0 +1,358 @@ +package types + +import ( + "sort" + "testing" + + "github.com/stretchr/testify/require" +) + +func Test_stringList_Distinct(t *testing.T) { + tests := []struct { + name string + provided StringList + wantDistinct StringList + }{ + { + name: "distinct", + provided: []string{"a", "b", "b", "a", "c", "d"}, + wantDistinct: []string{"a", "b", "c", "d"}, + }, + { + name: "distinct of single", + provided: []string{"a"}, + wantDistinct: []string{"a"}, + }, + { + name: "empty", + provided: []string{}, + wantDistinct: []string{}, + }, + { + name: "nil", + provided: nil, + wantDistinct: []string{}, + }, + { + name: "result must be sorted", + provided: []string{"d", "c", "a", "b", "a", "b", "e"}, + wantDistinct: []string{"a", "b", "c", "d", "e"}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + distinct := tt.provided.Distinct() + want := tt.wantDistinct + + sort.Strings(distinct) + sort.Strings(want) + + require.Equal(t, want, distinct) + }) + + t.Run(tt.name, func(t *testing.T) { + m := ReverseLookupDymNames{ + DymNames: tt.provided, + } + distinct := m.Distinct().DymNames + want := []string(tt.wantDistinct) + + sort.Strings(distinct) + sort.Strings(want) + + require.Equal(t, want, distinct) + }) + + t.Run(tt.name, func(t *testing.T) { + m := ReverseLookupBuyOrderIds{ + OrderIds: tt.provided, + } + distinct := m.Distinct().OrderIds + want := []string(tt.wantDistinct) + + sort.Strings(distinct) + sort.Strings(want) + + require.Equal(t, want, distinct) + }) + } +} + +func Test_stringList_Combine(t *testing.T) { + tests := []struct { + name string + provided StringList + others StringList + wantCombined StringList + }{ + { + name: "combined", + provided: []string{"a", "b"}, + others: []string{"c", "d"}, + wantCombined: []string{"a", "b", "c", "d"}, + }, + { + name: "combined, distinct", + provided: []string{"a", "b"}, + others: []string{"b", "c", "d"}, + wantCombined: []string{"a", "b", "c", "d"}, + }, + { + name: "combined, distinct", + provided: []string{"a"}, + others: []string{"a"}, + wantCombined: []string{"a"}, + }, + { + name: "combine empty with other", + provided: nil, + others: []string{"a"}, + wantCombined: []string{"a"}, + }, + { + name: "combine empty with other", + provided: []string{"a"}, + others: nil, + wantCombined: []string{"a"}, + }, + { + name: "combine empty with other", + provided: nil, + others: []string{"a", "b"}, + wantCombined: []string{"a", "b"}, + }, + { + name: "combine with other empty", + provided: []string{"a", "b"}, + others: nil, + wantCombined: []string{"a", "b"}, + }, + { + name: "distinct source", + provided: []string{"a", "b", "a"}, + others: []string{"c", "c", "d"}, + wantCombined: []string{"a", "b", "c", "d"}, + }, + { + name: "both empty", + provided: []string{}, + others: []string{}, + wantCombined: []string{}, + }, + { + name: "both nil", + provided: nil, + others: nil, + wantCombined: []string{}, + }, + { + name: "result must be sorted", + provided: []string{"d", "c", "a", "b"}, + others: []string{"a", "b", "e"}, + wantCombined: []string{"a", "b", "c", "d", "e"}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + combined := tt.provided.Combine(tt.others) + want := tt.wantCombined + + sort.Strings(combined) + sort.Strings(want) + + require.Equal(t, want, combined) + }) + + t.Run(tt.name, func(t *testing.T) { + m := ReverseLookupDymNames{ + DymNames: tt.provided, + } + other := ReverseLookupDymNames{ + DymNames: tt.others, + } + combined := m.Combine(other).DymNames + want := []string(tt.wantCombined) + + sort.Strings(combined) + sort.Strings(want) + + require.Equal(t, want, combined) + }) + + t.Run(tt.name, func(t *testing.T) { + m := ReverseLookupBuyOrderIds{ + OrderIds: tt.provided, + } + other := ReverseLookupBuyOrderIds{ + OrderIds: tt.others, + } + combined := m.Combine(other).OrderIds + want := []string(tt.wantCombined) + + sort.Strings(combined) + sort.Strings(want) + + require.Equal(t, want, combined) + }) + } +} + +func Test_stringList_Exclude(t *testing.T) { + tests := []struct { + name string + provided StringList + toBeExcluded StringList + want StringList + }{ + { + name: "exclude", + provided: []string{"a", "b", "c", "d"}, + toBeExcluded: []string{"b", "d"}, + want: []string{"a", "c"}, + }, + { + name: "exclude all", + provided: []string{"a", "b", "c", "d"}, + toBeExcluded: []string{"d", "c", "b", "a"}, + want: []string{}, + }, + { + name: "exclude none", + provided: []string{"a", "b", "c", "d"}, + toBeExcluded: []string{}, + want: []string{"a", "b", "c", "d"}, + }, + { + name: "exclude nil", + provided: []string{"a", "b", "c", "d"}, + toBeExcluded: []string{}, + want: []string{"a", "b", "c", "d"}, + }, + { + name: "none exclude", + provided: []string{}, + toBeExcluded: []string{"a", "b", "c", "d"}, + want: []string{}, + }, + { + name: "nil exclude", + provided: nil, + toBeExcluded: []string{"a", "b", "c", "d"}, + want: []string{}, + }, + { + name: "distinct after exclude", + provided: []string{"a", "a", "b"}, + toBeExcluded: []string{"b", "d"}, + want: []string{"a"}, + }, + { + name: "exclude partial", + provided: []string{"a", "b", "c"}, + toBeExcluded: []string{"b", "c", "d"}, + want: []string{"a"}, + }, + { + name: "result must be sorted", + provided: []string{"d", "c", "a", "b", "a", "b", "e"}, + toBeExcluded: []string{"e", "f"}, + want: []string{"a", "b", "c", "d"}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + combined := tt.provided.Exclude(tt.toBeExcluded) + want := tt.want + + sort.Strings(combined) + sort.Strings(want) + + require.Equal(t, want, combined) + }) + + t.Run(tt.name, func(t *testing.T) { + m := ReverseLookupDymNames{ + DymNames: tt.provided, + } + other := ReverseLookupDymNames{ + DymNames: tt.toBeExcluded, + } + combined := m.Exclude(other).DymNames + want := []string(tt.want) + + sort.Strings(combined) + sort.Strings(want) + + require.Equal(t, want, combined) + }) + + t.Run(tt.name, func(t *testing.T) { + m := ReverseLookupBuyOrderIds{ + OrderIds: tt.provided, + } + other := ReverseLookupBuyOrderIds{ + OrderIds: tt.toBeExcluded, + } + combined := m.Exclude(other).OrderIds + want := []string(tt.want) + + sort.Strings(combined) + sort.Strings(want) + + require.Equal(t, want, combined) + }) + } +} + +func Test_stringList_Sort(t *testing.T) { + tests := []struct { + name string + provided StringList + want StringList + }{ + { + name: "can sort", + provided: []string{"b", "a", "c"}, + want: []string{"a", "b", "c"}, + }, + { + name: "can sort single", + provided: []string{"a"}, + want: []string{"a"}, + }, + { + name: "sort will not try distinct", + provided: []string{"b", "a", "c", "a"}, + want: []string{"a", "a", "b", "c"}, + }, + { + name: "nil", + provided: nil, + want: nil, + }, + { + name: "empty", + provided: []string{}, + want: []string{}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + require.Equal(t, tt.want, tt.provided.Sort()) + }) + + t.Run(tt.name, func(t *testing.T) { + m := ReverseLookupDymNames{ + DymNames: tt.provided, + } + require.Equal(t, []string(tt.want), m.Sort().DymNames) + }) + + t.Run(tt.name, func(t *testing.T) { + m := ReverseLookupBuyOrderIds{ + OrderIds: tt.provided, + } + require.Equal(t, []string(tt.want), m.Sort().OrderIds) + }) + } +} diff --git a/x/dymns/types/tx.pb.go b/x/dymns/types/tx.pb.go new file mode 100644 index 000000000..bfcac46ae --- /dev/null +++ b/x/dymns/types/tx.pb.go @@ -0,0 +1,6689 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: dymensionxyz/dymension/dymns/tx.proto + +package types + +import ( + context "context" + fmt "fmt" + _ "github.com/cosmos/cosmos-proto" + types "github.com/cosmos/cosmos-sdk/types" + _ "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 + +// MsgRegisterName defines the message used for user to register or extends ownership duration of a Dym-Name. +type MsgRegisterName struct { + // name is the Dym-Name to be registered. + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + // owner is the account address of the account which owns the order. + Owner string `protobuf:"bytes,2,opt,name=owner,proto3" json:"owner,omitempty"` + // duration is the number of years the Dym-Name will be registered for. + Duration int64 `protobuf:"varint,3,opt,name=duration,proto3" json:"duration,omitempty"` + // confirm_payment is used to ensure user acknowledge of the amount coin that the user must pay. + // If the amount mis-match with the actual payment, the transaction will be rejected. + ConfirmPayment types.Coin `protobuf:"bytes,4,opt,name=confirm_payment,json=confirmPayment,proto3" json:"confirm_payment"` + // contact defines an optional contact information for the Dym-Name. + Contact string `protobuf:"bytes,5,opt,name=contact,proto3" json:"contact,omitempty"` +} + +func (m *MsgRegisterName) Reset() { *m = MsgRegisterName{} } +func (m *MsgRegisterName) String() string { return proto.CompactTextString(m) } +func (*MsgRegisterName) ProtoMessage() {} +func (*MsgRegisterName) Descriptor() ([]byte, []int) { + return fileDescriptor_88dd2f81468013c2, []int{0} +} +func (m *MsgRegisterName) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgRegisterName) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgRegisterName.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 *MsgRegisterName) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgRegisterName.Merge(m, src) +} +func (m *MsgRegisterName) XXX_Size() int { + return m.Size() +} +func (m *MsgRegisterName) XXX_DiscardUnknown() { + xxx_messageInfo_MsgRegisterName.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgRegisterName proto.InternalMessageInfo + +func (m *MsgRegisterName) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *MsgRegisterName) GetOwner() string { + if m != nil { + return m.Owner + } + return "" +} + +func (m *MsgRegisterName) GetDuration() int64 { + if m != nil { + return m.Duration + } + return 0 +} + +func (m *MsgRegisterName) GetConfirmPayment() types.Coin { + if m != nil { + return m.ConfirmPayment + } + return types.Coin{} +} + +func (m *MsgRegisterName) GetContact() string { + if m != nil { + return m.Contact + } + return "" +} + +// MsgRegisterNameResponse defines the response for the name registration. +type MsgRegisterNameResponse struct { +} + +func (m *MsgRegisterNameResponse) Reset() { *m = MsgRegisterNameResponse{} } +func (m *MsgRegisterNameResponse) String() string { return proto.CompactTextString(m) } +func (*MsgRegisterNameResponse) ProtoMessage() {} +func (*MsgRegisterNameResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_88dd2f81468013c2, []int{1} +} +func (m *MsgRegisterNameResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgRegisterNameResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgRegisterNameResponse.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 *MsgRegisterNameResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgRegisterNameResponse.Merge(m, src) +} +func (m *MsgRegisterNameResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgRegisterNameResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgRegisterNameResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgRegisterNameResponse proto.InternalMessageInfo + +// MsgRegisterAlias defines the message used for user to register a new Alias for their owned RollApp. +type MsgRegisterAlias struct { + // alias to be registered. + Alias string `protobuf:"bytes,1,opt,name=alias,proto3" json:"alias,omitempty"` + // rollapp_id is RollApp ID that the new Alias to be assigned for. + RollappId string `protobuf:"bytes,2,opt,name=rollapp_id,json=rollappId,proto3" json:"rollapp_id,omitempty"` + // owner is the account address of the RollApp which owns the Alias. + Owner string `protobuf:"bytes,3,opt,name=owner,proto3" json:"owner,omitempty"` + // confirm_payment is used to ensure user acknowledge of the amount coin that the user must pay. + // If the amount mis-match with the actual payment, the transaction will be rejected. + ConfirmPayment types.Coin `protobuf:"bytes,4,opt,name=confirm_payment,json=confirmPayment,proto3" json:"confirm_payment"` +} + +func (m *MsgRegisterAlias) Reset() { *m = MsgRegisterAlias{} } +func (m *MsgRegisterAlias) String() string { return proto.CompactTextString(m) } +func (*MsgRegisterAlias) ProtoMessage() {} +func (*MsgRegisterAlias) Descriptor() ([]byte, []int) { + return fileDescriptor_88dd2f81468013c2, []int{2} +} +func (m *MsgRegisterAlias) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgRegisterAlias) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgRegisterAlias.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 *MsgRegisterAlias) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgRegisterAlias.Merge(m, src) +} +func (m *MsgRegisterAlias) XXX_Size() int { + return m.Size() +} +func (m *MsgRegisterAlias) XXX_DiscardUnknown() { + xxx_messageInfo_MsgRegisterAlias.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgRegisterAlias proto.InternalMessageInfo + +func (m *MsgRegisterAlias) GetAlias() string { + if m != nil { + return m.Alias + } + return "" +} + +func (m *MsgRegisterAlias) GetRollappId() string { + if m != nil { + return m.RollappId + } + return "" +} + +func (m *MsgRegisterAlias) GetOwner() string { + if m != nil { + return m.Owner + } + return "" +} + +func (m *MsgRegisterAlias) GetConfirmPayment() types.Coin { + if m != nil { + return m.ConfirmPayment + } + return types.Coin{} +} + +// MsgRegisterAliasResponse defines the response for the alias registration. +type MsgRegisterAliasResponse struct { +} + +func (m *MsgRegisterAliasResponse) Reset() { *m = MsgRegisterAliasResponse{} } +func (m *MsgRegisterAliasResponse) String() string { return proto.CompactTextString(m) } +func (*MsgRegisterAliasResponse) ProtoMessage() {} +func (*MsgRegisterAliasResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_88dd2f81468013c2, []int{3} +} +func (m *MsgRegisterAliasResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgRegisterAliasResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgRegisterAliasResponse.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 *MsgRegisterAliasResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgRegisterAliasResponse.Merge(m, src) +} +func (m *MsgRegisterAliasResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgRegisterAliasResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgRegisterAliasResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgRegisterAliasResponse proto.InternalMessageInfo + +// MsgTransferDymNameOwnership defines the message used for user to transfer ownership of a Dym-Name. +type MsgTransferDymNameOwnership struct { + // name is the Dym-Name to be transferred ownership. + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + // owner is the account address of the account which is currently owner of the Dym-Name. + Owner string `protobuf:"bytes,2,opt,name=owner,proto3" json:"owner,omitempty"` + // new_owner is the account address of the next account which will own the Dym-Name. + NewOwner string `protobuf:"bytes,3,opt,name=new_owner,json=newOwner,proto3" json:"new_owner,omitempty"` +} + +func (m *MsgTransferDymNameOwnership) Reset() { *m = MsgTransferDymNameOwnership{} } +func (m *MsgTransferDymNameOwnership) String() string { return proto.CompactTextString(m) } +func (*MsgTransferDymNameOwnership) ProtoMessage() {} +func (*MsgTransferDymNameOwnership) Descriptor() ([]byte, []int) { + return fileDescriptor_88dd2f81468013c2, []int{4} +} +func (m *MsgTransferDymNameOwnership) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgTransferDymNameOwnership) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgTransferDymNameOwnership.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 *MsgTransferDymNameOwnership) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgTransferDymNameOwnership.Merge(m, src) +} +func (m *MsgTransferDymNameOwnership) XXX_Size() int { + return m.Size() +} +func (m *MsgTransferDymNameOwnership) XXX_DiscardUnknown() { + xxx_messageInfo_MsgTransferDymNameOwnership.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgTransferDymNameOwnership proto.InternalMessageInfo + +func (m *MsgTransferDymNameOwnership) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *MsgTransferDymNameOwnership) GetOwner() string { + if m != nil { + return m.Owner + } + return "" +} + +func (m *MsgTransferDymNameOwnership) GetNewOwner() string { + if m != nil { + return m.NewOwner + } + return "" +} + +// MsgTransferDymNameOwnershipResponse defines the response for the name transfer. +type MsgTransferDymNameOwnershipResponse struct { +} + +func (m *MsgTransferDymNameOwnershipResponse) Reset() { *m = MsgTransferDymNameOwnershipResponse{} } +func (m *MsgTransferDymNameOwnershipResponse) String() string { return proto.CompactTextString(m) } +func (*MsgTransferDymNameOwnershipResponse) ProtoMessage() {} +func (*MsgTransferDymNameOwnershipResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_88dd2f81468013c2, []int{5} +} +func (m *MsgTransferDymNameOwnershipResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgTransferDymNameOwnershipResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgTransferDymNameOwnershipResponse.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 *MsgTransferDymNameOwnershipResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgTransferDymNameOwnershipResponse.Merge(m, src) +} +func (m *MsgTransferDymNameOwnershipResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgTransferDymNameOwnershipResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgTransferDymNameOwnershipResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgTransferDymNameOwnershipResponse proto.InternalMessageInfo + +// MsgSetController defines the message used for user to set a controller for a Dym-Name. +type MsgSetController struct { + // name is the Dym-Name to change controller. + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + // owner is the account address of the account which is currently owner of the Dym-Name. + Owner string `protobuf:"bytes,2,opt,name=owner,proto3" json:"owner,omitempty"` + // controller is the account address of the account which will be the new controller of the Dym-Name. + Controller string `protobuf:"bytes,3,opt,name=controller,proto3" json:"controller,omitempty"` +} + +func (m *MsgSetController) Reset() { *m = MsgSetController{} } +func (m *MsgSetController) String() string { return proto.CompactTextString(m) } +func (*MsgSetController) ProtoMessage() {} +func (*MsgSetController) Descriptor() ([]byte, []int) { + return fileDescriptor_88dd2f81468013c2, []int{6} +} +func (m *MsgSetController) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgSetController) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgSetController.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 *MsgSetController) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgSetController.Merge(m, src) +} +func (m *MsgSetController) XXX_Size() int { + return m.Size() +} +func (m *MsgSetController) XXX_DiscardUnknown() { + xxx_messageInfo_MsgSetController.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgSetController proto.InternalMessageInfo + +func (m *MsgSetController) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *MsgSetController) GetOwner() string { + if m != nil { + return m.Owner + } + return "" +} + +func (m *MsgSetController) GetController() string { + if m != nil { + return m.Controller + } + return "" +} + +// MsgSetControllerResponse defines the response for the name controller setting. +type MsgSetControllerResponse struct { +} + +func (m *MsgSetControllerResponse) Reset() { *m = MsgSetControllerResponse{} } +func (m *MsgSetControllerResponse) String() string { return proto.CompactTextString(m) } +func (*MsgSetControllerResponse) ProtoMessage() {} +func (*MsgSetControllerResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_88dd2f81468013c2, []int{7} +} +func (m *MsgSetControllerResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgSetControllerResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgSetControllerResponse.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 *MsgSetControllerResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgSetControllerResponse.Merge(m, src) +} +func (m *MsgSetControllerResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgSetControllerResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgSetControllerResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgSetControllerResponse proto.InternalMessageInfo + +// MsgUpdateResolveAddress defines the message used for user to update the resolve address of a Dym-Name. +type MsgUpdateResolveAddress struct { + // name is the Dym-Name to be updated by controller. + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + // controller is the account address of the account which has permission to update the Dym-Name. + Controller string `protobuf:"bytes,2,opt,name=controller,proto3" json:"controller,omitempty"` + // chain_id is an optional field, chain-based mapping + ChainId string `protobuf:"bytes,3,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` + // sub_name is an optional field, sub-domain-like mapping + SubName string `protobuf:"bytes,4,opt,name=sub_name,json=subName,proto3" json:"sub_name,omitempty"` + // resolve_to is the address that this config will resolve to. + // Leave it empty to remove the resolve address. + ResolveTo string `protobuf:"bytes,5,opt,name=resolve_to,json=resolveTo,proto3" json:"resolve_to,omitempty"` +} + +func (m *MsgUpdateResolveAddress) Reset() { *m = MsgUpdateResolveAddress{} } +func (m *MsgUpdateResolveAddress) String() string { return proto.CompactTextString(m) } +func (*MsgUpdateResolveAddress) ProtoMessage() {} +func (*MsgUpdateResolveAddress) Descriptor() ([]byte, []int) { + return fileDescriptor_88dd2f81468013c2, []int{8} +} +func (m *MsgUpdateResolveAddress) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgUpdateResolveAddress) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgUpdateResolveAddress.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 *MsgUpdateResolveAddress) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgUpdateResolveAddress.Merge(m, src) +} +func (m *MsgUpdateResolveAddress) XXX_Size() int { + return m.Size() +} +func (m *MsgUpdateResolveAddress) XXX_DiscardUnknown() { + xxx_messageInfo_MsgUpdateResolveAddress.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgUpdateResolveAddress proto.InternalMessageInfo + +func (m *MsgUpdateResolveAddress) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *MsgUpdateResolveAddress) GetController() string { + if m != nil { + return m.Controller + } + return "" +} + +func (m *MsgUpdateResolveAddress) GetChainId() string { + if m != nil { + return m.ChainId + } + return "" +} + +func (m *MsgUpdateResolveAddress) GetSubName() string { + if m != nil { + return m.SubName + } + return "" +} + +func (m *MsgUpdateResolveAddress) GetResolveTo() string { + if m != nil { + return m.ResolveTo + } + return "" +} + +// MsgUpdateResolveAddressResponse defines the response for the name resolve address update. +type MsgUpdateResolveAddressResponse struct { +} + +func (m *MsgUpdateResolveAddressResponse) Reset() { *m = MsgUpdateResolveAddressResponse{} } +func (m *MsgUpdateResolveAddressResponse) String() string { return proto.CompactTextString(m) } +func (*MsgUpdateResolveAddressResponse) ProtoMessage() {} +func (*MsgUpdateResolveAddressResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_88dd2f81468013c2, []int{9} +} +func (m *MsgUpdateResolveAddressResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgUpdateResolveAddressResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgUpdateResolveAddressResponse.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 *MsgUpdateResolveAddressResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgUpdateResolveAddressResponse.Merge(m, src) +} +func (m *MsgUpdateResolveAddressResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgUpdateResolveAddressResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgUpdateResolveAddressResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgUpdateResolveAddressResponse proto.InternalMessageInfo + +// MsgUpdateDetails defines the message used for user to update the details of a Dym-Name. +type MsgUpdateDetails struct { + // name is the Dym-Name to be updated details. + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + // controller is the bech32-encoded address of the account which has permission to update details of the Dym-Name. + Controller string `protobuf:"bytes,2,opt,name=controller,proto3" json:"controller,omitempty"` + // contact is an optional field, contact information of the Dym-Name. + Contact string `protobuf:"bytes,3,opt,name=contact,proto3" json:"contact,omitempty"` + // clear_configs is an optional field, set to true to clear the current configuration. + ClearConfigs bool `protobuf:"varint,4,opt,name=clear_configs,json=clearConfigs,proto3" json:"clear_configs,omitempty"` +} + +func (m *MsgUpdateDetails) Reset() { *m = MsgUpdateDetails{} } +func (m *MsgUpdateDetails) String() string { return proto.CompactTextString(m) } +func (*MsgUpdateDetails) ProtoMessage() {} +func (*MsgUpdateDetails) Descriptor() ([]byte, []int) { + return fileDescriptor_88dd2f81468013c2, []int{10} +} +func (m *MsgUpdateDetails) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgUpdateDetails) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgUpdateDetails.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 *MsgUpdateDetails) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgUpdateDetails.Merge(m, src) +} +func (m *MsgUpdateDetails) XXX_Size() int { + return m.Size() +} +func (m *MsgUpdateDetails) XXX_DiscardUnknown() { + xxx_messageInfo_MsgUpdateDetails.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgUpdateDetails proto.InternalMessageInfo + +func (m *MsgUpdateDetails) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *MsgUpdateDetails) GetController() string { + if m != nil { + return m.Controller + } + return "" +} + +func (m *MsgUpdateDetails) GetContact() string { + if m != nil { + return m.Contact + } + return "" +} + +func (m *MsgUpdateDetails) GetClearConfigs() bool { + if m != nil { + return m.ClearConfigs + } + return false +} + +// MsgUpdateDetailsResponse defines the response for the name details update. +type MsgUpdateDetailsResponse struct { +} + +func (m *MsgUpdateDetailsResponse) Reset() { *m = MsgUpdateDetailsResponse{} } +func (m *MsgUpdateDetailsResponse) String() string { return proto.CompactTextString(m) } +func (*MsgUpdateDetailsResponse) ProtoMessage() {} +func (*MsgUpdateDetailsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_88dd2f81468013c2, []int{11} +} +func (m *MsgUpdateDetailsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgUpdateDetailsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgUpdateDetailsResponse.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 *MsgUpdateDetailsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgUpdateDetailsResponse.Merge(m, src) +} +func (m *MsgUpdateDetailsResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgUpdateDetailsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgUpdateDetailsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgUpdateDetailsResponse proto.InternalMessageInfo + +// MsgPlaceSellOrder defines the message used for user to put a Dym-Name/Alias for sale. +type MsgPlaceSellOrder struct { + // asset_id is the Dym-Name/Alias to be opened for sell. + AssetId string `protobuf:"bytes,1,opt,name=asset_id,json=assetId,proto3" json:"asset_id,omitempty"` + // asset_type is the type of the asset of the order, is Dym-Name/Alias. + AssetType AssetType `protobuf:"varint,2,opt,name=asset_type,json=assetType,proto3,enum=dymensionxyz.dymension.dymns.AssetType" json:"asset_type,omitempty"` + // owner is the bech32-encoded address of the account which owns the order. + Owner string `protobuf:"bytes,3,opt,name=owner,proto3" json:"owner,omitempty"` + // min_price is the minimum price that buyer must pay for the Dym-Name. + MinPrice types.Coin `protobuf:"bytes,4,opt,name=min_price,json=minPrice,proto3" json:"min_price"` + // sell_price is the price that buyer must pay for the Dym-Name to immediately own it. + // Leaving this field empty/zero means + // the Dym-Name is not for immediate purchase and must wait until the Sell-Order expired. + SellPrice *types.Coin `protobuf:"bytes,5,opt,name=sell_price,json=sellPrice,proto3" json:"sell_price,omitempty"` +} + +func (m *MsgPlaceSellOrder) Reset() { *m = MsgPlaceSellOrder{} } +func (m *MsgPlaceSellOrder) String() string { return proto.CompactTextString(m) } +func (*MsgPlaceSellOrder) ProtoMessage() {} +func (*MsgPlaceSellOrder) Descriptor() ([]byte, []int) { + return fileDescriptor_88dd2f81468013c2, []int{12} +} +func (m *MsgPlaceSellOrder) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgPlaceSellOrder) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgPlaceSellOrder.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 *MsgPlaceSellOrder) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgPlaceSellOrder.Merge(m, src) +} +func (m *MsgPlaceSellOrder) XXX_Size() int { + return m.Size() +} +func (m *MsgPlaceSellOrder) XXX_DiscardUnknown() { + xxx_messageInfo_MsgPlaceSellOrder.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgPlaceSellOrder proto.InternalMessageInfo + +func (m *MsgPlaceSellOrder) GetAssetId() string { + if m != nil { + return m.AssetId + } + return "" +} + +func (m *MsgPlaceSellOrder) GetAssetType() AssetType { + if m != nil { + return m.AssetType + } + return AssetType_AT_UNKNOWN +} + +func (m *MsgPlaceSellOrder) GetOwner() string { + if m != nil { + return m.Owner + } + return "" +} + +func (m *MsgPlaceSellOrder) GetMinPrice() types.Coin { + if m != nil { + return m.MinPrice + } + return types.Coin{} +} + +func (m *MsgPlaceSellOrder) GetSellPrice() *types.Coin { + if m != nil { + return m.SellPrice + } + return nil +} + +// MsgPlaceSellOrderResponse defines the response after placed the Sell-Order. +type MsgPlaceSellOrderResponse struct { +} + +func (m *MsgPlaceSellOrderResponse) Reset() { *m = MsgPlaceSellOrderResponse{} } +func (m *MsgPlaceSellOrderResponse) String() string { return proto.CompactTextString(m) } +func (*MsgPlaceSellOrderResponse) ProtoMessage() {} +func (*MsgPlaceSellOrderResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_88dd2f81468013c2, []int{13} +} +func (m *MsgPlaceSellOrderResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgPlaceSellOrderResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgPlaceSellOrderResponse.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 *MsgPlaceSellOrderResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgPlaceSellOrderResponse.Merge(m, src) +} +func (m *MsgPlaceSellOrderResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgPlaceSellOrderResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgPlaceSellOrderResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgPlaceSellOrderResponse proto.InternalMessageInfo + +// MsgCancelSellOrder defines the message used for user to cancel a Sell-Order. +type MsgCancelSellOrder struct { + // asset_id is the Dym-Name/Alias to cancel selling. + AssetId string `protobuf:"bytes,1,opt,name=asset_id,json=assetId,proto3" json:"asset_id,omitempty"` + // asset_type is the type of the asset of the order, is Dym-Name/Alias. + AssetType AssetType `protobuf:"varint,2,opt,name=asset_type,json=assetType,proto3,enum=dymensionxyz.dymension.dymns.AssetType" json:"asset_type,omitempty"` + // owner is the bech32-encoded address of the account which owns the Dym-Name as well as the order. + Owner string `protobuf:"bytes,3,opt,name=owner,proto3" json:"owner,omitempty"` +} + +func (m *MsgCancelSellOrder) Reset() { *m = MsgCancelSellOrder{} } +func (m *MsgCancelSellOrder) String() string { return proto.CompactTextString(m) } +func (*MsgCancelSellOrder) ProtoMessage() {} +func (*MsgCancelSellOrder) Descriptor() ([]byte, []int) { + return fileDescriptor_88dd2f81468013c2, []int{14} +} +func (m *MsgCancelSellOrder) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgCancelSellOrder) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgCancelSellOrder.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 *MsgCancelSellOrder) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgCancelSellOrder.Merge(m, src) +} +func (m *MsgCancelSellOrder) XXX_Size() int { + return m.Size() +} +func (m *MsgCancelSellOrder) XXX_DiscardUnknown() { + xxx_messageInfo_MsgCancelSellOrder.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgCancelSellOrder proto.InternalMessageInfo + +func (m *MsgCancelSellOrder) GetAssetId() string { + if m != nil { + return m.AssetId + } + return "" +} + +func (m *MsgCancelSellOrder) GetAssetType() AssetType { + if m != nil { + return m.AssetType + } + return AssetType_AT_UNKNOWN +} + +func (m *MsgCancelSellOrder) GetOwner() string { + if m != nil { + return m.Owner + } + return "" +} + +// MsgCancelSellOrderResponse defines the response for the Sell-Order cancellation. +type MsgCancelSellOrderResponse struct { +} + +func (m *MsgCancelSellOrderResponse) Reset() { *m = MsgCancelSellOrderResponse{} } +func (m *MsgCancelSellOrderResponse) String() string { return proto.CompactTextString(m) } +func (*MsgCancelSellOrderResponse) ProtoMessage() {} +func (*MsgCancelSellOrderResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_88dd2f81468013c2, []int{15} +} +func (m *MsgCancelSellOrderResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgCancelSellOrderResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgCancelSellOrderResponse.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 *MsgCancelSellOrderResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgCancelSellOrderResponse.Merge(m, src) +} +func (m *MsgCancelSellOrderResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgCancelSellOrderResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgCancelSellOrderResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgCancelSellOrderResponse proto.InternalMessageInfo + +// MsgPurchaseOrder defines the message used for user to bid/purchase a Sell-Order. +type MsgPurchaseOrder struct { + // asset_id is the Dym-Name/Alias to be purchased for. + AssetId string `protobuf:"bytes,1,opt,name=asset_id,json=assetId,proto3" json:"asset_id,omitempty"` + // asset_type is the type of the asset of the order, is Dym-Name/Alias. + AssetType AssetType `protobuf:"varint,2,opt,name=asset_type,json=assetType,proto3,enum=dymensionxyz.dymension.dymns.AssetType" json:"asset_type,omitempty"` + // params is the list of parameters of the bid. + // It is empty for asset type Dym-Name. + // It has one element for asset type Alias, which is the rollapp_id to assigned for. + Params []string `protobuf:"bytes,3,rep,name=params,proto3" json:"params,omitempty"` + // buyer is the account address of the account which is purchasing the Dym-Name. + Buyer string `protobuf:"bytes,4,opt,name=buyer,proto3" json:"buyer,omitempty"` + // offer is the price that buyer is willing to pay for the Dym-Name. + Offer types.Coin `protobuf:"bytes,5,opt,name=offer,proto3" json:"offer"` +} + +func (m *MsgPurchaseOrder) Reset() { *m = MsgPurchaseOrder{} } +func (m *MsgPurchaseOrder) String() string { return proto.CompactTextString(m) } +func (*MsgPurchaseOrder) ProtoMessage() {} +func (*MsgPurchaseOrder) Descriptor() ([]byte, []int) { + return fileDescriptor_88dd2f81468013c2, []int{16} +} +func (m *MsgPurchaseOrder) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgPurchaseOrder) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgPurchaseOrder.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 *MsgPurchaseOrder) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgPurchaseOrder.Merge(m, src) +} +func (m *MsgPurchaseOrder) XXX_Size() int { + return m.Size() +} +func (m *MsgPurchaseOrder) XXX_DiscardUnknown() { + xxx_messageInfo_MsgPurchaseOrder.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgPurchaseOrder proto.InternalMessageInfo + +func (m *MsgPurchaseOrder) GetAssetId() string { + if m != nil { + return m.AssetId + } + return "" +} + +func (m *MsgPurchaseOrder) GetAssetType() AssetType { + if m != nil { + return m.AssetType + } + return AssetType_AT_UNKNOWN +} + +func (m *MsgPurchaseOrder) GetParams() []string { + if m != nil { + return m.Params + } + return nil +} + +func (m *MsgPurchaseOrder) GetBuyer() string { + if m != nil { + return m.Buyer + } + return "" +} + +func (m *MsgPurchaseOrder) GetOffer() types.Coin { + if m != nil { + return m.Offer + } + return types.Coin{} +} + +// MsgPurchaseOrderResponse defines the response for the purchase order. +type MsgPurchaseOrderResponse struct { +} + +func (m *MsgPurchaseOrderResponse) Reset() { *m = MsgPurchaseOrderResponse{} } +func (m *MsgPurchaseOrderResponse) String() string { return proto.CompactTextString(m) } +func (*MsgPurchaseOrderResponse) ProtoMessage() {} +func (*MsgPurchaseOrderResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_88dd2f81468013c2, []int{17} +} +func (m *MsgPurchaseOrderResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgPurchaseOrderResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgPurchaseOrderResponse.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 *MsgPurchaseOrderResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgPurchaseOrderResponse.Merge(m, src) +} +func (m *MsgPurchaseOrderResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgPurchaseOrderResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgPurchaseOrderResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgPurchaseOrderResponse proto.InternalMessageInfo + +// MsgPlaceBuyOrder defines the message used for user to place an offer, to buy a Dym-Name. +type MsgPlaceBuyOrder struct { + // asset_id is the Dym-Name/Alias wishing to buy. + AssetId string `protobuf:"bytes,1,opt,name=asset_id,json=assetId,proto3" json:"asset_id,omitempty"` + // asset_type is the type of the asset of the order, is Dym-Name/Alias. + AssetType AssetType `protobuf:"varint,2,opt,name=asset_type,json=assetType,proto3,enum=dymensionxyz.dymension.dymns.AssetType" json:"asset_type,omitempty"` + // params is the list of parameters of the offer. + // It is empty for asset type Dym-Name. + // It has one element for asset type Alias, which is the rollapp_id to assigned for. + Params []string `protobuf:"bytes,3,rep,name=params,proto3" json:"params,omitempty"` + // buyer is the account address of the account which is purchasing the Dym-Name. + Buyer string `protobuf:"bytes,4,opt,name=buyer,proto3" json:"buyer,omitempty"` + // continue_order_id is the optional field, if the buyer wants to extends an existing offer. + ContinueOrderId string `protobuf:"bytes,5,opt,name=continue_order_id,json=continueOrderId,proto3" json:"continue_order_id,omitempty"` + // offer is the price that buyer is willing to pay for the Dym-Name. + Offer types.Coin `protobuf:"bytes,6,opt,name=offer,proto3" json:"offer"` +} + +func (m *MsgPlaceBuyOrder) Reset() { *m = MsgPlaceBuyOrder{} } +func (m *MsgPlaceBuyOrder) String() string { return proto.CompactTextString(m) } +func (*MsgPlaceBuyOrder) ProtoMessage() {} +func (*MsgPlaceBuyOrder) Descriptor() ([]byte, []int) { + return fileDescriptor_88dd2f81468013c2, []int{18} +} +func (m *MsgPlaceBuyOrder) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgPlaceBuyOrder) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgPlaceBuyOrder.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 *MsgPlaceBuyOrder) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgPlaceBuyOrder.Merge(m, src) +} +func (m *MsgPlaceBuyOrder) XXX_Size() int { + return m.Size() +} +func (m *MsgPlaceBuyOrder) XXX_DiscardUnknown() { + xxx_messageInfo_MsgPlaceBuyOrder.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgPlaceBuyOrder proto.InternalMessageInfo + +func (m *MsgPlaceBuyOrder) GetAssetId() string { + if m != nil { + return m.AssetId + } + return "" +} + +func (m *MsgPlaceBuyOrder) GetAssetType() AssetType { + if m != nil { + return m.AssetType + } + return AssetType_AT_UNKNOWN +} + +func (m *MsgPlaceBuyOrder) GetParams() []string { + if m != nil { + return m.Params + } + return nil +} + +func (m *MsgPlaceBuyOrder) GetBuyer() string { + if m != nil { + return m.Buyer + } + return "" +} + +func (m *MsgPlaceBuyOrder) GetContinueOrderId() string { + if m != nil { + return m.ContinueOrderId + } + return "" +} + +func (m *MsgPlaceBuyOrder) GetOffer() types.Coin { + if m != nil { + return m.Offer + } + return types.Coin{} +} + +// MsgPlaceBuyOrderResponse defines the response after placed the Buy-Order. +type MsgPlaceBuyOrderResponse struct { + // order_id is the unique identifier of the new generated Buy-Order. + OrderId string `protobuf:"bytes,1,opt,name=order_id,json=orderId,proto3" json:"order_id,omitempty"` +} + +func (m *MsgPlaceBuyOrderResponse) Reset() { *m = MsgPlaceBuyOrderResponse{} } +func (m *MsgPlaceBuyOrderResponse) String() string { return proto.CompactTextString(m) } +func (*MsgPlaceBuyOrderResponse) ProtoMessage() {} +func (*MsgPlaceBuyOrderResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_88dd2f81468013c2, []int{19} +} +func (m *MsgPlaceBuyOrderResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgPlaceBuyOrderResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgPlaceBuyOrderResponse.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 *MsgPlaceBuyOrderResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgPlaceBuyOrderResponse.Merge(m, src) +} +func (m *MsgPlaceBuyOrderResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgPlaceBuyOrderResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgPlaceBuyOrderResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgPlaceBuyOrderResponse proto.InternalMessageInfo + +func (m *MsgPlaceBuyOrderResponse) GetOrderId() string { + if m != nil { + return m.OrderId + } + return "" +} + +// MsgCancelBuyOrder defines the message used for user to cancel a Buy-Order. +type MsgCancelBuyOrder struct { + // order_id is the unique identifier of the Buy-Order. + OrderId string `protobuf:"bytes,1,opt,name=order_id,json=orderId,proto3" json:"order_id,omitempty"` + // buyer is the account address of the account which is purchasing the Dym-Name. + Buyer string `protobuf:"bytes,2,opt,name=buyer,proto3" json:"buyer,omitempty"` +} + +func (m *MsgCancelBuyOrder) Reset() { *m = MsgCancelBuyOrder{} } +func (m *MsgCancelBuyOrder) String() string { return proto.CompactTextString(m) } +func (*MsgCancelBuyOrder) ProtoMessage() {} +func (*MsgCancelBuyOrder) Descriptor() ([]byte, []int) { + return fileDescriptor_88dd2f81468013c2, []int{20} +} +func (m *MsgCancelBuyOrder) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgCancelBuyOrder) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgCancelBuyOrder.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 *MsgCancelBuyOrder) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgCancelBuyOrder.Merge(m, src) +} +func (m *MsgCancelBuyOrder) XXX_Size() int { + return m.Size() +} +func (m *MsgCancelBuyOrder) XXX_DiscardUnknown() { + xxx_messageInfo_MsgCancelBuyOrder.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgCancelBuyOrder proto.InternalMessageInfo + +func (m *MsgCancelBuyOrder) GetOrderId() string { + if m != nil { + return m.OrderId + } + return "" +} + +func (m *MsgCancelBuyOrder) GetBuyer() string { + if m != nil { + return m.Buyer + } + return "" +} + +// MsgCancelBuyOrderResponse defines the response for the Buy-Order cancellation. +type MsgCancelBuyOrderResponse struct { +} + +func (m *MsgCancelBuyOrderResponse) Reset() { *m = MsgCancelBuyOrderResponse{} } +func (m *MsgCancelBuyOrderResponse) String() string { return proto.CompactTextString(m) } +func (*MsgCancelBuyOrderResponse) ProtoMessage() {} +func (*MsgCancelBuyOrderResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_88dd2f81468013c2, []int{21} +} +func (m *MsgCancelBuyOrderResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgCancelBuyOrderResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgCancelBuyOrderResponse.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 *MsgCancelBuyOrderResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgCancelBuyOrderResponse.Merge(m, src) +} +func (m *MsgCancelBuyOrderResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgCancelBuyOrderResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgCancelBuyOrderResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgCancelBuyOrderResponse proto.InternalMessageInfo + +// MsgAcceptBuyOrder defines the message used for user to accept a Buy-Order. +type MsgAcceptBuyOrder struct { + // order_id is the unique identifier of the Buy-Order. + OrderId string `protobuf:"bytes,1,opt,name=order_id,json=orderId,proto3" json:"order_id,omitempty"` + // owner is the account address of the account which owns the Dym-Name. + // And is the only one who can accept the offer. + Owner string `protobuf:"bytes,2,opt,name=owner,proto3" json:"owner,omitempty"` + // min_accept is the minimum price that the owner is willing to accept for the Dym-Name. + // If this amount matches the offer, the Dym-Name will be transferred to the buyer. + // If the offer is lower than this amount, this information will be updated into offer record to inform the buyer. + MinAccept types.Coin `protobuf:"bytes,3,opt,name=min_accept,json=minAccept,proto3" json:"min_accept"` +} + +func (m *MsgAcceptBuyOrder) Reset() { *m = MsgAcceptBuyOrder{} } +func (m *MsgAcceptBuyOrder) String() string { return proto.CompactTextString(m) } +func (*MsgAcceptBuyOrder) ProtoMessage() {} +func (*MsgAcceptBuyOrder) Descriptor() ([]byte, []int) { + return fileDescriptor_88dd2f81468013c2, []int{22} +} +func (m *MsgAcceptBuyOrder) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgAcceptBuyOrder) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgAcceptBuyOrder.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 *MsgAcceptBuyOrder) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgAcceptBuyOrder.Merge(m, src) +} +func (m *MsgAcceptBuyOrder) XXX_Size() int { + return m.Size() +} +func (m *MsgAcceptBuyOrder) XXX_DiscardUnknown() { + xxx_messageInfo_MsgAcceptBuyOrder.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgAcceptBuyOrder proto.InternalMessageInfo + +func (m *MsgAcceptBuyOrder) GetOrderId() string { + if m != nil { + return m.OrderId + } + return "" +} + +func (m *MsgAcceptBuyOrder) GetOwner() string { + if m != nil { + return m.Owner + } + return "" +} + +func (m *MsgAcceptBuyOrder) GetMinAccept() types.Coin { + if m != nil { + return m.MinAccept + } + return types.Coin{} +} + +// MsgAcceptBuyOrderResponse defines the response for the Buy-Order acceptance. +type MsgAcceptBuyOrderResponse struct { + // accepted is the flag to indicate if the offer is accepted (price matched). + Accepted bool `protobuf:"varint,1,opt,name=accepted,proto3" json:"accepted,omitempty"` +} + +func (m *MsgAcceptBuyOrderResponse) Reset() { *m = MsgAcceptBuyOrderResponse{} } +func (m *MsgAcceptBuyOrderResponse) String() string { return proto.CompactTextString(m) } +func (*MsgAcceptBuyOrderResponse) ProtoMessage() {} +func (*MsgAcceptBuyOrderResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_88dd2f81468013c2, []int{23} +} +func (m *MsgAcceptBuyOrderResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgAcceptBuyOrderResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgAcceptBuyOrderResponse.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 *MsgAcceptBuyOrderResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgAcceptBuyOrderResponse.Merge(m, src) +} +func (m *MsgAcceptBuyOrderResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgAcceptBuyOrderResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgAcceptBuyOrderResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgAcceptBuyOrderResponse proto.InternalMessageInfo + +func (m *MsgAcceptBuyOrderResponse) GetAccepted() bool { + if m != nil { + return m.Accepted + } + return false +} + +// MsgUpdateParams allows to update module params. +type MsgUpdateParams struct { + // authority is the address that controls the module. + Authority string `protobuf:"bytes,1,opt,name=authority,proto3" json:"authority,omitempty"` + // new_price_params is the optional update new price params if provided. + NewPriceParams *PriceParams `protobuf:"bytes,2,opt,name=new_price_params,json=newPriceParams,proto3" json:"new_price_params,omitempty"` + // new_chains_params is the optional update new chains params if provided. + // Not recommended, consider using a dedicated gov proposal for this instead of update params by this way. + NewChainsParams *ChainsParams `protobuf:"bytes,3,opt,name=new_chains_params,json=newChainsParams,proto3" json:"new_chains_params,omitempty"` + // new_misc_params is the optional update new misc params if provided. + NewMiscParams *MiscParams `protobuf:"bytes,4,opt,name=new_misc_params,json=newMiscParams,proto3" json:"new_misc_params,omitempty"` +} + +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_88dd2f81468013c2, []int{24} +} +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) GetNewPriceParams() *PriceParams { + if m != nil { + return m.NewPriceParams + } + return nil +} + +func (m *MsgUpdateParams) GetNewChainsParams() *ChainsParams { + if m != nil { + return m.NewChainsParams + } + return nil +} + +func (m *MsgUpdateParams) GetNewMiscParams() *MiscParams { + if m != nil { + return m.NewMiscParams + } + return nil +} + +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_88dd2f81468013c2, []int{25} +} +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((*MsgRegisterName)(nil), "dymensionxyz.dymension.dymns.MsgRegisterName") + proto.RegisterType((*MsgRegisterNameResponse)(nil), "dymensionxyz.dymension.dymns.MsgRegisterNameResponse") + proto.RegisterType((*MsgRegisterAlias)(nil), "dymensionxyz.dymension.dymns.MsgRegisterAlias") + proto.RegisterType((*MsgRegisterAliasResponse)(nil), "dymensionxyz.dymension.dymns.MsgRegisterAliasResponse") + proto.RegisterType((*MsgTransferDymNameOwnership)(nil), "dymensionxyz.dymension.dymns.MsgTransferDymNameOwnership") + proto.RegisterType((*MsgTransferDymNameOwnershipResponse)(nil), "dymensionxyz.dymension.dymns.MsgTransferDymNameOwnershipResponse") + proto.RegisterType((*MsgSetController)(nil), "dymensionxyz.dymension.dymns.MsgSetController") + proto.RegisterType((*MsgSetControllerResponse)(nil), "dymensionxyz.dymension.dymns.MsgSetControllerResponse") + proto.RegisterType((*MsgUpdateResolveAddress)(nil), "dymensionxyz.dymension.dymns.MsgUpdateResolveAddress") + proto.RegisterType((*MsgUpdateResolveAddressResponse)(nil), "dymensionxyz.dymension.dymns.MsgUpdateResolveAddressResponse") + proto.RegisterType((*MsgUpdateDetails)(nil), "dymensionxyz.dymension.dymns.MsgUpdateDetails") + proto.RegisterType((*MsgUpdateDetailsResponse)(nil), "dymensionxyz.dymension.dymns.MsgUpdateDetailsResponse") + proto.RegisterType((*MsgPlaceSellOrder)(nil), "dymensionxyz.dymension.dymns.MsgPlaceSellOrder") + proto.RegisterType((*MsgPlaceSellOrderResponse)(nil), "dymensionxyz.dymension.dymns.MsgPlaceSellOrderResponse") + proto.RegisterType((*MsgCancelSellOrder)(nil), "dymensionxyz.dymension.dymns.MsgCancelSellOrder") + proto.RegisterType((*MsgCancelSellOrderResponse)(nil), "dymensionxyz.dymension.dymns.MsgCancelSellOrderResponse") + proto.RegisterType((*MsgPurchaseOrder)(nil), "dymensionxyz.dymension.dymns.MsgPurchaseOrder") + proto.RegisterType((*MsgPurchaseOrderResponse)(nil), "dymensionxyz.dymension.dymns.MsgPurchaseOrderResponse") + proto.RegisterType((*MsgPlaceBuyOrder)(nil), "dymensionxyz.dymension.dymns.MsgPlaceBuyOrder") + proto.RegisterType((*MsgPlaceBuyOrderResponse)(nil), "dymensionxyz.dymension.dymns.MsgPlaceBuyOrderResponse") + proto.RegisterType((*MsgCancelBuyOrder)(nil), "dymensionxyz.dymension.dymns.MsgCancelBuyOrder") + proto.RegisterType((*MsgCancelBuyOrderResponse)(nil), "dymensionxyz.dymension.dymns.MsgCancelBuyOrderResponse") + proto.RegisterType((*MsgAcceptBuyOrder)(nil), "dymensionxyz.dymension.dymns.MsgAcceptBuyOrder") + proto.RegisterType((*MsgAcceptBuyOrderResponse)(nil), "dymensionxyz.dymension.dymns.MsgAcceptBuyOrderResponse") + proto.RegisterType((*MsgUpdateParams)(nil), "dymensionxyz.dymension.dymns.MsgUpdateParams") + proto.RegisterType((*MsgUpdateParamsResponse)(nil), "dymensionxyz.dymension.dymns.MsgUpdateParamsResponse") +} + +func init() { + proto.RegisterFile("dymensionxyz/dymension/dymns/tx.proto", fileDescriptor_88dd2f81468013c2) +} + +var fileDescriptor_88dd2f81468013c2 = []byte{ + // 1305 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x58, 0x4f, 0x6f, 0x1b, 0x45, + 0x14, 0xf7, 0xda, 0x49, 0x6b, 0x3f, 0x9a, 0xa4, 0x5d, 0x45, 0xd4, 0xd9, 0x16, 0x37, 0xb8, 0xaa, + 0x48, 0x2b, 0x75, 0x4d, 0x53, 0xa5, 0x2d, 0x15, 0x20, 0x25, 0xa9, 0x10, 0x91, 0x08, 0xb5, 0x36, + 0x85, 0x03, 0x97, 0xd5, 0x78, 0x3d, 0xd9, 0xac, 0xd8, 0x9d, 0x59, 0xed, 0xac, 0x93, 0x1a, 0x21, + 0x21, 0xc1, 0x1d, 0x55, 0x1c, 0x90, 0xe0, 0x53, 0x20, 0xc1, 0x77, 0xa0, 0xc7, 0x8a, 0x53, 0x4f, + 0x08, 0xb5, 0x12, 0x7c, 0x0d, 0x34, 0x7f, 0xbc, 0xde, 0x71, 0xfd, 0x67, 0x1d, 0x21, 0xe0, 0xe4, + 0x7d, 0x33, 0xef, 0xcf, 0xef, 0xfd, 0xe6, 0xbd, 0x99, 0x27, 0xc3, 0xb5, 0x6e, 0x3f, 0xc2, 0x84, + 0x05, 0x94, 0x3c, 0xee, 0x7f, 0xd1, 0xca, 0x04, 0xfe, 0x45, 0x58, 0x2b, 0x7d, 0x6c, 0xc7, 0x09, + 0x4d, 0xa9, 0x79, 0x39, 0xaf, 0x66, 0x67, 0x82, 0x2d, 0xd4, 0xac, 0x55, 0x9f, 0xfa, 0x54, 0x28, + 0xb6, 0xf8, 0x97, 0xb4, 0xb1, 0x1a, 0x1e, 0x65, 0x11, 0x65, 0xad, 0x0e, 0x62, 0xb8, 0x75, 0x7c, + 0xab, 0x83, 0x53, 0x74, 0xab, 0xe5, 0xd1, 0x80, 0xa8, 0xfd, 0x8b, 0x6a, 0x3f, 0x62, 0x7e, 0xeb, + 0xf8, 0x16, 0xff, 0x51, 0x1b, 0x6b, 0x72, 0xc3, 0x95, 0x1e, 0xa5, 0xa0, 0xb6, 0xae, 0x4f, 0x85, + 0x1b, 0xa1, 0xe4, 0x73, 0x9c, 0x16, 0x52, 0x8d, 0x51, 0x82, 0x22, 0xe5, 0xb5, 0xf9, 0xab, 0x01, + 0x2b, 0xfb, 0xcc, 0x77, 0xb0, 0x1f, 0xb0, 0x14, 0x27, 0x1f, 0xa3, 0x08, 0x9b, 0x26, 0x2c, 0x10, + 0x14, 0xe1, 0xba, 0xb1, 0x6e, 0x6c, 0xd4, 0x1c, 0xf1, 0x6d, 0xae, 0xc2, 0x22, 0x3d, 0x21, 0x38, + 0xa9, 0x97, 0xc5, 0xa2, 0x14, 0x4c, 0x0b, 0xaa, 0xdd, 0x5e, 0x82, 0xd2, 0x80, 0x92, 0x7a, 0x65, + 0xdd, 0xd8, 0xa8, 0x38, 0x99, 0x6c, 0x7e, 0x08, 0x2b, 0x1e, 0x25, 0x87, 0x41, 0x12, 0xb9, 0x31, + 0xe2, 0x10, 0xd2, 0xfa, 0xc2, 0xba, 0xb1, 0xf1, 0xda, 0xe6, 0x9a, 0xad, 0xf2, 0xe2, 0xec, 0xd8, + 0x8a, 0x1d, 0x7b, 0x97, 0x06, 0x64, 0x67, 0xe1, 0xe9, 0xef, 0x57, 0x4a, 0xce, 0xb2, 0xb2, 0x6b, + 0x4b, 0x33, 0xb3, 0x0e, 0x67, 0x3d, 0x4a, 0x52, 0xe4, 0xa5, 0xf5, 0x45, 0x11, 0x7d, 0x20, 0xde, + 0x87, 0xaf, 0xff, 0xfa, 0xe9, 0x86, 0xc4, 0xd2, 0x5c, 0x83, 0x8b, 0x23, 0x89, 0x38, 0x98, 0xc5, + 0x94, 0x30, 0xdc, 0xfc, 0xd9, 0x80, 0xf3, 0xb9, 0xbd, 0xed, 0x30, 0x40, 0x8c, 0x67, 0x84, 0xf8, + 0x87, 0x4a, 0x53, 0x0a, 0xe6, 0x1b, 0x00, 0x09, 0x0d, 0x43, 0x14, 0xc7, 0x6e, 0xd0, 0x55, 0xc9, + 0xd6, 0xd4, 0xca, 0x5e, 0x77, 0x48, 0x43, 0x25, 0x4f, 0xc3, 0x3f, 0x96, 0xaa, 0x96, 0x90, 0x05, + 0xf5, 0x51, 0xd0, 0x59, 0x46, 0x31, 0x5c, 0xda, 0x67, 0xfe, 0xa3, 0x04, 0x11, 0x76, 0x88, 0x93, + 0x07, 0xfd, 0x88, 0xe7, 0xfb, 0x90, 0x9b, 0xb1, 0xa3, 0x20, 0x9e, 0xe3, 0x04, 0x2f, 0x41, 0x8d, + 0xe0, 0x13, 0x37, 0x9f, 0x54, 0x95, 0xe0, 0x13, 0xe1, 0x4a, 0x43, 0x73, 0x0d, 0xae, 0x4e, 0x89, + 0x98, 0x01, 0x3b, 0x12, 0x4c, 0x1f, 0xe0, 0x74, 0x97, 0x92, 0x94, 0xf3, 0x86, 0x93, 0x39, 0xd0, + 0x34, 0x00, 0xbc, 0xcc, 0x4e, 0xc1, 0xc9, 0xad, 0x8c, 0xa1, 0x47, 0x8b, 0x94, 0x3f, 0x70, 0x5e, + 0x0c, 0x9f, 0xc4, 0x5d, 0x94, 0xf2, 0x32, 0xa0, 0xe1, 0x31, 0xde, 0xee, 0x76, 0x13, 0xcc, 0xd8, + 0x58, 0x34, 0x7a, 0xdc, 0xf2, 0x68, 0x5c, 0x73, 0x0d, 0xaa, 0xde, 0x11, 0x0a, 0x08, 0xaf, 0x89, + 0x8a, 0x2a, 0x41, 0x2e, 0xef, 0x75, 0xf9, 0x16, 0xeb, 0x75, 0x5c, 0xe1, 0x72, 0x41, 0x6e, 0xb1, + 0x5e, 0x47, 0xf4, 0x11, 0xaf, 0x25, 0x19, 0xdb, 0x4d, 0xa9, 0x2a, 0xdd, 0x9a, 0x5a, 0x79, 0x44, + 0xef, 0xaf, 0xf0, 0x64, 0x72, 0x51, 0x9a, 0x6f, 0xc2, 0x95, 0x09, 0xa0, 0xb3, 0xc4, 0xbe, 0x97, + 0x95, 0x2c, 0x75, 0x1e, 0xe0, 0x14, 0x05, 0xe1, 0xe9, 0x32, 0xca, 0xf5, 0x54, 0x45, 0xeb, 0x29, + 0xf3, 0x2a, 0x2c, 0x79, 0x21, 0x46, 0x89, 0x2b, 0x4a, 0xd3, 0x67, 0x22, 0xab, 0xaa, 0x73, 0x4e, + 0x2c, 0xee, 0xca, 0xb5, 0x57, 0xb1, 0xcb, 0xd3, 0xd0, 0x70, 0x65, 0xa0, 0x9f, 0x94, 0xe1, 0xc2, + 0x3e, 0xf3, 0xdb, 0x21, 0xf2, 0xf0, 0x01, 0x0e, 0xc3, 0x87, 0x49, 0x57, 0x72, 0x8a, 0x18, 0xc3, + 0x29, 0xe7, 0x54, 0x22, 0x3f, 0x2b, 0xe4, 0xbd, 0xae, 0xf9, 0x01, 0x80, 0xdc, 0x4a, 0xfb, 0x31, + 0x16, 0xe0, 0x97, 0x37, 0xdf, 0xb2, 0xa7, 0xdd, 0xc3, 0xf6, 0x36, 0xd7, 0x7f, 0xd4, 0x8f, 0xb1, + 0x53, 0x43, 0x83, 0xcf, 0x09, 0xdd, 0xfa, 0x2e, 0xd4, 0xa2, 0x80, 0xb8, 0x71, 0x12, 0x78, 0xb8, + 0x68, 0x9f, 0x56, 0xa3, 0x80, 0xb4, 0xb9, 0x81, 0x79, 0x0f, 0x80, 0xe1, 0x30, 0x54, 0xe6, 0x8b, + 0x33, 0xcc, 0x9d, 0x1a, 0x57, 0x16, 0x96, 0x5a, 0xf1, 0x5e, 0x82, 0xb5, 0x57, 0x18, 0xc9, 0xf8, + 0xfa, 0xc1, 0x00, 0x73, 0x9f, 0xf9, 0xbb, 0x88, 0x78, 0x38, 0xfc, 0xef, 0x09, 0xd3, 0x80, 0x5f, + 0x06, 0xeb, 0x55, 0x68, 0x19, 0xf2, 0x3f, 0x65, 0x79, 0xb6, 0x7b, 0x89, 0x77, 0x84, 0x18, 0xfe, + 0xd7, 0x70, 0xbf, 0x0e, 0x67, 0xe4, 0xab, 0x56, 0xaf, 0xac, 0x57, 0x36, 0x6a, 0x8e, 0x92, 0x78, + 0x3e, 0x9d, 0x5e, 0x1f, 0x27, 0xaa, 0x33, 0xa5, 0x60, 0x6e, 0xc1, 0x22, 0x3d, 0x3c, 0xc4, 0xc9, + 0xcc, 0xd3, 0x53, 0x87, 0x2f, 0xb5, 0x15, 0x0d, 0xc2, 0x85, 0x2a, 0x77, 0x2d, 0xcf, 0x8c, 0x84, + 0xef, 0xca, 0x92, 0x04, 0x7e, 0xb8, 0x3b, 0xbd, 0xfe, 0xff, 0x94, 0x84, 0x1b, 0x70, 0x81, 0xb7, + 0x6f, 0x40, 0x7a, 0xd8, 0xa5, 0x1c, 0x22, 0x47, 0x26, 0xef, 0xa8, 0x95, 0xc1, 0x86, 0x80, 0xbe, + 0xd7, 0x1d, 0x12, 0x76, 0xe6, 0xd4, 0x84, 0x6d, 0x49, 0xc2, 0xf2, 0x9c, 0x0c, 0x08, 0xe3, 0xdc, + 0x64, 0x08, 0x14, 0x37, 0x54, 0x46, 0x6e, 0xb6, 0xc5, 0xcd, 0x21, 0xcb, 0x2d, 0xcf, 0xe5, 0x04, + 0xfd, 0x61, 0xae, 0xe5, 0x5c, 0xae, 0x1a, 0x10, 0xd9, 0x79, 0xba, 0xc7, 0xe1, 0x4d, 0x65, 0x88, + 0x78, 0xdb, 0x9e, 0x87, 0xe3, 0xb4, 0x60, 0xbc, 0x31, 0xcf, 0xd8, 0xfb, 0x00, 0xfc, 0x86, 0x41, + 0xc2, 0x8d, 0xe8, 0xa5, 0x02, 0xa4, 0xf1, 0x4b, 0x49, 0x06, 0xd6, 0x1a, 0xee, 0xae, 0xc0, 0xab, + 0x23, 0xca, 0x98, 0xb3, 0xa0, 0x2a, 0x83, 0x60, 0x89, 0xac, 0xea, 0x64, 0x72, 0xf3, 0x79, 0x59, + 0x4c, 0x76, 0xf2, 0x4a, 0x6e, 0xcb, 0x52, 0xb8, 0x03, 0x35, 0xd4, 0x4b, 0x8f, 0x68, 0x12, 0xa4, + 0x7d, 0x99, 0xca, 0x4e, 0xfd, 0xb7, 0x5f, 0x6e, 0xae, 0x2a, 0x68, 0xea, 0xb5, 0x39, 0x48, 0x93, + 0x80, 0xf8, 0xce, 0x50, 0xd5, 0x3c, 0x80, 0xf3, 0x7c, 0x4a, 0x10, 0x77, 0x9e, 0xab, 0x8a, 0xac, + 0x2c, 0xd2, 0xba, 0x3e, 0xbd, 0x50, 0xc5, 0xcd, 0x27, 0x83, 0x3b, 0xcb, 0x04, 0x9f, 0xe4, 0x64, + 0xf3, 0x53, 0xb8, 0xc0, 0x9d, 0x8a, 0x87, 0x94, 0xb9, 0x59, 0xe9, 0x72, 0xaf, 0x37, 0xa6, 0x7b, + 0xdd, 0x15, 0x26, 0xca, 0xed, 0x0a, 0xc1, 0x27, 0xf9, 0x05, 0xb3, 0x0d, 0x7c, 0xc9, 0x8d, 0x02, + 0xe6, 0x0d, 0xbc, 0xca, 0x5b, 0x7e, 0x63, 0xba, 0xd7, 0xfd, 0x80, 0x79, 0xca, 0xe7, 0x12, 0xc1, + 0x27, 0x43, 0xf1, 0xfe, 0x32, 0x3f, 0x8f, 0x21, 0x1d, 0x6a, 0xd4, 0xcc, 0x33, 0x3b, 0x38, 0x91, + 0xcd, 0x6f, 0x96, 0xa0, 0xb2, 0xcf, 0x7c, 0xf3, 0x18, 0xce, 0x69, 0x33, 0xf5, 0xcd, 0x19, 0xb1, + 0xf5, 0xc9, 0xd5, 0xda, 0x9a, 0x4b, 0x3d, 0xab, 0xdf, 0x92, 0xd9, 0x87, 0x25, 0x7d, 0xcc, 0xb5, + 0x0b, 0x7b, 0x12, 0xfa, 0xd6, 0x9d, 0xf9, 0xf4, 0x73, 0xa1, 0x7f, 0x34, 0xa0, 0x3e, 0x71, 0x22, + 0x7d, 0x67, 0xa6, 0xdb, 0x49, 0xa6, 0xd6, 0xf6, 0xa9, 0x4d, 0x75, 0x5e, 0xf4, 0xa1, 0x74, 0x36, + 0x2f, 0x9a, 0x7e, 0x01, 0x5e, 0xc6, 0x8f, 0xa2, 0x25, 0xf3, 0x5b, 0x03, 0x56, 0xc7, 0x4e, 0xa2, + 0xb3, 0x0f, 0x79, 0x9c, 0x99, 0xf5, 0xde, 0xa9, 0xcc, 0x74, 0x2e, 0xf4, 0x01, 0xd2, 0x2e, 0xe8, + 0x51, 0xe9, 0x17, 0xe0, 0x62, 0xfc, 0x20, 0x58, 0x32, 0xbf, 0x84, 0xe5, 0x91, 0x31, 0xb0, 0x35, + 0xd3, 0x97, 0x6e, 0x60, 0xdd, 0x9d, 0xd3, 0x20, 0x17, 0xfd, 0x2b, 0x58, 0x19, 0x1d, 0xaa, 0xde, + 0x9e, 0xe9, 0x6d, 0xc4, 0xc2, 0xba, 0x37, 0xaf, 0x85, 0xce, 0xbc, 0x3e, 0x1b, 0xcd, 0x66, 0x5e, + 0xd3, 0x2f, 0xc0, 0xfc, 0xf8, 0x99, 0x44, 0x86, 0xd6, 0x26, 0x12, 0xbb, 0x18, 0x8f, 0x03, 0xfd, + 0x22, 0xa1, 0xc7, 0xbd, 0xee, 0xf2, 0xd0, 0x47, 0x5e, 0xf0, 0x56, 0x41, 0x0e, 0xb3, 0xe0, 0x77, + 0xe7, 0x34, 0xd0, 0xa3, 0x8f, 0xbc, 0xe7, 0xb3, 0xa3, 0xeb, 0x06, 0x05, 0xa2, 0x8f, 0x7f, 0x9f, + 0x9b, 0x25, 0x33, 0x85, 0x73, 0xda, 0x0b, 0x7c, 0xb3, 0x60, 0xeb, 0x48, 0x75, 0x6b, 0x6b, 0x2e, + 0xf5, 0x41, 0xdc, 0x9d, 0x8f, 0x9e, 0xbe, 0x68, 0x18, 0xcf, 0x5e, 0x34, 0x8c, 0x3f, 0x5e, 0x34, + 0x8c, 0x27, 0x2f, 0x1b, 0xa5, 0x67, 0x2f, 0x1b, 0xa5, 0xe7, 0x2f, 0x1b, 0xa5, 0xcf, 0x36, 0xfd, + 0x20, 0x3d, 0xea, 0x75, 0x6c, 0x8f, 0x46, 0xad, 0x09, 0xff, 0x12, 0x1d, 0xdf, 0x6e, 0x3d, 0x1e, + 0xfc, 0x09, 0xd6, 0x8f, 0x31, 0xeb, 0x9c, 0x11, 0x7f, 0x15, 0xdd, 0xfe, 0x3b, 0x00, 0x00, 0xff, + 0xff, 0x59, 0x79, 0xe6, 0x28, 0x31, 0x13, 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 { + // RegisterName is message handler, handles registration of a new Dym-Name + // or extends the ownership duration of an existing Dym-Name. + RegisterName(ctx context.Context, in *MsgRegisterName, opts ...grpc.CallOption) (*MsgRegisterNameResponse, error) + // RegisterAlias is message handler, handles registration of a new Alias for an existing RollApp. + RegisterAlias(ctx context.Context, in *MsgRegisterAlias, opts ...grpc.CallOption) (*MsgRegisterAliasResponse, error) + // TransferDymNameOwnership is message handler, + // handles transfer of ownership of a Dym-Name, performed by the owner. + TransferDymNameOwnership(ctx context.Context, in *MsgTransferDymNameOwnership, opts ...grpc.CallOption) (*MsgTransferDymNameOwnershipResponse, error) + // SetController is message handler, + // handles setting a controller for a Dym-Name, performed by the owner. + SetController(ctx context.Context, in *MsgSetController, opts ...grpc.CallOption) (*MsgSetControllerResponse, error) + // UpdateResolveAddress is message handler, + // handles updating Dym-Name-Address resolution configuration, performed by the controller. + UpdateResolveAddress(ctx context.Context, in *MsgUpdateResolveAddress, opts ...grpc.CallOption) (*MsgUpdateResolveAddressResponse, error) + // UpdateDetails is message handler, + // handles updating Dym-Name details, performed by the controller. + UpdateDetails(ctx context.Context, in *MsgUpdateDetails, opts ...grpc.CallOption) (*MsgUpdateDetailsResponse, error) + // PlaceSellOrder is message handler, + // handles creating a Sell-Order that advertise a Dym-Name/Alias is for sale, performed by the owner. + PlaceSellOrder(ctx context.Context, in *MsgPlaceSellOrder, opts ...grpc.CallOption) (*MsgPlaceSellOrderResponse, error) + // CancelSellOrder is message handler, + // handles canceling Sell-Order, performed by the owner. + // This will stop the advertisement and remove the Dym-Name/Alias sale from the market. + // Can only be performed if no one has placed a bid on the asset. + CancelSellOrder(ctx context.Context, in *MsgCancelSellOrder, opts ...grpc.CallOption) (*MsgCancelSellOrderResponse, error) + // PurchaseOrder is message handler, + // handles purchasing a Dym-Name/Alias from a Sell-Order, performed by the buyer. + PurchaseOrder(ctx context.Context, in *MsgPurchaseOrder, opts ...grpc.CallOption) (*MsgPurchaseOrderResponse, error) + // PlaceBuyOrder is message handler, + // handles creating an offer to buy a Dym-Name/Alias, performed by the buyer. + PlaceBuyOrder(ctx context.Context, in *MsgPlaceBuyOrder, opts ...grpc.CallOption) (*MsgPlaceBuyOrderResponse, error) + // CancelBuyOrder is message handler, + // handles canceling a Buy-Order, performed by the buyer who placed the offer. + CancelBuyOrder(ctx context.Context, in *MsgCancelBuyOrder, opts ...grpc.CallOption) (*MsgCancelBuyOrderResponse, error) + // AcceptBuyOrder is message handler, + // handles accepting a Buy-Order or raising the amount for negotiation, + // performed by the owner of the asset. + AcceptBuyOrder(ctx context.Context, in *MsgAcceptBuyOrder, opts ...grpc.CallOption) (*MsgAcceptBuyOrderResponse, error) + // UpdateParams is used for updating module params. + 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) RegisterName(ctx context.Context, in *MsgRegisterName, opts ...grpc.CallOption) (*MsgRegisterNameResponse, error) { + out := new(MsgRegisterNameResponse) + err := c.cc.Invoke(ctx, "/dymensionxyz.dymension.dymns.Msg/RegisterName", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) RegisterAlias(ctx context.Context, in *MsgRegisterAlias, opts ...grpc.CallOption) (*MsgRegisterAliasResponse, error) { + out := new(MsgRegisterAliasResponse) + err := c.cc.Invoke(ctx, "/dymensionxyz.dymension.dymns.Msg/RegisterAlias", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) TransferDymNameOwnership(ctx context.Context, in *MsgTransferDymNameOwnership, opts ...grpc.CallOption) (*MsgTransferDymNameOwnershipResponse, error) { + out := new(MsgTransferDymNameOwnershipResponse) + err := c.cc.Invoke(ctx, "/dymensionxyz.dymension.dymns.Msg/TransferDymNameOwnership", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) SetController(ctx context.Context, in *MsgSetController, opts ...grpc.CallOption) (*MsgSetControllerResponse, error) { + out := new(MsgSetControllerResponse) + err := c.cc.Invoke(ctx, "/dymensionxyz.dymension.dymns.Msg/SetController", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) UpdateResolveAddress(ctx context.Context, in *MsgUpdateResolveAddress, opts ...grpc.CallOption) (*MsgUpdateResolveAddressResponse, error) { + out := new(MsgUpdateResolveAddressResponse) + err := c.cc.Invoke(ctx, "/dymensionxyz.dymension.dymns.Msg/UpdateResolveAddress", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) UpdateDetails(ctx context.Context, in *MsgUpdateDetails, opts ...grpc.CallOption) (*MsgUpdateDetailsResponse, error) { + out := new(MsgUpdateDetailsResponse) + err := c.cc.Invoke(ctx, "/dymensionxyz.dymension.dymns.Msg/UpdateDetails", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) PlaceSellOrder(ctx context.Context, in *MsgPlaceSellOrder, opts ...grpc.CallOption) (*MsgPlaceSellOrderResponse, error) { + out := new(MsgPlaceSellOrderResponse) + err := c.cc.Invoke(ctx, "/dymensionxyz.dymension.dymns.Msg/PlaceSellOrder", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) CancelSellOrder(ctx context.Context, in *MsgCancelSellOrder, opts ...grpc.CallOption) (*MsgCancelSellOrderResponse, error) { + out := new(MsgCancelSellOrderResponse) + err := c.cc.Invoke(ctx, "/dymensionxyz.dymension.dymns.Msg/CancelSellOrder", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) PurchaseOrder(ctx context.Context, in *MsgPurchaseOrder, opts ...grpc.CallOption) (*MsgPurchaseOrderResponse, error) { + out := new(MsgPurchaseOrderResponse) + err := c.cc.Invoke(ctx, "/dymensionxyz.dymension.dymns.Msg/PurchaseOrder", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) PlaceBuyOrder(ctx context.Context, in *MsgPlaceBuyOrder, opts ...grpc.CallOption) (*MsgPlaceBuyOrderResponse, error) { + out := new(MsgPlaceBuyOrderResponse) + err := c.cc.Invoke(ctx, "/dymensionxyz.dymension.dymns.Msg/PlaceBuyOrder", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) CancelBuyOrder(ctx context.Context, in *MsgCancelBuyOrder, opts ...grpc.CallOption) (*MsgCancelBuyOrderResponse, error) { + out := new(MsgCancelBuyOrderResponse) + err := c.cc.Invoke(ctx, "/dymensionxyz.dymension.dymns.Msg/CancelBuyOrder", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) AcceptBuyOrder(ctx context.Context, in *MsgAcceptBuyOrder, opts ...grpc.CallOption) (*MsgAcceptBuyOrderResponse, error) { + out := new(MsgAcceptBuyOrderResponse) + err := c.cc.Invoke(ctx, "/dymensionxyz.dymension.dymns.Msg/AcceptBuyOrder", 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, "/dymensionxyz.dymension.dymns.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 { + // RegisterName is message handler, handles registration of a new Dym-Name + // or extends the ownership duration of an existing Dym-Name. + RegisterName(context.Context, *MsgRegisterName) (*MsgRegisterNameResponse, error) + // RegisterAlias is message handler, handles registration of a new Alias for an existing RollApp. + RegisterAlias(context.Context, *MsgRegisterAlias) (*MsgRegisterAliasResponse, error) + // TransferDymNameOwnership is message handler, + // handles transfer of ownership of a Dym-Name, performed by the owner. + TransferDymNameOwnership(context.Context, *MsgTransferDymNameOwnership) (*MsgTransferDymNameOwnershipResponse, error) + // SetController is message handler, + // handles setting a controller for a Dym-Name, performed by the owner. + SetController(context.Context, *MsgSetController) (*MsgSetControllerResponse, error) + // UpdateResolveAddress is message handler, + // handles updating Dym-Name-Address resolution configuration, performed by the controller. + UpdateResolveAddress(context.Context, *MsgUpdateResolveAddress) (*MsgUpdateResolveAddressResponse, error) + // UpdateDetails is message handler, + // handles updating Dym-Name details, performed by the controller. + UpdateDetails(context.Context, *MsgUpdateDetails) (*MsgUpdateDetailsResponse, error) + // PlaceSellOrder is message handler, + // handles creating a Sell-Order that advertise a Dym-Name/Alias is for sale, performed by the owner. + PlaceSellOrder(context.Context, *MsgPlaceSellOrder) (*MsgPlaceSellOrderResponse, error) + // CancelSellOrder is message handler, + // handles canceling Sell-Order, performed by the owner. + // This will stop the advertisement and remove the Dym-Name/Alias sale from the market. + // Can only be performed if no one has placed a bid on the asset. + CancelSellOrder(context.Context, *MsgCancelSellOrder) (*MsgCancelSellOrderResponse, error) + // PurchaseOrder is message handler, + // handles purchasing a Dym-Name/Alias from a Sell-Order, performed by the buyer. + PurchaseOrder(context.Context, *MsgPurchaseOrder) (*MsgPurchaseOrderResponse, error) + // PlaceBuyOrder is message handler, + // handles creating an offer to buy a Dym-Name/Alias, performed by the buyer. + PlaceBuyOrder(context.Context, *MsgPlaceBuyOrder) (*MsgPlaceBuyOrderResponse, error) + // CancelBuyOrder is message handler, + // handles canceling a Buy-Order, performed by the buyer who placed the offer. + CancelBuyOrder(context.Context, *MsgCancelBuyOrder) (*MsgCancelBuyOrderResponse, error) + // AcceptBuyOrder is message handler, + // handles accepting a Buy-Order or raising the amount for negotiation, + // performed by the owner of the asset. + AcceptBuyOrder(context.Context, *MsgAcceptBuyOrder) (*MsgAcceptBuyOrderResponse, error) + // UpdateParams is used for updating module params. + UpdateParams(context.Context, *MsgUpdateParams) (*MsgUpdateParamsResponse, error) +} + +// UnimplementedMsgServer can be embedded to have forward compatible implementations. +type UnimplementedMsgServer struct { +} + +func (*UnimplementedMsgServer) RegisterName(ctx context.Context, req *MsgRegisterName) (*MsgRegisterNameResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method RegisterName not implemented") +} +func (*UnimplementedMsgServer) RegisterAlias(ctx context.Context, req *MsgRegisterAlias) (*MsgRegisterAliasResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method RegisterAlias not implemented") +} +func (*UnimplementedMsgServer) TransferDymNameOwnership(ctx context.Context, req *MsgTransferDymNameOwnership) (*MsgTransferDymNameOwnershipResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method TransferDymNameOwnership not implemented") +} +func (*UnimplementedMsgServer) SetController(ctx context.Context, req *MsgSetController) (*MsgSetControllerResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method SetController not implemented") +} +func (*UnimplementedMsgServer) UpdateResolveAddress(ctx context.Context, req *MsgUpdateResolveAddress) (*MsgUpdateResolveAddressResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method UpdateResolveAddress not implemented") +} +func (*UnimplementedMsgServer) UpdateDetails(ctx context.Context, req *MsgUpdateDetails) (*MsgUpdateDetailsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method UpdateDetails not implemented") +} +func (*UnimplementedMsgServer) PlaceSellOrder(ctx context.Context, req *MsgPlaceSellOrder) (*MsgPlaceSellOrderResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method PlaceSellOrder not implemented") +} +func (*UnimplementedMsgServer) CancelSellOrder(ctx context.Context, req *MsgCancelSellOrder) (*MsgCancelSellOrderResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method CancelSellOrder not implemented") +} +func (*UnimplementedMsgServer) PurchaseOrder(ctx context.Context, req *MsgPurchaseOrder) (*MsgPurchaseOrderResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method PurchaseOrder not implemented") +} +func (*UnimplementedMsgServer) PlaceBuyOrder(ctx context.Context, req *MsgPlaceBuyOrder) (*MsgPlaceBuyOrderResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method PlaceBuyOrder not implemented") +} +func (*UnimplementedMsgServer) CancelBuyOrder(ctx context.Context, req *MsgCancelBuyOrder) (*MsgCancelBuyOrderResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method CancelBuyOrder not implemented") +} +func (*UnimplementedMsgServer) AcceptBuyOrder(ctx context.Context, req *MsgAcceptBuyOrder) (*MsgAcceptBuyOrderResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method AcceptBuyOrder 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_RegisterName_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgRegisterName) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).RegisterName(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/dymensionxyz.dymension.dymns.Msg/RegisterName", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).RegisterName(ctx, req.(*MsgRegisterName)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_RegisterAlias_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgRegisterAlias) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).RegisterAlias(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/dymensionxyz.dymension.dymns.Msg/RegisterAlias", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).RegisterAlias(ctx, req.(*MsgRegisterAlias)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_TransferDymNameOwnership_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgTransferDymNameOwnership) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).TransferDymNameOwnership(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/dymensionxyz.dymension.dymns.Msg/TransferDymNameOwnership", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).TransferDymNameOwnership(ctx, req.(*MsgTransferDymNameOwnership)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_SetController_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgSetController) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).SetController(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/dymensionxyz.dymension.dymns.Msg/SetController", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).SetController(ctx, req.(*MsgSetController)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_UpdateResolveAddress_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgUpdateResolveAddress) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).UpdateResolveAddress(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/dymensionxyz.dymension.dymns.Msg/UpdateResolveAddress", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).UpdateResolveAddress(ctx, req.(*MsgUpdateResolveAddress)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_UpdateDetails_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgUpdateDetails) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).UpdateDetails(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/dymensionxyz.dymension.dymns.Msg/UpdateDetails", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).UpdateDetails(ctx, req.(*MsgUpdateDetails)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_PlaceSellOrder_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgPlaceSellOrder) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).PlaceSellOrder(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/dymensionxyz.dymension.dymns.Msg/PlaceSellOrder", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).PlaceSellOrder(ctx, req.(*MsgPlaceSellOrder)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_CancelSellOrder_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgCancelSellOrder) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).CancelSellOrder(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/dymensionxyz.dymension.dymns.Msg/CancelSellOrder", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).CancelSellOrder(ctx, req.(*MsgCancelSellOrder)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_PurchaseOrder_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgPurchaseOrder) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).PurchaseOrder(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/dymensionxyz.dymension.dymns.Msg/PurchaseOrder", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).PurchaseOrder(ctx, req.(*MsgPurchaseOrder)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_PlaceBuyOrder_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgPlaceBuyOrder) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).PlaceBuyOrder(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/dymensionxyz.dymension.dymns.Msg/PlaceBuyOrder", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).PlaceBuyOrder(ctx, req.(*MsgPlaceBuyOrder)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_CancelBuyOrder_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgCancelBuyOrder) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).CancelBuyOrder(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/dymensionxyz.dymension.dymns.Msg/CancelBuyOrder", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).CancelBuyOrder(ctx, req.(*MsgCancelBuyOrder)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_AcceptBuyOrder_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgAcceptBuyOrder) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).AcceptBuyOrder(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/dymensionxyz.dymension.dymns.Msg/AcceptBuyOrder", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).AcceptBuyOrder(ctx, req.(*MsgAcceptBuyOrder)) + } + 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: "/dymensionxyz.dymension.dymns.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 = grpc.ServiceDesc{ + ServiceName: "dymensionxyz.dymension.dymns.Msg", + HandlerType: (*MsgServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "RegisterName", + Handler: _Msg_RegisterName_Handler, + }, + { + MethodName: "RegisterAlias", + Handler: _Msg_RegisterAlias_Handler, + }, + { + MethodName: "TransferDymNameOwnership", + Handler: _Msg_TransferDymNameOwnership_Handler, + }, + { + MethodName: "SetController", + Handler: _Msg_SetController_Handler, + }, + { + MethodName: "UpdateResolveAddress", + Handler: _Msg_UpdateResolveAddress_Handler, + }, + { + MethodName: "UpdateDetails", + Handler: _Msg_UpdateDetails_Handler, + }, + { + MethodName: "PlaceSellOrder", + Handler: _Msg_PlaceSellOrder_Handler, + }, + { + MethodName: "CancelSellOrder", + Handler: _Msg_CancelSellOrder_Handler, + }, + { + MethodName: "PurchaseOrder", + Handler: _Msg_PurchaseOrder_Handler, + }, + { + MethodName: "PlaceBuyOrder", + Handler: _Msg_PlaceBuyOrder_Handler, + }, + { + MethodName: "CancelBuyOrder", + Handler: _Msg_CancelBuyOrder_Handler, + }, + { + MethodName: "AcceptBuyOrder", + Handler: _Msg_AcceptBuyOrder_Handler, + }, + { + MethodName: "UpdateParams", + Handler: _Msg_UpdateParams_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "dymensionxyz/dymension/dymns/tx.proto", +} + +func (m *MsgRegisterName) 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 *MsgRegisterName) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgRegisterName) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Contact) > 0 { + i -= len(m.Contact) + copy(dAtA[i:], m.Contact) + i = encodeVarintTx(dAtA, i, uint64(len(m.Contact))) + i-- + dAtA[i] = 0x2a + } + { + size, err := m.ConfirmPayment.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + if m.Duration != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.Duration)) + i-- + dAtA[i] = 0x18 + } + if len(m.Owner) > 0 { + i -= len(m.Owner) + copy(dAtA[i:], m.Owner) + i = encodeVarintTx(dAtA, i, uint64(len(m.Owner))) + i-- + dAtA[i] = 0x12 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = encodeVarintTx(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgRegisterNameResponse) 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 *MsgRegisterNameResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgRegisterNameResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgRegisterAlias) 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 *MsgRegisterAlias) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgRegisterAlias) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.ConfirmPayment.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + if len(m.Owner) > 0 { + i -= len(m.Owner) + copy(dAtA[i:], m.Owner) + i = encodeVarintTx(dAtA, i, uint64(len(m.Owner))) + i-- + dAtA[i] = 0x1a + } + if len(m.RollappId) > 0 { + i -= len(m.RollappId) + copy(dAtA[i:], m.RollappId) + i = encodeVarintTx(dAtA, i, uint64(len(m.RollappId))) + i-- + dAtA[i] = 0x12 + } + if len(m.Alias) > 0 { + i -= len(m.Alias) + copy(dAtA[i:], m.Alias) + i = encodeVarintTx(dAtA, i, uint64(len(m.Alias))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgRegisterAliasResponse) 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 *MsgRegisterAliasResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgRegisterAliasResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgTransferDymNameOwnership) 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 *MsgTransferDymNameOwnership) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgTransferDymNameOwnership) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.NewOwner) > 0 { + i -= len(m.NewOwner) + copy(dAtA[i:], m.NewOwner) + i = encodeVarintTx(dAtA, i, uint64(len(m.NewOwner))) + i-- + dAtA[i] = 0x1a + } + if len(m.Owner) > 0 { + i -= len(m.Owner) + copy(dAtA[i:], m.Owner) + i = encodeVarintTx(dAtA, i, uint64(len(m.Owner))) + i-- + dAtA[i] = 0x12 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = encodeVarintTx(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgTransferDymNameOwnershipResponse) 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 *MsgTransferDymNameOwnershipResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgTransferDymNameOwnershipResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgSetController) 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 *MsgSetController) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgSetController) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Controller) > 0 { + i -= len(m.Controller) + copy(dAtA[i:], m.Controller) + i = encodeVarintTx(dAtA, i, uint64(len(m.Controller))) + i-- + dAtA[i] = 0x1a + } + if len(m.Owner) > 0 { + i -= len(m.Owner) + copy(dAtA[i:], m.Owner) + i = encodeVarintTx(dAtA, i, uint64(len(m.Owner))) + i-- + dAtA[i] = 0x12 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = encodeVarintTx(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgSetControllerResponse) 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 *MsgSetControllerResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgSetControllerResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgUpdateResolveAddress) 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 *MsgUpdateResolveAddress) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgUpdateResolveAddress) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ResolveTo) > 0 { + i -= len(m.ResolveTo) + copy(dAtA[i:], m.ResolveTo) + i = encodeVarintTx(dAtA, i, uint64(len(m.ResolveTo))) + i-- + dAtA[i] = 0x2a + } + if len(m.SubName) > 0 { + i -= len(m.SubName) + copy(dAtA[i:], m.SubName) + i = encodeVarintTx(dAtA, i, uint64(len(m.SubName))) + i-- + dAtA[i] = 0x22 + } + if len(m.ChainId) > 0 { + i -= len(m.ChainId) + copy(dAtA[i:], m.ChainId) + i = encodeVarintTx(dAtA, i, uint64(len(m.ChainId))) + i-- + dAtA[i] = 0x1a + } + if len(m.Controller) > 0 { + i -= len(m.Controller) + copy(dAtA[i:], m.Controller) + i = encodeVarintTx(dAtA, i, uint64(len(m.Controller))) + i-- + dAtA[i] = 0x12 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = encodeVarintTx(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgUpdateResolveAddressResponse) 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 *MsgUpdateResolveAddressResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgUpdateResolveAddressResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgUpdateDetails) 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 *MsgUpdateDetails) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgUpdateDetails) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.ClearConfigs { + i-- + if m.ClearConfigs { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x20 + } + if len(m.Contact) > 0 { + i -= len(m.Contact) + copy(dAtA[i:], m.Contact) + i = encodeVarintTx(dAtA, i, uint64(len(m.Contact))) + i-- + dAtA[i] = 0x1a + } + if len(m.Controller) > 0 { + i -= len(m.Controller) + copy(dAtA[i:], m.Controller) + i = encodeVarintTx(dAtA, i, uint64(len(m.Controller))) + i-- + dAtA[i] = 0x12 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = encodeVarintTx(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgUpdateDetailsResponse) 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 *MsgUpdateDetailsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgUpdateDetailsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgPlaceSellOrder) 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 *MsgPlaceSellOrder) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgPlaceSellOrder) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.SellPrice != nil { + { + size, err := m.SellPrice.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + } + { + size, err := m.MinPrice.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + if len(m.Owner) > 0 { + i -= len(m.Owner) + copy(dAtA[i:], m.Owner) + i = encodeVarintTx(dAtA, i, uint64(len(m.Owner))) + i-- + dAtA[i] = 0x1a + } + if m.AssetType != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.AssetType)) + i-- + dAtA[i] = 0x10 + } + if len(m.AssetId) > 0 { + i -= len(m.AssetId) + copy(dAtA[i:], m.AssetId) + i = encodeVarintTx(dAtA, i, uint64(len(m.AssetId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgPlaceSellOrderResponse) 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 *MsgPlaceSellOrderResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgPlaceSellOrderResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgCancelSellOrder) 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 *MsgCancelSellOrder) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgCancelSellOrder) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Owner) > 0 { + i -= len(m.Owner) + copy(dAtA[i:], m.Owner) + i = encodeVarintTx(dAtA, i, uint64(len(m.Owner))) + i-- + dAtA[i] = 0x1a + } + if m.AssetType != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.AssetType)) + i-- + dAtA[i] = 0x10 + } + if len(m.AssetId) > 0 { + i -= len(m.AssetId) + copy(dAtA[i:], m.AssetId) + i = encodeVarintTx(dAtA, i, uint64(len(m.AssetId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgCancelSellOrderResponse) 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 *MsgCancelSellOrderResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgCancelSellOrderResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgPurchaseOrder) 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 *MsgPurchaseOrder) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgPurchaseOrder) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Offer.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + if len(m.Buyer) > 0 { + i -= len(m.Buyer) + copy(dAtA[i:], m.Buyer) + i = encodeVarintTx(dAtA, i, uint64(len(m.Buyer))) + i-- + dAtA[i] = 0x22 + } + if len(m.Params) > 0 { + for iNdEx := len(m.Params) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Params[iNdEx]) + copy(dAtA[i:], m.Params[iNdEx]) + i = encodeVarintTx(dAtA, i, uint64(len(m.Params[iNdEx]))) + i-- + dAtA[i] = 0x1a + } + } + if m.AssetType != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.AssetType)) + i-- + dAtA[i] = 0x10 + } + if len(m.AssetId) > 0 { + i -= len(m.AssetId) + copy(dAtA[i:], m.AssetId) + i = encodeVarintTx(dAtA, i, uint64(len(m.AssetId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgPurchaseOrderResponse) 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 *MsgPurchaseOrderResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgPurchaseOrderResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgPlaceBuyOrder) 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 *MsgPlaceBuyOrder) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgPlaceBuyOrder) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Offer.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x32 + if len(m.ContinueOrderId) > 0 { + i -= len(m.ContinueOrderId) + copy(dAtA[i:], m.ContinueOrderId) + i = encodeVarintTx(dAtA, i, uint64(len(m.ContinueOrderId))) + i-- + dAtA[i] = 0x2a + } + if len(m.Buyer) > 0 { + i -= len(m.Buyer) + copy(dAtA[i:], m.Buyer) + i = encodeVarintTx(dAtA, i, uint64(len(m.Buyer))) + i-- + dAtA[i] = 0x22 + } + if len(m.Params) > 0 { + for iNdEx := len(m.Params) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Params[iNdEx]) + copy(dAtA[i:], m.Params[iNdEx]) + i = encodeVarintTx(dAtA, i, uint64(len(m.Params[iNdEx]))) + i-- + dAtA[i] = 0x1a + } + } + if m.AssetType != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.AssetType)) + i-- + dAtA[i] = 0x10 + } + if len(m.AssetId) > 0 { + i -= len(m.AssetId) + copy(dAtA[i:], m.AssetId) + i = encodeVarintTx(dAtA, i, uint64(len(m.AssetId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgPlaceBuyOrderResponse) 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 *MsgPlaceBuyOrderResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgPlaceBuyOrderResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.OrderId) > 0 { + i -= len(m.OrderId) + copy(dAtA[i:], m.OrderId) + i = encodeVarintTx(dAtA, i, uint64(len(m.OrderId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgCancelBuyOrder) 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 *MsgCancelBuyOrder) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgCancelBuyOrder) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Buyer) > 0 { + i -= len(m.Buyer) + copy(dAtA[i:], m.Buyer) + i = encodeVarintTx(dAtA, i, uint64(len(m.Buyer))) + i-- + dAtA[i] = 0x12 + } + if len(m.OrderId) > 0 { + i -= len(m.OrderId) + copy(dAtA[i:], m.OrderId) + i = encodeVarintTx(dAtA, i, uint64(len(m.OrderId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgCancelBuyOrderResponse) 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 *MsgCancelBuyOrderResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgCancelBuyOrderResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgAcceptBuyOrder) 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 *MsgAcceptBuyOrder) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgAcceptBuyOrder) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.MinAccept.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + if len(m.Owner) > 0 { + i -= len(m.Owner) + copy(dAtA[i:], m.Owner) + i = encodeVarintTx(dAtA, i, uint64(len(m.Owner))) + i-- + dAtA[i] = 0x12 + } + if len(m.OrderId) > 0 { + i -= len(m.OrderId) + copy(dAtA[i:], m.OrderId) + i = encodeVarintTx(dAtA, i, uint64(len(m.OrderId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgAcceptBuyOrderResponse) 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 *MsgAcceptBuyOrderResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgAcceptBuyOrderResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Accepted { + i-- + if m.Accepted { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x8 + } + 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 + if m.NewMiscParams != nil { + { + size, err := m.NewMiscParams.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + if m.NewChainsParams != nil { + { + size, err := m.NewChainsParams.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + if m.NewPriceParams != nil { + { + size, err := m.NewPriceParams.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 *MsgRegisterName) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Name) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.Owner) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if m.Duration != 0 { + n += 1 + sovTx(uint64(m.Duration)) + } + l = m.ConfirmPayment.Size() + n += 1 + l + sovTx(uint64(l)) + l = len(m.Contact) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgRegisterNameResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgRegisterAlias) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Alias) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.RollappId) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.Owner) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = m.ConfirmPayment.Size() + n += 1 + l + sovTx(uint64(l)) + return n +} + +func (m *MsgRegisterAliasResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgTransferDymNameOwnership) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Name) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.Owner) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.NewOwner) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgTransferDymNameOwnershipResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgSetController) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Name) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.Owner) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.Controller) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgSetControllerResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgUpdateResolveAddress) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Name) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.Controller) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.ChainId) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.SubName) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.ResolveTo) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgUpdateResolveAddressResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgUpdateDetails) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Name) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.Controller) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.Contact) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if m.ClearConfigs { + n += 2 + } + return n +} + +func (m *MsgUpdateDetailsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgPlaceSellOrder) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.AssetId) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if m.AssetType != 0 { + n += 1 + sovTx(uint64(m.AssetType)) + } + l = len(m.Owner) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = m.MinPrice.Size() + n += 1 + l + sovTx(uint64(l)) + if m.SellPrice != nil { + l = m.SellPrice.Size() + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgPlaceSellOrderResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgCancelSellOrder) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.AssetId) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if m.AssetType != 0 { + n += 1 + sovTx(uint64(m.AssetType)) + } + l = len(m.Owner) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgCancelSellOrderResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgPurchaseOrder) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.AssetId) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if m.AssetType != 0 { + n += 1 + sovTx(uint64(m.AssetType)) + } + if len(m.Params) > 0 { + for _, s := range m.Params { + l = len(s) + n += 1 + l + sovTx(uint64(l)) + } + } + l = len(m.Buyer) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = m.Offer.Size() + n += 1 + l + sovTx(uint64(l)) + return n +} + +func (m *MsgPurchaseOrderResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgPlaceBuyOrder) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.AssetId) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if m.AssetType != 0 { + n += 1 + sovTx(uint64(m.AssetType)) + } + if len(m.Params) > 0 { + for _, s := range m.Params { + l = len(s) + n += 1 + l + sovTx(uint64(l)) + } + } + l = len(m.Buyer) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.ContinueOrderId) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = m.Offer.Size() + n += 1 + l + sovTx(uint64(l)) + return n +} + +func (m *MsgPlaceBuyOrderResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.OrderId) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgCancelBuyOrder) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.OrderId) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.Buyer) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgCancelBuyOrderResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgAcceptBuyOrder) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.OrderId) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.Owner) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = m.MinAccept.Size() + n += 1 + l + sovTx(uint64(l)) + return n +} + +func (m *MsgAcceptBuyOrderResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Accepted { + n += 2 + } + 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)) + } + if m.NewPriceParams != nil { + l = m.NewPriceParams.Size() + n += 1 + l + sovTx(uint64(l)) + } + if m.NewChainsParams != nil { + l = m.NewChainsParams.Size() + n += 1 + l + sovTx(uint64(l)) + } + if m.NewMiscParams != nil { + l = m.NewMiscParams.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 *MsgRegisterName) 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: MsgRegisterName: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgRegisterName: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", 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.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Owner", 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.Owner = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Duration", wireType) + } + m.Duration = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Duration |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ConfirmPayment", 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.ConfirmPayment.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Contact", 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.Contact = 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 *MsgRegisterNameResponse) 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: MsgRegisterNameResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgRegisterNameResponse: 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 *MsgRegisterAlias) 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: MsgRegisterAlias: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgRegisterAlias: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Alias", 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.Alias = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field RollappId", 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.RollappId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Owner", 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.Owner = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ConfirmPayment", 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.ConfirmPayment.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 *MsgRegisterAliasResponse) 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: MsgRegisterAliasResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgRegisterAliasResponse: 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 *MsgTransferDymNameOwnership) 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: MsgTransferDymNameOwnership: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgTransferDymNameOwnership: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", 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.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Owner", 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.Owner = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NewOwner", 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.NewOwner = 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 *MsgTransferDymNameOwnershipResponse) 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: MsgTransferDymNameOwnershipResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgTransferDymNameOwnershipResponse: 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 *MsgSetController) 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: MsgSetController: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgSetController: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", 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.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Owner", 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.Owner = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Controller", 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.Controller = 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 *MsgSetControllerResponse) 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: MsgSetControllerResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgSetControllerResponse: 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 *MsgUpdateResolveAddress) 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: MsgUpdateResolveAddress: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgUpdateResolveAddress: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", 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.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Controller", 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.Controller = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ChainId", 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.ChainId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SubName", 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.SubName = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ResolveTo", 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.ResolveTo = 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 *MsgUpdateResolveAddressResponse) 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: MsgUpdateResolveAddressResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgUpdateResolveAddressResponse: 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 *MsgUpdateDetails) 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: MsgUpdateDetails: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgUpdateDetails: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", 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.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Controller", 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.Controller = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Contact", 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.Contact = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ClearConfigs", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.ClearConfigs = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgUpdateDetailsResponse) 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: MsgUpdateDetailsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgUpdateDetailsResponse: 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 *MsgPlaceSellOrder) 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: MsgPlaceSellOrder: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgPlaceSellOrder: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AssetId", 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.AssetId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field AssetType", wireType) + } + m.AssetType = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.AssetType |= AssetType(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Owner", 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.Owner = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field MinPrice", 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.MinPrice.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SellPrice", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.SellPrice == nil { + m.SellPrice = &types.Coin{} + } + if err := m.SellPrice.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 *MsgPlaceSellOrderResponse) 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: MsgPlaceSellOrderResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgPlaceSellOrderResponse: 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 *MsgCancelSellOrder) 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: MsgCancelSellOrder: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgCancelSellOrder: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AssetId", 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.AssetId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field AssetType", wireType) + } + m.AssetType = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.AssetType |= AssetType(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Owner", 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.Owner = 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 *MsgCancelSellOrderResponse) 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: MsgCancelSellOrderResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgCancelSellOrderResponse: 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 *MsgPurchaseOrder) 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: MsgPurchaseOrder: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgPurchaseOrder: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AssetId", 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.AssetId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field AssetType", wireType) + } + m.AssetType = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.AssetType |= AssetType(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Params", 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.Params = append(m.Params, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Buyer", 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.Buyer = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Offer", 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.Offer.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 *MsgPurchaseOrderResponse) 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: MsgPurchaseOrderResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgPurchaseOrderResponse: 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 *MsgPlaceBuyOrder) 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: MsgPlaceBuyOrder: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgPlaceBuyOrder: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AssetId", 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.AssetId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field AssetType", wireType) + } + m.AssetType = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.AssetType |= AssetType(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Params", 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.Params = append(m.Params, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Buyer", 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.Buyer = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ContinueOrderId", 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.ContinueOrderId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Offer", 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.Offer.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 *MsgPlaceBuyOrderResponse) 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: MsgPlaceBuyOrderResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgPlaceBuyOrderResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field OrderId", 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.OrderId = 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 *MsgCancelBuyOrder) 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: MsgCancelBuyOrder: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgCancelBuyOrder: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field OrderId", 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.OrderId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Buyer", 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.Buyer = 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 *MsgCancelBuyOrderResponse) 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: MsgCancelBuyOrderResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgCancelBuyOrderResponse: 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 *MsgAcceptBuyOrder) 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: MsgAcceptBuyOrder: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgAcceptBuyOrder: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field OrderId", 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.OrderId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Owner", 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.Owner = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field MinAccept", 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.MinAccept.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 *MsgAcceptBuyOrderResponse) 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: MsgAcceptBuyOrderResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgAcceptBuyOrderResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Accepted", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Accepted = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *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 NewPriceParams", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.NewPriceParams == nil { + m.NewPriceParams = &PriceParams{} + } + if err := m.NewPriceParams.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NewChainsParams", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.NewChainsParams == nil { + m.NewChainsParams = &ChainsParams{} + } + if err := m.NewChainsParams.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NewMiscParams", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.NewMiscParams == nil { + m.NewMiscParams = &MiscParams{} + } + if err := m.NewMiscParams.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") +) diff --git a/x/dymns/types/types.go b/x/dymns/types/types.go new file mode 100644 index 000000000..52b8e5cd1 --- /dev/null +++ b/x/dymns/types/types.go @@ -0,0 +1,72 @@ +package types + +// Event to fire when a DymName is set into store. +const ( + EventTypeSetDymName = ModuleName + "_name" + AttributeKeyDymName = "name" + AttributeKeyDymNameOwner = "owner" + AttributeKeyDymNameController = "controller" + AttributeKeyDymNameExpiryEpoch = "expiry_epoch" + AttributeKeyDymNameConfigCount = "cfg_count" + AttributeKeyDymNameHasContactDetails = "has_contact" +) + +// Event to fire when refunding a deposited bidding amount of a Sell-Order. +const ( + EventTypeSoRefundBid = ModuleName + "_bid_refund" + AttributeKeySoRefundBidder = "bidder" + AttributeKeySoRefundAmount = "amount" +) + +// Event to fire when refunding deposited amount of a Buy-Order. +const ( + EventTypeBoRefundOffer = ModuleName + "_bo_refund" + AttributeKeyBoRefundBuyer = "buyer" + AttributeKeyBoRefundAmount = "amount" +) + +// Event to fire when a SellOrder is set into store. +const ( + EventTypeSellOrder = ModuleName + "_so" + AttributeKeySoActionName = "action" + AttributeKeySoAssetId = "asset_id" + AttributeKeySoAssetType = "asset_type" + AttributeKeySoExpiryEpoch = "expiry_epoch" + AttributeKeySoMinPrice = "min_price" + AttributeKeySoSellPrice = "sell_price" + AttributeKeySoHighestBidder = "highest_bidder" + AttributeKeySoHighestBidPrice = "highest_bid_price" +) + +// Event to fire corresponding to the action of CRUD a SellOrder. +const ( + AttributeValueSoActionNameSet = "set" + AttributeValueSoActionNameDelete = "delete" +) + +// Event to fire when a BuyOrder is set into store. +const ( + EventTypeBuyOrder = ModuleName + "_bo" + AttributeKeyBoActionName = "action" + AttributeKeyBoId = "id" + AttributeKeyBoAssetId = "asset_id" + AttributeKeyBoAssetType = "asset_type" + AttributeKeyBoBuyer = "buyer" + AttributeKeyBoOfferPrice = "offer_price" + AttributeKeyBoCounterpartyOfferPrice = "counterparty_offer_price" +) + +// Event to fire corresponding to the action of CRUD a BuyOrder. +const ( + AttributeValueBoActionNameSet = "set" + AttributeValueBoActionNameDelete = "delete" +) + +// Event to fire when a Dym-Name/Asset is sold. +const ( + EventTypeSell = ModuleName + "_sell" + AttributeKeySellAssetType = "asset_type" + AttributeKeySellName = "name" + AttributeKeySellPrice = "price" + AttributeKeySellTo = "buyer" +) diff --git a/x/dymns/utils/address.go b/x/dymns/utils/address.go new file mode 100644 index 000000000..f5c330880 --- /dev/null +++ b/x/dymns/utils/address.go @@ -0,0 +1,108 @@ +package utils + +import ( + "regexp" + "strings" + + "github.com/dymensionxyz/dymension/v3/app/params" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/bech32" + "github.com/ethereum/go-ethereum/common" +) + +var accAddrBech32Prefix = sdk.GetConfig().GetBech32AccountAddrPrefix() + +// IsValidBech32AccountAddress returns true if the given string is a valid bech32 account address. +// Depends on the flag, it will check the prefix of the bech32 account address +// matches with host chain's account address prefix. +func IsValidBech32AccountAddress(address string, matchAccountAddressBech32Prefix bool) bool { + hrp, bz, err := bech32.DecodeAndConvert(address) + if err != nil { + return false + } + + bytesCount := len(bz) + if bytesCount != 20 && bytesCount != 32 /*32 bytes is interchain account*/ { + return false + } + + return !matchAccountAddressBech32Prefix || hrp == accAddrBech32Prefix +} + +// pattern0xHex is a regex pattern for 0x prefixed hex string. +// Length check is omitted. +var pattern0xHex = regexp.MustCompile(`^0x[a-f\d]+$`) + +// IsValidHexAddress returns true if the given string is a valid hex address. +// It checks the length and the pattern of the hex address. +// The hex address can be either 20 bytes presents for normal accounts or 32 bytes presents for Interchain Accounts. +func IsValidHexAddress(address string) bool { + length := len(address) + if length != 42 && length != 66 /*32 bytes is interchain account*/ { + return false + } + + address = strings.ToLower(address) + + return pattern0xHex.MatchString(address) +} + +// GetBytesFromHexAddress returns the bytes from the given hex address. +func GetBytesFromHexAddress(address string) []byte { + if !IsValidHexAddress(address) { + panic("invalid hex address") + } + + if len(address) == 66 { + return common.HexToHash(address).Bytes() + } + + return common.HexToAddress(address).Bytes() +} + +// GetHexAddressFromBytes returns the hex address from the given bytes. +func GetHexAddressFromBytes(bytes []byte) string { + if len(bytes) == 32 { + return strings.ToLower(common.BytesToHash(bytes).Hex()) + } else if len(bytes) == 20 { + return strings.ToLower(common.BytesToAddress(bytes).Hex()) + } else { + panic("invalid bytes length") + } +} + +// PossibleAccountRegardlessChain returns true if the given string is a POSSIBLE account address regardless of chain. +// There is no guarantee that the address is valid on the chain. +func PossibleAccountRegardlessChain(address string) bool { + if length := len(address); length < 5 || length > 130 { + return false + } + + for _, r := range address { + if r >= 'a' && r <= 'z' { + continue + } + if r >= 'A' && r <= 'Z' { + continue + } + if r >= '0' && r <= '9' { + continue + } + if r == '-' || r == ':' || r == '.' { + continue + } + if r == '*' || r == '@' { + // Stellar Federated Address + continue + } + + return false + } + + return true +} + +func init() { + params.SetAddressPrefixes() +} diff --git a/x/dymns/utils/address_test.go b/x/dymns/utils/address_test.go new file mode 100644 index 000000000..e9f472af9 --- /dev/null +++ b/x/dymns/utils/address_test.go @@ -0,0 +1,667 @@ +package utils + +import ( + "fmt" + "testing" + + "github.com/dymensionxyz/dymension/v3/app/params" + + "github.com/cosmos/cosmos-sdk/types/bech32" + "github.com/stretchr/testify/require" +) + +func init() { + params.SetAddressPrefixes() +} + +func TestIsValidBech32AccountAddress(t *testing.T) { + //goland:noinspection SpellCheckingInspection + tests := []struct { + name string + address string + matchAccountAddressBech32Prefix bool + wantValid bool + }{ + { + name: "pass - valid bech32 account address", + address: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + matchAccountAddressBech32Prefix: true, + wantValid: true, + }, + { + name: "pass - valid bech32 account address, Interchain Account", + address: "dym1zg69v7yszg69v7yszg69v7yszg69v7yszg69v7yszg69v7yszg6qrz80ul", + matchAccountAddressBech32Prefix: true, + wantValid: true, + }, + { + name: "fail - reject bech32 account address which neither 20 nor 32 bytes, case 19 bytes", + address: func() string { + bz := make([]byte, 19) + addr, err := bech32.ConvertAndEncode("dym", bz) + require.NoError(t, err) + return addr + }(), + wantValid: false, + }, + { + name: "fail - reject bech32 account address which neither 20 nor 32 bytes, case 21 bytes", + address: func() string { + bz := make([]byte, 21) + addr, err := bech32.ConvertAndEncode("dym", bz) + require.NoError(t, err) + return addr + }(), + wantValid: false, + }, + { + name: "fail - reject bech32 account address which neither 20 nor 32 bytes, case 31 bytes", + address: func() string { + bz := make([]byte, 31) + addr, err := bech32.ConvertAndEncode("dym", bz) + require.NoError(t, err) + return addr + }(), + wantValid: false, + }, + { + name: "fail - reject bech32 account address which neither 20 nor 32 bytes, case 33 bytes", + address: func() string { + bz := make([]byte, 33) + addr, err := bech32.ConvertAndEncode("dym", bz) + require.NoError(t, err) + return addr + }(), + wantValid: false, + }, + { + name: "fail - reject bech32 account address which neither 20 nor 32 bytes, case 128 bytes", + address: func() string { + bz := make([]byte, 128) + addr, err := bech32.ConvertAndEncode("dym", bz) + require.NoError(t, err) + return addr + }(), + wantValid: false, + }, + { + name: "fail - bad checksum bech32 account address", + address: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9feu", + matchAccountAddressBech32Prefix: true, + wantValid: false, + }, + { + name: "fail - bad bech32 account address", + address: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3", + matchAccountAddressBech32Prefix: true, + wantValid: false, + }, + { + name: "fail - not bech32 address", + address: "0x4fea76427b8345861e80a3540a8a9d936fd39391", + matchAccountAddressBech32Prefix: true, + wantValid: false, + }, + { + name: "fail - not bech32 address", + address: "0x4fea76427b8345861e80a3540a8a9d936fd39391", + matchAccountAddressBech32Prefix: false, + wantValid: false, + }, + { + name: "fail - valid bech32 account address but mis-match HRP", + address: "nim1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3pklgjx", + matchAccountAddressBech32Prefix: true, + wantValid: false, + }, + { + name: "pass - valid bech32 account address ignore mis-match HRP", + address: "nim1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3pklgjx", + matchAccountAddressBech32Prefix: false, + wantValid: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + gotValid := IsValidBech32AccountAddress(tt.address, tt.matchAccountAddressBech32Prefix) + require.Equal(t, tt.wantValid, gotValid) + }) + } +} + +//goland:noinspection SpellCheckingInspection +func TestIsValidHexAddress(t *testing.T) { + tests := []struct { + name string + address string + wantValid bool + }{ + { + name: "pass - allow hex address with 20 bytes", + address: "0x1234567890123456789012345678901234567890", + wantValid: true, + }, + { + name: "pass - allow hex address with 32 bytes, Interchain Account", + address: "0x1234567890123456789012345678901234567890123456789012345678901234", + wantValid: true, + }, + { + name: "fail - disallow hex address with 19 bytes", + address: "0x123456789012345678901234567890123456789", + wantValid: false, + }, + { + name: "fail - disallow hex address with 21 bytes", + address: "0x12345678901234567890123456789012345678901", + wantValid: false, + }, + { + name: "fail - disallow hex address with 31 bytes", + address: "0x123456789012345678901234567890123456789012345678901234567890123", + wantValid: false, + }, + { + name: "fail - disallow hex address with 33 bytes", + address: "0x12345678901234567890123456789012345678901234567890123456789012345", + wantValid: false, + }, + { + name: "fail - disallow valid bech32 address", + address: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + wantValid: false, + }, + { + name: "fail - disallow valid bech32 address, Interchain Account", + address: "dym1zg69v7yszg69v7yszg69v7yszg69v7yszg69v7yszg69v7yszg6qrz80ul", + wantValid: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + require.Equal(t, tt.wantValid, IsValidHexAddress(tt.address)) + }) + } +} + +//goland:noinspection SpellCheckingInspection +func TestGetBytesFromHexAddress(t *testing.T) { + tests := []struct { + name string + address string + wantPanic bool + want []byte + }{ + { + name: "20 bytes address", + address: "0x1234567890123456789012345678901234567890", + want: []byte{ + 0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78, 0x90, + 0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78, 0x90, + }, + }, + { + name: "32 bytes address", + address: "0x1234567890123456789012345678901234567890123456789012345678901234", + want: []byte{ + 0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78, 0x90, + 0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78, 0x90, + 0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78, 0x90, + 0x12, 0x34, + }, + }, + { + name: "panic if invalid hex address", + address: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9feu", + wantPanic: true, + }, + { + name: "panic if neither 20 nor 32 bytes address", + address: "0x123456789012345678901234567890123456789", + wantPanic: true, + }, + { + name: "panic if neither 20 nor 32 bytes address", + address: "0x12345678901234567890123456789012345678901", + wantPanic: true, + }, + { + name: "panic if neither 20 nor 32 bytes address", + address: "0x123456789012345678901234567890123456789012345678901234567890123", + wantPanic: true, + }, + { + name: "panic if neither 20 nor 32 bytes address", + address: "0x12345678901234567890123456789012345678901234567890123456789012345", + wantPanic: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if tt.wantPanic { + require.Panics(t, func() { + _ = GetBytesFromHexAddress(tt.address) + }) + return + } + + require.Equal(t, tt.want, GetBytesFromHexAddress(tt.address)) + }) + } +} + +//goland:noinspection SpellCheckingInspection +func TestGetHexAddressFromBytes(t *testing.T) { + tests := []struct { + name string + bytes []byte + want string + wantPanic bool + }{ + { + name: "20 bytes address", + bytes: []byte{ + 0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78, 0x90, + 0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78, 0x90, + }, + want: "0x1234567890123456789012345678901234567890", + wantPanic: false, + }, + { + name: "32 bytes address", + bytes: []byte{ + 0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78, 0x90, + 0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78, 0x90, + 0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78, 0x90, + 0x12, 0x34, + }, + want: "0x1234567890123456789012345678901234567890123456789012345678901234", + wantPanic: false, + }, + { + name: "panic if neither 20 nor 32 bytes address", + bytes: []byte{ + 0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78, 0x90, + 0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78, + }, + wantPanic: true, + }, + { + name: "panic if neither 20 nor 32 bytes address", + bytes: []byte{ + 0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78, 0x90, + 0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78, 0x90, 0x12, + }, + wantPanic: true, + }, + { + name: "panic if neither 20 nor 32 bytes address", + bytes: []byte{ + 0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78, 0x90, + 0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78, 0x90, + 0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78, 0x90, + 0x12, + }, + wantPanic: true, + }, + { + name: "panic if neither 20 nor 32 bytes address", + bytes: []byte{ + 0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78, 0x90, + 0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78, 0x90, + 0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78, 0x90, + 0x12, 0x34, 0x56, + }, + wantPanic: true, + }, + { + name: "output must be lower case", + bytes: []byte{ + 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x11, 0x22, 0x33, 0x44, + 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, + }, + want: "0xaabbccddeeff112233445566778899aabbccddee", + }, + { + name: "output must be lower case", + bytes: []byte{ + 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x11, 0x22, 0x33, 0x44, + 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, + 0xff, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, + 0xaa, 0xbb, + }, + want: "0xaabbccddeeff112233445566778899aabbccddeeff112233445566778899aabb", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if tt.wantPanic { + require.Panics(t, func() { + _ = GetHexAddressFromBytes(tt.bytes) + }) + + return + } + + got := GetHexAddressFromBytes(tt.bytes) + require.Equal(t, tt.want, got) + }) + } +} + +func TestPossibleAccountRegardlessChain(t *testing.T) { + //goland:noinspection SpellCheckingInspection + tests := []struct { + name string + address string + want bool + }{ + { + name: "pass - Dymension bech32 address", + address: "dym1fl48vsnmsdzcv85q5d2q4z5ajdha8yu38x9fue", + want: true, + }, + { + name: "pass - Cosmos bech32 address", + address: "cosmos1fl48vsnmsdzcv85q5d2q4z5ajdha8yu34mf0eh", + want: true, + }, + { + name: "pass - bech32 address - Interchain Account", + address: "dym1zg69v7yszg69v7yszg69v7yszg69v7yszg69v7yszg69v7yszg6qrz80ul", + want: true, + }, + { + name: "pass - Ethereum - numeric only", + address: "0x1234567890123456789012345678901234567890", + want: true, + }, + { + name: "pass - Ethereum - lowercase address", + address: "0xaabbccddeeff112233445566778899aabbccddee", + want: true, + }, + { + name: "pass - Ethereum - checksum address", + address: "0xAabBccddeEfF112233445566778899aaBbccDdEe", + want: true, + }, + { + name: "pass - 0x address - 32 bytes", + address: "0x0380d46a00e427d89f35d78b4eacb4270bd5ecfd10b64662dcfe31eb117fc62c68", + want: true, + }, + { + name: "pass - Bitcoin - P2PK (Obsolete)", + address: "04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f", + want: true, + }, + { + name: "pass - Bitcoin - P2PKH (26 chars)", + address: "11111111111111111111BZbvjr", + want: true, + }, + { + name: "pass - Bitcoin - P2PKH (27 chars)", + address: "1111111111111111111114oLvT2", + want: true, + }, + { + name: "pass - Bitcoin - P2PKH (34 chars)", + address: "12higDjoCCNXSA95xZMWUdPvXNmkAduhWv", + want: true, + }, + { + name: "pass - Bitcoin - P2SH", + address: "342ftSRCvFHfCeFFBuz4xwbeqnDw6BGUey", + want: true, + }, + { + name: "pass - Bitcoin - P2WPKH", + address: "bc1q34aq5drpuwy3wgl9lhup9892qp6svr8ldzyy7c", + want: true, + }, + { + name: "pass - Bitcoin - P2WSH", + address: "bc1qeklep85ntjz4605drds6aww9u0qr46qzrv5xswd35uhjuj8ahfcqgf6hak", + want: true, + }, + { + name: "pass - Bitcoin - P2TR", + address: "bc1pxwww0ct9ue7e8tdnlmug5m2tamfn7q06sahstg39ys4c9f3340qqxrdu9k", + want: true, + }, + { + name: "pass - Bitcoin - BC1P", + address: "bc1prwgcpptoxrpfl5go81wpd5qlsig5yt4g7urb45e", + want: true, + }, + { + name: "pass - Bitcoin - bech32 (Native SegWit)", + address: "bc1qwqdg6squsna38e46795at95yu9atm8azzmyvckulcc7kytlcckxswvvzej", + want: true, + }, + { + name: "pass - Avalanche - C-Chain", + address: "0x3cA8ac240F6ebeA8684b3E629A8e8C1f0E3bC0Ff", + want: true, + }, + { + name: "pass - Avalanche - X-Chain", + address: "X-avax1tzdcgj4ehsvhhgpl7zylwpw0gl2rxcg4r5afk5", + want: true, + }, + { + name: "pass - Cardano - legacy Byron (Icarus)", + address: "Ae2tdPwUPEZFSi1cTyL1ZL6bgixhc2vSy5heg6Zg9uP7PpumkAJ82Qprt8b", + want: true, + }, + { + name: "pass - Cardano - legacy Byron (Daedalus)", + address: "DdzFFzCqrhsfZHjaBunVySZBU8i9Zom7Gujham6Jz8scCcAdkDmEbD9XSdXKdBiPoa1fjgL4ksGjQXD8ZkSNHGJfT25ieA9rWNCSA5qc", + want: true, + }, + { + name: "pass - Cardano - Shelley", + address: "addr1q8gg2r3vf9zggn48g7m8vx62rwf6warcs4k7ej8mdzmqmesj30jz7psduyk6n4n2qrud2xlv9fgj53n6ds3t8cs4fvzs05yzmz", + want: true, + }, + { + name: "pass - Polkadot - Polkadot SS58", + address: "1a1LcBX6hGPKg5aQ6DXZpAHCCzWjckhea4sz3P1PvL3oc4F", + want: true, + }, + { + name: "pass - Polkadot - Kusama SS58", + address: "HNZata7iMYWmk5RvZRTiAsSDhV8366zq2YGb3tLH5Upf74F", + want: true, + }, + { + name: "pass - Polkadot - Generic Substrate SS58", + address: "5CdiCGvTEuzut954STAXRfL8Lazs3KCZa5LPpkPeqqJXdTHp", + want: true, + }, + { + name: "pass - Polkadot - Account ID", + address: "0x192c3c7e5789b461fbf1c7f614ba5eed0b22efc507cda60a5e7fda8e046bcdce", + want: true, + }, + { + name: "pass - XRP Ledger - base58", + address: "rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz", + want: true, + }, + { + name: "pass - XRP Ledger - X-address", + address: "XV5sbjUmgPpvXv4ixFWZ5ptAYZ6PD28Sq49uo34VyjnmK5H", + want: true, + }, + { + name: "pass - Solana", + address: "7EcDhSYGxXyscszYEp35KHN8vvw3svAuLKTzXwCFLtV", + want: true, + }, + { + name: "pass - Tron - hex", + address: "414450cf8c8b6a8229b7f628e36b3a658e84441b6f", + want: true, + }, + { + name: "pass - Tron - base58", + address: "TGCRkw1Vq759FBCrwxkZGgqZbRX1WkBHSu", + want: true, + }, + { + name: "pass - XinFin - xdc", + address: "xdc64b3b0a417775cfb441ed064611bf79826649c0f", + want: true, + }, + { + name: "pass - XinFin - hex", + address: "0x64b3b0a417775cfb441ed064611bf79826649c0f", + want: true, + }, + { + name: "pass - Stellar", + address: "GBH4TZYZ4IRCPO44CBOLFUHULU2WGALXTAVESQA6432MBJMABBB4GIYI", + want: true, + }, + { + name: "pass - Stellar - Federated addresses", + address: "jed*stellar.org", + want: true, + }, + { + name: "pass - Stellar - Federated addresses", + address: "maria@gmail.com*stellar.org", + want: true, + }, + { + name: "pass - Klaytn", + address: "0xBe588061d20fe359E69D78824EC45EA98C87069A", + want: true, + }, + { + name: "pass - Neo N3", + address: "NVeu7XqbZ6WiL1prhChC1jMWgicuWtneDP", + want: true, + }, + { + name: "pass - Neo Legacy", + address: "ALuhj3QNoxvAnMZsA2oKP5UxYsBmRwjwHL", + want: true, + }, + { + name: "pass - Tezos - user tz1", + address: "tz1YWK1gDPQx9N1Jh4JnmVre7xN6xhGGM4uC", + want: true, + }, + { + name: "pass - Tezos - user tz3", + address: "tz3T8djchG5FDwt7H6wEUU3sRFJwonYPqMJe", + want: true, + }, + { + name: "pass - Tezos - Smart Contract", + address: "KT1S5hgipNSTFehZo7v81gq6fcLChbRwptqy", + want: true, + }, + { + name: "pass - Litecoin - Legacy", + address: "LMHEFMwRsQ3nHDfb9zZqynLHxjuJ2hgyyW", + want: true, + }, + { + name: "pass - Litecoin - P2SH", + address: "MC2JYMPVWaxqUb9qUkUbjtUwoNMo1tPaLF", + want: true, + }, + { + name: "pass - Litecoin - bech32", + address: "ltc1qhzjptwpym9afcdjhs7jcz6fd0jma0l0rc0e5yr", + want: true, + }, + { + name: "pass - Litecoin - bech32", + address: "ltc1qzvcgmntglcuv4smv3lzj6k8szcvsrmvk0phrr9wfq8w493r096ssm2fgsw", + want: true, + }, + { + name: "pass - Bitcoin Cash", + address: "qrvax3jgtwqssnkpctlqdl0rq7rjn0l0hgny8pt0hp", + want: true, + }, + { + name: "pass - Bitcoin Cash - with prefix", + address: "bitcoincash:qrvax3jgtwqssnkpctlqdl0rq7rjn0l0hgny8pt0hp", + want: true, + }, + { + name: "pass - Dogecoin", + address: "D7wbmbjBWG5HPkT6d4gh6SdQPp6z25vcF2", + want: true, + }, + { + name: "pass - Monero", + address: "4BDtRc8Ym9wGFyEBzDWMSZ7iuUcNJ1ssiRkU6LjQgHURD4PGAMsZnzxAz2SGmNhinLxPF111N41bTHQBiu6QTmaZwKngDWrH", + want: true, + }, + { + name: "pass - Zcash - Transparent Address", + address: "t1Rv4exT7bqhZqi2j7xz8bUHDMxwosrjADU", + want: true, + }, + { + name: "pass - Zcash - Sapling Shielded Address", + address: "zs1z7rejlpsa98s2rrrfkwmaxu53e4ue0ulcrw0h4x5g8jl04tak0d3mm47vdtahatqrlkngh9sly", + want: true, + }, + { + name: "pass - Zcash - Legacy Sprout Shielded Address", + address: "zcU1Cd6zYyZCd2VJF8yKgmzjxdiiU1rgTTjEwoN1CGUWCziPkUTXUjXmX7TMqdMNsTfuiGN1jQoVN4kGxUR4sAPN4XZ7pxb", + want: true, + }, + { + name: "pass - Dash", + address: "XpLM8qBMd7CqukVzKXkQWuQJmgrAFb87Qr", + want: true, + }, + { + name: "pass - Binance Smart Chain", + address: "0x7f533b5fbf6ef86c3b7df76cc27fc67744a9a760", + want: true, + }, + { + name: "pass - Algorand", + address: "2UEQTE5QDNXPI7M3TU44G6SYKLFWLPQO7EBZM7K7MHMQQMFI4QJPLHQFHM", + want: true, + }, + { + name: "pass - Algorand - typed", + address: "ALGO-2UEQTE5QDNXPI7M3TU44G6SYKLFWLPQO7EBZM7K7MHMQQMFI4QJPLHQFHM", + want: true, + }, + { + name: "pass - Hedera Hashgraph - without checksum", + address: "0.0.123", + want: true, + }, + { + name: "pass - Hedera Hashgraph - without checksum", + address: "0.0.0", + want: true, + }, + { + name: "pass - Hedera Hashgraph - with checksum", + address: "0.0.123-vfmkw", + want: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + fmt.Println(tt.address) + got := PossibleAccountRegardlessChain(tt.address) + require.Equal(t, tt.want, got) + }) + } +} diff --git a/x/dymns/utils/alias.go b/x/dymns/utils/alias.go new file mode 100644 index 000000000..58e148883 --- /dev/null +++ b/x/dymns/utils/alias.go @@ -0,0 +1,19 @@ +package utils + +import "regexp" + +// patternValidateAlias is a regex pattern for validating Alias (partially). +var patternValidateAlias = regexp.MustCompile(`^[a-z\d]{1,32}$`) + +// IsValidAlias returns true if the given string is a valid Alias. +func IsValidAlias(alias string) bool { + if alias == "" { + return false + } + + if len(alias) > MaxAliasLength { + return false + } + + return patternValidateAlias.MatchString(alias) +} diff --git a/x/dymns/utils/alias_test.go b/x/dymns/utils/alias_test.go new file mode 100644 index 000000000..ef5e717f0 --- /dev/null +++ b/x/dymns/utils/alias_test.go @@ -0,0 +1,76 @@ +package utils + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestIsValidAlias(t *testing.T) { + tests := []struct { + name string + alias string + wantValid bool + }{ + { + name: "pass - valid 1 char", + alias: "a", + wantValid: true, + }, + { + name: "pass - valid 2 chars", + alias: "aa", + wantValid: true, + }, + { + name: "pass - valid 10 chars", + alias: "1234567890", + wantValid: true, + }, + { + name: "pass - valid 32 chars", + alias: "12345678901234567890123456789012", + wantValid: true, + }, + { + name: "fail - not accept 33+ chars", + alias: "123456789012345678901234567890123", + wantValid: false, + }, + { + name: "fail - not accept special chars", + alias: "a$a", + wantValid: false, + }, + { + name: "fail - not accept underscore", + alias: "a_a", + wantValid: false, + }, + { + name: "fail - not accept dash", + alias: "a-a", + wantValid: false, + }, + { + name: "fail - not accept empty", + alias: "", + wantValid: false, + }, + { + name: "fail - not accept leading space", + alias: " a", + wantValid: false, + }, + { + name: "fail - not accept trailing space", + alias: "a ", + wantValid: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + require.Equal(t, tt.wantValid, IsValidAlias(tt.alias)) + }) + } +} diff --git a/x/dymns/utils/chain.go b/x/dymns/utils/chain.go new file mode 100644 index 000000000..72e746fe9 --- /dev/null +++ b/x/dymns/utils/chain.go @@ -0,0 +1,22 @@ +package utils + +import ( + "regexp" + + cometbfttypes "github.com/cometbft/cometbft/types" +) + +// patternValidChainId is a regex pattern for valid chain id format. +var patternValidChainId = regexp.MustCompile(`^[a-z]+(-[a-z]+)?(_\d+)?(-\d+)?$`) + +// IsValidChainIdFormat returns true if the given string is a valid chain id format. +// It checks the length and the pattern of the chain id. +// The chain id length can be between 3 and 50 characters. +func IsValidChainIdFormat(chainId string) bool { + // TODO: move validation functions like this to sdk-utils repo + if len(chainId) < 3 || len(chainId) > cometbfttypes.MaxChainIDLen { + return false + } + + return patternValidChainId.MatchString(chainId) +} diff --git a/x/dymns/utils/chain_test.go b/x/dymns/utils/chain_test.go new file mode 100644 index 000000000..524f39498 --- /dev/null +++ b/x/dymns/utils/chain_test.go @@ -0,0 +1,75 @@ +package utils + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestIsValidChainIdFormat(t *testing.T) { + tests := []struct { + chainId string + invalid bool + }{ + { + chainId: "cosmoshub-4", + }, + { + chainId: "dymension_1100-1", + }, + { + chainId: "ethermint_9000-1", + }, + { + chainId: "axelar-dojo-1", + }, + { + chainId: "abc", + }, + { + chainId: "ab", + invalid: true, + }, + { + chainId: "ab99", + invalid: true, + }, + { + chainId: "abc-1", + }, + { + chainId: "abc-", + invalid: true, + }, + { + chainId: "-abc", + invalid: true, + }, + { + chainId: "abc_", + invalid: true, + }, + { + chainId: "_abc", + invalid: true, + }, + { + chainId: "500_abc", + invalid: true, + }, + { + chainId: "4-cosmoshub", + invalid: true, + }, + } + for _, tt := range tests { + t.Run(tt.chainId, func(t *testing.T) { + valid := IsValidChainIdFormat(tt.chainId) + if tt.invalid { + require.Falsef(t, valid, "expected invalid chain id: %s", tt.chainId) + } else { + require.Truef(t, valid, "expected valid chain id: %s", tt.chainId) + } + }) + } +} diff --git a/x/dymns/utils/constants.go b/x/dymns/utils/constants.go new file mode 100644 index 000000000..68ef53fec --- /dev/null +++ b/x/dymns/utils/constants.go @@ -0,0 +1,12 @@ +package utils + +const ( + // MaxDymNameLength is the maximum length allowed for Dym-Name. + MaxDymNameLength = 20 + + // MaxSubNameLength is the maximum length allowed for Sub-Name part of Dym-Name. + MaxSubNameLength = 66 + + // MaxAliasLength is the maximum length allowed for Alias. + MaxAliasLength = 32 +) diff --git a/x/dymns/utils/dym_name.go b/x/dymns/utils/dym_name.go new file mode 100644 index 000000000..721df135a --- /dev/null +++ b/x/dymns/utils/dym_name.go @@ -0,0 +1,68 @@ +package utils + +import ( + "regexp" + "strings" +) + +// patternValidateDymNameStep1 is a regex pattern for validating Dym-Name in first step. +var patternValidateDymNameStep1 = regexp.MustCompile(`^[a-z\d]+([a-z\d_-]*[a-z\d]+)?$`) + +// IsValidDymName returns true if the given string is a valid Dym-Name. +// Read code and the comments for more details. +func IsValidDymName(dymName string) bool { + if len(dymName) > MaxDymNameLength { + return false + } + + if dymName == "" { + return false + } + + // step 1: check if the dym name is valid with following rules + // 1. only lowercase letters, digits, hyphens, and underscores are allowed + // 2. the first character must be a letter or a digit + // 3. the last character must be a letter or a digit + + if !patternValidateDymNameStep1.MatchString(dymName) { + return false + } + + // step 2: check if the dym name does not contain consecutive hyphens or underscores + for i := 0; i < len(dymName)-1; i++ { + if (dymName[i] == '-' || dymName[i] == '_') && (dymName[i+1] == '-' || dymName[i+1] == '_') { + return false + } + } + + return true +} + +// IsValidSubDymName returns true if the given string is a valid Sub-Name of Dym-Name. +func IsValidSubDymName(subDymName string) bool { + if subDymName == "" { + // allowed to be empty, means no sub name + return true + } + + if len(subDymName) > MaxSubNameLength { + return false + } + + if strings.HasPrefix(subDymName, ".") || strings.HasSuffix(subDymName, ".") { + return false + } + + spl := strings.Split(subDymName, ".") + for _, s := range spl { + if s == "" { + return false + } + + if !IsValidDymName(s) { + return false + } + } + + return true +} diff --git a/x/dymns/utils/dym_name_test.go b/x/dymns/utils/dym_name_test.go new file mode 100644 index 000000000..fb1d9d28e --- /dev/null +++ b/x/dymns/utils/dym_name_test.go @@ -0,0 +1,319 @@ +package utils + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +//goland:noinspection SpellCheckingInspection +func TestIsValidDymName(t *testing.T) { + t.Run("maximum accepted length is 20", func(t *testing.T) { + require.True(t, IsValidDymName("12345678901234567890")) + require.False(t, IsValidDymName("123456789012345678901")) + }) + + t.Run("not allow empty", func(t *testing.T) { + require.False(t, IsValidDymName("")) + }) + + t.Run("single character", func(t *testing.T) { + for i := 'a'; i <= 'z'; i++ { + require.Truef(t, IsValidDymName(string(i)), "failed for single char '%c'", i) + } + for i := 'A'; i <= 'Z'; i++ { + require.Falsef(t, IsValidDymName(string(i)), "should not accept '%c'", i) + } + for i := '0'; i <= '9'; i++ { + require.Truef(t, IsValidDymName(string(i)), "failed for single char '%c'", i) + } + require.False(t, IsValidDymName("-"), "should not accept single dash") + require.False(t, IsValidDymName("_"), "should not accept single underscore") + }) + + t.Run("not starts or ends with dash or underscore", func(t *testing.T) { + for _, prototype := range []string{"a", "aa", "aaa", "8"} { + check := func(dymName string) { + require.Falsef(t, IsValidDymName(dymName), "should not accept '%s'", dymName) + } + check(prototype + "-") + check(prototype + "_") + check("-" + prototype) + check("_" + prototype) + } + }) + + tests := []struct { + dymName string + invalid bool + }{ + { + dymName: "a", + }, + { + dymName: "aa", + }, + { + dymName: "9", + }, + { + dymName: "9999", + }, + { + dymName: "-", + invalid: true, + }, + { + dymName: "_", + invalid: true, + }, + { + dymName: "9-", + invalid: true, + }, + { + dymName: "9_", + invalid: true, + }, + { + dymName: "-9", + invalid: true, + }, + { + dymName: "_9", + invalid: true, + }, + { + dymName: "_9", + invalid: true, + }, + { + dymName: "a_9", + }, + { + dymName: "a-9", + }, + { + dymName: "a--9", + invalid: true, + }, + { + dymName: "a__9", + invalid: true, + }, + { + dymName: "a.dym", + invalid: true, + }, + { + dymName: "a a", + invalid: true, + }, + } + for _, tt := range tests { + t.Run(tt.dymName, func(t *testing.T) { + if tt.invalid { + require.Falsef(t, IsValidDymName(tt.dymName), "should not accept '%s'", tt.dymName) + } else { + require.Truef(t, IsValidDymName(tt.dymName), "should accept '%s'", tt.dymName) + } + }) + } + + t.Run("not allow hex address", func(t *testing.T) { + require.False(t, IsValidDymName("0x1234567890123456789012345678901234567890")) + require.False(t, IsValidDymName("0x1234567890123456789012345678901234567890123456789012345678901234")) + require.False(t, IsValidDymName("dym1zg69v7yszg69v7yszg69v7yszg69v7ys8xdv96")) + require.False(t, IsValidDymName("dym1zg69v7yszg69v7yszg69v7yszg69v7yszg69v7yszg69v7yszg6qrz80ul")) + }) +} + +func TestIsValidSubDymName(t *testing.T) { + t.Run("maximum accepted length is 20", func(t *testing.T) { + require.True(t, IsValidSubDymName("12345678901234567890")) + require.False(t, IsValidSubDymName("123456789012345678901.12345678901234567890")) + require.False(t, IsValidSubDymName("1234567890123456789012345678901234567890123456789012345678901234567.12345678901234567890")) + }) + + t.Run("allow empty", func(t *testing.T) { + require.True(t, IsValidSubDymName("")) + }) + + t.Run("single character", func(t *testing.T) { + for i := 'a'; i <= 'z'; i++ { + require.Truef(t, IsValidSubDymName(string(i)), "failed for single char '%c'", i) + } + for i := 'A'; i <= 'Z'; i++ { + require.Falsef(t, IsValidSubDymName(string(i)), "should not accept '%c'", i) + } + for i := '0'; i <= '9'; i++ { + require.Truef(t, IsValidSubDymName(string(i)), "failed for single char '%c'", i) + } + require.False(t, IsValidSubDymName("-"), "should not accept single dash") + require.False(t, IsValidSubDymName("_"), "should not accept single underscore") + }) + + t.Run("not starts or ends with dash or underscore or dot", func(t *testing.T) { + for _, prototype := range []string{"a", "aa", "aaa", "8"} { + check := func(dymName string) { + require.Falsef(t, IsValidSubDymName(dymName), "should not accept '%s'", dymName) + } + check(prototype + "-") + check(prototype + "_") + check("-" + prototype) + check("_" + prototype) + check(prototype + ".") + check("." + prototype) + } + }) + + tests := []struct { + subDymName string + invalid bool + }{ + { + subDymName: "a", + }, + { + subDymName: "a.a", + }, + { + subDymName: "aa", + }, + { + subDymName: "aa.aa", + }, + { + subDymName: "9", + }, + { + subDymName: "9.9", + }, + { + subDymName: "9999", + }, + { + subDymName: "9999.9999", + }, + { + subDymName: "-", + invalid: true, + }, + { + subDymName: "-.-", + invalid: true, + }, + { + subDymName: "-.a", + invalid: true, + }, + { + subDymName: "_", + invalid: true, + }, + { + subDymName: "_._", + invalid: true, + }, + { + subDymName: "a._", + invalid: true, + }, + { + subDymName: "9-", + invalid: true, + }, + { + subDymName: "a.9-", + invalid: true, + }, + { + subDymName: "9_", + invalid: true, + }, + { + subDymName: "9_.a", + invalid: true, + }, + { + subDymName: "-9", + invalid: true, + }, + { + subDymName: "-9.a", + invalid: true, + }, + { + subDymName: "_9", + invalid: true, + }, + { + subDymName: "a._9", + invalid: true, + }, + { + subDymName: "_9", + invalid: true, + }, + { + subDymName: "a_9", + }, + { + subDymName: "a_9.a_9", + }, + { + subDymName: "a-9", + }, + { + subDymName: "a-9.a-9", + }, + { + subDymName: "a--9", + invalid: true, + }, + { + subDymName: "a--9.a", + invalid: true, + }, + { + subDymName: "a__9", + invalid: true, + }, + { + subDymName: "a.a__9", + invalid: true, + }, + { + subDymName: "a.dym", + }, + { + subDymName: "a a", + invalid: true, + }, + { + subDymName: "a a.a", + invalid: true, + }, + { + subDymName: "aa..a", + invalid: true, + }, + { + subDymName: "aa. .a", + invalid: true, + }, + { + subDymName: "a .a", + invalid: true, + }, + } + for _, tt := range tests { + t.Run(tt.subDymName, func(t *testing.T) { + if tt.invalid { + require.Falsef(t, IsValidSubDymName(tt.subDymName), "should not accept '%s'", tt.subDymName) + } else { + require.Truef(t, IsValidSubDymName(tt.subDymName), "should accept '%s'", tt.subDymName) + } + }) + } +} diff --git a/x/dymns/utils/map.go b/x/dymns/utils/map.go new file mode 100644 index 000000000..1817e0ea7 --- /dev/null +++ b/x/dymns/utils/map.go @@ -0,0 +1,15 @@ +package utils + +import "sort" + +// GetSortedStringKeys returns the sorted keys of a map[string]V. +func GetSortedStringKeys[V any](m map[string]V) []string { + keys := make([]string, 0, len(m)) + for k := range m { + keys = append(keys, k) + } + sort.Slice(keys, func(i, j int) bool { + return keys[i] < keys[j] + }) + return keys +} diff --git a/x/dymns/utils/map_test.go b/x/dymns/utils/map_test.go new file mode 100644 index 000000000..baee981ff --- /dev/null +++ b/x/dymns/utils/map_test.go @@ -0,0 +1,38 @@ +package utils + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestGetSortedStringKeys(t *testing.T) { + t.Run("allow input is empty/nil", func(t *testing.T) { + require.Empty(t, GetSortedStringKeys(map[string]bool{})) + require.Empty(t, GetSortedStringKeys(map[string]string{})) + require.Empty(t, GetSortedStringKeys(map[string]int{})) + var nilMap map[string]bool + require.Empty(t, GetSortedStringKeys(nilMap)) + }) + + t.Run("sorted keys are in ascending order", func(t *testing.T) { + require.Equal(t, []string{"a", "b", "c", "d"}, GetSortedStringKeys(map[string]bool{ + "b": true, + "a": true, + "c": true, + "d": false, + })) + require.Equal(t, []string{"a", "b", "c", "d"}, GetSortedStringKeys(map[string]int64{ + "b": -1, + "a": 1, + "c": 2, + "d": 0, + })) + require.Equal(t, []string{"a", "b", "c", "d"}, GetSortedStringKeys(map[string]any{ + "b": nil, + "a": nil, + "c": 2, + "d": "f", + })) + }) +} diff --git a/x/incentives/client/cli/query_test.go b/x/incentives/client/cli/query_test.go index dc7745065..b044bf137 100644 --- a/x/incentives/client/cli/query_test.go +++ b/x/incentives/client/cli/query_test.go @@ -28,16 +28,17 @@ type QueryTestSuite struct { // SetupLockAndGauge creates both a lock and a gauge. func (suite *QueryTestSuite) CreateDefaultRollapp() string { alice := sdk.AccAddress("addr1---------------") - // suite.FundAcc(alice, sdk.NewCoins(rollapptypes.DefaultRegistrationFee)) TODO: enable after x/dymns hooks are wired msgCreateRollapp := rollapptypes.MsgCreateRollapp{ Creator: alice.String(), RollappId: urand.RollappID(), Bech32Prefix: strings.ToLower(tmrand.Str(3)), - Alias: strings.ToLower(tmrand.Str(3)), + Alias: strings.ToLower(tmrand.Str(7)), VmType: rollapptypes.Rollapp_EVM, } + suite.FundForAliasRegistration(msgCreateRollapp) + msgServer := rollapp.NewMsgServerImpl(*suite.App.RollappKeeper) _, err := msgServer.CreateRollapp(suite.Ctx, &msgCreateRollapp) suite.Require().NoError(err) diff --git a/x/incentives/keeper/suite_test.go b/x/incentives/keeper/suite_test.go index b964af264..7015ca1ee 100644 --- a/x/incentives/keeper/suite_test.go +++ b/x/incentives/keeper/suite_test.go @@ -206,18 +206,18 @@ func (suite *KeeperTestSuite) SetupLockAndGauge(isPerpetual bool) (sdk.AccAddres // SetupLockAndGauge creates both a lock and a gauge. func (suite *KeeperTestSuite) CreateDefaultRollapp(addr sdk.AccAddress) string { - // suite.FundAcc(addr, sdk.NewCoins(rollapptypes.DefaultRegistrationFee)) TODO: enable after x/dymns hooks are wired - msgCreateRollapp := rollapptypes.MsgCreateRollapp{ Creator: addr.String(), RollappId: urand.RollappID(), Bech32Prefix: strings.ToLower(tmrand.Str(3)), GenesisChecksum: "checksum", InitialSequencer: addr.String(), - Alias: "alias", + Alias: strings.ToLower(tmrand.Str(7)), VmType: rollapptypes.Rollapp_EVM, } + suite.FundForAliasRegistration(msgCreateRollapp) + msgServer := rollapp.NewMsgServerImpl(*suite.App.RollappKeeper) _, err := msgServer.CreateRollapp(suite.Ctx, &msgCreateRollapp) suite.Require().NoError(err) diff --git a/x/rollapp/keeper/msg_server_create_rollapp_test.go b/x/rollapp/keeper/msg_server_create_rollapp_test.go index 6a7d41432..e25ea61a1 100644 --- a/x/rollapp/keeper/msg_server_create_rollapp_test.go +++ b/x/rollapp/keeper/msg_server_create_rollapp_test.go @@ -3,6 +3,8 @@ package keeper_test import ( "strings" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cometbft/cometbft/libs/rand" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/dymensionxyz/sdk-utils/utils/urand" @@ -17,9 +19,12 @@ func (suite *RollappTestSuite) TestCreateRollapp() { } func (suite *RollappTestSuite) TestCreateRollappUnauthorizedRollappCreator() { - suite.T().Skip() // TODO: enable after x/dymns hook is wired suite.SetupTest() - suite.createRollappWithCreatorAndVerify(types.ErrFeePayment, bob) // bob is broke + + _ = types.ErrFeePayment // this error is not being used anywhere so keep it used here to pass lint + // TODO: ^^^^^^^^^^^^^^ use this error or delete it + + suite.createRollappWithCreatorAndVerify(sdkerrors.ErrInsufficientFunds, bob, false) // bob is broke } func (suite *RollappTestSuite) TestCreateRollappWithBechGenesisSum() { @@ -30,7 +35,7 @@ func (suite *RollappTestSuite) TestCreateRollappWithBechGenesisSum() { Creator: alice, RollappId: "rollapp_1234-1", InitialSequencer: sample.AccAddress(), - Alias: "Rollapp", + Alias: "rollapp", VmType: types.Rollapp_EVM, Bech32Prefix: "rol", GenesisChecksum: "checksum", @@ -47,7 +52,7 @@ func (suite *RollappTestSuite) TestCreateRollappAlreadyExists() { Creator: alice, RollappId: "rollapp_1234-1", InitialSequencer: sample.AccAddress(), - Alias: "Rollapp", + Alias: "rollapp", VmType: types.Rollapp_EVM, } @@ -90,13 +95,15 @@ func (suite *RollappTestSuite) TestCreateRollappAlreadyExists() { RollappId: test.rollappId, Bech32Prefix: "rol", VmType: types.Rollapp_EVM, - Alias: strings.ToLower(rand.Str(3)), + Alias: strings.ToLower(rand.Str(7)), } if test.malleate != nil { test.malleate() } + suite.FundForAliasRegistration(newRollapp) + _, err := suite.msgServer.CreateRollapp(goCtx, &newRollapp) suite.Require().ErrorIs(err, test.expErr) }) @@ -199,11 +206,13 @@ func (suite *RollappTestSuite) TestForkChainId() { InitialSequencer: sample.AccAddress(), Bech32Prefix: "rol", GenesisChecksum: "checksum", - Alias: "Rollapp1", + Alias: "rollapp1", VmType: types.Rollapp_EVM, Metadata: &mockRollappMetadata, } + suite.FundForAliasRegistration(rollappMsg) + _, err := suite.msgServer.CreateRollapp(goCtx, &rollappMsg) suite.Require().NoError(err) rollapp, found := suite.App.RollappKeeper.GetRollapp(suite.Ctx, rollappMsg.RollappId) @@ -217,10 +226,13 @@ func (suite *RollappTestSuite) TestForkChainId() { InitialSequencer: sample.AccAddress(), Bech32Prefix: "rol", GenesisChecksum: "checksum1", - Alias: "Rollapp2", + Alias: "rollapp2", VmType: types.Rollapp_EVM, Metadata: &mockRollappMetadata, } + + suite.FundForAliasRegistration(rollappMsg2) + _, err = suite.msgServer.CreateRollapp(goCtx, &rollappMsg2) if test.valid { suite.Require().NoError(err) @@ -263,6 +275,7 @@ func (suite *RollappTestSuite) TestOverwriteEIP155Key() { Alias: "alias", VmType: types.Rollapp_EVM, } + suite.FundForAliasRegistration(rollapp) _, err := suite.msgServer.CreateRollapp(goCtx, &rollapp) suite.Require().NoError(err) @@ -285,6 +298,7 @@ func (suite *RollappTestSuite) TestOverwriteEIP155Key() { Alias: "alias", VmType: types.Rollapp_EVM, } + suite.FundForAliasRegistration(rollapp) _, err = suite.msgServer.CreateRollapp(goCtx, &badRollapp) // it should not be possible to register rollapp name with extra space suite.Require().ErrorIs(err, types.ErrRollappExists) @@ -314,10 +328,12 @@ func (suite *RollappTestSuite) createRollapp(expectedErr error) { } func (suite *RollappTestSuite) createRollappAndVerify(expectedErr error) types.RollappSummary { - return suite.createRollappWithCreatorAndVerify(expectedErr, alice) + return suite.createRollappWithCreatorAndVerify(expectedErr, alice, true) } -func (suite *RollappTestSuite) createRollappWithCreatorAndVerify(expectedErr error, creator string) types.RollappSummary { +func (suite *RollappTestSuite) createRollappWithCreatorAndVerify( + expectedErr error, creator string, fundAccount bool, +) types.RollappSummary { goCtx := sdk.WrapSDKContext(suite.Ctx) // generate sequencer address address := sample.AccAddress() @@ -328,10 +344,13 @@ func (suite *RollappTestSuite) createRollappWithCreatorAndVerify(expectedErr err InitialSequencer: address, Bech32Prefix: "rol", GenesisChecksum: "checksum", - Alias: "alias", + Alias: strings.ToLower(rand.Str(7)), VmType: types.Rollapp_EVM, Metadata: &mockRollappMetadata, } + if fundAccount { + suite.FundForAliasRegistration(rollapp) + } // rollappExpect is the expected result of creating rollapp rollappExpect := types.Rollapp{ RollappId: rollapp.GetRollappId(), diff --git a/x/rollapp/keeper/msg_server_update_rollapp_test.go b/x/rollapp/keeper/msg_server_update_rollapp_test.go index b439a1f87..4456d5545 100644 --- a/x/rollapp/keeper/msg_server_update_rollapp_test.go +++ b/x/rollapp/keeper/msg_server_update_rollapp_test.go @@ -165,7 +165,7 @@ func (suite *RollappTestSuite) TestCreateAndUpdateRollapp() { const rollappId = "rollapp_1234-1" // 1. register rollapp - _, err := suite.msgServer.CreateRollapp(suite.Ctx, &types.MsgCreateRollapp{ + msg := types.MsgCreateRollapp{ RollappId: rollappId, Creator: alice, GenesisChecksum: "", @@ -173,7 +173,9 @@ func (suite *RollappTestSuite) TestCreateAndUpdateRollapp() { Alias: "default", VmType: types.Rollapp_EVM, Bech32Prefix: "rol", - }) + } + suite.FundForAliasRegistration(msg) + _, err := suite.msgServer.CreateRollapp(suite.Ctx, &msg) suite.Require().NoError(err) // 2. try to register sequencer (not initial) - should fail because rollapp is not sealed diff --git a/x/rollapp/simulation/create_rollapp.go b/x/rollapp/simulation/create_rollapp.go index ef9766bec..7811928e7 100644 --- a/x/rollapp/simulation/create_rollapp.go +++ b/x/rollapp/simulation/create_rollapp.go @@ -34,7 +34,7 @@ func SimulateMsgCreateRollapp(ak simulationtypes.AccountKeeper, bk simulationtyp RollappId: rollappId, InitialSequencer: sample.AccAddress(), Bech32Prefix: "rol", - Alias: "Rollapp", + Alias: "rollapp", Metadata: &types.RollappMetadata{ Website: "https://dymension.xyz", Description: "Sample description", diff --git a/x/rollapp/types/codec.go b/x/rollapp/types/codec.go index 6b08c6e81..334d8c188 100644 --- a/x/rollapp/types/codec.go +++ b/x/rollapp/types/codec.go @@ -12,7 +12,7 @@ import ( func RegisterCodec(cdc *codec.LegacyAmino) { cdc.RegisterConcrete(&MsgCreateRollapp{}, "rollapp/CreateRollapp", nil) cdc.RegisterConcrete(&MsgUpdateRollappInformation{}, "rollapp/UpdateRollappInformation", nil) - cdc.RegisterConcrete(&MsgTransferOwnership{}, "rollapp/TransferOwnership", nil) + cdc.RegisterConcrete(&MsgTransferOwnership{}, "rollapp/TransferDymNameOwnership", nil) cdc.RegisterConcrete(&MsgUpdateState{}, "rollapp/UpdateState", nil) }