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

> Create, resize, and close accounts that live entirely on the Ephemeral Rollup.

Ephemeral accounts are accounts that exist only within the Ephemeral Rollup. A **sponsor** account (which is delegated to the ER) pays rent on behalf of ephemeral accounts at **32 lamports/byte** — 109x cheaper than Solana's base rent.

**Key properties:**

* Born, live, and die entirely on the ER
* Owned by the calling program (inferred from CPI context)
* Funded by a sponsor account's lamports
* Can be created, resized, and closed

***

## The `#[ephemeral_accounts]` Macro

This proc-macro attribute goes on an Anchor `Accounts` struct. It recognizes two custom markers inside `#[account(...)]`:

| Marker    | Purpose                                                 |
| --------- | ------------------------------------------------------- |
| `sponsor` | Marks the account that pays rent for ephemeral accounts |
| `eph`     | Marks an account as ephemeral (ER-only)                 |

### Validation Rules

* At least one `sponsor` is required if any `eph` fields exist
* Only **one** `sponsor` is allowed per struct
* `eph` cannot be combined with `init` or `init_if_needed` (use the generated methods instead)
* If the sponsor is a PDA (not a `Signer`), it must have `seeds` for PDA signing

### Generated Methods

For a field named `conversation`, the macro generates:

| Method                                  | Signature                           | Description                             |
| --------------------------------------- | ----------------------------------- | --------------------------------------- |
| `create_ephemeral_conversation`         | `(data_len: u32) -> Result<()>`     | Creates the ephemeral account           |
| `init_if_needed_ephemeral_conversation` | `(data_len: u32) -> Result<()>`     | Creates only if `data_len == 0`         |
| `resize_ephemeral_conversation`         | `(new_data_len: u32) -> Result<()>` | Grows or shrinks the account            |
| `close_ephemeral_conversation`          | `() -> Result<()>`                  | Closes account, refunds rent to sponsor |

***

## Signing Requirements

* **Sponsor**: Must be a signer for all operations (create, resize, close)
* **Ephemeral**: Must be a signer **only on create** (prevents pubkey squatting). Not required for resize or close.
* For PDA accounts, the macro auto-derives signer seeds via `find_program_address`

***

## Rent Model

```rust theme={null}
pub const EPHEMERAL_RENT_PER_BYTE: u64 = 32;
const ACCOUNT_OVERHEAD: u32 = 60;

// rent = (data_len + 60) * 32
pub const fn rent(data_len: u32) -> u64 {
    (data_len as u64 + ACCOUNT_OVERHEAD as u64) * EPHEMERAL_RENT_PER_BYTE
}
```

* **Growing**: sponsor pays additional rent to vault
* **Shrinking**: vault refunds excess rent to sponsor
* **Close**: all rent refunded from vault to sponsor

***

## Create an Ephemeral Account

```rust theme={null}
use ephemeral_rollups_sdk::anchor::ephemeral_accounts;

pub fn create_conversation(ctx: Context<CreateConversation>) -> Result<()> {
    ctx.accounts
        .create_ephemeral_conversation((8 + Conversation::space_for_message_count(0)) as u32)?;

    let conversation = Conversation {
        handle_owner: ctx.accounts.profile_owner.handle.clone(),
        handle_other: ctx.accounts.profile_other.handle.clone(),
        bump: ctx.bumps.conversation,
        messages: Vec::new(),
    };
    let mut data = ctx.accounts.conversation.try_borrow_mut_data()?;
    conversation.try_serialize(&mut &mut data[..])?;

    Ok(())
}

#[ephemeral_accounts]
#[derive(Accounts)]
pub struct CreateConversation<'info> {
    #[account(mut)]
    pub authority: Signer<'info>,

    #[account(
        mut,
        sponsor,
        seeds = [b"profile", profile_owner.handle.as_bytes()],
        bump = profile_owner.bump,
        has_one = authority
    )]
    pub profile_owner: Account<'info, Profile>,

    #[account(
        seeds = [b"profile", profile_other.handle.as_bytes()],
        bump = profile_other.bump,
    )]
    pub profile_other: Account<'info, Profile>,

    /// CHECK: Ephemeral conversation PDA sponsored by the profile.
    #[account(
        mut,
        eph,
        seeds = [b"conversation", profile_owner.handle.as_bytes(), profile_other.handle.as_bytes()],
        bump
    )]
    pub conversation: AccountInfo<'info>,
    // vault and magic_program are auto-injected by the macro
}
```

<Warning>
  After calling `create_ephemeral_*`, you must manually serialize your data struct into the raw account data. The macro allocates space but does not initialize the data.
</Warning>

***

## Resize an Ephemeral Account

