github.com/Finschia/finschia-sdk@v0.49.1/x/feegrant/periodic_fee_test.go (about) 1 package feegrant_test 2 3 import ( 4 "testing" 5 "time" 6 7 "github.com/stretchr/testify/assert" 8 "github.com/stretchr/testify/require" 9 tmproto "github.com/tendermint/tendermint/proto/tendermint/types" 10 11 "github.com/Finschia/finschia-sdk/simapp" 12 sdk "github.com/Finschia/finschia-sdk/types" 13 "github.com/Finschia/finschia-sdk/x/feegrant" 14 ) 15 16 func TestPeriodicFeeValidAllow(t *testing.T) { 17 app := simapp.Setup(false) 18 ctx := app.BaseApp.NewContext(false, tmproto.Header{ 19 Time: time.Now(), 20 }) 21 22 atom := sdk.NewCoins(sdk.NewInt64Coin("atom", 555)) 23 smallAtom := sdk.NewCoins(sdk.NewInt64Coin("atom", 43)) 24 leftAtom := sdk.NewCoins(sdk.NewInt64Coin("atom", 512)) 25 oneAtom := sdk.NewCoins(sdk.NewInt64Coin("atom", 1)) 26 eth := sdk.NewCoins(sdk.NewInt64Coin("eth", 1)) 27 28 now := ctx.BlockTime() 29 oneHour := now.Add(1 * time.Hour) 30 twoHours := now.Add(2 * time.Hour) 31 tenMinutes := time.Duration(10) * time.Minute 32 33 cases := map[string]struct { 34 allow feegrant.PeriodicAllowance 35 fee sdk.Coins 36 blockTime time.Time 37 valid bool // all other checks are ignored if valid=false 38 accept bool 39 remove bool 40 remains sdk.Coins 41 remainsPeriod sdk.Coins 42 periodReset time.Time 43 }{ 44 "empty": { 45 allow: feegrant.PeriodicAllowance{}, 46 valid: false, 47 }, 48 "only basic": { 49 allow: feegrant.PeriodicAllowance{ 50 Basic: feegrant.BasicAllowance{ 51 SpendLimit: atom, 52 Expiration: &oneHour, 53 }, 54 }, 55 valid: false, 56 }, 57 "empty basic": { 58 allow: feegrant.PeriodicAllowance{ 59 Period: tenMinutes, 60 PeriodSpendLimit: smallAtom, 61 PeriodReset: now.Add(30 * time.Minute), 62 }, 63 blockTime: now, 64 valid: true, 65 accept: true, 66 remove: false, 67 periodReset: now.Add(30 * time.Minute), 68 }, 69 "mismatched currencies": { 70 allow: feegrant.PeriodicAllowance{ 71 Basic: feegrant.BasicAllowance{ 72 SpendLimit: atom, 73 Expiration: &oneHour, 74 }, 75 Period: tenMinutes, 76 PeriodSpendLimit: eth, 77 }, 78 valid: false, 79 }, 80 "same period": { 81 allow: feegrant.PeriodicAllowance{ 82 Basic: feegrant.BasicAllowance{ 83 SpendLimit: atom, 84 Expiration: &twoHours, 85 }, 86 Period: tenMinutes, 87 PeriodReset: now.Add(1 * time.Hour), 88 PeriodSpendLimit: leftAtom, 89 PeriodCanSpend: smallAtom, 90 }, 91 valid: true, 92 fee: smallAtom, 93 blockTime: now, 94 accept: true, 95 remove: false, 96 remainsPeriod: nil, 97 remains: leftAtom, 98 periodReset: now.Add(1 * time.Hour), 99 }, 100 "step one period": { 101 allow: feegrant.PeriodicAllowance{ 102 Basic: feegrant.BasicAllowance{ 103 SpendLimit: atom, 104 Expiration: &twoHours, 105 }, 106 Period: tenMinutes, 107 PeriodReset: now, 108 PeriodSpendLimit: leftAtom, 109 }, 110 valid: true, 111 fee: leftAtom, 112 blockTime: now.Add(1 * time.Hour), 113 accept: true, 114 remove: false, 115 remainsPeriod: nil, 116 remains: smallAtom, 117 periodReset: oneHour.Add(tenMinutes), // one step from last reset, not now 118 }, 119 "step limited by global allowance": { 120 allow: feegrant.PeriodicAllowance{ 121 Basic: feegrant.BasicAllowance{ 122 SpendLimit: smallAtom, 123 Expiration: &twoHours, 124 }, 125 Period: tenMinutes, 126 PeriodReset: now, 127 PeriodSpendLimit: atom, 128 }, 129 valid: true, 130 fee: oneAtom, 131 blockTime: oneHour, 132 accept: true, 133 remove: false, 134 remainsPeriod: smallAtom.Sub(oneAtom), 135 remains: smallAtom.Sub(oneAtom), 136 periodReset: oneHour.Add(tenMinutes), // one step from last reset, not now 137 }, 138 "period reset no spend limit": { 139 allow: feegrant.PeriodicAllowance{ 140 Period: tenMinutes, 141 PeriodReset: now, 142 PeriodSpendLimit: atom, 143 }, 144 valid: true, 145 fee: atom, 146 blockTime: oneHour, 147 accept: true, 148 remove: false, 149 periodReset: oneHour.Add(tenMinutes), // one step from last reset, not now 150 }, 151 "expired": { 152 allow: feegrant.PeriodicAllowance{ 153 Basic: feegrant.BasicAllowance{ 154 SpendLimit: atom, 155 Expiration: &now, 156 }, 157 Period: time.Hour, 158 PeriodSpendLimit: smallAtom, 159 }, 160 valid: true, 161 fee: smallAtom, 162 blockTime: oneHour, 163 accept: false, 164 remove: true, 165 }, 166 "over period limit": { 167 allow: feegrant.PeriodicAllowance{ 168 Basic: feegrant.BasicAllowance{ 169 SpendLimit: atom, 170 Expiration: &now, 171 }, 172 Period: time.Hour, 173 PeriodReset: now.Add(1 * time.Hour), 174 PeriodSpendLimit: leftAtom, 175 PeriodCanSpend: smallAtom, 176 }, 177 valid: true, 178 fee: leftAtom, 179 blockTime: now, 180 accept: false, 181 remove: true, 182 }, 183 } 184 185 for name, stc := range cases { 186 tc := stc // to make scopelint happy 187 t.Run(name, func(t *testing.T) { 188 err := tc.allow.ValidateBasic() 189 if !tc.valid { 190 require.Error(t, err) 191 return 192 } 193 require.NoError(t, err) 194 195 ctx := app.BaseApp.NewContext(false, tmproto.Header{}).WithBlockTime(tc.blockTime) 196 // now try to deduct 197 remove, err := tc.allow.Accept(ctx, tc.fee, []sdk.Msg{}) 198 if !tc.accept { 199 require.Error(t, err) 200 return 201 } 202 require.NoError(t, err) 203 204 require.Equal(t, tc.remove, remove) 205 if !remove { 206 assert.Equal(t, tc.remains, tc.allow.Basic.SpendLimit) 207 assert.Equal(t, tc.remainsPeriod, tc.allow.PeriodCanSpend) 208 assert.Equal(t, tc.periodReset.String(), tc.allow.PeriodReset.String()) 209 } 210 }) 211 } 212 }