Skip to content

Commit

Permalink
fix: Liquidator tests
Browse files Browse the repository at this point in the history
  • Loading branch information
naddison36 committed Jul 16, 2021
1 parent 4cb9e6b commit 70a3d98
Show file tree
Hide file tree
Showing 9 changed files with 570 additions and 353 deletions.
42 changes: 21 additions & 21 deletions contracts/masset/liquidator/Liquidator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -350,27 +350,27 @@ contract Liquidator is ILiquidator, Initializable, ModuleKeysStorage, ImmutableM
);
uniswapRouter.exactInput(param);

// address mAsset = liquidation.mAsset;
// // If the integration contract is connected to a mAsset like mUSD or mBTC
// if (mAsset != address(0)) {
// // 4a. Mint mAsset using purchased bAsset
// uint256 minted = _mint(bAsset, mAsset);

// // 5a. Send to SavingsManager
// address savings = _savingsManager();
// ISavingsManager(savings).depositLiquidation(mAsset, minted);

// emit Liquidated(sellToken, mAsset, minted, bAsset);
// } else {
// // If a feeder pool like alUSD
// // 4b. transfer bAsset directly to the integration contract.
// // this will then increase the boosted savings vault price.
// IERC20 bAssetToken = IERC20(bAsset);
// uint256 bAssetBal = bAssetToken.balanceOf(address(this));
// bAssetToken.transfer(_integration, bAssetBal);

// emit Liquidated(aaveToken, mAsset, bAssetBal, bAsset);
// }
address mAsset = liquidation.mAsset;
// If the integration contract is connected to a mAsset like mUSD or mBTC
if (mAsset != address(0)) {
// 4a. Mint mAsset using purchased bAsset
uint256 minted = _mint(bAsset, mAsset);

// 5a. Send to SavingsManager
address savings = _savingsManager();
ISavingsManager(savings).depositLiquidation(mAsset, minted);

emit Liquidated(sellToken, mAsset, minted, bAsset);
} else {
// If a feeder pool like alUSD
// 4b. transfer bAsset directly to the integration contract.
// this will then increase the boosted savings vault price.
IERC20 bAssetToken = IERC20(bAsset);
uint256 bAssetBal = bAssetToken.balanceOf(address(this));
bAssetToken.transfer(_integration, bAssetBal);

emit Liquidated(sellToken, mAsset, bAssetBal, bAsset);
}
}

/**
Expand Down
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"lint:fix": "yarn pretty-quick --pattern '**/*.*(sol|json)' --staged --verbose",
"lint-ts": "yarn eslint ./test --ext .ts --fix --quiet",
"lint-sol": "solhint 'contracts/**/*.sol'",
"compile-abis": "typechain --target=ethers-v5 --out-dir types/generated --out-dir dist/types/generated \"?(contracts|build)/!(build-info)/**/+([a-zA-Z0-9]).json\"",
"compile-abis": "typechain --target=ethers-v5 --out-dir types/generated \"?(contracts|build)/!(build-info)/**/+([a-zA-Z0-9]).json\"",
"compile-ts": "npx tsc",
"compile": "yarn hardhat compile --force && yarn run compile-abis",
"flatten": "npx sol-merger \"./contracts/**/*.sol\" ./_flat",
Expand All @@ -37,7 +37,8 @@
"test:file:fork": "yarn hardhat --config hardhat-fork.config.ts test",
"test:file": "yarn hardhat test",
"test:file:long": "LONG_TESTS=true yarn hardhat test",
"prepublishOnly": "yarn run compile"
"copy-types": "cp -R types/generated dist/types",
"prepublishOnly": "yarn run compile && yarn run copy-types"
},
"repository": {
"type": "git",
Expand Down
720 changes: 413 additions & 307 deletions test-fork/feeders/feeders-musd-alchemix.spec.ts

Large diffs are not rendered by default.

142 changes: 126 additions & 16 deletions test/masset/liquidator.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
} from "types/generated"
import { increaseTime } from "@utils/time"
import { EncodedPaths, encodeUniswapPath } from "@utils/peripheral/uniswap"
import { assertBNClose } from "@utils/assertions"
import { shouldBehaveLikeModule, IModuleBehaviourContext } from "../shared/Module.behaviour"

