From cec2a7b64009dd29735002983faecab5ffa8b1c0 Mon Sep 17 00:00:00 2001 From: yuandu Date: Thu, 24 Aug 2023 18:29:25 +0800 Subject: [PATCH] feat: load ibc nft-transfer module --- app/app.go | 36 ++++++-- app/modules.go | 12 +++ go.mod | 2 +- go.sum | 3 +- modules/internft/interface.go | 49 ++++++++++ modules/internft/keeper.go | 164 ++++++++++++++++++++++++++++++++++ 6 files changed, 258 insertions(+), 8 deletions(-) create mode 100644 modules/internft/interface.go create mode 100644 modules/internft/keeper.go diff --git a/app/app.go b/app/app.go index 256a7018e..d6c0fc985 100644 --- a/app/app.go +++ b/app/app.go @@ -99,6 +99,10 @@ import ( tibcroutingtypes "github.com/bianjieai/tibc-go/modules/tibc/core/26-routing/types" tibckeeper "github.com/bianjieai/tibc-go/modules/tibc/core/keeper" + nfttransfer "github.com/bianjieai/nft-transfer" + ibcnfttransferkeeper "github.com/bianjieai/nft-transfer/keeper" + ibcnfttransfertypes "github.com/bianjieai/nft-transfer/types" + "github.com/evmos/ethermint/ethereum/eip712" srvflags "github.com/evmos/ethermint/server/flags" ethermint "github.com/evmos/ethermint/types" @@ -113,6 +117,7 @@ import ( "github.com/irisnet/irishub/lite" guardiankeeper "github.com/irisnet/irishub/modules/guardian/keeper" guardiantypes "github.com/irisnet/irishub/modules/guardian/types" + "github.com/irisnet/irishub/modules/internft" mintkeeper "github.com/irisnet/irishub/modules/mint/keeper" minttypes "github.com/irisnet/irishub/modules/mint/types" iristypes "github.com/irisnet/irishub/types" @@ -156,8 +161,9 @@ type IrisApp struct { ConsensusParamsKeeper consensuskeeper.Keeper //ibc - IBCKeeper *ibckeeper.Keeper // IBC Keeper must be a pointer in the app, so we can SetRouter on it correctly - IBCTransferKeeper ibctransferkeeper.Keeper + IBCKeeper *ibckeeper.Keeper // IBC Keeper must be a pointer in the app, so we can SetRouter on it correctly + IBCTransferKeeper ibctransferkeeper.Keeper + IBCNFTTransferKeeper ibcnfttransferkeeper.Keeper // make scoped keepers public for test purposes scopedIBCKeeper capabilitykeeper.ScopedKeeper @@ -193,9 +199,10 @@ type IrisApp struct { // simulation manager sm *module.SimulationManager - transferModule transfer.AppModule - nfttransferModule tibcnfttransfer.AppModule - mttransferModule tibcmttransfer.AppModule + transferModule transfer.AppModule + nfttransferModule tibcnfttransfer.AppModule + mttransferModule tibcmttransfer.AppModule + ibcnfttransferModule nfttransfer.AppModule } // NewIrisApp returns a reference to an initialized IrisApp. @@ -237,6 +244,7 @@ func NewIrisApp( consensustypes.StoreKey, evidencetypes.StoreKey, ibctransfertypes.StoreKey, + ibcnfttransfertypes.StoreKey, capabilitytypes.StoreKey, guardiantypes.StoreKey, tokentypes.StoreKey, @@ -306,6 +314,7 @@ func NewIrisApp( ) scopedIBCKeeper := app.CapabilityKeeper.ScopeToModule(ibcexported.ModuleName) scopedTransferKeeper := app.CapabilityKeeper.ScopeToModule(ibctransfertypes.ModuleName) + scopedNFTTransferKeeper := app.CapabilityKeeper.ScopeToModule(ibcnfttransfertypes.ModuleName) app.AccountKeeper = authkeeper.NewAccountKeeper( appCodec, @@ -468,10 +477,25 @@ func NewIrisApp( app.transferModule = transfer.NewAppModule(app.IBCTransferKeeper) transferIBCModule := transfer.NewIBCModule(app.IBCTransferKeeper) + app.IBCNFTTransferKeeper = ibcnfttransferkeeper.NewKeeper( + appCodec, + keys[ibcnfttransfertypes.StoreKey], + authtypes.NewModuleAddress(govtypes.ModuleName).String(), + app.IBCKeeper.ChannelKeeper, + app.IBCKeeper.ChannelKeeper, + &app.IBCKeeper.PortKeeper, + app.AccountKeeper, + internft.NewInterNftKeeper(appCodec, app.NFTKeeper, app.AccountKeeper), + scopedNFTTransferKeeper, + ) + app.ibcnfttransferModule = nfttransfer.NewAppModule(app.IBCNFTTransferKeeper) + nfttransferIBCModule := nfttransfer.NewIBCModule(app.IBCNFTTransferKeeper) + // routerModule := router.NewAppModule(app.RouterKeeper, transferIBCModule) // create static IBC router, add transfer route, then set and seal it ibcRouter := porttypes.NewRouter() - ibcRouter.AddRoute(ibctransfertypes.ModuleName, transferIBCModule) + ibcRouter.AddRoute(ibctransfertypes.ModuleName, transferIBCModule). + AddRoute(ibcnfttransfertypes.ModuleName, nfttransferIBCModule) app.IBCKeeper.SetRouter(ibcRouter) app.nfttransferModule = tibcnfttransfer.NewAppModule(app.TIBCNFTTransferKeeper) diff --git a/app/modules.go b/app/modules.go index f7e5706d1..ea7819293 100644 --- a/app/modules.go +++ b/app/modules.go @@ -1,6 +1,7 @@ package app import ( + ibcnfttransfertypes "github.com/bianjieai/nft-transfer/types" "github.com/cosmos/cosmos-sdk/types/module" "github.com/cosmos/cosmos-sdk/x/auth" authsims "github.com/cosmos/cosmos-sdk/x/auth/simulation" @@ -74,6 +75,8 @@ import ( tibchost "github.com/bianjieai/tibc-go/modules/tibc/core/24-host" tibccli "github.com/bianjieai/tibc-go/modules/tibc/core/client/cli" + nfttransfer "github.com/bianjieai/nft-transfer" + "github.com/evmos/ethermint/x/evm" evmtypes "github.com/evmos/ethermint/x/evm/types" "github.com/evmos/ethermint/x/feemarket" @@ -138,6 +141,7 @@ var ( tibcnfttransfer.AppModuleBasic{}, tibcmttransfer.AppModuleBasic{}, mt.AppModuleBasic{}, + nfttransfer.AppModuleBasic{}, evm.AppModuleBasic{}, feemarket.AppModuleBasic{}, @@ -257,6 +261,7 @@ func appModules( ibc.NewAppModule(app.IBCKeeper), tibc.NewAppModule(app.TIBCKeeper), params.NewAppModule(app.ParamsKeeper), app.transferModule, + app.ibcnfttransferModule, app.nfttransferModule, app.mttransferModule, guardian.NewAppModule(appCodec, app.GuardianKeeper), @@ -384,6 +389,7 @@ func simulationModules( ), ibc.NewAppModule(app.IBCKeeper), app.transferModule, + app.ibcnfttransferModule, guardian.NewAppModule(appCodec, app.GuardianKeeper), token.NewAppModule( appCodec, @@ -488,6 +494,8 @@ func orderBeginBlockers() []string { tibcnfttypes.ModuleName, tibcmttypes.ModuleName, guardiantypes.ModuleName, + + ibcnfttransfertypes.ModuleName, } } @@ -538,6 +546,8 @@ func orderEndBlockers() []string { tibcnfttypes.ModuleName, tibcmttypes.ModuleName, guardiantypes.ModuleName, + + ibcnfttransfertypes.ModuleName, } } @@ -592,5 +602,7 @@ func orderInitBlockers() []string { guardiantypes.ModuleName, // NOTE: crisis module must go at the end to check for invariants on each module crisistypes.ModuleName, + + ibcnfttransfertypes.ModuleName, } } diff --git a/go.mod b/go.mod index 11076a0ed..e1ff0e9bf 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/irisnet/irishub go 1.19 require ( + github.com/bianjieai/nft-transfer v1.1.2-beta.0.20230824091700-ad134c96cf57 github.com/bianjieai/tibc-go v0.4.4-0.20230824091732-bbd58021f825 github.com/cometbft/cometbft v0.37.2 github.com/cometbft/cometbft-db v0.8.0 @@ -59,7 +60,6 @@ require ( github.com/cosmos/rosetta-sdk-go v0.10.0 // indirect github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91 // indirect github.com/dop251/goja v0.0.0-20220405120441-9037c2b61cbf // indirect - github.com/go-playground/locales v0.14.0 // indirect github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect github.com/gogo/googleapis v1.4.1 // indirect github.com/golang/mock v1.6.0 // indirect diff --git a/go.sum b/go.sum index f8d03346d..b29f049cd 100644 --- a/go.sum +++ b/go.sum @@ -293,6 +293,8 @@ github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 h1:41iFGWnSlI2 github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bianjieai/ethermint v0.6.1-0.20230824080820-7416e4b8e2c6 h1:eF39/n5pVKfxqanQvNdKAeqKF3Oz3iGDL/cX202HK8g= github.com/bianjieai/ethermint v0.6.1-0.20230824080820-7416e4b8e2c6/go.mod h1:JEhGmVj5rZX5bTfOqh3nltE2N6+qMI4HVNV2vW6PpOQ= +github.com/bianjieai/nft-transfer v1.1.2-beta.0.20230824091700-ad134c96cf57 h1:L3pt1Rd8Fr8wqY3Nr6PHllxRtYQgj3GRlQcBaFzzZa4= +github.com/bianjieai/nft-transfer v1.1.2-beta.0.20230824091700-ad134c96cf57/go.mod h1:Mzg0WR5zWqosdlJ+sW6LTNBiCeuoKXWPt7E/owSn0Xw= github.com/bianjieai/tibc-go v0.4.4-0.20230824091732-bbd58021f825 h1:+/6FK0V7uXouVYigb1EvPGuDN1ZDPfBnvzOUbTRe4rg= github.com/bianjieai/tibc-go v0.4.4-0.20230824091732-bbd58021f825/go.mod h1:OBT3OZWqF8eTyQNGOvHycGg+pkhWWm5RwELT/NRZM9k= github.com/bmizerany/pat v0.0.0-20170815010413-6226ea591a40/go.mod h1:8rLXio+WjiTceGBHIoTvn60HIbs7Hm7bcHjyrSqYB9c= @@ -552,7 +554,6 @@ github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU= -github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho= github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= diff --git a/modules/internft/interface.go b/modules/internft/interface.go new file mode 100644 index 000000000..431f9f383 --- /dev/null +++ b/modules/internft/interface.go @@ -0,0 +1,49 @@ +package internft + +import ( + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + + nftkeeper "github.com/cosmos/cosmos-sdk/x/nft/keeper" + nfttypes "github.com/irisnet/irismod/modules/nft/types" +) + +type ( + // AccountKeeper defines the contract required for account APIs. + AccountKeeper interface { + NewAccountWithAddress(ctx sdk.Context, addr sdk.AccAddress) authtypes.AccountI + // Set an account in the store. + SetAccount(sdk.Context, authtypes.AccountI) + GetModuleAddress(name string) sdk.AccAddress + } + // InterNftKeeper defines the ICS721 Keeper + InterNftKeeper struct { + nk nftkeeper.Keeper + cdc codec.Codec + ak AccountKeeper + cb nfttypes.ClassBuilder + tb nfttypes.TokenBuilder + } + + InterClass struct { + ID string + URI string + Data string + } + + InterToken struct { + ClassID string + ID string + URI string + Data string + } +) + +func (d InterClass) GetID() string { return d.ID } +func (d InterClass) GetURI() string { return d.URI } +func (d InterClass) GetData() string { return d.Data } +func (t InterToken) GetClassID() string { return t.ClassID } +func (t InterToken) GetID() string { return t.ID } +func (t InterToken) GetURI() string { return t.URI } +func (t InterToken) GetData() string { return t.Data } diff --git a/modules/internft/keeper.go b/modules/internft/keeper.go new file mode 100644 index 000000000..175f7dd56 --- /dev/null +++ b/modules/internft/keeper.go @@ -0,0 +1,164 @@ +package internft + +import ( + "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" + + nfttransfer "github.com/bianjieai/nft-transfer/types" + + irismodnftkeeper "github.com/irisnet/irismod/modules/nft/keeper" + "github.com/irisnet/irismod/modules/nft/types" +) + +// NewInterNftKeeper creates a new ics721 Keeper instance +func NewInterNftKeeper(cdc codec.Codec, + k irismodnftkeeper.Keeper, + ak AccountKeeper, +) InterNftKeeper { + return InterNftKeeper{ + nk: k.NFTkeeper(), + cdc: cdc, + ak: ak, + cb: types.NewClassBuilder(cdc, ak.GetModuleAddress), + tb: types.NewTokenBuilder(cdc), + } +} + +// CreateOrUpdateClass implement the method of ICS721Keeper.CreateOrUpdateClass +func (ik InterNftKeeper) CreateOrUpdateClass(ctx sdk.Context, + classID, + classURI, + classData string, +) error { + var ( + class nft.Class + err error + ) + if len(classData) != 0 { + class, err = ik.cb.Build(classID, classURI, classData) + if err != nil { + return err + } + } else { + var denomMetadata = &types.DenomMetadata{ + Creator: ik.ak.GetModuleAddress(types.ModuleName).String(), + MintRestricted: true, + UpdateRestricted: true, + } + + metadata, err := codectypes.NewAnyWithValue(denomMetadata) + if err != nil { + return err + } + class = nft.Class{ + Id: classID, + Uri: classURI, + Data: metadata, + } + } + + if ik.nk.HasClass(ctx, classID) { + return ik.nk.UpdateClass(ctx, class) + } + return ik.nk.SaveClass(ctx, class) +} + +// Mint implement the method of ICS721Keeper.Mint +func (ik InterNftKeeper) Mint(ctx sdk.Context, + classID, + tokenID, + tokenURI, + tokenData string, + receiver sdk.AccAddress, +) error { + token, err := ik.tb.Build(classID, tokenID, tokenURI, tokenData) + if err != nil { + return err + } + return ik.nk.Mint(ctx, token, receiver) +} + +// Transfer implement the method of ICS721Keeper.Transfer +func (ik InterNftKeeper) Transfer( + ctx sdk.Context, + classID, + tokenID, + tokenData string, + receiver sdk.AccAddress, +) error { + if err := ik.nk.Transfer(ctx, classID, tokenID, receiver); err != nil { + return err + } + if len(tokenData) == 0 { + return nil + } + nft, _ := ik.nk.GetNFT(ctx, classID, tokenID) + token, err := ik.tb.Build(classID, tokenID, nft.Uri, tokenData) + if err != nil { + return err + } + + return ik.nk.Update(ctx, token) +} + +// GetClass implement the method of ICS721Keeper.GetClass +func (ik InterNftKeeper) GetClass(ctx sdk.Context, classID string) (nfttransfer.Class, bool) { + class, exist := ik.nk.GetClass(ctx, classID) + if !exist { + return nil, false + } + + metadata, err := ik.cb.BuildMetadata(class) + if err != nil { + ik.Logger(ctx).Error("encode class data failed") + return nil, false + } + return InterClass{ + ID: classID, + URI: class.Uri, + Data: metadata, + }, true +} + +// GetNFT implement the method of ICS721Keeper.GetNFT +func (ik InterNftKeeper) GetNFT(ctx sdk.Context, classID, tokenID string) (nfttransfer.NFT, bool) { + nft, has := ik.nk.GetNFT(ctx, classID, tokenID) + if !has { + return nil, false + } + metadata, err := ik.tb.BuildMetadata(nft) + if err != nil { + ik.Logger(ctx).Error("encode nft data failed") + return nil, false + } + return InterToken{ + ClassID: classID, + ID: tokenID, + URI: nft.Uri, + Data: metadata, + }, true +} + +// Burn implement the method of ICS721Keeper.Burn +func (ik InterNftKeeper) Burn(ctx sdk.Context, classID string, tokenID string) error { + return ik.nk.Burn(ctx, classID, tokenID) +} + +// GetOwner implement the method of ICS721Keeper.GetOwner +func (ik InterNftKeeper) GetOwner(ctx sdk.Context, classID string, tokenID string) sdk.AccAddress { + return ik.nk.GetOwner(ctx, classID, tokenID) +} + +// HasClass implement the method of ICS721Keeper.HasClass +func (ik InterNftKeeper) HasClass(ctx sdk.Context, classID string) bool { + return ik.nk.HasClass(ctx, classID) +} + +// Logger returns a module-specific logger. +func (ik InterNftKeeper) Logger(ctx sdk.Context) log.Logger { + return ctx.Logger().With("module", "ics721/NFTKeeper") +}