Skip to content

Commit 90579c0

Browse files
aaroncAlessio Treglia
and
Alessio Treglia
authored
ADR 029: Fee Grant Module (#7106)
* Add ADR 029 stub * ADR 029 first draft * Cleanup * Updates from code review * Updates from review Co-authored-by: Alessio Treglia <[email protected]>
1 parent 257eff3 commit 90579c0

File tree

2 files changed

+163
-0
lines changed

2 files changed

+163
-0
lines changed

docs/architecture/README.md

+1
Original file line numberDiff line numberDiff line change
@@ -54,3 +54,4 @@ Please add a entry below in your Pull Request for an ADR.
5454
- [ADR 025: IBC Passive Channels](./adr-025-ibc-passive-channels.md)
5555
- [ADR 026: IBC Client Recovery Mechanisms](./adr-026-ibc-client-recovery-mechanisms.md)
5656
- [ADR 027: Deterministic Protobuf Serialization](./adr-027-deterministic-protobuf-serialization.md)
57+
- [ADR 029: Fee Grant Module](./adr-029-fee-grant-module.md)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
# ADR 029: Fee Grant Module
2+
3+
## Changelog
4+
5+
- 2020/08/18: Initial Draft
6+
7+
## Status
8+
9+
Accepted
10+
11+
## Context
12+
13+
In order to make blockchain transactions, the signing account must possess a sufficient balance of the right denomination
14+
in order to pay fees. There are classes of transactions where needing to maintain a wallet with sufficient fees is a
15+
barrier to adoption.
16+
17+
For instance, when proper permissions are setup, someone may temporarily delegate the ability to vote on proposals to
18+
a "burner" account that is stored on a mobile phone with only minimal security.
19+
20+
Other use cases include workers tracking items in a supply chain or farmers submitting field data for analytics
21+
or compliance purposes.
22+
23+
For all of these use cases, UX would be significantly enhanced by obviating the need for these accounts to always
24+
maintain the appropriate fee balance. This is especially true if we wanted to achieve enterprise adoption for something
25+
like supply chain tracking.
26+
27+
While one solution would be to have a service that fills up these accounts automatically with the appropriate fees, a better UX
28+
would be provided by allowing these accounts to pull from a common fee pool account with proper spending limits.
29+
A single pool would reduce the churn of making lots of small "fill up" transactions and also more effectively leverages
30+
the resources of the organization setting up the pool.
31+
32+
## Decision
33+
34+
As a solution we propose a module, `x/feegrant` which allows one account, the "granter" to grant another account, the "grantee"
35+
an allowance to spend the granter's account balance for fees within certain well-defined limits.
36+
37+
Fee allowances are defined by the extensible `FeeAllowanceI` interface:
38+
39+
```go
40+
type FeeAllowanceI {
41+
// Accept can use fee payment requested as well as timestamp/height of the current block
42+
// to determine whether or not to process this. This is checked in
43+
// Keeper.UseGrantedFees and the return values should match how it is handled there.
44+
//
45+
// If it returns an error, the fee payment is rejected, otherwise it is accepted.
46+
// The FeeAllowance implementation is expected to update it's internal state
47+
// and will be saved again after an acceptance.
48+
//
49+
// If remove is true (regardless of the error), the FeeAllowance will be deleted from storage
50+
// (eg. when it is used up). (See call to RevokeFeeAllowance in Keeper.UseGrantedFees)
51+
Accept(fee sdk.Coins, blockTime time.Time, blockHeight int64) (remove bool, err error)
52+
}
53+
```
54+
55+
Two basic fee allowance types, `BasicFeeAllowance` and `PeriodicFeeAllowance` are defined to support known use cases:
56+
57+
```proto
58+
// BasicFeeAllowance implements FeeAllowance with a one-time grant of tokens
59+
// that optionally expires. The delegatee can use up to SpendLimit to cover fees.
60+
message BasicFeeAllowance {
61+
// spend_limit specifies the maximum amount of tokens that can be spent
62+
// by this allowance and will be updated as tokens are spent. If it is
63+
// empty, there is no spend limit and any amount of coins can be spent.
64+
repeated cosmos_sdk.v1.Coin spend_limit = 1;
65+
66+
// expires_at specifies an optional time when this allowance expires
67+
ExpiresAt expiration = 2;
68+
}
69+
70+
// PeriodicFeeAllowance extends FeeAllowance to allow for both a maximum cap,
71+
// as well as a limit per time period.
72+
message PeriodicFeeAllowance {
73+
BasicFeeAllowance basic = 1;
74+
75+
// period specifies the time duration in which period_spend_limit coins can
76+
// be spent before that allowance is reset
77+
Duration period = 2;
78+
79+
// period_spend_limit specifies the maximum number of coins that can be spent
80+
// in the period
81+
repeated cosmos_sdk.v1.Coin period_spend_limit = 3;
82+
83+
// period_can_spend is the number of coins left to be spent before the period_reset time
84+
repeated cosmos_sdk.v1.Coin period_can_spend = 4;
85+
86+
// period_reset is the time at which this period resets and a new one begins,
87+
// it is calculated from the start time of the first transaction after the
88+
// last period ended
89+
ExpiresAt period_reset = 5;
90+
}
91+
92+
// ExpiresAt is a point in time where something expires.
93+
// It may be *either* block time or block height
94+
message ExpiresAt {
95+
oneof sum {
96+
google.protobuf.Timestamp time = 1;
97+
uint64 height = 2;
98+
}
99+
}
100+
101+
// Duration is a repeating unit of either clock time or number of blocks.
102+
message Duration {
103+
oneof sum {
104+
google.protobuf.Duration duration = 1;
105+
uint64 blocks = 2;
106+
}
107+
}
108+
109+
```
110+
111+
Allowances can be granted and revoked using `MsgGrantFeeAllowance` and `MsgRevokeFeeAllowance`:
112+
113+
```proto
114+
message MsgGrantFeeAllowance {
115+
string granter = 1;
116+
string grantee = 2;
117+
google.protobuf.Any allowance = 3;
118+
}
119+
120+
// MsgRevokeFeeAllowance removes any existing FeeAllowance from Granter to Grantee.
121+
message MsgRevokeFeeAllowance {
122+
string granter = 1;
123+
string grantee = 2;
124+
}
125+
```
126+
127+
In order to use allowances in transactions, we add a new field `granter` to the transaction `Fee` type:
128+
```proto
129+
package cosmos.tx.v1beta1;
130+
131+
message Fee {
132+
repeated cosmos.base.v1beta1.Coin amount = 1;
133+
uint64 gas_limit = 2;
134+
string payer = 3;
135+
string granter = 4;
136+
}
137+
```
138+
139+
`granter` must either be left empty or must correspond to an account which has granted
140+
a fee allowance to fee payer (either the first signer or the value of the `payer` field).
141+
142+
A new `AnteDecorator` named `DeductGrantedFeeDecorator` will be created in order to process transactions with `fee_payer`
143+
set and correctly deduct fees based on fee allowances.
144+
145+
## Consequences
146+
147+
### Positive
148+
149+
- improved UX for use cases where it is cumbersome to maintain an account balance just for fees
150+
151+
### Negative
152+
153+
### Neutral
154+
155+
- a new field must be added to the transaction `Fee` message and a new `AnteDecorator` must be
156+
created to use it
157+
158+
## References
159+
160+
- Blog article describing initial work: https://medium.com/regen-network/hacking-the-cosmos-cosmwasm-and-key-management-a08b9f561d1b
161+
- Initial public specification: https://gist.github.com/aaronc/b60628017352df5983791cad30babe56
162+
- Original subkeys proposal from B-harvest which influenced this design: https://github.com/cosmos/cosmos-sdk/issues/4480

0 commit comments

Comments
 (0)