From 37bc808455690a8210d491eebe02c21cbb1d5205 Mon Sep 17 00:00:00 2001 From: "Edward (Mike's bot)" Date: Wed, 29 Apr 2026 00:42:18 +0000 Subject: [PATCH 1/3] fix(token-extensions): migrate quasar-svm to git dep WHAT: Update all 11 token-extensions/*/quasar/Cargo.toml to fetch quasar-svm from the blueshift-gg/quasar-svm git repo, matching every other quasar crate in the repository. WHY: cursor[bot] flagged that token-extensions still pinned quasar-svm = { version = "0.1" } from crates.io while quasar-lang and quasar-spl had been migrated to git deps. Mixing crates.io and git sources for a coordinated trio risks version conflicts and test runtime mismatches. Verified by building + testing basics, cpi-guard, and transfer-fee crates locally - all pass. Addresses cursor[bot] comment #6 on PR #8 (discussion_r3157138724). --- tokens/token-extensions/basics/quasar/Cargo.toml | 2 +- tokens/token-extensions/cpi-guard/quasar/Cargo.toml | 2 +- tokens/token-extensions/default-account-state/quasar/Cargo.toml | 2 +- tokens/token-extensions/group/quasar/Cargo.toml | 2 +- tokens/token-extensions/immutable-owner/quasar/Cargo.toml | 2 +- tokens/token-extensions/interest-bearing/quasar/Cargo.toml | 2 +- tokens/token-extensions/memo-transfer/quasar/Cargo.toml | 2 +- tokens/token-extensions/mint-close-authority/quasar/Cargo.toml | 2 +- tokens/token-extensions/non-transferable/quasar/Cargo.toml | 2 +- tokens/token-extensions/permanent-delegate/quasar/Cargo.toml | 2 +- tokens/token-extensions/transfer-fee/quasar/Cargo.toml | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/tokens/token-extensions/basics/quasar/Cargo.toml b/tokens/token-extensions/basics/quasar/Cargo.toml index 46facab14..983c74fc5 100644 --- a/tokens/token-extensions/basics/quasar/Cargo.toml +++ b/tokens/token-extensions/basics/quasar/Cargo.toml @@ -26,6 +26,6 @@ quasar-spl = { git = "https://github.com/blueshift-gg/quasar", branch = "master" 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-extensions/cpi-guard/quasar/Cargo.toml b/tokens/token-extensions/cpi-guard/quasar/Cargo.toml index 4d7009639..9c158e5ad 100644 --- a/tokens/token-extensions/cpi-guard/quasar/Cargo.toml +++ b/tokens/token-extensions/cpi-guard/quasar/Cargo.toml @@ -25,6 +25,6 @@ quasar-spl = { git = "https://github.com/blueshift-gg/quasar", branch = "master" 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-extensions/default-account-state/quasar/Cargo.toml b/tokens/token-extensions/default-account-state/quasar/Cargo.toml index 2d4d4482b..91fe73004 100644 --- a/tokens/token-extensions/default-account-state/quasar/Cargo.toml +++ b/tokens/token-extensions/default-account-state/quasar/Cargo.toml @@ -25,6 +25,6 @@ quasar-spl = { git = "https://github.com/blueshift-gg/quasar", branch = "master" 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-extensions/group/quasar/Cargo.toml b/tokens/token-extensions/group/quasar/Cargo.toml index 5c02b0cd8..45a9244ef 100644 --- a/tokens/token-extensions/group/quasar/Cargo.toml +++ b/tokens/token-extensions/group/quasar/Cargo.toml @@ -25,6 +25,6 @@ quasar-spl = { git = "https://github.com/blueshift-gg/quasar", branch = "master" 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-extensions/immutable-owner/quasar/Cargo.toml b/tokens/token-extensions/immutable-owner/quasar/Cargo.toml index f8f488857..3ec07d69e 100644 --- a/tokens/token-extensions/immutable-owner/quasar/Cargo.toml +++ b/tokens/token-extensions/immutable-owner/quasar/Cargo.toml @@ -25,6 +25,6 @@ quasar-spl = { git = "https://github.com/blueshift-gg/quasar", branch = "master" 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-extensions/interest-bearing/quasar/Cargo.toml b/tokens/token-extensions/interest-bearing/quasar/Cargo.toml index 6e9ec58dd..c918193d5 100644 --- a/tokens/token-extensions/interest-bearing/quasar/Cargo.toml +++ b/tokens/token-extensions/interest-bearing/quasar/Cargo.toml @@ -25,6 +25,6 @@ quasar-spl = { git = "https://github.com/blueshift-gg/quasar", branch = "master" 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-extensions/memo-transfer/quasar/Cargo.toml b/tokens/token-extensions/memo-transfer/quasar/Cargo.toml index 07f0ac8e7..2c8abc21f 100644 --- a/tokens/token-extensions/memo-transfer/quasar/Cargo.toml +++ b/tokens/token-extensions/memo-transfer/quasar/Cargo.toml @@ -25,6 +25,6 @@ quasar-spl = { git = "https://github.com/blueshift-gg/quasar", branch = "master" 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-extensions/mint-close-authority/quasar/Cargo.toml b/tokens/token-extensions/mint-close-authority/quasar/Cargo.toml index c6c41957e..5f12408a7 100644 --- a/tokens/token-extensions/mint-close-authority/quasar/Cargo.toml +++ b/tokens/token-extensions/mint-close-authority/quasar/Cargo.toml @@ -25,6 +25,6 @@ quasar-spl = { git = "https://github.com/blueshift-gg/quasar", branch = "master" 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-extensions/non-transferable/quasar/Cargo.toml b/tokens/token-extensions/non-transferable/quasar/Cargo.toml index be7d11b69..b72ebeff5 100644 --- a/tokens/token-extensions/non-transferable/quasar/Cargo.toml +++ b/tokens/token-extensions/non-transferable/quasar/Cargo.toml @@ -25,6 +25,6 @@ quasar-spl = { git = "https://github.com/blueshift-gg/quasar", branch = "master" 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-extensions/permanent-delegate/quasar/Cargo.toml b/tokens/token-extensions/permanent-delegate/quasar/Cargo.toml index 470299022..875193ec7 100644 --- a/tokens/token-extensions/permanent-delegate/quasar/Cargo.toml +++ b/tokens/token-extensions/permanent-delegate/quasar/Cargo.toml @@ -25,6 +25,6 @@ quasar-spl = { git = "https://github.com/blueshift-gg/quasar", branch = "master" 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-extensions/transfer-fee/quasar/Cargo.toml b/tokens/token-extensions/transfer-fee/quasar/Cargo.toml index 907756375..cfa93d5a9 100644 --- a/tokens/token-extensions/transfer-fee/quasar/Cargo.toml +++ b/tokens/token-extensions/transfer-fee/quasar/Cargo.toml @@ -25,6 +25,6 @@ quasar-spl = { git = "https://github.com/blueshift-gg/quasar", branch = "master" 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" } From ddea756d9b45f6f8255d399bb74c745f3fb8cf90 Mon Sep 17 00:00:00 2001 From: "Edward (Mike's bot)" Date: Wed, 29 Apr 2026 00:49:17 +0000 Subject: [PATCH 2/3] fix(cpi/lever): migrate to current Quasar API WHAT: Update the lever program (basics/cross-program-invocation/quasar/lever) to compile against the current quasar-lang and quasar-spl APIs: - Drop `<'info>` lifetime parameters from account context structs - Replace `&'info mut Signer` / `&'info mut Account<...>` / `&'info Program<...>` reference fields with their owned counterparts (`Signer`, `Account<...>`, `Program<...>`) - Move PDA seed declaration onto the state struct via `#[seeds(b"power")]` and reference it as `seeds = PowerStatus::seeds()` in the accounts derive - Mark `PowerStatus` with `set_inner` and call `set_inner(PowerStatusInner { .. })` from the initialize handler (matches the counter pattern for fixed-size Pod accounts) - Bind the `name` instruction arg to `String<50>` (Quasar's PodString needs a capacity generic; plain `String` no longer compiles) WHY: cursor[bot] flagged the lever crate as still using the pre-migration Quasar API even after PR #8 updated lever's Cargo.toml. The crate is excluded from CI via .ghaignore (the workflow expects a root Quasar.toml per project but cross-program-invocation/quasar uses hand/ + lever/ subdirectories), so the breakage was masked. Verified locally: `quasar build && cargo test --release` passes for both lever (4 tests) and hand (3 tests). Addresses cursor[bot] comment #8 on PR #8 (discussion_r3157509603). --- .../quasar/lever/src/instructions/initialize.rs | 16 ++++++++-------- .../lever/src/instructions/switch_power.rs | 4 ++-- .../quasar/lever/src/lib.rs | 2 +- .../quasar/lever/src/state.rs | 4 +++- 4 files changed, 14 insertions(+), 12 deletions(-) diff --git a/basics/cross-program-invocation/quasar/lever/src/instructions/initialize.rs b/basics/cross-program-invocation/quasar/lever/src/instructions/initialize.rs index 527360342..832a8732b 100644 --- a/basics/cross-program-invocation/quasar/lever/src/instructions/initialize.rs +++ b/basics/cross-program-invocation/quasar/lever/src/instructions/initialize.rs @@ -1,21 +1,21 @@ use { - crate::state::PowerStatus, + crate::state::{PowerStatus, PowerStatusInner}, quasar_lang::prelude::*, }; /// Accounts for initialising the power status (PDA seeded by "power"). #[derive(Accounts)] -pub struct InitializeLever<'info> { +pub struct InitializeLever { #[account(mut)] - pub payer: &'info mut Signer, - #[account(mut, init, payer = payer, seeds = [b"power"], bump)] - pub power: &'info mut Account, - pub system_program: &'info Program, + pub payer: Signer, + #[account(mut, init, payer = payer, seeds = PowerStatus::seeds(), bump)] + pub power: Account, + pub system_program: Program, } #[inline(always)] pub fn handle_initialize(accounts: &mut InitializeLever) -> Result<(), ProgramError> { - // Power starts off (false). - accounts.power.set_inner(PodBool::from(false)); + // Power starts off (false). Counter-style fixed-size set_inner takes only the inner value. + accounts.power.set_inner(PowerStatusInner { is_on: PodBool::from(false) }); Ok(()) } diff --git a/basics/cross-program-invocation/quasar/lever/src/instructions/switch_power.rs b/basics/cross-program-invocation/quasar/lever/src/instructions/switch_power.rs index d5f376c16..513506e8c 100644 --- a/basics/cross-program-invocation/quasar/lever/src/instructions/switch_power.rs +++ b/basics/cross-program-invocation/quasar/lever/src/instructions/switch_power.rs @@ -5,9 +5,9 @@ use { /// Accounts for toggling the power switch. #[derive(Accounts)] -pub struct SwitchPower<'info> { +pub struct SwitchPower { #[account(mut)] - pub power: &'info mut Account, + pub power: Account, } #[inline(always)] diff --git a/basics/cross-program-invocation/quasar/lever/src/lib.rs b/basics/cross-program-invocation/quasar/lever/src/lib.rs index 00aa9464e..1922feca8 100644 --- a/basics/cross-program-invocation/quasar/lever/src/lib.rs +++ b/basics/cross-program-invocation/quasar/lever/src/lib.rs @@ -22,7 +22,7 @@ mod quasar_lever { /// Toggle the power switch. Logs who is pulling the lever. #[instruction(discriminator = 1)] - pub fn switch_power(ctx: Ctx, name: String) -> Result<(), ProgramError> { + pub fn switch_power(ctx: Ctx, name: String<50>) -> Result<(), ProgramError> { instructions::handle_switch_power(&mut ctx.accounts, name) } } diff --git a/basics/cross-program-invocation/quasar/lever/src/state.rs b/basics/cross-program-invocation/quasar/lever/src/state.rs index 4b4cc2abe..84610a4f3 100644 --- a/basics/cross-program-invocation/quasar/lever/src/state.rs +++ b/basics/cross-program-invocation/quasar/lever/src/state.rs @@ -1,7 +1,9 @@ use quasar_lang::prelude::*; /// Onchain power status: a single boolean toggle. -#[account(discriminator = 1)] +/// Derived as a PDA from the seed "power" (single global account). +#[account(discriminator = 1, set_inner)] +#[seeds(b"power")] pub struct PowerStatus { pub is_on: PodBool, } From 6ec7de121bfcfc9084fc29d0b4952747432a28db Mon Sep 17 00:00:00 2001 From: Mike MacCana Date: Wed, 29 Apr 2026 01:31:45 +0000 Subject: [PATCH 3/3] fix(cpi/hand): use u8 length prefix for String<50> in CPI wire format MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The lever's switch_power instruction takes `String<50>`, which Quasar serialises with a single-byte length prefix (matching every other Quasar program: account-data, close-account, rent, realloc, repository-layout). The hand program's pull_lever CPI builder was manually constructing instruction data with a u32 (4-byte) length prefix, so every hand→lever CPI call sent a malformed payload. The breakage was masked because the lever's handler ignored the deserialised name (`_name`), but the value itself was corrupted — e.g. "\\0\\0\\0Al" instead of "Alice". The lever's own tests.rs had the same stale u32 prefix in its instruction-data builder, and hand's tests.rs had it for inbound pull_lever data too. Both now use `data.push(name.len() as u8)`, matching the canonical pattern used across the Quasar example suite. To keep this from regressing again, the lever now logs the deserialised name and both test suites assert that the round-tripped name appears in program logs verbatim ("Alice" / "Bob"). A stale length prefix on either leg of the CPI would surface immediately as a corrupted log line. Files changed: - hand/src/instructions/pull_lever.rs (CPI builder: u32 → u8) - hand/src/tests.rs (inbound builder: u32 → u8, name assert) - lever/src/instructions/switch_power.rs (log name) - lever/src/tests.rs (test builder: u32 → u8, name assert) --- .../hand/src/instructions/pull_lever.rs | 25 +++++++++++-------- .../quasar/hand/src/tests.rs | 25 ++++++++++++++++--- .../lever/src/instructions/switch_power.rs | 7 +++++- .../quasar/lever/src/tests.rs | 25 ++++++++++++++++--- 4 files changed, 64 insertions(+), 18 deletions(-) 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 7b15184c7..f602af292 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 @@ -15,24 +15,27 @@ pub struct PullLever { pub fn handle_pull_lever(accounts: &PullLever, name: &str) -> Result<(), ProgramError> { log("Hand is pulling the lever!"); - // Build the switch_power instruction data for the lever program: - // [disc=1] [name: u32 len + bytes] - // 128 bytes is enough for any reasonable name. + // Build the switch_power instruction data for the lever program. + // + // Wire format: [discriminator = 1] [name: u8 length prefix + bytes]. + // + // The lever's switch_power instruction takes `String<50>`, which Quasar + // serialises with a single-byte length prefix (matching every other + // Quasar program: account-data, close-account, rent, realloc, + // repository-layout). An earlier version of this builder used a u32 + // length prefix, which sent a malformed payload on every CPI call. + // + // 128 bytes is enough for any reasonable name (max 50 + 1 + 1 = 52). let mut data = [0u8; 128]; let name_bytes = name.as_bytes(); - let data_len = 1 + 4 + name_bytes.len(); + let data_len = 1 + 1 + name_bytes.len(); data[0] = 1; - - 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]; + data[1] = name_bytes.len() as u8; let mut i = 0; while i < name_bytes.len() { - data[5 + i] = name_bytes[i]; + data[2 + i] = name_bytes[i]; i += 1; } diff --git a/basics/cross-program-invocation/quasar/hand/src/tests.rs b/basics/cross-program-invocation/quasar/hand/src/tests.rs index 8ec1c3232..05ede5562 100644 --- a/basics/cross-program-invocation/quasar/hand/src/tests.rs +++ b/basics/cross-program-invocation/quasar/hand/src/tests.rs @@ -30,10 +30,17 @@ fn power_account(address: Pubkey, is_on: bool) -> Account { } /// Build pull_lever instruction data (discriminator = 0). -/// Wire format: [disc=0] [name: String] +/// +/// Wire format: [discriminator = 0] [name: u8 length prefix + bytes]. +/// +/// The hand's pull_lever instruction takes `String<50>`, which Quasar +/// serialises with a single-byte length prefix. The CPI builder in +/// `pull_lever.rs` re-serialises the same name into the lever's +/// instruction data using the same u8 prefix. fn build_pull_lever(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 = 0 + data.push(name.len() as u8); data.extend_from_slice(name.as_bytes()); data } @@ -72,6 +79,14 @@ fn test_pull_lever_turns_on() { assert!(logs.contains("Hand is pulling"), "hand should log"); assert!(logs.contains("pulling the power switch"), "lever should log"); assert!(logs.contains("now on"), "power should turn on"); + // Verifies the CPI wire format: the lever logs the name it + // deserialised. A stale u32 length prefix on either the inbound + // `pull_lever` payload or the CPI to `switch_power` would corrupt + // this (e.g. "\0\0\0Al" instead of "Alice"). + assert!( + logs.contains("Alice"), + "name should round-trip through hand → lever CPI; logs: {logs}" + ); let account = result.account(&power_addr).unwrap(); assert_eq!(account.data[1], 1, "power should be on"); @@ -107,6 +122,10 @@ fn test_pull_lever_turns_off() { let logs = result.logs.join("\n"); assert!(logs.contains("now off"), "power should turn off"); + assert!( + logs.contains("Bob"), + "name should round-trip through hand → lever CPI; logs: {logs}" + ); let account = result.account(&power_addr).unwrap(); assert_eq!(account.data[1], 0, "power should be off"); diff --git a/basics/cross-program-invocation/quasar/lever/src/instructions/switch_power.rs b/basics/cross-program-invocation/quasar/lever/src/instructions/switch_power.rs index 513506e8c..52ac4401e 100644 --- a/basics/cross-program-invocation/quasar/lever/src/instructions/switch_power.rs +++ b/basics/cross-program-invocation/quasar/lever/src/instructions/switch_power.rs @@ -11,13 +11,18 @@ pub struct SwitchPower { } #[inline(always)] -pub fn handle_switch_power(accounts: &mut SwitchPower, _name: &str) -> Result<(), ProgramError> { +pub fn handle_switch_power(accounts: &mut SwitchPower, name: &str) -> Result<(), ProgramError> { let current: bool = accounts.power.is_on.into(); let new_state = !current; accounts.power.is_on = PodBool::from(new_state); // Quasar's log() takes &str — no format! in no_std. + // Logging the name verifies the wire format end-to-end: a stale u32 + // length prefix would surface here as a corrupted name (e.g. the + // first three bytes parsed as zeros, leaving "\0\0\0Al" instead of + // "Alice"). log("Someone is pulling the power switch!"); + log(name); if new_state { log("The power is now on."); diff --git a/basics/cross-program-invocation/quasar/lever/src/tests.rs b/basics/cross-program-invocation/quasar/lever/src/tests.rs index b6005e4a5..ed2c32e12 100644 --- a/basics/cross-program-invocation/quasar/lever/src/tests.rs +++ b/basics/cross-program-invocation/quasar/lever/src/tests.rs @@ -47,10 +47,19 @@ fn build_initialize() -> Vec { } /// Build switch_power instruction data (discriminator = 1). -/// Wire format: [disc=1] [name: String] +/// +/// Wire format: [discriminator = 1] [name: u8 length prefix + bytes]. +/// +/// The lever's switch_power instruction takes `String<50>`, which Quasar +/// serialises with a single-byte length prefix (matching every other +/// Quasar program: account-data, close-account, rent, realloc, +/// repository-layout). An earlier version of this builder used a u32 +/// length prefix, which produced a malformed payload that happened to +/// pass because the handler ignored the deserialised name. fn build_switch_power(name: &str) -> Vec { - let mut data = vec![1u8]; // discriminator = 1 - data.extend_from_slice(&(name.len() as u32).to_le_bytes()); + let mut data = Vec::with_capacity(2 + name.len()); + data.push(1u8); // discriminator = 1 + data.push(name.len() as u8); data.extend_from_slice(name.as_bytes()); data } @@ -104,6 +113,12 @@ fn test_switch_power_on() { let logs = result.logs.join("\n"); assert!(logs.contains("pulling the power switch"), "should log switch"); assert!(logs.contains("now on"), "should say power is on"); + // Verifies wire format: a stale u32 length prefix would corrupt the + // deserialised name (e.g. "\0\0\0Al" instead of "Alice"). + assert!( + logs.contains("Alice"), + "deserialised name should round-trip exactly; logs: {logs}" + ); let account = result.account(&power_addr).unwrap(); assert_eq!(account.data[1], 1, "power should now be on"); @@ -128,6 +143,10 @@ fn test_switch_power_off() { let logs = result.logs.join("\n"); assert!(logs.contains("now off"), "should say power is off"); + assert!( + logs.contains("Bob"), + "deserialised name should round-trip exactly; logs: {logs}" + ); let account = result.account(&power_addr).unwrap(); assert_eq!(account.data[1], 0, "power should now be off");