@layerzerolabs/protocol-stellar-v2 0.2.18 → 0.2.20

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 (213) hide show
  1. package/.turbo/turbo-build.log +303 -253
  2. package/.turbo/turbo-lint.log +66 -65
  3. package/.turbo/turbo-test.log +1312 -1282
  4. package/Cargo.lock +21 -8
  5. package/Cargo.toml +2 -0
  6. package/contracts/ERROR_SPEC.md +9 -2
  7. package/contracts/common-macros/src/contract_ttl.rs +18 -7
  8. package/contracts/common-macros/src/lib.rs +4 -4
  9. package/contracts/common-macros/src/tests/contract_ttl.rs +1 -1
  10. package/contracts/common-macros/src/tests/snapshots/common_macros__tests__contract_ttl__snapshot_generated_contractimpl_code.snap +2 -1
  11. package/contracts/common-macros/src/tests/snapshots/common_macros__tests__upgradeable__snapshot_generated_upgradeable_code.snap +7 -12
  12. package/contracts/common-macros/src/upgradeable.rs +15 -21
  13. package/contracts/message-libs/uln-302/src/events.rs +4 -0
  14. package/contracts/message-libs/uln-302/src/send_uln.rs +23 -7
  15. package/contracts/message-libs/uln-302/src/tests/send_uln302/send.rs +38 -64
  16. package/contracts/oapps/counter/Cargo.toml +1 -0
  17. package/contracts/oapps/counter/integration_tests/setup_uln.rs +1 -1
  18. package/contracts/oapps/oapp/src/oapp_receiver.rs +1 -1
  19. package/contracts/oapps/oapp/src/tests/test_oapp_core.rs +113 -65
  20. package/contracts/oapps/oapp/src/tests/test_oapp_options_type3.rs +111 -82
  21. package/contracts/oapps/oapp/src/tests/test_oapp_receiver.rs +293 -65
  22. package/contracts/oapps/oapp/src/tests/test_oapp_sender.rs +331 -56
  23. package/contracts/oapps/oft/Cargo.toml +10 -7
  24. package/contracts/oapps/{oft-std → oft}/integration-tests/extensions/test_oft_fee.rs +3 -4
  25. package/contracts/oapps/{oft-std → oft}/integration-tests/extensions/test_pausable.rs +2 -3
  26. package/contracts/oapps/{oft-std → oft}/integration-tests/extensions/test_rate_limiter.rs +1 -1
  27. package/contracts/oapps/oft/integration-tests/mod.rs +1 -1
  28. package/contracts/oapps/oft/integration-tests/setup.rs +28 -127
  29. package/contracts/oapps/oft/integration-tests/utils.rs +254 -21
  30. package/contracts/oapps/oft/src/extensions/oft_fee.rs +23 -8
  31. package/contracts/oapps/oft/src/extensions/pausable.rs +19 -4
  32. package/contracts/oapps/oft/src/extensions/rate_limiter.rs +52 -28
  33. package/contracts/oapps/oft/src/lib.rs +10 -14
  34. package/contracts/oapps/oft/src/oft.rs +143 -193
  35. package/contracts/oapps/oft/src/oft_types/lock_unlock.rs +9 -11
  36. package/contracts/oapps/oft/src/oft_types/mint_burn.rs +12 -14
  37. package/contracts/oapps/oft/src/oft_types/mod.rs +13 -0
  38. package/contracts/oapps/{oft-std → oft-core}/Cargo.toml +6 -4
  39. package/contracts/oapps/{oft-std → oft-core}/integration-tests/mod.rs +1 -1
  40. package/contracts/oapps/{oft-std → oft-core}/integration-tests/setup.rs +126 -29
  41. package/contracts/oapps/{oft → oft-core}/integration-tests/test_with_sml.rs +3 -3
  42. package/contracts/oapps/oft-core/integration-tests/utils.rs +201 -0
  43. package/contracts/oapps/oft-core/src/lib.rs +18 -0
  44. package/contracts/oapps/oft-core/src/oft_core.rs +479 -0
  45. package/contracts/oapps/{oft → oft-core}/src/tests/mod.rs +0 -2
  46. package/contracts/oapps/{oft → oft-core}/src/tests/test_lz_receive.rs +7 -7
  47. package/contracts/oapps/{oft → oft-core}/src/tests/test_oft_msg_codec.rs +4 -4
  48. package/contracts/oapps/{oft → oft-core}/src/tests/test_resolve_address.rs +3 -3
  49. package/contracts/oapps/{oft → oft-core}/src/tests/test_utils.rs +46 -27
  50. package/contracts/oapps/{oft → oft-core}/src/utils.rs +1 -1
  51. package/contracts/upgrader/src/lib.rs +30 -57
  52. package/contracts/upgrader/src/tests/test_data/test_upgradeable_contract1.wasm +0 -0
  53. package/contracts/upgrader/src/tests/test_data/test_upgradeable_contract2.wasm +0 -0
  54. package/contracts/upgrader/src/tests/test_upgrader.rs +44 -35
  55. package/contracts/utils/src/buffer_reader.rs +1 -0
  56. package/contracts/utils/src/errors.rs +8 -2
  57. package/contracts/utils/src/ownable.rs +125 -3
  58. package/contracts/utils/src/tests/option_ext.rs +1 -1
  59. package/contracts/utils/src/tests/ownable.rs +445 -7
  60. package/contracts/utils/src/tests/ttl_configurable.rs +2 -2
  61. package/contracts/utils/src/tests/upgradeable.rs +372 -175
  62. package/contracts/utils/src/ttl_configurable.rs +3 -3
  63. package/contracts/utils/src/upgradeable.rs +48 -23
  64. package/contracts/workers/dvn/Cargo.toml +1 -0
  65. package/contracts/workers/dvn/src/auth.rs +12 -42
  66. package/contracts/workers/dvn/src/dvn.rs +16 -31
  67. package/contracts/workers/dvn/src/errors.rs +0 -1
  68. package/contracts/workers/dvn/src/interfaces/dvn.rs +35 -0
  69. package/contracts/workers/dvn/src/lib.rs +4 -3
  70. package/contracts/workers/dvn/src/tests/auth.rs +1 -1
  71. package/contracts/workers/dvn/src/tests/dvn.rs +19 -15
  72. package/contracts/workers/dvn/src/tests/multisig/set_threshold.rs +2 -4
  73. package/contracts/workers/dvn/src/tests/multisig/verify_signatures.rs +1 -3
  74. package/contracts/workers/dvn/src/tests/setup.rs +5 -9
  75. package/contracts/workers/dvn-fee-lib/Cargo.toml +1 -1
  76. package/contracts/workers/dvn-fee-lib/src/dvn_fee_lib.rs +3 -5
  77. package/contracts/workers/dvn-fee-lib/src/tests/dvn_fee_lib.rs +2 -3
  78. package/contracts/workers/executor/Cargo.toml +1 -0
  79. package/contracts/workers/executor/src/executor.rs +15 -26
  80. package/contracts/workers/executor-fee-lib/Cargo.toml +2 -1
  81. package/contracts/workers/executor-fee-lib/src/executor_fee_lib.rs +63 -5
  82. package/contracts/workers/executor-fee-lib/src/executor_option.rs +28 -1
  83. package/contracts/workers/executor-fee-lib/src/lib.rs +3 -0
  84. package/contracts/workers/executor-fee-lib/src/tests/executor_fee_lib.rs +701 -0
  85. package/contracts/workers/executor-fee-lib/src/tests/executor_option.rs +370 -0
  86. package/contracts/workers/executor-fee-lib/src/tests/mod.rs +4 -0
  87. package/contracts/workers/executor-fee-lib/src/tests/setup.rs +60 -0
  88. package/contracts/workers/executor-helper/src/lib.rs +3 -0
  89. package/contracts/workers/executor-helper/src/tests/executor_helper.rs +184 -0
  90. package/contracts/workers/executor-helper/src/tests/mod.rs +2 -0
  91. package/contracts/workers/executor-helper/src/tests/setup.rs +366 -0
  92. package/contracts/workers/fee-lib-interfaces/Cargo.toml +14 -0
  93. package/contracts/workers/{worker/src/interfaces/mod.rs → fee-lib-interfaces/src/lib.rs} +4 -3
  94. package/contracts/workers/price-feed/Cargo.toml +2 -1
  95. package/contracts/workers/price-feed/src/events.rs +1 -1
  96. package/contracts/workers/price-feed/src/lib.rs +3 -0
  97. package/contracts/workers/price-feed/src/price_feed.rs +6 -12
  98. package/contracts/workers/price-feed/src/storage.rs +1 -1
  99. package/contracts/workers/price-feed/src/tests/mod.rs +2 -0
  100. package/contracts/workers/price-feed/src/tests/price_feed.rs +869 -0
  101. package/contracts/workers/price-feed/src/tests/setup.rs +70 -0
  102. package/contracts/workers/price-feed/src/types.rs +1 -1
  103. package/contracts/workers/worker/src/errors.rs +0 -3
  104. package/contracts/workers/worker/src/lib.rs +0 -2
  105. package/contracts/workers/worker/src/storage.rs +32 -29
  106. package/contracts/workers/worker/src/tests/setup.rs +1 -7
  107. package/contracts/workers/worker/src/tests/worker.rs +50 -42
  108. package/contracts/workers/worker/src/worker.rs +49 -58
  109. package/package.json +4 -5
  110. package/sdk/.turbo/turbo-test.log +229 -217
  111. package/sdk/dist/generated/bml.d.ts +39 -1
  112. package/sdk/dist/generated/bml.js +33 -8
  113. package/sdk/dist/generated/counter.d.ts +131 -3
  114. package/sdk/dist/generated/counter.js +41 -10
  115. package/sdk/dist/generated/dvn.d.ts +431 -362
  116. package/sdk/dist/generated/dvn.js +80 -55
  117. package/sdk/dist/generated/dvn_fee_lib.d.ts +327 -251
  118. package/sdk/dist/generated/dvn_fee_lib.js +55 -57
  119. package/sdk/dist/generated/endpoint.d.ts +131 -3
  120. package/sdk/dist/generated/endpoint.js +41 -10
  121. package/sdk/dist/generated/executor.d.ts +503 -339
  122. package/sdk/dist/generated/executor.js +80 -48
  123. package/sdk/dist/generated/executor_fee_lib.d.ts +395 -319
  124. package/sdk/dist/generated/executor_fee_lib.js +54 -56
  125. package/sdk/dist/generated/executor_helper.d.ts +53 -187
  126. package/sdk/dist/generated/executor_helper.js +47 -29
  127. package/sdk/dist/generated/layerzero_view.d.ts +1271 -0
  128. package/sdk/dist/generated/layerzero_view.js +294 -0
  129. package/sdk/dist/generated/oft.d.ts +1851 -0
  130. package/sdk/dist/generated/oft.js +347 -0
  131. package/sdk/dist/generated/price_feed.d.ts +329 -253
  132. package/sdk/dist/generated/price_feed.js +55 -57
  133. package/sdk/dist/generated/sml.d.ts +131 -3
  134. package/sdk/dist/generated/sml.js +41 -10
  135. package/sdk/dist/generated/treasury.d.ts +131 -3
  136. package/sdk/dist/generated/treasury.js +41 -10
  137. package/sdk/dist/generated/uln302.d.ts +131 -3
  138. package/sdk/dist/generated/uln302.js +43 -12
  139. package/sdk/dist/generated/upgrader.d.ts +201 -15
  140. package/sdk/dist/generated/upgrader.js +99 -1
  141. package/sdk/dist/index.d.ts +2 -2
  142. package/sdk/dist/index.js +3 -3
  143. package/sdk/package.json +3 -2
  144. package/sdk/src/index.ts +3 -3
  145. package/sdk/test/oft-sml.test.ts +20 -20
  146. package/sdk/test/upgrader.test.ts +2 -3
  147. package/sdk/turbo.json +8 -0
  148. package/tools/ts-bindings-gen/Cargo.toml +2 -0
  149. package/tools/ts-bindings-gen/src/main.rs +53 -5
  150. package/turbo.json +0 -2
  151. package/contracts/oapps/oft/src/interfaces/mint_burn_token.rs +0 -23
  152. package/contracts/oapps/oft/src/interfaces/mod.rs +0 -3
  153. package/contracts/oapps/oft/src/oft_impl.rs +0 -201
  154. package/contracts/oapps/oft/src/tests/extensions/mod.rs +0 -11
  155. package/contracts/oapps/oft/src/tests/extensions/setup.rs +0 -917
  156. package/contracts/oapps/oft/src/tests/extensions/test_oft_fee.rs +0 -751
  157. package/contracts/oapps/oft/src/tests/extensions/test_pausable.rs +0 -434
  158. package/contracts/oapps/oft/src/tests/extensions/test_rate_limiter.rs +0 -1080
  159. package/contracts/oapps/oft-std/integration-tests/utils.rs +0 -427
  160. package/contracts/oapps/oft-std/src/lib.rs +0 -16
  161. package/contracts/oapps/oft-std/src/oft.rs +0 -174
  162. package/sdk/dist/generated/oft_std.d.ts +0 -1722
  163. package/sdk/dist/generated/oft_std.js +0 -316
  164. package/sdk/dist/wasm/blocked-message-lib.d.ts +0 -1
  165. package/sdk/dist/wasm/blocked-message-lib.js +0 -2
  166. package/sdk/dist/wasm/counter.d.ts +0 -1
  167. package/sdk/dist/wasm/counter.js +0 -2
  168. package/sdk/dist/wasm/dvn-fee-lib.d.ts +0 -1
  169. package/sdk/dist/wasm/dvn-fee-lib.js +0 -2
  170. package/sdk/dist/wasm/dvn.d.ts +0 -1
  171. package/sdk/dist/wasm/dvn.js +0 -2
  172. package/sdk/dist/wasm/endpoint-v2.d.ts +0 -1
  173. package/sdk/dist/wasm/endpoint-v2.js +0 -2
  174. package/sdk/dist/wasm/executor-fee-lib.d.ts +0 -1
  175. package/sdk/dist/wasm/executor-fee-lib.js +0 -2
  176. package/sdk/dist/wasm/executor-helper.d.ts +0 -1
  177. package/sdk/dist/wasm/executor-helper.js +0 -2
  178. package/sdk/dist/wasm/executor.d.ts +0 -1
  179. package/sdk/dist/wasm/executor.js +0 -2
  180. package/sdk/dist/wasm/layerzero-views.d.ts +0 -1
  181. package/sdk/dist/wasm/layerzero-views.js +0 -2
  182. package/sdk/dist/wasm/oft-std.d.ts +0 -1
  183. package/sdk/dist/wasm/oft-std.js +0 -2
  184. package/sdk/dist/wasm/price-feed.d.ts +0 -1
  185. package/sdk/dist/wasm/price-feed.js +0 -2
  186. package/sdk/dist/wasm/simple-message-lib.d.ts +0 -1
  187. package/sdk/dist/wasm/simple-message-lib.js +0 -2
  188. package/sdk/dist/wasm/treasury.d.ts +0 -1
  189. package/sdk/dist/wasm/treasury.js +0 -2
  190. package/sdk/dist/wasm/uln302.d.ts +0 -1
  191. package/sdk/dist/wasm/uln302.js +0 -2
  192. package/sdk/dist/wasm/upgrader.d.ts +0 -1
  193. package/sdk/dist/wasm/upgrader.js +0 -2
  194. package/sdk/dist/wasm.d.ts +0 -15
  195. package/sdk/dist/wasm.js +0 -15
  196. /package/contracts/oapps/{oft-std → oft}/integration-tests/extensions/mod.rs +0 -0
  197. /package/contracts/oapps/{oft → oft-core}/src/codec/mod.rs +0 -0
  198. /package/contracts/oapps/{oft → oft-core}/src/codec/oft_compose_msg_codec.rs +0 -0
  199. /package/contracts/oapps/{oft → oft-core}/src/codec/oft_msg_codec.rs +0 -0
  200. /package/contracts/oapps/{oft → oft-core}/src/errors.rs +0 -0
  201. /package/contracts/oapps/{oft → oft-core}/src/events.rs +0 -0
  202. /package/contracts/oapps/{oft → oft-core}/src/storage.rs +0 -0
  203. /package/contracts/oapps/{oft → oft-core}/src/tests/test_decimals.rs +0 -0
  204. /package/contracts/oapps/{oft → oft-core}/src/tests/test_oft_compose_msg_codec.rs +0 -0
  205. /package/contracts/oapps/{oft → oft-core}/src/tests/test_oft_version.rs +0 -0
  206. /package/contracts/oapps/{oft → oft-core}/src/tests/test_quote_oft.rs +0 -0
  207. /package/contracts/oapps/{oft → oft-core}/src/tests/test_quote_send.rs +0 -0
  208. /package/contracts/oapps/{oft → oft-core}/src/tests/test_send.rs +0 -0
  209. /package/contracts/oapps/{oft → oft-core}/src/tests/test_token.rs +0 -0
  210. /package/contracts/oapps/{oft → oft-core}/src/types.rs +0 -0
  211. /package/contracts/workers/{worker/src/interfaces → fee-lib-interfaces/src}/dvn_fee_lib.rs +0 -0
  212. /package/contracts/workers/{worker/src/interfaces → fee-lib-interfaces/src}/executor_fee_lib.rs +0 -0
  213. /package/contracts/workers/{worker/src/interfaces → fee-lib-interfaces/src}/price_feed.rs +0 -0
@@ -1,1080 +0,0 @@
1
- //! Tests for the rate limiter extension functionality.
2
- //!
3
- //! Tests verify:
4
- //! - Rate limit configuration (set/unset)
5
- //! - Capacity tracking and decay over time
6
- //! - Rate limit enforcement on send (outbound)
7
- //! - Rate limit enforcement on lz_receive (inbound)
8
- //! - Capacity release on opposite direction
9
-
10
- use crate::{
11
- extensions::rate_limiter::{Direction, RateLimitError},
12
- tests::test_utils::{create_origin, create_recipient_address, encode_oft_message},
13
- utils::address_to_bytes32,
14
- };
15
- use endpoint_v2::MessagingFee;
16
- use soroban_sdk::{
17
- testutils::{Address as _, Ledger as _},
18
- Address, Bytes, BytesN, Env, IntoVal,
19
- };
20
-
21
- use super::setup::ExtensiveOFTTestSetup;
22
-
23
- // ==================== Rate Limit Configuration Tests ====================
24
-
25
- #[test]
26
- fn test_initial_no_rate_limit() {
27
- let env = Env::default();
28
- let setup = ExtensiveOFTTestSetup::new(&env);
29
-
30
- let dst_eid = 100u32;
31
-
32
- // No rate limit configured, capacity should be MAX
33
- let capacity = setup.rate_limit_capacity(&Direction::Outbound, dst_eid);
34
- assert_eq!(capacity, i128::MAX);
35
-
36
- let (limit, window) = setup.rate_limit_config(&Direction::Outbound, dst_eid);
37
- assert_eq!(limit, 0);
38
- assert_eq!(window, 0);
39
- }
40
-
41
- #[test]
42
- fn test_set_rate_limit_outbound() {
43
- let env = Env::default();
44
- let setup = ExtensiveOFTTestSetup::new(&env);
45
-
46
- let dst_eid = 100u32;
47
- let limit = 1_000_000i128;
48
- let window_seconds = 3600u64; // 1 hour
49
-
50
- setup.set_rate_limit(&Direction::Outbound, dst_eid, limit, window_seconds);
51
-
52
- let (stored_limit, stored_window) = setup.rate_limit_config(&Direction::Outbound, dst_eid);
53
- assert_eq!(stored_limit, limit);
54
- assert_eq!(stored_window, window_seconds);
55
- }
56
-
57
- #[test]
58
- fn test_set_rate_limit_inbound() {
59
- let env = Env::default();
60
- let setup = ExtensiveOFTTestSetup::new(&env);
61
-
62
- let src_eid = 100u32;
63
- let limit = 2_000_000i128;
64
- let window_seconds = 7200u64; // 2 hours
65
-
66
- setup.set_rate_limit(&Direction::Inbound, src_eid, limit, window_seconds);
67
-
68
- let (stored_limit, stored_window) = setup.rate_limit_config(&Direction::Inbound, src_eid);
69
- assert_eq!(stored_limit, limit);
70
- assert_eq!(stored_window, window_seconds);
71
- }
72
-
73
- #[test]
74
- fn test_set_rate_limit_both_directions() {
75
- let env = Env::default();
76
- let setup = ExtensiveOFTTestSetup::new(&env);
77
-
78
- let eid = 100u32;
79
-
80
- setup.set_rate_limit(&Direction::Outbound, eid, 1_000_000, 3600);
81
- setup.set_rate_limit(&Direction::Inbound, eid, 2_000_000, 7200);
82
-
83
- let (outbound_limit, outbound_window) = setup.rate_limit_config(&Direction::Outbound, eid);
84
- let (inbound_limit, inbound_window) = setup.rate_limit_config(&Direction::Inbound, eid);
85
-
86
- assert_eq!(outbound_limit, 1_000_000);
87
- assert_eq!(outbound_window, 3600);
88
- assert_eq!(inbound_limit, 2_000_000);
89
- assert_eq!(inbound_window, 7200);
90
- }
91
-
92
- #[test]
93
- fn test_set_rate_limit_zero_limit_fails() {
94
- let env = Env::default();
95
- let setup = ExtensiveOFTTestSetup::new(&env);
96
-
97
- let result = setup.try_set_rate_limit(&Direction::Outbound, 100, 0, 3600);
98
- assert_eq!(result.err().unwrap().ok().unwrap(), RateLimitError::InvalidLimit.into());
99
- }
100
-
101
- #[test]
102
- fn test_set_rate_limit_zero_window_fails() {
103
- let env = Env::default();
104
- let setup = ExtensiveOFTTestSetup::new(&env);
105
-
106
- let result = setup.try_set_rate_limit(&Direction::Outbound, 100, 1_000_000, 0);
107
- assert_eq!(result.err().unwrap().ok().unwrap(), RateLimitError::InvalidWindowSeconds.into());
108
- }
109
-
110
- #[test]
111
- fn test_set_rate_limit_same_values_fails() {
112
- let env = Env::default();
113
- let setup = ExtensiveOFTTestSetup::new(&env);
114
-
115
- let dst_eid = 100u32;
116
- let limit = 1_000_000i128;
117
- let window_seconds = 3600u64;
118
-
119
- // Set rate limit
120
- setup.set_rate_limit(&Direction::Outbound, dst_eid, limit, window_seconds);
121
-
122
- // Try to set same values again - should fail with SameValue
123
- let result = setup.try_set_rate_limit(&Direction::Outbound, dst_eid, limit, window_seconds);
124
- assert_eq!(result.err().unwrap().ok().unwrap(), RateLimitError::SameValue.into());
125
- }
126
-
127
- #[test]
128
- fn test_unset_rate_limit() {
129
- let env = Env::default();
130
- let setup = ExtensiveOFTTestSetup::new(&env);
131
-
132
- let dst_eid = 100u32;
133
- setup.set_rate_limit(&Direction::Outbound, dst_eid, 1_000_000, 3600);
134
-
135
- let (limit, _) = setup.rate_limit_config(&Direction::Outbound, dst_eid);
136
- assert_eq!(limit, 1_000_000);
137
-
138
- setup.unset_rate_limit(&Direction::Outbound, dst_eid);
139
-
140
- let (limit_after, _) = setup.rate_limit_config(&Direction::Outbound, dst_eid);
141
- assert_eq!(limit_after, 0);
142
- }
143
-
144
- #[test]
145
- fn test_unset_rate_limit_not_found() {
146
- let env = Env::default();
147
- let setup = ExtensiveOFTTestSetup::new(&env);
148
-
149
- let dst_eid = 100u32;
150
-
151
- // Try to unset rate limit that doesn't exist - should fail with SameValue
152
- let result = setup.try_unset_rate_limit(&Direction::Outbound, dst_eid);
153
- assert_eq!(result.err().unwrap().ok().unwrap(), RateLimitError::SameValue.into());
154
- }
155
-
156
- #[test]
157
- fn test_update_rate_limit() {
158
- let env = Env::default();
159
- let setup = ExtensiveOFTTestSetup::new(&env);
160
-
161
- let dst_eid = 100u32;
162
- setup.set_rate_limit(&Direction::Outbound, dst_eid, 1_000_000, 3600);
163
-
164
- // Update to new values
165
- setup.set_rate_limit(&Direction::Outbound, dst_eid, 2_000_000, 7200);
166
-
167
- let (limit, window) = setup.rate_limit_config(&Direction::Outbound, dst_eid);
168
- assert_eq!(limit, 2_000_000);
169
- assert_eq!(window, 7200);
170
- }
171
-
172
- #[test]
173
- fn test_update_rate_limit_only_limit() {
174
- let env = Env::default();
175
- let setup = ExtensiveOFTTestSetup::new(&env);
176
-
177
- let dst_eid = 100u32;
178
- setup.set_rate_limit(&Direction::Outbound, dst_eid, 1_000_000, 3600);
179
-
180
- // Update only the limit (keep same window)
181
- setup.set_rate_limit(&Direction::Outbound, dst_eid, 2_000_000, 3600);
182
-
183
- let (limit, window) = setup.rate_limit_config(&Direction::Outbound, dst_eid);
184
- assert_eq!(limit, 2_000_000);
185
- assert_eq!(window, 3600);
186
- }
187
-
188
- #[test]
189
- fn test_update_rate_limit_only_window() {
190
- let env = Env::default();
191
- let setup = ExtensiveOFTTestSetup::new(&env);
192
-
193
- let dst_eid = 100u32;
194
- setup.set_rate_limit(&Direction::Outbound, dst_eid, 1_000_000, 3600);
195
-
196
- // Update only the window (keep same limit)
197
- setup.set_rate_limit(&Direction::Outbound, dst_eid, 1_000_000, 7200);
198
-
199
- let (limit, window) = setup.rate_limit_config(&Direction::Outbound, dst_eid);
200
- assert_eq!(limit, 1_000_000);
201
- assert_eq!(window, 7200);
202
- }
203
-
204
- // ==================== Capacity Tests ====================
205
-
206
- #[test]
207
- fn test_initial_capacity_equals_limit() {
208
- let env = Env::default();
209
- let setup = ExtensiveOFTTestSetup::new(&env);
210
-
211
- let dst_eid = 100u32;
212
- let limit = 1_000_000i128;
213
- setup.set_rate_limit(&Direction::Outbound, dst_eid, limit, 3600);
214
-
215
- let capacity = setup.rate_limit_capacity(&Direction::Outbound, dst_eid);
216
- assert_eq!(capacity, limit);
217
- }
218
-
219
- #[test]
220
- fn test_in_flight_initially_zero() {
221
- let env = Env::default();
222
- let setup = ExtensiveOFTTestSetup::new(&env);
223
-
224
- let dst_eid = 100u32;
225
- setup.set_rate_limit(&Direction::Outbound, dst_eid, 1_000_000, 3600);
226
-
227
- let in_flight = setup.rate_limit_in_flight(&Direction::Outbound, dst_eid);
228
- assert_eq!(in_flight, 0);
229
- }
230
-
231
- #[test]
232
- fn test_rate_limit_in_flight_no_rate_limit_returns_zero() {
233
- let env = Env::default();
234
- let setup = ExtensiveOFTTestSetup::new(&env);
235
-
236
- let dst_eid = 100u32;
237
-
238
- // No rate limit set - in_flight should return 0
239
- let in_flight = setup.rate_limit_in_flight(&Direction::Outbound, dst_eid);
240
- assert_eq!(in_flight, 0);
241
- }
242
-
243
- #[test]
244
- fn test_rate_limit_capacity_zero_when_in_flight_exceeds_limit() {
245
- let env = Env::default();
246
- let setup = ExtensiveOFTTestSetup::new(&env);
247
-
248
- // Set fee deposit address (required by ExtensiveOFT)
249
- let fee_collector = create_recipient_address(&env);
250
- setup.set_fee_deposit_address(&fee_collector);
251
-
252
- let sender = Address::generate(&env);
253
- let dst_eid = 100u32;
254
- let peer = BytesN::from_array(&env, &[2u8; 32]);
255
- setup.set_peer(dst_eid, &peer);
256
-
257
- // Set outbound rate limit
258
- let limit = 1_000_000i128;
259
- setup.set_rate_limit(&Direction::Outbound, dst_eid, limit, 3600);
260
-
261
- // Use all capacity
262
- let amount_ld = limit;
263
- setup.fund_tokens(&sender, amount_ld);
264
- setup.fund_native_fees(&sender, setup.native_fee);
265
-
266
- let send_param = setup.create_send_param(dst_eid, amount_ld, amount_ld);
267
- let fee = MessagingFee { native_fee: setup.native_fee, zro_fee: 0 };
268
- let oft_receipt = setup.quote_oft(&send_param);
269
- setup.send(&sender, &send_param, &fee, &sender, &oft_receipt);
270
-
271
- // Capacity should be exactly 0 after using all of it
272
- let capacity = setup.rate_limit_capacity(&Direction::Outbound, dst_eid);
273
- assert_eq!(capacity, 0);
274
-
275
- // In-flight should equal limit
276
- let in_flight = setup.rate_limit_in_flight(&Direction::Outbound, dst_eid);
277
- assert_eq!(in_flight, limit);
278
- }
279
-
280
- #[test]
281
- fn test_update_rate_limit_preserves_in_flight() {
282
- let env = Env::default();
283
- let setup = ExtensiveOFTTestSetup::new(&env);
284
-
285
- // Set fee deposit address (required by ExtensiveOFT)
286
- let fee_collector = create_recipient_address(&env);
287
- setup.set_fee_deposit_address(&fee_collector);
288
-
289
- let sender = Address::generate(&env);
290
- let dst_eid = 100u32;
291
- let peer = BytesN::from_array(&env, &[2u8; 32]);
292
- setup.set_peer(dst_eid, &peer);
293
-
294
- // Set initial rate limit
295
- setup.set_rate_limit(&Direction::Outbound, dst_eid, 1_000_000, 3600);
296
-
297
- // Use some capacity
298
- let amount_ld = 300_000i128;
299
- setup.fund_tokens(&sender, amount_ld);
300
- setup.fund_native_fees(&sender, setup.native_fee);
301
-
302
- let send_param = setup.create_send_param(dst_eid, amount_ld, amount_ld);
303
- let fee = MessagingFee { native_fee: setup.native_fee, zro_fee: 0 };
304
- let oft_receipt = setup.quote_oft(&send_param);
305
- setup.send(&sender, &send_param, &fee, &sender, &oft_receipt);
306
-
307
- // In-flight should be 300_000
308
- let in_flight_before = setup.rate_limit_in_flight(&Direction::Outbound, dst_eid);
309
- assert_eq!(in_flight_before, amount_ld);
310
-
311
- // Update rate limit to higher value
312
- setup.set_rate_limit(&Direction::Outbound, dst_eid, 2_000_000, 3600);
313
-
314
- // In-flight should be preserved (checkpointed)
315
- let in_flight_after = setup.rate_limit_in_flight(&Direction::Outbound, dst_eid);
316
- assert_eq!(in_flight_after, amount_ld);
317
-
318
- // New capacity should be new_limit - in_flight
319
- let capacity = setup.rate_limit_capacity(&Direction::Outbound, dst_eid);
320
- assert_eq!(capacity, 2_000_000 - amount_ld);
321
- }
322
-
323
- // ==================== Capacity Decay Tests ====================
324
-
325
- #[test]
326
- fn test_rate_limit_invalid_timestamp_fails() {
327
- let env = Env::default();
328
- let setup = ExtensiveOFTTestSetup::new(&env);
329
-
330
- let dst_eid = 100u32;
331
-
332
- // Start with a timestamp in the future
333
- setup.advance_time(10000);
334
-
335
- // Set rate limit (last_update will be set to current timestamp: 10000)
336
- setup.set_rate_limit(&Direction::Outbound, dst_eid, 1_000_000, 3600);
337
-
338
- // Move time backwards by modifying ledger info directly
339
- env.ledger().with_mut(|li| {
340
- li.timestamp = 5000; // Earlier than last_update
341
- });
342
-
343
- // Querying in_flight should fail because timestamp < last_update
344
- let result = setup.try_rate_limit_in_flight(&Direction::Outbound, dst_eid);
345
- assert!(result.is_err());
346
- let err = result.err().unwrap().ok().unwrap();
347
- assert_eq!(err, RateLimitError::InvalidTimestamp.into());
348
- }
349
-
350
- #[test]
351
- fn test_consume_rate_limit_when_capacity_exactly_zero() {
352
- let env = Env::default();
353
- let setup = ExtensiveOFTTestSetup::new(&env);
354
-
355
- // Set fee deposit address (required by ExtensiveOFT)
356
- let fee_collector = create_recipient_address(&env);
357
- setup.set_fee_deposit_address(&fee_collector);
358
-
359
- let sender = Address::generate(&env);
360
- let dst_eid = 100u32;
361
- let peer = BytesN::from_array(&env, &[2u8; 32]);
362
- setup.set_peer(dst_eid, &peer);
363
-
364
- // Set outbound rate limit
365
- let limit = 1_000_000i128;
366
- setup.set_rate_limit(&Direction::Outbound, dst_eid, limit, 3600);
367
-
368
- // Use all capacity
369
- setup.fund_tokens(&sender, limit);
370
- setup.fund_native_fees(&sender, setup.native_fee);
371
-
372
- let send_param = setup.create_send_param(dst_eid, limit, limit);
373
- let fee = MessagingFee { native_fee: setup.native_fee, zro_fee: 0 };
374
- let oft_receipt = setup.quote_oft(&send_param);
375
- setup.send(&sender, &send_param, &fee, &sender, &oft_receipt);
376
-
377
- // Capacity should be 0 now (in_flight equals limit)
378
- assert_eq!(setup.rate_limit_capacity(&Direction::Outbound, dst_eid), 0);
379
-
380
- // Try to send a small amount - should fail because capacity is exactly 0
381
- // This tests the branch where capacity = 0 (limit <= in_flight) in __consume_rate_limit_capacity
382
- // Use amount that survives dust removal (conversion_rate = 10)
383
- let small_amount = 100i128;
384
- setup.fund_tokens(&sender, small_amount);
385
- setup.fund_native_fees(&sender, setup.native_fee);
386
-
387
- // quote_oft will report the limit in OFTLimit but won't enforce
388
- let send_param_2 = setup.create_send_param(dst_eid, small_amount, small_amount);
389
- let oft_receipt_2 = setup.quote_oft(&send_param_2);
390
-
391
- // send will fail because capacity is 0
392
- let result = setup.try_send(&sender, &send_param_2, &fee, &sender, &oft_receipt_2);
393
- assert_eq!(result.err().unwrap().ok().unwrap(), RateLimitError::ExceededRateLimit.into());
394
- }
395
-
396
- #[test]
397
- fn test_capacity_recovers_over_time() {
398
- let env = Env::default();
399
- let setup = ExtensiveOFTTestSetup::new(&env);
400
-
401
- // Set fee deposit address (required by ExtensiveOFT)
402
- let fee_collector = create_recipient_address(&env);
403
- setup.set_fee_deposit_address(&fee_collector);
404
-
405
- let sender = Address::generate(&env);
406
- let dst_eid = 100u32;
407
- let peer = BytesN::from_array(&env, &[2u8; 32]);
408
- setup.set_peer(dst_eid, &peer);
409
-
410
- // Set outbound rate limit: 1_000_000 per hour (3600 seconds)
411
- let limit = 1_000_000i128;
412
- let window_seconds = 3600u64;
413
- setup.set_rate_limit(&Direction::Outbound, dst_eid, limit, window_seconds);
414
-
415
- // Use all capacity
416
- let amount_ld = limit;
417
- setup.fund_tokens(&sender, amount_ld);
418
- setup.fund_native_fees(&sender, setup.native_fee);
419
-
420
- let send_param = setup.create_send_param(dst_eid, amount_ld, amount_ld);
421
- let fee = MessagingFee { native_fee: setup.native_fee, zro_fee: 0 };
422
- let oft_receipt = setup.quote_oft(&send_param);
423
- setup.send(&sender, &send_param, &fee, &sender, &oft_receipt);
424
-
425
- // Capacity should be 0
426
- assert_eq!(setup.rate_limit_capacity(&Direction::Outbound, dst_eid), 0);
427
-
428
- // Advance time by half the window (1800 seconds)
429
- setup.advance_time(1800);
430
-
431
- // Capacity should recover to ~50%
432
- let capacity_after_half = setup.rate_limit_capacity(&Direction::Outbound, dst_eid);
433
- assert!(capacity_after_half >= 490_000 && capacity_after_half <= 510_000);
434
-
435
- // Advance time by another half (full window elapsed)
436
- setup.advance_time(1800);
437
-
438
- // Capacity should be fully recovered
439
- let capacity_after_full = setup.rate_limit_capacity(&Direction::Outbound, dst_eid);
440
- assert_eq!(capacity_after_full, limit);
441
- }
442
-
443
- #[test]
444
- fn test_send_after_capacity_recovery() {
445
- let env = Env::default();
446
- let setup = ExtensiveOFTTestSetup::new(&env);
447
-
448
- // Set fee deposit address (required by ExtensiveOFT)
449
- let fee_collector = create_recipient_address(&env);
450
- setup.set_fee_deposit_address(&fee_collector);
451
-
452
- let sender = Address::generate(&env);
453
- let dst_eid = 100u32;
454
- let peer = BytesN::from_array(&env, &[2u8; 32]);
455
- setup.set_peer(dst_eid, &peer);
456
-
457
- // Set outbound rate limit
458
- let limit = 1_000_000i128;
459
- let window_seconds = 3600u64;
460
- setup.set_rate_limit(&Direction::Outbound, dst_eid, limit, window_seconds);
461
-
462
- // Use all capacity
463
- setup.fund_tokens(&sender, limit);
464
- setup.fund_native_fees(&sender, setup.native_fee);
465
-
466
- let send_param = setup.create_send_param(dst_eid, limit, limit);
467
- let fee = MessagingFee { native_fee: setup.native_fee, zro_fee: 0 };
468
- let oft_receipt = setup.quote_oft(&send_param);
469
- setup.send(&sender, &send_param, &fee, &sender, &oft_receipt);
470
-
471
- // Advance time to fully recover
472
- setup.advance_time(window_seconds);
473
-
474
- // Should be able to send again
475
- setup.fund_tokens(&sender, limit);
476
- setup.fund_native_fees(&sender, setup.native_fee);
477
-
478
- let send_param_2 = setup.create_send_param(dst_eid, limit, limit);
479
- let oft_receipt_2 = setup.quote_oft(&send_param_2);
480
- let (msg_receipt, _) = setup.send(&sender, &send_param_2, &fee, &sender, &oft_receipt_2);
481
- assert!(msg_receipt.nonce > 0);
482
- }
483
-
484
- // ==================== Send Tests (Outbound Rate Limit) ====================
485
-
486
- #[test]
487
- fn test_send_within_rate_limit() {
488
- let env = Env::default();
489
- let setup = ExtensiveOFTTestSetup::new(&env);
490
-
491
- // Set fee deposit address (required by ExtensiveOFT)
492
- let fee_collector = create_recipient_address(&env);
493
- setup.set_fee_deposit_address(&fee_collector);
494
-
495
- let sender = Address::generate(&env);
496
- let dst_eid = 100u32;
497
- let peer = BytesN::from_array(&env, &[2u8; 32]);
498
- setup.set_peer(dst_eid, &peer);
499
-
500
- // Set outbound rate limit
501
- let limit = 1_000_000i128;
502
- setup.set_rate_limit(&Direction::Outbound, dst_eid, limit, 3600);
503
-
504
- let amount_ld = 500_000i128; // Within limit
505
- setup.fund_tokens(&sender, amount_ld);
506
- setup.fund_native_fees(&sender, setup.native_fee);
507
-
508
- let send_param = setup.create_send_param(dst_eid, amount_ld, amount_ld);
509
- let fee = MessagingFee { native_fee: setup.native_fee, zro_fee: 0 };
510
- let oft_receipt = setup.quote_oft(&send_param);
511
-
512
- // Should succeed within limit
513
- let (msg_receipt, _) = setup.send(&sender, &send_param, &fee, &sender, &oft_receipt);
514
- assert!(msg_receipt.nonce > 0);
515
-
516
- // Check capacity decreased
517
- let capacity = setup.rate_limit_capacity(&Direction::Outbound, dst_eid);
518
- assert_eq!(capacity, limit - amount_ld);
519
- }
520
-
521
- #[test]
522
- fn test_send_exactly_at_rate_limit() {
523
- let env = Env::default();
524
- let setup = ExtensiveOFTTestSetup::new(&env);
525
-
526
- // Set fee deposit address (required by ExtensiveOFT)
527
- let fee_collector = create_recipient_address(&env);
528
- setup.set_fee_deposit_address(&fee_collector);
529
-
530
- let sender = Address::generate(&env);
531
- let dst_eid = 100u32;
532
- let peer = BytesN::from_array(&env, &[2u8; 32]);
533
- setup.set_peer(dst_eid, &peer);
534
-
535
- // Set outbound rate limit
536
- let limit = 1_000_000i128;
537
- setup.set_rate_limit(&Direction::Outbound, dst_eid, limit, 3600);
538
-
539
- let amount_ld = limit; // Exactly at limit
540
- setup.fund_tokens(&sender, amount_ld);
541
- setup.fund_native_fees(&sender, setup.native_fee);
542
-
543
- let send_param = setup.create_send_param(dst_eid, amount_ld, amount_ld);
544
- let fee = MessagingFee { native_fee: setup.native_fee, zro_fee: 0 };
545
- let oft_receipt = setup.quote_oft(&send_param);
546
-
547
- // Should succeed at exactly limit
548
- let (msg_receipt, _) = setup.send(&sender, &send_param, &fee, &sender, &oft_receipt);
549
- assert!(msg_receipt.nonce > 0);
550
-
551
- // Capacity should be zero
552
- let capacity = setup.rate_limit_capacity(&Direction::Outbound, dst_eid);
553
- assert_eq!(capacity, 0);
554
- }
555
-
556
- #[test]
557
- fn test_send_exceeds_rate_limit() {
558
- let env = Env::default();
559
- let setup = ExtensiveOFTTestSetup::new(&env);
560
-
561
- // Set fee deposit address (required by ExtensiveOFT)
562
- let fee_collector = create_recipient_address(&env);
563
- setup.set_fee_deposit_address(&fee_collector);
564
-
565
- let sender = Address::generate(&env);
566
- let dst_eid = 100u32;
567
- let peer = BytesN::from_array(&env, &[2u8; 32]);
568
- setup.set_peer(dst_eid, &peer);
569
-
570
- // Set outbound rate limit
571
- let limit = 1_000_000i128;
572
- setup.set_rate_limit(&Direction::Outbound, dst_eid, limit, 3600);
573
-
574
- // Use amount that exceeds limit and use min_amount_ld = 0 to avoid slippage errors
575
- let amount_ld = limit + 10; // Exceeds limit (use +10 to account for dust)
576
- setup.fund_tokens(&sender, amount_ld);
577
- setup.fund_native_fees(&sender, setup.native_fee);
578
-
579
- // Use min_amount_ld = 0 to avoid slippage check interference
580
- let send_param = setup.create_send_param(dst_eid, amount_ld, 0);
581
- let fee = MessagingFee { native_fee: setup.native_fee, zro_fee: 0 };
582
-
583
- // quote_oft returns the limit but doesn't enforce - the send will fail
584
- let (oft_limit, _, _) = setup.oft.quote_oft(&send_param);
585
- assert_eq!(oft_limit.max_amount_ld, limit); // Capacity is 1_000_000
586
-
587
- let oft_receipt = setup.quote_oft(&send_param);
588
-
589
- // Should fail at send - amount exceeds rate limit
590
- let result = setup.try_send(&sender, &send_param, &fee, &sender, &oft_receipt);
591
- assert_eq!(result.err().unwrap().ok().unwrap(), RateLimitError::ExceededRateLimit.into());
592
- }
593
-
594
- #[test]
595
- fn test_send_multiple_within_limit() {
596
- let env = Env::default();
597
- let setup = ExtensiveOFTTestSetup::new(&env);
598
-
599
- // Set fee deposit address (required by ExtensiveOFT)
600
- let fee_collector = create_recipient_address(&env);
601
- setup.set_fee_deposit_address(&fee_collector);
602
-
603
- let sender = Address::generate(&env);
604
- let dst_eid = 100u32;
605
- let peer = BytesN::from_array(&env, &[2u8; 32]);
606
- setup.set_peer(dst_eid, &peer);
607
-
608
- // Set outbound rate limit
609
- let limit = 1_000_000i128;
610
- setup.set_rate_limit(&Direction::Outbound, dst_eid, limit, 3600);
611
-
612
- // First send
613
- let amount_1 = 300_000i128;
614
- setup.fund_tokens(&sender, amount_1);
615
- setup.fund_native_fees(&sender, setup.native_fee);
616
-
617
- let send_param_1 = setup.create_send_param(dst_eid, amount_1, amount_1);
618
- let fee = MessagingFee { native_fee: setup.native_fee, zro_fee: 0 };
619
- let oft_receipt_1 = setup.quote_oft(&send_param_1);
620
- setup.send(&sender, &send_param_1, &fee, &sender, &oft_receipt_1);
621
-
622
- // Second send
623
- let amount_2 = 400_000i128;
624
- setup.fund_tokens(&sender, amount_2);
625
- setup.fund_native_fees(&sender, setup.native_fee);
626
-
627
- let send_param_2 = setup.create_send_param(dst_eid, amount_2, amount_2);
628
- let oft_receipt_2 = setup.quote_oft(&send_param_2);
629
- setup.send(&sender, &send_param_2, &fee, &sender, &oft_receipt_2);
630
-
631
- // Check remaining capacity
632
- let capacity = setup.rate_limit_capacity(&Direction::Outbound, dst_eid);
633
- assert_eq!(capacity, limit - amount_1 - amount_2);
634
- }
635
-
636
- #[test]
637
- fn test_send_multiple_exceeds_cumulative_limit() {
638
- let env = Env::default();
639
- let setup = ExtensiveOFTTestSetup::new(&env);
640
-
641
- // Set fee deposit address (required by ExtensiveOFT)
642
- let fee_collector = create_recipient_address(&env);
643
- setup.set_fee_deposit_address(&fee_collector);
644
-
645
- let sender = Address::generate(&env);
646
- let dst_eid = 100u32;
647
- let peer = BytesN::from_array(&env, &[2u8; 32]);
648
- setup.set_peer(dst_eid, &peer);
649
-
650
- // Set outbound rate limit
651
- let limit = 1_000_000i128;
652
- setup.set_rate_limit(&Direction::Outbound, dst_eid, limit, 3600);
653
-
654
- // First send - uses most of the limit
655
- let amount_1 = 800_000i128;
656
- setup.fund_tokens(&sender, amount_1);
657
- setup.fund_native_fees(&sender, setup.native_fee);
658
-
659
- let send_param_1 = setup.create_send_param(dst_eid, amount_1, amount_1);
660
- let fee = MessagingFee { native_fee: setup.native_fee, zro_fee: 0 };
661
- let oft_receipt_1 = setup.quote_oft(&send_param_1);
662
- setup.send(&sender, &send_param_1, &fee, &sender, &oft_receipt_1);
663
-
664
- // Verify capacity was consumed
665
- let remaining_capacity = setup.rate_limit_capacity(&Direction::Outbound, dst_eid);
666
- assert_eq!(remaining_capacity, limit - amount_1); // 200_000 remaining
667
-
668
- // Second send - would exceed remaining capacity
669
- let amount_2 = 300_000i128; // Only 200_000 capacity left
670
- setup.fund_tokens(&sender, amount_2);
671
- setup.fund_native_fees(&sender, setup.native_fee);
672
-
673
- let send_param_2 = setup.create_send_param(dst_eid, amount_2, amount_2);
674
- let oft_receipt_2 = setup.quote_oft(&send_param_2);
675
-
676
- // Should fail at send - cumulative exceeds rate limit
677
- let result = setup.try_send(&sender, &send_param_2, &fee, &sender, &oft_receipt_2);
678
- let err = result.err().unwrap().ok().unwrap();
679
- assert_eq!(err, RateLimitError::ExceededRateLimit.into());
680
- }
681
-
682
- // ==================== lz_receive Tests (Inbound Rate Limit) ====================
683
-
684
- #[test]
685
- fn test_lz_receive_within_rate_limit() {
686
- let env = Env::default();
687
- let setup = ExtensiveOFTTestSetup::new(&env);
688
-
689
- let executor = Address::generate(&env);
690
- let recipient = create_recipient_address(&env);
691
- let src_eid = 100u32;
692
- let peer = BytesN::from_array(&env, &[2u8; 32]);
693
- setup.set_peer(src_eid, &peer);
694
-
695
- // Set inbound rate limit
696
- let limit = 10_000_000i128; // 10M in local decimals
697
- setup.set_rate_limit(&Direction::Inbound, src_eid, limit, 3600);
698
-
699
- let amount_sd = 500_000u64; // Within limit (will be converted to ld)
700
- let recipient_bytes32 = address_to_bytes32(&recipient);
701
- let message = encode_oft_message(&env, &recipient_bytes32, amount_sd);
702
-
703
- let guid = BytesN::from_array(&env, &[1u8; 32]);
704
- let origin = create_origin(src_eid, &peer, 1);
705
- let extra_data = Bytes::new(&env);
706
-
707
- let initial_balance = setup.token_client.balance(&recipient);
708
-
709
- // Should succeed within limit
710
- setup.lz_receive(&executor, &origin, &guid, &message, &extra_data, 0);
711
-
712
- let conversion_rate = setup.oft.decimal_conversion_rate();
713
- let expected_amount_ld = (amount_sd as i128) * conversion_rate;
714
- assert_eq!(setup.token_client.balance(&recipient), initial_balance + expected_amount_ld);
715
- }
716
-
717
- #[test]
718
- fn test_lz_receive_exceeds_rate_limit() {
719
- let env = Env::default();
720
- let setup = ExtensiveOFTTestSetup::new(&env);
721
-
722
- let executor = Address::generate(&env);
723
- let recipient = create_recipient_address(&env);
724
- let src_eid = 100u32;
725
- let peer = BytesN::from_array(&env, &[2u8; 32]);
726
- setup.set_peer(src_eid, &peer);
727
-
728
- // Set small inbound rate limit
729
- let limit = 100_000i128; // Small limit
730
- setup.set_rate_limit(&Direction::Inbound, src_eid, limit, 3600);
731
-
732
- // Try to receive more than limit
733
- // conversion_rate is 10, so 100_000 sd = 1_000_000 ld > limit of 100_000
734
- let amount_sd = 100_000u64;
735
- let recipient_bytes32 = address_to_bytes32(&recipient);
736
- let message = encode_oft_message(&env, &recipient_bytes32, amount_sd);
737
-
738
- let guid = BytesN::from_array(&env, &[1u8; 32]);
739
- let origin = create_origin(src_eid, &peer, 1);
740
- let extra_data = Bytes::new(&env);
741
-
742
- // Should fail - exceeds limit
743
- let result = setup.try_lz_receive(&executor, &origin, &guid, &message, &extra_data, 0);
744
- assert!(result.is_err());
745
- let err = result.err().unwrap().ok().unwrap();
746
- assert_eq!(err, RateLimitError::ExceededRateLimit.into());
747
- }
748
-
749
- // ==================== Capacity Release Tests (Bidirectional) ====================
750
-
751
- #[test]
752
- fn test_inbound_release_outbound_capacity() {
753
- let env = Env::default();
754
- let setup = ExtensiveOFTTestSetup::new(&env);
755
-
756
- // Set fee deposit address (required by ExtensiveOFT)
757
- let fee_collector = create_recipient_address(&env);
758
- setup.set_fee_deposit_address(&fee_collector);
759
-
760
- let sender = Address::generate(&env);
761
- let recipient = create_recipient_address(&env);
762
- let eid = 100u32;
763
- let peer = BytesN::from_array(&env, &[2u8; 32]);
764
- setup.set_peer(eid, &peer);
765
-
766
- // Set outbound rate limit
767
- let limit = 1_000_000i128;
768
- setup.set_rate_limit(&Direction::Outbound, eid, limit, 3600);
769
-
770
- // Use half the outbound capacity
771
- let outbound_amount = 500_000i128;
772
- setup.fund_tokens(&sender, outbound_amount);
773
- setup.fund_native_fees(&sender, setup.native_fee);
774
-
775
- let send_param = setup.create_send_param(eid, outbound_amount, outbound_amount);
776
- let fee = MessagingFee { native_fee: setup.native_fee, zro_fee: 0 };
777
- let oft_receipt = setup.quote_oft(&send_param);
778
- setup.send(&sender, &send_param, &fee, &sender, &oft_receipt);
779
-
780
- // Outbound in-flight should be 500_000
781
- let in_flight_before = setup.rate_limit_in_flight(&Direction::Outbound, eid);
782
- assert_eq!(in_flight_before, outbound_amount);
783
-
784
- // Now receive tokens (which releases outbound capacity)
785
- let executor = Address::generate(&env);
786
- let amount_sd = 30_000u64; // 300_000 in ld (conversion rate is 10)
787
- let recipient_bytes32 = address_to_bytes32(&recipient);
788
- let message = encode_oft_message(&env, &recipient_bytes32, amount_sd);
789
-
790
- let guid = BytesN::from_array(&env, &[1u8; 32]);
791
- let origin = create_origin(eid, &peer, 1);
792
- let extra_data = Bytes::new(&env);
793
-
794
- setup.lz_receive(&executor, &origin, &guid, &message, &extra_data, 0);
795
-
796
- // Outbound in-flight should be reduced by inbound amount
797
- let conversion_rate = setup.oft.decimal_conversion_rate();
798
- let inbound_amount_ld = (amount_sd as i128) * conversion_rate;
799
- let in_flight_after = setup.rate_limit_in_flight(&Direction::Outbound, eid);
800
- assert_eq!(in_flight_after, outbound_amount - inbound_amount_ld);
801
- }
802
-
803
- #[test]
804
- fn test_release_rate_limit_more_than_in_flight_resets_to_zero() {
805
- let env = Env::default();
806
- let setup = ExtensiveOFTTestSetup::new(&env);
807
-
808
- // Set fee deposit address (required by ExtensiveOFT)
809
- let fee_collector = create_recipient_address(&env);
810
- setup.set_fee_deposit_address(&fee_collector);
811
-
812
- let sender = Address::generate(&env);
813
- let recipient = create_recipient_address(&env);
814
- let eid = 100u32;
815
- let peer = BytesN::from_array(&env, &[2u8; 32]);
816
- setup.set_peer(eid, &peer);
817
-
818
- // Set outbound rate limit
819
- let limit = 1_000_000i128;
820
- setup.set_rate_limit(&Direction::Outbound, eid, limit, 3600);
821
-
822
- // Use small amount of outbound capacity
823
- let outbound_amount = 100_000i128;
824
- setup.fund_tokens(&sender, outbound_amount);
825
- setup.fund_native_fees(&sender, setup.native_fee);
826
-
827
- let send_param = setup.create_send_param(eid, outbound_amount, outbound_amount);
828
- let fee = MessagingFee { native_fee: setup.native_fee, zro_fee: 0 };
829
- let oft_receipt = setup.quote_oft(&send_param);
830
- setup.send(&sender, &send_param, &fee, &sender, &oft_receipt);
831
-
832
- // Outbound in-flight should be 100_000
833
- let in_flight_before = setup.rate_limit_in_flight(&Direction::Outbound, eid);
834
- assert_eq!(in_flight_before, outbound_amount);
835
-
836
- // Receive MORE tokens than were sent (releases more than in-flight)
837
- let executor = Address::generate(&env);
838
- let amount_sd = 50_000u64; // 500_000 in ld (more than 100_000 in flight)
839
- let recipient_bytes32 = address_to_bytes32(&recipient);
840
- let message = encode_oft_message(&env, &recipient_bytes32, amount_sd);
841
-
842
- let guid = BytesN::from_array(&env, &[1u8; 32]);
843
- let origin = create_origin(eid, &peer, 1);
844
- let extra_data = Bytes::new(&env);
845
-
846
- setup.lz_receive(&executor, &origin, &guid, &message, &extra_data, 0);
847
-
848
- // Outbound in-flight should reset to 0 (not go negative)
849
- let in_flight_after = setup.rate_limit_in_flight(&Direction::Outbound, eid);
850
- assert_eq!(in_flight_after, 0);
851
-
852
- // Capacity should be back to full limit
853
- let capacity = setup.rate_limit_capacity(&Direction::Outbound, eid);
854
- assert_eq!(capacity, limit);
855
- }
856
-
857
- // ==================== quote_oft Tests (Rate Limit in OFTLimit) ====================
858
-
859
- #[test]
860
- fn test_quote_oft_returns_rate_limit_capacity() {
861
- let env = Env::default();
862
- let setup = ExtensiveOFTTestSetup::new(&env);
863
-
864
- // Set fee deposit address (required by ExtensiveOFT)
865
- let fee_collector = create_recipient_address(&env);
866
- setup.set_fee_deposit_address(&fee_collector);
867
-
868
- let dst_eid = 100u32;
869
- let peer = BytesN::from_array(&env, &[2u8; 32]);
870
- setup.set_peer(dst_eid, &peer);
871
-
872
- // Set outbound rate limit
873
- let limit = 1_000_000i128;
874
- setup.set_rate_limit(&Direction::Outbound, dst_eid, limit, 3600);
875
-
876
- let amount_ld = 500_000i128;
877
- let send_param = setup.create_send_param(dst_eid, amount_ld, amount_ld);
878
-
879
- let (oft_limit, _, _) = setup.oft.quote_oft(&send_param);
880
-
881
- // max_amount_ld should reflect rate limit capacity
882
- assert_eq!(oft_limit.max_amount_ld, limit);
883
- }
884
-
885
- #[test]
886
- fn test_quote_oft_no_rate_limit_returns_max() {
887
- let env = Env::default();
888
- let setup = ExtensiveOFTTestSetup::new(&env);
889
-
890
- // Set fee deposit address (required by ExtensiveOFT)
891
- let fee_collector = create_recipient_address(&env);
892
- setup.set_fee_deposit_address(&fee_collector);
893
-
894
- let dst_eid = 100u32;
895
- let peer = BytesN::from_array(&env, &[2u8; 32]);
896
- setup.set_peer(dst_eid, &peer);
897
-
898
- // No rate limit set
899
-
900
- let amount_ld = 500_000i128;
901
- let send_param = setup.create_send_param(dst_eid, amount_ld, amount_ld);
902
-
903
- let (oft_limit, _, _) = setup.oft.quote_oft(&send_param);
904
-
905
- // max_amount_ld should be i128::MAX when no rate limit
906
- assert_eq!(oft_limit.max_amount_ld, i128::MAX);
907
- }
908
-
909
- // ==================== Different Destinations Tests ====================
910
-
911
- #[test]
912
- fn test_rate_limits_independent_per_destination() {
913
- let env = Env::default();
914
- let setup = ExtensiveOFTTestSetup::new(&env);
915
-
916
- // Set fee deposit address (required by ExtensiveOFT)
917
- let fee_collector = create_recipient_address(&env);
918
- setup.set_fee_deposit_address(&fee_collector);
919
-
920
- let sender = Address::generate(&env);
921
-
922
- let dst_eid_1 = 100u32;
923
- let dst_eid_2 = 200u32;
924
- let peer = BytesN::from_array(&env, &[2u8; 32]);
925
- setup.set_peer(dst_eid_1, &peer);
926
- setup.set_peer(dst_eid_2, &peer);
927
-
928
- // Set different limits for different destinations
929
- setup.set_rate_limit(&Direction::Outbound, dst_eid_1, 1_000_000, 3600);
930
- setup.set_rate_limit(&Direction::Outbound, dst_eid_2, 2_000_000, 3600);
931
-
932
- // Use all capacity for dst_eid_1
933
- let amount_1 = 1_000_000i128;
934
- setup.fund_tokens(&sender, amount_1);
935
- setup.fund_native_fees(&sender, setup.native_fee);
936
-
937
- let send_param_1 = setup.create_send_param(dst_eid_1, amount_1, amount_1);
938
- let fee = MessagingFee { native_fee: setup.native_fee, zro_fee: 0 };
939
- let oft_receipt_1 = setup.quote_oft(&send_param_1);
940
- setup.send(&sender, &send_param_1, &fee, &sender, &oft_receipt_1);
941
-
942
- // dst_eid_1 should have no capacity
943
- assert_eq!(setup.rate_limit_capacity(&Direction::Outbound, dst_eid_1), 0);
944
-
945
- // dst_eid_2 should still have full capacity
946
- assert_eq!(setup.rate_limit_capacity(&Direction::Outbound, dst_eid_2), 2_000_000);
947
-
948
- // Should be able to send to dst_eid_2
949
- let amount_2 = 1_500_000i128;
950
- setup.fund_tokens(&sender, amount_2);
951
- setup.fund_native_fees(&sender, setup.native_fee);
952
-
953
- let send_param_2 = setup.create_send_param(dst_eid_2, amount_2, amount_2);
954
- let oft_receipt_2 = setup.quote_oft(&send_param_2);
955
- let (msg_receipt, _) = setup.send(&sender, &send_param_2, &fee, &sender, &oft_receipt_2);
956
- assert!(msg_receipt.nonce > 0);
957
- }
958
-
959
- // ==================== Authentication Tests ====================
960
-
961
- #[test]
962
- fn test_set_rate_limit_requires_owner_auth() {
963
- let env = Env::default();
964
- let setup = ExtensiveOFTTestSetup::new(&env);
965
-
966
- let dst_eid = 100u32;
967
- let limit = 1_000_000i128;
968
- let window_seconds = 3600u64;
969
-
970
- // Try to set rate limit without auth
971
- let result = setup.oft.try_set_rate_limit(&Direction::Outbound, &dst_eid, &limit, &window_seconds);
972
- assert!(result.is_err());
973
-
974
- // Rate limit should not be set
975
- let (stored_limit, stored_window) = setup.rate_limit_config(&Direction::Outbound, dst_eid);
976
- assert_eq!(stored_limit, 0);
977
- assert_eq!(stored_window, 0);
978
- }
979
-
980
- #[test]
981
- fn test_set_rate_limit_wrong_auth_fails() {
982
- let env = Env::default();
983
- let setup = ExtensiveOFTTestSetup::new(&env);
984
-
985
- let non_owner = Address::generate(&env);
986
- let dst_eid = 100u32;
987
- let limit = 1_000_000i128;
988
- let window_seconds = 3600u64;
989
-
990
- // Try to set rate limit with wrong auth (non-owner)
991
- env.mock_auths(&[soroban_sdk::testutils::MockAuth {
992
- address: &non_owner,
993
- invoke: &soroban_sdk::testutils::MockAuthInvoke {
994
- contract: &setup.oft.address,
995
- fn_name: "set_rate_limit",
996
- args: (&Direction::Outbound, dst_eid, limit, window_seconds).into_val(&env),
997
- sub_invokes: &[],
998
- },
999
- }]);
1000
- let result = setup.oft.try_set_rate_limit(&Direction::Outbound, &dst_eid, &limit, &window_seconds);
1001
- assert!(result.is_err());
1002
-
1003
- // Rate limit should not be set
1004
- let (stored_limit, stored_window) = setup.rate_limit_config(&Direction::Outbound, dst_eid);
1005
- assert_eq!(stored_limit, 0);
1006
- assert_eq!(stored_window, 0);
1007
- }
1008
-
1009
- #[test]
1010
- fn test_set_rate_limit_inbound_requires_owner_auth() {
1011
- let env = Env::default();
1012
- let setup = ExtensiveOFTTestSetup::new(&env);
1013
-
1014
- let src_eid = 100u32;
1015
- let limit = 1_000_000i128;
1016
- let window_seconds = 3600u64;
1017
-
1018
- // Try to set inbound rate limit without auth
1019
- let result = setup.oft.try_set_rate_limit(&Direction::Inbound, &src_eid, &limit, &window_seconds);
1020
- assert!(result.is_err());
1021
-
1022
- // Rate limit should not be set
1023
- let (stored_limit, stored_window) = setup.rate_limit_config(&Direction::Inbound, src_eid);
1024
- assert_eq!(stored_limit, 0);
1025
- assert_eq!(stored_window, 0);
1026
- }
1027
-
1028
- #[test]
1029
- fn test_unset_rate_limit_requires_owner_auth() {
1030
- let env = Env::default();
1031
- let setup = ExtensiveOFTTestSetup::new(&env);
1032
-
1033
- let dst_eid = 100u32;
1034
- let limit = 1_000_000i128;
1035
- let window_seconds = 3600u64;
1036
-
1037
- // First set rate limit as owner
1038
- setup.set_rate_limit(&Direction::Outbound, dst_eid, limit, window_seconds);
1039
- let (stored_limit, _) = setup.rate_limit_config(&Direction::Outbound, dst_eid);
1040
- assert_eq!(stored_limit, limit);
1041
-
1042
- // Try to unset rate limit without auth
1043
- let result = setup.oft.try_unset_rate_limit(&Direction::Outbound, &dst_eid);
1044
- assert!(result.is_err());
1045
-
1046
- // Rate limit should still be set
1047
- let (stored_limit, _) = setup.rate_limit_config(&Direction::Outbound, dst_eid);
1048
- assert_eq!(stored_limit, limit);
1049
- }
1050
-
1051
- #[test]
1052
- fn test_unset_rate_limit_wrong_auth_fails() {
1053
- let env = Env::default();
1054
- let setup = ExtensiveOFTTestSetup::new(&env);
1055
-
1056
- let non_owner = Address::generate(&env);
1057
- let dst_eid = 100u32;
1058
- let limit = 1_000_000i128;
1059
- let window_seconds = 3600u64;
1060
-
1061
- // First set rate limit as owner
1062
- setup.set_rate_limit(&Direction::Outbound, dst_eid, limit, window_seconds);
1063
-
1064
- // Try to unset rate limit with wrong auth (non-owner)
1065
- env.mock_auths(&[soroban_sdk::testutils::MockAuth {
1066
- address: &non_owner,
1067
- invoke: &soroban_sdk::testutils::MockAuthInvoke {
1068
- contract: &setup.oft.address,
1069
- fn_name: "unset_rate_limit",
1070
- args: (&Direction::Outbound, dst_eid).into_val(&env),
1071
- sub_invokes: &[],
1072
- },
1073
- }]);
1074
- let result = setup.oft.try_unset_rate_limit(&Direction::Outbound, &dst_eid);
1075
- assert!(result.is_err());
1076
-
1077
- // Rate limit should still be set
1078
- let (stored_limit, _) = setup.rate_limit_config(&Direction::Outbound, dst_eid);
1079
- assert_eq!(stored_limit, limit);
1080
- }