@layerzerolabs/protocol-stellar-v2 0.2.19 → 0.2.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (249) hide show
  1. package/.turbo/turbo-build.log +795 -791
  2. package/.turbo/turbo-lint.log +325 -155
  3. package/.turbo/turbo-test.log +1398 -1277
  4. package/Cargo.lock +122 -111
  5. package/Cargo.toml +32 -16
  6. package/contracts/common-macros/Cargo.toml +7 -7
  7. package/contracts/common-macros/src/auth.rs +18 -37
  8. package/contracts/common-macros/src/contract_ttl.rs +18 -7
  9. package/contracts/common-macros/src/lib.rs +31 -14
  10. package/contracts/common-macros/src/lz_contract.rs +38 -7
  11. package/contracts/common-macros/src/storage.rs +251 -292
  12. package/contracts/common-macros/src/tests/contract_ttl.rs +1 -1
  13. package/contracts/common-macros/src/tests/snapshots/common_macros__tests__auth__snapshot_generated_multisig_code.snap +6 -12
  14. package/contracts/common-macros/src/tests/snapshots/common_macros__tests__auth__snapshot_generated_ownable_code.snap +12 -17
  15. package/contracts/common-macros/src/tests/snapshots/common_macros__tests__contract_ttl__snapshot_generated_contractimpl_code.snap +2 -1
  16. package/contracts/common-macros/src/tests/snapshots/common_macros__tests__ttl_configurable__snapshot_generated_ttl_configurable_code.snap +2 -7
  17. package/contracts/common-macros/src/tests/snapshots/common_macros__tests__upgradeable__snapshot_generated_upgradeable_code.snap +20 -14
  18. package/contracts/common-macros/src/tests/upgradeable.rs +26 -4
  19. package/contracts/common-macros/src/ttl_configurable.rs +2 -10
  20. package/contracts/common-macros/src/ttl_extendable.rs +2 -10
  21. package/contracts/common-macros/src/upgradeable.rs +61 -26
  22. package/contracts/common-macros/src/utils.rs +0 -9
  23. package/contracts/endpoint-v2/src/lib.rs +3 -2
  24. package/contracts/endpoint-v2/src/tests/endpoint_v2/clear.rs +2 -2
  25. package/contracts/endpoint-v2/src/tests/endpoint_v2/lz_receive_alert.rs +3 -3
  26. package/contracts/endpoint-v2/src/tests/endpoint_v2/send.rs +4 -4
  27. package/contracts/endpoint-v2/src/tests/endpoint_v2/set_delegate.rs +17 -5
  28. package/contracts/endpoint-v2/src/tests/endpoint_v2/set_zro.rs +4 -4
  29. package/contracts/endpoint-v2/src/tests/endpoint_v2/verify.rs +2 -2
  30. package/contracts/endpoint-v2/src/tests/message_lib_manager/register_library.rs +2 -2
  31. package/contracts/endpoint-v2/src/tests/message_lib_manager/set_default_receive_lib_timeout.rs +6 -6
  32. package/contracts/endpoint-v2/src/tests/message_lib_manager/set_default_receive_library.rs +67 -37
  33. package/contracts/endpoint-v2/src/tests/message_lib_manager/set_default_send_library.rs +5 -5
  34. package/contracts/endpoint-v2/src/tests/message_lib_manager/set_receive_library.rs +44 -54
  35. package/contracts/endpoint-v2/src/tests/message_lib_manager/set_receive_library_timeout.rs +7 -7
  36. package/contracts/endpoint-v2/src/tests/message_lib_manager/set_send_library.rs +8 -8
  37. package/contracts/endpoint-v2/src/tests/messaging_channel/burn.rs +3 -3
  38. package/contracts/endpoint-v2/src/tests/messaging_channel/nilify.rs +4 -4
  39. package/contracts/endpoint-v2/src/tests/messaging_channel/skip.rs +3 -3
  40. package/contracts/endpoint-v2/src/tests/messaging_composer/clear_compose.rs +2 -2
  41. package/contracts/endpoint-v2/src/tests/messaging_composer/lz_compose_alert.rs +3 -3
  42. package/contracts/endpoint-v2/src/tests/messaging_composer/send_compose.rs +2 -2
  43. package/contracts/layerzero-views/Cargo.toml +0 -1
  44. package/contracts/layerzero-views/src/layerzero_view.rs +1 -13
  45. package/contracts/macro-integration-tests/Cargo.toml +5 -15
  46. package/contracts/macro-integration-tests/tests/runtime/oapp/mod.rs +48 -0
  47. package/contracts/macro-integration-tests/tests/runtime/oapp/oapp_core.rs +170 -0
  48. package/contracts/macro-integration-tests/tests/runtime/oapp/options_type3.rs +154 -0
  49. package/contracts/macro-integration-tests/tests/runtime/oapp/receiver.rs +338 -0
  50. package/contracts/macro-integration-tests/tests/runtime/oapp/sender.rs +435 -0
  51. package/contracts/macro-integration-tests/tests/runtime.rs +1 -0
  52. package/contracts/macro-integration-tests/tests/ui/oapp/fail/custom_wrong_value.rs +8 -0
  53. package/contracts/macro-integration-tests/tests/ui/oapp/fail/custom_wrong_value.stderr +5 -0
  54. package/contracts/macro-integration-tests/tests/ui/oapp/fail/missing_lz_receive_internal.rs +8 -0
  55. package/contracts/macro-integration-tests/tests/ui/oapp/fail/missing_lz_receive_internal.stderr +71 -0
  56. package/contracts/macro-integration-tests/tests/ui/oapp/fail/non_struct_input.rs +10 -0
  57. package/contracts/macro-integration-tests/tests/ui/oapp/fail/non_struct_input.stderr +5 -0
  58. package/contracts/macro-integration-tests/tests/ui/oapp/fail/unknown_custom_option.rs +8 -0
  59. package/contracts/macro-integration-tests/tests/ui/oapp/fail/unknown_custom_option.stderr +5 -0
  60. package/contracts/macro-integration-tests/tests/ui/oapp/fail/wrong_key.rs +8 -0
  61. package/contracts/macro-integration-tests/tests/ui/oapp/fail/wrong_key.stderr +5 -0
  62. package/contracts/macro-integration-tests/tests/ui/oapp/pass/custom_all.rs +38 -0
  63. package/contracts/macro-integration-tests/tests/ui/oapp/pass/custom_single_trait.rs +96 -0
  64. package/contracts/macro-integration-tests/tests/ui/oapp/pass/minimal_contract.rs +64 -0
  65. package/contracts/macro-integration-tests/tests/ui/oapp/pass/struct_with_fields.rs +46 -0
  66. package/contracts/macro-integration-tests/tests/ui/ownable/fail/only_auth_missing_env.stderr +8 -0
  67. package/contracts/macro-integration-tests/tests/ui/ownable/pass/namespacing_and_imports.rs +1 -1
  68. package/contracts/macro-integration-tests/tests/ui/ownable/pass/only_auth_env_param_variants.rs +1 -1
  69. package/contracts/macro-integration-tests/tests/ui_oapp.rs +11 -0
  70. package/contracts/message-libs/message-lib-common/Cargo.toml +0 -1
  71. package/contracts/message-libs/message-lib-common/src/errors.rs +1 -1
  72. package/contracts/message-libs/treasury/Cargo.toml +0 -2
  73. package/contracts/message-libs/treasury/src/tests/treasury_tests.rs +2 -2
  74. package/contracts/message-libs/uln-302/src/events.rs +4 -0
  75. package/contracts/message-libs/uln-302/src/send_uln.rs +22 -6
  76. package/contracts/message-libs/uln-302/src/tests/receive_uln302/effective_receive_uln_config.rs +2 -2
  77. package/contracts/message-libs/uln-302/src/tests/receive_uln302/set_default_receive_uln_configs.rs +2 -2
  78. package/contracts/message-libs/uln-302/src/tests/receive_uln302/verify.rs +2 -2
  79. package/contracts/message-libs/uln-302/src/tests/send_uln302/effective_executor_config.rs +2 -2
  80. package/contracts/message-libs/uln-302/src/tests/send_uln302/effective_send_uln_config.rs +2 -2
  81. package/contracts/message-libs/uln-302/src/tests/send_uln302/send.rs +21 -67
  82. package/contracts/message-libs/uln-302/src/tests/send_uln302/set_default_executor_configs.rs +2 -2
  83. package/contracts/message-libs/uln-302/src/tests/send_uln302/set_default_send_uln_configs.rs +2 -2
  84. package/contracts/oapps/counter/Cargo.toml +5 -6
  85. package/contracts/oapps/counter/integration_tests/setup_uln.rs +1 -1
  86. package/contracts/oapps/counter/integration_tests/utils.rs +19 -12
  87. package/contracts/oapps/oapp/src/errors.rs +1 -1
  88. package/contracts/oapps/oapp/src/interfaces/mod.rs +3 -0
  89. package/contracts/oapps/oapp/src/interfaces/oapp_msg_inspector.rs +47 -0
  90. package/contracts/oapps/oapp/src/lib.rs +1 -0
  91. package/contracts/oapps/oapp/src/macro_tests/test_macros.rs +4 -4
  92. package/contracts/oapps/oapp/src/oapp_core.rs +5 -5
  93. package/contracts/oapps/oapp/src/oapp_options_type3.rs +12 -4
  94. package/contracts/oapps/oapp/src/oapp_receiver.rs +14 -9
  95. package/contracts/oapps/oapp/src/tests/mod.rs +4 -4
  96. package/contracts/oapps/oapp/src/tests/oapp_core.rs +223 -0
  97. package/contracts/oapps/oapp/src/tests/oapp_options_type3.rs +240 -0
  98. package/contracts/oapps/oapp/src/tests/oapp_receiver.rs +381 -0
  99. package/contracts/oapps/oapp/src/tests/oapp_sender.rs +569 -0
  100. package/contracts/oapps/oapp-macros/Cargo.toml +8 -4
  101. package/contracts/oapps/oapp-macros/src/generators.rs +9 -34
  102. package/contracts/oapps/oapp-macros/src/lib.rs +3 -0
  103. package/contracts/oapps/oapp-macros/src/tests/mod.rs +2 -0
  104. package/contracts/oapps/oapp-macros/src/tests/oapp.rs +88 -0
  105. package/contracts/oapps/oapp-macros/src/tests/parse_custom_impls.rs +86 -0
  106. package/contracts/oapps/oapp-macros/src/tests/snapshots/oapp_macros__tests__oapp__snapshot_generate_oapp.snap +103 -0
  107. package/contracts/oapps/oft/integration-tests/utils.rs +28 -8
  108. package/contracts/oapps/oft/src/extensions/oft_fee.rs +153 -75
  109. package/contracts/oapps/oft/src/extensions/pausable.rs +61 -12
  110. package/contracts/oapps/oft/src/extensions/rate_limiter.rs +198 -134
  111. package/contracts/oapps/oft/src/oft.rs +45 -50
  112. package/contracts/oapps/oft/src/oft_types/lock_unlock.rs +1 -1
  113. package/contracts/oapps/oft/src/oft_types/mint_burn.rs +4 -26
  114. package/contracts/oapps/oft-core/Cargo.toml +1 -4
  115. package/contracts/oapps/oft-core/integration-tests/setup.rs +3 -3
  116. package/contracts/oapps/oft-core/integration-tests/utils.rs +21 -3
  117. package/contracts/oapps/oft-core/src/errors.rs +3 -2
  118. package/contracts/oapps/oft-core/src/events.rs +6 -0
  119. package/contracts/oapps/oft-core/src/lib.rs +1 -1
  120. package/contracts/oapps/oft-core/src/oft_core.rs +341 -246
  121. package/contracts/oapps/oft-core/src/storage.rs +7 -3
  122. package/contracts/oapps/oft-core/src/tests/mod.rs +1 -0
  123. package/contracts/oapps/oft-core/src/tests/test_decimals.rs +37 -2
  124. package/contracts/oapps/oft-core/src/tests/test_lz_receive.rs +2 -2
  125. package/contracts/oapps/oft-core/src/tests/test_msg_inspector.rs +323 -0
  126. package/contracts/oapps/oft-core/src/tests/test_send.rs +2 -2
  127. package/contracts/oapps/oft-core/src/tests/test_utils.rs +61 -16
  128. package/contracts/upgrader/src/lib.rs +30 -57
  129. package/contracts/upgrader/src/tests/test_data/test_upgradeable_contract1.wasm +0 -0
  130. package/contracts/upgrader/src/tests/test_data/test_upgradeable_contract2.wasm +0 -0
  131. package/contracts/upgrader/src/tests/test_upgrader.rs +44 -35
  132. package/contracts/utils/Cargo.toml +0 -1
  133. package/contracts/utils/src/buffer_reader.rs +1 -0
  134. package/contracts/utils/src/errors.rs +4 -2
  135. package/contracts/utils/src/multisig.rs +17 -8
  136. package/contracts/utils/src/ownable.rs +6 -6
  137. package/contracts/utils/src/testing_utils.rs +124 -54
  138. package/contracts/utils/src/tests/multisig.rs +12 -12
  139. package/contracts/utils/src/tests/ownable.rs +6 -6
  140. package/contracts/utils/src/tests/testing_utils.rs +50 -167
  141. package/contracts/utils/src/tests/ttl_configurable.rs +5 -5
  142. package/contracts/utils/src/tests/upgradeable.rs +372 -175
  143. package/contracts/utils/src/ttl_configurable.rs +13 -7
  144. package/contracts/utils/src/upgradeable.rs +48 -23
  145. package/contracts/workers/dvn/Cargo.toml +6 -6
  146. package/contracts/workers/dvn/src/auth.rs +12 -42
  147. package/contracts/workers/dvn/src/dvn.rs +15 -40
  148. package/contracts/workers/dvn/src/errors.rs +0 -1
  149. package/contracts/workers/dvn/src/interfaces/dvn.rs +35 -0
  150. package/contracts/workers/dvn/src/lib.rs +4 -3
  151. package/contracts/workers/dvn/src/tests/auth.rs +1 -1
  152. package/contracts/workers/dvn/src/tests/dvn.rs +19 -15
  153. package/contracts/workers/dvn/src/tests/multisig/set_threshold.rs +2 -4
  154. package/contracts/workers/dvn/src/tests/multisig/verify_signatures.rs +1 -3
  155. package/contracts/workers/dvn/src/tests/setup.rs +5 -9
  156. package/contracts/workers/dvn-fee-lib/Cargo.toml +2 -2
  157. package/contracts/workers/dvn-fee-lib/src/dvn_fee_lib.rs +38 -22
  158. package/contracts/workers/dvn-fee-lib/src/lib.rs +12 -2
  159. package/contracts/workers/dvn-fee-lib/src/tests/dvn_fee_lib.rs +17 -16
  160. package/contracts/workers/executor/Cargo.toml +4 -0
  161. package/contracts/workers/executor/src/executor.rs +15 -36
  162. package/contracts/workers/executor/src/lib.rs +2 -2
  163. package/contracts/workers/executor/src/tests/auth.rs +394 -0
  164. package/contracts/workers/executor/src/tests/executor.rs +410 -0
  165. package/contracts/workers/executor/src/tests/mod.rs +3 -0
  166. package/contracts/workers/executor/src/tests/setup.rs +250 -0
  167. package/contracts/workers/executor-fee-lib/Cargo.toml +7 -1
  168. package/contracts/workers/executor-fee-lib/src/executor_fee_lib.rs +62 -15
  169. package/contracts/workers/executor-fee-lib/src/executor_option.rs +28 -1
  170. package/contracts/workers/executor-fee-lib/src/lib.rs +11 -2
  171. package/contracts/workers/executor-fee-lib/src/tests/executor_fee_lib.rs +701 -0
  172. package/contracts/workers/executor-fee-lib/src/tests/executor_option.rs +370 -0
  173. package/contracts/workers/executor-fee-lib/src/tests/mod.rs +4 -0
  174. package/contracts/workers/executor-fee-lib/src/tests/setup.rs +60 -0
  175. package/contracts/workers/executor-helper/Cargo.toml +0 -1
  176. package/contracts/workers/executor-helper/src/lib.rs +3 -0
  177. package/contracts/workers/executor-helper/src/tests/executor_helper.rs +184 -0
  178. package/contracts/workers/executor-helper/src/tests/mod.rs +2 -0
  179. package/contracts/workers/executor-helper/src/tests/setup.rs +366 -0
  180. package/contracts/workers/fee-lib-interfaces/Cargo.toml +14 -0
  181. package/contracts/workers/{worker/src/interfaces/mod.rs → fee-lib-interfaces/src/lib.rs} +4 -3
  182. package/contracts/workers/price-feed/Cargo.toml +7 -1
  183. package/contracts/workers/price-feed/src/events.rs +1 -1
  184. package/contracts/workers/price-feed/src/lib.rs +12 -4
  185. package/contracts/workers/price-feed/src/price_feed.rs +5 -21
  186. package/contracts/workers/price-feed/src/storage.rs +1 -1
  187. package/contracts/workers/price-feed/src/tests/mod.rs +2 -0
  188. package/contracts/workers/price-feed/src/tests/price_feed.rs +869 -0
  189. package/contracts/workers/price-feed/src/tests/setup.rs +70 -0
  190. package/contracts/workers/price-feed/src/types.rs +1 -1
  191. package/contracts/workers/worker/src/errors.rs +1 -4
  192. package/contracts/workers/worker/src/lib.rs +0 -2
  193. package/contracts/workers/worker/src/storage.rs +32 -29
  194. package/contracts/workers/worker/src/tests/setup.rs +2 -8
  195. package/contracts/workers/worker/src/tests/worker.rs +96 -74
  196. package/contracts/workers/worker/src/worker.rs +75 -75
  197. package/docs/error-spec.md +55 -0
  198. package/docs/layerzero-v2-on-stellar.md +447 -0
  199. package/docs/oapp-guide.md +212 -0
  200. package/docs/oft-guide.md +314 -0
  201. package/package.json +3 -3
  202. package/sdk/.turbo/turbo-test.log +268 -263
  203. package/sdk/dist/generated/bml.d.ts +12 -4
  204. package/sdk/dist/generated/bml.js +9 -7
  205. package/sdk/dist/generated/counter.d.ts +306 -298
  206. package/sdk/dist/generated/counter.js +48 -46
  207. package/sdk/dist/generated/dvn.d.ts +450 -411
  208. package/sdk/dist/generated/dvn.js +66 -64
  209. package/sdk/dist/generated/dvn_fee_lib.d.ts +294 -338
  210. package/sdk/dist/generated/dvn_fee_lib.js +33 -64
  211. package/sdk/dist/generated/endpoint.d.ts +108 -100
  212. package/sdk/dist/generated/endpoint.js +21 -19
  213. package/sdk/dist/generated/executor.d.ts +414 -370
  214. package/sdk/dist/generated/executor.js +58 -55
  215. package/sdk/dist/generated/executor_fee_lib.d.ts +333 -377
  216. package/sdk/dist/generated/executor_fee_lib.js +34 -65
  217. package/sdk/dist/generated/executor_helper.d.ts +26 -190
  218. package/sdk/dist/generated/executor_helper.js +23 -28
  219. package/sdk/dist/generated/layerzero_view.d.ts +1271 -0
  220. package/sdk/dist/generated/layerzero_view.js +294 -0
  221. package/sdk/dist/generated/oft.d.ts +408 -385
  222. package/sdk/dist/generated/oft.js +89 -92
  223. package/sdk/dist/generated/price_feed.d.ts +385 -429
  224. package/sdk/dist/generated/price_feed.js +50 -81
  225. package/sdk/dist/generated/sml.d.ts +108 -100
  226. package/sdk/dist/generated/sml.js +21 -19
  227. package/sdk/dist/generated/treasury.d.ts +108 -100
  228. package/sdk/dist/generated/treasury.js +21 -19
  229. package/sdk/dist/generated/uln302.d.ts +108 -100
  230. package/sdk/dist/generated/uln302.js +23 -21
  231. package/sdk/dist/generated/upgrader.d.ts +189 -18
  232. package/sdk/dist/generated/upgrader.js +84 -4
  233. package/sdk/dist/index.d.ts +1 -0
  234. package/sdk/dist/index.js +2 -0
  235. package/sdk/package.json +1 -1
  236. package/sdk/src/index.ts +3 -0
  237. package/sdk/test/oft-sml.test.ts +4 -4
  238. package/sdk/test/suites/localnet.ts +84 -20
  239. package/sdk/test/upgrader.test.ts +2 -3
  240. package/tools/ts-bindings-gen/src/main.rs +2 -1
  241. package/contracts/ERROR_SPEC.md +0 -44
  242. package/contracts/endpoint-v2/ARCHITECTURE.md +0 -233
  243. package/contracts/oapps/oapp/src/tests/test_oapp_core.rs +0 -175
  244. package/contracts/oapps/oapp/src/tests/test_oapp_options_type3.rs +0 -212
  245. package/contracts/oapps/oapp/src/tests/test_oapp_receiver.rs +0 -153
  246. package/contracts/oapps/oapp/src/tests/test_oapp_sender.rs +0 -294
  247. /package/contracts/workers/{worker/src/interfaces → fee-lib-interfaces/src}/dvn_fee_lib.rs +0 -0
  248. /package/contracts/workers/{worker/src/interfaces → fee-lib-interfaces/src}/executor_fee_lib.rs +0 -0
  249. /package/contracts/workers/{worker/src/interfaces → fee-lib-interfaces/src}/price_feed.rs +0 -0
