> ## Documentation Index
> Fetch the complete documentation index at: https://docs.magicblock.gg/llms.txt
> Use this file to discover all available pages before exploring further.

# Implementation

> Learn how to implement cranks in your Solana program using MagicBlock Ephemeral Rollups

## Overview

This guide demonstrates how to implement cranks using **MagicBlock's Ephemeral Rollups (ER)** with the Anchor framework. The implementation follows this flow:

1. Initialize counter on Solana base layer
2. Delegate counter account to Ephemeral Rollup for faster execution
3. Schedule a crank task that automatically increments the counter
4. Execute the crank automatically at specified intervals
5. Undelegate the account back to Solana base layer when done

## Execution Flow

```
1. User calls initialize() on Solana base layer
   └─> Creates Counter PDA with count = 0

2. User calls delegate() on Solana base layer
   └─> Moves Counter account to Ephemeral Rollup

3. User calls schedule_increment() on Ephemeral Rollup
   └─> CPI to MagicBlock program
       └─> Schedules task with:
           - task_id: 1
           - interval: 100ms
           - iterations: 3
           - instruction: increment()

4. MagicBlock automatically executes increment() 3 times:
   └─> Execution 1: count = 1 (at T+0ms)
   └─> Execution 2: count = 2 (at T+100ms)
   └─> Execution 3: count = 3 (at T+200ms)

5. User calls undelegate() on Ephemeral Rollup
   └─> Commits changes and moves Counter back to Solana base layer
```

## Core Components

### Scheduled Function

Let's take for example that you wanted to schedule the following simple increment instrcution for a counter.

```rust theme={null}
pub fn increment(ctx: Context<Increment>) -> Result<()> {
    let counter = &mut ctx.accounts.counter;
    counter.count += 1;
    if counter.count > 1000 {
        counter.count = 0;
    }
    msg!("PDA {} count: {}", counter.key(), counter.count);
    Ok(())
}
```

### Schedule Increment Function

The core crank scheduling logic:

```rust theme={null}
pub fn schedule_increment(ctx: Context<ScheduleIncrement>, args: ScheduleIncrementArgs) -> Result<()> {
    let increment_ix = Instruction {
        program_id: crate::ID,
        accounts: vec![AccountMeta::new(ctx.accounts.counter.key(), false)],
         // Defining the instruction to call.
        data: anchor_lang::InstructionData::data(&crate::instruction::Increment {}),
    };
    
    let ix_data = bincode::serialize(&MagicBlockInstruction::ScheduleTask(
        ScheduleTaskArgs {
            task_id: args.task_id,
            execution_interval_millis: args.execution_interval_millis,
            iterations: args.iterations,
            instructions: vec![increment_ix],
        },
    ))
    .map_err(|err| {
        msg!("ERROR: failed to serialize args {:?}", err);
        ProgramError::InvalidArgument
    })?;

    let schedule_ix = Instruction::new_with_bytes(
        MAGIC_PROGRAM_ID,
        &ix_data,
        vec![
            AccountMeta::new(ctx.accounts.payer.key(), true),
            AccountMeta::new(ctx.accounts.counter.key(), false),
        ],
    );
    
    invoke_signed(
        &schedule_ix,
        &[
            ctx.accounts.payer.to_account_info(),
            ctx.accounts.counter.to_account_info(),
        ],
        &[],
    )?;
    
    Ok(())
}
```

**Key Points:**

* Creates an instruction to increment the counter
* Serializes it into a `ScheduleTask` instruction for MagicBlock
* Uses CPI (Cross-Program Invocation) to call the MagicBlock program
* The MagicBlock program handles the scheduling and execution

### Schedule Arguments

```rust theme={null}
#[derive(AnchorSerialize, AnchorDeserialize)]
pub struct ScheduleIncrementArgs {
    pub task_id: u64,                      // Unique identifier for the task
    pub execution_interval_millis: u64,    // Time between executions in milliseconds
    pub iterations: u64,                   // Number of times to execute
}
```

### ScheduleIncrement Context

```rust theme={null}
#[derive(Accounts)]
pub struct ScheduleIncrement<'info> {
    /// CHECK: used for CPI
    #[account()]
    pub magic_program: AccountInfo<'info>,
    #[account(mut)]
    pub payer: Signer<'info>,
    /// CHECK: Passed to CPI - using AccountInfo to avoid Anchor re-serializing stale data after CPI
    #[account(mut, seeds = [COUNTER_SEED], bump)]
    pub counter: AccountInfo<'info>,
    /// CHECK: used for CPI
    pub program: AccountInfo<'info>,
}
```

**Important**: Uses `AccountInfo` instead of `Account<Counter>` to avoid Anchor re-serializing stale data after CPI calls.
