Initial commit with 🏗️ create-eth @ 2.0.4

This commit is contained in:
han
2026-01-23 20:20:58 +07:00
commit b330aba2b4
185 changed files with 36981 additions and 0 deletions

View 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);
}
}

View 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
}
}
}

View 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);
};

View 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);
}
};

View 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;
};
}

View 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);
}
};