Secret Network
WebsiteDiscordGithub
  • ๐Ÿ‘‹INTRODUCTION
    • Secret Network Introduction
    • Secret Network Techstack
      • Private transactions - A quick summary
      • Blockchain Technology
        • Cosmos Basics
        • Tendermint
        • Cosmos SDK
        • IBC
        • CosmWasm
      • Privacy Technology
        • Encryption - Key Management
          • Overview
          • Key Derivation & Encryption Techniques
          • The Initialization Of Secret Network
          • Full Node Boostrap
          • Contract State Encryption
          • Transaction Encryption
          • Consensus seed rotation
        • Trusted Execution Environmentsโ€Š (TEE) โ€”โ€Š Intel SGX
          • How Secret Network Uses SGX
          • SGX-SPS Security & Reliabillity
          • Remote Attestation
          • Trusted & Untrusted Core
          • Sealing
        • Private smart contracts - overview
  • ๐Ÿ’ปDevelopment
    • ๐Ÿ Getting Started
      • Setting Up Your Environment
        • Cargo.toml Best Practices (Crates vs Dependencies)
      • Compile and Deploy
      • Running the Application
      • Compile and Deploy on Secret testnet (best for Javascript devs)
      • Fullstack dApp Integration
    • ๐Ÿค“Secret Contracts
      • Secret Contracts & CosmWasm
        • Framework overview
        • Secret Contract Components
          • Instantiation Message
          • Execution Message
          • Query Message
          • Deps/DepsMut
          • Storage
            • Prefixed Storage
            • Singleton
            • Keymap
            • Append Store
            • Best practices
        • CosmWasm vs Secret CosmWasm
      • Secret Tokens (SNIP-20)
      • Contract - module call
      • Secret contract - Design Space/Features
        • Secret Contracts introduction
        • Gas/Fee usage
        • TPS and scalability
        • Privacy Essentials
        • Access Control
          • Viewing Keys
          • Permits
        • Trusted and untrusted data
        • Secret-VRF - on-chain Randomness
        • Privacy design
          • Mitigate privacy risks - full guide
          • Gas Evaporation & Tracking
        • Confidential Computing Layer
        • Fiat/Crypto Onboarding
        • Account abstraction
        • Fee abstraction
        • Wallet support
        • Bridge (messaging/tokens)
        • IBC (Hooks, PFM, Wasm)
        • Price Oracles
        • Auto Restaking
      • Permissioned Viewing
        • Viewing Keys
        • Permits
      • Cross Contract Communication
      • Submessages
        • get_contract_code_hash
      • Randomness API - Secret VRF
        • Native On-chain randomness
        • Randomness over IBC
      • Execution Finalization
      • Factory Contracts
      • Contract Migration
        • Manual - < v1.11
        • Native - from v1.11
      • Cross-deploy Vanilla CW and Secret Contracts
      • Testing Secret Contracts
        • Unit Tests
        • Continuous Integration
        • Datatype Handling - Uint, floats etc.
    • ๐Ÿ‘€Secret Contract - Reference, Guides, Examples
      • Starter guide - Millionaire's Problem
      • Reference Contracts
      • Open source dApps
      • Tools & Libraries
        • Network interaction SDKs
          • Secret.js (JavaScript)
          • SecretPy (Python)
          • SecretK (Kotlin)
          • Secret.NET
            • Snippets
              • Deploying a Contract
              • Send Native Coin
              • Query a Contract
              • Create a new Wallet
              • Permits
              • SNIP20
              • SNIP721
          • Shade.Js
        • LocalSecret - Devnet docker
        • Smart contract - Tools/Utils
          • Secret Toolkit
          • CW-Plus
          • Fadroma - SC framework
          • Hidden Gems
          • Other
            • Secret IDE
            • Polar
    • ๐Ÿ–ผ๏ธFrontend Development
      • Getting Started with SecretJS
        • Usage Examples
          • Sending Queries
          • Sending Messages
          • Contract Migration
          • Wallet Integrations
          • Query Permits
          • SNIP20 (SCRT Tokens)
          • SNIP721 (Secret NFTs)
      • Feegrant
        • Understanding Feegrant allowances
        • Grant allowances
        • Using grant allowances to execute transactions
        • Using the Fee Grant Faucet
    • ๐Ÿ“ฌDeployment Addresses/ API Endpoints
      • Secret (SNIP 20) token contracts list
        • SNIP Asset naming guidelines
      • Connecting to the Network
        • API Endpoints Mainnet (Secret-4)
        • API Endpoints Testnet (Pulsar-3)
        • Usage examples
        • Comparison of endpoint types
      • Create your own SNIP-25 IBC Token
  • Secret AI
    • ๐ŸคIntroduction
    • ๐Ÿ—๏ธArchitecture
    • ๐Ÿ‘ฉโ€๐Ÿ’ปSecret AI SDK
      • Setting Up Your Environment
      • Running the Application
    • ๐Ÿช™Economics
    • Smart Contract Reference
      • SubscriptionManager
      • WorkerManager
      • RewardsManager
  • ๐ŸŒSecretVM - Confidential Virtual Machines
    • ๐ŸคIntroduction
    • ๐Ÿ—๏ธArchitecture
    • โ˜‘๏ธAttestation
      • What is Attestation
      • Obtaining Attestation Data
      • Attestation Report - Key Fields
      • Chain of Trust
    • ๐ŸLaunching a SecretVM
    • ๐Ÿ› ๏ธManaging SecretVM Lifecycle
    • โœ…Verifying a SecretVM
    • ๐Ÿ’กBest Practices for Developers
    • ๐Ÿ“–Glossary
  • ๐Ÿ”“Confidential Computing Layer
    • ๐Ÿง‘โ€๐Ÿš€IBC Developer Toolkit
      • Basics
        • Overview
        • Cross-chain Messaging with IBC Hooks
          • Functions, Methods, and Data Structures
          • Typescript SDK
          • IBC-Hooks
        • IBC Relaying with Go Relayer
      • Usecases
        • Storing Encrypted Data on Secret Network
          • Key-Value store Developer Tutorial
        • Secret VRF for IBC with IBC-Hooks
        • Confidential Voting
        • Sealed Bid Auctions
      • Supported Networks
        • Mainnet
        • Testnet
    • ๐ŸคEthereum (EVM) Developer Toolkit
      • Basics
        • Overview
        • Connecting Metamask to Secret Network
        • SecretPath + Reown integration
        • Cross-chain Messaging
          • SecretPath
            • Architecture Overview
            • Detailed Architecture
            • SecretPath Developer Tutorials
            • Public EVM Gateway Architecture
            • How to deploy SecretPath on your chain
          • Axelar GMP
            • Architecture Overview
            • Axelar GMP Developer Tutorial
      • Usecases
        • Storing Encrypted Data on Secret Network
          • Key-Value store Developer Tutorial
        • Sealed Bid Auction
          • Sealed Bid Auction Developer Tutorial
        • Confidential Voting
          • Confidential Voting Developer Tutorial with SecretPath
        • VRF
          • Implementing VRF into any EVM Contract
          • VRF Developer Tutorial
          • Performance figures of SecretVRF vs competitors
          • Using encrypted payloads for VRF
          • Converting from Chainlink VRF to Secret VRF in four steps
        • Confidential Document Sharing
        • Tokens
          • From EVM to Secret
      • Supported Networks
        • EVM
          • EVM Mainnet
          • EVM Testnet
          • Gateway Contract ABI
        • Secret Gateway
          • SecretPath mainnet (secret-4) contracts
          • SecretPath testnet (pulsar-3) contracts
    • ๐Ÿ™ŒSolana Developer Toolkit
      • Usecases
        • Storing Encrypted Data on Secret Network
          • Key-value Store Developer Tutorial
        • VRF
          • VRF Developer Tutorial
      • Program IDs
        • Solana Mainnet & Testnet
        • Gateway Contract IDL
  • ๐ŸคซOverview, Ecosystem and Technology
    • ๐Ÿš€Secret Network Overview
      • The SCRT coin
      • Private Tokens
      • Use Cases
        • Decentralized Finance (DeFi)
        • Art And Digital Media
        • Gaming
        • Data Privacy
        • Payments And Transactions
        • Communication
      • The technology
      • History
      • Roadmap (Core development)
        • Secret 2.0
      • Where To Buy SCRT?
      • Using the Testnet
    • ๐ŸธEcosystem Overview
      • Wallets
      • Applications
      • Explorers & tools
      • Funding
        • SCRT Labs Grants
        • Dilutive funding/VC raise
        • Community Pool
        • Developer bounties
          • SCRT Labs bounties [on-hold]
          • CCBL [on-hold]
          • CCR [On-hold]
        • Application specific
          • Shade Grants
      • Contributors & Entities
        • Validators
        • SCRT Labs
        • Secret Foundation
        • Secret Committees
          • Support
          • Governance
      • Secret Network Dictionary
  • ๐Ÿ”งInfrastructure
    • ๐Ÿ”“Use SecretCLI
      • Secretcli vs. Secretd
      • Install
      • Configuration
      • Address Types
      • Key Types
      • Generating Keys
      • Viewing Keys
      • Query Transactions
      • Send Tokens
      • Multisig Keys
      • Multisig Transactions
      • Transaction Broadcasting
      • Fees & Gas
      • Fee Distribution
      • Secret Contracts
      • Slashing
      • Minting
      • Delegating
      • Restake
      • Nodes
      • Governance
        • Creating Governance Proposals
        • Query Proposals
        • Deposits
        • Voting
    • ๐Ÿ”Use Ledger hardware wallet
      • ๐Ÿ”Ledger with SecretCLI
    • ๐Ÿ–ฅ๏ธRunning a node/validator
      • Setting up a node/validator
        • Hardware setup
          • Hardware Compliance
          • VPS/Bare-Metal Compliance
            • Leaseweb Setup
            • PhoenixNAP Setup
            • Psychz Setup
            • nForce Setup
            • Vultr Setup
            • OVHCloud Setup
            • Microsoft Azure Setup
          • Patching your Node
          • Enclave verification
          • Registration troubleshooting
        • Testnet Setup
          • Install SGX
          • Install secretd
          • Setup Full Node
          • Testnet State Sync
          • Becoming a Testnet Validator
          • Installing CLI & Creating A New Address
        • Mainnet Setup
          • Install SGX
          • Install secretd
          • Setup Full Node
          • Quicksync / Snapshot
          • Statesync
          • Becoming A Validator
          • Installing CLI & Creating A New Address
      • Maintaining a node/validator
        • Slashing information
        • Migrating a Validator
        • Troubleshooting
        • Validator Backup
        • Server security
          • SSH authentication
          • Server configuration
          • Uncomplicated-Firewall (UFW)
          • Local CLI
        • Node Monitoring
          • Prometheus
            • Environment Preperation
            • Install Node Exporter
            • Install Prometheus
            • Configuring Prometheus
          • Grafana
            • Install Grafana
            • Grafana Dashboard
            • Next Steps
          • Docker
            • Install Docker
            • Configuration
            • Start Containers
            • Grafana Dashboard
            • Application Ports
            • Stop Containers
          • Goaccess
            • Install Goaccess
            • Setup Goaccess
        • Helpful commands
          • Query Validators
          • Bond Tokens
          • Withdraw Rewards
          • Query Delegations
          • Unbond Tokens
          • Query Unbonding-Delegations
          • Redelegate Tokens
          • Query Redelegations
          • Query Parameters
          • Query Pool
          • Query Delegations To Validator
      • API Noderunning
        • Running Multiple Nodes on the Same Server
        • Node Loadbalancing using Nginx
          • Setup Nginx
          • Example Nginx config
        • Using Auto heal to improve cluster uptime for Nginx
      • Sentry and Archive nodes
        • Mantlemint
        • Sentry Nodes
        • Archive Nodes
    • โ›“๏ธIBC Relayers
      • Hermes
      • RLY
      • IBC channel database
    • ๐Ÿ†™Upgrade Instructions
      • v1.13
      • v1.12
      • v1.11
      • v1.10
      • v1.9
      • v1.8
      • v1.7
      • Shockwave Omega v1.6
      • v1.5
      • Shockwave Delta v1.4
      • Shockwave Delta v1.4 (Testnet)
      • Shockwave Alpha v1.3
      • Cosmovisor
      • Vulcan Network Upgrade (OLD)
    • โ˜ ๏ธPostmortems
      • SNIP-20 leaks
      • xApic
      • Secpk-Verifications Bloat
      • Earn Contract Exploit
      • Testnet Halt 95
    • โœ๏ธContribute to the documentation
      • Report Bugs
      • Suggest Enhancements
      • First Contribution Guide
      • Pull Request Templates
        • Update Documentation
        • Bug Fix
        • Improve Performance
        • Change Functionality
      • Style Guide
    • ๐ŸŒŠVersioning & Changelog
      • Secret Network v1.13
      • Secret Network v1.12
      • Secret Network v1.11
      • Secret Network v1.10
      • Secret network v1.9
      • Secret Network v1.7/1.8
      • Secret Network v1.6
      • Secret Network v1.5
      • Secret Network v1.4 (CosmWasm 1.0)
