@layerzerolabs/protocol-stellar-v2 0.2.19 → 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 (125) hide show
  1. package/.turbo/turbo-build.log +245 -222
  2. package/.turbo/turbo-lint.log +77 -70
  3. package/.turbo/turbo-test.log +1385 -1221
  4. package/Cargo.lock +13 -3
  5. package/Cargo.toml +2 -0
  6. package/contracts/ERROR_SPEC.md +8 -1
  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 +22 -6
  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/tests/test_oapp_core.rs +113 -65
  19. package/contracts/oapps/oapp/src/tests/test_oapp_options_type3.rs +111 -82
  20. package/contracts/oapps/oapp/src/tests/test_oapp_receiver.rs +293 -65
  21. package/contracts/oapps/oapp/src/tests/test_oapp_sender.rs +331 -56
  22. package/contracts/oapps/oft/src/extensions/oft_fee.rs +18 -2
  23. package/contracts/oapps/oft/src/extensions/pausable.rs +19 -4
  24. package/contracts/oapps/oft/src/extensions/rate_limiter.rs +52 -28
  25. package/contracts/oapps/oft/src/oft.rs +29 -41
  26. package/contracts/oapps/oft/src/oft_types/mint_burn.rs +3 -25
  27. package/contracts/oapps/oft-core/integration-tests/setup.rs +2 -2
  28. package/contracts/oapps/oft-core/src/oft_core.rs +247 -207
  29. package/contracts/oapps/oft-core/src/tests/test_utils.rs +4 -4
  30. package/contracts/upgrader/src/lib.rs +30 -57
  31. package/contracts/upgrader/src/tests/test_data/test_upgradeable_contract1.wasm +0 -0
  32. package/contracts/upgrader/src/tests/test_data/test_upgradeable_contract2.wasm +0 -0
  33. package/contracts/upgrader/src/tests/test_upgrader.rs +44 -35
  34. package/contracts/utils/src/buffer_reader.rs +1 -0
  35. package/contracts/utils/src/errors.rs +3 -1
  36. package/contracts/utils/src/tests/upgradeable.rs +372 -175
  37. package/contracts/utils/src/ttl_configurable.rs +3 -3
  38. package/contracts/utils/src/upgradeable.rs +48 -23
  39. package/contracts/workers/dvn/Cargo.toml +1 -0
  40. package/contracts/workers/dvn/src/auth.rs +12 -42
  41. package/contracts/workers/dvn/src/dvn.rs +16 -31
  42. package/contracts/workers/dvn/src/errors.rs +0 -1
  43. package/contracts/workers/dvn/src/interfaces/dvn.rs +35 -0
  44. package/contracts/workers/dvn/src/lib.rs +4 -3
  45. package/contracts/workers/dvn/src/tests/auth.rs +1 -1
  46. package/contracts/workers/dvn/src/tests/dvn.rs +19 -15
  47. package/contracts/workers/dvn/src/tests/multisig/set_threshold.rs +2 -4
  48. package/contracts/workers/dvn/src/tests/multisig/verify_signatures.rs +1 -3
  49. package/contracts/workers/dvn/src/tests/setup.rs +5 -9
  50. package/contracts/workers/dvn-fee-lib/Cargo.toml +1 -1
  51. package/contracts/workers/dvn-fee-lib/src/dvn_fee_lib.rs +3 -5
  52. package/contracts/workers/dvn-fee-lib/src/tests/dvn_fee_lib.rs +2 -3
  53. package/contracts/workers/executor/Cargo.toml +1 -0
  54. package/contracts/workers/executor/src/executor.rs +15 -26
  55. package/contracts/workers/executor-fee-lib/Cargo.toml +2 -1
  56. package/contracts/workers/executor-fee-lib/src/executor_fee_lib.rs +63 -5
  57. package/contracts/workers/executor-fee-lib/src/executor_option.rs +28 -1
  58. package/contracts/workers/executor-fee-lib/src/lib.rs +3 -0
  59. package/contracts/workers/executor-fee-lib/src/tests/executor_fee_lib.rs +701 -0
  60. package/contracts/workers/executor-fee-lib/src/tests/executor_option.rs +370 -0
  61. package/contracts/workers/executor-fee-lib/src/tests/mod.rs +4 -0
  62. package/contracts/workers/executor-fee-lib/src/tests/setup.rs +60 -0
  63. package/contracts/workers/executor-helper/src/lib.rs +3 -0
  64. package/contracts/workers/executor-helper/src/tests/executor_helper.rs +184 -0
  65. package/contracts/workers/executor-helper/src/tests/mod.rs +2 -0
  66. package/contracts/workers/executor-helper/src/tests/setup.rs +366 -0
  67. package/contracts/workers/fee-lib-interfaces/Cargo.toml +14 -0
  68. package/contracts/workers/{worker/src/interfaces/mod.rs → fee-lib-interfaces/src/lib.rs} +4 -3
  69. package/contracts/workers/price-feed/Cargo.toml +2 -1
  70. package/contracts/workers/price-feed/src/events.rs +1 -1
  71. package/contracts/workers/price-feed/src/lib.rs +3 -0
  72. package/contracts/workers/price-feed/src/price_feed.rs +6 -12
  73. package/contracts/workers/price-feed/src/storage.rs +1 -1
  74. package/contracts/workers/price-feed/src/tests/mod.rs +2 -0
  75. package/contracts/workers/price-feed/src/tests/price_feed.rs +869 -0
  76. package/contracts/workers/price-feed/src/tests/setup.rs +70 -0
  77. package/contracts/workers/price-feed/src/types.rs +1 -1
  78. package/contracts/workers/worker/src/errors.rs +0 -3
  79. package/contracts/workers/worker/src/lib.rs +0 -2
  80. package/contracts/workers/worker/src/storage.rs +32 -29
  81. package/contracts/workers/worker/src/tests/setup.rs +1 -7
  82. package/contracts/workers/worker/src/tests/worker.rs +50 -42
  83. package/contracts/workers/worker/src/worker.rs +49 -58
  84. package/package.json +3 -3
  85. package/sdk/.turbo/turbo-test.log +220 -218
  86. package/sdk/dist/generated/bml.d.ts +12 -4
  87. package/sdk/dist/generated/bml.js +8 -6
  88. package/sdk/dist/generated/counter.d.ts +12 -4
  89. package/sdk/dist/generated/counter.js +8 -6
  90. package/sdk/dist/generated/dvn.d.ts +404 -365
  91. package/sdk/dist/generated/dvn.js +55 -53
  92. package/sdk/dist/generated/dvn_fee_lib.d.ts +224 -268
  93. package/sdk/dist/generated/dvn_fee_lib.js +22 -53
  94. package/sdk/dist/generated/endpoint.d.ts +12 -4
  95. package/sdk/dist/generated/endpoint.js +8 -6
  96. package/sdk/dist/generated/executor.d.ts +370 -326
  97. package/sdk/dist/generated/executor.js +47 -44
  98. package/sdk/dist/generated/executor_fee_lib.d.ts +258 -302
  99. package/sdk/dist/generated/executor_fee_lib.js +21 -52
  100. package/sdk/dist/generated/executor_helper.d.ts +26 -190
  101. package/sdk/dist/generated/executor_helper.js +22 -27
  102. package/sdk/dist/generated/layerzero_view.d.ts +1271 -0
  103. package/sdk/dist/generated/layerzero_view.js +294 -0
  104. package/sdk/dist/generated/oft.d.ts +49 -40
  105. package/sdk/dist/generated/oft.js +25 -23
  106. package/sdk/dist/generated/price_feed.d.ts +225 -269
  107. package/sdk/dist/generated/price_feed.js +22 -53
  108. package/sdk/dist/generated/sml.d.ts +12 -4
  109. package/sdk/dist/generated/sml.js +8 -6
  110. package/sdk/dist/generated/treasury.d.ts +12 -4
  111. package/sdk/dist/generated/treasury.js +8 -6
  112. package/sdk/dist/generated/uln302.d.ts +12 -4
  113. package/sdk/dist/generated/uln302.js +10 -8
  114. package/sdk/dist/generated/upgrader.d.ts +189 -18
  115. package/sdk/dist/generated/upgrader.js +84 -4
  116. package/sdk/dist/index.d.ts +1 -0
  117. package/sdk/dist/index.js +2 -0
  118. package/sdk/package.json +1 -1
  119. package/sdk/src/index.ts +3 -0
  120. package/sdk/test/oft-sml.test.ts +4 -4
  121. package/sdk/test/upgrader.test.ts +2 -3
  122. package/tools/ts-bindings-gen/src/main.rs +2 -1
  123. /package/contracts/workers/{worker/src/interfaces → fee-lib-interfaces/src}/dvn_fee_lib.rs +0 -0
  124. /package/contracts/workers/{worker/src/interfaces → fee-lib-interfaces/src}/executor_fee_lib.rs +0 -0
  125. /package/contracts/workers/{worker/src/interfaces → fee-lib-interfaces/src}/price_feed.rs +0 -0
