@layerzerolabs/framework-solana-anchor-trait 0.2.60

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 (34) hide show
  1. package/Cargo.toml +18 -0
  2. package/examples/shapes/Anchor.toml +19 -0
  3. package/examples/shapes/macros/Cargo.toml +17 -0
  4. package/examples/shapes/macros/src/instructions/compute_area.rs +50 -0
  5. package/examples/shapes/macros/src/instructions/compute_perimeter.rs +50 -0
  6. package/examples/shapes/macros/src/instructions/get_dimension_count.rs +44 -0
  7. package/examples/shapes/macros/src/instructions/get_name.rs +18 -0
  8. package/examples/shapes/macros/src/instructions/initialize.rs +18 -0
  9. package/examples/shapes/macros/src/instructions/mod.rs +26 -0
  10. package/examples/shapes/macros/src/lib.rs +50 -0
  11. package/examples/shapes/macros/src/shapes.rs +56 -0
  12. package/examples/shapes/programs/ellipse/Cargo.toml +30 -0
  13. package/examples/shapes/programs/ellipse/src/lib.rs +88 -0
  14. package/examples/shapes/programs/ellipse/src/state.rs +9 -0
  15. package/examples/shapes/programs/triangle/Cargo.toml +30 -0
  16. package/examples/shapes/programs/triangle/src/lib.rs +83 -0
  17. package/examples/shapes/programs/triangle/src/state.rs +9 -0
  18. package/package.json +36 -0
  19. package/rust-toolchain.toml +3 -0
  20. package/rustfmt.toml +23 -0
  21. package/src/assembly.rs +63 -0
  22. package/src/generation.rs +226 -0
  23. package/src/lib.rs +126 -0
  24. package/src/overrides.rs +289 -0
  25. package/tests/assembly.rs +339 -0
  26. package/tests/common/mod.rs +206 -0
  27. package/tests/declare_instructions.rs +206 -0
  28. package/tests/generation.rs +358 -0
  29. package/tests/override.rs +414 -0
  30. package/tests/snapshots/snapshots__tests__snapshot_all_defaults.snap +35 -0
  31. package/tests/snapshots/snapshots__tests__snapshot_full_override.snap +47 -0
  32. package/tests/snapshots/snapshots__tests__snapshot_handler_override.snap +42 -0
  33. package/tests/snapshots/snapshots__tests__snapshot_mixed_overrides.snap +57 -0
  34. package/tests/snapshots.rs +139 -0