@@ -1,6 +1,10 @@
1
1
  use common_macros::{contract_error, contract_trait, only_auth, storage};
2
2
  use soroban_sdk::{assert_with_error, contractevent, Env};
3
- use utils::ownable::Ownable;
3
+ use utils::auth::Auth;
4
+
5
+ // =========================================================================
6
+ // Storage
7
+ // =========================================================================
4
8
 
5
9
  #[storage]
6
10
  pub enum OFTPausableStorage {
@@ -9,42 +13,87 @@ pub enum OFTPausableStorage {
9
13
  Paused,
10
14
  }
11
15
 
16
+ // =========================================================================
17
+ // Errors
18
+ // =========================================================================
19
+
12
20
  #[contract_error]
13
21
  pub enum OFTPausableError {
14
- Paused = 2300,
22
+ Paused = 3110,
15
23
  PauseStatusUnchanged,
16
24
  }
17
25
 
26
+ // =========================================================================
27
+ // Events
28
+ // =========================================================================
29
+
18
30
  #[contractevent]
19
31
  #[derive(Clone, Debug, Eq, PartialEq)]
20
32
  pub struct PausedSet {
21
33
  pub paused: bool,
22
34
  }
23
35
 
36
+ // =========================================================================
37
+ // Trait With Default Implementations
38
+ // =========================================================================
39
+
24
40
  #[contract_trait]
25
- pub trait OFTPausable: OFTPausableInternal + Ownable + Sized {
41
+ pub trait OFTPausable: OFTPausableInternal + Auth {
42
+ /// Sets the paused state of the OFT.
43
+ ///
44
+ /// When paused, the OFT will reject new send/receive/quote_send/quote_oft operations.
45
+ ///
46
+ /// # Arguments
47
+ /// * `paused` - `true` to pause, `false` to unpause
26
48
  #[only_auth]
27
- fn set_paused(env: &Env, paused: bool) {
28
- let current_paused = OFTPausableStorage::paused(env);
29
- assert_with_error!(env, current_paused != paused, OFTPausableError::PauseStatusUnchanged);
30
- OFTPausableStorage::set_paused(env, &paused);
31
- PausedSet { paused }.publish(env);
49
+ fn set_paused(env: &soroban_sdk::Env, paused: bool) {
50
+ Self::__set_paused(env, paused);
32
51
  }
33
52
 
34
- fn is_paused(env: &Env) -> bool {
35
- OFTPausableStorage::paused(env)
53
+ /// Returns the paused state of the OFT.
54
+ fn is_paused(env: &soroban_sdk::Env) -> bool {
55
+ Self::__is_paused(env)
36
56
  }
37
57
  }
38
58
 
39
59
  /// Internal trait for pausable operations used by OFT hooks.
40
60
  /// Contains only truly internal methods that are called from OFTInternal implementations.
41
61
  pub trait OFTPausableInternal {
62
+ // =========================================================================
63
+ // OFT Hooks
64
+ // =========================================================================
65
+
42
66
  /// Asserts that the OFT is not paused, panics otherwise.
43
- /// Used internally by `send`, `quote_send`, and `__lz_receive` to enforce pause state.
67
+ /// Used internally by `send`, `quote_send`, `quote_oft`, and `__lz_receive` to enforce pause state.
44
68
  ///
45
69
  /// # Errors
46
70
  /// * `Paused` - If the OFT is currently paused.
47
71
  fn __assert_not_paused(env: &Env) {
48
- assert_with_error!(env, !OFTPausableStorage::paused(env), OFTPausableError::Paused);
72
+ assert_with_error!(env, !Self::__is_paused(env), OFTPausableError::Paused);
73
+ }
74
+
75
+ // =========================================================================
76
+ // Management Functions
77
+ // =========================================================================
78
+
79
+ /// Sets the paused state of the OFT.
80
+ ///
81
+ /// When paused, the OFT will reject new send/receive/quote_send/quote_oft operations.
82
+ ///
83
+ /// # Arguments
84
+ /// * `paused` - `true` to pause, `false` to unpause
85
+ fn __set_paused(env: &Env, paused: bool) {
86
+ assert_with_error!(env, Self::__is_paused(env) != paused, OFTPausableError::PauseStatusUnchanged);
87
+ OFTPausableStorage::set_paused(env, &paused);
88
+ PausedSet { paused }.publish(env);
89
+ }
90
+
91
+ // =========================================================================
92
+ // View Functions
93
+ // =========================================================================
94
+
95
+ /// Returns the paused state of the OFT.
96
+ fn __is_paused(env: &Env) -> bool {
97
+ OFTPausableStorage::paused(env)
49
98
  }
50
99
  }
@@ -1,6 +1,11 @@
1
+ use crate as oft;
1
2
  use common_macros::{contract_error, contract_trait, only_auth, storage};
2
- use soroban_sdk::{assert_with_error, contractevent, contracttype, panic_with_error, Env};
3
- use utils::ownable::Ownable;
3
+ use soroban_sdk::{assert_with_error, contractevent, contracttype, Env};
4
+ use utils::auth::Auth;
5
+
6
+ // =========================================================================
7
+ // Types
8
+ // =========================================================================
4
9
 
5
10
  #[contracttype]
6
11
  #[derive(Clone, Debug, Eq, PartialEq)]
@@ -10,191 +15,250 @@ pub enum Direction {
10
15
  Outbound,
11
16
  }
12
17
 
18
+ /// Configuration for rate limiting, used as input parameter.
13
19
  #[contracttype]
14
20
  #[derive(Clone, Debug, Eq, PartialEq)]
15
- pub struct RateLimit {
16
- limit: i128,
17
- window_seconds: u64,
21
+ pub struct RateLimitConfig {
22
+ pub limit: i128,
23
+ pub window_seconds: u64,
24
+ }
25
+
26
+ /// Internal storage struct for rate limit state.
27
+ ///
28
+ /// The rate limiter uses a "leaky bucket" algorithm where:
29
+ /// - `config.limit` defines the maximum tokens that can be "in flight" at any time
30
+ /// - `config.window_seconds` defines how long it takes for the bucket to fully drain
31
+ /// - Tokens decay linearly over time: `decay = elapsed_time * limit / window_seconds`
32
+ /// - Current in-flight = `in_flight_on_last_update - decay` (clamped to 0)
33
+ #[contracttype]
34
+ #[derive(Clone, Debug, Eq, PartialEq)]
35
+ struct RateLimitState {
36
+ /// The rate limit configuration (limit and window_seconds)
37
+ config: RateLimitConfig,
38
+ /// The in-flight amount at the time of `last_update` (before decay is applied)
18
39
  in_flight_on_last_update: i128,
40
+ /// Timestamp of the last update (used to calculate decay)
19
41
  last_update: u64,
20
42
  }
21
43
 
44
+ // =========================================================================
45
+ // Storage
46
+ // =========================================================================
47
+
22
48
  #[storage]
23
- pub enum RateLimitStorage {
24
- #[persistent(RateLimit)]
49
+ enum RateLimitStorage {
50
+ #[persistent(RateLimitState)]
25
51
  RateLimit { direction: Direction, eid: u32 },
26
52
  }
27
53
 
54
+ // =========================================================================
55
+ // Errors
56
+ // =========================================================================
57
+
28
58
  #[contract_error]
29
59
  pub enum RateLimitError {
30
- ExceededRateLimit = 2400,
60
+ ExceededRateLimit = 3120,
61
+ InvalidAmount,
31
62
  InvalidTimestamp,
32
- InvalidWindowSeconds,
33
- InvalidLimit,
63
+ InvalidConfig,
34
64
  SameValue,
35
65
  }
36
66
 
37
- #[contractevent]
38
- #[derive(Clone, Debug, Eq, PartialEq)]
39
- pub struct RateLimitSet {
40
- pub direction: Direction,
41
- pub eid: u32,
42
- pub limit: i128,
43
- pub window_seconds: u64,
44
- }
45
-
46
- #[contractevent]
47
- #[derive(Clone, Debug, Eq, PartialEq)]
48
- pub struct RateLimitUpdated {
49
- pub direction: Direction,
50
- pub eid: u32,
51
- pub limit: i128,
52
- pub window_seconds: u64,
53
- }
67
+ // =========================================================================
68
+ // Events
69
+ // =========================================================================
54
70
 
55
71
  #[contractevent]
56
72
  #[derive(Clone, Debug, Eq, PartialEq)]
57
- pub struct RateLimitUnset {
73
+ pub struct RateLimitSet {
58
74
  pub direction: Direction,
59
75
  pub eid: u32,
76
+ /// The rate limit configuration, or None if the rate limit is removed
77
+ pub config: Option<RateLimitConfig>,
60
78
  }
61
79
 
62
- /// Helper function to calculate the current in-flight amount with decay.
63
- /// Used by both public trait methods and internal implementations.
64
- fn calculate_in_flight(env: &Env, direction: &Direction, eid: u32) -> i128 {
65
- if !RateLimitStorage::has_rate_limit(env, direction, eid) {
66
- return 0;
67
- }
68
- let rate_limit = RateLimitStorage::rate_limit(env, direction, eid).unwrap();
69
- let timestamp = env.ledger().timestamp();
70
- assert_with_error!(env, timestamp >= rate_limit.last_update, RateLimitError::InvalidTimestamp);
71
- let elapsed = timestamp - rate_limit.last_update;
72
- let decay = (elapsed as i128) * rate_limit.limit / (rate_limit.window_seconds as i128);
73
- if decay < rate_limit.in_flight_on_last_update {
74
- rate_limit.in_flight_on_last_update - decay
75
- } else {
76
- 0
77
- }
78
- }
79
-
80
- /// Checkpoints the current in-flight amount by applying decay and updating timestamp.
81
- /// Used internally before modifying rate limit state.
82
- fn checkpoint_rate_limit_in_flight(env: &Env, direction: &Direction, eid: u32) {
83
- let in_flight = calculate_in_flight(env, direction, eid);
84
- let mut rate_limit = RateLimitStorage::rate_limit(env, direction, eid).unwrap();
85
- rate_limit.in_flight_on_last_update = in_flight;
86
- rate_limit.last_update = env.ledger().timestamp();
87
- RateLimitStorage::set_rate_limit(env, direction, eid, &rate_limit);
88
- }
80
+ // =========================================================================
81
+ // Trait With Default Implementations
82
+ // =========================================================================
89
83
 
90
84
  #[contract_trait]
91
- pub trait RateLimiter: RateLimiterInternal + Ownable + Sized {
92
- #[only_auth]
93
- fn set_rate_limit(env: &Env, direction: &Direction, eid: u32, limit: i128, window_seconds: u64) {
94
- assert_with_error!(env, limit > 0, RateLimitError::InvalidLimit);
95
- assert_with_error!(env, window_seconds > 0, RateLimitError::InvalidWindowSeconds);
96
- if RateLimitStorage::has_rate_limit(env, direction, eid) {
97
- let rate_limit_data = RateLimitStorage::rate_limit(env, direction, eid).unwrap();
98
- assert_with_error!(
99
- env,
100
- limit != rate_limit_data.limit || window_seconds != rate_limit_data.window_seconds,
101
- RateLimitError::SameValue
102
- );
103
- checkpoint_rate_limit_in_flight(env, direction, eid);
104
- let mut rate_limit = RateLimitStorage::rate_limit(env, direction, eid).unwrap();
105
- rate_limit.limit = limit;
106
- rate_limit.window_seconds = window_seconds;
107
- RateLimitStorage::set_rate_limit(env, direction, eid, &rate_limit);
108
- RateLimitUpdated { direction: direction.clone(), eid, limit, window_seconds }.publish(env);
109
- } else {
110
- RateLimitStorage::set_rate_limit(
111
- env,
112
- direction,
113
- eid,
114
- &RateLimit {
115
- limit,
116
- window_seconds,
117
- in_flight_on_last_update: 0,
118
- last_update: env.ledger().timestamp(),
119
- },
120
- );
121
- RateLimitSet { direction: direction.clone(), eid, limit, window_seconds }.publish(env);
122
- }
123
- }
85
+ pub trait RateLimiter: RateLimiterInternal + Auth {
86
+ // =========================================================================
87
+ // Management Functions
88
+ // =========================================================================
124
89
 
90
+ /// Sets or removes a rate limit for a specific direction and endpoint.
91
+ ///
92
+ /// # Arguments
93
+ /// * `direction` - The direction (Inbound or Outbound)
94
+ /// * `eid` - The endpoint ID
95
+ /// * `config` - The rate limit configuration, or None to remove the rate limit
125
96
  #[only_auth]
126
- fn unset_rate_limit(env: &Env, direction: &Direction, eid: u32) {
127
- assert_with_error!(env, RateLimitStorage::has_rate_limit(env, direction, eid), RateLimitError::SameValue);
128
- RateLimitStorage::remove_rate_limit(env, direction, eid);
129
- RateLimitUnset { direction: direction.clone(), eid }.publish(env);
97
+ fn set_rate_limit(
98
+ env: &soroban_sdk::Env,
99
+ direction: &oft::rate_limiter::Direction,
100
+ eid: u32,
101
+ config: Option<oft::rate_limiter::RateLimitConfig>,
102
+ ) {
103
+ Self::__set_rate_limit(env, direction, eid, config);
130
104
  }
131
105
 
132
- fn rate_limit_config(env: &Env, direction: &Direction, eid: u32) -> (i128, u64) {
133
- if !RateLimitStorage::has_rate_limit(env, direction, eid) {
134
- return (0, 0);
135
- }
136
- let rate_limit = RateLimitStorage::rate_limit(env, direction, eid).unwrap();
137
- (rate_limit.limit, rate_limit.window_seconds)
106
+ // =========================================================================
107
+ // View Functions
108
+ // =========================================================================
109
+
110
+ /// Returns the rate limit configuration for a direction and endpoint.
111
+ /// Returns None if no rate limit is configured.
112
+ fn rate_limit_config(
113
+ env: &soroban_sdk::Env,
114
+ direction: &oft::rate_limiter::Direction,
115
+ eid: u32,
116
+ ) -> Option<oft::rate_limiter::RateLimitConfig> {
117
+ Self::__rate_limit_config(env, direction, eid)
138
118
  }
139
119
 
140
- fn rate_limit_in_flight(env: &Env, direction: &Direction, eid: u32) -> i128 {
141
- calculate_in_flight(env, direction, eid)
120
+ /// Returns the current in-flight amount for a direction and endpoint.
121
+ fn rate_limit_in_flight(env: &soroban_sdk::Env, direction: &oft::rate_limiter::Direction, eid: u32) -> i128 {
122
+ Self::__rate_limit_in_flight(env, direction, eid)
142
123
  }
143
124
 
144
- fn rate_limit_capacity(env: &Env, direction: &Direction, eid: u32) -> i128 {
145
- if !RateLimitStorage::has_rate_limit(env, direction, eid) {
146
- return i128::MAX;
147
- }
148
- let rate_limit = RateLimitStorage::rate_limit(env, direction, eid).unwrap();
149
- let in_flight = calculate_in_flight(env, direction, eid);
150
- if rate_limit.limit > in_flight {
151
- rate_limit.limit - in_flight
152
- } else {
153
- 0
154
- }
125
+ /// Returns the available capacity for a direction and endpoint.
126
+ /// Returns i128::MAX if no rate limit is configured.
127
+ fn rate_limit_capacity(env: &soroban_sdk::Env, direction: &oft::rate_limiter::Direction, eid: u32) -> i128 {
128
+ Self::__rate_limit_capacity(env, direction, eid)
155
129
  }
156
130
  }
157
131
 
158
132
  /// Internal trait for rate limiter operations used by OFT hooks.
159
133
  /// Contains only truly internal methods that are called from OFTInternal implementations.
160
134
  pub trait RateLimiterInternal {
135
+ // =========================================================================
136
+ // OFT Hooks
137
+ // =========================================================================
138
+
161
139
  /// Consumes the specified amount from the rate limit capacity.
162
140
  /// Used internally by `__debit` and `__credit` to enforce rate limits.
163
141
  ///
164
142
  /// # Errors
165
143
  /// * `ExceededRateLimit` - If the amount exceeds the available capacity.
166
144
  fn __consume_rate_limit_capacity(env: &Env, direction: &Direction, eid: u32, amount: i128) {
167
- if !RateLimitStorage::has_rate_limit(env, direction, eid) {
145
+ assert_with_error!(env, amount >= 0, RateLimitError::InvalidAmount);
146
+
147
+ let Some(mut state) = RateLimitStorage::rate_limit(env, direction, eid) else {
168
148
  return;
169
- }
170
- checkpoint_rate_limit_in_flight(env, direction, eid);
171
- let mut rate_limit = RateLimitStorage::rate_limit(env, direction, eid).unwrap();
172
- // Check against remaining capacity (limit - current in_flight), not total limit
173
- let capacity = if rate_limit.limit > rate_limit.in_flight_on_last_update {
174
- rate_limit.limit - rate_limit.in_flight_on_last_update
175
- } else {
176
- 0
177
149
  };
178
- if amount > capacity {
179
- panic_with_error!(env, RateLimitError::ExceededRateLimit);
180
- }
181
- rate_limit.in_flight_on_last_update += amount;
182
- RateLimitStorage::set_rate_limit(env, direction, eid, &rate_limit);
150
+
151
+ // Apply decay and update timestamp
152
+ let in_flight = calculate_decayed_in_flight(env, &state);
153
+ state.in_flight_on_last_update = in_flight;
154
+ state.last_update = env.ledger().timestamp();
155
+
156
+ // Check capacity and consume
157
+ let capacity = (state.config.limit - in_flight).max(0);
158
+ assert_with_error!(env, amount <= capacity, RateLimitError::ExceededRateLimit);
159
+ state.in_flight_on_last_update += amount;
160
+
161
+ RateLimitStorage::set_rate_limit(env, direction, eid, &state);
183
162
  }
184
163
 
185
164
  /// Releases the specified amount back to the rate limit capacity.
186
165
  /// Used internally by `__credit` to release outbound capacity on inbound messages.
187
166
  fn __release_rate_limit_capacity(env: &Env, direction: &Direction, eid: u32, amount: i128) {
188
- if !RateLimitStorage::has_rate_limit(env, direction, eid) {
167
+ assert_with_error!(env, amount >= 0, RateLimitError::InvalidAmount);
168
+
169
+ let Some(mut state) = RateLimitStorage::rate_limit(env, direction, eid) else {
189
170
  return;
171
+ };
172
+
173
+ // Apply decay and update timestamp
174
+ let in_flight = calculate_decayed_in_flight(env, &state);
175
+ state.in_flight_on_last_update = (in_flight - amount).max(0);
176
+ state.last_update = env.ledger().timestamp();
177
+
178
+ RateLimitStorage::set_rate_limit(env, direction, eid, &state);
179
+ }
180
+
181
+ // =========================================================================
182
+ // Management Functions
183
+ // =========================================================================
184
+
185
+ /// Sets or removes a rate limit for a specific direction and endpoint.
186
+ ///
187
+ /// # Arguments
188
+ /// * `direction` - The direction (Inbound or Outbound)
189
+ /// * `eid` - The endpoint ID
190
+ /// * `config` - The rate limit configuration, or None to remove the rate limit
191
+ fn __set_rate_limit(env: &Env, direction: &Direction, eid: u32, config: Option<RateLimitConfig>) {
192
+ let current_state = RateLimitStorage::rate_limit(env, direction, eid);
193
+ let current_config = current_state.as_ref().map(|s| s.config.clone());
194
+ assert_with_error!(env, current_config != config, RateLimitError::SameValue);
195
+
196
+ match config {
197
+ Some(cfg) => {
198
+ assert_with_error!(env, cfg.limit >= 0 && cfg.window_seconds > 0, RateLimitError::InvalidConfig);
199
+
200
+ let state = if let Some(mut existing) = current_state {
201
+ // Update existing: checkpoint in-flight before changing config
202
+ existing.in_flight_on_last_update = calculate_decayed_in_flight(env, &existing);
203
+ existing.last_update = env.ledger().timestamp();
204
+ existing.config = cfg.clone();
205
+ existing
206
+ } else {
207
+ // Create new
208
+ RateLimitState {
209
+ config: cfg.clone(),
210
+ in_flight_on_last_update: 0,
211
+ last_update: env.ledger().timestamp(),
212
+ }
213
+ };
214
+
215
+ RateLimitStorage::set_rate_limit(env, direction, eid, &state);
216
+ RateLimitSet { direction: direction.clone(), eid, config: Some(cfg) }.publish(env);
217
+ }
218
+ None => {
219
+ RateLimitStorage::remove_rate_limit(env, direction, eid);
220
+ RateLimitSet { direction: direction.clone(), eid, config: None }.publish(env);
221
+ }
190
222
  }
191
- checkpoint_rate_limit_in_flight(env, direction, eid);
192
- let mut rate_limit = RateLimitStorage::rate_limit(env, direction, eid).unwrap();
193
- if amount >= rate_limit.in_flight_on_last_update {
194
- rate_limit.in_flight_on_last_update = 0;
195
- } else {
196
- rate_limit.in_flight_on_last_update -= amount;
197
- }
198
- RateLimitStorage::set_rate_limit(env, direction, eid, &rate_limit);
199
223
  }
224
+
225
+ // =========================================================================
226
+ // View Functions
227
+ // =========================================================================
228
+
229
+ /// Returns the rate limit configuration for a direction and endpoint.
230
+ /// Returns None if no rate limit is configured.
231
+ fn __rate_limit_config(env: &Env, direction: &Direction, eid: u32) -> Option<RateLimitConfig> {
232
+ RateLimitStorage::rate_limit(env, direction, eid).map(|s| s.config)
233
+ }
234
+
235
+ /// Returns the current in-flight amount for a direction and endpoint.
236
+ fn __rate_limit_in_flight(env: &Env, direction: &Direction, eid: u32) -> i128 {
237
+ RateLimitStorage::rate_limit(env, direction, eid).map(|s| calculate_decayed_in_flight(env, &s)).unwrap_or(0)
238
+ }
239
+
240
+ /// Returns the available capacity for a direction and endpoint.
241
+ /// Returns i128::MAX if no rate limit is configured.
242
+ fn __rate_limit_capacity(env: &Env, direction: &Direction, eid: u32) -> i128 {
243
+ RateLimitStorage::rate_limit(env, direction, eid)
244
+ .map(|s| (s.config.limit - calculate_decayed_in_flight(env, &s)).max(0))
245
+ .unwrap_or(i128::MAX)
246
+ }
247
+ }
248
+
249
+ // =========================================================================
250
+ // Helper Functions
251
+ // =========================================================================
252
+
253
+ /// Calculates the current in-flight amount with decay applied.
254
+ /// This is a pure function that doesn't access storage.
255
+ fn calculate_decayed_in_flight(env: &Env, state: &RateLimitState) -> i128 {
256
+ let timestamp = env.ledger().timestamp();
257
+ assert_with_error!(env, timestamp >= state.last_update, RateLimitError::InvalidTimestamp);
258
+
259
+ let elapsed = timestamp - state.last_update;
260
+ let decay = (elapsed as i128) * state.config.limit / (state.config.window_seconds as i128);
261
+
262
+ // Ensure the decayed in-flight amount is not negative
263
+ (state.in_flight_on_last_update - decay).max(0)
200
264
  }