From 7e0c3b15033904bc46f96ba57b8377a25022c446 Mon Sep 17 00:00:00 2001 From: Jimii Date: Wed, 22 Apr 2026 17:19:16 +0300 Subject: [PATCH 01/12] update: migrate quasar programs to latest API --- basics/account-data/quasar/Cargo.toml | 2 +- .../quasar/src/instructions/create.rs | 42 ++- basics/account-data/quasar/src/lib.rs | 4 +- basics/account-data/quasar/src/state.rs | 1 + basics/checking-accounts/quasar/Cargo.toml | 2 +- .../quasar/src/instructions/check_accounts.rs | 22 +- basics/checking-accounts/quasar/src/lib.rs | 4 +- basics/close-account/quasar/Cargo.toml | 2 +- .../quasar/src/instructions/close_user.rs | 14 +- .../quasar/src/instructions/create_user.rs | 32 +- basics/close-account/quasar/src/lib.rs | 6 +- basics/close-account/quasar/src/state.rs | 3 +- basics/counter/quasar/Cargo.toml | 2 +- .../quasar/src/instructions/increment.rs | 16 +- .../src/instructions/initialize_counter.rs | 20 +- basics/counter/quasar/src/lib.rs | 6 +- basics/counter/quasar/src/state.rs | 1 + basics/create-account/quasar/Cargo.toml | 2 +- .../src/instructions/create_system_account.rs | 38 +- basics/create-account/quasar/src/lib.rs | 4 +- .../quasar/hand/Cargo.toml | 2 +- .../quasar/hand/src/lib.rs | 2 +- .../quasar/lever/Cargo.toml | 2 +- .../quasar/lever/src/lib.rs | 2 +- basics/favorites/quasar/Cargo.toml | 2 +- .../quasar/src/instructions/set_favorites.rs | 28 +- basics/favorites/quasar/src/lib.rs | 4 +- basics/favorites/quasar/src/state.rs | 1 + basics/hello-solana/quasar/Cargo.toml | 2 +- .../quasar/src/instructions/hello.rs | 16 +- basics/hello-solana/quasar/src/lib.rs | 4 +- basics/pda-rent-payer/quasar/Cargo.toml | 2 +- .../src/instructions/create_new_account.rs | 48 +-- .../src/instructions/init_rent_vault.rs | 20 +- basics/pda-rent-payer/quasar/src/lib.rs | 6 +- .../processing-instructions/quasar/Cargo.toml | 2 +- .../quasar/src/instructions/go_to_park.rs | 30 +- .../processing-instructions/quasar/src/lib.rs | 4 +- .../quasar/Cargo.toml | 2 +- .../quasar/src/instructions/create.rs | 20 +- .../quasar/src/instructions/increment.rs | 18 +- .../quasar/src/lib.rs | 6 +- .../quasar/src/state/page_visits.rs | 1 + basics/realloc/quasar/Cargo.toml | 2 +- .../quasar/src/instructions/initialize.rs | 24 +- .../realloc/quasar/src/instructions/update.rs | 24 +- basics/realloc/quasar/src/lib.rs | 6 +- basics/rent/quasar/Cargo.toml | 2 +- .../src/instructions/create_system_account.rs | 57 ++- basics/rent/quasar/src/lib.rs | 4 +- basics/repository-layout/quasar/Cargo.toml | 2 +- .../src/instructions/carnival_context.rs | 54 +-- basics/repository-layout/quasar/src/lib.rs | 8 +- basics/transfer-sol/quasar/Cargo.toml | 2 +- .../src/instructions/transfer_sol_with_cpi.rs | 20 +- .../instructions/transfer_sol_with_program.rs | 22 +- basics/transfer-sol/quasar/src/lib.rs | 6 +- compression/cnft-burn/quasar/Cargo.toml | 2 +- .../quasar/src/instructions/burn_cnft.rs | 152 ++++---- compression/cnft-burn/quasar/src/lib.rs | 4 +- compression/cnft-vault/quasar/Cargo.toml | 2 +- .../quasar/src/instructions/withdraw.rs | 176 ++++----- .../quasar/src/instructions/withdraw_two.rs | 314 +++++++-------- compression/cnft-vault/quasar/src/lib.rs | 6 +- compression/cutils/quasar/Cargo.toml | 2 +- .../cutils/quasar/src/instructions/mint.rs | 168 +++++---- .../cutils/quasar/src/instructions/verify.rs | 152 ++++---- compression/cutils/quasar/src/lib.rs | 6 +- oracles/pyth/quasar/Cargo.toml | 2 +- .../quasar/src/instructions/read_price.rs | 80 ++-- oracles/pyth/quasar/src/lib.rs | 4 +- tokens/create-token/quasar/Cargo.toml | 4 +- tokens/create-token/quasar/src/lib.rs | 50 +-- tokens/escrow/quasar/Cargo.toml | 4 +- tokens/escrow/quasar/src/instructions/make.rs | 60 +-- .../escrow/quasar/src/instructions/refund.rs | 58 ++- tokens/escrow/quasar/src/instructions/take.rs | 88 +++-- tokens/escrow/quasar/src/lib.rs | 12 +- tokens/escrow/quasar/src/state.rs | 3 +- .../quasar/Cargo.toml | 4 +- .../quasar/src/lib.rs | 194 +++++----- tokens/nft-minter/quasar/Cargo.toml | 4 +- tokens/nft-minter/quasar/src/lib.rs | 133 +++---- tokens/nft-operations/quasar/Cargo.toml | 4 +- .../src/instructions/create_collection.rs | 122 +++--- .../quasar/src/instructions/mint_nft.rs | 122 +++--- .../src/instructions/verify_collection.rs | 60 +-- tokens/nft-operations/quasar/src/lib.rs | 8 +- tokens/pda-mint-authority/quasar/Cargo.toml | 4 +- tokens/pda-mint-authority/quasar/src/lib.rs | 62 +-- tokens/spl-token-minter/quasar/Cargo.toml | 4 +- .../quasar/src/instructions/create.rs | 71 ++-- .../quasar/src/instructions/mint.rs | 50 +-- tokens/spl-token-minter/quasar/src/lib.rs | 6 +- .../token-extensions/basics/quasar/Cargo.toml | 4 +- .../token-extensions/basics/quasar/src/lib.rs | 112 +++--- .../cpi-guard/quasar/Cargo.toml | 4 +- .../cpi-guard/quasar/src/lib.rs | 66 ++-- .../default-account-state/quasar/Cargo.toml | 4 +- .../default-account-state/quasar/src/lib.rs | 154 ++++---- .../token-extensions/group/quasar/Cargo.toml | 4 +- .../token-extensions/group/quasar/src/lib.rs | 112 +++--- .../immutable-owner/quasar/Cargo.toml | 4 +- .../immutable-owner/quasar/src/lib.rs | 100 ++--- .../interest-bearing/quasar/Cargo.toml | 4 +- .../interest-bearing/quasar/src/lib.rs | 166 ++++---- .../memo-transfer/quasar/Cargo.toml | 4 +- .../memo-transfer/quasar/src/lib.rs | 152 ++++---- .../mint-close-authority/quasar/Cargo.toml | 4 +- .../mint-close-authority/quasar/src/lib.rs | 154 ++++---- .../non-transferable/quasar/Cargo.toml | 4 +- .../non-transferable/quasar/src/lib.rs | 96 ++--- .../permanent-delegate/quasar/Cargo.toml | 4 +- .../permanent-delegate/quasar/src/lib.rs | 100 ++--- .../transfer-fee/quasar/Cargo.toml | 4 +- .../transfer-fee/quasar/src/lib.rs | 306 +++++++-------- .../account-data-as-seed/quasar/Cargo.toml | 4 +- .../account-data-as-seed/quasar/src/lib.rs | 2 +- .../allow-block-list-token/quasar/Cargo.toml | 4 +- .../allow-block-list-token/quasar/src/lib.rs | 2 +- .../transfer-hook/counter/quasar/Cargo.toml | 4 +- .../transfer-hook/counter/quasar/src/lib.rs | 294 +++++++-------- .../hello-world/quasar/Cargo.toml | 4 +- .../hello-world/quasar/src/lib.rs | 282 +++++++------- .../transfer-cost/quasar/Cargo.toml | 4 +- .../transfer-cost/quasar/src/lib.rs | 232 ++++++------ .../transfer-switch/quasar/Cargo.toml | 4 +- .../transfer-switch/quasar/src/lib.rs | 356 +++++++++--------- .../transfer-hook/whitelist/quasar/Cargo.toml | 4 +- .../transfer-hook/whitelist/quasar/src/lib.rs | 2 +- tokens/token-fundraiser/quasar/Cargo.toml | 4 +- .../src/instructions/check_contributions.rs | 58 ++- .../quasar/src/instructions/contribute.rs | 44 +-- .../quasar/src/instructions/initialize.rs | 57 +-- .../quasar/src/instructions/refund.rs | 56 ++- tokens/token-fundraiser/quasar/src/lib.rs | 10 +- tokens/token-fundraiser/quasar/src/state.rs | 3 +- tokens/token-swap/quasar/Cargo.toml | 4 +- .../quasar/src/instructions/create_amm.rs | 24 +- .../quasar/src/instructions/create_pool.rs | 40 +- .../src/instructions/deposit_liquidity.rs | 167 ++++---- .../swap_exact_tokens_for_tokens.rs | 213 +++++------ .../src/instructions/withdraw_liquidity.rs | 111 +++--- tokens/token-swap/quasar/src/lib.rs | 12 +- tokens/transfer-tokens/quasar/Cargo.toml | 4 +- tokens/transfer-tokens/quasar/src/lib.rs | 50 +-- 146 files changed, 3306 insertions(+), 3188 deletions(-) diff --git a/basics/account-data/quasar/Cargo.toml b/basics/account-data/quasar/Cargo.toml index a5999e69d..9027e9784 100644 --- a/basics/account-data/quasar/Cargo.toml +++ b/basics/account-data/quasar/Cargo.toml @@ -22,7 +22,7 @@ client = [] debug = [] [dependencies] -quasar-lang = "0.0" +quasar-lang = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } solana-instruction = { version = "3.2.0" } [dev-dependencies] diff --git a/basics/account-data/quasar/src/instructions/create.rs b/basics/account-data/quasar/src/instructions/create.rs index 1cb4a5a12..0099c8843 100644 --- a/basics/account-data/quasar/src/instructions/create.rs +++ b/basics/account-data/quasar/src/instructions/create.rs @@ -7,27 +7,29 @@ use { /// Dynamic accounts use owned `Account` rather than `&'info mut Account` because /// dynamic types carry cached byte offsets that cannot be represented as a pointer cast. #[derive(Accounts)] -pub struct CreateAddressInfo<'info> { +pub struct CreateAddressInfo { #[account(mut)] - pub payer: &'info mut Signer, - #[account(mut, init, payer = payer, seeds = [b"address_info", payer], bump)] - pub address_info: Account>, - pub system_program: &'info Program, + pub payer: Signer, + #[account(mut, init, payer = payer, seeds = AddressInfo::seeds(payer), bump)] + pub address_info: Account>, + pub system_program: Program, } -#[inline(always)] -pub fn handle_create_address_info( - accounts: &mut CreateAddressInfo, name: &str, - house_number: u8, - street: &str, - city: &str, -) -> Result<(), ProgramError> { - accounts.address_info.set_inner( - house_number, - name, - street, - city, - accounts.payer.to_account_view(), - None, - ) +impl CreateAddressInfo { + #[inline(always)] + pub fn create_address_info( + &mut self, name: &str, + house_number: u8, + street: &str, + city: &str, + ) -> Result<(), ProgramError> { + self.address_info.set_inner( + house_number, + name, + street, + city, + self.payer.to_account_view(), + None, + ) + } } diff --git a/basics/account-data/quasar/src/lib.rs b/basics/account-data/quasar/src/lib.rs index 36253c9ae..7f7c6af0e 100644 --- a/basics/account-data/quasar/src/lib.rs +++ b/basics/account-data/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![cfg_attr(not(test), no_std)] +#![no_std] use quasar_lang::prelude::*; @@ -30,6 +30,6 @@ mod quasar_account_data { street: String, city: String, ) -> Result<(), ProgramError> { - instructions::handle_create_address_info(&mut ctx.accounts, name, house_number, street, city) + ctx.accounts.create_address_info(name, house_number, street, city) } } diff --git a/basics/account-data/quasar/src/state.rs b/basics/account-data/quasar/src/state.rs index ba7a8e14f..3c7af75b4 100644 --- a/basics/account-data/quasar/src/state.rs +++ b/basics/account-data/quasar/src/state.rs @@ -6,6 +6,7 @@ use quasar_lang::prelude::*; /// /// Note: Quasar requires all fixed-size fields to precede dynamic (String/Vec) fields. #[account(discriminator = 1)] +#[seeds(b"address_info", payer: Address)] pub struct AddressInfo<'a> { pub house_number: u8, pub name: String, diff --git a/basics/checking-accounts/quasar/Cargo.toml b/basics/checking-accounts/quasar/Cargo.toml index 5ce153113..af16e98ee 100644 --- a/basics/checking-accounts/quasar/Cargo.toml +++ b/basics/checking-accounts/quasar/Cargo.toml @@ -22,7 +22,7 @@ client = [] debug = [] [dependencies] -quasar-lang = "0.0" +quasar-lang = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } solana-instruction = { version = "3.2.0" } [dev-dependencies] diff --git a/basics/checking-accounts/quasar/src/instructions/check_accounts.rs b/basics/checking-accounts/quasar/src/instructions/check_accounts.rs index f64b05137..d8dfdd3ab 100644 --- a/basics/checking-accounts/quasar/src/instructions/check_accounts.rs +++ b/basics/checking-accounts/quasar/src/instructions/check_accounts.rs @@ -8,22 +8,24 @@ use quasar_lang::prelude::*; /// Note: Anchor's `#[account(owner = id())]` owner constraint is not directly available /// in Quasar. Owner checks can be done manually in the instruction body if needed. #[derive(Accounts)] -pub struct CheckAccounts<'info> { +pub struct CheckAccounts { /// Checks that this account signed the transaction. - pub payer: &'info Signer, + pub payer: Signer, /// No checks performed — the caller is responsible for validation. #[account(mut)] - pub account_to_create: &'info mut UncheckedAccount, + pub account_to_create: UncheckedAccount, /// No automatic owner check in Quasar; see note above. #[account(mut)] - pub account_to_change: &'info mut UncheckedAccount, + pub account_to_change: UncheckedAccount, /// Checks the account is executable and matches the system program address. - pub system_program: &'info Program, + pub system_program: Program, } -#[inline(always)] -pub fn handle_check_accounts(accounts: &CheckAccounts) -> Result<(), ProgramError> { - // All validation happens declaratively via the account types above. - // If any check fails, the runtime rejects the transaction before this runs. - Ok(()) +impl CheckAccounts { + #[inline(always)] + pub fn check_accounts(&mut self) -> Result<(), ProgramError> { + // All validation happens declaratively via the account types above. + // If any check fails, the runtime rejects the transaction before this runs. + Ok(()) + } } diff --git a/basics/checking-accounts/quasar/src/lib.rs b/basics/checking-accounts/quasar/src/lib.rs index 43d523190..f77707649 100644 --- a/basics/checking-accounts/quasar/src/lib.rs +++ b/basics/checking-accounts/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![cfg_attr(not(test), no_std)] +#![no_std] use quasar_lang::prelude::*; @@ -19,6 +19,6 @@ mod quasar_checking_accounts { /// - Program: checks account is executable and is the system program #[instruction(discriminator = 0)] pub fn check_accounts(ctx: Ctx) -> Result<(), ProgramError> { - instructions::handle_check_accounts(&mut ctx.accounts) + ctx.accounts.check_accounts() } } diff --git a/basics/close-account/quasar/Cargo.toml b/basics/close-account/quasar/Cargo.toml index 1f3c9b00d..18b91004a 100644 --- a/basics/close-account/quasar/Cargo.toml +++ b/basics/close-account/quasar/Cargo.toml @@ -20,7 +20,7 @@ client = [] debug = [] [dependencies] -quasar-lang = "0.0" +quasar-lang = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } solana-instruction = { version = "3.2.0" } [dev-dependencies] diff --git a/basics/close-account/quasar/src/instructions/close_user.rs b/basics/close-account/quasar/src/instructions/close_user.rs index c8444f2cd..a15db3958 100644 --- a/basics/close-account/quasar/src/instructions/close_user.rs +++ b/basics/close-account/quasar/src/instructions/close_user.rs @@ -8,14 +8,16 @@ use { /// In Quasar, we call `close()` explicitly — it zeros the discriminator, drains lamports /// to the destination, reassigns the owner to the system program, and resizes to 0. #[derive(Accounts)] -pub struct CloseUser<'info> { +pub struct CloseUser { #[account(mut)] - pub user: &'info mut Signer, + pub user: Signer, #[account(mut)] - pub user_account: Account>, + pub user_account: Account>, } -#[inline(always)] -pub fn handle_close_user(accounts: &mut CloseUser) -> Result<(), ProgramError> { - accounts.user_account.close(accounts.user.to_account_view()) +impl CloseUser { + #[inline(always)] + pub fn close_user(&mut self) -> Result<(), ProgramError> { + self.user_account.close(self.user.to_account_view()) + } } diff --git a/basics/close-account/quasar/src/instructions/create_user.rs b/basics/close-account/quasar/src/instructions/create_user.rs index 1f50705f5..1559e80fd 100644 --- a/basics/close-account/quasar/src/instructions/create_user.rs +++ b/basics/close-account/quasar/src/instructions/create_user.rs @@ -5,22 +5,24 @@ use { /// Accounts for creating a new user. #[derive(Accounts)] -pub struct CreateUser<'info> { +pub struct CreateUser { #[account(mut)] - pub user: &'info mut Signer, - #[account(mut, init, payer = user, seeds = [b"USER", user], bump)] - pub user_account: Account>, - pub system_program: &'info Program, + pub user: Signer, + #[account(mut, init, payer = user, seeds = UserState::seeds(user), bump)] + pub user_account: Account>, + pub system_program: Program, } -#[inline(always)] -pub fn handle_create_user(accounts: &mut CreateUser, name: &str, bump: u8) -> Result<(), ProgramError> { - let user_address = *accounts.user.to_account_view().address(); - accounts.user_account.set_inner( - bump, - user_address, - name, - accounts.user.to_account_view(), - None, - ) +impl CreateUser { + #[inline(always)] + pub fn create_user(&mut self, name: &str, bump: u8) -> Result<(), ProgramError> { + let user_address = *self.user.to_account_view().address(); + self.user_account.set_inner( + bump, + user_address, + name, + self.user.to_account_view(), + None, + ) + } } diff --git a/basics/close-account/quasar/src/lib.rs b/basics/close-account/quasar/src/lib.rs index d8838eb01..96e01ea09 100644 --- a/basics/close-account/quasar/src/lib.rs +++ b/basics/close-account/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![cfg_attr(not(test), no_std)] +#![no_std] use quasar_lang::prelude::*; @@ -18,12 +18,12 @@ mod quasar_close_account { #[instruction(discriminator = 0)] pub fn create_user(ctx: Ctx, name: String) -> Result<(), ProgramError> { let bump = ctx.bumps.user_account; - instructions::handle_create_user(&mut ctx.accounts, name, bump) + ctx.accounts.create_user(name, bump) } /// Close a user account and return lamports to the user. #[instruction(discriminator = 1)] pub fn close_user(ctx: Ctx) -> Result<(), ProgramError> { - instructions::handle_close_user(&mut ctx.accounts) + ctx.accounts.close_user() } } diff --git a/basics/close-account/quasar/src/state.rs b/basics/close-account/quasar/src/state.rs index fa9a9c925..26c4d7b01 100644 --- a/basics/close-account/quasar/src/state.rs +++ b/basics/close-account/quasar/src/state.rs @@ -2,7 +2,8 @@ use quasar_lang::prelude::*; /// User account with a dynamic name field. /// Fixed fields (bump, user) must precede dynamic fields (name). -#[account(discriminator = 1)] +#[account(discriminator = 1, set_inner)] +#[seeds(b"USER", user: Address)] pub struct UserState<'a> { pub bump: u8, pub user: Address, diff --git a/basics/counter/quasar/Cargo.toml b/basics/counter/quasar/Cargo.toml index 7963c22b5..3e61142e1 100644 --- a/basics/counter/quasar/Cargo.toml +++ b/basics/counter/quasar/Cargo.toml @@ -22,7 +22,7 @@ client = [] debug = [] [dependencies] -quasar-lang = "0.0" +quasar-lang = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } solana-instruction = { version = "3.2.0" } [dev-dependencies] diff --git a/basics/counter/quasar/src/instructions/increment.rs b/basics/counter/quasar/src/instructions/increment.rs index 989101f9e..32aae2b64 100644 --- a/basics/counter/quasar/src/instructions/increment.rs +++ b/basics/counter/quasar/src/instructions/increment.rs @@ -5,14 +5,16 @@ use { /// Accounts for incrementing a counter. #[derive(Accounts)] -pub struct Increment<'info> { +pub struct Increment { #[account(mut)] - pub counter: &'info mut Account, + pub counter: Account, } -#[inline(always)] -pub fn handle_increment(accounts: &mut Increment) -> Result<(), ProgramError> { - let current: u64 = accounts.counter.count.into(); - accounts.counter.count = PodU64::from(current.checked_add(1).unwrap()); - Ok(()) +impl Increment { + #[inline(always)] + pub fn increment(&mut self) -> Result<(), ProgramError> { + let current: u64 = self.counter.count.into(); + self.counter.count = PodU64::from(current.checked_add(1).unwrap()); + Ok(()) + } } diff --git a/basics/counter/quasar/src/instructions/initialize_counter.rs b/basics/counter/quasar/src/instructions/initialize_counter.rs index 8d02d6df5..96309da16 100644 --- a/basics/counter/quasar/src/instructions/initialize_counter.rs +++ b/basics/counter/quasar/src/instructions/initialize_counter.rs @@ -6,16 +6,18 @@ use { /// Accounts for creating a new counter. /// The counter is derived as a PDA from ["counter", payer] seeds. #[derive(Accounts)] -pub struct InitializeCounter<'info> { +pub struct InitializeCounter { #[account(mut)] - pub payer: &'info mut Signer, - #[account(mut, init, payer = payer, seeds = [b"counter", payer], bump)] - pub counter: &'info mut Account, - pub system_program: &'info Program, + pub payer: Signer, + #[account(mut, init, payer = payer, seeds = Counter::seeds(payer), bump)] + pub counter: Account, + pub system_program: Program, } -#[inline(always)] -pub fn handle_initialize_counter(accounts: &mut InitializeCounter) -> Result<(), ProgramError> { - accounts.counter.set_inner(0u64); - Ok(()) +impl InitializeCounter { + #[inline(always)] + pub fn initialize_counter(&mut self) -> Result<(), ProgramError> { + self.counter.set_inner(0u64); + Ok(()) + } } diff --git a/basics/counter/quasar/src/lib.rs b/basics/counter/quasar/src/lib.rs index b265456ee..21e738209 100644 --- a/basics/counter/quasar/src/lib.rs +++ b/basics/counter/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![cfg_attr(not(test), no_std)] +#![no_std] use quasar_lang::prelude::*; @@ -16,11 +16,11 @@ mod quasar_counter { #[instruction(discriminator = 0)] pub fn initialize_counter(ctx: Ctx) -> Result<(), ProgramError> { - instructions::handle_initialize_counter(&mut ctx.accounts) + ctx.accounts.initialize_counter() } #[instruction(discriminator = 1)] pub fn increment(ctx: Ctx) -> Result<(), ProgramError> { - instructions::handle_increment(&mut ctx.accounts) + ctx.accounts.increment() } } diff --git a/basics/counter/quasar/src/state.rs b/basics/counter/quasar/src/state.rs index d97b5558e..d49abab1d 100644 --- a/basics/counter/quasar/src/state.rs +++ b/basics/counter/quasar/src/state.rs @@ -2,6 +2,7 @@ use quasar_lang::prelude::*; /// Onchain counter account. #[account(discriminator = 1)] +#[seeds(b"counter", payer: Address)] pub struct Counter { pub count: u64, } diff --git a/basics/create-account/quasar/Cargo.toml b/basics/create-account/quasar/Cargo.toml index d1e584d97..f980fe3d1 100644 --- a/basics/create-account/quasar/Cargo.toml +++ b/basics/create-account/quasar/Cargo.toml @@ -22,7 +22,7 @@ client = [] debug = [] [dependencies] -quasar-lang = "0.0" +quasar-lang = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } solana-instruction = { version = "3.2.0" } [dev-dependencies] diff --git a/basics/create-account/quasar/src/instructions/create_system_account.rs b/basics/create-account/quasar/src/instructions/create_system_account.rs index 876e9883b..71f8a8fa2 100644 --- a/basics/create-account/quasar/src/instructions/create_system_account.rs +++ b/basics/create-account/quasar/src/instructions/create_system_account.rs @@ -3,26 +3,28 @@ use quasar_lang::prelude::*; /// Accounts for creating a new system-owned account. /// Both payer and new_account must sign the transaction. #[derive(Accounts)] -pub struct CreateSystemAccount<'info> { +pub struct CreateSystemAccount { #[account(mut)] - pub payer: &'info Signer, + pub payer: Signer, #[account(mut)] - pub new_account: &'info Signer, - pub system_program: &'info Program, + pub new_account: Signer, + pub system_program: Program, } -#[inline(always)] -pub fn handle_create_system_account(accounts: &CreateSystemAccount) -> Result<(), ProgramError> { - // Create a zero-data account owned by the system program, - // funded with the minimum rent-exempt balance. - let system_program_address = Address::default(); - accounts.system_program - .create_account_with_minimum_balance( - accounts.payer, - accounts.new_account, - 0, // space: zero bytes of data - &system_program_address, - None, // fetch Rent sysvar automatically - )? - .invoke() +impl CreateSystemAccount { + #[inline(always)] + pub fn create_system_account(&mut self) -> Result<(), ProgramError> { + // Create a zero-data account owned by the system program, + // funded with the minimum rent-exempt balance. + let system_program_address = Address::default(); + self.system_program + .create_account_with_minimum_balance( + &self.payer, + &self.new_account, + 0, // space: zero bytes of data + &system_program_address, + None, // fetch Rent sysvar automatically + )? + .invoke() + } } diff --git a/basics/create-account/quasar/src/lib.rs b/basics/create-account/quasar/src/lib.rs index 8f71eeba5..775b74ce6 100644 --- a/basics/create-account/quasar/src/lib.rs +++ b/basics/create-account/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![cfg_attr(not(test), no_std)] +#![no_std] use quasar_lang::prelude::*; @@ -16,6 +16,6 @@ mod quasar_create_account { /// Create a new system-owned account via CPI to the system program. #[instruction(discriminator = 0)] pub fn create_system_account(ctx: Ctx) -> Result<(), ProgramError> { - instructions::handle_create_system_account(&mut ctx.accounts) + ctx.accounts.create_system_account() } } diff --git a/basics/cross-program-invocation/quasar/hand/Cargo.toml b/basics/cross-program-invocation/quasar/hand/Cargo.toml index 55ec9838e..acc1d0002 100644 --- a/basics/cross-program-invocation/quasar/hand/Cargo.toml +++ b/basics/cross-program-invocation/quasar/hand/Cargo.toml @@ -21,7 +21,7 @@ client = [] debug = [] [dependencies] -quasar-lang = "0.0" +quasar-lang = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } solana-instruction = { version = "3.2.0" } [dev-dependencies] diff --git a/basics/cross-program-invocation/quasar/hand/src/lib.rs b/basics/cross-program-invocation/quasar/hand/src/lib.rs index e272cf83c..6eda4c7c3 100644 --- a/basics/cross-program-invocation/quasar/hand/src/lib.rs +++ b/basics/cross-program-invocation/quasar/hand/src/lib.rs @@ -1,4 +1,4 @@ -#![cfg_attr(not(test), no_std)] +#![no_std] use quasar_lang::prelude::*; diff --git a/basics/cross-program-invocation/quasar/lever/Cargo.toml b/basics/cross-program-invocation/quasar/lever/Cargo.toml index a50958f7d..28ad2933f 100644 --- a/basics/cross-program-invocation/quasar/lever/Cargo.toml +++ b/basics/cross-program-invocation/quasar/lever/Cargo.toml @@ -21,7 +21,7 @@ client = [] debug = [] [dependencies] -quasar-lang = "0.0" +quasar-lang = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } solana-instruction = { version = "3.2.0" } [dev-dependencies] diff --git a/basics/cross-program-invocation/quasar/lever/src/lib.rs b/basics/cross-program-invocation/quasar/lever/src/lib.rs index 00aa9464e..f5691fafc 100644 --- a/basics/cross-program-invocation/quasar/lever/src/lib.rs +++ b/basics/cross-program-invocation/quasar/lever/src/lib.rs @@ -1,4 +1,4 @@ -#![cfg_attr(not(test), no_std)] +#![no_std] use quasar_lang::prelude::*; diff --git a/basics/favorites/quasar/Cargo.toml b/basics/favorites/quasar/Cargo.toml index e2cf1940e..cf87bca66 100644 --- a/basics/favorites/quasar/Cargo.toml +++ b/basics/favorites/quasar/Cargo.toml @@ -20,7 +20,7 @@ client = [] debug = [] [dependencies] -quasar-lang = "0.0" +quasar-lang = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } solana-instruction = { version = "3.2.0" } [dev-dependencies] diff --git a/basics/favorites/quasar/src/instructions/set_favorites.rs b/basics/favorites/quasar/src/instructions/set_favorites.rs index d5bfe44a0..9ba96747a 100644 --- a/basics/favorites/quasar/src/instructions/set_favorites.rs +++ b/basics/favorites/quasar/src/instructions/set_favorites.rs @@ -6,20 +6,22 @@ use { /// Accounts for setting user favourites. Uses `init_if_needed` so the same /// instruction can create or update the favourites PDA. #[derive(Accounts)] -pub struct SetFavorites<'info> { +pub struct SetFavorites { #[account(mut)] - pub user: &'info mut Signer, - #[account(mut, init_if_needed, payer = user, seeds = [b"favorites", user], bump)] - pub favorites: Account>, - pub system_program: &'info Program, + pub user: Signer, + #[account(mut, init_if_needed, payer = user, seeds = Favorites::seeds(user), bump)] + pub favorites: Account>, + pub system_program: Program, } -#[inline(always)] -pub fn handle_set_favorites(accounts: &mut SetFavorites, number: u64, color: &str) -> Result<(), ProgramError> { - accounts.favorites.set_inner( - number, - color, - accounts.user.to_account_view(), - None, - ) +impl SetFavorites { + #[inline(always)] + pub fn set_favorites(&mut self, number: u64, color: &str) -> Result<(), ProgramError> { + self.favorites.set_inner( + number, + color, + self.user.to_account_view(), + None, + ) + } } diff --git a/basics/favorites/quasar/src/lib.rs b/basics/favorites/quasar/src/lib.rs index d6af2e37d..c81a10891 100644 --- a/basics/favorites/quasar/src/lib.rs +++ b/basics/favorites/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![cfg_attr(not(test), no_std)] +#![no_std] use quasar_lang::prelude::*; @@ -24,6 +24,6 @@ mod quasar_favorites { number: u64, color: String, ) -> Result<(), ProgramError> { - instructions::handle_set_favorites(&mut ctx.accounts, number, color) + ctx.accounts.set_favorites(number, color) } } diff --git a/basics/favorites/quasar/src/state.rs b/basics/favorites/quasar/src/state.rs index 5d9fa39a5..a58199273 100644 --- a/basics/favorites/quasar/src/state.rs +++ b/basics/favorites/quasar/src/state.rs @@ -6,6 +6,7 @@ use quasar_lang::prelude::*; /// support nested dynamic types (Vec). We keep number + color, which /// demonstrates fixed + dynamic field mixing in Quasar. #[account(discriminator = 1)] +#[seeds(b"favorites", user: Address)] pub struct Favorites<'a> { pub number: u64, pub color: String, diff --git a/basics/hello-solana/quasar/Cargo.toml b/basics/hello-solana/quasar/Cargo.toml index cf3cdcacb..16c22f6ae 100644 --- a/basics/hello-solana/quasar/Cargo.toml +++ b/basics/hello-solana/quasar/Cargo.toml @@ -22,7 +22,7 @@ client = [] debug = [] [dependencies] -quasar-lang = "0.0" +quasar-lang = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } solana-instruction = { version = "3.2.0" } [dev-dependencies] diff --git a/basics/hello-solana/quasar/src/instructions/hello.rs b/basics/hello-solana/quasar/src/instructions/hello.rs index aec626623..16569b19c 100644 --- a/basics/hello-solana/quasar/src/instructions/hello.rs +++ b/basics/hello-solana/quasar/src/instructions/hello.rs @@ -4,14 +4,16 @@ use quasar_lang::prelude::*; /// A payer (signer) is required to submit the transaction, but the program /// simply logs a greeting and the program ID. #[derive(Accounts)] -pub struct Hello<'info> { +pub struct Hello { #[allow(dead_code)] - pub payer: &'info Signer, + pub payer: Signer, } -#[inline(always)] -pub fn handle_hello(accounts: &Hello) -> Result<(), ProgramError> { - log("Hello, Solana!"); - log("Our program's Program ID: FLUH9c5oAfXb1eYbkZvdGK9r9SLQJBUi2DZQaBVj7Tzr"); - Ok(()) +impl Hello { + #[inline(always)] + pub fn hello(&mut self) -> Result<(), ProgramError> { + log("Hello, Solana!"); + log("Our program's Program ID: FLUH9c5oAfXb1eYbkZvdGK9r9SLQJBUi2DZQaBVj7Tzr"); + Ok(()) + } } diff --git a/basics/hello-solana/quasar/src/lib.rs b/basics/hello-solana/quasar/src/lib.rs index 53fcf9ad8..724306d0a 100644 --- a/basics/hello-solana/quasar/src/lib.rs +++ b/basics/hello-solana/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![cfg_attr(not(test), no_std)] +#![no_std] use quasar_lang::prelude::*; @@ -15,6 +15,6 @@ mod quasar_hello_solana { #[instruction(discriminator = 0)] pub fn hello(ctx: Ctx) -> Result<(), ProgramError> { - instructions::handle_hello(&mut ctx.accounts) + ctx.accounts.hello() } } diff --git a/basics/pda-rent-payer/quasar/Cargo.toml b/basics/pda-rent-payer/quasar/Cargo.toml index f4189e248..a848eacd4 100644 --- a/basics/pda-rent-payer/quasar/Cargo.toml +++ b/basics/pda-rent-payer/quasar/Cargo.toml @@ -22,7 +22,7 @@ client = [] debug = [] [dependencies] -quasar-lang = "0.0" +quasar-lang = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } solana-instruction = { version = "3.2.0" } [dev-dependencies] diff --git a/basics/pda-rent-payer/quasar/src/instructions/create_new_account.rs b/basics/pda-rent-payer/quasar/src/instructions/create_new_account.rs index aa9eac30a..f21bc41e7 100644 --- a/basics/pda-rent-payer/quasar/src/instructions/create_new_account.rs +++ b/basics/pda-rent-payer/quasar/src/instructions/create_new_account.rs @@ -3,33 +3,35 @@ use quasar_lang::prelude::*; /// Accounts for creating a new account funded by the rent vault PDA. /// The rent vault signs the create_account CPI via PDA seeds. #[derive(Accounts)] -pub struct CreateNewAccount<'info> { +pub struct CreateNewAccount { #[account(mut)] - pub new_account: &'info Signer, + pub new_account: Signer, #[account(mut, seeds = [b"rent_vault"], bump)] - pub rent_vault: &'info mut UncheckedAccount, - pub system_program: &'info Program, + pub rent_vault: UncheckedAccount, + pub system_program: Program, } -#[inline(always)] -pub fn handle_create_new_account(accounts: &CreateNewAccount, rent_vault_bump: u8) -> Result<(), ProgramError> { - // Build PDA signer seeds: ["rent_vault", bump]. - let bump_bytes = [rent_vault_bump]; - let seeds: &[Seed] = &[ - Seed::from(b"rent_vault" as &[u8]), - Seed::from(&bump_bytes as &[u8]), - ]; +impl CreateNewAccount { + #[inline(always)] + pub fn create_new_account(&mut self, rent_vault_bump: u8) -> Result<(), ProgramError> { + // Build PDA signer seeds: ["rent_vault", bump]. + let bump_bytes = [rent_vault_bump]; + let seeds: &[Seed] = &[ + Seed::from(b"rent_vault" as &[u8]), + Seed::from(&bump_bytes as &[u8]), + ]; - let system_program_address = Address::default(); + let system_program_address = Address::default(); - // Create a zero-data system-owned account, funded from the vault. - accounts.system_program - .create_account_with_minimum_balance( - accounts.rent_vault, - accounts.new_account, - 0, // space: zero bytes of data - &system_program_address, - None, // fetch Rent sysvar automatically - )? - .invoke_signed(seeds) + // Create a zero-data system-owned account, funded from the vault. + self.system_program + .create_account_with_minimum_balance( + &self.rent_vault, + &self.new_account, + 0, // space: zero bytes of data + &system_program_address, + None, // fetch Rent sysvar automatically + )? + .invoke_signed(seeds) + } } diff --git a/basics/pda-rent-payer/quasar/src/instructions/init_rent_vault.rs b/basics/pda-rent-payer/quasar/src/instructions/init_rent_vault.rs index 8765f2b61..8943e9290 100644 --- a/basics/pda-rent-payer/quasar/src/instructions/init_rent_vault.rs +++ b/basics/pda-rent-payer/quasar/src/instructions/init_rent_vault.rs @@ -5,17 +5,19 @@ use quasar_lang::prelude::*; /// When lamports are sent to a new address, the system program creates /// a system-owned account automatically. #[derive(Accounts)] -pub struct InitRentVault<'info> { +pub struct InitRentVault { #[account(mut)] - pub payer: &'info Signer, + pub payer: Signer, #[account(mut, seeds = [b"rent_vault"], bump)] - pub rent_vault: &'info mut UncheckedAccount, - pub system_program: &'info Program, + pub rent_vault: UncheckedAccount, + pub system_program: Program, } -#[inline(always)] -pub fn handle_init_rent_vault(accounts: &InitRentVault, fund_lamports: u64) -> Result<(), ProgramError> { - accounts.system_program - .transfer(accounts.payer, accounts.rent_vault, fund_lamports) - .invoke() +impl InitRentVault { + #[inline(always)] + pub fn init_rent_vault(&mut self, fund_lamports: u64) -> Result<(), ProgramError> { + self.system_program + .transfer(&self.payer, &self.rent_vault, fund_lamports) + .invoke() + } } diff --git a/basics/pda-rent-payer/quasar/src/lib.rs b/basics/pda-rent-payer/quasar/src/lib.rs index 925379a55..a27ffce4a 100644 --- a/basics/pda-rent-payer/quasar/src/lib.rs +++ b/basics/pda-rent-payer/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![cfg_attr(not(test), no_std)] +#![no_std] use quasar_lang::prelude::*; @@ -16,13 +16,13 @@ mod quasar_pda_rent_payer { /// Fund a PDA "rent vault" by transferring lamports from the payer. #[instruction(discriminator = 0)] pub fn init_rent_vault(ctx: Ctx, fund_lamports: u64) -> Result<(), ProgramError> { - instructions::handle_init_rent_vault(&mut ctx.accounts, fund_lamports) + ctx.accounts.init_rent_vault(fund_lamports) } /// Create a new account using the rent vault PDA as the funding source. /// The vault signs the CPI via PDA seeds. #[instruction(discriminator = 1)] pub fn create_new_account(ctx: Ctx) -> Result<(), ProgramError> { - instructions::handle_create_new_account(&mut ctx.accounts, ctx.bumps.rent_vault) + ctx.accounts.create_new_account(ctx.bumps.rent_vault) } } diff --git a/basics/processing-instructions/quasar/Cargo.toml b/basics/processing-instructions/quasar/Cargo.toml index 751b86ffe..480403bb4 100644 --- a/basics/processing-instructions/quasar/Cargo.toml +++ b/basics/processing-instructions/quasar/Cargo.toml @@ -20,7 +20,7 @@ client = [] debug = [] [dependencies] -quasar-lang = "0.0" +quasar-lang = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } solana-instruction = { version = "3.2.0" } [dev-dependencies] diff --git a/basics/processing-instructions/quasar/src/instructions/go_to_park.rs b/basics/processing-instructions/quasar/src/instructions/go_to_park.rs index af93b6c15..1f579ce7b 100644 --- a/basics/processing-instructions/quasar/src/instructions/go_to_park.rs +++ b/basics/processing-instructions/quasar/src/instructions/go_to_park.rs @@ -3,22 +3,24 @@ use quasar_lang::prelude::*; /// Minimal accounts context — a signer is needed to submit the transaction. /// The instruction just processes instruction data (name + height). #[derive(Accounts)] -pub struct Park<'info> { +pub struct Park { #[allow(dead_code)] - pub signer: &'info Signer, + pub signer: Signer, } -#[inline(always)] -pub fn handle_go_to_park(accounts: &Park, _name: &str, height: u32) -> Result<(), ProgramError> { - // Quasar's `log()` takes &str, no format! macro available in no_std. - // We can't interpolate the name or height into the log message, so - // we use static messages — same logic as the Anchor version, just - // without formatted output. - log("Welcome to the park!"); - if height > 5 { - log("You are tall enough to ride this ride. Congratulations."); - } else { - log("You are NOT tall enough to ride this ride. Sorry mate."); +impl Park { + #[inline(always)] + pub fn go_to_park(&mut self, _name: &str, height: u32) -> Result<(), ProgramError> { + // Quasar's `log()` takes &str, no format! macro available in no_std. + // We can't interpolate the name or height into the log message, so + // we use static messages — same logic as the Anchor version, just + // without formatted output. + log("Welcome to the park!"); + if height > 5 { + log("You are tall enough to ride this ride. Congratulations."); + } else { + log("You are NOT tall enough to ride this ride. Sorry mate."); + } + Ok(()) } - Ok(()) } diff --git a/basics/processing-instructions/quasar/src/lib.rs b/basics/processing-instructions/quasar/src/lib.rs index 909d1dcc3..0f1f02331 100644 --- a/basics/processing-instructions/quasar/src/lib.rs +++ b/basics/processing-instructions/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![cfg_attr(not(test), no_std)] +#![no_std] use quasar_lang::prelude::*; @@ -18,6 +18,6 @@ mod quasar_processing_instructions { /// can't interpolate them into log messages (no format! in no_std). #[instruction(discriminator = 0)] pub fn go_to_park(ctx: Ctx, name: String, height: u32) -> Result<(), ProgramError> { - instructions::handle_go_to_park(&mut ctx.accounts, name, height) + ctx.accounts.go_to_park(name, height) } } diff --git a/basics/program-derived-addresses/quasar/Cargo.toml b/basics/program-derived-addresses/quasar/Cargo.toml index fbd8f73ef..184b18162 100644 --- a/basics/program-derived-addresses/quasar/Cargo.toml +++ b/basics/program-derived-addresses/quasar/Cargo.toml @@ -22,7 +22,7 @@ client = [] debug = [] [dependencies] -quasar-lang = "0.0" +quasar-lang = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } solana-instruction = { version = "3.2.0" } [dev-dependencies] diff --git a/basics/program-derived-addresses/quasar/src/instructions/create.rs b/basics/program-derived-addresses/quasar/src/instructions/create.rs index 29c1fda11..44cb197fd 100644 --- a/basics/program-derived-addresses/quasar/src/instructions/create.rs +++ b/basics/program-derived-addresses/quasar/src/instructions/create.rs @@ -6,16 +6,18 @@ use { /// Accounts for creating a new page visits counter. /// The counter is derived as a PDA from ["page_visits", payer] seeds. #[derive(Accounts)] -pub struct CreatePageVisits<'info> { +pub struct CreatePageVisits { #[account(mut)] - pub payer: &'info mut Signer, - #[account(mut, init, payer = payer, seeds = [b"page_visits", payer], bump)] - pub page_visits: &'info mut Account, - pub system_program: &'info Program, + pub payer: Signer, + #[account(mut, init, payer = payer, seeds = PageVisits::seeds(payer), bump)] + pub page_visits: Account, + pub system_program: Program, } -#[inline(always)] -pub fn handle_create_page_visits(accounts: &mut CreatePageVisits) -> Result<(), ProgramError> { - accounts.page_visits.set_inner(0u64); - Ok(()) +impl CreatePageVisits { + #[inline(always)] + pub fn create_page_visits(&mut self) -> Result<(), ProgramError> { + self.page_visits.set_inner(0u64); + Ok(()) + } } diff --git a/basics/program-derived-addresses/quasar/src/instructions/increment.rs b/basics/program-derived-addresses/quasar/src/instructions/increment.rs index 8925e2694..33bac0ce7 100644 --- a/basics/program-derived-addresses/quasar/src/instructions/increment.rs +++ b/basics/program-derived-addresses/quasar/src/instructions/increment.rs @@ -6,15 +6,17 @@ use { /// Accounts for incrementing page visits. /// The user account is needed to derive the PDA seeds for validation. #[derive(Accounts)] -pub struct IncrementPageVisits<'info> { - pub user: &'info UncheckedAccount, +pub struct IncrementPageVisits { + pub user: UncheckedAccount, #[account(mut)] - pub page_visits: &'info mut Account, + pub page_visits: Account, } -#[inline(always)] -pub fn handle_increment_page_visits(accounts: &mut IncrementPageVisits) -> Result<(), ProgramError> { - let current: u64 = accounts.page_visits.page_visits.into(); - accounts.page_visits.page_visits = PodU64::from(current.checked_add(1).unwrap()); - Ok(()) +impl IncrementPageVisits { + #[inline(always)] + pub fn increment_page_visits(&mut self) -> Result<(), ProgramError> { + let current: u64 = self.page_visits.page_visits.into(); + self.page_visits.page_visits = PodU64::from(current.checked_add(1).unwrap()); + Ok(()) + } } diff --git a/basics/program-derived-addresses/quasar/src/lib.rs b/basics/program-derived-addresses/quasar/src/lib.rs index 1f44e5fb5..4f20ee5ee 100644 --- a/basics/program-derived-addresses/quasar/src/lib.rs +++ b/basics/program-derived-addresses/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![cfg_attr(not(test), no_std)] +#![no_std] use quasar_lang::prelude::*; @@ -17,12 +17,12 @@ mod quasar_program_derived_addresses { /// Create a PDA-based page visits counter for the payer. #[instruction(discriminator = 0)] pub fn create_page_visits(ctx: Ctx) -> Result<(), ProgramError> { - instructions::handle_create_page_visits(&mut ctx.accounts) + ctx.accounts.create_page_visits() } /// Increment the page visits counter. #[instruction(discriminator = 1)] pub fn increment_page_visits(ctx: Ctx) -> Result<(), ProgramError> { - instructions::handle_increment_page_visits(&mut ctx.accounts) + ctx.accounts.increment_page_visits() } } diff --git a/basics/program-derived-addresses/quasar/src/state/page_visits.rs b/basics/program-derived-addresses/quasar/src/state/page_visits.rs index 9b9898af6..6333e9728 100644 --- a/basics/program-derived-addresses/quasar/src/state/page_visits.rs +++ b/basics/program-derived-addresses/quasar/src/state/page_visits.rs @@ -3,6 +3,7 @@ use quasar_lang::prelude::*; /// PDA account that tracks page visits for a user. /// Derived from seeds: ["page_visits", user_pubkey]. #[account(discriminator = 1)] +#[seeds(b"page_visits", payer: Address)] pub struct PageVisits { pub page_visits: u64, } diff --git a/basics/realloc/quasar/Cargo.toml b/basics/realloc/quasar/Cargo.toml index 4ed2077eb..e0f18ea97 100644 --- a/basics/realloc/quasar/Cargo.toml +++ b/basics/realloc/quasar/Cargo.toml @@ -20,7 +20,7 @@ client = [] debug = [] [dependencies] -quasar-lang = "0.0" +quasar-lang = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } solana-instruction = { version = "3.2.0" } [dev-dependencies] diff --git a/basics/realloc/quasar/src/instructions/initialize.rs b/basics/realloc/quasar/src/instructions/initialize.rs index 6072e9e1e..f251a7c19 100644 --- a/basics/realloc/quasar/src/instructions/initialize.rs +++ b/basics/realloc/quasar/src/instructions/initialize.rs @@ -6,19 +6,21 @@ use { /// Accounts for initialising a new message account. /// The message_account is a random keypair (not a PDA) — same as the Anchor version. #[derive(Accounts)] -pub struct Initialize<'info> { +pub struct Initialize { #[account(mut)] - pub payer: &'info mut Signer, + pub payer: Signer, #[account(mut, init, payer = payer)] - pub message_account: Account>, - pub system_program: &'info Program, + pub message_account: Account>, + pub system_program: Program, } -#[inline(always)] -pub fn handle_initialize(accounts: &mut Initialize, message: &str) -> Result<(), ProgramError> { - accounts.message_account.set_inner( - message, - accounts.payer.to_account_view(), - None, - ) +impl Initialize { + #[inline(always)] + pub fn initialize(&mut self, message: &str) -> Result<(), ProgramError> { + self.message_account.set_inner( + message, + self.payer.to_account_view(), + None, + ) + } } diff --git a/basics/realloc/quasar/src/instructions/update.rs b/basics/realloc/quasar/src/instructions/update.rs index 77ad2b665..7cabac0a3 100644 --- a/basics/realloc/quasar/src/instructions/update.rs +++ b/basics/realloc/quasar/src/instructions/update.rs @@ -7,19 +7,21 @@ use { /// Quasar's `set_inner` automatically handles realloc when the new message /// is longer than the current account data. No explicit realloc needed. #[derive(Accounts)] -pub struct Update<'info> { +pub struct Update { #[account(mut)] - pub payer: &'info mut Signer, + pub payer: Signer, #[account(mut)] - pub message_account: Account>, - pub system_program: &'info Program, + pub message_account: Account>, + pub system_program: Program, } -#[inline(always)] -pub fn handle_update(accounts: &mut Update, message: &str) -> Result<(), ProgramError> { - accounts.message_account.set_inner( - message, - accounts.payer.to_account_view(), - None, - ) +impl Update { + #[inline(always)] + pub fn update(&mut self, message: &str) -> Result<(), ProgramError> { + self.message_account.set_inner( + message, + self.payer.to_account_view(), + None, + ) + } } diff --git a/basics/realloc/quasar/src/lib.rs b/basics/realloc/quasar/src/lib.rs index 3373f864a..da21029a2 100644 --- a/basics/realloc/quasar/src/lib.rs +++ b/basics/realloc/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![cfg_attr(not(test), no_std)] +#![no_std] use quasar_lang::prelude::*; @@ -17,13 +17,13 @@ mod quasar_realloc { /// Create a message account with an initial message. #[instruction(discriminator = 0)] pub fn initialize(ctx: Ctx, message: String) -> Result<(), ProgramError> { - instructions::handle_initialize(&mut ctx.accounts, message) + ctx.accounts.initialize(message) } /// Update the message, reallocating if the new message is longer. /// Quasar's `set_inner` handles realloc transparently. #[instruction(discriminator = 1)] pub fn update(ctx: Ctx, message: String) -> Result<(), ProgramError> { - instructions::handle_update(&mut ctx.accounts, message) + ctx.accounts.update(message) } } diff --git a/basics/rent/quasar/Cargo.toml b/basics/rent/quasar/Cargo.toml index 7b99dbfad..cfb1c9006 100644 --- a/basics/rent/quasar/Cargo.toml +++ b/basics/rent/quasar/Cargo.toml @@ -22,7 +22,7 @@ client = [] debug = [] [dependencies] -quasar-lang = "0.0" +quasar-lang = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } solana-instruction = { version = "3.2.0" } [dev-dependencies] diff --git a/basics/rent/quasar/src/instructions/create_system_account.rs b/basics/rent/quasar/src/instructions/create_system_account.rs index 1f26b493e..a4170e0e9 100644 --- a/basics/rent/quasar/src/instructions/create_system_account.rs +++ b/basics/rent/quasar/src/instructions/create_system_account.rs @@ -2,41 +2,40 @@ use quasar_lang::prelude::*; /// Accounts for creating a system account sized for address data. #[derive(Accounts)] -pub struct CreateSystemAccount<'info> { +pub struct CreateSystemAccount { #[account(mut)] - pub payer: &'info Signer, + pub payer: Signer, #[account(mut)] - pub new_account: &'info Signer, - pub system_program: &'info Program, + pub new_account: Signer, + pub system_program: Program, } -#[inline(always)] -pub fn handle_create_system_account( - accounts: &CreateSystemAccount, name: &str, - address: &str, -) -> Result<(), ProgramError> { - // Calculate space needed for the serialised AddressData: - // borsh-style: 4-byte length prefix + bytes for each String field. - let space = 4 + name.len() + 4 + address.len(); +impl CreateSystemAccount { + #[inline(always)] + pub fn create_system_account(&mut self, name: &str, address: &str) -> Result<(), ProgramError> { + // Calculate space needed for the serialised AddressData: + // borsh-style: 4-byte length prefix + bytes for each String field. + let space = 4 + name.len() + 4 + address.len(); - log("Program invoked. Creating a system account..."); + log("Program invoked. Creating a system account..."); - // The owner of the new account is the system program. - let system_program_address = Address::default(); + // The owner of the new account is the system program. + let system_program_address = Address::default(); - // Create the account with the computed space. - // create_account_with_minimum_balance automatically fetches Rent - // sysvar and calculates the minimum rent-exempt lamports. - accounts.system_program - .create_account_with_minimum_balance( - accounts.payer, - accounts.new_account, - space as u64, - &system_program_address, - None, // fetch Rent sysvar automatically - )? - .invoke()?; + // Create the account with the computed space. + // create_account_with_minimum_balance automatically fetches Rent + // sysvar and calculates the minimum rent-exempt lamports. + self.system_program + .create_account_with_minimum_balance( + &self.payer, + &self.new_account, + space as u64, + &system_program_address, + None, // fetch Rent sysvar automatically + )? + .invoke()?; - log("Account created successfully."); - Ok(()) + log("Account created successfully."); + Ok(()) + } } diff --git a/basics/rent/quasar/src/lib.rs b/basics/rent/quasar/src/lib.rs index 854b3945f..fce2f678a 100644 --- a/basics/rent/quasar/src/lib.rs +++ b/basics/rent/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![cfg_attr(not(test), no_std)] +#![no_std] use quasar_lang::prelude::*; @@ -25,6 +25,6 @@ mod quasar_rent { name: String, address: String, ) -> Result<(), ProgramError> { - instructions::handle_create_system_account(&mut ctx.accounts, name, address) + ctx.accounts.create_system_account(name, address) } } diff --git a/basics/repository-layout/quasar/Cargo.toml b/basics/repository-layout/quasar/Cargo.toml index 5699b68bf..50c1ebc2d 100644 --- a/basics/repository-layout/quasar/Cargo.toml +++ b/basics/repository-layout/quasar/Cargo.toml @@ -22,7 +22,7 @@ client = [] debug = [] [dependencies] -quasar-lang = "0.0" +quasar-lang = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } solana-instruction = { version = "3.2.0" } [dev-dependencies] diff --git a/basics/repository-layout/quasar/src/instructions/carnival_context.rs b/basics/repository-layout/quasar/src/instructions/carnival_context.rs index 095d39230..7c60b2d1d 100644 --- a/basics/repository-layout/quasar/src/instructions/carnival_context.rs +++ b/basics/repository-layout/quasar/src/instructions/carnival_context.rs @@ -5,35 +5,37 @@ use super::{eat_food, get_on_ride, play_game}; /// Minimal accounts context — a signer submits the transaction. /// The instructions just process instruction data (no onchain state). #[derive(Accounts)] -pub struct CarnivalContext<'info> { +pub struct CarnivalContext { #[allow(dead_code)] - pub payer: &'info Signer, + pub payer: Signer, } -#[inline(always)] -pub fn handle_go_on_ride( - accounts: &CarnivalContext, name: &str, - height: u32, - ticket_count: u32, - ride_name: &str, -) -> Result<(), ProgramError> { - get_on_ride::get_on_ride(name, height, ticket_count, ride_name) -} +impl CarnivalContext { + #[inline(always)] + pub fn go_on_ride( + &mut self, name: &str, + height: u32, + ticket_count: u32, + ride_name: &str, + ) -> Result<(), ProgramError> { + get_on_ride::get_on_ride(name, height, ticket_count, ride_name) + } -#[inline(always)] -pub fn handle_play_game( - accounts: &CarnivalContext, name: &str, - ticket_count: u32, - game_name: &str, -) -> Result<(), ProgramError> { - play_game::play_game(name, ticket_count, game_name) -} + #[inline(always)] + pub fn play_game( + &mut self, name: &str, + ticket_count: u32, + game_name: &str, + ) -> Result<(), ProgramError> { + play_game::play_game(name, ticket_count, game_name) + } -#[inline(always)] -pub fn handle_eat_food( - accounts: &CarnivalContext, name: &str, - ticket_count: u32, - food_stand_name: &str, -) -> Result<(), ProgramError> { - eat_food::eat_food(name, ticket_count, food_stand_name) + #[inline(always)] + pub fn eat_food( + &mut self, name: &str, + ticket_count: u32, + food_stand_name: &str, + ) -> Result<(), ProgramError> { + eat_food::eat_food(name, ticket_count, food_stand_name) + } } diff --git a/basics/repository-layout/quasar/src/lib.rs b/basics/repository-layout/quasar/src/lib.rs index 43ecd62de..2b67aae96 100644 --- a/basics/repository-layout/quasar/src/lib.rs +++ b/basics/repository-layout/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![cfg_attr(not(test), no_std)] +#![no_std] use quasar_lang::prelude::*; @@ -23,7 +23,7 @@ mod quasar_carnival { ticket_count: u32, ride_name: String, ) -> Result<(), ProgramError> { - instructions::handle_go_on_ride(&mut ctx.accounts, name, height, ticket_count, ride_name) + ctx.accounts.go_on_ride(name, height, ticket_count, ride_name) } /// Play a carnival game. Validates ticket requirements. @@ -34,7 +34,7 @@ mod quasar_carnival { ticket_count: u32, game_name: String, ) -> Result<(), ProgramError> { - instructions::handle_play_game(&mut ctx.accounts, name, ticket_count, game_name) + ctx.accounts.play_game(name, ticket_count, game_name) } /// Eat at a carnival food stand. Validates ticket requirements. @@ -45,6 +45,6 @@ mod quasar_carnival { ticket_count: u32, food_stand_name: String, ) -> Result<(), ProgramError> { - instructions::handle_eat_food(&mut ctx.accounts, name, ticket_count, food_stand_name) + ctx.accounts.eat_food(name, ticket_count, food_stand_name) } } diff --git a/basics/transfer-sol/quasar/Cargo.toml b/basics/transfer-sol/quasar/Cargo.toml index afb62d252..934ed396e 100644 --- a/basics/transfer-sol/quasar/Cargo.toml +++ b/basics/transfer-sol/quasar/Cargo.toml @@ -22,7 +22,7 @@ client = [] debug = [] [dependencies] -quasar-lang = "0.0" +quasar-lang = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } solana-instruction = { version = "3.2.0" } [dev-dependencies] diff --git a/basics/transfer-sol/quasar/src/instructions/transfer_sol_with_cpi.rs b/basics/transfer-sol/quasar/src/instructions/transfer_sol_with_cpi.rs index b74aafdad..81be8a541 100644 --- a/basics/transfer-sol/quasar/src/instructions/transfer_sol_with_cpi.rs +++ b/basics/transfer-sol/quasar/src/instructions/transfer_sol_with_cpi.rs @@ -2,17 +2,19 @@ use quasar_lang::prelude::*; /// Accounts for transferring SOL via system program CPI. #[derive(Accounts)] -pub struct TransferSolWithCpi<'info> { +pub struct TransferSolWithCpi { #[account(mut)] - pub payer: &'info Signer, + pub payer: Signer, #[account(mut)] - pub recipient: &'info UncheckedAccount, - pub system_program: &'info Program, + pub recipient: UncheckedAccount, + pub system_program: Program, } -#[inline(always)] -pub fn handle_transfer_sol_with_cpi(accounts: &TransferSolWithCpi, amount: u64) -> Result<(), ProgramError> { - accounts.system_program - .transfer(accounts.payer, accounts.recipient, amount) - .invoke() +impl TransferSolWithCpi { + #[inline(always)] + pub fn transfer_sol_with_cpi(&mut self, amount: u64) -> Result<(), ProgramError> { + self.system_program + .transfer(&self.payer, &self.recipient, amount) + .invoke() + } } diff --git a/basics/transfer-sol/quasar/src/instructions/transfer_sol_with_program.rs b/basics/transfer-sol/quasar/src/instructions/transfer_sol_with_program.rs index 5aea3d670..55615a108 100644 --- a/basics/transfer-sol/quasar/src/instructions/transfer_sol_with_program.rs +++ b/basics/transfer-sol/quasar/src/instructions/transfer_sol_with_program.rs @@ -3,18 +3,20 @@ use quasar_lang::prelude::*; /// Accounts for transferring SOL by directly manipulating lamports. /// The payer account must be owned by this program for direct lamport access. #[derive(Accounts)] -pub struct TransferSolWithProgram<'info> { +pub struct TransferSolWithProgram { #[account(mut)] - pub payer: &'info UncheckedAccount, + pub payer: UncheckedAccount, #[account(mut)] - pub recipient: &'info UncheckedAccount, + pub recipient: UncheckedAccount, } -#[inline(always)] -pub fn handle_transfer_sol_with_program(accounts: &TransferSolWithProgram, amount: u64) -> Result<(), ProgramError> { - let payer_view = accounts.payer.to_account_view(); - let recipient_view = accounts.recipient.to_account_view(); - set_lamports(payer_view, payer_view.lamports() - amount); - set_lamports(recipient_view, recipient_view.lamports() + amount); - Ok(()) +impl TransferSolWithProgram { + #[inline(always)] + pub fn transfer_sol_with_program(&mut self, amount: u64) -> Result<(), ProgramError> { + let payer_view = self.payer.to_account_view(); + let recipient_view = self.recipient.to_account_view(); + set_lamports(payer_view, payer_view.lamports() - amount); + set_lamports(recipient_view, recipient_view.lamports() + amount); + Ok(()) + } } diff --git a/basics/transfer-sol/quasar/src/lib.rs b/basics/transfer-sol/quasar/src/lib.rs index 6942be4d8..7e66ed75b 100644 --- a/basics/transfer-sol/quasar/src/lib.rs +++ b/basics/transfer-sol/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![cfg_attr(not(test), no_std)] +#![no_std] use quasar_lang::prelude::*; @@ -19,7 +19,7 @@ mod quasar_transfer_sol { ctx: Ctx, amount: u64, ) -> Result<(), ProgramError> { - instructions::handle_transfer_sol_with_cpi(&mut ctx.accounts, amount) + ctx.accounts.transfer_sol_with_cpi(amount) } /// Transfer SOL by directly manipulating lamports. @@ -29,6 +29,6 @@ mod quasar_transfer_sol { ctx: Ctx, amount: u64, ) -> Result<(), ProgramError> { - instructions::handle_transfer_sol_with_program(&mut ctx.accounts, amount) + ctx.accounts.transfer_sol_with_program(amount) } } diff --git a/compression/cnft-burn/quasar/Cargo.toml b/compression/cnft-burn/quasar/Cargo.toml index 69f8a46a3..21b7aaa66 100644 --- a/compression/cnft-burn/quasar/Cargo.toml +++ b/compression/cnft-burn/quasar/Cargo.toml @@ -22,7 +22,7 @@ client = [] debug = [] [dependencies] -quasar-lang = "0.0" +quasar-lang = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } # Direct dependency for invoke_with_bounds — needed for raw CPI with variable # proof accounts. quasar-lang re-exports types but not the invoke functions. solana-instruction-view = { version = "2", features = ["cpi"] } diff --git a/compression/cnft-burn/quasar/src/instructions/burn_cnft.rs b/compression/cnft-burn/quasar/src/instructions/burn_cnft.rs index 5775c4b7e..a7a83bcdc 100644 --- a/compression/cnft-burn/quasar/src/instructions/burn_cnft.rs +++ b/compression/cnft-burn/quasar/src/instructions/burn_cnft.rs @@ -10,104 +10,104 @@ const MAX_CPI_ACCOUNTS: usize = 7 + MAX_PROOF_NODES; /// Accounts for burning a compressed NFT via mpl-bubblegum CPI. #[derive(Accounts)] -pub struct BurnCnft<'info> { +pub struct BurnCnft { #[account(mut)] - pub leaf_owner: &'info Signer, + pub leaf_owner: Signer, /// Tree authority PDA (seeds checked by Bubblegum). #[account(mut)] - pub tree_authority: &'info UncheckedAccount, + pub tree_authority: UncheckedAccount, /// Merkle tree account modified by the compression program. #[account(mut)] - pub merkle_tree: &'info UncheckedAccount, + pub merkle_tree: UncheckedAccount, /// SPL Noop log wrapper. - pub log_wrapper: &'info UncheckedAccount, + pub log_wrapper: UncheckedAccount, /// SPL Account Compression program. #[account(address = SPL_ACCOUNT_COMPRESSION_ID)] - pub compression_program: &'info UncheckedAccount, + pub compression_program: UncheckedAccount, /// mpl-bubblegum program. #[account(address = MPL_BUBBLEGUM_ID)] - pub bubblegum_program: &'info UncheckedAccount, - pub system_program: &'info Program, + pub bubblegum_program: UncheckedAccount, + pub system_program: Program, } -pub fn handle_burn_cnft<'info>( - accounts: &BurnCnft<'info>, ctx: &CtxWithRemaining<'info, BurnCnft<'info>>, -) -> Result<(), ProgramError> { - // Parse instruction args from raw data: - // root(32) + data_hash(32) + creator_hash(32) + nonce(8) + index(4) = 108 bytes - let data = ctx.data; - if data.len() < 108 { - return Err(ProgramError::InvalidInstructionData); - } +impl BurnCnft { + pub fn burn_cnft(&mut self, ctx: &CtxWithRemaining) -> Result<(), ProgramError> { + // Parse instruction args from raw data: + // root(32) + data_hash(32) + creator_hash(32) + nonce(8) + index(4) = 108 bytes + let data = ctx.data; + if data.len() < 108 { + return Err(ProgramError::InvalidInstructionData); + } - // Build instruction data: discriminator + args - // 8 + 32 + 32 + 32 + 8 + 4 = 116 bytes - let mut ix_data = [0u8; 116]; - ix_data[0..8].copy_from_slice(&BURN_DISCRIMINATOR); - ix_data[8..116].copy_from_slice(&data[0..108]); + // Build instruction data: discriminator + args + // 8 + 32 + 32 + 32 + 8 + 4 = 116 bytes + let mut ix_data = [0u8; 116]; + ix_data[0..8].copy_from_slice(&BURN_DISCRIMINATOR); + ix_data[8..116].copy_from_slice(&data[0..108]); - // Collect remaining accounts (proof nodes) into a stack buffer - let remaining = ctx.remaining_accounts(); - let placeholder = accounts.system_program.to_account_view().clone(); - let mut proof_views: [AccountView; MAX_PROOF_NODES] = - core::array::from_fn(|_| placeholder.clone()); - let mut proof_count = 0usize; - for result in remaining.iter() { - if proof_count >= MAX_PROOF_NODES { - break; + // Collect remaining accounts (proof nodes) into a stack buffer + let remaining = ctx.remaining_accounts(); + let placeholder = self.system_program.to_account_view().clone(); + let mut proof_views: [AccountView; MAX_PROOF_NODES] = + core::array::from_fn(|_| placeholder.clone()); + let mut proof_count = 0usize; + for result in remaining.iter() { + if proof_count >= MAX_PROOF_NODES { + break; + } + proof_views[proof_count] = result?; + proof_count += 1; } - proof_views[proof_count] = result?; - proof_count += 1; - } - let total_accounts = 7 + proof_count; + let total_accounts = 7 + proof_count; - // Build instruction account metas. - // Layout matches mpl-bubblegum Burn: tree_authority, leaf_owner (signer), - // leaf_delegate (= leaf_owner, not signer), merkle_tree, log_wrapper, - // compression_program, system_program, then proof nodes. - let sys_addr = accounts.system_program.address(); - let mut ix_accounts: [InstructionAccount; MAX_CPI_ACCOUNTS] = core::array::from_fn(|_| { - InstructionAccount::readonly(sys_addr) - }); + // Build instruction account metas. + // Layout matches mpl-bubblegum Burn: tree_authority, leaf_owner (signer), + // leaf_delegate (= leaf_owner, not signer), merkle_tree, log_wrapper, + // compression_program, system_program, then proof nodes. + let sys_addr = self.system_program.address(); + let mut ix_accounts: [InstructionAccount; MAX_CPI_ACCOUNTS] = core::array::from_fn(|_| { + InstructionAccount::readonly(sys_addr) + }); - ix_accounts[0] = InstructionAccount::readonly(accounts.tree_authority.address()); - ix_accounts[1] = InstructionAccount::readonly_signer(accounts.leaf_owner.address()); - // leaf_delegate = leaf_owner, not a signer in this call - ix_accounts[2] = InstructionAccount::readonly(accounts.leaf_owner.address()); - ix_accounts[3] = InstructionAccount::writable(accounts.merkle_tree.address()); - ix_accounts[4] = InstructionAccount::readonly(accounts.log_wrapper.address()); - ix_accounts[5] = InstructionAccount::readonly(accounts.compression_program.address()); - ix_accounts[6] = InstructionAccount::readonly(accounts.system_program.address()); + ix_accounts[0] = InstructionAccount::readonly(self.tree_authority.address()); + ix_accounts[1] = InstructionAccount::readonly_signer(self.leaf_owner.address()); + // leaf_delegate = leaf_owner, not a signer in this call + ix_accounts[2] = InstructionAccount::readonly(self.leaf_owner.address()); + ix_accounts[3] = InstructionAccount::writable(self.merkle_tree.address()); + ix_accounts[4] = InstructionAccount::readonly(self.log_wrapper.address()); + ix_accounts[5] = InstructionAccount::readonly(self.compression_program.address()); + ix_accounts[6] = InstructionAccount::readonly(self.system_program.address()); - for i in 0..proof_count { - ix_accounts[7 + i] = InstructionAccount::readonly(proof_views[i].address()); - } + for i in 0..proof_count { + ix_accounts[7 + i] = InstructionAccount::readonly(proof_views[i].address()); + } - // Build account views array for the CPI - let sys_view = accounts.system_program.to_account_view().clone(); - let mut views: [AccountView; MAX_CPI_ACCOUNTS] = core::array::from_fn(|_| sys_view.clone()); + // Build account views array for the CPI + let sys_view = self.system_program.to_account_view().clone(); + let mut views: [AccountView; MAX_CPI_ACCOUNTS] = core::array::from_fn(|_| sys_view.clone()); - views[0] = accounts.tree_authority.to_account_view().clone(); - views[1] = accounts.leaf_owner.to_account_view().clone(); - views[2] = accounts.leaf_owner.to_account_view().clone(); // leaf_delegate = leaf_owner - views[3] = accounts.merkle_tree.to_account_view().clone(); - views[4] = accounts.log_wrapper.to_account_view().clone(); - views[5] = accounts.compression_program.to_account_view().clone(); - views[6] = accounts.system_program.to_account_view().clone(); + views[0] = self.tree_authority.to_account_view().clone(); + views[1] = self.leaf_owner.to_account_view().clone(); + views[2] = self.leaf_owner.to_account_view().clone(); // leaf_delegate = leaf_owner + views[3] = self.merkle_tree.to_account_view().clone(); + views[4] = self.log_wrapper.to_account_view().clone(); + views[5] = self.compression_program.to_account_view().clone(); + views[6] = self.system_program.to_account_view().clone(); - for i in 0..proof_count { - views[7 + i] = proof_views[i].clone(); - } + for i in 0..proof_count { + views[7 + i] = proof_views[i].clone(); + } - let instruction = InstructionView { - program_id: &MPL_BUBBLEGUM_ID, - data: &ix_data, - accounts: &ix_accounts[..total_accounts], - }; + let instruction = InstructionView { + program_id: &MPL_BUBBLEGUM_ID, + data: &ix_data, + accounts: &ix_accounts[..total_accounts], + }; - solana_instruction_view::cpi::invoke_with_bounds::( - &instruction, - &views[..total_accounts], - ) + solana_instruction_view::cpi::invoke_with_bounds::( + &instruction, + &views[..total_accounts], + ) + } } diff --git a/compression/cnft-burn/quasar/src/lib.rs b/compression/cnft-burn/quasar/src/lib.rs index fb170d22c..be63732b1 100644 --- a/compression/cnft-burn/quasar/src/lib.rs +++ b/compression/cnft-burn/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![cfg_attr(not(test), no_std)] +#![no_std] use quasar_lang::prelude::*; @@ -32,6 +32,6 @@ mod quasar_cnft_burn { #[instruction(discriminator = 0)] pub fn burn_cnft(ctx: CtxWithRemaining) -> Result<(), ProgramError> { - instructions::handle_burn_cnft(&ctx.accounts, &ctx) + ctx.accounts.burn_cnft(&ctx) } } diff --git a/compression/cnft-vault/quasar/Cargo.toml b/compression/cnft-vault/quasar/Cargo.toml index 2f89cfebb..60f3b06db 100644 --- a/compression/cnft-vault/quasar/Cargo.toml +++ b/compression/cnft-vault/quasar/Cargo.toml @@ -22,7 +22,7 @@ client = [] debug = [] [dependencies] -quasar-lang = "0.0" +quasar-lang = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } # Direct dependency for invoke_signed_with_bounds — needed for raw CPI with # variable proof accounts. quasar-lang re-exports types but not the invoke fns. solana-instruction-view = { version = "2", features = ["cpi"] } diff --git a/compression/cnft-vault/quasar/src/instructions/withdraw.rs b/compression/cnft-vault/quasar/src/instructions/withdraw.rs index 56014c0e1..bbdaeaa23 100644 --- a/compression/cnft-vault/quasar/src/instructions/withdraw.rs +++ b/compression/cnft-vault/quasar/src/instructions/withdraw.rs @@ -12,27 +12,27 @@ const TRANSFER_ARGS_LEN: usize = 108; /// Accounts for withdrawing a single compressed NFT from the vault. #[derive(Accounts)] -pub struct Withdraw<'info> { +pub struct Withdraw { /// Tree authority PDA (seeds checked by Bubblegum). #[account(mut)] - pub tree_authority: &'info UncheckedAccount, + pub tree_authority: UncheckedAccount, /// Vault PDA that owns the cNFT — signs the transfer via invoke_signed. #[account(seeds = [b"cNFT-vault"], bump)] - pub leaf_owner: &'info UncheckedAccount, + pub leaf_owner: UncheckedAccount, /// New owner to receive the cNFT. - pub new_leaf_owner: &'info UncheckedAccount, + pub new_leaf_owner: UncheckedAccount, /// Merkle tree account. #[account(mut)] - pub merkle_tree: &'info UncheckedAccount, + pub merkle_tree: UncheckedAccount, /// SPL Noop log wrapper. - pub log_wrapper: &'info UncheckedAccount, + pub log_wrapper: UncheckedAccount, /// SPL Account Compression program. #[account(address = SPL_ACCOUNT_COMPRESSION_ID)] - pub compression_program: &'info UncheckedAccount, + pub compression_program: UncheckedAccount, /// mpl-bubblegum program. #[account(address = MPL_BUBBLEGUM_ID)] - pub bubblegum_program: &'info UncheckedAccount, - pub system_program: &'info Program, + pub bubblegum_program: UncheckedAccount, + pub system_program: Program, } /// Build mpl-bubblegum Transfer instruction data from raw args. @@ -43,88 +43,88 @@ fn build_transfer_data(args: &[u8]) -> [u8; 8 + TRANSFER_ARGS_LEN] { ix_data } -pub fn handle_withdraw_cnft<'info>( - accounts: &Withdraw<'info>, ctx: &CtxWithRemaining<'info, Withdraw<'info>>, -) -> Result<(), ProgramError> { - let data = ctx.data; - if data.len() < TRANSFER_ARGS_LEN { - return Err(ProgramError::InvalidInstructionData); - } +impl Withdraw { + pub fn withdraw_cnft(&mut self, ctx: &CtxWithRemaining) -> Result<(), ProgramError> { + let data = ctx.data; + if data.len() < TRANSFER_ARGS_LEN { + return Err(ProgramError::InvalidInstructionData); + } - let ix_data = build_transfer_data(&data[0..TRANSFER_ARGS_LEN]); - - // Collect proof nodes - let remaining = ctx.remaining_accounts(); - let placeholder = accounts.system_program.to_account_view().clone(); - let mut proof_views: [AccountView; MAX_PROOF_NODES] = - core::array::from_fn(|_| placeholder.clone()); - let mut proof_count = 0usize; - for result in remaining.iter() { - if proof_count >= MAX_PROOF_NODES { - break; + let ix_data = build_transfer_data(&data[0..TRANSFER_ARGS_LEN]); + + // Collect proof nodes + let remaining = ctx.remaining_accounts(); + let placeholder = self.system_program.to_account_view().clone(); + let mut proof_views: [AccountView; MAX_PROOF_NODES] = + core::array::from_fn(|_| placeholder.clone()); + let mut proof_count = 0usize; + for result in remaining.iter() { + if proof_count >= MAX_PROOF_NODES { + break; + } + proof_views[proof_count] = result?; + proof_count += 1; } - proof_views[proof_count] = result?; - proof_count += 1; - } - let total_accounts = 8 + proof_count; - - // Build instruction account metas matching mpl-bubblegum Transfer layout: - // tree_config, leaf_owner (signer/PDA), leaf_delegate, new_leaf_owner, - // merkle_tree, log_wrapper, compression_program, system_program, then proofs. - let sys_addr = accounts.system_program.address(); - let mut ix_accounts: [InstructionAccount; MAX_CPI_ACCOUNTS] = - core::array::from_fn(|_| InstructionAccount::readonly(sys_addr)); - - ix_accounts[0] = InstructionAccount::readonly(accounts.tree_authority.address()); - ix_accounts[1] = InstructionAccount::readonly_signer(accounts.leaf_owner.address()); - // leaf_delegate = leaf_owner, not an additional signer - ix_accounts[2] = InstructionAccount::readonly(accounts.leaf_owner.address()); - ix_accounts[3] = InstructionAccount::readonly(accounts.new_leaf_owner.address()); - ix_accounts[4] = InstructionAccount::writable(accounts.merkle_tree.address()); - ix_accounts[5] = InstructionAccount::readonly(accounts.log_wrapper.address()); - ix_accounts[6] = InstructionAccount::readonly(accounts.compression_program.address()); - ix_accounts[7] = InstructionAccount::readonly(accounts.system_program.address()); - - for i in 0..proof_count { - ix_accounts[8 + i] = InstructionAccount::readonly(proof_views[i].address()); - } + let total_accounts = 8 + proof_count; + + // Build instruction account metas matching mpl-bubblegum Transfer layout: + // tree_config, leaf_owner (signer/PDA), leaf_delegate, new_leaf_owner, + // merkle_tree, log_wrapper, compression_program, system_program, then proofs. + let sys_addr = self.system_program.address(); + let mut ix_accounts: [InstructionAccount; MAX_CPI_ACCOUNTS] = + core::array::from_fn(|_| InstructionAccount::readonly(sys_addr)); + + ix_accounts[0] = InstructionAccount::readonly(self.tree_authority.address()); + ix_accounts[1] = InstructionAccount::readonly_signer(self.leaf_owner.address()); + // leaf_delegate = leaf_owner, not an additional signer + ix_accounts[2] = InstructionAccount::readonly(self.leaf_owner.address()); + ix_accounts[3] = InstructionAccount::readonly(self.new_leaf_owner.address()); + ix_accounts[4] = InstructionAccount::writable(self.merkle_tree.address()); + ix_accounts[5] = InstructionAccount::readonly(self.log_wrapper.address()); + ix_accounts[6] = InstructionAccount::readonly(self.compression_program.address()); + ix_accounts[7] = InstructionAccount::readonly(self.system_program.address()); + + for i in 0..proof_count { + ix_accounts[8 + i] = InstructionAccount::readonly(proof_views[i].address()); + } - // Build account views - let sys_view = accounts.system_program.to_account_view().clone(); - let mut views: [AccountView; MAX_CPI_ACCOUNTS] = - core::array::from_fn(|_| sys_view.clone()); - - views[0] = accounts.tree_authority.to_account_view().clone(); - views[1] = accounts.leaf_owner.to_account_view().clone(); - views[2] = accounts.leaf_owner.to_account_view().clone(); - views[3] = accounts.new_leaf_owner.to_account_view().clone(); - views[4] = accounts.merkle_tree.to_account_view().clone(); - views[5] = accounts.log_wrapper.to_account_view().clone(); - views[6] = accounts.compression_program.to_account_view().clone(); - views[7] = accounts.system_program.to_account_view().clone(); - - for i in 0..proof_count { - views[8 + i] = proof_views[i].clone(); - } + // Build account views + let sys_view = self.system_program.to_account_view().clone(); + let mut views: [AccountView; MAX_CPI_ACCOUNTS] = + core::array::from_fn(|_| sys_view.clone()); + + views[0] = self.tree_authority.to_account_view().clone(); + views[1] = self.leaf_owner.to_account_view().clone(); + views[2] = self.leaf_owner.to_account_view().clone(); + views[3] = self.new_leaf_owner.to_account_view().clone(); + views[4] = self.merkle_tree.to_account_view().clone(); + views[5] = self.log_wrapper.to_account_view().clone(); + views[6] = self.compression_program.to_account_view().clone(); + views[7] = self.system_program.to_account_view().clone(); + + for i in 0..proof_count { + views[8 + i] = proof_views[i].clone(); + } - let instruction = InstructionView { - program_id: &MPL_BUBBLEGUM_ID, - data: &ix_data, - accounts: &ix_accounts[..total_accounts], - }; - - // PDA signer seeds: ["cNFT-vault", bump] - let bump_bytes = [ctx.bumps.leaf_owner]; - let seeds: [Seed; 2] = [ - Seed::from(b"cNFT-vault" as &[u8]), - Seed::from(&bump_bytes as &[u8]), - ]; - let signer = Signer::from(&seeds as &[Seed]); - - solana_instruction_view::cpi::invoke_signed_with_bounds::( - &instruction, - &views[..total_accounts], - &[signer], - ) + let instruction = InstructionView { + program_id: &MPL_BUBBLEGUM_ID, + data: &ix_data, + accounts: &ix_accounts[..total_accounts], + }; + + // PDA signer seeds: ["cNFT-vault", bump] + let bump_bytes = [ctx.bumps.leaf_owner]; + let seeds: [Seed; 2] = [ + Seed::from(b"cNFT-vault" as &[u8]), + Seed::from(&bump_bytes as &[u8]), + ]; + let signer = Signer::from(&seeds as &[Seed]); + + solana_instruction_view::cpi::invoke_signed_with_bounds::( + &instruction, + &views[..total_accounts], + &[signer], + ) + } } diff --git a/compression/cnft-vault/quasar/src/instructions/withdraw_two.rs b/compression/cnft-vault/quasar/src/instructions/withdraw_two.rs index c2fc09a7a..e717ae0fb 100644 --- a/compression/cnft-vault/quasar/src/instructions/withdraw_two.rs +++ b/compression/cnft-vault/quasar/src/instructions/withdraw_two.rs @@ -13,189 +13,189 @@ const TRANSFER_ARGS_LEN: usize = 108; /// Accounts for withdrawing two compressed NFTs from the vault in one transaction. /// Each cNFT can be from a different merkle tree. #[derive(Accounts)] -pub struct WithdrawTwo<'info> { +pub struct WithdrawTwo { /// Tree authority PDA for tree 1. #[account(mut)] - pub tree_authority1: &'info UncheckedAccount, + pub tree_authority1: UncheckedAccount, /// Vault PDA that owns the cNFTs — signs both transfers. #[account(seeds = [b"cNFT-vault"], bump)] - pub leaf_owner: &'info UncheckedAccount, + pub leaf_owner: UncheckedAccount, /// Recipient for cNFT 1. - pub new_leaf_owner1: &'info UncheckedAccount, + pub new_leaf_owner1: UncheckedAccount, /// Merkle tree for cNFT 1. #[account(mut)] - pub merkle_tree1: &'info UncheckedAccount, + pub merkle_tree1: UncheckedAccount, /// Tree authority PDA for tree 2. #[account(mut)] - pub tree_authority2: &'info UncheckedAccount, + pub tree_authority2: UncheckedAccount, /// Recipient for cNFT 2. - pub new_leaf_owner2: &'info UncheckedAccount, + pub new_leaf_owner2: UncheckedAccount, /// Merkle tree for cNFT 2. #[account(mut)] - pub merkle_tree2: &'info UncheckedAccount, + pub merkle_tree2: UncheckedAccount, /// SPL Noop log wrapper. - pub log_wrapper: &'info UncheckedAccount, + pub log_wrapper: UncheckedAccount, /// SPL Account Compression program. #[account(address = SPL_ACCOUNT_COMPRESSION_ID)] - pub compression_program: &'info UncheckedAccount, + pub compression_program: UncheckedAccount, /// mpl-bubblegum program. #[account(address = MPL_BUBBLEGUM_ID)] - pub bubblegum_program: &'info UncheckedAccount, - pub system_program: &'info Program, + pub bubblegum_program: UncheckedAccount, + pub system_program: Program, } -#[allow(clippy::too_many_lines)] -pub fn handle_withdraw_two_cnfts<'info>( - accounts: &WithdrawTwo<'info>, ctx: &CtxWithRemaining<'info, WithdrawTwo<'info>>, -) -> Result<(), ProgramError> { - // Parse instruction args: - // args1(108) + proof_1_length(1) + args2(108) + _proof_2_length(1) = 218 bytes - let data = ctx.data; - if data.len() < 218 { - return Err(ProgramError::InvalidInstructionData); - } - - let args1 = &data[0..TRANSFER_ARGS_LEN]; - let proof_1_length = data[TRANSFER_ARGS_LEN] as usize; - let args2 = &data[TRANSFER_ARGS_LEN + 1..TRANSFER_ARGS_LEN * 2 + 1]; - // _proof_2_length at data[217] — not needed, remaining after proof1 is proof2 - - // PDA signer seeds - let bump_bytes = [ctx.bumps.leaf_owner]; - let seeds: [Seed; 2] = [ - Seed::from(b"cNFT-vault" as &[u8]), - Seed::from(&bump_bytes as &[u8]), - ]; - let signer = Signer::from(&seeds as &[Seed]); - - // Collect all remaining accounts (proof1 ++ proof2) - let remaining = ctx.remaining_accounts(); - let placeholder = accounts.system_program.to_account_view().clone(); - let mut all_proofs: [AccountView; MAX_PROOF_NODES * 2] = - core::array::from_fn(|_| placeholder.clone()); - let mut total_proofs = 0usize; - for result in remaining.iter() { - if total_proofs >= MAX_PROOF_NODES * 2 { - break; - } - all_proofs[total_proofs] = result?; - total_proofs += 1; - } - - // Split into proof1 and proof2 - let proof1_count = proof_1_length.min(total_proofs); - let proof2_count = total_proofs.saturating_sub(proof1_count); - - // --- Withdraw cNFT #1 --- - log("withdrawing cNFT#1"); - { - let mut ix_data = [0u8; 8 + TRANSFER_ARGS_LEN]; - ix_data[0..8].copy_from_slice(&TRANSFER_DISCRIMINATOR); - ix_data[8..].copy_from_slice(args1); - - let total_accounts = 8 + proof1_count; - let sys_addr = accounts.system_program.address(); - let mut ix_accounts: [InstructionAccount; MAX_CPI_ACCOUNTS] = - core::array::from_fn(|_| InstructionAccount::readonly(sys_addr)); - - ix_accounts[0] = InstructionAccount::readonly(accounts.tree_authority1.address()); - ix_accounts[1] = InstructionAccount::readonly_signer(accounts.leaf_owner.address()); - ix_accounts[2] = InstructionAccount::readonly(accounts.leaf_owner.address()); - ix_accounts[3] = InstructionAccount::readonly(accounts.new_leaf_owner1.address()); - ix_accounts[4] = InstructionAccount::writable(accounts.merkle_tree1.address()); - ix_accounts[5] = InstructionAccount::readonly(accounts.log_wrapper.address()); - ix_accounts[6] = InstructionAccount::readonly(accounts.compression_program.address()); - ix_accounts[7] = InstructionAccount::readonly(accounts.system_program.address()); - - for i in 0..proof1_count { - ix_accounts[8 + i] = InstructionAccount::readonly(all_proofs[i].address()); +impl WithdrawTwo { + #[allow(clippy::too_many_lines)] + pub fn withdraw_two_cnfts(&mut self, ctx: &CtxWithRemaining) -> Result<(), ProgramError> { + // Parse instruction args: + // args1(108) + proof_1_length(1) + args2(108) + _proof_2_length(1) = 218 bytes + let data = ctx.data; + if data.len() < 218 { + return Err(ProgramError::InvalidInstructionData); } - let sys_view = accounts.system_program.to_account_view().clone(); - let mut views: [AccountView; MAX_CPI_ACCOUNTS] = - core::array::from_fn(|_| sys_view.clone()); - - views[0] = accounts.tree_authority1.to_account_view().clone(); - views[1] = accounts.leaf_owner.to_account_view().clone(); - views[2] = accounts.leaf_owner.to_account_view().clone(); - views[3] = accounts.new_leaf_owner1.to_account_view().clone(); - views[4] = accounts.merkle_tree1.to_account_view().clone(); - views[5] = accounts.log_wrapper.to_account_view().clone(); - views[6] = accounts.compression_program.to_account_view().clone(); - views[7] = accounts.system_program.to_account_view().clone(); - - for i in 0..proof1_count { - views[8 + i] = all_proofs[i].clone(); + let args1 = &data[0..TRANSFER_ARGS_LEN]; + let proof_1_length = data[TRANSFER_ARGS_LEN] as usize; + let args2 = &data[TRANSFER_ARGS_LEN + 1..TRANSFER_ARGS_LEN * 2 + 1]; + // _proof_2_length at data[217] — not needed, remaining after proof1 is proof2 + + // PDA signer seeds + let bump_bytes = [ctx.bumps.leaf_owner]; + let seeds: [Seed; 2] = [ + Seed::from(b"cNFT-vault" as &[u8]), + Seed::from(&bump_bytes as &[u8]), + ]; + let signer = Signer::from(&seeds as &[Seed]); + + // Collect all remaining accounts (proof1 ++ proof2) + let remaining = ctx.remaining_accounts(); + let placeholder = self.system_program.to_account_view().clone(); + let mut all_proofs: [AccountView; MAX_PROOF_NODES * 2] = + core::array::from_fn(|_| placeholder.clone()); + let mut total_proofs = 0usize; + for result in remaining.iter() { + if total_proofs >= MAX_PROOF_NODES * 2 { + break; + } + all_proofs[total_proofs] = result?; + total_proofs += 1; } - let instruction = InstructionView { - program_id: &MPL_BUBBLEGUM_ID, - data: &ix_data, - accounts: &ix_accounts[..total_accounts], - }; - - solana_instruction_view::cpi::invoke_signed_with_bounds::< - MAX_CPI_ACCOUNTS, - AccountView, - >(&instruction, &views[..total_accounts], &[signer.clone()])?; - } - - // --- Withdraw cNFT #2 --- - log("withdrawing cNFT#2"); - { - let mut ix_data = [0u8; 8 + TRANSFER_ARGS_LEN]; - ix_data[0..8].copy_from_slice(&TRANSFER_DISCRIMINATOR); - ix_data[8..].copy_from_slice(args2); - - let total_accounts = 8 + proof2_count; - let sys_addr = accounts.system_program.address(); - let mut ix_accounts: [InstructionAccount; MAX_CPI_ACCOUNTS] = - core::array::from_fn(|_| InstructionAccount::readonly(sys_addr)); - - ix_accounts[0] = InstructionAccount::readonly(accounts.tree_authority2.address()); - ix_accounts[1] = InstructionAccount::readonly_signer(accounts.leaf_owner.address()); - ix_accounts[2] = InstructionAccount::readonly(accounts.leaf_owner.address()); - ix_accounts[3] = InstructionAccount::readonly(accounts.new_leaf_owner2.address()); - ix_accounts[4] = InstructionAccount::writable(accounts.merkle_tree2.address()); - ix_accounts[5] = InstructionAccount::readonly(accounts.log_wrapper.address()); - ix_accounts[6] = InstructionAccount::readonly(accounts.compression_program.address()); - ix_accounts[7] = InstructionAccount::readonly(accounts.system_program.address()); - - let proof2_start = proof1_count; - for i in 0..proof2_count { - ix_accounts[8 + i] = - InstructionAccount::readonly(all_proofs[proof2_start + i].address()); + // Split into proof1 and proof2 + let proof1_count = proof_1_length.min(total_proofs); + let proof2_count = total_proofs.saturating_sub(proof1_count); + + // --- Withdraw cNFT #1 --- + log("withdrawing cNFT#1"); + { + let mut ix_data = [0u8; 8 + TRANSFER_ARGS_LEN]; + ix_data[0..8].copy_from_slice(&TRANSFER_DISCRIMINATOR); + ix_data[8..].copy_from_slice(args1); + + let total_accounts = 8 + proof1_count; + let sys_addr = self.system_program.address(); + let mut ix_accounts: [InstructionAccount; MAX_CPI_ACCOUNTS] = + core::array::from_fn(|_| InstructionAccount::readonly(sys_addr)); + + ix_accounts[0] = InstructionAccount::readonly(self.tree_authority1.address()); + ix_accounts[1] = InstructionAccount::readonly_signer(self.leaf_owner.address()); + ix_accounts[2] = InstructionAccount::readonly(self.leaf_owner.address()); + ix_accounts[3] = InstructionAccount::readonly(self.new_leaf_owner1.address()); + ix_accounts[4] = InstructionAccount::writable(self.merkle_tree1.address()); + ix_accounts[5] = InstructionAccount::readonly(self.log_wrapper.address()); + ix_accounts[6] = InstructionAccount::readonly(self.compression_program.address()); + ix_accounts[7] = InstructionAccount::readonly(self.system_program.address()); + + for i in 0..proof1_count { + ix_accounts[8 + i] = InstructionAccount::readonly(all_proofs[i].address()); + } + + let sys_view = self.system_program.to_account_view().clone(); + let mut views: [AccountView; MAX_CPI_ACCOUNTS] = + core::array::from_fn(|_| sys_view.clone()); + + views[0] = self.tree_authority1.to_account_view().clone(); + views[1] = self.leaf_owner.to_account_view().clone(); + views[2] = self.leaf_owner.to_account_view().clone(); + views[3] = self.new_leaf_owner1.to_account_view().clone(); + views[4] = self.merkle_tree1.to_account_view().clone(); + views[5] = self.log_wrapper.to_account_view().clone(); + views[6] = self.compression_program.to_account_view().clone(); + views[7] = self.system_program.to_account_view().clone(); + + for i in 0..proof1_count { + views[8 + i] = all_proofs[i].clone(); + } + + let instruction = InstructionView { + program_id: &MPL_BUBBLEGUM_ID, + data: &ix_data, + accounts: &ix_accounts[..total_accounts], + }; + + solana_instruction_view::cpi::invoke_signed_with_bounds::< + MAX_CPI_ACCOUNTS, + AccountView, + >(&instruction, &views[..total_accounts], &[signer.clone()])?; } - let sys_view = accounts.system_program.to_account_view().clone(); - let mut views: [AccountView; MAX_CPI_ACCOUNTS] = - core::array::from_fn(|_| sys_view.clone()); - - views[0] = accounts.tree_authority2.to_account_view().clone(); - views[1] = accounts.leaf_owner.to_account_view().clone(); - views[2] = accounts.leaf_owner.to_account_view().clone(); - views[3] = accounts.new_leaf_owner2.to_account_view().clone(); - views[4] = accounts.merkle_tree2.to_account_view().clone(); - views[5] = accounts.log_wrapper.to_account_view().clone(); - views[6] = accounts.compression_program.to_account_view().clone(); - views[7] = accounts.system_program.to_account_view().clone(); - - for i in 0..proof2_count { - views[8 + i] = all_proofs[proof2_start + i].clone(); + // --- Withdraw cNFT #2 --- + log("withdrawing cNFT#2"); + { + let mut ix_data = [0u8; 8 + TRANSFER_ARGS_LEN]; + ix_data[0..8].copy_from_slice(&TRANSFER_DISCRIMINATOR); + ix_data[8..].copy_from_slice(args2); + + let total_accounts = 8 + proof2_count; + let sys_addr = self.system_program.address(); + let mut ix_accounts: [InstructionAccount; MAX_CPI_ACCOUNTS] = + core::array::from_fn(|_| InstructionAccount::readonly(sys_addr)); + + ix_accounts[0] = InstructionAccount::readonly(self.tree_authority2.address()); + ix_accounts[1] = InstructionAccount::readonly_signer(self.leaf_owner.address()); + ix_accounts[2] = InstructionAccount::readonly(self.leaf_owner.address()); + ix_accounts[3] = InstructionAccount::readonly(self.new_leaf_owner2.address()); + ix_accounts[4] = InstructionAccount::writable(self.merkle_tree2.address()); + ix_accounts[5] = InstructionAccount::readonly(self.log_wrapper.address()); + ix_accounts[6] = InstructionAccount::readonly(self.compression_program.address()); + ix_accounts[7] = InstructionAccount::readonly(self.system_program.address()); + + let proof2_start = proof1_count; + for i in 0..proof2_count { + ix_accounts[8 + i] = + InstructionAccount::readonly(all_proofs[proof2_start + i].address()); + } + + let sys_view = self.system_program.to_account_view().clone(); + let mut views: [AccountView; MAX_CPI_ACCOUNTS] = + core::array::from_fn(|_| sys_view.clone()); + + views[0] = self.tree_authority2.to_account_view().clone(); + views[1] = self.leaf_owner.to_account_view().clone(); + views[2] = self.leaf_owner.to_account_view().clone(); + views[3] = self.new_leaf_owner2.to_account_view().clone(); + views[4] = self.merkle_tree2.to_account_view().clone(); + views[5] = self.log_wrapper.to_account_view().clone(); + views[6] = self.compression_program.to_account_view().clone(); + views[7] = self.system_program.to_account_view().clone(); + + for i in 0..proof2_count { + views[8 + i] = all_proofs[proof2_start + i].clone(); + } + + let instruction = InstructionView { + program_id: &MPL_BUBBLEGUM_ID, + data: &ix_data, + accounts: &ix_accounts[..total_accounts], + }; + + solana_instruction_view::cpi::invoke_signed_with_bounds::< + MAX_CPI_ACCOUNTS, + AccountView, + >(&instruction, &views[..total_accounts], &[signer])?; } - let instruction = InstructionView { - program_id: &MPL_BUBBLEGUM_ID, - data: &ix_data, - accounts: &ix_accounts[..total_accounts], - }; - - solana_instruction_view::cpi::invoke_signed_with_bounds::< - MAX_CPI_ACCOUNTS, - AccountView, - >(&instruction, &views[..total_accounts], &[signer])?; + log("successfully sent cNFTs"); + Ok(()) } - - log("successfully sent cNFTs"); - Ok(()) } diff --git a/compression/cnft-vault/quasar/src/lib.rs b/compression/cnft-vault/quasar/src/lib.rs index cf62058a7..1f65adc4a 100644 --- a/compression/cnft-vault/quasar/src/lib.rs +++ b/compression/cnft-vault/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![cfg_attr(not(test), no_std)] +#![no_std] use quasar_lang::prelude::*; @@ -33,12 +33,12 @@ mod quasar_cnft_vault { /// Withdraw a single compressed NFT from the vault PDA. #[instruction(discriminator = 0)] pub fn withdraw_cnft(ctx: CtxWithRemaining) -> Result<(), ProgramError> { - instructions::handle_withdraw_cnft(&ctx.accounts, &ctx) + ctx.accounts.withdraw_cnft(&ctx) } /// Withdraw two compressed NFTs from the vault PDA in a single transaction. #[instruction(discriminator = 1)] pub fn withdraw_two_cnfts(ctx: CtxWithRemaining) -> Result<(), ProgramError> { - instructions::handle_withdraw_two_cnfts(&ctx.accounts, &ctx) + ctx.accounts.withdraw_two_cnfts(&ctx) } } diff --git a/compression/cutils/quasar/Cargo.toml b/compression/cutils/quasar/Cargo.toml index ee04326cb..07727e2f2 100644 --- a/compression/cutils/quasar/Cargo.toml +++ b/compression/cutils/quasar/Cargo.toml @@ -22,7 +22,7 @@ client = [] debug = [] [dependencies] -quasar-lang = "0.0" +quasar-lang = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } # Direct dependency for invoke_with_bounds — raw CPI with variable proof accounts. solana-instruction-view = { version = "2", features = ["cpi"] } solana-instruction = { version = "3.2.0" } diff --git a/compression/cutils/quasar/src/instructions/mint.rs b/compression/cutils/quasar/src/instructions/mint.rs index 34232016b..29cde50e0 100644 --- a/compression/cutils/quasar/src/instructions/mint.rs +++ b/compression/cutils/quasar/src/instructions/mint.rs @@ -13,114 +13,116 @@ const MAX_IX_DATA: usize = 400; /// Accounts for minting a compressed NFT to a collection. #[derive(Accounts)] -pub struct Mint<'info> { - pub payer: &'info Signer, +pub struct Mint { + pub payer: Signer, /// Tree authority PDA (seeds checked by Bubblegum). #[account(mut)] - pub tree_authority: &'info UncheckedAccount, + pub tree_authority: UncheckedAccount, /// Owner of the newly minted cNFT. - pub leaf_owner: &'info UncheckedAccount, + pub leaf_owner: UncheckedAccount, /// Delegate for the newly minted cNFT. - pub leaf_delegate: &'info UncheckedAccount, + pub leaf_delegate: UncheckedAccount, /// Merkle tree to mint into. #[account(mut)] - pub merkle_tree: &'info UncheckedAccount, + pub merkle_tree: UncheckedAccount, /// Tree delegate (must be signer). - pub tree_delegate: &'info Signer, + pub tree_delegate: Signer, /// Collection authority (must be signer). - pub collection_authority: &'info Signer, + pub collection_authority: Signer, /// Collection authority record PDA (or Bubblegum program address). - pub collection_authority_record_pda: &'info UncheckedAccount, + pub collection_authority_record_pda: UncheckedAccount, /// Collection mint account. - pub collection_mint: &'info UncheckedAccount, + pub collection_mint: UncheckedAccount, /// Collection metadata account. #[account(mut)] - pub collection_metadata: &'info UncheckedAccount, + pub collection_metadata: UncheckedAccount, /// Edition account for the collection. - pub edition_account: &'info UncheckedAccount, + pub edition_account: UncheckedAccount, /// Bubblegum signer PDA. - pub bubblegum_signer: &'info UncheckedAccount, + pub bubblegum_signer: UncheckedAccount, /// SPL Noop log wrapper. - pub log_wrapper: &'info UncheckedAccount, + pub log_wrapper: UncheckedAccount, /// SPL Account Compression program. #[account(address = SPL_ACCOUNT_COMPRESSION_ID)] - pub compression_program: &'info UncheckedAccount, + pub compression_program: UncheckedAccount, /// Token Metadata program. - pub token_metadata_program: &'info UncheckedAccount, + pub token_metadata_program: UncheckedAccount, /// mpl-bubblegum program. #[account(address = MPL_BUBBLEGUM_ID)] - pub bubblegum_program: &'info UncheckedAccount, - pub system_program: &'info Program, + pub bubblegum_program: UncheckedAccount, + pub system_program: Program, } -pub fn handle_mint<'info>(accounts: &Mint<'info>, ctx: &Ctx<'info, Mint<'info>>) -> Result<(), ProgramError> { - // Parse URI from instruction data: u32 length prefix + utf8 bytes (borsh String) - let data = ctx.data; - if data.len() < 4 { - return Err(ProgramError::InvalidInstructionData); - } - let uri_len = u32::from_le_bytes(data[0..4].try_into().unwrap()) as usize; - if data.len() < 4 + uri_len || uri_len > MAX_URI_LEN { - return Err(ProgramError::InvalidInstructionData); - } - let uri = &data[4..4 + uri_len]; +impl Mint { + pub fn mint(&mut self, ctx: &Ctx) -> Result<(), ProgramError> { + // Parse URI from instruction data: u32 length prefix + utf8 bytes (borsh String) + let data = ctx.data; + if data.len() < 4 { + return Err(ProgramError::InvalidInstructionData); + } + let uri_len = u32::from_le_bytes(data[0..4].try_into().unwrap()) as usize; + if data.len() < 4 + uri_len || uri_len > MAX_URI_LEN { + return Err(ProgramError::InvalidInstructionData); + } + let uri = &data[4..4 + uri_len]; - // Build CPI instruction data - let mut ix_data = [0u8; MAX_IX_DATA]; - let ix_len = encode_mint_to_collection_v1( - &mut ix_data, - uri, - accounts.collection_authority.address(), - accounts.collection_mint.address(), - ); + // Build CPI instruction data + let mut ix_data = [0u8; MAX_IX_DATA]; + let ix_len = encode_mint_to_collection_v1( + &mut ix_data, + uri, + self.collection_authority.address(), + self.collection_mint.address(), + ); - // Build instruction account metas matching MintToCollectionV1 layout - let ix_accounts: [InstructionAccount; MINT_CPI_ACCOUNTS] = [ - InstructionAccount::writable(accounts.tree_authority.address()), - InstructionAccount::readonly(accounts.leaf_owner.address()), - InstructionAccount::readonly(accounts.leaf_delegate.address()), - InstructionAccount::writable(accounts.merkle_tree.address()), - InstructionAccount::readonly_signer(accounts.payer.address()), - InstructionAccount::readonly_signer(accounts.tree_delegate.address()), - InstructionAccount::readonly_signer(accounts.collection_authority.address()), - InstructionAccount::readonly(accounts.collection_authority_record_pda.address()), - InstructionAccount::readonly(accounts.collection_mint.address()), - InstructionAccount::writable(accounts.collection_metadata.address()), - InstructionAccount::readonly(accounts.edition_account.address()), - InstructionAccount::readonly(accounts.bubblegum_signer.address()), - InstructionAccount::readonly(accounts.log_wrapper.address()), - InstructionAccount::readonly(accounts.compression_program.address()), - InstructionAccount::readonly(accounts.token_metadata_program.address()), - InstructionAccount::readonly(accounts.system_program.address()), - ]; + // Build instruction account metas matching MintToCollectionV1 layout + let ix_accounts: [InstructionAccount; MINT_CPI_ACCOUNTS] = [ + InstructionAccount::writable(self.tree_authority.address()), + InstructionAccount::readonly(self.leaf_owner.address()), + InstructionAccount::readonly(self.leaf_delegate.address()), + InstructionAccount::writable(self.merkle_tree.address()), + InstructionAccount::readonly_signer(self.payer.address()), + InstructionAccount::readonly_signer(self.tree_delegate.address()), + InstructionAccount::readonly_signer(self.collection_authority.address()), + InstructionAccount::readonly(self.collection_authority_record_pda.address()), + InstructionAccount::readonly(self.collection_mint.address()), + InstructionAccount::writable(self.collection_metadata.address()), + InstructionAccount::readonly(self.edition_account.address()), + InstructionAccount::readonly(self.bubblegum_signer.address()), + InstructionAccount::readonly(self.log_wrapper.address()), + InstructionAccount::readonly(self.compression_program.address()), + InstructionAccount::readonly(self.token_metadata_program.address()), + InstructionAccount::readonly(self.system_program.address()), + ]; - let views: [AccountView; MINT_CPI_ACCOUNTS] = [ - accounts.tree_authority.to_account_view().clone(), - accounts.leaf_owner.to_account_view().clone(), - accounts.leaf_delegate.to_account_view().clone(), - accounts.merkle_tree.to_account_view().clone(), - accounts.payer.to_account_view().clone(), - accounts.tree_delegate.to_account_view().clone(), - accounts.collection_authority.to_account_view().clone(), - accounts.collection_authority_record_pda.to_account_view().clone(), - accounts.collection_mint.to_account_view().clone(), - accounts.collection_metadata.to_account_view().clone(), - accounts.edition_account.to_account_view().clone(), - accounts.bubblegum_signer.to_account_view().clone(), - accounts.log_wrapper.to_account_view().clone(), - accounts.compression_program.to_account_view().clone(), - accounts.token_metadata_program.to_account_view().clone(), - accounts.system_program.to_account_view().clone(), - ]; + let views: [AccountView; MINT_CPI_ACCOUNTS] = [ + self.tree_authority.to_account_view().clone(), + self.leaf_owner.to_account_view().clone(), + self.leaf_delegate.to_account_view().clone(), + self.merkle_tree.to_account_view().clone(), + self.payer.to_account_view().clone(), + self.tree_delegate.to_account_view().clone(), + self.collection_authority.to_account_view().clone(), + self.collection_authority_record_pda.to_account_view().clone(), + self.collection_mint.to_account_view().clone(), + self.collection_metadata.to_account_view().clone(), + self.edition_account.to_account_view().clone(), + self.bubblegum_signer.to_account_view().clone(), + self.log_wrapper.to_account_view().clone(), + self.compression_program.to_account_view().clone(), + self.token_metadata_program.to_account_view().clone(), + self.system_program.to_account_view().clone(), + ]; - let instruction = InstructionView { - program_id: &MPL_BUBBLEGUM_ID, - data: &ix_data[..ix_len], - accounts: &ix_accounts, - }; + let instruction = InstructionView { + program_id: &MPL_BUBBLEGUM_ID, + data: &ix_data[..ix_len], + accounts: &ix_accounts, + }; - solana_instruction_view::cpi::invoke::( - &instruction, - &views, - ) + solana_instruction_view::cpi::invoke::( + &instruction, + &views, + ) + } } diff --git a/compression/cutils/quasar/src/instructions/verify.rs b/compression/cutils/quasar/src/instructions/verify.rs index 42b926a3d..20df0fcdf 100644 --- a/compression/cutils/quasar/src/instructions/verify.rs +++ b/compression/cutils/quasar/src/instructions/verify.rs @@ -13,95 +13,95 @@ const VERIFY_LEAF_DISCRIMINATOR: [u8; 8] = [0x7c, 0xdc, 0x16, 0xdf, 0x68, 0x0a, /// Accounts for verifying a compressed NFT leaf in the merkle tree. #[derive(Accounts)] -pub struct Verify<'info> { - pub leaf_owner: &'info Signer, +pub struct Verify { + pub leaf_owner: Signer, /// Leaf delegate. - pub leaf_delegate: &'info UncheckedAccount, + pub leaf_delegate: UncheckedAccount, /// Merkle tree to verify against. - pub merkle_tree: &'info UncheckedAccount, + pub merkle_tree: UncheckedAccount, /// SPL Account Compression program. #[account(address = SPL_ACCOUNT_COMPRESSION_ID)] - pub compression_program: &'info UncheckedAccount, + pub compression_program: UncheckedAccount, } -pub fn handle_verify<'info>( - accounts: &Verify<'info>, ctx: &CtxWithRemaining<'info, Verify<'info>>, -) -> Result<(), ProgramError> { - // Parse verify params from instruction data: - // root(32) + data_hash(32) + creator_hash(32) + nonce(8) + index(4) = 108 bytes - let data = ctx.data; - if data.len() < 108 { - return Err(ProgramError::InvalidInstructionData); - } +impl Verify { + pub fn verify(&mut self, ctx: &CtxWithRemaining) -> Result<(), ProgramError> { + // Parse verify params from instruction data: + // root(32) + data_hash(32) + creator_hash(32) + nonce(8) + index(4) = 108 bytes + let data = ctx.data; + if data.len() < 108 { + return Err(ProgramError::InvalidInstructionData); + } - let root: [u8; 32] = data[0..32].try_into().unwrap(); - let data_hash: [u8; 32] = data[32..64].try_into().unwrap(); - let creator_hash: [u8; 32] = data[64..96].try_into().unwrap(); - let nonce = u64::from_le_bytes(data[96..104].try_into().unwrap()); - let index = u32::from_le_bytes(data[104..108].try_into().unwrap()); - - // Compute asset ID and leaf hash - let asset_id = get_asset_id(accounts.merkle_tree.address(), nonce); - let leaf_hash = leaf_schema_v1_hash( - &asset_id, - accounts.leaf_owner.address(), - accounts.leaf_delegate.address(), - nonce, - &data_hash, - &creator_hash, - ); - - // Build verify_leaf instruction data: discriminator(8) + root(32) + leaf(32) + index(4) = 76 - let mut ix_data = [0u8; 76]; - ix_data[0..8].copy_from_slice(&VERIFY_LEAF_DISCRIMINATOR); - ix_data[8..40].copy_from_slice(&root); - ix_data[40..72].copy_from_slice(&leaf_hash); - ix_data[72..76].copy_from_slice(&index.to_le_bytes()); - - // Collect proof nodes - let remaining = ctx.remaining_accounts(); - let placeholder = accounts.compression_program.to_account_view().clone(); - let mut proof_views: [AccountView; MAX_PROOF_NODES] = - core::array::from_fn(|_| placeholder.clone()); - let mut proof_count = 0usize; - for result in remaining.iter() { - if proof_count >= MAX_PROOF_NODES { - break; + let root: [u8; 32] = data[0..32].try_into().unwrap(); + let data_hash: [u8; 32] = data[32..64].try_into().unwrap(); + let creator_hash: [u8; 32] = data[64..96].try_into().unwrap(); + let nonce = u64::from_le_bytes(data[96..104].try_into().unwrap()); + let index = u32::from_le_bytes(data[104..108].try_into().unwrap()); + + // Compute asset ID and leaf hash + let asset_id = get_asset_id(self.merkle_tree.address(), nonce); + let leaf_hash = leaf_schema_v1_hash( + &asset_id, + self.leaf_owner.address(), + self.leaf_delegate.address(), + nonce, + &data_hash, + &creator_hash, + ); + + // Build verify_leaf instruction data: discriminator(8) + root(32) + leaf(32) + index(4) = 76 + let mut ix_data = [0u8; 76]; + ix_data[0..8].copy_from_slice(&VERIFY_LEAF_DISCRIMINATOR); + ix_data[8..40].copy_from_slice(&root); + ix_data[40..72].copy_from_slice(&leaf_hash); + ix_data[72..76].copy_from_slice(&index.to_le_bytes()); + + // Collect proof nodes + let remaining = ctx.remaining_accounts(); + let placeholder = self.compression_program.to_account_view().clone(); + let mut proof_views: [AccountView; MAX_PROOF_NODES] = + core::array::from_fn(|_| placeholder.clone()); + let mut proof_count = 0usize; + for result in remaining.iter() { + if proof_count >= MAX_PROOF_NODES { + break; + } + proof_views[proof_count] = result?; + proof_count += 1; } - proof_views[proof_count] = result?; - proof_count += 1; - } - let total_accounts = 1 + proof_count; + let total_accounts = 1 + proof_count; - // Build instruction accounts: merkle_tree + proof nodes - let tree_addr = accounts.merkle_tree.address(); - let mut ix_accounts: [InstructionAccount; MAX_CPI_ACCOUNTS] = - core::array::from_fn(|_| InstructionAccount::readonly(tree_addr)); + // Build instruction accounts: merkle_tree + proof nodes + let tree_addr = self.merkle_tree.address(); + let mut ix_accounts: [InstructionAccount; MAX_CPI_ACCOUNTS] = + core::array::from_fn(|_| InstructionAccount::readonly(tree_addr)); - ix_accounts[0] = InstructionAccount::readonly(accounts.merkle_tree.address()); - for i in 0..proof_count { - ix_accounts[1 + i] = InstructionAccount::readonly(proof_views[i].address()); - } + ix_accounts[0] = InstructionAccount::readonly(self.merkle_tree.address()); + for i in 0..proof_count { + ix_accounts[1 + i] = InstructionAccount::readonly(proof_views[i].address()); + } - // Build account views - let tree_view = accounts.merkle_tree.to_account_view().clone(); - let mut views: [AccountView; MAX_CPI_ACCOUNTS] = - core::array::from_fn(|_| tree_view.clone()); + // Build account views + let tree_view = self.merkle_tree.to_account_view().clone(); + let mut views: [AccountView; MAX_CPI_ACCOUNTS] = + core::array::from_fn(|_| tree_view.clone()); - views[0] = accounts.merkle_tree.to_account_view().clone(); - for i in 0..proof_count { - views[1 + i] = proof_views[i].clone(); - } + views[0] = self.merkle_tree.to_account_view().clone(); + for i in 0..proof_count { + views[1 + i] = proof_views[i].clone(); + } - let instruction = InstructionView { - program_id: accounts.compression_program.address(), - data: &ix_data, - accounts: &ix_accounts[..total_accounts], - }; + let instruction = InstructionView { + program_id: self.compression_program.address(), + data: &ix_data, + accounts: &ix_accounts[..total_accounts], + }; - solana_instruction_view::cpi::invoke_with_bounds::( - &instruction, - &views[..total_accounts], - ) + solana_instruction_view::cpi::invoke_with_bounds::( + &instruction, + &views[..total_accounts], + ) + } } diff --git a/compression/cutils/quasar/src/lib.rs b/compression/cutils/quasar/src/lib.rs index cdead5023..100a91f94 100644 --- a/compression/cutils/quasar/src/lib.rs +++ b/compression/cutils/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![cfg_attr(not(test), no_std)] +#![no_std] use quasar_lang::prelude::*; @@ -32,12 +32,12 @@ mod quasar_cutils { /// Mint a compressed NFT to a collection via MintToCollectionV1. #[instruction(discriminator = 0)] pub fn mint(ctx: Ctx) -> Result<(), ProgramError> { - instructions::handle_mint(&ctx.accounts, &ctx) + ctx.accounts.mint(&ctx) } /// Verify a compressed NFT leaf exists in the merkle tree. #[instruction(discriminator = 1)] pub fn verify(ctx: CtxWithRemaining) -> Result<(), ProgramError> { - instructions::handle_verify(&ctx.accounts, &ctx) + ctx.accounts.verify(&ctx) } } diff --git a/oracles/pyth/quasar/Cargo.toml b/oracles/pyth/quasar/Cargo.toml index 948cd6576..877636ea5 100644 --- a/oracles/pyth/quasar/Cargo.toml +++ b/oracles/pyth/quasar/Cargo.toml @@ -22,7 +22,7 @@ client = [] debug = [] [dependencies] -quasar-lang = "0.0" +quasar-lang = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } solana-instruction = { version = "3.2.0" } [dev-dependencies] diff --git a/oracles/pyth/quasar/src/instructions/read_price.rs b/oracles/pyth/quasar/src/instructions/read_price.rs index 6b5d08a45..2afa802a0 100644 --- a/oracles/pyth/quasar/src/instructions/read_price.rs +++ b/oracles/pyth/quasar/src/instructions/read_price.rs @@ -19,50 +19,52 @@ const MIN_DATA_LEN: usize = 101; /// Uses `UncheckedAccount` because Quasar does not have a built-in Pyth account type; /// the caller is responsible for passing a valid PriceUpdateV2 account. #[derive(Accounts)] -pub struct ReadPrice<'info> { +pub struct ReadPrice { /// The Pyth PriceUpdateV2 price update account. - pub price_update: &'info UncheckedAccount, + pub price_update: UncheckedAccount, } -#[inline(always)] -pub fn handle_read_price(accounts: &mut ReadPrice) -> Result<(), ProgramError> { - let view = accounts.price_update.to_account_view(); - let data = view.data(); +impl ReadPrice { + #[inline(always)] + pub fn read_price(&mut self) -> Result<(), ProgramError> { + let view = self.price_update.to_account_view(); + let data = view.data(); - if data.len() < MIN_DATA_LEN { - return Err(ProgramError::InvalidAccountData); - } + if data.len() < MIN_DATA_LEN { + return Err(ProgramError::InvalidAccountData); + } - let price = i64::from_le_bytes( - data[PRICE_OFFSET..PRICE_OFFSET + 8] - .try_into() - .map_err(|_| ProgramError::InvalidAccountData)?, - ); - let conf = u64::from_le_bytes( - data[CONF_OFFSET..CONF_OFFSET + 8] - .try_into() - .map_err(|_| ProgramError::InvalidAccountData)?, - ); - let exponent = i32::from_le_bytes( - data[EXPONENT_OFFSET..EXPONENT_OFFSET + 4] - .try_into() - .map_err(|_| ProgramError::InvalidAccountData)?, - ); - let publish_time = i64::from_le_bytes( - data[PUBLISH_TIME_OFFSET..PUBLISH_TIME_OFFSET + 8] - .try_into() - .map_err(|_| ProgramError::InvalidAccountData)?, - ); + let price = i64::from_le_bytes( + data[PRICE_OFFSET..PRICE_OFFSET + 8] + .try_into() + .map_err(|_| ProgramError::InvalidAccountData)?, + ); + let conf = u64::from_le_bytes( + data[CONF_OFFSET..CONF_OFFSET + 8] + .try_into() + .map_err(|_| ProgramError::InvalidAccountData)?, + ); + let exponent = i32::from_le_bytes( + data[EXPONENT_OFFSET..EXPONENT_OFFSET + 4] + .try_into() + .map_err(|_| ProgramError::InvalidAccountData)?, + ); + let publish_time = i64::from_le_bytes( + data[PUBLISH_TIME_OFFSET..PUBLISH_TIME_OFFSET + 8] + .try_into() + .map_err(|_| ProgramError::InvalidAccountData)?, + ); - log("Pyth price feed data:"); - log(" price (raw):"); - log_64(price as u64); - log(" confidence:"); - log_64(conf); - log(" exponent:"); - log_64(exponent as u64); - log(" publish_time:"); - log_64(publish_time as u64); + log("Pyth price feed data:"); + log(" price (raw):"); + log_64(price as u64); + log(" confidence:"); + log_64(conf); + log(" exponent:"); + log_64(exponent as u64); + log(" publish_time:"); + log_64(publish_time as u64); - Ok(()) + Ok(()) + } } diff --git a/oracles/pyth/quasar/src/lib.rs b/oracles/pyth/quasar/src/lib.rs index 87c23f22c..2fd64b23c 100644 --- a/oracles/pyth/quasar/src/lib.rs +++ b/oracles/pyth/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![cfg_attr(not(test), no_std)] +#![no_std] use quasar_lang::prelude::*; @@ -16,6 +16,6 @@ mod quasar_pyth_example { /// Read and log Pyth price feed data from a PriceUpdateV2 account. #[instruction(discriminator = 0)] pub fn read_price(ctx: Ctx) -> Result<(), ProgramError> { - instructions::handle_read_price(&mut ctx.accounts) + ctx.accounts.read_price() } } diff --git a/tokens/create-token/quasar/Cargo.toml b/tokens/create-token/quasar/Cargo.toml index 510afb6db..da603962e 100644 --- a/tokens/create-token/quasar/Cargo.toml +++ b/tokens/create-token/quasar/Cargo.toml @@ -22,8 +22,8 @@ client = [] debug = [] [dependencies] -quasar-lang = "0.0" -quasar-spl = "0.0" +quasar-lang = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } +quasar-spl = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } solana-instruction = { version = "3.2.0" } [dev-dependencies] diff --git a/tokens/create-token/quasar/src/lib.rs b/tokens/create-token/quasar/src/lib.rs index 7d0d92b82..12cd4b748 100644 --- a/tokens/create-token/quasar/src/lib.rs +++ b/tokens/create-token/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![cfg_attr(not(test), no_std)] +#![no_std] use quasar_lang::prelude::*; use quasar_spl::{Mint, Token, TokenCpi}; @@ -20,50 +20,54 @@ mod quasar_create_token { /// Create a new token mint (account init handled by Quasar's `#[account(init)]`). #[instruction(discriminator = 0)] pub fn create_token(ctx: Ctx, _decimals: u8) -> Result<(), ProgramError> { - handle_create_token(&mut ctx.accounts) + ctx.accounts.create_token() } /// Mint tokens to the creator's token account. #[instruction(discriminator = 1)] pub fn mint_tokens(ctx: Ctx, amount: u64) -> Result<(), ProgramError> { - handle_mint_tokens(&mut ctx.accounts, amount) + ctx.accounts.mint_tokens(amount) } } /// Accounts for creating a new token mint. /// Quasar's `#[account(init)]` handles the create_account + initialize_mint CPI. #[derive(Accounts)] -pub struct CreateToken<'info> { +pub struct CreateToken { #[account(mut)] - pub payer: &'info Signer, + pub payer: Signer, #[account(mut, init, payer = payer, mint::decimals = 9, mint::authority = payer)] - pub mint: &'info mut Account, - pub rent: &'info Sysvar, - pub token_program: &'info Program, - pub system_program: &'info Program, + pub mint: Account, + pub rent: Sysvar, + pub token_program: Program, + pub system_program: Program, } -#[inline(always)] -pub fn handle_create_token(accounts: &CreateToken) -> Result<(), ProgramError> { - // Mint account is created and initialised by Quasar's account init. - Ok(()) +impl CreateToken { + #[inline(always)] + pub fn create_token(&mut self) -> Result<(), ProgramError> { + // Mint account is created and initialised by Quasar's account init. + Ok(()) + } } /// Accounts for minting tokens to an existing token account. #[derive(Accounts)] -pub struct MintTokens<'info> { +pub struct MintTokens { #[account(mut)] - pub authority: &'info Signer, + pub authority: Signer, #[account(mut)] - pub mint: &'info mut Account, + pub mint: Account, #[account(mut)] - pub token_account: &'info mut Account, - pub token_program: &'info Program, + pub token_account: Account, + pub token_program: Program, } -#[inline(always)] -pub fn handle_mint_tokens(accounts: &mut MintTokens, amount: u64) -> Result<(), ProgramError> { - accounts.token_program - .mint_to(accounts.mint, accounts.token_account, accounts.authority, amount) - .invoke() +impl MintTokens { + #[inline(always)] + pub fn mint_tokens(&mut self, amount: u64) -> Result<(), ProgramError> { + self.token_program + .mint_to(&self.mint, &self.token_account, &self.authority, amount) + .invoke() + } } diff --git a/tokens/escrow/quasar/Cargo.toml b/tokens/escrow/quasar/Cargo.toml index 9c21b47a2..f43961cdc 100644 --- a/tokens/escrow/quasar/Cargo.toml +++ b/tokens/escrow/quasar/Cargo.toml @@ -22,8 +22,8 @@ client = [] debug = [] [dependencies] -quasar-lang = "0.0" -quasar-spl = "0.0" +quasar-lang = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } +quasar-spl = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } solana-address = { version = "2.2.0" } solana-instruction = { version = "3.2.0" } diff --git a/tokens/escrow/quasar/src/instructions/make.rs b/tokens/escrow/quasar/src/instructions/make.rs index bfbb4144d..5e26d495e 100644 --- a/tokens/escrow/quasar/src/instructions/make.rs +++ b/tokens/escrow/quasar/src/instructions/make.rs @@ -5,40 +5,42 @@ use { }; #[derive(Accounts)] -pub struct Make<'info> { +pub struct Make { #[account(mut)] - pub maker: &'info Signer, - #[account(mut, init, payer = maker, seeds = [b"escrow", maker], bump)] - pub escrow: &'info mut Account, - pub mint_a: &'info Account, - pub mint_b: &'info Account, + pub maker: Signer, + #[account(mut, init, payer = maker, seeds = Escrow::seeds(maker), bump)] + pub escrow: Account, + pub mint_a: Account, + pub mint_b: Account, #[account(mut)] - pub maker_ta_a: &'info mut Account, + pub maker_ta_a: Account, #[account(mut, init_if_needed, payer = maker, token::mint = mint_b, token::authority = maker)] - pub maker_ta_b: &'info mut Account, + pub maker_ta_b: Account, #[account(mut, init_if_needed, payer = maker, token::mint = mint_a, token::authority = escrow)] - pub vault_ta_a: &'info mut Account, - pub rent: &'info Sysvar, - pub token_program: &'info Program, - pub system_program: &'info Program, + pub vault_ta_a: Account, + pub rent: Sysvar, + pub token_program: Program, + pub system_program: Program, } -#[inline(always)] -pub fn handle_make_escrow(accounts: &mut Make, receive: u64, bumps: &MakeBumps) -> Result<(), ProgramError> { - accounts.escrow.set_inner( - *accounts.maker.address(), - *accounts.mint_a.address(), - *accounts.mint_b.address(), - *accounts.maker_ta_b.address(), - receive, - bumps.escrow, - ); - Ok(()) -} +impl Make { + #[inline(always)] + pub fn make_escrow(&mut self, receive: u64, bumps: &MakeBumps) -> Result<(), ProgramError> { + self.escrow.set_inner( + *self.maker.address(), + *self.mint_a.address(), + *self.mint_b.address(), + *self.maker_ta_b.address(), + receive, + bumps.escrow, + ); + Ok(()) + } -#[inline(always)] -pub fn handle_deposit_tokens(accounts: &mut Make, amount: u64) -> Result<(), ProgramError> { - accounts.token_program - .transfer(accounts.maker_ta_a, accounts.vault_ta_a, accounts.maker, amount) - .invoke() + #[inline(always)] + pub fn deposit_tokens(&mut self, amount: u64) -> Result<(), ProgramError> { + self.token_program + .transfer(&self.maker_ta_a, &self.vault_ta_a, &self.maker, amount) + .invoke() + } } diff --git a/tokens/escrow/quasar/src/instructions/refund.rs b/tokens/escrow/quasar/src/instructions/refund.rs index 0de24d9d7..14474f168 100644 --- a/tokens/escrow/quasar/src/instructions/refund.rs +++ b/tokens/escrow/quasar/src/instructions/refund.rs @@ -5,48 +5,44 @@ use { }; #[derive(Accounts)] -pub struct Refund<'info> { +pub struct Refund { #[account(mut)] - pub maker: &'info Signer, + pub maker: Signer, #[account( mut, has_one = maker, close = maker, - seeds = [b"escrow", maker], + seeds = Escrow::seeds(maker), bump = escrow.bump )] - pub escrow: &'info mut Account, - pub mint_a: &'info Account, + pub escrow: Account, + pub mint_a: Account, #[account(mut, init_if_needed, payer = maker, token::mint = mint_a, token::authority = maker)] - pub maker_ta_a: &'info mut Account, + pub maker_ta_a: Account, #[account(mut)] - pub vault_ta_a: &'info mut Account, - pub rent: &'info Sysvar, - pub token_program: &'info Program, - pub system_program: &'info Program, + pub vault_ta_a: Account, + pub rent: Sysvar, + pub token_program: Program, + pub system_program: Program, } -#[inline(always)] -pub fn handle_withdraw_tokens_and_close(accounts: &mut Refund, bumps: &RefundBumps) -> Result<(), ProgramError> { - let maker_key = accounts.escrow.maker; - let bump = [bumps.escrow]; - let seeds: &[Seed] = &[ - Seed::from(b"escrow" as &[u8]), - Seed::from(maker_key.as_ref()), - Seed::from(&bump as &[u8]), - ]; +impl Refund { + #[inline(always)] + pub fn withdraw_tokens_and_close(&mut self, bumps: &RefundBumps) -> Result<(), ProgramError> { + let seeds = self.escrow_seeds(bumps); - accounts.token_program - .transfer( - accounts.vault_ta_a, - accounts.maker_ta_a, - accounts.escrow, - accounts.vault_ta_a.amount(), - ) - .invoke_signed(seeds)?; + self.token_program + .transfer( + &self.vault_ta_a, + &self.maker_ta_a, + &self.escrow, + self.vault_ta_a.amount(), + ) + .invoke_signed(&seeds)?; - accounts.token_program - .close_account(accounts.vault_ta_a, accounts.maker, accounts.escrow) - .invoke_signed(seeds)?; - Ok(()) + self.token_program + .close_account(&self.vault_ta_a, &self.maker, &self.escrow) + .invoke_signed(&seeds)?; + Ok(()) + } } diff --git a/tokens/escrow/quasar/src/instructions/take.rs b/tokens/escrow/quasar/src/instructions/take.rs index dd988a648..11024a4eb 100644 --- a/tokens/escrow/quasar/src/instructions/take.rs +++ b/tokens/escrow/quasar/src/instructions/take.rs @@ -5,69 +5,65 @@ use { }; #[derive(Accounts)] -pub struct Take<'info> { +pub struct Take { #[account(mut)] - pub taker: &'info Signer, + pub taker: Signer, #[account( mut, has_one = maker, has_one = maker_ta_b, constraint = escrow.receive > 0, close = taker, - seeds = [b"escrow", maker], + seeds = Escrow::seeds(maker), bump = escrow.bump )] - pub escrow: &'info mut Account, + pub escrow: Account, #[account(mut)] - pub maker: &'info UncheckedAccount, - pub mint_a: &'info Account, - pub mint_b: &'info Account, + pub maker: UncheckedAccount, + pub mint_a: Account, + pub mint_b: Account, #[account(mut, init_if_needed, payer = taker, token::mint = mint_a, token::authority = taker)] - pub taker_ta_a: &'info mut Account, + pub taker_ta_a: Account, #[account(mut)] - pub taker_ta_b: &'info mut Account, + pub taker_ta_b: Account, #[account(mut, init_if_needed, payer = taker, token::mint = mint_b, token::authority = maker)] - pub maker_ta_b: &'info mut Account, + pub maker_ta_b: Account, #[account(mut)] - pub vault_ta_a: &'info mut Account, - pub rent: &'info Sysvar, - pub token_program: &'info Program, - pub system_program: &'info Program, + pub vault_ta_a: Account, + pub rent: Sysvar, + pub token_program: Program, + pub system_program: Program, } -#[inline(always)] -pub fn handle_transfer_tokens(accounts: &mut Take) -> Result<(), ProgramError> { - accounts.token_program - .transfer( - accounts.taker_ta_b, - accounts.maker_ta_b, - accounts.taker, - accounts.escrow.receive, - ) - .invoke() -} +impl Take { + #[inline(always)] + pub fn transfer_tokens(&mut self) -> Result<(), ProgramError> { + self.token_program + .transfer( + &self.taker_ta_b, + &self.maker_ta_b, + &self.taker, + self.escrow.receive, + ) + .invoke() + } -#[inline(always)] -pub fn handle_withdraw_tokens_and_close(accounts: &mut Take, bumps: &TakeBumps) -> Result<(), ProgramError> { - let maker_key = accounts.escrow.maker; - let bump = [bumps.escrow]; - let seeds: &[Seed] = &[ - Seed::from(b"escrow" as &[u8]), - Seed::from(maker_key.as_ref()), - Seed::from(&bump as &[u8]), - ]; + #[inline(always)] + pub fn withdraw_tokens_and_close(&mut self, bumps: &TakeBumps) -> Result<(), ProgramError> { + let seeds = self.escrow_seeds(bumps); - accounts.token_program - .transfer( - accounts.vault_ta_a, - accounts.taker_ta_a, - accounts.escrow, - accounts.vault_ta_a.amount(), - ) - .invoke_signed(seeds)?; + self.token_program + .transfer( + &self.vault_ta_a, + &self.taker_ta_a, + &self.escrow, + self.vault_ta_a.amount(), + ) + .invoke_signed(&seeds)?; - accounts.token_program - .close_account(accounts.vault_ta_a, accounts.taker, accounts.escrow) - .invoke_signed(seeds)?; - Ok(()) + self.token_program + .close_account(&self.vault_ta_a, &self.taker, &self.escrow) + .invoke_signed(&seeds)?; + Ok(()) + } } diff --git a/tokens/escrow/quasar/src/lib.rs b/tokens/escrow/quasar/src/lib.rs index 4bf53a1d2..9cd7d9c1a 100644 --- a/tokens/escrow/quasar/src/lib.rs +++ b/tokens/escrow/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![cfg_attr(not(test), no_std)] +#![no_std] use quasar_lang::prelude::*; @@ -19,18 +19,18 @@ mod quasar_escrow { #[instruction(discriminator = 0)] pub fn make(ctx: Ctx, deposit: u64, receive: u64) -> Result<(), ProgramError> { - instructions::handle_make_escrow(&mut ctx.accounts, receive, &ctx.bumps)?; - instructions::handle_deposit_tokens(&mut ctx.accounts, deposit) + ctx.accounts.make_escrow(receive, &ctx.bumps)?; + ctx.accounts.deposit_tokens(deposit) } #[instruction(discriminator = 1)] pub fn take(ctx: Ctx) -> Result<(), ProgramError> { - instructions::handle_transfer_tokens(&mut ctx.accounts)?; - instructions::take::handle_withdraw_tokens_and_close(&mut ctx.accounts, &ctx.bumps) + ctx.accounts.transfer_tokens()?; + ctx.accounts.withdraw_tokens_and_close(&ctx.bumps) } #[instruction(discriminator = 2)] pub fn refund(ctx: Ctx) -> Result<(), ProgramError> { - instructions::refund::handle_withdraw_tokens_and_close(&mut ctx.accounts, &ctx.bumps) + ctx.accounts.withdraw_tokens_and_close(&ctx.bumps) } } diff --git a/tokens/escrow/quasar/src/state.rs b/tokens/escrow/quasar/src/state.rs index 77d218381..04c6b2d07 100644 --- a/tokens/escrow/quasar/src/state.rs +++ b/tokens/escrow/quasar/src/state.rs @@ -2,7 +2,8 @@ use quasar_lang::prelude::*; /// Escrow state: records the maker's desired receive amount and the /// associated mint/token-account addresses. -#[account(discriminator = 1)] +#[account(discriminator = 1, set_inner)] +#[seeds(b"escrow", maker: Address)] pub struct Escrow { pub maker: Address, pub mint_a: Address, diff --git a/tokens/external-delegate-token-master/quasar/Cargo.toml b/tokens/external-delegate-token-master/quasar/Cargo.toml index c82dfe38f..bc68d5080 100644 --- a/tokens/external-delegate-token-master/quasar/Cargo.toml +++ b/tokens/external-delegate-token-master/quasar/Cargo.toml @@ -22,8 +22,8 @@ client = [] debug = [] [dependencies] -quasar-lang = "0.0" -quasar-spl = "0.0" +quasar-lang = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } +quasar-spl = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } solana-instruction = { version = "3.2.0" } solana-define-syscall = "4.0" solana-keccak-hasher = "3.1" diff --git a/tokens/external-delegate-token-master/quasar/src/lib.rs b/tokens/external-delegate-token-master/quasar/src/lib.rs index 492a1b820..985e841a9 100644 --- a/tokens/external-delegate-token-master/quasar/src/lib.rs +++ b/tokens/external-delegate-token-master/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![cfg_attr(not(test), no_std)] +#![no_std] use quasar_lang::prelude::*; use quasar_spl::{Token, TokenCpi}; @@ -24,7 +24,7 @@ mod quasar_external_delegate_token_master { /// Initialize a user account with zero Ethereum address. #[instruction(discriminator = 0)] pub fn initialize(ctx: Ctx) -> Result<(), ProgramError> { - handle_initialize(&mut ctx.accounts) + ctx.accounts.initialize() } /// Set the Ethereum address for signature verification. @@ -33,7 +33,7 @@ mod quasar_external_delegate_token_master { ctx: Ctx, ethereum_address: [u8; 20], ) -> Result<(), ProgramError> { - handle_set_ethereum_address(&mut ctx.accounts, ethereum_address) + ctx.accounts.set_ethereum_address(ethereum_address) } /// Transfer tokens using an Ethereum signature for authorisation. @@ -44,7 +44,7 @@ mod quasar_external_delegate_token_master { signature: [u8; 65], message: [u8; 32], ) -> Result<(), ProgramError> { - handle_transfer_tokens(&mut ctx.accounts, amount, &signature, &message, &ctx.bumps) + ctx.accounts.transfer_tokens(amount, &signature, &message, &ctx.bumps) } /// Transfer tokens using the Solana authority directly. @@ -53,7 +53,7 @@ mod quasar_external_delegate_token_master { ctx: Ctx, amount: u64, ) -> Result<(), ProgramError> { - handle_authority_transfer(&mut ctx.accounts, amount, &ctx.bumps) + ctx.accounts.authority_transfer(amount, &ctx.bumps) } } @@ -62,125 +62,129 @@ mod quasar_external_delegate_token_master { // --------------------------------------------------------------------------- #[derive(Accounts)] -pub struct Initialize<'info> { +pub struct Initialize { #[account(mut, init, payer = authority)] - pub user_account: &'info mut Account, + pub user_account: Account, #[account(mut)] - pub authority: &'info Signer, - pub system_program: &'info Program, + pub authority: Signer, + pub system_program: Program, } -#[inline(always)] -pub fn handle_initialize(accounts: &mut Initialize) -> Result<(), ProgramError> { - accounts.user_account - .set_inner(*accounts.authority.address(), [0u8; 20]); - Ok(()) +impl Initialize { + #[inline(always)] + pub fn initialize(&mut self) -> Result<(), ProgramError> { + self.user_account + .set_inner(*self.authority.address(), [0u8; 20]); + Ok(()) + } } #[derive(Accounts)] -pub struct SetEthereumAddress<'info> { +pub struct SetEthereumAddress { #[account(mut)] - pub user_account: &'info mut Account, - pub authority: &'info Signer, + pub user_account: Account, + pub authority: Signer, } -#[inline(always)] -pub fn handle_set_ethereum_address( - accounts: &mut SetEthereumAddress, ethereum_address: [u8; 20], -) -> Result<(), ProgramError> { - require_keys_eq!( - accounts.user_account.authority, - *accounts.authority.address(), - ProgramError::MissingRequiredSignature - ); - accounts.user_account.ethereum_address = ethereum_address; - Ok(()) +impl SetEthereumAddress { + #[inline(always)] + pub fn set_ethereum_address(&mut self, ethereum_address: [u8; 20]) -> Result<(), ProgramError> { + require_keys_eq!( + self.user_account.authority, + *self.authority.address(), + ProgramError::MissingRequiredSignature + ); + self.user_account.ethereum_address = ethereum_address; + Ok(()) + } } #[derive(Accounts)] -pub struct TransferTokens<'info> { - pub user_account: &'info Account, - pub authority: &'info Signer, +pub struct TransferTokens { + pub user_account: Account, + pub authority: Signer, #[account(mut)] - pub user_token_account: &'info mut Account, + pub user_token_account: Account, #[account(mut)] - pub recipient_token_account: &'info mut Account, + pub recipient_token_account: Account, /// PDA derived from user_account address. #[account(seeds = [user_account], bump)] - pub user_pda: &'info UncheckedAccount, - pub token_program: &'info Program, + pub user_pda: UncheckedAccount, + pub token_program: Program, } -#[inline(always)] -pub fn handle_transfer_tokens( - accounts: &TransferTokens, amount: u64, - signature: &[u8; 65], - message: &[u8; 32], - bumps: &TransferTokensBumps, -) -> Result<(), ProgramError> { - if !verify_ethereum_signature( - &accounts.user_account.ethereum_address, - message, - signature, - ) { - return Err(ProgramError::Custom(1)); // InvalidSignature - } +impl TransferTokens { + #[inline(always)] + pub fn transfer_tokens( + &mut self, + amount: u64, + signature: &[u8; 65], + message: &[u8; 32], + bumps: &TransferTokensBumps, + ) -> Result<(), ProgramError> { + if !verify_ethereum_signature( + &self.user_account.ethereum_address, + message, + signature, + ) { + return Err(ProgramError::Custom(1)); // InvalidSignature + } + + let bump = [bumps.user_pda]; + let seeds: &[Seed] = &[ + Seed::from(self.user_account.address().as_ref()), + Seed::from(&bump as &[u8]), + ]; - let bump = [bumps.user_pda]; - let seeds: &[Seed] = &[ - Seed::from(accounts.user_account.address().as_ref()), - Seed::from(&bump as &[u8]), - ]; - - accounts.token_program - .transfer( - accounts.user_token_account, - accounts.recipient_token_account, - accounts.user_pda, - amount, - ) - .invoke_signed(seeds) + self.token_program + .transfer( + &self.user_token_account, + &self.recipient_token_account, + &self.user_pda, + amount, + ) + .invoke_signed(seeds) + } } #[derive(Accounts)] -pub struct AuthorityTransfer<'info> { - pub user_account: &'info Account, - pub authority: &'info Signer, +pub struct AuthorityTransfer { + pub user_account: Account, + pub authority: Signer, #[account(mut)] - pub user_token_account: &'info mut Account, + pub user_token_account: Account, #[account(mut)] - pub recipient_token_account: &'info mut Account, + pub recipient_token_account: Account, /// PDA derived from user_account address. #[account(seeds = [user_account], bump)] - pub user_pda: &'info UncheckedAccount, - pub token_program: &'info Program, + pub user_pda: UncheckedAccount, + pub token_program: Program, } -#[inline(always)] -pub fn handle_authority_transfer( - accounts: &AuthorityTransfer, amount: u64, - bumps: &AuthorityTransferBumps, -) -> Result<(), ProgramError> { - require_keys_eq!( - accounts.user_account.authority, - *accounts.authority.address(), - ProgramError::MissingRequiredSignature - ); - - let bump = [bumps.user_pda]; - let seeds: &[Seed] = &[ - Seed::from(accounts.user_account.address().as_ref()), - Seed::from(&bump as &[u8]), - ]; - - accounts.token_program - .transfer( - accounts.user_token_account, - accounts.recipient_token_account, - accounts.user_pda, - amount, - ) - .invoke_signed(seeds) +impl AuthorityTransfer { + #[inline(always)] + pub fn authority_transfer(&mut self, amount: u64, bumps: &AuthorityTransferBumps) -> Result<(), ProgramError> { + require_keys_eq!( + self.user_account.authority, + *self.authority.address(), + ProgramError::MissingRequiredSignature + ); + + let bump = [bumps.user_pda]; + let seeds: &[Seed] = &[ + Seed::from(self.user_account.address().as_ref()), + Seed::from(&bump as &[u8]), + ]; + + self.token_program + .transfer( + &self.user_token_account, + &self.recipient_token_account, + &self.user_pda, + amount, + ) + .invoke_signed(seeds) + } } // --------------------------------------------------------------------------- diff --git a/tokens/nft-minter/quasar/Cargo.toml b/tokens/nft-minter/quasar/Cargo.toml index 04f79cfe3..1dc6abaab 100644 --- a/tokens/nft-minter/quasar/Cargo.toml +++ b/tokens/nft-minter/quasar/Cargo.toml @@ -22,8 +22,8 @@ client = [] debug = [] [dependencies] -quasar-lang = "0.0" -quasar-spl = { version = "0.0", features = ["metadata"] } +quasar-lang = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } +quasar-spl = { git = "https://github.com/blueshift-gg/quasar", branch = "master", features = ["metadata"] } solana-instruction = { version = "3.2.0" } [dev-dependencies] diff --git a/tokens/nft-minter/quasar/src/lib.rs b/tokens/nft-minter/quasar/src/lib.rs index 50921d447..fc6ce08c1 100644 --- a/tokens/nft-minter/quasar/src/lib.rs +++ b/tokens/nft-minter/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![cfg_attr(not(test), no_std)] +#![no_std] use quasar_lang::prelude::*; use quasar_spl::{ @@ -24,92 +24,95 @@ mod quasar_nft_minter { nft_symbol: String, nft_uri: String, ) -> Result<(), ProgramError> { - handle_mint_nft(&mut ctx.accounts, &nft_name, &nft_symbol, &nft_uri) + ctx.accounts.mint_nft(&nft_name, &nft_symbol, &nft_uri) } } /// All accounts needed to mint an NFT in one transaction. #[derive(Accounts)] -pub struct MintNft<'info> { +pub struct MintNft { #[account(mut)] - pub payer: &'info Signer, + pub payer: Signer, /// Metadata PDA — initialised by the Metaplex program. #[account(mut)] - pub metadata_account: &'info UncheckedAccount, + pub metadata_account: UncheckedAccount, /// Master edition PDA — initialised by the Metaplex program. #[account(mut)] - pub edition_account: &'info UncheckedAccount, + pub edition_account: UncheckedAccount, /// NFT mint (decimals = 0). #[account(mut, init, payer = payer, mint::decimals = 0, mint::authority = payer, mint::freeze_authority = payer)] - pub mint_account: &'info mut Account, + pub mint_account: Account, /// Token account holding the NFT. #[account(mut, init_if_needed, payer = payer, token::mint = mint_account, token::authority = payer)] - pub associated_token_account: &'info mut Account, + pub associated_token_account: Account, - pub token_program: &'info Program, - pub token_metadata_program: &'info MetadataProgram, - pub system_program: &'info Program, - pub rent: &'info Sysvar, + pub token_program: Program, + pub token_metadata_program: MetadataProgram, + pub system_program: Program, + pub rent: Sysvar, } -#[inline(always)] -pub fn handle_mint_nft( - accounts: &MintNft, nft_name: &str, - nft_symbol: &str, - nft_uri: &str, -) -> Result<(), ProgramError> { - // 1. Mint one token to the associated token account. - log("Minting token"); - accounts.token_program - .mint_to( - accounts.mint_account, - accounts.associated_token_account, - accounts.payer, - 1u64, - ) - .invoke()?; +impl MintNft { + #[inline(always)] + pub fn mint_nft( + &mut self, + nft_name: &str, + nft_symbol: &str, + nft_uri: &str, + ) -> Result<(), ProgramError> { + // 1. Mint one token to the associated token account. + log("Minting token"); + self.token_program + .mint_to( + &self.mint_account, + &self.associated_token_account, + &self.payer, + 1u64, + ) + .invoke()?; - // 2. Create Metaplex metadata account. - log("Creating metadata account"); - accounts.token_metadata_program - .create_metadata_accounts_v3( - accounts.metadata_account, - accounts.mint_account, - accounts.payer, - accounts.payer, - accounts.payer, - accounts.system_program, - accounts.rent, - nft_name, - nft_symbol, - nft_uri, - 0, // seller_fee_basis_points - false, // is_mutable - true, // update_authority_is_signer - ) - .invoke()?; + // 2. Create Metaplex metadata account. + log("Creating metadata account"); + self.token_metadata_program + .create_metadata_accounts_v3( + &self.metadata_account, + &self.mint_account, + &self.payer, + &self.payer, + &self.payer, + &self.system_program, + &self.rent, + nft_name, + nft_symbol, + nft_uri, + 0, // seller_fee_basis_points + false, // is_mutable + true, // update_authority_is_signer + ) + .invoke()?; - // 3. Create master edition (makes it a verified NFT). - log("Creating master edition account"); - accounts.token_metadata_program - .create_master_edition_v3( - accounts.edition_account, - accounts.mint_account, - accounts.payer, // update_authority - accounts.payer, // mint_authority - accounts.payer, // payer - accounts.metadata_account, - accounts.token_program, - accounts.system_program, - accounts.rent, - None, // max_supply = unlimited - ) - .invoke()?; + // 3. Create master edition (makes it a verified NFT). + log("Creating master edition account"); + self.token_metadata_program + .create_master_edition_v3( + &self.edition_account, + &self.mint_account, + &self.payer, // update_authority + &self.payer, // mint_authority + &self.payer, // payer + &self.metadata_account, + &self.token_program, + &self.system_program, + &self.rent, + None, // max_supply = unlimited + ) + .invoke()?; - log("NFT minted successfully."); - Ok(()) + log("NFT minted successfully."); + Ok(()) + } } diff --git a/tokens/nft-operations/quasar/Cargo.toml b/tokens/nft-operations/quasar/Cargo.toml index 629c249c9..c5716a2f5 100644 --- a/tokens/nft-operations/quasar/Cargo.toml +++ b/tokens/nft-operations/quasar/Cargo.toml @@ -22,8 +22,8 @@ client = [] debug = [] [dependencies] -quasar-lang = "0.0" -quasar-spl = { version = "0.0", features = ["metadata"] } +quasar-lang = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } +quasar-spl = { git = "https://github.com/blueshift-gg/quasar", branch = "master", features = ["metadata"] } solana-instruction = { version = "3.2.0" } [dev-dependencies] diff --git a/tokens/nft-operations/quasar/src/instructions/create_collection.rs b/tokens/nft-operations/quasar/src/instructions/create_collection.rs index 29ef6450b..c41c16a7a 100644 --- a/tokens/nft-operations/quasar/src/instructions/create_collection.rs +++ b/tokens/nft-operations/quasar/src/instructions/create_collection.rs @@ -8,81 +8,81 @@ use quasar_spl::{ /// /// The PDA `["authority"]` acts as mint authority and update authority. #[derive(Accounts)] -pub struct CreateCollection<'info> { +pub struct CreateCollection { #[account(mut)] - pub user: &'info Signer, + pub user: Signer, #[account(mut, init, payer = user, mint::decimals = 0, mint::authority = mint_authority, mint::freeze_authority = mint_authority)] - pub mint: &'info mut Account, + pub mint: Account, /// PDA used as mint authority and update authority. #[account(seeds = [b"authority"], bump)] - pub mint_authority: &'info UncheckedAccount, + pub mint_authority: UncheckedAccount, /// Metadata PDA — initialised by the Metaplex program. #[account(mut)] - pub metadata: &'info UncheckedAccount, + pub metadata: UncheckedAccount, /// Master edition PDA — initialised by the Metaplex program. #[account(mut)] - pub master_edition: &'info UncheckedAccount, + pub master_edition: UncheckedAccount, /// Token account to hold the collection NFT. #[account(mut, init_if_needed, payer = user, token::mint = mint, token::authority = user)] - pub destination: &'info mut Account, - pub system_program: &'info Program, - pub token_program: &'info Program, - pub token_metadata_program: &'info MetadataProgram, - pub rent: &'info Sysvar, + pub destination: Account, + pub system_program: Program, + pub token_program: Program, + pub token_metadata_program: MetadataProgram, + pub rent: Sysvar, } -#[inline(always)] -pub fn handle_create_collection( - accounts: &CreateCollection, bumps: &CreateCollectionBumps, -) -> Result<(), ProgramError> { - let bump = [bumps.mint_authority]; - let seeds: &[Seed] = &[ - Seed::from(b"authority" as &[u8]), - Seed::from(&bump as &[u8]), - ]; +impl CreateCollection { + #[inline(always)] + pub fn create_collection(&mut self, bumps: &CreateCollectionBumps) -> Result<(), ProgramError> { + let bump = [bumps.mint_authority]; + let seeds: &[Seed] = &[ + Seed::from(b"authority" as &[u8]), + Seed::from(&bump as &[u8]), + ]; - // Mint 1 token to the destination. - accounts.token_program - .mint_to(accounts.mint, accounts.destination, accounts.mint_authority, 1u64) - .invoke_signed(seeds)?; - log("Collection NFT minted!"); + // Mint 1 token to the destination. + self.token_program + .mint_to(&self.mint, &self.destination, &self.mint_authority, 1u64) + .invoke_signed(seeds)?; + log("Collection NFT minted!"); - // Create metadata account. - accounts.token_metadata_program - .create_metadata_accounts_v3( - accounts.metadata, - accounts.mint, - accounts.mint_authority, - accounts.user, - accounts.mint_authority, - accounts.system_program, - accounts.rent, - "DummyCollection", - "DC", - "", - 0, // seller_fee_basis_points - true, // is_mutable - true, // update_authority_is_signer - ) - .invoke_signed(seeds)?; - log("Metadata Account created!"); + // Create metadata account. + self.token_metadata_program + .create_metadata_accounts_v3( + &self.metadata, + &self.mint, + &self.mint_authority, + &self.user, + &self.mint_authority, + &self.system_program, + &self.rent, + "DummyCollection", + "DC", + "", + 0, // seller_fee_basis_points + true, // is_mutable + true, // update_authority_is_signer + ) + .invoke_signed(seeds)?; + log("Metadata Account created!"); - // Create master edition. - accounts.token_metadata_program - .create_master_edition_v3( - accounts.master_edition, - accounts.mint, - accounts.mint_authority, // update_authority - accounts.mint_authority, // mint_authority - accounts.user, // payer - accounts.metadata, - accounts.token_program, - accounts.system_program, - accounts.rent, - Some(0), // max_supply = 0 means unique 1/1 - ) - .invoke_signed(seeds)?; - log("Master Edition Account created"); + // Create master edition. + self.token_metadata_program + .create_master_edition_v3( + &self.master_edition, + &self.mint, + &self.mint_authority, // update_authority + &self.mint_authority, // mint_authority + &self.user, // payer + &self.metadata, + &self.token_program, + &self.system_program, + &self.rent, + Some(0), // max_supply = 0 means unique 1/1 + ) + .invoke_signed(seeds)?; + log("Master Edition Account created"); - Ok(()) + Ok(()) + } } diff --git a/tokens/nft-operations/quasar/src/instructions/mint_nft.rs b/tokens/nft-operations/quasar/src/instructions/mint_nft.rs index 14264bbe2..7ca8b92d8 100644 --- a/tokens/nft-operations/quasar/src/instructions/mint_nft.rs +++ b/tokens/nft-operations/quasar/src/instructions/mint_nft.rs @@ -6,82 +6,84 @@ use quasar_spl::{ /// Accounts for minting an individual NFT with a collection reference. #[derive(Accounts)] -pub struct MintNft<'info> { +pub struct MintNft { #[account(mut)] - pub owner: &'info Signer, + pub owner: Signer, #[account(mut, init, payer = owner, mint::decimals = 0, mint::authority = mint_authority, mint::freeze_authority = mint_authority)] - pub mint: &'info mut Account, + pub mint: Account, /// Token account to hold the NFT. #[account(mut, init_if_needed, payer = owner, token::mint = mint, token::authority = owner)] - pub destination: &'info mut Account, + pub destination: Account, /// Metadata PDA — initialised by the Metaplex program. #[account(mut)] - pub metadata: &'info UncheckedAccount, + pub metadata: UncheckedAccount, /// Master edition PDA — initialised by the Metaplex program. #[account(mut)] - pub master_edition: &'info UncheckedAccount, + pub master_edition: UncheckedAccount, /// PDA used as mint authority and update authority. #[account(seeds = [b"authority"], bump)] - pub mint_authority: &'info UncheckedAccount, + pub mint_authority: UncheckedAccount, /// The collection mint (must already exist). #[account(mut)] - pub collection_mint: &'info Account, - pub system_program: &'info Program, - pub token_program: &'info Program, - pub token_metadata_program: &'info MetadataProgram, - pub rent: &'info Sysvar, + pub collection_mint: Account, + pub system_program: Program, + pub token_program: Program, + pub token_metadata_program: MetadataProgram, + pub rent: Sysvar, } -#[inline(always)] -pub fn handle_mint_nft(accounts: &MintNft, bumps: &MintNftBumps) -> Result<(), ProgramError> { - let bump = [bumps.mint_authority]; - let seeds: &[Seed] = &[ - Seed::from(b"authority" as &[u8]), - Seed::from(&bump as &[u8]), - ]; +impl MintNft { + #[inline(always)] + pub fn mint_nft(&mut self, bumps: &MintNftBumps) -> Result<(), ProgramError> { + let bump = [bumps.mint_authority]; + let seeds: &[Seed] = &[ + Seed::from(b"authority" as &[u8]), + Seed::from(&bump as &[u8]), + ]; - // Mint 1 token to the destination. - accounts.token_program - .mint_to(accounts.mint, accounts.destination, accounts.mint_authority, 1u64) - .invoke_signed(seeds)?; - log("NFT minted!"); + // Mint 1 token to the destination. + self.token_program + .mint_to(&self.mint, &self.destination, &self.mint_authority, 1u64) + .invoke_signed(seeds)?; + log("NFT minted!"); - // Create metadata with collection reference. - // Note: The collection is set as unverified here; call verify_collection - // separately to verify it. - accounts.token_metadata_program - .create_metadata_accounts_v3( - accounts.metadata, - accounts.mint, - accounts.mint_authority, - accounts.owner, - accounts.mint_authority, - accounts.system_program, - accounts.rent, - "Mint Test", - "YAY", - "", - 0, // seller_fee_basis_points - true, // is_mutable - true, // update_authority_is_signer - ) - .invoke_signed(seeds)?; + // Create metadata with collection reference. + // Note: The collection is set as unverified here; call verify_collection + // separately to verify it. + self.token_metadata_program + .create_metadata_accounts_v3( + &self.metadata, + &self.mint, + &self.mint_authority, + &self.owner, + &self.mint_authority, + &self.system_program, + &self.rent, + "Mint Test", + "YAY", + "", + 0, // seller_fee_basis_points + true, // is_mutable + true, // update_authority_is_signer + ) + .invoke_signed(seeds)?; - // Create master edition. - accounts.token_metadata_program - .create_master_edition_v3( - accounts.master_edition, - accounts.mint, - accounts.mint_authority, // update_authority - accounts.mint_authority, // mint_authority - accounts.owner, // payer - accounts.metadata, - accounts.token_program, - accounts.system_program, - accounts.rent, - Some(0), // max_supply = 0 means unique 1/1 - ) - .invoke_signed(seeds)?; + // Create master edition. + self.token_metadata_program + .create_master_edition_v3( + &self.master_edition, + &self.mint, + &self.mint_authority, // update_authority + &self.mint_authority, // mint_authority + &self.owner, // payer + &self.metadata, + &self.token_program, + &self.system_program, + &self.rent, + Some(0), // max_supply = 0 means unique 1/1 + ) + .invoke_signed(seeds)?; - Ok(()) + Ok(()) + } } diff --git a/tokens/nft-operations/quasar/src/instructions/verify_collection.rs b/tokens/nft-operations/quasar/src/instructions/verify_collection.rs index 9101bbc69..8cd065087 100644 --- a/tokens/nft-operations/quasar/src/instructions/verify_collection.rs +++ b/tokens/nft-operations/quasar/src/instructions/verify_collection.rs @@ -11,46 +11,46 @@ use quasar_spl::metadata::{MetadataCpi, MetadataProgram}; /// `UncheckedAccount` and rely on the Metaplex program itself to validate /// the accounts during CPI — the onchain program enforces correctness. #[derive(Accounts)] -pub struct VerifyCollectionMint<'info> { - pub authority: &'info Signer, +pub struct VerifyCollectionMint { + pub authority: Signer, /// The NFT's metadata account (will be updated with verified=true). #[account(mut)] - pub metadata: &'info UncheckedAccount, + pub metadata: UncheckedAccount, /// PDA used as collection authority. #[account(seeds = [b"authority"], bump)] - pub mint_authority: &'info UncheckedAccount, + pub mint_authority: UncheckedAccount, /// The collection mint. - pub collection_mint: &'info UncheckedAccount, + pub collection_mint: UncheckedAccount, /// The collection's metadata account. #[account(mut)] - pub collection_metadata: &'info UncheckedAccount, + pub collection_metadata: UncheckedAccount, /// The collection's master edition account. - pub collection_master_edition: &'info UncheckedAccount, - pub system_program: &'info Program, - pub token_metadata_program: &'info MetadataProgram, + pub collection_master_edition: UncheckedAccount, + pub system_program: Program, + pub token_metadata_program: MetadataProgram, } -#[inline(always)] -pub fn handle_verify_collection( - accounts: &VerifyCollectionMint, bumps: &VerifyCollectionMintBumps, -) -> Result<(), ProgramError> { - let bump = [bumps.mint_authority]; - let seeds: &[Seed] = &[ - Seed::from(b"authority" as &[u8]), - Seed::from(&bump as &[u8]), - ]; +impl VerifyCollectionMint { + #[inline(always)] + pub fn verify_collection(&mut self, bumps: &VerifyCollectionMintBumps) -> Result<(), ProgramError> { + let bump = [bumps.mint_authority]; + let seeds: &[Seed] = &[ + Seed::from(b"authority" as &[u8]), + Seed::from(&bump as &[u8]), + ]; - accounts.token_metadata_program - .verify_sized_collection_item( - accounts.metadata, - accounts.mint_authority, - accounts.authority, // payer - accounts.collection_mint, - accounts.collection_metadata, - accounts.collection_master_edition, - ) - .invoke_signed(seeds)?; + self.token_metadata_program + .verify_sized_collection_item( + &self.metadata, + &self.mint_authority, + &self.authority, // payer + &self.collection_mint, + &self.collection_metadata, + &self.collection_master_edition, + ) + .invoke_signed(seeds)?; - log("Collection Verified!"); - Ok(()) + log("Collection Verified!"); + Ok(()) + } } diff --git a/tokens/nft-operations/quasar/src/lib.rs b/tokens/nft-operations/quasar/src/lib.rs index e40c748a8..e52a98859 100644 --- a/tokens/nft-operations/quasar/src/lib.rs +++ b/tokens/nft-operations/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![cfg_attr(not(test), no_std)] +#![no_std] use quasar_lang::prelude::*; @@ -21,18 +21,18 @@ mod quasar_nft_operations { /// Create a collection NFT: mint, metadata, and master edition. #[instruction(discriminator = 0)] pub fn create_collection(ctx: Ctx) -> Result<(), ProgramError> { - instructions::handle_create_collection(&mut ctx.accounts, &ctx.bumps) + ctx.accounts.create_collection(&ctx.bumps) } /// Mint an individual NFT with a reference to the collection. #[instruction(discriminator = 1)] pub fn mint_nft(ctx: Ctx) -> Result<(), ProgramError> { - instructions::handle_mint_nft(&mut ctx.accounts, &ctx.bumps) + ctx.accounts.mint_nft(&ctx.bumps) } /// Verify the NFT as a member of the collection. #[instruction(discriminator = 2)] pub fn verify_collection(ctx: Ctx) -> Result<(), ProgramError> { - instructions::handle_verify_collection(&mut ctx.accounts, &ctx.bumps) + ctx.accounts.verify_collection(&ctx.bumps) } } diff --git a/tokens/pda-mint-authority/quasar/Cargo.toml b/tokens/pda-mint-authority/quasar/Cargo.toml index 330e74acf..6b6367f50 100644 --- a/tokens/pda-mint-authority/quasar/Cargo.toml +++ b/tokens/pda-mint-authority/quasar/Cargo.toml @@ -21,8 +21,8 @@ client = [] debug = [] [dependencies] -quasar-lang = "0.0" -quasar-spl = "0.0" +quasar-lang = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } +quasar-spl = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } solana-instruction = { version = "3.2.0" } [dev-dependencies] diff --git a/tokens/pda-mint-authority/quasar/src/lib.rs b/tokens/pda-mint-authority/quasar/src/lib.rs index 23f8fc5f3..7acc6bd57 100644 --- a/tokens/pda-mint-authority/quasar/src/lib.rs +++ b/tokens/pda-mint-authority/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![cfg_attr(not(test), no_std)] +#![no_std] use quasar_lang::prelude::*; use quasar_spl::{Mint, Token, TokenCpi}; @@ -24,59 +24,63 @@ mod quasar_pda_mint_authority { /// Create a token mint at a PDA. The PDA is its own mint authority. #[instruction(discriminator = 0)] pub fn create_mint(ctx: Ctx, _decimals: u8) -> Result<(), ProgramError> { - handle_create_mint(&mut ctx.accounts) + ctx.accounts.create_mint() } /// Mint tokens using the PDA mint authority. #[instruction(discriminator = 1)] pub fn mint_tokens(ctx: Ctx, amount: u64) -> Result<(), ProgramError> { - handle_mint_tokens(&mut ctx.accounts, amount, ctx.bumps.mint) + ctx.accounts.mint_tokens(amount, ctx.bumps.mint) } } /// Create the mint at a PDA. The mint authority is the mint PDA itself. #[derive(Accounts)] -pub struct CreateMint<'info> { +pub struct CreateMint { #[account(mut)] - pub payer: &'info Signer, + pub payer: Signer, /// The mint account at PDA ["mint"]. Its authority is set to itself. #[account(mut, init, payer = payer, seeds = [b"mint"], bump, mint::decimals = 9, mint::authority = mint)] - pub mint: &'info mut Account, - pub rent: &'info Sysvar, - pub token_program: &'info Program, - pub system_program: &'info Program, + pub mint: Account, + pub rent: Sysvar, + pub token_program: Program, + pub system_program: Program, } -#[inline(always)] -pub fn handle_create_mint(accounts: &CreateMint) -> Result<(), ProgramError> { - // Mint is created and initialised by Quasar's #[account(init)]. - Ok(()) +impl CreateMint { + #[inline(always)] + pub fn create_mint(&mut self) -> Result<(), ProgramError> { + // Mint is created and initialised by Quasar's #[account(init)]. + Ok(()) + } } /// Mint tokens to a token account, signing with the PDA mint authority. #[derive(Accounts)] -pub struct MintTokens<'info> { +pub struct MintTokens { #[account(mut)] - pub payer: &'info Signer, + pub payer: Signer, /// The PDA mint whose authority is itself. #[account(mut, seeds = [b"mint"], bump)] - pub mint: &'info mut Account, + pub mint: Account, /// Recipient token account (must already exist). #[account(mut)] - pub token_account: &'info mut Account, - pub token_program: &'info Program, + pub token_account: Account, + pub token_program: Program, } -#[inline(always)] -pub fn handle_mint_tokens(accounts: &mut MintTokens, amount: u64, mint_bump: u8) -> Result<(), ProgramError> { - // The PDA mint is its own authority. Build signer seeds. - let bump = [mint_bump]; - let seeds: &[Seed] = &[ - Seed::from(b"mint" as &[u8]), - Seed::from(&bump as &[u8]), - ]; +impl MintTokens { + #[inline(always)] + pub fn mint_tokens(&mut self, amount: u64, mint_bump: u8) -> Result<(), ProgramError> { + // The PDA mint is its own authority. Build signer seeds. + let bump = [mint_bump]; + let seeds: &[Seed] = &[ + Seed::from(b"mint" as &[u8]), + Seed::from(&bump as &[u8]), + ]; - accounts.token_program - .mint_to(accounts.mint, accounts.token_account, accounts.mint, amount) - .invoke_signed(seeds) + self.token_program + .mint_to(&self.mint, &self.token_account, &self.mint, amount) + .invoke_signed(seeds) + } } diff --git a/tokens/spl-token-minter/quasar/Cargo.toml b/tokens/spl-token-minter/quasar/Cargo.toml index 092c37493..6ad8ed2b4 100644 --- a/tokens/spl-token-minter/quasar/Cargo.toml +++ b/tokens/spl-token-minter/quasar/Cargo.toml @@ -22,8 +22,8 @@ client = [] debug = [] [dependencies] -quasar-lang = "0.0" -quasar-spl = { version = "0.0", features = ["metadata"] } +quasar-lang = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } +quasar-spl = { git = "https://github.com/blueshift-gg/quasar", branch = "master", features = ["metadata"] } solana-instruction = { version = "3.2.0" } [dev-dependencies] diff --git a/tokens/spl-token-minter/quasar/src/instructions/create.rs b/tokens/spl-token-minter/quasar/src/instructions/create.rs index ba3f8b284..84b257aff 100644 --- a/tokens/spl-token-minter/quasar/src/instructions/create.rs +++ b/tokens/spl-token-minter/quasar/src/instructions/create.rs @@ -9,9 +9,9 @@ use quasar_spl::{ /// The mint is initialised via Quasar's `#[account(init)]`. The metadata /// PDA is created by CPI-ing into the Metaplex Token Metadata program. #[derive(Accounts)] -pub struct CreateToken<'info> { +pub struct CreateToken { #[account(mut)] - pub payer: &'info Signer, + pub payer: Signer, #[account( mut, init, @@ -20,42 +20,45 @@ pub struct CreateToken<'info> { mint::authority = payer, mint::freeze_authority = payer, )] - pub mint_account: &'info mut Account, + pub mint_account: Account, /// The metadata PDA — will be initialised by the Metaplex program. #[account(mut)] - pub metadata_account: &'info UncheckedAccount, - pub token_program: &'info Program, - pub token_metadata_program: &'info MetadataProgram, - pub system_program: &'info Program, - pub rent: &'info Sysvar, + pub metadata_account: UncheckedAccount, + pub token_program: Program, + pub token_metadata_program: MetadataProgram, + pub system_program: Program, + pub rent: Sysvar, } -#[inline(always)] -pub fn handle_create_token( - accounts: &CreateToken, token_name: &str, - token_symbol: &str, - token_uri: &str, -) -> Result<(), ProgramError> { - log("Creating metadata account"); +impl CreateToken { + #[inline(always)] + pub fn create_token( + &mut self, + token_name: &str, + token_symbol: &str, + token_uri: &str, + ) -> Result<(), ProgramError> { + log("Creating metadata account"); - accounts.token_metadata_program - .create_metadata_accounts_v3( - accounts.metadata_account, - accounts.mint_account, - accounts.payer, // mint_authority - accounts.payer, // payer - accounts.payer, // update_authority - accounts.system_program, - accounts.rent, - token_name, - token_symbol, - token_uri, - 0, // seller_fee_basis_points - false, // is_mutable - true, // update_authority_is_signer - ) - .invoke()?; + self.token_metadata_program + .create_metadata_accounts_v3( + &self.metadata_account, + &self.mint_account, + &self.payer, // mint_authority + &self.payer, // payer + &self.payer, // update_authority + &self.system_program, + &self.rent, + token_name, + token_symbol, + token_uri, + 0, // seller_fee_basis_points + false, // is_mutable + true, // update_authority_is_signer + ) + .invoke()?; - log("Token created successfully."); - Ok(()) + log("Token created successfully."); + Ok(()) + } } diff --git a/tokens/spl-token-minter/quasar/src/instructions/mint.rs b/tokens/spl-token-minter/quasar/src/instructions/mint.rs index 127797d00..00b968e76 100644 --- a/tokens/spl-token-minter/quasar/src/instructions/mint.rs +++ b/tokens/spl-token-minter/quasar/src/instructions/mint.rs @@ -3,36 +3,38 @@ use quasar_spl::{Mint, Token, TokenCpi}; /// Accounts for minting tokens to a recipient's token account. #[derive(Accounts)] -pub struct MintToken<'info> { +pub struct MintToken { #[account(mut)] - pub mint_authority: &'info Signer, - pub recipient: &'info UncheckedAccount, + pub mint_authority: Signer, + pub recipient: UncheckedAccount, #[account(mut)] - pub mint_account: &'info mut Account, + pub mint_account: Account, #[account(mut, init_if_needed, payer = mint_authority, token::mint = mint_account, token::authority = recipient)] - pub associated_token_account: &'info mut Account, - pub token_program: &'info Program, - pub system_program: &'info Program, + pub associated_token_account: Account, + pub token_program: Program, + pub system_program: Program, } -#[inline(always)] -pub fn handle_mint_token(accounts: &mut MintToken, amount: u64) -> Result<(), ProgramError> { - log("Minting tokens to associated token account..."); +impl MintToken { + #[inline(always)] + pub fn mint_token(&mut self, amount: u64) -> Result<(), ProgramError> { + log("Minting tokens to associated token account..."); - let decimals = accounts.mint_account.decimals(); - let adjusted_amount = amount - .checked_mul(10u64.pow(decimals as u32)) - .ok_or(ProgramError::ArithmeticOverflow)?; + let decimals = self.mint_account.decimals(); + let adjusted_amount = amount + .checked_mul(10u64.pow(decimals as u32)) + .ok_or(ProgramError::ArithmeticOverflow)?; - accounts.token_program - .mint_to( - accounts.mint_account, - accounts.associated_token_account, - accounts.mint_authority, - adjusted_amount, - ) - .invoke()?; + self.token_program + .mint_to( + &self.mint_account, + &self.associated_token_account, + &self.mint_authority, + adjusted_amount, + ) + .invoke()?; - log("Token minted successfully."); - Ok(()) + log("Token minted successfully."); + Ok(()) + } } diff --git a/tokens/spl-token-minter/quasar/src/lib.rs b/tokens/spl-token-minter/quasar/src/lib.rs index d5701f343..bb4c0c079 100644 --- a/tokens/spl-token-minter/quasar/src/lib.rs +++ b/tokens/spl-token-minter/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![cfg_attr(not(test), no_std)] +#![no_std] use quasar_lang::prelude::*; @@ -25,11 +25,11 @@ mod quasar_spl_token_minter { token_symbol: String, token_uri: String, ) -> Result<(), ProgramError> { - instructions::handle_create_token(&mut ctx.accounts, &token_name, &token_symbol, &token_uri) + ctx.accounts.create_token(&token_name, &token_symbol, &token_uri) } #[instruction(discriminator = 1)] pub fn mint_token(ctx: Ctx, amount: u64) -> Result<(), ProgramError> { - instructions::handle_mint_token(&mut ctx.accounts, amount) + ctx.accounts.mint_token(amount) } } diff --git a/tokens/token-extensions/basics/quasar/Cargo.toml b/tokens/token-extensions/basics/quasar/Cargo.toml index 108b7fdc4..46facab14 100644 --- a/tokens/token-extensions/basics/quasar/Cargo.toml +++ b/tokens/token-extensions/basics/quasar/Cargo.toml @@ -21,8 +21,8 @@ client = [] debug = [] [dependencies] -quasar-lang = "0.0" -quasar-spl = "0.0" +quasar-lang = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } +quasar-spl = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } solana-instruction = { version = "3.2.0" } [dev-dependencies] diff --git a/tokens/token-extensions/basics/quasar/src/lib.rs b/tokens/token-extensions/basics/quasar/src/lib.rs index f128c56ee..7cddd4fa2 100644 --- a/tokens/token-extensions/basics/quasar/src/lib.rs +++ b/tokens/token-extensions/basics/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![cfg_attr(not(test), no_std)] +#![no_std] use quasar_lang::{ cpi::{CpiCall, InstructionAccount}, @@ -34,83 +34,87 @@ mod quasar_token_2022_basics { /// Mint tokens to a recipient's token account. #[instruction(discriminator = 0)] pub fn mint_token(ctx: Ctx, amount: u64) -> Result<(), ProgramError> { - handle_mint_token(&mut ctx.accounts, amount) + ctx.accounts.mint_token(amount) } /// Transfer tokens using transfer_checked (required for Token-2022). #[instruction(discriminator = 1)] pub fn transfer_token(ctx: Ctx, amount: u64) -> Result<(), ProgramError> { - handle_transfer_token(&mut ctx.accounts, amount) + ctx.accounts.transfer_token(amount) } } /// Accounts for minting tokens via Token-2022. #[derive(Accounts)] -pub struct MintToken<'info> { +pub struct MintToken { #[account(mut)] - pub authority: &'info Signer, + pub authority: Signer, #[account(mut)] - pub mint: &'info mut UncheckedAccount, + pub mint: UncheckedAccount, #[account(mut)] - pub receiver: &'info mut UncheckedAccount, - pub token_program: &'info Program, + pub receiver: UncheckedAccount, + pub token_program: Program, } -#[inline(always)] -pub fn handle_mint_token(accounts: &mut MintToken, amount: u64) -> Result<(), ProgramError> { - // SPL Token MintTo instruction: opcode 7, amount as u64 LE. - let data = build_u64_data(7, amount); - CpiCall::new( - accounts.token_program.to_account_view().address(), - [ - InstructionAccount::writable(accounts.mint.to_account_view().address()), - InstructionAccount::writable(accounts.receiver.to_account_view().address()), - InstructionAccount::readonly_signer(accounts.authority.to_account_view().address()), - ], - [ - accounts.mint.to_account_view(), - accounts.receiver.to_account_view(), - accounts.authority.to_account_view(), - ], - data, - ) - .invoke() +impl MintToken { + #[inline(always)] + pub fn mint_token(&mut self, amount: u64) -> Result<(), ProgramError> { + // SPL Token MintTo instruction: opcode 7, amount as u64 LE. + let data = build_u64_data(7, amount); + CpiCall::new( + self.token_program.to_account_view().address(), + [ + InstructionAccount::writable(self.mint.to_account_view().address()), + InstructionAccount::writable(self.receiver.to_account_view().address()), + InstructionAccount::readonly_signer(self.authority.to_account_view().address()), + ], + [ + self.mint.to_account_view(), + self.receiver.to_account_view(), + self.authority.to_account_view(), + ], + data, + ) + .invoke() + } } /// Accounts for transferring tokens via Token-2022 transfer_checked. #[derive(Accounts)] -pub struct TransferToken<'info> { +pub struct TransferToken { #[account(mut)] - pub sender: &'info Signer, + pub sender: Signer, #[account(mut)] - pub from: &'info mut UncheckedAccount, - pub mint: &'info UncheckedAccount, + pub from: UncheckedAccount, + pub mint: UncheckedAccount, #[account(mut)] - pub to: &'info mut UncheckedAccount, - pub token_program: &'info Program, + pub to: UncheckedAccount, + pub token_program: Program, } -#[inline(always)] -pub fn handle_transfer_token(accounts: &mut TransferToken, amount: u64) -> Result<(), ProgramError> { - // SPL Token TransferChecked instruction: opcode 12, amount as u64 LE, decimals as u8. - let data = build_transfer_checked_data(amount, 6); - CpiCall::new( - accounts.token_program.to_account_view().address(), - [ - InstructionAccount::writable(accounts.from.to_account_view().address()), - InstructionAccount::readonly(accounts.mint.to_account_view().address()), - InstructionAccount::writable(accounts.to.to_account_view().address()), - InstructionAccount::readonly_signer(accounts.sender.to_account_view().address()), - ], - [ - accounts.from.to_account_view(), - accounts.mint.to_account_view(), - accounts.to.to_account_view(), - accounts.sender.to_account_view(), - ], - data, - ) - .invoke() +impl TransferToken { + #[inline(always)] + pub fn transfer_token(&mut self, amount: u64) -> Result<(), ProgramError> { + // SPL Token TransferChecked instruction: opcode 12, amount as u64 LE, decimals as u8. + let data = build_transfer_checked_data(amount, 6); + CpiCall::new( + self.token_program.to_account_view().address(), + [ + InstructionAccount::writable(self.from.to_account_view().address()), + InstructionAccount::readonly(self.mint.to_account_view().address()), + InstructionAccount::writable(self.to.to_account_view().address()), + InstructionAccount::readonly_signer(self.sender.to_account_view().address()), + ], + [ + self.from.to_account_view(), + self.mint.to_account_view(), + self.to.to_account_view(), + self.sender.to_account_view(), + ], + data, + ) + .invoke() + } } /// Build a 9-byte instruction data: [opcode, u64 LE amount]. diff --git a/tokens/token-extensions/cpi-guard/quasar/Cargo.toml b/tokens/token-extensions/cpi-guard/quasar/Cargo.toml index 48e1aa207..4d7009639 100644 --- a/tokens/token-extensions/cpi-guard/quasar/Cargo.toml +++ b/tokens/token-extensions/cpi-guard/quasar/Cargo.toml @@ -20,8 +20,8 @@ client = [] debug = [] [dependencies] -quasar-lang = "0.0" -quasar-spl = "0.0" +quasar-lang = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } +quasar-spl = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } solana-instruction = { version = "3.2.0" } [dev-dependencies] diff --git a/tokens/token-extensions/cpi-guard/quasar/src/lib.rs b/tokens/token-extensions/cpi-guard/quasar/src/lib.rs index 85a4e4c25..5ca0803ec 100644 --- a/tokens/token-extensions/cpi-guard/quasar/src/lib.rs +++ b/tokens/token-extensions/cpi-guard/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![cfg_attr(not(test), no_std)] +#![no_std] use quasar_lang::{ cpi::{CpiCall, InstructionAccount}, @@ -30,45 +30,47 @@ mod quasar_cpi_guard { /// on the sender's token account. #[instruction(discriminator = 0)] pub fn cpi_transfer(ctx: Ctx) -> Result<(), ProgramError> { - handle_cpi_transfer(&mut ctx.accounts) + ctx.accounts.cpi_transfer() } } #[derive(Accounts)] -pub struct CpiTransfer<'info> { +pub struct CpiTransfer { #[account(mut)] - pub sender: &'info Signer, + pub sender: Signer, #[account(mut)] - pub sender_token_account: &'info mut UncheckedAccount, - pub mint_account: &'info UncheckedAccount, + pub sender_token_account: UncheckedAccount, + pub mint_account: UncheckedAccount, #[account(mut)] - pub recipient_token_account: &'info mut UncheckedAccount, - pub token_program: &'info Program, + pub recipient_token_account: UncheckedAccount, + pub token_program: Program, } -#[inline(always)] -pub fn handle_cpi_transfer(accounts: &mut CpiTransfer) -> Result<(), ProgramError> { - // TransferChecked: opcode 12, amount=1, decimals=9 - let mut data = [0u8; 10]; - data[0] = 12; - data[1..9].copy_from_slice(&1u64.to_le_bytes()); - data[9] = 9; // decimals +impl CpiTransfer { + #[inline(always)] + pub fn cpi_transfer(&mut self) -> Result<(), ProgramError> { + // TransferChecked: opcode 12, amount=1, decimals=9 + let mut data = [0u8; 10]; + data[0] = 12; + data[1..9].copy_from_slice(&1u64.to_le_bytes()); + data[9] = 9; // decimals - CpiCall::new( - accounts.token_program.to_account_view().address(), - [ - InstructionAccount::writable(accounts.sender_token_account.to_account_view().address()), - InstructionAccount::readonly(accounts.mint_account.to_account_view().address()), - InstructionAccount::writable(accounts.recipient_token_account.to_account_view().address()), - InstructionAccount::readonly_signer(accounts.sender.to_account_view().address()), - ], - [ - accounts.sender_token_account.to_account_view(), - accounts.mint_account.to_account_view(), - accounts.recipient_token_account.to_account_view(), - accounts.sender.to_account_view(), - ], - data, - ) - .invoke() + CpiCall::new( + self.token_program.to_account_view().address(), + [ + InstructionAccount::writable(self.sender_token_account.to_account_view().address()), + InstructionAccount::readonly(self.mint_account.to_account_view().address()), + InstructionAccount::writable(self.recipient_token_account.to_account_view().address()), + InstructionAccount::readonly_signer(self.sender.to_account_view().address()), + ], + [ + self.sender_token_account.to_account_view(), + self.mint_account.to_account_view(), + self.recipient_token_account.to_account_view(), + self.sender.to_account_view(), + ], + data, + ) + .invoke() + } } diff --git a/tokens/token-extensions/default-account-state/quasar/Cargo.toml b/tokens/token-extensions/default-account-state/quasar/Cargo.toml index f5cd06339..2d4d4482b 100644 --- a/tokens/token-extensions/default-account-state/quasar/Cargo.toml +++ b/tokens/token-extensions/default-account-state/quasar/Cargo.toml @@ -20,8 +20,8 @@ client = [] debug = [] [dependencies] -quasar-lang = "0.0" -quasar-spl = "0.0" +quasar-lang = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } +quasar-spl = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } solana-instruction = { version = "3.2.0" } [dev-dependencies] diff --git a/tokens/token-extensions/default-account-state/quasar/src/lib.rs b/tokens/token-extensions/default-account-state/quasar/src/lib.rs index 5fc2905fb..2c7cbc5ee 100644 --- a/tokens/token-extensions/default-account-state/quasar/src/lib.rs +++ b/tokens/token-extensions/default-account-state/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![cfg_attr(not(test), no_std)] +#![no_std] use quasar_lang::sysvars::Sysvar; use quasar_lang::{ @@ -30,7 +30,7 @@ mod quasar_default_account_state { /// The mint account must be a signer (keypair created client-side). #[instruction(discriminator = 0)] pub fn initialize(ctx: Ctx) -> Result<(), ProgramError> { - handle_initialize(&mut ctx.accounts) + ctx.accounts.initialize() } /// Update the default account state on an existing mint. @@ -40,97 +40,101 @@ mod quasar_default_account_state { ctx: Ctx, account_state: u8, ) -> Result<(), ProgramError> { - handle_update_default_state(&mut ctx.accounts, account_state) + ctx.accounts.update_default_state(account_state) } } #[derive(Accounts)] -pub struct Initialize<'info> { +pub struct Initialize { #[account(mut)] - pub payer: &'info Signer, + pub payer: Signer, #[account(mut)] - pub mint_account: &'info Signer, - pub token_program: &'info Program, - pub system_program: &'info Program, + pub mint_account: Signer, + pub token_program: Program, + pub system_program: Program, } -#[inline(always)] -pub fn handle_initialize(accounts: &Initialize) -> Result<(), ProgramError> { - // 165 (base account) + 1 (account type) + 4 (TLV header) + 1 (DefaultAccountState data) = 171 bytes - let mint_size: u64 = 171; - let lamports = Rent::get()?.try_minimum_balance(mint_size as usize)?; +impl Initialize { + #[inline(always)] + pub fn initialize(&mut self) -> Result<(), ProgramError> { + // 165 (base account) + 1 (account type) + 4 (TLV header) + 1 (DefaultAccountState data) = 171 bytes + let mint_size: u64 = 171; + let lamports = Rent::get()?.try_minimum_balance(mint_size as usize)?; - // 1. Create account owned by Token-2022 - accounts.system_program - .create_account( - accounts.payer, - accounts.mint_account, - lamports, - mint_size, - accounts.token_program.to_account_view().address(), + // 1. Create account owned by Token-2022 + self.system_program + .create_account( + &self.payer, + &self.mint_account, + lamports, + mint_size, + self.token_program.to_account_view().address(), + ) + .invoke()?; + + // 2. Initialize DefaultAccountState extension (frozen = 2) + // Instruction: ExtensionInstruction(DefaultAccountStateInitialize) = [28, 0, 2] + let ext_data: [u8; 3] = [28, 0, 2]; // opcode 28, sub-opcode 0, state = Frozen + CpiCall::new( + self.token_program.to_account_view().address(), + [InstructionAccount::writable( + self.mint_account.to_account_view().address(), + )], + [self.mint_account.to_account_view()], + ext_data, ) .invoke()?; - // 2. Initialize DefaultAccountState extension (frozen = 2) - // Instruction: ExtensionInstruction(DefaultAccountStateInitialize) = [28, 0, 2] - let ext_data: [u8; 3] = [28, 0, 2]; // opcode 28, sub-opcode 0, state = Frozen - CpiCall::new( - accounts.token_program.to_account_view().address(), - [InstructionAccount::writable( - accounts.mint_account.to_account_view().address(), - )], - [accounts.mint_account.to_account_view()], - ext_data, - ) - .invoke()?; - - // 3. InitializeMint2: opcode 20, decimals, mint_authority, freeze_authority_option, freeze_authority - // COption is encoded as 1-byte flag (1 = Some, 0 = None) + 32-byte pubkey - // Total: 1 (opcode) + 1 (decimals) + 32 (mint_authority) + 1 (COption flag) + 32 (freeze_authority) = 67 bytes - let mut mint_data = [0u8; 67]; - mint_data[0] = 20; // InitializeMint2 - mint_data[1] = 2; // decimals - mint_data[2..34].copy_from_slice(accounts.payer.to_account_view().address().as_ref()); - mint_data[34] = 1; // COption::Some flag (1-byte format used by quasar-svm token-2022) - mint_data[35..67].copy_from_slice(accounts.payer.to_account_view().address().as_ref()); + // 3. InitializeMint2: opcode 20, decimals, mint_authority, freeze_authority_option, freeze_authority + // COption is encoded as 1-byte flag (1 = Some, 0 = None) + 32-byte pubkey + // Total: 1 (opcode) + 1 (decimals) + 32 (mint_authority) + 1 (COption flag) + 32 (freeze_authority) = 67 bytes + let mut mint_data = [0u8; 67]; + mint_data[0] = 20; // InitializeMint2 + mint_data[1] = 2; // decimals + mint_data[2..34].copy_from_slice(self.payer.to_account_view().address().as_ref()); + mint_data[34] = 1; // COption::Some flag (1-byte format used by quasar-svm token-2022) + mint_data[35..67].copy_from_slice(self.payer.to_account_view().address().as_ref()); - CpiCall::new( - accounts.token_program.to_account_view().address(), - [InstructionAccount::writable( - accounts.mint_account.to_account_view().address(), - )], - [accounts.mint_account.to_account_view()], - mint_data, - ) - .invoke() + CpiCall::new( + self.token_program.to_account_view().address(), + [InstructionAccount::writable( + self.mint_account.to_account_view().address(), + )], + [self.mint_account.to_account_view()], + mint_data, + ) + .invoke() + } } #[derive(Accounts)] -pub struct UpdateDefaultState<'info> { +pub struct UpdateDefaultState { #[account(mut)] - pub freeze_authority: &'info Signer, + pub freeze_authority: Signer, #[account(mut)] - pub mint_account: &'info mut UncheckedAccount, - pub token_program: &'info Program, + pub mint_account: UncheckedAccount, + pub token_program: Program, } -#[inline(always)] -pub fn handle_update_default_state(accounts: &UpdateDefaultState, account_state: u8) -> Result<(), ProgramError> { - // DefaultAccountState Update: opcode 28, sub-opcode 1, new state - let data: [u8; 3] = [28, 1, account_state]; - CpiCall::new( - accounts.token_program.to_account_view().address(), - [ - InstructionAccount::writable(accounts.mint_account.to_account_view().address()), - InstructionAccount::readonly_signer( - accounts.freeze_authority.to_account_view().address(), - ), - ], - [ - accounts.mint_account.to_account_view(), - accounts.freeze_authority.to_account_view(), - ], - data, - ) - .invoke() +impl UpdateDefaultState { + #[inline(always)] + pub fn update_default_state(&mut self, account_state: u8) -> Result<(), ProgramError> { + // DefaultAccountState Update: opcode 28, sub-opcode 1, new state + let data: [u8; 3] = [28, 1, account_state]; + CpiCall::new( + self.token_program.to_account_view().address(), + [ + InstructionAccount::writable(self.mint_account.to_account_view().address()), + InstructionAccount::readonly_signer( + self.freeze_authority.to_account_view().address(), + ), + ], + [ + self.mint_account.to_account_view(), + self.freeze_authority.to_account_view(), + ], + data, + ) + .invoke() + } } diff --git a/tokens/token-extensions/group/quasar/Cargo.toml b/tokens/token-extensions/group/quasar/Cargo.toml index b2b66939b..5c02b0cd8 100644 --- a/tokens/token-extensions/group/quasar/Cargo.toml +++ b/tokens/token-extensions/group/quasar/Cargo.toml @@ -20,8 +20,8 @@ client = [] debug = [] [dependencies] -quasar-lang = "0.0" -quasar-spl = "0.0" +quasar-lang = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } +quasar-spl = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } solana-instruction = { version = "3.2.0" } [dev-dependencies] diff --git a/tokens/token-extensions/group/quasar/src/lib.rs b/tokens/token-extensions/group/quasar/src/lib.rs index 122dde04f..9e494fa8e 100644 --- a/tokens/token-extensions/group/quasar/src/lib.rs +++ b/tokens/token-extensions/group/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![cfg_attr(not(test), no_std)] +#![no_std] use quasar_lang::sysvars::Sysvar; use quasar_lang::{ @@ -31,71 +31,73 @@ mod quasar_group { #[instruction(discriminator = 0)] pub fn initialize_group(ctx: Ctx) -> Result<(), ProgramError> { - handle_initialize(&mut ctx.accounts) + ctx.accounts.initialize_group() } } #[derive(Accounts)] -pub struct InitializeGroup<'info> { +pub struct InitializeGroup { #[account(mut)] - pub payer: &'info Signer, + pub payer: Signer, #[account(mut)] - pub mint_account: &'info Signer, - pub token_program: &'info Program, - pub system_program: &'info Program, + pub mint_account: Signer, + pub token_program: Program, + pub system_program: Program, } -#[inline(always)] -pub fn handle_initialize(accounts: &InitializeGroup) -> Result<(), ProgramError> { - // Mint + GroupPointer extension = 250 bytes - let mint_size: u64 = 250; - let lamports = Rent::get()?.try_minimum_balance(mint_size as usize)?; +impl InitializeGroup { + #[inline(always)] + pub fn initialize_group(&mut self) -> Result<(), ProgramError> { + // Mint + GroupPointer extension = 250 bytes + let mint_size: u64 = 250; + let lamports = Rent::get()?.try_minimum_balance(mint_size as usize)?; - accounts.system_program - .create_account( - accounts.payer, - accounts.mint_account, - lamports, - mint_size, - accounts.token_program.to_account_view().address(), - ) - .invoke()?; + self.system_program + .create_account( + &self.payer, + &self.mint_account, + lamports, + mint_size, + self.token_program.to_account_view().address(), + ) + .invoke()?; - // InitializeGroupPointer: opcode 41, sub-opcode 0 - // Data: [41, 0, authority (32 bytes), group_address (32 bytes)] - let mut ext_data = [0u8; 66]; - ext_data[0] = 41; - ext_data[1] = 0; - // authority = mint itself (self-referential PDA pattern) - ext_data[2..34].copy_from_slice(accounts.mint_account.to_account_view().address().as_ref()); - // group_address = mint itself - ext_data[34..66].copy_from_slice(accounts.mint_account.to_account_view().address().as_ref()); + // InitializeGroupPointer: opcode 41, sub-opcode 0 + // Data: [41, 0, authority (32 bytes), group_address (32 bytes)] + let mut ext_data = [0u8; 66]; + ext_data[0] = 41; + ext_data[1] = 0; + // authority = mint itself (self-referential PDA pattern) + ext_data[2..34].copy_from_slice(self.mint_account.to_account_view().address().as_ref()); + // group_address = mint itself + ext_data[34..66].copy_from_slice(self.mint_account.to_account_view().address().as_ref()); - CpiCall::new( - accounts.token_program.to_account_view().address(), - [InstructionAccount::writable( - accounts.mint_account.to_account_view().address(), - )], - [accounts.mint_account.to_account_view()], - ext_data, - ) - .invoke()?; + CpiCall::new( + self.token_program.to_account_view().address(), + [InstructionAccount::writable( + self.mint_account.to_account_view().address(), + )], + [self.mint_account.to_account_view()], + ext_data, + ) + .invoke()?; - // InitializeMint2: mint authority = mint itself (for self-signing) - let mut mint_data = [0u8; 67]; - mint_data[0] = 20; - mint_data[1] = 2; - mint_data[2..34].copy_from_slice(accounts.mint_account.to_account_view().address().as_ref()); - mint_data[34] = 1; - mint_data[35..67].copy_from_slice(accounts.mint_account.to_account_view().address().as_ref()); + // InitializeMint2: mint authority = mint itself (for self-signing) + let mut mint_data = [0u8; 67]; + mint_data[0] = 20; + mint_data[1] = 2; + mint_data[2..34].copy_from_slice(self.mint_account.to_account_view().address().as_ref()); + mint_data[34] = 1; + mint_data[35..67].copy_from_slice(self.mint_account.to_account_view().address().as_ref()); - CpiCall::new( - accounts.token_program.to_account_view().address(), - [InstructionAccount::writable( - accounts.mint_account.to_account_view().address(), - )], - [accounts.mint_account.to_account_view()], - mint_data, - ) - .invoke() + CpiCall::new( + self.token_program.to_account_view().address(), + [InstructionAccount::writable( + self.mint_account.to_account_view().address(), + )], + [self.mint_account.to_account_view()], + mint_data, + ) + .invoke() + } } diff --git a/tokens/token-extensions/immutable-owner/quasar/Cargo.toml b/tokens/token-extensions/immutable-owner/quasar/Cargo.toml index b6345037c..f8f488857 100644 --- a/tokens/token-extensions/immutable-owner/quasar/Cargo.toml +++ b/tokens/token-extensions/immutable-owner/quasar/Cargo.toml @@ -20,8 +20,8 @@ client = [] debug = [] [dependencies] -quasar-lang = "0.0" -quasar-spl = "0.0" +quasar-lang = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } +quasar-spl = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } solana-instruction = { version = "3.2.0" } [dev-dependencies] diff --git a/tokens/token-extensions/immutable-owner/quasar/src/lib.rs b/tokens/token-extensions/immutable-owner/quasar/src/lib.rs index f4fd7a16b..74b750662 100644 --- a/tokens/token-extensions/immutable-owner/quasar/src/lib.rs +++ b/tokens/token-extensions/immutable-owner/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![cfg_attr(not(test), no_std)] +#![no_std] use quasar_lang::sysvars::Sysvar; use quasar_lang::{ @@ -27,65 +27,67 @@ mod quasar_immutable_owner { #[instruction(discriminator = 0)] pub fn initialize(ctx: Ctx) -> Result<(), ProgramError> { - handle_initialize(&mut ctx.accounts) + ctx.accounts.initialize() } } #[derive(Accounts)] -pub struct Initialize<'info> { +pub struct Initialize { #[account(mut)] - pub payer: &'info Signer, + pub payer: Signer, #[account(mut)] - pub token_account: &'info Signer, - pub mint_account: &'info UncheckedAccount, - pub token_program: &'info Program, - pub system_program: &'info Program, + pub token_account: Signer, + pub mint_account: UncheckedAccount, + pub token_program: Program, + pub system_program: Program, } -#[inline(always)] -pub fn handle_initialize(accounts: &Initialize) -> Result<(), ProgramError> { - // 165 (base) + 1 (account type) + 4 (TLV header, ImmutableOwner is zero-size) = 170 bytes - let account_size: u64 = 170; - let lamports = Rent::get()?.try_minimum_balance(account_size as usize)?; +impl Initialize { + #[inline(always)] + pub fn initialize(&mut self) -> Result<(), ProgramError> { + // 165 (base) + 1 (account type) + 4 (TLV header, ImmutableOwner is zero-size) = 170 bytes + let account_size: u64 = 170; + let lamports = Rent::get()?.try_minimum_balance(account_size as usize)?; - // 1. Create account - accounts.system_program - .create_account( - accounts.payer, - accounts.token_account, - lamports, - account_size, - accounts.token_program.to_account_view().address(), + // 1. Create account + self.system_program + .create_account( + &self.payer, + &self.token_account, + lamports, + account_size, + self.token_program.to_account_view().address(), + ) + .invoke()?; + + // 2. Initialize ImmutableOwner extension: opcode 22 (no additional data) + CpiCall::new( + self.token_program.to_account_view().address(), + [InstructionAccount::writable( + self.token_account.to_account_view().address(), + )], + [self.token_account.to_account_view()], + [22u8], ) .invoke()?; - // 2. Initialize ImmutableOwner extension: opcode 22 (no additional data) - CpiCall::new( - accounts.token_program.to_account_view().address(), - [InstructionAccount::writable( - accounts.token_account.to_account_view().address(), - )], - [accounts.token_account.to_account_view()], - [22u8], - ) - .invoke()?; - - // 3. InitializeAccount3: opcode 18, owner pubkey - let mut data = [0u8; 33]; - data[0] = 18; - data[1..33].copy_from_slice(accounts.payer.to_account_view().address().as_ref()); + // 3. InitializeAccount3: opcode 18, owner pubkey + let mut data = [0u8; 33]; + data[0] = 18; + data[1..33].copy_from_slice(self.payer.to_account_view().address().as_ref()); - CpiCall::new( - accounts.token_program.to_account_view().address(), - [ - InstructionAccount::writable(accounts.token_account.to_account_view().address()), - InstructionAccount::readonly(accounts.mint_account.to_account_view().address()), - ], - [ - accounts.token_account.to_account_view(), - accounts.mint_account.to_account_view(), - ], - data, - ) - .invoke() + CpiCall::new( + self.token_program.to_account_view().address(), + [ + InstructionAccount::writable(self.token_account.to_account_view().address()), + InstructionAccount::readonly(self.mint_account.to_account_view().address()), + ], + [ + self.token_account.to_account_view(), + self.mint_account.to_account_view(), + ], + data, + ) + .invoke() + } } diff --git a/tokens/token-extensions/interest-bearing/quasar/Cargo.toml b/tokens/token-extensions/interest-bearing/quasar/Cargo.toml index 602a1f489..6e9ec58dd 100644 --- a/tokens/token-extensions/interest-bearing/quasar/Cargo.toml +++ b/tokens/token-extensions/interest-bearing/quasar/Cargo.toml @@ -20,8 +20,8 @@ client = [] debug = [] [dependencies] -quasar-lang = "0.0" -quasar-spl = "0.0" +quasar-lang = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } +quasar-spl = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } solana-instruction = { version = "3.2.0" } [dev-dependencies] diff --git a/tokens/token-extensions/interest-bearing/quasar/src/lib.rs b/tokens/token-extensions/interest-bearing/quasar/src/lib.rs index 51f48cea6..97744ed23 100644 --- a/tokens/token-extensions/interest-bearing/quasar/src/lib.rs +++ b/tokens/token-extensions/interest-bearing/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![cfg_attr(not(test), no_std)] +#![no_std] use quasar_lang::sysvars::Sysvar; use quasar_lang::{ @@ -27,106 +27,110 @@ mod quasar_interest_bearing { #[instruction(discriminator = 0)] pub fn initialize(ctx: Ctx, rate: i16) -> Result<(), ProgramError> { - handle_initialize(&mut ctx.accounts, rate) + ctx.accounts.initialize(rate) } #[instruction(discriminator = 1)] pub fn update_rate(ctx: Ctx, rate: i16) -> Result<(), ProgramError> { - handle_update_rate(&mut ctx.accounts, rate) + ctx.accounts.update_rate(rate) } } #[derive(Accounts)] -pub struct Initialize<'info> { +pub struct Initialize { #[account(mut)] - pub payer: &'info Signer, + pub payer: Signer, #[account(mut)] - pub mint_account: &'info Signer, - pub token_program: &'info Program, - pub system_program: &'info Program, + pub mint_account: Signer, + pub token_program: Program, + pub system_program: Program, } -#[inline(always)] -pub fn handle_initialize(accounts: &Initialize, rate: i16) -> Result<(), ProgramError> { - // 165 (base) + 1 (account type) + 4 (TLV header) + 52 (InterestBearingConfig data) = 222 bytes - let mint_size: u64 = 222; - let lamports = Rent::get()?.try_minimum_balance(mint_size as usize)?; - - accounts.system_program - .create_account( - accounts.payer, - accounts.mint_account, - lamports, - mint_size, - accounts.token_program.to_account_view().address(), +impl Initialize { + #[inline(always)] + pub fn initialize(&mut self, rate: i16) -> Result<(), ProgramError> { + // 165 (base) + 1 (account type) + 4 (TLV header) + 52 (InterestBearingConfig data) = 222 bytes + let mint_size: u64 = 222; + let lamports = Rent::get()?.try_minimum_balance(mint_size as usize)?; + + self.system_program + .create_account( + &self.payer, + &self.mint_account, + lamports, + mint_size, + self.token_program.to_account_view().address(), + ) + .invoke()?; + + // InterestBearingMintInitialize: opcode 33, sub-opcode 0 + // Data: [33, 0, rate_authority (32 bytes), rate (i16 LE)] + let mut ext_data = [0u8; 36]; + ext_data[0] = 33; + ext_data[1] = 0; // Initialize sub-opcode + ext_data[2..34].copy_from_slice(self.payer.to_account_view().address().as_ref()); + ext_data[34..36].copy_from_slice(&rate.to_le_bytes()); + + CpiCall::new( + self.token_program.to_account_view().address(), + [InstructionAccount::writable( + self.mint_account.to_account_view().address(), + )], + [self.mint_account.to_account_view()], + ext_data, ) .invoke()?; - // InterestBearingMintInitialize: opcode 33, sub-opcode 0 - // Data: [33, 0, rate_authority (32 bytes), rate (i16 LE)] - let mut ext_data = [0u8; 36]; - ext_data[0] = 33; - ext_data[1] = 0; // Initialize sub-opcode - ext_data[2..34].copy_from_slice(accounts.payer.to_account_view().address().as_ref()); - ext_data[34..36].copy_from_slice(&rate.to_le_bytes()); - - CpiCall::new( - accounts.token_program.to_account_view().address(), - [InstructionAccount::writable( - accounts.mint_account.to_account_view().address(), - )], - [accounts.mint_account.to_account_view()], - ext_data, - ) - .invoke()?; - - // InitializeMint2 - let mut mint_data = [0u8; 67]; - mint_data[0] = 20; - mint_data[1] = 2; - mint_data[2..34].copy_from_slice(accounts.payer.to_account_view().address().as_ref()); - mint_data[34] = 1; - mint_data[35..67].copy_from_slice(accounts.payer.to_account_view().address().as_ref()); - - CpiCall::new( - accounts.token_program.to_account_view().address(), - [InstructionAccount::writable( - accounts.mint_account.to_account_view().address(), - )], - [accounts.mint_account.to_account_view()], - mint_data, - ) - .invoke() + // InitializeMint2 + let mut mint_data = [0u8; 67]; + mint_data[0] = 20; + mint_data[1] = 2; + mint_data[2..34].copy_from_slice(self.payer.to_account_view().address().as_ref()); + mint_data[34] = 1; + mint_data[35..67].copy_from_slice(self.payer.to_account_view().address().as_ref()); + + CpiCall::new( + self.token_program.to_account_view().address(), + [InstructionAccount::writable( + self.mint_account.to_account_view().address(), + )], + [self.mint_account.to_account_view()], + mint_data, + ) + .invoke() + } } #[derive(Accounts)] -pub struct UpdateRate<'info> { +pub struct UpdateRate { #[account(mut)] - pub authority: &'info Signer, + pub authority: Signer, #[account(mut)] - pub mint_account: &'info mut UncheckedAccount, - pub token_program: &'info Program, + pub mint_account: UncheckedAccount, + pub token_program: Program, } -#[inline(always)] -pub fn handle_update_rate(accounts: &UpdateRate, rate: i16) -> Result<(), ProgramError> { - // InterestBearingMintUpdateRate: opcode 33, sub-opcode 1, rate (i16 LE) - let mut data = [0u8; 4]; - data[0] = 33; - data[1] = 1; - data[2..4].copy_from_slice(&rate.to_le_bytes()); - - CpiCall::new( - accounts.token_program.to_account_view().address(), - [ - InstructionAccount::writable(accounts.mint_account.to_account_view().address()), - InstructionAccount::readonly_signer(accounts.authority.to_account_view().address()), - ], - [ - accounts.mint_account.to_account_view(), - accounts.authority.to_account_view(), - ], - data, - ) - .invoke() +impl UpdateRate { + #[inline(always)] + pub fn update_rate(&mut self, rate: i16) -> Result<(), ProgramError> { + // InterestBearingMintUpdateRate: opcode 33, sub-opcode 1, rate (i16 LE) + let mut data = [0u8; 4]; + data[0] = 33; + data[1] = 1; + data[2..4].copy_from_slice(&rate.to_le_bytes()); + + CpiCall::new( + self.token_program.to_account_view().address(), + [ + InstructionAccount::writable(self.mint_account.to_account_view().address()), + InstructionAccount::readonly_signer(self.authority.to_account_view().address()), + ], + [ + self.mint_account.to_account_view(), + self.authority.to_account_view(), + ], + data, + ) + .invoke() + } } diff --git a/tokens/token-extensions/memo-transfer/quasar/Cargo.toml b/tokens/token-extensions/memo-transfer/quasar/Cargo.toml index b00779f7a..07f0ac8e7 100644 --- a/tokens/token-extensions/memo-transfer/quasar/Cargo.toml +++ b/tokens/token-extensions/memo-transfer/quasar/Cargo.toml @@ -20,8 +20,8 @@ client = [] debug = [] [dependencies] -quasar-lang = "0.0" -quasar-spl = "0.0" +quasar-lang = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } +quasar-spl = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } solana-instruction = { version = "3.2.0" } [dev-dependencies] diff --git a/tokens/token-extensions/memo-transfer/quasar/src/lib.rs b/tokens/token-extensions/memo-transfer/quasar/src/lib.rs index b5852d5f0..b4e7c0aff 100644 --- a/tokens/token-extensions/memo-transfer/quasar/src/lib.rs +++ b/tokens/token-extensions/memo-transfer/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![cfg_attr(not(test), no_std)] +#![no_std] use quasar_lang::sysvars::Sysvar; use quasar_lang::{ @@ -27,100 +27,104 @@ mod quasar_memo_transfer { #[instruction(discriminator = 0)] pub fn initialize(ctx: Ctx) -> Result<(), ProgramError> { - handle_initialize(&mut ctx.accounts) + ctx.accounts.initialize() } #[instruction(discriminator = 1)] pub fn disable(ctx: Ctx) -> Result<(), ProgramError> { - handle_disable(&mut ctx.accounts) + ctx.accounts.disable() } } #[derive(Accounts)] -pub struct Initialize<'info> { +pub struct Initialize { #[account(mut)] - pub payer: &'info Signer, + pub payer: Signer, #[account(mut)] - pub token_account: &'info Signer, - pub mint_account: &'info UncheckedAccount, - pub token_program: &'info Program, - pub system_program: &'info Program, + pub token_account: Signer, + pub mint_account: UncheckedAccount, + pub token_program: Program, + pub system_program: Program, } -#[inline(always)] -pub fn handle_initialize(accounts: &Initialize) -> Result<(), ProgramError> { - // Token account + MemoTransfer extension = 300 bytes - let account_size: u64 = 300; - let lamports = Rent::get()?.try_minimum_balance(account_size as usize)?; +impl Initialize { + #[inline(always)] + pub fn initialize(&mut self) -> Result<(), ProgramError> { + // Token account + MemoTransfer extension = 300 bytes + let account_size: u64 = 300; + let lamports = Rent::get()?.try_minimum_balance(account_size as usize)?; - accounts.system_program - .create_account( - accounts.payer, - accounts.token_account, - lamports, - account_size, - accounts.token_program.to_account_view().address(), - ) - .invoke()?; + self.system_program + .create_account( + &self.payer, + &self.token_account, + lamports, + account_size, + self.token_program.to_account_view().address(), + ) + .invoke()?; - // InitializeAccount3: opcode 18, owner pubkey - let mut init_data = [0u8; 33]; - init_data[0] = 18; - init_data[1..33].copy_from_slice(accounts.payer.to_account_view().address().as_ref()); + // InitializeAccount3: opcode 18, owner pubkey + let mut init_data = [0u8; 33]; + init_data[0] = 18; + init_data[1..33].copy_from_slice(self.payer.to_account_view().address().as_ref()); - CpiCall::new( - accounts.token_program.to_account_view().address(), - [ - InstructionAccount::writable(accounts.token_account.to_account_view().address()), - InstructionAccount::readonly(accounts.mint_account.to_account_view().address()), - ], - [ - accounts.token_account.to_account_view(), - accounts.mint_account.to_account_view(), - ], - init_data, - ) - .invoke()?; + CpiCall::new( + self.token_program.to_account_view().address(), + [ + InstructionAccount::writable(self.token_account.to_account_view().address()), + InstructionAccount::readonly(self.mint_account.to_account_view().address()), + ], + [ + self.token_account.to_account_view(), + self.mint_account.to_account_view(), + ], + init_data, + ) + .invoke()?; - // MemoTransfer enable: opcode 30, sub-opcode 0 - CpiCall::new( - accounts.token_program.to_account_view().address(), - [ - InstructionAccount::writable(accounts.token_account.to_account_view().address()), - InstructionAccount::readonly_signer(accounts.payer.to_account_view().address()), - ], - [ - accounts.token_account.to_account_view(), - accounts.payer.to_account_view(), - ], - [30u8, 0], - ) - .invoke() + // MemoTransfer enable: opcode 30, sub-opcode 0 + CpiCall::new( + self.token_program.to_account_view().address(), + [ + InstructionAccount::writable(self.token_account.to_account_view().address()), + InstructionAccount::readonly_signer(self.payer.to_account_view().address()), + ], + [ + self.token_account.to_account_view(), + self.payer.to_account_view(), + ], + [30u8, 0], + ) + .invoke() + } } #[derive(Accounts)] -pub struct Disable<'info> { +pub struct Disable { #[account(mut)] - pub owner: &'info Signer, + pub owner: Signer, #[account(mut)] - pub token_account: &'info mut UncheckedAccount, - pub token_program: &'info Program, + pub token_account: UncheckedAccount, + pub token_program: Program, } -#[inline(always)] -pub fn handle_disable(accounts: &Disable) -> Result<(), ProgramError> { - // MemoTransfer disable: opcode 30, sub-opcode 1 - CpiCall::new( - accounts.token_program.to_account_view().address(), - [ - InstructionAccount::writable(accounts.token_account.to_account_view().address()), - InstructionAccount::readonly_signer(accounts.owner.to_account_view().address()), - ], - [ - accounts.token_account.to_account_view(), - accounts.owner.to_account_view(), - ], - [30u8, 1], - ) - .invoke() +impl Disable { + #[inline(always)] + pub fn disable(&mut self) -> Result<(), ProgramError> { + // MemoTransfer disable: opcode 30, sub-opcode 1 + CpiCall::new( + self.token_program.to_account_view().address(), + [ + InstructionAccount::writable(self.token_account.to_account_view().address()), + InstructionAccount::readonly_signer(self.owner.to_account_view().address()), + ], + [ + self.token_account.to_account_view(), + self.owner.to_account_view(), + ], + [30u8, 1], + ) + .invoke() + } } diff --git a/tokens/token-extensions/mint-close-authority/quasar/Cargo.toml b/tokens/token-extensions/mint-close-authority/quasar/Cargo.toml index 5e93ac70d..c6c41957e 100644 --- a/tokens/token-extensions/mint-close-authority/quasar/Cargo.toml +++ b/tokens/token-extensions/mint-close-authority/quasar/Cargo.toml @@ -20,8 +20,8 @@ client = [] debug = [] [dependencies] -quasar-lang = "0.0" -quasar-spl = "0.0" +quasar-lang = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } +quasar-spl = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } solana-instruction = { version = "3.2.0" } [dev-dependencies] diff --git a/tokens/token-extensions/mint-close-authority/quasar/src/lib.rs b/tokens/token-extensions/mint-close-authority/quasar/src/lib.rs index 8a887f431..5f279b8e6 100644 --- a/tokens/token-extensions/mint-close-authority/quasar/src/lib.rs +++ b/tokens/token-extensions/mint-close-authority/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![cfg_attr(not(test), no_std)] +#![no_std] use quasar_lang::sysvars::Sysvar; use quasar_lang::{ @@ -28,101 +28,105 @@ mod quasar_mint_close_authority { /// Create a mint with the MintCloseAuthority extension. #[instruction(discriminator = 0)] pub fn initialize(ctx: Ctx) -> Result<(), ProgramError> { - handle_initialize(&mut ctx.accounts) + ctx.accounts.initialize() } /// Close the mint account, reclaiming lamports to the authority. #[instruction(discriminator = 1)] pub fn close(ctx: Ctx) -> Result<(), ProgramError> { - handle_close(&mut ctx.accounts) + ctx.accounts.close() } } #[derive(Accounts)] -pub struct Initialize<'info> { +pub struct Initialize { #[account(mut)] - pub payer: &'info Signer, + pub payer: Signer, #[account(mut)] - pub mint_account: &'info Signer, - pub token_program: &'info Program, - pub system_program: &'info Program, + pub mint_account: Signer, + pub token_program: Program, + pub system_program: Program, } -#[inline(always)] -pub fn handle_initialize(accounts: &Initialize) -> Result<(), ProgramError> { - // 165 (base) + 1 (account type) + 4 (TLV header) + 32 (MintCloseAuthority data) = 202 bytes - let mint_size: u64 = 202; - let lamports = Rent::get()?.try_minimum_balance(mint_size as usize)?; - - accounts.system_program - .create_account( - accounts.payer, - accounts.mint_account, - lamports, - mint_size, - accounts.token_program.to_account_view().address(), +impl Initialize { + #[inline(always)] + pub fn initialize(&mut self) -> Result<(), ProgramError> { + // 165 (base) + 1 (account type) + 4 (TLV header) + 32 (MintCloseAuthority data) = 202 bytes + let mint_size: u64 = 202; + let lamports = Rent::get()?.try_minimum_balance(mint_size as usize)?; + + self.system_program + .create_account( + &self.payer, + &self.mint_account, + lamports, + mint_size, + self.token_program.to_account_view().address(), + ) + .invoke()?; + + // InitializeMintCloseAuthority: opcode 25, COption::Some flag (1 byte), close_authority pubkey (32 bytes) + let mut ext_data = [0u8; 34]; + ext_data[0] = 25; // InitializeMintCloseAuthority + ext_data[1] = 1; // COption::Some + ext_data[2..34].copy_from_slice(self.payer.to_account_view().address().as_ref()); + + CpiCall::new( + self.token_program.to_account_view().address(), + [InstructionAccount::writable( + self.mint_account.to_account_view().address(), + )], + [self.mint_account.to_account_view()], + ext_data, ) .invoke()?; - // InitializeMintCloseAuthority: opcode 25, COption::Some flag (1 byte), close_authority pubkey (32 bytes) - let mut ext_data = [0u8; 34]; - ext_data[0] = 25; // InitializeMintCloseAuthority - ext_data[1] = 1; // COption::Some - ext_data[2..34].copy_from_slice(accounts.payer.to_account_view().address().as_ref()); - - CpiCall::new( - accounts.token_program.to_account_view().address(), - [InstructionAccount::writable( - accounts.mint_account.to_account_view().address(), - )], - [accounts.mint_account.to_account_view()], - ext_data, - ) - .invoke()?; - - // InitializeMint2 - let mut mint_data = [0u8; 67]; - mint_data[0] = 20; - mint_data[1] = 2; - mint_data[2..34].copy_from_slice(accounts.payer.to_account_view().address().as_ref()); - mint_data[34] = 0; // no freeze authority - - CpiCall::new( - accounts.token_program.to_account_view().address(), - [InstructionAccount::writable( - accounts.mint_account.to_account_view().address(), - )], - [accounts.mint_account.to_account_view()], - mint_data, - ) - .invoke() + // InitializeMint2 + let mut mint_data = [0u8; 67]; + mint_data[0] = 20; + mint_data[1] = 2; + mint_data[2..34].copy_from_slice(self.payer.to_account_view().address().as_ref()); + mint_data[34] = 0; // no freeze authority + + CpiCall::new( + self.token_program.to_account_view().address(), + [InstructionAccount::writable( + self.mint_account.to_account_view().address(), + )], + [self.mint_account.to_account_view()], + mint_data, + ) + .invoke() + } } #[derive(Accounts)] -pub struct Close<'info> { +pub struct Close { #[account(mut)] - pub authority: &'info Signer, + pub authority: Signer, #[account(mut)] - pub mint_account: &'info mut UncheckedAccount, - pub token_program: &'info Program, + pub mint_account: UncheckedAccount, + pub token_program: Program, } -#[inline(always)] -pub fn handle_close(accounts: &Close) -> Result<(), ProgramError> { - // CloseAccount: opcode 9 - CpiCall::new( - accounts.token_program.to_account_view().address(), - [ - InstructionAccount::writable(accounts.mint_account.to_account_view().address()), - InstructionAccount::writable(accounts.authority.to_account_view().address()), - InstructionAccount::readonly_signer(accounts.authority.to_account_view().address()), - ], - [ - accounts.mint_account.to_account_view(), - accounts.authority.to_account_view(), - accounts.authority.to_account_view(), - ], - [9u8], - ) - .invoke() +impl Close { + #[inline(always)] + pub fn close(&mut self) -> Result<(), ProgramError> { + // CloseAccount: opcode 9 + CpiCall::new( + self.token_program.to_account_view().address(), + [ + InstructionAccount::writable(self.mint_account.to_account_view().address()), + InstructionAccount::writable(self.authority.to_account_view().address()), + InstructionAccount::readonly_signer(self.authority.to_account_view().address()), + ], + [ + self.mint_account.to_account_view(), + self.authority.to_account_view(), + self.authority.to_account_view(), + ], + [9u8], + ) + .invoke() + } } diff --git a/tokens/token-extensions/non-transferable/quasar/Cargo.toml b/tokens/token-extensions/non-transferable/quasar/Cargo.toml index 23bf84a37..be7d11b69 100644 --- a/tokens/token-extensions/non-transferable/quasar/Cargo.toml +++ b/tokens/token-extensions/non-transferable/quasar/Cargo.toml @@ -20,8 +20,8 @@ client = [] debug = [] [dependencies] -quasar-lang = "0.0" -quasar-spl = "0.0" +quasar-lang = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } +quasar-spl = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } solana-instruction = { version = "3.2.0" } [dev-dependencies] diff --git a/tokens/token-extensions/non-transferable/quasar/src/lib.rs b/tokens/token-extensions/non-transferable/quasar/src/lib.rs index df053282f..f2115aa37 100644 --- a/tokens/token-extensions/non-transferable/quasar/src/lib.rs +++ b/tokens/token-extensions/non-transferable/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![cfg_attr(not(test), no_std)] +#![no_std] use quasar_lang::sysvars::Sysvar; use quasar_lang::{ @@ -27,63 +27,65 @@ mod quasar_non_transferable { #[instruction(discriminator = 0)] pub fn initialize(ctx: Ctx) -> Result<(), ProgramError> { - handle_initialize(&mut ctx.accounts) + ctx.accounts.initialize() } } #[derive(Accounts)] -pub struct Initialize<'info> { +pub struct Initialize { #[account(mut)] - pub payer: &'info Signer, + pub payer: Signer, #[account(mut)] - pub mint_account: &'info Signer, - pub token_program: &'info Program, - pub system_program: &'info Program, + pub mint_account: Signer, + pub token_program: Program, + pub system_program: Program, } -#[inline(always)] -pub fn handle_initialize(accounts: &Initialize) -> Result<(), ProgramError> { - // Mint + NonTransferable extension = 170 bytes - let mint_size: u64 = 170; - let lamports = Rent::get()?.try_minimum_balance(mint_size as usize)?; +impl Initialize { + #[inline(always)] + pub fn initialize(&mut self) -> Result<(), ProgramError> { + // Mint + NonTransferable extension = 170 bytes + let mint_size: u64 = 170; + let lamports = Rent::get()?.try_minimum_balance(mint_size as usize)?; - // 1. Create account - accounts.system_program - .create_account( - accounts.payer, - accounts.mint_account, - lamports, - mint_size, - accounts.token_program.to_account_view().address(), + // 1. Create account + self.system_program + .create_account( + &self.payer, + &self.mint_account, + lamports, + mint_size, + self.token_program.to_account_view().address(), + ) + .invoke()?; + + // 2. Initialize NonTransferable extension: opcode 32 (InitializeNonTransferableMint, no data) + CpiCall::new( + self.token_program.to_account_view().address(), + [InstructionAccount::writable( + self.mint_account.to_account_view().address(), + )], + [self.mint_account.to_account_view()], + [32u8], ) .invoke()?; - // 2. Initialize NonTransferable extension: opcode 32 (InitializeNonTransferableMint, no data) - CpiCall::new( - accounts.token_program.to_account_view().address(), - [InstructionAccount::writable( - accounts.mint_account.to_account_view().address(), - )], - [accounts.mint_account.to_account_view()], - [32u8], - ) - .invoke()?; - - // 3. InitializeMint2 - let mut mint_data = [0u8; 67]; - mint_data[0] = 20; - mint_data[1] = 2; // decimals - mint_data[2..34].copy_from_slice(accounts.payer.to_account_view().address().as_ref()); - mint_data[34] = 1; // has freeze authority - mint_data[35..67].copy_from_slice(accounts.payer.to_account_view().address().as_ref()); + // 3. InitializeMint2 + let mut mint_data = [0u8; 67]; + mint_data[0] = 20; + mint_data[1] = 2; // decimals + mint_data[2..34].copy_from_slice(self.payer.to_account_view().address().as_ref()); + mint_data[34] = 1; // has freeze authority + mint_data[35..67].copy_from_slice(self.payer.to_account_view().address().as_ref()); - CpiCall::new( - accounts.token_program.to_account_view().address(), - [InstructionAccount::writable( - accounts.mint_account.to_account_view().address(), - )], - [accounts.mint_account.to_account_view()], - mint_data, - ) - .invoke() + CpiCall::new( + self.token_program.to_account_view().address(), + [InstructionAccount::writable( + self.mint_account.to_account_view().address(), + )], + [self.mint_account.to_account_view()], + mint_data, + ) + .invoke() + } } diff --git a/tokens/token-extensions/permanent-delegate/quasar/Cargo.toml b/tokens/token-extensions/permanent-delegate/quasar/Cargo.toml index c4d507f10..470299022 100644 --- a/tokens/token-extensions/permanent-delegate/quasar/Cargo.toml +++ b/tokens/token-extensions/permanent-delegate/quasar/Cargo.toml @@ -20,8 +20,8 @@ client = [] debug = [] [dependencies] -quasar-lang = "0.0" -quasar-spl = "0.0" +quasar-lang = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } +quasar-spl = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } solana-instruction = { version = "3.2.0" } [dev-dependencies] diff --git a/tokens/token-extensions/permanent-delegate/quasar/src/lib.rs b/tokens/token-extensions/permanent-delegate/quasar/src/lib.rs index 43f07a4a7..aa399a912 100644 --- a/tokens/token-extensions/permanent-delegate/quasar/src/lib.rs +++ b/tokens/token-extensions/permanent-delegate/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![cfg_attr(not(test), no_std)] +#![no_std] use quasar_lang::sysvars::Sysvar; use quasar_lang::{ @@ -27,65 +27,67 @@ mod quasar_permanent_delegate { #[instruction(discriminator = 0)] pub fn initialize(ctx: Ctx) -> Result<(), ProgramError> { - handle_initialize(&mut ctx.accounts) + ctx.accounts.initialize() } } #[derive(Accounts)] -pub struct Initialize<'info> { +pub struct Initialize { #[account(mut)] - pub payer: &'info Signer, + pub payer: Signer, #[account(mut)] - pub mint_account: &'info Signer, - pub token_program: &'info Program, - pub system_program: &'info Program, + pub mint_account: Signer, + pub token_program: Program, + pub system_program: Program, } -#[inline(always)] -pub fn handle_initialize(accounts: &Initialize) -> Result<(), ProgramError> { - // 165 (base) + 1 (account type) + 4 (TLV header) + 32 (PermanentDelegate data) = 202 bytes - let mint_size: u64 = 202; - let lamports = Rent::get()?.try_minimum_balance(mint_size as usize)?; +impl Initialize { + #[inline(always)] + pub fn initialize(&mut self) -> Result<(), ProgramError> { + // 165 (base) + 1 (account type) + 4 (TLV header) + 32 (PermanentDelegate data) = 202 bytes + let mint_size: u64 = 202; + let lamports = Rent::get()?.try_minimum_balance(mint_size as usize)?; - accounts.system_program - .create_account( - accounts.payer, - accounts.mint_account, - lamports, - mint_size, - accounts.token_program.to_account_view().address(), - ) - .invoke()?; + self.system_program + .create_account( + &self.payer, + &self.mint_account, + lamports, + mint_size, + self.token_program.to_account_view().address(), + ) + .invoke()?; - // InitializePermanentDelegate: opcode 35, delegate pubkey (32 bytes, not COption) - let mut ext_data = [0u8; 33]; - ext_data[0] = 35; - ext_data[1..33].copy_from_slice(accounts.payer.to_account_view().address().as_ref()); + // InitializePermanentDelegate: opcode 35, delegate pubkey (32 bytes, not COption) + let mut ext_data = [0u8; 33]; + ext_data[0] = 35; + ext_data[1..33].copy_from_slice(self.payer.to_account_view().address().as_ref()); - CpiCall::new( - accounts.token_program.to_account_view().address(), - [InstructionAccount::writable( - accounts.mint_account.to_account_view().address(), - )], - [accounts.mint_account.to_account_view()], - ext_data, - ) - .invoke()?; + CpiCall::new( + self.token_program.to_account_view().address(), + [InstructionAccount::writable( + self.mint_account.to_account_view().address(), + )], + [self.mint_account.to_account_view()], + ext_data, + ) + .invoke()?; - // InitializeMint2 - let mut mint_data = [0u8; 67]; - mint_data[0] = 20; - mint_data[1] = 2; - mint_data[2..34].copy_from_slice(accounts.payer.to_account_view().address().as_ref()); - mint_data[34] = 0; // no freeze authority + // InitializeMint2 + let mut mint_data = [0u8; 67]; + mint_data[0] = 20; + mint_data[1] = 2; + mint_data[2..34].copy_from_slice(self.payer.to_account_view().address().as_ref()); + mint_data[34] = 0; // no freeze authority - CpiCall::new( - accounts.token_program.to_account_view().address(), - [InstructionAccount::writable( - accounts.mint_account.to_account_view().address(), - )], - [accounts.mint_account.to_account_view()], - mint_data, - ) - .invoke() + CpiCall::new( + self.token_program.to_account_view().address(), + [InstructionAccount::writable( + self.mint_account.to_account_view().address(), + )], + [self.mint_account.to_account_view()], + mint_data, + ) + .invoke() + } } diff --git a/tokens/token-extensions/transfer-fee/quasar/Cargo.toml b/tokens/token-extensions/transfer-fee/quasar/Cargo.toml index 0aff4de50..907756375 100644 --- a/tokens/token-extensions/transfer-fee/quasar/Cargo.toml +++ b/tokens/token-extensions/transfer-fee/quasar/Cargo.toml @@ -20,8 +20,8 @@ client = [] debug = [] [dependencies] -quasar-lang = "0.0" -quasar-spl = "0.0" +quasar-lang = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } +quasar-spl = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } solana-instruction = { version = "3.2.0" } [dev-dependencies] diff --git a/tokens/token-extensions/transfer-fee/quasar/src/lib.rs b/tokens/token-extensions/transfer-fee/quasar/src/lib.rs index 3b61b6e6f..9863f15c1 100644 --- a/tokens/token-extensions/transfer-fee/quasar/src/lib.rs +++ b/tokens/token-extensions/transfer-fee/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![cfg_attr(not(test), no_std)] +#![no_std] use quasar_lang::sysvars::Sysvar; use quasar_lang::{ @@ -32,13 +32,13 @@ mod quasar_transfer_fee { transfer_fee_basis_points: u16, maximum_fee: u64, ) -> Result<(), ProgramError> { - handle_initialize(&mut ctx.accounts, transfer_fee_basis_points, maximum_fee) + ctx.accounts.initialize(transfer_fee_basis_points, maximum_fee) } /// Transfer tokens with fee. #[instruction(discriminator = 1)] pub fn transfer(ctx: Ctx, amount: u64, fee: u64) -> Result<(), ProgramError> { - handle_transfer(&mut ctx.accounts, amount, fee) + ctx.accounts.transfer(amount, fee) } /// Update the transfer fee (takes effect after 2 epochs). @@ -48,187 +48,195 @@ mod quasar_transfer_fee { transfer_fee_basis_points: u16, maximum_fee: u64, ) -> Result<(), ProgramError> { - handle_update_fee(&mut ctx.accounts, transfer_fee_basis_points, maximum_fee) + ctx.accounts.update_fee(transfer_fee_basis_points, maximum_fee) } /// Withdraw withheld fees from the mint account. #[instruction(discriminator = 3)] pub fn withdraw(ctx: Ctx) -> Result<(), ProgramError> { - handle_withdraw(&mut ctx.accounts) + ctx.accounts.withdraw() } } #[derive(Accounts)] -pub struct Initialize<'info> { +pub struct Initialize { #[account(mut)] - pub payer: &'info Signer, + pub payer: Signer, #[account(mut)] - pub mint_account: &'info Signer, - pub token_program: &'info Program, - pub system_program: &'info Program, + pub mint_account: Signer, + pub token_program: Program, + pub system_program: Program, } -#[inline(always)] -pub fn handle_initialize(accounts: &Initialize, basis_points: u16, max_fee: u64) -> Result<(), ProgramError> { - // 165 (base) + 1 (AccountType) + 4 (TLV header) + 108 (TransferFeeConfig data) = 278 bytes - let mint_size: u64 = 278; - let lamports = Rent::get()?.try_minimum_balance(mint_size as usize)?; - - accounts.system_program - .create_account( - accounts.payer, - accounts.mint_account, - lamports, - mint_size, - accounts.token_program.to_account_view().address(), +impl Initialize { + #[inline(always)] + pub fn initialize(&mut self, basis_points: u16, max_fee: u64) -> Result<(), ProgramError> { + // 165 (base) + 1 (AccountType) + 4 (TLV header) + 108 (TransferFeeConfig data) = 278 bytes + let mint_size: u64 = 278; + let lamports = Rent::get()?.try_minimum_balance(mint_size as usize)?; + + self.system_program + .create_account( + &self.payer, + &self.mint_account, + lamports, + mint_size, + self.token_program.to_account_view().address(), + ) + .invoke()?; + + // TransferFeeExtension opcode 26, sub-instruction 0 = InitializeTransferFeeConfig + // Data: [26, 0, COption_flag(1), config_authority(32), COption_flag(1), withdraw_authority(32), + // basis_points(u16 LE), max_fee(u64 LE)] + let mut ext_data = [0u8; 78]; + ext_data[0] = 26; // TransferFeeExtension + ext_data[1] = 0; // InitializeTransferFeeConfig sub-instruction + ext_data[2] = 1; // COption::Some for config_authority + ext_data[3..35].copy_from_slice(self.payer.to_account_view().address().as_ref()); + ext_data[35] = 1; // COption::Some for withdraw_authority + ext_data[36..68].copy_from_slice(self.payer.to_account_view().address().as_ref()); + ext_data[68..70].copy_from_slice(&basis_points.to_le_bytes()); + ext_data[70..78].copy_from_slice(&max_fee.to_le_bytes()); + + CpiCall::new( + self.token_program.to_account_view().address(), + [InstructionAccount::writable( + self.mint_account.to_account_view().address(), + )], + [self.mint_account.to_account_view()], + ext_data, ) .invoke()?; - // TransferFeeExtension opcode 26, sub-instruction 0 = InitializeTransferFeeConfig - // Data: [26, 0, COption_flag(1), config_authority(32), COption_flag(1), withdraw_authority(32), - // basis_points(u16 LE), max_fee(u64 LE)] - let mut ext_data = [0u8; 78]; - ext_data[0] = 26; // TransferFeeExtension - ext_data[1] = 0; // InitializeTransferFeeConfig sub-instruction - ext_data[2] = 1; // COption::Some for config_authority - ext_data[3..35].copy_from_slice(accounts.payer.to_account_view().address().as_ref()); - ext_data[35] = 1; // COption::Some for withdraw_authority - ext_data[36..68].copy_from_slice(accounts.payer.to_account_view().address().as_ref()); - ext_data[68..70].copy_from_slice(&basis_points.to_le_bytes()); - ext_data[70..78].copy_from_slice(&max_fee.to_le_bytes()); - - CpiCall::new( - accounts.token_program.to_account_view().address(), - [InstructionAccount::writable( - accounts.mint_account.to_account_view().address(), - )], - [accounts.mint_account.to_account_view()], - ext_data, - ) - .invoke()?; - - // InitializeMint2 - let mut mint_data = [0u8; 67]; - mint_data[0] = 20; - mint_data[1] = 2; - mint_data[2..34].copy_from_slice(accounts.payer.to_account_view().address().as_ref()); - mint_data[34] = 1; - mint_data[35..67].copy_from_slice(accounts.payer.to_account_view().address().as_ref()); - - CpiCall::new( - accounts.token_program.to_account_view().address(), - [InstructionAccount::writable( - accounts.mint_account.to_account_view().address(), - )], - [accounts.mint_account.to_account_view()], - mint_data, - ) - .invoke() + // InitializeMint2 + let mut mint_data = [0u8; 67]; + mint_data[0] = 20; + mint_data[1] = 2; + mint_data[2..34].copy_from_slice(self.payer.to_account_view().address().as_ref()); + mint_data[34] = 1; + mint_data[35..67].copy_from_slice(self.payer.to_account_view().address().as_ref()); + + CpiCall::new( + self.token_program.to_account_view().address(), + [InstructionAccount::writable( + self.mint_account.to_account_view().address(), + )], + [self.mint_account.to_account_view()], + mint_data, + ) + .invoke() + } } #[derive(Accounts)] -pub struct Transfer<'info> { +pub struct Transfer { #[account(mut)] - pub sender: &'info Signer, + pub sender: Signer, #[account(mut)] - pub from: &'info mut UncheckedAccount, - pub mint: &'info UncheckedAccount, + pub from: UncheckedAccount, + pub mint: UncheckedAccount, #[account(mut)] - pub to: &'info mut UncheckedAccount, - pub token_program: &'info Program, + pub to: UncheckedAccount, + pub token_program: Program, } -#[inline(always)] -pub fn handle_transfer(accounts: &mut Transfer, amount: u64, fee: u64) -> Result<(), ProgramError> { - // TransferCheckedWithFee: opcode 37 - // Data: [37, amount (u64 LE), decimals (u8), fee (u64 LE)] - let mut data = [0u8; 18]; - data[0] = 37; - data[1..9].copy_from_slice(&amount.to_le_bytes()); - data[9] = 2; // decimals - data[10..18].copy_from_slice(&fee.to_le_bytes()); - - CpiCall::new( - accounts.token_program.to_account_view().address(), - [ - InstructionAccount::writable(accounts.from.to_account_view().address()), - InstructionAccount::readonly(accounts.mint.to_account_view().address()), - InstructionAccount::writable(accounts.to.to_account_view().address()), - InstructionAccount::readonly_signer(accounts.sender.to_account_view().address()), - ], - [ - accounts.from.to_account_view(), - accounts.mint.to_account_view(), - accounts.to.to_account_view(), - accounts.sender.to_account_view(), - ], - data, - ) - .invoke() +impl Transfer { + #[inline(always)] + pub fn transfer(&mut self, amount: u64, fee: u64) -> Result<(), ProgramError> { + // TransferCheckedWithFee: opcode 37 + // Data: [37, amount (u64 LE), decimals (u8), fee (u64 LE)] + let mut data = [0u8; 18]; + data[0] = 37; + data[1..9].copy_from_slice(&amount.to_le_bytes()); + data[9] = 2; // decimals + data[10..18].copy_from_slice(&fee.to_le_bytes()); + + CpiCall::new( + self.token_program.to_account_view().address(), + [ + InstructionAccount::writable(self.from.to_account_view().address()), + InstructionAccount::readonly(self.mint.to_account_view().address()), + InstructionAccount::writable(self.to.to_account_view().address()), + InstructionAccount::readonly_signer(self.sender.to_account_view().address()), + ], + [ + self.from.to_account_view(), + self.mint.to_account_view(), + self.to.to_account_view(), + self.sender.to_account_view(), + ], + data, + ) + .invoke() + } } #[derive(Accounts)] -pub struct UpdateFee<'info> { - pub authority: &'info Signer, +pub struct UpdateFee { + pub authority: Signer, #[account(mut)] - pub mint_account: &'info mut UncheckedAccount, - pub token_program: &'info Program, + pub mint_account: UncheckedAccount, + pub token_program: Program, } -#[inline(always)] -pub fn handle_update_fee(accounts: &UpdateFee, basis_points: u16, max_fee: u64) -> Result<(), ProgramError> { - // SetTransferFee: opcode 26, sub-opcode 4 - // Actually: extension instruction layout is different. - // TransferFeeInstruction::SetTransferFee = 4 within type 26 - let mut data = [0u8; 12]; - data[0] = 26; - data[1] = 4; // SetTransferFee sub-instruction - data[2..4].copy_from_slice(&basis_points.to_le_bytes()); - data[4..12].copy_from_slice(&max_fee.to_le_bytes()); - - CpiCall::new( - accounts.token_program.to_account_view().address(), - [ - InstructionAccount::writable(accounts.mint_account.to_account_view().address()), - InstructionAccount::readonly_signer(accounts.authority.to_account_view().address()), - ], - [ - accounts.mint_account.to_account_view(), - accounts.authority.to_account_view(), - ], - data, - ) - .invoke() +impl UpdateFee { + #[inline(always)] + pub fn update_fee(&mut self, basis_points: u16, max_fee: u64) -> Result<(), ProgramError> { + // SetTransferFee: opcode 26, sub-opcode 4 + // Actually: extension instruction layout is different. + // TransferFeeInstruction::SetTransferFee = 4 within type 26 + let mut data = [0u8; 12]; + data[0] = 26; + data[1] = 4; // SetTransferFee sub-instruction + data[2..4].copy_from_slice(&basis_points.to_le_bytes()); + data[4..12].copy_from_slice(&max_fee.to_le_bytes()); + + CpiCall::new( + self.token_program.to_account_view().address(), + [ + InstructionAccount::writable(self.mint_account.to_account_view().address()), + InstructionAccount::readonly_signer(self.authority.to_account_view().address()), + ], + [ + self.mint_account.to_account_view(), + self.authority.to_account_view(), + ], + data, + ) + .invoke() + } } #[derive(Accounts)] -pub struct Withdraw<'info> { - pub authority: &'info Signer, +pub struct Withdraw { + pub authority: Signer, #[account(mut)] - pub mint_account: &'info mut UncheckedAccount, + pub mint_account: UncheckedAccount, #[account(mut)] - pub destination: &'info mut UncheckedAccount, - pub token_program: &'info Program, + pub destination: UncheckedAccount, + pub token_program: Program, } -#[inline(always)] -pub fn handle_withdraw(accounts: &Withdraw) -> Result<(), ProgramError> { - // WithdrawWithheldTokensFromMint: opcode 26, sub-opcode 3 - let data: [u8; 2] = [26, 3]; - - CpiCall::new( - accounts.token_program.to_account_view().address(), - [ - InstructionAccount::writable(accounts.mint_account.to_account_view().address()), - InstructionAccount::writable(accounts.destination.to_account_view().address()), - InstructionAccount::readonly_signer(accounts.authority.to_account_view().address()), - ], - [ - accounts.mint_account.to_account_view(), - accounts.destination.to_account_view(), - accounts.authority.to_account_view(), - ], - data, - ) - .invoke() +impl Withdraw { + #[inline(always)] + pub fn withdraw(&mut self) -> Result<(), ProgramError> { + // WithdrawWithheldTokensFromMint: opcode 26, sub-opcode 3 + let data: [u8; 2] = [26, 3]; + + CpiCall::new( + self.token_program.to_account_view().address(), + [ + InstructionAccount::writable(self.mint_account.to_account_view().address()), + InstructionAccount::writable(self.destination.to_account_view().address()), + InstructionAccount::readonly_signer(self.authority.to_account_view().address()), + ], + [ + self.mint_account.to_account_view(), + self.destination.to_account_view(), + self.authority.to_account_view(), + ], + data, + ) + .invoke() + } } diff --git a/tokens/token-extensions/transfer-hook/account-data-as-seed/quasar/Cargo.toml b/tokens/token-extensions/transfer-hook/account-data-as-seed/quasar/Cargo.toml index 11a0f9a48..5e3049aed 100644 --- a/tokens/token-extensions/transfer-hook/account-data-as-seed/quasar/Cargo.toml +++ b/tokens/token-extensions/transfer-hook/account-data-as-seed/quasar/Cargo.toml @@ -20,8 +20,8 @@ client = [] debug = [] [dependencies] -quasar-lang = "0.0" -quasar-spl = "0.0" +quasar-lang = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } +quasar-spl = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } solana-instruction = { version = "3.2.0" } [dev-dependencies] diff --git a/tokens/token-extensions/transfer-hook/account-data-as-seed/quasar/src/lib.rs b/tokens/token-extensions/transfer-hook/account-data-as-seed/quasar/src/lib.rs index b91114dff..00024482a 100644 --- a/tokens/token-extensions/transfer-hook/account-data-as-seed/quasar/src/lib.rs +++ b/tokens/token-extensions/transfer-hook/account-data-as-seed/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![cfg_attr(not(test), no_std)] +#![no_std] use quasar_lang::sysvars::Sysvar; use quasar_lang::{ diff --git a/tokens/token-extensions/transfer-hook/allow-block-list-token/quasar/Cargo.toml b/tokens/token-extensions/transfer-hook/allow-block-list-token/quasar/Cargo.toml index 12dd396a9..f00f63f19 100644 --- a/tokens/token-extensions/transfer-hook/allow-block-list-token/quasar/Cargo.toml +++ b/tokens/token-extensions/transfer-hook/allow-block-list-token/quasar/Cargo.toml @@ -18,8 +18,8 @@ client = [] debug = [] [dependencies] -quasar-lang = "0.0" -quasar-spl = "0.0" +quasar-lang = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } +quasar-spl = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } solana-instruction = { version = "3.2.0" } [dev-dependencies] diff --git a/tokens/token-extensions/transfer-hook/allow-block-list-token/quasar/src/lib.rs b/tokens/token-extensions/transfer-hook/allow-block-list-token/quasar/src/lib.rs index 74b3df3f1..d3227a07c 100644 --- a/tokens/token-extensions/transfer-hook/allow-block-list-token/quasar/src/lib.rs +++ b/tokens/token-extensions/transfer-hook/allow-block-list-token/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![cfg_attr(not(test), no_std)] +#![no_std] use quasar_lang::prelude::*; diff --git a/tokens/token-extensions/transfer-hook/counter/quasar/Cargo.toml b/tokens/token-extensions/transfer-hook/counter/quasar/Cargo.toml index f638977bf..9d50e2b7e 100644 --- a/tokens/token-extensions/transfer-hook/counter/quasar/Cargo.toml +++ b/tokens/token-extensions/transfer-hook/counter/quasar/Cargo.toml @@ -20,8 +20,8 @@ client = [] debug = [] [dependencies] -quasar-lang = "0.0" -quasar-spl = "0.0" +quasar-lang = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } +quasar-spl = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } solana-instruction = { version = "3.2.0" } [dev-dependencies] diff --git a/tokens/token-extensions/transfer-hook/counter/quasar/src/lib.rs b/tokens/token-extensions/transfer-hook/counter/quasar/src/lib.rs index 28b66c913..91d2dba2e 100644 --- a/tokens/token-extensions/transfer-hook/counter/quasar/src/lib.rs +++ b/tokens/token-extensions/transfer-hook/counter/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![cfg_attr(not(test), no_std)] +#![no_std] use quasar_lang::sysvars::Sysvar; use quasar_lang::{ @@ -29,14 +29,14 @@ mod quasar_transfer_hook_counter { pub fn initialize_extra_account_meta_list( ctx: Ctx, ) -> Result<(), ProgramError> { - handle_initialize_extra_account_meta_list(&mut ctx.accounts) + ctx.accounts.initialize_extra_account_meta_list() } /// Transfer hook handler — increments the counter on each transfer. /// Discriminator = sha256("spl-transfer-hook-interface:execute")[:8] #[instruction(discriminator = [105, 37, 101, 197, 75, 251, 102, 26])] pub fn transfer_hook(ctx: Ctx, _amount: u64) -> Result<(), ProgramError> { - handle_transfer_hook(&mut ctx.accounts) + ctx.accounts.transfer_hook() } } @@ -45,134 +45,116 @@ mod quasar_transfer_hook_counter { // --------------------------------------------------------------------------- #[derive(Accounts)] -pub struct InitializeExtraAccountMetaList<'info> { +pub struct InitializeExtraAccountMetaList { #[account(mut)] - pub payer: &'info Signer, + pub payer: Signer, /// ExtraAccountMetaList PDA: ["extra-account-metas", mint] #[account(mut)] - pub extra_account_meta_list: &'info mut UncheckedAccount, - pub mint: &'info UncheckedAccount, + pub extra_account_meta_list: UncheckedAccount, + pub mint: UncheckedAccount, /// Counter PDA: ["counter"] #[account(mut)] - pub counter_account: &'info mut UncheckedAccount, - pub system_program: &'info Program, + pub counter_account: UncheckedAccount, + pub system_program: Program, } -#[inline(always)] -pub fn handle_initialize_extra_account_meta_list(accounts: &InitializeExtraAccountMetaList) -> Result<(), ProgramError> { - // ExtraAccountMetaList with 1 extra account: - // [8 bytes: Execute discriminator] - // [4 bytes: data length] - // [4 bytes: PodSlice count = 1] - // [35 bytes: ExtraAccountMeta entry for the counter PDA] - // Total = 8 + 4 + 4 + 35 = 51 bytes - let meta_list_size: u64 = 51; - let lamports = Rent::get()?.try_minimum_balance(meta_list_size as usize)?; - - // Derive ExtraAccountMetaList PDA - let mint_address = accounts.mint.to_account_view().address(); - let (expected_pda, bump) = Address::find_program_address( - &[b"extra-account-metas", mint_address.as_ref()], - &crate::ID, - ); - - let meta_list_address = accounts.extra_account_meta_list.to_account_view().address(); - if meta_list_address != &expected_pda { - return Err(ProgramError::InvalidSeeds); - } - - // Create ExtraAccountMetaList PDA - let bump_bytes = [bump]; - let seeds = [ - Seed::from(b"extra-account-metas" as &[u8]), - Seed::from(mint_address.as_ref()), - Seed::from(&bump_bytes as &[u8]), - ]; - - accounts.system_program - .create_account( - accounts.payer, - &*accounts.extra_account_meta_list, - lamports, - meta_list_size, +impl InitializeExtraAccountMetaList { + #[inline(always)] + pub fn initialize_extra_account_meta_list(&mut self) -> Result<(), ProgramError> { + // ExtraAccountMetaList with 1 extra account: + // [8 bytes: Execute discriminator] + // [4 bytes: data length] + // [4 bytes: PodSlice count = 1] + // [35 bytes: ExtraAccountMeta entry for the counter PDA] + // Total = 8 + 4 + 4 + 35 = 51 bytes + let meta_list_size: u64 = 51; + let lamports = Rent::get()?.try_minimum_balance(meta_list_size as usize)?; + + // Derive ExtraAccountMetaList PDA + let mint_address = self.mint.to_account_view().address(); + let (expected_pda, bump) = Address::find_program_address( + &[b"extra-account-metas", mint_address.as_ref()], &crate::ID, - ) - .invoke_signed(&seeds)?; - - // Write TLV data with the counter PDA as an extra account - let view = unsafe { - &mut *(accounts.extra_account_meta_list as *const UncheckedAccount as *mut UncheckedAccount - as *mut AccountView) - }; - let mut data = view.try_borrow_mut()?; - - // Execute discriminator (TLV type tag) - data[0..8].copy_from_slice(&EXECUTE_DISCRIMINATOR); - // Data length: 4 (count) + 35 (one ExtraAccountMeta) = 39 - data[8..12].copy_from_slice(&39u32.to_le_bytes()); - // PodSlice count: 1 entry - data[12..16].copy_from_slice(&1u32.to_le_bytes()); - - // ExtraAccountMeta for counter PDA (35 bytes): - // [0]: discriminator (1 = PDA from seeds) - // [1]: address_config (32 bytes encoding the seeds) - // [33]: is_signer (0) - // [34]: is_writable (1) - // - // For a PDA with seeds = [Literal("counter")], the address_config - // uses the ExtraAccountMeta seed encoding format. The seeds are: - // Seed::Literal { bytes: b"counter" } - // Encoded as: [length: 1 byte][data: N bytes] - // - // The full ExtraAccountMeta seed-based encoding: - // discriminator = 1 (PDA) - // address_config[0] = 1 (number of seeds) - // address_config[1] = 0 (seed type: literal) - // address_config[2] = 7 (seed length) - // address_config[3..10] = b"counter" - // address_config[10..32] = zeroes (padding) - // is_signer = 0 - // is_writable = 1 - data[16] = 1; // discriminator: PDA from seeds - let mut config = [0u8; 32]; - config[0] = 1; // number of seeds - config[1] = 0; // seed type: literal - config[2] = 7; // seed length - config[3..10].copy_from_slice(b"counter"); - data[17..49].copy_from_slice(&config); - data[49] = 0; // is_signer = false - data[50] = 1; // is_writable = true - - // Also create the counter PDA (8 bytes for u64 counter + 8 bytes discriminator) - let counter_size: u64 = 16; - let counter_lamports = Rent::get()?.try_minimum_balance(counter_size as usize)?; - - let (counter_pda, counter_bump) = - Address::find_program_address(&[b"counter"], &crate::ID); - - let counter_address = accounts.counter_account.to_account_view().address(); - if counter_address != &counter_pda { - return Err(ProgramError::InvalidSeeds); + ); + + let meta_list_address = self.extra_account_meta_list.to_account_view().address(); + if meta_list_address != &expected_pda { + return Err(ProgramError::InvalidSeeds); + } + + // Create ExtraAccountMetaList PDA + let bump_bytes = [bump]; + let seeds = [ + Seed::from(b"extra-account-metas" as &[u8]), + Seed::from(mint_address.as_ref()), + Seed::from(&bump_bytes as &[u8]), + ]; + + self.system_program + .create_account( + &self.payer, + &*self.extra_account_meta_list, + lamports, + meta_list_size, + &crate::ID, + ) + .invoke_signed(&seeds)?; + + // Write TLV data with the counter PDA as an extra account + let view = unsafe { + &mut *(self.extra_account_meta_list as *const UncheckedAccount as *mut UncheckedAccount + as *mut AccountView) + }; + let mut data = view.try_borrow_mut()?; + + // Execute discriminator (TLV type tag) + data[0..8].copy_from_slice(&EXECUTE_DISCRIMINATOR); + // Data length: 4 (count) + 35 (one ExtraAccountMeta) = 39 + data[8..12].copy_from_slice(&39u32.to_le_bytes()); + // PodSlice count: 1 entry + data[12..16].copy_from_slice(&1u32.to_le_bytes()); + + data[16] = 1; // discriminator: PDA from seeds + let mut config = [0u8; 32]; + config[0] = 1; // number of seeds + config[1] = 0; // seed type: literal + config[2] = 7; // seed length + config[3..10].copy_from_slice(b"counter"); + data[17..49].copy_from_slice(&config); + data[49] = 0; // is_signer = false + data[50] = 1; // is_writable = true + + // Also create the counter PDA (8 bytes for u64 counter + 8 bytes discriminator) + let counter_size: u64 = 16; + let counter_lamports = Rent::get()?.try_minimum_balance(counter_size as usize)?; + + let (counter_pda, counter_bump) = + Address::find_program_address(&[b"counter"], &crate::ID); + + let counter_address = self.counter_account.to_account_view().address(); + if counter_address != &counter_pda { + return Err(ProgramError::InvalidSeeds); + } + + let counter_bump_bytes = [counter_bump]; + let counter_seeds = [ + Seed::from(b"counter" as &[u8]), + Seed::from(&counter_bump_bytes as &[u8]), + ]; + + self.system_program + .create_account( + &self.payer, + &*self.counter_account, + counter_lamports, + counter_size, + &crate::ID, + ) + .invoke_signed(&counter_seeds)?; + + log("Extra account meta list and counter initialized"); + Ok(()) } - - let counter_bump_bytes = [counter_bump]; - let counter_seeds = [ - Seed::from(b"counter" as &[u8]), - Seed::from(&counter_bump_bytes as &[u8]), - ]; - - accounts.system_program - .create_account( - accounts.payer, - &*accounts.counter_account, - counter_lamports, - counter_size, - &crate::ID, - ) - .invoke_signed(&counter_seeds)?; - - log("Extra account meta list and counter initialized"); - Ok(()) } // --------------------------------------------------------------------------- @@ -180,47 +162,49 @@ pub fn handle_initialize_extra_account_meta_list(accounts: &InitializeExtraAccou // --------------------------------------------------------------------------- #[derive(Accounts)] -pub struct TransferHook<'info> { +pub struct TransferHook { /// Source token account - pub source_token: &'info UncheckedAccount, + pub source_token: UncheckedAccount, /// Mint - pub mint: &'info UncheckedAccount, + pub mint: UncheckedAccount, /// Destination token account - pub destination_token: &'info UncheckedAccount, + pub destination_token: UncheckedAccount, /// Source token account owner - pub owner: &'info UncheckedAccount, + pub owner: UncheckedAccount, /// ExtraAccountMetaList PDA - pub extra_account_meta_list: &'info UncheckedAccount, + pub extra_account_meta_list: UncheckedAccount, /// Counter PDA (extra account resolved by Token-2022) #[account(mut)] - pub counter_account: &'info mut UncheckedAccount, + pub counter_account: UncheckedAccount, } -#[inline(always)] -pub fn handle_transfer_hook(accounts: &TransferHook) -> Result<(), ProgramError> { - // Read the current counter from the account data - let view = unsafe { - &mut *(accounts.counter_account as *const UncheckedAccount as *mut UncheckedAccount - as *mut AccountView) - }; - let mut data = view.try_borrow_mut()?; - - // Counter is at offset 8 (after 8-byte Anchor-style discriminator) - // In our case we just use the first 8 bytes as the counter - if data.len() < 16 { - return Err(ProgramError::AccountDataTooSmall); +impl TransferHook { + #[inline(always)] + pub fn transfer_hook(&mut self) -> Result<(), ProgramError> { + // Read the current counter from the account data + let view = unsafe { + &mut *(self.counter_account as *const UncheckedAccount as *mut UncheckedAccount + as *mut AccountView) + }; + let mut data = view.try_borrow_mut()?; + + // Counter is at offset 8 (after 8-byte Anchor-style discriminator) + // In our case we just use the first 8 bytes as the counter + if data.len() < 16 { + return Err(ProgramError::AccountDataTooSmall); + } + + let mut counter_bytes = [0u8; 8]; + counter_bytes.copy_from_slice(&data[8..16]); + let counter = u64::from_le_bytes(counter_bytes); + + let new_counter = counter + .checked_add(1) + .ok_or(ProgramError::ArithmeticOverflow)?; + + data[8..16].copy_from_slice(&new_counter.to_le_bytes()); + + log("Transfer hook: counter incremented"); + Ok(()) } - - let mut counter_bytes = [0u8; 8]; - counter_bytes.copy_from_slice(&data[8..16]); - let counter = u64::from_le_bytes(counter_bytes); - - let new_counter = counter - .checked_add(1) - .ok_or(ProgramError::ArithmeticOverflow)?; - - data[8..16].copy_from_slice(&new_counter.to_le_bytes()); - - log("Transfer hook: counter incremented"); - Ok(()) } diff --git a/tokens/token-extensions/transfer-hook/hello-world/quasar/Cargo.toml b/tokens/token-extensions/transfer-hook/hello-world/quasar/Cargo.toml index 2166fba4a..1429a14da 100644 --- a/tokens/token-extensions/transfer-hook/hello-world/quasar/Cargo.toml +++ b/tokens/token-extensions/transfer-hook/hello-world/quasar/Cargo.toml @@ -20,8 +20,8 @@ client = [] debug = [] [dependencies] -quasar-lang = "0.0" -quasar-spl = "0.0" +quasar-lang = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } +quasar-spl = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } solana-instruction = { version = "3.2.0" } [dev-dependencies] diff --git a/tokens/token-extensions/transfer-hook/hello-world/quasar/src/lib.rs b/tokens/token-extensions/transfer-hook/hello-world/quasar/src/lib.rs index 27ab88289..98b04b71b 100644 --- a/tokens/token-extensions/transfer-hook/hello-world/quasar/src/lib.rs +++ b/tokens/token-extensions/transfer-hook/hello-world/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![cfg_attr(not(test), no_std)] +#![no_std] use quasar_lang::sysvars::Sysvar; use quasar_lang::{ @@ -37,7 +37,7 @@ mod quasar_transfer_hook_hello_world { /// Custom discriminator (not part of the transfer hook interface). #[instruction(discriminator = [0, 0, 0, 0, 0, 0, 0, 1])] pub fn initialize(ctx: Ctx, decimals: u8) -> Result<(), ProgramError> { - handle_initialize(&mut ctx.accounts, decimals) + ctx.accounts.initialize(decimals) } /// Create the ExtraAccountMetaList PDA (empty — no extra accounts). @@ -46,14 +46,14 @@ mod quasar_transfer_hook_hello_world { pub fn initialize_extra_account_meta_list( ctx: Ctx, ) -> Result<(), ProgramError> { - handle_initialize_extra_account_meta_list(&mut ctx.accounts) + ctx.accounts.initialize_extra_account_meta_list() } /// Transfer hook handler — called automatically by Token-2022 during transfers. /// Discriminator = sha256("spl-transfer-hook-interface:execute")[:8] #[instruction(discriminator = [105, 37, 101, 197, 75, 251, 102, 26])] pub fn transfer_hook(ctx: Ctx, _amount: u64) -> Result<(), ProgramError> { - handle_transfer_hook(&mut ctx.accounts) + ctx.accounts.transfer_hook() } } @@ -62,69 +62,71 @@ mod quasar_transfer_hook_hello_world { // --------------------------------------------------------------------------- #[derive(Accounts)] -pub struct Initialize<'info> { +pub struct Initialize { #[account(mut)] - pub payer: &'info Signer, + pub payer: Signer, #[account(mut)] - pub mint_account: &'info Signer, - pub token_program: &'info Program, - pub system_program: &'info Program, + pub mint_account: Signer, + pub token_program: Program, + pub system_program: Program, } -#[inline(always)] -pub fn handle_initialize(accounts: &Initialize, decimals: u8) -> Result<(), ProgramError> { - // Mint with TransferHook extension: - // 165 (base account + padding) + 1 (account type) + 4 (TLV header) + 64 (extension) = 234 - let mint_size: u64 = 234; - let lamports = Rent::get()?.try_minimum_balance(mint_size as usize)?; - - // 1. Create account owned by Token-2022 - accounts.system_program - .create_account( - accounts.payer, - accounts.mint_account, - lamports, - mint_size, - accounts.token_program.to_account_view().address(), +impl Initialize { + #[inline(always)] + pub fn initialize(&mut self, decimals: u8) -> Result<(), ProgramError> { + // Mint with TransferHook extension: + // 165 (base account + padding) + 1 (account type) + 4 (TLV header) + 64 (extension) = 234 + let mint_size: u64 = 234; + let lamports = Rent::get()?.try_minimum_balance(mint_size as usize)?; + + // 1. Create account owned by Token-2022 + self.system_program + .create_account( + &self.payer, + &self.mint_account, + lamports, + mint_size, + self.token_program.to_account_view().address(), + ) + .invoke()?; + + // 2. InitializeTransferHook extension + // Layout: [36u8 (TransferHookExtension), 0u8 (Initialize), + // authority(32), program_id(32)] + let mut ext_data = [0u8; 66]; + ext_data[0] = 36; // TokenInstruction::TransferHookExtension + ext_data[1] = 0; // TransferHookInstruction::Initialize + ext_data[2..34].copy_from_slice(self.payer.to_account_view().address().as_ref()); + ext_data[34..66].copy_from_slice(crate::ID.as_ref()); + + CpiCall::new( + self.token_program.to_account_view().address(), + [InstructionAccount::writable( + self.mint_account.to_account_view().address(), + )], + [self.mint_account.to_account_view()], + ext_data, ) .invoke()?; - // 2. InitializeTransferHook extension - // Layout: [36u8 (TransferHookExtension), 0u8 (Initialize), - // authority(32), program_id(32)] - let mut ext_data = [0u8; 66]; - ext_data[0] = 36; // TokenInstruction::TransferHookExtension - ext_data[1] = 0; // TransferHookInstruction::Initialize - ext_data[2..34].copy_from_slice(accounts.payer.to_account_view().address().as_ref()); - ext_data[34..66].copy_from_slice(crate::ID.as_ref()); - - CpiCall::new( - accounts.token_program.to_account_view().address(), - [InstructionAccount::writable( - accounts.mint_account.to_account_view().address(), - )], - [accounts.mint_account.to_account_view()], - ext_data, - ) - .invoke()?; - - // 3. InitializeMint2: opcode 20 - let mut mint_data = [0u8; 67]; - mint_data[0] = 20; - mint_data[1] = decimals; - mint_data[2..34].copy_from_slice(accounts.payer.to_account_view().address().as_ref()); - mint_data[34] = 1; // has freeze authority - mint_data[35..67].copy_from_slice(accounts.payer.to_account_view().address().as_ref()); - - CpiCall::new( - accounts.token_program.to_account_view().address(), - [InstructionAccount::writable( - accounts.mint_account.to_account_view().address(), - )], - [accounts.mint_account.to_account_view()], - mint_data, - ) - .invoke() + // 3. InitializeMint2: opcode 20 + let mut mint_data = [0u8; 67]; + mint_data[0] = 20; + mint_data[1] = decimals; + mint_data[2..34].copy_from_slice(self.payer.to_account_view().address().as_ref()); + mint_data[34] = 1; // has freeze authority + mint_data[35..67].copy_from_slice(self.payer.to_account_view().address().as_ref()); + + CpiCall::new( + self.token_program.to_account_view().address(), + [InstructionAccount::writable( + self.mint_account.to_account_view().address(), + )], + [self.mint_account.to_account_view()], + mint_data, + ) + .invoke() + } } // --------------------------------------------------------------------------- @@ -132,75 +134,77 @@ pub fn handle_initialize(accounts: &Initialize, decimals: u8) -> Result<(), Prog // --------------------------------------------------------------------------- #[derive(Accounts)] -pub struct InitializeExtraAccountMetaList<'info> { +pub struct InitializeExtraAccountMetaList { #[account(mut)] - pub payer: &'info Signer, + pub payer: Signer, /// ExtraAccountMetaList PDA seeded by ["extra-account-metas", mint] #[account(mut)] - pub extra_account_meta_list: &'info mut UncheckedAccount, - pub mint: &'info UncheckedAccount, - pub system_program: &'info Program, + pub extra_account_meta_list: UncheckedAccount, + pub mint: UncheckedAccount, + pub system_program: Program, } -#[inline(always)] -pub fn handle_initialize_extra_account_meta_list(accounts: &InitializeExtraAccountMetaList) -> Result<(), ProgramError> { - use quasar_lang::cpi::Seed; - - // ExtraAccountMetaList with 0 extra accounts: - // [8 bytes: Execute discriminator] - // [4 bytes: data length = 4] - // [4 bytes: PodSlice count = 0] - // Total = 16 bytes - let meta_list_size: u64 = 16; - let lamports = Rent::get()?.try_minimum_balance(meta_list_size as usize)?; - - // Derive PDA - let mint_address = accounts.mint.to_account_view().address(); - let (expected_pda, bump) = Address::find_program_address( - &[b"extra-account-metas", mint_address.as_ref()], - &crate::ID, - ); - - let meta_list_address = accounts.extra_account_meta_list.to_account_view().address(); - if meta_list_address != &expected_pda { - return Err(ProgramError::InvalidSeeds); - } - - // Create PDA account owned by this program - let bump_bytes = [bump]; - let seeds = [ - Seed::from(b"extra-account-metas" as &[u8]), - Seed::from(mint_address.as_ref()), - Seed::from(&bump_bytes as &[u8]), - ]; - - accounts.system_program - .create_account( - accounts.payer, - &*accounts.extra_account_meta_list, - lamports, - meta_list_size, +impl InitializeExtraAccountMetaList { + #[inline(always)] + pub fn initialize_extra_account_meta_list(&mut self) -> Result<(), ProgramError> { + use quasar_lang::cpi::Seed; + + // ExtraAccountMetaList with 0 extra accounts: + // [8 bytes: Execute discriminator] + // [4 bytes: data length = 4] + // [4 bytes: PodSlice count = 0] + // Total = 16 bytes + let meta_list_size: u64 = 16; + let lamports = Rent::get()?.try_minimum_balance(meta_list_size as usize)?; + + // Derive PDA + let mint_address = self.mint.to_account_view().address(); + let (expected_pda, bump) = Address::find_program_address( + &[b"extra-account-metas", mint_address.as_ref()], &crate::ID, - ) - .invoke_signed(&seeds)?; - - // Write TLV data into the account. - // SAFETY: Account was just created (16 bytes) and is owned by this program. - // UncheckedAccount is #[repr(transparent)] over AccountView, so the cast is safe. - let view = unsafe { - &mut *(accounts.extra_account_meta_list as *const UncheckedAccount as *mut UncheckedAccount - as *mut AccountView) - }; - let mut data = view.try_borrow_mut()?; - // Execute discriminator (type tag in TLV) - data[0..8].copy_from_slice(&EXECUTE_DISCRIMINATOR); - // Data length: 4 bytes for the PodSlice count field - data[8..12].copy_from_slice(&4u32.to_le_bytes()); - // PodSlice count: 0 entries - data[12..16].copy_from_slice(&0u32.to_le_bytes()); - - log("Extra account meta list initialized"); - Ok(()) + ); + + let meta_list_address = self.extra_account_meta_list.to_account_view().address(); + if meta_list_address != &expected_pda { + return Err(ProgramError::InvalidSeeds); + } + + // Create PDA account owned by this program + let bump_bytes = [bump]; + let seeds = [ + Seed::from(b"extra-account-metas" as &[u8]), + Seed::from(mint_address.as_ref()), + Seed::from(&bump_bytes as &[u8]), + ]; + + self.system_program + .create_account( + &self.payer, + &*self.extra_account_meta_list, + lamports, + meta_list_size, + &crate::ID, + ) + .invoke_signed(&seeds)?; + + // Write TLV data into the account. + // SAFETY: Account was just created (16 bytes) and is owned by this program. + // UncheckedAccount is #[repr(transparent)] over AccountView, so the cast is safe. + let view = unsafe { + &mut *(self.extra_account_meta_list as *const UncheckedAccount as *mut UncheckedAccount + as *mut AccountView) + }; + let mut data = view.try_borrow_mut()?; + // Execute discriminator (type tag in TLV) + data[0..8].copy_from_slice(&EXECUTE_DISCRIMINATOR); + // Data length: 4 bytes for the PodSlice count field + data[8..12].copy_from_slice(&4u32.to_le_bytes()); + // PodSlice count: 0 entries + data[12..16].copy_from_slice(&0u32.to_le_bytes()); + + log("Extra account meta list initialized"); + Ok(()) + } } // --------------------------------------------------------------------------- @@ -208,26 +212,28 @@ pub fn handle_initialize_extra_account_meta_list(accounts: &InitializeExtraAccou // --------------------------------------------------------------------------- #[derive(Accounts)] -pub struct TransferHook<'info> { +pub struct TransferHook { /// Source token account - pub source_token: &'info UncheckedAccount, + pub source_token: UncheckedAccount, /// Mint - pub mint: &'info UncheckedAccount, + pub mint: UncheckedAccount, /// Destination token account - pub destination_token: &'info UncheckedAccount, + pub destination_token: UncheckedAccount, /// Source token account owner - pub owner: &'info UncheckedAccount, + pub owner: UncheckedAccount, /// ExtraAccountMetaList PDA - pub extra_account_meta_list: &'info UncheckedAccount, + pub extra_account_meta_list: UncheckedAccount, } -#[inline(always)] -pub fn handle_transfer_hook(accounts: &TransferHook) -> Result<(), ProgramError> { - // In production, verify the source token's TransferHookAccount.transferring - // flag is set. The Token-2022 program sets this before invoking the hook - // and clears it after, preventing standalone invocation. - // - // For this hello-world example, we simply log a message. - log("Hello Transfer Hook!"); - Ok(()) +impl TransferHook { + #[inline(always)] + pub fn transfer_hook(&mut self) -> Result<(), ProgramError> { + // In production, verify the source token's TransferHookAccount.transferring + // flag is set. The Token-2022 program sets this before invoking the hook + // and clears it after, preventing standalone invocation. + // + // For this hello-world example, we simply log a message. + log("Hello Transfer Hook!"); + Ok(()) + } } diff --git a/tokens/token-extensions/transfer-hook/transfer-cost/quasar/Cargo.toml b/tokens/token-extensions/transfer-hook/transfer-cost/quasar/Cargo.toml index d8b3bb9cb..428b1ad8c 100644 --- a/tokens/token-extensions/transfer-hook/transfer-cost/quasar/Cargo.toml +++ b/tokens/token-extensions/transfer-hook/transfer-cost/quasar/Cargo.toml @@ -18,8 +18,8 @@ client = [] debug = [] [dependencies] -quasar-lang = "0.0" -quasar-spl = "0.0" +quasar-lang = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } +quasar-spl = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } solana-instruction = { version = "3.2.0" } [dev-dependencies] diff --git a/tokens/token-extensions/transfer-hook/transfer-cost/quasar/src/lib.rs b/tokens/token-extensions/transfer-hook/transfer-cost/quasar/src/lib.rs index 51ff8c549..143a4f6f7 100644 --- a/tokens/token-extensions/transfer-hook/transfer-cost/quasar/src/lib.rs +++ b/tokens/token-extensions/transfer-hook/transfer-cost/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![cfg_attr(not(test), no_std)] +#![no_std] use quasar_lang::sysvars::Sysvar; use quasar_lang::{ @@ -33,7 +33,7 @@ mod quasar_transfer_hook_cost { pub fn initialize_extra_account_meta_list( ctx: Ctx, ) -> Result<(), ProgramError> { - handle_initialize(&mut ctx.accounts) + ctx.accounts.initialize_extra_account_meta_list() } /// Transfer hook handler — validates the amount and increments the counter. @@ -41,7 +41,7 @@ mod quasar_transfer_hook_cost { /// Discriminator = sha256("spl-transfer-hook-interface:execute")[:8] #[instruction(discriminator = [105, 37, 101, 197, 75, 251, 102, 26])] pub fn transfer_hook(ctx: Ctx, amount: u64) -> Result<(), ProgramError> { - handle_transfer_hook(&mut ctx.accounts, amount) + ctx.accounts.transfer_hook(amount) } } @@ -50,84 +50,86 @@ mod quasar_transfer_hook_cost { // --------------------------------------------------------------------------- #[derive(Accounts)] -pub struct InitializeExtraAccountMetaList<'info> { +pub struct InitializeExtraAccountMetaList { #[account(mut)] - pub payer: &'info Signer, + pub payer: Signer, #[account(mut)] - pub extra_account_meta_list: &'info mut UncheckedAccount, - pub mint: &'info UncheckedAccount, + pub extra_account_meta_list: UncheckedAccount, + pub mint: UncheckedAccount, #[account(mut)] - pub counter_account: &'info mut UncheckedAccount, - pub system_program: &'info Program, + pub counter_account: UncheckedAccount, + pub system_program: Program, } -#[inline(always)] -pub fn handle_initialize(accounts: &InitializeExtraAccountMetaList) -> Result<(), ProgramError> { - // Create ExtraAccountMetaList PDA with 1 extra account: counter - let meta_list_size: u64 = 51; - let lamports = Rent::get()?.try_minimum_balance(meta_list_size as usize)?; - - let mint_address = accounts.mint.to_account_view().address(); - let (expected_pda, bump) = Address::find_program_address( - &[b"extra-account-metas", mint_address.as_ref()], - &crate::ID, - ); - if accounts.extra_account_meta_list.to_account_view().address() != &expected_pda { - return Err(ProgramError::InvalidSeeds); +impl InitializeExtraAccountMetaList { + #[inline(always)] + pub fn initialize_extra_account_meta_list(&mut self) -> Result<(), ProgramError> { + // Create ExtraAccountMetaList PDA with 1 extra account: counter + let meta_list_size: u64 = 51; + let lamports = Rent::get()?.try_minimum_balance(meta_list_size as usize)?; + + let mint_address = self.mint.to_account_view().address(); + let (expected_pda, bump) = Address::find_program_address( + &[b"extra-account-metas", mint_address.as_ref()], + &crate::ID, + ); + if self.extra_account_meta_list.to_account_view().address() != &expected_pda { + return Err(ProgramError::InvalidSeeds); + } + + let bump_bytes = [bump]; + let seeds = [ + Seed::from(b"extra-account-metas" as &[u8]), + Seed::from(mint_address.as_ref()), + Seed::from(&bump_bytes as &[u8]), + ]; + self.system_program + .create_account(&self.payer, &*self.extra_account_meta_list, lamports, meta_list_size, &crate::ID) + .invoke_signed(&seeds)?; + + // Write TLV data + let view = unsafe { + &mut *(self.extra_account_meta_list as *const UncheckedAccount + as *mut UncheckedAccount as *mut AccountView) + }; + let mut data = view.try_borrow_mut()?; + data[0..8].copy_from_slice(&EXECUTE_DISCRIMINATOR); + data[8..12].copy_from_slice(&39u32.to_le_bytes()); + data[12..16].copy_from_slice(&1u32.to_le_bytes()); + + // ExtraAccountMeta: counter PDA with seeds = [Literal("counter")] + data[16] = 1; + let mut config = [0u8; 32]; + config[0] = 1; + config[1] = 0; // literal + config[2] = 7; + config[3..10].copy_from_slice(b"counter"); + data[17..49].copy_from_slice(&config); + data[49] = 0; + data[50] = 1; // writable + + // Create counter PDA: 1 byte for counter (u8) + let counter_size: u64 = 9; // 8 discriminator + 1 counter + let counter_lamports = Rent::get()?.try_minimum_balance(counter_size as usize)?; + + let (counter_pda, counter_bump) = + Address::find_program_address(&[b"counter"], &crate::ID); + if self.counter_account.to_account_view().address() != &counter_pda { + return Err(ProgramError::InvalidSeeds); + } + + let counter_bump_bytes = [counter_bump]; + let counter_seeds = [ + Seed::from(b"counter" as &[u8]), + Seed::from(&counter_bump_bytes as &[u8]), + ]; + self.system_program + .create_account(&self.payer, &*self.counter_account, counter_lamports, counter_size, &crate::ID) + .invoke_signed(&counter_seeds)?; + + log("Transfer cost hook initialized"); + Ok(()) } - - let bump_bytes = [bump]; - let seeds = [ - Seed::from(b"extra-account-metas" as &[u8]), - Seed::from(mint_address.as_ref()), - Seed::from(&bump_bytes as &[u8]), - ]; - accounts.system_program - .create_account(accounts.payer, &*accounts.extra_account_meta_list, lamports, meta_list_size, &crate::ID) - .invoke_signed(&seeds)?; - - // Write TLV data - let view = unsafe { - &mut *(accounts.extra_account_meta_list as *const UncheckedAccount - as *mut UncheckedAccount as *mut AccountView) - }; - let mut data = view.try_borrow_mut()?; - data[0..8].copy_from_slice(&EXECUTE_DISCRIMINATOR); - data[8..12].copy_from_slice(&39u32.to_le_bytes()); - data[12..16].copy_from_slice(&1u32.to_le_bytes()); - - // ExtraAccountMeta: counter PDA with seeds = [Literal("counter")] - data[16] = 1; - let mut config = [0u8; 32]; - config[0] = 1; - config[1] = 0; // literal - config[2] = 7; - config[3..10].copy_from_slice(b"counter"); - data[17..49].copy_from_slice(&config); - data[49] = 0; - data[50] = 1; // writable - - // Create counter PDA: 1 byte for counter (u8) - let counter_size: u64 = 9; // 8 discriminator + 1 counter - let counter_lamports = Rent::get()?.try_minimum_balance(counter_size as usize)?; - - let (counter_pda, counter_bump) = - Address::find_program_address(&[b"counter"], &crate::ID); - if accounts.counter_account.to_account_view().address() != &counter_pda { - return Err(ProgramError::InvalidSeeds); - } - - let counter_bump_bytes = [counter_bump]; - let counter_seeds = [ - Seed::from(b"counter" as &[u8]), - Seed::from(&counter_bump_bytes as &[u8]), - ]; - accounts.system_program - .create_account(accounts.payer, &*accounts.counter_account, counter_lamports, counter_size, &crate::ID) - .invoke_signed(&counter_seeds)?; - - log("Transfer cost hook initialized"); - Ok(()) } // --------------------------------------------------------------------------- @@ -135,47 +137,49 @@ pub fn handle_initialize(accounts: &InitializeExtraAccountMetaList) -> Result<() // --------------------------------------------------------------------------- #[derive(Accounts)] -pub struct TransferHook<'info> { - pub source_token: &'info UncheckedAccount, - pub mint: &'info UncheckedAccount, - pub destination_token: &'info UncheckedAccount, - pub owner: &'info UncheckedAccount, - pub extra_account_meta_list: &'info UncheckedAccount, +pub struct TransferHook { + pub source_token: UncheckedAccount, + pub mint: UncheckedAccount, + pub destination_token: UncheckedAccount, + pub owner: UncheckedAccount, + pub extra_account_meta_list: UncheckedAccount, #[account(mut)] - pub counter_account: &'info mut UncheckedAccount, + pub counter_account: UncheckedAccount, } -#[inline(always)] -pub fn handle_transfer_hook(accounts: &TransferHook, amount: u64) -> Result<(), ProgramError> { - // Validate amount - if amount > 50 { - log("Warning: large transfer amount"); +impl TransferHook { + #[inline(always)] + pub fn transfer_hook(&mut self, amount: u64) -> Result<(), ProgramError> { + // Validate amount + if amount > 50 { + log("Warning: large transfer amount"); + } + + // Increment transfer counter + let view = unsafe { + &mut *(self.counter_account as *const UncheckedAccount as *mut UncheckedAccount + as *mut AccountView) + }; + let mut data = view.try_borrow_mut()?; + + if data.len() < 9 { + return Err(ProgramError::AccountDataTooSmall); + } + + let counter = data[8]; + let new_counter = counter + .checked_add(1) + .ok_or(ProgramError::ArithmeticOverflow)?; + data[8] = new_counter; + + // In the full Anchor version, this would also: + // 1. Transfer WSOL from sender's ATA to delegate's ATA + // using the delegate PDA as the authority + // 2. The WSOL amount equals the token transfer amount + // This requires several additional accounts (WSOL mint, + // token program, ATA program, delegate PDA, and both ATAs). + + log("Transfer cost hook: counter incremented"); + Ok(()) } - - // Increment transfer counter - let view = unsafe { - &mut *(accounts.counter_account as *const UncheckedAccount as *mut UncheckedAccount - as *mut AccountView) - }; - let mut data = view.try_borrow_mut()?; - - if data.len() < 9 { - return Err(ProgramError::AccountDataTooSmall); - } - - let counter = data[8]; - let new_counter = counter - .checked_add(1) - .ok_or(ProgramError::ArithmeticOverflow)?; - data[8] = new_counter; - - // In the full Anchor version, this would also: - // 1. Transfer WSOL from sender's ATA to delegate's ATA - // using the delegate PDA as the authority - // 2. The WSOL amount equals the token transfer amount - // This requires several additional accounts (WSOL mint, - // token program, ATA program, delegate PDA, and both ATAs). - - log("Transfer cost hook: counter incremented"); - Ok(()) } diff --git a/tokens/token-extensions/transfer-hook/transfer-switch/quasar/Cargo.toml b/tokens/token-extensions/transfer-hook/transfer-switch/quasar/Cargo.toml index f46cc30a9..efa045273 100644 --- a/tokens/token-extensions/transfer-hook/transfer-switch/quasar/Cargo.toml +++ b/tokens/token-extensions/transfer-hook/transfer-switch/quasar/Cargo.toml @@ -18,8 +18,8 @@ client = [] debug = [] [dependencies] -quasar-lang = "0.0" -quasar-spl = "0.0" +quasar-lang = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } +quasar-spl = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } solana-instruction = { version = "3.2.0" } [dev-dependencies] diff --git a/tokens/token-extensions/transfer-hook/transfer-switch/quasar/src/lib.rs b/tokens/token-extensions/transfer-hook/transfer-switch/quasar/src/lib.rs index 4e7c8b77b..d12b86d4f 100644 --- a/tokens/token-extensions/transfer-hook/transfer-switch/quasar/src/lib.rs +++ b/tokens/token-extensions/transfer-hook/transfer-switch/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![cfg_attr(not(test), no_std)] +#![no_std] use quasar_lang::sysvars::Sysvar; use quasar_lang::{ @@ -25,7 +25,7 @@ mod quasar_transfer_hook_switch { /// Set up or change the admin. The first caller becomes admin. #[instruction(discriminator = [0, 0, 0, 0, 0, 0, 0, 1])] pub fn configure_admin(ctx: Ctx) -> Result<(), ProgramError> { - handle_configure_admin(&mut ctx.accounts) + ctx.accounts.configure_admin() } /// Create the ExtraAccountMetaList PDA. @@ -34,20 +34,20 @@ mod quasar_transfer_hook_switch { pub fn initialize_extra_account_metas_list( ctx: Ctx, ) -> Result<(), ProgramError> { - handle_initialize(&mut ctx.accounts) + ctx.accounts.initialize_extra_account_metas_list() } /// Toggle the transfer switch for a wallet. #[instruction(discriminator = [0, 0, 0, 0, 0, 0, 0, 3])] pub fn switch(ctx: Ctx, on: u8) -> Result<(), ProgramError> { - handle_switch(&mut ctx.accounts, on != 0) + ctx.accounts.switch(on != 0) } /// Transfer hook handler — checks the sender's switch is on. /// Discriminator = sha256("spl-transfer-hook-interface:execute")[:8] #[instruction(discriminator = [105, 37, 101, 197, 75, 251, 102, 26])] pub fn transfer_hook(ctx: Ctx, _amount: u64) -> Result<(), ProgramError> { - handle_transfer_hook(&mut ctx.accounts) + ctx.accounts.transfer_hook() } } @@ -61,63 +61,65 @@ mod quasar_transfer_hook_switch { // --------------------------------------------------------------------------- #[derive(Accounts)] -pub struct ConfigureAdmin<'info> { +pub struct ConfigureAdmin { #[account(mut)] - pub admin: &'info Signer, - pub new_admin: &'info UncheckedAccount, + pub admin: Signer, + pub new_admin: UncheckedAccount, #[account(mut)] - pub admin_config: &'info mut UncheckedAccount, - pub system_program: &'info Program, + pub admin_config: UncheckedAccount, + pub system_program: Program, } -#[inline(always)] -pub fn handle_configure_admin(accounts: &ConfigureAdmin) -> Result<(), ProgramError> { - let view = accounts.admin_config.to_account_view(); - let data = view.try_borrow()?; +impl ConfigureAdmin { + #[inline(always)] + pub fn configure_admin(&mut self) -> Result<(), ProgramError> { + let view = self.admin_config.to_account_view(); + let data = view.try_borrow()?; + + // If already initialised, verify caller is the current admin + if data.len() >= 33 && data[32] != 0 { + let admin_address = self.admin.to_account_view().address(); + if &data[0..32] != admin_address.as_ref() { + log("Only the current admin can change the admin"); + return Err(ProgramError::IllegalOwner); + } + } + drop(data); - // If already initialised, verify caller is the current admin - if data.len() >= 33 && data[32] != 0 { - let admin_address = accounts.admin.to_account_view().address(); - if &data[0..32] != admin_address.as_ref() { - log("Only the current admin can change the admin"); - return Err(ProgramError::IllegalOwner); + // Create or reuse admin_config PDA + let (admin_config_pda, bump) = + Address::find_program_address(&[b"admin-config"], &crate::ID); + if self.admin_config.to_account_view().address() != &admin_config_pda { + return Err(ProgramError::InvalidSeeds); } - } - drop(data); - // Create or reuse admin_config PDA - let (admin_config_pda, bump) = - Address::find_program_address(&[b"admin-config"], &crate::ID); - if accounts.admin_config.to_account_view().address() != &admin_config_pda { - return Err(ProgramError::InvalidSeeds); - } + // If account doesn't exist, create it + if self.admin_config.to_account_view().data_len() == 0 { + let size: u64 = 33; // 32 admin + 1 flag + let lamports = Rent::get()?.try_minimum_balance(size as usize)?; + let bump_bytes = [bump]; + let seeds = [ + Seed::from(b"admin-config" as &[u8]), + Seed::from(&bump_bytes as &[u8]), + ]; + self.system_program + .create_account(&self.admin, &*self.admin_config, lamports, size, &crate::ID) + .invoke_signed(&seeds)?; + } - // If account doesn't exist, create it - if accounts.admin_config.to_account_view().data_len() == 0 { - let size: u64 = 33; // 32 admin + 1 flag - let lamports = Rent::get()?.try_minimum_balance(size as usize)?; - let bump_bytes = [bump]; - let seeds = [ - Seed::from(b"admin-config" as &[u8]), - Seed::from(&bump_bytes as &[u8]), - ]; - accounts.system_program - .create_account(accounts.admin, &*accounts.admin_config, lamports, size, &crate::ID) - .invoke_signed(&seeds)?; + // Write new admin + let mview = unsafe { + &mut *(self.admin_config as *const UncheckedAccount as *mut UncheckedAccount + as *mut AccountView) + }; + let mut data = mview.try_borrow_mut()?; + let new_admin_address = self.new_admin.to_account_view().address(); + data[0..32].copy_from_slice(new_admin_address.as_ref()); + data[32] = 1; // is_initialised + + log("Admin configured"); + Ok(()) } - - // Write new admin - let mview = unsafe { - &mut *(accounts.admin_config as *const UncheckedAccount as *mut UncheckedAccount - as *mut AccountView) - }; - let mut data = mview.try_borrow_mut()?; - let new_admin_address = accounts.new_admin.to_account_view().address(); - data[0..32].copy_from_slice(new_admin_address.as_ref()); - data[32] = 1; // is_initialised - - log("Admin configured"); - Ok(()) } // --------------------------------------------------------------------------- @@ -125,62 +127,64 @@ pub fn handle_configure_admin(accounts: &ConfigureAdmin) -> Result<(), ProgramEr // --------------------------------------------------------------------------- #[derive(Accounts)] -pub struct InitializeExtraAccountMetas<'info> { +pub struct InitializeExtraAccountMetas { #[account(mut)] - pub payer: &'info Signer, - pub token_mint: &'info UncheckedAccount, + pub payer: Signer, + pub token_mint: UncheckedAccount, #[account(mut)] - pub extra_account_metas_list: &'info mut UncheckedAccount, - pub system_program: &'info Program, + pub extra_account_metas_list: UncheckedAccount, + pub system_program: Program, } -#[inline(always)] -pub fn handle_initialize(accounts: &InitializeExtraAccountMetas) -> Result<(), ProgramError> { - // 1 extra account: wallet switch PDA seeded by [AccountKey(index=3)] (sender/owner) - let meta_list_size: u64 = 51; // 8 + 4 + 4 + 35 - let lamports = Rent::get()?.try_minimum_balance(meta_list_size as usize)?; - - let mint_address = accounts.token_mint.to_account_view().address(); - let (expected_pda, bump) = Address::find_program_address( - &[b"extra-account-metas", mint_address.as_ref()], - &crate::ID, - ); - if accounts.extra_account_metas_list.to_account_view().address() != &expected_pda { - return Err(ProgramError::InvalidSeeds); - } +impl InitializeExtraAccountMetas { + #[inline(always)] + pub fn initialize_extra_account_metas_list(&mut self) -> Result<(), ProgramError> { + // 1 extra account: wallet switch PDA seeded by [AccountKey(index=3)] (sender/owner) + let meta_list_size: u64 = 51; // 8 + 4 + 4 + 35 + let lamports = Rent::get()?.try_minimum_balance(meta_list_size as usize)?; + + let mint_address = self.token_mint.to_account_view().address(); + let (expected_pda, bump) = Address::find_program_address( + &[b"extra-account-metas", mint_address.as_ref()], + &crate::ID, + ); + if self.extra_account_metas_list.to_account_view().address() != &expected_pda { + return Err(ProgramError::InvalidSeeds); + } - let bump_bytes = [bump]; - let seeds = [ - Seed::from(b"extra-account-metas" as &[u8]), - Seed::from(mint_address.as_ref()), - Seed::from(&bump_bytes as &[u8]), - ]; - - accounts.system_program - .create_account(accounts.payer, &*accounts.extra_account_metas_list, lamports, meta_list_size, &crate::ID) - .invoke_signed(&seeds)?; - - let view = unsafe { - &mut *(accounts.extra_account_metas_list as *const UncheckedAccount - as *mut UncheckedAccount as *mut AccountView) - }; - let mut data = view.try_borrow_mut()?; - data[0..8].copy_from_slice(&EXECUTE_DISCRIMINATOR); - data[8..12].copy_from_slice(&39u32.to_le_bytes()); - data[12..16].copy_from_slice(&1u32.to_le_bytes()); - - // ExtraAccountMeta: PDA seeded by [AccountKey(index=3)] — the sender/owner - data[16] = 1; // PDA from seeds - let mut config = [0u8; 32]; - config[0] = 1; // 1 seed - config[1] = 2; // seed type: account key - config[2] = 3; // account index 3 (owner/sender) - data[17..49].copy_from_slice(&config); - data[49] = 0; // not signer - data[50] = 0; // not writable (just reading switch state) - - log("Extra account metas list initialized"); - Ok(()) + let bump_bytes = [bump]; + let seeds = [ + Seed::from(b"extra-account-metas" as &[u8]), + Seed::from(mint_address.as_ref()), + Seed::from(&bump_bytes as &[u8]), + ]; + + self.system_program + .create_account(&self.payer, &*self.extra_account_metas_list, lamports, meta_list_size, &crate::ID) + .invoke_signed(&seeds)?; + + let view = unsafe { + &mut *(self.extra_account_metas_list as *const UncheckedAccount + as *mut UncheckedAccount as *mut AccountView) + }; + let mut data = view.try_borrow_mut()?; + data[0..8].copy_from_slice(&EXECUTE_DISCRIMINATOR); + data[8..12].copy_from_slice(&39u32.to_le_bytes()); + data[12..16].copy_from_slice(&1u32.to_le_bytes()); + + // ExtraAccountMeta: PDA seeded by [AccountKey(index=3)] — the sender/owner + data[16] = 1; // PDA from seeds + let mut config = [0u8; 32]; + config[0] = 1; // 1 seed + config[1] = 2; // seed type: account key + config[2] = 3; // account index 3 (owner/sender) + data[17..49].copy_from_slice(&config); + data[49] = 0; // not signer + data[50] = 0; // not writable (just reading switch state) + + log("Extra account metas list initialized"); + Ok(()) + } } // --------------------------------------------------------------------------- @@ -188,62 +192,64 @@ pub fn handle_initialize(accounts: &InitializeExtraAccountMetas) -> Result<(), P // --------------------------------------------------------------------------- #[derive(Accounts)] -pub struct Switch<'info> { +pub struct Switch { #[account(mut)] - pub admin: &'info Signer, - pub wallet: &'info UncheckedAccount, - pub admin_config: &'info UncheckedAccount, + pub admin: Signer, + pub wallet: UncheckedAccount, + pub admin_config: UncheckedAccount, #[account(mut)] - pub wallet_switch: &'info mut UncheckedAccount, - pub system_program: &'info Program, + pub wallet_switch: UncheckedAccount, + pub system_program: Program, } -#[inline(always)] -pub fn handle_switch(accounts: &Switch, on: bool) -> Result<(), ProgramError> { - // Verify admin - let config_view = accounts.admin_config.to_account_view(); - let config_data = config_view.try_borrow()?; - if config_data.len() < 33 || config_data[32] == 0 { - return Err(ProgramError::UninitializedAccount); - } - let admin_address = accounts.admin.to_account_view().address(); - if &config_data[0..32] != admin_address.as_ref() { - log("Only admin can switch"); - return Err(ProgramError::IllegalOwner); - } - drop(config_data); - - // Create wallet switch PDA if needed - let wallet_address = accounts.wallet.to_account_view().address(); - let (switch_pda, switch_bump) = - Address::find_program_address(&[wallet_address.as_ref()], &crate::ID); - if accounts.wallet_switch.to_account_view().address() != &switch_pda { - return Err(ProgramError::InvalidSeeds); - } +impl Switch { + #[inline(always)] + pub fn switch(&mut self, on: bool) -> Result<(), ProgramError> { + // Verify admin + let config_view = self.admin_config.to_account_view(); + let config_data = config_view.try_borrow()?; + if config_data.len() < 33 || config_data[32] == 0 { + return Err(ProgramError::UninitializedAccount); + } + let admin_address = self.admin.to_account_view().address(); + if &config_data[0..32] != admin_address.as_ref() { + log("Only admin can switch"); + return Err(ProgramError::IllegalOwner); + } + drop(config_data); + + // Create wallet switch PDA if needed + let wallet_address = self.wallet.to_account_view().address(); + let (switch_pda, switch_bump) = + Address::find_program_address(&[wallet_address.as_ref()], &crate::ID); + if self.wallet_switch.to_account_view().address() != &switch_pda { + return Err(ProgramError::InvalidSeeds); + } - if accounts.wallet_switch.to_account_view().data_len() == 0 { - let size: u64 = 33; // 32 wallet + 1 on - let lamports = Rent::get()?.try_minimum_balance(size as usize)?; - let switch_bump_bytes = [switch_bump]; - let switch_seeds = [ - Seed::from(wallet_address.as_ref()), - Seed::from(&switch_bump_bytes as &[u8]), - ]; - accounts.system_program - .create_account(accounts.admin, &*accounts.wallet_switch, lamports, size, &crate::ID) - .invoke_signed(&switch_seeds)?; - } + if self.wallet_switch.to_account_view().data_len() == 0 { + let size: u64 = 33; // 32 wallet + 1 on + let lamports = Rent::get()?.try_minimum_balance(size as usize)?; + let switch_bump_bytes = [switch_bump]; + let switch_seeds = [ + Seed::from(wallet_address.as_ref()), + Seed::from(&switch_bump_bytes as &[u8]), + ]; + self.system_program + .create_account(&self.admin, &*self.wallet_switch, lamports, size, &crate::ID) + .invoke_signed(&switch_seeds)?; + } - let mview = unsafe { - &mut *(accounts.wallet_switch as *const UncheckedAccount as *mut UncheckedAccount - as *mut AccountView) - }; - let mut data = mview.try_borrow_mut()?; - data[0..32].copy_from_slice(wallet_address.as_ref()); - data[32] = if on { 1 } else { 0 }; + let mview = unsafe { + &mut *(self.wallet_switch as *const UncheckedAccount as *mut UncheckedAccount + as *mut AccountView) + }; + let mut data = mview.try_borrow_mut()?; + data[0..32].copy_from_slice(wallet_address.as_ref()); + data[32] = if on { 1 } else { 0 }; - log("Switch toggled"); - Ok(()) + log("Switch toggled"); + Ok(()) + } } // --------------------------------------------------------------------------- @@ -251,31 +257,33 @@ pub fn handle_switch(accounts: &Switch, on: bool) -> Result<(), ProgramError> { // --------------------------------------------------------------------------- #[derive(Accounts)] -pub struct TransferHook<'info> { - pub source_token_account: &'info UncheckedAccount, - pub token_mint: &'info UncheckedAccount, - pub receiver_token_account: &'info UncheckedAccount, - pub wallet: &'info UncheckedAccount, - pub extra_account_metas_list: &'info UncheckedAccount, +pub struct TransferHook { + pub source_token_account: UncheckedAccount, + pub token_mint: UncheckedAccount, + pub receiver_token_account: UncheckedAccount, + pub wallet: UncheckedAccount, + pub extra_account_metas_list: UncheckedAccount, /// Wallet switch PDA resolved by Token-2022 - pub wallet_switch: &'info UncheckedAccount, + pub wallet_switch: UncheckedAccount, } -#[inline(always)] -pub fn handle_transfer_hook(accounts: &TransferHook) -> Result<(), ProgramError> { - let switch_view = accounts.wallet_switch.to_account_view(); - let data = switch_view.try_borrow()?; +impl TransferHook { + #[inline(always)] + pub fn transfer_hook(&mut self) -> Result<(), ProgramError> { + let switch_view = self.wallet_switch.to_account_view(); + let data = switch_view.try_borrow()?; - if data.len() < 33 { - log("Switch not initialized — transfers disabled by default"); - return Err(ProgramError::UninitializedAccount); - } + if data.len() < 33 { + log("Switch not initialized — transfers disabled by default"); + return Err(ProgramError::UninitializedAccount); + } - if data[32] != 1 { - log("Transfer switch is OFF"); - return Err(ProgramError::InvalidArgument); - } + if data[32] != 1 { + log("Transfer switch is OFF"); + return Err(ProgramError::InvalidArgument); + } - log("Transfer switch is ON — transfer allowed"); - Ok(()) + log("Transfer switch is ON — transfer allowed"); + Ok(()) + } } diff --git a/tokens/token-extensions/transfer-hook/whitelist/quasar/Cargo.toml b/tokens/token-extensions/transfer-hook/whitelist/quasar/Cargo.toml index d733505f5..3d77ee491 100644 --- a/tokens/token-extensions/transfer-hook/whitelist/quasar/Cargo.toml +++ b/tokens/token-extensions/transfer-hook/whitelist/quasar/Cargo.toml @@ -18,8 +18,8 @@ client = [] debug = [] [dependencies] -quasar-lang = "0.0" -quasar-spl = "0.0" +quasar-lang = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } +quasar-spl = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } solana-instruction = { version = "3.2.0" } [dev-dependencies] diff --git a/tokens/token-extensions/transfer-hook/whitelist/quasar/src/lib.rs b/tokens/token-extensions/transfer-hook/whitelist/quasar/src/lib.rs index f58370958..1d696efb9 100644 --- a/tokens/token-extensions/transfer-hook/whitelist/quasar/src/lib.rs +++ b/tokens/token-extensions/transfer-hook/whitelist/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![cfg_attr(not(test), no_std)] +#![no_std] use quasar_lang::sysvars::Sysvar; use quasar_lang::{ diff --git a/tokens/token-fundraiser/quasar/Cargo.toml b/tokens/token-fundraiser/quasar/Cargo.toml index 301b33bd3..c6b52f936 100644 --- a/tokens/token-fundraiser/quasar/Cargo.toml +++ b/tokens/token-fundraiser/quasar/Cargo.toml @@ -21,8 +21,8 @@ client = [] debug = [] [dependencies] -quasar-lang = "0.0" -quasar-spl = "0.0" +quasar-lang = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } +quasar-spl = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } solana-instruction = { version = "3.2.0" } [dev-dependencies] diff --git a/tokens/token-fundraiser/quasar/src/instructions/check_contributions.rs b/tokens/token-fundraiser/quasar/src/instructions/check_contributions.rs index d315a0dad..f975bef48 100644 --- a/tokens/token-fundraiser/quasar/src/instructions/check_contributions.rs +++ b/tokens/token-fundraiser/quasar/src/instructions/check_contributions.rs @@ -5,50 +5,46 @@ use { }; #[derive(Accounts)] -pub struct CheckContributions<'info> { +pub struct CheckContributions { #[account(mut)] - pub maker: &'info Signer, + pub maker: Signer, #[account( mut, has_one = maker, close = maker, - seeds = [b"fundraiser", maker], + seeds = Fundraiser::seeds(maker), bump = fundraiser.bump )] - pub fundraiser: &'info mut Account, + pub fundraiser: Account, #[account(mut)] - pub vault: &'info mut Account, + pub vault: Account, #[account(mut)] - pub maker_ta: &'info mut Account, - pub token_program: &'info Program, + pub maker_ta: Account, + pub token_program: Program, } -#[inline(always)] -pub fn handle_check_contributions(accounts: &mut CheckContributions, fundraiser_bump: u8) -> Result<(), ProgramError> { - // Verify the target was met - require!( - accounts.fundraiser.current_amount >= accounts.fundraiser.amount_to_raise, - ProgramError::Custom(0) // TargetNotMet - ); +impl CheckContributions { + #[inline(always)] + pub fn check_contributions(&mut self, bumps: &CheckContributionsBumps) -> Result<(), ProgramError> { + // Verify the target was met + require!( + self.fundraiser.current_amount >= self.fundraiser.amount_to_raise, + ProgramError::Custom(0) // TargetNotMet + ); - let maker_key = accounts.fundraiser.maker; - let bump = [fundraiser_bump]; - let seeds: &[Seed] = &[ - Seed::from(b"fundraiser" as &[u8]), - Seed::from(maker_key.as_ref()), - Seed::from(&bump as &[u8]), - ]; + let seeds = self.fundraiser_seeds(bumps); - // Transfer all vault funds to the maker - let vault_amount = accounts.vault.amount(); - accounts.token_program - .transfer(accounts.vault, accounts.maker_ta, accounts.fundraiser, vault_amount) - .invoke_signed(seeds)?; + // Transfer all vault funds to the maker + let vault_amount = self.vault.amount(); + self.token_program + .transfer(&self.vault, &self.maker_ta, &self.fundraiser, vault_amount) + .invoke_signed(&seeds)?; - // Close the vault token account - accounts.token_program - .close_account(accounts.vault, accounts.maker, accounts.fundraiser) - .invoke_signed(seeds)?; + // Close the vault token account + self.token_program + .close_account(&self.vault, &self.maker, &self.fundraiser) + .invoke_signed(&seeds)?; - Ok(()) + Ok(()) + } } diff --git a/tokens/token-fundraiser/quasar/src/instructions/contribute.rs b/tokens/token-fundraiser/quasar/src/instructions/contribute.rs index a0470b277..7bbc5fa2c 100644 --- a/tokens/token-fundraiser/quasar/src/instructions/contribute.rs +++ b/tokens/token-fundraiser/quasar/src/instructions/contribute.rs @@ -5,36 +5,38 @@ use { }; #[derive(Accounts)] -pub struct Contribute<'info> { +pub struct Contribute { #[account(mut)] - pub contributor: &'info Signer, + pub contributor: Signer, #[account(mut)] - pub fundraiser: &'info mut Account, + pub fundraiser: Account, #[account(mut)] - pub contributor_account: &'info mut Account, + pub contributor_account: Account, #[account(mut)] - pub contributor_ta: &'info mut Account, + pub contributor_ta: Account, #[account(mut)] - pub vault: &'info mut Account, - pub token_program: &'info Program, + pub vault: Account, + pub token_program: Program, } -#[inline(always)] -pub fn handle_contribute(accounts: &mut Contribute, amount: u64) -> Result<(), ProgramError> { - require!(amount > 0, ProgramError::InvalidArgument); +impl Contribute { + #[inline(always)] + pub fn contribute(&mut self, amount: u64) -> Result<(), ProgramError> { + require!(amount > 0, ProgramError::InvalidArgument); - // Transfer tokens from contributor to vault - accounts.token_program - .transfer(accounts.contributor_ta, accounts.vault, accounts.contributor, amount) - .invoke()?; + // Transfer tokens from contributor to vault + self.token_program + .transfer(&self.contributor_ta, &self.vault, &self.contributor, amount) + .invoke()?; - // Update fundraiser state - accounts.fundraiser.current_amount = accounts.fundraiser.current_amount.checked_add(amount) - .ok_or(ProgramError::ArithmeticOverflow)?; + // Update fundraiser state + self.fundraiser.current_amount = self.fundraiser.current_amount.checked_add(amount) + .ok_or(ProgramError::ArithmeticOverflow)?; - // Update contributor tracking - accounts.contributor_account.amount = accounts.contributor_account.amount.checked_add(amount) - .ok_or(ProgramError::ArithmeticOverflow)?; + // Update contributor tracking + self.contributor_account.amount = self.contributor_account.amount.checked_add(amount) + .ok_or(ProgramError::ArithmeticOverflow)?; - Ok(()) + Ok(()) + } } diff --git a/tokens/token-fundraiser/quasar/src/instructions/initialize.rs b/tokens/token-fundraiser/quasar/src/instructions/initialize.rs index 3f8ee0df6..161211050 100644 --- a/tokens/token-fundraiser/quasar/src/instructions/initialize.rs +++ b/tokens/token-fundraiser/quasar/src/instructions/initialize.rs @@ -5,36 +5,39 @@ use { }; #[derive(Accounts)] -pub struct Initialize<'info> { +pub struct Initialize { #[account(mut)] - pub maker: &'info Signer, - pub mint_to_raise: &'info Account, - #[account(mut, init, payer = maker, seeds = [b"fundraiser", maker], bump)] - pub fundraiser: &'info mut Account, + pub maker: Signer, + pub mint_to_raise: Account, + #[account(mut, init, payer = maker, seeds = Fundraiser::seeds(maker), bump)] + pub fundraiser: Account, #[account(mut, init_if_needed, payer = maker, token::mint = mint_to_raise, token::authority = fundraiser)] - pub vault: &'info mut Account, - pub rent: &'info Sysvar, - pub token_program: &'info Program, - pub system_program: &'info Program, + pub vault: Account, + pub rent: Sysvar, + pub token_program: Program, + pub system_program: Program, } -#[inline(always)] -pub fn handle_initialize( - accounts: &mut Initialize, amount_to_raise: u64, - duration: u16, - bump: u8, -) -> Result<(), ProgramError> { - // Validate minimum raise amount - require!(amount_to_raise > 0, ProgramError::InvalidArgument); +impl Initialize { + #[inline(always)] + pub fn initialize( + &mut self, + amount_to_raise: u64, + duration: u16, + bump: u8, + ) -> Result<(), ProgramError> { + // Validate minimum raise amount + require!(amount_to_raise > 0, ProgramError::InvalidArgument); - accounts.fundraiser.set_inner( - *accounts.maker.address(), - *accounts.mint_to_raise.address(), - amount_to_raise, - 0, // current_amount starts at 0 - 0, // time_started — would be Clock::get() onchain - duration, - bump, - ); - Ok(()) + self.fundraiser.set_inner( + *self.maker.address(), + *self.mint_to_raise.address(), + amount_to_raise, + 0, // current_amount starts at 0 + 0, // time_started — would be Clock::get() onchain + duration, + bump, + ); + Ok(()) + } } diff --git a/tokens/token-fundraiser/quasar/src/instructions/refund.rs b/tokens/token-fundraiser/quasar/src/instructions/refund.rs index 135cf7dc1..6e3a4de56 100644 --- a/tokens/token-fundraiser/quasar/src/instructions/refund.rs +++ b/tokens/token-fundraiser/quasar/src/instructions/refund.rs @@ -5,50 +5,46 @@ use { }; #[derive(Accounts)] -pub struct Refund<'info> { +pub struct Refund { #[account(mut)] - pub contributor: &'info Signer, - pub maker: &'info UncheckedAccount, + pub contributor: Signer, + pub maker: UncheckedAccount, #[account( mut, has_one = maker, - seeds = [b"fundraiser", maker], + seeds = Fundraiser::seeds(maker), bump = fundraiser.bump )] - pub fundraiser: &'info mut Account, + pub fundraiser: Account, #[account(mut)] - pub contributor_account: &'info mut Account, + pub contributor_account: Account, #[account(mut)] - pub contributor_ta: &'info mut Account, + pub contributor_ta: Account, #[account(mut)] - pub vault: &'info mut Account, - pub token_program: &'info Program, + pub vault: Account, + pub token_program: Program, } -#[inline(always)] -pub fn handle_refund(accounts: &mut Refund, fundraiser_bump: u8) -> Result<(), ProgramError> { - let refund_amount = accounts.contributor_account.amount; +impl Refund { + #[inline(always)] + pub fn refund(&mut self, bumps: &RefundBumps) -> Result<(), ProgramError> { + let refund_amount = self.contributor_account.amount; - let maker_key = accounts.fundraiser.maker; - let bump = [fundraiser_bump]; - let seeds: &[Seed] = &[ - Seed::from(b"fundraiser" as &[u8]), - Seed::from(maker_key.as_ref()), - Seed::from(&bump as &[u8]), - ]; + let seeds = self.fundraiser_seeds(bumps); - // Transfer contributor's tokens back from vault - accounts.token_program - .transfer(accounts.vault, accounts.contributor_ta, accounts.fundraiser, refund_amount) - .invoke_signed(seeds)?; + // Transfer contributor's tokens back from vault + self.token_program + .transfer(&self.vault, &self.contributor_ta, &self.fundraiser, refund_amount) + .invoke_signed(&seeds)?; - // Update fundraiser state - accounts.fundraiser.current_amount = accounts.fundraiser.current_amount - .checked_sub(refund_amount) - .ok_or(ProgramError::ArithmeticOverflow)?; + // Update fundraiser state + self.fundraiser.current_amount = self.fundraiser.current_amount + .checked_sub(refund_amount) + .ok_or(ProgramError::ArithmeticOverflow)?; - // Zero out contributor amount - accounts.contributor_account.set_inner(0); + // Zero out contributor amount + self.contributor_account.set_inner(0); - Ok(()) + Ok(()) + } } diff --git a/tokens/token-fundraiser/quasar/src/lib.rs b/tokens/token-fundraiser/quasar/src/lib.rs index 7ec183a48..00349cce0 100644 --- a/tokens/token-fundraiser/quasar/src/lib.rs +++ b/tokens/token-fundraiser/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![cfg_attr(not(test), no_std)] +#![no_std] use quasar_lang::prelude::*; @@ -24,24 +24,24 @@ mod quasar_token_fundraiser { amount_to_raise: u64, duration: u16, ) -> Result<(), ProgramError> { - instructions::handle_initialize(&mut ctx.accounts, amount_to_raise, duration, ctx.bumps.fundraiser) + ctx.accounts.initialize(amount_to_raise, duration, ctx.bumps.fundraiser) } /// Contribute tokens to the fundraiser. #[instruction(discriminator = 1)] pub fn contribute(ctx: Ctx, amount: u64) -> Result<(), ProgramError> { - instructions::handle_contribute(&mut ctx.accounts, amount) + ctx.accounts.contribute(amount) } /// Maker withdraws all funds once the target is met. #[instruction(discriminator = 2)] pub fn check_contributions(ctx: Ctx) -> Result<(), ProgramError> { - instructions::handle_check_contributions(&mut ctx.accounts, ctx.bumps.fundraiser) + ctx.accounts.check_contributions(&ctx.bumps) } /// Contributors reclaim their tokens if the fundraiser fails. #[instruction(discriminator = 3)] pub fn refund(ctx: Ctx) -> Result<(), ProgramError> { - instructions::handle_refund(&mut ctx.accounts, ctx.bumps.fundraiser) + ctx.accounts.refund(&ctx.bumps) } } diff --git a/tokens/token-fundraiser/quasar/src/state.rs b/tokens/token-fundraiser/quasar/src/state.rs index a90abfa36..9db1768d9 100644 --- a/tokens/token-fundraiser/quasar/src/state.rs +++ b/tokens/token-fundraiser/quasar/src/state.rs @@ -1,7 +1,8 @@ use quasar_lang::prelude::*; /// State for the fundraiser: records the maker, target mint, amounts, and timing. -#[account(discriminator = 1)] +#[account(discriminator = 1, set_inner)] +#[seeds(b"fundraiser", maker: Address)] pub struct Fundraiser { pub maker: Address, pub mint_to_raise: Address, diff --git a/tokens/token-swap/quasar/Cargo.toml b/tokens/token-swap/quasar/Cargo.toml index d4a759f1f..8286c3ec6 100644 --- a/tokens/token-swap/quasar/Cargo.toml +++ b/tokens/token-swap/quasar/Cargo.toml @@ -22,8 +22,8 @@ client = [] debug = [] [dependencies] -quasar-lang = "0.0" -quasar-spl = "0.0" +quasar-lang = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } +quasar-spl = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } solana-instruction = { version = "3.2.0" } [dev-dependencies] diff --git a/tokens/token-swap/quasar/src/instructions/create_amm.rs b/tokens/token-swap/quasar/src/instructions/create_amm.rs index 2807c04b2..ffaf1ad87 100644 --- a/tokens/token-swap/quasar/src/instructions/create_amm.rs +++ b/tokens/token-swap/quasar/src/instructions/create_amm.rs @@ -6,21 +6,23 @@ use {crate::state::Amm, quasar_lang::prelude::*}; /// In Quasar, we use a simpler fixed seed `["amm"]` since the Quasar derive /// macro seeds reference account addresses, not instruction data. #[derive(Accounts)] -pub struct CreateAmm<'info> { +pub struct CreateAmm { #[account(mut, init, payer = payer, seeds = [b"amm"], bump)] - pub amm: &'info mut Account, + pub amm: Account, /// Admin authority for the AMM. - pub admin: &'info UncheckedAccount, + pub admin: UncheckedAccount, #[account(mut)] - pub payer: &'info Signer, - pub system_program: &'info Program, + pub payer: Signer, + pub system_program: Program, } -#[inline(always)] -pub fn handle_create_amm(accounts: &mut CreateAmm, id: Address, fee: u16) -> Result<(), ProgramError> { - if fee >= 10000 { - return Err(ProgramError::InvalidArgument); +impl CreateAmm { + #[inline(always)] + pub fn create_amm(&mut self, id: Address, fee: u16) -> Result<(), ProgramError> { + if fee >= 10000 { + return Err(ProgramError::InvalidArgument); + } + self.amm.set_inner(id, *self.admin.address(), fee); + Ok(()) } - accounts.amm.set_inner(id, *accounts.admin.address(), fee); - Ok(()) } diff --git a/tokens/token-swap/quasar/src/instructions/create_pool.rs b/tokens/token-swap/quasar/src/instructions/create_pool.rs index a25e4acb2..252b11d55 100644 --- a/tokens/token-swap/quasar/src/instructions/create_pool.rs +++ b/tokens/token-swap/quasar/src/instructions/create_pool.rs @@ -10,14 +10,14 @@ use { /// pool_authority = [amm, mint_a, mint_b, "authority"], /// mint_liquidity = [amm, mint_a, mint_b, "liquidity"]. #[derive(Accounts)] -pub struct CreatePool<'info> { +pub struct CreatePool { #[account(seeds = [b"amm"], bump)] - pub amm: &'info Account, + pub amm: Account, #[account(mut, init, payer = payer, seeds = [amm, mint_a, mint_b], bump)] - pub pool: &'info mut Account, + pub pool: Account, /// Pool authority PDA — signs for pool token operations. #[account(seeds = [amm, mint_a, mint_b, crate::AUTHORITY_SEED], bump)] - pub pool_authority: &'info UncheckedAccount, + pub pool_authority: UncheckedAccount, /// Liquidity token mint — created at a PDA. #[account( mut, @@ -28,26 +28,28 @@ pub struct CreatePool<'info> { mint::decimals = 6, mint::authority = pool_authority, )] - pub mint_liquidity: &'info mut Account, - pub mint_a: &'info Account, - pub mint_b: &'info Account, + pub mint_liquidity: Account, + pub mint_a: Account, + pub mint_b: Account, /// Pool's token A account. #[account(mut, init_if_needed, payer = payer, token::mint = mint_a, token::authority = pool_authority)] - pub pool_account_a: &'info mut Account, + pub pool_account_a: Account, /// Pool's token B account. #[account(mut, init_if_needed, payer = payer, token::mint = mint_b, token::authority = pool_authority)] - pub pool_account_b: &'info mut Account, + pub pool_account_b: Account, #[account(mut)] - pub payer: &'info Signer, - pub token_program: &'info Program, - pub system_program: &'info Program, - pub rent: &'info Sysvar, + pub payer: Signer, + pub token_program: Program, + pub system_program: Program, + pub rent: Sysvar, } -#[inline(always)] -pub fn handle_create_pool(accounts: &mut CreatePool) -> Result<(), ProgramError> { - accounts.pool.amm = *accounts.amm.address(); - accounts.pool.mint_a = *accounts.mint_a.address(); - accounts.pool.mint_b = *accounts.mint_b.address(); - Ok(()) +impl CreatePool { + #[inline(always)] + pub fn create_pool(&mut self) -> Result<(), ProgramError> { + self.pool.amm = *self.amm.address(); + self.pool.mint_a = *self.mint_a.address(); + self.pool.mint_b = *self.mint_b.address(); + Ok(()) + } } diff --git a/tokens/token-swap/quasar/src/instructions/deposit_liquidity.rs b/tokens/token-swap/quasar/src/instructions/deposit_liquidity.rs index 79d7aa835..f12c9e580 100644 --- a/tokens/token-swap/quasar/src/instructions/deposit_liquidity.rs +++ b/tokens/token-swap/quasar/src/instructions/deposit_liquidity.rs @@ -9,39 +9,39 @@ use { /// Seeds reference the amm, mint_a, and mint_b account addresses — these /// must be provided as separate account inputs. #[derive(Accounts)] -pub struct DepositLiquidity<'info> { +pub struct DepositLiquidity { #[account(seeds = [b"amm"], bump)] - pub amm: &'info Account, + pub amm: Account, #[account(seeds = [amm, mint_a, mint_b], bump)] - pub pool: &'info Account, + pub pool: Account, /// Pool authority PDA. #[account(seeds = [amm, mint_a, mint_b, crate::AUTHORITY_SEED], bump)] - pub pool_authority: &'info UncheckedAccount, + pub pool_authority: UncheckedAccount, /// Depositor (must be signer to authorise transfers). - pub depositor: &'info Signer, + pub depositor: Signer, #[account(mut, seeds = [amm, mint_a, mint_b, crate::LIQUIDITY_SEED], bump)] - pub mint_liquidity: &'info mut Account, - pub mint_a: &'info Account, - pub mint_b: &'info Account, + pub mint_liquidity: Account, + pub mint_a: Account, + pub mint_b: Account, /// Pool's token A vault. #[account(mut)] - pub pool_account_a: &'info mut Account, + pub pool_account_a: Account, /// Pool's token B vault. #[account(mut)] - pub pool_account_b: &'info mut Account, + pub pool_account_b: Account, /// Depositor's LP token account. #[account(mut, init_if_needed, payer = payer, token::mint = mint_liquidity, token::authority = depositor)] - pub depositor_account_liquidity: &'info mut Account, + pub depositor_account_liquidity: Account, /// Depositor's token A account. #[account(mut)] - pub depositor_account_a: &'info mut Account, + pub depositor_account_a: Account, /// Depositor's token B account. #[account(mut)] - pub depositor_account_b: &'info mut Account, + pub depositor_account_b: Account, #[account(mut)] - pub payer: &'info Signer, - pub token_program: &'info Program, - pub system_program: &'info Program, + pub payer: Signer, + pub token_program: Program, + pub system_program: Program, } /// Integer square root via Newton's method. @@ -58,81 +58,84 @@ fn isqrt(n: u128) -> u64 { x as u64 } -#[inline(always)] -pub fn handle_deposit_liquidity( - accounts: &mut DepositLiquidity, amount_a: u64, - amount_b: u64, - bumps: &DepositLiquidityBumps, -) -> Result<(), ProgramError> { - // Clamp to what the depositor actually has. - let depositor_a = accounts.depositor_account_a.amount(); - let depositor_b = accounts.depositor_account_b.amount(); - let mut amount_a = if amount_a > depositor_a { depositor_a } else { amount_a }; - let mut amount_b = if amount_b > depositor_b { depositor_b } else { amount_b }; +impl DepositLiquidity { + #[inline(always)] + pub fn deposit_liquidity( + &mut self, + amount_a: u64, + amount_b: u64, + bumps: &DepositLiquidityBumps, + ) -> Result<(), ProgramError> { + // Clamp to what the depositor actually has. + let depositor_a = self.depositor_account_a.amount(); + let depositor_b = self.depositor_account_b.amount(); + let mut amount_a = if amount_a > depositor_a { depositor_a } else { amount_a }; + let mut amount_b = if amount_b > depositor_b { depositor_b } else { amount_b }; - let pool_a_amount = accounts.pool_account_a.amount(); - let pool_b_amount = accounts.pool_account_b.amount(); - let pool_creation = pool_a_amount == 0 && pool_b_amount == 0; + let pool_a_amount = self.pool_account_a.amount(); + let pool_b_amount = self.pool_account_b.amount(); + let pool_creation = pool_a_amount == 0 && pool_b_amount == 0; - if !pool_creation { - // Adjust amounts to maintain the pool ratio. - if pool_a_amount > pool_b_amount { - amount_a = (amount_b as u128) - .checked_mul(pool_a_amount as u128) - .ok_or(ProgramError::ArithmeticOverflow)? - .checked_div(pool_b_amount as u128) - .ok_or(ProgramError::ArithmeticOverflow)? as u64; - } else { - amount_b = (amount_a as u128) - .checked_mul(pool_b_amount as u128) - .ok_or(ProgramError::ArithmeticOverflow)? - .checked_div(pool_a_amount as u128) - .ok_or(ProgramError::ArithmeticOverflow)? as u64; + if !pool_creation { + // Adjust amounts to maintain the pool ratio. + if pool_a_amount > pool_b_amount { + amount_a = (amount_b as u128) + .checked_mul(pool_a_amount as u128) + .ok_or(ProgramError::ArithmeticOverflow)? + .checked_div(pool_b_amount as u128) + .ok_or(ProgramError::ArithmeticOverflow)? as u64; + } else { + amount_b = (amount_a as u128) + .checked_mul(pool_b_amount as u128) + .ok_or(ProgramError::ArithmeticOverflow)? + .checked_div(pool_a_amount as u128) + .ok_or(ProgramError::ArithmeticOverflow)? as u64; + } } - } - // Compute liquidity = sqrt(amount_a * amount_b). - let product = (amount_a as u128) - .checked_mul(amount_b as u128) - .ok_or(ProgramError::ArithmeticOverflow)?; - let mut liquidity = isqrt(product); + // Compute liquidity = sqrt(amount_a * amount_b). + let product = (amount_a as u128) + .checked_mul(amount_b as u128) + .ok_or(ProgramError::ArithmeticOverflow)?; + let mut liquidity = isqrt(product); - // Lock minimum liquidity on first deposit. - if pool_creation { - if liquidity < crate::MINIMUM_LIQUIDITY { - return Err(ProgramError::InsufficientFunds); + // Lock minimum liquidity on first deposit. + if pool_creation { + if liquidity < crate::MINIMUM_LIQUIDITY { + return Err(ProgramError::InsufficientFunds); + } + liquidity -= crate::MINIMUM_LIQUIDITY; } - liquidity -= crate::MINIMUM_LIQUIDITY; - } - // Transfer token A to the pool. - accounts.token_program - .transfer(accounts.depositor_account_a, accounts.pool_account_a, accounts.depositor, amount_a) - .invoke()?; + // Transfer token A to the pool. + self.token_program + .transfer(&self.depositor_account_a, &self.pool_account_a, &self.depositor, amount_a) + .invoke()?; - // Transfer token B to the pool. - accounts.token_program - .transfer(accounts.depositor_account_b, accounts.pool_account_b, accounts.depositor, amount_b) - .invoke()?; + // Transfer token B to the pool. + self.token_program + .transfer(&self.depositor_account_b, &self.pool_account_b, &self.depositor, amount_b) + .invoke()?; - // Mint LP tokens to the depositor (signed by pool authority). - let bump = [bumps.pool_authority]; - let seeds: &[Seed] = &[ - Seed::from(accounts.amm.address().as_ref()), - Seed::from(accounts.mint_a.address().as_ref()), - Seed::from(accounts.mint_b.address().as_ref()), - Seed::from(crate::AUTHORITY_SEED), - Seed::from(&bump as &[u8]), - ]; + // Mint LP tokens to the depositor (signed by pool authority). + let bump = [bumps.pool_authority]; + let seeds: &[Seed] = &[ + Seed::from(self.amm.address().as_ref()), + Seed::from(self.mint_a.address().as_ref()), + Seed::from(self.mint_b.address().as_ref()), + Seed::from(crate::AUTHORITY_SEED), + Seed::from(&bump as &[u8]), + ]; - accounts.token_program - .mint_to( - accounts.mint_liquidity, - accounts.depositor_account_liquidity, - accounts.pool_authority, - liquidity, - ) - .invoke_signed(seeds)?; + self.token_program + .mint_to( + &self.mint_liquidity, + &self.depositor_account_liquidity, + &self.pool_authority, + liquidity, + ) + .invoke_signed(seeds)?; - Ok(()) + Ok(()) + } } diff --git a/tokens/token-swap/quasar/src/instructions/swap_exact_tokens_for_tokens.rs b/tokens/token-swap/quasar/src/instructions/swap_exact_tokens_for_tokens.rs index 330b25725..c783cc72f 100644 --- a/tokens/token-swap/quasar/src/instructions/swap_exact_tokens_for_tokens.rs +++ b/tokens/token-swap/quasar/src/instructions/swap_exact_tokens_for_tokens.rs @@ -6,130 +6,133 @@ use { /// Accounts for swapping tokens using the constant-product formula. #[derive(Accounts)] -pub struct SwapExactTokensForTokens<'info> { +pub struct SwapExactTokensForTokens { #[account(seeds = [b"amm"], bump)] - pub amm: &'info Account, + pub amm: Account, #[account(seeds = [amm, mint_a, mint_b], bump)] - pub pool: &'info Account, + pub pool: Account, /// Pool authority PDA. #[account(seeds = [amm, mint_a, mint_b, crate::AUTHORITY_SEED], bump)] - pub pool_authority: &'info UncheckedAccount, - pub trader: &'info Signer, - pub mint_a: &'info Account, - pub mint_b: &'info Account, + pub pool_authority: UncheckedAccount, + pub trader: Signer, + pub mint_a: Account, + pub mint_b: Account, #[account(mut)] - pub pool_account_a: &'info mut Account, + pub pool_account_a: Account, #[account(mut)] - pub pool_account_b: &'info mut Account, + pub pool_account_b: Account, #[account(mut, init_if_needed, payer = payer, token::mint = mint_a, token::authority = trader)] - pub trader_account_a: &'info mut Account, + pub trader_account_a: Account, #[account(mut, init_if_needed, payer = payer, token::mint = mint_b, token::authority = trader)] - pub trader_account_b: &'info mut Account, + pub trader_account_b: Account, #[account(mut)] - pub payer: &'info Signer, - pub token_program: &'info Program, - pub system_program: &'info Program, + pub payer: Signer, + pub token_program: Program, + pub system_program: Program, } -#[inline(always)] -pub fn handle_swap_exact_tokens_for_tokens( - accounts: &mut SwapExactTokensForTokens, swap_a: bool, - input_amount: u64, - min_output_amount: u64, - bumps: &SwapExactTokensForTokensBumps, -) -> Result<(), ProgramError> { - // Clamp input to what the trader has. - let input = if swap_a { - let trader_a = accounts.trader_account_a.amount(); - if input_amount > trader_a { trader_a } else { input_amount } - } else { - let trader_b = accounts.trader_account_b.amount(); - if input_amount > trader_b { trader_b } else { input_amount } - }; +impl SwapExactTokensForTokens { + #[inline(always)] + pub fn swap_exact_tokens_for_tokens( + &mut self, + swap_a: bool, + input_amount: u64, + min_output_amount: u64, + bumps: &SwapExactTokensForTokensBumps, + ) -> Result<(), ProgramError> { + // Clamp input to what the trader has. + let input = if swap_a { + let trader_a = self.trader_account_a.amount(); + if input_amount > trader_a { trader_a } else { input_amount } + } else { + let trader_b = self.trader_account_b.amount(); + if input_amount > trader_b { trader_b } else { input_amount } + }; - // Apply fee. - let fee = accounts.amm.fee.get() as u64; - let taxed_input = input - input * fee / 10000; + // Apply fee. + let fee = self.amm.fee.get() as u64; + let taxed_input = input - input * fee / 10000; - // Constant-product formula: output = taxed_input * pool_out / (pool_in + taxed_input) - let pool_a = accounts.pool_account_a.amount(); - let pool_b = accounts.pool_account_b.amount(); + // Constant-product formula: output = taxed_input * pool_out / (pool_in + taxed_input) + let pool_a = self.pool_account_a.amount(); + let pool_b = self.pool_account_b.amount(); - let output = if swap_a { - (taxed_input as u128) - .checked_mul(pool_b as u128) - .ok_or(ProgramError::ArithmeticOverflow)? - .checked_div( - (pool_a as u128) - .checked_add(taxed_input as u128) - .ok_or(ProgramError::ArithmeticOverflow)?, - ) - .ok_or(ProgramError::ArithmeticOverflow)? as u64 - } else { - (taxed_input as u128) - .checked_mul(pool_a as u128) - .ok_or(ProgramError::ArithmeticOverflow)? - .checked_div( - (pool_b as u128) - .checked_add(taxed_input as u128) - .ok_or(ProgramError::ArithmeticOverflow)?, - ) - .ok_or(ProgramError::ArithmeticOverflow)? as u64 - }; + let output = if swap_a { + (taxed_input as u128) + .checked_mul(pool_b as u128) + .ok_or(ProgramError::ArithmeticOverflow)? + .checked_div( + (pool_a as u128) + .checked_add(taxed_input as u128) + .ok_or(ProgramError::ArithmeticOverflow)?, + ) + .ok_or(ProgramError::ArithmeticOverflow)? as u64 + } else { + (taxed_input as u128) + .checked_mul(pool_a as u128) + .ok_or(ProgramError::ArithmeticOverflow)? + .checked_div( + (pool_b as u128) + .checked_add(taxed_input as u128) + .ok_or(ProgramError::ArithmeticOverflow)?, + ) + .ok_or(ProgramError::ArithmeticOverflow)? as u64 + }; - if output < min_output_amount { - return Err(ProgramError::Custom(4)); // OutputTooSmall - } + if output < min_output_amount { + return Err(ProgramError::Custom(4)); // OutputTooSmall + } + + // Record invariant before the trade. + let invariant = (pool_a as u128) + .checked_mul(pool_b as u128) + .ok_or(ProgramError::ArithmeticOverflow)?; - // Record invariant before the trade. - let invariant = (pool_a as u128) - .checked_mul(pool_b as u128) - .ok_or(ProgramError::ArithmeticOverflow)?; + // Build authority signer seeds. + let bump = [bumps.pool_authority]; + let seeds: &[Seed] = &[ + Seed::from(self.amm.address().as_ref()), + Seed::from(self.mint_a.address().as_ref()), + Seed::from(self.mint_b.address().as_ref()), + Seed::from(crate::AUTHORITY_SEED), + Seed::from(&bump as &[u8]), + ]; - // Build authority signer seeds. - let bump = [bumps.pool_authority]; - let seeds: &[Seed] = &[ - Seed::from(accounts.amm.address().as_ref()), - Seed::from(accounts.mint_a.address().as_ref()), - Seed::from(accounts.mint_b.address().as_ref()), - Seed::from(crate::AUTHORITY_SEED), - Seed::from(&bump as &[u8]), - ]; + if swap_a { + // Trader sends token A to pool. + self.token_program + .transfer(&self.trader_account_a, &self.pool_account_a, &self.trader, input) + .invoke()?; + // Pool sends token B to trader (signed). + self.token_program + .transfer(&self.pool_account_b, &self.trader_account_b, &self.pool_authority, output) + .invoke_signed(seeds)?; + } else { + // Pool sends token A to trader (signed). + self.token_program + .transfer(&self.pool_account_a, &self.trader_account_a, &self.pool_authority, output) + .invoke_signed(seeds)?; + // Trader sends token B to pool. + self.token_program + .transfer(&self.trader_account_b, &self.pool_account_b, &self.trader, input) + .invoke()?; + } - if swap_a { - // Trader sends token A to pool. - accounts.token_program - .transfer(accounts.trader_account_a, accounts.pool_account_a, accounts.trader, input) - .invoke()?; - // Pool sends token B to trader (signed). - accounts.token_program - .transfer(accounts.pool_account_b, accounts.trader_account_b, accounts.pool_authority, output) - .invoke_signed(seeds)?; - } else { - // Pool sends token A to trader (signed). - accounts.token_program - .transfer(accounts.pool_account_a, accounts.trader_account_a, accounts.pool_authority, output) - .invoke_signed(seeds)?; - // Trader sends token B to pool. - accounts.token_program - .transfer(accounts.trader_account_b, accounts.pool_account_b, accounts.trader, input) - .invoke()?; - } + // Verify invariant holds (new product >= old product). + let new_pool_a = pool_a as u128 + + if swap_a { input as u128 } else { 0 } + - if !swap_a { output as u128 } else { 0 }; + let new_pool_b = pool_b as u128 + + if !swap_a { input as u128 } else { 0 } + - if swap_a { output as u128 } else { 0 }; + let new_invariant = new_pool_a + .checked_mul(new_pool_b) + .ok_or(ProgramError::ArithmeticOverflow)?; - // Verify invariant holds (new product >= old product). - let new_pool_a = pool_a as u128 - + if swap_a { input as u128 } else { 0 } - - if !swap_a { output as u128 } else { 0 }; - let new_pool_b = pool_b as u128 - + if !swap_a { input as u128 } else { 0 } - - if swap_a { output as u128 } else { 0 }; - let new_invariant = new_pool_a - .checked_mul(new_pool_b) - .ok_or(ProgramError::ArithmeticOverflow)?; + if new_invariant < invariant { + return Err(ProgramError::Custom(5)); // InvariantViolated + } - if new_invariant < invariant { - return Err(ProgramError::Custom(5)); // InvariantViolated + Ok(()) } - - Ok(()) } diff --git a/tokens/token-swap/quasar/src/instructions/withdraw_liquidity.rs b/tokens/token-swap/quasar/src/instructions/withdraw_liquidity.rs index 2516ebe0f..a5336071f 100644 --- a/tokens/token-swap/quasar/src/instructions/withdraw_liquidity.rs +++ b/tokens/token-swap/quasar/src/instructions/withdraw_liquidity.rs @@ -6,80 +6,83 @@ use { /// Accounts for withdrawing liquidity from a pool. #[derive(Accounts)] -pub struct WithdrawLiquidity<'info> { +pub struct WithdrawLiquidity { #[account(seeds = [b"amm"], bump)] - pub amm: &'info Account, + pub amm: Account, #[account(seeds = [amm, mint_a, mint_b], bump)] - pub pool: &'info Account, + pub pool: Account, /// Pool authority PDA. #[account(seeds = [amm, mint_a, mint_b, crate::AUTHORITY_SEED], bump)] - pub pool_authority: &'info UncheckedAccount, - pub depositor: &'info Signer, + pub pool_authority: UncheckedAccount, + pub depositor: Signer, #[account(mut, seeds = [amm, mint_a, mint_b, crate::LIQUIDITY_SEED], bump)] - pub mint_liquidity: &'info mut Account, + pub mint_liquidity: Account, #[account(mut)] - pub mint_a: &'info mut Account, + pub mint_a: Account, #[account(mut)] - pub mint_b: &'info mut Account, + pub mint_b: Account, #[account(mut)] - pub pool_account_a: &'info mut Account, + pub pool_account_a: Account, #[account(mut)] - pub pool_account_b: &'info mut Account, + pub pool_account_b: Account, #[account(mut)] - pub depositor_account_liquidity: &'info mut Account, + pub depositor_account_liquidity: Account, #[account(mut, init_if_needed, payer = payer, token::mint = mint_a, token::authority = depositor)] - pub depositor_account_a: &'info mut Account, + pub depositor_account_a: Account, #[account(mut, init_if_needed, payer = payer, token::mint = mint_b, token::authority = depositor)] - pub depositor_account_b: &'info mut Account, + pub depositor_account_b: Account, #[account(mut)] - pub payer: &'info Signer, - pub token_program: &'info Program, - pub system_program: &'info Program, + pub payer: Signer, + pub token_program: Program, + pub system_program: Program, } -#[inline(always)] -pub fn handle_withdraw_liquidity( - accounts: &mut WithdrawLiquidity, amount: u64, - bumps: &WithdrawLiquidityBumps, -) -> Result<(), ProgramError> { - let bump = [bumps.pool_authority]; - let seeds: &[Seed] = &[ - Seed::from(accounts.amm.address().as_ref()), - Seed::from(accounts.mint_a.address().as_ref()), - Seed::from(accounts.mint_b.address().as_ref()), - Seed::from(crate::AUTHORITY_SEED), - Seed::from(&bump as &[u8]), - ]; +impl WithdrawLiquidity { + #[inline(always)] + pub fn withdraw_liquidity( + &mut self, + amount: u64, + bumps: &WithdrawLiquidityBumps, + ) -> Result<(), ProgramError> { + let bump = [bumps.pool_authority]; + let seeds: &[Seed] = &[ + Seed::from(self.amm.address().as_ref()), + Seed::from(self.mint_a.address().as_ref()), + Seed::from(self.mint_b.address().as_ref()), + Seed::from(crate::AUTHORITY_SEED), + Seed::from(&bump as &[u8]), + ]; - // Compute proportional amounts. - let total_liquidity = accounts.mint_liquidity.supply() + crate::MINIMUM_LIQUIDITY; + // Compute proportional amounts. + let total_liquidity = self.mint_liquidity.supply() + crate::MINIMUM_LIQUIDITY; - let amount_a = (amount as u128) - .checked_mul(accounts.pool_account_a.amount() as u128) - .ok_or(ProgramError::ArithmeticOverflow)? - .checked_div(total_liquidity as u128) - .ok_or(ProgramError::ArithmeticOverflow)? as u64; + let amount_a = (amount as u128) + .checked_mul(self.pool_account_a.amount() as u128) + .ok_or(ProgramError::ArithmeticOverflow)? + .checked_div(total_liquidity as u128) + .ok_or(ProgramError::ArithmeticOverflow)? as u64; - let amount_b = (amount as u128) - .checked_mul(accounts.pool_account_b.amount() as u128) - .ok_or(ProgramError::ArithmeticOverflow)? - .checked_div(total_liquidity as u128) - .ok_or(ProgramError::ArithmeticOverflow)? as u64; + let amount_b = (amount as u128) + .checked_mul(self.pool_account_b.amount() as u128) + .ok_or(ProgramError::ArithmeticOverflow)? + .checked_div(total_liquidity as u128) + .ok_or(ProgramError::ArithmeticOverflow)? as u64; - // Transfer token A from pool to depositor. - accounts.token_program - .transfer(accounts.pool_account_a, accounts.depositor_account_a, accounts.pool_authority, amount_a) - .invoke_signed(seeds)?; + // Transfer token A from pool to depositor. + self.token_program + .transfer(&self.pool_account_a, &self.depositor_account_a, &self.pool_authority, amount_a) + .invoke_signed(seeds)?; - // Transfer token B from pool to depositor. - accounts.token_program - .transfer(accounts.pool_account_b, accounts.depositor_account_b, accounts.pool_authority, amount_b) - .invoke_signed(seeds)?; + // Transfer token B from pool to depositor. + self.token_program + .transfer(&self.pool_account_b, &self.depositor_account_b, &self.pool_authority, amount_b) + .invoke_signed(seeds)?; - // Burn LP tokens. - accounts.token_program - .burn(accounts.depositor_account_liquidity, accounts.mint_liquidity, accounts.depositor, amount) - .invoke()?; + // Burn LP tokens. + self.token_program + .burn(&self.depositor_account_liquidity, &self.mint_liquidity, &self.depositor, amount) + .invoke()?; - Ok(()) + Ok(()) + } } diff --git a/tokens/token-swap/quasar/src/lib.rs b/tokens/token-swap/quasar/src/lib.rs index 8895660d5..b94d0ed1e 100644 --- a/tokens/token-swap/quasar/src/lib.rs +++ b/tokens/token-swap/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![cfg_attr(not(test), no_std)] +#![no_std] use quasar_lang::prelude::*; @@ -35,12 +35,12 @@ mod quasar_token_swap { id: Address, fee: u16, ) -> Result<(), ProgramError> { - instructions::handle_create_amm(&mut ctx.accounts, id, fee) + ctx.accounts.create_amm(id, fee) } #[instruction(discriminator = 1)] pub fn create_pool(ctx: Ctx) -> Result<(), ProgramError> { - instructions::handle_create_pool(&mut ctx.accounts) + ctx.accounts.create_pool() } #[instruction(discriminator = 2)] @@ -49,7 +49,7 @@ mod quasar_token_swap { amount_a: u64, amount_b: u64, ) -> Result<(), ProgramError> { - instructions::handle_deposit_liquidity(&mut ctx.accounts, amount_a, amount_b, &ctx.bumps) + ctx.accounts.deposit_liquidity(amount_a, amount_b, &ctx.bumps) } #[instruction(discriminator = 3)] @@ -57,7 +57,7 @@ mod quasar_token_swap { ctx: Ctx, amount: u64, ) -> Result<(), ProgramError> { - instructions::handle_withdraw_liquidity(&mut ctx.accounts, amount, &ctx.bumps) + ctx.accounts.withdraw_liquidity(amount, &ctx.bumps) } #[instruction(discriminator = 4)] @@ -67,6 +67,6 @@ mod quasar_token_swap { input_amount: u64, min_output_amount: u64, ) -> Result<(), ProgramError> { - instructions::handle_swap_exact_tokens_for_tokens(&mut ctx.accounts, swap_a, input_amount, min_output_amount, &ctx.bumps) + ctx.accounts.swap_exact_tokens_for_tokens(swap_a, input_amount, min_output_amount, &ctx.bumps) } } diff --git a/tokens/transfer-tokens/quasar/Cargo.toml b/tokens/transfer-tokens/quasar/Cargo.toml index e61a66e99..f5fea0b98 100644 --- a/tokens/transfer-tokens/quasar/Cargo.toml +++ b/tokens/transfer-tokens/quasar/Cargo.toml @@ -22,8 +22,8 @@ client = [] debug = [] [dependencies] -quasar-lang = "0.0" -quasar-spl = "0.0" +quasar-lang = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } +quasar-spl = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } solana-instruction = { version = "3.2.0" } [dev-dependencies] diff --git a/tokens/transfer-tokens/quasar/src/lib.rs b/tokens/transfer-tokens/quasar/src/lib.rs index 354a75bbe..cd0d3cc5a 100644 --- a/tokens/transfer-tokens/quasar/src/lib.rs +++ b/tokens/transfer-tokens/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![cfg_attr(not(test), no_std)] +#![no_std] use quasar_lang::prelude::*; use quasar_spl::{Mint, Token, TokenCpi}; @@ -20,51 +20,55 @@ mod quasar_transfer_tokens { /// Mint tokens to a recipient's token account. #[instruction(discriminator = 0)] pub fn mint_tokens(ctx: Ctx, amount: u64) -> Result<(), ProgramError> { - handle_mint_tokens(&mut ctx.accounts, amount) + ctx.accounts.mint_tokens(amount) } /// Transfer tokens from sender to recipient. #[instruction(discriminator = 1)] pub fn transfer_tokens(ctx: Ctx, amount: u64) -> Result<(), ProgramError> { - handle_transfer_tokens(&mut ctx.accounts, amount) + ctx.accounts.transfer_tokens(amount) } } /// Accounts for minting tokens to a recipient. #[derive(Accounts)] -pub struct MintTokens<'info> { +pub struct MintTokens { #[account(mut)] - pub mint_authority: &'info Signer, + pub mint_authority: Signer, #[account(mut)] - pub mint: &'info mut Account, + pub mint: Account, /// The recipient's token account. Must already exist. #[account(mut)] - pub recipient_token_account: &'info mut Account, - pub token_program: &'info Program, + pub recipient_token_account: Account, + pub token_program: Program, } -#[inline(always)] -pub fn handle_mint_tokens(accounts: &mut MintTokens, amount: u64) -> Result<(), ProgramError> { - accounts.token_program - .mint_to(accounts.mint, accounts.recipient_token_account, accounts.mint_authority, amount) - .invoke() +impl MintTokens { + #[inline(always)] + pub fn mint_tokens(&mut self, amount: u64) -> Result<(), ProgramError> { + self.token_program + .mint_to(&self.mint, &self.recipient_token_account, &self.mint_authority, amount) + .invoke() + } } /// Accounts for transferring tokens between two token accounts. #[derive(Accounts)] -pub struct TransferTokens<'info> { +pub struct TransferTokens { #[account(mut)] - pub sender: &'info Signer, + pub sender: Signer, #[account(mut)] - pub sender_token_account: &'info mut Account, + pub sender_token_account: Account, #[account(mut)] - pub recipient_token_account: &'info mut Account, - pub token_program: &'info Program, + pub recipient_token_account: Account, + pub token_program: Program, } -#[inline(always)] -pub fn handle_transfer_tokens(accounts: &mut TransferTokens, amount: u64) -> Result<(), ProgramError> { - accounts.token_program - .transfer(accounts.sender_token_account, accounts.recipient_token_account, accounts.sender, amount) - .invoke() +impl TransferTokens { + #[inline(always)] + pub fn transfer_tokens(&mut self, amount: u64) -> Result<(), ProgramError> { + self.token_program + .transfer(&self.sender_token_account, &self.recipient_token_account, &self.sender, amount) + .invoke() + } } From c2ce660cc7c326ed76a17a4f4f1027604990e37b Mon Sep 17 00:00:00 2001 From: Jimii Date: Wed, 22 Apr 2026 17:26:14 +0300 Subject: [PATCH 02/12] chore: gitignore claude --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 406e30870..8442fdd7a 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,5 @@ node_modules/ /target deploy +.claude +.claire From 91b9e454138c5775ba7b36fff5de90baf72f3445 Mon Sep 17 00:00:00 2001 From: Jimii Date: Wed, 22 Apr 2026 19:52:26 +0300 Subject: [PATCH 03/12] update: remove impl blocks --- .gitignore | 3 + .../quasar/src/instructions/create.rs | 33 +- .../quasar/src/instructions/check_accounts.rs | 12 +- .../quasar/src/instructions/close_user.rs | 8 +- .../quasar/src/instructions/create_user.rs | 22 +- .../quasar/src/instructions/increment.rs | 12 +- .../src/instructions/initialize_counter.rs | 10 +- .../src/instructions/create_system_account.rs | 30 +- .../quasar/src/instructions/set_favorites.rs | 18 +- .../quasar/src/instructions/hello.rs | 12 +- .../src/instructions/create_new_account.rs | 40 ++- .../src/instructions/init_rent_vault.rs | 12 +- .../quasar/src/instructions/go_to_park.rs | 26 +- .../quasar/src/instructions/create.rs | 10 +- .../quasar/src/instructions/increment.rs | 12 +- .../quasar/src/instructions/initialize.rs | 16 +- .../realloc/quasar/src/instructions/update.rs | 16 +- .../src/instructions/create_system_account.rs | 46 ++- .../src/instructions/carnival_context.rs | 53 ++-- .../quasar/src/instructions/eat_food.rs | 2 +- .../quasar/src/instructions/get_on_ride.rs | 2 +- .../quasar/src/instructions/play_game.rs | 2 +- .../quasar/src/state/food.rs | 6 +- .../quasar/src/state/game.rs | 6 +- .../quasar/src/state/ride.rs | 8 +- .../src/instructions/transfer_sol_with_cpi.rs | 12 +- .../instructions/transfer_sol_with_program.rs | 16 +- .../quasar/src/instructions/burn_cnft.rs | 134 ++++---- .../quasar/src/instructions/withdraw.rs | 156 +++++----- .../quasar/src/instructions/withdraw_two.rs | 288 +++++++++--------- .../cutils/quasar/src/instructions/mint.rs | 132 ++++---- .../cutils/quasar/src/instructions/verify.rs | 140 +++++---- .../quasar/src/instructions/read_price.rs | 76 +++-- tokens/create-token/quasar/src/lib.rs | 30 +- tokens/escrow/quasar/src/instructions/make.rs | 36 ++- .../escrow/quasar/src/instructions/refund.rs | 32 +- tokens/escrow/quasar/src/instructions/take.rs | 54 ++-- .../quasar/src/lib.rs | 146 +++++---- tokens/nft-minter/quasar/src/lib.rs | 112 ++++--- .../src/instructions/create_collection.rs | 98 +++--- .../quasar/src/instructions/mint_nft.rs | 98 +++--- .../src/instructions/verify_collection.rs | 40 ++- tokens/pda-mint-authority/quasar/src/lib.rs | 38 ++- .../quasar/src/instructions/create.rs | 56 ++-- .../quasar/src/instructions/mint.rs | 36 ++- .../token-extensions/basics/quasar/src/lib.rs | 88 +++--- .../cpi-guard/quasar/src/lib.rs | 52 ++-- .../default-account-state/quasar/src/lib.rs | 136 ++++----- .../token-extensions/group/quasar/src/lib.rs | 101 +++--- .../immutable-owner/quasar/src/lib.rs | 87 +++--- .../interest-bearing/quasar/src/lib.rs | 147 +++++---- .../memo-transfer/quasar/src/lib.rs | 131 ++++---- .../mint-close-authority/quasar/src/lib.rs | 135 ++++---- .../non-transferable/quasar/src/lib.rs | 85 +++--- .../permanent-delegate/quasar/src/lib.rs | 89 +++--- .../transfer-fee/quasar/src/lib.rs | 102 +++---- .../transfer-hook/counter/quasar/src/lib.rs | 42 +-- .../hello-world/quasar/src/lib.rs | 65 ++-- .../transfer-cost/quasar/src/lib.rs | 38 +-- .../transfer-switch/quasar/src/lib.rs | 233 +++++++------- .../src/instructions/check_contributions.rs | 38 ++- .../quasar/src/instructions/contribute.rs | 30 +- .../quasar/src/instructions/initialize.rs | 40 ++- .../quasar/src/instructions/refund.rs | 32 +- .../quasar/src/instructions/create_amm.rs | 14 +- .../quasar/src/instructions/create_pool.rs | 14 +- .../src/instructions/deposit_liquidity.rs | 136 ++++----- .../swap_exact_tokens_for_tokens.rs | 186 ++++++----- .../src/instructions/withdraw_liquidity.rs | 80 +++-- tokens/transfer-tokens/quasar/src/lib.rs | 28 +- 70 files changed, 2066 insertions(+), 2210 deletions(-) diff --git a/.gitignore b/.gitignore index 406e30870..370b21606 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,6 @@ node_modules/ /target deploy + +.claire +.claude diff --git a/basics/account-data/quasar/src/instructions/create.rs b/basics/account-data/quasar/src/instructions/create.rs index 0099c8843..51fe12083 100644 --- a/basics/account-data/quasar/src/instructions/create.rs +++ b/basics/account-data/quasar/src/instructions/create.rs @@ -15,21 +15,20 @@ pub struct CreateAddressInfo { pub system_program: Program, } -impl CreateAddressInfo { - #[inline(always)] - pub fn create_address_info( - &mut self, name: &str, - house_number: u8, - street: &str, - city: &str, - ) -> Result<(), ProgramError> { - self.address_info.set_inner( - house_number, - name, - street, - city, - self.payer.to_account_view(), - None, - ) - } +#[inline(always)] +pub fn handle_create_address_info( + accounts: &mut CreateAddressInfo, + name: &str, + house_number: u8, + street: &str, + city: &str, +) -> Result<(), ProgramError> { + accounts.address_info.set_inner( + house_number, + name, + street, + city, + accounts.payer.to_account_view(), + None, + ) } diff --git a/basics/checking-accounts/quasar/src/instructions/check_accounts.rs b/basics/checking-accounts/quasar/src/instructions/check_accounts.rs index d8dfdd3ab..f25b55aef 100644 --- a/basics/checking-accounts/quasar/src/instructions/check_accounts.rs +++ b/basics/checking-accounts/quasar/src/instructions/check_accounts.rs @@ -21,11 +21,9 @@ pub struct CheckAccounts { pub system_program: Program, } -impl CheckAccounts { - #[inline(always)] - pub fn check_accounts(&mut self) -> Result<(), ProgramError> { - // All validation happens declaratively via the account types above. - // If any check fails, the runtime rejects the transaction before this runs. - Ok(()) - } +#[inline(always)] +pub fn handle_check_accounts(_accounts: &mut CheckAccounts) -> Result<(), ProgramError> { + // All validation happens declaratively via the account types above. + // If any check fails, the runtime rejects the transaction before this runs. + Ok(()) } diff --git a/basics/close-account/quasar/src/instructions/close_user.rs b/basics/close-account/quasar/src/instructions/close_user.rs index a15db3958..dcebbb7b5 100644 --- a/basics/close-account/quasar/src/instructions/close_user.rs +++ b/basics/close-account/quasar/src/instructions/close_user.rs @@ -15,9 +15,7 @@ pub struct CloseUser { pub user_account: Account>, } -impl CloseUser { - #[inline(always)] - pub fn close_user(&mut self) -> Result<(), ProgramError> { - self.user_account.close(self.user.to_account_view()) - } +#[inline(always)] +pub fn handle_close_user(accounts: &mut CloseUser) -> Result<(), ProgramError> { + accounts.user_account.close(accounts.user.to_account_view()) } diff --git a/basics/close-account/quasar/src/instructions/create_user.rs b/basics/close-account/quasar/src/instructions/create_user.rs index 1559e80fd..26194325b 100644 --- a/basics/close-account/quasar/src/instructions/create_user.rs +++ b/basics/close-account/quasar/src/instructions/create_user.rs @@ -13,16 +13,14 @@ pub struct CreateUser { pub system_program: Program, } -impl CreateUser { - #[inline(always)] - pub fn create_user(&mut self, name: &str, bump: u8) -> Result<(), ProgramError> { - let user_address = *self.user.to_account_view().address(); - self.user_account.set_inner( - bump, - user_address, - name, - self.user.to_account_view(), - None, - ) - } +#[inline(always)] +pub fn handle_create_user(accounts: &mut CreateUser, name: &str, bump: u8) -> Result<(), ProgramError> { + let user_address = *accounts.user.to_account_view().address(); + accounts.user_account.set_inner( + bump, + user_address, + name, + accounts.user.to_account_view(), + None, + ) } diff --git a/basics/counter/quasar/src/instructions/increment.rs b/basics/counter/quasar/src/instructions/increment.rs index 32aae2b64..90b73ae7f 100644 --- a/basics/counter/quasar/src/instructions/increment.rs +++ b/basics/counter/quasar/src/instructions/increment.rs @@ -10,11 +10,9 @@ pub struct Increment { pub counter: Account, } -impl Increment { - #[inline(always)] - pub fn increment(&mut self) -> Result<(), ProgramError> { - let current: u64 = self.counter.count.into(); - self.counter.count = PodU64::from(current.checked_add(1).unwrap()); - Ok(()) - } +#[inline(always)] +pub fn handle_increment(accounts: &mut Increment) -> Result<(), ProgramError> { + let current: u64 = accounts.counter.count.into(); + accounts.counter.count = PodU64::from(current.checked_add(1).unwrap()); + Ok(()) } diff --git a/basics/counter/quasar/src/instructions/initialize_counter.rs b/basics/counter/quasar/src/instructions/initialize_counter.rs index 96309da16..4d81ea689 100644 --- a/basics/counter/quasar/src/instructions/initialize_counter.rs +++ b/basics/counter/quasar/src/instructions/initialize_counter.rs @@ -14,10 +14,8 @@ pub struct InitializeCounter { pub system_program: Program, } -impl InitializeCounter { - #[inline(always)] - pub fn initialize_counter(&mut self) -> Result<(), ProgramError> { - self.counter.set_inner(0u64); - Ok(()) - } +#[inline(always)] +pub fn handle_initialize_counter(accounts: &mut InitializeCounter) -> Result<(), ProgramError> { + accounts.counter.set_inner(0u64); + Ok(()) } diff --git a/basics/create-account/quasar/src/instructions/create_system_account.rs b/basics/create-account/quasar/src/instructions/create_system_account.rs index 71f8a8fa2..88a03be63 100644 --- a/basics/create-account/quasar/src/instructions/create_system_account.rs +++ b/basics/create-account/quasar/src/instructions/create_system_account.rs @@ -11,20 +11,18 @@ pub struct CreateSystemAccount { pub system_program: Program, } -impl CreateSystemAccount { - #[inline(always)] - pub fn create_system_account(&mut self) -> Result<(), ProgramError> { - // Create a zero-data account owned by the system program, - // funded with the minimum rent-exempt balance. - let system_program_address = Address::default(); - self.system_program - .create_account_with_minimum_balance( - &self.payer, - &self.new_account, - 0, // space: zero bytes of data - &system_program_address, - None, // fetch Rent sysvar automatically - )? - .invoke() - } +#[inline(always)] +pub fn handle_create_system_account(accounts: &mut CreateSystemAccount) -> Result<(), ProgramError> { + // Create a zero-data account owned by the system program, + // funded with the minimum rent-exempt balance. + let system_program_address = Address::default(); + accounts.system_program + .create_account_with_minimum_balance( + &accounts.payer, + &accounts.new_account, + 0, // space: zero bytes of data + &system_program_address, + None, // fetch Rent sysvar automatically + )? + .invoke() } diff --git a/basics/favorites/quasar/src/instructions/set_favorites.rs b/basics/favorites/quasar/src/instructions/set_favorites.rs index 9ba96747a..6debd3ae1 100644 --- a/basics/favorites/quasar/src/instructions/set_favorites.rs +++ b/basics/favorites/quasar/src/instructions/set_favorites.rs @@ -14,14 +14,12 @@ pub struct SetFavorites { pub system_program: Program, } -impl SetFavorites { - #[inline(always)] - pub fn set_favorites(&mut self, number: u64, color: &str) -> Result<(), ProgramError> { - self.favorites.set_inner( - number, - color, - self.user.to_account_view(), - None, - ) - } +#[inline(always)] +pub fn handle_set_favorites(accounts: &mut SetFavorites, number: u64, color: &str) -> Result<(), ProgramError> { + accounts.favorites.set_inner( + number, + color, + accounts.user.to_account_view(), + None, + ) } diff --git a/basics/hello-solana/quasar/src/instructions/hello.rs b/basics/hello-solana/quasar/src/instructions/hello.rs index 16569b19c..d36abb017 100644 --- a/basics/hello-solana/quasar/src/instructions/hello.rs +++ b/basics/hello-solana/quasar/src/instructions/hello.rs @@ -9,11 +9,9 @@ pub struct Hello { pub payer: Signer, } -impl Hello { - #[inline(always)] - pub fn hello(&mut self) -> Result<(), ProgramError> { - log("Hello, Solana!"); - log("Our program's Program ID: FLUH9c5oAfXb1eYbkZvdGK9r9SLQJBUi2DZQaBVj7Tzr"); - Ok(()) - } +#[inline(always)] +pub fn handle_hello(_accounts: &mut Hello) -> Result<(), ProgramError> { + log("Hello, Solana!"); + log("Our program's Program ID: FLUH9c5oAfXb1eYbkZvdGK9r9SLQJBUi2DZQaBVj7Tzr"); + Ok(()) } diff --git a/basics/pda-rent-payer/quasar/src/instructions/create_new_account.rs b/basics/pda-rent-payer/quasar/src/instructions/create_new_account.rs index f21bc41e7..d26b89a10 100644 --- a/basics/pda-rent-payer/quasar/src/instructions/create_new_account.rs +++ b/basics/pda-rent-payer/quasar/src/instructions/create_new_account.rs @@ -11,27 +11,25 @@ pub struct CreateNewAccount { pub system_program: Program, } -impl CreateNewAccount { - #[inline(always)] - pub fn create_new_account(&mut self, rent_vault_bump: u8) -> Result<(), ProgramError> { - // Build PDA signer seeds: ["rent_vault", bump]. - let bump_bytes = [rent_vault_bump]; - let seeds: &[Seed] = &[ - Seed::from(b"rent_vault" as &[u8]), - Seed::from(&bump_bytes as &[u8]), - ]; +#[inline(always)] +pub fn handle_create_new_account(accounts: &mut CreateNewAccount, rent_vault_bump: u8) -> Result<(), ProgramError> { + // Build PDA signer seeds: ["rent_vault", bump]. + let bump_bytes = [rent_vault_bump]; + let seeds: &[Seed] = &[ + Seed::from(b"rent_vault" as &[u8]), + Seed::from(&bump_bytes as &[u8]), + ]; - let system_program_address = Address::default(); + let system_program_address = Address::default(); - // Create a zero-data system-owned account, funded from the vault. - self.system_program - .create_account_with_minimum_balance( - &self.rent_vault, - &self.new_account, - 0, // space: zero bytes of data - &system_program_address, - None, // fetch Rent sysvar automatically - )? - .invoke_signed(seeds) - } + // Create a zero-data system-owned account, funded from the vault. + accounts.system_program + .create_account_with_minimum_balance( + &accounts.rent_vault, + &accounts.new_account, + 0, // space: zero bytes of data + &system_program_address, + None, // fetch Rent sysvar automatically + )? + .invoke_signed(seeds) } diff --git a/basics/pda-rent-payer/quasar/src/instructions/init_rent_vault.rs b/basics/pda-rent-payer/quasar/src/instructions/init_rent_vault.rs index 8943e9290..191b46f45 100644 --- a/basics/pda-rent-payer/quasar/src/instructions/init_rent_vault.rs +++ b/basics/pda-rent-payer/quasar/src/instructions/init_rent_vault.rs @@ -13,11 +13,9 @@ pub struct InitRentVault { pub system_program: Program, } -impl InitRentVault { - #[inline(always)] - pub fn init_rent_vault(&mut self, fund_lamports: u64) -> Result<(), ProgramError> { - self.system_program - .transfer(&self.payer, &self.rent_vault, fund_lamports) - .invoke() - } +#[inline(always)] +pub fn handle_init_rent_vault(accounts: &mut InitRentVault, fund_lamports: u64) -> Result<(), ProgramError> { + accounts.system_program + .transfer(&accounts.payer, &accounts.rent_vault, fund_lamports) + .invoke() } diff --git a/basics/processing-instructions/quasar/src/instructions/go_to_park.rs b/basics/processing-instructions/quasar/src/instructions/go_to_park.rs index 1f579ce7b..ce71aeef9 100644 --- a/basics/processing-instructions/quasar/src/instructions/go_to_park.rs +++ b/basics/processing-instructions/quasar/src/instructions/go_to_park.rs @@ -8,19 +8,17 @@ pub struct Park { pub signer: Signer, } -impl Park { - #[inline(always)] - pub fn go_to_park(&mut self, _name: &str, height: u32) -> Result<(), ProgramError> { - // Quasar's `log()` takes &str, no format! macro available in no_std. - // We can't interpolate the name or height into the log message, so - // we use static messages — same logic as the Anchor version, just - // without formatted output. - log("Welcome to the park!"); - if height > 5 { - log("You are tall enough to ride this ride. Congratulations."); - } else { - log("You are NOT tall enough to ride this ride. Sorry mate."); - } - Ok(()) +#[inline(always)] +pub fn handle_go_to_park(_accounts: &mut Park, _name: &str, height: u32) -> Result<(), ProgramError> { + // Quasar's `log()` takes &str, no format! macro available in no_std. + // We can't interpolate the name or height into the log message, so + // we use static messages — same logic as the Anchor version, just + // without formatted output. + log("Welcome to the park!"); + if height > 5 { + log("You are tall enough to ride this ride. Congratulations."); + } else { + log("You are NOT tall enough to ride this ride. Sorry mate."); } + Ok(()) } diff --git a/basics/program-derived-addresses/quasar/src/instructions/create.rs b/basics/program-derived-addresses/quasar/src/instructions/create.rs index 44cb197fd..7094e64f7 100644 --- a/basics/program-derived-addresses/quasar/src/instructions/create.rs +++ b/basics/program-derived-addresses/quasar/src/instructions/create.rs @@ -14,10 +14,8 @@ pub struct CreatePageVisits { pub system_program: Program, } -impl CreatePageVisits { - #[inline(always)] - pub fn create_page_visits(&mut self) -> Result<(), ProgramError> { - self.page_visits.set_inner(0u64); - Ok(()) - } +#[inline(always)] +pub fn handle_create_page_visits(accounts: &mut CreatePageVisits) -> Result<(), ProgramError> { + accounts.page_visits.set_inner(0u64); + Ok(()) } diff --git a/basics/program-derived-addresses/quasar/src/instructions/increment.rs b/basics/program-derived-addresses/quasar/src/instructions/increment.rs index 33bac0ce7..042038386 100644 --- a/basics/program-derived-addresses/quasar/src/instructions/increment.rs +++ b/basics/program-derived-addresses/quasar/src/instructions/increment.rs @@ -12,11 +12,9 @@ pub struct IncrementPageVisits { pub page_visits: Account, } -impl IncrementPageVisits { - #[inline(always)] - pub fn increment_page_visits(&mut self) -> Result<(), ProgramError> { - let current: u64 = self.page_visits.page_visits.into(); - self.page_visits.page_visits = PodU64::from(current.checked_add(1).unwrap()); - Ok(()) - } +#[inline(always)] +pub fn handle_increment_page_visits(accounts: &mut IncrementPageVisits) -> Result<(), ProgramError> { + let current: u64 = accounts.page_visits.page_visits.into(); + accounts.page_visits.page_visits = PodU64::from(current.checked_add(1).unwrap()); + Ok(()) } diff --git a/basics/realloc/quasar/src/instructions/initialize.rs b/basics/realloc/quasar/src/instructions/initialize.rs index f251a7c19..0efba2ced 100644 --- a/basics/realloc/quasar/src/instructions/initialize.rs +++ b/basics/realloc/quasar/src/instructions/initialize.rs @@ -14,13 +14,11 @@ pub struct Initialize { pub system_program: Program, } -impl Initialize { - #[inline(always)] - pub fn initialize(&mut self, message: &str) -> Result<(), ProgramError> { - self.message_account.set_inner( - message, - self.payer.to_account_view(), - None, - ) - } +#[inline(always)] +pub fn handle_initialize(accounts: &mut Initialize, message: &str) -> Result<(), ProgramError> { + accounts.message_account.set_inner( + message, + accounts.payer.to_account_view(), + None, + ) } diff --git a/basics/realloc/quasar/src/instructions/update.rs b/basics/realloc/quasar/src/instructions/update.rs index 7cabac0a3..9e6d60693 100644 --- a/basics/realloc/quasar/src/instructions/update.rs +++ b/basics/realloc/quasar/src/instructions/update.rs @@ -15,13 +15,11 @@ pub struct Update { pub system_program: Program, } -impl Update { - #[inline(always)] - pub fn update(&mut self, message: &str) -> Result<(), ProgramError> { - self.message_account.set_inner( - message, - self.payer.to_account_view(), - None, - ) - } +#[inline(always)] +pub fn handle_update(accounts: &mut Update, message: &str) -> Result<(), ProgramError> { + accounts.message_account.set_inner( + message, + accounts.payer.to_account_view(), + None, + ) } diff --git a/basics/rent/quasar/src/instructions/create_system_account.rs b/basics/rent/quasar/src/instructions/create_system_account.rs index a4170e0e9..e1e4227cd 100644 --- a/basics/rent/quasar/src/instructions/create_system_account.rs +++ b/basics/rent/quasar/src/instructions/create_system_account.rs @@ -10,32 +10,30 @@ pub struct CreateSystemAccount { pub system_program: Program, } -impl CreateSystemAccount { - #[inline(always)] - pub fn create_system_account(&mut self, name: &str, address: &str) -> Result<(), ProgramError> { - // Calculate space needed for the serialised AddressData: - // borsh-style: 4-byte length prefix + bytes for each String field. - let space = 4 + name.len() + 4 + address.len(); +#[inline(always)] +pub fn handle_create_system_account(accounts: &mut CreateSystemAccount, name: &str, address: &str) -> Result<(), ProgramError> { + // Calculate space needed for the serialised AddressData: + // borsh-style: 4-byte length prefix + bytes for each String field. + let space = 4 + name.len() + 4 + address.len(); - log("Program invoked. Creating a system account..."); + log("Program invoked. Creating a system account..."); - // The owner of the new account is the system program. - let system_program_address = Address::default(); + // The owner of the new account is the system program. + let system_program_address = Address::default(); - // Create the account with the computed space. - // create_account_with_minimum_balance automatically fetches Rent - // sysvar and calculates the minimum rent-exempt lamports. - self.system_program - .create_account_with_minimum_balance( - &self.payer, - &self.new_account, - space as u64, - &system_program_address, - None, // fetch Rent sysvar automatically - )? - .invoke()?; + // Create the account with the computed space. + // create_account_with_minimum_balance automatically fetches Rent + // sysvar and calculates the minimum rent-exempt lamports. + accounts.system_program + .create_account_with_minimum_balance( + &accounts.payer, + &accounts.new_account, + space as u64, + &system_program_address, + None, // fetch Rent sysvar automatically + )? + .invoke()?; - log("Account created successfully."); - Ok(()) - } + log("Account created successfully."); + Ok(()) } diff --git a/basics/repository-layout/quasar/src/instructions/carnival_context.rs b/basics/repository-layout/quasar/src/instructions/carnival_context.rs index 7c60b2d1d..33960b498 100644 --- a/basics/repository-layout/quasar/src/instructions/carnival_context.rs +++ b/basics/repository-layout/quasar/src/instructions/carnival_context.rs @@ -10,32 +10,33 @@ pub struct CarnivalContext { pub payer: Signer, } -impl CarnivalContext { - #[inline(always)] - pub fn go_on_ride( - &mut self, name: &str, - height: u32, - ticket_count: u32, - ride_name: &str, - ) -> Result<(), ProgramError> { - get_on_ride::get_on_ride(name, height, ticket_count, ride_name) - } +#[inline(always)] +pub fn handle_go_on_ride( + _accounts: &mut CarnivalContext, + name: &str, + height: u32, + ticket_count: u32, + ride_name: &str, +) -> Result<(), ProgramError> { + get_on_ride::get_on_ride(name, height, ticket_count, ride_name) +} - #[inline(always)] - pub fn play_game( - &mut self, name: &str, - ticket_count: u32, - game_name: &str, - ) -> Result<(), ProgramError> { - play_game::play_game(name, ticket_count, game_name) - } +#[inline(always)] +pub fn handle_play_game( + _accounts: &mut CarnivalContext, + name: &str, + ticket_count: u32, + game_name: &str, +) -> Result<(), ProgramError> { + play_game::play_game(name, ticket_count, game_name) +} - #[inline(always)] - pub fn eat_food( - &mut self, name: &str, - ticket_count: u32, - food_stand_name: &str, - ) -> Result<(), ProgramError> { - eat_food::eat_food(name, ticket_count, food_stand_name) - } +#[inline(always)] +pub fn handle_eat_food( + _accounts: &mut CarnivalContext, + name: &str, + ticket_count: u32, + food_stand_name: &str, +) -> Result<(), ProgramError> { + eat_food::eat_food(name, ticket_count, food_stand_name) } diff --git a/basics/repository-layout/quasar/src/instructions/eat_food.rs b/basics/repository-layout/quasar/src/instructions/eat_food.rs index f34dde13e..48592bb9f 100644 --- a/basics/repository-layout/quasar/src/instructions/eat_food.rs +++ b/basics/repository-layout/quasar/src/instructions/eat_food.rs @@ -12,7 +12,7 @@ pub fn eat_food( let mut i = 0; while i < stands.len() { - if stands[i].name_matches(food_stand_name) { + if food::food_stand_name_matches(&stands[i], food_stand_name) { log("Welcome to the food stand!"); if ticket_count < stands[i].tickets { diff --git a/basics/repository-layout/quasar/src/instructions/get_on_ride.rs b/basics/repository-layout/quasar/src/instructions/get_on_ride.rs index b0e670b02..66b85c5ee 100644 --- a/basics/repository-layout/quasar/src/instructions/get_on_ride.rs +++ b/basics/repository-layout/quasar/src/instructions/get_on_ride.rs @@ -15,7 +15,7 @@ pub fn get_on_ride( let mut i = 0; while i < rides.len() { - if rides[i].name_matches(ride_name) { + if ride::ride_name_matches(&rides[i], ride_name) { log("You're about to go on a ride!"); if ticket_count < rides[i].tickets { diff --git a/basics/repository-layout/quasar/src/instructions/play_game.rs b/basics/repository-layout/quasar/src/instructions/play_game.rs index 84e900c15..fdac0e9cf 100644 --- a/basics/repository-layout/quasar/src/instructions/play_game.rs +++ b/basics/repository-layout/quasar/src/instructions/play_game.rs @@ -12,7 +12,7 @@ pub fn play_game( let mut i = 0; while i < games.len() { - if games[i].name_matches(game_name) { + if game::game_name_matches(&games[i], game_name) { log("You're about to play a game!"); if ticket_count < games[i].tickets { diff --git a/basics/repository-layout/quasar/src/state/food.rs b/basics/repository-layout/quasar/src/state/food.rs index ecb564034..e8569a4c7 100644 --- a/basics/repository-layout/quasar/src/state/food.rs +++ b/basics/repository-layout/quasar/src/state/food.rs @@ -5,10 +5,8 @@ pub struct FoodStand { pub tickets: u32, } -impl FoodStand { - pub fn name_matches(&self, other: &str) -> bool { - self.name.as_bytes() == other.as_bytes() - } +pub fn food_stand_name_matches(stand: &FoodStand, other: &str) -> bool { + stand.name.as_bytes() == other.as_bytes() } /// Static list of food stands. diff --git a/basics/repository-layout/quasar/src/state/game.rs b/basics/repository-layout/quasar/src/state/game.rs index c7682a4b9..ded4a7d55 100644 --- a/basics/repository-layout/quasar/src/state/game.rs +++ b/basics/repository-layout/quasar/src/state/game.rs @@ -8,10 +8,8 @@ pub struct Game { const DEFAULT_TICKETS_TO_PLAY: u32 = 3; -impl Game { - pub fn name_matches(&self, other: &str) -> bool { - self.name.as_bytes() == other.as_bytes() - } +pub fn game_name_matches(game: &Game, other: &str) -> bool { + game.name.as_bytes() == other.as_bytes() } /// Static list of carnival games. diff --git a/basics/repository-layout/quasar/src/state/ride.rs b/basics/repository-layout/quasar/src/state/ride.rs index 5939b697a..5759a9087 100644 --- a/basics/repository-layout/quasar/src/state/ride.rs +++ b/basics/repository-layout/quasar/src/state/ride.rs @@ -7,11 +7,9 @@ pub struct Ride { pub min_height: u32, } -impl Ride { - /// Check if a ride name matches (byte comparison, no alloc). - pub fn name_matches(&self, other: &str) -> bool { - self.name.as_bytes() == other.as_bytes() - } +/// Check if a ride name matches (byte comparison, no alloc). +pub fn ride_name_matches(ride: &Ride, other: &str) -> bool { + ride.name.as_bytes() == other.as_bytes() } /// Static list of carnival rides. diff --git a/basics/transfer-sol/quasar/src/instructions/transfer_sol_with_cpi.rs b/basics/transfer-sol/quasar/src/instructions/transfer_sol_with_cpi.rs index 81be8a541..cd85cf08f 100644 --- a/basics/transfer-sol/quasar/src/instructions/transfer_sol_with_cpi.rs +++ b/basics/transfer-sol/quasar/src/instructions/transfer_sol_with_cpi.rs @@ -10,11 +10,9 @@ pub struct TransferSolWithCpi { pub system_program: Program, } -impl TransferSolWithCpi { - #[inline(always)] - pub fn transfer_sol_with_cpi(&mut self, amount: u64) -> Result<(), ProgramError> { - self.system_program - .transfer(&self.payer, &self.recipient, amount) - .invoke() - } +#[inline(always)] +pub fn handle_transfer_sol_with_cpi(accounts: &mut TransferSolWithCpi, amount: u64) -> Result<(), ProgramError> { + accounts.system_program + .transfer(&accounts.payer, &accounts.recipient, amount) + .invoke() } diff --git a/basics/transfer-sol/quasar/src/instructions/transfer_sol_with_program.rs b/basics/transfer-sol/quasar/src/instructions/transfer_sol_with_program.rs index 55615a108..14dc016ed 100644 --- a/basics/transfer-sol/quasar/src/instructions/transfer_sol_with_program.rs +++ b/basics/transfer-sol/quasar/src/instructions/transfer_sol_with_program.rs @@ -10,13 +10,11 @@ pub struct TransferSolWithProgram { pub recipient: UncheckedAccount, } -impl TransferSolWithProgram { - #[inline(always)] - pub fn transfer_sol_with_program(&mut self, amount: u64) -> Result<(), ProgramError> { - let payer_view = self.payer.to_account_view(); - let recipient_view = self.recipient.to_account_view(); - set_lamports(payer_view, payer_view.lamports() - amount); - set_lamports(recipient_view, recipient_view.lamports() + amount); - Ok(()) - } +#[inline(always)] +pub fn handle_transfer_sol_with_program(accounts: &mut TransferSolWithProgram, amount: u64) -> Result<(), ProgramError> { + let payer_view = accounts.payer.to_account_view(); + let recipient_view = accounts.recipient.to_account_view(); + set_lamports(payer_view, payer_view.lamports() - amount); + set_lamports(recipient_view, recipient_view.lamports() + amount); + Ok(()) } diff --git a/compression/cnft-burn/quasar/src/instructions/burn_cnft.rs b/compression/cnft-burn/quasar/src/instructions/burn_cnft.rs index a7a83bcdc..246750e63 100644 --- a/compression/cnft-burn/quasar/src/instructions/burn_cnft.rs +++ b/compression/cnft-burn/quasar/src/instructions/burn_cnft.rs @@ -30,84 +30,82 @@ pub struct BurnCnft { pub system_program: Program, } -impl BurnCnft { - pub fn burn_cnft(&mut self, ctx: &CtxWithRemaining) -> Result<(), ProgramError> { - // Parse instruction args from raw data: - // root(32) + data_hash(32) + creator_hash(32) + nonce(8) + index(4) = 108 bytes - let data = ctx.data; - if data.len() < 108 { - return Err(ProgramError::InvalidInstructionData); - } +pub fn handle_burn_cnft(accounts: &mut BurnCnft, ctx: &CtxWithRemaining) -> Result<(), ProgramError> { + // Parse instruction args from raw data: + // root(32) + data_hash(32) + creator_hash(32) + nonce(8) + index(4) = 108 bytes + let data = ctx.data; + if data.len() < 108 { + return Err(ProgramError::InvalidInstructionData); + } - // Build instruction data: discriminator + args - // 8 + 32 + 32 + 32 + 8 + 4 = 116 bytes - let mut ix_data = [0u8; 116]; - ix_data[0..8].copy_from_slice(&BURN_DISCRIMINATOR); - ix_data[8..116].copy_from_slice(&data[0..108]); + // Build instruction data: discriminator + args + // 8 + 32 + 32 + 32 + 8 + 4 = 116 bytes + let mut ix_data = [0u8; 116]; + ix_data[0..8].copy_from_slice(&BURN_DISCRIMINATOR); + ix_data[8..116].copy_from_slice(&data[0..108]); - // Collect remaining accounts (proof nodes) into a stack buffer - let remaining = ctx.remaining_accounts(); - let placeholder = self.system_program.to_account_view().clone(); - let mut proof_views: [AccountView; MAX_PROOF_NODES] = - core::array::from_fn(|_| placeholder.clone()); - let mut proof_count = 0usize; - for result in remaining.iter() { - if proof_count >= MAX_PROOF_NODES { - break; - } - proof_views[proof_count] = result?; - proof_count += 1; + // Collect remaining accounts (proof nodes) into a stack buffer + let remaining = ctx.remaining_accounts(); + let placeholder = accounts.system_program.to_account_view().clone(); + let mut proof_views: [AccountView; MAX_PROOF_NODES] = + core::array::from_fn(|_| placeholder.clone()); + let mut proof_count = 0usize; + for result in remaining.iter() { + if proof_count >= MAX_PROOF_NODES { + break; } + proof_views[proof_count] = result?; + proof_count += 1; + } - let total_accounts = 7 + proof_count; + let total_accounts = 7 + proof_count; - // Build instruction account metas. - // Layout matches mpl-bubblegum Burn: tree_authority, leaf_owner (signer), - // leaf_delegate (= leaf_owner, not signer), merkle_tree, log_wrapper, - // compression_program, system_program, then proof nodes. - let sys_addr = self.system_program.address(); - let mut ix_accounts: [InstructionAccount; MAX_CPI_ACCOUNTS] = core::array::from_fn(|_| { - InstructionAccount::readonly(sys_addr) - }); + // Build instruction account metas. + // Layout matches mpl-bubblegum Burn: tree_authority, leaf_owner (signer), + // leaf_delegate (= leaf_owner, not signer), merkle_tree, log_wrapper, + // compression_program, system_program, then proof nodes. + let sys_addr = accounts.system_program.address(); + let mut ix_accounts: [InstructionAccount; MAX_CPI_ACCOUNTS] = core::array::from_fn(|_| { + InstructionAccount::readonly(sys_addr) + }); - ix_accounts[0] = InstructionAccount::readonly(self.tree_authority.address()); - ix_accounts[1] = InstructionAccount::readonly_signer(self.leaf_owner.address()); - // leaf_delegate = leaf_owner, not a signer in this call - ix_accounts[2] = InstructionAccount::readonly(self.leaf_owner.address()); - ix_accounts[3] = InstructionAccount::writable(self.merkle_tree.address()); - ix_accounts[4] = InstructionAccount::readonly(self.log_wrapper.address()); - ix_accounts[5] = InstructionAccount::readonly(self.compression_program.address()); - ix_accounts[6] = InstructionAccount::readonly(self.system_program.address()); + ix_accounts[0] = InstructionAccount::readonly(accounts.tree_authority.address()); + ix_accounts[1] = InstructionAccount::readonly_signer(accounts.leaf_owner.address()); + // leaf_delegate = leaf_owner, not a signer in this call + ix_accounts[2] = InstructionAccount::readonly(accounts.leaf_owner.address()); + ix_accounts[3] = InstructionAccount::writable(accounts.merkle_tree.address()); + ix_accounts[4] = InstructionAccount::readonly(accounts.log_wrapper.address()); + ix_accounts[5] = InstructionAccount::readonly(accounts.compression_program.address()); + ix_accounts[6] = InstructionAccount::readonly(accounts.system_program.address()); - for i in 0..proof_count { - ix_accounts[7 + i] = InstructionAccount::readonly(proof_views[i].address()); - } + for i in 0..proof_count { + ix_accounts[7 + i] = InstructionAccount::readonly(proof_views[i].address()); + } - // Build account views array for the CPI - let sys_view = self.system_program.to_account_view().clone(); - let mut views: [AccountView; MAX_CPI_ACCOUNTS] = core::array::from_fn(|_| sys_view.clone()); + // Build account views array for the CPI + let sys_view = accounts.system_program.to_account_view().clone(); + let mut views: [AccountView; MAX_CPI_ACCOUNTS] = core::array::from_fn(|_| sys_view.clone()); - views[0] = self.tree_authority.to_account_view().clone(); - views[1] = self.leaf_owner.to_account_view().clone(); - views[2] = self.leaf_owner.to_account_view().clone(); // leaf_delegate = leaf_owner - views[3] = self.merkle_tree.to_account_view().clone(); - views[4] = self.log_wrapper.to_account_view().clone(); - views[5] = self.compression_program.to_account_view().clone(); - views[6] = self.system_program.to_account_view().clone(); + views[0] = accounts.tree_authority.to_account_view().clone(); + views[1] = accounts.leaf_owner.to_account_view().clone(); + views[2] = accounts.leaf_owner.to_account_view().clone(); // leaf_delegate = leaf_owner + views[3] = accounts.merkle_tree.to_account_view().clone(); + views[4] = accounts.log_wrapper.to_account_view().clone(); + views[5] = accounts.compression_program.to_account_view().clone(); + views[6] = accounts.system_program.to_account_view().clone(); - for i in 0..proof_count { - views[7 + i] = proof_views[i].clone(); - } + for i in 0..proof_count { + views[7 + i] = proof_views[i].clone(); + } - let instruction = InstructionView { - program_id: &MPL_BUBBLEGUM_ID, - data: &ix_data, - accounts: &ix_accounts[..total_accounts], - }; + let instruction = InstructionView { + program_id: &MPL_BUBBLEGUM_ID, + data: &ix_data, + accounts: &ix_accounts[..total_accounts], + }; - solana_instruction_view::cpi::invoke_with_bounds::( - &instruction, - &views[..total_accounts], - ) - } + solana_instruction_view::cpi::invoke_with_bounds::( + &instruction, + &views[..total_accounts], + ) } diff --git a/compression/cnft-vault/quasar/src/instructions/withdraw.rs b/compression/cnft-vault/quasar/src/instructions/withdraw.rs index bbdaeaa23..7032f53ba 100644 --- a/compression/cnft-vault/quasar/src/instructions/withdraw.rs +++ b/compression/cnft-vault/quasar/src/instructions/withdraw.rs @@ -43,88 +43,86 @@ fn build_transfer_data(args: &[u8]) -> [u8; 8 + TRANSFER_ARGS_LEN] { ix_data } -impl Withdraw { - pub fn withdraw_cnft(&mut self, ctx: &CtxWithRemaining) -> Result<(), ProgramError> { - let data = ctx.data; - if data.len() < TRANSFER_ARGS_LEN { - return Err(ProgramError::InvalidInstructionData); - } - - let ix_data = build_transfer_data(&data[0..TRANSFER_ARGS_LEN]); - - // Collect proof nodes - let remaining = ctx.remaining_accounts(); - let placeholder = self.system_program.to_account_view().clone(); - let mut proof_views: [AccountView; MAX_PROOF_NODES] = - core::array::from_fn(|_| placeholder.clone()); - let mut proof_count = 0usize; - for result in remaining.iter() { - if proof_count >= MAX_PROOF_NODES { - break; - } - proof_views[proof_count] = result?; - proof_count += 1; - } +pub fn handle_withdraw_cnft(accounts: &mut Withdraw, ctx: &CtxWithRemaining) -> Result<(), ProgramError> { + let data = ctx.data; + if data.len() < TRANSFER_ARGS_LEN { + return Err(ProgramError::InvalidInstructionData); + } - let total_accounts = 8 + proof_count; - - // Build instruction account metas matching mpl-bubblegum Transfer layout: - // tree_config, leaf_owner (signer/PDA), leaf_delegate, new_leaf_owner, - // merkle_tree, log_wrapper, compression_program, system_program, then proofs. - let sys_addr = self.system_program.address(); - let mut ix_accounts: [InstructionAccount; MAX_CPI_ACCOUNTS] = - core::array::from_fn(|_| InstructionAccount::readonly(sys_addr)); - - ix_accounts[0] = InstructionAccount::readonly(self.tree_authority.address()); - ix_accounts[1] = InstructionAccount::readonly_signer(self.leaf_owner.address()); - // leaf_delegate = leaf_owner, not an additional signer - ix_accounts[2] = InstructionAccount::readonly(self.leaf_owner.address()); - ix_accounts[3] = InstructionAccount::readonly(self.new_leaf_owner.address()); - ix_accounts[4] = InstructionAccount::writable(self.merkle_tree.address()); - ix_accounts[5] = InstructionAccount::readonly(self.log_wrapper.address()); - ix_accounts[6] = InstructionAccount::readonly(self.compression_program.address()); - ix_accounts[7] = InstructionAccount::readonly(self.system_program.address()); - - for i in 0..proof_count { - ix_accounts[8 + i] = InstructionAccount::readonly(proof_views[i].address()); + let ix_data = build_transfer_data(&data[0..TRANSFER_ARGS_LEN]); + + // Collect proof nodes + let remaining = ctx.remaining_accounts(); + let placeholder = accounts.system_program.to_account_view().clone(); + let mut proof_views: [AccountView; MAX_PROOF_NODES] = + core::array::from_fn(|_| placeholder.clone()); + let mut proof_count = 0usize; + for result in remaining.iter() { + if proof_count >= MAX_PROOF_NODES { + break; } + proof_views[proof_count] = result?; + proof_count += 1; + } - // Build account views - let sys_view = self.system_program.to_account_view().clone(); - let mut views: [AccountView; MAX_CPI_ACCOUNTS] = - core::array::from_fn(|_| sys_view.clone()); - - views[0] = self.tree_authority.to_account_view().clone(); - views[1] = self.leaf_owner.to_account_view().clone(); - views[2] = self.leaf_owner.to_account_view().clone(); - views[3] = self.new_leaf_owner.to_account_view().clone(); - views[4] = self.merkle_tree.to_account_view().clone(); - views[5] = self.log_wrapper.to_account_view().clone(); - views[6] = self.compression_program.to_account_view().clone(); - views[7] = self.system_program.to_account_view().clone(); - - for i in 0..proof_count { - views[8 + i] = proof_views[i].clone(); - } + let total_accounts = 8 + proof_count; + + // Build instruction account metas matching mpl-bubblegum Transfer layout: + // tree_config, leaf_owner (signer/PDA), leaf_delegate, new_leaf_owner, + // merkle_tree, log_wrapper, compression_program, system_program, then proofs. + let sys_addr = accounts.system_program.address(); + let mut ix_accounts: [InstructionAccount; MAX_CPI_ACCOUNTS] = + core::array::from_fn(|_| InstructionAccount::readonly(sys_addr)); + + ix_accounts[0] = InstructionAccount::readonly(accounts.tree_authority.address()); + ix_accounts[1] = InstructionAccount::readonly_signer(accounts.leaf_owner.address()); + // leaf_delegate = leaf_owner, not an additional signer + ix_accounts[2] = InstructionAccount::readonly(accounts.leaf_owner.address()); + ix_accounts[3] = InstructionAccount::readonly(accounts.new_leaf_owner.address()); + ix_accounts[4] = InstructionAccount::writable(accounts.merkle_tree.address()); + ix_accounts[5] = InstructionAccount::readonly(accounts.log_wrapper.address()); + ix_accounts[6] = InstructionAccount::readonly(accounts.compression_program.address()); + ix_accounts[7] = InstructionAccount::readonly(accounts.system_program.address()); + + for i in 0..proof_count { + ix_accounts[8 + i] = InstructionAccount::readonly(proof_views[i].address()); + } - let instruction = InstructionView { - program_id: &MPL_BUBBLEGUM_ID, - data: &ix_data, - accounts: &ix_accounts[..total_accounts], - }; - - // PDA signer seeds: ["cNFT-vault", bump] - let bump_bytes = [ctx.bumps.leaf_owner]; - let seeds: [Seed; 2] = [ - Seed::from(b"cNFT-vault" as &[u8]), - Seed::from(&bump_bytes as &[u8]), - ]; - let signer = Signer::from(&seeds as &[Seed]); - - solana_instruction_view::cpi::invoke_signed_with_bounds::( - &instruction, - &views[..total_accounts], - &[signer], - ) + // Build account views + let sys_view = accounts.system_program.to_account_view().clone(); + let mut views: [AccountView; MAX_CPI_ACCOUNTS] = + core::array::from_fn(|_| sys_view.clone()); + + views[0] = accounts.tree_authority.to_account_view().clone(); + views[1] = accounts.leaf_owner.to_account_view().clone(); + views[2] = accounts.leaf_owner.to_account_view().clone(); + views[3] = accounts.new_leaf_owner.to_account_view().clone(); + views[4] = accounts.merkle_tree.to_account_view().clone(); + views[5] = accounts.log_wrapper.to_account_view().clone(); + views[6] = accounts.compression_program.to_account_view().clone(); + views[7] = accounts.system_program.to_account_view().clone(); + + for i in 0..proof_count { + views[8 + i] = proof_views[i].clone(); } + + let instruction = InstructionView { + program_id: &MPL_BUBBLEGUM_ID, + data: &ix_data, + accounts: &ix_accounts[..total_accounts], + }; + + // PDA signer seeds: ["cNFT-vault", bump] + let bump_bytes = [ctx.bumps.leaf_owner]; + let seeds: [Seed; 2] = [ + Seed::from(b"cNFT-vault" as &[u8]), + Seed::from(&bump_bytes as &[u8]), + ]; + let signer = Signer::from(&seeds as &[Seed]); + + solana_instruction_view::cpi::invoke_signed_with_bounds::( + &instruction, + &views[..total_accounts], + &[signer], + ) } diff --git a/compression/cnft-vault/quasar/src/instructions/withdraw_two.rs b/compression/cnft-vault/quasar/src/instructions/withdraw_two.rs index e717ae0fb..29f69142e 100644 --- a/compression/cnft-vault/quasar/src/instructions/withdraw_two.rs +++ b/compression/cnft-vault/quasar/src/instructions/withdraw_two.rs @@ -44,158 +44,156 @@ pub struct WithdrawTwo { pub system_program: Program, } -impl WithdrawTwo { - #[allow(clippy::too_many_lines)] - pub fn withdraw_two_cnfts(&mut self, ctx: &CtxWithRemaining) -> Result<(), ProgramError> { - // Parse instruction args: - // args1(108) + proof_1_length(1) + args2(108) + _proof_2_length(1) = 218 bytes - let data = ctx.data; - if data.len() < 218 { - return Err(ProgramError::InvalidInstructionData); +#[allow(clippy::too_many_lines)] +pub fn handle_withdraw_two_cnfts(accounts: &mut WithdrawTwo, ctx: &CtxWithRemaining) -> Result<(), ProgramError> { + // Parse instruction args: + // args1(108) + proof_1_length(1) + args2(108) + _proof_2_length(1) = 218 bytes + let data = ctx.data; + if data.len() < 218 { + return Err(ProgramError::InvalidInstructionData); + } + + let args1 = &data[0..TRANSFER_ARGS_LEN]; + let proof_1_length = data[TRANSFER_ARGS_LEN] as usize; + let args2 = &data[TRANSFER_ARGS_LEN + 1..TRANSFER_ARGS_LEN * 2 + 1]; + // _proof_2_length at data[217] — not needed, remaining after proof1 is proof2 + + // PDA signer seeds + let bump_bytes = [ctx.bumps.leaf_owner]; + let seeds: [Seed; 2] = [ + Seed::from(b"cNFT-vault" as &[u8]), + Seed::from(&bump_bytes as &[u8]), + ]; + let signer = Signer::from(&seeds as &[Seed]); + + // Collect all remaining accounts (proof1 ++ proof2) + let remaining = ctx.remaining_accounts(); + let placeholder = accounts.system_program.to_account_view().clone(); + let mut all_proofs: [AccountView; MAX_PROOF_NODES * 2] = + core::array::from_fn(|_| placeholder.clone()); + let mut total_proofs = 0usize; + for result in remaining.iter() { + if total_proofs >= MAX_PROOF_NODES * 2 { + break; + } + all_proofs[total_proofs] = result?; + total_proofs += 1; + } + + // Split into proof1 and proof2 + let proof1_count = proof_1_length.min(total_proofs); + let proof2_count = total_proofs.saturating_sub(proof1_count); + + // --- Withdraw cNFT #1 --- + log("withdrawing cNFT#1"); + { + let mut ix_data = [0u8; 8 + TRANSFER_ARGS_LEN]; + ix_data[0..8].copy_from_slice(&TRANSFER_DISCRIMINATOR); + ix_data[8..].copy_from_slice(args1); + + let total_accounts = 8 + proof1_count; + let sys_addr = accounts.system_program.address(); + let mut ix_accounts: [InstructionAccount; MAX_CPI_ACCOUNTS] = + core::array::from_fn(|_| InstructionAccount::readonly(sys_addr)); + + ix_accounts[0] = InstructionAccount::readonly(accounts.tree_authority1.address()); + ix_accounts[1] = InstructionAccount::readonly_signer(accounts.leaf_owner.address()); + ix_accounts[2] = InstructionAccount::readonly(accounts.leaf_owner.address()); + ix_accounts[3] = InstructionAccount::readonly(accounts.new_leaf_owner1.address()); + ix_accounts[4] = InstructionAccount::writable(accounts.merkle_tree1.address()); + ix_accounts[5] = InstructionAccount::readonly(accounts.log_wrapper.address()); + ix_accounts[6] = InstructionAccount::readonly(accounts.compression_program.address()); + ix_accounts[7] = InstructionAccount::readonly(accounts.system_program.address()); + + for i in 0..proof1_count { + ix_accounts[8 + i] = InstructionAccount::readonly(all_proofs[i].address()); } - let args1 = &data[0..TRANSFER_ARGS_LEN]; - let proof_1_length = data[TRANSFER_ARGS_LEN] as usize; - let args2 = &data[TRANSFER_ARGS_LEN + 1..TRANSFER_ARGS_LEN * 2 + 1]; - // _proof_2_length at data[217] — not needed, remaining after proof1 is proof2 - - // PDA signer seeds - let bump_bytes = [ctx.bumps.leaf_owner]; - let seeds: [Seed; 2] = [ - Seed::from(b"cNFT-vault" as &[u8]), - Seed::from(&bump_bytes as &[u8]), - ]; - let signer = Signer::from(&seeds as &[Seed]); - - // Collect all remaining accounts (proof1 ++ proof2) - let remaining = ctx.remaining_accounts(); - let placeholder = self.system_program.to_account_view().clone(); - let mut all_proofs: [AccountView; MAX_PROOF_NODES * 2] = - core::array::from_fn(|_| placeholder.clone()); - let mut total_proofs = 0usize; - for result in remaining.iter() { - if total_proofs >= MAX_PROOF_NODES * 2 { - break; - } - all_proofs[total_proofs] = result?; - total_proofs += 1; + let sys_view = accounts.system_program.to_account_view().clone(); + let mut views: [AccountView; MAX_CPI_ACCOUNTS] = + core::array::from_fn(|_| sys_view.clone()); + + views[0] = accounts.tree_authority1.to_account_view().clone(); + views[1] = accounts.leaf_owner.to_account_view().clone(); + views[2] = accounts.leaf_owner.to_account_view().clone(); + views[3] = accounts.new_leaf_owner1.to_account_view().clone(); + views[4] = accounts.merkle_tree1.to_account_view().clone(); + views[5] = accounts.log_wrapper.to_account_view().clone(); + views[6] = accounts.compression_program.to_account_view().clone(); + views[7] = accounts.system_program.to_account_view().clone(); + + for i in 0..proof1_count { + views[8 + i] = all_proofs[i].clone(); } - // Split into proof1 and proof2 - let proof1_count = proof_1_length.min(total_proofs); - let proof2_count = total_proofs.saturating_sub(proof1_count); - - // --- Withdraw cNFT #1 --- - log("withdrawing cNFT#1"); - { - let mut ix_data = [0u8; 8 + TRANSFER_ARGS_LEN]; - ix_data[0..8].copy_from_slice(&TRANSFER_DISCRIMINATOR); - ix_data[8..].copy_from_slice(args1); - - let total_accounts = 8 + proof1_count; - let sys_addr = self.system_program.address(); - let mut ix_accounts: [InstructionAccount; MAX_CPI_ACCOUNTS] = - core::array::from_fn(|_| InstructionAccount::readonly(sys_addr)); - - ix_accounts[0] = InstructionAccount::readonly(self.tree_authority1.address()); - ix_accounts[1] = InstructionAccount::readonly_signer(self.leaf_owner.address()); - ix_accounts[2] = InstructionAccount::readonly(self.leaf_owner.address()); - ix_accounts[3] = InstructionAccount::readonly(self.new_leaf_owner1.address()); - ix_accounts[4] = InstructionAccount::writable(self.merkle_tree1.address()); - ix_accounts[5] = InstructionAccount::readonly(self.log_wrapper.address()); - ix_accounts[6] = InstructionAccount::readonly(self.compression_program.address()); - ix_accounts[7] = InstructionAccount::readonly(self.system_program.address()); - - for i in 0..proof1_count { - ix_accounts[8 + i] = InstructionAccount::readonly(all_proofs[i].address()); - } - - let sys_view = self.system_program.to_account_view().clone(); - let mut views: [AccountView; MAX_CPI_ACCOUNTS] = - core::array::from_fn(|_| sys_view.clone()); - - views[0] = self.tree_authority1.to_account_view().clone(); - views[1] = self.leaf_owner.to_account_view().clone(); - views[2] = self.leaf_owner.to_account_view().clone(); - views[3] = self.new_leaf_owner1.to_account_view().clone(); - views[4] = self.merkle_tree1.to_account_view().clone(); - views[5] = self.log_wrapper.to_account_view().clone(); - views[6] = self.compression_program.to_account_view().clone(); - views[7] = self.system_program.to_account_view().clone(); - - for i in 0..proof1_count { - views[8 + i] = all_proofs[i].clone(); - } - - let instruction = InstructionView { - program_id: &MPL_BUBBLEGUM_ID, - data: &ix_data, - accounts: &ix_accounts[..total_accounts], - }; - - solana_instruction_view::cpi::invoke_signed_with_bounds::< - MAX_CPI_ACCOUNTS, - AccountView, - >(&instruction, &views[..total_accounts], &[signer.clone()])?; + let instruction = InstructionView { + program_id: &MPL_BUBBLEGUM_ID, + data: &ix_data, + accounts: &ix_accounts[..total_accounts], + }; + + solana_instruction_view::cpi::invoke_signed_with_bounds::< + MAX_CPI_ACCOUNTS, + AccountView, + >(&instruction, &views[..total_accounts], &[signer.clone()])?; + } + + // --- Withdraw cNFT #2 --- + log("withdrawing cNFT#2"); + { + let mut ix_data = [0u8; 8 + TRANSFER_ARGS_LEN]; + ix_data[0..8].copy_from_slice(&TRANSFER_DISCRIMINATOR); + ix_data[8..].copy_from_slice(args2); + + let total_accounts = 8 + proof2_count; + let sys_addr = accounts.system_program.address(); + let mut ix_accounts: [InstructionAccount; MAX_CPI_ACCOUNTS] = + core::array::from_fn(|_| InstructionAccount::readonly(sys_addr)); + + ix_accounts[0] = InstructionAccount::readonly(accounts.tree_authority2.address()); + ix_accounts[1] = InstructionAccount::readonly_signer(accounts.leaf_owner.address()); + ix_accounts[2] = InstructionAccount::readonly(accounts.leaf_owner.address()); + ix_accounts[3] = InstructionAccount::readonly(accounts.new_leaf_owner2.address()); + ix_accounts[4] = InstructionAccount::writable(accounts.merkle_tree2.address()); + ix_accounts[5] = InstructionAccount::readonly(accounts.log_wrapper.address()); + ix_accounts[6] = InstructionAccount::readonly(accounts.compression_program.address()); + ix_accounts[7] = InstructionAccount::readonly(accounts.system_program.address()); + + let proof2_start = proof1_count; + for i in 0..proof2_count { + ix_accounts[8 + i] = + InstructionAccount::readonly(all_proofs[proof2_start + i].address()); } - // --- Withdraw cNFT #2 --- - log("withdrawing cNFT#2"); - { - let mut ix_data = [0u8; 8 + TRANSFER_ARGS_LEN]; - ix_data[0..8].copy_from_slice(&TRANSFER_DISCRIMINATOR); - ix_data[8..].copy_from_slice(args2); - - let total_accounts = 8 + proof2_count; - let sys_addr = self.system_program.address(); - let mut ix_accounts: [InstructionAccount; MAX_CPI_ACCOUNTS] = - core::array::from_fn(|_| InstructionAccount::readonly(sys_addr)); - - ix_accounts[0] = InstructionAccount::readonly(self.tree_authority2.address()); - ix_accounts[1] = InstructionAccount::readonly_signer(self.leaf_owner.address()); - ix_accounts[2] = InstructionAccount::readonly(self.leaf_owner.address()); - ix_accounts[3] = InstructionAccount::readonly(self.new_leaf_owner2.address()); - ix_accounts[4] = InstructionAccount::writable(self.merkle_tree2.address()); - ix_accounts[5] = InstructionAccount::readonly(self.log_wrapper.address()); - ix_accounts[6] = InstructionAccount::readonly(self.compression_program.address()); - ix_accounts[7] = InstructionAccount::readonly(self.system_program.address()); - - let proof2_start = proof1_count; - for i in 0..proof2_count { - ix_accounts[8 + i] = - InstructionAccount::readonly(all_proofs[proof2_start + i].address()); - } - - let sys_view = self.system_program.to_account_view().clone(); - let mut views: [AccountView; MAX_CPI_ACCOUNTS] = - core::array::from_fn(|_| sys_view.clone()); - - views[0] = self.tree_authority2.to_account_view().clone(); - views[1] = self.leaf_owner.to_account_view().clone(); - views[2] = self.leaf_owner.to_account_view().clone(); - views[3] = self.new_leaf_owner2.to_account_view().clone(); - views[4] = self.merkle_tree2.to_account_view().clone(); - views[5] = self.log_wrapper.to_account_view().clone(); - views[6] = self.compression_program.to_account_view().clone(); - views[7] = self.system_program.to_account_view().clone(); - - for i in 0..proof2_count { - views[8 + i] = all_proofs[proof2_start + i].clone(); - } - - let instruction = InstructionView { - program_id: &MPL_BUBBLEGUM_ID, - data: &ix_data, - accounts: &ix_accounts[..total_accounts], - }; - - solana_instruction_view::cpi::invoke_signed_with_bounds::< - MAX_CPI_ACCOUNTS, - AccountView, - >(&instruction, &views[..total_accounts], &[signer])?; + let sys_view = accounts.system_program.to_account_view().clone(); + let mut views: [AccountView; MAX_CPI_ACCOUNTS] = + core::array::from_fn(|_| sys_view.clone()); + + views[0] = accounts.tree_authority2.to_account_view().clone(); + views[1] = accounts.leaf_owner.to_account_view().clone(); + views[2] = accounts.leaf_owner.to_account_view().clone(); + views[3] = accounts.new_leaf_owner2.to_account_view().clone(); + views[4] = accounts.merkle_tree2.to_account_view().clone(); + views[5] = accounts.log_wrapper.to_account_view().clone(); + views[6] = accounts.compression_program.to_account_view().clone(); + views[7] = accounts.system_program.to_account_view().clone(); + + for i in 0..proof2_count { + views[8 + i] = all_proofs[proof2_start + i].clone(); } - log("successfully sent cNFTs"); - Ok(()) + let instruction = InstructionView { + program_id: &MPL_BUBBLEGUM_ID, + data: &ix_data, + accounts: &ix_accounts[..total_accounts], + }; + + solana_instruction_view::cpi::invoke_signed_with_bounds::< + MAX_CPI_ACCOUNTS, + AccountView, + >(&instruction, &views[..total_accounts], &[signer])?; } + + log("successfully sent cNFTs"); + Ok(()) } diff --git a/compression/cutils/quasar/src/instructions/mint.rs b/compression/cutils/quasar/src/instructions/mint.rs index 29cde50e0..2cd5bacb5 100644 --- a/compression/cutils/quasar/src/instructions/mint.rs +++ b/compression/cutils/quasar/src/instructions/mint.rs @@ -53,76 +53,74 @@ pub struct Mint { pub system_program: Program, } -impl Mint { - pub fn mint(&mut self, ctx: &Ctx) -> Result<(), ProgramError> { - // Parse URI from instruction data: u32 length prefix + utf8 bytes (borsh String) - let data = ctx.data; - if data.len() < 4 { - return Err(ProgramError::InvalidInstructionData); - } - let uri_len = u32::from_le_bytes(data[0..4].try_into().unwrap()) as usize; - if data.len() < 4 + uri_len || uri_len > MAX_URI_LEN { - return Err(ProgramError::InvalidInstructionData); - } - let uri = &data[4..4 + uri_len]; +pub fn handle_mint(accounts: &mut Mint, ctx: &Ctx) -> Result<(), ProgramError> { + // Parse URI from instruction data: u32 length prefix + utf8 bytes (borsh String) + let data = ctx.data; + if data.len() < 4 { + return Err(ProgramError::InvalidInstructionData); + } + let uri_len = u32::from_le_bytes(data[0..4].try_into().unwrap()) as usize; + if data.len() < 4 + uri_len || uri_len > MAX_URI_LEN { + return Err(ProgramError::InvalidInstructionData); + } + let uri = &data[4..4 + uri_len]; - // Build CPI instruction data - let mut ix_data = [0u8; MAX_IX_DATA]; - let ix_len = encode_mint_to_collection_v1( - &mut ix_data, - uri, - self.collection_authority.address(), - self.collection_mint.address(), - ); + // Build CPI instruction data + let mut ix_data = [0u8; MAX_IX_DATA]; + let ix_len = encode_mint_to_collection_v1( + &mut ix_data, + uri, + accounts.collection_authority.address(), + accounts.collection_mint.address(), + ); - // Build instruction account metas matching MintToCollectionV1 layout - let ix_accounts: [InstructionAccount; MINT_CPI_ACCOUNTS] = [ - InstructionAccount::writable(self.tree_authority.address()), - InstructionAccount::readonly(self.leaf_owner.address()), - InstructionAccount::readonly(self.leaf_delegate.address()), - InstructionAccount::writable(self.merkle_tree.address()), - InstructionAccount::readonly_signer(self.payer.address()), - InstructionAccount::readonly_signer(self.tree_delegate.address()), - InstructionAccount::readonly_signer(self.collection_authority.address()), - InstructionAccount::readonly(self.collection_authority_record_pda.address()), - InstructionAccount::readonly(self.collection_mint.address()), - InstructionAccount::writable(self.collection_metadata.address()), - InstructionAccount::readonly(self.edition_account.address()), - InstructionAccount::readonly(self.bubblegum_signer.address()), - InstructionAccount::readonly(self.log_wrapper.address()), - InstructionAccount::readonly(self.compression_program.address()), - InstructionAccount::readonly(self.token_metadata_program.address()), - InstructionAccount::readonly(self.system_program.address()), - ]; + // Build instruction account metas matching MintToCollectionV1 layout + let ix_accounts: [InstructionAccount; MINT_CPI_ACCOUNTS] = [ + InstructionAccount::writable(accounts.tree_authority.address()), + InstructionAccount::readonly(accounts.leaf_owner.address()), + InstructionAccount::readonly(accounts.leaf_delegate.address()), + InstructionAccount::writable(accounts.merkle_tree.address()), + InstructionAccount::readonly_signer(accounts.payer.address()), + InstructionAccount::readonly_signer(accounts.tree_delegate.address()), + InstructionAccount::readonly_signer(accounts.collection_authority.address()), + InstructionAccount::readonly(accounts.collection_authority_record_pda.address()), + InstructionAccount::readonly(accounts.collection_mint.address()), + InstructionAccount::writable(accounts.collection_metadata.address()), + InstructionAccount::readonly(accounts.edition_account.address()), + InstructionAccount::readonly(accounts.bubblegum_signer.address()), + InstructionAccount::readonly(accounts.log_wrapper.address()), + InstructionAccount::readonly(accounts.compression_program.address()), + InstructionAccount::readonly(accounts.token_metadata_program.address()), + InstructionAccount::readonly(accounts.system_program.address()), + ]; - let views: [AccountView; MINT_CPI_ACCOUNTS] = [ - self.tree_authority.to_account_view().clone(), - self.leaf_owner.to_account_view().clone(), - self.leaf_delegate.to_account_view().clone(), - self.merkle_tree.to_account_view().clone(), - self.payer.to_account_view().clone(), - self.tree_delegate.to_account_view().clone(), - self.collection_authority.to_account_view().clone(), - self.collection_authority_record_pda.to_account_view().clone(), - self.collection_mint.to_account_view().clone(), - self.collection_metadata.to_account_view().clone(), - self.edition_account.to_account_view().clone(), - self.bubblegum_signer.to_account_view().clone(), - self.log_wrapper.to_account_view().clone(), - self.compression_program.to_account_view().clone(), - self.token_metadata_program.to_account_view().clone(), - self.system_program.to_account_view().clone(), - ]; + let views: [AccountView; MINT_CPI_ACCOUNTS] = [ + accounts.tree_authority.to_account_view().clone(), + accounts.leaf_owner.to_account_view().clone(), + accounts.leaf_delegate.to_account_view().clone(), + accounts.merkle_tree.to_account_view().clone(), + accounts.payer.to_account_view().clone(), + accounts.tree_delegate.to_account_view().clone(), + accounts.collection_authority.to_account_view().clone(), + accounts.collection_authority_record_pda.to_account_view().clone(), + accounts.collection_mint.to_account_view().clone(), + accounts.collection_metadata.to_account_view().clone(), + accounts.edition_account.to_account_view().clone(), + accounts.bubblegum_signer.to_account_view().clone(), + accounts.log_wrapper.to_account_view().clone(), + accounts.compression_program.to_account_view().clone(), + accounts.token_metadata_program.to_account_view().clone(), + accounts.system_program.to_account_view().clone(), + ]; - let instruction = InstructionView { - program_id: &MPL_BUBBLEGUM_ID, - data: &ix_data[..ix_len], - accounts: &ix_accounts, - }; + let instruction = InstructionView { + program_id: &MPL_BUBBLEGUM_ID, + data: &ix_data[..ix_len], + accounts: &ix_accounts, + }; - solana_instruction_view::cpi::invoke::( - &instruction, - &views, - ) - } + solana_instruction_view::cpi::invoke::( + &instruction, + &views, + ) } diff --git a/compression/cutils/quasar/src/instructions/verify.rs b/compression/cutils/quasar/src/instructions/verify.rs index 20df0fcdf..cedc13c68 100644 --- a/compression/cutils/quasar/src/instructions/verify.rs +++ b/compression/cutils/quasar/src/instructions/verify.rs @@ -24,84 +24,82 @@ pub struct Verify { pub compression_program: UncheckedAccount, } -impl Verify { - pub fn verify(&mut self, ctx: &CtxWithRemaining) -> Result<(), ProgramError> { - // Parse verify params from instruction data: - // root(32) + data_hash(32) + creator_hash(32) + nonce(8) + index(4) = 108 bytes - let data = ctx.data; - if data.len() < 108 { - return Err(ProgramError::InvalidInstructionData); - } +pub fn handle_verify(accounts: &mut Verify, ctx: &CtxWithRemaining) -> Result<(), ProgramError> { + // Parse verify params from instruction data: + // root(32) + data_hash(32) + creator_hash(32) + nonce(8) + index(4) = 108 bytes + let data = ctx.data; + if data.len() < 108 { + return Err(ProgramError::InvalidInstructionData); + } - let root: [u8; 32] = data[0..32].try_into().unwrap(); - let data_hash: [u8; 32] = data[32..64].try_into().unwrap(); - let creator_hash: [u8; 32] = data[64..96].try_into().unwrap(); - let nonce = u64::from_le_bytes(data[96..104].try_into().unwrap()); - let index = u32::from_le_bytes(data[104..108].try_into().unwrap()); - - // Compute asset ID and leaf hash - let asset_id = get_asset_id(self.merkle_tree.address(), nonce); - let leaf_hash = leaf_schema_v1_hash( - &asset_id, - self.leaf_owner.address(), - self.leaf_delegate.address(), - nonce, - &data_hash, - &creator_hash, - ); - - // Build verify_leaf instruction data: discriminator(8) + root(32) + leaf(32) + index(4) = 76 - let mut ix_data = [0u8; 76]; - ix_data[0..8].copy_from_slice(&VERIFY_LEAF_DISCRIMINATOR); - ix_data[8..40].copy_from_slice(&root); - ix_data[40..72].copy_from_slice(&leaf_hash); - ix_data[72..76].copy_from_slice(&index.to_le_bytes()); - - // Collect proof nodes - let remaining = ctx.remaining_accounts(); - let placeholder = self.compression_program.to_account_view().clone(); - let mut proof_views: [AccountView; MAX_PROOF_NODES] = - core::array::from_fn(|_| placeholder.clone()); - let mut proof_count = 0usize; - for result in remaining.iter() { - if proof_count >= MAX_PROOF_NODES { - break; - } - proof_views[proof_count] = result?; - proof_count += 1; + let root: [u8; 32] = data[0..32].try_into().unwrap(); + let data_hash: [u8; 32] = data[32..64].try_into().unwrap(); + let creator_hash: [u8; 32] = data[64..96].try_into().unwrap(); + let nonce = u64::from_le_bytes(data[96..104].try_into().unwrap()); + let index = u32::from_le_bytes(data[104..108].try_into().unwrap()); + + // Compute asset ID and leaf hash + let asset_id = get_asset_id(accounts.merkle_tree.address(), nonce); + let leaf_hash = leaf_schema_v1_hash( + &asset_id, + accounts.leaf_owner.address(), + accounts.leaf_delegate.address(), + nonce, + &data_hash, + &creator_hash, + ); + + // Build verify_leaf instruction data: discriminator(8) + root(32) + leaf(32) + index(4) = 76 + let mut ix_data = [0u8; 76]; + ix_data[0..8].copy_from_slice(&VERIFY_LEAF_DISCRIMINATOR); + ix_data[8..40].copy_from_slice(&root); + ix_data[40..72].copy_from_slice(&leaf_hash); + ix_data[72..76].copy_from_slice(&index.to_le_bytes()); + + // Collect proof nodes + let remaining = ctx.remaining_accounts(); + let placeholder = accounts.compression_program.to_account_view().clone(); + let mut proof_views: [AccountView; MAX_PROOF_NODES] = + core::array::from_fn(|_| placeholder.clone()); + let mut proof_count = 0usize; + for result in remaining.iter() { + if proof_count >= MAX_PROOF_NODES { + break; } + proof_views[proof_count] = result?; + proof_count += 1; + } - let total_accounts = 1 + proof_count; + let total_accounts = 1 + proof_count; - // Build instruction accounts: merkle_tree + proof nodes - let tree_addr = self.merkle_tree.address(); - let mut ix_accounts: [InstructionAccount; MAX_CPI_ACCOUNTS] = - core::array::from_fn(|_| InstructionAccount::readonly(tree_addr)); + // Build instruction accounts: merkle_tree + proof nodes + let tree_addr = accounts.merkle_tree.address(); + let mut ix_accounts: [InstructionAccount; MAX_CPI_ACCOUNTS] = + core::array::from_fn(|_| InstructionAccount::readonly(tree_addr)); - ix_accounts[0] = InstructionAccount::readonly(self.merkle_tree.address()); - for i in 0..proof_count { - ix_accounts[1 + i] = InstructionAccount::readonly(proof_views[i].address()); - } + ix_accounts[0] = InstructionAccount::readonly(accounts.merkle_tree.address()); + for i in 0..proof_count { + ix_accounts[1 + i] = InstructionAccount::readonly(proof_views[i].address()); + } - // Build account views - let tree_view = self.merkle_tree.to_account_view().clone(); - let mut views: [AccountView; MAX_CPI_ACCOUNTS] = - core::array::from_fn(|_| tree_view.clone()); + // Build account views + let tree_view = accounts.merkle_tree.to_account_view().clone(); + let mut views: [AccountView; MAX_CPI_ACCOUNTS] = + core::array::from_fn(|_| tree_view.clone()); - views[0] = self.merkle_tree.to_account_view().clone(); - for i in 0..proof_count { - views[1 + i] = proof_views[i].clone(); - } + views[0] = accounts.merkle_tree.to_account_view().clone(); + for i in 0..proof_count { + views[1 + i] = proof_views[i].clone(); + } - let instruction = InstructionView { - program_id: self.compression_program.address(), - data: &ix_data, - accounts: &ix_accounts[..total_accounts], - }; + let instruction = InstructionView { + program_id: accounts.compression_program.address(), + data: &ix_data, + accounts: &ix_accounts[..total_accounts], + }; - solana_instruction_view::cpi::invoke_with_bounds::( - &instruction, - &views[..total_accounts], - ) - } + solana_instruction_view::cpi::invoke_with_bounds::( + &instruction, + &views[..total_accounts], + ) } diff --git a/oracles/pyth/quasar/src/instructions/read_price.rs b/oracles/pyth/quasar/src/instructions/read_price.rs index 2afa802a0..9a150e6a4 100644 --- a/oracles/pyth/quasar/src/instructions/read_price.rs +++ b/oracles/pyth/quasar/src/instructions/read_price.rs @@ -24,47 +24,45 @@ pub struct ReadPrice { pub price_update: UncheckedAccount, } -impl ReadPrice { - #[inline(always)] - pub fn read_price(&mut self) -> Result<(), ProgramError> { - let view = self.price_update.to_account_view(); - let data = view.data(); +#[inline(always)] +pub fn handle_read_price(accounts: &mut ReadPrice) -> Result<(), ProgramError> { + let view = accounts.price_update.to_account_view(); + let data = view.data(); - if data.len() < MIN_DATA_LEN { - return Err(ProgramError::InvalidAccountData); - } + if data.len() < MIN_DATA_LEN { + return Err(ProgramError::InvalidAccountData); + } - let price = i64::from_le_bytes( - data[PRICE_OFFSET..PRICE_OFFSET + 8] - .try_into() - .map_err(|_| ProgramError::InvalidAccountData)?, - ); - let conf = u64::from_le_bytes( - data[CONF_OFFSET..CONF_OFFSET + 8] - .try_into() - .map_err(|_| ProgramError::InvalidAccountData)?, - ); - let exponent = i32::from_le_bytes( - data[EXPONENT_OFFSET..EXPONENT_OFFSET + 4] - .try_into() - .map_err(|_| ProgramError::InvalidAccountData)?, - ); - let publish_time = i64::from_le_bytes( - data[PUBLISH_TIME_OFFSET..PUBLISH_TIME_OFFSET + 8] - .try_into() - .map_err(|_| ProgramError::InvalidAccountData)?, - ); + let price = i64::from_le_bytes( + data[PRICE_OFFSET..PRICE_OFFSET + 8] + .try_into() + .map_err(|_| ProgramError::InvalidAccountData)?, + ); + let conf = u64::from_le_bytes( + data[CONF_OFFSET..CONF_OFFSET + 8] + .try_into() + .map_err(|_| ProgramError::InvalidAccountData)?, + ); + let exponent = i32::from_le_bytes( + data[EXPONENT_OFFSET..EXPONENT_OFFSET + 4] + .try_into() + .map_err(|_| ProgramError::InvalidAccountData)?, + ); + let publish_time = i64::from_le_bytes( + data[PUBLISH_TIME_OFFSET..PUBLISH_TIME_OFFSET + 8] + .try_into() + .map_err(|_| ProgramError::InvalidAccountData)?, + ); - log("Pyth price feed data:"); - log(" price (raw):"); - log_64(price as u64); - log(" confidence:"); - log_64(conf); - log(" exponent:"); - log_64(exponent as u64); - log(" publish_time:"); - log_64(publish_time as u64); + log("Pyth price feed data:"); + log(" price (raw):"); + log_64(price as u64); + log(" confidence:"); + log_64(conf); + log(" exponent:"); + log_64(exponent as u64); + log(" publish_time:"); + log_64(publish_time as u64); - Ok(()) - } + Ok(()) } diff --git a/tokens/create-token/quasar/src/lib.rs b/tokens/create-token/quasar/src/lib.rs index 12cd4b748..fdb9fe606 100644 --- a/tokens/create-token/quasar/src/lib.rs +++ b/tokens/create-token/quasar/src/lib.rs @@ -20,13 +20,13 @@ mod quasar_create_token { /// Create a new token mint (account init handled by Quasar's `#[account(init)]`). #[instruction(discriminator = 0)] pub fn create_token(ctx: Ctx, _decimals: u8) -> Result<(), ProgramError> { - ctx.accounts.create_token() + handle_create_token(&mut ctx.accounts) } /// Mint tokens to the creator's token account. #[instruction(discriminator = 1)] pub fn mint_tokens(ctx: Ctx, amount: u64) -> Result<(), ProgramError> { - ctx.accounts.mint_tokens(amount) + handle_mint_tokens(&mut ctx.accounts, amount) } } @@ -43,14 +43,6 @@ pub struct CreateToken { pub system_program: Program, } -impl CreateToken { - #[inline(always)] - pub fn create_token(&mut self) -> Result<(), ProgramError> { - // Mint account is created and initialised by Quasar's account init. - Ok(()) - } -} - /// Accounts for minting tokens to an existing token account. #[derive(Accounts)] pub struct MintTokens { @@ -63,11 +55,15 @@ pub struct MintTokens { pub token_program: Program, } -impl MintTokens { - #[inline(always)] - pub fn mint_tokens(&mut self, amount: u64) -> Result<(), ProgramError> { - self.token_program - .mint_to(&self.mint, &self.token_account, &self.authority, amount) - .invoke() - } +#[inline(always)] +fn handle_create_token(_accounts: &mut CreateToken) -> Result<(), ProgramError> { + // Mint account is created and initialised by Quasar's account init. + Ok(()) +} + +#[inline(always)] +fn handle_mint_tokens(accounts: &mut MintTokens, amount: u64) -> Result<(), ProgramError> { + accounts.token_program + .mint_to(&accounts.mint, &accounts.token_account, &accounts.authority, amount) + .invoke() } diff --git a/tokens/escrow/quasar/src/instructions/make.rs b/tokens/escrow/quasar/src/instructions/make.rs index 5e26d495e..c26987f03 100644 --- a/tokens/escrow/quasar/src/instructions/make.rs +++ b/tokens/escrow/quasar/src/instructions/make.rs @@ -23,24 +23,22 @@ pub struct Make { pub system_program: Program, } -impl Make { - #[inline(always)] - pub fn make_escrow(&mut self, receive: u64, bumps: &MakeBumps) -> Result<(), ProgramError> { - self.escrow.set_inner( - *self.maker.address(), - *self.mint_a.address(), - *self.mint_b.address(), - *self.maker_ta_b.address(), - receive, - bumps.escrow, - ); - Ok(()) - } +#[inline(always)] +pub fn handle_make_escrow(accounts: &mut Make, receive: u64, bumps: &MakeBumps) -> Result<(), ProgramError> { + accounts.escrow.set_inner( + *accounts.maker.address(), + *accounts.mint_a.address(), + *accounts.mint_b.address(), + *accounts.maker_ta_b.address(), + receive, + bumps.escrow, + ); + Ok(()) +} - #[inline(always)] - pub fn deposit_tokens(&mut self, amount: u64) -> Result<(), ProgramError> { - self.token_program - .transfer(&self.maker_ta_a, &self.vault_ta_a, &self.maker, amount) - .invoke() - } +#[inline(always)] +pub fn handle_deposit_tokens(accounts: &mut Make, amount: u64) -> Result<(), ProgramError> { + accounts.token_program + .transfer(&accounts.maker_ta_a, &accounts.vault_ta_a, &accounts.maker, amount) + .invoke() } diff --git a/tokens/escrow/quasar/src/instructions/refund.rs b/tokens/escrow/quasar/src/instructions/refund.rs index 14474f168..d1e5d1f5b 100644 --- a/tokens/escrow/quasar/src/instructions/refund.rs +++ b/tokens/escrow/quasar/src/instructions/refund.rs @@ -26,23 +26,21 @@ pub struct Refund { pub system_program: Program, } -impl Refund { - #[inline(always)] - pub fn withdraw_tokens_and_close(&mut self, bumps: &RefundBumps) -> Result<(), ProgramError> { - let seeds = self.escrow_seeds(bumps); +#[inline(always)] +pub fn handle_withdraw_tokens_and_close_refund(accounts: &mut Refund, bumps: &RefundBumps) -> Result<(), ProgramError> { + let seeds = accounts.escrow_seeds(bumps); - self.token_program - .transfer( - &self.vault_ta_a, - &self.maker_ta_a, - &self.escrow, - self.vault_ta_a.amount(), - ) - .invoke_signed(&seeds)?; + accounts.token_program + .transfer( + &accounts.vault_ta_a, + &accounts.maker_ta_a, + &accounts.escrow, + accounts.vault_ta_a.amount(), + ) + .invoke_signed(&seeds)?; - self.token_program - .close_account(&self.vault_ta_a, &self.maker, &self.escrow) - .invoke_signed(&seeds)?; - Ok(()) - } + accounts.token_program + .close_account(&accounts.vault_ta_a, &accounts.maker, &accounts.escrow) + .invoke_signed(&seeds)?; + Ok(()) } diff --git a/tokens/escrow/quasar/src/instructions/take.rs b/tokens/escrow/quasar/src/instructions/take.rs index 11024a4eb..78b8e695c 100644 --- a/tokens/escrow/quasar/src/instructions/take.rs +++ b/tokens/escrow/quasar/src/instructions/take.rs @@ -35,35 +35,33 @@ pub struct Take { pub system_program: Program, } -impl Take { - #[inline(always)] - pub fn transfer_tokens(&mut self) -> Result<(), ProgramError> { - self.token_program - .transfer( - &self.taker_ta_b, - &self.maker_ta_b, - &self.taker, - self.escrow.receive, - ) - .invoke() - } +#[inline(always)] +pub fn handle_transfer_tokens(accounts: &mut Take) -> Result<(), ProgramError> { + accounts.token_program + .transfer( + &accounts.taker_ta_b, + &accounts.maker_ta_b, + &accounts.taker, + accounts.escrow.receive, + ) + .invoke() +} - #[inline(always)] - pub fn withdraw_tokens_and_close(&mut self, bumps: &TakeBumps) -> Result<(), ProgramError> { - let seeds = self.escrow_seeds(bumps); +#[inline(always)] +pub fn handle_withdraw_tokens_and_close_take(accounts: &mut Take, bumps: &TakeBumps) -> Result<(), ProgramError> { + let seeds = accounts.escrow_seeds(bumps); - self.token_program - .transfer( - &self.vault_ta_a, - &self.taker_ta_a, - &self.escrow, - self.vault_ta_a.amount(), - ) - .invoke_signed(&seeds)?; + accounts.token_program + .transfer( + &accounts.vault_ta_a, + &accounts.taker_ta_a, + &accounts.escrow, + accounts.vault_ta_a.amount(), + ) + .invoke_signed(&seeds)?; - self.token_program - .close_account(&self.vault_ta_a, &self.taker, &self.escrow) - .invoke_signed(&seeds)?; - Ok(()) - } + accounts.token_program + .close_account(&accounts.vault_ta_a, &accounts.taker, &accounts.escrow) + .invoke_signed(&seeds)?; + Ok(()) } diff --git a/tokens/external-delegate-token-master/quasar/src/lib.rs b/tokens/external-delegate-token-master/quasar/src/lib.rs index 985e841a9..265c1ef69 100644 --- a/tokens/external-delegate-token-master/quasar/src/lib.rs +++ b/tokens/external-delegate-token-master/quasar/src/lib.rs @@ -24,7 +24,7 @@ mod quasar_external_delegate_token_master { /// Initialize a user account with zero Ethereum address. #[instruction(discriminator = 0)] pub fn initialize(ctx: Ctx) -> Result<(), ProgramError> { - ctx.accounts.initialize() + handle_initialize(&mut ctx.accounts) } /// Set the Ethereum address for signature verification. @@ -33,7 +33,7 @@ mod quasar_external_delegate_token_master { ctx: Ctx, ethereum_address: [u8; 20], ) -> Result<(), ProgramError> { - ctx.accounts.set_ethereum_address(ethereum_address) + handle_set_ethereum_address(&mut ctx.accounts, ethereum_address) } /// Transfer tokens using an Ethereum signature for authorisation. @@ -44,7 +44,7 @@ mod quasar_external_delegate_token_master { signature: [u8; 65], message: [u8; 32], ) -> Result<(), ProgramError> { - ctx.accounts.transfer_tokens(amount, &signature, &message, &ctx.bumps) + handle_transfer_tokens(&mut ctx.accounts, amount, &signature, &message, &ctx.bumps) } /// Transfer tokens using the Solana authority directly. @@ -53,7 +53,7 @@ mod quasar_external_delegate_token_master { ctx: Ctx, amount: u64, ) -> Result<(), ProgramError> { - ctx.accounts.authority_transfer(amount, &ctx.bumps) + handle_authority_transfer(&mut ctx.accounts, amount, &ctx.bumps) } } @@ -70,13 +70,11 @@ pub struct Initialize { pub system_program: Program, } -impl Initialize { - #[inline(always)] - pub fn initialize(&mut self) -> Result<(), ProgramError> { - self.user_account - .set_inner(*self.authority.address(), [0u8; 20]); - Ok(()) - } +#[inline(always)] +fn handle_initialize(accounts: &mut Initialize) -> Result<(), ProgramError> { + accounts.user_account + .set_inner(*accounts.authority.address(), [0u8; 20]); + Ok(()) } #[derive(Accounts)] @@ -86,17 +84,15 @@ pub struct SetEthereumAddress { pub authority: Signer, } -impl SetEthereumAddress { - #[inline(always)] - pub fn set_ethereum_address(&mut self, ethereum_address: [u8; 20]) -> Result<(), ProgramError> { - require_keys_eq!( - self.user_account.authority, - *self.authority.address(), - ProgramError::MissingRequiredSignature - ); - self.user_account.ethereum_address = ethereum_address; - Ok(()) - } +#[inline(always)] +fn handle_set_ethereum_address(accounts: &mut SetEthereumAddress, ethereum_address: [u8; 20]) -> Result<(), ProgramError> { + require_keys_eq!( + accounts.user_account.authority, + *accounts.authority.address(), + ProgramError::MissingRequiredSignature + ); + accounts.user_account.ethereum_address = ethereum_address; + Ok(()) } #[derive(Accounts)] @@ -113,38 +109,36 @@ pub struct TransferTokens { pub token_program: Program, } -impl TransferTokens { - #[inline(always)] - pub fn transfer_tokens( - &mut self, - amount: u64, - signature: &[u8; 65], - message: &[u8; 32], - bumps: &TransferTokensBumps, - ) -> Result<(), ProgramError> { - if !verify_ethereum_signature( - &self.user_account.ethereum_address, - message, - signature, - ) { - return Err(ProgramError::Custom(1)); // InvalidSignature - } - - let bump = [bumps.user_pda]; - let seeds: &[Seed] = &[ - Seed::from(self.user_account.address().as_ref()), - Seed::from(&bump as &[u8]), - ]; - - self.token_program - .transfer( - &self.user_token_account, - &self.recipient_token_account, - &self.user_pda, - amount, - ) - .invoke_signed(seeds) +#[inline(always)] +fn handle_transfer_tokens( + accounts: &mut TransferTokens, + amount: u64, + signature: &[u8; 65], + message: &[u8; 32], + bumps: &TransferTokensBumps, +) -> Result<(), ProgramError> { + if !verify_ethereum_signature( + &accounts.user_account.ethereum_address, + message, + signature, + ) { + return Err(ProgramError::Custom(1)); // InvalidSignature } + + let bump = [bumps.user_pda]; + let seeds: &[Seed] = &[ + Seed::from(accounts.user_account.address().as_ref()), + Seed::from(&bump as &[u8]), + ]; + + accounts.token_program + .transfer( + &accounts.user_token_account, + &accounts.recipient_token_account, + &accounts.user_pda, + amount, + ) + .invoke_signed(seeds) } #[derive(Accounts)] @@ -161,30 +155,28 @@ pub struct AuthorityTransfer { pub token_program: Program, } -impl AuthorityTransfer { - #[inline(always)] - pub fn authority_transfer(&mut self, amount: u64, bumps: &AuthorityTransferBumps) -> Result<(), ProgramError> { - require_keys_eq!( - self.user_account.authority, - *self.authority.address(), - ProgramError::MissingRequiredSignature - ); - - let bump = [bumps.user_pda]; - let seeds: &[Seed] = &[ - Seed::from(self.user_account.address().as_ref()), - Seed::from(&bump as &[u8]), - ]; - - self.token_program - .transfer( - &self.user_token_account, - &self.recipient_token_account, - &self.user_pda, - amount, - ) - .invoke_signed(seeds) - } +#[inline(always)] +fn handle_authority_transfer(accounts: &mut AuthorityTransfer, amount: u64, bumps: &AuthorityTransferBumps) -> Result<(), ProgramError> { + require_keys_eq!( + accounts.user_account.authority, + *accounts.authority.address(), + ProgramError::MissingRequiredSignature + ); + + let bump = [bumps.user_pda]; + let seeds: &[Seed] = &[ + Seed::from(accounts.user_account.address().as_ref()), + Seed::from(&bump as &[u8]), + ]; + + accounts.token_program + .transfer( + &accounts.user_token_account, + &accounts.recipient_token_account, + &accounts.user_pda, + amount, + ) + .invoke_signed(seeds) } // --------------------------------------------------------------------------- diff --git a/tokens/nft-minter/quasar/src/lib.rs b/tokens/nft-minter/quasar/src/lib.rs index fc6ce08c1..a629f389a 100644 --- a/tokens/nft-minter/quasar/src/lib.rs +++ b/tokens/nft-minter/quasar/src/lib.rs @@ -24,7 +24,7 @@ mod quasar_nft_minter { nft_symbol: String, nft_uri: String, ) -> Result<(), ProgramError> { - ctx.accounts.mint_nft(&nft_name, &nft_symbol, &nft_uri) + handle_mint_nft(&mut ctx.accounts, &nft_name, &nft_symbol, &nft_uri) } } @@ -56,63 +56,61 @@ pub struct MintNft { pub rent: Sysvar, } -impl MintNft { - #[inline(always)] - pub fn mint_nft( - &mut self, - nft_name: &str, - nft_symbol: &str, - nft_uri: &str, - ) -> Result<(), ProgramError> { - // 1. Mint one token to the associated token account. - log("Minting token"); - self.token_program - .mint_to( - &self.mint_account, - &self.associated_token_account, - &self.payer, - 1u64, - ) - .invoke()?; +#[inline(always)] +fn handle_mint_nft( + accounts: &mut MintNft, + nft_name: &str, + nft_symbol: &str, + nft_uri: &str, +) -> Result<(), ProgramError> { + // 1. Mint one token to the associated token account. + log("Minting token"); + accounts.token_program + .mint_to( + &accounts.mint_account, + &accounts.associated_token_account, + &accounts.payer, + 1u64, + ) + .invoke()?; - // 2. Create Metaplex metadata account. - log("Creating metadata account"); - self.token_metadata_program - .create_metadata_accounts_v3( - &self.metadata_account, - &self.mint_account, - &self.payer, - &self.payer, - &self.payer, - &self.system_program, - &self.rent, - nft_name, - nft_symbol, - nft_uri, - 0, // seller_fee_basis_points - false, // is_mutable - true, // update_authority_is_signer - ) - .invoke()?; + // 2. Create Metaplex metadata account. + log("Creating metadata account"); + accounts.token_metadata_program + .create_metadata_accounts_v3( + &accounts.metadata_account, + &accounts.mint_account, + &accounts.payer, + &accounts.payer, + &accounts.payer, + &accounts.system_program, + &accounts.rent, + nft_name, + nft_symbol, + nft_uri, + 0, // seller_fee_basis_points + false, // is_mutable + true, // update_authority_is_signer + ) + .invoke()?; - // 3. Create master edition (makes it a verified NFT). - log("Creating master edition account"); - self.token_metadata_program - .create_master_edition_v3( - &self.edition_account, - &self.mint_account, - &self.payer, // update_authority - &self.payer, // mint_authority - &self.payer, // payer - &self.metadata_account, - &self.token_program, - &self.system_program, - &self.rent, - None, // max_supply = unlimited - ) - .invoke()?; + // 3. Create master edition (makes it a verified NFT). + log("Creating master edition account"); + accounts.token_metadata_program + .create_master_edition_v3( + &accounts.edition_account, + &accounts.mint_account, + &accounts.payer, // update_authority + &accounts.payer, // mint_authority + &accounts.payer, // payer + &accounts.metadata_account, + &accounts.token_program, + &accounts.system_program, + &accounts.rent, + None, // max_supply = unlimited + ) + .invoke()?; - log("NFT minted successfully."); - Ok(()) - } + log("NFT minted successfully."); + Ok(()) } diff --git a/tokens/nft-operations/quasar/src/instructions/create_collection.rs b/tokens/nft-operations/quasar/src/instructions/create_collection.rs index c41c16a7a..e0f2032a7 100644 --- a/tokens/nft-operations/quasar/src/instructions/create_collection.rs +++ b/tokens/nft-operations/quasar/src/instructions/create_collection.rs @@ -31,58 +31,56 @@ pub struct CreateCollection { pub rent: Sysvar, } -impl CreateCollection { - #[inline(always)] - pub fn create_collection(&mut self, bumps: &CreateCollectionBumps) -> Result<(), ProgramError> { - let bump = [bumps.mint_authority]; - let seeds: &[Seed] = &[ - Seed::from(b"authority" as &[u8]), - Seed::from(&bump as &[u8]), - ]; +#[inline(always)] +pub fn handle_create_collection(accounts: &mut CreateCollection, bumps: &CreateCollectionBumps) -> Result<(), ProgramError> { + let bump = [bumps.mint_authority]; + let seeds: &[Seed] = &[ + Seed::from(b"authority" as &[u8]), + Seed::from(&bump as &[u8]), + ]; - // Mint 1 token to the destination. - self.token_program - .mint_to(&self.mint, &self.destination, &self.mint_authority, 1u64) - .invoke_signed(seeds)?; - log("Collection NFT minted!"); + // Mint 1 token to the destination. + accounts.token_program + .mint_to(&accounts.mint, &accounts.destination, &accounts.mint_authority, 1u64) + .invoke_signed(seeds)?; + log("Collection NFT minted!"); - // Create metadata account. - self.token_metadata_program - .create_metadata_accounts_v3( - &self.metadata, - &self.mint, - &self.mint_authority, - &self.user, - &self.mint_authority, - &self.system_program, - &self.rent, - "DummyCollection", - "DC", - "", - 0, // seller_fee_basis_points - true, // is_mutable - true, // update_authority_is_signer - ) - .invoke_signed(seeds)?; - log("Metadata Account created!"); + // Create metadata account. + accounts.token_metadata_program + .create_metadata_accounts_v3( + &accounts.metadata, + &accounts.mint, + &accounts.mint_authority, + &accounts.user, + &accounts.mint_authority, + &accounts.system_program, + &accounts.rent, + "DummyCollection", + "DC", + "", + 0, // seller_fee_basis_points + true, // is_mutable + true, // update_authority_is_signer + ) + .invoke_signed(seeds)?; + log("Metadata Account created!"); - // Create master edition. - self.token_metadata_program - .create_master_edition_v3( - &self.master_edition, - &self.mint, - &self.mint_authority, // update_authority - &self.mint_authority, // mint_authority - &self.user, // payer - &self.metadata, - &self.token_program, - &self.system_program, - &self.rent, - Some(0), // max_supply = 0 means unique 1/1 - ) - .invoke_signed(seeds)?; - log("Master Edition Account created"); + // Create master edition. + accounts.token_metadata_program + .create_master_edition_v3( + &accounts.master_edition, + &accounts.mint, + &accounts.mint_authority, // update_authority + &accounts.mint_authority, // mint_authority + &accounts.user, // payer + &accounts.metadata, + &accounts.token_program, + &accounts.system_program, + &accounts.rent, + Some(0), // max_supply = 0 means unique 1/1 + ) + .invoke_signed(seeds)?; + log("Master Edition Account created"); - Ok(()) - } + Ok(()) } diff --git a/tokens/nft-operations/quasar/src/instructions/mint_nft.rs b/tokens/nft-operations/quasar/src/instructions/mint_nft.rs index 7ca8b92d8..b0a16944b 100644 --- a/tokens/nft-operations/quasar/src/instructions/mint_nft.rs +++ b/tokens/nft-operations/quasar/src/instructions/mint_nft.rs @@ -32,58 +32,56 @@ pub struct MintNft { pub rent: Sysvar, } -impl MintNft { - #[inline(always)] - pub fn mint_nft(&mut self, bumps: &MintNftBumps) -> Result<(), ProgramError> { - let bump = [bumps.mint_authority]; - let seeds: &[Seed] = &[ - Seed::from(b"authority" as &[u8]), - Seed::from(&bump as &[u8]), - ]; +#[inline(always)] +pub fn handle_mint_nft(accounts: &mut MintNft, bumps: &MintNftBumps) -> Result<(), ProgramError> { + let bump = [bumps.mint_authority]; + let seeds: &[Seed] = &[ + Seed::from(b"authority" as &[u8]), + Seed::from(&bump as &[u8]), + ]; - // Mint 1 token to the destination. - self.token_program - .mint_to(&self.mint, &self.destination, &self.mint_authority, 1u64) - .invoke_signed(seeds)?; - log("NFT minted!"); + // Mint 1 token to the destination. + accounts.token_program + .mint_to(&accounts.mint, &accounts.destination, &accounts.mint_authority, 1u64) + .invoke_signed(seeds)?; + log("NFT minted!"); - // Create metadata with collection reference. - // Note: The collection is set as unverified here; call verify_collection - // separately to verify it. - self.token_metadata_program - .create_metadata_accounts_v3( - &self.metadata, - &self.mint, - &self.mint_authority, - &self.owner, - &self.mint_authority, - &self.system_program, - &self.rent, - "Mint Test", - "YAY", - "", - 0, // seller_fee_basis_points - true, // is_mutable - true, // update_authority_is_signer - ) - .invoke_signed(seeds)?; + // Create metadata with collection reference. + // Note: The collection is set as unverified here; call verify_collection + // separately to verify it. + accounts.token_metadata_program + .create_metadata_accounts_v3( + &accounts.metadata, + &accounts.mint, + &accounts.mint_authority, + &accounts.owner, + &accounts.mint_authority, + &accounts.system_program, + &accounts.rent, + "Mint Test", + "YAY", + "", + 0, // seller_fee_basis_points + true, // is_mutable + true, // update_authority_is_signer + ) + .invoke_signed(seeds)?; - // Create master edition. - self.token_metadata_program - .create_master_edition_v3( - &self.master_edition, - &self.mint, - &self.mint_authority, // update_authority - &self.mint_authority, // mint_authority - &self.owner, // payer - &self.metadata, - &self.token_program, - &self.system_program, - &self.rent, - Some(0), // max_supply = 0 means unique 1/1 - ) - .invoke_signed(seeds)?; + // Create master edition. + accounts.token_metadata_program + .create_master_edition_v3( + &accounts.master_edition, + &accounts.mint, + &accounts.mint_authority, // update_authority + &accounts.mint_authority, // mint_authority + &accounts.owner, // payer + &accounts.metadata, + &accounts.token_program, + &accounts.system_program, + &accounts.rent, + Some(0), // max_supply = 0 means unique 1/1 + ) + .invoke_signed(seeds)?; - Ok(()) - } + Ok(()) } diff --git a/tokens/nft-operations/quasar/src/instructions/verify_collection.rs b/tokens/nft-operations/quasar/src/instructions/verify_collection.rs index 8cd065087..0306e5179 100644 --- a/tokens/nft-operations/quasar/src/instructions/verify_collection.rs +++ b/tokens/nft-operations/quasar/src/instructions/verify_collection.rs @@ -30,27 +30,25 @@ pub struct VerifyCollectionMint { pub token_metadata_program: MetadataProgram, } -impl VerifyCollectionMint { - #[inline(always)] - pub fn verify_collection(&mut self, bumps: &VerifyCollectionMintBumps) -> Result<(), ProgramError> { - let bump = [bumps.mint_authority]; - let seeds: &[Seed] = &[ - Seed::from(b"authority" as &[u8]), - Seed::from(&bump as &[u8]), - ]; +#[inline(always)] +pub fn handle_verify_collection(accounts: &mut VerifyCollectionMint, bumps: &VerifyCollectionMintBumps) -> Result<(), ProgramError> { + let bump = [bumps.mint_authority]; + let seeds: &[Seed] = &[ + Seed::from(b"authority" as &[u8]), + Seed::from(&bump as &[u8]), + ]; - self.token_metadata_program - .verify_sized_collection_item( - &self.metadata, - &self.mint_authority, - &self.authority, // payer - &self.collection_mint, - &self.collection_metadata, - &self.collection_master_edition, - ) - .invoke_signed(seeds)?; + accounts.token_metadata_program + .verify_sized_collection_item( + &accounts.metadata, + &accounts.mint_authority, + &accounts.authority, // payer + &accounts.collection_mint, + &accounts.collection_metadata, + &accounts.collection_master_edition, + ) + .invoke_signed(seeds)?; - log("Collection Verified!"); - Ok(()) - } + log("Collection Verified!"); + Ok(()) } diff --git a/tokens/pda-mint-authority/quasar/src/lib.rs b/tokens/pda-mint-authority/quasar/src/lib.rs index 7acc6bd57..e655cd02e 100644 --- a/tokens/pda-mint-authority/quasar/src/lib.rs +++ b/tokens/pda-mint-authority/quasar/src/lib.rs @@ -24,13 +24,13 @@ mod quasar_pda_mint_authority { /// Create a token mint at a PDA. The PDA is its own mint authority. #[instruction(discriminator = 0)] pub fn create_mint(ctx: Ctx, _decimals: u8) -> Result<(), ProgramError> { - ctx.accounts.create_mint() + handle_create_mint(&mut ctx.accounts) } /// Mint tokens using the PDA mint authority. #[instruction(discriminator = 1)] pub fn mint_tokens(ctx: Ctx, amount: u64) -> Result<(), ProgramError> { - ctx.accounts.mint_tokens(amount, ctx.bumps.mint) + handle_mint_tokens(&mut ctx.accounts, amount, ctx.bumps.mint) } } @@ -47,12 +47,10 @@ pub struct CreateMint { pub system_program: Program, } -impl CreateMint { - #[inline(always)] - pub fn create_mint(&mut self) -> Result<(), ProgramError> { - // Mint is created and initialised by Quasar's #[account(init)]. - Ok(()) - } +#[inline(always)] +fn handle_create_mint(_accounts: &mut CreateMint) -> Result<(), ProgramError> { + // Mint is created and initialised by Quasar's #[account(init)]. + Ok(()) } /// Mint tokens to a token account, signing with the PDA mint authority. @@ -69,18 +67,16 @@ pub struct MintTokens { pub token_program: Program, } -impl MintTokens { - #[inline(always)] - pub fn mint_tokens(&mut self, amount: u64, mint_bump: u8) -> Result<(), ProgramError> { - // The PDA mint is its own authority. Build signer seeds. - let bump = [mint_bump]; - let seeds: &[Seed] = &[ - Seed::from(b"mint" as &[u8]), - Seed::from(&bump as &[u8]), - ]; +#[inline(always)] +fn handle_mint_tokens(accounts: &mut MintTokens, amount: u64, mint_bump: u8) -> Result<(), ProgramError> { + // The PDA mint is its own authority. Build signer seeds. + let bump = [mint_bump]; + let seeds: &[Seed] = &[ + Seed::from(b"mint" as &[u8]), + Seed::from(&bump as &[u8]), + ]; - self.token_program - .mint_to(&self.mint, &self.token_account, &self.mint, amount) - .invoke_signed(seeds) - } + accounts.token_program + .mint_to(&accounts.mint, &accounts.token_account, &accounts.mint, amount) + .invoke_signed(seeds) } diff --git a/tokens/spl-token-minter/quasar/src/instructions/create.rs b/tokens/spl-token-minter/quasar/src/instructions/create.rs index 84b257aff..3291ceb17 100644 --- a/tokens/spl-token-minter/quasar/src/instructions/create.rs +++ b/tokens/spl-token-minter/quasar/src/instructions/create.rs @@ -30,35 +30,33 @@ pub struct CreateToken { pub rent: Sysvar, } -impl CreateToken { - #[inline(always)] - pub fn create_token( - &mut self, - token_name: &str, - token_symbol: &str, - token_uri: &str, - ) -> Result<(), ProgramError> { - log("Creating metadata account"); +#[inline(always)] +pub fn handle_create_token( + accounts: &mut CreateToken, + token_name: &str, + token_symbol: &str, + token_uri: &str, +) -> Result<(), ProgramError> { + log("Creating metadata account"); - self.token_metadata_program - .create_metadata_accounts_v3( - &self.metadata_account, - &self.mint_account, - &self.payer, // mint_authority - &self.payer, // payer - &self.payer, // update_authority - &self.system_program, - &self.rent, - token_name, - token_symbol, - token_uri, - 0, // seller_fee_basis_points - false, // is_mutable - true, // update_authority_is_signer - ) - .invoke()?; + accounts.token_metadata_program + .create_metadata_accounts_v3( + &accounts.metadata_account, + &accounts.mint_account, + &accounts.payer, // mint_authority + &accounts.payer, // payer + &accounts.payer, // update_authority + &accounts.system_program, + &accounts.rent, + token_name, + token_symbol, + token_uri, + 0, // seller_fee_basis_points + false, // is_mutable + true, // update_authority_is_signer + ) + .invoke()?; - log("Token created successfully."); - Ok(()) - } + log("Token created successfully."); + Ok(()) } diff --git a/tokens/spl-token-minter/quasar/src/instructions/mint.rs b/tokens/spl-token-minter/quasar/src/instructions/mint.rs index 00b968e76..697a658be 100644 --- a/tokens/spl-token-minter/quasar/src/instructions/mint.rs +++ b/tokens/spl-token-minter/quasar/src/instructions/mint.rs @@ -15,26 +15,24 @@ pub struct MintToken { pub system_program: Program, } -impl MintToken { - #[inline(always)] - pub fn mint_token(&mut self, amount: u64) -> Result<(), ProgramError> { - log("Minting tokens to associated token account..."); +#[inline(always)] +pub fn handle_mint_token(accounts: &mut MintToken, amount: u64) -> Result<(), ProgramError> { + log("Minting tokens to associated token account..."); - let decimals = self.mint_account.decimals(); - let adjusted_amount = amount - .checked_mul(10u64.pow(decimals as u32)) - .ok_or(ProgramError::ArithmeticOverflow)?; + let decimals = accounts.mint_account.decimals(); + let adjusted_amount = amount + .checked_mul(10u64.pow(decimals as u32)) + .ok_or(ProgramError::ArithmeticOverflow)?; - self.token_program - .mint_to( - &self.mint_account, - &self.associated_token_account, - &self.mint_authority, - adjusted_amount, - ) - .invoke()?; + accounts.token_program + .mint_to( + &accounts.mint_account, + &accounts.associated_token_account, + &accounts.mint_authority, + adjusted_amount, + ) + .invoke()?; - log("Token minted successfully."); - Ok(()) - } + log("Token minted successfully."); + Ok(()) } diff --git a/tokens/token-extensions/basics/quasar/src/lib.rs b/tokens/token-extensions/basics/quasar/src/lib.rs index 7cddd4fa2..11c329f9a 100644 --- a/tokens/token-extensions/basics/quasar/src/lib.rs +++ b/tokens/token-extensions/basics/quasar/src/lib.rs @@ -34,13 +34,13 @@ mod quasar_token_2022_basics { /// Mint tokens to a recipient's token account. #[instruction(discriminator = 0)] pub fn mint_token(ctx: Ctx, amount: u64) -> Result<(), ProgramError> { - ctx.accounts.mint_token(amount) + handle_mint_token(&mut ctx.accounts, amount) } /// Transfer tokens using transfer_checked (required for Token-2022). #[instruction(discriminator = 1)] pub fn transfer_token(ctx: Ctx, amount: u64) -> Result<(), ProgramError> { - ctx.accounts.transfer_token(amount) + handle_transfer_token(&mut ctx.accounts, amount) } } @@ -56,27 +56,25 @@ pub struct MintToken { pub token_program: Program, } -impl MintToken { - #[inline(always)] - pub fn mint_token(&mut self, amount: u64) -> Result<(), ProgramError> { - // SPL Token MintTo instruction: opcode 7, amount as u64 LE. - let data = build_u64_data(7, amount); - CpiCall::new( - self.token_program.to_account_view().address(), - [ - InstructionAccount::writable(self.mint.to_account_view().address()), - InstructionAccount::writable(self.receiver.to_account_view().address()), - InstructionAccount::readonly_signer(self.authority.to_account_view().address()), - ], - [ - self.mint.to_account_view(), - self.receiver.to_account_view(), - self.authority.to_account_view(), - ], - data, - ) - .invoke() - } +#[inline(always)] +fn handle_mint_token(accounts: &mut MintToken, amount: u64) -> Result<(), ProgramError> { + // SPL Token MintTo instruction: opcode 7, amount as u64 LE. + let data = build_u64_data(7, amount); + CpiCall::new( + accounts.token_program.to_account_view().address(), + [ + InstructionAccount::writable(accounts.mint.to_account_view().address()), + InstructionAccount::writable(accounts.receiver.to_account_view().address()), + InstructionAccount::readonly_signer(accounts.authority.to_account_view().address()), + ], + [ + accounts.mint.to_account_view(), + accounts.receiver.to_account_view(), + accounts.authority.to_account_view(), + ], + data, + ) + .invoke() } /// Accounts for transferring tokens via Token-2022 transfer_checked. @@ -92,29 +90,27 @@ pub struct TransferToken { pub token_program: Program, } -impl TransferToken { - #[inline(always)] - pub fn transfer_token(&mut self, amount: u64) -> Result<(), ProgramError> { - // SPL Token TransferChecked instruction: opcode 12, amount as u64 LE, decimals as u8. - let data = build_transfer_checked_data(amount, 6); - CpiCall::new( - self.token_program.to_account_view().address(), - [ - InstructionAccount::writable(self.from.to_account_view().address()), - InstructionAccount::readonly(self.mint.to_account_view().address()), - InstructionAccount::writable(self.to.to_account_view().address()), - InstructionAccount::readonly_signer(self.sender.to_account_view().address()), - ], - [ - self.from.to_account_view(), - self.mint.to_account_view(), - self.to.to_account_view(), - self.sender.to_account_view(), - ], - data, - ) - .invoke() - } +#[inline(always)] +fn handle_transfer_token(accounts: &mut TransferToken, amount: u64) -> Result<(), ProgramError> { + // SPL Token TransferChecked instruction: opcode 12, amount as u64 LE, decimals as u8. + let data = build_transfer_checked_data(amount, 6); + CpiCall::new( + accounts.token_program.to_account_view().address(), + [ + InstructionAccount::writable(accounts.from.to_account_view().address()), + InstructionAccount::readonly(accounts.mint.to_account_view().address()), + InstructionAccount::writable(accounts.to.to_account_view().address()), + InstructionAccount::readonly_signer(accounts.sender.to_account_view().address()), + ], + [ + accounts.from.to_account_view(), + accounts.mint.to_account_view(), + accounts.to.to_account_view(), + accounts.sender.to_account_view(), + ], + data, + ) + .invoke() } /// Build a 9-byte instruction data: [opcode, u64 LE amount]. diff --git a/tokens/token-extensions/cpi-guard/quasar/src/lib.rs b/tokens/token-extensions/cpi-guard/quasar/src/lib.rs index 5ca0803ec..ff733df35 100644 --- a/tokens/token-extensions/cpi-guard/quasar/src/lib.rs +++ b/tokens/token-extensions/cpi-guard/quasar/src/lib.rs @@ -30,7 +30,7 @@ mod quasar_cpi_guard { /// on the sender's token account. #[instruction(discriminator = 0)] pub fn cpi_transfer(ctx: Ctx) -> Result<(), ProgramError> { - ctx.accounts.cpi_transfer() + handle_cpi_transfer(&mut ctx.accounts) } } @@ -46,31 +46,29 @@ pub struct CpiTransfer { pub token_program: Program, } -impl CpiTransfer { - #[inline(always)] - pub fn cpi_transfer(&mut self) -> Result<(), ProgramError> { - // TransferChecked: opcode 12, amount=1, decimals=9 - let mut data = [0u8; 10]; - data[0] = 12; - data[1..9].copy_from_slice(&1u64.to_le_bytes()); - data[9] = 9; // decimals +#[inline(always)] +fn handle_cpi_transfer(accounts: &mut CpiTransfer) -> Result<(), ProgramError> { + // TransferChecked: opcode 12, amount=1, decimals=9 + let mut data = [0u8; 10]; + data[0] = 12; + data[1..9].copy_from_slice(&1u64.to_le_bytes()); + data[9] = 9; // decimals - CpiCall::new( - self.token_program.to_account_view().address(), - [ - InstructionAccount::writable(self.sender_token_account.to_account_view().address()), - InstructionAccount::readonly(self.mint_account.to_account_view().address()), - InstructionAccount::writable(self.recipient_token_account.to_account_view().address()), - InstructionAccount::readonly_signer(self.sender.to_account_view().address()), - ], - [ - self.sender_token_account.to_account_view(), - self.mint_account.to_account_view(), - self.recipient_token_account.to_account_view(), - self.sender.to_account_view(), - ], - data, - ) - .invoke() - } + CpiCall::new( + accounts.token_program.to_account_view().address(), + [ + InstructionAccount::writable(accounts.sender_token_account.to_account_view().address()), + InstructionAccount::readonly(accounts.mint_account.to_account_view().address()), + InstructionAccount::writable(accounts.recipient_token_account.to_account_view().address()), + InstructionAccount::readonly_signer(accounts.sender.to_account_view().address()), + ], + [ + accounts.sender_token_account.to_account_view(), + accounts.mint_account.to_account_view(), + accounts.recipient_token_account.to_account_view(), + accounts.sender.to_account_view(), + ], + data, + ) + .invoke() } diff --git a/tokens/token-extensions/default-account-state/quasar/src/lib.rs b/tokens/token-extensions/default-account-state/quasar/src/lib.rs index 2c7cbc5ee..7026d788b 100644 --- a/tokens/token-extensions/default-account-state/quasar/src/lib.rs +++ b/tokens/token-extensions/default-account-state/quasar/src/lib.rs @@ -30,7 +30,7 @@ mod quasar_default_account_state { /// The mint account must be a signer (keypair created client-side). #[instruction(discriminator = 0)] pub fn initialize(ctx: Ctx) -> Result<(), ProgramError> { - ctx.accounts.initialize() + handle_initialize(&mut ctx.accounts) } /// Update the default account state on an existing mint. @@ -40,7 +40,7 @@ mod quasar_default_account_state { ctx: Ctx, account_state: u8, ) -> Result<(), ProgramError> { - ctx.accounts.update_default_state(account_state) + handle_update_default_state(&mut ctx.accounts, account_state) } } @@ -54,57 +54,56 @@ pub struct Initialize { pub system_program: Program, } -impl Initialize { - #[inline(always)] - pub fn initialize(&mut self) -> Result<(), ProgramError> { - // 165 (base account) + 1 (account type) + 4 (TLV header) + 1 (DefaultAccountState data) = 171 bytes - let mint_size: u64 = 171; - let lamports = Rent::get()?.try_minimum_balance(mint_size as usize)?; +#[inline(always)] +fn handle_initialize(accounts: &mut Initialize) -> Result<(), ProgramError> { + // 165 (base account) + 1 (account type) + 4 (TLV header) + 1 (DefaultAccountState data) = 171 bytes + let mint_size: u64 = 171; + let lamports = Rent::get()?.try_minimum_balance(mint_size as usize)?; - // 1. Create account owned by Token-2022 - self.system_program - .create_account( - &self.payer, - &self.mint_account, - lamports, - mint_size, - self.token_program.to_account_view().address(), - ) - .invoke()?; - - // 2. Initialize DefaultAccountState extension (frozen = 2) - // Instruction: ExtensionInstruction(DefaultAccountStateInitialize) = [28, 0, 2] - let ext_data: [u8; 3] = [28, 0, 2]; // opcode 28, sub-opcode 0, state = Frozen - CpiCall::new( - self.token_program.to_account_view().address(), - [InstructionAccount::writable( - self.mint_account.to_account_view().address(), - )], - [self.mint_account.to_account_view()], - ext_data, + // 1. Create account owned by Token-2022 + accounts + .system_program + .create_account( + &accounts.payer, + &accounts.mint_account, + lamports, + mint_size, + accounts.token_program.to_account_view().address(), ) .invoke()?; - // 3. InitializeMint2: opcode 20, decimals, mint_authority, freeze_authority_option, freeze_authority - // COption is encoded as 1-byte flag (1 = Some, 0 = None) + 32-byte pubkey - // Total: 1 (opcode) + 1 (decimals) + 32 (mint_authority) + 1 (COption flag) + 32 (freeze_authority) = 67 bytes - let mut mint_data = [0u8; 67]; - mint_data[0] = 20; // InitializeMint2 - mint_data[1] = 2; // decimals - mint_data[2..34].copy_from_slice(self.payer.to_account_view().address().as_ref()); - mint_data[34] = 1; // COption::Some flag (1-byte format used by quasar-svm token-2022) - mint_data[35..67].copy_from_slice(self.payer.to_account_view().address().as_ref()); + // 2. Initialize DefaultAccountState extension (frozen = 2) + // Instruction: ExtensionInstruction(DefaultAccountStateInitialize) = [28, 0, 2] + let ext_data: [u8; 3] = [28, 0, 2]; // opcode 28, sub-opcode 0, state = Frozen + CpiCall::new( + accounts.token_program.to_account_view().address(), + [InstructionAccount::writable( + accounts.mint_account.to_account_view().address(), + )], + [accounts.mint_account.to_account_view()], + ext_data, + ) + .invoke()?; - CpiCall::new( - self.token_program.to_account_view().address(), - [InstructionAccount::writable( - self.mint_account.to_account_view().address(), - )], - [self.mint_account.to_account_view()], - mint_data, - ) - .invoke() - } + // 3. InitializeMint2: opcode 20, decimals, mint_authority, freeze_authority_option, freeze_authority + // COption is encoded as 1-byte flag (1 = Some, 0 = None) + 32-byte pubkey + // Total: 1 (opcode) + 1 (decimals) + 32 (mint_authority) + 1 (COption flag) + 32 (freeze_authority) = 67 bytes + let mut mint_data = [0u8; 67]; + mint_data[0] = 20; // InitializeMint2 + mint_data[1] = 2; // decimals + mint_data[2..34].copy_from_slice(accounts.payer.to_account_view().address().as_ref()); + mint_data[34] = 1; // COption::Some flag (1-byte format used by quasar-svm token-2022) + mint_data[35..67].copy_from_slice(accounts.payer.to_account_view().address().as_ref()); + + CpiCall::new( + accounts.token_program.to_account_view().address(), + [InstructionAccount::writable( + accounts.mint_account.to_account_view().address(), + )], + [accounts.mint_account.to_account_view()], + mint_data, + ) + .invoke() } #[derive(Accounts)] @@ -116,25 +115,24 @@ pub struct UpdateDefaultState { pub token_program: Program, } -impl UpdateDefaultState { - #[inline(always)] - pub fn update_default_state(&mut self, account_state: u8) -> Result<(), ProgramError> { - // DefaultAccountState Update: opcode 28, sub-opcode 1, new state - let data: [u8; 3] = [28, 1, account_state]; - CpiCall::new( - self.token_program.to_account_view().address(), - [ - InstructionAccount::writable(self.mint_account.to_account_view().address()), - InstructionAccount::readonly_signer( - self.freeze_authority.to_account_view().address(), - ), - ], - [ - self.mint_account.to_account_view(), - self.freeze_authority.to_account_view(), - ], - data, - ) - .invoke() - } +#[inline(always)] +fn handle_update_default_state( + accounts: &mut UpdateDefaultState, + account_state: u8, +) -> Result<(), ProgramError> { + // DefaultAccountState Update: opcode 28, sub-opcode 1, new state + let data: [u8; 3] = [28, 1, account_state]; + CpiCall::new( + accounts.token_program.to_account_view().address(), + [ + InstructionAccount::writable(accounts.mint_account.to_account_view().address()), + InstructionAccount::readonly_signer(accounts.freeze_authority.to_account_view().address()), + ], + [ + accounts.mint_account.to_account_view(), + accounts.freeze_authority.to_account_view(), + ], + data, + ) + .invoke() } diff --git a/tokens/token-extensions/group/quasar/src/lib.rs b/tokens/token-extensions/group/quasar/src/lib.rs index 9e494fa8e..5388e72f1 100644 --- a/tokens/token-extensions/group/quasar/src/lib.rs +++ b/tokens/token-extensions/group/quasar/src/lib.rs @@ -31,7 +31,7 @@ mod quasar_group { #[instruction(discriminator = 0)] pub fn initialize_group(ctx: Ctx) -> Result<(), ProgramError> { - ctx.accounts.initialize_group() + handle_initialize_group(&mut ctx.accounts) } } @@ -45,59 +45,58 @@ pub struct InitializeGroup { pub system_program: Program, } -impl InitializeGroup { - #[inline(always)] - pub fn initialize_group(&mut self) -> Result<(), ProgramError> { - // Mint + GroupPointer extension = 250 bytes - let mint_size: u64 = 250; - let lamports = Rent::get()?.try_minimum_balance(mint_size as usize)?; +#[inline(always)] +fn handle_initialize_group(accounts: &mut InitializeGroup) -> Result<(), ProgramError> { + // Mint + GroupPointer extension = 250 bytes + let mint_size: u64 = 250; + let lamports = Rent::get()?.try_minimum_balance(mint_size as usize)?; - self.system_program - .create_account( - &self.payer, - &self.mint_account, - lamports, - mint_size, - self.token_program.to_account_view().address(), - ) - .invoke()?; - - // InitializeGroupPointer: opcode 41, sub-opcode 0 - // Data: [41, 0, authority (32 bytes), group_address (32 bytes)] - let mut ext_data = [0u8; 66]; - ext_data[0] = 41; - ext_data[1] = 0; - // authority = mint itself (self-referential PDA pattern) - ext_data[2..34].copy_from_slice(self.mint_account.to_account_view().address().as_ref()); - // group_address = mint itself - ext_data[34..66].copy_from_slice(self.mint_account.to_account_view().address().as_ref()); - - CpiCall::new( - self.token_program.to_account_view().address(), - [InstructionAccount::writable( - self.mint_account.to_account_view().address(), - )], - [self.mint_account.to_account_view()], - ext_data, + accounts + .system_program + .create_account( + &accounts.payer, + &accounts.mint_account, + lamports, + mint_size, + accounts.token_program.to_account_view().address(), ) .invoke()?; - // InitializeMint2: mint authority = mint itself (for self-signing) - let mut mint_data = [0u8; 67]; - mint_data[0] = 20; - mint_data[1] = 2; - mint_data[2..34].copy_from_slice(self.mint_account.to_account_view().address().as_ref()); - mint_data[34] = 1; - mint_data[35..67].copy_from_slice(self.mint_account.to_account_view().address().as_ref()); + // InitializeGroupPointer: opcode 41, sub-opcode 0 + // Data: [41, 0, authority (32 bytes), group_address (32 bytes)] + let mut ext_data = [0u8; 66]; + ext_data[0] = 41; + ext_data[1] = 0; + // authority = mint itself (self-referential PDA pattern) + ext_data[2..34].copy_from_slice(accounts.mint_account.to_account_view().address().as_ref()); + // group_address = mint itself + ext_data[34..66].copy_from_slice(accounts.mint_account.to_account_view().address().as_ref()); - CpiCall::new( - self.token_program.to_account_view().address(), - [InstructionAccount::writable( - self.mint_account.to_account_view().address(), - )], - [self.mint_account.to_account_view()], - mint_data, - ) - .invoke() - } + CpiCall::new( + accounts.token_program.to_account_view().address(), + [InstructionAccount::writable( + accounts.mint_account.to_account_view().address(), + )], + [accounts.mint_account.to_account_view()], + ext_data, + ) + .invoke()?; + + // InitializeMint2: mint authority = mint itself (for self-signing) + let mut mint_data = [0u8; 67]; + mint_data[0] = 20; + mint_data[1] = 2; + mint_data[2..34].copy_from_slice(accounts.mint_account.to_account_view().address().as_ref()); + mint_data[34] = 1; + mint_data[35..67].copy_from_slice(accounts.mint_account.to_account_view().address().as_ref()); + + CpiCall::new( + accounts.token_program.to_account_view().address(), + [InstructionAccount::writable( + accounts.mint_account.to_account_view().address(), + )], + [accounts.mint_account.to_account_view()], + mint_data, + ) + .invoke() } diff --git a/tokens/token-extensions/immutable-owner/quasar/src/lib.rs b/tokens/token-extensions/immutable-owner/quasar/src/lib.rs index 74b750662..0a9fe0919 100644 --- a/tokens/token-extensions/immutable-owner/quasar/src/lib.rs +++ b/tokens/token-extensions/immutable-owner/quasar/src/lib.rs @@ -27,7 +27,7 @@ mod quasar_immutable_owner { #[instruction(discriminator = 0)] pub fn initialize(ctx: Ctx) -> Result<(), ProgramError> { - ctx.accounts.initialize() + handle_initialize(&mut ctx.accounts) } } @@ -42,52 +42,51 @@ pub struct Initialize { pub system_program: Program, } -impl Initialize { - #[inline(always)] - pub fn initialize(&mut self) -> Result<(), ProgramError> { - // 165 (base) + 1 (account type) + 4 (TLV header, ImmutableOwner is zero-size) = 170 bytes - let account_size: u64 = 170; - let lamports = Rent::get()?.try_minimum_balance(account_size as usize)?; +#[inline(always)] +fn handle_initialize(accounts: &mut Initialize) -> Result<(), ProgramError> { + // 165 (base) + 1 (account type) + 4 (TLV header, ImmutableOwner is zero-size) = 170 bytes + let account_size: u64 = 170; + let lamports = Rent::get()?.try_minimum_balance(account_size as usize)?; - // 1. Create account - self.system_program - .create_account( - &self.payer, - &self.token_account, - lamports, - account_size, - self.token_program.to_account_view().address(), - ) - .invoke()?; - - // 2. Initialize ImmutableOwner extension: opcode 22 (no additional data) - CpiCall::new( - self.token_program.to_account_view().address(), - [InstructionAccount::writable( - self.token_account.to_account_view().address(), - )], - [self.token_account.to_account_view()], - [22u8], + // 1. Create account + accounts + .system_program + .create_account( + &accounts.payer, + &accounts.token_account, + lamports, + account_size, + accounts.token_program.to_account_view().address(), ) .invoke()?; - // 3. InitializeAccount3: opcode 18, owner pubkey - let mut data = [0u8; 33]; - data[0] = 18; - data[1..33].copy_from_slice(self.payer.to_account_view().address().as_ref()); + // 2. Initialize ImmutableOwner extension: opcode 22 (no additional data) + CpiCall::new( + accounts.token_program.to_account_view().address(), + [InstructionAccount::writable( + accounts.token_account.to_account_view().address(), + )], + [accounts.token_account.to_account_view()], + [22u8], + ) + .invoke()?; - CpiCall::new( - self.token_program.to_account_view().address(), - [ - InstructionAccount::writable(self.token_account.to_account_view().address()), - InstructionAccount::readonly(self.mint_account.to_account_view().address()), - ], - [ - self.token_account.to_account_view(), - self.mint_account.to_account_view(), - ], - data, - ) - .invoke() - } + // 3. InitializeAccount3: opcode 18, owner pubkey + let mut data = [0u8; 33]; + data[0] = 18; + data[1..33].copy_from_slice(accounts.payer.to_account_view().address().as_ref()); + + CpiCall::new( + accounts.token_program.to_account_view().address(), + [ + InstructionAccount::writable(accounts.token_account.to_account_view().address()), + InstructionAccount::readonly(accounts.mint_account.to_account_view().address()), + ], + [ + accounts.token_account.to_account_view(), + accounts.mint_account.to_account_view(), + ], + data, + ) + .invoke() } diff --git a/tokens/token-extensions/interest-bearing/quasar/src/lib.rs b/tokens/token-extensions/interest-bearing/quasar/src/lib.rs index 97744ed23..ec868491f 100644 --- a/tokens/token-extensions/interest-bearing/quasar/src/lib.rs +++ b/tokens/token-extensions/interest-bearing/quasar/src/lib.rs @@ -27,12 +27,12 @@ mod quasar_interest_bearing { #[instruction(discriminator = 0)] pub fn initialize(ctx: Ctx, rate: i16) -> Result<(), ProgramError> { - ctx.accounts.initialize(rate) + handle_initialize(&mut ctx.accounts, rate) } #[instruction(discriminator = 1)] pub fn update_rate(ctx: Ctx, rate: i16) -> Result<(), ProgramError> { - ctx.accounts.update_rate(rate) + handle_update_rate(&mut ctx.accounts, rate) } } @@ -46,59 +46,58 @@ pub struct Initialize { pub system_program: Program, } -impl Initialize { - #[inline(always)] - pub fn initialize(&mut self, rate: i16) -> Result<(), ProgramError> { - // 165 (base) + 1 (account type) + 4 (TLV header) + 52 (InterestBearingConfig data) = 222 bytes - let mint_size: u64 = 222; - let lamports = Rent::get()?.try_minimum_balance(mint_size as usize)?; - - self.system_program - .create_account( - &self.payer, - &self.mint_account, - lamports, - mint_size, - self.token_program.to_account_view().address(), - ) - .invoke()?; - - // InterestBearingMintInitialize: opcode 33, sub-opcode 0 - // Data: [33, 0, rate_authority (32 bytes), rate (i16 LE)] - let mut ext_data = [0u8; 36]; - ext_data[0] = 33; - ext_data[1] = 0; // Initialize sub-opcode - ext_data[2..34].copy_from_slice(self.payer.to_account_view().address().as_ref()); - ext_data[34..36].copy_from_slice(&rate.to_le_bytes()); - - CpiCall::new( - self.token_program.to_account_view().address(), - [InstructionAccount::writable( - self.mint_account.to_account_view().address(), - )], - [self.mint_account.to_account_view()], - ext_data, +#[inline(always)] +fn handle_initialize(accounts: &mut Initialize, rate: i16) -> Result<(), ProgramError> { + // 165 (base) + 1 (account type) + 4 (TLV header) + 52 (InterestBearingConfig data) = 222 bytes + let mint_size: u64 = 222; + let lamports = Rent::get()?.try_minimum_balance(mint_size as usize)?; + + accounts + .system_program + .create_account( + &accounts.payer, + &accounts.mint_account, + lamports, + mint_size, + accounts.token_program.to_account_view().address(), ) .invoke()?; - // InitializeMint2 - let mut mint_data = [0u8; 67]; - mint_data[0] = 20; - mint_data[1] = 2; - mint_data[2..34].copy_from_slice(self.payer.to_account_view().address().as_ref()); - mint_data[34] = 1; - mint_data[35..67].copy_from_slice(self.payer.to_account_view().address().as_ref()); - - CpiCall::new( - self.token_program.to_account_view().address(), - [InstructionAccount::writable( - self.mint_account.to_account_view().address(), - )], - [self.mint_account.to_account_view()], - mint_data, - ) - .invoke() - } + // InterestBearingMintInitialize: opcode 33, sub-opcode 0 + // Data: [33, 0, rate_authority (32 bytes), rate (i16 LE)] + let mut ext_data = [0u8; 36]; + ext_data[0] = 33; + ext_data[1] = 0; // Initialize sub-opcode + ext_data[2..34].copy_from_slice(accounts.payer.to_account_view().address().as_ref()); + ext_data[34..36].copy_from_slice(&rate.to_le_bytes()); + + CpiCall::new( + accounts.token_program.to_account_view().address(), + [InstructionAccount::writable( + accounts.mint_account.to_account_view().address(), + )], + [accounts.mint_account.to_account_view()], + ext_data, + ) + .invoke()?; + + // InitializeMint2 + let mut mint_data = [0u8; 67]; + mint_data[0] = 20; + mint_data[1] = 2; + mint_data[2..34].copy_from_slice(accounts.payer.to_account_view().address().as_ref()); + mint_data[34] = 1; + mint_data[35..67].copy_from_slice(accounts.payer.to_account_view().address().as_ref()); + + CpiCall::new( + accounts.token_program.to_account_view().address(), + [InstructionAccount::writable( + accounts.mint_account.to_account_view().address(), + )], + [accounts.mint_account.to_account_view()], + mint_data, + ) + .invoke() } #[derive(Accounts)] @@ -110,27 +109,25 @@ pub struct UpdateRate { pub token_program: Program, } -impl UpdateRate { - #[inline(always)] - pub fn update_rate(&mut self, rate: i16) -> Result<(), ProgramError> { - // InterestBearingMintUpdateRate: opcode 33, sub-opcode 1, rate (i16 LE) - let mut data = [0u8; 4]; - data[0] = 33; - data[1] = 1; - data[2..4].copy_from_slice(&rate.to_le_bytes()); - - CpiCall::new( - self.token_program.to_account_view().address(), - [ - InstructionAccount::writable(self.mint_account.to_account_view().address()), - InstructionAccount::readonly_signer(self.authority.to_account_view().address()), - ], - [ - self.mint_account.to_account_view(), - self.authority.to_account_view(), - ], - data, - ) - .invoke() - } +#[inline(always)] +fn handle_update_rate(accounts: &mut UpdateRate, rate: i16) -> Result<(), ProgramError> { + // InterestBearingMintUpdateRate: opcode 33, sub-opcode 1, rate (i16 LE) + let mut data = [0u8; 4]; + data[0] = 33; + data[1] = 1; + data[2..4].copy_from_slice(&rate.to_le_bytes()); + + CpiCall::new( + accounts.token_program.to_account_view().address(), + [ + InstructionAccount::writable(accounts.mint_account.to_account_view().address()), + InstructionAccount::readonly_signer(accounts.authority.to_account_view().address()), + ], + [ + accounts.mint_account.to_account_view(), + accounts.authority.to_account_view(), + ], + data, + ) + .invoke() } diff --git a/tokens/token-extensions/memo-transfer/quasar/src/lib.rs b/tokens/token-extensions/memo-transfer/quasar/src/lib.rs index b4e7c0aff..4a8b45464 100644 --- a/tokens/token-extensions/memo-transfer/quasar/src/lib.rs +++ b/tokens/token-extensions/memo-transfer/quasar/src/lib.rs @@ -27,12 +27,12 @@ mod quasar_memo_transfer { #[instruction(discriminator = 0)] pub fn initialize(ctx: Ctx) -> Result<(), ProgramError> { - ctx.accounts.initialize() + handle_initialize(&mut ctx.accounts) } #[instruction(discriminator = 1)] pub fn disable(ctx: Ctx) -> Result<(), ProgramError> { - ctx.accounts.disable() + handle_disable(&mut ctx.accounts) } } @@ -47,57 +47,56 @@ pub struct Initialize { pub system_program: Program, } -impl Initialize { - #[inline(always)] - pub fn initialize(&mut self) -> Result<(), ProgramError> { - // Token account + MemoTransfer extension = 300 bytes - let account_size: u64 = 300; - let lamports = Rent::get()?.try_minimum_balance(account_size as usize)?; +#[inline(always)] +fn handle_initialize(accounts: &mut Initialize) -> Result<(), ProgramError> { + // Token account + MemoTransfer extension = 300 bytes + let account_size: u64 = 300; + let lamports = Rent::get()?.try_minimum_balance(account_size as usize)?; - self.system_program - .create_account( - &self.payer, - &self.token_account, - lamports, - account_size, - self.token_program.to_account_view().address(), - ) - .invoke()?; - - // InitializeAccount3: opcode 18, owner pubkey - let mut init_data = [0u8; 33]; - init_data[0] = 18; - init_data[1..33].copy_from_slice(self.payer.to_account_view().address().as_ref()); - - CpiCall::new( - self.token_program.to_account_view().address(), - [ - InstructionAccount::writable(self.token_account.to_account_view().address()), - InstructionAccount::readonly(self.mint_account.to_account_view().address()), - ], - [ - self.token_account.to_account_view(), - self.mint_account.to_account_view(), - ], - init_data, + accounts + .system_program + .create_account( + &accounts.payer, + &accounts.token_account, + lamports, + account_size, + accounts.token_program.to_account_view().address(), ) .invoke()?; - // MemoTransfer enable: opcode 30, sub-opcode 0 - CpiCall::new( - self.token_program.to_account_view().address(), - [ - InstructionAccount::writable(self.token_account.to_account_view().address()), - InstructionAccount::readonly_signer(self.payer.to_account_view().address()), - ], - [ - self.token_account.to_account_view(), - self.payer.to_account_view(), - ], - [30u8, 0], - ) - .invoke() - } + // InitializeAccount3: opcode 18, owner pubkey + let mut init_data = [0u8; 33]; + init_data[0] = 18; + init_data[1..33].copy_from_slice(accounts.payer.to_account_view().address().as_ref()); + + CpiCall::new( + accounts.token_program.to_account_view().address(), + [ + InstructionAccount::writable(accounts.token_account.to_account_view().address()), + InstructionAccount::readonly(accounts.mint_account.to_account_view().address()), + ], + [ + accounts.token_account.to_account_view(), + accounts.mint_account.to_account_view(), + ], + init_data, + ) + .invoke()?; + + // MemoTransfer enable: opcode 30, sub-opcode 0 + CpiCall::new( + accounts.token_program.to_account_view().address(), + [ + InstructionAccount::writable(accounts.token_account.to_account_view().address()), + InstructionAccount::readonly_signer(accounts.payer.to_account_view().address()), + ], + [ + accounts.token_account.to_account_view(), + accounts.payer.to_account_view(), + ], + [30u8, 0], + ) + .invoke() } #[derive(Accounts)] @@ -109,22 +108,20 @@ pub struct Disable { pub token_program: Program, } -impl Disable { - #[inline(always)] - pub fn disable(&mut self) -> Result<(), ProgramError> { - // MemoTransfer disable: opcode 30, sub-opcode 1 - CpiCall::new( - self.token_program.to_account_view().address(), - [ - InstructionAccount::writable(self.token_account.to_account_view().address()), - InstructionAccount::readonly_signer(self.owner.to_account_view().address()), - ], - [ - self.token_account.to_account_view(), - self.owner.to_account_view(), - ], - [30u8, 1], - ) - .invoke() - } +#[inline(always)] +fn handle_disable(accounts: &mut Disable) -> Result<(), ProgramError> { + // MemoTransfer disable: opcode 30, sub-opcode 1 + CpiCall::new( + accounts.token_program.to_account_view().address(), + [ + InstructionAccount::writable(accounts.token_account.to_account_view().address()), + InstructionAccount::readonly_signer(accounts.owner.to_account_view().address()), + ], + [ + accounts.token_account.to_account_view(), + accounts.owner.to_account_view(), + ], + [30u8, 1], + ) + .invoke() } diff --git a/tokens/token-extensions/mint-close-authority/quasar/src/lib.rs b/tokens/token-extensions/mint-close-authority/quasar/src/lib.rs index 5f279b8e6..e33a8f47c 100644 --- a/tokens/token-extensions/mint-close-authority/quasar/src/lib.rs +++ b/tokens/token-extensions/mint-close-authority/quasar/src/lib.rs @@ -28,13 +28,13 @@ mod quasar_mint_close_authority { /// Create a mint with the MintCloseAuthority extension. #[instruction(discriminator = 0)] pub fn initialize(ctx: Ctx) -> Result<(), ProgramError> { - ctx.accounts.initialize() + handle_initialize(&mut ctx.accounts) } /// Close the mint account, reclaiming lamports to the authority. #[instruction(discriminator = 1)] pub fn close(ctx: Ctx) -> Result<(), ProgramError> { - ctx.accounts.close() + handle_close(&mut ctx.accounts) } } @@ -48,56 +48,55 @@ pub struct Initialize { pub system_program: Program, } -impl Initialize { - #[inline(always)] - pub fn initialize(&mut self) -> Result<(), ProgramError> { - // 165 (base) + 1 (account type) + 4 (TLV header) + 32 (MintCloseAuthority data) = 202 bytes - let mint_size: u64 = 202; - let lamports = Rent::get()?.try_minimum_balance(mint_size as usize)?; - - self.system_program - .create_account( - &self.payer, - &self.mint_account, - lamports, - mint_size, - self.token_program.to_account_view().address(), - ) - .invoke()?; - - // InitializeMintCloseAuthority: opcode 25, COption::Some flag (1 byte), close_authority pubkey (32 bytes) - let mut ext_data = [0u8; 34]; - ext_data[0] = 25; // InitializeMintCloseAuthority - ext_data[1] = 1; // COption::Some - ext_data[2..34].copy_from_slice(self.payer.to_account_view().address().as_ref()); - - CpiCall::new( - self.token_program.to_account_view().address(), - [InstructionAccount::writable( - self.mint_account.to_account_view().address(), - )], - [self.mint_account.to_account_view()], - ext_data, +#[inline(always)] +fn handle_initialize(accounts: &mut Initialize) -> Result<(), ProgramError> { + // 165 (base) + 1 (account type) + 4 (TLV header) + 32 (MintCloseAuthority data) = 202 bytes + let mint_size: u64 = 202; + let lamports = Rent::get()?.try_minimum_balance(mint_size as usize)?; + + accounts + .system_program + .create_account( + &accounts.payer, + &accounts.mint_account, + lamports, + mint_size, + accounts.token_program.to_account_view().address(), ) .invoke()?; - // InitializeMint2 - let mut mint_data = [0u8; 67]; - mint_data[0] = 20; - mint_data[1] = 2; - mint_data[2..34].copy_from_slice(self.payer.to_account_view().address().as_ref()); - mint_data[34] = 0; // no freeze authority - - CpiCall::new( - self.token_program.to_account_view().address(), - [InstructionAccount::writable( - self.mint_account.to_account_view().address(), - )], - [self.mint_account.to_account_view()], - mint_data, - ) - .invoke() - } + // InitializeMintCloseAuthority: opcode 25, COption::Some flag (1 byte), close_authority pubkey (32 bytes) + let mut ext_data = [0u8; 34]; + ext_data[0] = 25; // InitializeMintCloseAuthority + ext_data[1] = 1; // COption::Some + ext_data[2..34].copy_from_slice(accounts.payer.to_account_view().address().as_ref()); + + CpiCall::new( + accounts.token_program.to_account_view().address(), + [InstructionAccount::writable( + accounts.mint_account.to_account_view().address(), + )], + [accounts.mint_account.to_account_view()], + ext_data, + ) + .invoke()?; + + // InitializeMint2 + let mut mint_data = [0u8; 67]; + mint_data[0] = 20; + mint_data[1] = 2; + mint_data[2..34].copy_from_slice(accounts.payer.to_account_view().address().as_ref()); + mint_data[34] = 0; // no freeze authority + + CpiCall::new( + accounts.token_program.to_account_view().address(), + [InstructionAccount::writable( + accounts.mint_account.to_account_view().address(), + )], + [accounts.mint_account.to_account_view()], + mint_data, + ) + .invoke() } #[derive(Accounts)] @@ -109,24 +108,22 @@ pub struct Close { pub token_program: Program, } -impl Close { - #[inline(always)] - pub fn close(&mut self) -> Result<(), ProgramError> { - // CloseAccount: opcode 9 - CpiCall::new( - self.token_program.to_account_view().address(), - [ - InstructionAccount::writable(self.mint_account.to_account_view().address()), - InstructionAccount::writable(self.authority.to_account_view().address()), - InstructionAccount::readonly_signer(self.authority.to_account_view().address()), - ], - [ - self.mint_account.to_account_view(), - self.authority.to_account_view(), - self.authority.to_account_view(), - ], - [9u8], - ) - .invoke() - } +#[inline(always)] +fn handle_close(accounts: &mut Close) -> Result<(), ProgramError> { + // CloseAccount: opcode 9 + CpiCall::new( + accounts.token_program.to_account_view().address(), + [ + InstructionAccount::writable(accounts.mint_account.to_account_view().address()), + InstructionAccount::writable(accounts.authority.to_account_view().address()), + InstructionAccount::readonly_signer(accounts.authority.to_account_view().address()), + ], + [ + accounts.mint_account.to_account_view(), + accounts.authority.to_account_view(), + accounts.authority.to_account_view(), + ], + [9u8], + ) + .invoke() } diff --git a/tokens/token-extensions/non-transferable/quasar/src/lib.rs b/tokens/token-extensions/non-transferable/quasar/src/lib.rs index f2115aa37..de5f6e0ee 100644 --- a/tokens/token-extensions/non-transferable/quasar/src/lib.rs +++ b/tokens/token-extensions/non-transferable/quasar/src/lib.rs @@ -27,7 +27,7 @@ mod quasar_non_transferable { #[instruction(discriminator = 0)] pub fn initialize(ctx: Ctx) -> Result<(), ProgramError> { - ctx.accounts.initialize() + handle_initialize(&mut ctx.accounts) } } @@ -41,51 +41,50 @@ pub struct Initialize { pub system_program: Program, } -impl Initialize { - #[inline(always)] - pub fn initialize(&mut self) -> Result<(), ProgramError> { - // Mint + NonTransferable extension = 170 bytes - let mint_size: u64 = 170; - let lamports = Rent::get()?.try_minimum_balance(mint_size as usize)?; +#[inline(always)] +fn handle_initialize(accounts: &mut Initialize) -> Result<(), ProgramError> { + // Mint + NonTransferable extension = 170 bytes + let mint_size: u64 = 170; + let lamports = Rent::get()?.try_minimum_balance(mint_size as usize)?; - // 1. Create account - self.system_program - .create_account( - &self.payer, - &self.mint_account, - lamports, - mint_size, - self.token_program.to_account_view().address(), - ) - .invoke()?; - - // 2. Initialize NonTransferable extension: opcode 32 (InitializeNonTransferableMint, no data) - CpiCall::new( - self.token_program.to_account_view().address(), - [InstructionAccount::writable( - self.mint_account.to_account_view().address(), - )], - [self.mint_account.to_account_view()], - [32u8], + // 1. Create account + accounts + .system_program + .create_account( + &accounts.payer, + &accounts.mint_account, + lamports, + mint_size, + accounts.token_program.to_account_view().address(), ) .invoke()?; - // 3. InitializeMint2 - let mut mint_data = [0u8; 67]; - mint_data[0] = 20; - mint_data[1] = 2; // decimals - mint_data[2..34].copy_from_slice(self.payer.to_account_view().address().as_ref()); - mint_data[34] = 1; // has freeze authority - mint_data[35..67].copy_from_slice(self.payer.to_account_view().address().as_ref()); + // 2. Initialize NonTransferable extension: opcode 32 (InitializeNonTransferableMint, no data) + CpiCall::new( + accounts.token_program.to_account_view().address(), + [InstructionAccount::writable( + accounts.mint_account.to_account_view().address(), + )], + [accounts.mint_account.to_account_view()], + [32u8], + ) + .invoke()?; - CpiCall::new( - self.token_program.to_account_view().address(), - [InstructionAccount::writable( - self.mint_account.to_account_view().address(), - )], - [self.mint_account.to_account_view()], - mint_data, - ) - .invoke() - } + // 3. InitializeMint2 + let mut mint_data = [0u8; 67]; + mint_data[0] = 20; + mint_data[1] = 2; // decimals + mint_data[2..34].copy_from_slice(accounts.payer.to_account_view().address().as_ref()); + mint_data[34] = 1; // has freeze authority + mint_data[35..67].copy_from_slice(accounts.payer.to_account_view().address().as_ref()); + + CpiCall::new( + accounts.token_program.to_account_view().address(), + [InstructionAccount::writable( + accounts.mint_account.to_account_view().address(), + )], + [accounts.mint_account.to_account_view()], + mint_data, + ) + .invoke() } diff --git a/tokens/token-extensions/permanent-delegate/quasar/src/lib.rs b/tokens/token-extensions/permanent-delegate/quasar/src/lib.rs index aa399a912..d9e8f44d2 100644 --- a/tokens/token-extensions/permanent-delegate/quasar/src/lib.rs +++ b/tokens/token-extensions/permanent-delegate/quasar/src/lib.rs @@ -27,7 +27,7 @@ mod quasar_permanent_delegate { #[instruction(discriminator = 0)] pub fn initialize(ctx: Ctx) -> Result<(), ProgramError> { - ctx.accounts.initialize() + handle_initialize(&mut ctx.accounts) } } @@ -41,53 +41,52 @@ pub struct Initialize { pub system_program: Program, } -impl Initialize { - #[inline(always)] - pub fn initialize(&mut self) -> Result<(), ProgramError> { - // 165 (base) + 1 (account type) + 4 (TLV header) + 32 (PermanentDelegate data) = 202 bytes - let mint_size: u64 = 202; - let lamports = Rent::get()?.try_minimum_balance(mint_size as usize)?; +#[inline(always)] +fn handle_initialize(accounts: &mut Initialize) -> Result<(), ProgramError> { + // 165 (base) + 1 (account type) + 4 (TLV header) + 32 (PermanentDelegate data) = 202 bytes + let mint_size: u64 = 202; + let lamports = Rent::get()?.try_minimum_balance(mint_size as usize)?; - self.system_program - .create_account( - &self.payer, - &self.mint_account, - lamports, - mint_size, - self.token_program.to_account_view().address(), - ) - .invoke()?; - - // InitializePermanentDelegate: opcode 35, delegate pubkey (32 bytes, not COption) - let mut ext_data = [0u8; 33]; - ext_data[0] = 35; - ext_data[1..33].copy_from_slice(self.payer.to_account_view().address().as_ref()); - - CpiCall::new( - self.token_program.to_account_view().address(), - [InstructionAccount::writable( - self.mint_account.to_account_view().address(), - )], - [self.mint_account.to_account_view()], - ext_data, + accounts + .system_program + .create_account( + &accounts.payer, + &accounts.mint_account, + lamports, + mint_size, + accounts.token_program.to_account_view().address(), ) .invoke()?; - // InitializeMint2 - let mut mint_data = [0u8; 67]; - mint_data[0] = 20; - mint_data[1] = 2; - mint_data[2..34].copy_from_slice(self.payer.to_account_view().address().as_ref()); - mint_data[34] = 0; // no freeze authority + // InitializePermanentDelegate: opcode 35, delegate pubkey (32 bytes, not COption) + let mut ext_data = [0u8; 33]; + ext_data[0] = 35; + ext_data[1..33].copy_from_slice(accounts.payer.to_account_view().address().as_ref()); - CpiCall::new( - self.token_program.to_account_view().address(), - [InstructionAccount::writable( - self.mint_account.to_account_view().address(), - )], - [self.mint_account.to_account_view()], - mint_data, - ) - .invoke() - } + CpiCall::new( + accounts.token_program.to_account_view().address(), + [InstructionAccount::writable( + accounts.mint_account.to_account_view().address(), + )], + [accounts.mint_account.to_account_view()], + ext_data, + ) + .invoke()?; + + // InitializeMint2 + let mut mint_data = [0u8; 67]; + mint_data[0] = 20; + mint_data[1] = 2; + mint_data[2..34].copy_from_slice(accounts.payer.to_account_view().address().as_ref()); + mint_data[34] = 0; // no freeze authority + + CpiCall::new( + accounts.token_program.to_account_view().address(), + [InstructionAccount::writable( + accounts.mint_account.to_account_view().address(), + )], + [accounts.mint_account.to_account_view()], + mint_data, + ) + .invoke() } diff --git a/tokens/token-extensions/transfer-fee/quasar/src/lib.rs b/tokens/token-extensions/transfer-fee/quasar/src/lib.rs index 9863f15c1..b19e4948c 100644 --- a/tokens/token-extensions/transfer-fee/quasar/src/lib.rs +++ b/tokens/token-extensions/transfer-fee/quasar/src/lib.rs @@ -32,13 +32,13 @@ mod quasar_transfer_fee { transfer_fee_basis_points: u16, maximum_fee: u64, ) -> Result<(), ProgramError> { - ctx.accounts.initialize(transfer_fee_basis_points, maximum_fee) + handle_initialize(&mut ctx.accounts, transfer_fee_basis_points, maximum_fee) } /// Transfer tokens with fee. #[instruction(discriminator = 1)] pub fn transfer(ctx: Ctx, amount: u64, fee: u64) -> Result<(), ProgramError> { - ctx.accounts.transfer(amount, fee) + handle_transfer(&mut ctx.accounts, amount, fee) } /// Update the transfer fee (takes effect after 2 epochs). @@ -48,13 +48,13 @@ mod quasar_transfer_fee { transfer_fee_basis_points: u16, maximum_fee: u64, ) -> Result<(), ProgramError> { - ctx.accounts.update_fee(transfer_fee_basis_points, maximum_fee) + handle_update_fee(&mut ctx.accounts, transfer_fee_basis_points, maximum_fee) } /// Withdraw withheld fees from the mint account. #[instruction(discriminator = 3)] pub fn withdraw(ctx: Ctx) -> Result<(), ProgramError> { - ctx.accounts.withdraw() + handle_withdraw(&mut ctx.accounts) } } @@ -68,20 +68,19 @@ pub struct Initialize { pub system_program: Program, } -impl Initialize { - #[inline(always)] - pub fn initialize(&mut self, basis_points: u16, max_fee: u64) -> Result<(), ProgramError> { +#[inline(always)] +fn handle_initialize(accounts: &mut Initialize, basis_points: u16, max_fee: u64) -> Result<(), ProgramError> { // 165 (base) + 1 (AccountType) + 4 (TLV header) + 108 (TransferFeeConfig data) = 278 bytes let mint_size: u64 = 278; let lamports = Rent::get()?.try_minimum_balance(mint_size as usize)?; - self.system_program + accounts.system_program .create_account( - &self.payer, - &self.mint_account, + &accounts.payer, + &accounts.mint_account, lamports, mint_size, - self.token_program.to_account_view().address(), + accounts.token_program.to_account_view().address(), ) .invoke()?; @@ -92,18 +91,18 @@ impl Initialize { ext_data[0] = 26; // TransferFeeExtension ext_data[1] = 0; // InitializeTransferFeeConfig sub-instruction ext_data[2] = 1; // COption::Some for config_authority - ext_data[3..35].copy_from_slice(self.payer.to_account_view().address().as_ref()); + ext_data[3..35].copy_from_slice(accounts.payer.to_account_view().address().as_ref()); ext_data[35] = 1; // COption::Some for withdraw_authority - ext_data[36..68].copy_from_slice(self.payer.to_account_view().address().as_ref()); + ext_data[36..68].copy_from_slice(accounts.payer.to_account_view().address().as_ref()); ext_data[68..70].copy_from_slice(&basis_points.to_le_bytes()); ext_data[70..78].copy_from_slice(&max_fee.to_le_bytes()); CpiCall::new( - self.token_program.to_account_view().address(), + accounts.token_program.to_account_view().address(), [InstructionAccount::writable( - self.mint_account.to_account_view().address(), + accounts.mint_account.to_account_view().address(), )], - [self.mint_account.to_account_view()], + [accounts.mint_account.to_account_view()], ext_data, ) .invoke()?; @@ -112,20 +111,19 @@ impl Initialize { let mut mint_data = [0u8; 67]; mint_data[0] = 20; mint_data[1] = 2; - mint_data[2..34].copy_from_slice(self.payer.to_account_view().address().as_ref()); + mint_data[2..34].copy_from_slice(accounts.payer.to_account_view().address().as_ref()); mint_data[34] = 1; - mint_data[35..67].copy_from_slice(self.payer.to_account_view().address().as_ref()); + mint_data[35..67].copy_from_slice(accounts.payer.to_account_view().address().as_ref()); CpiCall::new( - self.token_program.to_account_view().address(), + accounts.token_program.to_account_view().address(), [InstructionAccount::writable( - self.mint_account.to_account_view().address(), + accounts.mint_account.to_account_view().address(), )], - [self.mint_account.to_account_view()], + [accounts.mint_account.to_account_view()], mint_data, ) .invoke() - } } #[derive(Accounts)] @@ -140,9 +138,8 @@ pub struct Transfer { pub token_program: Program, } -impl Transfer { - #[inline(always)] - pub fn transfer(&mut self, amount: u64, fee: u64) -> Result<(), ProgramError> { +#[inline(always)] +fn handle_transfer(accounts: &mut Transfer, amount: u64, fee: u64) -> Result<(), ProgramError> { // TransferCheckedWithFee: opcode 37 // Data: [37, amount (u64 LE), decimals (u8), fee (u64 LE)] let mut data = [0u8; 18]; @@ -152,23 +149,22 @@ impl Transfer { data[10..18].copy_from_slice(&fee.to_le_bytes()); CpiCall::new( - self.token_program.to_account_view().address(), + accounts.token_program.to_account_view().address(), [ - InstructionAccount::writable(self.from.to_account_view().address()), - InstructionAccount::readonly(self.mint.to_account_view().address()), - InstructionAccount::writable(self.to.to_account_view().address()), - InstructionAccount::readonly_signer(self.sender.to_account_view().address()), + InstructionAccount::writable(accounts.from.to_account_view().address()), + InstructionAccount::readonly(accounts.mint.to_account_view().address()), + InstructionAccount::writable(accounts.to.to_account_view().address()), + InstructionAccount::readonly_signer(accounts.sender.to_account_view().address()), ], [ - self.from.to_account_view(), - self.mint.to_account_view(), - self.to.to_account_view(), - self.sender.to_account_view(), + accounts.from.to_account_view(), + accounts.mint.to_account_view(), + accounts.to.to_account_view(), + accounts.sender.to_account_view(), ], data, ) .invoke() - } } #[derive(Accounts)] @@ -179,9 +175,8 @@ pub struct UpdateFee { pub token_program: Program, } -impl UpdateFee { - #[inline(always)] - pub fn update_fee(&mut self, basis_points: u16, max_fee: u64) -> Result<(), ProgramError> { +#[inline(always)] +fn handle_update_fee(accounts: &mut UpdateFee, basis_points: u16, max_fee: u64) -> Result<(), ProgramError> { // SetTransferFee: opcode 26, sub-opcode 4 // Actually: extension instruction layout is different. // TransferFeeInstruction::SetTransferFee = 4 within type 26 @@ -192,19 +187,18 @@ impl UpdateFee { data[4..12].copy_from_slice(&max_fee.to_le_bytes()); CpiCall::new( - self.token_program.to_account_view().address(), + accounts.token_program.to_account_view().address(), [ - InstructionAccount::writable(self.mint_account.to_account_view().address()), - InstructionAccount::readonly_signer(self.authority.to_account_view().address()), + InstructionAccount::writable(accounts.mint_account.to_account_view().address()), + InstructionAccount::readonly_signer(accounts.authority.to_account_view().address()), ], [ - self.mint_account.to_account_view(), - self.authority.to_account_view(), + accounts.mint_account.to_account_view(), + accounts.authority.to_account_view(), ], data, ) .invoke() - } } #[derive(Accounts)] @@ -217,26 +211,24 @@ pub struct Withdraw { pub token_program: Program, } -impl Withdraw { - #[inline(always)] - pub fn withdraw(&mut self) -> Result<(), ProgramError> { +#[inline(always)] +fn handle_withdraw(accounts: &mut Withdraw) -> Result<(), ProgramError> { // WithdrawWithheldTokensFromMint: opcode 26, sub-opcode 3 let data: [u8; 2] = [26, 3]; CpiCall::new( - self.token_program.to_account_view().address(), + accounts.token_program.to_account_view().address(), [ - InstructionAccount::writable(self.mint_account.to_account_view().address()), - InstructionAccount::writable(self.destination.to_account_view().address()), - InstructionAccount::readonly_signer(self.authority.to_account_view().address()), + InstructionAccount::writable(accounts.mint_account.to_account_view().address()), + InstructionAccount::writable(accounts.destination.to_account_view().address()), + InstructionAccount::readonly_signer(accounts.authority.to_account_view().address()), ], [ - self.mint_account.to_account_view(), - self.destination.to_account_view(), - self.authority.to_account_view(), + accounts.mint_account.to_account_view(), + accounts.destination.to_account_view(), + accounts.authority.to_account_view(), ], data, ) .invoke() - } } diff --git a/tokens/token-extensions/transfer-hook/counter/quasar/src/lib.rs b/tokens/token-extensions/transfer-hook/counter/quasar/src/lib.rs index 91d2dba2e..01656552e 100644 --- a/tokens/token-extensions/transfer-hook/counter/quasar/src/lib.rs +++ b/tokens/token-extensions/transfer-hook/counter/quasar/src/lib.rs @@ -29,14 +29,14 @@ mod quasar_transfer_hook_counter { pub fn initialize_extra_account_meta_list( ctx: Ctx, ) -> Result<(), ProgramError> { - ctx.accounts.initialize_extra_account_meta_list() + handle_initialize_extra_account_meta_list(&mut ctx.accounts) } /// Transfer hook handler — increments the counter on each transfer. /// Discriminator = sha256("spl-transfer-hook-interface:execute")[:8] #[instruction(discriminator = [105, 37, 101, 197, 75, 251, 102, 26])] pub fn transfer_hook(ctx: Ctx, _amount: u64) -> Result<(), ProgramError> { - ctx.accounts.transfer_hook() + handle_transfer_hook(&mut ctx.accounts) } } @@ -58,9 +58,10 @@ pub struct InitializeExtraAccountMetaList { pub system_program: Program, } -impl InitializeExtraAccountMetaList { - #[inline(always)] - pub fn initialize_extra_account_meta_list(&mut self) -> Result<(), ProgramError> { +#[inline(always)] +fn handle_initialize_extra_account_meta_list( + accounts: &mut InitializeExtraAccountMetaList, +) -> Result<(), ProgramError> { // ExtraAccountMetaList with 1 extra account: // [8 bytes: Execute discriminator] // [4 bytes: data length] @@ -71,13 +72,13 @@ impl InitializeExtraAccountMetaList { let lamports = Rent::get()?.try_minimum_balance(meta_list_size as usize)?; // Derive ExtraAccountMetaList PDA - let mint_address = self.mint.to_account_view().address(); + let mint_address = accounts.mint.to_account_view().address(); let (expected_pda, bump) = Address::find_program_address( &[b"extra-account-metas", mint_address.as_ref()], &crate::ID, ); - let meta_list_address = self.extra_account_meta_list.to_account_view().address(); + let meta_list_address = accounts.extra_account_meta_list.to_account_view().address(); if meta_list_address != &expected_pda { return Err(ProgramError::InvalidSeeds); } @@ -90,10 +91,11 @@ impl InitializeExtraAccountMetaList { Seed::from(&bump_bytes as &[u8]), ]; - self.system_program + accounts + .system_program .create_account( - &self.payer, - &*self.extra_account_meta_list, + &accounts.payer, + &*accounts.extra_account_meta_list, lamports, meta_list_size, &crate::ID, @@ -102,7 +104,7 @@ impl InitializeExtraAccountMetaList { // Write TLV data with the counter PDA as an extra account let view = unsafe { - &mut *(self.extra_account_meta_list as *const UncheckedAccount as *mut UncheckedAccount + &mut *(accounts.extra_account_meta_list as *const UncheckedAccount as *mut UncheckedAccount as *mut AccountView) }; let mut data = view.try_borrow_mut()?; @@ -131,7 +133,7 @@ impl InitializeExtraAccountMetaList { let (counter_pda, counter_bump) = Address::find_program_address(&[b"counter"], &crate::ID); - let counter_address = self.counter_account.to_account_view().address(); + let counter_address = accounts.counter_account.to_account_view().address(); if counter_address != &counter_pda { return Err(ProgramError::InvalidSeeds); } @@ -142,10 +144,11 @@ impl InitializeExtraAccountMetaList { Seed::from(&counter_bump_bytes as &[u8]), ]; - self.system_program + accounts + .system_program .create_account( - &self.payer, - &*self.counter_account, + &accounts.payer, + &*accounts.counter_account, counter_lamports, counter_size, &crate::ID, @@ -154,7 +157,6 @@ impl InitializeExtraAccountMetaList { log("Extra account meta list and counter initialized"); Ok(()) - } } // --------------------------------------------------------------------------- @@ -178,12 +180,11 @@ pub struct TransferHook { pub counter_account: UncheckedAccount, } -impl TransferHook { - #[inline(always)] - pub fn transfer_hook(&mut self) -> Result<(), ProgramError> { +#[inline(always)] +fn handle_transfer_hook(accounts: &mut TransferHook) -> Result<(), ProgramError> { // Read the current counter from the account data let view = unsafe { - &mut *(self.counter_account as *const UncheckedAccount as *mut UncheckedAccount + &mut *(accounts.counter_account as *const UncheckedAccount as *mut UncheckedAccount as *mut AccountView) }; let mut data = view.try_borrow_mut()?; @@ -206,5 +207,4 @@ impl TransferHook { log("Transfer hook: counter incremented"); Ok(()) - } } diff --git a/tokens/token-extensions/transfer-hook/hello-world/quasar/src/lib.rs b/tokens/token-extensions/transfer-hook/hello-world/quasar/src/lib.rs index 98b04b71b..d7c3c73a8 100644 --- a/tokens/token-extensions/transfer-hook/hello-world/quasar/src/lib.rs +++ b/tokens/token-extensions/transfer-hook/hello-world/quasar/src/lib.rs @@ -37,7 +37,7 @@ mod quasar_transfer_hook_hello_world { /// Custom discriminator (not part of the transfer hook interface). #[instruction(discriminator = [0, 0, 0, 0, 0, 0, 0, 1])] pub fn initialize(ctx: Ctx, decimals: u8) -> Result<(), ProgramError> { - ctx.accounts.initialize(decimals) + handle_initialize(&mut ctx.accounts, decimals) } /// Create the ExtraAccountMetaList PDA (empty — no extra accounts). @@ -46,14 +46,14 @@ mod quasar_transfer_hook_hello_world { pub fn initialize_extra_account_meta_list( ctx: Ctx, ) -> Result<(), ProgramError> { - ctx.accounts.initialize_extra_account_meta_list() + handle_initialize_extra_account_meta_list(&mut ctx.accounts) } /// Transfer hook handler — called automatically by Token-2022 during transfers. /// Discriminator = sha256("spl-transfer-hook-interface:execute")[:8] #[instruction(discriminator = [105, 37, 101, 197, 75, 251, 102, 26])] pub fn transfer_hook(ctx: Ctx, _amount: u64) -> Result<(), ProgramError> { - ctx.accounts.transfer_hook() + handle_transfer_hook(&mut ctx.accounts) } } @@ -71,22 +71,21 @@ pub struct Initialize { pub system_program: Program, } -impl Initialize { - #[inline(always)] - pub fn initialize(&mut self, decimals: u8) -> Result<(), ProgramError> { +#[inline(always)] +fn handle_initialize(accounts: &mut Initialize, decimals: u8) -> Result<(), ProgramError> { // Mint with TransferHook extension: // 165 (base account + padding) + 1 (account type) + 4 (TLV header) + 64 (extension) = 234 let mint_size: u64 = 234; let lamports = Rent::get()?.try_minimum_balance(mint_size as usize)?; // 1. Create account owned by Token-2022 - self.system_program + accounts.system_program .create_account( - &self.payer, - &self.mint_account, + &accounts.payer, + &accounts.mint_account, lamports, mint_size, - self.token_program.to_account_view().address(), + accounts.token_program.to_account_view().address(), ) .invoke()?; @@ -96,15 +95,15 @@ impl Initialize { let mut ext_data = [0u8; 66]; ext_data[0] = 36; // TokenInstruction::TransferHookExtension ext_data[1] = 0; // TransferHookInstruction::Initialize - ext_data[2..34].copy_from_slice(self.payer.to_account_view().address().as_ref()); + ext_data[2..34].copy_from_slice(accounts.payer.to_account_view().address().as_ref()); ext_data[34..66].copy_from_slice(crate::ID.as_ref()); CpiCall::new( - self.token_program.to_account_view().address(), + accounts.token_program.to_account_view().address(), [InstructionAccount::writable( - self.mint_account.to_account_view().address(), + accounts.mint_account.to_account_view().address(), )], - [self.mint_account.to_account_view()], + [accounts.mint_account.to_account_view()], ext_data, ) .invoke()?; @@ -113,20 +112,19 @@ impl Initialize { let mut mint_data = [0u8; 67]; mint_data[0] = 20; mint_data[1] = decimals; - mint_data[2..34].copy_from_slice(self.payer.to_account_view().address().as_ref()); + mint_data[2..34].copy_from_slice(accounts.payer.to_account_view().address().as_ref()); mint_data[34] = 1; // has freeze authority - mint_data[35..67].copy_from_slice(self.payer.to_account_view().address().as_ref()); + mint_data[35..67].copy_from_slice(accounts.payer.to_account_view().address().as_ref()); CpiCall::new( - self.token_program.to_account_view().address(), + accounts.token_program.to_account_view().address(), [InstructionAccount::writable( - self.mint_account.to_account_view().address(), + accounts.mint_account.to_account_view().address(), )], - [self.mint_account.to_account_view()], + [accounts.mint_account.to_account_view()], mint_data, ) .invoke() - } } // --------------------------------------------------------------------------- @@ -144,9 +142,10 @@ pub struct InitializeExtraAccountMetaList { pub system_program: Program, } -impl InitializeExtraAccountMetaList { - #[inline(always)] - pub fn initialize_extra_account_meta_list(&mut self) -> Result<(), ProgramError> { +#[inline(always)] +fn handle_initialize_extra_account_meta_list( + accounts: &mut InitializeExtraAccountMetaList, +) -> Result<(), ProgramError> { use quasar_lang::cpi::Seed; // ExtraAccountMetaList with 0 extra accounts: @@ -158,13 +157,13 @@ impl InitializeExtraAccountMetaList { let lamports = Rent::get()?.try_minimum_balance(meta_list_size as usize)?; // Derive PDA - let mint_address = self.mint.to_account_view().address(); + let mint_address = accounts.mint.to_account_view().address(); let (expected_pda, bump) = Address::find_program_address( &[b"extra-account-metas", mint_address.as_ref()], &crate::ID, ); - let meta_list_address = self.extra_account_meta_list.to_account_view().address(); + let meta_list_address = accounts.extra_account_meta_list.to_account_view().address(); if meta_list_address != &expected_pda { return Err(ProgramError::InvalidSeeds); } @@ -177,10 +176,11 @@ impl InitializeExtraAccountMetaList { Seed::from(&bump_bytes as &[u8]), ]; - self.system_program + accounts + .system_program .create_account( - &self.payer, - &*self.extra_account_meta_list, + &accounts.payer, + &*accounts.extra_account_meta_list, lamports, meta_list_size, &crate::ID, @@ -191,7 +191,7 @@ impl InitializeExtraAccountMetaList { // SAFETY: Account was just created (16 bytes) and is owned by this program. // UncheckedAccount is #[repr(transparent)] over AccountView, so the cast is safe. let view = unsafe { - &mut *(self.extra_account_meta_list as *const UncheckedAccount as *mut UncheckedAccount + &mut *(accounts.extra_account_meta_list as *const UncheckedAccount as *mut UncheckedAccount as *mut AccountView) }; let mut data = view.try_borrow_mut()?; @@ -204,7 +204,6 @@ impl InitializeExtraAccountMetaList { log("Extra account meta list initialized"); Ok(()) - } } // --------------------------------------------------------------------------- @@ -225,9 +224,8 @@ pub struct TransferHook { pub extra_account_meta_list: UncheckedAccount, } -impl TransferHook { - #[inline(always)] - pub fn transfer_hook(&mut self) -> Result<(), ProgramError> { +#[inline(always)] +fn handle_transfer_hook(_accounts: &mut TransferHook) -> Result<(), ProgramError> { // In production, verify the source token's TransferHookAccount.transferring // flag is set. The Token-2022 program sets this before invoking the hook // and clears it after, preventing standalone invocation. @@ -235,5 +233,4 @@ impl TransferHook { // For this hello-world example, we simply log a message. log("Hello Transfer Hook!"); Ok(()) - } } diff --git a/tokens/token-extensions/transfer-hook/transfer-cost/quasar/src/lib.rs b/tokens/token-extensions/transfer-hook/transfer-cost/quasar/src/lib.rs index 143a4f6f7..225ff62a6 100644 --- a/tokens/token-extensions/transfer-hook/transfer-cost/quasar/src/lib.rs +++ b/tokens/token-extensions/transfer-hook/transfer-cost/quasar/src/lib.rs @@ -33,7 +33,7 @@ mod quasar_transfer_hook_cost { pub fn initialize_extra_account_meta_list( ctx: Ctx, ) -> Result<(), ProgramError> { - ctx.accounts.initialize_extra_account_meta_list() + handle_initialize_extra_account_meta_list(&mut ctx.accounts) } /// Transfer hook handler — validates the amount and increments the counter. @@ -41,7 +41,7 @@ mod quasar_transfer_hook_cost { /// Discriminator = sha256("spl-transfer-hook-interface:execute")[:8] #[instruction(discriminator = [105, 37, 101, 197, 75, 251, 102, 26])] pub fn transfer_hook(ctx: Ctx, amount: u64) -> Result<(), ProgramError> { - ctx.accounts.transfer_hook(amount) + handle_transfer_hook(&mut ctx.accounts, amount) } } @@ -61,19 +61,20 @@ pub struct InitializeExtraAccountMetaList { pub system_program: Program, } -impl InitializeExtraAccountMetaList { - #[inline(always)] - pub fn initialize_extra_account_meta_list(&mut self) -> Result<(), ProgramError> { +#[inline(always)] +fn handle_initialize_extra_account_meta_list( + accounts: &mut InitializeExtraAccountMetaList, +) -> Result<(), ProgramError> { // Create ExtraAccountMetaList PDA with 1 extra account: counter let meta_list_size: u64 = 51; let lamports = Rent::get()?.try_minimum_balance(meta_list_size as usize)?; - let mint_address = self.mint.to_account_view().address(); + let mint_address = accounts.mint.to_account_view().address(); let (expected_pda, bump) = Address::find_program_address( &[b"extra-account-metas", mint_address.as_ref()], &crate::ID, ); - if self.extra_account_meta_list.to_account_view().address() != &expected_pda { + if accounts.extra_account_meta_list.to_account_view().address() != &expected_pda { return Err(ProgramError::InvalidSeeds); } @@ -83,13 +84,14 @@ impl InitializeExtraAccountMetaList { Seed::from(mint_address.as_ref()), Seed::from(&bump_bytes as &[u8]), ]; - self.system_program - .create_account(&self.payer, &*self.extra_account_meta_list, lamports, meta_list_size, &crate::ID) + accounts + .system_program + .create_account(&accounts.payer, &*accounts.extra_account_meta_list, lamports, meta_list_size, &crate::ID) .invoke_signed(&seeds)?; // Write TLV data let view = unsafe { - &mut *(self.extra_account_meta_list as *const UncheckedAccount + &mut *(accounts.extra_account_meta_list as *const UncheckedAccount as *mut UncheckedAccount as *mut AccountView) }; let mut data = view.try_borrow_mut()?; @@ -114,7 +116,7 @@ impl InitializeExtraAccountMetaList { let (counter_pda, counter_bump) = Address::find_program_address(&[b"counter"], &crate::ID); - if self.counter_account.to_account_view().address() != &counter_pda { + if accounts.counter_account.to_account_view().address() != &counter_pda { return Err(ProgramError::InvalidSeeds); } @@ -123,13 +125,13 @@ impl InitializeExtraAccountMetaList { Seed::from(b"counter" as &[u8]), Seed::from(&counter_bump_bytes as &[u8]), ]; - self.system_program - .create_account(&self.payer, &*self.counter_account, counter_lamports, counter_size, &crate::ID) + accounts + .system_program + .create_account(&accounts.payer, &*accounts.counter_account, counter_lamports, counter_size, &crate::ID) .invoke_signed(&counter_seeds)?; log("Transfer cost hook initialized"); Ok(()) - } } // --------------------------------------------------------------------------- @@ -147,9 +149,8 @@ pub struct TransferHook { pub counter_account: UncheckedAccount, } -impl TransferHook { - #[inline(always)] - pub fn transfer_hook(&mut self, amount: u64) -> Result<(), ProgramError> { +#[inline(always)] +fn handle_transfer_hook(accounts: &mut TransferHook, amount: u64) -> Result<(), ProgramError> { // Validate amount if amount > 50 { log("Warning: large transfer amount"); @@ -157,7 +158,7 @@ impl TransferHook { // Increment transfer counter let view = unsafe { - &mut *(self.counter_account as *const UncheckedAccount as *mut UncheckedAccount + &mut *(accounts.counter_account as *const UncheckedAccount as *mut UncheckedAccount as *mut AccountView) }; let mut data = view.try_borrow_mut()?; @@ -181,5 +182,4 @@ impl TransferHook { log("Transfer cost hook: counter incremented"); Ok(()) - } } diff --git a/tokens/token-extensions/transfer-hook/transfer-switch/quasar/src/lib.rs b/tokens/token-extensions/transfer-hook/transfer-switch/quasar/src/lib.rs index d12b86d4f..87e7b33fe 100644 --- a/tokens/token-extensions/transfer-hook/transfer-switch/quasar/src/lib.rs +++ b/tokens/token-extensions/transfer-hook/transfer-switch/quasar/src/lib.rs @@ -25,7 +25,7 @@ mod quasar_transfer_hook_switch { /// Set up or change the admin. The first caller becomes admin. #[instruction(discriminator = [0, 0, 0, 0, 0, 0, 0, 1])] pub fn configure_admin(ctx: Ctx) -> Result<(), ProgramError> { - ctx.accounts.configure_admin() + handle_configure_admin(&mut ctx.accounts) } /// Create the ExtraAccountMetaList PDA. @@ -34,20 +34,20 @@ mod quasar_transfer_hook_switch { pub fn initialize_extra_account_metas_list( ctx: Ctx, ) -> Result<(), ProgramError> { - ctx.accounts.initialize_extra_account_metas_list() + handle_initialize_extra_account_metas_list(&mut ctx.accounts) } /// Toggle the transfer switch for a wallet. #[instruction(discriminator = [0, 0, 0, 0, 0, 0, 0, 3])] pub fn switch(ctx: Ctx, on: u8) -> Result<(), ProgramError> { - ctx.accounts.switch(on != 0) + handle_switch(&mut ctx.accounts, on != 0) } /// Transfer hook handler — checks the sender's switch is on. /// Discriminator = sha256("spl-transfer-hook-interface:execute")[:8] #[instruction(discriminator = [105, 37, 101, 197, 75, 251, 102, 26])] pub fn transfer_hook(ctx: Ctx, _amount: u64) -> Result<(), ProgramError> { - ctx.accounts.transfer_hook() + handle_transfer_hook(&mut ctx.accounts) } } @@ -70,56 +70,54 @@ pub struct ConfigureAdmin { pub system_program: Program, } -impl ConfigureAdmin { - #[inline(always)] - pub fn configure_admin(&mut self) -> Result<(), ProgramError> { - let view = self.admin_config.to_account_view(); - let data = view.try_borrow()?; - - // If already initialised, verify caller is the current admin - if data.len() >= 33 && data[32] != 0 { - let admin_address = self.admin.to_account_view().address(); - if &data[0..32] != admin_address.as_ref() { - log("Only the current admin can change the admin"); - return Err(ProgramError::IllegalOwner); - } - } - drop(data); +#[inline(always)] +fn handle_configure_admin(accounts: &mut ConfigureAdmin) -> Result<(), ProgramError> { + let view = accounts.admin_config.to_account_view(); + let data = view.try_borrow()?; - // Create or reuse admin_config PDA - let (admin_config_pda, bump) = - Address::find_program_address(&[b"admin-config"], &crate::ID); - if self.admin_config.to_account_view().address() != &admin_config_pda { - return Err(ProgramError::InvalidSeeds); - } - - // If account doesn't exist, create it - if self.admin_config.to_account_view().data_len() == 0 { - let size: u64 = 33; // 32 admin + 1 flag - let lamports = Rent::get()?.try_minimum_balance(size as usize)?; - let bump_bytes = [bump]; - let seeds = [ - Seed::from(b"admin-config" as &[u8]), - Seed::from(&bump_bytes as &[u8]), - ]; - self.system_program - .create_account(&self.admin, &*self.admin_config, lamports, size, &crate::ID) - .invoke_signed(&seeds)?; + // If already initialised, verify caller is the current admin + if data.len() >= 33 && data[32] != 0 { + let admin_address = accounts.admin.to_account_view().address(); + if &data[0..32] != admin_address.as_ref() { + log("Only the current admin can change the admin"); + return Err(ProgramError::IllegalOwner); } + } + drop(data); - // Write new admin - let mview = unsafe { - &mut *(self.admin_config as *const UncheckedAccount as *mut UncheckedAccount - as *mut AccountView) - }; - let mut data = mview.try_borrow_mut()?; - let new_admin_address = self.new_admin.to_account_view().address(); - data[0..32].copy_from_slice(new_admin_address.as_ref()); - data[32] = 1; // is_initialised + // Create or reuse admin_config PDA + let (admin_config_pda, bump) = Address::find_program_address(&[b"admin-config"], &crate::ID); + if accounts.admin_config.to_account_view().address() != &admin_config_pda { + return Err(ProgramError::InvalidSeeds); + } - log("Admin configured"); - Ok(()) + // If account doesn't exist, create it + if accounts.admin_config.to_account_view().data_len() == 0 { + let size: u64 = 33; // 32 admin + 1 flag + let lamports = Rent::get()?.try_minimum_balance(size as usize)?; + let bump_bytes = [bump]; + let seeds = [ + Seed::from(b"admin-config" as &[u8]), + Seed::from(&bump_bytes as &[u8]), + ]; + accounts + .system_program + .create_account(&accounts.admin, &*accounts.admin_config, lamports, size, &crate::ID) + .invoke_signed(&seeds)?; } + + // Write new admin + let mview = unsafe { + &mut *(accounts.admin_config as *const UncheckedAccount as *mut UncheckedAccount + as *mut AccountView) + }; + let mut data = mview.try_borrow_mut()?; + let new_admin_address = accounts.new_admin.to_account_view().address(); + data[0..32].copy_from_slice(new_admin_address.as_ref()); + data[32] = 1; // is_initialised + + log("Admin configured"); + Ok(()) } // --------------------------------------------------------------------------- @@ -136,19 +134,20 @@ pub struct InitializeExtraAccountMetas { pub system_program: Program, } -impl InitializeExtraAccountMetas { - #[inline(always)] - pub fn initialize_extra_account_metas_list(&mut self) -> Result<(), ProgramError> { +#[inline(always)] +fn handle_initialize_extra_account_metas_list( + accounts: &mut InitializeExtraAccountMetas, +) -> Result<(), ProgramError> { // 1 extra account: wallet switch PDA seeded by [AccountKey(index=3)] (sender/owner) let meta_list_size: u64 = 51; // 8 + 4 + 4 + 35 let lamports = Rent::get()?.try_minimum_balance(meta_list_size as usize)?; - let mint_address = self.token_mint.to_account_view().address(); + let mint_address = accounts.token_mint.to_account_view().address(); let (expected_pda, bump) = Address::find_program_address( &[b"extra-account-metas", mint_address.as_ref()], &crate::ID, ); - if self.extra_account_metas_list.to_account_view().address() != &expected_pda { + if accounts.extra_account_metas_list.to_account_view().address() != &expected_pda { return Err(ProgramError::InvalidSeeds); } @@ -159,12 +158,12 @@ impl InitializeExtraAccountMetas { Seed::from(&bump_bytes as &[u8]), ]; - self.system_program - .create_account(&self.payer, &*self.extra_account_metas_list, lamports, meta_list_size, &crate::ID) + accounts.system_program + .create_account(&accounts.payer, &*accounts.extra_account_metas_list, lamports, meta_list_size, &crate::ID) .invoke_signed(&seeds)?; let view = unsafe { - &mut *(self.extra_account_metas_list as *const UncheckedAccount + &mut *(accounts.extra_account_metas_list as *const UncheckedAccount as *mut UncheckedAccount as *mut AccountView) }; let mut data = view.try_borrow_mut()?; @@ -184,7 +183,6 @@ impl InitializeExtraAccountMetas { log("Extra account metas list initialized"); Ok(()) - } } // --------------------------------------------------------------------------- @@ -202,54 +200,53 @@ pub struct Switch { pub system_program: Program, } -impl Switch { - #[inline(always)] - pub fn switch(&mut self, on: bool) -> Result<(), ProgramError> { - // Verify admin - let config_view = self.admin_config.to_account_view(); - let config_data = config_view.try_borrow()?; - if config_data.len() < 33 || config_data[32] == 0 { - return Err(ProgramError::UninitializedAccount); - } - let admin_address = self.admin.to_account_view().address(); - if &config_data[0..32] != admin_address.as_ref() { - log("Only admin can switch"); - return Err(ProgramError::IllegalOwner); - } - drop(config_data); - - // Create wallet switch PDA if needed - let wallet_address = self.wallet.to_account_view().address(); - let (switch_pda, switch_bump) = - Address::find_program_address(&[wallet_address.as_ref()], &crate::ID); - if self.wallet_switch.to_account_view().address() != &switch_pda { - return Err(ProgramError::InvalidSeeds); - } +#[inline(always)] +fn handle_switch(accounts: &mut Switch, on: bool) -> Result<(), ProgramError> { + // Verify admin + let config_view = accounts.admin_config.to_account_view(); + let config_data = config_view.try_borrow()?; + if config_data.len() < 33 || config_data[32] == 0 { + return Err(ProgramError::UninitializedAccount); + } + let admin_address = accounts.admin.to_account_view().address(); + if &config_data[0..32] != admin_address.as_ref() { + log("Only admin can switch"); + return Err(ProgramError::IllegalOwner); + } + drop(config_data); + + // Create wallet switch PDA if needed + let wallet_address = accounts.wallet.to_account_view().address(); + let (switch_pda, switch_bump) = + Address::find_program_address(&[wallet_address.as_ref()], &crate::ID); + if accounts.wallet_switch.to_account_view().address() != &switch_pda { + return Err(ProgramError::InvalidSeeds); + } - if self.wallet_switch.to_account_view().data_len() == 0 { - let size: u64 = 33; // 32 wallet + 1 on - let lamports = Rent::get()?.try_minimum_balance(size as usize)?; - let switch_bump_bytes = [switch_bump]; - let switch_seeds = [ - Seed::from(wallet_address.as_ref()), - Seed::from(&switch_bump_bytes as &[u8]), - ]; - self.system_program - .create_account(&self.admin, &*self.wallet_switch, lamports, size, &crate::ID) - .invoke_signed(&switch_seeds)?; - } + if accounts.wallet_switch.to_account_view().data_len() == 0 { + let size: u64 = 33; // 32 wallet + 1 on + let lamports = Rent::get()?.try_minimum_balance(size as usize)?; + let switch_bump_bytes = [switch_bump]; + let switch_seeds = [ + Seed::from(wallet_address.as_ref()), + Seed::from(&switch_bump_bytes as &[u8]), + ]; + accounts + .system_program + .create_account(&accounts.admin, &*accounts.wallet_switch, lamports, size, &crate::ID) + .invoke_signed(&switch_seeds)?; + } - let mview = unsafe { - &mut *(self.wallet_switch as *const UncheckedAccount as *mut UncheckedAccount - as *mut AccountView) - }; - let mut data = mview.try_borrow_mut()?; - data[0..32].copy_from_slice(wallet_address.as_ref()); - data[32] = if on { 1 } else { 0 }; + let mview = unsafe { + &mut *(accounts.wallet_switch as *const UncheckedAccount as *mut UncheckedAccount + as *mut AccountView) + }; + let mut data = mview.try_borrow_mut()?; + data[0..32].copy_from_slice(wallet_address.as_ref()); + data[32] = if on { 1 } else { 0 }; - log("Switch toggled"); - Ok(()) - } + log("Switch toggled"); + Ok(()) } // --------------------------------------------------------------------------- @@ -267,23 +264,21 @@ pub struct TransferHook { pub wallet_switch: UncheckedAccount, } -impl TransferHook { - #[inline(always)] - pub fn transfer_hook(&mut self) -> Result<(), ProgramError> { - let switch_view = self.wallet_switch.to_account_view(); - let data = switch_view.try_borrow()?; - - if data.len() < 33 { - log("Switch not initialized — transfers disabled by default"); - return Err(ProgramError::UninitializedAccount); - } +#[inline(always)] +fn handle_transfer_hook(accounts: &mut TransferHook) -> Result<(), ProgramError> { + let switch_view = accounts.wallet_switch.to_account_view(); + let data = switch_view.try_borrow()?; - if data[32] != 1 { - log("Transfer switch is OFF"); - return Err(ProgramError::InvalidArgument); - } + if data.len() < 33 { + log("Switch not initialized — transfers disabled by default"); + return Err(ProgramError::UninitializedAccount); + } - log("Transfer switch is ON — transfer allowed"); - Ok(()) + if data[32] != 1 { + log("Transfer switch is OFF"); + return Err(ProgramError::InvalidArgument); } + + log("Transfer switch is ON — transfer allowed"); + Ok(()) } diff --git a/tokens/token-fundraiser/quasar/src/instructions/check_contributions.rs b/tokens/token-fundraiser/quasar/src/instructions/check_contributions.rs index f975bef48..5731db714 100644 --- a/tokens/token-fundraiser/quasar/src/instructions/check_contributions.rs +++ b/tokens/token-fundraiser/quasar/src/instructions/check_contributions.rs @@ -23,28 +23,26 @@ pub struct CheckContributions { pub token_program: Program, } -impl CheckContributions { - #[inline(always)] - pub fn check_contributions(&mut self, bumps: &CheckContributionsBumps) -> Result<(), ProgramError> { - // Verify the target was met - require!( - self.fundraiser.current_amount >= self.fundraiser.amount_to_raise, - ProgramError::Custom(0) // TargetNotMet - ); +#[inline(always)] +pub fn handle_check_contributions(accounts: &mut CheckContributions, bumps: &CheckContributionsBumps) -> Result<(), ProgramError> { + // Verify the target was met + require!( + accounts.fundraiser.current_amount >= accounts.fundraiser.amount_to_raise, + ProgramError::Custom(0) // TargetNotMet + ); - let seeds = self.fundraiser_seeds(bumps); + let seeds = accounts.fundraiser_seeds(bumps); - // Transfer all vault funds to the maker - let vault_amount = self.vault.amount(); - self.token_program - .transfer(&self.vault, &self.maker_ta, &self.fundraiser, vault_amount) - .invoke_signed(&seeds)?; + // Transfer all vault funds to the maker + let vault_amount = accounts.vault.amount(); + accounts.token_program + .transfer(&accounts.vault, &accounts.maker_ta, &accounts.fundraiser, vault_amount) + .invoke_signed(&seeds)?; - // Close the vault token account - self.token_program - .close_account(&self.vault, &self.maker, &self.fundraiser) - .invoke_signed(&seeds)?; + // Close the vault token account + accounts.token_program + .close_account(&accounts.vault, &accounts.maker, &accounts.fundraiser) + .invoke_signed(&seeds)?; - Ok(()) - } + Ok(()) } diff --git a/tokens/token-fundraiser/quasar/src/instructions/contribute.rs b/tokens/token-fundraiser/quasar/src/instructions/contribute.rs index 7bbc5fa2c..fa5d06c29 100644 --- a/tokens/token-fundraiser/quasar/src/instructions/contribute.rs +++ b/tokens/token-fundraiser/quasar/src/instructions/contribute.rs @@ -19,24 +19,22 @@ pub struct Contribute { pub token_program: Program, } -impl Contribute { - #[inline(always)] - pub fn contribute(&mut self, amount: u64) -> Result<(), ProgramError> { - require!(amount > 0, ProgramError::InvalidArgument); +#[inline(always)] +pub fn handle_contribute(accounts: &mut Contribute, amount: u64) -> Result<(), ProgramError> { + require!(amount > 0, ProgramError::InvalidArgument); - // Transfer tokens from contributor to vault - self.token_program - .transfer(&self.contributor_ta, &self.vault, &self.contributor, amount) - .invoke()?; + // Transfer tokens from contributor to vault + accounts.token_program + .transfer(&accounts.contributor_ta, &accounts.vault, &accounts.contributor, amount) + .invoke()?; - // Update fundraiser state - self.fundraiser.current_amount = self.fundraiser.current_amount.checked_add(amount) - .ok_or(ProgramError::ArithmeticOverflow)?; + // Update fundraiser state + accounts.fundraiser.current_amount = accounts.fundraiser.current_amount.checked_add(amount) + .ok_or(ProgramError::ArithmeticOverflow)?; - // Update contributor tracking - self.contributor_account.amount = self.contributor_account.amount.checked_add(amount) - .ok_or(ProgramError::ArithmeticOverflow)?; + // Update contributor tracking + accounts.contributor_account.amount = accounts.contributor_account.amount.checked_add(amount) + .ok_or(ProgramError::ArithmeticOverflow)?; - Ok(()) - } + Ok(()) } diff --git a/tokens/token-fundraiser/quasar/src/instructions/initialize.rs b/tokens/token-fundraiser/quasar/src/instructions/initialize.rs index 161211050..8be74ba09 100644 --- a/tokens/token-fundraiser/quasar/src/instructions/initialize.rs +++ b/tokens/token-fundraiser/quasar/src/instructions/initialize.rs @@ -18,26 +18,24 @@ pub struct Initialize { pub system_program: Program, } -impl Initialize { - #[inline(always)] - pub fn initialize( - &mut self, - amount_to_raise: u64, - duration: u16, - bump: u8, - ) -> Result<(), ProgramError> { - // Validate minimum raise amount - require!(amount_to_raise > 0, ProgramError::InvalidArgument); +#[inline(always)] +pub fn handle_initialize( + accounts: &mut Initialize, + amount_to_raise: u64, + duration: u16, + bump: u8, +) -> Result<(), ProgramError> { + // Validate minimum raise amount + require!(amount_to_raise > 0, ProgramError::InvalidArgument); - self.fundraiser.set_inner( - *self.maker.address(), - *self.mint_to_raise.address(), - amount_to_raise, - 0, // current_amount starts at 0 - 0, // time_started — would be Clock::get() onchain - duration, - bump, - ); - Ok(()) - } + accounts.fundraiser.set_inner( + *accounts.maker.address(), + *accounts.mint_to_raise.address(), + amount_to_raise, + 0, // current_amount starts at 0 + 0, // time_started — would be Clock::get() onchain + duration, + bump, + ); + Ok(()) } diff --git a/tokens/token-fundraiser/quasar/src/instructions/refund.rs b/tokens/token-fundraiser/quasar/src/instructions/refund.rs index 6e3a4de56..59172ced8 100644 --- a/tokens/token-fundraiser/quasar/src/instructions/refund.rs +++ b/tokens/token-fundraiser/quasar/src/instructions/refund.rs @@ -25,26 +25,24 @@ pub struct Refund { pub token_program: Program, } -impl Refund { - #[inline(always)] - pub fn refund(&mut self, bumps: &RefundBumps) -> Result<(), ProgramError> { - let refund_amount = self.contributor_account.amount; +#[inline(always)] +pub fn handle_refund(accounts: &mut Refund, bumps: &RefundBumps) -> Result<(), ProgramError> { + let refund_amount = accounts.contributor_account.amount; - let seeds = self.fundraiser_seeds(bumps); + let seeds = accounts.fundraiser_seeds(bumps); - // Transfer contributor's tokens back from vault - self.token_program - .transfer(&self.vault, &self.contributor_ta, &self.fundraiser, refund_amount) - .invoke_signed(&seeds)?; + // Transfer contributor's tokens back from vault + accounts.token_program + .transfer(&accounts.vault, &accounts.contributor_ta, &accounts.fundraiser, refund_amount) + .invoke_signed(&seeds)?; - // Update fundraiser state - self.fundraiser.current_amount = self.fundraiser.current_amount - .checked_sub(refund_amount) - .ok_or(ProgramError::ArithmeticOverflow)?; + // Update fundraiser state + accounts.fundraiser.current_amount = accounts.fundraiser.current_amount + .checked_sub(refund_amount) + .ok_or(ProgramError::ArithmeticOverflow)?; - // Zero out contributor amount - self.contributor_account.set_inner(0); + // Zero out contributor amount + accounts.contributor_account.set_inner(0); - Ok(()) - } + Ok(()) } diff --git a/tokens/token-swap/quasar/src/instructions/create_amm.rs b/tokens/token-swap/quasar/src/instructions/create_amm.rs index ffaf1ad87..2f2cd6be4 100644 --- a/tokens/token-swap/quasar/src/instructions/create_amm.rs +++ b/tokens/token-swap/quasar/src/instructions/create_amm.rs @@ -16,13 +16,11 @@ pub struct CreateAmm { pub system_program: Program, } -impl CreateAmm { - #[inline(always)] - pub fn create_amm(&mut self, id: Address, fee: u16) -> Result<(), ProgramError> { - if fee >= 10000 { - return Err(ProgramError::InvalidArgument); - } - self.amm.set_inner(id, *self.admin.address(), fee); - Ok(()) +#[inline(always)] +pub fn handle_create_amm(accounts: &mut CreateAmm, id: Address, fee: u16) -> Result<(), ProgramError> { + if fee >= 10000 { + return Err(ProgramError::InvalidArgument); } + accounts.amm.set_inner(id, *accounts.admin.address(), fee); + Ok(()) } diff --git a/tokens/token-swap/quasar/src/instructions/create_pool.rs b/tokens/token-swap/quasar/src/instructions/create_pool.rs index 252b11d55..c75e01b41 100644 --- a/tokens/token-swap/quasar/src/instructions/create_pool.rs +++ b/tokens/token-swap/quasar/src/instructions/create_pool.rs @@ -44,12 +44,10 @@ pub struct CreatePool { pub rent: Sysvar, } -impl CreatePool { - #[inline(always)] - pub fn create_pool(&mut self) -> Result<(), ProgramError> { - self.pool.amm = *self.amm.address(); - self.pool.mint_a = *self.mint_a.address(); - self.pool.mint_b = *self.mint_b.address(); - Ok(()) - } +#[inline(always)] +pub fn handle_create_pool(accounts: &mut CreatePool) -> Result<(), ProgramError> { + accounts.pool.amm = *accounts.amm.address(); + accounts.pool.mint_a = *accounts.mint_a.address(); + accounts.pool.mint_b = *accounts.mint_b.address(); + Ok(()) } diff --git a/tokens/token-swap/quasar/src/instructions/deposit_liquidity.rs b/tokens/token-swap/quasar/src/instructions/deposit_liquidity.rs index f12c9e580..a8ec95cb0 100644 --- a/tokens/token-swap/quasar/src/instructions/deposit_liquidity.rs +++ b/tokens/token-swap/quasar/src/instructions/deposit_liquidity.rs @@ -58,84 +58,82 @@ fn isqrt(n: u128) -> u64 { x as u64 } -impl DepositLiquidity { - #[inline(always)] - pub fn deposit_liquidity( - &mut self, - amount_a: u64, - amount_b: u64, - bumps: &DepositLiquidityBumps, - ) -> Result<(), ProgramError> { - // Clamp to what the depositor actually has. - let depositor_a = self.depositor_account_a.amount(); - let depositor_b = self.depositor_account_b.amount(); - let mut amount_a = if amount_a > depositor_a { depositor_a } else { amount_a }; - let mut amount_b = if amount_b > depositor_b { depositor_b } else { amount_b }; +#[inline(always)] +pub fn handle_deposit_liquidity( + accounts: &mut DepositLiquidity, + amount_a: u64, + amount_b: u64, + bumps: &DepositLiquidityBumps, +) -> Result<(), ProgramError> { + // Clamp to what the depositor actually has. + let depositor_a = accounts.depositor_account_a.amount(); + let depositor_b = accounts.depositor_account_b.amount(); + let mut amount_a = if amount_a > depositor_a { depositor_a } else { amount_a }; + let mut amount_b = if amount_b > depositor_b { depositor_b } else { amount_b }; - let pool_a_amount = self.pool_account_a.amount(); - let pool_b_amount = self.pool_account_b.amount(); - let pool_creation = pool_a_amount == 0 && pool_b_amount == 0; + let pool_a_amount = accounts.pool_account_a.amount(); + let pool_b_amount = accounts.pool_account_b.amount(); + let pool_creation = pool_a_amount == 0 && pool_b_amount == 0; - if !pool_creation { - // Adjust amounts to maintain the pool ratio. - if pool_a_amount > pool_b_amount { - amount_a = (amount_b as u128) - .checked_mul(pool_a_amount as u128) - .ok_or(ProgramError::ArithmeticOverflow)? - .checked_div(pool_b_amount as u128) - .ok_or(ProgramError::ArithmeticOverflow)? as u64; - } else { - amount_b = (amount_a as u128) - .checked_mul(pool_b_amount as u128) - .ok_or(ProgramError::ArithmeticOverflow)? - .checked_div(pool_a_amount as u128) - .ok_or(ProgramError::ArithmeticOverflow)? as u64; - } + if !pool_creation { + // Adjust amounts to maintain the pool ratio. + if pool_a_amount > pool_b_amount { + amount_a = (amount_b as u128) + .checked_mul(pool_a_amount as u128) + .ok_or(ProgramError::ArithmeticOverflow)? + .checked_div(pool_b_amount as u128) + .ok_or(ProgramError::ArithmeticOverflow)? as u64; + } else { + amount_b = (amount_a as u128) + .checked_mul(pool_b_amount as u128) + .ok_or(ProgramError::ArithmeticOverflow)? + .checked_div(pool_a_amount as u128) + .ok_or(ProgramError::ArithmeticOverflow)? as u64; } + } - // Compute liquidity = sqrt(amount_a * amount_b). - let product = (amount_a as u128) - .checked_mul(amount_b as u128) - .ok_or(ProgramError::ArithmeticOverflow)?; - let mut liquidity = isqrt(product); + // Compute liquidity = sqrt(amount_a * amount_b). + let product = (amount_a as u128) + .checked_mul(amount_b as u128) + .ok_or(ProgramError::ArithmeticOverflow)?; + let mut liquidity = isqrt(product); - // Lock minimum liquidity on first deposit. - if pool_creation { - if liquidity < crate::MINIMUM_LIQUIDITY { - return Err(ProgramError::InsufficientFunds); - } - liquidity -= crate::MINIMUM_LIQUIDITY; + // Lock minimum liquidity on first deposit. + if pool_creation { + if liquidity < crate::MINIMUM_LIQUIDITY { + return Err(ProgramError::InsufficientFunds); } + liquidity -= crate::MINIMUM_LIQUIDITY; + } - // Transfer token A to the pool. - self.token_program - .transfer(&self.depositor_account_a, &self.pool_account_a, &self.depositor, amount_a) - .invoke()?; + // Transfer token A to the pool. + accounts.token_program + .transfer(&accounts.depositor_account_a, &accounts.pool_account_a, &accounts.depositor, amount_a) + .invoke()?; - // Transfer token B to the pool. - self.token_program - .transfer(&self.depositor_account_b, &self.pool_account_b, &self.depositor, amount_b) - .invoke()?; + // Transfer token B to the pool. + accounts.token_program + .transfer(&accounts.depositor_account_b, &accounts.pool_account_b, &accounts.depositor, amount_b) + .invoke()?; - // Mint LP tokens to the depositor (signed by pool authority). - let bump = [bumps.pool_authority]; - let seeds: &[Seed] = &[ - Seed::from(self.amm.address().as_ref()), - Seed::from(self.mint_a.address().as_ref()), - Seed::from(self.mint_b.address().as_ref()), - Seed::from(crate::AUTHORITY_SEED), - Seed::from(&bump as &[u8]), - ]; + // Mint LP tokens to the depositor (signed by pool authority). + let bump = [bumps.pool_authority]; + let seeds: &[Seed] = &[ + Seed::from(accounts.amm.address().as_ref()), + Seed::from(accounts.mint_a.address().as_ref()), + Seed::from(accounts.mint_b.address().as_ref()), + Seed::from(crate::AUTHORITY_SEED), + Seed::from(&bump as &[u8]), + ]; - self.token_program - .mint_to( - &self.mint_liquidity, - &self.depositor_account_liquidity, - &self.pool_authority, - liquidity, - ) - .invoke_signed(seeds)?; + accounts.token_program + .mint_to( + &accounts.mint_liquidity, + &accounts.depositor_account_liquidity, + &accounts.pool_authority, + liquidity, + ) + .invoke_signed(seeds)?; - Ok(()) - } + Ok(()) } diff --git a/tokens/token-swap/quasar/src/instructions/swap_exact_tokens_for_tokens.rs b/tokens/token-swap/quasar/src/instructions/swap_exact_tokens_for_tokens.rs index c783cc72f..92cdc9118 100644 --- a/tokens/token-swap/quasar/src/instructions/swap_exact_tokens_for_tokens.rs +++ b/tokens/token-swap/quasar/src/instructions/swap_exact_tokens_for_tokens.rs @@ -31,108 +31,106 @@ pub struct SwapExactTokensForTokens { pub system_program: Program, } -impl SwapExactTokensForTokens { - #[inline(always)] - pub fn swap_exact_tokens_for_tokens( - &mut self, - swap_a: bool, - input_amount: u64, - min_output_amount: u64, - bumps: &SwapExactTokensForTokensBumps, - ) -> Result<(), ProgramError> { - // Clamp input to what the trader has. - let input = if swap_a { - let trader_a = self.trader_account_a.amount(); - if input_amount > trader_a { trader_a } else { input_amount } - } else { - let trader_b = self.trader_account_b.amount(); - if input_amount > trader_b { trader_b } else { input_amount } - }; +#[inline(always)] +pub fn handle_swap_exact_tokens_for_tokens( + accounts: &mut SwapExactTokensForTokens, + swap_a: bool, + input_amount: u64, + min_output_amount: u64, + bumps: &SwapExactTokensForTokensBumps, +) -> Result<(), ProgramError> { + // Clamp input to what the trader has. + let input = if swap_a { + let trader_a = accounts.trader_account_a.amount(); + if input_amount > trader_a { trader_a } else { input_amount } + } else { + let trader_b = accounts.trader_account_b.amount(); + if input_amount > trader_b { trader_b } else { input_amount } + }; - // Apply fee. - let fee = self.amm.fee.get() as u64; - let taxed_input = input - input * fee / 10000; + // Apply fee. + let fee = accounts.amm.fee.get() as u64; + let taxed_input = input - input * fee / 10000; - // Constant-product formula: output = taxed_input * pool_out / (pool_in + taxed_input) - let pool_a = self.pool_account_a.amount(); - let pool_b = self.pool_account_b.amount(); + // Constant-product formula: output = taxed_input * pool_out / (pool_in + taxed_input) + let pool_a = accounts.pool_account_a.amount(); + let pool_b = accounts.pool_account_b.amount(); - let output = if swap_a { - (taxed_input as u128) - .checked_mul(pool_b as u128) - .ok_or(ProgramError::ArithmeticOverflow)? - .checked_div( - (pool_a as u128) - .checked_add(taxed_input as u128) - .ok_or(ProgramError::ArithmeticOverflow)?, - ) - .ok_or(ProgramError::ArithmeticOverflow)? as u64 - } else { - (taxed_input as u128) - .checked_mul(pool_a as u128) - .ok_or(ProgramError::ArithmeticOverflow)? - .checked_div( - (pool_b as u128) - .checked_add(taxed_input as u128) - .ok_or(ProgramError::ArithmeticOverflow)?, - ) - .ok_or(ProgramError::ArithmeticOverflow)? as u64 - }; - - if output < min_output_amount { - return Err(ProgramError::Custom(4)); // OutputTooSmall - } - - // Record invariant before the trade. - let invariant = (pool_a as u128) + let output = if swap_a { + (taxed_input as u128) .checked_mul(pool_b as u128) - .ok_or(ProgramError::ArithmeticOverflow)?; + .ok_or(ProgramError::ArithmeticOverflow)? + .checked_div( + (pool_a as u128) + .checked_add(taxed_input as u128) + .ok_or(ProgramError::ArithmeticOverflow)?, + ) + .ok_or(ProgramError::ArithmeticOverflow)? as u64 + } else { + (taxed_input as u128) + .checked_mul(pool_a as u128) + .ok_or(ProgramError::ArithmeticOverflow)? + .checked_div( + (pool_b as u128) + .checked_add(taxed_input as u128) + .ok_or(ProgramError::ArithmeticOverflow)?, + ) + .ok_or(ProgramError::ArithmeticOverflow)? as u64 + }; + + if output < min_output_amount { + return Err(ProgramError::Custom(4)); // OutputTooSmall + } - // Build authority signer seeds. - let bump = [bumps.pool_authority]; - let seeds: &[Seed] = &[ - Seed::from(self.amm.address().as_ref()), - Seed::from(self.mint_a.address().as_ref()), - Seed::from(self.mint_b.address().as_ref()), - Seed::from(crate::AUTHORITY_SEED), - Seed::from(&bump as &[u8]), - ]; + // Record invariant before the trade. + let invariant = (pool_a as u128) + .checked_mul(pool_b as u128) + .ok_or(ProgramError::ArithmeticOverflow)?; - if swap_a { - // Trader sends token A to pool. - self.token_program - .transfer(&self.trader_account_a, &self.pool_account_a, &self.trader, input) - .invoke()?; - // Pool sends token B to trader (signed). - self.token_program - .transfer(&self.pool_account_b, &self.trader_account_b, &self.pool_authority, output) - .invoke_signed(seeds)?; - } else { - // Pool sends token A to trader (signed). - self.token_program - .transfer(&self.pool_account_a, &self.trader_account_a, &self.pool_authority, output) - .invoke_signed(seeds)?; - // Trader sends token B to pool. - self.token_program - .transfer(&self.trader_account_b, &self.pool_account_b, &self.trader, input) - .invoke()?; - } + // Build authority signer seeds. + let bump = [bumps.pool_authority]; + let seeds: &[Seed] = &[ + Seed::from(accounts.amm.address().as_ref()), + Seed::from(accounts.mint_a.address().as_ref()), + Seed::from(accounts.mint_b.address().as_ref()), + Seed::from(crate::AUTHORITY_SEED), + Seed::from(&bump as &[u8]), + ]; - // Verify invariant holds (new product >= old product). - let new_pool_a = pool_a as u128 - + if swap_a { input as u128 } else { 0 } - - if !swap_a { output as u128 } else { 0 }; - let new_pool_b = pool_b as u128 - + if !swap_a { input as u128 } else { 0 } - - if swap_a { output as u128 } else { 0 }; - let new_invariant = new_pool_a - .checked_mul(new_pool_b) - .ok_or(ProgramError::ArithmeticOverflow)?; + if swap_a { + // Trader sends token A to pool. + accounts.token_program + .transfer(&accounts.trader_account_a, &accounts.pool_account_a, &accounts.trader, input) + .invoke()?; + // Pool sends token B to trader (signed). + accounts.token_program + .transfer(&accounts.pool_account_b, &accounts.trader_account_b, &accounts.pool_authority, output) + .invoke_signed(seeds)?; + } else { + // Pool sends token A to trader (signed). + accounts.token_program + .transfer(&accounts.pool_account_a, &accounts.trader_account_a, &accounts.pool_authority, output) + .invoke_signed(seeds)?; + // Trader sends token B to pool. + accounts.token_program + .transfer(&accounts.trader_account_b, &accounts.pool_account_b, &accounts.trader, input) + .invoke()?; + } - if new_invariant < invariant { - return Err(ProgramError::Custom(5)); // InvariantViolated - } + // Verify invariant holds (new product >= old product). + let new_pool_a = pool_a as u128 + + if swap_a { input as u128 } else { 0 } + - if !swap_a { output as u128 } else { 0 }; + let new_pool_b = pool_b as u128 + + if !swap_a { input as u128 } else { 0 } + - if swap_a { output as u128 } else { 0 }; + let new_invariant = new_pool_a + .checked_mul(new_pool_b) + .ok_or(ProgramError::ArithmeticOverflow)?; - Ok(()) + if new_invariant < invariant { + return Err(ProgramError::Custom(5)); // InvariantViolated } + + Ok(()) } diff --git a/tokens/token-swap/quasar/src/instructions/withdraw_liquidity.rs b/tokens/token-swap/quasar/src/instructions/withdraw_liquidity.rs index a5336071f..6fc9ecf69 100644 --- a/tokens/token-swap/quasar/src/instructions/withdraw_liquidity.rs +++ b/tokens/token-swap/quasar/src/instructions/withdraw_liquidity.rs @@ -37,52 +37,50 @@ pub struct WithdrawLiquidity { pub system_program: Program, } -impl WithdrawLiquidity { - #[inline(always)] - pub fn withdraw_liquidity( - &mut self, - amount: u64, - bumps: &WithdrawLiquidityBumps, - ) -> Result<(), ProgramError> { - let bump = [bumps.pool_authority]; - let seeds: &[Seed] = &[ - Seed::from(self.amm.address().as_ref()), - Seed::from(self.mint_a.address().as_ref()), - Seed::from(self.mint_b.address().as_ref()), - Seed::from(crate::AUTHORITY_SEED), - Seed::from(&bump as &[u8]), - ]; +#[inline(always)] +pub fn handle_withdraw_liquidity( + accounts: &mut WithdrawLiquidity, + amount: u64, + bumps: &WithdrawLiquidityBumps, +) -> Result<(), ProgramError> { + let bump = [bumps.pool_authority]; + let seeds: &[Seed] = &[ + Seed::from(accounts.amm.address().as_ref()), + Seed::from(accounts.mint_a.address().as_ref()), + Seed::from(accounts.mint_b.address().as_ref()), + Seed::from(crate::AUTHORITY_SEED), + Seed::from(&bump as &[u8]), + ]; - // Compute proportional amounts. - let total_liquidity = self.mint_liquidity.supply() + crate::MINIMUM_LIQUIDITY; + // Compute proportional amounts. + let total_liquidity = accounts.mint_liquidity.supply() + crate::MINIMUM_LIQUIDITY; - let amount_a = (amount as u128) - .checked_mul(self.pool_account_a.amount() as u128) - .ok_or(ProgramError::ArithmeticOverflow)? - .checked_div(total_liquidity as u128) - .ok_or(ProgramError::ArithmeticOverflow)? as u64; + let amount_a = (amount as u128) + .checked_mul(accounts.pool_account_a.amount() as u128) + .ok_or(ProgramError::ArithmeticOverflow)? + .checked_div(total_liquidity as u128) + .ok_or(ProgramError::ArithmeticOverflow)? as u64; - let amount_b = (amount as u128) - .checked_mul(self.pool_account_b.amount() as u128) - .ok_or(ProgramError::ArithmeticOverflow)? - .checked_div(total_liquidity as u128) - .ok_or(ProgramError::ArithmeticOverflow)? as u64; + let amount_b = (amount as u128) + .checked_mul(accounts.pool_account_b.amount() as u128) + .ok_or(ProgramError::ArithmeticOverflow)? + .checked_div(total_liquidity as u128) + .ok_or(ProgramError::ArithmeticOverflow)? as u64; - // Transfer token A from pool to depositor. - self.token_program - .transfer(&self.pool_account_a, &self.depositor_account_a, &self.pool_authority, amount_a) - .invoke_signed(seeds)?; + // Transfer token A from pool to depositor. + accounts.token_program + .transfer(&accounts.pool_account_a, &accounts.depositor_account_a, &accounts.pool_authority, amount_a) + .invoke_signed(seeds)?; - // Transfer token B from pool to depositor. - self.token_program - .transfer(&self.pool_account_b, &self.depositor_account_b, &self.pool_authority, amount_b) - .invoke_signed(seeds)?; + // Transfer token B from pool to depositor. + accounts.token_program + .transfer(&accounts.pool_account_b, &accounts.depositor_account_b, &accounts.pool_authority, amount_b) + .invoke_signed(seeds)?; - // Burn LP tokens. - self.token_program - .burn(&self.depositor_account_liquidity, &self.mint_liquidity, &self.depositor, amount) - .invoke()?; + // Burn LP tokens. + accounts.token_program + .burn(&accounts.depositor_account_liquidity, &accounts.mint_liquidity, &accounts.depositor, amount) + .invoke()?; - Ok(()) - } + Ok(()) } diff --git a/tokens/transfer-tokens/quasar/src/lib.rs b/tokens/transfer-tokens/quasar/src/lib.rs index cd0d3cc5a..3bc0e075a 100644 --- a/tokens/transfer-tokens/quasar/src/lib.rs +++ b/tokens/transfer-tokens/quasar/src/lib.rs @@ -20,13 +20,13 @@ mod quasar_transfer_tokens { /// Mint tokens to a recipient's token account. #[instruction(discriminator = 0)] pub fn mint_tokens(ctx: Ctx, amount: u64) -> Result<(), ProgramError> { - ctx.accounts.mint_tokens(amount) + handle_mint_tokens(&mut ctx.accounts, amount) } /// Transfer tokens from sender to recipient. #[instruction(discriminator = 1)] pub fn transfer_tokens(ctx: Ctx, amount: u64) -> Result<(), ProgramError> { - ctx.accounts.transfer_tokens(amount) + handle_transfer_tokens(&mut ctx.accounts, amount) } } @@ -43,13 +43,11 @@ pub struct MintTokens { pub token_program: Program, } -impl MintTokens { - #[inline(always)] - pub fn mint_tokens(&mut self, amount: u64) -> Result<(), ProgramError> { - self.token_program - .mint_to(&self.mint, &self.recipient_token_account, &self.mint_authority, amount) - .invoke() - } +#[inline(always)] +fn handle_mint_tokens(accounts: &mut MintTokens, amount: u64) -> Result<(), ProgramError> { + accounts.token_program + .mint_to(&accounts.mint, &accounts.recipient_token_account, &accounts.mint_authority, amount) + .invoke() } /// Accounts for transferring tokens between two token accounts. @@ -64,11 +62,9 @@ pub struct TransferTokens { pub token_program: Program, } -impl TransferTokens { - #[inline(always)] - pub fn transfer_tokens(&mut self, amount: u64) -> Result<(), ProgramError> { - self.token_program - .transfer(&self.sender_token_account, &self.recipient_token_account, &self.sender, amount) - .invoke() - } +#[inline(always)] +fn handle_transfer_tokens(accounts: &mut TransferTokens, amount: u64) -> Result<(), ProgramError> { + accounts.token_program + .transfer(&accounts.sender_token_account, &accounts.recipient_token_account, &accounts.sender, amount) + .invoke() } From 92e79dab46e8521e49c481ec517e105f04f635b4 Mon Sep 17 00:00:00 2001 From: Jimii Date: Wed, 22 Apr 2026 22:34:29 +0300 Subject: [PATCH 04/12] fix: replace method-call dispatch with free-function handlers across all quasar programs --- basics/account-data/quasar/src/lib.rs | 2 +- basics/checking-accounts/quasar/src/lib.rs | 2 +- basics/close-account/quasar/src/lib.rs | 4 ++-- basics/counter/quasar/src/lib.rs | 4 ++-- basics/create-account/quasar/src/lib.rs | 2 +- basics/favorites/quasar/src/lib.rs | 2 +- basics/hello-solana/quasar/src/lib.rs | 2 +- basics/pda-rent-payer/quasar/src/lib.rs | 4 ++-- basics/processing-instructions/quasar/src/lib.rs | 2 +- .../program-derived-addresses/quasar/src/lib.rs | 4 ++-- basics/realloc/quasar/src/lib.rs | 4 ++-- basics/rent/quasar/src/lib.rs | 2 +- basics/repository-layout/quasar/src/lib.rs | 6 +++--- basics/transfer-sol/quasar/src/lib.rs | 4 ++-- compression/cnft-burn/quasar/src/lib.rs | 2 +- compression/cnft-vault/quasar/src/lib.rs | 4 ++-- compression/cutils/quasar/src/lib.rs | 4 ++-- oracles/pyth/quasar/src/lib.rs | 2 +- tokens/escrow/quasar/src/lib.rs | 10 +++++----- tokens/nft-operations/quasar/src/lib.rs | 6 +++--- tokens/spl-token-minter/quasar/src/lib.rs | 4 ++-- tokens/token-fundraiser/quasar/src/lib.rs | 8 ++++---- tokens/token-swap/quasar/src/lib.rs | 16 +++++++++++----- 23 files changed, 53 insertions(+), 47 deletions(-) diff --git a/basics/account-data/quasar/src/lib.rs b/basics/account-data/quasar/src/lib.rs index 7f7c6af0e..c32361401 100644 --- a/basics/account-data/quasar/src/lib.rs +++ b/basics/account-data/quasar/src/lib.rs @@ -30,6 +30,6 @@ mod quasar_account_data { street: String, city: String, ) -> Result<(), ProgramError> { - ctx.accounts.create_address_info(name, house_number, street, city) + instructions::handle_create_address_info(&mut ctx.accounts, name, house_number, street, city) } } diff --git a/basics/checking-accounts/quasar/src/lib.rs b/basics/checking-accounts/quasar/src/lib.rs index f77707649..61df8ebd2 100644 --- a/basics/checking-accounts/quasar/src/lib.rs +++ b/basics/checking-accounts/quasar/src/lib.rs @@ -19,6 +19,6 @@ mod quasar_checking_accounts { /// - Program: checks account is executable and is the system program #[instruction(discriminator = 0)] pub fn check_accounts(ctx: Ctx) -> Result<(), ProgramError> { - ctx.accounts.check_accounts() + instructions::handle_check_accounts(&mut ctx.accounts) } } diff --git a/basics/close-account/quasar/src/lib.rs b/basics/close-account/quasar/src/lib.rs index 96e01ea09..54aaa0e93 100644 --- a/basics/close-account/quasar/src/lib.rs +++ b/basics/close-account/quasar/src/lib.rs @@ -18,12 +18,12 @@ mod quasar_close_account { #[instruction(discriminator = 0)] pub fn create_user(ctx: Ctx, name: String) -> Result<(), ProgramError> { let bump = ctx.bumps.user_account; - ctx.accounts.create_user(name, bump) + instructions::handle_create_user(&mut ctx.accounts, name, bump) } /// Close a user account and return lamports to the user. #[instruction(discriminator = 1)] pub fn close_user(ctx: Ctx) -> Result<(), ProgramError> { - ctx.accounts.close_user() + instructions::handle_close_user(&mut ctx.accounts) } } diff --git a/basics/counter/quasar/src/lib.rs b/basics/counter/quasar/src/lib.rs index 21e738209..2655e7db5 100644 --- a/basics/counter/quasar/src/lib.rs +++ b/basics/counter/quasar/src/lib.rs @@ -16,11 +16,11 @@ mod quasar_counter { #[instruction(discriminator = 0)] pub fn initialize_counter(ctx: Ctx) -> Result<(), ProgramError> { - ctx.accounts.initialize_counter() + instructions::handle_initialize_counter(&mut ctx.accounts) } #[instruction(discriminator = 1)] pub fn increment(ctx: Ctx) -> Result<(), ProgramError> { - ctx.accounts.increment() + instructions::handle_increment(&mut ctx.accounts) } } diff --git a/basics/create-account/quasar/src/lib.rs b/basics/create-account/quasar/src/lib.rs index 775b74ce6..60e356a0d 100644 --- a/basics/create-account/quasar/src/lib.rs +++ b/basics/create-account/quasar/src/lib.rs @@ -16,6 +16,6 @@ mod quasar_create_account { /// Create a new system-owned account via CPI to the system program. #[instruction(discriminator = 0)] pub fn create_system_account(ctx: Ctx) -> Result<(), ProgramError> { - ctx.accounts.create_system_account() + instructions::handle_create_system_account(&mut ctx.accounts) } } diff --git a/basics/favorites/quasar/src/lib.rs b/basics/favorites/quasar/src/lib.rs index c81a10891..c158b1ba1 100644 --- a/basics/favorites/quasar/src/lib.rs +++ b/basics/favorites/quasar/src/lib.rs @@ -24,6 +24,6 @@ mod quasar_favorites { number: u64, color: String, ) -> Result<(), ProgramError> { - ctx.accounts.set_favorites(number, color) + instructions::handle_set_favorites(&mut ctx.accounts, number, color) } } diff --git a/basics/hello-solana/quasar/src/lib.rs b/basics/hello-solana/quasar/src/lib.rs index 724306d0a..467d6b323 100644 --- a/basics/hello-solana/quasar/src/lib.rs +++ b/basics/hello-solana/quasar/src/lib.rs @@ -15,6 +15,6 @@ mod quasar_hello_solana { #[instruction(discriminator = 0)] pub fn hello(ctx: Ctx) -> Result<(), ProgramError> { - ctx.accounts.hello() + instructions::handle_hello(&mut ctx.accounts) } } diff --git a/basics/pda-rent-payer/quasar/src/lib.rs b/basics/pda-rent-payer/quasar/src/lib.rs index a27ffce4a..de3cb39a6 100644 --- a/basics/pda-rent-payer/quasar/src/lib.rs +++ b/basics/pda-rent-payer/quasar/src/lib.rs @@ -16,13 +16,13 @@ mod quasar_pda_rent_payer { /// Fund a PDA "rent vault" by transferring lamports from the payer. #[instruction(discriminator = 0)] pub fn init_rent_vault(ctx: Ctx, fund_lamports: u64) -> Result<(), ProgramError> { - ctx.accounts.init_rent_vault(fund_lamports) + instructions::handle_init_rent_vault(&mut ctx.accounts, fund_lamports) } /// Create a new account using the rent vault PDA as the funding source. /// The vault signs the CPI via PDA seeds. #[instruction(discriminator = 1)] pub fn create_new_account(ctx: Ctx) -> Result<(), ProgramError> { - ctx.accounts.create_new_account(ctx.bumps.rent_vault) + instructions::handle_create_new_account(&mut ctx.accounts, ctx.bumps.rent_vault) } } diff --git a/basics/processing-instructions/quasar/src/lib.rs b/basics/processing-instructions/quasar/src/lib.rs index 0f1f02331..8af46fce5 100644 --- a/basics/processing-instructions/quasar/src/lib.rs +++ b/basics/processing-instructions/quasar/src/lib.rs @@ -18,6 +18,6 @@ mod quasar_processing_instructions { /// can't interpolate them into log messages (no format! in no_std). #[instruction(discriminator = 0)] pub fn go_to_park(ctx: Ctx, name: String, height: u32) -> Result<(), ProgramError> { - ctx.accounts.go_to_park(name, height) + instructions::handle_go_to_park(&mut ctx.accounts, name, height) } } diff --git a/basics/program-derived-addresses/quasar/src/lib.rs b/basics/program-derived-addresses/quasar/src/lib.rs index 4f20ee5ee..d9bde8884 100644 --- a/basics/program-derived-addresses/quasar/src/lib.rs +++ b/basics/program-derived-addresses/quasar/src/lib.rs @@ -17,12 +17,12 @@ mod quasar_program_derived_addresses { /// Create a PDA-based page visits counter for the payer. #[instruction(discriminator = 0)] pub fn create_page_visits(ctx: Ctx) -> Result<(), ProgramError> { - ctx.accounts.create_page_visits() + instructions::handle_create_page_visits(&mut ctx.accounts) } /// Increment the page visits counter. #[instruction(discriminator = 1)] pub fn increment_page_visits(ctx: Ctx) -> Result<(), ProgramError> { - ctx.accounts.increment_page_visits() + instructions::handle_increment_page_visits(&mut ctx.accounts) } } diff --git a/basics/realloc/quasar/src/lib.rs b/basics/realloc/quasar/src/lib.rs index da21029a2..e5af82ee9 100644 --- a/basics/realloc/quasar/src/lib.rs +++ b/basics/realloc/quasar/src/lib.rs @@ -17,13 +17,13 @@ mod quasar_realloc { /// Create a message account with an initial message. #[instruction(discriminator = 0)] pub fn initialize(ctx: Ctx, message: String) -> Result<(), ProgramError> { - ctx.accounts.initialize(message) + instructions::handle_initialize(&mut ctx.accounts, message) } /// Update the message, reallocating if the new message is longer. /// Quasar's `set_inner` handles realloc transparently. #[instruction(discriminator = 1)] pub fn update(ctx: Ctx, message: String) -> Result<(), ProgramError> { - ctx.accounts.update(message) + instructions::handle_update(&mut ctx.accounts, message) } } diff --git a/basics/rent/quasar/src/lib.rs b/basics/rent/quasar/src/lib.rs index fce2f678a..4c2740a1f 100644 --- a/basics/rent/quasar/src/lib.rs +++ b/basics/rent/quasar/src/lib.rs @@ -25,6 +25,6 @@ mod quasar_rent { name: String, address: String, ) -> Result<(), ProgramError> { - ctx.accounts.create_system_account(name, address) + instructions::handle_create_system_account(&mut ctx.accounts, name, address) } } diff --git a/basics/repository-layout/quasar/src/lib.rs b/basics/repository-layout/quasar/src/lib.rs index 2b67aae96..d8b47eefe 100644 --- a/basics/repository-layout/quasar/src/lib.rs +++ b/basics/repository-layout/quasar/src/lib.rs @@ -23,7 +23,7 @@ mod quasar_carnival { ticket_count: u32, ride_name: String, ) -> Result<(), ProgramError> { - ctx.accounts.go_on_ride(name, height, ticket_count, ride_name) + instructions::handle_go_on_ride(&mut ctx.accounts, name, height, ticket_count, ride_name) } /// Play a carnival game. Validates ticket requirements. @@ -34,7 +34,7 @@ mod quasar_carnival { ticket_count: u32, game_name: String, ) -> Result<(), ProgramError> { - ctx.accounts.play_game(name, ticket_count, game_name) + instructions::handle_play_game(&mut ctx.accounts, name, ticket_count, game_name) } /// Eat at a carnival food stand. Validates ticket requirements. @@ -45,6 +45,6 @@ mod quasar_carnival { ticket_count: u32, food_stand_name: String, ) -> Result<(), ProgramError> { - ctx.accounts.eat_food(name, ticket_count, food_stand_name) + instructions::handle_eat_food(&mut ctx.accounts, name, ticket_count, food_stand_name) } } diff --git a/basics/transfer-sol/quasar/src/lib.rs b/basics/transfer-sol/quasar/src/lib.rs index 7e66ed75b..f0a57bfb2 100644 --- a/basics/transfer-sol/quasar/src/lib.rs +++ b/basics/transfer-sol/quasar/src/lib.rs @@ -19,7 +19,7 @@ mod quasar_transfer_sol { ctx: Ctx, amount: u64, ) -> Result<(), ProgramError> { - ctx.accounts.transfer_sol_with_cpi(amount) + instructions::handle_transfer_sol_with_cpi(&mut ctx.accounts, amount) } /// Transfer SOL by directly manipulating lamports. @@ -29,6 +29,6 @@ mod quasar_transfer_sol { ctx: Ctx, amount: u64, ) -> Result<(), ProgramError> { - ctx.accounts.transfer_sol_with_program(amount) + instructions::handle_transfer_sol_with_program(&mut ctx.accounts, amount) } } diff --git a/compression/cnft-burn/quasar/src/lib.rs b/compression/cnft-burn/quasar/src/lib.rs index be63732b1..5c9f9978f 100644 --- a/compression/cnft-burn/quasar/src/lib.rs +++ b/compression/cnft-burn/quasar/src/lib.rs @@ -32,6 +32,6 @@ mod quasar_cnft_burn { #[instruction(discriminator = 0)] pub fn burn_cnft(ctx: CtxWithRemaining) -> Result<(), ProgramError> { - ctx.accounts.burn_cnft(&ctx) + instructions::handle_burn_cnft(&mut ctx.accounts, &ctx) } } diff --git a/compression/cnft-vault/quasar/src/lib.rs b/compression/cnft-vault/quasar/src/lib.rs index 1f65adc4a..95ca7a28a 100644 --- a/compression/cnft-vault/quasar/src/lib.rs +++ b/compression/cnft-vault/quasar/src/lib.rs @@ -33,12 +33,12 @@ mod quasar_cnft_vault { /// Withdraw a single compressed NFT from the vault PDA. #[instruction(discriminator = 0)] pub fn withdraw_cnft(ctx: CtxWithRemaining) -> Result<(), ProgramError> { - ctx.accounts.withdraw_cnft(&ctx) + instructions::handle_withdraw_cnft(&mut ctx.accounts, &ctx) } /// Withdraw two compressed NFTs from the vault PDA in a single transaction. #[instruction(discriminator = 1)] pub fn withdraw_two_cnfts(ctx: CtxWithRemaining) -> Result<(), ProgramError> { - ctx.accounts.withdraw_two_cnfts(&ctx) + instructions::handle_withdraw_two_cnfts(&mut ctx.accounts, &ctx) } } diff --git a/compression/cutils/quasar/src/lib.rs b/compression/cutils/quasar/src/lib.rs index 100a91f94..eebbf2c02 100644 --- a/compression/cutils/quasar/src/lib.rs +++ b/compression/cutils/quasar/src/lib.rs @@ -32,12 +32,12 @@ mod quasar_cutils { /// Mint a compressed NFT to a collection via MintToCollectionV1. #[instruction(discriminator = 0)] pub fn mint(ctx: Ctx) -> Result<(), ProgramError> { - ctx.accounts.mint(&ctx) + instructions::handle_mint(&mut ctx.accounts, &ctx) } /// Verify a compressed NFT leaf exists in the merkle tree. #[instruction(discriminator = 1)] pub fn verify(ctx: CtxWithRemaining) -> Result<(), ProgramError> { - ctx.accounts.verify(&ctx) + instructions::handle_verify(&mut ctx.accounts, &ctx) } } diff --git a/oracles/pyth/quasar/src/lib.rs b/oracles/pyth/quasar/src/lib.rs index 2fd64b23c..a27ec3f28 100644 --- a/oracles/pyth/quasar/src/lib.rs +++ b/oracles/pyth/quasar/src/lib.rs @@ -16,6 +16,6 @@ mod quasar_pyth_example { /// Read and log Pyth price feed data from a PriceUpdateV2 account. #[instruction(discriminator = 0)] pub fn read_price(ctx: Ctx) -> Result<(), ProgramError> { - ctx.accounts.read_price() + instructions::handle_read_price(&mut ctx.accounts) } } diff --git a/tokens/escrow/quasar/src/lib.rs b/tokens/escrow/quasar/src/lib.rs index 9cd7d9c1a..78923aef5 100644 --- a/tokens/escrow/quasar/src/lib.rs +++ b/tokens/escrow/quasar/src/lib.rs @@ -19,18 +19,18 @@ mod quasar_escrow { #[instruction(discriminator = 0)] pub fn make(ctx: Ctx, deposit: u64, receive: u64) -> Result<(), ProgramError> { - ctx.accounts.make_escrow(receive, &ctx.bumps)?; - ctx.accounts.deposit_tokens(deposit) + instructions::make::handle_make_escrow(&mut ctx.accounts, receive, &ctx.bumps)?; + instructions::make::handle_deposit_tokens(&mut ctx.accounts, deposit) } #[instruction(discriminator = 1)] pub fn take(ctx: Ctx) -> Result<(), ProgramError> { - ctx.accounts.transfer_tokens()?; - ctx.accounts.withdraw_tokens_and_close(&ctx.bumps) + instructions::take::handle_transfer_tokens(&mut ctx.accounts)?; + instructions::take::handle_withdraw_tokens_and_close_take(&mut ctx.accounts, &ctx.bumps) } #[instruction(discriminator = 2)] pub fn refund(ctx: Ctx) -> Result<(), ProgramError> { - ctx.accounts.withdraw_tokens_and_close(&ctx.bumps) + instructions::refund::handle_withdraw_tokens_and_close_refund(&mut ctx.accounts, &ctx.bumps) } } diff --git a/tokens/nft-operations/quasar/src/lib.rs b/tokens/nft-operations/quasar/src/lib.rs index e52a98859..95beeed8c 100644 --- a/tokens/nft-operations/quasar/src/lib.rs +++ b/tokens/nft-operations/quasar/src/lib.rs @@ -21,18 +21,18 @@ mod quasar_nft_operations { /// Create a collection NFT: mint, metadata, and master edition. #[instruction(discriminator = 0)] pub fn create_collection(ctx: Ctx) -> Result<(), ProgramError> { - ctx.accounts.create_collection(&ctx.bumps) + instructions::handle_create_collection(&mut ctx.accounts, &ctx.bumps) } /// Mint an individual NFT with a reference to the collection. #[instruction(discriminator = 1)] pub fn mint_nft(ctx: Ctx) -> Result<(), ProgramError> { - ctx.accounts.mint_nft(&ctx.bumps) + instructions::handle_mint_nft(&mut ctx.accounts, &ctx.bumps) } /// Verify the NFT as a member of the collection. #[instruction(discriminator = 2)] pub fn verify_collection(ctx: Ctx) -> Result<(), ProgramError> { - ctx.accounts.verify_collection(&ctx.bumps) + instructions::handle_verify_collection(&mut ctx.accounts, &ctx.bumps) } } diff --git a/tokens/spl-token-minter/quasar/src/lib.rs b/tokens/spl-token-minter/quasar/src/lib.rs index bb4c0c079..88d5c7f15 100644 --- a/tokens/spl-token-minter/quasar/src/lib.rs +++ b/tokens/spl-token-minter/quasar/src/lib.rs @@ -25,11 +25,11 @@ mod quasar_spl_token_minter { token_symbol: String, token_uri: String, ) -> Result<(), ProgramError> { - ctx.accounts.create_token(&token_name, &token_symbol, &token_uri) + instructions::handle_create_token(&mut ctx.accounts, &token_name, &token_symbol, &token_uri) } #[instruction(discriminator = 1)] pub fn mint_token(ctx: Ctx, amount: u64) -> Result<(), ProgramError> { - ctx.accounts.mint_token(amount) + instructions::handle_mint_token(&mut ctx.accounts, amount) } } diff --git a/tokens/token-fundraiser/quasar/src/lib.rs b/tokens/token-fundraiser/quasar/src/lib.rs index 00349cce0..f69a425d9 100644 --- a/tokens/token-fundraiser/quasar/src/lib.rs +++ b/tokens/token-fundraiser/quasar/src/lib.rs @@ -24,24 +24,24 @@ mod quasar_token_fundraiser { amount_to_raise: u64, duration: u16, ) -> Result<(), ProgramError> { - ctx.accounts.initialize(amount_to_raise, duration, ctx.bumps.fundraiser) + instructions::handle_initialize(&mut ctx.accounts, amount_to_raise, duration, ctx.bumps.fundraiser) } /// Contribute tokens to the fundraiser. #[instruction(discriminator = 1)] pub fn contribute(ctx: Ctx, amount: u64) -> Result<(), ProgramError> { - ctx.accounts.contribute(amount) + instructions::handle_contribute(&mut ctx.accounts, amount) } /// Maker withdraws all funds once the target is met. #[instruction(discriminator = 2)] pub fn check_contributions(ctx: Ctx) -> Result<(), ProgramError> { - ctx.accounts.check_contributions(&ctx.bumps) + instructions::handle_check_contributions(&mut ctx.accounts, &ctx.bumps) } /// Contributors reclaim their tokens if the fundraiser fails. #[instruction(discriminator = 3)] pub fn refund(ctx: Ctx) -> Result<(), ProgramError> { - ctx.accounts.refund(&ctx.bumps) + instructions::handle_refund(&mut ctx.accounts, &ctx.bumps) } } diff --git a/tokens/token-swap/quasar/src/lib.rs b/tokens/token-swap/quasar/src/lib.rs index b94d0ed1e..bdf86b832 100644 --- a/tokens/token-swap/quasar/src/lib.rs +++ b/tokens/token-swap/quasar/src/lib.rs @@ -35,12 +35,12 @@ mod quasar_token_swap { id: Address, fee: u16, ) -> Result<(), ProgramError> { - ctx.accounts.create_amm(id, fee) + instructions::handle_create_amm(&mut ctx.accounts, id, fee) } #[instruction(discriminator = 1)] pub fn create_pool(ctx: Ctx) -> Result<(), ProgramError> { - ctx.accounts.create_pool() + instructions::handle_create_pool(&mut ctx.accounts) } #[instruction(discriminator = 2)] @@ -49,7 +49,7 @@ mod quasar_token_swap { amount_a: u64, amount_b: u64, ) -> Result<(), ProgramError> { - ctx.accounts.deposit_liquidity(amount_a, amount_b, &ctx.bumps) + instructions::handle_deposit_liquidity(&mut ctx.accounts, amount_a, amount_b, &ctx.bumps) } #[instruction(discriminator = 3)] @@ -57,7 +57,7 @@ mod quasar_token_swap { ctx: Ctx, amount: u64, ) -> Result<(), ProgramError> { - ctx.accounts.withdraw_liquidity(amount, &ctx.bumps) + instructions::handle_withdraw_liquidity(&mut ctx.accounts, amount, &ctx.bumps) } #[instruction(discriminator = 4)] @@ -67,6 +67,12 @@ mod quasar_token_swap { input_amount: u64, min_output_amount: u64, ) -> Result<(), ProgramError> { - ctx.accounts.swap_exact_tokens_for_tokens(swap_a, input_amount, min_output_amount, &ctx.bumps) + instructions::handle_swap_exact_tokens_for_tokens( + &mut ctx.accounts, + swap_a, + input_amount, + min_output_amount, + &ctx.bumps, + ) } } From 116230fa9dcfce5bbdf59a432dd7af917caee244 Mon Sep 17 00:00:00 2001 From: Jimii Date: Wed, 22 Apr 2026 22:43:03 +0300 Subject: [PATCH 05/12] fix: updated from `#![no_std]` to `#![cfg_attr(not(test), no_std)]` --- .gitignore | 1 - basics/account-data/quasar/src/lib.rs | 2 +- basics/checking-accounts/quasar/src/lib.rs | 2 +- basics/close-account/quasar/src/lib.rs | 2 +- basics/counter/quasar/src/lib.rs | 2 +- basics/create-account/quasar/src/lib.rs | 2 +- basics/favorites/quasar/src/lib.rs | 2 +- basics/hello-solana/quasar/src/lib.rs | 2 +- basics/pda-rent-payer/quasar/src/lib.rs | 2 +- basics/processing-instructions/quasar/src/lib.rs | 2 +- basics/program-derived-addresses/quasar/src/lib.rs | 2 +- basics/realloc/quasar/src/lib.rs | 2 +- basics/rent/quasar/src/lib.rs | 2 +- basics/repository-layout/quasar/src/lib.rs | 2 +- basics/transfer-sol/quasar/src/lib.rs | 2 +- compression/cnft-burn/quasar/src/lib.rs | 2 +- compression/cnft-vault/quasar/src/lib.rs | 2 +- compression/cutils/quasar/src/lib.rs | 2 +- oracles/pyth/quasar/src/lib.rs | 2 +- tokens/create-token/quasar/src/lib.rs | 2 +- tokens/escrow/quasar/src/lib.rs | 2 +- tokens/external-delegate-token-master/quasar/src/lib.rs | 2 +- tokens/nft-minter/quasar/src/lib.rs | 2 +- tokens/nft-operations/quasar/src/lib.rs | 2 +- tokens/pda-mint-authority/quasar/src/lib.rs | 2 +- tokens/spl-token-minter/quasar/src/lib.rs | 2 +- tokens/token-extensions/basics/quasar/src/lib.rs | 2 +- tokens/token-extensions/cpi-guard/quasar/src/lib.rs | 2 +- tokens/token-extensions/default-account-state/quasar/src/lib.rs | 2 +- tokens/token-extensions/group/quasar/src/lib.rs | 2 +- tokens/token-extensions/immutable-owner/quasar/src/lib.rs | 2 +- tokens/token-extensions/interest-bearing/quasar/src/lib.rs | 2 +- tokens/token-extensions/memo-transfer/quasar/src/lib.rs | 2 +- tokens/token-extensions/mint-close-authority/quasar/src/lib.rs | 2 +- tokens/token-extensions/non-transferable/quasar/src/lib.rs | 2 +- tokens/token-extensions/permanent-delegate/quasar/src/lib.rs | 2 +- tokens/token-extensions/transfer-fee/quasar/src/lib.rs | 2 +- .../transfer-hook/account-data-as-seed/quasar/src/lib.rs | 2 +- .../transfer-hook/allow-block-list-token/quasar/src/lib.rs | 2 +- tokens/token-extensions/transfer-hook/counter/quasar/src/lib.rs | 2 +- .../transfer-hook/hello-world/quasar/src/lib.rs | 2 +- .../transfer-hook/transfer-cost/quasar/src/lib.rs | 2 +- .../transfer-hook/transfer-switch/quasar/src/lib.rs | 2 +- .../token-extensions/transfer-hook/whitelist/quasar/src/lib.rs | 2 +- tokens/token-fundraiser/quasar/src/lib.rs | 2 +- tokens/token-swap/quasar/src/lib.rs | 2 +- tokens/transfer-tokens/quasar/src/lib.rs | 2 +- 47 files changed, 46 insertions(+), 47 deletions(-) diff --git a/.gitignore b/.gitignore index af879932b..d137957db 100644 --- a/.gitignore +++ b/.gitignore @@ -22,5 +22,4 @@ node_modules/ /target deploy -.claire .claude diff --git a/basics/account-data/quasar/src/lib.rs b/basics/account-data/quasar/src/lib.rs index c32361401..36253c9ae 100644 --- a/basics/account-data/quasar/src/lib.rs +++ b/basics/account-data/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![no_std] +#![cfg_attr(not(test), no_std)] use quasar_lang::prelude::*; diff --git a/basics/checking-accounts/quasar/src/lib.rs b/basics/checking-accounts/quasar/src/lib.rs index 61df8ebd2..43d523190 100644 --- a/basics/checking-accounts/quasar/src/lib.rs +++ b/basics/checking-accounts/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![no_std] +#![cfg_attr(not(test), no_std)] use quasar_lang::prelude::*; diff --git a/basics/close-account/quasar/src/lib.rs b/basics/close-account/quasar/src/lib.rs index 54aaa0e93..d8838eb01 100644 --- a/basics/close-account/quasar/src/lib.rs +++ b/basics/close-account/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![no_std] +#![cfg_attr(not(test), no_std)] use quasar_lang::prelude::*; diff --git a/basics/counter/quasar/src/lib.rs b/basics/counter/quasar/src/lib.rs index 2655e7db5..b265456ee 100644 --- a/basics/counter/quasar/src/lib.rs +++ b/basics/counter/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![no_std] +#![cfg_attr(not(test), no_std)] use quasar_lang::prelude::*; diff --git a/basics/create-account/quasar/src/lib.rs b/basics/create-account/quasar/src/lib.rs index 60e356a0d..8f71eeba5 100644 --- a/basics/create-account/quasar/src/lib.rs +++ b/basics/create-account/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![no_std] +#![cfg_attr(not(test), no_std)] use quasar_lang::prelude::*; diff --git a/basics/favorites/quasar/src/lib.rs b/basics/favorites/quasar/src/lib.rs index c158b1ba1..d6af2e37d 100644 --- a/basics/favorites/quasar/src/lib.rs +++ b/basics/favorites/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![no_std] +#![cfg_attr(not(test), no_std)] use quasar_lang::prelude::*; diff --git a/basics/hello-solana/quasar/src/lib.rs b/basics/hello-solana/quasar/src/lib.rs index 467d6b323..53fcf9ad8 100644 --- a/basics/hello-solana/quasar/src/lib.rs +++ b/basics/hello-solana/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![no_std] +#![cfg_attr(not(test), no_std)] use quasar_lang::prelude::*; diff --git a/basics/pda-rent-payer/quasar/src/lib.rs b/basics/pda-rent-payer/quasar/src/lib.rs index de3cb39a6..925379a55 100644 --- a/basics/pda-rent-payer/quasar/src/lib.rs +++ b/basics/pda-rent-payer/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![no_std] +#![cfg_attr(not(test), no_std)] use quasar_lang::prelude::*; diff --git a/basics/processing-instructions/quasar/src/lib.rs b/basics/processing-instructions/quasar/src/lib.rs index 8af46fce5..909d1dcc3 100644 --- a/basics/processing-instructions/quasar/src/lib.rs +++ b/basics/processing-instructions/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![no_std] +#![cfg_attr(not(test), no_std)] use quasar_lang::prelude::*; diff --git a/basics/program-derived-addresses/quasar/src/lib.rs b/basics/program-derived-addresses/quasar/src/lib.rs index d9bde8884..1f44e5fb5 100644 --- a/basics/program-derived-addresses/quasar/src/lib.rs +++ b/basics/program-derived-addresses/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![no_std] +#![cfg_attr(not(test), no_std)] use quasar_lang::prelude::*; diff --git a/basics/realloc/quasar/src/lib.rs b/basics/realloc/quasar/src/lib.rs index e5af82ee9..3373f864a 100644 --- a/basics/realloc/quasar/src/lib.rs +++ b/basics/realloc/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![no_std] +#![cfg_attr(not(test), no_std)] use quasar_lang::prelude::*; diff --git a/basics/rent/quasar/src/lib.rs b/basics/rent/quasar/src/lib.rs index 4c2740a1f..854b3945f 100644 --- a/basics/rent/quasar/src/lib.rs +++ b/basics/rent/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![no_std] +#![cfg_attr(not(test), no_std)] use quasar_lang::prelude::*; diff --git a/basics/repository-layout/quasar/src/lib.rs b/basics/repository-layout/quasar/src/lib.rs index d8b47eefe..43ecd62de 100644 --- a/basics/repository-layout/quasar/src/lib.rs +++ b/basics/repository-layout/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![no_std] +#![cfg_attr(not(test), no_std)] use quasar_lang::prelude::*; diff --git a/basics/transfer-sol/quasar/src/lib.rs b/basics/transfer-sol/quasar/src/lib.rs index f0a57bfb2..6942be4d8 100644 --- a/basics/transfer-sol/quasar/src/lib.rs +++ b/basics/transfer-sol/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![no_std] +#![cfg_attr(not(test), no_std)] use quasar_lang::prelude::*; diff --git a/compression/cnft-burn/quasar/src/lib.rs b/compression/cnft-burn/quasar/src/lib.rs index 5c9f9978f..817f392f3 100644 --- a/compression/cnft-burn/quasar/src/lib.rs +++ b/compression/cnft-burn/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![no_std] +#![cfg_attr(not(test), no_std)] use quasar_lang::prelude::*; diff --git a/compression/cnft-vault/quasar/src/lib.rs b/compression/cnft-vault/quasar/src/lib.rs index 95ca7a28a..8b1a4f81f 100644 --- a/compression/cnft-vault/quasar/src/lib.rs +++ b/compression/cnft-vault/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![no_std] +#![cfg_attr(not(test), no_std)] use quasar_lang::prelude::*; diff --git a/compression/cutils/quasar/src/lib.rs b/compression/cutils/quasar/src/lib.rs index eebbf2c02..fdfaf20ba 100644 --- a/compression/cutils/quasar/src/lib.rs +++ b/compression/cutils/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![no_std] +#![cfg_attr(not(test), no_std)] use quasar_lang::prelude::*; diff --git a/oracles/pyth/quasar/src/lib.rs b/oracles/pyth/quasar/src/lib.rs index a27ec3f28..87c23f22c 100644 --- a/oracles/pyth/quasar/src/lib.rs +++ b/oracles/pyth/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![no_std] +#![cfg_attr(not(test), no_std)] use quasar_lang::prelude::*; diff --git a/tokens/create-token/quasar/src/lib.rs b/tokens/create-token/quasar/src/lib.rs index fdb9fe606..b68466633 100644 --- a/tokens/create-token/quasar/src/lib.rs +++ b/tokens/create-token/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![no_std] +#![cfg_attr(not(test), no_std)] use quasar_lang::prelude::*; use quasar_spl::{Mint, Token, TokenCpi}; diff --git a/tokens/escrow/quasar/src/lib.rs b/tokens/escrow/quasar/src/lib.rs index 78923aef5..d779c4770 100644 --- a/tokens/escrow/quasar/src/lib.rs +++ b/tokens/escrow/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![no_std] +#![cfg_attr(not(test), no_std)] use quasar_lang::prelude::*; diff --git a/tokens/external-delegate-token-master/quasar/src/lib.rs b/tokens/external-delegate-token-master/quasar/src/lib.rs index 265c1ef69..d7923128e 100644 --- a/tokens/external-delegate-token-master/quasar/src/lib.rs +++ b/tokens/external-delegate-token-master/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![no_std] +#![cfg_attr(not(test), no_std)] use quasar_lang::prelude::*; use quasar_spl::{Token, TokenCpi}; diff --git a/tokens/nft-minter/quasar/src/lib.rs b/tokens/nft-minter/quasar/src/lib.rs index a629f389a..9127dcab8 100644 --- a/tokens/nft-minter/quasar/src/lib.rs +++ b/tokens/nft-minter/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![no_std] +#![cfg_attr(not(test), no_std)] use quasar_lang::prelude::*; use quasar_spl::{ diff --git a/tokens/nft-operations/quasar/src/lib.rs b/tokens/nft-operations/quasar/src/lib.rs index 95beeed8c..e40c748a8 100644 --- a/tokens/nft-operations/quasar/src/lib.rs +++ b/tokens/nft-operations/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![no_std] +#![cfg_attr(not(test), no_std)] use quasar_lang::prelude::*; diff --git a/tokens/pda-mint-authority/quasar/src/lib.rs b/tokens/pda-mint-authority/quasar/src/lib.rs index e655cd02e..c3c87f011 100644 --- a/tokens/pda-mint-authority/quasar/src/lib.rs +++ b/tokens/pda-mint-authority/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![no_std] +#![cfg_attr(not(test), no_std)] use quasar_lang::prelude::*; use quasar_spl::{Mint, Token, TokenCpi}; diff --git a/tokens/spl-token-minter/quasar/src/lib.rs b/tokens/spl-token-minter/quasar/src/lib.rs index 88d5c7f15..d5701f343 100644 --- a/tokens/spl-token-minter/quasar/src/lib.rs +++ b/tokens/spl-token-minter/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![no_std] +#![cfg_attr(not(test), no_std)] use quasar_lang::prelude::*; diff --git a/tokens/token-extensions/basics/quasar/src/lib.rs b/tokens/token-extensions/basics/quasar/src/lib.rs index 11c329f9a..5ab5ace02 100644 --- a/tokens/token-extensions/basics/quasar/src/lib.rs +++ b/tokens/token-extensions/basics/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![no_std] +#![cfg_attr(not(test), no_std)] use quasar_lang::{ cpi::{CpiCall, InstructionAccount}, diff --git a/tokens/token-extensions/cpi-guard/quasar/src/lib.rs b/tokens/token-extensions/cpi-guard/quasar/src/lib.rs index ff733df35..40f3b75b4 100644 --- a/tokens/token-extensions/cpi-guard/quasar/src/lib.rs +++ b/tokens/token-extensions/cpi-guard/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![no_std] +#![cfg_attr(not(test), no_std)] use quasar_lang::{ cpi::{CpiCall, InstructionAccount}, diff --git a/tokens/token-extensions/default-account-state/quasar/src/lib.rs b/tokens/token-extensions/default-account-state/quasar/src/lib.rs index 7026d788b..28b888d5a 100644 --- a/tokens/token-extensions/default-account-state/quasar/src/lib.rs +++ b/tokens/token-extensions/default-account-state/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![no_std] +#![cfg_attr(not(test), no_std)] use quasar_lang::sysvars::Sysvar; use quasar_lang::{ diff --git a/tokens/token-extensions/group/quasar/src/lib.rs b/tokens/token-extensions/group/quasar/src/lib.rs index 5388e72f1..3f65707ce 100644 --- a/tokens/token-extensions/group/quasar/src/lib.rs +++ b/tokens/token-extensions/group/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![no_std] +#![cfg_attr(not(test), no_std)] use quasar_lang::sysvars::Sysvar; use quasar_lang::{ diff --git a/tokens/token-extensions/immutable-owner/quasar/src/lib.rs b/tokens/token-extensions/immutable-owner/quasar/src/lib.rs index 0a9fe0919..3e995b9d4 100644 --- a/tokens/token-extensions/immutable-owner/quasar/src/lib.rs +++ b/tokens/token-extensions/immutable-owner/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![no_std] +#![cfg_attr(not(test), no_std)] use quasar_lang::sysvars::Sysvar; use quasar_lang::{ diff --git a/tokens/token-extensions/interest-bearing/quasar/src/lib.rs b/tokens/token-extensions/interest-bearing/quasar/src/lib.rs index ec868491f..1f10c7dae 100644 --- a/tokens/token-extensions/interest-bearing/quasar/src/lib.rs +++ b/tokens/token-extensions/interest-bearing/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![no_std] +#![cfg_attr(not(test), no_std)] use quasar_lang::sysvars::Sysvar; use quasar_lang::{ diff --git a/tokens/token-extensions/memo-transfer/quasar/src/lib.rs b/tokens/token-extensions/memo-transfer/quasar/src/lib.rs index 4a8b45464..049ec6adc 100644 --- a/tokens/token-extensions/memo-transfer/quasar/src/lib.rs +++ b/tokens/token-extensions/memo-transfer/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![no_std] +#![cfg_attr(not(test), no_std)] use quasar_lang::sysvars::Sysvar; use quasar_lang::{ diff --git a/tokens/token-extensions/mint-close-authority/quasar/src/lib.rs b/tokens/token-extensions/mint-close-authority/quasar/src/lib.rs index e33a8f47c..9a7986663 100644 --- a/tokens/token-extensions/mint-close-authority/quasar/src/lib.rs +++ b/tokens/token-extensions/mint-close-authority/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![no_std] +#![cfg_attr(not(test), no_std)] use quasar_lang::sysvars::Sysvar; use quasar_lang::{ diff --git a/tokens/token-extensions/non-transferable/quasar/src/lib.rs b/tokens/token-extensions/non-transferable/quasar/src/lib.rs index de5f6e0ee..f4ed62ad6 100644 --- a/tokens/token-extensions/non-transferable/quasar/src/lib.rs +++ b/tokens/token-extensions/non-transferable/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![no_std] +#![cfg_attr(not(test), no_std)] use quasar_lang::sysvars::Sysvar; use quasar_lang::{ diff --git a/tokens/token-extensions/permanent-delegate/quasar/src/lib.rs b/tokens/token-extensions/permanent-delegate/quasar/src/lib.rs index d9e8f44d2..c5085ed59 100644 --- a/tokens/token-extensions/permanent-delegate/quasar/src/lib.rs +++ b/tokens/token-extensions/permanent-delegate/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![no_std] +#![cfg_attr(not(test), no_std)] use quasar_lang::sysvars::Sysvar; use quasar_lang::{ diff --git a/tokens/token-extensions/transfer-fee/quasar/src/lib.rs b/tokens/token-extensions/transfer-fee/quasar/src/lib.rs index b19e4948c..82aa73350 100644 --- a/tokens/token-extensions/transfer-fee/quasar/src/lib.rs +++ b/tokens/token-extensions/transfer-fee/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![no_std] +#![cfg_attr(not(test), no_std)] use quasar_lang::sysvars::Sysvar; use quasar_lang::{ diff --git a/tokens/token-extensions/transfer-hook/account-data-as-seed/quasar/src/lib.rs b/tokens/token-extensions/transfer-hook/account-data-as-seed/quasar/src/lib.rs index 00024482a..b91114dff 100644 --- a/tokens/token-extensions/transfer-hook/account-data-as-seed/quasar/src/lib.rs +++ b/tokens/token-extensions/transfer-hook/account-data-as-seed/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![no_std] +#![cfg_attr(not(test), no_std)] use quasar_lang::sysvars::Sysvar; use quasar_lang::{ diff --git a/tokens/token-extensions/transfer-hook/allow-block-list-token/quasar/src/lib.rs b/tokens/token-extensions/transfer-hook/allow-block-list-token/quasar/src/lib.rs index d3227a07c..74b3df3f1 100644 --- a/tokens/token-extensions/transfer-hook/allow-block-list-token/quasar/src/lib.rs +++ b/tokens/token-extensions/transfer-hook/allow-block-list-token/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![no_std] +#![cfg_attr(not(test), no_std)] use quasar_lang::prelude::*; diff --git a/tokens/token-extensions/transfer-hook/counter/quasar/src/lib.rs b/tokens/token-extensions/transfer-hook/counter/quasar/src/lib.rs index 01656552e..5416dfae8 100644 --- a/tokens/token-extensions/transfer-hook/counter/quasar/src/lib.rs +++ b/tokens/token-extensions/transfer-hook/counter/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![no_std] +#![cfg_attr(not(test), no_std)] use quasar_lang::sysvars::Sysvar; use quasar_lang::{ diff --git a/tokens/token-extensions/transfer-hook/hello-world/quasar/src/lib.rs b/tokens/token-extensions/transfer-hook/hello-world/quasar/src/lib.rs index d7c3c73a8..2f331c132 100644 --- a/tokens/token-extensions/transfer-hook/hello-world/quasar/src/lib.rs +++ b/tokens/token-extensions/transfer-hook/hello-world/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![no_std] +#![cfg_attr(not(test), no_std)] use quasar_lang::sysvars::Sysvar; use quasar_lang::{ diff --git a/tokens/token-extensions/transfer-hook/transfer-cost/quasar/src/lib.rs b/tokens/token-extensions/transfer-hook/transfer-cost/quasar/src/lib.rs index 225ff62a6..0529a2a23 100644 --- a/tokens/token-extensions/transfer-hook/transfer-cost/quasar/src/lib.rs +++ b/tokens/token-extensions/transfer-hook/transfer-cost/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![no_std] +#![cfg_attr(not(test), no_std)] use quasar_lang::sysvars::Sysvar; use quasar_lang::{ diff --git a/tokens/token-extensions/transfer-hook/transfer-switch/quasar/src/lib.rs b/tokens/token-extensions/transfer-hook/transfer-switch/quasar/src/lib.rs index 87e7b33fe..30a1a9db7 100644 --- a/tokens/token-extensions/transfer-hook/transfer-switch/quasar/src/lib.rs +++ b/tokens/token-extensions/transfer-hook/transfer-switch/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![no_std] +#![cfg_attr(not(test), no_std)] use quasar_lang::sysvars::Sysvar; use quasar_lang::{ diff --git a/tokens/token-extensions/transfer-hook/whitelist/quasar/src/lib.rs b/tokens/token-extensions/transfer-hook/whitelist/quasar/src/lib.rs index 1d696efb9..f58370958 100644 --- a/tokens/token-extensions/transfer-hook/whitelist/quasar/src/lib.rs +++ b/tokens/token-extensions/transfer-hook/whitelist/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![no_std] +#![cfg_attr(not(test), no_std)] use quasar_lang::sysvars::Sysvar; use quasar_lang::{ diff --git a/tokens/token-fundraiser/quasar/src/lib.rs b/tokens/token-fundraiser/quasar/src/lib.rs index f69a425d9..cb00e6c1d 100644 --- a/tokens/token-fundraiser/quasar/src/lib.rs +++ b/tokens/token-fundraiser/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![no_std] +#![cfg_attr(not(test), no_std)] use quasar_lang::prelude::*; diff --git a/tokens/token-swap/quasar/src/lib.rs b/tokens/token-swap/quasar/src/lib.rs index bdf86b832..21ecd409c 100644 --- a/tokens/token-swap/quasar/src/lib.rs +++ b/tokens/token-swap/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![no_std] +#![cfg_attr(not(test), no_std)] use quasar_lang::prelude::*; diff --git a/tokens/transfer-tokens/quasar/src/lib.rs b/tokens/transfer-tokens/quasar/src/lib.rs index 3bc0e075a..3f148405d 100644 --- a/tokens/transfer-tokens/quasar/src/lib.rs +++ b/tokens/transfer-tokens/quasar/src/lib.rs @@ -1,4 +1,4 @@ -#![no_std] +#![cfg_attr(not(test), no_std)] use quasar_lang::prelude::*; use quasar_spl::{Mint, Token, TokenCpi}; From a65c134a6f0aa3b4973dd6f943db2a67ef98a613 Mon Sep 17 00:00:00 2001 From: Jimii Date: Wed, 22 Apr 2026 22:50:58 +0300 Subject: [PATCH 06/12] fix: updated from `#![no_std]` to `#![cfg_attr(not(test), no_std)]` --- basics/cross-program-invocation/quasar/hand/src/lib.rs | 2 +- basics/cross-program-invocation/quasar/lever/src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/basics/cross-program-invocation/quasar/hand/src/lib.rs b/basics/cross-program-invocation/quasar/hand/src/lib.rs index 6eda4c7c3..e272cf83c 100644 --- a/basics/cross-program-invocation/quasar/hand/src/lib.rs +++ b/basics/cross-program-invocation/quasar/hand/src/lib.rs @@ -1,4 +1,4 @@ -#![no_std] +#![cfg_attr(not(test), no_std)] use quasar_lang::prelude::*; diff --git a/basics/cross-program-invocation/quasar/lever/src/lib.rs b/basics/cross-program-invocation/quasar/lever/src/lib.rs index f5691fafc..00aa9464e 100644 --- a/basics/cross-program-invocation/quasar/lever/src/lib.rs +++ b/basics/cross-program-invocation/quasar/lever/src/lib.rs @@ -1,4 +1,4 @@ -#![no_std] +#![cfg_attr(not(test), no_std)] use quasar_lang::prelude::*; From be261cfa34edff9143d2f9269cfade061522145e Mon Sep 17 00:00:00 2001 From: Jimii <30603522+jim4067@users.noreply.github.com> Date: Fri, 24 Apr 2026 21:03:07 +0300 Subject: [PATCH 07/12] [WIP]: FIX CI --- basics/account-data/quasar/Cargo.toml | 4 ++-- basics/account-data/quasar/Quasar.toml | 16 +-------------- .../quasar/src/instructions/create.rs | 17 +++++++--------- basics/account-data/quasar/src/lib.rs | 6 +++--- basics/account-data/quasar/src/state.rs | 13 +++++------- basics/checking-accounts/quasar/Cargo.toml | 4 ++-- basics/checking-accounts/quasar/Quasar.toml | 16 +-------------- basics/close-account/quasar/Cargo.toml | 4 ++-- basics/close-account/quasar/Quasar.toml | 16 +-------------- .../quasar/src/instructions/close_user.rs | 7 ++----- .../quasar/src/instructions/create_user.rs | 20 +++++++++++-------- basics/close-account/quasar/src/lib.rs | 2 +- basics/close-account/quasar/src/state.rs | 4 ++-- basics/counter/quasar/Cargo.toml | 4 ++-- basics/counter/quasar/Quasar.toml | 16 +-------------- .../quasar/src/instructions/increment.rs | 5 +---- .../src/instructions/initialize_counter.rs | 8 +++----- basics/counter/quasar/src/state.rs | 2 +- basics/create-account/quasar/Cargo.toml | 4 ++-- basics/create-account/quasar/Quasar.toml | 16 +-------------- .../src/instructions/create_system_account.rs | 7 +++++-- .../quasar/hand/Cargo.toml | 4 ++-- .../quasar/hand/Quasar.toml | 15 +------------- .../quasar/lever/Cargo.toml | 4 ++-- .../quasar/lever/Quasar.toml | 15 +------------- basics/favorites/quasar/Cargo.toml | 4 ++-- basics/favorites/quasar/Quasar.toml | 16 +-------------- basics/favorites/quasar/src/state.rs | 2 +- basics/hello-solana/quasar/Cargo.toml | 4 ++-- basics/hello-solana/quasar/Quasar.toml | 16 +-------------- basics/pda-rent-payer/quasar/Cargo.toml | 4 ++-- basics/pda-rent-payer/quasar/Quasar.toml | 16 +-------------- .../processing-instructions/quasar/Cargo.toml | 4 ++-- .../quasar/Quasar.toml | 16 +-------------- .../quasar/Cargo.toml | 4 ++-- .../quasar/Quasar.toml | 16 +-------------- basics/realloc/quasar/Cargo.toml | 4 ++-- basics/realloc/quasar/Quasar.toml | 16 +-------------- basics/rent/quasar/Cargo.toml | 4 ++-- basics/rent/quasar/Quasar.toml | 16 +-------------- basics/repository-layout/quasar/Cargo.toml | 4 ++-- basics/repository-layout/quasar/Quasar.toml | 16 +-------------- basics/transfer-sol/quasar/Cargo.toml | 4 ++-- basics/transfer-sol/quasar/Quasar.toml | 16 +-------------- compression/cnft-burn/quasar/Cargo.toml | 4 ++-- compression/cnft-burn/quasar/Quasar.toml | 16 +-------------- compression/cnft-vault/quasar/Cargo.toml | 4 ++-- compression/cnft-vault/quasar/Quasar.toml | 16 +-------------- compression/cutils/quasar/Cargo.toml | 4 ++-- compression/cutils/quasar/Quasar.toml | 16 +-------------- oracles/pyth/quasar/Cargo.toml | 4 ++-- oracles/pyth/quasar/Quasar.toml | 16 +-------------- tokens/create-token/quasar/Cargo.toml | 6 +++--- tokens/create-token/quasar/Quasar.toml | 16 +-------------- tokens/escrow/quasar/Cargo.toml | 6 +++--- tokens/escrow/quasar/Quasar.toml | 16 +-------------- .../quasar/Cargo.toml | 6 +++--- tokens/nft-minter/quasar/Cargo.toml | 4 ++-- tokens/nft-operations/quasar/Cargo.toml | 4 ++-- tokens/pda-mint-authority/quasar/Cargo.toml | 6 +++--- tokens/pda-mint-authority/quasar/Quasar.toml | 16 +-------------- tokens/spl-token-minter/quasar/Cargo.toml | 4 ++-- tokens/token-fundraiser/quasar/Cargo.toml | 6 +++--- tokens/token-fundraiser/quasar/Quasar.toml | 16 +-------------- tokens/token-swap/quasar/Cargo.toml | 6 +++--- tokens/transfer-tokens/quasar/Cargo.toml | 6 +++--- tokens/transfer-tokens/quasar/Quasar.toml | 16 +-------------- 67 files changed, 135 insertions(+), 490 deletions(-) diff --git a/basics/account-data/quasar/Cargo.toml b/basics/account-data/quasar/Cargo.toml index 9027e9784..993ca2b42 100644 --- a/basics/account-data/quasar/Cargo.toml +++ b/basics/account-data/quasar/Cargo.toml @@ -22,13 +22,13 @@ client = [] debug = [] [dependencies] -quasar-lang = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } +quasar-lang = { git = "https://github.com/blueshift-gg/quasar" } solana-instruction = { version = "3.2.0" } [dev-dependencies] # Not using the generated client: it depends on quasar_lang::client::DynBytes # which isn't in the published crate yet. Tests build instruction data manually. -quasar-svm = { version = "0.1" } +quasar-svm = { git = "https://github.com/blueshift-gg/quasar-svm" } solana-account = { version = "3.4.0" } solana-address = { version = "2.2.0", features = ["decode"] } solana-instruction = { version = "3.2.0", features = ["bincode"] } diff --git a/basics/account-data/quasar/Quasar.toml b/basics/account-data/quasar/Quasar.toml index 7e8c7764c..cf360c065 100644 --- a/basics/account-data/quasar/Quasar.toml +++ b/basics/account-data/quasar/Quasar.toml @@ -5,18 +5,4 @@ name = "quasar_account_data" type = "solana" [testing] -language = "rust" - -[testing.rust] -framework = "quasar-svm" - -[testing.rust.test] -program = "cargo" -args = [ - "test", - "tests::", -] - -[clients] -path = "target/client" -languages = ["rust"] +framework = "quasarsvm-rust" diff --git a/basics/account-data/quasar/src/instructions/create.rs b/basics/account-data/quasar/src/instructions/create.rs index 51fe12083..1a3503e00 100644 --- a/basics/account-data/quasar/src/instructions/create.rs +++ b/basics/account-data/quasar/src/instructions/create.rs @@ -1,17 +1,15 @@ use { - crate::state::AddressInfo, - quasar_lang::prelude::*, + crate::state::{AddressInfo, AddressInfoInner}, + quasar_lang::{prelude::*, sysvars::Sysvar}, }; /// Accounts for creating a new address info account. -/// Dynamic accounts use owned `Account` rather than `&'info mut Account` because -/// dynamic types carry cached byte offsets that cannot be represented as a pointer cast. #[derive(Accounts)] pub struct CreateAddressInfo { #[account(mut)] pub payer: Signer, #[account(mut, init, payer = payer, seeds = AddressInfo::seeds(payer), bump)] - pub address_info: Account>, + pub address_info: Account, pub system_program: Program, } @@ -23,12 +21,11 @@ pub fn handle_create_address_info( street: &str, city: &str, ) -> Result<(), ProgramError> { + let rent = Rent::get()?; accounts.address_info.set_inner( - house_number, - name, - street, - city, + AddressInfoInner { house_number, name, street, city }, accounts.payer.to_account_view(), - None, + rent.lamports_per_byte(), + rent.exemption_threshold_raw(), ) } diff --git a/basics/account-data/quasar/src/lib.rs b/basics/account-data/quasar/src/lib.rs index 36253c9ae..cc6bb40c4 100644 --- a/basics/account-data/quasar/src/lib.rs +++ b/basics/account-data/quasar/src/lib.rs @@ -25,10 +25,10 @@ mod quasar_account_data { #[instruction(discriminator = 0)] pub fn create_address_info( ctx: Ctx, - name: String, house_number: u8, - street: String, - city: String, + name: String<50>, + street: String<50>, + city: String<50>, ) -> Result<(), ProgramError> { instructions::handle_create_address_info(&mut ctx.accounts, name, house_number, street, city) } diff --git a/basics/account-data/quasar/src/state.rs b/basics/account-data/quasar/src/state.rs index 3c7af75b4..e54e874cd 100644 --- a/basics/account-data/quasar/src/state.rs +++ b/basics/account-data/quasar/src/state.rs @@ -1,15 +1,12 @@ use quasar_lang::prelude::*; /// Onchain address info account with dynamic string fields. -/// Uses Quasar's `String` marker type for variable-length string data. -/// The lifetime `'a` is required because the generated code produces `&'a str` accessors. -/// /// Note: Quasar requires all fixed-size fields to precede dynamic (String/Vec) fields. -#[account(discriminator = 1)] +#[account(discriminator = 1, set_inner)] #[seeds(b"address_info", payer: Address)] -pub struct AddressInfo<'a> { +pub struct AddressInfo { pub house_number: u8, - pub name: String, - pub street: String, - pub city: String, + pub name: String<50>, + pub street: String<50>, + pub city: String<50>, } diff --git a/basics/checking-accounts/quasar/Cargo.toml b/basics/checking-accounts/quasar/Cargo.toml index af16e98ee..eec54aa7d 100644 --- a/basics/checking-accounts/quasar/Cargo.toml +++ b/basics/checking-accounts/quasar/Cargo.toml @@ -22,12 +22,12 @@ client = [] debug = [] [dependencies] -quasar-lang = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } +quasar-lang = { git = "https://github.com/blueshift-gg/quasar" } solana-instruction = { version = "3.2.0" } [dev-dependencies] quasar-checking-accounts-client = { path = "target/client/rust/quasar-checking-accounts-client" } -quasar-svm = { version = "0.1" } +quasar-svm = { git = "https://github.com/blueshift-gg/quasar-svm" } solana-account = { version = "3.4.0" } solana-address = { version = "2.2.0", features = ["decode"] } solana-instruction = { version = "3.2.0", features = ["bincode"] } diff --git a/basics/checking-accounts/quasar/Quasar.toml b/basics/checking-accounts/quasar/Quasar.toml index b352a401a..3c05d24e7 100644 --- a/basics/checking-accounts/quasar/Quasar.toml +++ b/basics/checking-accounts/quasar/Quasar.toml @@ -5,18 +5,4 @@ name = "quasar_checking_accounts" type = "solana" [testing] -language = "rust" - -[testing.rust] -framework = "quasar-svm" - -[testing.rust.test] -program = "cargo" -args = [ - "test", - "tests::", -] - -[clients] -path = "target/client" -languages = ["rust"] +framework = "quasarsvm-rust" diff --git a/basics/close-account/quasar/Cargo.toml b/basics/close-account/quasar/Cargo.toml index 18b91004a..9da62371f 100644 --- a/basics/close-account/quasar/Cargo.toml +++ b/basics/close-account/quasar/Cargo.toml @@ -20,11 +20,11 @@ client = [] debug = [] [dependencies] -quasar-lang = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } +quasar-lang = { git = "https://github.com/blueshift-gg/quasar" } solana-instruction = { version = "3.2.0" } [dev-dependencies] -quasar-svm = { version = "0.1" } +quasar-svm = { git = "https://github.com/blueshift-gg/quasar-svm" } solana-account = { version = "3.4.0" } solana-address = { version = "2.2.0", features = ["decode"] } solana-instruction = { version = "3.2.0", features = ["bincode"] } diff --git a/basics/close-account/quasar/Quasar.toml b/basics/close-account/quasar/Quasar.toml index 86103ea57..0223f4c9c 100644 --- a/basics/close-account/quasar/Quasar.toml +++ b/basics/close-account/quasar/Quasar.toml @@ -5,18 +5,4 @@ name = "quasar_close_account" type = "solana" [testing] -language = "rust" - -[testing.rust] -framework = "quasar-svm" - -[testing.rust.test] -program = "cargo" -args = [ - "test", - "tests::", -] - -[clients] -path = "target/client" -languages = ["rust"] +framework = "quasarsvm-rust" diff --git a/basics/close-account/quasar/src/instructions/close_user.rs b/basics/close-account/quasar/src/instructions/close_user.rs index dcebbb7b5..ff56535f3 100644 --- a/basics/close-account/quasar/src/instructions/close_user.rs +++ b/basics/close-account/quasar/src/instructions/close_user.rs @@ -1,7 +1,4 @@ -use { - crate::state::UserState, - quasar_lang::prelude::*, -}; +use {crate::state::UserState, quasar_lang::prelude::*}; /// Accounts for closing a user account. /// The `close = user` attribute in the Anchor version triggers an automatic epilogue. @@ -12,7 +9,7 @@ pub struct CloseUser { #[account(mut)] pub user: Signer, #[account(mut)] - pub user_account: Account>, + pub user_account: Account, } #[inline(always)] diff --git a/basics/close-account/quasar/src/instructions/create_user.rs b/basics/close-account/quasar/src/instructions/create_user.rs index 26194325b..be2da0b88 100644 --- a/basics/close-account/quasar/src/instructions/create_user.rs +++ b/basics/close-account/quasar/src/instructions/create_user.rs @@ -1,6 +1,6 @@ use { - crate::state::UserState, - quasar_lang::prelude::*, + crate::state::{UserState, UserStateInner}, + quasar_lang::{prelude::*, sysvars::Sysvar}, }; /// Accounts for creating a new user. @@ -9,18 +9,22 @@ pub struct CreateUser { #[account(mut)] pub user: Signer, #[account(mut, init, payer = user, seeds = UserState::seeds(user), bump)] - pub user_account: Account>, + pub user_account: Account, pub system_program: Program, } #[inline(always)] -pub fn handle_create_user(accounts: &mut CreateUser, name: &str, bump: u8) -> Result<(), ProgramError> { +pub fn handle_create_user( + accounts: &mut CreateUser, + name: &str, + bump: u8, +) -> Result<(), ProgramError> { let user_address = *accounts.user.to_account_view().address(); + let rent = Rent::get()?; accounts.user_account.set_inner( - bump, - user_address, - name, + UserStateInner { bump, user: user_address, name }, accounts.user.to_account_view(), - None, + rent.lamports_per_byte(), + rent.exemption_threshold_raw(), ) } diff --git a/basics/close-account/quasar/src/lib.rs b/basics/close-account/quasar/src/lib.rs index d8838eb01..64af1b7f3 100644 --- a/basics/close-account/quasar/src/lib.rs +++ b/basics/close-account/quasar/src/lib.rs @@ -16,7 +16,7 @@ mod quasar_close_account { /// Create a user account with a name. #[instruction(discriminator = 0)] - pub fn create_user(ctx: Ctx, name: String) -> Result<(), ProgramError> { + pub fn create_user(ctx: Ctx, name: String<50>) -> Result<(), ProgramError> { let bump = ctx.bumps.user_account; instructions::handle_create_user(&mut ctx.accounts, name, bump) } diff --git a/basics/close-account/quasar/src/state.rs b/basics/close-account/quasar/src/state.rs index 26c4d7b01..6e3c71590 100644 --- a/basics/close-account/quasar/src/state.rs +++ b/basics/close-account/quasar/src/state.rs @@ -4,8 +4,8 @@ use quasar_lang::prelude::*; /// Fixed fields (bump, user) must precede dynamic fields (name). #[account(discriminator = 1, set_inner)] #[seeds(b"USER", user: Address)] -pub struct UserState<'a> { +pub struct UserState { pub bump: u8, pub user: Address, - pub name: String, + pub name: String<50>, } diff --git a/basics/counter/quasar/Cargo.toml b/basics/counter/quasar/Cargo.toml index 3e61142e1..2ac1aa296 100644 --- a/basics/counter/quasar/Cargo.toml +++ b/basics/counter/quasar/Cargo.toml @@ -22,12 +22,12 @@ client = [] debug = [] [dependencies] -quasar-lang = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } +quasar-lang = { git = "https://github.com/blueshift-gg/quasar" } solana-instruction = { version = "3.2.0" } [dev-dependencies] quasar-counter-client = { path = "target/client/rust/quasar-counter-client" } -quasar-svm = { version = "0.1" } +quasar-svm = { git = "https://github.com/blueshift-gg/quasar-svm" } solana-account = { version = "3.4.0" } solana-address = { version = "2.2.0", features = ["decode"] } solana-instruction = { version = "3.2.0", features = ["bincode"] } diff --git a/basics/counter/quasar/Quasar.toml b/basics/counter/quasar/Quasar.toml index ae2854a56..53191deec 100644 --- a/basics/counter/quasar/Quasar.toml +++ b/basics/counter/quasar/Quasar.toml @@ -5,18 +5,4 @@ name = "quasar_counter" type = "solana" [testing] -language = "rust" - -[testing.rust] -framework = "quasar-svm" - -[testing.rust.test] -program = "cargo" -args = [ - "test", - "tests::", -] - -[clients] -path = "target/client" -languages = ["rust"] +framework = "quasarsvm-rust" diff --git a/basics/counter/quasar/src/instructions/increment.rs b/basics/counter/quasar/src/instructions/increment.rs index 90b73ae7f..0a6fc9b95 100644 --- a/basics/counter/quasar/src/instructions/increment.rs +++ b/basics/counter/quasar/src/instructions/increment.rs @@ -1,7 +1,4 @@ -use { - crate::state::Counter, - quasar_lang::prelude::*, -}; +use {crate::state::Counter, quasar_lang::prelude::*}; /// Accounts for incrementing a counter. #[derive(Accounts)] diff --git a/basics/counter/quasar/src/instructions/initialize_counter.rs b/basics/counter/quasar/src/instructions/initialize_counter.rs index 4d81ea689..c7335c0cf 100644 --- a/basics/counter/quasar/src/instructions/initialize_counter.rs +++ b/basics/counter/quasar/src/instructions/initialize_counter.rs @@ -1,7 +1,5 @@ -use { - crate::state::Counter, - quasar_lang::prelude::*, -}; +use crate::state::{Counter, CounterInner}; +use quasar_lang::prelude::*; /// Accounts for creating a new counter. /// The counter is derived as a PDA from ["counter", payer] seeds. @@ -16,6 +14,6 @@ pub struct InitializeCounter { #[inline(always)] pub fn handle_initialize_counter(accounts: &mut InitializeCounter) -> Result<(), ProgramError> { - accounts.counter.set_inner(0u64); + accounts.counter.set_inner(CounterInner { count: 0 }); Ok(()) } diff --git a/basics/counter/quasar/src/state.rs b/basics/counter/quasar/src/state.rs index d49abab1d..988faa3e6 100644 --- a/basics/counter/quasar/src/state.rs +++ b/basics/counter/quasar/src/state.rs @@ -1,7 +1,7 @@ use quasar_lang::prelude::*; /// Onchain counter account. -#[account(discriminator = 1)] +#[account(discriminator = 1, set_inner)] #[seeds(b"counter", payer: Address)] pub struct Counter { pub count: u64, diff --git a/basics/create-account/quasar/Cargo.toml b/basics/create-account/quasar/Cargo.toml index f980fe3d1..2c3e2cae2 100644 --- a/basics/create-account/quasar/Cargo.toml +++ b/basics/create-account/quasar/Cargo.toml @@ -22,12 +22,12 @@ client = [] debug = [] [dependencies] -quasar-lang = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } +quasar-lang = { git = "https://github.com/blueshift-gg/quasar" } solana-instruction = { version = "3.2.0" } [dev-dependencies] quasar-create-account-client = { path = "target/client/rust/quasar-create-account-client" } -quasar-svm = { version = "0.1" } +quasar-svm = { git = "https://github.com/blueshift-gg/quasar-svm" } solana-account = { version = "3.4.0" } solana-address = { version = "2.2.0", features = ["decode"] } solana-instruction = { version = "3.2.0", features = ["bincode"] } diff --git a/basics/create-account/quasar/Quasar.toml b/basics/create-account/quasar/Quasar.toml index e42289144..4df3a1f5f 100644 --- a/basics/create-account/quasar/Quasar.toml +++ b/basics/create-account/quasar/Quasar.toml @@ -5,18 +5,4 @@ name = "quasar_create_account" type = "solana" [testing] -language = "rust" - -[testing.rust] -framework = "quasar-svm" - -[testing.rust.test] -program = "cargo" -args = [ - "test", - "tests::", -] - -[clients] -path = "target/client" -languages = ["rust"] +framework = "quasarsvm-rust" diff --git a/basics/create-account/quasar/src/instructions/create_system_account.rs b/basics/create-account/quasar/src/instructions/create_system_account.rs index 88a03be63..fe00070a1 100644 --- a/basics/create-account/quasar/src/instructions/create_system_account.rs +++ b/basics/create-account/quasar/src/instructions/create_system_account.rs @@ -12,11 +12,14 @@ pub struct CreateSystemAccount { } #[inline(always)] -pub fn handle_create_system_account(accounts: &mut CreateSystemAccount) -> Result<(), ProgramError> { +pub fn handle_create_system_account( + accounts: &mut CreateSystemAccount, +) -> Result<(), ProgramError> { // Create a zero-data account owned by the system program, // funded with the minimum rent-exempt balance. let system_program_address = Address::default(); - accounts.system_program + accounts + .system_program .create_account_with_minimum_balance( &accounts.payer, &accounts.new_account, diff --git a/basics/cross-program-invocation/quasar/hand/Cargo.toml b/basics/cross-program-invocation/quasar/hand/Cargo.toml index acc1d0002..f86dd8f0c 100644 --- a/basics/cross-program-invocation/quasar/hand/Cargo.toml +++ b/basics/cross-program-invocation/quasar/hand/Cargo.toml @@ -21,11 +21,11 @@ client = [] debug = [] [dependencies] -quasar-lang = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } +quasar-lang = { git = "https://github.com/blueshift-gg/quasar" } solana-instruction = { version = "3.2.0" } [dev-dependencies] -quasar-svm = { version = "0.1" } +quasar-svm = { git = "https://github.com/blueshift-gg/quasar-svm" } solana-account = { version = "3.4.0" } solana-address = { version = "2.2.0", features = ["decode"] } solana-instruction = { version = "3.2.0", features = ["bincode"] } diff --git a/basics/cross-program-invocation/quasar/hand/Quasar.toml b/basics/cross-program-invocation/quasar/hand/Quasar.toml index 3a8dba276..885b64faf 100644 --- a/basics/cross-program-invocation/quasar/hand/Quasar.toml +++ b/basics/cross-program-invocation/quasar/hand/Quasar.toml @@ -5,17 +5,4 @@ name = "quasar_hand" type = "solana" [testing] -language = "rust" - -[testing.rust] -framework = "quasar-svm" - -[testing.rust.test] -program = "cargo" -args = [ - "test", - "tests::", -] - -[clients] -languages = ["rust"] +framework = "quasarsvm-rust" diff --git a/basics/cross-program-invocation/quasar/lever/Cargo.toml b/basics/cross-program-invocation/quasar/lever/Cargo.toml index 28ad2933f..fceab3f06 100644 --- a/basics/cross-program-invocation/quasar/lever/Cargo.toml +++ b/basics/cross-program-invocation/quasar/lever/Cargo.toml @@ -21,11 +21,11 @@ client = [] debug = [] [dependencies] -quasar-lang = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } +quasar-lang = { git = "https://github.com/blueshift-gg/quasar" } solana-instruction = { version = "3.2.0" } [dev-dependencies] -quasar-svm = { version = "0.1" } +quasar-svm = { git = "https://github.com/blueshift-gg/quasar-svm" } solana-account = { version = "3.4.0" } solana-address = { version = "2.2.0", features = ["decode"] } solana-instruction = { version = "3.2.0", features = ["bincode"] } diff --git a/basics/cross-program-invocation/quasar/lever/Quasar.toml b/basics/cross-program-invocation/quasar/lever/Quasar.toml index b994e3950..794df3e59 100644 --- a/basics/cross-program-invocation/quasar/lever/Quasar.toml +++ b/basics/cross-program-invocation/quasar/lever/Quasar.toml @@ -5,17 +5,4 @@ name = "quasar_lever" type = "solana" [testing] -language = "rust" - -[testing.rust] -framework = "quasar-svm" - -[testing.rust.test] -program = "cargo" -args = [ - "test", - "tests::", -] - -[clients] -languages = ["rust"] +framework = "quasarsvm-rust" diff --git a/basics/favorites/quasar/Cargo.toml b/basics/favorites/quasar/Cargo.toml index cf87bca66..3fbf2f1b4 100644 --- a/basics/favorites/quasar/Cargo.toml +++ b/basics/favorites/quasar/Cargo.toml @@ -20,11 +20,11 @@ client = [] debug = [] [dependencies] -quasar-lang = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } +quasar-lang = { git = "https://github.com/blueshift-gg/quasar" } solana-instruction = { version = "3.2.0" } [dev-dependencies] -quasar-svm = { version = "0.1" } +quasar-svm = { git = "https://github.com/blueshift-gg/quasar-svm" } solana-account = { version = "3.4.0" } solana-address = { version = "2.2.0", features = ["decode"] } solana-instruction = { version = "3.2.0", features = ["bincode"] } diff --git a/basics/favorites/quasar/Quasar.toml b/basics/favorites/quasar/Quasar.toml index 93604701a..f0b7c8246 100644 --- a/basics/favorites/quasar/Quasar.toml +++ b/basics/favorites/quasar/Quasar.toml @@ -5,18 +5,4 @@ name = "quasar_favorites" type = "solana" [testing] -language = "rust" - -[testing.rust] -framework = "quasar-svm" - -[testing.rust.test] -program = "cargo" -args = [ - "test", - "tests::", -] - -[clients] -path = "target/client" -languages = ["rust"] +framework = "quasarsvm-rust" diff --git a/basics/favorites/quasar/src/state.rs b/basics/favorites/quasar/src/state.rs index a58199273..13673130b 100644 --- a/basics/favorites/quasar/src/state.rs +++ b/basics/favorites/quasar/src/state.rs @@ -9,5 +9,5 @@ use quasar_lang::prelude::*; #[seeds(b"favorites", user: Address)] pub struct Favorites<'a> { pub number: u64, - pub color: String, + pub color: String<50>, } diff --git a/basics/hello-solana/quasar/Cargo.toml b/basics/hello-solana/quasar/Cargo.toml index 16c22f6ae..fc1e68a99 100644 --- a/basics/hello-solana/quasar/Cargo.toml +++ b/basics/hello-solana/quasar/Cargo.toml @@ -22,12 +22,12 @@ client = [] debug = [] [dependencies] -quasar-lang = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } +quasar-lang = { git = "https://github.com/blueshift-gg/quasar" } solana-instruction = { version = "3.2.0" } [dev-dependencies] quasar-hello-solana-client = { path = "target/client/rust/quasar-hello-solana-client" } -quasar-svm = { version = "0.1" } +quasar-svm = { git = "https://github.com/blueshift-gg/quasar-svm" } solana-account = { version = "3.4.0" } solana-address = { version = "2.2.0", features = ["decode"] } solana-instruction = { version = "3.2.0", features = ["bincode"] } diff --git a/basics/hello-solana/quasar/Quasar.toml b/basics/hello-solana/quasar/Quasar.toml index 381e0c017..6ccea9dcb 100644 --- a/basics/hello-solana/quasar/Quasar.toml +++ b/basics/hello-solana/quasar/Quasar.toml @@ -5,18 +5,4 @@ name = "quasar_hello_solana" type = "solana" [testing] -language = "rust" - -[testing.rust] -framework = "quasar-svm" - -[testing.rust.test] -program = "cargo" -args = [ - "test", - "tests::", -] - -[clients] -path = "target/client" -languages = ["rust"] +framework = "quasarsvm-rust" diff --git a/basics/pda-rent-payer/quasar/Cargo.toml b/basics/pda-rent-payer/quasar/Cargo.toml index a848eacd4..a0af6710b 100644 --- a/basics/pda-rent-payer/quasar/Cargo.toml +++ b/basics/pda-rent-payer/quasar/Cargo.toml @@ -22,12 +22,12 @@ client = [] debug = [] [dependencies] -quasar-lang = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } +quasar-lang = { git = "https://github.com/blueshift-gg/quasar" } solana-instruction = { version = "3.2.0" } [dev-dependencies] quasar-pda-rent-payer-client = { path = "target/client/rust/quasar-pda-rent-payer-client" } -quasar-svm = { version = "0.1" } +quasar-svm = { git = "https://github.com/blueshift-gg/quasar-svm" } solana-account = { version = "3.4.0" } solana-address = { version = "2.2.0", features = ["decode"] } solana-instruction = { version = "3.2.0", features = ["bincode"] } diff --git a/basics/pda-rent-payer/quasar/Quasar.toml b/basics/pda-rent-payer/quasar/Quasar.toml index 2afdab05b..c35b299c8 100644 --- a/basics/pda-rent-payer/quasar/Quasar.toml +++ b/basics/pda-rent-payer/quasar/Quasar.toml @@ -5,18 +5,4 @@ name = "quasar_pda_rent_payer" type = "solana" [testing] -language = "rust" - -[testing.rust] -framework = "quasar-svm" - -[testing.rust.test] -program = "cargo" -args = [ - "test", - "tests::", -] - -[clients] -path = "target/client" -languages = ["rust"] +framework = "quasarsvm-rust" diff --git a/basics/processing-instructions/quasar/Cargo.toml b/basics/processing-instructions/quasar/Cargo.toml index 480403bb4..26cf2e808 100644 --- a/basics/processing-instructions/quasar/Cargo.toml +++ b/basics/processing-instructions/quasar/Cargo.toml @@ -20,11 +20,11 @@ client = [] debug = [] [dependencies] -quasar-lang = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } +quasar-lang = { git = "https://github.com/blueshift-gg/quasar" } solana-instruction = { version = "3.2.0" } [dev-dependencies] -quasar-svm = { version = "0.1" } +quasar-svm = { git = "https://github.com/blueshift-gg/quasar-svm" } solana-account = { version = "3.4.0" } solana-address = { version = "2.2.0", features = ["decode"] } solana-instruction = { version = "3.2.0", features = ["bincode"] } diff --git a/basics/processing-instructions/quasar/Quasar.toml b/basics/processing-instructions/quasar/Quasar.toml index c11d53cb9..092e40881 100644 --- a/basics/processing-instructions/quasar/Quasar.toml +++ b/basics/processing-instructions/quasar/Quasar.toml @@ -5,18 +5,4 @@ name = "quasar_processing_instructions" type = "solana" [testing] -language = "rust" - -[testing.rust] -framework = "quasar-svm" - -[testing.rust.test] -program = "cargo" -args = [ - "test", - "tests::", -] - -[clients] -path = "target/client" -languages = ["rust"] +framework = "quasarsvm-rust" diff --git a/basics/program-derived-addresses/quasar/Cargo.toml b/basics/program-derived-addresses/quasar/Cargo.toml index 184b18162..1f4e9e287 100644 --- a/basics/program-derived-addresses/quasar/Cargo.toml +++ b/basics/program-derived-addresses/quasar/Cargo.toml @@ -22,12 +22,12 @@ client = [] debug = [] [dependencies] -quasar-lang = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } +quasar-lang = { git = "https://github.com/blueshift-gg/quasar" } solana-instruction = { version = "3.2.0" } [dev-dependencies] quasar-program-derived-addresses-client = { path = "target/client/rust/quasar-program-derived-addresses-client" } -quasar-svm = { version = "0.1" } +quasar-svm = { git = "https://github.com/blueshift-gg/quasar-svm" } solana-account = { version = "3.4.0" } solana-address = { version = "2.2.0", features = ["decode"] } solana-instruction = { version = "3.2.0", features = ["bincode"] } diff --git a/basics/program-derived-addresses/quasar/Quasar.toml b/basics/program-derived-addresses/quasar/Quasar.toml index c87c6899a..1f980da5a 100644 --- a/basics/program-derived-addresses/quasar/Quasar.toml +++ b/basics/program-derived-addresses/quasar/Quasar.toml @@ -5,18 +5,4 @@ name = "quasar_program_derived_addresses" type = "solana" [testing] -language = "rust" - -[testing.rust] -framework = "quasar-svm" - -[testing.rust.test] -program = "cargo" -args = [ - "test", - "tests::", -] - -[clients] -path = "target/client" -languages = ["rust"] +framework = "quasarsvm-rust" diff --git a/basics/realloc/quasar/Cargo.toml b/basics/realloc/quasar/Cargo.toml index e0f18ea97..d61ef5b9d 100644 --- a/basics/realloc/quasar/Cargo.toml +++ b/basics/realloc/quasar/Cargo.toml @@ -20,11 +20,11 @@ client = [] debug = [] [dependencies] -quasar-lang = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } +quasar-lang = { git = "https://github.com/blueshift-gg/quasar" } solana-instruction = { version = "3.2.0" } [dev-dependencies] -quasar-svm = { version = "0.1" } +quasar-svm = { git = "https://github.com/blueshift-gg/quasar-svm" } solana-account = { version = "3.4.0" } solana-address = { version = "2.2.0", features = ["decode"] } solana-instruction = { version = "3.2.0", features = ["bincode"] } diff --git a/basics/realloc/quasar/Quasar.toml b/basics/realloc/quasar/Quasar.toml index b1b68fdbb..951ed2a80 100644 --- a/basics/realloc/quasar/Quasar.toml +++ b/basics/realloc/quasar/Quasar.toml @@ -5,18 +5,4 @@ name = "quasar_realloc" type = "solana" [testing] -language = "rust" - -[testing.rust] -framework = "quasar-svm" - -[testing.rust.test] -program = "cargo" -args = [ - "test", - "tests::", -] - -[clients] -path = "target/client" -languages = ["rust"] +framework = "quasarsvm-rust" diff --git a/basics/rent/quasar/Cargo.toml b/basics/rent/quasar/Cargo.toml index cfb1c9006..84c8f1eb4 100644 --- a/basics/rent/quasar/Cargo.toml +++ b/basics/rent/quasar/Cargo.toml @@ -22,11 +22,11 @@ client = [] debug = [] [dependencies] -quasar-lang = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } +quasar-lang = { git = "https://github.com/blueshift-gg/quasar" } solana-instruction = { version = "3.2.0" } [dev-dependencies] -quasar-svm = { version = "0.1" } +quasar-svm = { git = "https://github.com/blueshift-gg/quasar-svm" } solana-account = { version = "3.4.0" } solana-address = { version = "2.2.0", features = ["decode"] } solana-instruction = { version = "3.2.0", features = ["bincode"] } diff --git a/basics/rent/quasar/Quasar.toml b/basics/rent/quasar/Quasar.toml index ce63074d6..0ef6a41d9 100644 --- a/basics/rent/quasar/Quasar.toml +++ b/basics/rent/quasar/Quasar.toml @@ -5,18 +5,4 @@ name = "quasar_rent" type = "solana" [testing] -language = "rust" - -[testing.rust] -framework = "quasar-svm" - -[testing.rust.test] -program = "cargo" -args = [ - "test", - "tests::", -] - -[clients] -path = "target/client" -languages = ["rust"] +framework = "quasarsvm-rust" diff --git a/basics/repository-layout/quasar/Cargo.toml b/basics/repository-layout/quasar/Cargo.toml index 50c1ebc2d..a476a5926 100644 --- a/basics/repository-layout/quasar/Cargo.toml +++ b/basics/repository-layout/quasar/Cargo.toml @@ -22,11 +22,11 @@ client = [] debug = [] [dependencies] -quasar-lang = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } +quasar-lang = { git = "https://github.com/blueshift-gg/quasar" } solana-instruction = { version = "3.2.0" } [dev-dependencies] -quasar-svm = { version = "0.1" } +quasar-svm = { git = "https://github.com/blueshift-gg/quasar-svm" } solana-account = { version = "3.4.0" } solana-address = { version = "2.2.0", features = ["decode"] } solana-instruction = { version = "3.2.0", features = ["bincode"] } diff --git a/basics/repository-layout/quasar/Quasar.toml b/basics/repository-layout/quasar/Quasar.toml index 16496422a..e4d1a4a1d 100644 --- a/basics/repository-layout/quasar/Quasar.toml +++ b/basics/repository-layout/quasar/Quasar.toml @@ -5,18 +5,4 @@ name = "quasar_carnival" type = "solana" [testing] -language = "rust" - -[testing.rust] -framework = "quasar-svm" - -[testing.rust.test] -program = "cargo" -args = [ - "test", - "tests::", -] - -[clients] -path = "target/client" -languages = ["rust"] +framework = "quasarsvm-rust" diff --git a/basics/transfer-sol/quasar/Cargo.toml b/basics/transfer-sol/quasar/Cargo.toml index 934ed396e..dacb49696 100644 --- a/basics/transfer-sol/quasar/Cargo.toml +++ b/basics/transfer-sol/quasar/Cargo.toml @@ -22,12 +22,12 @@ client = [] debug = [] [dependencies] -quasar-lang = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } +quasar-lang = { git = "https://github.com/blueshift-gg/quasar" } solana-instruction = { version = "3.2.0" } [dev-dependencies] quasar-transfer-sol-client = { path = "target/client/rust/quasar-transfer-sol-client" } -quasar-svm = { version = "0.1" } +quasar-svm = { git = "https://github.com/blueshift-gg/quasar-svm" } solana-account = { version = "3.4.0" } solana-address = { version = "2.2.0", features = ["decode"] } solana-instruction = { version = "3.2.0", features = ["bincode"] } diff --git a/basics/transfer-sol/quasar/Quasar.toml b/basics/transfer-sol/quasar/Quasar.toml index 8d0bcab7a..1d154bb09 100644 --- a/basics/transfer-sol/quasar/Quasar.toml +++ b/basics/transfer-sol/quasar/Quasar.toml @@ -5,18 +5,4 @@ name = "quasar_transfer_sol" type = "solana" [testing] -language = "rust" - -[testing.rust] -framework = "quasar-svm" - -[testing.rust.test] -program = "cargo" -args = [ - "test", - "tests::", -] - -[clients] -path = "target/client" -languages = ["rust"] +framework = "quasarsvm-rust" diff --git a/compression/cnft-burn/quasar/Cargo.toml b/compression/cnft-burn/quasar/Cargo.toml index 21b7aaa66..497e22f2c 100644 --- a/compression/cnft-burn/quasar/Cargo.toml +++ b/compression/cnft-burn/quasar/Cargo.toml @@ -22,12 +22,12 @@ client = [] debug = [] [dependencies] -quasar-lang = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } +quasar-lang = { git = "https://github.com/blueshift-gg/quasar" } # Direct dependency for invoke_with_bounds — needed for raw CPI with variable # proof accounts. quasar-lang re-exports types but not the invoke functions. solana-instruction-view = { version = "2", features = ["cpi"] } solana-instruction = { version = "3.2.0" } [dev-dependencies] -quasar-svm = { version = "0.1" } +quasar-svm = { git = "https://github.com/blueshift-gg/quasar-svm" } solana-address = { version = "2.2.0", features = ["decode"] } diff --git a/compression/cnft-burn/quasar/Quasar.toml b/compression/cnft-burn/quasar/Quasar.toml index 09f8d9792..016ce0246 100644 --- a/compression/cnft-burn/quasar/Quasar.toml +++ b/compression/cnft-burn/quasar/Quasar.toml @@ -5,18 +5,4 @@ name = "quasar_cnft_burn" type = "solana" [testing] -language = "rust" - -[testing.rust] -framework = "quasar-svm" - -[testing.rust.test] -program = "cargo" -args = [ - "test", - "tests::", -] - -[clients] -path = "target/client" -languages = ["rust"] +framework = "quasarsvm-rust" diff --git a/compression/cnft-vault/quasar/Cargo.toml b/compression/cnft-vault/quasar/Cargo.toml index 60f3b06db..bd5cb17ab 100644 --- a/compression/cnft-vault/quasar/Cargo.toml +++ b/compression/cnft-vault/quasar/Cargo.toml @@ -22,12 +22,12 @@ client = [] debug = [] [dependencies] -quasar-lang = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } +quasar-lang = { git = "https://github.com/blueshift-gg/quasar" } # Direct dependency for invoke_signed_with_bounds — needed for raw CPI with # variable proof accounts. quasar-lang re-exports types but not the invoke fns. solana-instruction-view = { version = "2", features = ["cpi"] } solana-instruction = { version = "3.2.0" } [dev-dependencies] -quasar-svm = { version = "0.1" } +quasar-svm = { git = "https://github.com/blueshift-gg/quasar-svm" } solana-address = { version = "2.2.0", features = ["decode"] } diff --git a/compression/cnft-vault/quasar/Quasar.toml b/compression/cnft-vault/quasar/Quasar.toml index 504c00825..630915afc 100644 --- a/compression/cnft-vault/quasar/Quasar.toml +++ b/compression/cnft-vault/quasar/Quasar.toml @@ -5,18 +5,4 @@ name = "quasar_cnft_vault" type = "solana" [testing] -language = "rust" - -[testing.rust] -framework = "quasar-svm" - -[testing.rust.test] -program = "cargo" -args = [ - "test", - "tests::", -] - -[clients] -path = "target/client" -languages = ["rust"] +framework = "quasarsvm-rust" diff --git a/compression/cutils/quasar/Cargo.toml b/compression/cutils/quasar/Cargo.toml index 07727e2f2..16379e000 100644 --- a/compression/cutils/quasar/Cargo.toml +++ b/compression/cutils/quasar/Cargo.toml @@ -22,11 +22,11 @@ client = [] debug = [] [dependencies] -quasar-lang = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } +quasar-lang = { git = "https://github.com/blueshift-gg/quasar" } # Direct dependency for invoke_with_bounds — raw CPI with variable proof accounts. solana-instruction-view = { version = "2", features = ["cpi"] } solana-instruction = { version = "3.2.0" } [dev-dependencies] -quasar-svm = { version = "0.1" } +quasar-svm = { git = "https://github.com/blueshift-gg/quasar-svm" } solana-address = { version = "2.2.0", features = ["decode"] } diff --git a/compression/cutils/quasar/Quasar.toml b/compression/cutils/quasar/Quasar.toml index dbb185800..cbffba36a 100644 --- a/compression/cutils/quasar/Quasar.toml +++ b/compression/cutils/quasar/Quasar.toml @@ -5,18 +5,4 @@ name = "quasar_cutils" type = "solana" [testing] -language = "rust" - -[testing.rust] -framework = "quasar-svm" - -[testing.rust.test] -program = "cargo" -args = [ - "test", - "tests::", -] - -[clients] -path = "target/client" -languages = ["rust"] +framework = "quasarsvm-rust" diff --git a/oracles/pyth/quasar/Cargo.toml b/oracles/pyth/quasar/Cargo.toml index 877636ea5..ec4480da1 100644 --- a/oracles/pyth/quasar/Cargo.toml +++ b/oracles/pyth/quasar/Cargo.toml @@ -22,9 +22,9 @@ client = [] debug = [] [dependencies] -quasar-lang = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } +quasar-lang = { git = "https://github.com/blueshift-gg/quasar" } solana-instruction = { version = "3.2.0" } [dev-dependencies] -quasar-svm = { version = "0.1" } +quasar-svm = { git = "https://github.com/blueshift-gg/quasar-svm" } solana-address = { version = "2.2.0", features = ["decode"] } diff --git a/oracles/pyth/quasar/Quasar.toml b/oracles/pyth/quasar/Quasar.toml index 61d6f2eea..b63d035f7 100644 --- a/oracles/pyth/quasar/Quasar.toml +++ b/oracles/pyth/quasar/Quasar.toml @@ -5,18 +5,4 @@ name = "quasar_pyth_example" type = "solana" [testing] -language = "rust" - -[testing.rust] -framework = "quasar-svm" - -[testing.rust.test] -program = "cargo" -args = [ - "test", - "tests::", -] - -[clients] -path = "target/client" -languages = ["rust"] +framework = "quasarsvm-rust" diff --git a/tokens/create-token/quasar/Cargo.toml b/tokens/create-token/quasar/Cargo.toml index da603962e..3eef7659c 100644 --- a/tokens/create-token/quasar/Cargo.toml +++ b/tokens/create-token/quasar/Cargo.toml @@ -22,11 +22,11 @@ client = [] debug = [] [dependencies] -quasar-lang = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } -quasar-spl = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } +quasar-lang = { git = "https://github.com/blueshift-gg/quasar" } +quasar-spl = { git = "https://github.com/blueshift-gg/quasar" } solana-instruction = { version = "3.2.0" } [dev-dependencies] -quasar-svm = { version = "0.1" } +quasar-svm = { git = "https://github.com/blueshift-gg/quasar-svm" } spl-token-interface = { version = "2.0.0" } solana-program-pack = { version = "3.1.0" } diff --git a/tokens/create-token/quasar/Quasar.toml b/tokens/create-token/quasar/Quasar.toml index f41cddeb0..4177553a0 100644 --- a/tokens/create-token/quasar/Quasar.toml +++ b/tokens/create-token/quasar/Quasar.toml @@ -5,18 +5,4 @@ name = "quasar_create_token" type = "solana" [testing] -language = "rust" - -[testing.rust] -framework = "quasar-svm" - -[testing.rust.test] -program = "cargo" -args = [ - "test", - "tests::", -] - -[clients] -path = "target/client" -languages = ["rust"] +framework = "quasarsvm-rust" diff --git a/tokens/escrow/quasar/Cargo.toml b/tokens/escrow/quasar/Cargo.toml index f43961cdc..1e0e264cf 100644 --- a/tokens/escrow/quasar/Cargo.toml +++ b/tokens/escrow/quasar/Cargo.toml @@ -22,12 +22,12 @@ client = [] debug = [] [dependencies] -quasar-lang = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } -quasar-spl = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } +quasar-lang = { git = "https://github.com/blueshift-gg/quasar" } +quasar-spl = { git = "https://github.com/blueshift-gg/quasar" } solana-address = { version = "2.2.0" } solana-instruction = { version = "3.2.0" } [dev-dependencies] -quasar-svm = { version = "0.1" } +quasar-svm = { git = "https://github.com/blueshift-gg/quasar-svm" } spl-token-interface = { version = "2.0.0" } solana-program-pack = { version = "3.1.0" } diff --git a/tokens/escrow/quasar/Quasar.toml b/tokens/escrow/quasar/Quasar.toml index 90a78b55a..ae5f22c2b 100644 --- a/tokens/escrow/quasar/Quasar.toml +++ b/tokens/escrow/quasar/Quasar.toml @@ -5,18 +5,4 @@ name = "quasar_escrow" type = "solana" [testing] -language = "rust" - -[testing.rust] -framework = "quasar-svm" - -[testing.rust.test] -program = "cargo" -args = [ - "test", - "tests::", -] - -[clients] -path = "target/client" -languages = ["rust"] +framework = "quasarsvm-rust" diff --git a/tokens/external-delegate-token-master/quasar/Cargo.toml b/tokens/external-delegate-token-master/quasar/Cargo.toml index bc68d5080..347832b68 100644 --- a/tokens/external-delegate-token-master/quasar/Cargo.toml +++ b/tokens/external-delegate-token-master/quasar/Cargo.toml @@ -22,13 +22,13 @@ client = [] debug = [] [dependencies] -quasar-lang = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } -quasar-spl = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } +quasar-lang = { git = "https://github.com/blueshift-gg/quasar" } +quasar-spl = { git = "https://github.com/blueshift-gg/quasar" } solana-instruction = { version = "3.2.0" } solana-define-syscall = "4.0" solana-keccak-hasher = "3.1" [dev-dependencies] -quasar-svm = { version = "0.1" } +quasar-svm = { git = "https://github.com/blueshift-gg/quasar-svm" } spl-token-interface = { version = "2.0.0" } solana-program-pack = { version = "3.1.0" } diff --git a/tokens/nft-minter/quasar/Cargo.toml b/tokens/nft-minter/quasar/Cargo.toml index 1dc6abaab..c6d67aca2 100644 --- a/tokens/nft-minter/quasar/Cargo.toml +++ b/tokens/nft-minter/quasar/Cargo.toml @@ -22,11 +22,11 @@ client = [] debug = [] [dependencies] -quasar-lang = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } +quasar-lang = { git = "https://github.com/blueshift-gg/quasar" } quasar-spl = { git = "https://github.com/blueshift-gg/quasar", branch = "master", features = ["metadata"] } solana-instruction = { version = "3.2.0" } [dev-dependencies] -quasar-svm = { version = "0.1" } +quasar-svm = { git = "https://github.com/blueshift-gg/quasar-svm" } spl-token-interface = { version = "2.0.0" } solana-program-pack = { version = "3.1.0" } diff --git a/tokens/nft-operations/quasar/Cargo.toml b/tokens/nft-operations/quasar/Cargo.toml index c5716a2f5..0ea933855 100644 --- a/tokens/nft-operations/quasar/Cargo.toml +++ b/tokens/nft-operations/quasar/Cargo.toml @@ -22,11 +22,11 @@ client = [] debug = [] [dependencies] -quasar-lang = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } +quasar-lang = { git = "https://github.com/blueshift-gg/quasar" } quasar-spl = { git = "https://github.com/blueshift-gg/quasar", branch = "master", features = ["metadata"] } solana-instruction = { version = "3.2.0" } [dev-dependencies] -quasar-svm = { version = "0.1" } +quasar-svm = { git = "https://github.com/blueshift-gg/quasar-svm" } spl-token-interface = { version = "2.0.0" } solana-program-pack = { version = "3.1.0" } diff --git a/tokens/pda-mint-authority/quasar/Cargo.toml b/tokens/pda-mint-authority/quasar/Cargo.toml index 6b6367f50..f4bf7ddd9 100644 --- a/tokens/pda-mint-authority/quasar/Cargo.toml +++ b/tokens/pda-mint-authority/quasar/Cargo.toml @@ -21,11 +21,11 @@ client = [] debug = [] [dependencies] -quasar-lang = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } -quasar-spl = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } +quasar-lang = { git = "https://github.com/blueshift-gg/quasar" } +quasar-spl = { git = "https://github.com/blueshift-gg/quasar" } solana-instruction = { version = "3.2.0" } [dev-dependencies] -quasar-svm = { version = "0.1" } +quasar-svm = { git = "https://github.com/blueshift-gg/quasar-svm" } spl-token-interface = { version = "2.0.0" } solana-program-pack = { version = "3.1.0" } diff --git a/tokens/pda-mint-authority/quasar/Quasar.toml b/tokens/pda-mint-authority/quasar/Quasar.toml index bc309a3c5..e87f67349 100644 --- a/tokens/pda-mint-authority/quasar/Quasar.toml +++ b/tokens/pda-mint-authority/quasar/Quasar.toml @@ -5,18 +5,4 @@ name = "quasar_pda_mint_authority" type = "solana" [testing] -language = "rust" - -[testing.rust] -framework = "quasar-svm" - -[testing.rust.test] -program = "cargo" -args = [ - "test", - "tests::", -] - -[clients] -path = "target/client" -languages = ["rust"] +framework = "quasarsvm-rust" diff --git a/tokens/spl-token-minter/quasar/Cargo.toml b/tokens/spl-token-minter/quasar/Cargo.toml index 6ad8ed2b4..c04390da9 100644 --- a/tokens/spl-token-minter/quasar/Cargo.toml +++ b/tokens/spl-token-minter/quasar/Cargo.toml @@ -22,11 +22,11 @@ client = [] debug = [] [dependencies] -quasar-lang = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } +quasar-lang = { git = "https://github.com/blueshift-gg/quasar" } quasar-spl = { git = "https://github.com/blueshift-gg/quasar", branch = "master", features = ["metadata"] } solana-instruction = { version = "3.2.0" } [dev-dependencies] -quasar-svm = { version = "0.1" } +quasar-svm = { git = "https://github.com/blueshift-gg/quasar-svm" } spl-token-interface = { version = "2.0.0" } solana-program-pack = { version = "3.1.0" } diff --git a/tokens/token-fundraiser/quasar/Cargo.toml b/tokens/token-fundraiser/quasar/Cargo.toml index c6b52f936..b8bee4098 100644 --- a/tokens/token-fundraiser/quasar/Cargo.toml +++ b/tokens/token-fundraiser/quasar/Cargo.toml @@ -21,11 +21,11 @@ client = [] debug = [] [dependencies] -quasar-lang = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } -quasar-spl = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } +quasar-lang = { git = "https://github.com/blueshift-gg/quasar" } +quasar-spl = { git = "https://github.com/blueshift-gg/quasar" } solana-instruction = { version = "3.2.0" } [dev-dependencies] -quasar-svm = { version = "0.1" } +quasar-svm = { git = "https://github.com/blueshift-gg/quasar-svm" } spl-token-interface = { version = "2.0.0" } solana-program-pack = { version = "3.1.0" } diff --git a/tokens/token-fundraiser/quasar/Quasar.toml b/tokens/token-fundraiser/quasar/Quasar.toml index 6f34b8e28..52f1edf9a 100644 --- a/tokens/token-fundraiser/quasar/Quasar.toml +++ b/tokens/token-fundraiser/quasar/Quasar.toml @@ -5,18 +5,4 @@ name = "quasar_token_fundraiser" type = "solana" [testing] -language = "rust" - -[testing.rust] -framework = "quasar-svm" - -[testing.rust.test] -program = "cargo" -args = [ - "test", - "tests::", -] - -[clients] -path = "target/client" -languages = ["rust"] +framework = "quasarsvm-rust" diff --git a/tokens/token-swap/quasar/Cargo.toml b/tokens/token-swap/quasar/Cargo.toml index 8286c3ec6..fc8297ff8 100644 --- a/tokens/token-swap/quasar/Cargo.toml +++ b/tokens/token-swap/quasar/Cargo.toml @@ -22,11 +22,11 @@ client = [] debug = [] [dependencies] -quasar-lang = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } -quasar-spl = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } +quasar-lang = { git = "https://github.com/blueshift-gg/quasar" } +quasar-spl = { git = "https://github.com/blueshift-gg/quasar" } solana-instruction = { version = "3.2.0" } [dev-dependencies] -quasar-svm = { version = "0.1" } +quasar-svm = { git = "https://github.com/blueshift-gg/quasar-svm" } spl-token-interface = { version = "2.0.0" } solana-program-pack = { version = "3.1.0" } diff --git a/tokens/transfer-tokens/quasar/Cargo.toml b/tokens/transfer-tokens/quasar/Cargo.toml index f5fea0b98..b7c4aace9 100644 --- a/tokens/transfer-tokens/quasar/Cargo.toml +++ b/tokens/transfer-tokens/quasar/Cargo.toml @@ -22,11 +22,11 @@ client = [] debug = [] [dependencies] -quasar-lang = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } -quasar-spl = { git = "https://github.com/blueshift-gg/quasar", branch = "master" } +quasar-lang = { git = "https://github.com/blueshift-gg/quasar" } +quasar-spl = { git = "https://github.com/blueshift-gg/quasar" } solana-instruction = { version = "3.2.0" } [dev-dependencies] -quasar-svm = { version = "0.1" } +quasar-svm = { git = "https://github.com/blueshift-gg/quasar-svm" } spl-token-interface = { version = "2.0.0" } solana-program-pack = { version = "3.1.0" } diff --git a/tokens/transfer-tokens/quasar/Quasar.toml b/tokens/transfer-tokens/quasar/Quasar.toml index 7cf3db560..9e6e449f2 100644 --- a/tokens/transfer-tokens/quasar/Quasar.toml +++ b/tokens/transfer-tokens/quasar/Quasar.toml @@ -5,18 +5,4 @@ name = "quasar_transfer_tokens" type = "solana" [testing] -language = "rust" - -[testing.rust] -framework = "quasar-svm" - -[testing.rust.test] -program = "cargo" -args = [ - "test", - "tests::", -] - -[clients] -path = "target/client" -languages = ["rust"] +framework = "quasarsvm-rust" From af10959d1d95f4f691bf7e1c9641d14c61796c3d Mon Sep 17 00:00:00 2001 From: Jimii Date: Fri, 24 Apr 2026 22:06:22 +0300 Subject: [PATCH 08/12] fix --- .../src/instructions/create_system_account.rs | 2 +- .../hand/src/instructions/pull_lever.rs | 29 +++++-------------- .../quasar/hand/src/lib.rs | 2 +- 3 files changed, 9 insertions(+), 24 deletions(-) diff --git a/basics/create-account/quasar/src/instructions/create_system_account.rs b/basics/create-account/quasar/src/instructions/create_system_account.rs index fe00070a1..481cbc8bd 100644 --- a/basics/create-account/quasar/src/instructions/create_system_account.rs +++ b/basics/create-account/quasar/src/instructions/create_system_account.rs @@ -27,5 +27,5 @@ pub fn handle_create_system_account( &system_program_address, None, // fetch Rent sysvar automatically )? - .invoke() + .invoke(); } diff --git a/basics/cross-program-invocation/quasar/hand/src/instructions/pull_lever.rs b/basics/cross-program-invocation/quasar/hand/src/instructions/pull_lever.rs index 5713a60aa..7b15184c7 100644 --- a/basics/cross-program-invocation/quasar/hand/src/instructions/pull_lever.rs +++ b/basics/cross-program-invocation/quasar/hand/src/instructions/pull_lever.rs @@ -1,17 +1,14 @@ -use quasar_lang::{ - cpi::{BufCpiCall, InstructionAccount}, - prelude::*, -}; +use quasar_lang::prelude::*; /// Accounts for the hand program's pull_lever instruction. /// The lever_program uses `Program` with a custom marker type /// that implements `Id` — this lets Quasar verify the program address and /// the executable flag during account parsing. #[derive(Accounts)] -pub struct PullLever<'info> { +pub struct PullLever { #[account(mut)] - pub power: &'info UncheckedAccount, - pub lever_program: &'info Program, + pub power: UncheckedAccount, + pub lever_program: Program, } #[inline(always)] @@ -25,34 +22,22 @@ pub fn handle_pull_lever(accounts: &PullLever, name: &str) -> Result<(), Program let name_bytes = name.as_bytes(); let data_len = 1 + 4 + name_bytes.len(); - // Discriminator = 1 (switch_power) data[0] = 1; - // Name string: u32 little-endian length prefix + bytes let len_bytes = (name_bytes.len() as u32).to_le_bytes(); data[1] = len_bytes[0]; data[2] = len_bytes[1]; data[3] = len_bytes[2]; data[4] = len_bytes[3]; - // Copy name bytes let mut i = 0; while i < name_bytes.len() { data[5 + i] = name_bytes[i]; i += 1; } - let power_view = accounts.power.to_account_view(); - let lever_view = accounts.lever_program.to_account_view(); - - // Build CPI call with 1 account (power, writable, not a signer). - let cpi = BufCpiCall::<1, 128>::new( - lever_view.address(), - [InstructionAccount::writable(power_view.address())], - [power_view], - data, - data_len, - ); - + let mut cpi = DynCpiCall::<1, 128>::new(accounts.lever_program.address()); + cpi.push_account(accounts.power.to_account_view(), false, true)?; + cpi.set_data(&data[..data_len])?; cpi.invoke() } diff --git a/basics/cross-program-invocation/quasar/hand/src/lib.rs b/basics/cross-program-invocation/quasar/hand/src/lib.rs index e272cf83c..e3e425d92 100644 --- a/basics/cross-program-invocation/quasar/hand/src/lib.rs +++ b/basics/cross-program-invocation/quasar/hand/src/lib.rs @@ -26,7 +26,7 @@ mod quasar_hand { /// Pull the lever by invoking the lever program's switch_power via CPI. #[instruction(discriminator = 0)] - pub fn pull_lever(ctx: Ctx, name: String) -> Result<(), ProgramError> { + pub fn pull_lever(ctx: Ctx, name: String<50>) -> Result<(), ProgramError> { instructions::handle_pull_lever(&mut ctx.accounts, name) } } From 2d258009470977a0d0a7adf2c3f25b5077fdf114 Mon Sep 17 00:00:00 2001 From: Jimii <30603522+jim4067@users.noreply.github.com> Date: Sat, 25 Apr 2026 16:49:23 +0300 Subject: [PATCH 09/12] fix: failing ci --- .../src/instructions/create_system_account.rs | 16 ++--- .../quasar/src/instructions/set_favorites.rs | 13 ++-- basics/favorites/quasar/src/lib.rs | 2 +- basics/favorites/quasar/src/state.rs | 4 +- .../src/instructions/create_new_account.rs | 13 ++-- .../processing-instructions/quasar/src/lib.rs | 2 +- .../quasar/src/instructions/create.rs | 4 +- .../quasar/src/state/page_visits.rs | 2 +- .../quasar/src/instructions/initialize.rs | 12 ++-- .../realloc/quasar/src/instructions/update.rs | 12 ++-- basics/realloc/quasar/src/lib.rs | 4 +- basics/realloc/quasar/src/state.rs | 6 +- .../src/instructions/create_system_account.rs | 16 ++--- basics/rent/quasar/src/lib.rs | 4 +- basics/repository-layout/quasar/src/lib.rs | 12 ++-- .../quasar/src/instructions/burn_cnft.rs | 6 +- compression/cnft-burn/quasar/src/lib.rs | 4 +- .../quasar/src/instructions/withdraw.rs | 8 +-- .../quasar/src/instructions/withdraw_two.rs | 8 +-- compression/cnft-vault/quasar/src/lib.rs | 10 ++- .../cutils/quasar/src/instructions/mint.rs | 3 +- .../cutils/quasar/src/instructions/verify.rs | 6 +- compression/cutils/quasar/src/lib.rs | 7 ++- .../quasar/src/instructions/read_price.rs | 20 ++---- tokens/escrow/quasar/src/instructions/make.rs | 16 ++--- tokens/pda-mint-authority/quasar/src/lib.rs | 61 +++++++++++++------ .../quasar/src/instructions/initialize.rs | 14 ++--- .../quasar/src/instructions/refund.rs | 4 +- tokens/token-fundraiser/quasar/src/state.rs | 2 +- 29 files changed, 148 insertions(+), 143 deletions(-) diff --git a/basics/create-account/quasar/src/instructions/create_system_account.rs b/basics/create-account/quasar/src/instructions/create_system_account.rs index 481cbc8bd..18f984ca0 100644 --- a/basics/create-account/quasar/src/instructions/create_system_account.rs +++ b/basics/create-account/quasar/src/instructions/create_system_account.rs @@ -1,4 +1,4 @@ -use quasar_lang::prelude::*; +use quasar_lang::{prelude::*, sysvars::Sysvar}; /// Accounts for creating a new system-owned account. /// Both payer and new_account must sign the transaction. @@ -15,17 +15,11 @@ pub struct CreateSystemAccount { pub fn handle_create_system_account( accounts: &mut CreateSystemAccount, ) -> Result<(), ProgramError> { - // Create a zero-data account owned by the system program, - // funded with the minimum rent-exempt balance. let system_program_address = Address::default(); + let rent = Rent::get()?; + let lamports = rent.minimum_balance_unchecked(0); accounts .system_program - .create_account_with_minimum_balance( - &accounts.payer, - &accounts.new_account, - 0, // space: zero bytes of data - &system_program_address, - None, // fetch Rent sysvar automatically - )? - .invoke(); + .create_account(&accounts.payer, &accounts.new_account, lamports, 0u64, &system_program_address) + .invoke() } diff --git a/basics/favorites/quasar/src/instructions/set_favorites.rs b/basics/favorites/quasar/src/instructions/set_favorites.rs index 6debd3ae1..f1c4e1f0b 100644 --- a/basics/favorites/quasar/src/instructions/set_favorites.rs +++ b/basics/favorites/quasar/src/instructions/set_favorites.rs @@ -1,6 +1,6 @@ use { - crate::state::Favorites, - quasar_lang::prelude::*, + crate::state::{Favorites, FavoritesInner}, + quasar_lang::{prelude::*, sysvars::Sysvar}, }; /// Accounts for setting user favourites. Uses `init_if_needed` so the same @@ -10,16 +10,17 @@ pub struct SetFavorites { #[account(mut)] pub user: Signer, #[account(mut, init_if_needed, payer = user, seeds = Favorites::seeds(user), bump)] - pub favorites: Account>, + pub favorites: Account, pub system_program: Program, } #[inline(always)] pub fn handle_set_favorites(accounts: &mut SetFavorites, number: u64, color: &str) -> Result<(), ProgramError> { + let rent = Rent::get()?; accounts.favorites.set_inner( - number, - color, + FavoritesInner { number, color }, accounts.user.to_account_view(), - None, + rent.lamports_per_byte(), + rent.exemption_threshold_raw(), ) } diff --git a/basics/favorites/quasar/src/lib.rs b/basics/favorites/quasar/src/lib.rs index d6af2e37d..e716772fc 100644 --- a/basics/favorites/quasar/src/lib.rs +++ b/basics/favorites/quasar/src/lib.rs @@ -22,7 +22,7 @@ mod quasar_favorites { pub fn set_favorites( ctx: Ctx, number: u64, - color: String, + color: String<50>, ) -> Result<(), ProgramError> { instructions::handle_set_favorites(&mut ctx.accounts, number, color) } diff --git a/basics/favorites/quasar/src/state.rs b/basics/favorites/quasar/src/state.rs index 13673130b..d30d895bb 100644 --- a/basics/favorites/quasar/src/state.rs +++ b/basics/favorites/quasar/src/state.rs @@ -5,9 +5,9 @@ use quasar_lang::prelude::*; /// The Anchor version also stores `hobbies: Vec`, but Quasar doesn't /// support nested dynamic types (Vec). We keep number + color, which /// demonstrates fixed + dynamic field mixing in Quasar. -#[account(discriminator = 1)] +#[account(discriminator = 1, set_inner)] #[seeds(b"favorites", user: Address)] -pub struct Favorites<'a> { +pub struct Favorites { pub number: u64, pub color: String<50>, } diff --git a/basics/pda-rent-payer/quasar/src/instructions/create_new_account.rs b/basics/pda-rent-payer/quasar/src/instructions/create_new_account.rs index d26b89a10..3fdf30b91 100644 --- a/basics/pda-rent-payer/quasar/src/instructions/create_new_account.rs +++ b/basics/pda-rent-payer/quasar/src/instructions/create_new_account.rs @@ -1,4 +1,4 @@ -use quasar_lang::prelude::*; +use quasar_lang::{prelude::*, sysvars::Sysvar}; /// Accounts for creating a new account funded by the rent vault PDA. /// The rent vault signs the create_account CPI via PDA seeds. @@ -21,15 +21,10 @@ pub fn handle_create_new_account(accounts: &mut CreateNewAccount, rent_vault_bum ]; let system_program_address = Address::default(); + let rent = Rent::get()?; + let lamports = rent.minimum_balance_unchecked(0); - // Create a zero-data system-owned account, funded from the vault. accounts.system_program - .create_account_with_minimum_balance( - &accounts.rent_vault, - &accounts.new_account, - 0, // space: zero bytes of data - &system_program_address, - None, // fetch Rent sysvar automatically - )? + .create_account(&accounts.rent_vault, &accounts.new_account, lamports, 0u64, &system_program_address) .invoke_signed(seeds) } diff --git a/basics/processing-instructions/quasar/src/lib.rs b/basics/processing-instructions/quasar/src/lib.rs index 909d1dcc3..653b403d3 100644 --- a/basics/processing-instructions/quasar/src/lib.rs +++ b/basics/processing-instructions/quasar/src/lib.rs @@ -17,7 +17,7 @@ mod quasar_processing_instructions { /// Quasar can parse String instruction args (u32-prefixed wire format) but /// can't interpolate them into log messages (no format! in no_std). #[instruction(discriminator = 0)] - pub fn go_to_park(ctx: Ctx, name: String, height: u32) -> Result<(), ProgramError> { + pub fn go_to_park(ctx: Ctx, height: u32, name: String<50>) -> Result<(), ProgramError> { instructions::handle_go_to_park(&mut ctx.accounts, name, height) } } diff --git a/basics/program-derived-addresses/quasar/src/instructions/create.rs b/basics/program-derived-addresses/quasar/src/instructions/create.rs index 7094e64f7..2b4210d08 100644 --- a/basics/program-derived-addresses/quasar/src/instructions/create.rs +++ b/basics/program-derived-addresses/quasar/src/instructions/create.rs @@ -1,5 +1,5 @@ use { - crate::state::PageVisits, + crate::state::{PageVisits, PageVisitsInner}, quasar_lang::prelude::*, }; @@ -16,6 +16,6 @@ pub struct CreatePageVisits { #[inline(always)] pub fn handle_create_page_visits(accounts: &mut CreatePageVisits) -> Result<(), ProgramError> { - accounts.page_visits.set_inner(0u64); + accounts.page_visits.set_inner(PageVisitsInner { page_visits: 0 }); Ok(()) } diff --git a/basics/program-derived-addresses/quasar/src/state/page_visits.rs b/basics/program-derived-addresses/quasar/src/state/page_visits.rs index 6333e9728..d81790b49 100644 --- a/basics/program-derived-addresses/quasar/src/state/page_visits.rs +++ b/basics/program-derived-addresses/quasar/src/state/page_visits.rs @@ -2,7 +2,7 @@ use quasar_lang::prelude::*; /// PDA account that tracks page visits for a user. /// Derived from seeds: ["page_visits", user_pubkey]. -#[account(discriminator = 1)] +#[account(discriminator = 1, set_inner)] #[seeds(b"page_visits", payer: Address)] pub struct PageVisits { pub page_visits: u64, diff --git a/basics/realloc/quasar/src/instructions/initialize.rs b/basics/realloc/quasar/src/instructions/initialize.rs index 0efba2ced..ad652200d 100644 --- a/basics/realloc/quasar/src/instructions/initialize.rs +++ b/basics/realloc/quasar/src/instructions/initialize.rs @@ -1,6 +1,6 @@ use { - crate::state::MessageAccount, - quasar_lang::prelude::*, + crate::state::{MessageAccount, MessageAccountInner}, + quasar_lang::{prelude::*, sysvars::Sysvar}, }; /// Accounts for initialising a new message account. @@ -10,15 +10,17 @@ pub struct Initialize { #[account(mut)] pub payer: Signer, #[account(mut, init, payer = payer)] - pub message_account: Account>, + pub message_account: Account, pub system_program: Program, } #[inline(always)] pub fn handle_initialize(accounts: &mut Initialize, message: &str) -> Result<(), ProgramError> { + let rent = Rent::get()?; accounts.message_account.set_inner( - message, + MessageAccountInner { message }, accounts.payer.to_account_view(), - None, + rent.lamports_per_byte(), + rent.exemption_threshold_raw(), ) } diff --git a/basics/realloc/quasar/src/instructions/update.rs b/basics/realloc/quasar/src/instructions/update.rs index 9e6d60693..c0aa1a022 100644 --- a/basics/realloc/quasar/src/instructions/update.rs +++ b/basics/realloc/quasar/src/instructions/update.rs @@ -1,6 +1,6 @@ use { - crate::state::MessageAccount, - quasar_lang::prelude::*, + crate::state::{MessageAccount, MessageAccountInner}, + quasar_lang::{prelude::*, sysvars::Sysvar}, }; /// Accounts for updating a message account. @@ -11,15 +11,17 @@ pub struct Update { #[account(mut)] pub payer: Signer, #[account(mut)] - pub message_account: Account>, + pub message_account: Account, pub system_program: Program, } #[inline(always)] pub fn handle_update(accounts: &mut Update, message: &str) -> Result<(), ProgramError> { + let rent = Rent::get()?; accounts.message_account.set_inner( - message, + MessageAccountInner { message }, accounts.payer.to_account_view(), - None, + rent.lamports_per_byte(), + rent.exemption_threshold_raw(), ) } diff --git a/basics/realloc/quasar/src/lib.rs b/basics/realloc/quasar/src/lib.rs index 3373f864a..6b0a512b2 100644 --- a/basics/realloc/quasar/src/lib.rs +++ b/basics/realloc/quasar/src/lib.rs @@ -16,14 +16,14 @@ mod quasar_realloc { /// Create a message account with an initial message. #[instruction(discriminator = 0)] - pub fn initialize(ctx: Ctx, message: String) -> Result<(), ProgramError> { + pub fn initialize(ctx: Ctx, message: String<1024>) -> Result<(), ProgramError> { instructions::handle_initialize(&mut ctx.accounts, message) } /// Update the message, reallocating if the new message is longer. /// Quasar's `set_inner` handles realloc transparently. #[instruction(discriminator = 1)] - pub fn update(ctx: Ctx, message: String) -> Result<(), ProgramError> { + pub fn update(ctx: Ctx, message: String<1024>) -> Result<(), ProgramError> { instructions::handle_update(&mut ctx.accounts, message) } } diff --git a/basics/realloc/quasar/src/state.rs b/basics/realloc/quasar/src/state.rs index 746d56d9e..183de8eca 100644 --- a/basics/realloc/quasar/src/state.rs +++ b/basics/realloc/quasar/src/state.rs @@ -3,7 +3,7 @@ use quasar_lang::prelude::*; /// Message account with a dynamic-length message field. /// Quasar's `set_inner` automatically reallocs when the new message exceeds /// the current account size, making explicit realloc unnecessary. -#[account(discriminator = 1)] -pub struct MessageAccount<'a> { - pub message: String, +#[account(discriminator = 1, set_inner)] +pub struct MessageAccount { + pub message: String<1024>, } diff --git a/basics/rent/quasar/src/instructions/create_system_account.rs b/basics/rent/quasar/src/instructions/create_system_account.rs index e1e4227cd..ccd087a15 100644 --- a/basics/rent/quasar/src/instructions/create_system_account.rs +++ b/basics/rent/quasar/src/instructions/create_system_account.rs @@ -1,4 +1,4 @@ -use quasar_lang::prelude::*; +use quasar_lang::{prelude::*, sysvars::Sysvar}; /// Accounts for creating a system account sized for address data. #[derive(Accounts)] @@ -18,20 +18,12 @@ pub fn handle_create_system_account(accounts: &mut CreateSystemAccount, name: &s log("Program invoked. Creating a system account..."); - // The owner of the new account is the system program. let system_program_address = Address::default(); + let rent = Rent::get()?; + let lamports = rent.minimum_balance_unchecked(space); - // Create the account with the computed space. - // create_account_with_minimum_balance automatically fetches Rent - // sysvar and calculates the minimum rent-exempt lamports. accounts.system_program - .create_account_with_minimum_balance( - &accounts.payer, - &accounts.new_account, - space as u64, - &system_program_address, - None, // fetch Rent sysvar automatically - )? + .create_account(&accounts.payer, &accounts.new_account, lamports, space as u64, &system_program_address) .invoke()?; log("Account created successfully."); diff --git a/basics/rent/quasar/src/lib.rs b/basics/rent/quasar/src/lib.rs index 854b3945f..931d0b02d 100644 --- a/basics/rent/quasar/src/lib.rs +++ b/basics/rent/quasar/src/lib.rs @@ -22,8 +22,8 @@ mod quasar_rent { #[instruction(discriminator = 0)] pub fn create_system_account( ctx: Ctx, - name: String, - address: String, + name: String<50>, + address: String<50>, ) -> Result<(), ProgramError> { instructions::handle_create_system_account(&mut ctx.accounts, name, address) } diff --git a/basics/repository-layout/quasar/src/lib.rs b/basics/repository-layout/quasar/src/lib.rs index 43ecd62de..a60775c39 100644 --- a/basics/repository-layout/quasar/src/lib.rs +++ b/basics/repository-layout/quasar/src/lib.rs @@ -18,10 +18,10 @@ mod quasar_carnival { #[instruction(discriminator = 0)] pub fn go_on_ride( ctx: Ctx, - name: String, height: u32, ticket_count: u32, - ride_name: String, + name: String<50>, + ride_name: String<50>, ) -> Result<(), ProgramError> { instructions::handle_go_on_ride(&mut ctx.accounts, name, height, ticket_count, ride_name) } @@ -30,9 +30,9 @@ mod quasar_carnival { #[instruction(discriminator = 1)] pub fn play_game( ctx: Ctx, - name: String, ticket_count: u32, - game_name: String, + name: String<50>, + game_name: String<50>, ) -> Result<(), ProgramError> { instructions::handle_play_game(&mut ctx.accounts, name, ticket_count, game_name) } @@ -41,9 +41,9 @@ mod quasar_carnival { #[instruction(discriminator = 2)] pub fn eat_food( ctx: Ctx, - name: String, ticket_count: u32, - food_stand_name: String, + name: String<50>, + food_stand_name: String<50>, ) -> Result<(), ProgramError> { instructions::handle_eat_food(&mut ctx.accounts, name, ticket_count, food_stand_name) } diff --git a/compression/cnft-burn/quasar/src/instructions/burn_cnft.rs b/compression/cnft-burn/quasar/src/instructions/burn_cnft.rs index 246750e63..e5eeab412 100644 --- a/compression/cnft-burn/quasar/src/instructions/burn_cnft.rs +++ b/compression/cnft-burn/quasar/src/instructions/burn_cnft.rs @@ -1,5 +1,5 @@ use crate::*; -use quasar_lang::cpi::{InstructionAccount, InstructionView}; +use quasar_lang::{cpi::{InstructionAccount, InstructionView}, remaining::RemainingAccounts}; /// Maximum number of proof nodes for the merkle tree. /// Concurrent merkle trees support up to depth 30, but typical depth is 14-20. @@ -30,10 +30,9 @@ pub struct BurnCnft { pub system_program: Program, } -pub fn handle_burn_cnft(accounts: &mut BurnCnft, ctx: &CtxWithRemaining) -> Result<(), ProgramError> { +pub fn handle_burn_cnft(accounts: &mut BurnCnft, data: &[u8], remaining: RemainingAccounts<'_>) -> Result<(), ProgramError> { // Parse instruction args from raw data: // root(32) + data_hash(32) + creator_hash(32) + nonce(8) + index(4) = 108 bytes - let data = ctx.data; if data.len() < 108 { return Err(ProgramError::InvalidInstructionData); } @@ -45,7 +44,6 @@ pub fn handle_burn_cnft(accounts: &mut BurnCnft, ctx: &CtxWithRemaining) -> Result<(), ProgramError> { - instructions::handle_burn_cnft(&mut ctx.accounts, &ctx) + let data = ctx.data; + let remaining = ctx.remaining_accounts(); + instructions::handle_burn_cnft(&mut ctx.accounts, data, remaining) } } diff --git a/compression/cnft-vault/quasar/src/instructions/withdraw.rs b/compression/cnft-vault/quasar/src/instructions/withdraw.rs index 7032f53ba..c1bde7f46 100644 --- a/compression/cnft-vault/quasar/src/instructions/withdraw.rs +++ b/compression/cnft-vault/quasar/src/instructions/withdraw.rs @@ -1,5 +1,5 @@ use crate::*; -use quasar_lang::cpi::{InstructionAccount, InstructionView, Seed, Signer}; +use quasar_lang::{cpi::{InstructionAccount, InstructionView, Seed, Signer}, remaining::RemainingAccounts}; /// Maximum proof nodes for the merkle tree. const MAX_PROOF_NODES: usize = 24; @@ -43,8 +43,7 @@ fn build_transfer_data(args: &[u8]) -> [u8; 8 + TRANSFER_ARGS_LEN] { ix_data } -pub fn handle_withdraw_cnft(accounts: &mut Withdraw, ctx: &CtxWithRemaining) -> Result<(), ProgramError> { - let data = ctx.data; +pub fn handle_withdraw_cnft(accounts: &mut Withdraw, data: &[u8], remaining: RemainingAccounts<'_>, leaf_owner_bump: u8) -> Result<(), ProgramError> { if data.len() < TRANSFER_ARGS_LEN { return Err(ProgramError::InvalidInstructionData); } @@ -52,7 +51,6 @@ pub fn handle_withdraw_cnft(accounts: &mut Withdraw, ctx: &CtxWithRemaining) -> Result<(), ProgramError> { +pub fn handle_withdraw_two_cnfts(accounts: &mut WithdrawTwo, data: &[u8], remaining: RemainingAccounts<'_>, leaf_owner_bump: u8) -> Result<(), ProgramError> { // Parse instruction args: // args1(108) + proof_1_length(1) + args2(108) + _proof_2_length(1) = 218 bytes - let data = ctx.data; if data.len() < 218 { return Err(ProgramError::InvalidInstructionData); } @@ -59,7 +58,7 @@ pub fn handle_withdraw_two_cnfts(accounts: &mut WithdrawTwo, ctx: &CtxWithRemain // _proof_2_length at data[217] — not needed, remaining after proof1 is proof2 // PDA signer seeds - let bump_bytes = [ctx.bumps.leaf_owner]; + let bump_bytes = [leaf_owner_bump]; let seeds: [Seed; 2] = [ Seed::from(b"cNFT-vault" as &[u8]), Seed::from(&bump_bytes as &[u8]), @@ -67,7 +66,6 @@ pub fn handle_withdraw_two_cnfts(accounts: &mut WithdrawTwo, ctx: &CtxWithRemain let signer = Signer::from(&seeds as &[Seed]); // Collect all remaining accounts (proof1 ++ proof2) - let remaining = ctx.remaining_accounts(); let placeholder = accounts.system_program.to_account_view().clone(); let mut all_proofs: [AccountView; MAX_PROOF_NODES * 2] = core::array::from_fn(|_| placeholder.clone()); diff --git a/compression/cnft-vault/quasar/src/lib.rs b/compression/cnft-vault/quasar/src/lib.rs index 8b1a4f81f..c066b0245 100644 --- a/compression/cnft-vault/quasar/src/lib.rs +++ b/compression/cnft-vault/quasar/src/lib.rs @@ -33,12 +33,18 @@ mod quasar_cnft_vault { /// Withdraw a single compressed NFT from the vault PDA. #[instruction(discriminator = 0)] pub fn withdraw_cnft(ctx: CtxWithRemaining) -> Result<(), ProgramError> { - instructions::handle_withdraw_cnft(&mut ctx.accounts, &ctx) + let data = ctx.data; + let remaining = ctx.remaining_accounts(); + let leaf_owner_bump = ctx.bumps.leaf_owner; + instructions::handle_withdraw_cnft(&mut ctx.accounts, data, remaining, leaf_owner_bump) } /// Withdraw two compressed NFTs from the vault PDA in a single transaction. #[instruction(discriminator = 1)] pub fn withdraw_two_cnfts(ctx: CtxWithRemaining) -> Result<(), ProgramError> { - instructions::handle_withdraw_two_cnfts(&mut ctx.accounts, &ctx) + let data = ctx.data; + let remaining = ctx.remaining_accounts(); + let leaf_owner_bump = ctx.bumps.leaf_owner; + instructions::handle_withdraw_two_cnfts(&mut ctx.accounts, data, remaining, leaf_owner_bump) } } diff --git a/compression/cutils/quasar/src/instructions/mint.rs b/compression/cutils/quasar/src/instructions/mint.rs index 2cd5bacb5..9f95275b4 100644 --- a/compression/cutils/quasar/src/instructions/mint.rs +++ b/compression/cutils/quasar/src/instructions/mint.rs @@ -53,9 +53,8 @@ pub struct Mint { pub system_program: Program, } -pub fn handle_mint(accounts: &mut Mint, ctx: &Ctx) -> Result<(), ProgramError> { +pub fn handle_mint(accounts: &mut Mint, data: &[u8]) -> Result<(), ProgramError> { // Parse URI from instruction data: u32 length prefix + utf8 bytes (borsh String) - let data = ctx.data; if data.len() < 4 { return Err(ProgramError::InvalidInstructionData); } diff --git a/compression/cutils/quasar/src/instructions/verify.rs b/compression/cutils/quasar/src/instructions/verify.rs index cedc13c68..76184326d 100644 --- a/compression/cutils/quasar/src/instructions/verify.rs +++ b/compression/cutils/quasar/src/instructions/verify.rs @@ -1,6 +1,6 @@ use crate::bubblegum_types::{get_asset_id, leaf_schema_v1_hash}; use crate::*; -use quasar_lang::cpi::{InstructionAccount, InstructionView}; +use quasar_lang::{cpi::{InstructionAccount, InstructionView}, remaining::RemainingAccounts}; /// Maximum proof nodes for the merkle tree. const MAX_PROOF_NODES: usize = 24; @@ -24,10 +24,9 @@ pub struct Verify { pub compression_program: UncheckedAccount, } -pub fn handle_verify(accounts: &mut Verify, ctx: &CtxWithRemaining) -> Result<(), ProgramError> { +pub fn handle_verify(accounts: &mut Verify, data: &[u8], remaining: RemainingAccounts<'_>) -> Result<(), ProgramError> { // Parse verify params from instruction data: // root(32) + data_hash(32) + creator_hash(32) + nonce(8) + index(4) = 108 bytes - let data = ctx.data; if data.len() < 108 { return Err(ProgramError::InvalidInstructionData); } @@ -57,7 +56,6 @@ pub fn handle_verify(accounts: &mut Verify, ctx: &CtxWithRemaining) -> R ix_data[72..76].copy_from_slice(&index.to_le_bytes()); // Collect proof nodes - let remaining = ctx.remaining_accounts(); let placeholder = accounts.compression_program.to_account_view().clone(); let mut proof_views: [AccountView; MAX_PROOF_NODES] = core::array::from_fn(|_| placeholder.clone()); diff --git a/compression/cutils/quasar/src/lib.rs b/compression/cutils/quasar/src/lib.rs index fdfaf20ba..e9b674c61 100644 --- a/compression/cutils/quasar/src/lib.rs +++ b/compression/cutils/quasar/src/lib.rs @@ -32,12 +32,15 @@ mod quasar_cutils { /// Mint a compressed NFT to a collection via MintToCollectionV1. #[instruction(discriminator = 0)] pub fn mint(ctx: Ctx) -> Result<(), ProgramError> { - instructions::handle_mint(&mut ctx.accounts, &ctx) + let data = ctx.data; + instructions::handle_mint(&mut ctx.accounts, data) } /// Verify a compressed NFT leaf exists in the merkle tree. #[instruction(discriminator = 1)] pub fn verify(ctx: CtxWithRemaining) -> Result<(), ProgramError> { - instructions::handle_verify(&mut ctx.accounts, &ctx) + let data = ctx.data; + let remaining = ctx.remaining_accounts(); + instructions::handle_verify(&mut ctx.accounts, data, remaining) } } diff --git a/oracles/pyth/quasar/src/instructions/read_price.rs b/oracles/pyth/quasar/src/instructions/read_price.rs index 9a150e6a4..9c28d9018 100644 --- a/oracles/pyth/quasar/src/instructions/read_price.rs +++ b/oracles/pyth/quasar/src/instructions/read_price.rs @@ -27,42 +27,34 @@ pub struct ReadPrice { #[inline(always)] pub fn handle_read_price(accounts: &mut ReadPrice) -> Result<(), ProgramError> { let view = accounts.price_update.to_account_view(); - let data = view.data(); + let data = unsafe { core::slice::from_raw_parts(view.data_ptr(), view.data_len()) }; if data.len() < MIN_DATA_LEN { return Err(ProgramError::InvalidAccountData); } - let price = i64::from_le_bytes( + let _price = i64::from_le_bytes( data[PRICE_OFFSET..PRICE_OFFSET + 8] .try_into() .map_err(|_| ProgramError::InvalidAccountData)?, ); - let conf = u64::from_le_bytes( + let _conf = u64::from_le_bytes( data[CONF_OFFSET..CONF_OFFSET + 8] .try_into() .map_err(|_| ProgramError::InvalidAccountData)?, ); - let exponent = i32::from_le_bytes( + let _exponent = i32::from_le_bytes( data[EXPONENT_OFFSET..EXPONENT_OFFSET + 4] .try_into() .map_err(|_| ProgramError::InvalidAccountData)?, ); - let publish_time = i64::from_le_bytes( + let _publish_time = i64::from_le_bytes( data[PUBLISH_TIME_OFFSET..PUBLISH_TIME_OFFSET + 8] .try_into() .map_err(|_| ProgramError::InvalidAccountData)?, ); - log("Pyth price feed data:"); - log(" price (raw):"); - log_64(price as u64); - log(" confidence:"); - log_64(conf); - log(" exponent:"); - log_64(exponent as u64); - log(" publish_time:"); - log_64(publish_time as u64); + log("Pyth price feed data read successfully."); Ok(()) } diff --git a/tokens/escrow/quasar/src/instructions/make.rs b/tokens/escrow/quasar/src/instructions/make.rs index c26987f03..9ab4ec730 100644 --- a/tokens/escrow/quasar/src/instructions/make.rs +++ b/tokens/escrow/quasar/src/instructions/make.rs @@ -1,5 +1,5 @@ use { - crate::state::Escrow, + crate::state::{Escrow, EscrowInner}, quasar_lang::prelude::*, quasar_spl::{Mint, Token, TokenCpi}, }; @@ -25,14 +25,14 @@ pub struct Make { #[inline(always)] pub fn handle_make_escrow(accounts: &mut Make, receive: u64, bumps: &MakeBumps) -> Result<(), ProgramError> { - accounts.escrow.set_inner( - *accounts.maker.address(), - *accounts.mint_a.address(), - *accounts.mint_b.address(), - *accounts.maker_ta_b.address(), + accounts.escrow.set_inner(EscrowInner { + maker: *accounts.maker.address(), + mint_a: *accounts.mint_a.address(), + mint_b: *accounts.mint_b.address(), + maker_ta_b: *accounts.maker_ta_b.address(), receive, - bumps.escrow, - ); + bump: bumps.escrow, + }); Ok(()) } diff --git a/tokens/pda-mint-authority/quasar/src/lib.rs b/tokens/pda-mint-authority/quasar/src/lib.rs index c3c87f011..6a72cb367 100644 --- a/tokens/pda-mint-authority/quasar/src/lib.rs +++ b/tokens/pda-mint-authority/quasar/src/lib.rs @@ -1,22 +1,21 @@ #![cfg_attr(not(test), no_std)] -use quasar_lang::prelude::*; -use quasar_spl::{Mint, Token, TokenCpi}; +use quasar_lang::{prelude::*, sysvars::Sysvar}; +use quasar_spl::{initialize_mint2, Mint, Token, TokenCpi}; #[cfg(test)] mod tests; declare_id!("22222222222222222222222222222222222222222222"); +/// SPL Mint account size in bytes. +const MINT_SPACE: usize = 82; + /// Demonstrates using a PDA as the mint authority for an SPL token. /// -/// The mint account itself is at a PDA address derived from `["mint"]`. +/// The mint account is created at the PDA address derived from `["mint"]`. /// The same PDA serves as both the mint address AND the mint authority, /// so minting requires PDA signing. -/// -/// The Anchor version uses Metaplex for onchain metadata. Quasar does not have -/// a Metaplex integration crate, so this example focuses on the PDA-as-authority -/// pattern. #[program] mod quasar_pda_mint_authority { use super::*; @@ -24,7 +23,7 @@ mod quasar_pda_mint_authority { /// Create a token mint at a PDA. The PDA is its own mint authority. #[instruction(discriminator = 0)] pub fn create_mint(ctx: Ctx, _decimals: u8) -> Result<(), ProgramError> { - handle_create_mint(&mut ctx.accounts) + handle_create_mint(&mut ctx.accounts, ctx.bumps.mint) } /// Mint tokens using the PDA mint authority. @@ -34,23 +33,49 @@ mod quasar_pda_mint_authority { } } -/// Create the mint at a PDA. The mint authority is the mint PDA itself. +/// Create the mint at a PDA. Manually created and initialized to avoid +/// a borrow conflict from `mint::authority = mint` in the init constraint. #[derive(Accounts)] pub struct CreateMint { #[account(mut)] pub payer: Signer, - /// The mint account at PDA ["mint"]. Its authority is set to itself. - #[account(mut, init, payer = payer, seeds = [b"mint"], bump, mint::decimals = 9, mint::authority = mint)] - pub mint: Account, - pub rent: Sysvar, + /// The PDA that will become the mint (and its own authority). + #[account(mut, seeds = [b"mint"], bump)] + pub mint: UncheckedAccount, pub token_program: Program, pub system_program: Program, } #[inline(always)] -fn handle_create_mint(_accounts: &mut CreateMint) -> Result<(), ProgramError> { - // Mint is created and initialised by Quasar's #[account(init)]. - Ok(()) +fn handle_create_mint(accounts: &mut CreateMint, bump: u8) -> Result<(), ProgramError> { + let mint_address = *accounts.mint.address(); + let bump_bytes = [bump]; + let seeds: &[Seed] = &[ + Seed::from(b"mint" as &[u8]), + Seed::from(&bump_bytes as &[u8]), + ]; + + let rent = Rent::get()?; + let lamports = rent.minimum_balance_unchecked(MINT_SPACE); + + accounts.system_program + .create_account( + &accounts.payer, + &accounts.mint, + lamports, + MINT_SPACE as u64, + accounts.token_program.address(), + ) + .invoke_signed(seeds)?; + + initialize_mint2( + accounts.token_program.to_account_view(), + accounts.mint.to_account_view(), + 9, + &mint_address, + None, + ) + .invoke() } /// Mint tokens to a token account, signing with the PDA mint authority. @@ -69,14 +94,14 @@ pub struct MintTokens { #[inline(always)] fn handle_mint_tokens(accounts: &mut MintTokens, amount: u64, mint_bump: u8) -> Result<(), ProgramError> { - // The PDA mint is its own authority. Build signer seeds. let bump = [mint_bump]; let seeds: &[Seed] = &[ Seed::from(b"mint" as &[u8]), Seed::from(&bump as &[u8]), ]; + let mint_view = accounts.mint.to_account_view().clone(); accounts.token_program - .mint_to(&accounts.mint, &accounts.token_account, &accounts.mint, amount) + .mint_to(&mint_view, &accounts.token_account, &mint_view, amount) .invoke_signed(seeds) } diff --git a/tokens/token-fundraiser/quasar/src/instructions/initialize.rs b/tokens/token-fundraiser/quasar/src/instructions/initialize.rs index 8be74ba09..6fa0e4d39 100644 --- a/tokens/token-fundraiser/quasar/src/instructions/initialize.rs +++ b/tokens/token-fundraiser/quasar/src/instructions/initialize.rs @@ -1,5 +1,5 @@ use { - crate::state::Fundraiser, + crate::state::{Fundraiser, FundraiserInner}, quasar_lang::prelude::*, quasar_spl::{Mint, Token}, }; @@ -28,14 +28,14 @@ pub fn handle_initialize( // Validate minimum raise amount require!(amount_to_raise > 0, ProgramError::InvalidArgument); - accounts.fundraiser.set_inner( - *accounts.maker.address(), - *accounts.mint_to_raise.address(), + accounts.fundraiser.set_inner(FundraiserInner { + maker: *accounts.maker.address(), + mint_to_raise: *accounts.mint_to_raise.address(), amount_to_raise, - 0, // current_amount starts at 0 - 0, // time_started — would be Clock::get() onchain + current_amount: 0, + time_started: 0, duration, bump, - ); + }); Ok(()) } diff --git a/tokens/token-fundraiser/quasar/src/instructions/refund.rs b/tokens/token-fundraiser/quasar/src/instructions/refund.rs index 59172ced8..aadbe0bb7 100644 --- a/tokens/token-fundraiser/quasar/src/instructions/refund.rs +++ b/tokens/token-fundraiser/quasar/src/instructions/refund.rs @@ -1,5 +1,5 @@ use { - crate::state::{Contributor, Fundraiser}, + crate::state::{Contributor, ContributorInner, Fundraiser}, quasar_lang::prelude::*, quasar_spl::{Token, TokenCpi}, }; @@ -42,7 +42,7 @@ pub fn handle_refund(accounts: &mut Refund, bumps: &RefundBumps) -> Result<(), P .ok_or(ProgramError::ArithmeticOverflow)?; // Zero out contributor amount - accounts.contributor_account.set_inner(0); + accounts.contributor_account.set_inner(ContributorInner { amount: 0 }); Ok(()) } diff --git a/tokens/token-fundraiser/quasar/src/state.rs b/tokens/token-fundraiser/quasar/src/state.rs index 9db1768d9..9bbe003dc 100644 --- a/tokens/token-fundraiser/quasar/src/state.rs +++ b/tokens/token-fundraiser/quasar/src/state.rs @@ -14,7 +14,7 @@ pub struct Fundraiser { } /// Tracks how much a specific contributor has given. -#[account(discriminator = 2)] +#[account(discriminator = 2, set_inner)] pub struct Contributor { pub amount: u64, } From 70c45d2a404e66480cc746de93a44dc48f76f9d8 Mon Sep 17 00:00:00 2001 From: Mike MacCana Date: Tue, 28 Apr 2026 20:37:17 +0000 Subject: [PATCH 10/12] fix(quasar): migrate Quasar.toml manifests to new format MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Every per-project Quasar.toml in old format failed CI with: ✘ Quasar.toml uses an outdated format. Run quasar config reset and re-init your project. The PR migrated program code to the new Quasar API but did not regenerate the per-project manifests. The current Quasar CLI expects the new structured form: [testing] language = "rust" [testing.rust] framework = "quasar-svm" [testing.rust.test] program = "cargo" args = ["test", "tests::"] [clients] languages = ["rust"] instead of the old single-line: [testing] framework = "quasarsvm-rust" This commit converts all 25 remaining old-format Quasar.toml files in place. The 18 manifests that were already migrated (most token-extension projects) are left untouched. Verified locally with quasar build + cargo test on representative projects across each category: basics/create-account, basics/hello-solana, compression/cnft-burn, tokens/transfer-tokens, and a token-extensions sample. --- basics/account-data/quasar/Quasar.toml | 16 +++++++++++++++- basics/checking-accounts/quasar/Quasar.toml | 16 +++++++++++++++- basics/close-account/quasar/Quasar.toml | 16 +++++++++++++++- basics/counter/quasar/Quasar.toml | 16 +++++++++++++++- basics/create-account/quasar/Quasar.toml | 16 +++++++++++++++- .../quasar/hand/Quasar.toml | 16 +++++++++++++++- .../quasar/lever/Quasar.toml | 16 +++++++++++++++- basics/favorites/quasar/Quasar.toml | 16 +++++++++++++++- basics/hello-solana/quasar/Quasar.toml | 16 +++++++++++++++- basics/pda-rent-payer/quasar/Quasar.toml | 16 +++++++++++++++- .../processing-instructions/quasar/Quasar.toml | 16 +++++++++++++++- .../program-derived-addresses/quasar/Quasar.toml | 16 +++++++++++++++- basics/realloc/quasar/Quasar.toml | 16 +++++++++++++++- basics/rent/quasar/Quasar.toml | 16 +++++++++++++++- basics/repository-layout/quasar/Quasar.toml | 16 +++++++++++++++- basics/transfer-sol/quasar/Quasar.toml | 16 +++++++++++++++- compression/cnft-burn/quasar/Quasar.toml | 16 +++++++++++++++- compression/cnft-vault/quasar/Quasar.toml | 16 +++++++++++++++- compression/cutils/quasar/Quasar.toml | 16 +++++++++++++++- oracles/pyth/quasar/Quasar.toml | 16 +++++++++++++++- tokens/create-token/quasar/Quasar.toml | 16 +++++++++++++++- tokens/escrow/quasar/Quasar.toml | 16 +++++++++++++++- tokens/pda-mint-authority/quasar/Quasar.toml | 16 +++++++++++++++- tokens/token-fundraiser/quasar/Quasar.toml | 16 +++++++++++++++- tokens/transfer-tokens/quasar/Quasar.toml | 16 +++++++++++++++- 25 files changed, 375 insertions(+), 25 deletions(-) diff --git a/basics/account-data/quasar/Quasar.toml b/basics/account-data/quasar/Quasar.toml index cf360c065..7e8c7764c 100644 --- a/basics/account-data/quasar/Quasar.toml +++ b/basics/account-data/quasar/Quasar.toml @@ -5,4 +5,18 @@ name = "quasar_account_data" type = "solana" [testing] -framework = "quasarsvm-rust" +language = "rust" + +[testing.rust] +framework = "quasar-svm" + +[testing.rust.test] +program = "cargo" +args = [ + "test", + "tests::", +] + +[clients] +path = "target/client" +languages = ["rust"] diff --git a/basics/checking-accounts/quasar/Quasar.toml b/basics/checking-accounts/quasar/Quasar.toml index 3c05d24e7..b352a401a 100644 --- a/basics/checking-accounts/quasar/Quasar.toml +++ b/basics/checking-accounts/quasar/Quasar.toml @@ -5,4 +5,18 @@ name = "quasar_checking_accounts" type = "solana" [testing] -framework = "quasarsvm-rust" +language = "rust" + +[testing.rust] +framework = "quasar-svm" + +[testing.rust.test] +program = "cargo" +args = [ + "test", + "tests::", +] + +[clients] +path = "target/client" +languages = ["rust"] diff --git a/basics/close-account/quasar/Quasar.toml b/basics/close-account/quasar/Quasar.toml index 0223f4c9c..86103ea57 100644 --- a/basics/close-account/quasar/Quasar.toml +++ b/basics/close-account/quasar/Quasar.toml @@ -5,4 +5,18 @@ name = "quasar_close_account" type = "solana" [testing] -framework = "quasarsvm-rust" +language = "rust" + +[testing.rust] +framework = "quasar-svm" + +[testing.rust.test] +program = "cargo" +args = [ + "test", + "tests::", +] + +[clients] +path = "target/client" +languages = ["rust"] diff --git a/basics/counter/quasar/Quasar.toml b/basics/counter/quasar/Quasar.toml index 53191deec..ae2854a56 100644 --- a/basics/counter/quasar/Quasar.toml +++ b/basics/counter/quasar/Quasar.toml @@ -5,4 +5,18 @@ name = "quasar_counter" type = "solana" [testing] -framework = "quasarsvm-rust" +language = "rust" + +[testing.rust] +framework = "quasar-svm" + +[testing.rust.test] +program = "cargo" +args = [ + "test", + "tests::", +] + +[clients] +path = "target/client" +languages = ["rust"] diff --git a/basics/create-account/quasar/Quasar.toml b/basics/create-account/quasar/Quasar.toml index 4df3a1f5f..e42289144 100644 --- a/basics/create-account/quasar/Quasar.toml +++ b/basics/create-account/quasar/Quasar.toml @@ -5,4 +5,18 @@ name = "quasar_create_account" type = "solana" [testing] -framework = "quasarsvm-rust" +language = "rust" + +[testing.rust] +framework = "quasar-svm" + +[testing.rust.test] +program = "cargo" +args = [ + "test", + "tests::", +] + +[clients] +path = "target/client" +languages = ["rust"] diff --git a/basics/cross-program-invocation/quasar/hand/Quasar.toml b/basics/cross-program-invocation/quasar/hand/Quasar.toml index 885b64faf..1ecfdff83 100644 --- a/basics/cross-program-invocation/quasar/hand/Quasar.toml +++ b/basics/cross-program-invocation/quasar/hand/Quasar.toml @@ -5,4 +5,18 @@ name = "quasar_hand" type = "solana" [testing] -framework = "quasarsvm-rust" +language = "rust" + +[testing.rust] +framework = "quasar-svm" + +[testing.rust.test] +program = "cargo" +args = [ + "test", + "tests::", +] + +[clients] +path = "target/client" +languages = ["rust"] diff --git a/basics/cross-program-invocation/quasar/lever/Quasar.toml b/basics/cross-program-invocation/quasar/lever/Quasar.toml index 794df3e59..a3aa238be 100644 --- a/basics/cross-program-invocation/quasar/lever/Quasar.toml +++ b/basics/cross-program-invocation/quasar/lever/Quasar.toml @@ -5,4 +5,18 @@ name = "quasar_lever" type = "solana" [testing] -framework = "quasarsvm-rust" +language = "rust" + +[testing.rust] +framework = "quasar-svm" + +[testing.rust.test] +program = "cargo" +args = [ + "test", + "tests::", +] + +[clients] +path = "target/client" +languages = ["rust"] diff --git a/basics/favorites/quasar/Quasar.toml b/basics/favorites/quasar/Quasar.toml index f0b7c8246..93604701a 100644 --- a/basics/favorites/quasar/Quasar.toml +++ b/basics/favorites/quasar/Quasar.toml @@ -5,4 +5,18 @@ name = "quasar_favorites" type = "solana" [testing] -framework = "quasarsvm-rust" +language = "rust" + +[testing.rust] +framework = "quasar-svm" + +[testing.rust.test] +program = "cargo" +args = [ + "test", + "tests::", +] + +[clients] +path = "target/client" +languages = ["rust"] diff --git a/basics/hello-solana/quasar/Quasar.toml b/basics/hello-solana/quasar/Quasar.toml index 6ccea9dcb..381e0c017 100644 --- a/basics/hello-solana/quasar/Quasar.toml +++ b/basics/hello-solana/quasar/Quasar.toml @@ -5,4 +5,18 @@ name = "quasar_hello_solana" type = "solana" [testing] -framework = "quasarsvm-rust" +language = "rust" + +[testing.rust] +framework = "quasar-svm" + +[testing.rust.test] +program = "cargo" +args = [ + "test", + "tests::", +] + +[clients] +path = "target/client" +languages = ["rust"] diff --git a/basics/pda-rent-payer/quasar/Quasar.toml b/basics/pda-rent-payer/quasar/Quasar.toml index c35b299c8..2afdab05b 100644 --- a/basics/pda-rent-payer/quasar/Quasar.toml +++ b/basics/pda-rent-payer/quasar/Quasar.toml @@ -5,4 +5,18 @@ name = "quasar_pda_rent_payer" type = "solana" [testing] -framework = "quasarsvm-rust" +language = "rust" + +[testing.rust] +framework = "quasar-svm" + +[testing.rust.test] +program = "cargo" +args = [ + "test", + "tests::", +] + +[clients] +path = "target/client" +languages = ["rust"] diff --git a/basics/processing-instructions/quasar/Quasar.toml b/basics/processing-instructions/quasar/Quasar.toml index 092e40881..c11d53cb9 100644 --- a/basics/processing-instructions/quasar/Quasar.toml +++ b/basics/processing-instructions/quasar/Quasar.toml @@ -5,4 +5,18 @@ name = "quasar_processing_instructions" type = "solana" [testing] -framework = "quasarsvm-rust" +language = "rust" + +[testing.rust] +framework = "quasar-svm" + +[testing.rust.test] +program = "cargo" +args = [ + "test", + "tests::", +] + +[clients] +path = "target/client" +languages = ["rust"] diff --git a/basics/program-derived-addresses/quasar/Quasar.toml b/basics/program-derived-addresses/quasar/Quasar.toml index 1f980da5a..c87c6899a 100644 --- a/basics/program-derived-addresses/quasar/Quasar.toml +++ b/basics/program-derived-addresses/quasar/Quasar.toml @@ -5,4 +5,18 @@ name = "quasar_program_derived_addresses" type = "solana" [testing] -framework = "quasarsvm-rust" +language = "rust" + +[testing.rust] +framework = "quasar-svm" + +[testing.rust.test] +program = "cargo" +args = [ + "test", + "tests::", +] + +[clients] +path = "target/client" +languages = ["rust"] diff --git a/basics/realloc/quasar/Quasar.toml b/basics/realloc/quasar/Quasar.toml index 951ed2a80..b1b68fdbb 100644 --- a/basics/realloc/quasar/Quasar.toml +++ b/basics/realloc/quasar/Quasar.toml @@ -5,4 +5,18 @@ name = "quasar_realloc" type = "solana" [testing] -framework = "quasarsvm-rust" +language = "rust" + +[testing.rust] +framework = "quasar-svm" + +[testing.rust.test] +program = "cargo" +args = [ + "test", + "tests::", +] + +[clients] +path = "target/client" +languages = ["rust"] diff --git a/basics/rent/quasar/Quasar.toml b/basics/rent/quasar/Quasar.toml index 0ef6a41d9..ce63074d6 100644 --- a/basics/rent/quasar/Quasar.toml +++ b/basics/rent/quasar/Quasar.toml @@ -5,4 +5,18 @@ name = "quasar_rent" type = "solana" [testing] -framework = "quasarsvm-rust" +language = "rust" + +[testing.rust] +framework = "quasar-svm" + +[testing.rust.test] +program = "cargo" +args = [ + "test", + "tests::", +] + +[clients] +path = "target/client" +languages = ["rust"] diff --git a/basics/repository-layout/quasar/Quasar.toml b/basics/repository-layout/quasar/Quasar.toml index e4d1a4a1d..16496422a 100644 --- a/basics/repository-layout/quasar/Quasar.toml +++ b/basics/repository-layout/quasar/Quasar.toml @@ -5,4 +5,18 @@ name = "quasar_carnival" type = "solana" [testing] -framework = "quasarsvm-rust" +language = "rust" + +[testing.rust] +framework = "quasar-svm" + +[testing.rust.test] +program = "cargo" +args = [ + "test", + "tests::", +] + +[clients] +path = "target/client" +languages = ["rust"] diff --git a/basics/transfer-sol/quasar/Quasar.toml b/basics/transfer-sol/quasar/Quasar.toml index 1d154bb09..8d0bcab7a 100644 --- a/basics/transfer-sol/quasar/Quasar.toml +++ b/basics/transfer-sol/quasar/Quasar.toml @@ -5,4 +5,18 @@ name = "quasar_transfer_sol" type = "solana" [testing] -framework = "quasarsvm-rust" +language = "rust" + +[testing.rust] +framework = "quasar-svm" + +[testing.rust.test] +program = "cargo" +args = [ + "test", + "tests::", +] + +[clients] +path = "target/client" +languages = ["rust"] diff --git a/compression/cnft-burn/quasar/Quasar.toml b/compression/cnft-burn/quasar/Quasar.toml index 016ce0246..09f8d9792 100644 --- a/compression/cnft-burn/quasar/Quasar.toml +++ b/compression/cnft-burn/quasar/Quasar.toml @@ -5,4 +5,18 @@ name = "quasar_cnft_burn" type = "solana" [testing] -framework = "quasarsvm-rust" +language = "rust" + +[testing.rust] +framework = "quasar-svm" + +[testing.rust.test] +program = "cargo" +args = [ + "test", + "tests::", +] + +[clients] +path = "target/client" +languages = ["rust"] diff --git a/compression/cnft-vault/quasar/Quasar.toml b/compression/cnft-vault/quasar/Quasar.toml index 630915afc..504c00825 100644 --- a/compression/cnft-vault/quasar/Quasar.toml +++ b/compression/cnft-vault/quasar/Quasar.toml @@ -5,4 +5,18 @@ name = "quasar_cnft_vault" type = "solana" [testing] -framework = "quasarsvm-rust" +language = "rust" + +[testing.rust] +framework = "quasar-svm" + +[testing.rust.test] +program = "cargo" +args = [ + "test", + "tests::", +] + +[clients] +path = "target/client" +languages = ["rust"] diff --git a/compression/cutils/quasar/Quasar.toml b/compression/cutils/quasar/Quasar.toml index cbffba36a..dbb185800 100644 --- a/compression/cutils/quasar/Quasar.toml +++ b/compression/cutils/quasar/Quasar.toml @@ -5,4 +5,18 @@ name = "quasar_cutils" type = "solana" [testing] -framework = "quasarsvm-rust" +language = "rust" + +[testing.rust] +framework = "quasar-svm" + +[testing.rust.test] +program = "cargo" +args = [ + "test", + "tests::", +] + +[clients] +path = "target/client" +languages = ["rust"] diff --git a/oracles/pyth/quasar/Quasar.toml b/oracles/pyth/quasar/Quasar.toml index b63d035f7..61d6f2eea 100644 --- a/oracles/pyth/quasar/Quasar.toml +++ b/oracles/pyth/quasar/Quasar.toml @@ -5,4 +5,18 @@ name = "quasar_pyth_example" type = "solana" [testing] -framework = "quasarsvm-rust" +language = "rust" + +[testing.rust] +framework = "quasar-svm" + +[testing.rust.test] +program = "cargo" +args = [ + "test", + "tests::", +] + +[clients] +path = "target/client" +languages = ["rust"] diff --git a/tokens/create-token/quasar/Quasar.toml b/tokens/create-token/quasar/Quasar.toml index 4177553a0..f41cddeb0 100644 --- a/tokens/create-token/quasar/Quasar.toml +++ b/tokens/create-token/quasar/Quasar.toml @@ -5,4 +5,18 @@ name = "quasar_create_token" type = "solana" [testing] -framework = "quasarsvm-rust" +language = "rust" + +[testing.rust] +framework = "quasar-svm" + +[testing.rust.test] +program = "cargo" +args = [ + "test", + "tests::", +] + +[clients] +path = "target/client" +languages = ["rust"] diff --git a/tokens/escrow/quasar/Quasar.toml b/tokens/escrow/quasar/Quasar.toml index ae5f22c2b..90a78b55a 100644 --- a/tokens/escrow/quasar/Quasar.toml +++ b/tokens/escrow/quasar/Quasar.toml @@ -5,4 +5,18 @@ name = "quasar_escrow" type = "solana" [testing] -framework = "quasarsvm-rust" +language = "rust" + +[testing.rust] +framework = "quasar-svm" + +[testing.rust.test] +program = "cargo" +args = [ + "test", + "tests::", +] + +[clients] +path = "target/client" +languages = ["rust"] diff --git a/tokens/pda-mint-authority/quasar/Quasar.toml b/tokens/pda-mint-authority/quasar/Quasar.toml index e87f67349..bc309a3c5 100644 --- a/tokens/pda-mint-authority/quasar/Quasar.toml +++ b/tokens/pda-mint-authority/quasar/Quasar.toml @@ -5,4 +5,18 @@ name = "quasar_pda_mint_authority" type = "solana" [testing] -framework = "quasarsvm-rust" +language = "rust" + +[testing.rust] +framework = "quasar-svm" + +[testing.rust.test] +program = "cargo" +args = [ + "test", + "tests::", +] + +[clients] +path = "target/client" +languages = ["rust"] diff --git a/tokens/token-fundraiser/quasar/Quasar.toml b/tokens/token-fundraiser/quasar/Quasar.toml index 52f1edf9a..6f34b8e28 100644 --- a/tokens/token-fundraiser/quasar/Quasar.toml +++ b/tokens/token-fundraiser/quasar/Quasar.toml @@ -5,4 +5,18 @@ name = "quasar_token_fundraiser" type = "solana" [testing] -framework = "quasarsvm-rust" +language = "rust" + +[testing.rust] +framework = "quasar-svm" + +[testing.rust.test] +program = "cargo" +args = [ + "test", + "tests::", +] + +[clients] +path = "target/client" +languages = ["rust"] diff --git a/tokens/transfer-tokens/quasar/Quasar.toml b/tokens/transfer-tokens/quasar/Quasar.toml index 9e6e449f2..7cf3db560 100644 --- a/tokens/transfer-tokens/quasar/Quasar.toml +++ b/tokens/transfer-tokens/quasar/Quasar.toml @@ -5,4 +5,18 @@ name = "quasar_transfer_tokens" type = "solana" [testing] -framework = "quasarsvm-rust" +language = "rust" + +[testing.rust] +framework = "quasar-svm" + +[testing.rust.test] +program = "cargo" +args = [ + "test", + "tests::", +] + +[clients] +path = "target/client" +languages = ["rust"] From 8eff6b6a8d6c671805b01548c0d9db03422a7ad2 Mon Sep 17 00:00:00 2001 From: "Edward (Mike's assistant)" Date: Tue, 28 Apr 2026 21:30:29 +0000 Subject: [PATCH 11/12] fix(transfer-hook): update Quasar programs for new UncheckedAccount API The Quasar master branch (commit 37a531dc) replaced the registry version of `solana-account-view`, changing `AccountView::try_borrow_mut` from `&self` to `&mut self`. It also dropped the `alloc` Cargo feature on `quasar-lang`, renamed `BufCpiCall` to `DynCpiCall`, and rejects all-zero multi-byte instruction discriminators at compile time. This commit migrates all 7 broken transfer-hook examples to compile and run against the current API: - hello-world, counter, transfer-cost, transfer-switch: replaced the invalid `account as *const UncheckedAccount as *mut UncheckedAccount` cast (which tried to cast a value, not a reference) with `&mut account as *mut UncheckedAccount`. - whitelist, account-data-as-seed: converted account structs from `&'info mut UncheckedAccount` field syntax (which the Quasar derive macro no longer accepts) to owned `UncheckedAccount` fields, updated handler signatures from `&Foo` to `&mut Foo`, and applied the same pointer-cast fix. - allow-block-list-token: same `&'info` -> owned conversion across all instruction modules; removed the `alloc` feature dependency from Cargo.toml (upstream `quasar-lang` master no longer exposes it); rewrote two `BufCpiCall` call sites to use `DynCpiCall` (the new variable-length CPI builder); changed the all-zero `init_mint` instruction discriminator to a non-zero value. All 7 projects now pass `quasar build` and `cargo test`. --- .../account-data-as-seed/quasar/src/lib.rs | 42 +++++----- .../allow-block-list-token/quasar/Cargo.toml | 3 +- .../quasar/src/instructions/attach_to_mint.rs | 20 ++--- .../quasar/src/instructions/change_mode.rs | 42 +++++----- .../quasar/src/instructions/init_config.rs | 16 ++-- .../quasar/src/instructions/init_mint.rs | 77 ++++++++----------- .../quasar/src/instructions/init_wallet.rs | 20 ++--- .../quasar/src/instructions/remove_wallet.rs | 12 +-- .../quasar/src/instructions/tx_hook.rs | 16 ++-- .../allow-block-list-token/quasar/src/lib.rs | 2 +- .../transfer-hook/counter/quasar/src/lib.rs | 8 +- .../hello-world/quasar/src/lib.rs | 13 ++-- .../transfer-cost/quasar/src/lib.rs | 9 +-- .../transfer-switch/quasar/src/lib.rs | 13 ++-- .../transfer-hook/whitelist/quasar/src/lib.rs | 49 ++++++------ 15 files changed, 161 insertions(+), 181 deletions(-) diff --git a/tokens/token-extensions/transfer-hook/account-data-as-seed/quasar/src/lib.rs b/tokens/token-extensions/transfer-hook/account-data-as-seed/quasar/src/lib.rs index b91114dff..ea3808630 100644 --- a/tokens/token-extensions/transfer-hook/account-data-as-seed/quasar/src/lib.rs +++ b/tokens/token-extensions/transfer-hook/account-data-as-seed/quasar/src/lib.rs @@ -46,21 +46,21 @@ mod quasar_transfer_hook_account_data_as_seed { // --------------------------------------------------------------------------- #[derive(Accounts)] -pub struct InitializeExtraAccountMetaList<'info> { +pub struct InitializeExtraAccountMetaList { #[account(mut)] - pub payer: &'info Signer, + pub payer: Signer, /// ExtraAccountMetaList PDA: ["extra-account-metas", mint] #[account(mut)] - pub extra_account_meta_list: &'info mut UncheckedAccount, - pub mint: &'info UncheckedAccount, + pub extra_account_meta_list: UncheckedAccount, + pub mint: UncheckedAccount, /// Counter PDA: ["counter", payer_key] #[account(mut)] - pub counter_account: &'info mut UncheckedAccount, - pub system_program: &'info Program, + pub counter_account: UncheckedAccount, + pub system_program: Program, } #[inline(always)] -pub fn handle_initialize_extra_account_meta_list(accounts: &InitializeExtraAccountMetaList) -> Result<(), ProgramError> { +pub fn handle_initialize_extra_account_meta_list(accounts: &mut InitializeExtraAccountMetaList) -> Result<(), ProgramError> { // ExtraAccountMetaList with 1 extra account. // ExtraAccountMeta for a PDA with seeds [Literal("counter"), AccountData(0, 32, 32)]: // The AccountData seed resolves the owner pubkey from account_index=0 @@ -94,8 +94,8 @@ pub fn handle_initialize_extra_account_meta_list(accounts: &InitializeExtraAccou accounts.system_program .create_account( - accounts.payer, - &*accounts.extra_account_meta_list, + &accounts.payer, + &accounts.extra_account_meta_list, lamports, meta_list_size, &crate::ID, @@ -104,7 +104,7 @@ pub fn handle_initialize_extra_account_meta_list(accounts: &InitializeExtraAccou // Write TLV data let view = unsafe { - &mut *(accounts.extra_account_meta_list as *const UncheckedAccount as *mut UncheckedAccount + &mut *(&mut accounts.extra_account_meta_list as *mut UncheckedAccount as *mut AccountView) }; let mut data = view.try_borrow_mut()?; @@ -151,8 +151,8 @@ pub fn handle_initialize_extra_account_meta_list(accounts: &InitializeExtraAccou accounts.system_program .create_account( - accounts.payer, - &*accounts.counter_account, + &accounts.payer, + &accounts.counter_account, counter_lamports, counter_size, &crate::ID, @@ -168,21 +168,21 @@ pub fn handle_initialize_extra_account_meta_list(accounts: &InitializeExtraAccou // --------------------------------------------------------------------------- #[derive(Accounts)] -pub struct TransferHook<'info> { - pub source_token: &'info UncheckedAccount, - pub mint: &'info UncheckedAccount, - pub destination_token: &'info UncheckedAccount, - pub owner: &'info UncheckedAccount, - pub extra_account_meta_list: &'info UncheckedAccount, +pub struct TransferHook { + pub source_token: UncheckedAccount, + pub mint: UncheckedAccount, + pub destination_token: UncheckedAccount, + pub owner: UncheckedAccount, + pub extra_account_meta_list: UncheckedAccount, /// Counter PDA resolved by Token-2022 using account data seeds #[account(mut)] - pub counter_account: &'info mut UncheckedAccount, + pub counter_account: UncheckedAccount, } #[inline(always)] -pub fn handle_transfer_hook(accounts: &TransferHook) -> Result<(), ProgramError> { +pub fn handle_transfer_hook(accounts: &mut TransferHook) -> Result<(), ProgramError> { let view = unsafe { - &mut *(accounts.counter_account as *const UncheckedAccount as *mut UncheckedAccount + &mut *(&mut accounts.counter_account as *mut UncheckedAccount as *mut AccountView) }; let mut data = view.try_borrow_mut()?; diff --git a/tokens/token-extensions/transfer-hook/allow-block-list-token/quasar/Cargo.toml b/tokens/token-extensions/transfer-hook/allow-block-list-token/quasar/Cargo.toml index f00f63f19..742727dc9 100644 --- a/tokens/token-extensions/transfer-hook/allow-block-list-token/quasar/Cargo.toml +++ b/tokens/token-extensions/transfer-hook/allow-block-list-token/quasar/Cargo.toml @@ -13,7 +13,8 @@ check-cfg = ['cfg(target_os, values("solana"))'] crate-type = ["cdylib", "lib"] [features] -alloc = ["quasar-lang/alloc"] +# Removed `alloc` feature — the upstream `quasar-lang` master no longer +# exposes it, and nothing in this crate depends on alloc. client = [] debug = [] diff --git a/tokens/token-extensions/transfer-hook/allow-block-list-token/quasar/src/instructions/attach_to_mint.rs b/tokens/token-extensions/transfer-hook/allow-block-list-token/quasar/src/instructions/attach_to_mint.rs index 7f011f49f..962c9df31 100644 --- a/tokens/token-extensions/transfer-hook/allow-block-list-token/quasar/src/instructions/attach_to_mint.rs +++ b/tokens/token-extensions/transfer-hook/allow-block-list-token/quasar/src/instructions/attach_to_mint.rs @@ -6,19 +6,19 @@ use crate::constants::*; use crate::instructions::init_mint::Token2022; #[derive(Accounts)] -pub struct AttachToMint<'info> { +pub struct AttachToMint { #[account(mut)] - pub payer: &'info Signer, + pub payer: Signer, #[account(mut)] - pub mint: &'info UncheckedAccount, + pub mint: UncheckedAccount, #[account(mut)] - pub extra_metas_account: &'info mut UncheckedAccount, - pub system_program: &'info Program, - pub token_program: &'info Program, + pub extra_metas_account: UncheckedAccount, + pub system_program: Program, + pub token_program: Program, } #[inline(always)] -pub fn handle_attach_to_mint(accounts: &AttachToMint) -> Result<(), ProgramError> { +pub fn handle_attach_to_mint(accounts: &mut AttachToMint) -> Result<(), ProgramError> { let mint_key = accounts.mint.to_account_view().address(); let payer_key = accounts.payer.to_account_view().address(); let token_prog = accounts.token_program.to_account_view().address(); @@ -79,8 +79,8 @@ pub fn handle_attach_to_mint(accounts: &AttachToMint) -> Result<(), ProgramError accounts.system_program .create_account( - accounts.payer, - &*accounts.extra_metas_account, + &accounts.payer, + &accounts.extra_metas_account, lamports, meta_list_size, &crate::ID, @@ -89,7 +89,7 @@ pub fn handle_attach_to_mint(accounts: &AttachToMint) -> Result<(), ProgramError // Write ExtraAccountMeta TLV data let view = unsafe { - &mut *(accounts.extra_metas_account as *const UncheckedAccount as *mut UncheckedAccount + &mut *(&mut accounts.extra_metas_account as *mut UncheckedAccount as *mut AccountView) }; let mut data = view.try_borrow_mut()?; diff --git a/tokens/token-extensions/transfer-hook/allow-block-list-token/quasar/src/instructions/change_mode.rs b/tokens/token-extensions/transfer-hook/allow-block-list-token/quasar/src/instructions/change_mode.rs index 1184189c6..3d6b342ff 100644 --- a/tokens/token-extensions/transfer-hook/allow-block-list-token/quasar/src/instructions/change_mode.rs +++ b/tokens/token-extensions/transfer-hook/allow-block-list-token/quasar/src/instructions/change_mode.rs @@ -1,4 +1,4 @@ -use quasar_lang::cpi::{BufCpiCall, InstructionAccount}; +use quasar_lang::cpi::DynCpiCall; use quasar_lang::prelude::*; use quasar_lang::sysvars::Sysvar; @@ -7,17 +7,17 @@ use crate::instructions::init_mint::Token2022; use crate::state::mode_to_metadata_value; #[derive(Accounts)] -pub struct ChangeMode<'info> { +pub struct ChangeMode { #[account(mut)] - pub authority: &'info Signer, + pub authority: Signer, #[account(mut)] - pub mint: &'info UncheckedAccount, - pub token_program: &'info Program, - pub system_program: &'info Program, + pub mint: UncheckedAccount, + pub token_program: Program, + pub system_program: Program, } #[inline(always)] -pub fn handle_change_mode(accounts: &ChangeMode, mode: u8, threshold: u64) -> Result<(), ProgramError> { +pub fn handle_change_mode(accounts: &mut ChangeMode, mode: u8, threshold: u64) -> Result<(), ProgramError> { let mode_value = mode_to_metadata_value(mode); let token_prog = accounts.token_program.to_account_view().address(); let mint_key = accounts.mint.to_account_view().address(); @@ -52,7 +52,7 @@ pub fn handle_change_mode(accounts: &ChangeMode, mode: u8, threshold: u64) -> Re if min_balance > current_lamports { let diff = min_balance - current_lamports; accounts.system_program - .transfer(accounts.authority, &*accounts.mint, diff) + .transfer(&accounts.authority, &accounts.mint, diff) .invoke()?; } @@ -65,7 +65,7 @@ fn emit_update_field( token_prog: &Address, mint_key: &Address, auth_key: &Address, - ctx: &ChangeMode<'_>, + ctx: &ChangeMode, key: &[u8], value: &[u8], ) -> Result<(), ProgramError> { @@ -86,24 +86,18 @@ fn emit_update_field( buf[pos..pos + value.len()].copy_from_slice(value); pos += value.len(); - BufCpiCall::new( - token_prog, - [ - InstructionAccount::writable(mint_key), - InstructionAccount::readonly_signer(auth_key), - ], - [ - ctx.mint.to_account_view(), - ctx.authority.to_account_view(), - ], - buf, - pos, - ) - .invoke() + let _ = (mint_key, auth_key); + let mut cpi = DynCpiCall::<2, MAX_META_IX>::new(token_prog); + // mint: writable, not signer + cpi.push_account(ctx.mint.to_account_view(), false, true)?; + // authority: signer, not writable + cpi.push_account(ctx.authority.to_account_view(), true, false)?; + cpi.set_data(&buf[..pos])?; + cpi.invoke() } /// Check if the mint's metadata already contains a "threshold" key. -fn has_threshold_in_metadata(ctx: &ChangeMode<'_>) -> Result { +fn has_threshold_in_metadata(ctx: &ChangeMode) -> Result { let mint_view = ctx.mint.to_account_view(); let data = mint_view.try_borrow()?; diff --git a/tokens/token-extensions/transfer-hook/allow-block-list-token/quasar/src/instructions/init_config.rs b/tokens/token-extensions/transfer-hook/allow-block-list-token/quasar/src/instructions/init_config.rs index d5b50b54b..17676b3a3 100644 --- a/tokens/token-extensions/transfer-hook/allow-block-list-token/quasar/src/instructions/init_config.rs +++ b/tokens/token-extensions/transfer-hook/allow-block-list-token/quasar/src/instructions/init_config.rs @@ -6,16 +6,16 @@ use crate::constants::CONFIG_SEED; use crate::state::{write_config, CONFIG_SIZE}; #[derive(Accounts)] -pub struct InitConfig<'info> { +pub struct InitConfig { #[account(mut)] - pub payer: &'info Signer, + pub payer: Signer, #[account(mut)] - pub config: &'info mut UncheckedAccount, - pub system_program: &'info Program, + pub config: UncheckedAccount, + pub system_program: Program, } #[inline(always)] -pub fn handle_init_config(accounts: &InitConfig) -> Result<(), ProgramError> { +pub fn handle_init_config(accounts: &mut InitConfig) -> Result<(), ProgramError> { let (config_pda, bump) = Address::find_program_address(&[CONFIG_SEED], &crate::ID); if accounts.config.to_account_view().address() != &config_pda { @@ -31,8 +31,8 @@ pub fn handle_init_config(accounts: &InitConfig) -> Result<(), ProgramError> { accounts.system_program .create_account( - accounts.payer, - &*accounts.config, + &accounts.payer, + &accounts.config, lamports, CONFIG_SIZE, &crate::ID, @@ -40,7 +40,7 @@ pub fn handle_init_config(accounts: &InitConfig) -> Result<(), ProgramError> { .invoke_signed(&seeds)?; let view = unsafe { - &mut *(accounts.config as *const UncheckedAccount as *mut UncheckedAccount + &mut *(&mut accounts.config as *mut UncheckedAccount as *mut AccountView) }; let mut data = view.try_borrow_mut()?; diff --git a/tokens/token-extensions/transfer-hook/allow-block-list-token/quasar/src/instructions/init_mint.rs b/tokens/token-extensions/transfer-hook/allow-block-list-token/quasar/src/instructions/init_mint.rs index 14aac5596..da948f145 100644 --- a/tokens/token-extensions/transfer-hook/allow-block-list-token/quasar/src/instructions/init_mint.rs +++ b/tokens/token-extensions/transfer-hook/allow-block-list-token/quasar/src/instructions/init_mint.rs @@ -1,4 +1,4 @@ -use quasar_lang::cpi::{BufCpiCall, CpiCall, InstructionAccount, Seed}; +use quasar_lang::cpi::{CpiCall, DynCpiCall, InstructionAccount, Seed}; use quasar_lang::prelude::*; use quasar_lang::sysvars::Sysvar; @@ -15,22 +15,22 @@ impl Id for Token2022 { } #[derive(Accounts)] -pub struct InitMint<'info> { +pub struct InitMint { #[account(mut)] - pub payer: &'info Signer, + pub payer: Signer, /// The mint account (must also be a signer for create_account). #[account(mut)] - pub mint: &'info Signer, + pub mint: Signer, /// ExtraAccountMetaList PDA: ["extra-account-metas", mint] #[account(mut)] - pub extra_metas_account: &'info mut UncheckedAccount, - pub system_program: &'info Program, - pub token_program: &'info Program, + pub extra_metas_account: UncheckedAccount, + pub system_program: Program, + pub token_program: Program, } #[inline(always)] pub fn handle_init_mint( - accounts: &InitMint, decimals: u8, + accounts: &mut InitMint, decimals: u8, freeze_authority: &Address, permanent_delegate: &Address, transfer_hook_authority: &Address, @@ -70,7 +70,8 @@ pub fn handle_init_mint( // Create the mint account owned by Token2022. accounts.system_program - .create_account(accounts.payer, accounts.mint, lamports, mint_size as u64, token_prog) + .create_account( + &accounts.payer, &accounts.mint, lamports, mint_size as u64, token_prog) .invoke()?; // Initialize PermanentDelegate extension: opcode 35 @@ -157,22 +158,15 @@ pub fn handle_init_mint( buf[pos..pos + uri.len()].copy_from_slice(uri); pos += uri.len(); - BufCpiCall::new( - token_prog, - [ - InstructionAccount::writable(mint_key), - InstructionAccount::readonly_signer(payer_key), - InstructionAccount::readonly_signer(payer_key), - ], - [ - accounts.mint.to_account_view(), - accounts.payer.to_account_view(), - accounts.payer.to_account_view(), - ], - buf, - pos, - ) - .invoke()?; + { + let mut cpi = DynCpiCall::<3, MAX_META_IX>::new(token_prog); + cpi.push_account(accounts.mint.to_account_view(), false, true)?; + cpi.push_account(accounts.payer.to_account_view(), true, false)?; + cpi.push_account(accounts.payer.to_account_view(), true, false)?; + cpi.set_data(&buf[..pos])?; + cpi.invoke()?; + } + let _ = mint_key; // TokenMetadataUpdateField for "AB" key: opcode 44, sub-opcode 1 emit_update_field_cpi(accounts, b"AB", mode_value)?; @@ -197,7 +191,7 @@ pub fn handle_init_mint( /// Emit a Token-2022 TokenMetadataUpdateField CPI. /// Opcode 44, sub-opcode 1, followed by Field::Key (discriminator 2, then borsh /// string for key, then borsh string for value). -fn emit_update_field_cpi(ctx: &InitMint<'_>, key: &[u8], value: &[u8]) -> Result<(), ProgramError> { +fn emit_update_field_cpi(ctx: &InitMint, key: &[u8], value: &[u8]) -> Result<(), ProgramError> { let token_prog = ctx.token_program.to_account_view().address(); let mint_key = ctx.mint.to_account_view().address(); let payer_key = ctx.payer.to_account_view().address(); @@ -221,25 +215,17 @@ fn emit_update_field_cpi(ctx: &InitMint<'_>, key: &[u8], value: &[u8]) -> Result buf[pos..pos + value.len()].copy_from_slice(value); pos += value.len(); - BufCpiCall::new( - token_prog, - [ - InstructionAccount::writable(mint_key), - InstructionAccount::readonly_signer(payer_key), - ], - [ - ctx.mint.to_account_view(), - ctx.payer.to_account_view(), - ], - buf, - pos, - ) - .invoke() + let _ = (mint_key, payer_key); + let mut cpi = DynCpiCall::<2, MAX_META_IX>::new(token_prog); + cpi.push_account(ctx.mint.to_account_view(), false, true)?; + cpi.push_account(ctx.payer.to_account_view(), true, false)?; + cpi.set_data(&buf[..pos])?; + cpi.invoke() } /// Top up the mint account if its balance is below the rent minimum for its /// current data size. -fn top_up_rent(ctx: &InitMint<'_>) -> Result<(), ProgramError> { +fn top_up_rent(ctx: &InitMint) -> Result<(), ProgramError> { let mint_view = ctx.mint.to_account_view(); let data_len = mint_view.data_len(); let min_balance = Rent::get()?.try_minimum_balance(data_len)?; @@ -248,7 +234,7 @@ fn top_up_rent(ctx: &InitMint<'_>) -> Result<(), ProgramError> { if min_balance > current_lamports { let diff = min_balance - current_lamports; ctx.system_program - .transfer(ctx.payer, ctx.mint, diff) + .transfer(&ctx.payer, &ctx.mint, diff) .invoke()?; } Ok(()) @@ -256,7 +242,7 @@ fn top_up_rent(ctx: &InitMint<'_>) -> Result<(), ProgramError> { /// Create the ExtraAccountMetaList PDA and populate it with the ABWallet /// extra account meta (PDA seeded by [AB_WALLET_SEED, AccountData(2, 32, 32)]). -fn init_extra_metas(ctx: &InitMint<'_>) -> Result<(), ProgramError> { +fn init_extra_metas(ctx: &mut InitMint) -> Result<(), ProgramError> { let mint_key = ctx.mint.to_account_view().address(); // Meta list with 1 extra account = 51 bytes @@ -279,11 +265,12 @@ fn init_extra_metas(ctx: &InitMint<'_>) -> Result<(), ProgramError> { ]; ctx.system_program - .create_account(ctx.payer, &*ctx.extra_metas_account, lamports, meta_list_size, &crate::ID) + .create_account( + &ctx.payer, &ctx.extra_metas_account, lamports, meta_list_size, &crate::ID) .invoke_signed(&seeds)?; let view = unsafe { - &mut *(ctx.extra_metas_account as *const UncheckedAccount as *mut UncheckedAccount + &mut *(&mut ctx.extra_metas_account as *mut UncheckedAccount as *mut AccountView) }; let mut data = view.try_borrow_mut()?; diff --git a/tokens/token-extensions/transfer-hook/allow-block-list-token/quasar/src/instructions/init_wallet.rs b/tokens/token-extensions/transfer-hook/allow-block-list-token/quasar/src/instructions/init_wallet.rs index c794db52e..e3af0187c 100644 --- a/tokens/token-extensions/transfer-hook/allow-block-list-token/quasar/src/instructions/init_wallet.rs +++ b/tokens/token-extensions/transfer-hook/allow-block-list-token/quasar/src/instructions/init_wallet.rs @@ -7,18 +7,18 @@ use crate::errors; use crate::state::{read_config_authority, write_ab_wallet, AB_WALLET_SIZE, CONFIG_SIZE}; #[derive(Accounts)] -pub struct InitWallet<'info> { +pub struct InitWallet { #[account(mut)] - pub authority: &'info Signer, - pub config: &'info UncheckedAccount, - pub wallet: &'info UncheckedAccount, + pub authority: Signer, + pub config: UncheckedAccount, + pub wallet: UncheckedAccount, #[account(mut)] - pub ab_wallet: &'info mut UncheckedAccount, - pub system_program: &'info Program, + pub ab_wallet: UncheckedAccount, + pub system_program: Program, } #[inline(always)] -pub fn handle_init_wallet(accounts: &InitWallet, allowed: bool) -> Result<(), ProgramError> { +pub fn handle_init_wallet(accounts: &mut InitWallet, allowed: bool) -> Result<(), ProgramError> { // Verify config PDA let (config_pda, _) = Address::find_program_address(&[CONFIG_SEED], &crate::ID); if accounts.config.to_account_view().address() != &config_pda { @@ -57,8 +57,8 @@ pub fn handle_init_wallet(accounts: &InitWallet, allowed: bool) -> Result<(), Pr accounts.system_program .create_account( - accounts.authority, - &*accounts.ab_wallet, + &accounts.authority, + &accounts.ab_wallet, lamports, AB_WALLET_SIZE, &crate::ID, @@ -67,7 +67,7 @@ pub fn handle_init_wallet(accounts: &InitWallet, allowed: bool) -> Result<(), Pr // Write wallet data let view = unsafe { - &mut *(accounts.ab_wallet as *const UncheckedAccount as *mut UncheckedAccount + &mut *(&mut accounts.ab_wallet as *mut UncheckedAccount as *mut AccountView) }; let mut data = view.try_borrow_mut()?; diff --git a/tokens/token-extensions/transfer-hook/allow-block-list-token/quasar/src/instructions/remove_wallet.rs b/tokens/token-extensions/transfer-hook/allow-block-list-token/quasar/src/instructions/remove_wallet.rs index a9f6bfc2b..a9d31f872 100644 --- a/tokens/token-extensions/transfer-hook/allow-block-list-token/quasar/src/instructions/remove_wallet.rs +++ b/tokens/token-extensions/transfer-hook/allow-block-list-token/quasar/src/instructions/remove_wallet.rs @@ -5,16 +5,16 @@ use crate::errors; use crate::state::{read_config_authority, CONFIG_SIZE}; #[derive(Accounts)] -pub struct RemoveWallet<'info> { +pub struct RemoveWallet { #[account(mut)] - pub authority: &'info Signer, - pub config: &'info UncheckedAccount, + pub authority: Signer, + pub config: UncheckedAccount, #[account(mut)] - pub ab_wallet: &'info mut UncheckedAccount, + pub ab_wallet: UncheckedAccount, } #[inline(always)] -pub fn handle_remove_wallet(accounts: &RemoveWallet) -> Result<(), ProgramError> { +pub fn handle_remove_wallet(accounts: &mut RemoveWallet) -> Result<(), ProgramError> { // Verify config PDA let (config_pda, _) = Address::find_program_address(&[CONFIG_SEED], &crate::ID); if accounts.config.to_account_view().address() != &config_pda { @@ -44,7 +44,7 @@ pub fn handle_remove_wallet(accounts: &RemoveWallet) -> Result<(), ProgramError> // Zero the account data let mview = unsafe { - &mut *(accounts.ab_wallet as *const UncheckedAccount as *mut UncheckedAccount + &mut *(&mut accounts.ab_wallet as *mut UncheckedAccount as *mut AccountView) }; let mut data = mview.try_borrow_mut()?; diff --git a/tokens/token-extensions/transfer-hook/allow-block-list-token/quasar/src/instructions/tx_hook.rs b/tokens/token-extensions/transfer-hook/allow-block-list-token/quasar/src/instructions/tx_hook.rs index d55e96ba8..4a5362b45 100644 --- a/tokens/token-extensions/transfer-hook/allow-block-list-token/quasar/src/instructions/tx_hook.rs +++ b/tokens/token-extensions/transfer-hook/allow-block-list-token/quasar/src/instructions/tx_hook.rs @@ -13,17 +13,17 @@ use crate::state::{read_wallet_allowed, MODE_ALLOW, MODE_BLOCK, MODE_MIXED, AB_W /// [4] extra_account_meta_list /// [5] ab_wallet — resolved from extra account metas (PDA for destination owner) #[derive(Accounts)] -pub struct TxHook<'info> { - pub source_token_account: &'info UncheckedAccount, - pub mint: &'info UncheckedAccount, - pub destination_token_account: &'info UncheckedAccount, - pub owner_delegate: &'info UncheckedAccount, - pub meta_list: &'info UncheckedAccount, - pub ab_wallet: &'info UncheckedAccount, +pub struct TxHook { + pub source_token_account: UncheckedAccount, + pub mint: UncheckedAccount, + pub destination_token_account: UncheckedAccount, + pub owner_delegate: UncheckedAccount, + pub meta_list: UncheckedAccount, + pub ab_wallet: UncheckedAccount, } #[inline(always)] -pub fn handle_tx_hook(accounts: &TxHook, amount: u64) -> Result<(), ProgramError> { +pub fn handle_tx_hook(accounts: &mut TxHook, amount: u64) -> Result<(), ProgramError> { let mint_view = accounts.mint.to_account_view(); let mint_data = mint_view.try_borrow()?; diff --git a/tokens/token-extensions/transfer-hook/allow-block-list-token/quasar/src/lib.rs b/tokens/token-extensions/transfer-hook/allow-block-list-token/quasar/src/lib.rs index 74b3df3f1..0ad2df70b 100644 --- a/tokens/token-extensions/transfer-hook/allow-block-list-token/quasar/src/lib.rs +++ b/tokens/token-extensions/transfer-hook/allow-block-list-token/quasar/src/lib.rs @@ -36,7 +36,7 @@ mod quasar_abl_token { /// name: [u8; 32], name_len: u8 /// symbol: [u8; 10], symbol_len: u8 /// uri: [u8; 128], uri_len: u8 - #[instruction(discriminator = [0, 0, 0, 0, 0, 0, 0, 0])] + #[instruction(discriminator = [1, 0, 0, 0, 0, 0, 0, 0])] pub fn init_mint( ctx: Ctx, decimals: u8, diff --git a/tokens/token-extensions/transfer-hook/counter/quasar/src/lib.rs b/tokens/token-extensions/transfer-hook/counter/quasar/src/lib.rs index 5416dfae8..00bc481c5 100644 --- a/tokens/token-extensions/transfer-hook/counter/quasar/src/lib.rs +++ b/tokens/token-extensions/transfer-hook/counter/quasar/src/lib.rs @@ -95,7 +95,7 @@ fn handle_initialize_extra_account_meta_list( .system_program .create_account( &accounts.payer, - &*accounts.extra_account_meta_list, + &accounts.extra_account_meta_list, lamports, meta_list_size, &crate::ID, @@ -104,7 +104,7 @@ fn handle_initialize_extra_account_meta_list( // Write TLV data with the counter PDA as an extra account let view = unsafe { - &mut *(accounts.extra_account_meta_list as *const UncheckedAccount as *mut UncheckedAccount + &mut *(&mut accounts.extra_account_meta_list as *mut UncheckedAccount as *mut AccountView) }; let mut data = view.try_borrow_mut()?; @@ -148,7 +148,7 @@ fn handle_initialize_extra_account_meta_list( .system_program .create_account( &accounts.payer, - &*accounts.counter_account, + &accounts.counter_account, counter_lamports, counter_size, &crate::ID, @@ -184,7 +184,7 @@ pub struct TransferHook { fn handle_transfer_hook(accounts: &mut TransferHook) -> Result<(), ProgramError> { // Read the current counter from the account data let view = unsafe { - &mut *(accounts.counter_account as *const UncheckedAccount as *mut UncheckedAccount + &mut *(&mut accounts.counter_account as *mut UncheckedAccount as *mut AccountView) }; let mut data = view.try_borrow_mut()?; diff --git a/tokens/token-extensions/transfer-hook/hello-world/quasar/src/lib.rs b/tokens/token-extensions/transfer-hook/hello-world/quasar/src/lib.rs index 2f331c132..387afb332 100644 --- a/tokens/token-extensions/transfer-hook/hello-world/quasar/src/lib.rs +++ b/tokens/token-extensions/transfer-hook/hello-world/quasar/src/lib.rs @@ -180,18 +180,19 @@ fn handle_initialize_extra_account_meta_list( .system_program .create_account( &accounts.payer, - &*accounts.extra_account_meta_list, + &accounts.extra_account_meta_list, lamports, meta_list_size, &crate::ID, ) .invoke_signed(&seeds)?; - // Write TLV data into the account. - // SAFETY: Account was just created (16 bytes) and is owned by this program. - // UncheckedAccount is #[repr(transparent)] over AccountView, so the cast is safe. - let view = unsafe { - &mut *(accounts.extra_account_meta_list as *const UncheckedAccount as *mut UncheckedAccount + // Write TLV data into the account. The account was just created + // (16 bytes) and is owned by this program, so the borrow is safe. + // SAFETY: `UncheckedAccount` is `#[repr(transparent)]` over + // `AccountView`, so the reference cast is sound. + let view: &mut AccountView = unsafe { + &mut *(&mut accounts.extra_account_meta_list as *mut UncheckedAccount as *mut AccountView) }; let mut data = view.try_borrow_mut()?; diff --git a/tokens/token-extensions/transfer-hook/transfer-cost/quasar/src/lib.rs b/tokens/token-extensions/transfer-hook/transfer-cost/quasar/src/lib.rs index 0529a2a23..0fd5b9400 100644 --- a/tokens/token-extensions/transfer-hook/transfer-cost/quasar/src/lib.rs +++ b/tokens/token-extensions/transfer-hook/transfer-cost/quasar/src/lib.rs @@ -86,13 +86,12 @@ fn handle_initialize_extra_account_meta_list( ]; accounts .system_program - .create_account(&accounts.payer, &*accounts.extra_account_meta_list, lamports, meta_list_size, &crate::ID) + .create_account(&accounts.payer, &accounts.extra_account_meta_list, lamports, meta_list_size, &crate::ID) .invoke_signed(&seeds)?; // Write TLV data let view = unsafe { - &mut *(accounts.extra_account_meta_list as *const UncheckedAccount - as *mut UncheckedAccount as *mut AccountView) + &mut *(&mut accounts.extra_account_meta_list as *mut UncheckedAccount as *mut AccountView) }; let mut data = view.try_borrow_mut()?; data[0..8].copy_from_slice(&EXECUTE_DISCRIMINATOR); @@ -127,7 +126,7 @@ fn handle_initialize_extra_account_meta_list( ]; accounts .system_program - .create_account(&accounts.payer, &*accounts.counter_account, counter_lamports, counter_size, &crate::ID) + .create_account(&accounts.payer, &accounts.counter_account, counter_lamports, counter_size, &crate::ID) .invoke_signed(&counter_seeds)?; log("Transfer cost hook initialized"); @@ -158,7 +157,7 @@ fn handle_transfer_hook(accounts: &mut TransferHook, amount: u64) -> Result<(), // Increment transfer counter let view = unsafe { - &mut *(accounts.counter_account as *const UncheckedAccount as *mut UncheckedAccount + &mut *(&mut accounts.counter_account as *mut UncheckedAccount as *mut AccountView) }; let mut data = view.try_borrow_mut()?; diff --git a/tokens/token-extensions/transfer-hook/transfer-switch/quasar/src/lib.rs b/tokens/token-extensions/transfer-hook/transfer-switch/quasar/src/lib.rs index 30a1a9db7..58861212c 100644 --- a/tokens/token-extensions/transfer-hook/transfer-switch/quasar/src/lib.rs +++ b/tokens/token-extensions/transfer-hook/transfer-switch/quasar/src/lib.rs @@ -102,13 +102,13 @@ fn handle_configure_admin(accounts: &mut ConfigureAdmin) -> Result<(), ProgramEr ]; accounts .system_program - .create_account(&accounts.admin, &*accounts.admin_config, lamports, size, &crate::ID) + .create_account(&accounts.admin, &accounts.admin_config, lamports, size, &crate::ID) .invoke_signed(&seeds)?; } // Write new admin let mview = unsafe { - &mut *(accounts.admin_config as *const UncheckedAccount as *mut UncheckedAccount + &mut *(&mut accounts.admin_config as *mut UncheckedAccount as *mut AccountView) }; let mut data = mview.try_borrow_mut()?; @@ -159,12 +159,11 @@ fn handle_initialize_extra_account_metas_list( ]; accounts.system_program - .create_account(&accounts.payer, &*accounts.extra_account_metas_list, lamports, meta_list_size, &crate::ID) + .create_account(&accounts.payer, &accounts.extra_account_metas_list, lamports, meta_list_size, &crate::ID) .invoke_signed(&seeds)?; let view = unsafe { - &mut *(accounts.extra_account_metas_list as *const UncheckedAccount - as *mut UncheckedAccount as *mut AccountView) + &mut *(&mut accounts.extra_account_metas_list as *mut UncheckedAccount as *mut AccountView) }; let mut data = view.try_borrow_mut()?; data[0..8].copy_from_slice(&EXECUTE_DISCRIMINATOR); @@ -233,12 +232,12 @@ fn handle_switch(accounts: &mut Switch, on: bool) -> Result<(), ProgramError> { ]; accounts .system_program - .create_account(&accounts.admin, &*accounts.wallet_switch, lamports, size, &crate::ID) + .create_account(&accounts.admin, &accounts.wallet_switch, lamports, size, &crate::ID) .invoke_signed(&switch_seeds)?; } let mview = unsafe { - &mut *(accounts.wallet_switch as *const UncheckedAccount as *mut UncheckedAccount + &mut *(&mut accounts.wallet_switch as *mut UncheckedAccount as *mut AccountView) }; let mut data = mview.try_borrow_mut()?; diff --git a/tokens/token-extensions/transfer-hook/whitelist/quasar/src/lib.rs b/tokens/token-extensions/transfer-hook/whitelist/quasar/src/lib.rs index f58370958..dc3afc0da 100644 --- a/tokens/token-extensions/transfer-hook/whitelist/quasar/src/lib.rs +++ b/tokens/token-extensions/transfer-hook/whitelist/quasar/src/lib.rs @@ -49,20 +49,20 @@ mod quasar_transfer_hook_whitelist { // --------------------------------------------------------------------------- #[derive(Accounts)] -pub struct InitializeExtraAccountMetaList<'info> { +pub struct InitializeExtraAccountMetaList { #[account(mut)] - pub payer: &'info Signer, + pub payer: Signer, #[account(mut)] - pub extra_account_meta_list: &'info mut UncheckedAccount, - pub mint: &'info UncheckedAccount, + pub extra_account_meta_list: UncheckedAccount, + pub mint: UncheckedAccount, /// Whitelist PDA: ["white_list"] #[account(mut)] - pub white_list: &'info mut UncheckedAccount, - pub system_program: &'info Program, + pub white_list: UncheckedAccount, + pub system_program: Program, } #[inline(always)] -pub fn handle_initialize(accounts: &InitializeExtraAccountMetaList) -> Result<(), ProgramError> { +pub fn handle_initialize(accounts: &mut InitializeExtraAccountMetaList) -> Result<(), ProgramError> { // Create ExtraAccountMetaList PDA (1 extra account: whitelist) let meta_list_size: u64 = 51; // 8 + 4 + 4 + 35 let lamports = Rent::get()?.try_minimum_balance(meta_list_size as usize)?; @@ -85,13 +85,12 @@ pub fn handle_initialize(accounts: &InitializeExtraAccountMetaList) -> Result<() ]; accounts.system_program - .create_account(accounts.payer, &*accounts.extra_account_meta_list, lamports, meta_list_size, &crate::ID) + .create_account(&accounts.payer, &accounts.extra_account_meta_list, lamports, meta_list_size, &crate::ID) .invoke_signed(&seeds)?; // Write TLV data let view = unsafe { - &mut *(accounts.extra_account_meta_list as *const UncheckedAccount - as *mut UncheckedAccount as *mut AccountView) + &mut *(&mut accounts.extra_account_meta_list as *mut UncheckedAccount as *mut AccountView) }; let mut data = view.try_borrow_mut()?; data[0..8].copy_from_slice(&EXECUTE_DISCRIMINATOR); @@ -127,12 +126,12 @@ pub fn handle_initialize(accounts: &InitializeExtraAccountMetaList) -> Result<() ]; accounts.system_program - .create_account(accounts.payer, &*accounts.white_list, wl_lamports, wl_size, &crate::ID) + .create_account(&accounts.payer, &accounts.white_list, wl_lamports, wl_size, &crate::ID) .invoke_signed(&wl_seeds)?; // Write authority (payer) to whitelist account let wl_view = unsafe { - &mut *(accounts.white_list as *const UncheckedAccount as *mut UncheckedAccount + &mut *(&mut accounts.white_list as *mut UncheckedAccount as *mut AccountView) }; let mut wl_data = wl_view.try_borrow_mut()?; @@ -148,13 +147,13 @@ pub fn handle_initialize(accounts: &InitializeExtraAccountMetaList) -> Result<() // --------------------------------------------------------------------------- #[derive(Accounts)] -pub struct TransferHook<'info> { - pub source_token: &'info UncheckedAccount, - pub mint: &'info UncheckedAccount, - pub destination_token: &'info UncheckedAccount, - pub owner: &'info UncheckedAccount, - pub extra_account_meta_list: &'info UncheckedAccount, - pub white_list: &'info UncheckedAccount, +pub struct TransferHook { + pub source_token: UncheckedAccount, + pub mint: UncheckedAccount, + pub destination_token: UncheckedAccount, + pub owner: UncheckedAccount, + pub extra_account_meta_list: UncheckedAccount, + pub white_list: UncheckedAccount, } #[inline(always)] @@ -199,17 +198,17 @@ pub fn handle_transfer_hook(accounts: &TransferHook) -> Result<(), ProgramError> // --------------------------------------------------------------------------- #[derive(Accounts)] -pub struct AddToWhitelist<'info> { - pub signer: &'info Signer, - pub new_account: &'info UncheckedAccount, +pub struct AddToWhitelist { + pub signer: Signer, + pub new_account: UncheckedAccount, #[account(mut)] - pub white_list: &'info mut UncheckedAccount, + pub white_list: UncheckedAccount, } #[inline(always)] -pub fn handle_add_to_whitelist(accounts: &AddToWhitelist) -> Result<(), ProgramError> { +pub fn handle_add_to_whitelist(accounts: &mut AddToWhitelist) -> Result<(), ProgramError> { let view = unsafe { - &mut *(accounts.white_list as *const UncheckedAccount as *mut UncheckedAccount + &mut *(&mut accounts.white_list as *mut UncheckedAccount as *mut AccountView) }; let mut data = view.try_borrow_mut()?; From 6c2bef307d1cd820edbbb8da4c1222514d90f314 Mon Sep 17 00:00:00 2001 From: "Edward (Mike's assistant)" Date: Tue, 28 Apr 2026 21:57:38 +0000 Subject: [PATCH 12/12] fix(tests): align test fixtures with Quasar's compact wire format MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Quasar `#[account]` and `#[instruction]` derive macros use a single "compact" wire layout: a header containing all fixed-size fields and length prefixes grouped together, followed by a tail with all dynamic byte payloads grouped together. The previous tests assumed an interleaved field-by-field layout with u32 length prefixes, which never matched the program code that was already migrated to the compact format. Two consequences flowed from that mismatch: 1. Length prefixes for `String` default to a single byte (the second `String` generic argument is the prefix type and its default is `u8`). The tests were using u32 prefixes, so the program rejected the instruction data or wrote wildly wrong account contents. 2. The compact format groups all length prefixes ahead of the data bytes; the tests expected each prefix to immediately precede its own bytes. This commit rewrites the failing test fixtures so they encode and decode the same compact format the programs already produce. The program code itself is not changed — programs are canonical, tests follow. Also removes a stray `rent` sysvar account from the `tokens/pda-mint-authority` `create_mint` test: the program's `CreateMint` accounts struct no longer takes a rent sysvar, so the extra entry was shifting `token_program` and `system_program` to the wrong slots. Projects fixed: basics/account-data basics/close-account basics/favorites basics/realloc basics/rent basics/repository-layout tokens/pda-mint-authority --- basics/account-data/quasar/src/tests.rs | 71 ++++++++----------- basics/close-account/quasar/src/tests.rs | 12 ++-- basics/favorites/quasar/src/tests.rs | 16 +++-- basics/realloc/quasar/src/tests.rs | 28 ++++---- basics/rent/quasar/src/tests.rs | 22 +++--- basics/repository-layout/quasar/src/tests.rs | 58 ++++++++------- tokens/pda-mint-authority/quasar/src/tests.rs | 4 +- 7 files changed, 111 insertions(+), 100 deletions(-) diff --git a/basics/account-data/quasar/src/tests.rs b/basics/account-data/quasar/src/tests.rs index d95a14815..79eac1313 100644 --- a/basics/account-data/quasar/src/tests.rs +++ b/basics/account-data/quasar/src/tests.rs @@ -20,28 +20,28 @@ fn empty(address: Pubkey) -> Account { } } -/// Build create_address_info instruction data manually. +/// Build the create_address_info instruction data using Quasar's compact +/// wire format: a header containing all fixed fields and length prefixes, +/// followed by a tail with all dynamic byte payloads grouped together. /// -/// Wire format (from reading the #[instruction] codegen): -/// [disc: 1 byte] -/// [ZC struct: house_number u8] -/// [name: u32 LE length prefix + bytes] (String → DynKind::Str with U32 prefix) -/// [street: u32 LE length prefix + bytes] -/// [city: u32 LE length prefix + bytes] +/// Layout: +/// header: [disc: u8 = 0][house_number: u8][name_len: u8][street_len: u8][city_len: u8] +/// tail: [name bytes][street bytes][city bytes] +/// +/// `String<50>` defaults to a u8 length prefix because MAX (50) fits in a byte. fn build_create_instruction_data(name: &str, house_number: u8, street: &str, city: &str) -> Vec { - let mut data = vec![0u8]; // discriminator = 0 + let mut data = Vec::with_capacity(5 + name.len() + street.len() + city.len()); - // Fixed ZC struct: house_number + // Header + data.push(0u8); // instruction discriminator data.push(house_number); + data.push(name.len() as u8); + data.push(street.len() as u8); + data.push(city.len() as u8); - // Dynamic String args with u32 length prefix - data.extend_from_slice(&(name.len() as u32).to_le_bytes()); + // Tail data.extend_from_slice(name.as_bytes()); - - data.extend_from_slice(&(street.len() as u32).to_le_bytes()); data.extend_from_slice(street.as_bytes()); - - data.extend_from_slice(&(city.len() as u32).to_le_bytes()); data.extend_from_slice(city.as_bytes()); data @@ -81,34 +81,25 @@ fn test_create_address_info() { // Verify the account data. let account = result.account(&address_info).unwrap(); - // Onchain layout (from #[account] dynamic codegen): - // [disc: 1 byte = 1] - // [ZC header: house_number u8] - // [name: u8 prefix + bytes] (String uses u8 prefix) - // [street: u8 prefix + bytes] - // [city: u8 prefix + bytes] + // Onchain layout for a Quasar `#[account]` with dynamic fields uses the + // compact "header then tail" format. Length prefixes are grouped in the + // header, the actual bytes follow in the tail. + // header: [disc: 1][house_number: u8][name_len: u8][street_len: u8][city_len: u8] + // tail: [name bytes][street bytes][city bytes] + // String<50> defaults to a u8 length prefix because MAX (50) fits in a byte. assert_eq!(account.data[0], 1, "discriminator"); assert_eq!(account.data[1], 42, "house_number"); - - let mut offset = 2; - - // name: u8 prefix + "Alice" - let name_len = account.data[offset] as usize; - offset += 1; + let name_len = account.data[2] as usize; + let street_len = account.data[3] as usize; + let city_len = account.data[4] as usize; assert_eq!(name_len, 5); - assert_eq!(&account.data[offset..offset + name_len], b"Alice"); - offset += name_len; - - // street: u8 prefix + "Main Street" - let street_len = account.data[offset] as usize; - offset += 1; assert_eq!(street_len, 11); - assert_eq!(&account.data[offset..offset + street_len], b"Main Street"); - offset += street_len; - - // city: u8 prefix + "New York" - let city_len = account.data[offset] as usize; - offset += 1; assert_eq!(city_len, 8); - assert_eq!(&account.data[offset..offset + city_len], b"New York"); + + let header_end = 5; + assert_eq!(&account.data[header_end..header_end + name_len], b"Alice"); + let street_start = header_end + name_len; + assert_eq!(&account.data[street_start..street_start + street_len], b"Main Street"); + let city_start = street_start + street_len; + assert_eq!(&account.data[city_start..city_start + city_len], b"New York"); } diff --git a/basics/close-account/quasar/src/tests.rs b/basics/close-account/quasar/src/tests.rs index 370496f46..a1fcc3d5c 100644 --- a/basics/close-account/quasar/src/tests.rs +++ b/basics/close-account/quasar/src/tests.rs @@ -20,11 +20,15 @@ fn empty(address: Pubkey) -> Account { } } -/// Build create_user instruction data. -/// Wire format: [disc=0] [name: u32 prefix + bytes] +/// Build create_user instruction data using Quasar's compact wire format +/// (header then tail). `String<50>` defaults to a u8 length prefix. +/// +/// header: [disc: u8 = 0][name_len: u8] +/// tail: [name bytes] fn build_create_instruction(name: &str) -> Vec { - let mut data = vec![0u8]; // discriminator = 0 - data.extend_from_slice(&(name.len() as u32).to_le_bytes()); + let mut data = Vec::with_capacity(2 + name.len()); + data.push(0u8); // discriminator + data.push(name.len() as u8); data.extend_from_slice(name.as_bytes()); data } diff --git a/basics/favorites/quasar/src/tests.rs b/basics/favorites/quasar/src/tests.rs index ef141e2dd..78d56563e 100644 --- a/basics/favorites/quasar/src/tests.rs +++ b/basics/favorites/quasar/src/tests.rs @@ -20,16 +20,20 @@ fn empty(address: Pubkey) -> Account { } } -/// Build set_favorites instruction data. -/// Wire format: [disc=0] [ZC: number(u64)] [color: u32 prefix + bytes] +/// Build set_favorites instruction data using Quasar's compact wire format +/// (header then tail). `String<50>` defaults to a u8 length prefix. +/// +/// header: [disc: u8 = 0][number: u64 LE][color_len: u8] +/// tail: [color bytes] fn build_set_favorites(number: u64, color: &str) -> Vec { - let mut data = vec![0u8]; // discriminator = 0 + let mut data = Vec::with_capacity(10 + color.len()); - // Fixed ZC args: number (u64, but as Pod it's le bytes) + // Header + data.push(0u8); // discriminator data.extend_from_slice(&number.to_le_bytes()); + data.push(color.len() as u8); - // Dynamic String arg: color with u32 prefix - data.extend_from_slice(&(color.len() as u32).to_le_bytes()); + // Tail data.extend_from_slice(color.as_bytes()); data diff --git a/basics/realloc/quasar/src/tests.rs b/basics/realloc/quasar/src/tests.rs index 8382e85f3..25d35106e 100644 --- a/basics/realloc/quasar/src/tests.rs +++ b/basics/realloc/quasar/src/tests.rs @@ -20,20 +20,25 @@ fn empty(address: Pubkey) -> Account { } } -/// Build initialize instruction data. -/// Wire format: [disc=0] [message: u32 prefix + bytes] +/// Build initialize instruction data using Quasar's compact wire format. +/// `String<1024>` defaults to a u8 length prefix (the second `String` generic +/// argument is the prefix type and its default is `u8`). +/// +/// header: [disc: u8 = 0][message_len: u8] +/// tail: [message bytes] fn build_initialize(message: &str) -> Vec { - let mut data = vec![0u8]; // discriminator = 0 - data.extend_from_slice(&(message.len() as u32).to_le_bytes()); + let mut data = Vec::with_capacity(2 + message.len()); + data.push(0u8); // discriminator + data.push(message.len() as u8); data.extend_from_slice(message.as_bytes()); data } -/// Build update instruction data. -/// Wire format: [disc=1] [message: u32 prefix + bytes] +/// Build update instruction data using the same compact wire format. fn build_update(message: &str) -> Vec { - let mut data = vec![1u8]; // discriminator = 1 - data.extend_from_slice(&(message.len() as u32).to_le_bytes()); + let mut data = Vec::with_capacity(2 + message.len()); + data.push(1u8); // discriminator + data.push(message.len() as u8); data.extend_from_slice(message.as_bytes()); data } @@ -65,14 +70,13 @@ fn test_initialize() { let result = svm.process_instruction(&ix, &[signer(payer), empty(message_account)]); result.assert_success(); - // Verify: disc(1) + message (u32 prefix "Hello, World!") + // Verify: disc(1) + message (u8 prefix + bytes) let account = result.account(&message_account).unwrap(); assert_eq!(account.data[0], 1, "discriminator"); - // Default String uses u32 prefix, max 1024 - let msg_len = u32::from_le_bytes(account.data[1..5].try_into().unwrap()) as usize; + let msg_len = account.data[1] as usize; assert_eq!(msg_len, 13); - assert_eq!(&account.data[5..5 + msg_len], b"Hello, World!"); + assert_eq!(&account.data[2..2 + msg_len], b"Hello, World!"); } #[test] diff --git a/basics/rent/quasar/src/tests.rs b/basics/rent/quasar/src/tests.rs index 7340a67de..f27427e3d 100644 --- a/basics/rent/quasar/src/tests.rs +++ b/basics/rent/quasar/src/tests.rs @@ -20,18 +20,22 @@ fn empty(address: Pubkey) -> Account { } } -/// Build create_system_account instruction data (discriminator = 0). -/// Wire format: [disc=0] [name: String] [address: String] -/// Both String args are dynamic (u32 length prefix + bytes). +/// Build create_system_account instruction data using Quasar's compact +/// wire format (header then tail). `String<50>` defaults to a u8 length +/// prefix (the second `String` generic argument is the prefix type). +/// +/// header: [disc: u8 = 0][name_len: u8][address_len: u8] +/// tail: [name bytes][address bytes] fn build_create_system_account(name: &str, address: &str) -> Vec { - let mut data = vec![0u8]; // discriminator = 0 + let mut data = Vec::with_capacity(3 + name.len() + address.len()); - // Dynamic String: name - data.extend_from_slice(&(name.len() as u32).to_le_bytes()); - data.extend_from_slice(name.as_bytes()); + // Header + data.push(0u8); // discriminator + data.push(name.len() as u8); + data.push(address.len() as u8); - // Dynamic String: address - data.extend_from_slice(&(address.len() as u32).to_le_bytes()); + // Tail + data.extend_from_slice(name.as_bytes()); data.extend_from_slice(address.as_bytes()); data diff --git a/basics/repository-layout/quasar/src/tests.rs b/basics/repository-layout/quasar/src/tests.rs index 95921d411..90a993343 100644 --- a/basics/repository-layout/quasar/src/tests.rs +++ b/basics/repository-layout/quasar/src/tests.rs @@ -10,59 +10,63 @@ fn signer(address: Pubkey) -> Account { quasar_svm::token::create_keyed_system_account(&address, 10_000_000_000) } -/// Build go_on_ride instruction data (discriminator = 0). -/// Wire format: [disc=0] [ZC: height(u32), ticket_count(u32)] [name: String] [ride_name: String] +/// Build go_on_ride instruction data using Quasar's compact wire format +/// (header then tail). `String<50>` defaults to a u8 length prefix. +/// +/// header: [disc: u8 = 0][height: u32 LE][ticket_count: u32 LE][name_len: u8][ride_name_len: u8] +/// tail: [name bytes][ride_name bytes] fn build_go_on_ride(name: &str, height: u32, ticket_count: u32, ride_name: &str) -> Vec { - let mut data = vec![0u8]; // discriminator = 0 + let mut data = Vec::with_capacity(11 + name.len() + ride_name.len()); - // Fixed ZC fields: height, ticket_count + // Header + data.push(0u8); // discriminator data.extend_from_slice(&height.to_le_bytes()); data.extend_from_slice(&ticket_count.to_le_bytes()); + data.push(name.len() as u8); + data.push(ride_name.len() as u8); - // Dynamic String: name - data.extend_from_slice(&(name.len() as u32).to_le_bytes()); + // Tail data.extend_from_slice(name.as_bytes()); - - // Dynamic String: ride_name - data.extend_from_slice(&(ride_name.len() as u32).to_le_bytes()); data.extend_from_slice(ride_name.as_bytes()); data } -/// Build play_game instruction data (discriminator = 1). -/// Wire format: [disc=1] [ZC: ticket_count(u32)] [name: String] [game_name: String] +/// Build play_game instruction data using the same compact wire format. +/// +/// header: [disc: u8 = 1][ticket_count: u32 LE][name_len: u8][game_name_len: u8] +/// tail: [name bytes][game_name bytes] fn build_play_game(name: &str, ticket_count: u32, game_name: &str) -> Vec { - let mut data = vec![1u8]; // discriminator = 1 + let mut data = Vec::with_capacity(7 + name.len() + game_name.len()); - // Fixed ZC: ticket_count + // Header + data.push(1u8); // discriminator data.extend_from_slice(&ticket_count.to_le_bytes()); + data.push(name.len() as u8); + data.push(game_name.len() as u8); - // Dynamic String: name - data.extend_from_slice(&(name.len() as u32).to_le_bytes()); + // Tail data.extend_from_slice(name.as_bytes()); - - // Dynamic String: game_name - data.extend_from_slice(&(game_name.len() as u32).to_le_bytes()); data.extend_from_slice(game_name.as_bytes()); data } -/// Build eat_food instruction data (discriminator = 2). -/// Wire format: [disc=2] [ZC: ticket_count(u32)] [name: String] [food_stand_name: String] +/// Build eat_food instruction data using the same compact wire format. +/// +/// header: [disc: u8 = 2][ticket_count: u32 LE][name_len: u8][food_stand_name_len: u8] +/// tail: [name bytes][food_stand_name bytes] fn build_eat_food(name: &str, ticket_count: u32, food_stand_name: &str) -> Vec { - let mut data = vec![2u8]; // discriminator = 2 + let mut data = Vec::with_capacity(7 + name.len() + food_stand_name.len()); - // Fixed ZC: ticket_count + // Header + data.push(2u8); // discriminator data.extend_from_slice(&ticket_count.to_le_bytes()); + data.push(name.len() as u8); + data.push(food_stand_name.len() as u8); - // Dynamic String: name - data.extend_from_slice(&(name.len() as u32).to_le_bytes()); + // Tail data.extend_from_slice(name.as_bytes()); - - // Dynamic String: food_stand_name - data.extend_from_slice(&(food_stand_name.len() as u32).to_le_bytes()); data.extend_from_slice(food_stand_name.as_bytes()); data diff --git a/tokens/pda-mint-authority/quasar/src/tests.rs b/tokens/pda-mint-authority/quasar/src/tests.rs index 03274a17a..1fa3244b8 100644 --- a/tokens/pda-mint-authority/quasar/src/tests.rs +++ b/tokens/pda-mint-authority/quasar/src/tests.rs @@ -75,16 +75,16 @@ fn test_create_mint() { let (mint_pda, _) = Pubkey::find_program_address(&[b"mint"], &crate::ID); let token_program = quasar_svm::SPL_TOKEN_PROGRAM_ID; let system_program = quasar_svm::system_program::ID; - let rent = quasar_svm::solana_sdk_ids::sysvar::rent::ID; let data = build_create_mint_data(9); + // Account order matches the `CreateMint` Accounts struct: + // payer, mint, token_program, system_program. let instruction = Instruction { program_id: crate::ID, accounts: vec![ solana_instruction::AccountMeta::new(payer.into(), true), solana_instruction::AccountMeta::new(mint_pda.into(), false), - solana_instruction::AccountMeta::new_readonly(rent.into(), false), solana_instruction::AccountMeta::new_readonly(token_program.into(), false), solana_instruction::AccountMeta::new_readonly(system_program.into(), false), ],