import { HardhatRuntimeEnvironment } from "hardhat/types"; import { DeployFunction } from "hardhat-deploy/types"; import { decodeEventLog } from "viem"; import { fetchPriceFromUniswap } from "../scripts/fetchPriceFromUniswap"; /** * Deploys a WhitelistOracle contract and creates SimpleOracle instances through it * * @param hre HardhatRuntimeEnvironment object. */ const deployWhitelistOracleContracts: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { const { deployer } = await hre.getNamedAccounts(); const { deploy } = hre.deployments; const { viem } = hre; const publicClient = await viem.getPublicClient(); console.log("Deploying WhitelistOracle contract..."); const whitelistOracleDeployment = await deploy("WhitelistOracle", { from: deployer, args: [], log: true, autoMine: false, }); const whitelistOracleAddress = whitelistOracleDeployment.address as `0x${string}`; const whitelistOracleAbi = whitelistOracleDeployment.abi; // Skip the rest of the setup if we are on a live network if (hre.network.name === "localhost") { // Get 10 wallet clients (accounts) to be oracle owners const accounts = await viem.getWalletClients(); const nodeAccounts = accounts.slice(0, 10); console.log("Creating SimpleOracle instances through WhitelistOracle..."); const deployerAccount = accounts.find(a => a.account.address.toLowerCase() === deployer.toLowerCase()); if (!deployerAccount) throw new Error("Deployer account not found in wallet clients"); // Create SimpleOracle instances through WhitelistOracle.addOracle() sequentially // (parallel nonce assignment doesn't work reliably with automining) const addOracleReceipts = []; for (let i = 0; i < nodeAccounts.length; i++) { const ownerAddress = nodeAccounts[i].account.address; console.log(`Creating SimpleOracle ${i + 1}/10 with owner: ${ownerAddress}`); const txHash = await deployerAccount.writeContract({ address: whitelistOracleAddress, abi: whitelistOracleAbi, functionName: "addOracle", args: [ownerAddress], }); const receipt = await publicClient.waitForTransactionReceipt({ hash: txHash }); addOracleReceipts.push(receipt); } // Map owner => created oracle address from events const ownerToOracleAddress = new Map(); for (const receipt of addOracleReceipts) { const oracleAddedEvent = receipt.logs.find(log => { try { const decoded = decodeEventLog({ abi: whitelistOracleAbi, data: log.data, topics: log.topics, }) as { eventName: string; args: { oracleAddress: string; oracleOwner: string } }; return decoded.eventName === "OracleAdded"; } catch { return false; } }); if (!oracleAddedEvent) continue; const decoded = decodeEventLog({ abi: whitelistOracleAbi, data: oracleAddedEvent.data, topics: oracleAddedEvent.topics, }) as { eventName: string; args: { oracleAddress: string; oracleOwner: string } }; ownerToOracleAddress.set(decoded.args.oracleOwner.toLowerCase(), decoded.args.oracleAddress); console.log(`✅ Created SimpleOracle at: ${decoded.args.oracleAddress}`); } const createdOracleAddresses: string[] = nodeAccounts.map(acc => { const addr = ownerToOracleAddress.get(acc.account.address.toLowerCase()); if (!addr) throw new Error(`Missing oracle address for owner ${acc.account.address}`); return addr; }); // Set initial prices for each created SimpleOracle console.log("Setting initial prices for each SimpleOracle..."); const initialPrice = await fetchPriceFromUniswap(); // Get SimpleOracle ABI from deployments const simpleOracleDeployment = await hre.deployments.getArtifact("SimpleOracle"); const simpleOracleAbi = simpleOracleDeployment.abi; // Fire all setPrice transactions concurrently from each node owner const setPriceTxPromises = nodeAccounts.map((account, i) => { const oracleAddress = createdOracleAddresses[i]; return account.writeContract({ address: oracleAddress as `0x${string}`, abi: simpleOracleAbi, functionName: "setPrice", args: [initialPrice], }); }); const setPriceTxHashes = await Promise.all(setPriceTxPromises); await Promise.all(setPriceTxHashes.map(hash => publicClient.waitForTransactionReceipt({ hash }))); for (let i = 0; i < createdOracleAddresses.length; i++) { console.log(`Set price for SimpleOracle ${i + 1} to: ${initialPrice}`); } // Calculate initial median price console.log("Calculating initial median price..."); const medianPrice = await publicClient.readContract({ address: whitelistOracleAddress, abi: whitelistOracleAbi, functionName: "getPrice", args: [], }); console.log(`Initial median price: ${medianPrice?.toString()}`); } console.log("WhitelistOracle contract deployed and configured successfully!"); }; export default deployWhitelistOracleContracts; deployWhitelistOracleContracts.tags = ["Oracles"];