ab_contract_playground/
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 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 }
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 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}