Skip to main content

SemiFungiblePositionManager

Git Source

Inherits: ERC1155, Multicall, TransientReentrancyGuard

Title: Semi-Fungible Position Manager (ERC1155) - a gas-efficient Uniswap V3 position manager.

Author: Axicon Labs Limited

Wraps Uniswap V3 positions with up to 4 legs behind an ERC1155 token.

Replaces the NonfungiblePositionManager.sol (ERC721) from Uniswap Labs.

State Variables
​

MINT
​

Flag used to indicate a regular position mint.

bool internal constant MINT = false

BURN
​

Flag used to indicate that a position burn (with a burnTokenId) is occurring.

bool internal constant BURN = true

FACTORY
​

Canonical Uniswap V3 Factory address.

Used to verify callbacks and initialize pools.

IUniswapV3Factory internal immutable FACTORY

MIN_ENFORCED_TICKFILL_COST
​

The approximate minimum amount of tokens it should require to fill maxLiquidityPerTick at the minimum and maximum enforced ticks.

uint256 internal immutable MIN_ENFORCED_TICKFILL_COST

SUPPLY_MULTIPLIER_TICKFILL
​

The multiplier, in basis points, to apply to the token supply and set as the minimum enforced tick fill cost if greater than MIN_ENFORCED_TICKFILL_COST.

uint256 internal immutable SUPPLY_MULTIPLIER_TICKFILL

s_addressToPoolData
​

Retrieve the corresponding poolId for a given Uniswap V3 pool address.

mapping(address univ3pool => mapping(uint256 vegoid => PoolData poolData)) internal s_addressToPoolData

s_poolIdToAddress
​

Retrieve the PoolData struct corresponding to a given poolId.

mapping(uint64 poolId => address univ3pool) internal s_poolIdToAddress

s_accountLiquidity
​

Retrieve the current liquidity state in a chunk for a given user.

removedAndNetLiquidity is a LeftRight. The right slot represents the liquidity currently sold (added) in the AMM owned by the user and

mapping(bytes32 positionKey => LeftRightUnsigned removedAndNetLiquidity) internal s_accountLiquidity

s_accountPremiumOwed
​

Per-liquidity accumulator for the premium owed by buyers on a given chunk, tokenType and account.

mapping(bytes32 positionKey => LeftRightUnsigned accountPremium) private s_accountPremiumOwed

s_accountPremiumGross
​

Per-liquidity accumulator for the premium earned by sellers on a given chunk, tokenType and account.

mapping(bytes32 positionKey => LeftRightUnsigned accountPremium) private s_accountPremiumGross

s_accountFeesBase
​

Per-liquidity accumulator for the fees collected on an account for a given chunk.

Base fees are stored as int128((feeGrowthInsideLastX128 * liquidity) / 2**128), which allows us to store the accumulated fees as int128 instead of uint256.

Right slot: int128 token0 base fees, Left slot: int128 token1 base fees.

feesBase represents the baseline fees collected by the position last time it was updated - this is recalculated every time the position is collected from with the new value.

mapping(bytes32 positionKey => LeftRightSigned baseFees0And1) internal s_accountFeesBase

Functions
​

constructor
​

Set the canonical Uniswap V3 Factory address.

constructor(IUniswapV3Factory _factory, uint256 _minEnforcedTickFillCost, uint256 _supplyMultiplierTickFill) ;

Parameters

NameTypeDescription
_factoryIUniswapV3FactoryThe canonical Uniswap V3 Factory address
_minEnforcedTickFillCostuint256The minimum amount of tokens it should require to fill maxLiquidityPerTick at the minimum and maximum enforced ticks
_supplyMultiplierTickFilluint256The multiplier, in basis points, to apply to the token supply and set as the minimum enforced tick fill cost if greater than MIN_ENFORCED_TICKFILL_COST

initializeAMMPool
​

Initialize a Uniswap V3 pool in the SFPM.

Revert if already initialized.

function initializeAMMPool(address token0, address token1, uint24 fee, uint8 vegoid)
external
returns (uint64 poolId);

Parameters

