Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: dynamic nfts #195

Merged
merged 10 commits into from
Nov 11, 2024
2 changes: 2 additions & 0 deletions proto/OmniFlix/onft/v1beta1/onft.proto
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ message Denom {
repeated WeightedAddress royalty_receivers = 11 [
(gogoproto.moretags) = "yaml:\"royalty_receivers\""
];
bool updatable_data = 12;
}

message DenomMetadata {
Expand All @@ -47,6 +48,7 @@ message DenomMetadata {
repeated WeightedAddress royalty_receivers = 7 [
(gogoproto.moretags) = "yaml:\"royalty_receivers\""
];
bool updatable_data = 8;
}

//ASSET or ONFT
Expand Down
17 changes: 17 additions & 0 deletions proto/OmniFlix/onft/v1beta1/tx.proto
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ service Msg {

rpc BurnONFT(MsgBurnONFT) returns (MsgBurnONFTResponse);

rpc UpdateONFTData(MsgUpdateONFTData) returns (MsgUpdateONFTDataResponse);

// UpdateParams defines a governance operation for updating the onft module
// parameters. The authority is hard-coded to the onft module account.
//
Expand Down Expand Up @@ -62,6 +64,7 @@ message MsgCreateDenom {
repeated WeightedAddress royalty_receivers = 12 [
(gogoproto.moretags) = "yaml:\"royalty_receivers\""
];
bool updatable_data = 13;
}

message MsgCreateDenomResponse {}
Expand Down Expand Up @@ -157,6 +160,20 @@ message MsgBurnONFT {

message MsgBurnONFTResponse {}

message MsgUpdateONFTData {
option (cosmos.msg.v1.signer) = "sender";
option (amino.name) = "OmniFlix/onft/MsgUpdateONFTData";
option (gogoproto.equal) = false;

string id = 1;
string denom_id = 2 [(gogoproto.moretags) = "yaml:\"denom_id\""];
string data = 3;
string sender = 4;
}

message MsgUpdateONFTDataResponse {}


// MsgUpdateParams is the Msg/UpdateParams request type.
//
// Since: cosmos-sdk 0.47
Expand Down
3 changes: 3 additions & 0 deletions x/itc/keeper/keeper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ func (suite *KeeperTestSuite) createDefaultNftDenom() {
Weight: sdkmath.LegacyOneDec(),
},
},
false,
)
createDenomMsg.Id = defaultNftDenomId

Expand All @@ -171,6 +172,7 @@ func (suite *KeeperTestSuite) createSecondaryNftDenom() {
suite.TestAccs[0].String(),
onfttypes.DefaultDenomCreationFee,
nil,
false,
)
createDenomMsg.Id = secondaryNftDenomId

Expand Down Expand Up @@ -198,6 +200,7 @@ func (suite *KeeperTestSuite) createDefaultMintNftDenom() {
Weight: sdkmath.LegacyOneDec(),
},
},
false,
)
createDenomMsg.Id = defaultNftMintDenomId

Expand Down
20 changes: 13 additions & 7 deletions x/onft/client/cli/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,18 @@ const (
FlagURI = "uri"
FlagURIHash = "uri-hash"
FlagRoyaltyReceivers = "royalty-receivers"
FlagUpdatableData = "updatable-data"
)

var (
FsCreateDenom = flag.NewFlagSet("", flag.ContinueOnError)
FsUpdateDenom = flag.NewFlagSet("", flag.ContinueOnError)
FsTransferDenom = flag.NewFlagSet("", flag.ContinueOnError)
FsMintONFT = flag.NewFlagSet("", flag.ContinueOnError)
FsTransferONFT = flag.NewFlagSet("", flag.ContinueOnError)
FsQuerySupply = flag.NewFlagSet("", flag.ContinueOnError)
FsQueryOwner = flag.NewFlagSet("", flag.ContinueOnError)
FsCreateDenom = flag.NewFlagSet("", flag.ContinueOnError)
FsUpdateDenom = flag.NewFlagSet("", flag.ContinueOnError)
FsTransferDenom = flag.NewFlagSet("", flag.ContinueOnError)
FsMintONFT = flag.NewFlagSet("", flag.ContinueOnError)
FsTransferONFT = flag.NewFlagSet("", flag.ContinueOnError)
FsUpdateONFTData = flag.NewFlagSet("", flag.ContinueOnError)
FsQuerySupply = flag.NewFlagSet("", flag.ContinueOnError)
FsQueryOwner = flag.NewFlagSet("", flag.ContinueOnError)
)

