mirror of
https://github.com/0glabs/0g-chain.git
synced 2024-12-27 08:45:19 +00:00
9de9de671e
Module reserve represents fractional balances, so it should be hidden to consumers to not have a misleading total balance that doubles the fractional balances. This modifies GetBalance() and SpendableCoin() to always return zero coins when fetching the reserve address balance for fractional amounts.
297 lines
9.2 KiB
Go
297 lines
9.2 KiB
Go
package keeper_test
|
|
|
|
import (
|
|
"testing"
|
|
|
|
sdkmath "cosmossdk.io/math"
|
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
|
|
"github.com/kava-labs/kava/x/precisebank/types"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestKeeper_GetBalance(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
giveDenom string // queried denom for balance
|
|
|
|
giveBankBal sdk.Coins // mocked bank balance for giveAddr
|
|
giveFractionalBal sdkmath.Int // stored fractional balance for giveAddr
|
|
|
|
wantBal sdk.Coin
|
|
}{
|
|
{
|
|
"extended denom - no fractional balance",
|
|
types.ExtendedCoinDenom,
|
|
// queried bank balance in ukava when querying for akava
|
|
sdk.NewCoins(sdk.NewCoin(types.IntegerCoinDenom, sdk.NewInt(1000))),
|
|
sdkmath.ZeroInt(),
|
|
// integer + fractional
|
|
sdk.NewCoin(types.ExtendedCoinDenom, sdk.NewInt(1000_000_000_000_000)),
|
|
},
|
|
{
|
|
"extended denom - with fractional balance",
|
|
types.ExtendedCoinDenom,
|
|
sdk.NewCoins(sdk.NewCoin(types.IntegerCoinDenom, sdk.NewInt(1000))),
|
|
sdkmath.NewInt(100),
|
|
// integer + fractional
|
|
sdk.NewCoin(types.ExtendedCoinDenom, sdk.NewInt(1000_000_000_000_100)),
|
|
},
|
|
{
|
|
"extended denom - only fractional balance",
|
|
types.ExtendedCoinDenom,
|
|
// no coins in bank, only fractional balance
|
|
sdk.NewCoins(),
|
|
sdkmath.NewInt(100),
|
|
sdk.NewCoin(types.ExtendedCoinDenom, sdk.NewInt(100)),
|
|
},
|
|
{
|
|
"extended denom - max fractional balance",
|
|
types.ExtendedCoinDenom,
|
|
sdk.NewCoins(sdk.NewCoin(types.IntegerCoinDenom, sdk.NewInt(1000))),
|
|
types.ConversionFactor().SubRaw(1),
|
|
// integer + fractional
|
|
sdk.NewCoin(types.ExtendedCoinDenom, sdk.NewInt(1000_999_999_999_999)),
|
|
},
|
|
{
|
|
"non-extended denom - ukava returns ukava",
|
|
types.IntegerCoinDenom,
|
|
sdk.NewCoins(sdk.NewCoin(types.IntegerCoinDenom, sdk.NewInt(1000))),
|
|
sdk.ZeroInt(),
|
|
sdk.NewCoin("ukava", sdk.NewInt(1000)),
|
|
},
|
|
{
|
|
"non-extended denom - unaffected by fractional balance",
|
|
"ukava",
|
|
sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(1000))),
|
|
sdkmath.NewInt(100),
|
|
sdk.NewCoin("ukava", sdk.NewInt(1000)),
|
|
},
|
|
{
|
|
"unrelated denom - no fractional",
|
|
"busd",
|
|
sdk.NewCoins(sdk.NewCoin("busd", sdk.NewInt(1000))),
|
|
sdk.ZeroInt(),
|
|
sdk.NewCoin("busd", sdk.NewInt(1000)),
|
|
},
|
|
{
|
|
"unrelated denom - unaffected by fractional balance",
|
|
"busd",
|
|
sdk.NewCoins(sdk.NewCoin("busd", sdk.NewInt(1000))),
|
|
sdkmath.NewInt(100),
|
|
sdk.NewCoin("busd", sdk.NewInt(1000)),
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
tk := NewMockedTestData(t)
|
|
addr := sdk.AccAddress([]byte("test-address"))
|
|
|
|
// Set fractional balance in store before query
|
|
tk.keeper.SetFractionalBalance(tk.ctx, addr, tt.giveFractionalBal)
|
|
|
|
// Checks address if its a reserve denom
|
|
if tt.giveDenom == types.ExtendedCoinDenom {
|
|
tk.ak.EXPECT().GetModuleAddress(types.ModuleName).
|
|
Return(authtypes.NewModuleAddress(types.ModuleName)).
|
|
Once()
|
|
}
|
|
|
|
if tt.giveDenom == types.ExtendedCoinDenom {
|
|
// No balance pass through
|
|
tk.bk.EXPECT().
|
|
GetBalance(tk.ctx, addr, types.IntegerCoinDenom).
|
|
RunAndReturn(func(_ sdk.Context, _ sdk.AccAddress, _ string) sdk.Coin {
|
|
amt := tt.giveBankBal.AmountOf(types.IntegerCoinDenom)
|
|
return sdk.NewCoin(types.IntegerCoinDenom, amt)
|
|
}).
|
|
Once()
|
|
} else {
|
|
// Pass through to x/bank for denoms except ExtendedCoinDenom
|
|
tk.bk.EXPECT().
|
|
GetBalance(tk.ctx, addr, tt.giveDenom).
|
|
RunAndReturn(func(ctx sdk.Context, aa sdk.AccAddress, s string) sdk.Coin {
|
|
require.Equal(t, s, tt.giveDenom, "unexpected denom passed to x/bank.GetBalance")
|
|
|
|
return sdk.NewCoin(tt.giveDenom, tt.giveBankBal.AmountOf(s))
|
|
}).
|
|
Once()
|
|
}
|
|
|
|
bal := tk.keeper.GetBalance(tk.ctx, addr, tt.giveDenom)
|
|
require.Equal(t, tt.wantBal, bal)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestKeeper_SpendableCoin(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
giveDenom string // queried denom for balance
|
|
|
|
giveBankBal sdk.Coins // mocked bank balance for giveAddr
|
|
giveFractionalBal sdkmath.Int // stored fractional balance for giveAddr
|
|
|
|
wantBal sdk.Coin
|
|
}{
|
|
{
|
|
"extended denom - no fractional balance",
|
|
types.ExtendedCoinDenom,
|
|
// queried bank balance in ukava when querying for akava
|
|
sdk.NewCoins(sdk.NewCoin(types.IntegerCoinDenom, sdk.NewInt(1000))),
|
|
sdkmath.ZeroInt(),
|
|
// integer + fractional
|
|
sdk.NewCoin(types.ExtendedCoinDenom, sdk.NewInt(1000_000_000_000_000)),
|
|
},
|
|
{
|
|
"extended denom - with fractional balance",
|
|
types.ExtendedCoinDenom,
|
|
sdk.NewCoins(sdk.NewCoin(types.IntegerCoinDenom, sdk.NewInt(1000))),
|
|
sdkmath.NewInt(100),
|
|
// integer + fractional
|
|
sdk.NewCoin(types.ExtendedCoinDenom, sdk.NewInt(1000_000_000_000_100)),
|
|
},
|
|
{
|
|
"extended denom - only fractional balance",
|
|
types.ExtendedCoinDenom,
|
|
// no coins in bank, only fractional balance
|
|
sdk.NewCoins(),
|
|
sdkmath.NewInt(100),
|
|
sdk.NewCoin(types.ExtendedCoinDenom, sdk.NewInt(100)),
|
|
},
|
|
{
|
|
"extended denom - max fractional balance",
|
|
types.ExtendedCoinDenom,
|
|
sdk.NewCoins(sdk.NewCoin(types.IntegerCoinDenom, sdk.NewInt(1000))),
|
|
types.ConversionFactor().SubRaw(1),
|
|
// integer + fractional
|
|
sdk.NewCoin(types.ExtendedCoinDenom, sdk.NewInt(1000_999_999_999_999)),
|
|
},
|
|
{
|
|
"non-extended denom - ukava returns ukava",
|
|
types.IntegerCoinDenom,
|
|
sdk.NewCoins(sdk.NewCoin(types.IntegerCoinDenom, sdk.NewInt(1000))),
|
|
sdk.ZeroInt(),
|
|
sdk.NewCoin("ukava", sdk.NewInt(1000)),
|
|
},
|
|
{
|
|
"non-extended denom - unaffected by fractional balance",
|
|
"ukava",
|
|
sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(1000))),
|
|
sdkmath.NewInt(100),
|
|
sdk.NewCoin("ukava", sdk.NewInt(1000)),
|
|
},
|
|
{
|
|
"unrelated denom - no fractional",
|
|
"busd",
|
|
sdk.NewCoins(sdk.NewCoin("busd", sdk.NewInt(1000))),
|
|
sdk.ZeroInt(),
|
|
sdk.NewCoin("busd", sdk.NewInt(1000)),
|
|
},
|
|
{
|
|
"unrelated denom - unaffected by fractional balance",
|
|
"busd",
|
|
sdk.NewCoins(sdk.NewCoin("busd", sdk.NewInt(1000))),
|
|
sdkmath.NewInt(100),
|
|
sdk.NewCoin("busd", sdk.NewInt(1000)),
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
tk := NewMockedTestData(t)
|
|
addr := sdk.AccAddress([]byte("test-address"))
|
|
|
|
// Set fractional balance in store before query
|
|
tk.keeper.SetFractionalBalance(tk.ctx, addr, tt.giveFractionalBal)
|
|
|
|
// If its a reserve denom, module address is checked
|
|
if tt.giveDenom == types.ExtendedCoinDenom {
|
|
tk.ak.EXPECT().GetModuleAddress(types.ModuleName).
|
|
Return(authtypes.NewModuleAddress(types.ModuleName)).
|
|
Once()
|
|
}
|
|
|
|
if tt.giveDenom == types.ExtendedCoinDenom {
|
|
// No balance pass through
|
|
tk.bk.EXPECT().
|
|
SpendableCoin(tk.ctx, addr, types.IntegerCoinDenom).
|
|
RunAndReturn(func(_ sdk.Context, _ sdk.AccAddress, _ string) sdk.Coin {
|
|
amt := tt.giveBankBal.AmountOf(types.IntegerCoinDenom)
|
|
return sdk.NewCoin(types.IntegerCoinDenom, amt)
|
|
}).
|
|
Once()
|
|
} else {
|
|
// Pass through to x/bank for denoms except ExtendedCoinDenom
|
|
tk.bk.EXPECT().
|
|
SpendableCoin(tk.ctx, addr, tt.giveDenom).
|
|
RunAndReturn(func(ctx sdk.Context, aa sdk.AccAddress, s string) sdk.Coin {
|
|
require.Equal(t, s, tt.giveDenom, "unexpected denom passed to x/bank.GetBalance")
|
|
|
|
return sdk.NewCoin(tt.giveDenom, tt.giveBankBal.AmountOf(s))
|
|
}).
|
|
Once()
|
|
}
|
|
|
|
bal := tk.keeper.SpendableCoin(tk.ctx, addr, tt.giveDenom)
|
|
require.Equal(t, tt.wantBal, bal)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestHiddenReserve(t *testing.T) {
|
|
// Reserve balances should not be shown to consumers of x/precisebank, as it
|
|
// represents the fractional balances of accounts.
|
|
|
|
tk := NewMockedTestData(t)
|
|
|
|
moduleAddr := authtypes.NewModuleAddress(types.ModuleName)
|
|
|
|
// No mock bankkeeper expectations, which means the zero coin is returned
|
|
// directly for reserve address. So the mock bankkeeper doesn't need to have
|
|
// a handler for getting underlying balance.
|
|
|
|
tests := []struct {
|
|
name string
|
|
denom string
|
|
expectedBalance sdk.Coin
|
|
}{
|
|
{"akava", types.ExtendedCoinDenom, sdk.NewCoin(types.ExtendedCoinDenom, sdkmath.ZeroInt())},
|
|
{"ukava", types.IntegerCoinDenom, sdk.NewCoin(types.IntegerCoinDenom, sdkmath.NewInt(1))},
|
|
{"unrelated denom", "cat", sdk.NewCoin("cat", sdkmath.ZeroInt())},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
// 2 calls for GetBalance and SpendableCoin, only for reserve coins
|
|
if tt.denom == "akava" {
|
|
tk.ak.EXPECT().GetModuleAddress(types.ModuleName).
|
|
Return(moduleAddr).
|
|
Twice()
|
|
} else {
|
|
// Passthrough to x/bank for non-reserve denoms
|
|
tk.bk.EXPECT().
|
|
GetBalance(tk.ctx, moduleAddr, tt.denom).
|
|
Return(sdk.NewCoin(tt.denom, sdkmath.ZeroInt())).
|
|
Once()
|
|
|
|
tk.bk.EXPECT().
|
|
SpendableCoin(tk.ctx, moduleAddr, tt.denom).
|
|
Return(sdk.NewCoin(tt.denom, sdkmath.ZeroInt())).
|
|
Once()
|
|
}
|
|
|
|
// GetBalance should return zero balance for reserve address
|
|
coin := tk.keeper.GetBalance(tk.ctx, moduleAddr, tt.denom)
|
|
require.Equal(t, tt.denom, coin.Denom)
|
|
require.Equal(t, sdkmath.ZeroInt(), coin.Amount)
|
|
|
|
// SpendableCoin should return zero balance for reserve address
|
|
spendableCoin := tk.keeper.SpendableCoin(tk.ctx, moduleAddr, tt.denom)
|
|
require.Equal(t, tt.denom, spendableCoin.Denom)
|
|
require.Equal(t, sdkmath.ZeroInt(), spendableCoin.Amount)
|
|
})
|
|
}
|
|
}
|