Skip to content

feat: add magic-actions-advanced example (post-delegation actions + PDA-paid commits)#79

Open
crocodiledundalk wants to merge 3 commits into
magicblock-labs:mainfrom
crocodiledundalk:additional-magic-action-examples
Open

feat: add magic-actions-advanced example (post-delegation actions + PDA-paid commits)#79
crocodiledundalk wants to merge 3 commits into
magicblock-labs:mainfrom
crocodiledundalk:additional-magic-action-examples

Conversation

@crocodiledundalk

@crocodiledundalk crocodiledundalk commented May 4, 2026

Copy link
Copy Markdown

Summary

Two new Magic Actions examples that extend the existing magic-actions demo.

magic-action-shared-payer

Shows how to use a protocol-owned PDA (global_signer, seeds: [b"global_signer"]) as the escrow authority for Magic Action callbacks instead of the individual user's wallet. Uses MagicInstructionBuilder::build() + manual AccountMeta patching + invoke_signed to authorize the PDA over the MagicProgram CPI.

magic-actions-on-delegation

Demonstrates post-delegation actions: an increment instruction is bundled into the delegate call via delegate_account_with_actions + PostDelegationActions. The ER validator fires it automatically when the account is first cloned — no separate client transaction needed.

Also adds:

  • docs/post-delegation-actions.md — specification for the PostDelegationActions key-table encoding (compact AccountMeta, encryption variants, signer constraints)
  • Root README.md updated to list both new examples and link to the new guide

Test plan

  • cd magic-action-shared-payer && yarn && anchor test passes
  • cd magic-actions-on-delegation && yarn && anchor test passes
  • Both example READMEs render correctly on GitHub
  • Root README links resolve correctly

Summary by CodeRabbit

  • New Features

    • Added "Magic Actions Advanced" example demonstrating post-delegation actions with PDA-paid magic action commits.
  • Documentation

    • New comprehensive guide on post-delegation actions, including lifecycle overview, encryption constraints, and troubleshooting checklist.
    • Complete example setup instructions with software requirements and build steps.
  • Tests

    • Added integration test suite validating post-delegation workflows and leaderboard updates.

@vercel

vercel Bot commented May 4, 2026

Copy link
Copy Markdown

@crocodiledundalk is attempting to deploy a commit to the MagicBlock Labs Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented May 4, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

This pull request adds a complete "Magic Actions Advanced" example program for Solana ephemeral rollups. It demonstrates post-delegation actions (queued instructions executed automatically after account delegation) combined with PDA-paid magic action commits using a protocol-owned escrow signer. The PR includes comprehensive documentation, a full Anchor program implementation with initialization, delegation, and leaderboard commit flows, and an end-to-end integration test suite with tooling updates.

Changes

Magic Actions Advanced Example Program

