1use crate::cep18_token::Cep18;
3use crate::wrapped_native::events::{Deposit, Withdrawal};
4use odra::casper_types::{U256, U512};
5use odra::uints::{ToU256, ToU512};
6use odra::{prelude::*, ContractRef};
7
8#[odra::event]
10pub struct OnCsprDeposit {
11 pub account: Address,
13 pub value: U512
15}
16
17#[odra::module]
19pub struct CsprDeposit {}
20
21#[odra::module]
23impl CsprDeposit {
24 #[odra(payable)]
26 pub fn deposit(&self) {
27 self.env().emit_event(OnCsprDeposit {
28 account: self.env().caller(),
29 value: self.env().attached_value()
30 });
31 }
32}
33
34#[odra::module(events = [Deposit, Withdrawal])]
36pub struct WrappedNativeToken {
37 token: SubModule<Cep18>
38}
39
40#[odra::module]
42impl WrappedNativeToken {
43 pub fn init(&mut self) {
45 let symbol = "WCSPR".to_string();
46 let name = "Wrapped CSPR".to_string();
47 self.token.init(symbol, name, 9, U256::zero());
48 }
49
50 #[odra(payable)]
52 pub fn deposit(&mut self) {
53 let caller = self.env().caller();
54
55 let amount = self.env().attached_value();
56
57 let amount = amount.to_u256().unwrap_or_revert(self);
58 self.token.raw_mint(&caller, &amount);
59
60 self.env().emit_event(Deposit {
61 account: caller,
62 value: amount
63 });
64 }
65
66 pub fn withdraw(&mut self, amount: &U256) {
68 let caller = self.env().caller();
69
70 self.token.raw_burn(&caller, amount);
71 if caller.is_contract() {
72 CsprDepositContractRef::new(self.env(), caller)
73 .with_tokens(amount.to_u512())
74 .deposit();
75 } else {
76 self.env().transfer_tokens(&caller, &amount.to_u512());
77 }
78
79 self.env().emit_event(Withdrawal {
80 account: caller,
81 value: *amount
82 });
83 }
84
85 pub fn allowance(&self, owner: &Address, spender: &Address) -> U256 {
87 self.token.allowance(owner, spender)
88 }
89
90 pub fn balance_of(&self, address: &Address) -> U256 {
92 self.token.balance_of(address)
93 }
94
95 pub fn total_supply(&self) -> U256 {
97 self.token.total_supply()
98 }
99
100 pub fn decimals(&self) -> u8 {
102 self.token.decimals()
103 }
104
105 pub fn symbol(&self) -> String {
107 self.token.symbol()
108 }
109
110 pub fn name(&self) -> String {
112 self.token.name()
113 }
114
115 pub fn approve(&mut self, spender: &Address, amount: &U256) {
117 self.token.approve(spender, amount)
118 }
119
120 pub fn transfer_from(&mut self, owner: &Address, recipient: &Address, amount: &U256) {
122 self.token.transfer_from(owner, recipient, amount)
123 }
124
125 pub fn transfer(&mut self, recipient: &Address, amount: &U256) {
127 self.token.transfer(recipient, amount)
128 }
129}
130
131pub mod events {
133 use odra::casper_event_standard;
134 use odra::casper_types::U256;
135 use odra::prelude::*;
136
137 #[odra::event]
139 pub struct Deposit {
140 pub account: Address,
142 pub value: U256
144 }
145
146 #[odra::event]
148 pub struct Withdrawal {
149 pub account: Address,
151 pub value: U256
153 }
154}
155
156#[cfg(test)]
157mod tests {
158 use crate::cep18::errors::Error::InsufficientBalance;
159 use crate::cep18::events::{Burn, Mint};
160 use crate::wrapped_native::events::{Deposit, Withdrawal};
161 use crate::wrapped_native::WrappedNativeTokenHostRef;
162 use odra::casper_event_standard::EventInstance;
163 use odra::casper_types::{U256, U512};
164 use odra::host::{Deployer, HostEnv, HostRef, NoArgs};
165 use odra::prelude::*;
166 use odra::uints::{ToU256, ToU512};
167 use odra::VmError::BalanceExceeded;
168
169 use super::WrappedNativeToken;
170
171 fn setup() -> (
172 HostEnv,
173 WrappedNativeTokenHostRef,
174 Address,
175 U512,
176 Address,
177 U512
178 ) {
179 let env = odra_test::env();
180 let token = WrappedNativeToken::deploy(&env, NoArgs);
181 let account_1 = env.get_account(0);
182 let account_1_balance = env.balance_of(&account_1);
183 let account_2 = env.get_account(1);
184 let account_2_balance = env.balance_of(&account_2);
185
186 (
187 env,
188 token,
189 account_1,
190 account_1_balance,
191 account_2,
192 account_2_balance
193 )
194 }
195
196 #[test]
197 fn test_init() {
198 let (_, token, _, _, _, _) = setup();
200
201 assert_eq!(token.total_supply(), U256::zero());
203 assert_eq!(token.name(), "Wrapped CSPR".to_string());
204 assert_eq!(token.symbol(), "WCSPR".to_string());
205 assert_eq!(token.decimals(), 9);
206 }
207
208 #[test]
209 fn test_deposit() {
210 let (env, token, account, account_balance, _, _) = setup();
212
213 let deposit_amount = 1_000u32;
215 token.with_tokens(deposit_amount.into()).deposit();
216
217 assert_eq!(account_balance - deposit_amount, env.balance_of(&account));
219
220 assert_eq!(token.balance_of(&account), deposit_amount.into());
222
223 assert!(env.emitted_event(
225 &token,
226 Mint {
227 recipient: account,
228 amount: deposit_amount.into()
229 }
230 ));
231
232 assert!(env.emitted_event(
233 &token,
234 Deposit {
235 account,
236 value: deposit_amount.into()
237 }
238 ));
239 }
240
241 #[test]
242 fn test_minting() {
243 let (env, token, account_1, _, account_2, _) = setup();
245
246 let deposit_amount = 1_000.into();
248
249 env.set_caller(account_1);
250 token.with_tokens(deposit_amount).deposit();
251
252 env.set_caller(account_2);
253 token.with_tokens(deposit_amount).deposit();
254
255 assert_eq!(
257 token.total_supply(),
258 (deposit_amount + deposit_amount)
259 .to_u256()
260 .expect("Valid U256")
261 );
262 assert!(env.event_names(&token).ends_with(
264 vec![Mint::name(), Deposit::name(), Mint::name(), Deposit::name()].as_slice()
265 ));
266 }
267
268 #[test]
269 fn test_deposit_amount_exceeding_account_balance() {
270 let (_, token, _, balance, _, _) = setup();
272 assert_eq!(
275 token.with_tokens(balance + U512::one()).try_deposit(),
276 Err(OdraError::VmError(BalanceExceeded))
277 );
278 }
279
280 #[test]
281 fn test_withdrawal() {
282 let (env, mut token, account, _, _, _) = setup();
284 let deposit_amount: U512 = 3_000.into();
285 token.with_tokens(deposit_amount).deposit();
286 let account_balance = env.balance_of(&account);
287
288 let withdrawal_amount: U256 = 1_000.into();
290 token.withdraw(&withdrawal_amount);
291
292 assert_eq!(
294 account_balance + withdrawal_amount.to_u512(),
295 env.balance_of(&account)
296 );
297 assert_eq!(
299 token.balance_of(&account),
300 deposit_amount.to_u256().expect("Valid U256") - withdrawal_amount
301 );
302
303 assert!(env.emitted_event(
305 &token,
306 Burn {
307 owner: account,
308 amount: withdrawal_amount
309 }
310 ));
311 assert!(env.emitted_event(
312 &token,
313 Withdrawal {
314 account,
315 value: withdrawal_amount
316 }
317 ));
318 }
319
320 #[test]
321 fn test_withdrawal_amount_exceeding_account_deposit() {
322 let (_, mut token, _, _, _, _) = setup();
324 assert_eq!(
327 token.try_withdraw(&U256::one()),
328 Err(InsufficientBalance.into())
329 );
330 }
331}