@@ -2,6 +2,8 @@ pub mod errors;
2
2
pub mod receiver;
3
3
mod state;
4
4
mod types;
5
+ use std:: ops:: Neg ;
6
+
5
7
use self :: state:: TokenState ;
6
8
pub use self :: types:: * ;
7
9
@@ -10,30 +12,33 @@ use anyhow::Ok;
10
12
use anyhow:: Result ;
11
13
use cid:: Cid ;
12
14
use fvm_ipld_blockstore:: Blockstore as IpldStore ;
13
- use fvm_shared:: bigint:: Zero ;
14
15
use fvm_shared:: econ:: TokenAmount ;
15
16
use fvm_shared:: ActorID ;
17
+ use num_traits:: Signed ;
16
18
17
19
/// Library functions that implement core FRC-??? standards
18
20
///
19
21
/// Holds injectable services to access/interface with IPLD/FVM layer.
20
- pub struct TokenHelper < BS >
22
+ pub struct Token < BS >
21
23
where
22
24
BS : IpldStore + Clone ,
23
25
{
24
- /// Injected blockstore
26
+ /// Injected blockstore. The blockstore must reference the same underlying storage under Clone
25
27
bs : BS ,
26
28
/// Root of the token state tree
27
- token_state : Cid ,
29
+ state_cid : Cid ,
28
30
}
29
31
30
- impl < BS > TokenHelper < BS >
32
+ impl < BS > Token < BS >
31
33
where
32
34
BS : IpldStore + Clone ,
33
35
{
34
36
/// Instantiate a token helper with access to a blockstore and runtime
35
37
pub fn new ( bs : BS , token_state : Cid ) -> Self {
36
- Self { bs, token_state }
38
+ Self {
39
+ bs,
40
+ state_cid : token_state,
41
+ }
37
42
}
38
43
39
44
/// Constructs the token state tree and saves it at a CID
@@ -43,22 +48,27 @@ where
43
48
}
44
49
45
50
/// Helper function that loads the root of the state tree related to token-accounting
46
- fn load_state ( & self ) -> Result < TokenState > {
47
- TokenState :: load ( & self . bs , & self . token_state )
51
+ ///
52
+ /// Actors can't usefully recover if state wasn't initialized (failure to call `init_state`) in
53
+ /// the constructor so this method panics if the state tree if missing
54
+ fn load_state ( & self ) -> TokenState {
55
+ TokenState :: load ( & self . bs , & self . state_cid ) . unwrap ( )
48
56
}
49
57
50
58
/// Mints the specified value of tokens into an account
51
59
///
52
- /// If the total supply or account balance overflows, this method returns an error. The mint
53
- /// amount must be non-negative or the method returns an error.
60
+ /// The mint amount must be non-negative or the method returns an error
54
61
pub fn mint ( & self , initial_holder : ActorID , value : TokenAmount ) -> Result < ( ) > {
55
- if value. lt ( & TokenAmount :: zero ( ) ) {
62
+ if value. is_negative ( ) {
56
63
bail ! ( "value of mint was negative {}" , value) ;
57
64
}
58
65
59
66
// Increase the balance of the actor and increase total supply
60
- let mut state = self . load_state ( ) ?;
61
- state. increase_balance ( & self . bs , initial_holder, & value) ?;
67
+ let mut state = self . load_state ( ) ;
68
+ state. change_balance_by ( & self . bs , initial_holder, & value) ?;
69
+
70
+ // TODO: invoke the receiver hook on the initial_holder
71
+
62
72
state. increase_supply ( & value) ?;
63
73
64
74
// Commit the state atomically if supply and balance increased
72
82
/// This equals the sum of `balance_of` called on all addresses. This equals sum of all
73
83
/// successful `mint` calls minus the sum of all successful `burn`/`burn_from` calls
74
84
pub fn total_supply ( & self ) -> TokenAmount {
75
- let state = self . load_state ( ) . unwrap ( ) ;
85
+ let state = self . load_state ( ) ;
76
86
state. supply
77
87
}
78
88
81
91
/// Accounts that have never received transfers implicitly have a zero-balance
82
92
pub fn balance_of ( & self , holder : ActorID ) -> Result < TokenAmount > {
83
93
// Load the HAMT holding balances
84
- let state = self . load_state ( ) ? ;
94
+ let state = self . load_state ( ) ;
85
95
state. get_balance ( & self . bs , holder)
86
96
}
87
97
@@ -90,27 +100,27 @@ where
90
100
/// The allowance is the amount that the spender can transfer or burn out of the owner's account
91
101
/// via the `transfer_from` and `burn_from` methods.
92
102
pub fn allowance ( & self , owner : ActorID , spender : ActorID ) -> Result < TokenAmount > {
93
- let state = self . load_state ( ) ? ;
103
+ let state = self . load_state ( ) ;
94
104
let allowance = state. get_allowance_between ( & self . bs , owner, spender) ?;
95
105
Ok ( allowance)
96
106
}
97
107
98
108
/// Increase the allowance that a spender controls of the owner's balance by the requested delta
99
109
///
100
- /// Returns an error if requested delta is negative or there are errors in (de)sereliazation of
110
+ /// Returns an error if requested delta is negative or there are errors in (de)serialization of
101
111
/// state. Else returns the new allowance.
102
112
pub fn increase_allowance (
103
113
& self ,
104
114
owner : ActorID ,
105
115
spender : ActorID ,
106
116
delta : TokenAmount ,
107
117
) -> Result < TokenAmount > {
108
- if delta. lt ( & TokenAmount :: zero ( ) ) {
118
+ if delta. is_negative ( ) {
109
119
bail ! ( "value of delta was negative {}" , delta) ;
110
120
}
111
121
112
- let mut state = self . load_state ( ) ? ;
113
- let new_amount = state. increase_allowance ( & self . bs , owner, spender, & delta) ?;
122
+ let mut state = self . load_state ( ) ;
123
+ let new_amount = state. change_allowance_by ( & self . bs , owner, spender, & delta) ?;
114
124
state. save ( & self . bs ) ?;
115
125
116
126
Ok ( new_amount)
@@ -119,29 +129,28 @@ where
119
129
/// Decrease the allowance that a spender controls of the owner's balance by the requested delta
120
130
///
121
131
/// If the resulting allowance would be negative, the allowance between owner and spender is set
122
- /// to zero. If resulting allowance is zero, the entry is removed from the state map. Returns an
123
- /// error if either the spender or owner address is unresolvable. Returns an error if requested
124
- /// delta is negative. Else returns the new allowance
132
+ /// to zero. Returns an error if either the spender or owner address is unresolvable. Returns an
133
+ /// error if requested delta is negative. Else returns the new allowance
125
134
pub fn decrease_allowance (
126
135
& self ,
127
136
owner : ActorID ,
128
137
spender : ActorID ,
129
138
delta : TokenAmount ,
130
139
) -> Result < TokenAmount > {
131
- if delta. lt ( & TokenAmount :: zero ( ) ) {
140
+ if delta. is_negative ( ) {
132
141
bail ! ( "value of delta was negative {}" , delta) ;
133
142
}
134
143
135
- let mut state = self . load_state ( ) ? ;
136
- let new_allowance = state. decrease_allowance ( & self . bs , owner, spender, & delta) ?;
144
+ let mut state = self . load_state ( ) ;
145
+ let new_allowance = state. change_allowance_by ( & self . bs , owner, spender, & delta. neg ( ) ) ?;
137
146
state. save ( & self . bs ) ?;
138
147
139
148
Ok ( new_allowance)
140
149
}
141
150
142
151
/// Sets the allowance between owner and spender to 0
143
152
pub fn revoke_allowance ( & self , owner : ActorID , spender : ActorID ) -> Result < ( ) > {
144
- let mut state = self . load_state ( ) ? ;
153
+ let mut state = self . load_state ( ) ;
145
154
state. revoke_allowance ( & self . bs , owner, spender) ?;
146
155
state. save ( & self . bs ) ?;
147
156
Ok ( ( ) )
@@ -157,37 +166,37 @@ where
157
166
/// - The target's balance MUST decrease by the requested value
158
167
/// - The total_supply MUST decrease by the requested value
159
168
///
160
- /// ## Operator equals target address
161
- /// If the operator is the targeted address, they are implicitly approved to burn an unlimited
169
+ /// ## Spender equals owner address
170
+ /// If the spender is the targeted address, they are implicitly approved to burn an unlimited
162
171
/// amount of tokens (up to their balance)
163
172
///
164
- /// ## Operator burning on behalf of target address
165
- /// If the operator is burning on behalf of the target token holder the following preconditions
173
+ /// ## Spender burning on behalf of owner address
174
+ /// If the spender is burning on behalf of the owner the following preconditions
166
175
/// must be met on top of the general burn conditions:
167
- /// - The operator MUST have an allowance not less than the requested value
176
+ /// - The spender MUST have an allowance not less than the requested value
168
177
/// In addition to the general postconditions:
169
- /// - The target-operator allowance MUST decrease by the requested value
178
+ /// - The target-spender allowance MUST decrease by the requested value
170
179
///
171
- /// If the burn operation would result in a negative balance for the targeted address , the burn
172
- /// is discarded and this method returns an error
180
+ /// If the burn operation would result in a negative balance for the owner , the burn is
181
+ /// discarded and this method returns an error
173
182
pub fn burn (
174
183
& self ,
175
184
spender : ActorID ,
176
185
owner : ActorID ,
177
186
value : TokenAmount ,
178
187
) -> Result < TokenAmount > {
179
- if value. lt ( & TokenAmount :: zero ( ) ) {
180
- bail ! ( "Cannot burn a negative amount" ) ;
188
+ if value. is_negative ( ) {
189
+ bail ! ( "cannot burn a negative amount" ) ;
181
190
}
182
191
183
- let mut state = self . load_state ( ) ? ;
192
+ let mut state = self . load_state ( ) ;
184
193
185
194
if spender != owner {
186
195
// attempt to use allowance and return early if not enough
187
196
state. attempt_use_allowance ( & self . bs , spender, owner, & value) ?;
188
197
}
189
198
// attempt to burn the requested amount
190
- let new_amount = state. decrease_balance ( & self . bs , owner, & value) ?;
199
+ let new_amount = state. change_balance_by ( & self . bs , owner, & value. neg ( ) ) ?;
191
200
192
201
// if both succeeded, atomically commit the transaction
193
202
state. save ( & self . bs ) ?;
@@ -208,42 +217,43 @@ where
208
217
/// - The senders's balance MUST decrease by the requested value
209
218
/// - The receiver's balance MUST increase by the requested value
210
219
///
211
- /// ## Operator equals target address
212
- /// If the operator is the 'from' address, they are implicitly approved to transfer an unlimited
220
+ /// ## Spender equals owner address
221
+ /// If the spender is the owner address, they are implicitly approved to transfer an unlimited
213
222
/// amount of tokens (up to their balance)
214
223
///
215
- /// ## Operator transferring on behalf of target address
216
- /// If the operator is transferring on behalf of the target token holder the following preconditions
224
+ /// ## Spender transferring on behalf of owner address
225
+ /// If the spender is transferring on behalf of the target token holder the following preconditions
217
226
/// must be met on top of the general burn conditions:
218
- /// - The operator MUST have an allowance not less than the requested value
227
+ /// - The spender MUST have an allowance not less than the requested value
219
228
/// In addition to the general postconditions:
220
- /// - The from-operator allowance MUST decrease by the requested value
229
+ /// - The owner-spender allowance MUST decrease by the requested value
221
230
pub fn transfer (
222
231
& self ,
223
- operator : ActorID ,
224
- from : ActorID ,
225
- to : ActorID ,
232
+ spender : ActorID ,
233
+ owner : ActorID ,
234
+ receiver : ActorID ,
226
235
value : TokenAmount ,
227
236
) -> Result < ( ) > {
228
- if value. lt ( & TokenAmount :: zero ( ) ) {
229
- bail ! ( "Cannot transfer a negative amount" ) ;
237
+ if value. is_negative ( ) {
238
+ bail ! ( "cannot transfer a negative amount" ) ;
230
239
}
231
240
232
- let mut state = self . load_state ( ) ? ;
241
+ let mut state = self . load_state ( ) ;
233
242
234
- if operator != from {
243
+ if spender != owner {
235
244
// attempt to use allowance and return early if not enough
236
- state. attempt_use_allowance ( & self . bs , operator , from , & value) ?;
245
+ state. attempt_use_allowance ( & self . bs , spender , owner , & value) ?;
237
246
}
238
247
248
+ // attempt to credit the receiver
249
+ state. change_balance_by ( & self . bs , receiver, & value) ?;
250
+ // attempt to debit from the sender
251
+ state. change_balance_by ( & self . bs , owner, & value. neg ( ) ) ?;
252
+
239
253
// call the receiver hook
240
254
// FIXME: use fvm_dispatch to make a standard runtime call to the receiver
241
255
// - ensure the hook did not abort
242
-
243
- // attempt to debit from the sender
244
- state. decrease_balance ( & self . bs , from, & value) ?;
245
- // attempt to credit the receiver
246
- state. increase_balance ( & self . bs , to, & value) ?;
256
+ // - receiver hook should see the new balances...
247
257
248
258
// if all succeeded, atomically commit the transaction
249
259
state. save ( & self . bs ) ?;
0 commit comments