Layer / File(s) Summary
Post-Delegation Actions Documentation and Examples
docs/post-delegation-actions.md, README.md, magic-actions-advanced/README.md
Introduces the post-delegation actions pattern with mental model, client/CPI builder examples, action construction and encryption constraints, signer/account validation rules, and failure diagnostics. Updates main README with example link and adds program-specific README describing flow, required software versions, and build/test instructions.
Project Configuration and Build Setup
magic-actions-advanced/Cargo.toml, magic-actions-advanced/package.json, magic-actions-advanced/Anchor.toml, magic-actions-advanced/tsconfig.json, magic-actions-advanced/migrations/deploy.ts, magic-actions-advanced/programs/magic-actions-advanced/Cargo.toml, magic-actions-advanced/.gitignore, magic-actions-advanced/.prettierignore, .gitignore, magic-actions-advanced/target/deploy/magic_actions_advanced-keypair.json
Configures the workspace with Rust/Anchor/npm dependencies, feature flags, release optimizations, TypeScript settings, Anchor 0.32.1 toolchain, test script with yarn run ts-mocha, and deployment keypair.
Anchor Program: Counter, Delegation, and Commit Instructions
magic-actions-advanced/programs/magic-actions-advanced/src/lib.rs
Implements magic_actions_advanced program with initialize and increment for counter state, update_leaderboard for high-score tracking, delegate to queue post-delegation increment actions, undelegate to reverse delegation, and commit_and_update_leaderboard using ephemeral-rollups intent building with global_signer PDA escrow funding. Defines Counter and Leaderboard state structs and account context constraints for PDA derivation and delegation-specific ownership rules.
Integration Tests and Test Automation
magic-actions-advanced/tests/magic-actions-advanced.ts, test-locally.sh
Implements full integration test suite: initialization (counter/leaderboard to zero), delegation with automatic post-action execution on ER, direct ER-side increments, PDA-paid commit with leaderboard updates, and undelegation verification. Adds utility helpers for state printing, account labeling, transaction inspection, and sqlite database access for L1 signature verification. Updates test-locally.sh with ephemeral validator lifecycle (clear /tmp/mb-storage, new flags), router endpoint configuration, and magic-actions-advanced test invocation.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • jonasXchen
  • GabrielePicco
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 35.14% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title accurately summarizes the main change: adding a new example combining post-delegation actions with PDA-paid magic action commits.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@magic-action-shared-payer/programs/magic-action-shared-payer/src/lib.rs`:
- Around line 38-42: In update_leaderboard, don't deserialize
ctx.accounts.counter blindly; first validate the account is the expected PDA and
owned by the allowed program(s) before reading its bytes. Derive the expected
PDA (using the same seeds used to create the Counter account), compare it to
ctx.accounts.counter.to_account_info().key, and assert the account owner equals
the expected program id (or system program if appropriate); also check the
account data length/is_initialized flag if present. Only after these checks
succeed, call try_borrow_data and Counter::try_deserialize to mutate
high_score/leaderboard.

In `@magic-action-shared-payer/README.md`:
- Around line 41-42: The README currently instructs users to run "rm -rf
/target/deploy/*.keypair" which uses an absolute path and can delete /target on
a machine; change the instruction to use a repository-relative path instead
(e.g., "target/deploy/*.keypair" or "./target/deploy/*.keypair") in the README
so it targets the repo's target/deploy directory; update the line referencing
the command (the rm -rf /target/deploy/*.keypair entry) to the safer relative
form and consider adding a short safety note or existence check before removal.

In `@magic-action-shared-payer/tests/magic-action-shared-payer.ts`:
- Around line 292-302: Replace the fixed animated sleep helper
(sleepWithAnimation) and any hard-coded sleeps used before
delegate/commit/undelegate assertions with a polling helper that repeatedly
checks the expected on-chain/state condition until it becomes true or a
configurable timeout elapses; implement a function like
waitForCondition(checkFn, timeoutMs, intervalMs) and use it in the tests where
delegate/commit/undelegate state is asserted (replace calls to
sleepWithAnimation and direct setTimeout waits) so each test polls the relevant
getter/contract call (e.g., the delegate status, commit inclusion, or undelegate
balance) at short intervals and fails only after the timeout instead of waiting
a fixed duration.

In `@magic-action-shared-payer/tsconfig.json`:
- Around line 3-8: The tsconfig.json restricts global type resolution to
["mocha","chai"], causing Node types used by migrations/deploy.ts (which uses
module.exports) to be missing; update the "types" array in tsconfig.json to
include "node" (e.g., ["mocha","chai","node"]) so `@types/node` is picked up and
the migration entrypoint type-checks correctly.

In `@magic-actions-on-delegation/programs/magic-actions-on-delegation/src/lib.rs`:
- Around line 163-164: The delegation_program account (field delegation_program)
is unconstrained and can be swapped by callers; add an address constraint to
ensure it is the expected MagicBlock delegation program before performing the
PDA-signed CPI. Update the struct field annotation for delegation_program to
include an address check that compares to the MagicBlock delegation program's ID
constant (for example using #[account(address =
<magic_block_program_crate>::ID)] or the appropriate crate/program ID constant),
and keep references in the CPI call sites such as delegate_account_with_actions
unchanged so the inner invocation only ever receives the verified program.

In `@magic-actions-on-delegation/README.md`:
- Around line 38-42: Update the README command that currently uses an absolute
path "/target/deploy/*.keypair" to a project-local path; replace the command
string (rm -rf /target/deploy/*.keypair) with a safe relative version such as rm
-rf target/deploy/*.keypair or rm -f ./target/deploy/*.keypair so it deletes the
local target/deploy keypairs instead of touching the filesystem root.

In `@magic-actions-on-delegation/tests/magic-actions-on-delegation.ts`:
- Around line 193-195: Replace fixed sleep() waits with a polling helper that
repeatedly calls readCounterOnER() or readCounterOnBase() until the expected
value is observed or a real timeout elapses; implement a function like
waitForCounterOnER(expected: number, timeoutMs: number, intervalMs = 500) and
similarly waitForCounterOnBase(...) that measures elapsed time, loops awaiting
the corresponding readCounter... call every interval, returns when value
matches, and throws an error on timeout, then replace all uses of sleep(seconds)
in the tests with calls to the appropriate waitForCounter... helper to avoid
flaky fixed delays.

In `@magic-actions-on-delegation/tsconfig.json`:
- Around line 3-8: The tsconfig currently restricts ambient types to
["mocha","chai"] but migrations/deploy.ts uses CommonJS module.exports, so add
Node ambient types: update the "types" array in
magic-actions-on-delegation/tsconfig.json to include "node" (e.g.
["mocha","chai","node"]) and also add `@types/node` to magic-actions-on-delegation
package.json devDependencies so TypeScript recognizes Node globals and module
typings used by module.exports in migrations/deploy.ts.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 001d36c7-81f6-4dd0-9c0f-a3e1f2dd0bb1

📥 Commits

Reviewing files that changed from the base of the PR and between 3d91320 and 78f2d1d.

⛔ Files ignored due to path filters (4)
  • magic-action-shared-payer/Cargo.lock is excluded by !**/*.lock
  • magic-action-shared-payer/yarn.lock is excluded by !**/yarn.lock, !**/*.lock
  • magic-actions-on-delegation/Cargo.lock is excluded by !**/*.lock
  • magic-actions-on-delegation/yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (24)
  • README.md
  • docs/post-delegation-actions.md
  • magic-action-shared-payer/.gitignore
  • magic-action-shared-payer/.prettierignore
  • magic-action-shared-payer/Anchor.toml
  • magic-action-shared-payer/Cargo.toml
  • magic-action-shared-payer/README.md
  • magic-action-shared-payer/migrations/deploy.ts
  • magic-action-shared-payer/package.json
  • magic-action-shared-payer/programs/magic-action-shared-payer/Cargo.toml
  • magic-action-shared-payer/programs/magic-action-shared-payer/src/lib.rs
  • magic-action-shared-payer/tests/magic-action-shared-payer.ts
  • magic-action-shared-payer/tsconfig.json
  • magic-actions-on-delegation/.gitignore
  • magic-actions-on-delegation/.prettierignore
  • magic-actions-on-delegation/Anchor.toml
  • magic-actions-on-delegation/Cargo.toml
  • magic-actions-on-delegation/README.md
  • magic-actions-on-delegation/migrations/deploy.ts
  • magic-actions-on-delegation/package.json
  • magic-actions-on-delegation/programs/magic-actions-on-delegation/Cargo.toml
  • magic-actions-on-delegation/programs/magic-actions-on-delegation/src/lib.rs
  • magic-actions-on-delegation/tests/magic-actions-on-delegation.ts
  • magic-actions-on-delegation/tsconfig.json

Comment thread magic-action-shared-payer/README.md
Comment thread magic-action-shared-payer/tests/magic-action-shared-payer.ts Outdated
Comment thread magic-action-shared-payer/tsconfig.json
Comment thread magic-actions-on-delegation/programs/magic-actions-on-delegation/src/lib.rs Outdated
Comment thread magic-actions-on-delegation/README.md
Comment thread magic-actions-on-delegation/tests/magic-actions-on-delegation.ts Outdated
Comment thread magic-actions-on-delegation/tsconfig.json

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

♻️ Duplicate comments (1)
magic-action-shared-payer/programs/magic-action-shared-payer/src/lib.rs (1)

38-42: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Still validate the counter owner before deserializing its bytes.

The new seeds constraint fixes the PDA address, but update_leaderboard still trusts raw bytes from an UncheckedAccount without checking that the owner is one of the expected programs for the delegated/undelegated states. A wrong owner or malformed account at that PDA can still make this action read invalid state or fail unexpectedly.

Also applies to: 145-146

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@magic-action-shared-payer/programs/magic-action-shared-payer/src/lib.rs`
around lines 38 - 42, The code in update_leaderboard calls
Counter::try_deserialize on an UncheckedAccount (counter) without verifying the
account owner; before deserializing, assert that counter.to_account_info().owner
matches one of the allowed program IDs for the delegated/undelegated counter
states (e.g., compare to the expected program pubkeys/constants) and return an
appropriate error if not, then proceed to call Counter::try_deserialize; apply
the same owner check before any other Counter::try_deserialize usage (e.g., the
other occurrence around lines 145-146) so you never deserialize bytes from an
account with an unexpected owner.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@magic-action-shared-payer/programs/magic-action-shared-payer/src/lib.rs`:
- Around line 31-35: The increment function uses unchecked addition on
counter.count which will wrap at u64::MAX; change the logic in pub fn
increment(ctx: Context<Increment>) -> Result<()> to use
counter.count.checked_add(1) and if it returns None return an explicit error
(e.g., a custom OverflowError or ProgramError::Custom) instead of performing the
addition; update any associated error enum (or define a new variant) so callers
can see the overflow failure and keep the msg! log only after a successful
checked add.
- Around line 22-26: The initialize function currently mutates shared PDA
accounts (counter and leaderboard) unguarded, allowing any caller to rerun and
zero them; change the account constraints used where you see init_if_needed for
these PDAs to init so they can only be created once, or alternatively add an
explicit authority/initialized guard in the initialize function that checks a
designated authority signer or an "initialized" flag before writing (and return
an error if already initialized). Apply the same change to the other
initialization block in this file that sets count/high_score to 0 (the other
function/section using the same counter/leaderboard pattern).

In `@magic-actions-on-delegation/programs/magic-actions-on-delegation/src/lib.rs`:
- Around line 26-27: The increment function (pub fn increment in lib.rs)
currently does unchecked u64 addition; change it to use checked_add(1) and
return a proper error if None is returned to prevent overflow. Add the suggested
ErrorCode enum with a CounterOverflow variant (#[error_code] pub enum ErrorCode
{ #[msg("counter overflow")] CounterOverflow, }) and update increment to set
ctx.accounts.counter.count =
ctx.accounts.counter.count.checked_add(1).ok_or(ErrorCode::CounterOverflow)?; so
the handler returns an error instead of silently wrapping on overflow.
- Around line 20-21: The initialize handler currently allows reinitialization
and wiping of the global PDA counter by using init_if_needed and then
unconditionally setting ctx.accounts.counter.count = 0; update the account
constraint so the PDA is created only once by replacing init_if_needed with init
on the Counter account (the PDA derived from COUNTER_SEED) and remove or guard
the unconditional reset in the initialize function (Context<Initialize> /
initialize) so the counter is not overwritten on subsequent calls; ensure the
account is created with the same seeds and bump used elsewhere and that access
control prevents repeat creation or modification by unauthorized signers.

---

Duplicate comments:
In `@magic-action-shared-payer/programs/magic-action-shared-payer/src/lib.rs`:
- Around line 38-42: The code in update_leaderboard calls
Counter::try_deserialize on an UncheckedAccount (counter) without verifying the
account owner; before deserializing, assert that counter.to_account_info().owner
matches one of the allowed program IDs for the delegated/undelegated counter
states (e.g., compare to the expected program pubkeys/constants) and return an
appropriate error if not, then proceed to call Counter::try_deserialize; apply
the same owner check before any other Counter::try_deserialize usage (e.g., the
other occurrence around lines 145-146) so you never deserialize bytes from an
account with an unexpected owner.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 3b20ec78-f526-4f80-b656-9fe03413bb68

📥 Commits

Reviewing files that changed from the base of the PR and between 78f2d1d and 23156e8.

📒 Files selected for processing (6)
  • magic-action-shared-payer/README.md
  • magic-action-shared-payer/programs/magic-action-shared-payer/src/lib.rs
  • magic-action-shared-payer/tsconfig.json
  • magic-actions-on-delegation/README.md
  • magic-actions-on-delegation/programs/magic-actions-on-delegation/src/lib.rs
  • magic-actions-on-delegation/tsconfig.json

Comment on lines +31 to +35
pub fn increment(ctx: Context<Increment>) -> Result<()> {
let counter = &mut ctx.accounts.counter;
counter.count += 1;
msg!("PDA {} count: {}", counter.key(), counter.count);
Ok(())

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Use checked arithmetic for count.

Line 33 can wrap on-chain once count reaches u64::MAX, which silently resets the counter and can corrupt the observed high score. Please switch this to checked_add and return an explicit error on overflow.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@magic-action-shared-payer/programs/magic-action-shared-payer/src/lib.rs`
around lines 31 - 35, The increment function uses unchecked addition on
counter.count which will wrap at u64::MAX; change the logic in pub fn
increment(ctx: Context<Increment>) -> Result<()> to use
counter.count.checked_add(1) and if it returns None return an explicit error
(e.g., a custom OverflowError or ProgramError::Custom) instead of performing the
addition; update any associated error enum (or define a new variant) so callers
can see the overflow failure and keep the msg! log only after a successful
checked add.