```rust theme={null}
pub fn extend_conversation(
    ctx: Context<ExtendConversation>,
    additional_messages: u32,
) -> Result<()> {
    let current_capacity =
        Conversation::message_capacity(ctx.accounts.conversation.to_account_info().data_len());
    let new_capacity = current_capacity + additional_messages as usize;

    ctx.accounts.resize_ephemeral_conversation(
        (8 + Conversation::space_for_message_count(new_capacity)) as u32,
    )?;

    Ok(())
}

#[ephemeral_accounts]
#[derive(Accounts)]
pub struct ExtendConversation<'info> {
    #[account(mut)]
    pub authority: Signer<'info>,
    #[account(mut, sponsor, seeds = [...], bump = ..., has_one = authority)]
    pub profile_sender: Account<'info, Profile>,
    #[account(seeds = [...], bump = ...)]
    pub profile_other: Account<'info, Profile>,
    /// CHECK: Ephemeral conversation PDA
    #[account(mut, eph, seeds = [...], bump)]
    pub conversation: AccountInfo<'info>,
}
```

***

## Close an Ephemeral Account

```rust theme={null}
pub fn close_conversation(ctx: Context<CloseConversation>) -> Result<()> {
    let profile = &mut ctx.accounts.profile_owner;
    profile.active_conversation_count = profile
        .active_conversation_count
        .checked_sub(1)
        .ok_or(ChatError::ConversationCountUnderflow)?;

    ctx.accounts.close_ephemeral_conversation()?;

    Ok(())
}

#[ephemeral_accounts]
#[derive(Accounts)]
pub struct CloseConversation<'info> {
    #[account(mut)]
    pub authority: Signer<'info>,
    #[account(mut, sponsor, seeds = [...], bump = ..., has_one = authority)]
    pub profile_owner: Account<'info, Profile>,
    #[account(seeds = [...], bump = ...)]
    pub profile_other: Account<'info, Profile>,
    /// CHECK: Ephemeral conversation PDA
    #[account(mut, eph, seeds = [...], bump)]
    pub conversation: AccountInfo<'info>,
}
```

***

## Using a Wallet as Sponsor

A `Signer` can be used directly as the sponsor instead of a PDA:

```rust theme={null}
#[ephemeral_accounts]
#[derive(Accounts)]
pub struct CreateGame<'info> {
    #[account(mut, sponsor)]
    pub payer: Signer<'info>,

    /// CHECK: Ephemeral PDA
    #[account(mut, eph, seeds = [b"game", payer.key().as_ref()], bump)]
    pub game_state: AccountInfo<'info>,
}
```

***

## TypeScript Client Usage

All ephemeral account operations are sent to the **ER connection**, not the base layer:

```typescript theme={null}
// Create
await erProgram.methods
    .createConversation()
    .accounts({
        authority: userA.publicKey,
        profileOwner: profileAPda,
        profileOther: profileBPda,
        conversation: conversationPda,
        systemProgram: SystemProgram.programId,
    })
    .rpc();

// Resize
await erProgram.methods
    .extendConversation(5)
    .accounts({
        authority: userA.publicKey,
        profileSender: profileAPda,
        profileOther: profileBPda,
    })
    .rpc();

// Close
await erProgram.methods
    .closeConversation()
    .accounts({
        authority: userA.publicKey,
        profileOwner: profileAPda,
        profileOther: profileBPda,
        conversation: conversationPda,
    })
    .rpc();
```

***

## Common Gotchas

<AccordionGroup>
  <Accordion title="eph fields must use AccountInfo, not Account">
    `eph` fields must use `AccountInfo<'info>`, not `Account<'info, T>`. The account doesn't exist yet at validation time, so Anchor cannot deserialize it.
  </Accordion>

  <Accordion title="Manual serialization is required after create">
    After calling `create_ephemeral_*`, you must serialize your data struct into the raw account data yourself. The macro allocates space but does not write any data.
  </Accordion>

  <Accordion title="Cannot combine eph with init">
    The macro enforces this at compile time. Use the generated `create_ephemeral_*` method instead of Anchor's `init` constraint.
  </Accordion>

  <Accordion title="Sponsor must be delegated first">
    The sponsor account needs lamports on the ER to pay ephemeral rent. It must be delegated before creating ephemeral accounts.
  </Accordion>

  <Accordion title="Top up the sponsor before delegation">
    Transfer extra SOL to the sponsor account before delegating it, so it has enough lamports to fund ephemeral accounts on the ER.
  </Accordion>

  <Accordion title="vault and magic_program are auto-injected">
    You don't need to declare them in your struct, but they appear in the IDL and must be passed from the client. Anchor resolves them automatically if named correctly.
  </Accordion>
</AccordionGroup>

***

## Learn More

<CardGroup cols={3}>
  <Card title="Ephemeral Accounts Demo" icon="github" href="https://github.com/magicblock-labs/magicblock-engine-examples/tree/main/ephemeral-account-chats/programs/ephemeral-account-chats" iconType="duotone">
    Full example program on GitHub
  </Card>

  <Card title="Delegation & Undelegation" icon="arrows-rotate" href="/pages/ephemeral-rollups-ers/introduction/ephemeral-rollup" iconType="duotone">
    How delegation and state synchronization work
  </Card>

  <Card title="Quickstart" icon="hammer" href="/pages/ephemeral-rollups-ers/how-to-guide/quickstart" iconType="duotone">
    Build your first program with Ephemeral Rollups
  </Card>
</CardGroup>
