Skip to content

Commit

Permalink
"Oracle and fees"
Browse files Browse the repository at this point in the history
  • Loading branch information
MxianD committed Aug 30, 2024
1 parent e58647b commit 4182e1f
Show file tree
Hide file tree
Showing 20 changed files with 3,176 additions and 1,751 deletions.
23 changes: 12 additions & 11 deletions src/UniswapV3Factory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ contract UniswapV3Factory is IUniswapV3PoolDeployer{
error UnsupportedTickSpacing();
error ZeroAddressNotAllowed();
error PoolAlreadyExists();
mapping(uint24=>bool) public tickSpacings;
PoolParameters public parameters;
mapping(address=> mapping(address=>mapping(uint24=>address))) public pools;
mapping(uint24=>uint24) public fees;

event PoolCreated(
address indexed token0,
Expand All @@ -20,41 +20,42 @@ contract UniswapV3Factory is IUniswapV3PoolDeployer{
);

constructor() {
tickSpacings[10] = true;
tickSpacings[60] = true;
fees[500] = 10;
fees[3000] = 60;
}

function createPool(
address tokenX,
address tokenY,
uint24 tickSpacing
uint24 fee
)public returns(address pool){
if(tokenX == tokenY) revert TokensMustBeDifferent();
if(!tickSpacings[tickSpacing]) revert UnsupportedTickSpacing();
if(fees[fee] == 0) revert UnsupportedTickSpacing();

(tokenX, tokenY) = tokenX < tokenY
? (tokenX, tokenY)
:(tokenY, tokenX);
if(tokenX == address(0)) revert ZeroAddressNotAllowed();
if(pools[tokenX][tokenY][tickSpacing] != address(0)) revert PoolAlreadyExists();
if(pools[tokenX][tokenY][fee] != address(0)) revert PoolAlreadyExists();

parameters = PoolParameters({
factory: address(this),
token0: tokenX,
token1: tokenY,
tickSpacing: tickSpacing
tickSpacing: fees[fee],
fee: fee
});
pool = address(
new UniswapV3Pool{
salt: keccak256(abi.encodePacked(tokenX, tokenY, tickSpacing))
salt: keccak256(abi.encodePacked(tokenX, tokenY, fee))
}()
);

delete parameters;

pools[tokenX][tokenY][tickSpacing] = pool;
pools[tokenY][tokenX][tickSpacing] = pool;
pools[tokenX][tokenY][fee] = pool;
pools[tokenY][tokenX][fee] = pool;

emit PoolCreated(tokenX, tokenY, tickSpacing, pool);
emit PoolCreated(tokenX, tokenY, fee, pool);
}
}
167 changes: 138 additions & 29 deletions src/UniswapV3Manager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,59 @@ import "./interfaces/IERC20.sol";
import "./interfaces/IUniswapV3Pool.sol";
import "./interfaces/IUniswapV3Manager.sol";
import "./lib/LiquidityMath.sol";
import "./lib/Path.sol";
import "./lib/PoolAddress.sol";
import "./lib/TickMath.sol";

contract UniswapV3Manager is IUniswapV3Manager {
using Path for bytes;

error SlippageCheckFailed(uint256 amount0, uint256 amount1);
error TooLittleReceived(uint256 amountOut);

address public immutable factory;

constructor(address factory_) {
factory = factory_;
}

function getPosition(GetPositionParams calldata params)
public
view
returns (
uint128 liquidity,
uint256 feeGrowthInside0LastX128,
uint256 feeGrowthInside1LastX128,
uint128 tokensOwed0,
uint128 tokensOwed1
)
{
IUniswapV3Pool pool = getPool(params.tokenA, params.tokenB, params.fee);

(
liquidity,
feeGrowthInside0LastX128,
feeGrowthInside1LastX128,
tokensOwed0,
tokensOwed1
) = pool.positions(
keccak256(
abi.encodePacked(
params.owner,
params.lowerTick,
params.upperTick
)
)
);
}

function mint(MintParams calldata params)
public
returns (uint256 amount0, uint256 amount1)
{
IUniswapV3Pool pool = IUniswapV3Pool(params.poolAddress);
IUniswapV3Pool pool = getPool(params.tokenA, params.tokenB, params.fee);

(uint160 sqrtPriceX96, ) = pool.slot0();
(uint160 sqrtPriceX96, , , , ) = pool.slot0();
uint160 sqrtPriceLowerX96 = TickMath.getSqrtRatioAtTick(
params.lowerTick
);
Expand Down Expand Up @@ -50,27 +91,99 @@ contract UniswapV3Manager is IUniswapV3Manager {
revert SlippageCheckFailed(amount0, amount1);
}

function swap(
address poolAddress_,
bool zeroForOne,
uint256 amountSpecified,
function swapSingle(SwapSingleParams calldata params)
public
returns (uint256 amountOut)
{
amountOut = _swap(
params.amountIn,
msg.sender,
params.sqrtPriceLimitX96,
SwapCallbackData({
path: abi.encodePacked(
params.tokenIn,
params.fee,
params.tokenOut
),
payer: msg.sender
})
);
}

function swap(SwapParams memory params) public returns (uint256 amountOut) {
address payer = msg.sender;
bool hasMultiplePools;

while (true) {
hasMultiplePools = params.path.hasMultiplePools();

params.amountIn = _swap(
params.amountIn,
hasMultiplePools ? address(this) : params.recipient,
0,
SwapCallbackData({
path: params.path.getFirstPool(),
payer: payer
})
);

if (hasMultiplePools) {
payer = address(this);
params.path = params.path.skipToken();
} else {
amountOut = params.amountIn;
break;
}
}

if (amountOut < params.minAmountOut)
revert TooLittleReceived(amountOut);
}

function _swap(
uint256 amountIn,
address recipient,
uint160 sqrtPriceLimitX96,
bytes calldata data
) public returns (int256, int256) {
return
IUniswapV3Pool(poolAddress_).swap(
msg.sender,
SwapCallbackData memory data
) internal returns (uint256 amountOut) {
(address tokenIn, address tokenOut, uint24 tickSpacing) = data
.path
.decodeFirstPool();

bool zeroForOne = tokenIn < tokenOut;

(int256 amount0, int256 amount1) = getPool(
tokenIn,
tokenOut,
tickSpacing
).swap(
recipient,
zeroForOne,
amountSpecified,
amountIn,
sqrtPriceLimitX96 == 0
? (
zeroForOne
? TickMath.MIN_SQRT_RATIO + 1
: TickMath.MAX_SQRT_RATIO - 1
)
: sqrtPriceLimitX96,
data
abi.encode(data)
);

amountOut = uint256(-(zeroForOne ? amount1 : amount0));
}

function getPool(
address token0,
address token1,
uint24 fee
) internal view returns (IUniswapV3Pool pool) {
(token0, token1) = token0 < token1
? (token0, token1)
: (token1, token0);
pool = IUniswapV3Pool(
PoolAddress.computeAddress(factory, token0, token1, fee)
);
}

function uniswapV3MintCallback(
Expand All @@ -90,26 +203,22 @@ contract UniswapV3Manager is IUniswapV3Manager {
function uniswapV3SwapCallback(
int256 amount0,
int256 amount1,
bytes calldata data
bytes calldata data_
) public {
IUniswapV3Pool.CallbackData memory extra = abi.decode(
data,
(IUniswapV3Pool.CallbackData)
);
SwapCallbackData memory data = abi.decode(data_, (SwapCallbackData));
(address tokenIn, address tokenOut, ) = data.path.decodeFirstPool();

if (amount0 > 0) {
IERC20(extra.token0).transferFrom(
extra.payer,
msg.sender,
uint256(amount0)
);
}
bool zeroForOne = tokenIn < tokenOut;

int256 amount = zeroForOne ? amount0 : amount1;

if (amount1 > 0) {
IERC20(extra.token1).transferFrom(
extra.payer,
if (data.payer == address(this)) {
IERC20(tokenIn).transfer(msg.sender, uint256(amount));
} else {
IERC20(tokenIn).transferFrom(
data.payer,
msg.sender,
uint256(amount1)
uint256(amount)
);
}
}
Expand Down
Loading

0 comments on commit 4182e1f

Please sign in to comment.