diff --git a/pages/ephemeral-rollups-ers/how-to-guide/quickstart.mdx b/pages/ephemeral-rollups-ers/how-to-guide/quickstart.mdx
index 70f8586..10566c0 100644
--- a/pages/ephemeral-rollups-ers/how-to-guide/quickstart.mdx
+++ b/pages/ephemeral-rollups-ers/how-to-guide/quickstart.mdx
@@ -172,10 +172,13 @@ Build your program and upgrade it with delegation hooks with MagicBlock's Delega
- Now you’re program is upgraded and ready! Build and deploy to the desired
- cluster:
+ Now you’re program is upgraded and ready! Fund your deployer wallet, then build and deploy to the
+ desired cluster:
```bash
+ # Devnet: airdrop SOL to your configured keypair before deploying
+ solana airdrop 2 --url https://api.devnet.solana.com
+
anchor build && anchor deploy
```
diff --git a/pages/tools/session-keys/integrating-sessions-in-your-program.mdx b/pages/tools/session-keys/integrating-sessions-in-your-program.mdx
index 4c9d16f..76c866c 100644
--- a/pages/tools/session-keys/integrating-sessions-in-your-program.mdx
+++ b/pages/tools/session-keys/integrating-sessions-in-your-program.mdx
@@ -11,7 +11,7 @@ First, add the session-keys crate to your Cargo.toml:
```toml
[dependencies]
-session-keys = { version = "1.0.0", features = ["no-entrypoint"] }
+session-keys = { version = "3.1.1", features = ["no-entrypoint"] }
```
Or use the cargo command:
diff --git a/pages/tools/session-keys/usage-examples.mdx b/pages/tools/session-keys/usage-examples.mdx
index 74234d5..70309c6 100644
--- a/pages/tools/session-keys/usage-examples.mdx
+++ b/pages/tools/session-keys/usage-examples.mdx
@@ -86,4 +86,4 @@ Revoking a session ensures that the ephemeral key pair is no longer valid and us
These examples should help you get started with implementing session management and wallet functionality in your app.
-_**Please refer to the "Create Post" section in the**_ [_**Example App**_](https://github.com/gumhq/gum-example-app) _**to see how the Session Token implementation is done.**_
+_**Please refer to the**_ [_**session-keys example**_](https://github.com/magicblock-labs/magicblock-engine-examples/tree/main/session-keys) _**to see how the Session Token implementation is done.**_
diff --git a/pages/tools/session-keys/use-sessionkey-manager.mdx b/pages/tools/session-keys/use-sessionkey-manager.mdx
index 5a2701f..d309369 100644
--- a/pages/tools/session-keys/use-sessionkey-manager.mdx
+++ b/pages/tools/session-keys/use-sessionkey-manager.mdx
@@ -64,7 +64,7 @@ Here's an example of how to use the `useSessionKeyManager`:
```typescript
import { useAnchorWallet, useConnection } from '@solana/wallet-adapter-react';
-import { useSessionKeyManager } from '@gumhq/react-sdk';
+import { useSessionKeyManager } from '@magicblock-labs/gum-react-sdk';
function YourComponent() {
const wallet = useAnchorWallet();
diff --git a/pages/verifiable-randomness-functions-vrfs/how-to-guide/quickstart.mdx b/pages/verifiable-randomness-functions-vrfs/how-to-guide/quickstart.mdx
index b18ca4e..6efcfac 100644
--- a/pages/verifiable-randomness-functions-vrfs/how-to-guide/quickstart.mdx
+++ b/pages/verifiable-randomness-functions-vrfs/how-to-guide/quickstart.mdx
@@ -90,18 +90,18 @@ Any Solana program can request and consume verifiable randomness onchain within
- 1. Add `ephemeral_vrf_sdk` with Anchor features to your program
+ 1. Add `ephemeral-rollups-sdk` with the `anchor` and `vrf` features to your program
```bash
- cargo add ephemeral_vrf_sdk --features anchor
+ cargo add ephemeral-rollups-sdk --features anchor,vrf
```
- Import `vrf` , `create_request_randomness_ix`, `RequestRandomnessParams`, and `SerializableAccountMeta`:
+ Import the `vrf` and `vrf_callback` macros, `create_request_scoped_randomness_ix`, `RequestRandomnessParams`, and `SerializableAccountMeta`:
```rust
- use ephemeral_vrf_sdk::anchor::vrf;
- use ephemeral_vrf_sdk::instructions::{create_request_randomness_ix, RequestRandomnessParams};
- use ephemeral_vrf_sdk::types::SerializableAccountMeta;
+ use ephemeral_rollups_sdk::anchor::{vrf, vrf_callback};
+ use ephemeral_rollups_sdk::vrf::instructions::{create_request_scoped_randomness_ix, RequestRandomnessParams};
+ use ephemeral_rollups_sdk::vrf::types::SerializableAccountMeta;
```
2. Add instructions `roll_dice` to request randomness and `callback_roll_dice` to consume randomness, along with its context:
@@ -109,7 +109,7 @@ Any Solana program can request and consume verifiable randomness onchain within
- **VRF SDK constants** (`ephemeral_vrf_sdk::consts`) — reference these instead of hardcoding addresses, both in your program and in client/test code:
+ **VRF SDK constants** (`ephemeral_rollups_sdk::vrf::consts`) — reference these instead of hardcoding addresses, both in your program and in client/test code:
| Constant | Purpose | Address |
| --- | --- | --- |
@@ -145,7 +145,7 @@ Any Solana program can request and consume verifiable randomness onchain within
anchor test --skip-build --skip-deploy --skip-local-validator
```
- **VRF SDK constants** (`ephemeral_vrf_sdk::consts`) — reference these instead of hardcoding addresses, in both your program and client/test code:
+ **VRF SDK constants** (`ephemeral_rollups_sdk::vrf::consts`) — reference these instead of hardcoding addresses, in both your program and client/test code:
| Constant | Purpose | Address |
| --- | --- | --- |
diff --git a/pages/verifiable-randomness-functions-vrfs/introduction/security.mdx b/pages/verifiable-randomness-functions-vrfs/introduction/security.mdx
index 203ff1c..abbc03d 100644
--- a/pages/verifiable-randomness-functions-vrfs/introduction/security.mdx
+++ b/pages/verifiable-randomness-functions-vrfs/introduction/security.mdx
@@ -6,7 +6,7 @@ description: "How Solana VRF randomness proofs are verified"
Solana VRF proofs are cryptographically bound to the input `caller_seed` and to MagicBlock's VRF signer identity. Your callback enforces this with:
```rust
-#[account(address = ephemeral_vrf_sdk::consts::VRF_PROGRAM_IDENTITY)]
+#[account(address = ephemeral_rollups_sdk::vrf::consts::VRF_PROGRAM_IDENTITY)]
pub vrf_program_identity: Signer<'info>,
```
diff --git a/pages/verifiable-randomness-functions-vrfs/introduction/solana-vrf.mdx b/pages/verifiable-randomness-functions-vrfs/introduction/solana-vrf.mdx
index 7d4a7d4..e2ddfa7 100644
--- a/pages/verifiable-randomness-functions-vrfs/introduction/solana-vrf.mdx
+++ b/pages/verifiable-randomness-functions-vrfs/introduction/solana-vrf.mdx
@@ -66,7 +66,7 @@ That proof makes the result auditable. Users and programs can verify that a rand
Traditional randomness sources fall short on-chain: users cannot verify how off-chain numbers were generated, validators or operators can influence blockhash-based outcomes, and off-chain delivery does not always align with on-chain execution timing. Solana VRF addresses this:
-- **Built for Solana programs**: request randomness through the `ephemeral_vrf_sdk` and consume the result in your own callback instruction.
+- **Built for Solana programs**: request randomness through the `ephemeral-rollups-sdk` VRF module and consume the result in your own callback instruction.
- **Designed for real-time apps**: MagicBlock's ephemeral rollup execution model keeps randomness delivery low-latency for games and interactive flows.
- **Verifiable by design**: proofs are validated on-chain before your callback logic runs.
- **Open source and audited**: the VRF program is public, with audit coverage linked from the security docs.
diff --git a/pages/verifiable-randomness-functions-vrfs/introduction/technical-details.mdx b/pages/verifiable-randomness-functions-vrfs/introduction/technical-details.mdx
index 0702d3e..4a8a36b 100644
--- a/pages/verifiable-randomness-functions-vrfs/introduction/technical-details.mdx
+++ b/pages/verifiable-randomness-functions-vrfs/introduction/technical-details.mdx
@@ -23,7 +23,7 @@ The flow starts with a “Request for randomness”.
## Oracle queues
-Every randomness request names an **oracle queue** account (the `oracle_queue` field of `RequestRandomnessParams`). Like every Solana account, the queue lives on Solana — but a **delegated** queue is directly writable only from inside an ephemeral rollup, while a **non-delegated** queue is directly writable on the base layer. Request randomness from the queue that matches where your transaction runs — the base-layer queue from Solana, or the delegated queue from inside the ephemeral rollup. Reference the SDK constants from `ephemeral_vrf_sdk::consts` instead of hardcoding addresses wherever possible.
+Every randomness request names an **oracle queue** account (the `oracle_queue` field of `RequestRandomnessParams`). Like every Solana account, the queue lives on Solana — but a **delegated** queue is directly writable only from inside an ephemeral rollup, while a **non-delegated** queue is directly writable on the base layer. Request randomness from the queue that matches where your transaction runs — the base-layer queue from Solana, or the delegated queue from inside the ephemeral rollup. Reference the SDK constants from `ephemeral_rollups_sdk::vrf::consts` instead of hardcoding addresses wherever possible.
| Network | Base-layer queue | Delegated queue (ephemeral rollup) |
| -------- | --------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- |
diff --git a/snippets/notes/endpoints.mdx b/snippets/notes/endpoints.mdx
index d1a6ec2..4965f6e 100644
--- a/snippets/notes/endpoints.mdx
+++ b/snippets/notes/endpoints.mdx
@@ -2,7 +2,9 @@
These public RPC endpoints are currently free and supported for development:
Magic Router Devnet: https://devnet-router.magicblock.app
Solana Devnet: https://api.devnet.solana.com
- ER Devnet: https://devnet.magicblock.app
+ ER Devnet (Asia): https://devnet-as.magicblock.app
+ ER Devnet (EU): https://devnet-eu.magicblock.app
+ ER Devnet (US): https://devnet-us.magicblock.app
TEE Devnet: https://devnet-tee.magicblock.app/
Find out more details{" "}
here
diff --git a/snippets/roll-dice-code/request-consume-randomness.mdx b/snippets/roll-dice-code/request-consume-randomness.mdx
index 70bc7f3..d0acd8f 100644
--- a/snippets/roll-dice-code/request-consume-randomness.mdx
+++ b/snippets/roll-dice-code/request-consume-randomness.mdx
@@ -1,4 +1,13 @@
```rust
+use ephemeral_rollups_sdk::{
+ anchor::{vrf, vrf_callback},
+ vrf::{
+ self,
+ instructions::{create_request_scoped_randomness_ix, RequestRandomnessParams},
+ types::SerializableAccountMeta,
+ },
+};
+
#[program]
pub mod random_dice {
use super::*;
@@ -8,7 +17,7 @@ pub mod random_dice {
// Request Randomness
pub fn roll_dice(ctx: Context, client_seed: u8) -> Result<()> {
msg!("Requesting randomness...");
- let ix = create_request_randomness_ix(RequestRandomnessParams {
+ let ix = create_request_scoped_randomness_ix(RequestRandomnessParams {
payer: ctx.accounts.payer.key(),
oracle_queue: ctx.accounts.oracle_queue.key(),
callback_program_id: ID,
@@ -20,6 +29,7 @@ pub mod random_dice {
is_signer: false,
is_writable: true,
}]),
+ callback_args: Some(vec![client_seed]),
..Default::default()
});
ctx.accounts
@@ -31,8 +41,10 @@ pub mod random_dice {
pub fn callback_roll_dice(
ctx: Context,
randomness: [u8; 32],
+ client_seed: u8,
) -> Result<()> {
- let rnd_u8 = ephemeral_vrf_sdk::rnd::random_u8_with_range(&randomness, 1, 6);
+ msg!("client_seed={}", client_seed);
+ let rnd_u8 = vrf::rnd::random_u8_with_range(&randomness, 1, 6);
msg!("Consuming random number: {:?}", rnd_u8);
let player = &mut ctx.accounts.player;
player.last_result = rnd_u8; // Update the player's last result
@@ -48,16 +60,22 @@ pub struct DoRollDiceCtx<'info> {
#[account(seeds = [PLAYER, payer.key().to_bytes().as_slice()], bump)]
pub player: Account<'info, Player>,
/// CHECK: The oracle queue
- #[account(mut, address = ephemeral_vrf_sdk::consts::DEFAULT_QUEUE)]
- pub oracle_queue: AccountInfo<'info>,
+ #[account(
+ mut,
+ constraint =
+ oracle_queue.key() == vrf::consts::DEFAULT_QUEUE || // Devnet
+ oracle_queue.key() == vrf::consts::DEFAULT_TEST_QUEUE || // Local
+ oracle_queue.key() == vrf::consts::DEFAULT_EPHEMERAL_QUEUE || // ER Devnet
+ oracle_queue.key() == vrf::consts::DEFAULT_EPHEMERAL_TEST_QUEUE // ER Local
+ )]
+ pub oracle_queue: UncheckedAccount<'info>,
}
+// `#[vrf_callback]` enforces that only the VRF program (via CPI) can invoke the
+// callback — omitting it leaves the callback spoofable by any caller.
+#[vrf_callback]
#[derive(Accounts)]
pub struct CallbackRollDiceCtx<'info> {
- /// This check ensure that the vrf_program_identity (which is a PDA) is a singer
- /// enforcing the callback is executed by the VRF program trough CPI
- #[account(address = ephemeral_vrf_sdk::consts::VRF_PROGRAM_IDENTITY)]
- pub vrf_program_identity: Signer<'info>,
#[account(mut)]
pub player: Account<'info, Player>,
}
diff --git a/snippets/roll-dice-code/test.mdx b/snippets/roll-dice-code/test.mdx
index 1659eed..17a79b6 100644
--- a/snippets/roll-dice-code/test.mdx
+++ b/snippets/roll-dice-code/test.mdx
@@ -2,29 +2,80 @@
import * as anchor from "@coral-xyz/anchor";
import { Program, web3 } from "@coral-xyz/anchor";
import { RandomDice } from "../target/types/random_dice";
+import { PublicKey } from "@solana/web3.js";
+
+// Devnet base-layer VRF queue (override with VRF_BASE_QUEUE for local runs)
+const DEFAULT_BASE_QUEUE = new PublicKey(
+ process.env.VRF_BASE_QUEUE || "Cuj97ggrhhidhbu39TijNVqE74xvKJ69gDervRUXAxGh",
+);
describe("roll-dice", () => {
- // Configure the client to use the local cluster.
anchor.setProvider(anchor.AnchorProvider.env());
-
+ const provider = anchor.getProvider() as anchor.AnchorProvider;
const program = anchor.workspace.RandomDice as Program;
+ const playerPda = web3.PublicKey.findProgramAddressSync(
+ [Buffer.from("playerd"), provider.publicKey!.toBytes()],
+ program.programId,
+ )[0];
+
it("Initialized player!", async () => {
- const tx = await program.methods.initialize().rpc();
+ const tx = await program.methods
+ .initialize()
+ .rpc({ skipPreflight: true, commitment: "confirmed" });
console.log("Your transaction signature", tx);
});
- it("Do Roll Dice!", async () => {
- const tx = await program.methods.rollDice(0).rpc();
- console.log("Your transaction signature", tx);
- const playerPk = web3.PublicKey.findProgramAddressSync(
- [Buffer.from("playerd"), anchor.getProvider().publicKey.toBytes()],
- program.programId
- )[0];
- let player = await program.account.player.fetch(playerPk, "processed");
- await new Promise((resolve) => setTimeout(resolve, 3000));
- console.log("Player PDA: ", playerPk.toBase58());
- console.log("player: ", player);
+ it("Do Roll Dice!", async function () {
+ // The base-chain callback can take up to 10s, so raise Mocha's timeout.
+ this.timeout(20_000);
+
+ // Generate the seed BEFORE subscribing so the handler closes over it.
+ // The program logs "client_seed=N" inside callback_roll_dice — we match
+ // on that exact substring to pin the callback to our specific request.
+ const clientSeed = Math.floor(Math.random() * 256);
+ const seedTag = `client_seed=${clientSeed}`;
+
+ // Pre-arm a one-shot promise that the onLogs handler resolves with the
+ // matching signature. No polling — we just await it, racing a timeout.
+ let resolveSig!: (sig: string) => void;
+ const sigPromise = new Promise((r) => {
+ resolveSig = r;
+ });
+ const callbackSubId = provider.connection.onLogs(
+ program.programId,
+ (info) => {
+ if (
+ !info.err &&
+ info.logs.some((l) => l.includes("CallbackRollDice")) &&
+ info.logs.some((l) => l.includes(seedTag))
+ ) {
+ resolveSig(info.signature);
+ }
+ },
+ "confirmed",
+ );
+
+ try {
+ const tx = await program.methods
+ .rollDice(clientSeed)
+ .accounts({ oracleQueue: DEFAULT_BASE_QUEUE })
+ .rpc({ skipPreflight: true, commitment: "confirmed" });
+ console.log("rollDice tx:", tx);
+
+ // Base-chain VRF response is slower than ER (~1-5s typical) so 10s timeout.
+ const sig = await Promise.race([
+ sigPromise,
+ new Promise((r) => setTimeout(() => r(null), 10_000)),
+ ]);
+ if (!sig) throw new Error("callbackRollDice not observed within 10s.");
+ console.log("callbackRollDice tx:", sig);
+
+ const player = await program.account.player.fetch(playerPda, "processed");
+ console.log("player:", player);
+ } finally {
+ await provider.connection.removeOnLogsListener(callbackSubId);
+ }
});
});
```