Skip to content

Commit

Permalink
Merge pull request #478 from 00labs/synpress-updates
Browse files Browse the repository at this point in the history
Synpress test support
  • Loading branch information
mliu authored May 9, 2024
2 parents 61ceeeb + 936ae7a commit 1e7790f
Show file tree
Hide file tree
Showing 9 changed files with 223 additions and 14 deletions.
2 changes: 2 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
LOCALHOST_CHAIN_ID=
LOCALHOST_MNEMONIC_PHRASE=
66 changes: 66 additions & 0 deletions contracts/common/mock/MockToken18Decimal.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity 0.8.23;

import {ERC20, ERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/draft-ERC20Permit.sol";
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";

contract MockToken18Decimal is ERC20Permit, IERC165 {
using EnumerableSet for EnumerableSet.AddressSet;

/// Transfers to these addresses will fail and return `false` so that we can test transfer failure handling.
EnumerableSet.AddressSet internal _softFailBlocklist;
/// Transfers to these addresses will fail and revert with an error so that we can test transfer failure handling.
EnumerableSet.AddressSet internal _revertingBlocklist;

constructor() ERC20Permit("TestToken") ERC20("TestToken", "USDC") {
_mint(msg.sender, 1000 * 10 ** decimals());
}

function give1000To(address to) external {
_mint(to, 1000 * 10 ** decimals());
}

function give100000To(address to) external {
_mint(to, 100000 * 10 ** decimals());
}

function mint(address to, uint256 amount) external {
_mint(to, amount);
}

function burn(address from, uint256 amount) external {
_burn(from, amount);
}

function addToSoftFailBlocklist(address addr) external {
_softFailBlocklist.add(addr);
}

function removeFromSoftFailBlocklist(address addr) external {
_softFailBlocklist.remove(addr);
}

function addToRevertingBlocklist(address addr) external {
_revertingBlocklist.add(addr);
}

function removeFromRevertingBlocklist(address addr) external {
_revertingBlocklist.remove(addr);
}

function supportsInterface(bytes4 interfaceId) external view virtual override returns (bool) {
return interfaceId == 0x36372b07 || interfaceId == 0x01ffc9a7;
}

function transfer(address to, uint256 amount) public virtual override returns (bool) {
if (_softFailBlocklist.contains(to)) return false;
require(!_revertingBlocklist.contains(to));

return super.transfer(to, amount);
}

function decimals() public view virtual override returns (uint8) {
return 18;
}
}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"deploy-local": "hardhat run ./scripts/deploy-all-local-test-pools.ts --network localhost",
"deploy-local-credit": "hardhat run ./scripts/deploy-credit-line-pools.ts --network localhost",
"deploy-local-receivable": "hardhat run ./scripts/deploy-receivable-backed-credit-line-pools.ts --network localhost",
"deploy-local-synpress-scenario": "hardhat run ./scripts/deploy-all-local-test-pools-synpress-scenario.ts --network localhost",
"format": "prettier '**/*.{js,json,ts,yml,yaml}' --write --plugin=prettier-plugin-organize-imports",
"format-solidity": "prettier 'contracts/**/*.sol' --write --plugin=prettier-plugin-solidity",
"foundry": "bash scripts/install-foundry.sh",
Expand Down
61 changes: 61 additions & 0 deletions scripts/deploy-all-local-test-pools-synpress-scenario.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { ethers } from "hardhat";
import moment from "moment";
import { getAccountSigners } from "../tasks/utils";
import { toToken } from "../test/TestUtils";
import { deployPools } from "./deploy-local-test-pools";
import { LOCAL_PROVIDER, advanceChainBySeconds, advanceChainToTime } from "./utils";

(async () => {
try {
const { poolOwner, juniorLender, sentinelServiceAccount, borrowerInactive } =
await getAccountSigners(ethers);

const contracts = await deployPools();

const creditContracts = contracts[0];
const lpConfig = await creditContracts.poolConfigContract.getLPConfig();

// Allow for withdrawals immediately
await creditContracts.poolConfigContract
.connect(poolOwner)
.setLPConfig({ ...lpConfig, ...{ withdrawalLockoutPeriodInDays: 1 } });

// Advance time to allow for withdrawals
await advanceChainBySeconds(24 * 60 * 60 + 60);

// Create redemption request
await creditContracts.juniorTrancheVaultContract
.connect(juniorLender)
.addRedemptionRequest(toToken(10));

// Advance time to next epoch
let block = await LOCAL_PROVIDER.getBlock("latest");
await advanceChainToTime(
moment.unix(block.timestamp).utc().add(1, "month").startOf("month"),
);

// Process redemption requests by closing epoch
await creditContracts.juniorTrancheVaultContract
.connect(sentinelServiceAccount)
.processYieldForLenders();
await creditContracts.seniorTrancheVaultContract
.connect(sentinelServiceAccount)
.processYieldForLenders();
await creditContracts.epochManagerContract.connect(sentinelServiceAccount).closeEpoch();

// Revoking allowance for inactive borrower
await creditContracts.mockTokenContract
.connect(borrowerInactive)
.approve(creditContracts.creditContract.address, 0);
await creditContracts.mockTokenContract
.connect(borrowerInactive)
.approve(creditContracts.poolSafeContract.address, 0);

console.log(
"Pools are deployed. Junior lender is ready to withdraw from the credit line pool",
);
} catch (error) {
console.error(error);
process.exitCode = 1;
}
})();
80 changes: 73 additions & 7 deletions scripts/deploy-local-test-pools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import moment from "moment";
import { getAccountSigners } from "../tasks/utils";
import {
CreditContractName,
CreditContractType,
CreditManagerContractName,
deployAndSetupPoolContracts,
deployProtocolContracts,
Expand All @@ -17,12 +18,17 @@ import { overrideFirstLossCoverConfig, toToken } from "../test/TestUtils";
import {
CreditLine,
CreditLineManager,
EpochManager,
FirstLossCover,
MockToken,
Pool,
PoolConfig,
PoolSafe,
ReceivableBackedCreditLine,
ReceivableBackedCreditLineManager,
TrancheVault,
} from "../typechain-types";
import { advanceChainTime } from "./utils";
import { advanceChainToTime } from "./utils";

const poolsToDeploy: {
creditContract: CreditContractName;
Expand Down Expand Up @@ -64,7 +70,16 @@ async function deployPool(
creditContractName: CreditContractName,
creditManagerContractName: CreditManagerContractName,
poolName?: LocalPoolName,
) {
): Promise<{
poolContract: Pool;
poolConfigContract: PoolConfig;
poolSafeContract: PoolSafe;
creditContract: CreditContractType;
epochManagerContract: EpochManager;
juniorTrancheVaultContract: TrancheVault;
seniorTrancheVaultContract: TrancheVault;
mockTokenContract: MockToken;
}> {
console.log("=====================================");
console.log(`Deploying pool with ${creditContractName} and ${creditManagerContractName}`);
if (poolName) {
Expand All @@ -84,6 +99,7 @@ async function deployPool(
seniorLender,
lenderRedemptionActive,
borrowerActive,
borrowerInactive,
} = await getAccountSigners(ethers);

console.log("Deploying and setting up protocol contracts");
Expand Down Expand Up @@ -122,7 +138,7 @@ async function deployPool(
treasury,
poolOwnerTreasury,
poolOperator,
[juniorLender, seniorLender, lenderRedemptionActive, borrowerActive],
[juniorLender, seniorLender, lenderRedemptionActive, borrowerActive, borrowerInactive],
);

// Deposit first loss cover
Expand Down Expand Up @@ -180,6 +196,17 @@ async function deployPool(
0,
true,
);
await (creditManagerContract as CreditLineManager)
.connect(evaluationAgent)
.approveBorrower(
borrowerInactive.address,
toToken(100_000),
5, // numOfPeriods
1217, // yieldInBps
toToken(0),
0,
true,
);
const borrowAmount = toToken(100_000);

// Drawing down credit line
Expand Down Expand Up @@ -219,6 +246,17 @@ async function deployPool(
0,
true,
);
await (creditManagerContract as ReceivableBackedCreditLineManager)
.connect(evaluationAgent)
.approveBorrower(
borrowerInactive.address,
toToken(100_000),
5, // numOfPeriods
1217, // yieldInBps
toToken(0),
0,
true,
);
const borrowAmount = toToken(100_000);

const currentBlockTimestamp = await time.latest();
Expand Down Expand Up @@ -248,6 +286,7 @@ async function deployPool(
console.log(`Junior lender: ${juniorLender.address}`);
console.log(`Senior lender: ${seniorLender.address}`);
console.log(`Borrower: ${borrowerActive.address}`);
console.log(`Inactive Borrower: ${borrowerInactive.address}`);
console.log(`Sentinel Service: ${sentinelServiceAccount.address}`);
console.log(`Pool owner: ${poolOwner.address}`);
console.log("-------------------------------------");
Expand All @@ -256,7 +295,6 @@ async function deployPool(
console.log(`Pool: ${poolContract.address}`);
console.log(`Epoch manager: ${epochManagerContract.address}`);
console.log(`Pool config: ${poolConfigContract.address}`);
console.log(`Pool credit: ${creditContract.address}`);
console.log(`Junior tranche: ${juniorTrancheVaultContract.address}`);
console.log(`Senior tranche: ${seniorTrancheVaultContract.address}`);
console.log(`Pool safe: ${poolSafeContract.address}`);
Expand All @@ -269,17 +307,41 @@ async function deployPool(
console.log(`Receivable: ${receivableContract.address}`);
}
console.log("=====================================");

return {
poolContract,
epochManagerContract,
poolSafeContract,
creditContract,
poolConfigContract,
juniorTrancheVaultContract,
seniorTrancheVaultContract,
mockTokenContract,
};
}

export async function deployPools(
onlyDeployPoolName: LocalPoolName | undefined = undefined,
shouldAdvanceTime: boolean = true,
) {
): Promise<
Array<{
poolContract: Pool;
poolSafeContract: PoolSafe;
epochManagerContract: EpochManager;
poolConfigContract: PoolConfig;
creditContract: CreditContractType;
juniorTrancheVaultContract: TrancheVault;
seniorTrancheVaultContract: TrancheVault;
mockTokenContract: MockToken;
}>
> {
try {
const contracts = [];

if (shouldAdvanceTime) {
// always set the date to the 1st of the next month
const blockchainStartDate = moment().utc().add(1, "month").startOf("month");
await advanceChainTime(blockchainStartDate);
await advanceChainToTime(blockchainStartDate);
}

if (onlyDeployPoolName) {
Expand All @@ -298,11 +360,15 @@ export async function deployPools(
}
} else {
for (const pool of poolsToDeploy) {
await deployPool(pool.creditContract, pool.manager, pool.poolName);
contracts.push(await deployPool(pool.creditContract, pool.manager, pool.poolName));
}
}

return contracts;
} catch (error) {
console.error(error);
process.exitCode = 1;

throw error;
}
}
17 changes: 15 additions & 2 deletions scripts/utils.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { ethers } from "hardhat";
import moment from "moment";

const LOCAL_PROVIDER = new ethers.providers.JsonRpcProvider("http://127.0.0.1:8545");
export const LOCAL_PROVIDER = new ethers.providers.JsonRpcProvider("http://127.0.0.1:8545");

export const advanceChainTime = async (date: moment.Moment) => {
export const advanceChainToTime = async (date: moment.Moment) => {
console.log("Advancing to fix date");
let block = await LOCAL_PROVIDER.getBlock("latest");
const timeToAdvance = date.unix() - block.timestamp;
Expand All @@ -22,3 +22,16 @@ export const advanceChainTime = async (date: moment.Moment) => {
block = await LOCAL_PROVIDER.getBlock("latest");
console.log("Block timestamp after advancing: ", block.timestamp);
};

export const advanceChainBySeconds = async (seconds: number) => {
console.log(
`Advancing blockchain by ${seconds} seconds (~${(seconds / 60 / 60 / 24).toPrecision(
3,
)} days)`,
);

await LOCAL_PROVIDER.send("evm_increaseTime", [seconds]);
await LOCAL_PROVIDER.send("evm_mine", []);
const block = await LOCAL_PROVIDER.getBlock("latest");
console.log("Block timestamp after advancing: ", block.timestamp);
};
5 changes: 1 addition & 4 deletions tasks/advance-week-and-drawdown-receivable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,7 @@ task(
"advanceWeekAndDrawdownReceivable",
"Pays back to the pool and draws down with a receivable, then advances blockchain time by a week",
)
.addParam(
"poolConfigAddr",
"The PoolConfig contract address of the pool in question",
)
.addParam("poolConfigAddr", "The PoolConfig contract address of the pool in question")
.setAction(async (taskArgs: { poolConfigAddr: string }, hre: HardhatRuntimeEnvironment) => {
const { borrowerActive } = await getAccountSigners(hre.ethers);

Expand Down
3 changes: 3 additions & 0 deletions tasks/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export const getAccountSigners = async (
seniorLender: SignerWithAddress;
lenderRedemptionActive: SignerWithAddress;
borrowerActive: SignerWithAddress;
borrowerInactive: SignerWithAddress;
}> => {
const [
defaultDeployer,
Expand All @@ -30,6 +31,7 @@ export const getAccountSigners = async (
seniorLender,
lenderRedemptionActive,
borrowerActive,
borrowerInactive,
] = await ethersClient.getSigners();

return {
Expand All @@ -45,5 +47,6 @@ export const getAccountSigners = async (
seniorLender,
lenderRedemptionActive,
borrowerActive,
borrowerInactive,
};
};

0 comments on commit 1e7790f

Please sign in to comment.