Skip to content

Commit

Permalink
feat(flash-swap): repay USDC liquidation 0.3% Uniswap fee from bot wa…
Browse files Browse the repository at this point in the history
…llet balance
  • Loading branch information
scorpion9979 committed Nov 3, 2021
1 parent 4e3f8cb commit 59db094
Show file tree
Hide file tree
Showing 4 changed files with 32 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -99,9 +99,11 @@ contract HifiFlashUniswapV2Underlying is IHifiFlashUniswapV2Underlying {

struct UniswapV2CallLocalVars {
IHToken bond;
address bot;
address borrower;
IErc20 collateral;
uint256 mintedHTokenAmount;
uint256 overshootCollateralAmount;
uint256 repayCollateralAmount;
uint256 seizedCollateralAmount;
address swapToken;
Expand All @@ -119,7 +121,7 @@ contract HifiFlashUniswapV2Underlying is IHifiFlashUniswapV2Underlying {
UniswapV2CallLocalVars memory vars;

// Unpack the ABI encoded data passed by the UniswapV2Pair contract.
(vars.borrower, vars.bond) = abi.decode(data, (address, IHToken));
(vars.borrower, vars.bond, vars.bot) = abi.decode(data, (address, IHToken, address));

// Figure out which token is the collateral and which token is the underlying.
vars.underlying = vars.bond.underlying();
Expand Down Expand Up @@ -151,6 +153,14 @@ contract HifiFlashUniswapV2Underlying is IHifiFlashUniswapV2Underlying {
// Calculate the amount of collateral required to repay.
vars.repayCollateralAmount = getRepayCollateralAmount(vars.underlyingAmount);

// The bot wallet compensates for any overshoot of collateral repay amount above seized amount.
if (vars.repayCollateralAmount > vars.seizedCollateralAmount) {
unchecked {
vars.overshootCollateralAmount = vars.repayCollateralAmount - vars.seizedCollateralAmount;
}
vars.collateral.safeTransferFrom(vars.bot, address(this), vars.overshootCollateralAmount);
}

// Pay back the loan.
vars.collateral.safeTransfer(msg.sender, vars.repayCollateralAmount);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ async function bumpPoolReserves(this: Mocha.Context, wbtcAmount: BigNumber, usdc
}

function encodeCallData(this: Mocha.Context): string {
const types = ["address", "address"];
const values = [this.signers.borrower.address, this.contracts.hToken.address];
const types = ["address", "address", "address"];
const values = [this.signers.borrower.address, this.contracts.hToken.address, this.signers.bot.address];
const data: string = defaultAbiCoder.encode(types, values);
return data;
}
Expand Down Expand Up @@ -226,11 +226,11 @@ export function shouldBehaveLikeUniswapV2Call(): void {
.connect(this.signers.borrower)
.approve(this.contracts.balanceSheet.address, wbtcDepositAmount);

// Minst USDC and send it to flash-swap to be used for Uniswap V2 fee repay
await this.contracts.usdc.__godMode_mint(
this.contracts.hifiFlashUniswapV2Underlying.address,
usdcRepayFeeAmount,
);
// Minst USDC to the bot wallet and approve the flash swap contract to spend it
await this.contracts.usdc.__godMode_mint(this.signers.bot.address, usdcRepayFeeAmount);
await this.contracts.usdc
.connect(this.signers.bot)
.approve(this.contracts.hifiFlashUniswapV2Underlying.address, usdcRepayFeeAmount);

// Deposit the USDC in the BalanceSheet.
await this.contracts.balanceSheet
Expand Down Expand Up @@ -271,14 +271,14 @@ export function shouldBehaveLikeUniswapV2Call(): void {
it("flash swaps USDC making no USDC profit and spending allocated USDC to pay swap fee", async function () {
const to: string = this.contracts.hifiFlashUniswapV2Underlying.address;
const preUsdcBalanceAccount = await this.contracts.usdc.balanceOf(this.signers.liquidator.address);
const preUsdcBalanceContract = await this.contracts.usdc.balanceOf(to);
const preUsdcBalanceBot = await this.contracts.usdc.balanceOf(this.signers.bot.address);
await this.contracts.uniswapV2Pair
.connect(this.signers.liquidator)
.swap(token0Amount, token1Amount, to, data);
const newUsdcBalanceAccount = await this.contracts.usdc.balanceOf(this.signers.liquidator.address);
const newUsdcBalanceContract = await this.contracts.usdc.balanceOf(to);
const newUsdcBalanceBot = await this.contracts.usdc.balanceOf(this.signers.bot.address);
expect(newUsdcBalanceAccount).to.equal(preUsdcBalanceAccount);
expect(preUsdcBalanceContract.sub(newUsdcBalanceContract)).to.equal(usdcRepayFeeAmount);
expect(preUsdcBalanceBot.sub(newUsdcBalanceBot)).to.equal(usdcRepayFeeAmount);
});
});

Expand Down Expand Up @@ -306,14 +306,14 @@ export function shouldBehaveLikeUniswapV2Call(): void {
it("flash swaps USDC making no USDC profit and spending allocated USDC to pay swap fee", async function () {
const to: string = this.contracts.hifiFlashUniswapV2Underlying.address;
const preUsdcBalanceAccount = await this.contracts.usdc.balanceOf(this.signers.liquidator.address);
const preUsdcBalanceContract = await this.contracts.usdc.balanceOf(to);
const preUsdcBalanceBot = await this.contracts.usdc.balanceOf(this.signers.bot.address);
await this.contracts.uniswapV2Pair
.connect(this.signers.liquidator)
.swap(token0Amount, token1Amount, to, data);
const newUsdcBalanceAccount = await this.contracts.usdc.balanceOf(this.signers.liquidator.address);
const newUsdcBalanceContract = await this.contracts.usdc.balanceOf(to);
const newUsdcBalanceBot = await this.contracts.usdc.balanceOf(this.signers.bot.address);
expect(newUsdcBalanceAccount).to.equal(preUsdcBalanceAccount);
expect(preUsdcBalanceContract.sub(newUsdcBalanceContract)).to.equal(usdcRepayFeeAmount);
expect(preUsdcBalanceBot.sub(newUsdcBalanceBot)).to.equal(usdcRepayFeeAmount);
});
});

Expand Down Expand Up @@ -360,29 +360,29 @@ export function shouldBehaveLikeUniswapV2Call(): void {
it("flash swaps USDC making no USDC profit and spending allocated USDC to pay swap fee", async function () {
const to: string = this.contracts.hifiFlashUniswapV2Underlying.address;
const preUsdcBalanceAccount = await this.contracts.usdc.balanceOf(this.signers.liquidator.address);
const preUsdcBalanceContract = await this.contracts.usdc.balanceOf(to);
const preUsdcBalanceBot = await this.contracts.usdc.balanceOf(this.signers.bot.address);
await this.contracts.uniswapV2Pair
.connect(this.signers.liquidator)
.swap(localToken0Amount, localToken1Amount, to, data);
const newUsdcBalanceAccount = await this.contracts.usdc.balanceOf(this.signers.liquidator.address);
const newUsdcBalanceContract = await this.contracts.usdc.balanceOf(to);
const newUsdcBalanceBot = await this.contracts.usdc.balanceOf(this.signers.bot.address);
expect(newUsdcBalanceAccount).to.equal(preUsdcBalanceAccount);
expect(preUsdcBalanceContract.sub(newUsdcBalanceContract)).to.equal(usdcRepayFeeAmount);
expect(preUsdcBalanceBot.sub(newUsdcBalanceBot)).to.equal(usdcRepayFeeAmount);
});
});

context("initial order of tokens in the pair", function () {
it("flash swaps USDC making no USDC profit and spending allocated USDC to pay swap fee", async function () {
const to: string = this.contracts.hifiFlashUniswapV2Underlying.address;
const preUsdcBalanceAccount = await this.contracts.usdc.balanceOf(this.signers.liquidator.address);
const preUsdcBalanceContract = await this.contracts.usdc.balanceOf(to);
const preUsdcBalanceBot = await this.contracts.usdc.balanceOf(this.signers.bot.address);
await this.contracts.uniswapV2Pair
.connect(this.signers.liquidator)
.swap(token0Amount, token1Amount, to, data);
const newUsdcBalanceAccount = await this.contracts.usdc.balanceOf(this.signers.liquidator.address);
const newUsdcBalanceContract = await this.contracts.usdc.balanceOf(to);
const newUsdcBalanceBot = await this.contracts.usdc.balanceOf(this.signers.bot.address);
expect(newUsdcBalanceAccount).to.equal(preUsdcBalanceAccount);
expect(preUsdcBalanceContract.sub(newUsdcBalanceContract)).to.equal(usdcRepayFeeAmount);
expect(preUsdcBalanceBot.sub(newUsdcBalanceBot)).to.equal(usdcRepayFeeAmount);
});

it("emits a FlashLiquidateBorrow event", async function () {
Expand Down
1 change: 1 addition & 0 deletions packages/flash-swap/test/shared/contexts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export function baseContext(description: string, hooks: () => void): void {
this.signers.borrower = signers[1];
this.signers.liquidator = signers[2];
this.signers.raider = signers[3];
this.signers.bot = signers[4];

// Get rid of this when https://github.com/nomiclabs/hardhat/issues/849 gets fixed.
this.loadFixture = createFixtureLoader(signers as Signer[] as Wallet[]);
Expand Down
1 change: 1 addition & 0 deletions packages/flash-swap/test/shared/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,5 @@ export interface Signers {
borrower: SignerWithAddress;
liquidator: SignerWithAddress;
raider: SignerWithAddress;
bot: SignerWithAddress;
}

0 comments on commit 59db094

Please sign in to comment.