@layerzerolabs/protocol-stellar-v2 0.2.10 → 0.2.12

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 (129) hide show
  1. package/.turbo/turbo-build.log +273 -219
  2. package/.turbo/turbo-lint.log +79 -107
  3. package/.turbo/turbo-test.log +1016 -840
  4. package/Cargo.lock +14 -6
  5. package/contracts/common-macros/src/contract_impl.rs +6 -3
  6. package/contracts/common-macros/src/error.rs +9 -17
  7. package/contracts/common-macros/src/lib.rs +4 -37
  8. package/contracts/common-macros/src/ownable.rs +9 -5
  9. package/contracts/common-macros/src/tests/contract_impl.rs +178 -86
  10. package/contracts/common-macros/src/tests/error.rs +168 -0
  11. package/contracts/common-macros/src/tests/mod.rs +2 -4
  12. package/contracts/common-macros/src/tests/ownable.rs +37 -60
  13. package/contracts/common-macros/src/tests/snapshots/common_macros__tests__contract_impl__snapshot_generated_contract_impl_code.snap +16 -6
  14. package/contracts/common-macros/src/tests/snapshots/common_macros__tests__error__snapshot_generated_contract_error_code.snap +20 -0
  15. package/contracts/common-macros/src/tests/snapshots/common_macros__tests__ownable__snapshot_generated_ownable_code.snap +3 -1
  16. package/contracts/common-macros/src/tests/snapshots/common_macros__tests__ownable__snapshot_only_owner_preserves_function_signature.snap +12 -2
  17. package/contracts/common-macros/src/tests/snapshots/common_macros__tests__ttl_configurable__snapshot_generated_ttl_configurable_code.snap +5 -1
  18. package/contracts/common-macros/src/tests/utils.rs +267 -0
  19. package/contracts/common-macros/src/ttl_configurable.rs +15 -12
  20. package/contracts/common-macros/src/utils.rs +35 -6
  21. package/contracts/endpoint-v2/src/endpoint_v2.rs +4 -4
  22. package/contracts/endpoint-v2/src/events.rs +40 -22
  23. package/contracts/endpoint-v2/src/interfaces/message_lib.rs +2 -2
  24. package/contracts/endpoint-v2/src/interfaces/message_lib_manager.rs +2 -2
  25. package/contracts/endpoint-v2/src/interfaces/messaging_channel.rs +2 -2
  26. package/contracts/endpoint-v2/src/interfaces/messaging_composer.rs +2 -2
  27. package/contracts/endpoint-v2/src/interfaces/send_lib.rs +2 -2
  28. package/contracts/endpoint-v2/src/message_lib_manager.rs +3 -3
  29. package/contracts/endpoint-v2/src/messaging_channel.rs +1 -1
  30. package/contracts/endpoint-v2/src/messaging_composer.rs +1 -1
  31. package/contracts/endpoint-v2/src/tests/message_lib_manager/set_default_receive_lib_timeout.rs +4 -8
  32. package/contracts/endpoint-v2/src/tests/message_lib_manager/set_default_receive_library.rs +3 -7
  33. package/contracts/message-libs/{block-message-lib → blocked-message-lib}/Cargo.toml +1 -1
  34. package/contracts/message-libs/treasury/src/events.rs +9 -6
  35. package/contracts/message-libs/uln-302/src/events.rs +19 -11
  36. package/contracts/message-libs/uln-302/src/interfaces/receive_uln.rs +2 -2
  37. package/contracts/message-libs/uln-302/src/interfaces/send_uln.rs +2 -2
  38. package/contracts/message-libs/uln-302/src/receive_uln.rs +2 -2
  39. package/contracts/message-libs/uln-302/src/send_uln.rs +3 -3
  40. package/contracts/message-libs/uln-302/src/tests/receive_uln302/set_default_receive_uln_configs.rs +5 -5
  41. package/contracts/message-libs/uln-302/src/tests/send_uln302/set_default_send_uln_configs.rs +5 -5
  42. package/contracts/message-libs/uln-302/src/tests/setup.rs +3 -3
  43. package/contracts/message-libs/uln-302/src/types.rs +24 -24
  44. package/contracts/message-libs/uln-302/src/uln302.rs +2 -2
  45. package/contracts/oapp-macros/src/oapp_core.rs +1 -1
  46. package/contracts/oapps/counter/integration_tests/utils.rs +1 -1
  47. package/contracts/oapps/oapp/src/oapp_core.rs +4 -3
  48. package/contracts/oapps/oapp/src/oapp_options_type3.rs +4 -3
  49. package/contracts/oapps/oft/integration-tests/setup.rs +4 -3
  50. package/contracts/oapps/oft/integration-tests/utils.rs +1 -1
  51. package/contracts/oapps/oft/src/default_oft_impl.rs +146 -0
  52. package/contracts/oapps/oft/src/events.rs +5 -4
  53. package/contracts/oapps/oft/src/extensions/mod.rs +3 -0
  54. package/contracts/oapps/oft/src/extensions/oft_fee.rs +168 -0
  55. package/contracts/oapps/oft/src/extensions/pausable.rs +50 -0
  56. package/contracts/oapps/oft/src/extensions/rate_limiter.rs +200 -0
  57. package/contracts/oapps/oft/src/lib.rs +2 -3
  58. package/contracts/oapps/oft/src/oft.rs +16 -85
  59. package/contracts/oapps/oft/src/oft_types/mint_burn.rs +1 -1
  60. package/contracts/oapps/oft/src/tests/extensions/mod.rs +11 -0
  61. package/contracts/oapps/oft/src/tests/extensions/setup.rs +888 -0
  62. package/contracts/oapps/oft/src/tests/extensions/test_oft_fee.rs +749 -0
  63. package/contracts/oapps/oft/src/tests/extensions/test_pausable.rs +432 -0
  64. package/contracts/oapps/oft/src/tests/extensions/test_rate_limiter.rs +1078 -0
  65. package/contracts/oapps/oft/src/tests/mod.rs +2 -0
  66. package/contracts/oapps/oft/src/tests/test_utils.rs +24 -6
  67. package/contracts/oapps/{oft-mint-burn → oft-std}/Cargo.toml +1 -8
  68. package/contracts/oapps/oft-std/src/lib.rs +5 -0
  69. package/contracts/oapps/oft-std/src/oft.rs +59 -0
  70. package/contracts/utils/src/ownable.rs +8 -6
  71. package/contracts/utils/src/tests/ownable.rs +0 -63
  72. package/contracts/utils/src/tests/testing_utils.rs +7 -5
  73. package/contracts/utils/src/ttl.rs +21 -2
  74. package/contracts/workers/dvn/src/auth.rs +108 -30
  75. package/contracts/workers/dvn/src/dvn.rs +103 -33
  76. package/contracts/workers/dvn/src/errors.rs +10 -13
  77. package/contracts/workers/dvn/src/events.rs +7 -5
  78. package/contracts/workers/dvn/src/interfaces/dvn.rs +76 -3
  79. package/contracts/workers/dvn/src/interfaces/multisig.rs +41 -0
  80. package/contracts/workers/dvn/src/lib.rs +6 -8
  81. package/contracts/workers/dvn/src/multisig.rs +98 -72
  82. package/contracts/workers/dvn/src/storage.rs +9 -12
  83. package/contracts/workers/dvn/src/tests/auth.rs +56 -26
  84. package/contracts/workers/dvn/src/tests/dvn.rs +40 -41
  85. package/contracts/workers/dvn/src/tests/multisig/set_signer.rs +8 -8
  86. package/contracts/workers/dvn/src/tests/multisig/set_threshold.rs +9 -9
  87. package/contracts/workers/dvn/src/tests/multisig/verify_signatures.rs +6 -6
  88. package/contracts/workers/dvn/src/tests/setup.rs +5 -5
  89. package/contracts/workers/dvn-fee-lib/Cargo.toml +2 -1
  90. package/contracts/workers/dvn-fee-lib/src/dvn_fee_lib.rs +4 -3
  91. package/contracts/workers/dvn-fee-lib/src/tests/dvn_fee_lib.rs +8 -6
  92. package/contracts/workers/executor/src/auth.rs +93 -0
  93. package/contracts/workers/executor/src/events.rs +5 -4
  94. package/contracts/workers/executor/src/{lz_executor.rs → executor.rs} +30 -103
  95. package/contracts/workers/executor/src/interfaces/executor.rs +5 -2
  96. package/contracts/workers/executor/src/interfaces/mod.rs +1 -1
  97. package/contracts/workers/executor/src/lib.rs +6 -5
  98. package/contracts/workers/price-feed/Cargo.toml +21 -0
  99. package/contracts/workers/price-feed/src/errors.rs +9 -0
  100. package/contracts/workers/price-feed/src/events.rs +30 -0
  101. package/contracts/workers/price-feed/src/lib.rs +11 -0
  102. package/contracts/workers/price-feed/src/price_feed.rs +265 -0
  103. package/contracts/workers/price-feed/src/storage.rs +42 -0
  104. package/contracts/workers/price-feed/src/types.rs +59 -0
  105. package/contracts/workers/worker/src/events.rs +23 -13
  106. package/contracts/workers/worker/src/interfaces/dvn_fee_lib.rs +2 -1
  107. package/contracts/workers/worker/src/worker.rs +32 -21
  108. package/package.json +3 -3
  109. package/sdk/dist/generated/bml.js +24 -22
  110. package/sdk/dist/generated/counter.d.ts +102 -0
  111. package/sdk/dist/generated/counter.js +36 -24
  112. package/sdk/dist/generated/endpoint.js +24 -22
  113. package/sdk/dist/generated/sml.js +24 -22
  114. package/sdk/dist/generated/uln302.d.ts +1 -1
  115. package/sdk/dist/generated/uln302.js +34 -32
  116. package/sdk/package.json +1 -1
  117. package/sdk/test/index.test.ts +1 -1
  118. package/sdk/test/oft.test.ts +847 -0
  119. package/sdk/test/suites/scan.ts +20 -4
  120. package/tools/ts-bindings-gen/src/main.rs +2 -1
  121. package/contracts/common-macros/src/event.rs +0 -16
  122. package/contracts/oapps/oft/src/macro_tests/mod.rs +0 -2
  123. package/contracts/oapps/oft/src/macro_tests/test_all_default.rs +0 -41
  124. package/contracts/oapps/oft/src/macro_tests/test_override.rs +0 -83
  125. package/contracts/oapps/oft-mint-burn/src/lib.rs +0 -3
  126. package/contracts/oapps/oft-mint-burn/src/oft.rs +0 -28
  127. package/contracts/oapps/oft-mint-burn/src/tests/mod.rs +0 -1
  128. package/contracts/workers/dvn/src/types.rs +0 -26
  129. /package/contracts/message-libs/{block-message-lib → blocked-message-lib}/src/lib.rs +0 -0
