@layerzerolabs/protocol-stellar-v2 0.2.15 → 0.2.19

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 (262) hide show
  1. package/.turbo/turbo-build.log +365 -297
  2. package/.turbo/turbo-lint.log +142 -110
  3. package/.turbo/turbo-test.log +1273 -1222
  4. package/Cargo.lock +20 -5
  5. package/Cargo.toml +4 -1
  6. package/contracts/ERROR_SPEC.md +44 -0
  7. package/contracts/common-macros/src/auth.rs +113 -0
  8. package/contracts/common-macros/src/contract_ttl.rs +84 -0
  9. package/contracts/common-macros/src/lib.rs +181 -30
  10. package/contracts/common-macros/src/lz_contract.rs +83 -0
  11. package/contracts/common-macros/src/tests/{ownable.rs → auth.rs} +48 -15
  12. package/contracts/common-macros/src/tests/contract_ttl.rs +662 -0
  13. package/contracts/common-macros/src/tests/mod.rs +2 -2
  14. package/contracts/common-macros/src/tests/snapshots/common_macros__tests__auth__snapshot_generated_multisig_code.snap +20 -0
  15. package/contracts/common-macros/src/tests/snapshots/common_macros__tests__auth__snapshot_generated_ownable_code.snap +24 -0
  16. package/contracts/common-macros/src/tests/snapshots/{common_macros__tests__ownable__snapshot_only_owner_preserves_function_signature.snap → common_macros__tests__auth__snapshot_only_auth_preserves_function_signature.snap} +4 -4
  17. package/contracts/common-macros/src/tests/snapshots/{common_macros__tests__contract_impl__snapshot_generated_contract_impl_code.snap → common_macros__tests__contract_ttl__snapshot_generated_contractimpl_code.snap} +3 -3
  18. package/contracts/common-macros/src/tests/snapshots/common_macros__tests__contract_ttl__snapshot_generated_contracttrait_code.snap +69 -0
  19. package/contracts/common-macros/src/tests/snapshots/common_macros__tests__ttl_configurable__snapshot_generated_ttl_configurable_code.snap +7 -21
  20. package/contracts/common-macros/src/tests/snapshots/common_macros__tests__upgradeable__snapshot_generated_upgradeable_code.snap +2 -2
  21. package/contracts/common-macros/src/ttl_configurable.rs +19 -34
  22. package/contracts/common-macros/src/ttl_extendable.rs +36 -0
  23. package/contracts/common-macros/src/upgradeable.rs +5 -5
  24. package/contracts/common-macros/src/utils.rs +9 -0
  25. package/contracts/endpoint-v2/src/constants.rs +4 -4
  26. package/contracts/endpoint-v2/src/endpoint_v2.rs +38 -40
  27. package/contracts/endpoint-v2/src/errors.rs +4 -3
  28. package/contracts/endpoint-v2/src/events.rs +1 -1
  29. package/contracts/endpoint-v2/src/message_lib_manager.rs +18 -5
  30. package/contracts/endpoint-v2/src/messaging_channel.rs +11 -1
  31. package/contracts/endpoint-v2/src/messaging_composer.rs +11 -1
  32. package/contracts/endpoint-v2/src/storage.rs +1 -1
  33. package/contracts/endpoint-v2/src/tests/endpoint_v2/pay_messaging_fees.rs +3 -3
  34. package/contracts/endpoint-v2/src/tests/endpoint_v2/quote.rs +1 -1
  35. package/contracts/endpoint-v2/src/tests/endpoint_v2/require_oapp_auth.rs +2 -2
  36. package/contracts/endpoint-v2/src/tests/endpoint_v2/send.rs +3 -3
  37. package/contracts/endpoint-v2/src/tests/endpoint_v2/set_zro.rs +4 -4
  38. package/contracts/endpoint-v2/src/tests/message_lib_manager/require_receive_lib_for_eid.rs +3 -3
  39. package/contracts/endpoint-v2/src/tests/message_lib_manager/require_registered.rs +1 -1
  40. package/contracts/endpoint-v2/src/tests/message_lib_manager/require_send_lib_for_eid.rs +3 -3
  41. package/contracts/endpoint-v2/src/tests/message_lib_manager/require_supported_eid.rs +1 -1
  42. package/contracts/endpoint-v2/src/tests/messaging_channel/clear_payload.rs +4 -4
  43. package/contracts/endpoint-v2/src/tests/messaging_channel/inbound.rs +1 -1
  44. package/contracts/layerzero-views/src/layerzero_view.rs +3 -6
  45. package/contracts/macro-integration-tests/tests/runtime/ownable/mod.rs +2 -2
  46. package/contracts/macro-integration-tests/tests/runtime/ownable/{only_owner_guard.rs → only_auth_guard.rs} +1 -1
  47. package/contracts/macro-integration-tests/tests/runtime/ttl_configurable/configuration.rs +1 -1
  48. package/contracts/macro-integration-tests/tests/runtime/ttl_configurable/freeze.rs +1 -1
  49. package/contracts/macro-integration-tests/tests/runtime/ttl_configurable/mod.rs +0 -1
  50. package/contracts/macro-integration-tests/tests/ui/ownable/fail/{only_owner_missing_env.rs → only_auth_missing_env.rs} +3 -3
  51. package/contracts/macro-integration-tests/tests/ui/ownable/fail/{only_owner_missing_env.stderr → only_auth_missing_env.stderr} +4 -4
  52. package/contracts/macro-integration-tests/tests/ui/ownable/pass/namespacing_and_imports.rs +2 -3
  53. package/contracts/macro-integration-tests/tests/ui/ownable/pass/{only_owner_env_param_variants.rs → only_auth_env_param_variants.rs} +9 -9
  54. package/contracts/macro-integration-tests/tests/ui/ttl_configurable/pass/minimal_contract.rs +6 -6
  55. package/contracts/message-libs/message-lib-common/src/errors.rs +7 -2
  56. package/contracts/message-libs/message-lib-common/src/tests/packet_codec_v1/decode_packet_header.rs +3 -3
  57. package/contracts/message-libs/message-lib-common/src/tests/worker_options/append_lz_receive_option.rs +1 -2
  58. package/contracts/message-libs/message-lib-common/src/tests/worker_options/append_native_drop_option.rs +1 -2
  59. package/contracts/message-libs/message-lib-common/src/tests/worker_options/convert_legacy_options.rs +9 -9
  60. package/contracts/message-libs/message-lib-common/src/tests/worker_options/extract_type_3_options.rs +1 -1
  61. package/contracts/message-libs/message-lib-common/src/tests/worker_options/left_pad_to_bytes32.rs +1 -1
  62. package/contracts/message-libs/message-lib-common/src/tests/worker_options/split_worker_options.rs +2 -2
  63. package/contracts/message-libs/simple-message-lib/src/simple_message_lib.rs +7 -9
  64. package/contracts/message-libs/treasury/src/errors.rs +2 -2
  65. package/contracts/message-libs/treasury/src/events.rs +1 -1
  66. package/contracts/message-libs/treasury/src/interfaces/zro_fee_lib.rs +2 -2
  67. package/contracts/message-libs/treasury/src/storage.rs +1 -1
  68. package/contracts/message-libs/treasury/src/tests/treasury_tests.rs +1 -1
  69. package/contracts/message-libs/treasury/src/treasury.rs +14 -16
  70. package/contracts/message-libs/uln-302/src/receive_uln.rs +13 -2
  71. package/contracts/message-libs/uln-302/src/send_uln.rs +24 -4
  72. package/contracts/message-libs/uln-302/src/uln302.rs +6 -24
  73. package/contracts/oapps/counter/Cargo.toml +14 -1
  74. package/contracts/oapps/counter/integration_tests/mod.rs +4 -1
  75. package/contracts/oapps/counter/integration_tests/{setup.rs → setup_sml.rs} +48 -80
  76. package/contracts/oapps/counter/integration_tests/setup_uln.rs +997 -0
  77. package/contracts/oapps/counter/integration_tests/signing.rs +62 -0
  78. package/contracts/oapps/counter/integration_tests/test_with_sml.rs +24 -55
  79. package/contracts/oapps/counter/integration_tests/test_with_uln.rs +314 -0
  80. package/contracts/oapps/counter/integration_tests/utils.rs +196 -53
  81. package/contracts/oapps/counter/src/counter.rs +67 -43
  82. package/contracts/oapps/counter/src/tests/mod.rs +0 -13
  83. package/contracts/oapps/counter/src/tests/test_counter.rs +5 -7
  84. package/contracts/oapps/oapp/src/errors.rs +5 -1
  85. package/contracts/oapps/oapp/src/macro_tests/test_macros.rs +93 -78
  86. package/contracts/oapps/oapp/src/oapp_core.rs +36 -21
  87. package/contracts/oapps/oapp/src/oapp_options_type3.rs +48 -12
  88. package/contracts/oapps/oapp/src/oapp_receiver.rs +106 -41
  89. package/contracts/oapps/oapp/src/oapp_sender.rs +26 -34
  90. package/contracts/oapps/oapp/src/tests/test_oapp_core.rs +9 -8
  91. package/contracts/oapps/oapp/src/tests/test_oapp_options_type3.rs +25 -17
  92. package/contracts/oapps/oapp/src/tests/test_oapp_receiver.rs +7 -7
  93. package/contracts/oapps/oapp/src/tests/test_oapp_sender.rs +14 -15
  94. package/contracts/oapps/oapp-macros/src/generators.rs +128 -0
  95. package/contracts/oapps/oapp-macros/src/lib.rs +113 -56
  96. package/contracts/oapps/oft/Cargo.toml +10 -7
  97. package/contracts/oapps/{oft-std → oft}/integration-tests/extensions/test_oft_fee.rs +3 -4
  98. package/contracts/oapps/{oft-std → oft}/integration-tests/extensions/test_pausable.rs +2 -3
  99. package/contracts/oapps/{oft-std → oft}/integration-tests/extensions/test_rate_limiter.rs +1 -1
  100. package/contracts/oapps/oft/integration-tests/mod.rs +1 -1
  101. package/contracts/oapps/oft/integration-tests/setup.rs +29 -110
  102. package/contracts/oapps/oft/integration-tests/utils.rs +254 -21
  103. package/contracts/oapps/oft/src/extensions/oft_fee.rs +13 -14
  104. package/contracts/oapps/oft/src/extensions/pausable.rs +4 -4
  105. package/contracts/oapps/oft/src/extensions/rate_limiter.rs +5 -5
  106. package/contracts/oapps/oft/src/lib.rs +11 -13
  107. package/contracts/oapps/oft/src/oft.rs +147 -225
  108. package/contracts/oapps/oft/src/oft_types/lock_unlock.rs +9 -13
  109. package/contracts/oapps/oft/src/oft_types/mint_burn.rs +31 -14
  110. package/contracts/oapps/oft/src/oft_types/mod.rs +13 -0
  111. package/contracts/oapps/{oft-std → oft-core}/Cargo.toml +6 -4
  112. package/contracts/oapps/{oft-std → oft-core}/integration-tests/mod.rs +1 -1
  113. package/contracts/oapps/{oft-std → oft-core}/integration-tests/setup.rs +129 -30
  114. package/contracts/oapps/{oft → oft-core}/integration-tests/test_with_sml.rs +3 -3
  115. package/contracts/oapps/oft-core/integration-tests/utils.rs +201 -0
  116. package/contracts/oapps/oft-core/src/errors.rs +13 -0
  117. package/contracts/oapps/oft-core/src/lib.rs +18 -0
  118. package/contracts/oapps/oft-core/src/oft_core.rs +439 -0
  119. package/contracts/oapps/{oft → oft-core}/src/storage.rs +2 -0
  120. package/contracts/oapps/{oft → oft-core}/src/tests/mod.rs +0 -2
  121. package/contracts/oapps/{oft → oft-core}/src/tests/test_decimals.rs +2 -2
  122. package/contracts/oapps/{oft → oft-core}/src/tests/test_lz_receive.rs +7 -7
  123. package/contracts/oapps/{oft → oft-core}/src/tests/test_oft_msg_codec.rs +4 -5
  124. package/contracts/oapps/{oft → oft-core}/src/tests/test_resolve_address.rs +3 -3
  125. package/contracts/oapps/{oft → oft-core}/src/tests/test_utils.rs +78 -37
  126. package/contracts/oapps/oft-core/src/types.rs +58 -0
  127. package/contracts/oapps/{oft → oft-core}/src/utils.rs +1 -1
  128. package/contracts/upgrader/src/lib.rs +4 -4
  129. package/contracts/utils/src/auth.rs +44 -0
  130. package/contracts/utils/src/errors.rs +31 -5
  131. package/contracts/utils/src/lib.rs +3 -0
  132. package/contracts/utils/src/multisig.rs +211 -0
  133. package/contracts/utils/src/ownable.rs +137 -13
  134. package/contracts/utils/src/tests/buffer_reader.rs +6 -6
  135. package/contracts/utils/src/tests/buffer_writer.rs +6 -6
  136. package/contracts/utils/src/tests/bytes_ext.rs +2 -4
  137. package/contracts/utils/src/tests/mod.rs +1 -0
  138. package/contracts/utils/src/tests/multisig.rs +731 -0
  139. package/contracts/utils/src/tests/option_ext.rs +2 -5
  140. package/contracts/utils/src/tests/ownable.rs +456 -7
  141. package/contracts/utils/src/tests/ttl_configurable.rs +27 -16
  142. package/contracts/utils/src/tests/upgradeable.rs +4 -2
  143. package/contracts/utils/src/ttl_configurable.rs +23 -8
  144. package/contracts/utils/src/ttl_extendable.rs +27 -0
  145. package/contracts/utils/src/upgradeable.rs +2 -0
  146. package/contracts/workers/dvn/Cargo.toml +1 -1
  147. package/contracts/workers/dvn/src/auth.rs +7 -7
  148. package/contracts/workers/dvn/src/dvn.rs +10 -38
  149. package/contracts/workers/dvn/src/errors.rs +0 -7
  150. package/contracts/workers/dvn/src/events.rs +1 -14
  151. package/contracts/workers/dvn/src/interfaces/dvn.rs +2 -2
  152. package/contracts/workers/dvn/src/interfaces/mod.rs +0 -2
  153. package/contracts/workers/dvn/src/storage.rs +3 -13
  154. package/contracts/workers/dvn/src/tests/auth.rs +4 -4
  155. package/contracts/workers/dvn/src/tests/dvn.rs +1 -2
  156. package/contracts/workers/dvn/src/tests/multisig/set_signer.rs +7 -8
  157. package/contracts/workers/dvn/src/tests/multisig/set_threshold.rs +11 -8
  158. package/contracts/workers/dvn/src/tests/multisig/verify_signatures.rs +11 -12
  159. package/contracts/workers/dvn/src/tests/setup.rs +5 -5
  160. package/contracts/workers/dvn-fee-lib/Cargo.toml +1 -1
  161. package/contracts/workers/dvn-fee-lib/src/dvn_fee_lib.rs +3 -6
  162. package/contracts/workers/executor/src/auth.rs +80 -16
  163. package/contracts/workers/executor/src/executor.rs +5 -31
  164. package/contracts/workers/executor/src/storage.rs +2 -9
  165. package/contracts/workers/executor-fee-lib/Cargo.toml +1 -1
  166. package/contracts/workers/executor-fee-lib/src/executor_fee_lib.rs +3 -6
  167. package/contracts/workers/executor-helper/Cargo.toml +1 -1
  168. package/contracts/workers/executor-helper/src/executor_helper.rs +53 -73
  169. package/contracts/workers/price-feed/Cargo.toml +1 -1
  170. package/contracts/workers/price-feed/src/price_feed.rs +7 -10
  171. package/contracts/workers/worker/src/errors.rs +4 -0
  172. package/contracts/workers/worker/src/tests/worker.rs +7 -6
  173. package/contracts/workers/worker/src/worker.rs +20 -16
  174. package/package.json +7 -5
  175. package/sdk/.turbo/turbo-build.log +1 -0
  176. package/sdk/.turbo/turbo-test.log +1019 -0
  177. package/sdk/dist/generated/bml.d.ts +95 -8
  178. package/sdk/dist/generated/bml.js +95 -36
  179. package/sdk/dist/generated/counter.d.ts +289 -44
  180. package/sdk/dist/generated/counter.js +119 -49
  181. package/sdk/dist/generated/dvn.d.ts +312 -229
  182. package/sdk/dist/generated/dvn.js +144 -83
  183. package/sdk/dist/generated/dvn_fee_lib.d.ts +258 -63
  184. package/sdk/dist/generated/dvn_fee_lib.js +95 -26
  185. package/sdk/dist/generated/endpoint.d.ts +219 -24
  186. package/sdk/dist/generated/endpoint.js +108 -41
  187. package/sdk/dist/generated/executor.d.ts +239 -87
  188. package/sdk/dist/generated/executor.js +135 -63
  189. package/sdk/dist/generated/executor_fee_lib.d.ts +278 -74
  190. package/sdk/dist/generated/executor_fee_lib.js +135 -59
  191. package/sdk/dist/generated/executor_helper.d.ts +163 -21
  192. package/sdk/dist/generated/executor_helper.js +124 -52
  193. package/sdk/dist/generated/oft.d.ts +1842 -0
  194. package/sdk/dist/generated/oft.js +345 -0
  195. package/sdk/dist/generated/price_feed.d.ts +258 -63
  196. package/sdk/dist/generated/price_feed.js +95 -26
  197. package/sdk/dist/generated/sml.d.ts +235 -34
  198. package/sdk/dist/generated/sml.js +126 -53
  199. package/sdk/dist/generated/treasury.d.ts +1016 -0
  200. package/sdk/dist/generated/treasury.js +248 -0
  201. package/sdk/dist/generated/uln302.d.ts +235 -34
  202. package/sdk/dist/generated/uln302.js +126 -53
  203. package/sdk/dist/generated/upgrader.d.ts +17 -2
  204. package/sdk/dist/generated/upgrader.js +19 -1
  205. package/sdk/dist/index.d.ts +2 -1
  206. package/sdk/dist/index.js +2 -1
  207. package/sdk/package.json +6 -3
  208. package/sdk/src/index.ts +2 -1
  209. package/sdk/test/counter-sml.test.ts +376 -0
  210. package/sdk/test/counter-uln.test.ts +493 -0
  211. package/sdk/test/{oft.test.ts → oft-sml.test.ts} +196 -321
  212. package/sdk/test/suites/constants.ts +22 -2
  213. package/sdk/test/suites/globalSetup.ts +450 -0
  214. package/sdk/test/suites/localnet.ts +23 -6
  215. package/sdk/test/upgrader.test.ts +7 -16
  216. package/sdk/test/utils.ts +558 -85
  217. package/sdk/turbo.json +8 -0
  218. package/sdk/vitest.config.ts +21 -0
  219. package/tools/ts-bindings-gen/Cargo.toml +2 -0
  220. package/tools/ts-bindings-gen/src/main.rs +52 -4
  221. package/contracts/common-macros/src/contract_impl.rs +0 -52
  222. package/contracts/common-macros/src/ownable.rs +0 -41
  223. package/contracts/common-macros/src/tests/contract_impl.rs +0 -386
  224. package/contracts/common-macros/src/tests/snapshots/common_macros__tests__ownable__snapshot_generated_ownable_code.snap +0 -12
  225. package/contracts/macro-integration-tests/tests/runtime/ttl_configurable/extend_instance_ttl.rs +0 -50
  226. package/contracts/oapps/oapp-macros/src/oapp_core.rs +0 -41
  227. package/contracts/oapps/oapp-macros/src/oapp_full.rs +0 -21
  228. package/contracts/oapps/oapp-macros/src/oapp_options_type3.rs +0 -31
  229. package/contracts/oapps/oapp-macros/src/oapp_receiver.rs +0 -48
  230. package/contracts/oapps/oapp-macros/src/oapp_sender.rs +0 -21
  231. package/contracts/oapps/oapp-macros/src/util.rs +0 -107
  232. package/contracts/oapps/oft/src/constants.rs +0 -5
  233. package/contracts/oapps/oft/src/default_oft_impl.rs +0 -152
  234. package/contracts/oapps/oft/src/errors.rs +0 -8
  235. package/contracts/oapps/oft/src/interfaces/mint_burn_token.rs +0 -23
  236. package/contracts/oapps/oft/src/interfaces/mod.rs +0 -3
  237. package/contracts/oapps/oft/src/tests/extensions/mod.rs +0 -11
  238. package/contracts/oapps/oft/src/tests/extensions/setup.rs +0 -903
  239. package/contracts/oapps/oft/src/tests/extensions/test_oft_fee.rs +0 -749
  240. package/contracts/oapps/oft/src/tests/extensions/test_pausable.rs +0 -432
  241. package/contracts/oapps/oft/src/tests/extensions/test_rate_limiter.rs +0 -1078
  242. package/contracts/oapps/oft/src/types.rs +0 -38
  243. package/contracts/oapps/oft-std/integration-tests/utils.rs +0 -427
  244. package/contracts/oapps/oft-std/src/lib.rs +0 -16
  245. package/contracts/oapps/oft-std/src/oft.rs +0 -156
  246. package/contracts/workers/dvn/src/interfaces/multisig.rs +0 -56
  247. package/contracts/workers/dvn/src/multisig.rs +0 -157
  248. package/sdk/dist/generated/oft_std.d.ts +0 -1544
  249. package/sdk/dist/generated/oft_std.js +0 -271
  250. package/sdk/test/index.test.ts +0 -375
  251. /package/contracts/oapps/{oft-std → oft}/integration-tests/extensions/mod.rs +0 -0
  252. /package/contracts/oapps/{oft → oft-core}/src/codec/mod.rs +0 -0
  253. /package/contracts/oapps/{oft → oft-core}/src/codec/oft_compose_msg_codec.rs +0 -0
  254. /package/contracts/oapps/{oft → oft-core}/src/codec/oft_msg_codec.rs +0 -0
  255. /package/contracts/oapps/{oft → oft-core}/src/events.rs +0 -0
  256. /package/contracts/oapps/{oft → oft-core}/src/tests/test_oft_compose_msg_codec.rs +0 -0
  257. /package/contracts/oapps/{oft → oft-core}/src/tests/test_oft_version.rs +0 -0
  258. /package/contracts/oapps/{oft → oft-core}/src/tests/test_quote_oft.rs +0 -0
  259. /package/contracts/oapps/{oft → oft-core}/src/tests/test_quote_send.rs +0 -0
  260. /package/contracts/oapps/{oft → oft-core}/src/tests/test_send.rs +0 -0
  261. /package/contracts/oapps/{oft → oft-core}/src/tests/test_token.rs +0 -0
  262. /package/sdk/test/suites/{testUpgradeable.ts → dummyContractClient.ts} +0 -0
