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 install
or with yarn:
yarn install
- Set up environment variables
cp .env.example .env
Create 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_ADDRESS=0xdfbb5Cbc88E382de007bfe6CE99C388176ED80aD
POLLING_INTERVAL=30000 # Price check interval in milliseconds
The 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-restart
const { 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 docs
This 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.