From 0d313655bca08a29359a756965aec19b02a3157a Mon Sep 17 00:00:00 2001 From: Bucur David Date: Thu, 30 May 2024 21:27:14 +0300 Subject: [PATCH 1/8] feat: fee collector + unwrap fee --- src/admin.rs | 20 ++++++ src/config.rs | 9 +++ src/events.rs | 9 +++ src/lib.rs | 122 +++++++++++++++++++++++++-------- src/proxies/mod.rs | 1 + src/proxies/wegld_proxy.rs | 129 +++++++++++++++++++++++++++++++++++ src/storage.rs | 12 ++++ tests/bridge_sc/bridge_sc.rs | 47 +++++++++++++ tests/unit_tests.rs | 23 +++++++ 9 files changed, 345 insertions(+), 27 deletions(-) create mode 100644 src/proxies/mod.rs create mode 100644 src/proxies/wegld_proxy.rs diff --git a/src/admin.rs b/src/admin.rs index b1068f5..548d4b4 100644 --- a/src/admin.rs +++ b/src/admin.rs @@ -86,6 +86,26 @@ pub trait AdminModule: self.maximum_deposit(&token_identifier).set(maximum); } + #[endpoint(setFeeCollector)] + fn set_fee_collector(&self, fee_collector: ManagedAddress) { + only_privileged!(self, ERR_NOT_PRIVILEGED); + self.set_fee_collector_event(&fee_collector); + self.fee_colector().set(fee_collector); + } + + #[endpoint(setFeeValue)] + fn set_fee_value(&self, fee_value: BigUint) { + only_privileged!(self, ERR_NOT_PRIVILEGED); + self.fee_value().set(fee_value); + } + + #[endpoint(setWegldContractAddress)] + fn set_wegld_contract_address(&self, wegld_contract_address: ManagedAddress) { + only_privileged!(self, ERR_NOT_PRIVILEGED); + self.set_wegld_contract_address_event(&wegld_contract_address); + self.wegld_contract_address().set(wegld_contract_address); + } + #[endpoint(addTokensToWhitelist)] fn add_tokens_to_whitelist( &self, diff --git a/src/config.rs b/src/config.rs index c19ea6c..cf204b5 100644 --- a/src/config.rs +++ b/src/config.rs @@ -82,6 +82,15 @@ pub trait ConfigModule: storage::StorageModule + events::EventsModule { if self.administrator().is_empty() { is_ready = false; } + + if self.fee_colector().is_empty() { + is_ready = false; + } + + if self.wegld_contract_address().is_empty() { + is_ready = false; + } + if self.relayer().is_empty() { is_ready = false; } diff --git a/src/events.rs b/src/events.rs index 2282145..6d5222e 100644 --- a/src/events.rs +++ b/src/events.rs @@ -64,4 +64,13 @@ pub trait EventsModule { #[indexed] minimum: &BigUint, #[indexed] maximum: &BigUint, ); + + #[event("setFeeCollectorEvent")] + fn set_fee_collector_event(&self, #[indexed] fee_collector: &ManagedAddress); + + #[event("setFeeValueEvent")] + fn set_fee_value_event(&self, #[indexed] fee_value: &BigUint); + + #[event("setWegldContractAddressEvent")] + fn set_wegld_contract_address_event(&self, #[indexed] wegld_contract_address: &ManagedAddress); } diff --git a/src/lib.rs b/src/lib.rs index c2fdd93..b33e40a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,9 +3,11 @@ use crate::errors::{ ERR_ADDRESS_NOT_WHITELISTED, ERR_CONTRACT_NOT_READY, ERR_NOT_ENOUGH_LIQUIDITY, ERR_NOT_PRIVILEGED, ERR_NOT_WHOLE_NUMBER, ERR_PAYMENT_AMOUNT_NOT_IN_ACCEPTED_RANGE, - ERR_TOKEN_NOT_WHITELISTED, + ERR_TOKEN_NOT_WHITELISTED, ERR_WRONG_VALUES, }; +use crate::proxies::wegld_proxy; + multiversx_sc::imports!(); pub mod admin; @@ -13,6 +15,7 @@ pub mod config; pub mod errors; pub mod events; pub mod macros; +pub mod proxies; pub mod storage; pub mod utils; #[multiversx_sc::contract] @@ -41,37 +44,102 @@ pub trait CoreMxBridgeSc: let caller = self.blockchain().get_caller(); require_contract_ready!(self, ERR_CONTRACT_NOT_READY); check_whitelist!(self, &caller, ERR_ADDRESS_NOT_WHITELISTED); - let payment = self.call_value().single_esdt(); - require!( - self.tokens_whitelist().contains(&payment.token_identifier), - ERR_TOKEN_NOT_WHITELISTED - ); + let fee_value = self.fee_value().get(); - require!( - self.check_amount( - &payment.amount, - self.token_decimals(&payment.token_identifier).get() - ), - ERR_NOT_WHOLE_NUMBER - ); + if fee_value == BigUint::zero() { + let deposit = self.call_value().single_esdt(); - require!( - self.minimum_deposit(&payment.token_identifier).get() <= payment.amount - && payment.amount <= self.maximum_deposit(&payment.token_identifier).get(), - ERR_PAYMENT_AMOUNT_NOT_IN_ACCEPTED_RANGE - ); + require!( + self.tokens_whitelist().contains(&deposit.token_identifier), + ERR_TOKEN_NOT_WHITELISTED + ); - self.send_to_liquidity_event( - &payment.token_identifier, - &payment.amount, - &caller, - &destination_address, - &destination_signature, - ); + require!( + self.check_amount( + &deposit.amount, + self.token_decimals(&deposit.token_identifier).get() + ), + ERR_NOT_WHOLE_NUMBER + ); + + require!( + self.minimum_deposit(&deposit.token_identifier).get() <= deposit.amount + && deposit.amount <= self.maximum_deposit(&deposit.token_identifier).get(), + ERR_PAYMENT_AMOUNT_NOT_IN_ACCEPTED_RANGE + ); + + self.send_to_liquidity_event( + &deposit.token_identifier, + &deposit.amount, + &caller, + &destination_address, + &destination_signature, + ); + + self.liquidity(&deposit.token_identifier) + .update(|value| *value += deposit.amount); + } else { + let [deposit, fee] = self.call_value().multi_esdt(); + + require!(self.fee_value().get() == fee.amount, ERR_WRONG_VALUES); + + let wegld_token_identifier = self + .tx() + .to(&self.wegld_contract_address().get()) + .typed(wegld_proxy::EgldEsdtSwapProxy) + .wrapped_egld_token_id() + .returns(ReturnsResult) + .sync_call(); + + require!( + fee.token_identifier == wegld_token_identifier, + ERR_TOKEN_NOT_WHITELISTED + ); + + let back_transfers = self + .tx() + .to(&self.wegld_contract_address().get()) + .typed(wegld_proxy::EgldEsdtSwapProxy) + .unwrap_egld() + .returns(ReturnsBackTransfers) + .sync_call(); + + self.send().direct_egld( + &self.fee_colector().get(), + &back_transfers.total_egld_amount, + ); + + require!( + self.tokens_whitelist().contains(&deposit.token_identifier), + ERR_TOKEN_NOT_WHITELISTED + ); + + require!( + self.check_amount( + &deposit.amount, + self.token_decimals(&deposit.token_identifier).get() + ), + ERR_NOT_WHOLE_NUMBER + ); + + require!( + self.minimum_deposit(&deposit.token_identifier).get() <= deposit.amount + && deposit.amount <= self.maximum_deposit(&deposit.token_identifier).get(), + ERR_PAYMENT_AMOUNT_NOT_IN_ACCEPTED_RANGE + ); + + self.send_to_liquidity_event( + &deposit.token_identifier, + &deposit.amount, + &caller, + &destination_address, + &destination_signature, + ); - self.liquidity(&payment.token_identifier) - .update(|value| *value += payment.amount); + self.liquidity(&deposit.token_identifier) + .update(|value| *value += deposit.amount); + } } #[endpoint(sendFromLiquidity)] diff --git a/src/proxies/mod.rs b/src/proxies/mod.rs new file mode 100644 index 0000000..74f14be --- /dev/null +++ b/src/proxies/mod.rs @@ -0,0 +1 @@ +pub mod wegld_proxy; diff --git a/src/proxies/wegld_proxy.rs b/src/proxies/wegld_proxy.rs new file mode 100644 index 0000000..404831a --- /dev/null +++ b/src/proxies/wegld_proxy.rs @@ -0,0 +1,129 @@ +// Code generated by the multiversx-sc proxy generator. DO NOT EDIT. + +//////////////////////////////////////////////////// +////////////////// AUTO-GENERATED ////////////////// +//////////////////////////////////////////////////// + +#![allow(dead_code)] +#![allow(clippy::all)] + +use multiversx_sc::proxy_imports::*; + +pub struct EgldEsdtSwapProxy; + +impl TxProxyTrait for EgldEsdtSwapProxy +where + Env: TxEnv, + From: TxFrom, + To: TxTo, + Gas: TxGas, +{ + type TxProxyMethods = EgldEsdtSwapProxyMethods; + + fn proxy_methods(self, tx: Tx) -> Self::TxProxyMethods { + EgldEsdtSwapProxyMethods { wrapped_tx: tx } + } +} + +pub struct EgldEsdtSwapProxyMethods +where + Env: TxEnv, + From: TxFrom, + To: TxTo, + Gas: TxGas, +{ + wrapped_tx: Tx, +} + +#[rustfmt::skip] +impl EgldEsdtSwapProxyMethods +where + Env: TxEnv, + Env::Api: VMApi, + From: TxFrom, + Gas: TxGas, +{ + pub fn init< + Arg0: ProxyArg>, + >( + self, + wrapped_egld_token_id: Arg0, + ) -> TxTypedDeploy { + self.wrapped_tx + .payment(NotPayable) + .raw_deploy() + .argument(&wrapped_egld_token_id) + .original_result() + } +} + +#[rustfmt::skip] +impl EgldEsdtSwapProxyMethods +where + Env: TxEnv, + Env::Api: VMApi, + From: TxFrom, + To: TxTo, + Gas: TxGas, +{ + pub fn wrap_egld( + self, + ) -> TxTypedCall> { + self.wrapped_tx + .raw_call("wrapEgld") + .original_result() + } + + pub fn unwrap_egld( + self, + ) -> TxTypedCall { + self.wrapped_tx + .raw_call("unwrapEgld") + .original_result() + } + + pub fn get_locked_egld_balance( + self, + ) -> TxTypedCall> { + self.wrapped_tx + .payment(NotPayable) + .raw_call("getLockedEgldBalance") + .original_result() + } + + pub fn wrapped_egld_token_id( + self, + ) -> TxTypedCall> { + self.wrapped_tx + .payment(NotPayable) + .raw_call("getWrappedEgldTokenId") + .original_result() + } + + pub fn pause_endpoint( + self, + ) -> TxTypedCall { + self.wrapped_tx + .payment(NotPayable) + .raw_call("pause") + .original_result() + } + + pub fn unpause_endpoint( + self, + ) -> TxTypedCall { + self.wrapped_tx + .payment(NotPayable) + .raw_call("unpause") + .original_result() + } + + pub fn paused_status( + self, + ) -> TxTypedCall { + self.wrapped_tx + .payment(NotPayable) + .raw_call("isPaused") + .original_result() + } +} diff --git a/src/storage.rs b/src/storage.rs index aded34d..33aa769 100644 --- a/src/storage.rs +++ b/src/storage.rs @@ -18,4 +18,16 @@ pub trait StorageModule { #[view(getMaximumDeposit)] #[storage_mapper("maximum_deposit")] fn maximum_deposit(&self, token_identifier: &TokenIdentifier) -> SingleValueMapper; + + #[view(getFeeCollector)] + #[storage_mapper("fee_colector")] + fn fee_colector(&self) -> SingleValueMapper; + + #[view(getFeeValue)] + #[storage_mapper("fee_value")] + fn fee_value(&self) -> SingleValueMapper; + + #[view(getWegldContractAddress)] + #[storage_mapper("wegld_contract_address")] + fn wegld_contract_address(&self) -> SingleValueMapper; } diff --git a/tests/bridge_sc/bridge_sc.rs b/tests/bridge_sc/bridge_sc.rs index 359d707..714ec47 100644 --- a/tests/bridge_sc/bridge_sc.rs +++ b/tests/bridge_sc/bridge_sc.rs @@ -139,6 +139,16 @@ impl ContractState { OWNER_BRIDGE_CONTRACT_ADDRESS_EXPR, AddressValue::from(RELAYER_BRIDGE_CONTRACT_ADDRESS_EXPR).to_address(), None, + ) + .set_fee_collector( + OWNER_BRIDGE_CONTRACT_ADDRESS_EXPR, + AddressValue::from(RELAYER_BRIDGE_CONTRACT_ADDRESS_EXPR).to_address(), + None, + ) + .set_wegld_contract_address( + OWNER_BRIDGE_CONTRACT_ADDRESS_EXPR, + AddressValue::from(RELAYER_BRIDGE_CONTRACT_ADDRESS_EXPR).to_address(), + None, ); self @@ -320,6 +330,43 @@ impl ContractState { self } + pub fn set_fee_collector( + &mut self, + caller: &str, + fee_collector: Address, + expect: Option, + ) -> &mut Self { + let tx_expect = expect.unwrap_or(TxExpect::ok()); + + self.world.sc_call( + ScCallStep::new() + .from(caller) + .call(self.contract.set_fee_collector(fee_collector)) + .expect(tx_expect), + ); + self + } + + pub fn set_wegld_contract_address( + &mut self, + caller: &str, + wegld_contract_address: Address, + expect: Option, + ) -> &mut Self { + let tx_expect = expect.unwrap_or(TxExpect::ok()); + + self.world.sc_call( + ScCallStep::new() + .from(caller) + .call( + self.contract + .set_wegld_contract_address(wegld_contract_address), + ) + .expect(tx_expect), + ); + self + } + pub fn add_token_to_whitelist( &mut self, caller: &str, diff --git a/tests/unit_tests.rs b/tests/unit_tests.rs index fbc15bb..9eb2723 100644 --- a/tests/unit_tests.rs +++ b/tests/unit_tests.rs @@ -3,6 +3,7 @@ use bridge_sc::bridge_sc::{ }; use core_mx_bridge_sc::{ config::{ConfigModule, State}, + storage::StorageModule, utils::UtilsModule, }; use multiversx_sc::types::{BigUint, TokenIdentifier}; @@ -84,6 +85,28 @@ fn contract_is_ready_test() { check = bridge_sc.contract_is_ready(); + assert_eq!(check, false); + + bridge_sc + .fee_colector() + .set(managed_address!(&AddressValue::from( + ADMIN_BRIDGE_CONTRACT_ADDRESS_EXPR + ) + .to_address())); + + check = bridge_sc.contract_is_ready(); + + assert_eq!(check, false); + + bridge_sc + .wegld_contract_address() + .set(managed_address!(&AddressValue::from( + ADMIN_BRIDGE_CONTRACT_ADDRESS_EXPR + ) + .to_address())); + + check = bridge_sc.contract_is_ready(); + assert_eq!(check, true); } From dac7e70c68a3e1644da069727052d4d9ec31b872 Mon Sep 17 00:00:00 2001 From: Bucur David Date: Thu, 30 May 2024 21:29:26 +0300 Subject: [PATCH 2/8] fix: send fee after all checks --- src/lib.rs | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index b33e40a..7be5e61 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -97,19 +97,6 @@ pub trait CoreMxBridgeSc: ERR_TOKEN_NOT_WHITELISTED ); - let back_transfers = self - .tx() - .to(&self.wegld_contract_address().get()) - .typed(wegld_proxy::EgldEsdtSwapProxy) - .unwrap_egld() - .returns(ReturnsBackTransfers) - .sync_call(); - - self.send().direct_egld( - &self.fee_colector().get(), - &back_transfers.total_egld_amount, - ); - require!( self.tokens_whitelist().contains(&deposit.token_identifier), ERR_TOKEN_NOT_WHITELISTED @@ -129,6 +116,19 @@ pub trait CoreMxBridgeSc: ERR_PAYMENT_AMOUNT_NOT_IN_ACCEPTED_RANGE ); + let back_transfers = self + .tx() + .to(&self.wegld_contract_address().get()) + .typed(wegld_proxy::EgldEsdtSwapProxy) + .unwrap_egld() + .returns(ReturnsBackTransfers) + .sync_call(); + + self.send().direct_egld( + &self.fee_colector().get(), + &back_transfers.total_egld_amount, + ); + self.send_to_liquidity_event( &deposit.token_identifier, &deposit.amount, From 6a9f10e9672b3f98a76aa9f2c2f3a43c09becf6c Mon Sep 17 00:00:00 2001 From: Bucur David Date: Tue, 4 Jun 2024 13:38:07 +0300 Subject: [PATCH 3/8] docs: updated README.md --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 2341038..bbe6ce7 100644 --- a/README.md +++ b/README.md @@ -22,15 +22,15 @@ See `devnet.snippets.sh` for list of available endpoints for user testing. ### Setting up dev environment (project development bootstrap) + how to build (and upgrade) -- Uses `multiversx-sc-* 0.49.0` (In v2.0.0, we used0.49.0) SDK libs (see Cargo.toml) +- Uses `multiversx-sc-* 0.50.3` (In v2.0.0, we used0.50.3) SDK libs (see Cargo.toml) - Building requires minimum **mxpy 9.5.1** (In v2.0.0, we used mxpy 9.5.1). Check version using `mxpy --version` -- To build the project, requires minimum Rust version `1.78.0-nightly` (In v2.0.0, we used 1.78.0-nightly). Check your Rust version by running `rustc --version`. To update your Rust, run `rustup update`. To set to nightly run `rustup default nightly`. Note that `mxpy deps install rust --overwrite` also brings in it's own compatible rust version so running `rustup default nightly` might have a higher rust version than what is used via `mxpy deps install rust --overwrite`. -- Uses `multiversx-sc-*0.49.0` (In v1.0.0, we used0.49.0) SDK libs (see Cargo.toml) +- To build the project, requires minimum Rust version `1.78.0` (In v2.0.0, we used 1.78.0). Check your Rust version by running `rustc --version`. To update your Rust, run `rustup update`. To set to nightly run `rustup default stable`. Note that `mxpy deps install rust --overwrite` also brings in it's own compatible rust version so running `rustup default stable` might have a higher rust version than what is used via `mxpy deps install rust --overwrite`. +- Uses `multiversx-sc-*0.50.3` (In v1.0.0, we used0.50.3) SDK libs (see Cargo.toml) - Building requires minimum **mxpy 9.5.1** (In v1.0.0, we used mxpy 9.5.1). Check version using `mxpy --version` -- To build the project, requires minimum Rust version `1.78.0-nightly` (In v1.0.0, we used 1.78.0-nightly). Check your Rust version by running `rustc --version`. To update your Rust, run `rustup update`. To set to nightly run `rustup default nightly`. Note that `mxpy deps install rust --overwrite` also brings in it's own compatible rust version so running `rustup default nightly` might have a higher rust version than what is used via `mxpy deps install rust --overwrite`. +- To build the project, requires minimum Rust version `1.78.0` (In v1.0.0, we used 1.78.0-nightly). Check your Rust version by running `rustc --version`. To update your Rust, run `rustup update`. To set to nightly run `rustup default stable`. Note that `mxpy deps install rust --overwrite` also brings in it's own compatible rust version so running `rustup default nightly` might have a higher rust version than what is used via `mxpy deps install rust --overwrite`. ``` -rustup default nightly +rustup default stable mxpy deps install rust --overwrite cargo clean cargo build @@ -58,7 +58,7 @@ Another way of running the tests is by using the rust-analyzer extension in Visu Note: In order to run the tests, one has to use the rust nightly version. One can switch to the nightly version by using: ```shell - rustup default nightly + rustup default stable ``` ### How to deploy From bf82074152bddbd3e60346024e33a104ee4c5eec Mon Sep 17 00:00:00 2001 From: Bucur David Date: Tue, 4 Jun 2024 15:10:47 +0300 Subject: [PATCH 4/8] test: unwrap wegld fee integration tests --- Cargo.lock | 22 ++- Cargo.toml | 7 +- coverage.sh | 2 +- src/admin.rs | 2 +- src/config.rs | 2 +- src/errors.rs | 1 + src/lib.rs | 9 +- src/storage.rs | 4 +- .../multiversx-wegld-swap-sc.mxsc.json | 167 ++++++++++++++++++ tests/bridge_sc/bridge_sc.rs | 142 +++++++++++++-- tests/endpoints/public.rs | 127 ++++++++++++- tests/unit_tests.rs | 2 +- wasm/src/lib.rs | 2 +- 13 files changed, 458 insertions(+), 31 deletions(-) create mode 100644 tests-contracts/multiversx-wegld-swap-sc.mxsc.json diff --git a/Cargo.lock b/Cargo.lock index d18cd72..4ad994d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -346,8 +346,9 @@ name = "core-mx-bridge-sc" version = "0.1.0" dependencies = [ "multiversx-sc", + "multiversx-sc-modules", "multiversx-sc-scenario", - "num-bigint", + "multiversx-wegld-swap-sc", ] [[package]] @@ -1099,6 +1100,15 @@ dependencies = [ "zip", ] +[[package]] +name = "multiversx-sc-modules" +version = "0.50.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f9a8c10ddadc7adfc04edbdd1ea80a9ae9a7e11202fc4cfb2e08c8e27f17aa5" +dependencies = [ + "multiversx-sc", +] + [[package]] name = "multiversx-sc-scenario" version = "0.50.3" @@ -1154,6 +1164,16 @@ dependencies = [ "zeroize", ] +[[package]] +name = "multiversx-wegld-swap-sc" +version = "0.50.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72d3aa3ee209e07f3562407530a636080437f348811961ab3127cf646a1e32ce" +dependencies = [ + "multiversx-sc", + "multiversx-sc-modules", +] + [[package]] name = "native-tls" version = "0.2.11" diff --git a/Cargo.toml b/Cargo.toml index fd0c8af..c377e1c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,13 +11,18 @@ path = "src/lib.rs" [dependencies.multiversx-sc] version = "0.50.3" +[dependencies.multiversx-sc-modules] +version = "0.50.3" [dev-dependencies] -num-bigint = "0.4" +multiversx-wegld-swap-sc = "0.50.3" + [dev-dependencies.multiversx-sc-scenario] version = "0.50.3" + + [workspace] members = [ ".", diff --git a/coverage.sh b/coverage.sh index 4ac66d7..209afd7 100644 --- a/coverage.sh +++ b/coverage.sh @@ -1,3 +1,3 @@ #!/bin/bash -cargo llvm-cov --ignore-filename-regex '(storage.rs|events.rs|macros|errors.rs)' --open \ No newline at end of file +cargo llvm-cov --ignore-filename-regex '(storage.rs|events.rs|macros|errors.rs|proxies)' --open \ No newline at end of file diff --git a/src/admin.rs b/src/admin.rs index 548d4b4..84bad01 100644 --- a/src/admin.rs +++ b/src/admin.rs @@ -90,7 +90,7 @@ pub trait AdminModule: fn set_fee_collector(&self, fee_collector: ManagedAddress) { only_privileged!(self, ERR_NOT_PRIVILEGED); self.set_fee_collector_event(&fee_collector); - self.fee_colector().set(fee_collector); + self.fee_collector().set(fee_collector); } #[endpoint(setFeeValue)] diff --git a/src/config.rs b/src/config.rs index cf204b5..5b4af27 100644 --- a/src/config.rs +++ b/src/config.rs @@ -83,7 +83,7 @@ pub trait ConfigModule: storage::StorageModule + events::EventsModule { is_ready = false; } - if self.fee_colector().is_empty() { + if self.fee_collector().is_empty() { is_ready = false; } diff --git a/src/errors.rs b/src/errors.rs index 2f022be..8c344b8 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -10,3 +10,4 @@ pub const ERR_NOT_WHOLE_NUMBER: &str = "Not a whole number"; pub const ERR_WRONG_VALUES: &str = "Wrong values"; pub const ERR_ADDRESS_NOT_WHITELISTED: &str = "Address not whitelisted"; pub const ERR_ADDRESS_ALREADY_WHITELISTED: &str = "Address already whitelisted"; +pub const ERR_WRONG_FEE_TOKEN_IDENTIFIER: &str = "Wrong fee token identifier"; diff --git a/src/lib.rs b/src/lib.rs index 7be5e61..ec58376 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,7 +3,7 @@ use crate::errors::{ ERR_ADDRESS_NOT_WHITELISTED, ERR_CONTRACT_NOT_READY, ERR_NOT_ENOUGH_LIQUIDITY, ERR_NOT_PRIVILEGED, ERR_NOT_WHOLE_NUMBER, ERR_PAYMENT_AMOUNT_NOT_IN_ACCEPTED_RANGE, - ERR_TOKEN_NOT_WHITELISTED, ERR_WRONG_VALUES, + ERR_TOKEN_NOT_WHITELISTED, ERR_WRONG_FEE_TOKEN_IDENTIFIER, ERR_WRONG_VALUES, }; use crate::proxies::wegld_proxy; @@ -82,7 +82,7 @@ pub trait CoreMxBridgeSc: } else { let [deposit, fee] = self.call_value().multi_esdt(); - require!(self.fee_value().get() == fee.amount, ERR_WRONG_VALUES); + require!(fee_value == fee.amount, ERR_WRONG_VALUES); let wegld_token_identifier = self .tx() @@ -94,7 +94,7 @@ pub trait CoreMxBridgeSc: require!( fee.token_identifier == wegld_token_identifier, - ERR_TOKEN_NOT_WHITELISTED + ERR_WRONG_FEE_TOKEN_IDENTIFIER ); require!( @@ -121,11 +121,12 @@ pub trait CoreMxBridgeSc: .to(&self.wegld_contract_address().get()) .typed(wegld_proxy::EgldEsdtSwapProxy) .unwrap_egld() + .with_esdt_transfer(fee) .returns(ReturnsBackTransfers) .sync_call(); self.send().direct_egld( - &self.fee_colector().get(), + &self.fee_collector().get(), &back_transfers.total_egld_amount, ); diff --git a/src/storage.rs b/src/storage.rs index 33aa769..85e5b8b 100644 --- a/src/storage.rs +++ b/src/storage.rs @@ -20,8 +20,8 @@ pub trait StorageModule { fn maximum_deposit(&self, token_identifier: &TokenIdentifier) -> SingleValueMapper; #[view(getFeeCollector)] - #[storage_mapper("fee_colector")] - fn fee_colector(&self) -> SingleValueMapper; + #[storage_mapper("fee_collector")] + fn fee_collector(&self) -> SingleValueMapper; #[view(getFeeValue)] #[storage_mapper("fee_value")] diff --git a/tests-contracts/multiversx-wegld-swap-sc.mxsc.json b/tests-contracts/multiversx-wegld-swap-sc.mxsc.json new file mode 100644 index 0000000..8de68d9 --- /dev/null +++ b/tests-contracts/multiversx-wegld-swap-sc.mxsc.json @@ -0,0 +1,167 @@ +{ + "buildInfo": { + "rustc": { + "version": "1.78.0", + "commitHash": "9b00956e56009bab2aa15d7bff10916599e3d6d6", + "commitDate": "2024-04-29", + "channel": "Stable", + "short": "rustc 1.78.0 (9b00956e5 2024-04-29)" + }, + "contractCrate": { + "name": "multiversx-wegld-swap-sc", + "version": "0.50.3" + }, + "framework": { + "name": "multiversx-sc", + "version": "0.50.3" + } + }, + "abi": { + "name": "EgldEsdtSwap", + "constructor": { + "inputs": [ + { + "name": "wrapped_egld_token_id", + "type": "TokenIdentifier" + } + ], + "outputs": [] + }, + "endpoints": [ + { + "name": "wrapEgld", + "mutability": "mutable", + "payableInTokens": [ + "EGLD" + ], + "inputs": [], + "outputs": [ + { + "type": "EsdtTokenPayment" + } + ] + }, + { + "name": "unwrapEgld", + "mutability": "mutable", + "payableInTokens": [ + "*" + ], + "inputs": [], + "outputs": [] + }, + { + "name": "getLockedEgldBalance", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "BigUint" + } + ] + }, + { + "name": "getWrappedEgldTokenId", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "TokenIdentifier" + } + ] + }, + { + "name": "pause", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [], + "outputs": [] + }, + { + "name": "unpause", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [], + "outputs": [] + }, + { + "name": "isPaused", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "bool" + } + ] + } + ], + "esdtAttributes": [], + "hasCallback": false, + "types": { + "EsdtTokenPayment": { + "type": "struct", + "fields": [ + { + "name": "token_identifier", + "type": "TokenIdentifier" + }, + { + "name": "token_nonce", + "type": "u64" + }, + { + "name": "amount", + "type": "BigUint" + } + ] + } + } + }, + "size": 3528, + "code": "0061736d0100000001580f60000060027f7f017f60017f006000017f60017f017f60027f7f0060037f7f7f017f60057f7f7e7f7f017f60047f7f7f7f0060027f7e0060037f7f7f006000017e60067e7f7f7f7f7f017f60047f7f7f7f017f60017e0002cc062303656e760e626967496e74536574496e743634000903656e7609626967496e74416464000a03656e760b7369676e616c4572726f72000503656e760a6d4275666665724e6577000303656e760d6d427566666572417070656e64000103656e76096d4275666665724571000103656e760d6d616e6167656443616c6c6572000203656e76136d616e616765644f776e657241646472657373000203656e76126d427566666572476574417267756d656e74000103656e760f6765744e756d417267756d656e7473000303656e760a6765744761734c656674000b03656e76106d616e61676564534341646472657373000203656e761b6d616e61676564457865637574654f6e44657374436f6e74657874000c03656e760f636c65616e52657475726e44617461000003656e760f6d4275666665725365744279746573000603656e76196d42756666657246726f6d426967496e74556e7369676e6564000103656e76126d427566666572417070656e644279746573000603656e76126d42756666657253746f726167654c6f6164000103656e76106d4275666665724765744c656e677468000403656e76136d42756666657247657442797465536c696365000d03656e76126d616e616765645369676e616c4572726f72000203656e76136d42756666657253746f7261676553746f7265000103656e7618626967496e7447657445787465726e616c42616c616e6365000503656e760a626967496e745369676e000403656e760e636865636b4e6f5061796d656e74000003656e76136765744e756d455344545472616e7366657273000303656e7612626967496e7447657443616c6c56616c7565000203656e76226d616e616765644d756c74695472616e73666572455344544e465445786563757465000703656e760d6d42756666657246696e697368000403656e761c6d616e616765644765744d756c74694553445443616c6c56616c7565000203656e7609626967496e74436d70000103656e761b6d616e616765645472616e7366657256616c756545786563757465000703656e7614626967496e7446696e697368556e7369676e6564000203656e7614736d616c6c496e7446696e6973685369676e6564000e03656e760f6d4275666665724765744279746573000103252404030401050300020803040508050105040405050203000304020300000000000000000005030100030616037f01418080080b7f0041cd83080b7f0041d083080b079b010c066d656d6f7279020004696e6974003e087772617045676c64003f0a756e7772617045676c640040146765744c6f636b656445676c6442616c616e63650041156765745772617070656445676c64546f6b656e49640042057061757365004307756e7061757365004408697350617573656400450863616c6c4261636b00460a5f5f646174615f656e6403010b5f5f686561705f6261736503020af90e241601017f1024220142001000200120012000100120010b1901017f41a4830841a4830828020041016b220036020020000b0f01017f10032201200010041a20010b0b0020002001100541004a0b0900200020011002000b0c01017f10242200100620000b1d01017f10242200100720001028102604400f0b41f0820841241002000b1400100920004604400f0b41c7800841191002000b1b00102c1a20022003102d102e20002002360204200020013602000b1301017f1024220041d482084100100e1a20000b0f01017f102422012000100f1a20010b4601017f230041106b220224002002200141187420014180fe03714108747220014108764180fe03712001411876727236020c20002002410c6a410410101a200241106a24000b1b00102c1a200220031025102e20002002360204200020013602000b18002001410d103121012000102c360204200020013602000b1101017f1024220220002001100e1a20020b1f01017e100a4162100b41764200100041624176200020011024100c1a100d0b0d0020001024220010111a20000bc70102017e037f230041106b2202240020024200370308200010332204101222004109490440200441002000200220006b41106a10131a024002402002290308220142388620014280fe0383422886842001428080fc0783421886200142808080f80f834208868484200142088842808080f80f832001421888428080fc07838420014228884280fe03832001423888848422018442015804402001a741016b0d020c010b419e810841121035000b410121030b200241106a240020030f0b41fa8008410e1035000b1a01017f418881084116103122022000200110101a20021014000b5101027f230041106b22022400200220001012220341187420034180fe03714108747220034108764180fe03712003411876727236020c20012002410c6a410410101a2001200010041a200241106a24000b5701027f230041106b2201240010382001410036000b20014100360208200120003a000f4107410820001b2200200141086a22022000410771722c00004107756a220020026a410820006b103110151a200141106a24000b0a0041be8208411310310b120010381034044041b0810841121027000b0b2101027f10242201100b10242100200141ad830810221a41ad83082000101620000b09002000101741004a0b1601017f102c21012000102c360204200020013602000b0a0041ac8208411210310b1a01017f10184101102a41001024220010081a103d200010151a0bf00201087f230041306b22002400024010194504404100102a103941752101024041a883082d000022020440417541ffffffff0720021b21010c010b41a8830841013a00004175101a0b2001103b450d01103d10332104200041186a41ed80081030200041106a2000280218200028021c2004102f200041086a200028021020002802142001102b2000280208200028020c1032416210062000103c2000280204210620002802002107102c2105200410252102200110232103200042003702242000200241187420024180fe03714108747220024108764180fe0371200241187672723602202000200341187420034180fe03714108747220034108764180fe03712003411876727236022c2005200041206a2202411010101a41622005420020072006101b1a20011023200441d4820841001031220110362000420037032020012002410810101a102d200110362001101c1a200041306a24000f0b41a2800841251002000b41918208411b1027000b8d0402097f017e230041306b220024004100102a1039416b2102024041ac83082d000022010440416b41ffffffff0720011b21020c010b41ac830841013a0000416b101d0b024020021012417071411046044041002101200210122105200041286a2106410121040340200141106a220720054b0d022006420037030020004200370320200220014110200041206a10131a200404402000290224220942388620094280fe0383422886842009428080fc0783421886200942808080f80f834208868484200942088842808080f80f832009421888428080fc07838420094228884280fe0383200942388884848421092000280220220141187420014180fe03714108747220014108764180fe0371200141187672722108200028022c220141187420014180fe03714108747220014108764180fe037120014118767272210341002104200721010c010b0b41948308410e1002000b4180800841221002000b02400240024020095004402008103d103322011026450d012003103b450d022003103a101e41004a0d03200041186a41e080081030200041106a2000280218200028021c2001102f200041086a200028021020002802142003102b2000280208200028020c103210282000103c2003420020002802002000280204101f1a200041306a24000f0b41d48208411c1002000b41c2810841101027000b41d28108411c1027000b41ee810841231027000b0c0010184100102a103a10200b0f0010184100102a103d1033101c1a0b0e00101810294100102a410110370b0e00101810294100102a410010370b0f0010184100102a10381034ad10210b0300010b0bb6030200418080080ba203696e636f7272656374206e756d626572206f662045534454207472616e736665727366756e6374696f6e20646f6573206e6f74206163636570742045534454207061796d656e7477726f6e67206e756d626572206f6620617267756d656e7473455344544c6f63616c4275726e455344544c6f63616c4d696e74696e70757420746f6f206c6f6e6773746f72616765206465636f6465206572726f723a20696e707574206f7574206f662072616e6765436f6e74726163742069732070617573656457726f6e67206573647420746f6b656e4d75737420706179206d6f7265207468616e203020746f6b656e7321436f6e747261637420646f6573206e6f74206861766520656e6f7567682066756e64735061796d656e74206d757374206265206d6f7265207468616e20307772617070656445676c64546f6b656e496470617573655f6d6f64756c653a70617573656400000066756e6769626c65204553445420746f6b656e206578706563746564456e64706f696e742063616e206f6e6c792062652063616c6c6564206279206f776e657270616e6963206f636375727265640041a483080b049cffffff", + "report": { + "imports": [ + "bigIntAdd", + "bigIntCmp", + "bigIntFinishUnsigned", + "bigIntGetCallValue", + "bigIntGetExternalBalance", + "bigIntSetInt64", + "bigIntSign", + "checkNoPayment", + "cleanReturnData", + "getGasLeft", + "getNumArguments", + "getNumESDTTransfers", + "mBufferAppend", + "mBufferAppendBytes", + "mBufferEq", + "mBufferFinish", + "mBufferFromBigIntUnsigned", + "mBufferGetArgument", + "mBufferGetByteSlice", + "mBufferGetBytes", + "mBufferGetLength", + "mBufferNew", + "mBufferSetBytes", + "mBufferStorageLoad", + "mBufferStorageStore", + "managedCaller", + "managedExecuteOnDestContext", + "managedGetMultiESDTCallValue", + "managedMultiTransferESDTNFTExecute", + "managedOwnerAddress", + "managedSCAddress", + "managedSignalError", + "managedTransferValueExecute", + "signalError", + "smallIntFinishSigned" + ], + "memoryAllocationError": false, + "isMemGrow": false, + "eiCheck": { + "eiVersion": "1.3", + "ok": true + } + } +} diff --git a/tests/bridge_sc/bridge_sc.rs b/tests/bridge_sc/bridge_sc.rs index 714ec47..ca6b024 100644 --- a/tests/bridge_sc/bridge_sc.rs +++ b/tests/bridge_sc/bridge_sc.rs @@ -6,27 +6,29 @@ use multiversx_sc::{ imports::MultiValue2, types::{Address, BigUint, MultiValueEncoded}, }; -use multiversx_sc_scenario::managed_address; -use multiversx_sc_scenario::scenario_model::ScQueryStep; -use multiversx_sc_scenario::{ - api::StaticApi, - managed_buffer, managed_token_id, - scenario_model::{ - Account, AddressValue, BigUintValue, ScCallStep, ScDeployStep, SetStateStep, TxExpect, - }, - ContractInfo, ScenarioWorld, -}; +use multiversx_sc_scenario::imports::*; +use multiversx_sc_scenario::scenario_model::BigUintValue; + +pub const BRIDGE_CONTRACT_PATH: &str = "mxsc:output/core-mx-bridge-sc-mxsc.json"; -pub const BRIDGE_CONTRACT_PATH: &str = "mxsc:output/core=mx-bridge-sc-mxsc.json"; +pub const WEGLD_SWAP_CONTRACT_PATH: &str = + "mxsc:tests-contracts/multiversx-wegld-swap-sc.mxsc.json"; pub const BRIDGE_CONTRACT_ADDRESS_EXPR: &str = "sc:bridge-sc"; +pub const WEGLD_SWAP_CONTRACT_ADDRESS_EXPR: &str = "sc:wegld-swap-sc"; + +pub const OWNER_WEGLD_SWAP_CONTRACT_ADDRESS_EXPR: &str = "address:owner-wegld-swap-sc"; + pub const OWNER_BRIDGE_CONTRACT_ADDRESS_EXPR: &str = "address:owner-bridge-sc"; pub const ADMIN_BRIDGE_CONTRACT_ADDRESS_EXPR: &str = "address:admin-bridge-sc"; pub const RELAYER_BRIDGE_CONTRACT_ADDRESS_EXPR: &str = "address:relayer-bridge-sc"; +pub const WEGLD_TOKEN_IDENTIFIER_EXPR: &str = "str:WEGLD-fce905"; +pub const WEGLD_TOKEN_IDENTIFIER: &[u8] = b"WEGLD-fce905"; + pub const ITHEUM_TOKEN_IDENTIFIER_EXPR: &str = "str:ITHEUM-fce905"; pub const ITHEUM_TOKEN_IDENTIFIER: &[u8] = b"ITHEUM-fce905"; @@ -36,7 +38,10 @@ pub const ANOTHER_TOKEN_IDENTIFIER: &[u8] = b"ANOTHER-fce905"; pub const FIRST_USER_ADDRESS_EXPR: &str = "address:first_user"; pub const SECOND_USER_ADDRESS_EXPR: &str = "address:second_user"; +pub const THIRD_USER_ADDRESS_EXPR: &str = "address:third_user"; + type Contract = ContractInfo>; +type WegldSwapContract = ContractInfo>; pub fn world() -> ScenarioWorld { let mut blockchain = ScenarioWorld::new(); @@ -45,17 +50,24 @@ pub fn world() -> ScenarioWorld { blockchain.register_contract(BRIDGE_CONTRACT_PATH, core_mx_bridge_sc::ContractBuilder); + blockchain.register_contract( + WEGLD_SWAP_CONTRACT_PATH, + multiversx_wegld_swap_sc::ContractBuilder, + ); + blockchain } pub struct ContractState { pub world: ScenarioWorld, pub contract: Contract, + pub wegld_swap_contract: WegldSwapContract, pub contract_owner: Address, pub admin: Address, pub relayer: Address, pub first_user: Address, pub second_user: Address, + pub third_user: Address, } impl ContractState { @@ -76,6 +88,15 @@ impl ContractState { 1, BRIDGE_CONTRACT_ADDRESS_EXPR, ) + .put_account( + OWNER_WEGLD_SWAP_CONTRACT_ADDRESS_EXPR, + Account::new().nonce(1).balance("1_000"), + ) + .new_address( + OWNER_WEGLD_SWAP_CONTRACT_ADDRESS_EXPR, + 1, + WEGLD_SWAP_CONTRACT_ADDRESS_EXPR, + ) .put_account( ADMIN_BRIDGE_CONTRACT_ADDRESS_EXPR, Account::new() @@ -86,7 +107,7 @@ impl ContractState { ) .put_account( RELAYER_BRIDGE_CONTRACT_ADDRESS_EXPR, - Account::new().nonce(1).balance("1_000"), + Account::new().nonce(1), ) .put_account( FIRST_USER_ADDRESS_EXPR, @@ -101,25 +122,30 @@ impl ContractState { .balance("100") .esdt_balance(ITHEUM_TOKEN_IDENTIFIER_EXPR, "1_000") .esdt_balance(ANOTHER_TOKEN_IDENTIFIER_EXPR, "1_000"), - ), + ) + .put_account(THIRD_USER_ADDRESS_EXPR, Account::new().balance("100")), ); let contract = Contract::new(BRIDGE_CONTRACT_ADDRESS_EXPR); + let wegld_swap_contract = WegldSwapContract::new(WEGLD_SWAP_CONTRACT_ADDRESS_EXPR); let contract_owner = AddressValue::from(OWNER_BRIDGE_CONTRACT_ADDRESS_EXPR).to_address(); let admin = AddressValue::from(ADMIN_BRIDGE_CONTRACT_ADDRESS_EXPR).to_address(); let relayer = AddressValue::from(RELAYER_BRIDGE_CONTRACT_ADDRESS_EXPR).to_address(); let first_user = AddressValue::from(FIRST_USER_ADDRESS_EXPR).to_address(); let second_user = AddressValue::from(SECOND_USER_ADDRESS_EXPR).to_address(); + let third_user = AddressValue::from(THIRD_USER_ADDRESS_EXPR).to_address(); Self { world, contract, + wegld_swap_contract, contract_owner, admin, relayer, first_user, second_user, + third_user, } } @@ -147,13 +173,46 @@ impl ContractState { ) .set_wegld_contract_address( OWNER_BRIDGE_CONTRACT_ADDRESS_EXPR, - AddressValue::from(RELAYER_BRIDGE_CONTRACT_ADDRESS_EXPR).to_address(), + AddressValue::from(WEGLD_SWAP_CONTRACT_ADDRESS_EXPR).to_address(), None, ); self } + pub fn deploy_wegld_swap(&mut self) -> &mut Self { + let wegld_swap_contract_code = self.world.code_expression(WEGLD_SWAP_CONTRACT_PATH); + + let mut acc = Account::new() + .esdt_roles( + WEGLD_TOKEN_IDENTIFIER_EXPR, + vec![ + "ESDTRoleLocalBurn".to_string(), + "ESDTRoleLocalMint".to_string(), + ], + ) + .code(wegld_swap_contract_code); + + acc.storage.insert( + b"wrappedEgldTokenId".to_vec().into(), + WEGLD_TOKEN_IDENTIFIER.to_vec().into(), + ); + + acc.storage.insert( + b"pause_module:paused".to_vec().into(), + "false".to_string().into(), + ); + + acc.owner = Option::Some(AddressValue::from(OWNER_WEGLD_SWAP_CONTRACT_ADDRESS_EXPR)); + self.world.set_state_step( + SetStateStep::new() + .new_token_identifier(WEGLD_TOKEN_IDENTIFIER_EXPR) + .put_account(WEGLD_SWAP_CONTRACT_ADDRESS_EXPR, acc), + ); + + self + } + pub fn deploy(&mut self) -> &mut Self { let bridge_contract_code = self.world.code_expression(BRIDGE_CONTRACT_PATH); @@ -207,6 +266,22 @@ impl ContractState { self } + pub fn set_fee_value( + &mut self, + caller: &str, + amount: u64, + expect: Option, + ) -> &mut Self { + let tx_expect = expect.unwrap_or(TxExpect::ok()); + self.world.sc_call( + ScCallStep::new() + .from(caller) + .call(self.contract.set_fee_value(BigUint::from(amount))) + .expect(tx_expect), + ); + self + } + pub fn set_contract_state_active( &mut self, caller: &str, @@ -467,6 +542,45 @@ impl ContractState { self } + pub fn send_to_liquidity_with_fee( + &mut self, + caller: &str, + payment: (&str, u64, &str), + fee: (&str, u64, &str), + extra_arguments: Vec<&[u8]>, + expect: Option, + ) -> &mut Self { + let tx_expect = expect.unwrap_or(TxExpect::ok()); + + // let mut tokens = Vec::::new(); + + // tokens.push(TxESDT { + // esdt_token_identifier: BytesValue::from(payment.0), + // nonce: U64Value::from(payment.1), + // esdt_value: BigUintValue::from(payment.2), + // }); + + // tokens.push(TxESDT { + // esdt_token_identifier: BytesValue::from(fee.0), + // nonce: U64Value::from(fee.1), + // esdt_value: BigUintValue::from(fee.2), + // }); + + self.world.sc_call( + ScCallStep::new() + .from(caller) + .esdt_transfer(payment.0, payment.1, BigUintValue::from(payment.2)) + .esdt_transfer(fee.0, fee.1, BigUintValue::from(fee.2)) + .call( + self.contract + .send_to_liquidity(extra_arguments[0], extra_arguments[1]), + ) + .expect(tx_expect), + ); + + self + } + pub fn send_from_liquidity( &mut self, caller: &str, diff --git a/tests/endpoints/public.rs b/tests/endpoints/public.rs index f4cf54e..4f31053 100644 --- a/tests/endpoints/public.rs +++ b/tests/endpoints/public.rs @@ -1,13 +1,18 @@ use multiversx_sc_scenario::imports::{ - Account, CheckAccount, CheckStateStep, SetStateStep, TxExpect, + Account, CheckAccount, CheckStateStep, ScCallStep, SetStateStep, TxExpect, }; use crate::bridge_sc::bridge_sc::{ - ContractState, ADMIN_BRIDGE_CONTRACT_ADDRESS_EXPR, BRIDGE_CONTRACT_ADDRESS_EXPR, - FIRST_USER_ADDRESS_EXPR, ITHEUM_TOKEN_IDENTIFIER, ITHEUM_TOKEN_IDENTIFIER_EXPR, - OWNER_BRIDGE_CONTRACT_ADDRESS_EXPR, + ContractState, ADMIN_BRIDGE_CONTRACT_ADDRESS_EXPR, ANOTHER_TOKEN_IDENTIFIER_EXPR, + BRIDGE_CONTRACT_ADDRESS_EXPR, FIRST_USER_ADDRESS_EXPR, ITHEUM_TOKEN_IDENTIFIER, + ITHEUM_TOKEN_IDENTIFIER_EXPR, OWNER_BRIDGE_CONTRACT_ADDRESS_EXPR, + OWNER_WEGLD_SWAP_CONTRACT_ADDRESS_EXPR, RELAYER_BRIDGE_CONTRACT_ADDRESS_EXPR, + THIRD_USER_ADDRESS_EXPR, WEGLD_TOKEN_IDENTIFIER_EXPR, }; +use multiversx_sc_modules::pause::ProxyTrait; +use multiversx_wegld_swap_sc::ProxyTrait as _; + #[test] fn send_to_bridge_test() { let mut state = ContractState::new(); @@ -129,3 +134,117 @@ fn send_to_bridge_test() { ), ); } + +#[test] +fn send_to_bridge_require_fee_test() { + let mut state = ContractState::new(); + + state + .default_deploy_and_set() + .deploy_wegld_swap() + .set_contract_state_active(OWNER_BRIDGE_CONTRACT_ADDRESS_EXPR, None) + .set_fee_value(OWNER_BRIDGE_CONTRACT_ADDRESS_EXPR, 1_000u64, None); + + state.set_deposit_limits( + OWNER_BRIDGE_CONTRACT_ADDRESS_EXPR, + ITHEUM_TOKEN_IDENTIFIER, + b"0", + b"1000000000000000000000", // 1000 tokens + None, + ); + + state.world.set_state_step( + SetStateStep::new().put_account( + THIRD_USER_ADDRESS_EXPR, + Account::new() + .nonce(1) + .balance("1_000") + .esdt_balance(ITHEUM_TOKEN_IDENTIFIER_EXPR, "100_000_000_000_000_000_000") // 100 tokens + .esdt_balance(ANOTHER_TOKEN_IDENTIFIER_EXPR, "1_000"), + ), + ); + + state.world.sc_call( + ScCallStep::new() + .from(OWNER_WEGLD_SWAP_CONTRACT_ADDRESS_EXPR) + .call(state.wegld_swap_contract.unpause_endpoint()) + .expect(TxExpect::ok()), + ); + + state.world.sc_call( + ScCallStep::new() + .from(THIRD_USER_ADDRESS_EXPR) + .egld_value("1_000") + .call(state.wegld_swap_contract.wrap_egld()) + .expect(TxExpect::ok()), + ); + + state.send_to_liquidity( + THIRD_USER_ADDRESS_EXPR, + ( + ITHEUM_TOKEN_IDENTIFIER_EXPR, + 0u64, + "1_000_000_000_000_000_000", + ), + vec![b"sol_address", b"sol_signature"], + Some(TxExpect::user_error( + "str:incorrect number of ESDT transfers", + )), + ); + + state.send_to_liquidity_with_fee( + THIRD_USER_ADDRESS_EXPR, + ( + ITHEUM_TOKEN_IDENTIFIER_EXPR, + 0u64, + "10_000_000_000_000_000_000", + ), + (ANOTHER_TOKEN_IDENTIFIER_EXPR, 0u64, "1_000"), + vec![b"sol_address", b"sol_signature"], + Some(TxExpect::user_error("str:Wrong fee token identifier")), + ); + + state.send_to_liquidity_with_fee( + THIRD_USER_ADDRESS_EXPR, + ( + ITHEUM_TOKEN_IDENTIFIER_EXPR, + 0u64, + "10_000_000_000_000_000_000", + ), + (WEGLD_TOKEN_IDENTIFIER_EXPR, 0u64, "100"), + vec![b"sol_address", b"sol_signature"], + Some(TxExpect::user_error("str:Wrong values")), + ); + + state.send_to_liquidity_with_fee( + THIRD_USER_ADDRESS_EXPR, + ( + ITHEUM_TOKEN_IDENTIFIER_EXPR, + 0u64, + "10_000_000_000_000_000_000", + ), + (WEGLD_TOKEN_IDENTIFIER_EXPR, 0u64, "1_000"), + vec![b"sol_address", b"sol_signature"], + None, + ); + + state.world.check_state_step( + CheckStateStep::new() + .put_account( + BRIDGE_CONTRACT_ADDRESS_EXPR, + CheckAccount::new() + .balance("0") + .esdt_balance(ITHEUM_TOKEN_IDENTIFIER_EXPR, "10_000_000_000_000_000_000"), + ) + .put_account( + THIRD_USER_ADDRESS_EXPR, + CheckAccount::new() + .balance("0") + .esdt_balance(ITHEUM_TOKEN_IDENTIFIER_EXPR, "90_000_000_000_000_000_000"), + ) + .put_account( + RELAYER_BRIDGE_CONTRACT_ADDRESS_EXPR, + CheckAccount::new().balance("1_000"), // fee + ), + ); +} diff --git a/tests/unit_tests.rs b/tests/unit_tests.rs index 9eb2723..922e874 100644 --- a/tests/unit_tests.rs +++ b/tests/unit_tests.rs @@ -88,7 +88,7 @@ fn contract_is_ready_test() { assert_eq!(check, false); bridge_sc - .fee_colector() + .fee_collector() .set(managed_address!(&AddressValue::from( ADMIN_BRIDGE_CONTRACT_ADDRESS_EXPR ) diff --git a/wasm/src/lib.rs b/wasm/src/lib.rs index a698f1a..d91ed14 100644 --- a/wasm/src/lib.rs +++ b/wasm/src/lib.rs @@ -26,7 +26,7 @@ multiversx_sc_wasm_adapter::endpoints! { getTokenDecimals => token_decimals getMinimumDeposit => minimum_deposit getMaximumDeposit => maximum_deposit - getFeeCollector => fee_colector + getFeeCollector => fee_collector getFeeValue => fee_value getWegldContractAddress => wegld_contract_address setAdministrator => set_administrator From de227f46a58fff3849748caae5fd386318a872a3 Mon Sep 17 00:00:00 2001 From: Bucur David Date: Tue, 4 Jun 2024 15:12:42 +0300 Subject: [PATCH 5/8] chore: code coverage action --- .github/workflows/coverage.yml | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 .github/workflows/coverage.yml diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml new file mode 100644 index 0000000..8bd5a80 --- /dev/null +++ b/.github/workflows/coverage.yml @@ -0,0 +1,29 @@ +name: Coverage + +on: + pull_request: + branches: + - main + - develop +jobs: + coverage: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Install latest nightly + uses: actions-rs/toolchain@v1 + with: + toolchain: 2024-02-27 + override: true + components: rustfmt, clippy + - name: Install cargo-llvm-cov + uses: taiki-e/install-action@cargo-llvm-cov + - name: Making coverage dir + run: mkdir coverage + - name: Generate code coverage + run: cargo llvm-cov --lcov --output-path coverage/lcov.info --ignore-filename-regex '(storage.rs|events.rs)' + - name: Generating report + uses: vebr/jest-lcov-reporter@v0.2.0 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + lcov-file: ./coverage/lcov.info From 357a73e0030f05786c5bc4efe5f8f451298eeba6 Mon Sep 17 00:00:00 2001 From: Bucur David Date: Tue, 4 Jun 2024 15:14:09 +0300 Subject: [PATCH 6/8] chore: code coverage action --- .github/workflows/coverage.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 8bd5a80..83ae6d5 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -13,7 +13,7 @@ jobs: - name: Install latest nightly uses: actions-rs/toolchain@v1 with: - toolchain: 2024-02-27 + toolchain: stable override: true components: rustfmt, clippy - name: Install cargo-llvm-cov From dd928576e47d4dbf92e48b0b8356db9e6c68ebb3 Mon Sep 17 00:00:00 2001 From: Bucur David Date: Tue, 4 Jun 2024 15:16:43 +0300 Subject: [PATCH 7/8] chore: code coverage action filter files --- .github/workflows/coverage.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 83ae6d5..3886867 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -21,7 +21,7 @@ jobs: - name: Making coverage dir run: mkdir coverage - name: Generate code coverage - run: cargo llvm-cov --lcov --output-path coverage/lcov.info --ignore-filename-regex '(storage.rs|events.rs)' + run: cargo llvm-cov --lcov --output-path coverage/lcov.info --ignore-filename-regex '(storage.rs|events.rs|macros|errors.rs|proxies)' - name: Generating report uses: vebr/jest-lcov-reporter@v0.2.0 with: From c784f130f1b12b23cc74be08b6c479682b18b3b4 Mon Sep 17 00:00:00 2001 From: Bucur David Date: Wed, 5 Jun 2024 15:28:12 +0300 Subject: [PATCH 8/8] fix: minor checks and fixes --- src/admin.rs | 18 ++++++++++-------- src/errors.rs | 1 + src/lib.rs | 13 +++++-------- wasm/Cargo.lock | 10 ++++++++++ 4 files changed, 26 insertions(+), 16 deletions(-) diff --git a/src/admin.rs b/src/admin.rs index 84bad01..28b1cb9 100644 --- a/src/admin.rs +++ b/src/admin.rs @@ -2,7 +2,8 @@ use crate::{ config::State, errors::{ ERR_ADDRESS_ALREADY_WHITELISTED, ERR_ADDRESS_NOT_WHITELISTED, ERR_ALREADY_ACTIVE, - ERR_ALREADY_INACTIVE, ERR_NOT_PRIVILEGED, ERR_TOKEN_NOT_WHITELISTED, ERR_WRONG_VALUES, + ERR_ALREADY_INACTIVE, ERR_NOT_PRIVILEGED, ERR_TOKEN_ALREADY_IN_WHITELIST, + ERR_TOKEN_NOT_WHITELISTED, ERR_WRONG_VALUES, }, events, only_privileged, storage, }; @@ -116,7 +117,10 @@ pub trait AdminModule: for token in tokens.into_iter() { let (token_identifier, token_decimals) = token.into_tuple(); self.token_decimals(&token_identifier).set(token_decimals); - self.tokens_whitelist().insert(token_identifier); + require!( + self.tokens_whitelist().insert(token_identifier), + ERR_TOKEN_ALREADY_IN_WHITELIST + ); } } @@ -126,7 +130,10 @@ pub trait AdminModule: self.remove_tokens_from_whitelist_event(&tokens.to_vec()); for token in tokens.into_iter() { self.token_decimals(&token).clear(); - self.tokens_whitelist().swap_remove(&token); + require!( + self.tokens_whitelist().swap_remove(&token), + ERR_TOKEN_NOT_WHITELISTED + ); } } @@ -185,11 +192,6 @@ pub trait AdminModule: fn remove_from_liquidity(&self, token_identifier: TokenIdentifier, amount: BigUint) { only_privileged!(self, ERR_NOT_PRIVILEGED); - require!( - self.tokens_whitelist().contains(&token_identifier), - ERR_TOKEN_NOT_WHITELISTED - ); - let caller = self.blockchain().get_caller(); self.remove_from_liquidity_event(&caller, &token_identifier, &amount); diff --git a/src/errors.rs b/src/errors.rs index 8c344b8..54f7c87 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -11,3 +11,4 @@ pub const ERR_WRONG_VALUES: &str = "Wrong values"; pub const ERR_ADDRESS_NOT_WHITELISTED: &str = "Address not whitelisted"; pub const ERR_ADDRESS_ALREADY_WHITELISTED: &str = "Address already whitelisted"; pub const ERR_WRONG_FEE_TOKEN_IDENTIFIER: &str = "Wrong fee token identifier"; +pub const ERR_TOKEN_ALREADY_IN_WHITELIST: &str = "Token already in whitelist"; diff --git a/src/lib.rs b/src/lib.rs index ec58376..b307c73 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -154,11 +154,6 @@ pub trait CoreMxBridgeSc: let caller = self.blockchain().get_caller(); require!(self.relayer().get() == caller, ERR_NOT_PRIVILEGED); - require!( - self.liquidity(&token_identifier).get() >= amount, - ERR_NOT_ENOUGH_LIQUIDITY - ); - self.send_from_liquidity_event( &self.relayer().get(), &token_identifier, @@ -166,10 +161,12 @@ pub trait CoreMxBridgeSc: &receiver, ); + self.liquidity(&token_identifier).update(|value| { + require!(*value >= amount, ERR_NOT_ENOUGH_LIQUIDITY); + *value -= &amount; + }); + self.send() .direct_esdt(&receiver, &token_identifier, 0u64, &amount); - - self.liquidity(&token_identifier) - .update(|value| *value -= amount); } } diff --git a/wasm/Cargo.lock b/wasm/Cargo.lock index e81f2bd..e57496e 100644 --- a/wasm/Cargo.lock +++ b/wasm/Cargo.lock @@ -25,6 +25,7 @@ name = "core-mx-bridge-sc" version = "0.1.0" dependencies = [ "multiversx-sc", + "multiversx-sc-modules", ] [[package]] @@ -103,6 +104,15 @@ dependencies = [ "syn", ] +[[package]] +name = "multiversx-sc-modules" +version = "0.50.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f9a8c10ddadc7adfc04edbdd1ea80a9ae9a7e11202fc4cfb2e08c8e27f17aa5" +dependencies = [ + "multiversx-sc", +] + [[package]] name = "multiversx-sc-wasm-adapter" version = "0.50.3"