NameTypeDescription
token0addressThe contract address of token0 of the pool
token1addressThe contract address of token1 of the pool
feeuint24The fee level of the of the underlying Uniswap V3 pool, denominated in hundredths of bips
vegoiduint8

_getPoolId
​

Given an address to a Uniswap V3 pool, return its 64-bit ID as used in the TokenId of Panoptic.

function _getPoolId(address univ3pool, int24 tickSpacing, uint256 vegoid) internal pure returns (uint64);

Parameters

NameTypeDescription
univ3pooladdressThe address of the Uniswap V3 pool to get the ID of
tickSpacingint24The tick spacing of univ3pool
vegoiduint256

Returns

NameTypeDescription
<none>uint64A uint64 representing a fingerprint of the Uniswap V3 pool address

expandEnforcedTickRange
​

Recomputes and decreases minEnforcedTick and/or increases maxEnforcedTick for a given poolId if certain conditions are met.

This function will only have an effect if both conditions are met:

  • The token supply for one of the tokens was greater than MIN_ENFORCED_TICKFILL_COST at the last initializeAMMPool or expandEnforcedTickRangeForPool call for poolId
  • The token supply for one of the tokens meeting the first condition has decreased significantly since the last call

This function cannot decrease the absolute value of either enforced tick, i.e., it can only widen the range of possible ticks.

The purpose of this function is to prevent pools created while a large amount of one of the tokens was flash-minted from being stuck in a narrow tick range.

function expandEnforcedTickRange(uint64 poolId) external;

Parameters

NameTypeDescription
poolIduint64The poolId on which to expand the enforced tick range

uniswapV3MintCallback
​

Called after minting liquidity to a position.

Pays the pool tokens owed for the minted liquidity from the payer (always the caller).

function uniswapV3MintCallback(uint256 amount0Owed, uint256 amount1Owed, bytes calldata data) external;

Parameters

NameTypeDescription
amount0Oweduint256The amount of token0 due to the pool for the minted liquidity
amount1Oweduint256The amount of token1 due to the pool for the minted liquidity
databytesContains the payer address and the pool features required to validate the callback

uniswapV3SwapCallback
​

Called by the pool after executing a swap during an ITM option mint/burn.

Pays the pool tokens owed for the swap from the payer (always the caller).

function uniswapV3SwapCallback(int256 amount0Delta, int256 amount1Delta, bytes calldata data) external;

Parameters

NameTypeDescription
amount0Deltaint256The amount of token0 that was sent (negative) or must be received (positive) by the pool by the end of the swap. If positive, the callback must send that amount of token0 to the pool
amount1Deltaint256The amount of token1 that was sent (negative) or must be received (positive) by the pool by the end of the swap. If positive, the callback must send that amount of token1 to the pool
databytesContains the payer address and the pool features required to validate the callback

burnTokenizedPosition
​

Burn a new position containing up to 4 legs wrapped in a ERC1155 token.

Auto-collect all accumulated fees.

function burnTokenizedPosition(
bytes calldata poolKey,
TokenId tokenId,
uint128 positionSize,
int24 tickLimitLow,
int24 tickLimitHigh
) external nonReentrant returns (LeftRightUnsigned[4] memory, LeftRightSigned, int24);

Parameters

NameTypeDescription
poolKeybytesThe abi.encode(address) of the Uniswap V3 Pool
tokenIdTokenIdThe tokenId of the minted position, which encodes information about up to 4 legs
positionSizeuint128The number of contracts minted, expressed in terms of the asset
tickLimitLowint24The lower bound of an acceptable open interval for the ending price
tickLimitHighint24The upper bound of an acceptable open interval for the ending price

Returns

NameTypeDescription
<none>LeftRightUnsigned[4]An array of LeftRight encoded words containing the amount of token0 and token1 collected as fees for each leg
<none>LeftRightSignedThe net amount of token0 and token1 moved to/from the Uniswap V3 pool
<none>int24currentTick The current tick of the Uniswap pool after the burn

mintTokenizedPosition
​

Create a new position tokenId containing up to 4 legs.

function mintTokenizedPosition(
bytes calldata poolKey,
TokenId tokenId,
uint128 positionSize,
int24 tickLimitLow,
int24 tickLimitHigh
) external nonReentrant returns (LeftRightUnsigned[4] memory, LeftRightSigned, int24);

