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

# Solana プログラム

> Solana 上でカウンターを委任し増加させるシンプルな Rust プログラムの書き方を学ぶ

***

### クイックアクセス

他の実装における基本的なカウンター例はこちらです。

<CardGroup cols={2}>
  <Card title="GitHub" icon="rust" href="https://github.com/magicblock-labs/magicblock-engine-examples/tree/main/rust-counter" iconType="duotone">
    Native Rust 実装
  </Card>

  <Card title="GitHub" icon="face-lying" href="https://github.com/magicblock-labs/magicblock-engine-examples/tree/main/pinocchio-counter" iconType="duotone">
    Pinocchio 実装
  </Card>

  <Card title="Guide" icon="computer" href="/jp/pages/ephemeral-rollups-ers/how-to-guide/local-development" iconType="duotone">
    ローカル開発
  </Card>
</CardGroup>

***

<div
  style={{
position: "relative",
paddingBottom: "56.25%",
height: 0,
overflow: "hidden",
}}
>
  <iframe
    src="https://www.youtube.com/embed/b77bwSGDHK0?si=Oknc5f8CuC17WBnV&list=PLWR_ZQiGMS8mIe1kPZe8OfHIbhvZqaM8V"
    title="Build a real-time Rust Counter on Solana with MagicBlock's Ephemeral Rollup | Tutorial"
    style={{
  position: "absolute",
  top: 0,
  left: 0,
  width: "100%",
  height: "100%",
}}
    frameBorder="0"
    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
    allowFullScreen
    referrerPolicy="strict-origin-when-cross-origin"
  />
</div>

***

## ステップバイステップガイド

プログラムを構築し、MagicBlock の Delegation Program `DELeGGvXpWV2fqJUhqcF5ZSYMS4JTLjteaAMARRSaeSh` を使って delegation hooks を追加しアップグレードします。

<Steps>
  <Step
    title={
  <a href="#1-write-program">
    プログラムを書き、delegation instructions を追加する
  </a>
}
  >
    いつも通りに Solana プログラムを書きます。
  </Step>

  <Step title={<a href="#2-delegate">Base Layer で PDA を Delegate する</a>}>
    CPI hooks を追加し、状態アカウントを Ephemeral Rollup セッション経由で delegate、commit、undelegate できるようにします。

    <Note>
      <p>
        これらの公開バリデータは開発用として利用できます。委任命令には、
        対象となる ER バリデータを必ず追加してください。
      </p>

      **メインネット**

      <ul>
        <li>
          アジア (as.magicblock.app):{" "}
          <code>MAS1Dt9qreoRMQ14YQuhg8UTZMMzDdKhmkZMECCzk57</code>
        </li>

        <li>
          EU (eu.magicblock.app):{" "}
          <code>MEUGGrYPxKk17hCr7wpT6s8dtNokZj5U2L57vjYMS8e</code>
        </li>

        <li>
          米国 (us.magicblock.app):{" "}
          <code>MUS3hc9TCw4cGC12vHNoYcCGzJG1txjgQLZWVoeNHNd</code>
        </li>

        <li>
          TEE (mainnet-tee.magicblock.app):{" "}
          <code>MTEWGuqxUpYZGFJQcp8tLN7x5v9BSeoFHYWQQ3n3xzo</code>
        </li>
      </ul>

      **Devnet**

      <ul>
        <li>
          アジア (devnet-as.magicblock.app):{" "}
          <code>MAS1Dt9qreoRMQ14YQuhg8UTZMMzDdKhmkZMECCzk57</code>
        </li>

        <li>
          EU (devnet-eu.magicblock.app):{" "}
          <code>MEUGGrYPxKk17hCr7wpT6s8dtNokZj5U2L57vjYMS8e</code>
        </li>

        <li>
          米国 (devnet-us.magicblock.app):{" "}
          <code>MUS3hc9TCw4cGC12vHNoYcCGzJG1txjgQLZWVoeNHNd</code>
        </li>

        <li>
          TEE (devnet-tee.magicblock.app):{" "}
          <code>MTEWGuqxUpYZGFJQcp8tLN7x5v9BSeoFHYWQQ3n3xzo</code>
        </li>
      </ul>

      **ローカルネット**

      <ul>
        <li>
          ローカル ER (localhost:7799):{" "}
          <code>mAGicPQYBMvcYveUZA5F5UNNwyHvfYh5xkLS2Fr1mev</code>
        </li>
      </ul>
    </Note>
  </Step>

  <Step title={<a href="#3-commit">ER で PDA を Commit する</a>}>
    Solana CLI を使ってプログラムを Solana に直接デプロイします。
  </Step>

  <Step title={<a href="#4-undelegate">ER で PDA を Undelegate する</a>}>
    SVM RPC 仕様に準拠したトランザクションを、変更なしでオンチェーン・オフチェーンの両方から送信します。
  </Step>