Powered by GitBook
On this page
  • Overview
  • VRF Demo
  • Getting Started
  • Solana Prerequisites
  • Understanding SecretPath โœ…
  • Understanding the RNG program on Secret Network
  • state.rs
  • msg.rs
  • contract.rs
  • Uploading the RNG program to Secret Network
  • Request Random Numbers on Solana
  • Generating the encryption key using ECDH
  • Define the Calldata for the Secret program & Callback information
  • Encrypting the Payload
  • Signing the Payload with Phantom Wallet
  • Packing the Transaction & Send
  • Connecting Your Solana RNG Program to Frontend
  • Initializing the Solana Client
  • Summary

Was this helpful?

Edit on GitHub
Export as PDF
  1. Confidential Computing Layer
  2. Solana Developer Toolkit
  3. Usecases
  4. VRF

VRF Developer Tutorial

Learn how to send encrypted on-chain random numbers to Solana from Secret Network

PreviousVRFNextProgram IDs

Last updated 8 months ago

Was this helpful?

Overview

, Secret Network's on-chain random number generator, enables Solana developers to access encrypted on-chain verifiable random numbers from Secret Network.

In this tutorial, you will learn how to send random numbers from Secret Network to a Solana program. By this end of this tutorial you will:

  • Have an understanding of , Secret Network's trustless Solana and EVM bridge

  • Upload and instantiate your very own randomness contract on Secret Network

  • Request random numbers on Solana from Secret Network

  • Connect a frontend to your Solana application

