@nodeset/contracts
Version:
Protocol for accessing NodeSet's Constellation Ethereum staking network
177 lines (141 loc) • 11 kB
text/typescript
import { expect } from "chai";
import { ethers, upgrades, hardhatArguments } from "hardhat";
import { loadFixture } from "@nomicfoundation/hardhat-network-helpers";
import { protocolFixture } from "../test";
describe("Yield Accrual", function () {
describe("When sender is not protocol", async () => {
it("should revert", async () => {
const { protocol, signers, rocketPool } = await loadFixture(protocolFixture);
await expect(protocol.operatorDistributor.connect(signers.random5).onEthBeaconRewardsReceived(6, 6, 6)).to.be.revertedWith("Can only be called by Protocol!");
})
})
describe("When sender is protocol", async () => {
describe("When rewards are positive", async () => {
describe("When insufficient balance", async () => {
describe("When there is not enough eth in Asset Router for avgTreasuryFee", async () => {
it("should revert", async () => {
const { protocol, signers, rocketPool } = await loadFixture(protocolFixture);
const reward = ethers.utils.parseEther("1")
const avgTreasuryFee = ethers.utils.parseEther("1")
const avgOperatorsFee = ethers.utils.parseEther("0")
await expect(protocol.operatorDistributor.connect(signers.protocolSigner).onEthBeaconRewardsReceived(reward, avgTreasuryFee, avgOperatorsFee)).to.be.revertedWith("Transfer to treasury failed");
})
})
describe("When there is not enough eth in Asset Router for avgOperatorsFee", async () => {
it("should revert", async () => {
const { protocol, signers, rocketPool } = await loadFixture(protocolFixture);
const reward = ethers.utils.parseEther("1")
const avgTreasuryFee = ethers.utils.parseEther("0")
const avgOperatorsFee = ethers.utils.parseEther("1")
await expect(protocol.operatorDistributor.connect(signers.protocolSigner).onEthBeaconRewardsReceived(reward, avgTreasuryFee, avgOperatorsFee)).to.be.revertedWith("Transfer to operator fee address failed");
})
})
})
describe("When sufficent balance", async () => {
describe("When oracle error is true", async () => {
describe("When community rewards are positive", async () => {
type ParamsType = { avgTreasuryFeeRaw: number; avgOperatorsFeeRaw: number; };
[
{ avgTreasuryFeeRaw: .6, avgOperatorsFeeRaw: .4, },
{ avgTreasuryFeeRaw: .1, avgOperatorsFeeRaw: .1, },
{ avgTreasuryFeeRaw: .5, avgOperatorsFeeRaw: .4, },
{ avgTreasuryFeeRaw: .99, avgOperatorsFeeRaw: .01, },
{ avgTreasuryFeeRaw: .99999, avgOperatorsFeeRaw: .00001, },
{ avgTreasuryFeeRaw: 1, avgOperatorsFeeRaw: 0, },
{ avgTreasuryFeeRaw: 0, avgOperatorsFeeRaw: 1, }
].forEach((params: ParamsType) => {
it(`should increase balance & call onIncreaseOracleError: avgTreasuryFee=${params.avgTreasuryFeeRaw}, avgOperatorsFee=${params.avgOperatorsFeeRaw}`, async () => {
const { protocol, signers, rocketPool } = await loadFixture(protocolFixture);
const reward = ethers.utils.parseEther("1")
const avgTreasuryFee = ethers.utils.parseEther(`${params.avgTreasuryFeeRaw}`) // 50%
const avgOperatorsFee = ethers.utils.parseEther(`${params.avgOperatorsFeeRaw}`) // 50%
expect(await ethers.provider.getBalance(protocol.operatorDistributor.address)).equals(0);
// simulate reward sent to operatorDistributor
await signers.random.sendTransaction({
to: protocol.operatorDistributor.address,
value: reward
})
expect(await ethers.provider.getBalance(protocol.operatorDistributor.address)).equals(reward);
expect(await ethers.provider.getBalance(protocol.yieldDistributor.address)).equals(0);
expect(await protocol.wETH.balanceOf(protocol.operatorDistributor.address)).equals(0);
const expectedTreasuryPortion = reward.mul(avgTreasuryFee).div(ethers.utils.parseEther("1"))
const expectedOperatorPortion = reward.mul(avgOperatorsFee).div(ethers.utils.parseEther("1"))
const expectedCommunityPortion = reward.sub(expectedTreasuryPortion.add(expectedOperatorPortion));
const tx = await protocol.operatorDistributor.connect(signers.protocolSigner).onEthBeaconRewardsReceived(reward, avgTreasuryFee, avgOperatorsFee)
const receipt = await tx.wait();
const block = receipt.blockNumber;
const initialBalanceTreasury = await ethers.provider.getBalance(await protocol.directory.getTreasuryAddress(), block - 1);
const finalBalanceTreasury = await ethers.provider.getBalance(await protocol.directory.getTreasuryAddress(), block);
expect(finalBalanceTreasury.sub(initialBalanceTreasury)).equals(expectedTreasuryPortion);
expect(await ethers.provider.getBalance(protocol.yieldDistributor.address)).equals(expectedOperatorPortion);
expect(await ethers.provider.getBalance(protocol.operatorDistributor.address)).equals(expectedCommunityPortion);
//expect(await protocol.wETH.balanceOf(protocol.operatorDistributor.address)).equals(expectedCommunityPortion);
expect(await protocol.operatorDistributor.oracleError()).equals(expectedCommunityPortion);
})
})
})
})
describe("When community rewards are negative", async () => {
it("should revert", async () => {
const { protocol, signers, rocketPool } = await loadFixture(protocolFixture);
const reward = ethers.utils.parseEther("1")
const avgTreasuryFee = ethers.utils.parseEther(".9") // 90%
const avgOperatorsFee = ethers.utils.parseEther(".9") // 90%
expect(await ethers.provider.getBalance(protocol.operatorDistributor.address)).equals(0);
// simulate reward sent to asset router
await signers.ethWhale.sendTransaction({
to: protocol.operatorDistributor.address,
value: reward.mul(10)
})
expect(await ethers.provider.getBalance(protocol.operatorDistributor.address)).equals(reward.mul(10));
expect(await ethers.provider.getBalance(protocol.yieldDistributor.address)).equals(0);
const expectedTreasuryPortion = reward.mul(avgTreasuryFee).div(ethers.utils.parseEther("1"))
const expectedOperatorPortion = reward.mul(avgOperatorsFee).div(ethers.utils.parseEther("1"))
// expect the community rewards underflow to cause revert
await expect(protocol.operatorDistributor.connect(signers.protocolSigner).onEthBeaconRewardsReceived(reward, avgTreasuryFee, avgOperatorsFee)).to.be.revertedWithPanic('0x11')
})
})
})
})
describe("When rewards are 0", async () => {
it("does nothing", async () => {
const { protocol, signers, rocketPool } = await loadFixture(protocolFixture);
const reward = ethers.utils.parseEther("0")
const avgTreasuryFee = ethers.utils.parseEther(".6") // 50%
const avgOperatorsFee = ethers.utils.parseEther(".4") // 50%
expect(await ethers.provider.getBalance(protocol.operatorDistributor.address)).equals(0);
// simulate reward sent to asset router
await signers.ethWhale.sendTransaction({
to: protocol.operatorDistributor.address,
value: reward
})
expect(await ethers.provider.getBalance(protocol.operatorDistributor.address)).equals(reward);
expect(await ethers.provider.getBalance(protocol.yieldDistributor.address)).equals(0);
const initialBalanceTreasury = await ethers.provider.getBalance(await protocol.directory.getTreasuryAddress());
const expectedTreasuryPortion = 0
const expectedOperatorPortion = 0
const expectedCommunityPortion = 0
await protocol.operatorDistributor.connect(signers.protocolSigner).onEthBeaconRewardsReceived(reward, avgTreasuryFee, avgOperatorsFee)
const finalBalanceTreasury = await ethers.provider.getBalance(await protocol.directory.getTreasuryAddress());
expect(finalBalanceTreasury.sub(initialBalanceTreasury)).equals(expectedTreasuryPortion);
expect(await ethers.provider.getBalance(protocol.yieldDistributor.address)).equals(expectedOperatorPortion);
expect(await ethers.provider.getBalance(protocol.operatorDistributor.address)).equals(expectedCommunityPortion);
})
})
describe("When rewards are negative", async () => {
it("should revert", async () => {
const { protocol, signers, rocketPool } = await loadFixture(protocolFixture);
const reward = ethers.utils.parseEther("1").mul(-1);
const avgTreasuryFee = ethers.utils.parseEther(".6") // 50%
const avgOperatorsFee = ethers.utils.parseEther(".4") // 50%
expect(await ethers.provider.getBalance(protocol.operatorDistributor.address)).equals(0);
// simulate reward sent to asset router
await signers.ethWhale.sendTransaction({
to: protocol.operatorDistributor.address,
value: reward.mul(-1)
})
await expect(protocol.operatorDistributor.connect(signers.protocolSigner).onEthBeaconRewardsReceived(reward, avgTreasuryFee, avgOperatorsFee)).to.be.rejected;
})
})
})
})