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 a5999e69..9027e978 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 1cb4a5a1..0099c884 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 36253c9a..7f7c6af0 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 ba7a8e14..3c7af75b 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 5ce15311..af16e98e 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 f64b0513..d8dfdd3a 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 43d52319..f7770764 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 1f3c9b00..18b91004 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 c8444f2c..a15db395 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 1f50705f..1559e80f 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 d8838eb0..96e01ea0 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 fa9a9c92..26c4d7b0 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 7963c22b..3e61142e 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 989101f9..32aae2b6 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 8d02d6df..96309da1 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 b265456e..21e73820 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 d97b5558..d49abab1 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 d1e584d9..f980fe3d 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 876e9883..71f8a8fa 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 8f71eeba..775b74ce 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 55ec9838..acc1d000 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 e272cf83..6eda4c7c 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 a50958f7..28ad2933 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 00aa9464..f5691faf 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 e2cf1940..cf87bca6 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 d5bfe44a..9ba96747 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 d6af2e37..c81a1089 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 5d9fa39a..a5819927 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 cf3cdcac..16c22f6a 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 aec62662..16569b19 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 53fcf9ad..724306d0 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 f4189e24..a848eacd 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 aa9eac30..f21bc41e 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 8765f2b6..8943e929 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 925379a5..a27ffce4 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 751b86ff..480403bb 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 af93b6c1..1f579ce7 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 909d1dcc..0f1f0233 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 fbd8f73e..184b1816 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 29c1fda1..44cb197f 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 8925e269..33bac0ce 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 1f44e5fb..4f20ee5e 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 9b9898af..6333e972 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 4ed2077e..e0f18ea9 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 6072e9e1..f251a7c1 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 77ad2b66..7cabac0a 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 3373f864..da21029a 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 7b99dbfa..cfb1c900 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 1f26b493..a4170e0e 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 854b3945..fce2f678 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 5699b68b..50c1ebc2 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 095d3923..7c60b2d1 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 43ecd62d..2b67aae9 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 afb62d25..934ed396 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 b74aafda..81be8a54 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 5aea3d67..55615a10 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 6942be4d..7e66ed75 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 69f8a46a..21b7aaa6 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 5775c4b7..a7a83bcd 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 fb170d22..be63732b 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 2f89cfeb..60f3b06d 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 56014c0e..bbdaeaa2 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 c2fc09a7..e717ae0f 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 cf62058a..1f65adc4 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 ee04326c..07727e2f 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 34232016..29cde50e 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 42b926a3..20df0fcd 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 cdead502..100a91f9 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 948cd657..877636ea 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 6b5d08a4..2afa802a 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 87c23f22..2fd64b23 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 510afb6d..da603962 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 7d0d92b8..12cd4b74 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 9c21b47a..f43961cd 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 bfbb4144..5e26d495 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 0de24d9d..14474f16 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 dd988a64..11024a4e 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 4bf53a1d..9cd7d9c1 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 77d21838..04c6b2d0 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 c82dfe38..bc68d508 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 492a1b82..985e841a 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 04f79cfe..1dc6abaa 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 50921d44..fc6ce08c 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 629c249c..c5716a2f 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 29ef6450..c41c16a7 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 14264bbe..7ca8b92d 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 9101bbc6..8cd06508 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 e40c748a..e52a9885 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 330e74ac..6b6367f5 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 23f8fc5f..7acc6bd5 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 092c3749..6ad8ed2b 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 ba3f8b28..84b257af 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 127797d0..00b968e7 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 d5701f34..bb4c0c07 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 108b7fdc..46facab1 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 f128c56e..7cddd4fa 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 48e1aa20..4d700963 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 85a4e4c2..5ca0803e 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 f5cd0633..2d4d4482 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 5fc2905f..2c7cbc5e 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 b2b66939..5c02b0cd 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 122dde04..9e494fa8 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 b6345037..f8f48885 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 f4fd7a16..74b75066 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 602a1f48..6e9ec58d 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 51f48cea..97744ed2 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 b00779f7..07f0ac8e 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 b5852d5f..b4e7c0af 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 5e93ac70..c6c41957 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 8a887f43..5f279b8e 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 23bf84a3..be7d11b6 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 df053282..f2115aa3 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 c4d507f1..47029902 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 43f07a4a..aa399a91 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 0aff4de5..90775637 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 3b61b6e6..9863f15c 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 11a0f9a4..5e3049ae 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 b91114df..00024482 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 12dd396a..f00f63f1 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 74b3df3f..d3227a07 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 f638977b..9d50e2b7 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 28b66c91..91d2dba2 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 2166fba4..1429a14d 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 27ab8828..98b04b71 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 d8b3bb9c..428b1ad8 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 51ff8c54..143a4f6f 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 f46cc30a..efa04527 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 4e7c8b77..d12b86d4 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 d733505f..3d77ee49 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 f5837095..1d696efb 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 301b33bd..c6b52f93 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 d315a0da..f975bef4 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 a0470b27..7bbc5fa2 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 3f8ee0df..16121105 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 135cf7dc..6e3a4de5 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 7ec183a4..00349cce 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 a90abfa3..9db1768d 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 d4a759f1..8286c3ec 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 2807c04b..ffaf1ad8 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 a25e4acb..252b11d5 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 79d7aa83..f12c9e58 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 330b2572..c783cc72 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 2516ebe0..a5336071 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 8895660d..b94d0ed1 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 e61a66e9..f5fea0b9 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 354a75bb..cd0d3cc5 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 406e3087..8442fdd7 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 406e3087..370b2160 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 0099c884..51fe1208 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 d8dfdd3a..f25b55ae 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 a15db395..dcebbb7b 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 1559e80f..26194325 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 32aae2b6..90b73ae7 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 96309da1..4d81ea68 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 71f8a8fa..88a03be6 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 9ba96747..6debd3ae 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 16569b19..d36abb01 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 f21bc41e..d26b89a1 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 8943e929..191b46f4 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 1f579ce7..ce71aeef 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 44cb197f..7094e64f 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 33bac0ce..04203838 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 f251a7c1..0efba2ce 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 7cabac0a..9e6d6069 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 a4170e0e..e1e4227c 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 7c60b2d1..33960b49 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 f34dde13..48592bb9 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 b0e670b0..66b85c5e 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 84e900c1..fdac0e9c 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 ecb56403..e8569a4c 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 c7682a4b..ded4a7d5 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 5939b697..5759a908 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 81be8a54..cd85cf08 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 55615a10..14dc016e 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 a7a83bcd..246750e6 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 bbdaeaa2..7032f53b 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 e717ae0f..29f69142 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 29cde50e..2cd5bacb 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 20df0fcd..cedc13c6 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 2afa802a..9a150e6a 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 12cd4b74..fdb9fe60 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 5e26d495..c26987f0 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 14474f16..d1e5d1f5 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 11024a4e..78b8e695 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 985e841a..265c1ef6 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 fc6ce08c..a629f389 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 c41c16a7..e0f2032a 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 7ca8b92d..b0a16944 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 8cd06508..0306e517 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 7acc6bd5..e655cd02 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 84b257af..3291ceb1 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 00b968e7..697a658b 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 7cddd4fa..11c329f9 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 5ca0803e..ff733df3 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 2c7cbc5e..7026d788 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 9e494fa8..5388e72f 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 74b75066..0a9fe091 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 97744ed2..ec868491 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 b4e7c0af..4a8b4546 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 5f279b8e..e33a8f47 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 f2115aa3..de5f6e0e 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 aa399a91..d9e8f44d 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 9863f15c..b19e4948 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 91d2dba2..01656552 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 98b04b71..d7c3c73a 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 143a4f6f..225ff62a 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 d12b86d4..87e7b33f 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 f975bef4..5731db71 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 7bbc5fa2..fa5d06c2 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 16121105..8be74ba0 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 6e3a4de5..59172ced 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 ffaf1ad8..2f2cd6be 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 252b11d5..c75e01b4 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 f12c9e58..a8ec95cb 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 c783cc72..92cdc911 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 a5336071..6fc9ecf6 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 cd0d3cc5..3bc0e075 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 7f7c6af0..c3236140 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 f7770764..61df8ebd 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 96e01ea0..54aaa0e9 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 21e73820..2655e7db 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 775b74ce..60e356a0 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 c81a1089..c158b1ba 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 724306d0..467d6b32 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 a27ffce4..de3cb39a 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 0f1f0233..8af46fce 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 4f20ee5e..d9bde888 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 da21029a..e5af82ee 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 fce2f678..4c2740a1 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 2b67aae9..d8b47eef 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 7e66ed75..f0a57bfb 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 be63732b..5c9f9978 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 1f65adc4..95ca7a28 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 100a91f9..eebbf2c0 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 2fd64b23..a27ec3f2 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 9cd7d9c1..78923aef 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 e52a9885..95beeed8 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 bb4c0c07..88d5c7f1 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 00349cce..f69a425d 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 b94d0ed1..bdf86b83 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 af879932..d137957d 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 c3236140..36253c9a 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 61df8ebd..43d52319 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 54aaa0e9..d8838eb0 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 2655e7db..b265456e 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 60e356a0..8f71eeba 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 c158b1ba..d6af2e37 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 467d6b32..53fcf9ad 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 de3cb39a..925379a5 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 8af46fce..909d1dcc 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 d9bde888..1f44e5fb 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 e5af82ee..3373f864 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 4c2740a1..854b3945 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 d8b47eef..43ecd62d 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 f0a57bfb..6942be4d 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 5c9f9978..817f392f 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 95ca7a28..8b1a4f81 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 eebbf2c0..fdfaf20b 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 a27ec3f2..87c23f22 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 fdb9fe60..b6846663 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 78923aef..d779c477 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 265c1ef6..d7923128 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 a629f389..9127dcab 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 95beeed8..e40c748a 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 e655cd02..c3c87f01 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 88d5c7f1..d5701f34 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 11c329f9..5ab5ace0 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 ff733df3..40f3b75b 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 7026d788..28b888d5 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 5388e72f..3f65707c 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 0a9fe091..3e995b9d 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 ec868491..1f10c7da 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 4a8b4546..049ec6ad 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 e33a8f47..9a798666 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 de5f6e0e..f4ed62ad 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 d9e8f44d..c5085ed5 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 b19e4948..82aa7335 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 00024482..b91114df 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 d3227a07..74b3df3f 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 01656552..5416dfae 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 d7c3c73a..2f331c13 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 225ff62a..0529a2a2 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 87e7b33f..30a1a9db 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 1d696efb..f5837095 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 f69a425d..cb00e6c1 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 bdf86b83..21ecd409 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 3bc0e075..3f148405 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 6eda4c7c..e272cf83 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 f5691faf..00aa9464 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 9027e978..993ca2b4 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 7e8c7764..cf360c06 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 51fe1208..1a3503e0 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 36253c9a..cc6bb40c 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 3c7af75b..e54e874c 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 af16e98e..eec54aa7 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 b352a401..3c05d24e 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 18b91004..9da62371 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 86103ea5..0223f4c9 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 dcebbb7b..ff56535f 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 26194325..be2da0b8 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 d8838eb0..64af1b7f 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 26c4d7b0..6e3c7159 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 3e61142e..2ac1aa29 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 ae2854a5..53191dee 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 90b73ae7..0a6fc9b9 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 4d81ea68..c7335c0c 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 d49abab1..988faa3e 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 f980fe3d..2c3e2cae 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 e4228914..4df3a1f5 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 88a03be6..fe00070a 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 acc1d000..f86dd8f0 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 3a8dba27..885b64fa 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 28ad2933..fceab3f0 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 b994e395..794df3e5 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 cf87bca6..3fbf2f1b 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 93604701..f0b7c824 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 a5819927..13673130 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 16c22f6a..fc1e68a9 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 381e0c01..6ccea9dc 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 a848eacd..a0af6710 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 2afdab05..c35b299c 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 480403bb..26cf2e80 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 c11d53cb..092e4088 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 184b1816..1f4e9e28 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 c87c6899..1f980da5 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 e0f18ea9..d61ef5b9 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 b1b68fdb..951ed2a8 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 cfb1c900..84c8f1eb 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 ce63074d..0ef6a41d 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 50c1ebc2..a476a592 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 16496422..e4d1a4a1 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 934ed396..dacb4969 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 8d0bcab7..1d154bb0 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 21b7aaa6..497e22f2 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 09f8d979..016ce024 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 60f3b06d..bd5cb17a 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 504c0082..630915af 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 07727e2f..16379e00 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 dbb18580..cbffba36 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 877636ea..ec4480da 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 61d6f2ee..b63d035f 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 da603962..3eef7659 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 f41cddeb..4177553a 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 f43961cd..1e0e264c 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 90a78b55..ae5f22c2 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 bc68d508..347832b6 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 1dc6abaa..c6d67aca 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 c5716a2f..0ea93385 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 6b6367f5..f4bf7ddd 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 bc309a3c..e87f6734 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 6ad8ed2b..c04390da 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 c6b52f93..b8bee409 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 6f34b8e2..52f1edf9 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 8286c3ec..fc8297ff 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 f5fea0b9..b7c4aace 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 7cf3db56..9e6e449f 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 fe00070a..481cbc8b 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 5713a60a..7b15184c 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 e272cf83..e3e425d9 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 481cbc8b..18f984ca 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 6debd3ae..f1c4e1f0 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 d6af2e37..e716772f 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 13673130..d30d895b 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 d26b89a1..3fdf30b9 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 909d1dcc..653b403d 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 7094e64f..2b4210d0 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 6333e972..d81790b4 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 0efba2ce..ad652200 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 9e6d6069..c0aa1a02 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 3373f864..6b0a512b 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 746d56d9..183de8ec 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 e1e4227c..ccd087a1 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 854b3945..931d0b02 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 43ecd62d..a60775c3 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 246750e6..e5eeab41 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 7032f53b..c1bde7f4 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 8b1a4f81..c066b024 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 2cd5bacb..9f95275b 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 cedc13c6..76184326 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 fdfaf20b..e9b674c6 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 9a150e6a..9c28d901 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 c26987f0..9ab4ec73 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 c3c87f01..6a72cb36 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 8be74ba0..6fa0e4d3 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 59172ced..aadbe0bb 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 9db1768d..9bbe003d 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 cf360c06..7e8c7764 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 3c05d24e..b352a401 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 0223f4c9..86103ea5 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 53191dee..ae2854a5 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 4df3a1f5..e4228914 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 885b64fa..1ecfdff8 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 794df3e5..a3aa238b 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 f0b7c824..93604701 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 6ccea9dc..381e0c01 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 c35b299c..2afdab05 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 092e4088..c11d53cb 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 1f980da5..c87c6899 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 951ed2a8..b1b68fdb 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 0ef6a41d..ce63074d 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 e4d1a4a1..16496422 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 1d154bb0..8d0bcab7 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 016ce024..09f8d979 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 630915af..504c0082 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 cbffba36..dbb18580 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 b63d035f..61d6f2ee 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 4177553a..f41cddeb 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 ae5f22c2..90a78b55 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 e87f6734..bc309a3c 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 52f1edf9..6f34b8e2 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 9e6e449f..7cf3db56 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 b91114df..ea380863 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 f00f63f1..742727dc 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 7f011f49..962c9df3 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 1184189c..3d6b342f 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 d5b50b54..17676b3a 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 14aac559..da948f14 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 c794db52..e3af0187 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 a9f6bfc2..a9d31f87 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 d55e96ba..4a5362b4 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 74b3df3f..0ad2df70 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 5416dfae..00bc481c 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 2f331c13..387afb33 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 0529a2a2..0fd5b940 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 30a1a9db..58861212 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 f5837095..dc3afc0d 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 d95a1481..79eac131 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 370496f4..a1fcc3d5 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 ef141e2d..78d56563 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 8382e85f..25d35106 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 7340a67d..f27427e3 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 95921d41..90a99334 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 03274a17..1fa3244b 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), ],