@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.
- package/Cargo.toml +18 -0
- package/examples/shapes/Anchor.toml +19 -0
- package/examples/shapes/macros/Cargo.toml +17 -0
- package/examples/shapes/macros/src/instructions/compute_area.rs +50 -0
- package/examples/shapes/macros/src/instructions/compute_perimeter.rs +50 -0
- package/examples/shapes/macros/src/instructions/get_dimension_count.rs +44 -0
- package/examples/shapes/macros/src/instructions/get_name.rs +18 -0
- package/examples/shapes/macros/src/instructions/initialize.rs +18 -0
- package/examples/shapes/macros/src/instructions/mod.rs +26 -0
- package/examples/shapes/macros/src/lib.rs +50 -0
- package/examples/shapes/macros/src/shapes.rs +56 -0
- package/examples/shapes/programs/ellipse/Cargo.toml +30 -0
- package/examples/shapes/programs/ellipse/src/lib.rs +88 -0
- package/examples/shapes/programs/ellipse/src/state.rs +9 -0
- package/examples/shapes/programs/triangle/Cargo.toml +30 -0
- package/examples/shapes/programs/triangle/src/lib.rs +83 -0
- package/examples/shapes/programs/triangle/src/state.rs +9 -0
- package/package.json +36 -0
- package/rust-toolchain.toml +3 -0
- package/rustfmt.toml +23 -0
- package/src/assembly.rs +63 -0
- package/src/generation.rs +226 -0
- package/src/lib.rs +126 -0
- package/src/overrides.rs +289 -0
- package/tests/assembly.rs +339 -0
- package/tests/common/mod.rs +206 -0
- package/tests/declare_instructions.rs +206 -0
- package/tests/generation.rs +358 -0
- package/tests/override.rs +414 -0
- package/tests/snapshots/snapshots__tests__snapshot_all_defaults.snap +35 -0
- package/tests/snapshots/snapshots__tests__snapshot_full_override.snap +47 -0
- package/tests/snapshots/snapshots__tests__snapshot_handler_override.snap +42 -0
- package/tests/snapshots/snapshots__tests__snapshot_mixed_overrides.snap +57 -0
- 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,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
|
+
}
|
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
|
+
}
|