diff --git a/contracts/examples/digital-cash/scenarios/claim-egld.scen.json b/contracts/examples/digital-cash/scenarios/claim-egld.scen.json index 8945970816..689acf25b7 100644 --- a/contracts/examples/digital-cash/scenarios/claim-egld.scen.json +++ b/contracts/examples/digital-cash/scenarios/claim-egld.scen.json @@ -153,7 +153,7 @@ } }, "str:fee": "10", - "str:collected_fees": "10" + "str:collectedFees": "10" }, "code": "file:../output/digital-cash.wasm" }, diff --git a/contracts/examples/digital-cash/scenarios/claim-esdt.scen.json b/contracts/examples/digital-cash/scenarios/claim-esdt.scen.json index 7ced2430e0..3c2b82f7c5 100644 --- a/contracts/examples/digital-cash/scenarios/claim-esdt.scen.json +++ b/contracts/examples/digital-cash/scenarios/claim-esdt.scen.json @@ -152,7 +152,7 @@ } }, "str:fee": "10", - "str:collected_fees": "10" + "str:collectedFees": "10" }, "code": "file:../output/digital-cash.wasm" }, diff --git a/contracts/examples/digital-cash/scenarios/claim-fees.scen.json b/contracts/examples/digital-cash/scenarios/claim-fees.scen.json index 6256577609..fdbc0dbcbe 100644 --- a/contracts/examples/digital-cash/scenarios/claim-fees.scen.json +++ b/contracts/examples/digital-cash/scenarios/claim-fees.scen.json @@ -11,7 +11,7 @@ "tx": { "from": "address:acc2", "to": "sc:the_digital_cash_contract", - "function": "claim_fees", + "function": "claimFees", "gasLimit": "500,000,000", "gasPrice": "0" }, @@ -40,7 +40,7 @@ "tx": { "from": "address:digital_cash_owner_address", "to": "sc:the_digital_cash_contract", - "function": "claim_fees", + "function": "claimFees", "gasLimit": "500,000,000", "gasPrice": "0" }, diff --git a/contracts/examples/digital-cash/scenarios/claim-multi-esdt.scen.json b/contracts/examples/digital-cash/scenarios/claim-multi-esdt.scen.json index d2701252eb..1b3657ed3e 100644 --- a/contracts/examples/digital-cash/scenarios/claim-multi-esdt.scen.json +++ b/contracts/examples/digital-cash/scenarios/claim-multi-esdt.scen.json @@ -150,7 +150,7 @@ } }, "str:fee": "10", - "str:collected_fees": "30" + "str:collectedFees": "30" }, "code": "file:../output/digital-cash.wasm" }, diff --git a/contracts/examples/digital-cash/scenarios/forward.scen.json b/contracts/examples/digital-cash/scenarios/forward.scen.json index faed1b6a0d..20f7fa3307 100644 --- a/contracts/examples/digital-cash/scenarios/forward.scen.json +++ b/contracts/examples/digital-cash/scenarios/forward.scen.json @@ -36,7 +36,7 @@ "from": "address:acc2", "to": "sc:the_digital_cash_contract", "egldValue": "1,000", - "function": "deposit_fees", + "function": "depositFees", "arguments": [ "0xa40e72cdac3580e7203a4c2565c932f7691c35e624bcfd82718d7f559c88f440" ], @@ -82,7 +82,7 @@ "from": "address:acc2", "to": "sc:the_digital_cash_contract", "egldValue": "1,000", - "function": "deposit_fees", + "function": "depositFees", "arguments": [ "0x8dc17613990e9b7476401a36d112d1a4d31190dec21e7e9a3c933872a27613ee" ], @@ -168,7 +168,7 @@ } }, "str:fee": "10", - "str:collected_fees": "40" + "str:collectedFees": "40" }, "code": "file:../output/digital-cash.wasm" }, diff --git a/contracts/examples/digital-cash/scenarios/fund-egld-and-esdt.scen.json b/contracts/examples/digital-cash/scenarios/fund-egld-and-esdt.scen.json index 77f90d130d..9c0947c21d 100644 --- a/contracts/examples/digital-cash/scenarios/fund-egld-and-esdt.scen.json +++ b/contracts/examples/digital-cash/scenarios/fund-egld-and-esdt.scen.json @@ -36,7 +36,7 @@ "from": "address:acc1", "to": "sc:the_digital_cash_contract", "egldValue": "1,000", - "function": "deposit_fees", + "function": "depositFees", "arguments": [ "0xdb474a3a065d3f0c0a62ae680ef6435e48eb482899d2ae30ff7a3a4b0ef19c60" ], @@ -135,7 +135,7 @@ "from": "address:acc2", "to": "sc:the_digital_cash_contract", "egldValue": "1,000", - "function": "deposit_fees", + "function": "depositFees", "arguments": [ "0x487bd4010b50c24a02018345fe5171edf4182e6294325382c75ef4c4409f01bd" ], @@ -288,7 +288,7 @@ "from": "address:acc3", "to": "sc:the_digital_cash_contract", "egldValue": "1,000", - "function": "deposit_fees", + "function": "depositFees", "arguments": [ "0x885532043a061e0c779e4064b85193f72cffd22c5bcc208c209128e60f21bf0d" ], diff --git a/contracts/examples/digital-cash/src/deposit_info.rs b/contracts/examples/digital-cash/src/deposit_info.rs index db35242441..b156275aa7 100644 --- a/contracts/examples/digital-cash/src/deposit_info.rs +++ b/contracts/examples/digital-cash/src/deposit_info.rs @@ -1,8 +1,4 @@ -use multiversx_sc::{ - api::ManagedTypeApi, - types::{BigUint, EsdtTokenPayment, ManagedAddress, ManagedVec}, -}; - +multiversx_sc::imports!(); multiversx_sc::derive_imports!(); #[derive(NestedEncode, NestedDecode, TopEncode, TopDecode, TypeAbi)] @@ -20,7 +16,12 @@ where M: ManagedTypeApi, { pub fn get_num_tokens(&self) -> usize { - (self.egld_funds != BigUint::zero()) as usize + self.esdt_funds.len() + let mut amount = self.esdt_funds.len(); + if self.egld_funds > 0 { + amount += 1; + } + + amount } } diff --git a/contracts/examples/digital-cash/src/digital_cash.rs b/contracts/examples/digital-cash/src/digital_cash.rs index bfcd65a524..44e16eb6b4 100644 --- a/contracts/examples/digital-cash/src/digital_cash.rs +++ b/contracts/examples/digital-cash/src/digital_cash.rs @@ -11,6 +11,11 @@ use deposit_info::{DepositInfo, Fee}; pub const SECONDS_PER_ROUND: u64 = 6; pub use multiversx_sc::api::{ED25519_KEY_BYTE_LEN, ED25519_SIGNATURE_BYTE_LEN}; +static NON_EXISTENT_KEY_ERR_MSG: &[u8] = b"non-existent key"; +static FEES_NOT_COVERED_ERR_MSG: &[u8] = b"fees not covered"; +static CANNOT_DEPOSIT_FUNDS_ERR_MSG: &[u8] = + b"cannot deposit funds without covering the fee cost first"; + #[multiversx_sc::contract] pub trait DigitalCash { #[init] @@ -18,30 +23,28 @@ pub trait DigitalCash { self.fee().set(fee); } - //endpoints + // endpoints #[endpoint] #[payable("*")] fn fund(&self, address: ManagedAddress, valability: u64) { - require!(!self.deposit(&address).is_empty(), "fees not covered"); + let deposit_mapper = self.deposit(&address); + require!(!deposit_mapper.is_empty(), FEES_NOT_COVERED_ERR_MSG); - let esdt_payment = self.call_value().all_esdt_transfers().clone_value(); let egld_payment = self.call_value().egld_value().clone_value(); - - let num_tokens = (egld_payment != BigUint::zero()) as usize + esdt_payment.len(); - + let esdt_payment = self.call_value().all_esdt_transfers().clone_value(); + let num_tokens = self.get_num_token_transfers(&egld_payment, &esdt_payment); require!(num_tokens > 0, "amount must be greater than 0"); let fee = self.fee().get(); - - self.deposit(&address).update(|deposit| { + deposit_mapper.update(|deposit| { require!( - deposit.egld_funds == BigUint::zero() && deposit.esdt_funds.is_empty(), + deposit.egld_funds == 0 && deposit.esdt_funds.is_empty(), "key already used" ); require!( fee * num_tokens as u64 <= deposit.fees.value, - "cannot deposit funds without covering the fee cost first" + CANNOT_DEPOSIT_FUNDS_ERR_MSG ); deposit.fees.num_token_to_transfer += num_tokens; @@ -54,19 +57,18 @@ pub trait DigitalCash { #[endpoint] fn withdraw(&self, address: ManagedAddress) { - require!(!self.deposit(&address).is_empty(), "non-existent key"); + let deposit_mapper = self.deposit(&address); + require!(!deposit_mapper.is_empty(), NON_EXISTENT_KEY_ERR_MSG); let block_round = self.blockchain().get_block_round(); - - let deposit = self.deposit(&address).get(); - + let deposit = deposit_mapper.take(); require!( deposit.expiration_round < block_round, "withdrawal has not been available yet" ); let egld_funds = deposit.egld_funds + deposit.fees.value; - if egld_funds != BigUint::zero() { + if egld_funds > 0 { self.send() .direct_egld(&deposit.depositor_address, &egld_funds); } @@ -75,8 +77,6 @@ pub trait DigitalCash { self.send() .direct_multi(&deposit.depositor_address, &deposit.esdt_funds); } - - self.deposit(&address).clear(); } #[endpoint] @@ -85,90 +85,74 @@ pub trait DigitalCash { address: ManagedAddress, signature: ManagedByteArray, ) { - require!(!self.deposit(&address).is_empty(), "non-existent key"); + let deposit_mapper = self.deposit(&address); + require!(!deposit_mapper.is_empty(), NON_EXISTENT_KEY_ERR_MSG); let caller_address = self.blockchain().get_caller(); self.require_signature(&address, &caller_address, signature); let block_round = self.blockchain().get_block_round(); - let fee = self.fee().get(); + let mut deposit = deposit_mapper.take(); + require!(deposit.expiration_round >= block_round, "deposit expired"); - self.deposit(&address).update(|deposit| { - require!(deposit.expiration_round >= block_round, "deposit expired"); - let num_tokens_transfered = &deposit.get_num_tokens(); - let fee_cost = fee * *num_tokens_transfered as u64; - - deposit.fees.num_token_to_transfer -= num_tokens_transfered; - deposit.fees.value -= &fee_cost; + let num_tokens_transfered = deposit.get_num_tokens(); + let fee_cost = fee * num_tokens_transfered as u64; + deposit.fees.value -= &fee_cost; - self.collected_fees() - .update(|collected_fees| *collected_fees += fee_cost); - - if deposit.egld_funds != BigUint::zero() { - self.send() - .direct_egld(&caller_address, &deposit.egld_funds); - } - - if !deposit.esdt_funds.is_empty() { - self.send() - .direct_multi(&caller_address, &deposit.esdt_funds); - } - - if deposit.fees.value > 0 { - self.send() - .direct_egld(&deposit.depositor_address, &deposit.fees.value); - } - }); + self.collected_fees() + .update(|collected_fees| *collected_fees += fee_cost); - self.deposit(&address).clear(); + if deposit.egld_funds > 0 { + self.send() + .direct_egld(&caller_address, &deposit.egld_funds); + } + if !deposit.esdt_funds.is_empty() { + self.send() + .direct_multi(&caller_address, &deposit.esdt_funds); + } + if deposit.fees.value > 0 { + self.send() + .direct_egld(&deposit.depositor_address, &deposit.fees.value); + } } - #[endpoint] + #[endpoint(claimFees)] #[only_owner] fn claim_fees(&self) { - let caller_address = self.blockchain().get_caller(); - let fees = self.collected_fees().get(); + let fees = self.collected_fees().take(); + if fees == 0 { + return; + } + let caller_address = self.blockchain().get_caller(); self.send().direct_egld(&caller_address, &fees); - self.collected_fees().clear(); - } - - fn require_signature( - &self, - address: &ManagedAddress, - caller_address: &ManagedAddress, - signature: ManagedByteArray, - ) { - let addr = address.as_managed_buffer(); - let message = caller_address.as_managed_buffer(); - self.crypto() - .verify_ed25519(addr, message, signature.as_managed_buffer()); } - #[endpoint] + #[endpoint(depositFees)] #[payable("EGLD")] fn deposit_fees(&self, address: ManagedAddress) { let payment = self.call_value().egld_value().clone_value(); let caller_address = self.blockchain().get_caller(); + let deposit_mapper = self.deposit(&address); + if !deposit_mapper.is_empty() { + deposit_mapper.update(|deposit| deposit.fees.value += payment); - if self.deposit(&address).is_empty() { - let new_deposit = DepositInfo { - depositor_address: caller_address, - esdt_funds: ManagedVec::new(), - egld_funds: BigUint::zero(), - valability: 0, - expiration_round: 0, - fees: Fee { - num_token_to_transfer: 0, - value: payment, - }, - }; - self.deposit(&address).set(new_deposit) - } else { - self.deposit(&address) - .update(|deposit| deposit.fees.value += payment); + return; } + + let new_deposit = DepositInfo { + depositor_address: caller_address, + esdt_funds: ManagedVec::new(), + egld_funds: BigUint::zero(), + valability: 0, + expiration_round: 0, + fees: Fee { + num_token_to_transfer: 0, + value: payment, + }, + }; + deposit_mapper.set(new_deposit); } #[endpoint] @@ -178,18 +162,16 @@ pub trait DigitalCash { forward_address: ManagedAddress, signature: ManagedByteArray, ) { - require!( - !self.deposit(&forward_address).is_empty(), - "cannot deposit funds without covering the fee cost first" - ); + let deposit_mapper = self.deposit(&forward_address); + require!(!deposit_mapper.is_empty(), CANNOT_DEPOSIT_FUNDS_ERR_MSG); let caller_address = self.blockchain().get_caller(); let fee = self.fee().get(); self.require_signature(&address, &caller_address, signature); - let mut forwarded_deposit = self.deposit(&address).get(); + let mut forwarded_deposit = self.deposit(&address).take(); let num_tokens = forwarded_deposit.get_num_tokens(); - self.deposit(&forward_address).update(|deposit| { + deposit_mapper.update(|deposit| { require!( deposit.egld_funds == BigUint::zero() && deposit.esdt_funds.is_empty(), "key already used" @@ -207,7 +189,6 @@ pub trait DigitalCash { }); let forward_fee = &fee * num_tokens as u64; - forwarded_deposit.fees.value -= &forward_fee; self.collected_fees() @@ -219,47 +200,67 @@ pub trait DigitalCash { &forwarded_deposit.fees.value, ); } - - self.deposit(&address).clear(); } - //views + // views - #[view(amount)] + #[view(getAmount)] fn get_amount( &self, address: ManagedAddress, token: EgldOrEsdtTokenIdentifier, nonce: u64, ) -> BigUint { - require!(!self.deposit(&address).is_empty(), "non-existent key"); - - let mut amount = BigUint::zero(); + let deposit_mapper = self.deposit(&address); + require!(!deposit_mapper.is_empty(), NON_EXISTENT_KEY_ERR_MSG); - require!(!self.deposit(&address).is_empty(), "non-existent key"); - - let deposit = self.deposit(&address).get(); + let deposit = deposit_mapper.get(); if token.is_egld() { - amount = deposit.egld_funds; - } else { - for esdt in deposit.esdt_funds.into_iter() { - if esdt.token_identifier == token && esdt.token_nonce == nonce { - amount = esdt.amount; - } + return deposit.egld_funds; + } + + for esdt in deposit.esdt_funds.into_iter() { + if esdt.token_identifier == token && esdt.token_nonce == nonce { + return esdt.amount; } } - amount + BigUint::zero() } - //private functions + // private functions fn get_expiration_round(&self, valability: u64) -> u64 { let valability_rounds = valability / SECONDS_PER_ROUND; self.blockchain().get_block_round() + valability_rounds } - //storage + fn get_num_token_transfers( + &self, + egld_value: &BigUint, + esdt_transfers: &ManagedVec, + ) -> usize { + let mut amount = esdt_transfers.len(); + if egld_value > &0 { + amount += 1; + } + + amount + } + + fn require_signature( + &self, + address: &ManagedAddress, + caller_address: &ManagedAddress, + signature: ManagedByteArray, + ) { + let addr = address.as_managed_buffer(); + let message = caller_address.as_managed_buffer(); + self.crypto() + .verify_ed25519(addr, message, signature.as_managed_buffer()); + } + + // storage #[view] #[storage_mapper("deposit")] @@ -268,6 +269,6 @@ pub trait DigitalCash { #[storage_mapper("fee")] fn fee(&self) -> SingleValueMapper; - #[storage_mapper("collected_fees")] + #[storage_mapper("collectedFees")] fn collected_fees(&self) -> SingleValueMapper; } diff --git a/contracts/examples/digital-cash/wasm/src/lib.rs b/contracts/examples/digital-cash/wasm/src/lib.rs index 6d87882a6a..854d3310f9 100644 --- a/contracts/examples/digital-cash/wasm/src/lib.rs +++ b/contracts/examples/digital-cash/wasm/src/lib.rs @@ -10,7 +10,9 @@ // Total number of exported functions: 10 #![no_std] -#![allow(internal_features)] + +// Configuration that works with rustc < 1.73.0. +// TODO: Recommended rustc version: 1.73.0 or newer. #![feature(lang_items)] multiversx_sc_wasm_adapter::allocator!(); @@ -23,10 +25,10 @@ multiversx_sc_wasm_adapter::endpoints! { fund => fund withdraw => withdraw claim => claim - claim_fees => claim_fees - deposit_fees => deposit_fees + claimFees => claim_fees + depositFees => deposit_fees forward => forward - amount => get_amount + getAmount => get_amount deposit => deposit ) }