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 6625f4a
Show file tree
Hide file tree
Showing 6 changed files with 38 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ contract HifiFlashUniswapV2Underlying is IHifiFlashUniswapV2Underlying {
/// @inheritdoc IHifiFlashUniswapV2Underlying
IBalanceSheetV1 public override balanceSheet;

/// @inheritdoc IHifiFlashUniswapV2Underlying
address public override bot;

/// @inheritdoc IHifiFlashUniswapV2Underlying
address public override uniV2Factory;

Expand All @@ -43,10 +46,12 @@ contract HifiFlashUniswapV2Underlying is IHifiFlashUniswapV2Underlying {
/// CONSTRUCTOR ///
constructor(
IBalanceSheetV1 balanceSheet_,
address bot_,
address uniV2Factory_,
bytes32 uniV2PairInitCodeHash_
) {
balanceSheet = IBalanceSheetV1(balanceSheet_);
bot = bot_;
uniV2Factory = uniV2Factory_;
uniV2PairInitCodeHash = uniV2PairInitCodeHash_;
}
Expand Down Expand Up @@ -102,6 +107,7 @@ contract HifiFlashUniswapV2Underlying is IHifiFlashUniswapV2Underlying {
address borrower;
IErc20 collateral;
uint256 mintedHTokenAmount;
uint256 overshootCollateralAmount;
uint256 repayCollateralAmount;
uint256 seizedCollateralAmount;
address swapToken;
Expand Down Expand Up @@ -151,6 +157,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(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 @@ -27,6 +27,9 @@ interface IHifiFlashUniswapV2Underlying is IUniswapV2Callee {
/// @notice The unique BalanceSheet contract associated with this contract.
function balanceSheet() external view returns (IBalanceSheetV1);

/// @notice The unique bot wallet address associated with this contract.
function bot() external view returns (address);

/// @notice Compares the token addresses to find the collateral address and the underlying amount.
/// @dev See this StackExchange post: https://ethereum.stackexchange.com/q/102670/24693.
///
Expand Down
Original file line number Diff line number Diff line change
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
2 changes: 2 additions & 0 deletions packages/flash-swap/test/shared/fixtures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,10 +102,12 @@ export async function integrationFixture(signers: Signer[]): Promise<Integration
])
);

const bot: Signer = signers[4];
const hifiFlashUniswapV2UnderlyingArtifact: Artifact = await artifacts.readArtifact("HifiFlashUniswapV2Underlying");
const hifiFlashUniswapV2Underlying: HifiFlashUniswapV2Underlying = <HifiFlashUniswapV2Underlying>(
await waffle.deployContract(deployer, hifiFlashUniswapV2UnderlyingArtifact, [
balanceSheet.address,
await bot.getAddress(),
uniswapV2Factory.address,
uniV2PairInitCodeHash,
])
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 6625f4a

Please sign in to comment.