Skip to Content
DocumentationRadixIntegration Guide

Integration Guide

This guide provides detailed instructions for integrating the Morpher Oracle into your Radix DApp. Follow these steps to start using real-time price data in your smart contracts.

Integration Steps

  1. Subscribe to the Oracle NFT (buy a Subscription to get signed prices)
  2. Build a backend that signs price requests
  3. Validate signed price responses in your smart contracts

1. Subscribe to Oracle

First, you need to purchase a subscription NFT to access the Oracle services:

  1. Visit the appropriate Subscription Page:
  2. Connect your Radix wallet
  3. Purchase a subscription NFT
  4. Note your NFT ID for future reference

2. Generate Cryptographic Keys

Generate a BLS12-381 key pair for signing Oracle requests:

import * as ed from '@noble/ed25519'; import { bls12_381 as bls } from '@noble/curves/bls12-381'; // Generate a secure random private key const privateKey = ed.utils.randomPrivateKey(); const hexPrivateKey = ed.etc.bytesToHex(privateKey); // Derive the public key function getPublicKey(privateKey) { return Array.from( bls.getPublicKey(privateKey), byte => byte.toString(16).padStart(2, '0') ).join(''); } const publicKey = getPublicKey(privateKey); console.log('Private Key:', hexPrivateKey); console.log('Public Key:', publicKey);

⚠️ IMPORTANT: Store your private key securely! It should never be exposed to users or included in client-side code.

3. Enroll Your Public Key

Enroll your public key in your subscription NFT:

  1. Return to the appropriate Subscription Page:
  2. Connect your wallet containing the subscription NFT
  3. Enter your public key in the enrollment form
  4. Submit the transaction to update your NFT

4. Set Up Your DApp Backend

Create a backend service to handle Oracle requests:

import express from 'express'; import { bytesToHex } from '@noble/curves/abstract/utils'; import { bls12_381 as bls } from '@noble/curves/bls12-381'; const app = express(); const port = process.env.PORT || 3001; // Use custom DST for BLS signatures const htfEthereum = { DST: 'BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_' }; // Types for Oracle messages type PricePoint = { marketId: string; price: number; nonce: string; dataTimestamp: number; oracleTimestamp: number; marketStatusTimestamp: number; marketStatus: string; } type PriceMessage = { data: PricePoint[]; signature?: string; } type OracleRequestMessage = { marketId: string; publicKeyBLS: string; nftId: string; signature: string; } // Helper function to get public key from private key function getPublicKey(privateKey) { return Array.from( bls.getPublicKey(privateKey), byte => byte.toString(16).padStart(2, '0') ).join(''); } // Helper function to convert message to string format for signing function oracleRequestMsgToString(msg: OracleRequestMessage): string { return `${msg.marketId}##${msg.publicKeyBLS}##${msg.nftId}`; } // Endpoint to get signed price data app.get("/example/getPrice", async (req, res) => { try { const marketId = req.query.marketId || "GATEIO:XRD_USDT"; const nftId = process.env.ORACLE_NFT_ID; const privateKey = process.env.PK_DAPP; if (!nftId || !privateKey) { return res.status(500).json({ error: "Missing environment variables" }); } // Create the request message let oracleRequestMsg = { marketId: marketId.toString(), publicKeyBLS: getPublicKey(privateKey), nftId, signature: "" }; // Convert message to string format for signing const msgString = oracleRequestMsgToString(oracleRequestMsg); const msgHex = Buffer.from(msgString, 'utf8').toString('hex'); // Sign the message const signature = bytesToHex(await bls.sign(msgHex, privateKey, htfEthereum)); oracleRequestMsg.signature = signature; // Request price data from Oracle backend (v2 API) const oracleUrl = `${process.env.ORACLE_BACKEND_URL}/v2/price/${marketId}/${oracleRequestMsg.publicKeyBLS}/${oracleRequestMsg.nftId}/${oracleRequestMsg.signature}`; const response = await fetch(oracleUrl); if (!response.ok) { throw new Error(`Oracle API error: ${response.status}`); } const priceData = await response.json(); res.json(priceData); } catch (error) { console.error('Error fetching price:', error); res.status(500).json({ error: error.message }); } }); // Endpoint to get the DApp's public key app.get("/example/getPublicKey", async (req, res) => { const privateKey = process.env.PK_DAPP; res.json({ publicKey: getPublicKey(privateKey) }); }); app.listen(port, () => { console.log(`DApp backend running on port ${port}`); });

