Skip to content

Commit

Permalink
added tests
Browse files Browse the repository at this point in the history
  • Loading branch information
miladz68 committed Oct 31, 2023
1 parent 1a968d9 commit a10d254
Show file tree
Hide file tree
Showing 3 changed files with 194 additions and 6 deletions.
85 changes: 85 additions & 0 deletions integration-tests/modules/assetnft_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2054,6 +2054,91 @@ func TestAssetNFTClassWhitelist(t *testing.T) {
requireT.NoError(err)
}

func TestAssetNFTSoulbound(t *testing.T) {
t.Parallel()

ctx, chain := integrationtests.NewCoreumTestingContext(t)

requireT := require.New(t)
issuer := chain.GenAccount()
recipient := chain.GenAccount()

chain.FundAccountWithOptions(ctx, t, issuer, integration.BalancesOptions{
Messages: []sdk.Msg{
&assetnfttypes.MsgIssueClass{},
&assetnfttypes.MsgMint{},
&nft.MsgSend{},
},
Amount: chain.QueryAssetNFTParams(ctx, t).MintFee.Amount,
})

chain.FundAccountWithOptions(ctx, t, recipient, integration.BalancesOptions{
Messages: []sdk.Msg{
&nft.MsgSend{},
},
})

// issue new NFT class
issueMsg := &assetnfttypes.MsgIssueClass{
Issuer: issuer.String(),
Symbol: "NFTClassSymbol",
Features: []assetnfttypes.ClassFeature{
assetnfttypes.ClassFeature_soulbound,
},
}

// mint new token in that class
classID := assetnfttypes.BuildClassID(issueMsg.Symbol, issuer)
nftID := "id-1"
mintMsg1 := &assetnfttypes.MsgMint{
Sender: issuer.String(),
ID: nftID,
ClassID: classID,
}

msgList := []sdk.Msg{issueMsg, mintMsg1}
_, err := client.BroadcastTx(
ctx,
chain.ClientContext.WithFromAddress(issuer),
chain.TxFactory().WithGas(chain.GasLimitByMsgs(msgList...)),
msgList...,
)
requireT.NoError(err)

// try to send from issuer to recipient (it is allowed)
sendMsg := &nft.MsgSend{
ClassId: classID,
Id: nftID,
Sender: issuer.String(),
Receiver: recipient.String(),
}

_, err = client.BroadcastTx(
ctx,
chain.ClientContext.WithFromAddress(issuer),
chain.TxFactory().WithGas(chain.GasLimitByMsgs(sendMsg)),
sendMsg,
)
requireT.NoError(err)

// try to send from recipient to issuer (it is not allowed)
sendMsg = &nft.MsgSend{
ClassId: classID,
Id: nftID,
Sender: recipient.String(),
Receiver: issuer.String(),
}

_, err = client.BroadcastTx(
ctx,
chain.ClientContext.WithFromAddress(recipient),
chain.TxFactory().WithGas(chain.GasLimitByMsgs(sendMsg)),
sendMsg,
)
requireT.Error(err)
requireT.ErrorIs(err, cosmoserrors.ErrUnauthorized)
}

