@layerzerolabs/protocol-stellar-v2 0.2.40 → 0.2.41

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 (114) hide show
  1. package/.turbo/turbo-build.log +226 -313
  2. package/.turbo/turbo-lint.log +98 -227
  3. package/.turbo/turbo-test.log +1803 -1954
  4. package/contracts/common-macros/src/lib.rs +38 -15
  5. package/contracts/common-macros/src/lz_contract.rs +12 -21
  6. package/contracts/common-macros/src/tests/lz_contract.rs +17 -8
  7. package/contracts/common-macros/src/tests/snapshots/common_macros__tests__lz_contract__snapshot_generated_lz_contract_code.snap +20 -0
  8. package/contracts/common-macros/src/upgradeable.rs +37 -30
  9. package/contracts/endpoint-v2/src/endpoint_v2.rs +4 -3
  10. package/contracts/endpoint-v2/src/errors.rs +2 -2
  11. package/contracts/endpoint-v2/src/messaging_channel.rs +11 -0
  12. package/contracts/endpoint-v2/src/messaging_composer.rs +1 -0
  13. package/contracts/endpoint-v2/src/tests/endpoint_v2/clear.rs +12 -25
  14. package/contracts/endpoint-v2/src/tests/endpoint_v2/initializable.rs +4 -4
  15. package/contracts/endpoint-v2/src/tests/endpoint_v2/verifiable.rs +50 -10
  16. package/contracts/endpoint-v2/src/tests/endpoint_v2/verify.rs +6 -35
  17. package/contracts/endpoint-v2/src/tests/messaging_channel/burn.rs +2 -2
  18. package/contracts/endpoint-v2/src/tests/messaging_channel/clear_payload.rs +50 -1
  19. package/contracts/endpoint-v2/src/tests/messaging_channel/inbound.rs +78 -0
  20. package/contracts/endpoint-v2/src/tests/messaging_channel/insert_and_drain_pending_nonces.rs +272 -0
  21. package/contracts/endpoint-v2/src/tests/messaging_channel/mod.rs +1 -0
  22. package/contracts/endpoint-v2/src/tests/messaging_channel/nilify.rs +10 -5
  23. package/contracts/endpoint-v2/src/tests/messaging_channel/skip.rs +30 -0
  24. package/contracts/macro-integration-tests/tests/runtime/oapp/mod.rs +22 -1
  25. package/contracts/macro-integration-tests/tests/runtime/oapp/oapp_core.rs +13 -11
  26. package/contracts/macro-integration-tests/tests/runtime/oapp/options_type3.rs +13 -10
  27. package/contracts/macro-integration-tests/tests/runtime/oapp/receiver.rs +15 -11
  28. package/contracts/macro-integration-tests/tests/runtime/oapp/sender.rs +3 -2
  29. package/contracts/macro-integration-tests/tests/runtime/ownable/two_step_transfer.rs +14 -12
  30. package/contracts/macro-integration-tests/tests/runtime/upgradeable/migrate_guard_and_state.rs +3 -9
  31. package/contracts/macro-integration-tests/tests/ui/lz_contract/fail/upgradeable_invalid_inner_option.stderr +24 -1
  32. package/contracts/macro-integration-tests/tests/ui/lz_contract/fail/upgradeable_missing_internal.stderr +3 -3
  33. package/contracts/macro-integration-tests/tests/ui/lz_contract/pass/upgradeable_rbac.rs +44 -0
  34. package/contracts/macro-integration-tests/tests/ui/oapp/pass/custom_all.rs +3 -0
  35. package/contracts/macro-integration-tests/tests/ui/oapp/pass/custom_single_trait.rs +3 -0
  36. package/contracts/macro-integration-tests/tests/ui/ownable/pass/basic.rs +1 -1
  37. package/contracts/macro-integration-tests/tests/ui/upgradeable/fail/attr_args.stderr +1 -1
  38. package/contracts/macro-integration-tests/tests/ui/upgradeable/fail/missing_auth_trait.stderr +2 -2
  39. package/contracts/macro-integration-tests/tests/ui/upgradeable/fail/missing_upgradeable_internal.stderr +2 -2
  40. package/contracts/macro-integration-tests/tests/ui/upgradeable/pass/rbac.rs +44 -0
  41. package/contracts/oapps/counter/integration_tests/utils.rs +5 -3
  42. package/contracts/oapps/counter/src/tests/mod.rs +16 -1
  43. package/contracts/oapps/counter/src/tests/test_counter.rs +5 -2
  44. package/contracts/oapps/oapp/src/oapp_core.rs +21 -7
  45. package/contracts/oapps/oapp/src/oapp_options_type3.rs +7 -5
  46. package/contracts/oapps/oapp/src/tests/mod.rs +21 -0
  47. package/contracts/oapps/oapp/src/tests/oapp_core.rs +12 -10
  48. package/contracts/oapps/oapp/src/tests/oapp_options_type3.rs +11 -7
  49. package/contracts/oapps/oapp/src/tests/oapp_receiver.rs +4 -2
  50. package/contracts/oapps/oapp/src/tests/oapp_sender.rs +3 -2
  51. package/contracts/oapps/oapp/src/tests/test_macros.rs +15 -0
  52. package/contracts/oapps/oapp-macros/src/generators.rs +6 -0
  53. package/contracts/oapps/oapp-macros/src/tests/snapshots/oapp_macros__tests__oapp__snapshot_generate_oapp.snap +15 -0
  54. package/contracts/oapps/oft/integration-tests/setup.rs +22 -4
  55. package/contracts/oapps/oft/integration-tests/utils.rs +94 -13
  56. package/contracts/oapps/oft/src/extensions/oft_fee.rs +23 -10
  57. package/contracts/oapps/oft/src/extensions/pausable.rs +31 -10
  58. package/contracts/oapps/oft/src/extensions/rate_limiter.rs +9 -4
  59. package/contracts/oapps/oft/src/oft.rs +1 -2
  60. package/contracts/oapps/oft/src/tests/extensions/oft_fee.rs +39 -27
  61. package/contracts/oapps/oft/src/tests/extensions/pausable.rs +38 -24
  62. package/contracts/oapps/oft/src/tests/extensions/rate_limiter.rs +87 -69
  63. package/contracts/oapps/oft-core/integration-tests/setup.rs +27 -3
  64. package/contracts/oapps/oft-core/src/oft_core.rs +10 -5
  65. package/contracts/oapps/oft-core/src/tests/test_msg_inspector.rs +20 -20
  66. package/contracts/oapps/oft-core/src/tests/test_utils.rs +31 -3
  67. package/contracts/upgrader/src/lib.rs +67 -30
  68. package/contracts/upgrader/src/tests/test_data/test_upgradeable_contract3.wasm +0 -0
  69. package/contracts/upgrader/src/tests/test_data/test_upgradeable_contract4.wasm +0 -0
  70. package/contracts/upgrader/src/tests/test_upgrader.rs +50 -4
  71. package/contracts/utils/src/ownable.rs +16 -5
  72. package/contracts/utils/src/tests/ownable.rs +39 -39
  73. package/contracts/utils/src/upgradeable.rs +60 -17
  74. package/docs/oapp-guide.md +4 -4
  75. package/package.json +3 -4
  76. package/sdk/.turbo/turbo-test.log +381 -366
  77. package/sdk/dist/generated/bml.d.ts +4 -4
  78. package/sdk/dist/generated/bml.js +6 -6
  79. package/sdk/dist/generated/counter.d.ts +158 -12
  80. package/sdk/dist/generated/counter.js +32 -12
  81. package/sdk/dist/generated/dvn.d.ts +4 -6
  82. package/sdk/dist/generated/dvn.js +8 -8
  83. package/sdk/dist/generated/dvn_fee_lib.d.ts +8 -10
  84. package/sdk/dist/generated/dvn_fee_lib.js +8 -8
  85. package/sdk/dist/generated/endpoint.d.ts +9 -9
  86. package/sdk/dist/generated/endpoint.js +9 -9
  87. package/sdk/dist/generated/executor.d.ts +9 -11
  88. package/sdk/dist/generated/executor.js +11 -11
  89. package/sdk/dist/generated/executor_fee_lib.d.ts +9 -11
  90. package/sdk/dist/generated/executor_fee_lib.js +11 -11
  91. package/sdk/dist/generated/executor_helper.d.ts +4 -4
  92. package/sdk/dist/generated/executor_helper.js +6 -6
  93. package/sdk/dist/generated/layerzero_view.d.ts +9 -11
  94. package/sdk/dist/generated/layerzero_view.js +11 -11
  95. package/sdk/dist/generated/oft.d.ts +194 -27
  96. package/sdk/dist/generated/oft.js +44 -22
  97. package/sdk/dist/generated/price_feed.d.ts +8 -10
  98. package/sdk/dist/generated/price_feed.js +8 -8
  99. package/sdk/dist/generated/sac_manager.d.ts +8 -8
  100. package/sdk/dist/generated/sac_manager.js +6 -6
  101. package/sdk/dist/generated/sml.d.ts +9 -9
  102. package/sdk/dist/generated/sml.js +9 -9
  103. package/sdk/dist/generated/treasury.d.ts +9 -9
  104. package/sdk/dist/generated/treasury.js +9 -9
  105. package/sdk/dist/generated/uln302.d.ts +9 -9
  106. package/sdk/dist/generated/uln302.js +9 -9
  107. package/sdk/dist/generated/upgrader.d.ts +25 -16
  108. package/sdk/dist/generated/upgrader.js +5 -5
  109. package/sdk/package.json +1 -1
  110. package/sdk/test/counter-sml.test.ts +20 -0
  111. package/sdk/test/counter-uln.test.ts +20 -0
  112. package/sdk/test/oft-sml.test.ts +22 -0
  113. package/sdk/test/upgrader.test.ts +1 -0
  114. package/turbo.json +1 -8
