A Node.js client for interacting with Chainlink's cNGN/USD price feed oracle on Base mainnet. This implementation provides real-time exchange rate monitoring, event-driven updates, and transaction capabilities for the Nigerian Naira to US Dollar pair.
- Overview
- Features
- Prerequisites
- Installation
- Configuration
- Usage
- API Reference
- HTTP API Endpoints
- Events
- Examples
- Security Considerations
- Troubleshooting
- Contributing
- License
This client interfaces with Chainlink's cNGN/USD price feed oracle deployed on Base mainnet at address 0xdfbb5Cbc88E382de007bfe6CE99C388176ED80aD. The oracle provides reliable, decentralized exchange rate data for the Nigerian Naira (NGN) against the US Dollar (USD).
Chainlink's cNGN oracle is part of their comprehensive price feed network, providing:
- Decentralized data aggregation from multiple sources
- High reliability with multiple node operators
- Tamper-resistant price information
- Regular updates based on market conditions
- π Real-time Price Monitoring - Continuous polling of current exchange rates
- π‘ Event-Driven Architecture - Listen to price update events in real-time
- π Historical Data Query - Access past price updates and events
- πΈ Transaction Support - Send transactions to interact with the oracle
- π‘οΈ Error Handling - Robust error management and retry logic
- π Detailed Logging - Comprehensive logging for debugging and monitoring
Before you begin, ensure you have the following installed:
- Node.js v16.0.0 or higher
- npm or yarn package manager
- Git for version control
You'll also need:
- A Base mainnet compatible wallet with private key
- ETH on Base mainnet for gas fees
- RPC endpoint (default uses public Base RPC)
- Clone the repository
git clone https://github.com/wrappedcbdc/cngn-price-oracle.git
cd cngn-price-oracle- Install dependencies
npm installor with yarn:
yarn install- Set up environment variables
cp .env.example .envCreate a .env file in the root directory with the following variables:
# Required
PRIVATE_KEY=your_wallet_private_key_without_0x_prefix
# Optional (defaults provided)
BASE_RPC_URL=https://mainnet.base.org
ORACLE_CONTRACT_ADDRESS=0xdfbb5Cbc88E382de007bfe6CE99C388176ED80aD
POLLING_INTERVAL=30000 # Price check interval in millisecondsThe client is configured for Base mainnet by default. Network details:
| Parameter | Value |
|---|---|
| Network | Base Mainnet |
| Chain ID | 8453 |
| Currency | ETH |
| Oracle | Chainlink cNGN/USD |
| Contract | 0xdfbb5Cbc88E382de007bfe6CE99C388176ED80aD |
# Run the oracle client
npm start
# Run in development mode with auto-restart
npm run dev# Start the API server
npm run api # production mode
npm run dev:api # development mode with auto-restartconst { NGNUSDOracle } = require('./oracle');
const privateKey: string = "private_key";
const rpcUrl: string = "https://mainnet.base.org";
const provider = new ethers.JsonRpcProvider(rpcUrl);
const wallet = new ethers.Wallet(privateKey, provider);
const signer = wallet.connect(provider);
async function main() {
// Initialize the oracle client
const oracle = new NGNUSDOracle(process.env.PRIVATE_KEY);
// Get current price
const price = await oracle.getCurrentPrice();
console.log(`Current rate: ${price.formattedPrice}`);
// Start monitoring with events
oracle.setupEventListeners();
await oracle.monitorPrice(60000); // Check every minute
}
main().catch(console.error);new NGNUSDOracle(privateKey)Creates a new instance of the NGN/USD oracle client.
Parameters:
privateKey(string): Wallet private key for signing transactions
Fetches the current NGN/USD exchange rate.
Returns: Promise<Object>
{
price: 1450.50, // Numeric price
decimals: 8, // Price decimals
timestamp: 1699564800, // Unix timestamp
formattedPrice: "1 USD = 1450.50 NGN" // Human-readable format
}Retrieves detailed information about the latest price update round.
Returns: Promise<Object>
{
roundId: "18446744073709562776",
answer: 145050000000, // Raw price with decimals
startedAt: 1699564800,
updatedAt: 1699564800,
answeredInRound: "18446744073709562776"
}Initializes event listeners for real-time updates.
oracle.setupEventListeners();Starts continuous price monitoring.
Parameters:
intervalMs(number): Polling interval in milliseconds (default: 60000)
Queries past price update events.
Parameters:
fromBlock(number|string): Starting block number or 'latest'toBlock(number|string): Ending block number or 'latest'
Returns: Promise<Array> of event objects
The project includes a REST API server (implemented in api.js) that exposes HTTP endpoints for easy integration with web applications and external services.
Health check endpoint to verify API server status.
Response:
{
"status": "ok",
"timestamp": "2024-01-15T10:30:00.000Z",
"oracle": "initialized"
}Retrieves the current NGN/USD exchange rate with comprehensive price data.
Response:
{
"success": true,
"data": {
"usdToNgn": 1450.50,
"ngnToUsd": 0.000689,
"formattedPrice": "1 USD = 1450.50 NGN",
"reversePrice": "1 NGN = 0.000689 USD",
"decimals": 8,
"description": "NGN/USD",
"timestamp": "2024-01-15T10:30:00.000Z"
}
}Fetches detailed information about the latest price update round.
Response:
{
"success": true,
"data": {
"roundId": "18446744073709562776",
"usdToNgn": 1450.50,
"ngnToUsd": 0.000689,
"formattedPrice": "1 USD = 1450.50 NGN",
"reversePrice": "1 NGN = 0.000689 USD",
"updatedAt": 1699564800,
"updatedAtFormatted": "2024-01-15T10:30:00.000Z",
"answeredInRound": "18446744073709562776"
}
}Converts a specified USD amount to NGN using the current exchange rate.
Parameters:
amount(path parameter): USD amount to convert (must be a valid number)
Example: /api/convert/usd-to-ngn/100
Response:
{
"success": true,
"data": {
"usdAmount": 100,
"ngnAmount": 145050,
"rate": 1450.50,
"formattedResult": "100 USD = 145050.00 NGN",
"timestamp": "2024-01-15T10:30:00.000Z"
}
}Converts a specified NGN amount to USD using the current exchange rate.
Parameters:
amount(path parameter): NGN amount to convert (must be a valid number)
Example: /api/convert/ngn-to-usd/145050
Response:
{
"success": true,
"data": {
"ngnAmount": 145050,
"usdAmount": 100.000000,
"rate": 0.000689,
"formattedResult": "145050 NGN = 100.000000 USD",
"timestamp": "2024-01-15T10:30:00.000Z"
}
}Retrieves historical price update events from the blockchain.
Parameters:
blocks(query parameter, optional): Number of blocks to query (default: 50)
Example: /api/history?blocks=100
Response:
{
"success": true,
"data": {
"events": [
{
"roundId": "18446744073709562776",
"price": 1450.50,
"formattedPrice": "1 USD = 1450.50 NGN",
"updatedAt": 1699564800,
"updatedAtFormatted": "2024-01-15T10:30:00.000Z",
"blockNumber": 12345678,
"transactionHash": "0x..."
}
],
"count": 25,
"blocksQueried": 100
}
}Error Response Format:
All endpoints return errors in the following format:
{
"success": false,
"error": "Error description",
"message": "Detailed error message"
}The oracle emits the following events:
Emitted when the oracle price is updated.
oracle.contract.on('AnswerUpdated', (current, roundId, updatedAt, event) => {
console.log('New price:', current);
console.log('Round ID:', roundId);
console.log('Updated at:', updatedAt);
});async function checkPrice() {
const oracle = new NGNUSDOracle(process.env.PRIVATE_KEY);
const { formattedPrice } = await oracle.getCurrentPrice();
console.log(formattedPrice);
}async function analyzeHistory() {
const oracle = new NGNUSDOracle(process.env.PRIVATE_KEY);
const events = await oracle.queryHistoricalEvents(1000);
events.forEach(event => {
console.log(`${event.updatedAtFormatted}: ${event.formattedPrice}`);
});
}async function priceAlerts() {
const oracle = new NGNUSDOracle(process.env.PRIVATE_KEY);
const threshold = 1500; // Alert if 1 USD > 1500 NGN
oracle.contract.on('AnswerUpdated', async (current, roundId, updatedAt) => {
const decimals = await oracle.contract.decimals();
const rate = Number(current) / Math.pow(10, Number(decimals));
if (rate > threshold) {
console.log(`π¨ ALERT: NGN/USD rate exceeded ${threshold}!`);
// Send notification, email, etc.
}
});
}-
Private Key Management
- Never commit private keys to version control
- Use environment variables for sensitive data
- Consider using hardware wallets for production
-
RPC Security
- Use private RPC endpoints for production
- Implement rate limiting to avoid DOS
- Monitor for unusual activity
-
Transaction Security
- Always estimate gas before sending transactions
- Implement transaction retry logic
- Set appropriate gas price limits
-
Best Practices
// Good: Using environment variable
const oracle = new NGNUSDOracle(process.env.PRIVATE_KEY);
// Bad: Hardcoding private key
const oracle = new NGNUSDOracle("abc123...");Error: Could not connect to Base mainnet
Solution: Check your internet connection and RPC endpoint availability.
Error: Insufficient funds for gas
Solution: Ensure your wallet has enough ETH on Base mainnet.
Error: Contract method not found
Solution: Verify you're using the correct contract ABI.
Enable debug logging:
const oracle = new NGNUSDOracle(privateKey, { debug: true });async function healthCheck() {
const oracle = new NGNUSDOracle(process.env.PRIVATE_KEY);
try {
await oracle.getCurrentPrice();
console.log('β
Oracle is healthy');
} catch (error) {
console.error('β Oracle health check failed:', error);
}
}We welcome contributions! Please follow these steps:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
# Install development dependencies
npm install --save-dev
# Run tests
npm test
# Run linter
npm run lint
# Build documentation
npm run docsThis project is licensed under the MIT License - see the LICENSE file for details.
- Chainlink Documentation
- Base Network Documentation
- Ethers.js Documentation
- Oracle Contract on Basescan
- Base Bridge
- Chainlink Price Feeds
- Open an issue on GitHub
- Email: contact@cngn.co
Disclaimer: This software is provided "as is" without warranty of any kind. Always test thoroughly before using in production environments.