Skip to content

Commit d51ad9e

Browse files
authored
Merge pull request #9 from quiknode-labs/quasar-followup-fixes
Quasar followup: address cursor[bot] review and complete API migration in lever
2 parents 7ca968e + 6ec7de1 commit d51ad9e

18 files changed

Lines changed: 89 additions & 41 deletions

File tree

basics/cross-program-invocation/quasar/hand/src/instructions/pull_lever.rs

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,24 +15,27 @@ pub struct PullLever {
1515
pub fn handle_pull_lever(accounts: &PullLever, name: &str) -> Result<(), ProgramError> {
1616
log("Hand is pulling the lever!");
1717

18-
// Build the switch_power instruction data for the lever program:
19-
// [disc=1] [name: u32 len + bytes]
20-
// 128 bytes is enough for any reasonable name.
18+
// Build the switch_power instruction data for the lever program.
19+
//
20+
// Wire format: [discriminator = 1] [name: u8 length prefix + bytes].
21+
//
22+
// The lever's switch_power instruction takes `String<50>`, which Quasar
23+
// serialises with a single-byte length prefix (matching every other
24+
// Quasar program: account-data, close-account, rent, realloc,
25+
// repository-layout). An earlier version of this builder used a u32
26+
// length prefix, which sent a malformed payload on every CPI call.
27+
//
28+
// 128 bytes is enough for any reasonable name (max 50 + 1 + 1 = 52).
2129
let mut data = [0u8; 128];
2230
let name_bytes = name.as_bytes();
23-
let data_len = 1 + 4 + name_bytes.len();
31+
let data_len = 1 + 1 + name_bytes.len();
2432

2533
data[0] = 1;
26-
27-
let len_bytes = (name_bytes.len() as u32).to_le_bytes();
28-
data[1] = len_bytes[0];
29-
data[2] = len_bytes[1];
30-
data[3] = len_bytes[2];
31-
data[4] = len_bytes[3];
34+
data[1] = name_bytes.len() as u8;
3235

3336
let mut i = 0;
3437
while i < name_bytes.len() {
35-
data[5 + i] = name_bytes[i];
38+
data[2 + i] = name_bytes[i];
3639
i += 1;
3740
}
3841

basics/cross-program-invocation/quasar/hand/src/tests.rs

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,17 @@ fn power_account(address: Pubkey, is_on: bool) -> Account {
3030
}
3131

3232
/// Build pull_lever instruction data (discriminator = 0).
33-
/// Wire format: [disc=0] [name: String]
33+
///
34+
/// Wire format: [discriminator = 0] [name: u8 length prefix + bytes].
35+
///
36+
/// The hand's pull_lever instruction takes `String<50>`, which Quasar
37+
/// serialises with a single-byte length prefix. The CPI builder in
38+
/// `pull_lever.rs` re-serialises the same name into the lever's
39+
/// instruction data using the same u8 prefix.
3440
fn build_pull_lever(name: &str) -> Vec<u8> {
35-
let mut data = vec![0u8]; // discriminator = 0
36-
data.extend_from_slice(&(name.len() as u32).to_le_bytes());
41+
let mut data = Vec::with_capacity(2 + name.len());
42+
data.push(0u8); // discriminator = 0
43+
data.push(name.len() as u8);
3744
data.extend_from_slice(name.as_bytes());
3845
data
3946
}
@@ -72,6 +79,14 @@ fn test_pull_lever_turns_on() {
7279
assert!(logs.contains("Hand is pulling"), "hand should log");
7380
assert!(logs.contains("pulling the power switch"), "lever should log");
7481
assert!(logs.contains("now on"), "power should turn on");
82+
// Verifies the CPI wire format: the lever logs the name it
83+
// deserialised. A stale u32 length prefix on either the inbound
84+
// `pull_lever` payload or the CPI to `switch_power` would corrupt
85+
// this (e.g. "\0\0\0Al" instead of "Alice").
86+
assert!(
87+
logs.contains("Alice"),
88+
"name should round-trip through hand → lever CPI; logs: {logs}"
89+
);
7590

7691
let account = result.account(&power_addr).unwrap();
7792
assert_eq!(account.data[1], 1, "power should be on");
@@ -107,6 +122,10 @@ fn test_pull_lever_turns_off() {
107122

108123
let logs = result.logs.join("\n");
109124
assert!(logs.contains("now off"), "power should turn off");
125+
assert!(
126+
logs.contains("Bob"),
127+
"name should round-trip through hand → lever CPI; logs: {logs}"
128+
);
110129

111130
let account = result.account(&power_addr).unwrap();
112131
assert_eq!(account.data[1], 0, "power should be off");
Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
11
use {
2-
crate::state::PowerStatus,
2+
crate::state::{PowerStatus, PowerStatusInner},
33
quasar_lang::prelude::*,
44
};
55

66
/// Accounts for initialising the power status (PDA seeded by "power").
77
#[derive(Accounts)]
8-
pub struct InitializeLever<'info> {
8+
pub struct InitializeLever {
99
#[account(mut)]
10-
pub payer: &'info mut Signer,
11-
#[account(mut, init, payer = payer, seeds = [b"power"], bump)]
12-
pub power: &'info mut Account<PowerStatus>,
13-
pub system_program: &'info Program<System>,
10+
pub payer: Signer,
11+
#[account(mut, init, payer = payer, seeds = PowerStatus::seeds(), bump)]
12+
pub power: Account<PowerStatus>,
13+
pub system_program: Program<System>,
1414
}
1515

1616
#[inline(always)]
1717
pub fn handle_initialize(accounts: &mut InitializeLever) -> Result<(), ProgramError> {
18-
// Power starts off (false).
19-
accounts.power.set_inner(PodBool::from(false));
18+
// Power starts off (false). Counter-style fixed-size set_inner takes only the inner value.
19+
accounts.power.set_inner(PowerStatusInner { is_on: PodBool::from(false) });
2020
Ok(())
2121
}

basics/cross-program-invocation/quasar/lever/src/instructions/switch_power.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,24 @@ use {
55

66
/// Accounts for toggling the power switch.
77
#[derive(Accounts)]
8-
pub struct SwitchPower<'info> {
8+
pub struct SwitchPower {
99
#[account(mut)]
10-
pub power: &'info mut Account<PowerStatus>,
10+
pub power: Account<PowerStatus>,
1111
}
1212

1313
#[inline(always)]
14-
pub fn handle_switch_power(accounts: &mut SwitchPower, _name: &str) -> Result<(), ProgramError> {
14+
pub fn handle_switch_power(accounts: &mut SwitchPower, name: &str) -> Result<(), ProgramError> {
1515
let current: bool = accounts.power.is_on.into();
1616
let new_state = !current;
1717
accounts.power.is_on = PodBool::from(new_state);
1818

1919
// Quasar's log() takes &str — no format! in no_std.
20+
// Logging the name verifies the wire format end-to-end: a stale u32
21+
// length prefix would surface here as a corrupted name (e.g. the
22+
// first three bytes parsed as zeros, leaving "\0\0\0Al" instead of
23+
// "Alice").
2024
log("Someone is pulling the power switch!");
25+
log(name);
2126

2227
if new_state {
2328
log("The power is now on.");

basics/cross-program-invocation/quasar/lever/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ mod quasar_lever {
2222

2323
/// Toggle the power switch. Logs who is pulling the lever.
2424
#[instruction(discriminator = 1)]
25-
pub fn switch_power(ctx: Ctx<SwitchPower>, name: String) -> Result<(), ProgramError> {
25+
pub fn switch_power(ctx: Ctx<SwitchPower>, name: String<50>) -> Result<(), ProgramError> {
2626
instructions::handle_switch_power(&mut ctx.accounts, name)
2727
}
2828
}
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
use quasar_lang::prelude::*;
22

33
/// Onchain power status: a single boolean toggle.
4-
#[account(discriminator = 1)]
4+
/// Derived as a PDA from the seed "power" (single global account).
5+
#[account(discriminator = 1, set_inner)]
6+
#[seeds(b"power")]
57
pub struct PowerStatus {
68
pub is_on: PodBool,
79
}

basics/cross-program-invocation/quasar/lever/src/tests.rs

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,19 @@ fn build_initialize() -> Vec<u8> {
4747
}
4848

4949
/// Build switch_power instruction data (discriminator = 1).
50-
/// Wire format: [disc=1] [name: String]
50+
///
51+
/// Wire format: [discriminator = 1] [name: u8 length prefix + bytes].
52+
///
53+
/// The lever's switch_power instruction takes `String<50>`, which Quasar
54+
/// serialises with a single-byte length prefix (matching every other
55+
/// Quasar program: account-data, close-account, rent, realloc,
56+
/// repository-layout). An earlier version of this builder used a u32
57+
/// length prefix, which produced a malformed payload that happened to
58+
/// pass because the handler ignored the deserialised name.
5159
fn build_switch_power(name: &str) -> Vec<u8> {
52-
let mut data = vec![1u8]; // discriminator = 1
53-
data.extend_from_slice(&(name.len() as u32).to_le_bytes());
60+
let mut data = Vec::with_capacity(2 + name.len());
61+
data.push(1u8); // discriminator = 1
62+
data.push(name.len() as u8);
5463
data.extend_from_slice(name.as_bytes());
5564
data
5665
}
@@ -104,6 +113,12 @@ fn test_switch_power_on() {
104113
let logs = result.logs.join("\n");
105114
assert!(logs.contains("pulling the power switch"), "should log switch");
106115
assert!(logs.contains("now on"), "should say power is on");
116+
// Verifies wire format: a stale u32 length prefix would corrupt the
117+
// deserialised name (e.g. "\0\0\0Al" instead of "Alice").
118+
assert!(
119+
logs.contains("Alice"),
120+
"deserialised name should round-trip exactly; logs: {logs}"
121+
);
107122

108123
let account = result.account(&power_addr).unwrap();
109124
assert_eq!(account.data[1], 1, "power should now be on");
@@ -128,6 +143,10 @@ fn test_switch_power_off() {
128143

129144
let logs = result.logs.join("\n");
130145
assert!(logs.contains("now off"), "should say power is off");
146+
assert!(
147+
logs.contains("Bob"),
148+
"deserialised name should round-trip exactly; logs: {logs}"
149+
);
131150

132151
let account = result.account(&power_addr).unwrap();
133152
assert_eq!(account.data[1], 0, "power should now be off");

tokens/token-extensions/basics/quasar/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,6 @@ quasar-spl = { git = "https://github.com/blueshift-gg/quasar", branch = "master"
2626
solana-instruction = { version = "3.2.0" }
2727

2828
[dev-dependencies]
29-
quasar-svm = { version = "0.1" }
29+
quasar-svm = { git = "https://github.com/blueshift-gg/quasar-svm" }
3030
spl-token-interface = { version = "2.0.0" }
3131
solana-program-pack = { version = "3.1.0" }

tokens/token-extensions/cpi-guard/quasar/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,6 @@ quasar-spl = { git = "https://github.com/blueshift-gg/quasar", branch = "master"
2525
solana-instruction = { version = "3.2.0" }
2626

2727
[dev-dependencies]
28-
quasar-svm = { version = "0.1" }
28+
quasar-svm = { git = "https://github.com/blueshift-gg/quasar-svm" }
2929
spl-token-interface = { version = "2.0.0" }
3030
solana-program-pack = { version = "3.1.0" }

tokens/token-extensions/default-account-state/quasar/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,6 @@ quasar-spl = { git = "https://github.com/blueshift-gg/quasar", branch = "master"
2525
solana-instruction = { version = "3.2.0" }
2626

2727
[dev-dependencies]
28-
quasar-svm = { version = "0.1" }
28+
quasar-svm = { git = "https://github.com/blueshift-gg/quasar-svm" }
2929
spl-token-interface = { version = "2.0.0" }
3030
solana-program-pack = { version = "3.1.0" }

0 commit comments

Comments
 (0)