@@ -1,6 +1,8 @@
1
1
  //! Utility functions for OFT-STD integration tests.
2
2
 
3
- use crate::extensions::rate_limiter::{Direction, Mode, RateLimitConfig};
3
+ use crate::extensions::rate_limiter::{Direction, Mode, RateLimitConfig, RATE_LIMITER_ADMIN_ROLE};
4
+ use crate::extensions::oft_fee::FEE_ADMIN_ROLE;
5
+ use crate::extensions::pausable::{PAUSER_ROLE, UNPAUSER_ROLE};
4
6
  use crate::integration_tests::setup::{decode_packet, ChainSetup};
5
7
  use crate::MintableClient;
6
8
  use endpoint_v2::{MessagingFee, Origin, OutboundPacket};
@@ -13,7 +15,6 @@ use soroban_sdk::{
13
15
  xdr::ToXdr,
14
16
  Address, Bytes, BytesN, Env, IntoVal, Map, Symbol, Val, Vec,
15
17
  };
16
-
17
18
  // ============================================================================
18
19
  // Address Conversion Utilities
19
20
  // ============================================================================
@@ -356,16 +357,44 @@ pub fn token_balance(env: &Env, token: &Address, account: &Address) -> i128 {
356
357
  // ============================================================================
357
358
 
358
359
  pub fn set_paused(env: &Env, chain: &ChainSetup<'_>, paused: bool) {
360
+ // `pause` / `unpause` are protected by RBAC (`PAUSER_ROLE` / `UNPAUSER_ROLE`). Grant them to owner for tests.
361
+ let pauser = Symbol::new(env, PAUSER_ROLE);
362
+ let unpauser = Symbol::new(env, UNPAUSER_ROLE);
359
363
  env.mock_auths(&[MockAuth {
360
364
  address: &chain.owner,
361
365
  invoke: &MockAuthInvoke {
362
366
  contract: &chain.oft.address,
363
- fn_name: "set_paused",
364
- args: (&paused,).into_val(env),
367
+ fn_name: "grant_role",
368
+ args: (&chain.owner, &pauser, &chain.owner).into_val(env),
365
369
  sub_invokes: &[],
366
370
  },
367
371
  }]);
368
- chain.oft.set_paused(&paused);
372
+ chain.oft.grant_role(&chain.owner, &pauser, &chain.owner);
373
+ env.mock_auths(&[MockAuth {
374
+ address: &chain.owner,
375
+ invoke: &MockAuthInvoke {
376
+ contract: &chain.oft.address,
377
+ fn_name: "grant_role",
378
+ args: (&chain.owner, &unpauser, &chain.owner).into_val(env),
379
+ sub_invokes: &[],
380
+ },
381
+ }]);
382
+ chain.oft.grant_role(&chain.owner, &unpauser, &chain.owner);
383
+
384
+ env.mock_auths(&[MockAuth {
385
+ address: &chain.owner,
386
+ invoke: &MockAuthInvoke {
387
+ contract: &chain.oft.address,
388
+ fn_name: if paused { "pause" } else { "unpause" },
389
+ args: (&chain.owner,).into_val(env),
390
+ sub_invokes: &[],
391
+ },
392
+ }]);
393
+ if paused {
394
+ chain.oft.pause(&chain.owner);
395
+ } else {
396
+ chain.oft.unpause(&chain.owner);
397
+ }
369
398
  }
370
399
 
371
400
  pub fn is_paused(chain: &ChainSetup<'_>) -> bool {
@@ -377,45 +406,84 @@ pub fn is_paused(chain: &ChainSetup<'_>) -> bool {
377
406
  // ============================================================================
378
407
 
379
408
  pub fn set_fee_deposit_address(env: &Env, chain: &ChainSetup<'_>, deposit_address: &Address) {
409
+ // `set_fee_deposit_address` is protected by RBAC (`FEE_ADMIN_ROLE`). Grant it to owner for tests.
410
+ let role = Symbol::new(env, FEE_ADMIN_ROLE);
411
+ env.mock_auths(&[MockAuth {
412
+ address: &chain.owner,
413
+ invoke: &MockAuthInvoke {
414
+ contract: &chain.oft.address,
415
+ fn_name: "grant_role",
416
+ args: (&chain.owner, &role, &chain.owner).into_val(env),
417
+ sub_invokes: &[],
418
+ },
419
+ }]);
420
+ chain.oft.grant_role(&chain.owner, &role, &chain.owner);
421
+
380
422
  let deposit_address_opt = Some(deposit_address.clone());
381
423
  env.mock_auths(&[MockAuth {
382
424
  address: &chain.owner,
383
425
  invoke: &MockAuthInvoke {
384
426
  contract: &chain.oft.address,
385
427
  fn_name: "set_fee_deposit_address",
386
- args: (&deposit_address_opt,).into_val(env),
428
+ args: (&deposit_address_opt, &chain.owner).into_val(env),
387
429
  sub_invokes: &[],
388
430
  },
389
431
  }]);
390
- chain.oft.set_fee_deposit_address(&deposit_address_opt);
432
+ chain.oft.set_fee_deposit_address(&deposit_address_opt, &chain.owner);
391
433
  }
392
434
 
393
435
  pub fn set_default_fee_bps(env: &Env, chain: &ChainSetup<'_>, fee_bps: u32) {
436
+ // `set_default_fee_bps` is protected by RBAC (`FEE_ADMIN_ROLE`). Grant it to owner for tests.
437
+ let role = Symbol::new(env, FEE_ADMIN_ROLE);
438
+ env.mock_auths(&[MockAuth {
439
+ address: &chain.owner,
440
+ invoke: &MockAuthInvoke {
441
+ contract: &chain.oft.address,
442
+ fn_name: "grant_role",
443
+ args: (&chain.owner, &role, &chain.owner).into_val(env),
444
+ sub_invokes: &[],
445
+ },
446
+ }]);
447
+ chain.oft.grant_role(&chain.owner, &role, &chain.owner);
448
+
394
449
  let fee_bps_opt = Some(fee_bps);
395
450
  env.mock_auths(&[MockAuth {
396
451
  address: &chain.owner,
397
452
  invoke: &MockAuthInvoke {
398
453
  contract: &chain.oft.address,
399
454
  fn_name: "set_default_fee_bps",
400
- args: (&fee_bps_opt,).into_val(env),
455
+ args: (&fee_bps_opt, &chain.owner).into_val(env),
401
456
  sub_invokes: &[],
402
457
  },
403
458
  }]);
404
- chain.oft.set_default_fee_bps(&fee_bps_opt);
459
+ chain.oft.set_default_fee_bps(&fee_bps_opt, &chain.owner);
405
460
  }
406
461
 
407
462
  pub fn set_fee_bps(env: &Env, chain: &ChainSetup<'_>, dst_eid: u32, fee_bps: u32) {
463
+ // `set_fee_bps` is protected by RBAC (`FEE_ADMIN_ROLE`). Grant it to owner for tests.
464
+ let role = Symbol::new(env, FEE_ADMIN_ROLE);
465
+ env.mock_auths(&[MockAuth {
466
+ address: &chain.owner,
467
+ invoke: &MockAuthInvoke {
468
+ contract: &chain.oft.address,
469
+ fn_name: "grant_role",
470
+ args: (&chain.owner, &role, &chain.owner).into_val(env),
471
+ sub_invokes: &[],
472
+ },
473
+ }]);
474
+ chain.oft.grant_role(&chain.owner, &role, &chain.owner);
475
+
408
476
  let fee_bps_opt = Some(fee_bps);
409
477
  env.mock_auths(&[MockAuth {
410
478
  address: &chain.owner,
411
479
  invoke: &MockAuthInvoke {
412
480
  contract: &chain.oft.address,
413
481
  fn_name: "set_fee_bps",
414
- args: (&dst_eid, &fee_bps_opt).into_val(env),
482
+ args: (&dst_eid, &fee_bps_opt, &chain.owner).into_val(env),
415
483
  sub_invokes: &[],
416
484
  },
417
485
  }]);
418
- chain.oft.set_fee_bps(&dst_eid, &fee_bps_opt);
486
+ chain.oft.set_fee_bps(&dst_eid, &fee_bps_opt, &chain.owner);
419
487
  }
420
488
 
421
489
  // ============================================================================
@@ -442,17 +510,30 @@ pub fn set_rate_limit_with_mode(
442
510
  window_seconds: u64,
443
511
  mode: Mode,
444
512
  ) {
513
+ // `set_rate_limit` is protected by RBAC (`RATE_LIMITER_ADMIN_ROLE`). Grant it to owner for tests.
514
+ let role = Symbol::new(env, RATE_LIMITER_ADMIN_ROLE);
515
+ env.mock_auths(&[MockAuth {
516
+ address: &chain.owner,
517
+ invoke: &MockAuthInvoke {
518
+ contract: &chain.oft.address,
519
+ fn_name: "grant_role",
520
+ args: (&chain.owner, &role, &chain.owner).into_val(env),
521
+ sub_invokes: &[],
522
+ },
523
+ }]);
524
+ chain.oft.grant_role(&chain.owner, &role, &chain.owner);
525
+
445
526
  let config = Some(RateLimitConfig { limit, window_seconds, mode });
446
527
  env.mock_auths(&[MockAuth {
447
528
  address: &chain.owner,
448
529
  invoke: &MockAuthInvoke {
449
530
  contract: &chain.oft.address,
450
531
  fn_name: "set_rate_limit",
451
- args: (direction, &dst_eid, &config).into_val(env),
532
+ args: (direction, &dst_eid, &config, &chain.owner).into_val(env),
452
533
  sub_invokes: &[],
453
534
  },
454
535
  }]);
455
- chain.oft.set_rate_limit(direction, &dst_eid, &config);
536
+ chain.oft.set_rate_limit(direction, &dst_eid, &config, &chain.owner);
456
537
  }
457
538
 
458
539
  pub fn rate_limit_capacity(chain: &ChainSetup<'_>, direction: &Direction, eid: u32) -> i128 {
@@ -1,6 +1,9 @@
1
- use common_macros::{contract_error, contract_trait, only_auth, storage};
1
+ use common_macros::{contract_error, contract_trait, only_role, storage};
2
2
  use soroban_sdk::{assert_with_error, contractevent, token::TokenClient, Address, Env};
3
- use utils::{auth::Auth, option_ext::OptionExt};
3
+ use utils::{option_ext::OptionExt, rbac::RoleBasedAccessControl};
4
+
5
+ /// Role for fee configuration (set_default_fee_bps, set_fee_bps, set_fee_deposit_address).
6
+ pub const FEE_ADMIN_ROLE: &str = "FEE_ADMIN_ROLE";
4
7
 
5
8
  /// Base fee in basis points (10,000 BPS = 100%)
6
9
  /// Used as denominator in fee calculations
@@ -69,7 +72,7 @@ pub struct FeeDepositAddressSet {
69
72
  // =========================================================================
70
73
 
71
74
  #[contract_trait]
72
- pub trait OFTFee: OFTFeeInternal + Auth {
75
+ pub trait OFTFee: OFTFeeInternal + RoleBasedAccessControl {
73
76
  // =========================================================================
74
77
  // Management Functions
75
78
  // =========================================================================
@@ -79,8 +82,9 @@ pub trait OFTFee: OFTFeeInternal + Auth {
79
82
  /// - `Some(n)`: sets the default fee to `n` basis points (must be >0 and <=10,000).
80
83
  /// - `Some(0)`: rejected — use `None` to remove the default fee instead.
81
84
  /// - `None`: removes the default fee (effective rate becomes 0).
82
- #[only_auth]
83
- fn set_default_fee_bps(env: &soroban_sdk::Env, default_fee_bps: &Option<u32>) {
85
+ /// * `operator` - The address that must have FEE_ADMIN_ROLE
86
+ #[only_role(operator, FEE_ADMIN_ROLE)]
87
+ fn set_default_fee_bps(env: &soroban_sdk::Env, default_fee_bps: &Option<u32>, operator: &soroban_sdk::Address) {
84
88
  Self::__set_default_fee_bps(env, default_fee_bps);
85
89
  }
86
90
 
@@ -92,14 +96,23 @@ pub trait OFTFee: OFTFeeInternal + Auth {
92
96
  /// # Arguments
93
97
  /// * `dst_eid` - The destination endpoint ID
94
98
  /// * `fee_bps` - The fee rate (0-10,000), or None to remove the fee configuration
95
- #[only_auth]
96
- fn set_fee_bps(env: &soroban_sdk::Env, dst_eid: u32, fee_bps: &Option<u32>) {
99
+ /// * `operator` - The address that must have FEE_ADMIN_ROLE
100
+ #[only_role(operator, FEE_ADMIN_ROLE)]
101
+ fn set_fee_bps(env: &soroban_sdk::Env, dst_eid: u32, fee_bps: &Option<u32>, operator: &soroban_sdk::Address) {
97
102
  Self::__set_fee_bps(env, dst_eid, fee_bps);
98
103
  }
99
104
 
100
105
  /// Sets or removes the address where collected fees will be deposited.
101
- #[only_auth]
102
- fn set_fee_deposit_address(env: &soroban_sdk::Env, fee_deposit_address: &Option<soroban_sdk::Address>) {
106
+ ///
107
+ /// # Arguments
108
+ /// * `fee_deposit_address` - The address to deposit fees to, or None to remove the fee deposit address
109
+ /// * `operator` - The address that must have FEE_ADMIN_ROLE
110
+ #[only_role(operator, FEE_ADMIN_ROLE)]
111
+ fn set_fee_deposit_address(
112
+ env: &soroban_sdk::Env,
113
+ fee_deposit_address: &Option<soroban_sdk::Address>,
114
+ operator: &soroban_sdk::Address,
115
+ ) {
103
116
  Self::__set_fee_deposit_address(env, fee_deposit_address);
104
117
  }
105
118
 
@@ -134,7 +147,7 @@ pub trait OFTFee: OFTFeeInternal + Auth {
134
147
  }
135
148
 
136
149
  /// Internal trait for OFT fee operations used by OFT hooks.
137
- /// Contains only truly internal methods that are called from OFTInternal implementations.
150
+ /// Contains only truly internal methods that are called from OFTFee implementations.
138
151
  pub trait OFTFeeInternal {
139
152
  // =========================================================================
140
153
  // OFT Hooks
@@ -1,6 +1,12 @@
1
- use common_macros::{contract_error, contract_trait, only_auth, storage};
1
+ use common_macros::{contract_error, contract_trait, only_role, storage};
2
2
  use soroban_sdk::{assert_with_error, contractevent, Env};
3
- use utils::auth::Auth;
3
+ use utils::rbac::RoleBasedAccessControl;
4
+
5
+ /// Role for pausing the contract.
6
+ pub const PAUSER_ROLE: &str = "PAUSER_ROLE";
7
+
8
+ /// Role for unpausing the contract.
9
+ pub const UNPAUSER_ROLE: &str = "UNPAUSER_ROLE";
4
10
 
5
11
  // =========================================================================
6
12
  // Storage
@@ -37,18 +43,33 @@ pub struct PausedSet {
37
43
  // =========================================================================
38
44
 
39
45
  #[contract_trait]
40
- pub trait OFTPausable: OFTPausableInternal + Auth {
41
- /// Sets the paused state of the OFT.
46
+ pub trait OFTPausable: OFTPausableInternal + RoleBasedAccessControl {
47
+ // =========================================================================
48
+ // Management Functions
49
+ // =========================================================================
50
+
51
+ /// Pauses the OFT. When paused, the OFT will reject new send/receive/quote_send/quote_oft operations.
42
52
  ///
43
- /// When paused, the OFT will reject new send/receive/quote_send/quote_oft operations.
53
+ /// # Arguments
54
+ /// * `operator` - The address that must have PAUSER_ROLE
55
+ #[only_role(operator, PAUSER_ROLE)]
56
+ fn pause(env: &soroban_sdk::Env, operator: &soroban_sdk::Address) {
57
+ Self::__set_paused(env, true);
58
+ }
59
+
60
+ /// Unpauses the OFT.
44
61
  ///
45
62
  /// # Arguments
46
- /// * `paused` - `true` to pause, `false` to unpause
47
- #[only_auth]
48
- fn set_paused(env: &soroban_sdk::Env, paused: bool) {
49
- Self::__set_paused(env, paused);
63
+ /// * `operator` - The address that must have UNPAUSER_ROLE
64
+ #[only_role(operator, UNPAUSER_ROLE)]
65
+ fn unpause(env: &soroban_sdk::Env, operator: &soroban_sdk::Address) {
66
+ Self::__set_paused(env, false);
50
67
  }
51
68
 
69
+ // =========================================================================
70
+ // View Functions
71
+ // =========================================================================
72
+
52
73
  /// Returns the paused state of the OFT.
53
74
  fn is_paused(env: &soroban_sdk::Env) -> bool {
54
75
  Self::__is_paused(env)
@@ -56,7 +77,7 @@ pub trait OFTPausable: OFTPausableInternal + Auth {
56
77
  }
57
78
 
58
79
  /// Internal trait for pausable operations used by OFT hooks.
59
- /// Contains only truly internal methods that are called from OFTInternal implementations.
80
+ /// Contains only truly internal methods that are called from OFTPausable implementations.
60
81
  pub trait OFTPausableInternal {
61
82
  // =========================================================================
62
83
  // OFT Hooks
@@ -1,7 +1,10 @@
1
1
  use crate as oft;
2
- use common_macros::{contract_error, contract_trait, only_auth, storage};
2
+ use common_macros::{contract_error, contract_trait, only_role, storage};
3
3
  use soroban_sdk::{assert_with_error, contractevent, contracttype, Env};
4
- use utils::auth::Auth;
4
+ use utils::rbac::RoleBasedAccessControl;
5
+
6
+ /// Role for rate limiter configuration (set_rate_limit).
7
+ pub const RATE_LIMITER_ADMIN_ROLE: &str = "RATE_LIMITER_ADMIN_ROLE";
5
8
 
6
9
  // =========================================================================
7
10
  // Types
@@ -94,7 +97,7 @@ pub struct RateLimitSet {
94
97
  // =========================================================================
95
98
 
96
99
  #[contract_trait]
97
- pub trait RateLimiter: RateLimiterInternal + Auth {
100
+ pub trait RateLimiter: RateLimiterInternal + RoleBasedAccessControl {
98
101
  // =========================================================================
99
102
  // Management Functions
100
103
  // =========================================================================
@@ -105,12 +108,14 @@ pub trait RateLimiter: RateLimiterInternal + Auth {
105
108
  /// * `direction` - The direction (Inbound or Outbound)
106
109
  /// * `eid` - The endpoint ID
107
110
  /// * `config` - The rate limit configuration, or None to remove the rate limit
108
- #[only_auth]
111
+ /// * `operator` - The address that must have RATE_LIMITER_ADMIN_ROLE
112
+ #[only_role(operator, RATE_LIMITER_ADMIN_ROLE)]
109
113
  fn set_rate_limit(
110
114
  env: &soroban_sdk::Env,
111
115
  direction: &oft::rate_limiter::Direction,
112
116
  eid: u32,
113
117
  config: &Option<oft::rate_limiter::RateLimitConfig>,
118
+ operator: &soroban_sdk::Address,
114
119
  ) {
115
120
  Self::__set_rate_limit(env, direction, eid, config);
116
121
  }
@@ -42,11 +42,10 @@ impl OFT {
42
42
  token: &Address,
43
43
  shared_decimals: u32,
44
44
  oft_type: OftType,
45
- owner: &Address,
46
45
  endpoint: &Address,
47
46
  delegate: &Address,
48
47
  ) {
49
- Self::__initialize_oft(env, token, shared_decimals, owner, endpoint, delegate);
48
+ Self::__initialize_oft(env, token, shared_decimals, delegate, endpoint, delegate);
50
49
  OFTStorage::set_oft_type(env, &oft_type);
51
50
  }
52
51
 
@@ -1,13 +1,15 @@
1
1
  extern crate std;
2
2
 
3
3
  use crate::extensions::oft_fee::{OFTFee, OFTFeeError, OFTFeeInternal};
4
+ use crate::extensions::oft_fee::FEE_ADMIN_ROLE;
4
5
  use soroban_sdk::{
5
6
  contract, contractimpl,
6
7
  testutils::{Address as _, MockAuth, MockAuthInvoke},
7
8
  token::{StellarAssetClient, TokenClient},
8
- Address, Env, IntoVal,
9
+ Address, Env, IntoVal, Symbol,
9
10
  };
10
11
  use utils::auth::Auth;
12
+ use utils::rbac::{grant_role_no_auth, RoleBasedAccessControl};
11
13
 
12
14
  // ============================================================================
13
15
  // Test Contract
@@ -27,8 +29,17 @@ impl OFTFeeInternal for FeeTestContract {}
27
29
  #[contractimpl(contracttrait)]
28
30
  impl OFTFee for FeeTestContract {}
29
31
 
32
+ #[contractimpl(contracttrait)]
33
+ impl RoleBasedAccessControl for FeeTestContract {}
34
+
30
35
  #[contractimpl]
31
36
  impl FeeTestContract {
37
+ /// Test-only: grants FEE_ADMIN_ROLE to the contract.
38
+ pub fn init_roles(env: Env) {
39
+ let contract_id = env.current_contract_address();
40
+ grant_role_no_auth(&env, &contract_id, &Symbol::new(&env, FEE_ADMIN_ROLE), &contract_id);
41
+ }
42
+
32
43
  pub fn fee_view(env: Env, dst_eid: u32, amount_ld: i128) -> i128 {
33
44
  <Self as OFTFeeInternal>::__fee_view(&env, dst_eid, amount_ld)
34
45
  }
@@ -59,6 +70,7 @@ fn setup() -> TestSetup {
59
70
 
60
71
  let contract_id = env.register(FeeTestContract, ());
61
72
  let client = FeeTestContractClient::new(&env, &contract_id);
73
+ client.init_roles();
62
74
 
63
75
  let token_admin = Address::generate(&env);
64
76
  let sac = env.register_stellar_asset_contract_v2(token_admin.clone());
@@ -94,9 +106,9 @@ fn test_fee_deposit_address_returns_none_when_unset() {
94
106
 
95
107
  #[test]
96
108
  fn test_fee_view_nonzero_fee_errors_when_deposit_address_unset() {
97
- let TestSetup { client, .. } = setup();
109
+ let TestSetup { client, contract_id, .. } = setup();
98
110
 
99
- client.set_default_fee_bps(&Some(100u32));
111
+ client.set_default_fee_bps(&Some(100u32), &contract_id);
100
112
 
101
113
  let res = client.try_fee_view(&7u32, &1_000_000i128);
102
114
  assert_eq!(res.err().unwrap().ok().unwrap(), OFTFeeError::InvalidFeeDepositAddress.into());
@@ -108,29 +120,29 @@ fn test_fee_view_nonzero_fee_errors_when_deposit_address_unset() {
108
120
 
109
121
  #[test]
110
122
  fn test_set_default_fee_bps_rejects_invalid_value() {
111
- let TestSetup { client, .. } = setup();
123
+ let TestSetup { client, contract_id, .. } = setup();
112
124
 
113
125
  // Zero is not a valid default fee (use None to remove instead)
114
- let res = client.try_set_default_fee_bps(&Some(0u32));
126
+ let res = client.try_set_default_fee_bps(&Some(0u32), &contract_id);
115
127
  assert_eq!(res.err().unwrap().ok().unwrap(), OFTFeeError::InvalidFeeBps.into());
116
128
 
117
129
  // Exceeds maximum
118
- let res = client.try_set_default_fee_bps(&Some(10_001u32));
130
+ let res = client.try_set_default_fee_bps(&Some(10_001u32), &contract_id);
119
131
  assert_eq!(res.err().unwrap().ok().unwrap(), OFTFeeError::InvalidFeeBps.into());
120
132
  }
121
133
 
122
134
  #[test]
123
135
  fn test_set_default_fee_bps_rejects_same_value() {
124
- let TestSetup { client, .. } = setup();
136
+ let TestSetup { client, contract_id, .. } = setup();
125
137
 
126
138
  // None when already None (not set)
127
139
  let none: Option<u32> = None;
128
- let res = client.try_set_default_fee_bps(&none);
140
+ let res = client.try_set_default_fee_bps(&none, &contract_id);
129
141
  assert_eq!(res.err().unwrap().ok().unwrap(), OFTFeeError::SameValue.into());
130
142
 
131
143
  // Same value when already set
132
- client.set_default_fee_bps(&Some(123u32));
133
- let res2 = client.try_set_default_fee_bps(&Some(123u32));
144
+ client.set_default_fee_bps(&Some(123u32), &contract_id);
145
+ let res2 = client.try_set_default_fee_bps(&Some(123u32), &contract_id);
134
146
  assert_eq!(res2.err().unwrap().ok().unwrap(), OFTFeeError::SameValue.into());
135
147
  }
136
148
 
@@ -140,27 +152,27 @@ fn test_set_default_fee_bps_rejects_same_value() {
140
152
 
141
153
  #[test]
142
154
  fn test_set_fee_bps_rejects_invalid_and_same_value() {
143
- let TestSetup { client, .. } = setup();
155
+ let TestSetup { client, contract_id, .. } = setup();
144
156
  let dst_eid = 101u32;
145
157
 
146
- let res = client.try_set_fee_bps(&dst_eid, &None);
158
+ let res = client.try_set_fee_bps(&dst_eid, &None, &contract_id);
147
159
  assert_eq!(res.err().unwrap().ok().unwrap(), OFTFeeError::SameValue.into());
148
160
 
149
- let res2 = client.try_set_fee_bps(&dst_eid, &Some(10_001u32));
161
+ let res2 = client.try_set_fee_bps(&dst_eid, &Some(10_001u32), &contract_id);
150
162
  assert_eq!(res2.err().unwrap().ok().unwrap(), OFTFeeError::InvalidFeeBps.into());
151
163
  }
152
164
 
153
165
  #[test]
154
166
  fn test_set_fee_bps_set_and_remove() {
155
- let TestSetup { client, .. } = setup();
167
+ let TestSetup { client, contract_id, .. } = setup();
156
168
  let dst_eid = 101u32;
157
169
 
158
- client.set_fee_bps(&dst_eid, &Some(200u32));
170
+ client.set_fee_bps(&dst_eid, &Some(200u32), &contract_id);
159
171
  assert_eq!(client.fee_bps(&dst_eid), Some(200u32));
160
172
  assert_eq!(client.effective_fee_bps(&dst_eid), 200u32);
161
173
 
162
- client.set_default_fee_bps(&Some(111u32));
163
- client.set_fee_bps(&dst_eid, &None);
174
+ client.set_default_fee_bps(&Some(111u32), &contract_id);
175
+ client.set_fee_bps(&dst_eid, &None, &contract_id);
164
176
  assert_eq!(client.fee_bps(&dst_eid), None);
165
177
  assert_eq!(client.effective_fee_bps(&dst_eid), 111u32);
166
178
  }
@@ -195,19 +207,19 @@ fn test_charge_fee_errors_without_deposit_address() {
195
207
 
196
208
  #[test]
197
209
  fn test_set_fee_deposit_address_same_value() {
198
- let TestSetup { client, fee_deposit, .. } = setup();
210
+ let TestSetup { client, contract_id, fee_deposit, .. } = setup();
199
211
 
200
212
  let fee_deposit_opt = Some(fee_deposit);
201
- client.set_fee_deposit_address(&fee_deposit_opt);
202
- let res = client.try_set_fee_deposit_address(&fee_deposit_opt);
213
+ client.set_fee_deposit_address(&fee_deposit_opt, &contract_id);
214
+ let res = client.try_set_fee_deposit_address(&fee_deposit_opt, &contract_id);
203
215
  assert_eq!(res.err().unwrap().ok().unwrap(), OFTFeeError::SameValue.into());
204
216
  }
205
217
 
206
218
  #[test]
207
219
  fn test_charge_fee_zero_amount_no_transfer() {
208
- let TestSetup { env, client, token, from, fee_deposit, .. } = setup();
220
+ let TestSetup { env, client, contract_id, token, from, fee_deposit, .. } = setup();
209
221
 
210
- client.set_fee_deposit_address(&Some(fee_deposit.clone()));
222
+ client.set_fee_deposit_address(&Some(fee_deposit.clone()), &contract_id);
211
223
 
212
224
  let token_client = TokenClient::new(&env, &token);
213
225
  let from_before = token_client.balance(&from);
@@ -222,9 +234,9 @@ fn test_charge_fee_zero_amount_no_transfer() {
222
234
 
223
235
  #[test]
224
236
  fn test_charge_fee_transfers() {
225
- let TestSetup { env, client, token, from, fee_deposit, .. } = setup();
237
+ let TestSetup { env, client, contract_id, token, from, fee_deposit, .. } = setup();
226
238
 
227
- client.set_fee_deposit_address(&Some(fee_deposit.clone()));
239
+ client.set_fee_deposit_address(&Some(fee_deposit.clone()), &contract_id);
228
240
 
229
241
  let token_client = TokenClient::new(&env, &token);
230
242
  let from_before = token_client.balance(&from);
@@ -238,10 +250,10 @@ fn test_charge_fee_transfers() {
238
250
 
239
251
  #[test]
240
252
  fn test_fee_view_computes_correct_fee() {
241
- let TestSetup { client, fee_deposit, .. } = setup();
253
+ let TestSetup { client, contract_id, fee_deposit, .. } = setup();
242
254
 
243
- client.set_fee_deposit_address(&Some(fee_deposit));
244
- client.set_default_fee_bps(&Some(100u32)); // 1%
255
+ client.set_fee_deposit_address(&Some(fee_deposit), &contract_id);
256
+ client.set_default_fee_bps(&Some(100u32), &contract_id); // 1%
245
257
 
246
258
  let fee = client.fee_view(&999u32, &10_000i128);
247
259
  assert_eq!(fee, 100); // 1% of 10,000