Skip to content

Commit

Permalink
Merge pull request #149 from SweeprFi/grow-positions
Browse files Browse the repository at this point in the history
adds single-side liquidity from SWEEP
  • Loading branch information
maxcoto authored Jan 17, 2024
2 parents 3e98bdf + f07a054 commit 988961c
Show file tree
Hide file tree
Showing 6 changed files with 126 additions and 64 deletions.
2 changes: 1 addition & 1 deletion contracts/Balancer/BalancerMarketMaker.sol
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ contract BalancerMarketMaker is Stabilizer {
_addLiquidity(usdxAmount, sweepAmount);
TransferHelper.safeTransfer(address(sweep), msg.sender, sweepAmount);

if (getEquityRatio() < minEquityRatio) revert EquityRatioExcessed();
_checkRatio();
emit SweepPurchased(usdxAmount);
}

Expand Down
2 changes: 1 addition & 1 deletion contracts/Balancer/CurveMarketMaker.sol
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ contract CurveMarketMaker is Stabilizer {
_addLiquidity(usdxAmount, sweepAmount);
TransferHelper.safeTransfer(address(sweep), msg.sender, sweepAmount);

if (getEquityRatio() < minEquityRatio) revert EquityRatioExcessed();
_checkRatio();
emit SweepPurchased(usdxAmount);
}

Expand Down
2 changes: 1 addition & 1 deletion contracts/Balancer/PancakeMarketMaker.sol
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ contract PancakeMarketMaker is IERC721Receiver, Stabilizer {
_addLiquidity(usdxAmount, sweepAmount, usdxMinIn, sweepMinIn);

TransferHelper.safeTransfer(address(sweep), msg.sender, sweepAmount);
if (getEquityRatio() < minEquityRatio) revert EquityRatioExcessed();
_checkRatio();
emit SweepPurchased(usdxAmount);
}

