From af95c4c0b2228197a8220dc34ea5f2c44d783667 Mon Sep 17 00:00:00 2001 From: Masih Yeganeh Date: Fri, 12 Apr 2024 15:24:21 +0330 Subject: [PATCH 01/12] Implement Clawback feature for assetft --- docs/api.md | 38 ++ docs/static/openapi.json | 3 +- integration-tests/modules/assetft_test.go | 144 ++++++++ proto/coreum/asset/ft/v1/event.proto | 9 + proto/coreum/asset/ft/v1/token.proto | 1 + proto/coreum/asset/ft/v1/tx.proto | 10 + x/asset/ft/client/cli/tx.go | 43 +++ x/asset/ft/client/cli/tx_test.go | 37 ++ x/asset/ft/keeper/keeper.go | 38 ++ x/asset/ft/keeper/keeper_test.go | 80 ++++ x/asset/ft/keeper/msg_server.go | 22 ++ x/asset/ft/types/codec.go | 1 + x/asset/ft/types/event.pb.go | 344 +++++++++++++++-- x/asset/ft/types/msgs.go | 46 +++ x/asset/ft/types/token.pb.go | 98 ++--- x/asset/ft/types/tx.pb.go | 428 ++++++++++++++++++---- x/deterministicgas/config.go | 1 + 17 files changed, 1191 insertions(+), 152 deletions(-) diff --git a/docs/api.md b/docs/api.md index fe8f033d7..7d281e445 100644 --- a/docs/api.md +++ b/docs/api.md @@ -10,6 +10,7 @@ - [MintAuthorization](#coreum.asset.ft.v1.MintAuthorization) - [coreum/asset/ft/v1/event.proto](#coreum/asset/ft/v1/event.proto) + - [EventAmountClawedBack](#coreum.asset.ft.v1.EventAmountClawedBack) - [EventFrozenAmountChanged](#coreum.asset.ft.v1.EventFrozenAmountChanged) - [EventIssued](#coreum.asset.ft.v1.EventIssued) - [EventWhitelistedAmountChanged](#coreum.asset.ft.v1.EventWhitelistedAmountChanged) @@ -56,6 +57,7 @@ - [coreum/asset/ft/v1/tx.proto](#coreum/asset/ft/v1/tx.proto) - [EmptyResponse](#coreum.asset.ft.v1.EmptyResponse) - [MsgBurn](#coreum.asset.ft.v1.MsgBurn) + - [MsgClawback](#coreum.asset.ft.v1.MsgClawback) - [MsgFreeze](#coreum.asset.ft.v1.MsgFreeze) - [MsgGloballyFreeze](#coreum.asset.ft.v1.MsgGloballyFreeze) - [MsgGloballyUnfreeze](#coreum.asset.ft.v1.MsgGloballyUnfreeze) @@ -1579,6 +1581,23 @@ the granter's account. + + +### EventAmountClawedBack + + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `account` | [string](#string) | | | +| `denom` | [string](#string) | | | +| `amount` | [string](#string) | | | + + + + + + ### EventFrozenAmountChanged @@ -2236,6 +2255,7 @@ Feature defines possible features of fungible token. | whitelisting | 3 | | | ibc | 4 | | | block_smart_contracts | 5 | | +| clawback | 6 | | @@ -2279,6 +2299,23 @@ Feature defines possible features of fungible token. + + +### MsgClawback + + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `sender` | [string](#string) | | | +| `account` | [string](#string) | | | +| `coin` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | | | + + + + + + ### MsgFreeze @@ -2487,6 +2524,7 @@ Msg defines the Msg service. | `SetFrozen` | [MsgSetFrozen](#coreum.asset.ft.v1.MsgSetFrozen) | [EmptyResponse](#coreum.asset.ft.v1.EmptyResponse) | `SetFrozen sets the absolute value of frozen amount.` | | | `GloballyFreeze` | [MsgGloballyFreeze](#coreum.asset.ft.v1.MsgGloballyFreeze) | [EmptyResponse](#coreum.asset.ft.v1.EmptyResponse) | `GloballyFreeze freezes fungible token so no operations are allowed with it before unfrozen. This operation is idempotent so global freeze of already frozen token does nothing.` | | | `GloballyUnfreeze` | [MsgGloballyUnfreeze](#coreum.asset.ft.v1.MsgGloballyUnfreeze) | [EmptyResponse](#coreum.asset.ft.v1.EmptyResponse) | `GloballyUnfreeze unfreezes fungible token and unblocks basic operations on it. This operation is idempotent so global unfreezing of non-frozen token does nothing.` | | +| `Clawback` | [MsgClawback](#coreum.asset.ft.v1.MsgClawback) | [EmptyResponse](#coreum.asset.ft.v1.EmptyResponse) | `Clawback returns a part of fungible tokens from an account to the issuer, only if the clawback feature is enabled on that token.` | | | `SetWhitelistedLimit` | [MsgSetWhitelistedLimit](#coreum.asset.ft.v1.MsgSetWhitelistedLimit) | [EmptyResponse](#coreum.asset.ft.v1.EmptyResponse) | `SetWhitelistedLimit sets the limit of how many tokens a specific account may hold.` | | | `UpgradeTokenV1` | [MsgUpgradeTokenV1](#coreum.asset.ft.v1.MsgUpgradeTokenV1) | [EmptyResponse](#coreum.asset.ft.v1.EmptyResponse) | `TokenUpgradeV1 upgrades token to version V1.` | | | `UpdateParams` | [MsgUpdateParams](#coreum.asset.ft.v1.MsgUpdateParams) | [EmptyResponse](#coreum.asset.ft.v1.EmptyResponse) | `UpdateParams is a governance operation to modify the parameters of the module. NOTE: all parameters must be provided.` | | diff --git a/docs/static/openapi.json b/docs/static/openapi.json index ef60f1973..090af5fe5 100644 --- a/docs/static/openapi.json +++ b/docs/static/openapi.json @@ -9090,7 +9090,8 @@ "freezing", "whitelisting", "ibc", - "block_smart_contracts" + "block_smart_contracts", + "clawback" ], "default": "minting", "description": "Feature defines possible features of fungible token." diff --git a/integration-tests/modules/assetft_test.go b/integration-tests/modules/assetft_test.go index 9d0775217..26459e73f 100644 --- a/integration-tests/modules/assetft_test.go +++ b/integration-tests/modules/assetft_test.go @@ -2031,6 +2031,150 @@ func TestAssetFTFreeze(t *testing.T) { }, fungibleTokenFreezeEvts[0]) } +// TestAssetFTClawback checks clawback functionality of fungible tokens. +func TestAssetFTClawback(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() + from := chain.GenAccount() + randomAddress := chain.GenAccount() + chain.FundAccountWithOptions(ctx, t, issuer, integration.BalancesOptions{ + Messages: []sdk.Msg{ + &assetfttypes.MsgIssue{}, + &banktypes.MsgSend{}, + &assetfttypes.MsgClawback{}, + }, + Amount: chain.QueryAssetFTParams(ctx, t).IssueFee.Amount, + }) + chain.FundAccountWithOptions(ctx, t, from, integration.BalancesOptions{ + Messages: []sdk.Msg{ + &banktypes.MsgSend{}, + &banktypes.MsgMultiSend{}, + &banktypes.MsgSend{}, + &banktypes.MsgMultiSend{}, + &banktypes.MsgSend{}, + &banktypes.MsgSend{}, + &assetfttypes.MsgClawback{}, + }, + }) + chain.FundAccountWithOptions(ctx, t, randomAddress, integration.BalancesOptions{ + Messages: []sdk.Msg{ + &assetfttypes.MsgFreeze{}, + &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: from.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 + + // query account balance before clawback + bankRes, err := bankClient.Balance(ctx, banktypes.NewQueryBalanceRequest(from, denom)) + requireT.NoError(err) + requireT.EqualValues(sdk.NewCoin(denom, sdkmath.NewInt(1000)).String(), bankRes.Balance.String()) + + // try to pass non-issuer signature to clawback msg + clawbackMsg := &assetfttypes.MsgClawback{ + Sender: randomAddress.String(), + Account: from.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) + assertT.True(cosmoserrors.ErrUnauthorized.Is(err)) + + // clawback 400 tokens + clawbackMsg = &assetfttypes.MsgClawback{ + Sender: issuer.String(), + Account: from.String(), + Coin: sdk.NewCoin(denom, sdkmath.NewInt(400)), + } + res, err = client.BroadcastTx( + ctx, + chain.ClientContext.WithFromAddress(issuer), + chain.TxFactory().WithGas(chain.GasLimitByMsgs(clawbackMsg)), + clawbackMsg, + ) + requireT.NoError(err) + assertT.EqualValues(res.GasUsed, chain.GasLimitByMsgs(clawbackMsg)) + + fungibleTokenClawbackEvts, err := event.FindTypedEvents[*assetfttypes.EventAmountClawedBack](res.Events) + requireT.NoError(err) + assertT.EqualValues(&assetfttypes.EventAmountClawedBack{ + Account: from.String(), + Denom: denom, + Amount: sdkmath.NewInt(400), + }, fungibleTokenClawbackEvts[0]) + + // query account balance after clawback + bankRes, err = bankClient.Balance(ctx, banktypes.NewQueryBalanceRequest(from, 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: issuer.String(), + Account: from.String(), + Coin: coinsToClawback, + } + _, err = client.BroadcastTx( + ctx, + chain.ClientContext.WithFromAddress(from), + chain.TxFactory().WithGas(chain.GasLimitByMsgs(clawbackMsg)), + clawbackMsg, + ) + requireT.Error(err) + assertT.True(cosmoserrors.ErrInsufficientFunds.Is(err)) +} + // TestAssetFTFreezeUnfreezable checks freeze functionality on unfreezable fungible tokens. func TestAssetFTFreezeUnfreezable(t *testing.T) { t.Parallel() diff --git a/proto/coreum/asset/ft/v1/event.proto b/proto/coreum/asset/ft/v1/event.proto index 963a325ad..173f90f9c 100644 --- a/proto/coreum/asset/ft/v1/event.proto +++ b/proto/coreum/asset/ft/v1/event.proto @@ -44,6 +44,15 @@ message EventFrozenAmountChanged { ]; } +message EventAmountClawedBack { + string account = 1; + string denom = 2; + string amount = 3 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", + (gogoproto.nullable) = false + ]; +} + message EventWhitelistedAmountChanged { string account = 1; string denom = 2; diff --git a/proto/coreum/asset/ft/v1/token.proto b/proto/coreum/asset/ft/v1/token.proto index aa270a2ee..89965e57f 100644 --- a/proto/coreum/asset/ft/v1/token.proto +++ b/proto/coreum/asset/ft/v1/token.proto @@ -14,6 +14,7 @@ enum Feature { whitelisting = 3; ibc = 4; block_smart_contracts = 5; + clawback = 6; } // Definition defines the fungible token settings to store. diff --git a/proto/coreum/asset/ft/v1/tx.proto b/proto/coreum/asset/ft/v1/tx.proto index b54675ad6..fc2bdf6dc 100644 --- a/proto/coreum/asset/ft/v1/tx.proto +++ b/proto/coreum/asset/ft/v1/tx.proto @@ -38,6 +38,10 @@ service Msg { // This operation is idempotent so global unfreezing of non-frozen token does nothing. rpc GloballyUnfreeze(MsgGloballyUnfreeze) returns (EmptyResponse); + // Clawback confiscates a part of fungible tokens from an account + // to the issuer, only if the clawback feature is enabled on that token. + rpc Clawback(MsgClawback) returns (EmptyResponse); + // SetWhitelistedLimit sets the limit of how many tokens a specific account may hold. rpc SetWhitelistedLimit(MsgSetWhitelistedLimit) returns (EmptyResponse); @@ -116,6 +120,12 @@ message MsgGloballyUnfreeze { string denom = 2; } +message MsgClawback { + string sender = 1; + string account = 2; + cosmos.base.v1beta1.Coin coin = 3 [(gogoproto.nullable) = false]; +} + message MsgSetWhitelistedLimit { string sender = 1; string account = 2; diff --git a/x/asset/ft/client/cli/tx.go b/x/asset/ft/client/cli/tx.go index 51109b5c8..ece772f01 100644 --- a/x/asset/ft/client/cli/tx.go +++ b/x/asset/ft/client/cli/tx.go @@ -55,6 +55,7 @@ func GetTxCmd() *cobra.Command { CmdTxSetFrozen(), CmdTxGloballyFreeze(), CmdTxGloballyUnfreeze(), + CmdTxClawback(), CmdTxSetWhitelistedLimit(), CmdTxUpgradeV1(), CmdGrantAuthorization(), @@ -415,6 +416,48 @@ $ %s tx %s set-frozen [account_address] 100000ABC-%s --from [sender] return cmd } +// CmdTxClawback returns Clawback cobra command. +func CmdTxClawback() *cobra.Command { + cmd := &cobra.Command{ + Use: "clawback [account_address] [amount] --from [sender]", + Args: cobra.ExactArgs(2), + Short: "Confiscates any amount of fungible token from the specific account", + Long: strings.TrimSpace( + fmt.Sprintf(`Confiscate a portion of fungible token. + +Example: +$ %s tx %s clawback [account_address] 100000ABC-%s --from [sender] +`, + version.AppName, types.ModuleName, constant.AddressSampleTest, + ), + ), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return errors.WithStack(err) + } + + sender := clientCtx.GetFromAddress() + account := args[0] + amount, err := sdk.ParseCoinNormalized(args[1]) + if err != nil { + return sdkerrors.Wrap(err, "invalid amount") + } + + msg := &types.MsgClawback{ + Sender: sender.String(), + Account: account, + Coin: amount, + } + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + }, + } + + flags.AddTxFlagsToCmd(cmd) + + return cmd +} + // CmdTxSetWhitelistedLimit returns SetWhitelistedLimit cobra command. // //nolint:dupl // most code is identical, but reusing logic is not beneficial here. diff --git a/x/asset/ft/client/cli/tx_test.go b/x/asset/ft/client/cli/tx_test.go index 8aa72bde7..dee379f29 100644 --- a/x/asset/ft/client/cli/tx_test.go +++ b/x/asset/ft/client/cli/tx_test.go @@ -273,6 +273,43 @@ func TestGloballyFreezeUnfreeze(t *testing.T) { requireT.False(resp.Token.GloballyFrozen) } +func TestClawback(t *testing.T) { + requireT := require.New(t) + testNetwork := network.New(t) + + token := types.Token{ + Symbol: "btc" + uuid.NewString()[:4], + Subunit: "satoshi" + uuid.NewString()[:4], + Precision: 8, + Description: "description", + Features: []types.Feature{ + types.Feature_clawback, + }, + } + + ctx := testNetwork.Validators[0].ClientCtx + initialAmount := sdkmath.NewInt(777) + denom := issue(requireT, ctx, token, initialAmount, testNetwork) + account := sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()) + + coin := sdk.NewInt64Coin(denom, 100) + + args := append([]string{testNetwork.Validators[0].Address.String(), account.String(), coin.String()}, txValidator1Args(testNetwork)...) + _, err := coreumclitestutil.ExecTxCmd(ctx, testNetwork, bankcli.NewSendTxCmd(), args) + requireT.NoError(err) + + var balanceRsp banktypes.QueryAllBalancesResponse + requireT.NoError(coreumclitestutil.ExecQueryCmd(ctx, bankcli.GetBalancesCmd(), []string{account.String()}, &balanceRsp)) + requireT.Equal(sdkmath.NewInt(100).String(), balanceRsp.Balances.AmountOf(denom).String()) + + args = append([]string{account.String(), coin.String()}, txValidator1Args(testNetwork)...) + _, err = coreumclitestutil.ExecTxCmd(ctx, testNetwork, cli.CmdTxClawback(), args) + requireT.NoError(err) + + requireT.NoError(coreumclitestutil.ExecQueryCmd(ctx, bankcli.GetBalancesCmd(), []string{account.String()}, &balanceRsp)) + requireT.Equal(sdkmath.NewInt(0).String(), balanceRsp.Balances.AmountOf(denom).String()) +} + func TestWhitelistAndQueryWhitelisted(t *testing.T) { requireT := require.New(t) testNetwork := network.New(t) diff --git a/x/asset/ft/keeper/keeper.go b/x/asset/ft/keeper/keeper.go index 54e3e1683..4ba244ca1 100644 --- a/x/asset/ft/keeper/keeper.go +++ b/x/asset/ft/keeper/keeper.go @@ -532,6 +532,31 @@ func (k Keeper) SetGlobalFreeze(ctx sdk.Context, denom string, frozen bool) { ctx.KVStore(k.storeKey).Delete(types.CreateGlobalFreezeKey(denom)) } +// Clawback confiscates specified token from the specified account. +func (k Keeper) Clawback(ctx sdk.Context, sender, addr sdk.AccAddress, coin sdk.Coin) error { + if !coin.IsPositive() { + return sdkerrors.Wrap(cosmoserrors.ErrInvalidCoins, "clawback amount should be positive") + } + + if err := k.clawbackChecks(ctx, sender, addr, coin); err != nil { + return err + } + + if err := k.bankKeeper.SendCoins(ctx, addr, sender, sdk.NewCoins(coin)); err != nil { + return sdkerrors.Wrapf(err, "can't send coins from account %s to issuer %s", addr.String(), sender.String()) + } + + if err := ctx.EventManager().EmitTypedEvent(&types.EventAmountClawedBack{ + Account: addr.String(), + Denom: coin.Denom, + Amount: coin.Amount, + }); err != nil { + return sdkerrors.Wrapf(types.ErrInvalidState, "failed to emit EventAmountClawedBack event: %s", err) + } + + return nil +} + // SetWhitelistedBalance sets whitelisted limit for the account. func (k Keeper) SetWhitelistedBalance(ctx sdk.Context, sender, addr sdk.AccAddress, coin sdk.Coin) error { if coin.IsNil() || coin.IsNegative() { @@ -924,6 +949,19 @@ func (k Keeper) isGloballyFrozen(ctx sdk.Context, denom string) bool { return bytes.Equal(ctx.KVStore(k.storeKey).Get(types.CreateGlobalFreezeKey(denom)), types.StoreTrue) } +func (k Keeper) clawbackChecks(ctx sdk.Context, sender, addr sdk.AccAddress, coin sdk.Coin) error { + def, err := k.GetDefinition(ctx, coin.Denom) + if err != nil { + return sdkerrors.Wrapf(err, "not able to get token info for denom:%s", coin.Denom) + } + + if def.IsIssuer(addr) { + return sdkerrors.Wrap(cosmoserrors.ErrUnauthorized, "issuer's balance can't be clawed back") + } + + return def.CheckFeatureAllowed(sender, types.Feature_clawback) +} + // whitelistedAccountBalanceStore gets the store for the whitelisted balances of an account. func (k Keeper) whitelistedAccountBalanceStore(ctx sdk.Context, addr sdk.AccAddress) balanceStore { return newBalanceStore(k.cdc, ctx.KVStore(k.storeKey), types.CreateWhitelistedBalancesKey(addr)) diff --git a/x/asset/ft/keeper/keeper_test.go b/x/asset/ft/keeper/keeper_test.go index 3d8ec959f..542395c91 100644 --- a/x/asset/ft/keeper/keeper_test.go +++ b/x/asset/ft/keeper/keeper_test.go @@ -1264,6 +1264,86 @@ func TestKeeper_GlobalFreezeUnfreeze(t *testing.T) { requireT.Equal(sdk.NewCoin(freezableDenom, sdkmath.NewInt(12)), balance) } +func TestKeeper_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 + + issuer := sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()) + + settings := types.IssueSettings{ + Issuer: issuer, + Symbol: "DEF", + Subunit: "def", + Precision: 1, + Description: "DEF Desc", + InitialAmount: sdkmath.NewInt(666), + Features: []types.Feature{types.Feature_clawback}, + } + + denom, err := ftKeeper.Issue(ctx, settings) + requireT.NoError(err) + + clawbackDisabledSettings := types.IssueSettings{ + Issuer: issuer, + Symbol: "ABC", + Subunit: "abc", + Precision: 1, + Description: "ABC Desc", + InitialAmount: sdkmath.NewInt(666), + Features: []types.Feature{}, + } + + clawbackDisabledDenom, err := ftKeeper.Issue(ctx, clawbackDisabledSettings) + requireT.NoError(err) + _, err = ftKeeper.GetToken(ctx, clawbackDisabledDenom) + requireT.NoError(err) + + from := sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()) + err = bankKeeper.SendCoins(ctx, issuer, from, 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", issuer) + err = ftKeeper.Clawback(ctx, issuer, from, sdk.NewCoin(nonExistentDenom, sdkmath.NewInt(10))) + assertT.True(sdkerrors.IsOf(err, types.ErrTokenNotFound)) + + // try to clawback clawbackDisabled Token + err = ftKeeper.Clawback(ctx, issuer, from, sdk.NewCoin(clawbackDisabledDenom, sdkmath.NewInt(10))) + requireT.ErrorIs(err, types.ErrFeatureDisabled) + + // try to clawback from non issuer address + randomAddr := sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()) + err = ftKeeper.Clawback(ctx, randomAddr, from, sdk.NewCoin(denom, sdkmath.NewInt(10))) + requireT.ErrorIs(err, cosmoserrors.ErrUnauthorized) + + // try to clawback 0 balance + err = ftKeeper.Clawback(ctx, issuer, from, sdk.NewCoin(denom, sdkmath.NewInt(0))) + requireT.ErrorIs(err, cosmoserrors.ErrInvalidCoins) + + // try to clawback more than balance + err = ftKeeper.Clawback(ctx, issuer, from, sdk.NewCoin(denom, sdkmath.NewInt(110))) + requireT.ErrorIs(err, cosmoserrors.ErrInsufficientFunds) + + // clawback, query balance + issuerBalanceBefore := bankKeeper.GetBalance(ctx, issuer, denom) + accountBalanceBefore := bankKeeper.GetBalance(ctx, from, denom) + err = ftKeeper.Clawback(ctx, issuer, from, sdk.NewCoin(denom, sdkmath.NewInt(40))) + requireT.NoError(err) + issuerBalanceAfter := bankKeeper.GetBalance(ctx, issuer, denom) + accountBalanceAfter := bankKeeper.GetBalance(ctx, from, denom) + requireT.Equal(issuerBalanceBefore.Add(sdk.NewCoin(denom, sdkmath.NewInt(40))), issuerBalanceAfter) + requireT.Equal(accountBalanceBefore.Sub(sdk.NewCoin(denom, sdkmath.NewInt(40))), accountBalanceAfter) +} + func TestKeeper_Whitelist(t *testing.T) { requireT := require.New(t) assertT := assert.New(t) diff --git a/x/asset/ft/keeper/msg_server.go b/x/asset/ft/keeper/msg_server.go index f70af6d12..9060965d0 100644 --- a/x/asset/ft/keeper/msg_server.go +++ b/x/asset/ft/keeper/msg_server.go @@ -24,6 +24,7 @@ type MsgKeeper interface { SetFrozen(ctx sdk.Context, sender, addr sdk.AccAddress, coin sdk.Coin) error GloballyFreeze(ctx sdk.Context, sender sdk.AccAddress, denom string) error GloballyUnfreeze(ctx sdk.Context, sender sdk.AccAddress, denom string) error + Clawback(ctx sdk.Context, sender, addr sdk.AccAddress, coin sdk.Coin) error SetWhitelistedBalance(ctx sdk.Context, sender, addr sdk.AccAddress, coin sdk.Coin) error AddDelayedTokenUpgradeV1(ctx sdk.Context, sender sdk.AccAddress, denom string, ibcEnabled bool) error UpdateParams(ctx sdk.Context, authority string, params types.Params) error @@ -203,6 +204,27 @@ func (ms MsgServer) GloballyUnfreeze( return &types.EmptyResponse{}, nil } +// Clawback confiscates a part of fungible tokens from an account to the issuer. +func (ms MsgServer) Clawback(goCtx context.Context, req *types.MsgClawback) (*types.EmptyResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + sender, err := sdk.AccAddressFromBech32(req.Sender) + if err != nil { + return nil, sdkerrors.Wrap(cosmoserrors.ErrInvalidAddress, "invalid sender address") + } + + account, err := sdk.AccAddressFromBech32(req.Account) + if err != nil { + return nil, sdkerrors.Wrap(cosmoserrors.ErrInvalidAddress, "invalid account address") + } + + err = ms.keeper.Clawback(ctx, sender, account, req.Coin) + if err != nil { + return nil, err + } + + return &types.EmptyResponse{}, nil +} + // SetWhitelistedLimit sets the limit of how many tokens account may hold. func (ms MsgServer) SetWhitelistedLimit( goCtx context.Context, diff --git a/x/asset/ft/types/codec.go b/x/asset/ft/types/codec.go index 2f2e13ae6..0af6b10c8 100644 --- a/x/asset/ft/types/codec.go +++ b/x/asset/ft/types/codec.go @@ -19,6 +19,7 @@ func RegisterInterfaces(registry cdctypes.InterfaceRegistry) { &MsgSetFrozen{}, &MsgGloballyFreeze{}, &MsgGloballyUnfreeze{}, + &MsgClawback{}, &MsgSetWhitelistedLimit{}, &MsgUpgradeTokenV1{}, ) diff --git a/x/asset/ft/types/event.pb.go b/x/asset/ft/types/event.pb.go index 2cf0749f3..9d3f25366 100644 --- a/x/asset/ft/types/event.pb.go +++ b/x/asset/ft/types/event.pb.go @@ -190,6 +190,59 @@ func (m *EventFrozenAmountChanged) GetDenom() string { return "" } +type EventAmountClawedBack struct { + Account string `protobuf:"bytes,1,opt,name=account,proto3" json:"account,omitempty"` + Denom string `protobuf:"bytes,2,opt,name=denom,proto3" json:"denom,omitempty"` + Amount github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,3,opt,name=amount,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"amount"` +} + +func (m *EventAmountClawedBack) Reset() { *m = EventAmountClawedBack{} } +func (m *EventAmountClawedBack) String() string { return proto.CompactTextString(m) } +func (*EventAmountClawedBack) ProtoMessage() {} +func (*EventAmountClawedBack) Descriptor() ([]byte, []int) { + return fileDescriptor_bdf87682d70b967f, []int{2} +} +func (m *EventAmountClawedBack) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *EventAmountClawedBack) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_EventAmountClawedBack.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *EventAmountClawedBack) XXX_Merge(src proto.Message) { + xxx_messageInfo_EventAmountClawedBack.Merge(m, src) +} +func (m *EventAmountClawedBack) XXX_Size() int { + return m.Size() +} +func (m *EventAmountClawedBack) XXX_DiscardUnknown() { + xxx_messageInfo_EventAmountClawedBack.DiscardUnknown(m) +} + +var xxx_messageInfo_EventAmountClawedBack proto.InternalMessageInfo + +func (m *EventAmountClawedBack) GetAccount() string { + if m != nil { + return m.Account + } + return "" +} + +func (m *EventAmountClawedBack) GetDenom() string { + if m != nil { + return m.Denom + } + return "" +} + type EventWhitelistedAmountChanged struct { Account string `protobuf:"bytes,1,opt,name=account,proto3" json:"account,omitempty"` Denom string `protobuf:"bytes,2,opt,name=denom,proto3" json:"denom,omitempty"` @@ -201,7 +254,7 @@ func (m *EventWhitelistedAmountChanged) Reset() { *m = EventWhitelistedA func (m *EventWhitelistedAmountChanged) String() string { return proto.CompactTextString(m) } func (*EventWhitelistedAmountChanged) ProtoMessage() {} func (*EventWhitelistedAmountChanged) Descriptor() ([]byte, []int) { - return fileDescriptor_bdf87682d70b967f, []int{2} + return fileDescriptor_bdf87682d70b967f, []int{3} } func (m *EventWhitelistedAmountChanged) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -247,48 +300,51 @@ func (m *EventWhitelistedAmountChanged) GetDenom() string { func init() { proto.RegisterType((*EventIssued)(nil), "coreum.asset.ft.v1.EventIssued") proto.RegisterType((*EventFrozenAmountChanged)(nil), "coreum.asset.ft.v1.EventFrozenAmountChanged") + proto.RegisterType((*EventAmountClawedBack)(nil), "coreum.asset.ft.v1.EventAmountClawedBack") proto.RegisterType((*EventWhitelistedAmountChanged)(nil), "coreum.asset.ft.v1.EventWhitelistedAmountChanged") } func init() { proto.RegisterFile("coreum/asset/ft/v1/event.proto", fileDescriptor_bdf87682d70b967f) } var fileDescriptor_bdf87682d70b967f = []byte{ - // 552 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe4, 0x94, 0xcf, 0x8e, 0x12, 0x4f, - 0x10, 0xc7, 0x99, 0x65, 0x77, 0x81, 0xe6, 0xb7, 0xfc, 0x92, 0x0e, 0x31, 0xed, 0xaa, 0x03, 0xe1, - 0xb0, 0xe1, 0xe2, 0x4c, 0x56, 0x8d, 0x9e, 0x5d, 0x94, 0x48, 0xbc, 0x98, 0x49, 0xc8, 0x26, 0x5e, - 0x70, 0x98, 0x29, 0x98, 0xce, 0x32, 0xdd, 0xa4, 0xff, 0x10, 0xd7, 0xa7, 0xf0, 0x4d, 0x7c, 0x8d, - 0x3d, 0xee, 0xd1, 0x78, 0x20, 0x06, 0xde, 0xc2, 0x8b, 0xa6, 0xbb, 0x87, 0x85, 0x44, 0x2f, 0x72, - 0xf5, 0x34, 0x5d, 0xdf, 0xea, 0xfe, 0x54, 0x4f, 0x55, 0x75, 0x21, 0x3f, 0xe1, 0x02, 0x74, 0x1e, - 0xc6, 0x52, 0x82, 0x0a, 0x27, 0x2a, 0x5c, 0x9c, 0x87, 0xb0, 0x00, 0xa6, 0x82, 0xb9, 0xe0, 0x8a, - 0x63, 0xec, 0xfc, 0x81, 0xf5, 0x07, 0x13, 0x15, 0x2c, 0xce, 0x4f, 0xff, 0x74, 0x46, 0xf1, 0x2b, - 0x60, 0xee, 0xcc, 0x69, 0x73, 0xca, 0xa7, 0xdc, 0x2e, 0x43, 0xb3, 0x72, 0x6a, 0xe7, 0xcb, 0x21, - 0xaa, 0xbf, 0x36, 0xe4, 0x81, 0x94, 0x1a, 0x52, 0xdc, 0x44, 0x47, 0x29, 0x30, 0x9e, 0x13, 0xaf, - 0xed, 0x75, 0x6b, 0x91, 0x33, 0xf0, 0x3d, 0x74, 0x4c, 0x8d, 0x5f, 0x90, 0x03, 0x2b, 0x17, 0x96, - 0xd1, 0xe5, 0x75, 0x3e, 0xe6, 0x33, 0x52, 0x76, 0xba, 0xb3, 0x30, 0x41, 0x15, 0xa9, 0xc7, 0x9a, - 0x51, 0x45, 0x0e, 0xad, 0x63, 0x63, 0xe2, 0x87, 0xa8, 0x36, 0x17, 0x90, 0x50, 0x49, 0x39, 0x23, - 0x47, 0x6d, 0xaf, 0x7b, 0x12, 0x6d, 0x05, 0x3c, 0x44, 0x0d, 0xca, 0xa8, 0xa2, 0xf1, 0x6c, 0x14, - 0xe7, 0x5c, 0x33, 0x45, 0x8e, 0xcd, 0xf1, 0x8b, 0xe0, 0x66, 0xd9, 0x2a, 0x7d, 0x5b, 0xb6, 0xce, - 0xa6, 0x54, 0x65, 0x7a, 0x1c, 0x24, 0x3c, 0x0f, 0x13, 0x2e, 0x73, 0x2e, 0x8b, 0xcf, 0x63, 0x99, - 0x5e, 0x85, 0xea, 0x7a, 0x0e, 0x32, 0x18, 0x30, 0x15, 0x9d, 0x14, 0x94, 0x97, 0x16, 0x82, 0xdb, - 0xa8, 0x9e, 0x82, 0x4c, 0x04, 0x9d, 0x2b, 0x13, 0xb6, 0x62, 0xaf, 0xb4, 0x2b, 0xe1, 0x17, 0xa8, - 0x3a, 0x81, 0x58, 0x69, 0x01, 0x92, 0x54, 0xdb, 0xe5, 0x6e, 0xe3, 0xc9, 0x83, 0xe0, 0xf7, 0x1c, - 0x07, 0x7d, 0xb7, 0x27, 0xba, 0xdb, 0x8c, 0xdf, 0xa2, 0xda, 0x58, 0x0b, 0x36, 0x12, 0xb1, 0x02, - 0x52, 0xfb, 0xeb, 0xcb, 0xbe, 0x82, 0x24, 0xaa, 0x1a, 0x40, 0x14, 0x2b, 0xc0, 0x1f, 0x50, 0x53, - 0x02, 0x4b, 0x47, 0x09, 0xcf, 0x73, 0x2a, 0x4d, 0x46, 0x1c, 0x17, 0xed, 0xc5, 0xc5, 0x86, 0xd5, - 0xbb, 0x43, 0xd9, 0x08, 0xf7, 0x51, 0x59, 0x0b, 0x4a, 0xea, 0x16, 0x58, 0x59, 0x2d, 0x5b, 0xe5, - 0x61, 0x34, 0x88, 0x8c, 0x86, 0xcf, 0x50, 0x55, 0x0b, 0x3a, 0xca, 0x62, 0x99, 0x91, 0xff, 0xac, - 0xbf, 0xbe, 0x5a, 0xb6, 0x2a, 0xc3, 0x68, 0xf0, 0x26, 0x96, 0x59, 0x54, 0xd1, 0x82, 0x9a, 0x45, - 0xe7, 0x87, 0x87, 0x88, 0xed, 0x98, 0xbe, 0xe0, 0x9f, 0x80, 0xb9, 0x14, 0xf7, 0xb2, 0x98, 0x4d, - 0x21, 0x35, 0x85, 0x8f, 0x93, 0xc4, 0x56, 0xce, 0x35, 0xd0, 0xc6, 0xdc, 0x36, 0xd6, 0xc1, 0x6e, - 0x63, 0x5d, 0xa2, 0xff, 0xe7, 0x02, 0x16, 0x94, 0x6b, 0xb9, 0xa9, 0x78, 0x79, 0xaf, 0x8a, 0x37, - 0x36, 0x98, 0xa2, 0xe4, 0x43, 0xd4, 0x48, 0xb4, 0x10, 0xc0, 0xd4, 0x86, 0x7b, 0xb8, 0x5f, 0x27, - 0x15, 0x14, 0x87, 0xed, 0xfc, 0xf4, 0xd0, 0x23, 0xfb, 0xf3, 0x97, 0x19, 0x55, 0x30, 0xa3, 0x52, - 0x41, 0xfa, 0x4f, 0x65, 0xe0, 0xe2, 0xdd, 0xcd, 0xca, 0xf7, 0x6e, 0x57, 0xbe, 0xf7, 0x7d, 0xe5, - 0x7b, 0x9f, 0xd7, 0x7e, 0xe9, 0x76, 0xed, 0x97, 0xbe, 0xae, 0xfd, 0xd2, 0xfb, 0xe7, 0x3b, 0xc0, - 0x9e, 0x7d, 0x3b, 0x7d, 0xae, 0x59, 0x1a, 0x9b, 0x07, 0x16, 0x16, 0xc3, 0x69, 0xf1, 0x2c, 0xfc, - 0xb8, 0x9d, 0x50, 0x36, 0xc8, 0xf8, 0xd8, 0x4e, 0xa2, 0xa7, 0xbf, 0x02, 0x00, 0x00, 0xff, 0xff, - 0x70, 0x1a, 0x55, 0x0a, 0xf5, 0x04, 0x00, 0x00, + // 577 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe4, 0x94, 0xcd, 0x6e, 0x13, 0x31, + 0x10, 0xc7, 0xb3, 0x4d, 0x9b, 0x0f, 0x87, 0x06, 0xc9, 0x2a, 0xc8, 0x14, 0xd8, 0x44, 0x39, 0x54, + 0xbd, 0xb0, 0xab, 0x02, 0x82, 0x33, 0x2d, 0x44, 0x44, 0x5c, 0xd0, 0x4a, 0x51, 0x25, 0x2e, 0xc1, + 0xd9, 0x9d, 0x66, 0xad, 0x66, 0xed, 0xc8, 0x1f, 0x81, 0xf2, 0x12, 0xf0, 0x26, 0xbc, 0x46, 0x8f, + 0x3d, 0x22, 0x0e, 0x11, 0x4a, 0xde, 0x82, 0x0b, 0xc8, 0xde, 0x4d, 0x13, 0x09, 0x2e, 0x0d, 0x47, + 0x4e, 0xeb, 0xf9, 0xcf, 0xce, 0xcf, 0x1e, 0xcf, 0x78, 0x90, 0x1f, 0x0b, 0x09, 0x26, 0x0b, 0xa9, + 0x52, 0xa0, 0xc3, 0x33, 0x1d, 0x4e, 0x8f, 0x42, 0x98, 0x02, 0xd7, 0xc1, 0x44, 0x0a, 0x2d, 0x30, + 0xce, 0xfd, 0x81, 0xf3, 0x07, 0x67, 0x3a, 0x98, 0x1e, 0xed, 0xff, 0x2d, 0x46, 0x8b, 0x73, 0xe0, + 0x79, 0xcc, 0xfe, 0xde, 0x48, 0x8c, 0x84, 0x5b, 0x86, 0x76, 0x95, 0xab, 0x9d, 0xaf, 0xdb, 0xa8, + 0xf1, 0xca, 0x92, 0x7b, 0x4a, 0x19, 0x48, 0xf0, 0x1e, 0xda, 0x49, 0x80, 0x8b, 0x8c, 0x78, 0x6d, + 0xef, 0xb0, 0x1e, 0xe5, 0x06, 0xbe, 0x8b, 0x2a, 0xcc, 0xfa, 0x25, 0xd9, 0x72, 0x72, 0x61, 0x59, + 0x5d, 0x5d, 0x64, 0x43, 0x31, 0x26, 0xe5, 0x5c, 0xcf, 0x2d, 0x4c, 0x50, 0x55, 0x99, 0xa1, 0xe1, + 0x4c, 0x93, 0x6d, 0xe7, 0x58, 0x9a, 0xf8, 0x01, 0xaa, 0x4f, 0x24, 0xc4, 0x4c, 0x31, 0xc1, 0xc9, + 0x4e, 0xdb, 0x3b, 0xdc, 0x8d, 0x56, 0x02, 0xee, 0xa3, 0x26, 0xe3, 0x4c, 0x33, 0x3a, 0x1e, 0xd0, + 0x4c, 0x18, 0xae, 0x49, 0xc5, 0x86, 0x1f, 0x07, 0x97, 0xb3, 0x56, 0xe9, 0xfb, 0xac, 0x75, 0x30, + 0x62, 0x3a, 0x35, 0xc3, 0x20, 0x16, 0x59, 0x18, 0x0b, 0x95, 0x09, 0x55, 0x7c, 0x1e, 0xa9, 0xe4, + 0x3c, 0xd4, 0x17, 0x13, 0x50, 0x41, 0x8f, 0xeb, 0x68, 0xb7, 0xa0, 0xbc, 0x70, 0x10, 0xdc, 0x46, + 0x8d, 0x04, 0x54, 0x2c, 0xd9, 0x44, 0xdb, 0x6d, 0xab, 0xee, 0x48, 0xeb, 0x12, 0x7e, 0x8e, 0x6a, + 0x67, 0x40, 0xb5, 0x91, 0xa0, 0x48, 0xad, 0x5d, 0x3e, 0x6c, 0x3e, 0xbe, 0x1f, 0xfc, 0x79, 0xc7, + 0x41, 0x37, 0xff, 0x27, 0xba, 0xfe, 0x19, 0xbf, 0x41, 0xf5, 0xa1, 0x91, 0x7c, 0x20, 0xa9, 0x06, + 0x52, 0xbf, 0xf1, 0x61, 0x5f, 0x42, 0x1c, 0xd5, 0x2c, 0x20, 0xa2, 0x1a, 0xf0, 0x7b, 0xb4, 0xa7, + 0x80, 0x27, 0x83, 0x58, 0x64, 0x19, 0x53, 0xf6, 0x46, 0x72, 0x2e, 0xda, 0x88, 0x8b, 0x2d, 0xeb, + 0xe4, 0x1a, 0xe5, 0x76, 0xb8, 0x87, 0xca, 0x46, 0x32, 0xd2, 0x70, 0xc0, 0xea, 0x7c, 0xd6, 0x2a, + 0xf7, 0xa3, 0x5e, 0x64, 0x35, 0x7c, 0x80, 0x6a, 0x46, 0xb2, 0x41, 0x4a, 0x55, 0x4a, 0x6e, 0x39, + 0x7f, 0x63, 0x3e, 0x6b, 0x55, 0xfb, 0x51, 0xef, 0x35, 0x55, 0x69, 0x54, 0x35, 0x92, 0xd9, 0x45, + 0xe7, 0xa7, 0x87, 0x88, 0xeb, 0x98, 0xae, 0x14, 0x9f, 0x80, 0xe7, 0x57, 0x7c, 0x92, 0x52, 0x3e, + 0x82, 0xc4, 0x16, 0x9e, 0xc6, 0xb1, 0xab, 0x5c, 0xde, 0x40, 0x4b, 0x73, 0xd5, 0x58, 0x5b, 0xeb, + 0x8d, 0x75, 0x8a, 0x6e, 0x4f, 0x24, 0x4c, 0x99, 0x30, 0x6a, 0x59, 0xf1, 0xf2, 0x46, 0x15, 0x6f, + 0x2e, 0x31, 0x45, 0xc9, 0xfb, 0xa8, 0x19, 0x1b, 0x29, 0x81, 0xeb, 0x25, 0x77, 0x7b, 0xb3, 0x4e, + 0x2a, 0x28, 0x39, 0xb6, 0xf3, 0xd9, 0x43, 0x77, 0x5c, 0xf2, 0x45, 0xda, 0x63, 0xfa, 0x01, 0x92, + 0x63, 0x1a, 0x9f, 0xdf, 0x38, 0xf3, 0x2e, 0xaa, 0xfc, 0x53, 0xc2, 0x45, 0x74, 0xe7, 0x97, 0x87, + 0x1e, 0xba, 0x13, 0x9d, 0xa6, 0x4c, 0xc3, 0x98, 0x29, 0x0d, 0xc9, 0x7f, 0x55, 0x93, 0xe3, 0xb7, + 0x97, 0x73, 0xdf, 0xbb, 0x9a, 0xfb, 0xde, 0x8f, 0xb9, 0xef, 0x7d, 0x59, 0xf8, 0xa5, 0xab, 0x85, + 0x5f, 0xfa, 0xb6, 0xf0, 0x4b, 0xef, 0x9e, 0xad, 0x01, 0x4f, 0xdc, 0x6b, 0xee, 0x0a, 0xc3, 0x13, + 0x6a, 0x9f, 0x7c, 0x58, 0x8c, 0xcb, 0xe9, 0xd3, 0xf0, 0xe3, 0x6a, 0x66, 0xba, 0x4d, 0x86, 0x15, + 0x37, 0x1b, 0x9f, 0xfc, 0x0e, 0x00, 0x00, 0xff, 0xff, 0x41, 0x79, 0xe1, 0x94, 0x87, 0x05, 0x00, + 0x00, } func (m *EventIssued) Marshal() (dAtA []byte, err error) { @@ -473,6 +529,53 @@ func (m *EventFrozenAmountChanged) MarshalToSizedBuffer(dAtA []byte) (int, error return len(dAtA) - i, nil } +func (m *EventAmountClawedBack) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *EventAmountClawedBack) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *EventAmountClawedBack) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size := m.Amount.Size() + i -= size + if _, err := m.Amount.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintEvent(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + if len(m.Denom) > 0 { + i -= len(m.Denom) + copy(dAtA[i:], m.Denom) + i = encodeVarintEvent(dAtA, i, uint64(len(m.Denom))) + i-- + dAtA[i] = 0x12 + } + if len(m.Account) > 0 { + i -= len(m.Account) + copy(dAtA[i:], m.Account) + i = encodeVarintEvent(dAtA, i, uint64(len(m.Account))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + func (m *EventWhitelistedAmountChanged) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -615,6 +718,25 @@ func (m *EventFrozenAmountChanged) Size() (n int) { return n } +func (m *EventAmountClawedBack) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Account) + if l > 0 { + n += 1 + l + sovEvent(uint64(l)) + } + l = len(m.Denom) + if l > 0 { + n += 1 + l + sovEvent(uint64(l)) + } + l = m.Amount.Size() + n += 1 + l + sovEvent(uint64(l)) + return n +} + func (m *EventWhitelistedAmountChanged) Size() (n int) { if m == nil { return 0 @@ -1288,6 +1410,154 @@ func (m *EventFrozenAmountChanged) Unmarshal(dAtA []byte) error { } return nil } +func (m *EventAmountClawedBack) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvent + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: EventAmountClawedBack: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: EventAmountClawedBack: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Account", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvent + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvent + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvent + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Account = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Denom", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvent + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvent + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvent + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Denom = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Amount", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvent + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvent + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvent + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Amount.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipEvent(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthEvent + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *EventWhitelistedAmountChanged) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 diff --git a/x/asset/ft/types/msgs.go b/x/asset/ft/types/msgs.go index de245a496..253fb1156 100644 --- a/x/asset/ft/types/msgs.go +++ b/x/asset/ft/types/msgs.go @@ -21,6 +21,7 @@ const ( TypeMsgUnfreeze = "unfreeze" TypeMsgGloballyFreeze = "globally-freeze" TypeMsgGloballyUnfreeze = "globally-unfreeze" + TypeMsgClawback = "clawback" TypeMsgSetWhitelistedLimit = "set-whitelisted-limit" TypeMsgUpgradeTokenV1 = "upgrade-token-v1" TypeMsgUpdateParams = "update-params" @@ -52,6 +53,8 @@ var ( _ legacytx.LegacyMsg = &MsgGloballyFreeze{} _ sdk.Msg = &MsgGloballyUnfreeze{} _ legacytx.LegacyMsg = &MsgGloballyUnfreeze{} + _ sdk.Msg = &MsgClawback{} + _ legacytx.LegacyMsg = &MsgClawback{} _ sdk.Msg = &MsgSetWhitelistedLimit{} _ legacytx.LegacyMsg = &MsgSetWhitelistedLimit{} _ sdk.Msg = &MsgUpgradeTokenV1{} @@ -430,6 +433,49 @@ func (m MsgGloballyUnfreeze) Type() string { return TypeMsgGloballyUnfreeze } +func (m MsgClawback) ValidateBasic() error { + if _, err := sdk.AccAddressFromBech32(m.Sender); err != nil { + return sdkerrors.Wrap(cosmoserrors.ErrInvalidAddress, "invalid sender address") + } + + if _, err := sdk.AccAddressFromBech32(m.Account); err != nil { + return sdkerrors.Wrap(cosmoserrors.ErrInvalidAddress, "invalid account address") + } + + _, issuer, err := DeconstructDenom(m.Coin.Denom) + if err != nil { + return err + } + + if issuer.String() == m.Account { + return sdkerrors.Wrap(cosmoserrors.ErrUnauthorized, "issuer's balance can't be clawed back") + } + + if issuer.String() != m.Sender { + return sdkerrors.Wrap(cosmoserrors.ErrUnauthorized, "only issuer can claw back balance") + } + + return m.Coin.Validate() +} + +func (m MsgClawback) GetSigners() []sdk.AccAddress { + return []sdk.AccAddress{ + sdk.MustAccAddressFromBech32(m.Sender), + } +} + +func (m MsgClawback) GetSignBytes() []byte { + return sdk.MustSortJSON(moduleAminoCdc.MustMarshalJSON(&m)) +} + +func (m MsgClawback) Route() string { + return RouterKey +} + +func (m MsgClawback) Type() string { + return TypeMsgClawback +} + // ValidateBasic checks that message fields are valid. func (m MsgSetWhitelistedLimit) ValidateBasic() error { if _, err := sdk.AccAddressFromBech32(m.Sender); err != nil { diff --git a/x/asset/ft/types/token.pb.go b/x/asset/ft/types/token.pb.go index b50c2a2ea..0ed2c114f 100644 --- a/x/asset/ft/types/token.pb.go +++ b/x/asset/ft/types/token.pb.go @@ -38,6 +38,7 @@ const ( Feature_whitelisting Feature = 3 Feature_ibc Feature = 4 Feature_block_smart_contracts Feature = 5 + Feature_clawback Feature = 6 ) var Feature_name = map[int32]string{ @@ -47,6 +48,7 @@ var Feature_name = map[int32]string{ 3: "whitelisting", 4: "ibc", 5: "block_smart_contracts", + 6: "clawback", } var Feature_value = map[string]int32{ @@ -56,6 +58,7 @@ var Feature_value = map[string]int32{ "whitelisting": 3, "ibc": 4, "block_smart_contracts": 5, + "clawback": 6, } func (x Feature) String() string { @@ -332,53 +335,54 @@ func init() { func init() { proto.RegisterFile("coreum/asset/ft/v1/token.proto", fileDescriptor_fe80c7a2c55589e7) } var fileDescriptor_fe80c7a2c55589e7 = []byte{ - // 735 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x54, 0x4f, 0x4f, 0xe3, 0x46, - 0x14, 0x8f, 0x63, 0x12, 0x3b, 0x2f, 0x40, 0xa3, 0x51, 0x40, 0x86, 0x56, 0x71, 0x94, 0x03, 0x8d, - 0x2a, 0x61, 0x2b, 0xb4, 0x6a, 0xab, 0x5e, 0x2a, 0x01, 0x45, 0x45, 0xbd, 0x20, 0x17, 0x7a, 0xe8, - 0x25, 0xf5, 0x9f, 0x89, 0x33, 0xc2, 0xf6, 0x44, 0x33, 0xe3, 0xb4, 0xe1, 0x13, 0xb4, 0x37, 0xbe, - 0x41, 0xf9, 0x22, 0x7b, 0xe7, 0xc8, 0x71, 0xb5, 0x87, 0xec, 0x2a, 0x5c, 0xf6, 0x63, 0xac, 0x66, - 0x9c, 0xf0, 0x47, 0x1b, 0x69, 0x17, 0x24, 0x4e, 0xf1, 0xef, 0xf7, 0xde, 0xfc, 0xe6, 0xbd, 0x37, - 0xbf, 0x3c, 0x68, 0x85, 0x94, 0xe1, 0x3c, 0x75, 0x7d, 0xce, 0xb1, 0x70, 0x07, 0xc2, 0x1d, 0xf7, - 0x5c, 0x41, 0xcf, 0x71, 0xe6, 0x8c, 0x18, 0x15, 0x14, 0xa1, 0x22, 0xee, 0xa8, 0xb8, 0x33, 0x10, - 0xce, 0xb8, 0xb7, 0xdd, 0x8c, 0x69, 0x4c, 0x55, 0xd8, 0x95, 0x5f, 0x45, 0xe6, 0xb6, 0x1d, 0x53, - 0x1a, 0x27, 0xd8, 0x55, 0x28, 0xc8, 0x07, 0xae, 0x20, 0x29, 0xe6, 0xc2, 0x4f, 0x47, 0x45, 0x42, - 0xe7, 0x3f, 0x1d, 0xe0, 0x10, 0x0f, 0x48, 0x46, 0x04, 0xa1, 0x19, 0x6a, 0x42, 0x25, 0xc2, 0x19, - 0x4d, 0x2d, 0xad, 0xad, 0x75, 0x6b, 0x5e, 0x01, 0xd0, 0x26, 0x54, 0x09, 0xe7, 0x39, 0x66, 0x56, - 0x59, 0xd1, 0x73, 0x84, 0x7e, 0x00, 0x73, 0x80, 0x7d, 0x91, 0x33, 0xcc, 0x2d, 0xbd, 0xad, 0x77, - 0xd7, 0xf7, 0xbe, 0x74, 0x3e, 0x2e, 0xcd, 0x39, 0x2a, 0x72, 0xbc, 0xbb, 0x64, 0xf4, 0x1b, 0xd4, - 0x82, 0x9c, 0x65, 0x7d, 0xe6, 0x0b, 0x6c, 0xad, 0x48, 0xcd, 0x7d, 0xe7, 0x7a, 0x6a, 0x97, 0xde, - 0x4c, 0xed, 0x9d, 0x98, 0x88, 0x61, 0x1e, 0x38, 0x21, 0x4d, 0xdd, 0x90, 0xf2, 0x94, 0xf2, 0xf9, - 0xcf, 0x2e, 0x8f, 0xce, 0x5d, 0x31, 0x19, 0x61, 0xee, 0x1c, 0xe2, 0xd0, 0x33, 0xa5, 0x80, 0xe7, - 0x0b, 0x8c, 0xfe, 0x82, 0x26, 0xc7, 0x59, 0xd4, 0x0f, 0x69, 0x9a, 0x12, 0xce, 0x09, 0x9d, 0xeb, - 0x56, 0x9e, 0xa5, 0x8b, 0xa4, 0xd6, 0xc1, 0x9d, 0x94, 0xba, 0xc1, 0x02, 0x63, 0x8c, 0x99, 0x84, - 0x56, 0xb5, 0xad, 0x75, 0xd7, 0xbc, 0x05, 0x44, 0x5b, 0xa0, 0xe7, 0x8c, 0x58, 0x86, 0xba, 0xca, - 0x98, 0x4d, 0x6d, 0xfd, 0xcc, 0x3b, 0xf6, 0x24, 0x87, 0x76, 0xc0, 0xcc, 0x19, 0xe9, 0x0f, 0x7d, - 0x3e, 0xb4, 0x4c, 0x15, 0xaf, 0xcf, 0xa6, 0xb6, 0x71, 0xe6, 0x1d, 0xff, 0xea, 0xf3, 0xa1, 0x67, - 0xe4, 0x8c, 0xc8, 0x8f, 0x9f, 0xcc, 0x7f, 0xaf, 0xec, 0xd2, 0xfb, 0x2b, 0xbb, 0xd4, 0xf9, 0x7f, - 0x05, 0x2a, 0xa7, 0xf2, 0x99, 0x9f, 0xf8, 0x0c, 0x9b, 0x50, 0xe5, 0x93, 0x34, 0xa0, 0x89, 0xa5, - 0x17, 0x7c, 0x81, 0x64, 0xd9, 0x3c, 0x0f, 0xf2, 0x8c, 0x88, 0x62, 0xc6, 0xde, 0x02, 0xa2, 0xaf, - 0xa0, 0x36, 0x62, 0x38, 0x24, 0xaa, 0xa5, 0x8a, 0x6a, 0xe9, 0x9e, 0x40, 0x6d, 0xa8, 0x47, 0x98, - 0x87, 0x8c, 0x8c, 0xc4, 0xa2, 0xe5, 0x9a, 0xf7, 0x90, 0x42, 0x5f, 0xc3, 0x17, 0x71, 0x42, 0x03, - 0x3f, 0x49, 0x26, 0xfd, 0x01, 0xa3, 0x17, 0x38, 0x53, 0x23, 0x30, 0xbd, 0xf5, 0x05, 0x7d, 0xa4, - 0xd8, 0x47, 0x0e, 0x31, 0x9f, 0xed, 0x90, 0xda, 0x0b, 0x39, 0x04, 0x5e, 0xc2, 0x21, 0xf5, 0xa5, - 0x0e, 0x59, 0xfd, 0x84, 0x43, 0xd6, 0x3e, 0xcb, 0x21, 0xbb, 0xb0, 0x71, 0x88, 0x13, 0x7f, 0x82, - 0x23, 0xe5, 0x93, 0xb3, 0x51, 0xcc, 0xfc, 0x08, 0xff, 0xd1, 0x5b, 0x6e, 0x98, 0xce, 0x2b, 0x0d, - 0x9a, 0x8f, 0x13, 0x7f, 0x17, 0xbe, 0xc8, 0x39, 0xb2, 0xa1, 0x4e, 0x82, 0xb0, 0x8f, 0x33, 0x3f, - 0x48, 0x70, 0xa4, 0x0e, 0x99, 0x1e, 0x90, 0x20, 0xfc, 0xa5, 0x60, 0xd0, 0x01, 0x00, 0x17, 0x3e, - 0x13, 0x7d, 0xb9, 0x2f, 0x94, 0xdd, 0xea, 0x7b, 0xdb, 0x4e, 0xb1, 0x4c, 0x9c, 0xc5, 0x32, 0x71, - 0x4e, 0x17, 0xcb, 0x64, 0xdf, 0x94, 0x33, 0xbc, 0x7c, 0x6b, 0x6b, 0x5e, 0x4d, 0x9d, 0x93, 0x11, - 0xf4, 0x33, 0x98, 0x72, 0xea, 0x4a, 0x42, 0x7f, 0x82, 0x84, 0x81, 0xb3, 0x48, 0xf2, 0x9d, 0x93, - 0xc7, 0xe5, 0x17, 0xc5, 0x63, 0x8e, 0x7e, 0x84, 0xf2, 0xb8, 0xa7, 0xaa, 0xae, 0xef, 0x75, 0x97, - 0xf9, 0x69, 0x59, 0xd3, 0x5e, 0x79, 0xdc, 0xfb, 0x26, 0x06, 0x63, 0xee, 0x35, 0x54, 0x07, 0x23, - 0x25, 0x99, 0x20, 0x59, 0xdc, 0x28, 0x49, 0x20, 0xdd, 0x22, 0x81, 0x86, 0x56, 0xc1, 0x1c, 0x30, - 0x8c, 0x2f, 0x24, 0x2a, 0xa3, 0x06, 0xac, 0xfe, 0x3d, 0x24, 0x02, 0x27, 0x84, 0xab, 0x64, 0x1d, - 0x19, 0xa0, 0x93, 0x20, 0x6c, 0xac, 0xa0, 0x2d, 0xd8, 0x08, 0x12, 0x1a, 0x9e, 0xf7, 0x79, 0x2a, - 0x67, 0x15, 0xd2, 0x4c, 0x30, 0x3f, 0x14, 0xbc, 0x51, 0xd9, 0x3f, 0xb9, 0x9e, 0xb5, 0xb4, 0x9b, - 0x59, 0x4b, 0x7b, 0x37, 0x6b, 0x69, 0x97, 0xb7, 0xad, 0xd2, 0xcd, 0x6d, 0xab, 0xf4, 0xfa, 0xb6, - 0x55, 0xfa, 0xf3, 0xfb, 0x07, 0x36, 0x3b, 0x50, 0xa5, 0x1f, 0xd1, 0x3c, 0x8b, 0x7c, 0xf9, 0xc7, - 0x72, 0xe7, 0x8b, 0x7f, 0xfc, 0x9d, 0xfb, 0xcf, 0xfd, 0xf6, 0x57, 0xd6, 0x0b, 0xaa, 0x6a, 0x66, - 0xdf, 0x7e, 0x08, 0x00, 0x00, 0xff, 0xff, 0x76, 0x0e, 0xd5, 0x4e, 0x1d, 0x06, 0x00, 0x00, + // 746 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x55, 0x4f, 0x4f, 0xdb, 0x48, + 0x14, 0x8f, 0x63, 0x12, 0x3b, 0x13, 0x60, 0xa3, 0x51, 0x40, 0x86, 0x5d, 0xc5, 0x51, 0x0e, 0x6c, + 0xb4, 0x12, 0xb6, 0xc2, 0xae, 0x76, 0x57, 0x7b, 0x59, 0x09, 0x58, 0xb4, 0xa8, 0x17, 0xe4, 0x42, + 0x0f, 0xbd, 0xa4, 0x63, 0x7b, 0xe2, 0x8c, 0x62, 0x7b, 0xa2, 0x99, 0x71, 0x68, 0xf8, 0x04, 0xed, + 0x8d, 0x6f, 0x50, 0xbe, 0x48, 0xef, 0x1c, 0x39, 0x56, 0x3d, 0xa4, 0x55, 0xb8, 0xf4, 0x63, 0x54, + 0x33, 0x4e, 0xf8, 0xa3, 0x46, 0x6a, 0x41, 0xe2, 0x14, 0xff, 0x7e, 0xef, 0xf9, 0xe7, 0xf7, 0xde, + 0xfc, 0xf2, 0x06, 0x34, 0x02, 0xca, 0x70, 0x96, 0xb8, 0x88, 0x73, 0x2c, 0xdc, 0x9e, 0x70, 0x47, + 0x1d, 0x57, 0xd0, 0x01, 0x4e, 0x9d, 0x21, 0xa3, 0x82, 0x42, 0x98, 0xc7, 0x1d, 0x15, 0x77, 0x7a, + 0xc2, 0x19, 0x75, 0x36, 0xeb, 0x11, 0x8d, 0xa8, 0x0a, 0xbb, 0xf2, 0x29, 0xcf, 0xdc, 0xb4, 0x23, + 0x4a, 0xa3, 0x18, 0xbb, 0x0a, 0xf9, 0x59, 0xcf, 0x15, 0x24, 0xc1, 0x5c, 0xa0, 0x64, 0x98, 0x27, + 0xb4, 0xde, 0xea, 0x00, 0xec, 0xe3, 0x1e, 0x49, 0x89, 0x20, 0x34, 0x85, 0x75, 0x50, 0x0a, 0x71, + 0x4a, 0x13, 0x4b, 0x6b, 0x6a, 0xed, 0x8a, 0x97, 0x03, 0xb8, 0x0e, 0xca, 0x84, 0xf3, 0x0c, 0x33, + 0xab, 0xa8, 0xe8, 0x19, 0x82, 0x7f, 0x01, 0xb3, 0x87, 0x91, 0xc8, 0x18, 0xe6, 0x96, 0xde, 0xd4, + 0xdb, 0xab, 0x3b, 0x3f, 0x3b, 0xdf, 0x96, 0xe6, 0x1c, 0xe4, 0x39, 0xde, 0x4d, 0x32, 0x7c, 0x06, + 0x2a, 0x7e, 0xc6, 0xd2, 0x2e, 0x43, 0x02, 0x5b, 0x4b, 0x52, 0x73, 0xd7, 0xb9, 0x9c, 0xd8, 0x85, + 0x8f, 0x13, 0x7b, 0x2b, 0x22, 0xa2, 0x9f, 0xf9, 0x4e, 0x40, 0x13, 0x37, 0xa0, 0x3c, 0xa1, 0x7c, + 0xf6, 0xb3, 0xcd, 0xc3, 0x81, 0x2b, 0xc6, 0x43, 0xcc, 0x9d, 0x7d, 0x1c, 0x78, 0xa6, 0x14, 0xf0, + 0x90, 0xc0, 0xf0, 0x15, 0xa8, 0x73, 0x9c, 0x86, 0xdd, 0x80, 0x26, 0x09, 0xe1, 0x9c, 0xd0, 0x99, + 0x6e, 0xe9, 0x51, 0xba, 0x50, 0x6a, 0xed, 0xdd, 0x48, 0xa9, 0x2f, 0x58, 0xc0, 0x18, 0x61, 0x26, + 0xa1, 0x55, 0x6e, 0x6a, 0xed, 0x15, 0x6f, 0x0e, 0xe1, 0x06, 0xd0, 0x33, 0x46, 0x2c, 0x43, 0x7d, + 0xca, 0x98, 0x4e, 0x6c, 0xfd, 0xc4, 0x3b, 0xf4, 0x24, 0x07, 0xb7, 0x80, 0x99, 0x31, 0xd2, 0xed, + 0x23, 0xde, 0xb7, 0x4c, 0x15, 0xaf, 0x4e, 0x27, 0xb6, 0x71, 0xe2, 0x1d, 0xfe, 0x8f, 0x78, 0xdf, + 0x33, 0x32, 0x46, 0xe4, 0xc3, 0x3f, 0xe6, 0x9b, 0x0b, 0xbb, 0xf0, 0xe5, 0xc2, 0x2e, 0xb4, 0xde, + 0x2d, 0x81, 0xd2, 0xb1, 0x3c, 0xe6, 0x07, 0x1e, 0xc3, 0x3a, 0x28, 0xf3, 0x71, 0xe2, 0xd3, 0xd8, + 0xd2, 0x73, 0x3e, 0x47, 0xb2, 0x6c, 0x9e, 0xf9, 0x59, 0x4a, 0x44, 0x3e, 0x63, 0x6f, 0x0e, 0xe1, + 0x2f, 0xa0, 0x32, 0x64, 0x38, 0x20, 0xaa, 0xa5, 0x92, 0x6a, 0xe9, 0x96, 0x80, 0x4d, 0x50, 0x0d, + 0x31, 0x0f, 0x18, 0x19, 0x8a, 0x79, 0xcb, 0x15, 0xef, 0x2e, 0x05, 0x7f, 0x05, 0x3f, 0x45, 0x31, + 0xf5, 0x51, 0x1c, 0x8f, 0xbb, 0x3d, 0x46, 0xcf, 0x70, 0xaa, 0x46, 0x60, 0x7a, 0xab, 0x73, 0xfa, + 0x40, 0xb1, 0xf7, 0x1c, 0x62, 0x3e, 0xda, 0x21, 0x95, 0x27, 0x72, 0x08, 0x78, 0x0a, 0x87, 0x54, + 0x17, 0x3a, 0x64, 0xf9, 0x3b, 0x0e, 0x59, 0xf9, 0x21, 0x87, 0x6c, 0x83, 0xb5, 0x7d, 0x1c, 0xa3, + 0x31, 0x0e, 0x95, 0x4f, 0x4e, 0x86, 0x11, 0x43, 0x21, 0x7e, 0xd1, 0x59, 0x6c, 0x98, 0xd6, 0x7b, + 0x0d, 0xd4, 0xef, 0x27, 0x3e, 0x17, 0x48, 0x64, 0x1c, 0xda, 0xa0, 0x4a, 0xfc, 0xa0, 0x8b, 0x53, + 0xe4, 0xc7, 0x38, 0x54, 0x2f, 0x99, 0x1e, 0x20, 0x7e, 0xf0, 0x5f, 0xce, 0xc0, 0x3d, 0x00, 0xb8, + 0x40, 0x4c, 0x74, 0xe5, 0xbe, 0x50, 0x76, 0xab, 0xee, 0x6c, 0x3a, 0xf9, 0x32, 0x71, 0xe6, 0xcb, + 0xc4, 0x39, 0x9e, 0x2f, 0x93, 0x5d, 0x53, 0xce, 0xf0, 0xfc, 0x93, 0xad, 0x79, 0x15, 0xf5, 0x9e, + 0x8c, 0xc0, 0x7f, 0x81, 0x29, 0xa7, 0xae, 0x24, 0xf4, 0x07, 0x48, 0x18, 0x38, 0x0d, 0x25, 0xdf, + 0x3a, 0xba, 0x5f, 0x7e, 0x5e, 0x3c, 0xe6, 0xf0, 0x6f, 0x50, 0x1c, 0x75, 0x54, 0xd5, 0xd5, 0x9d, + 0xf6, 0x22, 0x3f, 0x2d, 0x6a, 0xda, 0x2b, 0x8e, 0x3a, 0xbf, 0x65, 0xc0, 0x98, 0x79, 0x0d, 0x56, + 0x81, 0x91, 0x90, 0x54, 0x90, 0x34, 0xaa, 0x15, 0x24, 0x90, 0x6e, 0x91, 0x40, 0x83, 0xcb, 0xc0, + 0xec, 0x31, 0x8c, 0xcf, 0x24, 0x2a, 0xc2, 0x1a, 0x58, 0x3e, 0xed, 0x13, 0x81, 0x63, 0xc2, 0x55, + 0xb2, 0x0e, 0x0d, 0xa0, 0x13, 0x3f, 0xa8, 0x2d, 0xc1, 0x0d, 0xb0, 0xe6, 0xc7, 0x34, 0x18, 0x74, + 0x79, 0x22, 0x67, 0x15, 0xd0, 0x54, 0x30, 0x14, 0x08, 0x5e, 0x2b, 0x49, 0x8d, 0x20, 0x46, 0xa7, + 0x3e, 0x0a, 0x06, 0xb5, 0xf2, 0xee, 0xd1, 0xe5, 0xb4, 0xa1, 0x5d, 0x4d, 0x1b, 0xda, 0xe7, 0x69, + 0x43, 0x3b, 0xbf, 0x6e, 0x14, 0xae, 0xae, 0x1b, 0x85, 0x0f, 0xd7, 0x8d, 0xc2, 0xcb, 0x3f, 0xef, + 0x98, 0x6e, 0x4f, 0x35, 0x72, 0x40, 0xb3, 0x34, 0x44, 0xf2, 0x6f, 0xe6, 0xce, 0xae, 0x81, 0xd1, + 0x1f, 0xee, 0xeb, 0xdb, 0xbb, 0x40, 0x19, 0xd1, 0x2f, 0xab, 0x09, 0xfe, 0xfe, 0x35, 0x00, 0x00, + 0xff, 0xff, 0x3e, 0xa0, 0x17, 0x2d, 0x2b, 0x06, 0x00, 0x00, } func (m *Definition) Marshal() (dAtA []byte, err error) { diff --git a/x/asset/ft/types/tx.pb.go b/x/asset/ft/types/tx.pb.go index d23fd82dc..2bb0b402b 100644 --- a/x/asset/ft/types/tx.pb.go +++ b/x/asset/ft/types/tx.pb.go @@ -355,6 +355,45 @@ func (m *MsgGloballyUnfreeze) XXX_DiscardUnknown() { var xxx_messageInfo_MsgGloballyUnfreeze proto.InternalMessageInfo +type MsgClawback struct { + Sender string `protobuf:"bytes,1,opt,name=sender,proto3" json:"sender,omitempty"` + Account string `protobuf:"bytes,2,opt,name=account,proto3" json:"account,omitempty"` + Coin types.Coin `protobuf:"bytes,3,opt,name=coin,proto3" json:"coin"` +} + +func (m *MsgClawback) Reset() { *m = MsgClawback{} } +func (m *MsgClawback) String() string { return proto.CompactTextString(m) } +func (*MsgClawback) ProtoMessage() {} +func (*MsgClawback) Descriptor() ([]byte, []int) { + return fileDescriptor_e54b0962ccfc4ca0, []int{8} +} +func (m *MsgClawback) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgClawback) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgClawback.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgClawback) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgClawback.Merge(m, src) +} +func (m *MsgClawback) XXX_Size() int { + return m.Size() +} +func (m *MsgClawback) XXX_DiscardUnknown() { + xxx_messageInfo_MsgClawback.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgClawback proto.InternalMessageInfo + type MsgSetWhitelistedLimit struct { Sender string `protobuf:"bytes,1,opt,name=sender,proto3" json:"sender,omitempty"` Account string `protobuf:"bytes,2,opt,name=account,proto3" json:"account,omitempty"` @@ -365,7 +404,7 @@ func (m *MsgSetWhitelistedLimit) Reset() { *m = MsgSetWhitelistedLimit{} func (m *MsgSetWhitelistedLimit) String() string { return proto.CompactTextString(m) } func (*MsgSetWhitelistedLimit) ProtoMessage() {} func (*MsgSetWhitelistedLimit) Descriptor() ([]byte, []int) { - return fileDescriptor_e54b0962ccfc4ca0, []int{8} + return fileDescriptor_e54b0962ccfc4ca0, []int{9} } func (m *MsgSetWhitelistedLimit) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -405,7 +444,7 @@ func (m *MsgUpgradeTokenV1) Reset() { *m = MsgUpgradeTokenV1{} } func (m *MsgUpgradeTokenV1) String() string { return proto.CompactTextString(m) } func (*MsgUpgradeTokenV1) ProtoMessage() {} func (*MsgUpgradeTokenV1) Descriptor() ([]byte, []int) { - return fileDescriptor_e54b0962ccfc4ca0, []int{9} + return fileDescriptor_e54b0962ccfc4ca0, []int{10} } func (m *MsgUpgradeTokenV1) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -443,7 +482,7 @@ func (m *MsgUpdateParams) Reset() { *m = MsgUpdateParams{} } func (m *MsgUpdateParams) String() string { return proto.CompactTextString(m) } func (*MsgUpdateParams) ProtoMessage() {} func (*MsgUpdateParams) Descriptor() ([]byte, []int) { - return fileDescriptor_e54b0962ccfc4ca0, []int{10} + return fileDescriptor_e54b0962ccfc4ca0, []int{11} } func (m *MsgUpdateParams) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -479,7 +518,7 @@ func (m *EmptyResponse) Reset() { *m = EmptyResponse{} } func (m *EmptyResponse) String() string { return proto.CompactTextString(m) } func (*EmptyResponse) ProtoMessage() {} func (*EmptyResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_e54b0962ccfc4ca0, []int{11} + return fileDescriptor_e54b0962ccfc4ca0, []int{12} } func (m *EmptyResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -517,6 +556,7 @@ func init() { proto.RegisterType((*MsgSetFrozen)(nil), "coreum.asset.ft.v1.MsgSetFrozen") proto.RegisterType((*MsgGloballyFreeze)(nil), "coreum.asset.ft.v1.MsgGloballyFreeze") proto.RegisterType((*MsgGloballyUnfreeze)(nil), "coreum.asset.ft.v1.MsgGloballyUnfreeze") + proto.RegisterType((*MsgClawback)(nil), "coreum.asset.ft.v1.MsgClawback") proto.RegisterType((*MsgSetWhitelistedLimit)(nil), "coreum.asset.ft.v1.MsgSetWhitelistedLimit") proto.RegisterType((*MsgUpgradeTokenV1)(nil), "coreum.asset.ft.v1.MsgUpgradeTokenV1") proto.RegisterType((*MsgUpdateParams)(nil), "coreum.asset.ft.v1.MsgUpdateParams") @@ -526,69 +566,70 @@ func init() { func init() { proto.RegisterFile("coreum/asset/ft/v1/tx.proto", fileDescriptor_e54b0962ccfc4ca0) } var fileDescriptor_e54b0962ccfc4ca0 = []byte{ - // 983 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x96, 0xcf, 0x6f, 0x1b, 0x45, - 0x14, 0xc7, 0x63, 0x9c, 0xc6, 0xf6, 0x73, 0x93, 0xd2, 0x6d, 0x54, 0x36, 0x49, 0xb1, 0x83, 0x11, - 0x25, 0x8a, 0x94, 0x5d, 0x39, 0x45, 0x45, 0x42, 0xe2, 0x10, 0x87, 0x9a, 0x06, 0x30, 0xaa, 0xb6, - 0x75, 0x90, 0x7a, 0xc0, 0xcc, 0xee, 0x4e, 0xd6, 0xa3, 0x7a, 0x67, 0xac, 0x99, 0xd9, 0xa8, 0xee, - 0x05, 0x89, 0x23, 0x27, 0x4e, 0xfc, 0x03, 0x5c, 0x38, 0xe6, 0x80, 0xc4, 0xbf, 0x90, 0x63, 0xc5, - 0x09, 0x71, 0x88, 0xc0, 0x39, 0xe4, 0xdf, 0x40, 0x33, 0xb3, 0x8e, 0x93, 0xd4, 0xab, 0x38, 0x3d, - 0xe4, 0x92, 0xec, 0xfb, 0xb1, 0x9f, 0xef, 0xcc, 0xf3, 0xdb, 0x37, 0x03, 0x2b, 0x01, 0xe3, 0x38, - 0x89, 0x5d, 0x24, 0x04, 0x96, 0xee, 0x9e, 0x74, 0xf7, 0xeb, 0xae, 0x7c, 0xe9, 0xf4, 0x39, 0x93, - 0xcc, 0xb2, 0x4c, 0xd0, 0xd1, 0x41, 0x67, 0x4f, 0x3a, 0xfb, 0xf5, 0xe5, 0xdb, 0x28, 0x26, 0x94, - 0xb9, 0xfa, 0xaf, 0x49, 0x5b, 0xae, 0x4e, 0x60, 0xf4, 0x11, 0x47, 0xb1, 0x48, 0x13, 0x2a, 0x93, - 0x44, 0xd8, 0x0b, 0x4c, 0xc7, 0x71, 0x11, 0x33, 0xe1, 0xfa, 0x48, 0x60, 0x77, 0xbf, 0xee, 0x63, - 0x89, 0xea, 0x6e, 0xc0, 0xc8, 0x28, 0xfe, 0x5e, 0x1a, 0x8f, 0x45, 0xa4, 0x5e, 0x8d, 0x45, 0x94, - 0x06, 0x96, 0x4c, 0xa0, 0xa3, 0x2d, 0xd7, 0x18, 0x69, 0x68, 0x31, 0x62, 0x11, 0x33, 0x7e, 0xf5, - 0x64, 0xbc, 0xb5, 0x5f, 0x67, 0xa1, 0xd8, 0x12, 0xd1, 0x8e, 0x10, 0x09, 0xb6, 0xee, 0xc2, 0x1c, - 0x51, 0x0f, 0xdc, 0xce, 0xad, 0xe6, 0xd6, 0x4a, 0x5e, 0x6a, 0x29, 0xbf, 0x18, 0xc4, 0x3e, 0xeb, - 0xd9, 0xef, 0x18, 0xbf, 0xb1, 0x2c, 0x1b, 0x0a, 0x22, 0xf1, 0x13, 0x4a, 0xa4, 0x9d, 0xd7, 0x81, - 0x91, 0x69, 0xdd, 0x83, 0x52, 0x9f, 0xe3, 0x80, 0x08, 0xc2, 0xa8, 0x3d, 0xbb, 0x9a, 0x5b, 0x9b, - 0xf7, 0xc6, 0x0e, 0xab, 0x0d, 0x0b, 0x84, 0x12, 0x49, 0x50, 0xaf, 0x83, 0x62, 0x96, 0x50, 0x69, - 0xdf, 0x50, 0xaf, 0x37, 0x9c, 0xc3, 0xa3, 0xea, 0xcc, 0x3f, 0x47, 0xd5, 0xfb, 0x11, 0x91, 0xdd, - 0xc4, 0x77, 0x02, 0x16, 0xa7, 0x7b, 0x48, 0xff, 0x6d, 0x88, 0xf0, 0x85, 0x2b, 0x07, 0x7d, 0x2c, - 0x9c, 0x1d, 0x2a, 0xbd, 0xf9, 0x94, 0xb2, 0xa5, 0x21, 0xd6, 0x2a, 0x94, 0x43, 0x2c, 0x02, 0x4e, - 0xfa, 0x52, 0xc9, 0xce, 0xe9, 0x25, 0x9d, 0x75, 0x59, 0x9f, 0x42, 0x71, 0x0f, 0x23, 0x99, 0x70, - 0x2c, 0xec, 0xc2, 0x6a, 0x7e, 0x6d, 0x61, 0x73, 0xc5, 0x79, 0xf3, 0x27, 0x75, 0x9a, 0x26, 0xc7, - 0x3b, 0x4d, 0xb6, 0xbe, 0x86, 0x92, 0x9f, 0x70, 0xda, 0xe1, 0x48, 0x62, 0xbb, 0x78, 0xe5, 0xc5, - 0x7e, 0x81, 0x03, 0xaf, 0xa8, 0x00, 0x1e, 0x92, 0xd8, 0xfa, 0x01, 0x16, 0x05, 0xa6, 0x61, 0x27, - 0x60, 0x71, 0x4c, 0x84, 0xaa, 0x88, 0xe1, 0x96, 0xde, 0x8a, 0x6b, 0x29, 0xd6, 0xf6, 0x29, 0x4a, - 0x2b, 0x2c, 0x41, 0x3e, 0xe1, 0xc4, 0x06, 0x0d, 0x2c, 0x0c, 0x8f, 0xaa, 0xf9, 0xb6, 0xb7, 0xe3, - 0x29, 0x9f, 0x75, 0x1f, 0x8a, 0x09, 0x27, 0x9d, 0x2e, 0x12, 0x5d, 0xbb, 0xac, 0xe3, 0xe5, 0xe1, - 0x51, 0xb5, 0xd0, 0xf6, 0x76, 0x1e, 0x23, 0xd1, 0xf5, 0x0a, 0x09, 0x27, 0xea, 0xa1, 0x26, 0xa1, - 0xd0, 0x12, 0x51, 0x8b, 0x50, 0xa9, 0x7f, 0x7e, 0x4c, 0xc3, 0x71, 0x5b, 0x18, 0xcb, 0x7a, 0x00, - 0xb3, 0xaa, 0x27, 0x75, 0x53, 0x94, 0x37, 0x97, 0x9c, 0xb4, 0xdd, 0x54, 0xd3, 0x3a, 0x69, 0xd3, - 0x3a, 0xdb, 0x8c, 0xd0, 0xc6, 0xac, 0xda, 0x92, 0xa7, 0x93, 0x55, 0x67, 0xa8, 0x3e, 0xe8, 0x13, - 0x4c, 0x47, 0x5d, 0x33, 0x76, 0xd4, 0x76, 0xb5, 0x6a, 0x23, 0xe1, 0xf4, 0x52, 0xd5, 0xfc, 0x15, - 0x54, 0x6b, 0x1c, 0x4a, 0x2d, 0x11, 0x35, 0x39, 0xc6, 0xaf, 0x70, 0x26, 0xd9, 0x86, 0x02, 0x0a, - 0x02, 0xdd, 0x8f, 0xa6, 0xcf, 0x47, 0xe6, 0xdb, 0x69, 0x4a, 0x28, 0xb7, 0x44, 0xd4, 0xa6, 0x7b, - 0xd7, 0xaa, 0x9a, 0xc0, 0xcd, 0x96, 0x88, 0x9e, 0x62, 0xd9, 0xe4, 0xec, 0x15, 0xa6, 0xd7, 0x25, - 0xbb, 0x05, 0xb7, 0x5b, 0x22, 0xfa, 0xb2, 0xc7, 0x7c, 0xd4, 0xeb, 0x0d, 0x2e, 0x29, 0xf4, 0x22, - 0xdc, 0x08, 0x31, 0x65, 0x71, 0xaa, 0x6c, 0x8c, 0xda, 0x36, 0xdc, 0x39, 0x83, 0xb8, 0xb4, 0x6e, - 0x93, 0x21, 0x3f, 0xc2, 0x5d, 0xb3, 0xfd, 0xef, 0xba, 0x44, 0xe2, 0x1e, 0x11, 0x12, 0x87, 0xdf, - 0x90, 0x98, 0xc8, 0xeb, 0x2a, 0x84, 0xaf, 0x0b, 0xd1, 0xee, 0x47, 0x1c, 0x85, 0xf8, 0x99, 0x9a, - 0xe9, 0xbb, 0xf5, 0xab, 0xed, 0xc1, 0xaa, 0x42, 0x99, 0xf8, 0x41, 0x07, 0x53, 0xe4, 0xf7, 0x70, - 0xa8, 0xe5, 0x8b, 0x1e, 0x10, 0x3f, 0x78, 0x64, 0x3c, 0xb5, 0x3f, 0x73, 0x70, 0x4b, 0x8b, 0x84, - 0x48, 0xe2, 0x27, 0xfa, 0x60, 0xb1, 0x1e, 0x42, 0x09, 0x25, 0xb2, 0xcb, 0x38, 0x91, 0x03, 0xa3, - 0xd2, 0xb0, 0xff, 0xfa, 0x63, 0x63, 0x31, 0x5d, 0xf4, 0x56, 0x18, 0x72, 0x2c, 0xc4, 0x53, 0xc9, - 0x09, 0x8d, 0xbc, 0x71, 0xaa, 0xf5, 0x39, 0xcc, 0x99, 0xa3, 0x29, 0xfd, 0x8c, 0x97, 0x27, 0x0d, - 0x44, 0xa3, 0xd1, 0x28, 0xa9, 0x7d, 0xfe, 0x7e, 0x72, 0xb0, 0x9e, 0xf3, 0xd2, 0x97, 0x3e, 0xdb, - 0xf8, 0xe9, 0xe4, 0x60, 0x7d, 0x8c, 0xfb, 0xf9, 0xe4, 0x60, 0x7d, 0xf9, 0xcc, 0x98, 0xba, 0xb0, - 0xca, 0xda, 0x2d, 0x98, 0x7f, 0x14, 0xf7, 0xe5, 0xc0, 0xc3, 0xa2, 0xcf, 0xa8, 0xc0, 0x9b, 0xbf, - 0x15, 0x20, 0xdf, 0x12, 0x91, 0xf5, 0x18, 0x6e, 0x98, 0x33, 0xe8, 0xde, 0x24, 0xfd, 0xd1, 0x09, - 0xb5, 0xfc, 0xc1, 0xa4, 0xe8, 0x39, 0xa2, 0xd5, 0x84, 0x59, 0x3d, 0xb5, 0x56, 0x32, 0x40, 0x2a, - 0x38, 0x25, 0x47, 0xcf, 0xa1, 0x2c, 0x8e, 0x0a, 0x4e, 0xc3, 0xf9, 0x0a, 0xe6, 0xd2, 0xcf, 0xe1, - 0xfd, 0x0c, 0x92, 0x09, 0x4f, 0xc3, 0xfa, 0x16, 0x8a, 0xa7, 0xdf, 0x45, 0x35, 0x83, 0x36, 0x4a, - 0x98, 0x86, 0xf7, 0x04, 0x4a, 0xe3, 0x49, 0xb1, 0x9a, 0x01, 0x3c, 0xcd, 0x98, 0x86, 0xf8, 0x1c, - 0x16, 0x2e, 0x0c, 0x81, 0x8f, 0x32, 0xb0, 0xe7, 0xd3, 0xa6, 0x61, 0x7f, 0x0f, 0xef, 0xbe, 0x31, - 0x1d, 0x3e, 0xbe, 0x84, 0x7e, 0x95, 0x6a, 0x84, 0x70, 0x67, 0xd2, 0xe0, 0x58, 0xcf, 0xae, 0xcb, - 0xc5, 0xdc, 0x29, 0x2b, 0x74, 0x61, 0x3a, 0x64, 0x55, 0xe8, 0x7c, 0xda, 0x34, 0xec, 0x5d, 0xb8, - 0x79, 0x6e, 0x28, 0x7c, 0x98, 0x49, 0x1e, 0x27, 0x4d, 0xc1, 0x6d, 0x3c, 0x3b, 0xfc, 0xaf, 0x32, - 0x73, 0x38, 0xac, 0xe4, 0x5e, 0x0f, 0x2b, 0xb9, 0x7f, 0x87, 0x95, 0xdc, 0x2f, 0xc7, 0x95, 0x99, - 0xd7, 0xc7, 0x95, 0x99, 0xbf, 0x8f, 0x2b, 0x33, 0xcf, 0x1f, 0x9e, 0xb9, 0xa9, 0x6c, 0x6b, 0x54, - 0x93, 0x25, 0x34, 0x44, 0xea, 0xca, 0xe5, 0xa6, 0x37, 0xdd, 0xfd, 0x4f, 0xdc, 0x97, 0xe3, 0xeb, - 0xae, 0xbe, 0xbd, 0xf8, 0x73, 0xfa, 0x0a, 0xfa, 0xe0, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x88, - 0xa6, 0x22, 0x67, 0x73, 0x0b, 0x00, 0x00, + // 1007 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x96, 0xcf, 0x4f, 0x1b, 0x47, + 0x14, 0xc7, 0x71, 0x4d, 0xb0, 0xfd, 0x1c, 0x48, 0xb3, 0x41, 0xe9, 0x02, 0xa9, 0x4d, 0x5d, 0x35, + 0x45, 0x48, 0xec, 0x0a, 0x52, 0xa5, 0x52, 0xa5, 0x1e, 0x30, 0x0d, 0x0d, 0x6d, 0x1d, 0x45, 0x9b, + 0x40, 0xa5, 0x1c, 0xea, 0xce, 0xee, 0x0e, 0xeb, 0x11, 0xde, 0x19, 0x6b, 0x66, 0x96, 0x86, 0x5c, + 0x2a, 0xf5, 0xd8, 0x53, 0x4f, 0xfd, 0x1b, 0x7a, 0xe4, 0x50, 0xa9, 0xff, 0x02, 0xc7, 0xa8, 0xa7, + 0xaa, 0x07, 0xd4, 0x9a, 0x03, 0x7f, 0x40, 0xff, 0x81, 0x6a, 0x66, 0xd6, 0x36, 0x10, 0xaf, 0x58, + 0x72, 0xe0, 0x02, 0xfb, 0x7e, 0xec, 0xe7, 0x3b, 0xf3, 0xfc, 0xf6, 0xcd, 0xc0, 0x42, 0xc0, 0x38, + 0x4e, 0x62, 0x17, 0x09, 0x81, 0xa5, 0xbb, 0x2b, 0xdd, 0xfd, 0x55, 0x57, 0xbe, 0x74, 0x7a, 0x9c, + 0x49, 0x66, 0x59, 0x26, 0xe8, 0xe8, 0xa0, 0xb3, 0x2b, 0x9d, 0xfd, 0xd5, 0xf9, 0xdb, 0x28, 0x26, + 0x94, 0xb9, 0xfa, 0xaf, 0x49, 0x9b, 0xaf, 0x8f, 0x61, 0xf4, 0x10, 0x47, 0xb1, 0x48, 0x13, 0x6a, + 0xe3, 0x44, 0xd8, 0x1e, 0xa6, 0xa3, 0xb8, 0x88, 0x99, 0x70, 0x7d, 0x24, 0xb0, 0xbb, 0xbf, 0xea, + 0x63, 0x89, 0x56, 0xdd, 0x80, 0x91, 0x41, 0xfc, 0xbd, 0x34, 0x1e, 0x8b, 0x48, 0xbd, 0x1a, 0x8b, + 0x28, 0x0d, 0xcc, 0x99, 0x40, 0x5b, 0x5b, 0xae, 0x31, 0xd2, 0xd0, 0x6c, 0xc4, 0x22, 0x66, 0xfc, + 0xea, 0xc9, 0x78, 0x1b, 0xbf, 0x4e, 0x42, 0xb9, 0x25, 0xa2, 0x2d, 0x21, 0x12, 0x6c, 0xdd, 0x85, + 0x29, 0xa2, 0x1e, 0xb8, 0x5d, 0x58, 0x2c, 0x2c, 0x55, 0xbc, 0xd4, 0x52, 0x7e, 0x71, 0x10, 0xfb, + 0xac, 0x6b, 0xbf, 0x63, 0xfc, 0xc6, 0xb2, 0x6c, 0x28, 0x89, 0xc4, 0x4f, 0x28, 0x91, 0x76, 0x51, + 0x07, 0x06, 0xa6, 0x75, 0x0f, 0x2a, 0x3d, 0x8e, 0x03, 0x22, 0x08, 0xa3, 0xf6, 0xe4, 0x62, 0x61, + 0x69, 0xda, 0x1b, 0x39, 0xac, 0x6d, 0x98, 0x21, 0x94, 0x48, 0x82, 0xba, 0x6d, 0x14, 0xb3, 0x84, + 0x4a, 0xfb, 0x86, 0x7a, 0xbd, 0xe9, 0x1c, 0x1d, 0xd7, 0x27, 0xfe, 0x3e, 0xae, 0xdf, 0x8f, 0x88, + 0xec, 0x24, 0xbe, 0x13, 0xb0, 0x38, 0xdd, 0x43, 0xfa, 0x6f, 0x45, 0x84, 0x7b, 0xae, 0x3c, 0xe8, + 0x61, 0xe1, 0x6c, 0x51, 0xe9, 0x4d, 0xa7, 0x94, 0x75, 0x0d, 0xb1, 0x16, 0xa1, 0x1a, 0x62, 0x11, + 0x70, 0xd2, 0x93, 0x4a, 0x76, 0x4a, 0x2f, 0xe9, 0xac, 0xcb, 0xfa, 0x14, 0xca, 0xbb, 0x18, 0xc9, + 0x84, 0x63, 0x61, 0x97, 0x16, 0x8b, 0x4b, 0x33, 0x6b, 0x0b, 0xce, 0x9b, 0x3f, 0xa9, 0xb3, 0x69, + 0x72, 0xbc, 0x61, 0xb2, 0xf5, 0x35, 0x54, 0xfc, 0x84, 0xd3, 0x36, 0x47, 0x12, 0xdb, 0xe5, 0x2b, + 0x2f, 0xf6, 0x0b, 0x1c, 0x78, 0x65, 0x05, 0xf0, 0x90, 0xc4, 0xd6, 0xf7, 0x30, 0x2b, 0x30, 0x0d, + 0xdb, 0x01, 0x8b, 0x63, 0x22, 0x54, 0x45, 0x0c, 0xb7, 0xf2, 0x56, 0x5c, 0x4b, 0xb1, 0x36, 0x86, + 0x28, 0xad, 0x30, 0x07, 0xc5, 0x84, 0x13, 0x1b, 0x34, 0xb0, 0xd4, 0x3f, 0xae, 0x17, 0xb7, 0xbd, + 0x2d, 0x4f, 0xf9, 0xac, 0xfb, 0x50, 0x4e, 0x38, 0x69, 0x77, 0x90, 0xe8, 0xd8, 0x55, 0x1d, 0xaf, + 0xf6, 0x8f, 0xeb, 0xa5, 0x6d, 0x6f, 0xeb, 0x31, 0x12, 0x1d, 0xaf, 0x94, 0x70, 0xa2, 0x1e, 0x1a, + 0x12, 0x4a, 0x2d, 0x11, 0xb5, 0x08, 0x95, 0xfa, 0xe7, 0xc7, 0x34, 0x1c, 0xb5, 0x85, 0xb1, 0xac, + 0x07, 0x30, 0xa9, 0x7a, 0x52, 0x37, 0x45, 0x75, 0x6d, 0xce, 0x49, 0xdb, 0x4d, 0x35, 0xad, 0x93, + 0x36, 0xad, 0xb3, 0xc1, 0x08, 0x6d, 0x4e, 0xaa, 0x2d, 0x79, 0x3a, 0x59, 0x75, 0x86, 0xea, 0x83, + 0x1e, 0xc1, 0x74, 0xd0, 0x35, 0x23, 0x47, 0x63, 0x47, 0xab, 0x36, 0x13, 0x4e, 0x2f, 0x55, 0x2d, + 0x5e, 0x41, 0xb5, 0xc1, 0xa1, 0xd2, 0x12, 0xd1, 0x26, 0xc7, 0xf8, 0x15, 0xce, 0x24, 0xdb, 0x50, + 0x42, 0x41, 0xa0, 0xfb, 0xd1, 0xf4, 0xf9, 0xc0, 0x7c, 0x3b, 0x4d, 0x09, 0xd5, 0x96, 0x88, 0xb6, + 0xe9, 0xee, 0xb5, 0xaa, 0x26, 0x70, 0xb3, 0x25, 0xa2, 0x67, 0x58, 0x6e, 0x72, 0xf6, 0x0a, 0xd3, + 0xeb, 0x92, 0x5d, 0x87, 0xdb, 0x2d, 0x11, 0x7d, 0xd9, 0x65, 0x3e, 0xea, 0x76, 0x0f, 0x2e, 0x29, + 0xf4, 0x2c, 0xdc, 0x08, 0x31, 0x65, 0x71, 0xaa, 0x6c, 0x8c, 0xc6, 0x06, 0xdc, 0x39, 0x83, 0xb8, + 0xb4, 0x6e, 0xe3, 0x21, 0xa6, 0xe8, 0x1b, 0x5d, 0xf4, 0x83, 0x8f, 0x82, 0xbd, 0xeb, 0xda, 0xfd, + 0x8f, 0x70, 0xd7, 0x14, 0xfd, 0xdb, 0x0e, 0x91, 0xb8, 0x4b, 0x84, 0xc4, 0xe1, 0x37, 0x24, 0x26, + 0xf2, 0xba, 0x16, 0xe0, 0xeb, 0xf2, 0x6f, 0xf7, 0x22, 0x8e, 0x42, 0xfc, 0x5c, 0x9d, 0x24, 0x3b, + 0xab, 0x57, 0xab, 0x9c, 0x55, 0x87, 0x2a, 0xf1, 0x83, 0x36, 0xa6, 0xc8, 0xef, 0xe2, 0x50, 0xcb, + 0x97, 0x3d, 0x20, 0x7e, 0xf0, 0xc8, 0x78, 0x1a, 0x7f, 0x14, 0xe0, 0x96, 0x16, 0x09, 0x91, 0xc4, + 0x4f, 0xf5, 0x71, 0x66, 0x3d, 0x84, 0x0a, 0x4a, 0x64, 0x87, 0x71, 0x22, 0x0f, 0x8c, 0x4a, 0xd3, + 0xfe, 0xf3, 0xf7, 0x95, 0xd9, 0x74, 0xd1, 0xeb, 0x61, 0xc8, 0xb1, 0x10, 0xcf, 0x24, 0x27, 0x34, + 0xf2, 0x46, 0xa9, 0xd6, 0xe7, 0x30, 0x65, 0x0e, 0xc4, 0x74, 0x78, 0xcc, 0x8f, 0x1b, 0xc3, 0x46, + 0xa3, 0x59, 0x51, 0xfb, 0xfc, 0xed, 0xf4, 0x70, 0xb9, 0xe0, 0xa5, 0x2f, 0x7d, 0xb6, 0xf2, 0xd3, + 0xe9, 0xe1, 0xf2, 0x08, 0xf7, 0xf3, 0xe9, 0xe1, 0xf2, 0xfc, 0x99, 0xe1, 0x78, 0x61, 0x95, 0x8d, + 0x5b, 0x30, 0xfd, 0x28, 0xee, 0xc9, 0x03, 0x0f, 0x8b, 0x1e, 0xa3, 0x02, 0xaf, 0xfd, 0x57, 0x82, + 0x62, 0x4b, 0x44, 0xd6, 0x63, 0xb8, 0x61, 0x4e, 0xbe, 0x7b, 0xe3, 0xf4, 0x07, 0xe7, 0xe2, 0xfc, + 0x07, 0xe3, 0xa2, 0xe7, 0x88, 0xd6, 0x26, 0x4c, 0xea, 0x59, 0xb9, 0x90, 0x01, 0x52, 0xc1, 0x9c, + 0x1c, 0x3d, 0xfd, 0xb2, 0x38, 0x2a, 0x98, 0x87, 0xf3, 0x15, 0x4c, 0xa5, 0x1f, 0xe1, 0xfb, 0x19, + 0x24, 0x13, 0xce, 0xc3, 0x7a, 0x02, 0xe5, 0xe1, 0xd7, 0x58, 0xcf, 0xa0, 0x0d, 0x12, 0xf2, 0xf0, + 0x9e, 0x42, 0x65, 0x34, 0x9f, 0x16, 0x33, 0x80, 0xc3, 0x8c, 0x3c, 0xc4, 0x17, 0x30, 0x73, 0x61, + 0xf4, 0x7c, 0x94, 0x81, 0x3d, 0x9f, 0x96, 0x87, 0xfd, 0x1d, 0xbc, 0xfb, 0xc6, 0x4c, 0xfa, 0xf8, + 0x12, 0xfa, 0x55, 0xaa, 0xf1, 0x04, 0xca, 0xc3, 0x71, 0x95, 0x55, 0xdd, 0x41, 0x42, 0x1e, 0x5e, + 0x08, 0x77, 0xc6, 0x0d, 0xa2, 0xe5, 0xec, 0x3a, 0x5f, 0xcc, 0xcd, 0x59, 0xf1, 0x0b, 0xd3, 0x26, + 0xab, 0xe2, 0xe7, 0xd3, 0xf2, 0xb0, 0x77, 0xe0, 0xe6, 0xb9, 0x21, 0xf3, 0x61, 0x26, 0x79, 0x94, + 0x94, 0x83, 0xdb, 0x7c, 0x7e, 0xf4, 0x6f, 0x6d, 0xe2, 0xa8, 0x5f, 0x2b, 0xbc, 0xee, 0xd7, 0x0a, + 0xff, 0xf4, 0x6b, 0x85, 0x5f, 0x4e, 0x6a, 0x13, 0xaf, 0x4f, 0x6a, 0x13, 0x7f, 0x9d, 0xd4, 0x26, + 0x5e, 0x3c, 0x3c, 0x73, 0xdf, 0xda, 0xd0, 0xa8, 0x4d, 0x96, 0xd0, 0x10, 0xa9, 0x8b, 0xa3, 0x9b, + 0xde, 0xd7, 0xf7, 0x3f, 0x71, 0x5f, 0x8e, 0x2e, 0xed, 0xfa, 0x0e, 0xe6, 0x4f, 0xe9, 0x8b, 0xf4, + 0x83, 0xff, 0x03, 0x00, 0x00, 0xff, 0xff, 0x77, 0xc6, 0x69, 0x0d, 0x39, 0x0c, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -623,6 +664,9 @@ type MsgClient interface { // GloballyUnfreeze unfreezes fungible token and unblocks basic operations on it. // This operation is idempotent so global unfreezing of non-frozen token does nothing. GloballyUnfreeze(ctx context.Context, in *MsgGloballyUnfreeze, opts ...grpc.CallOption) (*EmptyResponse, error) + // Clawback confiscates a part of fungible tokens from an account + // to the issuer, only if the clawback feature is enabled on that token. + Clawback(ctx context.Context, in *MsgClawback, opts ...grpc.CallOption) (*EmptyResponse, error) // SetWhitelistedLimit sets the limit of how many tokens a specific account may hold. SetWhitelistedLimit(ctx context.Context, in *MsgSetWhitelistedLimit, opts ...grpc.CallOption) (*EmptyResponse, error) // TokenUpgradeV1 upgrades token to version V1. @@ -712,6 +756,15 @@ func (c *msgClient) GloballyUnfreeze(ctx context.Context, in *MsgGloballyUnfreez return out, nil } +func (c *msgClient) Clawback(ctx context.Context, in *MsgClawback, opts ...grpc.CallOption) (*EmptyResponse, error) { + out := new(EmptyResponse) + err := c.cc.Invoke(ctx, "/coreum.asset.ft.v1.Msg/Clawback", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *msgClient) SetWhitelistedLimit(ctx context.Context, in *MsgSetWhitelistedLimit, opts ...grpc.CallOption) (*EmptyResponse, error) { out := new(EmptyResponse) err := c.cc.Invoke(ctx, "/coreum.asset.ft.v1.Msg/SetWhitelistedLimit", in, out, opts...) @@ -761,6 +814,9 @@ type MsgServer interface { // GloballyUnfreeze unfreezes fungible token and unblocks basic operations on it. // This operation is idempotent so global unfreezing of non-frozen token does nothing. GloballyUnfreeze(context.Context, *MsgGloballyUnfreeze) (*EmptyResponse, error) + // Clawback confiscates a part of fungible tokens from an account + // to the issuer, only if the clawback feature is enabled on that token. + Clawback(context.Context, *MsgClawback) (*EmptyResponse, error) // SetWhitelistedLimit sets the limit of how many tokens a specific account may hold. SetWhitelistedLimit(context.Context, *MsgSetWhitelistedLimit) (*EmptyResponse, error) // TokenUpgradeV1 upgrades token to version V1. @@ -798,6 +854,9 @@ func (*UnimplementedMsgServer) GloballyFreeze(ctx context.Context, req *MsgGloba func (*UnimplementedMsgServer) GloballyUnfreeze(ctx context.Context, req *MsgGloballyUnfreeze) (*EmptyResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GloballyUnfreeze not implemented") } +func (*UnimplementedMsgServer) Clawback(ctx context.Context, req *MsgClawback) (*EmptyResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Clawback not implemented") +} func (*UnimplementedMsgServer) SetWhitelistedLimit(ctx context.Context, req *MsgSetWhitelistedLimit) (*EmptyResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method SetWhitelistedLimit not implemented") } @@ -956,6 +1015,24 @@ func _Msg_GloballyUnfreeze_Handler(srv interface{}, ctx context.Context, dec fun return interceptor(ctx, in, info, handler) } +func _Msg_Clawback_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgClawback) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).Clawback(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/coreum.asset.ft.v1.Msg/Clawback", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).Clawback(ctx, req.(*MsgClawback)) + } + return interceptor(ctx, in, info, handler) +} + func _Msg_SetWhitelistedLimit_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(MsgSetWhitelistedLimit) if err := dec(in); err != nil { @@ -1046,6 +1123,10 @@ var _Msg_serviceDesc = grpc.ServiceDesc{ MethodName: "GloballyUnfreeze", Handler: _Msg_GloballyUnfreeze_Handler, }, + { + MethodName: "Clawback", + Handler: _Msg_Clawback_Handler, + }, { MethodName: "SetWhitelistedLimit", Handler: _Msg_SetWhitelistedLimit_Handler, @@ -1483,6 +1564,53 @@ func (m *MsgGloballyUnfreeze) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *MsgClawback) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgClawback) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgClawback) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Coin.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + if len(m.Account) > 0 { + i -= len(m.Account) + copy(dAtA[i:], m.Account) + i = encodeVarintTx(dAtA, i, uint64(len(m.Account))) + i-- + dAtA[i] = 0x12 + } + if len(m.Sender) > 0 { + i -= len(m.Sender) + copy(dAtA[i:], m.Sender) + i = encodeVarintTx(dAtA, i, uint64(len(m.Sender))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + func (m *MsgSetWhitelistedLimit) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -1825,6 +1953,25 @@ func (m *MsgGloballyUnfreeze) Size() (n int) { return n } +func (m *MsgClawback) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Sender) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.Account) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = m.Coin.Size() + n += 1 + l + sovTx(uint64(l)) + return n +} + func (m *MsgSetWhitelistedLimit) Size() (n int) { if m == nil { return 0 @@ -3257,6 +3404,153 @@ func (m *MsgGloballyUnfreeze) Unmarshal(dAtA []byte) error { } return nil } +func (m *MsgClawback) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgClawback: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgClawback: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Sender", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Sender = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Account", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Account = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Coin", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Coin.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *MsgSetWhitelistedLimit) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 diff --git a/x/deterministicgas/config.go b/x/deterministicgas/config.go index 7896c6baa..06e9ea2fe 100644 --- a/x/deterministicgas/config.go +++ b/x/deterministicgas/config.go @@ -83,6 +83,7 @@ func DefaultConfig() Config { MsgToMsgURL(&assetfttypes.MsgSetFrozen{}): constantGasFunc(8_500), MsgToMsgURL(&assetfttypes.MsgGloballyFreeze{}): constantGasFunc(5_000), MsgToMsgURL(&assetfttypes.MsgGloballyUnfreeze{}): constantGasFunc(5_000), + MsgToMsgURL(&assetfttypes.MsgClawback{}): constantGasFunc(73_500), MsgToMsgURL(&assetfttypes.MsgSetWhitelistedLimit{}): constantGasFunc(9_000), // TODO(v4): Once we add a new token upgrade MsgUpgradeTokenV2 we should remove this one and re-estimate gas. MsgToMsgURL(&assetfttypes.MsgUpgradeTokenV1{}): constantGasFunc(25_000), From 0212b8dbfce62a4c946aa74381ee4d2a6134652d Mon Sep 17 00:00:00 2001 From: Masih Yeganeh Date: Tue, 16 Apr 2024 12:49:32 +0330 Subject: [PATCH 02/12] Add test for clawback from issuer --- x/asset/ft/keeper/keeper_test.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/x/asset/ft/keeper/keeper_test.go b/x/asset/ft/keeper/keeper_test.go index 542395c91..a793fc5c0 100644 --- a/x/asset/ft/keeper/keeper_test.go +++ b/x/asset/ft/keeper/keeper_test.go @@ -1320,11 +1320,16 @@ func TestKeeper_Clawback(t *testing.T) { err = ftKeeper.Clawback(ctx, issuer, from, sdk.NewCoin(clawbackDisabledDenom, sdkmath.NewInt(10))) requireT.ErrorIs(err, types.ErrFeatureDisabled) - // try to clawback from non issuer address + // try to clawback by non issuer address randomAddr := sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()) err = ftKeeper.Clawback(ctx, randomAddr, from, sdk.NewCoin(denom, sdkmath.NewInt(10))) requireT.ErrorIs(err, cosmoserrors.ErrUnauthorized) + // try to clawback from issuer address + randomAddr = sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()) + err = ftKeeper.Clawback(ctx, randomAddr, from, sdk.NewCoin(denom, sdkmath.NewInt(10))) + requireT.ErrorIs(err, cosmoserrors.ErrUnauthorized) + // try to clawback 0 balance err = ftKeeper.Clawback(ctx, issuer, from, sdk.NewCoin(denom, sdkmath.NewInt(0))) requireT.ErrorIs(err, cosmoserrors.ErrInvalidCoins) From b67c44c83705ad7651e4132e7d65200c3e696ed3 Mon Sep 17 00:00:00 2001 From: Masih Yeganeh Date: Tue, 16 Apr 2024 13:28:05 +0330 Subject: [PATCH 03/12] Fixed issues mentioned in review --- docs/api.md | 2 +- x/asset/ft/client/cli/tx.go | 2 + x/asset/ft/client/cli/tx_test.go | 9 ++-- x/asset/ft/types/msgs.go | 5 +++ x/asset/ft/types/msgs_test.go | 69 +++++++++++++++++++++++++++++++ x/deterministicgas/config.go | 2 +- x/deterministicgas/spec/README.md | 1 + 7 files changed, 85 insertions(+), 5 deletions(-) diff --git a/docs/api.md b/docs/api.md index 7d281e445..73b5b9e7e 100644 --- a/docs/api.md +++ b/docs/api.md @@ -2524,7 +2524,7 @@ Msg defines the Msg service. | `SetFrozen` | [MsgSetFrozen](#coreum.asset.ft.v1.MsgSetFrozen) | [EmptyResponse](#coreum.asset.ft.v1.EmptyResponse) | `SetFrozen sets the absolute value of frozen amount.` | | | `GloballyFreeze` | [MsgGloballyFreeze](#coreum.asset.ft.v1.MsgGloballyFreeze) | [EmptyResponse](#coreum.asset.ft.v1.EmptyResponse) | `GloballyFreeze freezes fungible token so no operations are allowed with it before unfrozen. This operation is idempotent so global freeze of already frozen token does nothing.` | | | `GloballyUnfreeze` | [MsgGloballyUnfreeze](#coreum.asset.ft.v1.MsgGloballyUnfreeze) | [EmptyResponse](#coreum.asset.ft.v1.EmptyResponse) | `GloballyUnfreeze unfreezes fungible token and unblocks basic operations on it. This operation is idempotent so global unfreezing of non-frozen token does nothing.` | | -| `Clawback` | [MsgClawback](#coreum.asset.ft.v1.MsgClawback) | [EmptyResponse](#coreum.asset.ft.v1.EmptyResponse) | `Clawback returns a part of fungible tokens from an account to the issuer, only if the clawback feature is enabled on that token.` | | +| `Clawback` | [MsgClawback](#coreum.asset.ft.v1.MsgClawback) | [EmptyResponse](#coreum.asset.ft.v1.EmptyResponse) | `Clawback confiscates a part of fungible tokens from an account to the issuer, only if the clawback feature is enabled on that token.` | | | `SetWhitelistedLimit` | [MsgSetWhitelistedLimit](#coreum.asset.ft.v1.MsgSetWhitelistedLimit) | [EmptyResponse](#coreum.asset.ft.v1.EmptyResponse) | `SetWhitelistedLimit sets the limit of how many tokens a specific account may hold.` | | | `UpgradeTokenV1` | [MsgUpgradeTokenV1](#coreum.asset.ft.v1.MsgUpgradeTokenV1) | [EmptyResponse](#coreum.asset.ft.v1.EmptyResponse) | `TokenUpgradeV1 upgrades token to version V1.` | | | `UpdateParams` | [MsgUpdateParams](#coreum.asset.ft.v1.MsgUpdateParams) | [EmptyResponse](#coreum.asset.ft.v1.EmptyResponse) | `UpdateParams is a governance operation to modify the parameters of the module. NOTE: all parameters must be provided.` | | diff --git a/x/asset/ft/client/cli/tx.go b/x/asset/ft/client/cli/tx.go index ece772f01..ac28e5be8 100644 --- a/x/asset/ft/client/cli/tx.go +++ b/x/asset/ft/client/cli/tx.go @@ -417,6 +417,8 @@ $ %s tx %s set-frozen [account_address] 100000ABC-%s --from [sender] } // CmdTxClawback returns Clawback cobra command. +// +//nolint:dupl // most code is identical, but reusing logic is not beneficial here. func CmdTxClawback() *cobra.Command { cmd := &cobra.Command{ Use: "clawback [account_address] [amount] --from [sender]", diff --git a/x/asset/ft/client/cli/tx_test.go b/x/asset/ft/client/cli/tx_test.go index dee379f29..a96cbe6e6 100644 --- a/x/asset/ft/client/cli/tx_test.go +++ b/x/asset/ft/client/cli/tx_test.go @@ -294,19 +294,22 @@ func TestClawback(t *testing.T) { coin := sdk.NewInt64Coin(denom, 100) - args := append([]string{testNetwork.Validators[0].Address.String(), account.String(), coin.String()}, txValidator1Args(testNetwork)...) + valAddr := testNetwork.Validators[0].Address.String() + args := append([]string{valAddr, account.String(), coin.String()}, txValidator1Args(testNetwork)...) _, err := coreumclitestutil.ExecTxCmd(ctx, testNetwork, bankcli.NewSendTxCmd(), args) requireT.NoError(err) var balanceRsp banktypes.QueryAllBalancesResponse - requireT.NoError(coreumclitestutil.ExecQueryCmd(ctx, bankcli.GetBalancesCmd(), []string{account.String()}, &balanceRsp)) + args = []string{account.String()} + requireT.NoError(coreumclitestutil.ExecQueryCmd(ctx, bankcli.GetBalancesCmd(), args, &balanceRsp)) requireT.Equal(sdkmath.NewInt(100).String(), balanceRsp.Balances.AmountOf(denom).String()) args = append([]string{account.String(), coin.String()}, txValidator1Args(testNetwork)...) _, err = coreumclitestutil.ExecTxCmd(ctx, testNetwork, cli.CmdTxClawback(), args) requireT.NoError(err) - requireT.NoError(coreumclitestutil.ExecQueryCmd(ctx, bankcli.GetBalancesCmd(), []string{account.String()}, &balanceRsp)) + args = []string{account.String()} + requireT.NoError(coreumclitestutil.ExecQueryCmd(ctx, bankcli.GetBalancesCmd(), args, &balanceRsp)) requireT.Equal(sdkmath.NewInt(0).String(), balanceRsp.Balances.AmountOf(denom).String()) } diff --git a/x/asset/ft/types/msgs.go b/x/asset/ft/types/msgs.go index 253fb1156..9c3663820 100644 --- a/x/asset/ft/types/msgs.go +++ b/x/asset/ft/types/msgs.go @@ -433,6 +433,7 @@ func (m MsgGloballyUnfreeze) Type() string { return TypeMsgGloballyUnfreeze } +// ValidateBasic checks that message fields are valid. func (m MsgClawback) ValidateBasic() error { if _, err := sdk.AccAddressFromBech32(m.Sender); err != nil { return sdkerrors.Wrap(cosmoserrors.ErrInvalidAddress, "invalid sender address") @@ -458,20 +459,24 @@ func (m MsgClawback) ValidateBasic() error { return m.Coin.Validate() } +// GetSigners returns the required signers of this message type. func (m MsgClawback) GetSigners() []sdk.AccAddress { return []sdk.AccAddress{ sdk.MustAccAddressFromBech32(m.Sender), } } +// GetSignBytes returns sign bytes for LegacyMsg. func (m MsgClawback) GetSignBytes() []byte { return sdk.MustSortJSON(moduleAminoCdc.MustMarshalJSON(&m)) } +// Route returns message route for LegacyMsg. func (m MsgClawback) Route() string { return RouterKey } +// Type returns message type for LegacyMsg. func (m MsgClawback) Type() string { return TypeMsgClawback } diff --git a/x/asset/ft/types/msgs_test.go b/x/asset/ft/types/msgs_test.go index 247020d73..f99c0becd 100644 --- a/x/asset/ft/types/msgs_test.go +++ b/x/asset/ft/types/msgs_test.go @@ -415,6 +415,75 @@ func TestMsgBurn_ValidateBasic(t *testing.T) { } } +func TestMsgClawback_ValidateBasic(t *testing.T) { + testCases := []struct { + name string + message types.MsgClawback + expectedError error + }{ + { + name: "valid msg", + message: types.MsgClawback{ + Sender: "devcore172rc5sz2uclpsy3vvx3y79ah5dk450z5ruq2r5", + Account: "devcore1k3mke3gyf9apyd8vxveutgp9h4j2e80e05yfuq", + Coin: sdk.Coin{ + Denom: "abc-devcore172rc5sz2uclpsy3vvx3y79ah5dk450z5ruq2r5", + Amount: sdkmath.NewInt(100), + }, + }, + }, + { + name: "invalid sender address", + message: types.MsgClawback{ + Sender: "devcore172rc5sz2uclpsy3vvx3y79ah5dk450z5ruq2r5+", + Account: "devcore1k3mke3gyf9apyd8vxveutgp9h4j2e80e05yfuq", + Coin: sdk.Coin{ + Denom: "abc-devcore172rc5sz2uclpsy3vvx3y79ah5dk450z5ruq2r5", + Amount: sdkmath.NewInt(100), + }, + }, + expectedError: cosmoserrors.ErrInvalidAddress, + }, + { + name: "invalid account", + message: types.MsgClawback{ + Sender: "devcore172rc5sz2uclpsy3vvx3y79ah5dk450z5ruq2r5", + Account: "devcore1k3mke3gyf9apyd8vxveutgp9h4j2e80e05yfuq+", + Coin: sdk.Coin{ + Denom: "abc-devcore172rc5sz2uclpsy3vvx3y79ah5dk450z5ruq2r5", + Amount: sdkmath.NewInt(100), + }, + }, + expectedError: cosmoserrors.ErrInvalidAddress, + }, + { + name: "issuer freezing", + message: types.MsgClawback{ + Sender: "devcore172rc5sz2uclpsy3vvx3y79ah5dk450z5ruq2r5", + Account: "devcore172rc5sz2uclpsy3vvx3y79ah5dk450z5ruq2r5", + Coin: sdk.Coin{ + Denom: "abc-devcore172rc5sz2uclpsy3vvx3y79ah5dk450z5ruq2r5", + Amount: sdkmath.NewInt(100), + }, + }, + expectedError: cosmoserrors.ErrUnauthorized, + }, + } + + for _, testCase := range testCases { + tc := testCase + t.Run(tc.name, func(t *testing.T) { + requireT := require.New(t) + err := tc.message.ValidateBasic() + if tc.expectedError == nil { + requireT.NoError(err) + } else { + requireT.True(sdkerrors.IsOf(err, tc.expectedError)) + } + }) + } +} + func TestMsgSetWhitelistedLimit_ValidateBasic(t *testing.T) { testCases := []struct { name string diff --git a/x/deterministicgas/config.go b/x/deterministicgas/config.go index 06e9ea2fe..378636afa 100644 --- a/x/deterministicgas/config.go +++ b/x/deterministicgas/config.go @@ -83,7 +83,7 @@ func DefaultConfig() Config { MsgToMsgURL(&assetfttypes.MsgSetFrozen{}): constantGasFunc(8_500), MsgToMsgURL(&assetfttypes.MsgGloballyFreeze{}): constantGasFunc(5_000), MsgToMsgURL(&assetfttypes.MsgGloballyUnfreeze{}): constantGasFunc(5_000), - MsgToMsgURL(&assetfttypes.MsgClawback{}): constantGasFunc(73_500), + MsgToMsgURL(&assetfttypes.MsgClawback{}): constantGasFunc(8_500), MsgToMsgURL(&assetfttypes.MsgSetWhitelistedLimit{}): constantGasFunc(9_000), // TODO(v4): Once we add a new token upgrade MsgUpgradeTokenV2 we should remove this one and re-estimate gas. MsgToMsgURL(&assetfttypes.MsgUpgradeTokenV1{}): constantGasFunc(25_000), diff --git a/x/deterministicgas/spec/README.md b/x/deterministicgas/spec/README.md index 4b657bb37..592b5164b 100644 --- a/x/deterministicgas/spec/README.md +++ b/x/deterministicgas/spec/README.md @@ -80,6 +80,7 @@ TotalGas = 65000 + max(0, (21480 - 2 * 1000 + 2050 * 10)) + 2 * 70000 | `/cosmos.bank.v1beta1.MsgMultiSend` | [special case](#special-cases) | | `/cosmos.bank.v1beta1.MsgSend` | [special case](#special-cases) | | `/coreum.asset.ft.v1.MsgBurn` | 35000 | +| `/coreum.asset.ft.v1.MsgClawback` | 8500 | | `/coreum.asset.ft.v1.MsgFreeze` | 8500 | | `/coreum.asset.ft.v1.MsgGloballyFreeze` | 5000 | | `/coreum.asset.ft.v1.MsgGloballyUnfreeze` | 5000 | From cf70aa7267f6ed923938a5c7a941602a0b5a632f Mon Sep 17 00:00:00 2001 From: Masih Yeganeh Date: Tue, 16 Apr 2024 13:29:52 +0330 Subject: [PATCH 04/12] Ignore mac files --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 71acdd465..949fc3549 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ vendor /bin *.iml go.work* +.DS_Store From 2b8ed7c3e11c354ce360609de2c2bb52bd1ac4e1 Mon Sep 17 00:00:00 2001 From: Masih Yeganeh Date: Tue, 16 Apr 2024 13:55:42 +0330 Subject: [PATCH 05/12] Fix deterministicgas unit test --- x/deterministicgas/config_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x/deterministicgas/config_test.go b/x/deterministicgas/config_test.go index 98791e11d..a608a1fd3 100644 --- a/x/deterministicgas/config_test.go +++ b/x/deterministicgas/config_test.go @@ -193,7 +193,7 @@ func TestDeterministicGas_DeterministicMessages(t *testing.T) { // we assert length to be equal to exact number, so each change requires // explicit adjustment of tests. assert.Len(t, nondeterministicMsgs, 62) - assert.Len(t, deterministicMsgs, 65) + assert.Len(t, deterministicMsgs, 66) for _, sdkMsg := range deterministicMsgs { sdkMsg := sdkMsg From b88b05238b1f4a696fbf078db4c730f7aeddd888 Mon Sep 17 00:00:00 2001 From: Masih Yeganeh Date: Tue, 16 Apr 2024 14:07:58 +0330 Subject: [PATCH 06/12] Fix ValidateBasic unit tests --- x/asset/ft/types/msgs_test.go | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/x/asset/ft/types/msgs_test.go b/x/asset/ft/types/msgs_test.go index f99c0becd..8d970dd38 100644 --- a/x/asset/ft/types/msgs_test.go +++ b/x/asset/ft/types/msgs_test.go @@ -457,7 +457,7 @@ func TestMsgClawback_ValidateBasic(t *testing.T) { expectedError: cosmoserrors.ErrInvalidAddress, }, { - name: "issuer freezing", + name: "issuer clawback", message: types.MsgClawback{ Sender: "devcore172rc5sz2uclpsy3vvx3y79ah5dk450z5ruq2r5", Account: "devcore172rc5sz2uclpsy3vvx3y79ah5dk450z5ruq2r5", @@ -468,6 +468,18 @@ func TestMsgClawback_ValidateBasic(t *testing.T) { }, expectedError: cosmoserrors.ErrUnauthorized, }, + { + name: "clawback by non-issuer", + message: types.MsgClawback{ + Sender: "devcore172rc5sz2uclpsy3vvx3y79ah5dk450z5ruq2r5", + Account: "devcore1k3mke3gyf9apyd8vxveutgp9h4j2e80e05yfuq", + Coin: sdk.Coin{ + Denom: "abc-devcore1yetewq7jcujq7cqdes4qxxaqakzthwu72xfsnh", + Amount: sdkmath.NewInt(100), + }, + }, + expectedError: cosmoserrors.ErrUnauthorized, + }, } for _, testCase := range testCases { From cd94cacf40d465d2bdd07027976c183382785f01 Mon Sep 17 00:00:00 2001 From: Masih Yeganeh Date: Thu, 18 Apr 2024 10:02:08 +0330 Subject: [PATCH 07/12] Fix changes requested in review --- app/app.go | 1 + integration-tests/modules/assetft_test.go | 17 ++------- x/asset/ft/keeper/before_send_test.go | 2 +- x/asset/ft/keeper/keeper.go | 45 +++++++++++++---------- x/asset/ft/keeper/keeper_test.go | 21 ++++++++++- x/asset/ft/types/msgs.go | 10 +---- x/asset/ft/types/msgs_test.go | 24 ------------ x/deterministicgas/config.go | 2 +- x/deterministicgas/spec/README.md | 2 +- 9 files changed, 53 insertions(+), 71 deletions(-) diff --git a/app/app.go b/app/app.go index f929908a5..fbfd071a4 100644 --- a/app/app.go +++ b/app/app.go @@ -419,6 +419,7 @@ func New( // pointer is used here because there is cycle in keeper dependencies: // AssetFTKeeper -> WasmKeeper -> BankKeeper -> AssetFTKeeper &app.WasmKeeper, + &app.AccountKeeper, authtypes.NewModuleAddress(govtypes.ModuleName).String(), ) diff --git a/integration-tests/modules/assetft_test.go b/integration-tests/modules/assetft_test.go index 26459e73f..5e7daba0e 100644 --- a/integration-tests/modules/assetft_test.go +++ b/integration-tests/modules/assetft_test.go @@ -2051,23 +2051,12 @@ func TestAssetFTClawback(t *testing.T) { &assetfttypes.MsgIssue{}, &banktypes.MsgSend{}, &assetfttypes.MsgClawback{}, - }, - Amount: chain.QueryAssetFTParams(ctx, t).IssueFee.Amount, - }) - chain.FundAccountWithOptions(ctx, t, from, integration.BalancesOptions{ - Messages: []sdk.Msg{ - &banktypes.MsgSend{}, - &banktypes.MsgMultiSend{}, - &banktypes.MsgSend{}, - &banktypes.MsgMultiSend{}, - &banktypes.MsgSend{}, - &banktypes.MsgSend{}, &assetfttypes.MsgClawback{}, }, + Amount: chain.QueryAssetFTParams(ctx, t).IssueFee.Amount, }) chain.FundAccountWithOptions(ctx, t, randomAddress, integration.BalancesOptions{ Messages: []sdk.Msg{ - &assetfttypes.MsgFreeze{}, &assetfttypes.MsgClawback{}, }, }) @@ -2127,7 +2116,7 @@ func TestAssetFTClawback(t *testing.T) { clawbackMsg, ) requireT.Error(err) - assertT.True(cosmoserrors.ErrUnauthorized.Is(err)) + assertT.ErrorIs(err, cosmoserrors.ErrUnauthorized) // clawback 400 tokens clawbackMsg = &assetfttypes.MsgClawback{ @@ -2172,7 +2161,7 @@ func TestAssetFTClawback(t *testing.T) { clawbackMsg, ) requireT.Error(err) - assertT.True(cosmoserrors.ErrInsufficientFunds.Is(err)) + assertT.ErrorIs(err, cosmoserrors.ErrInsufficientFunds) } // TestAssetFTFreezeUnfreezable checks freeze functionality on unfreezable fungible tokens. diff --git a/x/asset/ft/keeper/before_send_test.go b/x/asset/ft/keeper/before_send_test.go index f3ed3c26a..5fc7f839b 100644 --- a/x/asset/ft/keeper/before_send_test.go +++ b/x/asset/ft/keeper/before_send_test.go @@ -44,7 +44,7 @@ func TestApplyRate(t *testing.T) { issuer := genAccount() dummyAddress := genAccount() key := sdk.NewKVStoreKey(types.StoreKey) - assetFTKeeper := assetftkeeper.NewKeeper(nil, key, nil, nil, nil, "") + assetFTKeeper := assetftkeeper.NewKeeper(nil, key, nil, nil, nil, nil, "") testCases := []struct { name string diff --git a/x/asset/ft/keeper/keeper.go b/x/asset/ft/keeper/keeper.go index 4ba244ca1..c3f1761e6 100644 --- a/x/asset/ft/keeper/keeper.go +++ b/x/asset/ft/keeper/keeper.go @@ -6,6 +6,10 @@ import ( sdkerrors "cosmossdk.io/errors" sdkmath "cosmossdk.io/math" + "github.com/CoreumFoundation/coreum/v4/x/asset/ft/types" + "github.com/CoreumFoundation/coreum/v4/x/wasm" + cwasmtypes "github.com/CoreumFoundation/coreum/v4/x/wasm/types" + wibctransfertypes "github.com/CoreumFoundation/coreum/v4/x/wibctransfer/types" "github.com/cometbft/cometbft/libs/log" "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/store/prefix" @@ -13,23 +17,20 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" cosmoserrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/types/query" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" - - "github.com/CoreumFoundation/coreum/v4/x/asset/ft/types" - "github.com/CoreumFoundation/coreum/v4/x/wasm" - cwasmtypes "github.com/CoreumFoundation/coreum/v4/x/wasm/types" - wibctransfertypes "github.com/CoreumFoundation/coreum/v4/x/wibctransfer/types" ) // Keeper is the asset module keeper. type Keeper struct { - cdc codec.BinaryCodec - storeKey storetypes.StoreKey - bankKeeper types.BankKeeper - delayKeeper types.DelayKeeper - wasmKeeper cwasmtypes.WasmKeeper - authority string + cdc codec.BinaryCodec + storeKey storetypes.StoreKey + bankKeeper types.BankKeeper + delayKeeper types.DelayKeeper + wasmKeeper cwasmtypes.WasmKeeper + accountKeeper types.AccountKeeper + authority string } // NewKeeper creates a new instance of the Keeper. @@ -39,15 +40,17 @@ func NewKeeper( bankKeeper types.BankKeeper, delayKeeper types.DelayKeeper, wasmKeeper cwasmtypes.WasmKeeper, + accountKeeper types.AccountKeeper, authority string, ) Keeper { return Keeper{ - cdc: cdc, - storeKey: storeKey, - bankKeeper: bankKeeper, - delayKeeper: delayKeeper, - wasmKeeper: wasmKeeper, - authority: authority, + cdc: cdc, + storeKey: storeKey, + bankKeeper: bankKeeper, + delayKeeper: delayKeeper, + wasmKeeper: wasmKeeper, + accountKeeper: accountKeeper, + authority: authority, } } @@ -538,7 +541,7 @@ func (k Keeper) Clawback(ctx sdk.Context, sender, addr sdk.AccAddress, coin sdk. return sdkerrors.Wrap(cosmoserrors.ErrInvalidCoins, "clawback amount should be positive") } - if err := k.clawbackChecks(ctx, sender, addr, coin); err != nil { + if err := k.validateClawbackAllowed(ctx, sender, addr, coin); err != nil { return err } @@ -949,7 +952,7 @@ func (k Keeper) isGloballyFrozen(ctx sdk.Context, denom string) bool { return bytes.Equal(ctx.KVStore(k.storeKey).Get(types.CreateGlobalFreezeKey(denom)), types.StoreTrue) } -func (k Keeper) clawbackChecks(ctx sdk.Context, sender, addr sdk.AccAddress, coin sdk.Coin) error { +func (k Keeper) validateClawbackAllowed(ctx sdk.Context, sender, addr sdk.AccAddress, coin sdk.Coin) error { def, err := k.GetDefinition(ctx, coin.Denom) if err != nil { return sdkerrors.Wrapf(err, "not able to get token info for denom:%s", coin.Denom) @@ -959,6 +962,10 @@ func (k Keeper) clawbackChecks(ctx sdk.Context, sender, addr sdk.AccAddress, coi return sdkerrors.Wrap(cosmoserrors.ErrUnauthorized, "issuer's balance can't be clawed back") } + if _, isModuleAccount := k.accountKeeper.GetAccount(ctx, addr).(*authtypes.ModuleAccount); isModuleAccount { + return sdkerrors.Wrap(cosmoserrors.ErrUnauthorized, "claw back from module accounts is prohibited") + } + return def.CheckFeatureAllowed(sender, types.Feature_clawback) } diff --git a/x/asset/ft/keeper/keeper_test.go b/x/asset/ft/keeper/keeper_test.go index a793fc5c0..2abe225af 100644 --- a/x/asset/ft/keeper/keeper_test.go +++ b/x/asset/ft/keeper/keeper_test.go @@ -1273,6 +1273,7 @@ func TestKeeper_Clawback(t *testing.T) { ftKeeper := testApp.AssetFTKeeper bankKeeper := testApp.BankKeeper + accountKeeper := testApp.AccountKeeper issuer := sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()) @@ -1283,7 +1284,10 @@ func TestKeeper_Clawback(t *testing.T) { Precision: 1, Description: "DEF Desc", InitialAmount: sdkmath.NewInt(666), - Features: []types.Feature{types.Feature_clawback}, + Features: []types.Feature{ + types.Feature_clawback, + types.Feature_freezing, + }, } denom, err := ftKeeper.Issue(ctx, settings) @@ -1327,7 +1331,12 @@ func TestKeeper_Clawback(t *testing.T) { // try to clawback from issuer address randomAddr = sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()) - err = ftKeeper.Clawback(ctx, randomAddr, from, sdk.NewCoin(denom, sdkmath.NewInt(10))) + err = ftKeeper.Clawback(ctx, randomAddr, issuer, 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, issuer, moduleAddr, sdk.NewCoin(denom, sdkmath.NewInt(10))) requireT.ErrorIs(err, cosmoserrors.ErrUnauthorized) // try to clawback 0 balance @@ -1347,6 +1356,14 @@ func TestKeeper_Clawback(t *testing.T) { accountBalanceAfter := bankKeeper.GetBalance(ctx, from, 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, issuer, from, sdk.NewCoin(denom, sdkmath.NewInt(60))) + requireT.NoError(err) + err = ftKeeper.Clawback(ctx, issuer, from, sdk.NewCoin(denom, sdkmath.NewInt(60))) + requireT.NoError(err) + accountBalance := bankKeeper.GetBalance(ctx, from, denom) + requireT.Equal(sdk.NewCoin(denom, sdkmath.NewInt(0)), accountBalance) } func TestKeeper_Whitelist(t *testing.T) { diff --git a/x/asset/ft/types/msgs.go b/x/asset/ft/types/msgs.go index 9c3663820..ac5fb4b71 100644 --- a/x/asset/ft/types/msgs.go +++ b/x/asset/ft/types/msgs.go @@ -443,19 +443,11 @@ func (m MsgClawback) ValidateBasic() error { return sdkerrors.Wrap(cosmoserrors.ErrInvalidAddress, "invalid account address") } - _, issuer, err := DeconstructDenom(m.Coin.Denom) + _, _, err := DeconstructDenom(m.Coin.Denom) if err != nil { return err } - if issuer.String() == m.Account { - return sdkerrors.Wrap(cosmoserrors.ErrUnauthorized, "issuer's balance can't be clawed back") - } - - if issuer.String() != m.Sender { - return sdkerrors.Wrap(cosmoserrors.ErrUnauthorized, "only issuer can claw back balance") - } - return m.Coin.Validate() } diff --git a/x/asset/ft/types/msgs_test.go b/x/asset/ft/types/msgs_test.go index 8d970dd38..99470c782 100644 --- a/x/asset/ft/types/msgs_test.go +++ b/x/asset/ft/types/msgs_test.go @@ -456,30 +456,6 @@ func TestMsgClawback_ValidateBasic(t *testing.T) { }, expectedError: cosmoserrors.ErrInvalidAddress, }, - { - name: "issuer clawback", - message: types.MsgClawback{ - Sender: "devcore172rc5sz2uclpsy3vvx3y79ah5dk450z5ruq2r5", - Account: "devcore172rc5sz2uclpsy3vvx3y79ah5dk450z5ruq2r5", - Coin: sdk.Coin{ - Denom: "abc-devcore172rc5sz2uclpsy3vvx3y79ah5dk450z5ruq2r5", - Amount: sdkmath.NewInt(100), - }, - }, - expectedError: cosmoserrors.ErrUnauthorized, - }, - { - name: "clawback by non-issuer", - message: types.MsgClawback{ - Sender: "devcore172rc5sz2uclpsy3vvx3y79ah5dk450z5ruq2r5", - Account: "devcore1k3mke3gyf9apyd8vxveutgp9h4j2e80e05yfuq", - Coin: sdk.Coin{ - Denom: "abc-devcore1yetewq7jcujq7cqdes4qxxaqakzthwu72xfsnh", - Amount: sdkmath.NewInt(100), - }, - }, - expectedError: cosmoserrors.ErrUnauthorized, - }, } for _, testCase := range testCases { diff --git a/x/deterministicgas/config.go b/x/deterministicgas/config.go index 378636afa..4351dd2ec 100644 --- a/x/deterministicgas/config.go +++ b/x/deterministicgas/config.go @@ -83,7 +83,7 @@ func DefaultConfig() Config { MsgToMsgURL(&assetfttypes.MsgSetFrozen{}): constantGasFunc(8_500), MsgToMsgURL(&assetfttypes.MsgGloballyFreeze{}): constantGasFunc(5_000), MsgToMsgURL(&assetfttypes.MsgGloballyUnfreeze{}): constantGasFunc(5_000), - MsgToMsgURL(&assetfttypes.MsgClawback{}): constantGasFunc(8_500), + MsgToMsgURL(&assetfttypes.MsgClawback{}): constantGasFunc(15_500), MsgToMsgURL(&assetfttypes.MsgSetWhitelistedLimit{}): constantGasFunc(9_000), // TODO(v4): Once we add a new token upgrade MsgUpgradeTokenV2 we should remove this one and re-estimate gas. MsgToMsgURL(&assetfttypes.MsgUpgradeTokenV1{}): constantGasFunc(25_000), diff --git a/x/deterministicgas/spec/README.md b/x/deterministicgas/spec/README.md index 592b5164b..1e5ca4471 100644 --- a/x/deterministicgas/spec/README.md +++ b/x/deterministicgas/spec/README.md @@ -80,7 +80,7 @@ TotalGas = 65000 + max(0, (21480 - 2 * 1000 + 2050 * 10)) + 2 * 70000 | `/cosmos.bank.v1beta1.MsgMultiSend` | [special case](#special-cases) | | `/cosmos.bank.v1beta1.MsgSend` | [special case](#special-cases) | | `/coreum.asset.ft.v1.MsgBurn` | 35000 | -| `/coreum.asset.ft.v1.MsgClawback` | 8500 | +| `/coreum.asset.ft.v1.MsgClawback` | 15500 | | `/coreum.asset.ft.v1.MsgFreeze` | 8500 | | `/coreum.asset.ft.v1.MsgGloballyFreeze` | 5000 | | `/coreum.asset.ft.v1.MsgGloballyUnfreeze` | 5000 | From 4ecce23c18d0ab782fb7cb0c04d758562605960a Mon Sep 17 00:00:00 2001 From: Masih Yeganeh Date: Thu, 18 Apr 2024 10:10:10 +0330 Subject: [PATCH 08/12] Fix linting --- integration-tests/modules/assetft_test.go | 4 ++-- x/asset/ft/keeper/keeper.go | 9 +++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/integration-tests/modules/assetft_test.go b/integration-tests/modules/assetft_test.go index 5e7daba0e..d0fd79762 100644 --- a/integration-tests/modules/assetft_test.go +++ b/integration-tests/modules/assetft_test.go @@ -2116,7 +2116,7 @@ func TestAssetFTClawback(t *testing.T) { clawbackMsg, ) requireT.Error(err) - assertT.ErrorIs(err, cosmoserrors.ErrUnauthorized) + requireT.ErrorIs(err, cosmoserrors.ErrUnauthorized) // clawback 400 tokens clawbackMsg = &assetfttypes.MsgClawback{ @@ -2161,7 +2161,7 @@ func TestAssetFTClawback(t *testing.T) { clawbackMsg, ) requireT.Error(err) - assertT.ErrorIs(err, cosmoserrors.ErrInsufficientFunds) + requireT.ErrorIs(err, cosmoserrors.ErrInsufficientFunds) } // TestAssetFTFreezeUnfreezable checks freeze functionality on unfreezable fungible tokens. diff --git a/x/asset/ft/keeper/keeper.go b/x/asset/ft/keeper/keeper.go index c3f1761e6..7c91d247f 100644 --- a/x/asset/ft/keeper/keeper.go +++ b/x/asset/ft/keeper/keeper.go @@ -6,10 +6,6 @@ import ( sdkerrors "cosmossdk.io/errors" sdkmath "cosmossdk.io/math" - "github.com/CoreumFoundation/coreum/v4/x/asset/ft/types" - "github.com/CoreumFoundation/coreum/v4/x/wasm" - cwasmtypes "github.com/CoreumFoundation/coreum/v4/x/wasm/types" - wibctransfertypes "github.com/CoreumFoundation/coreum/v4/x/wibctransfer/types" "github.com/cometbft/cometbft/libs/log" "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/store/prefix" @@ -20,6 +16,11 @@ import ( authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + + "github.com/CoreumFoundation/coreum/v4/x/asset/ft/types" + "github.com/CoreumFoundation/coreum/v4/x/wasm" + cwasmtypes "github.com/CoreumFoundation/coreum/v4/x/wasm/types" + wibctransfertypes "github.com/CoreumFoundation/coreum/v4/x/wibctransfer/types" ) // Keeper is the asset module keeper. From 84705806362902e296e14c09afe45453cdd7e15c Mon Sep 17 00:00:00 2001 From: Masih Yeganeh Date: Thu, 18 Apr 2024 10:41:17 +0330 Subject: [PATCH 09/12] Fix integration test --- integration-tests/modules/assetft_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration-tests/modules/assetft_test.go b/integration-tests/modules/assetft_test.go index d0fd79762..b53c525d8 100644 --- a/integration-tests/modules/assetft_test.go +++ b/integration-tests/modules/assetft_test.go @@ -2156,7 +2156,7 @@ func TestAssetFTClawback(t *testing.T) { } _, err = client.BroadcastTx( ctx, - chain.ClientContext.WithFromAddress(from), + chain.ClientContext.WithFromAddress(issuer), chain.TxFactory().WithGas(chain.GasLimitByMsgs(clawbackMsg)), clawbackMsg, ) From cd6474647fd9ab43babd759a2ef40ddf2c2582d9 Mon Sep 17 00:00:00 2001 From: Masih Yeganeh Date: Thu, 25 Apr 2024 17:04:46 +0330 Subject: [PATCH 10/12] Add more integration tests --- integration-tests/ibc/asset_ft_test.go | 99 ++++++++++++++++++++ integration-tests/modules/assetft_test.go | 104 ++++++++++++++++++++-- 2 files changed, 195 insertions(+), 8 deletions(-) diff --git a/integration-tests/ibc/asset_ft_test.go b/integration-tests/ibc/asset_ft_test.go index 7ec58b3f8..1f1cbe526 100644 --- a/integration-tests/ibc/asset_ft_test.go +++ b/integration-tests/ibc/asset_ft_test.go @@ -799,6 +799,105 @@ func TestIBCGlobalFreeze(t *testing.T) { requireT.NoError(coreumChain.AwaitForBalance(ctx, t, coreumRecipient, sendCoinBack)) } +func TestIBCAssetFTClawback(t *testing.T) { + t.Parallel() + + ctx, chains := integrationtests.NewChainsTestingContext(t) + requireT := require.New(t) + assertT := assert.New(t) + coreumChain := chains.Coreum + gaiaChain := chains.Gaia + + gaiaToCoreumChannelID := gaiaChain.AwaitForIBCChannelID( + ctx, t, ibctransfertypes.PortID, coreumChain.ChainSettings.ChainID, + ) + + coreumIssuer := coreumChain.GenAccount() + coreumSender := coreumChain.GenAccount() + gaiaRecipient := gaiaChain.GenAccount() + + gaiaChain.Faucet.FundAccounts(ctx, t, integration.FundedAccount{ + Address: gaiaRecipient, + Amount: gaiaChain.NewCoin(sdkmath.NewInt(1000000)), // coin for the fees + }) + + issueFee := coreumChain.QueryAssetFTParams(ctx, t).IssueFee.Amount + coreumChain.FundAccountWithOptions(ctx, t, coreumIssuer, integration.BalancesOptions{ + Messages: []sdk.Msg{ + &assetfttypes.MsgIssue{}, + &banktypes.MsgSend{}, + &assetfttypes.MsgClawback{}, + }, + Amount: issueFee, + }) + coreumChain.FundAccountWithOptions(ctx, t, coreumSender, integration.BalancesOptions{ + Messages: []sdk.Msg{ + &ibctransfertypes.MsgTransfer{}, + &ibctransfertypes.MsgTransfer{}, + }, + }) + + issueMsg := &assetfttypes.MsgIssue{ + Issuer: coreumIssuer.String(), + Symbol: "mysymbol", + Subunit: "mysubunit", + Precision: 8, + InitialAmount: sdkmath.NewInt(1_000_000), + Features: []assetfttypes.Feature{ + assetfttypes.Feature_block_smart_contracts, + assetfttypes.Feature_ibc, + assetfttypes.Feature_clawback, + }, + } + _, err := client.BroadcastTx( + ctx, + coreumChain.ClientContext.WithFromAddress(coreumIssuer), + coreumChain.TxFactory().WithGas(coreumChain.GasLimitByMsgs(issueMsg)), + issueMsg, + ) + require.NoError(t, err) + denom := assetfttypes.BuildDenom(issueMsg.Subunit, coreumIssuer) + + sendCoin := sdk.NewCoin(denom, sdkmath.NewInt(1000)) + halfCoin := sdk.NewCoin(denom, sdkmath.NewInt(500)) + msgSend := &banktypes.MsgSend{ + FromAddress: coreumIssuer.String(), + ToAddress: coreumSender.String(), + Amount: sdk.NewCoins(sendCoin), + } + _, err = client.BroadcastTx( + ctx, + coreumChain.ClientContext.WithFromAddress(coreumIssuer), + coreumChain.TxFactory().WithGas(coreumChain.GasLimitByMsgs(msgSend)), + msgSend, + ) + requireT.NoError(err) + + clawbackMsg := &assetfttypes.MsgClawback{ + Sender: coreumIssuer.String(), + Account: coreumSender.String(), + Coin: halfCoin, + } + _, err = client.BroadcastTx( + ctx, + coreumChain.ClientContext.WithFromAddress(coreumIssuer), + coreumChain.TxFactory().WithGas(coreumChain.GasLimitByMsgs(clawbackMsg)), + clawbackMsg, + ) + require.NoError(t, err) + + // send more than allowed, should fail + _, err = coreumChain.ExecuteIBCTransfer(ctx, t, coreumSender, sendCoin, gaiaChain.ChainContext, gaiaRecipient) + requireT.Error(err) + assertT.Contains(err.Error(), cosmoserrors.ErrInsufficientFunds.Error()) + + // send up to the limit, should succeed + ibcCoin := sdk.NewCoin(ConvertToIBCDenom(gaiaToCoreumChannelID, denom), halfCoin.Amount) + _, err = coreumChain.ExecuteIBCTransfer(ctx, t, coreumSender, halfCoin, gaiaChain.ChainContext, gaiaRecipient) + requireT.NoError(err) + requireT.NoError(gaiaChain.AwaitForBalance(ctx, t, gaiaRecipient, ibcCoin)) +} + func TestIBCAssetFTTimedOutTransfer(t *testing.T) { t.Parallel() diff --git a/integration-tests/modules/assetft_test.go b/integration-tests/modules/assetft_test.go index b53c525d8..b6d3451c3 100644 --- a/integration-tests/modules/assetft_test.go +++ b/integration-tests/modules/assetft_test.go @@ -2044,7 +2044,7 @@ func TestAssetFTClawback(t *testing.T) { bankClient := banktypes.NewQueryClient(clientCtx) issuer := chain.GenAccount() - from := chain.GenAccount() + account := chain.GenAccount() randomAddress := chain.GenAccount() chain.FundAccountWithOptions(ctx, t, issuer, integration.BalancesOptions{ Messages: []sdk.Msg{ @@ -2076,7 +2076,7 @@ func TestAssetFTClawback(t *testing.T) { msgSend := &banktypes.MsgSend{ FromAddress: issuer.String(), - ToAddress: from.String(), + ToAddress: account.String(), Amount: sdk.NewCoins( sdk.NewCoin(assetfttypes.BuildDenom(msg.Subunit, issuer), sdkmath.NewInt(1000)), ), @@ -2099,14 +2099,14 @@ func TestAssetFTClawback(t *testing.T) { denom := fungibleTokenIssuedEvts[0].Denom // query account balance before clawback - bankRes, err := bankClient.Balance(ctx, banktypes.NewQueryBalanceRequest(from, denom)) + 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-issuer signature to clawback msg clawbackMsg := &assetfttypes.MsgClawback{ Sender: randomAddress.String(), - Account: from.String(), + Account: account.String(), Coin: sdk.NewCoin(denom, sdkmath.NewInt(1000)), } _, err = client.BroadcastTx( @@ -2121,7 +2121,7 @@ func TestAssetFTClawback(t *testing.T) { // clawback 400 tokens clawbackMsg = &assetfttypes.MsgClawback{ Sender: issuer.String(), - Account: from.String(), + Account: account.String(), Coin: sdk.NewCoin(denom, sdkmath.NewInt(400)), } res, err = client.BroadcastTx( @@ -2136,13 +2136,13 @@ func TestAssetFTClawback(t *testing.T) { fungibleTokenClawbackEvts, err := event.FindTypedEvents[*assetfttypes.EventAmountClawedBack](res.Events) requireT.NoError(err) assertT.EqualValues(&assetfttypes.EventAmountClawedBack{ - Account: from.String(), + Account: account.String(), Denom: denom, Amount: sdkmath.NewInt(400), }, fungibleTokenClawbackEvts[0]) // query account balance after clawback - bankRes, err = bankClient.Balance(ctx, banktypes.NewQueryBalanceRequest(from, denom)) + bankRes, err = bankClient.Balance(ctx, banktypes.NewQueryBalanceRequest(account, denom)) requireT.NoError(err) requireT.EqualValues(sdk.NewCoin(denom, sdkmath.NewInt(600)).String(), bankRes.Balance.String()) @@ -2151,7 +2151,7 @@ func TestAssetFTClawback(t *testing.T) { clawbackMsg = &assetfttypes.MsgClawback{ Sender: issuer.String(), - Account: from.String(), + Account: account.String(), Coin: coinsToClawback, } _, err = client.BroadcastTx( @@ -2164,6 +2164,94 @@ func TestAssetFTClawback(t *testing.T) { requireT.ErrorIs(err, cosmoserrors.ErrInsufficientFunds) } +// TestAssetFTClawbackSmartContract verifies that this is possible to clawback token from smart contract +func TestAssetFTClawbackSmartContract(t *testing.T) { + t.Parallel() + + ctx, chain := integrationtests.NewCoreumTestingContext(t) + + issuer := chain.GenAccount() + + requireT := require.New(t) + chain.Faucet.FundAccounts(ctx, t, + integration.NewFundedAccount(issuer, chain.NewCoin(sdkmath.NewInt(5000000000))), + ) + + clientCtx := chain.ClientContext + + // Issue a fungible token which cannot be sent to the smart contract + issueMsg := &assetfttypes.MsgIssue{ + Issuer: issuer.String(), + Symbol: "ABC", + Subunit: "abc", + Precision: 6, + InitialAmount: sdkmath.NewInt(1000), + Description: "ABC Description", + Features: []assetfttypes.Feature{ + assetfttypes.Feature_clawback, + }, + BurnRate: sdk.ZeroDec(), + SendCommissionRate: sdk.ZeroDec(), + } + + _, err := client.BroadcastTx( + ctx, + chain.ClientContext.WithFromAddress(issuer), + chain.TxFactory().WithGas(chain.GasLimitByMsgs(issueMsg)), + issueMsg, + ) + + requireT.NoError(err) + denom := assetfttypes.BuildDenom(issueMsg.Subunit, issuer) + + initialPayload, err := json.Marshal(moduleswasm.SimpleState{ + Count: 1337, + }) + requireT.NoError(err) + + contractAddr, _, err := chain.Wasm.DeployAndInstantiateWASMContract( + ctx, + chain.TxFactory().WithSimulateAndExecute(true), + issuer, + moduleswasm.SimpleStateWASM, + integration.InstantiateConfig{ + AccessType: wasmtypes.AccessTypeUnspecified, + Payload: initialPayload, + Label: "simple_state", + }, + ) + requireT.NoError(err) + + // send coins to the smart contract + sendMsg := &banktypes.MsgSend{ + FromAddress: issuer.String(), + ToAddress: contractAddr, + Amount: sdk.NewCoins(sdk.NewInt64Coin(denom, 100)), + } + _, err = client.BroadcastTx( + ctx, + clientCtx.WithFromAddress(issuer), + chain.TxFactory().WithGas(chain.GasLimitByMsgs(sendMsg)), + sendMsg, + ) + requireT.NoError(err) + + // clawback the amount + clawbackMsg := &assetfttypes.MsgClawback{ + Sender: issuer.String(), + Account: contractAddr, + Coin: sdk.NewCoin(denom, sdkmath.NewInt(100)), + } + _, err = client.BroadcastTx( + ctx, + chain.ClientContext.WithFromAddress(issuer), + chain.TxFactory().WithGas(chain.GasLimitByMsgs(clawbackMsg)), + clawbackMsg, + ) + requireT.Error(err) + requireT.ErrorIs(err, cosmoserrors.ErrUnauthorized) +} + // TestAssetFTFreezeUnfreezable checks freeze functionality on unfreezable fungible tokens. func TestAssetFTFreezeUnfreezable(t *testing.T) { t.Parallel() From 0960ac03497737e18f53a3ffd25d75e9d383f8b3 Mon Sep 17 00:00:00 2001 From: Masih Yeganeh Date: Fri, 26 Apr 2024 11:11:33 +0330 Subject: [PATCH 11/12] Fix integration test --- integration-tests/modules/assetft_test.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/integration-tests/modules/assetft_test.go b/integration-tests/modules/assetft_test.go index b6d3451c3..5e4832159 100644 --- a/integration-tests/modules/assetft_test.go +++ b/integration-tests/modules/assetft_test.go @@ -2164,7 +2164,7 @@ func TestAssetFTClawback(t *testing.T) { requireT.ErrorIs(err, cosmoserrors.ErrInsufficientFunds) } -// TestAssetFTClawbackSmartContract verifies that this is possible to clawback token from smart contract +// TestAssetFTClawbackSmartContract verifies that this is possible to clawback token from smart contract. func TestAssetFTClawbackSmartContract(t *testing.T) { t.Parallel() @@ -2179,7 +2179,7 @@ func TestAssetFTClawbackSmartContract(t *testing.T) { clientCtx := chain.ClientContext - // Issue a fungible token which cannot be sent to the smart contract + // Issue a fungible token which can be sent to the smart contract issueMsg := &assetfttypes.MsgIssue{ Issuer: issuer.String(), Symbol: "ABC", @@ -2248,8 +2248,7 @@ func TestAssetFTClawbackSmartContract(t *testing.T) { chain.TxFactory().WithGas(chain.GasLimitByMsgs(clawbackMsg)), clawbackMsg, ) - requireT.Error(err) - requireT.ErrorIs(err, cosmoserrors.ErrUnauthorized) + requireT.NoError(err) } // TestAssetFTFreezeUnfreezable checks freeze functionality on unfreezable fungible tokens. From 5f2a4dd8c84f9f06fd6ec027f6b8a5232e964408 Mon Sep 17 00:00:00 2001 From: Masih Yeganeh Date: Fri, 26 Apr 2024 19:19:16 +0330 Subject: [PATCH 12/12] Add TODO comment --- x/asset/ft/keeper/keeper.go | 1 + 1 file changed, 1 insertion(+) diff --git a/x/asset/ft/keeper/keeper.go b/x/asset/ft/keeper/keeper.go index 7c91d247f..2db9e015c 100644 --- a/x/asset/ft/keeper/keeper.go +++ b/x/asset/ft/keeper/keeper.go @@ -959,6 +959,7 @@ func (k Keeper) validateClawbackAllowed(ctx sdk.Context, sender, addr sdk.AccAdd return sdkerrors.Wrapf(err, "not able to get token info for denom:%s", coin.Denom) } + // TODO: Make sure that it is going to work with the PR for separation of admin and issuer if def.IsIssuer(addr) { return sdkerrors.Wrap(cosmoserrors.ErrUnauthorized, "issuer's balance can't be clawed back") }