@nodeset/contracts
Version:
Protocol for accessing NodeSet's Constellation Ethereum staking network
267 lines (208 loc) • 14.8 kB
text/typescript
import { expect } from "chai";
import { ethers } from "hardhat";
import { loadFixture } from "@nomicfoundation/hardhat-network-helpers";
import { protocolFixture, SetupData } from "./test";
import { BigNumber } from "ethers";
import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"
import { assertAddOperator, assertMultipleTransfers } from "./utils/utils";
import { deposit } from "./rocketpool/deposit/scenario-deposit";
describe("xRPL", function () {
it("success - test initial values", async () => {
const setupData = await loadFixture(protocolFixture);
const { protocol, signers, rocketPool } = setupData;
const name = await protocol.vCRPL.name()
const symbol = await protocol.vCRPL.symbol();
expect(name).equals("Constellation RPL");
expect(symbol).equals("xRPL");
expect(await protocol.vCRPL.liquidityReservePercent()).equals(ethers.utils.parseUnits("0.02", 18))
expect(await protocol.vCRPL.minWethRplRatio()).equals(ethers.utils.parseUnits("0", 18))
expect(await protocol.vCRPL.treasuryFee()).equals(ethers.utils.parseUnits("0.01", 18))
})
it("fail - tries to deposit as 'bad actor' involved in AML or other flavors of bad", async () => {
const setupData = await loadFixture(protocolFixture);
const { protocol, signers, rocketPool } = setupData;
await protocol.sanctions.addBlacklist(signers.random.address);
await protocol.directory.connect(signers.admin).enableSanctions();
expect(await protocol.sanctions.isSanctioned(signers.random.address)).equals(true);
await rocketPool.rplContract.connect(signers.rplWhale).transfer(signers.random.address, ethers.utils.parseEther("100"));
await assertAddOperator(setupData, signers.random);
await rocketPool.rplContract.connect(signers.random).approve(protocol.vCRPL.address, ethers.utils.parseEther("100"));
const tx = await protocol.vCRPL.connect(signers.random).deposit(ethers.utils.parseEther("100"), signers.random.address);
const receipt = await tx.wait();
const { events } = receipt;
if (events) {
for (let i = 0; i < events.length; i++) {
expect(events[i].event).not.equals(null)
if (events[i].event?.includes("SanctionViolation")) {
expect(events[i].event?.includes("SanctionViolation")).equals(true)
}
}
}
const expectedRplInDP = ethers.utils.parseEther("0");
const actualRplInDP = await rocketPool.rplContract.balanceOf(protocol.operatorDistributor.address);
expect(expectedRplInDP).equals(actualRplInDP)
})
it("success - tries to deposit as 'good actor' not involved in AML or bad activities", async () => {
const setupData = await loadFixture(protocolFixture);
const { protocol, signers, rocketPool } = setupData;
await rocketPool.rplContract.connect(signers.rplWhale).transfer(signers.random.address, ethers.utils.parseEther("100"));
await assertAddOperator(setupData, signers.random);
await rocketPool.rplContract.connect(signers.random).approve(protocol.vCRPL.address, ethers.utils.parseEther("100"));
await protocol.vCRPL.connect(signers.random).deposit(ethers.utils.parseEther("100"), signers.random.address);
const expectedRplInSystem = ethers.utils.parseEther("100");
const actualRplInSystem = await protocol.vCRPL.totalAssets();
expect(expectedRplInSystem).equals(actualRplInSystem)
})
it("success - tries to deposit 100, then 1000000 rpl, then withdraws 100 rpl immediately", async () => {
const setupData = await loadFixture(protocolFixture);
const { protocol, signers, rocketPool } = setupData;
const deposit = ethers.utils.parseEther("1000000");
await protocol.vCRPL.connect(signers.admin).setMinWethRplRatio(ethers.BigNumber.from(0));
await protocol.vCWETH.connect(signers.admin).setMaxWethRplRatio(ethers.constants.MaxUint256);
await rocketPool.rplContract.connect(signers.rplWhale).transfer(signers.random.address, deposit);
await assertAddOperator(setupData, signers.random);
await rocketPool.rplContract.connect(signers.rplWhale).transfer(signers.random2.address, ethers.utils.parseEther("100"));
await assertAddOperator(setupData, signers.random2);
await rocketPool.rplContract.connect(signers.random).approve(protocol.vCRPL.address, deposit);
await protocol.vCRPL.connect(signers.random).deposit(deposit, signers.random.address);
const expectedRplInSystem = deposit;
const actualRplInSystem = await protocol.vCRPL.totalAssets();
expect(expectedRplInSystem).equals(actualRplInSystem)
await rocketPool.rplContract.connect(signers.random2).approve(protocol.vCRPL.address, ethers.utils.parseEther("100"));
console.log(await rocketPool.rplContract.balanceOf(protocol.vCRPL.address));
await protocol.vCRPL.connect(signers.random2).deposit(ethers.utils.parseEther("100"), signers.random2.address);
const tx = await protocol.vCRPL.connect(signers.random2).redeem(ethers.utils.parseEther("100"), signers.random2.address, signers.random2.address);
await assertMultipleTransfers(tx, [
{
from: protocol.vCRPL.address, to: signers.random2.address, value: ethers.utils.parseEther("100")
},
])
})
it("success - tries to deposit and redeem from rpl vault multiple times", async () => {
const setupData = await loadFixture(protocolFixture);
const { protocol, signers, rocketPool } = setupData;
const depositAmount = ethers.utils.parseEther("1000");
const expectedReserveInVault = ethers.utils.parseEther("20");
const surplusSentToOD = ethers.utils.parseEther("980");
await rocketPool.rplContract.connect(signers.rplWhale).transfer(signers.random.address, depositAmount)
await rocketPool.rplContract.connect(signers.random).approve(protocol.vCRPL.address, depositAmount);
await protocol.vCRPL.connect(signers.random).deposit(depositAmount, signers.random.address);
expect(await protocol.vCRPL.totalAssets()).equals(depositAmount)
expect(await rocketPool.rplContract.balanceOf(protocol.vCRPL.address)).equals(expectedReserveInVault);
expect(await rocketPool.rplContract.balanceOf(protocol.operatorDistributor.address)).equals(surplusSentToOD);
const shareValue = await protocol.vCRPL.convertToAssets(ethers.utils.parseEther("1"))
const expectedRedeemValue = await protocol.vCRPL.previewRedeem(shareValue);
let preBalance = await rocketPool.rplContract.balanceOf(signers.random.address);
await protocol.vCRPL.connect(signers.random).redeem(shareValue, signers.random.address, signers.random.address);
let postBalance = await rocketPool.rplContract.balanceOf(signers.random.address);
expect(expectedRedeemValue).equals(postBalance.sub(preBalance));
expect(await rocketPool.rplContract.balanceOf(protocol.vCRPL.address)).equals(
(await protocol.vCRPL.totalAssets())
.mul(await protocol.vCRPL.liquidityReservePercent())
.div(ethers.utils.parseEther("1"))
);
preBalance = await rocketPool.rplContract.balanceOf(signers.random.address);
await protocol.vCRPL.connect(signers.random).redeem(shareValue, signers.random.address, signers.random.address);
postBalance = await rocketPool.rplContract.balanceOf(signers.random.address);
expect(expectedRedeemValue).equals(postBalance.sub(preBalance));
expect(await rocketPool.rplContract.balanceOf(protocol.vCRPL.address)).equals(
(await protocol.vCRPL.totalAssets())
.mul(await protocol.vCRPL.liquidityReservePercent())
.div(ethers.utils.parseEther("1"))
);
preBalance = await rocketPool.rplContract.balanceOf(signers.random.address);
await protocol.vCRPL.connect(signers.random).redeem(shareValue, signers.random.address, signers.random.address);
postBalance = await rocketPool.rplContract.balanceOf(signers.random.address);
expect(expectedRedeemValue).equals(postBalance.sub(preBalance));
expect(await rocketPool.rplContract.balanceOf(protocol.vCRPL.address)).equals(
(await protocol.vCRPL.totalAssets())
.mul(await protocol.vCRPL.liquidityReservePercent())
.div(ethers.utils.parseEther("1"))
);
})
it("success - tries to deposit and redeem from rpl vault multiple times after getting merkle rewards", async () => {
const setupData = await loadFixture(protocolFixture);
const { protocol, signers, rocketPool } = setupData;
const depositAmount = ethers.utils.parseEther("100000");
await rocketPool.rplContract.connect(signers.rplWhale).transfer(signers.random.address, depositAmount)
await rocketPool.rplContract.connect(signers.random).approve(protocol.vCRPL.address, depositAmount);
await protocol.vCRPL.connect(signers.random).deposit(depositAmount, signers.random.address);
// handle merkle claims and process reward assertions
const rocketMDM = await ethers.getContractAt("RocketMerkleDistributorMainnet", await protocol.directory.getRocketMerkleDistributorMainnetAddress());
await rocketMDM.useMock();
const rocketVault = await ethers.getContractAt("RocketVault", await rocketPool.rockStorageContract.getAddress(await rocketMDM.rocketVaultKey()));
rocketVault.useMock();
const rplReward = ethers.utils.parseEther("100");
const ethReward = ethers.utils.parseEther("100");
rocketPool.rplContract.connect(signers.rplWhale).transfer(rocketVault.address, rplReward);
await signers.ethWhale.sendTransaction({
to: rocketVault.address,
value: ethReward
})
const rewardIndex = [0];
const amountRpl = [rplReward];
const amountEth = [ethReward];
const proof = [[ethers.utils.hexZeroPad("0x0", 32)], [ethers.utils.hexZeroPad("0x0", 32)]]
const rplTreasuryFee = await setupData.protocol.vCRPL.treasuryFee();
const expectedTreasuryPortion = rplReward.mul(rplTreasuryFee).div(ethers.utils.parseEther("1"));
const expectedCommunityPortion = rplReward.sub(expectedTreasuryPortion)
expect(await protocol.vCRPL.totalAssets()).equals(depositAmount);
//disable auto-mine of new blocks
await ethers.provider.send("evm_setAutomine", [false]);
await protocol.merkleClaimStreamer.submitMerkleClaim(rewardIndex, amountRpl, amountEth, proof);
expect(await protocol.vCRPL.totalAssets()).equals(depositAmount); // no time has passed, so no tvl has been streamed
const streamingInterval = await protocol.merkleClaimStreamer.streamingInterval()
// mine a block with above transactions
let timestamp = (await ethers.provider.getBlock("latest")).timestamp;
await ethers.provider.send("evm_mine", [timestamp+1]);
timestamp = timestamp+1;
expect(streamingInterval).equals(2419200); // 28 days in seconds
const expectedReserveInVault = depositAmount.mul(await setupData.protocol.vCRPL.liquidityReservePercent()).div(ethers.utils.parseEther("1"));
const surplusSentToOD = depositAmount.sub(expectedReserveInVault);
expect(await rocketPool.rplContract.balanceOf(protocol.vCRPL.address)).equals(expectedReserveInVault);
expect(await rocketPool.rplContract.balanceOf(protocol.operatorDistributor.address)).equals(surplusSentToOD);
// increase time by 1 day since claim
await ethers.provider.send("evm_mine", [timestamp+86400]); // 1 day in seconds
let lastClaimTime = await protocol.merkleClaimStreamer.lastClaimTime();
console.log("lastClaimTime", lastClaimTime);
expect(lastClaimTime).equals((await ethers.provider.getBlock('latest')).timestamp - 86400);
let expectedStreamedTVL = expectedCommunityPortion.div(28);
let expectedTotalAssets = depositAmount.add(expectedStreamedTVL);
expect(await protocol.merkleClaimStreamer.priorRplStreamAmount()).equals(expectedCommunityPortion);
expect(await protocol.merkleClaimStreamer.getStreamedTvlRpl()).equals(expectedStreamedTVL);
expect(await protocol.vCRPL.totalAssets()).equals(expectedTotalAssets);
// increase time to 3 days since claim
await ethers.provider.send("evm_mine", [timestamp+259200]); // 3 days in seconds
expect(await protocol.merkleClaimStreamer.lastClaimTime()).equals((await ethers.provider.getBlock('latest')).timestamp - 259200);
expectedStreamedTVL = expectedCommunityPortion.mul(3).div(28); // we should be 3/28ths streamed at this point
expectedTotalAssets = depositAmount.add(expectedStreamedTVL);
expect(await protocol.merkleClaimStreamer.priorRplStreamAmount()).equals(expectedCommunityPortion);
expect(await protocol.merkleClaimStreamer.getStreamedTvlRpl()).equals(expectedStreamedTVL);
expect(await protocol.vCRPL.totalAssets()).equals(expectedTotalAssets);
// increase time by 25 days, total is 28 days since claim
await ethers.provider.send("evm_mine", [timestamp+2419200]); // 28 days in seconds
expect(await protocol.merkleClaimStreamer.lastClaimTime()).equals((await ethers.provider.getBlock('latest')).timestamp - 2419200);
expectedStreamedTVL = expectedCommunityPortion; // we should be fully streamed at this point
expectedTotalAssets = depositAmount.add(expectedStreamedTVL);
expect(await protocol.merkleClaimStreamer.priorRplStreamAmount()).equals(expectedCommunityPortion);
expect(await protocol.merkleClaimStreamer.getStreamedTvlRpl()).equals(expectedStreamedTVL);
expect(await protocol.vCRPL.totalAssets()).equals(expectedTotalAssets);
// re-enable automine
await ethers.provider.send("evm_setAutomine", [true]);
const shareValue = await protocol.vCRPL.convertToAssets(ethers.utils.parseEther("1"))
const expectedRedeemValue = await protocol.vCRPL.previewRedeem(shareValue);
let preBalance = await rocketPool.rplContract.balanceOf(signers.random.address);
await protocol.vCRPL.connect(signers.random).redeem(shareValue, signers.random.address, signers.random.address);
let postBalance = await rocketPool.rplContract.balanceOf(signers.random.address);
expect(expectedRedeemValue).equals(postBalance.sub(preBalance));
preBalance = await rocketPool.rplContract.balanceOf(signers.random.address);
await protocol.vCRPL.connect(signers.random).redeem(shareValue, signers.random.address, signers.random.address);
postBalance = await rocketPool.rplContract.balanceOf(signers.random.address);
expect(expectedRedeemValue).equals(postBalance.sub(preBalance));
preBalance = await rocketPool.rplContract.balanceOf(signers.random.address);
await protocol.vCRPL.connect(signers.random).redeem(shareValue, signers.random.address, signers.random.address);
postBalance = await rocketPool.rplContract.balanceOf(signers.random.address);
expect(expectedRedeemValue).equals(postBalance.sub(preBalance));
expect(await rocketPool.rplContract.balanceOf(await protocol.directory.getTreasuryAddress())).equals(expectedTreasuryPortion);
})
});