Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/range order collateral send back #713

Open
wants to merge 2 commits into
base: production
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,15 @@ contract UniswapV3RangeOrderReactor is IUniswapV3MintCallback, IHedgingReactor,
);

event Withdraw(
uint256 amount
uint256 amount,
address receiver
);

event UniswapAssetsCollected(
uint256 burn0,
uint256 burn1,
uint256 fee0,
uint256 fee1
);

constructor(
Expand Down Expand Up @@ -246,6 +254,8 @@ contract UniswapV3RangeOrderReactor is IUniswapV3MintCallback, IHedgingReactor,
revert CustomErrors.RangeOrderNotFilled();
}
}
// after withdrawing liquidity send back collateral to parent pool
_transferCollateralBalanceToParentPool();
}

/// @notice compute total underlying holdings of the vault token supply
Expand Down Expand Up @@ -317,12 +327,12 @@ contract UniswapV3RangeOrderReactor is IUniswapV3MintCallback, IHedgingReactor,
}
if (_amount <= balance) {
SafeTransferLib.safeTransfer(ERC20(collateralAsset), msg.sender, _amount);
emit Withdraw(_amount);
emit Withdraw(_amount, msg.sender);
// return in collateral format
return _amount;
} else {
SafeTransferLib.safeTransfer(ERC20(collateralAsset), msg.sender, balance);
emit Withdraw(balance);
emit Withdraw(balance, msg.sender);
// return in collateral format
return balance;
}
Expand Down Expand Up @@ -633,6 +643,19 @@ contract UniswapV3RangeOrderReactor is IUniswapV3MintCallback, IHedgingReactor,
return currentPosition.activeLowerTick != currentPosition.activeUpperTick;
}

/**
* @notice use to transfer collateral balance to the parent liquidity pool
*/

function _transferCollateralBalanceToParentPool() internal {
uint256 balance = ERC20(collateralAsset).balanceOf(address(this));
if (balance == 0) {
return;
}
SafeTransferLib.safeTransfer(ERC20(collateralAsset), parentLiquidityPool, balance);
emit Withdraw(balance, parentLiquidityPool);
}

