From 94914d4ca1e2673f2525586fa42e75d484ed6281 Mon Sep 17 00:00:00 2001 From: drklee3 Date: Mon, 13 May 2024 14:16:05 -0700 Subject: [PATCH] feat(x/precisebank): Add FractionalBalance types (#1907) - Add necessary types to track account fractional balances. - Add FractionalBalance type to genesis --- docs/core/proto-docs.md | 22 ++ proto/kava/precisebank/v1/genesis.proto | 29 +- x/precisebank/genesis.go | 2 +- x/precisebank/types/fractional_balance.go | 57 ++++ .../types/fractional_balance_test.go | 164 ++++++++++ x/precisebank/types/fractional_balances.go | 35 ++ .../types/fractional_balances_test.go | 86 +++++ x/precisebank/types/genesis.go | 23 +- x/precisebank/types/genesis.pb.go | 308 +++++++++++++++++- x/precisebank/types/genesis_test.go | 37 ++- 10 files changed, 737 insertions(+), 26 deletions(-) create mode 100644 x/precisebank/types/fractional_balance.go create mode 100644 x/precisebank/types/fractional_balance_test.go create mode 100644 x/precisebank/types/fractional_balances.go create mode 100644 x/precisebank/types/fractional_balances_test.go diff --git a/docs/core/proto-docs.md b/docs/core/proto-docs.md index 0aee98cc..0bc7cdf5 100644 --- a/docs/core/proto-docs.md +++ b/docs/core/proto-docs.md @@ -477,6 +477,7 @@ - [Msg](#kava.liquid.v1beta1.Msg) - [kava/precisebank/v1/genesis.proto](#kava/precisebank/v1/genesis.proto) + - [FractionalBalance](#kava.precisebank.v1.FractionalBalance) - [GenesisState](#kava.precisebank.v1.GenesisState) - [kava/pricefeed/v1beta1/store.proto](#kava/pricefeed/v1beta1/store.proto) @@ -6634,12 +6635,33 @@ Msg defines the liquid Msg service. + + +### FractionalBalance +FractionalBalance defines the fractional portion of an account balance + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `address` | [string](#string) | | address is the address of the balance holder. | +| `amount` | [string](#string) | | amount indicates amount of only the fractional balance owned by the address. FractionalBalance currently only supports tracking 1 single asset, e.g. fractional balances of ukava. | + + + + + + ### GenesisState 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. | + + diff --git a/proto/kava/precisebank/v1/genesis.proto b/proto/kava/precisebank/v1/genesis.proto index 5b80179b..ecbebc28 100644 --- a/proto/kava/precisebank/v1/genesis.proto +++ b/proto/kava/precisebank/v1/genesis.proto @@ -1,7 +1,34 @@ syntax = "proto3"; package kava.precisebank.v1; +import "cosmos_proto/cosmos.proto"; +import "gogoproto/gogo.proto"; + option go_package = "github.com/kava-labs/kava/x/precisebank/types"; // GenesisState defines the precisebank module's genesis state. -message GenesisState {} +message GenesisState { + // balances is a list of all the balances in the precisebank module. + repeated FractionalBalance balances = 1 [ + (gogoproto.castrepeated) = "FractionalBalances", + (gogoproto.nullable) = false + ]; +} + +// FractionalBalance defines the fractional portion of an account balance +message FractionalBalance { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + // address is the address of the balance holder. + string address = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; + + // amount indicates amount of only the fractional balance owned by the + // address. FractionalBalance currently only supports tracking 1 single asset, + // e.g. fractional balances of ukava. + string amount = 2 [ + (cosmos_proto.scalar) = "cosmos.Int", + (gogoproto.customtype) = "cosmossdk.io/math.Int", + (gogoproto.nullable) = false + ]; +} diff --git a/x/precisebank/genesis.go b/x/precisebank/genesis.go index 4bcf0498..dfc92512 100644 --- a/x/precisebank/genesis.go +++ b/x/precisebank/genesis.go @@ -33,5 +33,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() + return types.NewGenesisState(nil) } diff --git a/x/precisebank/types/fractional_balance.go b/x/precisebank/types/fractional_balance.go new file mode 100644 index 00000000..bf05a1f0 --- /dev/null +++ b/x/precisebank/types/fractional_balance.go @@ -0,0 +1,57 @@ +package types + +import ( + fmt "fmt" + + sdkmath "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// maxFractionalAmount is the largest valid value in a FractionalBalance amount. +// This is for direct internal use so that there are no extra allocations. +var maxFractionalAmount = sdkmath.NewInt(1_000_000_000_000).SubRaw(1) + +// MaxFractionalAmount returns the largest valid value in a FractionalBalance +// amount. +// FractionalBalance contains **only** the fractional balance of an address. +// We want to extend the current KAVA decimal digits from 6 to 18, or in other +// words add 12 fractional digits to ukava. +// With 12 digits, the valid amount is 1 - 999_999_999_999. +func MaxFractionalAmount() sdkmath.Int { + // BigInt() returns a copy of the internal big.Int, so it's safe to directly + // use it for a new Int instead of creating another big.Int internally. + // We need to copy it because the internal value can be accessed and + // modified via Int.BigIntMut() + return sdkmath.NewIntFromBigIntMut(maxFractionalAmount.BigInt()) +} + +// FractionalBalance returns a new FractionalBalance with the given address and +// amount. +func NewFractionalBalance(address string, amount sdkmath.Int) FractionalBalance { + return FractionalBalance{ + Address: address, + Amount: amount, + } +} + +// Validate returns an error if the FractionalBalance has an invalid address or +// negative amount. +func (fb FractionalBalance) Validate() error { + if _, err := sdk.AccAddressFromBech32(fb.Address); err != nil { + return err + } + + if fb.Amount.IsNil() { + return fmt.Errorf("nil amount") + } + + if !fb.Amount.IsPositive() { + return fmt.Errorf("non-positive amount %v", fb.Amount) + } + + if fb.Amount.GT(maxFractionalAmount) { + return fmt.Errorf("amount %v exceeds max of %v", fb.Amount, maxFractionalAmount) + } + + return nil +} diff --git a/x/precisebank/types/fractional_balance_test.go b/x/precisebank/types/fractional_balance_test.go new file mode 100644 index 00000000..4de582b8 --- /dev/null +++ b/x/precisebank/types/fractional_balance_test.go @@ -0,0 +1,164 @@ +package types_test + +import ( + "math/big" + "testing" + + sdkmath "cosmossdk.io/math" + "github.com/kava-labs/kava/app" + "github.com/kava-labs/kava/x/precisebank/types" + "github.com/stretchr/testify/require" +) + +func TestMaxFractionalAmount_Immutable(t *testing.T) { + max1 := types.MaxFractionalAmount() + origInt64 := max1.Int64() + + // Get the internal pointer to the big.Int without copying + internalBigInt := max1.BigIntMut() + + // Mutate the big.Int -- .Add() mutates in place + internalBigInt.Add(internalBigInt, big.NewInt(5)) + // Ensure bigInt was actually mutated + require.Equal(t, origInt64+5, internalBigInt.Int64()) + + // Fetch the max amount again + max2 := types.MaxFractionalAmount() + + require.Equal( + t, + origInt64, + max2.Int64(), + "max amount should be immutable", + ) +} + +func TestMaxFractionalAmount_Copied(t *testing.T) { + max1 := types.MaxFractionalAmount().BigIntMut() + max2 := types.MaxFractionalAmount().BigIntMut() + + // Checks that the returned two pointers do not reference the same object + require.NotSame(t, max1, max2, "max fractional amount should be copied") +} + +func TestNewFractionalBalance(t *testing.T) { + tests := []struct { + name string + giveAddress string + giveAmount sdkmath.Int + }{ + { + "correctly sets fields", + "cosmos1qperwt9wrnkg5k9e5gzfgjppzpqur82k6c5a0n", + sdkmath.NewInt(100), + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + fb := types.NewFractionalBalance(tt.giveAddress, tt.giveAmount) + + require.Equal(t, tt.giveAddress, fb.Address) + require.Equal(t, tt.giveAmount, fb.Amount) + }) + } +} + +func TestFractionalBalance_Validate(t *testing.T) { + app.SetSDKConfig() + + tests := []struct { + name string + giveAddress string + giveAmount sdkmath.Int + wantErr string + }{ + { + "valid", + "kava1gpxd677pp8zr97xvy3pmgk70a9vcpagsakv0tx", + sdkmath.NewInt(100), + "", + }, + { + "valid - uppercase address", + "KAVA1GPXD677PP8ZR97XVY3PMGK70A9VCPAGSAKV0TX", + sdkmath.NewInt(100), + "", + }, + { + "valid - min balance", + "kava1gpxd677pp8zr97xvy3pmgk70a9vcpagsakv0tx", + sdkmath.NewInt(1), + "", + }, + { + "valid - max balance", + "kava1gpxd677pp8zr97xvy3pmgk70a9vcpagsakv0tx", + types.MaxFractionalAmount(), + "", + }, + { + "invalid - 0 balance", + "kava1gpxd677pp8zr97xvy3pmgk70a9vcpagsakv0tx", + sdkmath.NewInt(0), + "non-positive amount 0", + }, + { + "invalid - empty", + "kava1gpxd677pp8zr97xvy3pmgk70a9vcpagsakv0tx", + sdkmath.Int{}, + "nil amount", + }, + { + "invalid - mixed case address", + "kava1gpxd677pP8zr97xvy3pmgk70a9vcpagsakv0tx", + sdkmath.NewInt(100), + "decoding bech32 failed: string not all lowercase or all uppercase", + }, + { + "invalid - non-bech32 address", + "invalid", + sdkmath.NewInt(100), + "decoding bech32 failed: invalid bech32 string length 7", + }, + { + "invalid - wrong bech32 prefix", + "cosmos1qperwt9wrnkg5k9e5gzfgjppzpqur82k7gqd8n", + sdkmath.NewInt(100), + "invalid Bech32 prefix; expected kava, got cosmos", + }, + { + "invalid - negative amount", + "kava1gpxd677pp8zr97xvy3pmgk70a9vcpagsakv0tx", + sdkmath.NewInt(-100), + "non-positive amount -100", + }, + { + "invalid - max amount + 1", + "kava1gpxd677pp8zr97xvy3pmgk70a9vcpagsakv0tx", + types.MaxFractionalAmount().AddRaw(1), + "amount 1000000000000 exceeds max of 999999999999", + }, + { + "invalid - much more than max amount", + "kava1gpxd677pp8zr97xvy3pmgk70a9vcpagsakv0tx", + sdkmath.NewInt(100000000000_000), + "amount 100000000000000 exceeds max of 999999999999", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + fb := types.NewFractionalBalance(tt.giveAddress, tt.giveAmount) + err := fb.Validate() + + if tt.wantErr == "" { + require.NoError(t, err) + return + } + + require.Error(t, err) + require.EqualError(t, err, tt.wantErr) + }) + } +} diff --git a/x/precisebank/types/fractional_balances.go b/x/precisebank/types/fractional_balances.go new file mode 100644 index 00000000..774e5590 --- /dev/null +++ b/x/precisebank/types/fractional_balances.go @@ -0,0 +1,35 @@ +package types + +import ( + fmt "fmt" + "strings" +) + +// FractionalBalances is a slice of FractionalBalance +type FractionalBalances []FractionalBalance + +// Validate returns an error if any FractionalBalance in the slice is invalid. +func (fbs FractionalBalances) Validate() error { + seenAddresses := make(map[string]struct{}) + + for _, fb := range fbs { + // Individual FractionalBalance validation + if err := fb.Validate(); err != nil { + return fmt.Errorf("invalid fractional balance for %s: %w", fb.Address, err) + } + + // Make addresses all lowercase for unique check, as ALL UPPER is also + // a valid address. + lowerAddr := strings.ToLower(fb.Address) + + // If this is a duplicate address, return an error + if _, found := seenAddresses[lowerAddr]; found { + return fmt.Errorf("duplicate address %v", lowerAddr) + } + + // Mark it as seen + seenAddresses[lowerAddr] = struct{}{} + } + + return nil +} diff --git a/x/precisebank/types/fractional_balances_test.go b/x/precisebank/types/fractional_balances_test.go new file mode 100644 index 00000000..7583a13a --- /dev/null +++ b/x/precisebank/types/fractional_balances_test.go @@ -0,0 +1,86 @@ +package types_test + +import ( + "strings" + "testing" + + sdkmath "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/kava-labs/kava/app" + "github.com/kava-labs/kava/x/precisebank/types" + "github.com/stretchr/testify/require" +) + +func TestFractionalBalances_Validate(t *testing.T) { + app.SetSDKConfig() + + tests := []struct { + name string + fbs types.FractionalBalances + wantErr string + }{ + { + "valid - empty", + types.FractionalBalances{}, + "", + }, + { + "valid - nil", + nil, + "", + }, + { + "valid - multiple balances", + types.FractionalBalances{ + types.NewFractionalBalance(sdk.AccAddress{1}.String(), sdkmath.NewInt(100)), + types.NewFractionalBalance(sdk.AccAddress{2}.String(), sdkmath.NewInt(100)), + types.NewFractionalBalance(sdk.AccAddress{3}.String(), sdkmath.NewInt(100)), + }, + "", + }, + { + "invalid - single invalid balance", + types.FractionalBalances{ + types.NewFractionalBalance(sdk.AccAddress{1}.String(), sdkmath.NewInt(100)), + types.NewFractionalBalance(sdk.AccAddress{2}.String(), sdkmath.NewInt(-1)), + types.NewFractionalBalance(sdk.AccAddress{3}.String(), sdkmath.NewInt(100)), + }, + "invalid fractional balance for kava1qg7c45n6: non-positive amount -1", + }, + { + "invalid - duplicate address", + types.FractionalBalances{ + types.NewFractionalBalance(sdk.AccAddress{1}.String(), sdkmath.NewInt(100)), + types.NewFractionalBalance(sdk.AccAddress{1}.String(), sdkmath.NewInt(100)), + }, + "duplicate address kava1qy0xn7za", + }, + { + "invalid - duplicate address upper/lower case", + types.FractionalBalances{ + types.NewFractionalBalance( + strings.ToLower(sdk.AccAddress{1}.String()), + sdkmath.NewInt(100), + ), + types.NewFractionalBalance( + strings.ToUpper(sdk.AccAddress{1}.String()), + sdkmath.NewInt(100), + ), + }, + "duplicate address kava1qy0xn7za", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := tt.fbs.Validate() + if tt.wantErr == "" { + require.NoError(t, err) + return + } + + require.Error(t, err) + require.EqualError(t, err, tt.wantErr) + }) + } +} diff --git a/x/precisebank/types/genesis.go b/x/precisebank/types/genesis.go index aa301b33..9b88ee57 100644 --- a/x/precisebank/types/genesis.go +++ b/x/precisebank/types/genesis.go @@ -1,17 +1,30 @@ package types -// Validate performs basic validation of supply genesis data returning an -// error for any failed validation criteria. +import "fmt" + +// Validate performs basic validation of genesis data returning an error for +// any failed validation criteria. func (gs *GenesisState) Validate() error { + 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 + return nil } // NewGenesisState creates a new genesis state. -func NewGenesisState() *GenesisState { - return &GenesisState{} +func NewGenesisState(balances FractionalBalances) *GenesisState { + return &GenesisState{ + Balances: balances, + } } // DefaultGenesisState returns a default genesis state. func DefaultGenesisState() *GenesisState { - return NewGenesisState() + return NewGenesisState(FractionalBalances{}) } diff --git a/x/precisebank/types/genesis.pb.go b/x/precisebank/types/genesis.pb.go index d5d2519b..9c0e612c 100644 --- a/x/precisebank/types/genesis.pb.go +++ b/x/precisebank/types/genesis.pb.go @@ -4,7 +4,10 @@ package types import ( + cosmossdk_io_math "cosmossdk.io/math" fmt "fmt" + _ "github.com/cosmos/cosmos-proto" + _ "github.com/cosmos/gogoproto/gogoproto" proto "github.com/cosmos/gogoproto/proto" io "io" math "math" @@ -24,6 +27,8 @@ const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package // GenesisState defines the precisebank module's genesis state. type GenesisState struct { + // balances is a list of all the balances in the precisebank module. + Balances FractionalBalances `protobuf:"bytes,1,rep,name=balances,proto3,castrepeated=FractionalBalances" json:"balances"` } func (m *GenesisState) Reset() { *m = GenesisState{} } @@ -59,24 +64,86 @@ func (m *GenesisState) XXX_DiscardUnknown() { var xxx_messageInfo_GenesisState proto.InternalMessageInfo +func (m *GenesisState) GetBalances() FractionalBalances { + if m != nil { + return m.Balances + } + return nil +} + +// FractionalBalance defines the fractional portion of an account balance +type FractionalBalance struct { + // address is the address of the balance holder. + Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` + // amount indicates amount of only the fractional balance owned by the + // address. FractionalBalance currently only supports tracking 1 single asset, + // e.g. fractional balances of ukava. + Amount cosmossdk_io_math.Int `protobuf:"bytes,2,opt,name=amount,proto3,customtype=cosmossdk.io/math.Int" json:"amount"` +} + +func (m *FractionalBalance) Reset() { *m = FractionalBalance{} } +func (m *FractionalBalance) String() string { return proto.CompactTextString(m) } +func (*FractionalBalance) ProtoMessage() {} +func (*FractionalBalance) Descriptor() ([]byte, []int) { + return fileDescriptor_7f1c47a86fb0d2e0, []int{1} +} +func (m *FractionalBalance) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *FractionalBalance) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_FractionalBalance.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 *FractionalBalance) XXX_Merge(src proto.Message) { + xxx_messageInfo_FractionalBalance.Merge(m, src) +} +func (m *FractionalBalance) XXX_Size() int { + return m.Size() +} +func (m *FractionalBalance) XXX_DiscardUnknown() { + xxx_messageInfo_FractionalBalance.DiscardUnknown(m) +} + +var xxx_messageInfo_FractionalBalance proto.InternalMessageInfo + func init() { proto.RegisterType((*GenesisState)(nil), "kava.precisebank.v1.GenesisState") + proto.RegisterType((*FractionalBalance)(nil), "kava.precisebank.v1.FractionalBalance") } func init() { proto.RegisterFile("kava/precisebank/v1/genesis.proto", fileDescriptor_7f1c47a86fb0d2e0) } var fileDescriptor_7f1c47a86fb0d2e0 = []byte{ - // 145 bytes of a gzipped FileDescriptorProto + // 331 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0xcc, 0x4e, 0x2c, 0x4b, 0xd4, 0x2f, 0x28, 0x4a, 0x4d, 0xce, 0x2c, 0x4e, 0x4d, 0x4a, 0xcc, 0xcb, 0xd6, 0x2f, 0x33, 0xd4, 0x4f, 0x4f, 0xcd, 0x4b, 0x2d, 0xce, 0x2c, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x06, - 0x29, 0xd1, 0x43, 0x52, 0xa2, 0x57, 0x66, 0xa8, 0xc4, 0xc7, 0xc5, 0xe3, 0x0e, 0x51, 0x15, 0x5c, - 0x92, 0x58, 0x92, 0xea, 0xe4, 0x7e, 0xe2, 0x91, 0x1c, 0xe3, 0x85, 0x47, 0x72, 0x8c, 0x0f, 0x1e, - 0xc9, 0x31, 0x4e, 0x78, 0x2c, 0xc7, 0x70, 0xe1, 0xb1, 0x1c, 0xc3, 0x8d, 0xc7, 0x72, 0x0c, 0x51, - 0xba, 0xe9, 0x99, 0x25, 0x19, 0xa5, 0x49, 0x7a, 0xc9, 0xf9, 0xb9, 0xfa, 0x20, 0x93, 0x74, 0x73, - 0x12, 0x93, 0x8a, 0xc1, 0x2c, 0xfd, 0x0a, 0x14, 0x8b, 0x4b, 0x2a, 0x0b, 0x52, 0x8b, 0x93, 0xd8, - 0xc0, 0x96, 0x1a, 0x03, 0x02, 0x00, 0x00, 0xff, 0xff, 0x51, 0x07, 0x09, 0x29, 0x99, 0x00, 0x00, - 0x00, + 0x29, 0xd1, 0x43, 0x52, 0xa2, 0x57, 0x66, 0x28, 0x25, 0x99, 0x9c, 0x5f, 0x9c, 0x9b, 0x5f, 0x1c, + 0x0f, 0x56, 0xa2, 0x0f, 0xe1, 0x40, 0xd4, 0x4b, 0x89, 0xa4, 0xe7, 0xa7, 0xe7, 0x43, 0xc4, 0x41, + 0x2c, 0x88, 0xa8, 0x52, 0x1e, 0x17, 0x8f, 0x3b, 0xc4, 0xd8, 0xe0, 0x92, 0xc4, 0x92, 0x54, 0xa1, + 0x38, 0x2e, 0x8e, 0xa4, 0xc4, 0x9c, 0xc4, 0xbc, 0xe4, 0xd4, 0x62, 0x09, 0x46, 0x05, 0x66, 0x0d, + 0x6e, 0x23, 0x35, 0x3d, 0x2c, 0x16, 0xe9, 0xb9, 0x15, 0x25, 0x26, 0x97, 0x64, 0xe6, 0xe7, 0x25, + 0xe6, 0x38, 0x41, 0x94, 0x3b, 0x49, 0x9d, 0xb8, 0x27, 0xcf, 0xb0, 0xea, 0xbe, 0xbc, 0x10, 0x86, + 0x54, 0x71, 0x10, 0xdc, 0x4c, 0xa5, 0x69, 0x8c, 0x5c, 0x82, 0x18, 0x0a, 0x84, 0x8c, 0xb8, 0xd8, + 0x13, 0x53, 0x52, 0x8a, 0x52, 0x8b, 0x41, 0x96, 0x32, 0x6a, 0x70, 0x3a, 0x49, 0x5c, 0xda, 0xa2, + 0x2b, 0x02, 0x75, 0xbe, 0x23, 0x44, 0x26, 0xb8, 0xa4, 0x28, 0x33, 0x2f, 0x3d, 0x08, 0xa6, 0x50, + 0xc8, 0x99, 0x8b, 0x2d, 0x31, 0x37, 0xbf, 0x34, 0xaf, 0x44, 0x82, 0x09, 0xac, 0x45, 0x1b, 0x64, + 0xff, 0xad, 0x7b, 0xf2, 0xa2, 0x10, 0x6d, 0xc5, 0x29, 0xd9, 0x7a, 0x99, 0xf9, 0xfa, 0xb9, 0x89, + 0x25, 0x19, 0x7a, 0x9e, 0x79, 0x25, 0x97, 0xb6, 0xe8, 0x72, 0x41, 0xcd, 0xf3, 0xcc, 0x2b, 0x09, + 0x82, 0x6a, 0xb5, 0xe2, 0xe8, 0x58, 0x20, 0xcf, 0xf0, 0x62, 0x81, 0x3c, 0x83, 0x93, 0xfb, 0x89, + 0x47, 0x72, 0x8c, 0x17, 0x1e, 0xc9, 0x31, 0x3e, 0x78, 0x24, 0xc7, 0x38, 0xe1, 0xb1, 0x1c, 0xc3, + 0x85, 0xc7, 0x72, 0x0c, 0x37, 0x1e, 0xcb, 0x31, 0x44, 0xe9, 0xa6, 0x67, 0x96, 0x64, 0x94, 0x26, + 0xe9, 0x25, 0xe7, 0xe7, 0xea, 0x83, 0x82, 0x42, 0x37, 0x27, 0x31, 0xa9, 0x18, 0xcc, 0xd2, 0xaf, + 0x40, 0x89, 0xa2, 0x92, 0xca, 0x82, 0xd4, 0xe2, 0x24, 0x36, 0x70, 0xc0, 0x1a, 0x03, 0x02, 0x00, + 0x00, 0xff, 0xff, 0xf4, 0x2e, 0xbf, 0x96, 0xc3, 0x01, 0x00, 0x00, } func (m *GenesisState) Marshal() (dAtA []byte, err error) { @@ -99,6 +166,60 @@ func (m *GenesisState) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if len(m.Balances) > 0 { + for iNdEx := len(m.Balances) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Balances[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *FractionalBalance) 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 *FractionalBalance) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *FractionalBalance) 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 = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + if len(m.Address) > 0 { + i -= len(m.Address) + copy(dAtA[i:], m.Address) + i = encodeVarintGenesis(dAtA, i, uint64(len(m.Address))) + i-- + dAtA[i] = 0xa + } return len(dAtA) - i, nil } @@ -119,6 +240,27 @@ func (m *GenesisState) Size() (n int) { } var l int _ = l + if len(m.Balances) > 0 { + for _, e := range m.Balances { + l = e.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + } + return n +} + +func (m *FractionalBalance) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Address) + if l > 0 { + n += 1 + l + sovGenesis(uint64(l)) + } + l = m.Amount.Size() + n += 1 + l + sovGenesis(uint64(l)) return n } @@ -157,6 +299,156 @@ func (m *GenesisState) Unmarshal(dAtA []byte) error { return fmt.Errorf("proto: GenesisState: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Balances", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Balances = append(m.Balances, FractionalBalance{}) + if err := m.Balances[len(m.Balances)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenesis(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenesis + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *FractionalBalance) 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 ErrIntOverflowGenesis + } + 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: FractionalBalance: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: FractionalBalance: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Address", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + 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 ErrInvalidLengthGenesis + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Address = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + 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 ErrIntOverflowGenesis + } + 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 ErrInvalidLengthGenesis + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + 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 := skipGenesis(dAtA[iNdEx:]) diff --git a/x/precisebank/types/genesis_test.go b/x/precisebank/types/genesis_test.go index bd49a2fd..422bdfe6 100644 --- a/x/precisebank/types/genesis_test.go +++ b/x/precisebank/types/genesis_test.go @@ -3,6 +3,8 @@ package types_test import ( "testing" + sdkmath "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/kava-labs/kava/x/precisebank/types" "github.com/stretchr/testify/require" ) @@ -10,26 +12,39 @@ import ( func TestGenesisStateValidate(t *testing.T) { testCases := []struct { name string - genesisState types.GenesisState + genesisState *types.GenesisState expErr bool }{ { "empty genesisState", - types.GenesisState{}, + &types.GenesisState{}, false, }, { - "valid genesisState", - // TODO: Fill out fields - types.GenesisState{}, + "valid genesisState - nil", + types.NewGenesisState(nil), false, }, - // TODO: Sum of balances does not equal an integer amount - // { - // "invalid balances", - // types.GenesisState{}, - // true, - // }, + { + "invalid - calls (single) FractionalBalance.Validate()", + types.NewGenesisState( + types.FractionalBalances{ + types.NewFractionalBalance(sdk.AccAddress{1}.String(), sdkmath.NewInt(1)), + types.NewFractionalBalance(sdk.AccAddress{2}.String(), sdkmath.NewInt(-1)), + }, + ), + true, + }, + { + "invalid - calls (multi) FractionalBalances.Validate()", + types.NewGenesisState( + types.FractionalBalances{ + types.NewFractionalBalance(sdk.AccAddress{1}.String(), sdkmath.NewInt(1)), + types.NewFractionalBalance(sdk.AccAddress{1}.String(), sdkmath.NewInt(1)), + }, + ), + true, + }, } for _, tc := range testCases {