package/Cargo.toml ADDED
@@ -0,0 +1,18 @@
1
+ [package]
2
+ name = "anchor-trait"
3
+ version = "0.1.0"
4
+ edition = "2021"
5
+ description = "Trait-like instruction interfaces for Anchor Solana programs with compile-time enforcement and default implementations"
6
+
7
+ [features]
8
+ idl-build = []
9
+
10
+ [dependencies]
11
+ itertools = "0.14"
12
+ proc-macro2 = "1.0"
13
+ quote = "1.0"
14
+ syn = { version = "2.0", features = ["full", "parsing"] }
15
+
16
+ [dev-dependencies]
17
+ insta = "1.41"
18
+ prettyplease = "0.2"
@@ -0,0 +1,19 @@
1
+ [toolchain]
2
+ anchor_version = "0.32.1"
3
+ solana_version = "2.3.0"
4
+
5
+ [features]
6
+ seeds = false
7
+ skip-lint = false
8
+ resolution = false
9
+
10
+ [programs.localnet]
11
+ shapes_triangle = "Tri1111111111111111111111111111111111111111"
12
+ shapes_ellipse = "ELp1111111111111111111111111111111111111111"
13
+
14
+ [registry]
15
+ url = "https://api.apr.dev"
16
+
17
+ [provider]
18
+ cluster = "Localnet"
19
+ wallet = "~/.config/solana/id.json"
@@ -0,0 +1,17 @@
1
+ [package]
2
+ name = "shapes-macros"
3
+ version = "0.1.0"
4
+ edition = "2021"
5
+
6
+ [lib]
7
+ proc-macro = true
8
+
9
+ [features]
10
+ idl-build = ["anchor-trait/idl-build"]
11
+
12
+ [dependencies]
13
+ anchor-trait = { workspace = true }
14
+ syn = { version = "2.0", features = ["full", "parsing"] }
15
+ quote = "1.0"
16
+ proc-macro2 = "1.0"
17
+ darling = "0.20"
@@ -0,0 +1,50 @@
1
+ //! Code generator for the `compute_area` instruction.
2
+ //!
3
+ //! Provided instruction with default handler: width * height (rectangle area).
4
+
5
+ use anchor_trait::{DefaultImpl, InstructionSpec};
6
+ use proc_macro2::TokenStream;
7
+ use quote::{format_ident, quote};
8
+
9
+ use super::CodegenContext;
10
+
11
+ pub(super) fn spec(_ctx: &CodegenContext) -> InstructionSpec<CodegenContext> {
12
+ InstructionSpec {
13
+ params_type: Some(quote!(ComputeParams)),
14
+ return_type: quote!(u64),
15
+ is_view: true,
16
+ default_impl: Some(DefaultImpl {
17
+ accounts_ident: format_ident!("StdComputeArea"),
18
+ handler_ident: format_ident!("apply"),
19
+ generator: generate_default_context,
20
+ }),
21
+ }
22
+ }
23
+
24
+ fn generate_default_context(
25
+ ctx: &CodegenContext,
26
+ spec: &InstructionSpec<CodegenContext>,
27
+ ) -> TokenStream {
28
+ let state_type = &ctx.state_type;
29
+
30
+ let params_type = spec.params_type.as_ref().unwrap();
31
+ let return_type = &spec.return_type;
32
+ let default_impl = spec.default_impl.as_ref().unwrap();
33
+
34
+ let default_accounts_ident = &default_impl.accounts_ident;
35
+ let handler_ident = &default_impl.handler_ident;
36
+
37
+ quote! {
38
+ #[derive(Accounts)]
39
+ pub struct #default_accounts_ident<'info> {
40
+ pub shape: Account<'info, #state_type>,
41
+ }
42
+
43
+ impl #default_accounts_ident<'_> {
44
+ pub fn #handler_ident(ctx: &Context<Self>, _params: &#params_type) -> Result<#return_type> {
45
+ let s = &ctx.accounts.shape;
46
+ Ok(s.width * s.height)
47
+ }
48
+ }
49
+ }
50
+ }
@@ -0,0 +1,50 @@
1
+ //! Code generator for the `compute_perimeter` instruction.
2
+ //!
3
+ //! Provided instruction with default handler: 2 * (width + height) (rectangle perimeter).
4
+
5
+ use anchor_trait::{DefaultImpl, InstructionSpec};
6
+ use proc_macro2::TokenStream;
7
+ use quote::{format_ident, quote};
8
+
9
+ use super::CodegenContext;
10
+
11
+ pub(super) fn spec(_ctx: &CodegenContext) -> InstructionSpec<CodegenContext> {
12
+ InstructionSpec {
13
+ params_type: Some(quote!(ComputeParams)),
14
+ return_type: quote!(u64),
15
+ is_view: true,
16
+ default_impl: Some(DefaultImpl {
17
+ accounts_ident: format_ident!("StdComputePerimeter"),
18
+ handler_ident: format_ident!("apply"),
19
+ generator: generate_default_context,
20
+ }),
21
+ }
22
+ }
23
+
24
+ fn generate_default_context(
25
+ ctx: &CodegenContext,
26
+ spec: &InstructionSpec<CodegenContext>,
27
+ ) -> TokenStream {
28
+ let state_type = &ctx.state_type;
29
+
30
+ let params_type = spec.params_type.as_ref().unwrap();
31
+ let return_type = &spec.return_type;
32
+ let default_impl = spec.default_impl.as_ref().unwrap();
33
+
34
+ let default_accounts_ident = &default_impl.accounts_ident;
35
+ let handler_ident = &default_impl.handler_ident;
36
+
37
+ quote! {
38
+ #[derive(Accounts)]
39
+ pub struct #default_accounts_ident<'info> {
40
+ pub shape: Account<'info, #state_type>,
41
+ }
42
+
43
+ impl #default_accounts_ident<'_> {
44
+ pub fn #handler_ident(ctx: &Context<Self>, _params: &#params_type) -> Result<#return_type> {
45
+ let s = &ctx.accounts.shape;
46
+ Ok(2 * (s.width + s.height))
47
+ }
48
+ }
49
+ }
50
+ }
@@ -0,0 +1,44 @@
1
+ //! Code generator for the `get_dimension_count` instruction.
2
+ //!
3
+ //! Provided instruction with no params. Default returns 2 (all shapes are 2D).
4
+
5
+ use anchor_trait::{DefaultImpl, InstructionSpec};
6
+ use proc_macro2::TokenStream;
7
+ use quote::{format_ident, quote};
8
+
9
+ use super::CodegenContext;
10
+
11
+ pub(super) fn spec(_ctx: &CodegenContext) -> InstructionSpec<CodegenContext> {
12
+ InstructionSpec {
13
+ params_type: None,
14
+ return_type: quote!(u8),
15
+ is_view: true,
16
+ default_impl: Some(DefaultImpl {
17
+ accounts_ident: format_ident!("StdGetDimensionCount"),
18
+ handler_ident: format_ident!("apply"),
19
+ generator: generate_default_context,
20
+ }),
21
+ }
22
+ }
23
+
24
+ fn generate_default_context(
25
+ _ctx: &CodegenContext,
26
+ spec: &InstructionSpec<CodegenContext>,
27
+ ) -> TokenStream {
28
+ let return_type = &spec.return_type;
29
+ let default_impl = spec.default_impl.as_ref().unwrap();
30
+
31
+ let default_accounts_ident = &default_impl.accounts_ident;
32
+ let handler_ident = &default_impl.handler_ident;
33
+
34
+ quote! {
35
+ #[derive(Accounts)]
36
+ pub struct #default_accounts_ident {}
37
+
38
+ impl #default_accounts_ident {
39
+ pub fn #handler_ident(_ctx: &Context<Self>) -> Result<#return_type> {
40
+ Ok(2) // All shapes in this domain are 2D
41
+ }
42
+ }
43
+ }
44
+ }
@@ -0,0 +1,18 @@
1
+ //! Code generator for the `get_name` instruction.
2
+ //!
3
+ //! This is a **required** instruction — the implementor must return the shape's
4
+ //! name. There is no sensible default, so every consumer must provide a handler.
5
+
6
+ use anchor_trait::InstructionSpec;
7
+ use quote::quote;
8
+
9
+ use super::CodegenContext;
10
+
11
+ pub(super) fn spec() -> InstructionSpec<CodegenContext> {
12
+ InstructionSpec {
13
+ params_type: None,
14
+ return_type: quote!(String),
15
+ is_view: true,
16
+ default_impl: None, // Required — implementor MUST provide
17
+ }
18
+ }
@@ -0,0 +1,18 @@
1
+ //! Code generator for the `initialize` instruction.
2
+ //!
3
+ //! This is a **required** instruction — the implementor must provide their own
4
+ //! handler and accounts struct. There is no default implementation.
5
+
6
+ use anchor_trait::InstructionSpec;
7
+ use quote::quote;
8
+
9
+ use super::CodegenContext;
10
+
11
+ pub(super) fn spec() -> InstructionSpec<CodegenContext> {
12
+ InstructionSpec {
13
+ params_type: Some(quote!(InitParams)),
14
+ return_type: quote!(()),
15
+ is_view: false,
16
+ default_impl: None, // Required — implementor MUST provide
17
+ }
18
+ }
@@ -0,0 +1,26 @@
1
+ //! Shapes instruction definitions and code generation.
2
+
3
+ mod compute_area;
4
+ mod compute_perimeter;
5
+ mod get_dimension_count;
6
+ mod get_name;
7
+ mod initialize;
8
+
9
+ use anchor_trait::declare_instructions;
10
+ use proc_macro2::TokenStream;
11
+
12
+ /// Domain-specific context carrying the resolved state type for shapes code generation.
13
+ pub(crate) struct CodegenContext {
14
+ pub state_type: TokenStream,
15
+ }
16
+
17
+ declare_instructions! {
18
+ name = ShapesInstructionSet;
19
+ context = CodegenContext;
20
+
21
+ Initialize => initialize,
22
+ GetName => get_name,
23
+ ComputeArea => compute_area(ctx),
24
+ ComputePerimeter => compute_perimeter(ctx),
25
+ GetDimensionCount => get_dimension_count(ctx),
26
+ }
@@ -0,0 +1,50 @@
1
+ //! Procedural macros for the Shapes example program.
2
+ //!
3
+ //! Provides `#[shapes]` and `#[shapes_instruction]` attribute macros
4
+ //! demonstrating the anchor-trait instruction trait system.
5
+
6
+ mod instructions;
7
+ mod shapes;
8
+
9
+ use proc_macro::TokenStream;
10
+
11
+ /// Program-level macro that auto-generates shape instruction entrypoints.
12
+ ///
13
+ /// ## Usage
14
+ ///
15
+ /// ```ignore
16
+ /// #[shapes_macros::shapes(state = ShapeState)]
17
+ /// pub mod shapes_program {
18
+ /// use super::*;
19
+ ///
20
+ /// // Required: must provide initialize (no default)
21
+ /// pub fn initialize(ctx: Context<Initialize>, params: InitParams) -> Result<()> { ... }
22
+ ///
23
+ /// // Optional overrides for compute_area, compute_perimeter, get_dimension_count
24
+ /// }
25
+ /// ```
26
+ ///
27
+ /// ## Attributes
28
+ ///
29
+ /// - `state = TypeName`: The program state account type (required)
30
+ #[proc_macro_attribute]
31
+ pub fn shapes(attr: TokenStream, item: TokenStream) -> TokenStream {
32
+ shapes::expand(attr.into(), item.into())
33
+ .unwrap_or_else(|err| err.to_compile_error())
34
+ .into()
35
+ }
36
+
37
+ /// Marks a function as an override for a shapes instruction.
38
+ ///
39
+ /// ## Usage
40
+ ///
41
+ /// ```ignore
42
+ /// #[shapes_instruction(compute_area)]
43
+ /// pub fn triangle_area(ctx: Context<StdComputeArea>, _params: ComputeParams) -> Result<u64> {
44
+ /// // custom logic
45
+ /// }
46
+ /// ```
47
+ #[proc_macro_attribute]
48
+ pub fn shapes_instruction(_attr: TokenStream, item: TokenStream) -> TokenStream {
49
+ item
50
+ }
@@ -0,0 +1,56 @@
1
+ //! Top-level `#[shapes]` attribute macro implementation.
2
+ //!
3
+ //! Flow: parse inputs and resolve domain types, then collect -> generate -> assemble.
4
+
5
+ use crate::instructions::{CodegenContext, ShapesInstructionSet};
6
+ use anchor_trait::{generate_instructions, Assembly, InstructionOverrides, OverrideConfig};
7
+ use darling::{ast::NestedMeta, FromMeta};
8
+ use proc_macro2::TokenStream;
9
+ use quote::quote;
10
+ use syn::{ItemMod, Path};
11
+
12
+ // ---------------------------------------------------------------------------
13
+ // Attribute parsing
14
+ // ---------------------------------------------------------------------------
15
+
16
+ #[derive(FromMeta)]
17
+ struct ShapesAttr {
18
+ state: Path,
19
+ }
20
+
21
+ const OVERRIDE_CONFIG: OverrideConfig = OverrideConfig {
22
+ attr_name: "shapes_instruction",
23
+ domain: "Shapes",
24
+ };
25
+
26
+ // ---------------------------------------------------------------------------
27
+ // Entry point
28
+ // ---------------------------------------------------------------------------
29
+
30
+ pub fn expand(attr: TokenStream, item: TokenStream) -> syn::Result<TokenStream> {
31
+ // Parse inputs
32
+ let attr_args = NestedMeta::parse_meta_list(attr)?;
33
+ let attrs = ShapesAttr::from_list(&attr_args)?;
34
+ let module = syn::parse2::<ItemMod>(item)?;
35
+
36
+ let state = &attrs.state;
37
+
38
+ // 1. Collect overrides
39
+ let overrides = InstructionOverrides::collect(&module, OVERRIDE_CONFIG)?;
40
+
41
+ // 2. Generate instructions
42
+ let codegen_ctx = CodegenContext {
43
+ state_type: quote!(#state),
44
+ };
45
+ let instructions = generate_instructions::<ShapesInstructionSet>(&codegen_ctx, &overrides)?;
46
+
47
+ // 3. Assemble output
48
+ Ok(Assembly {
49
+ prelude: TokenStream::new(),
50
+ overrides,
51
+ instructions,
52
+ extra_mod_attrs: quote!(#[program]),
53
+ source_mod: module,
54
+ }
55
+ .assemble())
56
+ }
@@ -0,0 +1,30 @@
1
+ [package]
2
+ name = "shapes-ellipse"
3
+ version = "0.0.1"
4
+ description = "Ellipse shapes example implementation for anchor-trait"
5
+ edition = "2021"
6
+
7
+ [lib]
8
+ crate-type = ["cdylib", "lib"]
9
+ name = "shapes_ellipse"
10
+
11
+ [features]
12
+ no-entrypoint = []
13
+ no-idl = []
14
+ no-log-ix-name = []
15
+ cpi = ["no-entrypoint"]
16
+ default = []
17
+ idl-build = ["anchor-lang/idl-build", "shapes-macros/idl-build"]
18
+ custom-heap = []
19
+ custom-panic = []
20
+ anchor-debug = []
21
+
22
+ [lints.rust]
23
+ unexpected_cfgs = { level = "allow", check-cfg = [
24
+ "cfg(feature, values(\"anchor-debug\"))",
25
+ "cfg(target_os, values(\"solana\"))",
26
+ ] }
27
+
28
+ [dependencies]
29
+ anchor-lang = { workspace = true }
30
+ shapes-macros = { workspace = true }
@@ -0,0 +1,88 @@
1
+ pub mod state;
2
+
3
+ use anchor_lang::prelude::*;
4
+ use state::ShapeState;
5
+
6
+ declare_id!("ELp1111111111111111111111111111111111111111");
7
+
8
+ // --- Account structs ---
9
+
10
+ #[derive(Accounts)]
11
+ #[instruction(params: InitParams)]
12
+ pub struct Initialize<'info> {
13
+ #[account(mut)]
14
+ pub payer: Signer<'info>,
15
+ #[account(
16
+ init,
17
+ payer = payer,
18
+ space = 8 + ShapeState::INIT_SPACE,
19
+ seeds = [b"shape"],
20
+ bump,
21
+ )]
22
+ pub shape: Account<'info, ShapeState>,
23
+ pub system_program: Program<'info, System>,
24
+ }
25
+
26
+ #[derive(Accounts)]
27
+ pub struct GetName<'info> {
28
+ pub shape: Account<'info, ShapeState>,
29
+ }
30
+
31
+ /// Full Override accounts — ellipse perimeter reads state differently.
32
+ #[derive(Accounts)]
33
+ pub struct EllipseAccounts<'info> {
34
+ pub shape: Account<'info, ShapeState>,
35
+ }
36
+
37
+ // --- Param types ---
38
+
39
+ #[derive(Clone, AnchorSerialize, AnchorDeserialize)]
40
+ pub struct InitParams {
41
+ pub width: u64,
42
+ pub height: u64,
43
+ }
44
+
45
+ #[derive(Clone, AnchorSerialize, AnchorDeserialize)]
46
+ pub struct ComputeParams {}
47
+
48
+ // --- Program module ---
49
+ // Required: initialize, get_name
50
+ // Handler Override: compute_area (π*w*h/4)
51
+ // Full Override: compute_perimeter (custom EllipseAccounts)
52
+ // Default: get_dimension_count
53
+
54
+ #[shapes_macros::shapes(state = ShapeState)]
55
+ pub mod shapes_ellipse {
56
+
57
+ #[allow(unused_mut)]
58
+ #[shapes_instruction]
59
+ pub fn initialize(mut ctx: Context<Initialize>, params: InitParams) -> Result<()> {
60
+ ctx.accounts.shape.width = params.width;
61
+ ctx.accounts.shape.height = params.height;
62
+ ctx.accounts.shape.bump = ctx.bumps.shape;
63
+ Ok(())
64
+ }
65
+
66
+ #[shapes_instruction]
67
+ pub fn get_name(_ctx: Context<GetName>) -> Result<String> {
68
+ Ok("Ellipse".to_string())
69
+ }
70
+
71
+ // HANDLER OVERRIDE — reuses StdComputeArea accounts, ellipse formula.
72
+ #[shapes_instruction]
73
+ pub fn compute_area(ctx: Context<StdComputeArea>, _params: ComputeParams) -> Result<u64> {
74
+ let s = &ctx.accounts.shape;
75
+ // Ellipse area = π * a * b where a=w/2, b=h/2 → π * w * h / 4
76
+ Ok(314_159 * s.width * s.height / 400_000)
77
+ }
78
+
79
+ // FULL OVERRIDE — custom EllipseAccounts, ellipse perimeter approximation.
80
+ #[shapes_instruction]
81
+ pub fn compute_perimeter(ctx: Context<EllipseAccounts>, _params: ComputeParams) -> Result<u64> {
82
+ let s = &ctx.accounts.shape;
83
+ // Simple approximation: π * (w + h) / 2
84
+ Ok(314_159 * (s.width + s.height) / 200_000)
85
+ }
86
+
87
+ // DEFAULT — get_dimension_count returns 2, correct for any 2D shape.
88
+ }
@@ -0,0 +1,9 @@
1
+ use anchor_lang::prelude::*;
2
+
3
+ #[account]
4
+ #[derive(InitSpace)]
5
+ pub struct ShapeState {
6
+ pub width: u64,
7
+ pub height: u64,
8
+ pub bump: u8,
9
+ }
@@ -0,0 +1,30 @@
1
+ [package]
2
+ name = "shapes-triangle"
3
+ version = "0.0.1"
4
+ description = "Triangle shapes example implementation for anchor-trait"
5
+ edition = "2021"
6
+
7
+ [lib]
8
+ crate-type = ["cdylib", "lib"]
9
+ name = "shapes_triangle"
10
+
11
+ [features]
12
+ no-entrypoint = []
13
+ no-idl = []
14
+ no-log-ix-name = []
15
+ cpi = ["no-entrypoint"]
16
+ default = []
17
+ idl-build = ["anchor-lang/idl-build", "shapes-macros/idl-build"]
18
+ custom-heap = []
19
+ custom-panic = []
20
+ anchor-debug = []
21
+
22
+ [lints.rust]
23
+ unexpected_cfgs = { level = "allow", check-cfg = [
24
+ "cfg(feature, values(\"anchor-debug\"))",
25
+ "cfg(target_os, values(\"solana\"))",
26
+ ] }
27
+
28
+ [dependencies]
29
+ anchor-lang = { workspace = true }
30
+ shapes-macros = { workspace = true }
@@ -0,0 +1,83 @@
1
+ pub mod state;
2
+
3
+ use anchor_lang::prelude::*;
4
+ use state::ShapeState;
5
+
6
+ declare_id!("Tri1111111111111111111111111111111111111111");
7
+
8
+ // --- Account structs ---
9
+
10
+ #[derive(Accounts)]
11
+ #[instruction(params: InitParams)]
12
+ pub struct Initialize<'info> {
13
+ #[account(mut)]
14
+ pub payer: Signer<'info>,
15
+ #[account(
16
+ init,
17
+ payer = payer,
18
+ space = 8 + ShapeState::INIT_SPACE,
19
+ seeds = [b"shape"],
20
+ bump,
21
+ )]
22
+ pub shape: Account<'info, ShapeState>,
23
+ pub system_program: Program<'info, System>,
24
+ }
25
+
26
+ #[derive(Accounts)]
27
+ pub struct GetName<'info> {
28
+ pub shape: Account<'info, ShapeState>,
29
+ }
30
+
31
+ // --- Param types ---
32
+
33
+ #[derive(Clone, AnchorSerialize, AnchorDeserialize)]
34
+ pub struct InitParams {
35
+ pub width: u64,
36
+ pub height: u64,
37
+ }
38
+
39
+ #[derive(Clone, AnchorSerialize, AnchorDeserialize)]
40
+ pub struct ComputeParams {}
41
+
42
+ // --- Program module ---
43
+ // Required: initialize, get_name
44
+ // Handler Override: compute_area (triangle formula), compute_perimeter (isoceles)
45
+ // Default: get_dimension_count
46
+
47
+ #[shapes_macros::shapes(state = ShapeState)]
48
+ pub mod shapes_triangle {
49
+
50
+ #[shapes_instruction]
51
+ #[allow(unused_mut)]
52
+ pub fn initialize(mut ctx: Context<Initialize>, params: InitParams) -> Result<()> {
53
+ ctx.accounts.shape.width = params.width;
54
+ ctx.accounts.shape.height = params.height;
55
+ ctx.accounts.shape.bump = ctx.bumps.shape;
56
+ Ok(())
57
+ }
58
+
59
+ #[shapes_instruction]
60
+ pub fn get_name(_ctx: Context<GetName>) -> Result<String> {
61
+ Ok("Triangle".to_string())
62
+ }
63
+
64
+ // HANDLER OVERRIDE — reuses StdComputeArea accounts, triangle formula.
65
+ #[shapes_instruction]
66
+ pub fn compute_area(ctx: Context<StdComputeArea>, _params: ComputeParams) -> Result<u64> {
67
+ let s = &ctx.accounts.shape;
68
+ Ok(s.width * s.height / 2)
69
+ }
70
+
71
+ // HANDLER OVERRIDE — reuses StdComputePerimeter accounts, isoceles triangle.
72
+ #[shapes_instruction]
73
+ pub fn compute_perimeter(
74
+ ctx: Context<StdComputePerimeter>,
75
+ _params: ComputeParams,
76
+ ) -> Result<u64> {
77
+ let s = &ctx.accounts.shape;
78
+ // Isoceles: two equal sides (width) + base (height)
79
+ Ok(s.width + s.width + s.height)
80
+ }
81
+
82
+ // DEFAULT — get_dimension_count returns 2, correct for any 2D shape.
83
+ }
@@ -0,0 +1,9 @@
1
+ use anchor_lang::prelude::*;
2
+
3
+ #[account]
4
+ #[derive(InitSpace)]
5
+ pub struct ShapeState {
6
+ pub width: u64,
7
+ pub height: u64,
8
+ pub bump: u8,
9
+ }
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "@layerzerolabs/framework-solana-anchor-trait",
3
+ "version": "0.2.60",
4
+ "private": false,
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "files": [
8
+ "src",
9
+ "tests",
10
+ "examples",
11
+ "reference",
12
+ "Cargo.toml",
13
+ "rust-toolchain.toml",
14
+ "rustfmt.toml",
15
+ "SPEC.md"
16
+ ],
17
+ "devDependencies": {
18
+ "@layerzerolabs/build-utils-solana": "0.2.60",
19
+ "@layerzerolabs/typescript-configuration": "0.2.60",
20
+ "@layerzerolabs/vm-tooling-solana": "0.2.60",
21
+ "@layerzerolabs/tsup-configuration": "0.2.60"
22
+ },
23
+ "publishConfig": {
24
+ "access": "restricted",
25
+ "registry": "https://registry.npmjs.org/"
26
+ },
27
+ "scripts": {
28
+ "build": "pnpm exec lz-tool --script 'cargo build' anchor",
29
+ "check": "pnpm exec lz-tool --script 'cargo check' anchor",
30
+ "clean": "rm -rf target",
31
+ "format": "pnpm exec lz-tool --script 'cargo +nightly-2025-06-01 fmt' anchor",
32
+ "format:check": "pnpm exec lz-tool --script 'cargo +nightly-2025-06-01 fmt -- --check' anchor",
33
+ "lint": "pnpm exec lz-tool --script 'cargo +nightly-2025-06-01 fmt -- --check && cargo clippy --all-targets' anchor",
34
+ "test": "pnpm exec lz-tool --script 'cargo test --lib --tests' anchor"
35
+ }
36
+ }
@@ -0,0 +1,3 @@
1
+ [toolchain]
2
+ channel = "1.92.0"
3
+ components = ["rustfmt", "clippy"]