@layerzerolabs/protocol-stellar-v2 0.2.19 → 0.2.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (249) hide show
  1. package/.turbo/turbo-build.log +795 -791
  2. package/.turbo/turbo-lint.log +325 -155
  3. package/.turbo/turbo-test.log +1398 -1277
  4. package/Cargo.lock +122 -111
  5. package/Cargo.toml +32 -16
  6. package/contracts/common-macros/Cargo.toml +7 -7
  7. package/contracts/common-macros/src/auth.rs +18 -37
  8. package/contracts/common-macros/src/contract_ttl.rs +18 -7
  9. package/contracts/common-macros/src/lib.rs +31 -14
  10. package/contracts/common-macros/src/lz_contract.rs +38 -7
  11. package/contracts/common-macros/src/storage.rs +251 -292
  12. package/contracts/common-macros/src/tests/contract_ttl.rs +1 -1
  13. package/contracts/common-macros/src/tests/snapshots/common_macros__tests__auth__snapshot_generated_multisig_code.snap +6 -12
  14. package/contracts/common-macros/src/tests/snapshots/common_macros__tests__auth__snapshot_generated_ownable_code.snap +12 -17
  15. package/contracts/common-macros/src/tests/snapshots/common_macros__tests__contract_ttl__snapshot_generated_contractimpl_code.snap +2 -1
  16. package/contracts/common-macros/src/tests/snapshots/common_macros__tests__ttl_configurable__snapshot_generated_ttl_configurable_code.snap +2 -7
  17. package/contracts/common-macros/src/tests/snapshots/common_macros__tests__upgradeable__snapshot_generated_upgradeable_code.snap +20 -14
  18. package/contracts/common-macros/src/tests/upgradeable.rs +26 -4
  19. package/contracts/common-macros/src/ttl_configurable.rs +2 -10
  20. package/contracts/common-macros/src/ttl_extendable.rs +2 -10
  21. package/contracts/common-macros/src/upgradeable.rs +61 -26
  22. package/contracts/common-macros/src/utils.rs +0 -9
  23. package/contracts/endpoint-v2/src/lib.rs +3 -2
  24. package/contracts/endpoint-v2/src/tests/endpoint_v2/clear.rs +2 -2
  25. package/contracts/endpoint-v2/src/tests/endpoint_v2/lz_receive_alert.rs +3 -3
  26. package/contracts/endpoint-v2/src/tests/endpoint_v2/send.rs +4 -4
  27. package/contracts/endpoint-v2/src/tests/endpoint_v2/set_delegate.rs +17 -5
  28. package/contracts/endpoint-v2/src/tests/endpoint_v2/set_zro.rs +4 -4
  29. package/contracts/endpoint-v2/src/tests/endpoint_v2/verify.rs +2 -2
  30. package/contracts/endpoint-v2/src/tests/message_lib_manager/register_library.rs +2 -2
  31. package/contracts/endpoint-v2/src/tests/message_lib_manager/set_default_receive_lib_timeout.rs +6 -6
  32. package/contracts/endpoint-v2/src/tests/message_lib_manager/set_default_receive_library.rs +67 -37
  33. package/contracts/endpoint-v2/src/tests/message_lib_manager/set_default_send_library.rs +5 -5
  34. package/contracts/endpoint-v2/src/tests/message_lib_manager/set_receive_library.rs +44 -54
  35. package/contracts/endpoint-v2/src/tests/message_lib_manager/set_receive_library_timeout.rs +7 -7
  36. package/contracts/endpoint-v2/src/tests/message_lib_manager/set_send_library.rs +8 -8
  37. package/contracts/endpoint-v2/src/tests/messaging_channel/burn.rs +3 -3
  38. package/contracts/endpoint-v2/src/tests/messaging_channel/nilify.rs +4 -4
  39. package/contracts/endpoint-v2/src/tests/messaging_channel/skip.rs +3 -3
  40. package/contracts/endpoint-v2/src/tests/messaging_composer/clear_compose.rs +2 -2
  41. package/contracts/endpoint-v2/src/tests/messaging_composer/lz_compose_alert.rs +3 -3
  42. package/contracts/endpoint-v2/src/tests/messaging_composer/send_compose.rs +2 -2
  43. package/contracts/layerzero-views/Cargo.toml +0 -1
  44. package/contracts/layerzero-views/src/layerzero_view.rs +1 -13
  45. package/contracts/macro-integration-tests/Cargo.toml +5 -15
  46. package/contracts/macro-integration-tests/tests/runtime/oapp/mod.rs +48 -0
  47. package/contracts/macro-integration-tests/tests/runtime/oapp/oapp_core.rs +170 -0
  48. package/contracts/macro-integration-tests/tests/runtime/oapp/options_type3.rs +154 -0
  49. package/contracts/macro-integration-tests/tests/runtime/oapp/receiver.rs +338 -0
  50. package/contracts/macro-integration-tests/tests/runtime/oapp/sender.rs +435 -0
  51. package/contracts/macro-integration-tests/tests/runtime.rs +1 -0
  52. package/contracts/macro-integration-tests/tests/ui/oapp/fail/custom_wrong_value.rs +8 -0
  53. package/contracts/macro-integration-tests/tests/ui/oapp/fail/custom_wrong_value.stderr +5 -0
  54. package/contracts/macro-integration-tests/tests/ui/oapp/fail/missing_lz_receive_internal.rs +8 -0
  55. package/contracts/macro-integration-tests/tests/ui/oapp/fail/missing_lz_receive_internal.stderr +71 -0
  56. package/contracts/macro-integration-tests/tests/ui/oapp/fail/non_struct_input.rs +10 -0
  57. package/contracts/macro-integration-tests/tests/ui/oapp/fail/non_struct_input.stderr +5 -0
  58. package/contracts/macro-integration-tests/tests/ui/oapp/fail/unknown_custom_option.rs +8 -0
  59. package/contracts/macro-integration-tests/tests/ui/oapp/fail/unknown_custom_option.stderr +5 -0
  60. package/contracts/macro-integration-tests/tests/ui/oapp/fail/wrong_key.rs +8 -0
  61. package/contracts/macro-integration-tests/tests/ui/oapp/fail/wrong_key.stderr +5 -0
  62. package/contracts/macro-integration-tests/tests/ui/oapp/pass/custom_all.rs +38 -0
  63. package/contracts/macro-integration-tests/tests/ui/oapp/pass/custom_single_trait.rs +96 -0
  64. package/contracts/macro-integration-tests/tests/ui/oapp/pass/minimal_contract.rs +64 -0
  65. package/contracts/macro-integration-tests/tests/ui/oapp/pass/struct_with_fields.rs +46 -0
  66. package/contracts/macro-integration-tests/tests/ui/ownable/fail/only_auth_missing_env.stderr +8 -0
  67. package/contracts/macro-integration-tests/tests/ui/ownable/pass/namespacing_and_imports.rs +1 -1
  68. package/contracts/macro-integration-tests/tests/ui/ownable/pass/only_auth_env_param_variants.rs +1 -1
  69. package/contracts/macro-integration-tests/tests/ui_oapp.rs +11 -0
  70. package/contracts/message-libs/message-lib-common/Cargo.toml +0 -1
  71. package/contracts/message-libs/message-lib-common/src/errors.rs +1 -1
  72. package/contracts/message-libs/treasury/Cargo.toml +0 -2
  73. package/contracts/message-libs/treasury/src/tests/treasury_tests.rs +2 -2
  74. package/contracts/message-libs/uln-302/src/events.rs +4 -0
  75. package/contracts/message-libs/uln-302/src/send_uln.rs +22 -6
  76. package/contracts/message-libs/uln-302/src/tests/receive_uln302/effective_receive_uln_config.rs +2 -2
  77. package/contracts/message-libs/uln-302/src/tests/receive_uln302/set_default_receive_uln_configs.rs +2 -2
  78. package/contracts/message-libs/uln-302/src/tests/receive_uln302/verify.rs +2 -2
  79. package/contracts/message-libs/uln-302/src/tests/send_uln302/effective_executor_config.rs +2 -2
  80. package/contracts/message-libs/uln-302/src/tests/send_uln302/effective_send_uln_config.rs +2 -2
  81. package/contracts/message-libs/uln-302/src/tests/send_uln302/send.rs +21 -67
  82. package/contracts/message-libs/uln-302/src/tests/send_uln302/set_default_executor_configs.rs +2 -2
  83. package/contracts/message-libs/uln-302/src/tests/send_uln302/set_default_send_uln_configs.rs +2 -2
  84. package/contracts/oapps/counter/Cargo.toml +5 -6
  85. package/contracts/oapps/counter/integration_tests/setup_uln.rs +1 -1
  86. package/contracts/oapps/counter/integration_tests/utils.rs +19 -12
  87. package/contracts/oapps/oapp/src/errors.rs +1 -1
  88. package/contracts/oapps/oapp/src/interfaces/mod.rs +3 -0
  89. package/contracts/oapps/oapp/src/interfaces/oapp_msg_inspector.rs +47 -0
  90. package/contracts/oapps/oapp/src/lib.rs +1 -0
  91. package/contracts/oapps/oapp/src/macro_tests/test_macros.rs +4 -4
  92. package/contracts/oapps/oapp/src/oapp_core.rs +5 -5
  93. package/contracts/oapps/oapp/src/oapp_options_type3.rs +12 -4
  94. package/contracts/oapps/oapp/src/oapp_receiver.rs +14 -9
  95. package/contracts/oapps/oapp/src/tests/mod.rs +4 -4
  96. package/contracts/oapps/oapp/src/tests/oapp_core.rs +223 -0
  97. package/contracts/oapps/oapp/src/tests/oapp_options_type3.rs +240 -0
  98. package/contracts/oapps/oapp/src/tests/oapp_receiver.rs +381 -0
  99. package/contracts/oapps/oapp/src/tests/oapp_sender.rs +569 -0
  100. package/contracts/oapps/oapp-macros/Cargo.toml +8 -4
  101. package/contracts/oapps/oapp-macros/src/generators.rs +9 -34
  102. package/contracts/oapps/oapp-macros/src/lib.rs +3 -0
  103. package/contracts/oapps/oapp-macros/src/tests/mod.rs +2 -0
  104. package/contracts/oapps/oapp-macros/src/tests/oapp.rs +88 -0
  105. package/contracts/oapps/oapp-macros/src/tests/parse_custom_impls.rs +86 -0
  106. package/contracts/oapps/oapp-macros/src/tests/snapshots/oapp_macros__tests__oapp__snapshot_generate_oapp.snap +103 -0
  107. package/contracts/oapps/oft/integration-tests/utils.rs +28 -8
  108. package/contracts/oapps/oft/src/extensions/oft_fee.rs +153 -75
  109. package/contracts/oapps/oft/src/extensions/pausable.rs +61 -12
  110. package/contracts/oapps/oft/src/extensions/rate_limiter.rs +198 -134
  111. package/contracts/oapps/oft/src/oft.rs +45 -50
  112. package/contracts/oapps/oft/src/oft_types/lock_unlock.rs +1 -1
  113. package/contracts/oapps/oft/src/oft_types/mint_burn.rs +4 -26
  114. package/contracts/oapps/oft-core/Cargo.toml +1 -4
  115. package/contracts/oapps/oft-core/integration-tests/setup.rs +3 -3
  116. package/contracts/oapps/oft-core/integration-tests/utils.rs +21 -3
  117. package/contracts/oapps/oft-core/src/errors.rs +3 -2
  118. package/contracts/oapps/oft-core/src/events.rs +6 -0
  119. package/contracts/oapps/oft-core/src/lib.rs +1 -1
  120. package/contracts/oapps/oft-core/src/oft_core.rs +341 -246
  121. package/contracts/oapps/oft-core/src/storage.rs +7 -3
  122. package/contracts/oapps/oft-core/src/tests/mod.rs +1 -0
  123. package/contracts/oapps/oft-core/src/tests/test_decimals.rs +37 -2
  124. package/contracts/oapps/oft-core/src/tests/test_lz_receive.rs +2 -2
  125. package/contracts/oapps/oft-core/src/tests/test_msg_inspector.rs +323 -0
  126. package/contracts/oapps/oft-core/src/tests/test_send.rs +2 -2
  127. package/contracts/oapps/oft-core/src/tests/test_utils.rs +61 -16
  128. package/contracts/upgrader/src/lib.rs +30 -57
  129. package/contracts/upgrader/src/tests/test_data/test_upgradeable_contract1.wasm +0 -0
  130. package/contracts/upgrader/src/tests/test_data/test_upgradeable_contract2.wasm +0 -0
  131. package/contracts/upgrader/src/tests/test_upgrader.rs +44 -35
  132. package/contracts/utils/Cargo.toml +0 -1
  133. package/contracts/utils/src/buffer_reader.rs +1 -0
  134. package/contracts/utils/src/errors.rs +4 -2
  135. package/contracts/utils/src/multisig.rs +17 -8
  136. package/contracts/utils/src/ownable.rs +6 -6
  137. package/contracts/utils/src/testing_utils.rs +124 -54
  138. package/contracts/utils/src/tests/multisig.rs +12 -12
  139. package/contracts/utils/src/tests/ownable.rs +6 -6
  140. package/contracts/utils/src/tests/testing_utils.rs +50 -167
  141. package/contracts/utils/src/tests/ttl_configurable.rs +5 -5
  142. package/contracts/utils/src/tests/upgradeable.rs +372 -175
  143. package/contracts/utils/src/ttl_configurable.rs +13 -7
  144. package/contracts/utils/src/upgradeable.rs +48 -23
  145. package/contracts/workers/dvn/Cargo.toml +6 -6
  146. package/contracts/workers/dvn/src/auth.rs +12 -42
  147. package/contracts/workers/dvn/src/dvn.rs +15 -40
  148. package/contracts/workers/dvn/src/errors.rs +0 -1
  149. package/contracts/workers/dvn/src/interfaces/dvn.rs +35 -0
  150. package/contracts/workers/dvn/src/lib.rs +4 -3
  151. package/contracts/workers/dvn/src/tests/auth.rs +1 -1
  152. package/contracts/workers/dvn/src/tests/dvn.rs +19 -15
  153. package/contracts/workers/dvn/src/tests/multisig/set_threshold.rs +2 -4
  154. package/contracts/workers/dvn/src/tests/multisig/verify_signatures.rs +1 -3
  155. package/contracts/workers/dvn/src/tests/setup.rs +5 -9
  156. package/contracts/workers/dvn-fee-lib/Cargo.toml +2 -2
  157. package/contracts/workers/dvn-fee-lib/src/dvn_fee_lib.rs +38 -22
  158. package/contracts/workers/dvn-fee-lib/src/lib.rs +12 -2
  159. package/contracts/workers/dvn-fee-lib/src/tests/dvn_fee_lib.rs +17 -16
  160. package/contracts/workers/executor/Cargo.toml +4 -0
  161. package/contracts/workers/executor/src/executor.rs +15 -36
  162. package/contracts/workers/executor/src/lib.rs +2 -2
  163. package/contracts/workers/executor/src/tests/auth.rs +394 -0
  164. package/contracts/workers/executor/src/tests/executor.rs +410 -0
  165. package/contracts/workers/executor/src/tests/mod.rs +3 -0
  166. package/contracts/workers/executor/src/tests/setup.rs +250 -0
  167. package/contracts/workers/executor-fee-lib/Cargo.toml +7 -1
  168. package/contracts/workers/executor-fee-lib/src/executor_fee_lib.rs +62 -15
  169. package/contracts/workers/executor-fee-lib/src/executor_option.rs +28 -1
  170. package/contracts/workers/executor-fee-lib/src/lib.rs +11 -2
  171. package/contracts/workers/executor-fee-lib/src/tests/executor_fee_lib.rs +701 -0
  172. package/contracts/workers/executor-fee-lib/src/tests/executor_option.rs +370 -0
  173. package/contracts/workers/executor-fee-lib/src/tests/mod.rs +4 -0
  174. package/contracts/workers/executor-fee-lib/src/tests/setup.rs +60 -0
  175. package/contracts/workers/executor-helper/Cargo.toml +0 -1
  176. package/contracts/workers/executor-helper/src/lib.rs +3 -0
  177. package/contracts/workers/executor-helper/src/tests/executor_helper.rs +184 -0
  178. package/contracts/workers/executor-helper/src/tests/mod.rs +2 -0
  179. package/contracts/workers/executor-helper/src/tests/setup.rs +366 -0
  180. package/contracts/workers/fee-lib-interfaces/Cargo.toml +14 -0
  181. package/contracts/workers/{worker/src/interfaces/mod.rs → fee-lib-interfaces/src/lib.rs} +4 -3
  182. package/contracts/workers/price-feed/Cargo.toml +7 -1
  183. package/contracts/workers/price-feed/src/events.rs +1 -1
  184. package/contracts/workers/price-feed/src/lib.rs +12 -4
  185. package/contracts/workers/price-feed/src/price_feed.rs +5 -21
  186. package/contracts/workers/price-feed/src/storage.rs +1 -1
  187. package/contracts/workers/price-feed/src/tests/mod.rs +2 -0
  188. package/contracts/workers/price-feed/src/tests/price_feed.rs +869 -0
  189. package/contracts/workers/price-feed/src/tests/setup.rs +70 -0
  190. package/contracts/workers/price-feed/src/types.rs +1 -1
  191. package/contracts/workers/worker/src/errors.rs +1 -4
  192. package/contracts/workers/worker/src/lib.rs +0 -2
  193. package/contracts/workers/worker/src/storage.rs +32 -29
  194. package/contracts/workers/worker/src/tests/setup.rs +2 -8
  195. package/contracts/workers/worker/src/tests/worker.rs +96 -74
  196. package/contracts/workers/worker/src/worker.rs +75 -75
  197. package/docs/error-spec.md +55 -0
  198. package/docs/layerzero-v2-on-stellar.md +447 -0
  199. package/docs/oapp-guide.md +212 -0
  200. package/docs/oft-guide.md +314 -0
  201. package/package.json +3 -3
  202. package/sdk/.turbo/turbo-test.log +268 -263
  203. package/sdk/dist/generated/bml.d.ts +12 -4
  204. package/sdk/dist/generated/bml.js +9 -7
  205. package/sdk/dist/generated/counter.d.ts +306 -298
  206. package/sdk/dist/generated/counter.js +48 -46
  207. package/sdk/dist/generated/dvn.d.ts +450 -411
  208. package/sdk/dist/generated/dvn.js +66 -64
  209. package/sdk/dist/generated/dvn_fee_lib.d.ts +294 -338
  210. package/sdk/dist/generated/dvn_fee_lib.js +33 -64
  211. package/sdk/dist/generated/endpoint.d.ts +108 -100
  212. package/sdk/dist/generated/endpoint.js +21 -19
  213. package/sdk/dist/generated/executor.d.ts +414 -370
  214. package/sdk/dist/generated/executor.js +58 -55
  215. package/sdk/dist/generated/executor_fee_lib.d.ts +333 -377
  216. package/sdk/dist/generated/executor_fee_lib.js +34 -65
  217. package/sdk/dist/generated/executor_helper.d.ts +26 -190
  218. package/sdk/dist/generated/executor_helper.js +23 -28
  219. package/sdk/dist/generated/layerzero_view.d.ts +1271 -0
  220. package/sdk/dist/generated/layerzero_view.js +294 -0
  221. package/sdk/dist/generated/oft.d.ts +408 -385
  222. package/sdk/dist/generated/oft.js +89 -92
  223. package/sdk/dist/generated/price_feed.d.ts +385 -429
  224. package/sdk/dist/generated/price_feed.js +50 -81
  225. package/sdk/dist/generated/sml.d.ts +108 -100
  226. package/sdk/dist/generated/sml.js +21 -19
  227. package/sdk/dist/generated/treasury.d.ts +108 -100
  228. package/sdk/dist/generated/treasury.js +21 -19
  229. package/sdk/dist/generated/uln302.d.ts +108 -100
  230. package/sdk/dist/generated/uln302.js +23 -21
  231. package/sdk/dist/generated/upgrader.d.ts +189 -18
  232. package/sdk/dist/generated/upgrader.js +84 -4
  233. package/sdk/dist/index.d.ts +1 -0
  234. package/sdk/dist/index.js +2 -0
  235. package/sdk/package.json +1 -1
  236. package/sdk/src/index.ts +3 -0
  237. package/sdk/test/oft-sml.test.ts +4 -4
  238. package/sdk/test/suites/localnet.ts +84 -20
  239. package/sdk/test/upgrader.test.ts +2 -3
  240. package/tools/ts-bindings-gen/src/main.rs +2 -1
  241. package/contracts/ERROR_SPEC.md +0 -44
  242. package/contracts/endpoint-v2/ARCHITECTURE.md +0 -233
  243. package/contracts/oapps/oapp/src/tests/test_oapp_core.rs +0 -175
  244. package/contracts/oapps/oapp/src/tests/test_oapp_options_type3.rs +0 -212
  245. package/contracts/oapps/oapp/src/tests/test_oapp_receiver.rs +0 -153
  246. package/contracts/oapps/oapp/src/tests/test_oapp_sender.rs +0 -294
  247. /package/contracts/workers/{worker/src/interfaces → fee-lib-interfaces/src}/dvn_fee_lib.rs +0 -0
  248. /package/contracts/workers/{worker/src/interfaces → fee-lib-interfaces/src}/executor_fee_lib.rs +0 -0
  249. /package/contracts/workers/{worker/src/interfaces → fee-lib-interfaces/src}/price_feed.rs +0 -0
