Skip to content

Commit

Permalink
test(flash-swap): update integration tests to mirror FlashUniswapV3 c…
Browse files Browse the repository at this point in the history
…ontract changes

test(flash-swap): remove "uniswapV3Pool" contract type to "Contracts" interface
test(flash-swap): add "dai" contract type to "Contracts" interface
feat(constants): add "DAI_{DECIMALS,NAME,SYMBOL}" constants to tokens.ts
feat(constants): add "DEFAULT_FEE" constant to oracles.ts
  • Loading branch information
scorpion9979 committed Dec 12, 2023
1 parent 0f5868c commit 2e519d3
Show file tree
Hide file tree
Showing 8 changed files with 106 additions and 53 deletions.
1 change: 1 addition & 0 deletions packages/constants/src/oracles.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { BigNumber } from "@ethersproject/bignumber";

export const DEFAULT_CARDINALITY: number = 144;
export const DEFAULT_FEE: number = 500;
export const DEFAULT_TWAP_INTERVAL: number = 1800;
export const Q192: BigNumber = BigNumber.from("6277101735386680763835789423207666416102355444464034512896");
export const TICKS = {
Expand Down
4 changes: 4 additions & 0 deletions packages/constants/src/tokens.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import { BigNumber } from "@ethersproject/bignumber";

export const DAI_DECIMALS: BigNumber = BigNumber.from(18);
export const DAI_NAME: string = "Dai Stablecoin";
export const DAI_SYMBOL: string = "DAI";

export const USDC_DECIMALS: BigNumber = BigNumber.from(6);
export const USDC_NAME: string = "USD Coin";
export const USDC_PRICE_PRECISION_SCALAR: BigNumber = BigNumber.from(1_000_000_000_000);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,21 @@ export function integrationTestFlashUniswapV3(): void {
beforeEach(async function () {
const {
balanceSheet,
dai,
fintroller,
flashUniswapV3,
hToken,
uniswapV3Pool,
uniswapV3PositionManager,
usdc,
usdcPriceFeed,
wbtc,
wbtcPriceFeed,
} = await this.loadFixture(integrationFixture);
this.contracts.balanceSheet = balanceSheet;
this.contracts.dai = dai;
this.contracts.fintroller = fintroller;
this.contracts.flashUniswapV3 = flashUniswapV3;
this.contracts.hToken = hToken;
this.contracts.uniswapV3Pool = uniswapV3Pool;
this.contracts.uniswapV3PositionManager = uniswapV3PositionManager;
this.contracts.usdc = usdc;
this.contracts.usdcPriceFeed = usdcPriceFeed;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { BigNumber } from "@ethersproject/bignumber";
import { MaxUint256 } from "@ethersproject/constants";
import { Zero } from "@ethersproject/constants";
import { LIQUIDATION_INCENTIVES } from "@hifi/constants";
import { DEFAULT_FEE, LIQUIDATION_INCENTIVES } from "@hifi/constants";
import { BalanceSheetErrors, FlashUniswapV3Errors } from "@hifi/errors";
import { USDC, WBTC, getNow, hUSDC, price } from "@hifi/helpers";
import { expect } from "chai";
Expand All @@ -21,13 +21,15 @@ async function getFlashLiquidateParams(
): Promise<FlashLiquidateParams> {
const borrower: string = this.signers.borrower.address;
const bond: string = this.contracts.hToken.address;
const fee: number = await this.contracts.uniswapV3Pool.fee();
const underlying: string = await this.contracts.hToken.underlying();
const underlying: string = this.contracts.usdc.address;
const params: FlashLiquidateParams = {
borrower: borrower,
bond: bond,
collateral: collateral,
path: utils.solidityPack(["address", "uint24", "address"], [underlying, fee, collateral]),
path: utils.solidityPack(
["address", "uint24", "address", "uint24", "address"],
[underlying, DEFAULT_FEE, this.contracts.dai.address, DEFAULT_FEE, collateral],
),
turnout: turnout,
underlyingAmount: underlyingAmount,
};
Expand All @@ -40,7 +42,7 @@ export function shouldBehaveLikeFlashLiquidate(): void {
const collateralCeiling: BigNumber = USDC("1e6");
const debtCeiling: BigNumber = hUSDC("1e6");
const depositCollateralAmount: BigNumber = WBTC("1");
const profitCollateralAmount: BigNumber = WBTC("0.59979736");
const profitCollateralAmount: BigNumber = WBTC("0.59959926");
const swapUnderlyingAmount: BigNumber = USDC("10000");

context("when the collateral is the same as the underlying", function () {
Expand All @@ -60,8 +62,29 @@ export function shouldBehaveLikeFlashLiquidate(): void {
// Set the oracle price to 1 USDC = $1.
await this.contracts.usdcPriceFeed.setPrice(price("1"));

// Set up the Uniswap V3 Pool and mint a position.
await mintPoolReserves.call(this, "100000000000000");
// Set up the WBTC-DAI Uniswap V3 Pool and mint a position.
await mintPoolReserves.call(
this,
this.contracts.wbtc,
this.contracts.dai,
DEFAULT_FEE,
// Create a position with price range 1 WBTC ~ 25k DAI.
"1",
"250000000000000",
"10000000000000000000000",
);

// Set up the DAI-USDC Uniswap V3 Pool and mint a position.
await mintPoolReserves.call(
this,
this.contracts.usdc,
this.contracts.dai,
DEFAULT_FEE,
// Create a position with price range 1 DAI ~ 1 USDC.
"1",
"1000000000000",
"10000000000000000000000",
);

// List the bond in the Fintroller.
await this.contracts.fintroller.connect(this.signers.admin).listBond(this.contracts.hToken.address);
Expand Down Expand Up @@ -194,10 +217,10 @@ export function shouldBehaveLikeFlashLiquidate(): void {
});

context("when the repay amount is equal to the seized amount", function () {
const subsidyCollateralAmount: BigNumber = WBTC("0.00051608");
const liquidationIncentive = toBn("1.00051608");
const repayCollateralAmount: BigNumber = WBTC("1.00051608");
const seizeCollateralAmount: BigNumber = WBTC("1.00051608");
const subsidyCollateralAmount: BigNumber = WBTC("0.00100342");
const liquidationIncentive = toBn("1.00100342");
const repayCollateralAmount: BigNumber = WBTC("1.00100342");
const seizeCollateralAmount: BigNumber = WBTC("1.00100342");
const swapUnderlyingAmount: BigNumber = USDC("25000");
let params: FlashLiquidateParams;

Expand Down Expand Up @@ -247,9 +270,9 @@ export function shouldBehaveLikeFlashLiquidate(): void {
});

context("when the repay amount is greater than the seized amount", function () {
const subsidyCollateralAmount: BigNumber = WBTC("0.20062309");
const subsidyCollateralAmount: BigNumber = WBTC("0.20120474");
const seizeCollateralAmount: BigNumber = WBTC("1");
const repayCollateralAmount: BigNumber = WBTC("1.20062309");
const repayCollateralAmount: BigNumber = WBTC("1.20120474");
const swapUnderlyingAmount: BigNumber = USDC("30000");

beforeEach(async function () {
Expand Down Expand Up @@ -320,7 +343,7 @@ export function shouldBehaveLikeFlashLiquidate(): void {
});

context("when the repay amount is less than the seized amount", function () {
const repayCollateralAmount: BigNumber = WBTC("0.40020264");
const repayCollateralAmount: BigNumber = WBTC("0.40040074");
const seizeCollateralAmount: BigNumber = WBTC("1");

beforeEach(async function () {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import { defaultAbiCoder } from "@ethersproject/abi";
import { BigNumber } from "@ethersproject/bignumber";
import { Zero } from "@ethersproject/constants";
import { DEFAULT_FEE } from "@hifi/constants";
import { FlashUniswapV3Errors } from "@hifi/errors";
import { USDC, WBTC } from "@hifi/helpers";
import { expect } from "chai";
import { utils } from "ethers";

import { shouldBehaveLikeFlashLiquidate } from "./flashLiquidate";

async function getSwapCallbackData(this: Mocha.Context, collateral: string, fee: number): Promise<string> {
async function getSwapCallbackData(this: Mocha.Context, collateral: string): Promise<string> {
const bond: string = this.contracts.hToken.address;
const borrower: string = this.signers.borrower.address;
const sender: string = this.signers.raider.address;
Expand All @@ -32,7 +33,7 @@ async function getSwapCallbackData(this: Mocha.Context, collateral: string, fee:
bond,
borrower,
collateral,
path: utils.solidityPack(["address", "uint24", "address"], [underlying, fee, collateral]),
path: utils.solidityPack(["address", "uint24", "address"], [underlying, DEFAULT_FEE, collateral]),
sender,
turnout,
underlyingAmount,
Expand All @@ -59,8 +60,7 @@ export function shouldBehaveLikeUniswapV3SwapCallback(): void {
let data: string;

beforeEach(async function () {
const fee: number = await this.contracts.uniswapV3Pool.fee();
data = await getSwapCallbackData.call(this, this.contracts.wbtc.address, fee);
data = await getSwapCallbackData.call(this, this.contracts.wbtc.address);
});

context("when the caller is not the UniswapV3Pool contract", function () {
Expand Down
23 changes: 16 additions & 7 deletions packages/flash-swap/test/shared/fixtures.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
import { Signer } from "@ethersproject/abstract-signer";
import { keccak256 } from "@ethersproject/keccak256";
import { H_TOKEN_MATURITY_ONE_YEAR, WETH_DECIMALS, WETH_NAME, WETH_SYMBOL } from "@hifi/constants";
import {
DAI_DECIMALS,
DAI_NAME,
DAI_SYMBOL,
DEFAULT_FEE,
H_TOKEN_MATURITY_ONE_YEAR,
WETH_DECIMALS,
WETH_NAME,
WETH_SYMBOL,
} from "@hifi/constants";
import { USDC_DECIMALS, USDC_NAME, USDC_SYMBOL, WBTC_DECIMALS, WBTC_NAME, WBTC_SYMBOL } from "@hifi/constants";
import { getHTokenName, getHTokenSymbol } from "@hifi/helpers";
import type { BalanceSheetV2 } from "@hifi/protocol/dist/types/contracts/core/balance-sheet/BalanceSheetV2";
Expand All @@ -27,13 +36,13 @@ import { deployGodModeErc20 } from "./deployers";

type IntegrationFixtureReturnType = {
balanceSheet: BalanceSheetV2;
dai: GodModeErc20;
fintroller: Fintroller;
flashUniswapV2: FlashUniswapV2;
flashUniswapV3: FlashUniswapV3;
hToken: GodModeHToken;
maliciousV2Pair: MaliciousV2Pair;
uniswapV2Pair: GodModeUniswapV2Pair;
uniswapV3Pool: UniswapV3Pool;
uniswapV3PositionManager: GodModeNonfungiblePositionManager;
usdc: GodModeErc20;
usdcPriceFeed: SimplePriceFeed;
Expand All @@ -45,6 +54,7 @@ export async function integrationFixture(signers: Signer[]): Promise<Integration
const deployer: Signer = signers[0];

const wbtc: GodModeErc20 = await deployGodModeErc20(deployer, WBTC_NAME, WBTC_SYMBOL, WBTC_DECIMALS);
const dai: GodModeErc20 = await deployGodModeErc20(deployer, DAI_NAME, DAI_SYMBOL, DAI_DECIMALS);
const usdc: GodModeErc20 = await deployGodModeErc20(deployer, USDC_NAME, USDC_SYMBOL, USDC_DECIMALS);

const simplePriceFeedArtifact: Artifact = await artifacts.readArtifact("SimplePriceFeed");
Expand Down Expand Up @@ -112,10 +122,9 @@ export async function integrationFixture(signers: Signer[]): Promise<Integration
const uniswapV3Factory: IUniswapV3Factory = <IUniswapV3Factory>(
await waffle.deployContract(deployer, uniswapV3FactoryArtifact, [])
);
await uniswapV3Factory.createPool(wbtc.address, usdc.address, 500);
const v3PoolAddress: string = await uniswapV3Factory.getPool(wbtc.address, usdc.address, 500);

const uniswapV3Pool: UniswapV3Pool = UniswapV3Pool__factory.connect(v3PoolAddress, deployer);
// Create pools for WBTC-DAI and DAI-USDC.
await uniswapV3Factory.createPool(wbtc.address, dai.address, DEFAULT_FEE);
await uniswapV3Factory.createPool(dai.address, usdc.address, DEFAULT_FEE);

const weth: GodModeErc20 = await deployGodModeErc20(deployer, WETH_NAME, WETH_SYMBOL, WETH_DECIMALS);

Expand All @@ -131,13 +140,13 @@ export async function integrationFixture(signers: Signer[]): Promise<Integration

return {
balanceSheet,
dai,
fintroller,
flashUniswapV2,
flashUniswapV3,
hToken,
maliciousV2Pair,
uniswapV2Pair,
uniswapV3Pool,
uniswapV3PositionManager,
usdc,
usdcPriceFeed,
Expand Down
65 changes: 41 additions & 24 deletions packages/flash-swap/test/shared/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { BigNumber } from "@ethersproject/bignumber";
import { Zero } from "@ethersproject/constants";
import { Token } from "@uniswap/sdk-core";
import { Pool, Position, nearestUsableTick } from "@uniswap/v3-sdk";
import { Pool, Position, computePoolAddress, nearestUsableTick } from "@uniswap/v3-sdk";
import bn from "bignumber.js";

import { GodModeErc20, UniswapV3Pool, UniswapV3Pool__factory } from "../../src/types";

bn.config({ EXPONENTIAL_AT: 999999, DECIMAL_PLACES: 40 });

export async function increaseUniswapV2PoolReserves(
Expand Down Expand Up @@ -44,33 +46,48 @@ export async function reduceUniswapV2PoolReserves(
await this.contracts.uniswapV2Pair.sync();
}

export async function mintUniswapV3PoolReserves(this: Mocha.Context, mintLiquidity: string): Promise<void> {
// Create a position with price range 1 WBTC ~ 20k USDC.
const [reserve0, reserve1] = ["1", "250"];
export async function mintUniswapV3PoolReserves(
this: Mocha.Context,
token0: GodModeErc20,
token1: GodModeErc20,
fee: number,
reserve0: string,
reserve1: string,
mintLiquidity: string,
): Promise<void> {
await this.contracts.uniswapV3PositionManager.createAndInitializePoolIfNecessary(
this.contracts.wbtc.address,
this.contracts.usdc.address,
500,
token0.address,
token1.address,
fee,
new bn(reserve1).div(reserve0).sqrt().multipliedBy(new bn(2).pow(96)).integerValue(3).toString(),
);

const [tickSpacing, fee, liquidity, [sqrtPriceX96, tick]] = await Promise.all([
this.contracts.uniswapV3Pool.tickSpacing(),
this.contracts.uniswapV3Pool.fee(),
this.contracts.uniswapV3Pool.liquidity(),
this.contracts.uniswapV3Pool.slot0(),
]);

let usdc, wbtc;
{
const { name, symbol, decimals } = this.contracts.wbtc;
wbtc = new Token(31337, this.contracts.wbtc.address, await decimals(), await symbol(), await name());
const { name, symbol, decimals } = token0;
wbtc = new Token(31337, token0.address, await decimals(), await symbol(), await name());
}
{
const { name, symbol, decimals } = this.contracts.usdc;
usdc = new Token(31337, this.contracts.usdc.address, await decimals(), await symbol(), await name());
const { name, symbol, decimals } = token1;
usdc = new Token(31337, token1.address, await decimals(), await symbol(), await name());
}

const uniswapV3Pool: UniswapV3Pool = UniswapV3Pool__factory.connect(
computePoolAddress({
factoryAddress: await this.contracts.uniswapV3PositionManager.factory(),
tokenA: wbtc,
tokenB: usdc,
fee: fee,
}),
this.signers.admin,
);

const [tickSpacing, liquidity, [sqrtPriceX96, tick]] = await Promise.all([
uniswapV3Pool.tickSpacing(),
uniswapV3Pool.liquidity(),
uniswapV3Pool.slot0(),
]);

const pool = new Pool(wbtc, usdc, fee, sqrtPriceX96.toString(), liquidity.toString(), tick);

const position = new Position({
Expand All @@ -83,19 +100,19 @@ export async function mintUniswapV3PoolReserves(this: Mocha.Context, mintLiquidi
const { amount0: amount0Desired, amount1: amount1Desired } = position.mintAmounts;

// Mint WBTC to the admin address.
await this.contracts.wbtc.__godMode_mint(this.signers.admin.address, amount0Desired.toString());
await token0.__godMode_mint(this.signers.admin.address, amount0Desired.toString());

// Mint USDC to the admin address.
await this.contracts.usdc.__godMode_mint(this.signers.admin.address, amount1Desired.toString());
await token1.__godMode_mint(this.signers.admin.address, amount1Desired.toString());

await this.contracts.wbtc.approve(this.contracts.uniswapV3PositionManager.address, amount0Desired.toString());
await token0.approve(this.contracts.uniswapV3PositionManager.address, amount0Desired.toString());

await this.contracts.usdc.approve(this.contracts.uniswapV3PositionManager.address, amount1Desired.toString());
await token1.approve(this.contracts.uniswapV3PositionManager.address, amount1Desired.toString());

await this.contracts.uniswapV3PositionManager.mint(
{
token0: this.contracts.wbtc.address,
token1: this.contracts.usdc.address,
token0: token0.address,
token1: token1.address,
fee: fee,
tickLower: nearestUsableTick(tick, tickSpacing) - tickSpacing * 2,
tickUpper: nearestUsableTick(tick, tickSpacing) + tickSpacing * 2,
Expand Down
3 changes: 1 addition & 2 deletions packages/flash-swap/test/shared/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import type { FlashUniswapV2 } from "../../src/types/contracts/uniswap-v2/FlashU
import type { GodModeUniswapV2Pair } from "../../src/types/contracts/uniswap-v2/test/GodModeUniswapV2Pair";
import type { MaliciousPair as MaliciousV2Pair } from "../../src/types/contracts/uniswap-v2/test/MaliciousPair";
import type { FlashUniswapV3 } from "../../src/types/contracts/uniswap-v3/FlashUniswapV3";
import type { UniswapV3Pool } from "../../src/types/contracts/uniswap-v3/UniswapV3Pool";
import type { GodModeNonfungiblePositionManager } from "../../src/types/contracts/uniswap-v3/test/GodModeNonfungiblePositionManager";

declare module "mocha" {
Expand All @@ -21,6 +20,7 @@ declare module "mocha" {

export interface Contracts {
balanceSheet: BalanceSheetV2;
dai: GodModeErc20;
fintroller: Fintroller;
flashUniswapV2: FlashUniswapV2;
flashUniswapV3: FlashUniswapV3;
Expand All @@ -29,7 +29,6 @@ export interface Contracts {
usdc: GodModeErc20;
usdcPriceFeed: SimplePriceFeed;
uniswapV2Pair: GodModeUniswapV2Pair;
uniswapV3Pool: UniswapV3Pool;
uniswapV3PositionManager: GodModeNonfungiblePositionManager;
wbtc: GodModeErc20;
wbtcPriceFeed: SimplePriceFeed;
Expand Down

0 comments on commit 2e519d3

Please sign in to comment.