Skip to content

Commit

Permalink
chore; begin legacy test implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
0xApotheosis committed Sep 11, 2024
1 parent 32a877a commit 0e215a2
Show file tree
Hide file tree
Showing 3 changed files with 260 additions and 91 deletions.
11 changes: 11 additions & 0 deletions test/LegacyTests.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { deployStakingRewardsFixture, loadFixtureWithGas } from "./utils";
import { expect } from "chai";

describe("StakingRewards", () => {
describe("Deployment", function () {
it("deploy cost [ @skip-on-coverage ]", async () => {
const { gasUsed } = await loadFixtureWithGas(deployStakingRewardsFixture);
expect(gasUsed.toString()).to.eq("2388750");
});
});
});
95 changes: 4 additions & 91 deletions test/StakingRewards.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,98 +2,11 @@ import {
time,
loadFixture,
} from "@nomicfoundation/hardhat-toolbox-viem/network-helpers";
import { expect } from "chai";
import hre from "hardhat";
import { getAddress, parseEther, formatEther, toBytes } from "viem";
import { expect } from "chai";
import { getAddress, parseEther } from "viem";
import { mockToken, deployStakingRewardsFixture, DEFAULT_SUPPLY } from "./utils";

const defaultSupply = 1e8;

// Mock token helper function
async function mockToken({
accounts,
synth = undefined,
name = 'name',
symbol = 'ABC',
supply = defaultSupply,
skipInitialAllocation = false,
}: {
accounts: { account: { address: string } }[];
synth?: string;
name?: string;
symbol?: string;
supply?: number;
skipInitialAllocation?: boolean;
}) {
const [deployerAccount, owner] = accounts;

const totalSupply = parseEther(supply.toString());

const proxy = await hre.viem.deployContract('ProxyERC20', [owner.account.address]);
const tokenState = await hre.viem.deployContract('TokenState', [owner.account.address, deployerAccount.account.address]);

if (!skipInitialAllocation && supply > 0) {
await tokenState.write.setBalanceOf([owner.account.address, totalSupply], { account: deployerAccount.account });
}

const tokenArgs = [
proxy.address,
tokenState.address,
name,
symbol,
totalSupply,
owner.account.address,
];

if (synth) {
tokenArgs.push(synth);
}

const token = await hre.viem.deployContract(synth ? 'MockSynth' : 'PublicEST', tokenArgs);
await Promise.all([
tokenState.write.setAssociatedContract([token.address], { account: owner.account }),
proxy.write.setTarget([token.address], { account: owner.account }),
]);

return { token, tokenState, proxy };
}

