Skip to content

Commit

Permalink
Add validation for max supply >= mintable count
Browse files Browse the repository at this point in the history
MPR #245
  • Loading branch information
hochiw authored May 19, 2022
2 parents 7646a47 + 7fed6fe commit 57a8024
Show file tree
Hide file tree
Showing 4 changed files with 148 additions and 9 deletions.
7 changes: 6 additions & 1 deletion x/likenft/keeper/class_validations.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,12 +117,17 @@ func (k msgServer) sanitizeBlindBoxConfig(blindBoxConfig *types.BlindBoxConfig)
return blindBoxConfig, nil
}

func (k msgServer) sanitizeClassConfig(classConfig types.ClassConfig) (*types.ClassConfig, error) {
func (k msgServer) sanitizeClassConfig(classConfig types.ClassConfig, mintableCount uint64) (*types.ClassConfig, error) {
// Ensure mint periods and reveal time are set when blind box mode is enabled
cleanBlindBoxConfig, err := k.sanitizeBlindBoxConfig(classConfig.BlindBoxConfig)
if err != nil {
return nil, err
}
classConfig.BlindBoxConfig = cleanBlindBoxConfig

// Assert new max supply >= mintable count
if classConfig.IsBlindBox() && classConfig.MaxSupply < mintableCount {
return nil, sdkerrors.ErrInvalidRequest.Wrapf("New max supply %d is less than mintable count %d", classConfig.MaxSupply, mintableCount)
}
return &classConfig, nil
}
2 changes: 1 addition & 1 deletion x/likenft/keeper/msg_server_new_class.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ func (k msgServer) NewClass(goCtx context.Context, msg *types.MsgNewClass) (*typ
}

// Sanitize class config
cleanClassConfig, err := k.sanitizeClassConfig(msg.Input.Config)
cleanClassConfig, err := k.sanitizeClassConfig(msg.Input.Config, 0)
if cleanClassConfig == nil || err != nil {
return nil, err
}
Expand Down
13 changes: 6 additions & 7 deletions x/likenft/keeper/msg_server_update_class.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,20 +25,19 @@ func (k msgServer) UpdateClass(goCtx context.Context, msg *types.MsgUpdateClass)
return nil, types.ErrCannotUpdateClassWithMintedTokens.Wrap("Cannot update class with minted tokens")
}

var classData types.ClassData
if err := k.cdc.Unmarshal(class.Data.Value, &classData); err != nil {
return nil, types.ErrFailedToUnmarshalData.Wrapf(err.Error())
}

// Verify and Cleanup class config
cleanClassConfig, err := k.sanitizeClassConfig(msg.Input.Config)
cleanClassConfig, err := k.sanitizeClassConfig(msg.Input.Config, classData.MintableCount)
if cleanClassConfig == nil || err != nil {
return nil, err
}
msg.Input.Config = *cleanClassConfig

// Check class parent relation is valid and current user is owner

var classData types.ClassData
if err := k.cdc.Unmarshal(class.Data.Value, &classData); err != nil {
return nil, types.ErrFailedToUnmarshalData.Wrapf(err.Error())
}

if err := k.validateClassParentRelation(ctx, class.Id, classData.Parent); err != nil {
return nil, err
}
Expand Down
135 changes: 135 additions & 0 deletions x/likenft/keeper/msg_server_update_class_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2037,3 +2037,138 @@ func TestUpdateClassNoMintPeriod(t *testing.T) {
// Check mock was called as expected
ctrl.Finish()
}

