ab_example_contract_ft/
lib.rs1#![no_std]
2
3use ab_contracts_common::ContractError;
4use ab_contracts_common::env::{Env, MethodContext};
5use ab_contracts_macros::contract;
6use ab_contracts_standards::fungible::Fungible;
7use ab_core_primitives::address::Address;
8use ab_core_primitives::balance::Balance;
9use ab_io_type::maybe_data::MaybeData;
10use ab_io_type::trivial_type::TrivialType;
11use core::cmp::Ordering;
12
13#[derive(Debug, Default, Copy, Clone, TrivialType)]
14#[repr(C)]
15pub struct Slot {
16 pub balance: Balance,
17}
18
19#[derive(Debug, Copy, Clone, TrivialType)]
20#[repr(C)]
21pub struct ExampleFt {
22 pub total_supply: Balance,
23 pub owner: Address,
24}
25
26#[contract]
27impl Fungible for ExampleFt {
28 #[update]
29 fn transfer(
30 #[env] env: &mut Env<'_>,
31 #[input] from: &Address,
32 #[input] to: &Address,
33 #[input] amount: &Balance,
34 ) -> Result<(), ContractError> {
35 if !(env.context() == from || env.caller() == from || env.caller() == env.own_address()) {
36 return Err(ContractError::Forbidden);
37 }
38
39 env.example_ft_transfer(MethodContext::Replace, env.own_address(), from, to, amount)
40 }
41
42 #[view]
43 fn balance(#[env] env: &Env<'_>, #[input] address: &Address) -> Result<Balance, ContractError> {
44 env.example_ft_balance(env.own_address(), address)
45 }
46}
47
48#[contract]
49impl ExampleFt {
50 #[init]
51 pub fn new(
52 #[slot] (owner_addr, owner): (&Address, &mut MaybeData<Slot>),
53 #[input] total_supply: &Balance,
54 ) -> Self {
55 owner.replace(Slot {
56 balance: *total_supply,
57 });
58 Self {
59 total_supply: *total_supply,
60 owner: *owner_addr,
61 }
62 }
63
64 #[update]
65 pub fn mint(
66 &mut self,
67 #[env] env: &mut Env<'_>,
68 #[slot] to: &mut MaybeData<Slot>,
69 #[input] &value: &Balance,
70 ) -> Result<(), ContractError> {
71 if env.context() != self.owner && env.caller() != self.owner {
72 return Err(ContractError::Forbidden);
73 }
74
75 if Balance::MAX - value > self.total_supply {
76 return Err(ContractError::BadInput);
77 }
78
79 self.total_supply += value;
80 to.get_mut_or_default().balance += value;
81
82 Ok(())
83 }
84
85 #[view]
86 pub fn balance(#[slot] target: &MaybeData<Slot>) -> Balance {
87 target
88 .get()
89 .map_or_else(Balance::default, |slot| slot.balance)
90 }
91
92 #[update]
93 pub fn transfer(
94 #[env] env: &mut Env<'_>,
95 #[slot] (from_address, from): (&Address, &mut MaybeData<Slot>),
96 #[slot] to: &mut MaybeData<Slot>,
97 #[input] &amount: &Balance,
98 ) -> Result<(), ContractError> {
99 if !(env.context() == from_address
100 || env.caller() == from_address
101 || env.caller() == env.own_address())
102 {
103 return Err(ContractError::Forbidden);
104 }
105
106 {
107 let Some(contents) = from.get_mut() else {
108 return Err(ContractError::BadInput);
109 };
110
111 match contents.balance.cmp(&amount) {
112 Ordering::Less => {
113 return Err(ContractError::BadInput);
114 }
115 Ordering::Equal => {
116 from.remove();
118 }
119 Ordering::Greater => {
120 contents.balance -= amount;
121 }
122 }
123 }
124
125 to.get_mut_or_default().balance += amount;
126
127 Ok(())
128 }
129}