@@ -2,45 +2,25 @@
2
2
 
3
3
  //! # Upgrader Contract
4
4
  //!
5
- //! A stateless utility contract for performing upgrade and migrate operations on upgradeable contracts.
6
- //!
7
- //! This contract provides a convenient way to upgrade other contracts, especially useful for
8
- //! performing upgrade+migrate atomically in a single transaction.
5
+ //! A stateless utility contract for performing atomic upgrade and migrate operations
6
+ //! on contracts implementing the [`Upgradeable`](utils::upgradeable::Upgradeable) trait.
9
7
  //!
10
8
  //! ## Security Model
11
9
  //!
12
- //! The Upgrader is a permissionless utility - anyone can call it, but security is enforced
13
- //! by the target contract's authorization checks. When you call `upgrader.upgrade()`, the target
14
- //! contract's `#[only_auth]` guard ensures only the target's authorizer can successfully upgrade it.
10
+ //! The Upgrader is permissionless - anyone can call it, but security is enforced by the
11
+ //! target contract's authorization checks. The target contract's `#[only_auth]` guard
12
+ //! ensures only its authorizer can successfully upgrade it.
15
13
  //!
16
14
  //! ## Usage
17
15
  //!
18
- //! Deploy this contract once, then anyone can use it to upgrade contracts they own.
19
- //!
20
16
  //! ```ignore
