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 balance_output(
122 #[slot] target: &MaybeData<Slot>,
123 #[output] balance: &mut MaybeData<Balance>,
124 #[output] balance_2: &mut Balance,
125 ) {
126 let value = target
127 .get()
128 .map_or_else(Balance::default, |slot| slot.balance);
129 balance.replace(value);
130 *balance_2 = value;
131 }
132
133 #[view]
134 pub fn var_bytes(#[output] _out: &mut VariableBytes<1024>) {
135 }
137
138 #[update]
139 pub fn transfer(
140 #[env] env: &mut Env<'_>,
141 #[tmp] last_action: &mut MaybeData<LastAction>,
142 #[slot] (from_address, from): (&Address, &mut MaybeData<Slot>),
143 #[slot] to: &mut MaybeData<Slot>,
144 #[input] &amount: &Balance,
145 ) -> Result<(), ContractError> {
146 if !(env.context() == from_address
147 || env.caller() == from_address
148 || env.caller() == env.own_address())
149 {
150 return Err(ContractError::Forbidden);
151 }
152
153 {
154 let Some(contents) = from.get_mut() else {
155 return Err(ContractError::BadInput);
156 };
157
158 match contents.balance.cmp(&amount) {
159 Ordering::Less => {
160 return Err(ContractError::BadInput);
161 }
162 Ordering::Equal => {
163 from.remove();
165 }
166 Ordering::Greater => {
167 contents.balance -= amount;
168 }
169 }
170 }
171
172 to.get_mut_or_default().balance += amount;
173
174 last_action.replace(LastAction::Transfer);
175
176 Ok(())
177 }
178
179 #[update]
180 pub fn last_action(#[tmp] maybe_last_action: &MaybeData<LastAction>) -> LastAction {
181 maybe_last_action.get().copied().unwrap_or_default()
182 }
183}