@layerzerolabs/common-utils-macros-stellar-contracts 0.2.122

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 (58) hide show
  1. package/Cargo.toml +21 -0
  2. package/LICENSE +23 -0
  3. package/clippy.toml +7 -0
  4. package/package.json +37 -0
  5. package/rust-toolchain.toml +4 -0
  6. package/rustfmt.toml +15 -0
  7. package/src/auth.rs +95 -0
  8. package/src/contract_ttl.rs +92 -0
  9. package/src/error.rs +43 -0
  10. package/src/lib.rs +585 -0
  11. package/src/lz_contract.rs +105 -0
  12. package/src/rbac.rs +90 -0
  13. package/src/storage.rs +522 -0
  14. package/src/tests/auth.rs +230 -0
  15. package/src/tests/contract_ttl.rs +695 -0
  16. package/src/tests/error.rs +156 -0
  17. package/src/tests/lz_contract.rs +87 -0
  18. package/src/tests/mod.rs +11 -0
  19. package/src/tests/rbac.rs +523 -0
  20. package/src/tests/snapshots/common_macros__tests__auth__snapshot_generated_multisig_code.snap +31 -0
  21. package/src/tests/snapshots/common_macros__tests__auth__snapshot_generated_ownable_code.snap +39 -0
  22. package/src/tests/snapshots/common_macros__tests__auth__snapshot_only_auth_preserves_function_signature.snap +19 -0
  23. package/src/tests/snapshots/common_macros__tests__contract_ttl__snapshot_generated_contractimpl_code.snap +77 -0
  24. package/src/tests/snapshots/common_macros__tests__contract_ttl__snapshot_generated_contracttrait_code.snap +46 -0
  25. package/src/tests/snapshots/common_macros__tests__error__snapshot_generated_contract_error_code.snap +20 -0
  26. package/src/tests/snapshots/common_macros__tests__lz_contract__snapshot_generated_lz_contract_code.snap +51 -0
  27. package/src/tests/snapshots/common_macros__tests__rbac__snapshot_authorizer_role.snap +21 -0
  28. package/src/tests/snapshots/common_macros__tests__rbac__snapshot_preserve_function_signature.snap +21 -0
  29. package/src/tests/snapshots/common_macros__tests__ttl_configurable__snapshot_generated_ttl_configurable_code.snap +10 -0
  30. package/src/tests/snapshots/common_macros__tests__ttl_extendable__snapshot_generated_ttl_extendable_code.snap +8 -0
  31. package/src/tests/snapshots/common_macros__tests__upgradeable__snapshot_generated_upgradeable_code.snap +28 -0
  32. package/src/tests/storage/extract_fields.rs +87 -0
  33. package/src/tests/storage/gen_accessor_methods.rs +223 -0
  34. package/src/tests/storage/gen_args.rs +65 -0
  35. package/src/tests/storage/gen_enum_variant.rs +78 -0
  36. package/src/tests/storage/gen_key.rs +108 -0
  37. package/src/tests/storage/gen_params.rs +105 -0
  38. package/src/tests/storage/generate_storage.rs +410 -0
  39. package/src/tests/storage/is_primitive_type.rs +48 -0
  40. package/src/tests/storage/mod.rs +16 -0
  41. package/src/tests/storage/parse_default.rs +164 -0
  42. package/src/tests/storage/parse_name.rs +158 -0
  43. package/src/tests/storage/parse_no_ttl_extension.rs +124 -0
  44. package/src/tests/storage/parse_storage_type.rs +174 -0
  45. package/src/tests/storage/snapshots/common_macros__tests__storage__generate_storage__snapshot_generated_storage_code.snap +412 -0
  46. package/src/tests/storage/storage_kind.rs +39 -0
  47. package/src/tests/storage/test_setup.rs +25 -0
  48. package/src/tests/storage/validate_attrs.rs +138 -0
  49. package/src/tests/storage/variant_config.rs +226 -0
  50. package/src/tests/test_helpers.rs +87 -0
  51. package/src/tests/ttl_configurable.rs +34 -0
  52. package/src/tests/ttl_extendable.rs +32 -0
  53. package/src/tests/upgradeable.rs +169 -0
  54. package/src/tests/utils.rs +267 -0
  55. package/src/ttl_configurable.rs +24 -0
  56. package/src/ttl_extendable.rs +28 -0
  57. package/src/upgradeable.rs +136 -0
  58. package/src/utils.rs +56 -0
