Skip to content

Commit

Permalink
feat(sdk): Allow to define instructions without compressed accounts (#…
Browse files Browse the repository at this point in the history
…1273)

Make macros aware of a difference between context types:

- `LightContext`, which indicates the usage of compressed accounts.
- `anchor_lang::Context`, which indicates lack of compressed accounts.

By acknowledging that difference, allow developers to have one program
with different instructions, where some of them are using compressed
accounts and some are not.

After this change, the following instruction:

```rust
    pub fn with_compressed_account<'info>(
        ctx: LightContext<'_, '_, '_, 'info, WithCompressedAccount<'info>>,
        name: String,
    ) -> Result<()> {
```

is recognized as the one using cPDAs, because it's using `LightContext`
type.

The following:

```rust
    pub fn without_compressed_account<'info>(
        ctx: Context<'_, '_, '_, 'info, WithoutCompressedAccount<'info>>,
        name: String,
    ) -> Result<()> {
```

is recognized as a regular instruction without cPDAs, because it's using
`Context` type.
  • Loading branch information
vadorovsky authored Oct 1, 2024
1 parent 49ca247 commit 363624c
Show file tree
Hide file tree
Showing 10 changed files with 184 additions and 3 deletions.
1 change: 1 addition & 0 deletions .github/workflows/pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,4 @@ jobs:
merkle-tree
deps
deps-dev
sdk
19 changes: 19 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@ members = [
"test-utils",
"utils",
"xtask",
"examples/token-escrow/programs/*",
"examples/mixed-accounts/programs/*",
"examples/name-service/programs/*",
"examples/token-escrow/programs/*",
"test-programs/*",
"forester-utils",
"forester",
Expand Down
7 changes: 7 additions & 0 deletions examples/mixed-accounts/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.anchor
.DS_Store
target
**/*.rs.bk
node_modules
test-ledger
.yarn
18 changes: 18 additions & 0 deletions examples/mixed-accounts/Anchor.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[toolchain]

[features]
seeds = false
skip-lint = false

[programs.localnet]
name_service = "7yucc7fL3JGbyMwg4neUaenNSdySS39hbAk89Ao3t1Hz"

[registry]
url = "https://api.apr.dev"

[provider]
cluster = "Localnet"
wallet = "~/.config/solana/id.json"

[scripts]
test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts"
8 changes: 8 additions & 0 deletions examples/mixed-accounts/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Mixed accounts

An example of a program which has:

- a method using compressed accounts
- a method not using any compressed accounts

at the same time.
40 changes: 40 additions & 0 deletions examples/mixed-accounts/programs/mixed-accounts/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
[package]
name = "mixed-accounts"
version = "0.7.0"
description = "Created with Anchor"
edition = "2021"
rust-version = "1.75.0"
license = "Apache-2.0"

[lib]
crate-type = ["cdylib", "lib"]
name = "mixed_accounts"

[features]
no-entrypoint = []
no-idl = []
no-log-ix-name = []
cpi = ["no-entrypoint"]
default = ["idl-build"]
test-sbf = []
bench-sbf = []
idl-build = ["anchor-lang/idl-build", "light-sdk/idl-build"]

[dependencies]
anchor-lang = { workspace=true}
borsh = { workspace = true }
light-hasher = { workspace = true, features = ["solana"] }
light-macros = { workspace = true }
light-sdk = { workspace = true }
light-sdk-macros = { workspace = true }
light-utils = { workspace = true }
light-verifier = { workspace = true }

[target.'cfg(not(target_os = "solana"))'.dependencies]
solana-sdk = { workspace = true }

[dev-dependencies]
light-client = { workspace = true , features = ["devenv"]}
light-test-utils = { path = "../../../../test-utils", version = "1.2.0", features = ["devenv"] }
solana-program-test = { workspace = true }
tokio = { workspace = true }
2 changes: 2 additions & 0 deletions examples/mixed-accounts/programs/mixed-accounts/Xargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[target.bpfel-unknown-unknown.dependencies.std]
features = []
75 changes: 75 additions & 0 deletions examples/mixed-accounts/programs/mixed-accounts/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
use anchor_lang::prelude::*;
use light_sdk::{
compressed_account::LightAccount, light_account, light_accounts, light_program,
merkle_context::PackedAddressMerkleContext,
};

declare_id!("7yucc7fL3JGbyMwg4neUaenNSdySS39hbAk89Ao3t1Hz");

#[light_program]
#[program]
pub mod mixed_accounts {
use super::*;

pub fn with_compressed_account<'info>(
ctx: LightContext<'_, '_, '_, 'info, WithCompressedAccount<'info>>,
name: String,
) -> Result<()> {
ctx.light_accounts.my_compressed_account.name = name;
Ok(())
}

pub fn without_compressed_account<'info>(
ctx: Context<'_, '_, '_, 'info, WithoutCompressedAccount<'info>>,
name: String,
) -> Result<()> {
ctx.accounts.my_regular_account.name = name;
Ok(())
}
}

#[light_account]
#[derive(Clone, Debug, Default)]
pub struct MyCompressedAccount {
name: String,
}

#[account]
pub struct MyRegularAccount {
name: String,
}

#[light_accounts]
#[instruction(name: String)]
pub struct WithCompressedAccount<'info> {
#[account(mut)]
#[fee_payer]
pub signer: Signer<'info>,
#[self_program]
pub self_program: Program<'info, crate::program::MixedAccounts>,
/// CHECK: Checked in light-system-program.
#[authority]
pub cpi_signed: AccountInfo<'info>,

#[light_account(
init,
seeds = [b"compressed".as_slice(), name.as_bytes()],
)]
pub my_compressed_account: LightAccount<MyCompressedAccount>,
}

#[derive(Accounts)]
#[instruction(name: String)]
pub struct WithoutCompressedAccount<'info> {
#[account(mut)]
pub signer: Signer<'info>,
#[account(
init,
seeds = [b"compressed".as_slice(), name.as_bytes()],
bump,
payer = signer,
space = 8 + 8,
)]
pub my_regular_account: Account<'info, MyRegularAccount>,
pub system_program: Program<'info, System>,
}
14 changes: 12 additions & 2 deletions macros/light-sdk-macros/src/program.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,13 +141,23 @@ impl VisitMut for LightProgramTransform {
};

// Get the last path segment of `ctx` type.
// It should be `LightContext`...
let type_path = match pat_type.ty.as_mut() {
Type::Path(type_path) => type_path,
_ => return,
};
let ctx_segment = &mut type_path.path.segments.last_mut().unwrap();
// ...and we replace it with Anchor's `Context`
// If the `ctx` is of type `LightContext`, that means that the given
// instruction uses compressed accounts and we need to inject our code
// for handling them.
// Otherwise, stop processing the instruction and assume it's just a
// regular instruction using only regular accounts.
if ctx_segment.ident != "LightContext" {
return;
}

// Swap the type of `ctx` to Anchor's `Context` to keep the instruction
// signature correct. We are going to inject the code converting it to
// `LightContext` later.
ctx_segment.ident = Ident::new("Context", Span::call_site());

// Figure out what's are the names of:
Expand Down

0 comments on commit 363624c

Please sign in to comment.