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

# 스마트 컨트랙트 통합

> Ephemeral Rollup에서 자체 Solana 프로그램으로 SPL token을 커스터디하고 이동합니다. PDA 서명 전송, 위임, 커밋/위임 해제를 다룹니다.

***

### 빠른 접근

<CardGroup cols={3}>
  <Card title="binary-prediction" icon="github" href="https://github.com/magicblock-labs/magicblock-engine-examples/tree/main/binary-prediction" iconType="duotone">
    eATA 커스터디 전체 흐름
  </Card>

  <Card title="rewards-delegated-vrf" icon="github" href="https://github.com/magicblock-labs/magicblock-engine-examples/tree/main/rewards-delegated-vrf" iconType="duotone">
    Base-layer 커밋 후 지급
  </Card>

  <Card title="spl-tokens" icon="github" href="https://github.com/magicblock-labs/magicblock-engine-examples/tree/main/spl-tokens" iconType="duotone">
    비수탁 전송
  </Card>
</CardGroup>

이 가이드는 사용자 자금을 보유하고 [Ephemeral Rollup](/ko/pages/ephemeral-rollups-ers/introduction/ephemeral-rollup)
속도로 이동하는 DEX, AMM, 예측 시장, escrow, 게임 같은 온체인 프로그램을 위한 것입니다.
핵심 패턴은 프로그램 소유 계정을 ER에 위임하고 프로그램이 PDA로 token 전송에 서명하는 것입니다.

<Note>
  온체인이 아니라 클라이언트에서 token을 이동하려면
  [빠른 시작](/ko/pages/ephemeral-spl-token/quickstart)을 참조하세요.
</Note>

### 두 가지 커스터디 모델

| 모델                   | 위임 대상                                   | Token 이동                                  | 사용 시점                             |
| -------------------- | --------------------------------------- | ----------------------------------------- | --------------------------------- |
| **eATA 커스터디**        | 프로그램 소유 **ephemeral ATA(eATA)**         | **ER에서**, PDA 서명                          | rollup 내부에서 정산하거나 지급할 때(예: 시장 체결) |
| **상태 PDA + 커밋 후 지급** | **상태 PDA**만 위임하며 실제 ATA는 base layer에 유지 | **base layer에서**, post-commit action으로 실행 | 자금을 L1에 두고 커밋 시 정산할 때             |

아래 절차는 **eATA 커스터디**를 다루며 post-commit 방식은 고급 스니펫에서 설명합니다.

***

## 단계별 가이드

프로그램에 eATA authority를 부여하고 위임한 뒤 ER에서 PDA 서명으로 자금을 이동하고
Solana로 정산합니다:

<Steps>
  <Step title={<a href="#1-er-aware-program">프로그램이 ER을 지원하도록 설정</a>}>
    `#[ephemeral]` 속성을 추가하고 필요한 SDK helper를 import합니다.
  </Step>

  <Step title={<a href="#2-create-eata">custody PDA와 eATA 생성</a>}>
    custody PDA가 Ephemeral SPL Token 프로그램 아래의 eATA를 소유하도록 합니다.
  </Step>

  <Step title={<a href="#3-delegate-eata">eATA 위임</a>}>
    Ephemeral SPL Token 프로그램에 CPI하여 ER로 위임합니다.
  </Step>

  <Step title={<a href="#4-pda-signed-transfer">PDA 서명으로 token 이동</a>}>
    rollup 내부에서 custody PDA로 전송에 서명합니다.
  </Step>

  <Step title={<a href="#5-commit-undelegate">커밋 및 위임 해제</a>}>
    계정을 base layer로 되돌립니다.
  </Step>
</Steps>

***

## 커스터디 예제

다음 소프트웨어 패키지가 필요할 수 있으며, 다른 버전도 호환될 수 있습니다.