VRF Demo

Try out requesting a random number on Solana Devnet from Secret Network testnet using the demo !

Simply request a random number (up to 2000), and click "submit." You can view your returned random number on the gateway program .

After you make the request, you will see two transaction signatures within a few seconds of each other. Open the second transaction signature (which contains the callback info with your random numbers) in a new window, then, scroll down to the program instruction logs to view the returned random numbers as a base64 string:

Way to go! You just sent an encrypted on-chain random number from Secret Network to a Solana program. Now it's time to learn how to upload your own randomness contract on Secret Network so that you can program it as you see fit. Let's get started ๐Ÿ˜Š

Getting Started

To get started, clone the repo:

git clone https://github.com/writersblockchain/solana-rng

Solana Prerequisites

Understanding SecretPath โœ…

On Solana, smart contracts are referred to as "Programs," which is the terminology we will use for smart contracts on Solana and Secret Network throughout this tutorial.

SecretPath has two main components: gateways and relayers.

  1. The Gateway is a Solana program created by Secret Network that acts as the interface for handling messages between Solana and Secret Network. The gateway program packages, verifies, and encrypts/decrypts messages. For your purposes, all that you must know is how to properly format your Secret Network program's functions so they can be understood by the Solana gateway program, which you will learn shortly.

  2. Relayers watch for messages on Solana and then pass them to Secret Network, and vice-versa. They do not have access to the actual data (since messages are encrypted), so they can't compromise security. Their main job is ensuring the network runs smoothly by transmitting the messages, but they donโ€™t move tokens or handle funds. For your purposes, you needn't focus on relayers as Secret Network maintains a relayer for all Solana transactions.

