Skip to content

Commit

Permalink
Add tests for clawback after transferring admin
Browse files Browse the repository at this point in the history
  • Loading branch information
masihyeganeh committed Apr 29, 2024
1 parent 4c0444a commit 1a9c290
Show file tree
Hide file tree
Showing 2 changed files with 300 additions and 0 deletions.
179 changes: 179 additions & 0 deletions integration-tests/modules/assetft_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7413,6 +7413,185 @@ func TestAssetFTTransferAdminGloballyFreeze(t *testing.T) {
requireT.NoError(err)
}

// TestAssetFTTransferAdminClawback checks clawback functionality of fungible tokens after transferring admin.
func TestAssetFTTransferAdminClawback(t *testing.T) {
t.Parallel()

ctx, chain := integrationtests.NewCoreumTestingContext(t)

requireT := require.New(t)
assertT := assert.New(t)
clientCtx := chain.ClientContext

bankClient := banktypes.NewQueryClient(clientCtx)

issuer := chain.GenAccount()
admin := chain.GenAccount()
account := chain.GenAccount()
randomAddress := chain.GenAccount()
chain.FundAccountWithOptions(ctx, t, issuer, integration.BalancesOptions{
Messages: []sdk.Msg{
&assetfttypes.MsgIssue{},
&banktypes.MsgSend{},
&assetfttypes.MsgTransferAdmin{},
&assetfttypes.MsgClawback{},
},
Amount: chain.QueryAssetFTParams(ctx, t).IssueFee.Amount,
})
chain.FundAccountWithOptions(ctx, t, admin, integration.BalancesOptions{
Messages: []sdk.Msg{
&assetfttypes.MsgClawback{},
&assetfttypes.MsgClawback{},
},
Amount: chain.QueryAssetFTParams(ctx, t).IssueFee.Amount,
})
chain.FundAccountWithOptions(ctx, t, randomAddress, integration.BalancesOptions{
Messages: []sdk.Msg{
&assetfttypes.MsgClawback{},
},
})

// Issue the new fungible token
msg := &assetfttypes.MsgIssue{
Issuer: issuer.String(),
Symbol: "ABC",
Subunit: "uabc",
Precision: 6,
Description: "ABC Description",
InitialAmount: sdkmath.NewInt(1000),
Features: []assetfttypes.Feature{
assetfttypes.Feature_clawback,
},
}

msgSend := &banktypes.MsgSend{
FromAddress: issuer.String(),
ToAddress: account.String(),
Amount: sdk.NewCoins(
sdk.NewCoin(assetfttypes.BuildDenom(msg.Subunit, issuer), sdkmath.NewInt(1000)),
),
}

msgList := []sdk.Msg{
msg, msgSend,
}

res, err := client.BroadcastTx(
ctx,
chain.ClientContext.WithFromAddress(issuer),
chain.TxFactory().WithGas(chain.GasLimitByMsgs(msgList...)),
msgList...,
)

requireT.NoError(err)
fungibleTokenIssuedEvts, err := event.FindTypedEvents[*assetfttypes.EventIssued](res.Events)
requireT.NoError(err)
denom := fungibleTokenIssuedEvts[0].Denom

// transfer admin
transferAdminMsg := &assetfttypes.MsgTransferAdmin{
Sender: issuer.String(),
Account: admin.String(),
Denom: denom,
}
res, err = client.BroadcastTx(
ctx,
chain.ClientContext.WithFromAddress(issuer),
chain.TxFactory().WithGas(chain.GasLimitByMsgs(transferAdminMsg)),
transferAdminMsg,
)
requireT.NoError(err)
assertT.EqualValues(res.GasUsed, chain.GasLimitByMsgs(transferAdminMsg))

adminTransferredEvts, err := event.FindTypedEvents[*assetfttypes.EventAdminTransferred](res.Events)
requireT.NoError(err)
assertT.EqualValues(&assetfttypes.EventAdminTransferred{
Denom: denom,
PreviousAdmin: issuer.String(),
CurrentAdmin: admin.String(),
}, adminTransferredEvts[0])

// query account balance before clawback
bankRes, err := bankClient.Balance(ctx, banktypes.NewQueryBalanceRequest(account, denom))
requireT.NoError(err)
requireT.EqualValues(sdk.NewCoin(denom, sdkmath.NewInt(1000)).String(), bankRes.Balance.String())

// try to pass non-admin signature to clawback msg
clawbackMsg := &assetfttypes.MsgClawback{
Sender: randomAddress.String(),
Account: account.String(),
Coin: sdk.NewCoin(denom, sdkmath.NewInt(1000)),
}
_, err = client.BroadcastTx(
ctx,
chain.ClientContext.WithFromAddress(randomAddress),
chain.TxFactory().WithGas(chain.GasLimitByMsgs(clawbackMsg)),
clawbackMsg,
)
requireT.Error(err)
requireT.ErrorIs(err, cosmoserrors.ErrUnauthorized)

// try to pass original issuer signature which is not admin anymore to clawback msg
clawbackMsg = &assetfttypes.MsgClawback{
Sender: issuer.String(),
Account: account.String(),
Coin: sdk.NewCoin(denom, sdkmath.NewInt(1000)),
}
_, err = client.BroadcastTx(
ctx,
chain.ClientContext.WithFromAddress(issuer),
chain.TxFactory().WithGas(chain.GasLimitByMsgs(clawbackMsg)),
clawbackMsg,
)
requireT.Error(err)
requireT.ErrorIs(err, cosmoserrors.ErrUnauthorized)

// clawback 400 tokens
clawbackMsg = &assetfttypes.MsgClawback{
Sender: admin.String(),
Account: account.String(),
Coin: sdk.NewCoin(denom, sdkmath.NewInt(400)),
}
res, err = client.BroadcastTx(
ctx,
chain.ClientContext.WithFromAddress(admin),
chain.TxFactory().WithGas(chain.GasLimitByMsgs(clawbackMsg)),
clawbackMsg,
)
requireT.NoError(err)
assertT.EqualValues(res.GasUsed, chain.GasLimitByMsgs(clawbackMsg))

amountClawedBackEvts, err := event.FindTypedEvents[*assetfttypes.EventAmountClawedBack](res.Events)
requireT.NoError(err)
assertT.EqualValues(&assetfttypes.EventAmountClawedBack{
Account: account.String(),
Denom: denom,
Amount: sdkmath.NewInt(400),
}, amountClawedBackEvts[0])

// query account balance after clawback
bankRes, err = bankClient.Balance(ctx, banktypes.NewQueryBalanceRequest(account, denom))
requireT.NoError(err)
requireT.EqualValues(sdk.NewCoin(denom, sdkmath.NewInt(600)).String(), bankRes.Balance.String())

// try to clawback more than available (650) (600 is available)
coinsToClawback := sdk.NewCoin(denom, sdkmath.NewInt(650))

clawbackMsg = &assetfttypes.MsgClawback{
Sender: admin.String(),
Account: account.String(),
Coin: coinsToClawback,
}
_, err = client.BroadcastTx(
ctx,
chain.ClientContext.WithFromAddress(admin),
chain.TxFactory().WithGas(chain.GasLimitByMsgs(clawbackMsg)),
clawbackMsg,
)
requireT.Error(err)
requireT.ErrorIs(err, cosmoserrors.ErrInsufficientFunds)
}

// TestAssetFTTransferAdminWhitelist checks whitelist functionality of fungible tokens after transferring admin.
func TestAssetFTTransferAdminWhitelist(t *testing.T) {
t.Parallel()
Expand Down
121 changes: 121 additions & 0 deletions x/asset/ft/keeper/keeper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2276,6 +2276,127 @@ func TestKeeper_TransferAdmin_GlobalFreezeUnfreeze(t *testing.T) {
requireT.Equal(sdk.NewCoin(freezableDenom, sdkmath.NewInt(12)), balance)
}

func TestKeeper_TransferAdmin_Clawback(t *testing.T) {
requireT := require.New(t)
assertT := assert.New(t)

testApp := simapp.New()
ctx := testApp.BaseApp.NewContext(false, tmproto.Header{})

ftKeeper := testApp.AssetFTKeeper
bankKeeper := testApp.BankKeeper
accountKeeper := testApp.AccountKeeper

issuer := sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address())
admin := sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address())

settings := types.IssueSettings{
Issuer: issuer,
Symbol: "DEF",
Subunit: "def",
Precision: 1,
Description: "DEF Desc",
InitialAmount: sdkmath.NewInt(1666),
Features: []types.Feature{
types.Feature_clawback,
types.Feature_freezing,
},
}

denom, err := ftKeeper.Issue(ctx, settings)
requireT.NoError(err)

err = bankKeeper.SendCoins(ctx, issuer, admin, sdk.Coins{sdk.NewCoin(denom, sdkmath.NewInt(666))})
requireT.NoError(err)

// transfer the denom to new admin
err = ftKeeper.TransferAdmin(ctx, issuer, admin, denom)
requireT.NoError(err)

clawbackDisabledSettings := types.IssueSettings{
Issuer: issuer,
Symbol: "ABC",
Subunit: "abc",
Precision: 1,
Description: "ABC Desc",
InitialAmount: sdkmath.NewInt(1666),
Features: []types.Feature{},
}

