Skip to content

Commit

Permalink
add LiquidityAmountsCalculator
Browse files Browse the repository at this point in the history
  • Loading branch information
YouStillAlive committed Aug 20, 2024
1 parent e28ed92 commit ae74971
Show file tree
Hide file tree
Showing 9 changed files with 115 additions and 61 deletions.
62 changes: 31 additions & 31 deletions contracts/BNBPartyLiquidity.sol
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,9 @@ abstract contract BNBPartyLiquidity is BNBPartySwaps {
uint256 amount0,
uint256 amount1,
uint160 sqrtPriceX96,
uint24 fee
uint24 fee,
int24 tickLower,
int24 tickUpper
) internal returns (address liquidityPool) {
// Create LP
liquidityPool = liquidityManager.createAndInitializePoolIfNecessary(
Expand All @@ -69,8 +71,8 @@ abstract contract BNBPartyLiquidity is BNBPartySwaps {
token0: token0,
token1: token1,
fee: fee,
tickLower: party.tickLower,
tickUpper: party.tickUpper,
tickLower: tickLower,
tickUpper: tickUpper,
amount0Desired: amount0,
amount1Desired: amount1,
amount0Min: 0,
Expand All @@ -86,19 +88,22 @@ abstract contract BNBPartyLiquidity is BNBPartySwaps {
/// @dev Decreases liquidity, collects tokens, creates a new pool, and sends bonuses
function _handleLiquidity(address recipient) internal {
IUniswapV3Pool pool = IUniswapV3Pool(msg.sender);
(uint160 sqrtPriceX96, , , , , , ) = pool.slot0();
address token0 = pool.token0();
address token1 = pool.token1();
uint128 liquidity = pool.liquidity();

// Decrease liquidity and collect tokens
(uint256 amount0, uint256 amount1) = BNBPositionManager.decreaseLiquidity(
INonfungiblePositionManager.DecreaseLiquidityParams({
tokenId: lpToTokenId[msg.sender],
liquidity: pool.liquidity(),
amount0Min: 0,
amount1Min: 0,
deadline: block.timestamp
})
);
(uint256 amount0, uint256 amount1) = BNBPositionManager
.decreaseLiquidity(
INonfungiblePositionManager.DecreaseLiquidityParams({
tokenId: lpToTokenId[msg.sender],
liquidity: liquidity,
amount0Min: 0,
amount1Min: 0,
deadline: block.timestamp
})
);

BNBPositionManager.collect(
INonfungiblePositionManager.CollectParams({
Expand All @@ -110,38 +115,33 @@ abstract contract BNBPartyLiquidity is BNBPartySwaps {
);

uint256 unwrapAmount = party.bonusTargetReach + party.bonusPartyCreator + party.targetReachFee;
uint160 newSqrtPriceX96;
if (token0 == address(WBNB)) {
amount0 -= unwrapAmount; // Deduct unwrap amount from token0 if it is WBNB
isTokenOnPartyLP[token1] = false;
newSqrtPriceX96 = liquidityAmountsCalculator.getNextSqrtPriceFromAmount0RoundingUp(
sqrtPriceX96,
liquidity,
unwrapAmount,
false
);
} else {
amount1 -= unwrapAmount; // Deduct unwrap amount from token1 if it is WBNB
isTokenOnPartyLP[token0] = false;
newSqrtPriceX96 = liquidityAmountsCalculator.getNextSqrtPriceFromAmount1RoundingDown(
sqrtPriceX96,
liquidity,
unwrapAmount,
false
);
}

IERC20(token0).approve(address(positionManager), amount0);
IERC20(token1).approve(address(positionManager), amount1);
uint160 sqrtPriceX96 = _calcSqrtPriceX96(amount0, amount1);
// Create new Liquidity Pool
_createLP(positionManager, token0, token1, amount0, amount1, sqrtPriceX96, party.lpFee);
_createLP(positionManager, token0, token1, amount0, amount1, newSqrtPriceX96, party.lpFee, party.tickLower, party.tickUpper + 5800);

// Send bonuses
_unwrapAndSendBNB(recipient, unwrapAmount);
}

function _calcSqrtPriceX96(
uint256 amount0,
uint256 amount1
) internal pure returns (uint160 sqrtPriceX96) {
uint256 ratioX192 = (amount1 << 192) / amount0; // Shift left by 192 to maintain precision
sqrtPriceX96 = uint160(_sqrt(ratioX192));
}

function _sqrt(uint256 x) private pure returns (uint256 y) {
uint256 z = (x + 1) / 2;
y = x;
while (z < y) {
y = z;
z = (x / z + z) / 2;
}
}
}
6 changes: 6 additions & 0 deletions contracts/BNBPartyManageable.sol
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@ abstract contract BNBPartyManageable is BNBPartyModifiers {
BNBPositionManager = _BNBPositionManager;
}

function setLiquidityAmountsCalculator(
ILiquidityAmountsCalculator _liquidityAmountsCalculator
) external onlyOwner {
liquidityAmountsCalculator = _liquidityAmountsCalculator;
}

/// @notice Sets the swap router address
/// @param _swapRouter Address of the new swap router
/// @dev Reverts if the new swap router is identical to the current one
Expand Down
5 changes: 3 additions & 2 deletions contracts/BNBPartyState.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ pragma solidity ^0.8.0;

import "@openzeppelin/contracts/access/Ownable.sol";
import "@bnb-party/v3-periphery/contracts/interfaces/ISwapRouter.sol";

import "./interfaces/ILiquidityAmountsCalculator.sol";
import "./interfaces/INonfungiblePositionManager.sol";
import "./interfaces/IBNBPartyFactory.sol";
import "./interfaces/IUniswapV3Pool.sol";
Expand All @@ -19,7 +19,8 @@ abstract contract BNBPartyState is IBNBPartyFactory, Ownable {
mapping(address => bool) public isParty; // Mapping to track if a LiquidityPool is a party
mapping(address => uint256) public lpToTokenId; // Mapping from LiquidityPool to its NFT tokenId
mapping(address => address) public lpToCreator; // Mapping from LiquidityPool to its creator
mapping(address => bool) isTokenOnPartyLP; // Mapping to track if a token is part of a party
mapping(address => bool) public isTokenOnPartyLP; // Mapping to track if a token is part of a party
ILiquidityAmountsCalculator public liquidityAmountsCalculator;

Party public party; // store party parameters

Expand Down
24 changes: 24 additions & 0 deletions contracts/calc/LiquidityAmountsCalculator.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;

import "@uniswap/v3-core/contracts/libraries/SqrtPriceMath.sol";

contract LiquidityAmountsCalculator {
function getNextSqrtPriceFromAmount0RoundingUp(
uint160 sqrtPX96,
uint128 liquidity,
uint256 amount,
bool add
) external pure returns (uint160 sqrtPriceX96) {
sqrtPriceX96 = SqrtPriceMath.getNextSqrtPriceFromAmount0RoundingUp(sqrtPX96, liquidity, amount, add);
}

function getNextSqrtPriceFromAmount1RoundingDown(
uint160 sqrtPX96,
uint128 liquidity,
uint256 amount,
bool add
) external pure returns (uint160 sqrtPriceX96) {
sqrtPriceX96 = SqrtPriceMath.getNextSqrtPriceFromAmount1RoundingDown(sqrtPX96, liquidity, amount, add);
}
}
18 changes: 18 additions & 0 deletions contracts/interfaces/ILiquidityAmountsCalculator.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface ILiquidityAmountsCalculator {
function getNextSqrtPriceFromAmount0RoundingUp(
uint160 sqrtPX96,
uint128 liquidity,
uint256 amount,
bool add
) external pure returns (uint160 sqrtQX96);

function getNextSqrtPriceFromAmount1RoundingDown(
uint160 sqrtPX96,
uint128 liquidity,
uint256 amount,
bool add
) external pure returns (uint160 sqrtQX96);
}
27 changes: 14 additions & 13 deletions test/BNBPartyFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,21 @@ const POOL_BYTECODE_HASH = keccak256(bytecode)

describe("BNBPartyFactory", function () {
let signers: SignerWithAddress[]
const partyTarget = ethers.parseEther("90")
const tokenCreationFee = ethers.parseUnits("1", 16)
const returnFeeAmount = ethers.parseUnits("5", 17)
const bonusFee = ethers.parseUnits("1", 16)
const targetReachFee = ethers.parseUnits("1", 17)
const initialTokenAmount = "10000000000000000000000000"
const partyTarget = ethers.parseEther("13") // 13 BNB target
const tokenCreationFee = ethers.parseUnits("1", 16) // 0.01 BNB token creation fee
const returnFeeAmount = ethers.parseUnits("5", 16) // 0.05 BNB return fee (bonusTargetReach)
const bonusFee = ethers.parseUnits("1", 17) // 0.01 BNB bonus fee (bonusPartyCreator)
const targetReachFee = ethers.parseUnits("8.5", 17) // 0.85 BNB target reach fee
const initialTokenAmount = "1000000000000000000000000000"
const name = "Party"
const symbol = "Token"
const sqrtPriceX96 = "25052911542910170730777872"
const sqrtPriceX96 = "1252685732681638336686364"
const BNBToTarget: bigint = partyTarget + ethers.parseEther("1")
let liquidityAmountsCalculator: LiquidityAmountsCalculator

before(async () => {
signers = await ethers.getSigners()
await deployContracts()
await deployContracts(partyTarget)
})

it("should deploy BNBPartyFactory", async function () {
Expand All @@ -46,8 +47,8 @@ describe("BNBPartyFactory", function () {
expect((await bnbPartyFactory.party()).lpFee).to.equal(FeeAmount.HIGH)
expect((await bnbPartyFactory.party()).partyLpFee).to.equal(FeeAmount.HIGH)
expect((await bnbPartyFactory.party()).createTokenFee).to.equal(tokenCreationFee)
expect((await bnbPartyFactory.party()).tickUpper).to.equal("0")
expect((await bnbPartyFactory.party()).tickLower).to.equal("-92200")
expect((await bnbPartyFactory.party()).tickUpper).to.equal("195600")
expect((await bnbPartyFactory.party()).tickLower).to.equal("-214200")
})

it("should create party LP", async function () {
Expand Down Expand Up @@ -146,15 +147,15 @@ describe("BNBPartyFactory", function () {
const newBalance = await token.balanceOf(newLPPool)
const userBalance = await token.balanceOf(await signers[0].getAddress())
const bnbpartyBalance = await token.balanceOf(await bnbPartyFactory.getAddress())
expect(newBalance).to.be.equal(oldBalance + rest - userBalance - bnbpartyBalance - 1n)
expect(newBalance).to.be.equal(oldBalance + rest - userBalance - bnbpartyBalance - 2n)
})

it("should send WBNB to new LP", async () => {
await bnbPartyFactory.joinParty(MEME, 0, { value: BNBToTarget })
const lpAddress = await v3Factory.getPool(await weth9.getAddress(), MEME, FeeAmount.HIGH)
const balance = await weth9.balanceOf(lpAddress)
const percentFee = ethers.parseEther("0.91")
expect(balance).to.be.equal(BNBToTarget - returnFeeAmount - bonusFee - targetReachFee - percentFee - 3n)
const percentFee = ethers.parseEther("0.14") // target 13 + 1 BNB - 1% fee
expect(balance).to.be.equal(BNBToTarget - returnFeeAmount - bonusFee - targetReachFee - percentFee - 1n)
})
})
})
6 changes: 3 additions & 3 deletions test/SwapRouter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ describe("Smart Router", function () {
MEME = position.token1 == (await weth9.getAddress()) ? position.token0 : position.token1
lpAddress = await v3PartyFactory.getPool(await weth9.getAddress(), MEME, FeeAmount.HIGH)
MEMEToken = await ethers.getContractAt("ERC20", MEME)
await MEMEToken.approve(await bnbPartyFactory.getAddress(), ethers.parseEther("100"))
await MEMEToken.approve(await BNBSwapRouter.getAddress(), ethers.parseEther("100"))
await MEMEToken.approve(await bnbPartyFactory.getAddress(), ethers.parseEther("1000000"))
await MEMEToken.approve(await BNBSwapRouter.getAddress(), ethers.parseEther("10000000"))
})

it("should increase wbnb on party lp after join party", async () => {
Expand All @@ -58,7 +58,7 @@ describe("Smart Router", function () {
})

it("user should receive bnb after leave party", async () => {
const amountIn = ethers.parseUnits("5", 16)
const amountIn = ethers.parseUnits("5000", 18)
const bnbBalanceBefore = await ethers.provider.getBalance(await signers[0].getAddress())
await bnbPartyFactory.leaveParty(MEME, amountIn, 0)
const bnbBalanceAfter = await ethers.provider.getBalance(await signers[0].getAddress())
Expand Down
4 changes: 2 additions & 2 deletions test/WithdrawFee.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ describe("Withdraw fees", function () {
const partyLP = await v3PartyFactory.getPool(await weth9.getAddress(), MEME, FeeAmount.HIGH)
await bnbPartyFactory.withdrawPartyLPFee([partyLP])
const balanceAfter = await weth9.balanceOf(await signers[0].getAddress())
expect(balanceAfter).to.be.equal(balanceBefore + expectedFee - 1n)
expect(balanceAfter).to.be.equal(balanceBefore + expectedFee)
})

it("should revert LPNotAtParty", async () => {
Expand All @@ -88,7 +88,7 @@ describe("Withdraw fees", function () {
await bnbPartyFactory.joinParty(MEME, 0, { value: amountIn })
const lpPool = (await ethers.getContractAt("UniswapV3Pool", lpAddress)) as any as IUniswapV3Pool
const liquidity = await lpPool.liquidity()
const feeGrowthGlobalX128 = await lpPool.feeGrowthGlobal1X128()
const feeGrowthGlobalX128 = await lpPool.feeGrowthGlobal1X128() > 0 ? await lpPool.feeGrowthGlobal1X128() : await lpPool.feeGrowthGlobal0X128()
expect(await bnbPartyFactory.calculateFees(liquidity, feeGrowthGlobalX128)).to.be.equal(amountIn / 100n - 1n) // 1 % fee
})

Expand Down
24 changes: 14 additions & 10 deletions test/helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import FactoryArtifact from "@bnb-party/v3-core/artifacts/contracts/UniswapV3Fac
import ClassicFactoryArtifact from "@uniswap/v3-core/artifacts/contracts/UniswapV3Factory.sol/UniswapV3Factory.json"
import ClassicNonfungiblePositionManager from "@uniswap/v3-periphery/artifacts/contracts/NonfungiblePositionManager.sol/NonfungiblePositionManager.json"
import ClassicSwapRouter from "@uniswap/v3-periphery/artifacts/contracts/SwapRouter.sol/SwapRouter.json"
import { LiquidityAmountsCalculator } from "../typechain-types/contracts/calc/SqrtPriceCalculator.sol"

export enum FeeAmount {
LOW = 500,
Expand All @@ -36,14 +37,13 @@ export let BNBSwapRouter: SwapRouter
export let swapRouter: SwapRouter
export let weth9: IWBNB

export async function deployContracts() {
const partyTarget = ethers.parseEther("90")
const tokenCreationFee = ethers.parseUnits("1", 16)
const returnFeeAmount = ethers.parseUnits("5", 17)
const bonusFee = ethers.parseUnits("1", 16)
const targetReachFee = ethers.parseUnits("1", 17)
const initialTokenAmount = "10000000000000000000000000"
const sqrtPriceX96 = "25052911542910170730777872"
export async function deployContracts(partyTarget = ethers.parseEther("90")) {
const tokenCreationFee = ethers.parseUnits("1", 16) // 0.01 BNB token creation fee
const returnFeeAmount = ethers.parseUnits("5", 16) // 0.05 BNB return fee (bonusTargetReach)
const bonusFee = ethers.parseUnits("1", 17) // 0.01 BNB bonus fee (bonusPartyCreator)
const targetReachFee = ethers.parseUnits("8.5", 17) // 0.85 BNB target reach fee
const initialTokenAmount = "1000000000000000000000000000"
const sqrtPriceX96 = "1252685732681638336686364"
// Deploy WETH9
const WETH9 = await ethers.getContractFactory(WETH9Artifact.abi, WETH9Artifact.bytecode)
weth9 = (await WETH9.deploy()) as IWBNB
Expand All @@ -60,8 +60,8 @@ export async function deployContracts() {
bonusTargetReach: returnFeeAmount,
bonusPartyCreator: bonusFee,
targetReachFee: targetReachFee,
tickLower: "-92200",
tickUpper: "0",
tickLower: "-214200",
tickUpper: "195600",
},
await weth9.getAddress()
)) as BNBPartyFactory
Expand Down Expand Up @@ -110,4 +110,8 @@ export async function deployContracts() {
// Set Swap Router in BNBPartyFactory
await bnbPartyFactory.setBNBPartySwapRouter(await BNBSwapRouter.getAddress())
await bnbPartyFactory.setSwapRouter(await swapRouter.getAddress())

const liquidityAmountsCalculatorContract = await ethers.getContractFactory("LiquidityAmountsCalculator")
const liquidityAmountsCalculator = (await liquidityAmountsCalculatorContract.deploy()) as LiquidityAmountsCalculator
await bnbPartyFactory.setLiquidityAmountsCalculator(await liquidityAmountsCalculator.getAddress())
}

0 comments on commit ae74971

Please sign in to comment.