diff --git a/app/keepers/keepers.go b/app/keepers/keepers.go index 4e56ec8e..b027b7f6 100644 --- a/app/keepers/keepers.go +++ b/app/keepers/keepers.go @@ -461,7 +461,7 @@ func NewAppKeeper( appKeepers.IBCKeeper.ChannelKeeper, &appKeepers.IBCKeeper.PortKeeper, appKeepers.AccountKeeper, - ics721nft.NewKeeper(appCodec, appKeepers.ONFTKeeper, appKeepers.AccountKeeper), + ics721nft.NewKeeper(appCodec, appKeepers.ONFTKeeper, appKeepers.AccountKeeper, appKeepers.BankKeeper), appKeepers.ScopedNFTTransferKeeper, ) diff --git a/x/ics721nft/interface.go b/x/ics721nft/interface.go index 48ab05a9..15741772 100644 --- a/x/ics721nft/interface.go +++ b/x/ics721nft/interface.go @@ -12,6 +12,9 @@ type ( SetAccount(sdk.Context, authtypes.AccountI) GetModuleAddress(name string) sdk.AccAddress } + BankKeeper interface { + BlockedAddr(addr sdk.AccAddress) bool + } ICS721Class struct { ID string diff --git a/x/ics721nft/keeper.go b/x/ics721nft/keeper.go index 4fb767a1..0fb003f9 100644 --- a/x/ics721nft/keeper.go +++ b/x/ics721nft/keeper.go @@ -1,6 +1,7 @@ package ics721nft import ( + errorsmod "cosmossdk.io/errors" onftkeeper "github.com/OmniFlix/omniflixhub/v2/x/onft/keeper" onfttypes "github.com/OmniFlix/omniflixhub/v2/x/onft/types" nfttransfer "github.com/bianjieai/nft-transfer/types" @@ -8,6 +9,7 @@ import ( "github.com/cosmos/cosmos-sdk/codec" codectypes "github.com/cosmos/cosmos-sdk/codec/types" sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/x/nft" nftkeeper "github.com/cosmos/cosmos-sdk/x/nft/keeper" ) @@ -17,6 +19,7 @@ type Keeper struct { nk nftkeeper.Keeper cdc codec.Codec ak AccountKeeper + bk BankKeeper cb onfttypes.ClassBuilder nb onfttypes.NFTBuilder } @@ -25,11 +28,13 @@ type Keeper struct { func NewKeeper(cdc codec.Codec, k onftkeeper.Keeper, ak AccountKeeper, + bk BankKeeper, ) Keeper { return Keeper{ nk: k.NFTkeeper(), cdc: cdc, ak: ak, + bk: bk, cb: onfttypes.NewClassBuilder(cdc, ak.GetModuleAddress), nb: onfttypes.NewNFTBuilder(cdc), } @@ -53,9 +58,13 @@ func (k Keeper) CreateOrUpdateClass(ctx sdk.Context, } } else { denomMetadata := &onfttypes.DenomMetadata{ - Creator: k.ak.GetModuleAddress(onfttypes.ModuleName).String(), - PreviewUri: "", - Schema: "", + Creator: k.ak.GetModuleAddress(onfttypes.ModuleName).String(), + PreviewUri: "", + Schema: "", + Description: "", + Data: "", + UriHash: "", + RoyaltyReceivers: nil, } metadata, err := codectypes.NewAnyWithValue(denomMetadata) @@ -69,6 +78,18 @@ func (k Keeper) CreateOrUpdateClass(ctx sdk.Context, Data: metadata, } } + var denomMeta onfttypes.DenomMetadata + if err := k.cdc.UnpackAny(class.Data, &denomMeta); err != nil { + return err + } + if denomMeta.RoyaltyReceivers != nil && !k.validRoyaltyReceiverAddresses(denomMeta.RoyaltyReceivers) { + denomMeta.RoyaltyReceivers = nil + dMeta, err := codectypes.NewAnyWithValue(&denomMeta) + if err != nil { + return err + } + class.Data = dMeta + } if k.nk.HasClass(ctx, classID) { return k.nk.UpdateClass(ctx, class) } @@ -96,23 +117,22 @@ func (k Keeper) Transfer( ctx sdk.Context, classID, tokenID, - tokenData string, + _ string, receiver sdk.AccAddress, ) error { - if err := k.nk.Transfer(ctx, classID, tokenID, receiver); err != nil { + _nft, _ := k.nk.GetNFT(ctx, classID, tokenID) + nftMetadata, err := onfttypes.UnmarshalNFTMetadata(k.cdc, _nft.Data.GetValue()) + if err != nil { return err } - if len(tokenData) == 0 { - return nil + if !nftMetadata.Transferable { + k.Logger(ctx).Error("non-transferable nft") + return errorsmod.Wrap(sdkerrors.ErrUnauthorized, "non-transferable nft") } - _nft, _ := k.nk.GetNFT(ctx, classID, tokenID) - token, err := k.nb.Build(classID, tokenID, _nft.GetUri(), tokenData) - if err != nil { - k.Logger(ctx).Error("unable to build token on transfer from packet data", "error:", err.Error()) + if err := k.nk.Transfer(ctx, classID, tokenID, receiver); err != nil { return err } - - return k.nk.Update(ctx, token) + return nil } // GetClass implement the method of ICS721Keeper.GetClass @@ -140,14 +160,6 @@ func (k Keeper) GetNFT(ctx sdk.Context, classID, tokenID string) (nfttransfer.NF if !has { return nil, false } - nftMetadata, err := onfttypes.UnmarshalNFTMetadata(k.cdc, _nft.Data.GetValue()) - if err != nil { - return nil, false - } - if !nftMetadata.Transferable { - k.Logger(ctx).Error("non-transferable nft") - return nil, false - } metadata, err := k.nb.BuildMetadata(_nft) if err != nil { k.Logger(ctx).Error("encode nft data failed") @@ -180,3 +192,24 @@ func (k Keeper) HasClass(ctx sdk.Context, classID string) bool { func (k Keeper) Logger(ctx sdk.Context) log.Logger { return ctx.Logger().With("module", "ics721/NFTKeeper") } + +func (k Keeper) validRoyaltyReceiverAddresses(addresses []*onfttypes.WeightedAddress) bool { + weightSum := sdk.NewDec(0) + for _, addr := range addresses { + address, err := sdk.AccAddressFromBech32(addr.Address) + if err != nil { + return false + } + if k.bk.BlockedAddr(address) { + return false + } + if !addr.Weight.IsPositive() { + return false + } + if addr.Weight.GT(sdk.NewDec(1)) { + return false + } + weightSum = weightSum.Add(addr.Weight) + } + return weightSum.Equal(sdk.NewDec(1)) +} diff --git a/x/onft/types/builder.go b/x/onft/types/builder.go index e2e0d762..db96f087 100644 --- a/x/onft/types/builder.go +++ b/x/onft/types/builder.go @@ -21,21 +21,22 @@ const ( ) var ( - ClassKeyName = fmt.Sprintf("%s%s", Namespace, "name") - ClassKeySymbol = fmt.Sprintf("%s%s", Namespace, "symbol") - ClassKeyDescription = fmt.Sprintf("%s%s", Namespace, "description") - ClassKeyURIHash = fmt.Sprintf("%s%s", Namespace, "uri_hash") - ClassKeyCreator = fmt.Sprintf("%s%s", Namespace, "creator") - ClassKeySchema = fmt.Sprintf("%s%s", Namespace, "schema") - ClassKeyPreviewURI = fmt.Sprintf("%s%s", Namespace, "preview_uri") - nftKeyName = fmt.Sprintf("%s%s", Namespace, "name") - nftKeyURIHash = fmt.Sprintf("%s%s", Namespace, "uri_hash") - nftKeyPreviewURI = fmt.Sprintf("%s%s", Namespace, "preview_uri") - nftKeyDescription = fmt.Sprintf("%s%s", Namespace, "description") - nftKeyCreatedAt = fmt.Sprintf("%s%s", Namespace, "created_at") - nftKeyExtensible = fmt.Sprintf("%s%s", Namespace, "extensible") - nftKeyNSFW = fmt.Sprintf("%s%s", Namespace, "nsfw") - nftKeyRoyaltyShare = fmt.Sprintf("%s%s", Namespace, "royalty_share") + ClassKeyName = fmt.Sprintf("%s%s", Namespace, "name") + ClassKeySymbol = fmt.Sprintf("%s%s", Namespace, "symbol") + ClassKeyDescription = fmt.Sprintf("%s%s", Namespace, "description") + ClassKeyURIHash = fmt.Sprintf("%s%s", Namespace, "uri_hash") + ClassKeyCreator = fmt.Sprintf("%s%s", Namespace, "creator") + ClassKeySchema = fmt.Sprintf("%s%s", Namespace, "schema") + ClassKeyPreviewURI = fmt.Sprintf("%s%s", Namespace, "preview_uri") + ClassKeyRoyaltyReceivers = fmt.Sprintf("%s%s", Namespace, "royalty_receivers") + nftKeyName = fmt.Sprintf("%s%s", Namespace, "name") + nftKeyURIHash = fmt.Sprintf("%s%s", Namespace, "uri_hash") + nftKeyPreviewURI = fmt.Sprintf("%s%s", Namespace, "preview_uri") + nftKeyDescription = fmt.Sprintf("%s%s", Namespace, "description") + nftKeyCreatedAt = fmt.Sprintf("%s%s", Namespace, "created_at") + nftKeyExtensible = fmt.Sprintf("%s%s", Namespace, "extensible") + nftKeyNSFW = fmt.Sprintf("%s%s", Namespace, "nsfw") + nftKeyRoyaltyShare = fmt.Sprintf("%s%s", Namespace, "royalty_share") ) type ClassBuilder struct { @@ -97,6 +98,7 @@ func (cb ClassBuilder) BuildMetadata(class nft.Class) (string, error) { kvals[ClassKeyCreator] = MediaField{Value: hexCreator} kvals[ClassKeySchema] = MediaField{Value: metadata.Schema} kvals[ClassKeyPreviewURI] = MediaField{Value: metadata.PreviewUri} + kvals[ClassKeyRoyaltyReceivers] = MediaField{Value: metadata.RoyaltyReceivers} data, err := json.Marshal(kvals) if err != nil { return "", err @@ -112,13 +114,14 @@ func (cb ClassBuilder) Build(classID, classURI, classData string) (nft.Class, er } var ( - name = "" - symbol = "" - description = "" - uriHash = "" - schema = "" - previewURI = "" - creator = cb.getModuleAddress(ModuleName).String() + name = "" + symbol = "" + description = "" + uriHash = "" + schema = "" + previewURI = "" + royaltyReceivers []*WeightedAddress + creator = cb.getModuleAddress(ModuleName).String() ) dataMap := make(map[string]interface{}) @@ -210,6 +213,15 @@ func (cb ClassBuilder) Build(classID, classURI, classData string) (nft.Class, er } } + if v, ok := dataMap[ClassKeyRoyaltyReceivers]; ok { + if vMap, ok := v.(map[string]interface{}); ok { + if vAddrs, ok := vMap[KeyMediaFieldValue].([]*WeightedAddress); ok { + royaltyReceivers = vAddrs + delete(dataMap, ClassKeyRoyaltyReceivers) + } + } + } + data := "" if len(dataMap) > 0 { dataBz, err := json.Marshal(dataMap) @@ -220,12 +232,13 @@ func (cb ClassBuilder) Build(classID, classURI, classData string) (nft.Class, er } denomMeta, err := codectypes.NewAnyWithValue(&DenomMetadata{ - Creator: creator, - Schema: schema, - Description: description, - PreviewUri: previewURI, - Data: data, - UriHash: uriHash, + Creator: creator, + Schema: schema, + Description: description, + PreviewUri: previewURI, + Data: data, + UriHash: uriHash, + RoyaltyReceivers: royaltyReceivers, }) if err != nil { return nft.Class{}, err