package/Cargo.toml ADDED
@@ -0,0 +1,21 @@
1
+ # @layerzerolabs/common-utils-macros-stellar-contracts. Proc-macro crate; self-contained
2
+ # version/edition/deps so it builds as a path dep and vendors cleanly into consumers.
3
+ [package]
4
+ name = "common-macros"
5
+ version = "0.0.1"
6
+ edition = "2021"
7
+ publish = false
8
+
9
+ [lib]
10
+ proc-macro = true
11
+
12
+ [dependencies]
13
+ proc-macro2 = "1.0"
14
+ quote = "1.0"
15
+ syn = { version = "2.0", features = ["full", "extra-traits"] }
16
+ heck = "0.5"
17
+ itertools = "0.14"
18
+
19
+ [dev-dependencies]
20
+ insta = "1.44.3"
21
+ prettyplease = "0.2.37"
package/LICENSE ADDED
@@ -0,0 +1,23 @@
1
+ Copyright (c) 2026 - LayerZero Labs Ltd.
2
+
3
+ Permission is hereby granted, free of charge, to any
4
+ person obtaining a copy of this software and associated
5
+ documentation files (the "Software"), to deal in the
6
+ Software without restriction, including without
7
+ limitation the rights to use, copy, modify, merge,
8
+ publish, distribute, sublicense, and/or sell copies of
9
+ the Software, and to permit persons to whom the Software
10
+ is furnished to do so, subject to the following
11
+ conditions:
12
+ The above copyright notice and this permission notice
13
+ shall be included in all copies or substantial portions
14
+ of the Software.
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
16
+ ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
17
+ TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
18
+ PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
19
+ SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
22
+ IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23
+ DEALINGS IN THE SOFTWARE.
package/clippy.toml ADDED
@@ -0,0 +1,7 @@
1
+ # Clippy configuration for Stellar protocol contracts
2
+
3
+ # Set the too_many_arguments threshold to 11.
4
+ # This aligns with Stellar: contract functions are limited to 10 parameters, plus 1 for 'env'.
5
+ # (Default is 7; raised here to prevent unnecessary clippy warnings on valid contract interfaces.)
6
+ too-many-arguments-threshold = 11
7
+
package/package.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "@layerzerolabs/common-utils-macros-stellar-contracts",
3
+ "version": "0.2.122",
4
+ "private": false,
5
+ "description": "Stellar common-utils procedural macros",
6
+ "license": "MIT",
7
+ "files": [
8
+ "LICENSE",
9
+ "src",
10
+ "Cargo.toml",
11
+ "rust-toolchain.toml",
12
+ "rustfmt.toml",
13
+ "clippy.toml"
14
+ ],
15
+ "devDependencies": {
16
+ "@layerzerolabs/vm-tooling-stellar": "0.2.122"
17
+ },
18
+ "publishConfig": {
19
+ "access": "public",
20
+ "registry": "https://registry.npmjs.org/"
21
+ },
22
+ "externalRepoConfig": {
23
+ "targets": [
24
+ "audit-external",
25
+ "console-pen-test",
26
+ "monorepo-external",
27
+ "onesig-client"
28
+ ]
29
+ },
30
+ "scripts": {
31
+ "build": "pnpm exec lz-tool --script \"cargo build\" stellar",
32
+ "clean": "rm -rf ./target ./node_modules",
33
+ "format": "pnpm exec lz-tool --script \"cargo fmt\" stellar",
34
+ "lint": "pnpm exec lz-tool --script \"cargo clippy -- -D warnings\" stellar",
35
+ "test": "pnpm exec lz-tool --script \"cargo nextest run\" stellar"
36
+ }
37
+ }
@@ -0,0 +1,4 @@
1
+ [toolchain]
2
+ channel = "1.90.0"
3
+ targets = ["wasm32v1-none"]
4
+ components = ["rustfmt", "clippy"]
package/rustfmt.toml ADDED
@@ -0,0 +1,15 @@
1
+ format_macro_bodies = true
2
+ max_width = 120
3
+ format_macro_matchers = true
4
+ format_strings = true
5
+ imports_granularity = "Crate"
6
+ match_arm_blocks = false
7
+ reorder_impl_items = true
8
+ group_imports = "StdExternalCrate"
9
+ use_field_init_shorthand = true
10
+ use_small_heuristics = "Max"
11
+ wrap_comments = true
12
+ format_code_in_doc_comments = true
13
+
14
+ # most of these are unstable, so we enable them
15
+ unstable_features = true
package/src/auth.rs ADDED
@@ -0,0 +1,95 @@
1
+ //! Authorization macros for Stellar contracts.
2
+ //!
3
+ //! This module provides macros for implementing authorization patterns:
4
+ //! - `#[ownable]` - Owner-based access control (external owner address)
5
+ //! - `#[multisig]` - MultiSig-based access control (self-owning pattern)
6
+ //! - `#[only_auth]` - Authorization check attribute for functions
7
+
8
+ use crate::utils;
9
+ use proc_macro2::TokenStream;
10
+ use quote::{quote, ToTokens};
11
+ use syn::{parse_quote, ItemFn, ItemStruct};
12
+
13
+ // ============================================================================
14
+ // Ownable Macro Implementation
15
+ // ============================================================================
16
+
17
+ /// Generates the ownable implementation from the `#[ownable]` attribute macro.
18
+ ///
19
+ /// This macro implements `OwnableInitializer`, `Auth`, and `Ownable` traits for the contract:
20
+ /// - `OwnableInitializer` provides `init_owner()` for constructor use
21
+ /// - `Auth::authorizer()` returns the stored owner address
22
+ /// - `Ownable` provides ownership management (transfer, accept, renounce)
23
+ pub fn generate_ownable_impl(input: TokenStream) -> TokenStream {
24
+ let item_struct: ItemStruct = syn::parse2(input).unwrap_or_else(|e| panic!("failed to parse struct: {}", e));
25
+ let name = &item_struct.ident;
26
+
27
+ quote! {
28
+ #item_struct
29
+
30
+ use utils::{auth::Auth as _, ownable::{Ownable as _, OwnableInitializer as _}};
31
+
32
+ impl utils::ownable::OwnableInitializer for #name {}
33
+
34
+ #[common_macros::contract_impl]
35
+ impl utils::auth::Auth for #name {
36
+ fn authorizer(env: &soroban_sdk::Env) -> Option<soroban_sdk::Address> {
37
+ <Self as utils::ownable::Ownable>::owner(env)
38
+ }
39
+ }
40
+
41
+ #[common_macros::contract_impl(contracttrait)]
42
+ impl utils::ownable::Ownable for #name {}
43
+ }
44
+ }
45
+
46
+ // ============================================================================
47
+ // MultiSig Macro Implementation
48
+ // ============================================================================
49
+
50
+ /// Generates the multisig implementation from the `#[multisig]` attribute macro.
51
+ ///
52
+ /// This macro implements both `Auth` and `MultiSig` traits for the contract:
53
+ /// - `Auth::authorizer()` returns the contract's own address (self-owning pattern)
54
+ /// - `MultiSig` provides signature verification and signer management
55
+ pub fn generate_multisig_impl(input: TokenStream) -> TokenStream {
56
+ let item_struct: ItemStruct = syn::parse2(input).unwrap_or_else(|e| panic!("failed to parse struct: {}", e));
57
+ let name = &item_struct.ident;
58
+
59
+ quote! {
60
+ #item_struct
61
+
62
+ use utils::{auth::Auth as _, multisig::MultiSig as _};
63
+
64
+ #[common_macros::contract_impl]
65
+ impl utils::auth::Auth for #name {
66
+ fn authorizer(env: &soroban_sdk::Env) -> Option<soroban_sdk::Address> {
67
+ Some(env.current_contract_address())
68
+ }
69
+ }
70
+
71
+ #[common_macros::contract_impl(contracttrait)]
72
+ impl utils::multisig::MultiSig for #name {}
73
+ }
74
+ }
75
+
76
+ // ============================================================================
77
+ // Only Auth Macro Implementation
78
+ // ============================================================================
79
+
80
+ /// Prepends an auth check to a method using the `Auth` trait.
81
+ ///
82
+ /// Works with any contract that implements the `Auth` trait, including both
83
+ /// `Ownable` and `MultiSig` contracts.
84
+ pub fn prepend_only_auth_check(input: TokenStream) -> TokenStream {
85
+ let mut input_fn: ItemFn = syn::parse2(input).unwrap_or_else(|e| panic!("failed to parse function: {}", e));
86
+
87
+ let env_param = utils::expect_env_param(&input_fn.sig.inputs);
88
+
89
+ // Get a reference to env (handles both `Env` and `&Env` parameter types)
90
+ let env_ref = env_param.as_ref_tokens();
91
+
92
+ // Insert the auth check at the beginning of the function body
93
+ input_fn.block.stmts.insert(0, parse_quote!(utils::auth::require_auth::<Self>(#env_ref);));
94
+ input_fn.into_token_stream()
95
+ }
@@ -0,0 +1,92 @@
1
+ use crate::utils;
2
+ use proc_macro2::TokenStream;
3
+ use quote::quote;
4
+ use syn::{ImplItem, ItemImpl, ItemTrait, TraitItem, Visibility};
5
+
6
+ /// Generates a `#[soroban_sdk::contractimpl]` with automatic instance TTL extension.
7
+ ///
8
+ /// - For `__constructor` methods: injects `ttl_configurable::init_default_ttl_configs(env)`
9
+ /// - For other methods: injects TTL extension logic to extend instance TTL if configured
10
+ pub fn contractimpl_with_ttl(attr: TokenStream, input: TokenStream) -> TokenStream {
11
+ let mut impl_block: ItemImpl = syn::parse2(input).unwrap_or_else(|e| panic!("failed to parse impl block: {}", e));
12
+
13
+ let is_trait_impl = impl_block.trait_.is_some();
14
+
15
+ for item in &mut impl_block.items {
16
+ let ImplItem::Fn(method) = item else { continue };
17
+
18
+ // For trait impls, process all methods; for inherent impls, only public methods
19
+ if !is_trait_impl && !matches!(method.vis, Visibility::Public(_)) {
20
+ continue;
21
+ }
22
+
23
+ // Skip methods without Env parameter
24
+ let Some(env_param) = utils::find_env_param(&method.sig.inputs) else { continue };
25
+
26
+ if method.sig.ident == "__constructor" {
27
+ // Inject default TTL config initialization in constructor
28
+ method.block.stmts.insert(0, init_default_ttl_configs_stmt(&env_param));
29
+ } else {
30
+ // Inject TTL extension at the start of other methods
31
+ method.block.stmts.insert(0, extend_instance_ttl_stmt(&env_param));
32
+ }
33
+ }
34
+
35
+ let contract_attr = if attr.is_empty() {
36
+ quote! { #[soroban_sdk::contractimpl]}
37
+ } else {
38
+ quote! { #[soroban_sdk::contractimpl(#attr)] }
39
+ };
40
+ quote! {
41
+ #contract_attr
42
+ #impl_block
43
+ }
44
+ }
45
+
46
+ /// Generates a `#[soroban_sdk::contracttrait]` with automatic instance TTL extension.
47
+ ///
48
+ /// This macro processes trait definitions and injects TTL extension logic into
49
+ /// default method implementations that have an `Env` parameter.
50
+ pub fn contracttrait_with_ttl(attr: TokenStream, input: TokenStream) -> TokenStream {
51
+ let mut trait_def: ItemTrait =
52
+ syn::parse2(input).unwrap_or_else(|e| panic!("failed to parse trait definition: {}", e));
53
+
54
+ for item in &mut trait_def.items {
55
+ let TraitItem::Fn(method) = item else { continue };
56
+
57
+ // Only process methods with default implementations (have a body)
58
+ let Some(ref mut block) = method.default else { continue };
59
+
60
+ // Skip methods without Env parameter
61
+ let Some(env_param) = utils::find_env_param(&method.sig.inputs) else { continue };
62
+
63
+ // Inject TTL extension at the start of the method body
64
+ block.stmts.insert(0, extend_instance_ttl_stmt(&env_param));
65
+ }
66
+
67
+ let trait_attr = if attr.is_empty() {
68
+ quote! { #[soroban_sdk::contracttrait]}
69
+ } else {
70
+ quote! { #[soroban_sdk::contracttrait(#attr)] }
71
+ };
72
+ quote! {
73
+ #trait_attr
74
+ #trait_def
75
+ }
76
+ }
77
+
78
+ /// Generates a statement that initializes default TTL configs in the constructor.
79
+ fn init_default_ttl_configs_stmt(env_param: &utils::EnvParam<'_>) -> syn::Stmt {
80
+ let env_ref = env_param.as_ref_tokens();
81
+ syn::parse_quote! {
82
+ utils::ttl_configurable::init_default_ttl_configs(#env_ref);
83
+ }
84
+ }
85
+
86
+ /// Generates a statement that extends instance TTL if configured.
87
+ fn extend_instance_ttl_stmt(env_param: &utils::EnvParam<'_>) -> syn::Stmt {
88
+ let env_ref = env_param.as_ref_tokens();
89
+ syn::parse_quote! {
90
+ utils::ttl_configurable::extend_instance_ttl(#env_ref);
91
+ }
92
+ }
package/src/error.rs ADDED
@@ -0,0 +1,43 @@
1
+ use proc_macro2::TokenStream;
2
+ use quote::quote;
3
+ use syn::{parse_quote, spanned::Spanned, ExprLit, Fields, ItemEnum, Lit, Token};
4
+
5
+ pub fn generate_error(item: TokenStream) -> TokenStream {
6
+ let mut data_enum: ItemEnum = syn::parse2(item).unwrap_or_else(|e| panic!("failed to parse enum: {}", e));
7
+
8
+ // For variants without an explicit discriminant, assign sequential error codes starting at 1
9
+ // (by initializing the counter to 0 and incrementing before assignment).
10
+ let mut current_value = 0;
11
+ for variant in &mut data_enum.variants {
12
+ assert!(matches!(variant.fields, Fields::Unit), "Error enum variants must be unit variants");
13
+
14
+ // Handle variant discriminant assignment
15
+ if let Some((_, disc)) = &variant.discriminant {
16
+ // Explicit discriminant - validate it's greater than previous and update counter
17
+ let val = parse_discriminant_value(disc);
18
+ assert!(val > current_value, "Error enum discriminant must be greater than the previous discriminant");
19
+ current_value = val;
20
+ } else {
21
+ // No discriminant - assign the next sequential value
22
+ current_value += 1;
23
+ variant.discriminant = Some((Token![=](variant.span()), parse_quote!(#current_value)));
24
+ }
25
+ }
26
+
27
+ quote! {
28
+ #[soroban_sdk::contracterror]
29
+ #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
30
+ #[repr(u32)]
31
+ #data_enum
32
+ }
33
+ }
34
+
35
+ /// Parses a discriminant value from a variant, returning the u32 value if valid.
36
+ /// Panics if the discriminant is not a valid integer literal.
37
+ fn parse_discriminant_value(disc: &syn::Expr) -> u32 {
38
+ if let syn::Expr::Lit(ExprLit { lit: Lit::Int(lit_int), .. }) = disc {
39
+ lit_int.base10_parse().expect("Error enum discriminant must be a valid u32 integer")
40
+ } else {
41
+ panic!("Error enum discriminant must be an integer literal")
42
+ }
43
+ }