Code Monkey home page Code Monkey logo

v4-core's Introduction

Uniswap v4 Core

Lint Tests

Uniswap v4 is a new automated market maker protocol that provides extensible and customizable pools. v4-core hosts the core pool logic for creating pools and executing pool actions like swapping and providing liquidity.

The contracts in this repo are in early stages - we are releasing the draft code now so that v4 can be built in public, with open feedback and meaningful community contribution. We expect this will be a months-long process, and we appreciate any kind of contribution, no matter how small.

Contributing

If you’re interested in contributing please see our contribution guidelines!

Whitepaper

A more detailed description of Uniswap v4 Core can be found in the draft of the Uniswap v4 Core Whitepaper.

Architecture

v4-core uses a singleton-style architecture, where all pool state is managed in the PoolManager.sol contract. Pool actions can be taken after an initial call to unlock. Integrators implement the unlockCallback and proceed with any of the following actions on the pools:

  • swap
  • modifyLiquidity
  • donate
  • take
  • settle
  • mint
  • burn

Note that pool initialization can happen outside the context of unlocking the PoolManager.

Only the net balances owed to the user (positive) or to the pool (negative) are tracked throughout the duration of an unlock. This is the delta field held in the unlock state. Any number of actions can be run on the pools, as long as the deltas accumulated during the unlock reach 0 by the unlock’s release. This unlock and call style architecture gives callers maximum flexibility in integrating with the core code.

Additionally, a pool may be initialized with a hook contract, that can implement any of the following callbacks in the lifecycle of pool actions:

  • {before,after}Initialize
  • {before,after}AddLiquidity
  • {before,after}RemoveLiquidity
  • {before,after}Swap
  • {before,after}Donate

The callback logic, may be updated by the hooks dependent on their implementation. However which callbacks are executed on a pool cannot change after pool initialization.

Repository Structure

All contracts are held within the v4-core/src folder.

Note that helper contracts used by tests are held in the v4-core/src/test subfolder within the src folder. Any new test helper contracts should be added here, but all foundry tests are in the v4-core/test folder.

src/
----interfaces/
    | IPoolManager.sol
    | ...
----libraries/
    | Position.sol
    | Pool.sol
    | ...
----test
----PoolManager.sol
...
test/
----libraries/
    | Position.t.sol
    | Pool.t.sol

Local deployment and Usage

To utilize the contracts and deploy to a local testnet, you can install the code in your repo with forge:

forge install https://github.com/Uniswap/v4-core

To integrate with the contracts, the interfaces are available to use:

import {IPoolManager} from 'v4-core/contracts/interfaces/IPoolManager.sol';
import {IUnlockCallback} from 'v4-core/contracts/interfaces/callback/IUnlockCallback.sol';

contract MyContract is IUnlockCallback {
    IPoolManager poolManager;

    function doSomethingWithPools() {
        // this function will call `unlockCallback` below
        poolManager.unlock(...);
    }

    function unlockCallback(bytes calldata data) external returns (bytes memory) {
        // perform pool actions
        poolManager.swap(...)
    }
}

License

The primary license for Uniswap V4 Core is the Business Source License 1.1 (BUSL-1.1), seeΒ LICENSE. Minus the following exceptions:

Each of these files states their license type.

v4-core's People

Contributors

0xvolosnikov avatar aadams avatar danrobinson avatar devtooligan avatar dianakocsis avatar emmaguo13 avatar ewilz avatar gakonst avatar gitcoindev avatar gretzke avatar haydenadams avatar hensha256 avatar hyunchel avatar k06a avatar lint-action avatar marktoda avatar moodysalem avatar nishim3 avatar noahzinsmeister avatar partylikeits1983 avatar penandlim avatar prosperring avatar saucepoint avatar shuhuiluo avatar sipox11 avatar snreynolds avatar willpote avatar yorkemartin avatar zemse avatar zhongeric avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

v4-core's Issues