func init() {
Expand All @@ -49,6 +51,7 @@ func init() {
FsCreateDenom.String(FlagURI, "", "uri for denom")
FsCreateDenom.String(FlagURIHash, "", "uri hash for denom")
FsCreateDenom.String(FlagData, "", "json data of the denom")
FsCreateDenom.Bool(FlagUpdatableData, false, "allows updates to the nft data if true")

FsTransferDenom.String(FlagRecipient, "", "recipient of the denom")

Expand All @@ -65,6 +68,9 @@ func init() {
FsMintONFT.String(FlagURIHash, "", "uri hash for the nft")

FsTransferONFT.String(FlagRecipient, "", "Receiver of the onft. default value is sender address of transaction")

FsUpdateONFTData.String(FlagData, "", "custom data of onft")

FsQuerySupply.String(FlagOwner, "", "The owner of a nft")
FsQueryOwner.String(FlagDenomID, "", "id of the denom")
}
53 changes: 52 additions & 1 deletion x/onft/client/cli/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ func NewTxCmd() *cobra.Command {
GetCmdMintONFT(),
GetCmdTransferONFT(),
GetCmdBurnONFT(),
GetCmdUpdateONFTData(),
)

return txCmd
Expand All @@ -46,7 +47,7 @@ func GetCmdCreateDenom() *cobra.Command {
Example:
$ %s tx onft create [symbol] --name=<name> --schema=<schema> --description=<description>
--uri=<uri> --uri-hash=<uri hash> --preview-uri=<preview-uri> --royalty-receivers=<"addr1:weight,addr2:weight">
--creation-fee <fee> --chain-id=<chain-id> --from=<key-name> --fees=<fee>`,
--updatable-data --creation-fee <fee> --chain-id=<chain-id> --from=<key-name> --fees=<fee>`,
version.AppName,
),
),
Expand Down Expand Up @@ -112,6 +113,11 @@ $ %s tx onft create [symbol] --name=<name> --schema=<schema> --description=<desc
}
}

updatableData, err := cmd.Flags().GetBool(FlagUpdatableData)
if err != nil {
return err
}

msg := types.NewMsgCreateDenom(
symbol,
denomName,
Expand All @@ -124,6 +130,7 @@ $ %s tx onft create [symbol] --name=<name> --schema=<schema> --description=<desc
clientCtx.GetFromAddress().String(),
creationFee,
royaltyReceivers,
updatableData,
)
if err := msg.ValidateBasic(); err != nil {
return err
Expand Down Expand Up @@ -505,6 +512,50 @@ $ %s tx onft purge-denom [denom-id] --from=<key-name> --chain-id=<chain-id> --fe
return cmd
}

func GetCmdUpdateONFTData() *cobra.Command {
cmd := &cobra.Command{
Use: "update-onft-data [denom-id] [onft-id] --data <new-data-json-string>",
Long: strings.TrimSpace(
fmt.Sprintf(`Update the data of an oNFT.
Example:
$ %s tx onft update-onft-data [denom-id] [onft-id] --data <new-data-json-string> --from=<key-name> --chain-id=<chain-id> --fees=<fee>`,
version.AppName,
),
),
Args: cobra.ExactArgs(2),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientTxContext(cmd)
if err != nil {
return err
}

denomId := args[0]
onftId := args[1]

data, err := cmd.Flags().GetString(FlagData)
if err != nil {
return err
}

msg := types.NewMsgUpdateONFTData(
denomId,
onftId,
data,
clientCtx.GetFromAddress().String(),
)
if err := msg.ValidateBasic(); err != nil {
return err
}
return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
},
}
cmd.Flags().AddFlagSet(FsUpdateONFTData)
_ = cmd.MarkFlagRequired(FlagData)
flags.AddTxFlagsToCmd(cmd)