clawbackDisabledDenom, err := ftKeeper.Issue(ctx, clawbackDisabledSettings)
requireT.NoError(err)
_, err = ftKeeper.GetToken(ctx, clawbackDisabledDenom)
requireT.NoError(err)

err = bankKeeper.SendCoins(ctx, issuer, admin, sdk.Coins{sdk.NewCoin(clawbackDisabledDenom, sdkmath.NewInt(666))})
requireT.NoError(err)

// transfer the clawbackDisabledDenom to new admin
err = ftKeeper.TransferAdmin(ctx, issuer, admin, clawbackDisabledDenom)
requireT.NoError(err)

account := sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address())
err = bankKeeper.SendCoins(ctx, admin, account, sdk.NewCoins(
sdk.NewCoin(denom, sdkmath.NewInt(100)),
sdk.NewCoin(clawbackDisabledDenom, sdkmath.NewInt(100)),
))
requireT.NoError(err)

// try to clawback non-existent denom
nonExistentDenom := types.BuildDenom("nonexist", admin)
err = ftKeeper.Clawback(ctx, admin, account, sdk.NewCoin(nonExistentDenom, sdkmath.NewInt(10)))
assertT.True(sdkerrors.IsOf(err, types.ErrTokenNotFound))

// try to clawback clawbackDisabled Token
err = ftKeeper.Clawback(ctx, admin, account, sdk.NewCoin(clawbackDisabledDenom, sdkmath.NewInt(10)))
requireT.ErrorIs(err, types.ErrFeatureDisabled)

// try to clawback by non admin address
randomAddr := sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address())
err = ftKeeper.Clawback(ctx, randomAddr, account, sdk.NewCoin(denom, sdkmath.NewInt(10)))
requireT.ErrorIs(err, cosmoserrors.ErrUnauthorized)

// try to clawback from admin address
randomAddr = sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address())
err = ftKeeper.Clawback(ctx, randomAddr, admin, sdk.NewCoin(denom, sdkmath.NewInt(10)))
requireT.ErrorIs(err, cosmoserrors.ErrUnauthorized)

// try to clawback from module address
moduleAddr := accountKeeper.GetModuleAccount(ctx, types.ModuleName).GetAddress()
err = ftKeeper.Clawback(ctx, admin, moduleAddr, sdk.NewCoin(denom, sdkmath.NewInt(10)))
requireT.ErrorIs(err, cosmoserrors.ErrUnauthorized)

// try to clawback from the original issuer address which is not admin anymore
err = ftKeeper.Clawback(ctx, issuer, account, sdk.NewCoin(denom, sdkmath.NewInt(10)))
requireT.ErrorIs(err, cosmoserrors.ErrUnauthorized)

// try to clawback 0 balance
err = ftKeeper.Clawback(ctx, admin, account, sdk.NewCoin(denom, sdkmath.NewInt(0)))
requireT.ErrorIs(err, cosmoserrors.ErrInvalidCoins)

// try to clawback more than balance
err = ftKeeper.Clawback(ctx, admin, account, sdk.NewCoin(denom, sdkmath.NewInt(110)))
requireT.ErrorIs(err, cosmoserrors.ErrInsufficientFunds)

// clawback, query balance
issuerBalanceBefore := bankKeeper.GetBalance(ctx, admin, denom)
accountBalanceBefore := bankKeeper.GetBalance(ctx, account, denom)
err = ftKeeper.Clawback(ctx, admin, account, sdk.NewCoin(denom, sdkmath.NewInt(40)))
requireT.NoError(err)
issuerBalanceAfter := bankKeeper.GetBalance(ctx, admin, denom)
accountBalanceAfter := bankKeeper.GetBalance(ctx, account, denom)
requireT.Equal(issuerBalanceBefore.Add(sdk.NewCoin(denom, sdkmath.NewInt(40))), issuerBalanceAfter)
requireT.Equal(accountBalanceBefore.Sub(sdk.NewCoin(denom, sdkmath.NewInt(40))), accountBalanceAfter)

// clawback frozen token, query balance
err = ftKeeper.Freeze(ctx, admin, account, sdk.NewCoin(denom, sdkmath.NewInt(60)))
requireT.NoError(err)
err = ftKeeper.Clawback(ctx, admin, account, sdk.NewCoin(denom, sdkmath.NewInt(60)))
requireT.NoError(err)
accountBalance := bankKeeper.GetBalance(ctx, account, denom)
requireT.Equal(sdk.NewCoin(denom, sdkmath.NewInt(0)), accountBalance)
}

func TestKeeper_TransferAdmin_Whitelist(t *testing.T) {
requireT := require.New(t)
assertT := assert.New(t)
Expand Down

0 comments on commit 1a9c290

Please sign in to comment.