frc46_token/token/
mod.rs

1#![allow(clippy::result_large_err)]
2
3use std::ops::Neg;
4
5use cid::Cid;
6pub use error::TokenError;
7use fvm_actor_utils::messaging::{MessagingError, RECEIVER_HOOK_METHOD_NUM};
8use fvm_actor_utils::receiver::{ReceiverHook, ReceiverHookError};
9use fvm_actor_utils::syscalls::Syscalls;
10use fvm_actor_utils::util::ActorRuntime;
11use fvm_ipld_blockstore::Blockstore;
12use fvm_ipld_encoding::ipld_block::IpldBlock;
13use fvm_ipld_encoding::RawBytes;
14use fvm_shared::address::Address;
15use fvm_shared::econ::TokenAmount;
16use fvm_shared::error::ExitCode;
17use num_traits::Zero;
18
19use self::state::{StateError as TokenStateError, StateInvariantError, StateSummary, TokenState};
20use self::types::TransferFromIntermediate;
21use self::types::TransferFromReturn;
22use self::types::TransferReturn;
23use self::types::{BurnFromReturn, MintIntermediate};
24use self::types::{BurnReturn, TransferIntermediate};
25use crate::receiver::{FRC46ReceiverHook, FRC46TokenReceived};
26use crate::token::types::MintReturn;
27use crate::token::TokenError::InvalidGranularity;
28
29mod error;
30pub mod state;
31pub mod types;
32
33/// Ratio of integral units to interpretation as standard token units, as given by FRC-0046.
34/// Aka "18 decimals".
35pub const TOKEN_PRECISION: u64 = 1_000_000_000_000_000_000;
36
37type Result<T> = std::result::Result<T, TokenError>;
38
39/// Library functions that implement core FRC-??? standards.
40///
41/// Holds injectable services to access/interface with IPLD/FVM layer.
42pub struct Token<'st, S, BS>
43where
44    S: Syscalls,
45    BS: Blockstore,
46{
47    /// Runtime services to interact with the execution environment.
48    runtime: &'st ActorRuntime<S, BS>,
49    /// Reference to token state that will be inspected/mutated.
50    state: &'st mut TokenState,
51    /// Minimum granularity of token amounts. All balances and amounts must be a multiple of this
52    /// granularity.
53    ///
54    /// Set to 1 for standard 18-dp precision, [`TOKEN_PRECISION`] for whole units only, or some
55    /// value in between.
56    granularity: u64,
57}
58
59impl<'st, S, BS> Token<'st, S, BS>
60where
61    S: Syscalls,
62    BS: Blockstore,
63{
64    /// Creates a new clean token state instance.
65    ///
66    /// This should be wrapped in a [`Token`] handle for convenience. Must be flushed to the
67    /// blockstore explicitly to persist changes.
68    pub fn create_state(bs: &BS) -> Result<TokenState> {
69        Ok(TokenState::new(bs)?)
70    }
71
72    /// Creates a new clean token state instance, specifying the underlying Hamt bit widths.
73    ///
74    /// This should be wrapped in a [`Token`] handle for convenience. Must be flushed to the
75    /// blockstore explicitly to persist changes.
76    pub fn create_state_with_bit_width(bs: &BS, hamt_bit_width: u32) -> Result<TokenState> {
77        Ok(TokenState::new_with_bit_width(bs, hamt_bit_width)?)
78    }
79
80    /// Wrap an existing token state.
81    pub fn wrap(
82        runtime: &'st ActorRuntime<S, BS>,
83        granularity: u64,
84        state: &'st mut TokenState,
85    ) -> Self {
86        Self { runtime, granularity, state }
87    }
88
89    /// Replace the current state with another.
90    ///
91    /// The previous state is returned and can be safely dropped.
92    pub fn replace(&mut self, state: TokenState) -> TokenState {
93        std::mem::replace(self.state, state)
94    }
95
96    /// For an already initialised state tree, loads the state tree from the blockstore at a Cid.
97    pub fn load_state(bs: &BS, state_cid: &Cid) -> Result<TokenState> {
98        Ok(TokenState::load(bs, state_cid)?)
99    }
100
101    /// Loads a fresh copy of the state from a blockstore from a given cid, replacing existing
102    /// state. The old state is returned to enable comparisons and the like but can be safely
103    /// dropped otherwise.
104    pub fn load_replace(&mut self, cid: &Cid) -> Result<TokenState> {
105        Ok(self.replace(Self::load_state(self.runtime.bs(), cid)?))
106    }
107
108    /// Flush state and return Cid for root.
109    pub fn flush(&mut self) -> Result<Cid> {
110        Ok(self.state.save(&self.runtime)?)
111    }
112
113    /// Get a reference to the wrapped state tree.
114    pub fn state(&self) -> &TokenState {
115        self.state
116    }
117
118    /// Get a reference to the underlying runtime.
119    pub fn runtime(&self) -> &ActorRuntime<S, BS> {
120        self.runtime
121    }
122
123    /// Opens an atomic transaction on TokenState which allows a closure to make multiple
124    /// modifications to the state tree.
125    ///
126    /// If the closure returns an error, the transaction is dropped atomically and no change is
127    /// observed on token state.
128    fn transaction<F, Res>(&mut self, f: F) -> Result<Res>
129    where
130        F: FnOnce(&mut TokenState, &ActorRuntime<S, BS>) -> Result<Res>,
131    {
132        let mut mutable_state = self.state.clone();
133        let res = f(&mut mutable_state, self.runtime)?;
134        // if closure didn't error, save state
135        *self.state = mutable_state;
136        Ok(res)
137    }
138}
139
140impl<S, BS> Token<'_, S, BS>
141where
142    S: Syscalls,
143    BS: Blockstore,
144{
145    /// Returns the smallest amount of tokens which is indivisible.
146    ///
147    /// Transfers and balances must be in multiples of granularity but allowances need not be.
148    /// Granularity never changes after it is initially set.
149    pub fn granularity(&self) -> u64 {
150        self.granularity
151    }
152
153    /// Mints the specified value of tokens into an account.
154    ///
155    /// The minter is implicitly defined as the caller of the actor, and must be an ID address. The
156    /// mint amount must be non-negative or the method returns an error.
157    ///
158    /// Returns a [`ReceiverHook`] to call the owner's token receiver hook, and the owner's new
159    /// balance. ReceiverHook must be called or it will panic and abort the transaction.
160    ///
161    /// The hook call will return a [`MintIntermediate`] struct which must be passed to
162    /// [`Token::mint_return`] to get the final return data.
163    pub fn mint(
164        &mut self,
165        operator: &Address,
166        initial_owner: &Address,
167        amount: &TokenAmount,
168        operator_data: RawBytes,
169        token_data: RawBytes,
170    ) -> Result<ReceiverHook<MintIntermediate>> {
171        let amount = validate_amount_with_granularity(amount, "mint", self.granularity)?;
172        // init the operator account so that its actor ID can be referenced in the receiver hook
173        let operator_id = self.runtime.resolve_or_init(operator)?;
174        // init the owner account as allowance and balance checks are not performed for minting
175        let owner_id = self.runtime.resolve_or_init(initial_owner)?;
176
177        // Increase the balance of the actor and increase total supply
178        let result = self.transaction(|state, bs| {
179            state.change_balance_by(&bs, owner_id, amount)?;
180            state.change_supply_by(amount)?;
181            Ok(MintIntermediate { recipient: *initial_owner, recipient_data: RawBytes::default() })
182        })?;
183
184        // return the params we'll send to the receiver hook
185        let params = FRC46TokenReceived {
186            operator: operator_id,
187            from: self.runtime.actor_id(),
188            to: owner_id,
189            amount: amount.clone(),
190            operator_data,
191            token_data,
192        };
193
194        Ok(ReceiverHook::new_frc46(*initial_owner, params, result)?)
195    }
196
197    /// Finalise return data from [`MintIntermediate`] data returned by calling receiver hook after
198    /// minting.
199    ///
200    /// This is done to allow reloading the state if it changed as a result of the hook call so we
201    /// can return an accurate balance even if the receiver transferred or burned tokens upon
202    /// receipt.
203    pub fn mint_return(&self, intermediate: MintIntermediate) -> Result<MintReturn> {
204        Ok(MintReturn {
205            balance: self.balance_of(&intermediate.recipient)?,
206            supply: self.total_supply(),
207            recipient_data: intermediate.recipient_data,
208        })
209    }
210
211    /// Gets the total number of tokens in existence.
212    ///
213    /// This equals the sum of [`Token::balance_of`] called on all addresses. This equals sum of all
214    /// successful [`Token::mint`] calls minus the sum of all successful
215    /// [`Token::burn`]/[`Token::burn_from`] calls.
216    pub fn total_supply(&self) -> TokenAmount {
217        self.state.supply.clone()
218    }
219
220    /// Returns the balance associated with a particular address.
221    ///
222    /// Accounts that have never received transfers implicitly have a zero-balance.
223    pub fn balance_of(&self, owner: &Address) -> Result<TokenAmount> {
224        // Don't instantiate an account if unable to resolve to an ID address, as non-initialized
225        // addresses have an implicit zero balance
226        match self.runtime.resolve_id(owner) {
227            Ok(owner) => Ok(self.state.get_balance(&self.runtime, owner)?),
228            Err(MessagingError::AddressNotResolved(_)) => {
229                // uninitialized address has implicit zero balance
230                Ok(TokenAmount::zero())
231            }
232            Err(e) => Err(e.into()),
233        }
234    }
235
236    /// Gets the allowance between owner and operator.
237    ///
238    /// An allowance is the amount that the operator can transfer or burn out of the owner's account
239    /// via the [`Token::transfer`] and [`Token::burn`] methods.
240    pub fn allowance(&self, owner: &Address, operator: &Address) -> Result<TokenAmount> {
241        // Don't instantiate an account if unable to resolve owner-ID, as non-initialized addresses
242        // give implicit zero allowances to all addresses
243        let owner = match self.runtime.resolve_id(owner) {
244            Ok(owner) => owner,
245            Err(MessagingError::AddressNotResolved(_)) => {
246                return Ok(TokenAmount::zero());
247            }
248            Err(e) => return Err(e.into()),
249        };
250
251        // Don't instantiate an account if unable to resolve operator-ID, as non-initialized
252        // addresses have an implicit zero allowance
253        let operator = match self.runtime.resolve_id(operator) {
254            Ok(operator) => operator,
255            Err(MessagingError::AddressNotResolved(_)) => {
256                return Ok(TokenAmount::zero());
257            }
258            Err(e) => return Err(e.into()),
259        };
260
261        // For concretely resolved accounts, retrieve the allowance from the map
262        Ok(self.state.get_allowance_between(&self.runtime, owner, operator)?)
263    }
264
265    /// Increase the allowance that an operator can control of an owner's balance by the requested
266    /// delta.
267    ///
268    /// Returns an error if requested delta is negative or there are errors in (de)serialization of
269    /// state. If either owner or operator addresses are not resolvable and cannot be initialised,
270    /// this method returns [`MessagingError::AddressNotInitialized`]. Otherwise, it returns the new
271    /// allowance.
272    pub fn increase_allowance(
273        &mut self,
274        owner: &Address,
275        operator: &Address,
276        delta: &TokenAmount,
277    ) -> Result<TokenAmount> {
278        let delta = validate_allowance(delta, "increase allowance delta")?;
279
280        // Attempt to instantiate the accounts if they don't exist
281        let owner = self.runtime.resolve_or_init(owner)?;
282        let operator = self.runtime.resolve_or_init(operator)?;
283        let new_amount = self.state.change_allowance_by(&self.runtime, owner, operator, delta)?;
284
285        Ok(new_amount)
286    }
287
288    /// Decrease the allowance that an operator controls of the owner's balance by the requested
289    /// delta.
290    ///
291    /// Returns an error if requested delta is negative or there are errors in (de)serialization of
292    /// of state. If the resulting allowance would be negative, the allowance between owner and
293    /// operator is set to zero. Returns an error if either the operator or owner addresses are not
294    /// resolvable and cannot be initialized. Otherwise it returns the new allowance.
295    pub fn decrease_allowance(
296        &mut self,
297        owner: &Address,
298        operator: &Address,
299        delta: &TokenAmount,
300    ) -> Result<TokenAmount> {
301        let delta = validate_allowance(delta, "decrease allowance delta")?;
302
303        // Attempt to instantiate the accounts if they don't exist
304        let owner = self.runtime.resolve_or_init(owner)?;
305        let operator = self.runtime.resolve_or_init(operator)?;
306        let new_allowance =
307            self.state.change_allowance_by(&self.runtime, owner, operator, &delta.neg())?;
308
309        Ok(new_allowance)
310    }
311
312    /// Sets the allowance between owner and operator to zero, returning the old allowance.
313    pub fn revoke_allowance(&mut self, owner: &Address, operator: &Address) -> Result<TokenAmount> {
314        let owner = match self.runtime.resolve_id(owner) {
315            Ok(owner) => owner,
316            Err(MessagingError::AddressNotResolved(_)) => {
317                // uninitialized address has implicit zero allowance already
318                return Ok(TokenAmount::zero());
319            }
320            Err(e) => return Err(e.into()),
321        };
322        let operator = match self.runtime.resolve_id(operator) {
323            Ok(operator) => operator,
324            Err(MessagingError::AddressNotResolved(_)) => {
325                // uninitialized address has implicit zero allowance already
326                return Ok(TokenAmount::zero());
327            }
328            Err(e) => return Err(e.into()),
329        };
330        // if both accounts resolved, explicitly set allowance to zero
331        Ok(self.state.revoke_allowance(&self.runtime, owner, operator)?)
332    }
333
334    /// Sets the allowance to a specified amount, returning the old allowance.
335    pub fn set_allowance(
336        &mut self,
337        owner: &Address,
338        operator: &Address,
339        amount: &TokenAmount,
340    ) -> Result<TokenAmount> {
341        let amount = validate_allowance(amount, "set allowance amount")?;
342
343        // Handle special revoke allowance case to avoid unnecessary account initialization
344        if amount.is_zero() {
345            return self.revoke_allowance(owner, operator);
346        }
347
348        // Attempt to instantiate the accounts if they don't exist
349        let owner = self.runtime.resolve_or_init(owner)?;
350        let operator = self.runtime.resolve_or_init(operator)?;
351
352        // if both accounts resolved, explicitly set allowance
353        Ok(self.state.set_allowance(&self.runtime, owner, operator, amount)?)
354    }
355
356    /// Burns an amount of token from the specified address, decreasing total token supply.
357    ///
358    /// - The requested value MUST be non-negative.
359    /// - The requested value MUST NOT exceed the target's balance.
360    /// - If the burn operation would result in a negative balance for the owner, the burn is
361    ///   discarded and this method returns an error.
362    ///
363    /// Upon successful burn:
364    ///
365    /// - The target's balance decreases by the requested value.
366    /// - The total supply decreases by the requested value.
367    pub fn burn(&mut self, owner: &Address, amount: &TokenAmount) -> Result<BurnReturn> {
368        let amount = validate_amount_with_granularity(amount, "burn", self.granularity)?;
369
370        let owner = self.runtime.resolve_or_init(owner)?;
371        self.transaction(|state, bs| {
372            // attempt to burn the requested amount
373            let new_amount = state.change_balance_by(&bs, owner, &amount.clone().neg())?;
374            // decrease total_supply
375            state.change_supply_by(&amount.neg())?;
376            Ok(BurnReturn { balance: new_amount })
377        })
378    }
379
380    /// Burns an amount of token from the specified address, decreasing total token supply.
381    ///
382    /// If operator and owner are the same address, this method returns an InvalidOperator error.
383    ///
384    /// - The requested value MUST be non-negative.
385    /// - The requested value MUST NOT exceed the target's balance.
386    /// - If the burn operation would result in a negative balance for the owner, the burn is
387    ///   discarded and this method returns an error.
388    /// - The operator MUST have an allowance not less than the requested value.
389    ///
390    /// Upon successful burn:
391    ///
392    /// - The target's balance decreases by the requested value.
393    /// - The total supply decreases by the requested value.
394    /// - The operator's allowance is decreased by the requested value.
395    pub fn burn_from(
396        &mut self,
397        operator: &Address,
398        owner: &Address,
399        amount: &TokenAmount,
400    ) -> Result<BurnFromReturn> {
401        let amount = validate_amount_with_granularity(amount, "burn", self.granularity)?;
402        if self.runtime.same_address(operator, owner) {
403            return Err(TokenError::InvalidOperator(*operator));
404        }
405
406        // operator must exist to have a non-zero allowance
407        let operator = match self.runtime.resolve_id(operator) {
408            Ok(operator) => operator,
409            Err(MessagingError::AddressNotResolved(addr)) => {
410                // if not resolved, implicit zero allowance is not permitted to burn, so return an
411                // insufficient allowance error
412                return Err(TokenStateError::InsufficientAllowance {
413                    owner: (*owner).into(),
414                    operator: addr.into(),
415                    allowance: TokenAmount::zero(),
416                    delta: amount.clone(),
417                }
418                .into());
419            }
420            Err(e) => return Err(e.into()),
421        };
422
423        // owner must exist to have set a non-zero allowance
424        let owner = match self.runtime.resolve_id(owner) {
425            Ok(owner) => owner,
426            Err(MessagingError::AddressNotResolved(addr)) => {
427                return Err(TokenStateError::InsufficientAllowance {
428                    owner: (*owner).into(),
429                    operator: addr.into(),
430                    allowance: TokenAmount::zero(),
431                    delta: amount.clone(),
432                }
433                .into());
434            }
435            Err(e) => return Err(e.into()),
436        };
437
438        self.transaction(|state, bs| {
439            let new_allowance = state.attempt_use_allowance(&bs, operator, owner, amount)?;
440            // attempt to burn the requested amount
441            let new_balance = state.change_balance_by(&bs, owner, &amount.clone().neg())?;
442            // decrease total_supply
443            state.change_supply_by(&amount.neg())?;
444            Ok(BurnFromReturn { balance: new_balance, allowance: new_allowance })
445        })
446    }
447
448    /// Transfers an amount from the caller to another address.
449    ///
450    /// - The requested value MUST be non-negative.
451    /// - The requested value MUST NOT exceed the sender's balance.
452    /// - The receiving actor MUST implement a method called `tokens_received`, corresponding to the
453    ///   interface specified for FRC-0046 token receiver. If the receiving hook aborts, when called,
454    ///   the transfer is discarded and this method returns an error.
455    ///
456    /// Upon successful transfer:
457    ///
458    /// - The from balance decreases by the requested value.
459    /// - The to balance increases by the requested value.
460    ///
461    /// Returns a [`ReceiverHook`] to call the recipient's token receiver hook, and a
462    /// [`TransferIntermediate`] struct. [`ReceiverHook`] must be called or it will panic and abort
463    /// the transaction.
464    ///
465    /// Return data from the hook should be passed to transfer_return which will generate
466    /// the [`TransferReturn`] struct.
467    pub fn transfer(
468        &mut self,
469        from: &Address,
470        to: &Address,
471        amount: &TokenAmount,
472        operator_data: RawBytes,
473        token_data: RawBytes,
474    ) -> Result<ReceiverHook<TransferIntermediate>> {
475        let amount = validate_amount_with_granularity(amount, "transfer", self.granularity)?;
476
477        // owner-initiated transfer
478        let from_id = self.runtime.resolve_or_init(from)?;
479        let to_id = self.runtime.resolve_or_init(to)?;
480        // skip allowance check for self-managed transfers
481        self.transaction(|state, bs| {
482            state.make_transfer(&bs, from_id, to_id, amount)?;
483            Ok(())
484        })?;
485
486        let res =
487            TransferIntermediate { from: *from, to: *to, recipient_data: RawBytes::default() };
488
489        let params = FRC46TokenReceived {
490            operator: from_id,
491            from: from_id,
492            to: to_id,
493            amount: amount.clone(),
494            operator_data,
495            token_data,
496        };
497
498        Ok(ReceiverHook::new_frc46(*to, params, res)?)
499    }
500
501    /// Generate [`TransferReturn`] from the intermediate data returned by a receiver hook call.
502    pub fn transfer_return(&self, intermediate: TransferIntermediate) -> Result<TransferReturn> {
503        Ok(TransferReturn {
504            from_balance: self.balance_of(&intermediate.from)?,
505            to_balance: self.balance_of(&intermediate.to)?,
506            recipient_data: intermediate.recipient_data,
507        })
508    }
509
510    /// Transfers an amount from one address to another.
511    ///
512    /// - The requested value MUST be non-negative.
513    /// - The requested value MUST NOT exceed the sender's balance.
514    /// - The receiving actor MUST implement a method called `tokens_received`, corresponding to the
515    ///   interface specified for FRC-0046 token receiver. If the receiving hook aborts, when called,
516    ///   the transfer is discarded and this method returns an error.
517    /// - The operator MUST be initialised AND have an allowance not less than the requested value.
518    ///
519    /// Upon successful transfer:
520    ///
521    /// - The from balance decreases by the requested value.
522    /// - The to balance increases by the requested value.
523    /// - The owner-operator allowance decreases by the requested value.
524    ///
525    /// Returns a [`ReceiverHook`] to call the recipient's token receiver hook, and a
526    /// [`TransferFromIntermediate`]. [`ReceiverHook`] must be called or it will panic and abort the
527    /// transaction.
528    ///
529    /// Return data from the hook should be passed to [`Token::transfer_from_return`] which will
530    /// generate the [`TransferFromReturn`].
531    pub fn transfer_from(
532        &mut self,
533        operator: &Address,
534        from: &Address,
535        to: &Address,
536        amount: &TokenAmount,
537        operator_data: RawBytes,
538        token_data: RawBytes,
539    ) -> Result<ReceiverHook<TransferFromIntermediate>> {
540        let amount = validate_amount_with_granularity(amount, "transfer", self.granularity)?;
541        if self.runtime.same_address(operator, from) {
542            return Err(TokenError::InvalidOperator(*operator));
543        }
544
545        // operator-initiated transfer must have a resolvable operator
546        let operator_id = match self.runtime.resolve_id(operator) {
547            // if operator resolved, we can continue with other checks
548            Ok(id) => id,
549            // if we cannot resolve the operator, they are forbidden to transfer
550            Err(MessagingError::AddressNotResolved(_)) => {
551                return Err(TokenError::TokenState(TokenStateError::InsufficientAllowance {
552                    operator: (*operator).into(),
553                    owner: (*from).into(),
554                    allowance: TokenAmount::zero(),
555                    delta: amount.clone(),
556                }));
557            }
558            Err(e) => return Err(e.into()),
559        };
560
561        // the owner must exist to have specified a non-zero allowance
562        let from_id = match self.runtime.resolve_id(from) {
563            Ok(id) => id,
564            Err(MessagingError::AddressNotResolved(from)) => {
565                return Err(TokenError::TokenState(TokenStateError::InsufficientAllowance {
566                    operator: (*operator).into(),
567                    owner: from.into(),
568                    allowance: TokenAmount::zero(),
569                    delta: amount.clone(),
570                }));
571            }
572            Err(e) => return Err(e.into()),
573        };
574
575        // attempt to initialize the receiving account if not present
576        let to_id = self.runtime.resolve_or_init(to)?;
577
578        // update token state
579        self.transaction(|state, bs| {
580            state.attempt_use_allowance(&bs, operator_id, from_id, amount)?;
581            state.make_transfer(&bs, from_id, to_id, amount)?;
582            Ok(())
583        })?;
584
585        let res = TransferFromIntermediate {
586            operator: *operator,
587            from: *from,
588            to: *to,
589            recipient_data: RawBytes::default(),
590        };
591
592        let params = FRC46TokenReceived {
593            operator: operator_id,
594            from: from_id,
595            to: to_id,
596            amount: amount.clone(),
597            operator_data,
598            token_data,
599        };
600
601        Ok(ReceiverHook::new_frc46(*to, params, res)?)
602    }
603
604    /// Generate [`TransferReturn`] from the intermediate data returned by a receiver hook call.
605    pub fn transfer_from_return(
606        &self,
607        intermediate: TransferFromIntermediate,
608    ) -> Result<TransferFromReturn> {
609        Ok(TransferFromReturn {
610            from_balance: self.balance_of(&intermediate.from)?,
611            to_balance: self.balance_of(&intermediate.to)?,
612            allowance: self.allowance(&intermediate.from, &intermediate.operator)?, // allowance remains unchanged?
613            recipient_data: intermediate.recipient_data,
614        })
615    }
616
617    /// Sets the balance of an account to a specific amount.
618    ///
619    /// Using this library method obeys internal invariants (changing total supply etc.) but does
620    /// not invoke the receiver hook on recipient accounts. Returns the old balance.
621    pub fn set_balance(&mut self, owner: &Address, amount: &TokenAmount) -> Result<TokenAmount> {
622        let amount = validate_amount_with_granularity(amount, "set_balance", self.granularity)?;
623
624        let owner = self.runtime.resolve_or_init(owner)?;
625        let old_balance = self.transaction(|state, bs| {
626            // update the account's balance
627            let old_balance = state.set_balance(bs, owner, amount)?;
628            // update the total supply accordingly
629            let supply_change = amount - old_balance.clone();
630            state.supply += supply_change;
631            Ok(old_balance)
632        })?;
633
634        Ok(old_balance)
635    }
636}
637
638impl<S, BS> Token<'_, S, BS>
639where
640    S: Syscalls,
641    BS: Blockstore,
642{
643    /// Calls the receiver hook, returning the result.
644    pub fn call_receiver_hook(
645        &mut self,
646        token_receiver: &Address,
647        params: FRC46TokenReceived,
648    ) -> Result<()> {
649        let receipt = self.runtime.send(
650            token_receiver,
651            RECEIVER_HOOK_METHOD_NUM,
652            IpldBlock::serialize_cbor(&params)?,
653            TokenAmount::zero(),
654        )?;
655
656        match receipt.exit_code {
657            ExitCode::OK => Ok(()),
658            abort_code => Err(ReceiverHookError::new_receiver_error(
659                *token_receiver,
660                abort_code,
661                receipt.return_data,
662            )
663            .into()),
664        }
665    }
666
667    /// Checks the state invariants, throwing an error if they are not met.
668    pub fn assert_invariants(&self) -> std::result::Result<StateSummary, Vec<StateInvariantError>> {
669        let (summary, errors) = self.check_invariants();
670        match errors.is_empty() {
671            true => Ok(summary),
672            false => Err(errors),
673        }
674    }
675
676    /// Checks the state invariants, returning a state summary and list of errors.
677    pub fn check_invariants(&self) -> (StateSummary, Vec<StateInvariantError>) {
678        self.state.check_invariants(&self.runtime, self.granularity)
679    }
680}
681
682/// Validates that a token amount for burning/transfer/minting is non-negative, and an integer
683/// multiple of granularity.
684///
685/// Returns the argument, or an error.
686pub fn validate_amount_with_granularity<'a>(
687    a: &'a TokenAmount,
688    name: &'static str,
689    granularity: u64,
690) -> Result<&'a TokenAmount> {
691    if a.is_negative() {
692        return Err(TokenError::InvalidNegative { name, amount: a.clone() });
693    }
694    let (_, modulus) = a.div_rem(granularity);
695    if !modulus.is_zero() {
696        return Err(InvalidGranularity { name, amount: a.clone(), granularity });
697    }
698    Ok(a)
699}
700
701/// Validates that an allowance is non-negative. Allowances do not need to be an integer multiple of
702/// granularity.
703///
704/// Returns the argument, or an error.
705pub fn validate_allowance<'a>(a: &'a TokenAmount, name: &'static str) -> Result<&'a TokenAmount> {
706    if a.is_negative() {
707        return Err(TokenError::InvalidNegative { name, amount: a.clone() });
708    }
709    Ok(a)
710}
711
712#[cfg(test)]
713mod test {
714    use std::ops::Neg;
715
716    use fvm_actor_utils::messaging::{MessagingError, RECEIVER_HOOK_METHOD_NUM};
717    use fvm_actor_utils::receiver::{ReceiverHookError, UniversalReceiverParams};
718    use fvm_actor_utils::syscalls::fake_syscalls::FakeSyscalls;
719    use fvm_actor_utils::util::ActorRuntime;
720    use fvm_ipld_blockstore::MemoryBlockstore;
721    use fvm_ipld_encoding::RawBytes;
722    use fvm_sdk::sys::ErrorNumber;
723    use fvm_shared::address::{Address, BLS_PUB_LEN};
724    use fvm_shared::econ::TokenAmount;
725    use num_traits::Zero;
726
727    use crate::receiver::{FRC46TokenReceived, FRC46_TOKEN_TYPE};
728    use crate::token::state::StateError;
729    use crate::token::state::TokenState;
730    use crate::token::Token;
731    use crate::token::TokenError;
732
733    /// Returns a static secp256k1 address.
734    fn secp_address() -> Address {
735        let key = vec![0; 65];
736        Address::new_secp256k1(key.as_slice()).unwrap()
737    }
738
739    /// Returns a static BLS address.
740    fn bls_address() -> Address {
741        let key = vec![0; BLS_PUB_LEN];
742        Address::new_bls(key.as_slice()).unwrap()
743    }
744
745    /// Returns a new Actor address, that is uninitializable by the [`FakeMessenger`].
746    fn actor_address() -> Address {
747        Address::new_actor(Default::default())
748    }
749
750    const TOKEN_ACTOR: &Address = &Address::new_id(1);
751    const TREASURY: &Address = &Address::new_id(2);
752    const ALICE: &Address = &Address::new_id(3);
753    const BOB: &Address = &Address::new_id(4);
754    const CAROL: &Address = &Address::new_id(5);
755
756    fn new_token<'st>(
757        runtime: &'st ActorRuntime<FakeSyscalls, MemoryBlockstore>,
758        state: &'st mut TokenState,
759    ) -> Token<'st, FakeSyscalls, MemoryBlockstore> {
760        Token::wrap(runtime, 1, state)
761    }
762
763    fn assert_last_hook_call_eq(
764        runtime: &ActorRuntime<FakeSyscalls, MemoryBlockstore>,
765        expected: FRC46TokenReceived,
766    ) {
767        let last_message = runtime.syscalls.last_message.borrow().clone().unwrap();
768        assert_eq!(last_message.method, RECEIVER_HOOK_METHOD_NUM);
769        let last_called: UniversalReceiverParams =
770            last_message.params.unwrap().deserialize().unwrap();
771        assert_eq!(last_called.type_, FRC46_TOKEN_TYPE);
772        let last_called: FRC46TokenReceived = last_called.payload.deserialize().unwrap();
773        assert_eq!(last_called, expected);
774    }
775
776    #[test]
777    fn it_wraps_a_previously_loaded_state_tree() {
778        struct ActorState {
779            token_state: TokenState,
780        }
781
782        // simulate the token state being a node in a larger state tree
783        let helper = ActorRuntime::<FakeSyscalls, MemoryBlockstore>::new_test_runtime();
784        let mut actor_state = ActorState {
785            token_state: Token::<FakeSyscalls, MemoryBlockstore>::create_state(helper.bs())
786                .unwrap(),
787        };
788        // wrap the token state, moving it into a TokenHandle
789        let mut token = new_token(&helper, &mut actor_state.token_state);
790
791        let mut hook = token
792            .mint(
793                TOKEN_ACTOR,
794                TREASURY,
795                &TokenAmount::from_atto(1),
796                RawBytes::default(),
797                RawBytes::default(),
798            )
799            .unwrap();
800        token.flush().unwrap();
801        hook.call(token.runtime).unwrap();
802
803        let state = token.state();
804        // gets a read-only state
805        assert_eq!(state.supply, TokenAmount::from_atto(1));
806        // can get a token_state here but doing so borrows the value making the mutable borrow on line 550 invalid
807        assert_eq!(actor_state.token_state.supply, TokenAmount::from_atto(1));
808
809        // therefore, after the above line 560, can no longer use the token handle to read OR mutate state
810        // any single one of these lines now causes a compiler error
811        // token.mint(TOKEN_ACTOR, TREASURY, &TokenAmount::from_atto(1), Default::default(), Default::default()).unwrap();
812        // token.balance_of(TREASURY).unwrap();
813    }
814
815    #[test]
816    fn it_instantiates_and_persists() {
817        let helper = ActorRuntime::<FakeSyscalls, MemoryBlockstore>::new_test_runtime();
818        // create a new token
819        let mut state = Token::<FakeSyscalls, MemoryBlockstore>::create_state(helper.bs()).unwrap();
820        // wrap the token state, moving it into a TokenHandle
821        let mut token = new_token(&helper, &mut state);
822
823        // state exists but is empty
824        assert_eq!(token.total_supply(), TokenAmount::zero());
825
826        // mint some value
827        let mut hook = token
828            .mint(
829                TOKEN_ACTOR,
830                TREASURY,
831                &TokenAmount::from_atto(100),
832                RawBytes::default(),
833                RawBytes::default(),
834            )
835            .unwrap();
836        token.flush().unwrap();
837        hook.call(token.runtime).unwrap();
838
839        assert_eq!(token.total_supply(), TokenAmount::from_atto(100));
840
841        // flush token to blockstore
842        let cid = token.flush().unwrap();
843
844        // the returned cid can be used to reference the same token state
845        let mut state =
846            Token::<FakeSyscalls, MemoryBlockstore>::load_state(helper.bs(), &cid).unwrap();
847        let token2 = Token::wrap(token.runtime, 1, &mut state);
848        assert_eq!(token2.total_supply(), TokenAmount::from_atto(100));
849    }
850
851    #[test]
852    fn it_instantiates_with_variable_bit_width() {
853        let helper = ActorRuntime::<FakeSyscalls, MemoryBlockstore>::new_test_runtime();
854        let mut state =
855            Token::<FakeSyscalls, MemoryBlockstore>::create_state_with_bit_width(helper.bs(), 2)
856                .unwrap();
857        state.set_balance(&helper, ALICE.id().unwrap(), &TokenAmount::from_atto(100)).unwrap();
858        let state_cid = state.save(&helper).unwrap();
859
860        let token =
861            Token::<FakeSyscalls, MemoryBlockstore>::load_state(helper.bs(), &state_cid).unwrap();
862        assert_eq!(
863            token.get_balance(&helper, ALICE.id().unwrap()).unwrap(),
864            TokenAmount::from_atto(100)
865        );
866    }
867
868    #[test]
869    fn it_mutates_externally_loaded_state() {
870        let helper = ActorRuntime::<FakeSyscalls, MemoryBlockstore>::new_test_runtime();
871        let mut state = TokenState::new(&helper).unwrap();
872        let mut token = Token::<FakeSyscalls, MemoryBlockstore>::wrap(&helper, 1, &mut state);
873
874        // mutate state via the handle
875        let mut hook = token
876            .mint(
877                TOKEN_ACTOR,
878                ALICE,
879                &TokenAmount::from_atto(100),
880                RawBytes::default(),
881                RawBytes::default(),
882            )
883            .unwrap();
884        token.flush().unwrap();
885        hook.call(token.runtime).unwrap();
886
887        // visible via the handle
888        assert_eq!(token.total_supply(), TokenAmount::from_atto(100));
889
890        // the underlying state was mutated
891        assert_eq!(state.supply, TokenAmount::from_atto(100));
892        assert_eq!(
893            state.get_balance(&helper, ALICE.id().unwrap()).unwrap(),
894            TokenAmount::from_atto(100)
895        );
896
897        // note: its not allowed here to use the token handle anymore given that we have read from state
898        // assert_eq!(token.total_supply(), TokenAmount::from_atto(100));
899    }
900
901    #[test]
902    fn it_provides_atomic_transactions() {
903        let helper = ActorRuntime::<FakeSyscalls, MemoryBlockstore>::new_test_runtime();
904        let mut token_state =
905            Token::<FakeSyscalls, MemoryBlockstore>::create_state(helper.bs()).unwrap();
906        let mut token = new_token(&helper, &mut token_state);
907
908        // entire transaction succeeds
909        token
910            .transaction(|state, _bs| {
911                state.change_supply_by(&TokenAmount::from_atto(100))?;
912                state.change_supply_by(&TokenAmount::from_atto(100))?;
913                Ok(())
914            })
915            .unwrap();
916        assert_eq!(token.total_supply(), TokenAmount::from_atto(200));
917
918        // entire transaction fails
919        token
920            .transaction(|state, _bs| {
921                state.change_supply_by(&TokenAmount::from_atto(-100))?;
922                state.change_supply_by(&TokenAmount::from_atto(-100))?;
923                // this makes supply negative and should revert the entire transaction
924                state.change_supply_by(&TokenAmount::from_atto(-100))?;
925                Ok(())
926            })
927            .unwrap_err();
928        // total_supply should be unchanged
929        assert_eq!(token.total_supply(), TokenAmount::from_atto(200));
930    }
931
932    #[test]
933    fn it_mints() {
934        let mut helper = ActorRuntime::<FakeSyscalls, MemoryBlockstore>::new_test_runtime();
935        helper.syscalls.actor_id = TOKEN_ACTOR.id().unwrap(); // minting relies on runtime to determine the token actor's id
936
937        let mut token_state =
938            Token::<FakeSyscalls, MemoryBlockstore>::create_state(helper.bs()).unwrap();
939        let mut token = new_token(&helper, &mut token_state);
940
941        assert_eq!(token.balance_of(TREASURY).unwrap(), TokenAmount::zero());
942        let mut hook = token
943            .mint(
944                TOKEN_ACTOR,
945                TREASURY,
946                &TokenAmount::from_atto(1_000_000),
947                RawBytes::default(),
948                RawBytes::default(),
949            )
950            .unwrap();
951        token.flush().unwrap();
952        let hook_ret = hook.call(token.runtime).unwrap();
953
954        // check receiver hook was called with correct shape
955        assert_last_hook_call_eq(
956            token.runtime,
957            FRC46TokenReceived {
958                operator: TOKEN_ACTOR.id().unwrap(),
959                from: TOKEN_ACTOR.id().unwrap(),
960                to: TREASURY.id().unwrap(),
961                amount: TokenAmount::from_atto(1_000_000),
962                operator_data: Default::default(),
963                token_data: Default::default(),
964            },
965        );
966
967        let result = token.mint_return(hook_ret).unwrap();
968        assert_eq!(TokenAmount::from_atto(1_000_000), result.balance);
969        assert_eq!(TokenAmount::from_atto(1_000_000), result.supply);
970
971        // balance and total supply both went up
972        assert_eq!(token.balance_of(TREASURY).unwrap(), TokenAmount::from_atto(1_000_000));
973        assert_eq!(token.total_supply(), TokenAmount::from_atto(1_000_000));
974
975        // cannot mint a negative amount
976        token
977            .mint(
978                TOKEN_ACTOR,
979                ALICE,
980                &TokenAmount::from_atto(-1),
981                RawBytes::default(),
982                RawBytes::default(),
983            )
984            .unwrap_err();
985
986        // state remained unchanged
987        assert_eq!(token.balance_of(ALICE).unwrap(), TokenAmount::zero());
988        assert_eq!(token.total_supply(), TokenAmount::from_atto(1_000_000));
989
990        // mint zero
991        let mut hook = token
992            .mint(TOKEN_ACTOR, ALICE, &TokenAmount::zero(), Default::default(), Default::default())
993            .unwrap();
994        token.flush().unwrap();
995        hook.call(token.runtime).unwrap();
996
997        // check receiver hook was called with correct shape
998        assert_last_hook_call_eq(
999            token.runtime,
1000            FRC46TokenReceived {
1001                operator: TOKEN_ACTOR.id().unwrap(),
1002                from: TOKEN_ACTOR.id().unwrap(),
1003                to: ALICE.id().unwrap(),
1004                amount: TokenAmount::zero(),
1005                operator_data: Default::default(),
1006                token_data: Default::default(),
1007            },
1008        );
1009
1010        // state remained unchanged
1011        assert_eq!(token.balance_of(ALICE).unwrap(), TokenAmount::zero());
1012        assert_eq!(token.total_supply(), TokenAmount::from_atto(1_000_000));
1013
1014        // mint again to same address
1015        let mut hook = token
1016            .mint(
1017                TOKEN_ACTOR,
1018                TREASURY,
1019                &TokenAmount::from_atto(1_000_000),
1020                RawBytes::default(),
1021                RawBytes::default(),
1022            )
1023            .unwrap();
1024        token.flush().unwrap();
1025        let hook_ret = hook.call(token.runtime).unwrap();
1026        let result = token.mint_return(hook_ret).unwrap();
1027        assert_eq!(TokenAmount::from_atto(2_000_000), result.balance);
1028        assert_eq!(TokenAmount::from_atto(2_000_000), result.supply);
1029
1030        assert_eq!(token.balance_of(ALICE).unwrap(), TokenAmount::zero());
1031        assert_eq!(token.balance_of(TREASURY).unwrap(), TokenAmount::from_atto(2_000_000));
1032        assert_eq!(token.total_supply(), TokenAmount::from_atto(2_000_000));
1033
1034        // check receiver hook was called with correct shape
1035        assert_last_hook_call_eq(
1036            token.runtime,
1037            FRC46TokenReceived {
1038                operator: TOKEN_ACTOR.id().unwrap(),
1039                from: TOKEN_ACTOR.id().unwrap(),
1040                to: TREASURY.id().unwrap(),
1041                amount: TokenAmount::from_atto(1_000_000),
1042                operator_data: Default::default(),
1043                token_data: Default::default(),
1044            },
1045        );
1046
1047        // mint to a different address
1048        let mut hook = token
1049            .mint(
1050                TOKEN_ACTOR,
1051                ALICE,
1052                &TokenAmount::from_atto(1_000_000),
1053                RawBytes::default(),
1054                RawBytes::default(),
1055            )
1056            .unwrap();
1057        token.flush().unwrap();
1058        let hook_ret = hook.call(token.runtime).unwrap();
1059        let result = token.mint_return(hook_ret).unwrap();
1060        assert_eq!(TokenAmount::from_atto(1_000_000), result.balance);
1061        assert_eq!(TokenAmount::from_atto(3_000_000), result.supply);
1062
1063        assert_eq!(token.balance_of(ALICE).unwrap(), TokenAmount::from_atto(1_000_000));
1064        assert_eq!(token.balance_of(TREASURY).unwrap(), TokenAmount::from_atto(2_000_000));
1065        assert_eq!(token.total_supply(), TokenAmount::from_atto(3_000_000));
1066
1067        // check receiver hook was called with correct shape
1068        assert_last_hook_call_eq(
1069            token.runtime,
1070            FRC46TokenReceived {
1071                operator: TOKEN_ACTOR.id().unwrap(),
1072                from: TOKEN_ACTOR.id().unwrap(),
1073                to: ALICE.id().unwrap(),
1074                amount: TokenAmount::from_atto(1_000_000),
1075                operator_data: Default::default(),
1076                token_data: Default::default(),
1077            },
1078        );
1079
1080        // carols account was unaffected
1081        assert_eq!(token.balance_of(CAROL).unwrap(), TokenAmount::zero());
1082
1083        // can mint to secp address
1084        let secp_address = secp_address();
1085        // initially zero
1086        assert_eq!(token.balance_of(&secp_address).unwrap(), TokenAmount::zero());
1087        // self-mint to secp address
1088        let mut hook = token
1089            .mint(
1090                TOKEN_ACTOR,
1091                &secp_address,
1092                &TokenAmount::from_atto(1_000_000),
1093                RawBytes::default(),
1094                RawBytes::default(),
1095            )
1096            .unwrap();
1097        token.flush().unwrap();
1098        hook.call(token.runtime).unwrap();
1099
1100        // check receiver hook was called with correct shape
1101        assert_last_hook_call_eq(
1102            token.runtime,
1103            FRC46TokenReceived {
1104                operator: TOKEN_ACTOR.id().unwrap(),
1105                from: TOKEN_ACTOR.id().unwrap(),
1106                to: token.runtime.resolve_id(&secp_address).unwrap(),
1107                amount: TokenAmount::from_atto(1_000_000),
1108                operator_data: Default::default(),
1109                token_data: Default::default(),
1110            },
1111        );
1112
1113        // can mint to bls address
1114        let bls_address = bls_address();
1115        // initially zero
1116        assert_eq!(token.balance_of(&bls_address).unwrap(), TokenAmount::zero());
1117        // minting creates the account
1118        let mut hook = token
1119            .mint(
1120                TOKEN_ACTOR,
1121                &bls_address,
1122                &TokenAmount::from_atto(1_000_000),
1123                RawBytes::default(),
1124                RawBytes::default(),
1125            )
1126            .unwrap();
1127        token.flush().unwrap();
1128        hook.call(token.runtime).unwrap();
1129        assert_eq!(token.balance_of(ALICE).unwrap(), TokenAmount::from_atto(1_000_000));
1130        assert_eq!(token.balance_of(TREASURY).unwrap(), TokenAmount::from_atto(2_000_000));
1131        assert_eq!(token.balance_of(&secp_address).unwrap(), TokenAmount::from_atto(1_000_000));
1132        assert_eq!(token.balance_of(&bls_address).unwrap(), TokenAmount::from_atto(1_000_000));
1133        assert_eq!(token.total_supply(), TokenAmount::from_atto(5_000_000));
1134
1135        // check receiver hook was called with correct shape
1136        assert_last_hook_call_eq(
1137            token.runtime,
1138            FRC46TokenReceived {
1139                operator: TOKEN_ACTOR.id().unwrap(),
1140                from: TOKEN_ACTOR.id().unwrap(),
1141                to: token.runtime.resolve_id(&bls_address).unwrap(),
1142                amount: TokenAmount::from_atto(1_000_000),
1143                operator_data: Default::default(),
1144                token_data: Default::default(),
1145            },
1146        );
1147
1148        // mint fails if actor address cannot be initialised
1149        let actor_address: Address = actor_address();
1150        token
1151            .mint(
1152                TOKEN_ACTOR,
1153                &actor_address,
1154                &TokenAmount::from_atto(1_000_000),
1155                RawBytes::default(),
1156                RawBytes::default(),
1157            )
1158            .unwrap_err();
1159        assert_eq!(token.balance_of(ALICE).unwrap(), TokenAmount::from_atto(1_000_000));
1160        assert_eq!(token.balance_of(TREASURY).unwrap(), TokenAmount::from_atto(2_000_000));
1161        assert_eq!(token.balance_of(&secp_address).unwrap(), TokenAmount::from_atto(1_000_000));
1162        assert_eq!(token.balance_of(&bls_address).unwrap(), TokenAmount::from_atto(1_000_000));
1163        assert_eq!(token.total_supply(), TokenAmount::from_atto(5_000_000));
1164
1165        token.assert_invariants().unwrap();
1166    }
1167
1168    #[test]
1169    fn it_fails_to_mint_if_receiver_hook_aborts() {
1170        let helper = ActorRuntime::<FakeSyscalls, MemoryBlockstore>::new_test_runtime();
1171        let mut token_state =
1172            Token::<FakeSyscalls, MemoryBlockstore>::create_state(helper.bs()).unwrap();
1173        let mut token = new_token(&helper, &mut token_state);
1174
1175        // force hook to abort
1176        token.runtime.syscalls.abort_next_send.replace(true);
1177        let original_state = token.state().clone();
1178        let mut hook = token
1179            .mint(
1180                TOKEN_ACTOR,
1181                TREASURY,
1182                &TokenAmount::from_atto(1_000_000),
1183                RawBytes::default(),
1184                RawBytes::default(),
1185            )
1186            .unwrap();
1187        token.flush().unwrap();
1188        let err = hook.call(token.runtime).unwrap_err();
1189
1190        // messaging error as we told to abort
1191        if let ReceiverHookError::Messaging(MessagingError::Syscall(e)) = err {
1192            assert_eq!(e, ErrorNumber::AssertionFailed);
1193        } else {
1194            panic!("expected receiver hook error {err:?}");
1195        }
1196
1197        // restore original pre-mint state
1198        // in actor code, we'd just abort and let the VM handle this
1199        token.replace(original_state);
1200
1201        // state remained unchanged
1202        assert_eq!(token.balance_of(TREASURY).unwrap(), TokenAmount::zero());
1203        assert_eq!(token.total_supply(), TokenAmount::zero());
1204        token.assert_invariants().unwrap();
1205    }
1206
1207    #[test]
1208    fn it_burns() {
1209        let helper = ActorRuntime::<FakeSyscalls, MemoryBlockstore>::new_test_runtime();
1210        let mut token_state =
1211            Token::<FakeSyscalls, MemoryBlockstore>::create_state(helper.bs()).unwrap();
1212        let mut token = new_token(&helper, &mut token_state);
1213
1214        let mint_amount = TokenAmount::from_atto(1_000_000);
1215        let burn_amount = TokenAmount::from_atto(600_000);
1216        let mut hook = token
1217            .mint(TOKEN_ACTOR, TREASURY, &mint_amount, Default::default(), Default::default())
1218            .unwrap();
1219        token.flush().unwrap();
1220        hook.call(token.runtime).unwrap();
1221
1222        token.burn(TREASURY, &burn_amount).unwrap();
1223
1224        // total supply decreased
1225        let total_supply = token.total_supply();
1226        assert_eq!(total_supply, TokenAmount::from_atto(400_000));
1227        // treasury balance decreased
1228        assert_eq!(token.balance_of(TREASURY).unwrap(), TokenAmount::from_atto(400_000));
1229        // alice's account unaffected
1230        assert_eq!(token.balance_of(ALICE).unwrap(), TokenAmount::zero());
1231
1232        // cannot burn a negative amount
1233        token.burn(TREASURY, &TokenAmount::from_atto(-1)).unwrap_err();
1234
1235        // balances and supply were unchanged
1236        assert_eq!(token.balance_of(TREASURY).unwrap(), TokenAmount::from_atto(400_000));
1237        assert_eq!(token.total_supply(), TokenAmount::from_atto(400_000));
1238        // alice's account unaffected
1239        assert_eq!(token.balance_of(ALICE).unwrap(), TokenAmount::zero());
1240
1241        // burn zero
1242        token.burn(TREASURY, &TokenAmount::zero()).unwrap();
1243
1244        // balances and supply were unchanged
1245        let remaining_balance = token.balance_of(TREASURY).unwrap();
1246        assert_eq!(remaining_balance, TokenAmount::from_atto(400_000));
1247        assert_eq!(token.total_supply(), TokenAmount::from_atto(400_000));
1248        // alice's account unaffected
1249        assert_eq!(token.balance_of(ALICE).unwrap(), TokenAmount::zero());
1250
1251        // burn exact amount left
1252        token.burn(TREASURY, &remaining_balance).unwrap();
1253        assert_eq!(token.balance_of(TREASURY).unwrap(), TokenAmount::zero());
1254        assert_eq!(token.total_supply(), TokenAmount::zero());
1255        // alice's account unaffected
1256        assert_eq!(token.balance_of(ALICE).unwrap(), TokenAmount::zero());
1257        token.assert_invariants().unwrap();
1258    }
1259
1260    #[test]
1261    fn it_fails_to_burn_below_zero() {
1262        let helper = ActorRuntime::<FakeSyscalls, MemoryBlockstore>::new_test_runtime();
1263        let mut token_state =
1264            Token::<FakeSyscalls, MemoryBlockstore>::create_state(helper.bs()).unwrap();
1265        let mut token = new_token(&helper, &mut token_state);
1266
1267        let mint_amount = TokenAmount::from_atto(1_000_000);
1268        let burn_amount = TokenAmount::from_atto(2_000_000);
1269        let mut hook = token
1270            .mint(TOKEN_ACTOR, TREASURY, &mint_amount, Default::default(), Default::default())
1271            .unwrap();
1272        token.flush().unwrap();
1273        hook.call(token.runtime).unwrap();
1274
1275        token.burn(TREASURY, &burn_amount).unwrap_err();
1276
1277        // balances and supply were unchanged
1278        assert_eq!(token.total_supply(), TokenAmount::from_atto(1_000_000));
1279        assert_eq!(token.balance_of(TREASURY).unwrap(), TokenAmount::from_atto(1_000_000));
1280        token.assert_invariants().unwrap();
1281    }
1282
1283    #[test]
1284    fn it_sets_balances() {
1285        let helper = ActorRuntime::<FakeSyscalls, MemoryBlockstore>::new_test_runtime();
1286        let mut token_state =
1287            Token::<FakeSyscalls, MemoryBlockstore>::create_state(helper.bs()).unwrap();
1288        let mut token = new_token(&helper, &mut token_state);
1289
1290        // check that it obeys granularity
1291        token.granularity = 50;
1292        token.set_balance(ALICE, &TokenAmount::from_atto(49)).unwrap_err();
1293        assert_eq!(token.total_supply(), TokenAmount::zero());
1294
1295        // set balance for Alice to 100
1296        let old_balance = token.set_balance(ALICE, &TokenAmount::from_atto(100)).unwrap();
1297        assert_eq!(old_balance, TokenAmount::zero());
1298        let new_balance = token.balance_of(ALICE).unwrap();
1299        assert_eq!(new_balance, TokenAmount::from_atto(100));
1300        assert_eq!(token.total_supply(), TokenAmount::from_atto(100));
1301
1302        // set balance for Alice to 50
1303        let old_balance = token.set_balance(ALICE, &TokenAmount::from_atto(50)).unwrap();
1304        assert_eq!(old_balance, TokenAmount::from_atto(100));
1305        let new_balance = token.balance_of(ALICE).unwrap();
1306        assert_eq!(new_balance, TokenAmount::from_atto(50));
1307        assert_eq!(token.total_supply(), TokenAmount::from_atto(50));
1308
1309        // attempt to set balance for Alice to negative
1310        token.set_balance(ALICE, &TokenAmount::from_atto(-50)).unwrap_err();
1311        // see that balance was not changed
1312        let new_balance = token.balance_of(ALICE).unwrap();
1313        assert_eq!(new_balance, TokenAmount::from_atto(50));
1314        assert_eq!(token.total_supply(), TokenAmount::from_atto(50));
1315
1316        // set balance for Alice to 0
1317        let old_balance = token.set_balance(ALICE, &TokenAmount::from_atto(0)).unwrap();
1318        assert_eq!(old_balance, TokenAmount::from_atto(50));
1319        let new_balance = token.balance_of(ALICE).unwrap();
1320        assert_eq!(new_balance, TokenAmount::from_atto(0));
1321        assert_eq!(token.total_supply(), TokenAmount::from_atto(0));
1322
1323        // check that the balance map was emptied
1324        token.assert_invariants().unwrap();
1325    }
1326
1327    #[test]
1328    fn it_transfers() {
1329        let helper = ActorRuntime::<FakeSyscalls, MemoryBlockstore>::new_test_runtime();
1330        let mut token_state =
1331            Token::<FakeSyscalls, MemoryBlockstore>::create_state(helper.bs()).unwrap();
1332        let mut token = new_token(&helper, &mut token_state);
1333
1334        // mint 100 for owner
1335        let mut hook = token
1336            .mint(
1337                TOKEN_ACTOR,
1338                ALICE,
1339                &TokenAmount::from_atto(100),
1340                RawBytes::default(),
1341                RawBytes::default(),
1342            )
1343            .unwrap();
1344        token.flush().unwrap();
1345        hook.call(token.runtime).unwrap();
1346        // transfer 60 from owner -> receiver
1347        let mut hook = token
1348            .transfer(
1349                ALICE,
1350                BOB,
1351                &TokenAmount::from_atto(60),
1352                RawBytes::default(),
1353                RawBytes::default(),
1354            )
1355            .unwrap();
1356        token.flush().unwrap();
1357        let intermediate = hook.call(token.runtime).unwrap();
1358        let ret = token.transfer_return(intermediate).unwrap();
1359
1360        // owner has 100 - 60 = 40
1361        assert_eq!(token.balance_of(ALICE).unwrap(), TokenAmount::from_atto(40));
1362        assert_eq!(ret.from_balance, TokenAmount::from_atto(40));
1363        // receiver has 0 + 60 = 60
1364        assert_eq!(token.balance_of(BOB).unwrap(), TokenAmount::from_atto(60));
1365        assert_eq!(ret.to_balance, TokenAmount::from_atto(60));
1366        // total supply is unchanged
1367        assert_eq!(token.total_supply(), TokenAmount::from_atto(100));
1368
1369        // check receiver hook was called with correct shape
1370        assert_last_hook_call_eq(
1371            token.runtime,
1372            FRC46TokenReceived {
1373                operator: ALICE.id().unwrap(),
1374                from: ALICE.id().unwrap(),
1375                amount: TokenAmount::from_atto(60),
1376                to: BOB.id().unwrap(),
1377                operator_data: Default::default(),
1378                token_data: Default::default(),
1379            },
1380        );
1381
1382        // cannot transfer a negative value
1383        token
1384            .transfer(
1385                ALICE,
1386                BOB,
1387                &TokenAmount::from_atto(-1),
1388                RawBytes::default(),
1389                RawBytes::default(),
1390            )
1391            .unwrap_err();
1392        // balances are unchanged
1393        assert_eq!(token.balance_of(ALICE).unwrap(), TokenAmount::from_atto(40));
1394        assert_eq!(token.balance_of(BOB).unwrap(), TokenAmount::from_atto(60));
1395        // total supply is unchanged
1396        assert_eq!(token.total_supply(), TokenAmount::from_atto(100));
1397
1398        // transfer zero value
1399        let mut hook = token
1400            .transfer(ALICE, BOB, &TokenAmount::zero(), RawBytes::default(), RawBytes::default())
1401            .unwrap();
1402        token.flush().unwrap();
1403        hook.call(token.runtime).unwrap();
1404        // balances are unchanged
1405        assert_eq!(token.balance_of(ALICE).unwrap(), TokenAmount::from_atto(40));
1406        assert_eq!(token.balance_of(BOB).unwrap(), TokenAmount::from_atto(60));
1407        // total supply is unchanged
1408        assert_eq!(token.total_supply(), TokenAmount::from_atto(100));
1409
1410        // check receiver hook was called with correct shape
1411        assert_last_hook_call_eq(
1412            token.runtime,
1413            FRC46TokenReceived {
1414                operator: ALICE.id().unwrap(),
1415                from: ALICE.id().unwrap(),
1416                to: BOB.id().unwrap(),
1417                amount: TokenAmount::zero(),
1418                operator_data: Default::default(),
1419                token_data: Default::default(),
1420            },
1421        );
1422    }
1423
1424    #[test]
1425    fn it_transfers_to_self() {
1426        let helper = ActorRuntime::<FakeSyscalls, MemoryBlockstore>::new_test_runtime();
1427        let mut token_state =
1428            Token::<FakeSyscalls, MemoryBlockstore>::create_state(helper.bs()).unwrap();
1429        let mut token = new_token(&helper, &mut token_state);
1430
1431        // mint 100 for owner
1432        let mut hook = token
1433            .mint(
1434                TOKEN_ACTOR,
1435                ALICE,
1436                &TokenAmount::from_atto(100),
1437                RawBytes::default(),
1438                RawBytes::default(),
1439            )
1440            .unwrap();
1441        token.flush().unwrap();
1442        hook.call(token.runtime).unwrap();
1443        // transfer zero to self
1444        let mut hook = token
1445            .transfer(ALICE, ALICE, &TokenAmount::zero(), RawBytes::default(), RawBytes::default())
1446            .unwrap();
1447        token.flush().unwrap();
1448        hook.call(token.runtime).unwrap();
1449
1450        // balances are unchanged
1451        assert_eq!(token.balance_of(ALICE).unwrap(), TokenAmount::from_atto(100));
1452        // total supply is unchanged
1453        assert_eq!(token.total_supply(), TokenAmount::from_atto(100));
1454
1455        // check receiver hook was called with correct shape
1456        assert_last_hook_call_eq(
1457            token.runtime,
1458            FRC46TokenReceived {
1459                operator: ALICE.id().unwrap(),
1460                from: ALICE.id().unwrap(),
1461                to: ALICE.id().unwrap(),
1462                amount: TokenAmount::zero(),
1463                operator_data: Default::default(),
1464                token_data: Default::default(),
1465            },
1466        );
1467
1468        // transfer value to self
1469        let mut hook = token
1470            .transfer(
1471                ALICE,
1472                ALICE,
1473                &TokenAmount::from_atto(10),
1474                RawBytes::default(),
1475                RawBytes::default(),
1476            )
1477            .unwrap();
1478        token.flush().unwrap();
1479        hook.call(token.runtime).unwrap();
1480        // balances are unchanged
1481        assert_eq!(token.balance_of(ALICE).unwrap(), TokenAmount::from_atto(100));
1482        // total supply is unchanged
1483        assert_eq!(token.total_supply(), TokenAmount::from_atto(100));
1484
1485        // check receiver hook was called with correct shape
1486        assert_last_hook_call_eq(
1487            token.runtime,
1488            FRC46TokenReceived {
1489                operator: ALICE.id().unwrap(),
1490                from: ALICE.id().unwrap(),
1491                to: ALICE.id().unwrap(),
1492                amount: TokenAmount::from_atto(10),
1493                operator_data: Default::default(),
1494                token_data: Default::default(),
1495            },
1496        );
1497    }
1498
1499    #[test]
1500    fn it_transfers_to_uninitialized_addresses() {
1501        let helper = ActorRuntime::<FakeSyscalls, MemoryBlockstore>::new_test_runtime();
1502        let mut token_state =
1503            Token::<FakeSyscalls, MemoryBlockstore>::create_state(helper.bs()).unwrap();
1504        let mut token = new_token(&helper, &mut token_state);
1505
1506        let mut hook = token
1507            .mint(
1508                TOKEN_ACTOR,
1509                ALICE,
1510                &TokenAmount::from_atto(100),
1511                RawBytes::default(),
1512                RawBytes::default(),
1513            )
1514            .unwrap();
1515        token.flush().unwrap();
1516        hook.call(token.runtime).unwrap();
1517
1518        // transfer to an uninitialized pubkey
1519        let secp_address = &secp_address();
1520        assert_eq!(token.balance_of(secp_address).unwrap(), TokenAmount::zero());
1521        let mut hook = token
1522            .transfer(
1523                ALICE,
1524                secp_address,
1525                &TokenAmount::from_atto(10),
1526                RawBytes::default(),
1527                RawBytes::default(),
1528            )
1529            .unwrap();
1530        token.flush().unwrap();
1531        hook.call(token.runtime).unwrap();
1532
1533        // balances changed
1534        assert_eq!(token.balance_of(ALICE).unwrap(), TokenAmount::from_atto(90));
1535        assert_eq!(token.balance_of(secp_address).unwrap(), TokenAmount::from_atto(10));
1536
1537        // total supply is unchanged
1538        assert_eq!(token.total_supply(), TokenAmount::from_atto(100));
1539
1540        // check receiver hook was called with correct shape
1541        assert_last_hook_call_eq(
1542            token.runtime,
1543            FRC46TokenReceived {
1544                operator: ALICE.id().unwrap(),
1545                from: ALICE.id().unwrap(),
1546                to: token.runtime.resolve_id(secp_address).unwrap(),
1547                amount: TokenAmount::from_atto(10),
1548                operator_data: Default::default(),
1549                token_data: Default::default(),
1550            },
1551        );
1552    }
1553
1554    #[test]
1555    fn it_transfers_from_uninitialized_addresses() {
1556        let helper = ActorRuntime::<FakeSyscalls, MemoryBlockstore>::new_test_runtime();
1557        let mut token_state =
1558            Token::<FakeSyscalls, MemoryBlockstore>::create_state(helper.bs()).unwrap();
1559        let mut token = new_token(&helper, &mut token_state);
1560
1561        let secp_address = &secp_address();
1562        // non-zero transfer should fail
1563        assert!(token
1564            .transfer(
1565                secp_address,
1566                ALICE,
1567                &TokenAmount::from_atto(1),
1568                Default::default(),
1569                Default::default()
1570            )
1571            .is_err());
1572        // balances unchanged
1573        assert_eq!(token.balance_of(secp_address).unwrap(), TokenAmount::zero());
1574        assert_eq!(token.balance_of(ALICE).unwrap(), TokenAmount::zero());
1575        // supply unchanged
1576        assert_eq!(token.total_supply(), TokenAmount::zero());
1577
1578        // zero-transfer should succeed
1579        let mut hook = token
1580            .transfer(
1581                secp_address,
1582                ALICE,
1583                &TokenAmount::zero(),
1584                RawBytes::default(),
1585                RawBytes::default(),
1586            )
1587            .unwrap();
1588        token.flush().unwrap();
1589        hook.call(token.runtime).unwrap();
1590
1591        // balances unchanged
1592        assert_eq!(token.balance_of(secp_address).unwrap(), TokenAmount::zero());
1593        assert_eq!(token.balance_of(ALICE).unwrap(), TokenAmount::zero());
1594        // supply unchanged
1595        assert_eq!(token.total_supply(), TokenAmount::zero());
1596
1597        // secp_address was initialized
1598        assert!(&token.runtime.resolve_id(secp_address).is_ok());
1599
1600        let actor_address = &actor_address();
1601        // transfers from actors fail with uninitializable
1602        let err = token
1603            .transfer(
1604                actor_address,
1605                ALICE,
1606                &TokenAmount::zero(),
1607                RawBytes::default(),
1608                RawBytes::default(),
1609            )
1610            .unwrap_err();
1611
1612        if let TokenError::Messaging(MessagingError::AddressNotInitialized(e)) = err {
1613            assert_eq!(e, *actor_address);
1614        } else {
1615            panic!("Expected AddressNotInitialized error {err:?}");
1616        }
1617
1618        // balances unchanged
1619        assert_eq!(token.balance_of(secp_address).unwrap(), TokenAmount::zero());
1620        assert_eq!(token.balance_of(ALICE).unwrap(), TokenAmount::zero());
1621        // supply unchanged
1622        assert_eq!(token.total_supply(), TokenAmount::zero());
1623
1624        // actor address was not initialized
1625        assert!(&token.runtime.resolve_id(actor_address).is_err());
1626        token.assert_invariants().unwrap();
1627    }
1628
1629    #[test]
1630    fn it_fails_to_transfer_when_receiver_hook_aborts() {
1631        let helper = ActorRuntime::<FakeSyscalls, MemoryBlockstore>::new_test_runtime();
1632        let mut token_state =
1633            Token::<FakeSyscalls, MemoryBlockstore>::create_state(helper.bs()).unwrap();
1634        let mut token = new_token(&helper, &mut token_state);
1635
1636        // mint 100 for owner
1637        let mut hook = token
1638            .mint(
1639                TOKEN_ACTOR,
1640                ALICE,
1641                &TokenAmount::from_atto(100),
1642                RawBytes::default(),
1643                RawBytes::default(),
1644            )
1645            .unwrap();
1646        token.flush().unwrap();
1647        hook.call(token.runtime).unwrap();
1648
1649        // transfer 60 from owner -> receiver, but simulate receiver aborting the hook
1650        let _ = token.runtime.syscalls.abort_next_send.replace(true);
1651        let pre_transfer_state = token.state().clone();
1652        let mut hook = token
1653            .transfer(
1654                ALICE,
1655                BOB,
1656                &TokenAmount::from_atto(60),
1657                RawBytes::default(),
1658                RawBytes::default(),
1659            )
1660            .unwrap();
1661        token.flush().unwrap();
1662        hook.call(token.runtime).unwrap_err();
1663
1664        // restore original pre-mint state
1665        // in actor code, we'd just abort and let the VM handle this
1666        token.replace(pre_transfer_state);
1667
1668        // balances unchanged
1669        assert_eq!(token.balance_of(ALICE).unwrap(), TokenAmount::from_atto(100));
1670        assert_eq!(token.balance_of(BOB).unwrap(), TokenAmount::from_atto(0));
1671
1672        // transfer 60 from owner -> self, simulate receiver aborting the hook
1673        token.runtime.syscalls.abort_next_send.replace(true);
1674        let pre_transfer_state = token.state().clone();
1675        let mut hook = token
1676            .transfer(
1677                ALICE,
1678                ALICE,
1679                &TokenAmount::from_atto(60),
1680                RawBytes::default(),
1681                RawBytes::default(),
1682            )
1683            .unwrap();
1684        token.flush().unwrap();
1685        hook.call(token.runtime).unwrap_err();
1686
1687        // restore original pre-mint state
1688        // in actor code, we'd just abort and let the VM handle this
1689        token.replace(pre_transfer_state);
1690
1691        // balances unchanged
1692        assert_eq!(token.balance_of(ALICE).unwrap(), TokenAmount::from_atto(100));
1693        assert_eq!(token.balance_of(BOB).unwrap(), TokenAmount::from_atto(0));
1694        token.assert_invariants().unwrap();
1695    }
1696
1697    #[test]
1698    fn it_fails_to_transfer_when_insufficient_balance() {
1699        let helper = ActorRuntime::<FakeSyscalls, MemoryBlockstore>::new_test_runtime();
1700        let mut token_state =
1701            Token::<FakeSyscalls, MemoryBlockstore>::create_state(helper.bs()).unwrap();
1702        let mut token = new_token(&helper, &mut token_state);
1703
1704        // mint 50 for the owner
1705        let mut hook = token
1706            .mint(
1707                TOKEN_ACTOR,
1708                ALICE,
1709                &TokenAmount::from_atto(50),
1710                RawBytes::default(),
1711                RawBytes::default(),
1712            )
1713            .unwrap();
1714        token.flush().unwrap();
1715        hook.call(token.runtime).unwrap();
1716
1717        // attempt transfer 51 from owner -> receiver
1718        token
1719            .transfer(
1720                ALICE,
1721                BOB,
1722                &TokenAmount::from_atto(51),
1723                RawBytes::default(),
1724                RawBytes::default(),
1725            )
1726            .unwrap_err();
1727
1728        // balances remained unchanged
1729        assert_eq!(token.balance_of(ALICE).unwrap(), TokenAmount::from_atto(50));
1730        assert_eq!(token.balance_of(BOB).unwrap(), TokenAmount::zero());
1731        token.assert_invariants().unwrap();
1732    }
1733
1734    #[test]
1735    fn it_tracks_allowances() {
1736        let helper = ActorRuntime::<FakeSyscalls, MemoryBlockstore>::new_test_runtime();
1737        let mut token_state =
1738            Token::<FakeSyscalls, MemoryBlockstore>::create_state(helper.bs()).unwrap();
1739        let mut token = new_token(&helper, &mut token_state);
1740
1741        // set allowance between Alice and Carol as 100
1742        let new_allowance =
1743            token.increase_allowance(ALICE, CAROL, &TokenAmount::from_atto(100)).unwrap();
1744        let allowance = token.allowance(ALICE, CAROL).unwrap();
1745        // return value and allowance should be the same
1746        assert_eq!(new_allowance, allowance);
1747        assert_eq!(allowance, TokenAmount::from_atto(100));
1748
1749        // one-way only
1750        assert_eq!(token.allowance(CAROL, ALICE).unwrap(), TokenAmount::zero());
1751        // unrelated allowance unaffected
1752        assert_eq!(token.allowance(ALICE, BOB).unwrap(), TokenAmount::zero());
1753
1754        // cannot set negative deltas
1755        token.increase_allowance(ALICE, CAROL, &TokenAmount::from_atto(-1)).unwrap_err();
1756        token.decrease_allowance(ALICE, CAROL, &TokenAmount::from_atto(-1)).unwrap_err();
1757
1758        // allowance was unchanged
1759        let allowance = token.allowance(ALICE, CAROL).unwrap();
1760        assert_eq!(allowance, TokenAmount::from_atto(100));
1761
1762        // keeps track of decreasing allowances
1763        let new_allowance =
1764            token.decrease_allowance(ALICE, CAROL, &TokenAmount::from_atto(60)).unwrap();
1765        let allowance = token.allowance(ALICE, CAROL).unwrap();
1766        assert_eq!(new_allowance, allowance);
1767        assert_eq!(allowance, TokenAmount::from_atto(40));
1768
1769        // allowance revoking sets to 0
1770        token.revoke_allowance(ALICE, CAROL).unwrap();
1771        assert_eq!(token.allowance(ALICE, CAROL).unwrap(), TokenAmount::zero());
1772
1773        // allowances cannot be negative, but decreasing an allowance below 0 revokes the allowance
1774        token.increase_allowance(ALICE, CAROL, &TokenAmount::from_atto(10)).unwrap();
1775        let new_allowance =
1776            token.decrease_allowance(ALICE, CAROL, &TokenAmount::from_atto(20)).unwrap();
1777        assert_eq!(new_allowance, TokenAmount::zero());
1778        assert_eq!(token.allowance(ALICE, CAROL).unwrap(), TokenAmount::zero());
1779
1780        // allowances can be set for a pubkey address
1781        let resolvable_address = &secp_address();
1782        assert_eq!(token.allowance(ALICE, resolvable_address).unwrap(), TokenAmount::zero());
1783        token.increase_allowance(ALICE, resolvable_address, &TokenAmount::from_atto(10)).unwrap();
1784        assert_eq!(token.allowance(ALICE, resolvable_address).unwrap(), TokenAmount::from_atto(10));
1785
1786        let initializable_address = &bls_address();
1787        assert_eq!(token.allowance(ALICE, initializable_address).unwrap(), TokenAmount::zero());
1788        token
1789            .increase_allowance(ALICE, initializable_address, &TokenAmount::from_atto(10))
1790            .unwrap();
1791        assert_eq!(
1792            token.allowance(ALICE, initializable_address).unwrap(),
1793            TokenAmount::from_atto(10)
1794        );
1795
1796        let uninitializable_address = &actor_address();
1797        assert_eq!(token.allowance(ALICE, uninitializable_address).unwrap(), TokenAmount::zero());
1798        token
1799            .increase_allowance(ALICE, uninitializable_address, &TokenAmount::from_atto(10))
1800            .unwrap_err();
1801        token.assert_invariants().unwrap();
1802    }
1803
1804    #[test]
1805    fn it_sets_allowances() {
1806        let helper = ActorRuntime::<FakeSyscalls, MemoryBlockstore>::new_test_runtime();
1807        let mut token_state =
1808            Token::<FakeSyscalls, MemoryBlockstore>::create_state(helper.bs()).unwrap();
1809        let mut token = new_token(&helper, &mut token_state);
1810
1811        // set allowance between Alice and Carol as 100
1812        token.set_allowance(ALICE, CAROL, &TokenAmount::from_atto(100)).unwrap();
1813        let allowance = token.allowance(ALICE, CAROL).unwrap();
1814        assert_eq!(allowance, TokenAmount::from_atto(100));
1815
1816        // set allowance between Alice and Carol as 120
1817        token.set_allowance(ALICE, CAROL, &TokenAmount::from_atto(120)).unwrap();
1818        let allowance = token.allowance(ALICE, CAROL).unwrap();
1819        assert_eq!(allowance, TokenAmount::from_atto(120));
1820
1821        // set allowance between Alice and Carol as 0
1822        token.set_allowance(ALICE, CAROL, &TokenAmount::from_atto(0)).unwrap();
1823        let allowance = token.allowance(ALICE, CAROL).unwrap();
1824        assert_eq!(allowance, TokenAmount::from_atto(0));
1825
1826        // attempt to set allowance between Alice and Carol as -50 which should error
1827        token.set_allowance(ALICE, CAROL, &TokenAmount::from_atto(-50)).unwrap_err();
1828
1829        // check invariants (i.e. that the allowance map is emptied after being set to 0)
1830        token.assert_invariants().unwrap();
1831    }
1832
1833    #[test]
1834    fn it_allows_delegated_transfer() {
1835        let helper = ActorRuntime::<FakeSyscalls, MemoryBlockstore>::new_test_runtime();
1836        let mut token_state =
1837            Token::<FakeSyscalls, MemoryBlockstore>::create_state(helper.bs()).unwrap();
1838        let mut token = new_token(&helper, &mut token_state);
1839
1840        // mint 100 for the owner
1841        let mut hook = token
1842            .mint(
1843                ALICE,
1844                ALICE,
1845                &TokenAmount::from_atto(100),
1846                Default::default(),
1847                Default::default(),
1848            )
1849            .unwrap();
1850        token.flush().unwrap();
1851        hook.call(token.runtime).unwrap();
1852
1853        // operator can't transfer without allowance, even if amount is zero
1854        token
1855            .transfer_from(
1856                CAROL,
1857                ALICE,
1858                ALICE,
1859                &TokenAmount::zero(),
1860                RawBytes::default(),
1861                RawBytes::default(),
1862            )
1863            .unwrap_err();
1864
1865        // approve 100 spending allowance for operator
1866        token.increase_allowance(ALICE, CAROL, &TokenAmount::from_atto(100)).unwrap();
1867        // operator makes transfer of 60 from owner -> receiver
1868        let mut hook = token
1869            .transfer_from(
1870                CAROL,
1871                ALICE,
1872                BOB,
1873                &TokenAmount::from_atto(60),
1874                RawBytes::default(),
1875                RawBytes::default(),
1876            )
1877            .unwrap();
1878        token.flush().unwrap();
1879        let intermediate = hook.call(token.runtime).unwrap();
1880        let ret = token.transfer_from_return(intermediate).unwrap();
1881
1882        // verify all balances are correct
1883        assert_eq!(token.balance_of(ALICE).unwrap(), TokenAmount::from_atto(40));
1884        assert_eq!(ret.from_balance, TokenAmount::from_atto(40));
1885        assert_eq!(token.balance_of(BOB).unwrap(), TokenAmount::from_atto(60));
1886        assert_eq!(ret.to_balance, TokenAmount::from_atto(60));
1887        assert_eq!(token.balance_of(CAROL).unwrap(), TokenAmount::zero());
1888        // verify remaining allowance
1889        assert_eq!(token.allowance(ALICE, CAROL).unwrap(), TokenAmount::from_atto(40));
1890        assert_eq!(ret.allowance, TokenAmount::from_atto(40));
1891
1892        // check receiver hook was called with correct shape
1893        assert_last_hook_call_eq(
1894            token.runtime,
1895            FRC46TokenReceived {
1896                operator: CAROL.id().unwrap(),
1897                from: ALICE.id().unwrap(),
1898                to: BOB.id().unwrap(),
1899                amount: TokenAmount::from_atto(60),
1900                operator_data: Default::default(),
1901                token_data: Default::default(),
1902            },
1903        );
1904
1905        // verify allowance is correct
1906        let operator_allowance = token.allowance(ALICE, CAROL).unwrap();
1907        assert_eq!(operator_allowance, TokenAmount::from_atto(40));
1908
1909        // operator makes another transfer of 40 from owner -> self
1910        let mut hook = token
1911            .transfer_from(
1912                CAROL,
1913                ALICE,
1914                CAROL,
1915                &TokenAmount::from_atto(40),
1916                RawBytes::default(),
1917                RawBytes::default(),
1918            )
1919            .unwrap();
1920        token.flush().unwrap();
1921        hook.call(token.runtime).unwrap();
1922
1923        // verify all balances are correct
1924        assert_eq!(token.balance_of(ALICE).unwrap(), TokenAmount::zero());
1925        assert_eq!(token.balance_of(BOB).unwrap(), TokenAmount::from_atto(60));
1926        assert_eq!(token.balance_of(CAROL).unwrap(), TokenAmount::from_atto(40));
1927
1928        // check receiver hook was called with correct shape
1929        assert_last_hook_call_eq(
1930            token.runtime,
1931            FRC46TokenReceived {
1932                operator: CAROL.id().unwrap(),
1933                from: ALICE.id().unwrap(),
1934                to: CAROL.id().unwrap(),
1935                amount: TokenAmount::from_atto(40),
1936                operator_data: Default::default(),
1937                token_data: Default::default(),
1938            },
1939        );
1940
1941        // verify allowance is correct
1942        assert_eq!(token.allowance(ALICE, CAROL).unwrap(), TokenAmount::zero());
1943    }
1944
1945    #[test]
1946    fn it_allows_delegated_transfer_by_resolvable_pubkey() {
1947        let helper = ActorRuntime::<FakeSyscalls, MemoryBlockstore>::new_test_runtime();
1948        let mut token_state =
1949            Token::<FakeSyscalls, MemoryBlockstore>::create_state(helper.bs()).unwrap();
1950        let mut token = new_token(&helper, &mut token_state);
1951        // mint 100 for owner
1952        let mut hook = token
1953            .mint(
1954                TOKEN_ACTOR,
1955                ALICE,
1956                &TokenAmount::from_atto(100),
1957                RawBytes::default(),
1958                RawBytes::default(),
1959            )
1960            .unwrap();
1961        token.flush().unwrap();
1962        hook.call(token.runtime).unwrap();
1963
1964        let initialised_address = &secp_address();
1965        let _ = token.runtime.initialize_account(initialised_address).unwrap();
1966
1967        // an initialised pubkey cannot transfer zero out of Alice balance without an allowance
1968        token
1969            .transfer_from(
1970                initialised_address,
1971                ALICE,
1972                initialised_address,
1973                &TokenAmount::zero(),
1974                RawBytes::default(),
1975                RawBytes::default(),
1976            )
1977            .unwrap_err();
1978
1979        // balances remained same
1980        assert_eq!(token.balance_of(ALICE).unwrap(), TokenAmount::from_atto(100));
1981        assert_eq!(token.balance_of(initialised_address).unwrap(), TokenAmount::zero());
1982        // supply remains same
1983        assert_eq!(token.total_supply(), TokenAmount::from_atto(100));
1984
1985        // initialised pubkey can has zero-allowance, so cannot transfer non-zero amount
1986        token
1987            .transfer_from(
1988                initialised_address,
1989                ALICE,
1990                initialised_address,
1991                &TokenAmount::from_atto(1),
1992                RawBytes::default(),
1993                RawBytes::default(),
1994            )
1995            .unwrap_err();
1996        // balances remained same
1997        assert_eq!(token.balance_of(ALICE).unwrap(), TokenAmount::from_atto(100));
1998        assert_eq!(token.balance_of(initialised_address).unwrap(), TokenAmount::zero());
1999        // supply remains same
2000        assert_eq!(token.total_supply(), TokenAmount::from_atto(100));
2001
2002        // the pubkey can be given an allowance which it can use to transfer tokens
2003        token.increase_allowance(ALICE, initialised_address, &TokenAmount::from_atto(100)).unwrap();
2004        let mut hook = token
2005            .transfer_from(
2006                initialised_address,
2007                ALICE,
2008                initialised_address,
2009                &TokenAmount::from_atto(1),
2010                RawBytes::default(),
2011                RawBytes::default(),
2012            )
2013            .unwrap();
2014        token.flush().unwrap();
2015        hook.call(token.runtime).unwrap();
2016
2017        // balances and allowance changed
2018        assert_eq!(token.balance_of(ALICE).unwrap(), TokenAmount::from_atto(99));
2019        assert_eq!(token.balance_of(initialised_address).unwrap(), TokenAmount::from_atto(1));
2020        assert_eq!(
2021            token.allowance(ALICE, initialised_address).unwrap(),
2022            TokenAmount::from_atto(99)
2023        );
2024        // supply remains same
2025        assert_eq!(token.total_supply(), TokenAmount::from_atto(100));
2026    }
2027
2028    #[test]
2029    fn it_disallows_delgated_transfer_by_uninitialised_pubkey() {
2030        let helper = ActorRuntime::<FakeSyscalls, MemoryBlockstore>::new_test_runtime();
2031        let mut token_state =
2032            Token::<FakeSyscalls, MemoryBlockstore>::create_state(helper.bs()).unwrap();
2033        let mut token = new_token(&helper, &mut token_state);
2034
2035        // mint 100 for owner
2036        let mut hook = token
2037            .mint(
2038                TOKEN_ACTOR,
2039                ALICE,
2040                &TokenAmount::from_atto(100),
2041                RawBytes::default(),
2042                RawBytes::default(),
2043            )
2044            .unwrap();
2045        token.flush().unwrap();
2046        hook.call(token.runtime).unwrap();
2047
2048        // non-zero transfer by an uninitialized pubkey
2049        let secp_address = &secp_address();
2050        let err = token
2051            .transfer_from(
2052                secp_address,
2053                ALICE,
2054                ALICE,
2055                &TokenAmount::from_atto(10),
2056                RawBytes::default(),
2057                RawBytes::default(),
2058            )
2059            .unwrap_err();
2060
2061        // returns the implied insufficient allowance error
2062        match err {
2063            TokenError::TokenState(StateError::InsufficientAllowance {
2064                owner,
2065                operator,
2066                allowance,
2067                delta,
2068            }) => {
2069                assert_eq!(*owner, *ALICE);
2070                assert_eq!(*operator, *secp_address);
2071                assert_eq!(allowance, TokenAmount::zero());
2072                assert_eq!(delta, TokenAmount::from_atto(10));
2073            }
2074            e => panic!("Unexpected error {e:?}"),
2075        }
2076        // balances unchanged
2077        assert_eq!(token.balance_of(secp_address).unwrap(), TokenAmount::zero());
2078        assert_eq!(token.balance_of(ALICE).unwrap(), TokenAmount::from_atto(100));
2079        // supply unchanged
2080        assert_eq!(token.total_supply(), TokenAmount::from_atto(100));
2081        // account wasn't created
2082        assert!(&token.runtime.resolve_id(secp_address).is_err());
2083
2084        // zero transfer by an uninitialized pubkey
2085        let err = token
2086            .transfer_from(
2087                secp_address,
2088                ALICE,
2089                ALICE,
2090                &TokenAmount::zero(),
2091                RawBytes::default(),
2092                RawBytes::default(),
2093            )
2094            .unwrap_err();
2095
2096        // returns the implied insufficient allowance error even for zero transfers
2097        match err {
2098            TokenError::TokenState(StateError::InsufficientAllowance {
2099                owner,
2100                operator,
2101                allowance,
2102                delta,
2103            }) => {
2104                assert_eq!(*owner, *ALICE);
2105                assert_eq!(*operator, *secp_address);
2106                assert_eq!(allowance, TokenAmount::zero());
2107                assert_eq!(delta, TokenAmount::zero());
2108            }
2109            e => panic!("Unexpected error {e:?}"),
2110        }
2111        // balances unchanged
2112        assert_eq!(token.balance_of(secp_address).unwrap(), TokenAmount::zero());
2113        assert_eq!(token.balance_of(ALICE).unwrap(), TokenAmount::from_atto(100));
2114        // supply unchanged
2115        assert_eq!(token.total_supply(), TokenAmount::from_atto(100));
2116        // account wasn't created
2117        assert!(&token.runtime.resolve_id(secp_address).is_err());
2118    }
2119
2120    #[test]
2121    fn it_allows_delegated_burns() {
2122        let helper = ActorRuntime::<FakeSyscalls, MemoryBlockstore>::new_test_runtime();
2123        let mut token_state =
2124            Token::<FakeSyscalls, MemoryBlockstore>::create_state(helper.bs()).unwrap();
2125        let mut token = new_token(&helper, &mut token_state);
2126
2127        let mint_amount = TokenAmount::from_atto(1_000_000);
2128        let approval_amount = TokenAmount::from_atto(600_000);
2129        let burn_amount = TokenAmount::from_atto(600_000);
2130
2131        // mint the total amount
2132        let mut hook = token
2133            .mint(TOKEN_ACTOR, TREASURY, &mint_amount, Default::default(), Default::default())
2134            .unwrap();
2135        token.flush().unwrap();
2136        hook.call(token.runtime).unwrap();
2137
2138        // approve the burner to spend the allowance
2139        token.increase_allowance(TREASURY, ALICE, &approval_amount).unwrap();
2140        // burn the approved amount
2141        token.burn_from(ALICE, TREASURY, &burn_amount).unwrap();
2142
2143        // total supply decreased
2144        assert_eq!(token.total_supply(), TokenAmount::from_atto(400_000));
2145        // treasury balance decreased
2146        assert_eq!(token.balance_of(TREASURY).unwrap(), TokenAmount::from_atto(400_000));
2147        // burner approval decreased
2148        assert_eq!(token.allowance(TREASURY, ALICE).unwrap(), TokenAmount::zero());
2149
2150        // disallows another delegated burn as approval is now zero
2151        // burn the approved amount
2152        token.burn_from(ALICE, TREASURY, &burn_amount).unwrap_err();
2153
2154        // balances didn't change
2155        assert_eq!(token.total_supply(), TokenAmount::from_atto(400_000));
2156        assert_eq!(token.balance_of(TREASURY).unwrap(), TokenAmount::from_atto(400_000));
2157        assert_eq!(token.allowance(TREASURY, ALICE).unwrap(), TokenAmount::zero());
2158
2159        // cannot burn again due to insufficient balance
2160        let err = token.burn_from(ALICE, TREASURY, &burn_amount).unwrap_err();
2161
2162        // gets an allowance error
2163        match err {
2164            TokenError::TokenState(StateError::InsufficientAllowance {
2165                owner,
2166                operator,
2167                allowance,
2168                delta,
2169            }) => {
2170                assert_eq!(*owner, *TREASURY);
2171                assert_eq!(*operator, *ALICE);
2172                assert_eq!(allowance, TokenAmount::zero());
2173                assert_eq!(delta, burn_amount);
2174            }
2175            e => panic!("unexpected error {e:?}"),
2176        };
2177
2178        // balances didn't change
2179        assert_eq!(token.total_supply(), TokenAmount::from_atto(400_000));
2180        assert_eq!(token.balance_of(TREASURY).unwrap(), TokenAmount::from_atto(400_000));
2181        assert_eq!(token.allowance(TREASURY, ALICE).unwrap(), TokenAmount::zero());
2182    }
2183
2184    #[test]
2185    fn it_allows_delegated_burns_by_resolvable_pubkeys() {
2186        let helper = ActorRuntime::<FakeSyscalls, MemoryBlockstore>::new_test_runtime();
2187        let mut token_state =
2188            Token::<FakeSyscalls, MemoryBlockstore>::create_state(helper.bs()).unwrap();
2189        let mut token = new_token(&helper, &mut token_state);
2190
2191        let mint_amount = TokenAmount::from_atto(1_000_000);
2192        let approval_amount = TokenAmount::from_atto(600_000);
2193        let burn_amount = TokenAmount::from_atto(600_000);
2194
2195        // create a resolvable pubkey
2196        let secp_address = &secp_address();
2197        let secp_id = &token.runtime.initialize_account(secp_address).unwrap();
2198
2199        // mint the total amount
2200        let mut hook = token
2201            .mint(TOKEN_ACTOR, TREASURY, &mint_amount, Default::default(), Default::default())
2202            .unwrap();
2203        token.flush().unwrap();
2204        hook.call(token.runtime).unwrap();
2205
2206        // approve the burner to spend the allowance
2207        token.increase_allowance(TREASURY, secp_address, &approval_amount).unwrap();
2208        // burn the approved amount
2209        token.burn_from(secp_address, TREASURY, &burn_amount).unwrap();
2210
2211        // total supply decreased
2212        assert_eq!(token.total_supply(), TokenAmount::from_atto(400_000));
2213        // treasury balance decreased
2214        assert_eq!(token.balance_of(TREASURY).unwrap(), TokenAmount::from_atto(400_000));
2215        // burner approval decreased
2216        assert_eq!(token.allowance(TREASURY, secp_address).unwrap(), TokenAmount::zero());
2217
2218        // cannot burn non-zero again
2219        let err = token.burn_from(secp_address, TREASURY, &burn_amount).unwrap_err();
2220        // gets an allowance error
2221        match err {
2222            TokenError::TokenState(StateError::InsufficientAllowance {
2223                owner,
2224                operator,
2225                allowance,
2226                delta,
2227            }) => {
2228                assert_eq!(*owner, *TREASURY);
2229                assert_eq!(*operator, Address::new_id(*secp_id));
2230                assert_eq!(allowance, TokenAmount::zero());
2231                assert_eq!(delta, burn_amount);
2232            }
2233            e => panic!("unexpected error {e:?}"),
2234        };
2235        // balances unchanged
2236        assert_eq!(token.total_supply(), TokenAmount::from_atto(400_000));
2237        assert_eq!(token.balance_of(TREASURY).unwrap(), TokenAmount::from_atto(400_000));
2238        assert_eq!(token.allowance(TREASURY, secp_address).unwrap(), TokenAmount::zero());
2239
2240        // cannot burn zero now that allowance is zero
2241        let res = token.burn_from(secp_address, TREASURY, &TokenAmount::zero());
2242
2243        // balances unchanged
2244        assert!(res.is_err());
2245        assert_eq!(token.total_supply(), TokenAmount::from_atto(400_000));
2246        assert_eq!(token.balance_of(TREASURY).unwrap(), TokenAmount::from_atto(400_000));
2247        assert_eq!(token.allowance(TREASURY, secp_address).unwrap(), TokenAmount::zero());
2248    }
2249
2250    #[test]
2251    fn it_disallows_delegated_burns_by_uninitialised_pubkeys() {
2252        let helper = ActorRuntime::<FakeSyscalls, MemoryBlockstore>::new_test_runtime();
2253        let mut token_state =
2254            Token::<FakeSyscalls, MemoryBlockstore>::create_state(helper.bs()).unwrap();
2255        let mut token = new_token(&helper, &mut token_state);
2256
2257        let mint_amount = TokenAmount::from_atto(1_000_000);
2258        let burn_amount = TokenAmount::from_atto(600_000);
2259
2260        // create a resolvable pubkey
2261        let secp_address = &secp_address();
2262
2263        // mint the total amount
2264        let mut hook = token
2265            .mint(TOKEN_ACTOR, TREASURY, &mint_amount, Default::default(), Default::default())
2266            .unwrap();
2267        token.flush().unwrap();
2268        hook.call(token.runtime).unwrap();
2269
2270        // cannot burn non-zero
2271        let err = token.burn_from(secp_address, TREASURY, &burn_amount).unwrap_err();
2272        // gets an allowance error
2273        match err {
2274            TokenError::TokenState(StateError::InsufficientAllowance {
2275                owner,
2276                operator,
2277                allowance,
2278                delta,
2279            }) => {
2280                assert_eq!(*owner, *TREASURY);
2281                assert_eq!(*operator, *secp_address);
2282                assert_eq!(allowance, TokenAmount::zero());
2283                assert_eq!(delta, burn_amount);
2284            }
2285            e => panic!("unexpected error {e:?}"),
2286        };
2287        // balances unchanged
2288        assert_eq!(token.total_supply(), mint_amount);
2289        assert_eq!(token.balance_of(TREASURY).unwrap(), mint_amount);
2290        assert_eq!(token.allowance(TREASURY, secp_address).unwrap(), TokenAmount::zero());
2291
2292        // also cannot burn zero
2293        let err = token.burn_from(secp_address, TREASURY, &TokenAmount::zero()).unwrap_err();
2294        // gets an allowance error
2295        match err {
2296            TokenError::TokenState(StateError::InsufficientAllowance {
2297                owner,
2298                operator,
2299                allowance,
2300                delta,
2301            }) => {
2302                assert_eq!(*owner, *TREASURY);
2303                assert_eq!(*operator, *secp_address);
2304                assert_eq!(allowance, TokenAmount::zero());
2305                assert_eq!(delta, TokenAmount::zero());
2306            }
2307            e => panic!("unexpected error {e:?}"),
2308        };
2309        // balances unchanged
2310        assert_eq!(token.total_supply(), mint_amount);
2311        assert_eq!(token.balance_of(TREASURY).unwrap(), mint_amount);
2312        assert_eq!(token.allowance(TREASURY, secp_address).unwrap(), TokenAmount::zero());
2313
2314        // account was not initialised
2315        assert!(&token.runtime.resolve_id(secp_address).is_err());
2316    }
2317
2318    #[test]
2319    fn it_fails_to_transfer_when_insufficient_allowance() {
2320        let helper = ActorRuntime::<FakeSyscalls, MemoryBlockstore>::new_test_runtime();
2321        let mut token_state =
2322            Token::<FakeSyscalls, MemoryBlockstore>::create_state(helper.bs()).unwrap();
2323        let mut token = new_token(&helper, &mut token_state);
2324
2325        // mint 100 for the owner
2326        let mut hook = token
2327            .mint(
2328                TOKEN_ACTOR,
2329                ALICE,
2330                &TokenAmount::from_atto(100),
2331                RawBytes::default(),
2332                RawBytes::default(),
2333            )
2334            .unwrap();
2335        token.flush().unwrap();
2336        hook.call(token.runtime).unwrap();
2337
2338        // approve only 40 spending allowance for operator
2339        token.increase_allowance(ALICE, CAROL, &TokenAmount::from_atto(40)).unwrap();
2340        // operator attempts makes transfer of 60 from owner -> receiver
2341        // this is within the owner's balance but not within the operator's allowance
2342        token
2343            .transfer_from(
2344                CAROL,
2345                ALICE,
2346                BOB,
2347                &TokenAmount::from_atto(60),
2348                RawBytes::default(),
2349                RawBytes::default(),
2350            )
2351            .unwrap_err();
2352
2353        // verify all balances are correct
2354        assert_eq!(token.balance_of(ALICE).unwrap(), TokenAmount::from_atto(100));
2355        assert_eq!(token.balance_of(BOB).unwrap(), TokenAmount::zero());
2356        assert_eq!(token.balance_of(CAROL).unwrap(), TokenAmount::zero());
2357
2358        // verify allowance was not spent
2359        assert_eq!(token.allowance(ALICE, CAROL).unwrap(), TokenAmount::from_atto(40));
2360        token.assert_invariants().unwrap();
2361    }
2362
2363    #[test]
2364    fn it_doesnt_use_allowance_when_insufficent_balance() {
2365        let helper = ActorRuntime::<FakeSyscalls, MemoryBlockstore>::new_test_runtime();
2366        let mut token_state =
2367            Token::<FakeSyscalls, MemoryBlockstore>::create_state(helper.bs()).unwrap();
2368        let mut token = new_token(&helper, &mut token_state);
2369
2370        // mint 50 for the owner
2371        let mut hook = token
2372            .mint(
2373                TOKEN_ACTOR,
2374                ALICE,
2375                &TokenAmount::from_atto(50),
2376                RawBytes::default(),
2377                RawBytes::default(),
2378            )
2379            .unwrap();
2380        token.flush().unwrap();
2381        hook.call(token.runtime).unwrap();
2382
2383        // allow 100 to be spent by operator
2384        token.increase_allowance(ALICE, BOB, &TokenAmount::from_atto(100)).unwrap();
2385
2386        // operator attempts transfer 51 from owner -> operator
2387        // they have enough allowance, but not enough balance
2388        token
2389            .transfer_from(
2390                BOB,
2391                ALICE,
2392                BOB,
2393                &TokenAmount::from_atto(51),
2394                RawBytes::default(),
2395                RawBytes::default(),
2396            )
2397            .unwrap_err();
2398
2399        // attempt burn 51 by operator
2400        token.burn_from(BOB, ALICE, &TokenAmount::from_atto(51)).unwrap_err();
2401
2402        // balances remained unchanged
2403        assert_eq!(token.balance_of(ALICE).unwrap(), TokenAmount::from_atto(50));
2404        assert_eq!(token.balance_of(BOB).unwrap(), TokenAmount::zero());
2405        assert_eq!(token.allowance(ALICE, BOB).unwrap(), TokenAmount::from_atto(100));
2406        token.assert_invariants().unwrap();
2407    }
2408
2409    #[test]
2410    fn it_enforces_granularity() {
2411        let helper = ActorRuntime::<FakeSyscalls, MemoryBlockstore>::new_test_runtime();
2412        let mut token_state =
2413            Token::<FakeSyscalls, MemoryBlockstore>::create_state(helper.bs()).unwrap();
2414        // construct token with 100 granularity
2415        let mut token =
2416            Token::<FakeSyscalls, MemoryBlockstore>::wrap(&helper, 100, &mut token_state);
2417
2418        assert_eq!(token.granularity(), 100);
2419
2420        // Minting
2421        token
2422            .mint(
2423                TOKEN_ACTOR,
2424                ALICE,
2425                &TokenAmount::from_atto(1),
2426                Default::default(),
2427                Default::default(),
2428            )
2429            .expect_err("minted below granularity");
2430        token
2431            .mint(
2432                TOKEN_ACTOR,
2433                ALICE,
2434                &TokenAmount::from_atto(10),
2435                RawBytes::default(),
2436                RawBytes::default(),
2437            )
2438            .expect_err("minted below granularity");
2439        token
2440            .mint(
2441                TOKEN_ACTOR,
2442                ALICE,
2443                &TokenAmount::from_atto(99),
2444                RawBytes::default(),
2445                RawBytes::default(),
2446            )
2447            .expect_err("minted below granularity");
2448        token
2449            .mint(
2450                TOKEN_ACTOR,
2451                ALICE,
2452                &TokenAmount::from_atto(101),
2453                RawBytes::default(),
2454                RawBytes::default(),
2455            )
2456            .expect_err("minted below granularity");
2457        let mut hook = token
2458            .mint(
2459                TOKEN_ACTOR,
2460                ALICE,
2461                &TokenAmount::from_atto(0),
2462                Default::default(),
2463                Default::default(),
2464            )
2465            .unwrap();
2466        token.flush().unwrap();
2467        hook.call(token.runtime).unwrap();
2468        let mut hook = token
2469            .mint(
2470                TOKEN_ACTOR,
2471                ALICE,
2472                &TokenAmount::from_atto(100),
2473                RawBytes::default(),
2474                RawBytes::default(),
2475            )
2476            .unwrap();
2477        token.flush().unwrap();
2478        hook.call(token.runtime).unwrap();
2479        let mut hook = token
2480            .mint(
2481                TOKEN_ACTOR,
2482                ALICE,
2483                &TokenAmount::from_atto(200),
2484                RawBytes::default(),
2485                RawBytes::default(),
2486            )
2487            .unwrap();
2488        token.flush().unwrap();
2489        hook.call(token.runtime).unwrap();
2490        let mut hook = token
2491            .mint(
2492                TOKEN_ACTOR,
2493                ALICE,
2494                &TokenAmount::from_atto(1000),
2495                RawBytes::default(),
2496                RawBytes::default(),
2497            )
2498            .unwrap();
2499        token.flush().unwrap();
2500        hook.call(token.runtime).unwrap();
2501
2502        // Burn
2503        token.burn(ALICE, &TokenAmount::from_atto(1)).expect_err("burned below granularity");
2504        token.burn(ALICE, &TokenAmount::from_atto(0)).unwrap();
2505        token.burn(ALICE, &TokenAmount::from_atto(100)).unwrap();
2506
2507        // Transfer
2508        token
2509            .transfer(
2510                ALICE,
2511                BOB,
2512                &TokenAmount::from_atto(1),
2513                RawBytes::default(),
2514                RawBytes::default(),
2515            )
2516            .expect_err("transfer delta below granularity");
2517        let mut hook = token
2518            .transfer(
2519                ALICE,
2520                BOB,
2521                &TokenAmount::from_atto(0),
2522                RawBytes::default(),
2523                RawBytes::default(),
2524            )
2525            .unwrap();
2526        token.flush().unwrap();
2527        hook.call(token.runtime).unwrap();
2528        let mut hook = token
2529            .transfer(
2530                ALICE,
2531                BOB,
2532                &TokenAmount::from_atto(100),
2533                RawBytes::default(),
2534                RawBytes::default(),
2535            )
2536            .unwrap();
2537        token.flush().unwrap();
2538        hook.call(token.runtime).unwrap();
2539    }
2540
2541    #[test]
2542    fn it_doesnt_initialize_accounts_when_default_values_can_be_returned() {
2543        let helper = ActorRuntime::<FakeSyscalls, MemoryBlockstore>::new_test_runtime();
2544        let mut token_state =
2545            Token::<FakeSyscalls, MemoryBlockstore>::create_state(helper.bs()).unwrap();
2546        let token = new_token(&helper, &mut token_state);
2547
2548        let secp = &secp_address();
2549        let bls = &bls_address();
2550
2551        // allowances are all zero
2552        let allowance = token.allowance(secp, bls).unwrap();
2553        assert_eq!(allowance, TokenAmount::zero());
2554        let allowance = token.allowance(bls, secp).unwrap();
2555        assert_eq!(allowance, TokenAmount::zero());
2556        let allowance = token.allowance(ALICE, bls).unwrap();
2557        assert_eq!(allowance, TokenAmount::zero());
2558        let allowance = token.allowance(bls, ALICE).unwrap();
2559        assert_eq!(allowance, TokenAmount::zero());
2560
2561        // accounts were not initialized
2562        let err = &token.runtime.resolve_id(bls).unwrap_err();
2563        if let MessagingError::AddressNotResolved(e) = err {
2564            assert_eq!(e, bls);
2565        } else {
2566            panic!("expected AddressNotResolved error");
2567        }
2568        let err = &token.runtime.resolve_id(secp).unwrap_err();
2569        if let MessagingError::AddressNotResolved(e) = err {
2570            assert_eq!(e, secp);
2571        } else {
2572            panic!("expected AddressNotResolved error");
2573        }
2574
2575        // balances are zero
2576        let balance = token.balance_of(secp).unwrap();
2577        assert_eq!(balance, TokenAmount::zero());
2578        let balance = token.balance_of(bls).unwrap();
2579        assert_eq!(balance, TokenAmount::zero());
2580
2581        // accounts were not initialized
2582        let err = &token.runtime.resolve_id(bls).unwrap_err();
2583        if let MessagingError::AddressNotResolved(e) = err {
2584            assert_eq!(e, bls);
2585        } else {
2586            panic!("expected AddressNotResolved error");
2587        }
2588        let err = &token.runtime.resolve_id(secp).unwrap_err();
2589        if let MessagingError::AddressNotResolved(e) = err {
2590            assert_eq!(e, secp);
2591        } else {
2592            panic!("expected AddressNotResolved error");
2593        }
2594    }
2595
2596    #[test]
2597    fn test_account_combinations() {
2598        fn setup_accounts<'st>(
2599            operator: &Address,
2600            from: &Address,
2601            allowance: &TokenAmount,
2602            balance: &TokenAmount,
2603            runtime: &'st ActorRuntime<FakeSyscalls, MemoryBlockstore>,
2604            state: &'st mut TokenState,
2605        ) -> Token<'st, FakeSyscalls, MemoryBlockstore> {
2606            // fresh token state
2607            let mut token = new_token(runtime, state);
2608            // set allowance if not zero (avoiding unecessary account instantiation)
2609            if !allowance.is_zero() && from != operator {
2610                token.increase_allowance(from, operator, allowance).unwrap();
2611            }
2612            // set balance if not zero (avoiding unecessary account insantiation)
2613            if !balance.is_zero() {
2614                let mut hook = token
2615                    .mint(from, from, balance, Default::default(), Default::default())
2616                    .unwrap();
2617                token.flush().unwrap();
2618                hook.call(token.runtime).unwrap();
2619            }
2620            token
2621        }
2622
2623        fn assert_behaviour(
2624            operator: &Address,
2625            from: &Address,
2626            allowance: u32,
2627            balance: u32,
2628            transfer: u32,
2629            behaviour: &str,
2630        ) {
2631            let helper = ActorRuntime::<FakeSyscalls, MemoryBlockstore>::new_test_runtime();
2632            let mut token_state = TokenState::new(&helper).unwrap();
2633            let mut token = setup_accounts(
2634                operator,
2635                from,
2636                &TokenAmount::from_atto(allowance),
2637                &TokenAmount::from_atto(balance),
2638                &helper,
2639                &mut token_state,
2640            );
2641
2642            let assert_error = |err: TokenError, token: Token<FakeSyscalls, MemoryBlockstore>| {
2643                match behaviour {
2644                    "ALLOWANCE_ERR" => {
2645                        if let TokenError::TokenState(StateError::InsufficientAllowance {
2646                            // can't match addresses as may be pubkey or ID (though they would resolve to the same)
2647                            owner: _,
2648                            operator: _,
2649                            allowance: a,
2650                            delta,
2651                        }) = err
2652                        {
2653                            assert_eq!(a, TokenAmount::from_atto(allowance));
2654                            assert_eq!(delta, TokenAmount::from_atto(transfer));
2655                        } else {
2656                            panic!("unexpected error {err:?}");
2657                        }
2658                    }
2659                    "BALANCE_ERR" => {
2660                        if let TokenError::TokenState(StateError::InsufficientBalance {
2661                            owner,
2662                            balance: b,
2663                            delta,
2664                        }) = err
2665                        {
2666                            assert_eq!(owner, token.runtime.resolve_id(from).unwrap());
2667                            assert_eq!(delta, TokenAmount::from_atto(transfer).neg());
2668                            assert_eq!(b, TokenAmount::from_atto(balance));
2669                        } else {
2670                            panic!("unexpected error {err:?}");
2671                        }
2672                    }
2673                    "ADDRESS_ERR" => {
2674                        if let TokenError::Messaging(MessagingError::AddressNotInitialized(addr)) =
2675                            err
2676                        {
2677                            assert!((addr == *operator) || (addr == *from));
2678                        } else {
2679                            panic!("unexpected error {err:?}");
2680                        }
2681                    }
2682                    _ => panic!("test case not implemented"),
2683                }
2684            };
2685
2686            if token.runtime.same_address(operator, from) {
2687                let res = token.transfer(
2688                    from,
2689                    operator,
2690                    &TokenAmount::from_atto(transfer),
2691                    RawBytes::default(),
2692                    RawBytes::default(),
2693                );
2694
2695                if behaviour != "OK" {
2696                    assert_error(res.unwrap_err(), token);
2697                } else {
2698                    let mut hook = res.expect("expect transfer to succeed");
2699                    hook.call(token.runtime).expect("receiver hook should succeed");
2700                }
2701            } else {
2702                let res = token.transfer_from(
2703                    operator,
2704                    from,
2705                    operator,
2706                    &TokenAmount::from_atto(transfer),
2707                    RawBytes::default(),
2708                    RawBytes::default(),
2709                );
2710
2711                if behaviour != "OK" {
2712                    assert_error(res.unwrap_err(), token);
2713                } else {
2714                    let mut hook = res.expect("expect transfer to succeed");
2715                    hook.call(token.runtime).expect("receiver hook should succeed");
2716                }
2717            }
2718        }
2719
2720        // distinct resolvable address operates on resolvable address
2721        assert_behaviour(ALICE, BOB, 0, 0, 0, "ALLOWANCE_ERR");
2722        assert_behaviour(ALICE, BOB, 0, 0, 1, "ALLOWANCE_ERR");
2723        assert_behaviour(ALICE, BOB, 0, 1, 0, "ALLOWANCE_ERR");
2724        assert_behaviour(ALICE, BOB, 0, 1, 1, "ALLOWANCE_ERR");
2725        assert_behaviour(ALICE, BOB, 1, 0, 0, "OK");
2726        assert_behaviour(ALICE, BOB, 1, 0, 1, "BALANCE_ERR");
2727        assert_behaviour(ALICE, BOB, 1, 1, 0, "OK");
2728        assert_behaviour(ALICE, BOB, 1, 1, 1, "OK");
2729
2730        // initialisable (but uninitialised) address operates on resolved address
2731        assert_behaviour(&secp_address(), BOB, 0, 0, 0, "ALLOWANCE_ERR");
2732        assert_behaviour(&secp_address(), BOB, 0, 0, 1, "ALLOWANCE_ERR");
2733        assert_behaviour(&secp_address(), BOB, 0, 1, 0, "ALLOWANCE_ERR");
2734        assert_behaviour(&secp_address(), BOB, 0, 1, 1, "ALLOWANCE_ERR");
2735        // impossible to have non-zero allowance specified for uninitialised address
2736
2737        // resolvable address operates on initialisable address
2738        assert_behaviour(BOB, &secp_address(), 0, 0, 0, "ALLOWANCE_ERR");
2739        assert_behaviour(BOB, &secp_address(), 0, 0, 1, "ALLOWANCE_ERR");
2740        // impossible to have uninitialised address have a balance
2741        // impossible to have non-zero allowance specified by an uninitialised address
2742
2743        // distinct uninitialised address operates on uninitialised address
2744        assert_behaviour(&bls_address(), &secp_address(), 0, 0, 0, "ALLOWANCE_ERR");
2745        assert_behaviour(&bls_address(), &secp_address(), 0, 0, 1, "ALLOWANCE_ERR");
2746        // impossible to have uninitialised address have a balance
2747        // impossible to have non-zero allowance specified by an uninitialised address
2748
2749        // distinct actor address operates on actor address
2750        assert_behaviour(&Address::new_actor(&[1]), &actor_address(), 0, 0, 0, "ALLOWANCE_ERR");
2751        assert_behaviour(&Address::new_actor(&[1]), &actor_address(), 0, 0, 1, "ALLOWANCE_ERR");
2752        // impossible for actor to have balance (for now)
2753        // impossible for actor to have allowance (for now)
2754
2755        // actor addresses are currently never initialisable, but may be in the future, so it returns allowance errors to mirror pubkey behaviour
2756        // id address operates on actor address
2757        assert_behaviour(ALICE, &actor_address(), 0, 0, 0, "ALLOWANCE_ERR");
2758        assert_behaviour(ALICE, &actor_address(), 0, 0, 1, "ALLOWANCE_ERR");
2759        // impossible for actor to have balance (for now)
2760        // impossible for actor to have allowance (for now)
2761        // currently the same actor will fail to transfer to itself, here it fails with an address error as for self transfers, we
2762        // attempt to resolve the "from" address but currently that's not possible
2763        assert_behaviour(&actor_address(), &actor_address(), 0, 0, 0, "ADDRESS_ERR");
2764        assert_behaviour(&actor_address(), &actor_address(), 0, 0, 1, "ADDRESS_ERR");
2765
2766        // transfers should never fail with allowance err when the operator is the owner
2767        // all allowance err should be replaced with successes or balance errs
2768        assert_behaviour(ALICE, ALICE, 0, 0, 0, "OK");
2769        assert_behaviour(ALICE, ALICE, 0, 0, 1, "BALANCE_ERR");
2770        assert_behaviour(ALICE, ALICE, 0, 1, 0, "OK");
2771        assert_behaviour(ALICE, ALICE, 0, 1, 1, "OK");
2772        assert_behaviour(ALICE, ALICE, 1, 0, 0, "OK");
2773        assert_behaviour(ALICE, ALICE, 1, 0, 1, "BALANCE_ERR");
2774        assert_behaviour(ALICE, ALICE, 1, 1, 0, "OK");
2775        assert_behaviour(ALICE, ALICE, 1, 1, 1, "OK");
2776        // pubkey to pubkey
2777        assert_behaviour(&secp_address(), &secp_address(), 0, 0, 0, "OK");
2778        assert_behaviour(&secp_address(), &secp_address(), 0, 0, 1, "BALANCE_ERR");
2779        assert_behaviour(&bls_address(), &bls_address(), 0, 0, 0, "OK");
2780        assert_behaviour(&bls_address(), &bls_address(), 0, 0, 1, "BALANCE_ERR");
2781    }
2782
2783    #[test]
2784    fn check_invariants_returns_a_state_summary() {
2785        //! Simulate a delgated transfer flow and then check the invariants manually
2786        let helper = ActorRuntime::<FakeSyscalls, MemoryBlockstore>::new_test_runtime();
2787        let mut token_state =
2788            Token::<FakeSyscalls, MemoryBlockstore>::create_state(helper.bs()).unwrap();
2789        let mut token = new_token(&helper, &mut token_state);
2790
2791        // mint 100 for the owner
2792        let mut hook = token
2793            .mint(
2794                ALICE,
2795                ALICE,
2796                &TokenAmount::from_atto(100),
2797                Default::default(),
2798                Default::default(),
2799            )
2800            .unwrap();
2801        token.flush().unwrap();
2802        hook.call(token.runtime).unwrap();
2803
2804        // approve 100 spending allowance for operator
2805        token.increase_allowance(ALICE, CAROL, &TokenAmount::from_atto(100)).unwrap();
2806        // operator makes transfer of 60 from owner -> receiver
2807        let mut hook = token
2808            .transfer_from(
2809                CAROL,
2810                ALICE,
2811                BOB,
2812                &TokenAmount::from_atto(60),
2813                RawBytes::default(),
2814                RawBytes::default(),
2815            )
2816            .unwrap();
2817        token.flush().unwrap();
2818        hook.call(token.runtime).unwrap();
2819
2820        let summary = token.assert_invariants().unwrap();
2821        // remaining balance 100 - 60
2822        let balance_map = summary.balance_map.unwrap();
2823        assert_eq!(
2824            balance_map.get(&ALICE.id().unwrap()).unwrap().clone(),
2825            TokenAmount::from_atto(40)
2826        );
2827        // received balance = 0 + 60
2828        assert_eq!(
2829            balance_map.get(&BOB.id().unwrap()).unwrap().clone(),
2830            TokenAmount::from_atto(60)
2831        );
2832        // remaining allowance = 100 - 60
2833        assert_eq!(
2834            summary
2835                .allowance_map
2836                .unwrap()
2837                .get(&ALICE.id().unwrap())
2838                .unwrap()
2839                .get(&CAROL.id().unwrap())
2840                .unwrap()
2841                .clone(),
2842            TokenAmount::from_atto(40)
2843        );
2844    }
2845
2846    // TODO: test for re-entrancy bugs by implementing a MethodCaller that calls back on the token contract
2847}