Skip to content

Commit

Permalink
feat: Add remainder to genesis
Browse files Browse the repository at this point in the history
  • Loading branch information
drklee3 committed May 10, 2024
1 parent 68e9864 commit 581c6a8
Show file tree
Hide file tree
Showing 9 changed files with 306 additions and 37 deletions.
1 change: 1 addition & 0 deletions docs/core/proto-docs.md
Expand Up @@ -6660,6 +6660,7 @@ GenesisState defines the precisebank module's genesis state.
| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| `balances` | [FractionalBalance](#kava.precisebank.v1.FractionalBalance) | repeated | balances is a list of all the balances in the precisebank module. |
| `remainder` | [string](#string) | | remainder is an internal value of how much extra fractional digits are still backed by the reserve, but not assigned to any account. |



Expand Down
8 changes: 8 additions & 0 deletions proto/kava/precisebank/v1/genesis.proto
Expand Up @@ -13,6 +13,14 @@ message GenesisState {
(gogoproto.castrepeated) = "FractionalBalances",
(gogoproto.nullable) = false
];

// remainder is an internal value of how much extra fractional digits are
// still backed by the reserve, but not assigned to any account.
string remainder = 2 [
(cosmos_proto.scalar) = "cosmos.Int",
(gogoproto.customtype) = "cosmossdk.io/math.Int",
(gogoproto.nullable) = false
];
}

// FractionalBalance defines the fractional portion of an account balance
Expand Down
3 changes: 2 additions & 1 deletion x/precisebank/genesis.go
Expand Up @@ -3,6 +3,7 @@ package precisebank
import (
"fmt"

sdkmath "cosmossdk.io/math"
sdk "github.com/cosmos/cosmos-sdk/types"

"github.com/kava-labs/kava/x/precisebank/keeper"
Expand Down Expand Up @@ -33,5 +34,5 @@ func InitGenesis(

// ExportGenesis returns a GenesisState for a given context and keeper.
func ExportGenesis(ctx sdk.Context, keeper keeper.Keeper) *types.GenesisState {
return types.NewGenesisState(nil)
return types.NewGenesisState(nil, sdkmath.ZeroInt())
}
6 changes: 5 additions & 1 deletion x/precisebank/types/fractional_balance.go
Expand Up @@ -7,12 +7,16 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
)

// Conversion factor is used to convert the fractional balance to the integer
// balances.
var CONVERSION_FACTOR = sdkmath.NewInt(1_000_000_000_000)

// FractionalBalance contains __only__ the fractional balance of an address.
// We want to extend the current KAVA decimal digits from 6 to 18, thus 12 more
// digits are added to the fractional balance. With 12 digits, the maximum
// value of the fractional balance is 1_000_000_000_000 - 1.
// We subtract 1, as 1 more will roll over to the integer balance.
var maxFractionalAmount = sdkmath.NewInt(1_000_000_000_000).SubRaw(1)
var maxFractionalAmount = CONVERSION_FACTOR.SubRaw(1)

// GetMaxFractionalAmount returns the maximum value of a FractionalBalance.
func GetMaxFractionalAmount() sdkmath.Int {
Expand Down
13 changes: 13 additions & 0 deletions x/precisebank/types/fractional_balances.go
Expand Up @@ -2,6 +2,8 @@ package types

import (
fmt "fmt"

sdkmath "cosmossdk.io/math"
)

// FractionalBalances is a slice of FractionalBalance
Expand All @@ -28,3 +30,14 @@ func (fbs FractionalBalances) Validate() error {

return nil
}

// SumAmount returns the sum of all the amounts in the slice.
func (fbs FractionalBalances) SumAmount() sdkmath.Int {
sum := sdkmath.ZeroInt()

for _, fb := range fbs {
sum = sum.Add(fb.Amount)
}

return sum
}
51 changes: 51 additions & 0 deletions x/precisebank/types/fractional_balances_test.go
@@ -1,6 +1,7 @@
package types_test

import (
"math/rand"
"testing"

sdkmath "cosmossdk.io/math"
Expand Down Expand Up @@ -69,3 +70,53 @@ func TestFractionalBalances_Validate(t *testing.T) {
})
}
}

func TestFractionalBalances_SumAmount(t *testing.T) {
generateRandomFractionalBalances := func(n int) (types.FractionalBalances, sdkmath.Int) {
balances := make(types.FractionalBalances, n)
sum := sdkmath.ZeroInt()

for i := 0; i < n; i++ {
addr := sdk.AccAddress{byte(i)}.String()
amount := sdkmath.NewInt(rand.Int63())
balances[i] = types.NewFractionalBalance(addr, amount)

sum = sum.Add(amount)
}

return balances, sum
}

multiBalances, sum := generateRandomFractionalBalances(10)

tests := []struct {
name string
balances types.FractionalBalances
wantSum sdkmath.Int
}{
{
"empty",
types.FractionalBalances{},
sdkmath.ZeroInt(),
},
{
"single",
types.FractionalBalances{
types.NewFractionalBalance(sdk.AccAddress{1}.String(), sdkmath.NewInt(100)),
},
sdkmath.NewInt(100),
},
{
"multiple",
multiBalances,
sum,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
sum := tt.balances.SumAmount()
require.Equal(t, tt.wantSum, sum)
})
}
}
46 changes: 38 additions & 8 deletions x/precisebank/types/genesis.go
@@ -1,30 +1,60 @@
package types

import "fmt"
import (
"fmt"

sdkmath "cosmossdk.io/math"
)

// Validate performs basic validation of genesis data returning an error for
// any failed validation criteria.
func (gs *GenesisState) Validate() error {
// Validate all FractionalBalances
if err := gs.Balances.Validate(); err != nil {
return fmt.Errorf("invalid balances: %w", err)
}

// TODO:
// - Validate remainder amount
// - Validate sum(fractionalBalances) + remainder = whole integer value
// - Cannot validate here: reserve account exists & balance match
if gs.Remainder.IsNil() {
return fmt.Errorf("nil remainder amount")
}

// Validate remainder, 0 <= remainder <= maxFractionalAmount
if gs.Remainder.IsNegative() {
return fmt.Errorf("negative remainder amount: %s", gs.Remainder)
}

if gs.Remainder.GT(maxFractionalAmount) {
return fmt.Errorf("remainder exceeds max of %v: %v", maxFractionalAmount, gs.Remainder)
}

// Determine if sum(fractionalBalances) + remainder = whole integer value
// i.e total of all fractional balances + remainder == 0 fractional digits
sum := gs.Balances.SumAmount()
total := sum.Add(gs.Remainder)

if !total.Mod(CONVERSION_FACTOR).IsZero() {
return fmt.Errorf(
"sum of fractional balances + remainder is not a whole integer value: %v + %v == %v, but expected to end in 12 zeros",
sum, gs.Remainder,
total,
)
}

return nil
}

// NewGenesisState creates a new genesis state.
func NewGenesisState(balances FractionalBalances) *GenesisState {
func NewGenesisState(
balances FractionalBalances,
remainder sdkmath.Int,
) *GenesisState {
return &GenesisState{
Balances: balances,
Balances: balances,
Remainder: remainder,
}
}

// DefaultGenesisState returns a default genesis state.
func DefaultGenesisState() *GenesisState {
return NewGenesisState(nil)
return NewGenesisState(nil, sdkmath.ZeroInt())
}
94 changes: 72 additions & 22 deletions x/precisebank/types/genesis.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 581c6a8

Please sign in to comment.