Initial commit with 🏗️ create-eth @ 2.0.4
This commit is contained in:
29
packages/hardhat/scripts/oracle-bot/balances.ts
Normal file
29
packages/hardhat/scripts/oracle-bot/balances.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import { ethers } from "hardhat";
|
||||
import { formatEther } from "ethers";
|
||||
|
||||
export async function reportBalances() {
|
||||
try {
|
||||
// Get all signers (accounts)
|
||||
const signers = await ethers.getSigners();
|
||||
const oracleNodes = signers.slice(1, 11); // Get oracle node accounts
|
||||
|
||||
// Get the StakingOracle contract
|
||||
const oracleContract = await ethers.getContract("StakingOracle");
|
||||
const oracle = await ethers.getContractAt("StakingOracle", oracleContract.target);
|
||||
|
||||
// Get the ORA token address and create contract instance
|
||||
const oraTokenAddress = await oracle.oracleToken();
|
||||
const oraToken = await ethers.getContractAt("contracts/OracleToken.sol:ORA", oraTokenAddress);
|
||||
|
||||
console.log("\nNode Balances:");
|
||||
for (const node of oracleNodes) {
|
||||
const nodeInfo = await oracle.nodes(node.address);
|
||||
const oraBalance = await oraToken.balanceOf(node.address);
|
||||
console.log(`\nNode ${node.address}:`);
|
||||
console.log(` Staked ETH: ${formatEther(nodeInfo.stakedAmount)} ETH`);
|
||||
console.log(` ORA Balance: ${formatEther(oraBalance)} ORA`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error reporting balances:", error);
|
||||
}
|
||||
}
|
||||
56
packages/hardhat/scripts/oracle-bot/config.json
Normal file
56
packages/hardhat/scripts/oracle-bot/config.json
Normal file
@@ -0,0 +1,56 @@
|
||||
{
|
||||
"PRICE": {
|
||||
"CACHEDPRICE": 4000,
|
||||
"TIMESTAMP": 1761680177006
|
||||
},
|
||||
"INTERVALS": {
|
||||
"PRICE_REPORT": 1750,
|
||||
"VALIDATION": 1750
|
||||
},
|
||||
"NODE_CONFIGS": {
|
||||
"default": {
|
||||
"PROBABILITY_OF_SKIPPING_REPORT": 0,
|
||||
"PRICE_VARIANCE": 0
|
||||
},
|
||||
"0x70997970c51812dc3a010c7d01b50e0d17dc79c8": {
|
||||
"PROBABILITY_OF_SKIPPING_REPORT": 0,
|
||||
"PRICE_VARIANCE": 0
|
||||
},
|
||||
"0x9965507d1a55bcc2695c58ba16fb37d819b0a4dc": {
|
||||
"PROBABILITY_OF_SKIPPING_REPORT": 0,
|
||||
"PRICE_VARIANCE": 0
|
||||
},
|
||||
"0x976ea74026e726554db657fa54763abd0c3a0aa9": {
|
||||
"PROBABILITY_OF_SKIPPING_REPORT": 0,
|
||||
"PRICE_VARIANCE": 0
|
||||
},
|
||||
"0x90f79bf6eb2c4f870365e785982e1f101e93b906": {
|
||||
"PROBABILITY_OF_SKIPPING_REPORT": 0,
|
||||
"PRICE_VARIANCE": 0
|
||||
},
|
||||
"0x3c44cdddb6a900fa2b585dd299e03d12fa4293bc": {
|
||||
"PROBABILITY_OF_SKIPPING_REPORT": 0,
|
||||
"PRICE_VARIANCE": 0
|
||||
},
|
||||
"0x15d34aaf54267db7d7c367839aaf71a00a2c6a65": {
|
||||
"PROBABILITY_OF_SKIPPING_REPORT": 0,
|
||||
"PRICE_VARIANCE": 0
|
||||
},
|
||||
"0xbcd4042de499d14e55001ccbb24a551f3b954096": {
|
||||
"PROBABILITY_OF_SKIPPING_REPORT": 0,
|
||||
"PRICE_VARIANCE": 0
|
||||
},
|
||||
"0x14dc79964da2c08b23698b3d3cc7ca32193d9955": {
|
||||
"PROBABILITY_OF_SKIPPING_REPORT": 0,
|
||||
"PRICE_VARIANCE": 0
|
||||
},
|
||||
"0x23618e81e3f5cdf7f54c3d65f7fbc0abf5b21e8f": {
|
||||
"PROBABILITY_OF_SKIPPING_REPORT": 0,
|
||||
"PRICE_VARIANCE": 0
|
||||
},
|
||||
"0xa0ee7a142d267c1f36714e4a8f75612f20a79720": {
|
||||
"PROBABILITY_OF_SKIPPING_REPORT": 0,
|
||||
"PRICE_VARIANCE": 0
|
||||
}
|
||||
}
|
||||
}
|
||||
16
packages/hardhat/scripts/oracle-bot/price.ts
Normal file
16
packages/hardhat/scripts/oracle-bot/price.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { getConfig } from "../utils";
|
||||
|
||||
export const getRandomPrice = async (nodeAddress: string, currentPrice: number): Promise<number> => {
|
||||
const config = getConfig();
|
||||
const nodeConfig = config.NODE_CONFIGS[nodeAddress] || config.NODE_CONFIGS.default;
|
||||
|
||||
// Calculate variance range based on the node's PRICE_VARIANCE
|
||||
// PRICE_VARIANCE of 0 means no variance, higher values mean wider range
|
||||
const varianceRange = Math.floor(currentPrice * nodeConfig.PRICE_VARIANCE);
|
||||
|
||||
// Apply variance to the base price
|
||||
const finalPrice = currentPrice + (Math.random() * 2 - 1) * varianceRange;
|
||||
|
||||
// Round to nearest integer
|
||||
return Math.round(finalPrice);
|
||||
};
|
||||
80
packages/hardhat/scripts/oracle-bot/reporting.ts
Normal file
80
packages/hardhat/scripts/oracle-bot/reporting.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
import { PublicClient } from "viem";
|
||||
import { getRandomPrice } from "./price";
|
||||
import { HardhatRuntimeEnvironment } from "hardhat/types";
|
||||
import { getConfig } from "../utils";
|
||||
import { fetchPriceFromUniswap } from "../fetchPriceFromUniswap";
|
||||
import { DeployedContract } from "hardhat-deploy/types";
|
||||
|
||||
const getStakedAmount = async (
|
||||
publicClient: PublicClient,
|
||||
nodeAddress: `0x${string}`,
|
||||
oracleContract: DeployedContract,
|
||||
) => {
|
||||
const nodeInfo = (await publicClient.readContract({
|
||||
address: oracleContract.address as `0x${string}`,
|
||||
abi: oracleContract.abi,
|
||||
functionName: "nodes",
|
||||
args: [nodeAddress],
|
||||
})) as any[];
|
||||
|
||||
const [, stakedAmount] = nodeInfo;
|
||||
return stakedAmount as bigint;
|
||||
};
|
||||
|
||||
export const reportPrices = async (hre: HardhatRuntimeEnvironment) => {
|
||||
const { deployments } = hre;
|
||||
const oracleContract = await deployments.get("StakingOracle");
|
||||
const config = getConfig();
|
||||
const accounts = await hre.viem.getWalletClients();
|
||||
const oracleNodeAccounts = accounts.slice(1, 11);
|
||||
const publicClient = await hre.viem.getPublicClient();
|
||||
|
||||
// Get minimum stake requirement from contract
|
||||
const minimumStake = (await publicClient.readContract({
|
||||
address: oracleContract.address as `0x${string}`,
|
||||
abi: oracleContract.abi,
|
||||
functionName: "MINIMUM_STAKE",
|
||||
args: [],
|
||||
})) as unknown as bigint;
|
||||
|
||||
const currentPrice = Number(await fetchPriceFromUniswap());
|
||||
try {
|
||||
return Promise.all(
|
||||
oracleNodeAccounts.map(async account => {
|
||||
const nodeConfig = config.NODE_CONFIGS[account.account.address] || config.NODE_CONFIGS.default;
|
||||
const shouldReport = Math.random() > nodeConfig.PROBABILITY_OF_SKIPPING_REPORT;
|
||||
const stakedAmount = await getStakedAmount(publicClient, account.account.address, oracleContract);
|
||||
if (stakedAmount < minimumStake) {
|
||||
console.log(`Insufficient stake for ${account.account.address} for price reporting`);
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
if (shouldReport) {
|
||||
const price = BigInt(await getRandomPrice(account.account.address, currentPrice));
|
||||
console.log(`Reporting price ${price} from ${account.account.address}`);
|
||||
try {
|
||||
return await account.writeContract({
|
||||
address: oracleContract.address as `0x${string}`,
|
||||
abi: oracleContract.abi,
|
||||
functionName: "reportPrice",
|
||||
args: [price],
|
||||
});
|
||||
} catch (error: any) {
|
||||
if (error.message && error.message.includes("Not enough stake")) {
|
||||
console.log(
|
||||
`Skipping price report from ${account.account.address} - insufficient stake during execution`,
|
||||
);
|
||||
return Promise.resolve();
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
} else {
|
||||
console.log(`Skipping price report from ${account.account.address}`);
|
||||
return Promise.resolve();
|
||||
}
|
||||
}),
|
||||
);
|
||||
} catch (error) {
|
||||
console.error("Error reporting prices:", error);
|
||||
}
|
||||
};
|
||||
19
packages/hardhat/scripts/oracle-bot/types.ts
Normal file
19
packages/hardhat/scripts/oracle-bot/types.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
interface NodeConfig {
|
||||
PROBABILITY_OF_SKIPPING_REPORT: number;
|
||||
PRICE_VARIANCE: number; // Higher number means wider price range
|
||||
}
|
||||
|
||||
export interface Config {
|
||||
PRICE: {
|
||||
CACHEDPRICE: number;
|
||||
TIMESTAMP: number;
|
||||
};
|
||||
INTERVALS: {
|
||||
PRICE_REPORT: number;
|
||||
VALIDATION: number;
|
||||
};
|
||||
NODE_CONFIGS: {
|
||||
[key: string]: NodeConfig;
|
||||
default: NodeConfig;
|
||||
};
|
||||
}
|
||||
79
packages/hardhat/scripts/oracle-bot/validation.ts
Normal file
79
packages/hardhat/scripts/oracle-bot/validation.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
import { HardhatRuntimeEnvironment } from "hardhat/types";
|
||||
|
||||
const getStakedAmount = async (publicClient: any, nodeAddress: `0x${string}`, oracleContract: any) => {
|
||||
const nodeInfo = (await publicClient.readContract({
|
||||
address: oracleContract.address as `0x${string}`,
|
||||
abi: oracleContract.abi,
|
||||
functionName: "nodes",
|
||||
args: [nodeAddress],
|
||||
})) as any[];
|
||||
|
||||
const [, stakedAmount] = nodeInfo;
|
||||
return stakedAmount as bigint;
|
||||
};
|
||||
|
||||
export const claimRewards = async (hre: HardhatRuntimeEnvironment) => {
|
||||
const { deployments } = hre;
|
||||
const oracleContract = await deployments.get("StakingOracle");
|
||||
const accounts = await hre.viem.getWalletClients();
|
||||
const oracleNodeAccounts = accounts.slice(1, 11);
|
||||
const publicClient = await hre.viem.getPublicClient();
|
||||
|
||||
// Get minimum stake requirement from contract
|
||||
const minimumStake = (await publicClient.readContract({
|
||||
address: oracleContract.address as `0x${string}`,
|
||||
abi: oracleContract.abi,
|
||||
functionName: "MINIMUM_STAKE",
|
||||
args: [],
|
||||
})) as unknown as bigint;
|
||||
|
||||
try {
|
||||
return Promise.all(
|
||||
oracleNodeAccounts.map(async account => {
|
||||
const stakedAmount = await getStakedAmount(publicClient, account.account.address, oracleContract);
|
||||
|
||||
// Only claim rewards if the node has sufficient stake
|
||||
if (stakedAmount >= minimumStake) {
|
||||
try {
|
||||
console.log(`Claiming rewards for ${account.account.address}`);
|
||||
return await account.writeContract({
|
||||
address: oracleContract.address as `0x${string}`,
|
||||
abi: oracleContract.abi,
|
||||
functionName: "claimReward",
|
||||
args: [],
|
||||
});
|
||||
} catch (error: any) {
|
||||
if (error.message && error.message.includes("No rewards available")) {
|
||||
console.log(`Skipping reward claim for ${account.account.address} - no rewards available`);
|
||||
return Promise.resolve();
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
} else {
|
||||
console.log(`Skipping reward claim for ${account.account.address} - insufficient stake`);
|
||||
return Promise.resolve();
|
||||
}
|
||||
}),
|
||||
);
|
||||
} catch (error) {
|
||||
console.error("Error claiming rewards:", error);
|
||||
}
|
||||
};
|
||||
|
||||
// Keep the old validateNodes function for backward compatibility if needed
|
||||
export const validateNodes = async (hre: HardhatRuntimeEnvironment) => {
|
||||
const { deployments } = hre;
|
||||
const [account] = await hre.viem.getWalletClients();
|
||||
const oracleContract = await deployments.get("StakingOracle");
|
||||
|
||||
try {
|
||||
return await account.writeContract({
|
||||
address: oracleContract.address as `0x${string}`,
|
||||
abi: oracleContract.abi,
|
||||
functionName: "slashNodes",
|
||||
args: [],
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Error validating nodes:", error);
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user