Expand Down
123 changes: 75 additions & 48 deletions contracts/Balancer/UniswapMarketMaker.sol
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ contract UniswapMarketMaker is IERC721Receiver, Stabilizer {
int24 public constant TICK_SPACE = 10; // TICK_SPACE are 10, 60, 200
uint256 private constant PRECISION = 1e6;
uint256[] public positionIds;
uint256 public growPosition;
uint32 public slippage;

// Errors
Expand Down Expand Up @@ -151,7 +152,7 @@ contract UniswapMarketMaker is IERC721Receiver, Stabilizer {
_addLiquidity(usdxAmount, sweepAmount, usdxMinIn, sweepMinIn);
TransferHelper.safeTransfer(address(sweep), msg.sender, sweepAmount);

if (getEquityRatio() < minEquityRatio) revert EquityRatioExcessed();
_checkRatio();
emit SweepPurchased(usdxAmount);
}

Expand Down Expand Up @@ -258,60 +259,25 @@ contract UniswapMarketMaker is IERC721Receiver, Stabilizer {
}

function addSingleLiquidity(uint256 usdxAmount, uint256 tickSpread) external onlyBorrower nonReentrant {
address self = address(this);
address poolAddress = IAMM(amm()).pool();
uint8 decimals = sweep.decimals();
uint256 targetPrice = sweep.targetPrice();
uint256 minPrice = ((PRECISION - tickSpread) * targetPrice) / PRECISION;

uint256 sweepAmount;
TransferHelper.safeTransferFrom(address(usdx), msg.sender, self, usdxAmount);
TransferHelper.safeTransferFrom(address(usdx), msg.sender, address(this), usdxAmount);
TransferHelper.safeApprove(address(usdx), address(nonfungiblePositionManager), usdxAmount);
positionIds.push(_addSingleLiquidity(tickSpread, usdxAmount, 0));
}

int24 tickSpacing = IUniswapV3Pool(poolAddress).tickSpacing();
int24 minTick = liquidityHelper.getTickFromPrice(minPrice, decimals, tickSpacing, flag);
int24 maxTick = liquidityHelper.getTickFromPrice(targetPrice, decimals, tickSpacing, flag);
(minTick, maxTick) = minTick < maxTick ? (minTick, maxTick) : (maxTick, minTick);

(uint256 amount0Mint, uint256 amount1Mint) = flag
? (usdxAmount, sweepAmount)
: (sweepAmount, usdxAmount);
function lpGrow(uint256 sweepAmount, uint256 tickSpread) external onlyBorrower nonReentrant {
if(growPosition > 0) _removePosition(growPosition);
uint256 sweepBalance = sweep.balanceOf(address(this));
if(sweepAmount > sweepBalance) _borrow(sweepAmount - sweepBalance);

(uint256 _tokenId,,,) = nonfungiblePositionManager.mint(
INonfungiblePositionManager.MintParams({
token0: token0,
token1: token1,
fee: IUniswapV3Pool(poolAddress).fee(),
tickLower: minTick,
tickUpper: maxTick,
amount0Desired: amount0Mint,
amount1Desired: amount1Mint,
amount0Min: amount0Mint,
amount1Min: amount1Mint,
recipient: self,
deadline: block.timestamp
})
);
TransferHelper.safeApprove(address(sweep), address(nonfungiblePositionManager), sweepAmount);

positionIds.push(_tokenId);
emit LiquidityAdded(usdxAmount, sweepAmount);
growPosition = _addSingleLiquidity(tickSpread, 0, sweepAmount);
_checkRatio();
}

function removePosition(uint256 positionId) external onlyBorrower nonReentrant {
(,,,,,,,uint128 _liquidity,,,,) = nonfungiblePositionManager.positions(positionId);
nonfungiblePositionManager.decreaseLiquidity(
INonfungiblePositionManager.DecreaseLiquidityParams({
tokenId: positionId,
liquidity: _liquidity,
amount0Min: 0,
amount1Min: 0,
deadline: block.timestamp
})
);
_collect(positionId);

nonfungiblePositionManager.burn(positionId);
function removePosition(uint256 positionId) external onlyBorrower nonReentrant {
_removePosition(positionId);
_removeId(positionId);
}

function setSlippage(uint32 newSlippage) external nonReentrant onlyBorrower {
Expand Down Expand Up @@ -348,6 +314,47 @@ contract UniswapMarketMaker is IERC721Receiver, Stabilizer {
emit LiquidityAdded(usdxAmount, sweepAmount);
}

function _addSingleLiquidity(
uint256 tickSpread,
uint256 usdxAmount,
uint256 sweepAmount
) internal returns (uint256) {
address poolAddress = IAMM(amm()).pool();
uint8 decimals = sweep.decimals();
uint256 targetPrice = sweep.targetPrice();
uint256 maxPrice = ((PRECISION + tickSpread) * targetPrice) / PRECISION;

int24 tickSpacing = IUniswapV3Pool(poolAddress).tickSpacing();
uint24 fee = IUniswapV3Pool(poolAddress).fee();
int24 minTick = liquidityHelper.getTickFromPrice(targetPrice, decimals, tickSpacing, flag);
int24 maxTick = liquidityHelper.getTickFromPrice(maxPrice, decimals, tickSpacing, flag);
// (minTick, maxTick) = minTick < maxTick ? (minTick, maxTick) : (maxTick, minTick);

(uint256 amount0Mint, uint256 amount1Mint) = flag
? (usdxAmount, sweepAmount) : (sweepAmount, usdxAmount);

uint256 amount0Min = OvnMath.subBasisPoints(amount0Mint, slippage);
uint256 amount1Min = OvnMath.subBasisPoints(amount1Mint, slippage);

(uint256 _tokenId,,,) = nonfungiblePositionManager.mint(
INonfungiblePositionManager.MintParams({
token0: token0,
token1: token1,
fee: fee,
tickLower: minTick,
tickUpper: maxTick,
amount0Desired: amount0Mint,
amount1Desired: amount1Mint,
amount0Min: amount0Min,
amount1Min: amount1Min,
recipient: address(this),
deadline: block.timestamp
})
);

return _tokenId;
}

function _collect(uint256 id) internal {
(uint256 amount0, uint256 amount1) = nonfungiblePositionManager.collect(
INonfungiblePositionManager.CollectParams({
Expand Down Expand Up @@ -379,6 +386,26 @@ contract UniswapMarketMaker is IERC721Receiver, Stabilizer {
}

function _removePosition(uint256 positionId) internal {
(,,,,,,,uint128 _liquidity,,,,) = nonfungiblePositionManager.positions(positionId);
nonfungiblePositionManager.decreaseLiquidity(
INonfungiblePositionManager.DecreaseLiquidityParams({
tokenId: positionId,
liquidity: _liquidity,
amount0Min: 0,
amount1Min: 0,
deadline: block.timestamp
})
);
_collect(positionId);
nonfungiblePositionManager.burn(positionId);
}

function _removeId(uint256 positionId) internal {
if(positionId == growPosition) {
growPosition = 0;
return;
}

uint256 len = positionIds.length;
for (uint256 i = 0; i < len; ) {
if(positionIds[i] == positionId) {
Expand Down
18 changes: 7 additions & 11 deletions contracts/Stabilizer/Stabilizer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -377,10 +377,7 @@ contract Stabilizer is Owned, Pausable, ReentrancyGuard {
nonReentrant
{
_borrow(sweepAmount);

if (getEquityRatio() < minEquityRatio) {
revert EquityRatioExcessed();
}
_checkRatio();
}

/**
Expand Down Expand Up @@ -622,10 +619,7 @@ contract Stabilizer is Owned, Pausable, ReentrancyGuard {
}

_invest(usdxAmount, 0, slippage);

if (getEquityRatio() < minEquityRatio){
revert EquityRatioExcessed();
}
_checkRatio();
}

function oneStepDivest(uint256 usdxAmount, uint256 slippage, bool useAMM)
Expand Down Expand Up @@ -658,9 +652,7 @@ contract Stabilizer is Owned, Pausable, ReentrancyGuard {

TransferHelper.safeTransfer(token, msg.sender, amount);

if (sweepBorrowed > 0 && getEquityRatio() < minEquityRatio) {
revert EquityRatioExcessed();
}
if (sweepBorrowed > 0) _checkRatio();

emit Withdrawn(token, amount);
}
Expand Down Expand Up @@ -898,4 +890,8 @@ contract Stabilizer is Owned, Pausable, ReentrancyGuard {
oracleUsdx
);
}

function _checkRatio() internal view {
if (getEquityRatio() < minEquityRatio) revert EquityRatioExcessed();
}
}
43 changes: 41 additions & 2 deletions test/assets/uniswap_market_maker.js
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ contract('Uniswap Market Maker', async () => {
expect(await sweep.balanceOf(poolAddress)).to.greaterThan(sweepPoolBalance);
});

it('adds single side liquidty correctly', async () => {
it('adds single side liquidty for USDx correctly', async () => {
usdcPoolBalance = await usdc.balanceOf(poolAddress);
assetValue = await marketmaker.assetValue();
singleAmount0 = toBN("3000", 6);
Expand Down Expand Up @@ -208,12 +208,51 @@ contract('Uniswap Market Maker', async () => {
pUBB = await usdc.balanceOf(poolAddress);
sUBB = await sweep.balanceOf(poolAddress);

await(marketmaker.buySweepOnAMM(amount, 3e5));
await marketmaker.buySweepOnAMM(amount, 3e5);

expect(await usdc.balanceOf(marketmaker.address)).to.equal(mmUBB.sub(amount));
expect(await sweep.balanceOf(marketmaker.address)).to.greaterThan(mmSBB);
expect(await usdc.balanceOf(poolAddress)).to.greaterThan(pUBB);
expect(await sweep.balanceOf(poolAddress)).to.lessThan(sUBB);
})

it('adds single side liquidty for SWEEP correctly', async () => {
singleAmount0 = toBN("1000", 18);
amount = toBN("35000", 18);
tickSpread = 750;

await marketmaker.sellSweepOnAMM(amount, 2e5);
assetValue = await marketmaker.assetValue();
sweepPoolBalance = await sweep.balanceOf(poolAddress);

expect(await sweep.ammPrice()).to.lessThan(await sweep.targetPrice());
expect(await marketmaker.growPosition()).to.equal(0);
await marketmaker.lpGrow(singleAmount0, tickSpread);

sweepBalance = await sweep.balanceOf(poolAddress);
expect(sweepBalance).to.greaterThan(sweepPoolBalance);
growPosition = await marketmaker.growPosition();
expect(growPosition).to.greaterThan(0);

singleAmount1 = toBN("2000", 18);
await marketmaker.lpGrow(singleAmount1, tickSpread);
expect(await marketmaker.growPosition()).to.not.equal(growPosition);
});

it('removes the grow position id', async () => {
positionId = await marketmaker.growPosition();
sweepBalance = await sweep.balanceOf(marketmaker.address);
tickSpread = 1000;

expect(positionId).to.not.equal(0);
await marketmaker.removePosition(positionId);

expect(await marketmaker.growPosition()).to.equal(0);
expect(await sweep.balanceOf(marketmaker.address)).to.greaterThan(sweepBalance);

singleAmount2 = toBN("2000", 18);
await marketmaker.lpGrow(singleAmount2, tickSpread);
expect(await marketmaker.growPosition()).to.greaterThan(0);
})
})
});

0 comments on commit 988961c

Please sign in to comment.