Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

singleton uniswap contract #13

Merged
merged 42 commits into from
Jan 4, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
173c71d
first pass: turn the pool into a library
moodysalem Nov 11, 2021
dbbfcf6
remove IConfiguration.sol
moodysalem Nov 11, 2021
d21b6d4
trying to get the refactor to compile
moodysalem Nov 11, 2021
4791e96
more structifying, still not compiling
moodysalem Nov 11, 2021
e866db7
mint/burn compiling
moodysalem Nov 11, 2021
134f8b2
swap compiling!
moodysalem Nov 11, 2021
5a9ef73
add a bytecode test and an initialize test
moodysalem Nov 11, 2021
e1e1cb5
initialize gas test
moodysalem Nov 11, 2021
424eb1b
remove unused contracts
moodysalem Nov 11, 2021
78a2468
Merge branch 'main' into singleton
moodysalem Nov 11, 2021
2a81316
check the pool is initialized, mint test
moodysalem Nov 12, 2021
b9f26cd
Merge branch 'main' into singleton
moodysalem Nov 12, 2021
a5d5675
swap gas test
moodysalem Nov 12, 2021
5dbbfa5
move the struct to where it's used
moodysalem Nov 12, 2021
a14e662
add some logic for max liquidity per tick and tick spacings
moodysalem Nov 12, 2021
29f32f6
get all the code into the pool manager
moodysalem Nov 12, 2021
4c80ad8
feature parity
moodysalem Nov 12, 2021
45c4743
adapt how fees are handled to match the pattern of the vault
moodysalem Nov 13, 2021
6d1ee75
remove the vault
moodysalem Nov 13, 2021
fda8d10
trimming things that are not necessary anymore
moodysalem Nov 16, 2021
6eaf3f7
add the lock function and require every call to be within the context…
moodysalem Nov 18, 2021
17c7026
think payment is figured out
moodysalem Nov 19, 2021
1a621ed
use reserves
moodysalem Nov 19, 2021
a6d4a02
build in no delegate call
moodysalem Nov 19, 2021
8b8b115
start testing the lock function, pass through some data, add interfaces
moodysalem Nov 19, 2021
acae23c
remove the generic stuff
moodysalem Nov 19, 2021
4a81c6f
add the swap test code that should work
moodysalem Nov 19, 2021
9b608d5
unskip the tests
moodysalem Nov 19, 2021
d8a1c7f
improve how settle works (only one callback)
moodysalem Nov 20, 2021
2ce95e5
remove bloom filter for now
moodysalem Nov 20, 2021
a6b9ebe
cheaper no-op swap
moodysalem Nov 20, 2021
0b6e131
ts error
moodysalem Nov 20, 2021
637a801
initialize the fee growth global in gas test
moodysalem Nov 21, 2021
e252852
get the gas tests for swaps running
moodysalem Nov 23, 2021
8d267c7
remove file
moodysalem Nov 23, 2021
a2a14bd
remove more unused files
moodysalem Nov 23, 2021
f25da29
bring back old file
moodysalem Nov 23, 2021
3ae8b18
rename
moodysalem Nov 23, 2021
d4de24d
run the tests
moodysalem Nov 23, 2021
ddc7201
cheaper mapping
moodysalem Nov 23, 2021
807579f
fix the account delta function to correctly append tokens to set
moodysalem Nov 23, 2021
21700b1
rename
moodysalem Nov 30, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
900 changes: 0 additions & 900 deletions contracts/Pool.sol

This file was deleted.

38 changes: 0 additions & 38 deletions contracts/PoolDeployer.sol

This file was deleted.

73 changes: 0 additions & 73 deletions contracts/PoolFactory.sol

This file was deleted.

262 changes: 262 additions & 0 deletions contracts/PoolManager.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,262 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.10;

import {Pool} from './libraries/Pool.sol';
import {Tick} from './libraries/Tick.sol';
import {SafeCast} from './libraries/SafeCast.sol';