Custom ERC1155 contract

This will enable us to have optimisations and simplifications over the generic OZ one.

Personalisation of the transfer function:
If the to address is the PoolManager, and the PoolManager is locked, automatically burn the tokens and update the account delta - no need to call the receiving hook

introduce hooks

start by trying to move the fee collection and accounting logic into hooks twamm feature into hooks

experiment: move tick spacing to the pool key

rationale is since fragmentation within the singleton is cheaper, it's not as important to limit it at the factory level

pros:

  • gov minimization
  • market driven parameter

cons:

  • fragmentation

alternately, make the fee -> tickSpacing a pluggable contract, which stores them as immutables
this still costs as much or more than the current code, but can be more flexible in allowing well-spaced tickSpacings

need to check we have enough gas before fetching protocol fee

i think we have the problem here that there may be less than controllerGasLimit gas left, and the call will be made with that much gas instead of controllerGasLimit but the transaction will still succeed even if the fetch protocol fee call ran out of gas. we need an explicit gas check here to prevent the caller from sending a gas amount that will result in less than controllerGasLimit being allotted to fetching the protocol fee

https://github.com/Uniswap/core-next/blob/9e329dcce8d908e898f8f807b29d0c9a4fde45dd/contracts/PoolManager.sol#L385-L389

Put the V3 style oracle into a hook

since hooks can affect price or liquidity "between" blocks, e.g. add liquidity based on time, or execute swaps based on time, the oracle feature is incompatible with pools that have these kinds of hooks

we should consider externalizing any time based features to hooks (e.g. seconds per liquidity and tick/seconds per liquidity accumulators)

the nice benefit is that since these can be opt-in via hooks, pools that don't need the features will be cheaper to use. it also removes all references to time from the pool manager, which makes the code more portable to networks where time is unreliable or more manipulable

fuzz test pool library for solvency

this should be much easier to do now that pool logic is in a library. verify that via calls to modify position and swap, all positions will always be solvent. we should probably use mythx for this. tried it with forge, but it has some drawbacks over echidna/mythx style fuzz tests, mainly does not test sequences of transactions, but handling reverts is also painful
b18e172

Simple Hooks: Framework

  • Using initial bits to determine which hooks to call
  • Define where the hooks will be
  • In Hook constructor user specifies where they want hooks to be called
  • have some validate function that checks the address is valid given where they specified the hooks to be called (put this in a hook library)
library Hook {
	uint256 BEFORE_SWAP_BIT = 0b000001;
	uint256 AFTER_SWAP_BIT  = 0b000010;

	function validate(params) internal {
		if (params.beforeSwap && address(this) & 1 != 1) revert();
	}

	function shouldCall()
}

interface IHook {
	function beforeModifyPosition(ModifyPositionParams calldata params) external;
	function beforeSwap() external;
	// ...
}


contract MyHook is IHook {
	constructor() {
		Hook.validate(
			HookValidateParams({
				beforeSwap: true,
				afterSwap: false,
				beforeModifyPosition: true,
				afterModifyPosition: true,
				...
			})
		);
	}
}
  • Before first interaction in each block (swap/mint/burn/etc.)
  • Before mint
  • Before burn
  • Before swap
  • After swap
  • Before tick crossing

positions that span 1 underlying tick and are kicked out when you cross

similar to #22 but orders span 1 underlying tick instead of 1 tickSpacing. this would give limit order placers a tighter range/lower likelihood of partial fills.

ideally could be implemented via packing additional accounting data into the "starting" tick, and treating the tick+1 as a special case that might not need, for example, an entry in the tick bitmap.

implement TWAMM on top of the singleton contract

