-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
ca5fd6f
commit 4bb0e82
Showing
3 changed files
with
194 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
// SPDX-License-Identifier: GPL-2.0-or-later | ||
// Copyright (C) 2024 PancakeSwap | ||
pragma solidity ^0.8.19; | ||
|
||
import {SafeTransferLib} from "solmate/utils/SafeTransferLib.sol"; | ||
import {BaseMigrator, IV3NonfungiblePositionManager} from "../base/BaseMigrator.sol"; | ||
import {IBinMigrator, PoolKey} from "./interfaces/IBinMigrator.sol"; | ||
import {IBinFungiblePositionManager} from "./interfaces/IBinFungiblePositionManager.sol"; | ||
import {Currency} from "pancake-v4-core/src/types/Currency.sol"; | ||
import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol"; | ||
|
||
contract BinMigrator is IBinMigrator, BaseMigrator { | ||
IBinFungiblePositionManager public immutable binFungiblePositionManager; | ||
|
||
constructor(address _WETH9, address _binFungiblePositionManager) BaseMigrator(_WETH9) { | ||
binFungiblePositionManager = IBinFungiblePositionManager(_binFungiblePositionManager); | ||
} | ||
|
||
function migrateFromV2( | ||
V2PoolParams calldata v2PoolParams, | ||
V4BinPoolParams calldata v4MintParams, | ||
uint256 extraAmount0, | ||
uint256 extraAmount1 | ||
) external payable override { | ||
(uint256 amount0Received, uint256 amount1Received) = withdrawLiquidityFromV2(v2PoolParams); | ||
|
||
/// @notice if user mannually specify the price range, they might need to send extra token | ||
batchAndNormalizeTokens( | ||
v4MintParams.poolKey.currency0, v4MintParams.poolKey.currency1, extraAmount0, extraAmount1 | ||
); | ||
|
||
uint256 amount0Input = amount0Received + extraAmount0; | ||
uint256 amount1Input = amount1Received + extraAmount1; | ||
IBinFungiblePositionManager.AddLiquidityParams memory addLiquidityParams = IBinFungiblePositionManager | ||
.AddLiquidityParams({ | ||
poolKey: v4MintParams.poolKey, | ||
amount0: SafeCast.toUint128(amount0Input), | ||
amount1: SafeCast.toUint128(amount1Input), | ||
amount0Min: v4MintParams.amount0Min, | ||
amount1Min: v4MintParams.amount1Min, | ||
activeIdDesired: v4MintParams.activeIdDesired, | ||
idSlippage: v4MintParams.idSlippage, | ||
deltaIds: v4MintParams.deltaIds, | ||
distributionX: v4MintParams.distributionX, | ||
distributionY: v4MintParams.distributionY, | ||
to: v4MintParams.to, | ||
deadline: v4MintParams.deadline | ||
}); | ||
(uint256 amount0Consumed, uint256 amount1Consumed,,) = _addLiquidityToTargetPool(addLiquidityParams); | ||
|
||
// refund if necessary, ETH is supported by CurrencyLib | ||
unchecked { | ||
if (amount0Input > amount0Consumed) { | ||
v4MintParams.poolKey.currency0.transfer(v4MintParams.to, amount0Input - amount0Consumed); | ||
} | ||
if (amount1Input > amount1Consumed) { | ||
v4MintParams.poolKey.currency1.transfer(v4MintParams.to, amount1Input - amount1Consumed); | ||
} | ||
} | ||
} | ||
|
||
function migrateFromV3( | ||
V3PoolParams calldata v3PoolParams, | ||
V4BinPoolParams calldata v4MintParams, | ||
uint256 extraAmount0, | ||
uint256 extraAmount1 | ||
) external payable override { | ||
IV3NonfungiblePositionManager.DecreaseLiquidityParams memory decreaseLiquidityParams = | ||
IV3NonfungiblePositionManager.DecreaseLiquidityParams({ | ||
tokenId: v3PoolParams.tokenId, | ||
liquidity: v3PoolParams.liquidity, | ||
amount0Min: v3PoolParams.amount0Min, | ||
amount1Min: v3PoolParams.amount1Min, | ||
deadline: v4MintParams.deadline | ||
}); | ||
(uint256 amount0Received, uint256 amount1Received) = | ||
withdrawLiquidityFromV3(v3PoolParams.nfp, decreaseLiquidityParams, v3PoolParams.collectFee); | ||
|
||
/// @notice if user mannually specify the price range, they need to send extra token | ||
batchAndNormalizeTokens( | ||
v4MintParams.poolKey.currency0, v4MintParams.poolKey.currency1, extraAmount0, extraAmount1 | ||
); | ||
|
||
uint256 amount0Input = amount0Received + extraAmount0; | ||
uint256 amount1Input = amount1Received + extraAmount1; | ||
IBinFungiblePositionManager.AddLiquidityParams memory addLiquidityParams = IBinFungiblePositionManager | ||
.AddLiquidityParams({ | ||
poolKey: v4MintParams.poolKey, | ||
amount0: SafeCast.toUint128(amount0Input), | ||
amount1: SafeCast.toUint128(amount1Input), | ||
amount0Min: v4MintParams.amount0Min, | ||
amount1Min: v4MintParams.amount1Min, | ||
activeIdDesired: v4MintParams.activeIdDesired, | ||
idSlippage: v4MintParams.idSlippage, | ||
deltaIds: v4MintParams.deltaIds, | ||
distributionX: v4MintParams.distributionX, | ||
distributionY: v4MintParams.distributionY, | ||
to: v4MintParams.to, | ||
deadline: v4MintParams.deadline | ||
}); | ||
(uint256 amount0Consumed, uint256 amount1Consumed,,) = _addLiquidityToTargetPool(addLiquidityParams); | ||
|
||
// refund if necessary, ETH is supported by CurrencyLib | ||
unchecked { | ||
if (amount0Input > amount0Consumed) { | ||
v4MintParams.poolKey.currency0.transfer(v4MintParams.to, amount0Input - amount0Consumed); | ||
} | ||
if (amount1Input > amount1Consumed) { | ||
v4MintParams.poolKey.currency1.transfer(v4MintParams.to, amount1Input - amount1Consumed); | ||
} | ||
} | ||
} | ||
|
||
function _addLiquidityToTargetPool(IBinFungiblePositionManager.AddLiquidityParams memory params) | ||
internal | ||
returns (uint128 amount0, uint128 amount1, uint256[] memory tokenIds, uint256[] memory liquidityMinted) | ||
{ | ||
/// @dev currency1 cant be NATIVE | ||
bool nativePair = params.poolKey.currency0.isNative(); | ||
if (!nativePair) { | ||
approveMaxIfNeeded(params.poolKey.currency0, address(binFungiblePositionManager), params.amount0); | ||
} | ||
approveMaxIfNeeded(params.poolKey.currency1, address(binFungiblePositionManager), params.amount1); | ||
|
||
(amount0, amount1, tokenIds, liquidityMinted) = | ||
binFungiblePositionManager.addLiquidity{value: nativePair ? params.amount0 : 0}(params); | ||
if (nativePair) { | ||
binFungiblePositionManager.refundETH(); | ||
} | ||
} | ||
|
||
/// @notice Planned to be batched with migration operations through multicall to save gas | ||
function initialize(PoolKey memory poolKey, uint24 activeId, bytes calldata hookData) external payable override { | ||
return binFungiblePositionManager.initialize(poolKey, activeId, hookData); | ||
} | ||
|
||
receive() external payable { | ||
if (msg.sender != address(binFungiblePositionManager) && msg.sender != WETH9) { | ||
revert INVALID_ETHER_SENDER(); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
// SPDX-License-Identifier: GPL-2.0-or-later | ||
// Copyright (C) 2024 PancakeSwap | ||
pragma solidity ^0.8.19; | ||
|
||
import {PoolKey} from "pancake-v4-core/src/types/PoolKey.sol"; | ||
import {IBaseMigrator} from "../../interfaces/IBaseMigrator.sol"; | ||
import {IV3NonfungiblePositionManager} from "../../interfaces/external/IV3NonfungiblePositionManager.sol"; | ||
|
||
interface IBinMigrator is IBaseMigrator { | ||
/// @notice same fields as IBinFungiblePositionManager.AddLiquidityParams | ||
/// except amount0/amount1 which will be calculated by migrator | ||
struct V4BinPoolParams { | ||
PoolKey poolKey; | ||
// uint128 amount0; | ||
// uint128 amount1; | ||
uint128 amount0Min; | ||
uint128 amount1Min; | ||
uint256 activeIdDesired; | ||
uint256 idSlippage; | ||
int256[] deltaIds; | ||
uint256[] distributionX; | ||
uint256[] distributionY; | ||
address to; | ||
uint256 deadline; | ||
} | ||
|
||
function migrateFromV2( | ||
V2PoolParams calldata v2PoolParams, | ||
V4BinPoolParams calldata v4MintParams, | ||
// extra funds to be added | ||
uint256 extraAmount0, | ||
uint256 extraAmount1 | ||
) external payable; | ||
|
||
function migrateFromV3( | ||
V3PoolParams calldata v3PoolParams, | ||
V4BinPoolParams calldata v4MintParams, | ||
// extra funds to be added | ||
uint256 extraAmount0, | ||
uint256 extraAmount1 | ||
) external payable; | ||
|
||
/// @notice Initialize a new pool | ||
/// @dev Call this when the pool does not exist and is not initialized | ||
/// @param poolKey The pool key | ||
/// @param activeId The active id of the pool | ||
/// @param hookData Hook data for the pool | ||
function initialize(PoolKey memory poolKey, uint24 activeId, bytes calldata hookData) external payable; | ||
} |