Skip to content

Commit

Permalink
feat: memo example
Browse files Browse the repository at this point in the history
  • Loading branch information
sergeytimoshin committed Jan 31, 2025
1 parent 91a9c42 commit cb38a3b
Show file tree
Hide file tree
Showing 13 changed files with 675 additions and 0 deletions.
16 changes: 16 additions & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ members = [
"examples/token-escrow/programs/*",
"examples/name-service/programs/name-service-without-macros",
"examples/counter/programs/counter",
"examples/memo/programs/memo",
"program-tests/account-compression-test/",
"program-tests/compressed-token-test/",
"program-tests/e2e-test/",
Expand Down
8 changes: 8 additions & 0 deletions examples/memo/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@

.anchor
.DS_Store
target
**/*.rs.bk
node_modules
test-ledger
.yarn
8 changes: 8 additions & 0 deletions examples/memo/.prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@

.anchor
.DS_Store
target
node_modules
dist
build
test-ledger
18 changes: 18 additions & 0 deletions examples/memo/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]
counter = "GRLu2hKaAiMbxpkAM1HeXzks9YeGuz18SEgXEizVvPqX"

[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"
1 change: 1 addition & 0 deletions examples/memo/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Counter program example
12 changes: 12 additions & 0 deletions examples/memo/migrations/deploy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Migrations are an early feature. Currently, they're nothing more than this
// single deploy script that's invoked from the CLI, injecting a provider
// configured from the workspace's Anchor.toml.

const anchor = require("@coral-xyz/anchor");

module.exports = async function (provider) {
// Configure client to use the provider.
anchor.setProvider(provider);

// Add your deploy script here.
};
22 changes: 22 additions & 0 deletions examples/memo/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"scripts": {
"lint:fix": "prettier \"*/**/*{.js,.ts}\" -w",
"lint": "prettier \"*/**/*{.js,.ts}\" --check",
"test": "cargo test-sbf -p memo -- --test-threads 1"
},
"dependencies": {
"@coral-xyz/anchor": "^0.29.0"
},
"devDependencies": {
"@lightprotocol/zk-compression-cli": "workspace:*",

"chai": "^5.1.2",
"mocha": "^11.0.1",
"ts-mocha": "^10.0.0",
"@types/bn.js": "^5.1.6",
"@types/chai": "^5.0.1",
"@types/mocha": "^10.0.7",
"typescript": "^5.7.2",
"prettier": "^3.4.2"
}
}
37 changes: 37 additions & 0 deletions examples/memo/programs/memo/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
[package]
name = "memo"
version = "0.0.1"
description = "Created with Anchor"
edition = "2021"
rust-version = "1.75.0"
license = "Apache-2.0"

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

[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-sdk = { workspace = true }
light-utils = { workspace = true }

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

[dev-dependencies]
light-client = { workspace = true , features = ["devenv"]}
light-test-utils = { workspace = true, features = ["devenv"] }
light-program-test = { workspace = true, features = ["devenv"] }
tokio = "1.36.0"
2 changes: 2 additions & 0 deletions examples/memo/programs/memo/Xargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[target.bpfel-unknown-unknown.dependencies.std]
features = []
163 changes: 163 additions & 0 deletions examples/memo/programs/memo/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
use anchor_lang::prelude::*;
use borsh::BorshDeserialize;
use light_sdk::{
account::LightAccount, instruction_data::LightInstructionData, light_system_accounts,
verify::verify_light_accounts, LightDiscriminator, LightHasher, LightTraits,
};

declare_id!("GRLu2hKaAiMbxpkAM1HeXzks9YeGuz18SEgXEizVvPqX");

#[program]
pub mod memo {
use light_hasher::Discriminator;
use light_sdk::{
address::derive_address, error::LightSdkError,
program_merkle_context::unpack_address_merkle_context,
};

use super::*;

pub fn create_memo<'info>(
ctx: Context<'_, '_, '_, 'info, CreateMemo<'info>>,
inputs: Vec<u8>,
message: String,
) -> Result<()> {
let inputs = LightInstructionData::deserialize(&inputs)?;
let accounts = inputs
.accounts
.as_ref()
.ok_or(LightSdkError::ExpectedAccounts)?;

let address_merkle_context = accounts[0]
.address_merkle_context
.ok_or(LightSdkError::ExpectedAddressMerkleContext)?;
let address_merkle_context =
unpack_address_merkle_context(address_merkle_context, ctx.remaining_accounts);
let (address, address_seed) = derive_address(
&[b"memo", ctx.accounts.signer.key().as_ref()],
&address_merkle_context,
&crate::ID,
);

let mut memo: LightAccount<'_, MemoAccount> = LightAccount::from_meta_init(
&accounts[0],
MemoAccount::discriminator(),
address,
address_seed,
&crate::ID,
)?;

memo.authority = ctx.accounts.signer.key();
memo.message = message;

verify_light_accounts(&ctx, inputs.proof, &[memo], None, false, None)?;

Ok(())
}

pub fn update_memo<'info>(
ctx: Context<'_, '_, '_, 'info, UpdateMemo<'info>>,
inputs: Vec<u8>,
new_message: String,
) -> Result<()> {
let inputs = LightInstructionData::deserialize(&inputs)?;
let accounts = inputs
.accounts
.as_ref()
.ok_or(LightSdkError::ExpectedAccounts)?;

let mut memo: LightAccount<'_, MemoAccount> =
LightAccount::from_meta_mut(&accounts[0], MemoAccount::discriminator(), &crate::ID)?;

if memo.authority != ctx.accounts.signer.key() {
return err!(CustomError::Unauthorized);
}

memo.message = new_message;

verify_light_accounts(&ctx, inputs.proof, &[memo], None, false, None)?;

Ok(())
}

pub fn delete_memo<'info>(
ctx: Context<'_, '_, '_, 'info, DeleteMemo<'info>>,
inputs: Vec<u8>,
) -> Result<()> {
let inputs = LightInstructionData::deserialize(&inputs)?;
let accounts = inputs
.accounts
.as_ref()
.ok_or(LightSdkError::ExpectedAccounts)?;

let memo: LightAccount<'_, MemoAccount> =
LightAccount::from_meta_close(&accounts[0], MemoAccount::discriminator(), &crate::ID)?;

if memo.authority != ctx.accounts.signer.key() {
return err!(CustomError::Unauthorized);
}

verify_light_accounts(&ctx, inputs.proof, &[memo], None, false, None)?;

Ok(())
}
}

// Memo account structure
#[derive(
Clone, Debug, Default, AnchorDeserialize, AnchorSerialize, LightDiscriminator, LightHasher,
)]
pub struct MemoAccount {
#[truncate]
pub authority: Pubkey,
pub message: String,
}

// Custom errors
#[error_code]
pub enum CustomError {
#[msg("You are not authorized to perform this action.")]
Unauthorized,
}

// Context for creating a memo
#[light_system_accounts]
#[derive(Accounts, LightTraits)]
pub struct CreateMemo<'info> {
#[account(mut)]
#[fee_payer]
pub signer: Signer<'info>,
#[self_program]
pub self_program: Program<'info, crate::program::Memo>,
/// CHECK: Checked in light-system-program.
#[authority]
pub cpi_signer: AccountInfo<'info>,
}

// Context for updating a memo
#[light_system_accounts]
#[derive(Accounts, LightTraits)]
pub struct UpdateMemo<'info> {
#[account(mut)]
#[fee_payer]
pub signer: Signer<'info>,
#[self_program]
pub self_program: Program<'info, crate::program::Memo>,
/// CHECK: Checked in light-system-program.
#[authority]
pub cpi_signer: AccountInfo<'info>,
}

// Context for deleting a memo
#[light_system_accounts]
#[derive(Accounts, LightTraits)]
pub struct DeleteMemo<'info> {
#[account(mut)]
#[fee_payer]
pub signer: Signer<'info>,
#[self_program]
pub self_program: Program<'info, crate::program::Memo>,
/// CHECK: Checked in light-system-program.
#[authority]
pub cpi_signer: AccountInfo<'info>,
}
Loading

0 comments on commit cb38a3b

Please sign in to comment.