https://www.paradigm.xyz/2021/07/twamm/

  • SubmitOrder / Order Expiration

    • Decide whether expiration should be set by date or by n-intervals. I was fooling around with deterministic orderIds in which case only date works. If by n-intervals, expiration (which is the stored value) is not determined until tx is mined. with weiroll type txs, deterministic id's may not even be important at all, but weiroll is a bit gas guzzling. Honestly I just like the deterministic aspect of expiration timestamp better. But maybe length-of-time is the bigger usecase?
    • Then make expiration calculations to set accordingly.
  • Cancel LongTerm Orders

    • Do calculations for order cancellations
    • Update user deltas in Pool Manager (#34)
  • ClaimEarnings

    • Update user deltas in Pool Manager (#34)
  • executeTWAMMOrders (@ewilz currently looking into this)

    • Use pool state + twamm state to calculate new state sans fees/initialized ticks
    • Introduce pool fees into calculation
    • Introduce Initialized ticks into calculation
    • update twamm state (OrderPool earningsFactor params)
    • execute swap on pool
    • update TWAP state if necessary
    • Address order pool with 0 trades conditions
  • Edgecases to test/address:

    • TWAMM pushes pool price past highest/lowest price
    • when checking for tick crossings: don't return true if initialized tick directly after target price (might be a bug rn)
    • if executed exactly on expiry, order should be fully complete (must do +1 rn)
    • ddos attack on less active pools (too many expiries to process before gas runs out)
    • large orders trading over too little liquidity currently causes math overflow
    • try testing with "extreme tick spacing" (getting out of gas error with tickspacing of 1)
    • swapping on 0 liquidity pools: single pool sell + both pools selling
    • single pool sells to max/min
  • Features

    • Optimize expiry lookup with bitmap of active expiry periods.
    • Add Events??
  • ABDK to fix math

consider charging an exclusive protocol fee

right now, the protocol fee on swap is inclusive, i.e. 1/6 or 5 bips of a 30 bips pool

if the protocol fee on swap is turned on, it might be less mental overhead if it were exclusive, i.e. 5 bips on top of a 30 bips pool, for 35 bips total

additional hooks validation

we should do two additional validations for hooks to limit the number of duplicate pools with invalid hooks, i.e. hooks that are never called, or hooks that are called but do not contain any code (e.g. arbitrary random address with no code)

  • we should check that when calling a hook the return value is the selector of that hook, or we revert, e.g.:
if (key.hooks.shouldCallBeforeInitialize()) {
    require(key.hooks.beforeInitialize(msg.sender, key, sqrtPriceX96) == IHooks.beforeInitialize.selector);
}
  • we should validate in initialize that the hook address either has at least 1 set upper bit (called at at least one hook point), or is the address 0

put the pool logic in the factory contract (singleton uniswap contract)

tracked here: https://github.com/Uniswap/core-experiments/tree/singleton

benefits:

  • lower gas fees for hopping between pools (no transfers are necessary)
  • fewer storage slots for tracking a token's balance
  • aggregation is way cheaper
  • allows you to flash anything across all pools (e.g. any combination of swap/mint/burn for any pool, huge network effects)
  • allows you to flash borrow the entire aggregate balance of a token from uniswap

Fix: edge cases - earnings calculations on price boundaries

  • also requires refactor of calculateExecutionUpdates for gas savings & logical flow.
  • TWAMM pushes pool price past highest/lowest price
    • case 1: There is no liquidity at the highest/lowest prices so final price should not be max/min price.
    • case 2: There is liquidity at the highest/lowest prices so final price should be max price or min price.

future:

  • when checking for tick crossings: don't return true if initialized tick directly after target price (might be a bug rn)
  • if executed exactly on expiry, order should be fully complete (must do +1 rn )
    • also check exact expiry for both selling pool condition

missing events in pool manager

the following are missing any events and probably should have them:

  • pool initialize
  • modify position
  • swap

don't emit events and maybe shouldn't

  • settle (paying will emit an ERC20 transfer to pool)
  • take (taking will emit an ERC20 transfer from pool)

probably as a rule of thumb anything that deals with transient deltas wouldn't need events

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.