x/feegrant audit: clean up / add test coverage to types package (#9193)
* Squashed commit of the following: commit 58dc50051226a9eeb8d0ebea5bb0908fe5b9637f Author: technicallyty <48813565+tytech3@users.noreply.github.com> Date: Fri Apr 23 15:09:27 2021 -0700 remove comments commit a84107e1b3eaa31324cb0f4f097b49f02af79c69 Author: technicallyty <48813565+tytech3@users.noreply.github.com> Date: Fri Apr 23 15:02:41 2021 -0700 add tests for msgs.go commit 2ad16869237e9631b402c93cde650c3fc554daf2 Author: technicallyty <48813565+tytech3@users.noreply.github.com> Date: Fri Apr 23 13:20:21 2021 -0700 specify test name commit b7121277c9be586a7c80d010ec401e50b510e02a Merge: c0c134d9713c65c3dacd
Author: technicallyty <48813565+tytech3@users.noreply.github.com> Date: Fri Apr 23 12:54:55 2021 -0700 Merge branch 'master' into ty-9115-types_tests commit c0c134d97107194dc4f9d3c501a15d023ae083e5 Author: technicallyty <48813565+tytech3@users.noreply.github.com> Date: Thu Apr 22 19:59:11 2021 -0700 -add test case to cli_test.go for filtered fee -clean up identifiers -remove redundant import alias from filtered_fee.go commit f7ab3699da39be3ab886f96962d28d23438d2e8e Author: technicallyty <48813565+tytech3@users.noreply.github.com> Date: Thu Apr 22 09:57:31 2021 -0700 remove unecessary constant commit 9db59a82a7337cf5a7a3569c6900a44a6c81e8b4 Merge: a3e75ceb8ae28271b8e6
Author: technicallyty <48813565+tytech3@users.noreply.github.com> Date: Thu Apr 22 09:19:20 2021 -0700 Merge branch 'master' into ty-9115-types_tests commit a3e75ceb8a510ad9db43dd96073c43b7a8b062b0 Merge: 4d3dafab85bffcae54a1
Author: technicallyty <48813565+tytech3@users.noreply.github.com> Date: Wed Apr 21 12:04:39 2021 -0700 Merge branch 'master' into ty-9115-types_tests commit 4d3dafab85d85526a7c94b045289605289ee6b0d Author: technicallyty <48813565+tytech3@users.noreply.github.com> Date: Wed Apr 21 12:03:41 2021 -0700 cleanup id's / add test case commit e8f6924931ba95e592bfc3057ba167700458da41 Author: technicallyty <48813565+tytech3@users.noreply.github.com> Date: Wed Apr 21 10:55:29 2021 -0700 add test for 0 allowance / remove unused field on exp test commit 516b6ef89a4582ad681cc6c5c101cf50a9a54fb5 Author: technicallyty <48813565+tytech3@users.noreply.github.com> Date: Tue Apr 20 15:22:48 2021 -0700 make names more clear, remove unused field * fix imports * fix tests * rename test field Co-authored-by: technicallyty <48813565+tytech3@users.noreply.github.com> Co-authored-by: Marie Gauthier <marie.gauthier63@gmail.com> Co-authored-by: MD Aleem <72057206+aleem1314@users.noreply.github.com>
This commit is contained in:
parent
f45838ff3c
commit
0cbed20db8
|
@ -649,7 +649,7 @@ func (s *IntegrationTestSuite) TestFilteredFeeAllowance() {
|
||||||
}
|
}
|
||||||
spendLimit := sdk.NewCoin("stake", sdk.NewInt(1000))
|
spendLimit := sdk.NewCoin("stake", sdk.NewInt(1000))
|
||||||
|
|
||||||
allowMsgs := "/cosmos.gov.v1beta1.Msg/SubmitProposal"
|
allowMsgs := "/cosmos.gov.v1beta1.Msg/SubmitProposal,weighted_vote"
|
||||||
|
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
name string
|
name string
|
||||||
|
@ -659,10 +659,10 @@ func (s *IntegrationTestSuite) TestFilteredFeeAllowance() {
|
||||||
expectedCode uint32
|
expectedCode uint32
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
"wrong granter",
|
"invalid granter address",
|
||||||
append(
|
append(
|
||||||
[]string{
|
[]string{
|
||||||
"wrong granter",
|
"not an address",
|
||||||
"cosmos1nph3cfzk6trsmfxkeu943nvach5qw4vwstnvkl",
|
"cosmos1nph3cfzk6trsmfxkeu943nvach5qw4vwstnvkl",
|
||||||
fmt.Sprintf("--%s=%s", cli.FlagAllowedMsgs, allowMsgs),
|
fmt.Sprintf("--%s=%s", cli.FlagAllowedMsgs, allowMsgs),
|
||||||
fmt.Sprintf("--%s=%s", cli.FlagSpendLimit, spendLimit.String()),
|
fmt.Sprintf("--%s=%s", cli.FlagSpendLimit, spendLimit.String()),
|
||||||
|
@ -673,11 +673,11 @@ func (s *IntegrationTestSuite) TestFilteredFeeAllowance() {
|
||||||
true, &sdk.TxResponse{}, 0,
|
true, &sdk.TxResponse{}, 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"wrong grantee",
|
"invalid grantee address",
|
||||||
append(
|
append(
|
||||||
[]string{
|
[]string{
|
||||||
granter.String(),
|
granter.String(),
|
||||||
"wrong grantee",
|
"not an address",
|
||||||
fmt.Sprintf("--%s=%s", cli.FlagAllowedMsgs, allowMsgs),
|
fmt.Sprintf("--%s=%s", cli.FlagAllowedMsgs, allowMsgs),
|
||||||
fmt.Sprintf("--%s=%s", cli.FlagSpendLimit, spendLimit.String()),
|
fmt.Sprintf("--%s=%s", cli.FlagSpendLimit, spendLimit.String()),
|
||||||
fmt.Sprintf("--%s=%s", flags.FlagFrom, granter),
|
fmt.Sprintf("--%s=%s", flags.FlagFrom, granter),
|
||||||
|
@ -753,22 +753,30 @@ func (s *IntegrationTestSuite) TestFilteredFeeAllowance() {
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
name string
|
name string
|
||||||
malleate func() (testutil.BufferWriter, error)
|
malleate func() (testutil.BufferWriter, error)
|
||||||
expectErr bool
|
|
||||||
respType proto.Message
|
respType proto.Message
|
||||||
expectedCode uint32
|
expectedCode uint32
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
"valid tx",
|
"valid proposal tx",
|
||||||
func() (testutil.BufferWriter, error) {
|
func() (testutil.BufferWriter, error) {
|
||||||
return govtestutil.MsgSubmitProposal(val.ClientCtx, grantee.String(),
|
return govtestutil.MsgSubmitProposal(val.ClientCtx, grantee.String(),
|
||||||
"Text Proposal", "No desc", govtypes.ProposalTypeText,
|
"Text Proposal", "No desc", govtypes.ProposalTypeText,
|
||||||
fmt.Sprintf("--%s=%s", flags.FlagFeeAccount, granter.String()),
|
fmt.Sprintf("--%s=%s", flags.FlagFeeAccount, granter.String()),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
false,
|
|
||||||
&sdk.TxResponse{},
|
&sdk.TxResponse{},
|
||||||
0,
|
0,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"valid weighted_vote tx",
|
||||||
|
func() (testutil.BufferWriter, error) {
|
||||||
|
return govtestutil.MsgVote(val.ClientCtx, grantee.String(), "0", "yes",
|
||||||
|
fmt.Sprintf("--%s=%s", flags.FlagFeeAccount, granter.String()),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
&sdk.TxResponse{},
|
||||||
|
2,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"should fail with unauthorized msgs",
|
"should fail with unauthorized msgs",
|
||||||
func() (testutil.BufferWriter, error) {
|
func() (testutil.BufferWriter, error) {
|
||||||
|
@ -784,7 +792,8 @@ func (s *IntegrationTestSuite) TestFilteredFeeAllowance() {
|
||||||
cmd := cli.NewCmdFeeGrant()
|
cmd := cli.NewCmdFeeGrant()
|
||||||
return clitestutil.ExecTestCLICmd(clientCtx, cmd, args)
|
return clitestutil.ExecTestCLICmd(clientCtx, cmd, args)
|
||||||
},
|
},
|
||||||
false, &sdk.TxResponse{}, 7,
|
&sdk.TxResponse{},
|
||||||
|
7,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -793,16 +802,10 @@ func (s *IntegrationTestSuite) TestFilteredFeeAllowance() {
|
||||||
|
|
||||||
s.Run(tc.name, func() {
|
s.Run(tc.name, func() {
|
||||||
out, err := tc.malleate()
|
out, err := tc.malleate()
|
||||||
|
s.Require().NoError(err)
|
||||||
if tc.expectErr {
|
s.Require().NoError(clientCtx.JSONMarshaler.UnmarshalJSON(out.Bytes(), tc.respType), out.String())
|
||||||
s.Require().Error(err)
|
txResp := tc.respType.(*sdk.TxResponse)
|
||||||
} else {
|
s.Require().Equal(tc.expectedCode, txResp.Code, out.String())
|
||||||
s.Require().NoError(err)
|
|
||||||
s.Require().NoError(clientCtx.JSONMarshaler.UnmarshalJSON(out.Bytes(), tc.respType), out.String())
|
|
||||||
|
|
||||||
txResp := tc.respType.(*sdk.TxResponse)
|
|
||||||
s.Require().Equal(tc.expectedCode, txResp.Code, out.String())
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,53 +22,47 @@ func TestBasicFeeValidAllow(t *testing.T) {
|
||||||
leftAtom := sdk.NewCoins(sdk.NewInt64Coin("atom", 512))
|
leftAtom := sdk.NewCoins(sdk.NewInt64Coin("atom", 512))
|
||||||
|
|
||||||
cases := map[string]struct {
|
cases := map[string]struct {
|
||||||
allow *types.BasicFeeAllowance
|
allowance *types.BasicFeeAllowance
|
||||||
// all other checks are ignored if valid=false
|
// all other checks are ignored if valid=false
|
||||||
fee sdk.Coins
|
fee sdk.Coins
|
||||||
blockHeight int64
|
blockHeight int64
|
||||||
valid bool
|
|
||||||
accept bool
|
accept bool
|
||||||
remove bool
|
remove bool
|
||||||
remains sdk.Coins
|
remains sdk.Coins
|
||||||
}{
|
}{
|
||||||
"empty": {
|
"empty": {
|
||||||
allow: &types.BasicFeeAllowance{},
|
allowance: &types.BasicFeeAllowance{},
|
||||||
valid: true,
|
|
||||||
accept: true,
|
accept: true,
|
||||||
},
|
},
|
||||||
"small fee without expire": {
|
"small fee without expire": {
|
||||||
allow: &types.BasicFeeAllowance{
|
allowance: &types.BasicFeeAllowance{
|
||||||
SpendLimit: atom,
|
SpendLimit: atom,
|
||||||
},
|
},
|
||||||
valid: true,
|
|
||||||
fee: smallAtom,
|
fee: smallAtom,
|
||||||
accept: true,
|
accept: true,
|
||||||
remove: false,
|
remove: false,
|
||||||
remains: leftAtom,
|
remains: leftAtom,
|
||||||
},
|
},
|
||||||
"all fee without expire": {
|
"all fee without expire": {
|
||||||
allow: &types.BasicFeeAllowance{
|
allowance: &types.BasicFeeAllowance{
|
||||||
SpendLimit: smallAtom,
|
SpendLimit: smallAtom,
|
||||||
},
|
},
|
||||||
valid: true,
|
|
||||||
fee: smallAtom,
|
fee: smallAtom,
|
||||||
accept: true,
|
accept: true,
|
||||||
remove: true,
|
remove: true,
|
||||||
},
|
},
|
||||||
"wrong fee": {
|
"wrong fee": {
|
||||||
allow: &types.BasicFeeAllowance{
|
allowance: &types.BasicFeeAllowance{
|
||||||
SpendLimit: smallAtom,
|
SpendLimit: smallAtom,
|
||||||
},
|
},
|
||||||
valid: true,
|
|
||||||
fee: eth,
|
fee: eth,
|
||||||
accept: false,
|
accept: false,
|
||||||
},
|
},
|
||||||
"non-expired": {
|
"non-expired": {
|
||||||
allow: &types.BasicFeeAllowance{
|
allowance: &types.BasicFeeAllowance{
|
||||||
SpendLimit: atom,
|
SpendLimit: atom,
|
||||||
Expiration: types.ExpiresAtHeight(100),
|
Expiration: types.ExpiresAtHeight(100),
|
||||||
},
|
},
|
||||||
valid: true,
|
|
||||||
fee: smallAtom,
|
fee: smallAtom,
|
||||||
blockHeight: 85,
|
blockHeight: 85,
|
||||||
accept: true,
|
accept: true,
|
||||||
|
@ -76,40 +70,36 @@ func TestBasicFeeValidAllow(t *testing.T) {
|
||||||
remains: leftAtom,
|
remains: leftAtom,
|
||||||
},
|
},
|
||||||
"expired": {
|
"expired": {
|
||||||
allow: &types.BasicFeeAllowance{
|
allowance: &types.BasicFeeAllowance{
|
||||||
SpendLimit: atom,
|
SpendLimit: atom,
|
||||||
Expiration: types.ExpiresAtHeight(100),
|
Expiration: types.ExpiresAtHeight(100),
|
||||||
},
|
},
|
||||||
valid: true,
|
|
||||||
fee: smallAtom,
|
fee: smallAtom,
|
||||||
blockHeight: 121,
|
blockHeight: 121,
|
||||||
accept: false,
|
accept: false,
|
||||||
remove: true,
|
remove: true,
|
||||||
},
|
},
|
||||||
"fee more than allowed": {
|
"fee more than allowed": {
|
||||||
allow: &types.BasicFeeAllowance{
|
allowance: &types.BasicFeeAllowance{
|
||||||
SpendLimit: atom,
|
SpendLimit: atom,
|
||||||
Expiration: types.ExpiresAtHeight(100),
|
Expiration: types.ExpiresAtHeight(100),
|
||||||
},
|
},
|
||||||
valid: true,
|
|
||||||
fee: bigAtom,
|
fee: bigAtom,
|
||||||
blockHeight: 85,
|
blockHeight: 85,
|
||||||
accept: false,
|
accept: false,
|
||||||
},
|
},
|
||||||
"with out spend limit": {
|
"with out spend limit": {
|
||||||
allow: &types.BasicFeeAllowance{
|
allowance: &types.BasicFeeAllowance{
|
||||||
Expiration: types.ExpiresAtHeight(100),
|
Expiration: types.ExpiresAtHeight(100),
|
||||||
},
|
},
|
||||||
valid: true,
|
|
||||||
fee: bigAtom,
|
fee: bigAtom,
|
||||||
blockHeight: 85,
|
blockHeight: 85,
|
||||||
accept: true,
|
accept: true,
|
||||||
},
|
},
|
||||||
"expired no spend limit": {
|
"expired no spend limit": {
|
||||||
allow: &types.BasicFeeAllowance{
|
allowance: &types.BasicFeeAllowance{
|
||||||
Expiration: types.ExpiresAtHeight(100),
|
Expiration: types.ExpiresAtHeight(100),
|
||||||
},
|
},
|
||||||
valid: true,
|
|
||||||
fee: bigAtom,
|
fee: bigAtom,
|
||||||
blockHeight: 120,
|
blockHeight: 120,
|
||||||
accept: false,
|
accept: false,
|
||||||
|
@ -119,26 +109,22 @@ func TestBasicFeeValidAllow(t *testing.T) {
|
||||||
for name, stc := range cases {
|
for name, stc := range cases {
|
||||||
tc := stc // to make scopelint happy
|
tc := stc // to make scopelint happy
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
err := tc.allow.ValidateBasic()
|
err := tc.allowance.ValidateBasic()
|
||||||
if !tc.valid {
|
|
||||||
require.Error(t, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
ctx := app.BaseApp.NewContext(false, tmproto.Header{}).WithBlockHeight(tc.blockHeight)
|
ctx := app.BaseApp.NewContext(false, tmproto.Header{}).WithBlockHeight(tc.blockHeight)
|
||||||
|
|
||||||
// now try to deduct
|
// now try to deduct
|
||||||
remove, err := tc.allow.Accept(ctx, tc.fee, []sdk.Msg{})
|
removed, err := tc.allowance.Accept(ctx, tc.fee, []sdk.Msg{})
|
||||||
if !tc.accept {
|
if !tc.accept {
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
require.Equal(t, tc.remove, remove)
|
require.Equal(t, tc.remove, removed)
|
||||||
if !remove {
|
if !removed {
|
||||||
assert.Equal(t, tc.allow.SpendLimit, tc.remains)
|
assert.Equal(t, tc.allowance.SpendLimit, tc.remains)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,33 +14,28 @@ func TestExpiresAt(t *testing.T) {
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
|
|
||||||
cases := map[string]struct {
|
cases := map[string]struct {
|
||||||
example types.ExpiresAt
|
expires types.ExpiresAt
|
||||||
valid bool
|
|
||||||
zero bool
|
zero bool
|
||||||
before types.ExpiresAt
|
before types.ExpiresAt
|
||||||
after types.ExpiresAt
|
after types.ExpiresAt
|
||||||
}{
|
}{
|
||||||
"basic": {
|
"basic": {
|
||||||
example: types.ExpiresAtHeight(100),
|
expires: types.ExpiresAtHeight(100),
|
||||||
valid: true,
|
|
||||||
before: types.ExpiresAtHeight(50),
|
before: types.ExpiresAtHeight(50),
|
||||||
after: types.ExpiresAtHeight(122),
|
after: types.ExpiresAtHeight(122),
|
||||||
},
|
},
|
||||||
"zero": {
|
"zero": {
|
||||||
example: types.ExpiresAt{},
|
expires: types.ExpiresAt{},
|
||||||
zero: true,
|
zero: true,
|
||||||
valid: true,
|
|
||||||
before: types.ExpiresAtHeight(1),
|
before: types.ExpiresAtHeight(1),
|
||||||
},
|
},
|
||||||
"match height": {
|
"match height": {
|
||||||
example: types.ExpiresAtHeight(1000),
|
expires: types.ExpiresAtHeight(1000),
|
||||||
valid: true,
|
|
||||||
before: types.ExpiresAtHeight(999),
|
before: types.ExpiresAtHeight(999),
|
||||||
after: types.ExpiresAtHeight(1000),
|
after: types.ExpiresAtHeight(1000),
|
||||||
},
|
},
|
||||||
"match time": {
|
"match time": {
|
||||||
example: types.ExpiresAtTime(now),
|
expires: types.ExpiresAtTime(now),
|
||||||
valid: true,
|
|
||||||
before: types.ExpiresAtTime(now.Add(-1 * time.Second)),
|
before: types.ExpiresAtTime(now.Add(-1 * time.Second)),
|
||||||
after: types.ExpiresAtTime(now.Add(1 * time.Second)),
|
after: types.ExpiresAtTime(now.Add(1 * time.Second)),
|
||||||
},
|
},
|
||||||
|
@ -49,19 +44,15 @@ func TestExpiresAt(t *testing.T) {
|
||||||
for name, stc := range cases {
|
for name, stc := range cases {
|
||||||
tc := stc // to make scopelint happy
|
tc := stc // to make scopelint happy
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
err := tc.example.ValidateBasic()
|
err := tc.expires.ValidateBasic()
|
||||||
assert.Equal(t, tc.zero, tc.example.Undefined())
|
assert.Equal(t, tc.zero, tc.expires.Undefined())
|
||||||
if !tc.valid {
|
|
||||||
require.Error(t, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
if !tc.before.Undefined() {
|
if !tc.before.Undefined() {
|
||||||
assert.Equal(t, false, tc.example.IsExpired(tc.before.GetTime(), tc.before.GetHeight()))
|
assert.Equal(t, false, tc.expires.IsExpired(tc.before.GetTime(), tc.before.GetHeight()))
|
||||||
}
|
}
|
||||||
if !tc.after.Undefined() {
|
if !tc.after.Undefined() {
|
||||||
assert.Equal(t, true, tc.example.IsExpired(tc.after.GetTime(), tc.after.GetHeight()))
|
assert.Equal(t, true, tc.expires.IsExpired(tc.after.GetTime(), tc.after.GetHeight()))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,11 +3,10 @@ package types
|
||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
proto "github.com/gogo/protobuf/proto"
|
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/codec/types"
|
"github.com/cosmos/cosmos-sdk/codec/types"
|
||||||
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"
|
||||||
|
"github.com/gogo/protobuf/proto"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO: Revisit this once we have propoer gas fee framework.
|
// TODO: Revisit this once we have propoer gas fee framework.
|
||||||
|
|
|
@ -2,8 +2,8 @@ package types_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/simapp"
|
"github.com/cosmos/cosmos-sdk/simapp"
|
||||||
|
@ -18,66 +18,70 @@ func TestGrant(t *testing.T) {
|
||||||
addr2, err := sdk.AccAddressFromBech32("cosmos1p9qh4ldfd6n0qehujsal4k7g0e37kel90rc4ts")
|
addr2, err := sdk.AccAddressFromBech32("cosmos1p9qh4ldfd6n0qehujsal4k7g0e37kel90rc4ts")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
atom := sdk.NewCoins(sdk.NewInt64Coin("atom", 555))
|
atom := sdk.NewCoins(sdk.NewInt64Coin("atom", 555))
|
||||||
|
zeroAtoms := sdk.NewCoins(sdk.NewInt64Coin("atom", 0))
|
||||||
goodGrant, err := types.NewFeeAllowanceGrant(addr2, addr, &types.BasicFeeAllowance{
|
|
||||||
SpendLimit: atom,
|
|
||||||
Expiration: types.ExpiresAtHeight(100),
|
|
||||||
})
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
noGranteeGrant, err := types.NewFeeAllowanceGrant(addr2, nil, &types.BasicFeeAllowance{
|
|
||||||
SpendLimit: atom,
|
|
||||||
Expiration: types.ExpiresAtHeight(100),
|
|
||||||
})
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
noGranterGrant, err := types.NewFeeAllowanceGrant(nil, addr, &types.BasicFeeAllowance{
|
|
||||||
SpendLimit: atom,
|
|
||||||
Expiration: types.ExpiresAtHeight(100),
|
|
||||||
})
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
selfGrant, err := types.NewFeeAllowanceGrant(addr2, addr2, &types.BasicFeeAllowance{
|
|
||||||
SpendLimit: atom,
|
|
||||||
Expiration: types.ExpiresAtHeight(100),
|
|
||||||
})
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
badAllowanceGrant, err := types.NewFeeAllowanceGrant(addr2, addr, &types.BasicFeeAllowance{
|
|
||||||
SpendLimit: atom,
|
|
||||||
Expiration: types.ExpiresAtHeight(-1),
|
|
||||||
})
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
cdc := app.AppCodec()
|
cdc := app.AppCodec()
|
||||||
// RegisterLegacyAminoCodec(cdc)
|
|
||||||
|
|
||||||
cases := map[string]struct {
|
cases := map[string]struct {
|
||||||
grant types.FeeAllowanceGrant
|
granter sdk.AccAddress
|
||||||
|
grantee sdk.AccAddress
|
||||||
|
limit sdk.Coins
|
||||||
|
expires types.ExpiresAt
|
||||||
valid bool
|
valid bool
|
||||||
}{
|
}{
|
||||||
"good": {
|
"good": {
|
||||||
grant: goodGrant,
|
granter: addr2,
|
||||||
|
grantee: addr,
|
||||||
|
limit: atom,
|
||||||
|
expires: types.ExpiresAtHeight(100),
|
||||||
valid: true,
|
valid: true,
|
||||||
},
|
},
|
||||||
"no grantee": {
|
"no grantee": {
|
||||||
grant: noGranteeGrant,
|
granter: addr2,
|
||||||
|
grantee: nil,
|
||||||
|
limit: atom,
|
||||||
|
expires: types.ExpiresAtHeight(100),
|
||||||
|
valid: false,
|
||||||
},
|
},
|
||||||
"no granter": {
|
"no granter": {
|
||||||
grant: noGranterGrant,
|
granter: nil,
|
||||||
|
grantee: addr,
|
||||||
|
limit: atom,
|
||||||
|
expires: types.ExpiresAtHeight(100),
|
||||||
|
valid: false,
|
||||||
},
|
},
|
||||||
"self-grant": {
|
"self-grant": {
|
||||||
grant: selfGrant,
|
granter: addr2,
|
||||||
|
grantee: addr2,
|
||||||
|
limit: atom,
|
||||||
|
expires: types.ExpiresAtHeight(100),
|
||||||
|
valid: false,
|
||||||
},
|
},
|
||||||
"bad allowance": {
|
"bad height": {
|
||||||
grant: badAllowanceGrant,
|
granter: addr2,
|
||||||
|
grantee: addr,
|
||||||
|
limit: atom,
|
||||||
|
expires: types.ExpiresAtHeight(-100),
|
||||||
|
valid: false,
|
||||||
|
},
|
||||||
|
"zero allowance": {
|
||||||
|
granter: addr2,
|
||||||
|
grantee: addr,
|
||||||
|
limit: zeroAtoms,
|
||||||
|
expires: types.ExpiresAtTime(time.Now().Add(3 * time.Hour)),
|
||||||
|
valid: false,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for name, tc := range cases {
|
for name, tc := range cases {
|
||||||
tc := tc
|
tc := tc
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
err := tc.grant.ValidateBasic()
|
grant, err := types.NewFeeAllowanceGrant(tc.granter, tc.grantee, &types.BasicFeeAllowance{
|
||||||
|
SpendLimit: tc.limit,
|
||||||
|
Expiration: tc.expires,
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
err = grant.ValidateBasic()
|
||||||
|
|
||||||
if !tc.valid {
|
if !tc.valid {
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
return
|
return
|
||||||
|
@ -85,7 +89,7 @@ func TestGrant(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// if it is valid, let's try to serialize, deserialize, and make sure it matches
|
// if it is valid, let's try to serialize, deserialize, and make sure it matches
|
||||||
bz, err := cdc.MarshalBinaryBare(&tc.grant)
|
bz, err := cdc.MarshalBinaryBare(&grant)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
var loaded types.FeeAllowanceGrant
|
var loaded types.FeeAllowanceGrant
|
||||||
err = cdc.UnmarshalBinaryBare(bz, &loaded)
|
err = cdc.UnmarshalBinaryBare(bz, &loaded)
|
||||||
|
@ -93,8 +97,7 @@ func TestGrant(t *testing.T) {
|
||||||
|
|
||||||
err = loaded.ValidateBasic()
|
err = loaded.ValidateBasic()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, grant, loaded)
|
||||||
assert.Equal(t, tc.grant, loaded)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,6 +52,7 @@ func (msg MsgGrantFeeAllowance) ValidateBasic() error {
|
||||||
return allowance.ValidateBasic()
|
return allowance.ValidateBasic()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetSigners gets the granter account associated with an allowance
|
||||||
func (msg MsgGrantFeeAllowance) GetSigners() []sdk.AccAddress {
|
func (msg MsgGrantFeeAllowance) GetSigners() []sdk.AccAddress {
|
||||||
granter, err := sdk.AccAddressFromBech32(msg.Granter)
|
granter, err := sdk.AccAddressFromBech32(msg.Granter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -76,6 +77,8 @@ func (msg MsgGrantFeeAllowance) UnpackInterfaces(unpacker types.AnyUnpacker) err
|
||||||
return unpacker.UnpackAny(msg.Allowance, &allowance)
|
return unpacker.UnpackAny(msg.Allowance, &allowance)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewMsgRevokeFeeAllowance returns a message to revoke a fee allowance for a given
|
||||||
|
// granter and grantee
|
||||||
//nolint:interfacer
|
//nolint:interfacer
|
||||||
func NewMsgRevokeFeeAllowance(granter sdk.AccAddress, grantee sdk.AccAddress) MsgRevokeFeeAllowance {
|
func NewMsgRevokeFeeAllowance(granter sdk.AccAddress, grantee sdk.AccAddress) MsgRevokeFeeAllowance {
|
||||||
return MsgRevokeFeeAllowance{Granter: granter.String(), Grantee: grantee.String()}
|
return MsgRevokeFeeAllowance{Granter: granter.String(), Grantee: grantee.String()}
|
||||||
|
@ -88,10 +91,15 @@ func (msg MsgRevokeFeeAllowance) ValidateBasic() error {
|
||||||
if msg.Grantee == "" {
|
if msg.Grantee == "" {
|
||||||
return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "missing grantee address")
|
return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "missing grantee address")
|
||||||
}
|
}
|
||||||
|
if msg.Grantee == msg.Granter {
|
||||||
|
return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "addresses must be different")
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetSigners gets the granter address associated with an Allowance
|
||||||
|
// to revoke.
|
||||||
func (msg MsgRevokeFeeAllowance) GetSigners() []sdk.AccAddress {
|
func (msg MsgRevokeFeeAllowance) GetSigners() []sdk.AccAddress {
|
||||||
granter, err := sdk.AccAddressFromBech32(msg.Granter)
|
granter, err := sdk.AccAddressFromBech32(msg.Granter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -0,0 +1,128 @@
|
||||||
|
package types_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/cosmos/cosmos-sdk/codec"
|
||||||
|
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/feegrant/types"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMsgGrantFeeAllowance(t *testing.T) {
|
||||||
|
cdc := codec.NewProtoCodec(codectypes.NewInterfaceRegistry())
|
||||||
|
addr, _ := sdk.AccAddressFromBech32("cosmos1aeuqja06474dfrj7uqsvukm6rael982kk89mqr")
|
||||||
|
addr2, _ := sdk.AccAddressFromBech32("cosmos1nph3cfzk6trsmfxkeu943nvach5qw4vwstnvkl")
|
||||||
|
atom := sdk.NewCoins(sdk.NewInt64Coin("atom", 555))
|
||||||
|
basic := &types.BasicFeeAllowance{
|
||||||
|
SpendLimit: atom,
|
||||||
|
Expiration: types.ExpiresAtTime(time.Now().Add(3 * time.Hour)),
|
||||||
|
}
|
||||||
|
cases := map[string]struct {
|
||||||
|
grantee sdk.AccAddress
|
||||||
|
granter sdk.AccAddress
|
||||||
|
grant *types.BasicFeeAllowance
|
||||||
|
valid bool
|
||||||
|
}{
|
||||||
|
"valid":{
|
||||||
|
grantee: addr,
|
||||||
|
granter: addr2,
|
||||||
|
grant: basic,
|
||||||
|
valid: true,
|
||||||
|
},
|
||||||
|
"no grantee": {
|
||||||
|
granter: addr2,
|
||||||
|
grantee: sdk.AccAddress{},
|
||||||
|
grant: basic,
|
||||||
|
valid: false,
|
||||||
|
},
|
||||||
|
"no granter": {
|
||||||
|
granter: sdk.AccAddress{},
|
||||||
|
grantee: addr,
|
||||||
|
grant: basic,
|
||||||
|
valid: false,
|
||||||
|
},
|
||||||
|
"grantee == granter":{
|
||||||
|
grantee: addr,
|
||||||
|
granter: addr,
|
||||||
|
grant: basic,
|
||||||
|
valid: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _,tc := range cases {
|
||||||
|
msg, err := types.NewMsgGrantFeeAllowance(tc.grant, tc.granter, tc.grantee)
|
||||||
|
require.NoError(t, err)
|
||||||
|
err = msg.ValidateBasic()
|
||||||
|
|
||||||
|
if tc.valid {
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
addrSlice := msg.GetSigners()
|
||||||
|
require.Equal(t, tc.granter.String(), addrSlice[0].String())
|
||||||
|
|
||||||
|
allowance, err := msg.GetFeeAllowanceI()
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, tc.grant, allowance)
|
||||||
|
|
||||||
|
err = msg.UnpackInterfaces(cdc)
|
||||||
|
require.NoError(t, err)
|
||||||
|
} else {
|
||||||
|
require.Error(t, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMsgRevokeFeeAllowance(t *testing.T) {
|
||||||
|
addr, _ := sdk.AccAddressFromBech32("cosmos1aeuqja06474dfrj7uqsvukm6rael982kk89mqr")
|
||||||
|
addr2, _ := sdk.AccAddressFromBech32("cosmos1nph3cfzk6trsmfxkeu943nvach5qw4vwstnvkl")
|
||||||
|
atom := sdk.NewCoins(sdk.NewInt64Coin("atom", 555))
|
||||||
|
basic := &types.BasicFeeAllowance{
|
||||||
|
SpendLimit: atom,
|
||||||
|
Expiration: types.ExpiresAtTime(time.Now().Add(3 * time.Hour)),
|
||||||
|
}
|
||||||
|
cases := map[string]struct {
|
||||||
|
grantee sdk.AccAddress
|
||||||
|
granter sdk.AccAddress
|
||||||
|
grant *types.BasicFeeAllowance
|
||||||
|
valid bool
|
||||||
|
}{
|
||||||
|
"valid":{
|
||||||
|
grantee: addr,
|
||||||
|
granter: addr2,
|
||||||
|
grant: basic,
|
||||||
|
valid: true,
|
||||||
|
},
|
||||||
|
"no grantee": {
|
||||||
|
granter: addr2,
|
||||||
|
grantee: sdk.AccAddress{},
|
||||||
|
grant: basic,
|
||||||
|
valid: false,
|
||||||
|
},
|
||||||
|
"no granter": {
|
||||||
|
granter: sdk.AccAddress{},
|
||||||
|
grantee: addr,
|
||||||
|
grant: basic,
|
||||||
|
valid: false,
|
||||||
|
},
|
||||||
|
"grantee == granter":{
|
||||||
|
grantee: addr,
|
||||||
|
granter: addr,
|
||||||
|
grant: basic,
|
||||||
|
valid: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _,tc := range cases {
|
||||||
|
msg := types.NewMsgRevokeFeeAllowance(tc.granter, tc.grantee)
|
||||||
|
err := msg.ValidateBasic()
|
||||||
|
if tc.valid {
|
||||||
|
require.NoError(t, err)
|
||||||
|
addrSlice := msg.GetSigners()
|
||||||
|
require.Equal(t, tc.granter.String(), addrSlice[0].String())
|
||||||
|
} else {
|
||||||
|
require.Error(t, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -21,7 +21,7 @@ func TestPeriodicFeeValidAllow(t *testing.T) {
|
||||||
eth := sdk.NewCoins(sdk.NewInt64Coin("eth", 1))
|
eth := sdk.NewCoins(sdk.NewInt64Coin("eth", 1))
|
||||||
|
|
||||||
cases := map[string]struct {
|
cases := map[string]struct {
|
||||||
allow types.PeriodicFeeAllowance
|
allowance types.PeriodicFeeAllowance
|
||||||
// all other checks are ignored if valid=false
|
// all other checks are ignored if valid=false
|
||||||
fee sdk.Coins
|
fee sdk.Coins
|
||||||
blockHeight int64
|
blockHeight int64
|
||||||
|
@ -33,11 +33,11 @@ func TestPeriodicFeeValidAllow(t *testing.T) {
|
||||||
periodReset types.ExpiresAt
|
periodReset types.ExpiresAt
|
||||||
}{
|
}{
|
||||||
"empty": {
|
"empty": {
|
||||||
allow: types.PeriodicFeeAllowance{},
|
allowance: types.PeriodicFeeAllowance{},
|
||||||
valid: false,
|
valid: false,
|
||||||
},
|
},
|
||||||
"only basic": {
|
"only basic": {
|
||||||
allow: types.PeriodicFeeAllowance{
|
allowance: types.PeriodicFeeAllowance{
|
||||||
Basic: types.BasicFeeAllowance{
|
Basic: types.BasicFeeAllowance{
|
||||||
SpendLimit: atom,
|
SpendLimit: atom,
|
||||||
Expiration: types.ExpiresAtHeight(100),
|
Expiration: types.ExpiresAtHeight(100),
|
||||||
|
@ -46,7 +46,7 @@ func TestPeriodicFeeValidAllow(t *testing.T) {
|
||||||
valid: false,
|
valid: false,
|
||||||
},
|
},
|
||||||
"empty basic": {
|
"empty basic": {
|
||||||
allow: types.PeriodicFeeAllowance{
|
allowance: types.PeriodicFeeAllowance{
|
||||||
Period: types.BlockDuration(10),
|
Period: types.BlockDuration(10),
|
||||||
PeriodSpendLimit: smallAtom,
|
PeriodSpendLimit: smallAtom,
|
||||||
PeriodReset: types.ExpiresAtHeight(70),
|
PeriodReset: types.ExpiresAtHeight(70),
|
||||||
|
@ -58,7 +58,7 @@ func TestPeriodicFeeValidAllow(t *testing.T) {
|
||||||
periodReset: types.ExpiresAtHeight(80),
|
periodReset: types.ExpiresAtHeight(80),
|
||||||
},
|
},
|
||||||
"mismatched currencies": {
|
"mismatched currencies": {
|
||||||
allow: types.PeriodicFeeAllowance{
|
allowance: types.PeriodicFeeAllowance{
|
||||||
Basic: types.BasicFeeAllowance{
|
Basic: types.BasicFeeAllowance{
|
||||||
SpendLimit: atom,
|
SpendLimit: atom,
|
||||||
Expiration: types.ExpiresAtHeight(100),
|
Expiration: types.ExpiresAtHeight(100),
|
||||||
|
@ -69,7 +69,7 @@ func TestPeriodicFeeValidAllow(t *testing.T) {
|
||||||
valid: false,
|
valid: false,
|
||||||
},
|
},
|
||||||
"first time": {
|
"first time": {
|
||||||
allow: types.PeriodicFeeAllowance{
|
allowance: types.PeriodicFeeAllowance{
|
||||||
Basic: types.BasicFeeAllowance{
|
Basic: types.BasicFeeAllowance{
|
||||||
SpendLimit: atom,
|
SpendLimit: atom,
|
||||||
Expiration: types.ExpiresAtHeight(100),
|
Expiration: types.ExpiresAtHeight(100),
|
||||||
|
@ -87,7 +87,7 @@ func TestPeriodicFeeValidAllow(t *testing.T) {
|
||||||
periodReset: types.ExpiresAtHeight(85),
|
periodReset: types.ExpiresAtHeight(85),
|
||||||
},
|
},
|
||||||
"same period": {
|
"same period": {
|
||||||
allow: types.PeriodicFeeAllowance{
|
allowance: types.PeriodicFeeAllowance{
|
||||||
Basic: types.BasicFeeAllowance{
|
Basic: types.BasicFeeAllowance{
|
||||||
SpendLimit: atom,
|
SpendLimit: atom,
|
||||||
Expiration: types.ExpiresAtHeight(100),
|
Expiration: types.ExpiresAtHeight(100),
|
||||||
|
@ -107,7 +107,7 @@ func TestPeriodicFeeValidAllow(t *testing.T) {
|
||||||
periodReset: types.ExpiresAtHeight(80),
|
periodReset: types.ExpiresAtHeight(80),
|
||||||
},
|
},
|
||||||
"step one period": {
|
"step one period": {
|
||||||
allow: types.PeriodicFeeAllowance{
|
allowance: types.PeriodicFeeAllowance{
|
||||||
Basic: types.BasicFeeAllowance{
|
Basic: types.BasicFeeAllowance{
|
||||||
SpendLimit: atom,
|
SpendLimit: atom,
|
||||||
Expiration: types.ExpiresAtHeight(100),
|
Expiration: types.ExpiresAtHeight(100),
|
||||||
|
@ -126,7 +126,7 @@ func TestPeriodicFeeValidAllow(t *testing.T) {
|
||||||
periodReset: types.ExpiresAtHeight(80), // one step from last reset, not now
|
periodReset: types.ExpiresAtHeight(80), // one step from last reset, not now
|
||||||
},
|
},
|
||||||
"step limited by global allowance": {
|
"step limited by global allowance": {
|
||||||
allow: types.PeriodicFeeAllowance{
|
allowance: types.PeriodicFeeAllowance{
|
||||||
Basic: types.BasicFeeAllowance{
|
Basic: types.BasicFeeAllowance{
|
||||||
SpendLimit: smallAtom,
|
SpendLimit: smallAtom,
|
||||||
Expiration: types.ExpiresAtHeight(100),
|
Expiration: types.ExpiresAtHeight(100),
|
||||||
|
@ -145,7 +145,7 @@ func TestPeriodicFeeValidAllow(t *testing.T) {
|
||||||
periodReset: types.ExpiresAtHeight(80), // one step from last reset, not now
|
periodReset: types.ExpiresAtHeight(80), // one step from last reset, not now
|
||||||
},
|
},
|
||||||
"expired": {
|
"expired": {
|
||||||
allow: types.PeriodicFeeAllowance{
|
allowance: types.PeriodicFeeAllowance{
|
||||||
Basic: types.BasicFeeAllowance{
|
Basic: types.BasicFeeAllowance{
|
||||||
SpendLimit: atom,
|
SpendLimit: atom,
|
||||||
Expiration: types.ExpiresAtHeight(100),
|
Expiration: types.ExpiresAtHeight(100),
|
||||||
|
@ -160,7 +160,7 @@ func TestPeriodicFeeValidAllow(t *testing.T) {
|
||||||
remove: true,
|
remove: true,
|
||||||
},
|
},
|
||||||
"over period limit": {
|
"over period limit": {
|
||||||
allow: types.PeriodicFeeAllowance{
|
allowance: types.PeriodicFeeAllowance{
|
||||||
Basic: types.BasicFeeAllowance{
|
Basic: types.BasicFeeAllowance{
|
||||||
SpendLimit: atom,
|
SpendLimit: atom,
|
||||||
Expiration: types.ExpiresAtHeight(100),
|
Expiration: types.ExpiresAtHeight(100),
|
||||||
|
@ -181,7 +181,7 @@ func TestPeriodicFeeValidAllow(t *testing.T) {
|
||||||
for name, stc := range cases {
|
for name, stc := range cases {
|
||||||
tc := stc // to make scopelint happy
|
tc := stc // to make scopelint happy
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
err := tc.allow.ValidateBasic()
|
err := tc.allowance.ValidateBasic()
|
||||||
if !tc.valid {
|
if !tc.valid {
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
return
|
return
|
||||||
|
@ -190,18 +190,18 @@ func TestPeriodicFeeValidAllow(t *testing.T) {
|
||||||
|
|
||||||
ctx := app.BaseApp.NewContext(false, tmproto.Header{}).WithBlockHeight(tc.blockHeight)
|
ctx := app.BaseApp.NewContext(false, tmproto.Header{}).WithBlockHeight(tc.blockHeight)
|
||||||
// now try to deduct
|
// now try to deduct
|
||||||
remove, err := tc.allow.Accept(ctx, tc.fee, []sdk.Msg{})
|
removed, err := tc.allowance.Accept(ctx, tc.fee, []sdk.Msg{})
|
||||||
if !tc.accept {
|
if !tc.accept {
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
require.Equal(t, tc.remove, remove)
|
require.Equal(t, tc.remove, removed)
|
||||||
if !remove {
|
if !removed {
|
||||||
assert.Equal(t, tc.remains, tc.allow.Basic.SpendLimit)
|
assert.Equal(t, tc.remains, tc.allowance.Basic.SpendLimit)
|
||||||
assert.Equal(t, tc.remainsPeriod, tc.allow.PeriodCanSpend)
|
assert.Equal(t, tc.remainsPeriod, tc.allowance.PeriodCanSpend)
|
||||||
assert.Equal(t, tc.periodReset, tc.allow.PeriodReset)
|
assert.Equal(t, tc.periodReset, tc.allowance.PeriodReset)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue