ab_system_contract_native_token/
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 core::cmp::Ordering;
12
13#[derive(Debug, Default, Copy, Clone, TrivialType)]
14#[repr(C)]
15pub struct Slot {
16 pub balance: Balance,
17}
18
19#[derive(Debug, Copy, Clone, TrivialType)]
20#[repr(C)]
21pub struct NativeToken {}
22
23#[contract]
24impl Fungible for NativeToken {
25 #[update]
26 fn transfer(
27 #[env] env: &mut Env<'_>,
28 #[input] from: &Address,
29 #[input] to: &Address,
30 #[input] amount: &Balance,
31 ) -> Result<(), ContractError> {
32 if !(env.context() == from
33 || env.caller() == from
34 || env.caller() == env.own_address()
35 || env.caller() == Address::NULL)
36 {
37 return Err(ContractError::Forbidden);
38 }
39
40 env.native_token_transfer(MethodContext::Replace, env.own_address(), from, to, amount)
41 }
42
43 #[view]
44 fn balance(#[env] env: &Env<'_>, #[input] address: &Address) -> Result<Balance, ContractError> {
45 env.native_token_balance(env.own_address(), address)
46 }
47}
48
49#[contract]
50impl NativeToken {
51 #[update]
55 pub fn initialize(
56 #[env] env: &mut Env<'_>,
57 #[slot] (own_address, own_balance): (&Address, &mut MaybeData<Slot>),
58 #[input] &max_issuance: &Balance,
59 ) -> Result<Self, ContractError> {
60 if env.caller() != Address::NULL {
62 return Err(ContractError::Forbidden);
63 }
64
65 if own_address != env.own_address() {
66 return Err(ContractError::BadInput);
67 }
68
69 if own_balance.get().is_some() {
70 return Err(ContractError::Conflict);
71 }
72
73 own_balance.replace(Slot {
74 balance: max_issuance,
75 });
76
77 Ok(Self {})
78 }
79
80 #[view]
81 pub fn balance(#[slot] target: &MaybeData<Slot>) -> Balance {
82 target
83 .get()
84 .map_or_else(Balance::default, |slot| slot.balance)
85 }
86
87 #[update]
88 pub fn transfer(
89 #[env] env: &mut Env<'_>,
90 #[slot] (from_address, from): (&Address, &mut MaybeData<Slot>),
91 #[slot] to: &mut MaybeData<Slot>,
92 #[input] &amount: &Balance,
93 ) -> Result<(), ContractError> {
94 if !(env.context() == from_address
95 || env.caller() == from_address
96 || env.caller() == env.own_address()
97 || env.caller() == Address::NULL)
98 {
99 return Err(ContractError::Forbidden);
100 }
101
102 {
103 let Some(contents) = from.get_mut() else {
104 return Err(ContractError::BadInput);
105 };
106
107 match contents.balance.cmp(&amount) {
108 Ordering::Less => {
109 return Err(ContractError::BadInput);
110 }
111 Ordering::Equal => {
112 from.remove();
114 }
115 Ordering::Greater => {
116 contents.balance -= amount;
117 }
118 }
119 }
120
121 to.get_mut_or_default().balance += amount;
122
123 Ok(())
124 }
125}