A comprehensive Hardhat template for integrating all oracles supported by Monad blockchain. This template provides ready-to-use contracts and scripts for deploying and interacting with multiple oracle providers.
- Supported Oracles
- Prerequisites
- Setup Guide
- Quick Start Guide
- Configuration
- How to Use This Template
- Common Workflows
- Usage
- Contract Structure
- Scripts Structure
- Price Feed Decimals
- Post-Setup Checklist
- Troubleshooting
- Contributing
- Resources
This template includes implementations for the following oracle providers:
- Pyth Network - Decentralized price feeds
- Chronicle - Decentralized oracle protocol
- Redstone - Modular oracle protocol
- Stork Oracle - Chainlink and Pyth adapters
- Switchboard - On-demand oracle network
Before you begin, ensure you have the following installed:
- Node.js (v16 or higher) - Download here
- npm (comes with Node.js) or yarn
- Git (for cloning the repository)
- A crypto wallet (MetaMask, etc.) with Monad testnet tokens
Check your Node.js and npm versions:
node --version # Should be v16 or higher
npm --version # Should be v6 or higherClone this repository to your local machine:
git clone <repository-url>
cd Monad-All_Oracles-mainIf you downloaded a ZIP file, extract it and navigate to the directory:
cd Monad-All_Oracles-mainInstall all required npm packages:
npm installThis will install:
- Hardhat and Hardhat Toolbox
- Oracle SDKs (Pyth, Redstone, Stork, Switchboard)
- TypeScript and other development dependencies
Expected output: You should see packages being installed. The process may take 1-2 minutes.
Create a .env or .env.local file in the root directory. You can copy the example file:
cp .env.example .env.localOr create it manually:
On Linux/Mac:
touch .env.localOn Windows:
type nul > .env.localAdd your private key to the .env.local file:
DEPLOYER_ACCOUNT_PRIV_KEY=your_private_key_without_0x_prefixExample:
DEPLOYER_ACCOUNT_PRIV_KEY=1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef- Never commit your
.envor.env.localfile to version control - Never share your private key
- Use a dedicated testnet wallet, never use your Monad mainnet wallet
- The private key should NOT include the
0xprefix - The
.env.examplefile is safe to commit (it contains no real values)
The .gitignore file should already include .env and .env.local. Verify it exists and contains:
.env
.env.local
node_modules/
This ensures your private keys are never committed to version control.
Open hardhat.config.ts and verify/update the network configuration:
monadTestnet: {
chainId: 10143,
url: "https://monad-testnet.g.alchemy.com/v2/YOUR_API_KEY",
accounts: ACCOUNTS,
}RPC Options:
- Use the default Alchemy RPC (requires API key from Alchemy)
- Use a public RPC endpoint (if available)
- Use other Monad RPC providers
Example with public RPC (if available):
monadTestnet: {
chainId: 10143,
url: "https://monad-testnet-rpc.publicnode.com", // Example public RPC
accounts: ACCOUNTS,
}Verify everything is set up correctly by compiling the contracts:
npx hardhat compileExpected output: You should see compilation successful messages. If there are errors, check your Node.js version and try npm install again.
Before deploying, you'll need Monad testnet tokens:
-
Add Monad Testnet to your MetaMask:
- Network Name: Monad Testnet
- RPC URL:
https://monad-testnet-rpc.publicnode.com(or your configured RPC) - Chain ID:
10143 - Currency Symbol:
MON
-
Get testnet tokens from the Monad faucet (check Monad documentation for current faucet URL)
-
Choose an Oracle: Decide which oracle you want to use (Pyth is recommended for beginners)
-
Update Script Configuration:
- Open the deployment script for your chosen oracle (e.g.,
scripts/pyth/deploy.pythoracle.ts) - Update the oracle-specific addresses/IDs with Monad testnet values
- Open the deployment script for your chosen oracle (e.g.,
-
Deploy Your First Contract:
npx hardhat run scripts/pyth/deploy.pythoracle.ts --network monadTestnet
-
Save the Deployed Address: Copy the deployed contract address from the output
-
Read Price Data:
- Update the read script with your deployed contract address
- Run the read script:
npx hardhat run scripts/pyth/pyth.read.ts --network monadTestnet
Here's a complete walkthrough for setting up Pyth Oracle:
-
Find Pyth Contract Address on Monad:
- Check Pyth documentation for Monad testnet addresses
- Or check deployed contracts on Monad explorer
-
Edit
scripts/pyth/deploy.pythoracle.ts:const PYTH_ADDRESS = "0x7cE7845bDE4277e8Aa132aC4c042605e7d42B71C"; // Update with actual address
-
Deploy:
npx hardhat run scripts/pyth/deploy.pythoracle.ts --network monadTestnet
Output example:
Deploying... Deployed Pyth Contract Address: 0x1234567890123456789012345678901234567890 -
Edit
scripts/pyth/pyth.read.ts:const PythAddress = "0x1234567890123456789012345678901234567890"; // Your deployed address const pythId = "0x2f95862b045670cd22bee3114c39763a4a08beeb663b145d283c31d7d1101c4f"; // ETH/USD feed
-
Read Price:
npx hardhat run scripts/pyth/pyth.read.ts --network monadTestnet
The project is pre-configured for Monad Testnet. You can modify network settings in hardhat.config.ts:
Available Networks:
hardhat- Local Hardhat network (for local testing)monadTestnet- Monad Testnet (Chain ID: 10143)
Monad Testnet Configuration:
The default configuration in hardhat.config.ts:
networks: {
hardhat: { chainId: 31337 },
monadTestnet: {
chainId: 10143,
url: "https://monad-testnet.g.alchemy.com/v2/YOUR_API_KEY",
accounts: ACCOUNTS,
},
}Updating RPC URL:
If you need to use a different RPC endpoint for Monad Testnet, update the url in hardhat.config.ts:
monadTestnet: {
chainId: 10143,
url: "YOUR_MONAD_RPC_URL", // Update with your preferred RPC endpoint
accounts: ACCOUNTS,
}The project uses Solidity 0.8.28 with Paris EVM version. You can modify this in hardhat.config.ts:
solidity: {
version: "0.8.28",
settings: {
evmVersion: "paris",
optimizer: {
enabled: true,
runs: 200,
},
},
}Each oracle requires specific configuration:
- Pyth: Requires Pyth contract address on the network
- Chronicle: Uses pre-deployed contracts (addresses in contract)
- Redstone: Requires adapter address and data feed ID
- Stork: Requires adapter address and asset ID
- Switchboard: Requires Switchboard contract address and aggregator ID
See individual oracle sections below for detailed configuration.
This template is designed to be a starting point for your oracle integrations. Here's how to use it effectively:
- Copy Contracts: Copy the oracle contracts you need into your own project
- Adapt Scripts: Modify the deployment and read scripts for your use case
- Integrate: Import oracle contracts into your own smart contracts
Review the available oracles and select the ones that fit your needs:
- Pyth: Best for high-frequency price updates
- Chronicle: Good for stable, reliable feeds
- Redstone: Modular, flexible oracle solution
- Stork: Chainlink-compatible adapter
- Switchboard: On-demand oracle updates
Each oracle has deployment and read scripts. Customize them:
Example - Customizing Pyth Deployment:
// scripts/pyth/deploy.pythoracle.ts
import { ethers } from "hardhat";
async function deployPythContract() {
const CONTRACT_NAME = "Oracle_Pyth";
// Update with your network's Pyth address
const PYTH_ADDRESS = "0x7cE7845bDE4277e8Aa132aC4c042605e7d42B71C";
console.log("Deploying...");
const pyth = await ethers.deployContract(CONTRACT_NAME, [PYTH_ADDRESS]);
await pyth.waitForDeployment();
const address = await pyth.getAddress();
console.log("Deployed Pyth Contract Address:", address);
// Save address for later use
return address;
}
async function main() {
const deployedAddress = await deployPythContract();
// You can add additional logic here, like verifying the contract
}
main().catch((error) => {
console.error(error);
process.exit(1);
});Import and use oracle contracts in your own smart contracts:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
import "./contracts/pyth/PythOracle.sol";
contract MyDApp {
Oracle_Pyth public pythOracle;
constructor(address _pythOracle) {
pythOracle = Oracle_Pyth(_pythOracle);
}
function getPrice(bytes32 priceId) public view returns (uint256) {
return pythOracle.read(priceId);
}
// Your dApp logic here
}Create custom deployment scripts that deploy multiple contracts:
// scripts/deploy-all.ts
import { ethers } from "hardhat";
async function main() {
// Deploy Pyth Oracle
const pyth = await ethers.deployContract("Oracle_Pyth", [PYTH_ADDRESS]);
await pyth.waitForDeployment();
console.log("Pyth deployed to:", await pyth.getAddress());
// Deploy your dApp contract
const myDApp = await ethers.deployContract("MyDApp", [await pyth.getAddress()]);
await myDApp.waitForDeployment();
console.log("MyDApp deployed to:", await myDApp.getAddress());
}
main().catch((error) => {
console.error(error);
process.exit(1);
});-
Local Testing: Use Hardhat's local network:
npx hardhat node # In another terminal npx hardhat run scripts/pyth/deploy.pythoracle.ts --network localhost -
Testnet Testing: Deploy to Monad testnet:
npx hardhat run scripts/pyth/deploy.pythoracle.ts --network monadTestnet
-
Verify Contracts: After deployment, verify on explorer:
npx hardhat verify --network monadTestnet <CONTRACT_ADDRESS> <CONSTRUCTOR_ARGS>
- Error Handling: Always add error handling in your contracts
- Price Validation: Check price staleness and validity
- Gas Optimization: Consider gas costs when reading prices
- Multiple Oracles: Consider using multiple oracles for critical price feeds
- Testing: Thoroughly test on Monad testnet before deploying to Monad mainnet (when available)
This is the fastest way to test an oracle:
-
Deploy:
npx hardhat run scripts/pyth/deploy.pythoracle.ts --network monadTestnet
-
Copy the deployed address from output
-
Update read script with the address
-
Read price:
npx hardhat run scripts/pyth/pyth.read.ts --network monadTestnet
-
Deploy oracle wrapper (if needed)
-
Create your contract that uses the oracle:
import "./contracts/pyth/PythOracle.sol"; contract MyDApp { Oracle_Pyth public oracle; // Your contract logic }
-
Deploy your contract with oracle address as constructor parameter
-
Test locally first, then deploy to testnet
For redundancy, you can use multiple oracles:
contract MultiOraclePriceFeed {
Oracle_Pyth public pythOracle;
PriceFeedBase public redstoneOracle;
function getPrice() public view returns (uint256) {
uint256 pythPrice = pythOracle.read(ETH_FEED_ID);
// Compare with Redstone price
// Return average or use other logic
}
}Create your own reader contract:
contract CustomOracleReader {
Oracle_Pyth public pyth;
constructor(address _pyth) {
pyth = Oracle_Pyth(_pyth);
}
function getMultiplePrices(bytes32[] memory feedIds)
public
view
returns (uint256[] memory prices)
{
prices = new uint256[](feedIds.length);
for (uint i = 0; i < feedIds.length; i++) {
prices[i] = pyth.read(feedIds[i]);
}
}
}Pyth Network provides high-frequency price feeds for various assets. The template includes a wrapper contract that normalizes prices to 18 decimals.
-
Find Pyth Contract Address on Monad:
- Check Pyth Network Documentation for Monad testnet addresses
- Or check the Monad explorer for deployed Pyth contracts
- Example testnet address:
0x7cE7845bDE4277e8Aa132aC4c042605e7d42B71C
-
Edit Deployment Script:
Open
scripts/pyth/deploy.pythoracle.tsand update:const PYTH_ADDRESS = "0x7cE7845bDE4277e8Aa132aC4c042605e7d42B71C"; // Update this
-
Deploy the Contract:
npx hardhat run scripts/pyth/deploy.pythoracle.ts --network monadTestnet
-
Save the Deployed Address:
Copy the output address. You'll need it for reading prices and integrating into your contracts.
-
Find Price Feed ID:
Visit Pyth Network Price Feeds to find the feed ID for your asset.
Common feed IDs:
- ETH/USD:
0xff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace - BTC/USD:
0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43 - USDC/USD:
0xeaa020c61cc479712813461ce153894a96a6c00b21ed0cfc2798d1f9a9e9c94a
- ETH/USD:
-
Edit Read Script:
Open
scripts/pyth/pyth.read.tsand update:const PythAddress = "0xYOUR_DEPLOYED_CONTRACT_ADDRESS"; // Your deployed contract const pythId = "0xff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace"; // ETH/USD
-
Read the Price:
npx hardhat run scripts/pyth/pyth.read.ts --network monadTestnet
-
Interpret the Result:
The output shows the price in 18 decimals. To convert to USD:
const priceInUSD = ethers.formatUnits(price, 18); console.log("Price:", priceInUSD, "USD");
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
import "./contracts/pyth/PythOracle.sol";
contract MyContract {
Oracle_Pyth public pythOracle;
bytes32 public constant ETH_USD_FEED_ID = 0xff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace;
constructor(address _pythOracle) {
pythOracle = Oracle_Pyth(_pythOracle);
}
function getETHPrice() public view returns (uint256) {
return pythOracle.read(ETH_USD_FEED_ID);
}
function getPriceInUSD(bytes32 feedId) public view returns (uint256) {
uint256 price = pythOracle.read(feedId);
// Price is already in 18 decimals
return price;
}
}Key Points:
- Prices are normalized to 18 decimals
- Use
read(bytes32 priceFeedId)to get the latest price - Prices are updated frequently by Pyth network
The Chronicle oracle uses pre-deployed contracts on Monad. You can deploy a reader contract:
npx hardhat run scripts/chronicle/deploy.chronicleoracle.ts --network monadTestnetAvailable Chronicle Feeds on Monad:
- CBBTC_USD:
0xcB0ABe397952844C379A29343cDb17c914F33e40 - ETH_USD:
0xC32753217DcC7Bb2F449bD6f1bC384d1AC72a7B6 - PUMPBTC_USD:
0x9ee0DC1f7cF1a5c083914e3de197Fd1F484E0578 - SOLVBTC_USD:
0xC991e18E3f167F7457e06B780e92EA94a6b3c1bb - USDC_USD:
0xd800ca44fFABecd159c7889c3bf64a217361AEc8 - USDT_USD:
0x09672B2a62Db1cd4cCE379bdde5BF41931177A72 - WBTC_USD:
0x8f01f70bE5DeEA5D4273D9a299A1A609BF1649c0
Example Contract Usage:
import "./contracts/chronicle/ChroncleOracle.sol";
OracleReader chronicleReader = OracleReader(chronicleAddress);
(uint256 value, uint256 age) = chronicleReader.read(1); // 1 = CBBTC_USD, 2 = ETH_USD, etc.Redstone uses a base contract that needs to be extended. Deploy your custom price feed:
npx hardhat run scripts/redstone/deploy.redstone.ts --network monadTestnetnpx hardhat run scripts/redstone/getPriceFeed.ts --network monadTestnetConfiguration: Update PRICE_FEED_ADDRESS with your deployed Redstone price feed address.
Example Contract Usage:
import "./contracts/redstone/PriceFeedBase.sol";
PriceFeedBase redstoneFeed = PriceFeedBase(priceFeedAddress);
(uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound) =
redstoneFeed.latestRoundData();
// Price is in 8 decimals
uint256 price = uint256(answer);npx hardhat run scripts/storkoracle/deploy.storkchainlink.ts --network monadTestnetConfiguration:
- Update
storkChainlinkAdapterAddresswith the Stork adapter address - Update
assetIdwith the asset ID you want to query (e.g., ETH/USD)
npx hardhat run scripts/storkoracle/deploy.storkpyth.ts --network monadTestnetnpx hardhat run scripts/storkoracle/getData.stork.ts --network monadTestnetConfiguration: Update storkChainlinkAdapterAddress with your deployed adapter address.
Example Contract Usage:
import "./contracts/storkoracle/StorkChainlinkOracle.sol";
TestStorkChainlinkAdapter storkAdapter = TestStorkChainlinkAdapter(adapterAddress);
(uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound) =
storkAdapter.latestRoundData();npx hardhat run scripts/switchboard/deploy.switchboard_oracle.ts --network monadTestnetConfiguration:
- Update
_switchboardwith the Switchboard contract address on Monad - Update
_aggregatorIdwith the aggregator ID for your price feed
npx hardhat run scripts/switchboard/getPriceFeed.ts --network monadTestnetConfiguration: Update PRICE_FEED_ADDRESS with your deployed Switchboard reader address.
Example Contract Usage:
import "./contracts/switchboard/Switchboard.sol";
SwitchboardOracleReader switchboardReader = SwitchboardOracleReader(readerAddress);
// Note: Switchboard requires updates to be submitted with getFeedData()
// The result is stored in the contract's result variable
int128 price = switchboardReader.result(); // Price in 18 decimalscontracts/
├── chronicle/
│ └── ChroncleOracle.sol # Chronicle oracle reader
├── pyth/
│ └── PythOracle.sol # Pyth oracle wrapper
├── redstone/
│ ├── core/ # Redstone core contracts
│ ├── interfaces/ # Redstone interfaces
│ └── PriceFeedBase.sol # Base price feed contract
├── storkoracle/
│ ├── StorkChainlinkOracle.sol # Stork Chainlink adapter
│ └── StorkPythOracle.sol # Stork Pyth adapter
└── switchboard/
└── Switchboard.sol # Switchboard oracle reader
scripts/
├── chronicle/
│ └── deploy.chronicleoracle.ts # Deploy Chronicle reader
├── pyth/
│ ├── deploy.pythoracle.ts # Deploy Pyth oracle
│ └── pyth.read.ts # Read Pyth prices
├── redstone/
│ └── getPriceFeed.ts # Read Redstone prices
├── storkoracle/
│ ├── deploy.storkchainlink.ts # Deploy Stork Chainlink adapter
│ ├── deploy.storkpyth.ts # Deploy Stork Pyth adapter
│ └── getData.stork.ts # Read Stork prices
└── switchboard/
├── deploy.switchboard_oracle.ts # Deploy Switchboard reader
└── getPriceFeed.ts # Read Switchboard prices
Different oracles use different decimal precisions:
- Pyth: 18 decimals (normalized in the wrapper contract)
- Chronicle: Varies by feed
- Redstone: 8 decimals
- Stork: Varies by adapter (typically 8 decimals)
- Switchboard: 18 decimals
Always check the contract documentation or source code for the exact decimal precision.
npx hardhat compilenpx hardhat testAfter deployment, you can verify your contracts on the explorer:
npx hardhat verify --network monadTestnet <CONTRACT_ADDRESS> <CONSTRUCTOR_ARGS>- Chain ID: 10143
- Explorer: https://testnet.monadexplorer.com/
- RPC URL: Configure in
hardhat.config.ts - Currency Symbol: MON
- Block Explorer API: Configured in
hardhat.config.tsfor contract verification
-
Open MetaMask and click the network dropdown
-
Click "Add Network" or "Add a network manually"
-
Enter the following details:
- Network Name: Monad Testnet
- RPC URL:
https://monad-testnet.g.alchemy.com/v2/YOUR_API_KEY(or your preferred RPC) - Chain ID:
10143 - Currency Symbol:
MON - Block Explorer URL:
https://testnet.monadexplorer.com/
-
Click "Save" to add the network
After completing the setup, verify everything works:
- Dependencies installed (
npm installcompleted successfully) -
.envfile created withDEPLOYER_ACCOUNT_PRIV_KEY - Network configured in
hardhat.config.ts - Contracts compile successfully (
npx hardhat compile) - Wallet has testnet tokens
- Successfully deployed at least one oracle contract
- Successfully read price from deployed oracle
Solution:
# Delete node_modules and reinstall
rm -rf node_modules package-lock.json
npm installCauses:
- Private key includes
0xprefix (remove it) - Private key is incorrect format
.envfile not found
Solution:
# Wrong:
DEPLOYER_ACCOUNT_PRIV_KEY=0x1234...
# Correct:
DEPLOYER_ACCOUNT_PRIV_KEY=1234...Solution:
- Ensure
.envfile exists and has correct variable name - Check that
dotenvis installed:npm list dotenv - Verify
hardhat.config.tsimports dotenv:import "dotenv/config";
Solution:
- Check your wallet balance on Monad testnet explorer
- Get testnet tokens from Monad faucet
- Verify you're using the correct network in your command
Possible causes:
- Network RPC URL is incorrect or unreachable
- Gas limit too low
- Contract constructor parameters incorrect
Solution:
# Test network connection
npx hardhat run scripts/pyth/deploy.pythoracle.ts --network monadTestnet --verbose
# Check RPC URL in hardhat.config.ts
# Try a different RPC endpointSolution:
- Verify contract was deployed successfully
- Check contract address is correct in read script
- Ensure you're using the same network
- Verify contract exists on explorer
Solution:
- Verify price feed ID is correct (check Pyth documentation)
- Ensure feed exists on Monad network
- Check feed ID format (should be bytes32, starting with 0x)
Solution:
- Some oracles have staleness checks
- Update the oracle feed before reading
- Check oracle documentation for update requirements
Solution:
- Switchboard requires payment for updates
- Calculate required fee:
uint256 fees = switchboard.getFee(updates); - Send enough ETH with the transaction
Solution:
- Check your internet connection
- Try a different RPC endpoint
- Verify RPC URL is correct
- Check if RPC provider has rate limits
Solution:
- Verify chain ID in
hardhat.config.tsmatches network - Monad Testnet chain ID:
10143 - Check MetaMask network settings if using it
Solution:
- Check Solidity version in
hardhat.config.ts - Ensure version matches contract pragma statements
- Try updating Hardhat:
npm install --save-dev hardhat@latest
Solution:
- Ensure all dependencies are installed:
npm install - Check if oracle SDK packages are installed
- Verify import paths are correct
If you encounter issues not covered here:
- Check Logs: Look at the full error message for clues
- Verify Setup: Go through the setup checklist again
- Test Locally: Try deploying to Hardhat local network first
- Check Documentation: Review oracle provider documentation
- Community: Ask in Monad or oracle provider Discord/forums
Enable verbose logging:
# Add --verbose flag
npx hardhat run scripts/pyth/deploy.pythoracle.ts --network monadTestnet --verbose
# Or use Hardhat console for interactive debugging
npx hardhat console --network monadTestnetWhen adding new oracle integrations:
- Create a new directory under
contracts/for your oracle - Add deployment scripts under
scripts/ - Add read scripts for testing
- Update this README with usage instructions
- Hardhat Documentation
- Pyth Network Documentation
- Chronicle Documentation
- Redstone Documentation
- Stork Network Documentation
- Switchboard Documentation
This project is provided as-is for educational and development purposes. Check individual oracle provider licenses for their specific terms.
For issues specific to:
- Monad Network: Check Monad Documentation
- Oracle Providers: Refer to their respective documentation
- Hardhat: Check Hardhat Troubleshooting Guide
Note: Always test thoroughly on Monad testnet before deploying to Monad mainnet (when available). Oracle integrations handle critical price data, so ensure proper error handling and validation in your production contracts.