Update OpenZeppelin contracts submodule to a dirty state
Some checks failed
Verify Deployment / Verify Deployment (push) Has been cancelled
CI/CD Pipeline / Solidity Contracts (push) Has been cancelled
CI/CD Pipeline / Security Scanning (push) Has been cancelled
CI/CD Pipeline / Lint and Format (push) Has been cancelled
CI/CD Pipeline / Terraform Validation (push) Has been cancelled
CI/CD Pipeline / Kubernetes Validation (push) Has been cancelled
Validation / validate-genesis (push) Has been cancelled
Validation / validate-terraform (push) Has been cancelled
Validation / validate-kubernetes (push) Has been cancelled
Validation / validate-smart-contracts (push) Has been cancelled
Validation / validate-security (push) Has been cancelled
Validation / validate-documentation (push) Has been cancelled

This commit is contained in:
defiQUG
2025-12-12 16:25:54 -08:00
parent 1fb7266469
commit 8dc7562702
8 changed files with 1484 additions and 0 deletions

211
scripts/reserve/keeper-service.js Executable file
View File

@@ -0,0 +1,211 @@
#!/usr/bin/env node
/**
* Price Feed Keeper Service
* Automatically updates price feeds at regular intervals
*
* Usage:
* node scripts/reserve/keeper-service.js
*
* Environment Variables:
* RPC_URL_138 - ChainID 138 RPC endpoint
* KEEPER_PRIVATE_KEY - Keeper wallet private key
* PRICE_FEED_KEEPER_ADDRESS - PriceFeedKeeper contract address
* UPDATE_INTERVAL - Update interval in seconds (default: 30)
*/
const { ethers } = require('ethers');
const fs = require('fs');
const path = require('path');
// Load environment variables
require('dotenv').config();
// Configuration
const CONFIG = {
rpcUrl: process.env.RPC_URL_138 || 'http://localhost:8545',
keeperPrivateKey: process.env.KEEPER_PRIVATE_KEY,
keeperAddress: process.env.PRICE_FEED_KEEPER_ADDRESS,
updateInterval: parseInt(process.env.UPDATE_INTERVAL || '30') * 1000, // Convert to milliseconds
maxRetries: 3,
retryDelay: 5000, // 5 seconds
};
// ABI for PriceFeedKeeper
const KEEPER_ABI = [
"function checkUpkeep() external view returns (bool needsUpdate, address[] memory assets)",
"function performUpkeep() external returns (bool success, address[] memory updatedAssets)",
"function updateAssets(address[] calldata assets) external",
"function getTrackedAssets() external view returns (address[] memory)",
"function needsUpdate(address asset) external view returns (bool)",
"function updateInterval() external view returns (uint256)",
"function maxUpdatesPerCall() external view returns (uint256)",
"event PriceFeedsUpdated(address[] assets, uint256 timestamp)"
];
class PriceFeedKeeperService {
constructor() {
if (!CONFIG.keeperPrivateKey) {
throw new Error('KEEPER_PRIVATE_KEY environment variable is required');
}
if (!CONFIG.keeperAddress) {
throw new Error('PRICE_FEED_KEEPER_ADDRESS environment variable is required');
}
this.provider = new ethers.JsonRpcProvider(CONFIG.rpcUrl);
this.wallet = new ethers.Wallet(CONFIG.keeperPrivateKey, this.provider);
this.keeper = new ethers.Contract(CONFIG.keeperAddress, KEEPER_ABI, this.wallet);
this.isRunning = false;
this.updateCount = 0;
this.errorCount = 0;
}
async start() {
console.log('=== Price Feed Keeper Service ===');
console.log(`RPC URL: ${CONFIG.rpcUrl}`);
console.log(`Keeper Address: ${CONFIG.keeperAddress}`);
console.log(`Wallet Address: ${this.wallet.address}`);
console.log(`Update Interval: ${CONFIG.updateInterval / 1000} seconds`);
console.log('');
// Verify keeper contract
try {
const trackedAssets = await this.keeper.getTrackedAssets();
const updateInterval = await this.keeper.updateInterval();
const maxUpdates = await this.keeper.maxUpdatesPerCall();
console.log(`Tracked Assets: ${trackedAssets.length}`);
trackedAssets.forEach((asset, i) => {
console.log(` ${i + 1}. ${asset}`);
});
console.log(`Update Interval: ${updateInterval.toString()} seconds`);
console.log(`Max Updates Per Call: ${maxUpdates.toString()}`);
console.log('');
} catch (error) {
console.error('Error verifying keeper contract:', error.message);
process.exit(1);
}
this.isRunning = true;
console.log('Keeper service started. Press Ctrl+C to stop.');
console.log('');
// Start update loop
this.updateLoop();
}
async updateLoop() {
while (this.isRunning) {
try {
await this.performUpkeep();
} catch (error) {
console.error(`Error in update loop: ${error.message}`);
this.errorCount++;
}
// Wait for next interval
await this.sleep(CONFIG.updateInterval);
}
}
async performUpkeep() {
try {
// Check if upkeep is needed
const [needsUpdate, assets] = await this.keeper.checkUpkeep();
if (!needsUpdate || assets.length === 0) {
console.log(`[${new Date().toISOString()}] No updates needed`);
return;
}
console.log(`[${new Date().toISOString()}] Updating ${assets.length} asset(s)...`);
// Perform upkeep
let retries = 0;
let success = false;
while (retries < CONFIG.maxRetries && !success) {
try {
const tx = await this.keeper.performUpkeep();
console.log(` Transaction sent: ${tx.hash}`);
const receipt = await tx.wait();
if (receipt.status === 1) {
success = true;
this.updateCount++;
// Parse events
const events = receipt.logs.filter(log => {
try {
const parsed = this.keeper.interface.parseLog(log);
return parsed.name === 'PriceFeedsUpdated';
} catch {
return false;
}
});
if (events.length > 0) {
const event = this.keeper.interface.parseLog(events[0]);
console.log(` Updated assets: ${event.args.assets.length}`);
console.log(` Timestamp: ${event.args.timestamp.toString()}`);
}
console.log(` ✓ Update successful (Gas: ${receipt.gasUsed.toString()})`);
} else {
throw new Error('Transaction failed');
}
} catch (error) {
retries++;
if (retries < CONFIG.maxRetries) {
console.log(` Retry ${retries}/${CONFIG.maxRetries}...`);
await this.sleep(CONFIG.retryDelay);
} else {
throw error;
}
}
}
if (!success) {
throw new Error('Failed after retries');
}
} catch (error) {
console.error(` ✗ Update failed: ${error.message}`);
this.errorCount++;
throw error;
}
}
async stop() {
console.log('\nStopping keeper service...');
this.isRunning = false;
console.log('\n=== Statistics ===');
console.log(`Total Updates: ${this.updateCount}`);
console.log(`Total Errors: ${this.errorCount}`);
console.log(`Success Rate: ${this.updateCount > 0 ? ((this.updateCount / (this.updateCount + this.errorCount)) * 100).toFixed(2) : 0}%`);
process.exit(0);
}
sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
}
// Main execution
if (require.main === module) {
const keeper = new PriceFeedKeeperService();
// Handle graceful shutdown
process.on('SIGINT', () => keeper.stop());
process.on('SIGTERM', () => keeper.stop());
// Start keeper
keeper.start().catch(error => {
console.error('Fatal error:', error);
process.exit(1);
});
}
module.exports = PriceFeedKeeperService;

