ab_contract_playground/
lib.rs

1#![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 ab_io_type::variable_bytes::VariableBytes;
12use core::cmp::Ordering;
13
14#[derive(Debug, Default, Copy, Clone, PartialOrd, PartialEq, TrivialType)]
15#[repr(u8)]
16pub enum LastAction {
17    #[default]
18    None,
19    Mint,
20    Transfer,
21}
22
23#[derive(Debug, Default, Copy, Clone, TrivialType)]
24#[repr(C)]
25pub struct Slot {
26    pub balance: Balance,
27}
28
29#[derive(Debug, Copy, Clone, TrivialType)]
30#[repr(C)]
31pub struct Playground {
32    pub total_supply: Balance,
33    pub owner: Address,
34}
35
36#[contract]
37impl Fungible for Playground {
38    #[update]
39    fn transfer(
40        #[env] env: &mut Env<'_>,
41        #[input] from: &Address,
42        #[input] to: &Address,
43        #[input] amount: &Balance,
44    ) -> Result<(), ContractError> {
45        if !(env.context() == from || env.caller() == from || env.caller() == env.own_address()) {
46            return Err(ContractError::Forbidden);
47        }
48
49        env.playground_transfer(MethodContext::Replace, env.own_address(), from, to, amount)
50    }
51
52    #[view]
53    fn balance(#[env] env: &Env<'_>, #[input] address: &Address) -> Result<Balance, ContractError> {
54        env.playground_balance(env.own_address(), address)
55    }
56}
57
58#[contract]
59impl Playground {
60    #[init]
61    pub fn new(
62        #[slot] (owner_addr, owner): (&Address, &mut MaybeData<Slot>),
63        #[input] total_supply: &Balance,
64    ) -> Self {
65        owner.replace(Slot {
66            balance: *total_supply,
67        });
68        Self {
69            total_supply: *total_supply,
70            owner: *owner_addr,
71        }
72    }
73
74    #[init]
75    pub fn new_result(
76        #[slot] (owner_addr, owner): (&Address, &mut MaybeData<Slot>),
77        #[input] total_supply: &Balance,
78        #[output] result: &mut MaybeData<Self>,
79    ) {
80        owner.replace(Slot {
81            balance: *total_supply,
82        });
83        result.replace(Self {
84            total_supply: *total_supply,
85            owner: *owner_addr,
86        });
87    }
88
89    #[update]
90    pub fn mint(
91        &mut self,
92        #[env] env: &mut Env<'_>,
93        #[tmp] last_action: &mut MaybeData<LastAction>,
94        #[slot] to: &mut MaybeData<Slot>,
95        #[input] &value: &Balance,
96    ) -> Result<(), ContractError> {
97        if env.context() != self.owner && env.caller() != self.owner {
98            return Err(ContractError::Forbidden);
99        }
100
101        if Balance::MAX - value > self.total_supply {
102            return Err(ContractError::BadInput);
103        }
104
105        self.total_supply += value;
106        to.get_mut_or_default().balance += value;
107
108        last_action.replace(LastAction::Mint);
109
110        Ok(())
111    }
112
113    #[view]
114    pub fn balance(#[slot] target: &MaybeData<Slot>) -> Balance {
115        target
116            .get()
117            .map_or_else(Balance::default, |slot| slot.balance)
118    }
119
120    #[view]
121    pub fn balance2(#[slot] target: &MaybeData<Slot>, #[output] balance: &mut MaybeData<Balance>) {
122        balance.replace(
123            target
124                .get()
125                .map_or_else(Balance::default, |slot| slot.balance),
126        );
127    }
128
129    #[view]
130    pub fn balance3(#[slot] target: &MaybeData<Slot>, #[output] result: &mut MaybeData<Balance>) {
131        result.replace(
132            target
133                .get()
134                .map_or_else(Balance::default, |slot| slot.balance),
135        );
136    }
137
138    #[view]
139    pub fn var_bytes(#[output] _out: &mut VariableBytes<1024>) {
140        // TODO
141    }
142
143    #[update]
144    pub fn transfer(
145        #[env] env: &mut Env<'_>,
146        #[tmp] last_action: &mut MaybeData<LastAction>,
147        #[slot] (from_address, from): (&Address, &mut MaybeData<Slot>),
148        #[slot] to: &mut MaybeData<Slot>,
149        #[input] &amount: &Balance,
150    ) -> Result<(), ContractError> {
151        if !(env.context() == from_address
152            || env.caller() == from_address
153            || env.caller() == env.own_address())
154        {
155            return Err(ContractError::Forbidden);
156        }
157
158        {
159            let Some(contents) = from.get_mut() else {
160                return Err(ContractError::BadInput);
161            };
162
163            match contents.balance.cmp(&amount) {
164                Ordering::Less => {
165                    return Err(ContractError::BadInput);
166                }
167                Ordering::Equal => {
168                    // All balance is transferred out, remove slot contents
169                    from.remove();
170                }
171                Ordering::Greater => {
172                    contents.balance -= amount;
173                }
174            }
175        }
176
177        to.get_mut_or_default().balance += amount;
178
179        last_action.replace(LastAction::Transfer);
180
181        Ok(())
182    }
183
184    #[update]
185    pub fn last_action(#[tmp] maybe_last_action: &MaybeData<LastAction>) -> LastAction {
186        maybe_last_action.get().copied().unwrap_or_default()
187    }
188}