@layerzerolabs/protocol-stellar-v2 0.2.34 → 0.2.36

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 (135) hide show
  1. package/.turbo/turbo-build.log +281 -276
  2. package/.turbo/turbo-lint.log +209 -211
  3. package/.turbo/turbo-test.log +1705 -1701
  4. package/Cargo.lock +10 -10
  5. package/Cargo.toml +1 -1
  6. package/contracts/common-macros/src/auth.rs +5 -5
  7. package/contracts/common-macros/src/lib.rs +69 -0
  8. package/contracts/common-macros/src/rbac.rs +90 -0
  9. package/contracts/common-macros/src/storage.rs +7 -5
  10. package/contracts/common-macros/src/tests/lz_contract.rs +5 -7
  11. package/contracts/common-macros/src/tests/mod.rs +1 -0
  12. package/contracts/common-macros/src/tests/rbac.rs +420 -0
  13. package/contracts/common-macros/src/tests/snapshots/common_macros__tests__auth__snapshot_generated_multisig_code.snap +4 -4
  14. package/contracts/common-macros/src/tests/snapshots/common_macros__tests__auth__snapshot_generated_ownable_code.snap +5 -12
  15. package/contracts/common-macros/src/tests/snapshots/common_macros__tests__rbac__snapshot_preserve_function_signature.snap +17 -0
  16. package/contracts/common-macros/src/tests/storage/parse_name.rs +0 -1
  17. package/contracts/common-macros/src/tests/storage/snapshots/common_macros__tests__storage__generate_storage__snapshot_generated_storage_code.snap +3 -3
  18. package/contracts/endpoint-v2/src/endpoint_v2.rs +5 -4
  19. package/contracts/endpoint-v2/src/interfaces/messaging_channel.rs +7 -8
  20. package/contracts/endpoint-v2/src/messaging_channel.rs +78 -45
  21. package/contracts/endpoint-v2/src/storage.rs +8 -3
  22. package/contracts/endpoint-v2/src/tests/endpoint_setup.rs +2 -2
  23. package/contracts/endpoint-v2/src/tests/endpoint_v2/clear.rs +12 -15
  24. package/contracts/endpoint-v2/src/tests/endpoint_v2/verifiable.rs +46 -9
  25. package/contracts/endpoint-v2/src/tests/messaging_channel/burn.rs +7 -23
  26. package/contracts/endpoint-v2/src/tests/messaging_channel/clear_payload.rs +23 -20
  27. package/contracts/endpoint-v2/src/tests/messaging_channel/inbound.rs +94 -1
  28. package/contracts/endpoint-v2/src/tests/messaging_channel/inbound_nonce.rs +17 -15
  29. package/contracts/endpoint-v2/src/tests/messaging_channel/mod.rs +1 -1
  30. package/contracts/endpoint-v2/src/tests/messaging_channel/nilify.rs +48 -13
  31. package/contracts/endpoint-v2/src/tests/messaging_channel/pending_inbound_nonces.rs +111 -0
  32. package/contracts/endpoint-v2/src/tests/messaging_channel/skip.rs +15 -25
  33. package/contracts/layerzero-views/src/layerzero_view.rs +2 -2
  34. package/contracts/layerzero-views/src/tests/layerzero_view_tests.rs +3 -4
  35. package/contracts/layerzero-views/src/tests/setup.rs +0 -21
  36. package/contracts/macro-integration-tests/tests/runtime/lz_contract/wrapper_default.rs +1 -1
  37. package/contracts/macro-integration-tests/tests/runtime/lz_contract/wrapper_multisig.rs +1 -1
  38. package/contracts/macro-integration-tests/tests/runtime/lz_contract/wrapper_multisig_upgradeable.rs +1 -1
  39. package/contracts/macro-integration-tests/tests/runtime/multisig/self_auth.rs +1 -1
  40. package/contracts/macro-integration-tests/tests/runtime/ownable/initialization.rs +8 -5
  41. package/contracts/macro-integration-tests/tests/runtime/ownable/ownership_transfer.rs +2 -2
  42. package/contracts/macro-integration-tests/tests/runtime/rbac/guard_behavior.rs +91 -0
  43. package/contracts/macro-integration-tests/tests/runtime/rbac/mod.rs +30 -0
  44. package/contracts/macro-integration-tests/tests/runtime/ttl_configurable/configuration.rs +2 -2
  45. package/contracts/macro-integration-tests/tests/runtime/upgradeable/migrate_guard_and_state.rs +4 -4
  46. package/contracts/macro-integration-tests/tests/ui/lz_contract/pass/basic.rs +1 -1
  47. package/contracts/macro-integration-tests/tests/ui/ownable/pass/basic.rs +1 -1
  48. package/contracts/macro-integration-tests/tests/ui/rbac/fail/missing_env.rs +18 -0
  49. package/contracts/macro-integration-tests/tests/ui/rbac/fail/missing_env.stderr +16 -0
  50. package/contracts/macro-integration-tests/tests/ui/rbac/fail/param_not_address.rs +18 -0
  51. package/contracts/macro-integration-tests/tests/ui/rbac/fail/param_not_address.stderr +24 -0
  52. package/contracts/macro-integration-tests/tests/ui/rbac/fail/param_not_found.rs +18 -0
  53. package/contracts/macro-integration-tests/tests/ui/rbac/fail/param_not_found.stderr +24 -0
  54. package/contracts/macro-integration-tests/tests/ui/rbac/pass/basic.rs +71 -0
  55. package/contracts/macro-integration-tests/tests/ui_rbac.rs +12 -0
  56. package/contracts/message-libs/blocked-message-lib/src/lib.rs +4 -4
  57. package/contracts/message-libs/uln-302/src/send_uln.rs +5 -5
  58. package/contracts/oapps/counter/src/counter.rs +6 -0
  59. package/contracts/oapps/oapp/src/oapp_sender.rs +3 -2
  60. package/contracts/oapps/oft/src/extensions/oft_fee.rs +5 -0
  61. package/contracts/oapps/oft/src/interfaces/mintable.rs +2 -2
  62. package/contracts/oapps/oft/src/oft.rs +5 -4
  63. package/contracts/oapps/oft/src/tests/extensions/oft_fee.rs +2 -2
  64. package/contracts/oapps/oft/src/tests/extensions/pausable.rs +2 -2
  65. package/contracts/oapps/oft/src/tests/extensions/rate_limiter.rs +2 -2
  66. package/contracts/oapps/sac-manager/Cargo.toml +0 -1
  67. package/contracts/oapps/sac-manager/src/interfaces/mod.rs +3 -0
  68. package/contracts/oapps/sac-manager/src/interfaces/sac_admin_wrapper.rs +49 -0
  69. package/contracts/oapps/sac-manager/src/lib.rs +3 -3
  70. package/contracts/oapps/sac-manager/src/sac_manager.rs +45 -73
  71. package/contracts/oapps/sac-manager/src/storage.rs +2 -9
  72. package/contracts/oapps/sac-manager/src/tests/sac_manager/clawback.rs +8 -10
  73. package/contracts/oapps/sac-manager/src/tests/sac_manager/mint.rs +13 -18
  74. package/contracts/oapps/sac-manager/src/tests/sac_manager/mod.rs +0 -1
  75. package/contracts/oapps/sac-manager/src/tests/sac_manager/set_admin.rs +22 -12
  76. package/contracts/oapps/sac-manager/src/tests/sac_manager/set_authorized.rs +19 -9
  77. package/contracts/oapps/sac-manager/src/tests/sac_manager/test_helper.rs +27 -10
  78. package/contracts/oapps/sac-manager/src/tests/sac_manager/view_functions.rs +0 -15
  79. package/contracts/oapps/sac-manager/src/tests/test_helper.rs +19 -28
  80. package/contracts/upgrader/src/lib.rs +5 -2
  81. package/contracts/utils/src/auth.rs +6 -2
  82. package/contracts/utils/src/errors.rs +18 -0
  83. package/contracts/utils/src/lib.rs +1 -0
  84. package/contracts/utils/src/multisig.rs +5 -1
  85. package/contracts/utils/src/ownable.rs +1 -1
  86. package/contracts/utils/src/rbac.rs +428 -0
  87. package/contracts/utils/src/tests/auth.rs +2 -2
  88. package/contracts/utils/src/tests/mod.rs +1 -0
  89. package/contracts/utils/src/tests/multisig.rs +2 -2
  90. package/contracts/utils/src/tests/ownable.rs +4 -5
  91. package/contracts/utils/src/tests/rbac.rs +559 -0
  92. package/contracts/utils/src/tests/ttl_configurable.rs +5 -6
  93. package/contracts/utils/src/tests/upgradeable.rs +4 -5
  94. package/contracts/workers/worker/src/worker.rs +1 -1
  95. package/docs/layerzero-v2-on-stellar.md +46 -2
  96. package/package.json +3 -3
  97. package/sdk/.turbo/turbo-test.log +370 -372
  98. package/sdk/dist/generated/bml.d.ts +53 -3
  99. package/sdk/dist/generated/bml.js +27 -3
  100. package/sdk/dist/generated/counter.d.ts +84 -5
  101. package/sdk/dist/generated/counter.js +31 -4
  102. package/sdk/dist/generated/dvn.d.ts +55 -5
  103. package/sdk/dist/generated/dvn.js +28 -4
  104. package/sdk/dist/generated/dvn_fee_lib.d.ts +55 -5
  105. package/sdk/dist/generated/dvn_fee_lib.js +28 -4
  106. package/sdk/dist/generated/endpoint.d.ts +64 -15
  107. package/sdk/dist/generated/endpoint.js +32 -8
  108. package/sdk/dist/generated/executor.d.ts +55 -5
  109. package/sdk/dist/generated/executor.js +28 -4
  110. package/sdk/dist/generated/executor_fee_lib.d.ts +55 -5
  111. package/sdk/dist/generated/executor_fee_lib.js +28 -4
  112. package/sdk/dist/generated/executor_helper.d.ts +53 -3
  113. package/sdk/dist/generated/executor_helper.js +27 -3
  114. package/sdk/dist/generated/layerzero_view.d.ts +55 -5
  115. package/sdk/dist/generated/layerzero_view.js +28 -4
  116. package/sdk/dist/generated/oft.d.ts +84 -5
  117. package/sdk/dist/generated/oft.js +31 -4
  118. package/sdk/dist/generated/price_feed.d.ts +55 -5
  119. package/sdk/dist/generated/price_feed.js +28 -4
  120. package/sdk/dist/generated/sac_manager.d.ts +213 -666
  121. package/sdk/dist/generated/sac_manager.js +57 -238
  122. package/sdk/dist/generated/sml.d.ts +55 -5
  123. package/sdk/dist/generated/sml.js +28 -4
  124. package/sdk/dist/generated/treasury.d.ts +55 -5
  125. package/sdk/dist/generated/treasury.js +28 -4
  126. package/sdk/dist/generated/uln302.d.ts +55 -5
  127. package/sdk/dist/generated/uln302.js +28 -4
  128. package/sdk/dist/generated/upgrader.d.ts +53 -3
  129. package/sdk/dist/generated/upgrader.js +27 -3
  130. package/sdk/package.json +1 -1
  131. package/sdk/test/oft-sml.test.ts +10 -9
  132. package/sdk/test/{sac-manager-redistribution.test.ts → sac-manager.test.ts} +49 -25
  133. package/contracts/endpoint-v2/src/tests/messaging_channel/lazy_inbound_nonce.rs +0 -39
  134. package/contracts/oapps/sac-manager/src/errors.rs +0 -14
  135. package/contracts/oapps/sac-manager/src/tests/sac_manager/set_minter.rs +0 -69