return cmd
}

func parseSplitShares(splitSharesStr string) ([]*types.WeightedAddress, error) {
splitSharesStr = strings.TrimSpace(splitSharesStr)
splitsStrList := strings.Split(splitSharesStr, ",")
Expand Down
1 change: 1 addition & 0 deletions x/onft/keeper/collection.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ func (k Keeper) SetCollection(ctx sdk.Context, collection types.Collection) erro
denom.UriHash,
denom.Data,
denom.RoyaltyReceivers,
denom.UpdatableData,
); err != nil {
return err
}
Expand Down
18 changes: 18 additions & 0 deletions x/onft/keeper/denom.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,15 @@ func (k Keeper) SaveDenom(
uriHash,
data string,
royaltyReceivers []*types.WeightedAddress,
updatableData bool,
) error {
denomMetadata := &types.DenomMetadata{
Creator: creator.String(),
Schema: schema,
PreviewUri: previewUri,
Data: data,
RoyaltyReceivers: royaltyReceivers,
UpdatableData: updatableData,
}
metadata, err := codectypes.NewAnyWithValue(denomMetadata)
if err != nil {
Expand Down Expand Up @@ -83,6 +85,7 @@ func (k Keeper) TransferDenomOwner(
PreviewUri: denom.PreviewURI,
Data: denom.Data,
RoyaltyReceivers: denom.RoyaltyReceivers,
UpdatableData: denom.UpdatableData,
}
data, err := codectypes.NewAnyWithValue(denomMetadata)
if err != nil {
Expand Down Expand Up @@ -131,6 +134,7 @@ func (k Keeper) UpdateDenom(ctx sdk.Context, msg *types.MsgUpdateDenom) error {
PreviewUri: denom.PreviewURI,
Data: denom.Data,
RoyaltyReceivers: denom.RoyaltyReceivers,
UpdatableData: denom.UpdatableData,
}
if msg.PreviewURI != types.DoNotModify {
denomMetadata.PreviewUri = msg.PreviewURI
Expand Down Expand Up @@ -208,6 +212,19 @@ func (k Keeper) HasPermissionToMint(ctx sdk.Context, denomID string, sender sdk.
return false
}

func (k Keeper) HasPermissionToUpdateData(ctx sdk.Context, denomID string, sender sdk.AccAddress) bool {
denom, err := k.GetDenomInfo(ctx, denomID)
if err != nil {
return false
}

if denom.UpdatableData && sender.String() == denom.Creator {
return true
}

return false
}

func (k Keeper) GetDenomInfo(ctx sdk.Context, denomID string) (*types.Denom, error) {
class, ok := k.nk.GetClass(ctx, denomID)
if !ok {
Expand All @@ -230,6 +247,7 @@ func (k Keeper) GetDenomInfo(ctx sdk.Context, denomID string) (*types.Denom, err
UriHash: class.UriHash,
Data: denomMetadata.Data,
RoyaltyReceivers: denomMetadata.RoyaltyReceivers,
UpdatableData: denomMetadata.UpdatableData,
}, nil
}

Expand Down
25 changes: 25 additions & 0 deletions x/onft/keeper/msg_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ func (m msgServer) CreateDenom(goCtx context.Context, msg *types.MsgCreateDenom)
msg.UriHash,
msg.Data,
msg.RoyaltyReceivers,
msg.UpdatableData,
); err != nil {
return nil, err
}
Expand Down Expand Up @@ -188,6 +189,30 @@ func (m msgServer) MintONFT(goCtx context.Context, msg *types.MsgMintONFT) (*typ
return &types.MsgMintONFTResponse{}, nil
}

func (m msgServer) UpdateONFTData(goCtx context.Context, msg *types.MsgUpdateONFTData) (*types.MsgUpdateONFTDataResponse, error) {
ctx := sdk.UnwrapSDKContext(goCtx)

sender, err := sdk.AccAddressFromBech32(msg.Sender)
if err != nil {
return nil, err
}

if !m.Keeper.HasPermissionToUpdateData(ctx, msg.DenomId, sender) {
return nil, errorsmod.Wrapf(
sdkerrors.ErrUnauthorized,
"%s is not allowed to update nft data for this nft %s",
sender.String(),
msg.Id,
)
}

if err := m.Keeper.UpdateONFTData(ctx, msg.DenomId, msg.Id, msg.Data); err != nil {
return nil, err
}

return &types.MsgUpdateONFTDataResponse{}, nil
}

func (m msgServer) TransferONFT(goCtx context.Context,
msg *types.MsgTransferONFT,
) (*types.MsgTransferONFTResponse, error) {
Expand Down
39 changes: 39 additions & 0 deletions x/onft/keeper/onft.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,45 @@ func (k Keeper) BurnONFT(
return nil
}

func (k Keeper) UpdateONFTData(ctx sdk.Context, denomID, onftID, data string) error {
if !k.nk.HasClass(ctx, denomID) {
return errorsmod.Wrapf(types.ErrInvalidDenom, "denomID %s not exists", denomID)
}
if !k.nk.HasNFT(ctx, denomID, onftID) {
return errorsmod.Wrapf(types.ErrInvalidONFT, "nft ID %s not exists", onftID)
}
_nft, err := k.GetONFT(ctx, denomID, onftID)
if err != nil {
return err
}

nftMetadata := &types.ONFTMetadata{
Name: _nft.GetName(),
Description: _nft.GetDescription(),
PreviewURI: _nft.GetPreviewURI(),
Data: data,
Transferable: _nft.IsTransferable(),
Extensible: _nft.IsExtensible(),
Nsfw: _nft.IsNSFW(),
CreatedAt: _nft.GetCreatedTime(),
RoyaltyShare: _nft.GetRoyaltyShare(),
}

newData, err := codectypes.NewAnyWithValue(nftMetadata)
if err != nil {
return err
}
updatedNFT := nft.NFT{
ClassId: denomID,
Id: onftID,
Uri: _nft.GetMediaURI(),
UriHash: _nft.GetURIHash(),
Data: newData,
}

return k.nk.Update(ctx, updatedNFT)
}

func (k Keeper) GetONFT(ctx sdk.Context, denomID, onftID string) (nft exported.ONFTI, err error) {
if !k.nk.HasClass(ctx, denomID) {
return nil, errorsmod.Wrapf(types.ErrInvalidDenom, "denomID %s not exists", denomID)
Expand Down
1 change: 1 addition & 0 deletions x/onft/migrations/v2/expected_keepers.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,6 @@ type NFTKeeper interface {
uriHash,
data string,
royaltyReceivers []*onfttypes.WeightedAddress,
updatableData bool,
) error
}
1 change: 1 addition & 0 deletions x/onft/migrations/v2/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ func MigrateCollections(ctx sdk.Context,
denom.UriHash,
denom.Data,
denom.RoyaltyReceivers,
denom.UpdatableData,
); err != nil {
return err
}
Expand Down
1 change: 1 addition & 0 deletions x/onft/simulation/operations.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ func SimulateMsgCreateDenom(k keeper.Keeper, ak types.AccountKeeper, bk types.Ba
sender.Address.String(),
creationFee,
nil,
false,
)
msg.Id = denomId
denom, _ := k.GetDenomInfo(ctx, msg.Id)
Expand Down
Loading
Loading