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