@layerzerolabs/framework-solana-anchor-trait 0.2.98 → 0.2.100

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.
package/README.md CHANGED
@@ -33,7 +33,7 @@ InstructionSpec {
33
33
  }
34
34
  ```
35
35
 
36
- **Provided** — The trait supplies a complete default implementation, including an accounts struct and a handler function. The default accounts struct is always emitted, so it remains available even when the implementor overrides the handler. This is analogous to a default method in a Rust trait.
36
+ **Provided** — The trait supplies a complete default implementation: an accounts struct and a handler `impl`. Both are emitted unconditionally. This is analogous to a default method in a Rust trait.
37
37
 
38
38
  ```rust
39
39
  InstructionSpec {
@@ -54,7 +54,7 @@ For provided instructions, the implementor may accept the default or supply a cu
54
54
  - To **reuse** the default accounts struct, declare `Context<StdSetPeer>`.
55
55
  - To use **custom** accounts, declare `Context<MyCustomAccounts>`.
56
56
 
57
- The framework does not distinguish between these cases — the default struct is always emitted regardless.
57
+ The framework does not distinguish between these cases — both the default struct and its `apply` impl are always emitted, so an override handler can reuse the default by calling `<accounts>::apply(ctx, params)?`. For example, with `Context<StdSetPeer>` the override can run `StdSetPeer::apply(ctx, params)?` before or after its own logic.
58
58
 
59
59
  For required instructions, no default exists. The implementor must provide a handler.
60
60
 
@@ -202,22 +202,26 @@ pub fn my_macro_impl(attr: TokenStream, item: TokenStream) -> syn::Result<TokenS
202
202
  ### Assembly output structure
203
203
 
204
204
  ```text
205
- <prelude> — domain-specific definitions (emitted by the macro crate)
206
- <wrapper_defs> — relocated override handler modules
207
- <default_impls> — generated default accounts structs + handler impls
208
- <extend_macro> — macro_rules! for extending default accounts (if configured)
209
- <original_mod_attrs> — preserved module attributes
210
- <extra_mod_attrs> — additional attributes (e.g., #[program])
205
+ <prelude> — domain-specific definitions (emitted by the macro crate)
206
+ <default_impls> — generated default accounts structs + handler impls
207
+ <extend_macro> — macro_rules! for extending default accounts (if configured)
208
+ <original_outer_attrs> — preserved outer module attributes
209
+ <extra_mod_attrs> — additional attributes (e.g., #[program])
210
+ — any outer attr whose last path segment is `program`
211
+ is stripped from the input and re-emitted here
211
212
  [vis] mod <name> {
213
+ <original_inner_attrs> — preserved inner module attributes
212
214
  use super::*;
213
- <filtered_items> — original module items, excluding relocated handlers
214
- <entrypoints> — generated instruction entrypoints
215
+ <filtered_items> — original module items, excluding relocated handlers
216
+ <wrapper_defs> — relocated override handler modules (kept inside so handlers
217
+ can reach the user's nested same-module helpers)
218
+ <entrypoints> — generated instruction entrypoints
215
219
  }
216
220
  ```
217
221
 
218
222
  ## Extending Default Accounts
219
223
 
220
- For provided instructions, the framework generates a `macro_rules!` (named via `Assembly::extend_macro_name`) that lets users create extended accounts structs — the default fields plus extra accounts — without duplicating the base implementation.
224
+ For provided instructions, the framework generates a `macro_rules!` (named via `Assembly::extend_macro_name`) that lets users create extended accounts structs — the default fields plus extra accounts — without duplicating the base implementation. **Extension is additive**: the default struct and its `apply` impl remain emitted alongside any extended struct (see [How it works](#how-it-works)). For example, defining `ExtSetPeer` does not replace or hide `StdSetPeer`.
221
225
 
222
226
  ### Usage
223
227
 
@@ -243,7 +247,7 @@ my_domain_extend_accounts!(set_peer, ExtSetPeer, {
243
247
 
244
248
  ### How it works
245
249
 
246
- Each provided instruction's `DefaultAccounts` produces two outputs: the concrete `Std*` struct (via `to_definition()`, which emits the `ItemStruct` and `ItemImpl` as-is) and a `macro_rules!` arm (via `to_extend_rule()`, which decomposes the struct into attrs/fields, replaces the name with `$name`, and appends `$($extra)*`). `Assembly` collects all arms into one `macro_rules!` named by `extend_macro_name`.
250
+ Each provided instruction's `DefaultAccounts` produces two outputs: the concrete `Std*` struct (via `to_definition()`, which emits the `ItemStruct` and `ItemImpl` as-is) and a `macro_rules!` arm (via `to_extend_rule()`, which decomposes the struct into attrs/fields, replaces the name with `$name`, and appends `$($extra)*`). The arm also re-emits the default's `impl` items, so the extended struct gets an independent copy of `apply` — e.g., `ExtSetPeer::apply` is a parallel method, not a re-export of `StdSetPeer::apply`. `Assembly` collects all arms into one `macro_rules!` named by `extend_macro_name`.
247
251
 
248
252
  ## Reference
249
253
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@layerzerolabs/framework-solana-anchor-trait",
3
- "version": "0.2.98",
3
+ "version": "0.2.100",
4
4
  "private": false,
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -14,10 +14,10 @@
14
14
  "rustfmt.toml"
15
15
  ],
16
16
  "devDependencies": {
17
- "@layerzerolabs/build-utils-solana": "0.2.98",
18
- "@layerzerolabs/tsup-configuration": "0.2.98",
19
- "@layerzerolabs/vm-tooling-solana": "0.2.98",
20
- "@layerzerolabs/typescript-configuration": "0.2.98"
17
+ "@layerzerolabs/build-utils-solana": "0.2.100",
18
+ "@layerzerolabs/typescript-configuration": "0.2.100",
19
+ "@layerzerolabs/tsup-configuration": "0.2.100",
20
+ "@layerzerolabs/vm-tooling-solana": "0.2.100"
21
21
  },
22
22
  "publishConfig": {
23
23
  "access": "restricted",
package/src/assembly.rs CHANGED
@@ -28,9 +28,10 @@ impl<Ix: InstructionSet> Assembly<Ix> {
28
28
  ///
29
29
  /// `#[program]` is always emitted as the innermost attribute on the module so
30
30
  /// that Anchor expands last — after all instruction macros have injected their
31
- /// code. If `#[program]` already appears on `source_mod` (placed by a
32
- /// previously-expanded macro), it is filtered out and re-emitted at the correct
33
- /// position. This makes stacking order irrelevant for any combination of
31
+ /// code. Any outer module attribute whose last path segment is `program` is
32
+ /// stripped and re-emitted at the correct position; this catches qualified
33
+ /// forms like `#[anchor_lang::program]` and leaves inner `#![…program]`
34
+ /// untouched. This keeps stacking order irrelevant for any combination of
34
35
  /// domain macros (e.g. `#[oapp]` + `#[composer]`).
35
36
  ///
36
37
  /// ```text
@@ -75,11 +76,14 @@ impl<Ix: InstructionSet> Assembly<Ix> {
75
76
 
76
77
  let ItemMod { attrs: mod_attrs, vis: mod_vis, ident: mod_ident, .. } = &source_mod;
77
78
 
78
- // Inner attrs (`#![...]`, `//!`) must live inside the body — rustc rejects them outside.
79
- let (inner_attrs, outer_attrs): (Vec<_>, Vec<_>) = mod_attrs
80
- .iter()
79
+ // Partition first: only outer `#[...program]` attrs are framework-owned and re-emitted
80
+ // below. Inner attrs (`#![...]`, `//!`) must stay inside the module body unchanged.
81
+ let (inner_attrs, outer_attrs): (Vec<_>, Vec<_>) =
82
+ mod_attrs.iter().partition(|a| matches!(a.style, AttrStyle::Inner(_)));
83
+ let outer_attrs: Vec<_> = outer_attrs
84
+ .into_iter()
81
85
  .filter(|a| a.path().segments.last().is_none_or(|s| s.ident != "program"))
82
- .partition(|a| matches!(a.style, AttrStyle::Inner(_)));
86
+ .collect();
83
87
 
84
88
  quote! {
85
89
  #prelude
package/src/generation.rs CHANGED
@@ -40,16 +40,13 @@ pub struct DefaultImpl<Ctx> {
40
40
 
41
41
  /// Complete accounts struct and its handler impl block.
42
42
  ///
43
- /// The framework uses this template to produce two outputs:
44
- /// 1. The concrete struct + impl — emitted as-is via [`to_definition`](Self::to_definition).
45
- /// 2. A `macro_rules!` rule — built by decomposing the struct into attrs/fields and replacing the
46
- /// name with `$name` via [`to_extend_rule`](Self::to_extend_rule).
43
+ /// Produces two outputs: the concrete struct + impl via [`to_definition`](Self::to_definition),
44
+ /// and a `macro_rules!` rule via [`to_extend_rule`](Self::to_extend_rule) (which reconstructs
45
+ /// the type as `pub struct $name<'info>` / `impl $name<'_>`).
47
46
  ///
48
- /// The struct may carry at most a single `'info` lifetime parameter. Extra
49
- /// type or const generics are rejected at expansion time — the entrypoint
50
- /// and extend-rule codegen paths reduce the type to a bare ident, so any
51
- /// non-`'info` generics would be silently dropped and produce malformed
52
- /// output.
47
+ /// **Constraint:** `struct_type` must carry exactly `'info` — no type or const generics.
48
+ /// Extra generics are silently dropped by both codegen paths, surfacing as type errors at
49
+ /// the extend-macro call site.
53
50
  pub struct DefaultAccounts {
54
51
  /// Complete struct definition including attributes, name, generics, and fields.
55
52
  pub struct_type: ItemStruct,
package/src/overrides.rs CHANGED
@@ -110,8 +110,15 @@ impl<Ix: InstructionSet> InstructionOverrides<Ix> {
110
110
  quote!(#(#kept)*)
111
111
  }
112
112
 
113
- /// Relocates a handler function into an isolated wrapper module
114
- /// and returns the corresponding [`HandlerMeta`].
113
+ /// Wraps a handler fn in a generated module
114
+ /// (`mod __<attr>__<instr>_handler { use super::*; ... }`) and returns
115
+ /// its [`HandlerMeta`]. The override attribute (e.g. `#[oapp_instruction]`)
116
+ /// is stripped; other attributes ride along on the cloned fn.
117
+ ///
118
+ /// Each attribute expands inside the wrapper module rather than at the
119
+ /// fn's original location, so any code it generates is inserted there
120
+ /// and resolves names against the wrapper's scope (which reaches the
121
+ /// parent via `use super::*;`).
115
122
  fn wrap_handler(&mut self, target: Ix, fn_item: &ItemFn) -> syn::Result<HandlerMeta> {
116
123
  let context_type = infer_context_type(fn_item)?;
117
124
  let fn_generics = fn_item.sig.generics.clone();