21
- //! // Deploy upgrader (no initialization needed)
22
- //! let upgrader_id = env.register(Upgrader, ());
23
17
  //! let upgrader = UpgraderClient::new(&env, &upgrader_id);
24
- //!
25
- //! // Upgrade a contract (you must be the target contract's owner)
26
- //! upgrader.upgrade(&target_contract, &new_wasm_hash);
27
- //!
28
- //! // Or upgrade and migrate in one transaction
29
- //! let migration_data = vec![&env, val1, val2];
30
- //! upgrader.upgrade_and_migrate(&target_contract, &new_wasm_hash, migration_data);
18
+ //! let migration_data = my_data.to_xdr(&env);
19
+ //! upgrader.upgrade_and_migrate(&target_contract, &new_wasm_hash, &migration_data);
31
20
  //! ```
32
21
 
33
- use soroban_sdk::{contract, contractclient, contractimpl, symbol_short, Address, BytesN, Env, Symbol, Vec};
34
-
35
- /// Symbol for the migrate function call
36
- pub const MIGRATE: Symbol = symbol_short!("migrate");
37
-
38
- /// Trait representing an upgradeable contract's interface
39
- #[contractclient(name = "UpgradeableContractClient")]
40
- pub trait UpgradeableContract {
41
- /// Upgrades the contract to new WASM bytecode
42
- fn upgrade(env: &Env, new_wasm_hash: BytesN<32>);
43
- }
22
+ use soroban_sdk::{contract, contractimpl, xdr::ToXdr, Address, Bytes, BytesN, Env};
23
+ use utils::upgradeable::UpgradeableClient;
44
24
 