Comment on lines +20 to +21
pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
ctx.accounts.counter.count = 0;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

cd magic-actions-on-delegation/programs/magic-actions-on-delegation/src && wc -l lib.rs

Repository: magicblock-labs/magicblock-engine-examples

Length of output: 97


🏁 Script executed:

cd magic-actions-on-delegation/programs/magic-actions-on-delegation/src && cat -n lib.rs | head -40

Repository: magicblock-labs/magicblock-engine-examples

Length of output: 1825


🏁 Script executed:

cd magic-actions-on-delegation/programs/magic-actions-on-delegation/src && cat -n lib.rs | sed -n '100,130p'

Repository: magicblock-labs/magicblock-engine-examples

Length of output: 1084


🏁 Script executed:

cd magic-actions-on-delegation/programs/magic-actions-on-delegation/src && grep -n "init_if_needed\|init," lib.rs

Repository: magicblock-labs/magicblock-engine-examples

Length of output: 114


🏁 Script executed:

cd magic-actions-on-delegation/programs/magic-actions-on-delegation/src && cat -n lib.rs | sed -n '1,100p' | grep -A 15 "pub struct Initialize"

Repository: magicblock-labs/magicblock-engine-examples

Length of output: 68


initialize can reset the shared PDA counter after deployment.

init_if_needed allows the handler to execute on an existing account. Since Line 21 unconditionally writes 0 to the counter, any signer can re-call initialize and wipe the counter state. This PDA is global (derived from COUNTER_SEED only), and there is no access control preventing repeated calls. Change init_if_needed to init to prevent reinitialization.