Parameters

NameTypeDescription
poolKeybytesThe abi.encode(address) of the Uniswap V3 Pool
tokenIdTokenIdThe tokenId of the minted position, which encodes information for up to 4 legs
positionSizeuint128The number of contracts minted, expressed in terms of the asset
tickLimitLowint24The lower bound of an acceptable open interval for the ending price
tickLimitHighint24The upper bound of an acceptable open interval for the ending price

Returns

NameTypeDescription
<none>LeftRightUnsigned[4]An array of LeftRight encoded words containing the amount of token0 and token1 collected as fees for each leg
<none>LeftRightSignedThe net amount of token0 and token1 moved to/from the Uniswap V3 pool
<none>int24currentTick The current tick of the Uniswap pool after the mint

safeTransferFrom
​

All ERC1155 transfers are disabled.

function safeTransferFrom(address, address, uint256, uint256, bytes calldata) public pure override;

safeBatchTransferFrom
​

All ERC1155 transfers are disabled.

function safeBatchTransferFrom(address, address, uint256[] calldata, uint256[] calldata, bytes calldata)
public
pure
override;

swapInAMM
​

Called to perform an ITM swap in the Uniswap pool to resolve any non-tokenType token deltas.

When a position is minted or burnt in-the-money (ITM) we are not 100% token0 or 100% token1: we have a mix of both tokens.

The swapping for ITM options is needed because only one of the tokens are "borrowed" by a user to create the position.

function swapInAMM(IUniswapV3Pool univ3pool, LeftRightSigned itmAmounts, uint256 asset)
internal
returns (LeftRightSigned totalSwapped);

Parameters

NameTypeDescription
univ3poolIUniswapV3PoolThe Uniswap pool in which to swap.
itmAmountsLeftRightSignedHow much to swap (i.e. how many tokens are ITM)
assetuint256The asset of the first leg of the tokenId (determines which token to swap into)

Returns

NameTypeDescription
totalSwappedLeftRightSignedThe token deltas swapped in the AMM

_createPositionInAMM
​

Create the position in the AMM defined by tokenId.

Loops over each leg in the tokenId and calls _createLegInAMM for each, which does the mint/burn in the AMM.

function _createPositionInAMM(
bytes calldata poolKey,
bool invertedLimits,
uint128 positionSize,
TokenId tokenId,
bool isBurn
) internal returns (LeftRightUnsigned[4] memory collectedByLeg, LeftRightSigned totalMoved);

Parameters

NameTypeDescription
poolKeybytesThe abi.encode(address) of the Uniswap V3 Pool
invertedLimitsboolWhether the inputted lower limit > upper limit
positionSizeuint128The size of the option position
tokenIdTokenIdThe option position
isBurnboolWhether a position is being minted (false) or burned (true)

Returns

NameTypeDescription
collectedByLegLeftRightUnsigned[4]An array of LeftRight encoded words containing the amount of token0 and token1 collected as fees for each leg
totalMovedLeftRightSignedThe net amount of funds moved to/from Uniswap

_createLegInAMM
​

Create the position in the AMM for a specific leg in the tokenId.

For the leg specified by the _leg input:

  • mints any new liquidity in the AMM needed (via _mintLiquidity)

  • burns any new liquidity in the AMM needed (via _burnLiquidity)

  • tracks all amounts minted and burned

To burn a position, the opposing position is "created" through this function, but we need to pass in a flag to indicate that so the removedLiquidity is updated.

function _createLegInAMM(
IUniswapV3Pool univ3pool,
TokenId tokenId,
uint256 leg,
LiquidityChunk liquidityChunk,
bool isBurn
) internal returns (LeftRightSigned moved, LeftRightUnsigned collectedSingleLeg);

Parameters

NameTypeDescription
univ3poolIUniswapV3PoolThe Uniswap pool
tokenIdTokenIdThe option position
leguint256The leg index that needs to be modified
liquidityChunkLiquidityChunkThe liquidity chunk in Uniswap represented by the leg
isBurnboolWhether a position is being burned (true) or minted (false)

Returns

NameTypeDescription
movedLeftRightSignedThe net amount of funds moved to/from Uniswap
collectedSingleLegLeftRightUnsignedLeftRight encoded words containing the amount of token0 and token1 collected as fees

_updateStoredPremia
​

Updates the premium accumulators for a chunk with the latest collected tokens.

function _updateStoredPremia(
bytes32 positionKey,
LeftRightUnsigned currentLiquidity,
LeftRightUnsigned collectedAmounts,
uint256 vegoid
) private;

Parameters

NameTypeDescription
positionKeybytes32A key representing a liquidity chunk/range in Uniswap
currentLiquidityLeftRightUnsignedThe total amount of liquidity in the AMM for the specified chunk
collectedAmountsLeftRightUnsignedThe amount of tokens (token0 and token1) collected from Uniswap
vegoiduint256

_getFeesBase
​

Compute an up-to-date feeGrowth value without a poke.

Stored fees base is rounded up and the current fees base is rounded down to minimize the amount of fees collected (Ξ”feesbase) in favor of the protocol.

function _getFeesBase(IUniswapV3Pool univ3pool, uint128 liquidity, LiquidityChunk liquidityChunk, bool roundUp)
private
view
returns (LeftRightSigned feesBase);

Parameters

NameTypeDescription
univ3poolIUniswapV3PoolThe Uniswap pool
liquidityuint128The total amount of liquidity in the AMM for the specific position
liquidityChunkLiquidityChunkThe liquidity chunk in Uniswap to compute the feesBase for
roundUpboolIf true, round up the feesBase, otherwise round down

_mintLiquidity
​

Mint a chunk of liquidity (liquidityChunk) in the Uniswap V3 pool; return the amount moved.

function _mintLiquidity(LiquidityChunk liquidityChunk, IUniswapV3Pool univ3pool)
internal
returns (LeftRightSigned movedAmounts);

Parameters

NameTypeDescription
liquidityChunkLiquidityChunkThe liquidity chunk in Uniswap to mint
univ3poolIUniswapV3PoolThe Uniswap V3 pool to mint liquidity in/to

Returns

NameTypeDescription
movedAmountsLeftRightSignedHow many tokens were moved from msg.sender to Uniswap

_burnLiquidity
​

Burn a chunk of liquidity (liquidityChunk) in the Uniswap V3 pool and send to msg.sender; return the amount moved.

function _burnLiquidity(LiquidityChunk liquidityChunk, IUniswapV3Pool univ3pool)
internal
returns (LeftRightSigned movedAmounts);

Parameters

NameTypeDescription
liquidityChunkLiquidityChunkThe liquidity chunk in Uniswap to burn
univ3poolIUniswapV3PoolThe Uniswap V3 pool to burn liquidity in/from

Returns

NameTypeDescription
movedAmountsLeftRightSignedHow many tokens were moved from Uniswap to msg.sender

_collectAndWritePositionData
​

Helper to collect amounts between msg.sender and Uniswap and also to update the Uniswap fees collected to date from the AMM.

function _collectAndWritePositionData(
LiquidityChunk liquidityChunk,
IUniswapV3Pool univ3pool,
LeftRightUnsigned currentLiquidity,
bytes32 positionKey,
LeftRightSigned movedInLeg,
uint256 isLong,
uint256 vegoid
) internal returns (LeftRightUnsigned collectedChunk);

Parameters

NameTypeDescription
liquidityChunkLiquidityChunkThe liquidity chunk in Uniswap to collect from
univ3poolIUniswapV3PoolThe Uniswap pool where the position is deployed
currentLiquidityLeftRightUnsignedThe existing liquidity msg.sender owns in the AMM for this chunk before the SFPM was called
positionKeybytes32The unique key to identify the liquidity chunk/tokenType pairing in this Uniswap pool
movedInLegLeftRightSignedHow many tokens have been moved between msg.sender and Uniswap before this function call
isLonguint256Whether the leg in question is long (=1) or short (=0)
vegoiduint256

Returns

NameTypeDescription
collectedChunkLeftRightUnsignedThe amount of tokens collected from Uniswap

_getPremiaDeltas
​

