Skip to content

Commit

Permalink
Merge pull request #29 from Aperture-Finance/pcs-dev
Browse files Browse the repository at this point in the history
Add support for PancakeSwapV3
  • Loading branch information
gnarlycow authored Mar 23, 2024
2 parents 55629be + 6149754 commit 1a3c2f1
Show file tree
Hide file tree
Showing 19 changed files with 602 additions and 108 deletions.
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@aperture_finance/uni-v3-lib",
"description": "A suite of Solidity libraries that have been imported and rewritten from Uniswap's v3-core and v3-periphery",
"version": "1.2.2",
"version": "2.0.0",
"author": "Aperture Finance",
"homepage": "https://aperture.finance/",
"license": "GPL-2.0-or-later",
Expand Down Expand Up @@ -33,6 +33,8 @@
},
"dependencies": {
"@openzeppelin/contracts": "^5.0.2",
"@pancakeswap/v3-core": "^1.0.2",
"@pancakeswap/v3-periphery": "^1.0.2",
"@uniswap/v3-core": "^1.0.1",
"@uniswap/v3-periphery": "^1.4.4",
"solady": "^0.0.180"
Expand Down
1 change: 1 addition & 0 deletions remappings.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
@openzeppelin/=node_modules/@openzeppelin/
@pancakeswap/=node_modules/@pancakeswap/
@uniswap/=node_modules/@uniswap/
forge-std/=lib/forge-std/src/
solady/=node_modules/solady/
44 changes: 44 additions & 0 deletions src/CallbackValidationPancakeSwapV3.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

import "@pancakeswap/v3-core/contracts/interfaces/IPancakeV3Pool.sol";
import "./PoolAddressPancakeSwapV3.sol";

/// @notice Provides validation for callbacks from PancakeSwapV3 Pools
/// @author Aperture Finance
/// @author Modified from PancakeSwapV3 (https://www.npmjs.com/package/@pancakeswap/v3-periphery?activeTab=code and look for contracts/libraries/CallbackValidation.sol)
library CallbackValidationPancakeSwapV3 {
/// @notice Returns the address of a valid PancakeSwapV3 Pool
/// @param deployer The contract address of the PancakeSwapV3 deployer
/// @param tokenA The contract address of either token0 or token1
/// @param tokenB The contract address of the other token
/// @param fee The fee collected upon every swap in the pool, denominated in hundredths of a bip
/// @return pool The V3 pool contract address
function verifyCallback(
address deployer,
address tokenA,
address tokenB,
uint24 fee
) internal view returns (IPancakeV3Pool pool) {
pool = IPancakeV3Pool(PoolAddressPancakeSwapV3.computeAddress(deployer, tokenA, tokenB, fee));
require(msg.sender == address(pool));
}

/// @notice Returns the address of a valid PancakeSwapV3 Pool
/// @param deployer The contract address of the PancakeSwapV3 deployer
/// @param poolKey The identifying key of the V3 pool
/// @return pool The V3 pool contract address
function verifyCallback(address deployer, PoolKey memory poolKey) internal view returns (IPancakeV3Pool pool) {
pool = IPancakeV3Pool(PoolAddressPancakeSwapV3.computeAddressSorted(deployer, poolKey));
require(msg.sender == address(pool));
}

/// @notice Returns the address of a valid PancakeSwapV3 Pool
/// @param deployer The contract address of the PancakeSwapV3 deployer
/// @param poolKey The abi encoded PoolKey of the V3 pool
/// @return pool The V3 pool contract address
function verifyCallbackCalldata(address deployer, bytes calldata poolKey) internal view returns (address pool) {
pool = PoolAddressPancakeSwapV3.computeAddressCalldata(deployer, poolKey);
require(msg.sender == pool);
}
}
8 changes: 1 addition & 7 deletions src/PoolAddress.sol
Original file line number Diff line number Diff line change
@@ -1,15 +1,9 @@
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

import "./PoolKey.sol";
import "./TernaryLib.sol";

/// @notice The identifying key of the pool
struct PoolKey {
address token0;
address token1;
uint24 fee;
}

/// @title Provides functions for deriving a pool address from the factory, tokens, and the fee
/// @author Aperture Finance
/// @author Modified from Uniswap (https://github.com/uniswap/v3-periphery/blob/main/contracts/libraries/PoolAddress.sol)
Expand Down
150 changes: 150 additions & 0 deletions src/PoolAddressPancakeSwapV3.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

import "./PoolKey.sol";
import "./TernaryLib.sol";

/// @title Provides functions for deriving a pool address from the deployer, tokens, and the fee
/// @author Aperture Finance
/// @author Modified from PancakeSwapV3 (https://www.npmjs.com/package/@pancakeswap/v3-periphery?activeTab=code and look for contracts/libraries/PoolAddress.sol)
/// @dev Some functions in this library knowingly create dirty bits at the destination of the free memory pointer.
/// However, this is safe because "Note that you do not need to update the free memory pointer if there is no following
/// allocation, but you can only use memory starting from the current offset given by the free memory pointer."
/// according to https://docs.soliditylang.org/en/latest/assembly.html#memory-safety.
library PoolAddressPancakeSwapV3 {
bytes32 internal constant POOL_INIT_CODE_HASH = 0x6ce8eb472fa82df5469c6ab6d485f17c3ad13c8cd7af59b3d4a8026c5ce0f7e2;

/// @notice Returns PoolKey: the ordered tokens with the matched fee levels
/// @param tokenA The first token of a pool, unsorted
/// @param tokenB The second token of a pool, unsorted
/// @param fee The fee level of the pool
/// @return key The pool details with ordered token0 and token1 assignments
function getPoolKey(address tokenA, address tokenB, uint24 fee) internal pure returns (PoolKey memory key) {
(tokenA, tokenB) = TernaryLib.sort2(tokenA, tokenB);
/// @solidity memory-safe-assembly
assembly {
// Must inline this for best performance
mstore(key, tokenA)
mstore(add(key, 0x20), tokenB)
mstore(add(key, 0x40), fee)
}
}

/// @notice Returns PoolKey: the ordered tokens with the matched fee levels
/// @param token0 The first token of a pool, already sorted
/// @param token1 The second token of a pool, already sorted
/// @param fee The fee level of the pool
/// @return key The pool details with ordered token0 and token1 assignments
function getPoolKeySorted(address token0, address token1, uint24 fee) internal pure returns (PoolKey memory key) {
/// @solidity memory-safe-assembly
assembly {
mstore(key, token0)
mstore(add(key, 0x20), token1)
mstore(add(key, 0x40), fee)
}
}

/// @notice Deterministically computes the pool address given the deployer and PoolKey
/// @param deployer The PancakeSwapV3 deployer contract address
/// @param key The PoolKey
/// @return pool The contract address of the V3 pool
function computeAddress(address deployer, PoolKey memory key) internal pure returns (address pool) {
require(key.token0 < key.token1);
return computeAddressSorted(deployer, key);
}

/// @notice Deterministically computes the pool address given the deployer and PoolKey
/// @dev Assumes PoolKey is sorted
/// @param deployer The PancakeSwapV3 deployer contract address
/// @param key The PoolKey
/// @return pool The contract address of the V3 pool
function computeAddressSorted(address deployer, PoolKey memory key) internal pure returns (address pool) {
/// @solidity memory-safe-assembly
assembly {
// Cache the free memory pointer.
let fmp := mload(0x40)
// abi.encodePacked(hex'ff', deployer, poolHash, POOL_INIT_CODE_HASH)
// Prefix the deployer address with 0xff.
mstore(0, or(deployer, 0xff0000000000000000000000000000000000000000))
mstore(0x20, keccak256(key, 0x60))
mstore(0x40, POOL_INIT_CODE_HASH)
// Compute the CREATE2 pool address and clean the upper bits.
pool := and(keccak256(0x0b, 0x55), 0xffffffffffffffffffffffffffffffffffffffff)
// Restore the free memory pointer.
mstore(0x40, fmp)
}
}

/// @notice Deterministically computes the pool address given the deployer, tokens, and the fee
/// @param deployer The PancakeSwapV3 deployer contract address
/// @param tokenA One of the tokens in the pool, unsorted
/// @param tokenB The other token in the pool, unsorted
/// @param fee The fee tier of the pool
function computeAddress(
address deployer,
address tokenA,
address tokenB,
uint24 fee
) internal pure returns (address pool) {
(tokenA, tokenB) = TernaryLib.sort2(tokenA, tokenB);
return computeAddressSorted(deployer, tokenA, tokenB, fee);
}

/// @notice Deterministically computes the pool address given the deployer, tokens, and the fee
/// @dev Assumes tokens are sorted
/// @param deployer The PancakeSwapV3 deployer contract address
/// @param token0 The first token of a pool, already sorted
/// @param token1 The second token of a pool, already sorted
/// @param fee The fee tier of the pool
function computeAddressSorted(
address deployer,
address token0,
address token1,
uint24 fee
) internal pure returns (address pool) {
/// @solidity memory-safe-assembly
assembly {
// Cache the free memory pointer.
let fmp := mload(0x40)
// Hash the pool key.
mstore(0, token0)
mstore(0x20, token1)
mstore(0x40, fee)
let poolHash := keccak256(0, 0x60)
// abi.encodePacked(hex'ff', deployer, poolHash, POOL_INIT_CODE_HASH)
// Prefix the deployer address with 0xff.
mstore(0, or(deployer, 0xff0000000000000000000000000000000000000000))
mstore(0x20, poolHash)
mstore(0x40, POOL_INIT_CODE_HASH)
// Compute the CREATE2 pool address and clean the upper bits.
pool := and(keccak256(0x0b, 0x55), 0xffffffffffffffffffffffffffffffffffffffff)
// Restore the free memory pointer.
mstore(0x40, fmp)
}
}

/// @notice Deterministically computes the pool address given the deployer and PoolKey
/// @dev Uses PoolKey in calldata and assumes PoolKey is sorted
/// @param deployer The PancakeSwapV3 deployer contract address
/// @param key The abi encoded PoolKey of the V3 pool
/// @return pool The contract address of the V3 pool
function computeAddressCalldata(address deployer, bytes calldata key) internal pure returns (address pool) {
/// @solidity memory-safe-assembly
assembly {
// Cache the free memory pointer.
let fmp := mload(0x40)
// Hash the pool key.
calldatacopy(0, key.offset, 0x60)
let poolHash := keccak256(0, 0x60)
// abi.encodePacked(hex'ff', deployer, poolHash, POOL_INIT_CODE_HASH)
// Prefix the deployer address with 0xff.
mstore(0, or(deployer, 0xff0000000000000000000000000000000000000000))
mstore(0x20, poolHash)
mstore(0x40, POOL_INIT_CODE_HASH)
// Compute the CREATE2 pool address and clean the upper bits.
pool := and(keccak256(0x0b, 0x55), 0xffffffffffffffffffffffffffffffffffffffff)
// Restore the free memory pointer.
mstore(0x40, fmp)
}
}
}
9 changes: 9 additions & 0 deletions src/PoolKey.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

/// @notice The identifying key of a liquidity pool
struct PoolKey {
address token0;
address token1;
uint24 fee;
}
30 changes: 30 additions & 0 deletions src/test/PoolAddressPancakeSwapV3Test.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.7.6;
pragma abicoder v2;

import "@pancakeswap/v3-periphery/contracts/libraries/PoolAddress.sol";
import "./interfaces/IPoolAddress.sol";

/// @dev Expose internal functions to test the PoolAddress library.
contract PoolAddressPancakeSwapV3Test is IPoolAddress {
function getPoolKey(
address tokenA,
address tokenB,
uint24 fee
) external pure override returns (PoolKey memory key) {
PoolAddress.PoolKey memory _key = PoolAddress.getPoolKey(tokenA, tokenB, fee);
/// @solidity memory-safe-assembly
assembly {
key := _key
}
}

function computeAddress(address deployer, PoolKey memory key) external pure override returns (address pool) {
PoolAddress.PoolKey memory _key;
/// @solidity memory-safe-assembly
assembly {
_key := key
}
return PoolAddress.computeAddress(deployer, _key);
}
}
4 changes: 2 additions & 2 deletions src/test/PoolAddressTest.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@ contract PoolAddressTest is IPoolAddress {
address tokenA,
address tokenB,
uint24 fee
) external pure override returns (IPoolKey memory key) {
) external pure override returns (PoolKey memory key) {
PoolAddress.PoolKey memory _key = PoolAddress.getPoolKey(tokenA, tokenB, fee);
/// @solidity memory-safe-assembly
assembly {
key := _key
}
}

function computeAddress(address factory, IPoolKey memory key) external pure override returns (address pool) {
function computeAddress(address factory, PoolKey memory key) external pure override returns (address pool) {
PoolAddress.PoolKey memory _key;
/// @solidity memory-safe-assembly
assembly {
Expand Down
12 changes: 4 additions & 8 deletions src/test/interfaces/IPoolAddress.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,10 @@
pragma solidity >=0.5.0;
pragma abicoder v2;

interface IPoolAddress {
struct IPoolKey {
address token0;
address token1;
uint24 fee;
}
import "../../PoolKey.sol";

function getPoolKey(address tokenA, address tokenB, uint24 fee) external pure returns (IPoolKey memory);
interface IPoolAddress {
function getPoolKey(address tokenA, address tokenB, uint24 fee) external pure returns (PoolKey memory);

function computeAddress(address factory, IPoolKey memory key) external pure returns (address pool);
function computeAddress(address factory, PoolKey memory key) external pure returns (address pool);
}
Loading

0 comments on commit 1a3c2f1

Please sign in to comment.