@layerzerolabs/protocol-stellar-v2 0.2.8 → 0.2.10

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 (239) hide show
  1. package/.turbo/turbo-build.log +443 -302
  2. package/.turbo/turbo-lint.log +118 -96
  3. package/.turbo/turbo-test.log +853 -731
  4. package/Cargo.lock +120 -37
  5. package/Cargo.toml +8 -5
  6. package/contracts/common-macros/src/contract_impl.rs +44 -0
  7. package/contracts/common-macros/src/lib.rs +86 -40
  8. package/contracts/common-macros/src/ownable.rs +24 -32
  9. package/contracts/common-macros/src/storage.rs +95 -120
  10. package/contracts/common-macros/src/tests/contract_impl.rs +289 -0
  11. package/contracts/common-macros/src/tests/mod.rs +9 -0
  12. package/contracts/common-macros/src/tests/ownable.rs +151 -0
  13. package/contracts/common-macros/src/tests/snapshots/common_macros__tests__contract_impl__snapshot_generated_contract_impl_code.snap +85 -0
  14. package/contracts/common-macros/src/tests/snapshots/common_macros__tests__ownable__snapshot_generated_ownable_code.snap +30 -0
  15. package/contracts/common-macros/src/tests/snapshots/common_macros__tests__ownable__snapshot_only_owner_preserves_function_signature.snap +9 -0
  16. package/contracts/common-macros/src/tests/snapshots/common_macros__tests__storage__snapshot_generated_storage_code.snap +1072 -0
  17. package/contracts/common-macros/src/tests/snapshots/common_macros__tests__ttl_configurable__snapshot_generated_ttl_configurable_code.snap +45 -0
  18. package/contracts/common-macros/src/tests/storage.rs +485 -0
  19. package/contracts/common-macros/src/tests/test_helpers.rs +93 -0
  20. package/contracts/common-macros/src/tests/ttl_configurable.rs +34 -0
  21. package/contracts/common-macros/src/ttl_configurable.rs +31 -14
  22. package/contracts/common-macros/src/utils.rs +27 -0
  23. package/contracts/endpoint-v2/ARCHITECTURE.md +4 -4
  24. package/contracts/endpoint-v2/src/endpoint_v2.rs +18 -15
  25. package/contracts/endpoint-v2/src/interfaces/message_lib.rs +2 -3
  26. package/contracts/endpoint-v2/src/interfaces/message_lib_manager.rs +5 -3
  27. package/contracts/endpoint-v2/src/interfaces/messaging_channel.rs +2 -2
  28. package/contracts/endpoint-v2/src/interfaces/messaging_composer.rs +2 -2
  29. package/contracts/endpoint-v2/src/interfaces/send_lib.rs +4 -4
  30. package/contracts/endpoint-v2/src/lib.rs +6 -5
  31. package/contracts/endpoint-v2/src/message_lib_manager.rs +14 -6
  32. package/contracts/endpoint-v2/src/messaging_channel.rs +6 -2
  33. package/contracts/endpoint-v2/src/messaging_composer.rs +6 -2
  34. package/contracts/endpoint-v2/src/storage.rs +10 -7
  35. package/contracts/endpoint-v2/src/tests/endpoint_v2/pay_messaging_fees.rs +16 -16
  36. package/contracts/endpoint-v2/src/tests/endpoint_v2/ttl_config.rs +46 -46
  37. package/contracts/endpoint-v2/src/tests/mock.rs +2 -2
  38. package/contracts/endpoint-v2/src/util.rs +8 -2
  39. package/contracts/message-libs/block-message-lib/Cargo.toml +1 -0
  40. package/contracts/message-libs/block-message-lib/src/lib.rs +5 -5
  41. package/contracts/message-libs/message-lib-common/src/errors.rs +8 -8
  42. package/contracts/message-libs/message-lib-common/src/interfaces/dvn.rs +0 -1
  43. package/contracts/message-libs/message-lib-common/src/interfaces/mod.rs +3 -3
  44. package/contracts/message-libs/message-lib-common/src/lib.rs +0 -2
  45. package/contracts/message-libs/message-lib-common/src/packet_codec_v1.rs +4 -6
  46. package/contracts/message-libs/message-lib-common/src/tests/packet_codec_v1.rs +2 -2
  47. package/contracts/message-libs/message-lib-common/src/tests/worker_options.rs +11 -11
  48. package/contracts/message-libs/message-lib-common/src/worker_options.rs +10 -16
  49. package/contracts/message-libs/simple-message-lib/src/errors.rs +0 -4
  50. package/contracts/message-libs/simple-message-lib/src/simple_message_lib.rs +49 -34
  51. package/contracts/message-libs/simple-message-lib/src/storage.rs +3 -7
  52. package/contracts/message-libs/simple-message-lib/src/test.rs +3 -3
  53. package/contracts/message-libs/treasury/src/storage.rs +1 -2
  54. package/contracts/message-libs/treasury/src/tests/setup.rs +3 -2
  55. package/contracts/message-libs/treasury/src/tests/treasury_tests.rs +0 -13
  56. package/contracts/message-libs/treasury/src/treasury.rs +18 -21
  57. package/contracts/message-libs/uln-302/Cargo.toml +1 -0
  58. package/contracts/message-libs/uln-302/src/interfaces/mod.rs +4 -4
  59. package/contracts/message-libs/uln-302/src/interfaces/{receive.rs → receive_uln.rs} +3 -3
  60. package/contracts/message-libs/uln-302/src/interfaces/{send.rs → send_uln.rs} +8 -80
  61. package/contracts/message-libs/uln-302/src/lib.rs +5 -4
  62. package/contracts/message-libs/uln-302/src/{receive.rs → receive_uln.rs} +20 -12
  63. package/contracts/message-libs/uln-302/src/{send.rs → send_uln.rs} +19 -13
  64. package/contracts/message-libs/uln-302/src/storage.rs +1 -2
  65. package/contracts/message-libs/uln-302/src/tests/config/uln_config.rs +3 -2
  66. package/contracts/message-libs/uln-302/src/tests/send_uln302/send.rs +30 -30
  67. package/contracts/message-libs/uln-302/src/tests/setup.rs +12 -11
  68. package/contracts/message-libs/uln-302/src/tests/uln302/set_config.rs +1 -1
  69. package/contracts/message-libs/uln-302/src/{config_validation.rs → types.rs} +79 -11
  70. package/contracts/message-libs/uln-302/src/uln302.rs +15 -10
  71. package/contracts/oapp-macros/Cargo.toml +2 -8
  72. package/contracts/oapp-macros/src/lib.rs +57 -311
  73. package/contracts/oapp-macros/src/oapp_core.rs +23 -32
  74. package/contracts/oapp-macros/src/oapp_full.rs +8 -2
  75. package/contracts/oapp-macros/src/oapp_options_type3.rs +21 -36
  76. package/contracts/oapp-macros/src/oapp_receiver.rs +38 -57
  77. package/contracts/oapp-macros/src/oapp_sender.rs +12 -14
  78. package/contracts/oapp-macros/src/util.rs +14 -10
  79. package/contracts/oapps/counter/Cargo.toml +2 -1
  80. package/contracts/oapps/counter/integration_tests/utils.rs +4 -4
  81. package/contracts/oapps/counter/src/codec.rs +8 -9
  82. package/contracts/oapps/counter/src/counter.rs +156 -147
  83. package/contracts/oapps/counter/src/storage.rs +1 -2
  84. package/contracts/oapps/counter/src/tests/test_codec.rs +5 -5
  85. package/contracts/oapps/counter/src/tests/test_counter.rs +11 -13
  86. package/contracts/oapps/oapp/Cargo.toml +1 -0
  87. package/contracts/oapps/oapp/src/errors.rs +1 -1
  88. package/contracts/oapps/oapp/src/lib.rs +3 -0
  89. package/contracts/oapps/oapp/src/macro_tests/mod.rs +1 -0
  90. package/contracts/oapps/oapp/src/macro_tests/test_macros.rs +312 -0
  91. package/contracts/oapps/oapp/src/oapp_core.rs +52 -53
  92. package/contracts/oapps/oapp/src/oapp_options_type3.rs +18 -28
  93. package/contracts/oapps/oapp/src/oapp_receiver.rs +82 -31
  94. package/contracts/oapps/oapp/src/oapp_sender.rs +55 -13
  95. package/contracts/oapps/oapp/src/tests/test_oapp_core.rs +16 -3
  96. package/contracts/oapps/oapp/src/tests/test_oapp_options_type3.rs +33 -8
  97. package/contracts/oapps/oapp/src/tests/test_oapp_receiver.rs +6 -9
  98. package/contracts/oapps/oapp/src/tests/test_oapp_sender.rs +28 -15
  99. package/contracts/oapps/oft/Cargo.toml +27 -0
  100. package/contracts/oapps/oft/integration-tests/mod.rs +3 -0
  101. package/contracts/oapps/oft/integration-tests/setup.rs +320 -0
  102. package/contracts/oapps/oft/integration-tests/test_with_sml.rs +155 -0
  103. package/contracts/oapps/oft/integration-tests/utils.rs +201 -0
  104. package/contracts/oapps/oft/src/codec/mod.rs +2 -0
  105. package/contracts/oapps/oft/src/codec/oft_compose_msg_codec.rs +55 -0
  106. package/contracts/oapps/oft/src/codec/oft_msg_codec.rs +62 -0
  107. package/contracts/oapps/oft/src/constants.rs +5 -0
  108. package/contracts/oapps/oft/src/errors.rs +8 -0
  109. package/contracts/oapps/oft/src/events.rs +19 -0
  110. package/contracts/oapps/oft/src/interfaces/mint_burn_token.rs +23 -0
  111. package/contracts/oapps/oft/src/interfaces/mod.rs +3 -0
  112. package/contracts/oapps/oft/src/lib.rs +22 -0
  113. package/contracts/oapps/oft/src/macro_tests/mod.rs +2 -0
  114. package/contracts/oapps/oft/src/macro_tests/test_all_default.rs +41 -0
  115. package/contracts/oapps/oft/src/macro_tests/test_override.rs +83 -0
  116. package/contracts/oapps/oft/src/oft.rs +320 -0
  117. package/contracts/oapps/oft/src/oft_types/lock_unlock.rs +50 -0
  118. package/contracts/oapps/oft/src/oft_types/mint_burn.rs +50 -0
  119. package/contracts/oapps/oft/src/oft_types/mod.rs +10 -0
  120. package/contracts/oapps/oft/src/storage.rs +11 -0
  121. package/contracts/oapps/oft/src/tests/mod.rs +13 -0
  122. package/contracts/oapps/oft/src/tests/test_decimals.rs +89 -0
  123. package/contracts/oapps/oft/src/tests/test_lz_receive.rs +282 -0
  124. package/contracts/oapps/oft/src/tests/test_oft_compose_msg_codec.rs +68 -0
  125. package/contracts/oapps/oft/src/tests/test_oft_msg_codec.rs +136 -0
  126. package/contracts/oapps/oft/src/tests/test_oft_version.rs +13 -0
  127. package/contracts/oapps/oft/src/tests/test_quote_oft.rs +159 -0
  128. package/contracts/oapps/oft/src/tests/test_quote_send.rs +195 -0
  129. package/contracts/oapps/oft/src/tests/test_resolve_address.rs +37 -0
  130. package/contracts/oapps/oft/src/tests/test_send.rs +915 -0
  131. package/contracts/oapps/oft/src/tests/test_token.rs +47 -0
  132. package/contracts/oapps/oft/src/tests/test_utils.rs +789 -0
  133. package/contracts/oapps/oft/src/types.rs +38 -0
  134. package/contracts/oapps/oft/src/utils.rs +67 -0
  135. package/contracts/oapps/oft-mint-burn/Cargo.toml +26 -0
  136. package/contracts/oapps/oft-mint-burn/src/lib.rs +3 -0
  137. package/contracts/oapps/oft-mint-burn/src/oft.rs +28 -0
  138. package/contracts/oapps/oft-mint-burn/src/tests/mod.rs +1 -0
  139. package/contracts/utils/src/buffer_reader.rs +8 -9
  140. package/contracts/utils/src/buffer_writer.rs +11 -5
  141. package/contracts/utils/src/errors.rs +5 -5
  142. package/contracts/utils/src/ownable.rs +14 -6
  143. package/contracts/utils/src/testing_utils.rs +11 -1
  144. package/contracts/utils/src/tests/buffer_reader.rs +491 -730
  145. package/contracts/utils/src/tests/buffer_writer.rs +336 -148
  146. package/contracts/utils/src/tests/bytes_ext.rs +125 -40
  147. package/contracts/utils/src/tests/mod.rs +3 -0
  148. package/contracts/utils/src/tests/ownable.rs +379 -27
  149. package/contracts/utils/src/tests/test_helper.rs +47 -0
  150. package/contracts/utils/src/tests/testing_utils.rs +555 -0
  151. package/contracts/utils/src/tests/ttl.rs +421 -0
  152. package/contracts/utils/src/ttl.rs +29 -89
  153. package/contracts/workers/dvn/Cargo.toml +31 -0
  154. package/contracts/workers/dvn/src/auth.rs +66 -0
  155. package/contracts/workers/dvn/src/dvn.rs +143 -0
  156. package/contracts/workers/dvn/src/errors.rs +21 -0
  157. package/contracts/workers/dvn/src/events.rs +19 -0
  158. package/contracts/workers/dvn/src/interfaces/dvn.rs +12 -0
  159. package/contracts/workers/dvn/src/interfaces/mod.rs +5 -0
  160. package/contracts/workers/dvn/src/interfaces/multisig.rs +15 -0
  161. package/contracts/workers/dvn/src/lib.rs +24 -0
  162. package/contracts/workers/dvn/src/multisig.rs +127 -0
  163. package/contracts/workers/dvn/src/storage.rs +35 -0
  164. package/contracts/workers/dvn/src/tests/auth.rs +237 -0
  165. package/contracts/workers/dvn/src/tests/dvn.rs +349 -0
  166. package/contracts/workers/dvn/src/tests/key_pair.rs +66 -0
  167. package/contracts/workers/dvn/src/tests/mod.rs +5 -0
  168. package/contracts/workers/dvn/src/tests/multisig/mod.rs +3 -0
  169. package/contracts/workers/dvn/src/tests/multisig/set_signer.rs +133 -0
  170. package/contracts/workers/dvn/src/tests/multisig/set_threshold.rs +108 -0
  171. package/contracts/workers/dvn/src/tests/multisig/verify_signatures.rs +109 -0
  172. package/contracts/workers/dvn/src/tests/setup.rs +109 -0
  173. package/contracts/workers/dvn/src/types.rs +26 -0
  174. package/contracts/workers/dvn-fee-lib/Cargo.toml +24 -0
  175. package/contracts/workers/dvn-fee-lib/src/dvn_fee_lib.rs +113 -0
  176. package/contracts/workers/dvn-fee-lib/src/errors.rs +8 -0
  177. package/contracts/workers/dvn-fee-lib/src/lib.rs +17 -0
  178. package/contracts/workers/dvn-fee-lib/src/tests/dvn_fee_lib.rs +282 -0
  179. package/contracts/workers/dvn-fee-lib/src/tests/mod.rs +1 -0
  180. package/contracts/workers/executor/Cargo.toml +10 -7
  181. package/contracts/workers/executor/src/errors.rs +8 -0
  182. package/contracts/workers/executor/src/events.rs +4 -7
  183. package/contracts/workers/executor/src/interfaces/executor.rs +72 -22
  184. package/contracts/workers/executor/src/interfaces/mod.rs +0 -2
  185. package/contracts/workers/executor/src/lib.rs +16 -7
  186. package/contracts/workers/executor/src/lz_executor.rs +308 -0
  187. package/contracts/workers/executor/src/storage.rs +24 -16
  188. package/contracts/workers/executor-fee-lib/Cargo.toml +22 -0
  189. package/contracts/workers/executor-fee-lib/src/errors.rs +15 -0
  190. package/contracts/workers/executor-fee-lib/src/executor_fee_lib.rs +215 -0
  191. package/contracts/workers/executor-fee-lib/src/executor_option.rs +203 -0
  192. package/contracts/workers/executor-fee-lib/src/lib.rs +7 -0
  193. package/contracts/workers/executor-helper/Cargo.toml +29 -0
  194. package/contracts/workers/executor-helper/src/executor_helper.rs +161 -0
  195. package/contracts/workers/executor-helper/src/lib.rs +11 -0
  196. package/contracts/workers/{worker-common → worker}/Cargo.toml +1 -4
  197. package/contracts/workers/worker/src/errors.rs +24 -0
  198. package/contracts/workers/worker/src/events.rs +62 -0
  199. package/contracts/workers/worker/src/interfaces/dvn_fee_lib.rs +75 -0
  200. package/contracts/workers/worker/src/interfaces/executor_fee_lib.rs +84 -0
  201. package/contracts/workers/{worker-common → worker}/src/interfaces/mod.rs +2 -2
  202. package/contracts/workers/worker/src/interfaces/price_feed.rs +85 -0
  203. package/contracts/workers/worker/src/lib.rs +14 -0
  204. package/contracts/workers/worker/src/storage.rs +63 -0
  205. package/contracts/workers/worker/src/worker.rs +459 -0
  206. package/package.json +3 -3
  207. package/sdk/dist/generated/bml.d.ts +88 -17
  208. package/sdk/dist/generated/bml.js +62 -16
  209. package/sdk/dist/generated/counter.d.ts +281 -102
  210. package/sdk/dist/generated/counter.js +93 -41
  211. package/sdk/dist/generated/endpoint.d.ts +128 -105
  212. package/sdk/dist/generated/endpoint.js +47 -45
  213. package/sdk/dist/generated/sml.d.ts +212 -69
  214. package/sdk/dist/generated/sml.js +103 -53
  215. package/sdk/dist/generated/uln302.d.ts +270 -173
  216. package/sdk/dist/generated/uln302.js +112 -64
  217. package/sdk/package.json +11 -11
  218. package/sdk/test/index.test.ts +147 -42
  219. package/sdk/test/suites/constants.ts +7 -3
  220. package/sdk/test/suites/deploy.ts +65 -42
  221. package/sdk/test/suites/localnet.ts +2 -2
  222. package/sdk/test/suites/scan.ts +28 -25
  223. package/sdk/test/utils.ts +199 -0
  224. package/sdk/tsconfig.json +93 -95
  225. package/tools/ts-bindings-gen/src/main.rs +2 -0
  226. package/contracts/common-macros/src/snapshots/common_macros__tests__tests__snapshot_generated_storage_code.snap +0 -310
  227. package/contracts/common-macros/src/tests.rs +0 -287
  228. package/contracts/oapp-macros/tests/test_macros.rs +0 -522
  229. package/contracts/workers/executor/src/executor.rs +0 -347
  230. package/contracts/workers/executor/src/interfaces/types.rs +0 -51
  231. package/contracts/workers/worker-common/src/constants.rs +0 -17
  232. package/contracts/workers/worker-common/src/errors.rs +0 -6
  233. package/contracts/workers/worker-common/src/events.rs +0 -34
  234. package/contracts/workers/worker-common/src/interfaces/executor_fee_lib.rs +0 -35
  235. package/contracts/workers/worker-common/src/interfaces/price_feed.rs +0 -40
  236. package/contracts/workers/worker-common/src/interfaces/worker.rs +0 -60
  237. package/contracts/workers/worker-common/src/lib.rs +0 -19
  238. package/contracts/workers/worker-common/src/storage.rs +0 -32
  239. package/contracts/workers/worker-common/src/worker_common.rs +0 -166