@@ -0,0 +1,70 @@
1
+ use soroban_sdk::{
2
+ testutils::{Address as _, MockAuth, MockAuthInvoke},
3
+ vec, Address, Env, IntoVal, Val, Vec,
4
+ };
5
+
6
+ use crate::{LzPriceFeed, LzPriceFeedClient};
7
+ use fee_lib_interfaces::Price;
8
+
9
+ // =============================================================================
10
+ // Test Setup
11
+ // =============================================================================
12
+
13
+ pub struct TestSetup<'a> {
14
+ pub env: Env,
15
+ pub contract_id: Address,
16
+ pub client: LzPriceFeedClient<'a>,
17
+ pub owner: Address,
18
+ pub price_updater: Address,
19
+ }
20
+
21
+ impl<'a> TestSetup<'a> {
22
+ pub fn new() -> Self {
23
+ let env = Env::default();
24
+ let owner = Address::generate(&env);
25
+ let price_updater = Address::generate(&env);
26
+
27
+ let contract_id = env.register(LzPriceFeed, (&owner, &price_updater));
28
+ let client = LzPriceFeedClient::new(&env, &contract_id);
29
+
30
+ Self { env, contract_id, client, owner, price_updater }
31
+ }
32
+
33
+ pub fn mock_auth<T: IntoVal<Env, Vec<Val>>>(&self, address: &Address, fn_name: &str, args: T) {
34
+ self.env.mock_auths(&[MockAuth {
35
+ address,
36
+ invoke: &MockAuthInvoke {
37
+ contract: &self.contract_id,
38
+ fn_name,
39
+ args: args.into_val(&self.env),
40
+ sub_invokes: &[],
41
+ },
42
+ }]);
43
+ }
44
+
45
+ pub fn mock_owner_auth<T: IntoVal<Env, Vec<Val>>>(&self, fn_name: &str, args: T) {
46
+ self.mock_auth(&self.owner, fn_name, args)
47
+ }
48
+
49
+ pub fn mock_price_updater_auth<T: IntoVal<Env, Vec<Val>>>(&self, fn_name: &str, args: T) {
50
+ self.mock_auth(&self.price_updater, fn_name, args)
51
+ }
52
+
53
+ /// Creates a default Price for testing
54
+ pub fn new_price(&self, price_ratio: u128, gas_price_in_unit: u64, gas_per_byte: u32) -> Price {
55
+ Price { price_ratio, gas_price_in_unit, gas_per_byte }
56
+ }
57
+
58
+ /// Creates a standard test price
59
+ pub fn default_test_price(&self) -> Price {
60
+ // price_ratio = 1e20 means 1:1 ratio
61
+ Price { price_ratio: 10u128.pow(20), gas_price_in_unit: 1_000_000, gas_per_byte: 16 }
62
+ }
63
+
64
+ /// Sets up a default model price for a destination EID
65
+ pub fn setup_default_price(&self, dst_eid: u32, price: &Price) {
66
+ let prices = vec![&self.env, crate::types::UpdatePrice { eid: dst_eid, price: price.clone() }];
67
+ self.mock_price_updater_auth("set_price", (&self.price_updater, &prices));
68
+ self.client.set_price(&self.price_updater, &prices);
69
+ }
70
+ }
@@ -1,5 +1,5 @@
1
+ use fee_lib_interfaces::Price;
1
2
  use soroban_sdk::contracttype;