@@ -0,0 +1,420 @@
1
+ use proc_macro2::TokenStream;
2
+ use quote::quote;
3
+
4
+ use crate::tests::test_helpers::{assert_panics_contains, filter_item_inputs_excluding_labels};
5
+
6
+ // ============================================
7
+ // Snapshot Test: has_role and only_role
8
+ // ============================================
9
+
10
+ #[test]
11
+ fn snapshot_preserve_function_signature() {
12
+ let args = quote! { caller, "minter" };
13
+ let input = quote! {
14
+ pub fn mint(env: Env, caller: Address, amount: i128) {
15
+ // mint logic
16
+ }
17
+ };
18
+
19
+ let has_role_result = crate::rbac::generate_role_check(args.clone(), input.clone(), false);
20
+ let has_role_formatted =
21
+ prettyplease::unparse(&syn::parse2::<syn::File>(has_role_result).expect("failed to parse generated code"));
22
+
23
+ let only_role_result = crate::rbac::generate_role_check(args, input, true);
24
+ let only_role_formatted =
25
+ prettyplease::unparse(&syn::parse2::<syn::File>(only_role_result).expect("failed to parse generated code"));
26
+
27
+ let combined =
28
+ format!("// === has_role ===\n\n{}\n\n// === only_role ===\n\n{}", has_role_formatted, only_role_formatted);
29
+
30
+ insta::assert_snapshot!(combined);
31
+ }
32
+
33
+ // ============================================
34
+ // generate_role_check assertion tests
35
+ // ============================================
36
+
37
+ fn assert_stmt_eq(stmt: &syn::Stmt, expected: &str, test_name: &str) {
38
+ let actual = quote::quote!(#stmt).to_string().replace(" ", "");
39
+ assert_eq!(actual, expected, "{test_name}: expected '{expected}', got '{actual}'");
40
+ }
41
+
42
+ fn assert_role_check_exact_stmts(
43
+ args: TokenStream,
44
+ input: TokenStream,
45
+ require_auth: bool,
46
+ expected_ensure_stmt_no_spaces: &str,
47
+ expected_auth_stmt_no_spaces: Option<&str>,
48
+ expected_stmt_count: Option<usize>,
49
+ test_name: &str,
50
+ ) -> syn::ItemFn {
51
+ let result_tokens = crate::rbac::generate_role_check(args, input, require_auth);
52
+ let output_fn: syn::ItemFn =
53
+ syn::parse2(result_tokens).unwrap_or_else(|e| panic!("{test_name}: failed to parse output function: {e}"));
54
+
55
+ assert!(!output_fn.block.stmts.is_empty(), "{test_name}: function body should contain at least one statement");
56
+
57
+ assert_stmt_eq(&output_fn.block.stmts[0], expected_ensure_stmt_no_spaces, test_name);
58
+
59
+ if let Some(expected_auth) = expected_auth_stmt_no_spaces {
60
+ assert!(output_fn.block.stmts.len() >= 2, "{test_name}: expected at least two statements");
61
+ assert_stmt_eq(&output_fn.block.stmts[1], expected_auth, test_name);
62
+ }
63
+
64
+ if let Some(expected_count) = expected_stmt_count {
65
+ assert_eq!(
66
+ output_fn.block.stmts.len(),
67
+ expected_count,
68
+ "{test_name}: expected {expected_count} statements, got {}",
69
+ output_fn.block.stmts.len()
70
+ );
71
+ }
72
+
73
+ output_fn
74
+ }
75
+
76
+ #[test]
77
+ fn test_role_check_inserts_expected_statements_table_driven() {
78
+ struct Case {
79
+ name: &'static str,
80
+ args: TokenStream,
81
+ input: TokenStream,
82
+ require_auth: bool,
83
+ expected_ensure_stmt: &'static str,
84
+ expected_auth_stmt: Option<&'static str>,
85
+ expected_stmt_count: usize,
86
+ }
87
+
88
+ let cases = vec![
89
+ Case {
90
+ name: "has_role: Env ref + Address value",
91
+ args: quote! { caller, "minter" },
92
+ input: quote! { pub fn mint(env: &Env, caller: Address, amount: i128) {} },
93
+ require_auth: false,
94
+ expected_ensure_stmt: "utils::rbac::ensure_role(env,&soroban_sdk::Symbol::new(env,\"minter\"),&caller);",
95
+ expected_auth_stmt: None,
96
+ expected_stmt_count: 1,
97
+ },
98
+ Case {
99
+ name: "only_role: Env owned + Address value",
100
+ args: quote! { caller, "minter" },
101
+ input: quote! { pub fn mint(env: Env, caller: Address, amount: i128) {} },
102
+ require_auth: true,
103
+ expected_ensure_stmt: "utils::rbac::ensure_role(&env,&soroban_sdk::Symbol::new(&env,\"minter\"),&caller);",
104
+ expected_auth_stmt: Some("caller.require_auth();"),
105
+ expected_stmt_count: 2,
106
+ },
107
+ Case {
108
+ name: "only_role: Env ref + Address value",
109
+ args: quote! { caller, "minter" },
110
+ input: quote! { pub fn mint(env: &Env, caller: Address, amount: i128) {} },
111
+ require_auth: true,
112
+ expected_ensure_stmt: "utils::rbac::ensure_role(env,&soroban_sdk::Symbol::new(env,\"minter\"),&caller);",
113
+ expected_auth_stmt: Some("caller.require_auth();"),
114
+ expected_stmt_count: 2,
115
+ },
116
+ Case {
117
+ name: "has_role: &Address param uses account directly",
118
+ args: quote! { account, "admin" },
119
+ input: quote! { pub fn admin_action(env: Env, account: &Address) {} },
120
+ require_auth: false,
121
+ expected_ensure_stmt: "utils::rbac::ensure_role(&env,&soroban_sdk::Symbol::new(&env,\"admin\"),account);",
122
+ expected_auth_stmt: None,
123
+ expected_stmt_count: 1,
124
+ },
125
+ ];
126
+
127
+ for c in cases {
128
+ assert_role_check_exact_stmts(
129
+ c.args,
130
+ c.input,
131
+ c.require_auth,
132
+ c.expected_ensure_stmt,
133
+ c.expected_auth_stmt,
134
+ Some(c.expected_stmt_count),
135
+ c.name,
136
+ );
137
+ }
138
+ }
139
+
140
+ #[test]
141
+ fn test_has_role_role_arg_variants_generate_expected_symbol_new_table_driven() {
142
+ struct Case {
143
+ name: &'static str,
144
+ args: TokenStream,
145
+ input: TokenStream,
146
+ expected_ensure_stmt: &'static str,
147
+ }
148
+
149
+ let cases = vec![
150
+ Case {
151
+ name: "role string literal passed to Symbol::new (Env ref)",
152
+ args: quote! { caller, "minter" },
153
+ input: quote! { pub fn mint(env: &Env, caller: Address) {} },
154
+ expected_ensure_stmt: "utils::rbac::ensure_role(env,&soroban_sdk::Symbol::new(env,\"minter\"),&caller);",
155
+ },
156
+ Case {
157
+ name: "role const expr passed to Symbol::new (Env ref)",
158
+ args: quote! { caller, MINTER_ROLE },
159
+ input: quote! { pub fn mint(env: &Env, caller: Address) {} },
160
+ expected_ensure_stmt: "utils::rbac::ensure_role(env,&soroban_sdk::Symbol::new(env,MINTER_ROLE),&caller);",
161
+ },
162
+ Case {
163
+ name: "role const expr passed to Symbol::new (Env owned)",
164
+ args: quote! { caller, MINTER_ROLE },
165
+ input: quote! { pub fn mint(env: Env, caller: Address) {} },
166
+ expected_ensure_stmt: "utils::rbac::ensure_role(&env,&soroban_sdk::Symbol::new(&env,MINTER_ROLE),&caller);",
167
+ },
168
+ Case {
169
+ name: "role path expr passed to Symbol::new",
170
+ args: quote! { caller, roles::MINTER_ROLE },
171
+ input: quote! { pub fn mint(env: &Env, caller: Address) {} },
172
+ expected_ensure_stmt:
173
+ "utils::rbac::ensure_role(env,&soroban_sdk::Symbol::new(env,roles::MINTER_ROLE),&caller);",
174
+ },
175
+ ];
176
+
177
+ for c in cases {
178
+ assert_role_check_exact_stmts(c.args, c.input, false, c.expected_ensure_stmt, None, Some(1), c.name);
179
+ }
180
+ }
181
+
182
+ // ============================================
183
+ // Error cases: invalid args (table-driven)
184
+ // ============================================
185
+
186
+ #[test]
187
+ fn test_has_role_rejects_invalid_args_table_driven() {
188
+ struct Case {
189
+ name: &'static str,
190
+ args: TokenStream,
191
+ expected_substring: &'static str,
192
+ }
193
+
194
+ let input = quote! { pub fn mint(env: Env, caller: Address) {} };
195
+ let cases = vec![
196
+ Case {
197
+ name: "missing comma in args",
198
+ args: quote! { caller "minter" },
199
+ expected_substring: "failed to parse has_role/only_role args",
200
+ },
201
+ Case {
202
+ name: "missing role",
203
+ args: quote! { caller, },
204
+ expected_substring: "failed to parse has_role/only_role args",
205
+ },
206
+ Case {
207
+ name: "extra tokens in args",
208
+ args: quote! { caller, "minter", extra },
209
+ expected_substring: "failed to parse has_role/only_role args",
210
+ },
211
+ ];
212
+
213
+ for c in cases {
214
+ assert_panics_contains(c.name, c.expected_substring, || {
215
+ crate::rbac::generate_role_check(c.args.clone(), input.clone(), false);
216
+ });
217
+ }
218
+ }
219
+
220
+ // ============================================
221
+ // Error cases: invalid function signature inputs (table-driven)
222
+ // ============================================
223
+
224
+ #[test]
225
+ fn test_has_role_rejects_invalid_function_signature_table_driven() {
226
+ struct Case {
227
+ name: &'static str,
228
+ args: TokenStream,
229
+ input: TokenStream,
230
+ expected_substring: &'static str,
231
+ }
232
+
233
+ let args = quote! { caller, "minter" };
234
+ let cases = vec![
235
+ Case {
236
+ name: "no Env param",
237
+ args: args.clone(),
238
+ input: quote! { pub fn mint(caller: Address, amount: i128) {} },
239
+ expected_substring: "function must have an Env argument",
240
+ },
241
+ Case {
242
+ name: "param not in signature",
243
+ args: args.clone(),
244
+ input: quote! { pub fn mint(env: Env, account: Address, amount: i128) {} },
245
+ expected_substring: "not found in function signature",
246
+ },
247
+ Case {
248
+ name: "param not Address",
249
+ args: args.clone(),
250
+ input: quote! { pub fn mint(env: Env, caller: u32, amount: i128) {} },
251
+ expected_substring: "must be of type `Address` or `&Address`",
252
+ },
253
+ Case {
254
+ name: "wildcard Env pattern",
255
+ args: args.clone(),
256
+ input: quote! { pub fn mint(_: Env, caller: Address) {} },
257
+ expected_substring: "function must have an Env argument",
258
+ },
259
+ Case {
260
+ name: "tuple Env pattern",
261
+ args: args.clone(),
262
+ input: quote! { pub fn mint((env, _): (&Env, u32), caller: Address) { let _ = env; } },
263
+ expected_substring: "function must have an Env argument",
264
+ },
265
+ Case {
266
+ name: "struct Env pattern",
267
+ args: args.clone(),
268
+ input: quote! { pub fn mint(Env { .. }: Env, caller: Address) {} },
269
+ expected_substring: "function must have an Env argument",
270
+ },
271
+ Case {
272
+ name: "&&Address is invalid",
273
+ args: args.clone(),
274
+ input: quote! { pub fn mint(env: &Env, caller: &&Address) {} },
275
+ expected_substring: "must be of type `Address` or `&Address`",
276
+ },
277
+ ];
278
+
279
+ for c in cases {
280
+ assert_panics_contains(c.name, c.expected_substring, || {
281
+ crate::rbac::generate_role_check(c.args.clone(), c.input.clone(), false);
282
+ });
283
+ }
284
+ }
285
+
286
+ // ============================================
287
+ // Error cases: non-function input
288
+ // ============================================
289
+
290
+ #[test]
291
+ fn test_has_role_rejects_non_function_inputs() {
292
+ let args = quote! { caller, "minter" };
293
+ for (case, input) in filter_item_inputs_excluding_labels(&["function"]) {
294
+ assert_panics_contains(case, "failed to parse function", || {
295
+ crate::rbac::generate_role_check(args.clone(), input.clone(), false);
296
+ });
297
+ }
298
+ }
299
+
300
+ // ============================================
301
+ // High-value coverage: Env + Address + role variants (table-driven)
302
+ // ============================================
303
+
304
+ #[test]
305
+ fn test_has_role_env_variants_generate_correct_env_ref_in_ensure_role() {
306
+ struct Case {
307
+ name: &'static str,
308
+ input: TokenStream,
309
+ expected_ensure_stmt: &'static str,
310
+ }
311
+
312
+ // Role is a literal so we also validate Symbol::new uses the same env_ref form.
313
+ let args = quote! { caller, "minter" };
314
+
315
+ let cases = vec![
316
+ Case {
317
+ name: "owned Env",
318
+ input: quote! { pub fn mint(env: Env, caller: Address) {} },
319
+ expected_ensure_stmt: "utils::rbac::ensure_role(&env,&soroban_sdk::Symbol::new(&env,\"minter\"),&caller);",
320
+ },
321
+ Case {
322
+ name: "ref Env",
323
+ input: quote! { pub fn mint(env: &Env, caller: Address) {} },
324
+ expected_ensure_stmt: "utils::rbac::ensure_role(env,&soroban_sdk::Symbol::new(env,\"minter\"),&caller);",
325
+ },
326
+ Case {
327
+ name: "mut ref Env",
328
+ input: quote! { pub fn mint(env: &mut Env, caller: Address) {} },
329
+ expected_ensure_stmt: "utils::rbac::ensure_role(env,&soroban_sdk::Symbol::new(env,\"minter\"),&caller);",
330
+ },
331
+ Case {
332
+ name: "qualified owned Env",
333
+ input: quote! { pub fn mint(env: soroban_sdk::Env, caller: Address) {} },
334
+ expected_ensure_stmt: "utils::rbac::ensure_role(&env,&soroban_sdk::Symbol::new(&env,\"minter\"),&caller);",
335
+ },
336
+ Case {
337
+ name: "qualified ref Env",
338
+ input: quote! { pub fn mint(env: &soroban_sdk::Env, caller: Address) {} },
339
+ expected_ensure_stmt: "utils::rbac::ensure_role(env,&soroban_sdk::Symbol::new(env,\"minter\"),&caller);",
340
+ },
341
+ Case {
342
+ name: "leading :: qualified owned Env",
343
+ input: quote! { pub fn mint(env: ::soroban_sdk::Env, caller: Address) {} },
344
+ expected_ensure_stmt: "utils::rbac::ensure_role(&env,&soroban_sdk::Symbol::new(&env,\"minter\"),&caller);",
345
+ },
346
+ ];
347
+
348
+ for c in cases {
349
+ assert_role_check_exact_stmts(args.clone(), c.input, false, c.expected_ensure_stmt, None, Some(1), c.name);
350
+ }
351
+ }
352
+
353
+ #[test]
354
+ fn test_has_role_address_variants_generate_correct_param_reference() {
355
+ struct Case {
356
+ name: &'static str,
357
+ input: TokenStream,
358
+ expected_ensure_stmt: &'static str,
359
+ }
360
+
361
+ let args = quote! { caller, "minter" };
362
+
363
+ let cases = vec![
364
+ Case {
365
+ name: "Address by value -> &caller",
366
+ input: quote! { pub fn mint(env: &Env, caller: Address) {} },
367
+ expected_ensure_stmt: "utils::rbac::ensure_role(env,&soroban_sdk::Symbol::new(env,\"minter\"),&caller);",
368
+ },
369
+ Case {
370
+ name: "&Address -> caller",
371
+ input: quote! { pub fn mint(env: &Env, caller: &Address) {} },
372
+ expected_ensure_stmt: "utils::rbac::ensure_role(env,&soroban_sdk::Symbol::new(env,\"minter\"),caller);",
373
+ },
374
+ Case {
375
+ name: "&mut Address -> caller",
376
+ input: quote! { pub fn mint(env: &Env, caller: &mut Address) {} },
377
+ expected_ensure_stmt: "utils::rbac::ensure_role(env,&soroban_sdk::Symbol::new(env,\"minter\"),caller);",
378
+ },
379
+ Case {
380
+ name: "qualified Address by value",
381
+ input: quote! { pub fn mint(env: &Env, caller: soroban_sdk::Address) {} },
382
+ expected_ensure_stmt: "utils::rbac::ensure_role(env,&soroban_sdk::Symbol::new(env,\"minter\"),&caller);",
383
+ },
384
+ Case {
385
+ name: "qualified &Address",
386
+ input: quote! { pub fn mint(env: &Env, caller: &soroban_sdk::Address) {} },
387
+ expected_ensure_stmt: "utils::rbac::ensure_role(env,&soroban_sdk::Symbol::new(env,\"minter\"),caller);",
388
+ },
389
+ ];
390
+
391
+ for c in cases {
392
+ assert_role_check_exact_stmts(args.clone(), c.input, false, c.expected_ensure_stmt, None, Some(1), c.name);
393
+ }
394
+ }
395
+
396
+ #[test]
397
+ fn test_only_role_inserts_expected_statements_and_preserves_original_body() {
398
+ let args = quote! { caller, "minter" };
399
+ let input = quote! {
400
+ pub fn mint(env: Env, caller: Address) {
401
+ let x = 1u32;
402
+ let _ = x + 1;
403
+ }
404
+ };
405
+
406
+ let output_fn = assert_role_check_exact_stmts(
407
+ args,
408
+ input,
409
+ true,
410
+ "utils::rbac::ensure_role(&env,&soroban_sdk::Symbol::new(&env,\"minter\"),&caller);",
411
+ Some("caller.require_auth();"),
412
+ Some(4),
413
+ "only_role inserts ensure_role + require_auth",
414
+ );
415
+
416
+ // Ensure original statements remain after the inserted checks.
417
+ let third_stmt = &output_fn.block.stmts[2];
418
+ let third_stmt_str = quote::quote!(#third_stmt).to_string().replace(" ", "");
419
+ assert!(third_stmt_str.starts_with("letx=1u32;"), "expected original 'let x = 1u32;' to be preserved");
420
+ }
@@ -8,8 +8,8 @@ pub struct MyContract;
8
8
  use utils::{auth::Auth as _, multisig::MultiSig as _};
9
9
  #[common_macros::contract_impl]
10
10
  impl utils::auth::Auth for MyContract {
11
- fn authorizer(env: &soroban_sdk::Env) -> soroban_sdk::Address {
12
- env.current_contract_address()
11
+ fn authorizer(env: &soroban_sdk::Env) -> Option<soroban_sdk::Address> {
12
+ Some(env.current_contract_address())
13
13
  }
14
14
  }
15
15
  #[common_macros::contract_impl(contracttrait)]
@@ -23,8 +23,8 @@ pub struct MyContract(pub u32);
23
23
  use utils::{auth::Auth as _, multisig::MultiSig as _};
24
24
  #[common_macros::contract_impl]
25
25
  impl utils::auth::Auth for MyContract {
26
- fn authorizer(env: &soroban_sdk::Env) -> soroban_sdk::Address {
27
- env.current_contract_address()
26
+ fn authorizer(env: &soroban_sdk::Env) -> Option<soroban_sdk::Address> {
27
+ Some(env.current_contract_address())
28
28
  }
29
29
  }
30
30
  #[common_macros::contract_impl(contracttrait)]
@@ -1,20 +1,17 @@
1
1
  ---
2
2
  source: contracts/common-macros/src/tests/auth.rs
3
+ assertion_line: 36
3
4
  expression: combined
4
5
  ---
5
6
  // === Unit struct ===
6
7
 
7
8
  pub struct MyContract;
8
- use utils::{
9
- auth::Auth as _, option_ext::OptionExt as _,
10
- ownable::{Ownable as _, OwnableInitializer as _},
11
- };
9
+ use utils::{auth::Auth as _, ownable::{Ownable as _, OwnableInitializer as _}};
12
10
  impl utils::ownable::OwnableInitializer for MyContract {}
13
11
  #[common_macros::contract_impl]
14
12
  impl utils::auth::Auth for MyContract {
15
- fn authorizer(env: &soroban_sdk::Env) -> soroban_sdk::Address {
13
+ fn authorizer(env: &soroban_sdk::Env) -> Option<soroban_sdk::Address> {
16
14
  <Self as utils::ownable::Ownable>::owner(env)
17
- .unwrap_or_panic(env, utils::errors::OwnableError::OwnerNotSet)
18
15
  }
19
16
  }
20
17
  #[common_macros::contract_impl(contracttrait)]
@@ -30,16 +27,12 @@ where
30
27
  {
31
28
  pub value: T,
32
29
  }
33
- use utils::{
34
- auth::Auth as _, option_ext::OptionExt as _,
35
- ownable::{Ownable as _, OwnableInitializer as _},
36
- };
30
+ use utils::{auth::Auth as _, ownable::{Ownable as _, OwnableInitializer as _}};
37
31
  impl utils::ownable::OwnableInitializer for MyContract {}
38
32
  #[common_macros::contract_impl]
39
33
  impl utils::auth::Auth for MyContract {
40
- fn authorizer(env: &soroban_sdk::Env) -> soroban_sdk::Address {
34
+ fn authorizer(env: &soroban_sdk::Env) -> Option<soroban_sdk::Address> {
41
35
  <Self as utils::ownable::Ownable>::owner(env)
42
- .unwrap_or_panic(env, utils::errors::OwnableError::OwnerNotSet)
43
36
  }
44
37
  }
45
38
  #[common_macros::contract_impl(contracttrait)]
@@ -0,0 +1,17 @@
1
+ ---
2
+ source: contracts/common-macros/src/tests/rbac.rs
3
+ expression: combined
4
+ ---
5
+ // === has_role ===
6
+
7
+ pub fn mint(env: Env, caller: Address, amount: i128) {
8
+ utils::rbac::ensure_role(&env, &soroban_sdk::Symbol::new(&env, "minter"), &caller);
9
+ }
10
+
11
+
12
+ // === only_role ===
13
+
14
+ pub fn mint(env: Env, caller: Address, amount: i128) {
15
+ utils::rbac::ensure_role(&env, &soroban_sdk::Symbol::new(&env, "minter"), &caller);
16
+ caller.require_auth();
17
+ }
@@ -156,4 +156,3 @@ fn test_parses_name_with_special_chars() {
156
156
  let name = result.unwrap();
157
157
  assert_eq!(name, Some("key_v2_beta".to_string()));
158
158
  }
159
-
@@ -1,5 +1,5 @@
1
1
  ---
2
- source: contracts/common-macros/src/tests/storage.rs
2
+ source: contracts/common-macros/src/tests/storage/generate_storage.rs
3
3
  assertion_line: 409
4
4
  expression: formatted
5
5
  ---
@@ -20,7 +20,7 @@ impl StorageKeys {
20
20
  pub fn counter(env: &soroban_sdk::Env) -> u32 {
21
21
  let key = StorageKeys::Counter;
22
22
  let value = env.storage().instance().get::<_, u32>(&key);
23
- value.unwrap_or(0)
23
+ value.unwrap_or_else(|| 0)
24
24
  }
25
25
  pub fn set_counter(env: &soroban_sdk::Env, value: &u32) {
26
26
  let key = StorageKeys::Counter;
@@ -46,7 +46,7 @@ impl StorageKeys {
46
46
  if value.is_some() {
47
47
  utils::ttl_configurable::extend_persistent_ttl(env, &key);
48
48
  }
49
- value.unwrap_or(String::from_str(env, "hello"))
49
+ value.unwrap_or_else(|| String::from_str(env, "hello"))
50
50
  }
51
51
  pub fn set_message(env: &soroban_sdk::Env, sender: &Address, value: &String) {
52
52
  let key = StorageKeys::Message(sender.clone());
@@ -1,4 +1,5 @@
1
1
  use crate::{
2
+ endpoint_v2::messaging_channel::PENDING_INBOUND_NONCE_MAX_LEN,
2
3
  errors::EndpointError,
3
4
  events::{DelegateSet, LzReceiveAlert, PacketDelivered, PacketSent, PacketVerified, ZroSet},
4
5
  interfaces::{ILayerZeroEndpointV2, IMessageLibManager, IMessagingChannel, MessagingFee, MessagingReceipt, Origin},
@@ -165,14 +166,14 @@ impl ILayerZeroEndpointV2 for EndpointV2 {
165
166
 
166
167
  /// Checks if a messaging path can be/has been initialized for the given origin and receiver.
167
168
  fn initializable(env: &Env, origin: &Origin, receiver: &Address) -> bool {
168
- let lazy_inbound_nonce = Self::lazy_inbound_nonce(env, receiver, origin.src_eid, &origin.sender);
169
- lazy_inbound_nonce > 0 || LayerZeroReceiverClient::new(env, receiver).allow_initialize_path(origin)
169
+ let inbound_nonce = Self::inbound_nonce(env, receiver, origin.src_eid, &origin.sender);
170
+ inbound_nonce > 0 || LayerZeroReceiverClient::new(env, receiver).allow_initialize_path(origin)
170
171
  }
171
172
 
172
173
  /// Checks if a message can be verified for the given origin and receiver.
173
174
  fn verifiable(env: &Env, origin: &Origin, receiver: &Address) -> bool {
174
- let lazy_inbound_nonce = Self::lazy_inbound_nonce(env, receiver, origin.src_eid, &origin.sender);
175
- origin.nonce > lazy_inbound_nonce
175
+ let inbound_nonce = Self::inbound_nonce(env, receiver, origin.src_eid, &origin.sender);
176
+ (origin.nonce > inbound_nonce && origin.nonce <= inbound_nonce + PENDING_INBOUND_NONCE_MAX_LEN)
176
177
  || EndpointStorage::has_inbound_payload_hash(env, receiver, origin.src_eid, &origin.sender, origin.nonce)
177
178
  }
178
179
 
@@ -1,4 +1,4 @@
1
- use soroban_sdk::{contractclient, Address, BytesN, Env};
1
+ use soroban_sdk::{contractclient, Address, BytesN, Env, Vec};
2
2
 
3
3
  /// EndpointV2's Interface for managing messaging channels, nonces, and payload hashes.
4
4
  #[contractclient(name = "MessagingChannelClient")]
@@ -78,19 +78,18 @@ pub trait IMessagingChannel {
78
78
  /// The current outbound nonce (0 if no messages sent yet)
79
79
  fn outbound_nonce(env: &Env, sender: &Address, dst_eid: u32, receiver: &BytesN<32>) -> u64;
80
80
 
81
- /// Returns the max index of the longest gapless sequence of verified nonces.
82
- /// Example: `[1,2,3,4,6,7] => 4`, `[1,2,6,8,10] => 2`
81
+ /// Returns the current inbound nonce for a specific path.
83
82
  ///
84
83
  /// # Arguments
85
84
  /// * `receiver` - The receiver OApp address
86
85
  /// * `src_eid` - The source endpoint ID
87
- /// * `sender` - The sender address on the source chain
86
+ /// * `sender` - The sender OApp address on the source chain
88
87
  ///
89
88
  /// # Returns
90
- /// The highest nonce in the gapless sequence starting from lazy_inbound_nonce
89
+ /// The current inbound nonce (0 if no messages received yet)
91
90
  fn inbound_nonce(env: &Env, receiver: &Address, src_eid: u32, sender: &BytesN<32>) -> u64;
92
91
 
93
- /// Returns the lazy inbound nonce (last checkpoint) for a specific path.
92
+ /// Returns the pending inbound nonces for a specific path.
94
93
  ///
95
94
  /// # Arguments
96
95
  /// * `receiver` - The receiver OApp address
@@ -98,8 +97,8 @@ pub trait IMessagingChannel {
98
97
  /// * `sender` - The sender OApp address on the source chain
99
98
  ///
100
99
  /// # Returns
101
- /// The lazy inbound nonce, updated when messages are cleared/executed
102
- fn lazy_inbound_nonce(env: &Env, receiver: &Address, src_eid: u32, sender: &BytesN<32>) -> u64;
100
+ /// The pending inbound nonces
101
+ fn pending_inbound_nonces(env: &Env, receiver: &Address, src_eid: u32, sender: &BytesN<32>) -> Vec<u64>;
103
102
 
104
103
  /// Returns the payload hash for a specific inbound nonce.
105
104
  ///