#!/usr/bin/env node /** * Chainlink Keepers Setup Script * Registers PriceFeedKeeper with Chainlink Automation * * Usage: * node scripts/reserve/chainlink-keeper-setup.js * * Environment Variables: * RPC_URL_138 - ChainID 138 RPC endpoint * PRIVATE_KEY - Deployer private key * KEEPER_REGISTRY_ADDRESS - Chainlink KeeperRegistry address * PRICE_FEED_KEEPER_ADDRESS - PriceFeedKeeper contract address * LINK_TOKEN_ADDRESS - LINK token address * UPKEEP_INTERVAL - Upkeep interval in seconds (default: 30) * GAS_LIMIT - Gas limit for upkeep (default: 500000) * FUNDING_AMOUNT - LINK amount to fund (default: 10 LINK) */ const { ethers } = require('ethers'); require('dotenv').config(); const CONFIG = { rpcUrl: process.env.RPC_URL_138 || 'http://localhost:8545', privateKey: process.env.PRIVATE_KEY, keeperRegistry: process.env.KEEPER_REGISTRY_ADDRESS, keeperAddress: process.env.PRICE_FEED_KEEPER_ADDRESS, linkToken: process.env.LINK_TOKEN_ADDRESS, upkeepInterval: parseInt(process.env.UPKEEP_INTERVAL || '30'), gasLimit: parseInt(process.env.GAS_LIMIT || '500000'), fundingAmount: ethers.parseEther(process.env.FUNDING_AMOUNT || '10'), }; // Chainlink KeeperRegistry ABI (simplified) const KEEPER_REGISTRY_ABI = [ "function registerUpkeep(address target, uint32 gasLimit, address admin, bytes calldata checkData, uint96 amount, address source, bytes memory encryptedEmail) external returns (uint256)", "function getUpkeep(uint256 id) external view returns (address target, uint32 executeGas, bytes memory checkData, uint96 balance, address lastKeeper, uint96 balance, address admin, uint64 maxValidBlocknumber, uint96 amountSpent)", "function addFunds(uint256 id, uint96 amount) external", "event UpkeepRegistered(uint256 indexed id, address indexed target, uint32 gasLimit, address admin)", ]; // ChainlinkKeeperCompatible ABI const KEEPER_COMPATIBLE_ABI = [ "function checkUpkeep(bytes calldata checkData) external view returns (bool upkeepNeeded, bytes memory performData)", "function performUpkeep(bytes calldata performData) external", ]; // LINK Token ABI const LINK_TOKEN_ABI = [ "function approve(address spender, uint256 amount) external returns (bool)", "function transfer(address to, uint256 amount) external returns (bool)", "function balanceOf(address account) external view returns (uint256)", ]; async function main() { console.log('=== Chainlink Keepers Setup ===\n'); // Validate configuration if (!CONFIG.privateKey) throw new Error('PRIVATE_KEY is required'); if (!CONFIG.keeperRegistry) throw new Error('KEEPER_REGISTRY_ADDRESS is required'); if (!CONFIG.keeperAddress) throw new Error('PRICE_FEED_KEEPER_ADDRESS is required'); if (!CONFIG.linkToken) throw new Error('LINK_TOKEN_ADDRESS is required'); const provider = new ethers.JsonRpcProvider(CONFIG.rpcUrl); const wallet = new ethers.Wallet(CONFIG.privateKey, provider); console.log('Network:', CONFIG.rpcUrl); console.log('Deployer:', wallet.address); console.log('Keeper Registry:', CONFIG.keeperRegistry); console.log('Keeper Address:', CONFIG.keeperAddress); console.log('LINK Token:', CONFIG.linkToken); console.log('Upkeep Interval:', CONFIG.upkeepInterval, 'seconds'); console.log('Gas Limit:', CONFIG.gasLimit); console.log('Funding Amount:', ethers.formatEther(CONFIG.fundingAmount), 'LINK'); console.log(''); // Check LINK balance const linkToken = new ethers.Contract(CONFIG.linkToken, LINK_TOKEN_ABI, wallet); const linkBalance = await linkToken.balanceOf(wallet.address); console.log('LINK Balance:', ethers.formatEther(linkBalance), 'LINK'); if (linkBalance < CONFIG.fundingAmount) { throw new Error(`Insufficient LINK balance. Need ${ethers.formatEther(CONFIG.fundingAmount)} LINK`); } // Approve LINK spending console.log('\nApproving LINK spending...'); const approveTx = await linkToken.approve(CONFIG.keeperRegistry, CONFIG.fundingAmount); await approveTx.wait(); console.log('✓ LINK approved'); // Register upkeep console.log('\nRegistering upkeep...'); const keeperRegistry = new ethers.Contract(CONFIG.keeperRegistry, KEEPER_REGISTRY_ABI, wallet); const checkData = ethers.toUtf8Bytes(''); // Empty check data const encryptedEmail = ethers.toUtf8Bytes(''); // Empty email const registerTx = await keeperRegistry.registerUpkeep( CONFIG.keeperAddress, CONFIG.gasLimit, wallet.address, // Admin checkData, CONFIG.fundingAmount, wallet.address, // Source encryptedEmail ); console.log('Transaction sent:', registerTx.hash); const receipt = await registerTx.wait(); // Parse UpkeepRegistered event const event = receipt.logs.find(log => { try { const parsed = keeperRegistry.interface.parseLog(log); return parsed.name === 'UpkeepRegistered'; } catch { return false; } }); if (event) { const parsed = keeperRegistry.interface.parseLog(event); const upkeepId = parsed.args.id; console.log('✓ Upkeep registered!'); console.log('Upkeep ID:', upkeepId.toString()); console.log(''); console.log('=== Setup Complete ==='); console.log('Upkeep ID:', upkeepId.toString()); console.log('Monitor at:', `https://automation.chain.link/${upkeepId}`); } else { console.log('⚠ Could not parse UpkeepRegistered event'); console.log('Check transaction receipt for upkeep ID'); } } main().catch(error => { console.error('Error:', error.message); process.exit(1); });