2
- use worker::Price;
3
3
 
4
4
  /// Arbitrum-specific price extension
5
5
  #[contracttype]
@@ -10,17 +10,14 @@ pub enum WorkerError {
10
10
  AdminNotFound,
11
11
  AlreadyOnAllowlist,
12
12
  AlreadyOnDenylist,
13
- AttemptingToRemoveOnlyAdmin,
14
13
  DepositAddressNotSet,
15
14
  MessageLibAlreadySupported,
16
15
  MessageLibNotSupported,
17
- NoAdminsProvided,
18
16
  NotAllowed,
19
17
  NotOnAllowlist,
20
18
  NotOnDenylist,
21
19
  PauseStatusUnchanged,
22
20
  PriceFeedNotSet,
23
- ReInitialize,
24
21
  Unauthorized,
25
22
  UnsupportedMessageLib,
26
23
  WorkerFeeLibNotSet,
@@ -2,12 +2,10 @@
2
2
 
3
3
  pub mod errors;
4
4
  pub mod events;
5
- pub mod interfaces;
6
5
  pub mod storage;
7
6
 
8
7
  mod worker;
9
8
 
10
- pub use interfaces::*;
11
9
  pub use worker::*;
12
10
 
13
11
  #[cfg(test)]
@@ -9,31 +9,16 @@ pub enum WorkerStorage {
9
9
  #[default(false)]
10
10
  Paused,
11
11
 
12
- /// Address where worker fees are collected.
13
- #[instance(Address)]
14
- DepositAddress,
15
-
16
- /// Price feed contract address for cross-chain fee calculations.
17
- #[instance(Address)]
18
- PriceFeed,
19
-
20
- /// Worker fee library contract address for fee calculation logic.
21
- #[instance(Address)]
22
- WorkerFeeLib,
23
-
24
- /// Default fee multiplier in basis points (10000 = 1x).
25
- #[instance(u32)]
26
- DefaultMultiplierBps,
27
-
28
- /// Supported executor option types for a destination endpoint.
12
+ /// Admin addresses with configuration permissions.
29
13
  ///
30
- /// Encoded bytes defining which option types (lzReceive, lzCompose, nativeDrop, etc.)
31
- /// are supported for a specific destination chain.
32
- #[persistent(Bytes)]
33
- SupportedOptionTypes { eid: u32 },
14
+ /// Admins can modify worker settings but cannot change ownership.
15
+ #[persistent(Vec<Address>)]
16
+ #[default(Vec::new(env))]
17
+ Admins,
34
18
 
35
19
  /// List of supported message library addresses (e.g., ULN302).
36
20
  #[persistent(Vec<Address>)]
21
+ #[default(Vec::new(env))]
37
22
  MessageLibs,
38
23
 
39
24
  /// Allowlist status for an OApp address.
@@ -42,22 +27,40 @@ pub enum WorkerStorage {
42
27
  #[persistent(bool)]
43
28
  Allowlist { oapp: Address },
44
29
 
30
+ /// Counter for the number of addresses on the allowlist.
31
+ ///
32
+ /// Used to efficiently check if allowlist is empty (empty = allow all non-denylisted).
33
+ #[instance(u32)]
34
+ #[default(0)]
35
+ AllowlistSize,
36
+
45
37
  /// Denylist status for an OApp address.
46
38
  ///
47
39
  /// Denylisted OApps are blocked regardless of allowlist status.
48
40
  #[persistent(bool)]
49
41
  Denylist { oapp: Address },
50
42
 
51
- /// Counter for the number of addresses on the allowlist.
52
- ///
53
- /// Used to efficiently check if allowlist is empty (empty = allow all non-denylisted).
43
+ /// Default fee multiplier in basis points (10000 = 1x).
54
44
  #[instance(u32)]
55
45
  #[default(0)]
56
- AllowlistSize,
46
+ DefaultMultiplierBps,
57
47
 
58
- /// Admin addresses with configuration permissions.
48
+ /// Address where worker fees are collected.
49
+ #[instance(Address)]
50
+ DepositAddress,
51
+
52
+ /// Supported executor option types for a destination endpoint.
59
53
  ///
60
- /// Admins can modify worker settings but cannot change ownership.
61
- #[persistent(Vec<Address>)]
62
- Admins,
54
+ /// Since Stellar does not support Vec<u8>, we use Bytes instead where each byte
55
+ /// represents an option type (lzReceive, lzCompose, nativeDrop, etc.).
56
+ #[persistent(Bytes)]
57
+ SupportedOptionTypes { eid: u32 },
58
+
59
+ /// Worker fee library contract address for fee calculation logic.
60
+ #[instance(Address)]
61
+ WorkerFeeLib,
62
+
63
+ /// Price feed contract address for cross-chain fee calculations.
64
+ #[instance(Address)]
65
+ PriceFeed,
63
66
  }
@@ -3,7 +3,7 @@ use soroban_sdk::{
3
3
  Address, Bytes, Env, IntoVal, Val, Vec,
4
4
  };
5
5
 
6
- use crate::{init_worker, set_admin_by_admin, set_admin_by_owner, Worker};
6
+ use crate::{init_worker, set_admin_by_admin, Worker};
7
7
 
8
8
  use common_macros::{contract_impl, ownable};
9
9
  use soroban_sdk::contract;
@@ -37,12 +37,6 @@ impl WorkerTester {
37
37
  );
38
38
  }
39
39
 
40
- /// Test-only wrapper exposing `set_admin_by_owner` as a contract function
41
- /// so we can exercise auth + storage updates in unit tests.
42
- pub fn set_admin_by_owner_for_test(env: &Env, admin: &Address, active: bool) {
43
- set_admin_by_owner::<Self>(env, admin, active);
44
- }
45
-
46
40
  /// Test-only wrapper exposing `set_admin_by_admin` as a contract function
47
41
  /// so we can exercise auth + storage updates in unit tests.
48
42
  pub fn set_admin_by_admin_for_test(env: &Env, caller: &Address, admin: &Address, active: bool) {
@@ -12,7 +12,7 @@ use utils::testing_utils::assert_event;
12
12
  // pause
13
13
 
14
14
  #[test]
15
- #[should_panic(expected = "Error(Contract, #1212)")] // WorkerError::PauseStatusUnchanged
15
+ #[should_panic(expected = "Error(Contract, #1210)")] // WorkerError::PauseStatusUnchanged
16
16
  fn test_set_paused_rejects_unchanged_status() {
17
17
  let setup = TestSetup::new();
18
18
 
@@ -51,7 +51,7 @@ fn test_allowlist_rejects_duplicate_add() {
51
51
  }
52
52
 
53
53
  #[test]
54
- #[should_panic(expected = "Error(Contract, #1210)")] // WorkerError::NotOnAllowlist
54
+ #[should_panic(expected = "Error(Contract, #1208)")] // WorkerError::NotOnAllowlist
55
55
  fn test_allowlist_rejects_remove_missing() {
56
56
  let setup = TestSetup::new();
57
57
  let oapp = Address::generate(&setup.env);
@@ -101,7 +101,7 @@ fn test_denylist_rejects_duplicate_add() {
101
101
  }
102
102
 
103
103
  #[test]
104
- #[should_panic(expected = "Error(Contract, #1211)")] // WorkerError::NotOnDenylist
104
+ #[should_panic(expected = "Error(Contract, #1209)")] // WorkerError::NotOnDenylist
105
105
  fn test_denylist_rejects_remove_missing() {
106
106
  let setup = TestSetup::new();
107
107
  let oapp = Address::generate(&setup.env);
@@ -144,7 +144,7 @@ fn test_message_lib_add_remove_and_errors() {
144
144
  }
145
145
 
146
146
  #[test]
147
- #[should_panic(expected = "Error(Contract, #1206)")] // WorkerError::MessageLibAlreadySupported
147
+ #[should_panic(expected = "Error(Contract, #1205)")] // WorkerError::MessageLibAlreadySupported
148
148
  fn test_message_lib_rejects_duplicate_add() {
149
149
  let setup = TestSetup::new();
150
150
  let existing = setup.message_libs.get(0).unwrap();
@@ -154,7 +154,7 @@ fn test_message_lib_rejects_duplicate_add() {
154
154
  }
155
155
 
156
156
  #[test]
157
- #[should_panic(expected = "Error(Contract, #1207)")] // WorkerError::MessageLibNotSupported
157
+ #[should_panic(expected = "Error(Contract, #1206)")] // WorkerError::MessageLibNotSupported
158
158
  fn test_message_lib_rejects_remove_missing() {
159
159
  let setup = TestSetup::new();
160
160
  let missing = Address::generate(&setup.env);
@@ -166,7 +166,7 @@ fn test_message_lib_rejects_remove_missing() {
166
166
  // admin-only setters
167
167
 
168
168
  #[test]
169
- #[should_panic(expected = "Error(Contract, #1215)")] // WorkerError::Unauthorized
169
+ #[should_panic(expected = "Error(Contract, #1212)")] // WorkerError::Unauthorized
170
170
  fn test_admin_only_set_default_multiplier_requires_admin_membership() {
171
171
  let setup = TestSetup::new();
172
172
 
@@ -195,7 +195,7 @@ fn test_admin_setters_update_storage_and_events() {
195
195
  setup.mock_auth(&admin, "set_deposit_address", (&admin, &new_deposit));
196
196
  setup.client.set_deposit_address(&admin, &new_deposit);
197
197
  assert_event(&setup.env, &setup.contract_id, SetDepositAddress { deposit_address: new_deposit.clone() });
198
- assert_eq!(setup.client.deposit_address(), new_deposit);
198
+ assert_eq!(setup.client.deposit_address(), Some(new_deposit));
199
199
 
200
200
  // set_supported_option_types
201
201
  let eid_a = 1u32;
@@ -218,14 +218,14 @@ fn test_admin_setters_update_storage_and_events() {
218
218
  setup.mock_auth(&admin, "set_worker_fee_lib", (&admin, &new_fee_lib));
219
219
  setup.client.set_worker_fee_lib(&admin, &new_fee_lib);
220
220
  assert_event(&setup.env, &setup.contract_id, SetWorkerFeeLib { fee_lib: new_fee_lib.clone() });
221
- assert_eq!(setup.client.worker_fee_lib(), new_fee_lib);
221
+ assert_eq!(setup.client.worker_fee_lib(), Some(new_fee_lib));
222
222
 
223
223
  // set_price_feed
224
224
  let new_price_feed = Address::generate(&setup.env);
225
225
  setup.mock_auth(&admin, "set_price_feed", (&admin, &new_price_feed));
226
226
  setup.client.set_price_feed(&admin, &new_price_feed);
227
227
  assert_event(&setup.env, &setup.contract_id, SetPriceFeed { price_feed: new_price_feed.clone() });
228
- assert_eq!(setup.client.price_feed(), new_price_feed);
228
+ assert_eq!(setup.client.price_feed(), Some(new_price_feed));
229
229
  }
230
230
 
231
231
  // view functions
@@ -277,30 +277,27 @@ fn test_acl_allowlist_denylist_precedence() {
277
277
  // uninitialized getters (BareWorker)
278
278
 
279
279
  #[test]
280
- #[should_panic(expected = "Error(Contract, #1205)")] // WorkerError::DepositAddressNotSet
281
280
  fn test_view_panics_when_deposit_address_unset() {
282
281
  let env = Env::default();
283
282
  let owner = Address::generate(&env);
284
283
  let (_cid, client) = bare_worker(&env, &owner);
285
- let _ = client.deposit_address();
284
+ assert_eq!(client.deposit_address(), None);
286
285
  }
287
286
 
288
287
  #[test]
289
- #[should_panic(expected = "Error(Contract, #1213)")] // WorkerError::PriceFeedNotSet
290
288
  fn test_view_panics_when_price_feed_unset() {
291
289
  let env = Env::default();
292
290
  let owner = Address::generate(&env);
293
291
  let (_cid, client) = bare_worker(&env, &owner);
294
- let _ = client.price_feed();
292
+ assert_eq!(client.price_feed(), None);
295
293
  }
296
294
 
297
295
  #[test]
298
- #[should_panic(expected = "Error(Contract, #1217)")] // WorkerError::WorkerFeeLibNotSet
299
296
  fn test_view_panics_when_worker_fee_lib_unset() {
300
297
  let env = Env::default();
301
298
  let owner = Address::generate(&env);
302
299
  let (_cid, client) = bare_worker(&env, &owner);
303
- let _ = client.worker_fee_lib();
300
+ assert_eq!(client.worker_fee_lib(), None);
304
301
  }
305
302
 
306
303
  #[test]
@@ -314,8 +311,7 @@ fn test_default_multiplier_bps_returns_zero_when_unset() {
314
311
  // init_worker / construction
315
312
 
316
313
  #[test]
317
- #[should_panic(expected = "Error(Contract, #1208)")] // WorkerError::NoAdminsProvided
318
- fn test_init_worker_requires_non_empty_admins() {
314
+ fn test_init_worker_allows_empty_admins() {
319
315
  let env = Env::default();
320
316
 
321
317
  let owner = Address::generate(&env);
@@ -326,29 +322,39 @@ fn test_init_worker_requires_non_empty_admins() {
326
322
  let deposit_address = Address::generate(&env);
327
323
  let default_multiplier_bps = 10_000u32;
328
324
 
329
- // Panics inside __constructor via init_worker()
330
- let _ = env.register(
325
+ // Empty admins is now allowed
326
+ let cid = env.register(
331
327
  WorkerTester,
332
328
  (&owner, &admins, &message_libs, &price_feed, &default_multiplier_bps, &worker_fee_lib, &deposit_address),
333
329
  );
330
+ let client = super::setup::WorkerTesterClient::new(&env, &cid);
331
+ assert_eq!(client.admins().len(), 0);
334
332
  }
335
333
 
336
334
  #[test]
337
- #[should_panic(expected = "Error(Contract, #1214)")] // WorkerError::ReInitialize
338
- fn test_init_worker_cannot_reinitialize() {
335
+ fn test_init_worker_can_reinitialize() {
339
336
  let setup = TestSetup::new();
337
+ let new_admin = Address::generate(&setup.env);
338
+ let new_admins = vec![&setup.env, new_admin.clone()];
339
+ let new_message_lib = Address::generate(&setup.env);
340
+ let new_message_libs = vec![&setup.env, new_message_lib.clone()];
340
341
 
342
+ // Re-initialization is allowed - adds new admins and message libs
341
343
  setup.as_contract(|| {
342
344
  init_worker::<WorkerTester>(
343
345
  &setup.env,
344
- &setup.admins,
345
- &setup.message_libs,
346
+ &new_admins,
347
+ &new_message_libs, // Use new message libs to avoid duplicate error
346
348
  &setup.price_feed,
347
349
  setup.default_multiplier_bps,
348
350
  &setup.worker_fee_lib,
349
351
  &setup.deposit_address,
350
352
  );
351
353
  });
354
+
355
+ // Verify new admin and message lib were added
356
+ assert!(setup.client.is_admin(&new_admin));
357
+ assert!(setup.client.is_supported_message_lib(&new_message_lib));
352
358
  }
353
359
 
354
360
  #[test]
@@ -358,9 +364,9 @@ fn test_init_worker_sets_config_and_defaults() {
358
364
  assert_eq!(setup.client.paused(), false);
359
365
  assert_eq!(setup.client.admins(), setup.admins);
360
366
  assert_eq!(setup.client.message_libs(), setup.message_libs);
361
- assert_eq!(setup.client.price_feed(), setup.price_feed);
362
- assert_eq!(setup.client.worker_fee_lib(), setup.worker_fee_lib);
363
- assert_eq!(setup.client.deposit_address(), setup.deposit_address);
367
+ assert_eq!(setup.client.price_feed(), Some(setup.price_feed));
368
+ assert_eq!(setup.client.worker_fee_lib(), Some(setup.worker_fee_lib));
369
+ assert_eq!(setup.client.deposit_address(), Some(setup.deposit_address));
364
370
  assert_eq!(setup.client.default_multiplier_bps(), setup.default_multiplier_bps);
365
371
  assert_eq!(setup.client.allowlist_size(), 0);
366
372
 
@@ -375,13 +381,13 @@ fn test_admin_management_by_owner_adds_and_removes_admin() {
375
381
  let setup = TestSetup::new();
376
382
  let new_admin = Address::generate(&setup.env);
377
383
 
378
- setup.mock_owner_auth("set_admin_by_owner_for_test", (&new_admin, true));
379
- setup.client.set_admin_by_owner_for_test(&new_admin, &true);
384
+ setup.mock_owner_auth("set_admin", (&new_admin, true));
385
+ setup.client.set_admin(&new_admin, &true);
380
386
  assert_event(&setup.env, &setup.contract_id, SetAdmin { admin: new_admin.clone(), active: true });
381
387
  assert_eq!(setup.client.is_admin(&new_admin), true);
382
388
 
383
- setup.mock_owner_auth("set_admin_by_owner_for_test", (&new_admin, false));
384
- setup.client.set_admin_by_owner_for_test(&new_admin, &false);
389
+ setup.mock_owner_auth("set_admin", (&new_admin, false));
390
+ setup.client.set_admin(&new_admin, &false);
385
391
  assert_event(&setup.env, &setup.contract_id, SetAdmin { admin: new_admin.clone(), active: false });
386
392
  assert_eq!(setup.client.is_admin(&new_admin), false);
387
393
  }
@@ -392,8 +398,8 @@ fn test_admin_management_by_owner_rejects_duplicate_add() {
392
398
  let setup = TestSetup::new();
393
399
  let existing_admin = setup.admins.get(0).unwrap();
394
400
 
395
- setup.mock_owner_auth("set_admin_by_owner_for_test", (&existing_admin, true));
396
- setup.client.set_admin_by_owner_for_test(&existing_admin, &true);
401
+ setup.mock_owner_auth("set_admin", (&existing_admin, true));
402
+ setup.client.set_admin(&existing_admin, &true);
397
403
  }
398
404
 
399
405
  #[test]
@@ -402,13 +408,12 @@ fn test_admin_management_by_owner_rejects_remove_missing_admin() {
402
408
  let setup = TestSetup::new();
403
409
  let missing_admin = Address::generate(&setup.env);
404
410
 
405
- setup.mock_owner_auth("set_admin_by_owner_for_test", (&missing_admin, false));
406
- setup.client.set_admin_by_owner_for_test(&missing_admin, &false);
411
+ setup.mock_owner_auth("set_admin", (&missing_admin, false));
412
+ setup.client.set_admin(&missing_admin, &false);
407
413
  }
408
414
 
409
415
  #[test]
410
- #[should_panic(expected = "Error(Contract, #1204)")] // WorkerError::AttemptingToRemoveOnlyAdmin
411
- fn test_admin_management_cannot_remove_last_admin() {
416
+ fn test_admin_management_can_remove_last_admin() {
412
417
  let env = Env::default();
413
418
  let owner = Address::generate(&env);
414
419
  let admins: soroban_sdk::Vec<Address> = vec![&env, Address::generate(&env)];
@@ -429,13 +434,16 @@ fn test_admin_management_cannot_remove_last_admin() {
429
434
  address: &owner,
430
435
  invoke: &soroban_sdk::testutils::MockAuthInvoke {
431
436
  contract: &cid,
432
- fn_name: "set_admin_by_owner_for_test",
437
+ fn_name: "set_admin",
433
438
  args: (&only_admin, false).into_val(&env),
434
439
  sub_invokes: &[],
435
440
  },
436
441
  }]);
437
442
 
438
- client.set_admin_by_owner_for_test(&only_admin, &false);
443
+ // Removing the last admin is now allowed
444
+ client.set_admin(&only_admin, &false);
445
+ assert_eq!(client.is_admin(&only_admin), false);
446
+ assert_eq!(client.admins().len(), 0);
439
447
  }
440
448
 
441
449
  #[test]
@@ -451,7 +459,7 @@ fn test_admin_management_by_admin_can_add_admin() {
451
459
  }
452
460
 
453
461
  #[test]
454
- #[should_panic(expected = "Error(Contract, #1215)")] // WorkerError::Unauthorized
462
+ #[should_panic(expected = "Error(Contract, #1212)")] // WorkerError::Unauthorized
455
463
  fn test_admin_management_by_admin_requires_caller_is_admin() {
456
464
  let setup = TestSetup::new();
457
465
  let non_admin = Address::generate(&setup.env);
@@ -462,7 +470,7 @@ fn test_admin_management_by_admin_requires_caller_is_admin() {
462
470
  }
463
471
 
464
472
  #[test]
465
- #[should_panic(expected = "Error(Contract, #1209)")] // WorkerError::NotAllowed
473
+ #[should_panic(expected = "Error(Contract, #1207)")] // WorkerError::NotAllowed
466
474
  fn test_assert_acl_rejects_when_not_allowed() {
467
475
  let setup = TestSetup::new();
468
476
  let allowed = Address::generate(&setup.env);
@@ -478,7 +486,7 @@ fn test_assert_acl_rejects_when_not_allowed() {
478
486
  }
479
487
 
480
488
  #[test]
481
- #[should_panic(expected = "Error(Contract, #1216)")] // WorkerError::UnsupportedMessageLib
489
+ #[should_panic(expected = "Error(Contract, #1213)")] // WorkerError::UnsupportedMessageLib
482
490
  fn test_assert_supported_message_lib_rejects_unsupported() {
483
491
  let setup = TestSetup::new();
484
492
  let unsupported = Address::generate(&setup.env);
@@ -489,7 +497,7 @@ fn test_assert_supported_message_lib_rejects_unsupported() {
489
497
  }
490
498
 
491
499
  #[test]
492
- #[should_panic(expected = "Error(Contract, #1218)")] // WorkerError::WorkerIsPaused
500
+ #[should_panic(expected = "Error(Contract, #1215)")] // WorkerError::WorkerIsPaused
493
501
  fn test_assert_not_paused_panics_when_paused() {
494
502
  let setup = TestSetup::new();
495
503