> ## 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.

# 実装

> MagicBlock Ephemeral Rollups を使って Solana program に cranks を実装する方法を学ぶ

## 概要

このガイドでは、Anchor フレームワークと **MagicBlock の Ephemeral Rollups（ER）** を使って cranks を実装する方法を説明します。流れは次のとおりです。

1. Solana base layer 上で counter を初期化する
2. 高速実行のため counter account を Ephemeral Rollup に delegate する
3. counter を自動 increment する crank task を schedule する
4. 指定間隔で crank を自動実行する
5. 完了後に account を undelegate して Solana base layer に戻す

## 実行フロー

```
1. User が Solana base layer 上で `initialize()` を呼び出す
   └─> `count = 0` の Counter PDA を作成

2. User が Solana base layer 上で `delegate()` を呼び出す
   └─> Counter account を Ephemeral Rollup に移動

3. User が Ephemeral Rollup 上で `schedule_increment()` を呼び出す
   └─> MagicBlock program へ CPI
       └─> 次の内容で task を schedule:
           - task_id: 1
           - interval: 100ms
           - iterations: 3
           - instruction: increment()

4. MagicBlock が `increment()` を 3 回自動実行:
   └─> 実行 1: `count = 1`（T+0ms）
   └─> 実行 2: `count = 2`（T+100ms）
   └─> 実行 3: `count = 3`（T+200ms）

5. User が Ephemeral Rollup 上で `undelegate()` を呼び出す
   └─> 変更を commit し、Counter を Solana base layer に戻す
```

## コアコンポーネント

### スケジュール対象の関数

たとえば、counter に対して次のような単純な increment instruction を schedule したいとします。

```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(())
}
```

### Increment をスケジュールする関数

crank scheduling の中心となるロジックは次のとおりです。

```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:**

* counter を increment する instruction を作成する
* それを MagicBlock 向けの `ScheduleTask` instruction に serialize する
* CPI（Cross-Program Invocation）で MagicBlock program を呼び出す
* 実際の scheduling と execution は MagicBlock program が担当する

### スケジュール引数

```rust theme={null}
#[derive(AnchorSerialize, AnchorDeserialize)]
pub struct ScheduleIncrementArgs {
    pub task_id: u64,                      // task の一意識別子
    pub execution_interval_millis: u64,    // 実行間隔（ミリ秒）
    pub iterations: u64,                   // 実行回数
}
```

### 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>,
}
```

**重要**: CPI 呼び出し後に Anchor が古いデータを再 serialize しないよう、`Account<Counter>` ではなく `AccountInfo` を使っています。
