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
33pub const TOKEN_PRECISION: u64 = 1_000_000_000_000_000_000;
36
37type Result<T> = std::result::Result<T, TokenError>;
38
39pub struct Token<'st, S, BS>
43where
44 S: Syscalls,
45 BS: Blockstore,
46{
47 runtime: &'st ActorRuntime<S, BS>,
49 state: &'st mut TokenState,
51 granularity: u64,
57}
58
59impl<'st, S, BS> Token<'st, S, BS>
60where
61 S: Syscalls,
62 BS: Blockstore,
63{
64 pub fn create_state(bs: &BS) -> Result<TokenState> {
69 Ok(TokenState::new(bs)?)
70 }
71
72 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 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 pub fn replace(&mut self, state: TokenState) -> TokenState {
93 std::mem::replace(self.state, state)
94 }
95
96 pub fn load_state(bs: &BS, state_cid: &Cid) -> Result<TokenState> {
98 Ok(TokenState::load(bs, state_cid)?)
99 }
100
101 pub fn load_replace(&mut self, cid: &Cid) -> Result<TokenState> {
105 Ok(self.replace(Self::load_state(self.runtime.bs(), cid)?))
106 }
107
108 pub fn flush(&mut self) -> Result<Cid> {
110 Ok(self.state.save(&self.runtime)?)
111 }
112
113 pub fn state(&self) -> &TokenState {
115 self.state
116 }
117
118 pub fn runtime(&self) -> &ActorRuntime<S, BS> {
120 self.runtime
121 }
122
123 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 *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 pub fn granularity(&self) -> u64 {
150 self.granularity
151 }
152
153 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 let operator_id = self.runtime.resolve_or_init(operator)?;
174 let owner_id = self.runtime.resolve_or_init(initial_owner)?;
176
177 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 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 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 pub fn total_supply(&self) -> TokenAmount {
217 self.state.supply.clone()
218 }
219
220 pub fn balance_of(&self, owner: &Address) -> Result<TokenAmount> {
224 match self.runtime.resolve_id(owner) {
227 Ok(owner) => Ok(self.state.get_balance(&self.runtime, owner)?),
228 Err(MessagingError::AddressNotResolved(_)) => {
229 Ok(TokenAmount::zero())
231 }
232 Err(e) => Err(e.into()),
233 }
234 }
235
236 pub fn allowance(&self, owner: &Address, operator: &Address) -> Result<TokenAmount> {
241 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 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 Ok(self.state.get_allowance_between(&self.runtime, owner, operator)?)
263 }
264
265 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 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 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 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 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 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 return Ok(TokenAmount::zero());
327 }
328 Err(e) => return Err(e.into()),
329 };
330 Ok(self.state.revoke_allowance(&self.runtime, owner, operator)?)
332 }
333
334 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 if amount.is_zero() {
345 return self.revoke_allowance(owner, operator);
346 }
347
348 let owner = self.runtime.resolve_or_init(owner)?;
350 let operator = self.runtime.resolve_or_init(operator)?;
351
352 Ok(self.state.set_allowance(&self.runtime, owner, operator, amount)?)
354 }
355
356 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 let new_amount = state.change_balance_by(&bs, owner, &amount.clone().neg())?;
374 state.change_supply_by(&amount.neg())?;
376 Ok(BurnReturn { balance: new_amount })
377 })
378 }
379
380 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 let operator = match self.runtime.resolve_id(operator) {
408 Ok(operator) => operator,
409 Err(MessagingError::AddressNotResolved(addr)) => {
410 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 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 let new_balance = state.change_balance_by(&bs, owner, &amount.clone().neg())?;
442 state.change_supply_by(&amount.neg())?;
444 Ok(BurnFromReturn { balance: new_balance, allowance: new_allowance })
445 })
446 }
447
448 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 let from_id = self.runtime.resolve_or_init(from)?;
479 let to_id = self.runtime.resolve_or_init(to)?;
480 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 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 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 let operator_id = match self.runtime.resolve_id(operator) {
547 Ok(id) => id,
549 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 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 let to_id = self.runtime.resolve_or_init(to)?;
577
578 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 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)?, recipient_data: intermediate.recipient_data,
614 })
615 }
616
617 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 let old_balance = state.set_balance(bs, owner, amount)?;
628 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 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(¶ms)?,
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 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 pub fn check_invariants(&self) -> (StateSummary, Vec<StateInvariantError>) {
678 self.state.check_invariants(&self.runtime, self.granularity)
679 }
680}
681
682pub 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
701pub 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 fn secp_address() -> Address {
735 let key = vec![0; 65];
736 Address::new_secp256k1(key.as_slice()).unwrap()
737 }
738
739 fn bls_address() -> Address {
741 let key = vec![0; BLS_PUB_LEN];
742 Address::new_bls(key.as_slice()).unwrap()
743 }
744
745 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 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 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 assert_eq!(state.supply, TokenAmount::from_atto(1));
806 assert_eq!(actor_state.token_state.supply, TokenAmount::from_atto(1));
808
809 }
814
815 #[test]
816 fn it_instantiates_and_persists() {
817 let helper = ActorRuntime::<FakeSyscalls, MemoryBlockstore>::new_test_runtime();
818 let mut state = Token::<FakeSyscalls, MemoryBlockstore>::create_state(helper.bs()).unwrap();
820 let mut token = new_token(&helper, &mut state);
822
823 assert_eq!(token.total_supply(), TokenAmount::zero());
825
826 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 let cid = token.flush().unwrap();
843
844 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 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 assert_eq!(token.total_supply(), TokenAmount::from_atto(100));
889
890 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 }
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 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 token
920 .transaction(|state, _bs| {
921 state.change_supply_by(&TokenAmount::from_atto(-100))?;
922 state.change_supply_by(&TokenAmount::from_atto(-100))?;
923 state.change_supply_by(&TokenAmount::from_atto(-100))?;
925 Ok(())
926 })
927 .unwrap_err();
928 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(); 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 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 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 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 assert_eq!(token.balance_of(ALICE).unwrap(), TokenAmount::zero());
988 assert_eq!(token.total_supply(), TokenAmount::from_atto(1_000_000));
989
990 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 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 assert_eq!(token.balance_of(ALICE).unwrap(), TokenAmount::zero());
1012 assert_eq!(token.total_supply(), TokenAmount::from_atto(1_000_000));
1013
1014 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 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 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 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 assert_eq!(token.balance_of(CAROL).unwrap(), TokenAmount::zero());
1082
1083 let secp_address = secp_address();
1085 assert_eq!(token.balance_of(&secp_address).unwrap(), TokenAmount::zero());
1087 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 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 let bls_address = bls_address();
1115 assert_eq!(token.balance_of(&bls_address).unwrap(), TokenAmount::zero());
1117 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 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 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 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 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 token.replace(original_state);
1200
1201 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 let total_supply = token.total_supply();
1226 assert_eq!(total_supply, TokenAmount::from_atto(400_000));
1227 assert_eq!(token.balance_of(TREASURY).unwrap(), TokenAmount::from_atto(400_000));
1229 assert_eq!(token.balance_of(ALICE).unwrap(), TokenAmount::zero());
1231
1232 token.burn(TREASURY, &TokenAmount::from_atto(-1)).unwrap_err();
1234
1235 assert_eq!(token.balance_of(TREASURY).unwrap(), TokenAmount::from_atto(400_000));
1237 assert_eq!(token.total_supply(), TokenAmount::from_atto(400_000));
1238 assert_eq!(token.balance_of(ALICE).unwrap(), TokenAmount::zero());
1240
1241 token.burn(TREASURY, &TokenAmount::zero()).unwrap();
1243
1244 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 assert_eq!(token.balance_of(ALICE).unwrap(), TokenAmount::zero());
1250
1251 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 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 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 token.granularity = 50;
1292 token.set_balance(ALICE, &TokenAmount::from_atto(49)).unwrap_err();
1293 assert_eq!(token.total_supply(), TokenAmount::zero());
1294
1295 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 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 token.set_balance(ALICE, &TokenAmount::from_atto(-50)).unwrap_err();
1311 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 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 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 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 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 assert_eq!(token.balance_of(ALICE).unwrap(), TokenAmount::from_atto(40));
1362 assert_eq!(ret.from_balance, TokenAmount::from_atto(40));
1363 assert_eq!(token.balance_of(BOB).unwrap(), TokenAmount::from_atto(60));
1365 assert_eq!(ret.to_balance, TokenAmount::from_atto(60));
1366 assert_eq!(token.total_supply(), TokenAmount::from_atto(100));
1368
1369 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 token
1384 .transfer(
1385 ALICE,
1386 BOB,
1387 &TokenAmount::from_atto(-1),
1388 RawBytes::default(),
1389 RawBytes::default(),
1390 )
1391 .unwrap_err();
1392 assert_eq!(token.balance_of(ALICE).unwrap(), TokenAmount::from_atto(40));
1394 assert_eq!(token.balance_of(BOB).unwrap(), TokenAmount::from_atto(60));
1395 assert_eq!(token.total_supply(), TokenAmount::from_atto(100));
1397
1398 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 assert_eq!(token.balance_of(ALICE).unwrap(), TokenAmount::from_atto(40));
1406 assert_eq!(token.balance_of(BOB).unwrap(), TokenAmount::from_atto(60));
1407 assert_eq!(token.total_supply(), TokenAmount::from_atto(100));
1409
1410 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 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 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 assert_eq!(token.balance_of(ALICE).unwrap(), TokenAmount::from_atto(100));
1452 assert_eq!(token.total_supply(), TokenAmount::from_atto(100));
1454
1455 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 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 assert_eq!(token.balance_of(ALICE).unwrap(), TokenAmount::from_atto(100));
1482 assert_eq!(token.total_supply(), TokenAmount::from_atto(100));
1484
1485 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 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 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 assert_eq!(token.total_supply(), TokenAmount::from_atto(100));
1539
1540 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 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 assert_eq!(token.balance_of(secp_address).unwrap(), TokenAmount::zero());
1574 assert_eq!(token.balance_of(ALICE).unwrap(), TokenAmount::zero());
1575 assert_eq!(token.total_supply(), TokenAmount::zero());
1577
1578 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 assert_eq!(token.balance_of(secp_address).unwrap(), TokenAmount::zero());
1593 assert_eq!(token.balance_of(ALICE).unwrap(), TokenAmount::zero());
1594 assert_eq!(token.total_supply(), TokenAmount::zero());
1596
1597 assert!(&token.runtime.resolve_id(secp_address).is_ok());
1599
1600 let actor_address = &actor_address();
1601 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 assert_eq!(token.balance_of(secp_address).unwrap(), TokenAmount::zero());
1620 assert_eq!(token.balance_of(ALICE).unwrap(), TokenAmount::zero());
1621 assert_eq!(token.total_supply(), TokenAmount::zero());
1623
1624 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 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 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 token.replace(pre_transfer_state);
1667
1668 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 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 token.replace(pre_transfer_state);
1690
1691 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 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 token
1719 .transfer(
1720 ALICE,
1721 BOB,
1722 &TokenAmount::from_atto(51),
1723 RawBytes::default(),
1724 RawBytes::default(),
1725 )
1726 .unwrap_err();
1727
1728 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 let new_allowance =
1743 token.increase_allowance(ALICE, CAROL, &TokenAmount::from_atto(100)).unwrap();
1744 let allowance = token.allowance(ALICE, CAROL).unwrap();
1745 assert_eq!(new_allowance, allowance);
1747 assert_eq!(allowance, TokenAmount::from_atto(100));
1748
1749 assert_eq!(token.allowance(CAROL, ALICE).unwrap(), TokenAmount::zero());
1751 assert_eq!(token.allowance(ALICE, BOB).unwrap(), TokenAmount::zero());
1753
1754 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 let allowance = token.allowance(ALICE, CAROL).unwrap();
1760 assert_eq!(allowance, TokenAmount::from_atto(100));
1761
1762 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 token.revoke_allowance(ALICE, CAROL).unwrap();
1771 assert_eq!(token.allowance(ALICE, CAROL).unwrap(), TokenAmount::zero());
1772
1773 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 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 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 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 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 token.set_allowance(ALICE, CAROL, &TokenAmount::from_atto(-50)).unwrap_err();
1828
1829 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 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 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 token.increase_allowance(ALICE, CAROL, &TokenAmount::from_atto(100)).unwrap();
1867 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 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 assert_eq!(token.allowance(ALICE, CAROL).unwrap(), TokenAmount::from_atto(40));
1890 assert_eq!(ret.allowance, TokenAmount::from_atto(40));
1891
1892 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 let operator_allowance = token.allowance(ALICE, CAROL).unwrap();
1907 assert_eq!(operator_allowance, TokenAmount::from_atto(40));
1908
1909 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 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 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 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 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 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 assert_eq!(token.balance_of(ALICE).unwrap(), TokenAmount::from_atto(100));
1981 assert_eq!(token.balance_of(initialised_address).unwrap(), TokenAmount::zero());
1982 assert_eq!(token.total_supply(), TokenAmount::from_atto(100));
1984
1985 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 assert_eq!(token.balance_of(ALICE).unwrap(), TokenAmount::from_atto(100));
1998 assert_eq!(token.balance_of(initialised_address).unwrap(), TokenAmount::zero());
1999 assert_eq!(token.total_supply(), TokenAmount::from_atto(100));
2001
2002 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 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 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 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 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 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 assert_eq!(token.balance_of(secp_address).unwrap(), TokenAmount::zero());
2078 assert_eq!(token.balance_of(ALICE).unwrap(), TokenAmount::from_atto(100));
2079 assert_eq!(token.total_supply(), TokenAmount::from_atto(100));
2081 assert!(&token.runtime.resolve_id(secp_address).is_err());
2083
2084 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 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 assert_eq!(token.balance_of(secp_address).unwrap(), TokenAmount::zero());
2113 assert_eq!(token.balance_of(ALICE).unwrap(), TokenAmount::from_atto(100));
2114 assert_eq!(token.total_supply(), TokenAmount::from_atto(100));
2116 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 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 token.increase_allowance(TREASURY, ALICE, &approval_amount).unwrap();
2140 token.burn_from(ALICE, TREASURY, &burn_amount).unwrap();
2142
2143 assert_eq!(token.total_supply(), TokenAmount::from_atto(400_000));
2145 assert_eq!(token.balance_of(TREASURY).unwrap(), TokenAmount::from_atto(400_000));
2147 assert_eq!(token.allowance(TREASURY, ALICE).unwrap(), TokenAmount::zero());
2149
2150 token.burn_from(ALICE, TREASURY, &burn_amount).unwrap_err();
2153
2154 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 let err = token.burn_from(ALICE, TREASURY, &burn_amount).unwrap_err();
2161
2162 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 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 let secp_address = &secp_address();
2197 let secp_id = &token.runtime.initialize_account(secp_address).unwrap();
2198
2199 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 token.increase_allowance(TREASURY, secp_address, &approval_amount).unwrap();
2208 token.burn_from(secp_address, TREASURY, &burn_amount).unwrap();
2210
2211 assert_eq!(token.total_supply(), TokenAmount::from_atto(400_000));
2213 assert_eq!(token.balance_of(TREASURY).unwrap(), TokenAmount::from_atto(400_000));
2215 assert_eq!(token.allowance(TREASURY, secp_address).unwrap(), TokenAmount::zero());
2217
2218 let err = token.burn_from(secp_address, TREASURY, &burn_amount).unwrap_err();
2220 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 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 let res = token.burn_from(secp_address, TREASURY, &TokenAmount::zero());
2242
2243 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 let secp_address = &secp_address();
2262
2263 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 let err = token.burn_from(secp_address, TREASURY, &burn_amount).unwrap_err();
2272 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 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 let err = token.burn_from(secp_address, TREASURY, &TokenAmount::zero()).unwrap_err();
2294 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 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 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 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 token.increase_allowance(ALICE, CAROL, &TokenAmount::from_atto(40)).unwrap();
2340 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 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 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 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 token.increase_allowance(ALICE, BOB, &TokenAmount::from_atto(100)).unwrap();
2385
2386 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 token.burn_from(BOB, ALICE, &TokenAmount::from_atto(51)).unwrap_err();
2401
2402 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 let mut token =
2416 Token::<FakeSyscalls, MemoryBlockstore>::wrap(&helper, 100, &mut token_state);
2417
2418 assert_eq!(token.granularity(), 100);
2419
2420 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 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 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 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 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 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 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 let mut token = new_token(runtime, state);
2608 if !allowance.is_zero() && from != operator {
2610 token.increase_allowance(from, operator, allowance).unwrap();
2611 }
2612 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 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 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 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 assert_behaviour(BOB, &secp_address(), 0, 0, 0, "ALLOWANCE_ERR");
2739 assert_behaviour(BOB, &secp_address(), 0, 0, 1, "ALLOWANCE_ERR");
2740 assert_behaviour(&bls_address(), &secp_address(), 0, 0, 0, "ALLOWANCE_ERR");
2745 assert_behaviour(&bls_address(), &secp_address(), 0, 0, 1, "ALLOWANCE_ERR");
2746 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 assert_behaviour(ALICE, &actor_address(), 0, 0, 0, "ALLOWANCE_ERR");
2758 assert_behaviour(ALICE, &actor_address(), 0, 0, 1, "ALLOWANCE_ERR");
2759 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 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 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 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 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 token.increase_allowance(ALICE, CAROL, &TokenAmount::from_atto(100)).unwrap();
2806 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 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 assert_eq!(
2829 balance_map.get(&BOB.id().unwrap()).unwrap().clone(),
2830 TokenAmount::from_atto(60)
2831 );
2832 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 }