@@ -0,0 +1,215 @@
1
+ use crate::{errors::ExecutorFeeLibError, executor_option};
2
+ use common_macros::contract_impl;
3
+ use soroban_sdk::{assert_with_error, contract, Address, Bytes, Env};
4
+ use worker::{FeeParams, IExecutorFeeLib, LayerZeroPriceFeedClient};
5
+
6
+ // ============================================================================
7
+ // Constants
8
+ // ============================================================================
9
+
10
+ /// V1 endpoint ID threshold. EIDs below this are V1 endpoints with restrictions.
11
+ const V1_EID_THRESHOLD: u32 = 30000;
12
+
13
+ /// Basis points denominator (10000 = 100%).
14
+ const BPS_DENOMINATOR: i128 = 10000;
15
+
16
+ /// Overhead percentage for ordered execution (102 = 2% overhead).
17
+ const ORDERED_EXECUTION_OVERHEAD_PERCENT: u128 = 102;
18
+
19
+ /// Native token decimal rate for XLM (10^7 stroops per XLM).
20
+ const NATIVE_DECIMALS_RATE: u128 = 10_000_000;
21
+
22
+ /// ExecutorFeeLib contract for calculating executor fees.
23
+ ///
24
+ /// Provides fee calculation logic based on executor options, destination configuration,
25
+ /// and current gas prices from the price feed. Handles fee multipliers, margins, and
26
+ /// native token value conversions.
27
+ #[contract]
28
+ pub struct ExecutorFeeLib;
29
+
30
+ #[contract_impl]
31
+ impl ExecutorFeeLib {
32
+ // ========================================================================
33
+ // Internal Functions
34
+ // ========================================================================
35
+
36
+ /// Decodes executor options and calculates total gas and native value.
37
+ ///
38
+ /// Parses encoded executor options, accumulates gas requirements (including
39
+ /// compose calls and ordered execution overhead), and returns the total native
40
+ /// value and gas needed.
41
+ ///
42
+ /// # Arguments
43
+ /// * `options` - Encoded executor options bytes
44
+ /// * `dst_eid` - Destination endpoint ID
45
+ /// * `lz_receive_base_gas` - Base gas for lzReceive execution
46
+ /// * `lz_compose_base_gas` - Base gas per lzCompose call
47
+ /// * `native_cap` - Maximum allowed native token value
48
+ ///
49
+ /// # Returns
50
+ /// Tuple of (total_native_value, total_gas).
51
+ fn decode_executor_options(
52
+ env: &Env,
53
+ options: &Bytes,
54
+ dst_eid: u32,
55
+ lz_receive_base_gas: u64,
56
+ lz_compose_base_gas: u64,
57
+ native_cap: u128,
58
+ ) -> (u128, u128) {
59
+ let options_agg = executor_option::parse_executor_options(env, options, Self::is_v1_eid(dst_eid), native_cap);
60
+
61
+ let mut total_gas = (lz_receive_base_gas as u128)
62
+ + options_agg.total_gas
63
+ + ((lz_compose_base_gas as u128) * (options_agg.num_lz_compose as u128));
64
+
65
+ if options_agg.ordered {
66
+ total_gas = (total_gas * ORDERED_EXECUTION_OVERHEAD_PERCENT) / 100;
67
+ }
68
+
69
+ (options_agg.total_value, total_gas)
70
+ }
71
+
72
+ /// Returns the effective multiplier in basis points.
73
+ ///
74
+ /// Uses destination-specific multiplier if set, otherwise falls back to default multiplier.
75
+ ///
76
+ /// # Arguments
77
+ /// * `params` - Fee parameters containing multiplier settings
78
+ ///
79
+ /// # Returns
80
+ /// Effective multiplier in basis points.
81
+ fn get_effective_multiplier_bps(params: &FeeParams) -> u32 {
82
+ if params.multiplier_bps == 0 {
83
+ params.default_multiplier_bps
84
+ } else {
85
+ params.multiplier_bps
86
+ }
87
+ }
88
+
89
+ /// Applies premium (multiplier and margin) to gas fee.
90
+ ///
91
+ /// Calculates fee with multiplier and floor margin, returning the maximum of both
92
+ /// to ensure profitability.
93
+ ///
94
+ /// # Arguments
95
+ /// * `fee` - Base gas fee
96
+ /// * `multiplier_bps` - Fee multiplier in basis points
97
+ /// * `margin_usd` - Minimum margin in USD (scaled)
98
+ /// * `native_price_usd` - Native token price in USD (scaled)
99
+ ///
100
+ /// # Returns
101
+ /// Fee with premium applied (max of multiplier fee and margin fee).
102
+ fn apply_premium_to_gas(fee: i128, multiplier_bps: u32, margin_usd: u128, native_price_usd: u128) -> i128 {
103
+ let fee_with_multiplier = (fee * multiplier_bps as i128) / BPS_DENOMINATOR;
104
+
105
+ if native_price_usd == 0 || margin_usd == 0 {
106
+ return fee_with_multiplier;
107
+ }
108
+
109
+ let fee_with_margin = ((margin_usd * NATIVE_DECIMALS_RATE) / native_price_usd) as i128 + fee;
110
+
111
+ fee_with_margin.max(fee_with_multiplier)
112
+ }
113
+
114
+ /// Converts native value and applies premium multiplier.
115
+ ///
116
+ /// Converts value using price ratio and applies multiplier in basis points.
117
+ ///
118
+ /// # Arguments
119
+ /// * `value` - Native value to convert
120
+ /// * `ratio` - Price ratio numerator
121
+ /// * `denom` - Price ratio denominator
122
+ /// * `multiplier_bps` - Fee multiplier in basis points
123
+ ///
124
+ /// # Returns
125
+ /// Converted and multiplied value, or 0 if input value is 0.
126
+ fn convert_and_apply_premium_to_value(value: u128, ratio: u128, denom: u128, multiplier_bps: u32) -> i128 {
127
+ if value == 0 {
128
+ return 0;
129
+ }
130
+ let converted = (value * ratio) / denom;
131
+ ((converted * multiplier_bps as u128) / BPS_DENOMINATOR as u128) as i128
132
+ }
133
+
134
+ /// Checks if an endpoint ID is a V1 endpoint.
135
+ ///
136
+ /// V1 endpoints (EID < 30000) have restrictions on executor options.
137
+ ///
138
+ /// # Arguments
139
+ /// * `eid` - Endpoint ID to check
140
+ ///
141
+ /// # Returns
142
+ /// `true` if the endpoint is a V1 endpoint, `false` otherwise.
143
+ fn is_v1_eid(eid: u32) -> bool {
144
+ eid < V1_EID_THRESHOLD
145
+ }
146
+ }
147
+
148
+ // ============================================================================
149
+ // IExecutorFeeLib Implementation
150
+ // ============================================================================
151
+
152
+ #[contract_impl]
153
+ impl IExecutorFeeLib for ExecutorFeeLib {
154
+ /// Calculates the total execution fee for a cross-chain message.
155
+ ///
156
+ /// Decodes executor options, estimates gas fees from the price feed, applies
157
+ /// multipliers and margins, and converts native token values. Returns the
158
+ /// total fee in native tokens.
159
+ ///
160
+ /// # Arguments
161
+ /// * `executor` - Executor contract address (unused, kept for interface compatibility)
162
+ /// * `params` - Fee calculation parameters
163
+ ///
164
+ /// # Returns
165
+ /// Total execution fee in native tokens.
166
+ ///
167
+ /// # Errors
168
+ /// * `EidNotSupported` - If destination endpoint is not supported (lz_receive_base_gas is 0)
169
+ /// * Various executor option parsing errors (see `parse_executor_options`)
170
+ fn get_fee(env: &Env, _executor: &Address, params: &FeeParams) -> i128 {
171
+ assert_with_error!(env, params.lz_receive_base_gas != 0, ExecutorFeeLibError::EidNotSupported);
172
+
173
+ let (total_value, total_gas) = Self::decode_executor_options(
174
+ env,
175
+ &params.options,
176
+ params.dst_eid,
177
+ params.lz_receive_base_gas,
178
+ params.lz_compose_base_gas,
179
+ params.native_cap,
180
+ );
181
+
182
+ let fee_estimate = LayerZeroPriceFeedClient::new(env, &params.price_feed).estimate_fee_by_eid(
183
+ &env.current_contract_address(),
184
+ &params.dst_eid,
185
+ &params.calldata_size,
186
+ &total_gas,
187
+ );
188
+
189
+ let multiplier_bps = Self::get_effective_multiplier_bps(params);
190
+
191
+ let mut fee = Self::apply_premium_to_gas(
192
+ fee_estimate.total_gas_fee,
193
+ multiplier_bps,
194
+ params.floor_margin_usd,
195
+ fee_estimate.native_price_usd,
196
+ );
197
+
198
+ fee += Self::convert_and_apply_premium_to_value(
199
+ total_value,
200
+ fee_estimate.price_ratio,
201
+ fee_estimate.price_ratio_denominator,
202
+ multiplier_bps,
203
+ );
204
+
205
+ fee
206
+ }
207
+
208
+ /// Returns the version of the fee library.
209
+ ///
210
+ /// # Returns
211
+ /// Tuple of (major_version, minor_version).
212
+ fn version(_env: &Env) -> (u64, u32) {
213
+ (1, 1)
214
+ }
215
+ }
@@ -0,0 +1,203 @@
1
+ use crate::errors::ExecutorFeeLibError;
2
+ use message_lib_common::worker_options::{EXECUTOR_OPTION_TYPE_LZRECEIVE, EXECUTOR_OPTION_TYPE_NATIVE_DROP};
3
+ use soroban_sdk::{assert_with_error, panic_with_error, Bytes, BytesN, Env};
4
+ use utils::buffer_reader::BufferReader;
5
+
6
+ pub const EXECUTOR_OPTION_TYPE_LZCOMPOSE: u8 = 3;
7
+ pub const EXECUTOR_OPTION_TYPE_ORDERED_EXECUTION: u8 = 4;
8
+
9
+ /// Aggregated executor options parsed from encoded option bytes.
10
+ ///
11
+ /// Contains the accumulated values from all executor options for fee calculation.
12
+ /// This structure is built by iterating through all encoded options and summing
13
+ /// gas requirements and native token values.
14
+ pub struct ExecutorOptionsAgg {
15
+ /// Total native token value to transfer (from lzReceive value + nativeDrop + lzCompose value).
16
+ pub total_value: u128,
17
+ /// Total gas required (from lzReceive gas + lzCompose gas).
18
+ pub total_gas: u128,
19
+ /// Whether ordered execution is requested (messages must be delivered in sequence).
20
+ pub ordered: bool,
21
+ /// Number of lzCompose calls to execute.
22
+ pub num_lz_compose: u64,
23
+ }
24
+
25
+ // ============================================================================
26
+ // Main Parsing Function
27
+ // ============================================================================
28
+
29
+ /// Parses executor options from encoded bytes and returns aggregated values.
30
+ ///
31
+ /// Iterates through all encoded options, decoding each based on its type and
32
+ /// accumulating gas, value, and other parameters for fee calculation.
33
+ ///
34
+ /// # Arguments
35
+ /// * `options` - Encoded executor options bytes
36
+ /// * `is_v1_eid` - Whether destination is a V1 endpoint (< 30000), which has restrictions
37
+ /// * `native_cap` - Maximum allowed native token value transfer
38
+ ///
39
+ /// # Returns
40
+ /// `ExecutorOptionsAgg` containing accumulated gas, value, compose count, and ordered flag.
41
+ ///
42
+ /// # Errors
43
+ /// * `NoOptions` - If options bytes are empty
44
+ /// * `UnsupportedOptionType` - If an unknown option type is encountered or V1 restrictions violated
45
+ /// * `ZeroLzReceiveGasProvided` - If no lzReceive gas is specified
46
+ /// * `ZeroLzComposeGasProvided` - If lzCompose has zero gas
47
+ /// * `NativeAmountExceedsCap` - If total native value exceeds the cap
48
+ pub fn parse_executor_options(env: &Env, options: &Bytes, is_v1_eid: bool, native_cap: u128) -> ExecutorOptionsAgg {
49
+ // Assert that options are not empty (No executor options provided)
50
+ assert_with_error!(env, !options.is_empty(), ExecutorFeeLibError::NoOptions);
51
+
52
+ let mut reader = BufferReader::new(options);
53
+
54
+ let mut agg_options = ExecutorOptionsAgg { total_value: 0, total_gas: 0, ordered: false, num_lz_compose: 0 };
55
+
56
+ let mut lz_receive_gas: u128 = 0;
57
+
58
+ while reader.remaining_len() > 0 {
59
+ let (option_type, option_data) = next_executor_option(&mut reader);
60
+
61
+ match option_type {
62
+ EXECUTOR_OPTION_TYPE_LZRECEIVE => {
63
+ let (gas, value) = decode_lz_receive_option(env, &option_data);
64
+ // endpoint v1 does not support lzReceive with value
65
+ assert_with_error!(env, !(is_v1_eid && value > 0), ExecutorFeeLibError::UnsupportedOptionType);
66
+ lz_receive_gas += gas;
67
+ agg_options.total_value += value;
68
+ }
69
+ EXECUTOR_OPTION_TYPE_NATIVE_DROP => {
70
+ let (amount, _) = decode_native_drop_option(env, &option_data);
71
+ agg_options.total_value += amount;
72
+ }
73
+ EXECUTOR_OPTION_TYPE_LZCOMPOSE => {
74
+ // endpoint v1 does not support lzCompose
75
+ assert_with_error!(env, !is_v1_eid, ExecutorFeeLibError::UnsupportedOptionType);
76
+ let (_, gas, value) = decode_lz_compose_option(env, &option_data);
77
+ assert_with_error!(env, gas != 0, ExecutorFeeLibError::ZeroLzComposeGasProvided);
78
+ agg_options.total_gas += gas;
79
+ agg_options.total_value += value;
80
+ agg_options.num_lz_compose += 1;
81
+ }
82
+ EXECUTOR_OPTION_TYPE_ORDERED_EXECUTION => {
83
+ assert_with_error!(env, option_data.is_empty(), ExecutorFeeLibError::InvalidExecutorOptions);
84
+ agg_options.ordered = true;
85
+ }
86
+ _ => {
87
+ panic_with_error!(env, ExecutorFeeLibError::UnsupportedOptionType);
88
+ }
89
+ }
90
+ }
91
+
92
+ // Validate
93
+ assert_with_error!(env, agg_options.total_value <= native_cap, ExecutorFeeLibError::NativeAmountExceedsCap);
94
+ assert_with_error!(env, lz_receive_gas != 0, ExecutorFeeLibError::ZeroLzReceiveGasProvided);
95
+
96
+ agg_options.total_gas += lz_receive_gas;
97
+ agg_options
98
+ }
99
+
100
+ // ============================================================================
101
+ // Option Extraction
102
+ // ============================================================================
103
+
104
+ /// Extracts the next executor option from the options byte stream.
105
+ ///
106
+ /// Option format: [worker_id: u8][option_size: u16][option_type: u8][option_data: bytes]
107
+ ///
108
+ /// Parses the binary format to extract the option type and data, skipping the worker_id
109
+ /// which identifies which worker this option is intended for.
110
+ ///
111
+ /// # Arguments
112
+ /// * `reader` - Buffer reader positioned at the start of an option
113
+ ///
114
+ /// # Returns
115
+ /// Tuple of (option_type, option_data) where option_data excludes the option_type byte.
116
+ fn next_executor_option(reader: &mut BufferReader) -> (u8, Bytes) {
117
+ // Skip worker_id (1 byte) - identifies which worker this option is for
118
+ let _worker_id = reader.read_u8();
119
+
120
+ // Read option_size (2 bytes) - includes option_type + option_data
121
+ let option_size = reader.read_u16();
122
+
123
+ // Read option_type (1 byte)
124
+ let option_type = reader.read_u8();
125
+
126
+ // Read option_data (option_size - 1 bytes, since option_size includes option_type)
127
+ let option_data = reader.read_bytes((option_size - 1) as u32);
128
+
129
+ (option_type, option_data)
130
+ }
131
+
132
+ // ============================================================================
133
+ // Option Decoding Functions
134
+ // ============================================================================
135
+
136
+ /// Decodes an lzReceive option.
137
+ ///
138
+ /// Format: [gas: u128] (16 bytes) or [gas: u128][value: u128] (32 bytes)
139
+ ///
140
+ /// # Arguments
141
+ /// * `option` - The option data bytes (without option_type)
142
+ ///
143
+ /// # Returns
144
+ /// Tuple of (gas, value) where value is 0 if not specified.
145
+ ///
146
+ /// # Errors
147
+ /// * `InvalidLzReceiveOption` - If option length is not 16 or 32 bytes.
148
+ fn decode_lz_receive_option(env: &Env, option: &Bytes) -> (u128, u128) {
149
+ let len = option.len();
150
+ assert_with_error!(env, len == 16 || len == 32, ExecutorFeeLibError::InvalidLzReceiveOption);
151
+
152
+ let mut reader = BufferReader::new(option);
153
+ let gas = reader.read_u128();
154
+ let value = if len == 32 { reader.read_u128() } else { 0 };
155
+
156
+ (gas, value)
157
+ }
158
+
159
+ /// Decodes a native drop option.
160
+ ///
161
+ /// Format: [amount: u128][receiver: bytes32] (48 bytes)
162
+ ///
163
+ /// # Arguments
164
+ /// * `option` - The option data bytes (without option_type)
165
+ ///
166
+ /// # Returns
167
+ /// Tuple of (amount, receiver) for the native token transfer.
168
+ ///
169
+ /// # Errors
170
+ /// * `InvalidNativeDropOption` - If option length is not 48 bytes.
171
+ fn decode_native_drop_option(env: &Env, option: &Bytes) -> (u128, BytesN<32>) {
172
+ assert_with_error!(env, option.len() == 48, ExecutorFeeLibError::InvalidNativeDropOption);
173
+
174
+ let mut reader = BufferReader::new(option);
175
+ let amount = reader.read_u128();
176
+ let receiver = reader.read_bytes_n::<32>();
177
+
178
+ (amount, receiver)
179
+ }
180
+
181
+ /// Decodes an lzCompose option.
182
+ ///
183
+ /// Format: [index: u16][gas: u128] (18 bytes) or [index: u16][gas: u128][value: u128] (34 bytes)
184
+ ///
185
+ /// # Arguments
186
+ /// * `option` - The option data bytes (without option_type)
187
+ ///
188
+ /// # Returns
189
+ /// Tuple of (index, gas, value) where value is 0 if not specified.
190
+ ///
191
+ /// # Errors
192
+ /// * `InvalidLzComposeOption` - If option length is not 18 or 34 bytes.
193
+ fn decode_lz_compose_option(env: &Env, option: &Bytes) -> (u16, u128, u128) {
194
+ let len = option.len();
195
+ assert_with_error!(env, len == 18 || len == 34, ExecutorFeeLibError::InvalidLzComposeOption);
196
+
197
+ let mut reader = BufferReader::new(option);
198
+ let index = reader.read_u16();
199
+ let gas = reader.read_u128();
200
+ let value = if len == 34 { reader.read_u128() } else { 0 };
201
+
202
+ (index, gas, value)
203
+ }
@@ -0,0 +1,7 @@
1
+ #![no_std]
2
+
3
+ pub mod errors;
4
+ pub mod executor_fee_lib;
5
+ pub mod executor_option;
6
+
7
+ pub use executor_fee_lib::{ExecutorFeeLib, ExecutorFeeLibClient};
@@ -0,0 +1,29 @@
1
+ [package]
2
+ name = "executor-helper"
3
+ version.workspace = true
4
+ edition.workspace = true
5
+ license.workspace = true
6
+ publish = false
7
+
8
+ [lib]
9
+ crate-type = ["cdylib"]
10
+ doctest = false
11
+
12
+ [features]
13
+ library = []
14
+ testutils = []
15
+
16
+ [dependencies]
17
+ cfg-if = { workspace = true }
18
+ soroban-sdk = { workspace = true }
19
+ # workspace dependencies
20
+ common-macros = { workspace = true }
21
+ utils = { workspace = true }
22
+ worker = { workspace = true }
23
+ endpoint-v2 = { workspace = true, features = ["library"] }
24
+ executor = { workspace = true, features = ["library"] }
25
+
26
+ [dev-dependencies]
27
+ soroban-sdk = { workspace = true, features = ["testutils"] }
28
+
29
+
@@ -0,0 +1,161 @@
1
+ //! ABA-safe entry point for cross-chain message execution on Stellar.
2
+ //! Prevents reentry into the Executor during OApp Atomic Batch Actions.
3
+
4
+ use endpoint_v2::{
5
+ LayerZeroComposerClient, LayerZeroEndpointV2Client, LayerZeroReceiverClient, MessagingComposerClient, Origin,
6
+ };
7
+ use executor::{ExecutorClient, NativeDropParams};
8
+ use common_macros::contract_impl;
9
+ use soroban_sdk::{contract, contracttype, token::TokenClient, Address, Bytes, BytesN, Env, Vec};
10
+
11
+ /// Parameters for `lz_receive` execution.
12
+ #[contracttype]
13
+ #[derive(Clone, Debug, Eq, PartialEq)]
14
+ pub struct ExecutionParams {
15
+ pub receiver: Address,
16
+ pub origin: Origin,
17
+ pub guid: BytesN<32>,
18
+ pub message: Bytes,
19
+ pub extra_data: Bytes,
20
+ pub value: i128,
21
+ pub gas_limit: i128,
22
+ }
23
+
24
+ /// Parameters for `lz_compose` execution.
25
+ #[contracttype]
26
+ #[derive(Clone, Debug, Eq, PartialEq)]
27
+ pub struct ComposeParams {
28
+ pub from: Address,
29
+ pub to: Address,
30
+ pub guid: BytesN<32>,
31
+ pub index: u32,
32
+ pub message: Bytes,
33
+ pub extra_data: Bytes,
34
+ pub value: i128,
35
+ pub gas_limit: i128,
36
+ }
37
+
38
+ #[contract]
39
+ pub struct ExecutorHelper;
40
+
41
+ #[contract_impl]
42
+ impl ExecutorHelper {
43
+ /// Executes `lz_receive` on the target OApp; falls back to `lz_receive_alert` on failure.
44
+ pub fn execute(env: &Env, executor: &Address, params: &ExecutionParams, value_payer: &Address) {
45
+ if params.value > 0 {
46
+ value_payer.require_auth();
47
+ }
48
+ Self::execute_internal(env, executor, params, value_payer);
49
+ }
50
+
51
+ /// Executes `lz_compose` on the target composer; falls back to `lz_compose_alert` on failure.
52
+ pub fn compose(env: &Env, executor: &Address, params: &ComposeParams, value_payer: &Address) {
53
+ executor.require_auth();
54
+
55
+ let endpoint = if params.value > 0 {
56
+ let ep = ExecutorClient::new(env, executor).endpoint();
57
+ value_payer.require_auth();
58
+ transfer_value(env, &ep, value_payer, executor, params.value);
59
+ Some(ep)
60
+ } else {
61
+ None
62
+ };
63
+
64
+ let result = LayerZeroComposerClient::new(env, &params.to).try_lz_compose(
65
+ executor,
66
+ &params.from,
67
+ &params.guid,
68
+ &params.index,
69
+ &params.message,
70
+ &params.extra_data,
71
+ &params.value,
72
+ );
73
+
74
+ if result.is_err() {
75
+ let ep = endpoint.unwrap_or_else(|| ExecutorClient::new(env, executor).endpoint());
76
+ MessagingComposerClient::new(env, &ep).lz_compose_alert(
77
+ executor,
78
+ &params.from,
79
+ &params.to,
80
+ &params.guid,
81
+ &params.index,
82
+ &params.gas_limit,
83
+ &params.value,
84
+ &params.message,
85
+ &params.extra_data,
86
+ &Bytes::new(env),
87
+ );
88
+ }
89
+ }
90
+
91
+ /// Delegates `native_drop` to the executor contract.
92
+ pub fn native_drop(
93
+ env: &Env,
94
+ executor: &Address,
95
+ admin: &Address,
96
+ origin: &Origin,
97
+ dst_eid: u32,
98
+ oapp: &Address,
99
+ params: &Vec<NativeDropParams>,
100
+ ) {
101
+ admin.require_auth();
102
+ ExecutorClient::new(env, executor).native_drop(admin, origin, &dst_eid, oapp, params);
103
+ }
104
+
105
+ /// Executes `native_drop` followed by `lz_receive`.
106
+ pub fn native_drop_and_execute(
107
+ env: &Env,
108
+ executor: &Address,
109
+ admin: &Address,
110
+ origin: &Origin,
111
+ dst_eid: u32,
112
+ oapp: &Address,
113
+ native_drop_params: &Vec<NativeDropParams>,
114
+ execute_params: &ExecutionParams,
115
+ ) {
116
+ Self::native_drop(env, executor, admin, origin, dst_eid, oapp, native_drop_params);
117
+ Self::execute_internal(env, executor, execute_params, admin); // admin already authed above
118
+ }
119
+
120
+ fn execute_internal(env: &Env, executor: &Address, params: &ExecutionParams, value_payer: &Address) {
121
+ executor.require_auth();
122
+
123
+ let endpoint = if params.value > 0 {
124
+ let ep = ExecutorClient::new(env, executor).endpoint();
125
+ transfer_value(env, &ep, value_payer, executor, params.value);
126
+ Some(ep)
127
+ } else {
128
+ None
129
+ };
130
+
131
+ let result = LayerZeroReceiverClient::new(env, &params.receiver).try_lz_receive(
132
+ executor,
133
+ &params.origin,
134
+ &params.guid,
135
+ &params.message,
136
+ &params.extra_data,
137
+ &params.value,
138
+ );
139
+
140
+ if result.is_err() {
141
+ let ep = endpoint.unwrap_or_else(|| ExecutorClient::new(env, executor).endpoint());
142
+ LayerZeroEndpointV2Client::new(env, &ep).lz_receive_alert(
143
+ executor,
144
+ &params.origin,
145
+ &params.receiver,
146
+ &params.guid,
147
+ &params.gas_limit,
148
+ &params.value,
149
+ &params.message,
150
+ &params.extra_data,
151
+ &Bytes::new(env),
152
+ );
153
+ }
154
+ }
155
+ }
156
+
157
+ #[inline]
158
+ fn transfer_value(env: &Env, endpoint: &Address, payer: &Address, executor: &Address, value: i128) {
159
+ let native_token = LayerZeroEndpointV2Client::new(env, endpoint).native_token();
160
+ TokenClient::new(env, &native_token).transfer(payer, executor, &value);
161
+ }
@@ -0,0 +1,11 @@
1
+ #![no_std]
2
+
3
+ mod executor_helper;
4
+
5
+ cfg_if::cfg_if! {
6
+ // Include implementation when NOT in library mode, OR when testutils is enabled (for tests)
7
+ if #[cfg(any(not(feature = "library"), feature = "testutils"))] {
8
+
9
+ pub use executor_helper::*;
10
+ }
11
+ }
@@ -1,5 +1,5 @@
1
1
  [package]