/**
* @notice transfer tokens from the parent liquidity pool
* @param token the address of the token to transfer
Expand Down Expand Up @@ -696,6 +719,7 @@ contract UniswapV3RangeOrderReactor is IUniswapV3MintCallback, IHedgingReactor,
fee1 = collect1 - burn1;
// mark no current position
delete currentPosition;
emit UniswapAssetsCollected(burn0, burn1, fee0, fee1);
}

/// @notice Withdraws all liquidity from a range order and collection outstanding fees
Expand Down
26 changes: 21 additions & 5 deletions packages/contracts/test/UniswapV3RangeOrderReactor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,6 @@ import {
import { WETH } from "../types/WETH"
import { LiquidityPool } from "../types/LiquidityPool"
import { arbitrum as addresses } from "../contracts.json"
import { sign } from "crypto"
import { min } from "bn.js"

enum Direction {
ABOVE = 0,
Expand Down Expand Up @@ -672,7 +670,9 @@ describe("UniswapV3RangeOrderReactor", () => {
it("Reverts when trying to fulfill a range order that is partially filled", async () => {
const reactorDelta = Number(fromWei(await liquidityPoolDummy.getDelta()))
let poolInfo = await getPoolInfo(uniswapUSDCWETHPool)
const balancesBefore = await uniswapV3RangeOrderReactor.getUnderlyingBalances()
// transfer usdc to reactor to test it's not removed
await usdcContract.connect(signers[1]).transfer(uniswapV3RangeOrderReactor.address, toUSDC("100"))
const usdcBalance = await usdcContract.balanceOf(uniswapV3RangeOrderReactor.address)
const weth_usdc_price_before = poolInfo.token1Price.toFixed()
const amountToSwap = toUSDC("6000000")
await usdcContract.connect(signers[1]).approve(uniswapRouter.address, amountToSwap)
Expand Down Expand Up @@ -700,13 +700,16 @@ describe("UniswapV3RangeOrderReactor", () => {
const reactorDeltaAfter = Number(fromWei(await liquidityPoolDummy.getDelta()))

const fulfillAttempt = uniswapV3RangeOrderReactor.fulfillActiveRangeOrder()
const usdcBalanceAfter = await usdcContract.balanceOf(uniswapV3RangeOrderReactor.address)
expect(fulfillAttempt).to.be.revertedWithCustomError(
uniswapV3RangeOrderReactor,
"RangeOrderNotFilled"
)
expect(tick).to.be.gt(activeLowerTick)
expect(tickAfter).to.be.lt(activeUpperTick)
expect(reactorDeltaAfter).to.be.lt(reactorDelta)
// no collateral should be transfered when a fulfill fails
expect(usdcBalanceAfter).to.be.eq(usdcBalance)
})

it("Allows for rehedging after range order is partially filled", async () => {
Expand All @@ -722,7 +725,6 @@ describe("UniswapV3RangeOrderReactor", () => {
it("It fulfills when rehedge moves through range", async () => {
const reactorDelta = Number(fromWei(await liquidityPoolDummy.getDelta()))
let poolInfo = await getPoolInfo(uniswapUSDCWETHPool)
const balancesBefore = await uniswapV3RangeOrderReactor.getUnderlyingBalances()
const weth_usdc_price_before = poolInfo.token1Price.toFixed()
const amountToSwap = toUSDC("10000000")
await usdcContract.connect(signers[1]).approve(uniswapRouter.address, amountToSwap)
Expand All @@ -747,9 +749,13 @@ describe("UniswapV3RangeOrderReactor", () => {
const { tick: tickAfter } = await uniswapUSDCWETHPool.slot0()
const { activeLowerTick, activeUpperTick } = await uniswapV3RangeOrderReactor.currentPosition()
const balances = await uniswapV3RangeOrderReactor.getUnderlyingBalances()
const usdcBalanceBefore = balances.amount0Current
const reactorDeltaAfter = Number(fromWei(await liquidityPoolDummy.getDelta()))
const LpUsdcBalanceBefore = await usdcContract.balanceOf(liquidityPoolDummy.address)

const fulfillAttempt = await uniswapV3RangeOrderReactor.fulfillActiveRangeOrder()
const balancesAfer = await uniswapV3RangeOrderReactor.getUnderlyingBalances()
const usdcBalanceAfter = balancesAfer.amount0Current
const receipt = await fulfillAttempt.wait()
const [collectEvent] = getMatchingEvents(receipt, UNISWAP_POOL_COLLECT)
const { activeLowerTick: activeLowerAfter, activeUpperTick: activeUpperAfter } =
Expand All @@ -759,18 +765,26 @@ describe("UniswapV3RangeOrderReactor", () => {
// USDC amount
const amountOut = Number(fromUSDC(collectEvent.amount0))
const fillPrice = amountOut / 0.3
const LpUsdcBalanceAfter = await usdcContract.balanceOf(liquidityPoolDummy.address)
const lpUsdcBalanceAfterExpectation = LpUsdcBalanceBefore.add(usdcBalanceBefore)
// Amount of delta hedged
expect(deltaDifference).to.eq(0.3)
expect(fillPrice).to.be.gt(average)
expect(tickAfter).to.be.lt(activeLowerTick)
expect(activeLowerAfter).to.be.eq(0)
expect(activeUpperAfter).to.be.eq(0)
// collateral should be transfered when a fulfill succeeds
expect(usdcBalanceAfter).to.be.lt(usdcBalanceBefore)
expect(LpUsdcBalanceAfter).to.be.eq(lpUsdcBalanceAfterExpectation)
})

it("withdraws partial excess USDC to liquidity pool", async () => {
const withdrawAmount = toUSDC("1000")
await usdcContract
.connect(signers[1])
.transfer(uniswapV3RangeOrderReactor.address, withdrawAmount)
const usdcBalance = await usdcContract.balanceOf(uniswapV3RangeOrderReactor.address)
const usdcBalanceLp = await usdcContract.balanceOf(liquidityPoolDummy.address)
const withdrawAmount = toUSDC("1000")
const withdrawTx = await liquidityPoolDummy.withdraw(withdrawAmount)
const receipt = await withdrawTx.wait()
const usdcBalanceAfter = await usdcContract.balanceOf(uniswapV3RangeOrderReactor.address)
Expand All @@ -780,6 +794,8 @@ describe("UniswapV3RangeOrderReactor", () => {
})

it("Allows the guardian to recover an erc20 token directly", async () => {
const initAmount = toUSDC("1000")
await usdcContract.connect(signers[1]).transfer(uniswapV3RangeOrderReactor.address, initAmount)
const usdcBalance = await usdcContract.balanceOf(uniswapV3RangeOrderReactor.address)
// ensure there is a balance to recover for the test to be valid
expect(usdcBalance).to.be.gt(0)
Expand Down
Loading