5. Implement Frontend Integration

Add Oracle data fetching to your frontend:

// api.ts export async function getPrice( marketId: string = 'GATEIO:XRD_USDT', apiEndpoint: string = 'https://your-dapp-backend.com' ): Promise<{ data: PriceMessage, status: number }> { try { const response = await fetch(`${apiEndpoint}/api/getPrice?marketId=${marketId}`); if (!response.ok) { throw new Error(`API error: ${response.status}`); } const data = await response.json(); return { data, status: response.status }; } catch (error) { console.error('Error fetching price:', error); throw error; } } // Component using the price data function PriceDisplay() { const [priceData, setPriceData] = useState<PriceMessage | null>(null); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState<string | null>(null); const fetchPrice = async () => { setIsLoading(true); try { const { data } = await getPrice(); setPriceData(data); setError(null); } catch (err) { setError(err.message); } finally { setIsLoading(false); } }; // Use the price data to create a transaction manifest const createTransaction = () => { if (!priceData || !priceData.signature) return; // Create transaction manifest with the signed price data // ... }; return ( <div> <button onClick={fetchPrice} disabled={isLoading}> {isLoading ? 'Loading...' : 'Get Latest Price'} </button> {error && <div className="error">{error}</div>} {priceData && ( <div> <h3>Price Data</h3> <p>Market: {priceData.data[0].marketId}</p> <p>Price: {priceData.data[0].price}</p> <p>Timestamp: {new Date(priceData.data[0].dataTimestamp * 1000).toLocaleString()}</p> <button onClick={createTransaction}> Use This Price </button> </div> )} </div> ); }

6. Create Smart Contract with Oracle Integration

Implement a smart contract that uses the Oracle:

use scrypto::prelude::*; #[blueprint] mod my_dapp { struct MyDapp { oracle_component: Global<MorpherOracle>, price_lifetime: u64, } impl MyDapp { pub fn new(oracle_address: Global<MorpherOracle>) -> Global<MyDapp> { Self { oracle_component: oracle_address, price_lifetime: 120, // 2 minutes } .instantiate() .globalize() } pub fn execute_with_price_data(&mut self, message: String, signature: String) { // Call the Oracle component to verify the signature and get the price data // This is the key part - your smart contract calls the Oracle component directly let price_message = self.oracle_component.check_price_input(message, signature); // Perform additional validation on the verified price data self.validate_price_freshness(&price_message); // Use the verified price data in your business logic let price = price_message.price; info!("Verified price: {}", price); // Your business logic here... } fn validate_price_freshness(&self, price_message: &PriceMessage) { // Verify the price is fresh (not too old) assert!( price_message.oracle_timestamp + self.price_lifetime >= get_time(), "This price is out of date!" ); // Verify the market data is consistent with market status assert!( price_message.data_timestamp + 60 >= price_message.market_status_timestamp, "This price is out of date!" ); } } }

Testing Your Integration

  1. Local Testing: Use the Stokenet test network for initial development
  2. Subscription Testing: Verify your subscription NFT is properly configured
  3. Request Flow Testing: Test the full flow from frontend to backend to Oracle
  4. Transaction Testing: Verify that transactions with Oracle data execute correctly

Common Issues and Solutions

  • Signature Verification Failures: Ensure you’re using the correct key format and signing algorithm
  • NFT Ownership Issues: Verify the NFT is in the correct account and properly enrolled
  • Timestamp Errors: Check that your system clocks are synchronized
  • API Connection Problems: Verify network connectivity and API endpoint URLs

For a working example of Oracle integration, check out our Gumball Demo: