ab_example_contract_ft/
lib.rs

1#![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
79        match to.get_mut() {
80            Some(contents) => {
81                contents.balance += value;
82            }
83            None => {
84                to.replace(Slot { balance: value });
85            }
86        }
87
88        Ok(())
89    }
90
91    #[view]
92    pub fn balance(#[slot] target: &MaybeData<Slot>) -> Balance {
93        target
94            .get()
95            .map_or_else(Balance::default, |slot| slot.balance)
96    }
97
98    #[update]
99    pub fn transfer(
100        #[env] env: &mut Env<'_>,
101        #[slot] (from_address, from): (&Address, &mut MaybeData<Slot>),
102        #[slot] to: &mut MaybeData<Slot>,
103        #[input] &amount: &Balance,
104    ) -> Result<(), ContractError> {
105        if !(env.context() == from_address
106            || env.caller() == from_address
107            || env.caller() == env.own_address())
108        {
109            return Err(ContractError::Forbidden);
110        }
111
112        {
113            let Some(contents) = from.get_mut() else {
114                return Err(ContractError::BadInput);
115            };
116
117            match contents.balance.cmp(&amount) {
118                Ordering::Less => {
119                    return Err(ContractError::BadInput);
120                }
121                Ordering::Equal => {
122                    // All balance is transferred out, remove slot contents
123                    from.remove();
124                }
125                Ordering::Greater => {
126                    contents.balance -= amount;
127                }
128            }
129        }
130
131        match to.get_mut() {
132            Some(contents) => {
133                contents.balance += amount;
134            }
135            None => {
136                to.replace(Slot { balance: amount });
137            }
138        }
139
140        Ok(())
141    }
142}