[bank]: add balance tracking events (#8656)
* change(bank): add utxo events and simplify logic * add(bank): balance and supply tracking test * chore(bank): fix balance tracking test comment * fix(grpc): service test * fix(bank): sub unlocked coins to use less gas * fix(auth): cli test gas * fix(rest): grpc gas test * fix(staking/cli): increase delegation required gas * add: burn events, fix tests * fix(auth/tx): grpc test * add(bank): coin events in delegate * fix(bank): add amt check in delegate coins back * change(bank): add coin spent and coin recv events in burn and mint * change(bank): revert sub coin function * change(auth): revert cli test * change(auth): revert service test * chore(auth): fix events comment in service_test.go * chore: update CHANGELOG.md * remove(bank): balanceError func * chore(bank): address lint warnings * chore(bank): update events spec Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
This commit is contained in:
parent
19e79e00d6
commit
ef9968debd
|
@ -58,6 +58,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
|
||||||
* (x/ibc) [\#8405](https://github.com/cosmos/cosmos-sdk/pull/8405) Refactor IBC client update governance proposals to use a substitute client to update a frozen or expired client.
|
* (x/ibc) [\#8405](https://github.com/cosmos/cosmos-sdk/pull/8405) Refactor IBC client update governance proposals to use a substitute client to update a frozen or expired client.
|
||||||
* (x/evidence) [\#8502](https://github.com/cosmos/cosmos-sdk/pull/8502) `HandleEquivocationEvidence` persists the evidence to state.
|
* (x/evidence) [\#8502](https://github.com/cosmos/cosmos-sdk/pull/8502) `HandleEquivocationEvidence` persists the evidence to state.
|
||||||
* (x/gov) [\#7733](https://github.com/cosmos/cosmos-sdk/pull/7733) ADR 037 Implementation: Governance Split Votes
|
* (x/gov) [\#7733](https://github.com/cosmos/cosmos-sdk/pull/7733) ADR 037 Implementation: Governance Split Votes
|
||||||
|
* (x/bank) [\#8656](https://github.com/cosmos/cosmos-sdk/pull/8656) balance and supply are now correctly tracked via `coin_spent`, `coin_received`, `coinbase` and `burn` events.
|
||||||
|
|
||||||
### Improvements
|
### Improvements
|
||||||
|
|
||||||
|
|
|
@ -1102,7 +1102,7 @@ func (s *IntegrationTestSuite) TestSignWithMultiSigners_AminoJSON() {
|
||||||
banktypes.NewMsgSend(val1.Address, addr1, sdk.NewCoins(val1Coin)),
|
banktypes.NewMsgSend(val1.Address, addr1, sdk.NewCoins(val1Coin)),
|
||||||
)
|
)
|
||||||
txBuilder.SetFeeAmount(sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))))
|
txBuilder.SetFeeAmount(sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))))
|
||||||
txBuilder.SetGasLimit(testdata.NewTestGasLimit())
|
txBuilder.SetGasLimit(testdata.NewTestGasLimit()) // min required is 101892
|
||||||
require.Equal([]sdk.AccAddress{val0.Address, val1.Address}, txBuilder.GetTx().GetSigners())
|
require.Equal([]sdk.AccAddress{val0.Address, val1.Address}, txBuilder.GetTx().GetSigners())
|
||||||
|
|
||||||
// Write the unsigned tx into a file.
|
// Write the unsigned tx into a file.
|
||||||
|
@ -1126,7 +1126,12 @@ func (s *IntegrationTestSuite) TestSignWithMultiSigners_AminoJSON() {
|
||||||
signedTxFile := testutil.WriteToNewTempFile(s.T(), signedTx.String())
|
signedTxFile := testutil.WriteToNewTempFile(s.T(), signedTx.String())
|
||||||
|
|
||||||
// Now let's try to send this tx.
|
// Now let's try to send this tx.
|
||||||
res, err := authtest.TxBroadcastExec(val0.ClientCtx, signedTxFile.Name(), fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock))
|
res, err := authtest.TxBroadcastExec(
|
||||||
|
val0.ClientCtx,
|
||||||
|
signedTxFile.Name(),
|
||||||
|
fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock),
|
||||||
|
)
|
||||||
|
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
var txRes sdk.TxResponse
|
var txRes sdk.TxResponse
|
||||||
require.NoError(val0.ClientCtx.JSONMarshaler.UnmarshalJSON(res.Bytes(), &txRes))
|
require.NoError(val0.ClientCtx.JSONMarshaler.UnmarshalJSON(res.Bytes(), &txRes))
|
||||||
|
|
|
@ -5,13 +5,14 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/testutil/testdata"
|
||||||
|
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/client"
|
"github.com/cosmos/cosmos-sdk/client"
|
||||||
"github.com/cosmos/cosmos-sdk/client/flags"
|
"github.com/cosmos/cosmos-sdk/client/flags"
|
||||||
clienttx "github.com/cosmos/cosmos-sdk/client/tx"
|
clienttx "github.com/cosmos/cosmos-sdk/client/tx"
|
||||||
"github.com/cosmos/cosmos-sdk/testutil/network"
|
"github.com/cosmos/cosmos-sdk/testutil/network"
|
||||||
"github.com/cosmos/cosmos-sdk/testutil/testdata"
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||||
query "github.com/cosmos/cosmos-sdk/types/query"
|
query "github.com/cosmos/cosmos-sdk/types/query"
|
||||||
|
@ -106,7 +107,7 @@ func (s IntegrationTestSuite) TestSimulateTx_GRPC() {
|
||||||
} else {
|
} else {
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
// Check the result and gas used are correct.
|
// Check the result and gas used are correct.
|
||||||
s.Require().Equal(len(res.GetResult().GetEvents()), 4) // 1 transfer, 3 messages.
|
s.Require().Equal(len(res.GetResult().GetEvents()), 6) // 1 coin recv 1 coin spent, 1 transfer, 3 messages.
|
||||||
s.Require().True(res.GetGasInfo().GetGasUsed() > 0) // Gas used sometimes change, just check it's not empty.
|
s.Require().True(res.GetGasInfo().GetGasUsed() > 0) // Gas used sometimes change, just check it's not empty.
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -143,7 +144,7 @@ func (s IntegrationTestSuite) TestSimulateTx_GRPCGateway() {
|
||||||
err = val.ClientCtx.JSONMarshaler.UnmarshalJSON(res, &result)
|
err = val.ClientCtx.JSONMarshaler.UnmarshalJSON(res, &result)
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
// Check the result and gas used are correct.
|
// Check the result and gas used are correct.
|
||||||
s.Require().Equal(len(result.GetResult().GetEvents()), 4) // 1 transfer, 3 messages.
|
s.Require().Equal(len(result.GetResult().GetEvents()), 6) // 1 coin recv, 1 coin spent,1 transfer, 3 messages.
|
||||||
s.Require().True(result.GetGasInfo().GetGasUsed() > 0) // Gas used sometimes change, just check it's not empty.
|
s.Require().True(result.GetGasInfo().GetGasUsed() > 0) // Gas used sometimes change, just check it's not empty.
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -412,7 +413,7 @@ func (s IntegrationTestSuite) TestBroadcastTx_GRPCGateway() {
|
||||||
var result tx.BroadcastTxResponse
|
var result tx.BroadcastTxResponse
|
||||||
err = val.ClientCtx.JSONMarshaler.UnmarshalJSON(res, &result)
|
err = val.ClientCtx.JSONMarshaler.UnmarshalJSON(res, &result)
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
s.Require().Equal(uint32(0), result.TxResponse.Code)
|
s.Require().Equal(uint32(0), result.TxResponse.Code, "rawlog", result.TxResponse.RawLog)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,8 +25,6 @@ func (suite *IntegrationTestSuite) TestExportGenesis() {
|
||||||
Require().
|
Require().
|
||||||
NoError(app.BankKeeper.SendCoinsFromModuleToAccount(ctx, minttypes.ModuleName, accAddr, expectedBalances[i].Coins))
|
NoError(app.BankKeeper.SendCoinsFromModuleToAccount(ctx, minttypes.ModuleName, accAddr, expectedBalances[i].Coins))
|
||||||
}
|
}
|
||||||
// add mint module balance as nil
|
|
||||||
expectedBalances = append(expectedBalances, types.Balance{Address: "cosmos1m3h30wlvsf8llruxtpukdvsy0km2kum8g38c8q", Coins: nil})
|
|
||||||
app.BankKeeper.SetParams(ctx, types.DefaultParams())
|
app.BankKeeper.SetParams(ctx, types.DefaultParams())
|
||||||
|
|
||||||
exportGenesis := app.BankKeeper.ExportGenesis(ctx)
|
exportGenesis := app.BankKeeper.ExportGenesis(ctx)
|
||||||
|
@ -34,6 +32,8 @@ func (suite *IntegrationTestSuite) TestExportGenesis() {
|
||||||
suite.Require().Len(exportGenesis.Params.SendEnabled, 0)
|
suite.Require().Len(exportGenesis.Params.SendEnabled, 0)
|
||||||
suite.Require().Equal(types.DefaultParams().DefaultSendEnabled, exportGenesis.Params.DefaultSendEnabled)
|
suite.Require().Equal(types.DefaultParams().DefaultSendEnabled, exportGenesis.Params.DefaultSendEnabled)
|
||||||
suite.Require().Equal(totalSupply.Total, exportGenesis.Supply)
|
suite.Require().Equal(totalSupply.Total, exportGenesis.Supply)
|
||||||
|
// add mint module balance as nil
|
||||||
|
expectedBalances = append(expectedBalances, types.Balance{Address: "cosmos1m3h30wlvsf8llruxtpukdvsy0km2kum8g38c8q", Coins: nil})
|
||||||
suite.Require().Equal(expectedBalances, exportGenesis.Balances)
|
suite.Require().Equal(expectedBalances, exportGenesis.Balances)
|
||||||
suite.Require().Equal(expectedMetadata, exportGenesis.DenomMetadata)
|
suite.Require().Equal(expectedMetadata, exportGenesis.DenomMetadata)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
package keeper
|
package keeper
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/codec"
|
"github.com/cosmos/cosmos-sdk/codec"
|
||||||
"github.com/cosmos/cosmos-sdk/store/prefix"
|
"github.com/cosmos/cosmos-sdk/store/prefix"
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
@ -107,9 +105,13 @@ func (k BaseKeeper) DelegateCoins(ctx sdk.Context, delegatorAddr, moduleAccAddr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := k.trackDelegation(ctx, delegatorAddr, ctx.BlockHeader().Time, balances, amt); err != nil {
|
if err := k.trackDelegation(ctx, delegatorAddr, balances, amt); err != nil {
|
||||||
return sdkerrors.Wrap(err, "failed to track delegation")
|
return sdkerrors.Wrap(err, "failed to track delegation")
|
||||||
}
|
}
|
||||||
|
// emit coin spent event
|
||||||
|
ctx.EventManager().EmitEvent(
|
||||||
|
types.NewCoinSpentEvent(delegatorAddr, amt),
|
||||||
|
)
|
||||||
|
|
||||||
err := k.addCoins(ctx, moduleAccAddr, amt)
|
err := k.addCoins(ctx, moduleAccAddr, amt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -134,7 +136,7 @@ func (k BaseKeeper) UndelegateCoins(ctx sdk.Context, moduleAccAddr, delegatorAdd
|
||||||
return sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, amt.String())
|
return sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, amt.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
err := k.subtractCoins(ctx, moduleAccAddr, amt)
|
err := k.subUnlockedCoins(ctx, moduleAccAddr, amt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -345,6 +347,11 @@ func (k BaseKeeper) MintCoins(ctx sdk.Context, moduleName string, amt sdk.Coins)
|
||||||
logger := k.Logger(ctx)
|
logger := k.Logger(ctx)
|
||||||
logger.Info("minted coins from module account", "amount", amt.String(), "from", moduleName)
|
logger.Info("minted coins from module account", "amount", amt.String(), "from", moduleName)
|
||||||
|
|
||||||
|
// emit mint event
|
||||||
|
ctx.EventManager().EmitEvent(
|
||||||
|
types.NewCoinMintEvent(acc.GetAddress(), amt),
|
||||||
|
)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -360,7 +367,7 @@ func (k BaseKeeper) BurnCoins(ctx sdk.Context, moduleName string, amt sdk.Coins)
|
||||||
panic(sdkerrors.Wrapf(sdkerrors.ErrUnauthorized, "module account %s does not have permissions to burn tokens", moduleName))
|
panic(sdkerrors.Wrapf(sdkerrors.ErrUnauthorized, "module account %s does not have permissions to burn tokens", moduleName))
|
||||||
}
|
}
|
||||||
|
|
||||||
err := k.subtractCoins(ctx, acc.GetAddress(), amt)
|
err := k.subUnlockedCoins(ctx, acc.GetAddress(), amt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -373,10 +380,15 @@ func (k BaseKeeper) BurnCoins(ctx sdk.Context, moduleName string, amt sdk.Coins)
|
||||||
logger := k.Logger(ctx)
|
logger := k.Logger(ctx)
|
||||||
logger.Info("burned tokens from module account", "amount", amt.String(), "from", moduleName)
|
logger.Info("burned tokens from module account", "amount", amt.String(), "from", moduleName)
|
||||||
|
|
||||||
|
// emit burn event
|
||||||
|
ctx.EventManager().EmitEvent(
|
||||||
|
types.NewCoinBurnEvent(acc.GetAddress(), amt),
|
||||||
|
)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k BaseKeeper) trackDelegation(ctx sdk.Context, addr sdk.AccAddress, blockTime time.Time, balance, amt sdk.Coins) error {
|
func (k BaseKeeper) trackDelegation(ctx sdk.Context, addr sdk.AccAddress, balance, amt sdk.Coins) error {
|
||||||
acc := k.ak.GetAccount(ctx, addr)
|
acc := k.ak.GetAccount(ctx, addr)
|
||||||
if acc == nil {
|
if acc == nil {
|
||||||
return sdkerrors.Wrapf(sdkerrors.ErrUnknownAddress, "account %s does not exist", addr)
|
return sdkerrors.Wrapf(sdkerrors.ErrUnknownAddress, "account %s does not exist", addr)
|
||||||
|
@ -385,7 +397,7 @@ func (k BaseKeeper) trackDelegation(ctx sdk.Context, addr sdk.AccAddress, blockT
|
||||||
vacc, ok := acc.(vestexported.VestingAccount)
|
vacc, ok := acc.(vestexported.VestingAccount)
|
||||||
if ok {
|
if ok {
|
||||||
// TODO: return error on account.TrackDelegation
|
// TODO: return error on account.TrackDelegation
|
||||||
vacc.TrackDelegation(blockTime, balance, amt)
|
vacc.TrackDelegation(ctx.BlockHeader().Time, balance, amt)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -153,17 +153,17 @@ func (suite *IntegrationTestSuite) TestSupply_SendCoins() {
|
||||||
suite.Require().NoError(
|
suite.Require().NoError(
|
||||||
keeper.SendCoinsFromModuleToModule(ctx, holderAcc.GetName(), authtypes.Burner, initCoins),
|
keeper.SendCoinsFromModuleToModule(ctx, holderAcc.GetName(), authtypes.Burner, initCoins),
|
||||||
)
|
)
|
||||||
suite.Require().Equal(sdk.Coins(nil), getCoinsByName(ctx, keeper, authKeeper, holderAcc.GetName()))
|
suite.Require().Equal(sdk.NewCoins().String(), getCoinsByName(ctx, keeper, authKeeper, holderAcc.GetName()).String())
|
||||||
suite.Require().Equal(initCoins, getCoinsByName(ctx, keeper, authKeeper, authtypes.Burner))
|
suite.Require().Equal(initCoins, getCoinsByName(ctx, keeper, authKeeper, authtypes.Burner))
|
||||||
|
|
||||||
suite.Require().NoError(
|
suite.Require().NoError(
|
||||||
keeper.SendCoinsFromModuleToAccount(ctx, authtypes.Burner, baseAcc.GetAddress(), initCoins),
|
keeper.SendCoinsFromModuleToAccount(ctx, authtypes.Burner, baseAcc.GetAddress(), initCoins),
|
||||||
)
|
)
|
||||||
suite.Require().Equal(sdk.Coins(nil), getCoinsByName(ctx, keeper, authKeeper, authtypes.Burner))
|
suite.Require().Equal(sdk.NewCoins().String(), getCoinsByName(ctx, keeper, authKeeper, authtypes.Burner).String())
|
||||||
suite.Require().Equal(initCoins, keeper.GetAllBalances(ctx, baseAcc.GetAddress()))
|
suite.Require().Equal(initCoins, keeper.GetAllBalances(ctx, baseAcc.GetAddress()))
|
||||||
|
|
||||||
suite.Require().NoError(keeper.SendCoinsFromAccountToModule(ctx, baseAcc.GetAddress(), authtypes.Burner, initCoins))
|
suite.Require().NoError(keeper.SendCoinsFromAccountToModule(ctx, baseAcc.GetAddress(), authtypes.Burner, initCoins))
|
||||||
suite.Require().Equal(sdk.Coins(nil), keeper.GetAllBalances(ctx, baseAcc.GetAddress()))
|
suite.Require().Equal(sdk.NewCoins().String(), keeper.GetAllBalances(ctx, baseAcc.GetAddress()).String())
|
||||||
suite.Require().Equal(initCoins, getCoinsByName(ctx, keeper, authKeeper, authtypes.Burner))
|
suite.Require().Equal(initCoins, getCoinsByName(ctx, keeper, authKeeper, authtypes.Burner))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -266,7 +266,7 @@ func (suite *IntegrationTestSuite) TestSupply_BurnCoins() {
|
||||||
|
|
||||||
err = keeper.BurnCoins(ctx, authtypes.Burner, initCoins)
|
err = keeper.BurnCoins(ctx, authtypes.Burner, initCoins)
|
||||||
suite.Require().NoError(err)
|
suite.Require().NoError(err)
|
||||||
suite.Require().Equal(sdk.Coins(nil), getCoinsByName(ctx, keeper, authKeeper, authtypes.Burner))
|
suite.Require().Equal(sdk.NewCoins().String(), getCoinsByName(ctx, keeper, authKeeper, authtypes.Burner).String())
|
||||||
suite.Require().Equal(supplyAfterInflation.GetTotal().Sub(initCoins), keeper.GetSupply(ctx).GetTotal())
|
suite.Require().Equal(supplyAfterInflation.GetTotal().Sub(initCoins), keeper.GetSupply(ctx).GetTotal())
|
||||||
|
|
||||||
// test same functionality on module account with multiple permissions
|
// test same functionality on module account with multiple permissions
|
||||||
|
@ -280,7 +280,7 @@ func (suite *IntegrationTestSuite) TestSupply_BurnCoins() {
|
||||||
|
|
||||||
err = keeper.BurnCoins(ctx, multiPermAcc.GetName(), initCoins)
|
err = keeper.BurnCoins(ctx, multiPermAcc.GetName(), initCoins)
|
||||||
suite.Require().NoError(err)
|
suite.Require().NoError(err)
|
||||||
suite.Require().Equal(sdk.Coins(nil), getCoinsByName(ctx, keeper, authKeeper, multiPermAcc.GetName()))
|
suite.Require().Equal(sdk.NewCoins().String(), getCoinsByName(ctx, keeper, authKeeper, multiPermAcc.GetName()).String())
|
||||||
suite.Require().Equal(supplyAfterInflation.GetTotal().Sub(initCoins), keeper.GetSupply(ctx).GetTotal())
|
suite.Require().Equal(supplyAfterInflation.GetTotal().Sub(initCoins), keeper.GetSupply(ctx).GetTotal())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -537,6 +537,7 @@ func (suite *IntegrationTestSuite) TestMsgSendEvents() {
|
||||||
event1.Attributes,
|
event1.Attributes,
|
||||||
abci.EventAttribute{Key: []byte(sdk.AttributeKeyAmount), Value: []byte(newCoins.String())},
|
abci.EventAttribute{Key: []byte(sdk.AttributeKeyAmount), Value: []byte(newCoins.String())},
|
||||||
)
|
)
|
||||||
|
|
||||||
event2 := sdk.Event{
|
event2 := sdk.Event{
|
||||||
Type: sdk.EventTypeMessage,
|
Type: sdk.EventTypeMessage,
|
||||||
Attributes: []abci.EventAttribute{},
|
Attributes: []abci.EventAttribute{},
|
||||||
|
@ -556,9 +557,9 @@ func (suite *IntegrationTestSuite) TestMsgSendEvents() {
|
||||||
|
|
||||||
// events are shifted due to the funding account events
|
// events are shifted due to the funding account events
|
||||||
events = ctx.EventManager().ABCIEvents()
|
events = ctx.EventManager().ABCIEvents()
|
||||||
suite.Require().Equal(6, len(events))
|
suite.Require().Equal(12, len(events))
|
||||||
suite.Require().Equal(abci.Event(event1), events[4])
|
suite.Require().Equal(abci.Event(event1), events[8])
|
||||||
suite.Require().Equal(abci.Event(event2), events[5])
|
suite.Require().Equal(abci.Event(event2), events[9])
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *IntegrationTestSuite) TestMsgMultiSendEvents() {
|
func (suite *IntegrationTestSuite) TestMsgMultiSendEvents() {
|
||||||
|
@ -597,7 +598,7 @@ func (suite *IntegrationTestSuite) TestMsgMultiSendEvents() {
|
||||||
suite.Require().Error(app.BankKeeper.InputOutputCoins(ctx, inputs, outputs))
|
suite.Require().Error(app.BankKeeper.InputOutputCoins(ctx, inputs, outputs))
|
||||||
|
|
||||||
events = ctx.EventManager().ABCIEvents()
|
events = ctx.EventManager().ABCIEvents()
|
||||||
suite.Require().Equal(3, len(events)) // 3 events because minting event is there
|
suite.Require().Equal(8, len(events)) // 7 events because account funding causes extra minting + coin_spent + coin_recv events
|
||||||
|
|
||||||
event1 := sdk.Event{
|
event1 := sdk.Event{
|
||||||
Type: sdk.EventTypeMessage,
|
Type: sdk.EventTypeMessage,
|
||||||
|
@ -607,7 +608,7 @@ func (suite *IntegrationTestSuite) TestMsgMultiSendEvents() {
|
||||||
event1.Attributes,
|
event1.Attributes,
|
||||||
abci.EventAttribute{Key: []byte(types.AttributeKeySender), Value: []byte(addr.String())},
|
abci.EventAttribute{Key: []byte(types.AttributeKeySender), Value: []byte(addr.String())},
|
||||||
)
|
)
|
||||||
suite.Require().Equal(abci.Event(event1), events[2]) // it's the third event since we have the minting event before
|
suite.Require().Equal(abci.Event(event1), events[7])
|
||||||
|
|
||||||
// Set addr's coins and addr2's coins
|
// Set addr's coins and addr2's coins
|
||||||
suite.Require().NoError(simapp.FundAccount(app, ctx, addr, sdk.NewCoins(sdk.NewInt64Coin(fooDenom, 50))))
|
suite.Require().NoError(simapp.FundAccount(app, ctx, addr, sdk.NewCoins(sdk.NewInt64Coin(fooDenom, 50))))
|
||||||
|
@ -619,7 +620,7 @@ func (suite *IntegrationTestSuite) TestMsgMultiSendEvents() {
|
||||||
suite.Require().NoError(app.BankKeeper.InputOutputCoins(ctx, inputs, outputs))
|
suite.Require().NoError(app.BankKeeper.InputOutputCoins(ctx, inputs, outputs))
|
||||||
|
|
||||||
events = ctx.EventManager().ABCIEvents()
|
events = ctx.EventManager().ABCIEvents()
|
||||||
suite.Require().Equal(11, len(events))
|
suite.Require().Equal(28, len(events)) // 25 due to account funding + coin_spent + coin_recv events
|
||||||
|
|
||||||
event2 := sdk.Event{
|
event2 := sdk.Event{
|
||||||
Type: sdk.EventTypeMessage,
|
Type: sdk.EventTypeMessage,
|
||||||
|
@ -652,12 +653,11 @@ func (suite *IntegrationTestSuite) TestMsgMultiSendEvents() {
|
||||||
event4.Attributes,
|
event4.Attributes,
|
||||||
abci.EventAttribute{Key: []byte(sdk.AttributeKeyAmount), Value: []byte(newCoins2.String())},
|
abci.EventAttribute{Key: []byte(sdk.AttributeKeyAmount), Value: []byte(newCoins2.String())},
|
||||||
)
|
)
|
||||||
|
|
||||||
// events are shifted due to the funding account events
|
// events are shifted due to the funding account events
|
||||||
suite.Require().Equal(abci.Event(event1), events[7])
|
suite.Require().Equal(abci.Event(event1), events[21])
|
||||||
suite.Require().Equal(abci.Event(event2), events[8])
|
suite.Require().Equal(abci.Event(event2), events[23])
|
||||||
suite.Require().Equal(abci.Event(event3), events[9])
|
suite.Require().Equal(abci.Event(event3), events[25])
|
||||||
suite.Require().Equal(abci.Event(event4), events[10])
|
suite.Require().Equal(abci.Event(event4), events[27])
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *IntegrationTestSuite) TestSpendableCoins() {
|
func (suite *IntegrationTestSuite) TestSpendableCoins() {
|
||||||
|
@ -1002,6 +1002,103 @@ func (suite *IntegrationTestSuite) TestIterateAllDenomMetaData() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (suite *IntegrationTestSuite) TestBalanceTrackingEvents() {
|
||||||
|
// replace account keeper and bank keeper otherwise the account keeper won't be aware of the
|
||||||
|
// existence of the new module account because GetModuleAccount checks for the existence via
|
||||||
|
// permissions map and not via state... weird
|
||||||
|
maccPerms := simapp.GetMaccPerms()
|
||||||
|
maccPerms[multiPerm] = []string{authtypes.Burner, authtypes.Minter, authtypes.Staking}
|
||||||
|
|
||||||
|
suite.app.AccountKeeper = authkeeper.NewAccountKeeper(
|
||||||
|
suite.app.AppCodec(), suite.app.GetKey(authtypes.StoreKey), suite.app.GetSubspace(authtypes.ModuleName),
|
||||||
|
authtypes.ProtoBaseAccount, maccPerms,
|
||||||
|
)
|
||||||
|
|
||||||
|
suite.app.BankKeeper = keeper.NewBaseKeeper(suite.app.AppCodec(), suite.app.GetKey(types.StoreKey),
|
||||||
|
suite.app.AccountKeeper, suite.app.GetSubspace(types.ModuleName), nil)
|
||||||
|
|
||||||
|
// set account with multiple permissions
|
||||||
|
suite.app.AccountKeeper.SetModuleAccount(suite.ctx, multiPermAcc)
|
||||||
|
// mint coins
|
||||||
|
suite.Require().NoError(
|
||||||
|
suite.app.BankKeeper.MintCoins(
|
||||||
|
suite.ctx,
|
||||||
|
multiPermAcc.Name,
|
||||||
|
sdk.NewCoins(sdk.NewCoin("utxo", sdk.NewInt(100000)))),
|
||||||
|
)
|
||||||
|
// send coins to address
|
||||||
|
addr1 := sdk.AccAddress("addr1_______________")
|
||||||
|
suite.Require().NoError(
|
||||||
|
suite.app.BankKeeper.SendCoinsFromModuleToAccount(
|
||||||
|
suite.ctx,
|
||||||
|
multiPermAcc.Name,
|
||||||
|
addr1,
|
||||||
|
sdk.NewCoins(sdk.NewCoin("utxo", sdk.NewInt(50000))),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
// burn coins from module account
|
||||||
|
suite.Require().NoError(
|
||||||
|
suite.app.BankKeeper.BurnCoins(
|
||||||
|
suite.ctx,
|
||||||
|
multiPermAcc.Name,
|
||||||
|
sdk.NewCoins(sdk.NewInt64Coin("utxo", 1000)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
// process balances and supply from events
|
||||||
|
supply := sdk.NewCoins()
|
||||||
|
|
||||||
|
balances := make(map[string]sdk.Coins)
|
||||||
|
|
||||||
|
for _, e := range suite.ctx.EventManager().ABCIEvents() {
|
||||||
|
switch e.Type {
|
||||||
|
case types.EventTypeCoinBurn:
|
||||||
|
burnedCoins, err := sdk.ParseCoinsNormalized((string)(e.Attributes[1].Value))
|
||||||
|
suite.Require().NoError(err)
|
||||||
|
supply = supply.Sub(burnedCoins)
|
||||||
|
|
||||||
|
case types.EventTypeCoinMint:
|
||||||
|
mintedCoins, err := sdk.ParseCoinsNormalized((string)(e.Attributes[1].Value))
|
||||||
|
suite.Require().NoError(err)
|
||||||
|
supply = supply.Add(mintedCoins...)
|
||||||
|
|
||||||
|
case types.EventTypeCoinSpent:
|
||||||
|
coinsSpent, err := sdk.ParseCoinsNormalized((string)(e.Attributes[1].Value))
|
||||||
|
suite.Require().NoError(err)
|
||||||
|
spender, err := sdk.AccAddressFromBech32((string)(e.Attributes[0].Value))
|
||||||
|
suite.Require().NoError(err)
|
||||||
|
balances[spender.String()] = balances[spender.String()].Sub(coinsSpent)
|
||||||
|
|
||||||
|
case types.EventTypeCoinReceived:
|
||||||
|
coinsRecv, err := sdk.ParseCoinsNormalized((string)(e.Attributes[1].Value))
|
||||||
|
suite.Require().NoError(err)
|
||||||
|
receiver, err := sdk.AccAddressFromBech32((string)(e.Attributes[0].Value))
|
||||||
|
suite.Require().NoError(err)
|
||||||
|
balances[receiver.String()] = balances[receiver.String()].Add(coinsRecv...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check balance and supply tracking
|
||||||
|
savedSupply := suite.app.BankKeeper.GetSupply(suite.ctx)
|
||||||
|
utxoSupply := savedSupply.GetTotal().AmountOf("utxo")
|
||||||
|
suite.Require().Equal(utxoSupply, supply.AmountOf("utxo"))
|
||||||
|
// iterate accounts and check balances
|
||||||
|
suite.app.BankKeeper.IterateAllBalances(suite.ctx, func(address sdk.AccAddress, coin sdk.Coin) (stop bool) {
|
||||||
|
// if it's not utxo coin then skip
|
||||||
|
if coin.Denom != "utxo" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
balance, exists := balances[address.String()]
|
||||||
|
suite.Require().True(exists)
|
||||||
|
|
||||||
|
expectedUtxo := sdk.NewCoin("utxo", balance.AmountOf(coin.Denom))
|
||||||
|
suite.Require().Equal(expectedUtxo.String(), coin.String())
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func (suite *IntegrationTestSuite) getTestMetadata() []types.Metadata {
|
func (suite *IntegrationTestSuite) getTestMetadata() []types.Metadata {
|
||||||
return []types.Metadata{{
|
return []types.Metadata{{
|
||||||
Name: "Cosmos Hub Atom",
|
Name: "Cosmos Hub Atom",
|
||||||
|
|
|
@ -83,7 +83,7 @@ func (k BaseSendKeeper) InputOutputCoins(ctx sdk.Context, inputs []types.Input,
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = k.subtractCoins(ctx, inAddress, in.Coins)
|
err = k.subUnlockedCoins(ctx, inAddress, in.Coins)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -144,7 +144,7 @@ func (k BaseSendKeeper) SendCoins(ctx sdk.Context, fromAddr sdk.AccAddress, toAd
|
||||||
),
|
),
|
||||||
})
|
})
|
||||||
|
|
||||||
err := k.subtractCoins(ctx, fromAddr, amt)
|
err := k.subUnlockedCoins(ctx, fromAddr, amt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -167,9 +167,10 @@ func (k BaseSendKeeper) SendCoins(ctx sdk.Context, fromAddr sdk.AccAddress, toAd
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// subtractCoins removes amt coins the account by the given address. An error is
|
// subUnlockedCoins removes the unlocked amt coins of the given account. An error is
|
||||||
// returned if the resulting balance is negative or the initial amount is invalid.
|
// returned if the resulting balance is negative or the initial amount is invalid.
|
||||||
func (k BaseSendKeeper) subtractCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) error {
|
// A coin_spent event is emitted after.
|
||||||
|
func (k BaseSendKeeper) subUnlockedCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) error {
|
||||||
if !amt.IsValid() {
|
if !amt.IsValid() {
|
||||||
return sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, amt.String())
|
return sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, amt.String())
|
||||||
}
|
}
|
||||||
|
@ -194,12 +195,15 @@ func (k BaseSendKeeper) subtractCoins(ctx sdk.Context, addr sdk.AccAddress, amt
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// emit coin spent event
|
||||||
|
ctx.EventManager().EmitEvent(
|
||||||
|
types.NewCoinSpentEvent(addr, amt),
|
||||||
|
)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// addCoins adds amt to the account balance given by the provided address. An
|
// addCoins increase the addr balance by the given amt. Fails if the provided amt is invalid.
|
||||||
// error is returned if the initial amount is invalid or if any resulting new
|
// It emits a coin received event.
|
||||||
// balance is negative.
|
|
||||||
func (k BaseSendKeeper) addCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) error {
|
func (k BaseSendKeeper) addCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) error {
|
||||||
if !amt.IsValid() {
|
if !amt.IsValid() {
|
||||||
return sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, amt.String())
|
return sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, amt.String())
|
||||||
|
@ -215,11 +219,16 @@ func (k BaseSendKeeper) addCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.C
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// emit coin received event
|
||||||
|
ctx.EventManager().EmitEvent(
|
||||||
|
types.NewCoinReceivedEvent(addr, amt),
|
||||||
|
)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ClearBalances removes all balances for a given account by address.
|
// clearBalances removes all balances for a given account by address.
|
||||||
func (k BaseSendKeeper) ClearBalances(ctx sdk.Context, addr sdk.AccAddress) {
|
func (k BaseSendKeeper) clearBalances(ctx sdk.Context, addr sdk.AccAddress) {
|
||||||
keys := [][]byte{}
|
keys := [][]byte{}
|
||||||
k.IterateAccountBalances(ctx, addr, func(balance sdk.Coin) bool {
|
k.IterateAccountBalances(ctx, addr, func(balance sdk.Coin) bool {
|
||||||
keys = append(keys, []byte(balance.Denom))
|
keys = append(keys, []byte(balance.Denom))
|
||||||
|
@ -237,7 +246,7 @@ func (k BaseSendKeeper) ClearBalances(ctx sdk.Context, addr sdk.AccAddress) {
|
||||||
// clear out all balances prior to setting the new coins as to set existing balances
|
// clear out all balances prior to setting the new coins as to set existing balances
|
||||||
// to zero if they don't exist in amt. An error is returned upon failure.
|
// to zero if they don't exist in amt. An error is returned upon failure.
|
||||||
func (k BaseSendKeeper) setBalances(ctx sdk.Context, addr sdk.AccAddress, balances sdk.Coins) error {
|
func (k BaseSendKeeper) setBalances(ctx sdk.Context, addr sdk.AccAddress, balances sdk.Coins) error {
|
||||||
k.ClearBalances(ctx, addr)
|
k.clearBalances(ctx, addr)
|
||||||
|
|
||||||
for _, balance := range balances {
|
for _, balance := range balances {
|
||||||
err := k.setBalance(ctx, addr, balance)
|
err := k.setBalance(ctx, addr, balance)
|
||||||
|
|
|
@ -171,15 +171,23 @@ func (k BaseViewKeeper) LockedCoins(ctx sdk.Context, addr sdk.AccAddress) sdk.Co
|
||||||
// by address. If the account has no spendable coins, an empty Coins slice is
|
// by address. If the account has no spendable coins, an empty Coins slice is
|
||||||
// returned.
|
// returned.
|
||||||
func (k BaseViewKeeper) SpendableCoins(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins {
|
func (k BaseViewKeeper) SpendableCoins(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins {
|
||||||
balances := k.GetAllBalances(ctx, addr)
|
spendable, _ := k.spendableCoins(ctx, addr)
|
||||||
|
return spendable
|
||||||
|
}
|
||||||
|
|
||||||
|
// spendableCoins returns the coins the given address can spend alongside the total amount of coins it holds.
|
||||||
|
// It exists for gas efficiency, in order to avoid to have to get balance multiple times.
|
||||||
|
func (k BaseViewKeeper) spendableCoins(ctx sdk.Context, addr sdk.AccAddress) (spendable, total sdk.Coins) {
|
||||||
|
total = k.GetAllBalances(ctx, addr)
|
||||||
locked := k.LockedCoins(ctx, addr)
|
locked := k.LockedCoins(ctx, addr)
|
||||||
|
|
||||||
spendable, hasNeg := balances.SafeSub(locked)
|
spendable, hasNeg := total.SafeSub(locked)
|
||||||
if hasNeg {
|
if hasNeg {
|
||||||
return sdk.NewCoins()
|
spendable = sdk.NewCoins()
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
return spendable
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// ValidateBalance validates all balances for a given account address returning
|
// ValidateBalance validates all balances for a given account address returning
|
||||||
|
|
|
@ -27,3 +27,123 @@ The bank module emits the following events:
|
||||||
| message | module | bank |
|
| message | module | bank |
|
||||||
| message | action | multisend |
|
| message | action | multisend |
|
||||||
| message | sender | {senderAddress} |
|
| message | sender | {senderAddress} |
|
||||||
|
|
||||||
|
## Keeper events
|
||||||
|
|
||||||
|
In addition to handlers events, the bank keeper will produce events when the following methods are called (or any method which ends up calling them)
|
||||||
|
|
||||||
|
### MintCoins
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "coinbase",
|
||||||
|
"attributes": [
|
||||||
|
{
|
||||||
|
"key": "minter",
|
||||||
|
"value": "{{sdk.AccAddress of the module minting coins}}",
|
||||||
|
"index": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "amount",
|
||||||
|
"value": "{{sdk.Coins being minted}}",
|
||||||
|
"index": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "coin_received",
|
||||||
|
"attributes": [
|
||||||
|
{
|
||||||
|
"key": "receiver",
|
||||||
|
"value": "{{sdk.AccAddress of the module minting coins}}",
|
||||||
|
"index": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "amount",
|
||||||
|
"value": "{{sdk.Coins being received}}",
|
||||||
|
"index": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### BurnCoins
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "burn",
|
||||||
|
"attributes": [
|
||||||
|
{
|
||||||
|
"key": "burner",
|
||||||
|
"value": "{{sdk.AccAddress of the module burning coins}}",
|
||||||
|
"index": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "amount",
|
||||||
|
"value": "{{sdk.Coins being burned}}",
|
||||||
|
"index": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "coin_spent",
|
||||||
|
"attributes": [
|
||||||
|
{
|
||||||
|
"key": "spender",
|
||||||
|
"value": "{{sdk.AccAddress of the module burning coins}}",
|
||||||
|
"index": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "amount",
|
||||||
|
"value": "{{sdk.Coins being burned}}",
|
||||||
|
"index": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### addCoins
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "coin_received",
|
||||||
|
"attributes": [
|
||||||
|
{
|
||||||
|
"key": "receiver",
|
||||||
|
"value": "{{sdk.AccAddress of the address beneficiary of the coins}}",
|
||||||
|
"index": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "amount",
|
||||||
|
"value": "{{sdk.Coins being received}}",
|
||||||
|
"index": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### subUnlockedCoins/DelegateCoins
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "coin_spent",
|
||||||
|
"attributes": [
|
||||||
|
{
|
||||||
|
"key": "spender",
|
||||||
|
"value": "{{sdk.AccAddress of the address which is spending coins}}",
|
||||||
|
"index": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "amount",
|
||||||
|
"value": "{{sdk.Coins being spent}}",
|
||||||
|
"index": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
|
@ -1,5 +1,9 @@
|
||||||
package types
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
)
|
||||||
|
|
||||||
// bank module event types
|
// bank module event types
|
||||||
const (
|
const (
|
||||||
EventTypeTransfer = "transfer"
|
EventTypeTransfer = "transfer"
|
||||||
|
@ -8,4 +12,55 @@ const (
|
||||||
AttributeKeySender = "sender"
|
AttributeKeySender = "sender"
|
||||||
|
|
||||||
AttributeValueCategory = ModuleName
|
AttributeValueCategory = ModuleName
|
||||||
|
|
||||||
|
// supply and balance tracking events name and attributes
|
||||||
|
EventTypeCoinSpent = "coin_spent"
|
||||||
|
EventTypeCoinReceived = "coin_received"
|
||||||
|
EventTypeCoinMint = "coinbase" // NOTE(fdymylja): using mint clashes with mint module event
|
||||||
|
EventTypeCoinBurn = "burn"
|
||||||
|
|
||||||
|
AttributeKeySpender = "spender"
|
||||||
|
AttributeKeyReceiver = "receiver"
|
||||||
|
AttributeKeyMinter = "minter"
|
||||||
|
AttributeKeyBurner = "burner"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// NewCoinSpentEvent constructs a new coin spent sdk.Event
|
||||||
|
// nolint: interfacer
|
||||||
|
func NewCoinSpentEvent(spender sdk.AccAddress, amount sdk.Coins) sdk.Event {
|
||||||
|
return sdk.NewEvent(
|
||||||
|
EventTypeCoinSpent,
|
||||||
|
sdk.NewAttribute(AttributeKeySpender, spender.String()),
|
||||||
|
sdk.NewAttribute(sdk.AttributeKeyAmount, amount.String()),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCoinReceivedEvent constructs a new coin received sdk.Event
|
||||||
|
// nolint: interfacer
|
||||||
|
func NewCoinReceivedEvent(receiver sdk.AccAddress, amount sdk.Coins) sdk.Event {
|
||||||
|
return sdk.NewEvent(
|
||||||
|
EventTypeCoinReceived,
|
||||||
|
sdk.NewAttribute(AttributeKeyReceiver, receiver.String()),
|
||||||
|
sdk.NewAttribute(sdk.AttributeKeyAmount, amount.String()),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCoinMintEvent construct a new coin minted sdk.Event
|
||||||
|
// nolint: interfacer
|
||||||
|
func NewCoinMintEvent(minter sdk.AccAddress, amount sdk.Coins) sdk.Event {
|
||||||
|
return sdk.NewEvent(
|
||||||
|
EventTypeCoinMint,
|
||||||
|
sdk.NewAttribute(AttributeKeyMinter, minter.String()),
|
||||||
|
sdk.NewAttribute(sdk.AttributeKeyAmount, amount.String()),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCoinBurnEvent constructs a new coin burned sdk.Event
|
||||||
|
// nolint: interfacer
|
||||||
|
func NewCoinBurnEvent(burner sdk.AccAddress, amount sdk.Coins) sdk.Event {
|
||||||
|
return sdk.NewEvent(
|
||||||
|
EventTypeCoinBurn,
|
||||||
|
sdk.NewAttribute(AttributeKeyBurner, burner.String()),
|
||||||
|
sdk.NewAttribute(sdk.AttributeKeyAmount, amount.String()),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
@ -58,11 +58,17 @@ func (s *IntegrationTestSuite) SetupSuite() {
|
||||||
val2 := s.network.Validators[1]
|
val2 := s.network.Validators[1]
|
||||||
|
|
||||||
// redelegate
|
// redelegate
|
||||||
_, err = stakingtestutil.MsgRedelegateExec(val.ClientCtx, val.Address, val.ValAddress, val2.ValAddress, unbond)
|
_, err = stakingtestutil.MsgRedelegateExec(
|
||||||
|
val.ClientCtx,
|
||||||
|
val.Address,
|
||||||
|
val.ValAddress,
|
||||||
|
val2.ValAddress,
|
||||||
|
unbond,
|
||||||
|
fmt.Sprintf("--%s=%d", flags.FlagGas, 202954), // 202954 is the required
|
||||||
|
)
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
_, err = s.network.WaitForHeight(1)
|
_, err = s.network.WaitForHeight(1)
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
|
|
||||||
// unbonding
|
// unbonding
|
||||||
_, err = stakingtestutil.MsgUnbondExec(val.ClientCtx, val.Address, val.ValAddress, unbond)
|
_, err = stakingtestutil.MsgUnbondExec(val.ClientCtx, val.Address, val.ValAddress, unbond)
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
|
|
|
@ -6,6 +6,8 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/client/flags"
|
||||||
|
|
||||||
"github.com/gogo/protobuf/proto"
|
"github.com/gogo/protobuf/proto"
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
|
|
||||||
|
@ -46,7 +48,15 @@ func (s *IntegrationTestSuite) SetupSuite() {
|
||||||
val2 := s.network.Validators[1]
|
val2 := s.network.Validators[1]
|
||||||
|
|
||||||
// redelegate
|
// redelegate
|
||||||
_, err = stakingtestutil.MsgRedelegateExec(val.ClientCtx, val.Address, val.ValAddress, val2.ValAddress, unbond)
|
_, err = stakingtestutil.MsgRedelegateExec(
|
||||||
|
val.ClientCtx,
|
||||||
|
val.Address,
|
||||||
|
val.ValAddress,
|
||||||
|
val2.ValAddress,
|
||||||
|
unbond,
|
||||||
|
fmt.Sprintf("--%s=%d", flags.FlagGas, 254000),
|
||||||
|
) // expected gas is 202987
|
||||||
|
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
_, err = s.network.WaitForHeight(1)
|
_, err = s.network.WaitForHeight(1)
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
|
@ -330,7 +340,7 @@ func (s *IntegrationTestSuite) TestQueryDelegationGRPC() {
|
||||||
s.Run(tc.name, func() {
|
s.Run(tc.name, func() {
|
||||||
resp, err := rest.GetRequest(tc.url)
|
resp, err := rest.GetRequest(tc.url)
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
|
s.T().Logf("%s", resp)
|
||||||
err = val.ClientCtx.JSONMarshaler.UnmarshalJSON(resp, tc.respType)
|
err = val.ClientCtx.JSONMarshaler.UnmarshalJSON(resp, tc.respType)
|
||||||
|
|
||||||
if tc.error {
|
if tc.error {
|
||||||
|
|
|
@ -27,6 +27,7 @@ func MsgRedelegateExec(clientCtx client.Context, from, src, dst, amount fmt.Stri
|
||||||
amount.String(),
|
amount.String(),
|
||||||
fmt.Sprintf("--%s=%s", flags.FlagFrom, from.String()),
|
fmt.Sprintf("--%s=%s", flags.FlagFrom, from.String()),
|
||||||
}
|
}
|
||||||
|
args = append(args, extraArgs...)
|
||||||
|
|
||||||
args = append(args, commonArgs...)
|
args = append(args, commonArgs...)
|
||||||
return clitestutil.ExecTestCLICmd(clientCtx, stakingcli.NewRedelegateCmd(), args)
|
return clitestutil.ExecTestCLICmd(clientCtx, stakingcli.NewRedelegateCmd(), args)
|
||||||
|
@ -43,5 +44,6 @@ func MsgUnbondExec(clientCtx client.Context, from fmt.Stringer, valAddress,
|
||||||
}
|
}
|
||||||
|
|
||||||
args = append(args, commonArgs...)
|
args = append(args, commonArgs...)
|
||||||
|
args = append(args, extraArgs...)
|
||||||
return clitestutil.ExecTestCLICmd(clientCtx, stakingcli.NewUnbondCmd(), args)
|
return clitestutil.ExecTestCLICmd(clientCtx, stakingcli.NewUnbondCmd(), args)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue