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

# Router Integration

> Add support for rent-free AMMs on Solana.

Your existing quoting, routing, and swap logic stays the same.
The only addition: when a market has cold accounts, detect them, and prepend load instructions before the swap.

## What changes

|                  | Hot market (99%+) | Cold market                        |
| ---------------- | ----------------- | ---------------------------------- |
| Quoting          | No change         | No change                          |
| Swap instruction | No change         | No change                          |
| Transaction      | No change         | Prepend `create_load_instructions` |

***

<Accordion title="Agent skill">
  Use the [light-sdk](https://github.com/Lightprotocol/skills/tree/main/skills/light-sdk) agent skill to integrate rent-free markets into your router:

  ```
  npx skills add Lightprotocol/skills
  ```

  For orchestration, install the [general skill](https://zkcompression.com/skill.md):

  ```bash theme={null}
  npx skills add https://zkcompression.com
  ```
</Accordion>

<Tabs>
  <Tab title="Guide">
    ## Detecting cold accounts

    Add a cache for cold accounts. This can be independent from your regular account cache.

    ```rust theme={null}
    cold_cache: HashMap<[u8; 32], AccountInterface>, // Accounts that are currently cold (used for loading)
    ```

    If you stream, subscribe to accounts and transactions with the Light Token Program.

    | Subscription                                   | Detects                 |
    | :--------------------------------------------- | :---------------------- |
    | Account sub (`owner: cToken...`)               | Hot state + cold-to-hot |
    | Transaction sub (`account_include: cToken...`) | Hot-to-cold             |

    **Hot-to-cold** -- in your transaction handler, listen to accounts whose balance dropped to zero. Async-fetch the `AccountInterface`
    (which includes the `ColdContext` needed for load instructions):

    ```rust theme={null}
    Some(UpdateOneof::Transaction(tx_update)) => {
        if let Some(ref tx_info) = tx_update.transaction {
            for pubkey in find_closed_accounts(tx_info) {
                if cache.remove(&pubkey).is_some() {
                    let rpc = rpc.clone();
                    let cold_cache = cold_cache.clone();
                    tokio::spawn(async move {
                        if let Ok(Some(iface)) = rpc.get_account_interface(&pubkey, None).await {
                            cold_cache.insert(pubkey, iface);
                        }
                    });
                }
            }
        }
    }
    ```

    `find_closed_accounts` checks `pre_balances[i] > 0 && post_balances[i] == 0` across all transaction
    keys (including ALT-loaded addresses). See [full implementation](/light-token/streaming/tokens#detecting-transitions).

    **Cold-to-hot** -- your existing account subscription picks up the hot account again. No cache changes needed because account state does not change while cold.

    ```rust theme={null}
    Some(UpdateOneof::Account(account_update)) => {
        if let Some(account) = account_update.account {
            let pubkey: [u8; 32] = account.pubkey.as_slice().try_into().unwrap();
            // remove from cold cache.
            cold_cache.remove(&pubkey);
        }
    }
    ```

    For the full streaming guide, see [Streaming Token Accounts](/light-token/streaming/tokens)
    and [Streaming Mints](/light-token/streaming/mints).

    If you don't stream, call `get_multiple_account_interfaces` at swap time and check `is_cold()` to detect cold accounts.

    ***

    ## Building swap transactions with cold accounts

    When you detect cold accounts in a market (via your `cold_set` or via `is_cold()` on fetched accounts),
    fetch their `ColdContext` via `get_account_interface` and build load instructions.

    ```rust theme={null}
    use light_client::interface::{create_load_instructions, LightProgramInterface};

    // 1. Identify which accounts the swap touches
    let pubkeys = sdk.instruction_accounts(&AmmInstruction::Swap);

    // 2. Check which are cold (from your streaming cache, or fetch)
    let cold_pubkeys: Vec<_> = pubkeys.iter().filter(|p| cold_set.contains(p)).collect();

    // 3. If any are cold, fetch their ColdContext and build load instructions
    let mut ixs = vec![];
    if !cold_pubkeys.is_empty() {
        let interfaces = rpc
            .get_multiple_account_interfaces(cold_pubkeys, None)
            .await?
            .value;
        let cold: Vec<_> = interfaces.into_iter().flatten().collect();
        let specs = sdk.load_specs(&cold)?;
        ixs.extend(create_load_instructions(&specs, payer, config_pda, &rpc).await?);
    }

    // 4. Swap instruction is unchanged
    ixs.push(sdk.swap_ix(&swap_params)?);
    ```

    ***

    ## The LightProgramInterface trait

    Each rent-free AMM SDK exposes this trait. It tells you which accounts an instruction
    touches and how to build load specs for cold ones.

    ```rust theme={null}
    pub trait LightProgramInterface {
        type Variant: Pack<AccountMeta> + Clone + Debug;
        type Instruction;

        fn program_id() -> Pubkey;
        fn instruction_accounts(&self, ix: &Self::Instruction) -> Vec<Pubkey>;
        fn load_specs(
            &self,
            cold_accounts: &[AccountInterface],
        ) -> Result<Vec<AccountSpec<Self::Variant>>, Box<dyn Error>>;
    }
    ```

    * `instruction_accounts` -- returns the pubkeys the instruction reads/writes.
    * `load_specs` -- given cold `AccountInterface`s (with `ColdContext`), returns the `AccountSpec`s
      that `create_load_instructions` needs to load them.

    ***

    ## Full example

    ### Dependencies

    ```toml theme={null}
    [dependencies]
    light-client = { version = "0.23.0", features = ["v2"] }

    # AMM SDK that implements LightProgramInterface (provided by the AMM team)
    example-amm-sdk = "0.1"
    ```

    ### Code

    ```rust expandable theme={null}
    use light_client::interface::{create_load_instructions, LightProgramInterface};
    use example_amm_sdk::{ExampleAmmSdk, AmmInstruction};

    // Construct SDK from pool data (same as before -- pool data is always available,
    // hot or cold, via get_account_interface or your cache).
    let sdk = ExampleAmmSdk::new(pool_address, pool_data)?;

    // Quote works the same regardless of hot/cold.
    let quote = sdk.quote(amount_in, min_out)?;

    // Build transaction.
    let mut ixs = vec![];

    // Check if any swap accounts are cold.
    let pubkeys = sdk.instruction_accounts(&AmmInstruction::Swap);
    let cold_pubkeys: Vec<_> = pubkeys.iter().filter(|p| cold_set.contains(p)).collect();

    if !cold_pubkeys.is_empty() {
        // Fetch ColdContext for cold accounts.
        let interfaces = rpc
            .get_multiple_account_interfaces(cold_pubkeys, None)
            .await?
            .value;
        let cold: Vec<_> = interfaces.into_iter().flatten().collect();
        let specs = sdk.load_specs(&cold)?;
        ixs.extend(create_load_instructions(&specs, payer.pubkey(), config_pda, &rpc).await?);
    }

    // Swap instruction is the same as without rent-free accounts.
    ixs.push(sdk.swap_ix(&swap_params)?);

    rpc.send_transaction(&ixs, &payer).await?;
    ```

    ### Key types

    | Type                    | Source         | Purpose                                  |
    | ----------------------- | -------------- | ---------------------------------------- |
    | `AccountInterface`      | `light-client` | Account data with optional `ColdContext` |
    | `LightProgramInterface` | `light-client` | Trait that AMM SDKs implement            |
    | `AccountSpec`           | `light-client` | Input to `create_load_instructions`      |

    ### Reference implementation

    | Resource                         | Link                                                                                                             |
    | -------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
    | AMM Program                      | [cp-swap-reference](https://github.com/Lightprotocol/cp-swap-reference)                                          |
    | LightProgramInterface Trait Impl | [CpSwapSdk](https://github.com/Lightprotocol/cp-swap-reference/blob/main/programs/cp-swap/tests/program.rs#L409) |

    ***

    ## Hot vs Cold

    |          | Hot    | Cold                |
    | -------- | ------ | ------------------- |
    | On-chain | Yes    | Ledger (compressed) |
    | Quote    | Works  | Works               |
    | Swap     | Direct | Load first / Bundle |
    | Latency  | Normal | +0-200ms\*          |
    | Tx size  | Normal | +100-2400 bytes\*   |
    | CU       | Normal | +15k-400k CU\*      |

    *Depends on the number and type of cold accounts.*

    ### When does a market go cold?

    Accounts go cold after extended inactivity. Their virtual rent balance drops
    below a threshold and miners compress them onto the Solana ledger.

    They stay cold until any client loads them back in-flight via `create_load_instructions`.

    **Touching cold markets is rare.** The hot path has zero overhead.

    ***

    ## FAQ

    <Accordion title="Do I need to change my swap instructions?">
      No. Swap instructions are identical. If the market is hot, the transaction
      is the same as today. If cold, you prepend `create_load_instructions`.
    </Accordion>

    <Accordion title="Can I quote cold markets?">
      Yes. You regular account cache stays the same even if a market is cold, so you can keep quoting with it.
    </Accordion>

    <Accordion title="Do rent-free markets increase latency?">
      **Hot (common path)**: No.

      **Cold**: Loading accounts adds 1-200ms depending on whether a validity proof
      is needed. If load + swap exceed Solana's 1232 byte limit, use Jito bundles.
    </Accordion>

    <Accordion title="How long do accounts stay hot after loading?">
      Until they go inactive again. Each write resets the timer. The inactivity
      threshold is configurable by the program owner (e.g. 24h of no writes).
    </Accordion>

    <Accordion title="What if load + swap exceed Solana's tx size limit?">
      Send as a Jito bundle:

      ```rust expandable theme={null}
      use solana_sdk::{instruction::Instruction, pubkey::Pubkey, system_instruction};

      const JITO_TIP_ACCOUNTS: &[&str] = &[
          "96gYZGLnJYVFmbjzopPSU6QiEV5fGqZNyN9nmNhvrZU5",
          "HFqU5x63VTqvQss8hp11i4wVV8bD44PvwucfZ2bU7gRe",
          "Cw8CFyM9FkoMi7K7Crf6HNQqf4uEMzpKw6QNghXLvLkY",
          "ADaUMid9yfUytqMBgopwjb2DTLSokTSzL1zt6iGPaS49",
          "DfXygSm4jCyNCybVYYK6DwvWqjKee8pbDmJGcLWNDXjh",
          "ADuUkR4vqLUMWXxW9gh6D6L8pMSawimctcNZ5pGwDcEt",
          "DttWaMuVvTiduZRnguLF7jNxTgiMBZ1hyAumKUiL2KRL",
          "3AVi9Tg9Uo68tJfuvoKvqKNWKkC5wPdSSdeBnizKZ6jT",
      ];

      fn jito_tip_ix(payer: &Pubkey, tip_lamports: u64) -> Instruction {
          let tip_account = JITO_TIP_ACCOUNTS[rand::random::<usize>() % JITO_TIP_ACCOUNTS.len()]
              .parse::<Pubkey>().unwrap();
          system_instruction::transfer(payer, &tip_account, tip_lamports)
      }

      let tip_ix = jito_tip_ix(&payer.pubkey(), 10_000);
      swap_ixs.push(tip_ix);

      let bundle = vec![load_tx_base64, swap_tx_base64];
      let resp = client
          .post("https://mainnet.block-engine.jito.wtf/api/v1/bundles")
          .json(&serde_json::json!({
              "jsonrpc": "2.0",
              "id": 1,
              "method": "sendBundle",
              "params": [bundle, {"encoding": "base64"}]
          }))
          .send().await?;
      ```
    </Accordion>

    <Accordion title="Do RPC providers support get_account_interface?">
      Yes. Supported by Helius and Triton. Can also be self-hosted via the
      <a href="https://github.com/helius-labs/photon" target="_blank">open-source Photon indexer</a>.
    </Accordion>

    <Accordion title="What if the indexer is down?"> Hot markets work all the same
    as long as Solana is up. Cold accounts can't be loaded until the indexer
    recovers. Compression is cryptographically verifiable, and safety
    does not depend on the indexer. </Accordion>

    <Accordion title="I don't stream. Can I still support rent free markets?"> Yes.
    To read state, and to build txns, simply use `get_multiple_account_interfaces`
    for the instruction's accounts and check `is_cold()`. This adds a RPC round-trip
    but requires no streaming setup. </Accordion>

    ***

    <Callout type="info">
      Questions or need hands-on support? [Telegram](https://t.me/swen_light) | [email](mailto:support@lightprotocol.com) | [Discord](https://discord.com/invite/7cJ8BhAXhu)
    </Callout>
  </Tab>

  <Tab title="AI Prompt">
    <Prompt description="Integrate rent-free AMM markets into a router or aggregator" actions={["copy", "cursor"]}>
      {`---
            description: Integrate rent-free AMM markets into a router or aggregator
            allowed-tools: Bash, Read, Write, Edit, Glob, Grep, WebFetch, AskUserQuestion, Task, TaskCreate, TaskGet, TaskList, TaskUpdate, TaskOutput, mcp__deepwiki, mcp__zkcompression
            ---

            ## Integrate rent-free AMM markets into a router or aggregator

            Context:
            - Guide: https://zkcompression.com/light-token/defi/routers
            - Skills and resources index: https://zkcompression.com/skill.md
            - Dedicated skill: https://github.com/Lightprotocol/skills/tree/main/skills/light-sdk
            - Crate: light-client (features: v2) — provides AccountInterface, LightProgramInterface, create_load_instructions
            - AMM reference: https://github.com/Lightprotocol/cp-swap-reference
            - Streaming guides: https://zkcompression.com/light-token/streaming/tokens

            Key APIs: create_load_instructions(), LightProgramInterface trait, get_account_interface(), get_multiple_account_interfaces(), is_cold()

            ### 1. Index project
            - Grep \`LightProgramInterface|create_load_instructions|AccountInterface|is_cold|get_account_interface|swap|amm|router|aggregator\` across src/
            - Glob \`**/*.rs\` and \`**/Cargo.toml\` for project structure
            - Identify: existing routing/quoting logic, account caching, swap instruction building, streaming setup
            - Read Cargo.toml — note existing dependencies and light-client version
            - Task subagent (Grep/Read/WebFetch) if project has multiple crates to scan in parallel

            ### 2. Read references
            - WebFetch the guide above
            - WebFetch skill.md — check for a dedicated skill and resources matching this task
            - TaskCreate one todo per phase below to track progress

            ### 3. Clarify intention
            - AskUserQuestion: what is the goal? (add cold market support to existing router, build new router with rent-free market support, integrate a specific AMM SDK)
            - AskUserQuestion: does the project already stream account updates, or does it fetch at swap time?
            - AskUserQuestion: does the project use Jito bundles for multi-transaction swaps?
            - Summarize findings and wait for user confirmation before implementing

            ### 4. Create plan
            - Based on steps 1–3, draft an implementation plan
            - Follow the guide's progression: Cold Account Cache → Detecting Cold Accounts → Building Swap Transactions with Load Instructions
            - If streaming: add account + transaction subscriptions for the Light Token Program
            - If fetch-at-swap-time: add get_multiple_account_interfaces + is_cold() check before building swap tx
            - If anything is unclear or ambiguous, loop back to step 3 (AskUserQuestion)
            - Present the plan to the user for approval before proceeding

            ### 5. Implement
            - Add deps if missing: Bash \`cargo add light-client@0.23 --features v2\`
            - Add AMM SDK dependency per the approved plan
            - Follow the guide and the approved plan
            - Write/Edit to create or modify files
            - TaskUpdate to mark each step done

            ### 6. Verify
            - Bash \`cargo check\`
            - Bash \`cargo test\` if tests exist
            - TaskUpdate to mark complete

            ### Tools
            - mcp__zkcompression__SearchLightProtocol("<query>") for API details
            - mcp__deepwiki__ask_question("Lightprotocol/light-protocol", "<q>") for architecture
            - Task subagent with Grep/Read/WebFetch for parallel lookups
            - TaskList to check remaining work`}
    </Prompt>

    ```text theme={null}
    ---
    description: Integrate rent-free AMM markets into a router or aggregator
    allowed-tools: Bash, Read, Write, Edit, Glob, Grep, WebFetch, AskUserQuestion, Task, TaskCreate, TaskGet, TaskList, TaskUpdate, TaskOutput, mcp__deepwiki, mcp__zkcompression
    ---

    ## Integrate rent-free AMM markets into a router or aggregator

    Context:
    - Guide: https://zkcompression.com/light-token/defi/routers
    - Skills and resources index: https://zkcompression.com/skill.md
    - Dedicated skill: https://github.com/Lightprotocol/skills/tree/main/skills/light-sdk
    - Crate: light-client (features: v2) — provides AccountInterface, LightProgramInterface, create_load_instructions
    - AMM reference: https://github.com/Lightprotocol/cp-swap-reference
    - Streaming guides: https://zkcompression.com/light-token/streaming/tokens

    Key APIs: create_load_instructions(), LightProgramInterface trait, get_account_interface(), get_multiple_account_interfaces(), is_cold()

    ### 1. Index project
    - Grep `LightProgramInterface|create_load_instructions|AccountInterface|is_cold|get_account_interface|swap|amm|router|aggregator` across src/
    - Glob `**/*.rs` and `**/Cargo.toml` for project structure
    - Identify: existing routing/quoting logic, account caching, swap instruction building, streaming setup
    - Read Cargo.toml — note existing dependencies and light-client version
    - Task subagent (Grep/Read/WebFetch) if project has multiple crates to scan in parallel

    ### 2. Read references
    - WebFetch the guide above
    - WebFetch skill.md — check for a dedicated skill and resources matching this task
    - TaskCreate one todo per phase below to track progress

    ### 3. Clarify intention
    - AskUserQuestion: what is the goal? (add cold market support to existing router, build new router with rent-free market support, integrate a specific AMM SDK)
    - AskUserQuestion: does the project already stream account updates, or does it fetch at swap time?
    - AskUserQuestion: does the project use Jito bundles for multi-transaction swaps?
    - Summarize findings and wait for user confirmation before implementing

    ### 4. Create plan
    - Based on steps 1–3, draft an implementation plan
    - Follow the guide's progression: Cold Account Cache → Detecting Cold Accounts → Building Swap Transactions with Load Instructions
    - If streaming: add account + transaction subscriptions for the Light Token Program
    - If fetch-at-swap-time: add get_multiple_account_interfaces + is_cold() check before building swap tx
    - If anything is unclear or ambiguous, loop back to step 3 (AskUserQuestion)
    - Present the plan to the user for approval before proceeding

    ### 5. Implement
    - Add deps if missing: Bash `cargo add light-client@0.23 --features v2`
    - Add AMM SDK dependency per the approved plan
    - Follow the guide and the approved plan
    - Write/Edit to create or modify files
    - TaskUpdate to mark each step done

    ### 6. Verify
    - Bash `cargo check`
    - Bash `cargo test` if tests exist
    - TaskUpdate to mark complete

    ### Tools
    - mcp__zkcompression__SearchLightProtocol("<query>") for API details
    - mcp__deepwiki__ask_question("Lightprotocol/light-protocol", "<q>") for architecture
    - Task subagent with Grep/Read/WebFetch for parallel lookups
    - TaskList to check remaining work
    ```
  </Tab>
</Tabs>
