From 6e4788b09f8c5bd7fd57dd59717e650b23f49778 Mon Sep 17 00:00:00 2001 From: angelorc Date: Tue, 2 Sep 2025 18:09:24 +0200 Subject: [PATCH 01/15] feat(nft): implement NFT minting functionality and related types - Added MintNFT method in keeper to handle NFT minting. - Introduced error handling for collection existence and minter validation. - Created expected_keeper interface for account and bank operations. - Defined new error types for collection management. - Added protobuf definitions for Collection and Nft types. - Established key prefixes for collections and supply management. --- app/keepers/keepers.go | 12 + app/keepers/keys.go | 2 + proto/bitsong/nft/v1beta1/nft.proto | 27 + x/nft/keeper/collection.go | 105 ++++ x/nft/keeper/collection_test.go | 21 + x/nft/keeper/keeper.go | 45 ++ x/nft/keeper/keeper_test.go | 237 ++++++++ x/nft/keeper/nft.go | 65 +++ x/nft/types/errors.go | 8 + x/nft/types/expected_keeper.go | 27 + x/nft/types/keys.go | 14 + x/nft/types/nft.pb.go | 848 ++++++++++++++++++++++++++++ 12 files changed, 1411 insertions(+) create mode 100644 proto/bitsong/nft/v1beta1/nft.proto create mode 100644 x/nft/keeper/collection.go create mode 100644 x/nft/keeper/collection_test.go create mode 100644 x/nft/keeper/keeper.go create mode 100644 x/nft/keeper/keeper_test.go create mode 100644 x/nft/keeper/nft.go create mode 100644 x/nft/types/errors.go create mode 100644 x/nft/types/expected_keeper.go create mode 100644 x/nft/types/keys.go create mode 100644 x/nft/types/nft.pb.go diff --git a/app/keepers/keepers.go b/app/keepers/keepers.go index 219868f9..27218cb4 100644 --- a/app/keepers/keepers.go +++ b/app/keepers/keepers.go @@ -26,6 +26,8 @@ import ( "github.com/bitsongofficial/go-bitsong/x/fantoken" fantokenkeeper "github.com/bitsongofficial/go-bitsong/x/fantoken/keeper" fantokentypes "github.com/bitsongofficial/go-bitsong/x/fantoken/types" + nftkeeper "github.com/bitsongofficial/go-bitsong/x/nft/keeper" + nfttypes "github.com/bitsongofficial/go-bitsong/x/nft/types" "github.com/bitsongofficial/go-bitsong/x/smart-account/authenticator" smartaccountkeeper "github.com/bitsongofficial/go-bitsong/x/smart-account/keeper" smartaccounttypes "github.com/bitsongofficial/go-bitsong/x/smart-account/types" @@ -113,6 +115,7 @@ var maccPerms = map[string][]string{ govtypes.ModuleName: {authtypes.Burner}, ibctransfertypes.ModuleName: {authtypes.Minter, authtypes.Burner}, fantokentypes.ModuleName: {authtypes.Minter, authtypes.Burner}, + nfttypes.ModuleName: {authtypes.Minter, authtypes.Burner}, wasmtypes.ModuleName: {authtypes.Burner}, protocolpooltypes.ModuleName: nil, protocolpooltypes.ProtocolPoolEscrowAccount: nil, @@ -149,6 +152,7 @@ type AppKeepers struct { ICQKeeper *icqkeeper.Keeper EvidenceKeeper evidencekeeper.Keeper FanTokenKeeper fantokenkeeper.Keeper + NftKeeper nftkeeper.Keeper WasmKeeper wasmkeeper.Keeper CadenceKeeper cadencekeeper.Keeper IBCFeeKeeper ibcfeekeeper.Keeper @@ -406,6 +410,14 @@ func NewAppKeepers( BlockedAddrs(), ) + appKeepers.NftKeeper = nftkeeper.NewKeeper( + appCodec, + runtime.NewKVStoreService(appKeepers.keys[nfttypes.StoreKey]), + appKeepers.AccountKeeper, + appKeepers.BankKeeper, + bApp.Logger(), + ) + // Stargate Queries acceptedStargateQueries := wasmkeeper.AcceptedQueries{ // ibc diff --git a/app/keepers/keys.go b/app/keepers/keys.go index 38128e93..d5091ce5 100644 --- a/app/keepers/keys.go +++ b/app/keepers/keys.go @@ -2,6 +2,7 @@ package keepers import ( storetypes "cosmossdk.io/store/types" + nfttypes "github.com/bitsongofficial/go-bitsong/x/nft/types" "cosmossdk.io/x/feegrant" wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types" @@ -57,6 +58,7 @@ func (appKeepers *AppKeepers) GenerateKeys() { wasmtypes.StoreKey, icqtypes.StoreKey, fantokentypes.StoreKey, + nfttypes.StoreKey, cadencetypes.StoreKey, smartaccounttypes.StoreKey, protocolpooltypes.StoreKey, diff --git a/proto/bitsong/nft/v1beta1/nft.proto b/proto/bitsong/nft/v1beta1/nft.proto new file mode 100644 index 00000000..228c499c --- /dev/null +++ b/proto/bitsong/nft/v1beta1/nft.proto @@ -0,0 +1,27 @@ +syntax = "proto3"; +package bitsong.nft.v1beta1; + +option go_package = "github.com/bitsongofficial/go-bitsong/x/nft/types"; + +message Collection { + string symbol = 1; + string name = 2; + string description = 3; + string uri = 4; + string creator = 5; + string minter = 6; + uint64 num_tokens = 7; + // bool is_mutable + // update_autority (who can update name, description and uri if is_mutable = true) +} + +message Nft { + string name = 1; + string description = 2; + string uri = 3; + // string owner = 5; + // seller_fee_bps + // payment_address + // bool is_mutable + // update_autority (who can update name, description and uri if is_mutable = true) +} \ No newline at end of file diff --git a/x/nft/keeper/collection.go b/x/nft/keeper/collection.go new file mode 100644 index 00000000..d07e0bac --- /dev/null +++ b/x/nft/keeper/collection.go @@ -0,0 +1,105 @@ +package keeper + +import ( + "fmt" + + "cosmossdk.io/math" + "github.com/bitsongofficial/go-bitsong/x/nft/types" + tmcrypto "github.com/cometbft/cometbft/crypto" + sdk "github.com/cosmos/cosmos-sdk/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" +) + +func (k Keeper) CreateCollection(ctx sdk.Context, creator sdk.AccAddress, coll types.Collection) (denom string, err error) { + denom, err = k.validateCollectionDenom(ctx, creator, coll.Symbol) + if err != nil { + return "", err + } + + // TODO: charge fee + + metadata := banktypes.Metadata{ + DenomUnits: []*banktypes.DenomUnit{{ + Denom: denom, + Exponent: 0, + }}, + Base: denom, + Name: coll.Name, + Description: coll.Description, + Symbol: coll.Symbol, + Display: coll.Symbol, + URI: coll.Uri, + } + + k.bk.SetDenomMetaData(ctx, metadata) + + if err := k.setCollection(ctx, denom, coll); err != nil { + return "", err + } + + return denom, nil +} + +func (k Keeper) GetSupply(ctx sdk.Context, denom string) math.Int { + supply, err := k.Supply.Get(ctx, denom) + if err != nil { + return math.ZeroInt() + } + + return supply +} + +func (k Keeper) HasSupply(ctx sdk.Context, denom string) bool { + has, err := k.Supply.Has(ctx, denom) + return has && err == nil +} + +func (k Keeper) setSupply(ctx sdk.Context, denom string, supply math.Int) error { + return k.Supply.Set(ctx, denom, supply) +} + +func (k Keeper) incrementSupply(ctx sdk.Context, denom string) error { + supply := k.GetSupply(ctx, denom) + supply = supply.Add(math.NewInt(1)) + + return k.setSupply(ctx, denom, supply) +} + +func (k Keeper) createCollectionDenom(creator sdk.AccAddress, symbol string) string { + // TODO: if necessary add a salt field + + bz := []byte(fmt.Sprintf("%s%s", creator.String(), symbol)) + return "nft" + tmcrypto.AddressHash(bz).String() +} + +func (k Keeper) validateCollectionDenom(ctx sdk.Context, creator sdk.AccAddress, symbol string) (string, error) { + denom := k.createCollectionDenom(creator, symbol) + + if err := sdk.ValidateDenom(denom); err != nil { + return "", err + } + + if k.bk.HasSupply(ctx, symbol) { + return "", fmt.Errorf("denom %s already exists", denom) + } + + _, exists := k.bk.GetDenomMetaData(ctx, denom) + if exists { + return "", types.ErrCollectionAlreadyExists + } + + return denom, nil +} + +func (k Keeper) setCollection(ctx sdk.Context, denom string, coll types.Collection) error { + return k.Collections.Set(ctx, denom, coll) +} + +func (k Keeper) getCollection(ctx sdk.Context, denom string) (types.Collection, error) { + coll, err := k.Collections.Get(ctx, denom) + if err != nil { + return types.Collection{}, types.ErrCollectionNotFound + } + + return coll, nil +} diff --git a/x/nft/keeper/collection_test.go b/x/nft/keeper/collection_test.go new file mode 100644 index 00000000..89416850 --- /dev/null +++ b/x/nft/keeper/collection_test.go @@ -0,0 +1,21 @@ +package keeper + +import ( + "testing" + + "github.com/cometbft/cometbft/crypto/tmhash" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +func TestKeeper_createCollectionDenom(t *testing.T) { + creator := sdk.AccAddress(tmhash.SumTruncated([]byte("creator"))) + symbol := "MYNFT" + + expectedDenom := "nftF1D9FE89CCE1FAD3F83FFCBA6F496EFD30855C42" + k := Keeper{} + + denom := k.createCollectionDenom(creator, symbol) + if denom != expectedDenom { + t.Errorf("expected %s, got %s", expectedDenom, denom) + } +} diff --git a/x/nft/keeper/keeper.go b/x/nft/keeper/keeper.go new file mode 100644 index 00000000..da7550b1 --- /dev/null +++ b/x/nft/keeper/keeper.go @@ -0,0 +1,45 @@ +package keeper + +import ( + "cosmossdk.io/collections" + "cosmossdk.io/core/address" + "cosmossdk.io/core/store" + "cosmossdk.io/log" + "cosmossdk.io/math" + "github.com/bitsongofficial/go-bitsong/x/nft/types" + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +type Keeper struct { + cdc codec.BinaryCodec + storeService store.KVStoreService + ac address.Codec + bk types.BankKeeper + logger log.Logger + + Schema collections.Schema + Collections collections.Map[string, types.Collection] + Supply collections.Map[string, math.Int] +} + +func NewKeeper(cdc codec.BinaryCodec, storeService store.KVStoreService, ak types.AccountKeeper, bk types.BankKeeper, logger log.Logger) Keeper { + if addr := ak.GetModuleAddress(types.ModuleName); addr == nil { + panic("the " + types.ModuleName + " module account has not been set") + } + + logger = logger.With(log.ModuleKey, "x/"+types.ModuleName) + + sb := collections.NewSchemaBuilder(storeService) + + return Keeper{ + cdc: cdc, + storeService: storeService, + ac: ak.AddressCodec(), + bk: bk, + logger: logger, + // TODO: fix the store once we add queries + Collections: collections.NewMap(sb, types.CollectionsPrefix, "collections", collections.StringKey, codec.CollValue[types.Collection](cdc)), + Supply: collections.NewMap(sb, types.SupplyPrefix, "supply", collections.StringKey, sdk.IntValue), + } +} diff --git a/x/nft/keeper/keeper_test.go b/x/nft/keeper/keeper_test.go new file mode 100644 index 00000000..2698b6a2 --- /dev/null +++ b/x/nft/keeper/keeper_test.go @@ -0,0 +1,237 @@ +package keeper_test + +import ( + "fmt" + "testing" + + "cosmossdk.io/math" + simapp "github.com/bitsongofficial/go-bitsong/app" + apptesting "github.com/bitsongofficial/go-bitsong/app/testing" + fantokentypes "github.com/bitsongofficial/go-bitsong/x/fantoken/types" + "github.com/bitsongofficial/go-bitsong/x/nft/keeper" + "github.com/bitsongofficial/go-bitsong/x/nft/types" + "github.com/cometbft/cometbft/crypto/tmhash" + sdk "github.com/cosmos/cosmos-sdk/types" + bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" + types2 "github.com/cosmos/cosmos-sdk/x/bank/types" + "github.com/stretchr/testify/suite" +) + +var ( + creator = sdk.AccAddress(tmhash.SumTruncated([]byte("creator"))) + owner = sdk.AccAddress(tmhash.SumTruncated([]byte("owner"))) + initAmt = math.NewIntFromUint64(1000000000) + initCoin = sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, initAmt)} +) + +type KeeperTestSuite struct { + apptesting.KeeperTestHelper + + ctx sdk.Context + bk bankkeeper.Keeper + keeper keeper.Keeper + app *simapp.BitsongApp +} + +func (suite *KeeperTestSuite) SetupTest() { + suite.Setup() + + app := suite.App + suite.keeper = app.NftKeeper + suite.bk = app.BankKeeper + suite.App = app + suite.ctx = suite.Ctx + + // init tokens to addr + err := suite.bk.MintCoins(suite.ctx, fantokentypes.ModuleName, initCoin) + suite.NoError(err) + err = suite.bk.SendCoinsFromModuleToAccount(suite.ctx, fantokentypes.ModuleName, creator, initCoin) + suite.NoError(err) +} + +func TestKeeperSuite(t *testing.T) { + suite.Run(t, new(KeeperTestSuite)) +} + +func (suite *KeeperTestSuite) TestCreateCollection() { + testCollection := types.Collection{ + Name: "My NFT Collection", + Symbol: "MYNFT", + Description: "My NFT Collection Description", + Uri: "ipfs://my-nft-collection-metadata.json", + } + + _, err := suite.keeper.CreateCollection(suite.ctx, creator, testCollection) + suite.NoError(err) + + _, err = suite.keeper.CreateCollection(suite.ctx, creator, testCollection) + suite.Error(err) +} + +func (suite *KeeperTestSuite) TestMintNFT() { + testCollection := types.Collection{ + Name: "My NFT Collection", + Symbol: "MYNFT", + Description: "My NFT Collection Description", + Uri: "ipfs://my-nft-collection-metadata.json", + Minter: creator.String(), + } + + collectionDenom, err := suite.keeper.CreateCollection(suite.ctx, creator, testCollection) + suite.NoError(err) + fmt.Println("collectionDenom:", collectionDenom) + + resp, err := suite.bk.Balance(suite.ctx, &types2.QueryBalanceRequest{ + Address: creator.String(), + Denom: collectionDenom, + }) + suite.NoError(err) + suite.Equal(int64(0), resp.Balance.Amount.Int64()) + + nft := types.Nft{ + Name: "My First NFT", + Description: "This is my first NFT", + Uri: "ipfs://my-first-nft-metadata.json", + } + + nft1denom, err := suite.keeper.MintNFT(suite.ctx, collectionDenom, creator, owner, nft) + suite.NoError(err) + fmt.Println("nft1denom:", nft1denom) + + supply := suite.keeper.GetSupply(suite.ctx, collectionDenom) + suite.Equal(math.NewInt(1), supply) + + resp, err = suite.bk.Balance(suite.ctx, &types2.QueryBalanceRequest{ + Address: owner.String(), + Denom: nft1denom, + }) + suite.NoError(err) + suite.Equal(int64(1), resp.Balance.Amount.Int64()) + + nft2denom, err := suite.keeper.MintNFT(suite.ctx, collectionDenom, creator, owner, nft) + suite.NoError(err) + fmt.Println("nft2denom:", nft2denom) + + supply = suite.keeper.GetSupply(suite.ctx, collectionDenom) + suite.Equal(math.NewInt(2), supply) + + resp, err = suite.bk.Balance(suite.ctx, &types2.QueryBalanceRequest{ + Address: owner.String(), + Denom: nft2denom, + }) + suite.NoError(err) + suite.Equal(int64(1), resp.Balance.Amount.Int64()) + + balances, err := suite.bk.AllBalances(suite.ctx, &types2.QueryAllBalancesRequest{ + Address: owner.String(), + }) + suite.NoError(err) + suite.Equal(2, len(balances.Balances)) +} + +type MintNFTTestCase struct { + name string // A descriptive name for the test case + collection types.Collection + nftToMint types.Nft + minter sdk.AccAddress + owner sdk.AccAddress + expectErr bool // Do we expect an error during minting? + expectedSupply int64 // Expected supply of the collection after this mint + expectedBalanceForNewNFT int64 // Expected balance of the *specific* new NFT + expectedTotalOwnerBalances int // Expected total number of different assets the owner has +} + +func (suite *KeeperTestSuite) TestMintNFT_Advanced() { + collection := types.Collection{ + Name: "My NFT Collection", + Symbol: "MYNFT", + Description: "My NFT Collection Description", + Uri: "ipfs://my-nft-collection-metadata.json", + Minter: creator.String(), + } + + collectionDenom, err := suite.keeper.CreateCollection(suite.ctx, creator, collection) + suite.Require().NoError(err, "initial collection creation should succeed") + fmt.Println("collectionDenom:", collectionDenom) + + // Define the test cases + testCases := []MintNFTTestCase{ + { + name: "Successful first mint", + collection: collection, + nftToMint: types.Nft{ + Name: "My First NFT", + Description: "This is my first NFT", + Uri: "ipfs://my-first-nft-metadata.json", + }, + minter: creator, + owner: owner, + expectErr: false, + expectedSupply: 1, + expectedBalanceForNewNFT: 1, + expectedTotalOwnerBalances: 1, // Owner should now have 1 asset. + }, + { + name: "Successful second mint", + collection: collection, + nftToMint: types.Nft{ + Name: "My Second NFT", + Description: "This is my second NFT", + Uri: "ipfs://my-second-nft-metadata.json", + }, + minter: creator, + owner: owner, + expectErr: false, + expectedSupply: 2, // Total supply of the collection is now 2. + expectedBalanceForNewNFT: 1, + expectedTotalOwnerBalances: 2, // Owner now has two distinct NFTs. + }, + { + name: "Unauthorized minter should fail", + collection: collection, + nftToMint: types.Nft{ + Name: "Unauthorized NFT", + Description: "This NFT should not be minted", + Uri: "ipfs://unauthorized-nft-metadata.json", + }, + minter: owner, // 'owner' is not the authorized minter + owner: owner, + expectErr: true, + expectedSupply: 2, // Supply should NOT increase. + expectedBalanceForNewNFT: 0, // Not applicable, but setting to 0 for clarity. + expectedTotalOwnerBalances: 2, // Owner's total assets should remain unchanged. + }, + } + + for _, tc := range testCases { + suite.Run(tc.name, func() { + nftDenom, err := suite.keeper.MintNFT(suite.ctx, collectionDenom, tc.minter, tc.owner, tc.nftToMint) + + if tc.expectErr { + suite.Require().Error(err, "should have returned an error") + } else { + suite.Require().NoError(err, "should not have returned an error") + fmt.Println("nftDenom for '"+tc.name+"':", nftDenom) + + // Check the balance of the newly minted NFT for the owner + resp, err := suite.bk.Balance(suite.ctx, &types2.QueryBalanceRequest{ + Address: tc.owner.String(), + Denom: nftDenom, + }) + suite.NoError(err) + suite.Equal(tc.expectedBalanceForNewNFT, resp.Balance.Amount.Int64(), "owner's balance for the new NFT should match expected") + } + + // Check the supply of the collection + supply := suite.keeper.GetSupply(suite.ctx, collectionDenom) + suite.Equal(math.NewInt(tc.expectedSupply), supply, "collection supply should match expected") + + // Check the owner's total number of different assets + balances, err := suite.bk.AllBalances(suite.ctx, &types2.QueryAllBalancesRequest{ + Address: tc.owner.String(), + }) + suite.NoError(err, "querying all balances should not produce an error") + suite.Equal(tc.expectedTotalOwnerBalances, len(balances.Balances), "owner's total number of assets should match expected") + }) + } +} diff --git a/x/nft/keeper/nft.go b/x/nft/keeper/nft.go new file mode 100644 index 00000000..9359a015 --- /dev/null +++ b/x/nft/keeper/nft.go @@ -0,0 +1,65 @@ +package keeper + +import ( + "fmt" + + "github.com/bitsongofficial/go-bitsong/x/nft/types" + sdk "github.com/cosmos/cosmos-sdk/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" +) + +func (k Keeper) MintNFT(ctx sdk.Context, collectionDenom string, minter sdk.AccAddress, owner sdk.AccAddress, metadata types.Nft) (string, error) { + coll, err := k.Collections.Get(ctx, collectionDenom) + if err != nil { + return "", types.ErrCollectionNotFound + } + + collectionMinter, err := sdk.AccAddressFromBech32(coll.Minter) + if err != nil { + return "", fmt.Errorf("invalid minter address: %w", err) + } + + if !minter.Equals(collectionMinter) { + return "", fmt.Errorf("only the collection minter can mint NFTs") + } + + nftDenom := k.createNftDenom(ctx, collectionDenom) + + // TODO: Charge fee if necessary + + nftMetadata := banktypes.Metadata{ + DenomUnits: []*banktypes.DenomUnit{{ + Denom: nftDenom, + Exponent: 0, + }}, + Base: nftDenom, + Name: metadata.Name, + Description: metadata.Description, + URI: metadata.Uri, + Symbol: nftDenom, + Display: nftDenom, + } + + k.bk.SetDenomMetaData(ctx, nftMetadata) + + amount := sdk.NewInt64Coin(nftDenom, 1) + + if err := k.bk.MintCoins(ctx, types.ModuleName, sdk.NewCoins(amount)); err != nil { + return "", fmt.Errorf("failed to mint NFT: %w", err) + } + + if err := k.bk.SendCoinsFromModuleToAccount(ctx, types.ModuleName, owner, sdk.NewCoins(amount)); err != nil { + return "", fmt.Errorf("failed to send NFT to owner: %w", err) + } + + if err := k.incrementSupply(ctx, collectionDenom); err != nil { + return "", fmt.Errorf("failed to increment supply: %w", err) + } + + return nftDenom, nil +} + +func (k Keeper) createNftDenom(ctx sdk.Context, collectionDenom string) string { + supply := k.GetSupply(ctx, collectionDenom) + return fmt.Sprintf("%s-%d", collectionDenom, supply.Uint64()+1) +} diff --git a/x/nft/types/errors.go b/x/nft/types/errors.go new file mode 100644 index 00000000..7ba37f21 --- /dev/null +++ b/x/nft/types/errors.go @@ -0,0 +1,8 @@ +package types + +import sdkerrors "cosmossdk.io/errors" + +var ( + ErrCollectionAlreadyExists = sdkerrors.Register(ModuleName, 1, "invalid collection: already exists") + ErrCollectionNotFound = sdkerrors.Register(ModuleName, 2, "invalid collection: not found") +) diff --git a/x/nft/types/expected_keeper.go b/x/nft/types/expected_keeper.go new file mode 100644 index 00000000..02b74a65 --- /dev/null +++ b/x/nft/types/expected_keeper.go @@ -0,0 +1,27 @@ +package types + +import ( + "context" + + "cosmossdk.io/core/address" + sdk "github.com/cosmos/cosmos-sdk/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" +) + +type AccountKeeper interface { + GetModuleAddress(moduleName string) sdk.AccAddress + GetAccount(ctx context.Context, addr sdk.AccAddress) sdk.AccountI + AddressCodec() address.Codec +} + +type BankKeeper interface { + GetSupply(ctx context.Context, denom string) sdk.Coin + HasSupply(ctx context.Context, denom string) bool + + GetDenomMetaData(ctx context.Context, denom string) (banktypes.Metadata, bool) + HasDenomMetaData(ctx context.Context, denom string) bool + SetDenomMetaData(ctx context.Context, denomMetaData banktypes.Metadata) + + MintCoins(ctx context.Context, moduleName string, amt sdk.Coins) error + SendCoinsFromModuleToAccount(ctx context.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error +} diff --git a/x/nft/types/keys.go b/x/nft/types/keys.go new file mode 100644 index 00000000..187bc99f --- /dev/null +++ b/x/nft/types/keys.go @@ -0,0 +1,14 @@ +package types + +import "cosmossdk.io/collections" + +const ( + ModuleName = "nft" + StoreKey = ModuleName + RouterKey = ModuleName +) + +var ( + CollectionsPrefix = collections.NewPrefix(0) + SupplyPrefix = collections.NewPrefix(1) +) diff --git a/x/nft/types/nft.pb.go b/x/nft/types/nft.pb.go new file mode 100644 index 00000000..bfa2186a --- /dev/null +++ b/x/nft/types/nft.pb.go @@ -0,0 +1,848 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: bitsong/nft/v1beta1/nft.proto + +package types + +import ( + fmt "fmt" + proto "github.com/cosmos/gogoproto/proto" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +type Collection struct { + Symbol string `protobuf:"bytes,1,opt,name=symbol,proto3" json:"symbol,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + Description string `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty"` + Uri string `protobuf:"bytes,4,opt,name=uri,proto3" json:"uri,omitempty"` + Creator string `protobuf:"bytes,5,opt,name=creator,proto3" json:"creator,omitempty"` + Minter string `protobuf:"bytes,6,opt,name=minter,proto3" json:"minter,omitempty"` +} + +func (m *Collection) Reset() { *m = Collection{} } +func (m *Collection) String() string { return proto.CompactTextString(m) } +func (*Collection) ProtoMessage() {} +func (*Collection) Descriptor() ([]byte, []int) { + return fileDescriptor_51b0314c164430ab, []int{0} +} +func (m *Collection) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Collection) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Collection.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 *Collection) XXX_Merge(src proto.Message) { + xxx_messageInfo_Collection.Merge(m, src) +} +func (m *Collection) XXX_Size() int { + return m.Size() +} +func (m *Collection) XXX_DiscardUnknown() { + xxx_messageInfo_Collection.DiscardUnknown(m) +} + +var xxx_messageInfo_Collection proto.InternalMessageInfo + +func (m *Collection) GetSymbol() string { + if m != nil { + return m.Symbol + } + return "" +} + +func (m *Collection) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *Collection) GetDescription() string { + if m != nil { + return m.Description + } + return "" +} + +func (m *Collection) GetUri() string { + if m != nil { + return m.Uri + } + return "" +} + +func (m *Collection) GetCreator() string { + if m != nil { + return m.Creator + } + return "" +} + +func (m *Collection) GetMinter() string { + if m != nil { + return m.Minter + } + return "" +} + +type Nft struct { + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Description string `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty"` + Uri string `protobuf:"bytes,3,opt,name=uri,proto3" json:"uri,omitempty"` +} + +func (m *Nft) Reset() { *m = Nft{} } +func (m *Nft) String() string { return proto.CompactTextString(m) } +func (*Nft) ProtoMessage() {} +func (*Nft) Descriptor() ([]byte, []int) { + return fileDescriptor_51b0314c164430ab, []int{1} +} +func (m *Nft) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Nft) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Nft.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 *Nft) XXX_Merge(src proto.Message) { + xxx_messageInfo_Nft.Merge(m, src) +} +func (m *Nft) XXX_Size() int { + return m.Size() +} +func (m *Nft) XXX_DiscardUnknown() { + xxx_messageInfo_Nft.DiscardUnknown(m) +} + +var xxx_messageInfo_Nft proto.InternalMessageInfo + +func (m *Nft) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *Nft) GetDescription() string { + if m != nil { + return m.Description + } + return "" +} + +func (m *Nft) GetUri() string { + if m != nil { + return m.Uri + } + return "" +} + +func init() { + proto.RegisterType((*Collection)(nil), "bitsong.nft.v1beta1.Collection") + proto.RegisterType((*Nft)(nil), "bitsong.nft.v1beta1.Nft") +} + +func init() { proto.RegisterFile("bitsong/nft/v1beta1/nft.proto", fileDescriptor_51b0314c164430ab) } + +var fileDescriptor_51b0314c164430ab = []byte{ + // 258 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x90, 0x3f, 0x4e, 0xc3, 0x30, + 0x14, 0xc6, 0xe3, 0xa6, 0x04, 0xf1, 0x58, 0x90, 0x91, 0x90, 0x17, 0xac, 0xaa, 0x13, 0x0b, 0x89, + 0x22, 0x6e, 0x00, 0x23, 0x82, 0x81, 0x91, 0x2d, 0x31, 0x4e, 0xb0, 0x94, 0xf8, 0x45, 0xce, 0x2b, + 0xa2, 0xb7, 0xe0, 0x04, 0x9c, 0x87, 0xb1, 0x23, 0x23, 0x4a, 0x2e, 0x82, 0xec, 0xa6, 0x55, 0x97, + 0x6e, 0xdf, 0x1f, 0x3d, 0xbd, 0x4f, 0x3f, 0xb8, 0x2e, 0x0d, 0xf5, 0x68, 0xeb, 0xcc, 0x56, 0x94, + 0x7d, 0xe4, 0xa5, 0xa6, 0x22, 0xf7, 0x3a, 0xed, 0x1c, 0x12, 0xf2, 0xcb, 0xa9, 0x4e, 0x7d, 0x34, + 0xd5, 0xcb, 0x6f, 0x06, 0xf0, 0x80, 0x4d, 0xa3, 0x15, 0x19, 0xb4, 0xfc, 0x0a, 0x92, 0x7e, 0xdd, + 0x96, 0xd8, 0x08, 0xb6, 0x60, 0x37, 0x67, 0x2f, 0x93, 0xe3, 0x1c, 0xe6, 0xb6, 0x68, 0xb5, 0x98, + 0x85, 0x34, 0x68, 0xbe, 0x80, 0xf3, 0x37, 0xdd, 0x2b, 0x67, 0x3a, 0x7f, 0x2a, 0xe2, 0x50, 0x1d, + 0x46, 0xfc, 0x02, 0xe2, 0x95, 0x33, 0x62, 0x1e, 0x1a, 0x2f, 0xb9, 0x80, 0x53, 0xe5, 0x74, 0x41, + 0xe8, 0xc4, 0x49, 0x48, 0x77, 0xd6, 0x7f, 0x6e, 0x8d, 0x25, 0xed, 0x44, 0xb2, 0xfd, 0xbc, 0x75, + 0xcb, 0x27, 0x88, 0x9f, 0x2b, 0xda, 0x0f, 0x60, 0xc7, 0x07, 0xcc, 0x8e, 0x0e, 0x88, 0xf7, 0x03, + 0xee, 0x1f, 0x7f, 0x06, 0xc9, 0x36, 0x83, 0x64, 0x7f, 0x83, 0x64, 0x5f, 0xa3, 0x8c, 0x36, 0xa3, + 0x8c, 0x7e, 0x47, 0x19, 0xbd, 0xe6, 0xb5, 0xa1, 0xf7, 0x55, 0x99, 0x2a, 0x6c, 0xb3, 0x89, 0x14, + 0x56, 0x95, 0x51, 0xa6, 0x68, 0xb2, 0x1a, 0x6f, 0x77, 0x6c, 0x3f, 0x03, 0x5d, 0x5a, 0x77, 0xba, + 0x2f, 0x93, 0x00, 0xf6, 0xee, 0x3f, 0x00, 0x00, 0xff, 0xff, 0x73, 0x75, 0x1b, 0xf4, 0x79, 0x01, + 0x00, 0x00, +} + +func (m *Collection) 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 *Collection) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Collection) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Minter) > 0 { + i -= len(m.Minter) + copy(dAtA[i:], m.Minter) + i = encodeVarintNft(dAtA, i, uint64(len(m.Minter))) + i-- + dAtA[i] = 0x32 + } + if len(m.Creator) > 0 { + i -= len(m.Creator) + copy(dAtA[i:], m.Creator) + i = encodeVarintNft(dAtA, i, uint64(len(m.Creator))) + i-- + dAtA[i] = 0x2a + } + if len(m.Uri) > 0 { + i -= len(m.Uri) + copy(dAtA[i:], m.Uri) + i = encodeVarintNft(dAtA, i, uint64(len(m.Uri))) + i-- + dAtA[i] = 0x22 + } + if len(m.Description) > 0 { + i -= len(m.Description) + copy(dAtA[i:], m.Description) + i = encodeVarintNft(dAtA, i, uint64(len(m.Description))) + i-- + dAtA[i] = 0x1a + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = encodeVarintNft(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0x12 + } + if len(m.Symbol) > 0 { + i -= len(m.Symbol) + copy(dAtA[i:], m.Symbol) + i = encodeVarintNft(dAtA, i, uint64(len(m.Symbol))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Nft) 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 *Nft) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Nft) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Uri) > 0 { + i -= len(m.Uri) + copy(dAtA[i:], m.Uri) + i = encodeVarintNft(dAtA, i, uint64(len(m.Uri))) + i-- + dAtA[i] = 0x1a + } + if len(m.Description) > 0 { + i -= len(m.Description) + copy(dAtA[i:], m.Description) + i = encodeVarintNft(dAtA, i, uint64(len(m.Description))) + i-- + dAtA[i] = 0x12 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = encodeVarintNft(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintNft(dAtA []byte, offset int, v uint64) int { + offset -= sovNft(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *Collection) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Symbol) + if l > 0 { + n += 1 + l + sovNft(uint64(l)) + } + l = len(m.Name) + if l > 0 { + n += 1 + l + sovNft(uint64(l)) + } + l = len(m.Description) + if l > 0 { + n += 1 + l + sovNft(uint64(l)) + } + l = len(m.Uri) + if l > 0 { + n += 1 + l + sovNft(uint64(l)) + } + l = len(m.Creator) + if l > 0 { + n += 1 + l + sovNft(uint64(l)) + } + l = len(m.Minter) + if l > 0 { + n += 1 + l + sovNft(uint64(l)) + } + return n +} + +func (m *Nft) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Name) + if l > 0 { + n += 1 + l + sovNft(uint64(l)) + } + l = len(m.Description) + if l > 0 { + n += 1 + l + sovNft(uint64(l)) + } + l = len(m.Uri) + if l > 0 { + n += 1 + l + sovNft(uint64(l)) + } + return n +} + +func sovNft(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozNft(x uint64) (n int) { + return sovNft(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *Collection) 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 ErrIntOverflowNft + } + 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: Collection: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Collection: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Symbol", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowNft + } + 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 ErrInvalidLengthNft + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthNft + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Symbol = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + 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 ErrIntOverflowNft + } + 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 ErrInvalidLengthNft + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthNft + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + 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 ErrIntOverflowNft + } + 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 ErrInvalidLengthNft + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthNft + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Description = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Uri", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowNft + } + 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 ErrInvalidLengthNft + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthNft + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Uri = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Creator", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowNft + } + 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 ErrInvalidLengthNft + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthNft + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Creator = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Minter", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowNft + } + 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 ErrInvalidLengthNft + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthNft + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Minter = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipNft(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthNft + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Nft) 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 ErrIntOverflowNft + } + 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: Nft: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Nft: 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 ErrIntOverflowNft + } + 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 ErrInvalidLengthNft + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthNft + } + 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 Description", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowNft + } + 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 ErrInvalidLengthNft + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthNft + } + 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 Uri", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowNft + } + 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 ErrInvalidLengthNft + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthNft + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Uri = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipNft(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthNft + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipNft(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, ErrIntOverflowNft + } + 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, ErrIntOverflowNft + } + 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, ErrIntOverflowNft + } + 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, ErrInvalidLengthNft + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupNft + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthNft + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthNft = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowNft = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupNft = fmt.Errorf("proto: unexpected end of group") +) From 628135e44345c6ad9fe941772d35abe74b3ae271 Mon Sep 17 00:00:00 2001 From: angelorc Date: Wed, 3 Sep 2025 10:54:38 +0200 Subject: [PATCH 02/15] fix(nft): correct collection denom creation and update test to log denom --- x/nft/keeper/collection.go | 8 ++++---- x/nft/keeper/keeper_test.go | 3 ++- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/x/nft/keeper/collection.go b/x/nft/keeper/collection.go index d07e0bac..86c959fa 100644 --- a/x/nft/keeper/collection.go +++ b/x/nft/keeper/collection.go @@ -61,15 +61,15 @@ func (k Keeper) setSupply(ctx sdk.Context, denom string, supply math.Int) error func (k Keeper) incrementSupply(ctx sdk.Context, denom string) error { supply := k.GetSupply(ctx, denom) supply = supply.Add(math.NewInt(1)) - + return k.setSupply(ctx, denom, supply) } func (k Keeper) createCollectionDenom(creator sdk.AccAddress, symbol string) string { // TODO: if necessary add a salt field - bz := []byte(fmt.Sprintf("%s%s", creator.String(), symbol)) - return "nft" + tmcrypto.AddressHash(bz).String() + bz := []byte(fmt.Sprintf("%s/%s", creator.String(), symbol)) + return fmt.Sprintf("nft%x", tmcrypto.AddressHash(bz)) } func (k Keeper) validateCollectionDenom(ctx sdk.Context, creator sdk.AccAddress, symbol string) (string, error) { @@ -79,7 +79,7 @@ func (k Keeper) validateCollectionDenom(ctx sdk.Context, creator sdk.AccAddress, return "", err } - if k.bk.HasSupply(ctx, symbol) { + if k.bk.HasSupply(ctx, denom) { return "", fmt.Errorf("denom %s already exists", denom) } diff --git a/x/nft/keeper/keeper_test.go b/x/nft/keeper/keeper_test.go index 2698b6a2..8dd889f4 100644 --- a/x/nft/keeper/keeper_test.go +++ b/x/nft/keeper/keeper_test.go @@ -61,8 +61,9 @@ func (suite *KeeperTestSuite) TestCreateCollection() { Uri: "ipfs://my-nft-collection-metadata.json", } - _, err := suite.keeper.CreateCollection(suite.ctx, creator, testCollection) + denom, err := suite.keeper.CreateCollection(suite.ctx, creator, testCollection) suite.NoError(err) + fmt.Println("denom:", denom) _, err = suite.keeper.CreateCollection(suite.ctx, creator, testCollection) suite.Error(err) From e834bf625966f815d93df607b114bb508388158f Mon Sep 17 00:00:00 2001 From: angelorc Date: Wed, 3 Sep 2025 11:08:05 +0200 Subject: [PATCH 03/15] fix(workflow): clean up branch definitions in interchaintest E2E workflow --- .github/workflows/interchaintest-e2e.yml | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/.github/workflows/interchaintest-e2e.yml b/.github/workflows/interchaintest-e2e.yml index faf90c18..0d80913e 100644 --- a/.github/workflows/interchaintest-e2e.yml +++ b/.github/workflows/interchaintest-e2e.yml @@ -2,12 +2,20 @@ name: ictest E2E on: pull_request: + branches: + - main + - master + branches-ignore: + - "mvp/**" + push: tags: - "**" branches: - - "main" - - "master" + - main + - master + branches-ignore: + - "mvp/**" permissions: contents: read From a658097b79327ab02d8de5a845a7d284f8299a5d Mon Sep 17 00:00:00 2001 From: angelorc Date: Wed, 3 Sep 2025 18:40:41 +0200 Subject: [PATCH 04/15] feat: add gRPC gateway support for NFT queries - Implemented RESTful JSON APIs for NFT queries using gRPC gateway. - Added handlers for the following queries: - Collection - OwnerOf - NumTokens - NftInfo - Nfts - AllNftsByOwner - Generated code from protobuf definitions to facilitate communication between gRPC and HTTP. - Remove sdk.coin logic --- app/keepers/keepers.go | 2 - proto/bitsong/nft/v1beta1/nft.proto | 19 +- proto/bitsong/nft/v1beta1/query.proto | 136 + third_party/proto/cosmos/query/v1/query.proto | 35 + x/nft/keeper/collection.go | 28 +- x/nft/keeper/collection_test.go | 2 +- x/nft/keeper/grpc_query.go | 161 + x/nft/keeper/grpc_query_test.go | 235 ++ x/nft/keeper/keeper.go | 66 +- x/nft/keeper/keeper_test.go | 56 +- x/nft/keeper/nft.go | 58 +- x/nft/types/expected_keeper.go | 13 - x/nft/types/keys.go | 7 +- x/nft/types/nft.pb.go | 278 +- x/nft/types/query.pb.go | 2624 +++++++++++++++++ x/nft/types/query.pb.gw.go | 756 +++++ 16 files changed, 4268 insertions(+), 208 deletions(-) create mode 100644 proto/bitsong/nft/v1beta1/query.proto create mode 100644 third_party/proto/cosmos/query/v1/query.proto create mode 100644 x/nft/keeper/grpc_query.go create mode 100644 x/nft/keeper/grpc_query_test.go create mode 100644 x/nft/types/query.pb.go create mode 100644 x/nft/types/query.pb.gw.go diff --git a/app/keepers/keepers.go b/app/keepers/keepers.go index 27218cb4..c1297cbc 100644 --- a/app/keepers/keepers.go +++ b/app/keepers/keepers.go @@ -115,7 +115,6 @@ var maccPerms = map[string][]string{ govtypes.ModuleName: {authtypes.Burner}, ibctransfertypes.ModuleName: {authtypes.Minter, authtypes.Burner}, fantokentypes.ModuleName: {authtypes.Minter, authtypes.Burner}, - nfttypes.ModuleName: {authtypes.Minter, authtypes.Burner}, wasmtypes.ModuleName: {authtypes.Burner}, protocolpooltypes.ModuleName: nil, protocolpooltypes.ProtocolPoolEscrowAccount: nil, @@ -414,7 +413,6 @@ func NewAppKeepers( appCodec, runtime.NewKVStoreService(appKeepers.keys[nfttypes.StoreKey]), appKeepers.AccountKeeper, - appKeepers.BankKeeper, bApp.Logger(), ) diff --git a/proto/bitsong/nft/v1beta1/nft.proto b/proto/bitsong/nft/v1beta1/nft.proto index 228c499c..85074ec0 100644 --- a/proto/bitsong/nft/v1beta1/nft.proto +++ b/proto/bitsong/nft/v1beta1/nft.proto @@ -1,13 +1,18 @@ syntax = "proto3"; package bitsong.nft.v1beta1; +import "gogoproto/gogo.proto"; + option go_package = "github.com/bitsongofficial/go-bitsong/x/nft/types"; message Collection { + option (gogoproto.goproto_getters) = false; + string symbol = 1; string name = 2; string description = 3; string uri = 4; + string creator = 5; string minter = 6; uint64 num_tokens = 7; @@ -16,10 +21,16 @@ message Collection { } message Nft { - string name = 1; - string description = 2; - string uri = 3; - // string owner = 5; + option (gogoproto.goproto_getters) = false; + + string collection = 1; + string token_id = 2; + + string name = 3; + string description = 4; + string uri = 5; + + string owner = 6; // seller_fee_bps // payment_address // bool is_mutable diff --git a/proto/bitsong/nft/v1beta1/query.proto b/proto/bitsong/nft/v1beta1/query.proto new file mode 100644 index 00000000..74dc61c8 --- /dev/null +++ b/proto/bitsong/nft/v1beta1/query.proto @@ -0,0 +1,136 @@ +syntax = "proto3"; +package bitsong.nft.v1beta1; + +import "gogoproto/gogo.proto"; +import "google/api/annotations.proto"; +import "cosmos/query/v1/query.proto"; +import "cosmos/base/query/v1beta1/pagination.proto"; +import "bitsong/nft/v1beta1/nft.proto"; + +option go_package = "github.com/bitsongofficial/go-bitsong/x/nft/types"; + +service Query { + rpc Collection(QueryCollectionRequest) returns (QueryCollectionResponse) { + option (cosmos.query.v1.module_query_safe) = true; + option (google.api.http).get = "/bitsong/nft/v1beta1/collection/{collection}"; + } + + rpc OwnerOf(QueryOwnerOfRequest) returns (QueryOwnerOfResponse) { + option (cosmos.query.v1.module_query_safe) = true; + option (google.api.http).get = "/bitsong/nft/v1beta1/owner/{collection}/{token_id}"; + } + + rpc NumTokens(QueryNumTokensRequest) returns (QueryNumTokensResponse) { + option (cosmos.query.v1.module_query_safe) = true; + option (google.api.http).get = "/bitsong/nft/v1beta1/num_tokens/{collection}"; + } + + rpc NftInfo(QueryNftInfoRequest) returns (QueryNftInfoResponse) { + option (cosmos.query.v1.module_query_safe) = true; + option (google.api.http).get = "/bitsong/nft/v1beta1/nft_info/{collection}/{token_id}"; + } + + rpc Nfts(QueryNftsRequest) returns (QueryNftsResponse) { + option (cosmos.query.v1.module_query_safe) = true; + option (google.api.http).get = "/bitsong/nft/v1beta1/nfts/{collection}"; + } + + rpc AllNftsByOwner(QueryAllNftsByOwnerRequest) returns (QueryAllNftsByOwnerResponse) { + option (cosmos.query.v1.module_query_safe) = true; + option (google.api.http).get = "/bitsong/nft/v1beta1/nfts_by_owner/{owner}"; + } +} + +message QueryCollectionRequest { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + string collection = 1; +} + +message QueryCollectionResponse { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + bitsong.nft.v1beta1.Collection collection = 1; +} + +message QueryOwnerOfRequest { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + string collection = 1; + string token_id = 2; +} + +message QueryOwnerOfResponse { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + string owner = 1; +} + +message QueryNumTokensRequest { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + string collection = 1; +} + +message QueryNumTokensResponse { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + uint64 count = 1; +} + +message QueryNftInfoRequest { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + string collection = 1; + string token_id = 2; +} + +message QueryNftInfoResponse { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + bitsong.nft.v1beta1.Nft nft = 1; +} + +message QueryNftsRequest { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + string collection = 1; + + cosmos.base.query.v1beta1.PageRequest pagination = 2; +} + +message QueryNftsResponse { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + repeated bitsong.nft.v1beta1.Nft nfts = 1 [ + (gogoproto.nullable) = false + ]; + + cosmos.base.query.v1beta1.PageResponse pagination = 2; +} + +message QueryAllNftsByOwnerRequest { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + string owner = 1; +} + +message QueryAllNftsByOwnerResponse { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + repeated bitsong.nft.v1beta1.Nft nfts = 1 [ + (gogoproto.nullable) = false + ]; +} \ No newline at end of file diff --git a/third_party/proto/cosmos/query/v1/query.proto b/third_party/proto/cosmos/query/v1/query.proto new file mode 100644 index 00000000..e42e73d7 --- /dev/null +++ b/third_party/proto/cosmos/query/v1/query.proto @@ -0,0 +1,35 @@ +syntax = "proto3"; + +package cosmos.query.v1; + +import "google/protobuf/descriptor.proto"; + +// TODO: once we fully migrate to protov2 the go_package needs to be updated. +// We need this right now because gogoproto codegen needs to import the extension. +option go_package = "github.com/cosmos/cosmos-sdk/types/query"; + +extend google.protobuf.MethodOptions { + // module_query_safe is set to true when the query is safe to be called from + // within the state machine, for example from another module's Keeper, via + // ADR-033 calls or from CosmWasm contracts. + // Concretely, it means that the query is: + // 1. deterministic: given a block height, returns the exact same response + // upon multiple calls; and doesn't introduce any state-machine-breaking + // changes across SDK patch version. + // 2. consumes gas correctly. + // + // If you are a module developer and want to add this annotation to one of + // your own queries, please make sure that the corresponding query: + // 1. is deterministic and won't introduce state-machine-breaking changes + // without a coordinated upgrade path, + // 2. has its gas tracked, to avoid the attack vector where no gas is + // accounted for on potentially high-computation queries. + // + // For queries that potentially consume a large amount of gas (for example + // those with pagination, if the pagination field is incorrectly set), we + // also recommend adding Protobuf comments to warn module developers + // consuming these queries. + // + // When set to true, the query can safely be called + bool module_query_safe = 11110001; +} \ No newline at end of file diff --git a/x/nft/keeper/collection.go b/x/nft/keeper/collection.go index 86c959fa..c9e99f26 100644 --- a/x/nft/keeper/collection.go +++ b/x/nft/keeper/collection.go @@ -7,7 +7,6 @@ import ( "github.com/bitsongofficial/go-bitsong/x/nft/types" tmcrypto "github.com/cometbft/cometbft/crypto" sdk "github.com/cosmos/cosmos-sdk/types" - banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" ) func (k Keeper) CreateCollection(ctx sdk.Context, creator sdk.AccAddress, coll types.Collection) (denom string, err error) { @@ -18,21 +17,6 @@ func (k Keeper) CreateCollection(ctx sdk.Context, creator sdk.AccAddress, coll t // TODO: charge fee - metadata := banktypes.Metadata{ - DenomUnits: []*banktypes.DenomUnit{{ - Denom: denom, - Exponent: 0, - }}, - Base: denom, - Name: coll.Name, - Description: coll.Description, - Symbol: coll.Symbol, - Display: coll.Symbol, - URI: coll.Uri, - } - - k.bk.SetDenomMetaData(ctx, metadata) - if err := k.setCollection(ctx, denom, coll); err != nil { return "", err } @@ -79,12 +63,7 @@ func (k Keeper) validateCollectionDenom(ctx sdk.Context, creator sdk.AccAddress, return "", err } - if k.bk.HasSupply(ctx, denom) { - return "", fmt.Errorf("denom %s already exists", denom) - } - - _, exists := k.bk.GetDenomMetaData(ctx, denom) - if exists { + if k.HasCollection(ctx, denom) { return "", types.ErrCollectionAlreadyExists } @@ -103,3 +82,8 @@ func (k Keeper) getCollection(ctx sdk.Context, denom string) (types.Collection, return coll, nil } + +func (k Keeper) HasCollection(ctx sdk.Context, denom string) bool { + has, err := k.Collections.Has(ctx, denom) + return has && err == nil +} diff --git a/x/nft/keeper/collection_test.go b/x/nft/keeper/collection_test.go index 89416850..24fe1975 100644 --- a/x/nft/keeper/collection_test.go +++ b/x/nft/keeper/collection_test.go @@ -11,7 +11,7 @@ func TestKeeper_createCollectionDenom(t *testing.T) { creator := sdk.AccAddress(tmhash.SumTruncated([]byte("creator"))) symbol := "MYNFT" - expectedDenom := "nftF1D9FE89CCE1FAD3F83FFCBA6F496EFD30855C42" + expectedDenom := "nft653AF6715F0C4EE2E24A54B191EBD0AD5DB33723" k := Keeper{} denom := k.createCollectionDenom(creator, symbol) diff --git a/x/nft/keeper/grpc_query.go b/x/nft/keeper/grpc_query.go new file mode 100644 index 00000000..c0051389 --- /dev/null +++ b/x/nft/keeper/grpc_query.go @@ -0,0 +1,161 @@ +package keeper + +import ( + "context" + "errors" + + "cosmossdk.io/collections" + "cosmossdk.io/collections/indexes" + "github.com/bitsongofficial/go-bitsong/x/nft/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/query" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +var _ types.QueryServer = Keeper{} + +func (k Keeper) Collection(ctx context.Context, req *types.QueryCollectionRequest) (*types.QueryCollectionResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + if req.Collection == "" { + return nil, status.Error(codes.InvalidArgument, "collection cannot be empty") + } + + sdkCtx := sdk.UnwrapSDKContext(ctx) + + coll, err := k.Collections.Get(sdkCtx, req.Collection) + if err != nil { + if errors.Is(err, collections.ErrNotFound) { + return nil, status.Errorf(codes.NotFound, "collection %s not found", req.Collection) + } + return nil, status.Error(codes.Internal, err.Error()) + } + + return &types.QueryCollectionResponse{ + Collection: &coll, + }, nil +} + +func (k Keeper) OwnerOf(ctx context.Context, req *types.QueryOwnerOfRequest) (*types.QueryOwnerOfResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + if req.Collection == "" { + return nil, status.Error(codes.InvalidArgument, "collection cannot be empty") + } + if req.TokenId == "" { + return nil, status.Error(codes.InvalidArgument, "token_id cannot be empty") + } + + sdkCtx := sdk.UnwrapSDKContext(ctx) + + nft, err := k.NFTs.Get(sdkCtx, collections.Join(req.Collection, req.TokenId)) + if err != nil { + if errors.Is(err, collections.ErrNotFound) { + return nil, status.Errorf(codes.NotFound, "nft with collection %s and token_id %s not found", req.Collection, req.TokenId) + } + return nil, status.Error(codes.Internal, err.Error()) + } + + return &types.QueryOwnerOfResponse{ + Owner: nft.Owner, + }, nil +} + +func (k Keeper) NumTokens(ctx context.Context, req *types.QueryNumTokensRequest) (*types.QueryNumTokensResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + if req.Collection == "" { + return nil, status.Error(codes.InvalidArgument, "collection cannot be empty") + } + + sdkCtx := sdk.UnwrapSDKContext(ctx) + + supply := k.GetSupply(sdkCtx, req.Collection) + + return &types.QueryNumTokensResponse{ + Count: supply.Uint64(), + }, nil +} + +func (k Keeper) NftInfo(ctx context.Context, req *types.QueryNftInfoRequest) (*types.QueryNftInfoResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + if req.Collection == "" { + return nil, status.Error(codes.InvalidArgument, "collection cannot be empty") + } + if req.TokenId == "" { + return nil, status.Error(codes.InvalidArgument, "token_id cannot be empty") + } + + sdkCtx := sdk.UnwrapSDKContext(ctx) + + nft, err := k.NFTs.Get(sdkCtx, collections.Join(req.Collection, req.TokenId)) + if err != nil { + if errors.Is(err, collections.ErrNotFound) { + return nil, status.Errorf(codes.NotFound, "nft with collection %s and token_id %s not found", req.Collection, req.TokenId) + } + return nil, status.Error(codes.Internal, err.Error()) + } + + return &types.QueryNftInfoResponse{ + Nft: &nft, + }, nil +} + +func (k Keeper) Nfts(ctx context.Context, req *types.QueryNftsRequest) (*types.QueryNftsResponse, error) { + if req == nil || req.Collection == "" { + return nil, status.Error(codes.InvalidArgument, "collection cannot be empty") + } + + nfts, pageRes, err := query.CollectionPaginate( + ctx, + k.NFTs, + req.Pagination, + func(key collections.Pair[string, string], value types.Nft) (types.Nft, error) { + return value, nil + }, + query.WithCollectionPaginationPairPrefix[string, string](req.Collection), + ) + + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + + return &types.QueryNftsResponse{ + Nfts: nfts, + Pagination: pageRes, + }, nil +} + +func (k Keeper) AllNftsByOwner(ctx context.Context, req *types.QueryAllNftsByOwnerRequest) (*types.QueryAllNftsByOwnerResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + + owner, err := sdk.AccAddressFromBech32(req.Owner) + if err != nil { + return nil, status.Errorf(codes.InvalidArgument, "invalid owner address: %s", err.Error()) + } + + // TODO: Add pagination support for this query + + iter, err := k.NFTs.Indexes.Owner.MatchExact(ctx, owner) + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + defer iter.Close() + + nfts, err := indexes.CollectValues(ctx, k.NFTs, iter) + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + + return &types.QueryAllNftsByOwnerResponse{ + Nfts: nfts, + }, nil +} diff --git a/x/nft/keeper/grpc_query_test.go b/x/nft/keeper/grpc_query_test.go new file mode 100644 index 00000000..e643f27d --- /dev/null +++ b/x/nft/keeper/grpc_query_test.go @@ -0,0 +1,235 @@ +package keeper_test + +import ( + "github.com/bitsongofficial/go-bitsong/x/nft/types" +) + +func (suite *KeeperTestSuite) TestQueryCollection() { + testCollection := types.Collection{ + Name: "My NFT Collection", + Symbol: "MYNFT", + Description: "My NFT Collection Description", + Uri: "ipfs://my-nft-collection-metadata.json", + Minter: creator.String(), + } + expectedDenom := "nft653AF6715F0C4EE2E24A54B191EBD0AD5DB33723" + + collectionDenom, err := suite.keeper.CreateCollection(suite.ctx, creator, testCollection) + suite.NoError(err) + suite.Equal(expectedDenom, collectionDenom) + + res, err := suite.keeper.Collection(suite.ctx, &types.QueryCollectionRequest{ + Collection: collectionDenom, + }) + suite.NoError(err) + suite.Equal(testCollection.Name, res.Collection.Name) + suite.Equal(testCollection.Symbol, res.Collection.Symbol) + suite.Equal(testCollection.Description, res.Collection.Description) + suite.Equal(testCollection.Uri, res.Collection.Uri) + suite.Equal(testCollection.Minter, res.Collection.Minter) +} + +func (suite *KeeperTestSuite) TestQueryOwnerOf() { + testCollection := types.Collection{ + Name: "My NFT Collection", + Symbol: "MYNFT", + Description: "My NFT Collection Description", + Uri: "ipfs://my-nft-collection-metadata.json", + Minter: creator.String(), + } + expectedDenom := "nft653AF6715F0C4EE2E24A54B191EBD0AD5DB33723" + + collectionDenom, err := suite.keeper.CreateCollection(suite.ctx, creator, testCollection) + suite.NoError(err) + suite.Equal(expectedDenom, collectionDenom) + + nft1 := types.Nft{ + TokenId: "1", + Name: "My First NFT", + Description: "This is my first NFT", + Uri: "ipfs://my-first-nft-metadata.json", + } + + err = suite.keeper.MintNFT(suite.ctx, collectionDenom, creator, owner, nft1) + suite.NoError(err) + + res, err := suite.keeper.OwnerOf(suite.ctx, &types.QueryOwnerOfRequest{ + Collection: collectionDenom, + TokenId: "1", + }) + suite.NoError(err) + suite.Equal(owner.String(), res.Owner) +} + +func (suite *KeeperTestSuite) TestQueryNumTokens() { + testCollection := types.Collection{ + Name: "My NFT Collection", + Symbol: "MYNFT", + Description: "My NFT Collection Description", + Uri: "ipfs://my-nft-collection-metadata.json", + Minter: creator.String(), + } + expectedDenom := "nft653AF6715F0C4EE2E24A54B191EBD0AD5DB33723" + + collectionDenom, err := suite.keeper.CreateCollection(suite.ctx, creator, testCollection) + suite.NoError(err) + suite.Equal(expectedDenom, collectionDenom) + + supply := suite.keeper.GetSupply(suite.ctx, collectionDenom) + suite.Equal(uint64(0), supply.Uint64()) + + nft1 := types.Nft{ + TokenId: "1", + Name: "My First NFT", + Description: "This is my first NFT", + Uri: "ipfs://my-first-nft-metadata.json", + } + + nft2 := types.Nft{ + TokenId: "2", + Name: "My Second NFT", + Description: "This is my second NFT", + Uri: "ipfs://my-second-nft-metadata.json", + } + + err = suite.keeper.MintNFT(suite.ctx, collectionDenom, creator, owner, nft1) + suite.NoError(err) + + supply = suite.keeper.GetSupply(suite.ctx, collectionDenom) + suite.Equal(uint64(1), supply.Uint64()) + + err = suite.keeper.MintNFT(suite.ctx, collectionDenom, creator, owner, nft2) + suite.NoError(err) + + supply = suite.keeper.GetSupply(suite.ctx, collectionDenom) + suite.Equal(uint64(2), supply.Uint64()) + + res, err := suite.keeper.NumTokens(suite.ctx, &types.QueryNumTokensRequest{ + Collection: collectionDenom, + }) + suite.NoError(err) + suite.Equal(uint64(2), res.Count) +} + +func (suite *KeeperTestSuite) TestQueryNftInfo() { + testCollection := types.Collection{ + Name: "My NFT Collection", + Symbol: "MYNFT", + Description: "My NFT Collection Description", + Uri: "ipfs://my-nft-collection-metadata.json", + Minter: creator.String(), + } + expectedDenom := "nft653AF6715F0C4EE2E24A54B191EBD0AD5DB33723" + + collectionDenom, err := suite.keeper.CreateCollection(suite.ctx, creator, testCollection) + suite.NoError(err) + suite.Equal(expectedDenom, collectionDenom) + + nft1 := types.Nft{ + TokenId: "1", + Name: "My First NFT", + Description: "This is my first NFT", + Uri: "ipfs://my-first-nft-metadata.json", + } + + err = suite.keeper.MintNFT(suite.ctx, collectionDenom, creator, owner, nft1) + suite.NoError(err) + + res, err := suite.keeper.NftInfo(suite.ctx, &types.QueryNftInfoRequest{ + Collection: collectionDenom, + TokenId: "1", + }) + suite.NoError(err) + suite.Equal(nft1.TokenId, res.Nft.TokenId) + suite.Equal(nft1.Name, res.Nft.Name) + suite.Equal(nft1.Description, res.Nft.Description) + suite.Equal(nft1.Uri, res.Nft.Uri) + suite.Equal(collectionDenom, res.Nft.Collection) + suite.Equal(owner.String(), res.Nft.Owner) +} + +func (suite *KeeperTestSuite) TestQueryNftsOfOwner() { + testCollection := types.Collection{ + Name: "My NFT Collection", + Symbol: "MYNFT", + Description: "My NFT Collection Description", + Uri: "ipfs://my-nft-collection-metadata.json", + Minter: creator.String(), + } + expectedDenom := "nft653AF6715F0C4EE2E24A54B191EBD0AD5DB33723" + + collectionDenom, err := suite.keeper.CreateCollection(suite.ctx, creator, testCollection) + suite.NoError(err) + suite.Equal(expectedDenom, collectionDenom) + + nft1 := types.Nft{ + TokenId: "1", + Name: "My First NFT", + Description: "This is my first NFT", + Uri: "ipfs://my-first-nft-metadata.json", + } + + nft2 := types.Nft{ + TokenId: "2", + Name: "My Second NFT", + Description: "This is my second NFT", + Uri: "ipfs://my-second-nft-metadata.json", + } + + err = suite.keeper.MintNFT(suite.ctx, collectionDenom, creator, owner, nft1) + suite.NoError(err) + + err = suite.keeper.MintNFT(suite.ctx, collectionDenom, creator, owner, nft2) + suite.NoError(err) + + res, err := suite.keeper.Nfts(suite.ctx, &types.QueryNftsRequest{ + Collection: collectionDenom, + }) + suite.NoError(err) + suite.Len(res.Nfts, 2) + suite.Equal(nft1.TokenId, res.Nfts[0].TokenId) + suite.Equal(nft2.TokenId, res.Nfts[1].TokenId) +} + +func (suite *KeeperTestSuite) TestQueryNftsByOwner() { + testCollection := types.Collection{ + Name: "My NFT Collection", + Symbol: "MYNFT", + Description: "My NFT Collection Description", + Uri: "ipfs://my-nft-collection-metadata.json", + Minter: creator.String(), + } + expectedDenom := "nft653AF6715F0C4EE2E24A54B191EBD0AD5DB33723" + + collectionDenom, err := suite.keeper.CreateCollection(suite.ctx, creator, testCollection) + suite.NoError(err) + suite.Equal(expectedDenom, collectionDenom) + + nft1 := types.Nft{ + TokenId: "1", + Name: "My First NFT", + Description: "This is my first NFT", + Uri: "ipfs://my-first-nft-metadata.json", + } + + nft2 := types.Nft{ + TokenId: "2", + Name: "My Second NFT", + Description: "This is my second NFT", + Uri: "ipfs://my-second-nft-metadata.json", + } + + err = suite.keeper.MintNFT(suite.ctx, collectionDenom, creator, owner, nft1) + suite.NoError(err) + + err = suite.keeper.MintNFT(suite.ctx, collectionDenom, creator, owner, nft2) + suite.NoError(err) + + res, err := suite.keeper.AllNftsByOwner(suite.ctx, &types.QueryAllNftsByOwnerRequest{ + Owner: owner.String(), + }) + suite.NoError(err) + suite.Len(res.Nfts, 2) + suite.Equal(nft1.TokenId, res.Nfts[0].TokenId) + suite.Equal(nft2.TokenId, res.Nfts[1].TokenId) +} diff --git a/x/nft/keeper/keeper.go b/x/nft/keeper/keeper.go index da7550b1..0dcb263e 100644 --- a/x/nft/keeper/keeper.go +++ b/x/nft/keeper/keeper.go @@ -2,6 +2,7 @@ package keeper import ( "cosmossdk.io/collections" + "cosmossdk.io/collections/indexes" "cosmossdk.io/core/address" "cosmossdk.io/core/store" "cosmossdk.io/log" @@ -11,19 +12,57 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" ) +type NFTIndexes struct { + Collection *indexes.Multi[string, collections.Pair[string, string], types.Nft] + Owner *indexes.Multi[sdk.AccAddress, collections.Pair[string, string], types.Nft] +} + +func newNFTIndexes(sb *collections.SchemaBuilder) NFTIndexes { + return NFTIndexes{ + Collection: indexes.NewMulti( + sb, + types.NFTsByCollectionPrefix, + "nfts_by_collection", + collections.StringKey, + collections.PairKeyCodec(collections.StringKey, collections.StringKey), + func(pk collections.Pair[string, string], v types.Nft) (string, error) { + return v.Collection, nil + }, + ), + Owner: indexes.NewMulti( + sb, + types.OwnersPrefix, + "owners", + sdk.AccAddressKey, + collections.PairKeyCodec(collections.StringKey, collections.StringKey), + func(pk collections.Pair[string, string], v types.Nft) (sdk.AccAddress, error) { + return sdk.AccAddressFromBech32(v.Owner) + }, + ), + } +} + +func (i NFTIndexes) IndexesList() []collections.Index[collections.Pair[string, string], types.Nft] { + return []collections.Index[collections.Pair[string, string], types.Nft]{ + i.Collection, + i.Owner, + } +} + type Keeper struct { cdc codec.BinaryCodec storeService store.KVStoreService ac address.Codec - bk types.BankKeeper - logger log.Logger + // bk types.BankKeeper + logger log.Logger Schema collections.Schema Collections collections.Map[string, types.Collection] Supply collections.Map[string, math.Int] + NFTs *collections.IndexedMap[collections.Pair[string, string], types.Nft, NFTIndexes] } -func NewKeeper(cdc codec.BinaryCodec, storeService store.KVStoreService, ak types.AccountKeeper, bk types.BankKeeper, logger log.Logger) Keeper { +func NewKeeper(cdc codec.BinaryCodec, storeService store.KVStoreService, ak types.AccountKeeper, logger log.Logger) Keeper { if addr := ak.GetModuleAddress(types.ModuleName); addr == nil { panic("the " + types.ModuleName + " module account has not been set") } @@ -31,15 +70,30 @@ func NewKeeper(cdc codec.BinaryCodec, storeService store.KVStoreService, ak type logger = logger.With(log.ModuleKey, "x/"+types.ModuleName) sb := collections.NewSchemaBuilder(storeService) + ac := ak.AddressCodec() - return Keeper{ + k := Keeper{ cdc: cdc, storeService: storeService, - ac: ak.AddressCodec(), - bk: bk, + ac: ac, logger: logger, // TODO: fix the store once we add queries Collections: collections.NewMap(sb, types.CollectionsPrefix, "collections", collections.StringKey, codec.CollValue[types.Collection](cdc)), Supply: collections.NewMap(sb, types.SupplyPrefix, "supply", collections.StringKey, sdk.IntValue), + NFTs: collections.NewIndexedMap( + sb, + types.NFTsPrefix, + "nfts", + collections.PairKeyCodec(collections.StringKey, collections.StringKey), + codec.CollValue[types.Nft](cdc), + newNFTIndexes(sb), + ), + } + + schema, err := sb.Build() + if err != nil { + panic(err) } + k.Schema = schema + return k } diff --git a/x/nft/keeper/keeper_test.go b/x/nft/keeper/keeper_test.go index 8dd889f4..27e57fb6 100644 --- a/x/nft/keeper/keeper_test.go +++ b/x/nft/keeper/keeper_test.go @@ -1,7 +1,6 @@ package keeper_test import ( - "fmt" "testing" "cosmossdk.io/math" @@ -13,7 +12,6 @@ import ( "github.com/cometbft/cometbft/crypto/tmhash" sdk "github.com/cosmos/cosmos-sdk/types" bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" - types2 "github.com/cosmos/cosmos-sdk/x/bank/types" "github.com/stretchr/testify/suite" ) @@ -60,10 +58,11 @@ func (suite *KeeperTestSuite) TestCreateCollection() { Description: "My NFT Collection Description", Uri: "ipfs://my-nft-collection-metadata.json", } + expectedDenom := "nft653AF6715F0C4EE2E24A54B191EBD0AD5DB33723" denom, err := suite.keeper.CreateCollection(suite.ctx, creator, testCollection) suite.NoError(err) - fmt.Println("denom:", denom) + suite.Equal(expectedDenom, denom) _, err = suite.keeper.CreateCollection(suite.ctx, creator, testCollection) suite.Error(err) @@ -77,59 +76,43 @@ func (suite *KeeperTestSuite) TestMintNFT() { Uri: "ipfs://my-nft-collection-metadata.json", Minter: creator.String(), } + expectedDenom := "nft653AF6715F0C4EE2E24A54B191EBD0AD5DB33723" collectionDenom, err := suite.keeper.CreateCollection(suite.ctx, creator, testCollection) suite.NoError(err) - fmt.Println("collectionDenom:", collectionDenom) + suite.Equal(expectedDenom, collectionDenom) - resp, err := suite.bk.Balance(suite.ctx, &types2.QueryBalanceRequest{ - Address: creator.String(), - Denom: collectionDenom, - }) - suite.NoError(err) - suite.Equal(int64(0), resp.Balance.Amount.Int64()) + supply := suite.keeper.GetSupply(suite.ctx, collectionDenom) + suite.Equal(math.NewInt(0), supply) + + nft1 := types.Nft{ + TokenId: "1", + Name: "My First NFT", + Description: "This is my first NFT", + Uri: "ipfs://my-first-nft-metadata.json", + } - nft := types.Nft{ + nft2 := types.Nft{ + TokenId: "2", Name: "My First NFT", Description: "This is my first NFT", Uri: "ipfs://my-first-nft-metadata.json", } - nft1denom, err := suite.keeper.MintNFT(suite.ctx, collectionDenom, creator, owner, nft) + err = suite.keeper.MintNFT(suite.ctx, collectionDenom, creator, owner, nft1) suite.NoError(err) - fmt.Println("nft1denom:", nft1denom) - supply := suite.keeper.GetSupply(suite.ctx, collectionDenom) + supply = suite.keeper.GetSupply(suite.ctx, collectionDenom) suite.Equal(math.NewInt(1), supply) - resp, err = suite.bk.Balance(suite.ctx, &types2.QueryBalanceRequest{ - Address: owner.String(), - Denom: nft1denom, - }) - suite.NoError(err) - suite.Equal(int64(1), resp.Balance.Amount.Int64()) - - nft2denom, err := suite.keeper.MintNFT(suite.ctx, collectionDenom, creator, owner, nft) + err = suite.keeper.MintNFT(suite.ctx, collectionDenom, creator, owner, nft2) suite.NoError(err) - fmt.Println("nft2denom:", nft2denom) supply = suite.keeper.GetSupply(suite.ctx, collectionDenom) suite.Equal(math.NewInt(2), supply) - - resp, err = suite.bk.Balance(suite.ctx, &types2.QueryBalanceRequest{ - Address: owner.String(), - Denom: nft2denom, - }) - suite.NoError(err) - suite.Equal(int64(1), resp.Balance.Amount.Int64()) - - balances, err := suite.bk.AllBalances(suite.ctx, &types2.QueryAllBalancesRequest{ - Address: owner.String(), - }) - suite.NoError(err) - suite.Equal(2, len(balances.Balances)) } +/* type MintNFTTestCase struct { name string // A descriptive name for the test case collection types.Collection @@ -236,3 +219,4 @@ func (suite *KeeperTestSuite) TestMintNFT_Advanced() { }) } } +*/ diff --git a/x/nft/keeper/nft.go b/x/nft/keeper/nft.go index 9359a015..f07971f8 100644 --- a/x/nft/keeper/nft.go +++ b/x/nft/keeper/nft.go @@ -3,63 +3,53 @@ package keeper import ( "fmt" + "cosmossdk.io/collections" "github.com/bitsongofficial/go-bitsong/x/nft/types" sdk "github.com/cosmos/cosmos-sdk/types" - banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" ) -func (k Keeper) MintNFT(ctx sdk.Context, collectionDenom string, minter sdk.AccAddress, owner sdk.AccAddress, metadata types.Nft) (string, error) { +func (k Keeper) MintNFT(ctx sdk.Context, collectionDenom string, minter sdk.AccAddress, owner sdk.AccAddress, metadata types.Nft) error { + nftKey := collections.Join(collectionDenom, metadata.TokenId) + has, err := k.NFTs.Has(ctx, nftKey) + if err != nil { + return fmt.Errorf("failed to check NFT: %w", err) + } + if has { + return fmt.Errorf("NFT with token ID %s already exists in collection %s", metadata.TokenId, collectionDenom) + } + coll, err := k.Collections.Get(ctx, collectionDenom) if err != nil { - return "", types.ErrCollectionNotFound + return types.ErrCollectionNotFound } collectionMinter, err := sdk.AccAddressFromBech32(coll.Minter) if err != nil { - return "", fmt.Errorf("invalid minter address: %w", err) + return fmt.Errorf("invalid minter address: %w", err) } if !minter.Equals(collectionMinter) { - return "", fmt.Errorf("only the collection minter can mint NFTs") + return fmt.Errorf("only the collection minter can mint NFTs") } - nftDenom := k.createNftDenom(ctx, collectionDenom) - // TODO: Charge fee if necessary - nftMetadata := banktypes.Metadata{ - DenomUnits: []*banktypes.DenomUnit{{ - Denom: nftDenom, - Exponent: 0, - }}, - Base: nftDenom, - Name: metadata.Name, - Description: metadata.Description, - URI: metadata.Uri, - Symbol: nftDenom, - Display: nftDenom, - } - - k.bk.SetDenomMetaData(ctx, nftMetadata) + metadata.Collection = collectionDenom + metadata.Owner = owner.String() - amount := sdk.NewInt64Coin(nftDenom, 1) - - if err := k.bk.MintCoins(ctx, types.ModuleName, sdk.NewCoins(amount)); err != nil { - return "", fmt.Errorf("failed to mint NFT: %w", err) - } - - if err := k.bk.SendCoinsFromModuleToAccount(ctx, types.ModuleName, owner, sdk.NewCoins(amount)); err != nil { - return "", fmt.Errorf("failed to send NFT to owner: %w", err) + if err := k.setNft(ctx, collectionDenom, metadata.TokenId, metadata); err != nil { + return fmt.Errorf("failed to set NFT: %w", err) } - if err := k.incrementSupply(ctx, collectionDenom); err != nil { - return "", fmt.Errorf("failed to increment supply: %w", err) - } - - return nftDenom, nil + return k.incrementSupply(ctx, collectionDenom) } func (k Keeper) createNftDenom(ctx sdk.Context, collectionDenom string) string { supply := k.GetSupply(ctx, collectionDenom) return fmt.Sprintf("%s-%d", collectionDenom, supply.Uint64()+1) } + +func (k Keeper) setNft(ctx sdk.Context, collectionDenom string, tokenId string, nft types.Nft) error { + pk := collections.Join(collectionDenom, tokenId) + return k.NFTs.Set(ctx, pk, nft) +} diff --git a/x/nft/types/expected_keeper.go b/x/nft/types/expected_keeper.go index 02b74a65..65059897 100644 --- a/x/nft/types/expected_keeper.go +++ b/x/nft/types/expected_keeper.go @@ -5,7 +5,6 @@ import ( "cosmossdk.io/core/address" sdk "github.com/cosmos/cosmos-sdk/types" - banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" ) type AccountKeeper interface { @@ -13,15 +12,3 @@ type AccountKeeper interface { GetAccount(ctx context.Context, addr sdk.AccAddress) sdk.AccountI AddressCodec() address.Codec } - -type BankKeeper interface { - GetSupply(ctx context.Context, denom string) sdk.Coin - HasSupply(ctx context.Context, denom string) bool - - GetDenomMetaData(ctx context.Context, denom string) (banktypes.Metadata, bool) - HasDenomMetaData(ctx context.Context, denom string) bool - SetDenomMetaData(ctx context.Context, denomMetaData banktypes.Metadata) - - MintCoins(ctx context.Context, moduleName string, amt sdk.Coins) error - SendCoinsFromModuleToAccount(ctx context.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error -} diff --git a/x/nft/types/keys.go b/x/nft/types/keys.go index 187bc99f..35000f0e 100644 --- a/x/nft/types/keys.go +++ b/x/nft/types/keys.go @@ -9,6 +9,9 @@ const ( ) var ( - CollectionsPrefix = collections.NewPrefix(0) - SupplyPrefix = collections.NewPrefix(1) + CollectionsPrefix = collections.NewPrefix(0) + SupplyPrefix = collections.NewPrefix(1) + NFTsPrefix = collections.NewPrefix(2) + NFTsByCollectionPrefix = collections.NewPrefix(3) + OwnersPrefix = collections.NewPrefix(4) ) diff --git a/x/nft/types/nft.pb.go b/x/nft/types/nft.pb.go index bfa2186a..38ab9f4b 100644 --- a/x/nft/types/nft.pb.go +++ b/x/nft/types/nft.pb.go @@ -5,6 +5,7 @@ package types import ( fmt "fmt" + _ "github.com/cosmos/gogoproto/gogoproto" proto "github.com/cosmos/gogoproto/proto" io "io" math "math" @@ -29,6 +30,7 @@ type Collection struct { Uri string `protobuf:"bytes,4,opt,name=uri,proto3" json:"uri,omitempty"` Creator string `protobuf:"bytes,5,opt,name=creator,proto3" json:"creator,omitempty"` Minter string `protobuf:"bytes,6,opt,name=minter,proto3" json:"minter,omitempty"` + NumTokens uint64 `protobuf:"varint,7,opt,name=num_tokens,json=numTokens,proto3" json:"num_tokens,omitempty"` } func (m *Collection) Reset() { *m = Collection{} } @@ -64,52 +66,13 @@ func (m *Collection) XXX_DiscardUnknown() { var xxx_messageInfo_Collection proto.InternalMessageInfo -func (m *Collection) GetSymbol() string { - if m != nil { - return m.Symbol - } - return "" -} - -func (m *Collection) GetName() string { - if m != nil { - return m.Name - } - return "" -} - -func (m *Collection) GetDescription() string { - if m != nil { - return m.Description - } - return "" -} - -func (m *Collection) GetUri() string { - if m != nil { - return m.Uri - } - return "" -} - -func (m *Collection) GetCreator() string { - if m != nil { - return m.Creator - } - return "" -} - -func (m *Collection) GetMinter() string { - if m != nil { - return m.Minter - } - return "" -} - type Nft struct { - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - Description string `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty"` - Uri string `protobuf:"bytes,3,opt,name=uri,proto3" json:"uri,omitempty"` + Collection string `protobuf:"bytes,1,opt,name=collection,proto3" json:"collection,omitempty"` + TokenId string `protobuf:"bytes,2,opt,name=token_id,json=tokenId,proto3" json:"token_id,omitempty"` + Name string `protobuf:"bytes,3,opt,name=name,proto3" json:"name,omitempty"` + Description string `protobuf:"bytes,4,opt,name=description,proto3" json:"description,omitempty"` + Uri string `protobuf:"bytes,5,opt,name=uri,proto3" json:"uri,omitempty"` + Owner string `protobuf:"bytes,6,opt,name=owner,proto3" json:"owner,omitempty"` } func (m *Nft) Reset() { *m = Nft{} } @@ -145,27 +108,6 @@ func (m *Nft) XXX_DiscardUnknown() { var xxx_messageInfo_Nft proto.InternalMessageInfo -func (m *Nft) GetName() string { - if m != nil { - return m.Name - } - return "" -} - -func (m *Nft) GetDescription() string { - if m != nil { - return m.Description - } - return "" -} - -func (m *Nft) GetUri() string { - if m != nil { - return m.Uri - } - return "" -} - func init() { proto.RegisterType((*Collection)(nil), "bitsong.nft.v1beta1.Collection") proto.RegisterType((*Nft)(nil), "bitsong.nft.v1beta1.Nft") @@ -174,24 +116,28 @@ func init() { func init() { proto.RegisterFile("bitsong/nft/v1beta1/nft.proto", fileDescriptor_51b0314c164430ab) } var fileDescriptor_51b0314c164430ab = []byte{ - // 258 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x90, 0x3f, 0x4e, 0xc3, 0x30, - 0x14, 0xc6, 0xe3, 0xa6, 0x04, 0xf1, 0x58, 0x90, 0x91, 0x90, 0x17, 0xac, 0xaa, 0x13, 0x0b, 0x89, - 0x22, 0x6e, 0x00, 0x23, 0x82, 0x81, 0x91, 0x2d, 0x31, 0x4e, 0xb0, 0x94, 0xf8, 0x45, 0xce, 0x2b, - 0xa2, 0xb7, 0xe0, 0x04, 0x9c, 0x87, 0xb1, 0x23, 0x23, 0x4a, 0x2e, 0x82, 0xec, 0xa6, 0x55, 0x97, - 0x6e, 0xdf, 0x1f, 0x3d, 0xbd, 0x4f, 0x3f, 0xb8, 0x2e, 0x0d, 0xf5, 0x68, 0xeb, 0xcc, 0x56, 0x94, - 0x7d, 0xe4, 0xa5, 0xa6, 0x22, 0xf7, 0x3a, 0xed, 0x1c, 0x12, 0xf2, 0xcb, 0xa9, 0x4e, 0x7d, 0x34, - 0xd5, 0xcb, 0x6f, 0x06, 0xf0, 0x80, 0x4d, 0xa3, 0x15, 0x19, 0xb4, 0xfc, 0x0a, 0x92, 0x7e, 0xdd, - 0x96, 0xd8, 0x08, 0xb6, 0x60, 0x37, 0x67, 0x2f, 0x93, 0xe3, 0x1c, 0xe6, 0xb6, 0x68, 0xb5, 0x98, - 0x85, 0x34, 0x68, 0xbe, 0x80, 0xf3, 0x37, 0xdd, 0x2b, 0x67, 0x3a, 0x7f, 0x2a, 0xe2, 0x50, 0x1d, - 0x46, 0xfc, 0x02, 0xe2, 0x95, 0x33, 0x62, 0x1e, 0x1a, 0x2f, 0xb9, 0x80, 0x53, 0xe5, 0x74, 0x41, - 0xe8, 0xc4, 0x49, 0x48, 0x77, 0xd6, 0x7f, 0x6e, 0x8d, 0x25, 0xed, 0x44, 0xb2, 0xfd, 0xbc, 0x75, - 0xcb, 0x27, 0x88, 0x9f, 0x2b, 0xda, 0x0f, 0x60, 0xc7, 0x07, 0xcc, 0x8e, 0x0e, 0x88, 0xf7, 0x03, - 0xee, 0x1f, 0x7f, 0x06, 0xc9, 0x36, 0x83, 0x64, 0x7f, 0x83, 0x64, 0x5f, 0xa3, 0x8c, 0x36, 0xa3, - 0x8c, 0x7e, 0x47, 0x19, 0xbd, 0xe6, 0xb5, 0xa1, 0xf7, 0x55, 0x99, 0x2a, 0x6c, 0xb3, 0x89, 0x14, - 0x56, 0x95, 0x51, 0xa6, 0x68, 0xb2, 0x1a, 0x6f, 0x77, 0x6c, 0x3f, 0x03, 0x5d, 0x5a, 0x77, 0xba, - 0x2f, 0x93, 0x00, 0xf6, 0xee, 0x3f, 0x00, 0x00, 0xff, 0xff, 0x73, 0x75, 0x1b, 0xf4, 0x79, 0x01, - 0x00, 0x00, + // 336 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x91, 0x3f, 0x4f, 0xc2, 0x40, + 0x18, 0xc6, 0x7b, 0xb6, 0x80, 0xbc, 0x2e, 0xe6, 0x24, 0xe6, 0x34, 0xe1, 0x24, 0x4c, 0x2c, 0xd2, + 0x10, 0x37, 0x47, 0x9d, 0x8c, 0x89, 0x03, 0x71, 0x72, 0x21, 0x6d, 0xb9, 0xd6, 0x8b, 0xed, 0xbd, + 0xa4, 0xbd, 0xaa, 0x7c, 0x03, 0x47, 0x3f, 0x82, 0x83, 0xdf, 0xc4, 0xc5, 0x91, 0xd1, 0xd1, 0xc0, + 0x17, 0x31, 0xbd, 0x1e, 0xc8, 0xc2, 0xf6, 0xfc, 0x69, 0xfb, 0xf6, 0x97, 0x07, 0xba, 0xa1, 0xd4, + 0x05, 0xaa, 0xc4, 0x57, 0xb1, 0xf6, 0x9f, 0x47, 0xa1, 0xd0, 0xc1, 0xa8, 0xd2, 0xc3, 0x59, 0x8e, + 0x1a, 0xe9, 0x91, 0xad, 0x87, 0x55, 0x64, 0xeb, 0xd3, 0x4e, 0x82, 0x09, 0x9a, 0xde, 0xaf, 0x54, + 0xfd, 0x68, 0xff, 0x8b, 0x00, 0x5c, 0x63, 0x9a, 0x8a, 0x48, 0x4b, 0x54, 0xf4, 0x18, 0x9a, 0xc5, + 0x3c, 0x0b, 0x31, 0x65, 0xa4, 0x47, 0x06, 0xed, 0xb1, 0x75, 0x94, 0x82, 0xa7, 0x82, 0x4c, 0xb0, + 0x3d, 0x93, 0x1a, 0x4d, 0x7b, 0x70, 0x30, 0x15, 0x45, 0x94, 0xcb, 0x59, 0xf5, 0x2a, 0x73, 0x4d, + 0xb5, 0x1d, 0xd1, 0x43, 0x70, 0xcb, 0x5c, 0x32, 0xcf, 0x34, 0x95, 0xa4, 0x0c, 0x5a, 0x51, 0x2e, + 0x02, 0x8d, 0x39, 0x6b, 0x98, 0x74, 0x6d, 0xab, 0xcb, 0x99, 0x54, 0x5a, 0xe4, 0xac, 0x59, 0x5f, + 0xae, 0x1d, 0xed, 0x02, 0xa8, 0x32, 0x9b, 0x68, 0x7c, 0x12, 0xaa, 0x60, 0xad, 0x1e, 0x19, 0x78, + 0xe3, 0xb6, 0x2a, 0xb3, 0x7b, 0x13, 0x5c, 0x7a, 0x6f, 0x1f, 0x67, 0x4e, 0xff, 0x93, 0x80, 0x7b, + 0x17, 0x6b, 0xca, 0x01, 0xa2, 0x0d, 0x8c, 0x45, 0xd8, 0x4a, 0xe8, 0x09, 0xec, 0x9b, 0x0f, 0x4d, + 0xe4, 0xd4, 0xa2, 0xb4, 0x8c, 0xbf, 0x99, 0x6e, 0x08, 0xdd, 0xdd, 0x84, 0xde, 0x4e, 0xc2, 0xc6, + 0x3f, 0x61, 0x07, 0x1a, 0xf8, 0xa2, 0x36, 0x18, 0xb5, 0xa9, 0x7f, 0xf3, 0xea, 0xf6, 0x7b, 0xc9, + 0xc9, 0x62, 0xc9, 0xc9, 0xef, 0x92, 0x93, 0xf7, 0x15, 0x77, 0x16, 0x2b, 0xee, 0xfc, 0xac, 0xb8, + 0xf3, 0x30, 0x4a, 0xa4, 0x7e, 0x2c, 0xc3, 0x61, 0x84, 0x99, 0x6f, 0xc7, 0xc3, 0x38, 0x96, 0x91, + 0x0c, 0x52, 0x3f, 0xc1, 0xf3, 0xf5, 0xdc, 0xaf, 0x66, 0x70, 0x3d, 0x9f, 0x89, 0x22, 0x6c, 0x9a, + 0x01, 0x2f, 0xfe, 0x02, 0x00, 0x00, 0xff, 0xff, 0x6e, 0x12, 0x79, 0x6b, 0x0c, 0x02, 0x00, 0x00, } func (m *Collection) Marshal() (dAtA []byte, err error) { @@ -214,6 +160,11 @@ func (m *Collection) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.NumTokens != 0 { + i = encodeVarintNft(dAtA, i, uint64(m.NumTokens)) + i-- + dAtA[i] = 0x38 + } if len(m.Minter) > 0 { i -= len(m.Minter) copy(dAtA[i:], m.Minter) @@ -279,25 +230,46 @@ func (m *Nft) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if len(m.Owner) > 0 { + i -= len(m.Owner) + copy(dAtA[i:], m.Owner) + i = encodeVarintNft(dAtA, i, uint64(len(m.Owner))) + i-- + dAtA[i] = 0x32 + } if len(m.Uri) > 0 { i -= len(m.Uri) copy(dAtA[i:], m.Uri) i = encodeVarintNft(dAtA, i, uint64(len(m.Uri))) i-- - dAtA[i] = 0x1a + dAtA[i] = 0x2a } if len(m.Description) > 0 { i -= len(m.Description) copy(dAtA[i:], m.Description) i = encodeVarintNft(dAtA, i, uint64(len(m.Description))) i-- - dAtA[i] = 0x12 + dAtA[i] = 0x22 } if len(m.Name) > 0 { i -= len(m.Name) copy(dAtA[i:], m.Name) i = encodeVarintNft(dAtA, i, uint64(len(m.Name))) i-- + dAtA[i] = 0x1a + } + if len(m.TokenId) > 0 { + i -= len(m.TokenId) + copy(dAtA[i:], m.TokenId) + i = encodeVarintNft(dAtA, i, uint64(len(m.TokenId))) + i-- + dAtA[i] = 0x12 + } + if len(m.Collection) > 0 { + i -= len(m.Collection) + copy(dAtA[i:], m.Collection) + i = encodeVarintNft(dAtA, i, uint64(len(m.Collection))) + i-- dAtA[i] = 0xa } return len(dAtA) - i, nil @@ -344,6 +316,9 @@ func (m *Collection) Size() (n int) { if l > 0 { n += 1 + l + sovNft(uint64(l)) } + if m.NumTokens != 0 { + n += 1 + sovNft(uint64(m.NumTokens)) + } return n } @@ -353,6 +328,14 @@ func (m *Nft) Size() (n int) { } var l int _ = l + l = len(m.Collection) + if l > 0 { + n += 1 + l + sovNft(uint64(l)) + } + l = len(m.TokenId) + if l > 0 { + n += 1 + l + sovNft(uint64(l)) + } l = len(m.Name) if l > 0 { n += 1 + l + sovNft(uint64(l)) @@ -365,6 +348,10 @@ func (m *Nft) Size() (n int) { if l > 0 { n += 1 + l + sovNft(uint64(l)) } + l = len(m.Owner) + if l > 0 { + n += 1 + l + sovNft(uint64(l)) + } return n } @@ -595,6 +582,25 @@ func (m *Collection) Unmarshal(dAtA []byte) error { } m.Minter = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex + case 7: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field NumTokens", wireType) + } + m.NumTokens = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowNft + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.NumTokens |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skipNft(dAtA[iNdEx:]) @@ -646,6 +652,70 @@ func (m *Nft) Unmarshal(dAtA []byte) error { } switch fieldNum { case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Collection", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowNft + } + 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 ErrInvalidLengthNft + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthNft + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Collection = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TokenId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowNft + } + 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 ErrInvalidLengthNft + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthNft + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.TokenId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) } @@ -677,7 +747,7 @@ func (m *Nft) Unmarshal(dAtA []byte) error { } m.Name = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 2: + case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Description", wireType) } @@ -709,7 +779,7 @@ func (m *Nft) Unmarshal(dAtA []byte) error { } m.Description = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 3: + case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Uri", wireType) } @@ -741,6 +811,38 @@ func (m *Nft) Unmarshal(dAtA []byte) error { } m.Uri = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex + case 6: + 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 ErrIntOverflowNft + } + 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 ErrInvalidLengthNft + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthNft + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Owner = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipNft(dAtA[iNdEx:]) diff --git a/x/nft/types/query.pb.go b/x/nft/types/query.pb.go new file mode 100644 index 00000000..0be2937f --- /dev/null +++ b/x/nft/types/query.pb.go @@ -0,0 +1,2624 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: bitsong/nft/v1beta1/query.proto + +package types + +import ( + context "context" + fmt "fmt" + query "github.com/cosmos/cosmos-sdk/types/query" + _ "github.com/cosmos/gogoproto/gogoproto" + grpc1 "github.com/cosmos/gogoproto/grpc" + proto "github.com/cosmos/gogoproto/proto" + _ "google.golang.org/genproto/googleapis/api/annotations" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +type QueryCollectionRequest struct { + Collection string `protobuf:"bytes,1,opt,name=collection,proto3" json:"collection,omitempty"` +} + +func (m *QueryCollectionRequest) Reset() { *m = QueryCollectionRequest{} } +func (m *QueryCollectionRequest) String() string { return proto.CompactTextString(m) } +func (*QueryCollectionRequest) ProtoMessage() {} +func (*QueryCollectionRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_c3d20ffbceb85197, []int{0} +} +func (m *QueryCollectionRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryCollectionRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryCollectionRequest.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 *QueryCollectionRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryCollectionRequest.Merge(m, src) +} +func (m *QueryCollectionRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryCollectionRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryCollectionRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryCollectionRequest proto.InternalMessageInfo + +type QueryCollectionResponse struct { + Collection *Collection `protobuf:"bytes,1,opt,name=collection,proto3" json:"collection,omitempty"` +} + +func (m *QueryCollectionResponse) Reset() { *m = QueryCollectionResponse{} } +func (m *QueryCollectionResponse) String() string { return proto.CompactTextString(m) } +func (*QueryCollectionResponse) ProtoMessage() {} +func (*QueryCollectionResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_c3d20ffbceb85197, []int{1} +} +func (m *QueryCollectionResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryCollectionResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryCollectionResponse.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 *QueryCollectionResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryCollectionResponse.Merge(m, src) +} +func (m *QueryCollectionResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryCollectionResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryCollectionResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryCollectionResponse proto.InternalMessageInfo + +type QueryOwnerOfRequest struct { + Collection string `protobuf:"bytes,1,opt,name=collection,proto3" json:"collection,omitempty"` + TokenId string `protobuf:"bytes,2,opt,name=token_id,json=tokenId,proto3" json:"token_id,omitempty"` +} + +func (m *QueryOwnerOfRequest) Reset() { *m = QueryOwnerOfRequest{} } +func (m *QueryOwnerOfRequest) String() string { return proto.CompactTextString(m) } +func (*QueryOwnerOfRequest) ProtoMessage() {} +func (*QueryOwnerOfRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_c3d20ffbceb85197, []int{2} +} +func (m *QueryOwnerOfRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryOwnerOfRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryOwnerOfRequest.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 *QueryOwnerOfRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryOwnerOfRequest.Merge(m, src) +} +func (m *QueryOwnerOfRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryOwnerOfRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryOwnerOfRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryOwnerOfRequest proto.InternalMessageInfo + +type QueryOwnerOfResponse struct { + Owner string `protobuf:"bytes,1,opt,name=owner,proto3" json:"owner,omitempty"` +} + +func (m *QueryOwnerOfResponse) Reset() { *m = QueryOwnerOfResponse{} } +func (m *QueryOwnerOfResponse) String() string { return proto.CompactTextString(m) } +func (*QueryOwnerOfResponse) ProtoMessage() {} +func (*QueryOwnerOfResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_c3d20ffbceb85197, []int{3} +} +func (m *QueryOwnerOfResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryOwnerOfResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryOwnerOfResponse.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 *QueryOwnerOfResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryOwnerOfResponse.Merge(m, src) +} +func (m *QueryOwnerOfResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryOwnerOfResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryOwnerOfResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryOwnerOfResponse proto.InternalMessageInfo + +type QueryNumTokensRequest struct { + Collection string `protobuf:"bytes,1,opt,name=collection,proto3" json:"collection,omitempty"` +} + +func (m *QueryNumTokensRequest) Reset() { *m = QueryNumTokensRequest{} } +func (m *QueryNumTokensRequest) String() string { return proto.CompactTextString(m) } +func (*QueryNumTokensRequest) ProtoMessage() {} +func (*QueryNumTokensRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_c3d20ffbceb85197, []int{4} +} +func (m *QueryNumTokensRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryNumTokensRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryNumTokensRequest.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 *QueryNumTokensRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryNumTokensRequest.Merge(m, src) +} +func (m *QueryNumTokensRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryNumTokensRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryNumTokensRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryNumTokensRequest proto.InternalMessageInfo + +type QueryNumTokensResponse struct { + Count uint64 `protobuf:"varint,1,opt,name=count,proto3" json:"count,omitempty"` +} + +func (m *QueryNumTokensResponse) Reset() { *m = QueryNumTokensResponse{} } +func (m *QueryNumTokensResponse) String() string { return proto.CompactTextString(m) } +func (*QueryNumTokensResponse) ProtoMessage() {} +func (*QueryNumTokensResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_c3d20ffbceb85197, []int{5} +} +func (m *QueryNumTokensResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryNumTokensResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryNumTokensResponse.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 *QueryNumTokensResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryNumTokensResponse.Merge(m, src) +} +func (m *QueryNumTokensResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryNumTokensResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryNumTokensResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryNumTokensResponse proto.InternalMessageInfo + +type QueryNftInfoRequest struct { + Collection string `protobuf:"bytes,1,opt,name=collection,proto3" json:"collection,omitempty"` + TokenId string `protobuf:"bytes,2,opt,name=token_id,json=tokenId,proto3" json:"token_id,omitempty"` +} + +func (m *QueryNftInfoRequest) Reset() { *m = QueryNftInfoRequest{} } +func (m *QueryNftInfoRequest) String() string { return proto.CompactTextString(m) } +func (*QueryNftInfoRequest) ProtoMessage() {} +func (*QueryNftInfoRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_c3d20ffbceb85197, []int{6} +} +func (m *QueryNftInfoRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryNftInfoRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryNftInfoRequest.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 *QueryNftInfoRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryNftInfoRequest.Merge(m, src) +} +func (m *QueryNftInfoRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryNftInfoRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryNftInfoRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryNftInfoRequest proto.InternalMessageInfo + +type QueryNftInfoResponse struct { + Nft *Nft `protobuf:"bytes,1,opt,name=nft,proto3" json:"nft,omitempty"` +} + +func (m *QueryNftInfoResponse) Reset() { *m = QueryNftInfoResponse{} } +func (m *QueryNftInfoResponse) String() string { return proto.CompactTextString(m) } +func (*QueryNftInfoResponse) ProtoMessage() {} +func (*QueryNftInfoResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_c3d20ffbceb85197, []int{7} +} +func (m *QueryNftInfoResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryNftInfoResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryNftInfoResponse.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 *QueryNftInfoResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryNftInfoResponse.Merge(m, src) +} +func (m *QueryNftInfoResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryNftInfoResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryNftInfoResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryNftInfoResponse proto.InternalMessageInfo + +type QueryNftsRequest struct { + Collection string `protobuf:"bytes,1,opt,name=collection,proto3" json:"collection,omitempty"` + Pagination *query.PageRequest `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (m *QueryNftsRequest) Reset() { *m = QueryNftsRequest{} } +func (m *QueryNftsRequest) String() string { return proto.CompactTextString(m) } +func (*QueryNftsRequest) ProtoMessage() {} +func (*QueryNftsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_c3d20ffbceb85197, []int{8} +} +func (m *QueryNftsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryNftsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryNftsRequest.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 *QueryNftsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryNftsRequest.Merge(m, src) +} +func (m *QueryNftsRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryNftsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryNftsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryNftsRequest proto.InternalMessageInfo + +type QueryNftsResponse struct { + Nfts []Nft `protobuf:"bytes,1,rep,name=nfts,proto3" json:"nfts"` + Pagination *query.PageResponse `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (m *QueryNftsResponse) Reset() { *m = QueryNftsResponse{} } +func (m *QueryNftsResponse) String() string { return proto.CompactTextString(m) } +func (*QueryNftsResponse) ProtoMessage() {} +func (*QueryNftsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_c3d20ffbceb85197, []int{9} +} +func (m *QueryNftsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryNftsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryNftsResponse.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 *QueryNftsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryNftsResponse.Merge(m, src) +} +func (m *QueryNftsResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryNftsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryNftsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryNftsResponse proto.InternalMessageInfo + +type QueryAllNftsByOwnerRequest struct { + Owner string `protobuf:"bytes,1,opt,name=owner,proto3" json:"owner,omitempty"` +} + +func (m *QueryAllNftsByOwnerRequest) Reset() { *m = QueryAllNftsByOwnerRequest{} } +func (m *QueryAllNftsByOwnerRequest) String() string { return proto.CompactTextString(m) } +func (*QueryAllNftsByOwnerRequest) ProtoMessage() {} +func (*QueryAllNftsByOwnerRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_c3d20ffbceb85197, []int{10} +} +func (m *QueryAllNftsByOwnerRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryAllNftsByOwnerRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryAllNftsByOwnerRequest.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 *QueryAllNftsByOwnerRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryAllNftsByOwnerRequest.Merge(m, src) +} +func (m *QueryAllNftsByOwnerRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryAllNftsByOwnerRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryAllNftsByOwnerRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryAllNftsByOwnerRequest proto.InternalMessageInfo + +type QueryAllNftsByOwnerResponse struct { + Nfts []Nft `protobuf:"bytes,1,rep,name=nfts,proto3" json:"nfts"` +} + +func (m *QueryAllNftsByOwnerResponse) Reset() { *m = QueryAllNftsByOwnerResponse{} } +func (m *QueryAllNftsByOwnerResponse) String() string { return proto.CompactTextString(m) } +func (*QueryAllNftsByOwnerResponse) ProtoMessage() {} +func (*QueryAllNftsByOwnerResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_c3d20ffbceb85197, []int{11} +} +func (m *QueryAllNftsByOwnerResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryAllNftsByOwnerResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryAllNftsByOwnerResponse.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 *QueryAllNftsByOwnerResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryAllNftsByOwnerResponse.Merge(m, src) +} +func (m *QueryAllNftsByOwnerResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryAllNftsByOwnerResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryAllNftsByOwnerResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryAllNftsByOwnerResponse proto.InternalMessageInfo + +func init() { + proto.RegisterType((*QueryCollectionRequest)(nil), "bitsong.nft.v1beta1.QueryCollectionRequest") + proto.RegisterType((*QueryCollectionResponse)(nil), "bitsong.nft.v1beta1.QueryCollectionResponse") + proto.RegisterType((*QueryOwnerOfRequest)(nil), "bitsong.nft.v1beta1.QueryOwnerOfRequest") + proto.RegisterType((*QueryOwnerOfResponse)(nil), "bitsong.nft.v1beta1.QueryOwnerOfResponse") + proto.RegisterType((*QueryNumTokensRequest)(nil), "bitsong.nft.v1beta1.QueryNumTokensRequest") + proto.RegisterType((*QueryNumTokensResponse)(nil), "bitsong.nft.v1beta1.QueryNumTokensResponse") + proto.RegisterType((*QueryNftInfoRequest)(nil), "bitsong.nft.v1beta1.QueryNftInfoRequest") + proto.RegisterType((*QueryNftInfoResponse)(nil), "bitsong.nft.v1beta1.QueryNftInfoResponse") + proto.RegisterType((*QueryNftsRequest)(nil), "bitsong.nft.v1beta1.QueryNftsRequest") + proto.RegisterType((*QueryNftsResponse)(nil), "bitsong.nft.v1beta1.QueryNftsResponse") + proto.RegisterType((*QueryAllNftsByOwnerRequest)(nil), "bitsong.nft.v1beta1.QueryAllNftsByOwnerRequest") + proto.RegisterType((*QueryAllNftsByOwnerResponse)(nil), "bitsong.nft.v1beta1.QueryAllNftsByOwnerResponse") +} + +func init() { proto.RegisterFile("bitsong/nft/v1beta1/query.proto", fileDescriptor_c3d20ffbceb85197) } + +var fileDescriptor_c3d20ffbceb85197 = []byte{ + // 745 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x96, 0x41, 0x4f, 0x13, 0x41, + 0x14, 0xc7, 0x3b, 0x50, 0x2c, 0x3c, 0x12, 0xa3, 0x03, 0x6a, 0x5d, 0x74, 0x4b, 0x9a, 0x88, 0x58, + 0x70, 0x47, 0x8a, 0x8a, 0x1a, 0x13, 0x42, 0x4d, 0x34, 0x44, 0x03, 0xda, 0x78, 0xc2, 0x43, 0xb3, + 0x2d, 0xbb, 0xeb, 0xc6, 0x76, 0xa6, 0xb0, 0x53, 0x94, 0x10, 0x2e, 0xc6, 0x03, 0xf1, 0x64, 0xe2, + 0xcd, 0x8b, 0x84, 0xb3, 0x07, 0x3f, 0x06, 0x47, 0x8c, 0x17, 0x4f, 0xc6, 0x80, 0x89, 0x7e, 0x0c, + 0xb3, 0xb3, 0xd3, 0xee, 0x2e, 0xec, 0x96, 0xd5, 0x78, 0x62, 0x77, 0xe7, 0xbd, 0xff, 0xfb, 0xcd, + 0x7f, 0xde, 0x1b, 0x0a, 0xb9, 0xaa, 0xcd, 0x1d, 0x46, 0x2d, 0x42, 0x4d, 0x4e, 0xd6, 0xa6, 0xaa, + 0x06, 0xd7, 0xa7, 0xc8, 0x4a, 0xcb, 0x58, 0x5d, 0xd7, 0x9a, 0xab, 0x8c, 0x33, 0x3c, 0x24, 0x03, + 0x34, 0x6a, 0x72, 0x4d, 0x06, 0x28, 0xc3, 0x16, 0xb3, 0x98, 0x58, 0x27, 0xee, 0x93, 0x17, 0xaa, + 0x5c, 0xb0, 0x18, 0xb3, 0xea, 0x06, 0xd1, 0x9b, 0x36, 0xd1, 0x29, 0x65, 0x5c, 0xe7, 0x36, 0xa3, + 0x8e, 0x5c, 0x1d, 0xa9, 0x31, 0xa7, 0xc1, 0x1c, 0x4f, 0x9c, 0xac, 0x85, 0xaa, 0x28, 0x05, 0xb9, + 0x58, 0xd5, 0x1d, 0xa3, 0x13, 0xe1, 0xc1, 0x34, 0x75, 0xcb, 0xa6, 0x42, 0x49, 0xc6, 0x5e, 0x8c, + 0x42, 0x76, 0xe9, 0xc4, 0x72, 0xbe, 0x04, 0x67, 0x9f, 0xb8, 0x02, 0xf7, 0x58, 0xbd, 0x6e, 0xd4, + 0xdc, 0xbc, 0xb2, 0xb1, 0xd2, 0x32, 0x1c, 0x8e, 0x55, 0x80, 0x5a, 0xe7, 0x63, 0x16, 0x8d, 0xa2, + 0xf1, 0x81, 0x72, 0xe0, 0xcb, 0x9d, 0xfe, 0xad, 0xed, 0x5c, 0xea, 0xf7, 0x76, 0x2e, 0x95, 0x5f, + 0x86, 0x73, 0x47, 0x34, 0x9c, 0x26, 0xa3, 0x8e, 0x81, 0x67, 0x8f, 0x88, 0x0c, 0x16, 0x73, 0x5a, + 0x84, 0x49, 0x5a, 0x20, 0x39, 0xba, 0xca, 0x12, 0x0c, 0x89, 0x2a, 0x8b, 0x2f, 0xa9, 0xb1, 0xba, + 0x68, 0x26, 0xc4, 0xc4, 0xe7, 0xa1, 0x9f, 0xb3, 0x17, 0x06, 0xad, 0xd8, 0xcb, 0xd9, 0x1e, 0xb1, + 0x9a, 0x11, 0xef, 0xf3, 0xcb, 0x01, 0xed, 0x9b, 0x30, 0x1c, 0xd6, 0x96, 0xf8, 0xc3, 0xd0, 0xc7, + 0xdc, 0x4f, 0x52, 0xd7, 0x7b, 0x09, 0xe4, 0xcd, 0xc1, 0x19, 0x91, 0xb7, 0xd0, 0x6a, 0x3c, 0x75, + 0x45, 0x9d, 0xbf, 0x37, 0xef, 0x96, 0x3c, 0x80, 0x80, 0x84, 0x5f, 0xbc, 0xc6, 0x5a, 0x94, 0x8b, + 0xf4, 0x74, 0xd9, 0x7b, 0x89, 0x30, 0x64, 0xc1, 0xe4, 0xf3, 0xd4, 0x64, 0xff, 0xd5, 0x90, 0x47, + 0xd2, 0x90, 0x8e, 0xb6, 0x64, 0x2a, 0x40, 0x2f, 0x35, 0xb9, 0x3c, 0xc8, 0x6c, 0xe4, 0x41, 0x2e, + 0x98, 0xbc, 0xec, 0x06, 0x05, 0xd4, 0xde, 0x20, 0x38, 0xd5, 0x96, 0x4b, 0x6a, 0x11, 0xbe, 0x0f, + 0xe0, 0x37, 0xb3, 0x20, 0x1d, 0x2c, 0x8e, 0x69, 0x5e, 0xe7, 0x6b, 0x6e, 0xe7, 0x6b, 0xde, 0x48, + 0xb4, 0xeb, 0x3e, 0xd6, 0x2d, 0x43, 0x6a, 0x97, 0x03, 0x99, 0x01, 0x8c, 0x0f, 0x08, 0x4e, 0x07, + 0x30, 0xe4, 0x96, 0x8a, 0x90, 0xa6, 0x26, 0x77, 0xb2, 0x68, 0xb4, 0xb7, 0xdb, 0x9e, 0x4a, 0xe9, + 0xdd, 0xef, 0xb9, 0x54, 0x59, 0xc4, 0xe2, 0x07, 0x11, 0x6c, 0x97, 0x8f, 0x65, 0xf3, 0x0a, 0xc6, + 0xc0, 0xdd, 0x05, 0x45, 0xb0, 0xcd, 0xd5, 0xeb, 0x2e, 0x5e, 0xc9, 0xeb, 0xc5, 0xb6, 0x59, 0xc7, + 0x35, 0xe2, 0x33, 0x18, 0x89, 0xcc, 0xfe, 0xf7, 0x3d, 0xfa, 0xe2, 0xc5, 0x2f, 0x19, 0xe8, 0x13, + 0xea, 0x78, 0x07, 0x01, 0xf8, 0x83, 0x8a, 0x27, 0x22, 0x85, 0xa2, 0xef, 0x13, 0x65, 0x32, 0x59, + 0xb0, 0x47, 0x9c, 0xbf, 0xbd, 0xf5, 0xeb, 0x73, 0x01, 0xbd, 0xfe, 0xfa, 0xf3, 0x7d, 0x8f, 0x86, + 0x27, 0x49, 0xd4, 0x25, 0xe6, 0xf7, 0x0a, 0xd9, 0xf0, 0x9f, 0x37, 0xf1, 0x47, 0x04, 0x19, 0x39, + 0xc8, 0x78, 0x3c, 0xbe, 0x68, 0xf8, 0x1e, 0x51, 0xae, 0x24, 0x88, 0x94, 0x6c, 0xb3, 0x3e, 0xdb, + 0x75, 0x5c, 0x8c, 0x64, 0x13, 0xe7, 0x13, 0xc2, 0x22, 0x1b, 0xed, 0xa9, 0x13, 0x84, 0x03, 0x9d, + 0x79, 0xc7, 0x85, 0xf8, 0xca, 0x87, 0xef, 0x15, 0x65, 0x22, 0x51, 0x6c, 0x72, 0x0f, 0x69, 0xab, + 0x51, 0x11, 0x5c, 0x4e, 0xd8, 0xc3, 0x1d, 0x04, 0x19, 0x39, 0xfb, 0xdd, 0x3c, 0x0c, 0x5f, 0x3d, + 0xdd, 0x3c, 0x3c, 0x74, 0x91, 0xe4, 0x4b, 0x3e, 0xdb, 0x0c, 0xbe, 0x41, 0x62, 0xfe, 0x49, 0x55, + 0x6c, 0x6a, 0xb2, 0x58, 0x1b, 0xdf, 0x22, 0x48, 0xbb, 0xdd, 0x8e, 0x2f, 0x75, 0xad, 0xdb, 0x31, + 0x6f, 0xec, 0xb8, 0x30, 0xc9, 0x36, 0xed, 0xb3, 0x8d, 0xe3, 0xb1, 0x38, 0xb6, 0x43, 0x8e, 0x7d, + 0x42, 0x70, 0x32, 0x3c, 0x7d, 0x98, 0xc4, 0xd7, 0x8b, 0x9c, 0x72, 0xe5, 0x5a, 0xf2, 0x04, 0x89, + 0x3a, 0xe3, 0xa3, 0x4e, 0xe2, 0x42, 0x2c, 0x6a, 0xa5, 0xba, 0x5e, 0x91, 0x2d, 0x29, 0xfe, 0x6c, + 0x96, 0x1e, 0xee, 0xee, 0xab, 0x68, 0x6f, 0x5f, 0x45, 0x3f, 0xf6, 0x55, 0xf4, 0xee, 0x40, 0x4d, + 0xed, 0x1d, 0xa8, 0xa9, 0x6f, 0x07, 0x6a, 0x6a, 0x69, 0xca, 0xb2, 0xf9, 0xf3, 0x56, 0x55, 0xab, + 0xb1, 0x46, 0x5b, 0x8f, 0x99, 0xa6, 0x5d, 0xb3, 0xf5, 0x3a, 0xb1, 0xd8, 0xd5, 0x76, 0x89, 0x57, + 0xa2, 0x08, 0x5f, 0x6f, 0x1a, 0x4e, 0xf5, 0x84, 0xf8, 0x2d, 0x31, 0xfd, 0x27, 0x00, 0x00, 0xff, + 0xff, 0xa0, 0x04, 0x7a, 0x98, 0x1f, 0x09, 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 { + Collection(ctx context.Context, in *QueryCollectionRequest, opts ...grpc.CallOption) (*QueryCollectionResponse, error) + OwnerOf(ctx context.Context, in *QueryOwnerOfRequest, opts ...grpc.CallOption) (*QueryOwnerOfResponse, error) + NumTokens(ctx context.Context, in *QueryNumTokensRequest, opts ...grpc.CallOption) (*QueryNumTokensResponse, error) + NftInfo(ctx context.Context, in *QueryNftInfoRequest, opts ...grpc.CallOption) (*QueryNftInfoResponse, error) + Nfts(ctx context.Context, in *QueryNftsRequest, opts ...grpc.CallOption) (*QueryNftsResponse, error) + AllNftsByOwner(ctx context.Context, in *QueryAllNftsByOwnerRequest, opts ...grpc.CallOption) (*QueryAllNftsByOwnerResponse, error) +} + +type queryClient struct { + cc grpc1.ClientConn +} + +func NewQueryClient(cc grpc1.ClientConn) QueryClient { + return &queryClient{cc} +} + +func (c *queryClient) Collection(ctx context.Context, in *QueryCollectionRequest, opts ...grpc.CallOption) (*QueryCollectionResponse, error) { + out := new(QueryCollectionResponse) + err := c.cc.Invoke(ctx, "/bitsong.nft.v1beta1.Query/Collection", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) OwnerOf(ctx context.Context, in *QueryOwnerOfRequest, opts ...grpc.CallOption) (*QueryOwnerOfResponse, error) { + out := new(QueryOwnerOfResponse) + err := c.cc.Invoke(ctx, "/bitsong.nft.v1beta1.Query/OwnerOf", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) NumTokens(ctx context.Context, in *QueryNumTokensRequest, opts ...grpc.CallOption) (*QueryNumTokensResponse, error) { + out := new(QueryNumTokensResponse) + err := c.cc.Invoke(ctx, "/bitsong.nft.v1beta1.Query/NumTokens", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) NftInfo(ctx context.Context, in *QueryNftInfoRequest, opts ...grpc.CallOption) (*QueryNftInfoResponse, error) { + out := new(QueryNftInfoResponse) + err := c.cc.Invoke(ctx, "/bitsong.nft.v1beta1.Query/NftInfo", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) Nfts(ctx context.Context, in *QueryNftsRequest, opts ...grpc.CallOption) (*QueryNftsResponse, error) { + out := new(QueryNftsResponse) + err := c.cc.Invoke(ctx, "/bitsong.nft.v1beta1.Query/Nfts", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) AllNftsByOwner(ctx context.Context, in *QueryAllNftsByOwnerRequest, opts ...grpc.CallOption) (*QueryAllNftsByOwnerResponse, error) { + out := new(QueryAllNftsByOwnerResponse) + err := c.cc.Invoke(ctx, "/bitsong.nft.v1beta1.Query/AllNftsByOwner", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// QueryServer is the server API for Query service. +type QueryServer interface { + Collection(context.Context, *QueryCollectionRequest) (*QueryCollectionResponse, error) + OwnerOf(context.Context, *QueryOwnerOfRequest) (*QueryOwnerOfResponse, error) + NumTokens(context.Context, *QueryNumTokensRequest) (*QueryNumTokensResponse, error) + NftInfo(context.Context, *QueryNftInfoRequest) (*QueryNftInfoResponse, error) + Nfts(context.Context, *QueryNftsRequest) (*QueryNftsResponse, error) + AllNftsByOwner(context.Context, *QueryAllNftsByOwnerRequest) (*QueryAllNftsByOwnerResponse, error) +} + +// UnimplementedQueryServer can be embedded to have forward compatible implementations. +type UnimplementedQueryServer struct { +} + +func (*UnimplementedQueryServer) Collection(ctx context.Context, req *QueryCollectionRequest) (*QueryCollectionResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Collection not implemented") +} +func (*UnimplementedQueryServer) OwnerOf(ctx context.Context, req *QueryOwnerOfRequest) (*QueryOwnerOfResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method OwnerOf not implemented") +} +func (*UnimplementedQueryServer) NumTokens(ctx context.Context, req *QueryNumTokensRequest) (*QueryNumTokensResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method NumTokens not implemented") +} +func (*UnimplementedQueryServer) NftInfo(ctx context.Context, req *QueryNftInfoRequest) (*QueryNftInfoResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method NftInfo not implemented") +} +func (*UnimplementedQueryServer) Nfts(ctx context.Context, req *QueryNftsRequest) (*QueryNftsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Nfts not implemented") +} +func (*UnimplementedQueryServer) AllNftsByOwner(ctx context.Context, req *QueryAllNftsByOwnerRequest) (*QueryAllNftsByOwnerResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method AllNftsByOwner not implemented") +} + +func RegisterQueryServer(s grpc1.Server, srv QueryServer) { + s.RegisterService(&_Query_serviceDesc, srv) +} + +func _Query_Collection_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryCollectionRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).Collection(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/bitsong.nft.v1beta1.Query/Collection", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).Collection(ctx, req.(*QueryCollectionRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_OwnerOf_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryOwnerOfRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).OwnerOf(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/bitsong.nft.v1beta1.Query/OwnerOf", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).OwnerOf(ctx, req.(*QueryOwnerOfRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_NumTokens_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryNumTokensRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).NumTokens(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/bitsong.nft.v1beta1.Query/NumTokens", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).NumTokens(ctx, req.(*QueryNumTokensRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_NftInfo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryNftInfoRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).NftInfo(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/bitsong.nft.v1beta1.Query/NftInfo", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).NftInfo(ctx, req.(*QueryNftInfoRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_Nfts_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryNftsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).Nfts(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/bitsong.nft.v1beta1.Query/Nfts", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).Nfts(ctx, req.(*QueryNftsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_AllNftsByOwner_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryAllNftsByOwnerRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).AllNftsByOwner(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/bitsong.nft.v1beta1.Query/AllNftsByOwner", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).AllNftsByOwner(ctx, req.(*QueryAllNftsByOwnerRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var Query_serviceDesc = _Query_serviceDesc +var _Query_serviceDesc = grpc.ServiceDesc{ + ServiceName: "bitsong.nft.v1beta1.Query", + HandlerType: (*QueryServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Collection", + Handler: _Query_Collection_Handler, + }, + { + MethodName: "OwnerOf", + Handler: _Query_OwnerOf_Handler, + }, + { + MethodName: "NumTokens", + Handler: _Query_NumTokens_Handler, + }, + { + MethodName: "NftInfo", + Handler: _Query_NftInfo_Handler, + }, + { + MethodName: "Nfts", + Handler: _Query_Nfts_Handler, + }, + { + MethodName: "AllNftsByOwner", + Handler: _Query_AllNftsByOwner_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "bitsong/nft/v1beta1/query.proto", +} + +func (m *QueryCollectionRequest) 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 *QueryCollectionRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryCollectionRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Collection) > 0 { + i -= len(m.Collection) + copy(dAtA[i:], m.Collection) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Collection))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryCollectionResponse) 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 *QueryCollectionResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryCollectionResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Collection != nil { + { + size, err := m.Collection.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 *QueryOwnerOfRequest) 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 *QueryOwnerOfRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryOwnerOfRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.TokenId) > 0 { + i -= len(m.TokenId) + copy(dAtA[i:], m.TokenId) + i = encodeVarintQuery(dAtA, i, uint64(len(m.TokenId))) + i-- + dAtA[i] = 0x12 + } + if len(m.Collection) > 0 { + i -= len(m.Collection) + copy(dAtA[i:], m.Collection) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Collection))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryOwnerOfResponse) 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 *QueryOwnerOfResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryOwnerOfResponse) 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 *QueryNumTokensRequest) 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 *QueryNumTokensRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryNumTokensRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Collection) > 0 { + i -= len(m.Collection) + copy(dAtA[i:], m.Collection) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Collection))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryNumTokensResponse) 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 *QueryNumTokensResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryNumTokensResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Count != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.Count)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *QueryNftInfoRequest) 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 *QueryNftInfoRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryNftInfoRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.TokenId) > 0 { + i -= len(m.TokenId) + copy(dAtA[i:], m.TokenId) + i = encodeVarintQuery(dAtA, i, uint64(len(m.TokenId))) + i-- + dAtA[i] = 0x12 + } + if len(m.Collection) > 0 { + i -= len(m.Collection) + copy(dAtA[i:], m.Collection) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Collection))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryNftInfoResponse) 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 *QueryNftInfoResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryNftInfoResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Nft != nil { + { + size, err := m.Nft.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 *QueryNftsRequest) 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 *QueryNftsRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryNftsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Pagination != nil { + { + size, err := m.Pagination.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if len(m.Collection) > 0 { + i -= len(m.Collection) + copy(dAtA[i:], m.Collection) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Collection))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryNftsResponse) 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 *QueryNftsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryNftsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Pagination != nil { + { + size, err := m.Pagination.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if len(m.Nfts) > 0 { + for iNdEx := len(m.Nfts) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Nfts[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 *QueryAllNftsByOwnerRequest) 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 *QueryAllNftsByOwnerRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryAllNftsByOwnerRequest) 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 *QueryAllNftsByOwnerResponse) 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 *QueryAllNftsByOwnerResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryAllNftsByOwnerResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Nfts) > 0 { + for iNdEx := len(m.Nfts) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Nfts[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 *QueryCollectionRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Collection) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryCollectionResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Collection != nil { + l = m.Collection.Size() + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryOwnerOfRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Collection) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = len(m.TokenId) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryOwnerOfResponse) 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 *QueryNumTokensRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Collection) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryNumTokensResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Count != 0 { + n += 1 + sovQuery(uint64(m.Count)) + } + return n +} + +func (m *QueryNftInfoRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Collection) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = len(m.TokenId) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryNftInfoResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Nft != nil { + l = m.Nft.Size() + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryNftsRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Collection) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + if m.Pagination != nil { + l = m.Pagination.Size() + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryNftsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Nfts) > 0 { + for _, e := range m.Nfts { + l = e.Size() + n += 1 + l + sovQuery(uint64(l)) + } + } + if m.Pagination != nil { + l = m.Pagination.Size() + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryAllNftsByOwnerRequest) 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 *QueryAllNftsByOwnerResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Nfts) > 0 { + for _, e := range m.Nfts { + 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 *QueryCollectionRequest) 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: QueryCollectionRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryCollectionRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Collection", 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.Collection = 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 *QueryCollectionResponse) 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: QueryCollectionResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryCollectionResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Collection", 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.Collection == nil { + m.Collection = &Collection{} + } + if err := m.Collection.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 *QueryOwnerOfRequest) 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: QueryOwnerOfRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryOwnerOfRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Collection", 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.Collection = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TokenId", 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.TokenId = 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 *QueryOwnerOfResponse) 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: QueryOwnerOfResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryOwnerOfResponse: 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 *QueryNumTokensRequest) 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: QueryNumTokensRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryNumTokensRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Collection", 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.Collection = 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 *QueryNumTokensResponse) 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: QueryNumTokensResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryNumTokensResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Count", wireType) + } + m.Count = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Count |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + 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 *QueryNftInfoRequest) 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: QueryNftInfoRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryNftInfoRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Collection", 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.Collection = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TokenId", 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.TokenId = 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 *QueryNftInfoResponse) 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: QueryNftInfoResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryNftInfoResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Nft", 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.Nft == nil { + m.Nft = &Nft{} + } + if err := m.Nft.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 *QueryNftsRequest) 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: QueryNftsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryNftsRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Collection", 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.Collection = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pagination", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Pagination == nil { + m.Pagination = &query.PageRequest{} + } + if err := m.Pagination.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryNftsResponse) 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: QueryNftsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryNftsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Nfts", 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.Nfts = append(m.Nfts, Nft{}) + if err := m.Nfts[len(m.Nfts)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pagination", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Pagination == nil { + m.Pagination = &query.PageResponse{} + } + if err := m.Pagination.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryAllNftsByOwnerRequest) 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: QueryAllNftsByOwnerRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryAllNftsByOwnerRequest: 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 *QueryAllNftsByOwnerResponse) 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: QueryAllNftsByOwnerResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryAllNftsByOwnerResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Nfts", 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.Nfts = append(m.Nfts, Nft{}) + if err := m.Nfts[len(m.Nfts)-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/nft/types/query.pb.gw.go b/x/nft/types/query.pb.gw.go new file mode 100644 index 00000000..d2d559fa --- /dev/null +++ b/x/nft/types/query.pb.gw.go @@ -0,0 +1,756 @@ +// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. +// source: bitsong/nft/v1beta1/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_Collection_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryCollectionRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["collection"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "collection") + } + + protoReq.Collection, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "collection", err) + } + + msg, err := client.Collection(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_Collection_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryCollectionRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["collection"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "collection") + } + + protoReq.Collection, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "collection", err) + } + + msg, err := server.Collection(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_OwnerOf_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryOwnerOfRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["collection"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "collection") + } + + protoReq.Collection, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "collection", err) + } + + val, ok = pathParams["token_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "token_id") + } + + protoReq.TokenId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "token_id", err) + } + + msg, err := client.OwnerOf(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_OwnerOf_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryOwnerOfRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["collection"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "collection") + } + + protoReq.Collection, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "collection", err) + } + + val, ok = pathParams["token_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "token_id") + } + + protoReq.TokenId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "token_id", err) + } + + msg, err := server.OwnerOf(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_NumTokens_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryNumTokensRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["collection"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "collection") + } + + protoReq.Collection, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "collection", err) + } + + msg, err := client.NumTokens(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_NumTokens_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryNumTokensRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["collection"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "collection") + } + + protoReq.Collection, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "collection", err) + } + + msg, err := server.NumTokens(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_NftInfo_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryNftInfoRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["collection"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "collection") + } + + protoReq.Collection, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "collection", err) + } + + val, ok = pathParams["token_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "token_id") + } + + protoReq.TokenId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "token_id", err) + } + + msg, err := client.NftInfo(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_NftInfo_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryNftInfoRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["collection"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "collection") + } + + protoReq.Collection, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "collection", err) + } + + val, ok = pathParams["token_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "token_id") + } + + protoReq.TokenId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "token_id", err) + } + + msg, err := server.NftInfo(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_Query_Nfts_0 = &utilities.DoubleArray{Encoding: map[string]int{"collection": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}} +) + +func request_Query_Nfts_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryNftsRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["collection"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "collection") + } + + protoReq.Collection, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "collection", 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_Nfts_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.Nfts(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_Nfts_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryNftsRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["collection"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "collection") + } + + protoReq.Collection, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "collection", 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_Nfts_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.Nfts(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_AllNftsByOwner_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryAllNftsByOwnerRequest + 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.AllNftsByOwner(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_AllNftsByOwner_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryAllNftsByOwnerRequest + 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.AllNftsByOwner(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_Collection_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_Collection_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_Collection_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_OwnerOf_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_OwnerOf_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_OwnerOf_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_NumTokens_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_NumTokens_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_NumTokens_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_NftInfo_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_NftInfo_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_NftInfo_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_Nfts_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_Nfts_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_Nfts_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_AllNftsByOwner_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_AllNftsByOwner_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_AllNftsByOwner_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_Collection_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_Collection_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_Collection_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_OwnerOf_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_OwnerOf_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_OwnerOf_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_NumTokens_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_NumTokens_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_NumTokens_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_NftInfo_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_NftInfo_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_NftInfo_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_Nfts_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_Nfts_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_Nfts_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_AllNftsByOwner_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_AllNftsByOwner_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_AllNftsByOwner_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +var ( + pattern_Query_Collection_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 3}, []string{"bitsong", "nft", "v1beta1", "collection"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_OwnerOf_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{"bitsong", "nft", "v1beta1", "owner", "collection", "token_id"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_NumTokens_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"bitsong", "nft", "v1beta1", "num_tokens", "collection"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_NftInfo_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{"bitsong", "nft", "v1beta1", "nft_info", "collection", "token_id"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_Nfts_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"bitsong", "nft", "v1beta1", "nfts", "collection"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_AllNftsByOwner_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"bitsong", "nft", "v1beta1", "nfts_by_owner", "owner"}, "", runtime.AssumeColonVerbOpt(false))) +) + +var ( + forward_Query_Collection_0 = runtime.ForwardResponseMessage + + forward_Query_OwnerOf_0 = runtime.ForwardResponseMessage + + forward_Query_NumTokens_0 = runtime.ForwardResponseMessage + + forward_Query_NftInfo_0 = runtime.ForwardResponseMessage + + forward_Query_Nfts_0 = runtime.ForwardResponseMessage + + forward_Query_AllNftsByOwner_0 = runtime.ForwardResponseMessage +) From ed1caf04cd5c44cd674d5b669a054440fe4bf155 Mon Sep 17 00:00:00 2001 From: angelorc Date: Thu, 4 Sep 2025 13:09:44 +0200 Subject: [PATCH 05/15] feat(nft): enhance NFT minting and querying functionality - Added validation to ensure that the token ID is not empty when minting an NFT. - Implemented a check to disable minting for collections without a specified minter. - Introduced pagination support in the QueryAllNftsByOwnerRequest and QueryAllNftsByOwnerResponse messages. - Updated the keeper to handle the new pagination parameters and return paginated results. - Enhanced test cases to cover the new NFT minting and querying features, including scenarios for multiple owners and pagination. --- app/keepers/keepers.go | 1 + proto/bitsong/nft/v1beta1/query.proto | 4 + x/nft/keeper/collection.go | 59 ++++++++ x/nft/keeper/collection_test.go | 12 ++ x/nft/keeper/grpc_query.go | 37 ++++- x/nft/keeper/grpc_query_test.go | 23 +++ x/nft/keeper/keeper.go | 12 +- x/nft/keeper/keeper_test.go | 1 + x/nft/keeper/nft.go | 9 ++ x/nft/types/query.pb.go | 206 +++++++++++++++++++------- x/nft/types/query.pb.gw.go | 18 +++ 11 files changed, 322 insertions(+), 60 deletions(-) diff --git a/app/keepers/keepers.go b/app/keepers/keepers.go index c1297cbc..feb5c59d 100644 --- a/app/keepers/keepers.go +++ b/app/keepers/keepers.go @@ -411,6 +411,7 @@ func NewAppKeepers( appKeepers.NftKeeper = nftkeeper.NewKeeper( appCodec, + keys[nfttypes.StoreKey], runtime.NewKVStoreService(appKeepers.keys[nfttypes.StoreKey]), appKeepers.AccountKeeper, bApp.Logger(), diff --git a/proto/bitsong/nft/v1beta1/query.proto b/proto/bitsong/nft/v1beta1/query.proto index 74dc61c8..7a70f228 100644 --- a/proto/bitsong/nft/v1beta1/query.proto +++ b/proto/bitsong/nft/v1beta1/query.proto @@ -124,6 +124,8 @@ message QueryAllNftsByOwnerRequest { option (gogoproto.goproto_getters) = false; string owner = 1; + + cosmos.base.query.v1beta1.PageRequest pagination = 2; } message QueryAllNftsByOwnerResponse { @@ -133,4 +135,6 @@ message QueryAllNftsByOwnerResponse { repeated bitsong.nft.v1beta1.Nft nfts = 1 [ (gogoproto.nullable) = false ]; + + cosmos.base.query.v1beta1.PageResponse pagination = 2; } \ No newline at end of file diff --git a/x/nft/keeper/collection.go b/x/nft/keeper/collection.go index c9e99f26..a5885b40 100644 --- a/x/nft/keeper/collection.go +++ b/x/nft/keeper/collection.go @@ -1,14 +1,20 @@ package keeper import ( + "bytes" "fmt" + errorsmod "cosmossdk.io/errors" "cosmossdk.io/math" "github.com/bitsongofficial/go-bitsong/x/nft/types" tmcrypto "github.com/cometbft/cometbft/crypto" sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/pkg/errors" ) +const MaxDenomLength = 43 + func (k Keeper) CreateCollection(ctx sdk.Context, creator sdk.AccAddress, coll types.Collection) (denom string, err error) { denom, err = k.validateCollectionDenom(ctx, creator, coll.Symbol) if err != nil { @@ -87,3 +93,56 @@ func (k Keeper) HasCollection(ctx sdk.Context, denom string) bool { has, err := k.Collections.Has(ctx, denom) return has && err == nil } + +func LengthDenomPrefix(bz []byte) ([]byte, error) { + bzLen := len(bz) + if bzLen == 0 { + return bz, nil + } + + if bzLen > MaxDenomLength { + return nil, errorsmod.Wrapf(sdkerrors.ErrInvalidType, "denom length should be max %d bytes, got %d", MaxDenomLength, bzLen) + } + + return append([]byte{byte(bzLen)}, bz...), nil +} + +func MustLengthDenomPrefix(bz []byte) []byte { + res, err := LengthDenomPrefix(bz) + if err != nil { + panic(err) + } + + return res +} + +func SplitNftLengthPrefixedKey(key []byte) (denom, tokenId []byte, err error) { + parts := bytes.SplitN(key, []byte{0}, 2) + if len(parts) != 2 { + return nil, nil, fmt.Errorf("invalid composite key format: expected 2 parts, got %d", len(parts)) + } + + denomLen := len(parts[0]) + + if denomLen > MaxDenomLength { + return nil, nil, errors.Wrapf(sdkerrors.ErrInvalidType, "decoded denom key length %d exceeds max allowed length %d", denomLen, MaxDenomLength) + } + + if len(key)-1 < denomLen { + return nil, nil, fmt.Errorf("key is malformed: length prefix %d is greater than tokenId bytes %d", denomLen, len(key)-1) + } + + denom = parts[0] + tokenId = parts[1] + + return denom, tokenId, nil +} + +func MustSplitNftLengthPrefixedKey(key []byte) (denom, tokenId []byte) { + denom, tokenId, err := SplitNftLengthPrefixedKey(key) + if err != nil { + panic(err) + } + + return denom, tokenId +} diff --git a/x/nft/keeper/collection_test.go b/x/nft/keeper/collection_test.go index 24fe1975..bd8d9a12 100644 --- a/x/nft/keeper/collection_test.go +++ b/x/nft/keeper/collection_test.go @@ -5,6 +5,7 @@ import ( "github.com/cometbft/cometbft/crypto/tmhash" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" ) func TestKeeper_createCollectionDenom(t *testing.T) { @@ -19,3 +20,14 @@ func TestKeeper_createCollectionDenom(t *testing.T) { t.Errorf("expected %s, got %s", expectedDenom, denom) } } + +func TestSplitNftLengthPrefixedKey(t *testing.T) { + denom := "nft653AF6715F0C4EE2E24A54B191EBD0AD5DB33723" + tokenId := "1" + + keyBz := append(append([]byte(denom), 0), []byte(tokenId)...) + + denomBz, tokenIdBz := MustSplitNftLengthPrefixedKey(keyBz) + require.Equal(t, denom, string(denomBz)) + require.Equal(t, tokenId, string(tokenIdBz)) +} diff --git a/x/nft/keeper/grpc_query.go b/x/nft/keeper/grpc_query.go index c0051389..3a4a1a22 100644 --- a/x/nft/keeper/grpc_query.go +++ b/x/nft/keeper/grpc_query.go @@ -5,9 +5,10 @@ import ( "errors" "cosmossdk.io/collections" - "cosmossdk.io/collections/indexes" + "cosmossdk.io/store/prefix" "github.com/bitsongofficial/go-bitsong/x/nft/types" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/address" "github.com/cosmos/cosmos-sdk/types/query" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" @@ -142,20 +143,44 @@ func (k Keeper) AllNftsByOwner(ctx context.Context, req *types.QueryAllNftsByOwn return nil, status.Errorf(codes.InvalidArgument, "invalid owner address: %s", err.Error()) } - // TODO: Add pagination support for this query + sdkCtx := sdk.UnwrapSDKContext(ctx) + + store := prefix.NewStore( + sdkCtx.KVStore(k.storeKey), + append(types.OwnersPrefix, address.MustLengthPrefix(owner)...), + ) + + var nfts []types.Nft + + pageRes, err := query.Paginate(store, req.Pagination, func(key []byte, value []byte) error { + denom, tokenId := MustSplitNftLengthPrefixedKey(key) + + nft, err := k.NFTs.Get(ctx, collections.Join(string(denom), string(tokenId))) + if err != nil { + return err + } + + nfts = append(nfts, nft) + + return nil + }) - iter, err := k.NFTs.Indexes.Owner.MatchExact(ctx, owner) if err != nil { return nil, status.Error(codes.Internal, err.Error()) } - defer iter.Close() - nfts, err := indexes.CollectValues(ctx, k.NFTs, iter) + /*iter, err := k.NFTs.Indexes.Owner.MatchExact(ctx, owner) if err != nil { return nil, status.Error(codes.Internal, err.Error()) } + defer iter.Close() + nfts, err := indexes.CollectValues(ctx, k.NFTs, iter) + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + }*/ return &types.QueryAllNftsByOwnerResponse{ - Nfts: nfts, + Nfts: nfts, + Pagination: pageRes, }, nil } diff --git a/x/nft/keeper/grpc_query_test.go b/x/nft/keeper/grpc_query_test.go index e643f27d..df458d42 100644 --- a/x/nft/keeper/grpc_query_test.go +++ b/x/nft/keeper/grpc_query_test.go @@ -219,6 +219,13 @@ func (suite *KeeperTestSuite) TestQueryNftsByOwner() { Uri: "ipfs://my-second-nft-metadata.json", } + nft3 := types.Nft{ + TokenId: "3", + Name: "My Third NFT", + Description: "This is my third NFT", + Uri: "ipfs://my-third-nft-metadata.json", + } + err = suite.keeper.MintNFT(suite.ctx, collectionDenom, creator, owner, nft1) suite.NoError(err) @@ -232,4 +239,20 @@ func (suite *KeeperTestSuite) TestQueryNftsByOwner() { suite.Len(res.Nfts, 2) suite.Equal(nft1.TokenId, res.Nfts[0].TokenId) suite.Equal(nft2.TokenId, res.Nfts[1].TokenId) + + res, err = suite.keeper.AllNftsByOwner(suite.ctx, &types.QueryAllNftsByOwnerRequest{ + Owner: owner2.String(), + }) + suite.NoError(err) + suite.Len(res.Nfts, 0) + + err = suite.keeper.MintNFT(suite.ctx, collectionDenom, creator, owner2, nft3) + suite.NoError(err) + + res, err = suite.keeper.AllNftsByOwner(suite.ctx, &types.QueryAllNftsByOwnerRequest{ + Owner: owner2.String(), + }) + suite.NoError(err) + suite.Len(res.Nfts, 1) + suite.Equal(nft3.TokenId, res.Nfts[0].TokenId) } diff --git a/x/nft/keeper/keeper.go b/x/nft/keeper/keeper.go index 0dcb263e..6d446ff3 100644 --- a/x/nft/keeper/keeper.go +++ b/x/nft/keeper/keeper.go @@ -7,6 +7,7 @@ import ( "cosmossdk.io/core/store" "cosmossdk.io/log" "cosmossdk.io/math" + storetypes "cosmossdk.io/store/types" "github.com/bitsongofficial/go-bitsong/x/nft/types" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" @@ -51,6 +52,7 @@ func (i NFTIndexes) IndexesList() []collections.Index[collections.Pair[string, s type Keeper struct { cdc codec.BinaryCodec + storeKey storetypes.StoreKey storeService store.KVStoreService ac address.Codec // bk types.BankKeeper @@ -59,13 +61,14 @@ type Keeper struct { Schema collections.Schema Collections collections.Map[string, types.Collection] Supply collections.Map[string, math.Int] - NFTs *collections.IndexedMap[collections.Pair[string, string], types.Nft, NFTIndexes] + // (collectionDenom, tokenId) -> NFT + NFTs *collections.IndexedMap[collections.Pair[string, string], types.Nft, NFTIndexes] } -func NewKeeper(cdc codec.BinaryCodec, storeService store.KVStoreService, ak types.AccountKeeper, logger log.Logger) Keeper { - if addr := ak.GetModuleAddress(types.ModuleName); addr == nil { +func NewKeeper(cdc codec.BinaryCodec, key storetypes.StoreKey, storeService store.KVStoreService, ak types.AccountKeeper, logger log.Logger) Keeper { + /*if addr := ak.GetModuleAddress(types.ModuleName); addr == nil { panic("the " + types.ModuleName + " module account has not been set") - } + }*/ logger = logger.With(log.ModuleKey, "x/"+types.ModuleName) @@ -74,6 +77,7 @@ func NewKeeper(cdc codec.BinaryCodec, storeService store.KVStoreService, ak type k := Keeper{ cdc: cdc, + storeKey: key, storeService: storeService, ac: ac, logger: logger, diff --git a/x/nft/keeper/keeper_test.go b/x/nft/keeper/keeper_test.go index 27e57fb6..cf63bcb4 100644 --- a/x/nft/keeper/keeper_test.go +++ b/x/nft/keeper/keeper_test.go @@ -18,6 +18,7 @@ import ( var ( creator = sdk.AccAddress(tmhash.SumTruncated([]byte("creator"))) owner = sdk.AccAddress(tmhash.SumTruncated([]byte("owner"))) + owner2 = sdk.AccAddress(tmhash.SumTruncated([]byte("owner2"))) initAmt = math.NewIntFromUint64(1000000000) initCoin = sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, initAmt)} ) diff --git a/x/nft/keeper/nft.go b/x/nft/keeper/nft.go index f07971f8..fd6f55bf 100644 --- a/x/nft/keeper/nft.go +++ b/x/nft/keeper/nft.go @@ -2,6 +2,7 @@ package keeper import ( "fmt" + "strings" "cosmossdk.io/collections" "github.com/bitsongofficial/go-bitsong/x/nft/types" @@ -9,6 +10,10 @@ import ( ) func (k Keeper) MintNFT(ctx sdk.Context, collectionDenom string, minter sdk.AccAddress, owner sdk.AccAddress, metadata types.Nft) error { + if strings.TrimSpace(metadata.TokenId) == "" { + return fmt.Errorf("token ID cannot be empty") + } + nftKey := collections.Join(collectionDenom, metadata.TokenId) has, err := k.NFTs.Has(ctx, nftKey) if err != nil { @@ -23,6 +28,10 @@ func (k Keeper) MintNFT(ctx sdk.Context, collectionDenom string, minter sdk.AccA return types.ErrCollectionNotFound } + if coll.Minter == "" { + return fmt.Errorf("minting disabled for this collection") + } + collectionMinter, err := sdk.AccAddressFromBech32(coll.Minter) if err != nil { return fmt.Errorf("invalid minter address: %w", err) diff --git a/x/nft/types/query.pb.go b/x/nft/types/query.pb.go index 0be2937f..4c2e90cc 100644 --- a/x/nft/types/query.pb.go +++ b/x/nft/types/query.pb.go @@ -405,7 +405,8 @@ func (m *QueryNftsResponse) XXX_DiscardUnknown() { var xxx_messageInfo_QueryNftsResponse proto.InternalMessageInfo type QueryAllNftsByOwnerRequest struct { - Owner string `protobuf:"bytes,1,opt,name=owner,proto3" json:"owner,omitempty"` + Owner string `protobuf:"bytes,1,opt,name=owner,proto3" json:"owner,omitempty"` + Pagination *query.PageRequest `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` } func (m *QueryAllNftsByOwnerRequest) Reset() { *m = QueryAllNftsByOwnerRequest{} } @@ -442,7 +443,8 @@ func (m *QueryAllNftsByOwnerRequest) XXX_DiscardUnknown() { var xxx_messageInfo_QueryAllNftsByOwnerRequest proto.InternalMessageInfo type QueryAllNftsByOwnerResponse struct { - Nfts []Nft `protobuf:"bytes,1,rep,name=nfts,proto3" json:"nfts"` + Nfts []Nft `protobuf:"bytes,1,rep,name=nfts,proto3" json:"nfts"` + Pagination *query.PageResponse `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` } func (m *QueryAllNftsByOwnerResponse) Reset() { *m = QueryAllNftsByOwnerResponse{} } @@ -496,54 +498,54 @@ func init() { func init() { proto.RegisterFile("bitsong/nft/v1beta1/query.proto", fileDescriptor_c3d20ffbceb85197) } var fileDescriptor_c3d20ffbceb85197 = []byte{ - // 745 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x96, 0x41, 0x4f, 0x13, 0x41, - 0x14, 0xc7, 0x3b, 0x50, 0x2c, 0x3c, 0x12, 0xa3, 0x03, 0x6a, 0x5d, 0x74, 0x4b, 0x9a, 0x88, 0x58, - 0x70, 0x47, 0x8a, 0x8a, 0x1a, 0x13, 0x42, 0x4d, 0x34, 0x44, 0x03, 0xda, 0x78, 0xc2, 0x43, 0xb3, - 0x2d, 0xbb, 0xeb, 0xc6, 0x76, 0xa6, 0xb0, 0x53, 0x94, 0x10, 0x2e, 0xc6, 0x03, 0xf1, 0x64, 0xe2, - 0xcd, 0x8b, 0x84, 0xb3, 0x07, 0x3f, 0x06, 0x47, 0x8c, 0x17, 0x4f, 0xc6, 0x80, 0x89, 0x7e, 0x0c, - 0xb3, 0xb3, 0xd3, 0xee, 0x2e, 0xec, 0x96, 0xd5, 0x78, 0x62, 0x77, 0xe7, 0xbd, 0xff, 0xfb, 0xcd, - 0x7f, 0xde, 0x1b, 0x0a, 0xb9, 0xaa, 0xcd, 0x1d, 0x46, 0x2d, 0x42, 0x4d, 0x4e, 0xd6, 0xa6, 0xaa, - 0x06, 0xd7, 0xa7, 0xc8, 0x4a, 0xcb, 0x58, 0x5d, 0xd7, 0x9a, 0xab, 0x8c, 0x33, 0x3c, 0x24, 0x03, - 0x34, 0x6a, 0x72, 0x4d, 0x06, 0x28, 0xc3, 0x16, 0xb3, 0x98, 0x58, 0x27, 0xee, 0x93, 0x17, 0xaa, - 0x5c, 0xb0, 0x18, 0xb3, 0xea, 0x06, 0xd1, 0x9b, 0x36, 0xd1, 0x29, 0x65, 0x5c, 0xe7, 0x36, 0xa3, - 0x8e, 0x5c, 0x1d, 0xa9, 0x31, 0xa7, 0xc1, 0x1c, 0x4f, 0x9c, 0xac, 0x85, 0xaa, 0x28, 0x05, 0xb9, - 0x58, 0xd5, 0x1d, 0xa3, 0x13, 0xe1, 0xc1, 0x34, 0x75, 0xcb, 0xa6, 0x42, 0x49, 0xc6, 0x5e, 0x8c, - 0x42, 0x76, 0xe9, 0xc4, 0x72, 0xbe, 0x04, 0x67, 0x9f, 0xb8, 0x02, 0xf7, 0x58, 0xbd, 0x6e, 0xd4, - 0xdc, 0xbc, 0xb2, 0xb1, 0xd2, 0x32, 0x1c, 0x8e, 0x55, 0x80, 0x5a, 0xe7, 0x63, 0x16, 0x8d, 0xa2, - 0xf1, 0x81, 0x72, 0xe0, 0xcb, 0x9d, 0xfe, 0xad, 0xed, 0x5c, 0xea, 0xf7, 0x76, 0x2e, 0x95, 0x5f, - 0x86, 0x73, 0x47, 0x34, 0x9c, 0x26, 0xa3, 0x8e, 0x81, 0x67, 0x8f, 0x88, 0x0c, 0x16, 0x73, 0x5a, - 0x84, 0x49, 0x5a, 0x20, 0x39, 0xba, 0xca, 0x12, 0x0c, 0x89, 0x2a, 0x8b, 0x2f, 0xa9, 0xb1, 0xba, - 0x68, 0x26, 0xc4, 0xc4, 0xe7, 0xa1, 0x9f, 0xb3, 0x17, 0x06, 0xad, 0xd8, 0xcb, 0xd9, 0x1e, 0xb1, - 0x9a, 0x11, 0xef, 0xf3, 0xcb, 0x01, 0xed, 0x9b, 0x30, 0x1c, 0xd6, 0x96, 0xf8, 0xc3, 0xd0, 0xc7, - 0xdc, 0x4f, 0x52, 0xd7, 0x7b, 0x09, 0xe4, 0xcd, 0xc1, 0x19, 0x91, 0xb7, 0xd0, 0x6a, 0x3c, 0x75, - 0x45, 0x9d, 0xbf, 0x37, 0xef, 0x96, 0x3c, 0x80, 0x80, 0x84, 0x5f, 0xbc, 0xc6, 0x5a, 0x94, 0x8b, - 0xf4, 0x74, 0xd9, 0x7b, 0x89, 0x30, 0x64, 0xc1, 0xe4, 0xf3, 0xd4, 0x64, 0xff, 0xd5, 0x90, 0x47, - 0xd2, 0x90, 0x8e, 0xb6, 0x64, 0x2a, 0x40, 0x2f, 0x35, 0xb9, 0x3c, 0xc8, 0x6c, 0xe4, 0x41, 0x2e, - 0x98, 0xbc, 0xec, 0x06, 0x05, 0xd4, 0xde, 0x20, 0x38, 0xd5, 0x96, 0x4b, 0x6a, 0x11, 0xbe, 0x0f, - 0xe0, 0x37, 0xb3, 0x20, 0x1d, 0x2c, 0x8e, 0x69, 0x5e, 0xe7, 0x6b, 0x6e, 0xe7, 0x6b, 0xde, 0x48, - 0xb4, 0xeb, 0x3e, 0xd6, 0x2d, 0x43, 0x6a, 0x97, 0x03, 0x99, 0x01, 0x8c, 0x0f, 0x08, 0x4e, 0x07, - 0x30, 0xe4, 0x96, 0x8a, 0x90, 0xa6, 0x26, 0x77, 0xb2, 0x68, 0xb4, 0xb7, 0xdb, 0x9e, 0x4a, 0xe9, - 0xdd, 0xef, 0xb9, 0x54, 0x59, 0xc4, 0xe2, 0x07, 0x11, 0x6c, 0x97, 0x8f, 0x65, 0xf3, 0x0a, 0xc6, - 0xc0, 0xdd, 0x05, 0x45, 0xb0, 0xcd, 0xd5, 0xeb, 0x2e, 0x5e, 0xc9, 0xeb, 0xc5, 0xb6, 0x59, 0xc7, - 0x35, 0xe2, 0x33, 0x18, 0x89, 0xcc, 0xfe, 0xf7, 0x3d, 0xfa, 0xe2, 0xc5, 0x2f, 0x19, 0xe8, 0x13, - 0xea, 0x78, 0x07, 0x01, 0xf8, 0x83, 0x8a, 0x27, 0x22, 0x85, 0xa2, 0xef, 0x13, 0x65, 0x32, 0x59, - 0xb0, 0x47, 0x9c, 0xbf, 0xbd, 0xf5, 0xeb, 0x73, 0x01, 0xbd, 0xfe, 0xfa, 0xf3, 0x7d, 0x8f, 0x86, - 0x27, 0x49, 0xd4, 0x25, 0xe6, 0xf7, 0x0a, 0xd9, 0xf0, 0x9f, 0x37, 0xf1, 0x47, 0x04, 0x19, 0x39, - 0xc8, 0x78, 0x3c, 0xbe, 0x68, 0xf8, 0x1e, 0x51, 0xae, 0x24, 0x88, 0x94, 0x6c, 0xb3, 0x3e, 0xdb, - 0x75, 0x5c, 0x8c, 0x64, 0x13, 0xe7, 0x13, 0xc2, 0x22, 0x1b, 0xed, 0xa9, 0x13, 0x84, 0x03, 0x9d, - 0x79, 0xc7, 0x85, 0xf8, 0xca, 0x87, 0xef, 0x15, 0x65, 0x22, 0x51, 0x6c, 0x72, 0x0f, 0x69, 0xab, - 0x51, 0x11, 0x5c, 0x4e, 0xd8, 0xc3, 0x1d, 0x04, 0x19, 0x39, 0xfb, 0xdd, 0x3c, 0x0c, 0x5f, 0x3d, - 0xdd, 0x3c, 0x3c, 0x74, 0x91, 0xe4, 0x4b, 0x3e, 0xdb, 0x0c, 0xbe, 0x41, 0x62, 0xfe, 0x49, 0x55, - 0x6c, 0x6a, 0xb2, 0x58, 0x1b, 0xdf, 0x22, 0x48, 0xbb, 0xdd, 0x8e, 0x2f, 0x75, 0xad, 0xdb, 0x31, - 0x6f, 0xec, 0xb8, 0x30, 0xc9, 0x36, 0xed, 0xb3, 0x8d, 0xe3, 0xb1, 0x38, 0xb6, 0x43, 0x8e, 0x7d, - 0x42, 0x70, 0x32, 0x3c, 0x7d, 0x98, 0xc4, 0xd7, 0x8b, 0x9c, 0x72, 0xe5, 0x5a, 0xf2, 0x04, 0x89, - 0x3a, 0xe3, 0xa3, 0x4e, 0xe2, 0x42, 0x2c, 0x6a, 0xa5, 0xba, 0x5e, 0x91, 0x2d, 0x29, 0xfe, 0x6c, - 0x96, 0x1e, 0xee, 0xee, 0xab, 0x68, 0x6f, 0x5f, 0x45, 0x3f, 0xf6, 0x55, 0xf4, 0xee, 0x40, 0x4d, - 0xed, 0x1d, 0xa8, 0xa9, 0x6f, 0x07, 0x6a, 0x6a, 0x69, 0xca, 0xb2, 0xf9, 0xf3, 0x56, 0x55, 0xab, - 0xb1, 0x46, 0x5b, 0x8f, 0x99, 0xa6, 0x5d, 0xb3, 0xf5, 0x3a, 0xb1, 0xd8, 0xd5, 0x76, 0x89, 0x57, - 0xa2, 0x08, 0x5f, 0x6f, 0x1a, 0x4e, 0xf5, 0x84, 0xf8, 0x2d, 0x31, 0xfd, 0x27, 0x00, 0x00, 0xff, - 0xff, 0xa0, 0x04, 0x7a, 0x98, 0x1f, 0x09, 0x00, 0x00, + // 751 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x96, 0xc1, 0x4f, 0x13, 0x4f, + 0x14, 0xc7, 0x3b, 0x50, 0x7e, 0x85, 0x47, 0xf2, 0x8b, 0x0e, 0xa8, 0x75, 0xd1, 0x2d, 0x69, 0x22, + 0x62, 0xc1, 0x5d, 0x29, 0x2a, 0xea, 0x85, 0x50, 0x13, 0x0d, 0xd1, 0x80, 0x36, 0x9e, 0xb8, 0x34, + 0xdb, 0xb2, 0xbb, 0x6e, 0x6c, 0x67, 0x0a, 0x3b, 0x45, 0x09, 0xe1, 0x62, 0x38, 0x10, 0x4f, 0x26, + 0xde, 0xbc, 0x48, 0x88, 0x47, 0x0f, 0xfe, 0x19, 0x1c, 0x31, 0x5e, 0x3c, 0x19, 0x03, 0x26, 0xfa, + 0x67, 0x98, 0x9d, 0x9d, 0x76, 0x77, 0x61, 0xb6, 0xac, 0x89, 0x26, 0x9e, 0xd8, 0x9d, 0x79, 0xef, + 0xfb, 0x3e, 0xfb, 0x9d, 0x37, 0x8f, 0x42, 0xae, 0xea, 0x30, 0x97, 0x12, 0x5b, 0x27, 0x16, 0xd3, + 0xd7, 0xa6, 0xaa, 0x26, 0x33, 0xa6, 0xf4, 0x95, 0x96, 0xb9, 0xba, 0xae, 0x35, 0x57, 0x29, 0xa3, + 0x78, 0x48, 0x04, 0x68, 0xc4, 0x62, 0x9a, 0x08, 0x50, 0x86, 0x6d, 0x6a, 0x53, 0xbe, 0xaf, 0x7b, + 0x4f, 0x7e, 0xa8, 0x72, 0xc1, 0xa6, 0xd4, 0xae, 0x9b, 0xba, 0xd1, 0x74, 0x74, 0x83, 0x10, 0xca, + 0x0c, 0xe6, 0x50, 0xe2, 0x8a, 0xdd, 0x91, 0x1a, 0x75, 0x1b, 0xd4, 0xf5, 0xc5, 0xf5, 0xb5, 0x48, + 0x15, 0xa5, 0x20, 0x36, 0xab, 0x86, 0x6b, 0x76, 0x22, 0x7c, 0x98, 0xa6, 0x61, 0x3b, 0x84, 0x2b, + 0x89, 0xd8, 0x8b, 0x32, 0x64, 0x8f, 0x8e, 0x6f, 0xe7, 0x4b, 0x70, 0xf6, 0xb1, 0x27, 0x70, 0x97, + 0xd6, 0xeb, 0x66, 0xcd, 0xcb, 0x2b, 0x9b, 0x2b, 0x2d, 0xd3, 0x65, 0x58, 0x05, 0xa8, 0x75, 0x16, + 0xb3, 0x68, 0x14, 0x8d, 0x0f, 0x94, 0x43, 0x2b, 0x77, 0xfa, 0xb7, 0x77, 0x72, 0xa9, 0x9f, 0x3b, + 0xb9, 0x54, 0x7e, 0x19, 0xce, 0x1d, 0xd3, 0x70, 0x9b, 0x94, 0xb8, 0x26, 0x9e, 0x3d, 0x26, 0x32, + 0x58, 0xcc, 0x69, 0x12, 0x93, 0xb4, 0x50, 0xb2, 0xbc, 0xca, 0x12, 0x0c, 0xf1, 0x2a, 0x8b, 0xcf, + 0x89, 0xb9, 0xba, 0x68, 0x25, 0xc4, 0xc4, 0xe7, 0xa1, 0x9f, 0xd1, 0x67, 0x26, 0xa9, 0x38, 0xcb, + 0xd9, 0x1e, 0xbe, 0x9b, 0xe1, 0xef, 0xf3, 0xcb, 0x21, 0xed, 0x9b, 0x30, 0x1c, 0xd5, 0x16, 0xf8, + 0xc3, 0xd0, 0x47, 0xbd, 0x25, 0xa1, 0xeb, 0xbf, 0x84, 0xf2, 0xe6, 0xe0, 0x0c, 0xcf, 0x5b, 0x68, + 0x35, 0x9e, 0x78, 0xa2, 0xee, 0xef, 0x9b, 0x77, 0x4b, 0x1c, 0x40, 0x48, 0x22, 0x28, 0x5e, 0xa3, + 0x2d, 0xc2, 0x78, 0x7a, 0xba, 0xec, 0xbf, 0x48, 0x0c, 0x59, 0xb0, 0xd8, 0x3c, 0xb1, 0xe8, 0x1f, + 0x35, 0xe4, 0xa1, 0x30, 0xa4, 0xa3, 0x2d, 0x98, 0x0a, 0xd0, 0x4b, 0x2c, 0x26, 0x0e, 0x32, 0x2b, + 0x3d, 0xc8, 0x05, 0x8b, 0x95, 0xbd, 0xa0, 0x90, 0xda, 0x16, 0x82, 0x53, 0x6d, 0xb9, 0xa4, 0x16, + 0xe1, 0x7b, 0x00, 0x41, 0x33, 0x73, 0xd2, 0xc1, 0xe2, 0x98, 0xe6, 0x77, 0xbe, 0xe6, 0x75, 0xbe, + 0xe6, 0x5f, 0x89, 0x76, 0xdd, 0x47, 0x86, 0x6d, 0x0a, 0xed, 0x72, 0x28, 0x33, 0x84, 0xf1, 0x16, + 0xc1, 0xe9, 0x10, 0x86, 0xf8, 0xa4, 0x22, 0xa4, 0x89, 0xc5, 0xdc, 0x2c, 0x1a, 0xed, 0xed, 0xf6, + 0x4d, 0xa5, 0xf4, 0xde, 0xd7, 0x5c, 0xaa, 0xcc, 0x63, 0xf1, 0x7d, 0x09, 0xdb, 0xe5, 0x13, 0xd9, + 0xfc, 0x82, 0x31, 0x70, 0x5b, 0x08, 0x14, 0x0e, 0x37, 0x57, 0xaf, 0x7b, 0x7c, 0x25, 0xbf, 0x19, + 0xdb, 0x6e, 0x49, 0x3b, 0xf1, 0x2f, 0x78, 0xf4, 0x1e, 0xc1, 0x88, 0x14, 0xe3, 0x9f, 0x72, 0xab, + 0xf8, 0x29, 0x03, 0x7d, 0x1c, 0x13, 0xef, 0x22, 0x80, 0x60, 0x76, 0xe0, 0x09, 0x29, 0x91, 0x7c, + 0xc4, 0x29, 0x93, 0xc9, 0x82, 0x7d, 0x92, 0xfc, 0xed, 0xed, 0x1f, 0x1f, 0x0b, 0xe8, 0xe5, 0xe7, + 0xef, 0x6f, 0x7a, 0x34, 0x3c, 0xa9, 0xcb, 0xe6, 0x6a, 0xd0, 0xbe, 0xfa, 0x46, 0xf0, 0xbc, 0x89, + 0xdf, 0x21, 0xc8, 0x88, 0xd9, 0x82, 0xc7, 0xe3, 0x8b, 0x46, 0x47, 0x9b, 0x72, 0x25, 0x41, 0xa4, + 0x60, 0x9b, 0x0d, 0xd8, 0xae, 0xe3, 0xa2, 0x94, 0x8d, 0x77, 0x4c, 0x04, 0x4b, 0xdf, 0x68, 0x0f, + 0x02, 0x4e, 0x38, 0xd0, 0x19, 0x41, 0xb8, 0x10, 0x5f, 0xf9, 0xe8, 0xa8, 0x53, 0x26, 0x12, 0xc5, + 0x26, 0xf7, 0x90, 0xb4, 0x1a, 0x15, 0xce, 0xe5, 0x46, 0x3d, 0xdc, 0x45, 0x90, 0x11, 0xe3, 0xa8, + 0x9b, 0x87, 0xd1, 0x69, 0xd8, 0xcd, 0xc3, 0x23, 0xb3, 0x2d, 0x5f, 0x0a, 0xd8, 0x66, 0xf0, 0x0d, + 0x3d, 0xe6, 0xff, 0x66, 0xc5, 0x21, 0x16, 0x8d, 0xb5, 0xf1, 0x15, 0x82, 0xb4, 0x77, 0x6d, 0xf0, + 0xa5, 0xae, 0x75, 0x3b, 0xe6, 0x8d, 0x9d, 0x14, 0x26, 0xd8, 0xa6, 0x03, 0xb6, 0x71, 0x3c, 0x16, + 0xc7, 0x76, 0xc4, 0xb1, 0x0f, 0x08, 0xfe, 0x8f, 0x5e, 0x63, 0xac, 0xc7, 0xd7, 0x93, 0xce, 0x1d, + 0xe5, 0x5a, 0xf2, 0x04, 0x81, 0x3a, 0x13, 0xa0, 0x4e, 0xe2, 0x42, 0x2c, 0x6a, 0xa5, 0xba, 0x5e, + 0x11, 0x2d, 0xc9, 0xff, 0x6c, 0x96, 0x1e, 0xec, 0x1d, 0xa8, 0x68, 0xff, 0x40, 0x45, 0xdf, 0x0e, + 0x54, 0xf4, 0xfa, 0x50, 0x4d, 0xed, 0x1f, 0xaa, 0xa9, 0x2f, 0x87, 0x6a, 0x6a, 0x69, 0xca, 0x76, + 0xd8, 0xd3, 0x56, 0x55, 0xab, 0xd1, 0x46, 0x5b, 0x8f, 0x5a, 0x96, 0x53, 0x73, 0x8c, 0xba, 0x6e, + 0xd3, 0xab, 0xed, 0x12, 0x2f, 0x78, 0x11, 0xb6, 0xde, 0x34, 0xdd, 0xea, 0x7f, 0xfc, 0xe7, 0xcd, + 0xf4, 0xaf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x70, 0xd1, 0xdf, 0xe7, 0xb2, 0x09, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -1180,6 +1182,18 @@ func (m *QueryAllNftsByOwnerRequest) MarshalToSizedBuffer(dAtA []byte) (int, err _ = i var l int _ = l + if m.Pagination != nil { + { + size, err := m.Pagination.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } if len(m.Owner) > 0 { i -= len(m.Owner) copy(dAtA[i:], m.Owner) @@ -1210,6 +1224,18 @@ func (m *QueryAllNftsByOwnerResponse) MarshalToSizedBuffer(dAtA []byte) (int, er _ = i var l int _ = l + if m.Pagination != nil { + { + size, err := m.Pagination.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } if len(m.Nfts) > 0 { for iNdEx := len(m.Nfts) - 1; iNdEx >= 0; iNdEx-- { { @@ -1395,6 +1421,10 @@ func (m *QueryAllNftsByOwnerRequest) Size() (n int) { if l > 0 { n += 1 + l + sovQuery(uint64(l)) } + if m.Pagination != nil { + l = m.Pagination.Size() + n += 1 + l + sovQuery(uint64(l)) + } return n } @@ -1410,6 +1440,10 @@ func (m *QueryAllNftsByOwnerResponse) Size() (n int) { n += 1 + l + sovQuery(uint64(l)) } } + if m.Pagination != nil { + l = m.Pagination.Size() + n += 1 + l + sovQuery(uint64(l)) + } return n } @@ -2433,6 +2467,42 @@ func (m *QueryAllNftsByOwnerRequest) Unmarshal(dAtA []byte) error { } m.Owner = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pagination", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Pagination == nil { + m.Pagination = &query.PageRequest{} + } + if err := m.Pagination.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipQuery(dAtA[iNdEx:]) @@ -2517,6 +2587,42 @@ func (m *QueryAllNftsByOwnerResponse) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pagination", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Pagination == nil { + m.Pagination = &query.PageResponse{} + } + if err := m.Pagination.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipQuery(dAtA[iNdEx:]) diff --git a/x/nft/types/query.pb.gw.go b/x/nft/types/query.pb.gw.go index d2d559fa..61fa63d7 100644 --- a/x/nft/types/query.pb.gw.go +++ b/x/nft/types/query.pb.gw.go @@ -365,6 +365,10 @@ func local_request_Query_Nfts_0(ctx context.Context, marshaler runtime.Marshaler } +var ( + filter_Query_AllNftsByOwner_0 = &utilities.DoubleArray{Encoding: map[string]int{"owner": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}} +) + func request_Query_AllNftsByOwner_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq QueryAllNftsByOwnerRequest var metadata runtime.ServerMetadata @@ -387,6 +391,13 @@ func request_Query_AllNftsByOwner_0(ctx context.Context, marshaler runtime.Marsh return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "owner", 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_AllNftsByOwner_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + msg, err := client.AllNftsByOwner(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err @@ -414,6 +425,13 @@ func local_request_Query_AllNftsByOwner_0(ctx context.Context, marshaler runtime return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "owner", 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_AllNftsByOwner_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + msg, err := server.AllNftsByOwner(ctx, &protoReq) return msg, metadata, err From d8a9e7fb5e90e77946eb4b420d78bb7b743cb436 Mon Sep 17 00:00:00 2001 From: angelorc Date: Thu, 4 Sep 2025 13:11:26 +0200 Subject: [PATCH 06/15] refactor(nft): rename OwnersPrefix to NFTsByOwnerPrefix --- x/nft/keeper/grpc_query.go | 2 +- x/nft/keeper/keeper.go | 4 ++-- x/nft/types/keys.go | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/x/nft/keeper/grpc_query.go b/x/nft/keeper/grpc_query.go index 3a4a1a22..ee15bc82 100644 --- a/x/nft/keeper/grpc_query.go +++ b/x/nft/keeper/grpc_query.go @@ -147,7 +147,7 @@ func (k Keeper) AllNftsByOwner(ctx context.Context, req *types.QueryAllNftsByOwn store := prefix.NewStore( sdkCtx.KVStore(k.storeKey), - append(types.OwnersPrefix, address.MustLengthPrefix(owner)...), + append(types.NFTsByOwnerPrefix, address.MustLengthPrefix(owner)...), ) var nfts []types.Nft diff --git a/x/nft/keeper/keeper.go b/x/nft/keeper/keeper.go index 6d446ff3..60012f62 100644 --- a/x/nft/keeper/keeper.go +++ b/x/nft/keeper/keeper.go @@ -32,8 +32,8 @@ func newNFTIndexes(sb *collections.SchemaBuilder) NFTIndexes { ), Owner: indexes.NewMulti( sb, - types.OwnersPrefix, - "owners", + types.NFTsByOwnerPrefix, + "nfts_by_owner", sdk.AccAddressKey, collections.PairKeyCodec(collections.StringKey, collections.StringKey), func(pk collections.Pair[string, string], v types.Nft) (sdk.AccAddress, error) { diff --git a/x/nft/types/keys.go b/x/nft/types/keys.go index 35000f0e..1e1db64a 100644 --- a/x/nft/types/keys.go +++ b/x/nft/types/keys.go @@ -13,5 +13,5 @@ var ( SupplyPrefix = collections.NewPrefix(1) NFTsPrefix = collections.NewPrefix(2) NFTsByCollectionPrefix = collections.NewPrefix(3) - OwnersPrefix = collections.NewPrefix(4) + NFTsByOwnerPrefix = collections.NewPrefix(4) ) From 9b543d8957d847983d39d58c268888a88a3b76ad Mon Sep 17 00:00:00 2001 From: angelorc Date: Thu, 4 Sep 2025 14:05:46 +0200 Subject: [PATCH 07/15] feat(nft): refactor NFT keeper methods to use context and add NFT transfer functiona --- x/nft/keeper/collection.go | 86 +++------------- x/nft/keeper/collection_test.go | 12 --- x/nft/keeper/grpc_query.go | 2 +- x/nft/keeper/keeper.go | 11 +-- x/nft/keeper/keeper_test.go | 170 +++++++++++--------------------- x/nft/keeper/nft.go | 73 +++++++++++++- x/nft/types/events.go | 23 +++++ x/nft/types/expected_keeper.go | 3 + x/nft/types/keys.go | 42 +++++++- x/nft/types/keys_test.go | 19 ++++ 10 files changed, 235 insertions(+), 206 deletions(-) create mode 100644 x/nft/types/events.go create mode 100644 x/nft/types/keys_test.go diff --git a/x/nft/keeper/collection.go b/x/nft/keeper/collection.go index a5885b40..ada5b19f 100644 --- a/x/nft/keeper/collection.go +++ b/x/nft/keeper/collection.go @@ -1,21 +1,16 @@ package keeper import ( - "bytes" + "context" "fmt" - errorsmod "cosmossdk.io/errors" "cosmossdk.io/math" "github.com/bitsongofficial/go-bitsong/x/nft/types" tmcrypto "github.com/cometbft/cometbft/crypto" sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/pkg/errors" ) -const MaxDenomLength = 43 - -func (k Keeper) CreateCollection(ctx sdk.Context, creator sdk.AccAddress, coll types.Collection) (denom string, err error) { +func (k Keeper) CreateCollection(ctx context.Context, creator sdk.AccAddress, coll types.Collection) (denom string, err error) { denom, err = k.validateCollectionDenom(ctx, creator, coll.Symbol) if err != nil { return "", err @@ -30,7 +25,7 @@ func (k Keeper) CreateCollection(ctx sdk.Context, creator sdk.AccAddress, coll t return denom, nil } -func (k Keeper) GetSupply(ctx sdk.Context, denom string) math.Int { +func (k Keeper) GetSupply(ctx context.Context, denom string) math.Int { supply, err := k.Supply.Get(ctx, denom) if err != nil { return math.ZeroInt() @@ -39,16 +34,21 @@ func (k Keeper) GetSupply(ctx sdk.Context, denom string) math.Int { return supply } -func (k Keeper) HasSupply(ctx sdk.Context, denom string) bool { +func (k Keeper) HasSupply(ctx context.Context, denom string) bool { has, err := k.Supply.Has(ctx, denom) return has && err == nil } -func (k Keeper) setSupply(ctx sdk.Context, denom string, supply math.Int) error { +func (k Keeper) HasCollection(ctx context.Context, denom string) bool { + has, err := k.Collections.Has(ctx, denom) + return has && err == nil +} + +func (k Keeper) setSupply(ctx context.Context, denom string, supply math.Int) error { return k.Supply.Set(ctx, denom, supply) } -func (k Keeper) incrementSupply(ctx sdk.Context, denom string) error { +func (k Keeper) incrementSupply(ctx context.Context, denom string) error { supply := k.GetSupply(ctx, denom) supply = supply.Add(math.NewInt(1)) @@ -62,7 +62,7 @@ func (k Keeper) createCollectionDenom(creator sdk.AccAddress, symbol string) str return fmt.Sprintf("nft%x", tmcrypto.AddressHash(bz)) } -func (k Keeper) validateCollectionDenom(ctx sdk.Context, creator sdk.AccAddress, symbol string) (string, error) { +func (k Keeper) validateCollectionDenom(ctx context.Context, creator sdk.AccAddress, symbol string) (string, error) { denom := k.createCollectionDenom(creator, symbol) if err := sdk.ValidateDenom(denom); err != nil { @@ -76,11 +76,11 @@ func (k Keeper) validateCollectionDenom(ctx sdk.Context, creator sdk.AccAddress, return denom, nil } -func (k Keeper) setCollection(ctx sdk.Context, denom string, coll types.Collection) error { +func (k Keeper) setCollection(ctx context.Context, denom string, coll types.Collection) error { return k.Collections.Set(ctx, denom, coll) } -func (k Keeper) getCollection(ctx sdk.Context, denom string) (types.Collection, error) { +func (k Keeper) getCollection(ctx context.Context, denom string) (types.Collection, error) { coll, err := k.Collections.Get(ctx, denom) if err != nil { return types.Collection{}, types.ErrCollectionNotFound @@ -88,61 +88,3 @@ func (k Keeper) getCollection(ctx sdk.Context, denom string) (types.Collection, return coll, nil } - -func (k Keeper) HasCollection(ctx sdk.Context, denom string) bool { - has, err := k.Collections.Has(ctx, denom) - return has && err == nil -} - -func LengthDenomPrefix(bz []byte) ([]byte, error) { - bzLen := len(bz) - if bzLen == 0 { - return bz, nil - } - - if bzLen > MaxDenomLength { - return nil, errorsmod.Wrapf(sdkerrors.ErrInvalidType, "denom length should be max %d bytes, got %d", MaxDenomLength, bzLen) - } - - return append([]byte{byte(bzLen)}, bz...), nil -} - -func MustLengthDenomPrefix(bz []byte) []byte { - res, err := LengthDenomPrefix(bz) - if err != nil { - panic(err) - } - - return res -} - -func SplitNftLengthPrefixedKey(key []byte) (denom, tokenId []byte, err error) { - parts := bytes.SplitN(key, []byte{0}, 2) - if len(parts) != 2 { - return nil, nil, fmt.Errorf("invalid composite key format: expected 2 parts, got %d", len(parts)) - } - - denomLen := len(parts[0]) - - if denomLen > MaxDenomLength { - return nil, nil, errors.Wrapf(sdkerrors.ErrInvalidType, "decoded denom key length %d exceeds max allowed length %d", denomLen, MaxDenomLength) - } - - if len(key)-1 < denomLen { - return nil, nil, fmt.Errorf("key is malformed: length prefix %d is greater than tokenId bytes %d", denomLen, len(key)-1) - } - - denom = parts[0] - tokenId = parts[1] - - return denom, tokenId, nil -} - -func MustSplitNftLengthPrefixedKey(key []byte) (denom, tokenId []byte) { - denom, tokenId, err := SplitNftLengthPrefixedKey(key) - if err != nil { - panic(err) - } - - return denom, tokenId -} diff --git a/x/nft/keeper/collection_test.go b/x/nft/keeper/collection_test.go index bd8d9a12..24fe1975 100644 --- a/x/nft/keeper/collection_test.go +++ b/x/nft/keeper/collection_test.go @@ -5,7 +5,6 @@ import ( "github.com/cometbft/cometbft/crypto/tmhash" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/stretchr/testify/require" ) func TestKeeper_createCollectionDenom(t *testing.T) { @@ -20,14 +19,3 @@ func TestKeeper_createCollectionDenom(t *testing.T) { t.Errorf("expected %s, got %s", expectedDenom, denom) } } - -func TestSplitNftLengthPrefixedKey(t *testing.T) { - denom := "nft653AF6715F0C4EE2E24A54B191EBD0AD5DB33723" - tokenId := "1" - - keyBz := append(append([]byte(denom), 0), []byte(tokenId)...) - - denomBz, tokenIdBz := MustSplitNftLengthPrefixedKey(keyBz) - require.Equal(t, denom, string(denomBz)) - require.Equal(t, tokenId, string(tokenIdBz)) -} diff --git a/x/nft/keeper/grpc_query.go b/x/nft/keeper/grpc_query.go index ee15bc82..1cb735ff 100644 --- a/x/nft/keeper/grpc_query.go +++ b/x/nft/keeper/grpc_query.go @@ -153,7 +153,7 @@ func (k Keeper) AllNftsByOwner(ctx context.Context, req *types.QueryAllNftsByOwn var nfts []types.Nft pageRes, err := query.Paginate(store, req.Pagination, func(key []byte, value []byte) error { - denom, tokenId := MustSplitNftLengthPrefixedKey(key) + denom, tokenId := types.MustSplitNftLengthPrefixedKey(key) nft, err := k.NFTs.Get(ctx, collections.Join(string(denom), string(tokenId))) if err != nil { diff --git a/x/nft/keeper/keeper.go b/x/nft/keeper/keeper.go index 60012f62..f3b7ddbd 100644 --- a/x/nft/keeper/keeper.go +++ b/x/nft/keeper/keeper.go @@ -3,7 +3,6 @@ package keeper import ( "cosmossdk.io/collections" "cosmossdk.io/collections/indexes" - "cosmossdk.io/core/address" "cosmossdk.io/core/store" "cosmossdk.io/log" "cosmossdk.io/math" @@ -54,9 +53,8 @@ type Keeper struct { cdc codec.BinaryCodec storeKey storetypes.StoreKey storeService store.KVStoreService - ac address.Codec - // bk types.BankKeeper - logger log.Logger + ak types.AccountKeeper + logger log.Logger Schema collections.Schema Collections collections.Map[string, types.Collection] @@ -70,16 +68,17 @@ func NewKeeper(cdc codec.BinaryCodec, key storetypes.StoreKey, storeService stor panic("the " + types.ModuleName + " module account has not been set") }*/ + // TODO: validate all metadata length + logger = logger.With(log.ModuleKey, "x/"+types.ModuleName) sb := collections.NewSchemaBuilder(storeService) - ac := ak.AddressCodec() k := Keeper{ cdc: cdc, storeKey: key, storeService: storeService, - ac: ac, + ak: ak, logger: logger, // TODO: fix the store once we add queries Collections: collections.NewMap(sb, types.CollectionsPrefix, "collections", collections.StringKey, codec.CollValue[types.Collection](cdc)), diff --git a/x/nft/keeper/keeper_test.go b/x/nft/keeper/keeper_test.go index cf63bcb4..188fc04a 100644 --- a/x/nft/keeper/keeper_test.go +++ b/x/nft/keeper/keeper_test.go @@ -3,31 +3,30 @@ package keeper_test import ( "testing" + "cosmossdk.io/collections" "cosmossdk.io/math" simapp "github.com/bitsongofficial/go-bitsong/app" apptesting "github.com/bitsongofficial/go-bitsong/app/testing" - fantokentypes "github.com/bitsongofficial/go-bitsong/x/fantoken/types" "github.com/bitsongofficial/go-bitsong/x/nft/keeper" "github.com/bitsongofficial/go-bitsong/x/nft/types" "github.com/cometbft/cometbft/crypto/tmhash" sdk "github.com/cosmos/cosmos-sdk/types" - bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" "github.com/stretchr/testify/suite" ) var ( - creator = sdk.AccAddress(tmhash.SumTruncated([]byte("creator"))) - owner = sdk.AccAddress(tmhash.SumTruncated([]byte("owner"))) - owner2 = sdk.AccAddress(tmhash.SumTruncated([]byte("owner2"))) - initAmt = math.NewIntFromUint64(1000000000) - initCoin = sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, initAmt)} + creator = sdk.AccAddress(tmhash.SumTruncated([]byte("creator"))) + owner = sdk.AccAddress(tmhash.SumTruncated([]byte("owner"))) + owner2 = sdk.AccAddress(tmhash.SumTruncated([]byte("owner2"))) + // initAmt = math.NewIntFromUint64(1000000000) + // initCoin = sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, initAmt)} ) type KeeperTestSuite struct { apptesting.KeeperTestHelper - ctx sdk.Context - bk bankkeeper.Keeper + ctx sdk.Context + // bk bankkeeper.Keeper keeper keeper.Keeper app *simapp.BitsongApp } @@ -37,15 +36,15 @@ func (suite *KeeperTestSuite) SetupTest() { app := suite.App suite.keeper = app.NftKeeper - suite.bk = app.BankKeeper + // suite.bk = app.BankKeeper suite.App = app suite.ctx = suite.Ctx // init tokens to addr - err := suite.bk.MintCoins(suite.ctx, fantokentypes.ModuleName, initCoin) - suite.NoError(err) - err = suite.bk.SendCoinsFromModuleToAccount(suite.ctx, fantokentypes.ModuleName, creator, initCoin) + /*err := suite.bk.MintCoins(suite.ctx, types.ModuleName, initCoin) suite.NoError(err) + err = suite.bk.SendCoinsFromModuleToAccount(suite.ctx, types.ModuleName, creator, initCoin) + suite.NoError(err)*/ } func TestKeeperSuite(t *testing.T) { @@ -113,111 +112,60 @@ func (suite *KeeperTestSuite) TestMintNFT() { suite.Equal(math.NewInt(2), supply) } -/* -type MintNFTTestCase struct { - name string // A descriptive name for the test case - collection types.Collection - nftToMint types.Nft - minter sdk.AccAddress - owner sdk.AccAddress - expectErr bool // Do we expect an error during minting? - expectedSupply int64 // Expected supply of the collection after this mint - expectedBalanceForNewNFT int64 // Expected balance of the *specific* new NFT - expectedTotalOwnerBalances int // Expected total number of different assets the owner has -} - -func (suite *KeeperTestSuite) TestMintNFT_Advanced() { - collection := types.Collection{ +func (suite *KeeperTestSuite) TestSendNFT() { + testCollection := types.Collection{ Name: "My NFT Collection", Symbol: "MYNFT", Description: "My NFT Collection Description", Uri: "ipfs://my-nft-collection-metadata.json", Minter: creator.String(), } + expectedDenom := "nft653AF6715F0C4EE2E24A54B191EBD0AD5DB33723" - collectionDenom, err := suite.keeper.CreateCollection(suite.ctx, creator, collection) - suite.Require().NoError(err, "initial collection creation should succeed") - fmt.Println("collectionDenom:", collectionDenom) - - // Define the test cases - testCases := []MintNFTTestCase{ - { - name: "Successful first mint", - collection: collection, - nftToMint: types.Nft{ - Name: "My First NFT", - Description: "This is my first NFT", - Uri: "ipfs://my-first-nft-metadata.json", - }, - minter: creator, - owner: owner, - expectErr: false, - expectedSupply: 1, - expectedBalanceForNewNFT: 1, - expectedTotalOwnerBalances: 1, // Owner should now have 1 asset. - }, - { - name: "Successful second mint", - collection: collection, - nftToMint: types.Nft{ - Name: "My Second NFT", - Description: "This is my second NFT", - Uri: "ipfs://my-second-nft-metadata.json", - }, - minter: creator, - owner: owner, - expectErr: false, - expectedSupply: 2, // Total supply of the collection is now 2. - expectedBalanceForNewNFT: 1, - expectedTotalOwnerBalances: 2, // Owner now has two distinct NFTs. - }, - { - name: "Unauthorized minter should fail", - collection: collection, - nftToMint: types.Nft{ - Name: "Unauthorized NFT", - Description: "This NFT should not be minted", - Uri: "ipfs://unauthorized-nft-metadata.json", - }, - minter: owner, // 'owner' is not the authorized minter - owner: owner, - expectErr: true, - expectedSupply: 2, // Supply should NOT increase. - expectedBalanceForNewNFT: 0, // Not applicable, but setting to 0 for clarity. - expectedTotalOwnerBalances: 2, // Owner's total assets should remain unchanged. - }, - } + collectionDenom, err := suite.keeper.CreateCollection(suite.ctx, creator, testCollection) + suite.NoError(err) + suite.Equal(expectedDenom, collectionDenom) - for _, tc := range testCases { - suite.Run(tc.name, func() { - nftDenom, err := suite.keeper.MintNFT(suite.ctx, collectionDenom, tc.minter, tc.owner, tc.nftToMint) - - if tc.expectErr { - suite.Require().Error(err, "should have returned an error") - } else { - suite.Require().NoError(err, "should not have returned an error") - fmt.Println("nftDenom for '"+tc.name+"':", nftDenom) - - // Check the balance of the newly minted NFT for the owner - resp, err := suite.bk.Balance(suite.ctx, &types2.QueryBalanceRequest{ - Address: tc.owner.String(), - Denom: nftDenom, - }) - suite.NoError(err) - suite.Equal(tc.expectedBalanceForNewNFT, resp.Balance.Amount.Int64(), "owner's balance for the new NFT should match expected") - } - - // Check the supply of the collection - supply := suite.keeper.GetSupply(suite.ctx, collectionDenom) - suite.Equal(math.NewInt(tc.expectedSupply), supply, "collection supply should match expected") - - // Check the owner's total number of different assets - balances, err := suite.bk.AllBalances(suite.ctx, &types2.QueryAllBalancesRequest{ - Address: tc.owner.String(), - }) - suite.NoError(err, "querying all balances should not produce an error") - suite.Equal(tc.expectedTotalOwnerBalances, len(balances.Balances), "owner's total number of assets should match expected") - }) + nft1 := types.Nft{ + TokenId: "1", + Name: "My First NFT", + Description: "This is my first NFT", + Uri: "ipfs://my-first-nft-metadata.json", } + + err = suite.keeper.MintNFT(suite.ctx, collectionDenom, creator, owner, nft1) + suite.NoError(err) + + res, err := suite.keeper.AllNftsByOwner(suite.ctx, &types.QueryAllNftsByOwnerRequest{ + Owner: owner.String(), + }) + suite.NoError(err) + suite.Len(res.Nfts, 1) + suite.Equal(nft1.TokenId, res.Nfts[0].TokenId) + + res, err = suite.keeper.AllNftsByOwner(suite.ctx, &types.QueryAllNftsByOwnerRequest{ + Owner: owner2.String(), + }) + suite.NoError(err) + suite.Len(res.Nfts, 0) + + err = suite.keeper.SendNft(suite.ctx, owner, owner2, collectionDenom, "1") + suite.NoError(err) + + res, err = suite.keeper.AllNftsByOwner(suite.ctx, &types.QueryAllNftsByOwnerRequest{ + Owner: owner2.String(), + }) + suite.NoError(err) + suite.Len(res.Nfts, 1) + suite.Equal(nft1.TokenId, res.Nfts[0].TokenId) + + res, err = suite.keeper.AllNftsByOwner(suite.ctx, &types.QueryAllNftsByOwnerRequest{ + Owner: owner.String(), + }) + suite.NoError(err) + suite.Len(res.Nfts, 0) + + nft, err := suite.keeper.NFTs.Get(suite.ctx, collections.Join(collectionDenom, "1")) + suite.NoError(err) + suite.Equal(owner2.String(), nft.Owner) } -*/ diff --git a/x/nft/keeper/nft.go b/x/nft/keeper/nft.go index fd6f55bf..e9433f26 100644 --- a/x/nft/keeper/nft.go +++ b/x/nft/keeper/nft.go @@ -1,15 +1,17 @@ package keeper import ( + "context" "fmt" "strings" "cosmossdk.io/collections" "github.com/bitsongofficial/go-bitsong/x/nft/types" + "github.com/cosmos/cosmos-sdk/telemetry" sdk "github.com/cosmos/cosmos-sdk/types" ) -func (k Keeper) MintNFT(ctx sdk.Context, collectionDenom string, minter sdk.AccAddress, owner sdk.AccAddress, metadata types.Nft) error { +func (k Keeper) MintNFT(ctx context.Context, collectionDenom string, minter sdk.AccAddress, owner sdk.AccAddress, metadata types.Nft) error { if strings.TrimSpace(metadata.TokenId) == "" { return fmt.Errorf("token ID cannot be empty") } @@ -50,15 +52,80 @@ func (k Keeper) MintNFT(ctx sdk.Context, collectionDenom string, minter sdk.AccA return fmt.Errorf("failed to set NFT: %w", err) } + // TODO: add events + return k.incrementSupply(ctx, collectionDenom) } -func (k Keeper) createNftDenom(ctx sdk.Context, collectionDenom string) string { +func (k Keeper) SendNft(ctx context.Context, fromAddr, toAddr sdk.AccAddress, collectionDenom, tokenId string) error { + err := k.changeNftOwner(ctx, fromAddr, toAddr, collectionDenom, tokenId) + if err != nil { + return err + } + + // Same as https://github.com/cosmos/cosmos-sdk/blob/v0.53.4/x/bank/keeper/send.go + // Create account if recipient does not exist. + // + // NOTE: This should ultimately be removed in favor a more flexible approach + // such as delegated fee messages. + accExists := k.ak.HasAccount(ctx, toAddr) + if !accExists { + defer telemetry.IncrCounter(1, "new", "account") + k.ak.SetAccount(ctx, k.ak.NewAccountWithAddress(ctx, toAddr)) + } + + // Same as https://github.com/cosmos/cosmos-sdk/blob/v0.53.4/x/bank/keeper/send.go + // bech32 encoding is expensive! Only do it once for fromAddr + fromAddrString := fromAddr.String() + sdkCtx := sdk.UnwrapSDKContext(ctx) + sdkCtx.EventManager().EmitEvents(sdk.Events{ + sdk.NewEvent( + types.EventTypeTransferNft, + sdk.NewAttribute(types.AttributeKeyReceiver, toAddr.String()), + sdk.NewAttribute(types.AttributeKeySender, fromAddrString), + sdk.NewAttribute(types.AttributeKeyCollection, collectionDenom), + sdk.NewAttribute(types.AttributeKeyTokenId, tokenId), + ), + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(types.AttributeKeySender, fromAddrString), + ), + }) + + return nil +} + +func (k Keeper) createNftDenom(ctx context.Context, collectionDenom string) string { supply := k.GetSupply(ctx, collectionDenom) return fmt.Sprintf("%s-%d", collectionDenom, supply.Uint64()+1) } -func (k Keeper) setNft(ctx sdk.Context, collectionDenom string, tokenId string, nft types.Nft) error { +func (k Keeper) setNft(ctx context.Context, collectionDenom string, tokenId string, nft types.Nft) error { pk := collections.Join(collectionDenom, tokenId) return k.NFTs.Set(ctx, pk, nft) } + +func (k Keeper) changeNftOwner(ctx context.Context, oldOwner, newOwner sdk.AccAddress, collectionDenom string, tokenId string) error { + nft, err := k.NFTs.Get(ctx, collections.Join(collectionDenom, tokenId)) + if err != nil { + return fmt.Errorf("failed to get NFT: %w", err) + } + + if nft.Owner != oldOwner.String() { + return fmt.Errorf("only the owner can transfer the NFT") + } + + nft.Owner = newOwner.String() + err = k.setNft(ctx, collectionDenom, tokenId, nft) + if err != nil { + return fmt.Errorf("failed to set NFT Owner: %w", err) + } + + // emit nft received event + sdkCtx := sdk.UnwrapSDKContext(ctx) + sdkCtx.EventManager().EmitEvent( + types.NewNftReceivedEvent(newOwner, collectionDenom, tokenId), + ) + + return nil +} diff --git a/x/nft/types/events.go b/x/nft/types/events.go new file mode 100644 index 00000000..f8c85f16 --- /dev/null +++ b/x/nft/types/events.go @@ -0,0 +1,23 @@ +package types + +import sdk "github.com/cosmos/cosmos-sdk/types" + +const ( + EventTypeTransferNft = "transfer_nft" + EventTypeNftReceived = "nft_received" + + AttributeKeySender = "sender" + AttributeKeyReceiver = "receiver" + + AttributeKeyCollection = "collection" + AttributeKeyTokenId = "token_id" +) + +func NewNftReceivedEvent(receiver sdk.AccAddress, collection string, tokenId string) sdk.Event { + return sdk.NewEvent( + EventTypeNftReceived, + sdk.NewAttribute(AttributeKeyReceiver, receiver.String()), + sdk.NewAttribute(AttributeKeyCollection, collection), + sdk.NewAttribute(AttributeKeyTokenId, tokenId), + ) +} diff --git a/x/nft/types/expected_keeper.go b/x/nft/types/expected_keeper.go index 65059897..b816d2c6 100644 --- a/x/nft/types/expected_keeper.go +++ b/x/nft/types/expected_keeper.go @@ -11,4 +11,7 @@ type AccountKeeper interface { GetModuleAddress(moduleName string) sdk.AccAddress GetAccount(ctx context.Context, addr sdk.AccAddress) sdk.AccountI AddressCodec() address.Codec + HasAccount(ctx context.Context, addr sdk.AccAddress) bool + SetAccount(ctx context.Context, acc sdk.AccountI) + NewAccountWithAddress(ctx context.Context, addr sdk.AccAddress) sdk.AccountI } diff --git a/x/nft/types/keys.go b/x/nft/types/keys.go index 1e1db64a..f3924d9d 100644 --- a/x/nft/types/keys.go +++ b/x/nft/types/keys.go @@ -1,11 +1,20 @@ package types -import "cosmossdk.io/collections" +import ( + "bytes" + "fmt" + + "cosmossdk.io/collections" + "cosmossdk.io/errors" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) const ( ModuleName = "nft" StoreKey = ModuleName RouterKey = ModuleName + + MaxDenomLength = 43 ) var ( @@ -15,3 +24,34 @@ var ( NFTsByCollectionPrefix = collections.NewPrefix(3) NFTsByOwnerPrefix = collections.NewPrefix(4) ) + +func SplitNftLengthPrefixedKey(key []byte) (denom, tokenId []byte, err error) { + parts := bytes.SplitN(key, []byte{0}, 2) + if len(parts) != 2 { + return nil, nil, fmt.Errorf("invalid composite key format: expected 2 parts, got %d", len(parts)) + } + + denomLen := len(parts[0]) + + if denomLen > MaxDenomLength { + return nil, nil, errors.Wrapf(sdkerrors.ErrInvalidType, "decoded denom key length %d exceeds max allowed length %d", denomLen, MaxDenomLength) + } + + if len(key)-1 < denomLen { + return nil, nil, fmt.Errorf("key is malformed: length prefix %d is greater than tokenId bytes %d", denomLen, len(key)-1) + } + + denom = parts[0] + tokenId = parts[1] + + return denom, tokenId, nil +} + +func MustSplitNftLengthPrefixedKey(key []byte) (denom, tokenId []byte) { + denom, tokenId, err := SplitNftLengthPrefixedKey(key) + if err != nil { + panic(err) + } + + return denom, tokenId +} diff --git a/x/nft/types/keys_test.go b/x/nft/types/keys_test.go new file mode 100644 index 00000000..c73217c6 --- /dev/null +++ b/x/nft/types/keys_test.go @@ -0,0 +1,19 @@ +package types_test + +import ( + "testing" + + "github.com/bitsongofficial/go-bitsong/x/nft/types" + "github.com/stretchr/testify/require" +) + +func TestSplitNftLengthPrefixedKey(t *testing.T) { + denom := "nft653AF6715F0C4EE2E24A54B191EBD0AD5DB33723" + tokenId := "1" + + keyBz := append(append([]byte(denom), 0), []byte(tokenId)...) + + denomBz, tokenIdBz := types.MustSplitNftLengthPrefixedKey(keyBz) + require.Equal(t, denom, string(denomBz)) + require.Equal(t, tokenId, string(tokenIdBz)) +} From 466b9471e55fa34fcf10505e7784d754391d0688 Mon Sep 17 00:00:00 2001 From: angelorc Date: Thu, 4 Sep 2025 14:29:12 +0200 Subject: [PATCH 08/15] fix(nft): update query paths to use plural 'collections' --- proto/bitsong/nft/v1beta1/query.proto | 12 ++-- x/nft/types/query.pb.go | 97 ++++++++++++++------------- x/nft/types/query.pb.gw.go | 10 +-- 3 files changed, 61 insertions(+), 58 deletions(-) diff --git a/proto/bitsong/nft/v1beta1/query.proto b/proto/bitsong/nft/v1beta1/query.proto index 7a70f228..1b286c8c 100644 --- a/proto/bitsong/nft/v1beta1/query.proto +++ b/proto/bitsong/nft/v1beta1/query.proto @@ -12,27 +12,29 @@ option go_package = "github.com/bitsongofficial/go-bitsong/x/nft/types"; service Query { rpc Collection(QueryCollectionRequest) returns (QueryCollectionResponse) { option (cosmos.query.v1.module_query_safe) = true; - option (google.api.http).get = "/bitsong/nft/v1beta1/collection/{collection}"; + option (google.api.http).get = "/bitsong/nft/v1beta1/collections/{collection}"; } + // TODO: rpc AllCollections(QueryAllCollectionsRequest) returns (QueryAllCollectionsResponse)..... + rpc OwnerOf(QueryOwnerOfRequest) returns (QueryOwnerOfResponse) { option (cosmos.query.v1.module_query_safe) = true; - option (google.api.http).get = "/bitsong/nft/v1beta1/owner/{collection}/{token_id}"; + option (google.api.http).get = "/bitsong/nft/v1beta1/collections/{collection}/{token_id}/owner"; } rpc NumTokens(QueryNumTokensRequest) returns (QueryNumTokensResponse) { option (cosmos.query.v1.module_query_safe) = true; - option (google.api.http).get = "/bitsong/nft/v1beta1/num_tokens/{collection}"; + option (google.api.http).get = "/bitsong/nft/v1beta1/collections/{collection}/num_tokens"; } rpc NftInfo(QueryNftInfoRequest) returns (QueryNftInfoResponse) { option (cosmos.query.v1.module_query_safe) = true; - option (google.api.http).get = "/bitsong/nft/v1beta1/nft_info/{collection}/{token_id}"; + option (google.api.http).get = "/bitsong/nft/v1beta1/collections/{collection}/{token_id}"; } rpc Nfts(QueryNftsRequest) returns (QueryNftsResponse) { option (cosmos.query.v1.module_query_safe) = true; - option (google.api.http).get = "/bitsong/nft/v1beta1/nfts/{collection}"; + option (google.api.http).get = "/bitsong/nft/v1beta1/collections/{collection}/nfts"; } rpc AllNftsByOwner(QueryAllNftsByOwnerRequest) returns (QueryAllNftsByOwnerResponse) { diff --git a/x/nft/types/query.pb.go b/x/nft/types/query.pb.go index 4c2e90cc..56354654 100644 --- a/x/nft/types/query.pb.go +++ b/x/nft/types/query.pb.go @@ -498,54 +498,55 @@ func init() { func init() { proto.RegisterFile("bitsong/nft/v1beta1/query.proto", fileDescriptor_c3d20ffbceb85197) } var fileDescriptor_c3d20ffbceb85197 = []byte{ - // 751 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x96, 0xc1, 0x4f, 0x13, 0x4f, - 0x14, 0xc7, 0x3b, 0x50, 0x7e, 0x85, 0x47, 0xf2, 0x8b, 0x0e, 0xa8, 0x75, 0xd1, 0x2d, 0x69, 0x22, - 0x62, 0xc1, 0x5d, 0x29, 0x2a, 0xea, 0x85, 0x50, 0x13, 0x0d, 0xd1, 0x80, 0x36, 0x9e, 0xb8, 0x34, - 0xdb, 0xb2, 0xbb, 0x6e, 0x6c, 0x67, 0x0a, 0x3b, 0x45, 0x09, 0xe1, 0x62, 0x38, 0x10, 0x4f, 0x26, - 0xde, 0xbc, 0x48, 0x88, 0x47, 0x0f, 0xfe, 0x19, 0x1c, 0x31, 0x5e, 0x3c, 0x19, 0x03, 0x26, 0xfa, - 0x67, 0x98, 0x9d, 0x9d, 0x76, 0x77, 0x61, 0xb6, 0xac, 0x89, 0x26, 0x9e, 0xd8, 0x9d, 0x79, 0xef, - 0xfb, 0x3e, 0xfb, 0x9d, 0x37, 0x8f, 0x42, 0xae, 0xea, 0x30, 0x97, 0x12, 0x5b, 0x27, 0x16, 0xd3, - 0xd7, 0xa6, 0xaa, 0x26, 0x33, 0xa6, 0xf4, 0x95, 0x96, 0xb9, 0xba, 0xae, 0x35, 0x57, 0x29, 0xa3, - 0x78, 0x48, 0x04, 0x68, 0xc4, 0x62, 0x9a, 0x08, 0x50, 0x86, 0x6d, 0x6a, 0x53, 0xbe, 0xaf, 0x7b, - 0x4f, 0x7e, 0xa8, 0x72, 0xc1, 0xa6, 0xd4, 0xae, 0x9b, 0xba, 0xd1, 0x74, 0x74, 0x83, 0x10, 0xca, - 0x0c, 0xe6, 0x50, 0xe2, 0x8a, 0xdd, 0x91, 0x1a, 0x75, 0x1b, 0xd4, 0xf5, 0xc5, 0xf5, 0xb5, 0x48, - 0x15, 0xa5, 0x20, 0x36, 0xab, 0x86, 0x6b, 0x76, 0x22, 0x7c, 0x98, 0xa6, 0x61, 0x3b, 0x84, 0x2b, - 0x89, 0xd8, 0x8b, 0x32, 0x64, 0x8f, 0x8e, 0x6f, 0xe7, 0x4b, 0x70, 0xf6, 0xb1, 0x27, 0x70, 0x97, - 0xd6, 0xeb, 0x66, 0xcd, 0xcb, 0x2b, 0x9b, 0x2b, 0x2d, 0xd3, 0x65, 0x58, 0x05, 0xa8, 0x75, 0x16, - 0xb3, 0x68, 0x14, 0x8d, 0x0f, 0x94, 0x43, 0x2b, 0x77, 0xfa, 0xb7, 0x77, 0x72, 0xa9, 0x9f, 0x3b, - 0xb9, 0x54, 0x7e, 0x19, 0xce, 0x1d, 0xd3, 0x70, 0x9b, 0x94, 0xb8, 0x26, 0x9e, 0x3d, 0x26, 0x32, - 0x58, 0xcc, 0x69, 0x12, 0x93, 0xb4, 0x50, 0xb2, 0xbc, 0xca, 0x12, 0x0c, 0xf1, 0x2a, 0x8b, 0xcf, - 0x89, 0xb9, 0xba, 0x68, 0x25, 0xc4, 0xc4, 0xe7, 0xa1, 0x9f, 0xd1, 0x67, 0x26, 0xa9, 0x38, 0xcb, - 0xd9, 0x1e, 0xbe, 0x9b, 0xe1, 0xef, 0xf3, 0xcb, 0x21, 0xed, 0x9b, 0x30, 0x1c, 0xd5, 0x16, 0xf8, - 0xc3, 0xd0, 0x47, 0xbd, 0x25, 0xa1, 0xeb, 0xbf, 0x84, 0xf2, 0xe6, 0xe0, 0x0c, 0xcf, 0x5b, 0x68, - 0x35, 0x9e, 0x78, 0xa2, 0xee, 0xef, 0x9b, 0x77, 0x4b, 0x1c, 0x40, 0x48, 0x22, 0x28, 0x5e, 0xa3, - 0x2d, 0xc2, 0x78, 0x7a, 0xba, 0xec, 0xbf, 0x48, 0x0c, 0x59, 0xb0, 0xd8, 0x3c, 0xb1, 0xe8, 0x1f, - 0x35, 0xe4, 0xa1, 0x30, 0xa4, 0xa3, 0x2d, 0x98, 0x0a, 0xd0, 0x4b, 0x2c, 0x26, 0x0e, 0x32, 0x2b, - 0x3d, 0xc8, 0x05, 0x8b, 0x95, 0xbd, 0xa0, 0x90, 0xda, 0x16, 0x82, 0x53, 0x6d, 0xb9, 0xa4, 0x16, - 0xe1, 0x7b, 0x00, 0x41, 0x33, 0x73, 0xd2, 0xc1, 0xe2, 0x98, 0xe6, 0x77, 0xbe, 0xe6, 0x75, 0xbe, - 0xe6, 0x5f, 0x89, 0x76, 0xdd, 0x47, 0x86, 0x6d, 0x0a, 0xed, 0x72, 0x28, 0x33, 0x84, 0xf1, 0x16, - 0xc1, 0xe9, 0x10, 0x86, 0xf8, 0xa4, 0x22, 0xa4, 0x89, 0xc5, 0xdc, 0x2c, 0x1a, 0xed, 0xed, 0xf6, - 0x4d, 0xa5, 0xf4, 0xde, 0xd7, 0x5c, 0xaa, 0xcc, 0x63, 0xf1, 0x7d, 0x09, 0xdb, 0xe5, 0x13, 0xd9, - 0xfc, 0x82, 0x31, 0x70, 0x5b, 0x08, 0x14, 0x0e, 0x37, 0x57, 0xaf, 0x7b, 0x7c, 0x25, 0xbf, 0x19, - 0xdb, 0x6e, 0x49, 0x3b, 0xf1, 0x2f, 0x78, 0xf4, 0x1e, 0xc1, 0x88, 0x14, 0xe3, 0x9f, 0x72, 0xab, - 0xf8, 0x29, 0x03, 0x7d, 0x1c, 0x13, 0xef, 0x22, 0x80, 0x60, 0x76, 0xe0, 0x09, 0x29, 0x91, 0x7c, - 0xc4, 0x29, 0x93, 0xc9, 0x82, 0x7d, 0x92, 0xfc, 0xed, 0xed, 0x1f, 0x1f, 0x0b, 0xe8, 0xe5, 0xe7, - 0xef, 0x6f, 0x7a, 0x34, 0x3c, 0xa9, 0xcb, 0xe6, 0x6a, 0xd0, 0xbe, 0xfa, 0x46, 0xf0, 0xbc, 0x89, - 0xdf, 0x21, 0xc8, 0x88, 0xd9, 0x82, 0xc7, 0xe3, 0x8b, 0x46, 0x47, 0x9b, 0x72, 0x25, 0x41, 0xa4, - 0x60, 0x9b, 0x0d, 0xd8, 0xae, 0xe3, 0xa2, 0x94, 0x8d, 0x77, 0x4c, 0x04, 0x4b, 0xdf, 0x68, 0x0f, - 0x02, 0x4e, 0x38, 0xd0, 0x19, 0x41, 0xb8, 0x10, 0x5f, 0xf9, 0xe8, 0xa8, 0x53, 0x26, 0x12, 0xc5, - 0x26, 0xf7, 0x90, 0xb4, 0x1a, 0x15, 0xce, 0xe5, 0x46, 0x3d, 0xdc, 0x45, 0x90, 0x11, 0xe3, 0xa8, - 0x9b, 0x87, 0xd1, 0x69, 0xd8, 0xcd, 0xc3, 0x23, 0xb3, 0x2d, 0x5f, 0x0a, 0xd8, 0x66, 0xf0, 0x0d, - 0x3d, 0xe6, 0xff, 0x66, 0xc5, 0x21, 0x16, 0x8d, 0xb5, 0xf1, 0x15, 0x82, 0xb4, 0x77, 0x6d, 0xf0, - 0xa5, 0xae, 0x75, 0x3b, 0xe6, 0x8d, 0x9d, 0x14, 0x26, 0xd8, 0xa6, 0x03, 0xb6, 0x71, 0x3c, 0x16, - 0xc7, 0x76, 0xc4, 0xb1, 0x0f, 0x08, 0xfe, 0x8f, 0x5e, 0x63, 0xac, 0xc7, 0xd7, 0x93, 0xce, 0x1d, - 0xe5, 0x5a, 0xf2, 0x04, 0x81, 0x3a, 0x13, 0xa0, 0x4e, 0xe2, 0x42, 0x2c, 0x6a, 0xa5, 0xba, 0x5e, - 0x11, 0x2d, 0xc9, 0xff, 0x6c, 0x96, 0x1e, 0xec, 0x1d, 0xa8, 0x68, 0xff, 0x40, 0x45, 0xdf, 0x0e, - 0x54, 0xf4, 0xfa, 0x50, 0x4d, 0xed, 0x1f, 0xaa, 0xa9, 0x2f, 0x87, 0x6a, 0x6a, 0x69, 0xca, 0x76, - 0xd8, 0xd3, 0x56, 0x55, 0xab, 0xd1, 0x46, 0x5b, 0x8f, 0x5a, 0x96, 0x53, 0x73, 0x8c, 0xba, 0x6e, - 0xd3, 0xab, 0xed, 0x12, 0x2f, 0x78, 0x11, 0xb6, 0xde, 0x34, 0xdd, 0xea, 0x7f, 0xfc, 0xe7, 0xcd, - 0xf4, 0xaf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x70, 0xd1, 0xdf, 0xe7, 0xb2, 0x09, 0x00, 0x00, + // 755 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x96, 0x41, 0x4f, 0x13, 0x4d, + 0x18, 0xc7, 0x3b, 0x50, 0xde, 0xc2, 0x43, 0xf2, 0xe6, 0x7d, 0x07, 0xde, 0xd7, 0xba, 0xe8, 0x96, + 0x90, 0x88, 0x58, 0x60, 0x47, 0xaa, 0x51, 0xc2, 0x41, 0xa4, 0x46, 0x0d, 0xc1, 0x80, 0x36, 0x9e, + 0xb8, 0x34, 0xdb, 0xb2, 0xbb, 0x6e, 0x6c, 0x67, 0x0a, 0x33, 0x45, 0x09, 0xe1, 0x62, 0x38, 0x70, + 0x34, 0x31, 0xf1, 0xe0, 0x89, 0x44, 0x3d, 0xe1, 0xc1, 0x8f, 0xc1, 0x91, 0xc4, 0x8b, 0x27, 0x63, + 0xc0, 0x44, 0xbf, 0x82, 0x37, 0xb3, 0xb3, 0xd3, 0xee, 0x16, 0xb6, 0xa5, 0x8d, 0x9a, 0x78, 0x6a, + 0x67, 0xe7, 0x79, 0xfe, 0xcf, 0x6f, 0xff, 0xf3, 0xcc, 0x93, 0x85, 0x54, 0xc1, 0x15, 0x9c, 0x51, + 0x87, 0x50, 0x5b, 0x90, 0xf5, 0xa9, 0x82, 0x25, 0xcc, 0x29, 0xb2, 0x5a, 0xb5, 0xd6, 0x36, 0x8c, + 0xca, 0x1a, 0x13, 0x0c, 0x0f, 0xa8, 0x00, 0x83, 0xda, 0xc2, 0x50, 0x01, 0xda, 0xa0, 0xc3, 0x1c, + 0x26, 0xf7, 0x89, 0xf7, 0xcf, 0x0f, 0xd5, 0xce, 0x39, 0x8c, 0x39, 0x25, 0x8b, 0x98, 0x15, 0x97, + 0x98, 0x94, 0x32, 0x61, 0x0a, 0x97, 0x51, 0xae, 0x76, 0x87, 0x8a, 0x8c, 0x97, 0x19, 0xf7, 0xc5, + 0xc9, 0x7a, 0x43, 0x15, 0x2d, 0xad, 0x36, 0x0b, 0x26, 0xb7, 0xea, 0x11, 0x3e, 0x4c, 0xc5, 0x74, + 0x5c, 0x2a, 0x95, 0x54, 0xec, 0xf9, 0x28, 0x64, 0x8f, 0x4e, 0x6e, 0x8f, 0x64, 0xe1, 0xff, 0x07, + 0x9e, 0xc0, 0x2d, 0x56, 0x2a, 0x59, 0x45, 0x2f, 0x2f, 0x67, 0xad, 0x56, 0x2d, 0x2e, 0xb0, 0x0e, + 0x50, 0xac, 0x3f, 0x4c, 0xa2, 0x61, 0x34, 0xd6, 0x97, 0x0b, 0x3d, 0x99, 0xe9, 0xdd, 0xd9, 0x4d, + 0xc5, 0xbe, 0xed, 0xa6, 0x62, 0x23, 0x2b, 0x70, 0xe6, 0x84, 0x06, 0xaf, 0x30, 0xca, 0x2d, 0x3c, + 0x7b, 0x42, 0xa4, 0x3f, 0x93, 0x32, 0x22, 0x4c, 0x32, 0x42, 0xc9, 0xd1, 0x55, 0x96, 0x61, 0x40, + 0x56, 0x59, 0x7a, 0x42, 0xad, 0xb5, 0x25, 0xbb, 0x4d, 0x4c, 0x7c, 0x16, 0x7a, 0x05, 0x7b, 0x6c, + 0xd1, 0xbc, 0xbb, 0x92, 0xec, 0x92, 0xbb, 0x09, 0xb9, 0x9e, 0x5f, 0x09, 0x69, 0x5f, 0x83, 0xc1, + 0x46, 0x6d, 0x85, 0x3f, 0x08, 0x3d, 0xcc, 0x7b, 0xa4, 0x74, 0xfd, 0x45, 0x28, 0x6f, 0x0e, 0xfe, + 0x93, 0x79, 0x8b, 0xd5, 0xf2, 0x43, 0x4f, 0x94, 0x77, 0x6e, 0xde, 0xb4, 0x3a, 0x80, 0x90, 0x44, + 0x50, 0xbc, 0xc8, 0xaa, 0x54, 0xc8, 0xf4, 0x78, 0xce, 0x5f, 0x44, 0x18, 0xb2, 0x68, 0x8b, 0x79, + 0x6a, 0xb3, 0x5f, 0x6a, 0xc8, 0x3d, 0x65, 0x48, 0x5d, 0x5b, 0x31, 0xa5, 0xa1, 0x9b, 0xda, 0x42, + 0x1d, 0x64, 0x32, 0xf2, 0x20, 0x17, 0x6d, 0x91, 0xf3, 0x82, 0x42, 0x6a, 0xdb, 0x08, 0xfe, 0xa9, + 0xc9, 0xb5, 0x6b, 0x11, 0xbe, 0x03, 0x10, 0x34, 0xb3, 0x24, 0xed, 0xcf, 0x8c, 0x1a, 0x7e, 0xe7, + 0x1b, 0x5e, 0xe7, 0x1b, 0xfe, 0x95, 0xa8, 0xd5, 0xbd, 0x6f, 0x3a, 0x96, 0xd2, 0xce, 0x85, 0x32, + 0x43, 0x18, 0xaf, 0x10, 0xfc, 0x1b, 0xc2, 0x50, 0xaf, 0x94, 0x81, 0x38, 0xb5, 0x05, 0x4f, 0xa2, + 0xe1, 0xee, 0x56, 0xef, 0x94, 0x8d, 0xef, 0x7f, 0x4a, 0xc5, 0x72, 0x32, 0x16, 0xdf, 0x8d, 0x60, + 0xbb, 0x78, 0x2a, 0x9b, 0x5f, 0xb0, 0x09, 0xdc, 0x36, 0x02, 0x4d, 0xc2, 0xcd, 0x95, 0x4a, 0x1e, + 0x5f, 0xd6, 0x6f, 0xc6, 0x9a, 0x5b, 0x91, 0x9d, 0xf8, 0x1b, 0x3c, 0x7a, 0x83, 0x60, 0x28, 0x12, + 0xe3, 0x8f, 0x72, 0x2b, 0xf3, 0x3d, 0x01, 0x3d, 0x12, 0x13, 0xbf, 0x46, 0x00, 0xc1, 0xec, 0xc0, + 0xe3, 0x91, 0x44, 0xd1, 0x23, 0x4e, 0x9b, 0x68, 0x2f, 0xd8, 0x27, 0x19, 0x99, 0xd9, 0xf9, 0xfa, + 0x3e, 0x8d, 0x9e, 0x7d, 0xf8, 0xf2, 0xa2, 0x8b, 0xe0, 0x49, 0x12, 0x35, 0x57, 0x83, 0xf6, 0xe5, + 0x64, 0x33, 0x58, 0x6c, 0xe1, 0x3d, 0x04, 0x09, 0x35, 0x5c, 0xf0, 0x58, 0xf3, 0xaa, 0x8d, 0xb3, + 0x4d, 0xbb, 0xd4, 0x46, 0xa4, 0x82, 0x5b, 0x08, 0xe0, 0x6e, 0xe2, 0x1b, 0x1d, 0xc1, 0x91, 0xcd, + 0xda, 0x3c, 0xd8, 0x22, 0x7e, 0x5b, 0xed, 0x21, 0xe8, 0xab, 0xcf, 0x23, 0x9c, 0x6e, 0x4e, 0x71, + 0x7c, 0xee, 0x69, 0xe3, 0x6d, 0xc5, 0x2a, 0xe6, 0xdb, 0x01, 0xf3, 0x0c, 0x9e, 0xee, 0x8c, 0x99, + 0x56, 0xcb, 0x79, 0xe1, 0xf3, 0xbd, 0x45, 0x90, 0x50, 0x73, 0xaa, 0x95, 0xb7, 0x8d, 0x63, 0xb2, + 0x95, 0xb7, 0xc7, 0x86, 0xde, 0x4f, 0x71, 0x06, 0xde, 0xe2, 0x97, 0x08, 0xe2, 0xde, 0x95, 0xc2, + 0x17, 0x5a, 0x96, 0xae, 0x7b, 0x39, 0x7a, 0x5a, 0x98, 0xc2, 0x9b, 0x0d, 0xf0, 0xae, 0xe2, 0x4c, + 0x87, 0x36, 0x7a, 0x3c, 0xef, 0x10, 0xfc, 0xdd, 0x78, 0xdd, 0x31, 0x69, 0x5e, 0x3b, 0x72, 0x3e, + 0x69, 0x97, 0xdb, 0x4f, 0x50, 0xd8, 0xd7, 0x03, 0xec, 0x09, 0x9c, 0x26, 0x4d, 0x3e, 0x53, 0x78, + 0xbe, 0xb0, 0x91, 0x97, 0x5d, 0x49, 0x36, 0xe5, 0xcf, 0x56, 0x76, 0x61, 0xff, 0x50, 0x47, 0x07, + 0x87, 0x3a, 0xfa, 0x7c, 0xa8, 0xa3, 0xe7, 0x47, 0x7a, 0xec, 0xe0, 0x48, 0x8f, 0x7d, 0x3c, 0xd2, + 0x63, 0xcb, 0x53, 0x8e, 0x2b, 0x1e, 0x55, 0x0b, 0x46, 0x91, 0x95, 0x6b, 0x7a, 0xcc, 0xb6, 0xdd, + 0xa2, 0x6b, 0x96, 0x88, 0xc3, 0x26, 0x6b, 0x25, 0x9e, 0xca, 0x22, 0x62, 0xa3, 0x62, 0xf1, 0xc2, + 0x5f, 0xf2, 0x33, 0xe8, 0xca, 0x8f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x42, 0x29, 0x52, 0xbd, 0xda, + 0x09, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. diff --git a/x/nft/types/query.pb.gw.go b/x/nft/types/query.pb.gw.go index 61fa63d7..6b1bea62 100644 --- a/x/nft/types/query.pb.gw.go +++ b/x/nft/types/query.pb.gw.go @@ -746,15 +746,15 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie } var ( - pattern_Query_Collection_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 3}, []string{"bitsong", "nft", "v1beta1", "collection"}, "", runtime.AssumeColonVerbOpt(false))) + pattern_Query_Collection_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"bitsong", "nft", "v1beta1", "collections", "collection"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_OwnerOf_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{"bitsong", "nft", "v1beta1", "owner", "collection", "token_id"}, "", runtime.AssumeColonVerbOpt(false))) + pattern_Query_OwnerOf_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, 2, 6}, []string{"bitsong", "nft", "v1beta1", "collections", "collection", "token_id", "owner"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_NumTokens_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"bitsong", "nft", "v1beta1", "num_tokens", "collection"}, "", runtime.AssumeColonVerbOpt(false))) + pattern_Query_NumTokens_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5}, []string{"bitsong", "nft", "v1beta1", "collections", "collection", "num_tokens"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_NftInfo_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{"bitsong", "nft", "v1beta1", "nft_info", "collection", "token_id"}, "", runtime.AssumeColonVerbOpt(false))) + pattern_Query_NftInfo_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{"bitsong", "nft", "v1beta1", "collections", "collection", "token_id"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_Nfts_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"bitsong", "nft", "v1beta1", "nfts", "collection"}, "", runtime.AssumeColonVerbOpt(false))) + pattern_Query_Nfts_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5}, []string{"bitsong", "nft", "v1beta1", "collections", "collection", "nfts"}, "", runtime.AssumeColonVerbOpt(false))) pattern_Query_AllNftsByOwner_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"bitsong", "nft", "v1beta1", "nfts_by_owner", "owner"}, "", runtime.AssumeColonVerbOpt(false))) ) From c5ed2f84fc504e2adbbd69a888ed46b95ee13df4 Mon Sep 17 00:00:00 2001 From: angelorc Date: Thu, 4 Sep 2025 16:44:55 +0200 Subject: [PATCH 09/15] feat(nft): enhance NFT minting and metadata validation - Refactored MintNFT function to accept individual metadata fields (tokenId, name, description, uri) instead of a single metadata object. - Added validateNftMetadata function to enforce constraints on NFT metadata, including length checks for tokenId, name, description, and uri. - Introduced Editions field in the Nft struct to track the number of editions for each NFT. - Implemented GetNft function to retrieve NFT details by collection and token ID. - Updated setNft function to accept a complete Nft object. - Added incrementEdition function to increase the edition count for a specific NFT. - Updated protobuf definitions to include the new Editions field and added a new Edition type for tracking individual NFT editions. - Adjusted key definitions to accommodate new fields and prefixes. --- proto/bitsong/nft/v1beta1/nft.proto | 33 ++- x/nft/keeper/collection.go | 79 ++++- x/nft/keeper/collection_test.go | 10 +- x/nft/keeper/edition.go | 61 ++++ x/nft/keeper/edition_test.go | 56 ++++ x/nft/keeper/grpc_query_test.go | 320 ++++++++++---------- x/nft/keeper/keeper.go | 28 +- x/nft/keeper/keeper_test.go | 190 +++++++----- x/nft/keeper/nft.go | 90 +++++- x/nft/types/keys.go | 7 + x/nft/types/nft.pb.go | 437 +++++++++++++++++++++++++--- 11 files changed, 1002 insertions(+), 309 deletions(-) create mode 100644 x/nft/keeper/edition.go create mode 100644 x/nft/keeper/edition_test.go diff --git a/proto/bitsong/nft/v1beta1/nft.proto b/proto/bitsong/nft/v1beta1/nft.proto index 85074ec0..16db8948 100644 --- a/proto/bitsong/nft/v1beta1/nft.proto +++ b/proto/bitsong/nft/v1beta1/nft.proto @@ -8,14 +8,16 @@ option go_package = "github.com/bitsongofficial/go-bitsong/x/nft/types"; message Collection { option (gogoproto.goproto_getters) = false; - string symbol = 1; - string name = 2; - string description = 3; - string uri = 4; - - string creator = 5; - string minter = 6; - uint64 num_tokens = 7; + string denom = 1; + + string symbol = 2; + string name = 3; + string description = 4; + string uri = 5; + + string creator = 6; + string minter = 7; + uint64 num_tokens = 8; // bool is_mutable // update_autority (who can update name, description and uri if is_mutable = true) } @@ -31,8 +33,23 @@ message Nft { string uri = 5; string owner = 6; + + // TODO: add max_editions + uint64 editions = 7; // number of printed editions + // seller_fee_bps // payment_address // bool is_mutable // update_autority (who can update name, description and uri if is_mutable = true) +} + +message Edition { + option (gogoproto.goproto_getters) = false; + + string collection = 1; + string token_id = 2; + + uint64 seq = 3; // seq is the edition number + + string owner = 4; } \ No newline at end of file diff --git a/x/nft/keeper/collection.go b/x/nft/keeper/collection.go index ada5b19f..30bf1228 100644 --- a/x/nft/keeper/collection.go +++ b/x/nft/keeper/collection.go @@ -3,6 +3,7 @@ package keeper import ( "context" "fmt" + "strings" "cosmossdk.io/math" "github.com/bitsongofficial/go-bitsong/x/nft/types" @@ -10,15 +11,37 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" ) -func (k Keeper) CreateCollection(ctx context.Context, creator sdk.AccAddress, coll types.Collection) (denom string, err error) { - denom, err = k.validateCollectionDenom(ctx, creator, coll.Symbol) +func (k Keeper) CreateCollection( + ctx context.Context, + creator, + minter sdk.AccAddress, + symbol, + name, + description, + uri string, +) (denom string, err error) { + denom, err = k.validateCollectionDenom(ctx, creator, symbol) if err != nil { return "", err } // TODO: charge fee - if err := k.setCollection(ctx, denom, coll); err != nil { + if err := k.validateCollectionMetadata(name, description, uri); err != nil { + return "", err + } + + coll := types.Collection{ + Denom: denom, + Symbol: symbol, + Name: name, + Description: description, + Uri: uri, + Creator: creator.String(), + Minter: minter.String(), + } + + if err := k.setCollection(ctx, coll); err != nil { return "", err } @@ -44,6 +67,19 @@ func (k Keeper) HasCollection(ctx context.Context, denom string) bool { return has && err == nil } +func (k Keeper) GetMinter(ctx context.Context, denom string) (sdk.AccAddress, error) { + coll, err := k.Collections.Get(ctx, denom) + if err != nil { + return nil, types.ErrCollectionNotFound + } + + if coll.Minter == "" { + return nil, fmt.Errorf("minting disabled for this collection") + } + + return sdk.AccAddressFromBech32(coll.Minter) +} + func (k Keeper) setSupply(ctx context.Context, denom string, supply math.Int) error { return k.Supply.Set(ctx, denom, supply) } @@ -55,15 +91,26 @@ func (k Keeper) incrementSupply(ctx context.Context, denom string) error { return k.setSupply(ctx, denom, supply) } -func (k Keeper) createCollectionDenom(creator sdk.AccAddress, symbol string) string { +func (k Keeper) createCollectionDenom(creator sdk.AccAddress, symbol string) (string, error) { // TODO: if necessary add a salt field + if strings.TrimSpace(symbol) == "" { + return "", fmt.Errorf("symbol cannot be blank") + } + + if len(symbol) > types.MaxSymbolLength { + return "", fmt.Errorf("symbol cannot be longer than %d characters", types.MaxSymbolLength) + } + bz := []byte(fmt.Sprintf("%s/%s", creator.String(), symbol)) - return fmt.Sprintf("nft%x", tmcrypto.AddressHash(bz)) + return fmt.Sprintf("nft%x", tmcrypto.AddressHash(bz)), nil } func (k Keeper) validateCollectionDenom(ctx context.Context, creator sdk.AccAddress, symbol string) (string, error) { - denom := k.createCollectionDenom(creator, symbol) + denom, err := k.createCollectionDenom(creator, symbol) + if err != nil { + return "", err + } if err := sdk.ValidateDenom(denom); err != nil { return "", err @@ -76,8 +123,8 @@ func (k Keeper) validateCollectionDenom(ctx context.Context, creator sdk.AccAddr return denom, nil } -func (k Keeper) setCollection(ctx context.Context, denom string, coll types.Collection) error { - return k.Collections.Set(ctx, denom, coll) +func (k Keeper) setCollection(ctx context.Context, coll types.Collection) error { + return k.Collections.Set(ctx, coll.Denom, coll) } func (k Keeper) getCollection(ctx context.Context, denom string) (types.Collection, error) { @@ -88,3 +135,19 @@ func (k Keeper) getCollection(ctx context.Context, denom string) (types.Collecti return coll, nil } + +func (k Keeper) validateCollectionMetadata(name, description, uri string) error { + if len(name) > types.MaxNameLength { + return fmt.Errorf("name cannot be longer than %d characters", types.MaxNameLength) + } + + if len(description) > types.MaxDescriptionLength { + return fmt.Errorf("description cannot be longer than %d characters", types.MaxDescriptionLength) + } + + if len(uri) > types.MaxURILength { + return fmt.Errorf("uri cannot be longer than %d characters", types.MaxURILength) + } + + return nil +} diff --git a/x/nft/keeper/collection_test.go b/x/nft/keeper/collection_test.go index 24fe1975..0a90dd02 100644 --- a/x/nft/keeper/collection_test.go +++ b/x/nft/keeper/collection_test.go @@ -8,13 +8,17 @@ import ( ) func TestKeeper_createCollectionDenom(t *testing.T) { - creator := sdk.AccAddress(tmhash.SumTruncated([]byte("creator"))) + creator := sdk.AccAddress(tmhash.SumTruncated([]byte("creator1"))) symbol := "MYNFT" - expectedDenom := "nft653AF6715F0C4EE2E24A54B191EBD0AD5DB33723" + expectedDenom := "nft9436DDD23FB751AEA7BC6C767F20F943DD735E06" k := Keeper{} - denom := k.createCollectionDenom(creator, symbol) + denom, err := k.createCollectionDenom(creator, symbol) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if denom != expectedDenom { t.Errorf("expected %s, got %s", expectedDenom, denom) } diff --git a/x/nft/keeper/edition.go b/x/nft/keeper/edition.go new file mode 100644 index 00000000..9e8ac91a --- /dev/null +++ b/x/nft/keeper/edition.go @@ -0,0 +1,61 @@ +package keeper + +import ( + "context" + "fmt" + + "cosmossdk.io/collections" + "github.com/bitsongofficial/go-bitsong/x/nft/types" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +func (k Keeper) PrintEdition( + ctx context.Context, + minter, + owner sdk.AccAddress, + collectionDenom, + tokenId string, +) (uint64, error) { + // TODO: this is temporary, must be improved! + + nft, err := k.GetNft(ctx, collectionDenom, tokenId) + if err != nil { + return 0, err + } + if nft == nil { + return 0, fmt.Errorf("NFT with token ID %s does not exist in collection %s", tokenId, collectionDenom) + } + + collectionMinter, err := k.GetMinter(ctx, collectionDenom) + if err != nil { + return 0, err + } + + if !minter.Equals(collectionMinter) { + return 0, fmt.Errorf("only the collection minter can print editions") + } + + // TODO: Charge fee if necessary + + edition := types.Edition{ + Collection: collectionDenom, + TokenId: tokenId, + Seq: nft.Editions + 1, + Owner: owner.String(), + } + + if err := k.setEdition(ctx, edition); err != nil { + return 0, fmt.Errorf("failed to set edition: %w", err) + } + + if err := k.incrementEdition(ctx, collectionDenom, tokenId); err != nil { + return 0, fmt.Errorf("failed to increment edition: %w", err) + } + + return edition.Seq, nil +} + +func (k Keeper) setEdition(ctx context.Context, edition types.Edition) error { + editionKey := collections.Join3(edition.Collection, edition.TokenId, edition.Seq) + return k.Editions.Set(ctx, editionKey, edition) +} diff --git a/x/nft/keeper/edition_test.go b/x/nft/keeper/edition_test.go new file mode 100644 index 00000000..c722df72 --- /dev/null +++ b/x/nft/keeper/edition_test.go @@ -0,0 +1,56 @@ +package keeper_test + +func (suite *KeeperTestSuite) TestPrintEdition() { + collectionDenom, err := suite.keeper.CreateCollection( + suite.ctx, + creator1, + minter1, + testCollection1.Symbol, + testCollection1.Name, + testCollection1.Description, + testCollection1.Uri, + ) + suite.NoError(err) + + err = suite.keeper.MintNFT( + suite.ctx, + minter1, + owner1, + collectionDenom, + testNft1.TokenId, + testNft1.Name, + testNft1.Description, + testNft1.Uri, + ) + suite.NoError(err) + + edition, err := suite.keeper.PrintEdition( + suite.ctx, + minter1, + owner1, + collectionDenom, + "1", + ) + suite.NoError(err) + suite.Equal(uint64(1), edition) + + edition, err = suite.keeper.PrintEdition( + suite.ctx, + minter1, + owner1, + collectionDenom, + "1", + ) + suite.NoError(err) + suite.Equal(uint64(2), edition) + + edition, err = suite.keeper.PrintEdition( + suite.ctx, + minter1, + owner1, + collectionDenom, + "1", + ) + suite.NoError(err) + suite.Equal(uint64(3), edition) +} diff --git a/x/nft/keeper/grpc_query_test.go b/x/nft/keeper/grpc_query_test.go index df458d42..e9a30e3c 100644 --- a/x/nft/keeper/grpc_query_test.go +++ b/x/nft/keeper/grpc_query_test.go @@ -5,52 +5,50 @@ import ( ) func (suite *KeeperTestSuite) TestQueryCollection() { - testCollection := types.Collection{ - Name: "My NFT Collection", - Symbol: "MYNFT", - Description: "My NFT Collection Description", - Uri: "ipfs://my-nft-collection-metadata.json", - Minter: creator.String(), - } - expectedDenom := "nft653AF6715F0C4EE2E24A54B191EBD0AD5DB33723" - - collectionDenom, err := suite.keeper.CreateCollection(suite.ctx, creator, testCollection) + collectionDenom, err := suite.keeper.CreateCollection( + suite.ctx, + creator1, + minter1, + testCollection1.Symbol, + testCollection1.Name, + testCollection1.Description, + testCollection1.Uri, + ) suite.NoError(err) - suite.Equal(expectedDenom, collectionDenom) res, err := suite.keeper.Collection(suite.ctx, &types.QueryCollectionRequest{ Collection: collectionDenom, }) suite.NoError(err) - suite.Equal(testCollection.Name, res.Collection.Name) - suite.Equal(testCollection.Symbol, res.Collection.Symbol) - suite.Equal(testCollection.Description, res.Collection.Description) - suite.Equal(testCollection.Uri, res.Collection.Uri) - suite.Equal(testCollection.Minter, res.Collection.Minter) + suite.Equal(testCollection1.Name, res.Collection.Name) + suite.Equal(testCollection1.Symbol, res.Collection.Symbol) + suite.Equal(testCollection1.Description, res.Collection.Description) + suite.Equal(testCollection1.Uri, res.Collection.Uri) + suite.Equal(testCollection1.Minter, res.Collection.Minter) } func (suite *KeeperTestSuite) TestQueryOwnerOf() { - testCollection := types.Collection{ - Name: "My NFT Collection", - Symbol: "MYNFT", - Description: "My NFT Collection Description", - Uri: "ipfs://my-nft-collection-metadata.json", - Minter: creator.String(), - } - expectedDenom := "nft653AF6715F0C4EE2E24A54B191EBD0AD5DB33723" - - collectionDenom, err := suite.keeper.CreateCollection(suite.ctx, creator, testCollection) + collectionDenom, err := suite.keeper.CreateCollection( + suite.ctx, + creator1, + minter1, + testCollection1.Symbol, + testCollection1.Name, + testCollection1.Description, + testCollection1.Uri, + ) suite.NoError(err) - suite.Equal(expectedDenom, collectionDenom) - nft1 := types.Nft{ - TokenId: "1", - Name: "My First NFT", - Description: "This is my first NFT", - Uri: "ipfs://my-first-nft-metadata.json", - } - - err = suite.keeper.MintNFT(suite.ctx, collectionDenom, creator, owner, nft1) + err = suite.keeper.MintNFT( + suite.ctx, + minter1, + owner1, + collectionDenom, + testNft1.TokenId, + testNft1.Name, + testNft1.Description, + testNft1.Uri, + ) suite.NoError(err) res, err := suite.keeper.OwnerOf(suite.ctx, &types.QueryOwnerOfRequest{ @@ -58,47 +56,49 @@ func (suite *KeeperTestSuite) TestQueryOwnerOf() { TokenId: "1", }) suite.NoError(err) - suite.Equal(owner.String(), res.Owner) + suite.Equal(owner1.String(), res.Owner) } func (suite *KeeperTestSuite) TestQueryNumTokens() { - testCollection := types.Collection{ - Name: "My NFT Collection", - Symbol: "MYNFT", - Description: "My NFT Collection Description", - Uri: "ipfs://my-nft-collection-metadata.json", - Minter: creator.String(), - } - expectedDenom := "nft653AF6715F0C4EE2E24A54B191EBD0AD5DB33723" - - collectionDenom, err := suite.keeper.CreateCollection(suite.ctx, creator, testCollection) + collectionDenom, err := suite.keeper.CreateCollection( + suite.ctx, + creator1, + minter1, + testCollection1.Symbol, + testCollection1.Name, + testCollection1.Description, + testCollection1.Uri, + ) suite.NoError(err) - suite.Equal(expectedDenom, collectionDenom) supply := suite.keeper.GetSupply(suite.ctx, collectionDenom) suite.Equal(uint64(0), supply.Uint64()) - nft1 := types.Nft{ - TokenId: "1", - Name: "My First NFT", - Description: "This is my first NFT", - Uri: "ipfs://my-first-nft-metadata.json", - } - - nft2 := types.Nft{ - TokenId: "2", - Name: "My Second NFT", - Description: "This is my second NFT", - Uri: "ipfs://my-second-nft-metadata.json", - } - - err = suite.keeper.MintNFT(suite.ctx, collectionDenom, creator, owner, nft1) + err = suite.keeper.MintNFT( + suite.ctx, + minter1, + owner1, + collectionDenom, + testNft1.TokenId, + testNft1.Name, + testNft1.Description, + testNft1.Uri, + ) suite.NoError(err) supply = suite.keeper.GetSupply(suite.ctx, collectionDenom) suite.Equal(uint64(1), supply.Uint64()) - err = suite.keeper.MintNFT(suite.ctx, collectionDenom, creator, owner, nft2) + err = suite.keeper.MintNFT( + suite.ctx, + minter1, + owner1, + collectionDenom, + testNft2.TokenId, + testNft2.Name, + testNft2.Description, + testNft2.Uri, + ) suite.NoError(err) supply = suite.keeper.GetSupply(suite.ctx, collectionDenom) @@ -112,74 +112,76 @@ func (suite *KeeperTestSuite) TestQueryNumTokens() { } func (suite *KeeperTestSuite) TestQueryNftInfo() { - testCollection := types.Collection{ - Name: "My NFT Collection", - Symbol: "MYNFT", - Description: "My NFT Collection Description", - Uri: "ipfs://my-nft-collection-metadata.json", - Minter: creator.String(), - } - expectedDenom := "nft653AF6715F0C4EE2E24A54B191EBD0AD5DB33723" - - collectionDenom, err := suite.keeper.CreateCollection(suite.ctx, creator, testCollection) + collectionDenom, err := suite.keeper.CreateCollection( + suite.ctx, + creator1, + minter1, + testCollection1.Symbol, + testCollection1.Name, + testCollection1.Description, + testCollection1.Uri, + ) suite.NoError(err) - suite.Equal(expectedDenom, collectionDenom) - - nft1 := types.Nft{ - TokenId: "1", - Name: "My First NFT", - Description: "This is my first NFT", - Uri: "ipfs://my-first-nft-metadata.json", - } - err = suite.keeper.MintNFT(suite.ctx, collectionDenom, creator, owner, nft1) + err = suite.keeper.MintNFT( + suite.ctx, + minter1, + owner1, + collectionDenom, + testNft1.TokenId, + testNft1.Name, + testNft1.Description, + testNft1.Uri, + ) suite.NoError(err) res, err := suite.keeper.NftInfo(suite.ctx, &types.QueryNftInfoRequest{ Collection: collectionDenom, - TokenId: "1", + TokenId: testNft1.TokenId, }) suite.NoError(err) - suite.Equal(nft1.TokenId, res.Nft.TokenId) - suite.Equal(nft1.Name, res.Nft.Name) - suite.Equal(nft1.Description, res.Nft.Description) - suite.Equal(nft1.Uri, res.Nft.Uri) + suite.Equal(testNft1.TokenId, res.Nft.TokenId) + suite.Equal(testNft1.Name, res.Nft.Name) + suite.Equal(testNft1.Description, res.Nft.Description) + suite.Equal(testNft1.Uri, res.Nft.Uri) suite.Equal(collectionDenom, res.Nft.Collection) - suite.Equal(owner.String(), res.Nft.Owner) + suite.Equal(owner1.String(), res.Nft.Owner) } func (suite *KeeperTestSuite) TestQueryNftsOfOwner() { - testCollection := types.Collection{ - Name: "My NFT Collection", - Symbol: "MYNFT", - Description: "My NFT Collection Description", - Uri: "ipfs://my-nft-collection-metadata.json", - Minter: creator.String(), - } - expectedDenom := "nft653AF6715F0C4EE2E24A54B191EBD0AD5DB33723" - - collectionDenom, err := suite.keeper.CreateCollection(suite.ctx, creator, testCollection) + collectionDenom, err := suite.keeper.CreateCollection( + suite.ctx, + creator1, + minter1, + testCollection1.Symbol, + testCollection1.Name, + testCollection1.Description, + testCollection1.Uri, + ) suite.NoError(err) - suite.Equal(expectedDenom, collectionDenom) - - nft1 := types.Nft{ - TokenId: "1", - Name: "My First NFT", - Description: "This is my first NFT", - Uri: "ipfs://my-first-nft-metadata.json", - } - - nft2 := types.Nft{ - TokenId: "2", - Name: "My Second NFT", - Description: "This is my second NFT", - Uri: "ipfs://my-second-nft-metadata.json", - } - err = suite.keeper.MintNFT(suite.ctx, collectionDenom, creator, owner, nft1) + err = suite.keeper.MintNFT( + suite.ctx, + minter1, + owner1, + collectionDenom, + testNft1.TokenId, + testNft1.Name, + testNft1.Description, + testNft1.Uri, + ) suite.NoError(err) - err = suite.keeper.MintNFT(suite.ctx, collectionDenom, creator, owner, nft2) + err = suite.keeper.MintNFT( + suite.ctx, + minter1, + owner1, + collectionDenom, + testNft2.TokenId, + testNft2.Name, + testNft2.Description, + testNft2.Uri, + ) suite.NoError(err) res, err := suite.keeper.Nfts(suite.ctx, &types.QueryNftsRequest{ @@ -187,58 +189,55 @@ func (suite *KeeperTestSuite) TestQueryNftsOfOwner() { }) suite.NoError(err) suite.Len(res.Nfts, 2) - suite.Equal(nft1.TokenId, res.Nfts[0].TokenId) - suite.Equal(nft2.TokenId, res.Nfts[1].TokenId) + suite.Equal(testNft1.TokenId, res.Nfts[0].TokenId) + suite.Equal(testNft2.TokenId, res.Nfts[1].TokenId) } func (suite *KeeperTestSuite) TestQueryNftsByOwner() { - testCollection := types.Collection{ - Name: "My NFT Collection", - Symbol: "MYNFT", - Description: "My NFT Collection Description", - Uri: "ipfs://my-nft-collection-metadata.json", - Minter: creator.String(), - } - expectedDenom := "nft653AF6715F0C4EE2E24A54B191EBD0AD5DB33723" - - collectionDenom, err := suite.keeper.CreateCollection(suite.ctx, creator, testCollection) + collectionDenom, err := suite.keeper.CreateCollection( + suite.ctx, + creator1, + minter1, + testCollection1.Symbol, + testCollection1.Name, + testCollection1.Description, + testCollection1.Uri, + ) suite.NoError(err) - suite.Equal(expectedDenom, collectionDenom) - - nft1 := types.Nft{ - TokenId: "1", - Name: "My First NFT", - Description: "This is my first NFT", - Uri: "ipfs://my-first-nft-metadata.json", - } - - nft2 := types.Nft{ - TokenId: "2", - Name: "My Second NFT", - Description: "This is my second NFT", - Uri: "ipfs://my-second-nft-metadata.json", - } - - nft3 := types.Nft{ - TokenId: "3", - Name: "My Third NFT", - Description: "This is my third NFT", - Uri: "ipfs://my-third-nft-metadata.json", - } - err = suite.keeper.MintNFT(suite.ctx, collectionDenom, creator, owner, nft1) + err = suite.keeper.MintNFT( + suite.ctx, + minter1, + owner1, + collectionDenom, + testNft1.TokenId, + testNft1.Name, + testNft1.Description, + testNft1.Uri, + ) suite.NoError(err) - err = suite.keeper.MintNFT(suite.ctx, collectionDenom, creator, owner, nft2) + err = suite.keeper.MintNFT( + suite.ctx, + minter1, + owner1, + collectionDenom, + testNft2.TokenId, + testNft2.Name, + testNft2.Description, + testNft2.Uri, + ) suite.NoError(err) res, err := suite.keeper.AllNftsByOwner(suite.ctx, &types.QueryAllNftsByOwnerRequest{ - Owner: owner.String(), + Owner: owner1.String(), }) suite.NoError(err) suite.Len(res.Nfts, 2) - suite.Equal(nft1.TokenId, res.Nfts[0].TokenId) - suite.Equal(nft2.TokenId, res.Nfts[1].TokenId) + suite.Equal(testNft1.TokenId, res.Nfts[0].TokenId) + suite.Equal(uint64(0), res.Nfts[0].Editions) + suite.Equal(testNft2.TokenId, res.Nfts[1].TokenId) + suite.Equal(uint64(0), res.Nfts[1].Editions) res, err = suite.keeper.AllNftsByOwner(suite.ctx, &types.QueryAllNftsByOwnerRequest{ Owner: owner2.String(), @@ -246,7 +245,16 @@ func (suite *KeeperTestSuite) TestQueryNftsByOwner() { suite.NoError(err) suite.Len(res.Nfts, 0) - err = suite.keeper.MintNFT(suite.ctx, collectionDenom, creator, owner2, nft3) + err = suite.keeper.MintNFT( + suite.ctx, + minter1, + owner2, + collectionDenom, + testNft3.TokenId, + testNft3.Name, + testNft3.Description, + testNft3.Uri, + ) suite.NoError(err) res, err = suite.keeper.AllNftsByOwner(suite.ctx, &types.QueryAllNftsByOwnerRequest{ @@ -254,5 +262,5 @@ func (suite *KeeperTestSuite) TestQueryNftsByOwner() { }) suite.NoError(err) suite.Len(res.Nfts, 1) - suite.Equal(nft3.TokenId, res.Nfts[0].TokenId) + suite.Equal(testNft3.TokenId, res.Nfts[0].TokenId) } diff --git a/x/nft/keeper/keeper.go b/x/nft/keeper/keeper.go index f3b7ddbd..64c7bc53 100644 --- a/x/nft/keeper/keeper.go +++ b/x/nft/keeper/keeper.go @@ -57,10 +57,10 @@ type Keeper struct { logger log.Logger Schema collections.Schema - Collections collections.Map[string, types.Collection] + Collections collections.Map[string, types.Collection] // (collectionDenom) -> Collection Supply collections.Map[string, math.Int] - // (collectionDenom, tokenId) -> NFT - NFTs *collections.IndexedMap[collections.Pair[string, string], types.Nft, NFTIndexes] + NFTs *collections.IndexedMap[collections.Pair[string, string], types.Nft, NFTIndexes] // (collectionDenom, tokenId) -> NFT + Editions collections.Map[collections.Triple[string, string, uint64], types.Edition] // (collectionDenom, tokenId, edition) -> Edition } func NewKeeper(cdc codec.BinaryCodec, key storetypes.StoreKey, storeService store.KVStoreService, ak types.AccountKeeper, logger log.Logger) Keeper { @@ -81,8 +81,19 @@ func NewKeeper(cdc codec.BinaryCodec, key storetypes.StoreKey, storeService stor ak: ak, logger: logger, // TODO: fix the store once we add queries - Collections: collections.NewMap(sb, types.CollectionsPrefix, "collections", collections.StringKey, codec.CollValue[types.Collection](cdc)), - Supply: collections.NewMap(sb, types.SupplyPrefix, "supply", collections.StringKey, sdk.IntValue), + Collections: collections.NewMap( + sb, + types.CollectionsPrefix, + "collections", + collections.StringKey, codec.CollValue[types.Collection](cdc), + ), + Supply: collections.NewMap( + sb, + types.SupplyPrefix, + "supply", + collections.StringKey, + sdk.IntValue, + ), NFTs: collections.NewIndexedMap( sb, types.NFTsPrefix, @@ -91,6 +102,13 @@ func NewKeeper(cdc codec.BinaryCodec, key storetypes.StoreKey, storeService stor codec.CollValue[types.Nft](cdc), newNFTIndexes(sb), ), + Editions: collections.NewMap( + sb, + types.EditionsPrefix, + "editions", + collections.TripleKeyCodec(collections.StringKey, collections.StringKey, collections.Uint64Key), + codec.CollValue[types.Edition](cdc), + ), } schema, err := sb.Build() diff --git a/x/nft/keeper/keeper_test.go b/x/nft/keeper/keeper_test.go index 188fc04a..c129f2b3 100644 --- a/x/nft/keeper/keeper_test.go +++ b/x/nft/keeper/keeper_test.go @@ -15,18 +15,50 @@ import ( ) var ( - creator = sdk.AccAddress(tmhash.SumTruncated([]byte("creator"))) - owner = sdk.AccAddress(tmhash.SumTruncated([]byte("owner"))) - owner2 = sdk.AccAddress(tmhash.SumTruncated([]byte("owner2"))) - // initAmt = math.NewIntFromUint64(1000000000) - // initCoin = sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, initAmt)} + creator1 = sdk.AccAddress(tmhash.SumTruncated([]byte("creator1"))) + creator2 = sdk.AccAddress(tmhash.SumTruncated([]byte("creator2"))) + + minter1 = sdk.AccAddress(tmhash.SumTruncated([]byte("minter1"))) + minter2 = sdk.AccAddress(tmhash.SumTruncated([]byte("minter2"))) + + owner1 = sdk.AccAddress(tmhash.SumTruncated([]byte("owner1"))) + owner2 = sdk.AccAddress(tmhash.SumTruncated([]byte("owner2"))) + + testCollection1 = types.Collection{ + Name: "My NFT Collection", + Symbol: "MYNFT", + Description: "My NFT Collection Description", + Uri: "ipfs://my-nft-collection-metadata.json", + Minter: minter1.String(), + } + expectedDenom1 = "nft9436DDD23FB751AEA7BC6C767F20F943DD735E06" + + testNft1 = types.Nft{ + TokenId: "1", + Name: "My First NFT", + Description: "This is my first NFT", + Uri: "ipfs://my-first-nft-metadata.json", + } + + testNft2 = types.Nft{ + TokenId: "2", + Name: "My Second NFT", + Description: "This is my second NFT", + Uri: "ipfs://my-second-nft-metadata.json", + } + + testNft3 = types.Nft{ + TokenId: "3", + Name: "My Third NFT", + Description: "This is my third NFT", + Uri: "ipfs://my-third-nft-metadata.json", + } ) type KeeperTestSuite struct { apptesting.KeeperTestHelper - ctx sdk.Context - // bk bankkeeper.Keeper + ctx sdk.Context keeper keeper.Keeper app *simapp.BitsongApp } @@ -36,15 +68,8 @@ func (suite *KeeperTestSuite) SetupTest() { app := suite.App suite.keeper = app.NftKeeper - // suite.bk = app.BankKeeper suite.App = app suite.ctx = suite.Ctx - - // init tokens to addr - /*err := suite.bk.MintCoins(suite.ctx, types.ModuleName, initCoin) - suite.NoError(err) - err = suite.bk.SendCoinsFromModuleToAccount(suite.ctx, types.ModuleName, creator, initCoin) - suite.NoError(err)*/ } func TestKeeperSuite(t *testing.T) { @@ -52,60 +77,71 @@ func TestKeeperSuite(t *testing.T) { } func (suite *KeeperTestSuite) TestCreateCollection() { - testCollection := types.Collection{ - Name: "My NFT Collection", - Symbol: "MYNFT", - Description: "My NFT Collection Description", - Uri: "ipfs://my-nft-collection-metadata.json", - } - expectedDenom := "nft653AF6715F0C4EE2E24A54B191EBD0AD5DB33723" - - denom, err := suite.keeper.CreateCollection(suite.ctx, creator, testCollection) + denom, err := suite.keeper.CreateCollection( + suite.ctx, + creator1, + minter1, + testCollection1.Symbol, + testCollection1.Name, + testCollection1.Description, + testCollection1.Uri, + ) suite.NoError(err) - suite.Equal(expectedDenom, denom) - - _, err = suite.keeper.CreateCollection(suite.ctx, creator, testCollection) + suite.Equal(expectedDenom1, denom) + + _, err = suite.keeper.CreateCollection( + suite.ctx, + creator1, + minter1, + testCollection1.Symbol, + testCollection1.Name, + testCollection1.Description, + testCollection1.Uri, + ) suite.Error(err) } func (suite *KeeperTestSuite) TestMintNFT() { - testCollection := types.Collection{ - Name: "My NFT Collection", - Symbol: "MYNFT", - Description: "My NFT Collection Description", - Uri: "ipfs://my-nft-collection-metadata.json", - Minter: creator.String(), - } - expectedDenom := "nft653AF6715F0C4EE2E24A54B191EBD0AD5DB33723" - - collectionDenom, err := suite.keeper.CreateCollection(suite.ctx, creator, testCollection) + collectionDenom, err := suite.keeper.CreateCollection( + suite.ctx, + creator1, + minter1, + testCollection1.Symbol, + testCollection1.Name, + testCollection1.Description, + testCollection1.Uri, + ) suite.NoError(err) - suite.Equal(expectedDenom, collectionDenom) + suite.Equal(expectedDenom1, collectionDenom) supply := suite.keeper.GetSupply(suite.ctx, collectionDenom) suite.Equal(math.NewInt(0), supply) - nft1 := types.Nft{ - TokenId: "1", - Name: "My First NFT", - Description: "This is my first NFT", - Uri: "ipfs://my-first-nft-metadata.json", - } - - nft2 := types.Nft{ - TokenId: "2", - Name: "My First NFT", - Description: "This is my first NFT", - Uri: "ipfs://my-first-nft-metadata.json", - } - - err = suite.keeper.MintNFT(suite.ctx, collectionDenom, creator, owner, nft1) + err = suite.keeper.MintNFT( + suite.ctx, + minter1, + owner1, + collectionDenom, + testNft1.TokenId, + testNft1.Name, + testNft1.Description, + testNft1.Uri, + ) suite.NoError(err) supply = suite.keeper.GetSupply(suite.ctx, collectionDenom) suite.Equal(math.NewInt(1), supply) - err = suite.keeper.MintNFT(suite.ctx, collectionDenom, creator, owner, nft2) + err = suite.keeper.MintNFT( + suite.ctx, + minter1, + owner1, + collectionDenom, + testNft2.TokenId, + testNft2.Name, + testNft2.Description, + testNft2.Uri, + ) suite.NoError(err) supply = suite.keeper.GetSupply(suite.ctx, collectionDenom) @@ -113,35 +149,35 @@ func (suite *KeeperTestSuite) TestMintNFT() { } func (suite *KeeperTestSuite) TestSendNFT() { - testCollection := types.Collection{ - Name: "My NFT Collection", - Symbol: "MYNFT", - Description: "My NFT Collection Description", - Uri: "ipfs://my-nft-collection-metadata.json", - Minter: creator.String(), - } - expectedDenom := "nft653AF6715F0C4EE2E24A54B191EBD0AD5DB33723" - - collectionDenom, err := suite.keeper.CreateCollection(suite.ctx, creator, testCollection) + collectionDenom, err := suite.keeper.CreateCollection( + suite.ctx, + creator1, + minter1, + testCollection1.Symbol, + testCollection1.Name, + testCollection1.Description, + testCollection1.Uri, + ) suite.NoError(err) - suite.Equal(expectedDenom, collectionDenom) - - nft1 := types.Nft{ - TokenId: "1", - Name: "My First NFT", - Description: "This is my first NFT", - Uri: "ipfs://my-first-nft-metadata.json", - } - err = suite.keeper.MintNFT(suite.ctx, collectionDenom, creator, owner, nft1) + err = suite.keeper.MintNFT( + suite.ctx, + minter1, + owner1, + collectionDenom, + testNft1.TokenId, + testNft1.Name, + testNft1.Description, + testNft1.Uri, + ) suite.NoError(err) res, err := suite.keeper.AllNftsByOwner(suite.ctx, &types.QueryAllNftsByOwnerRequest{ - Owner: owner.String(), + Owner: owner1.String(), }) suite.NoError(err) suite.Len(res.Nfts, 1) - suite.Equal(nft1.TokenId, res.Nfts[0].TokenId) + suite.Equal(testNft1.TokenId, res.Nfts[0].TokenId) res, err = suite.keeper.AllNftsByOwner(suite.ctx, &types.QueryAllNftsByOwnerRequest{ Owner: owner2.String(), @@ -149,7 +185,7 @@ func (suite *KeeperTestSuite) TestSendNFT() { suite.NoError(err) suite.Len(res.Nfts, 0) - err = suite.keeper.SendNft(suite.ctx, owner, owner2, collectionDenom, "1") + err = suite.keeper.SendNft(suite.ctx, owner1, owner2, collectionDenom, "1") suite.NoError(err) res, err = suite.keeper.AllNftsByOwner(suite.ctx, &types.QueryAllNftsByOwnerRequest{ @@ -157,10 +193,10 @@ func (suite *KeeperTestSuite) TestSendNFT() { }) suite.NoError(err) suite.Len(res.Nfts, 1) - suite.Equal(nft1.TokenId, res.Nfts[0].TokenId) + suite.Equal(testNft1.TokenId, res.Nfts[0].TokenId) res, err = suite.keeper.AllNftsByOwner(suite.ctx, &types.QueryAllNftsByOwnerRequest{ - Owner: owner.String(), + Owner: owner1.String(), }) suite.NoError(err) suite.Len(res.Nfts, 0) diff --git a/x/nft/keeper/nft.go b/x/nft/keeper/nft.go index e9433f26..6e9a2d51 100644 --- a/x/nft/keeper/nft.go +++ b/x/nft/keeper/nft.go @@ -11,18 +11,27 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" ) -func (k Keeper) MintNFT(ctx context.Context, collectionDenom string, minter sdk.AccAddress, owner sdk.AccAddress, metadata types.Nft) error { - if strings.TrimSpace(metadata.TokenId) == "" { - return fmt.Errorf("token ID cannot be empty") +func (k Keeper) MintNFT( + ctx context.Context, + minter sdk.AccAddress, + owner sdk.AccAddress, + collectionDenom, + tokenId, + name, + description, + uri string, +) error { + if err := k.validateNftMetadata(tokenId, name, description, uri); err != nil { + return err } - nftKey := collections.Join(collectionDenom, metadata.TokenId) + nftKey := collections.Join(collectionDenom, tokenId) has, err := k.NFTs.Has(ctx, nftKey) if err != nil { return fmt.Errorf("failed to check NFT: %w", err) } if has { - return fmt.Errorf("NFT with token ID %s already exists in collection %s", metadata.TokenId, collectionDenom) + return fmt.Errorf("NFT with token ID %s already exists in collection %s", tokenId, collectionDenom) } coll, err := k.Collections.Get(ctx, collectionDenom) @@ -45,10 +54,17 @@ func (k Keeper) MintNFT(ctx context.Context, collectionDenom string, minter sdk. // TODO: Charge fee if necessary - metadata.Collection = collectionDenom - metadata.Owner = owner.String() + nft := types.Nft{ + Collection: collectionDenom, + TokenId: tokenId, + Name: name, + Description: description, + Uri: uri, + Owner: owner.String(), + Editions: 0, + } - if err := k.setNft(ctx, collectionDenom, metadata.TokenId, metadata); err != nil { + if err := k.setNft(ctx, nft); err != nil { return fmt.Errorf("failed to set NFT: %w", err) } @@ -95,13 +111,55 @@ func (k Keeper) SendNft(ctx context.Context, fromAddr, toAddr sdk.AccAddress, co return nil } +func (k Keeper) GetNft(ctx context.Context, collectionDenom, tokenId string) (*types.Nft, error) { + nftKey := collections.Join(collectionDenom, tokenId) + has, err := k.NFTs.Has(ctx, nftKey) + if err != nil { + return nil, fmt.Errorf("failed to check NFT: %w", err) + } + if !has { + return nil, fmt.Errorf("NFT with token ID %s does not exist in collection %s", tokenId, collectionDenom) + } + + nft, err := k.NFTs.Get(ctx, nftKey) + if err != nil { + return nil, fmt.Errorf("failed to get NFT: %w", err) + } + + return &nft, nil +} + +func (k Keeper) validateNftMetadata(tokenId, name, description, uri string) error { + if strings.TrimSpace(tokenId) == "" { + return fmt.Errorf("token ID cannot be empty") + } + + if len(tokenId) > types.MaxTokenIdLength { + return fmt.Errorf("token ID length exceeds maximum of %d", types.MaxTokenIdLength) + } + + if len(name) > types.MaxNameLength { + return fmt.Errorf("name length exceeds maximum of %d", types.MaxNameLength) + + } + if len(description) > types.MaxDescriptionLength { + return fmt.Errorf("description length exceeds maximum of %d", types.MaxDescriptionLength) + } + + if len(uri) > types.MaxURILength { + return fmt.Errorf("URI length exceeds maximum of %d", types.MaxURILength) + } + + return nil +} + func (k Keeper) createNftDenom(ctx context.Context, collectionDenom string) string { supply := k.GetSupply(ctx, collectionDenom) return fmt.Sprintf("%s-%d", collectionDenom, supply.Uint64()+1) } -func (k Keeper) setNft(ctx context.Context, collectionDenom string, tokenId string, nft types.Nft) error { - pk := collections.Join(collectionDenom, tokenId) +func (k Keeper) setNft(ctx context.Context, nft types.Nft) error { + pk := collections.Join(nft.Collection, nft.TokenId) return k.NFTs.Set(ctx, pk, nft) } @@ -116,7 +174,7 @@ func (k Keeper) changeNftOwner(ctx context.Context, oldOwner, newOwner sdk.AccAd } nft.Owner = newOwner.String() - err = k.setNft(ctx, collectionDenom, tokenId, nft) + err = k.setNft(ctx, nft) if err != nil { return fmt.Errorf("failed to set NFT Owner: %w", err) } @@ -129,3 +187,13 @@ func (k Keeper) changeNftOwner(ctx context.Context, oldOwner, newOwner sdk.AccAd return nil } + +func (k Keeper) incrementEdition(ctx context.Context, collectionDenom, tokenId string) error { + nft, err := k.NFTs.Get(ctx, collections.Join(collectionDenom, tokenId)) + if err != nil { + return fmt.Errorf("failed to get NFT: %w", err) + } + + nft.Editions += 1 + return k.setNft(ctx, nft) +} diff --git a/x/nft/types/keys.go b/x/nft/types/keys.go index f3924d9d..3c492015 100644 --- a/x/nft/types/keys.go +++ b/x/nft/types/keys.go @@ -15,6 +15,12 @@ const ( RouterKey = ModuleName MaxDenomLength = 43 + + MaxTokenIdLength = 20 + MaxSymbolLength = 15 + MaxNameLength = 128 + MaxDescriptionLength = 256 + MaxURILength = 150 ) var ( @@ -23,6 +29,7 @@ var ( NFTsPrefix = collections.NewPrefix(2) NFTsByCollectionPrefix = collections.NewPrefix(3) NFTsByOwnerPrefix = collections.NewPrefix(4) + EditionsPrefix = collections.NewPrefix(5) ) func SplitNftLengthPrefixedKey(key []byte) (denom, tokenId []byte, err error) { diff --git a/x/nft/types/nft.pb.go b/x/nft/types/nft.pb.go index 38ab9f4b..c6bcf8f1 100644 --- a/x/nft/types/nft.pb.go +++ b/x/nft/types/nft.pb.go @@ -24,13 +24,14 @@ var _ = math.Inf const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package type Collection struct { - Symbol string `protobuf:"bytes,1,opt,name=symbol,proto3" json:"symbol,omitempty"` - Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` - Description string `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty"` - Uri string `protobuf:"bytes,4,opt,name=uri,proto3" json:"uri,omitempty"` - Creator string `protobuf:"bytes,5,opt,name=creator,proto3" json:"creator,omitempty"` - Minter string `protobuf:"bytes,6,opt,name=minter,proto3" json:"minter,omitempty"` - NumTokens uint64 `protobuf:"varint,7,opt,name=num_tokens,json=numTokens,proto3" json:"num_tokens,omitempty"` + Denom string `protobuf:"bytes,1,opt,name=denom,proto3" json:"denom,omitempty"` + Symbol string `protobuf:"bytes,2,opt,name=symbol,proto3" json:"symbol,omitempty"` + Name string `protobuf:"bytes,3,opt,name=name,proto3" json:"name,omitempty"` + Description string `protobuf:"bytes,4,opt,name=description,proto3" json:"description,omitempty"` + Uri string `protobuf:"bytes,5,opt,name=uri,proto3" json:"uri,omitempty"` + Creator string `protobuf:"bytes,6,opt,name=creator,proto3" json:"creator,omitempty"` + Minter string `protobuf:"bytes,7,opt,name=minter,proto3" json:"minter,omitempty"` + NumTokens uint64 `protobuf:"varint,8,opt,name=num_tokens,json=numTokens,proto3" json:"num_tokens,omitempty"` } func (m *Collection) Reset() { *m = Collection{} } @@ -73,6 +74,8 @@ type Nft struct { Description string `protobuf:"bytes,4,opt,name=description,proto3" json:"description,omitempty"` Uri string `protobuf:"bytes,5,opt,name=uri,proto3" json:"uri,omitempty"` Owner string `protobuf:"bytes,6,opt,name=owner,proto3" json:"owner,omitempty"` + // TODO: add max_editions + Editions uint64 `protobuf:"varint,7,opt,name=editions,proto3" json:"editions,omitempty"` } func (m *Nft) Reset() { *m = Nft{} } @@ -108,36 +111,80 @@ func (m *Nft) XXX_DiscardUnknown() { var xxx_messageInfo_Nft proto.InternalMessageInfo +type Edition struct { + Collection string `protobuf:"bytes,1,opt,name=collection,proto3" json:"collection,omitempty"` + TokenId string `protobuf:"bytes,2,opt,name=token_id,json=tokenId,proto3" json:"token_id,omitempty"` + Seq uint64 `protobuf:"varint,3,opt,name=seq,proto3" json:"seq,omitempty"` + Owner string `protobuf:"bytes,4,opt,name=owner,proto3" json:"owner,omitempty"` +} + +func (m *Edition) Reset() { *m = Edition{} } +func (m *Edition) String() string { return proto.CompactTextString(m) } +func (*Edition) ProtoMessage() {} +func (*Edition) Descriptor() ([]byte, []int) { + return fileDescriptor_51b0314c164430ab, []int{2} +} +func (m *Edition) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Edition) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Edition.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 *Edition) XXX_Merge(src proto.Message) { + xxx_messageInfo_Edition.Merge(m, src) +} +func (m *Edition) XXX_Size() int { + return m.Size() +} +func (m *Edition) XXX_DiscardUnknown() { + xxx_messageInfo_Edition.DiscardUnknown(m) +} + +var xxx_messageInfo_Edition proto.InternalMessageInfo + func init() { proto.RegisterType((*Collection)(nil), "bitsong.nft.v1beta1.Collection") proto.RegisterType((*Nft)(nil), "bitsong.nft.v1beta1.Nft") + proto.RegisterType((*Edition)(nil), "bitsong.nft.v1beta1.Edition") } func init() { proto.RegisterFile("bitsong/nft/v1beta1/nft.proto", fileDescriptor_51b0314c164430ab) } var fileDescriptor_51b0314c164430ab = []byte{ - // 336 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x91, 0x3f, 0x4f, 0xc2, 0x40, - 0x18, 0xc6, 0x7b, 0xb6, 0x80, 0xbc, 0x2e, 0xe6, 0x24, 0xe6, 0x34, 0xe1, 0x24, 0x4c, 0x2c, 0xd2, - 0x10, 0x37, 0x47, 0x9d, 0x8c, 0x89, 0x03, 0x71, 0x72, 0x21, 0x6d, 0xb9, 0xd6, 0x8b, 0xed, 0xbd, - 0xa4, 0xbd, 0xaa, 0x7c, 0x03, 0x47, 0x3f, 0x82, 0x83, 0xdf, 0xc4, 0xc5, 0x91, 0xd1, 0xd1, 0xc0, - 0x17, 0x31, 0xbd, 0x1e, 0xc8, 0xc2, 0xf6, 0xfc, 0x69, 0xfb, 0xf6, 0x97, 0x07, 0xba, 0xa1, 0xd4, - 0x05, 0xaa, 0xc4, 0x57, 0xb1, 0xf6, 0x9f, 0x47, 0xa1, 0xd0, 0xc1, 0xa8, 0xd2, 0xc3, 0x59, 0x8e, - 0x1a, 0xe9, 0x91, 0xad, 0x87, 0x55, 0x64, 0xeb, 0xd3, 0x4e, 0x82, 0x09, 0x9a, 0xde, 0xaf, 0x54, - 0xfd, 0x68, 0xff, 0x8b, 0x00, 0x5c, 0x63, 0x9a, 0x8a, 0x48, 0x4b, 0x54, 0xf4, 0x18, 0x9a, 0xc5, - 0x3c, 0x0b, 0x31, 0x65, 0xa4, 0x47, 0x06, 0xed, 0xb1, 0x75, 0x94, 0x82, 0xa7, 0x82, 0x4c, 0xb0, - 0x3d, 0x93, 0x1a, 0x4d, 0x7b, 0x70, 0x30, 0x15, 0x45, 0x94, 0xcb, 0x59, 0xf5, 0x2a, 0x73, 0x4d, - 0xb5, 0x1d, 0xd1, 0x43, 0x70, 0xcb, 0x5c, 0x32, 0xcf, 0x34, 0x95, 0xa4, 0x0c, 0x5a, 0x51, 0x2e, - 0x02, 0x8d, 0x39, 0x6b, 0x98, 0x74, 0x6d, 0xab, 0xcb, 0x99, 0x54, 0x5a, 0xe4, 0xac, 0x59, 0x5f, - 0xae, 0x1d, 0xed, 0x02, 0xa8, 0x32, 0x9b, 0x68, 0x7c, 0x12, 0xaa, 0x60, 0xad, 0x1e, 0x19, 0x78, - 0xe3, 0xb6, 0x2a, 0xb3, 0x7b, 0x13, 0x5c, 0x7a, 0x6f, 0x1f, 0x67, 0x4e, 0xff, 0x93, 0x80, 0x7b, - 0x17, 0x6b, 0xca, 0x01, 0xa2, 0x0d, 0x8c, 0x45, 0xd8, 0x4a, 0xe8, 0x09, 0xec, 0x9b, 0x0f, 0x4d, - 0xe4, 0xd4, 0xa2, 0xb4, 0x8c, 0xbf, 0x99, 0x6e, 0x08, 0xdd, 0xdd, 0x84, 0xde, 0x4e, 0xc2, 0xc6, - 0x3f, 0x61, 0x07, 0x1a, 0xf8, 0xa2, 0x36, 0x18, 0xb5, 0xa9, 0x7f, 0xf3, 0xea, 0xf6, 0x7b, 0xc9, - 0xc9, 0x62, 0xc9, 0xc9, 0xef, 0x92, 0x93, 0xf7, 0x15, 0x77, 0x16, 0x2b, 0xee, 0xfc, 0xac, 0xb8, - 0xf3, 0x30, 0x4a, 0xa4, 0x7e, 0x2c, 0xc3, 0x61, 0x84, 0x99, 0x6f, 0xc7, 0xc3, 0x38, 0x96, 0x91, - 0x0c, 0x52, 0x3f, 0xc1, 0xf3, 0xf5, 0xdc, 0xaf, 0x66, 0x70, 0x3d, 0x9f, 0x89, 0x22, 0x6c, 0x9a, - 0x01, 0x2f, 0xfe, 0x02, 0x00, 0x00, 0xff, 0xff, 0x6e, 0x12, 0x79, 0x6b, 0x0c, 0x02, 0x00, 0x00, + // 381 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x92, 0xbd, 0x6e, 0xdb, 0x30, + 0x10, 0xc7, 0xc5, 0x4a, 0xb6, 0xec, 0xeb, 0x62, 0xb0, 0x46, 0xc1, 0x1a, 0xb0, 0x6a, 0x78, 0xf2, + 0x52, 0x0b, 0x46, 0xb7, 0x8e, 0x2d, 0x3a, 0x14, 0x05, 0x32, 0x08, 0x99, 0xb2, 0x18, 0xfa, 0xa0, + 0x14, 0x22, 0x12, 0xcf, 0x91, 0xa8, 0x24, 0x7e, 0x83, 0x8c, 0x79, 0x84, 0x3c, 0x4a, 0xc6, 0x8c, + 0x1e, 0x93, 0x2d, 0xb0, 0x5f, 0x24, 0x20, 0x25, 0x3b, 0xde, 0x93, 0xed, 0xff, 0x21, 0xf1, 0xee, + 0x07, 0x1c, 0x8c, 0x23, 0xa1, 0x2a, 0x94, 0x99, 0x2f, 0x53, 0xe5, 0x5f, 0x2d, 0x22, 0xae, 0xc2, + 0x85, 0xd6, 0xf3, 0x55, 0x89, 0x0a, 0xe9, 0x97, 0xb6, 0x9e, 0xeb, 0xa8, 0xad, 0x47, 0xc3, 0x0c, + 0x33, 0x34, 0xbd, 0xaf, 0x55, 0xf3, 0xe9, 0xf4, 0x99, 0x00, 0xfc, 0xc1, 0x3c, 0xe7, 0xb1, 0x12, + 0x28, 0xe9, 0x10, 0x3a, 0x09, 0x97, 0x58, 0x30, 0x32, 0x21, 0xb3, 0x7e, 0xd0, 0x18, 0xfa, 0x15, + 0xba, 0xd5, 0xba, 0x88, 0x30, 0x67, 0x9f, 0x4c, 0xdc, 0x3a, 0x4a, 0xc1, 0x91, 0x61, 0xc1, 0x99, + 0x6d, 0x52, 0xa3, 0xe9, 0x04, 0x3e, 0x27, 0xbc, 0x8a, 0x4b, 0xb1, 0xd2, 0x0f, 0x32, 0xc7, 0x54, + 0xc7, 0x11, 0x1d, 0x80, 0x5d, 0x97, 0x82, 0x75, 0x4c, 0xa3, 0x25, 0x65, 0xe0, 0xc6, 0x25, 0x0f, + 0x15, 0x96, 0xac, 0x6b, 0xd2, 0xbd, 0xd5, 0x93, 0x0b, 0x21, 0x15, 0x2f, 0x99, 0xdb, 0x4c, 0x6e, + 0x1c, 0x1d, 0x03, 0xc8, 0xba, 0x58, 0x2a, 0xbc, 0xe0, 0xb2, 0x62, 0xbd, 0x09, 0x99, 0x39, 0x41, + 0x5f, 0xd6, 0xc5, 0xa9, 0x09, 0x7e, 0x39, 0xb7, 0xf7, 0xdf, 0xad, 0xe9, 0x03, 0x01, 0xfb, 0x24, + 0x55, 0xd4, 0x03, 0x88, 0x0f, 0x88, 0x2d, 0xd9, 0x51, 0x42, 0xbf, 0x41, 0xcf, 0x3c, 0xb4, 0x14, + 0x49, 0x0b, 0xe8, 0x1a, 0xff, 0x2f, 0xf9, 0x30, 0xc2, 0x21, 0x74, 0xf0, 0x5a, 0xf2, 0x3d, 0x5f, + 0x63, 0xe8, 0x08, 0x7a, 0x3c, 0x11, 0xfa, 0x97, 0xca, 0xf0, 0x39, 0xc1, 0xc1, 0xb7, 0x08, 0x25, + 0xb8, 0x7f, 0x9b, 0xe4, 0x3d, 0x14, 0x03, 0xb0, 0x2b, 0x7e, 0x69, 0x20, 0x9c, 0x40, 0xcb, 0xb7, + 0x7d, 0x9c, 0xa3, 0x7d, 0x9a, 0x99, 0xbf, 0xff, 0x3f, 0x6e, 0x3d, 0xb2, 0xd9, 0x7a, 0xe4, 0x65, + 0xeb, 0x91, 0xbb, 0x9d, 0x67, 0x6d, 0x76, 0x9e, 0xf5, 0xb4, 0xf3, 0xac, 0xb3, 0x45, 0x26, 0xd4, + 0x79, 0x1d, 0xcd, 0x63, 0x2c, 0xfc, 0xf6, 0xc4, 0x30, 0x4d, 0x45, 0x2c, 0xc2, 0xdc, 0xcf, 0xf0, + 0xc7, 0xfe, 0x28, 0x6f, 0xcc, 0x59, 0xaa, 0xf5, 0x8a, 0x57, 0x51, 0xd7, 0x9c, 0xd9, 0xcf, 0xd7, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xd2, 0xcf, 0x86, 0x6c, 0xb2, 0x02, 0x00, 0x00, } func (m *Collection) Marshal() (dAtA []byte, err error) { @@ -163,48 +210,55 @@ func (m *Collection) MarshalToSizedBuffer(dAtA []byte) (int, error) { if m.NumTokens != 0 { i = encodeVarintNft(dAtA, i, uint64(m.NumTokens)) i-- - dAtA[i] = 0x38 + dAtA[i] = 0x40 } if len(m.Minter) > 0 { i -= len(m.Minter) copy(dAtA[i:], m.Minter) i = encodeVarintNft(dAtA, i, uint64(len(m.Minter))) i-- - dAtA[i] = 0x32 + dAtA[i] = 0x3a } if len(m.Creator) > 0 { i -= len(m.Creator) copy(dAtA[i:], m.Creator) i = encodeVarintNft(dAtA, i, uint64(len(m.Creator))) i-- - dAtA[i] = 0x2a + dAtA[i] = 0x32 } if len(m.Uri) > 0 { i -= len(m.Uri) copy(dAtA[i:], m.Uri) i = encodeVarintNft(dAtA, i, uint64(len(m.Uri))) i-- - dAtA[i] = 0x22 + dAtA[i] = 0x2a } if len(m.Description) > 0 { i -= len(m.Description) copy(dAtA[i:], m.Description) i = encodeVarintNft(dAtA, i, uint64(len(m.Description))) i-- - dAtA[i] = 0x1a + dAtA[i] = 0x22 } if len(m.Name) > 0 { i -= len(m.Name) copy(dAtA[i:], m.Name) i = encodeVarintNft(dAtA, i, uint64(len(m.Name))) i-- - dAtA[i] = 0x12 + dAtA[i] = 0x1a } if len(m.Symbol) > 0 { i -= len(m.Symbol) copy(dAtA[i:], m.Symbol) i = encodeVarintNft(dAtA, i, uint64(len(m.Symbol))) i-- + dAtA[i] = 0x12 + } + if len(m.Denom) > 0 { + i -= len(m.Denom) + copy(dAtA[i:], m.Denom) + i = encodeVarintNft(dAtA, i, uint64(len(m.Denom))) + i-- dAtA[i] = 0xa } return len(dAtA) - i, nil @@ -230,6 +284,11 @@ func (m *Nft) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.Editions != 0 { + i = encodeVarintNft(dAtA, i, uint64(m.Editions)) + i-- + dAtA[i] = 0x38 + } if len(m.Owner) > 0 { i -= len(m.Owner) copy(dAtA[i:], m.Owner) @@ -275,6 +334,55 @@ func (m *Nft) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *Edition) 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 *Edition) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Edition) 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 = encodeVarintNft(dAtA, i, uint64(len(m.Owner))) + i-- + dAtA[i] = 0x22 + } + if m.Seq != 0 { + i = encodeVarintNft(dAtA, i, uint64(m.Seq)) + i-- + dAtA[i] = 0x18 + } + if len(m.TokenId) > 0 { + i -= len(m.TokenId) + copy(dAtA[i:], m.TokenId) + i = encodeVarintNft(dAtA, i, uint64(len(m.TokenId))) + i-- + dAtA[i] = 0x12 + } + if len(m.Collection) > 0 { + i -= len(m.Collection) + copy(dAtA[i:], m.Collection) + i = encodeVarintNft(dAtA, i, uint64(len(m.Collection))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + func encodeVarintNft(dAtA []byte, offset int, v uint64) int { offset -= sovNft(v) base := offset @@ -292,6 +400,10 @@ func (m *Collection) Size() (n int) { } var l int _ = l + l = len(m.Denom) + if l > 0 { + n += 1 + l + sovNft(uint64(l)) + } l = len(m.Symbol) if l > 0 { n += 1 + l + sovNft(uint64(l)) @@ -352,6 +464,33 @@ func (m *Nft) Size() (n int) { if l > 0 { n += 1 + l + sovNft(uint64(l)) } + if m.Editions != 0 { + n += 1 + sovNft(uint64(m.Editions)) + } + return n +} + +func (m *Edition) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Collection) + if l > 0 { + n += 1 + l + sovNft(uint64(l)) + } + l = len(m.TokenId) + if l > 0 { + n += 1 + l + sovNft(uint64(l)) + } + if m.Seq != 0 { + n += 1 + sovNft(uint64(m.Seq)) + } + l = len(m.Owner) + if l > 0 { + n += 1 + l + sovNft(uint64(l)) + } return n } @@ -391,6 +530,38 @@ func (m *Collection) Unmarshal(dAtA []byte) error { } switch fieldNum { case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Denom", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowNft + } + 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 ErrInvalidLengthNft + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthNft + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Denom = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Symbol", wireType) } @@ -422,7 +593,7 @@ func (m *Collection) Unmarshal(dAtA []byte) error { } m.Symbol = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 2: + case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) } @@ -454,7 +625,7 @@ func (m *Collection) Unmarshal(dAtA []byte) error { } m.Name = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 3: + case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Description", wireType) } @@ -486,7 +657,7 @@ func (m *Collection) Unmarshal(dAtA []byte) error { } m.Description = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 4: + case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Uri", wireType) } @@ -518,7 +689,7 @@ func (m *Collection) Unmarshal(dAtA []byte) error { } m.Uri = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 5: + case 6: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Creator", wireType) } @@ -550,7 +721,7 @@ func (m *Collection) Unmarshal(dAtA []byte) error { } m.Creator = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 6: + case 7: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Minter", wireType) } @@ -582,7 +753,7 @@ func (m *Collection) Unmarshal(dAtA []byte) error { } m.Minter = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 7: + case 8: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field NumTokens", wireType) } @@ -843,6 +1014,190 @@ func (m *Nft) Unmarshal(dAtA []byte) error { } m.Owner = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex + case 7: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Editions", wireType) + } + m.Editions = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowNft + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Editions |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipNft(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthNft + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Edition) 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 ErrIntOverflowNft + } + 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: Edition: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Edition: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Collection", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowNft + } + 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 ErrInvalidLengthNft + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthNft + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Collection = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TokenId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowNft + } + 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 ErrInvalidLengthNft + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthNft + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.TokenId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Seq", wireType) + } + m.Seq = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowNft + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Seq |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + 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 ErrIntOverflowNft + } + 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 ErrInvalidLengthNft + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthNft + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Owner = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipNft(dAtA[iNdEx:]) From dd85dc498d205fc153da79940db24a152f6ec3c9 Mon Sep 17 00:00:00 2001 From: angelorc Date: Thu, 4 Sep 2025 17:28:05 +0200 Subject: [PATCH 10/15] Remove NumTokens query handlers and associated code from NFT query service - Deleted request and local_request functions for NumTokens. - Removed HTTP handler registration for NumTokens in both server and client. - Cleaned up related patterns and forwarding functions. --- proto/bitsong/nft/v1beta1/query.proto | 25 +- x/nft/keeper/grpc_query.go | 57 ++-- x/nft/keeper/grpc_query_test.go | 52 --- x/nft/types/query.pb.go | 452 +++----------------------- x/nft/types/query.pb.gw.go | 101 ------ 5 files changed, 87 insertions(+), 600 deletions(-) diff --git a/proto/bitsong/nft/v1beta1/query.proto b/proto/bitsong/nft/v1beta1/query.proto index 1b286c8c..5c5a754f 100644 --- a/proto/bitsong/nft/v1beta1/query.proto +++ b/proto/bitsong/nft/v1beta1/query.proto @@ -22,11 +22,6 @@ service Query { option (google.api.http).get = "/bitsong/nft/v1beta1/collections/{collection}/{token_id}/owner"; } - rpc NumTokens(QueryNumTokensRequest) returns (QueryNumTokensResponse) { - option (cosmos.query.v1.module_query_safe) = true; - option (google.api.http).get = "/bitsong/nft/v1beta1/collections/{collection}/num_tokens"; - } - rpc NftInfo(QueryNftInfoRequest) returns (QueryNftInfoResponse) { option (cosmos.query.v1.module_query_safe) = true; option (google.api.http).get = "/bitsong/nft/v1beta1/collections/{collection}/{token_id}"; @@ -41,6 +36,12 @@ service Query { option (cosmos.query.v1.module_query_safe) = true; option (google.api.http).get = "/bitsong/nft/v1beta1/nfts_by_owner/{owner}"; } + + // Edition returns a specific edition of an nft + // OwnerOfEdition returns the owner of a specific edition + // NumEditions returns the total number of editions + // NftEditions returns all editions of a specific nft + // AllNftEditionsByOwner returns all nft editions owned by the owner } message QueryCollectionRequest { @@ -72,20 +73,6 @@ message QueryOwnerOfResponse { string owner = 1; } -message QueryNumTokensRequest { - option (gogoproto.equal) = false; - option (gogoproto.goproto_getters) = false; - - string collection = 1; -} - -message QueryNumTokensResponse { - option (gogoproto.equal) = false; - option (gogoproto.goproto_getters) = false; - - uint64 count = 1; -} - message QueryNftInfoRequest { option (gogoproto.equal) = false; option (gogoproto.goproto_getters) = false; diff --git a/x/nft/keeper/grpc_query.go b/x/nft/keeper/grpc_query.go index 1cb735ff..0cc9d5b3 100644 --- a/x/nft/keeper/grpc_query.go +++ b/x/nft/keeper/grpc_query.go @@ -65,23 +65,6 @@ func (k Keeper) OwnerOf(ctx context.Context, req *types.QueryOwnerOfRequest) (*t }, nil } -func (k Keeper) NumTokens(ctx context.Context, req *types.QueryNumTokensRequest) (*types.QueryNumTokensResponse, error) { - if req == nil { - return nil, status.Error(codes.InvalidArgument, "empty request") - } - if req.Collection == "" { - return nil, status.Error(codes.InvalidArgument, "collection cannot be empty") - } - - sdkCtx := sdk.UnwrapSDKContext(ctx) - - supply := k.GetSupply(sdkCtx, req.Collection) - - return &types.QueryNumTokensResponse{ - Count: supply.Uint64(), - }, nil -} - func (k Keeper) NftInfo(ctx context.Context, req *types.QueryNftInfoRequest) (*types.QueryNftInfoResponse, error) { if req == nil { return nil, status.Error(codes.InvalidArgument, "empty request") @@ -133,6 +116,36 @@ func (k Keeper) Nfts(ctx context.Context, req *types.QueryNftsRequest) (*types.Q }, nil } +func (k Keeper) GetNftsByOwner(ctx context.Context, owner sdk.AccAddress, pagination *query.PageRequest) ([]types.Nft, *query.PageResponse, error) { + var nfts []types.Nft + + sdkCtx := sdk.UnwrapSDKContext(ctx) + + store := prefix.NewStore( + sdkCtx.KVStore(k.storeKey), + append(types.NFTsByOwnerPrefix, address.MustLengthPrefix(owner)...), + ) + + pageRes, err := query.Paginate(store, pagination, func(key []byte, value []byte) error { + denom, tokenId := types.MustSplitNftLengthPrefixedKey(key) + + nft, err := k.NFTs.Get(ctx, collections.Join(string(denom), string(tokenId))) + if err != nil { + return err + } + + nfts = append(nfts, nft) + + return nil + }) + + if err != nil { + return nil, nil, err + } + + return nfts, pageRes, nil +} + func (k Keeper) AllNftsByOwner(ctx context.Context, req *types.QueryAllNftsByOwnerRequest) (*types.QueryAllNftsByOwnerResponse, error) { if req == nil { return nil, status.Error(codes.InvalidArgument, "empty request") @@ -169,16 +182,6 @@ func (k Keeper) AllNftsByOwner(ctx context.Context, req *types.QueryAllNftsByOwn return nil, status.Error(codes.Internal, err.Error()) } - /*iter, err := k.NFTs.Indexes.Owner.MatchExact(ctx, owner) - if err != nil { - return nil, status.Error(codes.Internal, err.Error()) - } - defer iter.Close() - - nfts, err := indexes.CollectValues(ctx, k.NFTs, iter) - if err != nil { - return nil, status.Error(codes.Internal, err.Error()) - }*/ return &types.QueryAllNftsByOwnerResponse{ Nfts: nfts, Pagination: pageRes, diff --git a/x/nft/keeper/grpc_query_test.go b/x/nft/keeper/grpc_query_test.go index e9a30e3c..0c40cb85 100644 --- a/x/nft/keeper/grpc_query_test.go +++ b/x/nft/keeper/grpc_query_test.go @@ -59,58 +59,6 @@ func (suite *KeeperTestSuite) TestQueryOwnerOf() { suite.Equal(owner1.String(), res.Owner) } -func (suite *KeeperTestSuite) TestQueryNumTokens() { - collectionDenom, err := suite.keeper.CreateCollection( - suite.ctx, - creator1, - minter1, - testCollection1.Symbol, - testCollection1.Name, - testCollection1.Description, - testCollection1.Uri, - ) - suite.NoError(err) - - supply := suite.keeper.GetSupply(suite.ctx, collectionDenom) - suite.Equal(uint64(0), supply.Uint64()) - - err = suite.keeper.MintNFT( - suite.ctx, - minter1, - owner1, - collectionDenom, - testNft1.TokenId, - testNft1.Name, - testNft1.Description, - testNft1.Uri, - ) - suite.NoError(err) - - supply = suite.keeper.GetSupply(suite.ctx, collectionDenom) - suite.Equal(uint64(1), supply.Uint64()) - - err = suite.keeper.MintNFT( - suite.ctx, - minter1, - owner1, - collectionDenom, - testNft2.TokenId, - testNft2.Name, - testNft2.Description, - testNft2.Uri, - ) - suite.NoError(err) - - supply = suite.keeper.GetSupply(suite.ctx, collectionDenom) - suite.Equal(uint64(2), supply.Uint64()) - - res, err := suite.keeper.NumTokens(suite.ctx, &types.QueryNumTokensRequest{ - Collection: collectionDenom, - }) - suite.NoError(err) - suite.Equal(uint64(2), res.Count) -} - func (suite *KeeperTestSuite) TestQueryNftInfo() { collectionDenom, err := suite.keeper.CreateCollection( suite.ctx, diff --git a/x/nft/types/query.pb.go b/x/nft/types/query.pb.go index 56354654..a3023e79 100644 --- a/x/nft/types/query.pb.go +++ b/x/nft/types/query.pb.go @@ -179,80 +179,6 @@ func (m *QueryOwnerOfResponse) XXX_DiscardUnknown() { var xxx_messageInfo_QueryOwnerOfResponse proto.InternalMessageInfo -type QueryNumTokensRequest struct { - Collection string `protobuf:"bytes,1,opt,name=collection,proto3" json:"collection,omitempty"` -} - -func (m *QueryNumTokensRequest) Reset() { *m = QueryNumTokensRequest{} } -func (m *QueryNumTokensRequest) String() string { return proto.CompactTextString(m) } -func (*QueryNumTokensRequest) ProtoMessage() {} -func (*QueryNumTokensRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_c3d20ffbceb85197, []int{4} -} -func (m *QueryNumTokensRequest) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *QueryNumTokensRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_QueryNumTokensRequest.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 *QueryNumTokensRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_QueryNumTokensRequest.Merge(m, src) -} -func (m *QueryNumTokensRequest) XXX_Size() int { - return m.Size() -} -func (m *QueryNumTokensRequest) XXX_DiscardUnknown() { - xxx_messageInfo_QueryNumTokensRequest.DiscardUnknown(m) -} - -var xxx_messageInfo_QueryNumTokensRequest proto.InternalMessageInfo - -type QueryNumTokensResponse struct { - Count uint64 `protobuf:"varint,1,opt,name=count,proto3" json:"count,omitempty"` -} - -func (m *QueryNumTokensResponse) Reset() { *m = QueryNumTokensResponse{} } -func (m *QueryNumTokensResponse) String() string { return proto.CompactTextString(m) } -func (*QueryNumTokensResponse) ProtoMessage() {} -func (*QueryNumTokensResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_c3d20ffbceb85197, []int{5} -} -func (m *QueryNumTokensResponse) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *QueryNumTokensResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_QueryNumTokensResponse.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 *QueryNumTokensResponse) XXX_Merge(src proto.Message) { - xxx_messageInfo_QueryNumTokensResponse.Merge(m, src) -} -func (m *QueryNumTokensResponse) XXX_Size() int { - return m.Size() -} -func (m *QueryNumTokensResponse) XXX_DiscardUnknown() { - xxx_messageInfo_QueryNumTokensResponse.DiscardUnknown(m) -} - -var xxx_messageInfo_QueryNumTokensResponse proto.InternalMessageInfo - type QueryNftInfoRequest struct { Collection string `protobuf:"bytes,1,opt,name=collection,proto3" json:"collection,omitempty"` TokenId string `protobuf:"bytes,2,opt,name=token_id,json=tokenId,proto3" json:"token_id,omitempty"` @@ -262,7 +188,7 @@ func (m *QueryNftInfoRequest) Reset() { *m = QueryNftInfoRequest{} } func (m *QueryNftInfoRequest) String() string { return proto.CompactTextString(m) } func (*QueryNftInfoRequest) ProtoMessage() {} func (*QueryNftInfoRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_c3d20ffbceb85197, []int{6} + return fileDescriptor_c3d20ffbceb85197, []int{4} } func (m *QueryNftInfoRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -299,7 +225,7 @@ func (m *QueryNftInfoResponse) Reset() { *m = QueryNftInfoResponse{} } func (m *QueryNftInfoResponse) String() string { return proto.CompactTextString(m) } func (*QueryNftInfoResponse) ProtoMessage() {} func (*QueryNftInfoResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_c3d20ffbceb85197, []int{7} + return fileDescriptor_c3d20ffbceb85197, []int{5} } func (m *QueryNftInfoResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -337,7 +263,7 @@ func (m *QueryNftsRequest) Reset() { *m = QueryNftsRequest{} } func (m *QueryNftsRequest) String() string { return proto.CompactTextString(m) } func (*QueryNftsRequest) ProtoMessage() {} func (*QueryNftsRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_c3d20ffbceb85197, []int{8} + return fileDescriptor_c3d20ffbceb85197, []int{6} } func (m *QueryNftsRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -375,7 +301,7 @@ func (m *QueryNftsResponse) Reset() { *m = QueryNftsResponse{} } func (m *QueryNftsResponse) String() string { return proto.CompactTextString(m) } func (*QueryNftsResponse) ProtoMessage() {} func (*QueryNftsResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_c3d20ffbceb85197, []int{9} + return fileDescriptor_c3d20ffbceb85197, []int{7} } func (m *QueryNftsResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -413,7 +339,7 @@ func (m *QueryAllNftsByOwnerRequest) Reset() { *m = QueryAllNftsByOwnerR func (m *QueryAllNftsByOwnerRequest) String() string { return proto.CompactTextString(m) } func (*QueryAllNftsByOwnerRequest) ProtoMessage() {} func (*QueryAllNftsByOwnerRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_c3d20ffbceb85197, []int{10} + return fileDescriptor_c3d20ffbceb85197, []int{8} } func (m *QueryAllNftsByOwnerRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -451,7 +377,7 @@ func (m *QueryAllNftsByOwnerResponse) Reset() { *m = QueryAllNftsByOwner func (m *QueryAllNftsByOwnerResponse) String() string { return proto.CompactTextString(m) } func (*QueryAllNftsByOwnerResponse) ProtoMessage() {} func (*QueryAllNftsByOwnerResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_c3d20ffbceb85197, []int{11} + return fileDescriptor_c3d20ffbceb85197, []int{9} } func (m *QueryAllNftsByOwnerResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -485,8 +411,6 @@ func init() { proto.RegisterType((*QueryCollectionResponse)(nil), "bitsong.nft.v1beta1.QueryCollectionResponse") proto.RegisterType((*QueryOwnerOfRequest)(nil), "bitsong.nft.v1beta1.QueryOwnerOfRequest") proto.RegisterType((*QueryOwnerOfResponse)(nil), "bitsong.nft.v1beta1.QueryOwnerOfResponse") - proto.RegisterType((*QueryNumTokensRequest)(nil), "bitsong.nft.v1beta1.QueryNumTokensRequest") - proto.RegisterType((*QueryNumTokensResponse)(nil), "bitsong.nft.v1beta1.QueryNumTokensResponse") proto.RegisterType((*QueryNftInfoRequest)(nil), "bitsong.nft.v1beta1.QueryNftInfoRequest") proto.RegisterType((*QueryNftInfoResponse)(nil), "bitsong.nft.v1beta1.QueryNftInfoResponse") proto.RegisterType((*QueryNftsRequest)(nil), "bitsong.nft.v1beta1.QueryNftsRequest") @@ -498,55 +422,51 @@ func init() { func init() { proto.RegisterFile("bitsong/nft/v1beta1/query.proto", fileDescriptor_c3d20ffbceb85197) } var fileDescriptor_c3d20ffbceb85197 = []byte{ - // 755 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x96, 0x41, 0x4f, 0x13, 0x4d, - 0x18, 0xc7, 0x3b, 0x50, 0xde, 0xc2, 0x43, 0xf2, 0xe6, 0x7d, 0x07, 0xde, 0xd7, 0xba, 0xe8, 0x96, - 0x90, 0x88, 0x58, 0x60, 0x47, 0xaa, 0x51, 0xc2, 0x41, 0xa4, 0x46, 0x0d, 0xc1, 0x80, 0x36, 0x9e, - 0xb8, 0x34, 0xdb, 0xb2, 0xbb, 0x6e, 0x6c, 0x67, 0x0a, 0x33, 0x45, 0x09, 0xe1, 0x62, 0x38, 0x70, - 0x34, 0x31, 0xf1, 0xe0, 0x89, 0x44, 0x3d, 0xe1, 0xc1, 0x8f, 0xc1, 0x91, 0xc4, 0x8b, 0x27, 0x63, - 0xc0, 0x44, 0xbf, 0x82, 0x37, 0xb3, 0xb3, 0xd3, 0xee, 0x16, 0xb6, 0xa5, 0x8d, 0x9a, 0x78, 0x6a, - 0x67, 0xe7, 0x79, 0xfe, 0xcf, 0x6f, 0xff, 0xf3, 0xcc, 0x93, 0x85, 0x54, 0xc1, 0x15, 0x9c, 0x51, - 0x87, 0x50, 0x5b, 0x90, 0xf5, 0xa9, 0x82, 0x25, 0xcc, 0x29, 0xb2, 0x5a, 0xb5, 0xd6, 0x36, 0x8c, - 0xca, 0x1a, 0x13, 0x0c, 0x0f, 0xa8, 0x00, 0x83, 0xda, 0xc2, 0x50, 0x01, 0xda, 0xa0, 0xc3, 0x1c, - 0x26, 0xf7, 0x89, 0xf7, 0xcf, 0x0f, 0xd5, 0xce, 0x39, 0x8c, 0x39, 0x25, 0x8b, 0x98, 0x15, 0x97, - 0x98, 0x94, 0x32, 0x61, 0x0a, 0x97, 0x51, 0xae, 0x76, 0x87, 0x8a, 0x8c, 0x97, 0x19, 0xf7, 0xc5, - 0xc9, 0x7a, 0x43, 0x15, 0x2d, 0xad, 0x36, 0x0b, 0x26, 0xb7, 0xea, 0x11, 0x3e, 0x4c, 0xc5, 0x74, - 0x5c, 0x2a, 0x95, 0x54, 0xec, 0xf9, 0x28, 0x64, 0x8f, 0x4e, 0x6e, 0x8f, 0x64, 0xe1, 0xff, 0x07, - 0x9e, 0xc0, 0x2d, 0x56, 0x2a, 0x59, 0x45, 0x2f, 0x2f, 0x67, 0xad, 0x56, 0x2d, 0x2e, 0xb0, 0x0e, - 0x50, 0xac, 0x3f, 0x4c, 0xa2, 0x61, 0x34, 0xd6, 0x97, 0x0b, 0x3d, 0x99, 0xe9, 0xdd, 0xd9, 0x4d, - 0xc5, 0xbe, 0xed, 0xa6, 0x62, 0x23, 0x2b, 0x70, 0xe6, 0x84, 0x06, 0xaf, 0x30, 0xca, 0x2d, 0x3c, - 0x7b, 0x42, 0xa4, 0x3f, 0x93, 0x32, 0x22, 0x4c, 0x32, 0x42, 0xc9, 0xd1, 0x55, 0x96, 0x61, 0x40, - 0x56, 0x59, 0x7a, 0x42, 0xad, 0xb5, 0x25, 0xbb, 0x4d, 0x4c, 0x7c, 0x16, 0x7a, 0x05, 0x7b, 0x6c, - 0xd1, 0xbc, 0xbb, 0x92, 0xec, 0x92, 0xbb, 0x09, 0xb9, 0x9e, 0x5f, 0x09, 0x69, 0x5f, 0x83, 0xc1, - 0x46, 0x6d, 0x85, 0x3f, 0x08, 0x3d, 0xcc, 0x7b, 0xa4, 0x74, 0xfd, 0x45, 0x28, 0x6f, 0x0e, 0xfe, - 0x93, 0x79, 0x8b, 0xd5, 0xf2, 0x43, 0x4f, 0x94, 0x77, 0x6e, 0xde, 0xb4, 0x3a, 0x80, 0x90, 0x44, - 0x50, 0xbc, 0xc8, 0xaa, 0x54, 0xc8, 0xf4, 0x78, 0xce, 0x5f, 0x44, 0x18, 0xb2, 0x68, 0x8b, 0x79, - 0x6a, 0xb3, 0x5f, 0x6a, 0xc8, 0x3d, 0x65, 0x48, 0x5d, 0x5b, 0x31, 0xa5, 0xa1, 0x9b, 0xda, 0x42, - 0x1d, 0x64, 0x32, 0xf2, 0x20, 0x17, 0x6d, 0x91, 0xf3, 0x82, 0x42, 0x6a, 0xdb, 0x08, 0xfe, 0xa9, - 0xc9, 0xb5, 0x6b, 0x11, 0xbe, 0x03, 0x10, 0x34, 0xb3, 0x24, 0xed, 0xcf, 0x8c, 0x1a, 0x7e, 0xe7, - 0x1b, 0x5e, 0xe7, 0x1b, 0xfe, 0x95, 0xa8, 0xd5, 0xbd, 0x6f, 0x3a, 0x96, 0xd2, 0xce, 0x85, 0x32, - 0x43, 0x18, 0xaf, 0x10, 0xfc, 0x1b, 0xc2, 0x50, 0xaf, 0x94, 0x81, 0x38, 0xb5, 0x05, 0x4f, 0xa2, - 0xe1, 0xee, 0x56, 0xef, 0x94, 0x8d, 0xef, 0x7f, 0x4a, 0xc5, 0x72, 0x32, 0x16, 0xdf, 0x8d, 0x60, - 0xbb, 0x78, 0x2a, 0x9b, 0x5f, 0xb0, 0x09, 0xdc, 0x36, 0x02, 0x4d, 0xc2, 0xcd, 0x95, 0x4a, 0x1e, - 0x5f, 0xd6, 0x6f, 0xc6, 0x9a, 0x5b, 0x91, 0x9d, 0xf8, 0x1b, 0x3c, 0x7a, 0x83, 0x60, 0x28, 0x12, - 0xe3, 0x8f, 0x72, 0x2b, 0xf3, 0x3d, 0x01, 0x3d, 0x12, 0x13, 0xbf, 0x46, 0x00, 0xc1, 0xec, 0xc0, - 0xe3, 0x91, 0x44, 0xd1, 0x23, 0x4e, 0x9b, 0x68, 0x2f, 0xd8, 0x27, 0x19, 0x99, 0xd9, 0xf9, 0xfa, - 0x3e, 0x8d, 0x9e, 0x7d, 0xf8, 0xf2, 0xa2, 0x8b, 0xe0, 0x49, 0x12, 0x35, 0x57, 0x83, 0xf6, 0xe5, - 0x64, 0x33, 0x58, 0x6c, 0xe1, 0x3d, 0x04, 0x09, 0x35, 0x5c, 0xf0, 0x58, 0xf3, 0xaa, 0x8d, 0xb3, - 0x4d, 0xbb, 0xd4, 0x46, 0xa4, 0x82, 0x5b, 0x08, 0xe0, 0x6e, 0xe2, 0x1b, 0x1d, 0xc1, 0x91, 0xcd, - 0xda, 0x3c, 0xd8, 0x22, 0x7e, 0x5b, 0xed, 0x21, 0xe8, 0xab, 0xcf, 0x23, 0x9c, 0x6e, 0x4e, 0x71, - 0x7c, 0xee, 0x69, 0xe3, 0x6d, 0xc5, 0x2a, 0xe6, 0xdb, 0x01, 0xf3, 0x0c, 0x9e, 0xee, 0x8c, 0x99, - 0x56, 0xcb, 0x79, 0xe1, 0xf3, 0xbd, 0x45, 0x90, 0x50, 0x73, 0xaa, 0x95, 0xb7, 0x8d, 0x63, 0xb2, - 0x95, 0xb7, 0xc7, 0x86, 0xde, 0x4f, 0x71, 0x06, 0xde, 0xe2, 0x97, 0x08, 0xe2, 0xde, 0x95, 0xc2, - 0x17, 0x5a, 0x96, 0xae, 0x7b, 0x39, 0x7a, 0x5a, 0x98, 0xc2, 0x9b, 0x0d, 0xf0, 0xae, 0xe2, 0x4c, - 0x87, 0x36, 0x7a, 0x3c, 0xef, 0x10, 0xfc, 0xdd, 0x78, 0xdd, 0x31, 0x69, 0x5e, 0x3b, 0x72, 0x3e, - 0x69, 0x97, 0xdb, 0x4f, 0x50, 0xd8, 0xd7, 0x03, 0xec, 0x09, 0x9c, 0x26, 0x4d, 0x3e, 0x53, 0x78, - 0xbe, 0xb0, 0x91, 0x97, 0x5d, 0x49, 0x36, 0xe5, 0xcf, 0x56, 0x76, 0x61, 0xff, 0x50, 0x47, 0x07, - 0x87, 0x3a, 0xfa, 0x7c, 0xa8, 0xa3, 0xe7, 0x47, 0x7a, 0xec, 0xe0, 0x48, 0x8f, 0x7d, 0x3c, 0xd2, - 0x63, 0xcb, 0x53, 0x8e, 0x2b, 0x1e, 0x55, 0x0b, 0x46, 0x91, 0x95, 0x6b, 0x7a, 0xcc, 0xb6, 0xdd, - 0xa2, 0x6b, 0x96, 0x88, 0xc3, 0x26, 0x6b, 0x25, 0x9e, 0xca, 0x22, 0x62, 0xa3, 0x62, 0xf1, 0xc2, - 0x5f, 0xf2, 0x33, 0xe8, 0xca, 0x8f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x42, 0x29, 0x52, 0xbd, 0xda, - 0x09, 0x00, 0x00, + // 689 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x95, 0xcf, 0x4f, 0x13, 0x41, + 0x14, 0xc7, 0x3b, 0xfc, 0xf6, 0x91, 0x18, 0x1d, 0x88, 0xd6, 0x45, 0xb7, 0xa4, 0x89, 0x88, 0x15, + 0x76, 0xa4, 0x1a, 0x35, 0x1c, 0x44, 0x6b, 0xd4, 0x10, 0x0c, 0x68, 0x8f, 0x5c, 0xc8, 0xb6, 0xec, + 0xae, 0x1b, 0x97, 0x99, 0xc2, 0x0c, 0x28, 0x21, 0x5c, 0x0c, 0x07, 0x8e, 0x26, 0x26, 0x1e, 0x3c, + 0x91, 0xa8, 0x27, 0x3d, 0xf8, 0x67, 0x90, 0x78, 0x21, 0xf1, 0xe2, 0xc9, 0x18, 0x30, 0xd1, 0x3f, + 0xc3, 0xec, 0xec, 0x2c, 0xbb, 0x95, 0x69, 0x29, 0x46, 0x13, 0x4f, 0xed, 0xcc, 0xbc, 0xf9, 0xbe, + 0xcf, 0xbe, 0xfd, 0xbe, 0xb7, 0x90, 0xab, 0xf8, 0x82, 0x33, 0xea, 0x11, 0xea, 0x0a, 0xb2, 0x32, + 0x56, 0x71, 0x84, 0x3d, 0x46, 0x16, 0x97, 0x9d, 0xa5, 0x55, 0xab, 0xb6, 0xc4, 0x04, 0xc3, 0x7d, + 0x2a, 0xc0, 0xa2, 0xae, 0xb0, 0x54, 0x80, 0xd1, 0xef, 0x31, 0x8f, 0xc9, 0x73, 0x12, 0xfe, 0x8b, + 0x42, 0x8d, 0xb3, 0x1e, 0x63, 0x5e, 0xe0, 0x10, 0xbb, 0xe6, 0x13, 0x9b, 0x52, 0x26, 0x6c, 0xe1, + 0x33, 0xca, 0xd5, 0xe9, 0x40, 0x95, 0xf1, 0x05, 0xc6, 0x23, 0x71, 0xb2, 0x52, 0x97, 0xc5, 0x28, + 0xa8, 0xc3, 0x8a, 0xcd, 0x9d, 0xfd, 0x88, 0x08, 0xa6, 0x66, 0x7b, 0x3e, 0x95, 0x4a, 0x2a, 0xf6, + 0x9c, 0x0e, 0x39, 0xa4, 0x93, 0xc7, 0xf9, 0x12, 0x9c, 0x7a, 0x14, 0x0a, 0xdc, 0x61, 0x41, 0xe0, + 0x54, 0xc3, 0x7b, 0x65, 0x67, 0x71, 0xd9, 0xe1, 0x02, 0x9b, 0x00, 0xd5, 0xfd, 0xcd, 0x2c, 0x1a, + 0x44, 0xc3, 0xc7, 0xca, 0xa9, 0x9d, 0xf1, 0x9e, 0xcd, 0xad, 0x5c, 0xe6, 0xe7, 0x56, 0x2e, 0x93, + 0x9f, 0x87, 0xd3, 0x07, 0x34, 0x78, 0x8d, 0x51, 0xee, 0xe0, 0x89, 0x03, 0x22, 0xbd, 0xc5, 0x9c, + 0xa5, 0x29, 0x92, 0x95, 0xba, 0xac, 0xcf, 0x32, 0x0b, 0x7d, 0x32, 0xcb, 0xcc, 0x53, 0xea, 0x2c, + 0xcd, 0xb8, 0x2d, 0x62, 0xe2, 0x33, 0xd0, 0x23, 0xd8, 0x13, 0x87, 0xce, 0xf9, 0xf3, 0xd9, 0x36, + 0x79, 0xda, 0x2d, 0xd7, 0x93, 0xf3, 0x29, 0xed, 0x6b, 0xd0, 0x5f, 0xaf, 0xad, 0xf0, 0xfb, 0xa1, + 0x93, 0x85, 0x5b, 0x4a, 0x37, 0x5a, 0x68, 0x98, 0xa6, 0x5d, 0x31, 0x49, 0x5d, 0xf6, 0x57, 0x99, + 0x1e, 0x28, 0xa6, 0x7d, 0x6d, 0xc5, 0x54, 0x80, 0x76, 0xea, 0x0a, 0x55, 0xcb, 0xac, 0xb6, 0x96, + 0xd3, 0xae, 0x28, 0x87, 0x41, 0x29, 0xb5, 0x0d, 0x04, 0x27, 0x62, 0x39, 0xde, 0x2a, 0xe7, 0x3d, + 0x80, 0xc4, 0x4f, 0x92, 0xb4, 0xb7, 0x38, 0x64, 0x45, 0xe6, 0xb3, 0x42, 0xf3, 0x59, 0x91, 0x2b, + 0xe3, 0xbc, 0x0f, 0x6d, 0xcf, 0x51, 0xda, 0xe5, 0xd4, 0xcd, 0x14, 0xc6, 0x6b, 0x04, 0x27, 0x53, + 0x18, 0xea, 0x91, 0x8a, 0xd0, 0x41, 0x5d, 0xc1, 0xb3, 0x68, 0xb0, 0xbd, 0xd9, 0x33, 0x95, 0x3a, + 0xb6, 0xbf, 0xe6, 0x32, 0x65, 0x19, 0x8b, 0xef, 0x6b, 0xd8, 0x2e, 0x1c, 0xca, 0x16, 0x25, 0x6c, + 0x00, 0xb7, 0x81, 0xc0, 0x90, 0x70, 0xb7, 0x83, 0x20, 0xe4, 0x2b, 0x45, 0x7e, 0x88, 0xab, 0xa5, + 0x35, 0xc3, 0x3f, 0xa8, 0xd1, 0x5b, 0x04, 0x03, 0x5a, 0x8c, 0xff, 0xaa, 0x5a, 0xc5, 0x4f, 0x5d, + 0xd0, 0x29, 0x31, 0xf1, 0x1b, 0x04, 0x90, 0xb4, 0x2f, 0xbe, 0xa4, 0x25, 0xd2, 0x4f, 0x19, 0x63, + 0xa4, 0xb5, 0xe0, 0x88, 0x24, 0x3f, 0xbe, 0xf9, 0xe3, 0x63, 0x01, 0x3d, 0xff, 0xfc, 0xfd, 0x65, + 0x1b, 0xc1, 0xa3, 0x44, 0x37, 0xda, 0x12, 0xfb, 0x72, 0xb2, 0x96, 0x2c, 0xd6, 0xf1, 0x7b, 0x04, + 0xdd, 0xaa, 0xbf, 0xf1, 0x70, 0xe3, 0xac, 0xf5, 0xe3, 0xc5, 0xb8, 0xd8, 0x42, 0xa4, 0x82, 0x9b, + 0x4a, 0xe0, 0x6e, 0xe1, 0x9b, 0x47, 0x82, 0x23, 0x6b, 0xf1, 0x3c, 0x58, 0x27, 0x91, 0xad, 0xde, + 0x21, 0xe8, 0x56, 0x9d, 0xdf, 0x8c, 0xb6, 0x7e, 0xf0, 0x34, 0xa3, 0xfd, 0x6d, 0x8c, 0xe4, 0xef, + 0x26, 0xb4, 0xe3, 0xf8, 0xc6, 0x9f, 0xd2, 0xe2, 0x57, 0x08, 0x3a, 0x42, 0x93, 0xe2, 0xf3, 0x4d, + 0x53, 0xc7, 0x23, 0xc7, 0x18, 0x3a, 0x2c, 0x4c, 0xe1, 0x4d, 0x24, 0x78, 0x57, 0x71, 0xf1, 0x68, + 0x78, 0xd2, 0xf1, 0x1f, 0x10, 0x1c, 0xaf, 0x6f, 0x20, 0x4c, 0x1a, 0xe7, 0xd6, 0x76, 0xbc, 0x71, + 0xb9, 0xf5, 0x0b, 0x0a, 0xfb, 0x7a, 0x82, 0x3d, 0x82, 0x0b, 0xa4, 0xc1, 0xb7, 0x97, 0xcf, 0x55, + 0x56, 0xe7, 0xe4, 0x7b, 0x26, 0x6b, 0xf2, 0x67, 0xbd, 0x34, 0xb5, 0xbd, 0x6b, 0xa2, 0x9d, 0x5d, + 0x13, 0x7d, 0xdb, 0x35, 0xd1, 0x8b, 0x3d, 0x33, 0xb3, 0xb3, 0x67, 0x66, 0xbe, 0xec, 0x99, 0x99, + 0xd9, 0x31, 0xcf, 0x17, 0x8f, 0x97, 0x2b, 0x56, 0x95, 0x2d, 0xc4, 0x7a, 0xcc, 0x75, 0xfd, 0xaa, + 0x6f, 0x07, 0xc4, 0x63, 0xa3, 0x71, 0x8a, 0x67, 0x32, 0x89, 0x58, 0xad, 0x39, 0xbc, 0xd2, 0x25, + 0xbf, 0xed, 0x57, 0x7e, 0x05, 0x00, 0x00, 0xff, 0xff, 0x77, 0x92, 0xbd, 0x82, 0xaf, 0x08, 0x00, + 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -563,7 +483,6 @@ const _ = grpc.SupportPackageIsVersion4 type QueryClient interface { Collection(ctx context.Context, in *QueryCollectionRequest, opts ...grpc.CallOption) (*QueryCollectionResponse, error) OwnerOf(ctx context.Context, in *QueryOwnerOfRequest, opts ...grpc.CallOption) (*QueryOwnerOfResponse, error) - NumTokens(ctx context.Context, in *QueryNumTokensRequest, opts ...grpc.CallOption) (*QueryNumTokensResponse, error) NftInfo(ctx context.Context, in *QueryNftInfoRequest, opts ...grpc.CallOption) (*QueryNftInfoResponse, error) Nfts(ctx context.Context, in *QueryNftsRequest, opts ...grpc.CallOption) (*QueryNftsResponse, error) AllNftsByOwner(ctx context.Context, in *QueryAllNftsByOwnerRequest, opts ...grpc.CallOption) (*QueryAllNftsByOwnerResponse, error) @@ -595,15 +514,6 @@ func (c *queryClient) OwnerOf(ctx context.Context, in *QueryOwnerOfRequest, opts return out, nil } -func (c *queryClient) NumTokens(ctx context.Context, in *QueryNumTokensRequest, opts ...grpc.CallOption) (*QueryNumTokensResponse, error) { - out := new(QueryNumTokensResponse) - err := c.cc.Invoke(ctx, "/bitsong.nft.v1beta1.Query/NumTokens", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - func (c *queryClient) NftInfo(ctx context.Context, in *QueryNftInfoRequest, opts ...grpc.CallOption) (*QueryNftInfoResponse, error) { out := new(QueryNftInfoResponse) err := c.cc.Invoke(ctx, "/bitsong.nft.v1beta1.Query/NftInfo", in, out, opts...) @@ -635,7 +545,6 @@ func (c *queryClient) AllNftsByOwner(ctx context.Context, in *QueryAllNftsByOwne type QueryServer interface { Collection(context.Context, *QueryCollectionRequest) (*QueryCollectionResponse, error) OwnerOf(context.Context, *QueryOwnerOfRequest) (*QueryOwnerOfResponse, error) - NumTokens(context.Context, *QueryNumTokensRequest) (*QueryNumTokensResponse, error) NftInfo(context.Context, *QueryNftInfoRequest) (*QueryNftInfoResponse, error) Nfts(context.Context, *QueryNftsRequest) (*QueryNftsResponse, error) AllNftsByOwner(context.Context, *QueryAllNftsByOwnerRequest) (*QueryAllNftsByOwnerResponse, error) @@ -651,9 +560,6 @@ func (*UnimplementedQueryServer) Collection(ctx context.Context, req *QueryColle func (*UnimplementedQueryServer) OwnerOf(ctx context.Context, req *QueryOwnerOfRequest) (*QueryOwnerOfResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method OwnerOf not implemented") } -func (*UnimplementedQueryServer) NumTokens(ctx context.Context, req *QueryNumTokensRequest) (*QueryNumTokensResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method NumTokens not implemented") -} func (*UnimplementedQueryServer) NftInfo(ctx context.Context, req *QueryNftInfoRequest) (*QueryNftInfoResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method NftInfo not implemented") } @@ -704,24 +610,6 @@ func _Query_OwnerOf_Handler(srv interface{}, ctx context.Context, dec func(inter return interceptor(ctx, in, info, handler) } -func _Query_NumTokens_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(QueryNumTokensRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(QueryServer).NumTokens(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/bitsong.nft.v1beta1.Query/NumTokens", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(QueryServer).NumTokens(ctx, req.(*QueryNumTokensRequest)) - } - return interceptor(ctx, in, info, handler) -} - func _Query_NftInfo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(QueryNftInfoRequest) if err := dec(in); err != nil { @@ -789,10 +677,6 @@ var _Query_serviceDesc = grpc.ServiceDesc{ MethodName: "OwnerOf", Handler: _Query_OwnerOf_Handler, }, - { - MethodName: "NumTokens", - Handler: _Query_NumTokens_Handler, - }, { MethodName: "NftInfo", Handler: _Query_NftInfo_Handler, @@ -942,64 +826,6 @@ func (m *QueryOwnerOfResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } -func (m *QueryNumTokensRequest) 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 *QueryNumTokensRequest) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *QueryNumTokensRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if len(m.Collection) > 0 { - i -= len(m.Collection) - copy(dAtA[i:], m.Collection) - i = encodeVarintQuery(dAtA, i, uint64(len(m.Collection))) - i-- - dAtA[i] = 0xa - } - return len(dAtA) - i, nil -} - -func (m *QueryNumTokensResponse) 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 *QueryNumTokensResponse) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *QueryNumTokensResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if m.Count != 0 { - i = encodeVarintQuery(dAtA, i, uint64(m.Count)) - i-- - dAtA[i] = 0x8 - } - return len(dAtA) - i, nil -} - func (m *QueryNftInfoRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -1321,31 +1147,6 @@ func (m *QueryOwnerOfResponse) Size() (n int) { return n } -func (m *QueryNumTokensRequest) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.Collection) - if l > 0 { - n += 1 + l + sovQuery(uint64(l)) - } - return n -} - -func (m *QueryNumTokensResponse) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.Count != 0 { - n += 1 + sovQuery(uint64(m.Count)) - } - return n -} - func (m *QueryNftInfoRequest) Size() (n int) { if m == nil { return 0 @@ -1818,157 +1619,6 @@ func (m *QueryOwnerOfResponse) Unmarshal(dAtA []byte) error { } return nil } -func (m *QueryNumTokensRequest) 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: QueryNumTokensRequest: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: QueryNumTokensRequest: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Collection", 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.Collection = 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 *QueryNumTokensResponse) 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: QueryNumTokensResponse: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: QueryNumTokensResponse: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Count", wireType) - } - m.Count = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowQuery - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Count |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - 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 *QueryNftInfoRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 diff --git a/x/nft/types/query.pb.gw.go b/x/nft/types/query.pb.gw.go index 6b1bea62..139ff621 100644 --- a/x/nft/types/query.pb.gw.go +++ b/x/nft/types/query.pb.gw.go @@ -163,60 +163,6 @@ func local_request_Query_OwnerOf_0(ctx context.Context, marshaler runtime.Marsha } -func request_Query_NumTokens_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq QueryNumTokensRequest - var metadata runtime.ServerMetadata - - var ( - val string - ok bool - err error - _ = err - ) - - val, ok = pathParams["collection"] - if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "collection") - } - - protoReq.Collection, err = runtime.String(val) - - if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "collection", err) - } - - msg, err := client.NumTokens(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) - return msg, metadata, err - -} - -func local_request_Query_NumTokens_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq QueryNumTokensRequest - var metadata runtime.ServerMetadata - - var ( - val string - ok bool - err error - _ = err - ) - - val, ok = pathParams["collection"] - if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "collection") - } - - protoReq.Collection, err = runtime.String(val) - - if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "collection", err) - } - - msg, err := server.NumTokens(ctx, &protoReq) - return msg, metadata, err - -} - func request_Query_NftInfo_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq QueryNftInfoRequest var metadata runtime.ServerMetadata @@ -489,29 +435,6 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv }) - mux.Handle("GET", pattern_Query_NumTokens_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_NumTokens_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_NumTokens_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) - - }) - mux.Handle("GET", pattern_Query_NftInfo_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() @@ -662,26 +585,6 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie }) - mux.Handle("GET", pattern_Query_NumTokens_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_NumTokens_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_NumTokens_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) - - }) - mux.Handle("GET", pattern_Query_NftInfo_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() @@ -750,8 +653,6 @@ var ( pattern_Query_OwnerOf_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, 2, 6}, []string{"bitsong", "nft", "v1beta1", "collections", "collection", "token_id", "owner"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_NumTokens_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5}, []string{"bitsong", "nft", "v1beta1", "collections", "collection", "num_tokens"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_NftInfo_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{"bitsong", "nft", "v1beta1", "collections", "collection", "token_id"}, "", runtime.AssumeColonVerbOpt(false))) pattern_Query_Nfts_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5}, []string{"bitsong", "nft", "v1beta1", "collections", "collection", "nfts"}, "", runtime.AssumeColonVerbOpt(false))) @@ -764,8 +665,6 @@ var ( forward_Query_OwnerOf_0 = runtime.ForwardResponseMessage - forward_Query_NumTokens_0 = runtime.ForwardResponseMessage - forward_Query_NftInfo_0 = runtime.ForwardResponseMessage forward_Query_Nfts_0 = runtime.ForwardResponseMessage From 42402ba09ddca388225ec0c258a5ed9bbe957655 Mon Sep 17 00:00:00 2001 From: angelorc Date: Thu, 4 Sep 2025 18:12:04 +0200 Subject: [PATCH 11/15] refactor(nft): update query and nft.proto --- proto/bitsong/nft/v1beta1/nft.proto | 14 +++++++------- proto/bitsong/nft/v1beta1/query.proto | 1 - 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/proto/bitsong/nft/v1beta1/nft.proto b/proto/bitsong/nft/v1beta1/nft.proto index 16db8948..bd30c6aa 100644 --- a/proto/bitsong/nft/v1beta1/nft.proto +++ b/proto/bitsong/nft/v1beta1/nft.proto @@ -16,10 +16,10 @@ message Collection { string uri = 5; string creator = 6; - string minter = 7; - uint64 num_tokens = 8; - // bool is_mutable - // update_autority (who can update name, description and uri if is_mutable = true) + string minter = 7; // who can mint new nfts, if not set no one can mint + string authority = 8; // who can update name, description and uri, if not set no one can update + + uint64 num_tokens = 9; } message Nft { @@ -33,14 +33,14 @@ message Nft { string uri = 5; string owner = 6; + string authority = 7; // who can update name, description and uri, if not set no one can update // TODO: add max_editions - uint64 editions = 7; // number of printed editions + // uint64 max_editions = 8; // max number of printed editions, 0 means no limit + uint64 editions = 8; // number of printed editions // seller_fee_bps // payment_address - // bool is_mutable - // update_autority (who can update name, description and uri if is_mutable = true) } message Edition { diff --git a/proto/bitsong/nft/v1beta1/query.proto b/proto/bitsong/nft/v1beta1/query.proto index 5c5a754f..a0f770fa 100644 --- a/proto/bitsong/nft/v1beta1/query.proto +++ b/proto/bitsong/nft/v1beta1/query.proto @@ -39,7 +39,6 @@ service Query { // Edition returns a specific edition of an nft // OwnerOfEdition returns the owner of a specific edition - // NumEditions returns the total number of editions // NftEditions returns all editions of a specific nft // AllNftEditionsByOwner returns all nft editions owned by the owner } From 4f2e29feef298f58b5f6aa6d8e05fb75e793a99b Mon Sep 17 00:00:00 2001 From: angelorc Date: Fri, 5 Sep 2025 10:40:17 +0200 Subject: [PATCH 12/15] Remove description in NFT and Collection - Removed the 'description' field from the Collection and Nft messages in nft.proto. - Updated CreateCollection and MintNFT functions to eliminate the description parameter. - Adjusted validation functions to exclude description checks. - Modified test cases to reflect the removal of description from collection and NFT creation. - Updated the protobuf generated files to align with the new structure. --- proto/bitsong/nft/v1beta1/nft.proto | 2 - x/nft/keeper/collection.go | 22 ++-- x/nft/keeper/edition_test.go | 2 - x/nft/keeper/grpc_query_test.go | 14 --- x/nft/keeper/keeper_test.go | 37 +++--- x/nft/keeper/nft.go | 21 ++-- x/nft/types/keys.go | 9 +- x/nft/types/nft.pb.go | 174 ++++++++++++++-------------- 8 files changed, 121 insertions(+), 160 deletions(-) diff --git a/proto/bitsong/nft/v1beta1/nft.proto b/proto/bitsong/nft/v1beta1/nft.proto index bd30c6aa..182dd44d 100644 --- a/proto/bitsong/nft/v1beta1/nft.proto +++ b/proto/bitsong/nft/v1beta1/nft.proto @@ -12,7 +12,6 @@ message Collection { string symbol = 2; string name = 3; - string description = 4; string uri = 5; string creator = 6; @@ -29,7 +28,6 @@ message Nft { string token_id = 2; string name = 3; - string description = 4; string uri = 5; string owner = 6; diff --git a/x/nft/keeper/collection.go b/x/nft/keeper/collection.go index 30bf1228..4fd8a17f 100644 --- a/x/nft/keeper/collection.go +++ b/x/nft/keeper/collection.go @@ -17,7 +17,6 @@ func (k Keeper) CreateCollection( minter sdk.AccAddress, symbol, name, - description, uri string, ) (denom string, err error) { denom, err = k.validateCollectionDenom(ctx, creator, symbol) @@ -27,18 +26,17 @@ func (k Keeper) CreateCollection( // TODO: charge fee - if err := k.validateCollectionMetadata(name, description, uri); err != nil { + if err := k.validateCollectionMetadata(name, uri); err != nil { return "", err } coll := types.Collection{ - Denom: denom, - Symbol: symbol, - Name: name, - Description: description, - Uri: uri, - Creator: creator.String(), - Minter: minter.String(), + Denom: denom, + Symbol: symbol, + Name: name, + Uri: uri, + Creator: creator.String(), + Minter: minter.String(), } if err := k.setCollection(ctx, coll); err != nil { @@ -136,15 +134,11 @@ func (k Keeper) getCollection(ctx context.Context, denom string) (types.Collecti return coll, nil } -func (k Keeper) validateCollectionMetadata(name, description, uri string) error { +func (k Keeper) validateCollectionMetadata(name, uri string) error { if len(name) > types.MaxNameLength { return fmt.Errorf("name cannot be longer than %d characters", types.MaxNameLength) } - if len(description) > types.MaxDescriptionLength { - return fmt.Errorf("description cannot be longer than %d characters", types.MaxDescriptionLength) - } - if len(uri) > types.MaxURILength { return fmt.Errorf("uri cannot be longer than %d characters", types.MaxURILength) } diff --git a/x/nft/keeper/edition_test.go b/x/nft/keeper/edition_test.go index c722df72..8ef70b88 100644 --- a/x/nft/keeper/edition_test.go +++ b/x/nft/keeper/edition_test.go @@ -7,7 +7,6 @@ func (suite *KeeperTestSuite) TestPrintEdition() { minter1, testCollection1.Symbol, testCollection1.Name, - testCollection1.Description, testCollection1.Uri, ) suite.NoError(err) @@ -19,7 +18,6 @@ func (suite *KeeperTestSuite) TestPrintEdition() { collectionDenom, testNft1.TokenId, testNft1.Name, - testNft1.Description, testNft1.Uri, ) suite.NoError(err) diff --git a/x/nft/keeper/grpc_query_test.go b/x/nft/keeper/grpc_query_test.go index 0c40cb85..38f79f61 100644 --- a/x/nft/keeper/grpc_query_test.go +++ b/x/nft/keeper/grpc_query_test.go @@ -11,7 +11,6 @@ func (suite *KeeperTestSuite) TestQueryCollection() { minter1, testCollection1.Symbol, testCollection1.Name, - testCollection1.Description, testCollection1.Uri, ) suite.NoError(err) @@ -22,7 +21,6 @@ func (suite *KeeperTestSuite) TestQueryCollection() { suite.NoError(err) suite.Equal(testCollection1.Name, res.Collection.Name) suite.Equal(testCollection1.Symbol, res.Collection.Symbol) - suite.Equal(testCollection1.Description, res.Collection.Description) suite.Equal(testCollection1.Uri, res.Collection.Uri) suite.Equal(testCollection1.Minter, res.Collection.Minter) } @@ -34,7 +32,6 @@ func (suite *KeeperTestSuite) TestQueryOwnerOf() { minter1, testCollection1.Symbol, testCollection1.Name, - testCollection1.Description, testCollection1.Uri, ) suite.NoError(err) @@ -46,7 +43,6 @@ func (suite *KeeperTestSuite) TestQueryOwnerOf() { collectionDenom, testNft1.TokenId, testNft1.Name, - testNft1.Description, testNft1.Uri, ) suite.NoError(err) @@ -66,7 +62,6 @@ func (suite *KeeperTestSuite) TestQueryNftInfo() { minter1, testCollection1.Symbol, testCollection1.Name, - testCollection1.Description, testCollection1.Uri, ) suite.NoError(err) @@ -78,7 +73,6 @@ func (suite *KeeperTestSuite) TestQueryNftInfo() { collectionDenom, testNft1.TokenId, testNft1.Name, - testNft1.Description, testNft1.Uri, ) suite.NoError(err) @@ -90,7 +84,6 @@ func (suite *KeeperTestSuite) TestQueryNftInfo() { suite.NoError(err) suite.Equal(testNft1.TokenId, res.Nft.TokenId) suite.Equal(testNft1.Name, res.Nft.Name) - suite.Equal(testNft1.Description, res.Nft.Description) suite.Equal(testNft1.Uri, res.Nft.Uri) suite.Equal(collectionDenom, res.Nft.Collection) suite.Equal(owner1.String(), res.Nft.Owner) @@ -103,7 +96,6 @@ func (suite *KeeperTestSuite) TestQueryNftsOfOwner() { minter1, testCollection1.Symbol, testCollection1.Name, - testCollection1.Description, testCollection1.Uri, ) suite.NoError(err) @@ -115,7 +107,6 @@ func (suite *KeeperTestSuite) TestQueryNftsOfOwner() { collectionDenom, testNft1.TokenId, testNft1.Name, - testNft1.Description, testNft1.Uri, ) suite.NoError(err) @@ -127,7 +118,6 @@ func (suite *KeeperTestSuite) TestQueryNftsOfOwner() { collectionDenom, testNft2.TokenId, testNft2.Name, - testNft2.Description, testNft2.Uri, ) suite.NoError(err) @@ -148,7 +138,6 @@ func (suite *KeeperTestSuite) TestQueryNftsByOwner() { minter1, testCollection1.Symbol, testCollection1.Name, - testCollection1.Description, testCollection1.Uri, ) suite.NoError(err) @@ -160,7 +149,6 @@ func (suite *KeeperTestSuite) TestQueryNftsByOwner() { collectionDenom, testNft1.TokenId, testNft1.Name, - testNft1.Description, testNft1.Uri, ) suite.NoError(err) @@ -172,7 +160,6 @@ func (suite *KeeperTestSuite) TestQueryNftsByOwner() { collectionDenom, testNft2.TokenId, testNft2.Name, - testNft2.Description, testNft2.Uri, ) suite.NoError(err) @@ -200,7 +187,6 @@ func (suite *KeeperTestSuite) TestQueryNftsByOwner() { collectionDenom, testNft3.TokenId, testNft3.Name, - testNft3.Description, testNft3.Uri, ) suite.NoError(err) diff --git a/x/nft/keeper/keeper_test.go b/x/nft/keeper/keeper_test.go index c129f2b3..2245ff02 100644 --- a/x/nft/keeper/keeper_test.go +++ b/x/nft/keeper/keeper_test.go @@ -25,33 +25,29 @@ var ( owner2 = sdk.AccAddress(tmhash.SumTruncated([]byte("owner2"))) testCollection1 = types.Collection{ - Name: "My NFT Collection", - Symbol: "MYNFT", - Description: "My NFT Collection Description", - Uri: "ipfs://my-nft-collection-metadata.json", - Minter: minter1.String(), + Name: "My NFT Collection", + Symbol: "MYNFT", + Uri: "ipfs://my-nft-collection-metadata.json", + Minter: minter1.String(), } expectedDenom1 = "nft9436DDD23FB751AEA7BC6C767F20F943DD735E06" testNft1 = types.Nft{ - TokenId: "1", - Name: "My First NFT", - Description: "This is my first NFT", - Uri: "ipfs://my-first-nft-metadata.json", + TokenId: "1", + Name: "My First NFT", + Uri: "ipfs://my-first-nft-metadata.json", } testNft2 = types.Nft{ - TokenId: "2", - Name: "My Second NFT", - Description: "This is my second NFT", - Uri: "ipfs://my-second-nft-metadata.json", + TokenId: "2", + Name: "My Second NFT", + Uri: "ipfs://my-second-nft-metadata.json", } testNft3 = types.Nft{ - TokenId: "3", - Name: "My Third NFT", - Description: "This is my third NFT", - Uri: "ipfs://my-third-nft-metadata.json", + TokenId: "3", + Name: "My Third NFT", + Uri: "ipfs://my-third-nft-metadata.json", } ) @@ -83,7 +79,6 @@ func (suite *KeeperTestSuite) TestCreateCollection() { minter1, testCollection1.Symbol, testCollection1.Name, - testCollection1.Description, testCollection1.Uri, ) suite.NoError(err) @@ -95,7 +90,6 @@ func (suite *KeeperTestSuite) TestCreateCollection() { minter1, testCollection1.Symbol, testCollection1.Name, - testCollection1.Description, testCollection1.Uri, ) suite.Error(err) @@ -108,7 +102,6 @@ func (suite *KeeperTestSuite) TestMintNFT() { minter1, testCollection1.Symbol, testCollection1.Name, - testCollection1.Description, testCollection1.Uri, ) suite.NoError(err) @@ -124,7 +117,6 @@ func (suite *KeeperTestSuite) TestMintNFT() { collectionDenom, testNft1.TokenId, testNft1.Name, - testNft1.Description, testNft1.Uri, ) suite.NoError(err) @@ -139,7 +131,6 @@ func (suite *KeeperTestSuite) TestMintNFT() { collectionDenom, testNft2.TokenId, testNft2.Name, - testNft2.Description, testNft2.Uri, ) suite.NoError(err) @@ -155,7 +146,6 @@ func (suite *KeeperTestSuite) TestSendNFT() { minter1, testCollection1.Symbol, testCollection1.Name, - testCollection1.Description, testCollection1.Uri, ) suite.NoError(err) @@ -167,7 +157,6 @@ func (suite *KeeperTestSuite) TestSendNFT() { collectionDenom, testNft1.TokenId, testNft1.Name, - testNft1.Description, testNft1.Uri, ) suite.NoError(err) diff --git a/x/nft/keeper/nft.go b/x/nft/keeper/nft.go index 6e9a2d51..4a004dd5 100644 --- a/x/nft/keeper/nft.go +++ b/x/nft/keeper/nft.go @@ -18,10 +18,9 @@ func (k Keeper) MintNFT( collectionDenom, tokenId, name, - description, uri string, ) error { - if err := k.validateNftMetadata(tokenId, name, description, uri); err != nil { + if err := k.validateNftMetadata(tokenId, name, uri); err != nil { return err } @@ -55,13 +54,12 @@ func (k Keeper) MintNFT( // TODO: Charge fee if necessary nft := types.Nft{ - Collection: collectionDenom, - TokenId: tokenId, - Name: name, - Description: description, - Uri: uri, - Owner: owner.String(), - Editions: 0, + Collection: collectionDenom, + TokenId: tokenId, + Name: name, + Uri: uri, + Owner: owner.String(), + Editions: 0, } if err := k.setNft(ctx, nft); err != nil { @@ -129,7 +127,7 @@ func (k Keeper) GetNft(ctx context.Context, collectionDenom, tokenId string) (*t return &nft, nil } -func (k Keeper) validateNftMetadata(tokenId, name, description, uri string) error { +func (k Keeper) validateNftMetadata(tokenId, name, uri string) error { if strings.TrimSpace(tokenId) == "" { return fmt.Errorf("token ID cannot be empty") } @@ -142,9 +140,6 @@ func (k Keeper) validateNftMetadata(tokenId, name, description, uri string) erro return fmt.Errorf("name length exceeds maximum of %d", types.MaxNameLength) } - if len(description) > types.MaxDescriptionLength { - return fmt.Errorf("description length exceeds maximum of %d", types.MaxDescriptionLength) - } if len(uri) > types.MaxURILength { return fmt.Errorf("URI length exceeds maximum of %d", types.MaxURILength) diff --git a/x/nft/types/keys.go b/x/nft/types/keys.go index 3c492015..242d654a 100644 --- a/x/nft/types/keys.go +++ b/x/nft/types/keys.go @@ -16,11 +16,10 @@ const ( MaxDenomLength = 43 - MaxTokenIdLength = 20 - MaxSymbolLength = 15 - MaxNameLength = 128 - MaxDescriptionLength = 256 - MaxURILength = 150 + MaxTokenIdLength = 20 + MaxSymbolLength = 15 + MaxNameLength = 128 + MaxURILength = 150 ) var ( diff --git a/x/nft/types/nft.pb.go b/x/nft/types/nft.pb.go index c6bcf8f1..358d7a92 100644 --- a/x/nft/types/nft.pb.go +++ b/x/nft/types/nft.pb.go @@ -24,14 +24,14 @@ var _ = math.Inf const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package type Collection struct { - Denom string `protobuf:"bytes,1,opt,name=denom,proto3" json:"denom,omitempty"` - Symbol string `protobuf:"bytes,2,opt,name=symbol,proto3" json:"symbol,omitempty"` - Name string `protobuf:"bytes,3,opt,name=name,proto3" json:"name,omitempty"` - Description string `protobuf:"bytes,4,opt,name=description,proto3" json:"description,omitempty"` - Uri string `protobuf:"bytes,5,opt,name=uri,proto3" json:"uri,omitempty"` - Creator string `protobuf:"bytes,6,opt,name=creator,proto3" json:"creator,omitempty"` - Minter string `protobuf:"bytes,7,opt,name=minter,proto3" json:"minter,omitempty"` - NumTokens uint64 `protobuf:"varint,8,opt,name=num_tokens,json=numTokens,proto3" json:"num_tokens,omitempty"` + Denom string `protobuf:"bytes,1,opt,name=denom,proto3" json:"denom,omitempty"` + Symbol string `protobuf:"bytes,2,opt,name=symbol,proto3" json:"symbol,omitempty"` + Name string `protobuf:"bytes,3,opt,name=name,proto3" json:"name,omitempty"` + Uri string `protobuf:"bytes,5,opt,name=uri,proto3" json:"uri,omitempty"` + Creator string `protobuf:"bytes,6,opt,name=creator,proto3" json:"creator,omitempty"` + Minter string `protobuf:"bytes,7,opt,name=minter,proto3" json:"minter,omitempty"` + Authority string `protobuf:"bytes,8,opt,name=authority,proto3" json:"authority,omitempty"` + NumTokens uint64 `protobuf:"varint,9,opt,name=num_tokens,json=numTokens,proto3" json:"num_tokens,omitempty"` } func (m *Collection) Reset() { *m = Collection{} } @@ -68,14 +68,15 @@ func (m *Collection) XXX_DiscardUnknown() { var xxx_messageInfo_Collection proto.InternalMessageInfo type Nft struct { - Collection string `protobuf:"bytes,1,opt,name=collection,proto3" json:"collection,omitempty"` - TokenId string `protobuf:"bytes,2,opt,name=token_id,json=tokenId,proto3" json:"token_id,omitempty"` - Name string `protobuf:"bytes,3,opt,name=name,proto3" json:"name,omitempty"` - Description string `protobuf:"bytes,4,opt,name=description,proto3" json:"description,omitempty"` - Uri string `protobuf:"bytes,5,opt,name=uri,proto3" json:"uri,omitempty"` - Owner string `protobuf:"bytes,6,opt,name=owner,proto3" json:"owner,omitempty"` + Collection string `protobuf:"bytes,1,opt,name=collection,proto3" json:"collection,omitempty"` + TokenId string `protobuf:"bytes,2,opt,name=token_id,json=tokenId,proto3" json:"token_id,omitempty"` + Name string `protobuf:"bytes,3,opt,name=name,proto3" json:"name,omitempty"` + Uri string `protobuf:"bytes,5,opt,name=uri,proto3" json:"uri,omitempty"` + Owner string `protobuf:"bytes,6,opt,name=owner,proto3" json:"owner,omitempty"` + Authority string `protobuf:"bytes,7,opt,name=authority,proto3" json:"authority,omitempty"` // TODO: add max_editions - Editions uint64 `protobuf:"varint,7,opt,name=editions,proto3" json:"editions,omitempty"` + // uint64 max_editions = 8; // max number of printed editions, 0 means no limit + Editions uint64 `protobuf:"varint,8,opt,name=editions,proto3" json:"editions,omitempty"` } func (m *Nft) Reset() { *m = Nft{} } @@ -160,31 +161,32 @@ func init() { func init() { proto.RegisterFile("bitsong/nft/v1beta1/nft.proto", fileDescriptor_51b0314c164430ab) } var fileDescriptor_51b0314c164430ab = []byte{ - // 381 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x92, 0xbd, 0x6e, 0xdb, 0x30, - 0x10, 0xc7, 0xc5, 0x4a, 0xb6, 0xec, 0xeb, 0x62, 0xb0, 0x46, 0xc1, 0x1a, 0xb0, 0x6a, 0x78, 0xf2, - 0x52, 0x0b, 0x46, 0xb7, 0x8e, 0x2d, 0x3a, 0x14, 0x05, 0x32, 0x08, 0x99, 0xb2, 0x18, 0xfa, 0xa0, - 0x14, 0x22, 0x12, 0xcf, 0x91, 0xa8, 0x24, 0x7e, 0x83, 0x8c, 0x79, 0x84, 0x3c, 0x4a, 0xc6, 0x8c, - 0x1e, 0x93, 0x2d, 0xb0, 0x5f, 0x24, 0x20, 0x25, 0x3b, 0xde, 0x93, 0xed, 0xff, 0x21, 0xf1, 0xee, - 0x07, 0x1c, 0x8c, 0x23, 0xa1, 0x2a, 0x94, 0x99, 0x2f, 0x53, 0xe5, 0x5f, 0x2d, 0x22, 0xae, 0xc2, - 0x85, 0xd6, 0xf3, 0x55, 0x89, 0x0a, 0xe9, 0x97, 0xb6, 0x9e, 0xeb, 0xa8, 0xad, 0x47, 0xc3, 0x0c, - 0x33, 0x34, 0xbd, 0xaf, 0x55, 0xf3, 0xe9, 0xf4, 0x99, 0x00, 0xfc, 0xc1, 0x3c, 0xe7, 0xb1, 0x12, - 0x28, 0xe9, 0x10, 0x3a, 0x09, 0x97, 0x58, 0x30, 0x32, 0x21, 0xb3, 0x7e, 0xd0, 0x18, 0xfa, 0x15, - 0xba, 0xd5, 0xba, 0x88, 0x30, 0x67, 0x9f, 0x4c, 0xdc, 0x3a, 0x4a, 0xc1, 0x91, 0x61, 0xc1, 0x99, - 0x6d, 0x52, 0xa3, 0xe9, 0x04, 0x3e, 0x27, 0xbc, 0x8a, 0x4b, 0xb1, 0xd2, 0x0f, 0x32, 0xc7, 0x54, - 0xc7, 0x11, 0x1d, 0x80, 0x5d, 0x97, 0x82, 0x75, 0x4c, 0xa3, 0x25, 0x65, 0xe0, 0xc6, 0x25, 0x0f, - 0x15, 0x96, 0xac, 0x6b, 0xd2, 0xbd, 0xd5, 0x93, 0x0b, 0x21, 0x15, 0x2f, 0x99, 0xdb, 0x4c, 0x6e, - 0x1c, 0x1d, 0x03, 0xc8, 0xba, 0x58, 0x2a, 0xbc, 0xe0, 0xb2, 0x62, 0xbd, 0x09, 0x99, 0x39, 0x41, - 0x5f, 0xd6, 0xc5, 0xa9, 0x09, 0x7e, 0x39, 0xb7, 0xf7, 0xdf, 0xad, 0xe9, 0x03, 0x01, 0xfb, 0x24, - 0x55, 0xd4, 0x03, 0x88, 0x0f, 0x88, 0x2d, 0xd9, 0x51, 0x42, 0xbf, 0x41, 0xcf, 0x3c, 0xb4, 0x14, - 0x49, 0x0b, 0xe8, 0x1a, 0xff, 0x2f, 0xf9, 0x30, 0xc2, 0x21, 0x74, 0xf0, 0x5a, 0xf2, 0x3d, 0x5f, - 0x63, 0xe8, 0x08, 0x7a, 0x3c, 0x11, 0xfa, 0x97, 0xca, 0xf0, 0x39, 0xc1, 0xc1, 0xb7, 0x08, 0x25, - 0xb8, 0x7f, 0x9b, 0xe4, 0x3d, 0x14, 0x03, 0xb0, 0x2b, 0x7e, 0x69, 0x20, 0x9c, 0x40, 0xcb, 0xb7, - 0x7d, 0x9c, 0xa3, 0x7d, 0x9a, 0x99, 0xbf, 0xff, 0x3f, 0x6e, 0x3d, 0xb2, 0xd9, 0x7a, 0xe4, 0x65, - 0xeb, 0x91, 0xbb, 0x9d, 0x67, 0x6d, 0x76, 0x9e, 0xf5, 0xb4, 0xf3, 0xac, 0xb3, 0x45, 0x26, 0xd4, - 0x79, 0x1d, 0xcd, 0x63, 0x2c, 0xfc, 0xf6, 0xc4, 0x30, 0x4d, 0x45, 0x2c, 0xc2, 0xdc, 0xcf, 0xf0, - 0xc7, 0xfe, 0x28, 0x6f, 0xcc, 0x59, 0xaa, 0xf5, 0x8a, 0x57, 0x51, 0xd7, 0x9c, 0xd9, 0xcf, 0xd7, - 0x00, 0x00, 0x00, 0xff, 0xff, 0xd2, 0xcf, 0x86, 0x6c, 0xb2, 0x02, 0x00, 0x00, + // 388 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x92, 0xbd, 0x6e, 0xdb, 0x30, + 0x10, 0xc7, 0xc5, 0x4a, 0xb6, 0xac, 0x9b, 0x0c, 0xd6, 0x28, 0x58, 0xa3, 0x56, 0x0d, 0x4f, 0x5e, + 0x6a, 0xc1, 0xe8, 0xd6, 0xb1, 0x45, 0x87, 0xa2, 0x40, 0x07, 0xa1, 0x53, 0x17, 0x43, 0x1f, 0x94, + 0x4c, 0x54, 0xe2, 0x39, 0x12, 0x95, 0xc4, 0x6f, 0x90, 0x31, 0x8f, 0x90, 0x07, 0xc9, 0x03, 0x64, + 0xf4, 0x12, 0x20, 0x63, 0x60, 0xbf, 0x48, 0x20, 0x4a, 0xfe, 0x48, 0xa6, 0x00, 0xd9, 0xee, 0xff, + 0x3b, 0xe2, 0x78, 0x3f, 0xe0, 0x60, 0x14, 0x0a, 0x55, 0xa2, 0x4c, 0x3d, 0x99, 0x28, 0xef, 0x7c, + 0x1e, 0x72, 0x15, 0xcc, 0xeb, 0x7a, 0xb6, 0x2a, 0x50, 0x21, 0x7d, 0xdf, 0xb6, 0x67, 0x35, 0x6a, + 0xdb, 0xc3, 0x41, 0x8a, 0x29, 0xea, 0xbe, 0x57, 0x57, 0xcd, 0xd3, 0xc9, 0x3d, 0x01, 0xf8, 0x81, + 0x59, 0xc6, 0x23, 0x25, 0x50, 0xd2, 0x01, 0x74, 0x62, 0x2e, 0x31, 0x67, 0x64, 0x4c, 0xa6, 0x8e, + 0xdf, 0x04, 0xfa, 0x01, 0xba, 0xe5, 0x3a, 0x0f, 0x31, 0x63, 0xef, 0x34, 0x6e, 0x13, 0xa5, 0x60, + 0xc9, 0x20, 0xe7, 0xcc, 0xd4, 0x54, 0xd7, 0xb4, 0x0f, 0x66, 0x55, 0x08, 0xd6, 0xd1, 0xa8, 0x2e, + 0x29, 0x03, 0x3b, 0x2a, 0x78, 0xa0, 0xb0, 0x60, 0x5d, 0x4d, 0xf7, 0xb1, 0x9e, 0x9b, 0x0b, 0xa9, + 0x78, 0xc1, 0xec, 0x66, 0x6e, 0x93, 0xe8, 0x27, 0x70, 0x82, 0x4a, 0x2d, 0xb1, 0x10, 0x6a, 0xcd, + 0x7a, 0xba, 0x75, 0x04, 0x74, 0x04, 0x20, 0xab, 0x7c, 0xa1, 0xf0, 0x3f, 0x97, 0x25, 0x73, 0xc6, + 0x64, 0x6a, 0xf9, 0x8e, 0xac, 0xf2, 0xbf, 0x1a, 0x7c, 0xb3, 0xae, 0x6e, 0x3e, 0x1b, 0x93, 0x5b, + 0x02, 0xe6, 0x9f, 0x44, 0x51, 0x17, 0x20, 0x3a, 0xe8, 0xb5, 0x56, 0x27, 0x84, 0x7e, 0x84, 0x9e, + 0x1e, 0xb4, 0x10, 0x71, 0x2b, 0x67, 0xeb, 0xfc, 0x2b, 0x7e, 0xa5, 0xdd, 0x00, 0x3a, 0x78, 0x21, + 0xf9, 0xde, 0xad, 0x09, 0xcf, 0x0d, 0xec, 0x97, 0x06, 0x43, 0xe8, 0xf1, 0x58, 0xd4, 0xff, 0x97, + 0x5a, 0xcf, 0xf2, 0x0f, 0xb9, 0x5d, 0xbf, 0x00, 0xfb, 0x67, 0x43, 0xde, 0x62, 0xd0, 0x07, 0xb3, + 0xe4, 0x67, 0x5a, 0xc0, 0xf2, 0xeb, 0xf2, 0xb8, 0xad, 0x75, 0xb2, 0x6d, 0xf3, 0xe7, 0xf7, 0xdf, + 0x77, 0x5b, 0x97, 0x6c, 0xb6, 0x2e, 0x79, 0xdc, 0xba, 0xe4, 0x7a, 0xe7, 0x1a, 0x9b, 0x9d, 0x6b, + 0x3c, 0xec, 0x5c, 0xe3, 0xdf, 0x3c, 0x15, 0x6a, 0x59, 0x85, 0xb3, 0x08, 0x73, 0xaf, 0x3d, 0x2d, + 0x4c, 0x12, 0x11, 0x89, 0x20, 0xf3, 0x52, 0xfc, 0xb2, 0x3f, 0xc6, 0x4b, 0x7d, 0x8e, 0x6a, 0xbd, + 0xe2, 0x65, 0xd8, 0xd5, 0xe7, 0xf5, 0xf5, 0x29, 0x00, 0x00, 0xff, 0xff, 0x3a, 0xb6, 0xab, 0x47, + 0xaa, 0x02, 0x00, 0x00, } func (m *Collection) Marshal() (dAtA []byte, err error) { @@ -210,7 +212,14 @@ func (m *Collection) MarshalToSizedBuffer(dAtA []byte) (int, error) { if m.NumTokens != 0 { i = encodeVarintNft(dAtA, i, uint64(m.NumTokens)) i-- - dAtA[i] = 0x40 + dAtA[i] = 0x48 + } + if len(m.Authority) > 0 { + i -= len(m.Authority) + copy(dAtA[i:], m.Authority) + i = encodeVarintNft(dAtA, i, uint64(len(m.Authority))) + i-- + dAtA[i] = 0x42 } if len(m.Minter) > 0 { i -= len(m.Minter) @@ -233,13 +242,6 @@ func (m *Collection) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x2a } - if len(m.Description) > 0 { - i -= len(m.Description) - copy(dAtA[i:], m.Description) - i = encodeVarintNft(dAtA, i, uint64(len(m.Description))) - i-- - dAtA[i] = 0x22 - } if len(m.Name) > 0 { i -= len(m.Name) copy(dAtA[i:], m.Name) @@ -287,7 +289,14 @@ func (m *Nft) MarshalToSizedBuffer(dAtA []byte) (int, error) { if m.Editions != 0 { i = encodeVarintNft(dAtA, i, uint64(m.Editions)) i-- - dAtA[i] = 0x38 + dAtA[i] = 0x40 + } + if len(m.Authority) > 0 { + i -= len(m.Authority) + copy(dAtA[i:], m.Authority) + i = encodeVarintNft(dAtA, i, uint64(len(m.Authority))) + i-- + dAtA[i] = 0x3a } if len(m.Owner) > 0 { i -= len(m.Owner) @@ -303,13 +312,6 @@ func (m *Nft) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x2a } - if len(m.Description) > 0 { - i -= len(m.Description) - copy(dAtA[i:], m.Description) - i = encodeVarintNft(dAtA, i, uint64(len(m.Description))) - i-- - dAtA[i] = 0x22 - } if len(m.Name) > 0 { i -= len(m.Name) copy(dAtA[i:], m.Name) @@ -412,10 +414,6 @@ func (m *Collection) Size() (n int) { if l > 0 { n += 1 + l + sovNft(uint64(l)) } - l = len(m.Description) - if l > 0 { - n += 1 + l + sovNft(uint64(l)) - } l = len(m.Uri) if l > 0 { n += 1 + l + sovNft(uint64(l)) @@ -428,6 +426,10 @@ func (m *Collection) Size() (n int) { if l > 0 { n += 1 + l + sovNft(uint64(l)) } + l = len(m.Authority) + if l > 0 { + n += 1 + l + sovNft(uint64(l)) + } if m.NumTokens != 0 { n += 1 + sovNft(uint64(m.NumTokens)) } @@ -452,15 +454,15 @@ func (m *Nft) Size() (n int) { if l > 0 { n += 1 + l + sovNft(uint64(l)) } - l = len(m.Description) + l = len(m.Uri) if l > 0 { n += 1 + l + sovNft(uint64(l)) } - l = len(m.Uri) + l = len(m.Owner) if l > 0 { n += 1 + l + sovNft(uint64(l)) } - l = len(m.Owner) + l = len(m.Authority) if l > 0 { n += 1 + l + sovNft(uint64(l)) } @@ -625,9 +627,9 @@ func (m *Collection) Unmarshal(dAtA []byte) error { } m.Name = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 4: + case 5: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Description", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Uri", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -655,11 +657,11 @@ func (m *Collection) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Description = string(dAtA[iNdEx:postIndex]) + m.Uri = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 5: + case 6: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Uri", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Creator", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -687,11 +689,11 @@ func (m *Collection) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Uri = string(dAtA[iNdEx:postIndex]) + m.Creator = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 6: + case 7: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Creator", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Minter", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -719,11 +721,11 @@ func (m *Collection) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Creator = string(dAtA[iNdEx:postIndex]) + m.Minter = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 7: + case 8: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Minter", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Authority", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -751,9 +753,9 @@ func (m *Collection) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Minter = string(dAtA[iNdEx:postIndex]) + m.Authority = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 8: + case 9: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field NumTokens", wireType) } @@ -918,9 +920,9 @@ func (m *Nft) Unmarshal(dAtA []byte) error { } m.Name = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 4: + case 5: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Description", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Uri", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -948,11 +950,11 @@ func (m *Nft) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Description = string(dAtA[iNdEx:postIndex]) + m.Uri = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 5: + case 6: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Uri", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Owner", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -980,11 +982,11 @@ func (m *Nft) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Uri = string(dAtA[iNdEx:postIndex]) + m.Owner = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 6: + case 7: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Owner", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Authority", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -1012,9 +1014,9 @@ func (m *Nft) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Owner = string(dAtA[iNdEx:postIndex]) + m.Authority = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 7: + case 8: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Editions", wireType) } From abae283de98f4e41aac36279048d6257f83c3a1a Mon Sep 17 00:00:00 2001 From: angelorc Date: Fri, 5 Sep 2025 12:42:43 +0200 Subject: [PATCH 13/15] Add MsgServer --- proto/bitsong/nft/v1beta1/nft.proto | 4 +- proto/bitsong/nft/v1beta1/tx.proto | 95 + third_party/proto/amino/amino.proto | 84 + third_party/proto/cosmos/msg/v1/msg.proto | 30 + third_party/proto/cosmos_proto/cosmos.proto | 104 +- x/nft/keeper/collection.go | 2 +- x/nft/keeper/edition.go | 4 + x/nft/keeper/keeper.go | 3 + x/nft/keeper/keeper_test.go | 9 +- x/nft/keeper/msg_server.go | 119 + x/nft/keeper/msg_server_test.go | 527 ++++ x/nft/keeper/nft.go | 14 +- x/nft/types/keys.go | 4 + x/nft/types/nft.pb.go | 99 +- x/nft/types/tx.pb.go | 2495 +++++++++++++++++++ 15 files changed, 3510 insertions(+), 83 deletions(-) create mode 100644 proto/bitsong/nft/v1beta1/tx.proto create mode 100644 third_party/proto/amino/amino.proto create mode 100644 third_party/proto/cosmos/msg/v1/msg.proto create mode 100644 x/nft/keeper/msg_server.go create mode 100644 x/nft/keeper/msg_server_test.go create mode 100644 x/nft/types/tx.pb.go diff --git a/proto/bitsong/nft/v1beta1/nft.proto b/proto/bitsong/nft/v1beta1/nft.proto index 182dd44d..ae591b22 100644 --- a/proto/bitsong/nft/v1beta1/nft.proto +++ b/proto/bitsong/nft/v1beta1/nft.proto @@ -31,11 +31,11 @@ message Nft { string uri = 5; string owner = 6; - string authority = 7; // who can update name, description and uri, if not set no one can update + // string authority = 7; // who can update name, description and uri, if not set no one can update // TODO: add max_editions // uint64 max_editions = 8; // max number of printed editions, 0 means no limit - uint64 editions = 8; // number of printed editions + uint64 editions = 7; // number of printed editions // seller_fee_bps // payment_address diff --git a/proto/bitsong/nft/v1beta1/tx.proto b/proto/bitsong/nft/v1beta1/tx.proto new file mode 100644 index 00000000..07b9c6d5 --- /dev/null +++ b/proto/bitsong/nft/v1beta1/tx.proto @@ -0,0 +1,95 @@ +syntax = "proto3";; +package bitsong.nft.v1beta1; + +import "gogoproto/gogo.proto"; +import "cosmos/msg/v1/msg.proto"; +import "amino/amino.proto"; +import "cosmos_proto/cosmos.proto"; + +option go_package = "github.com/bitsongofficial/go-bitsong/x/nft/types"; + +service Msg { + option (cosmos.msg.v1.service) = true; + + rpc CreateCollection(MsgCreateCollection) returns (MsgCreateCollectionResponse); + rpc MintNFT(MsgMintNFT) returns (MsgMintNFTResponse); + rpc SendNFT(MsgSendNFT) returns (MsgSendNFTResponse); + rpc PrintEdition(MsgPrintEdition) returns (MsgPrintEditionResponse); +} + +message MsgCreateCollection { + option (cosmos.msg.v1.signer) = "creator"; + option (amino.name) = "cosmos-sdk/nft/MsgCreateCollection"; + + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + string symbol = 1; + string name = 2; + string uri = 3; + + string creator = 4 [(cosmos_proto.scalar) = "cosmos.AddressString"]; + string minter = 5 [(cosmos_proto.scalar) = "cosmos.AddressString"]; + string authority = 6 [(cosmos_proto.scalar) = "cosmos.AddressString"]; +} + +message MsgCreateCollectionResponse { + string denom = 1; +} + +message MsgMintNFT { + option (cosmos.msg.v1.signer) = "minter"; + option (amino.name) = "cosmos-sdk/nft/MsgMintNFT"; + + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + string collection = 1; + string token_id = 2; + string name = 3; + string uri = 4; + + string minter = 5 [(cosmos_proto.scalar) = "cosmos.AddressString"]; + string recipient = 7 [(cosmos_proto.scalar) = "cosmos.AddressString"]; +} + +message MsgMintNFTResponse { + string collection = 1; + string token_id = 2; +} + +message MsgSendNFT { + option (cosmos.msg.v1.signer) = "sender"; + option (amino.name) = "cosmos-sdk/nft/MsgSendNFT"; + + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + string collection = 1; + string token_id = 2; + + string sender = 3 [(cosmos_proto.scalar) = "cosmos.AddressString"]; + string recipient = 4 [(cosmos_proto.scalar) = "cosmos.AddressString"]; +} + +message MsgSendNFTResponse {} + +message MsgPrintEdition { + option (cosmos.msg.v1.signer) = "minter"; + option (amino.name) = "cosmos-sdk/nft/MsgPrintEdition"; + + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + string collection = 1; + string token_id = 2; + + string minter = 5 [(cosmos_proto.scalar) = "cosmos.AddressString"]; + string recipient = 7 [(cosmos_proto.scalar) = "cosmos.AddressString"]; +} + +message MsgPrintEditionResponse { + string collection = 1; + string token_id = 2; + uint64 seq = 3; +} \ No newline at end of file diff --git a/third_party/proto/amino/amino.proto b/third_party/proto/amino/amino.proto new file mode 100644 index 00000000..fb099b8e --- /dev/null +++ b/third_party/proto/amino/amino.proto @@ -0,0 +1,84 @@ +syntax = "proto3"; + +package amino; + +import "google/protobuf/descriptor.proto"; + +// TODO(fdymylja): once we fully migrate to protov2 the go_package needs to be updated. +// We need this right now because gogoproto codegen needs to import the extension. +option go_package = "github.com/cosmos/cosmos-sdk/types/tx/amino"; + +extend google.protobuf.MessageOptions { + // name is the string used when registering a concrete + // type into the Amino type registry, via the Amino codec's + // `RegisterConcrete()` method. This string MUST be at most 39 + // characters long, or else the message will be rejected by the + // Ledger hardware device. + string name = 11110001; + + // encoding describes the encoding format used by Amino for the given + // message. The field type is chosen to be a string for + // flexibility, but it should ideally be short and expected to be + // machine-readable, for example "base64" or "utf8_json". We + // highly recommend to use underscores for word separation instead of spaces. + // + // If left empty, then the Amino encoding is expected to be the same as the + // Protobuf one. + // + // This annotation should not be confused with the `encoding` + // one which operates on the field level. + string message_encoding = 11110002; +} + +extend google.protobuf.FieldOptions { + // encoding describes the encoding format used by Amino for + // the given field. The field type is chosen to be a string for + // flexibility, but it should ideally be short and expected to be + // machine-readable, for example "base64" or "utf8_json". We + // highly recommend to use underscores for word separation instead of spaces. + // + // If left empty, then the Amino encoding is expected to be the same as the + // Protobuf one. + // + // This annotation should not be confused with the + // `message_encoding` one which operates on the message level. + string encoding = 11110003; + + // field_name sets a different field name (i.e. key name) in + // the amino JSON object for the given field. + // + // Example: + // + // message Foo { + // string bar = 1 [(amino.field_name) = "baz"]; + // } + // + // Then the Amino encoding of Foo will be: + // `{"baz":"some value"}` + string field_name = 11110004; + + // dont_omitempty sets the field in the JSON object even if + // its value is empty, i.e. equal to the Golang zero value. To learn what + // the zero values are, see https://go.dev/ref/spec#The_zero_value. + // + // Fields default to `omitempty`, which is the default behavior when this + // annotation is unset. When set to true, then the field value in the + // JSON object will be set, i.e. not `undefined`. + // + // Example: + // + // message Foo { + // string bar = 1; + // string baz = 2 [(amino.dont_omitempty) = true]; + // } + // + // f := Foo{}; + // out := AminoJSONEncoder(&f); + // out == {"baz":""} + bool dont_omitempty = 11110005; + + // oneof_name sets the type name for the given field oneof field. This is used + // by the Amino JSON encoder to encode the type of the oneof field, and must be the same string in + // the RegisterConcrete() method usage used to register the concrete type. + string oneof_name = 11110006; +} \ No newline at end of file diff --git a/third_party/proto/cosmos/msg/v1/msg.proto b/third_party/proto/cosmos/msg/v1/msg.proto new file mode 100644 index 00000000..5f26d4c8 --- /dev/null +++ b/third_party/proto/cosmos/msg/v1/msg.proto @@ -0,0 +1,30 @@ +syntax = "proto3"; + +package cosmos.msg.v1; + +import "google/protobuf/descriptor.proto"; + +// TODO(fdymylja): once we fully migrate to protov2 the go_package needs to be updated. +// We need this right now because gogoproto codegen needs to import the extension. +option go_package = "github.com/cosmos/cosmos-sdk/types/msgservice"; + +extend google.protobuf.ServiceOptions { + // service indicates that the service is a Msg service and that requests + // must be transported via blockchain transactions rather than gRPC. + // Tooling can use this annotation to distinguish between Msg services and + // other types of services via reflection. + bool service = 11110000; +} + +extend google.protobuf.MessageOptions { + // signer must be used in cosmos messages in order + // to signal to external clients which fields in a + // given cosmos message must be filled with signer + // information (address). + // The field must be the protobuf name of the message + // field extended with this MessageOption. + // The field must either be of string kind, or of message + // kind in case the signer information is contained within + // a message inside the cosmos message. + repeated string signer = 11110000; +} \ No newline at end of file diff --git a/third_party/proto/cosmos_proto/cosmos.proto b/third_party/proto/cosmos_proto/cosmos.proto index 167b1707..ef8a95af 100644 --- a/third_party/proto/cosmos_proto/cosmos.proto +++ b/third_party/proto/cosmos_proto/cosmos.proto @@ -3,14 +3,110 @@ package cosmos_proto; import "google/protobuf/descriptor.proto"; -option go_package = "github.com/regen-network/cosmos-proto"; +option go_package = "github.com/cosmos/cosmos-proto;cosmos_proto"; + +extend google.protobuf.MethodOptions { + + // method_added_in is used to indicate from which version the method was added. + string method_added_in = 93001; +} extend google.protobuf.MessageOptions { - string interface_type = 93001; - string implements_interface = 93002; + // implements_interface is used to indicate the type name of the interface + // that a message implements so that it can be used in google.protobuf.Any + // fields that accept that interface. A message can implement multiple + // interfaces. Interfaces should be declared using a declare_interface + // file option. + repeated string implements_interface = 93001; + + // message_added_in is used to indicate from which version the message was added. + string message_added_in = 93002; } extend google.protobuf.FieldOptions { - string accepts_interface = 93001; + + // accepts_interface is used to annotate that a google.protobuf.Any + // field accepts messages that implement the specified interface. + // Interfaces should be declared using a declare_interface file option. + string accepts_interface = 93001; + + // scalar is used to indicate that this field follows the formatting defined + // by the named scalar which should be declared with declare_scalar. Code + // generators may choose to use this information to map this field to a + // language-specific type representing the scalar. + string scalar = 93002; + + // field_added_in is used to indicate from which version the field was added. + string field_added_in = 93003; +} + +extend google.protobuf.FileOptions { + + // declare_interface declares an interface type to be used with + // accepts_interface and implements_interface. Interface names are + // expected to follow the following convention such that their declaration + // can be discovered by tools: for a given interface type a.b.C, it is + // expected that the declaration will be found in a protobuf file named + // a/b/interfaces.proto in the file descriptor set. + repeated InterfaceDescriptor declare_interface = 793021; + + // declare_scalar declares a scalar type to be used with + // the scalar field option. Scalar names are + // expected to follow the following convention such that their declaration + // can be discovered by tools: for a given scalar type a.b.C, it is + // expected that the declaration will be found in a protobuf file named + // a/b/scalars.proto in the file descriptor set. + repeated ScalarDescriptor declare_scalar = 793022; + + // file_added_in is used to indicate from which the version the file was added. + string file_added_in = 793023; +} + +// InterfaceDescriptor describes an interface type to be used with +// accepts_interface and implements_interface and declared by declare_interface. +message InterfaceDescriptor { + + // name is the name of the interface. It should be a short-name (without + // a period) such that the fully qualified name of the interface will be + // package.name, ex. for the package a.b and interface named C, the + // fully-qualified name will be a.b.C. + string name = 1; + + // description is a human-readable description of the interface and its + // purpose. + string description = 2; } + +// ScalarDescriptor describes an scalar type to be used with +// the scalar field option and declared by declare_scalar. +// Scalars extend simple protobuf built-in types with additional +// syntax and semantics, for instance to represent big integers. +// Scalars should ideally define an encoding such that there is only one +// valid syntactical representation for a given semantic meaning, +// i.e. the encoding should be deterministic. +message ScalarDescriptor { + + // name is the name of the scalar. It should be a short-name (without + // a period) such that the fully qualified name of the scalar will be + // package.name, ex. for the package a.b and scalar named C, the + // fully-qualified name will be a.b.C. + string name = 1; + + // description is a human-readable description of the scalar and its + // encoding format. For instance a big integer or decimal scalar should + // specify precisely the expected encoding format. + string description = 2; + + // field_type is the type of field with which this scalar can be used. + // Scalars can be used with one and only one type of field so that + // encoding standards and simple and clear. Currently only string and + // bytes fields are supported for scalars. + repeated ScalarType field_type = 3; +} + +enum ScalarType { + SCALAR_TYPE_UNSPECIFIED = 0; + SCALAR_TYPE_STRING = 1; + SCALAR_TYPE_BYTES = 2; +} \ No newline at end of file diff --git a/x/nft/keeper/collection.go b/x/nft/keeper/collection.go index 4fd8a17f..e47f5dc2 100644 --- a/x/nft/keeper/collection.go +++ b/x/nft/keeper/collection.go @@ -93,7 +93,7 @@ func (k Keeper) createCollectionDenom(creator sdk.AccAddress, symbol string) (st // TODO: if necessary add a salt field if strings.TrimSpace(symbol) == "" { - return "", fmt.Errorf("symbol cannot be blank") + return "", fmt.Errorf("symbol cannot be empty") } if len(symbol) > types.MaxSymbolLength { diff --git a/x/nft/keeper/edition.go b/x/nft/keeper/edition.go index 9e8ac91a..432a0f8a 100644 --- a/x/nft/keeper/edition.go +++ b/x/nft/keeper/edition.go @@ -37,6 +37,10 @@ func (k Keeper) PrintEdition( // TODO: Charge fee if necessary + if nft.Editions == types.MaxEditions { + return 0, fmt.Errorf("max editions reached for NFT %s in collection %s", tokenId, collectionDenom) + } + edition := types.Edition{ Collection: collectionDenom, TokenId: tokenId, diff --git a/x/nft/keeper/keeper.go b/x/nft/keeper/keeper.go index 64c7bc53..9103ebd0 100644 --- a/x/nft/keeper/keeper.go +++ b/x/nft/keeper/keeper.go @@ -3,6 +3,7 @@ package keeper import ( "cosmossdk.io/collections" "cosmossdk.io/collections/indexes" + "cosmossdk.io/core/address" "cosmossdk.io/core/store" "cosmossdk.io/log" "cosmossdk.io/math" @@ -54,6 +55,7 @@ type Keeper struct { storeKey storetypes.StoreKey storeService store.KVStoreService ak types.AccountKeeper + ac address.Codec logger log.Logger Schema collections.Schema @@ -79,6 +81,7 @@ func NewKeeper(cdc codec.BinaryCodec, key storetypes.StoreKey, storeService stor storeKey: key, storeService: storeService, ak: ak, + ac: ak.AddressCodec(), logger: logger, // TODO: fix the store once we add queries Collections: collections.NewMap( diff --git a/x/nft/keeper/keeper_test.go b/x/nft/keeper/keeper_test.go index 2245ff02..44b34b08 100644 --- a/x/nft/keeper/keeper_test.go +++ b/x/nft/keeper/keeper_test.go @@ -56,7 +56,10 @@ type KeeperTestSuite struct { ctx sdk.Context keeper keeper.Keeper - app *simapp.BitsongApp + + msgServer types.MsgServer + + app *simapp.BitsongApp } func (suite *KeeperTestSuite) SetupTest() { @@ -66,6 +69,8 @@ func (suite *KeeperTestSuite) SetupTest() { suite.keeper = app.NftKeeper suite.App = app suite.ctx = suite.Ctx + + suite.msgServer = keeper.NewMsgServerImpl(suite.keeper) } func TestKeeperSuite(t *testing.T) { @@ -174,7 +179,7 @@ func (suite *KeeperTestSuite) TestSendNFT() { suite.NoError(err) suite.Len(res.Nfts, 0) - err = suite.keeper.SendNft(suite.ctx, owner1, owner2, collectionDenom, "1") + err = suite.keeper.SendNFT(suite.ctx, owner1, owner2, collectionDenom, "1") suite.NoError(err) res, err = suite.keeper.AllNftsByOwner(suite.ctx, &types.QueryAllNftsByOwnerRequest{ diff --git a/x/nft/keeper/msg_server.go b/x/nft/keeper/msg_server.go new file mode 100644 index 00000000..77632c3a --- /dev/null +++ b/x/nft/keeper/msg_server.go @@ -0,0 +1,119 @@ +package keeper + +import ( + "context" + + errorsmod "cosmossdk.io/errors" + "github.com/bitsongofficial/go-bitsong/x/nft/types" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +type msgServer struct { + Keeper +} + +var _ types.MsgServer = msgServer{} + +func NewMsgServerImpl(keeper Keeper) types.MsgServer { + return msgServer{Keeper: keeper} +} + +func (k msgServer) CreateCollection(goCtx context.Context, msg *types.MsgCreateCollection) (*types.MsgCreateCollectionResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + creator, err := k.ac.StringToBytes(msg.Creator) + if err != nil { + return nil, errorsmod.Wrapf(sdkerrors.ErrInvalidAddress, "invalid creator address: %s", err) + } + + minter, err := k.ac.StringToBytes(msg.Minter) + if err != nil { + return nil, errorsmod.Wrapf(sdkerrors.ErrInvalidAddress, "invalid minter address: %s", err) + } + + // TODO: add authority + /*authority, err := k.ac.StringToBytes(msg.Authority) + if err != nil { + return nil, errorsmod.Wrapf(sdkerrors.ErrInvalidAddress, "invalid authority address: %s", err) + }*/ + + denom, err := k.Keeper.CreateCollection(ctx, creator, minter, msg.Symbol, msg.Name, msg.Uri) + if err != nil { + return nil, err + } + + return &types.MsgCreateCollectionResponse{ + Denom: denom, + }, nil +} + +func (k msgServer) MintNFT(goCtx context.Context, msg *types.MsgMintNFT) (*types.MsgMintNFTResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + minter, err := k.ac.StringToBytes(msg.Minter) + if err != nil { + return nil, errorsmod.Wrapf(sdkerrors.ErrInvalidAddress, "invalid minter address: %s", err) + } + + recipient, err := k.ac.StringToBytes(msg.Recipient) + if err != nil { + return nil, errorsmod.Wrapf(sdkerrors.ErrInvalidAddress, "invalid recipient address: %s", err) + } + + err = k.Keeper.MintNFT(ctx, minter, recipient, msg.Collection, msg.TokenId, msg.Name, msg.Uri) + if err != nil { + return nil, err + } + + return &types.MsgMintNFTResponse{ + Collection: msg.Collection, + TokenId: msg.TokenId, + }, nil +} + +func (k msgServer) SendNFT(goCtx context.Context, msg *types.MsgSendNFT) (*types.MsgSendNFTResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + sender, err := k.ac.StringToBytes(msg.Sender) + if err != nil { + return nil, errorsmod.Wrapf(sdkerrors.ErrInvalidAddress, "invalid sender address: %s", err) + } + + recipient, err := k.ac.StringToBytes(msg.Recipient) + if err != nil { + return nil, errorsmod.Wrapf(sdkerrors.ErrInvalidAddress, "invalid recipient address: %s", err) + } + + err = k.Keeper.SendNFT(ctx, sender, recipient, msg.Collection, msg.TokenId) + if err != nil { + return nil, err + } + + return &types.MsgSendNFTResponse{}, nil +} + +func (k msgServer) PrintEdition(goCtx context.Context, msg *types.MsgPrintEdition) (*types.MsgPrintEditionResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + minter, err := k.ac.StringToBytes(msg.Minter) + if err != nil { + return nil, errorsmod.Wrapf(sdkerrors.ErrInvalidAddress, "invalid minter address: %s", err) + } + + recipient, err := k.ac.StringToBytes(msg.Recipient) + if err != nil { + return nil, errorsmod.Wrapf(sdkerrors.ErrInvalidAddress, "invalid recipient address: %s", err) + } + + seq, err := k.Keeper.PrintEdition(ctx, minter, recipient, msg.Collection, msg.TokenId) + if err != nil { + return nil, err + } + + return &types.MsgPrintEditionResponse{ + Collection: msg.Collection, + TokenId: msg.TokenId, + Seq: seq, + }, nil +} diff --git a/x/nft/keeper/msg_server_test.go b/x/nft/keeper/msg_server_test.go new file mode 100644 index 00000000..d09b5578 --- /dev/null +++ b/x/nft/keeper/msg_server_test.go @@ -0,0 +1,527 @@ +package keeper_test + +import ( + "github.com/bitsongofficial/go-bitsong/x/nft/types" +) + +func (suite *KeeperTestSuite) TestMsgCreateCollection() { + testCases := []struct { + name string + msg *types.MsgCreateCollection + expectError bool + expectedErrorMsg string + }{ + { + name: "valid message", + msg: &types.MsgCreateCollection{ + Creator: creator1.String(), + Minter: minter1.String(), + Name: testCollection1.Name, + Symbol: testCollection1.Symbol, + Uri: testCollection1.Uri, + }, + }, + { + name: "invalid creator address", + msg: &types.MsgCreateCollection{ + Creator: "invalid_address", + Minter: minter1.String(), + Name: testCollection1.Name, + Symbol: testCollection1.Symbol, + Uri: testCollection1.Uri, + }, + expectError: true, + expectedErrorMsg: "invalid creator address: decoding bech32 failed", + }, + { + name: "should fail on empty creator address", + msg: &types.MsgCreateCollection{ + Creator: "", + Minter: minter1.String(), + Name: testCollection1.Name, + Symbol: testCollection1.Symbol, + Uri: testCollection1.Uri, + }, + expectError: true, + expectedErrorMsg: "invalid creator address: empty address string", + }, + { + name: "invalid minter address", + msg: &types.MsgCreateCollection{ + Creator: creator1.String(), + Minter: "invalid_address", + Name: testCollection1.Name, + Symbol: testCollection1.Symbol, + Uri: testCollection1.Uri, + }, + expectError: true, + expectedErrorMsg: "invalid minter address: decoding bech32 failed", + }, + { + // TODO: this should be valid as minter can be empty (meaning no one can mint) + name: "invalid empty minter address", + msg: &types.MsgCreateCollection{ + Creator: creator1.String(), + Minter: "", + Name: testCollection1.Name, + Symbol: testCollection1.Symbol, + Uri: testCollection1.Uri, + }, + expectError: true, + expectedErrorMsg: "invalid minter address: empty address string", + }, + { + name: "should fail on empty symbol", + msg: &types.MsgCreateCollection{ + Creator: creator1.String(), + Minter: minter1.String(), + Name: testCollection1.Name, + Symbol: "", + Uri: testCollection1.Uri, + }, + expectError: true, + expectedErrorMsg: "symbol cannot be empty", + }, + { + name: "valid on empty name", + msg: &types.MsgCreateCollection{ + Creator: creator1.String(), + Minter: minter1.String(), + Name: "", + Symbol: testCollection1.Symbol + "1", + Uri: testCollection1.Uri, + }, + }, + { + name: "valid on empty uri", + msg: &types.MsgCreateCollection{ + Creator: creator1.String(), + Minter: minter1.String(), + Name: testCollection1.Name, + Symbol: testCollection1.Symbol + "2", + Uri: "", + }, + }, + } + + for _, tc := range testCases { + suite.Run(tc.name, func() { + _, err := suite.msgServer.CreateCollection(suite.ctx, tc.msg) + if tc.expectError { + suite.Require().Error(err) + if tc.expectedErrorMsg != "" { + suite.Require().Contains(err.Error(), tc.expectedErrorMsg) + } + } else { + suite.Require().NoError(err) + } + }) + } +} + +func (suite *KeeperTestSuite) TestMsgMintNFT() { + // first create a collection to mint into + createCollectionMsg := &types.MsgCreateCollection{ + Creator: creator1.String(), + Minter: minter1.String(), + Name: testCollection1.Name, + Symbol: testCollection1.Symbol, + Uri: testCollection1.Uri, + } + res, err := suite.msgServer.CreateCollection(suite.ctx, createCollectionMsg) + suite.Require().NoError(err) + suite.Require().NotNil(res) + suite.Require().NotEmpty(res.Denom) + + testCases := []struct { + name string + msg *types.MsgMintNFT + expectError bool + expectedErrorMsg string + }{ + { + name: "valid message", + msg: &types.MsgMintNFT{ + Minter: minter1.String(), + Recipient: owner1.String(), + Collection: res.Denom, + TokenId: testNft1.TokenId, + Name: testNft1.Name, + Uri: testNft1.Uri, + }, + }, + } + + for _, tc := range testCases { + suite.Run(tc.name, func() { + _, err := suite.msgServer.MintNFT(suite.ctx, tc.msg) + if tc.expectError { + suite.Require().Error(err) + if tc.expectedErrorMsg != "" { + suite.Require().Contains(err.Error(), tc.expectedErrorMsg) + } + } else { + suite.Require().NoError(err) + } + }) + } +} + +func (suite *KeeperTestSuite) TestMsgMintNFT_MaxLimit() { + createCollectionMsg := &types.MsgCreateCollection{ + Creator: creator1.String(), + Minter: minter1.String(), + Name: testCollection1.Name, + Symbol: testCollection1.Symbol, + Uri: testCollection1.Uri, + } + res, err := suite.msgServer.CreateCollection(suite.ctx, createCollectionMsg) + suite.Require().NoError(err) + suite.Require().NotNil(res) + suite.Require().NotEmpty(res.Denom) + + for i := 0; i < types.MaxNftsInCollection; i++ { + mintMsg := &types.MsgMintNFT{ + Minter: minter1.String(), + Recipient: owner1.String(), + Collection: res.Denom, + TokenId: "token" + string(rune(i)), + Name: "Token " + string(rune(i)), + Uri: "http://example.com/token" + string(rune(i)), + } + _, err := suite.msgServer.MintNFT(suite.ctx, mintMsg) + suite.Require().NoError(err) + } + + // attempt to mint one more NFT beyond the limit + mintMsg := &types.MsgMintNFT{ + Minter: minter1.String(), + Recipient: owner1.String(), + Collection: res.Denom, + TokenId: "token_overflow", + Name: "Token Overflow", + Uri: "http://example.com/token_overflow", + } + _, err = suite.msgServer.MintNFT(suite.ctx, mintMsg) + suite.Require().Error(err) + suite.Require().Contains(err.Error(), "max supply reached") +} + +func (suite *KeeperTestSuite) TestMsgSendNFT() { + createCollectionMsg := &types.MsgCreateCollection{ + Creator: creator1.String(), + Minter: minter1.String(), + Name: testCollection1.Name, + Symbol: testCollection1.Symbol, + Uri: testCollection1.Uri, + } + res, err := suite.msgServer.CreateCollection(suite.ctx, createCollectionMsg) + suite.Require().NoError(err) + suite.Require().NotNil(res) + suite.Require().NotEmpty(res.Denom) + + mintMsg := &types.MsgMintNFT{ + Minter: minter1.String(), + Recipient: owner1.String(), + Collection: res.Denom, + TokenId: testNft1.TokenId, + Name: testNft1.Name, + Uri: testNft1.Uri, + } + _, err = suite.msgServer.MintNFT(suite.ctx, mintMsg) + suite.Require().NoError(err) + + testCases := []struct { + name string + msg *types.MsgSendNFT + expectError bool + expectedErrorMsg string + }{ + { + name: "valid message", + msg: &types.MsgSendNFT{ + Sender: owner1.String(), + Recipient: owner2.String(), + Collection: res.Denom, + TokenId: testNft1.TokenId, + }, + }, + { + name: "should fail on same sender and recipient", + msg: &types.MsgSendNFT{ + Sender: owner1.String(), + Recipient: owner1.String(), + Collection: res.Denom, + TokenId: testNft1.TokenId, + }, + expectError: true, + expectedErrorMsg: "cannot transfer NFT to the same owner", + }, + { + name: "invalid sender address", + msg: &types.MsgSendNFT{ + Sender: "invalid_address", + Recipient: owner2.String(), + Collection: res.Denom, + TokenId: testNft1.TokenId, + }, + expectError: true, + expectedErrorMsg: "invalid sender address: decoding bech32 failed", + }, + { + name: "should fail on empty sender address", + msg: &types.MsgSendNFT{ + Sender: "", + Recipient: owner2.String(), + Collection: res.Denom, + TokenId: testNft1.TokenId, + }, + expectError: true, + expectedErrorMsg: "invalid sender address: empty address string", + }, + { + name: "invalid recipient address", + msg: &types.MsgSendNFT{ + Sender: owner1.String(), + Recipient: "invalid_address", + Collection: res.Denom, + TokenId: testNft1.TokenId, + }, + expectError: true, + expectedErrorMsg: "invalid recipient address: decoding bech32 failed", + }, + { + name: "should fail on empty recipient address", + msg: &types.MsgSendNFT{ + Sender: owner1.String(), + Recipient: "", + Collection: res.Denom, + TokenId: testNft1.TokenId, + }, + expectError: true, + expectedErrorMsg: "invalid recipient address: empty address string", + }, + { + name: "should fail on non existing collection", + msg: &types.MsgSendNFT{ + Sender: owner1.String(), + Recipient: owner2.String(), + Collection: "non_existing_collection", + TokenId: testNft1.TokenId, + }, + expectError: true, + expectedErrorMsg: "collection or token_id does not exist", + }, + { + name: "should fail on non existing token", + msg: &types.MsgSendNFT{ + Sender: owner1.String(), + Recipient: owner2.String(), + Collection: res.Denom, + TokenId: "non_existing_token", + }, + expectError: true, + expectedErrorMsg: "collection or token_id does not exist", + }, + { + name: "should fail when sender is not the owner", + msg: &types.MsgSendNFT{ + Sender: owner1.String(), + Recipient: owner2.String(), + Collection: res.Denom, + TokenId: testNft1.TokenId, + }, + expectError: true, + expectedErrorMsg: "only the owner can transfer the NFT", + }, + } + + for _, tc := range testCases { + suite.Run(tc.name, func() { + _, err := suite.msgServer.SendNFT(suite.ctx, tc.msg) + if tc.expectError { + suite.Require().Error(err) + if tc.expectedErrorMsg != "" { + suite.Require().Contains(err.Error(), tc.expectedErrorMsg) + } + } else { + suite.Require().NoError(err) + } + }) + } +} + +func (suite *KeeperTestSuite) TestMsgPrintEdition() { + createCollectionMsg := &types.MsgCreateCollection{ + Creator: creator1.String(), + Minter: minter1.String(), + Name: testCollection1.Name, + Symbol: testCollection1.Symbol, + Uri: testCollection1.Uri, + } + res, err := suite.msgServer.CreateCollection(suite.ctx, createCollectionMsg) + suite.Require().NoError(err) + suite.Require().NotNil(res) + suite.Require().NotEmpty(res.Denom) + + mintMsg := &types.MsgMintNFT{ + Minter: minter1.String(), + Recipient: owner1.String(), + Collection: res.Denom, + TokenId: testNft1.TokenId, + Name: testNft1.Name, + Uri: testNft1.Uri, + } + _, err = suite.msgServer.MintNFT(suite.ctx, mintMsg) + suite.Require().NoError(err) + + testCases := []struct { + name string + msg *types.MsgPrintEdition + expectError bool + expectedErrorMsg string + }{ + { + name: "valid message", + msg: &types.MsgPrintEdition{ + Minter: minter1.String(), + Recipient: owner2.String(), + Collection: res.Denom, + TokenId: testNft1.TokenId, + }, + }, + { + name: "invalid minter address", + msg: &types.MsgPrintEdition{ + Minter: "invalid_address", + Recipient: owner2.String(), + Collection: res.Denom, + TokenId: testNft1.TokenId, + }, + expectError: true, + expectedErrorMsg: "invalid minter address: decoding bech32 failed", + }, + { + name: "should fail on empty minter address", + msg: &types.MsgPrintEdition{ + Minter: "", + Recipient: owner2.String(), + Collection: res.Denom, + TokenId: testNft1.TokenId, + }, + expectError: true, + expectedErrorMsg: "invalid minter address: empty address string", + }, + { + name: "invalid recipient address", + msg: &types.MsgPrintEdition{ + Minter: minter1.String(), + Recipient: "invalid_address", + Collection: res.Denom, + TokenId: testNft1.TokenId, + }, + expectError: true, + expectedErrorMsg: "invalid recipient address: decoding bech32 failed", + }, + { + name: "should fail on empty recipient address", + msg: &types.MsgPrintEdition{ + Minter: minter1.String(), + Recipient: "", + Collection: res.Denom, + TokenId: testNft1.TokenId, + }, + expectError: true, + expectedErrorMsg: "invalid recipient address: empty address string", + }, + { + name: "should fail on non existing collection", + msg: &types.MsgPrintEdition{ + Minter: minter1.String(), + Recipient: owner2.String(), + Collection: "non_existing_collection", + TokenId: testNft1.TokenId, + }, + expectError: true, + expectedErrorMsg: "NFT with token ID", + }, + { + name: "should fail on non existing token", + msg: &types.MsgPrintEdition{ + Minter: minter1.String(), + Recipient: owner2.String(), + Collection: res.Denom, + TokenId: "non_existing_token", + }, + expectError: true, + expectedErrorMsg: "NFT with token ID", + }, + { + name: "should fail when minter is not the collection minter", + msg: &types.MsgPrintEdition{ + Minter: creator1.String(), + Recipient: owner2.String(), + Collection: res.Denom, + TokenId: testNft1.TokenId, + }, + expectError: true, + expectedErrorMsg: "only the collection minter can print editions", + }, + } + + for _, tc := range testCases { + suite.Run(tc.name, func() { + _, err := suite.msgServer.PrintEdition(suite.ctx, tc.msg) + if tc.expectError { + suite.Require().Error(err) + if tc.expectedErrorMsg != "" { + suite.Require().Contains(err.Error(), tc.expectedErrorMsg) + } + } else { + suite.Require().NoError(err) + } + }) + } +} + +func (suite *KeeperTestSuite) TestMsgPrintNFT_MaxLimit() { + createCollectionMsg := &types.MsgCreateCollection{ + Creator: creator1.String(), + Minter: minter1.String(), + Name: testCollection1.Name, + Symbol: testCollection1.Symbol, + Uri: testCollection1.Uri, + } + res, err := suite.msgServer.CreateCollection(suite.ctx, createCollectionMsg) + suite.Require().NoError(err) + suite.Require().NotNil(res) + suite.Require().NotEmpty(res.Denom) + + mintMsg := &types.MsgMintNFT{ + Minter: minter1.String(), + Recipient: owner1.String(), + Collection: res.Denom, + TokenId: testNft1.TokenId, + Name: testNft1.Name, + Uri: testNft1.Uri, + } + _, err = suite.msgServer.MintNFT(suite.ctx, mintMsg) + suite.Require().NoError(err) + + printEditionMsg := &types.MsgPrintEdition{ + Minter: minter1.String(), + Recipient: owner2.String(), + Collection: res.Denom, + TokenId: testNft1.TokenId, + } + + for i := 0; i < types.MaxEditions; i++ { + _, err := suite.msgServer.PrintEdition(suite.ctx, printEditionMsg) + suite.Require().NoError(err) + } + + // attempt to print one more edition beyond the limit + _, err = suite.msgServer.PrintEdition(suite.ctx, printEditionMsg) + suite.Require().Error(err) + suite.Require().Contains(err.Error(), "max editions reached") +} diff --git a/x/nft/keeper/nft.go b/x/nft/keeper/nft.go index 4a004dd5..655f3801 100644 --- a/x/nft/keeper/nft.go +++ b/x/nft/keeper/nft.go @@ -6,6 +6,7 @@ import ( "strings" "cosmossdk.io/collections" + "cosmossdk.io/math" "github.com/bitsongofficial/go-bitsong/x/nft/types" "github.com/cosmos/cosmos-sdk/telemetry" sdk "github.com/cosmos/cosmos-sdk/types" @@ -51,6 +52,11 @@ func (k Keeper) MintNFT( return fmt.Errorf("only the collection minter can mint NFTs") } + supply := k.GetSupply(ctx, collectionDenom) + if supply.Equal(math.NewInt(types.MaxNftsInCollection)) { + return fmt.Errorf("max supply reached for collection %s", collectionDenom) + } + // TODO: Charge fee if necessary nft := types.Nft{ @@ -71,7 +77,7 @@ func (k Keeper) MintNFT( return k.incrementSupply(ctx, collectionDenom) } -func (k Keeper) SendNft(ctx context.Context, fromAddr, toAddr sdk.AccAddress, collectionDenom, tokenId string) error { +func (k Keeper) SendNFT(ctx context.Context, fromAddr, toAddr sdk.AccAddress, collectionDenom, tokenId string) error { err := k.changeNftOwner(ctx, fromAddr, toAddr, collectionDenom, tokenId) if err != nil { return err @@ -159,9 +165,13 @@ func (k Keeper) setNft(ctx context.Context, nft types.Nft) error { } func (k Keeper) changeNftOwner(ctx context.Context, oldOwner, newOwner sdk.AccAddress, collectionDenom string, tokenId string) error { + if oldOwner.Equals(newOwner) { + return fmt.Errorf("cannot transfer NFT to the same owner") + } + nft, err := k.NFTs.Get(ctx, collections.Join(collectionDenom, tokenId)) if err != nil { - return fmt.Errorf("failed to get NFT: %w", err) + return fmt.Errorf("collection or token_id does not exist") } if nft.Owner != oldOwner.String() { diff --git a/x/nft/types/keys.go b/x/nft/types/keys.go index 242d654a..816b570f 100644 --- a/x/nft/types/keys.go +++ b/x/nft/types/keys.go @@ -20,6 +20,10 @@ const ( MaxSymbolLength = 15 MaxNameLength = 128 MaxURILength = 150 + + // TODO: move these to params + MaxNftsInCollection = 100 + MaxEditions = 1000 ) var ( diff --git a/x/nft/types/nft.pb.go b/x/nft/types/nft.pb.go index 358d7a92..0a113944 100644 --- a/x/nft/types/nft.pb.go +++ b/x/nft/types/nft.pb.go @@ -73,10 +73,9 @@ type Nft struct { Name string `protobuf:"bytes,3,opt,name=name,proto3" json:"name,omitempty"` Uri string `protobuf:"bytes,5,opt,name=uri,proto3" json:"uri,omitempty"` Owner string `protobuf:"bytes,6,opt,name=owner,proto3" json:"owner,omitempty"` - Authority string `protobuf:"bytes,7,opt,name=authority,proto3" json:"authority,omitempty"` // TODO: add max_editions // uint64 max_editions = 8; // max number of printed editions, 0 means no limit - Editions uint64 `protobuf:"varint,8,opt,name=editions,proto3" json:"editions,omitempty"` + Editions uint64 `protobuf:"varint,7,opt,name=editions,proto3" json:"editions,omitempty"` } func (m *Nft) Reset() { *m = Nft{} } @@ -161,32 +160,31 @@ func init() { func init() { proto.RegisterFile("bitsong/nft/v1beta1/nft.proto", fileDescriptor_51b0314c164430ab) } var fileDescriptor_51b0314c164430ab = []byte{ - // 388 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x92, 0xbd, 0x6e, 0xdb, 0x30, - 0x10, 0xc7, 0xc5, 0x4a, 0xb6, 0xac, 0x9b, 0x0c, 0xd6, 0x28, 0x58, 0xa3, 0x56, 0x0d, 0x4f, 0x5e, - 0x6a, 0xc1, 0xe8, 0xd6, 0xb1, 0x45, 0x87, 0xa2, 0x40, 0x07, 0xa1, 0x53, 0x17, 0x43, 0x1f, 0x94, - 0x4c, 0x54, 0xe2, 0x39, 0x12, 0x95, 0xc4, 0x6f, 0x90, 0x31, 0x8f, 0x90, 0x07, 0xc9, 0x03, 0x64, - 0xf4, 0x12, 0x20, 0x63, 0x60, 0xbf, 0x48, 0x20, 0x4a, 0xfe, 0x48, 0xa6, 0x00, 0xd9, 0xee, 0xff, - 0x3b, 0xe2, 0x78, 0x3f, 0xe0, 0x60, 0x14, 0x0a, 0x55, 0xa2, 0x4c, 0x3d, 0x99, 0x28, 0xef, 0x7c, - 0x1e, 0x72, 0x15, 0xcc, 0xeb, 0x7a, 0xb6, 0x2a, 0x50, 0x21, 0x7d, 0xdf, 0xb6, 0x67, 0x35, 0x6a, - 0xdb, 0xc3, 0x41, 0x8a, 0x29, 0xea, 0xbe, 0x57, 0x57, 0xcd, 0xd3, 0xc9, 0x3d, 0x01, 0xf8, 0x81, - 0x59, 0xc6, 0x23, 0x25, 0x50, 0xd2, 0x01, 0x74, 0x62, 0x2e, 0x31, 0x67, 0x64, 0x4c, 0xa6, 0x8e, - 0xdf, 0x04, 0xfa, 0x01, 0xba, 0xe5, 0x3a, 0x0f, 0x31, 0x63, 0xef, 0x34, 0x6e, 0x13, 0xa5, 0x60, - 0xc9, 0x20, 0xe7, 0xcc, 0xd4, 0x54, 0xd7, 0xb4, 0x0f, 0x66, 0x55, 0x08, 0xd6, 0xd1, 0xa8, 0x2e, - 0x29, 0x03, 0x3b, 0x2a, 0x78, 0xa0, 0xb0, 0x60, 0x5d, 0x4d, 0xf7, 0xb1, 0x9e, 0x9b, 0x0b, 0xa9, - 0x78, 0xc1, 0xec, 0x66, 0x6e, 0x93, 0xe8, 0x27, 0x70, 0x82, 0x4a, 0x2d, 0xb1, 0x10, 0x6a, 0xcd, - 0x7a, 0xba, 0x75, 0x04, 0x74, 0x04, 0x20, 0xab, 0x7c, 0xa1, 0xf0, 0x3f, 0x97, 0x25, 0x73, 0xc6, - 0x64, 0x6a, 0xf9, 0x8e, 0xac, 0xf2, 0xbf, 0x1a, 0x7c, 0xb3, 0xae, 0x6e, 0x3e, 0x1b, 0x93, 0x5b, - 0x02, 0xe6, 0x9f, 0x44, 0x51, 0x17, 0x20, 0x3a, 0xe8, 0xb5, 0x56, 0x27, 0x84, 0x7e, 0x84, 0x9e, - 0x1e, 0xb4, 0x10, 0x71, 0x2b, 0x67, 0xeb, 0xfc, 0x2b, 0x7e, 0xa5, 0xdd, 0x00, 0x3a, 0x78, 0x21, - 0xf9, 0xde, 0xad, 0x09, 0xcf, 0x0d, 0xec, 0x97, 0x06, 0x43, 0xe8, 0xf1, 0x58, 0xd4, 0xff, 0x97, - 0x5a, 0xcf, 0xf2, 0x0f, 0xb9, 0x5d, 0xbf, 0x00, 0xfb, 0x67, 0x43, 0xde, 0x62, 0xd0, 0x07, 0xb3, - 0xe4, 0x67, 0x5a, 0xc0, 0xf2, 0xeb, 0xf2, 0xb8, 0xad, 0x75, 0xb2, 0x6d, 0xf3, 0xe7, 0xf7, 0xdf, - 0x77, 0x5b, 0x97, 0x6c, 0xb6, 0x2e, 0x79, 0xdc, 0xba, 0xe4, 0x7a, 0xe7, 0x1a, 0x9b, 0x9d, 0x6b, - 0x3c, 0xec, 0x5c, 0xe3, 0xdf, 0x3c, 0x15, 0x6a, 0x59, 0x85, 0xb3, 0x08, 0x73, 0xaf, 0x3d, 0x2d, - 0x4c, 0x12, 0x11, 0x89, 0x20, 0xf3, 0x52, 0xfc, 0xb2, 0x3f, 0xc6, 0x4b, 0x7d, 0x8e, 0x6a, 0xbd, - 0xe2, 0x65, 0xd8, 0xd5, 0xe7, 0xf5, 0xf5, 0x29, 0x00, 0x00, 0xff, 0xff, 0x3a, 0xb6, 0xab, 0x47, - 0xaa, 0x02, 0x00, 0x00, + // 382 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x92, 0xbd, 0x6a, 0xe3, 0x40, + 0x14, 0x85, 0x35, 0x2b, 0xd9, 0xb2, 0x6e, 0x65, 0x66, 0xcd, 0x32, 0x6b, 0xd6, 0x5a, 0xe3, 0xca, + 0xcd, 0x5a, 0x98, 0xed, 0xb6, 0xdc, 0x65, 0x8b, 0x65, 0x21, 0x85, 0x48, 0x95, 0xc6, 0xe8, 0x67, + 0x24, 0x0f, 0x91, 0xe6, 0x3a, 0xd2, 0x28, 0x89, 0xdf, 0x20, 0x65, 0x9e, 0x20, 0xe4, 0x71, 0x52, + 0xba, 0x09, 0xa4, 0x0c, 0xf6, 0x8b, 0x04, 0x8d, 0xe4, 0x9f, 0x32, 0x90, 0xee, 0x9c, 0xef, 0x0e, + 0x77, 0xce, 0x81, 0x0b, 0xa3, 0x50, 0xa8, 0x12, 0x65, 0xea, 0xc9, 0x44, 0x79, 0xd7, 0xf3, 0x90, + 0xab, 0x60, 0x5e, 0xeb, 0xd9, 0xaa, 0x40, 0x85, 0xf4, 0x73, 0x3b, 0x9e, 0xd5, 0xa8, 0x1d, 0x0f, + 0x07, 0x29, 0xa6, 0xa8, 0xe7, 0x5e, 0xad, 0x9a, 0xa7, 0x93, 0x67, 0x02, 0xf0, 0x07, 0xb3, 0x8c, + 0x47, 0x4a, 0xa0, 0xa4, 0x03, 0xe8, 0xc4, 0x5c, 0x62, 0xce, 0xc8, 0x98, 0x4c, 0x1d, 0xbf, 0x31, + 0xf4, 0x0b, 0x74, 0xcb, 0x75, 0x1e, 0x62, 0xc6, 0x3e, 0x69, 0xdc, 0x3a, 0x4a, 0xc1, 0x92, 0x41, + 0xce, 0x99, 0xa9, 0xa9, 0xd6, 0xb4, 0x0f, 0x66, 0x55, 0x08, 0xd6, 0xd1, 0xa8, 0x96, 0x94, 0x81, + 0x1d, 0x15, 0x3c, 0x50, 0x58, 0xb0, 0xae, 0xa6, 0x7b, 0x5b, 0xef, 0xcd, 0x85, 0x54, 0xbc, 0x60, + 0x76, 0xb3, 0xb7, 0x71, 0xf4, 0x1b, 0x38, 0x41, 0xa5, 0x96, 0x58, 0x08, 0xb5, 0x66, 0x3d, 0x3d, + 0x3a, 0x02, 0x3a, 0x02, 0x90, 0x55, 0xbe, 0x50, 0x78, 0xc9, 0x65, 0xc9, 0x9c, 0x31, 0x99, 0x5a, + 0xbe, 0x23, 0xab, 0xfc, 0x5c, 0x83, 0x5f, 0xd6, 0xdd, 0xe3, 0x77, 0x63, 0xf2, 0x40, 0xc0, 0x3c, + 0x4b, 0x14, 0x75, 0x01, 0xa2, 0x43, 0xbd, 0xb6, 0xd5, 0x09, 0xa1, 0x5f, 0xa1, 0xa7, 0x17, 0x2d, + 0x44, 0xdc, 0x96, 0xb3, 0xb5, 0xff, 0x17, 0xbf, 0xb3, 0xdd, 0x00, 0x3a, 0x78, 0x23, 0xf9, 0xbe, + 0x5b, 0x63, 0xe8, 0x10, 0x7a, 0x3c, 0x16, 0xf5, 0x0f, 0xa5, 0xee, 0x66, 0xf9, 0x07, 0xdf, 0x06, + 0x2c, 0xc0, 0xfe, 0xdb, 0x90, 0x8f, 0x64, 0xec, 0x83, 0x59, 0xf2, 0x2b, 0x1d, 0xd1, 0xf2, 0x6b, + 0x79, 0xcc, 0x63, 0x9d, 0xe4, 0x69, 0xfe, 0xfc, 0xfd, 0xff, 0x69, 0xeb, 0x92, 0xcd, 0xd6, 0x25, + 0xaf, 0x5b, 0x97, 0xdc, 0xef, 0x5c, 0x63, 0xb3, 0x73, 0x8d, 0x97, 0x9d, 0x6b, 0x5c, 0xcc, 0x53, + 0xa1, 0x96, 0x55, 0x38, 0x8b, 0x30, 0xf7, 0xda, 0xe3, 0xc1, 0x24, 0x11, 0x91, 0x08, 0x32, 0x2f, + 0xc5, 0x1f, 0xfb, 0x73, 0xbb, 0xd5, 0x07, 0xa7, 0xd6, 0x2b, 0x5e, 0x86, 0x5d, 0x7d, 0x40, 0x3f, + 0xdf, 0x02, 0x00, 0x00, 0xff, 0xff, 0x51, 0x12, 0x7d, 0x2e, 0x8c, 0x02, 0x00, 0x00, } func (m *Collection) Marshal() (dAtA []byte, err error) { @@ -289,14 +287,7 @@ func (m *Nft) MarshalToSizedBuffer(dAtA []byte) (int, error) { if m.Editions != 0 { i = encodeVarintNft(dAtA, i, uint64(m.Editions)) i-- - dAtA[i] = 0x40 - } - if len(m.Authority) > 0 { - i -= len(m.Authority) - copy(dAtA[i:], m.Authority) - i = encodeVarintNft(dAtA, i, uint64(len(m.Authority))) - i-- - dAtA[i] = 0x3a + dAtA[i] = 0x38 } if len(m.Owner) > 0 { i -= len(m.Owner) @@ -462,10 +453,6 @@ func (m *Nft) Size() (n int) { if l > 0 { n += 1 + l + sovNft(uint64(l)) } - l = len(m.Authority) - if l > 0 { - n += 1 + l + sovNft(uint64(l)) - } if m.Editions != 0 { n += 1 + sovNft(uint64(m.Editions)) } @@ -985,38 +972,6 @@ func (m *Nft) Unmarshal(dAtA []byte) error { m.Owner = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 7: - 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 ErrIntOverflowNft - } - 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 ErrInvalidLengthNft - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthNft - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Authority = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 8: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Editions", wireType) } diff --git a/x/nft/types/tx.pb.go b/x/nft/types/tx.pb.go new file mode 100644 index 00000000..ef32cb6a --- /dev/null +++ b/x/nft/types/tx.pb.go @@ -0,0 +1,2495 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: bitsong/nft/v1beta1/tx.proto + +package types + +import ( + context "context" + fmt "fmt" + _ "github.com/cosmos/cosmos-proto" + _ "github.com/cosmos/cosmos-sdk/types/msgservice" + _ "github.com/cosmos/cosmos-sdk/types/tx/amino" + _ "github.com/cosmos/gogoproto/gogoproto" + grpc1 "github.com/cosmos/gogoproto/grpc" + proto "github.com/cosmos/gogoproto/proto" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +type MsgCreateCollection struct { + Symbol string `protobuf:"bytes,1,opt,name=symbol,proto3" json:"symbol,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + Uri string `protobuf:"bytes,3,opt,name=uri,proto3" json:"uri,omitempty"` + Creator string `protobuf:"bytes,4,opt,name=creator,proto3" json:"creator,omitempty"` + Minter string `protobuf:"bytes,5,opt,name=minter,proto3" json:"minter,omitempty"` + Authority string `protobuf:"bytes,6,opt,name=authority,proto3" json:"authority,omitempty"` +} + +func (m *MsgCreateCollection) Reset() { *m = MsgCreateCollection{} } +func (m *MsgCreateCollection) String() string { return proto.CompactTextString(m) } +func (*MsgCreateCollection) ProtoMessage() {} +func (*MsgCreateCollection) Descriptor() ([]byte, []int) { + return fileDescriptor_d3dab637c9b79d73, []int{0} +} +func (m *MsgCreateCollection) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgCreateCollection) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgCreateCollection.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 *MsgCreateCollection) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgCreateCollection.Merge(m, src) +} +func (m *MsgCreateCollection) XXX_Size() int { + return m.Size() +} +func (m *MsgCreateCollection) XXX_DiscardUnknown() { + xxx_messageInfo_MsgCreateCollection.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgCreateCollection proto.InternalMessageInfo + +type MsgCreateCollectionResponse struct { + Denom string `protobuf:"bytes,1,opt,name=denom,proto3" json:"denom,omitempty"` +} + +func (m *MsgCreateCollectionResponse) Reset() { *m = MsgCreateCollectionResponse{} } +func (m *MsgCreateCollectionResponse) String() string { return proto.CompactTextString(m) } +func (*MsgCreateCollectionResponse) ProtoMessage() {} +func (*MsgCreateCollectionResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_d3dab637c9b79d73, []int{1} +} +func (m *MsgCreateCollectionResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgCreateCollectionResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgCreateCollectionResponse.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 *MsgCreateCollectionResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgCreateCollectionResponse.Merge(m, src) +} +func (m *MsgCreateCollectionResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgCreateCollectionResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgCreateCollectionResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgCreateCollectionResponse proto.InternalMessageInfo + +func (m *MsgCreateCollectionResponse) GetDenom() string { + if m != nil { + return m.Denom + } + return "" +} + +type MsgMintNFT struct { + Collection string `protobuf:"bytes,1,opt,name=collection,proto3" json:"collection,omitempty"` + TokenId string `protobuf:"bytes,2,opt,name=token_id,json=tokenId,proto3" json:"token_id,omitempty"` + Name string `protobuf:"bytes,3,opt,name=name,proto3" json:"name,omitempty"` + Uri string `protobuf:"bytes,4,opt,name=uri,proto3" json:"uri,omitempty"` + Minter string `protobuf:"bytes,5,opt,name=minter,proto3" json:"minter,omitempty"` + Recipient string `protobuf:"bytes,7,opt,name=recipient,proto3" json:"recipient,omitempty"` +} + +func (m *MsgMintNFT) Reset() { *m = MsgMintNFT{} } +func (m *MsgMintNFT) String() string { return proto.CompactTextString(m) } +func (*MsgMintNFT) ProtoMessage() {} +func (*MsgMintNFT) Descriptor() ([]byte, []int) { + return fileDescriptor_d3dab637c9b79d73, []int{2} +} +func (m *MsgMintNFT) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgMintNFT) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgMintNFT.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 *MsgMintNFT) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgMintNFT.Merge(m, src) +} +func (m *MsgMintNFT) XXX_Size() int { + return m.Size() +} +func (m *MsgMintNFT) XXX_DiscardUnknown() { + xxx_messageInfo_MsgMintNFT.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgMintNFT proto.InternalMessageInfo + +type MsgMintNFTResponse struct { + Collection string `protobuf:"bytes,1,opt,name=collection,proto3" json:"collection,omitempty"` + TokenId string `protobuf:"bytes,2,opt,name=token_id,json=tokenId,proto3" json:"token_id,omitempty"` +} + +func (m *MsgMintNFTResponse) Reset() { *m = MsgMintNFTResponse{} } +func (m *MsgMintNFTResponse) String() string { return proto.CompactTextString(m) } +func (*MsgMintNFTResponse) ProtoMessage() {} +func (*MsgMintNFTResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_d3dab637c9b79d73, []int{3} +} +func (m *MsgMintNFTResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgMintNFTResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgMintNFTResponse.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 *MsgMintNFTResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgMintNFTResponse.Merge(m, src) +} +func (m *MsgMintNFTResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgMintNFTResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgMintNFTResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgMintNFTResponse proto.InternalMessageInfo + +func (m *MsgMintNFTResponse) GetCollection() string { + if m != nil { + return m.Collection + } + return "" +} + +func (m *MsgMintNFTResponse) GetTokenId() string { + if m != nil { + return m.TokenId + } + return "" +} + +type MsgSendNFT struct { + Collection string `protobuf:"bytes,1,opt,name=collection,proto3" json:"collection,omitempty"` + TokenId string `protobuf:"bytes,2,opt,name=token_id,json=tokenId,proto3" json:"token_id,omitempty"` + Sender string `protobuf:"bytes,3,opt,name=sender,proto3" json:"sender,omitempty"` + Recipient string `protobuf:"bytes,4,opt,name=recipient,proto3" json:"recipient,omitempty"` +} + +func (m *MsgSendNFT) Reset() { *m = MsgSendNFT{} } +func (m *MsgSendNFT) String() string { return proto.CompactTextString(m) } +func (*MsgSendNFT) ProtoMessage() {} +func (*MsgSendNFT) Descriptor() ([]byte, []int) { + return fileDescriptor_d3dab637c9b79d73, []int{4} +} +func (m *MsgSendNFT) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgSendNFT) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgSendNFT.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 *MsgSendNFT) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgSendNFT.Merge(m, src) +} +func (m *MsgSendNFT) XXX_Size() int { + return m.Size() +} +func (m *MsgSendNFT) XXX_DiscardUnknown() { + xxx_messageInfo_MsgSendNFT.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgSendNFT proto.InternalMessageInfo + +type MsgSendNFTResponse struct { +} + +func (m *MsgSendNFTResponse) Reset() { *m = MsgSendNFTResponse{} } +func (m *MsgSendNFTResponse) String() string { return proto.CompactTextString(m) } +func (*MsgSendNFTResponse) ProtoMessage() {} +func (*MsgSendNFTResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_d3dab637c9b79d73, []int{5} +} +func (m *MsgSendNFTResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgSendNFTResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgSendNFTResponse.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 *MsgSendNFTResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgSendNFTResponse.Merge(m, src) +} +func (m *MsgSendNFTResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgSendNFTResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgSendNFTResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgSendNFTResponse proto.InternalMessageInfo + +type MsgPrintEdition struct { + Collection string `protobuf:"bytes,1,opt,name=collection,proto3" json:"collection,omitempty"` + TokenId string `protobuf:"bytes,2,opt,name=token_id,json=tokenId,proto3" json:"token_id,omitempty"` + Minter string `protobuf:"bytes,5,opt,name=minter,proto3" json:"minter,omitempty"` + Recipient string `protobuf:"bytes,7,opt,name=recipient,proto3" json:"recipient,omitempty"` +} + +func (m *MsgPrintEdition) Reset() { *m = MsgPrintEdition{} } +func (m *MsgPrintEdition) String() string { return proto.CompactTextString(m) } +func (*MsgPrintEdition) ProtoMessage() {} +func (*MsgPrintEdition) Descriptor() ([]byte, []int) { + return fileDescriptor_d3dab637c9b79d73, []int{6} +} +func (m *MsgPrintEdition) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgPrintEdition) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgPrintEdition.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 *MsgPrintEdition) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgPrintEdition.Merge(m, src) +} +func (m *MsgPrintEdition) XXX_Size() int { + return m.Size() +} +func (m *MsgPrintEdition) XXX_DiscardUnknown() { + xxx_messageInfo_MsgPrintEdition.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgPrintEdition proto.InternalMessageInfo + +type MsgPrintEditionResponse struct { + Collection string `protobuf:"bytes,1,opt,name=collection,proto3" json:"collection,omitempty"` + TokenId string `protobuf:"bytes,2,opt,name=token_id,json=tokenId,proto3" json:"token_id,omitempty"` + Seq uint64 `protobuf:"varint,3,opt,name=seq,proto3" json:"seq,omitempty"` +} + +func (m *MsgPrintEditionResponse) Reset() { *m = MsgPrintEditionResponse{} } +func (m *MsgPrintEditionResponse) String() string { return proto.CompactTextString(m) } +func (*MsgPrintEditionResponse) ProtoMessage() {} +func (*MsgPrintEditionResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_d3dab637c9b79d73, []int{7} +} +func (m *MsgPrintEditionResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgPrintEditionResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgPrintEditionResponse.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 *MsgPrintEditionResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgPrintEditionResponse.Merge(m, src) +} +func (m *MsgPrintEditionResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgPrintEditionResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgPrintEditionResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgPrintEditionResponse proto.InternalMessageInfo + +func (m *MsgPrintEditionResponse) GetCollection() string { + if m != nil { + return m.Collection + } + return "" +} + +func (m *MsgPrintEditionResponse) GetTokenId() string { + if m != nil { + return m.TokenId + } + return "" +} + +func (m *MsgPrintEditionResponse) GetSeq() uint64 { + if m != nil { + return m.Seq + } + return 0 +} + +func init() { + proto.RegisterType((*MsgCreateCollection)(nil), "bitsong.nft.v1beta1.MsgCreateCollection") + proto.RegisterType((*MsgCreateCollectionResponse)(nil), "bitsong.nft.v1beta1.MsgCreateCollectionResponse") + proto.RegisterType((*MsgMintNFT)(nil), "bitsong.nft.v1beta1.MsgMintNFT") + proto.RegisterType((*MsgMintNFTResponse)(nil), "bitsong.nft.v1beta1.MsgMintNFTResponse") + proto.RegisterType((*MsgSendNFT)(nil), "bitsong.nft.v1beta1.MsgSendNFT") + proto.RegisterType((*MsgSendNFTResponse)(nil), "bitsong.nft.v1beta1.MsgSendNFTResponse") + proto.RegisterType((*MsgPrintEdition)(nil), "bitsong.nft.v1beta1.MsgPrintEdition") + proto.RegisterType((*MsgPrintEditionResponse)(nil), "bitsong.nft.v1beta1.MsgPrintEditionResponse") +} + +func init() { proto.RegisterFile("bitsong/nft/v1beta1/tx.proto", fileDescriptor_d3dab637c9b79d73) } + +var fileDescriptor_d3dab637c9b79d73 = []byte{ + // 650 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x95, 0xcd, 0x6e, 0xd3, 0x40, + 0x10, 0xc7, 0xe3, 0x26, 0x4d, 0xe8, 0x0a, 0x89, 0xb2, 0x8d, 0xa8, 0x6b, 0x90, 0x8b, 0x2c, 0x24, + 0xaa, 0x8a, 0xda, 0x49, 0x2b, 0xf5, 0x50, 0x4e, 0xb4, 0x02, 0x09, 0xa1, 0x00, 0x4a, 0x38, 0x71, + 0xa9, 0xfc, 0xb1, 0x71, 0x57, 0x8d, 0x77, 0x83, 0x77, 0x53, 0x35, 0x37, 0xc4, 0x09, 0xe5, 0xc4, + 0x23, 0xf4, 0x09, 0x50, 0x0e, 0x3c, 0x04, 0xc7, 0x8a, 0x13, 0x47, 0x94, 0x20, 0x85, 0x13, 0xcf, + 0x80, 0xbc, 0x5e, 0x27, 0x69, 0x13, 0x93, 0x2a, 0x45, 0x5c, 0x92, 0x9d, 0x99, 0xff, 0xcc, 0x6a, + 0x7e, 0x1e, 0x8f, 0xc1, 0x3d, 0x07, 0x73, 0x46, 0x89, 0x6f, 0x91, 0x3a, 0xb7, 0x4e, 0xca, 0x0e, + 0xe2, 0x76, 0xd9, 0xe2, 0xa7, 0x66, 0x33, 0xa4, 0x9c, 0xc2, 0x15, 0x19, 0x35, 0x49, 0x9d, 0x9b, + 0x32, 0xaa, 0x15, 0x7d, 0xea, 0x53, 0x11, 0xb7, 0xa2, 0x53, 0x2c, 0xd5, 0x56, 0x5d, 0xca, 0x02, + 0xca, 0xac, 0x80, 0xf9, 0xd6, 0x49, 0x39, 0xfa, 0x93, 0x81, 0xdb, 0x76, 0x80, 0x09, 0xb5, 0xc4, + 0xaf, 0x74, 0xad, 0xc5, 0xda, 0xc3, 0xb8, 0x48, 0x6c, 0xc4, 0x21, 0xe3, 0xf3, 0x02, 0x58, 0xa9, + 0x30, 0xff, 0x20, 0x44, 0x36, 0x47, 0x07, 0xb4, 0xd1, 0x40, 0x2e, 0xc7, 0x94, 0xc0, 0x3b, 0x20, + 0xcf, 0xda, 0x81, 0x43, 0x1b, 0xaa, 0x72, 0x5f, 0xd9, 0x58, 0xaa, 0x4a, 0x0b, 0x42, 0x90, 0x23, + 0x76, 0x80, 0xd4, 0x05, 0xe1, 0x15, 0x67, 0xb8, 0x0c, 0xb2, 0xad, 0x10, 0xab, 0x59, 0xe1, 0x8a, + 0x8e, 0x70, 0x1b, 0x14, 0xdc, 0xa8, 0x22, 0x0d, 0xd5, 0x5c, 0xe4, 0xdd, 0x57, 0xbf, 0x7d, 0xd9, + 0x2a, 0xca, 0x8b, 0x9f, 0x78, 0x5e, 0x88, 0x18, 0xab, 0xf1, 0x10, 0x13, 0xbf, 0x9a, 0x08, 0x61, + 0x09, 0xe4, 0x03, 0x4c, 0x38, 0x0a, 0xd5, 0xc5, 0x19, 0x29, 0x52, 0x07, 0x77, 0xc1, 0x92, 0xdd, + 0xe2, 0x47, 0x34, 0xc4, 0xbc, 0xad, 0xe6, 0x67, 0x24, 0x8d, 0xa4, 0x7b, 0x8f, 0x3f, 0x9e, 0xad, + 0x67, 0x7e, 0x9d, 0xad, 0x67, 0x3e, 0x0c, 0xba, 0x9b, 0xc9, 0xfd, 0x9d, 0x41, 0x77, 0xd3, 0x88, + 0x33, 0xb7, 0x98, 0x77, 0x2c, 0x9e, 0xcf, 0x14, 0x30, 0xc6, 0x0e, 0xb8, 0x3b, 0xc5, 0x5d, 0x45, + 0xac, 0x49, 0x09, 0x43, 0xb0, 0x08, 0x16, 0x3d, 0x44, 0x68, 0x20, 0xb1, 0xc5, 0x86, 0xd1, 0x59, + 0x00, 0xa0, 0xc2, 0xfc, 0x0a, 0x26, 0xfc, 0xe5, 0xb3, 0x37, 0x50, 0x07, 0xc0, 0x1d, 0xa6, 0x4a, + 0xe5, 0x98, 0x07, 0xae, 0x81, 0x1b, 0x9c, 0x1e, 0x23, 0x72, 0x88, 0x3d, 0x09, 0xba, 0x20, 0xec, + 0xe7, 0xde, 0x90, 0x7f, 0x76, 0x92, 0x7f, 0x6e, 0xc4, 0x7f, 0x2e, 0x96, 0x21, 0x72, 0x71, 0x13, + 0x23, 0xc2, 0xd5, 0xc2, 0x2c, 0x96, 0x43, 0xe9, 0x5e, 0x79, 0x9c, 0xa5, 0x2c, 0x16, 0xa1, 0x5c, + 0x9b, 0x44, 0x29, 0xbb, 0x37, 0x5e, 0x01, 0x38, 0xb2, 0x86, 0xe0, 0xe6, 0x67, 0x62, 0xfc, 0x54, + 0x04, 0xdd, 0x1a, 0x22, 0xde, 0x35, 0xe9, 0x96, 0x40, 0x9e, 0x21, 0xe2, 0xa1, 0x30, 0xe6, 0xfb, + 0x37, 0x6e, 0xb1, 0xee, 0x22, 0xb7, 0xdc, 0xbc, 0xdc, 0xe2, 0x62, 0x29, 0xdc, 0x64, 0x5f, 0x46, + 0x51, 0x70, 0x93, 0x56, 0xc2, 0xcd, 0xf8, 0xad, 0x80, 0x5b, 0x15, 0xe6, 0xbf, 0x0e, 0x31, 0xe1, + 0x4f, 0x3d, 0x2c, 0x3a, 0xbc, 0x1e, 0x81, 0xff, 0x34, 0x39, 0xbb, 0x29, 0x93, 0xa3, 0x4f, 0x12, + 0x18, 0x6f, 0xce, 0xa8, 0x83, 0xd5, 0x4b, 0xae, 0x7f, 0x30, 0x43, 0xd1, 0x3b, 0xc4, 0xd0, 0x3b, + 0xf1, 0xd8, 0x73, 0xd5, 0xe8, 0xb8, 0xdd, 0xc9, 0x82, 0x6c, 0x85, 0xf9, 0x90, 0x80, 0xe5, 0x89, + 0xed, 0xb8, 0x61, 0x4e, 0x59, 0xd4, 0xe6, 0x94, 0xbd, 0xa0, 0x95, 0xae, 0xaa, 0x1c, 0x36, 0x51, + 0x03, 0x85, 0x64, 0x4f, 0xac, 0xa7, 0x25, 0x4b, 0x81, 0xf6, 0x70, 0x86, 0x60, 0xbc, 0x68, 0xf2, + 0x7a, 0xa4, 0x16, 0x95, 0x82, 0xf4, 0xa2, 0x97, 0x46, 0x0f, 0x3a, 0xe0, 0xe6, 0x85, 0xb1, 0x7b, + 0x90, 0x96, 0x38, 0xae, 0xd2, 0x1e, 0x5d, 0x45, 0x95, 0xdc, 0xa1, 0x2d, 0xbe, 0x1f, 0x74, 0x37, + 0x95, 0xfd, 0x17, 0x5f, 0x7b, 0xba, 0x72, 0xde, 0xd3, 0x95, 0x1f, 0x3d, 0x5d, 0xf9, 0xd4, 0xd7, + 0x33, 0xe7, 0x7d, 0x3d, 0xf3, 0xbd, 0xaf, 0x67, 0xde, 0x96, 0x7d, 0xcc, 0x8f, 0x5a, 0x8e, 0xe9, + 0xd2, 0xc0, 0x92, 0x85, 0x69, 0xbd, 0x8e, 0x5d, 0x6c, 0x37, 0x2c, 0x9f, 0x6e, 0x25, 0x9f, 0xdb, + 0x53, 0x31, 0x4b, 0xbc, 0xdd, 0x44, 0xcc, 0xc9, 0x8b, 0x4f, 0xdf, 0xce, 0x9f, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x06, 0xf1, 0xea, 0xdd, 0x8c, 0x07, 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 { + CreateCollection(ctx context.Context, in *MsgCreateCollection, opts ...grpc.CallOption) (*MsgCreateCollectionResponse, error) + MintNFT(ctx context.Context, in *MsgMintNFT, opts ...grpc.CallOption) (*MsgMintNFTResponse, error) + SendNFT(ctx context.Context, in *MsgSendNFT, opts ...grpc.CallOption) (*MsgSendNFTResponse, error) + PrintEdition(ctx context.Context, in *MsgPrintEdition, opts ...grpc.CallOption) (*MsgPrintEditionResponse, error) +} + +type msgClient struct { + cc grpc1.ClientConn +} + +func NewMsgClient(cc grpc1.ClientConn) MsgClient { + return &msgClient{cc} +} + +func (c *msgClient) CreateCollection(ctx context.Context, in *MsgCreateCollection, opts ...grpc.CallOption) (*MsgCreateCollectionResponse, error) { + out := new(MsgCreateCollectionResponse) + err := c.cc.Invoke(ctx, "/bitsong.nft.v1beta1.Msg/CreateCollection", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) MintNFT(ctx context.Context, in *MsgMintNFT, opts ...grpc.CallOption) (*MsgMintNFTResponse, error) { + out := new(MsgMintNFTResponse) + err := c.cc.Invoke(ctx, "/bitsong.nft.v1beta1.Msg/MintNFT", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) SendNFT(ctx context.Context, in *MsgSendNFT, opts ...grpc.CallOption) (*MsgSendNFTResponse, error) { + out := new(MsgSendNFTResponse) + err := c.cc.Invoke(ctx, "/bitsong.nft.v1beta1.Msg/SendNFT", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) PrintEdition(ctx context.Context, in *MsgPrintEdition, opts ...grpc.CallOption) (*MsgPrintEditionResponse, error) { + out := new(MsgPrintEditionResponse) + err := c.cc.Invoke(ctx, "/bitsong.nft.v1beta1.Msg/PrintEdition", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// MsgServer is the server API for Msg service. +type MsgServer interface { + CreateCollection(context.Context, *MsgCreateCollection) (*MsgCreateCollectionResponse, error) + MintNFT(context.Context, *MsgMintNFT) (*MsgMintNFTResponse, error) + SendNFT(context.Context, *MsgSendNFT) (*MsgSendNFTResponse, error) + PrintEdition(context.Context, *MsgPrintEdition) (*MsgPrintEditionResponse, error) +} + +// UnimplementedMsgServer can be embedded to have forward compatible implementations. +type UnimplementedMsgServer struct { +} + +func (*UnimplementedMsgServer) CreateCollection(ctx context.Context, req *MsgCreateCollection) (*MsgCreateCollectionResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method CreateCollection not implemented") +} +func (*UnimplementedMsgServer) MintNFT(ctx context.Context, req *MsgMintNFT) (*MsgMintNFTResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method MintNFT not implemented") +} +func (*UnimplementedMsgServer) SendNFT(ctx context.Context, req *MsgSendNFT) (*MsgSendNFTResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method SendNFT not implemented") +} +func (*UnimplementedMsgServer) PrintEdition(ctx context.Context, req *MsgPrintEdition) (*MsgPrintEditionResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method PrintEdition not implemented") +} + +func RegisterMsgServer(s grpc1.Server, srv MsgServer) { + s.RegisterService(&_Msg_serviceDesc, srv) +} + +func _Msg_CreateCollection_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgCreateCollection) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).CreateCollection(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/bitsong.nft.v1beta1.Msg/CreateCollection", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).CreateCollection(ctx, req.(*MsgCreateCollection)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_MintNFT_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgMintNFT) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).MintNFT(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/bitsong.nft.v1beta1.Msg/MintNFT", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).MintNFT(ctx, req.(*MsgMintNFT)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_SendNFT_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgSendNFT) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).SendNFT(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/bitsong.nft.v1beta1.Msg/SendNFT", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).SendNFT(ctx, req.(*MsgSendNFT)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_PrintEdition_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgPrintEdition) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).PrintEdition(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/bitsong.nft.v1beta1.Msg/PrintEdition", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).PrintEdition(ctx, req.(*MsgPrintEdition)) + } + return interceptor(ctx, in, info, handler) +} + +var Msg_serviceDesc = _Msg_serviceDesc +var _Msg_serviceDesc = grpc.ServiceDesc{ + ServiceName: "bitsong.nft.v1beta1.Msg", + HandlerType: (*MsgServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "CreateCollection", + Handler: _Msg_CreateCollection_Handler, + }, + { + MethodName: "MintNFT", + Handler: _Msg_MintNFT_Handler, + }, + { + MethodName: "SendNFT", + Handler: _Msg_SendNFT_Handler, + }, + { + MethodName: "PrintEdition", + Handler: _Msg_PrintEdition_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "bitsong/nft/v1beta1/tx.proto", +} + +func (m *MsgCreateCollection) 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 *MsgCreateCollection) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgCreateCollection) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + 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] = 0x32 + } + if len(m.Minter) > 0 { + i -= len(m.Minter) + copy(dAtA[i:], m.Minter) + i = encodeVarintTx(dAtA, i, uint64(len(m.Minter))) + i-- + dAtA[i] = 0x2a + } + if len(m.Creator) > 0 { + i -= len(m.Creator) + copy(dAtA[i:], m.Creator) + i = encodeVarintTx(dAtA, i, uint64(len(m.Creator))) + i-- + dAtA[i] = 0x22 + } + if len(m.Uri) > 0 { + i -= len(m.Uri) + copy(dAtA[i:], m.Uri) + i = encodeVarintTx(dAtA, i, uint64(len(m.Uri))) + i-- + dAtA[i] = 0x1a + } + 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] = 0x12 + } + if len(m.Symbol) > 0 { + i -= len(m.Symbol) + copy(dAtA[i:], m.Symbol) + i = encodeVarintTx(dAtA, i, uint64(len(m.Symbol))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgCreateCollectionResponse) 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 *MsgCreateCollectionResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgCreateCollectionResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Denom) > 0 { + i -= len(m.Denom) + copy(dAtA[i:], m.Denom) + i = encodeVarintTx(dAtA, i, uint64(len(m.Denom))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgMintNFT) 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 *MsgMintNFT) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgMintNFT) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Recipient) > 0 { + i -= len(m.Recipient) + copy(dAtA[i:], m.Recipient) + i = encodeVarintTx(dAtA, i, uint64(len(m.Recipient))) + i-- + dAtA[i] = 0x3a + } + if len(m.Minter) > 0 { + i -= len(m.Minter) + copy(dAtA[i:], m.Minter) + i = encodeVarintTx(dAtA, i, uint64(len(m.Minter))) + i-- + dAtA[i] = 0x2a + } + if len(m.Uri) > 0 { + i -= len(m.Uri) + copy(dAtA[i:], m.Uri) + i = encodeVarintTx(dAtA, i, uint64(len(m.Uri))) + i-- + dAtA[i] = 0x22 + } + 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] = 0x1a + } + if len(m.TokenId) > 0 { + i -= len(m.TokenId) + copy(dAtA[i:], m.TokenId) + i = encodeVarintTx(dAtA, i, uint64(len(m.TokenId))) + i-- + dAtA[i] = 0x12 + } + if len(m.Collection) > 0 { + i -= len(m.Collection) + copy(dAtA[i:], m.Collection) + i = encodeVarintTx(dAtA, i, uint64(len(m.Collection))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgMintNFTResponse) 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 *MsgMintNFTResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgMintNFTResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.TokenId) > 0 { + i -= len(m.TokenId) + copy(dAtA[i:], m.TokenId) + i = encodeVarintTx(dAtA, i, uint64(len(m.TokenId))) + i-- + dAtA[i] = 0x12 + } + if len(m.Collection) > 0 { + i -= len(m.Collection) + copy(dAtA[i:], m.Collection) + i = encodeVarintTx(dAtA, i, uint64(len(m.Collection))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgSendNFT) 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 *MsgSendNFT) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgSendNFT) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Recipient) > 0 { + i -= len(m.Recipient) + copy(dAtA[i:], m.Recipient) + i = encodeVarintTx(dAtA, i, uint64(len(m.Recipient))) + i-- + dAtA[i] = 0x22 + } + if len(m.Sender) > 0 { + i -= len(m.Sender) + copy(dAtA[i:], m.Sender) + i = encodeVarintTx(dAtA, i, uint64(len(m.Sender))) + i-- + dAtA[i] = 0x1a + } + if len(m.TokenId) > 0 { + i -= len(m.TokenId) + copy(dAtA[i:], m.TokenId) + i = encodeVarintTx(dAtA, i, uint64(len(m.TokenId))) + i-- + dAtA[i] = 0x12 + } + if len(m.Collection) > 0 { + i -= len(m.Collection) + copy(dAtA[i:], m.Collection) + i = encodeVarintTx(dAtA, i, uint64(len(m.Collection))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgSendNFTResponse) 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 *MsgSendNFTResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgSendNFTResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgPrintEdition) 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 *MsgPrintEdition) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgPrintEdition) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Recipient) > 0 { + i -= len(m.Recipient) + copy(dAtA[i:], m.Recipient) + i = encodeVarintTx(dAtA, i, uint64(len(m.Recipient))) + i-- + dAtA[i] = 0x3a + } + if len(m.Minter) > 0 { + i -= len(m.Minter) + copy(dAtA[i:], m.Minter) + i = encodeVarintTx(dAtA, i, uint64(len(m.Minter))) + i-- + dAtA[i] = 0x2a + } + if len(m.TokenId) > 0 { + i -= len(m.TokenId) + copy(dAtA[i:], m.TokenId) + i = encodeVarintTx(dAtA, i, uint64(len(m.TokenId))) + i-- + dAtA[i] = 0x12 + } + if len(m.Collection) > 0 { + i -= len(m.Collection) + copy(dAtA[i:], m.Collection) + i = encodeVarintTx(dAtA, i, uint64(len(m.Collection))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgPrintEditionResponse) 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 *MsgPrintEditionResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgPrintEditionResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Seq != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.Seq)) + i-- + dAtA[i] = 0x18 + } + if len(m.TokenId) > 0 { + i -= len(m.TokenId) + copy(dAtA[i:], m.TokenId) + i = encodeVarintTx(dAtA, i, uint64(len(m.TokenId))) + i-- + dAtA[i] = 0x12 + } + if len(m.Collection) > 0 { + i -= len(m.Collection) + copy(dAtA[i:], m.Collection) + i = encodeVarintTx(dAtA, i, uint64(len(m.Collection))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintTx(dAtA []byte, offset int, v uint64) int { + offset -= sovTx(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *MsgCreateCollection) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Symbol) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.Name) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.Uri) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.Creator) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.Minter) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.Authority) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgCreateCollectionResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Denom) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgMintNFT) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Collection) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.TokenId) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.Name) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.Uri) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.Minter) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.Recipient) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgMintNFTResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Collection) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.TokenId) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgSendNFT) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Collection) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.TokenId) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.Sender) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.Recipient) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgSendNFTResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgPrintEdition) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Collection) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.TokenId) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.Minter) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.Recipient) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgPrintEditionResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Collection) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.TokenId) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if m.Seq != 0 { + n += 1 + sovTx(uint64(m.Seq)) + } + 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 *MsgCreateCollection) 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: MsgCreateCollection: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgCreateCollection: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Symbol", 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.Symbol = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + 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 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Uri", 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.Uri = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Creator", 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.Creator = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Minter", 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.Minter = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 6: + 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 + 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 *MsgCreateCollectionResponse) 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: MsgCreateCollectionResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgCreateCollectionResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Denom", 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.Denom = 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 *MsgMintNFT) 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: MsgMintNFT: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgMintNFT: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Collection", 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.Collection = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TokenId", 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.TokenId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + 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 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Uri", 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.Uri = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Minter", 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.Minter = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Recipient", 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.Recipient = 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 *MsgMintNFTResponse) 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: MsgMintNFTResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgMintNFTResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Collection", 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.Collection = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TokenId", 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.TokenId = 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 *MsgSendNFT) 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: MsgSendNFT: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgSendNFT: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Collection", 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.Collection = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TokenId", 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.TokenId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Sender", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Sender = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Recipient", 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.Recipient = 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 *MsgSendNFTResponse) 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: MsgSendNFTResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgSendNFTResponse: 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 *MsgPrintEdition) 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: MsgPrintEdition: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgPrintEdition: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Collection", 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.Collection = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TokenId", 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.TokenId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Minter", 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.Minter = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Recipient", 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.Recipient = 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 *MsgPrintEditionResponse) 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: MsgPrintEditionResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgPrintEditionResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Collection", 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.Collection = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TokenId", 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.TokenId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Seq", wireType) + } + m.Seq = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Seq |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipTx(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTx + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTx + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTx + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthTx + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupTx + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthTx + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthTx = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowTx = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupTx = fmt.Errorf("proto: unexpected end of group") +) From 59b13f5626f84c7bde04f9c3c4eb13c7a705f059 Mon Sep 17 00:00:00 2001 From: angelorc Date: Fri, 5 Sep 2025 16:49:58 +0200 Subject: [PATCH 14/15] feat(nft): add authority --- proto/bitsong/nft/v1beta1/nft.proto | 2 +- x/nft/keeper/collection.go | 160 +++++++++-- x/nft/keeper/edition_test.go | 5 +- x/nft/keeper/grpc_query_test.go | 25 +- x/nft/keeper/keeper_test.go | 403 +++++++++++++++++++++++++++- x/nft/keeper/msg_server.go | 18 +- x/nft/keeper/msg_server_test.go | 9 +- x/nft/keeper/nft.go | 66 +++++ 8 files changed, 623 insertions(+), 65 deletions(-) diff --git a/proto/bitsong/nft/v1beta1/nft.proto b/proto/bitsong/nft/v1beta1/nft.proto index ae591b22..fe4fcb34 100644 --- a/proto/bitsong/nft/v1beta1/nft.proto +++ b/proto/bitsong/nft/v1beta1/nft.proto @@ -16,7 +16,7 @@ message Collection { string creator = 6; string minter = 7; // who can mint new nfts, if not set no one can mint - string authority = 8; // who can update name, description and uri, if not set no one can update + string authority = 8; // who can update name and uri, if not set no one can update, this is valid for the all nfts in this collection uint64 num_tokens = 9; } diff --git a/x/nft/keeper/collection.go b/x/nft/keeper/collection.go index e47f5dc2..95745590 100644 --- a/x/nft/keeper/collection.go +++ b/x/nft/keeper/collection.go @@ -5,21 +5,29 @@ import ( "fmt" "strings" + errorsmod "cosmossdk.io/errors" "cosmossdk.io/math" "github.com/bitsongofficial/go-bitsong/x/nft/types" tmcrypto "github.com/cometbft/cometbft/crypto" sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" ) func (k Keeper) CreateCollection( - ctx context.Context, + ctx sdk.Context, creator, - minter sdk.AccAddress, + minter, + authority, symbol, name, uri string, ) (denom string, err error) { - denom, err = k.validateCollectionDenom(ctx, creator, symbol) + creatorAddr, err := k.ac.StringToBytes(creator) + if err != nil { + return "", errorsmod.Wrapf(sdkerrors.ErrInvalidAddress, "invalid creator address: %s", err) + } + + denom, err = k.validateCollectionDenom(ctx, creatorAddr, symbol) if err != nil { return "", err } @@ -35,8 +43,25 @@ func (k Keeper) CreateCollection( Symbol: symbol, Name: name, Uri: uri, - Creator: creator.String(), - Minter: minter.String(), + Creator: creator, + } + + if minter != "" { + _, err = k.ac.StringToBytes(minter) + if err != nil { + return "", errorsmod.Wrapf(sdkerrors.ErrInvalidAddress, "invalid minter address: %s", err) + } + + coll.Minter = minter + } + + if authority != "" { + _, err = k.ac.StringToBytes(authority) + if err != nil { + return "", errorsmod.Wrapf(sdkerrors.ErrInvalidAddress, "invalid authority address: %s", err) + } + + coll.Authority = authority } if err := k.setCollection(ctx, coll); err != nil { @@ -46,6 +71,112 @@ func (k Keeper) CreateCollection( return denom, nil } +func (k Keeper) SetCollectionName(ctx context.Context, authority sdk.AccAddress, denom, name string) error { + coll, err := k.GetCollection(ctx, denom) + if err != nil { + return err + } + + if coll.Authority != authority.String() { + return fmt.Errorf("only the collection authority can change the name") + } + + if err := k.validateCollectionMetadata(name, coll.Uri); err != nil { + return err + } + + coll.Name = name + + return k.setCollection(ctx, coll) +} + +func (k Keeper) SetCollectionUri(ctx context.Context, authority sdk.AccAddress, denom, uri string) error { + coll, err := k.GetCollection(ctx, denom) + if err != nil { + return err + } + + if coll.Authority != authority.String() { + return fmt.Errorf("only the collection authority can change the uri") + } + + if err := k.validateCollectionMetadata(coll.Name, uri); err != nil { + return err + } + + coll.Uri = uri + + return k.setCollection(ctx, coll) +} + +func (k Keeper) SetMinter(ctx context.Context, oldMinter sdk.AccAddress, newMinter *sdk.AccAddress, denom string) error { + coll, err := k.GetCollection(ctx, denom) + if err != nil { + return err + } + + if coll.Minter == "" { + return fmt.Errorf("minting disabled for this collection") + } + + if coll.Minter != oldMinter.String() { + return fmt.Errorf("only the current minter can change the minter") + } + + if newMinter != nil { + coll.Minter = newMinter.String() + } else { + coll.Minter = "" + } + + return k.setCollection(ctx, coll) +} + +func (k Keeper) SetAuthority(ctx context.Context, oldAuthority sdk.AccAddress, newAuthority *sdk.AccAddress, denom string) error { + coll, err := k.GetCollection(ctx, denom) + if err != nil { + return err + } + + if coll.Authority != oldAuthority.String() { + return fmt.Errorf("only the current authority can change the authority") + } + + if newAuthority != nil { + coll.Authority = newAuthority.String() + } else { + coll.Authority = "" + } + + return k.setCollection(ctx, coll) +} + +func (k Keeper) GetMinter(ctx context.Context, denom string) (sdk.AccAddress, error) { + coll, err := k.Collections.Get(ctx, denom) + if err != nil { + return nil, types.ErrCollectionNotFound + } + + if coll.Minter == "" { + return nil, fmt.Errorf("minting disabled for this collection") + } + + return sdk.AccAddressFromBech32(coll.Minter) +} + +func (k Keeper) GetAuthority(ctx context.Context, denom string) (sdk.AccAddress, error) { + coll, err := k.Collections.Get(ctx, denom) + if err != nil { + return nil, types.ErrCollectionNotFound + } + + if coll.Authority == "" { + return nil, fmt.Errorf("no authority set for this collection") + } + + return sdk.AccAddressFromBech32(coll.Authority) +} + func (k Keeper) GetSupply(ctx context.Context, denom string) math.Int { supply, err := k.Supply.Get(ctx, denom) if err != nil { @@ -65,17 +196,13 @@ func (k Keeper) HasCollection(ctx context.Context, denom string) bool { return has && err == nil } -func (k Keeper) GetMinter(ctx context.Context, denom string) (sdk.AccAddress, error) { +func (k Keeper) GetCollection(ctx context.Context, denom string) (types.Collection, error) { coll, err := k.Collections.Get(ctx, denom) if err != nil { - return nil, types.ErrCollectionNotFound - } - - if coll.Minter == "" { - return nil, fmt.Errorf("minting disabled for this collection") + return types.Collection{}, types.ErrCollectionNotFound } - return sdk.AccAddressFromBech32(coll.Minter) + return coll, nil } func (k Keeper) setSupply(ctx context.Context, denom string, supply math.Int) error { @@ -125,15 +252,6 @@ func (k Keeper) setCollection(ctx context.Context, coll types.Collection) error return k.Collections.Set(ctx, coll.Denom, coll) } -func (k Keeper) getCollection(ctx context.Context, denom string) (types.Collection, error) { - coll, err := k.Collections.Get(ctx, denom) - if err != nil { - return types.Collection{}, types.ErrCollectionNotFound - } - - return coll, nil -} - func (k Keeper) validateCollectionMetadata(name, uri string) error { if len(name) > types.MaxNameLength { return fmt.Errorf("name cannot be longer than %d characters", types.MaxNameLength) diff --git a/x/nft/keeper/edition_test.go b/x/nft/keeper/edition_test.go index 8ef70b88..03645a54 100644 --- a/x/nft/keeper/edition_test.go +++ b/x/nft/keeper/edition_test.go @@ -3,8 +3,9 @@ package keeper_test func (suite *KeeperTestSuite) TestPrintEdition() { collectionDenom, err := suite.keeper.CreateCollection( suite.ctx, - creator1, - minter1, + creator1.String(), + minter1.String(), + "", testCollection1.Symbol, testCollection1.Name, testCollection1.Uri, diff --git a/x/nft/keeper/grpc_query_test.go b/x/nft/keeper/grpc_query_test.go index 38f79f61..adf38b14 100644 --- a/x/nft/keeper/grpc_query_test.go +++ b/x/nft/keeper/grpc_query_test.go @@ -7,8 +7,9 @@ import ( func (suite *KeeperTestSuite) TestQueryCollection() { collectionDenom, err := suite.keeper.CreateCollection( suite.ctx, - creator1, - minter1, + creator1.String(), + minter1.String(), + "", testCollection1.Symbol, testCollection1.Name, testCollection1.Uri, @@ -28,8 +29,9 @@ func (suite *KeeperTestSuite) TestQueryCollection() { func (suite *KeeperTestSuite) TestQueryOwnerOf() { collectionDenom, err := suite.keeper.CreateCollection( suite.ctx, - creator1, - minter1, + creator1.String(), + minter1.String(), + "", testCollection1.Symbol, testCollection1.Name, testCollection1.Uri, @@ -58,8 +60,9 @@ func (suite *KeeperTestSuite) TestQueryOwnerOf() { func (suite *KeeperTestSuite) TestQueryNftInfo() { collectionDenom, err := suite.keeper.CreateCollection( suite.ctx, - creator1, - minter1, + creator1.String(), + minter1.String(), + "", testCollection1.Symbol, testCollection1.Name, testCollection1.Uri, @@ -92,8 +95,9 @@ func (suite *KeeperTestSuite) TestQueryNftInfo() { func (suite *KeeperTestSuite) TestQueryNftsOfOwner() { collectionDenom, err := suite.keeper.CreateCollection( suite.ctx, - creator1, - minter1, + creator1.String(), + minter1.String(), + "", testCollection1.Symbol, testCollection1.Name, testCollection1.Uri, @@ -134,8 +138,9 @@ func (suite *KeeperTestSuite) TestQueryNftsOfOwner() { func (suite *KeeperTestSuite) TestQueryNftsByOwner() { collectionDenom, err := suite.keeper.CreateCollection( suite.ctx, - creator1, - minter1, + creator1.String(), + minter1.String(), + "", testCollection1.Symbol, testCollection1.Name, testCollection1.Uri, diff --git a/x/nft/keeper/keeper_test.go b/x/nft/keeper/keeper_test.go index 44b34b08..1f7ae191 100644 --- a/x/nft/keeper/keeper_test.go +++ b/x/nft/keeper/keeper_test.go @@ -1,6 +1,7 @@ package keeper_test import ( + "strings" "testing" "cosmossdk.io/collections" @@ -24,6 +25,9 @@ var ( owner1 = sdk.AccAddress(tmhash.SumTruncated([]byte("owner1"))) owner2 = sdk.AccAddress(tmhash.SumTruncated([]byte("owner2"))) + authority1 = sdk.AccAddress(tmhash.SumTruncated([]byte("authority1"))) + authority2 = sdk.AccAddress(tmhash.SumTruncated([]byte("authority2"))) + testCollection1 = types.Collection{ Name: "My NFT Collection", Symbol: "MYNFT", @@ -32,6 +36,14 @@ var ( } expectedDenom1 = "nft9436DDD23FB751AEA7BC6C767F20F943DD735E06" + testCollection2 = types.Collection{ + Name: "My Second NFT Collection", + Symbol: "MYNFT2", + Uri: "ipfs://my-nft-collection-metadata.json", + Minter: minter1.String(), + } + expectedDenom2 = "nft6C5B4EC9EA22932F217B0A0CDCA3A987B8271CD0" + testNft1 = types.Nft{ TokenId: "1", Name: "My First NFT", @@ -80,8 +92,9 @@ func TestKeeperSuite(t *testing.T) { func (suite *KeeperTestSuite) TestCreateCollection() { denom, err := suite.keeper.CreateCollection( suite.ctx, - creator1, - minter1, + creator1.String(), + minter1.String(), + "", testCollection1.Symbol, testCollection1.Name, testCollection1.Uri, @@ -91,20 +104,134 @@ func (suite *KeeperTestSuite) TestCreateCollection() { _, err = suite.keeper.CreateCollection( suite.ctx, - creator1, - minter1, + creator1.String(), + minter1.String(), + authority1.String(), + testCollection1.Symbol, + testCollection1.Name, + testCollection1.Uri, + ) + suite.Error(err) + + denom, err = suite.keeper.CreateCollection( + suite.ctx, + creator1.String(), + minter1.String(), + authority1.String(), + testCollection2.Symbol, + testCollection2.Name, + testCollection2.Uri, + ) + suite.NoError(err) + suite.Equal(expectedDenom2, denom) +} + +func (suite *KeeperTestSuite) TestSetCollectionName() { + collectionDenom, err := suite.keeper.CreateCollection( + suite.ctx, + creator1.String(), + minter1.String(), + authority1.String(), testCollection1.Symbol, testCollection1.Name, testCollection1.Uri, ) + suite.NoError(err) + suite.Equal(expectedDenom1, collectionDenom) + + err = suite.keeper.SetCollectionName(suite.ctx, authority1, collectionDenom, "New Collection Name") + suite.NoError(err) + + collection, err := suite.keeper.GetCollection(suite.ctx, collectionDenom) + suite.NoError(err) + suite.Equal("New Collection Name", collection.Name) + + err = suite.keeper.SetCollectionName(suite.ctx, creator2, collectionDenom, "Another Collection Name") + suite.Error(err) + + err = suite.keeper.SetCollectionName(suite.ctx, creator1, collectionDenom, strings.Repeat("a", 65)) + suite.Error(err) + + // test setting name on a collection without authority + collectionDenom2, err := suite.keeper.CreateCollection( + suite.ctx, + creator1.String(), + minter2.String(), + "", + testCollection2.Symbol, + testCollection2.Name, + testCollection2.Uri, + ) + suite.NoError(err) + suite.Equal(expectedDenom2, collectionDenom2) + + err = suite.keeper.SetCollectionName(suite.ctx, creator1, collectionDenom2, "New Collection Name") + suite.Error(err) + suite.Contains(err.Error(), "only the collection authority can change the name") + + // create a collection without minter and authority + _, err = suite.keeper.CreateCollection( + suite.ctx, + creator2.String(), + "", + "", + "COLL3", + "Collection 3", + "ipfs://collection-3-metadata.json", + ) + suite.NoError(err) +} + +func (suite *KeeperTestSuite) TestSetCollectionUri() { + collectionDenom, err := suite.keeper.CreateCollection( + suite.ctx, + creator1.String(), + minter1.String(), + authority1.String(), + testCollection1.Symbol, + testCollection1.Name, + testCollection1.Uri, + ) + suite.NoError(err) + suite.Equal(expectedDenom1, collectionDenom) + + err = suite.keeper.SetCollectionUri(suite.ctx, authority1, collectionDenom, "ipfs://new-uri.json") + suite.NoError(err) + + collection, err := suite.keeper.GetCollection(suite.ctx, collectionDenom) + suite.NoError(err) + suite.Equal("ipfs://new-uri.json", collection.Uri) + + err = suite.keeper.SetCollectionUri(suite.ctx, creator2, collectionDenom, "ipfs://another-uri.json") + suite.Error(err) + + err = suite.keeper.SetCollectionUri(suite.ctx, authority1, collectionDenom, strings.Repeat("a", 165)) + suite.Error(err) + + // test setting uri on a collection without authority + collectionDenom2, err := suite.keeper.CreateCollection( + suite.ctx, + creator1.String(), + minter2.String(), + "", + testCollection2.Symbol, + testCollection2.Name, + testCollection2.Uri, + ) + suite.NoError(err) + suite.Equal(expectedDenom2, collectionDenom2) + + err = suite.keeper.SetCollectionUri(suite.ctx, creator1, collectionDenom2, "ipfs://new-uri.json") suite.Error(err) + suite.Contains(err.Error(), "only the collection authority can change the uri") } func (suite *KeeperTestSuite) TestMintNFT() { collectionDenom, err := suite.keeper.CreateCollection( suite.ctx, - creator1, - minter1, + creator1.String(), + minter1.String(), + "", testCollection1.Symbol, testCollection1.Name, testCollection1.Uri, @@ -142,13 +269,107 @@ func (suite *KeeperTestSuite) TestMintNFT() { supply = suite.keeper.GetSupply(suite.ctx, collectionDenom) suite.Equal(math.NewInt(2), supply) + + // collection with no minter + collectionDenom2, err := suite.keeper.CreateCollection( + suite.ctx, + creator2.String(), + "", + "", + testCollection2.Symbol, + testCollection2.Name, + testCollection2.Uri, + ) + suite.NoError(err) + + err = suite.keeper.MintNFT( + suite.ctx, + creator2, + owner2, + collectionDenom2, + testNft3.TokenId, + testNft3.Name, + testNft3.Uri, + ) + suite.Error(err) + suite.Contains(err.Error(), "minting disabled for this collection") +} + +func (suite *KeeperTestSuite) TestSetMinter() { + collectionDenom, err := suite.keeper.CreateCollection( + suite.ctx, + creator1.String(), + minter1.String(), + "", + testCollection1.Symbol, + testCollection1.Name, + testCollection1.Uri, + ) + suite.NoError(err) + + err = suite.keeper.SetMinter(suite.ctx, minter1, &minter2, collectionDenom) + suite.NoError(err) + + minter, err := suite.keeper.GetMinter(suite.ctx, collectionDenom) + suite.NoError(err) + suite.Equal(minter2.String(), minter.String()) + + err = suite.keeper.SetMinter(suite.ctx, minter1, &minter1, collectionDenom) + suite.Error(err) + + err = suite.keeper.SetMinter(suite.ctx, minter2, nil, collectionDenom) + suite.NoError(err) + + _, err = suite.keeper.GetMinter(suite.ctx, collectionDenom) + suite.Error(err) + suite.Contains(err.Error(), "minting disabled for this collection") + + err = suite.keeper.SetMinter(suite.ctx, minter2, &minter1, collectionDenom) + suite.Error(err) + suite.Contains(err.Error(), "minting disabled for this collection") +} + +func (suite *KeeperTestSuite) TestSetAuthority() { + collectionDenom, err := suite.keeper.CreateCollection( + suite.ctx, + creator1.String(), + minter1.String(), + authority1.String(), + testCollection1.Symbol, + testCollection1.Name, + testCollection1.Uri, + ) + suite.NoError(err) + + err = suite.keeper.SetAuthority(suite.ctx, authority1, &authority2, collectionDenom) + suite.NoError(err) + + collection, err := suite.keeper.GetCollection(suite.ctx, collectionDenom) + suite.NoError(err) + suite.Equal(authority2.String(), collection.Authority) + + err = suite.keeper.SetAuthority(suite.ctx, authority1, &creator2, collectionDenom) + suite.Error(err) + suite.Contains(err.Error(), "only the current authority can change the authority") + + err = suite.keeper.SetAuthority(suite.ctx, authority2, nil, collectionDenom) + suite.NoError(err) + + collection, err = suite.keeper.GetCollection(suite.ctx, collectionDenom) + suite.NoError(err) + suite.Equal("", collection.Authority) + + err = suite.keeper.SetAuthority(suite.ctx, authority2, &creator2, collectionDenom) + suite.Error(err) + suite.Contains(err.Error(), "only the current authority can change the authority") } func (suite *KeeperTestSuite) TestSendNFT() { collectionDenom, err := suite.keeper.CreateCollection( suite.ctx, - creator1, - minter1, + creator1.String(), + minter1.String(), + "", testCollection1.Symbol, testCollection1.Name, testCollection1.Uri, @@ -199,3 +420,169 @@ func (suite *KeeperTestSuite) TestSendNFT() { suite.NoError(err) suite.Equal(owner2.String(), nft.Owner) } + +func (suite *KeeperTestSuite) TestSetNFTName() { + collectionDenom, err := suite.keeper.CreateCollection( + suite.ctx, + creator1.String(), + minter1.String(), + authority1.String(), + testCollection1.Symbol, + testCollection1.Name, + testCollection1.Uri, + ) + suite.NoError(err) + + err = suite.keeper.MintNFT( + suite.ctx, + minter1, + owner1, + collectionDenom, + testNft1.TokenId, + testNft1.Name, + testNft1.Uri, + ) + suite.NoError(err) + + err = suite.keeper.SetNFTName(suite.ctx, authority1, collectionDenom, testNft1.TokenId, "New NFT Name") + suite.NoError(err) + + nft, err := suite.keeper.NFTs.Get(suite.ctx, collections.Join(collectionDenom, testNft1.TokenId)) + suite.NoError(err) + suite.Equal("New NFT Name", nft.Name) + + err = suite.keeper.SetNFTName(suite.ctx, owner2, collectionDenom, testNft1.TokenId, "Another NFT Name") + suite.Error(err) + suite.Contains(err.Error(), "only the collection authority can set NFT name") + + err = suite.keeper.SetNFTName(suite.ctx, authority1, collectionDenom, testNft1.TokenId, strings.Repeat("a", 165)) + suite.Error(err) + suite.Contains(err.Error(), "name length exceeds maximum") + + // test setting name on a collection without authority + collectionDenom2, err := suite.keeper.CreateCollection( + suite.ctx, + creator2.String(), + minter2.String(), + "", + testCollection2.Symbol, + testCollection2.Name, + testCollection2.Uri, + ) + suite.NoError(err) + + err = suite.keeper.MintNFT( + suite.ctx, + minter2, + owner2, + collectionDenom2, + testNft2.TokenId, + testNft2.Name, + testNft2.Uri, + ) + suite.NoError(err) + + err = suite.keeper.SetNFTName(suite.ctx, owner2, collectionDenom2, testNft2.TokenId, "New NFT Name") + suite.Error(err) + suite.Contains(err.Error(), "no authority, cannot set NFT name") + + // create a collection with authority + collectionDenom3, err := suite.keeper.CreateCollection( + suite.ctx, + creator1.String(), + minter1.String(), + authority1.String(), + testCollection1.Symbol+"3", + testCollection1.Name, + testCollection1.Uri, + ) + suite.NoError(err) + + err = suite.keeper.MintNFT( + suite.ctx, + minter1, + owner1, + collectionDenom3, + testNft3.TokenId, + testNft3.Name, + testNft3.Uri, + ) + suite.NoError(err) + + err = suite.keeper.SetNFTName(suite.ctx, owner1, collectionDenom3, testNft3.TokenId, "New NFT Name") + suite.Error(err) + suite.Contains(err.Error(), "only the collection authority can set NFT name") + + err = suite.keeper.SetNFTName(suite.ctx, authority1, collectionDenom3, testNft3.TokenId, "New NFT Name") + suite.NoError(err) + + nft, err = suite.keeper.NFTs.Get(suite.ctx, collections.Join(collectionDenom3, testNft3.TokenId)) + suite.NoError(err) + suite.Equal("New NFT Name", nft.Name) +} + +func (suite *KeeperTestSuite) TestSetNFTUri() { + collectionDenom, err := suite.keeper.CreateCollection( + suite.ctx, + creator1.String(), + minter1.String(), + authority1.String(), + testCollection1.Symbol, + testCollection1.Name, + testCollection1.Uri, + ) + suite.NoError(err) + + err = suite.keeper.MintNFT( + suite.ctx, + minter1, + owner1, + collectionDenom, + testNft1.TokenId, + testNft1.Name, + testNft1.Uri, + ) + suite.NoError(err) + + err = suite.keeper.SetNFTUri(suite.ctx, authority1, collectionDenom, testNft1.TokenId, "ipfs://new-uri.json") + suite.NoError(err) + + nft, err := suite.keeper.NFTs.Get(suite.ctx, collections.Join(collectionDenom, testNft1.TokenId)) + suite.NoError(err) + suite.Equal("ipfs://new-uri.json", nft.Uri) + + err = suite.keeper.SetNFTUri(suite.ctx, owner2, collectionDenom, testNft1.TokenId, "ipfs://another-uri.json") + suite.Error(err) + suite.Contains(err.Error(), "only the collection authority can set NFT uri") + + err = suite.keeper.SetNFTUri(suite.ctx, authority1, collectionDenom, testNft1.TokenId, strings.Repeat("a", 165)) + suite.Error(err) + suite.Contains(err.Error(), "URI length exceeds maximum") + + // test setting uri on a collection without authority + collectionDenom2, err := suite.keeper.CreateCollection( + suite.ctx, + creator2.String(), + minter2.String(), + "", + testCollection2.Symbol, + testCollection2.Name, + testCollection2.Uri, + ) + suite.NoError(err) + + err = suite.keeper.MintNFT( + suite.ctx, + minter2, + owner2, + collectionDenom2, + testNft2.TokenId, + testNft2.Name, + testNft2.Uri, + ) + suite.NoError(err) + + err = suite.keeper.SetNFTUri(suite.ctx, owner2, collectionDenom2, testNft2.TokenId, "ipfs://new-uri.json") + suite.Error(err) + suite.Contains(err.Error(), "no authority, cannot set NFT uri") +} diff --git a/x/nft/keeper/msg_server.go b/x/nft/keeper/msg_server.go index 77632c3a..1847e463 100644 --- a/x/nft/keeper/msg_server.go +++ b/x/nft/keeper/msg_server.go @@ -22,23 +22,7 @@ func NewMsgServerImpl(keeper Keeper) types.MsgServer { func (k msgServer) CreateCollection(goCtx context.Context, msg *types.MsgCreateCollection) (*types.MsgCreateCollectionResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) - creator, err := k.ac.StringToBytes(msg.Creator) - if err != nil { - return nil, errorsmod.Wrapf(sdkerrors.ErrInvalidAddress, "invalid creator address: %s", err) - } - - minter, err := k.ac.StringToBytes(msg.Minter) - if err != nil { - return nil, errorsmod.Wrapf(sdkerrors.ErrInvalidAddress, "invalid minter address: %s", err) - } - - // TODO: add authority - /*authority, err := k.ac.StringToBytes(msg.Authority) - if err != nil { - return nil, errorsmod.Wrapf(sdkerrors.ErrInvalidAddress, "invalid authority address: %s", err) - }*/ - - denom, err := k.Keeper.CreateCollection(ctx, creator, minter, msg.Symbol, msg.Name, msg.Uri) + denom, err := k.Keeper.CreateCollection(ctx, msg.Creator, msg.Minter, msg.Authority, msg.Symbol, msg.Name, msg.Uri) if err != nil { return nil, err } diff --git a/x/nft/keeper/msg_server_test.go b/x/nft/keeper/msg_server_test.go index d09b5578..87e5a4de 100644 --- a/x/nft/keeper/msg_server_test.go +++ b/x/nft/keeper/msg_server_test.go @@ -17,7 +17,7 @@ func (suite *KeeperTestSuite) TestMsgCreateCollection() { Creator: creator1.String(), Minter: minter1.String(), Name: testCollection1.Name, - Symbol: testCollection1.Symbol, + Symbol: testCollection1.Symbol + "0", Uri: testCollection1.Uri, }, }, @@ -58,8 +58,7 @@ func (suite *KeeperTestSuite) TestMsgCreateCollection() { expectedErrorMsg: "invalid minter address: decoding bech32 failed", }, { - // TODO: this should be valid as minter can be empty (meaning no one can mint) - name: "invalid empty minter address", + name: "valid empty minter address", msg: &types.MsgCreateCollection{ Creator: creator1.String(), Minter: "", @@ -67,8 +66,6 @@ func (suite *KeeperTestSuite) TestMsgCreateCollection() { Symbol: testCollection1.Symbol, Uri: testCollection1.Uri, }, - expectError: true, - expectedErrorMsg: "invalid minter address: empty address string", }, { name: "should fail on empty symbol", @@ -356,7 +353,7 @@ func (suite *KeeperTestSuite) TestMsgPrintEdition() { Creator: creator1.String(), Minter: minter1.String(), Name: testCollection1.Name, - Symbol: testCollection1.Symbol, + Symbol: testCollection1.Symbol + "3", Uri: testCollection1.Uri, } res, err := suite.msgServer.CreateCollection(suite.ctx, createCollectionMsg) diff --git a/x/nft/keeper/nft.go b/x/nft/keeper/nft.go index 655f3801..0423db60 100644 --- a/x/nft/keeper/nft.go +++ b/x/nft/keeper/nft.go @@ -115,6 +115,72 @@ func (k Keeper) SendNFT(ctx context.Context, fromAddr, toAddr sdk.AccAddress, co return nil } +func (k Keeper) SetNFTName(ctx context.Context, authority sdk.AccAddress, collectionDenom, tokenId, name string) error { + coll, err := k.Collections.Get(ctx, collectionDenom) + if err != nil { + return types.ErrCollectionNotFound + } + + if coll.Authority == "" { + return fmt.Errorf("no authority, cannot set NFT name") + } + + collectionAuthority, err := sdk.AccAddressFromBech32(coll.Authority) + if err != nil { + return fmt.Errorf("invalid authority address: %w", err) + } + + if !authority.Equals(collectionAuthority) { + return fmt.Errorf("only the collection authority can set NFT name") + } + + nft, err := k.GetNft(ctx, collectionDenom, tokenId) + if err != nil { + return err + } + + err = k.validateNftMetadata(tokenId, name, nft.Uri) + if err != nil { + return err + } + + nft.Name = name + return k.setNft(ctx, *nft) +} + +func (k Keeper) SetNFTUri(ctx context.Context, authority sdk.AccAddress, collectionDenom, tokenId, uri string) error { + coll, err := k.Collections.Get(ctx, collectionDenom) + if err != nil { + return types.ErrCollectionNotFound + } + + if coll.Authority == "" { + return fmt.Errorf("no authority, cannot set NFT uri") + } + + collectionAuthority, err := sdk.AccAddressFromBech32(coll.Authority) + if err != nil { + return fmt.Errorf("invalid authority address: %w", err) + } + + if !authority.Equals(collectionAuthority) { + return fmt.Errorf("only the collection authority can set NFT uri") + } + + nft, err := k.GetNft(ctx, collectionDenom, tokenId) + if err != nil { + return err + } + + err = k.validateNftMetadata(tokenId, nft.Name, uri) + if err != nil { + return err + } + + nft.Uri = uri + return k.setNft(ctx, *nft) +} + func (k Keeper) GetNft(ctx context.Context, collectionDenom, tokenId string) (*types.Nft, error) { nftKey := collections.Join(collectionDenom, tokenId) has, err := k.NFTs.Has(ctx, nftKey) From 45df9c0f5a1321cdc2a9c9f4e0ecc5fa7e9522ec Mon Sep 17 00:00:00 2001 From: angelorc Date: Mon, 8 Sep 2025 10:52:40 +0200 Subject: [PATCH 15/15] feat(drop): implement drop module - Added Keeper methods for creating and retrieving drops. - Implemented validation for drop rules including start and end time checks. - Introduced RuleEngine to manage and validate rules. - Created EndTimeRule and StartTimeRule for specific rule validations. - Added protobuf definitions for Drop and Rule types. - Established collections for storing drops and rules with appropriate prefixes. - Implemented unit tests for rules engine validation. --- proto/bitsong/drop/v1beta1/drop.proto | 40 ++ proto/buf.yaml | 3 + x/drop/keeper/drop.go | 66 ++ x/drop/keeper/keeper.go | 59 ++ x/drop/rules/end_time.go | 22 + x/drop/rules/engine.go | 78 +++ x/drop/rules/engine_test.go | 32 + x/drop/rules/start_time.go | 22 + x/drop/types/drop.pb.go | 867 ++++++++++++++++++++++++++ x/drop/types/keys.go | 17 + 10 files changed, 1206 insertions(+) create mode 100644 proto/bitsong/drop/v1beta1/drop.proto create mode 100644 x/drop/keeper/drop.go create mode 100644 x/drop/keeper/keeper.go create mode 100644 x/drop/rules/end_time.go create mode 100644 x/drop/rules/engine.go create mode 100644 x/drop/rules/engine_test.go create mode 100644 x/drop/rules/start_time.go create mode 100644 x/drop/types/drop.pb.go create mode 100644 x/drop/types/keys.go diff --git a/proto/bitsong/drop/v1beta1/drop.proto b/proto/bitsong/drop/v1beta1/drop.proto new file mode 100644 index 00000000..81031cab --- /dev/null +++ b/proto/bitsong/drop/v1beta1/drop.proto @@ -0,0 +1,40 @@ +syntax = "proto3"; +package bitsong.drop.v1beta1; + +option go_package = "github.com/bitsongofficial/go-bitsong/x/drop/types"; + +import "gogoproto/gogo.proto"; +import "google/protobuf/timestamp.proto"; +import "amino/amino.proto"; + +message Template { + option (gogoproto.goproto_getters) = false; + + string name = 1; + string uri = 2; +} + +message Rule { + option (gogoproto.goproto_getters) = false; + + uint64 id = 1; + + google.protobuf.Timestamp start_time = 2 + [(gogoproto.stdtime) = true, (gogoproto.nullable) = false, (amino.dont_omitempty) = true]; + + google.protobuf.Timestamp end_time = 3 + [(gogoproto.stdtime) = true, (gogoproto.nullable) = false, (amino.dont_omitempty) = true]; +} + +message Drop { + option (gogoproto.goproto_getters) = false; + + string collection = 1; + + uint64 max_available = 2; + + Template template = 3; + + // Template hidden_template + // bool is_hidden +} \ No newline at end of file diff --git a/proto/buf.yaml b/proto/buf.yaml index 0d6465bd..cc5ff316 100644 --- a/proto/buf.yaml +++ b/proto/buf.yaml @@ -5,9 +5,12 @@ deps: - buf.build/cosmos/cosmos-proto - buf.build/cosmos/gogo-proto - buf.build/googleapis/googleapis + - buf.build/protocolbuffers/wellknowntypes breaking: use: - FILE + ignore: + - testpb lint: use: - STANDARD diff --git a/x/drop/keeper/drop.go b/x/drop/keeper/drop.go new file mode 100644 index 00000000..0df59412 --- /dev/null +++ b/x/drop/keeper/drop.go @@ -0,0 +1,66 @@ +package keeper + +import ( + "context" + "fmt" + + "cosmossdk.io/math" + "github.com/bitsongofficial/go-bitsong/x/drop/types" + + nfttypes "github.com/bitsongofficial/go-bitsong/x/nft/types" +) + +func (k Keeper) CreateDrop( + ctx context.Context, + collectionDenom string, + maxAvailable uint64, + rules []types.Rule, +) error { + // 1. check if collections is not in our drop store + hasDrop, err := k.HasDrop(ctx, collectionDenom) + if err != nil { + return fmt.Errorf("failed to check drop existence: %w", err) + } + + if hasDrop { + return fmt.Errorf("drop already exists for collection %s", collectionDenom) + } + + // 2. check if collections has nfts, if yes return error, only empty collections can be dropped + collSupply := k.nftKeeper.GetSupply(ctx, collectionDenom) + + if collSupply.GT(math.ZeroInt()) { + return fmt.Errorf("collection %s is not empty, cannot create drop", collectionDenom) + } + + // 3. check drop max available (max is MaxNftsInCollection) + if math.NewUint(maxAvailable).GT(math.NewUint(nfttypes.MaxNftsInCollection)) { + return fmt.Errorf("max available %d exceeds max allowed %d", maxAvailable, nfttypes.MaxNftsInCollection) + } + + // 4. check max rules (5) + if len(rules) > types.MaxRulesPerDrop { + return fmt.Errorf("number of rules %d exceeds max allowed %d", len(rules), types.MaxRulesPerDrop) + } + + if err := k.validateRules(rules); err != nil { + return err + } + + // store drop + // store rules + + return nil +} + +func (k Keeper) GetDrop(ctx context.Context, collectionDenom string) (types.Drop, error) { + return k.Drops.Get(ctx, collectionDenom) +} + +func (k Keeper) HasDrop(ctx context.Context, collectionDenom string) (bool, error) { + return k.Drops.Has(ctx, collectionDenom) +} + +func (k Keeper) validateRules(rules []types.Rule) error { + return nil +} diff --git a/x/drop/keeper/keeper.go b/x/drop/keeper/keeper.go new file mode 100644 index 00000000..589ee748 --- /dev/null +++ b/x/drop/keeper/keeper.go @@ -0,0 +1,59 @@ +package keeper + +import ( + "cosmossdk.io/collections" + "cosmossdk.io/core/store" + "cosmossdk.io/log" + nftkeeper "github.com/bitsongofficial/go-bitsong/x/nft/keeper" + + storetypes "cosmossdk.io/store/types" + "github.com/bitsongofficial/go-bitsong/x/drop/types" + "github.com/cosmos/cosmos-sdk/codec" +) + +type Keeper struct { + cdc codec.BinaryCodec + storeKey storetypes.StoreKey + storeService store.KVStoreService + logger log.Logger + + nftKeeper *nftkeeper.Keeper + + Schema collections.Schema + Drops collections.Map[string, types.Drop] // (collectionDenom) -> Drop + Rules collections.Map[collections.Pair[string, string], types.Rule] // (collectionDenom, ruleId) -> Rule +} + +func NewKeeper(cdc codec.BinaryCodec, key storetypes.StoreKey, storeService store.KVStoreService, nftKeeper *nftkeeper.Keeper, logger log.Logger) Keeper { + logger = logger.With(log.ModuleKey, "x/"+types.ModuleName) + + sb := collections.NewSchemaBuilder(storeService) + + k := Keeper{ + cdc: cdc, + storeKey: key, + storeService: storeService, + logger: logger, + nftKeeper: nftKeeper, + Drops: collections.NewMap( + sb, + types.DropsPrefix, + "drops", + collections.StringKey, codec.CollValue[types.Drop](cdc), + ), + Rules: collections.NewMap( + sb, + types.RulesPrefix, + "rules", + collections.PairKeyCodec(collections.StringKey, collections.StringKey), + codec.CollValue[types.Rule](cdc), + ), + } + + schema, err := sb.Build() + if err != nil { + panic(err) + } + k.Schema = schema + return k +} diff --git a/x/drop/rules/end_time.go b/x/drop/rules/end_time.go new file mode 100644 index 00000000..d3ed90d9 --- /dev/null +++ b/x/drop/rules/end_time.go @@ -0,0 +1,22 @@ +package rules + +import ( + "fmt" + "time" +) + +type EndTimeRule struct { + EndTime time.Time +} + +func (r *EndTimeRule) Name() string { + return "end_time" +} + +func (r *EndTimeRule) Validate(req *RuleEngineRequest) error { + if req.currentTime.After(r.EndTime) { + return fmt.Errorf("current time %s is after end time %s", req.currentTime, r.EndTime) + } + + return nil +} diff --git a/x/drop/rules/engine.go b/x/drop/rules/engine.go new file mode 100644 index 00000000..38e83878 --- /dev/null +++ b/x/drop/rules/engine.go @@ -0,0 +1,78 @@ +package rules + +import ( + "fmt" + "time" + + "github.com/bitsongofficial/go-bitsong/x/drop/types" +) + +type RuleEngineRequest struct { + currentTime time.Time +} + +type RuleI interface { + Name() string + Validate(req *RuleEngineRequest) error +} + +type RuleEngine struct { + rules map[string]RuleI +} + +func NewRuleEngine() *RuleEngine { + return &RuleEngine{ + rules: make(map[string]RuleI), + } +} + +func (e *RuleEngine) Register(rule RuleI) error { + name := rule.Name() + if _, exists := e.rules[name]; exists { + return nil + } + + e.rules[name] = rule + return nil +} + +func (e *RuleEngine) Validate(req *RuleEngineRequest) error { + for name, rule := range e.rules { + if err := rule.Validate(req); err != nil { + return fmt.Errorf("rule %s validation failed: %w", name, err) + } + } + + return nil +} + +func NewRuleEngineFromRule(rule types.Rule) (*RuleEngine, error) { + engine := NewRuleEngine() + + if !rule.StartTime.IsZero() { + err := engine.Register(&StartTimeRule{ + StartTime: rule.StartTime, + }) + if err != nil { + return nil, fmt.Errorf("failed to register start time rule: %w", err) + } + } + + if !rule.EndTime.IsZero() { + err := engine.Register(&EndTimeRule{ + EndTime: rule.EndTime, + }) + if err != nil { + return nil, fmt.Errorf("failed to register end time rule: %w", err) + } + } else { + err := engine.Register(&EndTimeRule{ + EndTime: rule.StartTime.Add(7 * 24 * time.Hour), + }) + if err != nil { + return nil, fmt.Errorf("failed to register default end time rule: %w", err) + } + } + + return engine, nil +} diff --git a/x/drop/rules/engine_test.go b/x/drop/rules/engine_test.go new file mode 100644 index 00000000..94ce8fd1 --- /dev/null +++ b/x/drop/rules/engine_test.go @@ -0,0 +1,32 @@ +package rules + +import ( + "testing" + "time" + + "github.com/bitsongofficial/go-bitsong/x/drop/types" +) + +func TestRulesEngine(t *testing.T) { + startTime := time.Now().Add(-1 * time.Hour) + endTime := startTime.Add(24 * time.Hour) + + rules := types.Rule{ + StartTime: startTime, + EndTime: endTime, + } + + engine, err := NewRuleEngineFromRule(rules) + if err != nil { + t.Fatalf("failed to create rules engine: %v", err) + } + + req := &RuleEngineRequest{ + currentTime: time.Now(), + } + + err = engine.Validate(req) + if err != nil { + t.Fatalf("rules validation failed: %v", err) + } +} diff --git a/x/drop/rules/start_time.go b/x/drop/rules/start_time.go new file mode 100644 index 00000000..7e12e6f8 --- /dev/null +++ b/x/drop/rules/start_time.go @@ -0,0 +1,22 @@ +package rules + +import ( + "fmt" + "time" +) + +type StartTimeRule struct { + StartTime time.Time +} + +func (r *StartTimeRule) Name() string { + return "start_time" +} + +func (r *StartTimeRule) Validate(req *RuleEngineRequest) error { + if req.currentTime.Before(r.StartTime) { + return fmt.Errorf("current time %s is before start time %s", req.currentTime, r.StartTime) + } + + return nil +} diff --git a/x/drop/types/drop.pb.go b/x/drop/types/drop.pb.go new file mode 100644 index 00000000..56affafd --- /dev/null +++ b/x/drop/types/drop.pb.go @@ -0,0 +1,867 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: bitsong/drop/v1beta1/drop.proto + +package types + +import ( + fmt "fmt" + _ "github.com/cosmos/cosmos-sdk/types/tx/amino" + _ "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/timestamppb" + io "io" + math "math" + math_bits "math/bits" + time "time" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf +var _ = time.Kitchen + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +type Template struct { + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Uri string `protobuf:"bytes,2,opt,name=uri,proto3" json:"uri,omitempty"` +} + +func (m *Template) Reset() { *m = Template{} } +func (m *Template) String() string { return proto.CompactTextString(m) } +func (*Template) ProtoMessage() {} +func (*Template) Descriptor() ([]byte, []int) { + return fileDescriptor_3bbfac863f8f1f74, []int{0} +} +func (m *Template) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Template) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Template.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 *Template) XXX_Merge(src proto.Message) { + xxx_messageInfo_Template.Merge(m, src) +} +func (m *Template) XXX_Size() int { + return m.Size() +} +func (m *Template) XXX_DiscardUnknown() { + xxx_messageInfo_Template.DiscardUnknown(m) +} + +var xxx_messageInfo_Template proto.InternalMessageInfo + +type Rule struct { + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + StartTime time.Time `protobuf:"bytes,2,opt,name=start_time,json=startTime,proto3,stdtime" json:"start_time"` + EndTime time.Time `protobuf:"bytes,3,opt,name=end_time,json=endTime,proto3,stdtime" json:"end_time"` +} + +func (m *Rule) Reset() { *m = Rule{} } +func (m *Rule) String() string { return proto.CompactTextString(m) } +func (*Rule) ProtoMessage() {} +func (*Rule) Descriptor() ([]byte, []int) { + return fileDescriptor_3bbfac863f8f1f74, []int{1} +} +func (m *Rule) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Rule) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Rule.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 *Rule) XXX_Merge(src proto.Message) { + xxx_messageInfo_Rule.Merge(m, src) +} +func (m *Rule) XXX_Size() int { + return m.Size() +} +func (m *Rule) XXX_DiscardUnknown() { + xxx_messageInfo_Rule.DiscardUnknown(m) +} + +var xxx_messageInfo_Rule proto.InternalMessageInfo + +type Drop struct { + Collection string `protobuf:"bytes,1,opt,name=collection,proto3" json:"collection,omitempty"` + MaxAvailable uint64 `protobuf:"varint,2,opt,name=max_available,json=maxAvailable,proto3" json:"max_available,omitempty"` + Template *Template `protobuf:"bytes,3,opt,name=template,proto3" json:"template,omitempty"` +} + +func (m *Drop) Reset() { *m = Drop{} } +func (m *Drop) String() string { return proto.CompactTextString(m) } +func (*Drop) ProtoMessage() {} +func (*Drop) Descriptor() ([]byte, []int) { + return fileDescriptor_3bbfac863f8f1f74, []int{2} +} +func (m *Drop) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Drop) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Drop.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 *Drop) XXX_Merge(src proto.Message) { + xxx_messageInfo_Drop.Merge(m, src) +} +func (m *Drop) XXX_Size() int { + return m.Size() +} +func (m *Drop) XXX_DiscardUnknown() { + xxx_messageInfo_Drop.DiscardUnknown(m) +} + +var xxx_messageInfo_Drop proto.InternalMessageInfo + +func init() { + proto.RegisterType((*Template)(nil), "bitsong.drop.v1beta1.Template") + proto.RegisterType((*Rule)(nil), "bitsong.drop.v1beta1.Rule") + proto.RegisterType((*Drop)(nil), "bitsong.drop.v1beta1.Drop") +} + +func init() { proto.RegisterFile("bitsong/drop/v1beta1/drop.proto", fileDescriptor_3bbfac863f8f1f74) } + +var fileDescriptor_3bbfac863f8f1f74 = []byte{ + // 388 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x52, 0x31, 0x8f, 0xd3, 0x30, + 0x14, 0x8e, 0xdb, 0x08, 0x5a, 0x43, 0x11, 0x58, 0x1d, 0xaa, 0x0c, 0x0e, 0x2a, 0x0b, 0x42, 0xc2, + 0x56, 0x8b, 0xc4, 0xd0, 0x8d, 0xaa, 0x03, 0x03, 0x53, 0xd4, 0x89, 0xa5, 0x72, 0x12, 0x37, 0x58, + 0xb2, 0xe3, 0x28, 0x71, 0xaa, 0xf2, 0x0f, 0x58, 0x90, 0xfa, 0x13, 0x18, 0x19, 0x18, 0xf8, 0x19, + 0x1d, 0x3b, 0x32, 0x71, 0xa7, 0x76, 0xb8, 0xbf, 0x71, 0x8a, 0xe3, 0x9c, 0x6e, 0xb8, 0xe5, 0x16, + 0xeb, 0xbd, 0xef, 0x7d, 0xef, 0xf3, 0xe7, 0xe7, 0x07, 0xc3, 0x58, 0x98, 0x4a, 0xe7, 0x19, 0x4d, + 0x4b, 0x5d, 0xd0, 0xdd, 0x2c, 0xe6, 0x86, 0xcd, 0x6c, 0x42, 0x8a, 0x52, 0x1b, 0x8d, 0xc6, 0x8e, + 0x40, 0x2c, 0xe6, 0x08, 0xc1, 0x38, 0xd3, 0x99, 0xb6, 0x04, 0xda, 0x44, 0x2d, 0x37, 0x08, 0x33, + 0xad, 0x33, 0xc9, 0xa9, 0xcd, 0xe2, 0x7a, 0x4b, 0x8d, 0x50, 0xbc, 0x32, 0x4c, 0x39, 0xb1, 0xe0, + 0x15, 0x53, 0x22, 0xd7, 0xd4, 0x9e, 0x2d, 0x34, 0xfd, 0x08, 0x07, 0x6b, 0xae, 0x0a, 0xc9, 0x0c, + 0x47, 0x08, 0xfa, 0x39, 0x53, 0x7c, 0x02, 0x5e, 0x83, 0xb7, 0xc3, 0xc8, 0xc6, 0xe8, 0x25, 0xec, + 0xd7, 0xa5, 0x98, 0xf4, 0x2c, 0xd4, 0x84, 0x0b, 0xff, 0xc7, 0xaf, 0xd0, 0x9b, 0xfe, 0x01, 0xd0, + 0x8f, 0x6a, 0xc9, 0xd1, 0x0b, 0xd8, 0x13, 0xa9, 0x6b, 0xe9, 0x89, 0x14, 0x7d, 0x86, 0xb0, 0x32, + 0xac, 0x34, 0x9b, 0xe6, 0x72, 0xdb, 0xf7, 0x6c, 0x1e, 0x90, 0xd6, 0x19, 0xe9, 0x9c, 0x91, 0x75, + 0xe7, 0x6c, 0x39, 0x3a, 0xfe, 0x0f, 0xbd, 0xc3, 0x55, 0x08, 0x7e, 0xdf, 0xfc, 0x7d, 0x07, 0xa2, + 0xa1, 0x6d, 0x6e, 0xca, 0x68, 0x05, 0x07, 0x3c, 0x4f, 0x5b, 0x9d, 0xfe, 0x63, 0x75, 0x9e, 0xf2, + 0x3c, 0x6d, 0x8a, 0xce, 0xee, 0x4f, 0x00, 0xfd, 0x55, 0xa9, 0x0b, 0x84, 0x21, 0x4c, 0xb4, 0x94, + 0x3c, 0x31, 0x42, 0xe7, 0xce, 0xf6, 0x3d, 0x04, 0xbd, 0x81, 0x23, 0xc5, 0xf6, 0x1b, 0xb6, 0x63, + 0x42, 0xb2, 0x58, 0xb6, 0x2f, 0xf0, 0xa3, 0xe7, 0x8a, 0xed, 0x3f, 0x75, 0x18, 0x5a, 0xc0, 0x81, + 0x71, 0x43, 0x73, 0xce, 0x30, 0x79, 0xe8, 0x9f, 0x48, 0x37, 0xda, 0xe8, 0x8e, 0xdf, 0xfa, 0x59, + 0x7e, 0x39, 0x9e, 0x31, 0x38, 0x9d, 0x31, 0xb8, 0x3e, 0x63, 0x70, 0xb8, 0x60, 0xef, 0x74, 0xc1, + 0xde, 0xbf, 0x0b, 0xf6, 0xbe, 0xce, 0x33, 0x61, 0xbe, 0xd5, 0x31, 0x49, 0xb4, 0xa2, 0x4e, 0x53, + 0x6f, 0xb7, 0x22, 0x11, 0x4c, 0xd2, 0x4c, 0xbf, 0xef, 0xf6, 0x65, 0xdf, 0x6e, 0x8c, 0xf9, 0x5e, + 0xf0, 0x2a, 0x7e, 0x62, 0xe7, 0xf1, 0xe1, 0x36, 0x00, 0x00, 0xff, 0xff, 0x83, 0x90, 0xe8, 0x08, + 0x4e, 0x02, 0x00, 0x00, +} + +func (m *Template) 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 *Template) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Template) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Uri) > 0 { + i -= len(m.Uri) + copy(dAtA[i:], m.Uri) + i = encodeVarintDrop(dAtA, i, uint64(len(m.Uri))) + i-- + dAtA[i] = 0x12 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = encodeVarintDrop(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Rule) 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 *Rule) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Rule) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + n1, err1 := github_com_cosmos_gogoproto_types.StdTimeMarshalTo(m.EndTime, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdTime(m.EndTime):]) + if err1 != nil { + return 0, err1 + } + i -= n1 + i = encodeVarintDrop(dAtA, i, uint64(n1)) + i-- + dAtA[i] = 0x1a + n2, err2 := github_com_cosmos_gogoproto_types.StdTimeMarshalTo(m.StartTime, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdTime(m.StartTime):]) + if err2 != nil { + return 0, err2 + } + i -= n2 + i = encodeVarintDrop(dAtA, i, uint64(n2)) + i-- + dAtA[i] = 0x12 + if len(m.Id) > 0 { + i -= len(m.Id) + copy(dAtA[i:], m.Id) + i = encodeVarintDrop(dAtA, i, uint64(len(m.Id))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Drop) 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 *Drop) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Drop) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Template != nil { + { + size, err := m.Template.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintDrop(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + if m.MaxAvailable != 0 { + i = encodeVarintDrop(dAtA, i, uint64(m.MaxAvailable)) + i-- + dAtA[i] = 0x10 + } + if len(m.Collection) > 0 { + i -= len(m.Collection) + copy(dAtA[i:], m.Collection) + i = encodeVarintDrop(dAtA, i, uint64(len(m.Collection))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintDrop(dAtA []byte, offset int, v uint64) int { + offset -= sovDrop(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *Template) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Name) + if l > 0 { + n += 1 + l + sovDrop(uint64(l)) + } + l = len(m.Uri) + if l > 0 { + n += 1 + l + sovDrop(uint64(l)) + } + return n +} + +func (m *Rule) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Id) + if l > 0 { + n += 1 + l + sovDrop(uint64(l)) + } + l = github_com_cosmos_gogoproto_types.SizeOfStdTime(m.StartTime) + n += 1 + l + sovDrop(uint64(l)) + l = github_com_cosmos_gogoproto_types.SizeOfStdTime(m.EndTime) + n += 1 + l + sovDrop(uint64(l)) + return n +} + +func (m *Drop) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Collection) + if l > 0 { + n += 1 + l + sovDrop(uint64(l)) + } + if m.MaxAvailable != 0 { + n += 1 + sovDrop(uint64(m.MaxAvailable)) + } + if m.Template != nil { + l = m.Template.Size() + n += 1 + l + sovDrop(uint64(l)) + } + return n +} + +func sovDrop(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozDrop(x uint64) (n int) { + return sovDrop(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *Template) 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 ErrIntOverflowDrop + } + 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: Template: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Template: 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 ErrIntOverflowDrop + } + 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 ErrInvalidLengthDrop + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDrop + } + 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 Uri", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDrop + } + 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 ErrInvalidLengthDrop + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDrop + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Uri = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipDrop(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthDrop + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Rule) 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 ErrIntOverflowDrop + } + 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: Rule: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Rule: 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 ErrIntOverflowDrop + } + 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 ErrInvalidLengthDrop + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDrop + } + 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 StartTime", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDrop + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthDrop + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthDrop + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := github_com_cosmos_gogoproto_types.StdTimeUnmarshal(&m.StartTime, dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field EndTime", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDrop + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthDrop + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthDrop + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := github_com_cosmos_gogoproto_types.StdTimeUnmarshal(&m.EndTime, dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipDrop(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthDrop + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Drop) 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 ErrIntOverflowDrop + } + 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: Drop: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Drop: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Collection", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDrop + } + 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 ErrInvalidLengthDrop + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDrop + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Collection = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MaxAvailable", wireType) + } + m.MaxAvailable = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDrop + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MaxAvailable |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Template", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDrop + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthDrop + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthDrop + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Template == nil { + m.Template = &Template{} + } + if err := m.Template.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipDrop(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthDrop + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipDrop(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, ErrIntOverflowDrop + } + 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, ErrIntOverflowDrop + } + 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, ErrIntOverflowDrop + } + 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, ErrInvalidLengthDrop + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupDrop + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthDrop + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthDrop = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowDrop = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupDrop = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/drop/types/keys.go b/x/drop/types/keys.go new file mode 100644 index 00000000..772ba76a --- /dev/null +++ b/x/drop/types/keys.go @@ -0,0 +1,17 @@ +package types + +import "cosmossdk.io/collections" + +const ( + ModuleName = "drop" + StoreKey = ModuleName + RouterKey = ModuleName + + MaxRulesPerDrop = 5 + MaxRuleIDLength = 20 +) + +var ( + DropsPrefix = collections.NewPrefix(0) + RulesPrefix = collections.NewPrefix(1) +)