From 21d74ab09933d3e9ba747431ab3bf2245692f918 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Wed, 13 Nov 2024 15:47:14 +0200 Subject: [PATCH 01/45] storage naming snake case -> camel case --- bridge-proxy/src/config.rs | 6 +++--- bridged-tokens-wrapper/src/lib.rs | 2 +- multisig/src/setup.rs | 2 +- multisig/src/storage.rs | 8 ++++---- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/bridge-proxy/src/config.rs b/bridge-proxy/src/config.rs index 79f94ee1..c65aa314 100644 --- a/bridge-proxy/src/config.rs +++ b/bridge-proxy/src/config.rs @@ -4,17 +4,17 @@ use transaction::EthTransaction; #[multiversx_sc::module] pub trait ConfigModule { - #[storage_mapper("pending_transactions")] + #[storage_mapper("pendingTransactions")] fn pending_transactions(&self) -> MapMapper>; #[storage_mapper("payments")] fn payments(&self, tx_id: usize) -> SingleValueMapper>; - #[storage_mapper("batch_id")] + #[storage_mapper("batchId")] fn batch_id(&self, tx_id: usize) -> SingleValueMapper; #[view(highestTxId)] - #[storage_mapper("highest_tx_id")] + #[storage_mapper("highestTxId")] fn highest_tx_id(&self) -> SingleValueMapper; #[storage_mapper("ongoingExecution")] diff --git a/bridged-tokens-wrapper/src/lib.rs b/bridged-tokens-wrapper/src/lib.rs index deb5541f..fe88ddc0 100644 --- a/bridged-tokens-wrapper/src/lib.rs +++ b/bridged-tokens-wrapper/src/lib.rs @@ -327,6 +327,6 @@ pub trait BridgedTokensWrapper: universal_token_id: &TokenIdentifier, ) -> UnorderedSetMapper; - #[storage_mapper("token_decimals_num")] + #[storage_mapper("tokenDecimalsNum")] fn token_decimals_num(&self, token: &TokenIdentifier) -> SingleValueMapper; } diff --git a/multisig/src/setup.rs b/multisig/src/setup.rs index 9bf99bd7..7bdc30a1 100644 --- a/multisig/src/setup.rs +++ b/multisig/src/setup.rs @@ -66,7 +66,7 @@ pub trait SetupModule: fn slash_board_member(&self, board_member: ManagedAddress) { let slash_amount = self.slash_amount().get(); - // remove slashed amount from user stake amountself + // remove slashed amount from user stake amount self self.amount_staked(&board_member) .update(|stake| *stake -= &slash_amount); diff --git a/multisig/src/storage.rs b/multisig/src/storage.rs index 3823f73a..3820a97d 100644 --- a/multisig/src/storage.rs +++ b/multisig/src/storage.rs @@ -18,19 +18,19 @@ pub trait StorageModule { #[storage_mapper("user")] fn user_mapper(&self) -> UserMapper; - #[storage_mapper("user_role")] + #[storage_mapper("userRole")] fn user_id_to_role(&self, user_id: usize) -> SingleValueMapper; /// Denormalized board member count. /// It is kept in sync with the user list by the contract. #[view(getNumBoardMembers)] - #[storage_mapper("num_board_members")] + #[storage_mapper("numBoardMembers")] fn num_board_members(&self) -> SingleValueMapper; - #[storage_mapper("action_data")] + #[storage_mapper("actionData")] fn action_mapper(&self) -> VecMapper>; - #[storage_mapper("action_signer_ids")] + #[storage_mapper("actionSignerIds")] fn action_signer_ids(&self, action_id: usize) -> UnorderedSetMapper; /// The required amount to stake for accepting relayer position From de92e668d71b3ea60ce1f80828c1a5d34426ac21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Wed, 13 Nov 2024 17:36:16 +0200 Subject: [PATCH 02/45] multisig: add executed_actions storage --- multisig/src/lib.rs | 5 +++-- multisig/src/queries.rs | 8 +------- multisig/src/setup.rs | 7 +------ multisig/src/storage.rs | 3 +++ 4 files changed, 8 insertions(+), 15 deletions(-) diff --git a/multisig/src/lib.rs b/multisig/src/lib.rs index 87c526bb..e03db58f 100644 --- a/multisig/src/lib.rs +++ b/multisig/src/lib.rs @@ -269,7 +269,7 @@ pub trait Multisig: "Action already proposed" ); - let current_batch_len = current_batch_transactions.raw_len() / TX_MULTIRESULT_NR_FIELDS; + let current_batch_len = current_batch_transactions.len(); let status_batch_len = statuses_vec.len(); require!( current_batch_len == status_batch_len, @@ -416,7 +416,7 @@ pub trait Multisig: #[endpoint(performAction)] fn perform_action_endpoint(&self, action_id: usize) { require!( - !self.action_mapper().item_is_empty(action_id), + !self.executed_actions().contains(&action_id), "Action was already executed" ); @@ -433,6 +433,7 @@ pub trait Multisig: require!(self.not_paused(), "No actions may be executed while paused"); self.perform_action(action_id); + self.executed_actions().insert(action_id); } // private diff --git a/multisig/src/queries.rs b/multisig/src/queries.rs index ba5f3fc6..d9efd368 100644 --- a/multisig/src/queries.rs +++ b/multisig/src/queries.rs @@ -56,15 +56,9 @@ pub trait QueriesModule: crate::storage::StorageModule + crate::util::UtilModule .sync_call() } - /// Actions are cleared after execution, so an empty entry means the action was executed already - /// Returns "false" if the action ID is invalid #[view(wasActionExecuted)] fn was_action_executed(&self, action_id: usize) -> bool { - if self.is_valid_action_id(action_id) { - self.action_mapper().item_is_empty(action_id) - } else { - false - } + self.executed_actions().contains(&action_id) } /// Used for Ethereum -> MultiversX batches. diff --git a/multisig/src/setup.rs b/multisig/src/setup.rs index 7bdc30a1..28c1d62d 100644 --- a/multisig/src/setup.rs +++ b/multisig/src/setup.rs @@ -17,14 +17,9 @@ pub trait SetupModule: &self, child_sc_address: ManagedAddress, source_address: ManagedAddress, - is_payable: bool, init_args: MultiValueEncoded, ) { - let mut metadata = CodeMetadata::UPGRADEABLE; - if is_payable { - // TODO: Replace with PayableBySc when it's available - metadata |= CodeMetadata::PAYABLE; - } + let metadata = CodeMetadata::UPGRADEABLE; let gas = self.blockchain().get_gas_left(); self.send_raw().upgrade_from_source_contract( diff --git a/multisig/src/storage.rs b/multisig/src/storage.rs index 3820a97d..1f3478b8 100644 --- a/multisig/src/storage.rs +++ b/multisig/src/storage.rs @@ -73,6 +73,9 @@ pub trait StorageModule { esdt_safe_batch_id: u64, ) -> MapMapper, usize>; + #[storage_mapper("executedActions")] + fn executed_actions(&self) -> UnorderedSetMapper; + /// Mapping between ERC20 Ethereum address and MultiversX ESDT Token Identifiers #[view(getErc20AddressForTokenId)] From 81cdbfbca1d58476f39b9519f79ebdf50d92db27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Wed, 13 Nov 2024 18:47:10 +0200 Subject: [PATCH 03/45] multisig: change_quorum improvement --- multisig/src/setup.rs | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/multisig/src/setup.rs b/multisig/src/setup.rs index 28c1d62d..351458c0 100644 --- a/multisig/src/setup.rs +++ b/multisig/src/setup.rs @@ -3,6 +3,8 @@ use multiversx_sc::imports::*; use eth_address::EthAddress; use sc_proxies::{bridge_proxy_contract_proxy, esdt_safe_proxy, multi_transfer_esdt_proxy}; +const MAX_BOARD_MEMBERS: usize = 40; + #[multiversx_sc::module] pub trait SetupModule: crate::multisig_general::MultisigGeneralModule @@ -74,7 +76,28 @@ pub trait SetupModule: #[endpoint(changeQuorum)] fn change_quorum(&self, new_quorum: usize) { require!( - new_quorum <= self.num_board_members().get(), + new_quorum <= MAX_BOARD_MEMBERS && new_quorum > 1, + "Quorum size not appropriate" + ); + let total_users = self.user_mapper().get_user_count(); + let mut board_member_with_valid_stake: usize = 0; + + for user_id in 0..total_users { + let user_role = self.user_id_to_role(user_id).get(); + + if user_role.is_board_member() { + if let Some(board_member_addr) = self.user_mapper().get_user_address(user_id) { + let amount_staked = self.amount_staked(&board_member_addr).get(); + let required_stake_amount = self.required_stake_amount().get(); + if amount_staked >= required_stake_amount { + board_member_with_valid_stake += 1; + } + } + } + } + + require!( + new_quorum <= board_member_with_valid_stake, "quorum cannot exceed board size" ); self.quorum().set(new_quorum); From d9d15f1e4f370d635f6fe870dcf732c342e848c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Thu, 14 Nov 2024 08:37:32 +0200 Subject: [PATCH 04/45] wrapper: remove dfp_biguint tests --- bridged-tokens-wrapper/src/dfp_big_uint.rs | 7 ---- .../tests/dfp_big_uint_test.rs | 38 ------------------- 2 files changed, 45 deletions(-) delete mode 100644 bridged-tokens-wrapper/tests/dfp_big_uint_test.rs diff --git a/bridged-tokens-wrapper/src/dfp_big_uint.rs b/bridged-tokens-wrapper/src/dfp_big_uint.rs index 0a7c0090..546d784e 100644 --- a/bridged-tokens-wrapper/src/dfp_big_uint.rs +++ b/bridged-tokens-wrapper/src/dfp_big_uint.rs @@ -30,13 +30,6 @@ impl DFPBigUint { } } - pub fn trunc(&self) -> Self { - DFPBigUint { - bu: self.bu.clone() / 10u64.pow(self.num_decimals), - num_decimals: 1, - } - } - pub fn to_raw(&self) -> BigUint { self.bu.clone() } diff --git a/bridged-tokens-wrapper/tests/dfp_big_uint_test.rs b/bridged-tokens-wrapper/tests/dfp_big_uint_test.rs deleted file mode 100644 index 1c1c794b..00000000 --- a/bridged-tokens-wrapper/tests/dfp_big_uint_test.rs +++ /dev/null @@ -1,38 +0,0 @@ -use bridged_tokens_wrapper::DFPBigUint; -use multiversx_sc_scenario::DebugApi; - -#[test] -fn test_biguint() { - DebugApi::dummy(); - let raw = 123456789u64; - let dfp = DFPBigUint::::from_raw(raw.into(), 6); - let converted = dfp.clone().convert(9); - assert!(dfp.trunc() == converted.trunc()); - assert!(converted.clone().convert(9).to_raw() == 123456789000u64); - assert!(converted.clone().convert(1).to_raw() == 1234u64); - assert!(converted.clone().convert(3).to_raw() == 123456u64); - assert!(converted.clone().convert(0).to_raw() == 123u64); - assert!(converted.convert(5).to_raw() == 12345678u64); -} - -#[test] -fn test_biguint_zero_dec() { - DebugApi::dummy(); - let raw = 123u64; - let dfp = DFPBigUint::::from_raw(raw.into(), 0); - let converted = dfp.clone().convert(9); - assert!(dfp.trunc() == converted.trunc()); - assert!(converted.clone().convert(9).to_raw() == 123000000000u64); - assert!(converted.clone().convert(1).to_raw() == 1230u64); - assert!(converted.clone().convert(3).to_raw() == 123000u64); - assert!(converted.clone().convert(0).to_raw() == 123u64); - assert!(converted.convert(5).to_raw() == 12300000u64); -} - -#[test] -fn test_mandos_scenario_values() { - DebugApi::dummy(); - let raw = 300000000000000u64; - let dfp = DFPBigUint::::from_raw(raw.into(), 18); - assert!(dfp.convert(6).to_raw() == 300u64); -} From bd18c3122c257c9d3b0125b33ca0720d63e68352 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Thu, 14 Nov 2024 21:17:29 +0200 Subject: [PATCH 05/45] wrapper: more audit fixes --- .../scenarios/add_wrapped_token.scen.json | 4 +- .../scenarios/blacklist_token.scen.json | 97 +------------------ .../scenarios/remove_wrapped_token.scen.json | 69 +------------ .../scenarios/setup.scen.json | 18 ++-- .../scenarios/unwrap_token.scen.json | 62 ++---------- .../scenarios/whitelist_token.scen.json | 8 +- .../scenarios/wrap_token.scen.json | 10 +- bridged-tokens-wrapper/src/lib.rs | 45 +++++---- .../bridged_tokens_wrapper_whitebox_test.rs | 76 +++------------ bridged-tokens-wrapper/wasm/Cargo.lock | 10 ++ bridged-tokens-wrapper/wasm/src/lib.rs | 5 +- .../src/bridged_tokens_wrapper_proxy.rs | 16 --- 12 files changed, 85 insertions(+), 335 deletions(-) diff --git a/bridged-tokens-wrapper/scenarios/add_wrapped_token.scen.json b/bridged-tokens-wrapper/scenarios/add_wrapped_token.scen.json index 0a0d8c44..851a9ca4 100644 --- a/bridged-tokens-wrapper/scenarios/add_wrapped_token.scen.json +++ b/bridged-tokens-wrapper/scenarios/add_wrapped_token.scen.json @@ -120,7 +120,7 @@ "str:universalBridgedTokenIds.len": "1", "str:universalBridgedTokenIds.index|nested:str:WUSDC-abcdef": "1", "str:universalBridgedTokenIds.item|u32:1": "str:WUSDC-abcdef", - "str:token_decimals_num|nested:str:WUSDC-abcdef": "6" + "str:tokenDecimalsNum|nested:str:WUSDC-abcdef": "6" }, "code": "file:../output/bridged-tokens-wrapper.wasm", "owner": "address:owner" @@ -128,4 +128,4 @@ } } ] -} +} \ No newline at end of file diff --git a/bridged-tokens-wrapper/scenarios/blacklist_token.scen.json b/bridged-tokens-wrapper/scenarios/blacklist_token.scen.json index 7f36f67d..b06dfa0b 100644 --- a/bridged-tokens-wrapper/scenarios/blacklist_token.scen.json +++ b/bridged-tokens-wrapper/scenarios/blacklist_token.scen.json @@ -19,105 +19,12 @@ "gasLimit": "5,000,000", "gasPrice": "0" }, - "expect": { - "status": "0", - "message": "", - "gas": "*", - "refund": "*" - } - }, - { - "step": "scCall", - "txId": "unwrap-token", - "tx": { - "from": "address:user", - "to": "sc:bridged_tokens_wrapper", - "value": "0", - "esdt": { - "tokenIdentifier": "str:WUSDC-abcdef", - "value": "100" - }, - "function": "unwrapToken", - "arguments": [ - "str:USDC-aaaaaa" - ], - "gasLimit": "5,000,000", - "gasPrice": "0" - }, "expect": { "status": "4", - "message": "str:Esdt token unavailable", + "message": "str:Cannot blacklist token due to remaining liquidity", "gas": "*", "refund": "*" } - }, - { - "step": "checkState", - "accounts": { - "address:owner": { - "nonce": "4", - "balance": "0", - "storage": {} - }, - "address:user": { - "nonce": "5", - "esdt": { - "str:USDC-aaaaaa": { - "balance": "200000000000000" - }, - "str:USDC-bbbbbb": { - "balance": "500000000000000" - }, - "str:USDC-cccccc": { - "balance": "400000000000000" - }, - "str:WUSDC-abcdef": { - "balance": "900" - } - }, - "storage": {} - }, - "sc:bridged_tokens_wrapper": { - "nonce": "0", - "esdt": { - "str:WUSDC-abcdef": { - "balance": "1", - "roles": [ - "ESDTRoleLocalMint", - "ESDTRoleLocalBurn" - ] - }, - "str:WUSDC-uvwxyz": { - "balance": "1", - "roles": [ - "ESDTRoleLocalMint", - "ESDTRoleLocalBurn" - ] - }, - "str:USDC-aaaaaa": { - "balance": "300000000000000" - }, - "str:USDC-cccccc": { - "balance": "100000000000000" - } - }, - "storage": { - "str:chainSpecificTokenIds|nested:str:WUSDC-abcdef|str:.len": "1", - "str:chainSpecificTokenIds|nested:str:WUSDC-abcdef|str:.index|nested:str:USDC-cccccc": "1", - "str:chainSpecificTokenIds|nested:str:WUSDC-abcdef|str:.item|u32:1": "str:USDC-cccccc", - "str:chainSpecificToUniversalMapping|nested:str:USDC-cccccc": "str:WUSDC-abcdef", - "str:universalBridgedTokenIds.len": "1", - "str:universalBridgedTokenIds.index|nested:str:WUSDC-abcdef": "1", - "str:universalBridgedTokenIds.item|u32:1": "str:WUSDC-abcdef", - "str:tokenLiquidity|nested:str:USDC-aaaaaa": "300000000000000", - "str:tokenLiquidity|nested:str:USDC-cccccc": "100000000000000", - "str:token_decimals_num|nested:str:WUSDC-abcdef": "6", - "str:token_decimals_num|nested:str:USDC-cccccc": "18" - }, - "code": "file:../output/bridged-tokens-wrapper.wasm", - "owner": "address:owner" - } - } } ] -} +} \ No newline at end of file diff --git a/bridged-tokens-wrapper/scenarios/remove_wrapped_token.scen.json b/bridged-tokens-wrapper/scenarios/remove_wrapped_token.scen.json index 79970914..8f6f2f3d 100644 --- a/bridged-tokens-wrapper/scenarios/remove_wrapped_token.scen.json +++ b/bridged-tokens-wrapper/scenarios/remove_wrapped_token.scen.json @@ -41,74 +41,11 @@ "gasPrice": "0" }, "expect": { - "status": "0", - "message": "", + "status": "4", + "message": "str:Cannot remove wrapped token due to remaining liquidity", "gas": "*", "refund": "*" } - }, - { - "step": "checkState", - "accounts": { - "address:owner": { - "nonce": "6", - "balance": "0", - "storage": {} - }, - "address:user": { - "nonce": "5", - "esdt": { - "str:USDC-aaaaaa": { - "balance": "200000000000000" - }, - "str:USDC-bbbbbb": { - "balance": "500000000000000" - }, - "str:USDC-cccccc": { - "balance": "400000000000000" - }, - "str:WUSDC-abcdef": { - "balance": "900" - } - }, - "storage": {} - }, - "sc:bridged_tokens_wrapper": { - "nonce": "0", - "esdt": { - "str:WUSDC-abcdef": { - "balance": "1", - "roles": [ - "ESDTRoleLocalMint", - "ESDTRoleLocalBurn" - ] - }, - "str:WUSDC-uvwxyz": { - "balance": "1", - "roles": [ - "ESDTRoleLocalMint", - "ESDTRoleLocalBurn" - ] - }, - "str:USDC-aaaaaa": { - "balance": "300000000000000" - }, - "str:USDC-cccccc": { - "balance": "100000000000000" - } - }, - "storage": { - "str:universalBridgedTokenIds.len": "1", - "str:universalBridgedTokenIds.index|nested:str:WUSDC-uvwxyz": "1", - "str:universalBridgedTokenIds.item|u32:1": "str:WUSDC-uvwxyz", - "str:tokenLiquidity|nested:str:USDC-aaaaaa": "300000000000000", - "str:tokenLiquidity|nested:str:USDC-cccccc": "100000000000000", - "str:token_decimals_num|nested:str:WUSDC-uvwxyz": "18" - }, - "code": "file:../output/bridged-tokens-wrapper.wasm", - "owner": "address:owner" - } - } } ] -} +} \ No newline at end of file diff --git a/bridged-tokens-wrapper/scenarios/setup.scen.json b/bridged-tokens-wrapper/scenarios/setup.scen.json index 6a11bc37..ad7c5348 100644 --- a/bridged-tokens-wrapper/scenarios/setup.scen.json +++ b/bridged-tokens-wrapper/scenarios/setup.scen.json @@ -105,10 +105,10 @@ "str:tokenLiquidity|nested:str:USDC-cccccc": "100000000000000", "str:tokenLiquidity|nested:str:USDC-eeeeee": "400000000000000", "str:tokenLiquidity|nested:str:USDC-ffffff": "400000000000000", - "str:token_decimals_num|nested:str:WUSDC-abcdef": "6", - "str:token_decimals_num|nested:str:WUSDC-ghijkl": "6", - "str:token_decimals_num|nested:str:USDC-aaaaaa": "18", - "str:token_decimals_num|nested:str:USDC-cccccc": "18" + "str:tokenDecimalsNum|nested:str:WUSDC-abcdef": "6", + "str:tokenDecimalsNum|nested:str:WUSDC-ghijkl": "6", + "str:tokenDecimalsNum|nested:str:USDC-aaaaaa": "18", + "str:tokenDecimalsNum|nested:str:USDC-cccccc": "18" }, "code": "file:../output/bridged-tokens-wrapper.wasm", "owner": "address:owner" @@ -218,10 +218,10 @@ "str:tokenLiquidity|nested:str:USDC-cccccc": "100000000000000", "str:tokenLiquidity|nested:str:USDC-eeeeee": "400000000000000", "str:tokenLiquidity|nested:str:USDC-ffffff": "400000000000000", - "str:token_decimals_num|nested:str:WUSDC-abcdef": "6", - "str:token_decimals_num|nested:str:WUSDC-ghijkl": "6", - "str:token_decimals_num|nested:str:USDC-aaaaaa": "18", - "str:token_decimals_num|nested:str:USDC-cccccc": "18" + "str:tokenDecimalsNum|nested:str:WUSDC-abcdef": "6", + "str:tokenDecimalsNum|nested:str:WUSDC-ghijkl": "6", + "str:tokenDecimalsNum|nested:str:USDC-aaaaaa": "18", + "str:tokenDecimalsNum|nested:str:USDC-cccccc": "18" }, "code": "file:../output/bridged-tokens-wrapper.wasm", "owner": "address:owner" @@ -229,4 +229,4 @@ } } ] -} +} \ No newline at end of file diff --git a/bridged-tokens-wrapper/scenarios/unwrap_token.scen.json b/bridged-tokens-wrapper/scenarios/unwrap_token.scen.json index 819c3ecd..affc4b5c 100644 --- a/bridged-tokens-wrapper/scenarios/unwrap_token.scen.json +++ b/bridged-tokens-wrapper/scenarios/unwrap_token.scen.json @@ -31,31 +31,6 @@ "refund": "*" } }, - { - "step": "scCall", - "txId": "unwrap-token-not-enough-funds", - "tx": { - "from": "address:user", - "to": "sc:bridged_tokens_wrapper", - "value": "0", - "esdt": { - "tokenIdentifier": "str:WUSDC-abcdef", - "value": "500" - }, - "function": "unwrapToken", - "arguments": [ - "str:USDC-aaaaaa" - ], - "gasLimit": "5,000,000", - "gasPrice": "0" - }, - "expect": { - "status": "4", - "message": "str:Contract does not have enough funds", - "gas": "*", - "refund": "*" - } - }, { "step": "scCall", "txId": "unwrap-token-requires_updating", @@ -81,31 +56,6 @@ "refund": "*" } }, - { - "step": "scCall", - "txId": "chain-token-requires_updating", - "tx": { - "from": "address:user", - "to": "sc:bridged_tokens_wrapper", - "value": "0", - "esdt": { - "tokenIdentifier": "str:WUSDC-ghijkl", - "value": "500" - }, - "function": "unwrapToken", - "arguments": [ - "str:USDC-ffffff" - ], - "gasLimit": "5,000,000", - "gasPrice": "0" - }, - "expect": { - "status": "4", - "message": "str:Chain-specific token requires updating", - "gas": "*", - "refund": "*" - } - }, { "step": "scCall", "txId": "unwrap-token", @@ -140,7 +90,7 @@ "storage": {} }, "address:user": { - "nonce": "12", + "nonce": "10", "esdt": { "str:USDC-aaaaaa": { "balance": "400000000000000" @@ -234,10 +184,10 @@ "str:tokenLiquidity|nested:str:USDC-cccccc": "100000000000000", "str:tokenLiquidity|nested:str:USDC-eeeeee": "400000000000000", "str:tokenLiquidity|nested:str:USDC-ffffff": "400000000000000", - "str:token_decimals_num|nested:str:WUSDC-abcdef": "6", - "str:token_decimals_num|nested:str:WUSDC-ghijkl": "6", - "str:token_decimals_num|nested:str:USDC-aaaaaa": "18", - "str:token_decimals_num|nested:str:USDC-cccccc": "18" + "str:tokenDecimalsNum|nested:str:WUSDC-abcdef": "6", + "str:tokenDecimalsNum|nested:str:WUSDC-ghijkl": "6", + "str:tokenDecimalsNum|nested:str:USDC-aaaaaa": "18", + "str:tokenDecimalsNum|nested:str:USDC-cccccc": "18" }, "code": "file:../output/bridged-tokens-wrapper.wasm", "owner": "address:owner" @@ -245,4 +195,4 @@ } } ] -} +} \ No newline at end of file diff --git a/bridged-tokens-wrapper/scenarios/whitelist_token.scen.json b/bridged-tokens-wrapper/scenarios/whitelist_token.scen.json index 5a05ecaa..2d85e971 100644 --- a/bridged-tokens-wrapper/scenarios/whitelist_token.scen.json +++ b/bridged-tokens-wrapper/scenarios/whitelist_token.scen.json @@ -127,9 +127,9 @@ "str:universalBridgedTokenIds.len": "1", "str:universalBridgedTokenIds.index|nested:str:WUSDC-abcdef": "1", "str:universalBridgedTokenIds.item|u32:1": "str:WUSDC-abcdef", - "str:token_decimals_num|nested:str:WUSDC-abcdef": "6", - "str:token_decimals_num|nested:str:USDC-aaaaaa": "18", - "str:token_decimals_num|nested:str:USDC-cccccc": "18" + "str:tokenDecimalsNum|nested:str:WUSDC-abcdef": "6", + "str:tokenDecimalsNum|nested:str:USDC-aaaaaa": "18", + "str:tokenDecimalsNum|nested:str:USDC-cccccc": "18" }, "code": "file:../output/bridged-tokens-wrapper.wasm", "owner": "address:owner" @@ -137,4 +137,4 @@ } } ] -} +} \ No newline at end of file diff --git a/bridged-tokens-wrapper/scenarios/wrap_token.scen.json b/bridged-tokens-wrapper/scenarios/wrap_token.scen.json index 65a5dfba..8ab688c4 100644 --- a/bridged-tokens-wrapper/scenarios/wrap_token.scen.json +++ b/bridged-tokens-wrapper/scenarios/wrap_token.scen.json @@ -65,7 +65,7 @@ "function": "wrapTokens", "esdt": { "tokenIdentifier": "str:USDC-cccccc", - "value": "100000000000000" + "value": "100,000,000,000,000" }, "arguments": [], "gasLimit": "5,000,000", @@ -144,9 +144,9 @@ "str:universalBridgedTokenIds.item|u32:1": "str:WUSDC-abcdef", "str:tokenLiquidity|nested:str:USDC-aaaaaa": "300000000000000", "str:tokenLiquidity|nested:str:USDC-cccccc": "100000000000000", - "str:token_decimals_num|nested:str:WUSDC-abcdef": "6", - "str:token_decimals_num|nested:str:USDC-aaaaaa": "18", - "str:token_decimals_num|nested:str:USDC-cccccc": "18" + "str:tokenDecimalsNum|nested:str:WUSDC-abcdef": "6", + "str:tokenDecimalsNum|nested:str:USDC-aaaaaa": "18", + "str:tokenDecimalsNum|nested:str:USDC-cccccc": "18" }, "code": "file:../output/bridged-tokens-wrapper.wasm", "owner": "address:owner" @@ -154,4 +154,4 @@ } } ] -} +} \ No newline at end of file diff --git a/bridged-tokens-wrapper/src/lib.rs b/bridged-tokens-wrapper/src/lib.rs index fe88ddc0..73428a24 100644 --- a/bridged-tokens-wrapper/src/lib.rs +++ b/bridged-tokens-wrapper/src/lib.rs @@ -30,6 +30,12 @@ pub trait BridgedTokensWrapper: #[only_owner] #[endpoint(addWrappedToken)] fn add_wrapped_token(&self, universal_bridged_token_ids: TokenIdentifier, num_decimals: u32) { + require!( + !self + .universal_bridged_token_ids() + .contains(&universal_bridged_token_ids), + "Token already added" + ); self.require_mint_and_burn_roles(&universal_bridged_token_ids); self.token_decimals_num(&universal_bridged_token_ids) .set(num_decimals); @@ -62,6 +68,11 @@ pub trait BridgedTokensWrapper: let mut chain_specific_tokens = self.chain_specific_token_ids(&universal_bridged_token_ids); for token in chain_specific_tokens.iter() { + let token_liquidity = self.token_liquidity(&token).get(); + require!( + token_liquidity == 0, + "Cannot remove wrapped token due to remaining liquidity" + ); self.chain_specific_to_universal_mapping(&token).clear(); self.token_decimals_num(&token).clear(); } @@ -102,26 +113,14 @@ pub trait BridgedTokensWrapper: } #[only_owner] - #[endpoint(updateWhitelistedToken)] - fn update_whitelisted_token( - &self, - chain_specific_token_id: TokenIdentifier, - chain_specific_token_decimals: u32, - ) { - let chain_to_universal_mapper = - self.chain_specific_to_universal_mapping(&chain_specific_token_id); + #[endpoint(blacklistToken)] + fn blacklist_token(&self, chain_specific_token_id: TokenIdentifier) { + let token_liquidity = self.token_liquidity(&chain_specific_token_id).get(); require!( - !chain_to_universal_mapper.is_empty(), - "Chain-specific token was not whitelisted yet" + token_liquidity == 0, + "Cannot blacklist token due to remaining liquidity" ); - self.token_decimals_num(&chain_specific_token_id) - .set(chain_specific_token_decimals); - } - - #[only_owner] - #[endpoint(blacklistToken)] - fn blacklist_token(&self, chain_specific_token_id: TokenIdentifier) { let chain_to_universal_mapper = self.chain_specific_to_universal_mapping(&chain_specific_token_id); @@ -135,10 +134,18 @@ pub trait BridgedTokensWrapper: self.token_decimals_num(&chain_specific_token_id).clear(); } + #[only_owner] #[payable("*")] #[endpoint(depositLiquidity)] fn deposit_liquidity(&self) { let (payment_token, payment_amount) = self.call_value().single_fungible_esdt(); + require!( + !self + .chain_specific_to_universal_mapping(&payment_token) + .is_empty(), + "Provided token ID is not registered as a chain specific token" + ); + self.token_liquidity(&payment_token) .update(|liq| *liq += payment_amount); } @@ -156,6 +163,10 @@ pub trait BridgedTokensWrapper: let mut new_payments = ManagedVec::new(); for payment in &original_payments { + require!( + payment.token_nonce == 0, + "Only fungible tokens accepted for wrapping" + ); let universal_token_id_mapper = self.chain_specific_to_universal_mapping(&payment.token_identifier); diff --git a/bridged-tokens-wrapper/tests/bridged_tokens_wrapper_whitebox_test.rs b/bridged-tokens-wrapper/tests/bridged_tokens_wrapper_whitebox_test.rs index a36bd34d..a462f098 100644 --- a/bridged-tokens-wrapper/tests/bridged_tokens_wrapper_whitebox_test.rs +++ b/bridged-tokens-wrapper/tests/bridged_tokens_wrapper_whitebox_test.rs @@ -203,6 +203,9 @@ fn test_deposit_liquidity_should_work() { ), |sc| { sc.set_paused(false); + sc.chain_specific_to_universal_mapping(&managed_token_id!(UNIVERSAL_TOKEN_IDENTIFIER)) + .set(managed_token_id!(UNIVERSAL_TOKEN_IDENTIFIER)); + sc.deposit_liquidity(); let result = sc .token_liquidity(&managed_token_id!(UNIVERSAL_TOKEN_IDENTIFIER)) @@ -391,9 +394,7 @@ fn test_unwrap_token_create_transaction_should_fail_case_1() { let address = convert_to_eth_address(ETH_ADDRESS); sc.unwrap_token_create_transaction( managed_token_id!(UNIVERSAL_TOKEN_IDENTIFIER), - ManagedAddress::new_from_bytes( - b"0102030405060708090a0b0c0d0e0f10", - ), + ManagedAddress::new_from_bytes(b"0102030405060708090a0b0c0d0e0f10"), address, ); }, @@ -431,9 +432,7 @@ fn test_unwrap_token_create_transaction_should_fail_case_2() { let address = convert_to_eth_address(ETH_ADDRESS); sc.unwrap_token_create_transaction( managed_token_id!(UNIVERSAL_TOKEN_IDENTIFIER), - ManagedAddress::new_from_bytes( - b"0102030405060708090a0b0c0d0e0f10", - ), + ManagedAddress::new_from_bytes(b"0102030405060708090a0b0c0d0e0f10"), address, ); }, @@ -514,12 +513,13 @@ fn test_unwrap_token_create_transaction_should_fail_case_4() { |sc| { sc.set_paused(false); sc.add_wrapped_token(managed_token_id!(UNIVERSAL_TOKEN_IDENTIFIER), NUM_DECIMALS); + sc.chain_specific_to_universal_mapping(&managed_token_id!(UNIVERSAL_TOKEN_IDENTIFIER)) + .set(managed_token_id!(UNIVERSAL_TOKEN_IDENTIFIER)); + sc.deposit_liquidity(); let result = sc .token_liquidity(&managed_token_id!(UNIVERSAL_TOKEN_IDENTIFIER)) .get(); - sc.chain_specific_to_universal_mapping(&managed_token_id!(UNIVERSAL_TOKEN_IDENTIFIER)) - .set(managed_token_id!(UNIVERSAL_TOKEN_IDENTIFIER)); assert_ne!(result, 0); }, ); @@ -586,9 +586,10 @@ fn test_unwrap_token_create_transaction_should_work() { |sc| { sc.set_paused(false); sc.add_wrapped_token(managed_token_id!(UNIVERSAL_TOKEN_IDENTIFIER), NUM_DECIMALS); - sc.deposit_liquidity(); sc.chain_specific_to_universal_mapping(&managed_token_id!(UNIVERSAL_TOKEN_IDENTIFIER)) .set(managed_token_id!(UNIVERSAL_TOKEN_IDENTIFIER)); + + sc.deposit_liquidity(); }, ); } @@ -658,57 +659,6 @@ fn test_whitelist_token_should_work() { ); } -#[test] -fn test_update_whitelisted_token_should_fail_case_1() { - let mut world = setup(); - let bridged_tokens_wrapper = WhiteboxContract::new( - BRIDGE_TOKENS_WRAPPER_ADDRESS_EXPR, - bridged_tokens_wrapper::contract_obj, - ); - - world.whitebox_call_check( - &bridged_tokens_wrapper, - ScCallStep::new() - .from(OWNER_ADDRESS_EXPR) - .expect(TxExpect::user_error( - "str:Chain-specific token was not whitelisted yet", - )), - |sc| { - sc.update_whitelisted_token(managed_token_id!(CHAIN_TOKEN_IDENTIFIER), 18u32); - }, - |r| r.assert_user_error("Chain-specific token was not whitelisted yet"), - ); -} - -#[test] -fn test_update_whitelisted_token_should_work() { - let mut world = setup(); - let bridged_tokens_wrapper = WhiteboxContract::new( - BRIDGE_TOKENS_WRAPPER_ADDRESS_EXPR, - bridged_tokens_wrapper::contract_obj, - ); - let contract_address = AddressValue::from(BRIDGE_TOKENS_WRAPPER_ADDRESS_EXPR); - - world.set_esdt_local_roles( - managed_address!(&contract_address.to_address()), - UNIVERSAL_TOKEN_IDENTIFIER, - &[EsdtLocalRole::Mint, EsdtLocalRole::Burn], - ); - - world.whitebox_call( - &bridged_tokens_wrapper, - ScCallStep::new().from(OWNER_ADDRESS_EXPR), - |sc| { - sc.whitelist_token( - managed_token_id!(CHAIN_TOKEN_IDENTIFIER), - NUM_DECIMALS, - managed_token_id!(UNIVERSAL_TOKEN_IDENTIFIER), - ); - sc.update_whitelisted_token(managed_token_id!(CHAIN_TOKEN_IDENTIFIER), 12); - }, - ); -} - #[test] fn test_blacklist_token_should_work() { let mut world = setup(); @@ -1008,9 +958,10 @@ fn test_unwrap_token_should_fail_case_4() { |sc| { sc.set_paused(false); sc.add_wrapped_token(managed_token_id!(CHAIN_TOKEN_IDENTIFIER), NUM_DECIMALS); - sc.deposit_liquidity(); sc.chain_specific_to_universal_mapping(&managed_token_id!(CHAIN_TOKEN_IDENTIFIER)) .set(managed_token_id!(CHAIN_TOKEN_IDENTIFIER)); + + sc.deposit_liquidity(); }, ); @@ -1067,9 +1018,10 @@ fn test_unwrap_token_should_work() { |sc| { sc.set_paused(false); sc.add_wrapped_token(managed_token_id!(CHAIN_TOKEN_IDENTIFIER), NUM_DECIMALS); - sc.deposit_liquidity(); sc.chain_specific_to_universal_mapping(&managed_token_id!(CHAIN_TOKEN_IDENTIFIER)) .set(managed_token_id!(CHAIN_TOKEN_IDENTIFIER)); + + sc.deposit_liquidity(); }, ); diff --git a/bridged-tokens-wrapper/wasm/Cargo.lock b/bridged-tokens-wrapper/wasm/Cargo.lock index b9c24aa6..1a3bb6a8 100644 --- a/bridged-tokens-wrapper/wasm/Cargo.lock +++ b/bridged-tokens-wrapper/wasm/Cargo.lock @@ -25,6 +25,7 @@ name = "bridged-tokens-wrapper" version = "0.0.0" dependencies = [ "eth-address", + "mock-esdt-safe", "multiversx-sc", "multiversx-sc-modules", "sc-proxies", @@ -75,6 +76,15 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" +[[package]] +name = "mock-esdt-safe" +version = "0.0.0" +dependencies = [ + "eth-address", + "multiversx-sc", + "multiversx-sc-modules", +] + [[package]] name = "multiversx-sc" version = "0.53.2" diff --git a/bridged-tokens-wrapper/wasm/src/lib.rs b/bridged-tokens-wrapper/wasm/src/lib.rs index cdad1731..62c77484 100644 --- a/bridged-tokens-wrapper/wasm/src/lib.rs +++ b/bridged-tokens-wrapper/wasm/src/lib.rs @@ -6,9 +6,9 @@ // Init: 1 // Upgrade: 1 -// Endpoints: 17 +// Endpoints: 16 // Async Callback (empty): 1 -// Total number of exported functions: 20 +// Total number of exported functions: 19 #![no_std] @@ -24,7 +24,6 @@ multiversx_sc_wasm_adapter::endpoints! { updateWrappedToken => update_wrapped_token removeWrappedToken => remove_wrapped_token whitelistToken => whitelist_token - updateWhitelistedToken => update_whitelisted_token blacklistToken => blacklist_token depositLiquidity => deposit_liquidity wrapTokens => wrap_tokens diff --git a/common/sc-proxies/src/bridged_tokens_wrapper_proxy.rs b/common/sc-proxies/src/bridged_tokens_wrapper_proxy.rs index ac306e11..07b3185d 100644 --- a/common/sc-proxies/src/bridged_tokens_wrapper_proxy.rs +++ b/common/sc-proxies/src/bridged_tokens_wrapper_proxy.rs @@ -145,22 +145,6 @@ where .original_result() } - pub fn update_whitelisted_token< - Arg0: ProxyArg>, - Arg1: ProxyArg, - >( - self, - chain_specific_token_id: Arg0, - chain_specific_token_decimals: Arg1, - ) -> TxTypedCall { - self.wrapped_tx - .payment(NotPayable) - .raw_call("updateWhitelistedToken") - .argument(&chain_specific_token_id) - .argument(&chain_specific_token_decimals) - .original_result() - } - pub fn blacklist_token< Arg0: ProxyArg>, >( From 70a9a3519a13a51a7abfb242ebf91c48f9c05f71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Wed, 20 Nov 2024 00:10:15 +0200 Subject: [PATCH 06/45] esdt-safe fix: distribute_fees --- common/token-module/src/lib.rs | 60 ++++++++++++++++++++++------------ 1 file changed, 40 insertions(+), 20 deletions(-) diff --git a/common/token-module/src/lib.rs b/common/token-module/src/lib.rs index 4366d5ae..335f2a6b 100644 --- a/common/token-module/src/lib.rs +++ b/common/token-module/src/lib.rs @@ -27,9 +27,8 @@ pub trait TokenModule: fn distribute_fees( &self, address_percentage_pairs: ManagedVec>, + opt_tokens_to_distribute: OptionalValue>>, ) { - let percentage_total = BigUint::from(PERCENTAGE_TOTAL); - let mut percentage_sum = 0u64; for pair in &address_percentage_pairs { percentage_sum += pair.percentage as u64; @@ -39,31 +38,50 @@ pub trait TokenModule: INVALID_PERCENTAGE_SUM_OVER_ERR_MSG ); - for token_id in self.token_whitelist().iter() { - let accumulated_fees = self.accumulated_transaction_fees(&token_id).get(); - if accumulated_fees == 0u32 { - continue; + match opt_tokens_to_distribute { + OptionalValue::Some(tokens_to_distrbute) => { + for token_id in tokens_to_distrbute { + self.distribute_fees_token(&address_percentage_pairs, token_id); + } + } + OptionalValue::None => { + for token_id in self.token_whitelist().iter() { + self.distribute_fees_token(&address_percentage_pairs, token_id); + } } + }; + } - let mut remaining_fees = accumulated_fees.clone(); + fn distribute_fees_token( + &self, + address_percentage_pairs: &ManagedVec>, + token_id: TokenIdentifier, + ) { + let percentage_total = BigUint::from(PERCENTAGE_TOTAL); - for pair in &address_percentage_pairs { - let amount_to_send = - &(&accumulated_fees * &BigUint::from(pair.percentage)) / &percentage_total; + let accumulated_fees = self.accumulated_transaction_fees(&token_id).get(); + if accumulated_fees == 0u32 { + return; + } - if amount_to_send > 0 { - remaining_fees -= &amount_to_send; + let mut remaining_fees = accumulated_fees.clone(); - self.tx() - .to(&pair.address) - .single_esdt(&token_id, 0, &amount_to_send) - .transfer(); - } - } + for pair in address_percentage_pairs { + let amount_to_send = + &(&accumulated_fees * &BigUint::from(pair.percentage)) / &percentage_total; + + if amount_to_send > 0 { + remaining_fees -= &amount_to_send; - self.accumulated_transaction_fees(&token_id) - .set(&remaining_fees); + self.tx() + .to(&pair.address) + .single_esdt(&token_id, 0, &amount_to_send) + .transfer(); + } } + + self.accumulated_transaction_fees(&token_id) + .set(&remaining_fees); } #[only_owner] @@ -111,6 +129,8 @@ pub trait TokenModule: burn_balance == &BigUint::zero(), "Stored tokens must have 0 burn balance!" ); + let tokens_received = self.call_value().single_esdt(); + require!(tokens_received.amount == *total_balance, "Wrong payment"); if total_balance > &BigUint::zero() { self.init_supply(token_id, total_balance); } From 50657a45e0c9f04a6b1240225c23f8f2ee4a3985 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Wed, 20 Nov 2024 00:11:20 +0200 Subject: [PATCH 07/45] esdt-safe: Add failedRefund storage mapper --- esdt-safe/src/lib.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/esdt-safe/src/lib.rs b/esdt-safe/src/lib.rs index c7aad34a..4ae583ce 100644 --- a/esdt-safe/src/lib.rs +++ b/esdt-safe/src/lib.rs @@ -195,6 +195,7 @@ pub trait EsdtSafe: }; if refund_tx.amount <= required_fee { + self.failed_refunds().push(&refund_tx); continue; } @@ -687,4 +688,7 @@ pub trait EsdtSafe: address: &ManagedAddress, token_id: &TokenIdentifier, ) -> SingleValueMapper; + + #[storage_mapper("failedRefunds")] + fn failed_refunds(&self) -> VecMapper>; } From 939ad9d2e05ccb292e9dbf960edeee4ddc8804b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Wed, 20 Nov 2024 00:13:09 +0200 Subject: [PATCH 08/45] code refactor, compilation, wasm files, Cargo.locks --- bridge-proxy/wasm/Cargo.lock | 12 ++++++++++++ bridge-proxy/wasm/src/lib.rs | 4 ++-- .../mock-esdt-safe/wasm/Cargo.lock | 18 ++++++++++++++++++ .../mock-esdt-safe/wasm/Cargo.toml | 2 +- .../mock-esdt-safe/wasm/src/lib.rs | 5 +++-- common/tx-batch-module/src/lib.rs | 12 ++++++++---- common/tx-batch-module/src/tx_batch_mapper.rs | 3 +++ esdt-safe/wasm/Cargo.lock | 8 ++++++++ multi-transfer-esdt/src/lib.rs | 19 +++++++++++-------- multi-transfer-esdt/wasm/Cargo.lock | 14 ++++++++++++++ multisig/wasm/Cargo.lock | 14 ++++++++++++++ 11 files changed, 94 insertions(+), 17 deletions(-) diff --git a/bridge-proxy/wasm/Cargo.lock b/bridge-proxy/wasm/Cargo.lock index 27a44e1f..bb3f7599 100644 --- a/bridge-proxy/wasm/Cargo.lock +++ b/bridge-proxy/wasm/Cargo.lock @@ -33,6 +33,7 @@ dependencies = [ "mock-multi-transfer-esdt", "mock-multisig", "mock-price-aggregator", + "mock-proxies", "multiversx-sc", "multiversx-sc-modules", "sc-proxies", @@ -55,6 +56,7 @@ name = "bridged-tokens-wrapper" version = "0.0.0" dependencies = [ "eth-address", + "mock-esdt-safe", "multiversx-sc", "multiversx-sc-modules", "sc-proxies", @@ -108,6 +110,7 @@ dependencies = [ "mock-multi-transfer-esdt", "mock-multisig", "mock-price-aggregator", + "mock-proxies", "multiversx-price-aggregator-sc", "multiversx-sc", "multiversx-sc-modules", @@ -205,7 +208,9 @@ dependencies = [ name = "mock-esdt-safe" version = "0.0.0" dependencies = [ + "eth-address", "multiversx-sc", + "multiversx-sc-modules", ] [[package]] @@ -229,6 +234,13 @@ dependencies = [ "multiversx-sc", ] +[[package]] +name = "mock-proxies" +version = "0.0.0" +dependencies = [ + "multiversx-sc", +] + [[package]] name = "multiversx-price-aggregator-sc" version = "0.53.2" diff --git a/bridge-proxy/wasm/src/lib.rs b/bridge-proxy/wasm/src/lib.rs index 54e773c8..97c28d7c 100644 --- a/bridge-proxy/wasm/src/lib.rs +++ b/bridge-proxy/wasm/src/lib.rs @@ -6,10 +6,10 @@ // Init: 1 // Upgrade: 1 -// Endpoints: 9 +// Endpoints: 8 // Async Callback (empty): 1 // Promise callbacks: 1 -// Total number of exported functions: 13 +// Total number of exported functions: 12 #![no_std] diff --git a/common/mock-contracts/mock-esdt-safe/wasm/Cargo.lock b/common/mock-contracts/mock-esdt-safe/wasm/Cargo.lock index ba9f94e7..64083fe1 100644 --- a/common/mock-contracts/mock-esdt-safe/wasm/Cargo.lock +++ b/common/mock-contracts/mock-esdt-safe/wasm/Cargo.lock @@ -26,6 +26,13 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" +[[package]] +name = "eth-address" +version = "0.0.0" +dependencies = [ + "multiversx-sc", +] + [[package]] name = "hex" version = "0.4.3" @@ -42,7 +49,9 @@ checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" name = "mock-esdt-safe" version = "0.0.0" dependencies = [ + "eth-address", "multiversx-sc", + "multiversx-sc-modules", ] [[package]] @@ -103,6 +112,15 @@ dependencies = [ "syn", ] +[[package]] +name = "multiversx-sc-modules" +version = "0.53.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daeb48acbd39255868a3241798df2f85050f0ae8d82d6417bd2cd0e30a241855" +dependencies = [ + "multiversx-sc", +] + [[package]] name = "multiversx-sc-wasm-adapter" version = "0.53.2" diff --git a/common/mock-contracts/mock-esdt-safe/wasm/Cargo.toml b/common/mock-contracts/mock-esdt-safe/wasm/Cargo.toml index cf6bffc5..1c88bd9f 100644 --- a/common/mock-contracts/mock-esdt-safe/wasm/Cargo.toml +++ b/common/mock-contracts/mock-esdt-safe/wasm/Cargo.toml @@ -28,7 +28,7 @@ panic = "abort" path = ".." [dependencies.multiversx-sc-wasm-adapter] -version = "0.53.2" +version = "=0.53.2" [workspace] members = ["."] diff --git a/common/mock-contracts/mock-esdt-safe/wasm/src/lib.rs b/common/mock-contracts/mock-esdt-safe/wasm/src/lib.rs index e6872d67..7c9a8414 100644 --- a/common/mock-contracts/mock-esdt-safe/wasm/src/lib.rs +++ b/common/mock-contracts/mock-esdt-safe/wasm/src/lib.rs @@ -6,9 +6,9 @@ // Init: 1 // Upgrade: 1 -// Endpoints: 0 +// Endpoints: 1 // Async Callback (empty): 1 -// Total number of exported functions: 3 +// Total number of exported functions: 4 #![no_std] @@ -20,6 +20,7 @@ multiversx_sc_wasm_adapter::endpoints! { ( init => init upgrade => upgrade + createTransaction => create_transaction ) } diff --git a/common/tx-batch-module/src/lib.rs b/common/tx-batch-module/src/lib.rs index c033a911..3547d75b 100644 --- a/common/tx-batch-module/src/lib.rs +++ b/common/tx-batch-module/src/lib.rs @@ -9,6 +9,9 @@ use tx_batch_mapper::TxBatchMapper; pub mod batch_status; pub mod tx_batch_mapper; +const MAX_TX_BATCH_BLOCK_DURATION: u64 = 1000; +const MAX_TX_BATCH_SIZE: usize = 20; + #[multiversx_sc::module] pub trait TxBatchModule { // endpoints - owner-only @@ -17,8 +20,8 @@ pub trait TxBatchModule { #[endpoint(setMaxTxBatchSize)] fn set_max_tx_batch_size(&self, new_max_tx_batch_size: usize) { require!( - new_max_tx_batch_size > 0, - "Max tx batch size must be more than 0" + new_max_tx_batch_size > 0 && new_max_tx_batch_size < MAX_TX_BATCH_SIZE, + "Max tx batch size out of limits" ); self.max_tx_batch_size().set(new_max_tx_batch_size); @@ -28,8 +31,9 @@ pub trait TxBatchModule { #[endpoint(setMaxTxBatchBlockDuration)] fn set_max_tx_batch_block_duration(&self, new_max_tx_batch_block_duration: u64) { require!( - new_max_tx_batch_block_duration > 0, - "Max tx batch block duration must be more than 0" + new_max_tx_batch_block_duration > 0 + && new_max_tx_batch_block_duration < MAX_TX_BATCH_BLOCK_DURATION, + "Max tx batch block duration out of limits" ); self.max_tx_batch_block_duration() diff --git a/common/tx-batch-module/src/tx_batch_mapper.rs b/common/tx-batch-module/src/tx_batch_mapper.rs index 589d7c08..1b72bb2d 100644 --- a/common/tx-batch-module/src/tx_batch_mapper.rs +++ b/common/tx-batch-module/src/tx_batch_mapper.rs @@ -48,6 +48,9 @@ where { fn clear(&mut self) { self.vec_mapper.clear(); + self.vec_len = 0; + self.first_tx = None; + self.last_tx = None; } } diff --git a/esdt-safe/wasm/Cargo.lock b/esdt-safe/wasm/Cargo.lock index 0ddbdbb0..475f88df 100644 --- a/esdt-safe/wasm/Cargo.lock +++ b/esdt-safe/wasm/Cargo.lock @@ -56,6 +56,7 @@ dependencies = [ "mock-multi-transfer-esdt", "mock-multisig", "mock-price-aggregator", + "mock-proxies", "multiversx-price-aggregator-sc", "multiversx-sc", "multiversx-sc-modules", @@ -178,6 +179,13 @@ dependencies = [ "multiversx-sc", ] +[[package]] +name = "mock-proxies" +version = "0.0.0" +dependencies = [ + "multiversx-sc", +] + [[package]] name = "multiversx-price-aggregator-sc" version = "0.53.2" diff --git a/multi-transfer-esdt/src/lib.rs b/multi-transfer-esdt/src/lib.rs index 70a1c3bf..d063d4ba 100644 --- a/multi-transfer-esdt/src/lib.rs +++ b/multi-transfer-esdt/src/lib.rs @@ -87,14 +87,17 @@ pub trait MultiTransferEsdt: } // emit event before the actual transfer so we don't have to save the tx_nonces as well - self.transfer_performed_event( - batch_id, - eth_tx.from.clone(), - eth_tx.to.clone(), - eth_tx.token_id.clone(), - eth_tx.amount.clone(), - eth_tx.tx_nonce, - ); + // emit events only for non-SC destinations + if self.blockchain().is_smart_contract(ð_tx.to) { + self.transfer_performed_event( + batch_id, + eth_tx.from.clone(), + eth_tx.to.clone(), + eth_tx.token_id.clone(), + eth_tx.amount.clone(), + eth_tx.tx_nonce, + ); + } valid_tx_list.push(eth_tx.clone()); valid_payments_list.push(EsdtTokenPayment::new(eth_tx.token_id, 0, eth_tx.amount)); diff --git a/multi-transfer-esdt/wasm/Cargo.lock b/multi-transfer-esdt/wasm/Cargo.lock index 307057d2..620bff73 100644 --- a/multi-transfer-esdt/wasm/Cargo.lock +++ b/multi-transfer-esdt/wasm/Cargo.lock @@ -33,6 +33,7 @@ dependencies = [ "mock-multi-transfer-esdt", "mock-multisig", "mock-price-aggregator", + "mock-proxies", "multiversx-sc", "multiversx-sc-modules", "sc-proxies", @@ -47,6 +48,7 @@ name = "bridged-tokens-wrapper" version = "0.0.0" dependencies = [ "eth-address", + "mock-esdt-safe", "multiversx-sc", "multiversx-sc-modules", "sc-proxies", @@ -100,6 +102,7 @@ dependencies = [ "mock-multi-transfer-esdt", "mock-multisig", "mock-price-aggregator", + "mock-proxies", "multiversx-price-aggregator-sc", "multiversx-sc", "multiversx-sc-modules", @@ -197,7 +200,9 @@ dependencies = [ name = "mock-esdt-safe" version = "0.0.0" dependencies = [ + "eth-address", "multiversx-sc", + "multiversx-sc-modules", ] [[package]] @@ -221,6 +226,13 @@ dependencies = [ "multiversx-sc", ] +[[package]] +name = "mock-proxies" +version = "0.0.0" +dependencies = [ + "multiversx-sc", +] + [[package]] name = "multi-transfer-esdt" version = "0.0.0" @@ -230,8 +242,10 @@ dependencies = [ "esdt-safe", "eth-address", "max-bridged-amount-module", + "mock-multi-transfer-esdt", "mock-multisig", "mock-price-aggregator", + "mock-proxies", "multiversx-sc", "multiversx-sc-modules", "sc-proxies", diff --git a/multisig/wasm/Cargo.lock b/multisig/wasm/Cargo.lock index 25373d30..5af96c1d 100644 --- a/multisig/wasm/Cargo.lock +++ b/multisig/wasm/Cargo.lock @@ -33,6 +33,7 @@ dependencies = [ "mock-multi-transfer-esdt", "mock-multisig", "mock-price-aggregator", + "mock-proxies", "multiversx-sc", "multiversx-sc-modules", "sc-proxies", @@ -47,6 +48,7 @@ name = "bridged-tokens-wrapper" version = "0.0.0" dependencies = [ "eth-address", + "mock-esdt-safe", "multiversx-sc", "multiversx-sc-modules", "sc-proxies", @@ -100,6 +102,7 @@ dependencies = [ "mock-multi-transfer-esdt", "mock-multisig", "mock-price-aggregator", + "mock-proxies", "multiversx-price-aggregator-sc", "multiversx-sc", "multiversx-sc-modules", @@ -197,7 +200,9 @@ dependencies = [ name = "mock-esdt-safe" version = "0.0.0" dependencies = [ + "eth-address", "multiversx-sc", + "multiversx-sc-modules", ] [[package]] @@ -221,6 +226,13 @@ dependencies = [ "multiversx-sc", ] +[[package]] +name = "mock-proxies" +version = "0.0.0" +dependencies = [ + "multiversx-sc", +] + [[package]] name = "multi-transfer-esdt" version = "0.0.0" @@ -230,8 +242,10 @@ dependencies = [ "esdt-safe", "eth-address", "max-bridged-amount-module", + "mock-multi-transfer-esdt", "mock-multisig", "mock-price-aggregator", + "mock-proxies", "multiversx-sc", "multiversx-sc-modules", "sc-proxies", From f25b8ef7dcbd4d9fc71a0439d01c65067f82111d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Wed, 20 Nov 2024 00:35:25 +0200 Subject: [PATCH 09/45] esdt-safe: more audit fixes --- common/token-module/src/lib.rs | 25 +++++++++++++++++++++---- esdt-safe/src/lib.rs | 32 +++++++++++++++++++++++++++----- 2 files changed, 48 insertions(+), 9 deletions(-) diff --git a/common/token-module/src/lib.rs b/common/token-module/src/lib.rs index 335f2a6b..6ca196a6 100644 --- a/common/token-module/src/lib.rs +++ b/common/token-module/src/lib.rs @@ -98,6 +98,11 @@ pub trait TokenModule: burn_balance: &BigUint, opt_default_price_per_gas_unit: OptionalValue, ) { + require!( + !self.token_whitelist().contains(token_id), + "Token already whitelisted" + ); + self.token_ticker(token_id).set(&ticker); if let OptionalValue::Some(default_price_per_gas_unit) = opt_default_price_per_gas_unit { @@ -144,8 +149,6 @@ pub trait TokenModule: self.token_ticker(&token_id).clear(); self.default_price_per_gas_unit(&token_id).clear(); - self.mint_burn_token(&token_id).clear(); - self.native_token(&token_id).clear(); self.token_whitelist().swap_remove(&token_id); } @@ -214,7 +217,7 @@ pub trait TokenModule: ); require!( self.native_token(token_id).get(), - "Only native tokens can be stored!" + "Only native tokens can be stored" ); self.total_balances(token_id).update(|total| { @@ -231,13 +234,23 @@ pub trait TokenModule: burn_amount: &BigUint, ) { self.require_token_in_whitelist(token_id); + require!( + self.supply_mint_burn_initialized(token_id).is_empty(), + "Token already initialized" + ); require!( self.mint_burn_token(token_id).get(), - "Cannot init for non mintable/burnable tokens" + "Can init only for mintable/burnable tokens" + ); + + require!( + !self.native_token(token_id).get(), + "Cannot init native tokens" ); self.mint_balances(token_id).set(mint_amount); self.burn_balances(token_id).set(burn_amount); + self.supply_mint_burn_initialized(token_id).set(true); } // private @@ -310,4 +323,8 @@ pub trait TokenModule: #[view(getBurnBalances)] #[storage_mapper("burnBalances")] fn burn_balances(&self, token_id: &TokenIdentifier) -> SingleValueMapper; + + #[view(getsupplyMintBurnInitialized)] + #[storage_mapper("supplyMintBurnInitialized")] + fn supply_mint_burn_initialized(&self, token_id: &TokenIdentifier) -> SingleValueMapper; } diff --git a/esdt-safe/src/lib.rs b/esdt-safe/src/lib.rs index 4ae583ce..8c36da72 100644 --- a/esdt-safe/src/lib.rs +++ b/esdt-safe/src/lib.rs @@ -86,7 +86,7 @@ pub trait EsdtSafe: // set ticker for "GWEI" let gwei_token_id = TokenIdentifier::from(GWEI_STRING); self.token_ticker(&gwei_token_id) - .set(gwei_token_id.as_managed_buffer()); + .set_if_empty(gwei_token_id.as_managed_buffer()); self.set_paused(true); } @@ -522,18 +522,40 @@ pub trait EsdtSafe: fn get_refund_amounts( &self, address: ManagedAddress, + opt_tokens: OptionalValue>>, ) -> MultiValueEncoded> { let mut refund_amounts = MultiValueEncoded::new(); - for token_id in self.token_whitelist().iter() { - let amount = self.refund_amount(&address, &token_id).get(); - if amount > 0u32 { - refund_amounts.push((token_id, amount).into()); + match opt_tokens { + OptionalValue::Some(tokens) => { + for token_id in tokens { + self.get_refund_amount_for_token(&address, token_id, &mut refund_amounts); + } + } + OptionalValue::None => { + for token_id in self.token_whitelist().iter() { + let amount = self.refund_amount(&address, &token_id).get(); + if amount > 0u32 { + refund_amounts.push((token_id, amount).into()); + } + } } } refund_amounts } + fn get_refund_amount_for_token( + &self, + address: &ManagedAddress, + token_id: TokenIdentifier, + refund_amounts: &mut MultiValueEncoded>, + ) { + let amount = self.refund_amount(address, &token_id).get(); + if amount > 0u32 { + refund_amounts.push((token_id, amount).into()); + } + } + // views #[view(getTotalRefundAmounts)] From fe6ea589d60370bf1be69dca9f0d72195b9a2aba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Wed, 20 Nov 2024 00:42:45 +0200 Subject: [PATCH 10/45] bridge-proxy: audit fixes --- bridge-proxy/src/bridge-proxy.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/bridge-proxy/src/bridge-proxy.rs b/bridge-proxy/src/bridge-proxy.rs index 3ddd7c21..0d984ad4 100644 --- a/bridge-proxy/src/bridge-proxy.rs +++ b/bridge-proxy/src/bridge-proxy.rs @@ -118,6 +118,7 @@ pub trait BridgeProxyContract: self.tx().to(tx.to).payment(payment).transfer(); self.cleanup_transaction(tx_id); } + #[promises_callback] fn execution_callback(&self, #[call_result] result: ManagedAsyncCallResult<()>, tx_id: usize) { if result.is_err() { From e8ad7852700d477d905d3a52547fb53e6c692b32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Wed, 20 Nov 2024 01:32:10 +0200 Subject: [PATCH 11/45] esdt-safe: fix unit tests --- common/token-module/src/lib.rs | 11 +++++-- esdt-safe/tests/esdt_safe_blackbox_test.rs | 34 +++------------------- 2 files changed, 12 insertions(+), 33 deletions(-) diff --git a/common/token-module/src/lib.rs b/common/token-module/src/lib.rs index 6ca196a6..a795a4b8 100644 --- a/common/token-module/src/lib.rs +++ b/common/token-module/src/lib.rs @@ -134,8 +134,13 @@ pub trait TokenModule: burn_balance == &BigUint::zero(), "Stored tokens must have 0 burn balance!" ); - let tokens_received = self.call_value().single_esdt(); - require!(tokens_received.amount == *total_balance, "Wrong payment"); + let payments = self.call_value().all_esdt_transfers(); + if !payments.is_empty() { + let (payment_token_id, _, payment_token_amount) = payments.get(0).into_tuple(); + require!(payment_token_id == *token_id, "Wrong token id"); + require!(payment_token_amount == *total_balance, "Wrong payment"); + } + if total_balance > &BigUint::zero() { self.init_supply(token_id, total_balance); } @@ -235,7 +240,7 @@ pub trait TokenModule: ) { self.require_token_in_whitelist(token_id); require!( - self.supply_mint_burn_initialized(token_id).is_empty(), + self.supply_mint_burn_initialized(token_id).get() == false, "Token already initialized" ); require!( diff --git a/esdt-safe/tests/esdt_safe_blackbox_test.rs b/esdt-safe/tests/esdt_safe_blackbox_test.rs index ce8a98da..2cd1a3ad 100644 --- a/esdt-safe/tests/esdt_safe_blackbox_test.rs +++ b/esdt-safe/tests/esdt_safe_blackbox_test.rs @@ -239,8 +239,8 @@ impl EsdtSafeTestState { true, false, BigUint::from(0u64), - BigUint::from(0u64), - BigUint::from(0u64), + BigUint::from(10_000u64), + BigUint::from(10_000u64), OptionalValue::Some(BigUint::from(0u64)), ) .run(); @@ -257,19 +257,6 @@ impl EsdtSafeTestState { OptionalValue::Some(BigUint::from(0u64)), ) .run(); - - self.esdt_raw_transaction() - .add_token_to_whitelist( - TOKEN_ID, - "TOKEN_ID", - false, - true, - BigUint::from(0u64), - BigUint::from(0u64), - BigUint::from(0u64), - OptionalValue::Some(BigUint::from(0u64)), - ) - .run(); } fn init_supply_should_fail( @@ -600,7 +587,7 @@ fn init_supply_test_mint_burn() { .init_supply_mint_burn(TOKEN_ID, BigUint::from(10_000u64), BigUint::from(10_000u64)) .with_result(ExpectError( ERROR, - "Cannot init for non mintable/burnable tokens", + "Can init only for mintable/burnable tokens", )) .run(); @@ -611,7 +598,7 @@ fn init_supply_test_mint_burn() { BigUint::from(10_000u64), BigUint::from(10_000u64), ) - .with_result(ReturnsResult) + .with_result(ExpectError(ERROR, "Token already initialized")) .run(); let total_minted = state @@ -660,19 +647,6 @@ fn set_transaction_batch_status_test() { let mut tx_statuses_invalid = MultiValueEncoded::::new(); tx_statuses_invalid.push(TransactionStatus::Pending); - state - .world - .tx() - .from(MULTISIG_ADDRESS) - .to(ESDT_SAFE_ADDRESS) - .typed(esdt_safe_proxy::EsdtSafeProxy) - .init_supply_mint_burn( - TOKEN_WITH_BURN_ROLE, - BigUint::from(100_000u64), - BigUint::from(10_000u64), - ) - .run(); - state .world .tx() From 46c4ab2e98649b8f9ad8602431ff72c96b0079c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Wed, 20 Nov 2024 18:30:30 +0200 Subject: [PATCH 12/45] multi-transfer: unit tests fixes --- .../tests/multi_transfer_blackbox_test.rs | 104 ++++++++++-------- 1 file changed, 58 insertions(+), 46 deletions(-) diff --git a/multi-transfer-esdt/tests/multi_transfer_blackbox_test.rs b/multi-transfer-esdt/tests/multi_transfer_blackbox_test.rs index a1fb4d3c..8f00b06c 100644 --- a/multi-transfer-esdt/tests/multi_transfer_blackbox_test.rs +++ b/multi-transfer-esdt/tests/multi_transfer_blackbox_test.rs @@ -341,8 +341,8 @@ impl MultiTransferTestState { true, false, BigUint::zero(), - BigUint::zero(), - BigUint::zero(), + BigUint::from(600_000u64), + BigUint::from(0u64), OptionalValue::Some(BigUint::from(ESDT_SAFE_ETH_TX_GAS_LIMIT)), ) .run(); @@ -421,44 +421,44 @@ impl MultiTransferTestState { .set_eth_tx_gas_limit(0u64) .run(); - self.world - .tx() - .from(MULTISIG_ADDRESS) - .to(ESDT_SAFE_ADDRESS) - .typed(esdt_safe_proxy::EsdtSafeProxy) - .init_supply_mint_burn( - UNIVERSAL_TOKEN_IDENTIFIER, - BigUint::from(600_000u64), - BigUint::from(0u64), - ) - .run(); - self.world - .tx() - .from(MULTISIG_ADDRESS) - .to(ESDT_SAFE_ADDRESS) - .typed(esdt_safe_proxy::EsdtSafeProxy) - .add_token_to_whitelist( - TokenIdentifier::from_esdt_bytes("WRAPPED-123456"), - "BRIDGE2", - true, - false, - BigUint::zero(), - BigUint::zero(), - BigUint::zero(), - OptionalValue::Some(BigUint::from(ESDT_SAFE_ETH_TX_GAS_LIMIT)), - ) - .run(); - self.world - .tx() - .from(MULTISIG_ADDRESS) - .to(ESDT_SAFE_ADDRESS) - .typed(esdt_safe_proxy::EsdtSafeProxy) - .init_supply_mint_burn( - WRAPPED_TOKEN_ID, - BigUint::from(600_000u64), - BigUint::from(0u64), - ) - .run(); + // self.world + // .tx() + // .from(MULTISIG_ADDRESS) + // .to(ESDT_SAFE_ADDRESS) + // .typed(esdt_safe_proxy::EsdtSafeProxy) + // .init_supply_mint_burn( + // UNIVERSAL_TOKEN_IDENTIFIER, + // BigUint::from(600_000u64), + // BigUint::from(0u64), + // ) + // .run(); + // self.world + // .tx() + // .from(MULTISIG_ADDRESS) + // .to(ESDT_SAFE_ADDRESS) + // .typed(esdt_safe_proxy::EsdtSafeProxy) + // .add_token_to_whitelist( + // TokenIdentifier::from_esdt_bytes("WRAPPED-123456"), + // "BRIDGE2", + // true, + // false, + // BigUint::zero(), + // BigUint::zero(), + // BigUint::zero(), + // OptionalValue::Some(BigUint::from(ESDT_SAFE_ETH_TX_GAS_LIMIT)), + // ) + // .run(); + // self.world + // .tx() + // .from(MULTISIG_ADDRESS) + // .to(ESDT_SAFE_ADDRESS) + // .typed(esdt_safe_proxy::EsdtSafeProxy) + // .init_supply_mint_burn( + // WRAPPED_TOKEN_ID, + // BigUint::from(600_000u64), + // BigUint::from(0u64), + // ) + // .run(); self.world .tx() @@ -1068,7 +1068,11 @@ fn test_unwrap_token_create_transaction_insufficient_liquidity() { .from(USER1_ADDRESS) .to(BRIDGED_TOKENS_WRAPPER_ADDRESS) .typed(bridged_tokens_wrapper_proxy::BridgedTokensWrapperProxy) - .unwrap_token_create_transaction(WRAPPED_TOKEN_ID, ESDT_SAFE_ADDRESS.to_address(), EthAddress::zero()) + .unwrap_token_create_transaction( + WRAPPED_TOKEN_ID, + ESDT_SAFE_ADDRESS.to_address(), + EthAddress::zero(), + ) .egld_or_single_esdt( &EgldOrEsdtTokenIdentifier::esdt(UNIVERSAL_TOKEN_IDENTIFIER), 0u64, @@ -1090,7 +1094,7 @@ fn test_unwrap_token_create_transaction_should_work() { state .world .tx() - .from(OWNER_ADDRESS) + .from(MULTISIG_ADDRESS) .to(BRIDGED_TOKENS_WRAPPER_ADDRESS) .typed(bridged_tokens_wrapper_proxy::BridgedTokensWrapperProxy) .deposit_liquidity() @@ -1108,7 +1112,7 @@ fn test_unwrap_token_create_transaction_should_work() { state.check_balances_on_safe( WRAPPED_TOKEN_ID, BigUint::zero(), - BigUint::from(600000u64), + BigUint::from(600_000u64), BigUint::zero(), ); @@ -1118,7 +1122,7 @@ fn test_unwrap_token_create_transaction_should_work() { .to(BRIDGED_TOKENS_WRAPPER_ADDRESS) .typed(bridged_tokens_wrapper_proxy::BridgedTokensWrapperProxy) .token_liquidity(WRAPPED_TOKEN_ID) - .returns(ExpectValue(BigUint::from(1000u64))) + .returns(ExpectValue(BigUint::from(1_000u64))) .run(); state @@ -1127,7 +1131,11 @@ fn test_unwrap_token_create_transaction_should_work() { .from(USER1_ADDRESS) .to(BRIDGED_TOKENS_WRAPPER_ADDRESS) .typed(bridged_tokens_wrapper_proxy::BridgedTokensWrapperProxy) - .unwrap_token_create_transaction(WRAPPED_TOKEN_ID, ESDT_SAFE_ADDRESS.to_address(), EthAddress::zero()) + .unwrap_token_create_transaction( + WRAPPED_TOKEN_ID, + ESDT_SAFE_ADDRESS.to_address(), + EthAddress::zero(), + ) .egld_or_single_esdt( &EgldOrEsdtTokenIdentifier::esdt(UNIVERSAL_TOKEN_IDENTIFIER), 0u64, @@ -1171,7 +1179,11 @@ fn test_unwrap_token_create_transaction_should_fail() { .from(USER1_ADDRESS) .to(BRIDGED_TOKENS_WRAPPER_ADDRESS) .typed(bridged_tokens_wrapper_proxy::BridgedTokensWrapperProxy) - .unwrap_token_create_transaction(WRAPPED_TOKEN_ID, ESDT_SAFE_ADDRESS.to_address(), EthAddress::zero()) + .unwrap_token_create_transaction( + WRAPPED_TOKEN_ID, + ESDT_SAFE_ADDRESS.to_address(), + EthAddress::zero(), + ) .egld_or_single_esdt( &EgldOrEsdtTokenIdentifier::esdt(TOKEN_TICKER), 0u64, From 2a6350777a0e8652868678282c01c54f4c831dfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Fri, 22 Nov 2024 00:25:51 +0200 Subject: [PATCH 13/45] multisig: code fixes + unit test fixes --- ..._multiversx_to_ethereum_tx_batch.scen.json | 5 +- ..._multiversx_to_ethereum_tx_batch.scen.json | 4 +- multisig/scenarios/setup.scen.json | 44 +++---- multisig/scenarios/unstake.scen.json | 116 ++++++++++++++++-- multisig/src/setup.rs | 2 +- 5 files changed, 134 insertions(+), 37 deletions(-) diff --git a/multisig/scenarios/execute_multiversx_to_ethereum_tx_batch.scen.json b/multisig/scenarios/execute_multiversx_to_ethereum_tx_batch.scen.json index c965c992..a71e0268 100644 --- a/multisig/scenarios/execute_multiversx_to_ethereum_tx_batch.scen.json +++ b/multisig/scenarios/execute_multiversx_to_ethereum_tx_batch.scen.json @@ -38,7 +38,7 @@ "nonce": "*", "balance": "*", "storage": { - "str:action_data.item|u32:1": { + "str:actionData.item|u32:1": { "1-action_type": "u8:1", "2-batch_id": "u64:1", "3-tx_batch_status_len": "u32:2", @@ -51,7 +51,6 @@ "+": {} } }, - { "step": "scCall", "txId": "second-relayer-sign", @@ -257,4 +256,4 @@ } } ] -} +} \ No newline at end of file diff --git a/multisig/scenarios/reject_multiversx_to_ethereum_tx_batch.scen.json b/multisig/scenarios/reject_multiversx_to_ethereum_tx_batch.scen.json index 122fabab..033bd8ab 100644 --- a/multisig/scenarios/reject_multiversx_to_ethereum_tx_batch.scen.json +++ b/multisig/scenarios/reject_multiversx_to_ethereum_tx_batch.scen.json @@ -38,7 +38,7 @@ "nonce": "*", "balance": "*", "storage": { - "str:action_data.item|u32:1": { + "str:actionData.item|u32:1": { "1-action_type": "u8:1", "2-batch_id": "u64:1", "3-tx_batch_status_len": "u32:2", @@ -286,4 +286,4 @@ } } ] -} +} \ No newline at end of file diff --git a/multisig/scenarios/setup.scen.json b/multisig/scenarios/setup.scen.json index 56e2e062..e96de844 100644 --- a/multisig/scenarios/setup.scen.json +++ b/multisig/scenarios/setup.scen.json @@ -75,11 +75,6 @@ "str:tokenTicker|nested:str:GWEI": "str:GWEI", "str:tokenTicker|nested:str:WEGLD-123456": "str:WEGLD", "str:tokenTicker|nested:str:ETH-123456": "str:ETH", - "str:tokenWhitelist.index|nested:str:WEGLD-123456": "1", - "str:tokenWhitelist.item|u32:1": "str:WEGLD-123456", - "str:tokenWhitelist.index|nested:str:ETH-123456": "2", - "str:tokenWhitelist.item|u32:2": "str:ETH-123456", - "str:tokenWhitelist.len": "2", "str:mintBalances|nested:str:WEGLD-123456": "500,000,000,000", "str:mintBalances|nested:str:ETH-123456": "500,000,000,000" }, @@ -106,6 +101,11 @@ "balance": "1000", "storage": {} }, + "address:relayer3": { + "nonce": "0", + "balance": "1000", + "storage": {} + }, "address:user": { "nonce": "0", "balance": "0", @@ -170,12 +170,12 @@ "str:proxyAddress": "sc:bridge_proxy", "str:bridgedTokensWrapperAddress": "sc:bridged_tokens_wrapper", "str:feeEstimatorAddress": "sc:price_aggregator", - "str:num_board_members": "2", + "str:numBoardMembers": "2", "str:quorum": "2", "str:requiredStakeAmount": "1000", "str:slashAmount": "500", - "str:user_role|u32:1": "1", - "str:user_role|u32:2": "1", + "str:userRole|u32:1": "1", + "str:userRole|u32:2": "1", "str:user_address_to_id|address:relayer1": "1", "str:user_address_to_id|address:relayer2": "2", "str:user_count": "2", @@ -287,20 +287,6 @@ ] } }, - { - "step": "scQuery", - "txId": "get-all-known-tokens", - "tx": { - "to": "sc:esdt_safe", - "function": "getAllKnownTokens" - }, - "expect": { - "out": [ - "str:WEGLD-123456", - "str:ETH-123456" - ] - } - }, { "step": "scCall", "txId": "unpause multisig", @@ -375,6 +361,20 @@ "gas": "*", "refund": "*" } + }, + { + "step": "scQuery", + "txId": "get-all-known-tokens", + "tx": { + "to": "sc:esdt_safe", + "function": "getAllKnownTokens" + }, + "expect": { + "out": [ + "str:WEGLD-123456", + "str:ETH-123456" + ] + } } ] } \ No newline at end of file diff --git a/multisig/scenarios/unstake.scen.json b/multisig/scenarios/unstake.scen.json index ab177b2a..af19caaf 100644 --- a/multisig/scenarios/unstake.scen.json +++ b/multisig/scenarios/unstake.scen.json @@ -42,6 +42,67 @@ "gasLimit": "35,000,000", "gasPrice": "0" }, + "expect": { + "status": "4", + "message": "str:Quorum size not appropriate", + "out": [], + "gas": "*", + "refund": "*" + } + }, + { + "step": "scCall", + "txId": "third-relayer-stake", + "tx": { + "from": "address:relayer3", + "to": "sc:multisig", + "value": "1000", + "function": "stake", + "arguments": [], + "gasLimit": "35,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "4", + "message": "str:Only board members can stake", + "out": [], + "gas": "*", + "refund": "*" + } + }, + { + "step": "scCall", + "txId": "add-board-member", + "tx": { + "from": "address:owner", + "to": "sc:multisig", + "function": "addBoardMember", + "arguments": [ + "address:relayer3" + ], + "gasLimit": "35,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "0", + "message": "", + "out": [], + "gas": "*", + "refund": "*" + } + }, + { + "step": "scCall", + "txId": "third-relayer-stake", + "tx": { + "from": "address:relayer3", + "to": "sc:multisig", + "value": "1,000", + "function": "stake", + "arguments": [], + "gasLimit": "35,000,000", + "gasPrice": "0" + }, "expect": { "status": "0", "message": "", @@ -50,17 +111,52 @@ "refund": "*" } }, + { + "step": "scQuery", + "txId": "getAllStakedRelayers - only one staked", + "tx": { + "to": "sc:multisig", + "function": "getAllStakedRelayers", + "arguments": [] + }, + "expect": { + "status": "0", + "message": "", + "out": [ + "address:relayer1", + "address:relayer2", + "address:relayer3" + ] + } + }, { "step": "checkState", "accounts": { + "address:relayer1": { + "nonce": "2", + "balance": "0", + "storage": {} + }, + "address:relayer2": { + "nonce": "1", + "balance": "0", + "storage": {} + }, + "address:relayer3": { + "nonce": "2", + "balance": "0", + "storage": {} + }, "sc:multisig": { "nonce": "*", - "balance": "*", + "balance": "3000", "storage": { - "str:quorum": "1", + "str:amountStaked|address:relayer1": "1000", + "str:amountStaked|address:relayer2": "1000", + "str:amountStaked|address:relayer3": "1000", "+": "" }, - "code": "*" + "code": "file:../output/multisig.wasm" }, "+": {} } @@ -92,11 +188,12 @@ "accounts": { "sc:multisig": { "nonce": "*", - "balance": "2000", + "balance": "3000", "storage": { - "str:quorum": "1", - "str:user_role|u32:1": "1", + "str:quorum": "2", + "str:user_role|u32:1": "0", "str:user_role|u32:2": "0", + "str:user_role|u32:3": "0", "+": "" }, "code": "*" @@ -158,11 +255,12 @@ }, "sc:multisig": { "nonce": "*", - "balance": "1000", + "balance": "2000", "storage": { - "str:quorum": "1", - "str:user_role|u32:1": "1", + "str:quorum": "2", + "str:user_role|u32:1": "0", "str:user_role|u32:2": "0", + "str:user_role|u32:3": "0", "+": "" }, "code": "*" diff --git a/multisig/src/setup.rs b/multisig/src/setup.rs index 351458c0..8e90cdf1 100644 --- a/multisig/src/setup.rs +++ b/multisig/src/setup.rs @@ -82,7 +82,7 @@ pub trait SetupModule: let total_users = self.user_mapper().get_user_count(); let mut board_member_with_valid_stake: usize = 0; - for user_id in 0..total_users { + for user_id in 1..total_users + 1 { let user_role = self.user_id_to_role(user_id).get(); if user_role.is_board_member() { From 82684b2e8be71f01083b8c74b5b24f1039b08a5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Fri, 22 Nov 2024 00:37:02 +0200 Subject: [PATCH 14/45] bridge-proxy: allow empty endpoint only if empty args --- bridge-proxy/src/bridge-proxy.rs | 4 +- .../tests/bridge_proxy_blackbox_test.rs | 69 +++++++++++++++++++ 2 files changed, 71 insertions(+), 2 deletions(-) diff --git a/bridge-proxy/src/bridge-proxy.rs b/bridge-proxy/src/bridge-proxy.rs index 0d984ad4..f66581d4 100644 --- a/bridge-proxy/src/bridge-proxy.rs +++ b/bridge-proxy/src/bridge-proxy.rs @@ -67,8 +67,8 @@ pub trait BridgeProxyContract: } else { CallData::default() }; - - if call_data.endpoint.is_empty() + let non_empty_args = call_data.args.is_some(); + if (call_data.endpoint.is_empty() && non_empty_args) || call_data.gas_limit < MIN_GAS_LIMIT_FOR_SC_CALL || call_data.gas_limit > MAX_GAS_LIMIT_FOR_SC_CALL { diff --git a/bridge-proxy/tests/bridge_proxy_blackbox_test.rs b/bridge-proxy/tests/bridge_proxy_blackbox_test.rs index 20e287fc..8536cfff 100644 --- a/bridge-proxy/tests/bridge_proxy_blackbox_test.rs +++ b/bridge-proxy/tests/bridge_proxy_blackbox_test.rs @@ -776,3 +776,72 @@ fn bridge_proxy_too_small_gas_sc_call_test() { .check_account(ESDT_SAFE_ADDRESS) .esdt_balance(BRIDGE_TOKEN_ID, amount.clone()); } + +#[test] +fn bridge_proxy_empty_endpoint_with_args_test() { + let mut test = BridgeProxyTestState::new(); + + test.world.start_trace(); + + test.multisig_deploy(); + test.deploy_bridge_proxy(); + test.deploy_crowdfunding(); + test.config_bridge(); + + let mut args = ManagedVec::new(); + let call_data: CallData = CallData { + endpoint: ManagedBuffer::new(), + gas_limit: GAS_LIMIT, + args: ManagedOption::some(args), + }; + + let call_data: ManagedBuffer = + ManagedSerializer::new().top_encode_to_managed_buffer(&call_data); + + let eth_tx = EthTransaction { + from: EthAddress { + raw_addr: ManagedByteArray::new_from_bytes(b"01020304050607080910"), + }, + to: ManagedAddress::from(CROWDFUNDING_ADDRESS.eval_to_array()), + token_id: BRIDGE_TOKEN_ID.into(), + amount: BigUint::from(500u64), + tx_nonce: 1u64, + call_data: ManagedOption::some(call_data), + }; + + let amount = BigUint::from(500u64); + // Destination is not an initialized contract + test.world + .tx() + .from(MULTI_TRANSFER_ADDRESS) + .to(BRIDGE_PROXY_ADDRESS) + .typed(bridge_proxy_contract_proxy::BridgeProxyContractProxy) + .deposit(ð_tx, 1u64) + .egld_or_single_esdt( + &EgldOrEsdtTokenIdentifier::esdt(BRIDGE_TOKEN_ID), + 0, + &amount, + ) + .run(); + + test.world + .query() + .to(BRIDGE_PROXY_ADDRESS) + .typed(bridge_proxy_contract_proxy::BridgeProxyContractProxy) + .get_pending_transaction_by_id(1u32) + .returns(ExpectValue(eth_tx)) + .run(); + + test.world + .tx() + .from(OWNER_ADDRESS) + .to(BRIDGE_PROXY_ADDRESS) + .typed(bridge_proxy_contract_proxy::BridgeProxyContractProxy) + .execute(1u32) + .run(); + + // Refund: Funds are transfered to EsdtSafe + test.world + .check_account(ESDT_SAFE_ADDRESS) + .esdt_balance(BRIDGE_TOKEN_ID, amount.clone()); +} From c7f94909ad8e79e37b15a0dab1e76846c2af8bb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Mon, 25 Nov 2024 09:56:36 +0200 Subject: [PATCH 15/45] clippy, compilation, proxy fixes --- common/sc-proxies/src/esdt_safe_proxy.rs | 19 + common/sc-proxies/src/mock_multisig_proxy.rs | 156 ++++ common/sc-proxies/src/multisig_proxy.rs | 9 +- common/token-module/src/lib.rs | 2 +- esdt-safe/tests/esdt_safe_blackbox_test.rs | 6 +- esdt-safe/wasm/src/lib.rs | 5 +- .../tests/multi_transfer_blackbox_test.rs | 2 +- multisig/src/esdt_safe_proxy.rs | 828 ------------------ multisig/src/lib.rs | 4 +- multisig/tests/multisig_blackbox_test.rs | 2 +- 10 files changed, 190 insertions(+), 843 deletions(-) create mode 100644 common/sc-proxies/src/mock_multisig_proxy.rs delete mode 100644 multisig/src/esdt_safe_proxy.rs diff --git a/common/sc-proxies/src/esdt_safe_proxy.rs b/common/sc-proxies/src/esdt_safe_proxy.rs index 3b29e253..3501f54b 100644 --- a/common/sc-proxies/src/esdt_safe_proxy.rs +++ b/common/sc-proxies/src/esdt_safe_proxy.rs @@ -239,14 +239,17 @@ where /// Useful for knowing which token IDs to pass to the claimRefund endpoint. pub fn get_refund_amounts< Arg0: ProxyArg>, + Arg1: ProxyArg>>>, >( self, address: Arg0, + opt_tokens: Arg1, ) -> TxTypedCall, BigUint>>> { self.wrapped_tx .payment(NotPayable) .raw_call("getRefundAmounts") .argument(&address) + .argument(&opt_tokens) .original_result() } @@ -374,14 +377,17 @@ where /// where percentages must add up to the PERCENTAGE_TOTAL constant pub fn distribute_fees< Arg0: ProxyArg>>, + Arg1: ProxyArg>>>, >( self, address_percentage_pairs: Arg0, + opt_tokens_to_distribute: Arg1, ) -> TxTypedCall { self.wrapped_tx .payment(NotPayable) .raw_call("distributeFees") .argument(&address_percentage_pairs) + .argument(&opt_tokens_to_distribute) .original_result() } @@ -568,6 +574,19 @@ where .original_result() } + pub fn supply_mint_burn_initialized< + Arg0: ProxyArg>, + >( + self, + token_id: Arg0, + ) -> TxTypedCall { + self.wrapped_tx + .payment(NotPayable) + .raw_call("getsupplyMintBurnInitialized") + .argument(&token_id) + .original_result() + } + pub fn set_max_tx_batch_size< Arg0: ProxyArg, >( diff --git a/common/sc-proxies/src/mock_multisig_proxy.rs b/common/sc-proxies/src/mock_multisig_proxy.rs new file mode 100644 index 00000000..8f23fa83 --- /dev/null +++ b/common/sc-proxies/src/mock_multisig_proxy.rs @@ -0,0 +1,156 @@ +// 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 MockMultisigProxy; + +impl TxProxyTrait for MockMultisigProxy +where + Env: TxEnv, + From: TxFrom, + To: TxTo, + Gas: TxGas, +{ + type TxProxyMethods = MockMultisigProxyMethods; + + fn proxy_methods(self, tx: Tx) -> Self::TxProxyMethods { + MockMultisigProxyMethods { wrapped_tx: tx } + } +} + +pub struct MockMultisigProxyMethods +where + Env: TxEnv, + From: TxFrom, + To: TxTo, + Gas: TxGas, +{ + wrapped_tx: Tx, +} + +#[rustfmt::skip] +impl MockMultisigProxyMethods +where + Env: TxEnv, + Env::Api: VMApi, + From: TxFrom, + Gas: TxGas, +{ + pub fn init< + Arg0: ProxyArg>, + Arg1: ProxyArg>, + Arg2: ProxyArg>, + Arg3: ProxyArg>, + Arg4: ProxyArg>, + Arg5: ProxyArg>, + Arg6: ProxyArg>, + Arg7: ProxyArg, + Arg8: ProxyArg>>, + >( + self, + esdt_safe_sc_address: Arg0, + multi_transfer_sc_address: Arg1, + proxy_sc_address: Arg2, + bridged_tokens_wrapper_sc_address: Arg3, + price_aggregator_sc_address: Arg4, + _required_stake: Arg5, + _slash_amount: Arg6, + _quorum: Arg7, + _board: Arg8, + ) -> TxTypedDeploy { + self.wrapped_tx + .payment(NotPayable) + .raw_deploy() + .argument(&esdt_safe_sc_address) + .argument(&multi_transfer_sc_address) + .argument(&proxy_sc_address) + .argument(&bridged_tokens_wrapper_sc_address) + .argument(&price_aggregator_sc_address) + .argument(&_required_stake) + .argument(&_slash_amount) + .argument(&_quorum) + .argument(&_board) + .original_result() + } +} + +#[rustfmt::skip] +impl MockMultisigProxyMethods +where + Env: TxEnv, + Env::Api: VMApi, + From: TxFrom, + To: TxTo, + Gas: TxGas, +{ + pub fn upgrade( + self, + ) -> TxTypedUpgrade { + self.wrapped_tx + .payment(NotPayable) + .raw_upgrade() + .original_result() + } +} + +#[rustfmt::skip] +impl MockMultisigProxyMethods +where + Env: TxEnv, + Env::Api: VMApi, + From: TxFrom, + To: TxTo, + Gas: TxGas, +{ + pub fn esdt_safe_address( + self, + ) -> TxTypedCall> { + self.wrapped_tx + .payment(NotPayable) + .raw_call("getEsdtSafeAddress") + .original_result() + } + + pub fn multi_transfer_esdt_address( + self, + ) -> TxTypedCall> { + self.wrapped_tx + .payment(NotPayable) + .raw_call("getMultiTransferEsdtAddress") + .original_result() + } + + pub fn proxy_address( + self, + ) -> TxTypedCall> { + self.wrapped_tx + .payment(NotPayable) + .raw_call("getProxyAddress") + .original_result() + } + + pub fn bridged_tokens_wrapper_address( + self, + ) -> TxTypedCall> { + self.wrapped_tx + .payment(NotPayable) + .raw_call("getBridgedTokensWrapperAddress") + .original_result() + } + + pub fn fee_estimator_address( + self, + ) -> TxTypedCall> { + self.wrapped_tx + .payment(NotPayable) + .raw_call("getFeeEstimatorAddress") + .original_result() + } +} diff --git a/common/sc-proxies/src/multisig_proxy.rs b/common/sc-proxies/src/multisig_proxy.rs index 80334b47..8a92e992 100644 --- a/common/sc-proxies/src/multisig_proxy.rs +++ b/common/sc-proxies/src/multisig_proxy.rs @@ -315,21 +315,18 @@ where pub fn upgrade_child_contract_from_source< Arg0: ProxyArg>, Arg1: ProxyArg>, - Arg2: ProxyArg, - Arg3: ProxyArg>>, + Arg2: ProxyArg>>, >( self, child_sc_address: Arg0, source_address: Arg1, - is_payable: Arg2, - init_args: Arg3, + init_args: Arg2, ) -> TxTypedCall { self.wrapped_tx .payment(NotPayable) .raw_call("upgradeChildContractFromSource") .argument(&child_sc_address) .argument(&source_address) - .argument(&is_payable) .argument(&init_args) .original_result() } @@ -887,8 +884,6 @@ where .original_result() } - /// Actions are cleared after execution, so an empty entry means the action was executed already - /// Returns "false" if the action ID is invalid pub fn was_action_executed< Arg0: ProxyArg, >( diff --git a/common/token-module/src/lib.rs b/common/token-module/src/lib.rs index a795a4b8..a162afa7 100644 --- a/common/token-module/src/lib.rs +++ b/common/token-module/src/lib.rs @@ -240,7 +240,7 @@ pub trait TokenModule: ) { self.require_token_in_whitelist(token_id); require!( - self.supply_mint_burn_initialized(token_id).get() == false, + !self.supply_mint_burn_initialized(token_id).get(), "Token already initialized" ); require!( diff --git a/esdt-safe/tests/esdt_safe_blackbox_test.rs b/esdt-safe/tests/esdt_safe_blackbox_test.rs index 2cd1a3ad..2e69849b 100644 --- a/esdt-safe/tests/esdt_safe_blackbox_test.rs +++ b/esdt-safe/tests/esdt_safe_blackbox_test.rs @@ -1123,12 +1123,14 @@ fn claim_refund_test() { state.single_transaction_should_work(MINT_BURN_TOKEN, 1000u64); state.set_transaction_batch_status_should_work(1, tx_statuses.clone()); + let opt_tokens: OptionalValue>> = + OptionalValue::None; let refund = state .world .query() .to(ESDT_SAFE_ADDRESS) .typed(esdt_safe_proxy::EsdtSafeProxy) - .get_refund_amounts(MULTISIG_ADDRESS) + .get_refund_amounts(MULTISIG_ADDRESS, opt_tokens.clone()) .returns(ReturnsResult) .run(); @@ -1150,7 +1152,7 @@ fn claim_refund_test() { .query() .to(ESDT_SAFE_ADDRESS) .typed(esdt_safe_proxy::EsdtSafeProxy) - .get_refund_amounts(OWNER_ADDRESS) + .get_refund_amounts(OWNER_ADDRESS, opt_tokens) .returns(ReturnsResult) .run(); assert!(refund_after.is_empty()); diff --git a/esdt-safe/wasm/src/lib.rs b/esdt-safe/wasm/src/lib.rs index 283851b2..3f1f6eaa 100644 --- a/esdt-safe/wasm/src/lib.rs +++ b/esdt-safe/wasm/src/lib.rs @@ -6,9 +6,9 @@ // Init: 1 // Upgrade: 1 -// Endpoints: 44 +// Endpoints: 45 // Async Callback (empty): 1 -// Total number of exported functions: 47 +// Total number of exported functions: 48 #![no_std] @@ -51,6 +51,7 @@ multiversx_sc_wasm_adapter::endpoints! { getTotalBalances => total_balances getMintBalances => mint_balances getBurnBalances => burn_balances + getsupplyMintBurnInitialized => supply_mint_burn_initialized setMaxTxBatchSize => set_max_tx_batch_size setMaxTxBatchBlockDuration => set_max_tx_batch_block_duration getCurrentTxBatch => get_current_tx_batch diff --git a/multi-transfer-esdt/tests/multi_transfer_blackbox_test.rs b/multi-transfer-esdt/tests/multi_transfer_blackbox_test.rs index 8f00b06c..3d5d822b 100644 --- a/multi-transfer-esdt/tests/multi_transfer_blackbox_test.rs +++ b/multi-transfer-esdt/tests/multi_transfer_blackbox_test.rs @@ -151,7 +151,7 @@ impl MultiTransferTestState { .account(USER2_ADDRESS) .nonce(1); - let roles = vec![ + let roles = [ "ESDTRoleLocalMint".to_string(), "ESDTRoleLocalBurn".to_string(), ]; diff --git a/multisig/src/esdt_safe_proxy.rs b/multisig/src/esdt_safe_proxy.rs deleted file mode 100644 index 59e564f3..00000000 --- a/multisig/src/esdt_safe_proxy.rs +++ /dev/null @@ -1,828 +0,0 @@ -// 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 EsdtSafeProxy; - -impl TxProxyTrait for EsdtSafeProxy -where - Env: TxEnv, - From: TxFrom, - To: TxTo, - Gas: TxGas, -{ - type TxProxyMethods = EsdtSafeProxyMethods; - - fn proxy_methods(self, tx: Tx) -> Self::TxProxyMethods { - EsdtSafeProxyMethods { wrapped_tx: tx } - } -} - -pub struct EsdtSafeProxyMethods -where - Env: TxEnv, - From: TxFrom, - To: TxTo, - Gas: TxGas, -{ - wrapped_tx: Tx, -} - -#[rustfmt::skip] -impl EsdtSafeProxyMethods -where - Env: TxEnv, - Env::Api: VMApi, - From: TxFrom, - Gas: TxGas, -{ - /// fee_estimator_contract_address - The address of a Price Aggregator contract, - /// which will get the price of token A in token B - /// - /// eth_tx_gas_limit - The gas limit that will be used for transactions on the ETH side. - /// Will be used to compute the fees for the transfer - pub fn init< - Arg0: ProxyArg>, - Arg1: ProxyArg>, - Arg2: ProxyArg>, - >( - self, - fee_estimator_contract_address: Arg0, - multi_transfer_contract_address: Arg1, - eth_tx_gas_limit: Arg2, - ) -> TxTypedDeploy { - self.wrapped_tx - .payment(NotPayable) - .raw_deploy() - .argument(&fee_estimator_contract_address) - .argument(&multi_transfer_contract_address) - .argument(ð_tx_gas_limit) - .original_result() - } -} - -#[rustfmt::skip] -impl EsdtSafeProxyMethods -where - Env: TxEnv, - Env::Api: VMApi, - From: TxFrom, - To: TxTo, - Gas: TxGas, -{ - pub fn upgrade< - Arg0: ProxyArg>, - Arg1: ProxyArg>, - Arg2: ProxyArg>, - Arg3: ProxyArg>, - >( - self, - fee_estimator_contract_address: Arg0, - multi_transfer_contract_address: Arg1, - bridge_proxy_contract_address: Arg2, - eth_tx_gas_limit: Arg3, - ) -> TxTypedUpgrade { - self.wrapped_tx - .payment(NotPayable) - .raw_upgrade() - .argument(&fee_estimator_contract_address) - .argument(&multi_transfer_contract_address) - .argument(&bridge_proxy_contract_address) - .argument(ð_tx_gas_limit) - .original_result() - } -} - -#[rustfmt::skip] -impl EsdtSafeProxyMethods -where - Env: TxEnv, - Env::Api: VMApi, - From: TxFrom, - To: TxTo, - Gas: TxGas, -{ - /// Sets the statuses for the transactions, after they were executed on the Ethereum side. - /// - /// Only TransactionStatus::Executed (3) and TransactionStatus::Rejected (4) values are allowed. - /// Number of provided statuses must be equal to number of transactions in the batch. - pub fn set_transaction_batch_status< - Arg0: ProxyArg, - Arg1: ProxyArg>, - >( - self, - batch_id: Arg0, - tx_statuses: Arg1, - ) -> TxTypedCall { - self.wrapped_tx - .payment(NotPayable) - .raw_call("setTransactionBatchStatus") - .argument(&batch_id) - .argument(&tx_statuses) - .original_result() - } - - /// Converts failed Ethereum -> MultiversX transactions to MultiversX -> Ethereum transaction. - /// This is done every now and then to refund the tokens. - /// - /// As with normal MultiversX -> Ethereum transactions, a part of the tokens will be - /// subtracted to pay for the fees - pub fn add_refund_batch< - Arg0: ProxyArg>>, - >( - self, - refund_transactions: Arg0, - ) -> TxTypedCall { - self.wrapped_tx - .raw_call("addRefundBatch") - .argument(&refund_transactions) - .original_result() - } - - /// Create an MultiversX -> Ethereum transaction. Only fungible tokens are accepted. - /// - /// Every transfer will have a part of the tokens subtracted as fees. - /// The fee amount depends on the global eth_tx_gas_limit - /// and the current GWEI price, respective to the bridged token - /// - /// fee_amount = price_per_gas_unit * eth_tx_gas_limit - pub fn create_transaction< - Arg0: ProxyArg>, - Arg1: ProxyArg>>, - >( - self, - to: Arg0, - opt_refund_info: Arg1, - ) -> TxTypedCall { - self.wrapped_tx - .raw_call("createTransaction") - .argument(&to) - .argument(&opt_refund_info) - .original_result() - } - - pub fn create_transaction_sc_call< - Arg0: ProxyArg>, - Arg1: ProxyArg>, - Arg2: ProxyArg>>, - >( - self, - to: Arg0, - data: Arg1, - opt_refund_address: Arg2, - ) -> TxTypedCall { - self.wrapped_tx - .raw_call("createTransactionSCCall") - .argument(&to) - .argument(&data) - .argument(&opt_refund_address) - .original_result() - } - - /// Claim funds for failed MultiversX -> Ethereum transactions. - /// These are not sent automatically to prevent the contract getting stuck. - /// For example, if the receiver is a SC, a frozen account, etc. - pub fn claim_refund< - Arg0: ProxyArg>, - >( - self, - token_id: Arg0, - ) -> TxTypedCall> { - self.wrapped_tx - .payment(NotPayable) - .raw_call("claimRefund") - .argument(&token_id) - .original_result() - } - - pub fn set_bridged_tokens_wrapper_contract_address< - Arg0: ProxyArg>>, - >( - self, - opt_address: Arg0, - ) -> TxTypedCall { - self.wrapped_tx - .payment(NotPayable) - .raw_call("setBridgedTokensWrapperAddress") - .argument(&opt_address) - .original_result() - } - - pub fn set_bridge_proxy_contract_address< - Arg0: ProxyArg>>, - >( - self, - opt_new_address: Arg0, - ) -> TxTypedCall { - self.wrapped_tx - .payment(NotPayable) - .raw_call("setBridgeProxyContractAddress") - .argument(&opt_new_address) - .original_result() - } - - pub fn withdraw_refund_fees_for_ethereum< - Arg0: ProxyArg>, - Arg1: ProxyArg>, - >( - self, - token_id: Arg0, - multisig_owner: Arg1, - ) -> TxTypedCall { - self.wrapped_tx - .payment(NotPayable) - .raw_call("withdrawRefundFeesForEthereum") - .argument(&token_id) - .argument(&multisig_owner) - .original_result() - } - - pub fn withdraw_transaction_fees< - Arg0: ProxyArg>, - Arg1: ProxyArg>, - >( - self, - token_id: Arg0, - multisig_owner: Arg1, - ) -> TxTypedCall { - self.wrapped_tx - .payment(NotPayable) - .raw_call("withdrawTransactionFees") - .argument(&token_id) - .argument(&multisig_owner) - .original_result() - } - - pub fn compute_total_amounts_from_index< - Arg0: ProxyArg, - Arg1: ProxyArg, - >( - self, - start_index: Arg0, - end_index: Arg1, - ) -> TxTypedCall>> { - self.wrapped_tx - .payment(NotPayable) - .raw_call("computeTotalAmmountsFromIndex") - .argument(&start_index) - .argument(&end_index) - .original_result() - } - - /// Query function that lists all refund amounts for a user. - /// Useful for knowing which token IDs to pass to the claimRefund endpoint. - pub fn get_refund_amounts< - Arg0: ProxyArg>, - >( - self, - address: Arg0, - ) -> TxTypedCall, BigUint>>> { - self.wrapped_tx - .payment(NotPayable) - .raw_call("getRefundAmounts") - .argument(&address) - .original_result() - } - - pub fn get_total_refund_amounts( - self, - ) -> TxTypedCall, BigUint>>> { - self.wrapped_tx - .payment(NotPayable) - .raw_call("getTotalRefundAmounts") - .original_result() - } - - pub fn get_refund_fees_for_ethereum< - Arg0: ProxyArg>, - >( - self, - token_id: Arg0, - ) -> TxTypedCall> { - self.wrapped_tx - .payment(NotPayable) - .raw_call("getRefundFeesForEthereum") - .argument(&token_id) - .original_result() - } - - pub fn get_transaction_fees< - Arg0: ProxyArg>, - >( - self, - token_id: Arg0, - ) -> TxTypedCall> { - self.wrapped_tx - .payment(NotPayable) - .raw_call("getTransactionFees") - .argument(&token_id) - .original_result() - } - - pub fn bridged_tokens_wrapper_address( - self, - ) -> TxTypedCall> { - self.wrapped_tx - .payment(NotPayable) - .raw_call("getBridgedTokensWrapperAddress") - .original_result() - } - - pub fn bridge_proxy_contract_address( - self, - ) -> TxTypedCall> { - self.wrapped_tx - .payment(NotPayable) - .raw_call("getBridgeProxyContractAddress") - .original_result() - } - - pub fn set_fee_estimator_contract_address< - Arg0: ProxyArg>, - >( - self, - new_address: Arg0, - ) -> TxTypedCall { - self.wrapped_tx - .payment(NotPayable) - .raw_call("setFeeEstimatorContractAddress") - .argument(&new_address) - .original_result() - } - - pub fn set_eth_tx_gas_limit< - Arg0: ProxyArg>, - >( - self, - new_limit: Arg0, - ) -> TxTypedCall { - self.wrapped_tx - .payment(NotPayable) - .raw_call("setEthTxGasLimit") - .argument(&new_limit) - .original_result() - } - - /// Default price being used if the aggregator lacks a mapping for this token - /// or the aggregator address is not set - pub fn set_default_price_per_gas_unit< - Arg0: ProxyArg>, - Arg1: ProxyArg>, - >( - self, - token_id: Arg0, - default_price_per_gas_unit: Arg1, - ) -> TxTypedCall { - self.wrapped_tx - .payment(NotPayable) - .raw_call("setDefaultPricePerGasUnit") - .argument(&token_id) - .argument(&default_price_per_gas_unit) - .original_result() - } - - /// Token ticker being used when querying the aggregator for GWEI prices - pub fn set_token_ticker< - Arg0: ProxyArg>, - Arg1: ProxyArg>, - >( - self, - token_id: Arg0, - ticker: Arg1, - ) -> TxTypedCall { - self.wrapped_tx - .payment(NotPayable) - .raw_call("setTokenTicker") - .argument(&token_id) - .argument(&ticker) - .original_result() - } - - /// Returns the fee for the given token ID (the fee amount is in the given token) - pub fn calculate_required_fee< - Arg0: ProxyArg>, - >( - self, - token_id: Arg0, - ) -> TxTypedCall> { - self.wrapped_tx - .payment(NotPayable) - .raw_call("calculateRequiredFee") - .argument(&token_id) - .original_result() - } - - pub fn fee_estimator_contract_address( - self, - ) -> TxTypedCall> { - self.wrapped_tx - .payment(NotPayable) - .raw_call("getFeeEstimatorContractAddress") - .original_result() - } - - pub fn default_price_per_gas_unit< - Arg0: ProxyArg>, - >( - self, - token_id: Arg0, - ) -> TxTypedCall> { - self.wrapped_tx - .payment(NotPayable) - .raw_call("getDefaultPricePerGasUnit") - .argument(&token_id) - .original_result() - } - - pub fn eth_tx_gas_limit( - self, - ) -> TxTypedCall> { - self.wrapped_tx - .payment(NotPayable) - .raw_call("getEthTxGasLimit") - .original_result() - } - - /// Distributes the accumulated fees to the given addresses. - /// Expected arguments are pairs of (address, percentage), - /// where percentages must add up to the PERCENTAGE_TOTAL constant - pub fn distribute_fees< - Arg0: ProxyArg>>, - >( - self, - address_percentage_pairs: Arg0, - ) -> TxTypedCall { - self.wrapped_tx - .payment(NotPayable) - .raw_call("distributeFees") - .argument(&address_percentage_pairs) - .original_result() - } - - pub fn add_token_to_whitelist< - Arg0: ProxyArg>, - Arg1: ProxyArg>, - Arg2: ProxyArg, - Arg3: ProxyArg, - Arg4: ProxyArg>, - Arg5: ProxyArg>, - Arg6: ProxyArg>, - Arg7: ProxyArg>>, - >( - self, - token_id: Arg0, - ticker: Arg1, - mint_burn_token: Arg2, - native_token: Arg3, - total_balance: Arg4, - mint_balance: Arg5, - burn_balance: Arg6, - opt_default_price_per_gas_unit: Arg7, - ) -> TxTypedCall { - self.wrapped_tx - .raw_call("addTokenToWhitelist") - .argument(&token_id) - .argument(&ticker) - .argument(&mint_burn_token) - .argument(&native_token) - .argument(&total_balance) - .argument(&mint_balance) - .argument(&burn_balance) - .argument(&opt_default_price_per_gas_unit) - .original_result() - } - - pub fn remove_token_from_whitelist< - Arg0: ProxyArg>, - >( - self, - token_id: Arg0, - ) -> TxTypedCall { - self.wrapped_tx - .payment(NotPayable) - .raw_call("removeTokenFromWhitelist") - .argument(&token_id) - .original_result() - } - - pub fn get_tokens< - Arg0: ProxyArg>, - Arg1: ProxyArg>, - >( - self, - token_id: Arg0, - amount: Arg1, - ) -> TxTypedCall { - self.wrapped_tx - .payment(NotPayable) - .raw_call("getTokens") - .argument(&token_id) - .argument(&amount) - .original_result() - } - - pub fn init_supply< - Arg0: ProxyArg>, - Arg1: ProxyArg>, - >( - self, - token_id: Arg0, - amount: Arg1, - ) -> TxTypedCall { - self.wrapped_tx - .raw_call("initSupply") - .argument(&token_id) - .argument(&amount) - .original_result() - } - - pub fn init_supply_mint_burn< - Arg0: ProxyArg>, - Arg1: ProxyArg>, - Arg2: ProxyArg>, - >( - self, - token_id: Arg0, - mint_amount: Arg1, - burn_amount: Arg2, - ) -> TxTypedCall { - self.wrapped_tx - .payment(NotPayable) - .raw_call("initSupplyMintBurn") - .argument(&token_id) - .argument(&mint_amount) - .argument(&burn_amount) - .original_result() - } - - pub fn set_multi_transfer_contract_address< - Arg0: ProxyArg>>, - >( - self, - opt_new_address: Arg0, - ) -> TxTypedCall { - self.wrapped_tx - .payment(NotPayable) - .raw_call("setMultiTransferContractAddress") - .argument(&opt_new_address) - .original_result() - } - - pub fn token_whitelist( - self, - ) -> TxTypedCall>> { - self.wrapped_tx - .payment(NotPayable) - .raw_call("getAllKnownTokens") - .original_result() - } - - pub fn native_token< - Arg0: ProxyArg>, - >( - self, - token: Arg0, - ) -> TxTypedCall { - self.wrapped_tx - .payment(NotPayable) - .raw_call("isNativeToken") - .argument(&token) - .original_result() - } - - pub fn mint_burn_token< - Arg0: ProxyArg>, - >( - self, - token: Arg0, - ) -> TxTypedCall { - self.wrapped_tx - .payment(NotPayable) - .raw_call("isMintBurnToken") - .argument(&token) - .original_result() - } - - pub fn multi_transfer_contract_address( - self, - ) -> TxTypedCall> { - self.wrapped_tx - .payment(NotPayable) - .raw_call("getMultiTransferContractAddress") - .original_result() - } - - pub fn accumulated_transaction_fees< - Arg0: ProxyArg>, - >( - self, - token_id: Arg0, - ) -> TxTypedCall> { - self.wrapped_tx - .payment(NotPayable) - .raw_call("getAccumulatedTransactionFees") - .argument(&token_id) - .original_result() - } - - pub fn total_balances< - Arg0: ProxyArg>, - >( - self, - token_id: Arg0, - ) -> TxTypedCall> { - self.wrapped_tx - .payment(NotPayable) - .raw_call("getTotalBalances") - .argument(&token_id) - .original_result() - } - - pub fn mint_balances< - Arg0: ProxyArg>, - >( - self, - token_id: Arg0, - ) -> TxTypedCall> { - self.wrapped_tx - .payment(NotPayable) - .raw_call("getMintBalances") - .argument(&token_id) - .original_result() - } - - pub fn burn_balances< - Arg0: ProxyArg>, - >( - self, - token_id: Arg0, - ) -> TxTypedCall> { - self.wrapped_tx - .payment(NotPayable) - .raw_call("getBurnBalances") - .argument(&token_id) - .original_result() - } - - pub fn set_max_tx_batch_size< - Arg0: ProxyArg, - >( - self, - new_max_tx_batch_size: Arg0, - ) -> TxTypedCall { - self.wrapped_tx - .payment(NotPayable) - .raw_call("setMaxTxBatchSize") - .argument(&new_max_tx_batch_size) - .original_result() - } - - pub fn set_max_tx_batch_block_duration< - Arg0: ProxyArg, - >( - self, - new_max_tx_batch_block_duration: Arg0, - ) -> TxTypedCall { - self.wrapped_tx - .payment(NotPayable) - .raw_call("setMaxTxBatchBlockDuration") - .argument(&new_max_tx_batch_block_duration) - .original_result() - } - - pub fn get_current_tx_batch( - self, - ) -> TxTypedCall, ManagedBuffer, TokenIdentifier, BigUint>>>>> { - self.wrapped_tx - .payment(NotPayable) - .raw_call("getCurrentTxBatch") - .original_result() - } - - pub fn get_first_batch_any_status( - self, - ) -> TxTypedCall, ManagedBuffer, TokenIdentifier, BigUint>>>>> { - self.wrapped_tx - .payment(NotPayable) - .raw_call("getFirstBatchAnyStatus") - .original_result() - } - - pub fn get_batch< - Arg0: ProxyArg, - >( - self, - batch_id: Arg0, - ) -> TxTypedCall, ManagedBuffer, TokenIdentifier, BigUint>>>>> { - self.wrapped_tx - .payment(NotPayable) - .raw_call("getBatch") - .argument(&batch_id) - .original_result() - } - - pub fn get_batch_status< - Arg0: ProxyArg, - >( - self, - batch_id: Arg0, - ) -> TxTypedCall> { - self.wrapped_tx - .payment(NotPayable) - .raw_call("getBatchStatus") - .argument(&batch_id) - .original_result() - } - - pub fn first_batch_id( - self, - ) -> TxTypedCall { - self.wrapped_tx - .payment(NotPayable) - .raw_call("getFirstBatchId") - .original_result() - } - - pub fn last_batch_id( - self, - ) -> TxTypedCall { - self.wrapped_tx - .payment(NotPayable) - .raw_call("getLastBatchId") - .original_result() - } - - pub fn set_max_bridged_amount< - Arg0: ProxyArg>, - Arg1: ProxyArg>, - >( - self, - token_id: Arg0, - max_amount: Arg1, - ) -> TxTypedCall { - self.wrapped_tx - .payment(NotPayable) - .raw_call("setMaxBridgedAmount") - .argument(&token_id) - .argument(&max_amount) - .original_result() - } - - pub fn max_bridged_amount< - Arg0: ProxyArg>, - >( - self, - token_id: Arg0, - ) -> TxTypedCall> { - self.wrapped_tx - .payment(NotPayable) - .raw_call("getMaxBridgedAmount") - .argument(&token_id) - .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() - } -} - -#[type_abi] -#[derive(TopEncode, TopDecode, NestedEncode, NestedDecode, Clone, ManagedVecItem, PartialEq)] -pub struct RefundInfo -where - Api: ManagedTypeApi, -{ - pub address: ManagedAddress, - pub initial_batch_id: u64, - pub initial_nonce: u64, -} diff --git a/multisig/src/lib.rs b/multisig/src/lib.rs index e03db58f..d42b14d7 100644 --- a/multisig/src/lib.rs +++ b/multisig/src/lib.rs @@ -189,10 +189,12 @@ pub trait Multisig: INVALID_PERCENTAGE_SUM_OVER_ERR_MSG ); let esdt_safe_addr = self.esdt_safe_address().get(); + let opt_tokens_to_distribute: OptionalValue>> = + OptionalValue::None; self.tx() .to(esdt_safe_addr) .typed(esdt_safe_proxy::EsdtSafeProxy) - .distribute_fees(args) + .distribute_fees(args, opt_tokens_to_distribute) .sync_call(); } diff --git a/multisig/tests/multisig_blackbox_test.rs b/multisig/tests/multisig_blackbox_test.rs index b297eaf1..94085287 100644 --- a/multisig/tests/multisig_blackbox_test.rs +++ b/multisig/tests/multisig_blackbox_test.rs @@ -133,7 +133,7 @@ impl MultiTransferTestState { .account(NON_BOARD_MEMEBER_ADDRESS) .nonce(1); - let roles = vec![ + let roles = [ "ESDTRoleLocalMint".to_string(), "ESDTRoleLocalBurn".to_string(), ]; From 74b68f21633e6f053e02d0a192ce8bd1e2839c38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Mon, 25 Nov 2024 20:13:51 +0200 Subject: [PATCH 16/45] bridge-proxy: sc,test fixes --- bridge-proxy/src/bridge-proxy.rs | 11 +++++++--- bridge-proxy/src/config.rs | 4 ++++ .../tests/bridge_proxy_blackbox_test.rs | 21 +++++++++++++------ bridge-proxy/wasm/src/lib.rs | 6 ++++-- 4 files changed, 31 insertions(+), 11 deletions(-) diff --git a/bridge-proxy/src/bridge-proxy.rs b/bridge-proxy/src/bridge-proxy.rs index f66581d4..7926817a 100644 --- a/bridge-proxy/src/bridge-proxy.rs +++ b/bridge-proxy/src/bridge-proxy.rs @@ -122,13 +122,16 @@ pub trait BridgeProxyContract: #[promises_callback] fn execution_callback(&self, #[call_result] result: ManagedAsyncCallResult<()>, tx_id: usize) { if result.is_err() { - self.refund_transaction(tx_id); + // self.refund_transaction(tx_id); + let tx = self.get_pending_transaction_by_id(tx_id); + self.refund_transactions(tx_id).set(&tx); } self.cleanup_transaction(tx_id); } + #[endpoint(refundTransaction)] fn refund_transaction(&self, tx_id: usize) { - let tx = self.get_pending_transaction_by_id(tx_id); + let tx = self.refund_transactions(tx_id).get(); let esdt_safe_contract_address = self.get_esdt_safe_address(); let unwrapped_token = self.unwrap_token(&tx.token_id, tx_id); @@ -185,7 +188,9 @@ pub trait BridgeProxyContract: } fn finish_execute_gracefully(&self, tx_id: usize) { - self.refund_transaction(tx_id); + // self.refund_transaction(tx_id); + let tx = self.get_pending_transaction_by_id(tx_id); + self.refund_transactions(tx_id).set(&tx); self.cleanup_transaction(tx_id); } diff --git a/bridge-proxy/src/config.rs b/bridge-proxy/src/config.rs index c65aa314..d320a0f2 100644 --- a/bridge-proxy/src/config.rs +++ b/bridge-proxy/src/config.rs @@ -7,6 +7,10 @@ pub trait ConfigModule { #[storage_mapper("pendingTransactions")] fn pending_transactions(&self) -> MapMapper>; + #[view(refundTransactions)] + #[storage_mapper("refundTransactions")] + fn refund_transactions(&self, tx_id: usize) -> SingleValueMapper>; + #[storage_mapper("payments")] fn payments(&self, tx_id: usize) -> SingleValueMapper>; diff --git a/bridge-proxy/tests/bridge_proxy_blackbox_test.rs b/bridge-proxy/tests/bridge_proxy_blackbox_test.rs index 8536cfff..d9c10bdb 100644 --- a/bridge-proxy/tests/bridge_proxy_blackbox_test.rs +++ b/bridge-proxy/tests/bridge_proxy_blackbox_test.rs @@ -760,7 +760,7 @@ fn bridge_proxy_too_small_gas_sc_call_test() { .to(BRIDGE_PROXY_ADDRESS) .typed(bridge_proxy_contract_proxy::BridgeProxyContractProxy) .get_pending_transaction_by_id(1u32) - .returns(ExpectValue(eth_tx)) + .returns(ExpectValue(eth_tx.clone())) .run(); test.world @@ -771,10 +771,12 @@ fn bridge_proxy_too_small_gas_sc_call_test() { .execute(1u32) .run(); - // Refund: Funds are transfered to EsdtSafe test.world - .check_account(ESDT_SAFE_ADDRESS) - .esdt_balance(BRIDGE_TOKEN_ID, amount.clone()); + .check_account(BRIDGE_PROXY_ADDRESS) + .check_storage("str:refundTransactions|u32:1", "0x30313032303330343035303630373038303931300000000000000000050063726f7766756e64696e675f5f5f5f5f5f5f5f5f5f5f0000000d4252494447452d3132333435360000000201f4000000000000000101000000150000000466756e6400000000000f42400100000000") + .check_storage("str:batchId|u32:1", "1") + .check_storage("str:highestTxId", "1") + .check_storage("str:payments|u32:1", "nested:str:BRIDGE-123456|u64:0|biguint:500"); } #[test] @@ -841,7 +843,14 @@ fn bridge_proxy_empty_endpoint_with_args_test() { .run(); // Refund: Funds are transfered to EsdtSafe + // test.world + // .check_account(ESDT_SAFE_ADDRESS) + // .esdt_balance(BRIDGE_TOKEN_ID, amount.clone()); + test.world - .check_account(ESDT_SAFE_ADDRESS) - .esdt_balance(BRIDGE_TOKEN_ID, amount.clone()); + .check_account(BRIDGE_PROXY_ADDRESS) + .check_storage("str:refundTransactions|u32:1", "0x30313032303330343035303630373038303931300000000000000000050063726f7766756e64696e675f5f5f5f5f5f5f5f5f5f5f0000000d4252494447452d3132333435360000000201f4000000000000000101000000110000000000000000009896800100000000") + .check_storage("str:batchId|u32:1", "1") + .check_storage("str:highestTxId", "1") + .check_storage("str:payments|u32:1", "nested:str:BRIDGE-123456|u64:0|biguint:500"); } diff --git a/bridge-proxy/wasm/src/lib.rs b/bridge-proxy/wasm/src/lib.rs index 97c28d7c..76e2b616 100644 --- a/bridge-proxy/wasm/src/lib.rs +++ b/bridge-proxy/wasm/src/lib.rs @@ -6,10 +6,10 @@ // Init: 1 // Upgrade: 1 -// Endpoints: 8 +// Endpoints: 10 // Async Callback (empty): 1 // Promise callbacks: 1 -// Total number of exported functions: 12 +// Total number of exported functions: 14 #![no_std] @@ -23,8 +23,10 @@ multiversx_sc_wasm_adapter::endpoints! { upgrade => upgrade deposit => deposit execute => execute + refundTransaction => refund_transaction getPendingTransactionById => get_pending_transaction_by_id getPendingTransactions => get_pending_transactions + refundTransactions => refund_transactions highestTxId => highest_tx_id pause => pause_endpoint unpause => unpause_endpoint From 0da9aa341fa369f7e08bf64ee1e71552dd335041 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Mon, 25 Nov 2024 20:16:19 +0200 Subject: [PATCH 17/45] clean commented code --- bridge-proxy/src/bridge-proxy.rs | 2 -- bridge-proxy/tests/bridge_proxy_blackbox_test.rs | 5 ----- 2 files changed, 7 deletions(-) diff --git a/bridge-proxy/src/bridge-proxy.rs b/bridge-proxy/src/bridge-proxy.rs index 7926817a..14f3ac63 100644 --- a/bridge-proxy/src/bridge-proxy.rs +++ b/bridge-proxy/src/bridge-proxy.rs @@ -122,7 +122,6 @@ pub trait BridgeProxyContract: #[promises_callback] fn execution_callback(&self, #[call_result] result: ManagedAsyncCallResult<()>, tx_id: usize) { if result.is_err() { - // self.refund_transaction(tx_id); let tx = self.get_pending_transaction_by_id(tx_id); self.refund_transactions(tx_id).set(&tx); } @@ -188,7 +187,6 @@ pub trait BridgeProxyContract: } fn finish_execute_gracefully(&self, tx_id: usize) { - // self.refund_transaction(tx_id); let tx = self.get_pending_transaction_by_id(tx_id); self.refund_transactions(tx_id).set(&tx); self.cleanup_transaction(tx_id); diff --git a/bridge-proxy/tests/bridge_proxy_blackbox_test.rs b/bridge-proxy/tests/bridge_proxy_blackbox_test.rs index d9c10bdb..99062aae 100644 --- a/bridge-proxy/tests/bridge_proxy_blackbox_test.rs +++ b/bridge-proxy/tests/bridge_proxy_blackbox_test.rs @@ -842,11 +842,6 @@ fn bridge_proxy_empty_endpoint_with_args_test() { .execute(1u32) .run(); - // Refund: Funds are transfered to EsdtSafe - // test.world - // .check_account(ESDT_SAFE_ADDRESS) - // .esdt_balance(BRIDGE_TOKEN_ID, amount.clone()); - test.world .check_account(BRIDGE_PROXY_ADDRESS) .check_storage("str:refundTransactions|u32:1", "0x30313032303330343035303630373038303931300000000000000000050063726f7766756e64696e675f5f5f5f5f5f5f5f5f5f5f0000000d4252494447452d3132333435360000000201f4000000000000000101000000110000000000000000009896800100000000") From a4f51faf8fccb8a3b37dd2d5cc1adbb710aa8aa7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Wed, 27 Nov 2024 08:48:13 +0200 Subject: [PATCH 18/45] multi-transfer: batchTransferEsdtToken: refund if TransferRole --- multi-transfer-esdt/src/lib.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/multi-transfer-esdt/src/lib.rs b/multi-transfer-esdt/src/lib.rs index d063d4ba..af98254e 100644 --- a/multi-transfer-esdt/src/lib.rs +++ b/multi-transfer-esdt/src/lib.rs @@ -55,6 +55,14 @@ pub trait MultiTransferEsdt: let safe_address = self.get_esdt_safe_address(); for eth_tx in transfers { + let token_roles = self.blockchain().get_esdt_local_roles(ð_tx.token_id); + if token_roles.has_role(&EsdtLocalRole::Transfer) { + let refund_tx = self.convert_to_refund_tx(eth_tx); + refund_tx_list.push(refund_tx); + + continue; + } + let is_success: bool = self .tx() .to(safe_address.clone()) From dab4a145a8006f178924a89a5516f7b3b8fbcc72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Wed, 27 Nov 2024 08:58:04 +0200 Subject: [PATCH 19/45] multi-transfer: move_refund_batch_to_safe fix --- multi-transfer-esdt/src/lib.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/multi-transfer-esdt/src/lib.rs b/multi-transfer-esdt/src/lib.rs index af98254e..f049a1a0 100644 --- a/multi-transfer-esdt/src/lib.rs +++ b/multi-transfer-esdt/src/lib.rs @@ -139,6 +139,10 @@ pub trait MultiTransferEsdt: refund_batch.push(Transaction::from(tx_fields)); refund_payments.push(EsdtTokenPayment::new(token_identifier, 0, amount)); } else { + require!( + self.unprocessed_refund_txs(tx_nonce).is_empty(), + "This transcation is already marked as unprocessed" + ); self.unprocessed_refund_txs(tx_nonce) .set(Transaction::from(tx_fields)); @@ -175,10 +179,14 @@ pub trait MultiTransferEsdt: let esdt_safe_addr = self.get_esdt_safe_address(); let own_sc_address = self.blockchain().get_sc_address(); let sc_shard = self.blockchain().get_shard_of_address(&own_sc_address); + let token_roles = self.blockchain().get_esdt_local_roles(token_id); - if self.is_account_same_shard_frozen(sc_shard, &esdt_safe_addr, token_id) { + if self.is_account_same_shard_frozen(sc_shard, &esdt_safe_addr, token_id) + || token_roles.has_role(&EsdtLocalRole::Transfer) + { return false; } + return true; } From 6c1300776176aa0a74efe63a53ac7565d50588ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Mon, 2 Dec 2024 09:36:50 +0200 Subject: [PATCH 20/45] multisig: clearActionsForBatchId --- multisig/src/lib.rs | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/multisig/src/lib.rs b/multisig/src/lib.rs index d42b14d7..037787d8 100644 --- a/multisig/src/lib.rs +++ b/multisig/src/lib.rs @@ -19,8 +19,11 @@ use transaction::TxBatchSplitInFields; use transaction::*; use user_role::UserRole; +use core::cmp::max; use multiversx_sc::imports::*; +const MAX_ACTIONS_INTER: usize = 10; + /// Multi-signature smart contract implementation. /// Acts like a wallet that needs multiple signers for any action performed. #[multiversx_sc::contract] @@ -456,8 +459,11 @@ pub trait Multisig: // if there's only one proposed action, // the action was already cleared at the beginning of this function if action_ids_mapper.len() > 1 { - for act_id in action_ids_mapper.values() { - self.clear_action(act_id); + for _ in 0..max(action_ids_mapper.len(), MAX_ACTIONS_INTER) { + match action_ids_mapper.values().next() { + Some(act_id) => self.clear_action(act_id), + None => sc_panic!("Could not retrieve an action id"), + }; } } @@ -481,8 +487,11 @@ pub trait Multisig: // if there's only one proposed action, // the action was already cleared at the beginning of this function if action_ids_mapper.len() > 1 { - for act_id in action_ids_mapper.values() { - self.clear_action(act_id); + for _ in 0..max(action_ids_mapper.len(), MAX_ACTIONS_INTER) { + match action_ids_mapper.values().next() { + Some(act_id) => self.clear_action(act_id), + None => sc_panic!("Could not retrieve an action id"), + }; } } @@ -505,4 +514,19 @@ pub trait Multisig: } } } + + #[endpoint(clearActionsForBatchId)] + fn clear_actions_for_batch_id(&self, eth_batch_id: u64) { + let last_executed_eth_batch_id = self.last_executed_eth_batch_id().get(); + require!( + eth_batch_id < last_executed_eth_batch_id, + "Batch needs to be already executed" + ); + + let action_ids_mapper = self.batch_id_to_action_id_mapping(eth_batch_id); + + for act_id in action_ids_mapper.values() { + self.clear_action(act_id); + } + } } From 2f1b424b3369103f868b85fcb1b3991da27d00b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Mon, 2 Dec 2024 13:37:27 +0200 Subject: [PATCH 21/45] esdt-safe: create_transaction add min_bridge_amount --- bridge-proxy/src/bridge-proxy.rs | 2 +- bridged-tokens-wrapper/src/lib.rs | 11 +- .../bridged_tokens_wrapper_whitebox_test.rs | 4 + .../mock-esdt-safe/src/mock_esdt_safe.rs | 4 +- .../mock-esdt-safe/wasm/src/lib.rs | 2 +- .../src/bridged_tokens_wrapper_proxy.rs | 3 + common/sc-proxies/src/esdt_safe_proxy.rs | 28 +++- common/sc-proxies/src/multisig_proxy.rs | 13 ++ esdt-safe/src/lib.rs | 131 ++++++++++-------- esdt-safe/tests/esdt_safe_blackbox_test.rs | 18 +-- esdt-safe/wasm/src/lib.rs | 7 +- .../tests/multi_transfer_blackbox_test.rs | 9 +- multisig/wasm/src/lib.rs | 5 +- 13 files changed, 147 insertions(+), 90 deletions(-) diff --git a/bridge-proxy/src/bridge-proxy.rs b/bridge-proxy/src/bridge-proxy.rs index f66581d4..de96deb5 100644 --- a/bridge-proxy/src/bridge-proxy.rs +++ b/bridge-proxy/src/bridge-proxy.rs @@ -136,7 +136,7 @@ pub trait BridgeProxyContract: self.tx() .to(esdt_safe_contract_address) .typed(esdt_safe_proxy::EsdtSafeProxy) - .create_transaction( + .create_refund_transaction( tx.from, OptionalValue::Some(esdt_safe_proxy::RefundInfo { address: tx.to, diff --git a/bridged-tokens-wrapper/src/lib.rs b/bridged-tokens-wrapper/src/lib.rs index 73428a24..4bca2d4b 100644 --- a/bridged-tokens-wrapper/src/lib.rs +++ b/bridged-tokens-wrapper/src/lib.rs @@ -261,21 +261,14 @@ pub trait BridgedTokensWrapper: requested_token: TokenIdentifier, safe_address: ManagedAddress, to: EthAddress, + opt_min_bridge_amount: OptionalValue>, ) { let converted_amount = self.unwrap_token_common(&requested_token); - let caller = self.blockchain().get_caller(); self.tx() .to(safe_address) .typed(esdt_safe_proxy::EsdtSafeProxy) - .create_transaction( - to, - OptionalValue::Some(esdt_safe_proxy::RefundInfo { - address: caller, - initial_batch_id: 0, - initial_nonce: 0, - }), - ) + .create_transaction(to, opt_min_bridge_amount) .single_esdt(&requested_token, 0, &converted_amount) .sync_call(); } diff --git a/bridged-tokens-wrapper/tests/bridged_tokens_wrapper_whitebox_test.rs b/bridged-tokens-wrapper/tests/bridged_tokens_wrapper_whitebox_test.rs index a462f098..214d0588 100644 --- a/bridged-tokens-wrapper/tests/bridged_tokens_wrapper_whitebox_test.rs +++ b/bridged-tokens-wrapper/tests/bridged_tokens_wrapper_whitebox_test.rs @@ -396,6 +396,7 @@ fn test_unwrap_token_create_transaction_should_fail_case_1() { managed_token_id!(UNIVERSAL_TOKEN_IDENTIFIER), ManagedAddress::new_from_bytes(b"0102030405060708090a0b0c0d0e0f10"), address, + OptionalValue::None, ); }, |r| r.assert_user_error("Contract is paused"), @@ -434,6 +435,7 @@ fn test_unwrap_token_create_transaction_should_fail_case_2() { managed_token_id!(UNIVERSAL_TOKEN_IDENTIFIER), ManagedAddress::new_from_bytes(b"0102030405060708090a0b0c0d0e0f10"), address, + OptionalValue::None, ); }, |r| r.assert_user_error("Must pay more than 0 tokens!"), @@ -472,6 +474,7 @@ fn test_unwrap_token_create_transaction_should_fail_case_3() { managed_token_id!(UNIVERSAL_TOKEN_IDENTIFIER), ManagedAddress::zero(), address, + OptionalValue::None, ); }, |r| r.assert_user_error("Esdt token unavailable"), @@ -546,6 +549,7 @@ fn test_unwrap_token_create_transaction_should_fail_case_4() { managed_token_id!(UNIVERSAL_TOKEN_IDENTIFIER), ManagedAddress::zero(), address, + OptionalValue::None, ); }, |r| r.assert_user_error("Contract does not have enough funds"), diff --git a/common/mock-contracts/mock-esdt-safe/src/mock_esdt_safe.rs b/common/mock-contracts/mock-esdt-safe/src/mock_esdt_safe.rs index 991fc205..99718817 100644 --- a/common/mock-contracts/mock-esdt-safe/src/mock_esdt_safe.rs +++ b/common/mock-contracts/mock-esdt-safe/src/mock_esdt_safe.rs @@ -28,8 +28,8 @@ pub trait MockEsdtSafe { fn upgrade(&self) {} #[payable("*")] - #[endpoint(createTransaction)] - fn create_transaction( + #[endpoint(createRefundTransaction)] + fn create_refund_transaction( &self, _to: EthAddress, _opt_refund_info: OptionalValue>, diff --git a/common/mock-contracts/mock-esdt-safe/wasm/src/lib.rs b/common/mock-contracts/mock-esdt-safe/wasm/src/lib.rs index 7c9a8414..5f7aed31 100644 --- a/common/mock-contracts/mock-esdt-safe/wasm/src/lib.rs +++ b/common/mock-contracts/mock-esdt-safe/wasm/src/lib.rs @@ -20,7 +20,7 @@ multiversx_sc_wasm_adapter::endpoints! { ( init => init upgrade => upgrade - createTransaction => create_transaction + createRefundTransaction => create_refund_transaction ) } diff --git a/common/sc-proxies/src/bridged_tokens_wrapper_proxy.rs b/common/sc-proxies/src/bridged_tokens_wrapper_proxy.rs index 07b3185d..0610fc9b 100644 --- a/common/sc-proxies/src/bridged_tokens_wrapper_proxy.rs +++ b/common/sc-proxies/src/bridged_tokens_wrapper_proxy.rs @@ -191,17 +191,20 @@ where Arg0: ProxyArg>, Arg1: ProxyArg>, Arg2: ProxyArg>, + Arg3: ProxyArg>>, >( self, requested_token: Arg0, safe_address: Arg1, to: Arg2, + opt_min_bridge_amount: Arg3, ) -> TxTypedCall { self.wrapped_tx .raw_call("unwrapTokenCreateTransaction") .argument(&requested_token) .argument(&safe_address) .argument(&to) + .argument(&opt_min_bridge_amount) .original_result() } diff --git a/common/sc-proxies/src/esdt_safe_proxy.rs b/common/sc-proxies/src/esdt_safe_proxy.rs index 3501f54b..7759087a 100644 --- a/common/sc-proxies/src/esdt_safe_proxy.rs +++ b/common/sc-proxies/src/esdt_safe_proxy.rs @@ -139,6 +139,28 @@ where /// /// fee_amount = price_per_gas_unit * eth_tx_gas_limit pub fn create_transaction< + Arg0: ProxyArg>, + Arg1: ProxyArg>>, + >( + self, + to: Arg0, + opt_min_bridge_amount: Arg1, + ) -> TxTypedCall { + self.wrapped_tx + .raw_call("createTransaction") + .argument(&to) + .argument(&opt_min_bridge_amount) + .original_result() + } + + /// Create an Ethereum -> MultiversX refund transaction. Only fungible tokens are accepted. + /// + /// Every transfer will have a part of the tokens subtracted as fees. + /// The fee amount depends on the global eth_tx_gas_limit + /// and the current GWEI price, respective to the bridged token + /// + /// fee_amount = price_per_gas_unit * eth_tx_gas_limit + pub fn create_refund_transaction< Arg0: ProxyArg>, Arg1: ProxyArg>>, >( @@ -147,13 +169,13 @@ where opt_refund_info: Arg1, ) -> TxTypedCall { self.wrapped_tx - .raw_call("createTransaction") + .raw_call("createRefundTransaction") .argument(&to) .argument(&opt_refund_info) .original_result() } - pub fn create_transaction_sc_call< + pub fn create_refund_transaction_sc_call< Arg0: ProxyArg>, Arg1: ProxyArg>, Arg2: ProxyArg>>, @@ -164,7 +186,7 @@ where opt_refund_info: Arg2, ) -> TxTypedCall { self.wrapped_tx - .raw_call("createTransactionSCCall") + .raw_call("createRefundTransactionSCCall") .argument(&to) .argument(&data) .argument(&opt_refund_info) diff --git a/common/sc-proxies/src/multisig_proxy.rs b/common/sc-proxies/src/multisig_proxy.rs index 8a92e992..f3e21fdf 100644 --- a/common/sc-proxies/src/multisig_proxy.rs +++ b/common/sc-proxies/src/multisig_proxy.rs @@ -298,6 +298,19 @@ where .original_result() } + pub fn clear_actions_for_batch_id< + Arg0: ProxyArg, + >( + self, + eth_batch_id: Arg0, + ) -> TxTypedCall { + self.wrapped_tx + .payment(NotPayable) + .raw_call("clearActionsForBatchId") + .argument(ð_batch_id) + .original_result() + } + /// Used by board members to sign actions. pub fn sign< Arg0: ProxyArg, diff --git a/esdt-safe/src/lib.rs b/esdt-safe/src/lib.rs index 8c36da72..2d31003c 100644 --- a/esdt-safe/src/lib.rs +++ b/esdt-safe/src/lib.rs @@ -253,6 +253,7 @@ pub trait EsdtSafe: fn create_transaction_common( &self, to: EthAddress, + min_bridge_amount: BigUint, opt_refund_info: OptionalValue>, ) -> TransactionDetails { require!(self.not_paused(), "Cannot create transaction while paused"); @@ -266,6 +267,13 @@ pub trait EsdtSafe: "Transaction fees cost more than the entire bridged amount" ); + if min_bridge_amount != BigUint::zero() { + require!( + required_fee < min_bridge_amount, + "Minimum bridged amount after fee is not achieved" + ); + } + self.require_below_max_amount(&payment_token, &payment_amount); // This addr is used for the refund, if the transaction fails @@ -350,76 +358,83 @@ pub trait EsdtSafe: #[payable("*")] #[endpoint(createTransaction)] fn create_transaction( + &self, + to: EthAddress, + opt_min_bridge_amount: OptionalValue>, + ) { + let transaction_details = match opt_min_bridge_amount { + OptionalValue::Some(min_bridge_amount) => { + self.create_transaction_common(to, min_bridge_amount, OptionalValue::None) + } + OptionalValue::None => { + self.create_transaction_common(to, BigUint::zero(), OptionalValue::None) + } + }; + // let transaction_details = self.create_transaction_common(to, opt_refund_info); + self.create_transaction_event( + transaction_details.batch_id, + transaction_details.tx_nonce, + transaction_details.payment_token, + transaction_details.actual_bridged_amount, + transaction_details.required_fee, + transaction_details + .refund_info + .address + .as_managed_buffer() + .clone(), + transaction_details.to_address, + ); + } + + /// Create an Ethereum -> MultiversX refund transaction. Only fungible tokens are accepted. + /// + /// Every transfer will have a part of the tokens subtracted as fees. + /// The fee amount depends on the global eth_tx_gas_limit + /// and the current GWEI price, respective to the bridged token + /// + /// fee_amount = price_per_gas_unit * eth_tx_gas_limit + #[payable("*")] + #[endpoint(createRefundTransaction)] + fn create_refund_transaction( &self, to: EthAddress, opt_refund_info: OptionalValue>, ) { - let transaction_details = self.create_transaction_common(to, opt_refund_info); - - if !transaction_details.is_refund_tx { - self.create_transaction_event( - transaction_details.batch_id, - transaction_details.tx_nonce, - transaction_details.payment_token, - transaction_details.actual_bridged_amount, - transaction_details.required_fee, - transaction_details - .refund_info - .address - .as_managed_buffer() - .clone(), - transaction_details.to_address, - ); - } else { - self.create_refund_transaction_event( - transaction_details.batch_id, - transaction_details.tx_nonce, - transaction_details.payment_token, - transaction_details.actual_bridged_amount, - transaction_details.required_fee, - transaction_details.refund_info.initial_batch_id, - transaction_details.refund_info.initial_nonce, - ); - } + let transaction_details = + self.create_transaction_common(to, BigUint::zero(), opt_refund_info); + + self.create_refund_transaction_event( + transaction_details.batch_id, + transaction_details.tx_nonce, + transaction_details.payment_token, + transaction_details.actual_bridged_amount, + transaction_details.required_fee, + transaction_details.refund_info.initial_batch_id, + transaction_details.refund_info.initial_nonce, + ); } #[payable("*")] - #[endpoint(createTransactionSCCall)] - fn create_transaction_sc_call( + #[endpoint(createRefundTransactionSCCall)] + fn create_refund_transaction_sc_call( &self, to: EthAddress, data: ManagedBuffer, opt_refund_info: OptionalValue>, ) { - let transaction_details = self.create_transaction_common(to, opt_refund_info); - - if !transaction_details.is_refund_tx { - self.create_transaction_sc_call_event( - transaction_details.batch_id, - transaction_details.tx_nonce, - transaction_details.payment_token, - transaction_details.actual_bridged_amount, - transaction_details.required_fee, - transaction_details - .refund_info - .address - .as_managed_buffer() - .clone(), - transaction_details.to_address, - data, - ); - } else { - self.create_refund_transaction_sc_call_event( - transaction_details.batch_id, - transaction_details.tx_nonce, - transaction_details.payment_token, - transaction_details.actual_bridged_amount, - transaction_details.required_fee, - transaction_details.refund_info.initial_batch_id, - transaction_details.refund_info.initial_nonce, - data, - ); - } + let transaction_details = + self.create_transaction_common(to, BigUint::zero(), opt_refund_info); + + self.create_refund_transaction_sc_call_event( + transaction_details.batch_id, + transaction_details.tx_nonce, + transaction_details.payment_token, + transaction_details.actual_bridged_amount, + transaction_details.required_fee, + transaction_details.refund_info.initial_batch_id, + transaction_details.refund_info.initial_nonce, + data, + ); } /// Claim funds for failed MultiversX -> Ethereum transactions. diff --git a/esdt-safe/tests/esdt_safe_blackbox_test.rs b/esdt-safe/tests/esdt_safe_blackbox_test.rs index 2e69849b..8c2a8f63 100644 --- a/esdt-safe/tests/esdt_safe_blackbox_test.rs +++ b/esdt-safe/tests/esdt_safe_blackbox_test.rs @@ -348,7 +348,7 @@ impl EsdtSafeTestState { self.esdt_raw_transaction() .create_transaction( EthAddress::zero(), - OptionalValue::None::>, + OptionalValue::>::None, ) .egld_or_single_esdt( &EgldOrEsdtTokenIdentifier::esdt(token_id), @@ -363,7 +363,7 @@ impl EsdtSafeTestState { self.esdt_raw_transaction() .create_transaction( EthAddress::zero(), - OptionalValue::None::>, + OptionalValue::>::None, ) .egld_or_single_esdt( &EgldOrEsdtTokenIdentifier::esdt(token_id), @@ -736,7 +736,7 @@ fn esdt_safe_create_transaction() { .from(BRIDGE_PROXY_ADDRESS) .to(ESDT_SAFE_ADDRESS) .typed(esdt_safe_proxy::EsdtSafeProxy) - .create_transaction(EthAddress::zero(), OptionalValue::Some(refund_info.clone())) + .create_refund_transaction(EthAddress::zero(), OptionalValue::Some(refund_info.clone())) .single_esdt(&TOKEN_ID.into(), 0, &BigUint::from(10u64)) .run(); @@ -746,7 +746,7 @@ fn esdt_safe_create_transaction() { .from(OWNER_ADDRESS) .to(ESDT_SAFE_ADDRESS) .typed(esdt_safe_proxy::EsdtSafeProxy) - .create_transaction(EthAddress::zero(), OptionalValue::Some(refund_info.clone())) + .create_refund_transaction(EthAddress::zero(), OptionalValue::Some(refund_info.clone())) .single_esdt(&TOKEN_ID.into(), 0, &BigUint::from(10u64)) .returns(ExpectError( ERROR, @@ -760,7 +760,7 @@ fn esdt_safe_create_transaction() { .from(BRIDGED_TOKENS_WRAPPER_ADDRESS) .to(ESDT_SAFE_ADDRESS) .typed(esdt_safe_proxy::EsdtSafeProxy) - .create_transaction(EthAddress::zero(), OptionalValue::Some(refund_info.clone())) + .create_refund_transaction(EthAddress::zero(), OptionalValue::Some(refund_info.clone())) .single_esdt(&TOKEN_ID.into(), 0, &BigUint::from(10u64)) .run(); @@ -810,7 +810,7 @@ fn esdt_safe_create_transaction() { } #[test] -fn esdt_create_transaction_sc_call_test() { +fn esdt_create_refund_transaction_sc_call_test() { let mut state = EsdtSafeTestState::new(); state.multisig_deploy(); state.safe_deploy(); @@ -837,7 +837,7 @@ fn esdt_create_transaction_sc_call_test() { .from(BRIDGE_PROXY_ADDRESS) .to(ESDT_SAFE_ADDRESS) .typed(esdt_safe_proxy::EsdtSafeProxy) - .create_transaction_sc_call( + .create_refund_transaction_sc_call( EthAddress::zero(), data.clone(), OptionalValue::Some(refund_info.clone()), @@ -852,7 +852,7 @@ fn esdt_create_transaction_sc_call_test() { .from(MULTISIG_ADDRESS) .to(ESDT_SAFE_ADDRESS) .typed(esdt_safe_proxy::EsdtSafeProxy) - .create_transaction_sc_call( + .create_refund_transaction_sc_call( EthAddress::zero(), data.clone(), OptionalValue::None::>, @@ -1316,7 +1316,7 @@ fn withdraw_transaction_fees_test() { .typed(esdt_safe_proxy::EsdtSafeProxy) .create_transaction( EthAddress::zero(), - OptionalValue::None::>, + OptionalValue::>::None, ) .single_esdt(&TOKEN_ID.into(), 0, &BigUint::from(1_000_000u64)) .returns(ReturnsResult) diff --git a/esdt-safe/wasm/src/lib.rs b/esdt-safe/wasm/src/lib.rs index 3f1f6eaa..515a7c9e 100644 --- a/esdt-safe/wasm/src/lib.rs +++ b/esdt-safe/wasm/src/lib.rs @@ -6,9 +6,9 @@ // Init: 1 // Upgrade: 1 -// Endpoints: 45 +// Endpoints: 46 // Async Callback (empty): 1 -// Total number of exported functions: 48 +// Total number of exported functions: 49 #![no_std] @@ -23,7 +23,8 @@ multiversx_sc_wasm_adapter::endpoints! { setTransactionBatchStatus => set_transaction_batch_status addRefundBatch => add_refund_batch createTransaction => create_transaction - createTransactionSCCall => create_transaction_sc_call + createRefundTransaction => create_refund_transaction + createRefundTransactionSCCall => create_refund_transaction_sc_call claimRefund => claim_refund withdrawRefundFeesForEthereum => withdraw_refund_fees_for_ethereum withdrawTransactionFees => withdraw_transaction_fees diff --git a/multi-transfer-esdt/tests/multi_transfer_blackbox_test.rs b/multi-transfer-esdt/tests/multi_transfer_blackbox_test.rs index 3d5d822b..2322745c 100644 --- a/multi-transfer-esdt/tests/multi_transfer_blackbox_test.rs +++ b/multi-transfer-esdt/tests/multi_transfer_blackbox_test.rs @@ -606,7 +606,7 @@ impl MultiTransferTestState { self.esdt_raw_transaction() .create_transaction( EthAddress::zero(), - OptionalValue::None::>, + OptionalValue::>::None, ) .egld_or_single_esdt( &EgldOrEsdtTokenIdentifier::esdt(token_id), @@ -621,7 +621,7 @@ impl MultiTransferTestState { self.esdt_raw_transaction() .create_transaction( EthAddress::zero(), - OptionalValue::None::>, + OptionalValue::>::None, ) .egld_or_single_esdt( &EgldOrEsdtTokenIdentifier::esdt(token_id), @@ -1018,6 +1018,7 @@ fn test_unwrap_token_create_transaction_paused() { TokenIdentifier::from(UNIVERSAL_TOKEN_IDENTIFIER), ESDT_SAFE_ADDRESS.to_address(), EthAddress::zero(), + OptionalValue::>::None, ) .egld_or_single_esdt( &EgldOrEsdtTokenIdentifier::esdt(UNIVERSAL_TOKEN_IDENTIFIER), @@ -1072,6 +1073,7 @@ fn test_unwrap_token_create_transaction_insufficient_liquidity() { WRAPPED_TOKEN_ID, ESDT_SAFE_ADDRESS.to_address(), EthAddress::zero(), + OptionalValue::>::None, ) .egld_or_single_esdt( &EgldOrEsdtTokenIdentifier::esdt(UNIVERSAL_TOKEN_IDENTIFIER), @@ -1135,6 +1137,7 @@ fn test_unwrap_token_create_transaction_should_work() { WRAPPED_TOKEN_ID, ESDT_SAFE_ADDRESS.to_address(), EthAddress::zero(), + OptionalValue::>::None, ) .egld_or_single_esdt( &EgldOrEsdtTokenIdentifier::esdt(UNIVERSAL_TOKEN_IDENTIFIER), @@ -1183,6 +1186,7 @@ fn test_unwrap_token_create_transaction_should_fail() { WRAPPED_TOKEN_ID, ESDT_SAFE_ADDRESS.to_address(), EthAddress::zero(), + OptionalValue::>::None, ) .egld_or_single_esdt( &EgldOrEsdtTokenIdentifier::esdt(TOKEN_TICKER), @@ -1211,6 +1215,7 @@ fn test_unwrap_token_create_transaction_amount_zero() { TokenIdentifier::from(WRAPPED_TOKEN_ID), ESDT_SAFE_ADDRESS.to_address(), EthAddress::zero(), + OptionalValue::>::None, ) .egld_or_single_esdt( &EgldOrEsdtTokenIdentifier::esdt(UNIVERSAL_TOKEN_IDENTIFIER), diff --git a/multisig/wasm/src/lib.rs b/multisig/wasm/src/lib.rs index dba3a844..45ec811e 100644 --- a/multisig/wasm/src/lib.rs +++ b/multisig/wasm/src/lib.rs @@ -6,9 +6,9 @@ // Init: 1 // Upgrade: 1 -// Endpoints: 72 +// Endpoints: 73 // Async Callback (empty): 1 -// Total number of exported functions: 75 +// Total number of exported functions: 76 #![no_std] @@ -32,6 +32,7 @@ multiversx_sc_wasm_adapter::endpoints! { withdrawTransactionFees => withdraw_transaction_fees withdrawSlashedAmount => withdraw_slashed_amount performAction => perform_action_endpoint + clearActionsForBatchId => clear_actions_for_batch_id sign => sign upgradeChildContractFromSource => upgrade_child_contract_from_source addBoardMember => add_board_member_endpoint From 05616f4f8cedc90fe881eee2604cf15bc7c7b2e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Wed, 4 Dec 2024 09:24:07 +0200 Subject: [PATCH 22/45] multisig: add bridgedTokensWrapperDepositLiquidity --- .../src/bridge_proxy_contract_proxy.rs | 26 +++++++++++++++++++ common/sc-proxies/src/multisig_proxy.rs | 8 ++++++ multisig/src/setup.rs | 21 ++++++++++++++- multisig/wasm/src/lib.rs | 5 ++-- 4 files changed, 57 insertions(+), 3 deletions(-) diff --git a/common/sc-proxies/src/bridge_proxy_contract_proxy.rs b/common/sc-proxies/src/bridge_proxy_contract_proxy.rs index e6010500..bff74a6e 100644 --- a/common/sc-proxies/src/bridge_proxy_contract_proxy.rs +++ b/common/sc-proxies/src/bridge_proxy_contract_proxy.rs @@ -109,6 +109,19 @@ where .original_result() } + pub fn refund_transaction< + Arg0: ProxyArg, + >( + self, + tx_id: Arg0, + ) -> TxTypedCall { + self.wrapped_tx + .payment(NotPayable) + .raw_call("refundTransaction") + .argument(&tx_id) + .original_result() + } + pub fn get_pending_transaction_by_id< Arg0: ProxyArg, >( @@ -131,6 +144,19 @@ where .original_result() } + pub fn refund_transactions< + Arg0: ProxyArg, + >( + self, + tx_id: Arg0, + ) -> TxTypedCall> { + self.wrapped_tx + .payment(NotPayable) + .raw_call("refundTransactions") + .argument(&tx_id) + .original_result() + } + pub fn highest_tx_id( self, ) -> TxTypedCall { diff --git a/common/sc-proxies/src/multisig_proxy.rs b/common/sc-proxies/src/multisig_proxy.rs index f3e21fdf..06d2b7e7 100644 --- a/common/sc-proxies/src/multisig_proxy.rs +++ b/common/sc-proxies/src/multisig_proxy.rs @@ -697,6 +697,14 @@ where .original_result() } + pub fn bridged_tokens_wrapper_deposit_liquidity( + self, + ) -> TxTypedCall { + self.wrapped_tx + .raw_call("bridgedTokensWrapperDepositLiquidity") + .original_result() + } + /// Minimum number of signatures needed to perform any action. pub fn quorum( self, diff --git a/multisig/src/setup.rs b/multisig/src/setup.rs index 8e90cdf1..d2f6b8fc 100644 --- a/multisig/src/setup.rs +++ b/multisig/src/setup.rs @@ -1,7 +1,10 @@ use multiversx_sc::imports::*; use eth_address::EthAddress; -use sc_proxies::{bridge_proxy_contract_proxy, esdt_safe_proxy, multi_transfer_esdt_proxy}; +use sc_proxies::{ + bridge_proxy_contract_proxy, bridged_tokens_wrapper_proxy, esdt_safe_proxy, + multi_transfer_esdt_proxy, +}; const MAX_BOARD_MEMBERS: usize = 40; @@ -409,4 +412,20 @@ pub trait SetupModule: .set_max_tx_batch_block_duration(new_max_tx_batch_block_duration) .sync_call(); } + + #[only_owner] + #[payable("*")] + #[endpoint(bridgedTokensWrapperDepositLiquidity)] + fn bridged_tokens_wrapper_deposit_liquidity(&self) { + let (payment_token, payment_amount) = self.call_value().single_fungible_esdt(); + + let bridged_tokens_wrapper_addr = self.bridged_tokens_wrapper_address().get(); + + self.tx() + .to(bridged_tokens_wrapper_addr) + .typed(bridged_tokens_wrapper_proxy::BridgedTokensWrapperProxy) + .deposit_liquidity() + .single_esdt(&payment_token, 0, &payment_amount) + .sync_call(); + } } diff --git a/multisig/wasm/src/lib.rs b/multisig/wasm/src/lib.rs index 45ec811e..abffe2c6 100644 --- a/multisig/wasm/src/lib.rs +++ b/multisig/wasm/src/lib.rs @@ -6,9 +6,9 @@ // Init: 1 // Upgrade: 1 -// Endpoints: 73 +// Endpoints: 74 // Async Callback (empty): 1 -// Total number of exported functions: 76 +// Total number of exported functions: 77 #![no_std] @@ -58,6 +58,7 @@ multiversx_sc_wasm_adapter::endpoints! { multiTransferEsdtSetMaxBridgedAmountForToken => multi_transfer_esdt_set_max_bridged_amount_for_token multiTransferEsdtSetMaxRefundTxBatchSize => multi_transfer_esdt_set_max_refund_tx_batch_size multiTransferEsdtSetMaxRefundTxBatchBlockDuration => multi_transfer_esdt_set_max_refund_tx_batch_block_duration + bridgedTokensWrapperDepositLiquidity => bridged_tokens_wrapper_deposit_liquidity getQuorum => quorum getNumBoardMembers => num_board_members getRequiredStakeAmount => required_stake_amount From b560b2e5c6fef60481af5b42c02bf0009eaaa6d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Thu, 5 Dec 2024 13:30:36 +0200 Subject: [PATCH 23/45] fixes after review --- bridge-proxy/src/bridge-proxy.rs | 9 +- .../tests/bridge_proxy_blackbox_test.rs | 6 +- .../scenarios/remove_wrapped_token.scen.json | 75 +++++++++++++++ .../scenarios/unwrap_token.scen.json | 27 +++++- common/sc-proxies/src/esdt_safe_proxy.rs | 22 ++--- common/token-module/src/lib.rs | 25 ++--- esdt-safe/src/lib.rs | 91 +++++++++---------- esdt-safe/tests/esdt_safe_blackbox_test.rs | 8 +- esdt-safe/wasm/src/lib.rs | 2 +- multi-transfer-esdt/src/lib.rs | 50 +++++++--- .../tests/multi_transfer_blackbox_test.rs | 39 -------- 11 files changed, 214 insertions(+), 140 deletions(-) diff --git a/bridge-proxy/src/bridge-proxy.rs b/bridge-proxy/src/bridge-proxy.rs index 19179e35..f4cc17b9 100644 --- a/bridge-proxy/src/bridge-proxy.rs +++ b/bridge-proxy/src/bridge-proxy.rs @@ -122,8 +122,7 @@ pub trait BridgeProxyContract: #[promises_callback] fn execution_callback(&self, #[call_result] result: ManagedAsyncCallResult<()>, tx_id: usize) { if result.is_err() { - let tx = self.get_pending_transaction_by_id(tx_id); - self.refund_transactions(tx_id).set(&tx); + self.add_pending_tx_to_refund(tx_id); } self.cleanup_transaction(tx_id); } @@ -187,9 +186,13 @@ pub trait BridgeProxyContract: } fn finish_execute_gracefully(&self, tx_id: usize) { + self.add_pending_tx_to_refund(tx_id); + self.cleanup_transaction(tx_id); + } + + fn add_pending_tx_to_refund(&self, tx_id: usize) { let tx = self.get_pending_transaction_by_id(tx_id); self.refund_transactions(tx_id).set(&tx); - self.cleanup_transaction(tx_id); } fn cleanup_transaction(&self, tx_id: usize) { diff --git a/bridge-proxy/tests/bridge_proxy_blackbox_test.rs b/bridge-proxy/tests/bridge_proxy_blackbox_test.rs index 99062aae..c0838970 100644 --- a/bridge-proxy/tests/bridge_proxy_blackbox_test.rs +++ b/bridge-proxy/tests/bridge_proxy_blackbox_test.rs @@ -558,7 +558,7 @@ fn bridge_proxy_wrong_formatting_sc_call_test() { }; let amount = BigUint::from(500u64); - // Destination is not an initialized contract + test.world .tx() .from(MULTI_TRANSFER_ADDRESS) @@ -741,7 +741,7 @@ fn bridge_proxy_too_small_gas_sc_call_test() { }; let amount = BigUint::from(500u64); - // Destination is not an initialized contract + test.world .tx() .from(MULTI_TRANSFER_ADDRESS) @@ -812,7 +812,7 @@ fn bridge_proxy_empty_endpoint_with_args_test() { }; let amount = BigUint::from(500u64); - // Destination is not an initialized contract + test.world .tx() .from(MULTI_TRANSFER_ADDRESS) diff --git a/bridged-tokens-wrapper/scenarios/remove_wrapped_token.scen.json b/bridged-tokens-wrapper/scenarios/remove_wrapped_token.scen.json index 8f6f2f3d..b0366a74 100644 --- a/bridged-tokens-wrapper/scenarios/remove_wrapped_token.scen.json +++ b/bridged-tokens-wrapper/scenarios/remove_wrapped_token.scen.json @@ -46,6 +46,81 @@ "gas": "*", "refund": "*" } + }, + { + "step": "checkState", + "accounts": { + "address:owner": { + "nonce": "6", + "balance": "0", + "storage": {} + }, + "address:user": { + "nonce": "4", + "esdt": { + "str:USDC-aaaaaa": { + "balance": "200000000000000" + }, + "str:USDC-bbbbbb": { + "balance": "500000000000000" + }, + "str:USDC-cccccc": { + "balance": "400000000000000" + }, + "str:WUSDC-abcdef": { + "balance": "900" + } + }, + "storage": {} + }, + "sc:bridged_tokens_wrapper": { + "nonce": "0", + "esdt": { + "str:WUSDC-abcdef": { + "balance": "1", + "roles": [ + "ESDTRoleLocalMint", + "ESDTRoleLocalBurn" + ] + }, + "str:WUSDC-uvwxyz": { + "balance": "1", + "roles": [ + "ESDTRoleLocalMint", + "ESDTRoleLocalBurn" + ] + }, + "str:USDC-aaaaaa": { + "balance": "300000000000000" + }, + "str:USDC-cccccc": { + "balance": "100000000000000" + } + }, + "storage": { + "str:universalBridgedTokenIds.len": "2", + "str:universalBridgedTokenIds.index|nested:str:WUSDC-abcdef": "1", + "str:universalBridgedTokenIds.index|nested:str:WUSDC-uvwxyz": "2", + "str:universalBridgedTokenIds.item|u32:1": "str:WUSDC-abcdef", + "str:universalBridgedTokenIds.item|u32:2": "str:WUSDC-uvwxyz", + "str:tokenLiquidity|nested:str:USDC-aaaaaa": "300000000000000", + "str:tokenLiquidity|nested:str:USDC-cccccc": "100000000000000", + "str:tokenDecimalsNum|nested:str:WUSDC-uvwxyz": "18", + "str:tokenDecimalsNum|nested:str:USDC-aaaaaa": "18", + "str:tokenDecimalsNum|nested:str:USDC-cccccc": "18", + "str:tokenDecimalsNum|nested:str:WUSDC-abcdef": "6", + "str:chainSpecificToUniversalMapping|nested:str:USDC-cccccc": "str:WUSDC-abcdef", + "str:chainSpecificToUniversalMapping|nested:str:USDC-aaaaaa": "str:WUSDC-abcdef", + "str:chainSpecificTokenIds|nested:str:WUSDC-abcdef|str:.len": "2", + "str:chainSpecificTokenIds|nested:str:WUSDC-abcdef|str:.index|nested:str:USDC-aaaaaa": "1", + "str:chainSpecificTokenIds|nested:str:WUSDC-abcdef|str:.index|nested:str:USDC-cccccc": "2", + "str:chainSpecificTokenIds|nested:str:WUSDC-abcdef|str:.item|u32:1": "str:USDC-aaaaaa", + "str:chainSpecificTokenIds|nested:str:WUSDC-abcdef|str:.item|u32:2": "str:USDC-cccccc" + }, + "code": "file:../output/bridged-tokens-wrapper.wasm", + "owner": "address:owner" + } + } } ] } \ No newline at end of file diff --git a/bridged-tokens-wrapper/scenarios/unwrap_token.scen.json b/bridged-tokens-wrapper/scenarios/unwrap_token.scen.json index affc4b5c..f3b9e7f6 100644 --- a/bridged-tokens-wrapper/scenarios/unwrap_token.scen.json +++ b/bridged-tokens-wrapper/scenarios/unwrap_token.scen.json @@ -31,6 +31,31 @@ "refund": "*" } }, + { + "step": "scCall", + "txId": "unwrap-token-not-enough-funds", + "tx": { + "from": "address:user", + "to": "sc:bridged_tokens_wrapper", + "value": "0", + "esdt": { + "tokenIdentifier": "str:WUSDC-abcdef", + "value": "500" + }, + "function": "unwrapToken", + "arguments": [ + "str:USDC-aaaaaa" + ], + "gasLimit": "5,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "4", + "message": "str:Contract does not have enough funds", + "gas": "*", + "refund": "*" + } + }, { "step": "scCall", "txId": "unwrap-token-requires_updating", @@ -90,7 +115,7 @@ "storage": {} }, "address:user": { - "nonce": "10", + "nonce": "11", "esdt": { "str:USDC-aaaaaa": { "balance": "400000000000000" diff --git a/common/sc-proxies/src/esdt_safe_proxy.rs b/common/sc-proxies/src/esdt_safe_proxy.rs index f777c96c..39207314 100644 --- a/common/sc-proxies/src/esdt_safe_proxy.rs +++ b/common/sc-proxies/src/esdt_safe_proxy.rs @@ -131,6 +131,15 @@ where .original_result() } + pub fn add_refund_batch_for_failed_tx( + self, + ) -> TxTypedCall { + self.wrapped_tx + .payment(NotPayable) + .raw_call("addRefundBatchForFailedTx") + .original_result() + } + /// Create an MultiversX -> Ethereum transaction. Only fungible tokens are accepted. /// /// Every transfer will have a part of the tokens subtracted as fees. @@ -578,19 +587,6 @@ where .original_result() } - pub fn supply_mint_burn_initialized< - Arg0: ProxyArg>, - >( - self, - token_id: Arg0, - ) -> TxTypedCall { - self.wrapped_tx - .payment(NotPayable) - .raw_call("getsupplyMintBurnInitialized") - .argument(&token_id) - .original_result() - } - pub fn set_max_tx_batch_size< Arg0: ProxyArg, >( diff --git a/common/token-module/src/lib.rs b/common/token-module/src/lib.rs index a162afa7..1048a8a1 100644 --- a/common/token-module/src/lib.rs +++ b/common/token-module/src/lib.rs @@ -80,8 +80,12 @@ pub trait TokenModule: } } - self.accumulated_transaction_fees(&token_id) - .set(&remaining_fees); + if remaining_fees == 0 { + self.accumulated_transaction_fees(&token_id).clear(); + } else { + self.accumulated_transaction_fees(&token_id) + .set(&remaining_fees); + } } #[only_owner] @@ -239,23 +243,18 @@ pub trait TokenModule: burn_amount: &BigUint, ) { self.require_token_in_whitelist(token_id); - require!( - !self.supply_mint_burn_initialized(token_id).get(), - "Token already initialized" - ); require!( self.mint_burn_token(token_id).get(), "Can init only for mintable/burnable tokens" ); - require!( - !self.native_token(token_id).get(), - "Cannot init native tokens" - ); + // require!( + // !self.native_token(token_id).get(), + // "Cannot init native tokens" + // ); self.mint_balances(token_id).set(mint_amount); self.burn_balances(token_id).set(burn_amount); - self.supply_mint_burn_initialized(token_id).set(true); } // private @@ -328,8 +327,4 @@ pub trait TokenModule: #[view(getBurnBalances)] #[storage_mapper("burnBalances")] fn burn_balances(&self, token_id: &TokenIdentifier) -> SingleValueMapper; - - #[view(getsupplyMintBurnInitialized)] - #[storage_mapper("supplyMintBurnInitialized")] - fn supply_mint_burn_initialized(&self, token_id: &TokenIdentifier) -> SingleValueMapper; } diff --git a/esdt-safe/src/lib.rs b/esdt-safe/src/lib.rs index c4fea8e2..4cf39ef3 100644 --- a/esdt-safe/src/lib.rs +++ b/esdt-safe/src/lib.rs @@ -24,7 +24,6 @@ pub struct TransactionDetails { pub required_fee: BigUint, pub to_address: ManagedBuffer, pub is_refund_tx: bool, - pub refund_info: RefundInfo, } #[type_abi] @@ -250,11 +249,20 @@ pub trait EsdtSafe: } } + #[endpoint(addRefundBatchForFailedTx)] + fn add_refund_batch_for_failed_tx(&self) { + let mut refund_transactions: ManagedVec> = ManagedVec::new(); + for failed_refund in self.failed_refunds().iter() { + refund_transactions.push(failed_refund); + } + + self.add_refund_batch(refund_transactions); + } + fn create_transaction_common( &self, to: EthAddress, - min_bridge_amount: BigUint, - opt_refund_info: OptionalValue>, + opt_min_bridge_amount: OptionalValue>, ) -> TransactionDetails { require!(self.not_paused(), "Cannot create transaction while paused"); @@ -267,36 +275,23 @@ pub trait EsdtSafe: "Transaction fees cost more than the entire bridged amount" ); - if min_bridge_amount != BigUint::zero() { - require!( - required_fee < min_bridge_amount, - "Minimum bridged amount after fee is not achieved" - ); - } + match opt_min_bridge_amount { + OptionalValue::Some(min_bridge_amount) => { + require!( + required_fee < min_bridge_amount, + "Minimum bridged amount after fee is not achieved" + ); + } + OptionalValue::None => {} + }; self.require_below_max_amount(&payment_token, &payment_amount); + let caller = self.blockchain().get_caller(); - // This addr is used for the refund, if the transaction fails - // This is passed by the BridgeTokenWrapper contract let mut is_refund_tx = false; - let caller = self.blockchain().get_caller(); - let refund_info = match opt_refund_info { - OptionalValue::Some(refund_info) => { - if caller == self.get_bridge_proxy_address() { - is_refund_tx = true; - refund_info - } else if caller == self.get_bridged_tokens_wrapper_address() { - refund_info - } else { - sc_panic!("Cannot specify a refund address from this caller"); - } - } - OptionalValue::None => RefundInfo { - address: caller, - initial_batch_id: 0, - initial_nonce: 0, - }, - }; + if caller == self.get_bridge_proxy_address() { + is_refund_tx = true; + } self.accumulated_transaction_fees(&payment_token) .update(|fees| *fees += &required_fee); @@ -306,7 +301,7 @@ pub trait EsdtSafe: let tx = Transaction { block_nonce: self.blockchain().get_block_nonce(), nonce: tx_nonce, - from: refund_info.address.as_managed_buffer().clone(), + from: caller.as_managed_buffer().clone(), to: to.as_managed_buffer().clone(), token_identifier: payment_token.clone(), amount: actual_bridged_amount.clone(), @@ -342,7 +337,6 @@ pub trait EsdtSafe: required_fee, to_address: tx.to, is_refund_tx, - refund_info, } } @@ -362,26 +356,16 @@ pub trait EsdtSafe: to: EthAddress, opt_min_bridge_amount: OptionalValue>, ) { - let transaction_details = match opt_min_bridge_amount { - OptionalValue::Some(min_bridge_amount) => { - self.create_transaction_common(to, min_bridge_amount, OptionalValue::None) - } - OptionalValue::None => { - self.create_transaction_common(to, BigUint::zero(), OptionalValue::None) - } - }; - // let transaction_details = self.create_transaction_common(to, opt_refund_info); + let transaction_details = self.create_transaction_common(to, opt_min_bridge_amount); + let caller = self.blockchain().get_caller(); + self.create_transaction_event( transaction_details.batch_id, transaction_details.tx_nonce, transaction_details.payment_token, transaction_details.actual_bridged_amount, transaction_details.required_fee, - transaction_details - .refund_info - .address - .as_managed_buffer() - .clone(), + caller.as_managed_buffer().clone(), transaction_details.to_address, ); } @@ -400,8 +384,17 @@ pub trait EsdtSafe: to: EthAddress, opt_refund_info: OptionalValue>, ) { - let transaction_details = - self.create_transaction_common(to, BigUint::zero(), opt_refund_info); + let transaction_details = self.create_transaction_common(to, OptionalValue::None); + let caller = self.blockchain().get_caller(); + + let refund_info = match opt_refund_info { + OptionalValue::Some(refund_info) => refund_info, + OptionalValue::None => RefundInfo { + address: caller, + initial_batch_id: 0u64, + initial_nonce: 0, + }, + }; self.create_refund_transaction_event( transaction_details.batch_id, @@ -409,8 +402,8 @@ pub trait EsdtSafe: transaction_details.payment_token, transaction_details.actual_bridged_amount, transaction_details.required_fee, - transaction_details.refund_info.initial_batch_id, - transaction_details.refund_info.initial_nonce, + refund_info.initial_batch_id, + refund_info.initial_nonce, ); } diff --git a/esdt-safe/tests/esdt_safe_blackbox_test.rs b/esdt-safe/tests/esdt_safe_blackbox_test.rs index c36ff22e..79154a83 100644 --- a/esdt-safe/tests/esdt_safe_blackbox_test.rs +++ b/esdt-safe/tests/esdt_safe_blackbox_test.rs @@ -239,8 +239,8 @@ impl EsdtSafeTestState { true, false, BigUint::from(0u64), - BigUint::from(10_000u64), - BigUint::from(10_000u64), + BigUint::from(0u64), + BigUint::from(0u64), OptionalValue::Some(BigUint::from(0u64)), ) .run(); @@ -612,7 +612,7 @@ fn init_supply_test_mint_burn() { assert_eq!( total_minted, - BigUint::from(10_000u64), + BigUint::from(0u64), "Total supply should be 10,000" ); @@ -627,7 +627,7 @@ fn init_supply_test_mint_burn() { assert_eq!( total_burned, - BigUint::from(10_000u64), + BigUint::from(0u64), "Total supply should be 10,000" ); } diff --git a/esdt-safe/wasm/src/lib.rs b/esdt-safe/wasm/src/lib.rs index 2fcdda33..e5efcd8c 100644 --- a/esdt-safe/wasm/src/lib.rs +++ b/esdt-safe/wasm/src/lib.rs @@ -22,6 +22,7 @@ multiversx_sc_wasm_adapter::endpoints! { upgrade => upgrade setTransactionBatchStatus => set_transaction_batch_status addRefundBatch => add_refund_batch + addRefundBatchForFailedTx => add_refund_batch_for_failed_tx createTransaction => create_transaction createRefundTransaction => create_refund_transaction claimRefund => claim_refund @@ -51,7 +52,6 @@ multiversx_sc_wasm_adapter::endpoints! { getTotalBalances => total_balances getMintBalances => mint_balances getBurnBalances => burn_balances - getsupplyMintBurnInitialized => supply_mint_burn_initialized setMaxTxBatchSize => set_max_tx_batch_size setMaxTxBatchBlockDuration => set_max_tx_batch_block_duration getCurrentTxBatch => get_current_tx_batch diff --git a/multi-transfer-esdt/src/lib.rs b/multi-transfer-esdt/src/lib.rs index f049a1a0..04d8b777 100644 --- a/multi-transfer-esdt/src/lib.rs +++ b/multi-transfer-esdt/src/lib.rs @@ -55,24 +55,17 @@ pub trait MultiTransferEsdt: let safe_address = self.get_esdt_safe_address(); for eth_tx in transfers { - let token_roles = self.blockchain().get_esdt_local_roles(ð_tx.token_id); + let token_roles = self + .blockchain() + .get_esdt_local_roles(ð_tx.token_id.clone()); if token_roles.has_role(&EsdtLocalRole::Transfer) { - let refund_tx = self.convert_to_refund_tx(eth_tx); + let refund_tx = self.convert_to_refund_tx(eth_tx.clone()); refund_tx_list.push(refund_tx); + self.token_with_transfer_role(eth_tx.token_id); continue; } - let is_success: bool = self - .tx() - .to(safe_address.clone()) - .typed(esdt_safe_proxy::EsdtSafeProxy) - .get_tokens(ð_tx.token_id, ð_tx.amount) - .returns(ReturnsResult) - .sync_call(); - - require!(is_success, "Invalid token or amount"); - let universal_token = self.get_universal_token(eth_tx.clone()); let mut must_refund = false; @@ -94,9 +87,28 @@ pub trait MultiTransferEsdt: continue; } + let is_success: bool = self + .tx() + .to(safe_address.clone()) + .typed(esdt_safe_proxy::EsdtSafeProxy) + .get_tokens(ð_tx.token_id, ð_tx.amount) + .returns(ReturnsResult) + .sync_call(); + + require!(is_success, "Invalid token or amount"); + // emit event before the actual transfer so we don't have to save the tx_nonces as well // emit events only for non-SC destinations if self.blockchain().is_smart_contract(ð_tx.to) { + self.transfer_performed_sc_event( + batch_id, + eth_tx.from.clone(), + eth_tx.to.clone(), + eth_tx.token_id.clone(), + eth_tx.amount.clone(), + eth_tx.tx_nonce, + ); + } else { self.transfer_performed_event( batch_id, eth_tx.from.clone(), @@ -300,9 +312,23 @@ pub trait MultiTransferEsdt: #[indexed] tx_id: TxNonce, ); + #[event("transferPerformedSCEvent")] + fn transfer_performed_sc_event( + &self, + #[indexed] batch_id: u64, + #[indexed] from: EthAddress, + #[indexed] to: ManagedAddress, + #[indexed] token_id: TokenIdentifier, + #[indexed] amount: BigUint, + #[indexed] tx_id: TxNonce, + ); + #[event("transferFailedInvalidDestination")] fn transfer_failed_invalid_destination(&self, #[indexed] batch_id: u64, #[indexed] tx_id: u64); + #[event("tokenWithTransferRole")] + fn token_with_transfer_role(&self, #[indexed] token_id: TokenIdentifier); + #[event("transferFailedInvalidToken")] fn transfer_failed_invalid_token(&self, #[indexed] batch_id: u64, #[indexed] tx_id: u64); diff --git a/multi-transfer-esdt/tests/multi_transfer_blackbox_test.rs b/multi-transfer-esdt/tests/multi_transfer_blackbox_test.rs index 2322745c..73e44ca5 100644 --- a/multi-transfer-esdt/tests/multi_transfer_blackbox_test.rs +++ b/multi-transfer-esdt/tests/multi_transfer_blackbox_test.rs @@ -421,45 +421,6 @@ impl MultiTransferTestState { .set_eth_tx_gas_limit(0u64) .run(); - // self.world - // .tx() - // .from(MULTISIG_ADDRESS) - // .to(ESDT_SAFE_ADDRESS) - // .typed(esdt_safe_proxy::EsdtSafeProxy) - // .init_supply_mint_burn( - // UNIVERSAL_TOKEN_IDENTIFIER, - // BigUint::from(600_000u64), - // BigUint::from(0u64), - // ) - // .run(); - // self.world - // .tx() - // .from(MULTISIG_ADDRESS) - // .to(ESDT_SAFE_ADDRESS) - // .typed(esdt_safe_proxy::EsdtSafeProxy) - // .add_token_to_whitelist( - // TokenIdentifier::from_esdt_bytes("WRAPPED-123456"), - // "BRIDGE2", - // true, - // false, - // BigUint::zero(), - // BigUint::zero(), - // BigUint::zero(), - // OptionalValue::Some(BigUint::from(ESDT_SAFE_ETH_TX_GAS_LIMIT)), - // ) - // .run(); - // self.world - // .tx() - // .from(MULTISIG_ADDRESS) - // .to(ESDT_SAFE_ADDRESS) - // .typed(esdt_safe_proxy::EsdtSafeProxy) - // .init_supply_mint_burn( - // WRAPPED_TOKEN_ID, - // BigUint::from(600_000u64), - // BigUint::from(0u64), - // ) - // .run(); - self.world .tx() .from(MULTISIG_ADDRESS) From 52f720adab695cabd9a973e1678df5c7d1810533 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Fri, 6 Dec 2024 09:21:30 +0200 Subject: [PATCH 24/45] Fix tests --- esdt-safe/scenarios/setup_accounts.scen.json | 7 ++- esdt-safe/scenarios/zero_fees.scen.json | 6 ++- esdt-safe/tests/esdt_safe_blackbox_test.rs | 33 -------------- .../tests/multi_transfer_blackbox_test.rs | 44 ++++++++++++++++++- 4 files changed, 52 insertions(+), 38 deletions(-) diff --git a/esdt-safe/scenarios/setup_accounts.scen.json b/esdt-safe/scenarios/setup_accounts.scen.json index cb7d0d3c..8eda41e2 100644 --- a/esdt-safe/scenarios/setup_accounts.scen.json +++ b/esdt-safe/scenarios/setup_accounts.scen.json @@ -150,8 +150,11 @@ "nonce": "0", "balance": "0", "storage": { - "str:feeEstimatorAddress": "sc:price_aggregator", - "str:multiTransferEsdtAddress": "sc:multi_transfer" + "str:esdtSafeAddress": "sc:esdt_safe", + "str:multiTransferEsdtAddress": "sc:multi_transfer", + "str:proxyAddress": "sc:bridge_proxy", + "str:bridgedTokensWrapperAddress": "sc:bridged_tokens_wrapper", + "str:feeEstimatorAddress": "sc:price_aggregator" }, "code": "file:../../multisig/output/multisig.wasm", "owner": "address:owner" diff --git a/esdt-safe/scenarios/zero_fees.scen.json b/esdt-safe/scenarios/zero_fees.scen.json index de13dd78..6e7bf148 100644 --- a/esdt-safe/scenarios/zero_fees.scen.json +++ b/esdt-safe/scenarios/zero_fees.scen.json @@ -13,7 +13,11 @@ "nonce": "0", "balance": "0", "storage": { - "str:feeEstimatorAddress": "0x0000000000000000000000000000000000000000000000000000000000000000" + "str:feeEstimatorAddress": "0x0000000000000000000000000000000000000000000000000000000000000000", + "str:esdtSafeAddress": "sc:esdt_safe", + "str:multiTransferEsdtAddress": "sc:multi_transfer", + "str:proxyAddress": "sc:bridge_proxy", + "str:bridgedTokensWrapperAddress": "sc:bridged_tokens_wrapper" }, "code": "file:../../multisig/output/multisig.wasm", "owner": "address:owner" diff --git a/esdt-safe/tests/esdt_safe_blackbox_test.rs b/esdt-safe/tests/esdt_safe_blackbox_test.rs index 79154a83..8ab9799b 100644 --- a/esdt-safe/tests/esdt_safe_blackbox_test.rs +++ b/esdt-safe/tests/esdt_safe_blackbox_test.rs @@ -582,25 +582,6 @@ fn init_supply_test_mint_burn() { .with_result(ExpectError(ERROR, "Token not in whitelist")) .run(); - state - .esdt_raw_transaction() - .init_supply_mint_burn(TOKEN_ID, BigUint::from(10_000u64), BigUint::from(10_000u64)) - .with_result(ExpectError( - ERROR, - "Can init only for mintable/burnable tokens", - )) - .run(); - - state - .esdt_raw_transaction() - .init_supply_mint_burn( - TOKEN_WITH_BURN_ROLE, - BigUint::from(10_000u64), - BigUint::from(10_000u64), - ) - .with_result(ExpectError(ERROR, "Token already initialized")) - .run(); - let total_minted = state .world .query() @@ -740,20 +721,6 @@ fn esdt_safe_create_transaction() { .single_esdt(&TOKEN_ID.into(), 0, &BigUint::from(10u64)) .run(); - state - .world - .tx() - .from(OWNER_ADDRESS) - .to(ESDT_SAFE_ADDRESS) - .typed(esdt_safe_proxy::EsdtSafeProxy) - .create_refund_transaction(EthAddress::zero(), OptionalValue::Some(refund_info.clone())) - .single_esdt(&TOKEN_ID.into(), 0, &BigUint::from(10u64)) - .returns(ExpectError( - ERROR, - "Cannot specify a refund address from this caller", - )) - .run(); - state .world .tx() diff --git a/multi-transfer-esdt/tests/multi_transfer_blackbox_test.rs b/multi-transfer-esdt/tests/multi_transfer_blackbox_test.rs index 73e44ca5..62814c87 100644 --- a/multi-transfer-esdt/tests/multi_transfer_blackbox_test.rs +++ b/multi-transfer-esdt/tests/multi_transfer_blackbox_test.rs @@ -341,8 +341,8 @@ impl MultiTransferTestState { true, false, BigUint::zero(), - BigUint::from(600_000u64), - BigUint::from(0u64), + BigUint::zero(), + BigUint::zero(), OptionalValue::Some(BigUint::from(ESDT_SAFE_ETH_TX_GAS_LIMIT)), ) .run(); @@ -421,6 +421,45 @@ impl MultiTransferTestState { .set_eth_tx_gas_limit(0u64) .run(); + self.world + .tx() + .from(MULTISIG_ADDRESS) + .to(ESDT_SAFE_ADDRESS) + .typed(esdt_safe_proxy::EsdtSafeProxy) + .init_supply_mint_burn( + UNIVERSAL_TOKEN_IDENTIFIER, + BigUint::from(600_000u64), + BigUint::from(0u64), + ) + .run(); + self.world + .tx() + .from(MULTISIG_ADDRESS) + .to(ESDT_SAFE_ADDRESS) + .typed(esdt_safe_proxy::EsdtSafeProxy) + .add_token_to_whitelist( + TokenIdentifier::from_esdt_bytes("WRAPPED-123456"), + "BRIDGE2", + true, + false, + BigUint::zero(), + BigUint::zero(), + BigUint::zero(), + OptionalValue::Some(BigUint::from(ESDT_SAFE_ETH_TX_GAS_LIMIT)), + ) + .run(); + self.world + .tx() + .from(MULTISIG_ADDRESS) + .to(ESDT_SAFE_ADDRESS) + .typed(esdt_safe_proxy::EsdtSafeProxy) + .init_supply_mint_burn( + WRAPPED_TOKEN_ID, + BigUint::from(600_000u64), + BigUint::from(0u64), + ) + .run(); + self.world .tx() .from(MULTISIG_ADDRESS) @@ -1235,6 +1274,7 @@ fn add_refund_batch_test_should_work() { .typed(multi_transfer_esdt_proxy::MultiTransferEsdtProxy) .batch_transfer_esdt_token(1u32, transfers) .run(); + state.check_balances_on_safe( TOKEN_TICKER, BigUint::zero(), From bfc081cf6ca269dc67fd103a6a1ff8b9d11d345d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Fri, 6 Dec 2024 10:33:08 +0200 Subject: [PATCH 25/45] test fixes --- multi-transfer-esdt/src/lib.rs | 20 +++++++-------- .../tests/multi_transfer_blackbox_test.rs | 25 ++++++++++--------- 2 files changed, 23 insertions(+), 22 deletions(-) diff --git a/multi-transfer-esdt/src/lib.rs b/multi-transfer-esdt/src/lib.rs index 04d8b777..b32234ef 100644 --- a/multi-transfer-esdt/src/lib.rs +++ b/multi-transfer-esdt/src/lib.rs @@ -66,6 +66,16 @@ pub trait MultiTransferEsdt: continue; } + let is_success: bool = self + .tx() + .to(safe_address.clone()) + .typed(esdt_safe_proxy::EsdtSafeProxy) + .get_tokens(ð_tx.token_id, ð_tx.amount) + .returns(ReturnsResult) + .sync_call(); + + require!(is_success, "Invalid token or amount"); + let universal_token = self.get_universal_token(eth_tx.clone()); let mut must_refund = false; @@ -87,16 +97,6 @@ pub trait MultiTransferEsdt: continue; } - let is_success: bool = self - .tx() - .to(safe_address.clone()) - .typed(esdt_safe_proxy::EsdtSafeProxy) - .get_tokens(ð_tx.token_id, ð_tx.amount) - .returns(ReturnsResult) - .sync_call(); - - require!(is_success, "Invalid token or amount"); - // emit event before the actual transfer so we don't have to save the tx_nonces as well // emit events only for non-SC destinations if self.blockchain().is_smart_contract(ð_tx.to) { diff --git a/multi-transfer-esdt/tests/multi_transfer_blackbox_test.rs b/multi-transfer-esdt/tests/multi_transfer_blackbox_test.rs index 62814c87..fe8d6c1e 100644 --- a/multi-transfer-esdt/tests/multi_transfer_blackbox_test.rs +++ b/multi-transfer-esdt/tests/multi_transfer_blackbox_test.rs @@ -334,18 +334,18 @@ impl MultiTransferTestState { .set_max_bridged_amount(TOKEN_TICKER, MAX_AMOUNT - 1) .run(); - self.esdt_raw_transaction() - .add_token_to_whitelist( - TokenIdentifier::from_esdt_bytes("WRAPPED-123456"), - "BRIDGE2", - true, - false, - BigUint::zero(), - BigUint::zero(), - BigUint::zero(), - OptionalValue::Some(BigUint::from(ESDT_SAFE_ETH_TX_GAS_LIMIT)), - ) - .run(); + // self.esdt_raw_transaction() + // .add_token_to_whitelist( + // TokenIdentifier::from_esdt_bytes("WRAPPED-123456"), + // "BRIDGE2", + // true, + // false, + // BigUint::zero(), + // BigUint::zero(), + // BigUint::zero(), + // OptionalValue::Some(BigUint::from(ESDT_SAFE_ETH_TX_GAS_LIMIT)), + // ) + // .run(); self.esdt_raw_transaction().unpause_endpoint().run(); @@ -432,6 +432,7 @@ impl MultiTransferTestState { BigUint::from(0u64), ) .run(); + self.world .tx() .from(MULTISIG_ADDRESS) From d8637b2127a4125104826ecfe4cecae9b9d9b4e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Fri, 6 Dec 2024 13:25:17 +0200 Subject: [PATCH 26/45] bridge-proxy: Refactor refund storage and endpoint --- bridge-proxy/src/bridge-proxy.rs | 27 +++++++++++++++++++++++---- bridge-proxy/src/config.rs | 2 +- bridge-proxy/wasm/src/lib.rs | 8 +++++--- 3 files changed, 29 insertions(+), 8 deletions(-) diff --git a/bridge-proxy/src/bridge-proxy.rs b/bridge-proxy/src/bridge-proxy.rs index f4cc17b9..97e28401 100644 --- a/bridge-proxy/src/bridge-proxy.rs +++ b/bridge-proxy/src/bridge-proxy.rs @@ -127,9 +127,10 @@ pub trait BridgeProxyContract: self.cleanup_transaction(tx_id); } - #[endpoint(refundTransaction)] - fn refund_transaction(&self, tx_id: usize) { - let tx = self.refund_transactions(tx_id).get(); + #[endpoint(executeRefundTransaction)] + fn execute_refund_transaction(&self, tx_id: usize) { + let tx = self.get_refund_transaction_by_id(tx_id); + let esdt_safe_contract_address = self.get_esdt_safe_address(); let unwrapped_token = self.unwrap_token(&tx.token_id, tx_id); @@ -192,7 +193,7 @@ pub trait BridgeProxyContract: fn add_pending_tx_to_refund(&self, tx_id: usize) { let tx = self.get_pending_transaction_by_id(tx_id); - self.refund_transactions(tx_id).set(&tx); + self.refund_transactions().insert(tx_id, tx); } fn cleanup_transaction(&self, tx_id: usize) { @@ -224,4 +225,22 @@ pub trait BridgeProxyContract: } transactions } + + #[view(getRefundTransactionById)] + fn get_refund_transaction_by_id(&self, tx_id: usize) -> EthTransaction { + let tx = self.refund_transactions().get(&tx_id); + require!(tx.is_some(), "Invalid tx id"); + tx.unwrap() + } + + #[view(getRefundTransactions)] + fn get_refund_transactions( + &self, + ) -> MultiValueEncoded>> { + let mut transactions = MultiValueEncoded::new(); + for (tx_id, tx) in self.refund_transactions().iter() { + transactions.push(MultiValue2((tx_id, tx))); + } + transactions + } } diff --git a/bridge-proxy/src/config.rs b/bridge-proxy/src/config.rs index d320a0f2..0da6985c 100644 --- a/bridge-proxy/src/config.rs +++ b/bridge-proxy/src/config.rs @@ -9,7 +9,7 @@ pub trait ConfigModule { #[view(refundTransactions)] #[storage_mapper("refundTransactions")] - fn refund_transactions(&self, tx_id: usize) -> SingleValueMapper>; + fn refund_transactions(&self) -> MapMapper>; #[storage_mapper("payments")] fn payments(&self, tx_id: usize) -> SingleValueMapper>; diff --git a/bridge-proxy/wasm/src/lib.rs b/bridge-proxy/wasm/src/lib.rs index 76e2b616..cb212759 100644 --- a/bridge-proxy/wasm/src/lib.rs +++ b/bridge-proxy/wasm/src/lib.rs @@ -6,10 +6,10 @@ // Init: 1 // Upgrade: 1 -// Endpoints: 10 +// Endpoints: 12 // Async Callback (empty): 1 // Promise callbacks: 1 -// Total number of exported functions: 14 +// Total number of exported functions: 16 #![no_std] @@ -23,9 +23,11 @@ multiversx_sc_wasm_adapter::endpoints! { upgrade => upgrade deposit => deposit execute => execute - refundTransaction => refund_transaction + executeRefundTransaction => execute_refund_transaction getPendingTransactionById => get_pending_transaction_by_id getPendingTransactions => get_pending_transactions + getRefundTransactionById => get_refund_transaction_by_id + getRefundTransactions => get_refund_transactions refundTransactions => refund_transactions highestTxId => highest_tx_id pause => pause_endpoint From 2924e4f6c513020022ce7804c17cba81e417bfbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Fri, 6 Dec 2024 16:29:26 +0200 Subject: [PATCH 27/45] bridge-proxy: tests fixes --- .../tests/bridge_proxy_blackbox_test.rs | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/bridge-proxy/tests/bridge_proxy_blackbox_test.rs b/bridge-proxy/tests/bridge_proxy_blackbox_test.rs index c0838970..7aff72c6 100644 --- a/bridge-proxy/tests/bridge_proxy_blackbox_test.rs +++ b/bridge-proxy/tests/bridge_proxy_blackbox_test.rs @@ -773,7 +773,11 @@ fn bridge_proxy_too_small_gas_sc_call_test() { test.world .check_account(BRIDGE_PROXY_ADDRESS) - .check_storage("str:refundTransactions|u32:1", "0x30313032303330343035303630373038303931300000000000000000050063726f7766756e64696e675f5f5f5f5f5f5f5f5f5f5f0000000d4252494447452d3132333435360000000201f4000000000000000101000000150000000466756e6400000000000f42400100000000") + .check_storage("str:refundTransactions.mapped|u32:1", "0x30313032303330343035303630373038303931300000000000000000050063726f7766756e64696e675f5f5f5f5f5f5f5f5f5f5f0000000d4252494447452d3132333435360000000201f4000000000000000101000000150000000466756e6400000000000f42400100000000") + .check_storage("str:refundTransactions.value|u32:1", "0x01") + .check_storage("str:refundTransactions.node_id|u32:1", "0x01") + .check_storage("str:refundTransactions.info", "0x00000001000000010000000100000001") + .check_storage("str:refundTransactions.node_links|u32:1", "0x0000000000000000") .check_storage("str:batchId|u32:1", "1") .check_storage("str:highestTxId", "1") .check_storage("str:payments|u32:1", "nested:str:BRIDGE-123456|u64:0|biguint:500"); @@ -843,9 +847,16 @@ fn bridge_proxy_empty_endpoint_with_args_test() { .run(); test.world - .check_account(BRIDGE_PROXY_ADDRESS) - .check_storage("str:refundTransactions|u32:1", "0x30313032303330343035303630373038303931300000000000000000050063726f7766756e64696e675f5f5f5f5f5f5f5f5f5f5f0000000d4252494447452d3132333435360000000201f4000000000000000101000000110000000000000000009896800100000000") - .check_storage("str:batchId|u32:1", "1") - .check_storage("str:highestTxId", "1") - .check_storage("str:payments|u32:1", "nested:str:BRIDGE-123456|u64:0|biguint:500"); + .check_account(BRIDGE_PROXY_ADDRESS) + .check_storage("str:refundTransactions.mapped|u32:1", "0x30313032303330343035303630373038303931300000000000000000050063726f7766756e64696e675f5f5f5f5f5f5f5f5f5f5f0000000d4252494447452d3132333435360000000201f4000000000000000101000000110000000000000000009896800100000000") + .check_storage("str:refundTransactions.value|u32:1", "0x01") + .check_storage("str:refundTransactions.node_id|u32:1", "0x01") + .check_storage("str:refundTransactions.info", "0x00000001000000010000000100000001") + .check_storage("str:refundTransactions.node_links|u32:1", "0x0000000000000000") + .check_storage("str:batchId|u32:1", "1") + .check_storage("str:highestTxId", "1") + .check_storage( + "str:payments|u32:1", + "nested:str:BRIDGE-123456|u64:0|biguint:500", + ); } From 7499d0114e813dc6049dbc60590f7f9e8c0bb347 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Fri, 6 Dec 2024 17:34:22 +0200 Subject: [PATCH 28/45] multi-transfer: test fixes --- .../tests/multi_transfer_blackbox_test.rs | 88 ++++++++++++------- 1 file changed, 55 insertions(+), 33 deletions(-) diff --git a/multi-transfer-esdt/tests/multi_transfer_blackbox_test.rs b/multi-transfer-esdt/tests/multi_transfer_blackbox_test.rs index fe8d6c1e..fea39a5d 100644 --- a/multi-transfer-esdt/tests/multi_transfer_blackbox_test.rs +++ b/multi-transfer-esdt/tests/multi_transfer_blackbox_test.rs @@ -295,7 +295,7 @@ impl MultiTransferTestState { b"TOKEN-123456", &[EsdtLocalRole::Mint, EsdtLocalRole::Burn], ); - self.esdt_raw_transaction() + self.esdt_raw_transaction_esdt_safe() .add_token_to_whitelist( TokenIdentifier::from_esdt_bytes("BRIDGE-123456"), "BRIDGE", @@ -308,7 +308,7 @@ impl MultiTransferTestState { ) .run(); - self.esdt_raw_transaction() + self.esdt_raw_transaction_esdt_safe() .add_token_to_whitelist( TokenIdentifier::from_esdt_bytes("TOKEN"), "TOKEN", @@ -334,7 +334,7 @@ impl MultiTransferTestState { .set_max_bridged_amount(TOKEN_TICKER, MAX_AMOUNT - 1) .run(); - // self.esdt_raw_transaction() + // self.esdt_raw_transaction_esdt_safe() // .add_token_to_whitelist( // TokenIdentifier::from_esdt_bytes("WRAPPED-123456"), // "BRIDGE2", @@ -347,7 +347,9 @@ impl MultiTransferTestState { // ) // .run(); - self.esdt_raw_transaction().unpause_endpoint().run(); + self.esdt_raw_transaction_esdt_safe() + .unpause_endpoint() + .run(); self.world .tx() @@ -433,22 +435,23 @@ impl MultiTransferTestState { ) .run(); - self.world - .tx() - .from(MULTISIG_ADDRESS) - .to(ESDT_SAFE_ADDRESS) - .typed(esdt_safe_proxy::EsdtSafeProxy) - .add_token_to_whitelist( - TokenIdentifier::from_esdt_bytes("WRAPPED-123456"), - "BRIDGE2", - true, - false, - BigUint::zero(), - BigUint::zero(), - BigUint::zero(), - OptionalValue::Some(BigUint::from(ESDT_SAFE_ETH_TX_GAS_LIMIT)), - ) - .run(); + // self.world + // .tx() + // .from(MULTISIG_ADDRESS) + // .to(ESDT_SAFE_ADDRESS) + // .typed(esdt_safe_proxy::EsdtSafeProxy) + // .add_token_to_whitelist( + // TokenIdentifier::from_esdt_bytes("WRAPPED-123456"), + // "BRIDGE2", + // true, + // false, + // BigUint::zero(), + // BigUint::zero(), + // BigUint::zero(), + // OptionalValue::Some(BigUint::from(ESDT_SAFE_ETH_TX_GAS_LIMIT)), + // ) + // .run(); + self.world .tx() .from(MULTISIG_ADDRESS) @@ -539,7 +542,7 @@ impl MultiTransferTestState { self.bridged_tokens_wrapper_deploy(); } - fn config_esdtsafe(&mut self) { + fn config_esdt_safe(&mut self) { self.world.set_esdt_local_roles( ESDT_SAFE_ADDRESS, b"TOKEN-123456", @@ -550,7 +553,7 @@ impl MultiTransferTestState { b"TOKEN-123456", BigUint::from(10_000_000u64), ); - self.esdt_raw_transaction() + self.esdt_raw_transaction_esdt_safe() .add_token_to_whitelist( TOKEN_ID, "TOKEN", @@ -562,7 +565,7 @@ impl MultiTransferTestState { OptionalValue::Some(BigUint::from(10u64)), ) .run(); - self.esdt_raw_transaction() + self.esdt_raw_transaction_esdt_safe() .add_token_to_whitelist( TOKEN_WITH_BURN_ROLE, "TKN", @@ -574,7 +577,7 @@ impl MultiTransferTestState { OptionalValue::Some(BigUint::from(0u64)), ) .run(); - self.esdt_raw_transaction() + self.esdt_raw_transaction_esdt_safe() .add_token_to_whitelist( TOKEN_WITHOUT_BURN_ROLE, "TKNW", @@ -586,9 +589,22 @@ impl MultiTransferTestState { OptionalValue::Some(BigUint::from(0u64)), ) .run(); + self.esdt_raw_transaction_esdt_safe() + .add_token_to_whitelist( + TokenIdentifier::from_esdt_bytes("WRAPPED-123456"), + "BRIDGE2", + true, + false, + BigUint::zero(), + BigUint::zero(), + BigUint::zero(), + OptionalValue::Some(BigUint::from(ESDT_SAFE_ETH_TX_GAS_LIMIT)), + ) + .run(); + self.world .tx() - .from(OWNER_ADDRESS) + .from(MULTISIG_ADDRESS) .to(ESDT_SAFE_ADDRESS) .typed(esdt_safe_proxy::EsdtSafeProxy) .init_supply_mint_burn( @@ -598,13 +614,14 @@ impl MultiTransferTestState { ) .run(); } + fn single_transaction_should_fail( &mut self, token_id: TestTokenIdentifier, amount: u64, expected_error: &str, ) { - self.esdt_raw_transaction() + self.esdt_raw_transaction_esdt_safe() .create_transaction( EthAddress::zero(), OptionalValue::>::None, @@ -619,7 +636,7 @@ impl MultiTransferTestState { } fn single_transaction_should_work(&mut self, token_id: TestTokenIdentifier, amount: u64) { - self.esdt_raw_transaction() + self.esdt_raw_transaction_esdt_safe() .create_transaction( EthAddress::zero(), OptionalValue::>::None, @@ -640,7 +657,7 @@ impl MultiTransferTestState { expected_status: u64, expected_error: &str, ) { - self.esdt_raw_transaction() + self.esdt_raw_transaction_esdt_safe() .set_transaction_batch_status(batch_id, statuses) .returns(ExpectError(expected_status, expected_error)) .run(); @@ -651,7 +668,7 @@ impl MultiTransferTestState { batch_id: u32, statuses: MultiValueEncoded, ) { - self.esdt_raw_transaction() + self.esdt_raw_transaction_esdt_safe() .set_transaction_batch_status(batch_id, statuses) .returns(ReturnsResult) .run(); @@ -700,7 +717,7 @@ impl MultiTransferTestState { .run(); } - fn esdt_raw_transaction( + fn esdt_raw_transaction_esdt_safe( &mut self, ) -> EsdtSafeProxyMethods, TestSCAddress, TestSCAddress, ()> { self.world @@ -716,6 +733,7 @@ fn test_config() { let mut state = MultiTransferTestState::new(); state.deploy_contracts(); + state.config_esdt_safe(); state.config_multi_transfer(); state.config_bridged_tokens_wrapper(); } @@ -785,6 +803,8 @@ fn batch_transfer_both_executed_test() { let token_amount = BigUint::from(500u64); state.deploy_contracts(); + state.config_esdt_safe(); + state.config_multi_transfer(); let mut args = ManagedVec::new(); @@ -1006,7 +1026,7 @@ fn test_unwrap_token_create_transaction_paused() { let mut state = MultiTransferTestState::new(); state.deploy_contracts(); - + state.config_esdt_safe(); state.config_bridged_tokens_wrapper(); state @@ -1034,6 +1054,7 @@ fn test_unwrap_token_create_transaction_paused() { fn test_unwrap_token_create_transaction_insufficient_liquidity() { let mut state = MultiTransferTestState::new(); state.deploy_contracts(); + state.config_esdt_safe(); state.config_multi_transfer(); state.config_bridged_tokens_wrapper(); @@ -1091,6 +1112,7 @@ fn test_unwrap_token_create_transaction_should_work() { state.deploy_contracts(); + state.config_esdt_safe(); state.config_multi_transfer(); state.config_bridged_tokens_wrapper(); @@ -1169,7 +1191,7 @@ fn test_unwrap_token_create_transaction_should_fail() { let mut state = MultiTransferTestState::new(); state.deploy_contracts(); - + state.config_esdt_safe(); state.config_multi_transfer(); state.config_bridged_tokens_wrapper(); @@ -1203,7 +1225,7 @@ fn test_unwrap_token_create_transaction_amount_zero() { let mut state = MultiTransferTestState::new(); state.deploy_contracts(); - + state.config_esdt_safe(); state.config_multi_transfer(); state.config_bridged_tokens_wrapper(); state From 65aba3dc360657153fa00c779dc734426a136353 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Fri, 6 Dec 2024 17:51:38 +0200 Subject: [PATCH 29/45] multi-transfer: remove commented code --- .../tests/multi_transfer_blackbox_test.rs | 30 ------------------- 1 file changed, 30 deletions(-) diff --git a/multi-transfer-esdt/tests/multi_transfer_blackbox_test.rs b/multi-transfer-esdt/tests/multi_transfer_blackbox_test.rs index fea39a5d..56e8110f 100644 --- a/multi-transfer-esdt/tests/multi_transfer_blackbox_test.rs +++ b/multi-transfer-esdt/tests/multi_transfer_blackbox_test.rs @@ -334,19 +334,6 @@ impl MultiTransferTestState { .set_max_bridged_amount(TOKEN_TICKER, MAX_AMOUNT - 1) .run(); - // self.esdt_raw_transaction_esdt_safe() - // .add_token_to_whitelist( - // TokenIdentifier::from_esdt_bytes("WRAPPED-123456"), - // "BRIDGE2", - // true, - // false, - // BigUint::zero(), - // BigUint::zero(), - // BigUint::zero(), - // OptionalValue::Some(BigUint::from(ESDT_SAFE_ETH_TX_GAS_LIMIT)), - // ) - // .run(); - self.esdt_raw_transaction_esdt_safe() .unpause_endpoint() .run(); @@ -435,23 +422,6 @@ impl MultiTransferTestState { ) .run(); - // self.world - // .tx() - // .from(MULTISIG_ADDRESS) - // .to(ESDT_SAFE_ADDRESS) - // .typed(esdt_safe_proxy::EsdtSafeProxy) - // .add_token_to_whitelist( - // TokenIdentifier::from_esdt_bytes("WRAPPED-123456"), - // "BRIDGE2", - // true, - // false, - // BigUint::zero(), - // BigUint::zero(), - // BigUint::zero(), - // OptionalValue::Some(BigUint::from(ESDT_SAFE_ETH_TX_GAS_LIMIT)), - // ) - // .run(); - self.world .tx() .from(MULTISIG_ADDRESS) From 566a2a0c649dec6d252c9c65d6dec3dce8bb874c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Fri, 6 Dec 2024 18:42:51 +0200 Subject: [PATCH 30/45] bridge-proxy: refund unit test --- .../tests/bridge_proxy_blackbox_test.rs | 102 ++++++++++++++++++ .../src/bridge_proxy_contract_proxy.rs | 26 ++++- 2 files changed, 124 insertions(+), 4 deletions(-) diff --git a/bridge-proxy/tests/bridge_proxy_blackbox_test.rs b/bridge-proxy/tests/bridge_proxy_blackbox_test.rs index 7aff72c6..5116f267 100644 --- a/bridge-proxy/tests/bridge_proxy_blackbox_test.rs +++ b/bridge-proxy/tests/bridge_proxy_blackbox_test.rs @@ -49,6 +49,7 @@ const TOO_SMALL_GAS_LIMIT: u64 = 1_000_000; const CF_DEADLINE: u64 = 7 * 24 * 60 * 60; // 1 week in seconds const OWNER_ADDRESS: TestAddress = TestAddress::new("owner"); +const USER_ADDRESS: TestAddress = TestAddress::new("user"); const BRIDGE_PROXY_ADDRESS: TestSCAddress = TestSCAddress::new("bridge-proxy"); const CROWDFUNDING_ADDRESS: TestSCAddress = TestSCAddress::new("crowfunding"); const MULTI_TRANSFER_ADDRESS: TestSCAddress = TestSCAddress::new("multi-transfer"); @@ -120,6 +121,9 @@ impl BridgeProxyTestState { .account(OWNER_ADDRESS) .nonce(1) .esdt_balance(TokenIdentifier::from(BRIDGE_TOKEN_ID), 10_000u64) + .account(USER_ADDRESS) + .nonce(1) + .esdt_balance(TokenIdentifier::from(BRIDGE_TOKEN_ID), 10_000u64) .account(MULTI_TRANSFER_ADDRESS) .esdt_balance(TokenIdentifier::from(WBRIDGE_TOKEN_ID), 10_000u64) .esdt_balance(TokenIdentifier::from(BRIDGE_TOKEN_ID), 10_000u64) @@ -860,3 +864,101 @@ fn bridge_proxy_empty_endpoint_with_args_test() { "nested:str:BRIDGE-123456|u64:0|biguint:500", ); } + +#[test] +fn bridge_proxy_refund_tx_test() { + let mut test = BridgeProxyTestState::new(); + + test.world.start_trace(); + + test.multisig_deploy(); + test.deploy_bridge_proxy(); + test.deploy_crowdfunding(); + test.config_bridge(); + + let mut args = ManagedVec::new(); + let call_data: CallData = CallData { + endpoint: ManagedBuffer::new(), + gas_limit: GAS_LIMIT, + args: ManagedOption::some(args), + }; + + let call_data: ManagedBuffer = + ManagedSerializer::new().top_encode_to_managed_buffer(&call_data); + + let eth_tx = EthTransaction { + from: EthAddress { + raw_addr: ManagedByteArray::new_from_bytes(b"01020304050607080910"), + }, + to: ManagedAddress::from(CROWDFUNDING_ADDRESS.eval_to_array()), + token_id: BRIDGE_TOKEN_ID.into(), + amount: BigUint::from(500u64), + tx_nonce: 1u64, + call_data: ManagedOption::some(call_data), + }; + + let amount = BigUint::from(500u64); + + test.world + .tx() + .from(MULTI_TRANSFER_ADDRESS) + .to(BRIDGE_PROXY_ADDRESS) + .typed(bridge_proxy_contract_proxy::BridgeProxyContractProxy) + .deposit(ð_tx, 1u64) + .egld_or_single_esdt( + &EgldOrEsdtTokenIdentifier::esdt(BRIDGE_TOKEN_ID), + 0, + &amount, + ) + .run(); + + test.world + .query() + .to(BRIDGE_PROXY_ADDRESS) + .typed(bridge_proxy_contract_proxy::BridgeProxyContractProxy) + .get_pending_transaction_by_id(1u32) + .returns(ExpectValue(eth_tx)) + .run(); + + test.world + .tx() + .from(USER_ADDRESS) + .to(BRIDGE_PROXY_ADDRESS) + .typed(bridge_proxy_contract_proxy::BridgeProxyContractProxy) + .execute(1u32) + .run(); + + test.world + .check_account(BRIDGE_PROXY_ADDRESS) + .check_storage("str:refundTransactions.mapped|u32:1", "0x30313032303330343035303630373038303931300000000000000000050063726f7766756e64696e675f5f5f5f5f5f5f5f5f5f5f0000000d4252494447452d3132333435360000000201f4000000000000000101000000110000000000000000009896800100000000") + .check_storage("str:refundTransactions.value|u32:1", "0x01") + .check_storage("str:refundTransactions.node_id|u32:1", "0x01") + .check_storage("str:refundTransactions.info", "0x00000001000000010000000100000001") + .check_storage("str:refundTransactions.node_links|u32:1", "0x0000000000000000") + .check_storage("str:batchId|u32:1", "1") + .check_storage("str:highestTxId", "1") + .check_storage( + "str:payments|u32:1", + "nested:str:BRIDGE-123456|u64:0|biguint:500", + ); + + test.world + .check_account(BRIDGE_PROXY_ADDRESS) + .esdt_balance(BRIDGE_TOKEN_ID, BigUint::from(amount.clone())); + + test.world + .check_account(USER_ADDRESS) + .esdt_balance(BRIDGE_TOKEN_ID, BigUint::from(10_000u64)); + + test.world + .tx() + .from(USER_ADDRESS) + .to(BRIDGE_PROXY_ADDRESS) + .typed(bridge_proxy_contract_proxy::BridgeProxyContractProxy) + .execute_refund_transaction(1u32) + .run(); + + test.world + .check_account(BRIDGE_PROXY_ADDRESS) + .esdt_balance(BRIDGE_TOKEN_ID, BigUint::zero()); +} diff --git a/common/sc-proxies/src/bridge_proxy_contract_proxy.rs b/common/sc-proxies/src/bridge_proxy_contract_proxy.rs index bff74a6e..3c771c45 100644 --- a/common/sc-proxies/src/bridge_proxy_contract_proxy.rs +++ b/common/sc-proxies/src/bridge_proxy_contract_proxy.rs @@ -109,7 +109,7 @@ where .original_result() } - pub fn refund_transaction< + pub fn execute_refund_transaction< Arg0: ProxyArg, >( self, @@ -117,7 +117,7 @@ where ) -> TxTypedCall { self.wrapped_tx .payment(NotPayable) - .raw_call("refundTransaction") + .raw_call("executeRefundTransaction") .argument(&tx_id) .original_result() } @@ -144,7 +144,7 @@ where .original_result() } - pub fn refund_transactions< + pub fn get_refund_transaction_by_id< Arg0: ProxyArg, >( self, @@ -152,11 +152,29 @@ where ) -> TxTypedCall> { self.wrapped_tx .payment(NotPayable) - .raw_call("refundTransactions") + .raw_call("getRefundTransactionById") .argument(&tx_id) .original_result() } + pub fn get_refund_transactions( + self, + ) -> TxTypedCall>>> { + self.wrapped_tx + .payment(NotPayable) + .raw_call("getRefundTransactions") + .original_result() + } + + pub fn refund_transactions( + self, + ) -> TxTypedCall>>> { + self.wrapped_tx + .payment(NotPayable) + .raw_call("refundTransactions") + .original_result() + } + pub fn highest_tx_id( self, ) -> TxTypedCall { From 7cc699c7f26222d779e4033b161a4f5ec670b53b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Mon, 9 Dec 2024 09:27:26 +0200 Subject: [PATCH 31/45] clippy fixes --- bridge-proxy/tests/bridge_proxy_blackbox_test.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/bridge-proxy/tests/bridge_proxy_blackbox_test.rs b/bridge-proxy/tests/bridge_proxy_blackbox_test.rs index 5116f267..5156702b 100644 --- a/bridge-proxy/tests/bridge_proxy_blackbox_test.rs +++ b/bridge-proxy/tests/bridge_proxy_blackbox_test.rs @@ -47,6 +47,7 @@ const GAS_LIMIT: u64 = 10_000_000; const TOO_SMALL_GAS_LIMIT: u64 = 1_000_000; const CF_DEADLINE: u64 = 7 * 24 * 60 * 60; // 1 week in seconds +const INITIAL_BALANCE: u64 = 10_000u64; const OWNER_ADDRESS: TestAddress = TestAddress::new("owner"); const USER_ADDRESS: TestAddress = TestAddress::new("user"); @@ -120,13 +121,13 @@ impl BridgeProxyTestState { world .account(OWNER_ADDRESS) .nonce(1) - .esdt_balance(TokenIdentifier::from(BRIDGE_TOKEN_ID), 10_000u64) + .esdt_balance(TokenIdentifier::from(BRIDGE_TOKEN_ID), INITIAL_BALANCE) .account(USER_ADDRESS) .nonce(1) - .esdt_balance(TokenIdentifier::from(BRIDGE_TOKEN_ID), 10_000u64) + .esdt_balance(TokenIdentifier::from(BRIDGE_TOKEN_ID), INITIAL_BALANCE) .account(MULTI_TRANSFER_ADDRESS) - .esdt_balance(TokenIdentifier::from(WBRIDGE_TOKEN_ID), 10_000u64) - .esdt_balance(TokenIdentifier::from(BRIDGE_TOKEN_ID), 10_000u64) + .esdt_balance(TokenIdentifier::from(WBRIDGE_TOKEN_ID), INITIAL_BALANCE) + .esdt_balance(TokenIdentifier::from(BRIDGE_TOKEN_ID), INITIAL_BALANCE) .code(multi_transfer_code) .account(ESDT_SAFE_ADDRESS) .code(esdt_safe_code); @@ -944,11 +945,11 @@ fn bridge_proxy_refund_tx_test() { test.world .check_account(BRIDGE_PROXY_ADDRESS) - .esdt_balance(BRIDGE_TOKEN_ID, BigUint::from(amount.clone())); + .esdt_balance(BRIDGE_TOKEN_ID, amount.clone()); test.world .check_account(USER_ADDRESS) - .esdt_balance(BRIDGE_TOKEN_ID, BigUint::from(10_000u64)); + .esdt_balance(BRIDGE_TOKEN_ID, BigUint::from(INITIAL_BALANCE)); test.world .tx() From f18a0480ca295b9a2992a7d6e43a4a842c7a619b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Mon, 9 Dec 2024 09:33:29 +0200 Subject: [PATCH 32/45] multi-transfer: add unit test --- .../tests/multi_transfer_blackbox_test.rs | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/multi-transfer-esdt/tests/multi_transfer_blackbox_test.rs b/multi-transfer-esdt/tests/multi_transfer_blackbox_test.rs index 56e8110f..528c5a55 100644 --- a/multi-transfer-esdt/tests/multi_transfer_blackbox_test.rs +++ b/multi-transfer-esdt/tests/multi_transfer_blackbox_test.rs @@ -767,6 +767,50 @@ fn basic_transfer_test() { .esdt_balance(BRIDGE_TOKEN_ID, token_amount); } +#[test] +fn basic_transfer_smart_contract_dest_test() { + let mut state = MultiTransferTestState::new(); + let token_amount = BigUint::from(500u64); + + state.deploy_contracts(); + state.config_multi_transfer(); + + let call_data = ManagedBuffer::from(b"add"); + call_data + .clone() + .concat(ManagedBuffer::from(GAS_LIMIT.to_string())); + call_data.clone().concat(ManagedBuffer::default()); + + let eth_tx = EthTransaction { + from: EthAddress { + raw_addr: ManagedByteArray::default(), + }, + to: ManagedAddress::from(MULTISIG_ADDRESS.eval_to_array()), + token_id: TokenIdentifier::from(BRIDGE_TOKEN_ID), + amount: token_amount.clone(), + tx_nonce: 1u64, + call_data: ManagedOption::some(call_data), + }; + + let mut transfers: MultiValueEncoded> = + MultiValueEncoded::new(); + transfers.push(eth_tx); + + state + .world + .tx() + .from(MULTISIG_ADDRESS) + .to(MULTI_TRANSFER_ADDRESS) + .typed(multi_transfer_esdt_proxy::MultiTransferEsdtProxy) + .batch_transfer_esdt_token(1u32, transfers) + .run(); + + state + .world + .check_account(BRIDGE_PROXY_ADDRESS) + .esdt_balance(BRIDGE_TOKEN_ID, token_amount); +} + #[test] fn batch_transfer_both_executed_test() { let mut state = MultiTransferTestState::new(); From 13d34c7aeebb33a46874e2149d30435882b249ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Mon, 9 Dec 2024 15:29:04 +0200 Subject: [PATCH 33/45] bridge-proxy: Clear storages after refund --- bridge-proxy/src/bridge-proxy.rs | 46 ++++++++----------- .../tests/bridge_proxy_blackbox_test.rs | 21 ++++++++- 2 files changed, 40 insertions(+), 27 deletions(-) diff --git a/bridge-proxy/src/bridge-proxy.rs b/bridge-proxy/src/bridge-proxy.rs index 97e28401..bb041f3b 100644 --- a/bridge-proxy/src/bridge-proxy.rs +++ b/bridge-proxy/src/bridge-proxy.rs @@ -9,7 +9,7 @@ use transaction::{CallData, EthTransaction}; const MIN_GAS_LIMIT_FOR_SC_CALL: u64 = 10_000_000; const MAX_GAS_LIMIT_FOR_SC_CALL: u64 = 249999999; const DEFAULT_GAS_LIMIT_FOR_REFUND_CALLBACK: u64 = 20_000_000; // 20 million -const DELAY_BEFORE_OWNER_CAN_CANCEL_TRANSACTION: u64 = 300; +const DELAY_BEFORE_OWNER_CAN_REFUND_TRANSACTION: u64 = 300; #[multiversx_sc::contract] pub trait BridgeProxyContract: @@ -103,38 +103,28 @@ pub trait BridgeProxyContract: tx_call.register_promise(); } - // TODO: will activate endpoint in a future release - // #[endpoint(cancel)] - fn cancel(&self, tx_id: usize) { - let tx_start_round = self.ongoing_execution(tx_id).get(); - let current_block_round = self.blockchain().get_block_round(); - require!( - current_block_round - tx_start_round > DELAY_BEFORE_OWNER_CAN_CANCEL_TRANSACTION, - "Transaction can't be cancelled yet" - ); - - let tx = self.get_pending_transaction_by_id(tx_id); - let payment = self.payments(tx_id).get(); - self.tx().to(tx.to).payment(payment).transfer(); - self.cleanup_transaction(tx_id); - } - #[promises_callback] fn execution_callback(&self, #[call_result] result: ManagedAsyncCallResult<()>, tx_id: usize) { if result.is_err() { self.add_pending_tx_to_refund(tx_id); } - self.cleanup_transaction(tx_id); + self.pending_transactions().remove(&tx_id); } #[endpoint(executeRefundTransaction)] fn execute_refund_transaction(&self, tx_id: usize) { - let tx = self.get_refund_transaction_by_id(tx_id); + let tx_start_round = self.ongoing_execution(tx_id).get(); + let current_block_round = self.blockchain().get_block_round(); + require!( + current_block_round - tx_start_round > DELAY_BEFORE_OWNER_CAN_REFUND_TRANSACTION, + "Refund executed too early!" + ); + let tx = self.get_refund_transaction_by_id(tx_id); let esdt_safe_contract_address = self.get_esdt_safe_address(); - let unwrapped_token = self.unwrap_token(&tx.token_id, tx_id); let batch_id = self.batch_id(tx_id).get(); + self.tx() .to(esdt_safe_contract_address) .typed(esdt_safe_proxy::EsdtSafeProxy) @@ -152,6 +142,8 @@ pub trait BridgeProxyContract: &unwrapped_token.amount, ) .sync_call(); + + self.finish_refund(tx_id); } fn unwrap_token(&self, requested_token: &TokenIdentifier, tx_id: usize) -> EsdtTokenPayment { @@ -188,7 +180,14 @@ pub trait BridgeProxyContract: fn finish_execute_gracefully(&self, tx_id: usize) { self.add_pending_tx_to_refund(tx_id); - self.cleanup_transaction(tx_id); + self.pending_transactions().remove(&tx_id); + } + + fn finish_refund(&self, tx_id: usize) { + self.refund_transactions().remove(&tx_id); + self.ongoing_execution(tx_id).clear(); + self.payments(tx_id).clear(); + self.batch_id(tx_id).clear(); } fn add_pending_tx_to_refund(&self, tx_id: usize) { @@ -196,11 +195,6 @@ pub trait BridgeProxyContract: self.refund_transactions().insert(tx_id, tx); } - fn cleanup_transaction(&self, tx_id: usize) { - self.pending_transactions().remove(&tx_id); - self.ongoing_execution(tx_id).clear(); - } - fn get_next_tx_id(&self) -> usize { let mut next_tx_id = self.highest_tx_id().get(); next_tx_id += 1; diff --git a/bridge-proxy/tests/bridge_proxy_blackbox_test.rs b/bridge-proxy/tests/bridge_proxy_blackbox_test.rs index 5156702b..ec8dd809 100644 --- a/bridge-proxy/tests/bridge_proxy_blackbox_test.rs +++ b/bridge-proxy/tests/bridge_proxy_blackbox_test.rs @@ -33,7 +33,7 @@ use multiversx_sc_scenario::{ scenario_model::*, ContractInfo, ScenarioWorld, }; -use multiversx_sc_scenario::{ExpectValue, ScenarioTxRun}; +use multiversx_sc_scenario::{ExpectError, ExpectValue, ScenarioTxRun}; use eth_address::*; use mock_proxies::mock_multisig_proxy; @@ -43,6 +43,7 @@ use transaction::{CallData, EthTransaction}; const BRIDGE_TOKEN_ID: TestTokenIdentifier = TestTokenIdentifier::new("BRIDGE-123456"); const WBRIDGE_TOKEN_ID: TestTokenIdentifier = TestTokenIdentifier::new("WBRIDGE-123456"); +const DELAY_BEFORE_OWNER_CAN_REFUND_TRANSACTION: u64 = 300; const GAS_LIMIT: u64 = 10_000_000; const TOO_SMALL_GAS_LIMIT: u64 = 1_000_000; @@ -238,6 +239,9 @@ impl BridgeProxyTestState { self } + fn set_block_round(&mut self, block_round_expr: u64) { + self.world.current_block().block_round(block_round_expr); + } } #[test] @@ -951,6 +955,17 @@ fn bridge_proxy_refund_tx_test() { .check_account(USER_ADDRESS) .esdt_balance(BRIDGE_TOKEN_ID, BigUint::from(INITIAL_BALANCE)); + test.world + .tx() + .from(USER_ADDRESS) + .to(BRIDGE_PROXY_ADDRESS) + .typed(bridge_proxy_contract_proxy::BridgeProxyContractProxy) + .execute_refund_transaction(1u32) + .returns(ExpectError(4, "Refund executed too early!")) + .run(); + + test.set_block_round(DELAY_BEFORE_OWNER_CAN_REFUND_TRANSACTION + 1); + test.world .tx() .from(USER_ADDRESS) @@ -962,4 +977,8 @@ fn bridge_proxy_refund_tx_test() { test.world .check_account(BRIDGE_PROXY_ADDRESS) .esdt_balance(BRIDGE_TOKEN_ID, BigUint::zero()); + + test.world + .check_account(BRIDGE_PROXY_ADDRESS) + .check_storage("str:highestTxId", "1"); } From 5f05e2e2ba412a9660071af8718845e10c375a46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Mon, 9 Dec 2024 17:52:49 +0200 Subject: [PATCH 34/45] bridge-proxy: Remove cooldown from refund --- bridge-proxy/src/bridge-proxy.rs | 10 +--------- bridge-proxy/tests/bridge_proxy_blackbox_test.rs | 11 ----------- 2 files changed, 1 insertion(+), 20 deletions(-) diff --git a/bridge-proxy/src/bridge-proxy.rs b/bridge-proxy/src/bridge-proxy.rs index bb041f3b..d97d8050 100644 --- a/bridge-proxy/src/bridge-proxy.rs +++ b/bridge-proxy/src/bridge-proxy.rs @@ -9,7 +9,6 @@ use transaction::{CallData, EthTransaction}; const MIN_GAS_LIMIT_FOR_SC_CALL: u64 = 10_000_000; const MAX_GAS_LIMIT_FOR_SC_CALL: u64 = 249999999; const DEFAULT_GAS_LIMIT_FOR_REFUND_CALLBACK: u64 = 20_000_000; // 20 million -const DELAY_BEFORE_OWNER_CAN_REFUND_TRANSACTION: u64 = 300; #[multiversx_sc::contract] pub trait BridgeProxyContract: @@ -113,13 +112,6 @@ pub trait BridgeProxyContract: #[endpoint(executeRefundTransaction)] fn execute_refund_transaction(&self, tx_id: usize) { - let tx_start_round = self.ongoing_execution(tx_id).get(); - let current_block_round = self.blockchain().get_block_round(); - require!( - current_block_round - tx_start_round > DELAY_BEFORE_OWNER_CAN_REFUND_TRANSACTION, - "Refund executed too early!" - ); - let tx = self.get_refund_transaction_by_id(tx_id); let esdt_safe_contract_address = self.get_esdt_safe_address(); let unwrapped_token = self.unwrap_token(&tx.token_id, tx_id); @@ -184,10 +176,10 @@ pub trait BridgeProxyContract: } fn finish_refund(&self, tx_id: usize) { - self.refund_transactions().remove(&tx_id); self.ongoing_execution(tx_id).clear(); self.payments(tx_id).clear(); self.batch_id(tx_id).clear(); + self.refund_transactions().remove(&tx_id); } fn add_pending_tx_to_refund(&self, tx_id: usize) { diff --git a/bridge-proxy/tests/bridge_proxy_blackbox_test.rs b/bridge-proxy/tests/bridge_proxy_blackbox_test.rs index ec8dd809..641a3dcf 100644 --- a/bridge-proxy/tests/bridge_proxy_blackbox_test.rs +++ b/bridge-proxy/tests/bridge_proxy_blackbox_test.rs @@ -955,17 +955,6 @@ fn bridge_proxy_refund_tx_test() { .check_account(USER_ADDRESS) .esdt_balance(BRIDGE_TOKEN_ID, BigUint::from(INITIAL_BALANCE)); - test.world - .tx() - .from(USER_ADDRESS) - .to(BRIDGE_PROXY_ADDRESS) - .typed(bridge_proxy_contract_proxy::BridgeProxyContractProxy) - .execute_refund_transaction(1u32) - .returns(ExpectError(4, "Refund executed too early!")) - .run(); - - test.set_block_round(DELAY_BEFORE_OWNER_CAN_REFUND_TRANSACTION + 1); - test.world .tx() .from(USER_ADDRESS) From 8f1211f9b9741e31513172b1864f54e9576de444 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Tue, 10 Dec 2024 10:48:00 +0200 Subject: [PATCH 35/45] multisig: clear_action fix --- multisig/src/lib.rs | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/multisig/src/lib.rs b/multisig/src/lib.rs index 037787d8..e9e7c6b4 100644 --- a/multisig/src/lib.rs +++ b/multisig/src/lib.rs @@ -19,7 +19,6 @@ use transaction::TxBatchSplitInFields; use transaction::*; use user_role::UserRole; -use core::cmp::max; use multiversx_sc::imports::*; const MAX_ACTIONS_INTER: usize = 10; @@ -459,11 +458,8 @@ pub trait Multisig: // if there's only one proposed action, // the action was already cleared at the beginning of this function if action_ids_mapper.len() > 1 { - for _ in 0..max(action_ids_mapper.len(), MAX_ACTIONS_INTER) { - match action_ids_mapper.values().next() { - Some(act_id) => self.clear_action(act_id), - None => sc_panic!("Could not retrieve an action id"), - }; + for act_id in action_ids_mapper.values().take(MAX_ACTIONS_INTER) { + self.clear_action(act_id); } } @@ -487,11 +483,8 @@ pub trait Multisig: // if there's only one proposed action, // the action was already cleared at the beginning of this function if action_ids_mapper.len() > 1 { - for _ in 0..max(action_ids_mapper.len(), MAX_ACTIONS_INTER) { - match action_ids_mapper.values().next() { - Some(act_id) => self.clear_action(act_id), - None => sc_panic!("Could not retrieve an action id"), - }; + for act_id in action_ids_mapper.values().take(MAX_ACTIONS_INTER) { + self.clear_action(act_id); } } From e6d3095abacb77c84cddaa8339c460738257a671 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Tue, 10 Dec 2024 16:42:52 +0200 Subject: [PATCH 36/45] multisig: test fixes --- .../mock-esdt-safe/wasm/src/lib.rs | 6 +- common/tx-batch-module/src/lib.rs | 2 +- multisig/tests/multisig_blackbox_test.rs | 65 ++----------------- multisig/wasm/Cargo.lock | 3 + 4 files changed, 13 insertions(+), 63 deletions(-) diff --git a/common/mock-contracts/mock-esdt-safe/wasm/src/lib.rs b/common/mock-contracts/mock-esdt-safe/wasm/src/lib.rs index 5f7aed31..30309bd8 100644 --- a/common/mock-contracts/mock-esdt-safe/wasm/src/lib.rs +++ b/common/mock-contracts/mock-esdt-safe/wasm/src/lib.rs @@ -6,9 +6,9 @@ // Init: 1 // Upgrade: 1 -// Endpoints: 1 +// Endpoints: 3 // Async Callback (empty): 1 -// Total number of exported functions: 4 +// Total number of exported functions: 6 #![no_std] @@ -21,6 +21,8 @@ multiversx_sc_wasm_adapter::endpoints! { init => init upgrade => upgrade createRefundTransaction => create_refund_transaction + withdrawTransactionFees => withdraw_transaction_fees + withdrawRefundFeesForEthereum => withdraw_refund_fees_for_ethereum ) } diff --git a/common/tx-batch-module/src/lib.rs b/common/tx-batch-module/src/lib.rs index 3547d75b..046211e5 100644 --- a/common/tx-batch-module/src/lib.rs +++ b/common/tx-batch-module/src/lib.rs @@ -10,7 +10,7 @@ pub mod batch_status; pub mod tx_batch_mapper; const MAX_TX_BATCH_BLOCK_DURATION: u64 = 1000; -const MAX_TX_BATCH_SIZE: usize = 20; +const MAX_TX_BATCH_SIZE: usize = 100; #[multiversx_sc::module] pub trait TxBatchModule { diff --git a/multisig/tests/multisig_blackbox_test.rs b/multisig/tests/multisig_blackbox_test.rs index b1af3e82..ccbf853f 100644 --- a/multisig/tests/multisig_blackbox_test.rs +++ b/multisig/tests/multisig_blackbox_test.rs @@ -416,7 +416,7 @@ impl MultiTransferTestState { .typed(esdt_safe_proxy::EsdtSafeProxy) .create_transaction( EthAddress::zero(), - OptionalValue::None::>, + OptionalValue::>::None, ) .single_esdt( &TokenIdentifier::from(WEGLD_TOKEN_ID), @@ -432,7 +432,7 @@ impl MultiTransferTestState { .typed(esdt_safe_proxy::EsdtSafeProxy) .create_transaction( EthAddress::zero(), - OptionalValue::None::>, + OptionalValue::>::None, ) .single_esdt( &TokenIdentifier::from(WEGLD_TOKEN_ID), @@ -1695,7 +1695,6 @@ fn test_upgrade_child_contract_from_source_success() { .upgrade_child_contract_from_source( child_sc_address.clone(), MOCK_MULTI_TRANSFER_ADDRESS, - false, init_args.clone(), ) .run(); @@ -1709,7 +1708,6 @@ fn test_upgrade_child_contract_from_source_success() { .upgrade_child_contract_from_source( child_sc_address.clone(), MOCK_MULTI_TRANSFER_ADDRESS, - true, init_args.clone(), ) .run(); @@ -1763,59 +1761,6 @@ fn test_remove_user_success() { state.assert_board_member_count(2usize); } -#[test] -fn test_remove_user_cannot_remove_all() { - let mut state = MultiTransferTestState::new(); - - let mut board: MultiValueEncoded> = - MultiValueEncoded::new(); - board.push(RELAYER1_ADDRESS.to_managed_address()); - board.push(RELAYER2_ADDRESS.to_managed_address()); - state - .world - .tx() - .from(OWNER_ADDRESS) - .typed(multisig_proxy::MultisigProxy) - .init( - ESDT_SAFE_ADDRESS, - MULTI_TRANSFER_ADDRESS, - BRIDGE_PROXY_ADDRESS, - BRIDGED_TOKENS_WRAPPER_ADDRESS, - PRICE_AGGREGATOR_ADDRESS, - 1_000u64, - 500u64, - 1usize, - board, - ) - .code(MULTISIG_CODE_PATH) - .new_address(MULTISIG_ADDRESS) - .run(); - state.safe_deploy(); - state.multi_transfer_deploy(); - state.bridge_proxy_deploy(); - state.bridged_tokens_wrapper_deploy(); - state.config_multisig(); - - state - .world - .tx() - .from(OWNER_ADDRESS) - .to(MULTISIG_ADDRESS) - .typed(multisig_proxy::MultisigProxy) - .remove_user(RELAYER1_ADDRESS.to_managed_address()) - .run(); - - state - .world - .tx() - .from(OWNER_ADDRESS) - .to(MULTISIG_ADDRESS) - .typed(multisig_proxy::MultisigProxy) - .remove_user(RELAYER2_ADDRESS.to_managed_address()) - .returns(ExpectError(4u64, "cannot remove all board members")) - .run(); -} - #[test] fn test_remove_user_quorum_exceed_board_size() { let mut state = MultiTransferTestState::new(); @@ -1845,7 +1790,7 @@ fn test_change_quorum_success() { state.assert_board_member_count(2usize); - let new_quorum = 1usize; + let new_quorum = 2usize; state .world .tx() @@ -2376,7 +2321,7 @@ fn test_esdt_safe_settings_management() { let esdt_safe_address = ESDT_SAFE_ADDRESS; - let new_max_tx_batch_size = 100usize; + let new_max_tx_batch_size = 50usize; let new_max_tx_batch_block_duration = 600u64; let token_id = TokenIdentifier::from("TEST-123456"); let max_bridged_amount = BigUint::from(1_000_000u64); @@ -2428,7 +2373,7 @@ fn test_multi_transfer_esdt_settings_management() { let token_id = TokenIdentifier::from("TEST-123456"); let max_bridged_amount = BigUint::from(1_000_000u64); - let new_max_refund_tx_batch_size = 100usize; + let new_max_refund_tx_batch_size = 50usize; let new_max_refund_tx_batch_block_duration = 600u64; state diff --git a/multisig/wasm/Cargo.lock b/multisig/wasm/Cargo.lock index 5af96c1d..9993d89a 100644 --- a/multisig/wasm/Cargo.lock +++ b/multisig/wasm/Cargo.lock @@ -265,6 +265,9 @@ dependencies = [ "eth-address", "fee-estimator-module", "max-bridged-amount-module", + "mock-esdt-safe", + "mock-multi-transfer-esdt", + "mock-price-aggregator", "multi-transfer-esdt", "multiversx-price-aggregator-sc", "multiversx-sc", From 87f80bd922e120ba7e016d279df2849c030ce4bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Tue, 10 Dec 2024 17:21:51 +0200 Subject: [PATCH 37/45] bridge-proxy: fix tests & lower gas limit for callback --- bridge-proxy/src/bridge-proxy.rs | 2 +- .../tests/bridge_proxy_blackbox_test.rs | 83 ++++++++++++++++--- 2 files changed, 71 insertions(+), 14 deletions(-) diff --git a/bridge-proxy/src/bridge-proxy.rs b/bridge-proxy/src/bridge-proxy.rs index d97d8050..4d72db4d 100644 --- a/bridge-proxy/src/bridge-proxy.rs +++ b/bridge-proxy/src/bridge-proxy.rs @@ -8,7 +8,7 @@ use sc_proxies::esdt_safe_proxy; use transaction::{CallData, EthTransaction}; const MIN_GAS_LIMIT_FOR_SC_CALL: u64 = 10_000_000; const MAX_GAS_LIMIT_FOR_SC_CALL: u64 = 249999999; -const DEFAULT_GAS_LIMIT_FOR_REFUND_CALLBACK: u64 = 20_000_000; // 20 million +const DEFAULT_GAS_LIMIT_FOR_REFUND_CALLBACK: u64 = 1_000_000; // 1 million #[multiversx_sc::contract] pub trait BridgeProxyContract: diff --git a/bridge-proxy/tests/bridge_proxy_blackbox_test.rs b/bridge-proxy/tests/bridge_proxy_blackbox_test.rs index 641a3dcf..f29a42db 100644 --- a/bridge-proxy/tests/bridge_proxy_blackbox_test.rs +++ b/bridge-proxy/tests/bridge_proxy_blackbox_test.rs @@ -545,8 +545,7 @@ fn test_highest_tx_id() { } } -// Will be moved to integration test -// #[test] +#[test] fn bridge_proxy_wrong_formatting_sc_call_test() { let mut test = BridgeProxyTestState::new(); @@ -599,8 +598,31 @@ fn bridge_proxy_wrong_formatting_sc_call_test() { // Refund: Funds are transfered to BridgedTokensWrapper test.world - .check_account(BRIDGED_TOKENS_WRAPPER_ADDRESS) - .esdt_balance(BRIDGE_TOKEN_ID, amount.clone()); + .check_account(BRIDGE_PROXY_ADDRESS) + .esdt_balance(BRIDGE_TOKEN_ID, amount); + + test.world + .check_account(BRIDGE_PROXY_ADDRESS) + .check_storage("str:refundTransactions.mapped|u32:1", "0x3031303230333034303530363037303830393130000000000000000005006e6f2d696e69742d73635f5f5f5f5f5f5f5f5f5f5f5f0000000d4252494447452d3132333435360000000201f4000000000000000100") + .check_storage("str:refundTransactions.value|u32:1", "0x01") + .check_storage("str:refundTransactions.node_id|u32:1", "0x01") + .check_storage("str:refundTransactions.info", "0x00000001000000010000000100000001") + .check_storage("str:refundTransactions.node_links|u32:1", "0x0000000000000000") + .check_storage("str:batchId|u32:1", "1") + .check_storage("str:highestTxId", "1") + .check_storage("str:payments|u32:1", "nested:str:BRIDGE-123456|u64:0|biguint:500"); +} + +#[test] +fn bridge_proxy_wrong_endpoint_sc_call_test() { + let mut test = BridgeProxyTestState::new(); + + test.multisig_deploy(); + test.deploy_bridge_proxy(); + test.deploy_crowdfunding(); + test.config_bridge(); + + let amount = BigUint::from(500u64); // Wrong endpoint for callData let mut args = ManagedVec::new(); @@ -641,7 +663,7 @@ fn bridge_proxy_wrong_formatting_sc_call_test() { .query() .to(BRIDGE_PROXY_ADDRESS) .typed(bridge_proxy_contract_proxy::BridgeProxyContractProxy) - .get_pending_transaction_by_id(2u32) + .get_pending_transaction_by_id(1u32) .returns(ExpectValue(eth_tx)) .run(); @@ -650,13 +672,36 @@ fn bridge_proxy_wrong_formatting_sc_call_test() { .from(OWNER_ADDRESS) .to(BRIDGE_PROXY_ADDRESS) .typed(bridge_proxy_contract_proxy::BridgeProxyContractProxy) - .execute(2u32) + .execute(1u32) + .with_gas_limit(GAS_LIMIT * 2) .run(); - // Refund: Funds are transfered to BridgedTokensWrapper test.world - .check_account(BRIDGED_TOKENS_WRAPPER_ADDRESS) - .esdt_balance(BRIDGE_TOKEN_ID, amount.clone() * 2u64); + .check_account(BRIDGE_PROXY_ADDRESS) + .esdt_balance(BRIDGE_TOKEN_ID, amount); + + test.world + .check_account(BRIDGE_PROXY_ADDRESS) + .check_storage("str:refundTransactions.mapped|u32:1", "0x30313032303330343035303630373038303931300000000000000000050063726f7766756e64696e675f5f5f5f5f5f5f5f5f5f5f0000000d4252494447452d3132333435360000000201f400000000000000020100000017000000066e6f66756e6300000000009896800100000000") + .check_storage("str:refundTransactions.value|u32:1", "0x01") + .check_storage("str:refundTransactions.node_id|u32:1", "0x01") + .check_storage("str:refundTransactions.info", "0x00000001000000010000000100000001") + .check_storage("str:refundTransactions.node_links|u32:1", "0x0000000000000000") + .check_storage("str:batchId|u32:1", "1") + .check_storage("str:highestTxId", "1") + .check_storage("str:payments|u32:1", "nested:str:BRIDGE-123456|u64:0|biguint:500"); +} + +#[test] +fn bridge_proxy_wrong_args_sc_call_test() { + let mut test = BridgeProxyTestState::new(); + + test.multisig_deploy(); + test.deploy_bridge_proxy(); + test.deploy_crowdfunding(); + test.config_bridge(); + + let amount = BigUint::from(500u64); // Wrong args let mut args = ManagedVec::new(); @@ -699,7 +744,7 @@ fn bridge_proxy_wrong_formatting_sc_call_test() { .query() .to(BRIDGE_PROXY_ADDRESS) .typed(bridge_proxy_contract_proxy::BridgeProxyContractProxy) - .get_pending_transaction_by_id(3u32) + .get_pending_transaction_by_id(1u32) .returns(ExpectValue(eth_tx)) .run(); @@ -708,13 +753,25 @@ fn bridge_proxy_wrong_formatting_sc_call_test() { .from(OWNER_ADDRESS) .to(BRIDGE_PROXY_ADDRESS) .typed(bridge_proxy_contract_proxy::BridgeProxyContractProxy) - .execute(3u32) + .execute(1u32) + .with_gas_limit(GAS_LIMIT * 2) .run(); // Refund: Funds are transfered to BridgedTokensWrapper test.world - .check_account(BRIDGED_TOKENS_WRAPPER_ADDRESS) - .esdt_balance(BRIDGE_TOKEN_ID, amount * 3u64); + .check_account(BRIDGE_PROXY_ADDRESS) + .esdt_balance(BRIDGE_TOKEN_ID, amount); + + test.world + .check_account(BRIDGE_PROXY_ADDRESS) + .check_storage("str:refundTransactions.mapped|u32:1", "0x30313032303330343035303630373038303931300000000000000000050063726f7766756e64696e675f5f5f5f5f5f5f5f5f5f5f0000000d4252494447452d3132333435360000000201f4000000000000000301000000220000000466756e64000000000098968001000000010000000977726f6e6761726773") + .check_storage("str:refundTransactions.value|u32:1", "0x01") + .check_storage("str:refundTransactions.node_id|u32:1", "0x01") + .check_storage("str:refundTransactions.info", "0x00000001000000010000000100000001") + .check_storage("str:refundTransactions.node_links|u32:1", "0x0000000000000000") + .check_storage("str:batchId|u32:1", "1") + .check_storage("str:highestTxId", "1") + .check_storage("str:payments|u32:1", "nested:str:BRIDGE-123456|u64:0|biguint:500"); } #[test] From ad82d8fb95bb612c9494a027e3fc284b6cb0ba26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Thu, 12 Dec 2024 14:40:42 +0200 Subject: [PATCH 38/45] Fixes after review --- bridge-proxy/src/bridge-proxy.rs | 6 ++++-- common/token-module/src/lib.rs | 5 ----- esdt-safe/src/lib.rs | 9 +++++++++ multi-transfer-esdt/src/lib.rs | 3 +++ multisig/src/lib.rs | 2 +- multisig/src/setup.rs | 18 +----------------- multisig/wasm/src/lib.rs | 5 ++--- 7 files changed, 20 insertions(+), 28 deletions(-) diff --git a/bridge-proxy/src/bridge-proxy.rs b/bridge-proxy/src/bridge-proxy.rs index 4d72db4d..bd40a18d 100644 --- a/bridge-proxy/src/bridge-proxy.rs +++ b/bridge-proxy/src/bridge-proxy.rs @@ -66,8 +66,8 @@ pub trait BridgeProxyContract: } else { CallData::default() }; - let non_empty_args = call_data.args.is_some(); - if (call_data.endpoint.is_empty() && non_empty_args) + + if call_data.endpoint.is_empty() || call_data.gas_limit < MIN_GAS_LIMIT_FOR_SC_CALL || call_data.gas_limit > MAX_GAS_LIMIT_FOR_SC_CALL { @@ -106,8 +106,10 @@ pub trait BridgeProxyContract: fn execution_callback(&self, #[call_result] result: ManagedAsyncCallResult<()>, tx_id: usize) { if result.is_err() { self.add_pending_tx_to_refund(tx_id); + // TODO: add event } self.pending_transactions().remove(&tx_id); + // TODO: add event } #[endpoint(executeRefundTransaction)] diff --git a/common/token-module/src/lib.rs b/common/token-module/src/lib.rs index 1048a8a1..713b54e0 100644 --- a/common/token-module/src/lib.rs +++ b/common/token-module/src/lib.rs @@ -248,11 +248,6 @@ pub trait TokenModule: "Can init only for mintable/burnable tokens" ); - // require!( - // !self.native_token(token_id).get(), - // "Cannot init native tokens" - // ); - self.mint_balances(token_id).set(mint_amount); self.burn_balances(token_id).set(burn_amount); } diff --git a/esdt-safe/src/lib.rs b/esdt-safe/src/lib.rs index 4cf39ef3..d9e6c4fb 100644 --- a/esdt-safe/src/lib.rs +++ b/esdt-safe/src/lib.rs @@ -265,6 +265,7 @@ pub trait EsdtSafe: opt_min_bridge_amount: OptionalValue>, ) -> TransactionDetails { require!(self.not_paused(), "Cannot create transaction while paused"); + require!(to != EthAddress::zero(), "Cannot send to an empty address"); let (payment_token, payment_amount) = self.call_value().single_fungible_esdt(); self.require_token_in_whitelist(&payment_token); @@ -384,6 +385,14 @@ pub trait EsdtSafe: to: EthAddress, opt_refund_info: OptionalValue>, ) { + let bridge_proxy_address = self.get_bridge_proxy_address(); + let caller = self.blockchain().get_caller(); + + require!( + bridge_proxy_address == caller, + "Only BridgeProxy SC can call this endpoint" + ); + let transaction_details = self.create_transaction_common(to, OptionalValue::None); let caller = self.blockchain().get_caller(); diff --git a/multi-transfer-esdt/src/lib.rs b/multi-transfer-esdt/src/lib.rs index b32234ef..c4d3ef5f 100644 --- a/multi-transfer-esdt/src/lib.rs +++ b/multi-transfer-esdt/src/lib.rs @@ -66,6 +66,8 @@ pub trait MultiTransferEsdt: continue; } + //TODO simplify this + let is_success: bool = self .tx() .to(safe_address.clone()) @@ -74,6 +76,7 @@ pub trait MultiTransferEsdt: .returns(ReturnsResult) .sync_call(); + //TODO: transform require to if require!(is_success, "Invalid token or amount"); let universal_token = self.get_universal_token(eth_tx.clone()); diff --git a/multisig/src/lib.rs b/multisig/src/lib.rs index e9e7c6b4..893dd424 100644 --- a/multisig/src/lib.rs +++ b/multisig/src/lib.rs @@ -518,7 +518,7 @@ pub trait Multisig: let action_ids_mapper = self.batch_id_to_action_id_mapping(eth_batch_id); - for act_id in action_ids_mapper.values() { + for act_id in action_ids_mapper.values().take(MAX_ACTIONS_INTER) { self.clear_action(act_id); } } diff --git a/multisig/src/setup.rs b/multisig/src/setup.rs index d2f6b8fc..5c9b348c 100644 --- a/multisig/src/setup.rs +++ b/multisig/src/setup.rs @@ -2,7 +2,7 @@ use multiversx_sc::imports::*; use eth_address::EthAddress; use sc_proxies::{ - bridge_proxy_contract_proxy, bridged_tokens_wrapper_proxy, esdt_safe_proxy, + bridge_proxy_contract_proxy, esdt_safe_proxy, multi_transfer_esdt_proxy, }; @@ -412,20 +412,4 @@ pub trait SetupModule: .set_max_tx_batch_block_duration(new_max_tx_batch_block_duration) .sync_call(); } - - #[only_owner] - #[payable("*")] - #[endpoint(bridgedTokensWrapperDepositLiquidity)] - fn bridged_tokens_wrapper_deposit_liquidity(&self) { - let (payment_token, payment_amount) = self.call_value().single_fungible_esdt(); - - let bridged_tokens_wrapper_addr = self.bridged_tokens_wrapper_address().get(); - - self.tx() - .to(bridged_tokens_wrapper_addr) - .typed(bridged_tokens_wrapper_proxy::BridgedTokensWrapperProxy) - .deposit_liquidity() - .single_esdt(&payment_token, 0, &payment_amount) - .sync_call(); - } } diff --git a/multisig/wasm/src/lib.rs b/multisig/wasm/src/lib.rs index abffe2c6..45ec811e 100644 --- a/multisig/wasm/src/lib.rs +++ b/multisig/wasm/src/lib.rs @@ -6,9 +6,9 @@ // Init: 1 // Upgrade: 1 -// Endpoints: 74 +// Endpoints: 73 // Async Callback (empty): 1 -// Total number of exported functions: 77 +// Total number of exported functions: 76 #![no_std] @@ -58,7 +58,6 @@ multiversx_sc_wasm_adapter::endpoints! { multiTransferEsdtSetMaxBridgedAmountForToken => multi_transfer_esdt_set_max_bridged_amount_for_token multiTransferEsdtSetMaxRefundTxBatchSize => multi_transfer_esdt_set_max_refund_tx_batch_size multiTransferEsdtSetMaxRefundTxBatchBlockDuration => multi_transfer_esdt_set_max_refund_tx_batch_block_duration - bridgedTokensWrapperDepositLiquidity => bridged_tokens_wrapper_deposit_liquidity getQuorum => quorum getNumBoardMembers => num_board_members getRequiredStakeAmount => required_stake_amount From 227e6da2ebac4d5705662ca4c225ee18b16e9450 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Thu, 12 Dec 2024 22:51:27 +0200 Subject: [PATCH 39/45] bridged-tokens-wrapper: fix unit test --- .../scenarios/blacklist_token.scen.json | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/bridged-tokens-wrapper/scenarios/blacklist_token.scen.json b/bridged-tokens-wrapper/scenarios/blacklist_token.scen.json index b06dfa0b..5604d551 100644 --- a/bridged-tokens-wrapper/scenarios/blacklist_token.scen.json +++ b/bridged-tokens-wrapper/scenarios/blacklist_token.scen.json @@ -25,6 +25,78 @@ "gas": "*", "refund": "*" } + }, + { + "step": "checkState", + "accounts": { + "address:owner": { + "nonce": "4", + "balance": "0", + "storage": {} + }, + "address:user": { + "nonce": "4", + "esdt": { + "str:USDC-aaaaaa": { + "balance": "200000000000000" + }, + "str:USDC-bbbbbb": { + "balance": "500000000000000" + }, + "str:USDC-cccccc": { + "balance": "400000000000000" + }, + "str:WUSDC-abcdef": { + "balance": "900" + } + }, + "storage": {} + }, + "sc:bridged_tokens_wrapper": { + "nonce": "0", + "esdt": { + "str:WUSDC-abcdef": { + "balance": "1", + "roles": [ + "ESDTRoleLocalMint", + "ESDTRoleLocalBurn" + ] + }, + "str:WUSDC-uvwxyz": { + "balance": "1", + "roles": [ + "ESDTRoleLocalMint", + "ESDTRoleLocalBurn" + ] + }, + "str:USDC-aaaaaa": { + "balance": "300000000000000" + }, + "str:USDC-cccccc": { + "balance": "100000000000000" + } + }, + "storage": { + "str:chainSpecificTokenIds|nested:str:WUSDC-abcdef|str:.len": "2", + "str:chainSpecificTokenIds|nested:str:WUSDC-abcdef|str:.index|nested:str:USDC-aaaaaa": "1", + "str:chainSpecificTokenIds|nested:str:WUSDC-abcdef|str:.index|nested:str:USDC-cccccc": "2", + "str:chainSpecificTokenIds|nested:str:WUSDC-abcdef|str:.item|u32:1": "str:USDC-aaaaaa", + "str:chainSpecificTokenIds|nested:str:WUSDC-abcdef|str:.item|u32:2": "str:USDC-cccccc", + "str:chainSpecificToUniversalMapping|nested:str:USDC-cccccc": "str:WUSDC-abcdef", + "str:chainSpecificToUniversalMapping|nested:str:USDC-aaaaaa": "str:WUSDC-abcdef", + "str:universalBridgedTokenIds.len": "1", + "str:universalBridgedTokenIds.index|nested:str:WUSDC-abcdef": "1", + "str:universalBridgedTokenIds.item|u32:1": "str:WUSDC-abcdef", + "str:tokenLiquidity|nested:str:USDC-aaaaaa": "300000000000000", + "str:tokenLiquidity|nested:str:USDC-cccccc": "100000000000000", + "str:tokenDecimalsNum|nested:str:WUSDC-abcdef": "6", + "str:tokenDecimalsNum|nested:str:USDC-cccccc": "18", + "str:tokenDecimalsNum|nested:str:USDC-aaaaaa": "18" + }, + "code": "file:../output/bridged-tokens-wrapper.wasm", + "owner": "address:owner" + } + } } ] } \ No newline at end of file From 4f6a6ebe39303ac75325f88afbbe528f7f40c8a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Thu, 12 Dec 2024 22:51:43 +0200 Subject: [PATCH 40/45] bridge-proxy: add unit test for empty endpoint and non-empty gas limit --- .../tests/bridge_proxy_blackbox_test.rs | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/bridge-proxy/tests/bridge_proxy_blackbox_test.rs b/bridge-proxy/tests/bridge_proxy_blackbox_test.rs index f29a42db..33449bca 100644 --- a/bridge-proxy/tests/bridge_proxy_blackbox_test.rs +++ b/bridge-proxy/tests/bridge_proxy_blackbox_test.rs @@ -927,6 +927,83 @@ fn bridge_proxy_empty_endpoint_with_args_test() { ); } +#[test] +fn bridge_proxy_empty_endpoint_with_gas_test() { + let mut test = BridgeProxyTestState::new(); + + test.world.start_trace(); + + test.multisig_deploy(); + test.deploy_bridge_proxy(); + test.deploy_crowdfunding(); + test.config_bridge(); + + let call_data: CallData = CallData { + endpoint: ManagedBuffer::new(), + gas_limit: GAS_LIMIT, + args: ManagedOption::none(), + }; + + let call_data: ManagedBuffer = + ManagedSerializer::new().top_encode_to_managed_buffer(&call_data); + + let eth_tx = EthTransaction { + from: EthAddress { + raw_addr: ManagedByteArray::new_from_bytes(b"01020304050607080910"), + }, + to: ManagedAddress::from(CROWDFUNDING_ADDRESS.eval_to_array()), + token_id: BRIDGE_TOKEN_ID.into(), + amount: BigUint::from(500u64), + tx_nonce: 1u64, + call_data: ManagedOption::some(call_data), + }; + + let amount = BigUint::from(500u64); + + test.world + .tx() + .from(MULTI_TRANSFER_ADDRESS) + .to(BRIDGE_PROXY_ADDRESS) + .typed(bridge_proxy_contract_proxy::BridgeProxyContractProxy) + .deposit(ð_tx, 1u64) + .egld_or_single_esdt( + &EgldOrEsdtTokenIdentifier::esdt(BRIDGE_TOKEN_ID), + 0, + &amount, + ) + .run(); + + test.world + .query() + .to(BRIDGE_PROXY_ADDRESS) + .typed(bridge_proxy_contract_proxy::BridgeProxyContractProxy) + .get_pending_transaction_by_id(1u32) + .returns(ExpectValue(eth_tx)) + .run(); + + test.world + .tx() + .from(OWNER_ADDRESS) + .to(BRIDGE_PROXY_ADDRESS) + .typed(bridge_proxy_contract_proxy::BridgeProxyContractProxy) + .execute(1u32) + .run(); + + test.world + .check_account(BRIDGE_PROXY_ADDRESS) + .check_storage("str:refundTransactions.mapped|u32:1", "0x30313032303330343035303630373038303931300000000000000000050063726f7766756e64696e675f5f5f5f5f5f5f5f5f5f5f0000000d4252494447452d3132333435360000000201f4000000000000000101000000110000000000000000009896800100000000") + .check_storage("str:refundTransactions.value|u32:1", "0x01") + .check_storage("str:refundTransactions.node_id|u32:1", "0x01") + .check_storage("str:refundTransactions.info", "0x00000001000000010000000100000001") + .check_storage("str:refundTransactions.node_links|u32:1", "0x0000000000000000") + .check_storage("str:batchId|u32:1", "1") + .check_storage("str:highestTxId", "1") + .check_storage( + "str:payments|u32:1", + "nested:str:BRIDGE-123456|u64:0|biguint:500", + ); +} + #[test] fn bridge_proxy_refund_tx_test() { let mut test = BridgeProxyTestState::new(); From 05515ec2a212904b2641ebdfa3beabf766535a6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Thu, 12 Dec 2024 23:07:55 +0200 Subject: [PATCH 41/45] Add events for finish execute --- bridge-proxy/src/bridge-proxy.rs | 7 +++++-- bridge-proxy/src/events.rs | 8 ++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) create mode 100644 bridge-proxy/src/events.rs diff --git a/bridge-proxy/src/bridge-proxy.rs b/bridge-proxy/src/bridge-proxy.rs index bd40a18d..f587010c 100644 --- a/bridge-proxy/src/bridge-proxy.rs +++ b/bridge-proxy/src/bridge-proxy.rs @@ -2,6 +2,7 @@ use multiversx_sc::imports::*; pub mod config; +mod events; use sc_proxies::bridged_tokens_wrapper_proxy; use sc_proxies::esdt_safe_proxy; @@ -13,6 +14,7 @@ const DEFAULT_GAS_LIMIT_FOR_REFUND_CALLBACK: u64 = 1_000_000; // 1 million #[multiversx_sc::contract] pub trait BridgeProxyContract: config::ConfigModule + + events::EventsModule + multiversx_sc_modules::pause::PauseModule + storage_module::CommonStorageModule { @@ -106,10 +108,11 @@ pub trait BridgeProxyContract: fn execution_callback(&self, #[call_result] result: ManagedAsyncCallResult<()>, tx_id: usize) { if result.is_err() { self.add_pending_tx_to_refund(tx_id); - // TODO: add event + self.execute_generated_refund(tx_id); + } else { + self.execute_succesfully_finished(tx_id); } self.pending_transactions().remove(&tx_id); - // TODO: add event } #[endpoint(executeRefundTransaction)] diff --git a/bridge-proxy/src/events.rs b/bridge-proxy/src/events.rs new file mode 100644 index 00000000..beea9b17 --- /dev/null +++ b/bridge-proxy/src/events.rs @@ -0,0 +1,8 @@ +#[multiversx_sc::module] +pub trait EventsModule { + #[event("executeSuccesfullyFinished")] + fn execute_succesfully_finished(&self, #[indexed] tx_id: usize); + + #[event("executeGeneratedRefund")] + fn execute_generated_refund(&self, #[indexed] tx_id: usize); +} From 7db4577cb37111b90ffe068a7359a81e88f77f24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Thu, 12 Dec 2024 23:33:08 +0200 Subject: [PATCH 42/45] unit tests fixes after reviews applied --- .../tests/bridge_proxy_blackbox_test.rs | 2 +- common/sc-proxies/src/multisig_proxy.rs | 8 -- esdt-safe/tests/esdt_safe_blackbox_test.rs | 76 ++++++++++++++++++- .../tests/multi_transfer_blackbox_test.rs | 4 +- multisig/src/setup.rs | 5 +- multisig/tests/multisig_blackbox_test.rs | 24 ++++-- 6 files changed, 95 insertions(+), 24 deletions(-) diff --git a/bridge-proxy/tests/bridge_proxy_blackbox_test.rs b/bridge-proxy/tests/bridge_proxy_blackbox_test.rs index 33449bca..b0087c2b 100644 --- a/bridge-proxy/tests/bridge_proxy_blackbox_test.rs +++ b/bridge-proxy/tests/bridge_proxy_blackbox_test.rs @@ -991,7 +991,7 @@ fn bridge_proxy_empty_endpoint_with_gas_test() { test.world .check_account(BRIDGE_PROXY_ADDRESS) - .check_storage("str:refundTransactions.mapped|u32:1", "0x30313032303330343035303630373038303931300000000000000000050063726f7766756e64696e675f5f5f5f5f5f5f5f5f5f5f0000000d4252494447452d3132333435360000000201f4000000000000000101000000110000000000000000009896800100000000") + .check_storage("str:refundTransactions.mapped|u32:1", "0x30313032303330343035303630373038303931300000000000000000050063726f7766756e64696e675f5f5f5f5f5f5f5f5f5f5f0000000d4252494447452d3132333435360000000201f40000000000000001010000000d00000000000000000098968000") .check_storage("str:refundTransactions.value|u32:1", "0x01") .check_storage("str:refundTransactions.node_id|u32:1", "0x01") .check_storage("str:refundTransactions.info", "0x00000001000000010000000100000001") diff --git a/common/sc-proxies/src/multisig_proxy.rs b/common/sc-proxies/src/multisig_proxy.rs index 06d2b7e7..f3e21fdf 100644 --- a/common/sc-proxies/src/multisig_proxy.rs +++ b/common/sc-proxies/src/multisig_proxy.rs @@ -697,14 +697,6 @@ where .original_result() } - pub fn bridged_tokens_wrapper_deposit_liquidity( - self, - ) -> TxTypedCall { - self.wrapped_tx - .raw_call("bridgedTokensWrapperDepositLiquidity") - .original_result() - } - /// Minimum number of signatures needed to perform any action. pub fn quorum( self, diff --git a/esdt-safe/tests/esdt_safe_blackbox_test.rs b/esdt-safe/tests/esdt_safe_blackbox_test.rs index 8ab9799b..c3e3e06e 100644 --- a/esdt-safe/tests/esdt_safe_blackbox_test.rs +++ b/esdt-safe/tests/esdt_safe_blackbox_test.rs @@ -347,7 +347,9 @@ impl EsdtSafeTestState { ) { self.esdt_raw_transaction() .create_transaction( - EthAddress::zero(), + EthAddress { + raw_addr: ManagedByteArray::new_from_bytes(b"01020304050607080910"), + }, OptionalValue::>::None, ) .egld_or_single_esdt( @@ -362,7 +364,9 @@ impl EsdtSafeTestState { fn single_transaction_should_work(&mut self, token_id: TestTokenIdentifier, amount: u64) { self.esdt_raw_transaction() .create_transaction( - EthAddress::zero(), + EthAddress { + raw_addr: ManagedByteArray::new_from_bytes(b"01020304050607080910"), + }, OptionalValue::>::None, ) .egld_or_single_esdt( @@ -717,7 +721,12 @@ fn esdt_safe_create_transaction() { .from(BRIDGE_PROXY_ADDRESS) .to(ESDT_SAFE_ADDRESS) .typed(esdt_safe_proxy::EsdtSafeProxy) - .create_refund_transaction(EthAddress::zero(), OptionalValue::Some(refund_info.clone())) + .create_refund_transaction( + EthAddress { + raw_addr: ManagedByteArray::new_from_bytes(b"01020304050607080910"), + }, + OptionalValue::Some(refund_info.clone()), + ) .single_esdt(&TOKEN_ID.into(), 0, &BigUint::from(10u64)) .run(); @@ -727,7 +736,12 @@ fn esdt_safe_create_transaction() { .from(BRIDGED_TOKENS_WRAPPER_ADDRESS) .to(ESDT_SAFE_ADDRESS) .typed(esdt_safe_proxy::EsdtSafeProxy) - .create_refund_transaction(EthAddress::zero(), OptionalValue::Some(refund_info.clone())) + .create_transaction( + EthAddress { + raw_addr: ManagedByteArray::new_from_bytes(b"01020304050607080910"), + }, + OptionalValue::>::None, + ) .single_esdt(&TOKEN_ID.into(), 0, &BigUint::from(10u64)) .run(); @@ -776,6 +790,44 @@ fn esdt_safe_create_transaction() { assert_eq!(total_balances, 120000u64); } +#[test] +fn create_refund_transaction_not_from_bridge_proxy_test() { + let mut state = EsdtSafeTestState::new(); + state.multisig_deploy(); + state.safe_deploy(); + + state.world.set_esdt_balance( + MULTISIG_ADDRESS, + b"TOKEN-WITH", + BigUint::from(10_000_000u64), + ); + + let refund_info = sc_proxies::esdt_safe_proxy::RefundInfo:: { + address: ManagedAddress::from(OWNER_ADDRESS.eval_to_array()), + initial_batch_id: 1u64, + initial_nonce: 1u64, + }; + + state + .world + .tx() + .from(OWNER_ADDRESS) + .to(ESDT_SAFE_ADDRESS) + .typed(esdt_safe_proxy::EsdtSafeProxy) + .create_refund_transaction( + EthAddress { + raw_addr: ManagedByteArray::new_from_bytes(b"01020304050607080910"), + }, + OptionalValue::Some(refund_info.clone()), + ) + .single_esdt(&TOKEN_ID.into(), 0, &BigUint::from(10u64)) + .returns(ExpectError( + ERROR, + "Only BridgeProxy SC can call this endpoint", + )) + .run(); +} + #[test] fn add_refund_batch_test() { let mut state = EsdtSafeTestState::new(); @@ -1233,6 +1285,22 @@ fn withdraw_transaction_fees_test() { OptionalValue::>::None, ) .single_esdt(&TOKEN_ID.into(), 0, &BigUint::from(1_000_000u64)) + .returns(ExpectError(ERROR, "Cannot send to an empty address")) + .run(); + + state + .world + .tx() + .from(OWNER_ADDRESS) + .to(ESDT_SAFE_ADDRESS) + .typed(esdt_safe_proxy::EsdtSafeProxy) + .create_transaction( + EthAddress { + raw_addr: ManagedByteArray::new_from_bytes(b"01020304050607080910"), + }, + OptionalValue::>::None, + ) + .single_esdt(&TOKEN_ID.into(), 0, &BigUint::from(1_000_000u64)) .returns(ReturnsResult) .run(); diff --git a/multi-transfer-esdt/tests/multi_transfer_blackbox_test.rs b/multi-transfer-esdt/tests/multi_transfer_blackbox_test.rs index 528c5a55..5e193701 100644 --- a/multi-transfer-esdt/tests/multi_transfer_blackbox_test.rs +++ b/multi-transfer-esdt/tests/multi_transfer_blackbox_test.rs @@ -1173,7 +1173,9 @@ fn test_unwrap_token_create_transaction_should_work() { .unwrap_token_create_transaction( WRAPPED_TOKEN_ID, ESDT_SAFE_ADDRESS.to_address(), - EthAddress::zero(), + EthAddress { + raw_addr: ManagedByteArray::new_from_bytes(b"01020304050607080910"), + }, OptionalValue::>::None, ) .egld_or_single_esdt( diff --git a/multisig/src/setup.rs b/multisig/src/setup.rs index 5c9b348c..8e90cdf1 100644 --- a/multisig/src/setup.rs +++ b/multisig/src/setup.rs @@ -1,10 +1,7 @@ use multiversx_sc::imports::*; use eth_address::EthAddress; -use sc_proxies::{ - bridge_proxy_contract_proxy, esdt_safe_proxy, - multi_transfer_esdt_proxy, -}; +use sc_proxies::{bridge_proxy_contract_proxy, esdt_safe_proxy, multi_transfer_esdt_proxy}; const MAX_BOARD_MEMBERS: usize = 40; diff --git a/multisig/tests/multisig_blackbox_test.rs b/multisig/tests/multisig_blackbox_test.rs index ccbf853f..26588718 100644 --- a/multisig/tests/multisig_blackbox_test.rs +++ b/multisig/tests/multisig_blackbox_test.rs @@ -415,7 +415,9 @@ impl MultiTransferTestState { .to(ESDT_SAFE_ADDRESS) .typed(esdt_safe_proxy::EsdtSafeProxy) .create_transaction( - EthAddress::zero(), + EthAddress { + raw_addr: ManagedByteArray::new_from_bytes(b"01020304050607080910"), + }, OptionalValue::>::None, ) .single_esdt( @@ -431,7 +433,9 @@ impl MultiTransferTestState { .to(ESDT_SAFE_ADDRESS) .typed(esdt_safe_proxy::EsdtSafeProxy) .create_transaction( - EthAddress::zero(), + EthAddress { + raw_addr: ManagedByteArray::new_from_bytes(b"01020304050607080910"), + }, OptionalValue::>::None, ) .single_esdt( @@ -1820,7 +1824,9 @@ fn test_add_mapping_success() { let token_id = TokenIdentifier::from(WEGLD_TOKEN_ID); - let erc20_address = EthAddress::zero(); + let erc20_address = EthAddress { + raw_addr: ManagedByteArray::new_from_bytes(b"01020304050607080910"), + }; state .world @@ -1858,7 +1864,9 @@ fn test_add_mapping_token_id_already_mapped() { let token_id = TokenIdentifier::from(WEGLD_TOKEN_ID); - let erc20_address = EthAddress::zero(); + let erc20_address = EthAddress { + raw_addr: ManagedByteArray::new_from_bytes(b"01020304050607080910"), + }; state .world @@ -1888,7 +1896,9 @@ fn test_add_mapping_erc20_address_already_mapped() { let token_id = TokenIdentifier::from(WEGLD_TOKEN_ID); - let erc20_address = EthAddress::zero(); + let erc20_address = EthAddress { + raw_addr: ManagedByteArray::new_from_bytes(b"01020304050607080910"), + }; state .world @@ -1918,7 +1928,9 @@ fn test_clear_mapping_success() { let token_id = TokenIdentifier::from(WEGLD_TOKEN_ID); - let erc20_address = EthAddress::zero(); + let erc20_address = EthAddress { + raw_addr: ManagedByteArray::new_from_bytes(b"01020304050607080910"), + }; state .world From 247fb5b55f02481769239566be89878a3700f7ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Thu, 12 Dec 2024 23:51:44 +0200 Subject: [PATCH 43/45] multi-transfer: refactor batch_transfer --- multi-transfer-esdt/src/lib.rs | 64 ++++++++++++++++++++-------------- 1 file changed, 38 insertions(+), 26 deletions(-) diff --git a/multi-transfer-esdt/src/lib.rs b/multi-transfer-esdt/src/lib.rs index c4d3ef5f..9d9ff858 100644 --- a/multi-transfer-esdt/src/lib.rs +++ b/multi-transfer-esdt/src/lib.rs @@ -59,15 +59,15 @@ pub trait MultiTransferEsdt: .blockchain() .get_esdt_local_roles(ð_tx.token_id.clone()); if token_roles.has_role(&EsdtLocalRole::Transfer) { - let refund_tx = self.convert_to_refund_tx(eth_tx.clone()); - refund_tx_list.push(refund_tx); - self.token_with_transfer_role(eth_tx.token_id); + // let refund_tx = self.convert_to_refund_tx(eth_tx.clone()); + // refund_tx_list.push(refund_tx); + // self.token_with_transfer_role_event(eth_tx.token_id); + self.add_eth_tx_to_refund_tx_list(eth_tx.clone(), &mut refund_tx_list); + self.token_with_transfer_role_event(eth_tx.token_id); continue; } - //TODO simplify this - let is_success: bool = self .tx() .to(safe_address.clone()) @@ -76,27 +76,26 @@ pub trait MultiTransferEsdt: .returns(ReturnsResult) .sync_call(); - //TODO: transform require to if - require!(is_success, "Invalid token or amount"); + if !is_success { + self.add_eth_tx_to_refund_tx_list(eth_tx, &mut refund_tx_list); + continue; + } let universal_token = self.get_universal_token(eth_tx.clone()); - let mut must_refund = false; if eth_tx.to.is_zero() { - self.transfer_failed_invalid_destination(batch_id, eth_tx.tx_nonce); - must_refund = true; - } else if self.is_above_max_amount(ð_tx.token_id, ð_tx.amount) { - self.transfer_over_max_amount(batch_id, eth_tx.tx_nonce); - must_refund = true; - } else if self.is_account_same_shard_frozen(sc_shard, ð_tx.to, &universal_token) { - self.transfer_failed_frozen_destination_account(batch_id, eth_tx.tx_nonce); - must_refund = true; + self.add_eth_tx_to_refund_tx_list(eth_tx.clone(), &mut refund_tx_list); + self.transfer_failed_invalid_destination_event(batch_id, eth_tx.tx_nonce); + continue; } - - if must_refund { - let refund_tx = self.convert_to_refund_tx(eth_tx); - refund_tx_list.push(refund_tx); - + if self.is_above_max_amount(ð_tx.token_id, ð_tx.amount) { + self.add_eth_tx_to_refund_tx_list(eth_tx.clone(), &mut refund_tx_list); + self.transfer_over_max_amount_event(batch_id, eth_tx.tx_nonce); + continue; + } + if self.is_account_same_shard_frozen(sc_shard, ð_tx.to, &universal_token) { + self.add_eth_tx_to_refund_tx_list(eth_tx.clone(), &mut refund_tx_list); + self.transfer_failed_frozen_destination_account_event(batch_id, eth_tx.tx_nonce); continue; } @@ -190,6 +189,15 @@ pub trait MultiTransferEsdt: // private + fn add_eth_tx_to_refund_tx_list( + &self, + eth_tx: EthTransaction, + refund_tx_list: &mut ManagedVec>, + ) { + let refund_tx = self.convert_to_refund_tx(eth_tx); + refund_tx_list.push(refund_tx); + } + fn is_refund_valid(&self, token_id: &TokenIdentifier) -> bool { let esdt_safe_addr = self.get_esdt_safe_address(); let own_sc_address = self.blockchain().get_sc_address(); @@ -327,23 +335,27 @@ pub trait MultiTransferEsdt: ); #[event("transferFailedInvalidDestination")] - fn transfer_failed_invalid_destination(&self, #[indexed] batch_id: u64, #[indexed] tx_id: u64); + fn transfer_failed_invalid_destination_event( + &self, + #[indexed] batch_id: u64, + #[indexed] tx_id: u64, + ); #[event("tokenWithTransferRole")] - fn token_with_transfer_role(&self, #[indexed] token_id: TokenIdentifier); + fn token_with_transfer_role_event(&self, #[indexed] token_id: TokenIdentifier); #[event("transferFailedInvalidToken")] - fn transfer_failed_invalid_token(&self, #[indexed] batch_id: u64, #[indexed] tx_id: u64); + fn transfer_failed_invalid_token_event(&self, #[indexed] batch_id: u64, #[indexed] tx_id: u64); #[event("transferFailedFrozenDestinationAccount")] - fn transfer_failed_frozen_destination_account( + fn transfer_failed_frozen_destination_account_event( &self, #[indexed] batch_id: u64, #[indexed] tx_id: u64, ); #[event("transferOverMaxAmount")] - fn transfer_over_max_amount(&self, #[indexed] batch_id: u64, #[indexed] tx_id: u64); + fn transfer_over_max_amount_event(&self, #[indexed] batch_id: u64, #[indexed] tx_id: u64); #[event("unprocessedRefundTxs")] fn unprocessed_refund_txs_event(&self, #[indexed] tx_id: u64); From 38538b9e57d2fc54cea4d34d907924f45e3c8ebc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Thu, 12 Dec 2024 23:56:00 +0200 Subject: [PATCH 44/45] multisig: Fix unit tests --- ..._to_multiversx_relayer_call_data_several_tx_test.scen.json | 4 ++-- .../ethereum_to_multiversx_relayer_query2_test.scen.json | 4 ++-- multisig/tests/multisig_blackbox_test.rs | 2 -- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/multisig/scenarios/ethereum_to_multiversx_relayer_call_data_several_tx_test.scen.json b/multisig/scenarios/ethereum_to_multiversx_relayer_call_data_several_tx_test.scen.json index be6db6b8..e5305f1a 100644 --- a/multisig/scenarios/ethereum_to_multiversx_relayer_call_data_several_tx_test.scen.json +++ b/multisig/scenarios/ethereum_to_multiversx_relayer_call_data_several_tx_test.scen.json @@ -386,8 +386,8 @@ "gasLimit": "5,000,000" }, "expect": { - "status": "4", - "message": "str:Invalid token or amount" + "out": [], + "status": "0" } } ] diff --git a/multisig/scenarios/ethereum_to_multiversx_relayer_query2_test.scen.json b/multisig/scenarios/ethereum_to_multiversx_relayer_query2_test.scen.json index 2ded2f90..7434eed1 100644 --- a/multisig/scenarios/ethereum_to_multiversx_relayer_query2_test.scen.json +++ b/multisig/scenarios/ethereum_to_multiversx_relayer_query2_test.scen.json @@ -414,8 +414,8 @@ "gasLimit": "5,000,000" }, "expect": { - "status": "4", - "message": "str:Invalid token or amount" + "out": [], + "status": "0" } } ] diff --git a/multisig/tests/multisig_blackbox_test.rs b/multisig/tests/multisig_blackbox_test.rs index 26588718..36849c9a 100644 --- a/multisig/tests/multisig_blackbox_test.rs +++ b/multisig/tests/multisig_blackbox_test.rs @@ -704,7 +704,6 @@ fn ethereum_to_multiversx_relayer_call_data_several_tx_test() { .to(MULTISIG_ADDRESS) .typed(multisig_proxy::MultisigProxy) .perform_action_endpoint(1usize) - .returns(ExpectError(4, "Invalid token or amount")) .run(); state.world.write_scenario_trace( @@ -866,7 +865,6 @@ fn ethereum_to_multiversx_relayer_query2_test() { .to(MULTISIG_ADDRESS) .typed(multisig_proxy::MultisigProxy) .perform_action_endpoint(1usize) - .returns(ExpectError(4, "Invalid token or amount")) .run(); state From fdcecd437e1b469e6235f1d0d6348580e61d43fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Thu, 19 Dec 2024 11:46:26 +0200 Subject: [PATCH 45/45] Fixes after review --- bridged-tokens-wrapper/src/lib.rs | 1 - multi-transfer-esdt/src/lib.rs | 4 ---- 2 files changed, 5 deletions(-) diff --git a/bridged-tokens-wrapper/src/lib.rs b/bridged-tokens-wrapper/src/lib.rs index 4bca2d4b..7bdd06b0 100644 --- a/bridged-tokens-wrapper/src/lib.rs +++ b/bridged-tokens-wrapper/src/lib.rs @@ -134,7 +134,6 @@ pub trait BridgedTokensWrapper: self.token_decimals_num(&chain_specific_token_id).clear(); } - #[only_owner] #[payable("*")] #[endpoint(depositLiquidity)] fn deposit_liquidity(&self) { diff --git a/multi-transfer-esdt/src/lib.rs b/multi-transfer-esdt/src/lib.rs index 9d9ff858..b07689f3 100644 --- a/multi-transfer-esdt/src/lib.rs +++ b/multi-transfer-esdt/src/lib.rs @@ -59,10 +59,6 @@ pub trait MultiTransferEsdt: .blockchain() .get_esdt_local_roles(ð_tx.token_id.clone()); if token_roles.has_role(&EsdtLocalRole::Transfer) { - // let refund_tx = self.convert_to_refund_tx(eth_tx.clone()); - // refund_tx_list.push(refund_tx); - // self.token_with_transfer_role_event(eth_tx.token_id); - self.add_eth_tx_to_refund_tx_list(eth_tx.clone(), &mut refund_tx_list); self.token_with_transfer_role_event(eth_tx.token_id); continue;