UNPKG

@nodeset/contracts

Version:

Protocol for accessing NodeSet's Constellation Ethereum staking network

238 lines (200 loc) 12.9 kB
import { assert, expect } from "chai"; import { ethers } from "hardhat"; import { loadFixture } from "@nomicfoundation/hardhat-network-helpers"; import { Protocol, protocolFixture, RocketPool, SetupData } from "./test"; import { BigNumber } from "ethers"; import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers" import { expectNumberE18ToBeApproximately, getEventNames, prepareOperatorDistributionContract, registerNewValidator, upgradePriceFetcherToMock, whitelistUserServerSig } from "./utils/utils"; import { ContractTransaction } from "@ethersproject/contracts"; import { wEth } from "../typechain-types/factories/contracts/Testing"; import { MerkleClaimStreamer, RocketMerkleDistributorMainnet, RocketVault } from "../typechain-types"; describe("Merkle Claim Streamer", async () => { let setupData: SetupData; let protocol: Protocol; let admin: SignerWithAddress; let nonAdmin: SignerWithAddress; let mcs: MerkleClaimStreamer; let rocketpool: RocketPool; let rocketVault: RocketVault; let rocketMDM: RocketMerkleDistributorMainnet; beforeEach(async () => { setupData = await loadFixture(protocolFixture); protocol = setupData.protocol; admin = setupData.signers.admin; nonAdmin = setupData.signers.random; mcs = protocol.merkleClaimStreamer; rocketpool = setupData.rocketPool; rocketMDM = await ethers.getContractAt("RocketMerkleDistributorMainnet", await protocol.directory.getRocketMerkleDistributorMainnetAddress()) await rocketMDM.useMock(); rocketVault = await ethers.getContractAt("RocketVault", await rocketpool.rockStorageContract.getAddress(await rocketMDM.rocketVaultKey())); await rocketVault.useMock(); }) describe("streaming interval is changed", async () => { it("can be changed by admin to something above zero and up to 365 days", async () => { await expect(mcs.connect(admin).setStreamingInterval(69)).to.not.be.reverted; expect(await mcs.streamingInterval()).equals(69) await expect(mcs.connect(admin).setStreamingInterval(31536000)).to.not.be.reverted; expect(await mcs.streamingInterval()).equals(31536000) await expect(mcs.connect(admin).setStreamingInterval(1)).to.not.be.reverted; expect(await mcs.streamingInterval()).equals(1) }) it("cannot be set out of bounds", async () => { await expect(mcs.connect(admin).setStreamingInterval(0)).to.be.revertedWith("New streaming interval must be > 0 seconds and <= 365 days"); expect(await mcs.streamingInterval()).not.equals(0) await expect(mcs.connect(admin).setStreamingInterval(31536001)).to.be.revertedWith("New streaming interval must be > 0 seconds and <= 365 days"); expect(await mcs.streamingInterval()).not.equals(31536001) }) it("cannot be changed by nonAdmin", async () => { await expect(mcs.connect(nonAdmin).setStreamingInterval(69)).to.be.revertedWith("Can only be called by admin address!"); expect(await mcs.streamingInterval()).not.equals(69) await expect(mcs.connect(nonAdmin).setStreamingInterval(31536000)).to.be.revertedWith("Can only be called by admin address!"); expect(await mcs.streamingInterval()).not.equals(31536000) await expect(mcs.connect(nonAdmin).setStreamingInterval(1)).to.be.revertedWith("Can only be called by admin address!"); expect(await mcs.streamingInterval()).not.equals(1) }) it.skip("can submit merkle claim after changing streaming interval time", async () => { await assertMerkleClaim(rocketVault, setupData, ethers.utils.parseEther("1"),ethers.utils.parseEther("1"), 69); await expect(mcs.connect(admin).setStreamingInterval(6969)).to.not.be.reverted; expect(await mcs.streamingInterval()).equals(6969) await assertMerkleClaim(rocketVault, setupData, ethers.utils.parseEther("1"),ethers.utils.parseEther("1"), 69); }) }) describe("when claims are disabled", async () => { beforeEach(async () => { await expect(await mcs.connect(admin).setMerkleClaimsEnabled(false)).to.not.be.reverted; expect(await mcs.merkleClaimsEnabled()).equals(false); }) it("cannot submit claim", async () => { const rewardIndex = [0]; const amountRpl = [ethers.utils.parseEther("100")]; const amountEth = [ethers.utils.parseEther("100")]; const proof = [[ethers.utils.hexZeroPad("0x0", 32)], [ethers.utils.hexZeroPad("0x0", 32)]] expect(mcs.submitMerkleClaim([rewardIndex], amountRpl, amountEth, proof)).to.be.revertedWith("Merkle claims are disabled"); }); it("only admin can re-enable", async () => { await expect(mcs.connect(nonAdmin).setMerkleClaimsEnabled(true)).to.be.revertedWith('Can only be called by admin address!'); }) it("can submit claim if re-enabled", async () => { await mcs.connect(admin).setMerkleClaimsEnabled(true); expect(await mcs.merkleClaimsEnabled()).equals(true); await assertMerkleClaim(rocketVault, setupData, ethers.utils.parseEther("1"), ethers.utils.parseEther("1"), 1); }); }) describe("when claims are enabled", async () => { beforeEach(async () => { expect(await mcs.merkleClaimsEnabled()).equals(true); expect(await protocol.vCRPL.totalAssets()).equals(0); expect(await protocol.vCWETH.totalAssets()).equals(0); }) it("only admin can disable", async () => { await expect(mcs.connect(nonAdmin).setMerkleClaimsEnabled(false)).to.be.revertedWith('Can only be called by admin address!'); await expect(mcs.connect(admin).setMerkleClaimsEnabled(false)).to.not.be.reverted; expect(await mcs.merkleClaimsEnabled()).equals(false); }) describe("when eth and rpl amount are both 0", async () => { it("cannot submit claim", async () => { rocketMDM.disableMock(); const proof = [[ethers.utils.hexZeroPad("0x0", 32)], [ethers.utils.hexZeroPad("0x0", 32)]] await expect(protocol.merkleClaimStreamer.submitMerkleClaim([0], [0], [0], proof)).to.be.revertedWith("Invalid amount") }) }) describe("when either eth or rpl amount is above 0", async () => { type ParamsType = { ethRewards: number; rplRewards: number; waitTime: number}; [ { ethRewards: 28, rplRewards: 28, waitTime: 86400}, { ethRewards: .6, rplRewards: .4, waitTime: 1}, { ethRewards: .1, rplRewards: .1, waitTime: 86400}, // 1 day in seconds { ethRewards: .5, rplRewards: .4, waitTime: 31556952000}, // 1000 years { ethRewards: .99, rplRewards: .01, waitTime: 2419200}, // 28 days in seconds { ethRewards: .99999, rplRewards: .00001, waitTime: 259200 }, // 3 days in seconds { ethRewards: 1, rplRewards: 0, waitTime: 5 }, { ethRewards: 0, rplRewards: 1, waitTime: 69 }, { ethRewards: 69, rplRewards: 1, waitTime: 0 }, { ethRewards: 0, rplRewards: 69, waitTime: 0 }, ].forEach((params: ParamsType) => { let rplRewards = ethers.utils.parseEther(`${params.rplRewards}`) let ethRewards = ethers.utils.parseEther(`${params.ethRewards}`) it(`can submit claim with ethRewards=${ethRewards}, rplRewards=${rplRewards}, waitTime=${params.waitTime}`, async () => { await assertMerkleClaim(rocketVault, setupData, ethRewards, rplRewards, params.waitTime); }); it.skip(`can submit multiple claims with ethRewards=${ethRewards}, rplRewards=${rplRewards}, waitTime=${params.waitTime}`, async () => { await assertMerkleClaim(rocketVault, setupData, ethRewards, rplRewards, params.waitTime); await assertMerkleClaim(rocketVault, setupData, ethRewards, rplRewards, params.waitTime); await assertMerkleClaim(rocketVault, setupData, ethRewards, rplRewards, params.waitTime); }) it.skip(`can submit multiple claims in the same block with ethRewards=${ethRewards}, rplRewards=${rplRewards}, waitTime=${params.waitTime}`, async () => { await assertMerkleClaim(rocketVault, setupData, ethRewards, rplRewards, 0); await assertMerkleClaim(rocketVault, setupData, ethRewards, rplRewards, 0); await assertMerkleClaim(rocketVault, setupData, ethRewards, rplRewards, 0); }) it.skip(`can submit multiple claims with different wait times and ethRewards=${ethRewards}, rplRewards=${rplRewards}}`, async () => { await assertMerkleClaim(rocketVault, setupData, ethRewards, rplRewards, 69); await assertMerkleClaim(rocketVault, setupData, ethRewards, rplRewards, 2419200); await assertMerkleClaim(rocketVault, setupData, ethRewards, rplRewards, 31556952000); }) }) }) }) }); async function assertMerkleClaim(rocketVault: RocketVault, setupData: SetupData, ethRewards: BigNumber, rplRewards: BigNumber, waitTime: number ) { try { let protocol = setupData.protocol; let rocketpool = setupData.rocketPool; let mcs = protocol.merkleClaimStreamer; // send rewards to merkle distributor await rocketpool.rplContract.connect(setupData.signers.rplWhale).transfer(rocketVault.address, rplRewards); await setupData.signers.ethWhale.sendTransaction({ to: rocketVault.address, value: ethRewards }) const rewardIndex = [0]; const proof = [[ethers.utils.hexZeroPad("0x0", 32)], [ethers.utils.hexZeroPad("0x0", 32)]] let originalTotalAssetsEth = await protocol.vCWETH.totalAssets(); let originalTotalAssetsRpl = await protocol.vCRPL.totalAssets(); const streamingInterval = await mcs.streamingInterval() const expectedTreasuryPortionEth = await protocol.vCWETH.getTreasuryPortion(ethRewards); const expectedTreasuryPortionRpl = await protocol.vCRPL.getTreasuryPortion(rplRewards); const expectedOperatorPortionEth = await protocol.vCWETH.getOperatorPortion(ethRewards); const expectedCommunityPortionEth = ethRewards.sub(expectedTreasuryPortionEth).sub(expectedOperatorPortionEth); const expectedCommunityPortionRpl = rplRewards.sub(expectedTreasuryPortionRpl); //disable auto-mine of new blocks await ethers.provider.send("evm_setAutomine", [false]); let claimTimestamp = (await ethers.provider.getBlock("latest")).timestamp+1 let claimTx = mcs.submitMerkleClaim(rewardIndex, [rplRewards], [ethRewards], proof) // no time has passed, so no tvl has been streamed expect(await protocol.vCRPL.totalAssets()).equals(originalTotalAssetsRpl); expect(await protocol.vCWETH.totalAssets()).equals(originalTotalAssetsEth); // mine a block with the merkle claim transaction above await ethers.provider.send("evm_mine", [claimTimestamp]); await expect(claimTx).to.not.be.revertedWith(""); const tx = await ((await claimTx).wait()) expect((await ethers.provider.getBlock(tx.blockHash)).timestamp).equals(claimTimestamp) expect((await ethers.provider.getBlock("latest")).timestamp).equals(claimTimestamp); expect(await mcs.queryFilter(mcs.filters.MerkleClaimSubmitted(claimTimestamp))).not.equals([]); // todo: these don't work //expect(claimTx).to.emit(mcs, "ASDF"); // expect(claimTx).to.emit(mcs, "MerkleClaimSubmitted") // .withArgs(claimTimestamp, ethRewards, rplRewards, expectedTreasuryPortionEth, expectedOperatorPortionEth, expectedTreasuryPortionRpl) console.log("claimTimestamp", claimTimestamp) console.log("lastclaimtime()", await mcs.lastClaimTime()) expect(await mcs.lastClaimTime()).equals(claimTimestamp); expect(await rocketpool.rplContract.balanceOf(protocol.vCRPL.address)).equals(0); expect(await rocketpool.rplContract.balanceOf(protocol.operatorDistributor.address)).equals(0); if(waitTime > 0) { // mine another block to wait some time await ethers.provider.send("evm_mine", [waitTime + claimTimestamp]); } let expectedStreamedTVLEth = streamingInterval.gt(waitTime) ? expectedCommunityPortionEth.mul(waitTime).div(streamingInterval) : expectedCommunityPortionEth; let expectedStreamedTVLRpl = streamingInterval.gt(waitTime) ? expectedCommunityPortionRpl.mul(waitTime).div(streamingInterval) : expectedCommunityPortionRpl; let expectedTotalAssetsEth = originalTotalAssetsEth.add(expectedStreamedTVLEth); let expectedTotalAssetsRpl = originalTotalAssetsRpl.add(expectedStreamedTVLRpl); expect(await mcs.priorEthStreamAmount()).equals(expectedCommunityPortionEth); expect(await mcs.priorRplStreamAmount()).equals(expectedCommunityPortionRpl); expect(await mcs.getStreamedTvlEth()).equals(expectedStreamedTVLEth); expect(await mcs.getStreamedTvlRpl()).equals(expectedStreamedTVLRpl); expect(await protocol.vCWETH.totalAssets()).equals(expectedTotalAssetsEth); expect(await protocol.vCRPL.totalAssets()).equals(expectedTotalAssetsRpl); } // re-enable automine finally { await ethers.provider.send("evm_setAutomine", [true]); } }