</Steps>

***

## Counter 例

以下のソフトウェアパッケージが必要になる場合があります。ほかのバージョンでも互換性がある可能性があります。

| ソフトウェア     | バージョン   | インストールガイド                                               |
| ---------- | ------- | ------------------------------------------------------- |
| **Solana** | 3.1.9   | [Solana をインストール](https://docs.anza.xyz/cli/install)     |
| **Rust**   | 1.89.0  | [Rust をインストール](https://www.rust-lang.org/tools/install) |
| **Node**   | 24.10.0 | [Node をインストール](https://nodejs.org/en/download/current)  |

<div id="code-snippets" />

### コードスニペット

<Tabs>
  <Tab title="1. Write Program">
    このプログラムは 2 つの主要 instruction を実装しています。

    1. `InitializeCounter`: カウンターを初期化し 0 に設定（Base Layer で呼ばれる）
    2. `IncreaseCounter`: 初期化済みカウンターを X 増やす（Base Layer または ER で呼ばれる）

    このプログラムは、カウンターの delegation と undelegation 用の専用 instruction も実装しています。

    1. `Delegate`: カウンターを Base Layer から ER に委任する（Base Layer で呼ばれる）
    2. `CommitAndUndelegate`: カウンターを ER から Base Layer に同期するようスケジュールし、ER 上で undelegate する（ER で呼ばれる）
    3. `Commit`: カウンターを ER から Base Layer に同期するようスケジュールする（ER で呼ばれる）
    4. `Undelegate`: Base Layer 上でカウンターを undelegate する（validator CPI を通じて Base Layer で呼ばれる）
    5. `IncrementAndCommit`: 1 つの ER トランザクションでインクリメントとコミットを行う（ER で呼ばれる）
    6. `IncrementAndUndelegate`: 1 つの ER トランザクションでインクリメント、コミット、委任解除を行う（ER で呼ばれる）

    <Note>
      The undelegation callback discriminator `[196, 28, 41, 206, 48, 37, 51, 167]`
      and its instruction processor must be specified in your program. This
      instruction triggered by Delegation Program reverts account ownership on the
      Base Layer after calling undelegation on ER.

      With [`[#ephemeral]`](/jp/pages/ephemeral-rollups-ers/how-to-guide/quickstart#1-write-program) Anchor macro from MagicBlock's Ephemeral Rollup SDK, the undelegation callback discriminator and processor are injected into your program.
    </Note>

    以下がプログラムの基本構造です。

    {" "}

    ```rust theme={null}
    use borsh::BorshDeserialize;
    use solana_program::program_error::ProgramError;

    pub enum ProgramInstruction {
        InitializeCounter,
        IncreaseCounter { increase_by: u64 },
        Delegate,
        CommitAndUndelegate,
        Commit,
        Undelegate { pda_seeds: Vec<Vec<u8>> },
        IncrementAndCommit { increase_by: u64 },
        IncrementAndUndelegate { increase_by: u64 },
    }

    #[derive(BorshDeserialize)]
    struct IncreaseCounterPayload {
        increase_by: u64,
    }

    impl ProgramInstruction {
        pub fn unpack(input: &[u8]) -> Result<Self, ProgramError> {
            // Ensure the input has at least 8 bytes for the variant
            if input.len() < 8 {
                return Err(ProgramError::InvalidInstructionData);
            }

            // Extract the first 8 bytes as variant
            let (ix_discriminator, rest) = input.split_at(8);

            // Match instruction discriminator with process and deserialize payload
            Ok(match ix_discriminator {
                [0, 0, 0, 0, 0, 0, 0, 0] => Self::InitializeCounter,
                [1, 0, 0, 0, 0, 0, 0, 0] => {
                    let payload = IncreaseCounterPayload::try_from_slice(rest)?;
                    Self::IncreaseCounter {
                        increase_by: payload.increase_by,
                    }
                }
                [2, 0, 0, 0, 0, 0, 0, 0] => Self::Delegate,
                [3, 0, 0, 0, 0, 0, 0, 0] => Self::CommitAndUndelegate,
                [4, 0, 0, 0, 0, 0, 0, 0] => Self::Commit,
                [5, 0, 0, 0, 0, 0, 0, 0] => {
                    let payload = IncreaseCounterPayload::try_from_slice(rest)?;
                    Self::IncrementAndCommit {
                        increase_by: payload.increase_by,
                    }
                }
                [6, 0, 0, 0, 0, 0, 0, 0] => {
                    let payload = IncreaseCounterPayload::try_from_slice(rest)?;
                    Self::IncrementAndUndelegate {
                        increase_by: payload.increase_by,
                    }
                }
                [196, 28, 41, 206, 48, 37, 51, 167] => {
                    let pda_seeds: Vec<Vec<u8>> = Vec::<Vec<u8>>::try_from_slice(rest)?;
                    Self::Undelegate { pda_seeds }
                }
                _ => return Err(ProgramError::InvalidInstructionData),
            })
        }
    }
    ```

    <Note>
      "Undelegate" instruction には正確な discriminator が必要です。これは
      あなた自身が呼ぶものではなく、ER 上でアカウントが undelegate された後に、
      Base Layer 上の validator が CPI コールバックとしてあなたのプログラムへ入ります。
    </Note>

    [⬆️ Back to Top](#code-snippets)
  </Tab>

  <Tab title="2. Delegate">
    ### Counter PDA を委任する

    counter PDA を委任し、Ephemeral Rollup セッション内で writable にするには、内部で `delegate_account` 関数を呼ぶ instruction を追加する必要があります。`delegate_account` は delegation program に CPI し、検証が通るとそのアカウントの所有権を得ます。
    この後、ephemeral validator は counter PDA 上でトランザクションを処理し、delegation program を通じて状態差分を提案できるようになります。

    <Card title="トランザクション（Base Layer）: Delegate" icon="magnifying-glass" href="https://solscan.io/tx/5jUdf5rsfQsbLYAahS9axrnLnEjdbUqtXUmGfgGuS7QqbYJ4FZHgXTNoT1bxPXp7XQu78r8Ebpp1RT2u9V6qsc1r?cluster=devnet" iconType="duotone">
      Solana Explorer でトランザクション詳細を見る
    </Card>

    ```rust theme={null}
    use solana_program::{
        account_info::{next_account_info, AccountInfo},
        entrypoint::ProgramResult,
        msg,
        pubkey::Pubkey,
    };
    use ephemeral_rollups_sdk::cpi::{delegate_account, DelegateAccounts, DelegateConfig};

    // For Base Layer only
    // Set specific validator based on ER, see https://docs.magicblock.gg/pages/get-started/how-integrate-your-program/local-setup
    pub fn process_delegate(_program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult {
        // Get accounts
        let account_info_iter = &mut accounts.iter();
        let initializer = next_account_info(account_info_iter)?;
        let system_program = next_account_info(account_info_iter)?;
        let pda_to_delegate = next_account_info(account_info_iter)?;
        let owner_program = next_account_info(account_info_iter)?;
        let delegation_buffer = next_account_info(account_info_iter)?;
        let delegation_record = next_account_info(account_info_iter)?;
        let delegation_metadata = next_account_info(account_info_iter)?;
        let delegation_program = next_account_info(account_info_iter)?;
        let validator_account = account_info_iter.next();

        // Optional: client-provided validator or default validator
        let validator_pubkey: Option<Pubkey> = validator_account.map(|acc_info| acc_info.key.clone());

        // Prepare counter pda seeds
        let seed_1 = b"counter";
        let seed_2 = initializer.key.as_ref();
        let pda_seeds: &[&[u8]] = &[seed_1, seed_2];

        let delegate_accounts = DelegateAccounts {
            payer: initializer,
            pda: pda_to_delegate,
            owner_program,
            buffer: delegation_buffer,
            delegation_record,
            delegation_metadata,
            delegation_program,
            system_program,
        };

        let delegate_config = DelegateConfig {
            validator: validator_pubkey, // Set delegating ER validator
            ..Default::default()
        };

        delegate_account(delegate_accounts, pda_seeds, delegate_config)?;

        Ok(())
    }
    ```

    [⬆️ Back to Top](#code-snippets)
  </Tab>

  <Tab title="3. Commit">
    ### PDA が委任中のままコミットする

    ephemeral runtime では、PDA が委任されたままでもその状態をコミットできます。これは `commit_accounts` 関数を呼び出して行います。

    <CardGroup cols={2}>
      <Card title="トランザクション（ER）: Commit" icon="magnifying-glass" href="https://solscan.io/tx/5GiFGyrnJPkEQbhE8EHVYczoj2RPGe1YSnoq1DzcUHAxpyzMUKtxG2Tc7TLkxtcCHr72ftnvmkAVMfecTaf6TCK8?cluster=custom&customUrl=https://devnet.magicblock.app" iconType="duotone">
        Solana Explorer でトランザクション詳細を見る
      </Card>

      <Card title="トランザクション（Base layer）: Commit" icon="magnifying-glass" href="https://solscan.io/tx/5fHBADq99LAEBzGoeDXGtN3ut8RBf2s5UhbDM1N6TMTpvADNeYLz8e8vinNWj1VhLhrR7UxFoW7bo2u6pBR3YRjj?cluster=devnet" iconType="duotone">
        Solana Explorer でトランザクション詳細を見る
      </Card>
    </CardGroup>

    ```rust theme={null}
    use solana_program::{
        account_info::{next_account_info, AccountInfo},
        entrypoint::ProgramResult,
        msg,
        program_error::ProgramError,
        pubkey::Pubkey,
    };
    use ephemeral_rollups_sdk::ephem::{FoldableIntentBuilder, MagicIntentBundleBuilder};

    // For ER only
    pub fn process_commit(
        _program_id: &Pubkey,
        accounts: &[AccountInfo],
    ) -> ProgramResult {
        // Get accounts
        let account_info_iter = &mut accounts.iter();
        let initializer = next_account_info(account_info_iter)?;
        let counter_account = next_account_info(account_info_iter)?;
        let magic_program = next_account_info(account_info_iter)?;
        let magic_context = next_account_info(account_info_iter)?;

        // Signer should be the same as the initializer
        if !initializer.is_signer {
            msg!("Initializer {} should be the signer", initializer.key);
            return Err(ProgramError::MissingRequiredSignature);
        }

        MagicIntentBundleBuilder::new(
            initializer.clone(),
            magic_context.clone(),
            magic_program.clone(),
        )
        .commit(&[counter_account.clone()])
        .build_and_invoke()?;

        Ok(())
    }
    ```

    [⬆️ Back to Top](#code-snippets)
  </Tab>

  <Tab title="4. Undelegate">
    ### PDA の委任解除

    PDA の委任解除は、ある instruction の一部として `commit_and_undelegate_accounts` を呼ぶことで行います。
    undelegation は最新状態をコミットし、PDA の所有権を owner program に戻します。undelegation と state finalization の後、validator は Base Layer 上で `"undelegate"` への CPI コールバックを作成します。

    <Note>
      The undelegation callback discriminator `[196, 28, 41, 206, 48, 37, 51, 167]`
      and its instruction processor must be specified in your program. This
      instruction triggered by Delegation Program reverts account ownership on the
      Base Layer after calling undelegation on ER.

      With [`[#ephemeral]`](/jp/pages/ephemeral-rollups-ers/how-to-guide/quickstart#1-write-program) Anchor macro from MagicBlock's Ephemeral Rollup SDK, the undelegation callback discriminator and processor are injected into your program.
    </Note>

    <CardGroup cols={2}>
      <Card title="トランザクション（ER）: Undelegate" icon="magnifying-glass" href="https://solscan.io/tx/bMN6AhXrGH93Uc6ALibGgjnE39hcnY57mBhYkZ8TRaKxNRvyFaweaQPBmDxPv81cgR47WTTzzhfziTUEgAT8Y5m?cluster=custom&customUrl=https://devnet.magicblock.app" iconType="duotone">
        Solana Explorer でトランザクション詳細を見る
      </Card>

      <Card title="トランザクション（Base layer）: Undelegate" icon="magnifying-glass" href="https://solscan.io/tx/8JafYWiXmd4CHc2E97WKYnaNPmChZeg8aGYY7UUWaQ7Z54N5WoMcAVivyv2vdn9wKirMkR3y4UcmFPdXYqBtKAa?cluster=devnet" iconType="duotone">
        Solana Explorer でトランザクション詳細を見る
      </Card>
    </CardGroup>

    ```rust theme={null}
    use solana_program::{
        account_info::{next_account_info, AccountInfo},
        entrypoint::ProgramResult,
        msg,
        program_error::ProgramError,
        pubkey::Pubkey,
    };
    use ephemeral_rollups_sdk::cpi::undelegate_account;
    use ephemeral_rollups_sdk::ephem::{FoldableIntentBuilder, MagicIntentBundleBuilder};

    // For ER only
    pub fn process_commit_and_undelegate(
        _program_id: &Pubkey,
        accounts: &[AccountInfo],
    ) -> ProgramResult {
        // Get accounts
        let account_info_iter = &mut accounts.iter();
        let initializer = next_account_info(account_info_iter)?;
        let counter_account = next_account_info(account_info_iter)?;
        let magic_program = next_account_info(account_info_iter)?;
        let magic_context = next_account_info(account_info_iter)?;

        // Signer should be the same as the initializer
        if !initializer.is_signer {
            msg!("Initializer {} should be the signer", initializer.key);
            return Err(ProgramError::MissingRequiredSignature);
        }

        // Commit and undelegate counter_account on ER
        MagicIntentBundleBuilder::new(
            initializer.clone(),
            magic_context.clone(),
            magic_program.clone(),
        )
        .commit_and_undelegate(&[counter_account.clone()])
        .build_and_invoke()?;

        Ok(())
    }

    // For Base Layer CPI callback
    pub fn process_undelegate(
        program_id: &Pubkey,
        accounts: &[AccountInfo],
        pda_seeds: Vec<Vec<u8>>,
    ) -> ProgramResult {
        // Get accounts
        let account_info_iter = &mut accounts.iter();
        let delegated_pda = next_account_info(account_info_iter)?;
        let delegation_buffer = next_account_info(account_info_iter)?;
        let initializer = next_account_info(account_info_iter)?;
        let system_program = next_account_info(account_info_iter)?;

        // CPI on Solana
        undelegate_account(
            delegated_pda,
            program_id,
            delegation_buffer,
            initializer,
            system_program,
            pda_seeds,
        )?;

        Ok(())
    }
    ```

    [⬆️ Back to Top](#code-snippets)
  </Tab>
</Tabs>

***

<div id="advanced-code-snippets" />

### 高度なコードスニペット

<Tabs>
  <Tab title="Resize PDA">
    委任済み PDA をリサイズする際には:

    * 新しいアカウントサイズでも rent-exempt を保てるだけの lamports が PDA に必要です。
    * 追加 lamports が必要な場合、差額を提供するために **payer account も委任済みである必要があります**。
    * PDA はプログラム所有である必要があり、lamports 転送に必要な signer をトランザクションに含める必要があります。
    * `system_instruction::allocate` を使用します。

    ```rust theme={null}
    #[derive(BorshSerialize, BorshDeserialize, Debug)]
    pub struct Counter {
        pub count: u64,
    }

    // Resize counter account
    pub fn resize_counter_account(
        counter_acc: &AccountInfo,
        payer: &AccountInfo,
        program_id: &Pubkey,
        new_size: usize,
        bump: u8,
    ) -> ProgramResult {
        let rent = Rent::get()?;
        let lamports_required = rent.minimum_balance(new_size);

        let current_lamports = counter_acc.lamports();
        if lamports_required > current_lamports {
            let lamports_to_add = lamports_required - current_lamports;
            invoke_signed(
                &system_instruction::transfer(
                    &payer.key,
                    &counter_acc.key,
                    lamports_to_add,
                ),
                &[payer.clone(), counter_acc.clone()],
                &[&[COUNTER_SEED, &[bump]]],
            )?;
        }

        // Allocate new size
        invoke_signed(
            &system_instruction::allocate(&counter_acc.key, new_size as u64),
            &[counter_acc.clone()],
            &[&[COUNTER_SEED, &[bump]]],
        )?;

        // Assign back to program
        invoke_signed(
            &system_instruction::assign(&counter_acc.key, program_id),
            &[counter_acc.clone()],
            &[&[COUNTER_SEED, &[bump]]],
        )?;

        msg!("Counter account resized to {} bytes", new_size);
        Ok(())
    }
    ```

    [⬆️ Back to Top](#advanced-code-snippets)
  </Tab>

  <Tab title="On-Curve Delegation">
    ### クイックアクセス

    <CardGroup cols={2}>
      <Card title="GitHub" icon="wallet" href="https://github.com/magicblock-labs/magicblock-engine-examples/tree/main/oncurve-delegation" iconType="duotone">
        オンカーブ委任
      </Card>
    </CardGroup>

    オンカーブアカウントを委任するために必要な署名者:

    1. 委任対象のオンカーブアカウント
    2. 手数料支払い者

    オンカーブアカウントを委任するために必要な命令:

    1. System Account を Delegation Program に割り当てる
    2. Delegation Program に委任する

    <CodeGroup>
      ```typescript Kit theme={null}
      // Create assign instruction
      // The on-curve account must sign this instruction to change its owner
      const accountSigner = await cryptoKeyPairToTransactionSigner(userKeypair);
      const delegationProgramAddress = address(DELEGATION_PROGRAM_ID.toString());
      const assignInstruction = getAssignInstruction({
        account: accountSigner,
        programAddress: delegationProgramAddress,
      });

      // Create delegate instruction
      const delegateInstruction = await createDelegateInstruction({
        payer: feePayerAddress,
        delegatedAccount: userAddress,
        ownerProgram: ownerProgramAddress,
        validator: validatorAddress,
      });

      // Prepare transaction
      const transactionMessage = pipe(
        createTransactionMessage({ version: 0 }),
        (tx) => setTransactionMessageFeePayer(feePayerAddress, tx),
        (tx) =>
          appendTransactionMessageInstructions(
            [assignInstruction, delegateInstruction],
            tx
          )
      );
      // Send and confirm transaction (fee payer need to sign, on-curve account cannot be signer since delegated)
      const txHash = await connection.sendAndConfirmTransaction(
        transactionMessage,
        [userKeypair, feePayerKeypair],
        { commitment: "confirmed", skipPreflight: true }
      );
      ```

      ```typescript Web3.js theme={null}
      // Create assign instruction
      const assignInstruction = SystemProgram.assign({
        accountPubkey: userPubkey,
        programId: DELEGATION_PROGRAM_ID,
      });

      // Create delegate instruction
      const delegateInstruction = createDelegateInstruction({
        payer: feePayerKeypair.publicKey,
        delegatedAccount: userPubkey,
        ownerProgram: ownerProgram,
        validator: validator,
      });

      // Create and send transaction (fee payer need to sign, on-curve account cannot be signer since delegated)
      const tx = new Transaction().add(assignInstruction, delegateInstruction);
      tx.feePayer = feePayerKeypair.publicKey;
      const txSignature = await sendAndConfirmTransaction(
        connectionBaseLayer,
        tx,
        [userKeypair, feePayerKeypair],
        {
          skipPreflight: true,
        }
      );
      ```
    </CodeGroup>

    直接の commit と undelegate は Magic Program 経由でのみ行います。

    <CodeGroup>
      ```typescript Kit theme={null}
      // Create commit and undelegate instruction
      const commitAndUndelegateInstruction = createCommitAndUndelegateInstruction(
        userAddress,
        [userAddress]
      );

      // Prepare transaction
      const transactionMessage = pipe(
        createTransactionMessage({ version: 0 }),
        (tx) => setTransactionMessageFeePayer(feePayerAddress, tx),
        (tx) =>
          appendTransactionMessageInstructions([commitAndUndelegateInstruction], tx)
      );

      // Send and confirm transaction on ephemeral connection
      const txHash = await ephemeralConnection.sendAndConfirmTransaction(
        transactionMessage,
        [userKeypair, feePayerKeypair],
        { commitment: "confirmed", skipPreflight: true }
      );
      ```

      ```typescript Web3.js theme={null}
      // Create commit and undelegate instruction
      const commitAndUndelegateInstruction = createCommitAndUndelegateInstruction(
        userPubkey,
        [userPubkey]
      );

      // Send and confirm transaction on ephemeral connection
      const tx = new Transaction().add(commitAndUndelegateInstruction);
      tx.feePayer = feePayerKeypair.publicKey;
      const txSignature = await sendAndConfirmTransaction(
        ephemeralConnection,
        tx,
        [userKeypair, feePayerKeypair],
        {
          skipPreflight: true,
        }
      );
      ```
    </CodeGroup>

    [⬆️ Back to Top](#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="/jp/pages/overview/additional-information/system-status" iconType="duotone">
    Subscribe to MagicBlock Server Status
  </Card>
</CardGroup>

***
