Skip to content

Commit 7ca968e

Browse files
authored
Merge pull request #8 from jim4067/jimii/quasar-api-migration
update: migrate quasar programs to latest API
2 parents 4d2f313 + 6c2bef3 commit 7ca968e

160 files changed

Lines changed: 1858 additions & 1904 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,4 @@ node_modules/
2222

2323
/target
2424
deploy
25+
.claude

basics/account-data/quasar/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,13 @@ client = []
2222
debug = []
2323

2424
[dependencies]
25-
quasar-lang = "0.0"
25+
quasar-lang = { git = "https://github.com/blueshift-gg/quasar" }
2626
solana-instruction = { version = "3.2.0" }
2727

2828
[dev-dependencies]
2929
# Not using the generated client: it depends on quasar_lang::client::DynBytes
3030
# which isn't in the published crate yet. Tests build instruction data manually.
31-
quasar-svm = { version = "0.1" }
31+
quasar-svm = { git = "https://github.com/blueshift-gg/quasar-svm" }
3232
solana-account = { version = "3.4.0" }
3333
solana-address = { version = "2.2.0", features = ["decode"] }
3434
solana-instruction = { version = "3.2.0", features = ["bincode"] }
Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,31 @@
11
use {
2-
crate::state::AddressInfo,
3-
quasar_lang::prelude::*,
2+
crate::state::{AddressInfo, AddressInfoInner},
3+
quasar_lang::{prelude::*, sysvars::Sysvar},
44
};
55

66
/// Accounts for creating a new address info account.
7-
/// Dynamic accounts use owned `Account<T>` rather than `&'info mut Account<T>` because
8-
/// dynamic types carry cached byte offsets that cannot be represented as a pointer cast.
97
#[derive(Accounts)]
10-
pub struct CreateAddressInfo<'info> {
8+
pub struct CreateAddressInfo {
119
#[account(mut)]
12-
pub payer: &'info mut Signer,
13-
#[account(mut, init, payer = payer, seeds = [b"address_info", payer], bump)]
14-
pub address_info: Account<AddressInfo<'info>>,
15-
pub system_program: &'info Program<System>,
10+
pub payer: Signer,
11+
#[account(mut, init, payer = payer, seeds = AddressInfo::seeds(payer), bump)]
12+
pub address_info: Account<AddressInfo>,
13+
pub system_program: Program<System>,
1614
}
1715

1816
#[inline(always)]
1917
pub fn handle_create_address_info(
20-
accounts: &mut CreateAddressInfo, name: &str,
18+
accounts: &mut CreateAddressInfo,
19+
name: &str,
2120
house_number: u8,
2221
street: &str,
2322
city: &str,
2423
) -> Result<(), ProgramError> {
24+
let rent = Rent::get()?;
2525
accounts.address_info.set_inner(
26-
house_number,
27-
name,
28-
street,
29-
city,
26+
AddressInfoInner { house_number, name, street, city },
3027
accounts.payer.to_account_view(),
31-
None,
28+
rent.lamports_per_byte(),
29+
rent.exemption_threshold_raw(),
3230
)
3331
}

basics/account-data/quasar/src/lib.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,10 @@ mod quasar_account_data {
2525
#[instruction(discriminator = 0)]
2626
pub fn create_address_info(
2727
ctx: Ctx<CreateAddressInfo>,
28-
name: String,
2928
house_number: u8,
30-
street: String,
31-
city: String,
29+
name: String<50>,
30+
street: String<50>,
31+
city: String<50>,
3232
) -> Result<(), ProgramError> {
3333
instructions::handle_create_address_info(&mut ctx.accounts, name, house_number, street, city)
3434
}
Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
use quasar_lang::prelude::*;
22

33
/// Onchain address info account with dynamic string fields.
4-
/// Uses Quasar's `String<P, N>` marker type for variable-length string data.
5-
/// The lifetime `'a` is required because the generated code produces `&'a str` accessors.
6-
///
74
/// Note: Quasar requires all fixed-size fields to precede dynamic (String/Vec) fields.
8-
#[account(discriminator = 1)]
9-
pub struct AddressInfo<'a> {
5+
#[account(discriminator = 1, set_inner)]
6+
#[seeds(b"address_info", payer: Address)]
7+
pub struct AddressInfo {
108
pub house_number: u8,
11-
pub name: String<u8, 50>,
12-
pub street: String<u8, 50>,
13-
pub city: String<u8, 50>,
9+
pub name: String<50>,
10+
pub street: String<50>,
11+
pub city: String<50>,
1412
}

basics/account-data/quasar/src/tests.rs

Lines changed: 31 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -20,28 +20,28 @@ fn empty(address: Pubkey) -> Account {
2020
}
2121
}
2222

23-
/// Build create_address_info instruction data manually.
23+
/// Build the create_address_info instruction data using Quasar's compact
24+
/// wire format: a header containing all fixed fields and length prefixes,
25+
/// followed by a tail with all dynamic byte payloads grouped together.
2426
///
25-
/// Wire format (from reading the #[instruction] codegen):
26-
/// [disc: 1 byte]
27-
/// [ZC struct: house_number u8]
28-
/// [name: u32 LE length prefix + bytes] (String → DynKind::Str with U32 prefix)
29-
/// [street: u32 LE length prefix + bytes]
30-
/// [city: u32 LE length prefix + bytes]
27+
/// Layout:
28+
/// header: [disc: u8 = 0][house_number: u8][name_len: u8][street_len: u8][city_len: u8]
29+
/// tail: [name bytes][street bytes][city bytes]
30+
///
31+
/// `String<50>` defaults to a u8 length prefix because MAX (50) fits in a byte.
3132
fn build_create_instruction_data(name: &str, house_number: u8, street: &str, city: &str) -> Vec<u8> {
32-
let mut data = vec![0u8]; // discriminator = 0
33+
let mut data = Vec::with_capacity(5 + name.len() + street.len() + city.len());
3334

34-
// Fixed ZC struct: house_number
35+
// Header
36+
data.push(0u8); // instruction discriminator
3537
data.push(house_number);
38+
data.push(name.len() as u8);
39+
data.push(street.len() as u8);
40+
data.push(city.len() as u8);
3641

37-
// Dynamic String args with u32 length prefix
38-
data.extend_from_slice(&(name.len() as u32).to_le_bytes());
42+
// Tail
3943
data.extend_from_slice(name.as_bytes());
40-
41-
data.extend_from_slice(&(street.len() as u32).to_le_bytes());
4244
data.extend_from_slice(street.as_bytes());
43-
44-
data.extend_from_slice(&(city.len() as u32).to_le_bytes());
4545
data.extend_from_slice(city.as_bytes());
4646

4747
data
@@ -81,34 +81,25 @@ fn test_create_address_info() {
8181
// Verify the account data.
8282
let account = result.account(&address_info).unwrap();
8383

84-
// Onchain layout (from #[account] dynamic codegen):
85-
// [disc: 1 byte = 1]
86-
// [ZC header: house_number u8]
87-
// [name: u8 prefix + bytes] (String<u8, 50> uses u8 prefix)
88-
// [street: u8 prefix + bytes]
89-
// [city: u8 prefix + bytes]
84+
// Onchain layout for a Quasar `#[account]` with dynamic fields uses the
85+
// compact "header then tail" format. Length prefixes are grouped in the
86+
// header, the actual bytes follow in the tail.
87+
// header: [disc: 1][house_number: u8][name_len: u8][street_len: u8][city_len: u8]
88+
// tail: [name bytes][street bytes][city bytes]
89+
// String<50> defaults to a u8 length prefix because MAX (50) fits in a byte.
9090
assert_eq!(account.data[0], 1, "discriminator");
9191
assert_eq!(account.data[1], 42, "house_number");
92-
93-
let mut offset = 2;
94-
95-
// name: u8 prefix + "Alice"
96-
let name_len = account.data[offset] as usize;
97-
offset += 1;
92+
let name_len = account.data[2] as usize;
93+
let street_len = account.data[3] as usize;
94+
let city_len = account.data[4] as usize;
9895
assert_eq!(name_len, 5);
99-
assert_eq!(&account.data[offset..offset + name_len], b"Alice");
100-
offset += name_len;
101-
102-
// street: u8 prefix + "Main Street"
103-
let street_len = account.data[offset] as usize;
104-
offset += 1;
10596
assert_eq!(street_len, 11);
106-
assert_eq!(&account.data[offset..offset + street_len], b"Main Street");
107-
offset += street_len;
108-
109-
// city: u8 prefix + "New York"
110-
let city_len = account.data[offset] as usize;
111-
offset += 1;
11297
assert_eq!(city_len, 8);
113-
assert_eq!(&account.data[offset..offset + city_len], b"New York");
98+
99+
let header_end = 5;
100+
assert_eq!(&account.data[header_end..header_end + name_len], b"Alice");
101+
let street_start = header_end + name_len;
102+
assert_eq!(&account.data[street_start..street_start + street_len], b"Main Street");
103+
let city_start = street_start + street_len;
104+
assert_eq!(&account.data[city_start..city_start + city_len], b"New York");
114105
}

basics/checking-accounts/quasar/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,12 @@ client = []
2222
debug = []
2323

2424
[dependencies]
25-
quasar-lang = "0.0"
25+
quasar-lang = { git = "https://github.com/blueshift-gg/quasar" }
2626
solana-instruction = { version = "3.2.0" }
2727

2828
[dev-dependencies]
2929
quasar-checking-accounts-client = { path = "target/client/rust/quasar-checking-accounts-client" }
30-
quasar-svm = { version = "0.1" }
30+
quasar-svm = { git = "https://github.com/blueshift-gg/quasar-svm" }
3131
solana-account = { version = "3.4.0" }
3232
solana-address = { version = "2.2.0", features = ["decode"] }
3333
solana-instruction = { version = "3.2.0", features = ["bincode"] }

basics/checking-accounts/quasar/src/instructions/check_accounts.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,21 +8,21 @@ use quasar_lang::prelude::*;
88
/// Note: Anchor's `#[account(owner = id())]` owner constraint is not directly available
99
/// in Quasar. Owner checks can be done manually in the instruction body if needed.
1010
#[derive(Accounts)]
11-
pub struct CheckAccounts<'info> {
11+
pub struct CheckAccounts {
1212
/// Checks that this account signed the transaction.
13-
pub payer: &'info Signer,
13+
pub payer: Signer,
1414
/// No checks performed — the caller is responsible for validation.
1515
#[account(mut)]
16-
pub account_to_create: &'info mut UncheckedAccount,
16+
pub account_to_create: UncheckedAccount,
1717
/// No automatic owner check in Quasar; see note above.
1818
#[account(mut)]
19-
pub account_to_change: &'info mut UncheckedAccount,
19+
pub account_to_change: UncheckedAccount,
2020
/// Checks the account is executable and matches the system program address.
21-
pub system_program: &'info Program<System>,
21+
pub system_program: Program<System>,
2222
}
2323

2424
#[inline(always)]
25-
pub fn handle_check_accounts(accounts: &CheckAccounts) -> Result<(), ProgramError> {
25+
pub fn handle_check_accounts(_accounts: &mut CheckAccounts) -> Result<(), ProgramError> {
2626
// All validation happens declaratively via the account types above.
2727
// If any check fails, the runtime rejects the transaction before this runs.
2828
Ok(())

basics/close-account/quasar/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,11 @@ client = []
2020
debug = []
2121

2222
[dependencies]
23-
quasar-lang = "0.0"
23+
quasar-lang = { git = "https://github.com/blueshift-gg/quasar" }
2424
solana-instruction = { version = "3.2.0" }
2525

2626
[dev-dependencies]
27-
quasar-svm = { version = "0.1" }
27+
quasar-svm = { git = "https://github.com/blueshift-gg/quasar-svm" }
2828
solana-account = { version = "3.4.0" }
2929
solana-address = { version = "2.2.0", features = ["decode"] }
3030
solana-instruction = { version = "3.2.0", features = ["bincode"] }

basics/close-account/quasar/src/instructions/close_user.rs

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,15 @@
1-
use {
2-
crate::state::UserState,
3-
quasar_lang::prelude::*,
4-
};
1+
use {crate::state::UserState, quasar_lang::prelude::*};
52

63
/// Accounts for closing a user account.
74
/// The `close = user` attribute in the Anchor version triggers an automatic epilogue.
85
/// In Quasar, we call `close()` explicitly — it zeros the discriminator, drains lamports
96
/// to the destination, reassigns the owner to the system program, and resizes to 0.
107
#[derive(Accounts)]
11-
pub struct CloseUser<'info> {
8+
pub struct CloseUser {
129
#[account(mut)]
13-
pub user: &'info mut Signer,
10+
pub user: Signer,
1411
#[account(mut)]
15-
pub user_account: Account<UserState<'info>>,
12+
pub user_account: Account<UserState>,
1613
}
1714

1815
#[inline(always)]

0 commit comments

Comments
 (0)