@@ -0,0 +1,997 @@
1
+ extern crate std;
2
+
3
+ use crate::{
4
+ counter::{Counter, CounterClient},
5
+ integration_tests::{
6
+ signing::{Ed25519KeyPair, Secp256k1KeyPair},
7
+ utils::{address_to_bytes32, decode_packet, register_library, set_peer, set_zro, ChainSetupCommon},
8
+ },
9
+ };
10
+ use dvn::{DVNClient, DstConfig as DvnDstConfig, DstConfigParam as DvnDstConfigParam, LzDVN};
11
+ use dvn_fee_lib::DvnFeeLib;
12
+ use endpoint_v2::{EndpointV2, EndpointV2Client};
13
+ use executor::{DstConfig as ExecutorDstConfig, ExecutorClient, LzExecutor, SetDstConfigParam};
14
+ use executor_fee_lib::ExecutorFeeLib;
15
+ use executor_helper::{ExecutorHelper, ExecutorHelperClient};
16
+ use price_feed::{types::UpdatePrice, LzPriceFeed};
17
+ use soroban_sdk::{
18
+ testutils::{Address as _, MockAuth, MockAuthInvoke},
19
+ token::TokenClient,
20
+ vec, Address, BytesN, Env, IntoVal, Vec,
21
+ };
22
+ use treasury::Treasury;
23
+ use uln302::{
24
+ ExecutorConfig, ReceiveUln302Client, SendUln302Client, SetDefaultExecutorConfigParam, SetDefaultUlnConfigParam,
25
+ Uln302, Uln302Client, UlnConfig,
26
+ };
27
+ use utils::{buffer_reader::BufferReader, buffer_writer::BufferWriter};
28
+ use worker::Price;
29
+
30
+ pub const CONFIRMATIONS: u64 = 1;
31
+ pub const MAX_MESSAGE_SIZE: u32 = 10000;
32
+ pub const DVN_VID: u32 = 1; // DVN Verifier ID
33
+
34
+ // Price feed constants for testing
35
+ const PRICE_RATIO_DENOMINATOR: u128 = 10_u128.pow(20);
36
+ const DEFAULT_GAS_PRICE: u64 = 100;
37
+ const DEFAULT_GAS_PER_BYTE: u32 = 1;
38
+ const DEFAULT_MULTIPLIER_BPS: u32 = 10000; // 100%
39
+
40
+ // Worker options constants
41
+ const OPTIONS_TYPE_3: u16 = 3;
42
+ const EXECUTOR_WORKER_ID: u8 = 1;
43
+ const EXECUTOR_OPTION_TYPE_LZRECEIVE: u8 = 1;
44
+
45
+ #[allow(dead_code)]
46
+ const EXECUTOR_OPTION_TYPE_NATIVE_DROP: u8 = 2;
47
+
48
+ const EXECUTOR_OPTION_TYPE_LZCOMPOSE: u8 = 3;
49
+
50
+ // ============================================================================
51
+ // Options Creation Functions
52
+ // ============================================================================
53
+
54
+ /// Creates default ULN302 options with Type 3 format and basic lzReceive gas (no lzCompose).
55
+ /// This is required because ULN302 expects at least 2 bytes for the options type header.
56
+ pub fn create_default_options(env: &Env) -> soroban_sdk::Bytes {
57
+ create_options_with_gas(env, 100000, 0)
58
+ }
59
+
60
+ /// Extracts the executor value (native token amount) from Type 3 options.
61
+ /// Returns 0 if no value is specified in the options.
62
+ pub fn get_executor_value_from_options(_env: &Env, options: &soroban_sdk::Bytes) -> i128 {
63
+ let mut reader = BufferReader::new(options);
64
+
65
+ // Skip options type header (2 bytes)
66
+ let _options_type = reader.read_u16();
67
+
68
+ // Parse options until we find lzReceive with value
69
+ while reader.remaining_len() > 0 {
70
+ let _worker_id = reader.read_u8();
71
+ let option_size = reader.read_u16();
72
+ let option_type = reader.read_u8();
73
+
74
+ if option_type == EXECUTOR_OPTION_TYPE_LZRECEIVE {
75
+ // option_size includes option_type(1) + data
76
+ // If data is 32 bytes (gas + value), extract value
77
+ // If data is 16 bytes (gas only), value is 0
78
+ let data_size = option_size - 1; // subtract option_type byte
79
+ let _gas = reader.read_u128();
80
+ if data_size == 32 {
81
+ return reader.read_u128() as i128;
82
+ }
83
+ return 0;
84
+ } else {
85
+ // Skip this option's data (option_size - 1 for option_type already read)
86
+ reader.skip((option_size - 1).into());
87
+ }
88
+ }
89
+
90
+ 0
91
+ }
92
+
93
+ /// Extracts the lzCompose value (native token amount) from Type 3 options.
94
+ /// Returns 0 if no lzCompose value is specified in the options.
95
+ pub fn get_compose_value_from_options(_env: &Env, options: &soroban_sdk::Bytes) -> i128 {
96
+ let mut reader = BufferReader::new(options);
97
+
98
+ // Skip options type header (2 bytes)
99
+ let _options_type = reader.read_u16();
100
+
101
+ // Parse options until we find lzCompose with value
102
+ while reader.remaining_len() > 0 {
103
+ let _worker_id = reader.read_u8();
104
+ let option_size = reader.read_u16();
105
+ let option_type = reader.read_u8();
106
+
107
+ if option_type == EXECUTOR_OPTION_TYPE_LZCOMPOSE {
108
+ // option_size includes option_type(1) + data
109
+ // lzCompose format: [index: u16][gas: u128] (18 bytes) or [index: u16][gas: u128][value: u128] (34 bytes)
110
+ // So data_size is 18 or 34
111
+ let data_size = option_size - 1; // subtract option_type byte
112
+ let _index = reader.read_u16();
113
+ let _gas = reader.read_u128();
114
+ if data_size == 34 {
115
+ return reader.read_u128() as i128;
116
+ }
117
+ return 0;
118
+ } else {
119
+ // Skip this option's data (option_size - 1 for option_type already read)
120
+ reader.skip((option_size - 1).into());
121
+ }
122
+ }
123
+
124
+ 0
125
+ }
126
+
127
+ /// Creates ULN302 options with lzReceive gas and optional lzCompose gas.
128
+ /// Format: [options_type(2)][lzReceive option][lzCompose option (if lz_compose_gas > 0)]
129
+ ///
130
+ /// # Arguments
131
+ /// * `env` - Soroban environment
132
+ /// * `gas` - Gas limit for lzReceive execution (always required)
133
+ /// * `lz_compose_gas` - Gas limit for lzCompose execution (only added if > 0)
134
+ pub fn create_options_with_gas(env: &Env, gas: u128, lz_compose_gas: u128) -> soroban_sdk::Bytes {
135
+ create_options_with_gas_and_value(env, gas, 0, lz_compose_gas, 0)
136
+ }
137
+
138
+ /// Creates ULN302 options with lzReceive gas/value and optional lzCompose gas/value.
139
+ /// Format: [options_type(2)][lzReceive option][lzCompose option (if lz_compose_gas > 0)]
140
+ ///
141
+ /// # Arguments
142
+ /// * `env` - Soroban environment
143
+ /// * `gas` - Gas limit for lzReceive execution (always required)
144
+ /// * `value` - Native value for lzReceive (only added if > 0)
145
+ /// * `lz_compose_gas` - Gas limit for lzCompose execution (only added if > 0)
146
+ /// * `lz_compose_value` - Native value for lzCompose (only added if > 0)
147
+ pub fn create_options_with_gas_and_value(
148
+ env: &Env,
149
+ gas: u128,
150
+ value: u128,
151
+ lz_compose_gas: u128,
152
+ lz_compose_value: u128,
153
+ ) -> soroban_sdk::Bytes {
154
+ let mut writer = BufferWriter::new(env);
155
+
156
+ // Type 3 options header
157
+ writer.write_u16(OPTIONS_TYPE_3);
158
+
159
+ // Add lzReceive option - include value only if > 0
160
+ if value > 0 {
161
+ writer
162
+ .write_u8(EXECUTOR_WORKER_ID) // worker_id (1 byte)
163
+ .write_u16(33) // option_size: option_type(1) + gas(16) + value(16) = 33 bytes
164
+ .write_u8(EXECUTOR_OPTION_TYPE_LZRECEIVE) // option_type (1 byte)
165
+ .write_u128(gas) // execution gas (16 bytes)
166
+ .write_u128(value); // value (16 bytes)
167
+ } else {
168
+ writer
169
+ .write_u8(EXECUTOR_WORKER_ID) // worker_id (1 byte)
170
+ .write_u16(17) // option_size: option_type(1) + gas(16) = 17 bytes
171
+ .write_u8(EXECUTOR_OPTION_TYPE_LZRECEIVE) // option_type (1 byte)
172
+ .write_u128(gas); // execution gas (16 bytes)
173
+ }
174
+
175
+ // Add lzCompose option only if lz_compose_gas > 0
176
+ if lz_compose_gas > 0 {
177
+ if lz_compose_value > 0 {
178
+ // lzCompose with gas and value: [index: u16][gas: u128][value: u128] = 34 bytes
179
+ writer
180
+ .write_u8(EXECUTOR_WORKER_ID) // worker_id (1 byte)
181
+ .write_u16(35) // option_size: option_type(1) + index(2) + gas(16) + value(16) = 35 bytes
182
+ .write_u8(EXECUTOR_OPTION_TYPE_LZCOMPOSE) // option_type (1 byte)
183
+ .write_u16(0) // compose index (2 bytes)
184
+ .write_u128(lz_compose_gas) // compose gas (16 bytes)
185
+ .write_u128(lz_compose_value); // compose value (16 bytes)
186
+ } else {
187
+ // lzCompose with gas only: [index: u16][gas: u128] = 18 bytes
188
+ writer
189
+ .write_u8(EXECUTOR_WORKER_ID) // worker_id (1 byte)
190
+ .write_u16(19) // option_size: option_type(1) + index(2) + gas(16) = 19 bytes
191
+ .write_u8(EXECUTOR_OPTION_TYPE_LZCOMPOSE) // option_type (1 byte)
192
+ .write_u16(0) // compose index (2 bytes)
193
+ .write_u128(lz_compose_gas); // compose gas (16 bytes)
194
+ }
195
+ }
196
+
197
+ writer.to_bytes()
198
+ }
199
+
200
+ /// Creates ULN302 options with lzReceive gas and value (for ABA return messages).
201
+ /// Format: [options_type(2)][lzReceive option with gas and value]
202
+ pub fn create_aba_return_options(env: &Env) -> soroban_sdk::Bytes {
203
+ let mut writer = BufferWriter::new(env);
204
+
205
+ // Type 3 options header
206
+ writer.write_u16(OPTIONS_TYPE_3);
207
+
208
+ // Add lzReceive option with gas=200000 and value=10 (matching Counter's ABA return)
209
+ writer
210
+ .write_u8(EXECUTOR_WORKER_ID) // worker_id (1 byte)
211
+ .write_u16(33) // option_size: option_type(1) + gas(16) + value(16) = 33 bytes
212
+ .write_u8(EXECUTOR_OPTION_TYPE_LZRECEIVE) // option_type (1 byte)
213
+ .write_u128(200000) // execution gas (16 bytes)
214
+ .write_u128(10); // value (16 bytes)
215
+
216
+ writer.to_bytes()
217
+ }
218
+
219
+ /// Creates ULN302 options for ComposedABA return (gas=200000, no lzCompose).
220
+ pub fn create_composed_aba_return_options(env: &Env) -> soroban_sdk::Bytes {
221
+ create_options_with_gas(env, 200000, 0)
222
+ }
223
+
224
+ /// Creates ULN302 options with native drop included.
225
+ /// Format: [options_type(2)][lzReceive option][native drop option]
226
+ #[allow(dead_code)]
227
+ pub fn create_options_with_native_drop(env: &Env, amount: u128, receiver: &BytesN<32>) -> soroban_sdk::Bytes {
228
+ let mut writer = BufferWriter::new(env);
229
+
230
+ // Type 3 options header
231
+ writer.write_u16(OPTIONS_TYPE_3);
232
+
233
+ // Add a basic lzReceive option with default gas (100000)
234
+ writer
235
+ .write_u8(EXECUTOR_WORKER_ID) // worker_id (1 byte)
236
+ .write_u16(17) // option_size: option_type(1) + data(16) = 17 bytes
237
+ .write_u8(EXECUTOR_OPTION_TYPE_LZRECEIVE) // option_type (1 byte)
238
+ .write_u128(100000); // execution gas data (16 bytes)
239
+
240
+ // Add native drop option
241
+ writer
242
+ .write_u8(EXECUTOR_WORKER_ID) // worker_id (1 byte)
243
+ .write_u16(49) // option_size: option_type(1) + amount(16) + receiver(32) = 49 bytes
244
+ .write_u8(EXECUTOR_OPTION_TYPE_NATIVE_DROP) // option_type (1 byte)
245
+ .write_u128(amount) // drop amount (16 bytes)
246
+ .write_bytes_n(receiver); // receiver address (32 bytes)
247
+
248
+ writer.to_bytes()
249
+ }
250
+
251
+ // ============================================================================
252
+ // Test Setup
253
+ // ============================================================================
254
+
255
+ /// DVN credentials for initialization (addresses for signers and admin).
256
+ pub struct DvnCredentials {
257
+ /// secp256k1 addresses for DVN multisig
258
+ pub signers: std::vec::Vec<Secp256k1KeyPair>,
259
+ /// Ed25519 key pair for DVN admin address
260
+ pub admin_keypair: Ed25519KeyPair,
261
+ }
262
+
263
+ impl DvnCredentials {
264
+ /// Generate new DVN credentials with the specified number of signers.
265
+ pub fn generate(num_signers: usize) -> Self {
266
+ Self {
267
+ signers: (0..num_signers).map(|_| Secp256k1KeyPair::generate()).collect(),
268
+ admin_keypair: Ed25519KeyPair::generate(),
269
+ }
270
+ }
271
+
272
+ /// Get the signer addresses as a Soroban Vec<BytesN<20>>.
273
+ pub fn signer_addresses(&self, env: &Env) -> Vec<BytesN<20>> {
274
+ let mut addrs: Vec<BytesN<20>> = vec![env];
275
+ for kp in &self.signers {
276
+ addrs.push_back(kp.signer(env));
277
+ }
278
+ addrs
279
+ }
280
+
281
+ /// Get the admin address for DVN registration.
282
+ pub fn admin_address(&self, env: &Env) -> Address {
283
+ self.admin_keypair.address(env)
284
+ }
285
+ }
286
+
287
+ pub struct ChainSetup<'a> {
288
+ pub eid: u32,
289
+ pub owner: Address,
290
+ pub admin: Address, // Worker admin
291
+ pub native_token: Address,
292
+ pub endpoint: EndpointV2Client<'a>,
293
+ pub uln302: Uln302Client<'a>,
294
+ pub dvn: DVNClient<'a>,
295
+ pub dvn2: DVNClient<'a>, // Second DVN for multi-DVN tests
296
+ pub executor: ExecutorClient<'a>,
297
+ pub executor_helper: ExecutorHelperClient<'a>,
298
+ pub price_feed: Address,
299
+ pub counter: CounterClient<'a>,
300
+ }
301
+
302
+ impl<'a> ChainSetupCommon<'a> for ChainSetup<'a> {
303
+ fn counter(&self) -> &CounterClient<'a> {
304
+ &self.counter
305
+ }
306
+ fn endpoint(&self) -> &EndpointV2Client<'a> {
307
+ &self.endpoint
308
+ }
309
+ fn native_token(&self) -> &Address {
310
+ &self.native_token
311
+ }
312
+ fn owner(&self) -> &Address {
313
+ &self.owner
314
+ }
315
+ fn validate_packet(
316
+ &self,
317
+ env: &Env,
318
+ packet_event: &(soroban_sdk::Bytes, soroban_sdk::Bytes, Address),
319
+ ) -> endpoint_v2::OutboundPacket {
320
+ use message_lib_common::packet_codec_v1;
321
+
322
+ let packet = decode_packet(env, &packet_event.0);
323
+ let encoded_header = packet_codec_v1::encode_packet_header(env, &packet);
324
+ let payload_hash = packet_codec_v1::payload_hash(env, &packet);
325
+
326
+ // DVN verify with mock_auths
327
+ let receive_uln302 = ReceiveUln302Client::new(env, &self.uln302.address);
328
+
329
+ env.mock_auths(&[soroban_sdk::testutils::MockAuth {
330
+ address: &self.dvn.address,
331
+ invoke: &soroban_sdk::testutils::MockAuthInvoke {
332
+ contract: &self.uln302.address,
333
+ fn_name: "verify",
334
+ args: (&self.dvn.address, &encoded_header, &payload_hash, &CONFIRMATIONS).into_val(env),
335
+ sub_invokes: &[],
336
+ },
337
+ }]);
338
+ receive_uln302.verify(&self.dvn.address, &encoded_header, &payload_hash, &CONFIRMATIONS);
339
+
340
+ // Commit verification (permissionless)
341
+ receive_uln302.commit_verification(&encoded_header, &payload_hash);
342
+
343
+ packet
344
+ }
345
+ }
346
+
347
+ pub struct TestSetup<'a> {
348
+ pub env: Env,
349
+ pub chain_a: ChainSetup<'a>,
350
+ pub chain_b: ChainSetup<'a>,
351
+ }
352
+
353
+ /// Intermediate structure to hold chain infrastructure before DVN/Executor creation
354
+ struct ChainInfra {
355
+ endpoint_address: Address,
356
+ uln302_address: Address,
357
+ #[allow(dead_code)]
358
+ treasury_address: Address,
359
+ price_feed_address: Address,
360
+ dvn_fee_lib_address: Address,
361
+ executor_fee_lib_address: Address,
362
+ zro_token: Address,
363
+ admin: Address,
364
+ deposit_address: Address,
365
+ /// DVN credentials (addresses for initialization)
366
+ dvn_credentials: DvnCredentials,
367
+ }
368
+
369
+ /// Phase 1: Create basic chain infrastructure (Endpoint, ULN302, Treasury, etc.)
370
+ fn setup_chain_infrastructure(env: &Env, owner: &Address) -> ChainInfra {
371
+ // Create ZRO token
372
+ let zro_sac = env.register_stellar_asset_contract_v2(owner.clone());
373
+ let zro_token = zro_sac.address();
374
+
375
+ // Register endpoint
376
+ let endpoint_address = env.register(EndpointV2, (owner,));
377
+
378
+ // Register Treasury (real)
379
+ let treasury_address = env.register(Treasury, (owner,));
380
+
381
+ // Register PriceFeed (real)
382
+ let price_updater = Address::generate(env);
383
+ let price_feed_address = env.register(LzPriceFeed, (owner, &price_updater));
384
+
385
+ // Register fee libs
386
+ let dvn_fee_lib_address = env.register(DvnFeeLib, (owner,));
387
+ let executor_fee_lib_address = env.register(ExecutorFeeLib, (owner,));
388
+
389
+ // Register ULN302
390
+ let uln302_address = env.register(Uln302, (owner, &endpoint_address, &treasury_address));
391
+
392
+ // Create admin for workers
393
+ let admin = Address::generate(env);
394
+
395
+ // Deposit address for fee collection
396
+ let deposit_address = Address::generate(env);
397
+
398
+ // Generate real DVN credentials (1 signer with threshold 1)
399
+ let dvn_credentials = DvnCredentials::generate(1);
400
+
401
+ ChainInfra {
402
+ endpoint_address,
403
+ uln302_address,
404
+ treasury_address: treasury_address.clone(),
405
+ price_feed_address,
406
+ dvn_fee_lib_address,
407
+ executor_fee_lib_address,
408
+ zro_token,
409
+ admin,
410
+ deposit_address,
411
+ dvn_credentials,
412
+ }
413
+ }
414
+
415
+ /// Phase 2: Create DVN, Executor, and ExecutorHelper with cross-chain ULN302 support
416
+ fn setup_chain_workers<'a>(
417
+ env: &Env,
418
+ owner: &Address,
419
+ infra: &ChainInfra,
420
+ all_uln302_addresses: &Vec<Address>, // All ULN302 addresses across chains
421
+ ) -> (DVNClient<'a>, DVNClient<'a>, ExecutorClient<'a>, ExecutorHelperClient<'a>) {
422
+ // Use the DVN admin address from credentials, plus the worker admin
423
+ let admins = vec![env, infra.admin.clone(), infra.dvn_credentials.admin_address(env)];
424
+
425
+ // Use real secp256k1 signers from credentials
426
+ let signers = infra.dvn_credentials.signer_addresses(env);
427
+ let threshold: u32 = infra.dvn_credentials.signers.len() as u32;
428
+
429
+ // Register first DVN with ALL ULN302 addresses as supported message libs
430
+ let dvn_address = env.register(
431
+ LzDVN,
432
+ (
433
+ DVN_VID, // vid
434
+ &signers, // signers (real secp256k1 addresses)
435
+ threshold, // threshold
436
+ &admins, // admins
437
+ all_uln302_addresses, // supported_msglibs - ALL chains' ULN302s
438
+ &infra.price_feed_address, // price_feed
439
+ DEFAULT_MULTIPLIER_BPS, // default_multiplier_bps
440
+ &infra.dvn_fee_lib_address, // worker_fee_lib
441
+ &infra.deposit_address, // deposit_address
442
+ ),
443
+ );
444
+
445
+ // Register second DVN for multi-DVN verification tests
446
+ let dvn2_credentials = DvnCredentials::generate(1);
447
+ let admins2 = vec![env, infra.admin.clone(), dvn2_credentials.admin_address(env)];
448
+ let signers2 = dvn2_credentials.signer_addresses(env);
449
+ let dvn2_address = env.register(
450
+ LzDVN,
451
+ (
452
+ DVN_VID + 1, // Different vid for second DVN
453
+ &signers2,
454
+ 1u32, // threshold
455
+ &admins2,
456
+ all_uln302_addresses,
457
+ &infra.price_feed_address,
458
+ DEFAULT_MULTIPLIER_BPS,
459
+ &infra.dvn_fee_lib_address,
460
+ &infra.deposit_address,
461
+ ),
462
+ );
463
+
464
+ // Register ExecutorHelper (stateless entry point for executor AA workflow)
465
+ let executor_helper_address = env.register(ExecutorHelper, ());
466
+
467
+ // Register real Executor with ALL ULN302 addresses as supported message libs
468
+ let executor_address = env.register(
469
+ LzExecutor,
470
+ (
471
+ &infra.endpoint_address,
472
+ owner,
473
+ &admins,
474
+ all_uln302_addresses, // supported_msglibs - ALL chains' ULN302s
475
+ &infra.price_feed_address,
476
+ DEFAULT_MULTIPLIER_BPS,
477
+ &infra.executor_fee_lib_address, // worker_fee_lib
478
+ &infra.deposit_address, // deposit_address
479
+ ),
480
+ );
481
+
482
+ let dvn = DVNClient::new(env, &dvn_address);
483
+ let dvn2 = DVNClient::new(env, &dvn2_address);
484
+ let executor = ExecutorClient::new(env, &executor_address);
485
+ let executor_helper = ExecutorHelperClient::new(env, &executor_helper_address);
486
+
487
+ (dvn, dvn2, executor, executor_helper)
488
+ }
489
+
490
+ /// Phase 3: Complete chain setup by creating Counter and finalizing configuration
491
+ fn finalize_chain_setup<'a>(
492
+ env: &Env,
493
+ owner: &Address,
494
+ infra: ChainInfra,
495
+ dvn: DVNClient<'a>,
496
+ dvn2: DVNClient<'a>,
497
+ executor: ExecutorClient<'a>,
498
+ executor_helper: ExecutorHelperClient<'a>,
499
+ ) -> ChainSetup<'a> {
500
+ // Register Counter OApp
501
+ let counter_address = env.register(Counter, (owner, &infra.endpoint_address, owner));
502
+
503
+ // Create clients
504
+ let endpoint = EndpointV2Client::new(env, &infra.endpoint_address);
505
+
506
+ let native_token = endpoint.native_token();
507
+
508
+ let uln302 = Uln302Client::new(env, &infra.uln302_address);
509
+ let counter = CounterClient::new(env, &counter_address);
510
+
511
+ set_zro(env, owner, &endpoint, &infra.zro_token);
512
+ register_library(env, owner, &endpoint, &uln302.address);
513
+
514
+ let eid = endpoint.eid();
515
+
516
+ // Set up price feed with initial prices for this chain's eid
517
+ setup_price_feed(env, owner, &infra.price_feed_address, eid);
518
+
519
+ let chain_setup = ChainSetup {
520
+ owner: owner.clone(),
521
+ admin: infra.admin,
522
+ endpoint,
523
+ uln302,
524
+ dvn,
525
+ dvn2,
526
+ executor,
527
+ executor_helper,
528
+ price_feed: infra.price_feed_address,
529
+ counter,
530
+ eid,
531
+ native_token,
532
+ };
533
+
534
+ // Allow executor self-spend so transfer_from succeeds in compose tests.
535
+ env.mock_all_auths();
536
+ TokenClient::new(env, &chain_setup.native_token).approve(
537
+ &chain_setup.executor.address,
538
+ &chain_setup.executor.address,
539
+ &i128::MAX,
540
+ &10_u32,
541
+ );
542
+
543
+ chain_setup
544
+ }
545
+
546
+ /// Set up price feed with test prices
547
+ fn setup_price_feed(env: &Env, owner: &Address, price_feed: &Address, eid: u32) {
548
+ let client = price_feed::LzPriceFeedClient::new(env, price_feed);
549
+
550
+ // Set price ratio denominator
551
+ env.mock_auths(&[MockAuth {
552
+ address: owner,
553
+ invoke: &MockAuthInvoke {
554
+ contract: price_feed,
555
+ fn_name: "set_price_ratio_denominator",
556
+ args: (&PRICE_RATIO_DENOMINATOR,).into_val(env),
557
+ sub_invokes: &[],
558
+ },
559
+ }]);
560
+ client.set_price_ratio_denominator(&PRICE_RATIO_DENOMINATOR);
561
+
562
+ // Set native token price in USD (1 XLM = 1 USD for simplicity, scaled)
563
+ let native_price_usd: u128 = 10_000_000; // 1 USD with 7 decimals
564
+ env.mock_auths(&[MockAuth {
565
+ address: owner,
566
+ invoke: &MockAuthInvoke {
567
+ contract: price_feed,
568
+ fn_name: "set_native_token_price_usd",
569
+ args: (owner, &native_price_usd).into_val(env),
570
+ sub_invokes: &[],
571
+ },
572
+ }]);
573
+ client.set_native_token_price_usd(owner, &native_price_usd);
574
+
575
+ // Set price for this eid (for testing, use 1:1 price ratio)
576
+ // Price feed internally uses eid % 30000 for lookups, so we need to normalize
577
+ let normalized_eid = eid % 30_000;
578
+ let price = Price {
579
+ price_ratio: PRICE_RATIO_DENOMINATOR,
580
+ gas_price_in_unit: DEFAULT_GAS_PRICE,
581
+ gas_per_byte: DEFAULT_GAS_PER_BYTE,
582
+ };
583
+
584
+ // Note: STELLAR_EID (30111) normalizes to 111, which is the Optimism mainnet EID.
585
+ // The Optimism fee model requires prices for BOTH L2 (111) AND L1 Ethereum (101).
586
+ // So we set prices for both.
587
+ let mut prices = vec![env, UpdatePrice { eid: normalized_eid, price: price.clone() }];
588
+
589
+ // If this is an Optimism-style chain (111, 10132, 20132), also set L1 Ethereum price
590
+ if normalized_eid == 111 {
591
+ prices.push_back(UpdatePrice { eid: 101, price: price.clone() }); // Ethereum mainnet
592
+ } else if normalized_eid == 10132 {
593
+ prices.push_back(UpdatePrice { eid: 10121, price: price.clone() }); // Ethereum Goerli
594
+ } else if normalized_eid == 20132 {
595
+ prices.push_back(UpdatePrice { eid: 20121, price: price.clone() }); // Ethereum Goerli
596
+ }
597
+
598
+ env.mock_auths(&[MockAuth {
599
+ address: owner,
600
+ invoke: &MockAuthInvoke {
601
+ contract: price_feed,
602
+ fn_name: "set_price",
603
+ args: (owner, &prices).into_val(env),
604
+ sub_invokes: &[],
605
+ },
606
+ }]);
607
+ client.set_price(owner, &prices);
608
+ }
609
+
610
+ pub fn setup<'a>() -> TestSetup<'a> {
611
+ let env = Env::default();
612
+ env.mock_all_auths(); // Required for contract registration (DVN multisig init, etc.)
613
+ let owner = Address::generate(&env);
614
+
615
+ // Phase 1: Create basic infrastructure for both chains
616
+ let infra_a = setup_chain_infrastructure(&env, &owner);
617
+ let infra_b = setup_chain_infrastructure(&env, &owner);
618
+
619
+ // Collect all ULN302 addresses for cross-chain support
620
+ let all_uln302_addresses: Vec<Address> = vec![&env, infra_a.uln302_address.clone(), infra_b.uln302_address.clone()];
621
+
622
+ // Phase 2: Create workers with cross-chain ULN302 support
623
+ let (dvn_a, dvn2_a, executor_a, executor_helper_a) =
624
+ setup_chain_workers(&env, &owner, &infra_a, &all_uln302_addresses);
625
+ let (dvn_b, dvn2_b, executor_b, executor_helper_b) =
626
+ setup_chain_workers(&env, &owner, &infra_b, &all_uln302_addresses);
627
+
628
+ // Phase 3: Finalize chain setup
629
+ let chain_a = finalize_chain_setup(&env, &owner, infra_a, dvn_a, dvn2_a, executor_a, executor_helper_a);
630
+ let chain_b = finalize_chain_setup(&env, &owner, infra_b, dvn_b, dvn2_b, executor_b, executor_helper_b);
631
+
632
+ TestSetup { env, chain_a, chain_b }
633
+ }
634
+
635
+ /// DVN configuration mode for endpoint wiring.
636
+ #[derive(Clone, Copy)]
637
+ pub enum DvnMode {
638
+ /// Single DVN: required = [dvn], no optional
639
+ Single,
640
+ /// Two required DVNs: required = [dvn, dvn2], no optional
641
+ TwoRequired,
642
+ /// Duplicate DVN in optional: required = [dvn], optional = [dvn, dvn2], threshold = 1
643
+ DuplicateOptional,
644
+ }
645
+
646
+ /// Creates a fully wired test setup with the specified DVN mode.
647
+ pub fn wired_setup_with_dvn_mode<'a>(mode: DvnMode) -> TestSetup<'a> {
648
+ let test_setup = setup();
649
+ wire_endpoint(&test_setup.env, &[&test_setup.chain_a, &test_setup.chain_b], mode);
650
+ wire_counter(&test_setup.env, &[&test_setup.chain_a, &test_setup.chain_b]);
651
+ test_setup
652
+ }
653
+
654
+ // ============================================================================
655
+ // Wire functions
656
+ // ============================================================================
657
+
658
+ pub fn wire_endpoint(env: &Env, chains: &[&ChainSetup<'_>], mode: DvnMode) {
659
+ // First pass: Set up price feeds with cross-chain prices
660
+ for chain in chains {
661
+ for other_chain in chains {
662
+ if chain.endpoint.address == other_chain.endpoint.address {
663
+ continue;
664
+ }
665
+ setup_price_feed(env, &chain.owner, &chain.price_feed, other_chain.eid);
666
+ }
667
+ }
668
+
669
+ // Second pass: Set up DVN and Executor dst configs
670
+ for chain in chains {
671
+ for other_chain in chains {
672
+ if chain.endpoint.address == other_chain.endpoint.address {
673
+ continue;
674
+ }
675
+ set_dvn_dst_config(env, &chain.admin, &chain.dvn, other_chain.eid);
676
+ // For multi-DVN modes, also configure dvn2
677
+ if matches!(mode, DvnMode::TwoRequired | DvnMode::DuplicateOptional) {
678
+ set_dvn_dst_config(env, &chain.admin, &chain.dvn2, other_chain.eid);
679
+ }
680
+ set_executor_dst_config(env, &chain.admin, &chain.executor, other_chain.eid);
681
+ }
682
+ }
683
+
684
+ // Third pass: Set up ULN configs based on DVN mode
685
+ for chain in chains {
686
+ for other_chain in chains {
687
+ if chain.endpoint.address == other_chain.endpoint.address {
688
+ continue;
689
+ }
690
+
691
+ let (send_required, recv_required, send_optional, recv_optional, optional_threshold) = match mode {
692
+ DvnMode::Single => (
693
+ vec![env, other_chain.dvn.address.clone()],
694
+ vec![env, chain.dvn.address.clone()],
695
+ vec![env],
696
+ vec![env],
697
+ 0,
698
+ ),
699
+ DvnMode::TwoRequired => (
700
+ vec![env, other_chain.dvn.address.clone(), other_chain.dvn2.address.clone()],
701
+ vec![env, chain.dvn.address.clone(), chain.dvn2.address.clone()],
702
+ vec![env],
703
+ vec![env],
704
+ 0,
705
+ ),
706
+ DvnMode::DuplicateOptional => (
707
+ vec![env, other_chain.dvn.address.clone()],
708
+ vec![env, chain.dvn.address.clone()],
709
+ vec![env, other_chain.dvn.address.clone(), other_chain.dvn2.address.clone()],
710
+ vec![env, chain.dvn.address.clone(), chain.dvn2.address.clone()],
711
+ 1,
712
+ ),
713
+ };
714
+
715
+ set_default_send_uln_config_with_optional(
716
+ env,
717
+ &chain.owner,
718
+ &chain.uln302,
719
+ other_chain.eid,
720
+ &send_required,
721
+ &send_optional,
722
+ optional_threshold,
723
+ );
724
+
725
+ set_default_receive_uln_config_with_optional(
726
+ env,
727
+ &chain.owner,
728
+ &chain.uln302,
729
+ other_chain.eid,
730
+ &recv_required,
731
+ &recv_optional,
732
+ optional_threshold,
733
+ );
734
+
735
+ set_default_executor_config(env, &chain.owner, &chain.uln302, other_chain.eid, &chain.executor.address);
736
+ }
737
+ }
738
+
739
+ // Fourth pass: Set default send and receive libraries
740
+ for chain in chains {
741
+ for other_chain in chains {
742
+ if chain.endpoint.address == other_chain.endpoint.address {
743
+ continue;
744
+ }
745
+
746
+ set_default_send_library(env, &chain.owner, &chain.endpoint, other_chain.eid, &chain.uln302.address);
747
+ set_default_receive_library(env, &chain.owner, &chain.endpoint, other_chain.eid, &chain.uln302.address);
748
+ }
749
+ }
750
+ }
751
+
752
+ /// Set DVN destination config
753
+ fn set_dvn_dst_config(env: &Env, admin: &Address, dvn: &DVNClient<'_>, dst_eid: u32) {
754
+ let config = DvnDstConfig {
755
+ gas: 100_000, // Gas for verification
756
+ multiplier_bps: 10000, // 100%
757
+ floor_margin_usd: 0, // No floor margin for tests
758
+ };
759
+ let params = vec![env, DvnDstConfigParam { dst_eid, config }];
760
+
761
+ env.mock_auths(&[MockAuth {
762
+ address: admin,
763
+ invoke: &MockAuthInvoke {
764
+ contract: &dvn.address,
765
+ fn_name: "set_dst_config",
766
+ args: (admin, &params).into_val(env),
767
+ sub_invokes: &[],
768
+ },
769
+ }]);
770
+ dvn.set_dst_config(admin, &params);
771
+ }
772
+
773
+ /// Set Executor destination config
774
+ fn set_executor_dst_config(env: &Env, admin: &Address, executor: &ExecutorClient<'_>, dst_eid: u32) {
775
+ let config = ExecutorDstConfig {
776
+ lz_receive_base_gas: 50_000, // Base gas for lz_receive
777
+ multiplier_bps: 10000, // 100%
778
+ floor_margin_usd: 0, // No floor margin for tests
779
+ native_cap: 1_000_000_000, // Max native drop
780
+ lz_compose_base_gas: 30_000, // Base gas for lz_compose
781
+ };
782
+ let params = vec![env, SetDstConfigParam { dst_eid, dst_config: config }];
783
+
784
+ env.mock_auths(&[MockAuth {
785
+ address: admin,
786
+ invoke: &MockAuthInvoke {
787
+ contract: &executor.address,
788
+ fn_name: "set_dst_config",
789
+ args: (admin, &params).into_val(env),
790
+ sub_invokes: &[],
791
+ },
792
+ }]);
793
+ executor.set_dst_config(admin, &params);
794
+ }
795
+
796
+ pub fn wire_counter(env: &Env, chains: &[&ChainSetup<'_>]) {
797
+ for chain in chains {
798
+ for other_chain in chains {
799
+ if chain.endpoint.address == other_chain.endpoint.address {
800
+ continue;
801
+ }
802
+ set_peer(
803
+ env,
804
+ &chain.owner,
805
+ &chain.counter,
806
+ other_chain.eid,
807
+ &address_to_bytes32(&other_chain.counter.address),
808
+ );
809
+ }
810
+ }
811
+ }
812
+
813
+ pub fn set_default_send_library(
814
+ env: &Env,
815
+ owner: &Address,
816
+ endpoint: &EndpointV2Client<'_>,
817
+ dst_eid: u32,
818
+ lib: &Address,
819
+ ) {
820
+ env.mock_auths(&[MockAuth {
821
+ address: owner,
822
+ invoke: &MockAuthInvoke {
823
+ contract: &endpoint.address,
824
+ fn_name: "set_default_send_library",
825
+ args: (&dst_eid, lib).into_val(env),
826
+ sub_invokes: &[],
827
+ },
828
+ }]);
829
+ endpoint.set_default_send_library(&dst_eid, lib);
830
+ }
831
+
832
+ pub fn set_default_receive_library(
833
+ env: &Env,
834
+ owner: &Address,
835
+ endpoint: &EndpointV2Client<'_>,
836
+ src_eid: u32,
837
+ lib: &Address,
838
+ ) {
839
+ env.mock_auths(&[MockAuth {
840
+ address: owner,
841
+ invoke: &MockAuthInvoke {
842
+ contract: &endpoint.address,
843
+ fn_name: "set_default_receive_library",
844
+ args: (&src_eid, lib, &0u64).into_val(env),
845
+ sub_invokes: &[],
846
+ },
847
+ }]);
848
+ endpoint.set_default_receive_library(&src_eid, lib, &0u64);
849
+ }
850
+
851
+ pub fn set_default_send_uln_config_with_optional(
852
+ env: &Env,
853
+ owner: &Address,
854
+ uln302: &Uln302Client<'_>,
855
+ dst_eid: u32,
856
+ required_dvns: &soroban_sdk::Vec<Address>,
857
+ optional_dvns: &soroban_sdk::Vec<Address>,
858
+ optional_dvn_threshold: u32,
859
+ ) {
860
+ let config = UlnConfig {
861
+ confirmations: CONFIRMATIONS,
862
+ required_dvns: required_dvns.clone(),
863
+ optional_dvns: optional_dvns.clone(),
864
+ optional_dvn_threshold,
865
+ };
866
+ let params = vec![env, SetDefaultUlnConfigParam { eid: dst_eid, config }];
867
+
868
+ env.mock_auths(&[MockAuth {
869
+ address: owner,
870
+ invoke: &MockAuthInvoke {
871
+ contract: &uln302.address,
872
+ fn_name: "set_default_send_uln_configs",
873
+ args: (&params,).into_val(env),
874
+ sub_invokes: &[],
875
+ },
876
+ }]);
877
+ SendUln302Client::new(env, &uln302.address).set_default_send_uln_configs(&params);
878
+ }
879
+
880
+ pub fn set_default_receive_uln_config_with_optional(
881
+ env: &Env,
882
+ owner: &Address,
883
+ uln302: &Uln302Client<'_>,
884
+ src_eid: u32,
885
+ required_dvns: &soroban_sdk::Vec<Address>,
886
+ optional_dvns: &soroban_sdk::Vec<Address>,
887
+ optional_dvn_threshold: u32,
888
+ ) {
889
+ let config = UlnConfig {
890
+ confirmations: CONFIRMATIONS,
891
+ required_dvns: required_dvns.clone(),
892
+ optional_dvns: optional_dvns.clone(),
893
+ optional_dvn_threshold,
894
+ };
895
+ let params = vec![env, SetDefaultUlnConfigParam { eid: src_eid, config }];
896
+
897
+ env.mock_auths(&[MockAuth {
898
+ address: owner,
899
+ invoke: &MockAuthInvoke {
900
+ contract: &uln302.address,
901
+ fn_name: "set_default_receive_uln_configs",
902
+ args: (&params,).into_val(env),
903
+ sub_invokes: &[],
904
+ },
905
+ }]);
906
+ ReceiveUln302Client::new(env, &uln302.address).set_default_receive_uln_configs(&params);
907
+ }
908
+
909
+ pub fn set_default_executor_config(
910
+ env: &Env,
911
+ owner: &Address,
912
+ uln302: &Uln302Client<'_>,
913
+ dst_eid: u32,
914
+ executor: &Address,
915
+ ) {
916
+ let config = ExecutorConfig { max_message_size: MAX_MESSAGE_SIZE, executor: executor.clone() };
917
+ let params = vec![env, SetDefaultExecutorConfigParam { dst_eid, config }];
918
+
919
+ env.mock_auths(&[MockAuth {
920
+ address: owner,
921
+ invoke: &MockAuthInvoke {
922
+ contract: &uln302.address,
923
+ fn_name: "set_default_executor_configs",
924
+ args: (&params,).into_val(env),
925
+ sub_invokes: &[],
926
+ },
927
+ }]);
928
+ SendUln302Client::new(env, &uln302.address).set_default_executor_configs(&params);
929
+ }
930
+
931
+ // ============================================================================
932
+ // Executor Helper Functions
933
+ // ============================================================================
934
+
935
+ use crate::tests::mint_to;
936
+ use endpoint_v2::{Origin, OutboundPacket};
937
+ use executor_helper::{ComposeParams, ExecutionParams};
938
+ use soroban_sdk::Bytes;
939
+
940
+ /// Execute lz_receive via ExecutorHelper (executor AA workflow).
941
+ /// Automatically mints native tokens to the admin if value > 0.
942
+ pub fn lz_receive_via_executor(
943
+ env: &Env,
944
+ chain: &ChainSetup<'_>,
945
+ admin: &Address,
946
+ packet: &OutboundPacket,
947
+ value: i128,
948
+ ) {
949
+ // Mint native tokens to admin if value > 0
950
+ if value > 0 {
951
+ mint_to(env, &chain.owner, &chain.native_token, admin, value);
952
+ }
953
+
954
+ let origin = Origin { src_eid: packet.src_eid, sender: address_to_bytes32(&packet.sender), nonce: packet.nonce };
955
+
956
+ let params = ExecutionParams {
957
+ receiver: chain.counter.address.clone(),
958
+ origin: origin.clone(),
959
+ guid: packet.guid.clone(),
960
+ message: packet.message.clone(),
961
+ extra_data: Bytes::new(env),
962
+ value,
963
+ gas_limit: 1_000_000, // Arbitrary gas limit for tests
964
+ };
965
+
966
+ env.mock_all_auths_allowing_non_root_auth();
967
+ chain.executor_helper.execute(&chain.executor.address, &params, admin);
968
+ }
969
+
970
+ /// Execute lz_compose via ExecutorHelper (executor AA workflow).
971
+ /// Automatically mints native tokens to the admin if value > 0.
972
+ pub fn lz_compose_via_executor(
973
+ env: &Env,
974
+ chain: &ChainSetup<'_>,
975
+ admin: &Address,
976
+ packet: &OutboundPacket,
977
+ value: i128,
978
+ ) {
979
+ // Mint native tokens to admin if value > 0
980
+ if value > 0 {
981
+ mint_to(env, &chain.owner, &chain.native_token, admin, value);
982
+ }
983
+
984
+ let params = ComposeParams {
985
+ from: chain.counter.address.clone(),
986
+ to: chain.counter.address.clone(),
987
+ guid: packet.guid.clone(),
988
+ index: 0,
989
+ message: packet.message.clone(),
990
+ extra_data: Bytes::new(env),
991
+ value,
992
+ gas_limit: 1_000_000, // Arbitrary gas limit for tests
993
+ };
994
+
995
+ env.mock_all_auths_allowing_non_root_auth();
996
+ chain.executor_helper.compose(&chain.executor.address, &params, admin);
997
+ }