Now that you understand the basics of SecretPath, let's upload your very own RNG program to Secret Network! ๐Ÿค“

Understanding the RNG program on Secret Network

cd into solana-rng/rng:

cd rng

Let's examine the RNG program. Open the src folder and you will see four files:

Like Solana, programs on Secret Network are written in Rust. Unlike Solana, Secret Network developers use a framework called Cosmwasm instead of Anchor. Let's examine each of these files before uploading the program to Secret Network testnet.

On Solana, programs are stateless. On Secret Network, programs (ie smart contracts) are stateful. This means that unlike Solana's stateless programs, Secret Network programs maintain persistent state across transactions, allowing for private data storage and processing.

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct State {
    pub gateway_address: Addr,
    pub gateway_hash: String,
    pub gateway_key: Binary,
}

When you instantiate your program on Secret Network, you save the Solana Gateway Program info (gateway_address, gateway_hash, and gateway_key) in the Secret program so that it knows how to correctly route messages to the Solana Gateway program.

In Secret Network programs, a msg.rs file typically defines the structure of messages that your program will send or receive. It outlines how the program interacts with external users, other programs, or blockchain modules, specifying what kind of data is expected in those interactions.

For example, on Solana, you'd define instruction data formats in a similar way when developing programs that interact with accounts. In CosmWasm (Secret Network's smart contract framework), a msg.rs file similarly defines the data types for actions like instantiating, executing, and querying programs.