Compute deltas for Owed/Gross premium given quantities of tokens collected from Uniswap.

Returned accumulators are capped at the max value (2^128 - 1) for each token if they overflow.

function _getPremiaDeltas(LeftRightUnsigned currentLiquidity, LeftRightUnsigned collectedAmounts, uint256 vegoid)
private
pure
returns (LeftRightUnsigned deltaPremiumOwed, LeftRightUnsigned deltaPremiumGross);

Parameters

NameTypeDescription
currentLiquidityLeftRightUnsignedNetLiquidity (right) and removedLiquidity (left) at the start of the transaction
collectedAmountsLeftRightUnsignedTotal amount of tokens (token0 and token1) collected from Uniswap
vegoiduint256

Returns

NameTypeDescription
deltaPremiumOwedLeftRightUnsignedThe extra premium (per liquidity X64) to be added to the owed accumulator for token0 (right) and token1 (left)
deltaPremiumGrossLeftRightUnsignedThe extra premium (per liquidity X64) to be added to the gross accumulator for token0 (right) and token1 (left)

getAccountLiquidity
​

Return the liquidity associated with a given liquidity chunk/tokenType for a user on a Uniswap pool.

function getAccountLiquidity(
bytes calldata poolKey,
address owner,
uint256 tokenType,
int24 tickLower,
int24 tickUpper
) external view returns (LeftRightUnsigned accountLiquidities);

Parameters

NameTypeDescription
poolKeybytesThe abi.encode(address) of the Uniswap V3 Pool
owneraddressThe address of the account that is queried
tokenTypeuint256The tokenType of the position
tickLowerint24The lower end of the tick range for the position
tickUpperint24The upper end of the tick range for the position

Returns

NameTypeDescription
accountLiquiditiesLeftRightUnsignedThe amount of liquidity that held in and removed from Uniswap for that chunk (netLiquidity:removedLiquidity -> rightSlot:leftSlot)

getAccountPremium
​

Return the premium associated with a given position, where premium is an accumulator of feeGrowth for the touched position.

If an atTick parameter is provided that is different from type(int24).max, then it will update the premium up to the current block at the provided atTick value. We do this because this may be called immediately after the Uniswap V3 pool has been touched, so no need to read the feeGrowths from the Uniswap V3 pool.

function getAccountPremium(
bytes calldata poolKey,
address owner,
uint256 tokenType,
int24 tickLower,
int24 tickUpper,
int24 atTick,
uint256 isLong,
uint256 vegoid
) external view returns (uint128, uint128);

Parameters

NameTypeDescription
poolKeybytesThe abi.encode(address) of the Uniswap V3 Pool
owneraddressThe address of the account that is queried
tokenTypeuint256The tokenType of the position
tickLowerint24The lower end of the tick range for the position
tickUpperint24The upper end of the tick range for the position
atTickint24The current tick. Set atTick < (type(int24).max = 8388608) to get latest premium up to the current block
isLonguint256Whether the position is long (=1) or short (=0)
vegoiduint256

Returns

NameTypeDescription
<none>uint128The amount of premium (per liquidity X64) for token0 = sum(feeGrowthLast0X128) over every block where the position has been touched
<none>uint128The amount of premium (per liquidity X64) for token1 = sum(feeGrowthLast0X128) over every block where the position has been touched

getAccountFeesBase
​

Return the feesBase associated with a given liquidity chunk.

function getAccountFeesBase(
bytes calldata poolKey,
address owner,
uint256 tokenType,
int24 tickLower,
int24 tickUpper
) external view returns (int128 feesBase0, int128 feesBase1);

Parameters

NameTypeDescription
poolKeybytesThe abi.encode(address) of the Uniswap V3 Pool
owneraddressThe address of the account that is queried
tokenTypeuint256The tokenType of the position (the token it started as)
tickLowerint24The lower end of the tick range for the position
tickUpperint24The upper end of the tick range for the position

Returns

NameTypeDescription
feesBase0int128The feesBase of the position for token0
feesBase1int128The feesBase of the position for token1

getUniswapV3PoolFromId
​

Returns the Uniswap pool for a given poolId.

function getUniswapV3PoolFromId(uint64 poolId) external view returns (IUniswapV3Pool uniswapV3Pool);