func TestUpdateClassMaxSupplyNotLessThanMintableCount(t *testing.T) {
// Setup
ctrl := gomock.NewController(t)
accountKeeper := testutil.NewMockAccountKeeper(ctrl)
bankKeeper := testutil.NewMockBankKeeper(ctrl)
iscnKeeper := testutil.NewMockIscnKeeper(ctrl)
nftKeeper := testutil.NewMockNftKeeper(ctrl)
msgServer, goCtx, keeper := setupMsgServer(t, keeper.LikenftDependedKeepers{
AccountKeeper: accountKeeper,
BankKeeper: bankKeeper,
IscnKeeper: iscnKeeper,
NftKeeper: nftKeeper,
})
ctx := sdk.UnwrapSDKContext(goCtx)

// Test Input
ownerAddressBytes := []byte{0, 1, 0, 1, 0, 1, 0, 1, 1, 1}
ownerAddress, _ := sdk.Bech32ifyAddressBytes("cosmos", ownerAddressBytes)
classId := "likenft1aabbccddeeff"
iscnId := iscntypes.NewIscnId("likecoin-chain", "abcdef", 1)
name := "Class Name"
symbol := "ABC"
description := "Testing Class 123"
uri := "ipfs://abcdef"
uriHash := "abcdef"
metadata := types.JsonInput(
`{
"abc": "def",
"qwerty": 1234,
"bool": false,
"null": null,
"nested": {
"object": {
"abc": "def"
}
}
}`)
burnable := true
maxSupply := uint64(499)

// Mock keeper calls
oldClassData := types.ClassData{
Metadata: types.JsonInput(`{"aaaa": "bbbb"}`),
Parent: types.ClassParent{
Type: types.ClassParentType_ISCN,
IscnIdPrefix: iscnId.Prefix.String(),
},
Config: types.ClassConfig{
Burnable: false,
MaxSupply: uint64(500),
},
MintableCount: uint64(500),
}
oldClassDataInAny, _ := cdctypes.NewAnyWithValue(&oldClassData)
nftKeeper.
EXPECT().
GetClass(gomock.Any(), classId).
Return(nft.Class{
Id: classId,
Name: "Old Name",
Symbol: "OLD",
Description: "Old Class 234",
Uri: "ipfs://11223344",
UriHash: "11223344",
Data: oldClassDataInAny,
}, true)

mintPeriods := []types.MintPeriod{
{
StartTime: *testutil.MustParseTime(time.RFC3339, "2022-04-19T00:00:00Z"),
AllowedAddresses: []string{ownerAddress},
MintPrice: uint64(20000),
},
{
StartTime: *testutil.MustParseTime(time.RFC3339, "2022-04-20T00:00:00Z"),
AllowedAddresses: []string{ownerAddress},
MintPrice: uint64(30000),
},
{
StartTime: *testutil.MustParseTime(time.RFC3339, "2022-04-21T00:00:00Z"),
AllowedAddresses: make([]string, 0),
MintPrice: uint64(90000),
},
}
revealTime := *testutil.MustParseTime(time.RFC3339, "2022-04-28T00:00:00Z")

nftKeeper.
EXPECT().
GetTotalSupply(gomock.Any(), classId).
Return(uint64(0))

keeper.SetClassesByISCN(ctx, types.ClassesByISCN{
IscnIdPrefix: iscnId.Prefix.String(),
ClassIds: []string{classId},
})

// Ensure queue is empty
revealQueue := keeper.GetClassRevealQueue(ctx)
require.Equal(t, 0, len(revealQueue))

// Run
res, err := msgServer.UpdateClass(goCtx, &types.MsgUpdateClass{
Creator: ownerAddress,
ClassId: classId,
Input: types.ClassInput{
Name: name,
Symbol: symbol,
Description: description,
Uri: uri,
UriHash: uriHash,
Metadata: metadata,
Config: types.ClassConfig{
Burnable: burnable,
MaxSupply: maxSupply,
BlindBoxConfig: &types.BlindBoxConfig{
MintPeriods: mintPeriods,
RevealTime: revealTime,
},
},
},
})

// Check output
require.Error(t, err)
require.Contains(t, err.Error(), sdkerrors.ErrInvalidRequest.Error())
require.Nil(t, res)

// Check class is not enqueued
revealQueue = keeper.GetClassRevealQueue(ctx)
require.Equal(t, 0, len(revealQueue))

// Check mock was called as expected
ctrl.Finish()
}

0 comments on commit 57a8024

Please sign in to comment.