import {IERC20Minimal} from './interfaces/external/IERC20Minimal.sol';
import {NoDelegateCall} from './NoDelegateCall.sol';
import {IPoolManager} from './interfaces/IPoolManager.sol';
import {ILockCallback} from './interfaces/callback/ILockCallback.sol';

import {console} from 'hardhat/console.sol';

/// @notice Holds the state for all pools
contract PoolManager is IPoolManager, NoDelegateCall {
using SafeCast for *;
using Pool for *;

/// @notice Represents the configuration for a fee
struct FeeConfig {
int24 tickSpacing;
uint128 maxLiquidityPerTick;
}

// todo: can we make this documented in the interface
mapping(bytes32 => Pool.State) public pools;
mapping(uint24 => FeeConfig) public override configs;

constructor() {
_configure(100, 1);
_configure(500, 10);
_configure(3000, 60);
_configure(10000, 200);
}

function _configure(uint24 fee, int24 tickSpacing) internal {
require(tickSpacing > 0);
require(configs[fee].tickSpacing == 0);
configs[fee].tickSpacing = tickSpacing;
configs[fee].maxLiquidityPerTick = Tick.tickSpacingToMaxLiquidityPerTick(tickSpacing);
// todo: emit event
}

/// @dev For mocking in unit tests
function _blockTimestamp() internal view virtual returns (uint32) {
return uint32(block.timestamp);
}

function _getPool(IPoolManager.PoolKey memory key) private view returns (Pool.State storage) {
return pools[keccak256(abi.encode(key))];
}

/// @notice Initialize the state for a given pool ID
function initialize(IPoolManager.PoolKey memory key, uint160 sqrtPriceX96) external override returns (int24 tick) {
tick = _getPool(key).initialize(_blockTimestamp(), sqrtPriceX96);
}

/// @notice Increase the maximum number of stored observations for the pool's oracle
function increaseObservationCardinalityNext(IPoolManager.PoolKey memory key, uint16 observationCardinalityNext)
external
override
returns (uint16 observationCardinalityNextOld, uint16 observationCardinalityNextNew)
{
(observationCardinalityNextOld, observationCardinalityNextNew) = _getPool(key)
.increaseObservationCardinalityNext(observationCardinalityNext);
}

/// @notice Represents the address that has currently locked the pool
address public override lockedBy;

/// @notice All the latest tracked balances of tokens
mapping(IERC20Minimal => uint256) public override reservesOf;

/// @notice Internal transient enumerable set
IERC20Minimal[] public override tokensTouched;
struct PositionAndDelta {
uint8 slot;
int248 delta;
}
mapping(IERC20Minimal => PositionAndDelta) public override tokenDelta;

function lock(bytes calldata data) external override returns (bytes memory result) {
require(lockedBy == address(0));
lockedBy = msg.sender;

// the caller does everything in this callback, including paying what they owe via calls to settle
result = ILockCallback(msg.sender).lockAcquired(data);

unchecked {
for (uint256 i = 0; i < tokensTouched.length; i++) {
require(tokenDelta[tokensTouched[i]].delta == 0, 'Not settled');
delete tokenDelta[tokensTouched[i]];
}
}
delete tokensTouched;
delete lockedBy;
}

/// @dev Adds a token to a unique list of tokens that have been touched
function _addTokenToSet(IERC20Minimal token) internal returns (uint8 slot) {
uint256 len = tokensTouched.length;
if (len == 0) {
tokensTouched.push(token);
return 0;
}

PositionAndDelta storage pd = tokenDelta[token];
slot = pd.slot;

if (slot == 0 && tokensTouched[slot] != token) {
require(len < type(uint8).max);
slot = uint8(len);
pd.slot = slot;
tokensTouched.push(token);
}
}

function _accountDelta(IERC20Minimal token, int256 delta) internal {
if (delta == 0) return;
_addTokenToSet(token);
tokenDelta[token].delta += int248(delta);
}

/// @dev Accumulates a balance change to a map of token to balance changes
function _accountPoolBalanceDelta(PoolKey memory key, Pool.BalanceDelta memory delta) internal {
_accountDelta(key.token0, delta.amount0);
_accountDelta(key.token1, delta.amount1);
}

modifier onlyByLocker() {
require(msg.sender == lockedBy, 'LOK');
_;
}

/// @dev Mint some liquidity for the given pool
function mint(IPoolManager.PoolKey memory key, IPoolManager.MintParams memory params)
external
override
noDelegateCall
onlyByLocker
returns (Pool.BalanceDelta memory delta)
{
require(params.amount > 0);

FeeConfig memory config = configs[key.fee];

delta = _getPool(key).modifyPosition(
Pool.ModifyPositionParams({
owner: params.recipient,
tickLower: params.tickLower,
tickUpper: params.tickUpper,
liquidityDelta: int256(uint256(params.amount)).toInt128(),
time: _blockTimestamp(),
maxLiquidityPerTick: config.maxLiquidityPerTick,
tickSpacing: config.tickSpacing
})
);

_accountPoolBalanceDelta(key, delta);
}

/// @dev Mint some liquidity for the given pool
function burn(IPoolManager.PoolKey memory key, IPoolManager.BurnParams memory params)
external
override
noDelegateCall
onlyByLocker
returns (Pool.BalanceDelta memory delta)
{
require(params.amount > 0);

FeeConfig memory config = configs[key.fee];

delta = _getPool(key).modifyPosition(
Pool.ModifyPositionParams({
owner: msg.sender,
tickLower: params.tickLower,
tickUpper: params.tickUpper,
liquidityDelta: -int256(uint256(params.amount)).toInt128(),
time: _blockTimestamp(),
maxLiquidityPerTick: config.maxLiquidityPerTick,
tickSpacing: config.tickSpacing
})
);

_accountPoolBalanceDelta(key, delta);
}

function swap(IPoolManager.PoolKey memory key, IPoolManager.SwapParams memory params)
external
override
noDelegateCall
onlyByLocker
returns (Pool.BalanceDelta memory delta)
{
FeeConfig memory config = configs[key.fee];

delta = _getPool(key).swap(
Pool.SwapParams({
time: _blockTimestamp(),
fee: key.fee,
tickSpacing: config.tickSpacing,
zeroForOne: params.zeroForOne,
amountSpecified: params.amountSpecified,
sqrtPriceLimitX96: params.sqrtPriceLimitX96
})
);

_accountPoolBalanceDelta(key, delta);
}

/// @notice Called by the user to net out some value owed to the user
/// @dev Can also be used as a mechanism for _free_ flash loans
function take(
IERC20Minimal token,
address to,
uint256 amount
) external override noDelegateCall onlyByLocker {
_accountDelta(token, amount.toInt256());
reservesOf[token] -= amount;
token.transfer(to, amount);
}

/// @notice Called by the user to pay what is owed
function settle(IERC20Minimal token) external override noDelegateCall onlyByLocker returns (uint256 paid) {
uint256 reservesBefore = reservesOf[token];
reservesOf[token] = token.balanceOf(address(this));
paid = reservesOf[token] - reservesBefore;
// subtraction must be safe
_accountDelta(token, -(paid.toInt256()));
}

/// @notice Update the protocol fee for a given pool
function setFeeProtocol(IPoolManager.PoolKey calldata key, uint8 feeProtocol)
external
override
returns (uint8 feeProtocolOld)
{
return _getPool(key).setFeeProtocol(feeProtocol);
}

/// @notice Observe a past state of a pool
function observe(IPoolManager.PoolKey calldata key, uint32[] calldata secondsAgos)
external
view
override
noDelegateCall
returns (int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulativeX128s)
{
return _getPool(key).observe(_blockTimestamp(), secondsAgos);
}

/// @notice Get the snapshot of the cumulative values of a tick range
function snapshotCumulativesInside(
IPoolManager.PoolKey calldata key,
int24 tickLower,
int24 tickUpper
) external view override noDelegateCall returns (Pool.Snapshot memory) {
return _getPool(key).snapshotCumulativesInside(tickLower, tickUpper, _blockTimestamp());
}
}
17 changes: 0 additions & 17 deletions contracts/interfaces/IPool.sol

This file was deleted.

Loading