describe("StakingRewards", function () {
async function deployStakingRewardsFixture() {
const [owner, rewardsDistribution, stakingAccount1, otherAccount] = await hre.viem.getWalletClients();

const { token: rewardsToken } = await mockToken({
accounts: [owner, rewardsDistribution],
name: 'Rewards Token',
symbol: 'RWD',
});

const { token: stakingToken } = await mockToken({
accounts: [owner, stakingAccount1],
name: 'Staking Token',
symbol: 'STK',
});

const stakingRewards = await hre.viem.deployContract("contracts/StakingRewards.sol:StakingRewards", [
owner.account.address,
rewardsDistribution.account.address,
rewardsToken.address,
stakingToken.address,
]);

const publicClient = await hre.viem.getPublicClient();

return {
stakingRewards,
rewardsToken,
stakingToken,
owner,
rewardsDistribution,
stakingAccount1,
otherAccount,
publicClient,
};
}

describe("Deployment", function () {
it("Should set the right owner", async function () {
const { stakingRewards, owner } = await loadFixture(deployStakingRewardsFixture);
Expand Down Expand Up @@ -206,7 +119,7 @@ import {
await stakingRewards.write.withdraw([stakeAmount], { account: stakingAccount1.account });

expect(await stakingRewards.read.balanceOf([stakingAccount1.account.address])).to.equal(0n);
expect(await stakingToken.read.balanceOf([stakingAccount1.account.address])).to.equal(parseEther(defaultSupply.toString()));
expect(await stakingToken.read.balanceOf([stakingAccount1.account.address])).to.equal(parseEther(DEFAULT_SUPPLY.toString()));
});
});

Expand Down
245 changes: 245 additions & 0 deletions test/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,245 @@
import { BigNumber, providers, utils, Contract } from 'ethers'
import { getAddress, parseEther } from "viem";
import hre from "hardhat";
import { loadFixture, takeSnapshot } from "@nomicfoundation/hardhat-toolbox-viem/network-helpers";

export const DEFAULT_SUPPLY = 1e8;

export async function mockToken({
accounts,
synth = undefined,
name = 'name',
symbol = 'ABC',
supply = DEFAULT_SUPPLY,
skipInitialAllocation = false,
}: {
accounts: { account: { address: string } }[];
synth?: string;
name?: string;
symbol?: string;
supply?: number;
skipInitialAllocation?: boolean;
}) {
const [deployerAccount, owner] = accounts;

const totalSupply = parseEther(supply.toString());

const proxy = await hre.viem.deployContract('ProxyERC20', [owner.account.address]);
const tokenState = await hre.viem.deployContract('TokenState', [owner.account.address, deployerAccount.account.address]);

if (!skipInitialAllocation && supply > 0) {
await tokenState.write.setBalanceOf([owner.account.address, totalSupply], { account: deployerAccount.account });
}

const tokenArgs = [
proxy.address,
tokenState.address,
name,
symbol,
totalSupply,
owner.account.address,
];

if (synth) {
tokenArgs.push(synth);
}

const token = await hre.viem.deployContract(synth ? 'MockSynth' : 'PublicEST', tokenArgs);
await Promise.all([
tokenState.write.setAssociatedContract([token.address], { account: owner.account }),
proxy.write.setTarget([token.address], { account: owner.account }),
]);

return { token, tokenState, proxy };
}

export async function deployStakingRewardsFixture() {
const [owner, rewardsDistribution, stakingAccount1, otherAccount] = await hre.viem.getWalletClients();

const { token: rewardsToken } = await mockToken({
accounts: [owner, rewardsDistribution],
name: 'Rewards Token',
symbol: 'RWD',
});

const { token: stakingToken } = await mockToken({
accounts: [owner, stakingAccount1],
name: 'Staking Token',
symbol: 'STK',
});

const stakingRewards = await hre.viem.deployContract("contracts/StakingRewards.sol:StakingRewards", [
owner.account.address,
rewardsDistribution.account.address,
rewardsToken.address,
stakingToken.address,
]);

const publicClient = await hre.viem.getPublicClient();

return {
stakingRewards,
rewardsToken,
stakingToken,
owner,
rewardsDistribution,
stakingAccount1,
otherAccount,
publicClient,
};
}


type Fixture<T> = () => Promise<T>;

interface Snapshot<T> {
restorer: { restore: () => Promise<void>; snapshotId: string };
fixture: Fixture<T>;
data: T;
gasUsed: bigint;
}

let snapshots: Snapshot<any>[] = [];

// This has been adapted from @nomicfoundation/hardhat-network-helpers/src/loadFixture.ts to include the gas used during the fixture deployment.
export async function loadFixtureWithGas<T>(fixture: Fixture<T>): Promise<T & { gasUsed: bigint }> {
if (fixture.name === "") {
throw new Error("FixtureAnonymousFunctionError");
}

const snapshot = snapshots.find((s) => s.fixture === fixture);

if (snapshot !== undefined) {
await snapshot.restorer.restore();
snapshots = snapshots.filter(
(s) =>
Number(s.restorer.snapshotId) <= Number(snapshot.restorer.snapshotId)
);

return { ...snapshot.data, gasUsed: snapshot.gasUsed };
} else {
const client = await hre.viem.getPublicClient();
const initialGasUsed = await client.getBlockNumber().then(bn => client.getBlock({ blockNumber: bn })).then(b => b!.gasUsed);
const data = await fixture();
const finalGasUsed = await client.getBlockNumber().then(bn => client.getBlock({ blockNumber: bn })).then(b => b!.gasUsed);
const gasUsed = finalGasUsed - initialGasUsed;

const restorer = await takeSnapshot();

snapshots.push({
restorer,
fixture,
data,
gasUsed,
});

return { ...data, gasUsed };
}
}

const PERMIT_TYPEHASH = utils.keccak256(
utils.toUtf8Bytes('Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)')
)

function getDomainSeparator(name: string, tokenAddress: string, chainId: number) {
return utils.keccak256(
utils.defaultAbiCoder.encode(
['bytes32', 'bytes32', 'bytes32', 'uint256', 'address'],
[
utils.keccak256(
utils.toUtf8Bytes('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)')
),
utils.keccak256(utils.toUtf8Bytes(name)),
utils.keccak256(utils.toUtf8Bytes('1')),
chainId,
tokenAddress,
]
)
)
}

export async function getApprovalDigest(
token: Contract,
approve: {
owner: string
spender: string
value: BigNumber
},
nonce: BigNumber,
deadline: BigNumber,
chainId: number
): Promise<string> {
const name = await token.name()
const DOMAIN_SEPARATOR = getDomainSeparator(name, token.address, chainId)
return utils.keccak256(
utils.solidityPack(
['bytes1', 'bytes1', 'bytes32', 'bytes32'],
[
'0x19',
'0x01',
DOMAIN_SEPARATOR,
utils.keccak256(
utils.defaultAbiCoder.encode(
['bytes32', 'address', 'address', 'uint256', 'uint256', 'uint256'],
[PERMIT_TYPEHASH, approve.owner, approve.spender, approve.value, nonce, deadline]
)
),
]
)
)
}

export const REWARDS_DURATION = 60 * 60 * 24 * 135

export function expandTo18Decimals(n: number): BigNumber {
return BigNumber.from(n).mul(BigNumber.from(10).pow(18))
}

export async function mineBlock(provider: providers.JsonRpcProvider, timestamp: number): Promise<void> {
return provider.send('evm_mine', [timestamp])
}

export function setupTests() {
const chai = require('chai')
if (process.env.HARDHAT) {
const { ethers, waffle } = require('hardhat')
const { provider } = waffle
const stakingRewardsPath = '../artifacts/contracts/StakingRewards.sol/StakingRewards.json'
const stakingRewardsFactoryPath = '../artifacts/contracts/StakingRewardsFactory.sol/StakingRewardsFactory.json'
const testERC20Path = '../artifacts/contracts/test/TestERC20.sol/TestERC20.json'
return {
expect: chai.expect,
ethers,
waffle,
provider,
StakingRewards: require(stakingRewardsPath),
StakingRewardsFactory: require(stakingRewardsFactoryPath),
TestERC20: require(testERC20Path),
isHardhat: true,
}
} else {
const ethers = require('ethers')
const waffle = require('ethereum-waffle')
const provider = new waffle.MockProvider({
ganacheOptions: {
hardfork: 'istanbul',
mnemonic: 'horn horn horn horn horn horn horn horn horn horn horn horn',
gasLimit: 9999999,
},
})
const stakingRewardsPath = '../build/StakingRewards.json'
const stakingRewardsFactoryPath = '../build/StakingRewardsFactory.json'
const testERC20Path = '../build/TestERC20.json'
chai.use(waffle.solidity)
return {
expect: chai.expect,
ethers,
waffle,
provider,
StakingRewards: require(stakingRewardsPath),
StakingRewardsFactory: require(stakingRewardsFactoryPath),
TestERC20: require(testERC20Path),
isHardhat: false,
}
}
}

0 comments on commit 0e215a2

Please sign in to comment.