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,233 @@
import { expect } from "chai";
import { ethers } from "hardhat";
import type { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers";
import type { WhitelistOracle, SimpleOracle } from "../typechain-types";
describe("Checkpoint1", function () {
before(async () => {
await ethers.provider.send("evm_setAutomine", [true]);
await ethers.provider.send("evm_setIntervalMining", [0]);
});
let whitelistOracle: WhitelistOracle;
let owner: HardhatEthersSigner,
addr1: HardhatEthersSigner,
addr2: HardhatEthersSigner,
addr3: HardhatEthersSigner,
addr4: HardhatEthersSigner;
const contractAddress = process.env.CONTRACT_ADDRESS;
if (contractAddress) {
// If env variable is set then skip this test file (for the auto-grader)
return true;
}
beforeEach(async function () {
[owner, addr1, addr2, addr3, addr4] = await ethers.getSigners();
const WhitelistOracleFactory = await ethers.getContractFactory("WhitelistOracle");
whitelistOracle = await WhitelistOracleFactory.deploy();
});
it("Should deploy and set owner", async function () {
expect(await whitelistOracle.owner()).to.equal(owner.address);
});
it("Should allow adding oracles and deploy SimpleOracle contracts", async function () {
await whitelistOracle.addOracle(addr1.address);
const oracleAddress = await whitelistOracle.oracles(0);
expect(oracleAddress).to.not.equal(ethers.ZeroAddress);
// Check that the oracle is a SimpleOracle contract
const SimpleOracleFactory = await ethers.getContractFactory("SimpleOracle");
const oracle = SimpleOracleFactory.attach(oracleAddress) as SimpleOracle;
expect(await oracle.owner()).to.equal(addr1.address);
});
it("Should allow removing oracles by index", async function () {
await whitelistOracle.addOracle(addr1.address);
await whitelistOracle.addOracle(addr2.address);
const oracle1Address = await whitelistOracle.oracles(0);
await whitelistOracle.removeOracle(0);
// After removal, the oracle at index 0 should be different (swapped from end)
const newOracle0Address = await whitelistOracle.oracles(0);
expect(newOracle0Address).to.not.equal(oracle1Address);
// Should only have one oracle left
await expect(whitelistOracle.oracles(1)).to.be.reverted;
});
it("Should emit OracleAdded event when an oracle is added", async function () {
const tx = await whitelistOracle.addOracle(addr1.address);
await tx.wait();
const oracleAddress = await whitelistOracle.oracles(0);
expect(tx).to.emit(whitelistOracle, "OracleAdded").withArgs(oracleAddress, addr1.address);
});
it("Should emit OracleRemoved event when an oracle is removed", async function () {
await whitelistOracle.addOracle(addr1.address);
const oracleAddress = await whitelistOracle.oracles(0);
await expect(whitelistOracle.removeOracle(0)).to.emit(whitelistOracle, "OracleRemoved").withArgs(oracleAddress);
});
it("Should revert with IndexOutOfBounds when trying to remove non-existent oracle", async function () {
await expect(whitelistOracle.removeOracle(0)).to.be.revertedWithCustomError(whitelistOracle, "IndexOutOfBounds");
await whitelistOracle.addOracle(addr1.address);
await expect(whitelistOracle.removeOracle(1)).to.be.revertedWithCustomError(whitelistOracle, "IndexOutOfBounds");
await whitelistOracle.removeOracle(0);
await expect(whitelistOracle.removeOracle(0)).to.be.revertedWithCustomError(whitelistOracle, "IndexOutOfBounds");
});
it("Should revert with NoOraclesAvailable when getPrice is called with no oracles", async function () {
await expect(whitelistOracle.getPrice()).to.be.revertedWithCustomError(whitelistOracle, "NoOraclesAvailable");
});
it("Should return correct price with one oracle", async function () {
await whitelistOracle.addOracle(addr1.address);
const oracleAddress = await whitelistOracle.oracles(0);
const SimpleOracleFactory = await ethers.getContractFactory("SimpleOracle");
const oracle = SimpleOracleFactory.attach(oracleAddress) as SimpleOracle;
await oracle.setPrice(1000n);
const price = await whitelistOracle.getPrice();
expect(price).to.equal(1000n);
});
it("Should return correct median price with odd number of oracles", async function () {
await whitelistOracle.addOracle(addr1.address);
await whitelistOracle.addOracle(addr2.address);
await whitelistOracle.addOracle(addr3.address);
const SimpleOracleFactory = await ethers.getContractFactory("SimpleOracle");
const oracle1 = SimpleOracleFactory.attach(await whitelistOracle.oracles(0)) as SimpleOracle;
const oracle2 = SimpleOracleFactory.attach(await whitelistOracle.oracles(1)) as SimpleOracle;
const oracle3 = SimpleOracleFactory.attach(await whitelistOracle.oracles(2)) as SimpleOracle;
await oracle1.setPrice(1000n);
await oracle2.setPrice(3000n);
await oracle3.setPrice(2000n);
const medianPrice = await whitelistOracle.getPrice();
expect(medianPrice).to.equal(2000n);
});
it("Should return correct median price with even number of oracles", async function () {
await whitelistOracle.addOracle(addr1.address);
await whitelistOracle.addOracle(addr2.address);
await whitelistOracle.addOracle(addr3.address);
await whitelistOracle.addOracle(addr4.address);
const SimpleOracleFactory = await ethers.getContractFactory("SimpleOracle");
const oracle1 = SimpleOracleFactory.attach(await whitelistOracle.oracles(0)) as SimpleOracle;
const oracle2 = SimpleOracleFactory.attach(await whitelistOracle.oracles(1)) as SimpleOracle;
const oracle3 = SimpleOracleFactory.attach(await whitelistOracle.oracles(2)) as SimpleOracle;
const oracle4 = SimpleOracleFactory.attach(await whitelistOracle.oracles(3)) as SimpleOracle;
await oracle1.setPrice(1000n);
await oracle2.setPrice(3000n);
await oracle3.setPrice(2000n);
await oracle4.setPrice(4000n);
const medianPrice = await whitelistOracle.getPrice();
expect(medianPrice).to.equal(2500n);
});
it("Should exclude price reports older than 24 seconds from median calculation", async function () {
await whitelistOracle.addOracle(addr1.address);
await whitelistOracle.addOracle(addr2.address);
await whitelistOracle.addOracle(addr3.address);
const SimpleOracleFactory = await ethers.getContractFactory("SimpleOracle");
const oracle1 = SimpleOracleFactory.attach(await whitelistOracle.oracles(0)) as SimpleOracle;
const oracle2 = SimpleOracleFactory.attach(await whitelistOracle.oracles(1)) as SimpleOracle;
const oracle3 = SimpleOracleFactory.attach(await whitelistOracle.oracles(2)) as SimpleOracle;
await oracle1.setPrice(1000n);
await oracle2.setPrice(2000n);
await oracle3.setPrice(3000n);
let medianPrice = await whitelistOracle.getPrice();
expect(medianPrice).to.equal(2000n);
// Advance time by 25 seconds (more than STALE_DATA_WINDOW of 24 seconds)
await ethers.provider.send("evm_increaseTime", [25]);
await ethers.provider.send("evm_mine");
// Set new prices for only two oracles (the old prices should be stale)
await oracle1.setPrice(5000n);
await oracle2.setPrice(3000n);
// Should only use the two fresh prices: median of [5000, 3000] = 4000
medianPrice = await whitelistOracle.getPrice();
expect(medianPrice).to.equal(4000n);
});
it("Should return empty array when no oracles are active", async function () {
const activeNodes = await whitelistOracle.getActiveOracleNodes();
expect(activeNodes.length).to.equal(0);
});
it("Should return correct active oracle nodes", async function () {
await whitelistOracle.addOracle(addr1.address);
await whitelistOracle.addOracle(addr2.address);
const oracle1Address = await whitelistOracle.oracles(0);
const oracle2Address = await whitelistOracle.oracles(1);
const SimpleOracleFactory = await ethers.getContractFactory("SimpleOracle");
const oracle1 = SimpleOracleFactory.attach(oracle1Address) as SimpleOracle;
const oracle2 = SimpleOracleFactory.attach(oracle2Address) as SimpleOracle;
await oracle1.setPrice(1000n);
await oracle2.setPrice(2000n);
let activeNodes = await whitelistOracle.getActiveOracleNodes();
expect(activeNodes.length).to.equal(2);
expect(activeNodes).to.include(oracle1Address);
expect(activeNodes).to.include(oracle2Address);
// Make oracle1's price stale
await ethers.provider.send("evm_increaseTime", [25]);
await ethers.provider.send("evm_mine");
// Update only oracle2
await oracle2.setPrice(3000n);
activeNodes = await whitelistOracle.getActiveOracleNodes();
expect(activeNodes.length).to.equal(1);
expect(activeNodes[0]).to.equal(oracle2Address);
});
it("Should handle edge case when all prices are stale but array is not empty", async function () {
await whitelistOracle.addOracle(addr1.address);
await whitelistOracle.addOracle(addr2.address);
const SimpleOracleFactory = await ethers.getContractFactory("SimpleOracle");
const oracle1 = SimpleOracleFactory.attach(await whitelistOracle.oracles(0)) as SimpleOracle;
const oracle2 = SimpleOracleFactory.attach(await whitelistOracle.oracles(1)) as SimpleOracle;
await oracle1.setPrice(1000n);
await oracle2.setPrice(2000n);
// Verify median works initially
const medianPrice = await whitelistOracle.getPrice();
expect(medianPrice).to.equal(1500n);
// Make all prices stale
await ethers.provider.send("evm_increaseTime", [25]);
await ethers.provider.send("evm_mine");
const activeNodes = await whitelistOracle.getActiveOracleNodes();
expect(activeNodes.length).to.equal(0);
});
});