// TestAssetNFTSendAuthorization tests that assetnft SendAuthorization works as expected.
func TestAssetNFTSendAuthorization(t *testing.T) {
t.Parallel()
Expand Down
6 changes: 0 additions & 6 deletions x/asset/nft/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -992,12 +992,6 @@ func (k Keeper) isNFTReceivable(ctx sdk.Context, classID, nftID string, receiver
return sdkerrors.Wrapf(types.ErrNFTNotFound, "nft with classID:%s and ID:%s not found", classID, nftID)
}

// we check for soulbound before the check for issuer, since the issuer should not be able to receive the token.
// Or in other words, the non-issuer sender should not be able to send to issuer.
if classDefinition.IsFeatureEnabled(types.ClassFeature_soulbound) {
return sdkerrors.Wrapf(cosmoserrors.ErrUnauthorized, "nft with classID:%s and ID:%s is soulbound and cannot be sent", classID, nftID)
}

// always allow issuer to receive NFTs issued by them.
if classDefinition.IsIssuer(receiver) {
return nil
Expand Down
109 changes: 109 additions & 0 deletions x/asset/nft/keeper/keeper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,15 @@ import (
cosmoserrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/cosmos/cosmos-sdk/types/query"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
"github.com/cosmos/cosmos-sdk/x/nft"
"github.com/stretchr/testify/require"

"github.com/CoreumFoundation/coreum/v3/pkg/config/constant"
"github.com/CoreumFoundation/coreum/v3/testutil/event"
"github.com/CoreumFoundation/coreum/v3/testutil/simapp"
"github.com/CoreumFoundation/coreum/v3/x/asset/nft/keeper"
"github.com/CoreumFoundation/coreum/v3/x/asset/nft/types"
wnftkeeper "github.com/CoreumFoundation/coreum/v3/x/wnft/keeper"
)

func TestKeeper_IssueClass(t *testing.T) {
Expand Down Expand Up @@ -1281,6 +1283,107 @@ func TestKeeper_ClassFreeze_Nonexistent(t *testing.T) {
requireT.ErrorIs(err, types.ErrClassNotFound)
}

func TestKeeper_Soulbound(t *testing.T) {
requireT := require.New(t)
testApp := simapp.New()
ctx := testApp.NewContext(false, tmproto.Header{})
assetNFTKeeper := testApp.AssetNFTKeeper
nftKeeper := testApp.NFTKeeper

nftParams := types.Params{
MintFee: sdk.NewInt64Coin(constant.DenomDev, 0),
}
requireT.NoError(assetNFTKeeper.SetParams(ctx, nftParams))

issuer := sdk.AccAddress(ed25519.GenPrivKey().PubKey().Address())
classSettings := types.IssueClassSettings{
Issuer: issuer,
Symbol: "symbol",
Features: []types.ClassFeature{
types.ClassFeature_soulbound,
},
}

classID, err := assetNFTKeeper.IssueClass(ctx, classSettings)
requireT.NoError(err)

// mint NFT
recipient := sdk.AccAddress(ed25519.GenPrivKey().PubKey().Address())
settings := types.MintSettings{
Sender: issuer,
Recipient: recipient,
ClassID: classID,
ID: "my-id",
URI: "https://my-nft-meta.invalid/1",
URIHash: "content-hash",
}

requireT.NoError(assetNFTKeeper.Mint(ctx, settings))
nftID := settings.ID

// transfer must be rejected
recipient2 := sdk.AccAddress(ed25519.GenPrivKey().PubKey().Address())
err = nftKeeper.Transfer(ctx, classID, nftID, recipient2)
requireT.Error(err)
requireT.ErrorIs(err, cosmoserrors.ErrUnauthorized)

// transfer to issuer must also be rejected
err = nftKeeper.Transfer(ctx, classID, nftID, issuer)
requireT.Error(err)
requireT.ErrorIs(err, cosmoserrors.ErrUnauthorized)
}

func TestKeeper_Soulbound_Burning(t *testing.T) {
requireT := require.New(t)
testApp := simapp.New()
ctx := testApp.NewContext(false, tmproto.Header{})
assetNFTKeeper := testApp.AssetNFTKeeper
nftKeeper := testApp.NFTKeeper

nftParams := types.Params{
MintFee: sdk.NewInt64Coin(constant.DenomDev, 0),
}
requireT.NoError(assetNFTKeeper.SetParams(ctx, nftParams))

issuer := sdk.AccAddress(ed25519.GenPrivKey().PubKey().Address())
classSettings := types.IssueClassSettings{
Issuer: issuer,
Symbol: "symbol",
Features: []types.ClassFeature{
types.ClassFeature_soulbound,
types.ClassFeature_burning,
},
}

classID, err := assetNFTKeeper.IssueClass(ctx, classSettings)
requireT.NoError(err)

// mint NFT
recipient := sdk.AccAddress(ed25519.GenPrivKey().PubKey().Address())
settings := types.MintSettings{
Sender: issuer,
Recipient: recipient,
ClassID: classID,
ID: "my-id",
URI: "https://my-nft-meta.invalid/1",
URIHash: "content-hash",
}

requireT.NoError(assetNFTKeeper.Mint(ctx, settings))
nftID := settings.ID

// transfer must be rejected
recipient2 := sdk.AccAddress(ed25519.GenPrivKey().PubKey().Address())
err = nftKeeper.Transfer(ctx, classID, nftID, recipient2)
requireT.Error(err)
requireT.ErrorIs(err, cosmoserrors.ErrUnauthorized)

// burning is allowed
err = assetNFTKeeper.Burn(ctx, recipient, classID, nftID)
requireT.NoError(err)
requireT.False(nftKeeper.HasNFT(ctx, classID, nftID))
}

func genNFTData(requireT *require.Assertions) *codectypes.Any {
dataString := "metadata"
dataValue, err := codectypes.NewAnyWithValue(&types.DataBytes{Data: []byte(dataString)})
Expand Down Expand Up @@ -1309,3 +1412,9 @@ func assertFrozen(t *testing.T, ctx sdk.Context, k keeper.Keeper, classID, nftID
require.NoError(t, err)
require.EqualValues(t, frozen, expected)
}

func assertOwner(t *testing.T, ctx sdk.Context, k wnftkeeper.Wrapper, classID, nftID string, expectedAddress sdk.AccAddress) {
res, err := k.Owner(ctx, &nft.QueryOwnerRequest{ClassId: classID, Id: nftID})
require.NoError(t, err)
require.EqualValues(t, expectedAddress.String(), res.Owner)
}

0 comments on commit a10d254

Please sign in to comment.