2
- name = "worker-common"
2
+ name = "worker"
3
3
  version.workspace = true
4
4
  edition.workspace = true
5
5
  license.workspace = true
@@ -13,6 +13,3 @@ doctest = false
13
13
  soroban-sdk = { workspace = true }
14
14
  utils = { workspace = true }
15
15
  common-macros = { workspace = true }
16
- stellar-access = { workspace = true }
17
- stellar-macros = { workspace = true }
18
- stellar-contract-utils = { workspace = true }
@@ -0,0 +1,24 @@
1
+ use common_macros::contract_error;
2
+
3
+ #[contract_error]
4
+ pub enum WorkerError {
5
+ AdminAlreadyExists = 1200,
6
+ AdminNotFound,
7
+ AlreadyOnAllowlist,
8
+ AlreadyOnDenylist,
9
+ AttemptingToRemoveOnlyAdmin,
10
+ DepositAddressNotSet,
11
+ MessageLibAlreadySupported,
12
+ MessageLibNotSupported,
13
+ NoAdminsProvided,
14
+ NotAllowed,
15
+ NotOnAllowlist,
16
+ NotOnDenylist,
17
+ PauseStatusUnchanged,
18
+ PriceFeedNotSet,
19
+ ReInitialize,
20
+ Unauthorized,
21
+ UnsupportedMessageLib,
22
+ WorkerFeeLibNotSet,
23
+ WorkerIsPaused,
24
+ }