@layerzerolabs/protocol-stellar-v2 0.2.51 → 0.2.53

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 (55) hide show
  1. package/.turbo/turbo-build.log +281 -185
  2. package/.turbo/turbo-lint.log +69 -70
  3. package/.turbo/turbo-test.log +2010 -1754
  4. package/Cargo.lock +16 -0
  5. package/Cargo.toml +1 -0
  6. package/contracts/oapps/console-oft/Cargo.toml +30 -0
  7. package/contracts/oapps/console-oft/integration-tests/extensions/mod.rs +5 -0
  8. package/contracts/oapps/console-oft/integration-tests/extensions/test_combined.rs +90 -0
  9. package/contracts/oapps/console-oft/integration-tests/extensions/test_oft_fee.rs +186 -0
  10. package/contracts/oapps/console-oft/integration-tests/extensions/test_ownership.rs +161 -0
  11. package/contracts/oapps/console-oft/integration-tests/extensions/test_pausable.rs +154 -0
  12. package/contracts/oapps/console-oft/integration-tests/extensions/test_rate_limiter.rs +479 -0
  13. package/contracts/oapps/console-oft/integration-tests/mod.rs +3 -0
  14. package/contracts/oapps/console-oft/integration-tests/setup.rs +303 -0
  15. package/contracts/oapps/console-oft/integration-tests/utils.rs +685 -0
  16. package/contracts/oapps/console-oft/src/errors.rs +7 -0
  17. package/contracts/oapps/console-oft/src/extensions/mod.rs +3 -0
  18. package/contracts/oapps/console-oft/src/extensions/oft_fee.rs +239 -0
  19. package/contracts/oapps/console-oft/src/extensions/pausable.rs +185 -0
  20. package/contracts/oapps/console-oft/src/extensions/rate_limiter.rs +478 -0
  21. package/contracts/oapps/console-oft/src/interfaces/mintable.rs +14 -0
  22. package/contracts/oapps/console-oft/src/interfaces/mod.rs +3 -0
  23. package/contracts/oapps/console-oft/src/lib.rs +26 -0
  24. package/contracts/oapps/console-oft/src/oft.rs +208 -0
  25. package/contracts/oapps/console-oft/src/oft_access_control.rs +93 -0
  26. package/contracts/oapps/console-oft/src/oft_types/lock_unlock.rs +50 -0
  27. package/contracts/oapps/console-oft/src/oft_types/mint_burn.rs +50 -0
  28. package/contracts/oapps/console-oft/src/oft_types/mod.rs +24 -0
  29. package/contracts/oapps/console-oft/src/tests/extensions/mod.rs +3 -0
  30. package/contracts/oapps/console-oft/src/tests/extensions/oft_fee.rs +255 -0
  31. package/contracts/oapps/console-oft/src/tests/extensions/pausable.rs +212 -0
  32. package/contracts/oapps/console-oft/src/tests/extensions/rate_limiter.rs +992 -0
  33. package/contracts/oapps/console-oft/src/tests/mod.rs +2 -0
  34. package/contracts/oapps/console-oft/src/tests/oft_types/lock_unlock.rs +185 -0
  35. package/contracts/oapps/console-oft/src/tests/oft_types/mod.rs +1 -0
  36. package/contracts/oapps/oft/src/extensions/oft_fee.rs +5 -2
  37. package/contracts/oapps/oft/src/tests/extensions/oft_fee.rs +1 -1
  38. package/contracts/oapps/sac-manager/src/sac_manager.rs +8 -0
  39. package/contracts/workers/worker/src/worker.rs +8 -1
  40. package/docs/oft-guide.md +2 -2
  41. package/package.json +4 -4
  42. package/sdk/.turbo/turbo-test.log +359 -423
  43. package/sdk/dist/generated/dvn.d.ts +9 -3
  44. package/sdk/dist/generated/dvn.js +4 -4
  45. package/sdk/dist/generated/executor.d.ts +9 -3
  46. package/sdk/dist/generated/executor.js +4 -4
  47. package/sdk/package.json +1 -1
  48. package/sdk/test/oft-sml.test.ts +22 -41
  49. package/sdk/test/sac-manager.test.ts +23 -22
  50. package/sdk/test/secp256k1.ts +59 -0
  51. package/sdk/test/suites/constants.ts +5 -1
  52. package/sdk/test/suites/deploy.ts +14 -8
  53. package/sdk/test/suites/globalSetup.ts +144 -60
  54. package/sdk/test/suites/localnet.ts +20 -25
  55. package/sdk/test/utils.ts +1 -61
@@ -0,0 +1,685 @@
1
+ //! Utility functions for OFT-STD integration tests.
2
+
3
+ use crate::extensions::oft_fee::FEE_CONFIG_MANAGER_ROLE;
4
+ use crate::extensions::pausable::{PAUSER_ROLE, UNPAUSER_ROLE};
5
+ use crate::extensions::rate_limiter::{RateLimitConfig, RateLimitGlobalConfig, RATE_LIMITER_MANAGER_ROLE};
6
+ use crate::integration_tests::setup::{decode_packet, ChainSetup};
7
+ use crate::MintableClient;
8
+ use endpoint_v2::{MessagingFee, Origin, OutboundPacket};
9
+ use message_lib_common::packet_codec_v1;
10
+ use oft_core::{OFTFeeDetail, OFTLimit, OFTReceipt, SendParam};
11
+ use soroban_sdk::{
12
+ address_payload::AddressPayload,
13
+ testutils::{Events, Ledger, MockAuth, MockAuthInvoke},
14
+ token::StellarAssetClient,
15
+ xdr::ToXdr,
16
+ Address, Bytes, BytesN, Env, IntoVal, Map, Symbol, Val, Vec,
17
+ };
18
+ // ============================================================================
19
+ // Address Conversion Utilities
20
+ // ============================================================================
21
+
22
+ pub fn address_to_peer_bytes32(address: &Address) -> BytesN<32> {
23
+ match address.to_payload().unwrap() {
24
+ AddressPayload::ContractIdHash(payload) => payload,
25
+ AddressPayload::AccountIdPublicKeyEd25519(_) => panic!("peer must be a contract"),
26
+ }
27
+ }
28
+
29
+ pub fn peer_bytes32_to_address(env: &Env, bytes32: &BytesN<32>) -> Address {
30
+ AddressPayload::ContractIdHash(bytes32.clone()).to_address(env)
31
+ }
32
+
33
+ #[allow(dead_code)]
34
+ pub fn create_recipient_address(env: &Env) -> Address {
35
+ let bytes = BytesN::from_array(env, &[0u8; 32]);
36
+ peer_bytes32_to_address(env, &bytes)
37
+ }
38
+
39
+ // ============================================================================
40
+ // OFT Core Operations
41
+ // ============================================================================
42
+
43
+ pub fn quote_oft(
44
+ chain: &ChainSetup<'_>,
45
+ from: &Address,
46
+ send_param: &SendParam,
47
+ ) -> (OFTLimit, Vec<OFTFeeDetail>, OFTReceipt) {
48
+ chain.oft.quote_oft(from, send_param)
49
+ }
50
+
51
+ pub fn quote_send(
52
+ env: &Env,
53
+ chain: &ChainSetup<'_>,
54
+ sender: &Address,
55
+ send_param: &SendParam,
56
+ pay_in_zro: bool,
57
+ ) -> MessagingFee {
58
+ env.mock_auths(&[MockAuth {
59
+ address: sender,
60
+ invoke: &MockAuthInvoke {
61
+ contract: &chain.oft.address,
62
+ fn_name: "quote_send",
63
+ args: (sender, send_param, &pay_in_zro).into_val(env),
64
+ sub_invokes: &[],
65
+ },
66
+ }]);
67
+ chain.oft.quote_send(sender, send_param, &pay_in_zro)
68
+ }
69
+
70
+ /// Send without fee (standard OFT send).
71
+ /// Sender authorizes OFT send (OFT debits by calling token burn directly) and SAC burn.
72
+ pub fn send(
73
+ env: &Env,
74
+ chain: &ChainSetup<'_>,
75
+ sender: &Address,
76
+ send_param: &SendParam,
77
+ fee: &MessagingFee,
78
+ refund_address: &Address,
79
+ oft_receipt: &OFTReceipt,
80
+ ) {
81
+ env.mock_auths(&[MockAuth {
82
+ address: sender,
83
+ invoke: &MockAuthInvoke {
84
+ contract: &chain.oft.address,
85
+ fn_name: "send",
86
+ args: (sender, send_param, fee, refund_address).into_val(env),
87
+ sub_invokes: &[
88
+ MockAuthInvoke {
89
+ contract: &chain.native_token,
90
+ fn_name: "transfer",
91
+ args: (sender, &chain.endpoint.address, &fee.native_fee).into_val(env),
92
+ sub_invokes: &[],
93
+ },
94
+ MockAuthInvoke {
95
+ contract: &chain.oft_token,
96
+ fn_name: "burn",
97
+ args: (sender, &oft_receipt.amount_received_ld).into_val(env),
98
+ sub_invokes: &[],
99
+ },
100
+ ],
101
+ },
102
+ }]);
103
+ chain.oft.send(sender, send_param, fee, refund_address);
104
+ }
105
+
106
+ /// Send with fee (OFT fee extension enabled)
107
+ /// Order: transfer fee to deposit -> burn tokens -> transfer native fee
108
+ pub fn send_with_fee(
109
+ env: &Env,
110
+ chain: &ChainSetup<'_>,
111
+ sender: &Address,
112
+ send_param: &SendParam,
113
+ fee: &MessagingFee,
114
+ refund_address: &Address,
115
+ oft_receipt: &OFTReceipt,
116
+ fee_deposit_address: &Address,
117
+ ) {
118
+ let fee_amount = oft_receipt.amount_sent_ld - oft_receipt.amount_received_ld;
119
+ env.mock_auths(&[MockAuth {
120
+ address: sender,
121
+ invoke: &MockAuthInvoke {
122
+ contract: &chain.oft.address,
123
+ fn_name: "send",
124
+ args: (sender, send_param, fee, refund_address).into_val(env),
125
+ sub_invokes: &[
126
+ MockAuthInvoke {
127
+ contract: &chain.oft_token,
128
+ fn_name: "transfer",
129
+ args: (sender, fee_deposit_address, &fee_amount).into_val(env),
130
+ sub_invokes: &[],
131
+ },
132
+ MockAuthInvoke {
133
+ contract: &chain.oft_token,
134
+ fn_name: "burn",
135
+ args: (sender, &oft_receipt.amount_received_ld).into_val(env),
136
+ sub_invokes: &[],
137
+ },
138
+ MockAuthInvoke {
139
+ contract: &chain.native_token,
140
+ fn_name: "transfer",
141
+ args: (sender, &chain.endpoint.address, &fee.native_fee).into_val(env),
142
+ sub_invokes: &[],
143
+ },
144
+ ],
145
+ },
146
+ }]);
147
+ chain.oft.send(sender, send_param, fee, refund_address);
148
+ }
149
+
150
+ pub fn try_send(
151
+ env: &Env,
152
+ chain: &ChainSetup<'_>,
153
+ sender: &Address,
154
+ send_param: &SendParam,
155
+ fee: &MessagingFee,
156
+ refund_address: &Address,
157
+ oft_receipt: &OFTReceipt,
158
+ ) -> bool {
159
+ env.mock_auths(&[MockAuth {
160
+ address: sender,
161
+ invoke: &MockAuthInvoke {
162
+ contract: &chain.oft.address,
163
+ fn_name: "send",
164
+ args: (sender, send_param, fee, refund_address).into_val(env),
165
+ sub_invokes: &[
166
+ MockAuthInvoke {
167
+ contract: &chain.native_token,
168
+ fn_name: "transfer",
169
+ args: (sender, &chain.endpoint.address, &fee.native_fee).into_val(env),
170
+ sub_invokes: &[],
171
+ },
172
+ MockAuthInvoke {
173
+ contract: &chain.oft_token,
174
+ fn_name: "burn",
175
+ args: (sender, &oft_receipt.amount_received_ld).into_val(env),
176
+ sub_invokes: &[],
177
+ },
178
+ ],
179
+ },
180
+ }]);
181
+ chain.oft.try_send(sender, send_param, fee, refund_address).is_ok()
182
+ }
183
+
184
+ // ============================================================================
185
+ // Packet Handling
186
+ // ============================================================================
187
+
188
+ pub fn validate_packet(env: &Env, chain: &ChainSetup<'_>, packet_event: &(Bytes, Bytes, Address)) {
189
+ let packet = decode_packet(env, &packet_event.0);
190
+ let encoded_header = packet_codec_v1::encode_packet_header(env, &packet);
191
+ let payload_hash = packet_codec_v1::payload_hash(env, &packet);
192
+
193
+ env.mock_auths(&[MockAuth {
194
+ address: &chain.owner,
195
+ invoke: &MockAuthInvoke {
196
+ contract: &chain.sml.address,
197
+ fn_name: "validate_packet",
198
+ args: (&encoded_header, &payload_hash).into_val(env),
199
+ sub_invokes: &[],
200
+ },
201
+ }]);
202
+ chain.sml.validate_packet(&encoded_header, &payload_hash);
203
+ }
204
+
205
+ pub fn lz_receive(
206
+ env: &Env,
207
+ chain: &ChainSetup<'_>,
208
+ executor: &Address,
209
+ packet: &OutboundPacket,
210
+ recipient: &Address,
211
+ value: i128,
212
+ ) {
213
+ let origin =
214
+ Origin { src_eid: packet.src_eid, sender: address_to_peer_bytes32(&packet.sender), nonce: packet.nonce };
215
+ let extra_options = recipient.to_xdr(env);
216
+
217
+ env.mock_auths(&[MockAuth {
218
+ address: executor,
219
+ invoke: &MockAuthInvoke {
220
+ contract: &chain.oft.address,
221
+ fn_name: "lz_receive",
222
+ args: (executor, &origin, &packet.guid, &packet.message, &extra_options, &value).into_val(env),
223
+ sub_invokes: &[],
224
+ },
225
+ }]);
226
+ endpoint_v2::LayerZeroReceiverClient::new(env, &chain.oft.address).lz_receive(
227
+ executor,
228
+ &origin,
229
+ &packet.guid,
230
+ &packet.message,
231
+ &extra_options,
232
+ &value,
233
+ );
234
+ }
235
+
236
+ pub fn try_lz_receive(
237
+ env: &Env,
238
+ chain: &ChainSetup<'_>,
239
+ executor: &Address,
240
+ packet: &OutboundPacket,
241
+ recipient: &Address,
242
+ value: i128,
243
+ ) -> bool {
244
+ let origin =
245
+ Origin { src_eid: packet.src_eid, sender: address_to_peer_bytes32(&packet.sender), nonce: packet.nonce };
246
+ let extra_options = recipient.to_xdr(env);
247
+
248
+ env.mock_auths(&[MockAuth {
249
+ address: executor,
250
+ invoke: &MockAuthInvoke {
251
+ contract: &chain.oft.address,
252
+ fn_name: "lz_receive",
253
+ args: (executor, &origin, &packet.guid, &packet.message, &extra_options, &value).into_val(env),
254
+ sub_invokes: &[],
255
+ },
256
+ }]);
257
+ endpoint_v2::LayerZeroReceiverClient::new(env, &chain.oft.address)
258
+ .try_lz_receive(executor, &origin, &packet.guid, &packet.message, &extra_options, &value)
259
+ .is_ok()
260
+ }
261
+
262
+ // returns (encoded_payload, options, send_library)
263
+ pub fn scan_packet_sent_event(env: &Env, endpoint: &Address) -> Option<(Bytes, Bytes, Address)> {
264
+ use soroban_sdk::TryFromVal;
265
+
266
+ let mut packet = None;
267
+ let events = env.events().all().filter_by_contract(endpoint);
268
+ for event in events.events().iter() {
269
+ let v0 = match &event.body {
270
+ soroban_sdk::xdr::ContractEventBody::V0(v0) => v0,
271
+ };
272
+
273
+ // Check if this is a packet_sent event by looking at topics
274
+ let mut is_packet_sent = false;
275
+ for topic in v0.topics.iter() {
276
+ if let Ok(sym) = Symbol::try_from_val(env, topic) {
277
+ if sym == Symbol::new(env, "packet_sent") {
278
+ is_packet_sent = true;
279
+ break;
280
+ }
281
+ }
282
+ }
283
+
284
+ if is_packet_sent {
285
+ let data: Val = Val::try_from_val(env, &v0.data).unwrap();
286
+ let map: Map<Symbol, Val> = data.into_val(env);
287
+
288
+ let encoded_payload: Bytes = map.get(Symbol::new(env, "encoded_packet")).unwrap().into_val(env);
289
+ let options: Bytes = map.get(Symbol::new(env, "options")).unwrap().into_val(env);
290
+ let send_library: Address = map.get(Symbol::new(env, "send_library")).unwrap().into_val(env);
291
+
292
+ packet = Some((encoded_payload, options, send_library));
293
+ }
294
+ }
295
+
296
+ packet
297
+ }
298
+
299
+ // ============================================================================
300
+ // Token Operations
301
+ // ============================================================================
302
+
303
+ pub fn mint_to(env: &Env, owner: &Address, token: &Address, to: &Address, amount: i128) {
304
+ env.mock_auths(&[MockAuth {
305
+ address: owner,
306
+ invoke: &MockAuthInvoke {
307
+ contract: token,
308
+ fn_name: "mint",
309
+ args: (to, amount).into_val(env),
310
+ sub_invokes: &[],
311
+ },
312
+ }]);
313
+
314
+ let sac = StellarAssetClient::new(env, token);
315
+ sac.mint(to, &amount);
316
+ }
317
+
318
+ /// Mints the OFT token (via the Mintable wrapper) to the given address.
319
+ /// Use when OFT is MintBurn; the wrapper calls the underlying SAC mint.
320
+ pub fn mint_oft_token_to(env: &Env, chain: &ChainSetup<'_>, to: &Address, amount: i128) {
321
+ env.mock_auths(&[MockAuth {
322
+ address: &chain.owner,
323
+ invoke: &MockAuthInvoke {
324
+ contract: &chain.sac_wrapper,
325
+ fn_name: "mint",
326
+ args: (to, &amount, &chain.owner).into_val(env),
327
+ sub_invokes: &[MockAuthInvoke {
328
+ contract: &chain.oft_token,
329
+ fn_name: "mint",
330
+ args: (to, &amount).into_val(env),
331
+ sub_invokes: &[],
332
+ }],
333
+ },
334
+ }]);
335
+ MintableClient::new(env, &chain.sac_wrapper).mint(to, &amount, &chain.owner);
336
+ }
337
+
338
+ pub fn transfer_sac_admin(env: &Env, owner: &Address, token: &Address, new_admin: &Address) {
339
+ env.mock_auths(&[MockAuth {
340
+ address: owner,
341
+ invoke: &MockAuthInvoke {
342
+ contract: token,
343
+ fn_name: "set_admin",
344
+ args: (new_admin,).into_val(env),
345
+ sub_invokes: &[],
346
+ },
347
+ }]);
348
+ StellarAssetClient::new(env, token).set_admin(new_admin);
349
+ }
350
+
351
+ pub fn token_balance(env: &Env, token: &Address, account: &Address) -> i128 {
352
+ soroban_sdk::token::TokenClient::new(env, token).balance(account)
353
+ }
354
+
355
+ // ============================================================================
356
+ // Pausable Extension Operations
357
+ // ============================================================================
358
+
359
+ pub fn set_paused(env: &Env, chain: &ChainSetup<'_>, paused: bool) {
360
+ let pauser = Symbol::new(env, PAUSER_ROLE);
361
+ let unpauser = Symbol::new(env, UNPAUSER_ROLE);
362
+ env.mock_auths(&[MockAuth {
363
+ address: &chain.owner,
364
+ invoke: &MockAuthInvoke {
365
+ contract: &chain.oft.address,
366
+ fn_name: "grant_role",
367
+ args: (&chain.owner, &pauser, &chain.owner).into_val(env),
368
+ sub_invokes: &[],
369
+ },
370
+ }]);
371
+ chain.oft.grant_role(&chain.owner, &pauser, &chain.owner);
372
+ env.mock_auths(&[MockAuth {
373
+ address: &chain.owner,
374
+ invoke: &MockAuthInvoke {
375
+ contract: &chain.oft.address,
376
+ fn_name: "grant_role",
377
+ args: (&chain.owner, &unpauser, &chain.owner).into_val(env),
378
+ sub_invokes: &[],
379
+ },
380
+ }]);
381
+ chain.oft.grant_role(&chain.owner, &unpauser, &chain.owner);
382
+
383
+ env.mock_auths(&[MockAuth {
384
+ address: &chain.owner,
385
+ invoke: &MockAuthInvoke {
386
+ contract: &chain.oft.address,
387
+ fn_name: "set_default_paused",
388
+ args: (&paused, &chain.owner).into_val(env),
389
+ sub_invokes: &[],
390
+ },
391
+ }]);
392
+ chain.oft.set_default_paused(&paused, &chain.owner);
393
+ }
394
+
395
+ pub fn is_paused(chain: &ChainSetup<'_>) -> bool {
396
+ chain.oft.default_paused()
397
+ }
398
+
399
+ pub fn set_per_id_paused(env: &Env, chain: &ChainSetup<'_>, dst_eid: u32, paused: Option<bool>) {
400
+ let pauser = Symbol::new(env, PAUSER_ROLE);
401
+ let unpauser = Symbol::new(env, UNPAUSER_ROLE);
402
+ env.mock_auths(&[MockAuth {
403
+ address: &chain.owner,
404
+ invoke: &MockAuthInvoke {
405
+ contract: &chain.oft.address,
406
+ fn_name: "grant_role",
407
+ args: (&chain.owner, &pauser, &chain.owner).into_val(env),
408
+ sub_invokes: &[],
409
+ },
410
+ }]);
411
+ chain.oft.grant_role(&chain.owner, &pauser, &chain.owner);
412
+ env.mock_auths(&[MockAuth {
413
+ address: &chain.owner,
414
+ invoke: &MockAuthInvoke {
415
+ contract: &chain.oft.address,
416
+ fn_name: "grant_role",
417
+ args: (&chain.owner, &unpauser, &chain.owner).into_val(env),
418
+ sub_invokes: &[],
419
+ },
420
+ }]);
421
+ chain.oft.grant_role(&chain.owner, &unpauser, &chain.owner);
422
+
423
+ let id = dst_eid as u128;
424
+ env.mock_auths(&[MockAuth {
425
+ address: &chain.owner,
426
+ invoke: &MockAuthInvoke {
427
+ contract: &chain.oft.address,
428
+ fn_name: "set_paused",
429
+ args: (&id, &paused, &chain.owner).into_val(env),
430
+ sub_invokes: &[],
431
+ },
432
+ }]);
433
+ chain.oft.set_paused(&id, &paused, &chain.owner);
434
+ }
435
+
436
+ // ============================================================================
437
+ // OFT Fee Extension Operations
438
+ // ============================================================================
439
+
440
+ pub fn set_fee_deposit(env: &Env, chain: &ChainSetup<'_>, deposit_address: &Address) {
441
+ env.mock_auths(&[MockAuth {
442
+ address: &chain.owner,
443
+ invoke: &MockAuthInvoke {
444
+ contract: &chain.oft.address,
445
+ fn_name: "set_fee_deposit",
446
+ args: (deposit_address, &chain.owner).into_val(env),
447
+ sub_invokes: &[],
448
+ },
449
+ }]);
450
+ chain.oft.set_fee_deposit(deposit_address, &chain.owner);
451
+ }
452
+
453
+ pub fn set_default_fee_bps(env: &Env, chain: &ChainSetup<'_>, fee_bps: u32) {
454
+ let role = Symbol::new(env, FEE_CONFIG_MANAGER_ROLE);
455
+ env.mock_auths(&[MockAuth {
456
+ address: &chain.owner,
457
+ invoke: &MockAuthInvoke {
458
+ contract: &chain.oft.address,
459
+ fn_name: "grant_role",
460
+ args: (&chain.owner, &role, &chain.owner).into_val(env),
461
+ sub_invokes: &[],
462
+ },
463
+ }]);
464
+ chain.oft.grant_role(&chain.owner, &role, &chain.owner);
465
+
466
+ env.mock_auths(&[MockAuth {
467
+ address: &chain.owner,
468
+ invoke: &MockAuthInvoke {
469
+ contract: &chain.oft.address,
470
+ fn_name: "set_default_fee_bps",
471
+ args: (&fee_bps, &chain.owner).into_val(env),
472
+ sub_invokes: &[],
473
+ },
474
+ }]);
475
+ chain.oft.set_default_fee_bps(&fee_bps, &chain.owner);
476
+ }
477
+
478
+ pub fn set_fee_bps(env: &Env, chain: &ChainSetup<'_>, dst_eid: u32, fee_bps: u32) {
479
+ let role = Symbol::new(env, FEE_CONFIG_MANAGER_ROLE);
480
+ env.mock_auths(&[MockAuth {
481
+ address: &chain.owner,
482
+ invoke: &MockAuthInvoke {
483
+ contract: &chain.oft.address,
484
+ fn_name: "grant_role",
485
+ args: (&chain.owner, &role, &chain.owner).into_val(env),
486
+ sub_invokes: &[],
487
+ },
488
+ }]);
489
+ chain.oft.grant_role(&chain.owner, &role, &chain.owner);
490
+
491
+ let id = dst_eid as u128;
492
+ let fee_bps_opt = Some(fee_bps);
493
+ env.mock_auths(&[MockAuth {
494
+ address: &chain.owner,
495
+ invoke: &MockAuthInvoke {
496
+ contract: &chain.oft.address,
497
+ fn_name: "set_fee_bps",
498
+ args: (&id, &fee_bps_opt, &chain.owner).into_val(env),
499
+ sub_invokes: &[],
500
+ },
501
+ }]);
502
+ chain.oft.set_fee_bps(&id, &fee_bps_opt, &chain.owner);
503
+ }
504
+
505
+ // ============================================================================
506
+ // Rate Limiter Extension Operations
507
+ // ============================================================================
508
+
509
+ /// Globally disables rate limiting on this chain. All rate limit checks are bypassed.
510
+ pub fn globally_disable_rate_limiter(env: &Env, chain: &ChainSetup<'_>) {
511
+ let role = Symbol::new(env, RATE_LIMITER_MANAGER_ROLE);
512
+ env.mock_auths(&[MockAuth {
513
+ address: &chain.owner,
514
+ invoke: &MockAuthInvoke {
515
+ contract: &chain.oft.address,
516
+ fn_name: "grant_role",
517
+ args: (&chain.owner, &role, &chain.owner).into_val(env),
518
+ sub_invokes: &[],
519
+ },
520
+ }]);
521
+ chain.oft.grant_role(&chain.owner, &role, &chain.owner);
522
+
523
+ let gc = Some(RateLimitGlobalConfig { use_global_state: false, is_globally_disabled: true });
524
+ env.mock_auths(&[MockAuth {
525
+ address: &chain.owner,
526
+ invoke: &MockAuthInvoke {
527
+ contract: &chain.oft.address,
528
+ fn_name: "set_rate_limit_global_config",
529
+ args: (&gc, &chain.owner).into_val(env),
530
+ sub_invokes: &[],
531
+ },
532
+ }]);
533
+ chain.oft.set_rate_limit_global_config(&gc, &chain.owner);
534
+ }
535
+
536
+ pub fn set_rate_limit_config(env: &Env, chain: &ChainSetup<'_>, dst_eid: u32, config: RateLimitConfig) {
537
+ let role = Symbol::new(env, RATE_LIMITER_MANAGER_ROLE);
538
+ env.mock_auths(&[MockAuth {
539
+ address: &chain.owner,
540
+ invoke: &MockAuthInvoke {
541
+ contract: &chain.oft.address,
542
+ fn_name: "grant_role",
543
+ args: (&chain.owner, &role, &chain.owner).into_val(env),
544
+ sub_invokes: &[],
545
+ },
546
+ }]);
547
+ chain.oft.grant_role(&chain.owner, &role, &chain.owner);
548
+
549
+ let id = dst_eid as u128;
550
+ let config_opt = Some(config);
551
+ env.mock_auths(&[MockAuth {
552
+ address: &chain.owner,
553
+ invoke: &MockAuthInvoke {
554
+ contract: &chain.oft.address,
555
+ fn_name: "set_rate_limit_config",
556
+ args: (&id, &config_opt, &chain.owner).into_val(env),
557
+ sub_invokes: &[],
558
+ },
559
+ }]);
560
+ chain.oft.set_rate_limit_config(&id, &config_opt, &chain.owner);
561
+ }
562
+
563
+ pub fn set_outbound_rate_limit(env: &Env, chain: &ChainSetup<'_>, dst_eid: u32, limit: i128, window: u64) {
564
+ set_rate_limit_config(
565
+ env,
566
+ chain,
567
+ dst_eid,
568
+ RateLimitConfig {
569
+ outbound_enabled: true,
570
+ inbound_enabled: false,
571
+ net_accounting_enabled: false,
572
+ address_exemption_enabled: false,
573
+ outbound_limit: limit,
574
+ inbound_limit: 0,
575
+ outbound_window: window,
576
+ inbound_window: 0,
577
+ },
578
+ );
579
+ }
580
+
581
+ pub fn set_bidirectional_net_rate_limit(env: &Env, chain: &ChainSetup<'_>, dst_eid: u32, limit: i128, window: u64) {
582
+ set_rate_limit_config(
583
+ env,
584
+ chain,
585
+ dst_eid,
586
+ RateLimitConfig {
587
+ outbound_enabled: true,
588
+ inbound_enabled: true,
589
+ net_accounting_enabled: true,
590
+ address_exemption_enabled: false,
591
+ outbound_limit: limit,
592
+ inbound_limit: limit,
593
+ outbound_window: window,
594
+ inbound_window: window,
595
+ },
596
+ );
597
+ }
598
+
599
+ pub fn set_inbound_rate_limit(env: &Env, chain: &ChainSetup<'_>, src_eid: u32, limit: i128, window: u64) {
600
+ set_rate_limit_config(
601
+ env,
602
+ chain,
603
+ src_eid,
604
+ RateLimitConfig {
605
+ outbound_enabled: false,
606
+ inbound_enabled: true,
607
+ net_accounting_enabled: false,
608
+ address_exemption_enabled: false,
609
+ outbound_limit: 0,
610
+ inbound_limit: limit,
611
+ outbound_window: 0,
612
+ inbound_window: window,
613
+ },
614
+ );
615
+ }
616
+
617
+ pub fn set_rate_limit_exemption(env: &Env, chain: &ChainSetup<'_>, user: &Address, is_exempt: bool) {
618
+ let role = Symbol::new(env, RATE_LIMITER_MANAGER_ROLE);
619
+ env.mock_auths(&[MockAuth {
620
+ address: &chain.owner,
621
+ invoke: &MockAuthInvoke {
622
+ contract: &chain.oft.address,
623
+ fn_name: "grant_role",
624
+ args: (&chain.owner, &role, &chain.owner).into_val(env),
625
+ sub_invokes: &[],
626
+ },
627
+ }]);
628
+ chain.oft.grant_role(&chain.owner, &role, &chain.owner);
629
+
630
+ env.mock_auths(&[MockAuth {
631
+ address: &chain.owner,
632
+ invoke: &MockAuthInvoke {
633
+ contract: &chain.oft.address,
634
+ fn_name: "set_rate_limit_exemption",
635
+ args: (user, &is_exempt, &chain.owner).into_val(env),
636
+ sub_invokes: &[],
637
+ },
638
+ }]);
639
+ chain.oft.set_rate_limit_exemption(user, &is_exempt, &chain.owner);
640
+ }
641
+
642
+ pub fn outbound_rate_limit_capacity(_env: &Env, chain: &ChainSetup<'_>, eid: u32) -> i128 {
643
+ let id = eid as u128;
644
+ chain.oft.get_rate_limit_usages(&id).outbound_available_amount
645
+ }
646
+
647
+ pub fn outbound_rate_limit_usage(_env: &Env, chain: &ChainSetup<'_>, eid: u32) -> i128 {
648
+ let id = eid as u128;
649
+ chain.oft.get_rate_limit_usages(&id).outbound_usage
650
+ }
651
+
652
+ pub fn inbound_rate_limit_capacity(_env: &Env, chain: &ChainSetup<'_>, eid: u32) -> i128 {
653
+ let id = eid as u128;
654
+ chain.oft.get_rate_limit_usages(&id).inbound_available_amount
655
+ }
656
+
657
+ // ============================================================================
658
+ // Time Utilities
659
+ // ============================================================================
660
+
661
+ pub fn advance_time(env: &Env, seconds: u64) {
662
+ let current = env.ledger().timestamp();
663
+ env.ledger().set_timestamp(current + seconds);
664
+ }
665
+
666
+ #[allow(dead_code)]
667
+ pub fn set_timestamp(env: &Env, timestamp: u64) {
668
+ env.ledger().set_timestamp(timestamp);
669
+ }
670
+
671
+ // ============================================================================
672
+ // SendParam Builder
673
+ // ============================================================================
674
+
675
+ pub fn create_send_param(env: &Env, dst_eid: u32, amount_ld: i128, min_amount_ld: i128, to: &BytesN<32>) -> SendParam {
676
+ SendParam {
677
+ dst_eid,
678
+ to: to.clone(),
679
+ amount_ld,
680
+ min_amount_ld,
681
+ extra_options: Bytes::new(env),
682
+ compose_msg: Bytes::new(env),
683
+ oft_cmd: Bytes::new(env),
684
+ }
685
+ }
@@ -0,0 +1,7 @@
1
+ use common_macros::contract_error;
2
+
3
+ #[contract_error]
4
+ pub enum OFTError {
5
+ /// The function is disabled.
6
+ Disabled,
7
+ }
@@ -0,0 +1,3 @@
1
+ pub mod oft_fee;
2
+ pub mod pausable;
3
+ pub mod rate_limiter;