From 605a7c063a7d495e7fe2f216be61aeffb351d186 Mon Sep 17 00:00:00 2001 From: dhruvja Date: Sat, 7 Feb 2026 14:03:39 +0530 Subject: [PATCH 1/2] add skill for pinochio framework --- skill/SKILL.md | 10 +-- skill/delegation.md | 177 +++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 172 insertions(+), 15 deletions(-) diff --git a/skill/SKILL.md b/skill/SKILL.md index b5bc4a5..74e212d 100644 --- a/skill/SKILL.md +++ b/skill/SKILL.md @@ -1,6 +1,6 @@ --- name: magicblock -description: MagicBlock Ephemeral Rollups development patterns for Solana. Covers delegation/undelegation flows, dual-connection architecture (base layer + ER), cranks for scheduled tasks, VRF for verifiable randomness, and TypeScript/Anchor integration. Use for high-performance gaming, real-time apps, and fast transaction throughput on Solana. +description: MagicBlock Ephemeral Rollups development patterns for Solana. Covers delegation/undelegation flows, dual-connection architecture (base layer + ER), cranks for scheduled tasks, VRF for verifiable randomness, and TypeScript integration. Supports both Anchor and Pinocchio frameworks. Use for high-performance gaming, real-time apps, and fast transaction throughput on Solana. user-invocable: true --- @@ -34,10 +34,9 @@ Use this Skill when the user asks for: ## Default stack decisions (opinionated) -1) **Programs: Anchor with ephemeral-rollups-sdk** - - Use `ephemeral-rollups-sdk` with Anchor features - - Apply `#[ephemeral]` macro before `#[program]` - - Use `#[delegate]` and `#[commit]` macros for delegation contexts +1) **Programs: Anchor or Pinocchio with ephemeral-rollups SDK** + - **Anchor**: Use `ephemeral-rollups-sdk` with `#[ephemeral]`, `#[delegate]`, and `#[commit]` macros + - **Pinocchio**: Use `ephemeral-rollups-pinocchio` with explicit account slicing and manual dispatch 2) **Dual Connections** - Base layer connection for initialization and delegation @@ -68,6 +67,7 @@ Always be explicit about: - PDA seeds matching between delegate call and account definition - Using `skipPreflight: true` for ER transactions - Waiting for state propagation after delegate/undelegate +- Only marking delegated accounts as writable in ER instructions — non-delegated accounts must not be writable ### 4. Add appropriate features - Cranks for recurring automated transactions diff --git a/skill/delegation.md b/skill/delegation.md index 9fa5e99..67e158a 100644 --- a/skill/delegation.md +++ b/skill/delegation.md @@ -1,6 +1,6 @@ # Delegation Patterns (Rust Programs) -## Rust Program Setup +## Anchor Framework ### Dependencies @@ -30,7 +30,7 @@ pub mod my_program { } ``` -## Delegate Instruction +### Delegate Instruction ```rust pub fn delegate(ctx: Context, uid: String) -> Result<()> { @@ -55,7 +55,7 @@ pub struct DelegateInput<'info> { } ``` -## Undelegate Instruction +### Undelegate Instruction ```rust pub fn undelegate(ctx: Context) -> Result<()> { @@ -78,7 +78,7 @@ pub struct Undelegate<'info> { } ``` -## Commit Without Undelegating +### Commit Without Undelegating ```rust pub fn commit(ctx: Context) -> Result<()> { @@ -92,16 +92,166 @@ pub fn commit(ctx: Context) -> Result<()> { } ``` -## Common Gotchas +### Anchor Gotchas -### Method Name Convention +#### Method Name Convention The delegate method is auto-generated as `delegate_`: ```rust pub my_account: AccountInfo<'info>, // => ctx.accounts.delegate_my_account() ``` +#### Don't use `Account<>` in delegate context +Use `AccountInfo` with `del` constraint instead. + +#### Don't skip the `#[commit]` macro +Required for undelegate context. + +## Pinocchio Framework + +### Dependencies + +```toml +# Cargo.toml +[dependencies] +pinocchio = { version = "0.10.2", features = ["cpi", "copy"] } +pinocchio-log = { version = "0.5" } +pinocchio-system = { version = "0.5" } +ephemeral-rollups-pinocchio = { version = "0.8.5" } +``` + +### Imports + +```rust +use ephemeral_rollups_pinocchio::instruction::delegate_account; +use ephemeral_rollups_pinocchio::instruction::{ + commit_accounts, commit_and_undelegate_accounts, undelegate, +}; +use ephemeral_rollups_pinocchio::types::DelegateConfig; +``` + +### Program Setup + +No macros needed. Pinocchio uses explicit account slicing and manual instruction dispatch instead of Anchor's `#[program]`/`#[ephemeral]` macros. + +### Delegate Instruction + +```rust +pub fn delegate( + _program_id: &Address, + accounts: &[AccountView], + bump: u8, +) -> ProgramResult { + let [payer, pda_to_delegate, owner_program, delegation_buffer, + delegation_record, delegation_metadata, _delegation_program, + system_program, rest @ ..] = accounts + else { + return Err(ProgramError::NotEnoughAccountKeys); + }; + let validator = rest.first().map(|account| *account.address()); + + let seeds: &[&[u8]] = &[b"seed", payer.address().as_ref()]; + + delegate_account( + &[ + payer, + pda_to_delegate, + owner_program, + delegation_buffer, + delegation_record, + delegation_metadata, + system_program, + ], + seeds, + bump, + DelegateConfig { + validator, + ..Default::default() + }, + )?; + + Ok(()) +} +``` + +### Undelegate Instruction + +```rust +pub fn undelegate( + _program_id: &Address, + accounts: &[AccountView], +) -> ProgramResult { + let [payer, my_account, magic_program, magic_context] = accounts else { + return Err(ProgramError::NotEnoughAccountKeys); + }; + + if !payer.is_signer() { + return Err(ProgramError::MissingRequiredSignature); + } + + commit_and_undelegate_accounts( + payer, + &[*my_account], + magic_context, + magic_program, + )?; + + Ok(()) +} + +// REQUIRED: Handle the undelegation callback from the delegation program +pub fn undelegation_callback( + program_id: &Address, + accounts: &[AccountView], + ix_data: &[u8], +) -> ProgramResult { + let [delegated_acc, buffer_acc, payer, _system_program, ..] = accounts else { + return Err(ProgramError::NotEnoughAccountKeys); + }; + undelegate(delegated_acc, program_id, buffer_acc, payer, ix_data)?; + Ok(()) +} +``` + +### Commit Without Undelegating + +```rust +pub fn commit( + _program_id: &Address, + accounts: &[AccountView], +) -> ProgramResult { + let [payer, my_account, magic_program, magic_context] = accounts else { + return Err(ProgramError::NotEnoughAccountKeys); + }; + + if !payer.is_signer() { + return Err(ProgramError::MissingRequiredSignature); + } + + commit_accounts( + payer, + &[*my_account], + magic_context, + magic_program, + )?; + + Ok(()) +} +``` + +### Pinocchio Gotchas + +#### Undelegation Callback Required +You must implement a `process_undelegation_callback` handler that calls `undelegate()` — the delegation program invokes this on your program when undelegating. + +#### Copy Semantics for Account References +`commit_accounts` and `commit_and_undelegate_accounts` take `&[AccountView]` (copied), not references like Anchor's `&AccountInfo`. This requires the `copy` feature on the `pinocchio` crate. + +## Common Gotchas + ### PDA Seeds Must Match -Seeds in delegate instruction must exactly match account definition: +Seeds in delegate instruction must exactly match account definition. + +**Anchor:** ```rust #[account(mut, del, seeds = [b"tomo", uid.as_bytes()], bump)] pub tomo: AccountInfo<'info>, @@ -110,6 +260,15 @@ pub tomo: AccountInfo<'info>, ctx.accounts.delegate_tomo(&payer, &[b"tomo", uid.as_bytes()], config)?; ``` +**Pinocchio:** +```rust +// Seeds used to derive the PDA +let seeds: &[&[u8]] = &[b"seed", payer.address().as_ref()]; + +// delegate_account call - seeds must match the PDA derivation +delegate_account(&[...], seeds, bump, delegate_config)?; +``` + ### Account Owner Changes on Delegation ``` Not delegated: account.owner == YOUR_PROGRAM_ID @@ -128,6 +287,4 @@ Delegated: account.owner == DELEGATION_PROGRAM_ID ### Don'ts - Don't send delegate tx to ER - Delegation always goes to base layer - Don't send operations to base layer - Delegated account ops go to ER -- Don't forget the `#[ephemeral]` macro - Required on program module -- Don't use `Account<>` in delegate context - Use `AccountInfo` with `del` constraint -- Don't skip the `#[commit]` macro - Required for undelegate context +- Don't forget the `#[ephemeral]` macro - Required on program module (Anchor) From 7f9528b83cc424079c0c1409d7201fe1c76f30b2 Mon Sep 17 00:00:00 2001 From: dhruvja Date: Sat, 7 Feb 2026 14:42:04 +0530 Subject: [PATCH 2/2] prefix pinocchio methods with process --- skill/delegation.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/skill/delegation.md b/skill/delegation.md index 67e158a..871b5de 100644 --- a/skill/delegation.md +++ b/skill/delegation.md @@ -136,7 +136,7 @@ No macros needed. Pinocchio uses explicit account slicing and manual instruction ### Delegate Instruction ```rust -pub fn delegate( +pub fn process_delegate( _program_id: &Address, accounts: &[AccountView], bump: u8, @@ -176,7 +176,7 @@ pub fn delegate( ### Undelegate Instruction ```rust -pub fn undelegate( +pub fn process_undelegate( _program_id: &Address, accounts: &[AccountView], ) -> ProgramResult { @@ -199,7 +199,7 @@ pub fn undelegate( } // REQUIRED: Handle the undelegation callback from the delegation program -pub fn undelegation_callback( +pub fn process_undelegation_callback( program_id: &Address, accounts: &[AccountView], ix_data: &[u8], @@ -215,7 +215,7 @@ pub fn undelegation_callback( ### Commit Without Undelegating ```rust -pub fn commit( +pub fn process_commit( _program_id: &Address, accounts: &[AccountView], ) -> ProgramResult {