describe("Liquidator", () => {
Expand Down Expand Up @@ -65,6 +66,7 @@ describe("Liquidator", () => {

interface Data {
sellTokenBalance: Balance
buyTokenBalance: Balance
savingsManagerBal: BN
liquidation: Liquidation
}
Expand Down Expand Up @@ -144,6 +146,10 @@ describe("Liquidator", () => {
integration: sellBalIntegration,
liquidator: sellBalLiquidator,
},
buyTokenBalance: {
integration: await bAsset.balanceOf(compIntegration.address),
liquidator: await bAsset.balanceOf(liquidator.address),
},
savingsManagerBal,
liquidation,
}
Expand Down Expand Up @@ -478,7 +484,7 @@ describe("Liquidator", () => {
})
it("should update the bAsset successfully", async () => {
const validPath = encodeUniswapPath([compToken.address, DEAD_ADDRESS, bAsset2.address], [3000, 3000])
// update uniswap path, bAsset, tranch amount
// update uniswap path, bAsset, tranche amount
const tx = liquidator
.connect(sa.governor.signer)
.updateBasset(
Expand All @@ -495,6 +501,25 @@ describe("Liquidator", () => {
expect(liquidation.bAsset).eq(bAsset2.address)
expect(liquidation.trancheAmount).eq(simpleToExactAmount(123, 18))
})
it("should update with longer uniswap path", async () => {
const longerPath = encodeUniswapPath([compToken.address, DEAD_ADDRESS, bAsset.address, bAsset2.address], [10000, 3000, 500])
// update uniswap path
const tx = liquidator
.connect(sa.governor.signer)
.updateBasset(
compIntegration.address,
bAsset2.address,
longerPath.encoded,
longerPath.encodedReversed,
simpleToExactAmount(123, 18),
simpleToExactAmount(70, 18),
)
await expect(tx).to.emit(liquidator, "LiquidationModified").withArgs(compIntegration.address)
const liquidation = await liquidator.liquidations(compIntegration.address)
expect(liquidation.sellToken).eq(compToken.address)
expect(liquidation.bAsset).eq(bAsset2.address)
expect(liquidation.trancheAmount).eq(simpleToExactAmount(123, 18))
})
})
describe("removing the liquidation altogether", () => {
it("should fail if liquidation doesn't exist", async () => {
Expand All @@ -511,7 +536,7 @@ describe("Liquidator", () => {
})
})
})
context("triggering a Compound liquidation", () => {
context("triggering a liquidation for a mAsset", () => {
beforeEach(async () => {
await redeployLiquidator()
await liquidator
Expand Down Expand Up @@ -540,22 +565,30 @@ describe("Liquidator", () => {
})
it("should sell everything if the liquidator has less balance than tranche size", async () => {
const s0 = await snapshotData()
await liquidator
.connect(sa.governor.signer)
.updateBasset(
compIntegration.address,
bAsset.address,
uniswapCompBassetPaths.encoded,
uniswapCompBassetPaths.encodedReversed,
simpleToExactAmount(1, 30),
simpleToExactAmount(70, 18),
)
// set tranche size to 1e30
await liquidator.triggerLiquidation(compIntegration.address)
expect(s0.sellTokenBalance.integration, "integration COMP bal before").to.eq(simpleToExactAmount(10))
expect(s0.sellTokenBalance.liquidator, "liquidator COMP bal before").to.eq(0)
await liquidator.connect(sa.governor.signer).updateBasset(
compIntegration.address,
bAsset.address,
uniswapCompBassetPaths.encoded,
uniswapCompBassetPaths.encodedReversed,
simpleToExactAmount(1, 30), // set tranche size to 1e30
simpleToExactAmount(70, 18),
)

const tx = await liquidator.triggerLiquidation(compIntegration.address)

// 10 COMP liquidated at 440 COMP/USD with 0.3% fee
// Swap bAsset output = 10 * 440 * (100 - 0.3) / 100 = 4,386.8
// 4,386.8 bAsset is then minted for mUSD which costs 2%
// mUSD in Savings = 4,386.8 * (100 - 2) / 100 = 4,299.064
const mAssetsExpected = simpleToExactAmount(4299064, 15)
await expect(tx).to.emit(liquidator, "Liquidated").withArgs(compToken.address, mUSD.address, mAssetsExpected, bAsset.address)

const s1 = await snapshotData()
// 10 COMP liquidated for > 1000 mUSD
expect(s1.savingsManagerBal.sub(s0.savingsManagerBal)).gt(simpleToExactAmount(1000, 18))
expect(s1.sellTokenBalance.integration, "integration COMP bal after").to.eq(0)
expect(s1.sellTokenBalance.liquidator, "liquidator COMP bal after").to.eq(0)
expect(s1.savingsManagerBal, "savings manager COMP bal after").to.eq(s0.savingsManagerBal.add(mAssetsExpected))

await increaseTime(ONE_WEEK.add(1))
await expect(liquidator.triggerLiquidation(compIntegration.address)).to.be.revertedWith("No sell tokens to liquidate")
Expand All @@ -581,6 +614,83 @@ describe("Liquidator", () => {
await liquidator.triggerLiquidation(compIntegration.address)
})
})
context("triggering a liquidation for Feeder Pool", () => {
beforeEach(async () => {
await redeployLiquidator()
await liquidator.connect(sa.governor.signer).createLiquidation(
compIntegration.address,
compToken.address,
bAsset.address,
uniswapCompBassetPaths.encoded,
uniswapCompBassetPaths.encodedReversed,
simpleToExactAmount(10000, 18),
simpleToExactAmount(70, 18),
ZERO_ADDRESS, // no mAsset. This is a Feeder Pool integration
false,
)
await compIntegration.connect(sa.governor.signer).approveRewardToken()
})
context("send purchased asset to integration contract", () => {
it("should sell all COMP", async () => {
const s0 = await snapshotData()
expect(s0.sellTokenBalance.integration, "integration COMP bal before").to.eq(simpleToExactAmount(10))
expect(s0.sellTokenBalance.liquidator, "liquidator COMP bal before").to.eq(0)
expect(s0.buyTokenBalance.integration, "integration bAsset bal before").to.eq(0)
expect(s0.buyTokenBalance.liquidator, "liquidator bAsset bal before").to.eq(0)

const tx = await liquidator.triggerLiquidation(compIntegration.address)

// 10 COMP liquidated at 440 COMP/USD with 0.3% fee
// Swap bAsset output = 10 * 440 * (100 - 0.3) / 100 = 4,386.8
// 4,386.8 bAsset is then minted for mUSD which costs 2%
const purchasedBassetsExpected = simpleToExactAmount(43868, 17)

await expect(tx)
.to.emit(liquidator, "Liquidated")
.withArgs(compToken.address, ZERO_ADDRESS, purchasedBassetsExpected, bAsset.address)

const s1 = await snapshotData()
expect(s1.sellTokenBalance.integration, "integration COMP bal after").to.eq(0)
expect(s1.sellTokenBalance.liquidator, "liquidator COMP bal after").to.eq(0)
expect(s1.buyTokenBalance.integration, "integration bAsset bal after").to.eq(purchasedBassetsExpected)
expect(s1.buyTokenBalance.liquidator, "liquidator bAsset bal after").to.eq(0)
})
it("should partially sell COMP", async () => {
await liquidator
.connect(sa.governor.signer)
.updateBasset(
compIntegration.address,
bAsset.address,
uniswapCompBassetPaths.encoded,
uniswapCompBassetPaths.encodedReversed,
simpleToExactAmount(1000, 18),
simpleToExactAmount(70, 18),
)

const s0 = await snapshotData()
expect(s0.sellTokenBalance.integration, "integration COMP bal before").to.eq(simpleToExactAmount(10))
expect(s0.sellTokenBalance.liquidator, "liquidator COMP bal before").to.eq(0)
expect(s0.buyTokenBalance.integration, "integration bAsset bal before").to.eq(0)
expect(s0.buyTokenBalance.liquidator, "liquidator bAsset bal before").to.eq(0)

const tx = await liquidator.triggerLiquidation(compIntegration.address)

// purchased bAssets close to 1000 but not quite do to Uniswap calcs
const purchasedBassetsExpected = BN.from("999999999999999999926")
assertBNClose(purchasedBassetsExpected, simpleToExactAmount(1000, 18), 100)

await expect(tx)
.to.emit(liquidator, "Liquidated")
.withArgs(compToken.address, ZERO_ADDRESS, purchasedBassetsExpected, bAsset.address)

const s1 = await snapshotData()
expect(s1.sellTokenBalance.integration, "integration COMP bal after").to.eq(0)
expect(s1.sellTokenBalance.liquidator, "liquidator COMP bal after").to.gt(0)
expect(s1.buyTokenBalance.integration, "integration bAsset bal after").to.eq(purchasedBassetsExpected)
expect(s1.buyTokenBalance.liquidator, "liquidator bAsset bal after").to.eq(0)
})
})
})
context("Aave claim rewards", () => {
before(async () => {
await redeployLiquidator()
Expand Down
4 changes: 2 additions & 2 deletions test/masset/peripheral/aavev2.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ import {
MockERC20,
MockATokenV2,
} from "types/generated"
import { BassetIntegrationDetails } from "types"
import { BassetIntegrationDetails , Account } from "types"
import { shouldBehaveLikeModule, IModuleBehaviourContext } from "../../shared/Module.behaviour"
import { Account } from "types"


describe("AaveIntegration", async () => {
let sa: StandardAccounts
Expand Down
4 changes: 2 additions & 2 deletions test/masset/peripheral/compound.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ import { ethers } from "hardhat"
import { CompoundIntegration } from "types/generated/CompoundIntegration"
import { expect } from "chai"
import { CompoundIntegration__factory } from "types/generated/factories/CompoundIntegration__factory"
import { BassetIntegrationDetails } from "types"
import { BassetIntegrationDetails , Account } from "types"
import { assertBNClose, assertBNSlightlyGT, assertBNSlightlyGTPercent } from "@utils/assertions"
import { shouldBehaveLikeModule, IModuleBehaviourContext } from "../../shared/Module.behaviour"
import { Account } from "types"


const convertUnderlyingToCToken = async (cToken: MockCToken, underlyingAmount: BN): Promise<BN> => {
const exchangeRate = await cToken.exchangeRateStored()
Expand Down
2 changes: 1 addition & 1 deletion test/rewards/boosted-dual-vault.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ import {
BoostDirector__factory,
BoostDirector,
} from "types/generated"
import { Account } from "types"
import {
shouldBehaveLikeDistributionRecipient,
IRewardsDistributionRecipientContext,
} from "../shared/RewardsDistributionRecipient.behaviour"
import { Account } from "types"

interface StakingBalance {
raw: BN
Expand Down
2 changes: 1 addition & 1 deletion test/rewards/boosted-vault.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,11 @@ import {
MockBoostedVault,
MockBoostedVault__factory,
} from "types/generated"
import { Account } from "types"
import {
shouldBehaveLikeDistributionRecipient,
IRewardsDistributionRecipientContext,
} from "../shared/RewardsDistributionRecipient.behaviour"
import { Account } from "types"

interface StakingBalance {
raw: BN
Expand Down
2 changes: 1 addition & 1 deletion test/savings/savings-manager.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ import {
MockERC20,
MockRevenueRecipient__factory,
} from "types/generated"
import { shouldBehaveLikePausableModule, IPausableModuleBehaviourContext } from "../shared/PausableModule.behaviour"
import { Account } from "types"
import { shouldBehaveLikePausableModule, IPausableModuleBehaviourContext } from "../shared/PausableModule.behaviour"

describe("SavingsManager", async () => {
const TEN = BN.from(10)
Expand Down

0 comments on commit 70a3d98

Please sign in to comment.