Initial commit with 🏗️ create-eth @ 2.0.4
This commit is contained in:
233
packages/hardhat/test/WhitelistOracle.ts
Normal file
233
packages/hardhat/test/WhitelistOracle.ts
Normal 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);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user