In the context of SecretPath, this msg.rs file defines the messages that will be sent to and received from the master Solana gateway program, which is responsible for managing cross-chain communication.

  • The randomness generation in the contract creates a buffer to store random data, fills it using a pseudorandom number generator (PRNG), and sends the result to the gateway.

You can modify this code by changing the buffer size, applying custom post-processing like converting to hex, combining randomness with on-chain data, generating numbers in specific ranges, using a different algorithm like a hash function, etc. The sky's the limit!

  • After generating random numbers, the contract returns the result back to the master gateway program. The randomness result is packaged into a PostExecutionMsg and passed to the Solana gateway as a callback.

Now that you understand how the program works, let's upload it to Secret Network!

Uploading the RNG program to Secret Network

Before you upload the randomness program to Secret Network, you first must compile the code.

Compile the randomness contract:

make build-mainnet

cd into node:

cd node

Install secretjs:

npm i

Upload and instantiate the program on Secret Network testnet:

node upload

You will see a contract id, codehash, and address returned:

codeId:  10207
Contract hash: 931a6fa540446ca028955603fa4b924790cd3c65b3893196dc686de42b833f9c
contract address:  secret1zdz2h5883nlz757dsq2ejfwdy0wpq0uwe2mz0r

Now let's use this program to request random numbers on Solana!

Request Random Numbers on Solana

Now that you have deployed your randomness program on Secret Network, all that's left is to execute it from Solana! To do this, you will use the IDL of the master Solana Gateway program in order to execute your randomness program on Secret Network and correctly format the parameters that you pass to the master Solana Gateway program, which you will now learn how to do ๐Ÿค“

Submit.ts is where you format and submit the transaction data to the Solana Gateway program so that it can execute your Secret Network randomness program, and then send the randomness back to the Solana Gateway program ๐Ÿค“

Notice that the IDL of the master Solana Gateway Program is imported into submit.ts, which you can find here: Gateway Contract IDL.

Import the IDL using:

import idl from "./solana_gateway.json";
const routing_contract = "secret15n9rw7leh9zc64uqpfxqz2ap3uz4r90e0uz3y3"; //the contract you want to call in secret
const routing_code_hash = "931a6fa540446ca028955603fa4b924790cd3c65b3893196dc686de42b833f9c" //its codehash

Generating the encryption key using ECDH

  //Generating ephemeral keys
    const walletEpheremal = ethers.Wallet.createRandom();
    const userPrivateKeyBytes = Buffer.from(
      walletEpheremal.privateKey.slice(2),
      "hex"
    );
    const userPublicKey: string = new SigningKey(walletEpheremal.privateKey)
      .compressedPublicKey;
    const userPublicKeyBytes = Buffer.from(userPublicKey.slice(2), "hex");

    const sharedKey = await sha256(
      ecdh(userPrivateKeyBytes, gatewayPublicKeyBytes)
    );

Define the Calldata for the Secret program & Callback information

Next, you define all of the information that you need for calling the RNG program on Secret AND add the callback information for the message on its way back.

1. Define the Function and Parameters (Calldata)

The first step is to define the function name and the parameters that you want to pass into your RNG program on the Secret Network.

const handle = "request_random";
const numWords = document.querySelector<HTMLInputElement>("#input1")?.value;
const callback_gas_limit = document.querySelector<HTMLInputElement>("#input2")?.value;

const data = JSON.stringify({ numWords: Number(numWords) });
  • Function Name (handle): This specifies the function you wish to invoke within the Secret program. In this example, "request_random" is a function that generates a random number.

  • Parameters (data): This is the data that you will pass to the function in the Secret program. Here, we use a JSON string that contains the number of random words (numWords) that the function will generate.

2. Derive the Program Derived Addresses (PDAs)

In Solana, Program Derived Addresses (PDAs) are special types of addresses that are derived deterministically based on a seed and the program ID. Both PDAs are used here to store the gateway and the tasks' state. You do not need to manually save them as both of these can deterministically derived from the program id at any time.

// Derive the Gateway PDA / Program Derived Address
const [gateway_pda, gateway_bump] = web3.PublicKey.findProgramAddressSync(
  [Buffer.from("gateway_state")],
  program.programId
);

// Derive the Tasks PDA / Program Derived Address
const [tasks_pda, task_bump] = web3.PublicKey.findProgramAddressSync(
  [Buffer.from("task_state")],
  program.programId
);
  • gateway_pda: This is the Program Derived Address associated with the gateway's state. It's derived from a seed ("gateway_state") and the program ID.

  • tasks_pda: This is the Program Derived Address associated with the tasks' state. Similarly, it's derived from a seed ("task_state") and the program ID.

3. Define the Callback Information

The callback information specifies where and how the call should be handled. This involves setting a callback address and a callback selector.

// Include some address as a test (not needed here, you can add whatever you need for your dApp)
const testAddress1 = new web3.PublicKey("HZy2bXo1NmcTWURJvk9c8zofqE2MUvpu7wU722o7gtEN");
const testAddress2 = new web3.PublicKey("GPuidhXoR6PQ5skXEdrnJehYbffCXfLDf7pcnxH2EW7P");
const callbackAddress = Buffer.concat([testAddress1.toBuffer(),testAddress2.toBuffer()]).toString("base64");
  • Callback Address (callbackAddress): These are the addresses where the callback addresses needed for the CPI are included. In this example, itโ€™s simply a test address. In a real-world application, this would typically be your callback contract's address or the addresses designated to handle the callback. The callback addresses are the concatenated 32 bytes of all addresses that need to be accessed for the callback CPI. We take two address public keys (32 bytes each), concatinate them together and then base64 encode them.

Next, we define the callback selector. The callback selector is a unique identifier that indicates which program and function the callback CPI should access. It's a combination of the program ID and a specific function identifier.

// 8 bytes of the function Identifier = CallbackTest in the SecretPath Solana Contract
const functionIdentifier = [196, 61, 185, 224, 30, 229, 25, 52];
const programId = program.programId.toBuffer();

// Callback Selector is ProgramId (32 bytes) + function identifier (8 bytes) concatenated
const callbackSelector = Buffer.concat([programId, Buffer.from(functionIdentifier)]);
  • Function Identifier (functionIdentifier): This is an array of bytes that uniquely identifies the function within the contract that should process the callback. In this example, the identifier corresponds to a hypothetical function "CallbackTest" in the SecretPath Solana program.

  • Program ID (programId): This is callback program on Solana that is involved for the callback.

  • Callback Selector (callbackSelector): This is a combination of the programId and the functionIdentifier, and it uniquely identifies the callback program and function within the Solana program.

Finally, we specify the callback compute limit or callback gas limit:

const callbackGasLimit = Number(callback_gas_limit);
  • Callback Gas Limit (callbackGasLimit): This represents the amount of gas that should be allocated to process the callback on the Solana side. It's important to estimate this correctly to ensure that the callback can be executed without running out of resources.

After defining the contract call and callback, we now construct the payload:

//Payload data that are going to be encrypted
const payload = {
  data: data,
  routing_info: routing_contract,
  routing_code_hash: routing_code_hash,
  user_address: provider.publicKey.toBase58(),
  user_key: Buffer.from(userPublicKeyBytes).toString("base64"),
  callback_address: callbackAddress,
  callback_selector: Buffer.from(callbackSelector).toString("base64"),
  callback_gas_limit: callbackGasLimit,
};

Encrypting the Payload

Next, we encrypt the payload using ChaCha20-Poly1305. Then, we hash the encrypted payload into a ciphertextHash using Keccak256.

//build a JSON of the payload 
const plaintext = json_to_bytes(payload);

// Generate a nonce for ChaCha20-Poly1305 encryption
//DO NOT skip this, stream cipher encryptions are only secure with a random nonce!
const nonce = crypto.getRandomValues(new Uint8Array(12));

// Encrypt the payload using ChachaPoly1305 and concatenate the ciphertext + tag
const [ciphertextClient, tagClient] = chacha20_poly1305_seal(sharedKey, nonce, plaintext);
const ciphertext = concat([ciphertextClient, tagClient]);

// Create the payloadHash by keccak256 of the ciphertext
const payloadHash = Buffer.from(keccak256.arrayBuffer(ciphertext));

Signing the Payload with Phantom Wallet

Next, we use Phantom to sign the payloadHash using signMessage.

Internally, Phantom Wallet only allows for ASCII encoded strings to be signed to prevent any wallet drainers from signing arbitrary bytes. For us this means that we take the payloadHash and base64 encode it. Phantom then actually directly signs the base64 string (NOT: the payloadHash directly) of the payloadHash to get the signature. Keep this in mind when verifying the signature against the payloadHash.

const payloadHashBase64 = Buffer.from(payloadHash.toString("base64"));
const payloadSignature = await provider.signMessage(payloadHashBase64);

Packing the Transaction & Send

Lastly, we pack all the information we collected during previous steps into an info struct that we send into the Solana Gateway program. We encode the function data. Finally, we set the tx_params. Please make sure to set an appropriate gas amount for your contract call, here we used 150k gas. For the value of the TX, we send over the estimated callback gas that we calculated above.

const executionInfo = {
  userKey: Buffer.from(userPublicKeyBytes),
  userPubkey: payloadSignature.publicKey.toBuffer(),
  routingCodeHash: routing_code_hash,
  taskDestinationNetwork: "pulsar-3",
  handle: handle,
  nonce: Buffer.from(nonce),
  callbackGasLimit: callback_gas_limit,
  payload: Buffer.from(ciphertext),
  payloadSignature: payloadSignature.signature,
};

// Get the latest blockhash
const { blockhash } = await connection.getLatestBlockhash("confirmed");

// Construct the transaction
const tx = await program.methods
  .send(provider.publicKey, routing_contract, executionInfo)
  .accounts({
    gatewayState: gateway_pda,
    taskState: tasks_pda,
    user: provider.publicKey,
    systemProgram: web3.SystemProgram.programId,
  })
  .transaction();

// Set the recent blockhash
tx.recentBlockhash = blockhash;
tx.feePayer = provider.publicKey;

// Sign the transaction using Phantom wallet
const signedTx = await provider.signTransaction(tx);

// Send the signed transaction
const signature = await connection.sendRawTransaction(signedTx.serialize());

// Confirm the transaction
await connection.confirmTransaction(signature);

console.log("Final result after rpc:", tx);

Now that you know how to correctly format and package data for the Solana Gateway program, let's learn how to connect your program to a frontend ๐Ÿš€

Connecting Your Solana RNG Program to Frontend

Open a new terminal window and cd into solana-rng/solana-frontend:

cd solana-rng/solana-frontend

Install the dependencies:

npm i

Initializing the Solana Client

const network = "https://api.devnet.solana.com";
const connection = new Connection(network, "processed");

const getProvider = () => {
  if ("solana" in window) {
    const provider = window.solana as any;
    if (provider.isPhantom) {
      return provider;
    }
  }
  window.open("https://phantom.app/", "_blank");
};

const provider = getProvider();
if (!provider) {
  console.error("Phantom wallet not found");
} else {
  await provider.connect(); // Connect to the wallet
}

const wallet = {
  publicKey: provider.publicKey,
  signTransaction: provider.signTransaction.bind(provider),
  signAllTransactions: provider.signAllTransactions.bind(provider),
};

const anchorProvider = new AnchorProvider(connection, wallet, {
  preflightCommitment: "processed",
});
const program = new Program(idl, anchorProvider);

Now it's time to run the progam! In the terminal:

npm run dev 

Congrats! You now have your very own Solana RNG program deployed on Secret Network and can request encrypted random numbers on Solana!

Summary

In conclusion we constructed and encrypted a payload for SecretPath for Solana direct in the Frontend as well as calling the SecretPath gateway program.

Before you upload a randomness program to Secret Network, it is important for you to understand how messages are sent from Solana to Secret Network and vice-versa. This is thanks to a protocol that Secret Network developed called , which allows Solana developers to execute programs on Secret Network while preserving the privacy of the inputs and validity of the outputs using ECDH cryptography ๐Ÿคฏ.

is where you manage your contract's state, which in this case is simply the program info for the master Solana Gateway Contract:

In the context of the Secret Network, the file is the core of your program logic, much like the Solana program's main instruction handler. It contains the main program functions, handling instantiation, execution, and migration, as well as any specific functionality youโ€™ve built in.

In this file, you define how the program will handle inputs, store state, interact with other programs (like the Solana gateway in SecretPath), and process outputs. Letโ€™s focus on the key function, , since that's the core of how randomness is handled.

The function is designed to generate random numbers on Secret Network and send them to the Solana Gateway program. Here's a high-level explanation:

If you want to make any changes to the Secret Network RNG program before compiling, feel free to do so! You can learn more about Secret Network contracts in the Secret Network docs

Open and examine the code. You needn't change anything here unless you want to upload the Secret Network program to Mainnet instead of testnet.

Remember earlier when you learned that you must instantiate your Secret Network program to correctly communicate with the master Solana Gateway Program? Now that you are uploading your Secret Network randomness program, note that you are instantiating it with the master Solana Gateway

Let's start by examining .

To start, we first define all of our variables that we need for the encryption, as well as the .

Replace routing_contract and routing_code_hash with your contract address and code hash! This is the Solana program that you just uploaded to Secret network testnet

At this point, you can run the application exactly as is and the master Solana gateway program will correctly route to the program you deployed on Secret Network. But let's go more in depth so you understand how everything is working together

Next, you and load in the public encryption key for the Solana Secret Gateway program. Then, use ECDH to create the encryption key:

First, we define the function that we are going to call on your Secret RNG program, which in this case is . Next, we add the parameters/calldata for this function, which is ("{ numWords: Number(numWords) }"and convert it into a JSON string.

Next, that you are using to interact with the program. Connect to Phantom wallet and set up the Anchor provider with the Program IDL imported earlier.

Need help with using encrypted payloads with Secretpath or want to discuss use cases for your dApp? Please ask in the Secret Network or Discord.

๐Ÿ”“
๐Ÿ™Œ
๐Ÿ˜„
๐Ÿ˜„
Install Phantom wallet.
Fund your Solana devnet wallet.
SecretPath
contract.rs
lib.rs
state.rs
state.rs
state.rs
msg.rs
contract.rs
contract.rs
try_random
try_random
here.
upload.js
address, code_hash, and public_key :D
solana-rng/solana-frontend/src/submit.ts
gateway information
generate ephermal keys
request_random
initialize the Solana client
Telegram
msg.rs
SecretVRF
SecretPath
here
here