| 소프트웨어      | 버전      | 설치 가이드                                                     |
| ---------- | ------- | ---------------------------------------------------------- |
| **Solana** | 3.1.9   | [Solana 설치](https://docs.anza.xyz/cli/install)             |
| **Rust**   | 1.89.0  | [Rust 설치](https://www.rust-lang.org/tools/install)         |
| **Anchor** | 1.0.2   | [Anchor 설치](https://www.anchor-lang.com/docs/installation) |
| **Node**   | 24.10.0 | [Node 설치](https://nodejs.org/en/download/current)          |

### 코드 스니펫

<h4 id="1-er-aware-program">
  1. ER 지원 프로그램
</h4>

`#[program]`을 `#[ephemeral]`로 감싸고 필요한 SDK helper를 import합니다.

```rust theme={null}
use ephemeral_rollups_sdk::anchor::{commit, delegate, ephemeral};
use ephemeral_rollups_sdk::cpi::DelegateConfig;
use ephemeral_rollups_sdk::ephem::MagicIntentBundleBuilder;

#[ephemeral]
#[program]
pub mod your_program {
    use super::*;
    // ...
}
```

[⬆️ 맨 위로](#code-snippets)

<h4 id="2-create-eata">
  2. eATA 생성
</h4>

프로그램은 자금의 authority인 상태 PDA(예: `Pool`)를 소유합니다. ER의 커스터디는 해당 PDA가
소유하는 \*\*ephemeral ATA(eATA)\*\*를 사용합니다. eATA는 Ephemeral SPL Token 프로그램
(`SPLxh1LVZzEkX99H6rqYizhytLWPZVV296zyYDPagv2`) 아래에서 `[owner, mint]`로 파생됩니다.

```rust theme={null}
// The eATA is owned by your custody PDA (e.g. a Pool), derived under the
// Ephemeral SPL Token program from [owner, mint].
let (eata, _bump) = Pubkey::find_program_address(
    &[owner.as_ref(), mint.as_ref()],
    &EPHEMERAL_SPL_TOKEN_PROGRAM_ID,
);

// At initialize, create the eATA (InitializeEphemeralAta) and the per-mint
// global vault that backs every eATA (InitializeGlobalVault), then fund it.
```

[⬆️ 맨 위로](#code-snippets)

<h4 id="3-delegate-eata">
  3. eATA 위임
</h4>

Ephemeral SPL Token 프로그램에 대한 CPI(`DelegateEphemeralAta`, discriminator `4`)로
eATA를 위임합니다.

<Warning>
  일반 PDA 소유 ATA를 ER에 직접 위임하는 것은 지원되지 않습니다. ER의 token 커스터디는
  eATA를 통해 처리됩니다. token account가 아닌 프로그램 *상태*를 위임하려면 SDK의
  `#[delegate]` 매크로를 사용하세요.
</Warning>

```rust theme={null}
// Delegate the eATA with a CPI to the Ephemeral SPL Token program.
// An optional trailing validator pubkey routes to a specific validator.
let mut data = vec![4]; // DelegateEphemeralAta
if let Some(validator) = validator {
    data.extend_from_slice(validator.as_ref());
}

let instruction = Instruction {
    program_id: EPHEMERAL_SPL_TOKEN_PROGRAM_ID,
    accounts: vec![
        AccountMeta::new(payer.key(), true),
        AccountMeta::new(ephemeral_ata.key(), false),
        AccountMeta::new_readonly(EPHEMERAL_SPL_TOKEN_PROGRAM_ID, false),
        AccountMeta::new(buffer.key(), false),
        AccountMeta::new(record.key(), false),
        AccountMeta::new(metadata.key(), false),
        AccountMeta::new_readonly(delegation_program.key(), false),
        AccountMeta::new_readonly(system_program.key(), false),
    ],
    data,
};
invoke(&instruction, &account_infos)?;
```

[⬆️ 맨 위로](#code-snippets)

<h4 id="4-pda-signed-transfer">
  4. PDA 서명 전송
</h4>

eATA를 위임한 뒤 `CpiContext::new_with_signer`를 통해 커스터디 PDA가 서명하는 SPL Token CPI로
rollup 내부의 token을 이동합니다. signer seed는 `[POOL_SEED, bump]`입니다. 프로그램이 직접
지급을 승인하므로 사용자 서명은 필요하지 않습니다.

```rust theme={null}
use anchor_spl::token::{self, Transfer as SplTransfer};

// The custody PDA signs the transfer — no user signature required.
let bump_seed = [pool_bump];
let signer_seeds: &[&[&[u8]]] = &[&[POOL_SEED, &bump_seed]];

let cpi_accounts = SplTransfer {
    from,
    to,
    authority: pool, // the Pool PDA
};
let cpi_ctx = CpiContext::new_with_signer(token_program.to_account_info(), cpi_accounts, signer_seeds);
token::transfer(cpi_ctx, amount)?;
```

[⬆️ 맨 위로](#code-snippets)

<h4 id="5-commit-undelegate">
  5. 커밋 및 위임 해제
</h4>

`MagicIntentBundleBuilder`로 ER 상태를 커밋하고 계정을 base layer로 되돌립니다.

```rust theme={null}
// The #[commit] attribute on the accounts context supplies
// magic_context and magic_program.
MagicIntentBundleBuilder::new(
    ctx.accounts.payer.to_account_info(),
    ctx.accounts.magic_context.to_account_info(),
    ctx.accounts.magic_program.to_account_info(),
)
.commit_and_undelegate(&[ctx.accounts.pool.to_account_info()])
.build_and_invoke()?;
```

[⬆️ 맨 위로](#code-snippets)

***

### 고급 코드 스니펫

<Tabs>
  <Tab title="비수탁 전송">
    프로그램이 자금을 커스터디하지 않고 사용자를 대신해 이미 위임된 두 계정 사이에서 token을
    이동한다면 PDA signer가 필요하지 않습니다. authority는 사용자의 `Signer`이며 일반
    `CpiContext::new`를 사용합니다.

    ```rust theme={null}
    // No PDA signer — the authority is the user's Signer.
    let cpi_accounts = SplTransfer {
        from: ctx.accounts.from.to_account_info(),
        to: ctx.accounts.to.to_account_info(),
        authority: ctx.accounts.payer.to_account_info(),
    };
    token::transfer(CpiContext::new(ctx.accounts.token_program.to_account_info(), cpi_accounts), amount)?;
    ```

    [⬆️ 맨 위로](#advanced-code-snippets)
  </Tab>

  <Tab title="Base-layer 커밋 후 지급">
    실제 token account를 base layer에 유지하고 프로그램 상태만 위임하려면 지급을 post-commit
    action으로 예약하세요. 커밋 직후 base layer에서 PDA 서명으로 실행됩니다.
    `transfer_checked`(Token-2022 호환)를 사용하고 action을 첨부해 커밋합니다.

    ```rust theme={null}
    // Keep real token accounts on base, delegate only program state, and pay out
    // PDA-signed on the base layer right after the commit. Use the PDA AccountInfo
    // as the payer and pass its seeds to build_and_invoke_signed.
    MagicIntentBundleBuilder::new(
        payer_pda.to_account_info(),
        magic_context.to_account_info(),
        magic_program.to_account_info(),
    )
        .magic_fee_vault(magic_fee_vault.to_account_info())
        .commit(&[state_pda.to_account_info()])
        .add_post_commit_actions([action])
        .build_and_invoke_signed(&[payer_seeds])?;
    ```

    [⬆️ 맨 위로](#advanced-code-snippets)
  </Tab>
</Tabs>

***

## Solana 익스플로러

Solana에서의 트랜잭션과 계정 정보를 확인해 보세요.

<CardGroup cols={2}>
  <Card title="Solana 익스플로러" icon="search" href="https://explorer.solana.com/" iconType="duotone">
    공식 Solana 익스플로러
  </Card>

  <Card title="Solscan" icon="searchengin" href="https://solscan.io/" iconType="duotone">
    Solana 블록체인 살펴보기
  </Card>
</CardGroup>

## Solana RPC 제공업체

기존 RPC 제공업체를 통해 트랜잭션과 요청을 전송하세요.

<CardGroup cols={2}>
  <Card title="Solana" icon="star" href="https://solana.com/docs/references/clusters#on-a-high-level" iconType="duotone">
    Free Public Nodes
  </Card>

  <Card title="Helius" icon="sun" href="https://www.helius.dev/solana-rpc-nodes" iconType="duotone">
    Free Shared Nodes
  </Card>

  <Card title="Triton" icon="crystal-ball" href="https://triton.one/solana" iconType="duotone">
    Dedicated High-Performance Nodes
  </Card>
</CardGroup>

## Solana 검증자 대시보드

Solana 검증자 인프라의 실시간 업데이트를 확인하세요.

<CardGroup cols={2}>
  <Card title="Solana Beach" icon="wave" href="https://solanabeach.io/" iconType="duotone">
    Get Validator Insights
  </Card>

  <Card title="Validators App" icon="cloud-binary" href="https://www.validators.app/" iconType="duotone">
    Discover Validator Metrics
  </Card>
</CardGroup>

## 서버 상태

Solana와 MagicBlock의 서버 상태를 확인해 보세요.

<CardGroup cols={2}>
  <Card title="Solana Status" icon="server" href="https://status.solana.com/" iconType="duotone">
    Subscribe to Solana Server Updates
  </Card>

  <Card title="MagicBlock Status" icon="heart-pulse" href="/ko/pages/overview/additional-information/system-status" iconType="duotone">
    Subscribe to MagicBlock Server Status
  </Card>
</CardGroup>

***