109
scripts/reserve/keeper-service.sh Executable file
View File

@@ -0,0 +1,109 @@
#!/bin/bash
# Price Feed Keeper Service (Bash version)
# Simple bash-based keeper that calls the keeper contract periodically
set -e
# Load environment variables
if [ -f .env ]; then
source .env
fi
# Configuration
RPC_URL="${RPC_URL_138:-http://localhost:8545}"
KEEPER_ADDRESS="${PRICE_FEED_KEEPER_ADDRESS}"
UPDATE_INTERVAL="${UPDATE_INTERVAL:-30}" # seconds
MAX_RETRIES=3
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# Statistics
UPDATE_COUNT=0
ERROR_COUNT=0
# Function to log messages
log() {
echo -e "[$(date +'%Y-%m-%d %H:%M:%S')] $1"
}
# Function to check if upkeep is needed
check_upkeep() {
forge script script/reserve/CheckUpkeep.s.sol:CheckUpkeep \
--rpc-url "$RPC_URL" \
--silent 2>/dev/null | grep -E "(needsUpdate|assets)" || echo "false"
}
# Function to perform upkeep
perform_upkeep() {
log "${YELLOW}Performing upkeep...${NC}"
local retries=0
local success=false
while [ $retries -lt $MAX_RETRIES ] && [ "$success" = false ]; do
if forge script script/reserve/PerformUpkeep.s.sol:PerformUpkeep \
--rpc-url "$RPC_URL" \
--broadcast \
--silent 2>/dev/null; then
success=true
UPDATE_COUNT=$((UPDATE_COUNT + 1))
log "${GREEN}✓ Upkeep successful${NC}"
else
retries=$((retries + 1))
if [ $retries -lt $MAX_RETRIES ]; then
log "${YELLOW}Retry $retries/$MAX_RETRIES...${NC}"
sleep 5
else
ERROR_COUNT=$((ERROR_COUNT + 1))
log "${RED}✗ Upkeep failed after $MAX_RETRIES retries${NC}"
fi
fi
done
}
# Function to stop keeper
stop_keeper() {
log "\nStopping keeper service..."
log "\n=== Statistics ==="
log "Total Updates: $UPDATE_COUNT"
log "Total Errors: $ERROR_COUNT"
if [ $UPDATE_COUNT -gt 0 ]; then
local success_rate=$(echo "scale=2; ($UPDATE_COUNT * 100) / ($UPDATE_COUNT + $ERROR_COUNT)" | bc)
log "Success Rate: ${success_rate}%"
fi
exit 0
}
# Trap signals for graceful shutdown
trap stop_keeper SIGINT SIGTERM
# Main loop
main() {
log "${GREEN}=== Price Feed Keeper Service ===${NC}"
log "RPC URL: $RPC_URL"
log "Keeper Address: $KEEPER_ADDRESS"
log "Update Interval: $UPDATE_INTERVAL seconds"
log ""
log "Keeper service started. Press Ctrl+C to stop."
log ""
while true; do
# Check if upkeep is needed
if check_upkeep | grep -q "true"; then
perform_upkeep
else
log "No updates needed"
fi
# Wait for next interval
sleep "$UPDATE_INTERVAL"
done
}
# Run main function
main