From e5501b6b26f0823de46387e62dc1678e46b6ba6c Mon Sep 17 00:00:00 2001 From: Alin Cruceat Date: Tue, 19 Sep 2023 07:38:17 +0300 Subject: [PATCH 01/20] allow esdt as fee & config in init --- .../examples/digital-cash/src/digital_cash.rs | 52 ++++++++++++++----- 1 file changed, 40 insertions(+), 12 deletions(-) diff --git a/contracts/examples/digital-cash/src/digital_cash.rs b/contracts/examples/digital-cash/src/digital_cash.rs index aee3e173d3..f3e830cae2 100644 --- a/contracts/examples/digital-cash/src/digital_cash.rs +++ b/contracts/examples/digital-cash/src/digital_cash.rs @@ -19,8 +19,9 @@ static CANNOT_DEPOSIT_FUNDS_ERR_MSG: &[u8] = #[multiversx_sc::contract] pub trait DigitalCash { #[init] - fn init(&self, fee: BigUint) { + fn init(&self, fee: BigUint, coin: TokenIdentifier) { self.fee().set(fee); + self.fee_token().set(coin); } // endpoints @@ -63,6 +64,7 @@ pub trait DigitalCash { #[endpoint] fn withdraw(&self, address: ManagedAddress) { let deposit_mapper = self.deposit(&address); + let accepted_fee_token = self.fee_token().get(); require!(!deposit_mapper.is_empty(), NON_EXISTENT_KEY_ERR_MSG); let block_round = self.blockchain().get_block_round(); @@ -72,15 +74,24 @@ pub trait DigitalCash { "withdrawal has not been available yet" ); - let egld_funds = deposit.egld_funds + deposit.fees.value; + let mut egld_funds = deposit.egld_funds; + let mut esdt_funds = deposit.esdt_funds; + + if accepted_fee_token == EgldOrEsdtTokenIdentifier::egld() { + egld_funds += deposit.fees.value; + } else { + let esdt_fee = EsdtTokenPayment::new(accepted_fee_token, 0, deposit.fees.value); + esdt_funds.push(esdt_fee); + } + if egld_funds > 0 { self.send() .direct_egld(&deposit.depositor_address, &egld_funds); } - if !deposit.esdt_funds.is_empty() { + if !esdt_funds.is_empty() { self.send() - .direct_multi(&deposit.depositor_address, &deposit.esdt_funds); + .direct_multi(&deposit.depositor_address, &esdt_funds); } } @@ -117,8 +128,7 @@ pub trait DigitalCash { .direct_multi(&caller_address, &deposit.esdt_funds); } if deposit.fees.value > 0 { - self.send() - .direct_egld(&deposit.depositor_address, &deposit.fees.value); + self.send_fee_to_address(&deposit.fees.value, &deposit.depositor_address); } } @@ -131,17 +141,22 @@ pub trait DigitalCash { } let caller_address = self.blockchain().get_caller(); - self.send().direct_egld(&caller_address, &fees); + self.send_fee_to_address(&fees, &caller_address); } #[endpoint(depositFees)] #[payable("EGLD")] fn deposit_fees(&self, address: ManagedAddress) { - let payment = self.call_value().egld_value().clone_value(); + let payment = self.call_value().egld_or_single_esdt(); + let accepted_fee_token = self.fee_token().get(); + require!( + payment.token_identifier == accepted_fee_token, + "Invalid fee token provided" + ); 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); + deposit_mapper.update(|deposit| deposit.fees.value += payment.amount); return; } @@ -154,7 +169,7 @@ pub trait DigitalCash { expiration_round: 0, fees: Fee { num_token_to_transfer: 0, - value: payment, + value: payment.amount, }, }; deposit_mapper.set(new_deposit); @@ -200,15 +215,25 @@ pub trait DigitalCash { .update(|collected_fees| *collected_fees += forward_fee); if forwarded_deposit.fees.value > 0 { - self.send().direct_egld( - &forwarded_deposit.depositor_address, + self.send_fee_to_address( &forwarded_deposit.fees.value, + &forwarded_deposit.depositor_address, ); } } // views + fn send_fee_to_address(&self, fee_amount: &BigUint, address: &ManagedAddress) { + let accepted_fee_token = self.fee_token().get(); + if accepted_fee_token == EgldOrEsdtTokenIdentifier::egld() { + self.send().direct_egld(address, fee_amount); + } else { + self.send() + .direct_esdt(address, &accepted_fee_token, 0, fee_amount); + } + } + #[view(getAmount)] fn get_amount( &self, @@ -274,6 +299,9 @@ pub trait DigitalCash { #[storage_mapper("fee")] fn fee(&self) -> SingleValueMapper; + #[storage_mapper("feeToken")] + fn fee_token(&self) -> SingleValueMapper; + #[storage_mapper("collectedFees")] fn collected_fees(&self) -> SingleValueMapper; } From 4b30eb5cbd0b27560853e4b19f145e85d08e88ba Mon Sep 17 00:00:00 2001 From: Alin Cruceat Date: Tue, 19 Sep 2023 08:33:16 +0300 Subject: [PATCH 02/20] update test & fix token fee --- .../digital-cash/scenarios/claim-egld.scen.json | 1 + .../digital-cash/scenarios/claim-esdt.scen.json | 1 + .../digital-cash/scenarios/claim-fees.scen.json | 3 ++- .../scenarios/claim-multi-esdt.scen.json | 1 + .../digital-cash/scenarios/forward.scen.json | 1 + .../scenarios/fund-egld-and-esdt.scen.json | 3 ++- .../digital-cash/scenarios/set-accounts.scen.json | 6 ++++-- .../digital-cash/scenarios/withdraw-egld.scen.json | 5 +++-- .../digital-cash/scenarios/withdraw-esdt.scen.json | 3 ++- .../scenarios/withdraw-multi-esdt.scen.json | 6 ++++-- contracts/examples/digital-cash/src/digital_cash.rs | 12 +++++++----- contracts/examples/digital-cash/wasm/src/lib.rs | 4 +++- 12 files changed, 31 insertions(+), 15 deletions(-) diff --git a/contracts/examples/digital-cash/scenarios/claim-egld.scen.json b/contracts/examples/digital-cash/scenarios/claim-egld.scen.json index 085287a127..9e91e2fa05 100644 --- a/contracts/examples/digital-cash/scenarios/claim-egld.scen.json +++ b/contracts/examples/digital-cash/scenarios/claim-egld.scen.json @@ -153,6 +153,7 @@ } }, "str:fee": "10", + "str:feeToken": "str:EGLD", "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 d3010b1ed1..20b2f4c0f2 100644 --- a/contracts/examples/digital-cash/scenarios/claim-esdt.scen.json +++ b/contracts/examples/digital-cash/scenarios/claim-esdt.scen.json @@ -152,6 +152,7 @@ } }, "str:fee": "10", + "str:feeToken": "str:EGLD", "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 0b9c11b9d5..a176c13ffb 100644 --- a/contracts/examples/digital-cash/scenarios/claim-fees.scen.json +++ b/contracts/examples/digital-cash/scenarios/claim-fees.scen.json @@ -87,7 +87,8 @@ "1-value": "biguint:1,000" } }, - "str:fee": "10" + "str:fee": "10", + "str:feeToken": "str:EGLD" }, "code": "file:../output/digital-cash.wasm" }, 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 85df0810de..444c7753ed 100644 --- a/contracts/examples/digital-cash/scenarios/claim-multi-esdt.scen.json +++ b/contracts/examples/digital-cash/scenarios/claim-multi-esdt.scen.json @@ -150,6 +150,7 @@ } }, "str:fee": "10", + "str:feeToken": "str:EGLD", "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 ad9f41413d..7a2e26f89f 100644 --- a/contracts/examples/digital-cash/scenarios/forward.scen.json +++ b/contracts/examples/digital-cash/scenarios/forward.scen.json @@ -168,6 +168,7 @@ } }, "str:fee": "10", + "str:feeToken": "str:EGLD", "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 7263a472e0..accbeec1b6 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 @@ -414,7 +414,8 @@ "1-value": "biguint:1,000" } }, - "str:fee": "10" + "str:fee": "10", + "str:feeToken": "str:EGLD" }, "code": "file:../output/digital-cash.wasm" }, diff --git a/contracts/examples/digital-cash/scenarios/set-accounts.scen.json b/contracts/examples/digital-cash/scenarios/set-accounts.scen.json index 0c742a688d..f403c32cba 100644 --- a/contracts/examples/digital-cash/scenarios/set-accounts.scen.json +++ b/contracts/examples/digital-cash/scenarios/set-accounts.scen.json @@ -44,7 +44,8 @@ "from": "address:digital_cash_owner_address", "contractCode": "file:../output/digital-cash.wasm", "arguments": [ - "10" + "10", + "str:EGLD" ], "gasLimit": "5,000,000", "gasPrice": "0" @@ -63,7 +64,8 @@ "nonce": "0", "balance": "0", "storage": { - "str:fee": "10" + "str:fee": "10", + "str:feeToken": "str:EGLD" }, "code": "file:../output/digital-cash.wasm" }, diff --git a/contracts/examples/digital-cash/scenarios/withdraw-egld.scen.json b/contracts/examples/digital-cash/scenarios/withdraw-egld.scen.json index e89119fb44..8da19ef2da 100644 --- a/contracts/examples/digital-cash/scenarios/withdraw-egld.scen.json +++ b/contracts/examples/digital-cash/scenarios/withdraw-egld.scen.json @@ -196,7 +196,8 @@ "1-value": "biguint:1,000" } }, - "str:fee": "10" + "str:fee": "10", + "str:feeToken": "str:EGLD" }, "code": "file:../output/digital-cash.wasm" }, @@ -226,7 +227,7 @@ "address:digital_cash_owner_address": { "nonce": "1", "balance": "0", - "storage": {} + "storage": "*" } } } diff --git a/contracts/examples/digital-cash/scenarios/withdraw-esdt.scen.json b/contracts/examples/digital-cash/scenarios/withdraw-esdt.scen.json index 77a8fbe9c0..0a02a2e3eb 100644 --- a/contracts/examples/digital-cash/scenarios/withdraw-esdt.scen.json +++ b/contracts/examples/digital-cash/scenarios/withdraw-esdt.scen.json @@ -73,7 +73,8 @@ "1-value": "biguint:1,000" } }, - "str:fee": "10" + "str:fee": "10", + "str:feeToken": "str:EGLD" }, "code": "file:../output/digital-cash.wasm" }, diff --git a/contracts/examples/digital-cash/scenarios/withdraw-multi-esdt.scen.json b/contracts/examples/digital-cash/scenarios/withdraw-multi-esdt.scen.json index 7f1a96e2ea..60569d329e 100644 --- a/contracts/examples/digital-cash/scenarios/withdraw-multi-esdt.scen.json +++ b/contracts/examples/digital-cash/scenarios/withdraw-multi-esdt.scen.json @@ -73,7 +73,8 @@ "1-value": "biguint:1,000" } }, - "str:fee": "10" + "str:fee": "10", + "str:feeToken": "str:EGLD" }, "code": "file:../output/digital-cash.wasm" }, @@ -193,7 +194,8 @@ "1-value": "biguint:1,000" } }, - "str:fee": "10" + "str:fee": "10", + "str:feeToken": "str:EGLD" }, "code": "file:../output/digital-cash.wasm" }, diff --git a/contracts/examples/digital-cash/src/digital_cash.rs b/contracts/examples/digital-cash/src/digital_cash.rs index f3e830cae2..ef713aa923 100644 --- a/contracts/examples/digital-cash/src/digital_cash.rs +++ b/contracts/examples/digital-cash/src/digital_cash.rs @@ -19,9 +19,9 @@ static CANNOT_DEPOSIT_FUNDS_ERR_MSG: &[u8] = #[multiversx_sc::contract] pub trait DigitalCash { #[init] - fn init(&self, fee: BigUint, coin: TokenIdentifier) { + fn init(&self, fee: BigUint, token: EgldOrEsdtTokenIdentifier) { self.fee().set(fee); - self.fee_token().set(coin); + self.fee_token().set(token); } // endpoints @@ -80,7 +80,8 @@ pub trait DigitalCash { if accepted_fee_token == EgldOrEsdtTokenIdentifier::egld() { egld_funds += deposit.fees.value; } else { - let esdt_fee = EsdtTokenPayment::new(accepted_fee_token, 0, deposit.fees.value); + let esdt_fee_token = accepted_fee_token.unwrap_esdt(); + let esdt_fee = EsdtTokenPayment::new(esdt_fee_token, 0, deposit.fees.value); esdt_funds.push(esdt_fee); } @@ -229,8 +230,9 @@ pub trait DigitalCash { if accepted_fee_token == EgldOrEsdtTokenIdentifier::egld() { self.send().direct_egld(address, fee_amount); } else { + let esdt_fee_token = accepted_fee_token.unwrap_esdt(); self.send() - .direct_esdt(address, &accepted_fee_token, 0, fee_amount); + .direct_esdt(address, &esdt_fee_token, 0, fee_amount); } } @@ -300,7 +302,7 @@ pub trait DigitalCash { fn fee(&self) -> SingleValueMapper; #[storage_mapper("feeToken")] - fn fee_token(&self) -> SingleValueMapper; + fn fee_token(&self) -> SingleValueMapper; #[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 64b373a95d..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!(); From cb5a8737ee1463c0bba508e74306775063076bfb Mon Sep 17 00:00:00 2001 From: Alin Cruceat Date: Tue, 19 Sep 2023 08:51:50 +0300 Subject: [PATCH 03/20] separate fee and fund endpoints --- .../scenarios/withdraw-egld.scen.json | 2 +- .../examples/digital-cash/src/constants.rs | 5 + .../examples/digital-cash/src/digital_cash.rs | 114 +----------------- .../digital-cash/src/pay_fee_and_fund.rs | 95 +++++++++++++++ .../examples/digital-cash/src/storage.rs | 20 +++ 5 files changed, 126 insertions(+), 110 deletions(-) create mode 100644 contracts/examples/digital-cash/src/constants.rs create mode 100644 contracts/examples/digital-cash/src/pay_fee_and_fund.rs create mode 100644 contracts/examples/digital-cash/src/storage.rs diff --git a/contracts/examples/digital-cash/scenarios/withdraw-egld.scen.json b/contracts/examples/digital-cash/scenarios/withdraw-egld.scen.json index 8da19ef2da..84b6c9a123 100644 --- a/contracts/examples/digital-cash/scenarios/withdraw-egld.scen.json +++ b/contracts/examples/digital-cash/scenarios/withdraw-egld.scen.json @@ -227,7 +227,7 @@ "address:digital_cash_owner_address": { "nonce": "1", "balance": "0", - "storage": "*" + "storage": {} } } } diff --git a/contracts/examples/digital-cash/src/constants.rs b/contracts/examples/digital-cash/src/constants.rs new file mode 100644 index 0000000000..df563263de --- /dev/null +++ b/contracts/examples/digital-cash/src/constants.rs @@ -0,0 +1,5 @@ +pub static NON_EXISTENT_KEY_ERR_MSG: &[u8] = b"non-existent key"; +pub static FEES_NOT_COVERED_ERR_MSG: &[u8] = b"fees not covered"; +pub static CANNOT_DEPOSIT_FUNDS_ERR_MSG: &[u8] = + b"cannot deposit funds without covering the fee cost first"; +pub const SECONDS_PER_ROUND: u64 = 6; diff --git a/contracts/examples/digital-cash/src/digital_cash.rs b/contracts/examples/digital-cash/src/digital_cash.rs index ef713aa923..7bf644ad1e 100644 --- a/contracts/examples/digital-cash/src/digital_cash.rs +++ b/contracts/examples/digital-cash/src/digital_cash.rs @@ -4,63 +4,23 @@ multiversx_sc::imports!(); multiversx_sc::derive_imports!(); +mod constants; mod deposit_info; +mod pay_fee_and_fund; +mod storage; -use deposit_info::{DepositInfo, Fee}; +use constants::*; -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 { +pub trait DigitalCash: pay_fee_and_fund::PayFeeAndFund + storage::StorageModule { #[init] fn init(&self, fee: BigUint, token: EgldOrEsdtTokenIdentifier) { self.fee().set(fee); self.fee_token().set(token); } - // endpoints - - #[endpoint] - #[payable("*")] - fn fund(&self, address: ManagedAddress, valability: u64) { - let deposit_mapper = self.deposit(&address); - require!(!deposit_mapper.is_empty(), FEES_NOT_COVERED_ERR_MSG); - let depositor = deposit_mapper.get().depositor_address; - require!( - self.blockchain().get_caller() == depositor, - "invalid depositor" - ); - - let egld_payment = self.call_value().egld_value().clone_value(); - 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(); - deposit_mapper.update(|deposit| { - require!( - deposit.egld_funds == 0 && deposit.esdt_funds.is_empty(), - "key already used" - ); - require!( - fee * num_tokens as u64 <= deposit.fees.value, - CANNOT_DEPOSIT_FUNDS_ERR_MSG - ); - - deposit.fees.num_token_to_transfer += num_tokens; - deposit.valability = valability; - deposit.expiration_round = self.get_expiration_round(valability); - deposit.esdt_funds = esdt_payment; - deposit.egld_funds = egld_payment; - }); - } - #[endpoint] fn withdraw(&self, address: ManagedAddress) { let deposit_mapper = self.deposit(&address); @@ -145,37 +105,6 @@ pub trait DigitalCash { self.send_fee_to_address(&fees, &caller_address); } - #[endpoint(depositFees)] - #[payable("EGLD")] - fn deposit_fees(&self, address: ManagedAddress) { - let payment = self.call_value().egld_or_single_esdt(); - let accepted_fee_token = self.fee_token().get(); - require!( - payment.token_identifier == accepted_fee_token, - "Invalid fee token provided" - ); - 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.amount); - - 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.amount, - }, - }; - deposit_mapper.set(new_deposit); - } - #[endpoint] fn forward( &self, @@ -260,26 +189,6 @@ pub trait DigitalCash { BigUint::zero() } - // private functions - - fn get_expiration_round(&self, valability: u64) -> u64 { - let valability_rounds = valability / SECONDS_PER_ROUND; - self.blockchain().get_block_round() + valability_rounds - } - - 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, @@ -293,17 +202,4 @@ pub trait DigitalCash { } // storage - - #[view] - #[storage_mapper("deposit")] - fn deposit(&self, donor: &ManagedAddress) -> SingleValueMapper>; - - #[storage_mapper("fee")] - fn fee(&self) -> SingleValueMapper; - - #[storage_mapper("feeToken")] - fn fee_token(&self) -> SingleValueMapper; - - #[storage_mapper("collectedFees")] - fn collected_fees(&self) -> SingleValueMapper; } diff --git a/contracts/examples/digital-cash/src/pay_fee_and_fund.rs b/contracts/examples/digital-cash/src/pay_fee_and_fund.rs new file mode 100644 index 0000000000..3e760a6596 --- /dev/null +++ b/contracts/examples/digital-cash/src/pay_fee_and_fund.rs @@ -0,0 +1,95 @@ +multiversx_sc::imports!(); +multiversx_sc::derive_imports!(); + +use crate::{ + constants::*, + deposit_info::{DepositInfo, Fee}, + storage, +}; + +#[multiversx_sc::module] +pub trait PayFeeAndFund: storage::StorageModule { + #[endpoint] + #[payable("*")] + fn fund(&self, address: ManagedAddress, valability: u64) { + let deposit_mapper = self.deposit(&address); + require!(!deposit_mapper.is_empty(), FEES_NOT_COVERED_ERR_MSG); + let depositor = deposit_mapper.get().depositor_address; + require!( + self.blockchain().get_caller() == depositor, + "invalid depositor" + ); + + let egld_payment = self.call_value().egld_value().clone_value(); + 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(); + deposit_mapper.update(|deposit| { + require!( + deposit.egld_funds == 0 && deposit.esdt_funds.is_empty(), + "key already used" + ); + require!( + fee * num_tokens as u64 <= deposit.fees.value, + CANNOT_DEPOSIT_FUNDS_ERR_MSG + ); + + deposit.fees.num_token_to_transfer += num_tokens; + deposit.valability = valability; + deposit.expiration_round = self.get_expiration_round(valability); + deposit.esdt_funds = esdt_payment; + deposit.egld_funds = egld_payment; + }); + } + + #[endpoint(depositFees)] + #[payable("EGLD")] + fn deposit_fees(&self, address: ManagedAddress) { + let payment = self.call_value().egld_or_single_esdt(); + let accepted_fee_token = self.fee_token().get(); + require!( + payment.token_identifier == accepted_fee_token, + "Invalid fee token provided" + ); + 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.amount); + + 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.amount, + }, + }; + deposit_mapper.set(new_deposit); + } + + 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 get_expiration_round(&self, valability: u64) -> u64 { + let valability_rounds = valability / SECONDS_PER_ROUND; + self.blockchain().get_block_round() + valability_rounds + } +} diff --git a/contracts/examples/digital-cash/src/storage.rs b/contracts/examples/digital-cash/src/storage.rs new file mode 100644 index 0000000000..497b7bd86d --- /dev/null +++ b/contracts/examples/digital-cash/src/storage.rs @@ -0,0 +1,20 @@ +multiversx_sc::imports!(); +multiversx_sc::derive_imports!(); + +use crate::deposit_info::*; + +#[multiversx_sc::module] +pub trait StorageModule { + #[view] + #[storage_mapper("deposit")] + fn deposit(&self, donor: &ManagedAddress) -> SingleValueMapper>; + + #[storage_mapper("fee")] + fn fee(&self) -> SingleValueMapper; + + #[storage_mapper("feeToken")] + fn fee_token(&self) -> SingleValueMapper; + + #[storage_mapper("collectedFees")] + fn collected_fees(&self) -> SingleValueMapper; +} From 05faf586389ee33cd23896aa2d06e6dfd9ea7661 Mon Sep 17 00:00:00 2001 From: Alin Cruceat Date: Tue, 19 Sep 2023 08:52:50 +0300 Subject: [PATCH 04/20] change static to const --- contracts/examples/digital-cash/src/constants.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/contracts/examples/digital-cash/src/constants.rs b/contracts/examples/digital-cash/src/constants.rs index df563263de..275e6bc702 100644 --- a/contracts/examples/digital-cash/src/constants.rs +++ b/contracts/examples/digital-cash/src/constants.rs @@ -1,5 +1,5 @@ -pub static NON_EXISTENT_KEY_ERR_MSG: &[u8] = b"non-existent key"; -pub static FEES_NOT_COVERED_ERR_MSG: &[u8] = b"fees not covered"; -pub static CANNOT_DEPOSIT_FUNDS_ERR_MSG: &[u8] = +pub const NON_EXISTENT_KEY_ERR_MSG: &[u8] = b"non-existent key"; +pub const FEES_NOT_COVERED_ERR_MSG: &[u8] = b"fees not covered"; +pub const CANNOT_DEPOSIT_FUNDS_ERR_MSG: &[u8] = b"cannot deposit funds without covering the fee cost first"; pub const SECONDS_PER_ROUND: u64 = 6; From d4ceb15df3a6de5191c39d31c9bf8b31d9b09ae9 Mon Sep 17 00:00:00 2001 From: Alin Cruceat Date: Tue, 19 Sep 2023 09:04:10 +0300 Subject: [PATCH 05/20] split contract to modules --- .../examples/digital-cash/src/digital_cash.rs | 158 +----------------- .../examples/digital-cash/src/helpers.rs | 34 ++++ .../digital-cash/src/pay_fee_and_fund.rs | 22 +-- .../digital-cash/src/signature_operations.rs | 140 ++++++++++++++++ 4 files changed, 184 insertions(+), 170 deletions(-) create mode 100644 contracts/examples/digital-cash/src/helpers.rs create mode 100644 contracts/examples/digital-cash/src/signature_operations.rs diff --git a/contracts/examples/digital-cash/src/digital_cash.rs b/contracts/examples/digital-cash/src/digital_cash.rs index 7bf644ad1e..5b00a9a341 100644 --- a/contracts/examples/digital-cash/src/digital_cash.rs +++ b/contracts/examples/digital-cash/src/digital_cash.rs @@ -6,93 +6,25 @@ multiversx_sc::derive_imports!(); mod constants; mod deposit_info; +mod helpers; mod pay_fee_and_fund; +mod signature_operations; mod storage; use constants::*; -pub use multiversx_sc::api::{ED25519_KEY_BYTE_LEN, ED25519_SIGNATURE_BYTE_LEN}; - #[multiversx_sc::contract] -pub trait DigitalCash: pay_fee_and_fund::PayFeeAndFund + storage::StorageModule { +pub trait DigitalCash: + pay_fee_and_fund::PayFeeAndFund + + signature_operations::SignatureOperationsModule + + helpers::HelpersModule + + storage::StorageModule +{ #[init] fn init(&self, fee: BigUint, token: EgldOrEsdtTokenIdentifier) { self.fee().set(fee); self.fee_token().set(token); } - - #[endpoint] - fn withdraw(&self, address: ManagedAddress) { - let deposit_mapper = self.deposit(&address); - let accepted_fee_token = self.fee_token().get(); - require!(!deposit_mapper.is_empty(), NON_EXISTENT_KEY_ERR_MSG); - - let block_round = self.blockchain().get_block_round(); - let deposit = deposit_mapper.take(); - require!( - deposit.expiration_round < block_round, - "withdrawal has not been available yet" - ); - - let mut egld_funds = deposit.egld_funds; - let mut esdt_funds = deposit.esdt_funds; - - if accepted_fee_token == EgldOrEsdtTokenIdentifier::egld() { - egld_funds += deposit.fees.value; - } else { - let esdt_fee_token = accepted_fee_token.unwrap_esdt(); - let esdt_fee = EsdtTokenPayment::new(esdt_fee_token, 0, deposit.fees.value); - esdt_funds.push(esdt_fee); - } - - if egld_funds > 0 { - self.send() - .direct_egld(&deposit.depositor_address, &egld_funds); - } - - if !esdt_funds.is_empty() { - self.send() - .direct_multi(&deposit.depositor_address, &esdt_funds); - } - } - - #[endpoint] - fn claim( - &self, - address: ManagedAddress, - signature: ManagedByteArray, - ) { - 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"); - - 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 > 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_fee_to_address(&deposit.fees.value, &deposit.depositor_address); - } - } - #[endpoint(claimFees)] #[only_owner] fn claim_fees(&self) { @@ -105,66 +37,6 @@ pub trait DigitalCash: pay_fee_and_fund::PayFeeAndFund + storage::StorageModule self.send_fee_to_address(&fees, &caller_address); } - #[endpoint] - fn forward( - &self, - address: ManagedAddress, - forward_address: ManagedAddress, - signature: ManagedByteArray, - ) { - 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).take(); - let num_tokens = forwarded_deposit.get_num_tokens(); - deposit_mapper.update(|deposit| { - require!( - deposit.egld_funds == BigUint::zero() && deposit.esdt_funds.is_empty(), - "key already used" - ); - require!( - &fee * num_tokens as u64 <= deposit.fees.value, - "cannot forward funds without the owner covering the fee cost first" - ); - - deposit.fees.num_token_to_transfer += num_tokens; - deposit.valability = forwarded_deposit.valability; - deposit.expiration_round = self.get_expiration_round(forwarded_deposit.valability); - deposit.esdt_funds = forwarded_deposit.esdt_funds; - deposit.egld_funds = forwarded_deposit.egld_funds; - }); - - let forward_fee = &fee * num_tokens as u64; - forwarded_deposit.fees.value -= &forward_fee; - - self.collected_fees() - .update(|collected_fees| *collected_fees += forward_fee); - - if forwarded_deposit.fees.value > 0 { - self.send_fee_to_address( - &forwarded_deposit.fees.value, - &forwarded_deposit.depositor_address, - ); - } - } - - // views - - fn send_fee_to_address(&self, fee_amount: &BigUint, address: &ManagedAddress) { - let accepted_fee_token = self.fee_token().get(); - if accepted_fee_token == EgldOrEsdtTokenIdentifier::egld() { - self.send().direct_egld(address, fee_amount); - } else { - let esdt_fee_token = accepted_fee_token.unwrap_esdt(); - self.send() - .direct_esdt(address, &esdt_fee_token, 0, fee_amount); - } - } - #[view(getAmount)] fn get_amount( &self, @@ -188,18 +60,4 @@ pub trait DigitalCash: pay_fee_and_fund::PayFeeAndFund + storage::StorageModule BigUint::zero() } - - 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 } diff --git a/contracts/examples/digital-cash/src/helpers.rs b/contracts/examples/digital-cash/src/helpers.rs new file mode 100644 index 0000000000..48d7ba4d96 --- /dev/null +++ b/contracts/examples/digital-cash/src/helpers.rs @@ -0,0 +1,34 @@ +multiversx_sc::imports!(); + +use crate::{constants::*, storage}; +#[multiversx_sc::module] +pub trait HelpersModule: storage::StorageModule { + fn send_fee_to_address(&self, fee_amount: &BigUint, address: &ManagedAddress) { + let accepted_fee_token = self.fee_token().get(); + if accepted_fee_token == EgldOrEsdtTokenIdentifier::egld() { + self.send().direct_egld(address, fee_amount); + } else { + let esdt_fee_token = accepted_fee_token.unwrap_esdt(); + self.send() + .direct_esdt(address, &esdt_fee_token, 0, fee_amount); + } + } + + 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 get_expiration_round(&self, valability: u64) -> u64 { + let valability_rounds = valability / SECONDS_PER_ROUND; + self.blockchain().get_block_round() + valability_rounds + } +} diff --git a/contracts/examples/digital-cash/src/pay_fee_and_fund.rs b/contracts/examples/digital-cash/src/pay_fee_and_fund.rs index 3e760a6596..230914d5bf 100644 --- a/contracts/examples/digital-cash/src/pay_fee_and_fund.rs +++ b/contracts/examples/digital-cash/src/pay_fee_and_fund.rs @@ -4,11 +4,11 @@ multiversx_sc::derive_imports!(); use crate::{ constants::*, deposit_info::{DepositInfo, Fee}, - storage, + helpers, storage, }; #[multiversx_sc::module] -pub trait PayFeeAndFund: storage::StorageModule { +pub trait PayFeeAndFund: storage::StorageModule + helpers::HelpersModule { #[endpoint] #[payable("*")] fn fund(&self, address: ManagedAddress, valability: u64) { @@ -74,22 +74,4 @@ pub trait PayFeeAndFund: storage::StorageModule { }; deposit_mapper.set(new_deposit); } - - 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 get_expiration_round(&self, valability: u64) -> u64 { - let valability_rounds = valability / SECONDS_PER_ROUND; - self.blockchain().get_block_round() + valability_rounds - } } diff --git a/contracts/examples/digital-cash/src/signature_operations.rs b/contracts/examples/digital-cash/src/signature_operations.rs new file mode 100644 index 0000000000..3f3c1642ee --- /dev/null +++ b/contracts/examples/digital-cash/src/signature_operations.rs @@ -0,0 +1,140 @@ +multiversx_sc::imports!(); +multiversx_sc::derive_imports!(); + +use crate::{constants::*, helpers, storage}; + +pub use multiversx_sc::api::{ED25519_KEY_BYTE_LEN, ED25519_SIGNATURE_BYTE_LEN}; + +#[multiversx_sc::module] +pub trait SignatureOperationsModule: storage::StorageModule + helpers::HelpersModule { + #[endpoint] + fn withdraw(&self, address: ManagedAddress) { + let deposit_mapper = self.deposit(&address); + let accepted_fee_token = self.fee_token().get(); + require!(!deposit_mapper.is_empty(), NON_EXISTENT_KEY_ERR_MSG); + + let block_round = self.blockchain().get_block_round(); + let deposit = deposit_mapper.take(); + require!( + deposit.expiration_round < block_round, + "withdrawal has not been available yet" + ); + + let mut egld_funds = deposit.egld_funds; + let mut esdt_funds = deposit.esdt_funds; + + if accepted_fee_token == EgldOrEsdtTokenIdentifier::egld() { + egld_funds += deposit.fees.value; + } else { + let esdt_fee_token = accepted_fee_token.unwrap_esdt(); + let esdt_fee = EsdtTokenPayment::new(esdt_fee_token, 0, deposit.fees.value); + esdt_funds.push(esdt_fee); + } + + if egld_funds > 0 { + self.send() + .direct_egld(&deposit.depositor_address, &egld_funds); + } + + if !esdt_funds.is_empty() { + self.send() + .direct_multi(&deposit.depositor_address, &esdt_funds); + } + } + + #[endpoint] + fn claim( + &self, + address: ManagedAddress, + signature: ManagedByteArray, + ) { + 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"); + + 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 > 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_fee_to_address(&deposit.fees.value, &deposit.depositor_address); + } + } + + #[endpoint] + fn forward( + &self, + address: ManagedAddress, + forward_address: ManagedAddress, + signature: ManagedByteArray, + ) { + 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).take(); + let num_tokens = forwarded_deposit.get_num_tokens(); + deposit_mapper.update(|deposit| { + require!( + deposit.egld_funds == BigUint::zero() && deposit.esdt_funds.is_empty(), + "key already used" + ); + require!( + &fee * num_tokens as u64 <= deposit.fees.value, + "cannot forward funds without the owner covering the fee cost first" + ); + + deposit.fees.num_token_to_transfer += num_tokens; + deposit.valability = forwarded_deposit.valability; + deposit.expiration_round = self.get_expiration_round(forwarded_deposit.valability); + deposit.esdt_funds = forwarded_deposit.esdt_funds; + deposit.egld_funds = forwarded_deposit.egld_funds; + }); + + let forward_fee = &fee * num_tokens as u64; + forwarded_deposit.fees.value -= &forward_fee; + + self.collected_fees() + .update(|collected_fees| *collected_fees += forward_fee); + + if forwarded_deposit.fees.value > 0 { + self.send_fee_to_address( + &forwarded_deposit.fees.value, + &forwarded_deposit.depositor_address, + ); + } + } + + 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()); + } +} From 708298f73a8fa2d76143ef5649218b48d977cb99 Mon Sep 17 00:00:00 2001 From: Alin Cruceat Date: Tue, 19 Sep 2023 09:44:18 +0300 Subject: [PATCH 06/20] pay fee and fund --- .../examples/digital-cash/src/helpers.rs | 64 ++++++++++++++++- .../digital-cash/src/pay_fee_and_fund.rs | 68 +++++-------------- .../examples/digital-cash/wasm/src/lib.rs | 11 +-- 3 files changed, 85 insertions(+), 58 deletions(-) diff --git a/contracts/examples/digital-cash/src/helpers.rs b/contracts/examples/digital-cash/src/helpers.rs index 48d7ba4d96..70bd73ff32 100644 --- a/contracts/examples/digital-cash/src/helpers.rs +++ b/contracts/examples/digital-cash/src/helpers.rs @@ -1,6 +1,10 @@ multiversx_sc::imports!(); -use crate::{constants::*, storage}; +use crate::{ + constants::*, + deposit_info::{DepositInfo, Fee}, + storage, +}; #[multiversx_sc::module] pub trait HelpersModule: storage::StorageModule { fn send_fee_to_address(&self, fee_amount: &BigUint, address: &ManagedAddress) { @@ -31,4 +35,62 @@ pub trait HelpersModule: storage::StorageModule { let valability_rounds = valability / SECONDS_PER_ROUND; self.blockchain().get_block_round() + valability_rounds } + + fn check_token_is_accepted_as_fee(&self, token: &EgldOrEsdtTokenIdentifier) { + let accepted_fee_token = self.fee_token().get(); + require!(token == &accepted_fee_token, "Invalid fee token provided"); + } + + fn make_fund( + &self, + egld_payment: BigUint, + esdt_payment: ManagedVec, + address: ManagedAddress, + valability: u64, + ) { + let deposit_mapper = self.deposit(&address); + 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(); + deposit_mapper.update(|deposit| { + require!( + deposit.egld_funds == 0 && deposit.esdt_funds.is_empty(), + "key already used" + ); + require!( + fee * num_tokens as u64 <= deposit.fees.value, + CANNOT_DEPOSIT_FUNDS_ERR_MSG + ); + + deposit.fees.num_token_to_transfer += num_tokens; + deposit.valability = valability; + deposit.expiration_round = self.get_expiration_round(valability); + deposit.esdt_funds = esdt_payment; + deposit.egld_funds = egld_payment; + }); + } + + fn update_fees(&self, address: &ManagedAddress, payment: EgldOrEsdtTokenPayment) { + self.check_token_is_accepted_as_fee(&payment.token_identifier); + 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.amount); + 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.amount, + }, + }; + deposit_mapper.set(new_deposit); + } } diff --git a/contracts/examples/digital-cash/src/pay_fee_and_fund.rs b/contracts/examples/digital-cash/src/pay_fee_and_fund.rs index 230914d5bf..6ae4ee0343 100644 --- a/contracts/examples/digital-cash/src/pay_fee_and_fund.rs +++ b/contracts/examples/digital-cash/src/pay_fee_and_fund.rs @@ -1,14 +1,22 @@ multiversx_sc::imports!(); multiversx_sc::derive_imports!(); -use crate::{ - constants::*, - deposit_info::{DepositInfo, Fee}, - helpers, storage, -}; +use crate::{constants::*, helpers, storage}; #[multiversx_sc::module] pub trait PayFeeAndFund: storage::StorageModule + helpers::HelpersModule { + #[endpoint] + #[payable("*")] + fn pay_fee_and_fund(&self, address: ManagedAddress, valability: u64) { + let mut payments = self.call_value().all_esdt_transfers().clone_value(); + let fee = EgldOrEsdtTokenPayment::from(payments.get(0)); + self.update_fees(&address, fee); + + payments.remove(0); + + self.make_fund(0u64.into(), payments, address, valability) + } + #[endpoint] #[payable("*")] fn fund(&self, address: ManagedAddress, valability: u64) { @@ -19,59 +27,15 @@ pub trait PayFeeAndFund: storage::StorageModule + helpers::HelpersModule { self.blockchain().get_caller() == depositor, "invalid depositor" ); - let egld_payment = self.call_value().egld_value().clone_value(); 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(); - deposit_mapper.update(|deposit| { - require!( - deposit.egld_funds == 0 && deposit.esdt_funds.is_empty(), - "key already used" - ); - require!( - fee * num_tokens as u64 <= deposit.fees.value, - CANNOT_DEPOSIT_FUNDS_ERR_MSG - ); - - deposit.fees.num_token_to_transfer += num_tokens; - deposit.valability = valability; - deposit.expiration_round = self.get_expiration_round(valability); - deposit.esdt_funds = esdt_payment; - deposit.egld_funds = egld_payment; - }); + self.make_fund(egld_payment, esdt_payment, address, valability) } #[endpoint(depositFees)] #[payable("EGLD")] - fn deposit_fees(&self, address: ManagedAddress) { + fn deposit_fees(&self, address: &ManagedAddress) { let payment = self.call_value().egld_or_single_esdt(); - let accepted_fee_token = self.fee_token().get(); - require!( - payment.token_identifier == accepted_fee_token, - "Invalid fee token provided" - ); - 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.amount); - - 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.amount, - }, - }; - deposit_mapper.set(new_deposit); + self.update_fees(address, payment); } } diff --git a/contracts/examples/digital-cash/wasm/src/lib.rs b/contracts/examples/digital-cash/wasm/src/lib.rs index 854d3310f9..aca6cd6109 100644 --- a/contracts/examples/digital-cash/wasm/src/lib.rs +++ b/contracts/examples/digital-cash/wasm/src/lib.rs @@ -5,9 +5,9 @@ //////////////////////////////////////////////////// // Init: 1 -// Endpoints: 8 +// Endpoints: 9 // Async Callback (empty): 1 -// Total number of exported functions: 10 +// Total number of exported functions: 11 #![no_std] @@ -22,13 +22,14 @@ multiversx_sc_wasm_adapter::endpoints! { digital_cash ( init => init + claimFees => claim_fees + getAmount => get_amount + pay_fee_and_fund => pay_fee_and_fund fund => fund + depositFees => deposit_fees withdraw => withdraw claim => claim - claimFees => claim_fees - depositFees => deposit_fees forward => forward - getAmount => get_amount deposit => deposit ) } From 8df5ef6e9875d00b94fff0b65288eb8dfd798a94 Mon Sep 17 00:00:00 2001 From: Alin Cruceat Date: Tue, 19 Sep 2023 10:23:34 +0300 Subject: [PATCH 07/20] make forward payable and allow it to cover fees --- .../examples/digital-cash/src/helpers.rs | 17 ++++++-- .../digital-cash/src/pay_fee_and_fund.rs | 6 ++- .../digital-cash/src/signature_operations.rs | 40 ++++++++++--------- 3 files changed, 40 insertions(+), 23 deletions(-) diff --git a/contracts/examples/digital-cash/src/helpers.rs b/contracts/examples/digital-cash/src/helpers.rs index 70bd73ff32..3529619868 100644 --- a/contracts/examples/digital-cash/src/helpers.rs +++ b/contracts/examples/digital-cash/src/helpers.rs @@ -71,12 +71,23 @@ pub trait HelpersModule: storage::StorageModule { }); } - fn update_fees(&self, address: &ManagedAddress, payment: EgldOrEsdtTokenPayment) { + fn update_fees( + &self, + caller_address: ManagedAddress, + address: &ManagedAddress, + payment: EgldOrEsdtTokenPayment, + ) { self.check_token_is_accepted_as_fee(&payment.token_identifier); - 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.amount); + deposit_mapper.update(|deposit| { + require!( + deposit.depositor_address, + caller_address, + "invalid depositor address" + ); + deposit.fees.value += payment.amount; + }); return; } diff --git a/contracts/examples/digital-cash/src/pay_fee_and_fund.rs b/contracts/examples/digital-cash/src/pay_fee_and_fund.rs index 6ae4ee0343..ebc2435eac 100644 --- a/contracts/examples/digital-cash/src/pay_fee_and_fund.rs +++ b/contracts/examples/digital-cash/src/pay_fee_and_fund.rs @@ -10,7 +10,8 @@ pub trait PayFeeAndFund: storage::StorageModule + helpers::HelpersModule { fn pay_fee_and_fund(&self, address: ManagedAddress, valability: u64) { let mut payments = self.call_value().all_esdt_transfers().clone_value(); let fee = EgldOrEsdtTokenPayment::from(payments.get(0)); - self.update_fees(&address, fee); + let caller_address = self.blockchain().get_caller(); + self.update_fees(caller_address, &address, fee); payments.remove(0); @@ -36,6 +37,7 @@ pub trait PayFeeAndFund: storage::StorageModule + helpers::HelpersModule { #[payable("EGLD")] fn deposit_fees(&self, address: &ManagedAddress) { let payment = self.call_value().egld_or_single_esdt(); - self.update_fees(address, payment); + let caller_address = self.blockchain().get_caller(); + self.update_fees(caller_address, address, payment); } } diff --git a/contracts/examples/digital-cash/src/signature_operations.rs b/contracts/examples/digital-cash/src/signature_operations.rs index 3f3c1642ee..655e524d7e 100644 --- a/contracts/examples/digital-cash/src/signature_operations.rs +++ b/contracts/examples/digital-cash/src/signature_operations.rs @@ -80,52 +80,56 @@ pub trait SignatureOperationsModule: storage::StorageModule + helpers::HelpersMo } #[endpoint] + #[payable("*")] fn forward( &self, address: ManagedAddress, forward_address: ManagedAddress, signature: ManagedByteArray, ) { - let deposit_mapper = self.deposit(&forward_address); - require!(!deposit_mapper.is_empty(), CANNOT_DEPOSIT_FUNDS_ERR_MSG); - + let fee = self.call_value().egld_or_single_esdt(); let caller_address = self.blockchain().get_caller(); - let fee = self.fee().get(); self.require_signature(&address, &caller_address, signature); + self.update_fees(caller_address, &address, fee); - let mut forwarded_deposit = self.deposit(&address).take(); - let num_tokens = forwarded_deposit.get_num_tokens(); - deposit_mapper.update(|deposit| { + let new_deposit = self.deposit(&forward_address); + let fee = self.fee().get(); + + let mut current_deposit = self.deposit(&address).take(); + let num_tokens = current_deposit.get_num_tokens(); + new_deposit.update(|fwd_deposit| { require!( - deposit.egld_funds == BigUint::zero() && deposit.esdt_funds.is_empty(), + fwd_deposit.egld_funds == BigUint::zero() && fwd_deposit.esdt_funds.is_empty(), "key already used" ); require!( - &fee * num_tokens as u64 <= deposit.fees.value, + &fee * num_tokens as u64 <= fwd_deposit.fees.value, "cannot forward funds without the owner covering the fee cost first" ); - deposit.fees.num_token_to_transfer += num_tokens; - deposit.valability = forwarded_deposit.valability; - deposit.expiration_round = self.get_expiration_round(forwarded_deposit.valability); - deposit.esdt_funds = forwarded_deposit.esdt_funds; - deposit.egld_funds = forwarded_deposit.egld_funds; + fwd_deposit.fees.num_token_to_transfer += num_tokens; + fwd_deposit.valability = current_deposit.valability; + fwd_deposit.expiration_round = self.get_expiration_round(current_deposit.valability); + fwd_deposit.esdt_funds = current_deposit.esdt_funds; + fwd_deposit.egld_funds = current_deposit.egld_funds; }); let forward_fee = &fee * num_tokens as u64; - forwarded_deposit.fees.value -= &forward_fee; + current_deposit.fees.value -= &forward_fee; self.collected_fees() .update(|collected_fees| *collected_fees += forward_fee); - if forwarded_deposit.fees.value > 0 { + if current_deposit.fees.value > 0 { self.send_fee_to_address( - &forwarded_deposit.fees.value, - &forwarded_deposit.depositor_address, + ¤t_deposit.fees.value, + ¤t_deposit.depositor_address, ); } } + fn make_forward(&self) {} + fn require_signature( &self, address: &ManagedAddress, From aa6cfa5390d1855aa785cf90773df395dbcc8bc1 Mon Sep 17 00:00:00 2001 From: Alin Cruceat Date: Tue, 19 Sep 2023 10:29:16 +0300 Subject: [PATCH 08/20] update forward test --- .../digital-cash/scenarios/forward.scen.json | 36 ++++++++++++++++--- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/contracts/examples/digital-cash/scenarios/forward.scen.json b/contracts/examples/digital-cash/scenarios/forward.scen.json index 7a2e26f89f..cea54ec45e 100644 --- a/contracts/examples/digital-cash/scenarios/forward.scen.json +++ b/contracts/examples/digital-cash/scenarios/forward.scen.json @@ -11,6 +11,7 @@ "tx": { "from": "address:acc2", "to": "sc:the_digital_cash_contract", + "egldValue": "0", "function": "forward", "arguments": [ "0xdb474a3a065d3f0c0a62ae680ef6435e48eb482899d2ae30ff7a3a4b0ef19c60", @@ -54,10 +55,11 @@ }, { "step": "scCall", - "id": "forward-ok", + "id": "forward-without-fees-ok", "tx": { "from": "address:acc2", "to": "sc:the_digital_cash_contract", + "egldValue": "0", "function": "forward", "arguments": [ "0xdb474a3a065d3f0c0a62ae680ef6435e48eb482899d2ae30ff7a3a4b0ef19c60", @@ -81,7 +83,7 @@ "tx": { "from": "address:acc2", "to": "sc:the_digital_cash_contract", - "egldValue": "1,000", + "egldValue": "500", "function": "depositFees", "arguments": [ "0x8dc17613990e9b7476401a36d112d1a4d31190dec21e7e9a3c933872a27613ee" @@ -100,11 +102,37 @@ }, { "step": "scCall", - "id": "forward-ok", + "id": "forward-with-fees-fail", + "tx": { + "from": "address:acc1", + "to": "sc:the_digital_cash_contract", + "function": "forward", + "egldValue": "500", + "arguments": [ + "0x885532043a061e0c779e4064b85193f72cffd22c5bcc208c209128e60f21bf0d", + "0x8dc17613990e9b7476401a36d112d1a4d31190dec21e7e9a3c933872a27613ee", + "0x1ac4f6d4d45836d97ffeda83a66aaea7631a3bb3d4063421ccb2b9de9485bdb4c9bd6e44e003f6a9c9eb74379467238204ff579471d203b1878c3f1530592a02" + ], + "gasLimit": "500,000,000", + "gasPrice": "0" + }, + "expect": { + "out": [], + "status": "4", + "message": "invalid depositor address", + "logs": "*", + "gas": "*", + "refund": "*" + } + }, + { + "step": "scCall", + "id": "forward-with-fees-ok", "tx": { "from": "address:acc2", "to": "sc:the_digital_cash_contract", "function": "forward", + "egldValue": "500", "arguments": [ "0x885532043a061e0c779e4064b85193f72cffd22c5bcc208c209128e60f21bf0d", "0x8dc17613990e9b7476401a36d112d1a4d31190dec21e7e9a3c933872a27613ee", @@ -174,7 +202,7 @@ "code": "file:../output/digital-cash.wasm" }, "address:acc1": { - "nonce": "3", + "nonce": "4", "balance": "998,990", "storage": {} }, From 30bb5733abbc150dec5fd91c2611c4fc3553737a Mon Sep 17 00:00:00 2001 From: Alin Cruceat Date: Tue, 19 Sep 2023 10:33:34 +0300 Subject: [PATCH 09/20] fix broken require --- contracts/examples/digital-cash/src/helpers.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/contracts/examples/digital-cash/src/helpers.rs b/contracts/examples/digital-cash/src/helpers.rs index 3529619868..3ce6463196 100644 --- a/contracts/examples/digital-cash/src/helpers.rs +++ b/contracts/examples/digital-cash/src/helpers.rs @@ -82,8 +82,7 @@ pub trait HelpersModule: storage::StorageModule { if !deposit_mapper.is_empty() { deposit_mapper.update(|deposit| { require!( - deposit.depositor_address, - caller_address, + deposit.depositor_address == caller_address, "invalid depositor address" ); deposit.fees.value += payment.amount; From cdc1cfedf9da0e0511d4fb0367c44112c0670c60 Mon Sep 17 00:00:00 2001 From: Alin Cruceat Date: Tue, 19 Sep 2023 10:41:43 +0300 Subject: [PATCH 10/20] mandos error message typo --- contracts/examples/digital-cash/scenarios/forward.scen.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/examples/digital-cash/scenarios/forward.scen.json b/contracts/examples/digital-cash/scenarios/forward.scen.json index cea54ec45e..be6db19dff 100644 --- a/contracts/examples/digital-cash/scenarios/forward.scen.json +++ b/contracts/examples/digital-cash/scenarios/forward.scen.json @@ -119,7 +119,7 @@ "expect": { "out": [], "status": "4", - "message": "invalid depositor address", + "message": "str:invalid depositor address", "logs": "*", "gas": "*", "refund": "*" From fa52f7f6ccbe285b6ee08f1ad956a4029de373d5 Mon Sep 17 00:00:00 2001 From: Alin Cruceat Date: Tue, 19 Sep 2023 11:03:42 +0300 Subject: [PATCH 11/20] update scenario tests --- .../examples/digital-cash/scenarios/forward.scen.json | 8 ++++---- .../digital-cash/scenarios/fund-egld-and-esdt.scen.json | 6 ++++-- .../digital-cash/scenarios/withdraw-egld.scen.json | 3 ++- .../digital-cash/scenarios/withdraw-esdt.scen.json | 3 ++- .../examples/digital-cash/src/signature_operations.rs | 4 ++-- 5 files changed, 14 insertions(+), 10 deletions(-) diff --git a/contracts/examples/digital-cash/scenarios/forward.scen.json b/contracts/examples/digital-cash/scenarios/forward.scen.json index be6db19dff..289febebfd 100644 --- a/contracts/examples/digital-cash/scenarios/forward.scen.json +++ b/contracts/examples/digital-cash/scenarios/forward.scen.json @@ -104,13 +104,13 @@ "step": "scCall", "id": "forward-with-fees-fail", "tx": { - "from": "address:acc1", + "from": "address:acc2", "to": "sc:the_digital_cash_contract", "function": "forward", "egldValue": "500", "arguments": [ "0x885532043a061e0c779e4064b85193f72cffd22c5bcc208c209128e60f21bf0d", - "0x8dc17613990e9b7476401a36d112d1a4d31190dec21e7e9a3c933872a27613ee", + "0x885532043a061e0c779e4064b85193f72cffd22c5bcc208c209128e60f21bf0d", "0x1ac4f6d4d45836d97ffeda83a66aaea7631a3bb3d4063421ccb2b9de9485bdb4c9bd6e44e003f6a9c9eb74379467238204ff579471d203b1878c3f1530592a02" ], "gasLimit": "500,000,000", @@ -202,12 +202,12 @@ "code": "file:../output/digital-cash.wasm" }, "address:acc1": { - "nonce": "4", + "nonce": "3", "balance": "998,990", "storage": {} }, "address:acc2": { - "nonce": "8", + "nonce": "9", "balance": "997,000", "esdt": { "str:CASHTOKEN-123456": "50" 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 accbeec1b6..a271c06135 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 @@ -94,7 +94,8 @@ "1-value": "biguint:1,000" } }, - "str:fee": "10" + "str:fee": "10", + "str:feeToken": "str:EGLD" }, "code": "file:../output/digital-cash.wasm" }, @@ -211,7 +212,8 @@ "1-value": "biguint:1,000" } }, - "str:fee": "10" + "str:fee": "10", + "str:feeToken": "str:EGLD" }, "code": "file:../output/digital-cash.wasm" }, diff --git a/contracts/examples/digital-cash/scenarios/withdraw-egld.scen.json b/contracts/examples/digital-cash/scenarios/withdraw-egld.scen.json index 84b6c9a123..3f5dc4fba9 100644 --- a/contracts/examples/digital-cash/scenarios/withdraw-egld.scen.json +++ b/contracts/examples/digital-cash/scenarios/withdraw-egld.scen.json @@ -73,7 +73,8 @@ "1-value": "biguint:1,000" } }, - "str:fee": "10" + "str:fee": "10", + "str:feeToken": "str:EGLD" }, "code": "file:../output/digital-cash.wasm" }, diff --git a/contracts/examples/digital-cash/scenarios/withdraw-esdt.scen.json b/contracts/examples/digital-cash/scenarios/withdraw-esdt.scen.json index 0a02a2e3eb..199fcb59cb 100644 --- a/contracts/examples/digital-cash/scenarios/withdraw-esdt.scen.json +++ b/contracts/examples/digital-cash/scenarios/withdraw-esdt.scen.json @@ -196,7 +196,8 @@ "1-value": "biguint:1,000" } }, - "str:fee": "10" + "str:fee": "10", + "str:feeToken": "str:EGLD" }, "code": "file:../output/digital-cash.wasm" }, diff --git a/contracts/examples/digital-cash/src/signature_operations.rs b/contracts/examples/digital-cash/src/signature_operations.rs index 655e524d7e..a484ccacbe 100644 --- a/contracts/examples/digital-cash/src/signature_operations.rs +++ b/contracts/examples/digital-cash/src/signature_operations.rs @@ -90,7 +90,7 @@ pub trait SignatureOperationsModule: storage::StorageModule + helpers::HelpersMo let fee = self.call_value().egld_or_single_esdt(); let caller_address = self.blockchain().get_caller(); self.require_signature(&address, &caller_address, signature); - self.update_fees(caller_address, &address, fee); + self.update_fees(caller_address, &forward_address, fee); let new_deposit = self.deposit(&forward_address); let fee = self.fee().get(); @@ -104,7 +104,7 @@ pub trait SignatureOperationsModule: storage::StorageModule + helpers::HelpersMo ); require!( &fee * num_tokens as u64 <= fwd_deposit.fees.value, - "cannot forward funds without the owner covering the fee cost first" + "cannot deposit funds without covering the fee cost first" ); fwd_deposit.fees.num_token_to_transfer += num_tokens; From 8a320aab5f62c8286f9ef6fd4c21c174ef496d22 Mon Sep 17 00:00:00 2001 From: Alin Cruceat Date: Tue, 19 Sep 2023 13:32:13 +0300 Subject: [PATCH 12/20] pay fee and fund egld 1 transfer --- .../digital-cash/src/pay_fee_and_fund.rs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/contracts/examples/digital-cash/src/pay_fee_and_fund.rs b/contracts/examples/digital-cash/src/pay_fee_and_fund.rs index ebc2435eac..8082dfa7f8 100644 --- a/contracts/examples/digital-cash/src/pay_fee_and_fund.rs +++ b/contracts/examples/digital-cash/src/pay_fee_and_fund.rs @@ -7,7 +7,7 @@ use crate::{constants::*, helpers, storage}; pub trait PayFeeAndFund: storage::StorageModule + helpers::HelpersModule { #[endpoint] #[payable("*")] - fn pay_fee_and_fund(&self, address: ManagedAddress, valability: u64) { + fn pay_fee_and_fund_esdt(&self, address: ManagedAddress, valability: u64) { let mut payments = self.call_value().all_esdt_transfers().clone_value(); let fee = EgldOrEsdtTokenPayment::from(payments.get(0)); let caller_address = self.blockchain().get_caller(); @@ -17,6 +17,20 @@ pub trait PayFeeAndFund: storage::StorageModule + helpers::HelpersModule { self.make_fund(0u64.into(), payments, address, valability) } + #[endpoint] + #[payable("EGLD")] + fn pay_fee_and_fund_egld(&self, address: ManagedAddress, valability: u64) { + let mut fund = self.call_value().egld_value().clone_value(); + let fee_value = self.fee().get(); + require!(fund > fee_value, "payment not covering fees"); + + fund -= fee_value.clone(); + let fee = EgldOrEsdtTokenPayment::new(EgldOrEsdtTokenIdentifier::egld(), 0, fee_value); + let caller_address = self.blockchain().get_caller(); + self.update_fees(caller_address, &address, fee); + + self.make_fund(fund, ManagedVec::new(), address, valability); + } #[endpoint] #[payable("*")] From 7f981e9a49ee274a36e84854674d76a8ebd51084 Mon Sep 17 00:00:00 2001 From: Alin Cruceat Date: Tue, 26 Sep 2023 14:16:05 +0300 Subject: [PATCH 13/20] fix after review + tests --- .../scenarios/claim-egld.scen.json | 23 +++++-- .../scenarios/claim-esdt.scen.json | 23 +++++-- .../scenarios/claim-fees.scen.json | 21 +++++-- .../scenarios/claim-multi-esdt.scen.json | 23 +++++-- .../digital-cash/scenarios/forward.scen.json | 29 +++++++-- .../scenarios/fund-egld-and-esdt.scen.json | 63 +++++++++++++++---- .../scenarios/set-accounts.scen.json | 9 ++- .../scenarios/withdraw-egld.scen.json | 54 ++++++++++++---- .../scenarios/withdraw-esdt.scen.json | 54 ++++++++++++---- .../scenarios/withdraw-multi-esdt.scen.json | 48 +++++++++++--- .../examples/digital-cash/src/deposit_info.rs | 4 +- .../examples/digital-cash/src/digital_cash.rs | 44 ++++++++++--- .../examples/digital-cash/src/helpers.rs | 53 ++++++++++------ .../digital-cash/src/pay_fee_and_fund.rs | 16 +++-- .../digital-cash/src/signature_operations.rs | 48 +++++++------- .../examples/digital-cash/src/storage.rs | 13 ++-- 16 files changed, 391 insertions(+), 134 deletions(-) diff --git a/contracts/examples/digital-cash/scenarios/claim-egld.scen.json b/contracts/examples/digital-cash/scenarios/claim-egld.scen.json index 9e91e2fa05..c9c70411c9 100644 --- a/contracts/examples/digital-cash/scenarios/claim-egld.scen.json +++ b/contracts/examples/digital-cash/scenarios/claim-egld.scen.json @@ -138,7 +138,11 @@ "4-expiration_round": "u64:16", "5-fees": { "0-num_token_to_transfer": "u32:1", - "1-value": "biguint:1,000" + "1-value": { + "0-tokenIdentifier": "nested:str:EGLD", + "1-nonce": "u64:0", + "2-amount": "biguint:1,000" + } } }, "str:deposit|0x885532043a061e0c779e4064b85193f72cffd22c5bcc208c209128e60f21bf0d": { @@ -149,12 +153,21 @@ "4-expiration_round": "u64:16", "5-fees": { "0-num_token_to_transfer": "u32:3", - "1-value": "biguint:1,000" + "1-value": { + "0-tokenIdentifier": "nested:str:EGLD", + "1-nonce": "u64:0", + "2-amount": "biguint:1,000" + } } }, - "str:fee": "10", - "str:feeToken": "str:EGLD", - "str:collectedFees": "10" + "str:fee|nested:str:EGLD": "10", + "str:whitelistedFeeTokens|str:.len": "1", + "str:whitelistedFeeTokens|str:.item|u32:1": "str:EGLD", + "str:whitelistedFeeTokens|str:.index|nested:str:EGLD": "1", + "str:allTimeFeeTokens|str:.len": "1", + "str:allTimeFeeTokens|str:.item|u32:1": "str:EGLD", + "str:allTimeFeeTokens|str:.index|nested:str:EGLD": "1", + "str:collectedFees|nested:str:EGLD": "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 20b2f4c0f2..8c0931423b 100644 --- a/contracts/examples/digital-cash/scenarios/claim-esdt.scen.json +++ b/contracts/examples/digital-cash/scenarios/claim-esdt.scen.json @@ -137,7 +137,11 @@ "4-expiration_round": "u64:10", "5-fees": { "0-num_token_to_transfer": "u32:1", - "1-value": "biguint:1,000" + "1-value": { + "0-tokenIdentifier": "nested:str:EGLD", + "1-nonce": "u64:0", + "2-amount": "biguint:1,000" + } } }, "str:deposit|0x885532043a061e0c779e4064b85193f72cffd22c5bcc208c209128e60f21bf0d": { @@ -148,12 +152,21 @@ "4-expiration_round": "u64:16", "5-fees": { "0-num_token_to_transfer": "u32:3", - "1-value": "biguint:1,000" + "1-value": { + "0-tokenIdentifier": "nested:str:EGLD", + "1-nonce": "u64:0", + "2-amount": "biguint:1,000" + } } }, - "str:fee": "10", - "str:feeToken": "str:EGLD", - "str:collectedFees": "10" + "str:fee|nested:str:EGLD": "10", + "str:whitelistedFeeTokens|str:.len": "1", + "str:whitelistedFeeTokens|str:.item|u32:1": "str:EGLD", + "str:whitelistedFeeTokens|str:.index|nested:str:EGLD": "1", + "str:allTimeFeeTokens|str:.len": "1", + "str:allTimeFeeTokens|str:.item|u32:1": "str:EGLD", + "str:allTimeFeeTokens|str:.index|nested:str:EGLD": "1", + "str:collectedFees|nested:str:EGLD": "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 a176c13ffb..78611be938 100644 --- a/contracts/examples/digital-cash/scenarios/claim-fees.scen.json +++ b/contracts/examples/digital-cash/scenarios/claim-fees.scen.json @@ -73,7 +73,11 @@ "4-expiration_round": "u64:16", "5-fees": { "0-num_token_to_transfer": "u32:1", - "1-value": "biguint:1,000" + "1-value": { + "0-tokenIdentifier": "nested:str:EGLD", + "1-nonce": "u64:0", + "2-amount": "biguint:1,000" + } } }, "str:deposit|0x885532043a061e0c779e4064b85193f72cffd22c5bcc208c209128e60f21bf0d": { @@ -84,11 +88,20 @@ "4-expiration_round": "u64:16", "5-fees": { "0-num_token_to_transfer": "u32:3", - "1-value": "biguint:1,000" + "1-value": { + "0-tokenIdentifier": "nested:str:EGLD", + "1-nonce": "u64:0", + "2-amount": "biguint:1,000" + } } }, - "str:fee": "10", - "str:feeToken": "str:EGLD" + "str:fee|nested:str:EGLD": "10", + "str:whitelistedFeeTokens|str:.len": "1", + "str:whitelistedFeeTokens|str:.item|u32:1": "str:EGLD", + "str:whitelistedFeeTokens|str:.index|nested:str:EGLD": "1", + "str:allTimeFeeTokens|str:.len": "1", + "str:allTimeFeeTokens|str:.item|u32:1": "str:EGLD", + "str:allTimeFeeTokens|str:.index|nested:str:EGLD": "1" }, "code": "file:../output/digital-cash.wasm" }, 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 444c7753ed..301d037ab7 100644 --- a/contracts/examples/digital-cash/scenarios/claim-multi-esdt.scen.json +++ b/contracts/examples/digital-cash/scenarios/claim-multi-esdt.scen.json @@ -135,7 +135,11 @@ "4-expiration_round": "u64:10", "5-fees": { "0-num_token_to_transfer": "u32:1", - "1-value": "biguint:1,000" + "1-value": { + "0-tokenIdentifier": "nested:str:EGLD", + "1-nonce": "u64:0", + "2-amount": "biguint:1,000" + } } }, "str:deposit|0x487bd4010b50c24a02018345fe5171edf4182e6294325382c75ef4c4409f01bd": { @@ -146,12 +150,21 @@ "4-expiration_round": "u64:16", "5-fees": { "0-num_token_to_transfer": "u32:1", - "1-value": "biguint:1,000" + "1-value": { + "0-tokenIdentifier": "nested:str:EGLD", + "1-nonce": "u64:0", + "2-amount": "biguint:1,000" + } } }, - "str:fee": "10", - "str:feeToken": "str:EGLD", - "str:collectedFees": "30" + "str:fee|nested:str:EGLD": "10", + "str:whitelistedFeeTokens|str:.len": "1", + "str:whitelistedFeeTokens|str:.item|u32:1": "str:EGLD", + "str:whitelistedFeeTokens|str:.index|nested:str:EGLD": "1", + "str:allTimeFeeTokens|str:.len": "1", + "str:allTimeFeeTokens|str:.item|u32:1": "str:EGLD", + "str:allTimeFeeTokens|str:.index|nested:str:EGLD": "1", + "str:collectedFees|nested:str:EGLD": "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 289febebfd..44721e7f2f 100644 --- a/contracts/examples/digital-cash/scenarios/forward.scen.json +++ b/contracts/examples/digital-cash/scenarios/forward.scen.json @@ -170,7 +170,11 @@ "4-expiration_round": "u64:10", "5-fees": { "0-num_token_to_transfer": "u32:1", - "1-value": "biguint:1,000" + "1-value": { + "0-tokenIdentifier": "nested:str:EGLD", + "1-nonce": "u64:0", + "2-amount": "biguint:1,000" + } } }, "str:deposit|0x487bd4010b50c24a02018345fe5171edf4182e6294325382c75ef4c4409f01bd": { @@ -181,7 +185,11 @@ "4-expiration_round": "u64:16", "5-fees": { "0-num_token_to_transfer": "u32:1", - "1-value": "biguint:1,000" + "1-value": { + "0-tokenIdentifier": "nested:str:EGLD", + "1-nonce": "u64:0", + "2-amount": "biguint:1,000" + } } }, "str:deposit|0x8dc17613990e9b7476401a36d112d1a4d31190dec21e7e9a3c933872a27613ee": { @@ -192,12 +200,21 @@ "4-expiration_round": "u64:16", "5-fees": { "0-num_token_to_transfer": "u32:3", - "1-value": "biguint:1,000" + "1-value": { + "0-tokenIdentifier": "nested:str:EGLD", + "1-nonce": "u64:0", + "2-amount": "biguint:1,000" + } } }, - "str:fee": "10", - "str:feeToken": "str:EGLD", - "str:collectedFees": "40" + "str:fee|nested:str:EGLD": "10", + "str:whitelistedFeeTokens|str:.len": "1", + "str:whitelistedFeeTokens|str:.item|u32:1": "str:EGLD", + "str:whitelistedFeeTokens|str:.index|nested:str:EGLD": "1", + "str:allTimeFeeTokens|str:.len": "1", + "str:allTimeFeeTokens|str:.item|u32:1": "str:EGLD", + "str:allTimeFeeTokens|str:.index|nested:str:EGLD": "1", + "str:collectedFees|nested:str:EGLD": "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 a271c06135..b30b5a2b97 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 @@ -91,11 +91,20 @@ "4-expiration_round": "u64:10", "5-fees": { "0-num_token_to_transfer": "u32:1", - "1-value": "biguint:1,000" + "1-value": { + "0-tokenIdentifier": "nested:str:EGLD", + "1-nonce": "u64:0", + "2-amount": "biguint:1,000" + } } }, - "str:fee": "10", - "str:feeToken": "str:EGLD" + "str:fee|nested:str:EGLD": "10", + "str:whitelistedFeeTokens|str:.len": "1", + "str:whitelistedFeeTokens|str:.item|u32:1": "str:EGLD", + "str:whitelistedFeeTokens|str:.index|nested:str:EGLD": "1", + "str:allTimeFeeTokens|str:.len": "1", + "str:allTimeFeeTokens|str:.item|u32:1": "str:EGLD", + "str:allTimeFeeTokens|str:.index|nested:str:EGLD": "1" }, "code": "file:../output/digital-cash.wasm" }, @@ -198,7 +207,11 @@ "4-expiration_round": "u64:10", "5-fees": { "0-num_token_to_transfer": "u32:1", - "1-value": "biguint:1,000" + "1-value": { + "0-tokenIdentifier": "nested:str:EGLD", + "1-nonce": "u64:0", + "2-amount": "biguint:1,000" + } } }, "str:deposit|0x487bd4010b50c24a02018345fe5171edf4182e6294325382c75ef4c4409f01bd": { @@ -209,11 +222,20 @@ "4-expiration_round": "u64:16", "5-fees": { "0-num_token_to_transfer": "u32:1", - "1-value": "biguint:1,000" + "1-value": { + "0-tokenIdentifier": "nested:str:EGLD", + "1-nonce": "u64:0", + "2-amount": "biguint:1,000" + } } }, - "str:fee": "10", - "str:feeToken": "str:EGLD" + "str:fee|nested:str:EGLD": "10", + "str:whitelistedFeeTokens|str:.len": "1", + "str:whitelistedFeeTokens|str:.item|u32:1": "str:EGLD", + "str:whitelistedFeeTokens|str:.index|nested:str:EGLD": "1", + "str:allTimeFeeTokens|str:.len": "1", + "str:allTimeFeeTokens|str:.item|u32:1": "str:EGLD", + "str:allTimeFeeTokens|str:.index|nested:str:EGLD": "1" }, "code": "file:../output/digital-cash.wasm" }, @@ -391,7 +413,11 @@ "4-expiration_round": "u64:10", "5-fees": { "0-num_token_to_transfer": "u32:1", - "1-value": "biguint:1,000" + "1-value": { + "0-tokenIdentifier": "nested:str:EGLD", + "1-nonce": "u64:0", + "2-amount": "biguint:1,000" + } } }, "str:deposit|0x487bd4010b50c24a02018345fe5171edf4182e6294325382c75ef4c4409f01bd": { @@ -402,7 +428,11 @@ "4-expiration_round": "u64:16", "5-fees": { "0-num_token_to_transfer": "u32:1", - "1-value": "biguint:1,000" + "1-value": { + "0-tokenIdentifier": "nested:str:EGLD", + "1-nonce": "u64:0", + "2-amount": "biguint:1,000" + } } }, "str:deposit|0x885532043a061e0c779e4064b85193f72cffd22c5bcc208c209128e60f21bf0d": { @@ -413,11 +443,20 @@ "4-expiration_round": "u64:16", "5-fees": { "0-num_token_to_transfer": "u32:3", - "1-value": "biguint:1,000" + "1-value": { + "0-tokenIdentifier": "nested:str:EGLD", + "1-nonce": "u64:0", + "2-amount": "biguint:1,000" + } } }, - "str:fee": "10", - "str:feeToken": "str:EGLD" + "str:fee|nested:str:EGLD": "10", + "str:whitelistedFeeTokens|str:.len": "1", + "str:whitelistedFeeTokens|str:.item|u32:1": "str:EGLD", + "str:whitelistedFeeTokens|str:.index|nested:str:EGLD": "1", + "str:allTimeFeeTokens|str:.len": "1", + "str:allTimeFeeTokens|str:.item|u32:1": "str:EGLD", + "str:allTimeFeeTokens|str:.index|nested:str:EGLD": "1" }, "code": "file:../output/digital-cash.wasm" }, diff --git a/contracts/examples/digital-cash/scenarios/set-accounts.scen.json b/contracts/examples/digital-cash/scenarios/set-accounts.scen.json index f403c32cba..f010d40595 100644 --- a/contracts/examples/digital-cash/scenarios/set-accounts.scen.json +++ b/contracts/examples/digital-cash/scenarios/set-accounts.scen.json @@ -64,8 +64,13 @@ "nonce": "0", "balance": "0", "storage": { - "str:fee": "10", - "str:feeToken": "str:EGLD" + "str:fee|nested:str:EGLD": "10", + "str:whitelistedFeeTokens|str:.len": "1", + "str:whitelistedFeeTokens|str:.item|u32:1": "str:EGLD", + "str:whitelistedFeeTokens|str:.index|nested:str:EGLD": "1", + "str:allTimeFeeTokens|str:.len": "1", + "str:allTimeFeeTokens|str:.item|u32:1": "str:EGLD", + "str:allTimeFeeTokens|str:.index|nested:str:EGLD": "1" }, "code": "file:../output/digital-cash.wasm" }, diff --git a/contracts/examples/digital-cash/scenarios/withdraw-egld.scen.json b/contracts/examples/digital-cash/scenarios/withdraw-egld.scen.json index 3f5dc4fba9..5c4182a164 100644 --- a/contracts/examples/digital-cash/scenarios/withdraw-egld.scen.json +++ b/contracts/examples/digital-cash/scenarios/withdraw-egld.scen.json @@ -7,7 +7,7 @@ }, { "step": "scCall", - "id": "withdraw-esdt-1", + "id": "withdraw-egld-fail-1", "tx": { "from": "address:acc1", "to": "sc:the_digital_cash_contract", @@ -48,7 +48,11 @@ "4-expiration_round": "u64:10", "5-fees": { "0-num_token_to_transfer": "u32:1", - "1-value": "biguint:1,000" + "1-value": { + "0-tokenIdentifier": "nested:str:EGLD", + "1-nonce": "u64:0", + "2-amount": "biguint:1,000" + } } }, "str:deposit|0x487bd4010b50c24a02018345fe5171edf4182e6294325382c75ef4c4409f01bd": { @@ -59,7 +63,11 @@ "4-expiration_round": "u64:16", "5-fees": { "0-num_token_to_transfer": "u32:1", - "1-value": "biguint:1,000" + "1-value": { + "0-tokenIdentifier": "nested:str:EGLD", + "1-nonce": "u64:0", + "2-amount": "biguint:1,000" + } } }, "str:deposit|0x885532043a061e0c779e4064b85193f72cffd22c5bcc208c209128e60f21bf0d": { @@ -70,11 +78,20 @@ "4-expiration_round": "u64:16", "5-fees": { "0-num_token_to_transfer": "u32:3", - "1-value": "biguint:1,000" + "1-value": { + "0-tokenIdentifier": "nested:str:EGLD", + "1-nonce": "u64:0", + "2-amount": "biguint:1,000" + } } }, - "str:fee": "10", - "str:feeToken": "str:EGLD" + "str:fee|nested:str:EGLD": "10", + "str:whitelistedFeeTokens|str:.len": "1", + "str:whitelistedFeeTokens|str:.item|u32:1": "str:EGLD", + "str:whitelistedFeeTokens|str:.index|nested:str:EGLD": "1", + "str:allTimeFeeTokens|str:.len": "1", + "str:allTimeFeeTokens|str:.item|u32:1": "str:EGLD", + "str:allTimeFeeTokens|str:.index|nested:str:EGLD": "1" }, "code": "file:../output/digital-cash.wasm" }, @@ -110,7 +127,7 @@ }, { "step": "scCall", - "id": "withdraw-egld-2", + "id": "withdraw-egld-fail-2", "tx": { "from": "address:acc1", "to": "sc:the_digital_cash_contract", @@ -142,7 +159,7 @@ }, { "step": "scCall", - "id": "withdraw-egld-3", + "id": "withdraw-egld-success", "tx": { "from": "address:acc1", "to": "sc:the_digital_cash_contract", @@ -183,7 +200,11 @@ "4-expiration_round": "u64:16", "5-fees": { "0-num_token_to_transfer": "u32:1", - "1-value": "biguint:1,000" + "1-value": { + "0-tokenIdentifier": "nested:str:EGLD", + "1-nonce": "u64:0", + "2-amount": "biguint:1,000" + } } }, "str:deposit|0x885532043a061e0c779e4064b85193f72cffd22c5bcc208c209128e60f21bf0d": { @@ -194,11 +215,20 @@ "4-expiration_round": "u64:16", "5-fees": { "0-num_token_to_transfer": "u32:3", - "1-value": "biguint:1,000" + "1-value": { + "0-tokenIdentifier": "nested:str:EGLD", + "1-nonce": "u64:0", + "2-amount": "biguint:1,000" + } } }, - "str:fee": "10", - "str:feeToken": "str:EGLD" + "str:fee|nested:str:EGLD": "10", + "str:whitelistedFeeTokens|str:.len": "1", + "str:whitelistedFeeTokens|str:.item|u32:1": "str:EGLD", + "str:whitelistedFeeTokens|str:.index|nested:str:EGLD": "1", + "str:allTimeFeeTokens|str:.len": "1", + "str:allTimeFeeTokens|str:.item|u32:1": "str:EGLD", + "str:allTimeFeeTokens|str:.index|nested:str:EGLD": "1" }, "code": "file:../output/digital-cash.wasm" }, diff --git a/contracts/examples/digital-cash/scenarios/withdraw-esdt.scen.json b/contracts/examples/digital-cash/scenarios/withdraw-esdt.scen.json index 199fcb59cb..07d7c821e4 100644 --- a/contracts/examples/digital-cash/scenarios/withdraw-esdt.scen.json +++ b/contracts/examples/digital-cash/scenarios/withdraw-esdt.scen.json @@ -7,7 +7,7 @@ }, { "step": "scCall", - "id": "withdraw-esdt-1", + "id": "withdraw-esdt-fail-1", "tx": { "from": "address:acc2", "to": "sc:the_digital_cash_contract", @@ -48,7 +48,11 @@ "4-expiration_round": "u64:10", "5-fees": { "0-num_token_to_transfer": "u32:1", - "1-value": "biguint:1,000" + "1-value": { + "0-tokenIdentifier": "nested:str:EGLD", + "1-nonce": "u64:0", + "2-amount": "biguint:1,000" + } } }, "str:deposit|0x487bd4010b50c24a02018345fe5171edf4182e6294325382c75ef4c4409f01bd": { @@ -59,7 +63,11 @@ "4-expiration_round": "u64:16", "5-fees": { "0-num_token_to_transfer": "u32:1", - "1-value": "biguint:1,000" + "1-value": { + "0-tokenIdentifier": "nested:str:EGLD", + "1-nonce": "u64:0", + "2-amount": "biguint:1,000" + } } }, "str:deposit|0x885532043a061e0c779e4064b85193f72cffd22c5bcc208c209128e60f21bf0d": { @@ -70,11 +78,20 @@ "4-expiration_round": "u64:16", "5-fees": { "0-num_token_to_transfer": "u32:3", - "1-value": "biguint:1,000" + "1-value": { + "0-tokenIdentifier": "nested:str:EGLD", + "1-nonce": "u64:0", + "2-amount": "biguint:1,000" + } } }, - "str:fee": "10", - "str:feeToken": "str:EGLD" + "str:fee|nested:str:EGLD": "10", + "str:whitelistedFeeTokens|str:.len": "1", + "str:whitelistedFeeTokens|str:.item|u32:1": "str:EGLD", + "str:whitelistedFeeTokens|str:.index|nested:str:EGLD": "1", + "str:allTimeFeeTokens|str:.len": "1", + "str:allTimeFeeTokens|str:.item|u32:1": "str:EGLD", + "str:allTimeFeeTokens|str:.index|nested:str:EGLD": "1" }, "code": "file:../output/digital-cash.wasm" }, @@ -110,7 +127,7 @@ }, { "step": "scCall", - "id": "withdraw-esdt-2", + "id": "withdraw-esdt-fail-2", "tx": { "from": "address:acc2", "to": "sc:the_digital_cash_contract", @@ -142,7 +159,7 @@ }, { "step": "scCall", - "id": "withdraw-esdt-3", + "id": "withdraw-esdt-success", "tx": { "from": "address:acc2", "to": "sc:the_digital_cash_contract", @@ -182,7 +199,11 @@ "4-expiration_round": "u64:10", "5-fees": { "0-num_token_to_transfer": "u32:1", - "1-value": "biguint:1,000" + "1-value": { + "0-tokenIdentifier": "nested:str:EGLD", + "1-nonce": "u64:0", + "2-amount": "biguint:1,000" + } } }, "str:deposit|0x885532043a061e0c779e4064b85193f72cffd22c5bcc208c209128e60f21bf0d": { @@ -193,11 +214,20 @@ "4-expiration_round": "u64:16", "5-fees": { "0-num_token_to_transfer": "u32:3", - "1-value": "biguint:1,000" + "1-value": { + "0-tokenIdentifier": "nested:str:EGLD", + "1-nonce": "u64:0", + "2-amount": "biguint:1,000" + } } }, - "str:fee": "10", - "str:feeToken": "str:EGLD" + "str:fee|nested:str:EGLD": "10", + "str:whitelistedFeeTokens|str:.len": "1", + "str:whitelistedFeeTokens|str:.item|u32:1": "str:EGLD", + "str:whitelistedFeeTokens|str:.index|nested:str:EGLD": "1", + "str:allTimeFeeTokens|str:.len": "1", + "str:allTimeFeeTokens|str:.item|u32:1": "str:EGLD", + "str:allTimeFeeTokens|str:.index|nested:str:EGLD": "1" }, "code": "file:../output/digital-cash.wasm" }, diff --git a/contracts/examples/digital-cash/scenarios/withdraw-multi-esdt.scen.json b/contracts/examples/digital-cash/scenarios/withdraw-multi-esdt.scen.json index 60569d329e..fae381c069 100644 --- a/contracts/examples/digital-cash/scenarios/withdraw-multi-esdt.scen.json +++ b/contracts/examples/digital-cash/scenarios/withdraw-multi-esdt.scen.json @@ -48,7 +48,11 @@ "4-expiration_round": "u64:10", "5-fees": { "0-num_token_to_transfer": "u32:1", - "1-value": "biguint:1,000" + "1-value": { + "0-tokenIdentifier": "nested:str:EGLD", + "1-nonce": "u64:0", + "2-amount": "biguint:1,000" + } } }, "str:deposit|0x487bd4010b50c24a02018345fe5171edf4182e6294325382c75ef4c4409f01bd": { @@ -59,7 +63,11 @@ "4-expiration_round": "u64:16", "5-fees": { "0-num_token_to_transfer": "u32:1", - "1-value": "biguint:1,000" + "1-value": { + "0-tokenIdentifier": "nested:str:EGLD", + "1-nonce": "u64:0", + "2-amount": "biguint:1,000" + } } }, "str:deposit|0x885532043a061e0c779e4064b85193f72cffd22c5bcc208c209128e60f21bf0d": { @@ -70,11 +78,20 @@ "4-expiration_round": "u64:16", "5-fees": { "0-num_token_to_transfer": "u32:3", - "1-value": "biguint:1,000" + "1-value": { + "0-tokenIdentifier": "nested:str:EGLD", + "1-nonce": "u64:0", + "2-amount": "biguint:1,000" + } } }, - "str:fee": "10", - "str:feeToken": "str:EGLD" + "str:fee|nested:str:EGLD": "10", + "str:whitelistedFeeTokens|str:.len": "1", + "str:whitelistedFeeTokens|str:.item|u32:1": "str:EGLD", + "str:whitelistedFeeTokens|str:.index|nested:str:EGLD": "1", + "str:allTimeFeeTokens|str:.len": "1", + "str:allTimeFeeTokens|str:.item|u32:1": "str:EGLD", + "str:allTimeFeeTokens|str:.index|nested:str:EGLD": "1" }, "code": "file:../output/digital-cash.wasm" }, @@ -180,7 +197,11 @@ "4-expiration_round": "u64:10", "5-fees": { "0-num_token_to_transfer": "u32:1", - "1-value": "biguint:1,000" + "1-value": { + "0-tokenIdentifier": "nested:str:EGLD", + "1-nonce": "u64:0", + "2-amount": "biguint:1,000" + } } }, "str:deposit|0x487bd4010b50c24a02018345fe5171edf4182e6294325382c75ef4c4409f01bd": { @@ -191,11 +212,20 @@ "4-expiration_round": "u64:16", "5-fees": { "0-num_token_to_transfer": "u32:1", - "1-value": "biguint:1,000" + "1-value": { + "0-tokenIdentifier": "nested:str:EGLD", + "1-nonce": "u64:0", + "2-amount": "biguint:1,000" + } } }, - "str:fee": "10", - "str:feeToken": "str:EGLD" + "str:fee|nested:str:EGLD": "10", + "str:whitelistedFeeTokens|str:.len": "1", + "str:whitelistedFeeTokens|str:.item|u32:1": "str:EGLD", + "str:whitelistedFeeTokens|str:.index|nested:str:EGLD": "1", + "str:allTimeFeeTokens|str:.len": "1", + "str:allTimeFeeTokens|str:.item|u32:1": "str:EGLD", + "str:allTimeFeeTokens|str:.index|nested:str:EGLD": "1" }, "code": "file:../output/digital-cash.wasm" }, diff --git a/contracts/examples/digital-cash/src/deposit_info.rs b/contracts/examples/digital-cash/src/deposit_info.rs index b156275aa7..2838d5de84 100644 --- a/contracts/examples/digital-cash/src/deposit_info.rs +++ b/contracts/examples/digital-cash/src/deposit_info.rs @@ -25,8 +25,8 @@ where } } -#[derive(NestedEncode, NestedDecode, TopEncode, TopDecode, TypeAbi, Default)] +#[derive(NestedEncode, NestedDecode, TopEncode, TopDecode, TypeAbi)] pub struct Fee { pub num_token_to_transfer: usize, - pub value: BigUint, + pub value: EgldOrEsdtTokenPayment, } diff --git a/contracts/examples/digital-cash/src/digital_cash.rs b/contracts/examples/digital-cash/src/digital_cash.rs index 5b00a9a341..031b0191e3 100644 --- a/contracts/examples/digital-cash/src/digital_cash.rs +++ b/contracts/examples/digital-cash/src/digital_cash.rs @@ -22,19 +22,47 @@ pub trait DigitalCash: { #[init] fn init(&self, fee: BigUint, token: EgldOrEsdtTokenIdentifier) { - self.fee().set(fee); - self.fee_token().set(token); + self.fee(&token).set(fee); + self.whitelisted_fee_tokens().insert(token.clone()); + self.all_time_fee_tokens().insert(token); } + + #[endpoint(whitelistFeeToken)] + #[only_owner] + fn whitelist_fee_token(&self, fee: BigUint, token: EgldOrEsdtTokenIdentifier) { + self.fee(&token).set(fee); + self.whitelisted_fee_tokens().insert(token.clone()); + self.all_time_fee_tokens().insert(token); + } + + #[endpoint(blacklistFeeToken)] + #[only_owner] + fn blacklist_fee_token(&self, token: EgldOrEsdtTokenIdentifier) { + self.fee(&token).clear(); + self.whitelisted_fee_tokens().swap_remove(&token); + } + #[endpoint(claimFees)] #[only_owner] fn claim_fees(&self) { - let fees = self.collected_fees().take(); - if fees == 0 { - return; - } - + let fee_tokens_mapper = self.all_time_fee_tokens(); + let fee_tokens = fee_tokens_mapper.iter(); let caller_address = self.blockchain().get_caller(); - self.send_fee_to_address(&fees, &caller_address); + let mut collected_esdt_fees = ManagedVec::new(); + for token in fee_tokens { + let fee = self.collected_fees(&token).take(); + if fee == 0 { + continue; + } + if token == EgldOrEsdtTokenIdentifier::egld() { + self.send().direct_egld(&caller_address, &fee); + } else { + let collected_fee = EsdtTokenPayment::new(token.unwrap_esdt(), 0, fee); + collected_esdt_fees.push(collected_fee); + } + } + self.send() + .direct_multi(&caller_address, &collected_esdt_fees); } #[view(getAmount)] diff --git a/contracts/examples/digital-cash/src/helpers.rs b/contracts/examples/digital-cash/src/helpers.rs index 3ce6463196..c9bb1ea0aa 100644 --- a/contracts/examples/digital-cash/src/helpers.rs +++ b/contracts/examples/digital-cash/src/helpers.rs @@ -7,14 +7,13 @@ use crate::{ }; #[multiversx_sc::module] pub trait HelpersModule: storage::StorageModule { - fn send_fee_to_address(&self, fee_amount: &BigUint, address: &ManagedAddress) { - let accepted_fee_token = self.fee_token().get(); - if accepted_fee_token == EgldOrEsdtTokenIdentifier::egld() { - self.send().direct_egld(address, fee_amount); + fn send_fee_to_address(&self, fee: &EgldOrEsdtTokenPayment, address: &ManagedAddress) { + if fee.token_identifier == EgldOrEsdtTokenIdentifier::egld() { + self.send().direct_egld(address, &fee.amount); } else { - let esdt_fee_token = accepted_fee_token.unwrap_esdt(); + let esdt_fee = fee.clone().unwrap_esdt(); self.send() - .direct_esdt(address, &esdt_fee_token, 0, fee_amount); + .direct_esdt(address, &esdt_fee.token_identifier, 0, &esdt_fee.amount); } } @@ -36,9 +35,13 @@ pub trait HelpersModule: storage::StorageModule { self.blockchain().get_block_round() + valability_rounds } - fn check_token_is_accepted_as_fee(&self, token: &EgldOrEsdtTokenIdentifier) { - let accepted_fee_token = self.fee_token().get(); - require!(token == &accepted_fee_token, "Invalid fee token provided"); + fn get_fee_for_token(&self, token: &EgldOrEsdtTokenIdentifier) -> BigUint { + require!( + self.whitelisted_fee_tokens().contains(token), + "invalid fee toke provided" + ); + let fee_token = self.fee(token); + fee_token.get() } fn make_fund( @@ -49,20 +52,13 @@ pub trait HelpersModule: storage::StorageModule { valability: u64, ) { let deposit_mapper = self.deposit(&address); - 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(); deposit_mapper.update(|deposit| { require!( deposit.egld_funds == 0 && deposit.esdt_funds.is_empty(), "key already used" ); - require!( - fee * num_tokens as u64 <= deposit.fees.value, - CANNOT_DEPOSIT_FUNDS_ERR_MSG - ); - + let num_tokens = self.get_num_token_transfers(&egld_payment, &esdt_payment); deposit.fees.num_token_to_transfer += num_tokens; deposit.valability = valability; deposit.expiration_round = self.get_expiration_round(valability); @@ -71,13 +67,26 @@ pub trait HelpersModule: storage::StorageModule { }); } + fn check_fees_cover_number_of_tokens( + &self, + num_tokens: usize, + fee: BigUint, + paid_fee: BigUint, + ) { + require!(num_tokens > 0, "amount must be greater than 0"); + require!( + fee * num_tokens as u64 <= paid_fee, + CANNOT_DEPOSIT_FUNDS_ERR_MSG + ); + } + fn update_fees( &self, caller_address: ManagedAddress, address: &ManagedAddress, payment: EgldOrEsdtTokenPayment, ) { - self.check_token_is_accepted_as_fee(&payment.token_identifier); + self.get_fee_for_token(&payment.token_identifier); let deposit_mapper = self.deposit(address); if !deposit_mapper.is_empty() { deposit_mapper.update(|deposit| { @@ -85,7 +94,11 @@ pub trait HelpersModule: storage::StorageModule { deposit.depositor_address == caller_address, "invalid depositor address" ); - deposit.fees.value += payment.amount; + require!( + deposit.fees.value.token_identifier == payment.token_identifier, + "can only have 1 type of token as fee" + ); + deposit.fees.value.amount += payment.amount; }); return; } @@ -98,7 +111,7 @@ pub trait HelpersModule: storage::StorageModule { expiration_round: 0, fees: Fee { num_token_to_transfer: 0, - value: payment.amount, + value: payment, }, }; deposit_mapper.set(new_deposit); diff --git a/contracts/examples/digital-cash/src/pay_fee_and_fund.rs b/contracts/examples/digital-cash/src/pay_fee_and_fund.rs index 8082dfa7f8..1c87089b8a 100644 --- a/contracts/examples/digital-cash/src/pay_fee_and_fund.rs +++ b/contracts/examples/digital-cash/src/pay_fee_and_fund.rs @@ -21,7 +21,7 @@ pub trait PayFeeAndFund: storage::StorageModule + helpers::HelpersModule { #[payable("EGLD")] fn pay_fee_and_fund_egld(&self, address: ManagedAddress, valability: u64) { let mut fund = self.call_value().egld_value().clone_value(); - let fee_value = self.fee().get(); + let fee_value = self.fee(&EgldOrEsdtTokenIdentifier::egld()).get(); require!(fund > fee_value, "payment not covering fees"); fund -= fee_value.clone(); @@ -35,16 +35,22 @@ pub trait PayFeeAndFund: storage::StorageModule + helpers::HelpersModule { #[endpoint] #[payable("*")] fn fund(&self, address: ManagedAddress, valability: u64) { - let deposit_mapper = self.deposit(&address); - require!(!deposit_mapper.is_empty(), FEES_NOT_COVERED_ERR_MSG); - let depositor = deposit_mapper.get().depositor_address; + require!(!self.deposit(&address).is_empty(), FEES_NOT_COVERED_ERR_MSG); + let deposit_mapper = self.deposit(&address).get(); + let depositor = deposit_mapper.depositor_address; require!( self.blockchain().get_caller() == depositor, "invalid depositor" ); + let deposited_fee_token = deposit_mapper.fees.value; + let fee_amount = self.fee(&deposited_fee_token.token_identifier).get(); let egld_payment = self.call_value().egld_value().clone_value(); let esdt_payment = self.call_value().all_esdt_transfers().clone_value(); - self.make_fund(egld_payment, esdt_payment, address, valability) + + let num_tokens = self.get_num_token_transfers(&egld_payment, &esdt_payment); + self.check_fees_cover_number_of_tokens(num_tokens, fee_amount, deposited_fee_token.amount); + + self.make_fund(egld_payment, esdt_payment, address, valability); } #[endpoint(depositFees)] diff --git a/contracts/examples/digital-cash/src/signature_operations.rs b/contracts/examples/digital-cash/src/signature_operations.rs index a484ccacbe..f02c1c7cb8 100644 --- a/contracts/examples/digital-cash/src/signature_operations.rs +++ b/contracts/examples/digital-cash/src/signature_operations.rs @@ -10,11 +10,12 @@ pub trait SignatureOperationsModule: storage::StorageModule + helpers::HelpersMo #[endpoint] fn withdraw(&self, address: ManagedAddress) { let deposit_mapper = self.deposit(&address); - let accepted_fee_token = self.fee_token().get(); require!(!deposit_mapper.is_empty(), NON_EXISTENT_KEY_ERR_MSG); - let block_round = self.blockchain().get_block_round(); let deposit = deposit_mapper.take(); + let paid_fee_token = deposit.fees.value; + + let block_round = self.blockchain().get_block_round(); require!( deposit.expiration_round < block_round, "withdrawal has not been available yet" @@ -23,11 +24,12 @@ pub trait SignatureOperationsModule: storage::StorageModule + helpers::HelpersMo let mut egld_funds = deposit.egld_funds; let mut esdt_funds = deposit.esdt_funds; - if accepted_fee_token == EgldOrEsdtTokenIdentifier::egld() { - egld_funds += deposit.fees.value; + if paid_fee_token.token_identifier == EgldOrEsdtTokenIdentifier::egld() { + egld_funds += paid_fee_token.amount; } else { - let esdt_fee_token = accepted_fee_token.unwrap_esdt(); - let esdt_fee = EsdtTokenPayment::new(esdt_fee_token, 0, deposit.fees.value); + let esdt_fee_token = paid_fee_token.unwrap_esdt(); + let esdt_fee = + EsdtTokenPayment::new(esdt_fee_token.token_identifier, 0, esdt_fee_token.amount); esdt_funds.push(esdt_fee); } @@ -55,15 +57,18 @@ pub trait SignatureOperationsModule: storage::StorageModule + helpers::HelpersMo 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(); + let deposit = deposit_mapper.take(); + let num_tokens_transfered = deposit.get_num_tokens(); + let mut deposited_fee = deposit.fees.value; + + let fee_token = deposited_fee.token_identifier.clone(); + let fee = self.fee(&fee_token).get(); 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.value -= &fee_cost; + deposited_fee.amount -= &fee_cost; - self.collected_fees() + self.collected_fees(&fee_token) .update(|collected_fees| *collected_fees += fee_cost); if deposit.egld_funds > 0 { @@ -74,8 +79,8 @@ pub trait SignatureOperationsModule: storage::StorageModule + helpers::HelpersMo self.send() .direct_multi(&caller_address, &deposit.esdt_funds); } - if deposit.fees.value > 0 { - self.send_fee_to_address(&deposit.fees.value, &deposit.depositor_address); + if deposited_fee.amount > 0 { + self.send_fee_to_address(&deposited_fee, &deposit.depositor_address); } } @@ -87,13 +92,14 @@ pub trait SignatureOperationsModule: storage::StorageModule + helpers::HelpersMo forward_address: ManagedAddress, signature: ManagedByteArray, ) { - let fee = self.call_value().egld_or_single_esdt(); + let paid_fee = self.call_value().egld_or_single_esdt(); let caller_address = self.blockchain().get_caller(); + let fee_token = paid_fee.token_identifier.clone(); self.require_signature(&address, &caller_address, signature); - self.update_fees(caller_address, &forward_address, fee); + self.update_fees(caller_address, &forward_address, paid_fee); let new_deposit = self.deposit(&forward_address); - let fee = self.fee().get(); + let fee = self.fee(&fee_token).get(); let mut current_deposit = self.deposit(&address).take(); let num_tokens = current_deposit.get_num_tokens(); @@ -103,7 +109,7 @@ pub trait SignatureOperationsModule: storage::StorageModule + helpers::HelpersMo "key already used" ); require!( - &fee * num_tokens as u64 <= fwd_deposit.fees.value, + &fee * num_tokens as u64 <= fwd_deposit.fees.value.amount, "cannot deposit funds without covering the fee cost first" ); @@ -115,12 +121,12 @@ pub trait SignatureOperationsModule: storage::StorageModule + helpers::HelpersMo }); let forward_fee = &fee * num_tokens as u64; - current_deposit.fees.value -= &forward_fee; + current_deposit.fees.value.amount -= &forward_fee; - self.collected_fees() + self.collected_fees(&fee_token) .update(|collected_fees| *collected_fees += forward_fee); - if current_deposit.fees.value > 0 { + if current_deposit.fees.value.amount > 0 { self.send_fee_to_address( ¤t_deposit.fees.value, ¤t_deposit.depositor_address, @@ -128,8 +134,6 @@ pub trait SignatureOperationsModule: storage::StorageModule + helpers::HelpersMo } } - fn make_forward(&self) {} - fn require_signature( &self, address: &ManagedAddress, diff --git a/contracts/examples/digital-cash/src/storage.rs b/contracts/examples/digital-cash/src/storage.rs index 497b7bd86d..eb0fbef93d 100644 --- a/contracts/examples/digital-cash/src/storage.rs +++ b/contracts/examples/digital-cash/src/storage.rs @@ -10,11 +10,14 @@ pub trait StorageModule { fn deposit(&self, donor: &ManagedAddress) -> SingleValueMapper>; #[storage_mapper("fee")] - fn fee(&self) -> SingleValueMapper; - - #[storage_mapper("feeToken")] - fn fee_token(&self) -> SingleValueMapper; + fn fee(&self, token: &EgldOrEsdtTokenIdentifier) -> SingleValueMapper; #[storage_mapper("collectedFees")] - fn collected_fees(&self) -> SingleValueMapper; + fn collected_fees(&self, token: &EgldOrEsdtTokenIdentifier) -> SingleValueMapper; + + #[storage_mapper("whitelistedFeeTokens")] + fn whitelisted_fee_tokens(&self) -> UnorderedSetMapper; + + #[storage_mapper("allTimeFeeTokens")] + fn all_time_fee_tokens(&self) -> UnorderedSetMapper; } From 2a23fc3ca918174da327e6cce194d242ecd18902 Mon Sep 17 00:00:00 2001 From: Alin Cruceat Date: Tue, 26 Sep 2023 14:16:39 +0300 Subject: [PATCH 14/20] wasm lib --- contracts/examples/digital-cash/wasm/src/lib.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/contracts/examples/digital-cash/wasm/src/lib.rs b/contracts/examples/digital-cash/wasm/src/lib.rs index aca6cd6109..7de921cd80 100644 --- a/contracts/examples/digital-cash/wasm/src/lib.rs +++ b/contracts/examples/digital-cash/wasm/src/lib.rs @@ -5,9 +5,9 @@ //////////////////////////////////////////////////// // Init: 1 -// Endpoints: 9 +// Endpoints: 12 // Async Callback (empty): 1 -// Total number of exported functions: 11 +// Total number of exported functions: 14 #![no_std] @@ -22,9 +22,12 @@ multiversx_sc_wasm_adapter::endpoints! { digital_cash ( init => init + whitelistFeeToken => whitelist_fee_token + blacklistFeeToken => blacklist_fee_token claimFees => claim_fees getAmount => get_amount - pay_fee_and_fund => pay_fee_and_fund + pay_fee_and_fund_esdt => pay_fee_and_fund_esdt + pay_fee_and_fund_egld => pay_fee_and_fund_egld fund => fund depositFees => deposit_fees withdraw => withdraw From c2da120acee99a668bd2a08a0d97d9214bf658b7 Mon Sep 17 00:00:00 2001 From: Alin Cruceat Date: Wed, 27 Sep 2023 01:59:35 +0300 Subject: [PATCH 15/20] don't send if payment is empty --- contracts/examples/digital-cash/src/digital_cash.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/contracts/examples/digital-cash/src/digital_cash.rs b/contracts/examples/digital-cash/src/digital_cash.rs index 031b0191e3..2333cbf2cc 100644 --- a/contracts/examples/digital-cash/src/digital_cash.rs +++ b/contracts/examples/digital-cash/src/digital_cash.rs @@ -61,8 +61,10 @@ pub trait DigitalCash: collected_esdt_fees.push(collected_fee); } } - self.send() - .direct_multi(&caller_address, &collected_esdt_fees); + if !collected_esdt_fees.is_empty() { + self.send() + .direct_multi(&caller_address, &collected_esdt_fees); + } } #[view(getAmount)] From 0b6cea1037b18b0bb0fb655987d7e534a6ab91b5 Mon Sep 17 00:00:00 2001 From: Alin Cruceat Date: Wed, 27 Sep 2023 04:49:20 +0300 Subject: [PATCH 16/20] tests whitelist / blacklist fee token --- .../whitelist-blacklist-fee-tokens.scen.json | 176 ++++++++++++++++++ .../examples/digital-cash/src/digital_cash.rs | 6 +- .../tests/digital_cash_scenario_go_test.rs | 5 + .../tests/digital_cash_scenario_rs_test.rs | 5 + 4 files changed, 189 insertions(+), 3 deletions(-) create mode 100644 contracts/examples/digital-cash/scenarios/whitelist-blacklist-fee-tokens.scen.json diff --git a/contracts/examples/digital-cash/scenarios/whitelist-blacklist-fee-tokens.scen.json b/contracts/examples/digital-cash/scenarios/whitelist-blacklist-fee-tokens.scen.json new file mode 100644 index 0000000000..8f698381ea --- /dev/null +++ b/contracts/examples/digital-cash/scenarios/whitelist-blacklist-fee-tokens.scen.json @@ -0,0 +1,176 @@ +{ + "name": "whitelist-blacklist-fee-token", + "steps": [ + { + "step": "externalSteps", + "path": "set-accounts.scen.json" + }, + { + "step": "scCall", + "id": "whitelist-fail", + "tx": { + "from": "address:digital_cash_owner_address", + "to": "sc:the_digital_cash_contract", + "function": "whitelistFeeToken", + "arguments": [ + "biguint:10", + "str:EGLD" + ], + "gasLimit": "500,000,000", + "gasPrice": "0" + }, + "expect": { + "out": [], + "status": "4", + "message": "str:Token already whitelisted", + "logs": "*", + "gas": "*", + "refund": "*" + } + }, + { + "step": "scCall", + "id": "whitelist-success-1", + "tx": { + "from": "address:digital_cash_owner_address", + "to": "sc:the_digital_cash_contract", + "function": "whitelistFeeToken", + "arguments": [ + "3", + "str:ESDT-123456" + ], + "gasLimit": "500,000,000", + "gasPrice": "0" + }, + "expect": { + "out": [], + "status": "0", + "message": "", + "logs": "*", + "gas": "*", + "refund": "*" + } + }, + { + "step": "scCall", + "id": "whitelist-success-2", + "tx": { + "from": "address:digital_cash_owner_address", + "to": "sc:the_digital_cash_contract", + "function": "whitelistFeeToken", + "arguments": [ + "5", + "str:ESDT-778899" + ], + "gasLimit": "500,000,000", + "gasPrice": "0" + }, + "expect": { + "out": [], + "status": "0", + "message": "", + "logs": "*", + "gas": "*", + "refund": "*" + } + }, + { + "step": "scCall", + "id": "blacklist-fail", + "tx": { + "from": "address:digital_cash_owner_address", + "to": "sc:the_digital_cash_contract", + "function": "blacklistFeeToken", + "arguments": [ + "str:ESDT-000000" + ], + "gasLimit": "500,000,000", + "gasPrice": "0" + }, + "expect": { + "out": [], + "status": "4", + "message": "str:Token is not whitelisted", + "logs": "*", + "gas": "*", + "refund": "*" + } + }, + { + "step": "scCall", + "id": "blacklist-success", + "tx": { + "from": "address:digital_cash_owner_address", + "to": "sc:the_digital_cash_contract", + "function": "blacklistFeeToken", + "arguments": [ + "str:ESDT-778899" + ], + "gasLimit": "500,000,000", + "gasPrice": "0" + }, + "expect": { + "out": [], + "status": "0", + "message": "", + "logs": "*", + "gas": "*", + "refund": "*" + } + }, + { + "step": "checkState", + "accounts": { + "sc:the_digital_cash_contract": { + "nonce": "0", + "balance": "0", + "storage": { + "str:fee|nested:str:EGLD": "10", + "str:fee|nested:str:ESDT-123456": "3", + "str:whitelistedFeeTokens|str:.len": "2", + "str:whitelistedFeeTokens|str:.item|u32:1": "str:EGLD", + "str:whitelistedFeeTokens|str:.item|u32:2": "str:ESDT-123456", + "str:whitelistedFeeTokens|str:.index|nested:str:EGLD": "1", + "str:whitelistedFeeTokens|str:.index|nested:str:ESDT-123456": "2", + "str:allTimeFeeTokens|str:.len": "3", + "str:allTimeFeeTokens|str:.item|u32:1": "str:EGLD", + "str:allTimeFeeTokens|str:.item|u32:2": "str:ESDT-123456", + "str:allTimeFeeTokens|str:.item|u32:3": "str:ESDT-778899", + "str:allTimeFeeTokens|str:.index|nested:str:EGLD": "1", + "str:allTimeFeeTokens|str:.index|nested:str:ESDT-123456": "2", + "str:allTimeFeeTokens|str:.index|nested:str:ESDT-778899": "3" + }, + "code": "file:../output/digital-cash.wasm" + }, + "address:acc1": { + "nonce": "0", + "balance": "1,000,000", + "storage": {} + }, + "address:acc2": { + "nonce": "0", + "balance": "1,000,000", + "esdt": { + "str:CASHTOKEN-123456": "100" + }, + "storage": {} + }, + "address:acc3": { + "nonce": "0", + "balance": "1,000,000", + "esdt": { + "str:CASHTOKEN-112233": "100", + "str:CASHTOKEN-445566": "100", + "str:CASHTOKEN-778899": "100" + }, + "storage": {} + }, + "address:digital_cash_owner_address": { + "nonce": "6", + "balance": "0", + "storage": {} + } + } + } + ] +} diff --git a/contracts/examples/digital-cash/src/digital_cash.rs b/contracts/examples/digital-cash/src/digital_cash.rs index 2333cbf2cc..c1d6ac4af2 100644 --- a/contracts/examples/digital-cash/src/digital_cash.rs +++ b/contracts/examples/digital-cash/src/digital_cash.rs @@ -22,14 +22,13 @@ pub trait DigitalCash: { #[init] fn init(&self, fee: BigUint, token: EgldOrEsdtTokenIdentifier) { - self.fee(&token).set(fee); - self.whitelisted_fee_tokens().insert(token.clone()); - self.all_time_fee_tokens().insert(token); + self.whitelist_fee_token(fee, token); } #[endpoint(whitelistFeeToken)] #[only_owner] fn whitelist_fee_token(&self, fee: BigUint, token: EgldOrEsdtTokenIdentifier) { + require!(self.fee(&token).is_empty(), "Token already whitelisted"); self.fee(&token).set(fee); self.whitelisted_fee_tokens().insert(token.clone()); self.all_time_fee_tokens().insert(token); @@ -38,6 +37,7 @@ pub trait DigitalCash: #[endpoint(blacklistFeeToken)] #[only_owner] fn blacklist_fee_token(&self, token: EgldOrEsdtTokenIdentifier) { + require!(!self.fee(&token).is_empty(), "Token is not whitelisted"); self.fee(&token).clear(); self.whitelisted_fee_tokens().swap_remove(&token); } diff --git a/contracts/examples/digital-cash/tests/digital_cash_scenario_go_test.rs b/contracts/examples/digital-cash/tests/digital_cash_scenario_go_test.rs index e2d4d11267..9dcba6c0ad 100644 --- a/contracts/examples/digital-cash/tests/digital_cash_scenario_go_test.rs +++ b/contracts/examples/digital-cash/tests/digital_cash_scenario_go_test.rs @@ -39,6 +39,11 @@ fn set_accounts_go() { world().run("scenarios/set-accounts.scen.json"); } +#[test] +fn whitelist_blacklist_fee_token_go() { + world().run("scenarios/whitelist-blacklist-fee-tokens.scen.json"); +} + #[test] fn withdraw_egld_go() { world().run("scenarios/withdraw-egld.scen.json"); diff --git a/contracts/examples/digital-cash/tests/digital_cash_scenario_rs_test.rs b/contracts/examples/digital-cash/tests/digital_cash_scenario_rs_test.rs index bc20caac7d..37ffa54815 100644 --- a/contracts/examples/digital-cash/tests/digital_cash_scenario_rs_test.rs +++ b/contracts/examples/digital-cash/tests/digital_cash_scenario_rs_test.rs @@ -46,6 +46,11 @@ fn set_accounts_rs() { world().run("scenarios/set-accounts.scen.json"); } +#[test] +fn whitelist_blacklist_fee_token_rs() { + world().run("scenarios/whitelist-blacklist-fee-tokens.scen.json"); +} + #[test] fn withdraw_egld_rs() { world().run("scenarios/withdraw-egld.scen.json"); From b4421b2512a855b050fed54ce087cacf94819b53 Mon Sep 17 00:00:00 2001 From: Alin Cruceat Date: Wed, 27 Sep 2023 05:19:40 +0300 Subject: [PATCH 17/20] pay fee and fund egld test --- .../scenarios/pay-fee-and-fund-egld.scen.json | 126 ++++++++++++++++++ .../digital-cash/src/pay_fee_and_fund.rs | 4 +- .../tests/digital_cash_scenario_rs_test.rs | 5 + .../examples/digital-cash/wasm/src/lib.rs | 4 +- 4 files changed, 135 insertions(+), 4 deletions(-) create mode 100644 contracts/examples/digital-cash/scenarios/pay-fee-and-fund-egld.scen.json diff --git a/contracts/examples/digital-cash/scenarios/pay-fee-and-fund-egld.scen.json b/contracts/examples/digital-cash/scenarios/pay-fee-and-fund-egld.scen.json new file mode 100644 index 0000000000..7f0e9f8e7d --- /dev/null +++ b/contracts/examples/digital-cash/scenarios/pay-fee-and-fund-egld.scen.json @@ -0,0 +1,126 @@ +{ + "name": "fund-egld-esdt", + "steps": [ + { + "step": "externalSteps", + "path": "whitelist-blacklist-fee-tokens.scen.json" + }, + { + "step": "scCall", + "id": "pay-fee-and-fund-fail", + "tx": { + "from": "address:acc1", + "to": "sc:the_digital_cash_contract", + "egldValue": "10", + "function": "payFeeAndFundEGLD", + "arguments": [ + "0xdb474a3a065d3f0c0a62ae680ef6435e48eb482899d2ae30ff7a3a4b0ef19c60", + "u64:100" + ], + "gasLimit": "500,000,000", + "gasPrice": "0" + }, + "expect": { + "out": [], + "status": "4", + "message": "str:payment not covering fees", + "logs": "*", + "gas": "*", + "refund": "*" + } + }, + { + "step": "scCall", + "id": "pay-fee-and-fund-success", + "tx": { + "from": "address:acc1", + "to": "sc:the_digital_cash_contract", + "egldValue": "1,000", + "function": "payFeeAndFundEGLD", + "arguments": [ + "0xdb474a3a065d3f0c0a62ae680ef6435e48eb482899d2ae30ff7a3a4b0ef19c60", + "u64:100" + ], + "gasLimit": "500,000,000", + "gasPrice": "0" + }, + "expect": { + "out": [], + "status": "0", + "message": "", + "logs": "*", + "gas": "*", + "refund": "*" + } + }, + { + "step": "checkState", + "accounts": { + "sc:the_digital_cash_contract": { + "nonce": "0", + "balance": "1,000", + "storage": { + "str:deposit|0xdb474a3a065d3f0c0a62ae680ef6435e48eb482899d2ae30ff7a3a4b0ef19c60": { + "0-depositor_address": "address:acc1", + "1-esdt_funds": "u32:0", + "2-egld_funds": "biguint:990", + "3-valability": "u64:100", + "4-expiration_round": "u64:16", + "5-fees": { + "0-num_token_to_transfer": "u32:1", + "1-value": { + "0-tokenIdentifier": "nested:str:EGLD", + "1-nonce": "u64:0", + "2-amount": "biguint:10" + } + } + }, + "str:fee|nested:str:EGLD": "10", + "str:fee|nested:str:ESDT-123456": "3", + "str:whitelistedFeeTokens|str:.len": "2", + "str:whitelistedFeeTokens|str:.item|u32:1": "str:EGLD", + "str:whitelistedFeeTokens|str:.item|u32:2": "str:ESDT-123456", + "str:whitelistedFeeTokens|str:.index|nested:str:EGLD": "1", + "str:whitelistedFeeTokens|str:.index|nested:str:ESDT-123456": "2", + "str:allTimeFeeTokens|str:.len": "3", + "str:allTimeFeeTokens|str:.item|u32:1": "str:EGLD", + "str:allTimeFeeTokens|str:.item|u32:2": "str:ESDT-123456", + "str:allTimeFeeTokens|str:.item|u32:3": "str:ESDT-778899", + "str:allTimeFeeTokens|str:.index|nested:str:EGLD": "1", + "str:allTimeFeeTokens|str:.index|nested:str:ESDT-123456": "2", + "str:allTimeFeeTokens|str:.index|nested:str:ESDT-778899": "3" + }, + "code": "file:../output/digital-cash.wasm" + }, + "address:acc1": { + "nonce": "2", + "balance": "999000", + "storage": {} + }, + "address:acc2": { + "nonce": "0", + "balance": "1,000,000", + "esdt": { + "str:CASHTOKEN-123456": "100" + }, + "storage": {} + }, + "address:acc3": { + "nonce": "0", + "balance": "1,000,000", + "esdt": { + "str:CASHTOKEN-112233": "100", + "str:CASHTOKEN-445566": "100", + "str:CASHTOKEN-778899": "100" + }, + "storage": {} + }, + "address:digital_cash_owner_address": { + "nonce": "6", + "balance": "0", + "storage": {} + } + } + } + ] +} diff --git a/contracts/examples/digital-cash/src/pay_fee_and_fund.rs b/contracts/examples/digital-cash/src/pay_fee_and_fund.rs index 1c87089b8a..246ffefb6b 100644 --- a/contracts/examples/digital-cash/src/pay_fee_and_fund.rs +++ b/contracts/examples/digital-cash/src/pay_fee_and_fund.rs @@ -5,7 +5,7 @@ use crate::{constants::*, helpers, storage}; #[multiversx_sc::module] pub trait PayFeeAndFund: storage::StorageModule + helpers::HelpersModule { - #[endpoint] + #[endpoint(payFeeAndFundESDT)] #[payable("*")] fn pay_fee_and_fund_esdt(&self, address: ManagedAddress, valability: u64) { let mut payments = self.call_value().all_esdt_transfers().clone_value(); @@ -17,7 +17,7 @@ pub trait PayFeeAndFund: storage::StorageModule + helpers::HelpersModule { self.make_fund(0u64.into(), payments, address, valability) } - #[endpoint] + #[endpoint(payFeeAndFundEGLD)] #[payable("EGLD")] fn pay_fee_and_fund_egld(&self, address: ManagedAddress, valability: u64) { let mut fund = self.call_value().egld_value().clone_value(); diff --git a/contracts/examples/digital-cash/tests/digital_cash_scenario_rs_test.rs b/contracts/examples/digital-cash/tests/digital_cash_scenario_rs_test.rs index 37ffa54815..126f252d15 100644 --- a/contracts/examples/digital-cash/tests/digital_cash_scenario_rs_test.rs +++ b/contracts/examples/digital-cash/tests/digital_cash_scenario_rs_test.rs @@ -51,6 +51,11 @@ fn whitelist_blacklist_fee_token_rs() { world().run("scenarios/whitelist-blacklist-fee-tokens.scen.json"); } +#[test] +fn pay_fee_and_fund_egld_rs() { + world().run("scenarios/pay-fee-and-fund-egld.scen.json"); +} + #[test] fn withdraw_egld_rs() { world().run("scenarios/withdraw-egld.scen.json"); diff --git a/contracts/examples/digital-cash/wasm/src/lib.rs b/contracts/examples/digital-cash/wasm/src/lib.rs index 7de921cd80..4225446734 100644 --- a/contracts/examples/digital-cash/wasm/src/lib.rs +++ b/contracts/examples/digital-cash/wasm/src/lib.rs @@ -26,8 +26,8 @@ multiversx_sc_wasm_adapter::endpoints! { blacklistFeeToken => blacklist_fee_token claimFees => claim_fees getAmount => get_amount - pay_fee_and_fund_esdt => pay_fee_and_fund_esdt - pay_fee_and_fund_egld => pay_fee_and_fund_egld + payFeeAndFundESDT => pay_fee_and_fund_esdt + payFeeAndFundEGLD => pay_fee_and_fund_egld fund => fund depositFees => deposit_fees withdraw => withdraw From 95c39d2a2a81506ed47bbdd4c91e91cc1ba93a2a Mon Sep 17 00:00:00 2001 From: Alin Cruceat Date: Wed, 27 Sep 2023 08:55:20 +0300 Subject: [PATCH 18/20] pay fee and fund esdt test --- .../scenarios/pay-fee-and-fund-egld.scen.json | 30 ++-- .../scenarios/pay-fee-and-fund-esdt.scen.json | 144 ++++++++++++++++++ .../whitelist-blacklist-fee-tokens.scen.json | 12 +- .../tests/digital_cash_scenario_go_test.rs | 10 ++ .../tests/digital_cash_scenario_rs_test.rs | 5 + 5 files changed, 180 insertions(+), 21 deletions(-) create mode 100644 contracts/examples/digital-cash/scenarios/pay-fee-and-fund-esdt.scen.json diff --git a/contracts/examples/digital-cash/scenarios/pay-fee-and-fund-egld.scen.json b/contracts/examples/digital-cash/scenarios/pay-fee-and-fund-egld.scen.json index 7f0e9f8e7d..83e5d386e5 100644 --- a/contracts/examples/digital-cash/scenarios/pay-fee-and-fund-egld.scen.json +++ b/contracts/examples/digital-cash/scenarios/pay-fee-and-fund-egld.scen.json @@ -1,5 +1,5 @@ { - "name": "fund-egld-esdt", + "name": "pay-fee-adn-fund-egld", "steps": [ { "step": "externalSteps", @@ -7,9 +7,9 @@ }, { "step": "scCall", - "id": "pay-fee-and-fund-fail", + "id": "pay-fee-and-fund-egld-fail", "tx": { - "from": "address:acc1", + "from": "address:acc3", "to": "sc:the_digital_cash_contract", "egldValue": "10", "function": "payFeeAndFundEGLD", @@ -31,9 +31,9 @@ }, { "step": "scCall", - "id": "pay-fee-and-fund-success", + "id": "pay-fee-and-fund-egld-success", "tx": { - "from": "address:acc1", + "from": "address:acc3", "to": "sc:the_digital_cash_contract", "egldValue": "1,000", "function": "payFeeAndFundEGLD", @@ -61,7 +61,7 @@ "balance": "1,000", "storage": { "str:deposit|0xdb474a3a065d3f0c0a62ae680ef6435e48eb482899d2ae30ff7a3a4b0ef19c60": { - "0-depositor_address": "address:acc1", + "0-depositor_address": "address:acc3", "1-esdt_funds": "u32:0", "2-egld_funds": "biguint:990", "3-valability": "u64:100", @@ -76,25 +76,25 @@ } }, "str:fee|nested:str:EGLD": "10", - "str:fee|nested:str:ESDT-123456": "3", + "str:fee|nested:str:CASHTOKEN-778899": "3", "str:whitelistedFeeTokens|str:.len": "2", "str:whitelistedFeeTokens|str:.item|u32:1": "str:EGLD", - "str:whitelistedFeeTokens|str:.item|u32:2": "str:ESDT-123456", + "str:whitelistedFeeTokens|str:.item|u32:2": "str:CASHTOKEN-778899", "str:whitelistedFeeTokens|str:.index|nested:str:EGLD": "1", - "str:whitelistedFeeTokens|str:.index|nested:str:ESDT-123456": "2", + "str:whitelistedFeeTokens|str:.index|nested:str:CASHTOKEN-778899": "2", "str:allTimeFeeTokens|str:.len": "3", "str:allTimeFeeTokens|str:.item|u32:1": "str:EGLD", - "str:allTimeFeeTokens|str:.item|u32:2": "str:ESDT-123456", + "str:allTimeFeeTokens|str:.item|u32:2": "str:CASHTOKEN-778899", "str:allTimeFeeTokens|str:.item|u32:3": "str:ESDT-778899", "str:allTimeFeeTokens|str:.index|nested:str:EGLD": "1", - "str:allTimeFeeTokens|str:.index|nested:str:ESDT-123456": "2", + "str:allTimeFeeTokens|str:.index|nested:str:CASHTOKEN-778899": "2", "str:allTimeFeeTokens|str:.index|nested:str:ESDT-778899": "3" }, "code": "file:../output/digital-cash.wasm" }, "address:acc1": { - "nonce": "2", - "balance": "999000", + "nonce": "0", + "balance": "1,000,000", "storage": {} }, "address:acc2": { @@ -106,8 +106,8 @@ "storage": {} }, "address:acc3": { - "nonce": "0", - "balance": "1,000,000", + "nonce": "2", + "balance": "999000", "esdt": { "str:CASHTOKEN-112233": "100", "str:CASHTOKEN-445566": "100", diff --git a/contracts/examples/digital-cash/scenarios/pay-fee-and-fund-esdt.scen.json b/contracts/examples/digital-cash/scenarios/pay-fee-and-fund-esdt.scen.json new file mode 100644 index 0000000000..35fb5e1859 --- /dev/null +++ b/contracts/examples/digital-cash/scenarios/pay-fee-and-fund-esdt.scen.json @@ -0,0 +1,144 @@ +{ + "name": "pay-fee-and-fund-esdt", + "steps": [ + { + "step": "externalSteps", + "path": "whitelist-blacklist-fee-tokens.scen.json" + }, + { + "step": "scCall", + "id": "pay-fee-and-fund-esdt-fail", + "tx": { + "from": "address:acc3", + "to": "sc:the_digital_cash_contract", + "function": "payFeeAndFundESDT", + "esdtValue": [ + { + "tokenIdentifier": "str:CASHTOKEN-445566", + "value": "50" + }, + { + "tokenIdentifier": "str:CASHTOKEN-112233", + "value": "50" + } + ], + "arguments": [ + "0xdb474a3a065d3f0c0a62ae680ef6435e48eb482899d2ae30ff7a3a4b0ef19c60", + "u64:100" + ], + "gasLimit": "500,000,000", + "gasPrice": "0" + }, + "expect": { + "out": [], + "status": "4", + "message": "str:invalid fee toke provided", + "logs": "*", + "gas": "*", + "refund": "*" + } + }, + { + "step": "scCall", + "id": "pay-fee-and-fund-esdt-success", + "tx": { + "from": "address:acc3", + "to": "sc:the_digital_cash_contract", + "function": "payFeeAndFundESDT", + "esdtValue": [ + { + "tokenIdentifier": "str:CASHTOKEN-777777", + "value": "50" + }, + { + "tokenIdentifier": "str:CASHTOKEN-112233", + "value": "50" + } + ], + "arguments": [ + "0xdb474a3a065d3f0c0a62ae680ef6435e48eb482899d2ae30ff7a3a4b0ef19c60", + "u64:100" + ], + "gasLimit": "500,000,000", + "gasPrice": "0" + }, + "expect": { + "out": [], + "status": "0", + "message": "", + "logs": "*", + "gas": "*", + "refund": "*" + } + }, + { + "step": "checkState", + "accounts": { + "sc:the_digital_cash_contract": { + "nonce": "0", + "balance": "0", + "storage": { + "str:deposit|0xdb474a3a065d3f0c0a62ae680ef6435e48eb482899d2ae30ff7a3a4b0ef19c60": { + "0-depositor_address": "address:acc1", + "1-esdt_funds": "u32:1|nested:str:CASHTOKEN-123456|u64:0|biguint:50", + "2-egld_funds": "biguint:0", + "3-valability": "u64:100", + "4-expiration_round": "u64:16", + "5-fees": { + "0-num_token_to_transfer": "u32:1", + "1-value": { + "0-tokenIdentifier": "nested:str:CASHTOKEN-777777", + "1-nonce": "u64:0", + "2-amount": "biguint:50" + } + } + }, + "str:fee|nested:str:EGLD": "10", + "str:fee|nested:str:CASHTOKEN-778899": "3", + "str:whitelistedFeeTokens|str:.len": "2", + "str:whitelistedFeeTokens|str:.item|u32:1": "str:EGLD", + "str:whitelistedFeeTokens|str:.item|u32:2": "str:CASHTOKEN-778899", + "str:whitelistedFeeTokens|str:.index|nested:str:EGLD": "1", + "str:whitelistedFeeTokens|str:.index|nested:str:CASHTOKEN-778899": "2", + "str:allTimeFeeTokens|str:.len": "3", + "str:allTimeFeeTokens|str:.item|u32:1": "str:EGLD", + "str:allTimeFeeTokens|str:.item|u32:2": "str:CASHTOKEN-778899", + "str:allTimeFeeTokens|str:.item|u32:3": "str:ESDT-778899", + "str:allTimeFeeTokens|str:.index|nested:str:EGLD": "1", + "str:allTimeFeeTokens|str:.index|nested:str:CASHTOKEN-778899": "2", + "str:allTimeFeeTokens|str:.index|nested:str:ESDT-778899": "3" + }, + "code": "file:../output/digital-cash.wasm" + }, + "address:acc1": { + "nonce": "1", + "balance": "1,000,000", + "storage": {} + }, + "address:acc2": { + "nonce": "0", + "balance": "1,000,000", + "esdt": { + "str:CASHTOKEN-123456": "100" + }, + "storage": {} + }, + "address:acc3": { + "nonce": "2", + "balance": "1,000,000", + "esdt": { + "str:CASHTOKEN-112233": "50", + "str:CASHTOKEN-445566": "100", + "str:CASHTOKEN-778899": "50" + }, + "storage": {} + }, + "address:digital_cash_owner_address": { + "nonce": "6", + "balance": "0", + "storage": {} + } + } + } + ] +} diff --git a/contracts/examples/digital-cash/scenarios/whitelist-blacklist-fee-tokens.scen.json b/contracts/examples/digital-cash/scenarios/whitelist-blacklist-fee-tokens.scen.json index 8f698381ea..bd9f1afd1a 100644 --- a/contracts/examples/digital-cash/scenarios/whitelist-blacklist-fee-tokens.scen.json +++ b/contracts/examples/digital-cash/scenarios/whitelist-blacklist-fee-tokens.scen.json @@ -37,7 +37,7 @@ "function": "whitelistFeeToken", "arguments": [ "3", - "str:ESDT-123456" + "str:CASHTOKEN-778899" ], "gasLimit": "500,000,000", "gasPrice": "0" @@ -126,18 +126,18 @@ "balance": "0", "storage": { "str:fee|nested:str:EGLD": "10", - "str:fee|nested:str:ESDT-123456": "3", + "str:fee|nested:str:CASHTOKEN-778899": "3", "str:whitelistedFeeTokens|str:.len": "2", "str:whitelistedFeeTokens|str:.item|u32:1": "str:EGLD", - "str:whitelistedFeeTokens|str:.item|u32:2": "str:ESDT-123456", + "str:whitelistedFeeTokens|str:.item|u32:2": "str:CASHTOKEN-778899", "str:whitelistedFeeTokens|str:.index|nested:str:EGLD": "1", - "str:whitelistedFeeTokens|str:.index|nested:str:ESDT-123456": "2", + "str:whitelistedFeeTokens|str:.index|nested:str:CASHTOKEN-778899": "2", "str:allTimeFeeTokens|str:.len": "3", "str:allTimeFeeTokens|str:.item|u32:1": "str:EGLD", - "str:allTimeFeeTokens|str:.item|u32:2": "str:ESDT-123456", + "str:allTimeFeeTokens|str:.item|u32:2": "str:CASHTOKEN-778899", "str:allTimeFeeTokens|str:.item|u32:3": "str:ESDT-778899", "str:allTimeFeeTokens|str:.index|nested:str:EGLD": "1", - "str:allTimeFeeTokens|str:.index|nested:str:ESDT-123456": "2", + "str:allTimeFeeTokens|str:.index|nested:str:CASHTOKEN-778899": "2", "str:allTimeFeeTokens|str:.index|nested:str:ESDT-778899": "3" }, "code": "file:../output/digital-cash.wasm" diff --git a/contracts/examples/digital-cash/tests/digital_cash_scenario_go_test.rs b/contracts/examples/digital-cash/tests/digital_cash_scenario_go_test.rs index 9dcba6c0ad..7d48e43564 100644 --- a/contracts/examples/digital-cash/tests/digital_cash_scenario_go_test.rs +++ b/contracts/examples/digital-cash/tests/digital_cash_scenario_go_test.rs @@ -44,6 +44,16 @@ fn whitelist_blacklist_fee_token_go() { world().run("scenarios/whitelist-blacklist-fee-tokens.scen.json"); } +#[test] +fn pay_fee_and_fund_esdt_go() { + world().run("scenarios/pay-fee-and-fund-esdt.scen.json"); +} + +#[test] +fn pay_fee_and_fund_egld_go() { + world().run("scenarios/pay-fee-and-fund-egld.scen.json"); +} + #[test] fn withdraw_egld_go() { world().run("scenarios/withdraw-egld.scen.json"); diff --git a/contracts/examples/digital-cash/tests/digital_cash_scenario_rs_test.rs b/contracts/examples/digital-cash/tests/digital_cash_scenario_rs_test.rs index 126f252d15..61ab7ff699 100644 --- a/contracts/examples/digital-cash/tests/digital_cash_scenario_rs_test.rs +++ b/contracts/examples/digital-cash/tests/digital_cash_scenario_rs_test.rs @@ -51,6 +51,11 @@ fn whitelist_blacklist_fee_token_rs() { world().run("scenarios/whitelist-blacklist-fee-tokens.scen.json"); } +#[test] +fn pay_fee_and_fund_esdt_rs() { + world().run("scenarios/pay-fee-and-fund-esdt.scen.json"); +} + #[test] fn pay_fee_and_fund_egld_rs() { world().run("scenarios/pay-fee-and-fund-egld.scen.json"); From 08056f95d558fd24519c8403233704c2b8fe9819 Mon Sep 17 00:00:00 2001 From: Alin Cruceat Date: Wed, 27 Sep 2023 09:28:44 +0300 Subject: [PATCH 19/20] fix test --- .../scenarios/pay-fee-and-fund-esdt.scen.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/contracts/examples/digital-cash/scenarios/pay-fee-and-fund-esdt.scen.json b/contracts/examples/digital-cash/scenarios/pay-fee-and-fund-esdt.scen.json index 35fb5e1859..8598d935a4 100644 --- a/contracts/examples/digital-cash/scenarios/pay-fee-and-fund-esdt.scen.json +++ b/contracts/examples/digital-cash/scenarios/pay-fee-and-fund-esdt.scen.json @@ -47,7 +47,7 @@ "function": "payFeeAndFundESDT", "esdtValue": [ { - "tokenIdentifier": "str:CASHTOKEN-777777", + "tokenIdentifier": "str:CASHTOKEN-778899", "value": "50" }, { @@ -79,15 +79,15 @@ "balance": "0", "storage": { "str:deposit|0xdb474a3a065d3f0c0a62ae680ef6435e48eb482899d2ae30ff7a3a4b0ef19c60": { - "0-depositor_address": "address:acc1", - "1-esdt_funds": "u32:1|nested:str:CASHTOKEN-123456|u64:0|biguint:50", + "0-depositor_address": "address:acc3", + "1-esdt_funds": "u32:1|nested:str:CASHTOKEN-112233|u64:0|biguint:50", "2-egld_funds": "biguint:0", "3-valability": "u64:100", "4-expiration_round": "u64:16", "5-fees": { "0-num_token_to_transfer": "u32:1", "1-value": { - "0-tokenIdentifier": "nested:str:CASHTOKEN-777777", + "0-tokenIdentifier": "nested:str:CASHTOKEN-778899", "1-nonce": "u64:0", "2-amount": "biguint:50" } @@ -111,7 +111,7 @@ "code": "file:../output/digital-cash.wasm" }, "address:acc1": { - "nonce": "1", + "nonce": "0", "balance": "1,000,000", "storage": {} }, From 7c7d3c912a09cda4dd5470eac8d8673903029047 Mon Sep 17 00:00:00 2001 From: Alin Cruceat Date: Wed, 27 Sep 2023 10:10:26 +0300 Subject: [PATCH 20/20] fix test balance esdt --- .../digital-cash/scenarios/pay-fee-and-fund-esdt.scen.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/contracts/examples/digital-cash/scenarios/pay-fee-and-fund-esdt.scen.json b/contracts/examples/digital-cash/scenarios/pay-fee-and-fund-esdt.scen.json index 8598d935a4..6848eb9cd1 100644 --- a/contracts/examples/digital-cash/scenarios/pay-fee-and-fund-esdt.scen.json +++ b/contracts/examples/digital-cash/scenarios/pay-fee-and-fund-esdt.scen.json @@ -77,6 +77,10 @@ "sc:the_digital_cash_contract": { "nonce": "0", "balance": "0", + "esdt": { + "str:CASHTOKEN-112233": "50", + "str:CASHTOKEN-778899": "50" + }, "storage": { "str:deposit|0xdb474a3a065d3f0c0a62ae680ef6435e48eb482899d2ae30ff7a3a4b0ef19c60": { "0-depositor_address": "address:acc3",