@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
@@ -1,6 +1,6 @@
1
1
  use crate::{
2
2
  self as utils,
3
- auth::{require_auth, Auth},
3
+ auth::{self, Auth},
4
4
  errors::TtlConfigurableError,
5
5
  };
6
6
  use common_macros::contract_trait;
@@ -112,7 +112,7 @@ pub trait TtlConfigurable: Auth {
112
112
  /// - `TtlConfigFrozen` if configs are frozen
113
113
  /// - `InvalidTtlConfig` if validation fails
114
114
  fn set_ttl_configs(env: &Env, instance: &Option<TtlConfig>, persistent: &Option<TtlConfig>) {
115
- require_auth::<Self>(env);
115
+ auth::require_auth::<Self>(env);
116
116
  assert_with_error!(env, !Self::is_ttl_configs_frozen(env), TtlConfigurableError::TtlConfigFrozen);
117
117
 
118
118
  let max_ttl = u32::min(MAX_TTL, env.storage().max_ttl());
@@ -138,7 +138,7 @@ pub trait TtlConfigurable: Auth {
138
138
  /// # Panics
139
139
  /// - `TtlConfigAlreadyFrozen` if already frozen
140
140
  fn freeze_ttl_configs(env: &Env) {
141
- require_auth::<Self>(env);
141
+ auth::require_auth::<Self>(env);
142
142
  assert_with_error!(env, !Self::is_ttl_configs_frozen(env), TtlConfigurableError::TtlConfigAlreadyFrozen);
143
143
 
144
144
  TtlConfigStorage::set_frozen(env, &true);
@@ -1,20 +1,56 @@
1
- use crate::errors::UpgradeableError;
2
- use common_macros::storage;
3
- use soroban_sdk::{assert_with_error, BytesN, Env, FromVal, Val};
1
+ use crate::{
2
+ self as utils,
3
+ auth::{self, Auth},
4
+ errors::UpgradeableError,
5
+ option_ext::OptionExt,
6
+ };
7
+ use common_macros::{contract_trait, storage};
8
+ use soroban_sdk::{assert_with_error, xdr::FromXdr, Bytes, BytesN, Env};
4
9
 
5
10
  // ============================================
6
11
  // Upgradeable Interface
7
12
  // ============================================
8
13
 
9
14
  /// Trait for contracts with upgrade and migration support.
10
- pub trait Upgradeable: UpgradeableInternal {
15
+ #[contract_trait]
16
+ pub trait Upgradeable: UpgradeableInternal + Auth {
17
+ /// The type of data required for migration.
11
18
  /// Upgrades the contract to new WASM bytecode.
12
19
  /// Sets a migration flag that must be cleared by calling `migrate`.
13
- fn upgrade(env: &Env, new_wasm_hash: BytesN<32>);
20
+ fn upgrade(env: &Env, new_wasm_hash: &BytesN<32>) {
21
+ auth::require_auth::<Self>(env);
22
+ assert_with_error!(env, !Self::is_frozen(env), UpgradeableError::UpgradesFrozen);
23
+ UpgradeableStorage::set_migrating(env, &true);
24
+ env.deployer().update_current_contract_wasm(new_wasm_hash.clone());
25
+ }
14
26
 
15
27
  /// Runs migration logic after an upgrade.
16
28
  /// Can only be called when the migration flag is set by a previous `upgrade` call.
17
- fn migrate(env: &Env, migration_data: &Self::MigrationData);
29
+ fn migrate(env: &Env, migration_data: &Bytes) {
30
+ auth::require_auth::<Self>(env);
31
+ assert_with_error!(env, UpgradeableStorage::migrating(env), UpgradeableError::MigrationNotAllowed);
32
+
33
+ // Parse the migration data and call the internal migration logic
34
+ let parsed_data = Self::MigrationData::from_xdr(env, migration_data)
35
+ .ok()
36
+ .unwrap_or_panic(env, UpgradeableError::InvalidMigrationData);
37
+ Self::__migrate(env, &parsed_data);
38
+
39
+ // Clear the migration flag after migration is successful
40
+ UpgradeableStorage::set_migrating(env, &false);
41
+ }
42
+
43
+ /// Permanently freezes the contract, preventing any future upgrades.
44
+ /// This action is irreversible.
45
+ fn freeze(env: &Env) {
46
+ auth::require_auth::<Self>(env);
47
+ UpgradeableStorage::set_frozen(env, &true);
48
+ }
49
+
50
+ /// Returns whether the contract is frozen (upgrades disabled).
51
+ fn is_frozen(env: &Env) -> bool {
52
+ UpgradeableStorage::frozen(env)
53
+ }
18
54
  }
19
55
 
20
56
  /// Trait for defining contract-specific migration logic.
@@ -22,7 +58,7 @@ pub trait Upgradeable: UpgradeableInternal {
22
58
  pub trait UpgradeableInternal {
23
59
  /// The type of data required for migration.
24
60
  /// This is the type used as the `migration_data` parameter in `Upgradeable::migrate` for this contract.
25
- type MigrationData: FromVal<Env, Val>;
61
+ type MigrationData: FromXdr;
26
62
 
27
63
  /// Internal migration logic to be implemented by the contract.
28
64
  fn __migrate(env: &Env, migration_data: &Self::MigrationData);
@@ -36,21 +72,10 @@ pub trait UpgradeableInternal {
36
72
  #[storage]
37
73
  pub enum UpgradeableStorage {
38
74
  #[instance(bool)]
39
- Migrating,
40
- }
41
-
42
- /// Upgrades the contract to new WASM bytecode and sets the migration flag.
43
- pub fn upgrade(env: &Env, new_wasm_hash: BytesN<32>) {
44
- UpgradeableStorage::set_migrating(env, &true);
45
- env.deployer().update_current_contract_wasm(new_wasm_hash);
46
- }
75
+ #[default(false)]
76
+ Frozen,
47
77
 
48
- /// Runs migration logic after an upgrade.
49
- /// Can only be called when the migration flag is set by a previous `upgrade` call.
50
- pub fn migrate<T: UpgradeableInternal>(env: &Env, migration_data: &T::MigrationData) {
51
- assert_with_error!(env, UpgradeableStorage::migrating(env).unwrap_or(false), UpgradeableError::MigrationNotAllowed);
52
- UpgradeableStorage::set_migrating(env, &false);
53
- T::__migrate(env, migration_data);
78
+ #[instance(bool)]
79
+ #[default(false)]
80
+ Migrating,
54
81
  }
55
-
56
- // TODO (hanson): review and polish this
@@ -17,6 +17,7 @@ doctest = false
17
17
  cfg-if = "1.0"
18
18
  soroban-sdk = { workspace = true, features = ["hazmat-crypto"] }
19
19
  worker = { workspace = true }
20
+ fee-lib-interfaces = { workspace = true }
20
21
  message-lib-common = { workspace = true }
21
22
  utils = { workspace = true }
22
23
  common-macros = { workspace = true }
@@ -1,43 +1,12 @@
1
1
  use super::*;
2
+ use crate::{Sender, TransactionAuthData};
2
3
  use soroban_sdk::{
3
4
  address_payload::AddressPayload,
4
5
  auth::{Context, CustomAccountInterface},
5
- contracttype,
6
6
  crypto::Hash,
7
7
  vec, Symbol,
8
8
  };
9
9
 
10
- // ============================================================================
11
- // Authentication Data Types
12
- // ============================================================================
13
-
14
- #[contracttype]
15
- #[derive(Clone, Debug, Eq, PartialEq)]
16
- pub enum Sender {
17
- /// No explicit sender (permissionless execution).
18
- None,
19
- /// A registered admin (ed25519) submitting the transaction.
20
- /// The tuple is `(public_key, signature)` where the signature covers the Soroban payload.
21
- Admin(BytesN<32>, BytesN<64>),
22
- }
23
-
24
- /// Authentication data for DVN contract transactions.
25
- ///
26
- /// This struct is used with Soroban's custom account interface to authorize
27
- /// transactions through a combination of admin signature and multisig quorum.
28
- #[contracttype]
29
- #[derive(Clone, Debug, Eq, PartialEq)]
30
- pub struct TransactionAuthData {
31
- /// Verifier ID - must match the DVN's configured VID.
32
- pub vid: u32,
33
- /// Expiration timestamp (ledger time) after which this auth is invalid.
34
- pub expiration: u64,
35
- /// Signatures from multisig signers (secp256k1, 65 bytes each).
36
- pub signatures: Vec<BytesN<65>>,
37
- /// Entity submitting the transaction (admin, or permissionless).
38
- pub sender: Sender,
39
- }
40
-
41
10
  // ============================================================================
42
11
  // Custom Account Interface Implementation
43
12
  // ============================================================================
@@ -64,14 +33,14 @@ impl CustomAccountInterface for LzDVN {
64
33
  return Err(DvnError::AuthDataExpired);
65
34
  }
66
35
 
67
- // 2. Admin verification (quorum_change_admin bypasses this - allows quorum-only admin changes)
68
- let requires_admin = !Self::is_invoking_quorum_change_admin(&env, &auth_contexts);
36
+ // 2. Admin verification (`set_admin` bypasses this - allows quorum-only admin changes)
37
+ let requires_admin = !Self::is_invoking_worker_set_admin(&env, &auth_contexts);
69
38
  if requires_admin {
70
39
  Self::verify_admin_signature(&env, &sender, &signature_payload)?;
71
40
  }
72
41
 
73
42
  // 3. Replay protection
74
- let calls = Self::collect_contract_calls(&env, &auth_contexts)?;
43
+ let calls = Self::extract_contract_calls(&env, &auth_contexts)?;
75
44
  let hash = Self::hash_call_data(&env, vid, expiration, &calls);
76
45
  if DvnStorage::used_hash(&env, &hash) {
77
46
  return Err(DvnError::HashAlreadyUsed);
@@ -110,24 +79,25 @@ impl LzDVN {
110
79
  }
111
80
 
112
81
  /// Collects contract calls from the auth contexts.
113
- fn collect_contract_calls(env: &Env, auth_contexts: &Vec<Context>) -> Result<Vec<Call>, DvnError> {
114
- auth_contexts.iter().try_fold(vec![env], |mut calls, context| {
82
+ fn extract_contract_calls(env: &Env, auth_contexts: &Vec<Context>) -> Result<Vec<Call>, DvnError> {
83
+ let mut calls = vec![env];
84
+ for context in auth_contexts.iter() {
115
85
  let Context::Contract(ctx) = context else {
116
86
  return Err(DvnError::NonContractInvoke);
117
87
  };
118
88
  calls.push_back(Call { to: ctx.contract, func: ctx.fn_name, args: ctx.args });
119
- Ok(calls)
120
- })
89
+ }
90
+ Ok(calls)
121
91
  }
122
92
 
123
- /// Checks if the first auth context is a call to `quorum_change_admin` on this contract.
93
+ /// Checks if the first auth context is a call to `set_admin` on this contract.
124
94
  ///
125
95
  /// Used to allow quorum-only admin changes without requiring existing admin authorization.
126
- fn is_invoking_quorum_change_admin(env: &Env, auth_contexts: &Vec<Context>) -> bool {
96
+ fn is_invoking_worker_set_admin(env: &Env, auth_contexts: &Vec<Context>) -> bool {
127
97
  auth_contexts.len() == 1
128
98
  && auth_contexts.first().is_some_and(|ctx| {
129
99
  let Context::Contract(c) = ctx else { return false };
130
- c.contract == env.current_contract_address() && c.fn_name == Symbol::new(env, "quorum_change_admin")
100
+ c.contract == env.current_contract_address() && c.fn_name == Symbol::new(env, "set_admin")
131
101
  })
132
102
  }
133
103
  }
@@ -8,14 +8,13 @@
8
8
  use crate::{errors::DvnError, events::SetDstConfig, storage::DvnStorage, Call, DstConfig, DstConfigParam, IDVN};
9
9
  use common_macros::{contract_impl, lz_contract};
10
10
  use endpoint_v2::FeeRecipient;
11
+ use fee_lib_interfaces::{DvnFeeLibClient, DvnFeeParams};
11
12
  use message_lib_common::interfaces::ILayerZeroDVN;
12
13
  use soroban_sdk::{xdr::ToXdr, Address, Bytes, BytesN, Env, Vec};
13
- use utils::{
14
- buffer_writer::BufferWriter, multisig, option_ext::OptionExt, ttl_configurable, upgradeable::UpgradeableInternal,
15
- };
14
+ use utils::{buffer_writer::BufferWriter, multisig, option_ext::OptionExt, upgradeable::UpgradeableInternal};
16
15
  use worker::{
17
- assert_acl, assert_not_paused, assert_supported_message_lib, init_worker, require_admin_auth, set_admin_by_admin,
18
- set_admin_by_owner, DvnFeeLibClient, DvnFeeParams, Worker,
16
+ assert_acl, assert_not_paused, assert_supported_message_lib, errors::WorkerError, init_worker, require_admin_auth,
17
+ set_admin_by_admin, Worker,
19
18
  };
20
19
 
21
20
  /// LayerZero DVN contract.
@@ -55,7 +54,6 @@ impl LzDVN {
55
54
  worker_fee_lib: &Address,
56
55
  deposit_address: &Address,
57
56
  ) {
58
- ttl_configurable::init_default_ttl_configs(env);
59
57
  multisig::init_multisig(env, signers, threshold);
60
58
  init_worker::<Self>(
61
59
  env,
@@ -79,22 +77,9 @@ impl LzDVN {
79
77
  /// * `caller` - The admin calling this function (must provide authorization)
80
78
  /// * `admin` - The address to set admin status for
81
79
  /// * `active` - `true` to add admin, `false` to remove
82
- pub fn set_admin(env: &Env, caller: &Address, admin: &Address, active: bool) {
80
+ pub fn set_admin_by_admin(env: &Env, caller: &Address, admin: &Address, active: bool) {
83
81
  set_admin_by_admin::<Self>(env, caller, admin, active);
84
82
  }
85
-
86
- /// Allows the quorum to add/remove admins without requiring an admin signature.
87
- ///
88
- /// This function bypasses the normal admin verification in `__check_auth`,
89
- /// requiring only multisig quorum signatures. Must be called as a single
90
- /// operation (cannot be bundled with other calls).
91
- ///
92
- /// # Arguments
93
- /// * `admin` - The address to set admin status for
94
- /// * `active` - `true` to add admin, `false` to remove
95
- pub fn quorum_change_admin(env: &Env, admin: &Address, active: bool) {
96
- set_admin_by_owner::<Self>(env, admin, active);
97
- }
98
83
  }
99
84
 
100
85
  // ============================================================================
@@ -106,9 +91,7 @@ impl IDVN for LzDVN {
106
91
  /// Sets destination chain configurations. Requires admin authorization.
107
92
  fn set_dst_config(env: &Env, admin: &Address, params: &Vec<DstConfigParam>) {
108
93
  require_admin_auth::<Self>(env, admin);
109
- for param in params {
110
- DvnStorage::set_dst_config(env, param.dst_eid, &param.config);
111
- }
94
+ params.iter().for_each(|param| DvnStorage::set_dst_config(env, param.dst_eid, &param.config));
112
95
 
113
96
  SetDstConfig { params: params.clone() }.publish(env);
114
97
  }
@@ -120,6 +103,7 @@ impl IDVN for LzDVN {
120
103
 
121
104
  /// Returns the Verifier ID for this DVN.
122
105
  fn vid(env: &Env) -> u32 {
106
+ // VID is always set during initialization, so unwrap is safe here
123
107
  DvnStorage::vid(env).unwrap()
124
108
  }
125
109
 
@@ -156,12 +140,14 @@ impl ILayerZeroDVN for LzDVN {
156
140
  assert_acl::<Self>(env, sender);
157
141
 
158
142
  let dst_config = Self::dst_config(env, dst_eid).unwrap_or_panic(env, DvnError::EidNotSupported);
143
+ let price_feed = Self::price_feed(env).unwrap_or_panic(env, WorkerError::PriceFeedNotSet);
144
+ let worker_fee_lib = Self::worker_fee_lib(env).unwrap_or_panic(env, WorkerError::WorkerFeeLibNotSet);
159
145
  let params = DvnFeeParams {
160
146
  sender: sender.clone(),
161
147
  dst_eid,
162
148
  confirmations,
163
149
  options: options.clone(),
164
- price_feed: Self::price_feed(env),
150
+ price_feed,
165
151
  default_multiplier_bps: Self::default_multiplier_bps(env),
166
152
  quorum: Self::threshold(env),
167
153
  gas: dst_config.gas,
@@ -169,7 +155,7 @@ impl ILayerZeroDVN for LzDVN {
169
155
  floor_margin_usd: dst_config.floor_margin_usd,
170
156
  };
171
157
 
172
- DvnFeeLibClient::new(env, &Self::worker_fee_lib(env)).get_fee(&env.current_contract_address(), &params)
158
+ DvnFeeLibClient::new(env, &worker_fee_lib).get_fee(&env.current_contract_address(), &params)
173
159
  }
174
160
 
175
161
  /// Assigns a verification job to this DVN and returns fee payment info.
@@ -190,7 +176,8 @@ impl ILayerZeroDVN for LzDVN {
190
176
  assert_supported_message_lib::<Self>(env, send_lib);
191
177
 
192
178
  let fee = Self::get_fee(env, send_lib, sender, dst_eid, packet_header, payload_hash, confirmations, options);
193
- FeeRecipient { amount: fee, to: Self::deposit_address(env) }
179
+ let deposit_address = Self::deposit_address(env).unwrap_or_panic(env, WorkerError::DepositAddressNotSet);
180
+ FeeRecipient { amount: fee, to: deposit_address }
194
181
  }
195
182
  }
196
183
 
@@ -206,12 +193,10 @@ impl Worker for LzDVN {}
206
193
  // Upgradeable Implementation
207
194
  // ============================================================================
208
195
 
196
+ /// No migration logic needed for initial upgrade capability
209
197
  impl UpgradeableInternal for LzDVN {
210
198
  type MigrationData = ();
211
-
212
- fn __migrate(_env: &Env, _migration_data: &Self::MigrationData) {
213
- // No migration logic needed for initial upgrade capability
214
- }
199
+ fn __migrate(_env: &Env, _migration_data: &Self::MigrationData) {}
215
200
  }
216
201
 
217
202
  // ============================================================================
@@ -219,4 +204,4 @@ impl UpgradeableInternal for LzDVN {
219
204
  // ============================================================================
220
205
 
221
206
  #[path = "auth.rs"]
222
- pub mod auth;
207
+ mod auth;
@@ -5,7 +5,6 @@ pub enum DvnError {
5
5
  AuthDataExpired,
6
6
  EidNotSupported,
7
7
  HashAlreadyUsed,
8
- InvalidInvocation,
9
8
  InvalidVid,
10
9
  NonContractInvoke,
11
10
  OnlyAdmin,
@@ -3,6 +3,41 @@ use soroban_sdk::{contractclient, contracttype, Address, BytesN, Env, Symbol, Va
3
3
  use utils::multisig::Multisig;
4
4
  use worker::Worker;
5
5
 
6
+ // ============================================================================
7
+ // Authentication Data Types
8
+ // ============================================================================
9
+
10
+ #[contracttype]
11
+ #[derive(Clone, Debug, Eq, PartialEq)]
12
+ pub enum Sender {
13
+ /// No explicit sender (permissionless execution).
14
+ None,
15
+ /// A registered admin (ed25519) submitting the transaction.
16
+ /// The tuple is `(public_key, signature)` where the signature covers the Soroban payload.
17
+ Admin(BytesN<32>, BytesN<64>),
18
+ }
19
+
20
+ /// Authentication data for DVN contract transactions.
21
+ ///
22
+ /// This struct is used with Soroban's custom account interface to authorize
23
+ /// transactions through a combination of admin signature and multisig quorum.
24
+ #[contracttype]
25
+ #[derive(Clone, Debug, Eq, PartialEq)]
26
+ pub struct TransactionAuthData {
27
+ /// Verifier ID - must match the DVN's configured VID.
28
+ pub vid: u32,
29
+ /// Expiration timestamp (ledger time) after which this auth is invalid.
30
+ pub expiration: u64,
31
+ /// Signatures from multisig signers (secp256k1, 65 bytes each).
32
+ pub signatures: Vec<BytesN<65>>,
33
+ /// Entity submitting the transaction (admin, or permissionless).
34
+ pub sender: Sender,
35
+ }
36
+
37
+ // ============================================================================
38
+ // Destination Configuration Types
39
+ // ============================================================================
40
+
6
41
  /// Configuration for a destination chain.
7
42
  ///
8
43
  /// Contains fee calculation parameters specific to each destination endpoint.
@@ -1,9 +1,10 @@
1
1
  #![no_std]
2
2
 
3
- pub mod errors;
3
+ mod errors;
4
4
  pub mod events;
5
- pub mod interfaces;
5
+ mod interfaces;
6
6
 
7
+ pub use errors::*;
7
8
  pub use interfaces::*;
8
9
 
9
10
  cfg_if::cfg_if! {
@@ -11,7 +12,7 @@ cfg_if::cfg_if! {
11
12
  mod storage;
12
13
  mod dvn;
13
14
 
14
- pub use dvn::*;
15
+ pub use dvn::{LzDVN, LzDVNClient};
15
16
  }
16
17
  }
17
18
 
@@ -1,7 +1,7 @@
1
1
  use crate::{
2
- dvn::auth::{Sender, TransactionAuthData},
3
2
  errors::DvnError,
4
3
  tests::setup::{TestSetup, VID},
4
+ Sender, TransactionAuthData,
5
5
  };
6
6
  use ed25519_dalek::{Signer, SigningKey};
7
7
  use rand::thread_rng;
@@ -1,12 +1,13 @@
1
1
  use crate::{dvn::LzDVN, tests::setup::TestSetup, DVNClient, DstConfig, DstConfigParam, IDVN};
2
2
  use endpoint_v2::FeeRecipient;
3
+ use fee_lib_interfaces::{DvnFeeParams, IDvnFeeLib};
3
4
  use message_lib_common::interfaces::ILayerZeroDVN;
4
5
  use soroban_sdk::{
5
6
  contract, contractimpl,
6
7
  testutils::{Address as _, AuthorizedFunction},
7
8
  vec, Address, Bytes, BytesN, Env, IntoVal, Symbol,
8
9
  };
9
- use worker::{DvnFeeParams, IDvnFeeLib, Worker};
10
+ use worker::Worker;
10
11
 
11
12
  fn with_contract<F, R>(setup: &TestSetup, f: F) -> R
12
13
  where
@@ -73,7 +74,7 @@ fn test_get_fee_missing_dst_config_panics() {
73
74
  }
74
75
 
75
76
  #[test]
76
- #[should_panic(expected = "Error(Contract, #1209)")] // WorkerError::NotAllowed
77
+ #[should_panic(expected = "Error(Contract, #1207)")] // WorkerError::NotAllowed
77
78
  fn test_get_fee_not_allowed_due_to_allowlist() {
78
79
  let setup = TestSetup::new(1);
79
80
  let allowed = new_addr(&setup.env);
@@ -166,7 +167,7 @@ fn test_pause_and_setters_happy_paths() {
166
167
  LzDVN::set_deposit_address(&setup.env, &admin, &other);
167
168
  });
168
169
  with_contract(&setup, || {
169
- assert_eq!(LzDVN::deposit_address(&setup.env), other);
170
+ assert_eq!(LzDVN::deposit_address(&setup.env), Some(other));
170
171
  });
171
172
 
172
173
  let pf = new_addr(&setup.env);
@@ -174,12 +175,12 @@ fn test_pause_and_setters_happy_paths() {
174
175
  LzDVN::set_price_feed(&setup.env, &admin, &pf);
175
176
  });
176
177
  with_contract(&setup, || {
177
- assert_eq!(LzDVN::price_feed(&setup.env), pf);
178
+ assert_eq!(LzDVN::price_feed(&setup.env), Some(pf));
178
179
  });
179
180
 
180
181
  let opts = Bytes::from_array(&setup.env, &[1, 2, 3]);
181
182
  with_contract(&setup, || {
182
- LzDVN::set_supported_option_types(&setup.env, &admin, 77, opts.clone());
183
+ LzDVN::set_supported_option_types(&setup.env, &admin, 77, &opts);
183
184
  });
184
185
  with_contract(&setup, || {
185
186
  assert_eq!(LzDVN::get_supported_option_types(&setup.env, 77), Some(opts));
@@ -190,7 +191,7 @@ fn test_pause_and_setters_happy_paths() {
190
191
  LzDVN::set_worker_fee_lib(&setup.env, &admin, &fee_lib);
191
192
  });
192
193
  with_contract(&setup, || {
193
- assert_eq!(LzDVN::worker_fee_lib(&setup.env), fee_lib);
194
+ assert_eq!(LzDVN::worker_fee_lib(&setup.env), Some(fee_lib));
194
195
  });
195
196
  }
196
197
 
@@ -278,7 +279,7 @@ fn test_set_admin_add() {
278
279
 
279
280
  // Add new admin by existing admin
280
281
  with_contract(&setup, || {
281
- LzDVN::set_admin(&setup.env, &existing_admin, &new_admin, true);
282
+ LzDVN::set_admin_by_admin(&setup.env, &existing_admin, &new_admin, true);
282
283
  });
283
284
 
284
285
  // Verify new_admin is now an admin
@@ -294,12 +295,12 @@ fn test_set_admin_remove() {
294
295
 
295
296
  // Add new admin first
296
297
  with_contract(&setup, || {
297
- LzDVN::set_admin(&setup.env, &existing_admin, &new_admin, true);
298
+ LzDVN::set_admin_by_admin(&setup.env, &existing_admin, &new_admin, true);
298
299
  });
299
300
 
300
301
  // Remove the new admin
301
302
  with_contract(&setup, || {
302
- LzDVN::set_admin(&setup.env, &existing_admin, &new_admin, false);
303
+ LzDVN::set_admin_by_admin(&setup.env, &existing_admin, &new_admin, false);
303
304
  });
304
305
 
305
306
  // Verify new_admin is no longer an admin
@@ -308,7 +309,7 @@ fn test_set_admin_remove() {
308
309
  }
309
310
 
310
311
  #[test]
311
- #[should_panic(expected = "Error(Contract, #1215)")] // WorkerError::Unauthorized
312
+ #[should_panic(expected = "Error(Contract, #1212)")] // WorkerError::Unauthorized
312
313
  fn test_set_admin_unauthorized() {
313
314
  let setup = TestSetup::new(1);
314
315
  let non_admin = new_addr(&setup.env);
@@ -316,20 +317,23 @@ fn test_set_admin_unauthorized() {
316
317
 
317
318
  // Attempt to add admin by non-admin should fail
318
319
  with_contract(&setup, || {
319
- LzDVN::set_admin(&setup.env, &non_admin, &new_admin, true);
320
+ LzDVN::set_admin_by_admin(&setup.env, &non_admin, &new_admin, true);
320
321
  });
321
322
  }
322
323
 
323
324
  #[test]
324
- #[should_panic(expected = "Error(Contract, #1204)")] // WorkerError::AttemptingToRemoveOnlyAdmin
325
- fn test_set_admin_cannot_remove_last_admin() {
325
+ fn test_set_admin_remove_last_admin() {
326
326
  let setup = TestSetup::new(1);
327
327
  let existing_admin = setup.admins.get(0).unwrap();
328
328
 
329
- // Attempt to remove the only admin should fail
329
+ // Removing the only admin is now allowed
330
330
  with_contract(&setup, || {
331
- LzDVN::set_admin(&setup.env, &existing_admin, &existing_admin, false);
331
+ LzDVN::set_admin_by_admin(&setup.env, &existing_admin, &existing_admin, false);
332
332
  });
333
+
334
+ // Verify admin is no longer an admin
335
+ let is_admin = with_contract(&setup, || LzDVN::is_admin(&setup.env, &existing_admin));
336
+ assert!(!is_admin);
333
337
  }
334
338
 
335
339
  #[contract]
@@ -13,10 +13,8 @@ fn random_signer(env: &Env) -> BytesN<20> {
13
13
  }
14
14
 
15
15
  fn register_dvn(env: &Env, signer_count: usize, threshold: u32) {
16
- let signers: Vec<BytesN<20>> = (0..signer_count).map(|_| random_signer(env)).fold(vec![env], |mut acc, s| {
17
- acc.push_back(s);
18
- acc
19
- });
16
+ let mut signers: Vec<BytesN<20>> = vec![env];
17
+ (0..signer_count).map(|_| random_signer(env)).for_each(|s| signers.push_back(s));
20
18
  let admins: Vec<Address> = vec![env, Address::generate(env)];
21
19
  let supported_msglibs: Vec<Address> = vec![env, Address::generate(env)];
22
20
  let price_feed: Address = Address::generate(env);
@@ -17,9 +17,7 @@ fn sorted_signatures(setup: &TestSetup, digest: &BytesN<32>) -> Vec<BytesN<65>>
17
17
  setup.key_pairs.iter().map(|kp| (kp.eth_address, kp.sign_bytes(&setup.env, digest))).collect();
18
18
  pairs.sort_by(|a, b| a.0.cmp(&b.0));
19
19
  let mut result: Vec<BytesN<65>> = vec![&setup.env];
20
- for (_, sig) in pairs {
21
- result.push_back(sig);
22
- }
20
+ pairs.into_iter().for_each(|(_, sig)| result.push_back(sig));
23
21
  result
24
22
  }
25
23
 
@@ -4,8 +4,8 @@ extern crate std;
4
4
 
5
5
  use crate::{dvn::LzDVN, tests::key_pair::KeyPair};
6
6
  use soroban_sdk::{address_payload::AddressPayload, testutils::Address as _, vec, Address, BytesN, Env, Vec};
7
- use utils::multisig::MultisigClient;
8
7
  use std::vec::Vec as StdVec;
8
+ use utils::multisig::MultisigClient;
9
9
 
10
10
  pub const VID: u32 = 1;
11
11
  pub const DEFAULT_MULTIPLIER_BPS: u32 = 10000;
@@ -33,9 +33,7 @@ impl<'a> TestSetup<'a> {
33
33
  let key_pairs: StdVec<KeyPair> = (0..signer_count).map(|_| KeyPair::generate()).collect();
34
34
 
35
35
  let mut signers: Vec<BytesN<20>> = vec![&env];
36
- for kp in &key_pairs {
37
- signers.push_back(kp.signer(&env));
38
- }
36
+ key_pairs.iter().for_each(|kp| signers.push_back(kp.signer(&env)));
39
37
 
40
38
  let admins: Vec<Address> = vec![&env, Address::generate(&env)];
41
39
  let supported_msglibs: Vec<Address> = vec![&env, Address::generate(&env)];
@@ -71,16 +69,14 @@ impl<'a> TestSetup<'a> {
71
69
  let key_pairs: StdVec<KeyPair> = (0..signer_count).map(|_| KeyPair::generate()).collect();
72
70
 
73
71
  let mut signers: Vec<BytesN<20>> = vec![&env];
74
- for kp in &key_pairs {
75
- signers.push_back(kp.signer(&env));
76
- }
72
+ key_pairs.iter().for_each(|kp| signers.push_back(kp.signer(&env)));
77
73
 
78
74
  let mut admins: Vec<Address> = vec![&env, Address::generate(&env)];
79
- for bytes in admin_bytes {
75
+ admin_bytes.into_iter().for_each(|bytes| {
80
76
  let bytes_n = BytesN::from_array(&env, &bytes);
81
77
  let addr = Address::from_payload(&env, AddressPayload::AccountIdPublicKeyEd25519(bytes_n));
82
78
  admins.push_back(addr);
83
- }
79
+ });
84
80
 
85
81
  let supported_msglibs: Vec<Address> = vec![&env, Address::generate(&env)];
86
82
  let price_feed: Address = Address::generate(&env);
@@ -18,7 +18,7 @@ soroban-sdk = { workspace = true }
18
18
  common-macros = { workspace = true }
19
19
  message-lib-common = { workspace = true }
20
20
  utils = { workspace = true }
21
- worker = { workspace = true }
21
+ fee-lib-interfaces = { workspace = true }
22
22
 
23
23
  [dev-dependencies]
24
24
  soroban-sdk = { workspace = true, features = ["testutils"] }
@@ -1,8 +1,8 @@
1
1
  use crate::errors::DvnFeeLibError;
2
2
  use common_macros::{contract_impl, lz_contract};
3
+ use fee_lib_interfaces::{DvnFeeParams, IDvnFeeLib, LayerZeroPriceFeedClient};
3
4
  use soroban_sdk::{assert_with_error, Address, Env};
4
5
  use utils::upgradeable::UpgradeableInternal;
5
- use worker::{DvnFeeParams, IDvnFeeLib, LayerZeroPriceFeedClient};
6
6
 
7
7
  // ============================================================================
8
8
  // Constants
@@ -70,12 +70,10 @@ impl IDvnFeeLib for DvnFeeLib {
70
70
  // Upgradeable Implementation
71
71
  // ============================================================================
72
72
 
73
+ /// No migration logic needed for initial upgrade capability
73
74
  impl UpgradeableInternal for DvnFeeLib {
74
75
  type MigrationData = ();
75
-
76
- fn __migrate(_env: &Env, _migration_data: &Self::MigrationData) {
77
- // No migration logic needed for initial upgrade capability
78
- }
76
+ fn __migrate(_env: &Env, _migration_data: &Self::MigrationData) {}
79
77
  }
80
78
 
81
79
  // ============================================================================