@@ -11,3 +11,5 @@ pub mod test_quote_send;
11
11
  pub mod test_resolve_address;
12
12
  pub mod test_send;
13
13
  pub mod test_token;
14
+
15
+ pub mod extensions;
@@ -110,8 +110,15 @@ pub struct TestMintBurnOFT;
110
110
 
111
111
  #[contractimpl]
112
112
  impl TestMintBurnOFT {
113
- pub fn __constructor(env: &Env, token: &Address, owner: &Address, endpoint: &Address, delegate: &Option<Address>) {
114
- oft_initialize::<Self>(env, owner, token, endpoint, delegate)
113
+ pub fn __constructor(
114
+ env: &Env,
115
+ token: &Address,
116
+ owner: &Address,
117
+ endpoint: &Address,
118
+ delegate: &Option<Address>,
119
+ shared_decimals: u32,
120
+ ) {
121
+ oft_initialize::<Self>(env, owner, token, endpoint, delegate, shared_decimals)
115
122
  }
116
123
  }
117
124
 
@@ -134,8 +141,15 @@ pub struct TestLockUnlockOFT;
134
141
 
135
142
  #[contractimpl]
136
143
  impl TestLockUnlockOFT {
137
- pub fn __constructor(env: &Env, token: &Address, owner: &Address, endpoint: &Address, delegate: &Option<Address>) {
138
- oft_initialize::<Self>(env, owner, token, endpoint, delegate)
144
+ pub fn __constructor(
145
+ env: &Env,
146
+ token: &Address,
147
+ owner: &Address,
148
+ endpoint: &Address,
149
+ delegate: &Option<Address>,
150
+ shared_decimals: u32,
151
+ ) {
152
+ oft_initialize::<Self>(env, owner, token, endpoint, delegate, shared_decimals)
139
153
  }
140
154
  }
141
155
 
@@ -477,8 +491,12 @@ impl<'a> OFTTestSetupBuilder<'a> {
477
491
  // Register OFT based on type
478
492
  let delegate: Option<Address> = Some(owner.clone());
479
493
  let oft_address = match oft_type {
480
- OFTType::MintBurn => env.register(TestMintBurnOFT, (&token, &owner, &endpoint_address, &delegate)),
481
- OFTType::LockUnlock => env.register(TestLockUnlockOFT, (&token, &owner, &endpoint_address, &delegate)),
494
+ OFTType::MintBurn => {
495
+ env.register(TestMintBurnOFT, (&token, &owner, &endpoint_address, &delegate, &self.shared_decimals))
496
+ }
497
+ OFTType::LockUnlock => {
498
+ env.register(TestLockUnlockOFT, (&token, &owner, &endpoint_address, &delegate, &self.shared_decimals))
499
+ }
482
500
  };
483
501
  let oft = OFTClient::new(env, &oft_address);
484
502
 
@@ -1,5 +1,5 @@
1
1
  [package]
2
- name = "oft-mint-burn"
2
+ name = "oft-std"
3
3
  version.workspace = true
4
4
  edition.workspace = true
5
5
  license.workspace = true
@@ -17,10 +17,3 @@ common-macros = { workspace = true }
17
17
  oapp-macros = { workspace = true }
18
18
  oft = { workspace = true }
19
19
 
20
- [dev-dependencies]
21
- soroban-sdk = { workspace = true, features = ["testutils"] }
22
- assert_unordered = "0.3.5"
23
- simple-message-lib = { workspace = true }
24
- message-lib-common = { workspace = true, features = ["testutils"] }
25
- endpoint-v2 = { workspace = true, features = ["testutils"] }
26
- executor = { workspace = true, features = ["testutils"] }
@@ -0,0 +1,5 @@
1
+ #![no_std]
2
+
3
+ mod oft;
4
+
5
+ pub use oft::*;
@@ -0,0 +1,59 @@
1
+ use common_macros::{contract_impl, storage};
2
+ use oapp_macros::oapp;
3
+ use oft::oft::{oft_initialize, OFTInner, OFT};
4
+ use oft::oft_types::{lock_unlock, mint_burn};
5
+ use oft::types::OFTReceipt;
6
+ use soroban_sdk::{Address, Env};
7
+
8
+ #[storage]
9
+ enum OFTStdStorage {
10
+ #[instance(bool)]
11
+ IsLockUnlock,
12
+ }
13
+
14
+ #[oapp]
15
+ pub struct OFTStd;
16
+
17
+ #[contract_impl]
18
+ impl OFTStd {
19
+ pub fn __constructor(
20
+ env: &Env,
21
+ token: &Address,
22
+ owner: &Address,
23
+ endpoint: &Address,
24
+ delegate: &Option<Address>,
25
+ shared_decimals: u32,
26
+ is_lock_unlock: bool,
27
+ ) {
28
+ oft_initialize::<Self>(env, owner, token, endpoint, delegate, shared_decimals);
29
+ OFTStdStorage::set_is_lock_unlock(env, &is_lock_unlock);
30
+ }
31
+
32
+ /// Whether this OFT uses lock/unlock mode (true) or mint/burn mode (false)
33
+ pub fn is_lock_unlock(env: &Env) -> bool {
34
+ OFTStdStorage::is_lock_unlock(env).unwrap()
35
+ }
36
+ }
37
+
38
+ /// OFT trait implementation for standard OFT
39
+ #[contract_impl(contracttrait)]
40
+ impl OFT for OFTStd {}
41
+
42
+ /// OFT behavior for standard OFT
43
+ impl OFTInner for OFTStd {
44
+ fn __debit(env: &Env, sender: &Address, amount_ld: i128, min_amount_ld: i128, dst_eid: u32) -> OFTReceipt {
45
+ if Self::is_lock_unlock(env) {
46
+ lock_unlock::debit::<Self>(env, sender, amount_ld, min_amount_ld, dst_eid)
47
+ } else {
48
+ mint_burn::debit::<Self>(env, sender, amount_ld, min_amount_ld, dst_eid)
49
+ }
50
+ }
51
+
52
+ fn __credit(env: &Env, to: &Address, amount_ld: i128, src_eid: u32) -> i128 {
53
+ if Self::is_lock_unlock(env) {
54
+ lock_unlock::credit::<Self>(env, to, amount_ld, src_eid)
55
+ } else {
56
+ mint_burn::credit::<Self>(env, to, amount_ld, src_eid)
57
+ }
58
+ }
59
+ }
@@ -1,7 +1,7 @@
1
1
  use crate::errors::OwnableError;
2
2
  use crate::option_ext::OptionExt;
3
- use common_macros::{event, storage};
4
- use soroban_sdk::{assert_with_error, contractclient, Address, Env};
3
+ use common_macros::storage;
4
+ use soroban_sdk::{assert_with_error, contractclient, contractevent, Address, Env};
5
5
 
6
6
  // ============================================
7
7
  // Ownable Initializer Interface
@@ -44,14 +44,16 @@ pub fn require_owner_auth<T: Ownable>(env: &Env) {
44
44
  }
45
45
 
46
46
  /// Event emitted when ownership is transferred.
47
- #[event]
47
+ #[contractevent]
48
+ #[derive(Clone, Debug, Eq, PartialEq)]
48
49
  pub struct OwnershipTransferred {
49
50
  pub old_owner: Address,
50
51
  pub new_owner: Address,
51
52
  }
52
53
 
53
54
  /// Event emitted when ownership is renounced.
54
- #[event]
55
+ #[contractevent]
56
+ #[derive(Clone, Debug, Eq, PartialEq)]
55
57
  pub struct OwnershipRenounced {
56
58
  pub old_owner: Address,
57
59
  }
@@ -76,13 +78,13 @@ impl Ownable for DefaultOwnable {
76
78
  }
77
79
 
78
80
  fn transfer_ownership(env: &Env, new_owner: &Address) {
79
- let old_owner = enforce_owner_auth::<Self>(env);
81
+ let old_owner = Self::owner(env).unwrap_or_panic(env, OwnableError::OwnerNotSet);
80
82
  DefaultOwnableStorage::set_owner(env, new_owner);
81
83
  OwnershipTransferred { old_owner, new_owner: new_owner.clone() }.publish(env);
82
84
  }
83
85
 
84
86
  fn renounce_ownership(env: &Env) {
85
- let old_owner = enforce_owner_auth::<Self>(env);
87
+ let old_owner = Self::owner(env).unwrap_or_panic(env, OwnableError::OwnerNotSet);
86
88
  DefaultOwnableStorage::remove_owner(env);
87
89
  OwnershipRenounced { old_owner }.publish(env);
88
90
  }
@@ -186,20 +186,6 @@ fn transfer_ownership_to_same_owner() {
186
186
  assert_eq!(client.owner(), Some(owner));
187
187
  }
188
188
 
189
- #[test]
190
- #[should_panic(expected = "Error(Auth, InvalidAction)")]
191
- fn transfer_ownership_requires_auth() {
192
- let env = Env::default();
193
- let owner = Address::generate(&env);
194
- let new_owner = Address::generate(&env);
195
-
196
- let contract = env.register(Contract, (&owner,));
197
- let client = ContractClient::new(&env, &contract);
198
-
199
- // Try to transfer without auth should fail
200
- client.transfer_ownership(&new_owner);
201
- }
202
-
203
189
  #[test]
204
190
  #[should_panic(expected = "Error(Contract, #1301)")]
205
191
  fn transfer_after_renounce_fails() {
@@ -264,19 +250,6 @@ fn renounce_ownership_with_event() {
264
250
  assert_eq!(client.owner(), None);
265
251
  }
266
252
 
267
- #[test]
268
- #[should_panic(expected = "Error(Auth, InvalidAction)")]
269
- fn renounce_ownership_requires_auth() {
270
- let env = Env::default();
271
- let owner = Address::generate(&env);
272
-
273
- let contract = env.register(Contract, (&owner,));
274
- let client = ContractClient::new(&env, &contract);
275
-
276
- // Try to renounce without auth should fail
277
- client.renounce_ownership();
278
- }
279
-
280
253
  #[test]
281
254
  #[should_panic(expected = "Error(Contract, #1301)")]
282
255
  fn renounce_after_renounce_fails() {
@@ -352,42 +325,6 @@ fn chain_new_owner_can_transfer() {
352
325
  assert_eq!(client.owner(), Some(third_owner));
353
326
  }
354
327
 
355
- #[test]
356
- #[should_panic(expected = "Error(Auth, InvalidAction)")]
357
- fn chain_old_owner_cannot_transfer_after_transfer() {
358
- let env = Env::default();
359
- let owner = Address::generate(&env);
360
- let new_owner = Address::generate(&env);
361
- let third_owner = Address::generate(&env);
362
-
363
- let contract = env.register(Contract, (&owner,));
364
- let client = ContractClient::new(&env, &contract);
365
-
366
- // Transfer to new_owner
367
- env.mock_auths(&[MockAuth {
368
- address: &owner,
369
- invoke: &MockAuthInvoke {
370
- contract: &contract,
371
- args: (&new_owner,).into_val(&env),
372
- fn_name: "transfer_ownership",
373
- sub_invokes: &[],
374
- },
375
- }]);
376
- client.transfer_ownership(&new_owner);
377
-
378
- // Old owner tries to transfer again - should fail
379
- env.mock_auths(&[MockAuth {
380
- address: &owner,
381
- invoke: &MockAuthInvoke {
382
- contract: &contract,
383
- args: (&third_owner,).into_val(&env),
384
- fn_name: "transfer_ownership",
385
- sub_invokes: &[],
386
- },
387
- }]);
388
- client.transfer_ownership(&third_owner);
389
- }
390
-
391
328
  // ============================================
392
329
  // init: DefaultOwnable::init_owner tests
393
330
  // ============================================
@@ -1,23 +1,25 @@
1
1
  use crate::testing_utils::{assert_event, assert_events, ExpectedEvent, IntoExpectedEvent};
2
- use common_macros::event;
3
- use soroban_sdk::{contract, contractimpl, testutils::Address as _, Address, Env, IntoVal, Val, Vec};
2
+ use soroban_sdk::{contract, contractevent, contractimpl, testutils::Address as _, Address, Env, IntoVal, Val, Vec};
4
3
 
5
4
  // ============================================
6
5
  // Test Fixtures
7
6
  // ============================================
8
7
 
9
- #[event]
8
+ #[contractevent]
9
+ #[derive(Clone, Debug, Eq, PartialEq)]
10
10
  pub struct TestEvent1 {
11
11
  pub value: u32,
12
12
  }
13
13
 
14
- #[event]
14
+ #[contractevent]
15
+ #[derive(Clone, Debug, Eq, PartialEq)]
15
16
  pub struct TestEvent2 {
16
17
  pub name: u32,
17
18
  pub count: u64,
18
19
  }
19
20
 
20
- #[event]
21
+ #[contractevent]
22
+ #[derive(Clone, Debug, Eq, PartialEq)]
21
23
  pub struct TestEvent3 {
22
24
  pub address: Address,
23
25
  }
@@ -1,5 +1,5 @@
1
1
  use crate::errors::TtlError;
2
- use soroban_sdk::{assert_with_error, contractclient, contracttype, Env};
2
+ use soroban_sdk::{assert_with_error, contractclient, contractevent, contracttype, Env};
3
3
 
4
4
  /// Number of ledgers per day (~5 second ledger close time).
5
5
  pub const LEDGERS_PER_DAY: u32 = (24 * 3600) / 5;
@@ -85,6 +85,8 @@ impl TtlConfigurable for DefaultTtlConfigurable {
85
85
 
86
86
  TtlConfigStorage::set_or_remove_instance(env, instance);
87
87
  TtlConfigStorage::set_or_remove_persistent(env, persistent);
88
+
89
+ TtlConfigsSet { instance: *instance, persistent: *persistent }.publish(env);
88
90
  }
89
91
 
90
92
  fn ttl_configs(env: &Env) -> (Option<TtlConfig>, Option<TtlConfig>) {
@@ -94,6 +96,8 @@ impl TtlConfigurable for DefaultTtlConfigurable {
94
96
  fn freeze_ttl_configs(env: &Env) {
95
97
  assert_with_error!(env, !Self::is_ttl_configs_frozen(env), TtlError::TtlConfigAlreadyFrozen);
96
98
  TtlConfigStorage::set_frozen(env, &true);
99
+
100
+ TtlConfigsFrozen {}.publish(env);
97
101
  }
98
102
 
99
103
  fn is_ttl_configs_frozen(env: &Env) -> bool {
@@ -101,4 +105,19 @@ impl TtlConfigurable for DefaultTtlConfigurable {
101
105
  }
102
106
  }
103
107
 
104
- // TODO: add events
108
+ // ============================================
109
+ // Events
110
+ // ============================================
111
+
112
+ /// Event emitted when TTL configs are set.
113
+ #[contractevent]
114
+ #[derive(Clone, Debug, Eq, PartialEq)]
115
+ pub struct TtlConfigsSet {
116
+ pub instance: Option<TtlConfig>,
117
+ pub persistent: Option<TtlConfig>,
118
+ }
119
+
120
+ /// Event emitted when TTL configs are frozen.
121
+ #[contractevent]
122
+ #[derive(Clone, Debug, Eq, PartialEq)]
123
+ pub struct TtlConfigsFrozen {}
@@ -1,66 +1,144 @@
1
- use crate::TransactionAuthData;
1
+ use super::*;
2
+
2
3
  use soroban_sdk::{
3
4
  address_payload::AddressPayload,
4
5
  auth::{Context, CustomAccountInterface},
6
+ contractimpl, contracttype,
7
+ crypto::Hash,
8
+ vec, Symbol,
5
9
  };
6
10
 
11
+ // ============================================================================
12
+ // Authentication Data Types
13
+ // ============================================================================
14
+
15
+ #[contracttype]
16
+ #[derive(Clone, Debug, Eq, PartialEq)]
17
+ pub enum Sender {
18
+ /// No explicit sender (permissionless execution).
19
+ None,
20
+ /// A registered admin (ed25519) submitting the transaction.
21
+ /// The tuple is `(public_key, signature)` where the signature covers the Soroban payload.
22
+ Admin(BytesN<32>, BytesN<64>),
23
+ }
24
+
25
+ /// Authentication data for DVN contract transactions.
26
+ ///
27
+ /// This struct is used with Soroban's custom account interface to authorize
28
+ /// transactions through a combination of admin signature and multisig quorum.
29
+ #[contracttype]
30
+ #[derive(Clone, Debug, Eq, PartialEq)]
31
+ pub struct TransactionAuthData {
32
+ /// Verifier ID - must match the DVN's configured VID.
33
+ pub vid: u32,
34
+ /// Expiration timestamp (ledger time) after which this auth is invalid.
35
+ pub expiration: u64,
36
+ /// Signatures from multisig signers (secp256k1, 65 bytes each).
37
+ pub signatures: Vec<BytesN<65>>,
38
+ /// Entity submitting the transaction (admin, or permissionless).
39
+ pub sender: Sender,
40
+ }
41
+
42
+ // ============================================================================
43
+ // Custom Account Interface Implementation
44
+ // ============================================================================
45
+
7
46
  #[contractimpl]
8
- impl CustomAccountInterface for Dvn {
47
+ impl CustomAccountInterface for LzDVN {
9
48
  type Signature = TransactionAuthData;
10
49
  type Error = DvnError;
11
50
 
12
- #[allow(non_snake_case)]
51
+ /// Validates authorization for DVN contract operations.
13
52
  fn __check_auth(
14
53
  env: Env,
15
54
  signature_payload: Hash<32>,
16
55
  auth_data: Self::Signature,
17
56
  auth_contexts: Vec<Context>,
18
57
  ) -> Result<(), Self::Error> {
19
- let TransactionAuthData { vid, expiration, signatures, admin, admin_signature } = auth_data;
20
-
21
- verify_admin(&env, &admin, &admin_signature, &signature_payload)?;
58
+ let TransactionAuthData { vid, expiration, signatures, sender } = auth_data;
22
59
 
23
- let stored_vid = DvnStorage::vid(&env).unwrap();
24
- if vid != stored_vid {
60
+ // 1. Check VID and expiration
61
+ if vid != Self::vid(&env) {
25
62
  return Err(DvnError::InvalidVid);
26
63
  }
27
-
28
64
  if expiration <= env.ledger().timestamp() {
29
65
  return Err(DvnError::AuthDataExpired);
30
66
  }
31
67
 
32
- let hash = hash_auth_data(&env, vid, expiration, &auth_contexts);
68
+ // 2. Admin verification (skip for quorum_change_admin)
69
+ if !Self::is_invoking_quorum_change_admin(&env, &auth_contexts) {
70
+ let Sender::Admin(public_key, signature) = sender else {
71
+ return Err(DvnError::OnlyAdmin);
72
+ };
73
+ Self::verify_admin_signature(&env, &public_key, &signature, &signature_payload)?;
74
+ }
33
75
 
76
+ // 3. Replay protection
77
+ let calls = Self::extract_execution_calls(&env, &auth_contexts)?;
78
+ let hash = Self::hash_call_data(&env, vid, expiration, &calls);
34
79
  if DvnStorage::used_hash(&env, &hash) {
35
80
  return Err(DvnError::HashAlreadyUsed);
36
81
  }
37
-
38
- Dvn::verify_signatures(&env, &hash, &signatures);
39
-
40
82
  DvnStorage::set_used_hash(&env, &hash, &true);
41
83
 
84
+ // 4. Multisig verification (most expensive - do last)
85
+ Self::verify_signatures(&env, &hash, &signatures);
86
+
42
87
  Ok(())
43
88
  }
44
89
  }
45
90
 
46
- fn verify_admin(
47
- env: &Env,
48
- admin: &BytesN<32>,
49
- admin_signature: &BytesN<64>,
50
- signature_payload: &Hash<32>,
51
- ) -> Result<(), DvnError> {
52
- let admin_address = Address::from_payload(env, AddressPayload::AccountIdPublicKeyEd25519(admin.clone()));
53
- if !Dvn::is_admin(env, &admin_address) {
54
- return Err(DvnError::OnlyAdmin);
55
- }
91
+ // ============================================================================
92
+ // Internal Helper Functions
93
+ // ============================================================================
56
94
 
57
- env.crypto().ed25519_verify(admin, &signature_payload.clone().into(), admin_signature);
95
+ impl LzDVN {
96
+ /// Verifies that the admin signature is valid and from a registered admin.
97
+ ///
98
+ /// # Arguments
99
+ /// * `public_key` - The admin's Ed25519 public key (32 bytes)
100
+ /// * `signature` - The admin's signature over the signature payload (64 bytes)
101
+ /// * `signature_payload` - The payload that was signed
102
+ ///
103
+ /// # Errors
104
+ /// Returns `DvnError::OnlyAdmin` if the signer is not a registered admin.
105
+ fn verify_admin_signature(
106
+ env: &Env,
107
+ public_key: &BytesN<32>,
108
+ signature: &BytesN<64>,
109
+ signature_payload: &Hash<32>,
110
+ ) -> Result<(), DvnError> {
111
+ let admin_address = Address::from_payload(env, AddressPayload::AccountIdPublicKeyEd25519(public_key.clone()));
112
+ if !Self::is_admin(env, &admin_address) {
113
+ return Err(DvnError::OnlyAdmin);
114
+ }
58
115
 
59
- Ok(())
60
- }
116
+ env.crypto().ed25519_verify(public_key, &signature_payload.clone().into(), signature);
61
117
 
62
- fn hash_auth_data(env: &Env, vid: u32, expiration: u64, auth_contexts: &Vec<Context>) -> BytesN<32> {
63
- let mut writer = BufferWriter::new(env);
64
- let data = writer.write_u32(vid).write_u64(expiration).write_bytes(&auth_contexts.to_xdr(env)).to_bytes();
65
- env.crypto().keccak256(&data).into()
118
+ Ok(())
119
+ }
120
+
121
+ /// Extracts the execution calls from the auth contexts.
122
+ fn extract_execution_calls(env: &Env, auth_contexts: &Vec<Context>) -> Result<Vec<Call>, DvnError> {
123
+ auth_contexts.iter().try_fold(vec![env], |mut calls, context| {
124
+ let Context::Contract(ctx) = context else {
125
+ return Err(DvnError::NonContractInvoke);
126
+ };
127
+ calls.push_back(Call { to: ctx.contract, func: ctx.fn_name, args: ctx.args });
128
+ Ok(calls)
129
+ })
130
+ }
131
+
132
+ /// Checks if the auth context is for `quorum_change_admin` on this contract.
133
+ /// This function doesn't require admin verification (quorum-only).
134
+ fn is_invoking_quorum_change_admin(env: &Env, auth_contexts: &Vec<Context>) -> bool {
135
+ // Must be exactly one call (no bundling with other operations)
136
+ if auth_contexts.len() != 1 {
137
+ return false;
138
+ }
139
+ let Context::Contract(ctx) = auth_contexts.first().unwrap() else {
140
+ return false;
141
+ };
142
+ ctx.contract == env.current_contract_address() && ctx.fn_name == Symbol::new(env, "quorum_change_admin")
143
+ }
66
144
  }