Skip to content

Commit

Permalink
Problem: no efficient bank api to support evm add/sub balances
Browse files Browse the repository at this point in the history
  • Loading branch information
yihuang committed Mar 28, 2024
1 parent 4d32911 commit 0c80f4e
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 28 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ Ref: https://keepachangelog.com/en/1.0.0/

* (baseapp) [#205](https://github.com/crypto-org-chain/cosmos-sdk/pull/205) Add `TxExecutor` baseapp option, add `TxIndex`/`TxCount`/`MsgIndex`/`BlockGasUsed` fields to `Context, to support tx parallel execution.
* (baseapp) [#205](https://github.com/crypto-org-chain/cosmos-sdk/pull/205) Support mount object store in baseapp, add `ObjectStore` api in context..
* (x/bank) [#]() Add low level `AddBalance`,`SubBalance` APIs to bank keeper.

## [Unreleased-Upstream]

Expand Down
32 changes: 32 additions & 0 deletions x/bank/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ type Keeper interface {
MintCoins(ctx context.Context, moduleName string, amt sdk.Coins) error
BurnCoins(ctx context.Context, moduleName string, amt sdk.Coins) error

// it's for evm integration, call at your own risk.
AddBalance(ctx context.Context, addr sdk.AccAddress, coin sdk.Coin) error
// it's for evm integration, call at your own risk.
SubBalance(ctx context.Context, addr sdk.AccAddress, coin sdk.Coin) error

DelegateCoins(ctx context.Context, delegatorAddr, moduleAccAddr sdk.AccAddress, amt sdk.Coins) error
UndelegateCoins(ctx context.Context, moduleAccAddr, delegatorAddr sdk.AccAddress, amt sdk.Coins) error

Expand Down Expand Up @@ -412,6 +417,33 @@ func (k BaseKeeper) BurnCoins(ctx context.Context, moduleName string, amounts sd
return nil
}

// AddBalance is low level api to update balance directly, mainly used by evm integration,
// caller should make sure the total supply of the denom not changed and the account exists.
//
// it emits mint event.
func (k BaseKeeper) AddBalance(ctx context.Context, addr sdk.AccAddress, coin sdk.Coin) error {
sdkCtx := sdk.UnwrapSDKContext(ctx)
// emit mint event
sdkCtx.EventManager().EmitEvent(
types.NewCoinMintEvent(addr, sdk.NewCoins(coin)),
)
return k.addCoin(ctx, addr, coin)
}

// SubBalance is low level api to set balance directly, mainly used by evm integration,
// caller should make sure the total supply of the denom not changed, the account exists,
// and not a vesting account (no locked coins).
//
// it emits burn event.
func (k BaseKeeper) SubBalance(ctx context.Context, addr sdk.AccAddress, coin sdk.Coin) error {
sdkCtx := sdk.UnwrapSDKContext(ctx)
// emit burn event
sdkCtx.EventManager().EmitEvent(
types.NewCoinBurnEvent(addr, sdk.NewCoins(coin)),
)
return k.subCoin(ctx, addr, coin, sdk.NewCoin(coin.Denom, math.ZeroInt()))
}

// setSupply sets the supply for the given coin
func (k BaseKeeper) setSupply(ctx context.Context, coin sdk.Coin) {
// Bank invariants and IBC requires to remove zero coins.
Expand Down
61 changes: 33 additions & 28 deletions x/bank/keeper/send.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
"cosmossdk.io/core/store"
errorsmod "cosmossdk.io/errors"
"cosmossdk.io/log"
"cosmossdk.io/math"

"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/telemetry"
Expand Down Expand Up @@ -262,29 +261,8 @@ func (k BaseSendKeeper) subUnlockedCoins(ctx context.Context, addr sdk.AccAddres
lockedCoins := k.LockedCoins(ctx, addr)

for _, coin := range amt {
balance := k.GetBalance(ctx, addr, coin.Denom)
locked := sdk.NewCoin(coin.Denom, lockedCoins.AmountOf(coin.Denom))

spendable, hasNeg := sdk.Coins{balance}.SafeSub(locked)
if hasNeg {
return errorsmod.Wrapf(sdkerrors.ErrInsufficientFunds,
"locked amount exceeds account balance funds: %s > %s", locked, balance)
}

if _, hasNeg := spendable.SafeSub(coin); hasNeg {
if len(spendable) == 0 {
spendable = sdk.Coins{sdk.NewCoin(coin.Denom, math.ZeroInt())}
}
return errorsmod.Wrapf(
sdkerrors.ErrInsufficientFunds,
"spendable balance %s is smaller than %s",
spendable, coin,
)
}

newBalance := balance.Sub(coin)

if err := k.setBalance(ctx, addr, newBalance); err != nil {
if err := k.subCoin(ctx, addr, coin, locked); err != nil {
return err
}
}
Expand All @@ -297,6 +275,31 @@ func (k BaseSendKeeper) subUnlockedCoins(ctx context.Context, addr sdk.AccAddres
return nil
}

func (k BaseSendKeeper) subCoin(ctx context.Context, addr sdk.AccAddress, coin sdk.Coin, locked sdk.Coin) error {
var (
spendable sdk.Coin
err error
)
balance := k.GetBalance(ctx, addr, coin.Denom)
if locked.IsZero() {
spendable = balance
} else {
spendable, err = spendable.SafeSub(locked)
if err != nil {
return errorsmod.Wrapf(sdkerrors.ErrInsufficientFunds,
"locked amount exceeds account balance funds: %s > %s", locked, balance)
}
}
if spendable.Amount.LT(coin.Amount) {
return errorsmod.Wrapf(
sdkerrors.ErrInsufficientFunds,
"spendable balance %s is smaller than %s",
spendable, coin,
)
}
return k.setBalance(ctx, addr, balance.Sub(coin))
}

// addCoins increase the addr balance by the given amt. Fails if the provided
// amt is invalid. It emits a coin received event.
func (k BaseSendKeeper) addCoins(ctx context.Context, addr sdk.AccAddress, amt sdk.Coins) error {
Expand All @@ -305,11 +308,7 @@ func (k BaseSendKeeper) addCoins(ctx context.Context, addr sdk.AccAddress, amt s
}

for _, coin := range amt {
balance := k.GetBalance(ctx, addr, coin.Denom)
newBalance := balance.Add(coin)

err := k.setBalance(ctx, addr, newBalance)
if err != nil {
if err := k.addCoin(ctx, addr, coin); err != nil {
return err
}
}
Expand All @@ -323,6 +322,12 @@ func (k BaseSendKeeper) addCoins(ctx context.Context, addr sdk.AccAddress, amt s
return nil
}

func (k BaseSendKeeper) addCoin(ctx context.Context, addr sdk.AccAddress, coin sdk.Coin) error {
balance := k.GetBalance(ctx, addr, coin.Denom)
newBalance := balance.Add(coin)
return k.setBalance(ctx, addr, newBalance)
}

// setBalance sets the coin balance for an account by address.
func (k BaseSendKeeper) setBalance(ctx context.Context, addr sdk.AccAddress, balance sdk.Coin) error {
if !balance.IsValid() {
Expand Down

0 comments on commit 0c80f4e

Please sign in to comment.