Suggested fix
 #[derive(Accounts)]
 pub struct Initialize<'info> {
     #[account(
-        init_if_needed,
+        init,
         payer = user,
         space = 8 + 8,
         seeds = [COUNTER_SEED],
         bump,
     )]
     pub counter: Account<'info, Counter>,
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@magic-actions-on-delegation/programs/magic-actions-on-delegation/src/lib.rs`
around lines 20 - 21, The initialize handler currently allows reinitialization
and wiping of the global PDA counter by using init_if_needed and then
unconditionally setting ctx.accounts.counter.count = 0; update the account
constraint so the PDA is created only once by replacing init_if_needed with init
on the Counter account (the PDA derived from COUNTER_SEED) and remove or guard
the unconditional reset in the initialize function (Context<Initialize> /
initialize) so the counter is not overwritten on subsequent calls; ensure the
account is created with the same seeds and bump used elsewhere and that access
control prevents repeat creation or modification by unauthorized signers.

Comment on lines +26 to +27
pub fn increment(ctx: Context<Increment>) -> Result<()> {
ctx.accounts.counter.count += 1;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

find . -type f -name "lib.rs" | head -20

Repository: magicblock-labs/magicblock-engine-examples

Length of output: 1193


🏁 Script executed:

cat -n magic-actions-on-delegation/programs/magic-actions-on-delegation/src/lib.rs

Repository: magicblock-labs/magicblock-engine-examples

Length of output: 7810


Guard the counter increment against overflow.

Line 27 uses unchecked u64 arithmetic. In BPF (release) builds, overflow wraps silently, corrupting state. Use checked_add(1) with proper error handling.

Suggested fix
 pub fn increment(ctx: Context<Increment>) -> Result<()> {
-    ctx.accounts.counter.count += 1;
+    ctx.accounts.counter.count = ctx
+        .accounts
+        .counter
+        .count
+        .checked_add(1)
+        .ok_or(ErrorCode::CounterOverflow)?;
     msg!(
         "PDA {} count: {} (signer: {})",
         ctx.accounts.counter.key(),
         ctx.accounts.counter.count,
         ctx.accounts.user.key(),

Add an error variant alongside the instruction code:

#[error_code]
pub enum ErrorCode {
    #[msg("counter overflow")]
    CounterOverflow,
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@magic-actions-on-delegation/programs/magic-actions-on-delegation/src/lib.rs`
around lines 26 - 27, The increment function (pub fn increment in lib.rs)
currently does unchecked u64 addition; change it to use checked_add(1) and
return a proper error if None is returned to prevent overflow. Add the suggested
ErrorCode enum with a CounterOverflow variant (#[error_code] pub enum ErrorCode
{ #[msg("counter overflow")] CounterOverflow, }) and update increment to set
ctx.accounts.counter.count =
ctx.accounts.counter.count.checked_add(1).ok_or(ErrorCode::CounterOverflow)?; so
the handler returns an error instead of silently wrapping on overflow.

@Dodecahedr0x

Copy link
Copy Markdown
Contributor

Hey thanks for the PR!

It would be nice if this could be single folder combining delegation actions and PDA-paid intent bundles instead of 2 distinct programs (we're in the process of simplifying this repo)

Combined example demonstrating two advanced MagicBlock ER patterns:

1. Post-delegation actions — an `increment` instruction is queued inside the
   `delegate` call via `PostDelegationActions`. The ER validator fires it
   automatically when the account is first cloned; no separate client tx needed.

2. PDA-paid magic action — `commit_and_update_leaderboard` uses a
   `global_signer` PDA as the `escrow_authority` so the protocol escrow covers
   the magic-action fee instead of the individual user's wallet. Uses
   `.build()` + AccountMeta patch + `invoke_signed` to authorise the PDA over
   the MagicProgram CPI.

Includes full ts-mocha integration test (5 tests covering the full delegate →
increment on ER → commit + leaderboard update → undelegate lifecycle) and a
wired-up entry in test-locally.sh.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
@crocodiledundalk crocodiledundalk force-pushed the additional-magic-action-examples branch from 4c9327c to 7977cfb Compare June 8, 2026 08:17

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@magic-actions-advanced/tests/magic-actions-advanced.ts`:
- Around line 529-548: Replace the shell invocation in readCommitSigsFromDb with
a real SQLite client call: open the database at the passed dbPath using a
library like better-sqlite3 or sqlite3, execute the same SELECT query (`SELECT
pubkey, commit_status, commit_stage_signature, finalize_stage_signature FROM
commit_status ORDER BY created_at ASC;`), and map the returned rows into the
CommitRow shape (pubkey defaulting to "", status defaulting to "", commitSig and
finalizeSig set to null when empty). Keep the same try/catch semantics to return
[] on error and ensure you locate and update the readCommitSigsFromDb function
and its use of the dbPath and query constants.
- Around line 264-277: The test currently uses a hardcoded DB_PATH
("/tmp/magicblock-er-storage/committor_service.sqlite") which can be missing in
other environments; update the test to read the database path from an
environment variable (e.g., MAGICBLOCK_ER_DB_PATH) with the current hardcoded
value as the fallback, and use that variable when calling readCommitSigsFromDb;
additionally, make the behavior explicit in readCommitSigsFromDb or the test
(log a clear warning or throw) when the resolved path does not exist so failures
are visible; locate these changes around the DB_PATH declaration and the call to
readCommitSigsFromDb in magic-actions-advanced/tests/magic-actions-advanced.ts.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 178841db-cc1b-4e72-b47f-9563f76e5588

📥 Commits

Reviewing files that changed from the base of the PR and between 23156e8 and 4c9327c.

⛔ Files ignored due to path filters (2)
  • magic-actions-advanced/Cargo.lock is excluded by !**/*.lock
  • magic-actions-advanced/yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (15)
  • .gitignore
  • README.md
  • magic-actions-advanced/.gitignore
  • magic-actions-advanced/.prettierignore
  • magic-actions-advanced/Anchor.toml
  • magic-actions-advanced/Cargo.toml
  • magic-actions-advanced/README.md
  • magic-actions-advanced/migrations/deploy.ts
  • magic-actions-advanced/package.json
  • magic-actions-advanced/programs/magic-actions-advanced/Cargo.toml
  • magic-actions-advanced/programs/magic-actions-advanced/src/lib.rs
  • magic-actions-advanced/target/deploy/magic_actions_advanced-keypair.json
  • magic-actions-advanced/tests/magic-actions-advanced.ts
  • magic-actions-advanced/tsconfig.json
  • test-locally.sh
💤 Files with no reviewable changes (4)
  • magic-actions-advanced/.prettierignore
  • magic-actions-advanced/Cargo.toml
  • magic-actions-advanced/migrations/deploy.ts
  • magic-actions-advanced/tsconfig.json

Comment on lines +264 to +277
const DB_PATH = "/tmp/magicblock-er-storage/committor_service.sqlite";
const commitSigs = readCommitSigsFromDb(DB_PATH);
console.log(`\n Found ${commitSigs.length} L1 commit transaction(s) in ER database:`);
for (const { commitSig, finalizeSig, status, pubkey } of commitSigs) {
console.log(` pubkey=${pubkey} status=${status}`);
if (commitSig) {
console.log(` commit-stage sig: ${commitSig}`);
await printTransactionAccounts("L1 commit-stage", commitSig, baseConnection, knownAddresses);
}
if (finalizeSig) {
console.log(` finalize-stage sig: ${finalizeSig}`);
await printTransactionAccounts("L1 finalize-stage", finalizeSig, baseConnection, knownAddresses);
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Hardcoded database path may not work in all environments.

The database path /tmp/magicblock-er-storage/committor_service.sqlite is hardcoded and specific to the local ephemeral validator configuration. This test will fail silently (returning empty array from readCommitSigsFromDb) if:

  • The ER validator uses a different storage path
  • Tests run in CI with a different environment
  • The validator is configured differently

Consider making this configurable via environment variable or documenting the dependency on this specific path.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@magic-actions-advanced/tests/magic-actions-advanced.ts` around lines 264 -
277, The test currently uses a hardcoded DB_PATH
("/tmp/magicblock-er-storage/committor_service.sqlite") which can be missing in
other environments; update the test to read the database path from an
environment variable (e.g., MAGICBLOCK_ER_DB_PATH) with the current hardcoded
value as the fallback, and use that variable when calling readCommitSigsFromDb;
additionally, make the behavior explicit in readCommitSigsFromDb or the test
(log a clear warning or throw) when the resolved path does not exist so failures
are visible; locate these changes around the DB_PATH declaration and the call to
readCommitSigsFromDb in magic-actions-advanced/tests/magic-actions-advanced.ts.

Comment on lines +529 to +548
function readCommitSigsFromDb(dbPath: string): CommitRow[] {
try {
const query = `SELECT pubkey, commit_status, commit_stage_signature, finalize_stage_signature FROM commit_status ORDER BY created_at ASC;`;
const raw = execSync(`sqlite3 "${dbPath}" "${query}"`, {
encoding: "utf8",
}).trim();
if (!raw) return [];
return raw.split("\n").map((line) => {
const [pubkey, status, commitSig, finalizeSig] = line.split("|");
return {
pubkey: pubkey ?? "",
status: status ?? "",
commitSig: commitSig || null,
finalizeSig: finalizeSig || null,
};
});
} catch {
return [];
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial | ⚖️ Poor tradeoff

Static analysis flagged execSync usage, but current code is safe.

The static analysis tool correctly identified that execSync with string interpolation can be dangerous. However, in the current implementation:

  • dbPath is always the hardcoded constant /tmp/magicblock-er-storage/committor_service.sqlite (line 264)
  • query is a hardcoded constant (line 531)

So there's no actual command injection risk. The try-catch at line 530 also handles failures gracefully.

For maintainability, consider using a dedicated SQLite client library (e.g., better-sqlite3) instead of shelling out, which would be safer and more performant.

🧰 Tools
🪛 OpenGrep (1.22.0)

[ERROR] 532-534: Dynamic command passed to child_process.exec/execSync. Use child_process.execFile or spawn with an argument array instead.

(coderabbit.command-injection.exec-js)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@magic-actions-advanced/tests/magic-actions-advanced.ts` around lines 529 -
548, Replace the shell invocation in readCommitSigsFromDb with a real SQLite
client call: open the database at the passed dbPath using a library like
better-sqlite3 or sqlite3, execute the same SELECT query (`SELECT pubkey,
commit_status, commit_stage_signature, finalize_stage_signature FROM
commit_status ORDER BY created_at ASC;`), and map the returned rows into the
CommitRow shape (pubkey defaulting to "", status defaulting to "", commitSig and
finalizeSig set to null when empty). Keep the same try/catch semantics to return
[] on error and ensure you locate and update the readCommitSigsFromDb function
and its use of the dbPath and query constants.

Source: Linters/SAST tools

crocodiledundalk and others added 2 commits June 8, 2026 12:05
- Read ER commit DB path from MAGICBLOCK_ER_DB_PATH env var (fallback to
  /tmp/magicblock-er-storage/committor_service.sqlite)
- Log explicit warning when DB file is missing rather than silently returning []
- Surface sqlite3 error message in the catch branch

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
…ests

No test assertion depended on the DB read — it was purely diagnostic
console logging. Removes execSync/existsSync imports, DB_PATH block,
CommitRow interface, and readCommitSigsFromDb function.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
@crocodiledundalk crocodiledundalk changed the title feat: add magic-action-shared-payer and magic-actions-on-delegation examples feat: add magic-actions-advanced example (post-delegation actions + PDA-paid commits) Jun 8, 2026
@crocodiledundalk

Copy link
Copy Markdown
Author

Thanks @Dodecahedr0x - hope the above addresses your feedback

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants