Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[VEN-]: Fork test on zksync (testnet + mainnet) #434

Open
wants to merge 11 commits into
base: develop
Choose a base branch
from
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ cache
cache-zk
artifacts
artifacts-zk
.log

#Compound
allFiredEvents
Expand Down
3 changes: 3 additions & 0 deletions hardhat.config.zksync.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,9 @@ const config: HardhatUserConfig = {
"hardhat-deploy/solc_0.8/openzeppelin/proxy/transparent/ProxyAdmin.sol",
],
},
mocha: {
timeout: 200000000,
},
};

// Added a subtask to exclude some solidity files from compilation due to limitation in zksync compiler, https://docs.zksync.io/zk-stack/components/compiler/toolchain/solidity#limitations
Expand Down
8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,10 @@
"@types/node": "^12.20.50",
"@typescript-eslint/eslint-plugin": "^5.27.1",
"@typescript-eslint/parser": "^5.27.1",
"@venusprotocol/governance-contracts": "2.3.0",
"@venusprotocol/oracle": "2.5.1",
"@venusprotocol/protocol-reserve": "2.3.0",
"@venusprotocol/venus-protocol": "9.2.0",
"@venusprotocol/governance-contracts": "2.4.0-dev.1",
"@venusprotocol/oracle": "2.6.0-dev.3",
"@venusprotocol/protocol-reserve": "2.4.0-dev.1",
"@venusprotocol/venus-protocol": "9.3.0-dev.5",
"bignumber.js": "9.0.0",
"chai": "^4.3.6",
"dotenv": "^10.0.0",
Expand Down
145 changes: 107 additions & 38 deletions tests/hardhat/Fork/NativeTokenGateway.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,67 @@
import { mine } from "@nomicfoundation/hardhat-network-helpers";
import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";
import { expect } from "chai";
import { Signer } from "ethers";
import { parseUnits } from "ethers/lib/utils";
import { ethers } from "hardhat";
import { ethers, network } from "hardhat";

import { Comptroller, ERC20, NativeTokenGateway, VToken } from "../../../typechain";
import { initMainnetUser, setForkBlock } from "./utils";
import { getContractAddresses, initMainnetUser, mineOnZksync, setForkBlock } from "./utils";

const ADMIN = "0x285960C5B22fD66A736C7136967A3eB15e93CC67";
const COMPTROLLER_ADDRESS = "0x687a01ecF6d3907658f7A7c714749fAC32336D1B";
const VWETH = "0x7c8ff7d2A1372433726f879BD945fFb250B94c65";
const USDT = "0xdAC17F958D2ee523a2206206994597C13D831ec7";
const VUSDT = "0x8C3e3821259B82fFb32B2450A95d2dcbf161C24E";
const FORK = process.env.FORK === "true";
const FORKED_NETWORK = process.env.FORKED_NETWORK;

let VWETH: string;
let USDT: string;
let VUSDT: string;
let NTG: string;
let USER1: string;
let USER2: string;
let COMPTROLLER: string;
let ADMIN: string;
let BLOCK_NUMBER: number;

if (FORKED_NETWORK === "zksyncsepolia") {
VWETH = "0x31eb7305f9fE281027028D0ba0d7f57ddA836d49";
USDT = "0x9Bf62C9C6AaB7AB8e01271f0d7A401306579709B";
VUSDT = "0x7Bfd185eF8380a72027bF65bFEEAb0242b147778";
NTG = "0xC2bc5881f2c1ee08a1f0fee65Fbf2BB4C4DD81e9";
USER1 = "0xEF4B807f9442b0EbD8a051C2cAEA81e5e7BAcFBD";
USER2 = "0xE8C6Cf867CF962d289305ECE9b139a4116674541";
COMPTROLLER = "0xC527DE08E43aeFD759F7c0e6aE85433923064669";
ADMIN = getContractAddresses(FORKED_NETWORK as string).ADMIN;
BLOCK_NUMBER = getContractAddresses(FORKED_NETWORK as string).BLOCK_NUMBER;
}

if (FORKED_NETWORK === "zksyncmainnet") {
VWETH = "0x1Fa916C27c7C2c4602124A14C77Dbb40a5FF1BE8";
USDT = "0x493257fd37edb34451f62edf8d2a0c418852ba4c";
VUSDT = "0x69cDA960E3b20DFD480866fFfd377Ebe40bd0A46";
NTG = "0xeEDE4e1BDaC489BD851970bE3952B729C4238A68";
USER1 = "0xE0B015E54d54fc84a6cB9B666099c46adE9335FF";
USER2 = "0x4bBa932E9792A2b917D47830C93a9BC79320E4f7";
COMPTROLLER = "0xddE4D098D9995B659724ae6d5E3FB9681Ac941B1";
ADMIN = getContractAddresses(FORKED_NETWORK as string).ADMIN;
BLOCK_NUMBER = getContractAddresses(FORKED_NETWORK as string).BLOCK_NUMBER;
}

const USER1 = "0xf89d7b9c864f589bbF53a82105107622B35EaA40";
const USER2 = "0x974CaA59e49682CdA0AD2bbe82983419A2ECC400";
const BLOCK_NUMBER = 19781700;
if (FORKED_NETWORK === "ethereum") {
VWETH = "0x7c8ff7d2A1372433726f879BD945fFb250B94c65";
USDT = "0xdAC17F958D2ee523a2206206994597C13D831ec7";
VUSDT = "0x8C3e3821259B82fFb32B2450A95d2dcbf161C24E";
USER1 = "0xf89d7b9c864f589bbF53a82105107622B35EaA40";
USER2 = "0x974CaA59e49682CdA0AD2bbe82983419A2ECC400";
COMPTROLLER = "0x687a01ecF6d3907658f7A7c714749fAC32336D1B";
ADMIN = getContractAddresses(FORKED_NETWORK as string).ADMIN;
BLOCK_NUMBER = getContractAddresses(FORKED_NETWORK as string).BLOCK_NUMBER;
}

async function configureTimeLock() {
impersonatedTimeLock = await initMainnetUser(ADMIN, ethers.utils.parseUnits("2"));
}

const FORK = process.env.FORK === "true";
const FORKED_NETWORK = process.env.FORKED_NETWORK;

let user1: Signer;
let user2: Signer;
let user1: SignerWithAddress;
let user2: SignerWithAddress;
let impersonatedTimeLock: Signer;
let comptroller: Comptroller;
let vweth: VToken;
Expand All @@ -40,21 +77,20 @@ async function setup() {

usdt = await ethers.getContractAt("@openzeppelin/contracts/token/ERC20/ERC20.sol:ERC20", USDT);

const comptroller = await ethers.getContractAt("Comptroller", COMPTROLLER_ADDRESS);

const comptroller = await ethers.getContractAt("Comptroller", COMPTROLLER);
vusdt = await ethers.getContractAt("VToken", VUSDT);
vweth = await ethers.getContractAt("VToken", VWETH);

await comptroller
.connect(impersonatedTimeLock)
.setMarketSupplyCaps([VUSDT, VWETH], [parseUnits("10000", 18), parseUnits("10000", 18)]);

await comptroller.connect(user1).enterMarkets([vusdt.address, vweth.address]);
await comptroller.connect(user2).enterMarkets([vusdt.address, vweth.address]);

const nativeTokenGatewayFactory = await ethers.getContractFactory("NativeTokenGateway");
const nativeTokenGateway = await nativeTokenGatewayFactory.deploy(VWETH);

if (FORKED_NETWORK === "zksyncsepolia" || FORKED_NETWORK === "zksyncmainnet") {
nativeTokenGateway = await ethers.getContractAt("NativeTokenGateway", NTG);
} else {
const nativeTokenGatewayFactory = await ethers.getContractFactory("NativeTokenGateway");
nativeTokenGateway = await nativeTokenGatewayFactory.deploy(VWETH);
}
return {
usdt,
comptroller,
Expand All @@ -64,7 +100,10 @@ async function setup() {
};
}

if (FORK && FORKED_NETWORK === "ethereum") {
if (
FORK &&
(FORKED_NETWORK === "ethereum" || FORKED_NETWORK === "zksyncsepolia" || FORKED_NETWORK === "zksyncmainnet")
) {
describe("NativeTokenGateway", async () => {
const supplyAmount = parseUnits("10", 18);
beforeEach("setup", async () => {
Expand All @@ -75,34 +114,54 @@ if (FORK && FORKED_NETWORK === "ethereum") {
describe("wrapAndSupply", () => {
it("should wrap and supply eth", async () => {
const balanceBeforeSupplying = await vweth.balanceOf(await user1.getAddress());
const initialBalance = await user1.getBalance();

const tx = await nativeTokenGateway
.connect(user1)
.wrapAndSupply(await user1.getAddress(), { value: supplyAmount });
const receipt = await tx.wait();
const etherUsed = receipt.gasUsed.mul(receipt.effectiveGasPrice);

const balanceAfterSupplying = await vweth.balanceOf(await user1.getAddress());
await expect(balanceAfterSupplying.sub(balanceBeforeSupplying).toString()).to.closeTo(
expect(balanceAfterSupplying.sub(balanceBeforeSupplying).toString()).to.closeTo(
parseUnits("10", 8),
parseUnits("1", 7),
);
await expect(tx).to.changeEtherBalances([user1], [supplyAmount.mul(-1)]);
expect(await user1.getBalance()).to.be.equal(initialBalance.sub(supplyAmount).sub(etherUsed));
await network.provider.request({ method: "hardhat_reset" });
});
});

describe("redeemUnderlyingAndUnwrap", () => {
beforeEach(async () => {
await nativeTokenGateway.connect(user1).wrapAndSupply(await user1.getAddress(), { value: supplyAmount });

await usdt.connect(user2).approve(VUSDT, parseUnits("2000", 6));
await expect(vusdt.connect(user2).mint(parseUnits("2000", 6))).to.emit(vusdt, "Mint");

await vweth.connect(user2).borrow(ethers.utils.parseEther("0.5"));

if (FORKED_NETWORK == "zksyncsepolia" || FORKED_NETWORK === "zksyncmainnet") {
await mineOnZksync(1000);
} else {
await mine(1000);
}
});

it("should redeem underlying tokens and unwrap and send it to the user", async () => {
const redeemAmount = parseUnits("10", 18);

await comptroller.connect(user1).updateDelegate(nativeTokenGateway.address, true);

const ethBalanceBefore = await user1.getBalance();
await nativeTokenGateway.connect(user1).redeemUnderlyingAndUnwrap(redeemAmount);

const ethBalanceAfter = await user1.getBalance();

await expect(ethBalanceAfter.sub(ethBalanceBefore)).to.closeTo(redeemAmount, parseUnits("1", 16));
expect(ethBalanceAfter.sub(ethBalanceBefore)).to.closeTo(redeemAmount, parseUnits("1", 16));

expect(await vweth.balanceOf(await user1.getAddress())).to.closeTo(0, 10);
expect(await vweth.balanceOf(await user1.getAddress())).to.closeTo(0, 4000);
await network.provider.request({ method: "hardhat_reset" });
});
});

Expand All @@ -116,11 +175,13 @@ if (FORK && FORKED_NETWORK === "ethereum") {
await comptroller.connect(user1).updateDelegate(nativeTokenGateway.address, true);

const ethBalanceBefore = await user1.getBalance();

await nativeTokenGateway.connect(user1).redeemAndUnwrap(redeemTokens);
const ethBalanceAfter = await user1.getBalance();

await expect(ethBalanceAfter.sub(ethBalanceBefore)).to.closeTo(parseUnits("10", 18), parseUnits("1", 16));
expect(ethBalanceAfter.sub(ethBalanceBefore)).to.closeTo(parseUnits("10", 18), parseUnits("1", 16));
expect(await vweth.balanceOf(await user1.getAddress())).to.eq(0);
await network.provider.request({ method: "hardhat_reset" });
});
});

Expand All @@ -131,33 +192,41 @@ if (FORK && FORKED_NETWORK === "ethereum") {

it("should borrow and unwrap weth and send it to borrower", async () => {
await nativeTokenGateway.connect(user1).wrapAndSupply(await user1.getAddress(), { value: supplyAmount });
await usdt.connect(user2).approve(vusdt.address, parseUnits("5000", 6));

await vusdt.connect(user2).mint(parseUnits("5000", 6));
await usdt.connect(user2).approve(vusdt.address, parseUnits("2000", 6));

await vusdt.connect(user2).mint(parseUnits("2000", 6));

await comptroller.connect(user2).updateDelegate(nativeTokenGateway.address, true);

const balanceBefore = await user2.getBalance();
const borrowAmount = parseUnits("2", 6);
const tx = await nativeTokenGateway.connect(user2).borrowAndUnwrap(borrowAmount);
const receipt = await tx.wait();
const etherUsed = receipt.gasUsed.mul(receipt.effectiveGasPrice);

await expect(tx).to.changeEtherBalances([user2], [borrowAmount]);
expect(await user2.getBalance()).to.be.equal(balanceBefore.add(borrowAmount).sub(etherUsed));
await network.provider.request({ method: "hardhat_reset" });
});
});

describe("wrapAndRepay", () => {
it("should wrap and repay", async () => {
const borrowAmount = parseUnits("1", 18);
const repayAmount = parseUnits("10", 18);
await usdt.connect(user2).approve(vusdt.address, parseUnits("5000", 6));
await vusdt.connect(user2).mint(parseUnits("5000", 6));
const borrowAmount = parseUnits("0.5", 18);
const repayAmount = parseUnits("0.5", 18);
await usdt.connect(user2).approve(vusdt.address, parseUnits("2000", 6));
await vusdt.connect(user2).mint(parseUnits("2000", 6));
await vweth.connect(user2).borrow(borrowAmount);

const ethBalanceBefore = await user2.getBalance();
await nativeTokenGateway.connect(user2).wrapAndRepay({ value: repayAmount });
const tx = await nativeTokenGateway.connect(user2).wrapAndRepay({ value: repayAmount });
const receipt = await tx.wait();
const etherUsed = receipt.gasUsed.mul(receipt.effectiveGasPrice);
const ethBalanceAfter = await user2.getBalance();

expect(ethBalanceBefore.sub(ethBalanceAfter)).to.closeTo(borrowAmount, parseUnits("1", 18));
expect(ethBalanceBefore.sub(ethBalanceAfter).sub(etherUsed)).equals(borrowAmount);
expect(await vweth.balanceOf(await user1.getAddress())).to.eq(0);
await network.provider.request({ method: "hardhat_reset" });
});
});
});
Expand Down
40 changes: 33 additions & 7 deletions tests/hardhat/Fork/RewardsForkTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { mine } from "@nomicfoundation/hardhat-network-helpers";
import chai from "chai";
import { BigNumberish, Signer } from "ethers";
import { parseUnits } from "ethers/lib/utils";
import { ethers } from "hardhat";
import { ethers, network } from "hardhat";

import { convertToUnit } from "../../../helpers/utils";
import {
Expand All @@ -19,7 +19,7 @@ import {
VToken,
VToken__factory,
} from "../../../typechain";
import { getContractAddresses, initMainnetUser, setForkBlock } from "./utils";
import { getContractAddresses, initMainnetUser, mineOnZksync, setForkBlock } from "./utils";

const { expect } = chai;
chai.use(smock.matchers);
Expand Down Expand Up @@ -96,7 +96,12 @@ if (FORK) {
await binanceOracle.connect(impersonatedTimelock).setMaxStalePeriod("HAY", 31536000);
}

if (FORKED_NETWORK == "ethereum" || FORKED_NETWORK == "arbitrumsepolia" || FORKED_NETWORK == "arbitrumone") {
if (
FORKED_NETWORK == "ethereum" ||
FORKED_NETWORK == "arbitrumsepolia" ||
FORKED_NETWORK == "arbitrumone" ||
FORKED_NETWORK == "zksyncmainnet"
) {
chainlinkOracle = ChainlinkOracle__factory.connect(CHAINLINK_ORACLE, impersonatedTimelock);
let tuple = await chainlinkOracle.tokenConfigs(TOKEN2);
tuple = {
Expand Down Expand Up @@ -124,7 +129,12 @@ if (FORK) {
await rewardDistributor.connect(comptrollerSigner).updateRewardTokenSupplyIndex(vTokenAddress);

let supplyState = await rewardDistributor.rewardTokenSupplyState(vTokenAddress);
if (FORKED_NETWORK == "arbitrumsepolia" || FORKED_NETWORK == "arbitrumone") {
if (
FORKED_NETWORK == "arbitrumsepolia" ||
FORKED_NETWORK == "arbitrumone" ||
FORKED_NETWORK == "zksyncsepolia" ||
FORKED_NETWORK == "zksyncmainnet"
) {
supplyState = await rewardDistributor.rewardTokenSupplyStateTimeBased(vTokenAddress);
}
const supplyIndex = supplyState.index;
Expand Down Expand Up @@ -157,7 +167,12 @@ if (FORK) {
.updateRewardTokenBorrowIndex(vTokenAddress, { mantissa: marketBorrowIndex });

let borrowState = await rewardDistributor.rewardTokenBorrowState(vTokenAddress);
if (FORKED_NETWORK == "arbitrumsepolia" || FORKED_NETWORK == "arbitrumone") {
if (
FORKED_NETWORK == "arbitrumsepolia" ||
FORKED_NETWORK == "arbitrumone" ||
FORKED_NETWORK == "zksyncsepolia" ||
FORKED_NETWORK == "zksyncmainnet"
) {
borrowState = await rewardDistributor.rewardTokenBorrowStateTimeBased(vTokenAddress);
}
const borrowIndex = borrowState.index;
Expand Down Expand Up @@ -185,7 +200,11 @@ if (FORK) {

it("Rewards for suppliers", async function () {
await mintVTokens(acc1Signer, token2, vTOKEN2, mintAmount);
await mine(3000000);
if (FORKED_NETWORK == "zksyncsepolia" || FORKED_NETWORK == "zksyncmainnet") {
await mineOnZksync(3000000);
} else {
await mine(3000000);
}
await vTOKEN2.accrueInterest();

// Reward1 calculations for user 1
Expand All @@ -207,12 +226,18 @@ if (FORK) {
supplierAccruedExpected = await computeSupplyRewards(rewardDistributor1, VTOKEN2, vTOKEN2, ACC2);
supplierAccruedCurrent = await rewardDistributor1.rewardTokenAccrued(ACC2);
expect(supplierAccruedExpected).equals(supplierAccruedCurrent);
await network.provider.request({ method: "hardhat_reset" });
});

it("Rewards for borrowers", async function () {
await mintVTokens(acc1Signer, token2, vTOKEN2, mintAmount);
await vTOKEN2.connect(acc1Signer).borrow(bswBorrowAmount);
await mine(3000000);

if (FORKED_NETWORK == "zksyncsepolia" || FORKED_NETWORK == "zksyncmainnet") {
await mineOnZksync(3000000);
} else {
await mine(3000000);
}
await vTOKEN2.accrueInterest();

// Reward1 calculations for user 1
Expand All @@ -230,6 +255,7 @@ if (FORK) {
borrowerAccruedExpected = await computeBorrowRewards(rewardDistributor1, VTOKEN2, vTOKEN2, ACC1);
borrowerAccruedCurrent = await rewardDistributor1.rewardTokenAccrued(ACC1);
expect(borrowerAccruedExpected).to.closeTo(borrowerAccruedCurrent, parseUnits("0.0000000000000006", 18));
await network.provider.request({ method: "hardhat_reset" });
});
});
}
Loading