Documentation Index
Fetch the complete documentation index at: https://mintlify.com/magicblock-labs/magicblock-engine-examples/llms.txt
Use this file to discover all available pages before exploring further.
Overview
The TypeScript SDK provides functions and utilities to interact with MagicBlock Ephemeral Rollups from client applications. It includes delegation helpers, PDA derivation, connection management, and Bolt ECS integration.
Installation
npm install @magicblock-labs/ephemeral-rollups-sdk
For Bolt ECS applications:
npm install @magicblock-labs/bolt-sdk
Core Exports
Constants
import {
DELEGATION_PROGRAM_ID,
MAGIC_CONTEXT_ID,
MAGIC_PROGRAM_ID
} from "@magicblock-labs/ephemeral-rollups-sdk";
The program ID of the delegation program used to delegate accounts to Ephemeral Rollups
The program ID of the magic program used for committing accounts from ER back to Solana
The magic context account required for commit operations
Connection Management
Setting up Connections
Establish separate connections for Solana base layer and Ephemeral Rollups:
import { Connection } from "@solana/web3.js";
// Base layer connection (Solana)
const connectionBaseLayer = new Connection(
"https://api.devnet.solana.com",
{ wsEndpoint: "wss://api.devnet.solana.com" }
);
// Ephemeral Rollup connection
const connectionEphemeralRollup = new Connection(
"https://devnet-as.magicblock.app/",
{ wsEndpoint: "wss://devnet-as.magicblock.app/" }
);
import { Connection } from "@magicblock-labs/ephemeral-rollups-kit";
// Base layer connection
const connection = await Connection.create(
"https://api.devnet.solana.com",
"wss://api.devnet.solana.com"
);
// Ephemeral Rollup connection
const ephemeralConnection = await Connection.create(
"https://devnet-as.magicblock.app",
"wss://devnet-as.magicblock.app"
);
PDA Helpers
delegationRecordPdaFromDelegatedAccount
Derives the delegation record PDA for a delegated account.
import { delegationRecordPdaFromDelegatedAccount } from "@magicblock-labs/ephemeral-rollups-sdk";
const delegationRecord = delegationRecordPdaFromDelegatedAccount(counterPda);
The account being delegated
The derived delegation record PDA
Derives the delegation metadata PDA for a delegated account.
import { delegationMetadataPdaFromDelegatedAccount } from "@magicblock-labs/ephemeral-rollups-sdk";
const delegationMetadata = delegationMetadataPdaFromDelegatedAccount(counterPda);
The account being delegated
The derived delegation metadata PDA
delegateBufferPdaFromDelegatedAccountAndOwnerProgram
Derives the delegation buffer PDA for a delegated account and its owner program.
import { delegateBufferPdaFromDelegatedAccountAndOwnerProgram } from "@magicblock-labs/ephemeral-rollups-sdk";
const delegationBuffer = delegateBufferPdaFromDelegatedAccountAndOwnerProgram(
counterPda,
PROGRAM_ID
);
The account being delegated
The program that owns the delegated account
The derived delegation buffer PDA
Delegation Instructions
While programs typically handle delegation via CPI, you can construct delegation instructions manually for testing or custom flows.
Manual Delegation Example
import {
Transaction,
TransactionInstruction,
SystemProgram,
sendAndConfirmTransaction
} from "@solana/web3.js";
import {
DELEGATION_PROGRAM_ID,
delegationRecordPdaFromDelegatedAccount,
delegationMetadataPdaFromDelegatedAccount,
delegateBufferPdaFromDelegatedAccountAndOwnerProgram
} from "@magicblock-labs/ephemeral-rollups-sdk";
const tx = new Transaction();
// Build the delegate instruction
const keys = [
// Payer
{
pubkey: userKeypair.publicKey,
isSigner: true,
isWritable: true,
},
// System Program
{
pubkey: SystemProgram.programId,
isSigner: false,
isWritable: false,
},
// Account to delegate
{
pubkey: counterPda,
isSigner: false,
isWritable: true,
},
// Owner Program
{
pubkey: PROGRAM_ID,
isSigner: false,
isWritable: false,
},
// Delegation Buffer
{
pubkey: delegateBufferPdaFromDelegatedAccountAndOwnerProgram(
counterPda,
PROGRAM_ID
),
isSigner: false,
isWritable: true,
},
// Delegation Record
{
pubkey: delegationRecordPdaFromDelegatedAccount(counterPda),
isSigner: false,
isWritable: true,
},
// Delegation Metadata
{
pubkey: delegationMetadataPdaFromDelegatedAccount(counterPda),
isSigner: false,
isWritable: true,
},
// Delegation Program
{
pubkey: DELEGATION_PROGRAM_ID,
isSigner: false,
isWritable: false,
},
];
const delegateIx = new TransactionInstruction({
keys: keys,
programId: PROGRAM_ID,
data: Buffer.from("02", "hex"), // Your program's delegate discriminator
});
tx.add(delegateIx);
const txHash = await sendAndConfirmTransaction(
connectionBaseLayer,
tx,
[userKeypair],
{ skipPreflight: true, commitment: "confirmed" }
);
import {
AccountRole,
createTransactionMessage,
appendTransactionMessageInstructions,
pipe,
setTransactionMessageFeePayer,
Instruction
} from '@solana/kit';
import { SYSTEM_PROGRAM_ADDRESS } from "@solana-program/system";
import {
DELEGATION_PROGRAM_ID,
delegationRecordPdaFromDelegatedAccount,
delegationMetadataPdaFromDelegatedAccount,
delegateBufferPdaFromDelegatedAccountAndOwnerProgram
} from "@magicblock-labs/ephemeral-rollups-kit";
const accounts = [
{ address: userPubkey, role: AccountRole.WRITABLE_SIGNER },
{ address: SYSTEM_PROGRAM_ADDRESS, role: AccountRole.READONLY },
{ address: counterPda, role: AccountRole.WRITABLE },
{ address: PROGRAM_ID, role: AccountRole.READONLY },
{
address: await delegateBufferPdaFromDelegatedAccountAndOwnerProgram(
counterPda,
PROGRAM_ID
),
role: AccountRole.WRITABLE
},
{
address: await delegationRecordPdaFromDelegatedAccount(counterPda),
role: AccountRole.WRITABLE
},
{
address: await delegationMetadataPdaFromDelegatedAccount(counterPda),
role: AccountRole.WRITABLE
},
{ address: DELEGATION_PROGRAM_ID, role: AccountRole.READONLY },
];
const delegateIx: Instruction = {
accounts,
programAddress: PROGRAM_ID,
data: Buffer.from("02", "hex"), // Your program's delegate discriminator
};
const transactionMessage = pipe(
createTransactionMessage({ version: 0 }),
tx => setTransactionMessageFeePayer(userPubkey, tx),
tx => appendTransactionMessageInstructions([delegateIx], tx)
);
const txHash = await connection.sendAndConfirmTransaction(
transactionMessage,
[userKeypair],
{ commitment: "confirmed", skipPreflight: true }
);
Commitment Operations
GetCommitmentSignature
Waits for and returns the base layer commitment signature after committing state from ER.
import { GetCommitmentSignature } from "@magicblock-labs/ephemeral-rollups-sdk";
// After sending a commit transaction on ER
const txHash = await sendAndConfirmTransaction(
connectionEphemeralRollup,
commitTx,
[userKeypair]
);
// Wait for the commitment on base layer
const txCommitSignature = await GetCommitmentSignature(
txHash,
connectionEphemeralRollup
);
console.log(`Base layer commit signature: ${txCommitSignature}`);
// After sending a commit transaction on ER
const txHash = await ephemeralConnection.sendAndConfirmTransaction(
transactionMessage,
[userKeypair]
);
// Wait for the commitment on base layer
const txCommitSignature = await ephemeralConnection.getCommitmentSignature(
txHash
);
console.log(`Base layer commit signature: ${txCommitSignature}`);
The transaction hash of the commit transaction sent on the Ephemeral Rollup
The Ephemeral Rollup connection instance
The transaction signature on the base layer Solana chain
Commit Transaction Example
import {
Transaction,
TransactionInstruction,
sendAndConfirmTransaction
} from "@solana/web3.js";
import {
MAGIC_PROGRAM_ID,
MAGIC_CONTEXT_ID,
GetCommitmentSignature
} from "@magicblock-labs/ephemeral-rollups-sdk";
const tx = new Transaction();
const keys = [
// Payer
{
pubkey: userKeypair.publicKey,
isSigner: true,
isWritable: true,
},
// Account to commit
{
pubkey: counterPda,
isSigner: false,
isWritable: true,
},
// Magic Program
{
pubkey: MAGIC_PROGRAM_ID,
isSigner: false,
isWritable: false,
},
// Magic Context
{
pubkey: MAGIC_CONTEXT_ID,
isSigner: false,
isWritable: true,
}
];
const commitIx = new TransactionInstruction({
keys: keys,
programId: PROGRAM_ID,
data: Buffer.from("04", "hex"), // Commit instruction discriminator
});
tx.add(commitIx);
const txHash = await sendAndConfirmTransaction(
connectionEphemeralRollup,
tx,
[userKeypair],
{ skipPreflight: true, commitment: "confirmed" }
);
// Wait for base layer confirmation
const txCommitSgn = await GetCommitmentSignature(
txHash,
connectionEphemeralRollup
);
console.log(`Committed to base layer: ${txCommitSgn}`);
import {
AccountRole,
createTransactionMessage,
appendTransactionMessageInstructions,
pipe,
setTransactionMessageFeePayer,
address,
Instruction
} from '@solana/kit';
import {
MAGIC_PROGRAM_ID,
MAGIC_CONTEXT_ID
} from "@magicblock-labs/ephemeral-rollups-kit";
const accounts = [
{ address: userPubkey, role: AccountRole.WRITABLE_SIGNER },
{ address: counterPda, role: AccountRole.WRITABLE },
{ address: address(MAGIC_PROGRAM_ID.toString()), role: AccountRole.READONLY },
{ address: address(MAGIC_CONTEXT_ID.toString()), role: AccountRole.WRITABLE }
];
const commitIx: Instruction = {
accounts,
programAddress: PROGRAM_ID,
data: Buffer.from("04", "hex"), // Commit instruction discriminator
};
const transactionMessage = pipe(
createTransactionMessage({ version: 0 }),
tx => setTransactionMessageFeePayer(userPubkey, tx),
tx => appendTransactionMessageInstructions([commitIx], tx)
);
const txHash = await ephemeralConnection.sendAndConfirmTransaction(
transactionMessage,
[userKeypair],
{ commitment: "confirmed", skipPreflight: true }
);
// Wait for base layer confirmation
const txCommitSgn = await ephemeralConnection.getCommitmentSignature(txHash);
console.log(`Committed to base layer: ${txCommitSgn}`);
Bolt SDK (ECS)
For Bolt Entity Component System applications, use the Bolt SDK for specialized ECS operations.
Installation
npm install @magicblock-labs/bolt-sdk
Imports
import {
InitializeNewWorld,
AddEntity,
InitializeComponent,
ApplySystem,
FindComponentPda,
createDelegateInstruction,
createUndelegateInstruction,
DELEGATION_PROGRAM_ID
} from "@magicblock-labs/bolt-sdk";
ApplySystem
Executes a system on entities with specified components in the Bolt ECS framework.
import { ApplySystem } from "@magicblock-labs/bolt-sdk";
const applySystem = await ApplySystem({
authority: provider.wallet.publicKey,
world: worldPda,
entities: [
{
entity: entityPda,
components: [{ componentId: counterComponent.programId }],
},
],
systemId: systemIncrease.programId,
});
const tx = applySystem.transaction;
const txSign = await providerEphemeralRollup.sendAndConfirm(
tx,
[],
{ skipPreflight: true }
);
The wallet with authority to execute the system
The world PDA in which the system operates
entities
Array<{ entity: PublicKey, components: Array<{ componentId: PublicKey }> }>
required
Array of entities and their components that the system will operate on
The program ID of the system to execute
The constructed transaction ready to be signed and sent
FindComponentPda
Finds the PDA for a component attached to an entity.
import { FindComponentPda } from "@magicblock-labs/bolt-sdk";
const counterPda = FindComponentPda({
componentId: counterComponent.programId,
entity: entityPda,
});
The program ID of the component
The derived component PDA
createDelegateInstruction
Creates an instruction to delegate a Bolt component to an Ephemeral Rollup.
import { createDelegateInstruction } from "@magicblock-labs/bolt-sdk";
const counterPda = FindComponentPda({
componentId: counterComponent.programId,
entity: entityPda,
});
const delegateIx = createDelegateInstruction({
entity: entityPda,
account: counterPda,
ownerProgram: counterComponent.programId,
payer: provider.wallet.publicKey,
});
const tx = new anchor.web3.Transaction().add(delegateIx);
const txSign = await provider.sendAndConfirm(tx);
The entity that owns the component
The component account to delegate
The component’s program ID
The account paying for the delegation
The delegation instruction
createUndelegateInstruction
Creates an instruction to undelegate a Bolt component from an Ephemeral Rollup.
import { createUndelegateInstruction } from "@magicblock-labs/bolt-sdk";
const counterComponentPda = FindComponentPda({
componentId: counterComponent.programId,
entity: entityPda,
});
const undelegateIx = createUndelegateInstruction({
payer: provider.wallet.publicKey,
delegatedAccount: counterComponentPda,
componentPda: counterComponent.programId,
});
let tx = new anchor.web3.Transaction().add(undelegateIx);
const txSign = await providerEphemeralRollup.sendAndConfirm(tx);
The account paying for the undelegation
The component account to undelegate
The component’s program ID
The undelegation instruction
Complete Example
Here’s a complete workflow demonstrating delegation, execution on ER, and undelegation:
import { Connection, Keypair, PublicKey } from "@solana/web3.js";
import {
DELEGATION_PROGRAM_ID,
MAGIC_PROGRAM_ID,
MAGIC_CONTEXT_ID,
delegationRecordPdaFromDelegatedAccount,
delegationMetadataPdaFromDelegatedAccount,
delegateBufferPdaFromDelegatedAccountAndOwnerProgram,
GetCommitmentSignature
} from "@magicblock-labs/ephemeral-rollups-sdk";
// Setup connections
const baseConnection = new Connection("https://api.devnet.solana.com");
const erConnection = new Connection("https://devnet-as.magicblock.app/");
const userKeypair = Keypair.generate();
const [counterPda] = PublicKey.findProgramAddressSync(
[Buffer.from("counter"), userKeypair.publicKey.toBuffer()],
PROGRAM_ID
);
// 1. Delegate to ER (on base layer)
const delegateTx = await buildDelegateTransaction(
userKeypair.publicKey,
counterPda,
PROGRAM_ID
);
const delegateSig = await sendAndConfirmTransaction(
baseConnection,
delegateTx,
[userKeypair]
);
// 2. Execute transactions on ER
const incrementTx = await buildIncrementTransaction(
userKeypair.publicKey,
counterPda
);
const incrementSig = await sendAndConfirmTransaction(
erConnection,
incrementTx,
[userKeypair]
);
// 3. Commit and undelegate (on ER)
const commitTx = await buildCommitAndUndelegateTransaction(
userKeypair.publicKey,
counterPda
);
const commitSig = await sendAndConfirmTransaction(
erConnection,
commitTx,
[userKeypair]
);
// 4. Wait for base layer commitment
const baseSig = await GetCommitmentSignature(commitSig, erConnection);
console.log(`State committed to base layer: ${baseSig}`);
Network Endpoints
Devnet
- Base Layer:
https://api.devnet.solana.com (wss: wss://api.devnet.solana.com)
- Ephemeral Rollups:
https://devnet-as.magicblock.app/ (wss: wss://devnet-as.magicblock.app/)
Localnet
When running against localnet, you may need to include the validator identity as a remaining account:
const remainingAccounts = connection.rpcEndpoint.includes("localhost") ||
connection.rpcEndpoint.includes("127.0.0.1")
? [
{
pubkey: new PublicKey("mAGicPQYBMvcYveUZA5F5UNNwyHvfYh5xkLS2Fr1mev"),
isSigner: false,
isWritable: false,
},
]
: [];