Parameters

NameTypeDescription
poolIduint64The unique pool identifier for a Uniswap V3 pool

Returns

NameTypeDescription
uniswapV3PoolIUniswapV3PoolThe Uniswap pool corresponding to poolId

getEnforcedTickLimits
​

Returns the current enforced tick limits for a given poolId.

function getEnforcedTickLimits(uint64 poolId) external view returns (int24, int24);

Parameters

NameTypeDescription
poolIduint64The unique pool identifier for a Uniswap V3 pool

Returns

NameTypeDescription
<none>int24The minimum enforced tick for chunks created in the pool corresponding to poolId
<none>int24The maximum enforced tick for chunks created in the pool corresponding to poolId

getPoolId
​

Returns the poolId for a given Uniswap pool.

function getPoolId(bytes memory id, uint8 vegoid) external view returns (uint64 poolId);

Parameters

NameTypeDescription
idbytesThe address of the Uniswap Pool
vegoiduint8

Returns

NameTypeDescription
poolIduint64The unique pool identifier corresponding to univ3pool

getCurrentTick
​

Returns the current tick of a given Uniswap V3 pool

function getCurrentTick(bytes memory poolKey) public view returns (int24 currentTick);

Parameters

NameTypeDescription
poolKeybytesThe abi.encode(address) of the Uniswap V3 Pool

Returns

NameTypeDescription
currentTickint24The current tick of the Uniswap pool

Events
​

PoolInitialized
​

Emitted when a UniswapV3Pool is initialized in the SFPM.

event PoolInitialized(address indexed uniswapPool, uint64 poolId, int24 minEnforcedTick, int24 maxEnforcedTick);

Parameters

NameTypeDescription
uniswapPooladdressAddress of the underlying Uniswap V3 pool
poolIduint64The SFPM's pool identifier for the pool, including the 16-bit tick spacing and 48-bit pool pattern
minEnforcedTickint24The initial minimum enforced tick for the pool
maxEnforcedTickint24The initial maximum enforced tick for the pool

EnforcedTicksUpdated
​

Emitted when the enforced tick range is expanded for a given poolId.

Will be emitted on any expandEnforcedTickRange call, even if the enforced ticks are not actually changed.

event EnforcedTicksUpdated(address indexed uniswapPool, int24 minEnforcedTick, int24 maxEnforcedTick);

Parameters

NameTypeDescription
uniswapPooladdressAddress of the underlying Uniswap V3 pool
minEnforcedTickint24The new minimum enforced tick for the pool
maxEnforcedTickint24The new maximum enforced tick for the pool

TokenizedPositionBurnt
​

Emitted when a position is destroyed/burned.

event TokenizedPositionBurnt(address indexed recipient, TokenId indexed tokenId, uint128 positionSize);

Parameters

NameTypeDescription
recipientaddressThe address of the user who burned the position
tokenIdTokenIdThe tokenId of the burned position
positionSizeuint128The number of contracts burnt, expressed in terms of the asset

TokenizedPositionMinted
​

Emitted when a position is created/minted.

event TokenizedPositionMinted(address indexed caller, TokenId indexed tokenId, uint128 positionSize);

Parameters

NameTypeDescription
calleraddressThe address of the user who minted the position
tokenIdTokenIdThe tokenId of the minted position
positionSizeuint128The number of contracts minted, expressed in terms of the asset

LiquidityChunkUpdated
​

Emitted when liquidity is modified in a chunk during position mint/burn.

This event provides detailed information about liquidity changes per chunk, simplifying indexing.

event LiquidityChunkUpdated(
address indexed univ3pool,
address indexed owner,
uint256 indexed tokenType,
int24 tickLower,
int24 tickUpper,
int128 liquidityDelta
);

Parameters

NameTypeDescription
univ3pooladdressThe Uniswap V3 pool address
owneraddressThe owner of the position
tokenTypeuint256The type of token for this leg (token0 or token1)
tickLowerint24The lower tick of the liquidity chunk
tickUpperint24The upper tick of the liquidity chunk
liquidityDeltaint128The signed change in liquidity (positive for additions, negative for removals)