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