45
25
  /// Upgrader contract for managing upgrades of other contracts.
46
26
  ///
@@ -51,46 +31,39 @@ pub struct Upgrader;
51
31
 
52
32
  #[contractimpl]
53
33
  impl Upgrader {
54
- /// Upgrade a target contract and run its migration in a single transaction
34
+ /// Upgrades a target contract without custom migration data.
35
+ ///
36
+ /// This is a convenience wrapper that calls `upgrade_and_migrate` with empty migration data.
37
+ ///
38
+ /// # Arguments
39
+ /// * `contract_address` - The address of the contract to upgrade
40
+ /// * `wasm_hash` - The hash of the new WASM bytecode
41
+ pub fn upgrade(env: &Env, contract_address: &Address, wasm_hash: &BytesN<32>) {
42
+ Self::upgrade_and_migrate(env, contract_address, wasm_hash, &().to_xdr(env));
43
+ }
44
+
45
+ /// Upgrades a target contract and runs its migration in a single transaction.
55
46
  ///
56
47
  /// The caller must be authorized as the authorizer of the target contract.
57
48
  /// This is enforced by the target contract's `#[only_auth]` check.
58
49
  ///
59
- /// This is useful for atomic upgrades where you want to ensure the migration
60
- /// happens immediately after the upgrade, or the entire operation fails.
61
- ///
62
50
  /// # Arguments
63
51
  /// * `contract_address` - The address of the contract to upgrade
64
52
  /// * `wasm_hash` - The hash of the new WASM bytecode
65
- /// * `migration_data` - A vector of values to pass to the migrate function.
66
- /// The types of these values depend on the target contract's MigrationData type.
53
+ /// * `migration_data` - XDR-encoded bytes to pass to the migrate function.
54
+ /// Use `value.to_xdr(&env)` to encode the target contract's MigrationData type.
67
55
  ///
68
56
  /// # Example
69
57
  /// ```ignore
70
- /// // For a contract with MigrationData = (u32, bool)
71
- /// let migration_data = vec![
72
- /// &env,
73
- /// 42u32.into_val(&env),
74
- /// true.into_val(&env),
75
- /// ];
76
- /// upgrader.upgrade_and_migrate(&contract_addr, &wasm_hash, migration_data);
58
+ /// let migration_data = my_data.to_xdr(&env);
59
+ /// upgrader.upgrade_and_migrate(&contract_addr, &wasm_hash, &migration_data);
77
60
  /// ```
78
- pub fn upgrade_and_migrate(
79
- env: &Env,
80
- contract_address: &Address,
81
- wasm_hash: BytesN<32>,
82
- migration_data: Vec<soroban_sdk::Val>,
83
- ) {
84
- let contract_client = UpgradeableContractClient::new(env, contract_address);
85
-
86
- // First upgrade the contract
87
- contract_client.upgrade(&wasm_hash);
88
-
89
- // Then call migrate with the provided data
90
- // We use invoke_contract because the migration data type is unknown to this contract
91
- env.invoke_contract::<()>(contract_address, &MIGRATE, migration_data);
61
+ pub fn upgrade_and_migrate(env: &Env, contract_address: &Address, wasm_hash: &BytesN<32>, migration_data: &Bytes) {
62
+ let client = UpgradeableClient::new(env, contract_address);
63
+ client.upgrade(wasm_hash);
64
+ client.migrate(migration_data);
92
65
  }
93
66
  }
94
67
 
95
68
  #[cfg(test)]
96
- mod tests;
69
+ mod tests;
@@ -1,6 +1,6 @@
1
1
  extern crate std;
2
2
 
3
- use soroban_sdk::{contractclient, testutils::Address as _, Address, BytesN, Env, TryIntoVal};
3
+ use soroban_sdk::{contractclient, testutils::Address as _, xdr::ToXdr, Address, BytesN, Env};
4
4
 
5
5
  use crate::{Upgrader, UpgraderClient};
6
6
 
@@ -17,16 +17,20 @@ trait TestUpgradeableContract2 {
17
17
  }
18
18
 
19
19
  mod contract_v1 {
20
- // #[contract]
21
- // #[upgradeable]
22
- // #[ownable]
23
- // pub struct TestUpgradeableContract;
20
+ //#![no_std]
24
21
 
25
- // impl UpgradeableInternal for TestUpgradeableContract {
26
- // type MigrationData = ();
22
+ // use soroban_sdk::{contractimpl, Env, Symbol, Address, contractclient};
23
+ // use utils::{ upgradeable::UpgradeableInternal};
24
+ // use common_macros::lz_contract;
27
25
 
28
- // fn _migrate(env: &Env, _migration_data: &Self::MigrationData) {
29
- // env.storage().instance().set(&Symbol::new(env, "counter"), &2);
26
+ // #[lz_contract(upgradeable)]
27
+ // pub struct DummyContract;
28
+
29
+ // impl UpgradeableInternal for DummyContract {
30
+ // type MigrationData = u32;
31
+
32
+ // fn __migrate(env: &Env, migration_data: &Self::MigrationData) {
33
+ // env.storage().instance().set(&Symbol::new(env, "counter2"), migration_data);
30
34
  // }
31
35
  // }
32
36
 
@@ -35,34 +39,37 @@ mod contract_v1 {
35
39
  // fn counter(env: &Env) -> u32;
36
40
  // }
37
41
 
38
- // #[contract_impl]
39
- // impl TestUpgradeable for TestUpgradeableContract {
42
+ // #[contractimpl]
43
+ // impl TestUpgradeable for DummyContract {
40
44
  // fn counter(env: &Env) -> u32 {
41
45
  // env.storage().instance().get(&Symbol::new(env, "counter")).unwrap_or(0)
42
46
  // }
43
47
  // }
44
48
 
45
- // #[contract_impl]
46
- // impl TestUpgradeableContract {
49
+ // #[contractimpl]
50
+ // impl DummyContract {
47
51
  // pub fn __constructor(env: &Env, owner: &Address) {
48
52
  // Self::init_owner(env, owner);
49
53
  // env.storage().instance().set(&Symbol::new(env, "counter"), &1_u32);
50
54
  // }
51
55
  // }
52
- use super::MigrationData;
53
56
  soroban_sdk::contractimport!(file = "./src/tests/test_data/test_upgradeable_contract1.wasm");
54
57
  }
55
58
  mod contract_v2 {
56
- // #[contract]
57
- // #[upgradeable]
58
- // #[ownable]
59
- // pub struct TestUpgradeableContract;
59
+ //#![no_std]
60
+
61
+ // use soroban_sdk::{contractimpl, Env, Symbol, Address, contractclient};
62
+ // use utils::{ upgradeable::UpgradeableInternal};
63
+ // use common_macros::lz_contract;
64
+
65
+ // #[lz_contract(upgradeable)]
66
+ // pub struct DummyContract;
60
67
 
61
- // impl UpgradeableInternal for TestUpgradeableContract {
62
- // type MigrationData = ();
68
+ // impl UpgradeableInternal for DummyContract {
69
+ // type MigrationData = u32;
63
70
 
64
- // fn _migrate(env: &Env, _migration_data: &Self::MigrationData) {
65
- // env.storage().instance().set(&Symbol::new(env, "counter2"), &2_u32);
71
+ // fn __migrate(env: &Env, migration_data: &Self::MigrationData) {
72
+ // env.storage().instance().set(&Symbol::new(env, "counter2"), migration_data);
66
73
  // }
67
74
  // }
68
75
 
@@ -72,8 +79,8 @@ mod contract_v2 {
72
79
  // fn counter2(env: &Env) -> u32;
73
80
  // }
74
81
 
75
- // #[contract_impl]
76
- // impl TestUpgradeable for TestUpgradeableContract {
82
+ // #[contractimpl]
83
+ // impl TestUpgradeable for DummyContract {
77
84
  // fn counter(env: &Env) -> u32 {
78
85
  // env.storage().instance().get(&Symbol::new(env, "counter")).unwrap_or(0)
79
86
  // }
@@ -82,7 +89,14 @@ mod contract_v2 {
82
89
  // env.storage().instance().get(&Symbol::new(env, "counter2")).unwrap_or(0)
83
90
  // }
84
91
  // }
85
- use super::MigrationData;
92
+
93
+ // #[contractimpl]
94
+ // impl DummyContract {
95
+ // pub fn __constructor(env: &Env, owner: &Address) {
96
+ // Self::init_owner(env, owner);
97
+ // env.storage().instance().set(&Symbol::new(env, "counter"), &1_u32);
98
+ // }
99
+ // }
86
100
  soroban_sdk::contractimport!(file = "./src/tests/test_data/test_upgradeable_contract2.wasm");
87
101
  }
88
102
 
@@ -90,9 +104,6 @@ fn install_new_wasm(e: &Env) -> BytesN<32> {
90
104
  e.deployer().upload_contract_wasm(contract_v2::WASM)
91
105
  }
92
106
 
93
- #[allow(dead_code)]
94
- type MigrationData = ();
95
-
96
107
  #[test]
97
108
  fn test_upgrade_with_upgrader() {
98
109
  let e = Env::default();
@@ -107,14 +118,12 @@ fn test_upgrade_with_upgrader() {
107
118
  let upgrader_client = UpgraderClient::new(&e, &upgrader);
108
119
 
109
120
  let new_wasm_hash = install_new_wasm(&e);
110
-
111
- upgrader_client.upgrade_and_migrate(
112
- &contract_id,
113
- &new_wasm_hash,
114
- &soroban_sdk::vec![&e, ().try_into_val(&e).unwrap()],
115
- );
121
+ let counter_value = 2_u32;
122
+ // Encode migration data as XDR bytes
123
+ let migration_data = counter_value.to_xdr(&e);
124
+ upgrader_client.upgrade_and_migrate(&contract_id, &new_wasm_hash, &migration_data);
116
125
 
117
126
  let client_v2 = TestUpgradeableContractClient2::new(&e, &contract_id);
118
127
 
119
- assert_eq!(client_v2.counter2(), 2);
128
+ assert_eq!(client_v2.counter2(), counter_value);
120
129
  }
@@ -18,4 +18,3 @@ common-macros = { workspace = true }
18
18
 
19
19
  [dev-dependencies]
20
20
  soroban-sdk = { workspace = true, features = ["testutils"] }
21
- stellar-strkey = "0.0.14"
@@ -92,6 +92,7 @@ impl<'a> BufferReader<'a> {
92
92
  value
93
93
  }
94
94
 
95
+ /// Reads all bytes from current position to the end of the buffer.
95
96
  pub fn read_bytes_until_end(&mut self) -> Bytes {
96
97
  self.read_bytes(self.remaining_len())
97
98
  }
@@ -1,7 +1,7 @@
1
1
  use common_macros::contract_error;
2
2
 
3
3
  // Utils library error codes: 1000-1099
4
- // See ERROR_SPEC.md for allocation rules
4
+ // See docs/error-spec.md for allocation rules
5
5
 
6
6
  /// BufferReaderError: 1000-1009
7
7
  #[contract_error]
@@ -44,7 +44,9 @@ pub enum BytesExtError {
44
44
  /// UpgradeableError: 1050-1059
45
45
  #[contract_error]
46
46
  pub enum UpgradeableError {
47
- MigrationNotAllowed = 1050,
47
+ InvalidMigrationData = 1050,
48
+ MigrationNotAllowed,
49
+ UpgradesFrozen,
48
50
  }
49
51
 
50
52
  /// MultisigError: 1060-1069
@@ -58,7 +58,7 @@ pub trait Multisig: Auth {
58
58
  // ===========================================================================
59
59
 
60
60
  /// Adds or removes a signer from the multisig. Requires owner authorization.
61
- fn set_signer(env: &Env, signer: &BytesN<20>, active: bool) {
61
+ fn set_signer(env: &soroban_sdk::Env, signer: &soroban_sdk::BytesN<20>, active: bool) {
62
62
  auth::require_auth::<Self>(env);
63
63
  match active {
64
64
  true => add_signer(env, signer),
@@ -67,7 +67,7 @@ pub trait Multisig: Auth {
67
67
  }
68
68
 
69
69
  /// Sets the signature threshold (quorum). Requires owner authorization.
70
- fn set_threshold(env: &Env, threshold: u32) {
70
+ fn set_threshold(env: &soroban_sdk::Env, threshold: u32) {
71
71
  auth::require_auth::<Self>(env);
72
72
  set_threshold(env, threshold);
73
73
  }
@@ -77,22 +77,22 @@ pub trait Multisig: Auth {
77
77
  // ===========================================================================
78
78
 
79
79
  /// Returns all registered signers.
80
- fn get_signers(env: &Env) -> Vec<BytesN<20>> {
80
+ fn get_signers(env: &soroban_sdk::Env) -> soroban_sdk::Vec<soroban_sdk::BytesN<20>> {
81
81
  MultisigStorage::signers(env)
82
82
  }
83
83
 
84
84
  /// Returns the total number of registered signers.
85
- fn total_signers(env: &Env) -> u32 {
85
+ fn total_signers(env: &soroban_sdk::Env) -> u32 {
86
86
  MultisigStorage::signers(env).len()
87
87
  }
88
88
 
89
89
  /// Checks if an address is a registered signer.
90
- fn is_signer(env: &Env, signer: &BytesN<20>) -> bool {
90
+ fn is_signer(env: &soroban_sdk::Env, signer: &soroban_sdk::BytesN<20>) -> bool {
91
91
  MultisigStorage::signers(env).iter().any(|s| &s == signer)
92
92
  }
93
93
 
94
94
  /// Returns the current signature threshold (quorum).
95
- fn threshold(env: &Env) -> u32 {
95
+ fn threshold(env: &soroban_sdk::Env) -> u32 {
96
96
  MultisigStorage::threshold(env)
97
97
  }
98
98
 
@@ -101,12 +101,21 @@ pub trait Multisig: Auth {
101
101
  // ===========================================================================
102
102
 
103
103
  /// Verifies signatures against the configured threshold.
104
- fn verify_signatures(env: &Env, digest: &BytesN<32>, signatures: &Vec<BytesN<65>>) {
104
+ fn verify_signatures(
105
+ env: &soroban_sdk::Env,
106
+ digest: &soroban_sdk::BytesN<32>,
107
+ signatures: &soroban_sdk::Vec<soroban_sdk::BytesN<65>>,
108
+ ) {
105
109
  Self::verify_n_signatures(env, digest, signatures, MultisigStorage::threshold(env));
106
110
  }
107
111
 
108
112
  /// Verifies signatures against a custom threshold.
109
- fn verify_n_signatures(env: &Env, digest: &BytesN<32>, signatures: &Vec<BytesN<65>>, threshold: u32) {
113
+ fn verify_n_signatures(
114
+ env: &soroban_sdk::Env,
115
+ digest: &soroban_sdk::BytesN<32>,
116
+ signatures: &soroban_sdk::Vec<soroban_sdk::BytesN<65>>,
117
+ threshold: u32,
118
+ ) {
110
119
  assert_with_error!(env, threshold > 0, MultisigError::ZeroThreshold);
111
120
  assert_with_error!(env, signatures.len() >= threshold, MultisigError::SignatureError);
112
121
 
@@ -64,12 +64,12 @@ pub trait Ownable: Sized + Auth {
64
64
  // ===========================================================================
65
65
 
66
66
  /// Returns the current owner address, or None if no owner is set.
67
- fn owner(env: &Env) -> Option<Address> {
67
+ fn owner(env: &soroban_sdk::Env) -> Option<soroban_sdk::Address> {
68
68
  OwnableStorage::owner(env)
69
69
  }
70
70
 
71
71
  /// Returns the pending owner address for 2-step transfer, or None if no transfer is pending.
72
- fn pending_owner(env: &Env) -> Option<Address> {
72
+ fn pending_owner(env: &soroban_sdk::Env) -> Option<soroban_sdk::Address> {
73
73
  OwnableStorage::pending_owner(env)
74
74
  }
75
75
 
@@ -85,7 +85,7 @@ pub trait Ownable: Sized + Auth {
85
85
  /// # Panics
86
86
  /// - `OwnerNotSet` if no owner is currently set
87
87
  /// - `TransferInProgress` if a 2-step transfer is in progress
88
- fn transfer_ownership(env: &Env, new_owner: &Address) {
88
+ fn transfer_ownership(env: &soroban_sdk::Env, new_owner: &soroban_sdk::Address) {
89
89
  let old_owner = enforce_owner_auth::<Self>(env);
90
90
  assert_no_pending_transfer::<Self>(env);
91
91
 
@@ -112,7 +112,7 @@ pub trait Ownable: Sized + Auth {
112
112
  /// - `NoPendingTransfer` when cancelling and no pending transfer exists
113
113
  /// - `InvalidTtl` if ttl exceeds max TTL
114
114
  /// - `InvalidPendingOwner` when cancelling with wrong new_owner address
115
- fn propose_ownership_transfer(env: &Env, new_owner: &Address, ttl: u32) {
115
+ fn propose_ownership_transfer(env: &soroban_sdk::Env, new_owner: &soroban_sdk::Address, ttl: u32) {
116
116
  let old_owner = enforce_owner_auth::<Self>(env);
117
117
 
118
118
  // Cancel case: ttl == 0
@@ -142,7 +142,7 @@ pub trait Ownable: Sized + Auth {
142
142
  ///
143
143
  /// # Panics
144
144
  /// - `NoPendingTransfer` if there is no pending transfer (or it expired)
145
- fn accept_ownership(env: &Env) {
145
+ fn accept_ownership(env: &soroban_sdk::Env) {
146
146
  let new_owner = Self::pending_owner(env).unwrap_or_panic(env, OwnableError::NoPendingTransfer);
147
147
 
148
148
  // Require authorization from the pending owner
@@ -169,7 +169,7 @@ pub trait Ownable: Sized + Auth {
169
169
  /// # Panics
170
170
  /// - `OwnerNotSet` if no owner is currently set
171
171
  /// - `TransferInProgress` if a 2-step transfer is in progress (cancel it first)
172
- fn renounce_ownership(env: &Env) {
172
+ fn renounce_ownership(env: &soroban_sdk::Env) {
173
173
  let old_owner = enforce_owner_auth::<Self>(env);
174
174
  assert_no_pending_transfer::<Self>(env);
175
175
 
@@ -1,26 +1,76 @@
1
- use soroban_sdk::{testutils::Events, xdr::ToXdr, Address, Env, Event, Val, Vec};
1
+ use soroban_sdk::{testutils::Events, xdr, Address, Env, Event, TryFromVal, Val, Vec};
2
2
 
3
- fn topics_eq(env: &Env, a: &Vec<Val>, b: &Vec<Val>) -> bool {
4
- if a.len() != b.len() {
5
- return false;
6
- }
7
- for i in 0..a.len() {
8
- let av = a.get(i).unwrap();
9
- let bv = b.get(i).unwrap();
10
- // Compare XDR serialization for deep value equality
11
- if av.to_xdr(env) != bv.to_xdr(env) {
12
- return false;
13
- }
3
+ /// Decodes a raw emitted contract event (XDR) into `(topics, data)`.
4
+ ///
5
+ /// Soroban testutils exposes events as [`ContractEvents`](soroban_sdk::testutils::ContractEvents),
6
+ /// which internally stores XDR [`ContractEvent`](soroban_sdk::xdr::ContractEvent) values.
7
+ /// This helper converts the XDR topics/data into `soroban_sdk::Val` so tests can inspect
8
+ /// topics and data using normal Soroban conversions (`TryFromVal` / `IntoVal`).
9
+ ///
10
+ /// Returns `None` if any topic or the data payload can't be converted into `Val` for the
11
+ /// provided `Env`.
12
+ ///
13
+ /// # Example
14
+ /// ```ignore
15
+ /// let events = env.events().all().filter_by_contract(&contract);
16
+ /// for ev in events.events().iter() {
17
+ /// let (topics, data) = utils::testing_utils::decode_event_topics_data(&env, ev).unwrap();
18
+ /// // ... inspect topics/data ...
19
+ /// }
20
+ /// ```
21
+ pub fn decode_event_topics_data(env: &Env, event: &xdr::ContractEvent) -> Option<(Vec<Val>, Val)> {
22
+ // In the current Soroban SDK version this is always `V0`.
23
+ // Using a single-variant match avoids "irrefutable let-else" warnings.
24
+ let v0 = match &event.body {
25
+ xdr::ContractEventBody::V0(v0) => v0,
26
+ };
27
+
28
+ let mut topics = Vec::<Val>::new(env);
29
+ for t in v0.topics.iter() {
30
+ topics.push_back(Val::try_from_val(env, t).ok()?);
14
31
  }
15
- true
32
+ let data = Val::try_from_val(env, &v0.data).ok()?;
33
+ Some((topics, data))
16
34
  }
17
35
 
18
- fn data_eq(env: &Env, a: &Val, b: &Val) -> bool {
19
- // Compare XDR serialization for deep value equality
20
- a.to_xdr(env) == b.to_xdr(env)
36
+ /// Asserts that the environment emitted exactly one event: `expected`.
37
+ ///
38
+ /// This is a **strict equality** assertion over `env.events().all()`:
39
+ /// the full event list must be exactly `[expected]` (no extra events).
40
+ ///
41
+ /// Uses the event struct's `.topics()` and `.data()` methods (generated by `#[contractevent]`)
42
+ /// to compare against emitted events. No more hardcoding topic names or field keys!
43
+ ///
44
+ /// # Example
45
+ /// ```ignore
46
+ /// use crate::events::OwnershipTransferred;
47
+ ///
48
+ /// let expected = OwnershipTransferred {
49
+ /// previous_owner: owner.clone(),
50
+ /// new_owner: new_owner.clone(),
51
+ /// };
52
+ /// assert_eq_event(&env, &contract, expected);
53
+ /// ```
54
+ pub fn assert_eq_event<E: Event>(env: &Env, contract: &Address, expected: E) {
55
+ // Compare against the emitted `xdr::ContractEvent` directly. This avoids the
56
+ // `xdr -> Val` roundtrip and ensures we match exactly what was emitted.
57
+ //
58
+ // IMPORTANT: This asserts *equality* (not "contains").
59
+ extern crate std;
60
+
61
+ assert_eq!(
62
+ env.events().all(),
63
+ std::vec![expected.to_xdr(env, contract)],
64
+ "Expected exactly one event. Expected topics: {:?}, data: {:?}",
65
+ expected.topics(env),
66
+ expected.data(env),
67
+ );
21
68
  }
22
69
 
23
- /// Asserts that an event matching the expected event was emitted from the contract.
70
+ /// Asserts that the contract emitted an event equal to `expected`.
71
+ ///
72
+ /// This is a **contains** assertion: after filtering events by `contract`,
73
+ /// `expected` must appear at least once (order doesn't matter; extra events allowed).
24
74
  ///
25
75
  /// Uses the event struct's `.topics()` and `.data()` methods (generated by `#[contractevent]`)
26
76
  /// to compare against emitted events. No more hardcoding topic names or field keys!
@@ -33,68 +83,86 @@ fn data_eq(env: &Env, a: &Val, b: &Val) -> bool {
33
83
  /// previous_owner: owner.clone(),
34
84
  /// new_owner: new_owner.clone(),
35
85
  /// };
36
- /// assert_event(&env, &contract, expected);
86
+ /// assert_eq_event(&env, &contract, expected);
37
87
  /// ```
38
- pub fn assert_event<E: Event>(env: &Env, contract: &Address, expected: E) {
39
- let events = env.events().all();
40
- let expected_topics: Vec<Val> = expected.topics(env);
41
- let expected_data: Val = expected.data(env);
88
+ pub fn assert_contains_event<E: Event>(env: &Env, contract: &Address, expected: E) {
89
+ // Compare against the emitted `xdr::ContractEvent` directly. This avoids the
90
+ // `xdr -> Val` roundtrip and ensures we match exactly what was emitted.
91
+ let expected_xdr = expected.to_xdr(env, contract);
42
92
 
93
+ let events = env.events().all().filter_by_contract(contract);
43
94
  let mut found = false;
44
- for (address, topics, data) in events.iter() {
45
- if &address == contract && topics_eq(env, &topics, &expected_topics) && data_eq(env, &data, &expected_data) {
95
+ for event in events.events().iter() {
96
+ if *event == expected_xdr {
46
97
  found = true;
47
98
  break;
48
99
  }
49
100
  }
50
101
 
51
- assert!(found, "Expected event not found. Expected topics: {:?}, data: {:?}", expected_topics, expected_data);
102
+ assert!(
103
+ found,
104
+ "Expected event not found. Expected topics: {:?}, data: {:?}",
105
+ expected.topics(env),
106
+ expected.data(env)
107
+ );
52
108
  }
53
109
 
54
- /// A helper struct to hold event topics and data for batch assertion with mixed event types.
55
- pub struct ExpectedEvent {
56
- pub topics: Vec<Val>,
57
- pub data: Val,
58
- }
110
+ /// Asserts that the environment emitted events that match `expected_events` exactly.
111
+ ///
112
+ /// This is a **strict equality** assertion over `env.events().all()`:
113
+ /// the full event list must match `expected_events` **in order** and **count**
114
+ /// (no missing/extra events).
115
+ ///
116
+ /// # Example
117
+ /// ```ignore
118
+ /// assert_eq_events(env, &contract, &[
119
+ /// &Event1 { ... },
120
+ /// &Event2 { ... },
121
+ /// ]);
122
+ /// ```
59
123
 
60
- pub trait IntoExpectedEvent: Event + Sized {
61
- fn expected(self, env: &Env) -> ExpectedEvent {
62
- ExpectedEvent { topics: self.topics(env), data: self.data(env) }
63
- }
64
- }
124
+ pub fn assert_eq_events(env: &Env, contract: &Address, expected_events: &[&dyn Event]) {
125
+ // Note: this module is only compiled for tests / testutils usage, where `std` is available.
126
+ extern crate std;
127
+ let expected_xdrs: std::vec::Vec<xdr::ContractEvent> =
128
+ expected_events.iter().map(|e| e.to_xdr(env, contract)).collect();
65
129
 
66
- // Blanket implementation for all Event types
67
- impl<E: Event> IntoExpectedEvent for E {}
130
+ assert_eq!(env.events().all(), expected_xdrs, "Expected events to match exactly");
131
+ }
68
132
 
69
- /// Asserts that all expected events were emitted from the contract (in any order).
70
- /// Use this when asserting multiple events of different types.
133
+ /// Asserts that all `expected_events` were emitted by the contract (in any order).
134
+ ///
135
+ /// This is a **contains** assertion with multiset semantics: after filtering events by
136
+ /// `contract`, every expected event must be present, and duplicates in `expected_events`
137
+ /// require duplicate emissions. Extra emitted events are allowed.
71
138
  ///
72
139
  /// # Example
73
140
  /// ```ignore
74
- /// use utils::testing_utils::IntoExpectedEvent;
75
- ///
76
- /// assert_events(env, &contract, &[
77
- /// Event1 { ... }.expected(env),
78
- /// Event2 { ... }.expected(env),
141
+ /// assert_eq_events(env, &contract, &[
142
+ /// &Event1 { ... },
143
+ /// &Event2 { ... },
79
144
  /// ]);
80
145
  /// ```
81
- pub fn assert_events(env: &Env, contract: &Address, expected_events: &[ExpectedEvent]) {
82
- let events = env.events().all();
83
- // Track which emitted events have already been matched so that duplicate expectations
84
- // require duplicate emissions (multiset semantics).
85
- //
146
+
147
+ pub fn assert_contains_events(env: &Env, contract: &Address, expected_events: &[&dyn Event]) {
86
148
  // Note: this module is only compiled for tests / testutils usage, where `std` is available.
87
149
  extern crate std;
88
- let mut used: std::vec::Vec<bool> = std::vec![false; events.len() as usize];
150
+ let events = env.events().all().filter_by_contract(contract);
151
+ let raw_events = events.events();
152
+ // Track which emitted events have already been matched so that duplicate expectations
153
+ // require duplicate emissions (multiset semantics).
154
+ // We use a `std::vec::Vec<bool>` as a "used" mask to implement multiset matching.
155
+ let mut used: std::vec::Vec<bool> = std::vec![false; raw_events.len()];
89
156
 
90
157
  for (i, expected) in expected_events.iter().enumerate() {
158
+ let expected_xdr = expected.to_xdr(env, contract);
159
+
91
160
  let mut found = false;
92
- for (idx, (address, topics, data)) in events.iter().enumerate() {
161
+ for (idx, event) in raw_events.iter().enumerate() {
93
162
  if used[idx] {
94
163
  continue;
95
164
  }
96
- if &address == contract && topics_eq(env, &topics, &expected.topics) && data_eq(env, &data, &expected.data)
97
- {
165
+ if *event == expected_xdr {
98
166
  used[idx] = true;
99
167
  found = true;
100
168
  break;
@@ -104,7 +172,9 @@ pub fn assert_events(env: &Env, contract: &Address, expected_events: &[ExpectedE
104
172
  assert!(
105
173
  found,
106
174
  "Expected event #{} not found. Expected topics: {:?}, data: {:?}",
107
- i, expected.topics, expected.data
175
+ i,
176
+ expected.topics(env),
177
+ expected.data(env)
108
178
  );
109
179
  }
110
180
  }