diff --git a/app/keepers/keepers.go b/app/keepers/keepers.go index b8d0edc9..931f31ad 100644 --- a/app/keepers/keepers.go +++ b/app/keepers/keepers.go @@ -1,6 +1,7 @@ package keepers import ( + "github.com/OmniFlix/omniflixhub/v2/x/ics721nft" nfttransfer "github.com/bianjieai/nft-transfer" "github.com/cometbft/cometbft/libs/log" tmos "github.com/cometbft/cometbft/libs/os" @@ -459,8 +460,7 @@ func NewAppKeeper( appKeepers.IBCKeeper.ChannelKeeper, &appKeepers.IBCKeeper.PortKeeper, appKeepers.AccountKeeper, - nil, - // ics721nft.NewICS721NftKeeper(appCodec, appKeepers.ONFTKeeper, appKeepers.AccountKeeper), + ics721nft.NewKeeper(appCodec, appKeepers.ONFTKeeper, appKeepers.AccountKeeper), appKeepers.ScopedNFTTransferKeeper, ) diff --git a/x/ics721nft/interface.go b/x/ics721nft/interface.go new file mode 100644 index 00000000..48ab05a9 --- /dev/null +++ b/x/ics721nft/interface.go @@ -0,0 +1,36 @@ +package ics721nft + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" +) + +type ( + // AccountKeeper defines the contract required for account APIs. + AccountKeeper interface { + NewAccountWithAddress(ctx sdk.Context, addr sdk.AccAddress) authtypes.AccountI + SetAccount(sdk.Context, authtypes.AccountI) + GetModuleAddress(name string) sdk.AccAddress + } + + ICS721Class struct { + ID string + URI string + Data string + } + + ICS721Token struct { + ClassID string + ID string + URI string + Data string + } +) + +func (c ICS721Class) GetID() string { return c.ID } +func (c ICS721Class) GetURI() string { return c.URI } +func (c ICS721Class) GetData() string { return c.Data } +func (t ICS721Token) GetClassID() string { return t.ClassID } +func (t ICS721Token) GetID() string { return t.ID } +func (t ICS721Token) GetURI() string { return t.URI } +func (t ICS721Token) GetData() string { return t.Data } diff --git a/x/ics721nft/keeper.go b/x/ics721nft/keeper.go new file mode 100644 index 00000000..84715f4e --- /dev/null +++ b/x/ics721nft/keeper.go @@ -0,0 +1,174 @@ +package ics721nft + +import ( + 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" + "github.com/cometbft/cometbft/libs/log" + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/nft" + nftkeeper "github.com/cosmos/cosmos-sdk/x/nft/keeper" +) + +// Keeper defines the ICS721 Keeper +type Keeper struct { + nk nftkeeper.Keeper + cdc codec.Codec + ak AccountKeeper + cb onfttypes.ClassBuilder + tb onfttypes.NFTBuilder +} + +// NewKeeper creates a new ics721 Keeper instance +func NewKeeper(cdc codec.Codec, + k onftkeeper.Keeper, + ak AccountKeeper, +) Keeper { + return Keeper{ + nk: k.NFTkeeper(), + cdc: cdc, + ak: ak, + cb: onfttypes.NewClassBuilder(cdc, ak.GetModuleAddress), + tb: onfttypes.NewNFTBuilder(cdc), + } +} + +// CreateOrUpdateClass implement the method of ICS721Keeper.CreateOrUpdateClass +func (k Keeper) CreateOrUpdateClass(ctx sdk.Context, + classID, + classURI, + classData string, +) error { + var ( + class nft.Class + err error + ) + if len(classData) != 0 { + class, err = k.cb.Build(classID, classURI, classData) + if err != nil { + k.Logger(ctx).Error("unable to build class from packet data", "error:", err.Error()) + return err + } + } else { + denomMetadata := &onfttypes.DenomMetadata{ + Creator: k.ak.GetModuleAddress(onfttypes.ModuleName).String(), + PreviewUri: "", + Schema: "", + } + + metadata, err := codectypes.NewAnyWithValue(denomMetadata) + if err != nil { + k.Logger(ctx).Error("unable to build class metadata from packet data", "error:", err.Error()) + return err + } + class = nft.Class{ + Id: classID, + Uri: classURI, + Data: metadata, + } + } + if k.nk.HasClass(ctx, classID) { + return k.nk.UpdateClass(ctx, class) + } + return k.nk.SaveClass(ctx, class) +} + +// Mint implement the method of ICS721Keeper.Mint +func (k Keeper) Mint(ctx sdk.Context, + classID, + tokenID, + tokenURI, + tokenData string, + receiver sdk.AccAddress, +) error { + token, err := k.tb.Build(classID, tokenID, tokenURI, tokenData) + if err != nil { + k.Logger(ctx).Error("unable to build token from packet data", "error:", err.Error()) + return err + } + return k.nk.Mint(ctx, token, receiver) +} + +// Transfer implement the method of ICS721Keeper.Transfer +func (k Keeper) Transfer( + ctx sdk.Context, + classID, + tokenID, + tokenData string, + receiver sdk.AccAddress, +) error { + if err := k.nk.Transfer(ctx, classID, tokenID, receiver); err != nil { + return err + } + if len(tokenData) == 0 { + return nil + } + _nft, _ := k.nk.GetNFT(ctx, classID, tokenID) + token, err := k.tb.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()) + return err + } + + return k.nk.Update(ctx, token) +} + +// GetClass implement the method of ICS721Keeper.GetClass +func (k Keeper) GetClass(ctx sdk.Context, classID string) (nfttransfer.Class, bool) { + class, exist := k.nk.GetClass(ctx, classID) + if !exist { + return nil, false + } + metadata, err := k.cb.BuildMetadata(class) + if err != nil { + k.Logger(ctx).Error("encode class data failed") + return nil, false + } + + return ICS721Class{ + ID: classID, + URI: class.Uri, + Data: metadata, + }, true +} + +// GetNFT implement the method of ICS721Keeper.GetNFT +func (k Keeper) GetNFT(ctx sdk.Context, classID, tokenID string) (nfttransfer.NFT, bool) { + _nft, has := k.nk.GetNFT(ctx, classID, tokenID) + if !has { + return nil, false + } + metadata, err := k.tb.BuildMetadata(_nft) + if err != nil { + k.Logger(ctx).Error("encode nft data failed") + return nil, false + } + return ICS721Token{ + ClassID: classID, + ID: tokenID, + URI: _nft.Uri, + Data: metadata, + }, true +} + +// Burn implement the method of ICS721Keeper.Burn +func (k Keeper) Burn(ctx sdk.Context, classID string, tokenID string) error { + return k.nk.Burn(ctx, classID, tokenID) +} + +// GetOwner implement the method of ICS721Keeper.GetOwner +func (k Keeper) GetOwner(ctx sdk.Context, classID string, tokenID string) sdk.AccAddress { + return k.nk.GetOwner(ctx, classID, tokenID) +} + +// HasClass implement the method of ICS721Keeper.HasClass +func (k Keeper) HasClass(ctx sdk.Context, classID string) bool { + return k.nk.HasClass(ctx, classID) +} + +// Logger returns a module-specific logger. +func (k Keeper) Logger(ctx sdk.Context) log.Logger { + return ctx.Logger().With("module", "ics721/NFTKeeper") +}