| Transaction Hash |
Method
|
Block
|
From
|
|
To
|
||||
|---|---|---|---|---|---|---|---|---|---|
Latest 25 internal transactions (View All)
Advanced mode:
| Parent Transaction Hash | Method | Block |
From
|
|
To
|
||
|---|---|---|---|---|---|---|---|
| Transfer | 24293501 | 1 min ago | 0.02009166 ETH | ||||
| Transfer | 24293501 | 1 min ago | 0.02672817 ETH | ||||
| Transfer | 24293501 | 1 min ago | 0.05706225 ETH | ||||
| 0x00000000 | 24293501 | 1 min ago | 0.24981083 ETH | ||||
| Transfer | 24293500 | 1 min ago | 0.01671755 ETH | ||||
| Transfer | 24293499 | 1 min ago | 0.02537785 ETH | ||||
| Transfer | 24293497 | 1 min ago | 0.01745034 ETH | ||||
| 0x00000000 | 24293497 | 1 min ago | 0.05205719 ETH | ||||
| 0x00000000 | 24293494 | 2 mins ago | 0.002 ETH | ||||
| Transfer | 24293493 | 2 mins ago | 0.02245321 ETH | ||||
| Transfer | 24293493 | 2 mins ago | 0.01817856 ETH | ||||
| Transfer | 24293492 | 2 mins ago | 0.20281802 ETH | ||||
| 0x00000000 | 24293491 | 3 mins ago | 0.03401253 ETH | ||||
| Transfer | 24293489 | 3 mins ago | 0.02054305 ETH | ||||
| Transfer | 24293489 | 3 mins ago | 0.03128008 ETH | ||||
| Transfer | 24293485 | 4 mins ago | 0.04031289 ETH | ||||
| Transfer | 24293485 | 4 mins ago | 0.0193464 ETH | ||||
| Transfer | 24293484 | 4 mins ago | 0.0327304 ETH | ||||
| Transfer | 24293483 | 4 mins ago | 0.01316895 ETH | ||||
| Transfer | 24293481 | 5 mins ago | 0.03009945 ETH | ||||
| Transfer | 24293480 | 5 mins ago | 0.0105 ETH | ||||
| 0x00000000 | 24293480 | 5 mins ago | 0.27090931 ETH | ||||
| Transfer | 24293478 | 5 mins ago | 0.0286296 ETH | ||||
| Transfer | 24293478 | 5 mins ago | 0.0504 ETH | ||||
| Transfer | 24293477 | 5 mins ago | 0.11848748 ETH |
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
Contract Source Code Verified (Exact Match)
Contract Name:
Core
Compiler Version
v0.8.33+commit.64118f21
Optimization Enabled:
Yes with 9999999 runs
Other Settings:
osaka EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: ekubo-license-v1.eth
pragma solidity =0.8.33;
import {CallPoints, addressToCallPoints} from "./types/callPoints.sol";
import {PoolKey} from "./types/poolKey.sol";
import {PoolConfig} from "./types/poolConfig.sol";
import {PositionId} from "./types/positionId.sol";
import {FeesPerLiquidity, feesPerLiquidityFromAmounts} from "./types/feesPerLiquidity.sol";
import {Position} from "./types/position.sol";
import {tickToSqrtRatio, sqrtRatioToTick} from "./math/ticks.sol";
import {CoreStorageLayout} from "./libraries/CoreStorageLayout.sol";
import {ExtensionCallPointsLib} from "./libraries/ExtensionCallPointsLib.sol";
import {FixedPointMathLib} from "solady/utils/FixedPointMathLib.sol";
import {SafeCastLib} from "solady/utils/SafeCastLib.sol";
import {ExposedStorage} from "./base/ExposedStorage.sol";
import {liquidityDeltaToAmountDelta, addLiquidityDelta} from "./math/liquidity.sol";
import {findNextInitializedTick, findPrevInitializedTick, flipTick} from "./math/tickBitmap.sol";
import {ICore, IExtension} from "./interfaces/ICore.sol";
import {FlashAccountant} from "./base/FlashAccountant.sol";
import {MIN_TICK, MAX_TICK, NATIVE_TOKEN_ADDRESS} from "./math/constants.sol";
import {MIN_SQRT_RATIO, MAX_SQRT_RATIO, SqrtRatio} from "./types/sqrtRatio.sol";
import {PoolState, createPoolState} from "./types/poolState.sol";
import {SwapParameters} from "./types/swapParameters.sol";
import {TickInfo, createTickInfo} from "./types/tickInfo.sol";
import {PoolBalanceUpdate, createPoolBalanceUpdate} from "./types/poolBalanceUpdate.sol";
import {PoolId} from "./types/poolId.sol";
import {Locker} from "./types/locker.sol";
import {computeFee, amountBeforeFee} from "./math/fee.sol";
import {nextSqrtRatioFromAmount0, nextSqrtRatioFromAmount1} from "./math/sqrtRatio.sol";
import {
amount0Delta,
amount1Delta,
amount0DeltaSorted,
amount1DeltaSorted,
sortAndConvertToFixedSqrtRatios
} from "./math/delta.sol";
import {StorageSlot} from "./types/storageSlot.sol";
import {LibBit} from "solady/utils/LibBit.sol";
/// @title Ekubo Protocol Core
/// @author Moody Salem <[email protected]>
/// @notice Singleton contract holding all tokens and containing all possible operations in Ekubo Protocol
/// @dev Implements the core AMM functionality including pools, positions, swaps, and fee collection
/// @dev Note this code is under the terms of the Ekubo DAO Shared Revenue License 1.0.
/// @dev The full terms of the license can be found at the contenthash specified at ekubo-license-v1.eth.
contract Core is ICore, FlashAccountant, ExposedStorage {
using ExtensionCallPointsLib for *;
/// @inheritdoc ICore
function registerExtension(CallPoints memory expectedCallPoints) external {
CallPoints memory computed = addressToCallPoints(msg.sender);
if (!computed.eq(expectedCallPoints) || !computed.isValid()) {
revert FailedRegisterInvalidCallPoints();
}
StorageSlot isExtensionRegisteredSlot = CoreStorageLayout.isExtensionRegisteredSlot(msg.sender);
if (isExtensionRegisteredSlot.load() != bytes32(0)) revert ExtensionAlreadyRegistered();
isExtensionRegisteredSlot.store(bytes32(LibBit.rawToUint(true)));
emit ExtensionRegistered(msg.sender);
}
function readPoolState(PoolId poolId) internal view returns (PoolState state) {
state = PoolState.wrap(CoreStorageLayout.poolStateSlot(poolId).load());
}
function writePoolState(PoolId poolId, PoolState state) internal {
CoreStorageLayout.poolStateSlot(poolId).store(PoolState.unwrap(state));
}
/// @inheritdoc ICore
function initializePool(PoolKey memory poolKey, int32 tick) external returns (SqrtRatio sqrtRatio) {
poolKey.validate();
address extension = poolKey.config.extension();
if (extension != address(0)) {
StorageSlot isExtensionRegisteredSlot = CoreStorageLayout.isExtensionRegisteredSlot(extension);
if (isExtensionRegisteredSlot.load() == bytes32(0)) {
revert ExtensionNotRegistered();
}
IExtension(extension).maybeCallBeforeInitializePool(msg.sender, poolKey, tick);
}
PoolId poolId = poolKey.toPoolId();
PoolState state = readPoolState(poolId);
if (state.isInitialized()) revert PoolAlreadyInitialized();
sqrtRatio = tickToSqrtRatio(tick);
writePoolState(poolId, createPoolState({_sqrtRatio: sqrtRatio, _tick: tick, _liquidity: 0}));
// initialize these slots so the first swap or deposit on the pool is the same cost as any other swap
StorageSlot fplSlot0 = CoreStorageLayout.poolFeesPerLiquiditySlot(poolId);
fplSlot0.store(bytes32(uint256(1)));
fplSlot0.next().store(bytes32(uint256(1)));
emit PoolInitialized(poolId, poolKey, tick, sqrtRatio);
IExtension(extension).maybeCallAfterInitializePool(msg.sender, poolKey, tick, sqrtRatio);
}
/// @inheritdoc ICore
function prevInitializedTick(PoolId poolId, int32 fromTick, uint32 tickSpacing, uint256 skipAhead)
external
view
returns (int32 tick, bool isInitialized)
{
(tick, isInitialized) =
findPrevInitializedTick(CoreStorageLayout.tickBitmapsSlot(poolId), fromTick, tickSpacing, skipAhead);
}
/// @inheritdoc ICore
function nextInitializedTick(PoolId poolId, int32 fromTick, uint32 tickSpacing, uint256 skipAhead)
external
view
returns (int32 tick, bool isInitialized)
{
(tick, isInitialized) =
findNextInitializedTick(CoreStorageLayout.tickBitmapsSlot(poolId), fromTick, tickSpacing, skipAhead);
}
/// @inheritdoc ICore
function updateSavedBalances(
address token0,
address token1,
bytes32,
// positive is saving, negative is loading
int256 delta0,
int256 delta1
)
external
payable
{
if (token0 >= token1) revert SavedBalanceTokensNotSorted();
(uint256 id, address lockerAddr) = _requireLocker().parse();
assembly ("memory-safe") {
function addDelta(u, i) -> result {
// full‐width sum mod 2^256
let sum := add(u, i)
// 1 if i<0 else 0
let sign := shr(255, i)
// if sum > type(uint128).max || (i>=0 && sum<u) || (i<0 && sum>u) ⇒ 256-bit wrap or underflow
if or(shr(128, sum), or(and(iszero(sign), lt(sum, u)), and(sign, gt(sum, u)))) {
mstore(0x00, 0x1293d6fa) // `SavedBalanceOverflow()`
revert(0x1c, 0x04)
}
result := sum
}
// we can cheaply calldatacopy the arguments into memory, hence no call to CoreStorageLayout#savedBalancesSlot
let free := mload(0x40)
mstore(free, lockerAddr)
// copy the first 3 arguments in the same order
calldatacopy(add(free, 0x20), 4, 96)
let slot := keccak256(free, 128)
let balances := sload(slot)
let b0 := shr(128, balances)
let b1 := shr(128, shl(128, balances))
let b0Next := addDelta(b0, delta0)
let b1Next := addDelta(b1, delta1)
sstore(slot, add(shl(128, b0Next), b1Next))
}
_updatePairDebtWithNative(id, token0, token1, delta0, delta1);
}
/// @notice Returns the pool fees per liquidity inside the given bounds
/// @dev Internal function that calculates fees per liquidity within position bounds
/// @param poolId Unique identifier for the pool
/// @param tick Current tick of the pool
/// @param tickLower Lower tick of the price range to get the snapshot of
/// @param tickLower Upper tick of the price range to get the snapshot of
/// @return feesPerLiquidityInside Accumulated fees per liquidity snapshot inside the bounds. Note this is a relative value.
function _getPoolFeesPerLiquidityInside(PoolId poolId, int32 tick, int32 tickLower, int32 tickUpper)
internal
view
returns (FeesPerLiquidity memory feesPerLiquidityInside)
{
uint256 lower0;
uint256 lower1;
uint256 upper0;
uint256 upper1;
{
(StorageSlot l0, StorageSlot l1) = CoreStorageLayout.poolTickFeesPerLiquidityOutsideSlot(poolId, tickLower);
(lower0, lower1) = (uint256(l0.load()), uint256(l1.load()));
(StorageSlot u0, StorageSlot u1) = CoreStorageLayout.poolTickFeesPerLiquidityOutsideSlot(poolId, tickUpper);
(upper0, upper1) = (uint256(u0.load()), uint256(u1.load()));
}
unchecked {
if (tick < tickLower) {
feesPerLiquidityInside.value0 = lower0 - upper0;
feesPerLiquidityInside.value1 = lower1 - upper1;
} else if (tick < tickUpper) {
uint256 global0;
uint256 global1;
{
(bytes32 g0, bytes32 g1) = CoreStorageLayout.poolFeesPerLiquiditySlot(poolId).loadTwo();
(global0, global1) = (uint256(g0), uint256(g1));
}
feesPerLiquidityInside.value0 = global0 - upper0 - lower0;
feesPerLiquidityInside.value1 = global1 - upper1 - lower1;
} else {
feesPerLiquidityInside.value0 = upper0 - lower0;
feesPerLiquidityInside.value1 = upper1 - lower1;
}
}
}
/// @inheritdoc ICore
function getPoolFeesPerLiquidityInside(PoolId poolId, int32 tickLower, int32 tickUpper)
external
view
returns (FeesPerLiquidity memory)
{
return _getPoolFeesPerLiquidityInside(poolId, readPoolState(poolId).tick(), tickLower, tickUpper);
}
/// @inheritdoc ICore
function accumulateAsFees(PoolKey memory poolKey, uint128 _amount0, uint128 _amount1) external payable {
(uint256 id, address lockerAddr) = _requireLocker().parse();
require(lockerAddr == poolKey.config.extension());
PoolId poolId = poolKey.toPoolId();
uint256 amount0;
uint256 amount1;
assembly ("memory-safe") {
amount0 := _amount0
amount1 := _amount1
}
// Note we do not check pool is initialized. If the extension calls this for a pool that does not exist,
// the fees are simply burned since liquidity is 0.
if (amount0 != 0 || amount1 != 0) {
uint256 liquidity;
{
uint128 _liquidity = readPoolState(poolId).liquidity();
assembly ("memory-safe") {
liquidity := _liquidity
}
}
unchecked {
if (liquidity != 0) {
StorageSlot slot0 = CoreStorageLayout.poolFeesPerLiquiditySlot(poolId);
if (amount0 != 0) {
slot0.store(
bytes32(uint256(slot0.load()) + FixedPointMathLib.rawDiv(amount0 << 128, liquidity))
);
}
if (amount1 != 0) {
StorageSlot slot1 = slot0.next();
slot1.store(
bytes32(uint256(slot1.load()) + FixedPointMathLib.rawDiv(amount1 << 128, liquidity))
);
}
}
}
}
// whether the fees are actually accounted to any position, the caller owes the debt
_updatePairDebtWithNative(id, poolKey.token0, poolKey.token1, int256(amount0), int256(amount1));
emit FeesAccumulated(poolId, _amount0, _amount1);
}
/// @notice Updates tick information when liquidity is added or removed
/// @dev Private function that handles tick initialization and liquidity tracking
/// @param poolId Unique identifier for the pool
/// @param tick Tick to update
/// @param poolConfig Pool configuration containing tick spacing
/// @param liquidityDelta Change in liquidity
/// @param isUpper Whether this is the upper bound of a position
function _updateTick(PoolId poolId, int32 tick, PoolConfig poolConfig, int128 liquidityDelta, bool isUpper)
private
{
StorageSlot tickInfoSlot = CoreStorageLayout.poolTicksSlot(poolId, tick);
(int128 currentLiquidityDelta, uint128 currentLiquidityNet) = TickInfo.wrap(tickInfoSlot.load()).parse();
uint128 liquidityNetNext = addLiquidityDelta(currentLiquidityNet, liquidityDelta);
// this is checked math
int128 liquidityDeltaNext =
isUpper ? currentLiquidityDelta - liquidityDelta : currentLiquidityDelta + liquidityDelta;
// Check that liquidityNet doesn't exceed max liquidity per tick
uint128 maxLiquidity = poolConfig.concentratedMaxLiquidityPerTick();
if (liquidityNetNext > maxLiquidity) {
revert MaxLiquidityPerTickExceeded(tick, liquidityNetNext, maxLiquidity);
}
if ((currentLiquidityNet == 0) != (liquidityNetNext == 0)) {
flipTick(CoreStorageLayout.tickBitmapsSlot(poolId), tick, poolConfig.concentratedTickSpacing());
(StorageSlot fplSlot0, StorageSlot fplSlot1) =
CoreStorageLayout.poolTickFeesPerLiquidityOutsideSlot(poolId, tick);
bytes32 v;
assembly ("memory-safe") {
v := gt(liquidityNetNext, 0)
}
// initialize the storage slots for the fees per liquidity outside to non-zero so tick crossing is cheaper
fplSlot0.store(v);
fplSlot1.store(v);
}
tickInfoSlot.store(TickInfo.unwrap(createTickInfo(liquidityDeltaNext, liquidityNetNext)));
}
/// @notice Updates debt for a token pair, handling native token payments for token0
/// @dev Optimized version that updates both tokens' debts in a single operation when possible.
/// Assumes token0 < token1 (tokens are sorted).
/// @param id Lock ID for debt tracking
/// @param token0 Address of token0 (must be < token1)
/// @param token1 Address of token1 (must be > token0)
/// @param debtChange0 Change in debt amount for token0
/// @param debtChange1 Change in debt amount for token1
function _updatePairDebtWithNative(
uint256 id,
address token0,
address token1,
int256 debtChange0,
int256 debtChange1
) private {
if (msg.value == 0) {
// No native token payment included in the call, so use optimized pair update
_updatePairDebt(id, token0, token1, debtChange0, debtChange1);
} else {
if (token0 == NATIVE_TOKEN_ADDRESS) {
unchecked {
// token0 is native, so we can still use pair update with adjusted debtChange0
// Subtraction is safe because debtChange0 and msg.value are both bounded by int128/uint128
_updatePairDebt(id, token0, token1, debtChange0 - int256(msg.value), debtChange1);
}
} else {
// token0 is not native, and since token0 < token1, token1 cannot be native either
// Update the token0, token1 debt and then update native token debt separately
unchecked {
_updatePairDebt(id, token0, token1, debtChange0, debtChange1);
_accountDebt(id, NATIVE_TOKEN_ADDRESS, -int256(msg.value));
}
}
}
}
/// @inheritdoc ICore
function updatePosition(PoolKey memory poolKey, PositionId positionId, int128 liquidityDelta)
external
payable
returns (PoolBalanceUpdate balanceUpdate)
{
positionId.validate(poolKey.config);
Locker locker = _requireLocker();
IExtension(poolKey.config.extension())
.maybeCallBeforeUpdatePosition(locker, poolKey, positionId, liquidityDelta);
PoolId poolId = poolKey.toPoolId();
PoolState state = readPoolState(poolId);
if (!state.isInitialized()) revert PoolNotInitialized();
if (liquidityDelta != 0) {
(SqrtRatio sqrtRatioLower, SqrtRatio sqrtRatioUpper) =
(tickToSqrtRatio(positionId.tickLower()), tickToSqrtRatio(positionId.tickUpper()));
(int128 delta0, int128 delta1) =
liquidityDeltaToAmountDelta(state.sqrtRatio(), liquidityDelta, sqrtRatioLower, sqrtRatioUpper);
StorageSlot positionSlot = CoreStorageLayout.poolPositionsSlot(poolId, locker.addr(), positionId);
Position storage position;
assembly ("memory-safe") {
position.slot := positionSlot
}
uint128 liquidityNext = addLiquidityDelta(position.liquidity, liquidityDelta);
FeesPerLiquidity memory feesPerLiquidityInside;
if (poolKey.config.isConcentrated()) {
// the position is fully withdrawn
if (liquidityNext == 0) {
// we need to fetch it before the tick fees per liquidity outside is deleted
feesPerLiquidityInside = _getPoolFeesPerLiquidityInside(
poolId, state.tick(), positionId.tickLower(), positionId.tickUpper()
);
}
_updateTick(poolId, positionId.tickLower(), poolKey.config, liquidityDelta, false);
_updateTick(poolId, positionId.tickUpper(), poolKey.config, liquidityDelta, true);
if (liquidityNext != 0) {
feesPerLiquidityInside = _getPoolFeesPerLiquidityInside(
poolId, state.tick(), positionId.tickLower(), positionId.tickUpper()
);
}
if (state.tick() >= positionId.tickLower() && state.tick() < positionId.tickUpper()) {
state = createPoolState({
_sqrtRatio: state.sqrtRatio(),
_tick: state.tick(),
_liquidity: addLiquidityDelta(state.liquidity(), liquidityDelta)
});
writePoolState(poolId, state);
}
} else {
// we store the active liquidity in the liquidity slot for stableswap pools
state = createPoolState({
_sqrtRatio: state.sqrtRatio(),
_tick: state.tick(),
_liquidity: addLiquidityDelta(state.liquidity(), liquidityDelta)
});
writePoolState(poolId, state);
StorageSlot fplFirstSlot = CoreStorageLayout.poolFeesPerLiquiditySlot(poolId);
feesPerLiquidityInside.value0 = uint256(fplFirstSlot.load());
feesPerLiquidityInside.value1 = uint256(fplFirstSlot.next().load());
}
if (liquidityNext == 0) {
position.liquidity = 0;
position.feesPerLiquidityInsideLast = FeesPerLiquidity(0, 0);
} else {
(uint128 fees0, uint128 fees1) = position.fees(feesPerLiquidityInside);
position.liquidity = liquidityNext;
position.feesPerLiquidityInsideLast =
feesPerLiquidityInside.sub(feesPerLiquidityFromAmounts(fees0, fees1, liquidityNext));
}
_updatePairDebtWithNative(locker.id(), poolKey.token0, poolKey.token1, delta0, delta1);
balanceUpdate = createPoolBalanceUpdate(delta0, delta1);
emit PositionUpdated(locker.addr(), poolId, positionId, liquidityDelta, balanceUpdate, state);
} else {
if (msg.value != 0) {
_accountDebt(locker.id(), NATIVE_TOKEN_ADDRESS, -int256(msg.value));
}
}
IExtension(poolKey.config.extension())
.maybeCallAfterUpdatePosition(locker, poolKey, positionId, liquidityDelta, balanceUpdate, state);
}
/// @inheritdoc ICore
function setExtraData(PoolId poolId, PositionId positionId, bytes16 _extraData) external {
StorageSlot firstSlot = CoreStorageLayout.poolPositionsSlot(poolId, msg.sender, positionId);
bytes32 extraData;
assembly ("memory-safe") {
extraData := _extraData
}
firstSlot.store(((firstSlot.load() >> 128) << 128) | (extraData >> 128));
}
/// @inheritdoc ICore
function collectFees(PoolKey memory poolKey, PositionId positionId)
external
returns (uint128 amount0, uint128 amount1)
{
Locker locker = _requireLocker();
IExtension(poolKey.config.extension()).maybeCallBeforeCollectFees(locker, poolKey, positionId);
PoolId poolId = poolKey.toPoolId();
Position storage position;
StorageSlot positionSlot = CoreStorageLayout.poolPositionsSlot(poolId, locker.addr(), positionId);
assembly ("memory-safe") {
position.slot := positionSlot
}
FeesPerLiquidity memory feesPerLiquidityInside;
if (poolKey.config.isStableswap()) {
// Stableswap pools: use global fees per liquidity
StorageSlot fplFirstSlot = CoreStorageLayout.poolFeesPerLiquiditySlot(poolId);
feesPerLiquidityInside.value0 = uint256(fplFirstSlot.load());
feesPerLiquidityInside.value1 = uint256(fplFirstSlot.next().load());
} else {
// Concentrated pools: calculate fees per liquidity inside the position bounds
feesPerLiquidityInside = _getPoolFeesPerLiquidityInside(
poolId, readPoolState(poolId).tick(), positionId.tickLower(), positionId.tickUpper()
);
}
(amount0, amount1) = position.fees(feesPerLiquidityInside);
position.feesPerLiquidityInsideLast = feesPerLiquidityInside;
_updatePairDebt(
locker.id(), poolKey.token0, poolKey.token1, -int256(uint256(amount0)), -int256(uint256(amount1))
);
emit PositionFeesCollected(locker.addr(), poolId, positionId, amount0, amount1);
IExtension(poolKey.config.extension()).maybeCallAfterCollectFees(locker, poolKey, positionId, amount0, amount1);
}
/// @inheritdoc ICore
function swap_6269342730() external payable {
unchecked {
PoolKey memory poolKey;
address token0;
address token1;
PoolConfig config;
SwapParameters params;
assembly ("memory-safe") {
token0 := calldataload(4)
token1 := calldataload(36)
config := calldataload(68)
params := calldataload(100)
calldatacopy(poolKey, 4, 96)
}
SqrtRatio sqrtRatioLimit = params.sqrtRatioLimit();
if (!sqrtRatioLimit.isValid()) revert InvalidSqrtRatioLimit();
Locker locker = _requireLocker();
IExtension(config.extension()).maybeCallBeforeSwap(locker, poolKey, params);
PoolId poolId = poolKey.toPoolId();
PoolState stateAfter = readPoolState(poolId);
if (!stateAfter.isInitialized()) revert PoolNotInitialized();
int256 amountRemaining = params.amount();
PoolBalanceUpdate balanceUpdate;
// 0 swap amount or sqrt ratio limit == sqrt ratio is no-op
if (amountRemaining != 0 && stateAfter.sqrtRatio() != sqrtRatioLimit) {
(SqrtRatio sqrtRatio, int32 tick, uint128 liquidity) = stateAfter.parse();
bool isToken1 = params.isToken1();
bool isExactOut = amountRemaining < 0;
bool increasing;
assembly ("memory-safe") {
increasing := xor(isToken1, isExactOut)
}
if ((sqrtRatioLimit < sqrtRatio) == increasing) {
revert SqrtRatioLimitWrongDirection();
}
int256 calculatedAmount;
// fees per liquidity only for the input token
uint256 inputTokenFeesPerLiquidity;
// 0 = not loaded & not updated, 1 = loaded & not updated, 2 = loaded & updated
uint256 feesAccessed;
while (true) {
int32 nextTick;
bool isInitialized;
SqrtRatio nextTickSqrtRatio;
// For stableswap pools, determine active liquidity for this step
uint128 stepLiquidity = liquidity;
if (config.isStableswap()) {
if (config.isFullRange()) {
// special case since we don't need to compute min/max tick sqrt ratio
(nextTick, nextTickSqrtRatio) =
increasing ? (MAX_TICK, MAX_SQRT_RATIO) : (MIN_TICK, MIN_SQRT_RATIO);
} else {
(int32 lower, int32 upper) = config.stableswapActiveLiquidityTickRange();
bool inRange;
assembly ("memory-safe") {
inRange := and(slt(tick, upper), iszero(slt(tick, lower)))
}
if (inRange) {
nextTick = increasing ? upper : lower;
nextTickSqrtRatio = tickToSqrtRatio(nextTick);
} else {
if (tick < lower) {
(nextTick, nextTickSqrtRatio) =
increasing ? (lower, tickToSqrtRatio(lower)) : (MIN_TICK, MIN_SQRT_RATIO);
} else {
// tick >= upper implied
(nextTick, nextTickSqrtRatio) =
increasing ? (MAX_TICK, MAX_SQRT_RATIO) : (upper, tickToSqrtRatio(upper));
}
stepLiquidity = 0;
}
}
} else {
// concentrated liquidity pools use the tick bitmaps
(nextTick, isInitialized) = increasing
? findNextInitializedTick(
CoreStorageLayout.tickBitmapsSlot(poolId),
tick,
config.concentratedTickSpacing(),
params.skipAhead()
)
: findPrevInitializedTick(
CoreStorageLayout.tickBitmapsSlot(poolId),
tick,
config.concentratedTickSpacing(),
params.skipAhead()
);
nextTickSqrtRatio = tickToSqrtRatio(nextTick);
}
SqrtRatio limitedNextSqrtRatio =
increasing ? nextTickSqrtRatio.min(sqrtRatioLimit) : nextTickSqrtRatio.max(sqrtRatioLimit);
SqrtRatio sqrtRatioNext;
if (stepLiquidity == 0) {
// if the pool is empty, the swap will always move all the way to the limit price
sqrtRatioNext = limitedNextSqrtRatio;
} else {
// this amount is what moves the price
int128 priceImpactAmount;
if (isExactOut) {
assembly ("memory-safe") {
priceImpactAmount := amountRemaining
}
} else {
uint128 amountU128;
assembly ("memory-safe") {
// cast is safe because amountRemaining is g.t. 0 and fits in int128
amountU128 := amountRemaining
}
uint128 feeAmount = computeFee(amountU128, config.fee());
assembly ("memory-safe") {
// feeAmount will never exceed amountRemaining since fee is < 100%
priceImpactAmount := sub(amountRemaining, feeAmount)
}
}
SqrtRatio sqrtRatioNextFromAmount = isToken1
? nextSqrtRatioFromAmount1(sqrtRatio, stepLiquidity, priceImpactAmount)
: nextSqrtRatioFromAmount0(sqrtRatio, stepLiquidity, priceImpactAmount);
bool hitLimit;
assembly ("memory-safe") {
// Branchless limit check: (increasing && next > limit) || (!increasing && next < limit)
let exceedsUp := and(increasing, gt(sqrtRatioNextFromAmount, limitedNextSqrtRatio))
let exceedsDown :=
and(iszero(increasing), lt(sqrtRatioNextFromAmount, limitedNextSqrtRatio))
hitLimit := or(exceedsUp, exceedsDown)
}
// the change in fees per liquidity for this step of the iteration
uint256 stepFeesPerLiquidity;
if (hitLimit) {
(uint256 sqrtRatioLower, uint256 sqrtRatioUpper) =
sortAndConvertToFixedSqrtRatios(limitedNextSqrtRatio, sqrtRatio);
(uint128 limitSpecifiedAmountDelta, uint128 limitCalculatedAmountDelta) = isToken1
? (
amount1DeltaSorted(sqrtRatioLower, sqrtRatioUpper, stepLiquidity, !isExactOut),
amount0DeltaSorted(sqrtRatioLower, sqrtRatioUpper, stepLiquidity, isExactOut)
)
: (
amount0DeltaSorted(sqrtRatioLower, sqrtRatioUpper, stepLiquidity, !isExactOut),
amount1DeltaSorted(sqrtRatioLower, sqrtRatioUpper, stepLiquidity, isExactOut)
);
if (isExactOut) {
uint128 beforeFee = amountBeforeFee(limitCalculatedAmountDelta, config.fee());
assembly ("memory-safe") {
calculatedAmount := add(calculatedAmount, beforeFee)
amountRemaining := add(amountRemaining, limitSpecifiedAmountDelta)
stepFeesPerLiquidity := div(
shl(128, sub(beforeFee, limitCalculatedAmountDelta)),
stepLiquidity
)
}
} else {
uint128 beforeFee = amountBeforeFee(limitSpecifiedAmountDelta, config.fee());
assembly ("memory-safe") {
calculatedAmount := sub(calculatedAmount, limitCalculatedAmountDelta)
amountRemaining := sub(amountRemaining, beforeFee)
stepFeesPerLiquidity := div(
shl(128, sub(beforeFee, limitSpecifiedAmountDelta)),
stepLiquidity
)
}
}
sqrtRatioNext = limitedNextSqrtRatio;
} else if (sqrtRatioNextFromAmount != sqrtRatio) {
uint128 calculatedAmountWithoutFee = isToken1
? amount0Delta(sqrtRatioNextFromAmount, sqrtRatio, stepLiquidity, isExactOut)
: amount1Delta(sqrtRatioNextFromAmount, sqrtRatio, stepLiquidity, isExactOut);
if (isExactOut) {
uint128 includingFee = amountBeforeFee(calculatedAmountWithoutFee, config.fee());
assembly ("memory-safe") {
calculatedAmount := add(calculatedAmount, includingFee)
stepFeesPerLiquidity := div(
shl(128, sub(includingFee, calculatedAmountWithoutFee)),
stepLiquidity
)
}
} else {
assembly ("memory-safe") {
calculatedAmount := sub(calculatedAmount, calculatedAmountWithoutFee)
stepFeesPerLiquidity := div(
shl(128, sub(amountRemaining, priceImpactAmount)),
stepLiquidity
)
}
}
amountRemaining = 0;
sqrtRatioNext = sqrtRatioNextFromAmount;
} else {
// for an exact output swap, the price should always move since we have to round away from the current price
assert(!isExactOut);
// consume the entire input amount as fees since the price did not move
assembly ("memory-safe") {
stepFeesPerLiquidity := div(shl(128, amountRemaining), stepLiquidity)
}
amountRemaining = 0;
sqrtRatioNext = sqrtRatio;
}
// only if fees per liquidity was updated in this swap iteration
if (stepFeesPerLiquidity != 0) {
if (feesAccessed == 0) {
// this loads only the input token fees per liquidity
inputTokenFeesPerLiquidity = uint256(
CoreStorageLayout.poolFeesPerLiquiditySlot(poolId).add(LibBit.rawToUint(increasing))
.load()
) + stepFeesPerLiquidity;
} else {
inputTokenFeesPerLiquidity += stepFeesPerLiquidity;
}
feesAccessed = 2;
}
}
if (sqrtRatioNext == nextTickSqrtRatio) {
sqrtRatio = sqrtRatioNext;
assembly ("memory-safe") {
// no overflow danger because nextTick is always inside the valid tick bounds
tick := sub(nextTick, iszero(increasing))
}
if (isInitialized) {
bytes32 tickValue = CoreStorageLayout.poolTicksSlot(poolId, nextTick).load();
assembly ("memory-safe") {
// if increasing, we add the liquidity delta, otherwise we subtract it
let liquidityDelta :=
mul(signextend(15, tickValue), sub(increasing, iszero(increasing)))
liquidity := add(liquidity, liquidityDelta)
}
(StorageSlot tickFplFirstSlot, StorageSlot tickFplSecondSlot) =
CoreStorageLayout.poolTickFeesPerLiquidityOutsideSlot(poolId, nextTick);
if (feesAccessed == 0) {
inputTokenFeesPerLiquidity = uint256(
CoreStorageLayout.poolFeesPerLiquiditySlot(poolId).add(LibBit.rawToUint(increasing))
.load()
);
feesAccessed = 1;
}
uint256 globalFeesPerLiquidityOther = uint256(
CoreStorageLayout.poolFeesPerLiquiditySlot(poolId).add(LibBit.rawToUint(!increasing))
.load()
);
// if increasing, it means the pool is receiving token1 so the input fees per liquidity is token1
if (increasing) {
tickFplFirstSlot.store(
bytes32(globalFeesPerLiquidityOther - uint256(tickFplFirstSlot.load()))
);
tickFplSecondSlot.store(
bytes32(inputTokenFeesPerLiquidity - uint256(tickFplSecondSlot.load()))
);
} else {
tickFplFirstSlot.store(
bytes32(inputTokenFeesPerLiquidity - uint256(tickFplFirstSlot.load()))
);
tickFplSecondSlot.store(
bytes32(globalFeesPerLiquidityOther - uint256(tickFplSecondSlot.load()))
);
}
}
} else if (sqrtRatio != sqrtRatioNext) {
sqrtRatio = sqrtRatioNext;
tick = sqrtRatioToTick(sqrtRatio);
}
if (amountRemaining == 0 || sqrtRatio == sqrtRatioLimit) {
break;
}
}
int128 calculatedAmountDelta =
SafeCastLib.toInt128(FixedPointMathLib.max(type(int128).min, calculatedAmount));
int128 specifiedAmountDelta;
int128 specifiedAmount = params.amount();
assembly ("memory-safe") {
specifiedAmountDelta := sub(specifiedAmount, amountRemaining)
}
balanceUpdate = isToken1
? createPoolBalanceUpdate(calculatedAmountDelta, specifiedAmountDelta)
: createPoolBalanceUpdate(specifiedAmountDelta, calculatedAmountDelta);
stateAfter = createPoolState({_sqrtRatio: sqrtRatio, _tick: tick, _liquidity: liquidity});
writePoolState(poolId, stateAfter);
if (feesAccessed == 2) {
// this stores only the input token fees per liquidity
CoreStorageLayout.poolFeesPerLiquiditySlot(poolId).add(LibBit.rawToUint(increasing))
.store(bytes32(inputTokenFeesPerLiquidity));
}
_updatePairDebtWithNative(locker.id(), token0, token1, balanceUpdate.delta0(), balanceUpdate.delta1());
assembly ("memory-safe") {
let o := mload(0x40)
mstore(o, shl(96, locker))
mstore(add(o, 20), poolId)
mstore(add(o, 52), balanceUpdate)
mstore(add(o, 84), stateAfter)
log0(o, 116)
}
}
IExtension(config.extension()).maybeCallAfterSwap(locker, poolKey, params, balanceUpdate, stateAfter);
assembly ("memory-safe") {
mstore(0, balanceUpdate)
mstore(32, stateAfter)
return(0, 64)
}
}
}
}// SPDX-License-Identifier: ekubo-license-v1.eth
pragma solidity =0.8.33;
struct CallPoints {
bool beforeInitializePool;
bool afterInitializePool;
bool beforeSwap;
bool afterSwap;
bool beforeUpdatePosition;
bool afterUpdatePosition;
bool beforeCollectFees;
bool afterCollectFees;
}
using {eq, isValid, toUint8} for CallPoints global;
function eq(CallPoints memory a, CallPoints memory b) pure returns (bool) {
return (a.beforeInitializePool == b.beforeInitializePool && a.afterInitializePool == b.afterInitializePool
&& a.beforeSwap == b.beforeSwap && a.afterSwap == b.afterSwap
&& a.beforeUpdatePosition == b.beforeUpdatePosition && a.afterUpdatePosition == b.afterUpdatePosition
&& a.beforeCollectFees == b.beforeCollectFees && a.afterCollectFees == b.afterCollectFees);
}
function isValid(CallPoints memory a) pure returns (bool) {
return (a.beforeInitializePool || a.afterInitializePool || a.beforeSwap || a.afterSwap || a.beforeUpdatePosition
|| a.afterUpdatePosition || a.beforeCollectFees || a.afterCollectFees);
}
function toUint8(CallPoints memory callPoints) pure returns (uint8 b) {
assembly ("memory-safe") {
b := add(
add(
add(
add(
add(
add(
add(mload(callPoints), mul(128, mload(add(callPoints, 32)))),
mul(64, mload(add(callPoints, 64)))
),
mul(32, mload(add(callPoints, 96)))
),
mul(16, mload(add(callPoints, 128)))
),
mul(8, mload(add(callPoints, 160)))
),
mul(4, mload(add(callPoints, 192)))
),
mul(2, mload(add(callPoints, 224)))
)
}
}
function addressToCallPoints(address a) pure returns (CallPoints memory result) {
result = byteToCallPoints(uint8(uint160(a) >> 152));
}
function byteToCallPoints(uint8 b) pure returns (CallPoints memory result) {
// note the order of bytes does not match the struct order of elements because we are matching the cairo implementation
// which for legacy reasons has the fields in this order
result = CallPoints({
beforeInitializePool: (b & 1) != 0,
afterInitializePool: (b & 128) != 0,
beforeSwap: (b & 64) != 0,
afterSwap: (b & 32) != 0,
beforeUpdatePosition: (b & 16) != 0,
afterUpdatePosition: (b & 8) != 0,
beforeCollectFees: (b & 4) != 0,
afterCollectFees: (b & 2) != 0
});
}// SPDX-License-Identifier: ekubo-license-v1.eth
pragma solidity =0.8.33;
import {PoolId} from "./poolId.sol";
import {PoolConfig} from "./poolConfig.sol";
using {toPoolId, validate} for PoolKey global;
/// @notice Unique identifier for a pool containing token addresses and configuration
/// @dev Each pool has its own state associated with this key
struct PoolKey {
/// @notice Address of token0 (must be < token1)
address token0;
/// @notice Address of token1 (must be > token0)
address token1;
/// @notice Packed configuration containing extension, fee, and tick spacing
PoolConfig config;
}
/// @notice Thrown when tokens are not properly sorted (token0 >= token1)
error TokensMustBeSorted();
/// @notice Validates that a pool key is valid
/// @dev Checks that tokens are sorted and the config is valid
/// @param key The pool key to validate
function validate(PoolKey memory key) pure {
if (key.token0 >= key.token1) revert TokensMustBeSorted();
key.config.validate();
}
/// @notice Converts a pool key to a unique pool ID
/// @param key The pool key
/// @return result The unique pool ID (hash of the pool key)
function toPoolId(PoolKey memory key) pure returns (PoolId result) {
assembly ("memory-safe") {
// it's already copied into memory
result := keccak256(key, 96)
}
}// SPDX-License-Identifier: ekubo-license-v1.eth
pragma solidity =0.8.33;
import {MIN_TICK, MAX_TICK, MAX_TICK_SPACING} from "../math/constants.sol";
/// @notice Pool configuration packed into a single bytes32
/// @dev Contains extension address (20 bytes), fee (8 bytes), and pool type config (4 bytes)
/// Pool type config (32 bits):
/// - Bit 31: discriminator (1 = concentrated, 0 = stableswap)
/// - For concentrated (bit 31 = 1): bits 30-0 are tick spacing
/// - For stableswap (bit 31 = 0): bits 30-24 are amplification factor, bits 23-0 are center tick
type PoolConfig is bytes32;
using {
fee,
extension,
isConcentrated,
isStableswap,
isFullRange,
concentratedTickSpacing,
stableswapAmplification,
stableswapCenterTick,
stableswapActiveLiquidityTickRange,
stableswapLiquidityWidth,
validate,
concentratedMaxLiquidityPerTick
} for PoolConfig global;
/// @notice Extracts the fee from a pool config
/// @param config The pool config
/// @return r The fee
function fee(PoolConfig config) pure returns (uint64 r) {
assembly ("memory-safe") {
r := and(shr(32, config), 0xffffffffffffffff)
}
}
/// @notice Extracts the extension address from a pool config
/// @param config The pool config
/// @return r The extension address
function extension(PoolConfig config) pure returns (address r) {
assembly ("memory-safe") {
r := shr(96, config)
}
}
/// @notice Checks if this is a concentrated liquidity pool
/// @param config The pool config
/// @return r True if the pool is concentrated liquidity
function isConcentrated(PoolConfig config) pure returns (bool r) {
r = !config.isStableswap();
}
/// @notice Extracts the tick spacing from a concentrated liquidity pool config
/// @dev Only valid for concentrated liquidity pools (isConcentrated() == true)
/// @param config The pool config
/// @return r The tick spacing
function concentratedTickSpacing(PoolConfig config) pure returns (uint32 r) {
assembly ("memory-safe") {
// Extract lower 31 bits (bits 30-0)
r := and(config, 0x7fffffff)
}
}
/// @notice Checks if this is a stableswap pool
/// @param config The pool config
/// @return r True if the pool is stableswap
function isStableswap(PoolConfig config) pure returns (bool r) {
assembly ("memory-safe") {
// = iff bit 31 is not set
r := iszero(and(0x80000000, config))
}
}
/// @notice Determines if this pool is full range (special case of stableswap with amplification=0 and center=0)
/// @dev Full range can be slightly optimized in that we don't need to compute the sqrt ratio at the tick boundaries
/// @param config The pool config
/// @return r True if the pool is full range
function isFullRange(PoolConfig config) pure returns (bool r) {
assembly ("memory-safe") {
// Full range when all 32 bits are 0 (discriminator=0, amplification=0, center=0)
r := iszero(and(config, 0xffffffff))
}
}
/// @notice Extracts the amplification factor from a stableswap pool config
/// @dev Only valid for stableswap pools (isStableswap() == true)
/// @param config The pool config
/// @return r The amplification factor (0-127)
function stableswapAmplification(PoolConfig config) pure returns (uint8 r) {
assembly ("memory-safe") {
// Extract bits 30-24
r := and(shr(24, config), 0x7f)
}
}
/// @notice Extracts the center tick from a stableswap pool config
/// @dev Only valid for stableswap pools (isStableswap() == true)
/// @dev The 24-bit center tick is scaled by 16 to get the actual tick
/// @param config The pool config
/// @return r The center tick
function stableswapCenterTick(PoolConfig config) pure returns (int32 r) {
assembly ("memory-safe") {
// Extract bits 23-0 and sign extend to 32 bits (24-bit signed integer)
// Then multiply by 16, since the value does not have full precision
r := mul(signextend(2, and(config, 0xffffff)), 16)
}
}
/// @notice Returns the width of the liquidity range
function stableswapLiquidityWidth(PoolConfig config) pure returns (uint256 width) {
uint8 amp = config.stableswapAmplification();
assembly ("memory-safe") {
width := shr(amp, MAX_TICK)
}
}
/// @notice Computes the tick range where liquidity is active for stableswap pools
/// @param config The pool config
/// @return lower The lower tick of the bounds
/// @return upper The upper tick of the bounds
function stableswapActiveLiquidityTickRange(PoolConfig config) pure returns (int32 lower, int32 upper) {
int32 center = config.stableswapCenterTick();
uint256 width = config.stableswapLiquidityWidth();
assembly ("memory-safe") {
lower := sub(center, width)
lower := add(lower, mul(sgt(MIN_TICK, lower), sub(MIN_TICK, lower)))
upper := add(center, width)
upper := sub(upper, mul(sgt(upper, MAX_TICK), sub(upper, MAX_TICK)))
}
}
/// @notice Creates a PoolConfig for a concentrated liquidity pool
/// @param _fee The fee for the pool
/// @param _tickSpacing The tick spacing for the pool
/// @param _extension The extension address for the pool
/// @return c The packed configuration
function createConcentratedPoolConfig(uint64 _fee, uint32 _tickSpacing, address _extension)
pure
returns (PoolConfig c)
{
assembly ("memory-safe") {
// Set bit 31 to 1 for concentrated liquidity, then OR with tick spacing (bits 30-0)
let typeConfig := or(0x80000000, and(_tickSpacing, 0x7fffffff))
c := or(or(shl(96, _extension), shl(32, and(_fee, 0xffffffffffffffff))), typeConfig)
}
}
/// @notice Creates a PoolConfig for a stableswap pool
/// @param _fee The fee for the pool
/// @param _amplification The amplification factor (0-127)
/// @param _centerTick The center tick (will be divided by 16 and stored as 24-bit value)
/// @param _extension The extension address for the pool
/// @return c The packed configuration
function createStableswapPoolConfig(uint64 _fee, uint8 _amplification, int32 _centerTick, address _extension)
pure
returns (PoolConfig c)
{
assembly ("memory-safe") {
// Divide center tick by 16 to get 24-bit representation
let stableswapCenterTick24 := sdiv(_centerTick, 16)
// Pack: bit 31 = 0 (stableswap), bits 30-24 = amplification, bits 23-0 = center tick
let typeConfig := or(shl(24, and(_amplification, 0x7f)), and(stableswapCenterTick24, 0xffffff))
c := or(or(shl(96, _extension), shl(32, and(_fee, 0xffffffffffffffff))), typeConfig)
}
}
/// @notice Creates a PoolConfig for a full range pool (stableswap with amplification=0, center=0)
/// @param _fee The fee for the pool
/// @param _extension The extension address for the pool
/// @return c The packed configuration
function createFullRangePoolConfig(uint64 _fee, address _extension) pure returns (PoolConfig c) {
assembly ("memory-safe") {
// All 32 bits of type config are 0 (discriminator=0, amplification=0, center=0)
c := or(shl(96, _extension), shl(32, and(_fee, 0xffffffffffffffff)))
}
}
/// @notice Computes the maximum liquidity per tick for a given concentrated liquidity pool configuration.
/// @dev Only valid for concentrated liquidity pools. Stableswap pools don't use ticks.
/// @dev Calculated as type(uint128).max / (1 + (MAX_TICK_MAGNITUDE / tickSpacing) * 2)
/// @param config The concentrated liquidity pool configuration
/// @return maxLiquidity The maximum liquidity allowed to reference each tick
function concentratedMaxLiquidityPerTick(PoolConfig config) pure returns (uint128 maxLiquidity) {
uint32 _tickSpacing = config.concentratedTickSpacing();
assembly ("memory-safe") {
// Calculate total number of usable ticks: 1 + (MAX_TICK_MAGNITUDE / tickSpacing) * 2
// This represents all ticks from -MAX_TICK_MAGNITUDE to +MAX_TICK_MAGNITUDE, and tick 0
let numTicks := add(1, mul(div(MAX_TICK, _tickSpacing), 2))
maxLiquidity := div(sub(shl(128, 1), 1), numTicks)
}
}
/// @notice Thrown when tick spacing exceeds the maximum allowed value
error InvalidTickSpacing();
/// @notice Thrown when amplification factor exceeds the maximum allowed value
error InvalidStableswapAmplification();
/// @notice Thrown when center tick is not between min and max tick
error InvalidCenterTick();
/// @notice Validates that a pool config is properly formatted
/// @param config The config to validate
function validate(PoolConfig config) pure {
if (config.isConcentrated()) {
if (config.concentratedTickSpacing() > MAX_TICK_SPACING || config.concentratedTickSpacing() == 0) {
revert InvalidTickSpacing();
}
} else {
// Stableswap pool: validate amplification factor <= 26
if (config.stableswapAmplification() > 26) {
revert InvalidStableswapAmplification();
}
int32 centerTick = config.stableswapCenterTick();
if (centerTick < MIN_TICK || centerTick > MAX_TICK) {
revert InvalidCenterTick();
}
}
}// SPDX-License-Identifier: ekubo-license-v1.eth
pragma solidity =0.8.33;
import {MIN_TICK, MAX_TICK} from "../math/constants.sol";
import {PoolConfig} from "./poolConfig.sol";
type PositionId is bytes32;
using {validate, salt, tickLower, tickUpper} for PositionId global;
function salt(PositionId positionId) pure returns (bytes24 v) {
assembly ("memory-safe") {
v := shl(64, shr(64, positionId))
}
}
function tickLower(PositionId positionId) pure returns (int32 v) {
assembly ("memory-safe") {
// shift down, then signextend to 32 bits
v := signextend(3, shr(32, positionId))
}
}
function tickUpper(PositionId positionId) pure returns (int32 v) {
assembly ("memory-safe") {
// lowest 4 bytes, then signextend to 32 bits
v := signextend(3, positionId)
}
}
function createPositionId(bytes24 _salt, int32 _tickLower, int32 _tickUpper) pure returns (PositionId v) {
assembly ("memory-safe") {
// v = salt | (tickLower << 32) | tickUpper
v := or(shl(64, shr(64, _salt)), or(shl(32, and(_tickLower, 0xFFFFFFFF)), and(_tickUpper, 0xFFFFFFFF)))
}
}
/// @notice Thrown when the order of the position bounds is invalid, i.e. tickLower >= tickUpper
error BoundsOrder();
/// @notice Thrown when the bounds of the position are outside the pool's min/max tick range
error MinMaxBounds();
/// @notice Thrown when the ticks of the bounds do not align with tick spacing for concentrated pools
error BoundsTickSpacing();
/// @notice Thrown when stableswap pool positions are not at the min/max tick for the config
error StableswapMustBeFullRange();
function validate(PositionId positionId, PoolConfig config) pure {
if (config.isConcentrated()) {
if (positionId.tickLower() >= positionId.tickUpper()) revert BoundsOrder();
if (positionId.tickLower() < MIN_TICK || positionId.tickUpper() > MAX_TICK) revert MinMaxBounds();
int32 spacing = int32(config.concentratedTickSpacing());
if (positionId.tickLower() % spacing != 0 || positionId.tickUpper() % spacing != 0) revert BoundsTickSpacing();
} else {
(int32 lower, int32 upper) = config.stableswapActiveLiquidityTickRange();
// For stableswap pools, positions must be exactly min/max tick
if (positionId.tickLower() != lower || positionId.tickUpper() != upper) revert StableswapMustBeFullRange();
}
}// SPDX-License-Identifier: ekubo-license-v1.eth
pragma solidity =0.8.33;
// The total fees per liquidity for each token.
// Since these are always read together we put them in a struct, even though they cannot be packed.
struct FeesPerLiquidity {
uint256 value0;
uint256 value1;
}
using {sub} for FeesPerLiquidity global;
function sub(FeesPerLiquidity memory a, FeesPerLiquidity memory b) pure returns (FeesPerLiquidity memory result) {
assembly ("memory-safe") {
mstore(result, sub(mload(a), mload(b)))
mstore(add(result, 32), sub(mload(add(a, 32)), mload(add(b, 32))))
}
}
function feesPerLiquidityFromAmounts(uint128 amount0, uint128 amount1, uint128 liquidity)
pure
returns (FeesPerLiquidity memory result)
{
assembly ("memory-safe") {
mstore(result, div(shl(128, amount0), liquidity))
mstore(add(result, 32), div(shl(128, amount1), liquidity))
}
}// SPDX-License-Identifier: ekubo-license-v1.eth
pragma solidity =0.8.33;
import {FeesPerLiquidity} from "./feesPerLiquidity.sol";
import {FixedPointMathLib} from "solady/utils/FixedPointMathLib.sol";
// Position Type
// Represents a liquidity position in a pool
// Contains the position's liquidity amount and fee tracking information
/// @notice A liquidity position in a pool
/// @dev Tracks both the liquidity amount and the last known fees per liquidity for fee calculation
struct Position {
/// @notice Extra data that can be set by the owner of a position
bytes16 extraData;
/// @notice Amount of liquidity in the position
uint128 liquidity;
/// @notice Snapshot of fees per liquidity when the position was last updated
/// @dev Used to calculate fees owed to the position holder
FeesPerLiquidity feesPerLiquidityInsideLast;
}
using {fees} for Position global;
/// @notice Calculates the fees owed to a position
/// @dev Returns the fee amounts of token0 and token1 owed to a position based on the given fees per liquidity inside snapshot
/// Note: if the computed fees overflow the uint128 type, it will return only the lower 128 bits. It is assumed that accumulated
/// fees will never exceed type(uint128).max.
/// @param position The position to calculate fees for
/// @param feesPerLiquidityInside Current fees per liquidity inside the position's bounds
/// @return Amount of token0 fees owed
/// @return Amount of token1 fees owed
function fees(Position memory position, FeesPerLiquidity memory feesPerLiquidityInside)
pure
returns (uint128, uint128)
{
uint128 liquidity;
uint256 difference0;
uint256 difference1;
assembly ("memory-safe") {
liquidity := mload(add(position, 0x20))
// feesPerLiquidityInsideLast is now at offset 0x40 due to extraData field
let positionFpl := mload(add(position, 0x40))
difference0 := sub(mload(feesPerLiquidityInside), mload(positionFpl))
difference1 := sub(mload(add(feesPerLiquidityInside, 0x20)), mload(add(positionFpl, 0x20)))
}
return (
uint128(FixedPointMathLib.fullMulDivN(difference0, liquidity, 128)),
uint128(FixedPointMathLib.fullMulDivN(difference1, liquidity, 128))
);
}// SPDX-License-Identifier: ekubo-license-v1.eth
pragma solidity =0.8.33;
import {MAX_TICK_MAGNITUDE} from "./constants.sol";
import {FixedPointMathLib} from "solady/utils/FixedPointMathLib.sol";
import {LibBit} from "solady/utils/LibBit.sol";
import {SqrtRatio, toSqrtRatio} from "../types/sqrtRatio.sol";
// Tick Math Library
// Contains functions for converting between ticks and sqrt price ratios
// Ticks represent discrete price points, while sqrt ratios represent the actual prices
// The relationship is: sqrtRatio = sqrt(1.000001^tick)
/// @notice Thrown when a tick value is outside the valid range
/// @param tick The invalid tick value
error InvalidTick(int32 tick);
/// @notice Converts a tick to its corresponding sqrt price ratio
/// @dev Uses bit manipulation and precomputed constants for gas efficiency
/// @param tick The tick to convert (must be within MIN_TICK and MAX_TICK)
/// @return r The sqrt price ratio corresponding to the tick
function tickToSqrtRatio(int32 tick) pure returns (SqrtRatio r) {
unchecked {
uint256 t = FixedPointMathLib.abs(tick);
if (t > MAX_TICK_MAGNITUDE) revert InvalidTick(tick);
uint256 ratio;
assembly ("memory-safe") {
// bit 0 is handled with a single conditional subtract from 2^128
ratio := sub(0x100000000000000000000000000000000, mul(and(t, 0x1), 0x8637b66cd638344daef276cd7c5))
// -------- Gate 1: bits 1..7 (mask 0xFE) --------
if and(t, 0xFE) {
if and(t, 0x2) { ratio := shr(128, mul(ratio, 0xffffef390978c398134b4ff3764fe410)) }
if and(t, 0x4) { ratio := shr(128, mul(ratio, 0xffffde72140b00a354bd3dc828e976c9)) }
if and(t, 0x8) { ratio := shr(128, mul(ratio, 0xffffbce42c7be6c998ad6318193c0b18)) }
if and(t, 0x10) { ratio := shr(128, mul(ratio, 0xffff79c86a8f6150a32d9778eceef97c)) }
if and(t, 0x20) { ratio := shr(128, mul(ratio, 0xfffef3911b7cff24ba1b3dbb5f8f5974)) }
if and(t, 0x40) { ratio := shr(128, mul(ratio, 0xfffde72350725cc4ea8feece3b5f13c8)) }
if and(t, 0x80) { ratio := shr(128, mul(ratio, 0xfffbce4b06c196e9247ac87695d53c60)) }
}
// -------- Gate 2: bits 8..14 (mask 0x7F00) --------
if and(t, 0x7F00) {
if and(t, 0x100) { ratio := shr(128, mul(ratio, 0xfff79ca7a4d1bf1ee8556cea23cdbaa5)) }
if and(t, 0x200) { ratio := shr(128, mul(ratio, 0xffef3995a5b6a6267530f207142a5764)) }
if and(t, 0x400) { ratio := shr(128, mul(ratio, 0xffde7444b28145508125d10077ba83b8)) }
if and(t, 0x800) { ratio := shr(128, mul(ratio, 0xffbceceeb791747f10df216f2e53ec57)) }
if and(t, 0x1000) { ratio := shr(128, mul(ratio, 0xff79eb706b9a64c6431d76e63531e929)) }
if and(t, 0x2000) { ratio := shr(128, mul(ratio, 0xfef41d1a5f2ae3a20676bec6f7f9459a)) }
if and(t, 0x4000) { ratio := shr(128, mul(ratio, 0xfde95287d26d81bea159c37073122c73)) }
}
// -------- Gate 3: bits 15..20 (mask 0x1F8000) --------
if and(t, 0x1F8000) {
if and(t, 0x8000) { ratio := shr(128, mul(ratio, 0xfbd701c7cbc4c8a6bb81efd232d1e4e7)) }
if and(t, 0x10000) { ratio := shr(128, mul(ratio, 0xf7bf5211c72f5185f372aeb1d48f937e)) }
if and(t, 0x20000) { ratio := shr(128, mul(ratio, 0xefc2bf59df33ecc28125cf78ec4f167f)) }
if and(t, 0x40000) { ratio := shr(128, mul(ratio, 0xe08d35706200796273f0b3a981d90cfd)) }
if and(t, 0x80000) { ratio := shr(128, mul(ratio, 0xc4f76b68947482dc198a48a54348c4ed)) }
if and(t, 0x100000) { ratio := shr(128, mul(ratio, 0x978bcb9894317807e5fa4498eee7c0fa)) }
}
// -------- Gate 4: bits 21..26 (mask 0x7E00000) --------
if and(t, 0x7E00000) {
if and(t, 0x200000) { ratio := shr(128, mul(ratio, 0x59b63684b86e9f486ec54727371ba6ca)) }
if and(t, 0x400000) { ratio := shr(128, mul(ratio, 0x1f703399d88f6aa83a28b22d4a1f56e3)) }
if and(t, 0x800000) { ratio := shr(128, mul(ratio, 0x3dc5dac7376e20fc8679758d1bcdcfc)) }
if and(t, 0x1000000) { ratio := shr(128, mul(ratio, 0xee7e32d61fdb0a5e622b820f681d0)) }
if and(t, 0x2000000) { ratio := shr(128, mul(ratio, 0xde2ee4bc381afa7089aa84bb66)) }
if and(t, 0x4000000) { ratio := shr(128, mul(ratio, 0xc0d55d4d7152c25fb139)) }
}
// If original tick > 0, invert: ratio = maxUint / ratio
if sgt(tick, 0) { ratio := div(not(0), ratio) }
}
r = toSqrtRatio(ratio, false);
}
}
uint256 constant ONE_Q127 = 1 << 127;
// Convert ln(m) series to log2(m): log2(m) = (2 / ln 2) * s.
// Precompute K = round((2 / ln 2) * 2^64) as a uint (Q64 scalar).
// K = 53226052391377289966 (≈ 0x2e2a8eca5705fc2ee)
uint256 constant K_2_OVER_LN2_X64 = 53226052391377289966;
// 2^64 / log2(sqrt(1.000001)) for converting from log base 2 in X64 to log base tick
int256 constant INV_LB_X64 = 25572630076711825471857579;
// Error bounds of the tick computation based on the number of iterations ~= +-0.002 ticks
int256 constant ERROR_BOUNDS_X128 = int256((uint256(1) << 128) / 485);
/// @notice Converts a sqrt price ratio to its corresponding tick
/// @dev Computes log2 via one normalization + atanh series (no per-bit squaring loop)
/// @param sqrtRatio The valid sqrt price ratio to convert
/// @return tick The tick corresponding to the sqrt ratio
function sqrtRatioToTick(SqrtRatio sqrtRatio) pure returns (int32 tick) {
unchecked {
uint256 sqrtRatioFixed = sqrtRatio.toFixed();
// Normalize sign via reciprocal if < 1. Keep this branch-free.
bool negative;
uint256 x;
uint256 hi;
assembly ("memory-safe") {
negative := iszero(shr(128, sqrtRatioFixed))
// x = negative ? (type(uint256).max / R) : R
x := add(div(sub(0, negative), sqrtRatioFixed), mul(iszero(negative), sqrtRatioFixed))
// We know (x >> 128) != 0 because we reciprocated sqrtRatioFixed
hi := shr(128, x)
}
// Integer part of log2 via CLZ: floor(log2(hi)) = 255 - clz(hi)
uint256 msbHigh;
assembly ("memory-safe") {
msbHigh := sub(255, clz(hi))
}
// Reduce once so X ∈ [2^127, 2^128) (Q1.127 mantissa)
x = x >> (msbHigh + 1);
// Fractional log2 using atanh on y = (m-1)/(m+1), m = X/2^127 ∈ [1,2)
uint256 a = x - ONE_Q127; // (m - 1) * 2^127
uint256 b = x + ONE_Q127; // (m + 1) * 2^127
uint256 yQ = FixedPointMathLib.rawDiv(a << 127, b); // y in Q1.127
// Build odd powers via y^2 ladder
uint256 y2 = (yQ * yQ) >> 127; // y^2
uint256 y3 = (yQ * y2) >> 127; // y^3
uint256 y5 = (y3 * y2) >> 127; // y^5
uint256 y7 = (y5 * y2) >> 127; // y^7
uint256 y9 = (y7 * y2) >> 127; // y^9
uint256 y11 = (y9 * y2) >> 127; // y^11
uint256 y13 = (y11 * y2) >> 127; // y^13
uint256 y15 = (y13 * y2) >> 127; // y^15
// s = y + y^3/3 + y^5/5 + ... + y^15/15 (Q1.127)
uint256 s = yQ + (y3 / 3) + (y5 / 5) + (y7 / 7) + (y9 / 9) + (y11 / 11) + (y13 / 13) + (y15 / 15);
// fracX64 = ((2/ln2) * s) in Q64.64 => (s * K) >> 127
uint256 fracX64 = (s * K_2_OVER_LN2_X64) >> 127;
// Unsigned log2 in Q64.64
uint256 log2Unsigned = (msbHigh << 64) + fracX64;
// Map log2 to tick-space X128
int256 base = negative ? -int256(log2Unsigned) : int256(log2Unsigned);
int256 logBaseTickSizeX128 = base * INV_LB_X64;
// Add error bounds to the computed logarithm
int32 tickLow = int32((logBaseTickSizeX128 - ERROR_BOUNDS_X128) >> 128);
tick = int32((logBaseTickSizeX128 + ERROR_BOUNDS_X128) >> 128);
if (tick != tickLow) {
// tickHigh overshoots
if (tickToSqrtRatio(tick) > sqrtRatio) {
tick = tickLow;
}
}
}
}// SPDX-License-Identifier: ekubo-license-v1.eth
pragma solidity =0.8.33;
import {PoolId} from "../types/poolId.sol";
import {PositionId} from "../types/positionId.sol";
import {StorageSlot} from "../types/storageSlot.sol";
/// @title Core Storage Layout
/// @notice Library providing functions to compute the storage locations for the Core contract
/// @dev Core uses a custom storage layout to avoid keccak's where possible.
/// For certain storage values, the pool id is used as a base offset and
/// we allocate the following relative offsets (starting from the pool id) as:
/// 0: pool state
/// [FPL_OFFSET, FPL_OFFSET + 1]: fees per liquidity
/// [TICKS_OFFSET + MIN_TICK, TICKS_OFFSET + MAX_TICK]: tick info
/// [FPL_OUTSIDE_OFFSET_VALUE0 + MIN_TICK, FPL_OUTSIDE_OFFSET_VALUE0 + MAX_TICK]: fees per liquidity outside (value0)
/// [FPL_OUTSIDE_OFFSET_VALUE0 + FPL_OUTSIDE_OFFSET_VALUE1 + MIN_TICK, FPL_OUTSIDE_OFFSET_VALUE0 + FPL_OUTSIDE_OFFSET_VALUE1 + MAX_TICK]: fees per liquidity outside (value1)
/// [BITMAPS_OFFSET + FIRST_BITMAP_WORD, BITMAPS_OFFSET + LAST_BITMAP_WORD]: tick bitmaps
library CoreStorageLayout {
/// @dev Generated using: cast keccak "CoreStorageLayout#FPL_OFFSET"
uint256 internal constant FPL_OFFSET = 0xb09b03866d96933565a9435bfb511c8ac5b2be454285ca331201452704799f72;
/// @dev Generated using: cast keccak "CoreStorageLayout#TICKS_OFFSET"
uint256 internal constant TICKS_OFFSET = 0x435a5eb89a296820174331cf5a3902d9fca683928d56726d8e7acd6efb28c568;
/// @dev Generated using: cast keccak "CoreStorageLayout#FPL_OUTSIDE_OFFSET_VALUE0"
uint256 internal constant FPL_OUTSIDE_OFFSET_VALUE0 =
0x5695060fdb9cfea656f872ae4887221aff7dbfefc45eaf753e4e70cdfb5cd19c;
/// @dev Generated using: cast keccak "CoreStorageLayout#FPL_OUTSIDE_OFFSET_VALUE1"
uint256 internal constant FPL_OUTSIDE_OFFSET_VALUE1 =
0x7a2a03fc08af3dae7869678617dc8abe8f15a3b719b37ba108dba879571f8b02;
/// @dev Generated using: cast keccak "CoreStorageLayout#BITMAPS_OFFSET"
uint256 internal constant BITMAPS_OFFSET = 0x3def450d0010a2fef515ce5eba4b363b5a0f42fdd4c53e1c737975db05a2e3a5;
/// @notice Computes the storage slot containing information on whether an extension is registered
/// @param extension The extension address to check
/// @return slot The storage slot in the Core contract
function isExtensionRegisteredSlot(address extension) internal pure returns (StorageSlot slot) {
assembly ("memory-safe") {
mstore(0, extension)
mstore(32, 0)
slot := keccak256(0, 64)
}
}
/// @notice Computes the storage slot of the current state of a pool
/// @param poolId The unique identifier for the pool
/// @return slot The storage slot in the Core contract
function poolStateSlot(PoolId poolId) internal pure returns (StorageSlot slot) {
slot = StorageSlot.wrap(PoolId.unwrap(poolId));
}
/// @notice Computes the storage slots of the current global fees per liquidity of a pool
/// @param poolId The unique identifier for the pool
/// @return firstSlot The first of two consecutive storage slots in the Core contract
function poolFeesPerLiquiditySlot(PoolId poolId) internal pure returns (StorageSlot firstSlot) {
assembly ("memory-safe") {
firstSlot := add(poolId, FPL_OFFSET)
}
}
/// @notice Computes the storage slot of tick information for a specific tick in a pool
/// @param poolId The unique identifier for the pool
/// @param tick The tick to query
/// @return slot The storage slot in the Core contract
function poolTicksSlot(PoolId poolId, int32 tick) internal pure returns (StorageSlot slot) {
assembly ("memory-safe") {
slot := add(poolId, add(tick, TICKS_OFFSET))
}
}
/// @notice Computes the storage slots of the outside fees of a pool for a given tick
/// @param poolId The unique identifier for the pool
/// @param tick The tick to query
/// @return firstSlot The first storage slot in the Core contract
/// @return secondSlot The second storage slot in the Core contract
function poolTickFeesPerLiquidityOutsideSlot(PoolId poolId, int32 tick)
internal
pure
returns (StorageSlot firstSlot, StorageSlot secondSlot)
{
assembly ("memory-safe") {
firstSlot := add(poolId, add(FPL_OUTSIDE_OFFSET_VALUE0, tick))
secondSlot := add(firstSlot, FPL_OUTSIDE_OFFSET_VALUE1)
}
}
/// @notice Computes the first storage slot of the tick bitmaps for a specific pool
/// @param poolId The unique identifier for the pool
/// @return firstSlot The first storage slot in the Core contract
function tickBitmapsSlot(PoolId poolId) internal pure returns (StorageSlot firstSlot) {
assembly ("memory-safe") {
firstSlot := add(poolId, BITMAPS_OFFSET)
}
}
/// @notice Computes the first storage slot of the position data for a specific position in a pool
/// @param poolId The unique identifier for the pool
/// @param owner The position owner
/// @param positionId The unique identifier for the position
/// @return firstSlot The first of three consecutive storage slots in the Core contract
function poolPositionsSlot(PoolId poolId, address owner, PositionId positionId)
internal
pure
returns (StorageSlot firstSlot)
{
assembly ("memory-safe") {
let free := mload(0x40)
mstore(free, positionId)
mstore(add(free, 0x20), poolId)
mstore(add(free, 0x40), owner)
mstore(0, keccak256(free, 0x60))
mstore(32, 1)
firstSlot := keccak256(0, 64)
}
}
/// @notice Computes the storage slot for saved balances
/// @param owner The owner of the saved balances
/// @param token0 The first token address
/// @param token1 The second token address
/// @param salt The salt used for the saved balance key
/// @return slot The storage slot in the Core contract
function savedBalancesSlot(address owner, address token0, address token1, bytes32 salt)
internal
pure
returns (StorageSlot slot)
{
assembly ("memory-safe") {
let free := mload(0x40)
mstore(free, owner)
mstore(add(free, 0x20), token0)
mstore(add(free, 0x40), token1)
mstore(add(free, 0x60), salt)
slot := keccak256(free, 128)
}
}
}// SPDX-License-Identifier: ekubo-license-v1.eth
pragma solidity =0.8.33;
import {IExtension} from "../interfaces/ICore.sol";
import {PoolKey} from "../types/poolKey.sol";
import {PositionId} from "../types/positionId.sol";
import {SqrtRatio} from "../types/sqrtRatio.sol";
import {PoolState} from "../types/poolState.sol";
import {SwapParameters} from "../types/swapParameters.sol";
import {Locker} from "../types/locker.sol";
import {PoolBalanceUpdate} from "../types/poolBalanceUpdate.sol";
/// @dev Contains methods for determining whether an extension should be called
library ExtensionCallPointsLib {
function shouldCallBeforeInitializePool(IExtension extension, address initializer)
internal
pure
returns (bool yes)
{
assembly ("memory-safe") {
yes := and(shr(152, extension), iszero(eq(initializer, extension)))
}
}
function maybeCallBeforeInitializePool(
IExtension extension,
address initializer,
PoolKey memory poolKey,
int32 tick
) internal {
bool needCall = shouldCallBeforeInitializePool(extension, initializer);
assembly ("memory-safe") {
if needCall {
let freeMem := mload(0x40)
// cast sig "beforeInitializePool(address, (address, address, bytes32), int32)"
mstore(freeMem, shl(224, 0x1fbbb462))
mstore(add(freeMem, 4), initializer)
mcopy(add(freeMem, 36), poolKey, 96)
mstore(add(freeMem, 132), tick)
// bubbles up the revert
if iszero(call(gas(), extension, 0, freeMem, 164, 0, 0)) {
returndatacopy(freeMem, 0, returndatasize())
revert(freeMem, returndatasize())
}
}
}
}
function shouldCallAfterInitializePool(IExtension extension, address initializer) internal pure returns (bool yes) {
assembly ("memory-safe") {
yes := and(shr(159, extension), iszero(eq(initializer, extension)))
}
}
function maybeCallAfterInitializePool(
IExtension extension,
address initializer,
PoolKey memory poolKey,
int32 tick,
SqrtRatio sqrtRatio
) internal {
bool needCall = shouldCallAfterInitializePool(extension, initializer);
assembly ("memory-safe") {
if needCall {
let freeMem := mload(0x40)
// cast sig "afterInitializePool(address, (address, address, bytes32), int32, uint96)"
mstore(freeMem, shl(224, 0x948374ff))
mstore(add(freeMem, 4), initializer)
mcopy(add(freeMem, 36), poolKey, 96)
mstore(add(freeMem, 132), tick)
mstore(add(freeMem, 164), sqrtRatio)
// bubbles up the revert
if iszero(call(gas(), extension, 0, freeMem, 196, 0, 0)) {
returndatacopy(freeMem, 0, returndatasize())
revert(freeMem, returndatasize())
}
}
}
}
function shouldCallBeforeSwap(IExtension extension, Locker locker) internal pure returns (bool yes) {
assembly ("memory-safe") {
yes := and(shr(158, extension), iszero(eq(shl(96, locker), shl(96, extension))))
}
}
function maybeCallBeforeSwap(IExtension extension, Locker locker, PoolKey memory poolKey, SwapParameters params)
internal
{
bool needCall = shouldCallBeforeSwap(extension, locker);
assembly ("memory-safe") {
if needCall {
let freeMem := mload(0x40)
// cast sig "beforeSwap(bytes32,(address,address,bytes32),bytes32)"
mstore(freeMem, shl(224, 0xca11dba7))
mstore(add(freeMem, 4), locker)
mcopy(add(freeMem, 36), poolKey, 96)
mstore(add(freeMem, 132), params)
// bubbles up the revert
if iszero(call(gas(), extension, 0, freeMem, 164, 0, 0)) {
returndatacopy(freeMem, 0, returndatasize())
revert(freeMem, returndatasize())
}
}
}
}
function shouldCallAfterSwap(IExtension extension, Locker locker) internal pure returns (bool yes) {
assembly ("memory-safe") {
yes := and(shr(157, extension), iszero(eq(shl(96, locker), shl(96, extension))))
}
}
function maybeCallAfterSwap(
IExtension extension,
Locker locker,
PoolKey memory poolKey,
SwapParameters params,
PoolBalanceUpdate balanceUpdate,
PoolState stateAfter
) internal {
bool needCall = shouldCallAfterSwap(extension, locker);
assembly ("memory-safe") {
if needCall {
let freeMem := mload(0x40)
// cast sig "afterSwap(bytes32,(address,address,bytes32),bytes32,bytes32,bytes32)"
mstore(freeMem, shl(224, 0xa4e8f288))
mstore(add(freeMem, 4), locker)
mcopy(add(freeMem, 36), poolKey, 96)
mstore(add(freeMem, 132), params)
mstore(add(freeMem, 164), balanceUpdate)
mstore(add(freeMem, 196), stateAfter)
// bubbles up the revert
if iszero(call(gas(), extension, 0, freeMem, 228, 0, 0)) {
returndatacopy(freeMem, 0, returndatasize())
revert(freeMem, returndatasize())
}
}
}
}
function shouldCallBeforeUpdatePosition(IExtension extension, Locker locker) internal pure returns (bool yes) {
assembly ("memory-safe") {
yes := and(shr(156, extension), iszero(eq(shl(96, locker), shl(96, extension))))
}
}
function maybeCallBeforeUpdatePosition(
IExtension extension,
Locker locker,
PoolKey memory poolKey,
PositionId positionId,
int128 liquidityDelta
) internal {
bool needCall = shouldCallBeforeUpdatePosition(extension, locker);
assembly ("memory-safe") {
if needCall {
let freeMem := mload(0x40)
// cast sig "beforeUpdatePosition(bytes32, (address,address,bytes32), bytes32, int128)"
mstore(freeMem, shl(224, 0x0035c723))
mstore(add(freeMem, 4), locker)
mcopy(add(freeMem, 36), poolKey, 96)
mstore(add(freeMem, 132), positionId)
mstore(add(freeMem, 164), liquidityDelta)
// bubbles up the revert
if iszero(call(gas(), extension, 0, freeMem, 196, 0, 0)) {
returndatacopy(freeMem, 0, returndatasize())
revert(freeMem, returndatasize())
}
}
}
}
function shouldCallAfterUpdatePosition(IExtension extension, Locker locker) internal pure returns (bool yes) {
assembly ("memory-safe") {
yes := and(shr(155, extension), iszero(eq(shl(96, locker), shl(96, extension))))
}
}
function maybeCallAfterUpdatePosition(
IExtension extension,
Locker locker,
PoolKey memory poolKey,
PositionId positionId,
int128 liquidityDelta,
PoolBalanceUpdate balanceUpdate,
PoolState stateAfter
) internal {
bool needCall = shouldCallAfterUpdatePosition(extension, locker);
assembly ("memory-safe") {
if needCall {
let freeMem := mload(0x40)
// cast sig "afterUpdatePosition(bytes32,(address,address,bytes32),bytes32,int128,bytes32,bytes32)"
mstore(freeMem, shl(224, 0x25fa4e69))
mstore(add(freeMem, 4), locker)
mcopy(add(freeMem, 36), poolKey, 96)
mstore(add(freeMem, 132), positionId)
mstore(add(freeMem, 164), liquidityDelta)
mstore(add(freeMem, 196), balanceUpdate)
mstore(add(freeMem, 228), stateAfter)
// bubbles up the revert
if iszero(call(gas(), extension, 0, freeMem, 260, 0, 0)) {
returndatacopy(freeMem, 0, returndatasize())
revert(freeMem, returndatasize())
}
}
}
}
function shouldCallBeforeCollectFees(IExtension extension, Locker locker) internal pure returns (bool yes) {
assembly ("memory-safe") {
yes := and(shr(154, extension), iszero(eq(shl(96, locker), shl(96, extension))))
}
}
function maybeCallBeforeCollectFees(
IExtension extension,
Locker locker,
PoolKey memory poolKey,
PositionId positionId
) internal {
bool needCall = shouldCallBeforeCollectFees(extension, locker);
assembly ("memory-safe") {
if needCall {
let freeMem := mload(0x40)
// cast sig "beforeCollectFees(bytes32, (address,address,bytes32), bytes32)"
mstore(freeMem, shl(224, 0xdf65d8d1))
mstore(add(freeMem, 4), locker)
mcopy(add(freeMem, 36), poolKey, 96)
mstore(add(freeMem, 132), positionId)
// bubbles up the revert
if iszero(call(gas(), extension, 0, freeMem, 164, 0, 0)) {
returndatacopy(freeMem, 0, returndatasize())
revert(freeMem, returndatasize())
}
}
}
}
function shouldCallAfterCollectFees(IExtension extension, Locker locker) internal pure returns (bool yes) {
assembly ("memory-safe") {
yes := and(shr(153, extension), iszero(eq(shl(96, locker), shl(96, extension))))
}
}
function maybeCallAfterCollectFees(
IExtension extension,
Locker locker,
PoolKey memory poolKey,
PositionId positionId,
uint128 amount0,
uint128 amount1
) internal {
bool needCall = shouldCallAfterCollectFees(extension, locker);
assembly ("memory-safe") {
if needCall {
let freeMem := mload(0x40)
// cast sig "afterCollectFees(bytes32, (address,address,bytes32), bytes32, uint128, uint128)"
mstore(freeMem, shl(224, 0x751fd5df))
mstore(add(freeMem, 4), locker)
mcopy(add(freeMem, 36), poolKey, 96)
mstore(add(freeMem, 132), positionId)
mstore(add(freeMem, 164), amount0)
mstore(add(freeMem, 196), amount1)
// bubbles up the revert
if iszero(call(gas(), extension, 0, freeMem, 228, 0, 0)) {
returndatacopy(freeMem, 0, returndatasize())
revert(freeMem, returndatasize())
}
}
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Arithmetic library with operations for fixed-point numbers.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/FixedPointMathLib.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/FixedPointMathLib.sol)
library FixedPointMathLib {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The operation failed, as the output exceeds the maximum value of uint256.
error ExpOverflow();
/// @dev The operation failed, as the output exceeds the maximum value of uint256.
error FactorialOverflow();
/// @dev The operation failed, due to an overflow.
error RPowOverflow();
/// @dev The mantissa is too big to fit.
error MantissaOverflow();
/// @dev The operation failed, due to an multiplication overflow.
error MulWadFailed();
/// @dev The operation failed, due to an multiplication overflow.
error SMulWadFailed();
/// @dev The operation failed, either due to a multiplication overflow, or a division by a zero.
error DivWadFailed();
/// @dev The operation failed, either due to a multiplication overflow, or a division by a zero.
error SDivWadFailed();
/// @dev The operation failed, either due to a multiplication overflow, or a division by a zero.
error MulDivFailed();
/// @dev The division failed, as the denominator is zero.
error DivFailed();
/// @dev The full precision multiply-divide operation failed, either due
/// to the result being larger than 256 bits, or a division by a zero.
error FullMulDivFailed();
/// @dev The output is undefined, as the input is less-than-or-equal to zero.
error LnWadUndefined();
/// @dev The input outside the acceptable domain.
error OutOfDomain();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTANTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The scalar of ETH and most ERC20s.
uint256 internal constant WAD = 1e18;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* SIMPLIFIED FIXED POINT OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Equivalent to `(x * y) / WAD` rounded down.
function mulWad(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// Equivalent to `require(y == 0 || x <= type(uint256).max / y)`.
if gt(x, div(not(0), y)) {
if y {
mstore(0x00, 0xbac65e5b) // `MulWadFailed()`.
revert(0x1c, 0x04)
}
}
z := div(mul(x, y), WAD)
}
}
/// @dev Equivalent to `(x * y) / WAD` rounded down.
function sMulWad(int256 x, int256 y) internal pure returns (int256 z) {
/// @solidity memory-safe-assembly
assembly {
z := mul(x, y)
// Equivalent to `require((x == 0 || z / x == y) && !(x == -1 && y == type(int256).min))`.
if iszero(gt(or(iszero(x), eq(sdiv(z, x), y)), lt(not(x), eq(y, shl(255, 1))))) {
mstore(0x00, 0xedcd4dd4) // `SMulWadFailed()`.
revert(0x1c, 0x04)
}
z := sdiv(z, WAD)
}
}
/// @dev Equivalent to `(x * y) / WAD` rounded down, but without overflow checks.
function rawMulWad(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := div(mul(x, y), WAD)
}
}
/// @dev Equivalent to `(x * y) / WAD` rounded down, but without overflow checks.
function rawSMulWad(int256 x, int256 y) internal pure returns (int256 z) {
/// @solidity memory-safe-assembly
assembly {
z := sdiv(mul(x, y), WAD)
}
}
/// @dev Equivalent to `(x * y) / WAD` rounded up.
function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := mul(x, y)
// Equivalent to `require(y == 0 || x <= type(uint256).max / y)`.
if iszero(eq(div(z, y), x)) {
if y {
mstore(0x00, 0xbac65e5b) // `MulWadFailed()`.
revert(0x1c, 0x04)
}
}
z := add(iszero(iszero(mod(z, WAD))), div(z, WAD))
}
}
/// @dev Equivalent to `(x * y) / WAD` rounded up, but without overflow checks.
function rawMulWadUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := add(iszero(iszero(mod(mul(x, y), WAD))), div(mul(x, y), WAD))
}
}
/// @dev Equivalent to `(x * WAD) / y` rounded down.
function divWad(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// Equivalent to `require(y != 0 && x <= type(uint256).max / WAD)`.
if iszero(mul(y, lt(x, add(1, div(not(0), WAD))))) {
mstore(0x00, 0x7c5f487d) // `DivWadFailed()`.
revert(0x1c, 0x04)
}
z := div(mul(x, WAD), y)
}
}
/// @dev Equivalent to `(x * WAD) / y` rounded down.
function sDivWad(int256 x, int256 y) internal pure returns (int256 z) {
/// @solidity memory-safe-assembly
assembly {
z := mul(x, WAD)
// Equivalent to `require(y != 0 && ((x * WAD) / WAD == x))`.
if iszero(mul(y, eq(sdiv(z, WAD), x))) {
mstore(0x00, 0x5c43740d) // `SDivWadFailed()`.
revert(0x1c, 0x04)
}
z := sdiv(z, y)
}
}
/// @dev Equivalent to `(x * WAD) / y` rounded down, but without overflow and divide by zero checks.
function rawDivWad(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := div(mul(x, WAD), y)
}
}
/// @dev Equivalent to `(x * WAD) / y` rounded down, but without overflow and divide by zero checks.
function rawSDivWad(int256 x, int256 y) internal pure returns (int256 z) {
/// @solidity memory-safe-assembly
assembly {
z := sdiv(mul(x, WAD), y)
}
}
/// @dev Equivalent to `(x * WAD) / y` rounded up.
function divWadUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// Equivalent to `require(y != 0 && x <= type(uint256).max / WAD)`.
if iszero(mul(y, lt(x, add(1, div(not(0), WAD))))) {
mstore(0x00, 0x7c5f487d) // `DivWadFailed()`.
revert(0x1c, 0x04)
}
z := add(iszero(iszero(mod(mul(x, WAD), y))), div(mul(x, WAD), y))
}
}
/// @dev Equivalent to `(x * WAD) / y` rounded up, but without overflow and divide by zero checks.
function rawDivWadUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := add(iszero(iszero(mod(mul(x, WAD), y))), div(mul(x, WAD), y))
}
}
/// @dev Equivalent to `x` to the power of `y`.
/// because `x ** y = (e ** ln(x)) ** y = e ** (ln(x) * y)`.
/// Note: This function is an approximation.
function powWad(int256 x, int256 y) internal pure returns (int256) {
// Using `ln(x)` means `x` must be greater than 0.
return expWad((lnWad(x) * y) / int256(WAD));
}
/// @dev Returns `exp(x)`, denominated in `WAD`.
/// Credit to Remco Bloemen under MIT license: https://2π.com/22/exp-ln
/// Note: This function is an approximation. Monotonically increasing.
function expWad(int256 x) internal pure returns (int256 r) {
unchecked {
// When the result is less than 0.5 we return zero.
// This happens when `x <= (log(1e-18) * 1e18) ~ -4.15e19`.
if (x <= -41446531673892822313) return r;
/// @solidity memory-safe-assembly
assembly {
// When the result is greater than `(2**255 - 1) / 1e18` we can not represent it as
// an int. This happens when `x >= floor(log((2**255 - 1) / 1e18) * 1e18) ≈ 135`.
if iszero(slt(x, 135305999368893231589)) {
mstore(0x00, 0xa37bfec9) // `ExpOverflow()`.
revert(0x1c, 0x04)
}
}
// `x` is now in the range `(-42, 136) * 1e18`. Convert to `(-42, 136) * 2**96`
// for more intermediate precision and a binary basis. This base conversion
// is a multiplication by 1e18 / 2**96 = 5**18 / 2**78.
x = (x << 78) / 5 ** 18;
// Reduce range of x to (-½ ln 2, ½ ln 2) * 2**96 by factoring out powers
// of two such that exp(x) = exp(x') * 2**k, where k is an integer.
// Solving this gives k = round(x / log(2)) and x' = x - k * log(2).
int256 k = ((x << 96) / 54916777467707473351141471128 + 2 ** 95) >> 96;
x = x - k * 54916777467707473351141471128;
// `k` is in the range `[-61, 195]`.
// Evaluate using a (6, 7)-term rational approximation.
// `p` is made monic, we'll multiply by a scale factor later.
int256 y = x + 1346386616545796478920950773328;
y = ((y * x) >> 96) + 57155421227552351082224309758442;
int256 p = y + x - 94201549194550492254356042504812;
p = ((p * y) >> 96) + 28719021644029726153956944680412240;
p = p * x + (4385272521454847904659076985693276 << 96);
// We leave `p` in `2**192` basis so we don't need to scale it back up for the division.
int256 q = x - 2855989394907223263936484059900;
q = ((q * x) >> 96) + 50020603652535783019961831881945;
q = ((q * x) >> 96) - 533845033583426703283633433725380;
q = ((q * x) >> 96) + 3604857256930695427073651918091429;
q = ((q * x) >> 96) - 14423608567350463180887372962807573;
q = ((q * x) >> 96) + 26449188498355588339934803723976023;
/// @solidity memory-safe-assembly
assembly {
// Div in assembly because solidity adds a zero check despite the unchecked.
// The q polynomial won't have zeros in the domain as all its roots are complex.
// No scaling is necessary because p is already `2**96` too large.
r := sdiv(p, q)
}
// r should be in the range `(0.09, 0.25) * 2**96`.
// We now need to multiply r by:
// - The scale factor `s ≈ 6.031367120`.
// - The `2**k` factor from the range reduction.
// - The `1e18 / 2**96` factor for base conversion.
// We do this all at once, with an intermediate result in `2**213`
// basis, so the final right shift is always by a positive amount.
r = int256(
(uint256(r) * 3822833074963236453042738258902158003155416615667) >> uint256(195 - k)
);
}
}
/// @dev Returns `ln(x)`, denominated in `WAD`.
/// Credit to Remco Bloemen under MIT license: https://2π.com/22/exp-ln
/// Note: This function is an approximation. Monotonically increasing.
function lnWad(int256 x) internal pure returns (int256 r) {
/// @solidity memory-safe-assembly
assembly {
// We want to convert `x` from `10**18` fixed point to `2**96` fixed point.
// We do this by multiplying by `2**96 / 10**18`. But since
// `ln(x * C) = ln(x) + ln(C)`, we can simply do nothing here
// and add `ln(2**96 / 10**18)` at the end.
// Compute `k = log2(x) - 96`, `r = 159 - k = 255 - log2(x) = 255 ^ log2(x)`.
r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x))
r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
r := or(r, shl(4, lt(0xffff, shr(r, x))))
r := or(r, shl(3, lt(0xff, shr(r, x))))
// We place the check here for more optimal stack operations.
if iszero(sgt(x, 0)) {
mstore(0x00, 0x1615e638) // `LnWadUndefined()`.
revert(0x1c, 0x04)
}
// forgefmt: disable-next-item
r := xor(r, byte(and(0x1f, shr(shr(r, x), 0x8421084210842108cc6318c6db6d54be)),
0xf8f9f9faf9fdfafbf9fdfcfdfafbfcfef9fafdfafcfcfbfefafafcfbffffffff))
// Reduce range of x to (1, 2) * 2**96
// ln(2^k * x) = k * ln(2) + ln(x)
x := shr(159, shl(r, x))
// Evaluate using a (8, 8)-term rational approximation.
// `p` is made monic, we will multiply by a scale factor later.
// forgefmt: disable-next-item
let p := sub( // This heavily nested expression is to avoid stack-too-deep for via-ir.
sar(96, mul(add(43456485725739037958740375743393,
sar(96, mul(add(24828157081833163892658089445524,
sar(96, mul(add(3273285459638523848632254066296,
x), x))), x))), x)), 11111509109440967052023855526967)
p := sub(sar(96, mul(p, x)), 45023709667254063763336534515857)
p := sub(sar(96, mul(p, x)), 14706773417378608786704636184526)
p := sub(mul(p, x), shl(96, 795164235651350426258249787498))
// We leave `p` in `2**192` basis so we don't need to scale it back up for the division.
// `q` is monic by convention.
let q := add(5573035233440673466300451813936, x)
q := add(71694874799317883764090561454958, sar(96, mul(x, q)))
q := add(283447036172924575727196451306956, sar(96, mul(x, q)))
q := add(401686690394027663651624208769553, sar(96, mul(x, q)))
q := add(204048457590392012362485061816622, sar(96, mul(x, q)))
q := add(31853899698501571402653359427138, sar(96, mul(x, q)))
q := add(909429971244387300277376558375, sar(96, mul(x, q)))
// `p / q` is in the range `(0, 0.125) * 2**96`.
// Finalization, we need to:
// - Multiply by the scale factor `s = 5.549…`.
// - Add `ln(2**96 / 10**18)`.
// - Add `k * ln(2)`.
// - Multiply by `10**18 / 2**96 = 5**18 >> 78`.
// The q polynomial is known not to have zeros in the domain.
// No scaling required because p is already `2**96` too large.
p := sdiv(p, q)
// Multiply by the scaling factor: `s * 5**18 * 2**96`, base is now `5**18 * 2**192`.
p := mul(1677202110996718588342820967067443963516166, p)
// Add `ln(2) * k * 5**18 * 2**192`.
// forgefmt: disable-next-item
p := add(mul(16597577552685614221487285958193947469193820559219878177908093499208371, sub(159, r)), p)
// Add `ln(2**96 / 10**18) * 5**18 * 2**192`.
p := add(600920179829731861736702779321621459595472258049074101567377883020018308, p)
// Base conversion: mul `2**18 / 2**192`.
r := sar(174, p)
}
}
/// @dev Returns `W_0(x)`, denominated in `WAD`.
/// See: https://en.wikipedia.org/wiki/Lambert_W_function
/// a.k.a. Product log function. This is an approximation of the principal branch.
/// Note: This function is an approximation. Monotonically increasing.
function lambertW0Wad(int256 x) internal pure returns (int256 w) {
// forgefmt: disable-next-item
unchecked {
if ((w = x) <= -367879441171442322) revert OutOfDomain(); // `x` less than `-1/e`.
(int256 wad, int256 p) = (int256(WAD), x);
uint256 c; // Whether we need to avoid catastrophic cancellation.
uint256 i = 4; // Number of iterations.
if (w <= 0x1ffffffffffff) {
if (-0x4000000000000 <= w) {
i = 1; // Inputs near zero only take one step to converge.
} else if (w <= -0x3ffffffffffffff) {
i = 32; // Inputs near `-1/e` take very long to converge.
}
} else if (uint256(w >> 63) == uint256(0)) {
/// @solidity memory-safe-assembly
assembly {
// Inline log2 for more performance, since the range is small.
let v := shr(49, w)
let l := shl(3, lt(0xff, v))
l := add(or(l, byte(and(0x1f, shr(shr(l, v), 0x8421084210842108cc6318c6db6d54be)),
0x0706060506020504060203020504030106050205030304010505030400000000)), 49)
w := sdiv(shl(l, 7), byte(sub(l, 31), 0x0303030303030303040506080c13))
c := gt(l, 60)
i := add(2, add(gt(l, 53), c))
}
} else {
int256 ll = lnWad(w = lnWad(w));
/// @solidity memory-safe-assembly
assembly {
// `w = ln(x) - ln(ln(x)) + b * ln(ln(x)) / ln(x)`.
w := add(sdiv(mul(ll, 1023715080943847266), w), sub(w, ll))
i := add(3, iszero(shr(68, x)))
c := iszero(shr(143, x))
}
if (c == uint256(0)) {
do { // If `x` is big, use Newton's so that intermediate values won't overflow.
int256 e = expWad(w);
/// @solidity memory-safe-assembly
assembly {
let t := mul(w, div(e, wad))
w := sub(w, sdiv(sub(t, x), div(add(e, t), wad)))
}
if (p <= w) break;
p = w;
} while (--i != uint256(0));
/// @solidity memory-safe-assembly
assembly {
w := sub(w, sgt(w, 2))
}
return w;
}
}
do { // Otherwise, use Halley's for faster convergence.
int256 e = expWad(w);
/// @solidity memory-safe-assembly
assembly {
let t := add(w, wad)
let s := sub(mul(w, e), mul(x, wad))
w := sub(w, sdiv(mul(s, wad), sub(mul(e, t), sdiv(mul(add(t, wad), s), add(t, t)))))
}
if (p <= w) break;
p = w;
} while (--i != c);
/// @solidity memory-safe-assembly
assembly {
w := sub(w, sgt(w, 2))
}
// For certain ranges of `x`, we'll use the quadratic-rate recursive formula of
// R. Iacono and J.P. Boyd for the last iteration, to avoid catastrophic cancellation.
if (c == uint256(0)) return w;
int256 t = w | 1;
/// @solidity memory-safe-assembly
assembly {
x := sdiv(mul(x, wad), t)
}
x = (t * (wad + lnWad(x)));
/// @solidity memory-safe-assembly
assembly {
w := sdiv(x, add(wad, t))
}
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* GENERAL NUMBER UTILITIES */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns `a * b == x * y`, with full precision.
function fullMulEq(uint256 a, uint256 b, uint256 x, uint256 y)
internal
pure
returns (bool result)
{
/// @solidity memory-safe-assembly
assembly {
result := and(eq(mul(a, b), mul(x, y)), eq(mulmod(x, y, not(0)), mulmod(a, b, not(0))))
}
}
/// @dev Calculates `floor(x * y / d)` with full precision.
/// Throws if result overflows a uint256 or when `d` is zero.
/// Credit to Remco Bloemen under MIT license: https://2π.com/21/muldiv
function fullMulDiv(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// 512-bit multiply `[p1 p0] = x * y`.
// Compute the product mod `2**256` and mod `2**256 - 1`
// then use the Chinese Remainder Theorem to reconstruct
// the 512 bit result. The result is stored in two 256
// variables such that `product = p1 * 2**256 + p0`.
// Temporarily use `z` as `p0` to save gas.
z := mul(x, y) // Lower 256 bits of `x * y`.
for {} 1 {} {
// If overflows.
if iszero(mul(or(iszero(x), eq(div(z, x), y)), d)) {
let mm := mulmod(x, y, not(0))
let p1 := sub(mm, add(z, lt(mm, z))) // Upper 256 bits of `x * y`.
/*------------------- 512 by 256 division --------------------*/
// Make division exact by subtracting the remainder from `[p1 p0]`.
let r := mulmod(x, y, d) // Compute remainder using mulmod.
let t := and(d, sub(0, d)) // The least significant bit of `d`. `t >= 1`.
// Make sure `z` is less than `2**256`. Also prevents `d == 0`.
// Placing the check here seems to give more optimal stack operations.
if iszero(gt(d, p1)) {
mstore(0x00, 0xae47f702) // `FullMulDivFailed()`.
revert(0x1c, 0x04)
}
d := div(d, t) // Divide `d` by `t`, which is a power of two.
// Invert `d mod 2**256`
// Now that `d` is an odd number, it has an inverse
// modulo `2**256` such that `d * inv = 1 mod 2**256`.
// Compute the inverse by starting with a seed that is correct
// correct for four bits. That is, `d * inv = 1 mod 2**4`.
let inv := xor(2, mul(3, d))
// Now use Newton-Raphson iteration to improve the precision.
// Thanks to Hensel's lifting lemma, this also works in modular
// arithmetic, doubling the correct bits in each step.
inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**8
inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**16
inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**32
inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**64
inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**128
z :=
mul(
// Divide [p1 p0] by the factors of two.
// Shift in bits from `p1` into `p0`. For this we need
// to flip `t` such that it is `2**256 / t`.
or(mul(sub(p1, gt(r, z)), add(div(sub(0, t), t), 1)), div(sub(z, r), t)),
mul(sub(2, mul(d, inv)), inv) // inverse mod 2**256
)
break
}
z := div(z, d)
break
}
}
}
/// @dev Calculates `floor(x * y / d)` with full precision.
/// Behavior is undefined if `d` is zero or the final result cannot fit in 256 bits.
/// Performs the full 512 bit calculation regardless.
function fullMulDivUnchecked(uint256 x, uint256 y, uint256 d)
internal
pure
returns (uint256 z)
{
/// @solidity memory-safe-assembly
assembly {
z := mul(x, y)
let mm := mulmod(x, y, not(0))
let p1 := sub(mm, add(z, lt(mm, z)))
let t := and(d, sub(0, d))
let r := mulmod(x, y, d)
d := div(d, t)
let inv := xor(2, mul(3, d))
inv := mul(inv, sub(2, mul(d, inv)))
inv := mul(inv, sub(2, mul(d, inv)))
inv := mul(inv, sub(2, mul(d, inv)))
inv := mul(inv, sub(2, mul(d, inv)))
inv := mul(inv, sub(2, mul(d, inv)))
z :=
mul(
or(mul(sub(p1, gt(r, z)), add(div(sub(0, t), t), 1)), div(sub(z, r), t)),
mul(sub(2, mul(d, inv)), inv)
)
}
}
/// @dev Calculates `floor(x * y / d)` with full precision, rounded up.
/// Throws if result overflows a uint256 or when `d` is zero.
/// Credit to Uniswap-v3-core under MIT license:
/// https://github.com/Uniswap/v3-core/blob/main/contracts/libraries/FullMath.sol
function fullMulDivUp(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) {
z = fullMulDiv(x, y, d);
/// @solidity memory-safe-assembly
assembly {
if mulmod(x, y, d) {
z := add(z, 1)
if iszero(z) {
mstore(0x00, 0xae47f702) // `FullMulDivFailed()`.
revert(0x1c, 0x04)
}
}
}
}
/// @dev Calculates `floor(x * y / 2 ** n)` with full precision.
/// Throws if result overflows a uint256.
/// Credit to Philogy under MIT license:
/// https://github.com/SorellaLabs/angstrom/blob/main/contracts/src/libraries/X128MathLib.sol
function fullMulDivN(uint256 x, uint256 y, uint8 n) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// Temporarily use `z` as `p0` to save gas.
z := mul(x, y) // Lower 256 bits of `x * y`. We'll call this `z`.
for {} 1 {} {
if iszero(or(iszero(x), eq(div(z, x), y))) {
let k := and(n, 0xff) // `n`, cleaned.
let mm := mulmod(x, y, not(0))
let p1 := sub(mm, add(z, lt(mm, z))) // Upper 256 bits of `x * y`.
// | p1 | z |
// Before: | p1_0 ¦ p1_1 | z_0 ¦ z_1 |
// Final: | 0 ¦ p1_0 | p1_1 ¦ z_0 |
// Check that final `z` doesn't overflow by checking that p1_0 = 0.
if iszero(shr(k, p1)) {
z := add(shl(sub(256, k), p1), shr(k, z))
break
}
mstore(0x00, 0xae47f702) // `FullMulDivFailed()`.
revert(0x1c, 0x04)
}
z := shr(and(n, 0xff), z)
break
}
}
}
/// @dev Returns `floor(x * y / d)`.
/// Reverts if `x * y` overflows, or `d` is zero.
function mulDiv(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := mul(x, y)
// Equivalent to `require(d != 0 && (y == 0 || x <= type(uint256).max / y))`.
if iszero(mul(or(iszero(x), eq(div(z, x), y)), d)) {
mstore(0x00, 0xad251c27) // `MulDivFailed()`.
revert(0x1c, 0x04)
}
z := div(z, d)
}
}
/// @dev Returns `ceil(x * y / d)`.
/// Reverts if `x * y` overflows, or `d` is zero.
function mulDivUp(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := mul(x, y)
// Equivalent to `require(d != 0 && (y == 0 || x <= type(uint256).max / y))`.
if iszero(mul(or(iszero(x), eq(div(z, x), y)), d)) {
mstore(0x00, 0xad251c27) // `MulDivFailed()`.
revert(0x1c, 0x04)
}
z := add(iszero(iszero(mod(z, d))), div(z, d))
}
}
/// @dev Returns `x`, the modular multiplicative inverse of `a`, such that `(a * x) % n == 1`.
function invMod(uint256 a, uint256 n) internal pure returns (uint256 x) {
/// @solidity memory-safe-assembly
assembly {
let g := n
let r := mod(a, n)
for { let y := 1 } 1 {} {
let q := div(g, r)
let t := g
g := r
r := sub(t, mul(r, q))
let u := x
x := y
y := sub(u, mul(y, q))
if iszero(r) { break }
}
x := mul(eq(g, 1), add(x, mul(slt(x, 0), n)))
}
}
/// @dev Returns `ceil(x / d)`.
/// Reverts if `d` is zero.
function divUp(uint256 x, uint256 d) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
if iszero(d) {
mstore(0x00, 0x65244e4e) // `DivFailed()`.
revert(0x1c, 0x04)
}
z := add(iszero(iszero(mod(x, d))), div(x, d))
}
}
/// @dev Returns `max(0, x - y)`. Alias for `saturatingSub`.
function zeroFloorSub(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := mul(gt(x, y), sub(x, y))
}
}
/// @dev Returns `max(0, x - y)`.
function saturatingSub(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := mul(gt(x, y), sub(x, y))
}
}
/// @dev Returns `min(2 ** 256 - 1, x + y)`.
function saturatingAdd(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := or(sub(0, lt(add(x, y), x)), add(x, y))
}
}
/// @dev Returns `min(2 ** 256 - 1, x * y)`.
function saturatingMul(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := or(sub(or(iszero(x), eq(div(mul(x, y), x), y)), 1), mul(x, y))
}
}
/// @dev Returns `condition ? x : y`, without branching.
function ternary(bool condition, uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := xor(x, mul(xor(x, y), iszero(condition)))
}
}
/// @dev Returns `condition ? x : y`, without branching.
function ternary(bool condition, bytes32 x, bytes32 y) internal pure returns (bytes32 z) {
/// @solidity memory-safe-assembly
assembly {
z := xor(x, mul(xor(x, y), iszero(condition)))
}
}
/// @dev Returns `condition ? x : y`, without branching.
function ternary(bool condition, address x, address y) internal pure returns (address z) {
/// @solidity memory-safe-assembly
assembly {
z := xor(x, mul(xor(x, y), iszero(condition)))
}
}
/// @dev Returns `x != 0 ? x : y`, without branching.
function coalesce(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := or(x, mul(y, iszero(x)))
}
}
/// @dev Returns `x != bytes32(0) ? x : y`, without branching.
function coalesce(bytes32 x, bytes32 y) internal pure returns (bytes32 z) {
/// @solidity memory-safe-assembly
assembly {
z := or(x, mul(y, iszero(x)))
}
}
/// @dev Returns `x != address(0) ? x : y`, without branching.
function coalesce(address x, address y) internal pure returns (address z) {
/// @solidity memory-safe-assembly
assembly {
z := or(x, mul(y, iszero(shl(96, x))))
}
}
/// @dev Exponentiate `x` to `y` by squaring, denominated in base `b`.
/// Reverts if the computation overflows.
function rpow(uint256 x, uint256 y, uint256 b) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := mul(b, iszero(y)) // `0 ** 0 = 1`. Otherwise, `0 ** n = 0`.
if x {
z := xor(b, mul(xor(b, x), and(y, 1))) // `z = isEven(y) ? scale : x`
let half := shr(1, b) // Divide `b` by 2.
// Divide `y` by 2 every iteration.
for { y := shr(1, y) } y { y := shr(1, y) } {
let xx := mul(x, x) // Store x squared.
let xxRound := add(xx, half) // Round to the nearest number.
// Revert if `xx + half` overflowed, or if `x ** 2` overflows.
if or(lt(xxRound, xx), shr(128, x)) {
mstore(0x00, 0x49f7642b) // `RPowOverflow()`.
revert(0x1c, 0x04)
}
x := div(xxRound, b) // Set `x` to scaled `xxRound`.
// If `y` is odd:
if and(y, 1) {
let zx := mul(z, x) // Compute `z * x`.
let zxRound := add(zx, half) // Round to the nearest number.
// If `z * x` overflowed or `zx + half` overflowed:
if or(xor(div(zx, x), z), lt(zxRound, zx)) {
// Revert if `x` is non-zero.
if x {
mstore(0x00, 0x49f7642b) // `RPowOverflow()`.
revert(0x1c, 0x04)
}
}
z := div(zxRound, b) // Return properly scaled `zxRound`.
}
}
}
}
}
/// @dev Returns the square root of `x`, rounded down.
function sqrt(uint256 x) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// `floor(sqrt(2**15)) = 181`. `sqrt(2**15) - 181 = 2.84`.
z := 181 // The "correct" value is 1, but this saves a multiplication later.
// This segment is to get a reasonable initial estimate for the Babylonian method. With a bad
// start, the correct # of bits increases ~linearly each iteration instead of ~quadratically.
// Let `y = x / 2**r`. We check `y >= 2**(k + 8)`
// but shift right by `k` bits to ensure that if `x >= 256`, then `y >= 256`.
let r := shl(7, lt(0xffffffffffffffffffffffffffffffffff, x))
r := or(r, shl(6, lt(0xffffffffffffffffff, shr(r, x))))
r := or(r, shl(5, lt(0xffffffffff, shr(r, x))))
r := or(r, shl(4, lt(0xffffff, shr(r, x))))
z := shl(shr(1, r), z)
// Goal was to get `z*z*y` within a small factor of `x`. More iterations could
// get y in a tighter range. Currently, we will have y in `[256, 256*(2**16))`.
// We ensured `y >= 256` so that the relative difference between `y` and `y+1` is small.
// That's not possible if `x < 256` but we can just verify those cases exhaustively.
// Now, `z*z*y <= x < z*z*(y+1)`, and `y <= 2**(16+8)`, and either `y >= 256`, or `x < 256`.
// Correctness can be checked exhaustively for `x < 256`, so we assume `y >= 256`.
// Then `z*sqrt(y)` is within `sqrt(257)/sqrt(256)` of `sqrt(x)`, or about 20bps.
// For `s` in the range `[1/256, 256]`, the estimate `f(s) = (181/1024) * (s+1)`
// is in the range `(1/2.84 * sqrt(s), 2.84 * sqrt(s))`,
// with largest error when `s = 1` and when `s = 256` or `1/256`.
// Since `y` is in `[256, 256*(2**16))`, let `a = y/65536`, so that `a` is in `[1/256, 256)`.
// Then we can estimate `sqrt(y)` using
// `sqrt(65536) * 181/1024 * (a + 1) = 181/4 * (y + 65536)/65536 = 181 * (y + 65536)/2**18`.
// There is no overflow risk here since `y < 2**136` after the first branch above.
z := shr(18, mul(z, add(shr(r, x), 65536))) // A `mul()` is saved from starting `z` at 181.
// Given the worst case multiplicative error of 2.84 above, 7 iterations should be enough.
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
// If `x+1` is a perfect square, the Babylonian method cycles between
// `floor(sqrt(x))` and `ceil(sqrt(x))`. This statement ensures we return floor.
// See: https://en.wikipedia.org/wiki/Integer_square_root#Using_only_integer_division
z := sub(z, lt(div(x, z), z))
}
}
/// @dev Returns the cube root of `x`, rounded down.
/// Credit to bout3fiddy and pcaversaccio under AGPLv3 license:
/// https://github.com/pcaversaccio/snekmate/blob/main/src/snekmate/utils/math.vy
/// Formally verified by xuwinnie:
/// https://github.com/vectorized/solady/blob/main/audits/xuwinnie-solady-cbrt-proof.pdf
function cbrt(uint256 x) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
let r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x))
r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
r := or(r, shl(4, lt(0xffff, shr(r, x))))
r := or(r, shl(3, lt(0xff, shr(r, x))))
// Makeshift lookup table to nudge the approximate log2 result.
z := div(shl(div(r, 3), shl(lt(0xf, shr(r, x)), 0xf)), xor(7, mod(r, 3)))
// Newton-Raphson's.
z := div(add(add(div(x, mul(z, z)), z), z), 3)
z := div(add(add(div(x, mul(z, z)), z), z), 3)
z := div(add(add(div(x, mul(z, z)), z), z), 3)
z := div(add(add(div(x, mul(z, z)), z), z), 3)
z := div(add(add(div(x, mul(z, z)), z), z), 3)
z := div(add(add(div(x, mul(z, z)), z), z), 3)
z := div(add(add(div(x, mul(z, z)), z), z), 3)
// Round down.
z := sub(z, lt(div(x, mul(z, z)), z))
}
}
/// @dev Returns the square root of `x`, denominated in `WAD`, rounded down.
function sqrtWad(uint256 x) internal pure returns (uint256 z) {
unchecked {
if (x <= type(uint256).max / 10 ** 18) return sqrt(x * 10 ** 18);
z = (1 + sqrt(x)) * 10 ** 9;
z = (fullMulDivUnchecked(x, 10 ** 18, z) + z) >> 1;
}
/// @solidity memory-safe-assembly
assembly {
z := sub(z, gt(999999999999999999, sub(mulmod(z, z, x), 1))) // Round down.
}
}
/// @dev Returns the cube root of `x`, denominated in `WAD`, rounded down.
/// Formally verified by xuwinnie:
/// https://github.com/vectorized/solady/blob/main/audits/xuwinnie-solady-cbrt-proof.pdf
function cbrtWad(uint256 x) internal pure returns (uint256 z) {
unchecked {
if (x <= type(uint256).max / 10 ** 36) return cbrt(x * 10 ** 36);
z = (1 + cbrt(x)) * 10 ** 12;
z = (fullMulDivUnchecked(x, 10 ** 36, z * z) + z + z) / 3;
}
/// @solidity memory-safe-assembly
assembly {
let p := x
for {} 1 {} {
if iszero(shr(229, p)) {
if iszero(shr(199, p)) {
p := mul(p, 100000000000000000) // 10 ** 17.
break
}
p := mul(p, 100000000) // 10 ** 8.
break
}
if iszero(shr(249, p)) { p := mul(p, 100) }
break
}
let t := mulmod(mul(z, z), z, p)
z := sub(z, gt(lt(t, shr(1, p)), iszero(t))) // Round down.
}
}
/// @dev Returns the factorial of `x`.
function factorial(uint256 x) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := 1
if iszero(lt(x, 58)) {
mstore(0x00, 0xaba0f2a2) // `FactorialOverflow()`.
revert(0x1c, 0x04)
}
for {} x { x := sub(x, 1) } { z := mul(z, x) }
}
}
/// @dev Returns the log2 of `x`.
/// Equivalent to computing the index of the most significant bit (MSB) of `x`.
/// Returns 0 if `x` is zero.
function log2(uint256 x) internal pure returns (uint256 r) {
/// @solidity memory-safe-assembly
assembly {
r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x))
r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
r := or(r, shl(4, lt(0xffff, shr(r, x))))
r := or(r, shl(3, lt(0xff, shr(r, x))))
// forgefmt: disable-next-item
r := or(r, byte(and(0x1f, shr(shr(r, x), 0x8421084210842108cc6318c6db6d54be)),
0x0706060506020504060203020504030106050205030304010505030400000000))
}
}
/// @dev Returns the log2 of `x`, rounded up.
/// Returns 0 if `x` is zero.
function log2Up(uint256 x) internal pure returns (uint256 r) {
r = log2(x);
/// @solidity memory-safe-assembly
assembly {
r := add(r, lt(shl(r, 1), x))
}
}
/// @dev Returns the log10 of `x`.
/// Returns 0 if `x` is zero.
function log10(uint256 x) internal pure returns (uint256 r) {
/// @solidity memory-safe-assembly
assembly {
if iszero(lt(x, 100000000000000000000000000000000000000)) {
x := div(x, 100000000000000000000000000000000000000)
r := 38
}
if iszero(lt(x, 100000000000000000000)) {
x := div(x, 100000000000000000000)
r := add(r, 20)
}
if iszero(lt(x, 10000000000)) {
x := div(x, 10000000000)
r := add(r, 10)
}
if iszero(lt(x, 100000)) {
x := div(x, 100000)
r := add(r, 5)
}
r := add(r, add(gt(x, 9), add(gt(x, 99), add(gt(x, 999), gt(x, 9999)))))
}
}
/// @dev Returns the log10 of `x`, rounded up.
/// Returns 0 if `x` is zero.
function log10Up(uint256 x) internal pure returns (uint256 r) {
r = log10(x);
/// @solidity memory-safe-assembly
assembly {
r := add(r, lt(exp(10, r), x))
}
}
/// @dev Returns the log256 of `x`.
/// Returns 0 if `x` is zero.
function log256(uint256 x) internal pure returns (uint256 r) {
/// @solidity memory-safe-assembly
assembly {
r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x))
r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
r := or(r, shl(4, lt(0xffff, shr(r, x))))
r := or(shr(3, r), lt(0xff, shr(r, x)))
}
}
/// @dev Returns the log256 of `x`, rounded up.
/// Returns 0 if `x` is zero.
function log256Up(uint256 x) internal pure returns (uint256 r) {
r = log256(x);
/// @solidity memory-safe-assembly
assembly {
r := add(r, lt(shl(shl(3, r), 1), x))
}
}
/// @dev Returns the scientific notation format `mantissa * 10 ** exponent` of `x`.
/// Useful for compressing prices (e.g. using 25 bit mantissa and 7 bit exponent).
function sci(uint256 x) internal pure returns (uint256 mantissa, uint256 exponent) {
/// @solidity memory-safe-assembly
assembly {
mantissa := x
if mantissa {
if iszero(mod(mantissa, 1000000000000000000000000000000000)) {
mantissa := div(mantissa, 1000000000000000000000000000000000)
exponent := 33
}
if iszero(mod(mantissa, 10000000000000000000)) {
mantissa := div(mantissa, 10000000000000000000)
exponent := add(exponent, 19)
}
if iszero(mod(mantissa, 1000000000000)) {
mantissa := div(mantissa, 1000000000000)
exponent := add(exponent, 12)
}
if iszero(mod(mantissa, 1000000)) {
mantissa := div(mantissa, 1000000)
exponent := add(exponent, 6)
}
if iszero(mod(mantissa, 10000)) {
mantissa := div(mantissa, 10000)
exponent := add(exponent, 4)
}
if iszero(mod(mantissa, 100)) {
mantissa := div(mantissa, 100)
exponent := add(exponent, 2)
}
if iszero(mod(mantissa, 10)) {
mantissa := div(mantissa, 10)
exponent := add(exponent, 1)
}
}
}
}
/// @dev Convenience function for packing `x` into a smaller number using `sci`.
/// The `mantissa` will be in bits [7..255] (the upper 249 bits).
/// The `exponent` will be in bits [0..6] (the lower 7 bits).
/// Use `SafeCastLib` to safely ensure that the `packed` number is small
/// enough to fit in the desired unsigned integer type:
/// ```
/// uint32 packed = SafeCastLib.toUint32(FixedPointMathLib.packSci(777 ether));
/// ```
function packSci(uint256 x) internal pure returns (uint256 packed) {
(x, packed) = sci(x); // Reuse for `mantissa` and `exponent`.
/// @solidity memory-safe-assembly
assembly {
if shr(249, x) {
mstore(0x00, 0xce30380c) // `MantissaOverflow()`.
revert(0x1c, 0x04)
}
packed := or(shl(7, x), packed)
}
}
/// @dev Convenience function for unpacking a packed number from `packSci`.
function unpackSci(uint256 packed) internal pure returns (uint256 unpacked) {
unchecked {
unpacked = (packed >> 7) * 10 ** (packed & 0x7f);
}
}
/// @dev Returns the average of `x` and `y`. Rounds towards zero.
function avg(uint256 x, uint256 y) internal pure returns (uint256 z) {
unchecked {
z = (x & y) + ((x ^ y) >> 1);
}
}
/// @dev Returns the average of `x` and `y`. Rounds towards negative infinity.
function avg(int256 x, int256 y) internal pure returns (int256 z) {
unchecked {
z = (x >> 1) + (y >> 1) + (x & y & 1);
}
}
/// @dev Returns the absolute value of `x`.
function abs(int256 x) internal pure returns (uint256 z) {
unchecked {
z = (uint256(x) + uint256(x >> 255)) ^ uint256(x >> 255);
}
}
/// @dev Returns the absolute distance between `x` and `y`.
function dist(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := add(xor(sub(0, gt(x, y)), sub(y, x)), gt(x, y))
}
}
/// @dev Returns the absolute distance between `x` and `y`.
function dist(int256 x, int256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := add(xor(sub(0, sgt(x, y)), sub(y, x)), sgt(x, y))
}
}
/// @dev Returns the minimum of `x` and `y`.
function min(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := xor(x, mul(xor(x, y), lt(y, x)))
}
}
/// @dev Returns the minimum of `x` and `y`.
function min(int256 x, int256 y) internal pure returns (int256 z) {
/// @solidity memory-safe-assembly
assembly {
z := xor(x, mul(xor(x, y), slt(y, x)))
}
}
/// @dev Returns the maximum of `x` and `y`.
function max(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := xor(x, mul(xor(x, y), gt(y, x)))
}
}
/// @dev Returns the maximum of `x` and `y`.
function max(int256 x, int256 y) internal pure returns (int256 z) {
/// @solidity memory-safe-assembly
assembly {
z := xor(x, mul(xor(x, y), sgt(y, x)))
}
}
/// @dev Returns `x`, bounded to `minValue` and `maxValue`.
function clamp(uint256 x, uint256 minValue, uint256 maxValue)
internal
pure
returns (uint256 z)
{
/// @solidity memory-safe-assembly
assembly {
z := xor(x, mul(xor(x, minValue), gt(minValue, x)))
z := xor(z, mul(xor(z, maxValue), lt(maxValue, z)))
}
}
/// @dev Returns `x`, bounded to `minValue` and `maxValue`.
function clamp(int256 x, int256 minValue, int256 maxValue) internal pure returns (int256 z) {
/// @solidity memory-safe-assembly
assembly {
z := xor(x, mul(xor(x, minValue), sgt(minValue, x)))
z := xor(z, mul(xor(z, maxValue), slt(maxValue, z)))
}
}
/// @dev Returns greatest common divisor of `x` and `y`.
function gcd(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
for { z := x } y {} {
let t := y
y := mod(z, y)
z := t
}
}
}
/// @dev Returns `a + (b - a) * (t - begin) / (end - begin)`,
/// with `t` clamped between `begin` and `end` (inclusive).
/// Agnostic to the order of (`a`, `b`) and (`end`, `begin`).
/// If `begins == end`, returns `t <= begin ? a : b`.
function lerp(uint256 a, uint256 b, uint256 t, uint256 begin, uint256 end)
internal
pure
returns (uint256)
{
if (begin > end) (t, begin, end) = (~t, ~begin, ~end);
if (t <= begin) return a;
if (t >= end) return b;
unchecked {
if (b >= a) return a + fullMulDiv(b - a, t - begin, end - begin);
return a - fullMulDiv(a - b, t - begin, end - begin);
}
}
/// @dev Returns `a + (b - a) * (t - begin) / (end - begin)`.
/// with `t` clamped between `begin` and `end` (inclusive).
/// Agnostic to the order of (`a`, `b`) and (`end`, `begin`).
/// If `begins == end`, returns `t <= begin ? a : b`.
function lerp(int256 a, int256 b, int256 t, int256 begin, int256 end)
internal
pure
returns (int256)
{
if (begin > end) (t, begin, end) = (~t, ~begin, ~end);
if (t <= begin) return a;
if (t >= end) return b;
// forgefmt: disable-next-item
unchecked {
if (b >= a) return int256(uint256(a) + fullMulDiv(uint256(b - a),
uint256(t - begin), uint256(end - begin)));
return int256(uint256(a) - fullMulDiv(uint256(a - b),
uint256(t - begin), uint256(end - begin)));
}
}
/// @dev Returns if `x` is an even number. Some people may need this.
function isEven(uint256 x) internal pure returns (bool) {
return x & uint256(1) == uint256(0);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* RAW NUMBER OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns `x + y`, without checking for overflow.
function rawAdd(uint256 x, uint256 y) internal pure returns (uint256 z) {
unchecked {
z = x + y;
}
}
/// @dev Returns `x + y`, without checking for overflow.
function rawAdd(int256 x, int256 y) internal pure returns (int256 z) {
unchecked {
z = x + y;
}
}
/// @dev Returns `x - y`, without checking for underflow.
function rawSub(uint256 x, uint256 y) internal pure returns (uint256 z) {
unchecked {
z = x - y;
}
}
/// @dev Returns `x - y`, without checking for underflow.
function rawSub(int256 x, int256 y) internal pure returns (int256 z) {
unchecked {
z = x - y;
}
}
/// @dev Returns `x * y`, without checking for overflow.
function rawMul(uint256 x, uint256 y) internal pure returns (uint256 z) {
unchecked {
z = x * y;
}
}
/// @dev Returns `x * y`, without checking for overflow.
function rawMul(int256 x, int256 y) internal pure returns (int256 z) {
unchecked {
z = x * y;
}
}
/// @dev Returns `x / y`, returning 0 if `y` is zero.
function rawDiv(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := div(x, y)
}
}
/// @dev Returns `x / y`, returning 0 if `y` is zero.
function rawSDiv(int256 x, int256 y) internal pure returns (int256 z) {
/// @solidity memory-safe-assembly
assembly {
z := sdiv(x, y)
}
}
/// @dev Returns `x % y`, returning 0 if `y` is zero.
function rawMod(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := mod(x, y)
}
}
/// @dev Returns `x % y`, returning 0 if `y` is zero.
function rawSMod(int256 x, int256 y) internal pure returns (int256 z) {
/// @solidity memory-safe-assembly
assembly {
z := smod(x, y)
}
}
/// @dev Returns `(x + y) % d`, return 0 if `d` if zero.
function rawAddMod(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := addmod(x, y, d)
}
}
/// @dev Returns `(x * y) % d`, return 0 if `d` if zero.
function rawMulMod(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := mulmod(x, y, d)
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Safe integer casting library that reverts on overflow.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SafeCastLib.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/math/SafeCast.sol)
/// @dev Optimized for runtime gas for very high number of optimizer runs (i.e. >= 1000000).
library SafeCastLib {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Unable to cast to the target type due to overflow.
error Overflow();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* UNSIGNED INTEGER SAFE CASTING OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Casts `x` to a uint8. Reverts on overflow.
function toUint8(uint256 x) internal pure returns (uint8) {
if (x >= 1 << 8) _revertOverflow();
return uint8(x);
}
/// @dev Casts `x` to a uint16. Reverts on overflow.
function toUint16(uint256 x) internal pure returns (uint16) {
if (x >= 1 << 16) _revertOverflow();
return uint16(x);
}
/// @dev Casts `x` to a uint24. Reverts on overflow.
function toUint24(uint256 x) internal pure returns (uint24) {
if (x >= 1 << 24) _revertOverflow();
return uint24(x);
}
/// @dev Casts `x` to a uint32. Reverts on overflow.
function toUint32(uint256 x) internal pure returns (uint32) {
if (x >= 1 << 32) _revertOverflow();
return uint32(x);
}
/// @dev Casts `x` to a uint40. Reverts on overflow.
function toUint40(uint256 x) internal pure returns (uint40) {
if (x >= 1 << 40) _revertOverflow();
return uint40(x);
}
/// @dev Casts `x` to a uint48. Reverts on overflow.
function toUint48(uint256 x) internal pure returns (uint48) {
if (x >= 1 << 48) _revertOverflow();
return uint48(x);
}
/// @dev Casts `x` to a uint56. Reverts on overflow.
function toUint56(uint256 x) internal pure returns (uint56) {
if (x >= 1 << 56) _revertOverflow();
return uint56(x);
}
/// @dev Casts `x` to a uint64. Reverts on overflow.
function toUint64(uint256 x) internal pure returns (uint64) {
if (x >= 1 << 64) _revertOverflow();
return uint64(x);
}
/// @dev Casts `x` to a uint72. Reverts on overflow.
function toUint72(uint256 x) internal pure returns (uint72) {
if (x >= 1 << 72) _revertOverflow();
return uint72(x);
}
/// @dev Casts `x` to a uint80. Reverts on overflow.
function toUint80(uint256 x) internal pure returns (uint80) {
if (x >= 1 << 80) _revertOverflow();
return uint80(x);
}
/// @dev Casts `x` to a uint88. Reverts on overflow.
function toUint88(uint256 x) internal pure returns (uint88) {
if (x >= 1 << 88) _revertOverflow();
return uint88(x);
}
/// @dev Casts `x` to a uint96. Reverts on overflow.
function toUint96(uint256 x) internal pure returns (uint96) {
if (x >= 1 << 96) _revertOverflow();
return uint96(x);
}
/// @dev Casts `x` to a uint104. Reverts on overflow.
function toUint104(uint256 x) internal pure returns (uint104) {
if (x >= 1 << 104) _revertOverflow();
return uint104(x);
}
/// @dev Casts `x` to a uint112. Reverts on overflow.
function toUint112(uint256 x) internal pure returns (uint112) {
if (x >= 1 << 112) _revertOverflow();
return uint112(x);
}
/// @dev Casts `x` to a uint120. Reverts on overflow.
function toUint120(uint256 x) internal pure returns (uint120) {
if (x >= 1 << 120) _revertOverflow();
return uint120(x);
}
/// @dev Casts `x` to a uint128. Reverts on overflow.
function toUint128(uint256 x) internal pure returns (uint128) {
if (x >= 1 << 128) _revertOverflow();
return uint128(x);
}
/// @dev Casts `x` to a uint136. Reverts on overflow.
function toUint136(uint256 x) internal pure returns (uint136) {
if (x >= 1 << 136) _revertOverflow();
return uint136(x);
}
/// @dev Casts `x` to a uint144. Reverts on overflow.
function toUint144(uint256 x) internal pure returns (uint144) {
if (x >= 1 << 144) _revertOverflow();
return uint144(x);
}
/// @dev Casts `x` to a uint152. Reverts on overflow.
function toUint152(uint256 x) internal pure returns (uint152) {
if (x >= 1 << 152) _revertOverflow();
return uint152(x);
}
/// @dev Casts `x` to a uint160. Reverts on overflow.
function toUint160(uint256 x) internal pure returns (uint160) {
if (x >= 1 << 160) _revertOverflow();
return uint160(x);
}
/// @dev Casts `x` to a uint168. Reverts on overflow.
function toUint168(uint256 x) internal pure returns (uint168) {
if (x >= 1 << 168) _revertOverflow();
return uint168(x);
}
/// @dev Casts `x` to a uint176. Reverts on overflow.
function toUint176(uint256 x) internal pure returns (uint176) {
if (x >= 1 << 176) _revertOverflow();
return uint176(x);
}
/// @dev Casts `x` to a uint184. Reverts on overflow.
function toUint184(uint256 x) internal pure returns (uint184) {
if (x >= 1 << 184) _revertOverflow();
return uint184(x);
}
/// @dev Casts `x` to a uint192. Reverts on overflow.
function toUint192(uint256 x) internal pure returns (uint192) {
if (x >= 1 << 192) _revertOverflow();
return uint192(x);
}
/// @dev Casts `x` to a uint200. Reverts on overflow.
function toUint200(uint256 x) internal pure returns (uint200) {
if (x >= 1 << 200) _revertOverflow();
return uint200(x);
}
/// @dev Casts `x` to a uint208. Reverts on overflow.
function toUint208(uint256 x) internal pure returns (uint208) {
if (x >= 1 << 208) _revertOverflow();
return uint208(x);
}
/// @dev Casts `x` to a uint216. Reverts on overflow.
function toUint216(uint256 x) internal pure returns (uint216) {
if (x >= 1 << 216) _revertOverflow();
return uint216(x);
}
/// @dev Casts `x` to a uint224. Reverts on overflow.
function toUint224(uint256 x) internal pure returns (uint224) {
if (x >= 1 << 224) _revertOverflow();
return uint224(x);
}
/// @dev Casts `x` to a uint232. Reverts on overflow.
function toUint232(uint256 x) internal pure returns (uint232) {
if (x >= 1 << 232) _revertOverflow();
return uint232(x);
}
/// @dev Casts `x` to a uint240. Reverts on overflow.
function toUint240(uint256 x) internal pure returns (uint240) {
if (x >= 1 << 240) _revertOverflow();
return uint240(x);
}
/// @dev Casts `x` to a uint248. Reverts on overflow.
function toUint248(uint256 x) internal pure returns (uint248) {
if (x >= 1 << 248) _revertOverflow();
return uint248(x);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* SIGNED INTEGER SAFE CASTING OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Casts `x` to a int8. Reverts on overflow.
function toInt8(int256 x) internal pure returns (int8) {
unchecked {
if (((1 << 7) + uint256(x)) >> 8 == uint256(0)) return int8(x);
_revertOverflow();
}
}
/// @dev Casts `x` to a int16. Reverts on overflow.
function toInt16(int256 x) internal pure returns (int16) {
unchecked {
if (((1 << 15) + uint256(x)) >> 16 == uint256(0)) return int16(x);
_revertOverflow();
}
}
/// @dev Casts `x` to a int24. Reverts on overflow.
function toInt24(int256 x) internal pure returns (int24) {
unchecked {
if (((1 << 23) + uint256(x)) >> 24 == uint256(0)) return int24(x);
_revertOverflow();
}
}
/// @dev Casts `x` to a int32. Reverts on overflow.
function toInt32(int256 x) internal pure returns (int32) {
unchecked {
if (((1 << 31) + uint256(x)) >> 32 == uint256(0)) return int32(x);
_revertOverflow();
}
}
/// @dev Casts `x` to a int40. Reverts on overflow.
function toInt40(int256 x) internal pure returns (int40) {
unchecked {
if (((1 << 39) + uint256(x)) >> 40 == uint256(0)) return int40(x);
_revertOverflow();
}
}
/// @dev Casts `x` to a int48. Reverts on overflow.
function toInt48(int256 x) internal pure returns (int48) {
unchecked {
if (((1 << 47) + uint256(x)) >> 48 == uint256(0)) return int48(x);
_revertOverflow();
}
}
/// @dev Casts `x` to a int56. Reverts on overflow.
function toInt56(int256 x) internal pure returns (int56) {
unchecked {
if (((1 << 55) + uint256(x)) >> 56 == uint256(0)) return int56(x);
_revertOverflow();
}
}
/// @dev Casts `x` to a int64. Reverts on overflow.
function toInt64(int256 x) internal pure returns (int64) {
unchecked {
if (((1 << 63) + uint256(x)) >> 64 == uint256(0)) return int64(x);
_revertOverflow();
}
}
/// @dev Casts `x` to a int72. Reverts on overflow.
function toInt72(int256 x) internal pure returns (int72) {
unchecked {
if (((1 << 71) + uint256(x)) >> 72 == uint256(0)) return int72(x);
_revertOverflow();
}
}
/// @dev Casts `x` to a int80. Reverts on overflow.
function toInt80(int256 x) internal pure returns (int80) {
unchecked {
if (((1 << 79) + uint256(x)) >> 80 == uint256(0)) return int80(x);
_revertOverflow();
}
}
/// @dev Casts `x` to a int88. Reverts on overflow.
function toInt88(int256 x) internal pure returns (int88) {
unchecked {
if (((1 << 87) + uint256(x)) >> 88 == uint256(0)) return int88(x);
_revertOverflow();
}
}
/// @dev Casts `x` to a int96. Reverts on overflow.
function toInt96(int256 x) internal pure returns (int96) {
unchecked {
if (((1 << 95) + uint256(x)) >> 96 == uint256(0)) return int96(x);
_revertOverflow();
}
}
/// @dev Casts `x` to a int104. Reverts on overflow.
function toInt104(int256 x) internal pure returns (int104) {
unchecked {
if (((1 << 103) + uint256(x)) >> 104 == uint256(0)) return int104(x);
_revertOverflow();
}
}
/// @dev Casts `x` to a int112. Reverts on overflow.
function toInt112(int256 x) internal pure returns (int112) {
unchecked {
if (((1 << 111) + uint256(x)) >> 112 == uint256(0)) return int112(x);
_revertOverflow();
}
}
/// @dev Casts `x` to a int120. Reverts on overflow.
function toInt120(int256 x) internal pure returns (int120) {
unchecked {
if (((1 << 119) + uint256(x)) >> 120 == uint256(0)) return int120(x);
_revertOverflow();
}
}
/// @dev Casts `x` to a int128. Reverts on overflow.
function toInt128(int256 x) internal pure returns (int128) {
unchecked {
if (((1 << 127) + uint256(x)) >> 128 == uint256(0)) return int128(x);
_revertOverflow();
}
}
/// @dev Casts `x` to a int136. Reverts on overflow.
function toInt136(int256 x) internal pure returns (int136) {
unchecked {
if (((1 << 135) + uint256(x)) >> 136 == uint256(0)) return int136(x);
_revertOverflow();
}
}
/// @dev Casts `x` to a int144. Reverts on overflow.
function toInt144(int256 x) internal pure returns (int144) {
unchecked {
if (((1 << 143) + uint256(x)) >> 144 == uint256(0)) return int144(x);
_revertOverflow();
}
}
/// @dev Casts `x` to a int152. Reverts on overflow.
function toInt152(int256 x) internal pure returns (int152) {
unchecked {
if (((1 << 151) + uint256(x)) >> 152 == uint256(0)) return int152(x);
_revertOverflow();
}
}
/// @dev Casts `x` to a int160. Reverts on overflow.
function toInt160(int256 x) internal pure returns (int160) {
unchecked {
if (((1 << 159) + uint256(x)) >> 160 == uint256(0)) return int160(x);
_revertOverflow();
}
}
/// @dev Casts `x` to a int168. Reverts on overflow.
function toInt168(int256 x) internal pure returns (int168) {
unchecked {
if (((1 << 167) + uint256(x)) >> 168 == uint256(0)) return int168(x);
_revertOverflow();
}
}
/// @dev Casts `x` to a int176. Reverts on overflow.
function toInt176(int256 x) internal pure returns (int176) {
unchecked {
if (((1 << 175) + uint256(x)) >> 176 == uint256(0)) return int176(x);
_revertOverflow();
}
}
/// @dev Casts `x` to a int184. Reverts on overflow.
function toInt184(int256 x) internal pure returns (int184) {
unchecked {
if (((1 << 183) + uint256(x)) >> 184 == uint256(0)) return int184(x);
_revertOverflow();
}
}
/// @dev Casts `x` to a int192. Reverts on overflow.
function toInt192(int256 x) internal pure returns (int192) {
unchecked {
if (((1 << 191) + uint256(x)) >> 192 == uint256(0)) return int192(x);
_revertOverflow();
}
}
/// @dev Casts `x` to a int200. Reverts on overflow.
function toInt200(int256 x) internal pure returns (int200) {
unchecked {
if (((1 << 199) + uint256(x)) >> 200 == uint256(0)) return int200(x);
_revertOverflow();
}
}
/// @dev Casts `x` to a int208. Reverts on overflow.
function toInt208(int256 x) internal pure returns (int208) {
unchecked {
if (((1 << 207) + uint256(x)) >> 208 == uint256(0)) return int208(x);
_revertOverflow();
}
}
/// @dev Casts `x` to a int216. Reverts on overflow.
function toInt216(int256 x) internal pure returns (int216) {
unchecked {
if (((1 << 215) + uint256(x)) >> 216 == uint256(0)) return int216(x);
_revertOverflow();
}
}
/// @dev Casts `x` to a int224. Reverts on overflow.
function toInt224(int256 x) internal pure returns (int224) {
unchecked {
if (((1 << 223) + uint256(x)) >> 224 == uint256(0)) return int224(x);
_revertOverflow();
}
}
/// @dev Casts `x` to a int232. Reverts on overflow.
function toInt232(int256 x) internal pure returns (int232) {
unchecked {
if (((1 << 231) + uint256(x)) >> 232 == uint256(0)) return int232(x);
_revertOverflow();
}
}
/// @dev Casts `x` to a int240. Reverts on overflow.
function toInt240(int256 x) internal pure returns (int240) {
unchecked {
if (((1 << 239) + uint256(x)) >> 240 == uint256(0)) return int240(x);
_revertOverflow();
}
}
/// @dev Casts `x` to a int248. Reverts on overflow.
function toInt248(int256 x) internal pure returns (int248) {
unchecked {
if (((1 << 247) + uint256(x)) >> 248 == uint256(0)) return int248(x);
_revertOverflow();
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* OTHER SAFE CASTING OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Casts `x` to a int8. Reverts on overflow.
function toInt8(uint256 x) internal pure returns (int8) {
if (x >= 1 << 7) _revertOverflow();
return int8(int256(x));
}
/// @dev Casts `x` to a int16. Reverts on overflow.
function toInt16(uint256 x) internal pure returns (int16) {
if (x >= 1 << 15) _revertOverflow();
return int16(int256(x));
}
/// @dev Casts `x` to a int24. Reverts on overflow.
function toInt24(uint256 x) internal pure returns (int24) {
if (x >= 1 << 23) _revertOverflow();
return int24(int256(x));
}
/// @dev Casts `x` to a int32. Reverts on overflow.
function toInt32(uint256 x) internal pure returns (int32) {
if (x >= 1 << 31) _revertOverflow();
return int32(int256(x));
}
/// @dev Casts `x` to a int40. Reverts on overflow.
function toInt40(uint256 x) internal pure returns (int40) {
if (x >= 1 << 39) _revertOverflow();
return int40(int256(x));
}
/// @dev Casts `x` to a int48. Reverts on overflow.
function toInt48(uint256 x) internal pure returns (int48) {
if (x >= 1 << 47) _revertOverflow();
return int48(int256(x));
}
/// @dev Casts `x` to a int56. Reverts on overflow.
function toInt56(uint256 x) internal pure returns (int56) {
if (x >= 1 << 55) _revertOverflow();
return int56(int256(x));
}
/// @dev Casts `x` to a int64. Reverts on overflow.
function toInt64(uint256 x) internal pure returns (int64) {
if (x >= 1 << 63) _revertOverflow();
return int64(int256(x));
}
/// @dev Casts `x` to a int72. Reverts on overflow.
function toInt72(uint256 x) internal pure returns (int72) {
if (x >= 1 << 71) _revertOverflow();
return int72(int256(x));
}
/// @dev Casts `x` to a int80. Reverts on overflow.
function toInt80(uint256 x) internal pure returns (int80) {
if (x >= 1 << 79) _revertOverflow();
return int80(int256(x));
}
/// @dev Casts `x` to a int88. Reverts on overflow.
function toInt88(uint256 x) internal pure returns (int88) {
if (x >= 1 << 87) _revertOverflow();
return int88(int256(x));
}
/// @dev Casts `x` to a int96. Reverts on overflow.
function toInt96(uint256 x) internal pure returns (int96) {
if (x >= 1 << 95) _revertOverflow();
return int96(int256(x));
}
/// @dev Casts `x` to a int104. Reverts on overflow.
function toInt104(uint256 x) internal pure returns (int104) {
if (x >= 1 << 103) _revertOverflow();
return int104(int256(x));
}
/// @dev Casts `x` to a int112. Reverts on overflow.
function toInt112(uint256 x) internal pure returns (int112) {
if (x >= 1 << 111) _revertOverflow();
return int112(int256(x));
}
/// @dev Casts `x` to a int120. Reverts on overflow.
function toInt120(uint256 x) internal pure returns (int120) {
if (x >= 1 << 119) _revertOverflow();
return int120(int256(x));
}
/// @dev Casts `x` to a int128. Reverts on overflow.
function toInt128(uint256 x) internal pure returns (int128) {
if (x >= 1 << 127) _revertOverflow();
return int128(int256(x));
}
/// @dev Casts `x` to a int136. Reverts on overflow.
function toInt136(uint256 x) internal pure returns (int136) {
if (x >= 1 << 135) _revertOverflow();
return int136(int256(x));
}
/// @dev Casts `x` to a int144. Reverts on overflow.
function toInt144(uint256 x) internal pure returns (int144) {
if (x >= 1 << 143) _revertOverflow();
return int144(int256(x));
}
/// @dev Casts `x` to a int152. Reverts on overflow.
function toInt152(uint256 x) internal pure returns (int152) {
if (x >= 1 << 151) _revertOverflow();
return int152(int256(x));
}
/// @dev Casts `x` to a int160. Reverts on overflow.
function toInt160(uint256 x) internal pure returns (int160) {
if (x >= 1 << 159) _revertOverflow();
return int160(int256(x));
}
/// @dev Casts `x` to a int168. Reverts on overflow.
function toInt168(uint256 x) internal pure returns (int168) {
if (x >= 1 << 167) _revertOverflow();
return int168(int256(x));
}
/// @dev Casts `x` to a int176. Reverts on overflow.
function toInt176(uint256 x) internal pure returns (int176) {
if (x >= 1 << 175) _revertOverflow();
return int176(int256(x));
}
/// @dev Casts `x` to a int184. Reverts on overflow.
function toInt184(uint256 x) internal pure returns (int184) {
if (x >= 1 << 183) _revertOverflow();
return int184(int256(x));
}
/// @dev Casts `x` to a int192. Reverts on overflow.
function toInt192(uint256 x) internal pure returns (int192) {
if (x >= 1 << 191) _revertOverflow();
return int192(int256(x));
}
/// @dev Casts `x` to a int200. Reverts on overflow.
function toInt200(uint256 x) internal pure returns (int200) {
if (x >= 1 << 199) _revertOverflow();
return int200(int256(x));
}
/// @dev Casts `x` to a int208. Reverts on overflow.
function toInt208(uint256 x) internal pure returns (int208) {
if (x >= 1 << 207) _revertOverflow();
return int208(int256(x));
}
/// @dev Casts `x` to a int216. Reverts on overflow.
function toInt216(uint256 x) internal pure returns (int216) {
if (x >= 1 << 215) _revertOverflow();
return int216(int256(x));
}
/// @dev Casts `x` to a int224. Reverts on overflow.
function toInt224(uint256 x) internal pure returns (int224) {
if (x >= 1 << 223) _revertOverflow();
return int224(int256(x));
}
/// @dev Casts `x` to a int232. Reverts on overflow.
function toInt232(uint256 x) internal pure returns (int232) {
if (x >= 1 << 231) _revertOverflow();
return int232(int256(x));
}
/// @dev Casts `x` to a int240. Reverts on overflow.
function toInt240(uint256 x) internal pure returns (int240) {
if (x >= 1 << 239) _revertOverflow();
return int240(int256(x));
}
/// @dev Casts `x` to a int248. Reverts on overflow.
function toInt248(uint256 x) internal pure returns (int248) {
if (x >= 1 << 247) _revertOverflow();
return int248(int256(x));
}
/// @dev Casts `x` to a int256. Reverts on overflow.
function toInt256(uint256 x) internal pure returns (int256) {
if (int256(x) >= 0) return int256(x);
_revertOverflow();
}
/// @dev Casts `x` to a uint256. Reverts on overflow.
function toUint256(int256 x) internal pure returns (uint256) {
if (x >= 0) return uint256(x);
_revertOverflow();
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* PRIVATE HELPERS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
function _revertOverflow() private pure {
/// @solidity memory-safe-assembly
assembly {
// Store the function selector of `Overflow()`.
mstore(0x00, 0x35278d12)
// Revert with (offset, size).
revert(0x1c, 0x04)
}
}
}// SPDX-License-Identifier: ekubo-license-v1.eth
pragma solidity =0.8.33;
import {IExposedStorage} from "../interfaces/IExposedStorage.sol";
/// @title ExposedStorage
/// @notice Abstract contract that implements storage exposure functionality
/// @dev This contract provides the implementation for the IExposedStorage interface,
/// allowing inheriting contracts to expose their storage slots via view functions.
/// Uses inline assembly for efficient storage access.
abstract contract ExposedStorage is IExposedStorage {
/// @inheritdoc IExposedStorage
/// @dev Uses inline assembly to efficiently read multiple storage slots specified in calldata.
/// Each slot value is loaded using the SLOAD opcode and stored in memory.
function sload() external view {
assembly ("memory-safe") {
for { let i := 4 } lt(i, calldatasize()) { i := add(i, 32) } { mstore(sub(i, 4), sload(calldataload(i))) }
return(0, sub(calldatasize(), 4))
}
}
/// @inheritdoc IExposedStorage
/// @dev Uses inline assembly to efficiently read multiple transient storage slots specified in calldata.
/// Each slot value is loaded using the TLOAD opcode and stored in memory.
function tload() external view {
assembly ("memory-safe") {
for { let i := 4 } lt(i, calldatasize()) { i := add(i, 32) } { mstore(sub(i, 4), tload(calldataload(i))) }
return(0, sub(calldatasize(), 4))
}
}
}// SPDX-License-Identifier: ekubo-license-v1.eth
pragma solidity =0.8.33;
import {FixedPointMathLib} from "solady/utils/FixedPointMathLib.sol";
import {LibBit} from "solady/utils/LibBit.sol";
import {SafeCastLib} from "solady/utils/SafeCastLib.sol";
import {amount0Delta, amount1Delta, sortAndConvertToFixedSqrtRatios} from "./delta.sol";
import {SqrtRatio} from "../types/sqrtRatio.sol";
// Liquidity Math Library
// Contains functions for calculating liquidity-related amounts and conversions
// Provides utilities for converting between liquidity changes and token amounts
/// @notice Returns the token0 and token1 delta owed for a given change in liquidity
/// @dev Calculates the token amounts required or returned when liquidity is added or removed from a position
/// @param sqrtRatio Current price (as a valid sqrt ratio)
/// @param liquidityDelta Signed liquidity change; positive = added, negative = removed
/// @param sqrtRatioLower The lower bound of the price range (as a valid sqrt ratio)
/// @param sqrtRatioUpper The upper bound of the price range (as a valid sqrt ratio)
/// @return delta0 The change in token0 amount
/// @return delta1 The change in token1 amount
function liquidityDeltaToAmountDelta(
SqrtRatio sqrtRatio,
int128 liquidityDelta,
SqrtRatio sqrtRatioLower,
SqrtRatio sqrtRatioUpper
) pure returns (int128 delta0, int128 delta1) {
unchecked {
if (liquidityDelta == 0) {
return (0, 0);
}
bool isPositive = (liquidityDelta > 0);
int256 sign = -1 + 2 * int256(LibBit.rawToUint(isPositive));
// absolute value of a int128 always fits in a uint128
uint128 magnitude = uint128(FixedPointMathLib.abs(liquidityDelta));
if (sqrtRatio <= sqrtRatioLower) {
delta0 = SafeCastLib.toInt128(
sign * int256(uint256(amount0Delta(sqrtRatioLower, sqrtRatioUpper, magnitude, isPositive)))
);
} else if (sqrtRatio < sqrtRatioUpper) {
delta0 = SafeCastLib.toInt128(
sign * int256(uint256(amount0Delta(sqrtRatio, sqrtRatioUpper, magnitude, isPositive)))
);
delta1 = SafeCastLib.toInt128(
sign * int256(uint256(amount1Delta(sqrtRatioLower, sqrtRatio, magnitude, isPositive)))
);
} else {
delta1 = SafeCastLib.toInt128(
sign * int256(uint256(amount1Delta(sqrtRatioLower, sqrtRatioUpper, magnitude, isPositive)))
);
}
}
}
/// @notice Calculates the maximum liquidity that can be provided with a given amount of token0
/// @dev Used when the current price is below the position's range (only token0 is needed)
/// @param sqrtRatioLower The lower sqrt price ratio of the position
/// @param sqrtRatioUpper The upper sqrt price ratio of the position
/// @param amount The amount of token0 available
/// @return The maximum liquidity that can be provided
function maxLiquidityForToken0(uint256 sqrtRatioLower, uint256 sqrtRatioUpper, uint128 amount) pure returns (uint256) {
unchecked {
uint256 numerator1 = FixedPointMathLib.fullMulDivN(sqrtRatioLower, sqrtRatioUpper, 128);
return FixedPointMathLib.fullMulDiv(amount, numerator1, (sqrtRatioUpper - sqrtRatioLower));
}
}
/// @notice Calculates the maximum liquidity that can be provided with a given amount of token1
/// @dev Used when the current price is above the position's range (only token1 is needed)
/// @param sqrtRatioLower The lower sqrt price ratio of the position
/// @param sqrtRatioUpper The upper sqrt price ratio of the position
/// @param amount The amount of token1 available
/// @return The maximum liquidity that can be provided
function maxLiquidityForToken1(uint256 sqrtRatioLower, uint256 sqrtRatioUpper, uint128 amount) pure returns (uint256) {
unchecked {
return (uint256(amount) << 128) / (sqrtRatioUpper - sqrtRatioLower);
}
}
/// @notice Calculates the maximum liquidity that can be provided given amounts of both tokens
/// @dev Determines the limiting factor between token0 and token1 based on current price and position bounds
/// @param _sqrtRatio Current sqrt price ratio
/// @param sqrtRatioA One bound of the position (will be sorted with sqrtRatioB)
/// @param sqrtRatioB Other bound of the position (will be sorted with sqrtRatioA)
/// @param amount0 Available amount of token0
/// @param amount1 Available amount of token1
/// @return The maximum liquidity that can be provided with the given token amounts
function maxLiquidity(
SqrtRatio _sqrtRatio,
SqrtRatio sqrtRatioA,
SqrtRatio sqrtRatioB,
uint128 amount0,
uint128 amount1
) pure returns (uint128) {
uint256 sqrtRatio = _sqrtRatio.toFixed();
(uint256 sqrtRatioLower, uint256 sqrtRatioUpper) = sortAndConvertToFixedSqrtRatios(sqrtRatioA, sqrtRatioB);
if (sqrtRatio <= sqrtRatioLower) {
return uint128(
FixedPointMathLib.min(type(uint128).max, maxLiquidityForToken0(sqrtRatioLower, sqrtRatioUpper, amount0))
);
} else if (sqrtRatio < sqrtRatioUpper) {
return uint128(
FixedPointMathLib.min(
type(uint128).max,
FixedPointMathLib.min(
maxLiquidityForToken0(sqrtRatio, sqrtRatioUpper, amount0),
maxLiquidityForToken1(sqrtRatioLower, sqrtRatio, amount1)
)
)
);
} else {
return uint128(
FixedPointMathLib.min(type(uint128).max, maxLiquidityForToken1(sqrtRatioLower, sqrtRatioUpper, amount1))
);
}
}
/// @notice Thrown when a liquidity delta operation would cause overflow
error LiquidityDeltaOverflow();
/// @notice Safely adds a liquidity delta to a liquidity amount
/// @dev Reverts if the operation would cause overflow or underflow
/// @param liquidity The current liquidity amount
/// @param liquidityDelta The change in liquidity (can be positive or negative)
/// @return result The new liquidity amount after applying the delta
function addLiquidityDelta(uint128 liquidity, int128 liquidityDelta) pure returns (uint128 result) {
assembly ("memory-safe") {
result := add(liquidity, liquidityDelta)
if and(result, shl(128, 0xffffffffffffffffffffffffffffffff)) {
mstore(0, shl(224, 0x6d862c50))
revert(0, 4)
}
}
}// SPDX-License-Identifier: ekubo-license-v1.eth
pragma solidity =0.8.33;
import {Bitmap} from "../types/bitmap.sol";
import {MIN_TICK, MAX_TICK} from "../math/constants.sol";
import {StorageSlot} from "../types/storageSlot.sol";
// Addition of this offset does two things--it centers the 0 tick within a single bitmap regardless of tick spacing,
// and gives us a contiguous range of unsigned integers for all ticks
uint256 constant TICK_BITMAP_STORAGE_OFFSET = 89421695;
// Returns the index of the word and the index _in_ that word which contains the bit representing whether the tick is initialized
// Always rounds the tick down to the nearest multiple of tickSpacing
function tickToBitmapWordAndIndex(int32 tick, uint32 tickSpacing) pure returns (uint256 word, uint256 index) {
assembly ("memory-safe") {
let rawIndex := add(sub(sdiv(tick, tickSpacing), slt(smod(tick, tickSpacing), 0)), TICK_BITMAP_STORAGE_OFFSET)
word := shr(8, rawIndex)
index := and(rawIndex, 0xff)
}
}
// Returns the index of the word and the index _in_ that word which contains the bit representing whether the tick is initialized
/// @dev This function is only safe if tickSpacing is between 1 and MAX_TICK_SPACING, and word/index correspond to the results of tickToBitmapWordAndIndex for a tick between MIN_TICK and MAX_TICK
function bitmapWordAndIndexToTick(uint256 word, uint256 index, uint32 tickSpacing) pure returns (int32 tick) {
assembly ("memory-safe") {
let rawIndex := add(shl(8, word), index)
tick := mul(sub(rawIndex, TICK_BITMAP_STORAGE_OFFSET), tickSpacing)
}
}
function loadBitmap(StorageSlot slot, uint256 word) view returns (Bitmap bitmap) {
bitmap = Bitmap.wrap(uint256(slot.add(word).load()));
}
// Flips the tick in the bitmap from true to false or vice versa
function flipTick(StorageSlot slot, int32 tick, uint32 tickSpacing) {
(uint256 word, uint256 index) = tickToBitmapWordAndIndex(tick, tickSpacing);
StorageSlot wordSlot = slot.add(word);
wordSlot.store(wordSlot.load() ^ bytes32(1 << index));
}
function findNextInitializedTick(StorageSlot slot, int32 fromTick, uint32 tickSpacing, uint256 skipAhead)
view
returns (int32 nextTick, bool isInitialized)
{
unchecked {
nextTick = fromTick;
while (true) {
// convert the given tick to the bitmap position of the next nearest potential initialized tick
(uint256 word, uint256 index) = tickToBitmapWordAndIndex(nextTick + int32(tickSpacing), tickSpacing);
Bitmap bitmap = loadBitmap(slot, word);
// find the index of the previous tick in that word
uint256 nextIndex = bitmap.geSetBit(uint8(index));
// if we found one, return it
if (nextIndex != 0) {
(nextTick, isInitialized) = (bitmapWordAndIndexToTick(word, nextIndex - 1, tickSpacing), true);
break;
}
// otherwise, return the tick of the most significant bit in the word
nextTick = bitmapWordAndIndexToTick(word, 255, tickSpacing);
if (nextTick >= MAX_TICK) {
nextTick = MAX_TICK;
break;
}
// if we are done searching, stop here
if (skipAhead == 0) {
break;
}
skipAhead--;
}
}
}
function findPrevInitializedTick(StorageSlot slot, int32 fromTick, uint32 tickSpacing, uint256 skipAhead)
view
returns (int32 prevTick, bool isInitialized)
{
unchecked {
prevTick = fromTick;
while (true) {
// convert the given tick to its bitmap position
(uint256 word, uint256 index) = tickToBitmapWordAndIndex(prevTick, tickSpacing);
Bitmap bitmap = loadBitmap(slot, word);
// find the index of the previous tick in that word
uint256 prevIndex = bitmap.leSetBit(uint8(index));
if (prevIndex != 0) {
(prevTick, isInitialized) = (bitmapWordAndIndexToTick(word, prevIndex - 1, tickSpacing), true);
break;
}
prevTick = bitmapWordAndIndexToTick(word, 0, tickSpacing);
if (prevTick <= MIN_TICK) {
prevTick = MIN_TICK;
break;
}
if (skipAhead == 0) {
break;
}
skipAhead--;
prevTick--;
}
}
}// SPDX-License-Identifier: ekubo-license-v1.eth
pragma solidity ^0.8.0;
import {CallPoints} from "../types/callPoints.sol";
import {PoolKey} from "../types/poolKey.sol";
import {PositionId} from "../types/positionId.sol";
import {FeesPerLiquidity} from "../types/feesPerLiquidity.sol";
import {IExposedStorage} from "../interfaces/IExposedStorage.sol";
import {IFlashAccountant} from "../interfaces/IFlashAccountant.sol";
import {SqrtRatio} from "../types/sqrtRatio.sol";
import {PoolState} from "../types/poolState.sol";
import {SwapParameters} from "../types/swapParameters.sol";
import {PoolId} from "../types/poolId.sol";
import {Locker} from "../types/locker.sol";
import {PoolBalanceUpdate} from "../types/poolBalanceUpdate.sol";
/// @title Extension Interface
/// @notice Interface for pool extensions that can hook into core operations
/// @dev Extensions must register with the core contract and implement these hooks
interface IExtension {
/// @notice Called before a pool is initialized
/// @param caller Address that initiated the pool initialization
/// @param key Pool key identifying the pool
/// @param tick Initial tick for the pool
function beforeInitializePool(address caller, PoolKey calldata key, int32 tick) external;
/// @notice Called after a pool is initialized
/// @param caller Address that initiated the pool initialization
/// @param key Pool key identifying the pool
/// @param tick Initial tick for the pool
/// @param sqrtRatio Initial sqrt price ratio for the pool
function afterInitializePool(address caller, PoolKey calldata key, int32 tick, SqrtRatio sqrtRatio) external;
/// @notice Called before a position is updated
/// @param locker The current holder of the lock performing the position update
/// @param poolKey Pool key identifying the pool
/// @param positionId The key of the position that is being updated
/// @param liquidityDelta The change in liquidity that is being requested for the position
function beforeUpdatePosition(Locker locker, PoolKey memory poolKey, PositionId positionId, int128 liquidityDelta)
external;
/// @notice Called after a position is updated
/// @param locker The current holder of the lock performing the position update
/// @param poolKey Pool key identifying the pool
/// @param positionId The key of the position that was updated
/// @param liquidityDelta Change in liquidity of the specified position key range
/// @param balanceUpdate Change in token balances of the pool (delta0 and delta1)
function afterUpdatePosition(
Locker locker,
PoolKey memory poolKey,
PositionId positionId,
int128 liquidityDelta,
PoolBalanceUpdate balanceUpdate,
PoolState stateAfter
) external;
/// @notice Called before a swap is executed
/// @param locker The current holder of the lock performing the swap
/// @param poolKey Pool key identifying the pool
/// @param params Swap parameters containing amount, isToken1, sqrtRatioLimit, and skipAhead
function beforeSwap(Locker locker, PoolKey memory poolKey, SwapParameters params) external;
/// @notice Called after a swap is executed
/// @param locker The current holder of the lock performing the swap
/// @param poolKey Pool key identifying the pool
/// @param params Swap parameters containing amount, isToken1, sqrtRatioLimit, and skipAhead
/// @param balanceUpdate Change in token balances (delta0 and delta1)
/// @param stateAfter The pool state after the swap
function afterSwap(
Locker locker,
PoolKey memory poolKey,
SwapParameters params,
PoolBalanceUpdate balanceUpdate,
PoolState stateAfter
) external;
/// @notice Called before fees are collected from a position
/// @param locker The current holder of the lock collecting fees
/// @param poolKey Pool key identifying the pool
/// @param positionId The key of the position for which fees will be collected
function beforeCollectFees(Locker locker, PoolKey memory poolKey, PositionId positionId) external;
/// @notice Called after fees are collected from a position
/// @param locker The current holder of the lock collecting fees
/// @param poolKey Pool key identifying the pool
/// @param positionId The key of the position for which fees were collected
/// @param amount0 Amount of token0 fees collected
/// @param amount1 Amount of token1 fees collected
function afterCollectFees(
Locker locker,
PoolKey memory poolKey,
PositionId positionId,
uint128 amount0,
uint128 amount1
) external;
}
/// @title Core Interface
/// @notice Main interface for the Ekubo Protocol core contract
/// @dev Inherits from IFlashAccountant and IExposedStorage for additional functionality
interface ICore is IFlashAccountant, IExposedStorage {
/// @notice Emitted when an extension is registered
/// @param extension Address of the registered extension
event ExtensionRegistered(address extension);
/// @notice Emitted when a pool is initialized
/// @param poolId Unique identifier for the pool
/// @param poolKey Pool key containing token addresses and configuration
/// @param tick Initial tick for the pool
/// @param sqrtRatio Initial sqrt price ratio for the pool
event PoolInitialized(PoolId poolId, PoolKey poolKey, int32 tick, SqrtRatio sqrtRatio);
/// @notice Emitted when a position is updated
/// @param locker The locker that is updating the position
/// @param poolId Unique identifier for the pool
/// @param positionId Identifier of the position specifying a salt and the bounds
/// @param liquidityDelta The change in liquidity for the specified pool and position keys
/// @param balanceUpdate Change in token balances (delta0 and delta1)
event PositionUpdated(
address locker,
PoolId poolId,
PositionId positionId,
int128 liquidityDelta,
PoolBalanceUpdate balanceUpdate,
PoolState stateAfter
);
/// @notice Emitted when fees are collected from a position
/// @param locker The locker that is collecting fees
/// @param poolId Unique identifier for the pool
/// @param positionId Identifier of the position specifying a salt and the bounds
/// @param amount0 Amount of token0 fees collected
/// @param amount1 Amount of token1 fees collected
event PositionFeesCollected(address locker, PoolId poolId, PositionId positionId, uint128 amount0, uint128 amount1);
/// @notice Emitted when fees are accumulated to a pool
/// @param poolId Unique identifier for the pool
/// @param amount0 Amount of token0 fees accumulated
/// @param amount1 Amount of token1 fees accumulated
/// @dev Note locker is ommitted because it's always the extension of the pool associated with poolId
event FeesAccumulated(PoolId poolId, uint128 amount0, uint128 amount1);
/// @notice Thrown when extension registration fails due to invalid call points
error FailedRegisterInvalidCallPoints();
/// @notice Thrown when trying to register an already registered extension
error ExtensionAlreadyRegistered();
/// @notice Thrown when saved balance operations would cause overflow
error SavedBalanceOverflow();
/// @notice Thrown when trying to initialize an already initialized pool
error PoolAlreadyInitialized();
/// @notice Thrown when trying to use an unregistered extension
error ExtensionNotRegistered();
/// @notice Thrown when trying to operate on an uninitialized pool
error PoolNotInitialized();
/// @notice Thrown when sqrt ratio limit is out of valid range
error SqrtRatioLimitOutOfRange();
/// @notice Thrown when sqrt ratio limit parameter given to swap is not a valid sqrt ratio
error InvalidSqrtRatioLimit();
/// @notice Thrown when the sqrt ratio limit is in the wrong direction of the current price
error SqrtRatioLimitWrongDirection();
/// @notice Thrown when saved balance tokens are not properly sorted
error SavedBalanceTokensNotSorted();
/// @notice Thrown when a position update would cause liquidityNet on a tick to exceed the maximum allowed
/// @param tick The tick that would exceed the limit
/// @param liquidityNet The resulting liquidityNet that exceeds the limit
/// @param maxLiquidityPerTick The maximum allowed liquidity per tick
error MaxLiquidityPerTickExceeded(int32 tick, uint128 liquidityNet, uint128 maxLiquidityPerTick);
/// @notice Registers an extension with the core contract
/// @dev Extensions must call this function to become registered. The call points are validated against the caller address
/// @param expectedCallPoints Call points configuration for the extension
function registerExtension(CallPoints memory expectedCallPoints) external;
/// @notice Initializes a new pool with the given tick
/// @dev Sets the initial price for a new pool in terms of tick
/// @param poolKey Pool key identifying the pool to initialize
/// @param tick Initial tick for the pool
/// @return sqrtRatio Initial sqrt price ratio for the pool
function initializePool(PoolKey memory poolKey, int32 tick) external returns (SqrtRatio sqrtRatio);
/// @notice Finds the previous initialized tick
/// @param poolId Unique identifier for the pool
/// @param fromTick Starting tick to search from
/// @param tickSpacing Tick spacing for the pool
/// @param skipAhead Number of ticks to skip for gas optimization
/// @return tick The previous initialized tick
/// @return isInitialized Whether the tick is initialized
function prevInitializedTick(PoolId poolId, int32 fromTick, uint32 tickSpacing, uint256 skipAhead)
external
view
returns (int32 tick, bool isInitialized);
/// @notice Finds the next initialized tick
/// @param poolId Unique identifier for the pool
/// @param fromTick Starting tick to search from
/// @param tickSpacing Tick spacing for the pool
/// @param skipAhead Number of ticks to skip for gas optimization
/// @return tick The next initialized tick
/// @return isInitialized Whether the tick is initialized
function nextInitializedTick(PoolId poolId, int32 fromTick, uint32 tickSpacing, uint256 skipAhead)
external
view
returns (int32 tick, bool isInitialized);
/// @notice Updates saved balances for later use
/// @dev The saved balances are stored in a single slot. The resulting saved balance must fit within a uint128 container
/// @param token0 Address of the first token (must be < token1)
/// @param token1 Address of the second token (must be > token0)
/// @param salt Unique identifier for the saved balance
/// @param delta0 Change in token0 balance (positive for saving, negative for loading)
/// @param delta1 Change in token1 balance (positive for saving, negative for loading)
function updateSavedBalances(address token0, address token1, bytes32 salt, int256 delta0, int256 delta1)
external
payable;
/// @notice Returns the accumulated fees per liquidity inside the given bounds
/// @dev The reason this getter is exposed is that it requires conditional SLOADs for maximum efficiency
/// @param poolId The ID of the pool to fetch the fees per liquidity inside
/// @param tickLower Lower bound of the price range to get the snapshot
/// @param tickLower Upper bound of the price range to get the snapshot
/// @return feesPerLiquidity Accumulated fees per liquidity inside the bounds
function getPoolFeesPerLiquidityInside(PoolId poolId, int32 tickLower, int32 tickUpper)
external
view
returns (FeesPerLiquidity memory feesPerLiquidity);
/// @notice Accumulates tokens as fees for a pool
/// @dev Only callable by the extension of the specified pool key. The current locker must be the extension.
/// The extension must call this function within a lock callback
/// @param poolKey Pool key identifying the pool
/// @param amount0 Amount of token0 to accumulate as fees
/// @param amount1 Amount of token1 to accumulate as fees
function accumulateAsFees(PoolKey memory poolKey, uint128 amount0, uint128 amount1) external payable;
/// @notice Updates a liquidity position and sets extra data
/// @param poolKey Pool key identifying the pool
/// @param positionId The key of the position to update
/// @param liquidityDelta The change in liquidity
/// @return balanceUpdate Change in token balances (delta0 and delta1)
function updatePosition(PoolKey memory poolKey, PositionId positionId, int128 liquidityDelta)
external
payable
returns (PoolBalanceUpdate balanceUpdate);
/// @notice Sets extra data for the position
/// @param poolId ID of the pool for which the position exists
/// @param positionId The key of the position to set extra data at
/// @param extraData The data to set on the position
function setExtraData(PoolId poolId, PositionId positionId, bytes16 extraData) external;
/// @notice Collects accumulated fees from a position
/// @param poolKey Pool key identifying the pool
/// @param positionId The key of the position for which to collect fees
/// @return amount0 Amount of token0 fees collected
/// @return amount1 Amount of token1 fees collected
function collectFees(PoolKey memory poolKey, PositionId positionId)
external
returns (uint128 amount0, uint128 amount1);
/// @notice Executes a swap against a pool
/// @dev Function name is mined to have a zero function selector for gas efficiency
function swap_6269342730() external payable;
}// SPDX-License-Identifier: ekubo-license-v1.eth
pragma solidity =0.8.33;
import {NATIVE_TOKEN_ADDRESS} from "../math/constants.sol";
import {IFlashAccountant} from "../interfaces/IFlashAccountant.sol";
import {Locker} from "../types/locker.sol";
/// @title FlashAccountant
/// @notice Abstract contract that provides flash loan accounting functionality using transient storage
/// @dev This contract manages debt tracking for flash loans, allowing users to borrow tokens temporarily
/// and ensuring all debts are settled before the transaction completes. Uses transient storage
/// for gas-efficient temporary state management within a single transaction.
abstract contract FlashAccountant is IFlashAccountant {
// These offsets are selected so that they do not accidentally overlap with any other base contract's use of transient storage
/// @dev Transient storage slot for tracking the current locker ID and address
/// @dev The stored ID is kept as id + 1 to facilitate the NotLocked check (zero means unlocked)
/// @dev Generated using: cast keccak "FlashAccountant#CURRENT_LOCKER_SLOT"
uint256 private constant _CURRENT_LOCKER_SLOT = 0x07cc7f5195d862f505d6b095c82f92e00cfc1766f5bca4383c28dc5fca1555fd;
/// @dev Transient storage offset for tracking token debts for each locker
/// @dev Generated using: cast keccak "FlashAccountant#_DEBT_LOCKER_TOKEN_ADDRESS_OFFSET"
uint256 private constant _DEBT_LOCKER_TOKEN_ADDRESS_OFFSET =
0x753dfe4b4dfb3ff6c11bbf6a97f3c094e91c003ce904a55cc5662fbad220f599;
/// @dev Transient storage offset for tracking the count of tokens with non-zero debt for each locker
/// @dev Generated using: cast keccak "FlashAccountant#NONZERO_DEBT_COUNT_OFFSET"
uint256 private constant _NONZERO_DEBT_COUNT_OFFSET =
0x7772acfd7e0f66ebb20a058830296c3dc1301b111d23348e1c961d324223190d;
/// @dev Transient storage offset for tracking token balances during payment operations
/// @dev Generated using: cast keccak "FlashAccountant#_PAYMENT_TOKEN_ADDRESS_OFFSET"
uint256 private constant _PAYMENT_TOKEN_ADDRESS_OFFSET =
0x6747da56dbd05b26a7ecd2a0106781585141cf07098ad54c0e049e4e86dccb8c;
/// @notice Gets the current locker information from transient storage
/// @dev Reverts with NotLocked() if no lock is currently active
/// @return locker The current locker containing both id and address
function _getLocker() internal view returns (Locker locker) {
assembly ("memory-safe") {
locker := tload(_CURRENT_LOCKER_SLOT)
if iszero(locker) {
// cast sig "NotLocked()"
mstore(0, shl(224, 0x1834e265))
revert(0, 4)
}
}
}
/// @notice Gets the current locker information and ensures the caller is the locker
/// @dev Reverts with LockerOnly() if the caller is not the current locker
/// @return locker The current locker containing both id and address (address must be msg.sender)
function _requireLocker() internal view returns (Locker locker) {
locker = _getLocker();
if (locker.addr() != msg.sender) revert LockerOnly();
}
/// @notice Updates the debt tracking for a specific locker and token
/// @dev We assume debtChange cannot exceed a 128 bits value, even though it uses a int256 container.
/// This must be enforced at the places it is called for this contract's safety.
/// Negative values erase debt, positive values add debt.
/// Updates the non-zero debt count when debt transitions between zero and non-zero states.
/// @param id The locker ID to update debt for
/// @param token The token address to update debt for
/// @param debtChange The change in debt (negative to reduce, positive to increase)
function _accountDebt(uint256 id, address token, int256 debtChange) internal {
assembly ("memory-safe") {
let deltaSlot := add(_DEBT_LOCKER_TOKEN_ADDRESS_OFFSET, add(shl(160, id), token))
let current := tload(deltaSlot)
// we know this never overflows because debtChange is only ever derived from 128 bit values in inheriting contracts
let next := add(current, debtChange)
let countChange := sub(iszero(current), iszero(next))
if countChange {
let nzdCountSlot := add(id, _NONZERO_DEBT_COUNT_OFFSET)
tstore(nzdCountSlot, add(tload(nzdCountSlot), countChange))
}
tstore(deltaSlot, next)
}
}
/// @notice Updates the debt tracking for a specific locker and pair of tokens in a single operation
/// @dev Optimized version that updates both tokens' debts and performs a single tload/tstore on the non-zero debt count.
/// Individual token debt slots are still updated separately, but the non-zero debt count is only loaded/stored once.
/// We assume debtChange values cannot exceed 128 bits. This must be enforced at the places it is called for this contract's safety.
/// Negative values erase debt, positive values add debt.
/// @param id The locker ID to update debt for
/// @param tokenA The first token address
/// @param tokenB The second token address
/// @param debtChangeA The change in debt for tokenA (negative to reduce, positive to increase)
/// @param debtChangeB The change in debt for tokenB (negative to reduce, positive to increase)
function _updatePairDebt(uint256 id, address tokenA, address tokenB, int256 debtChangeA, int256 debtChangeB)
internal
{
assembly ("memory-safe") {
let nzdCountChange := 0
// Update token0 debt if there's a change
if debtChangeA {
let deltaSlotA := add(_DEBT_LOCKER_TOKEN_ADDRESS_OFFSET, add(shl(160, id), tokenA))
let currentA := tload(deltaSlotA)
let nextA := add(currentA, debtChangeA)
nzdCountChange := sub(iszero(currentA), iszero(nextA))
tstore(deltaSlotA, nextA)
}
if debtChangeB {
let deltaSlotB := add(_DEBT_LOCKER_TOKEN_ADDRESS_OFFSET, add(shl(160, id), tokenB))
let currentB := tload(deltaSlotB)
let nextB := add(currentB, debtChangeB)
nzdCountChange := add(nzdCountChange, sub(iszero(currentB), iszero(nextB)))
tstore(deltaSlotB, nextB)
}
// Update non-zero debt count only if it changed
if nzdCountChange {
let nzdCountSlot := add(id, _NONZERO_DEBT_COUNT_OFFSET)
tstore(nzdCountSlot, add(tload(nzdCountSlot), nzdCountChange))
}
}
}
/// @inheritdoc IFlashAccountant
function updateDebt() external {
if (msg.data.length != 20) {
revert UpdateDebtMessageLength();
}
uint256 id = _getLocker().id();
int256 delta;
assembly ("memory-safe") {
delta := signextend(15, shr(128, calldataload(4)))
}
_accountDebt(id, msg.sender, delta);
}
/// @inheritdoc IFlashAccountant
function lock() external {
assembly ("memory-safe") {
let current := tload(_CURRENT_LOCKER_SLOT)
let id := shr(160, current)
// store the count
tstore(_CURRENT_LOCKER_SLOT, or(shl(160, add(id, 1)), caller()))
let free := mload(0x40)
// Prepare call to locked_(uint256) -> selector 0
mstore(free, 0)
mstore(add(free, 4), id) // ID argument
calldatacopy(add(free, 36), 4, sub(calldatasize(), 4))
// Call the original caller with the packed data
let success := call(gas(), caller(), 0, free, add(calldatasize(), 32), 0, 0)
// Pass through the error on failure
if iszero(success) {
returndatacopy(free, 0, returndatasize())
revert(free, returndatasize())
}
// Undo the "locker" state changes
tstore(_CURRENT_LOCKER_SLOT, current)
// Check if something is nonzero
let nonzeroDebtCount := tload(add(_NONZERO_DEBT_COUNT_OFFSET, id))
if nonzeroDebtCount {
// cast sig "DebtsNotZeroed(uint256)"
mstore(0x00, 0x9731ba37)
mstore(0x20, id)
revert(0x1c, 0x24)
}
// Directly return whatever the subcall returned
returndatacopy(free, 0, returndatasize())
return(free, returndatasize())
}
}
/// @inheritdoc IFlashAccountant
function forward(address to) external {
Locker locker = _requireLocker();
// update this lock's locker to the forwarded address for the duration of the forwarded
// call, meaning only the forwarded address can update state
assembly ("memory-safe") {
tstore(_CURRENT_LOCKER_SLOT, or(shl(160, shr(160, locker)), to))
let free := mload(0x40)
// Prepare call to forwarded_2374103877(bytes32) -> selector 0x01
mstore(free, shl(224, 1))
mstore(add(free, 4), locker)
calldatacopy(add(free, 36), 36, sub(calldatasize(), 36))
// Call the forwardee with the packed data
let success := call(gas(), to, 0, free, calldatasize(), 0, 0)
// Pass through the error on failure
if iszero(success) {
returndatacopy(free, 0, returndatasize())
revert(free, returndatasize())
}
tstore(_CURRENT_LOCKER_SLOT, locker)
// Directly return whatever the subcall returned
returndatacopy(free, 0, returndatasize())
return(free, returndatasize())
}
}
/// @inheritdoc IFlashAccountant
function startPayments() external {
assembly ("memory-safe") {
// 0-52 are used for the balanceOf calldata
mstore(20, address()) // Store the `account` argument.
mstore(0, 0x70a08231000000000000000000000000) // `balanceOf(address)`.
let free := mload(0x40)
for { let i := 4 } lt(i, calldatasize()) { i := add(i, 32) } {
// clean upper 96 bits of the token argument at i
let token := shr(96, shl(96, calldataload(i)))
let returnLocation := add(free, sub(i, 4))
let success := staticcall(gas(), token, 0x10, 0x24, returnLocation, 0x20)
let tokenBalance :=
mul(
mload(returnLocation),
and(
gt(returndatasize(), 0x1f), // At least 32 bytes returned.
success
)
)
tstore(add(_PAYMENT_TOKEN_ADDRESS_OFFSET, token), add(tokenBalance, success))
}
return(free, sub(calldatasize(), 4))
}
}
/// @inheritdoc IFlashAccountant
function completePayments() external {
uint256 id = _getLocker().id();
assembly ("memory-safe") {
let paymentAmounts := mload(0x40)
let nzdCountChange := 0
for { let i := 4 } lt(i, calldatasize()) { i := add(i, 32) } {
let token := shr(96, shl(96, calldataload(i)))
let offset := add(_PAYMENT_TOKEN_ADDRESS_OFFSET, token)
let lastBalance := tload(offset)
tstore(offset, 0)
mstore(20, address()) // Store the `account` argument.
mstore(0, 0x70a08231000000000000000000000000) // `balanceOf(address)`.
let currentBalance :=
mul( // The arguments of `mul` are evaluated from right to left.
mload(0),
and( // The arguments of `and` are evaluated from right to left.
gt(returndatasize(), 0x1f), // At least 32 bytes returned.
staticcall(gas(), token, 0x10, 0x24, 0, 0x20)
)
)
let payment :=
mul(
and(gt(lastBalance, 0), not(lt(currentBalance, lastBalance))),
sub(currentBalance, sub(lastBalance, 1))
)
// We never expect tokens to have this much total supply
if shr(128, payment) {
// cast sig "PaymentOverflow()"
mstore(0x00, 0x9cac58ca)
revert(0x1c, 4)
}
mstore(add(paymentAmounts, mul(16, div(i, 32))), shl(128, payment))
if payment {
let deltaSlot := add(_DEBT_LOCKER_TOKEN_ADDRESS_OFFSET, add(shl(160, id), token))
let current := tload(deltaSlot)
// never overflows because of the payment overflow check that bounds payment to 128 bits
let next := sub(current, payment)
nzdCountChange := add(nzdCountChange, sub(iszero(current), iszero(next)))
tstore(deltaSlot, next)
}
}
// Update nzdCountSlot only once if there were any changes
if nzdCountChange {
let nzdCountSlot := add(id, _NONZERO_DEBT_COUNT_OFFSET)
tstore(nzdCountSlot, add(tload(nzdCountSlot), nzdCountChange))
}
return(paymentAmounts, mul(16, div(calldatasize(), 32)))
}
}
/// @inheritdoc IFlashAccountant
function withdraw() external {
uint256 id = _requireLocker().id();
assembly ("memory-safe") {
let nzdCountChange := 0
// Process each withdrawal entry
for { let i := 4 } lt(i, calldatasize()) { i := add(i, 56) } {
let token := shr(96, calldataload(i))
let recipient := shr(96, calldataload(add(i, 20)))
let amount := shr(128, calldataload(add(i, 40)))
if amount {
// Update debt tracking without updating nzdCountSlot yet
let deltaSlot := add(_DEBT_LOCKER_TOKEN_ADDRESS_OFFSET, add(shl(160, id), token))
let current := tload(deltaSlot)
let next := add(current, amount)
nzdCountChange := add(nzdCountChange, sub(iszero(current), iszero(next)))
tstore(deltaSlot, next)
// Perform the transfer of the withdrawn asset
// Note that these calls can re-enter and even relock with the same ID
// However the nzdCountChange is always applied as a delta at the end, meaning we load the latest value before updating it,
// so it's safe from re-entry
switch token
case 0 {
let success := call(gas(), recipient, amount, 0, 0, 0, 0)
if iszero(success) {
// cast sig "ETHTransferFailed()"
mstore(0x00, 0xb12d13eb)
revert(0x1c, 4)
}
}
default {
mstore(0x14, recipient)
mstore(0x34, amount)
mstore(0x00, 0xa9059cbb000000000000000000000000)
let success := call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
if iszero(and(eq(mload(0x00), 1), success)) {
if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
revert(0x1c, 0x04)
}
}
}
}
}
// Update nzdCountSlot only once if there were any changes
if nzdCountChange {
let nzdCountSlot := add(id, _NONZERO_DEBT_COUNT_OFFSET)
tstore(nzdCountSlot, add(tload(nzdCountSlot), nzdCountChange))
}
// we return from assembly so as to prevent solidity from accessing the free memory pointer after we have written into it
return(0, 0)
}
}
/// @inheritdoc IFlashAccountant
receive() external payable {
uint256 id = _getLocker().id();
// Note because we use msg.value here, this contract can never be multicallable, i.e. it should never expose the ability
// to delegatecall itself more than once in a single call
unchecked {
// We assume msg.value will never exceed type(uint128).max, so this should never cause an overflow/underflow of debt
_accountDebt(id, NATIVE_TOKEN_ADDRESS, -int256(msg.value));
}
}
}// SPDX-License-Identifier: ekubo-license-v1.eth pragma solidity =0.8.33; // Protocol Constants // Contains all constant values used throughout the Ekubo Protocol // These constants define the boundaries and special values for the protocol's operation // The minimum tick value supported by the protocol // Corresponds to the minimum possible price ratio in the protocol int32 constant MIN_TICK = -88722835; // The maximum tick value supported by the protocol // Corresponds to the maximum possible price ratio in the protocol int32 constant MAX_TICK = 88722835; // The maximum tick magnitude (absolute value of MAX_TICK) // Used for validation and bounds checking in tick-related calculations uint32 constant MAX_TICK_MAGNITUDE = uint32(MAX_TICK); // The maximum allowed tick spacing for pools // Defines the upper limit for tick spacing configuration in pool creation uint32 constant MAX_TICK_SPACING = 698605; // Address used to represent the native token (ETH) within the protocol // Using address(0) allows the protocol to handle native ETH alongside ERC20 tokens address constant NATIVE_TOKEN_ADDRESS = address(0);
// SPDX-License-Identifier: ekubo-license-v1.eth
pragma solidity =0.8.33;
// A dynamic fixed point number (a la floating point) that stores a shifting 94 bit view of the underlying fixed point value,
// based on the most significant bits (mantissa)
// If the most significant 2 bits are 11, it represents a 64.30
// If the most significant 2 bits are 10, it represents a 32.62 number
// If the most significant 2 bits are 01, it represents a 0.94 number
// If the most significant 2 bits are 00, it represents a 0.126 number that is always less than 2**-32
type SqrtRatio is uint96;
uint96 constant MIN_SQRT_RATIO_RAW = 4611797791050542631;
SqrtRatio constant MIN_SQRT_RATIO = SqrtRatio.wrap(MIN_SQRT_RATIO_RAW);
uint96 constant MAX_SQRT_RATIO_RAW = 79227682466138141934206691491;
SqrtRatio constant MAX_SQRT_RATIO = SqrtRatio.wrap(MAX_SQRT_RATIO_RAW);
uint96 constant TWO_POW_95 = 0x800000000000000000000000;
uint96 constant TWO_POW_94 = 0x400000000000000000000000;
uint96 constant TWO_POW_62 = 0x4000000000000000;
uint96 constant TWO_POW_62_MINUS_ONE = 0x3fffffffffffffff;
uint96 constant BIT_MASK = 0xc00000000000000000000000; // TWO_POW_95 | TWO_POW_94
SqrtRatio constant ONE = SqrtRatio.wrap((TWO_POW_95) + (1 << 62));
using {
toFixed,
isValid,
ge as >=,
le as <=,
lt as <,
gt as >,
eq as ==,
neq as !=,
isZero,
min,
max
} for SqrtRatio global;
function isValid(SqrtRatio sqrtRatio) pure returns (bool r) {
assembly ("memory-safe") {
r := and(
// greater than or equal to TWO_POW_62, i.e. the whole number portion is nonzero
gt(and(sqrtRatio, not(BIT_MASK)), TWO_POW_62_MINUS_ONE),
// and between min/max sqrt ratio
and(iszero(lt(sqrtRatio, MIN_SQRT_RATIO_RAW)), iszero(gt(sqrtRatio, MAX_SQRT_RATIO_RAW)))
)
}
}
error ValueOverflowsSqrtRatioContainer();
// If passing a value greater than this constant with roundUp = true, toSqrtRatio will overflow
// For roundUp = false, the constant is type(uint192).max
uint256 constant MAX_FIXED_VALUE_ROUND_UP =
0x1000000000000000000000000000000000000000000000000 - 0x4000000000000000000000000;
// Converts a 64.128 value into the compact SqrtRatio representation
function toSqrtRatio(uint256 sqrtRatio, bool roundUp) pure returns (SqrtRatio r) {
assembly ("memory-safe") {
function compute(sr, ru) -> v {
// rup = 0x00...00 when false, 0xff...ff when true
let rup := sub(0, ru)
// Region: < 2**96 (shift = 2)
let addmask := and(0x3, rup) // (1<<s)-1 if ru
if lt(add(sr, addmask), shl(96, 1)) {
v := shr(2, add(sr, addmask))
leave
}
// Region: < 2**128 (shift = 34) + set bit 94
addmask := and(0x3ffffffff, rup)
if lt(add(sr, addmask), shl(128, 1)) {
v := or(shl(94, 1), shr(34, add(sr, addmask)))
leave
}
// Region: < 2**160 (shift = 66) + set bit 95
addmask := and(0x3ffffffffffffffff, rup)
if lt(add(sr, addmask), shl(160, 1)) {
v := or(shl(95, 1), shr(66, add(sr, addmask)))
leave
}
// Region: < 2**192 (shift = 98) + set bits 95|94
addmask := and(0x3ffffffffffffffffffffffff, rup)
if lt(add(sr, addmask), shl(192, 1)) {
v := or(shl(94, 3), shr(98, add(sr, addmask))) // 3<<94 == bit95|bit94
leave
}
// cast sig "ValueOverflowsSqrtRatioContainer()"
mstore(0, shl(224, 0xa10459f4))
revert(0, 4)
}
r := compute(sqrtRatio, roundUp)
}
}
// Returns the 64.128 representation of the given sqrt ratio
function toFixed(SqrtRatio sqrtRatio) pure returns (uint256 r) {
assembly ("memory-safe") {
r := shl(add(2, shr(89, and(sqrtRatio, BIT_MASK))), and(sqrtRatio, not(BIT_MASK)))
}
}
// The below operators assume that the SqrtRatio is valid, i.e. SqrtRatio#isValid returns true
function lt(SqrtRatio a, SqrtRatio b) pure returns (bool r) {
r = SqrtRatio.unwrap(a) < SqrtRatio.unwrap(b);
}
function gt(SqrtRatio a, SqrtRatio b) pure returns (bool r) {
r = SqrtRatio.unwrap(a) > SqrtRatio.unwrap(b);
}
function le(SqrtRatio a, SqrtRatio b) pure returns (bool r) {
r = SqrtRatio.unwrap(a) <= SqrtRatio.unwrap(b);
}
function ge(SqrtRatio a, SqrtRatio b) pure returns (bool r) {
r = SqrtRatio.unwrap(a) >= SqrtRatio.unwrap(b);
}
function eq(SqrtRatio a, SqrtRatio b) pure returns (bool r) {
r = SqrtRatio.unwrap(a) == SqrtRatio.unwrap(b);
}
function neq(SqrtRatio a, SqrtRatio b) pure returns (bool r) {
r = SqrtRatio.unwrap(a) != SqrtRatio.unwrap(b);
}
function isZero(SqrtRatio a) pure returns (bool r) {
assembly ("memory-safe") {
r := iszero(a)
}
}
function max(SqrtRatio a, SqrtRatio b) pure returns (SqrtRatio r) {
assembly ("memory-safe") {
r := xor(a, mul(xor(a, b), gt(b, a)))
}
}
function min(SqrtRatio a, SqrtRatio b) pure returns (SqrtRatio r) {
assembly ("memory-safe") {
r := xor(a, mul(xor(a, b), lt(b, a)))
}
}// SPDX-License-Identifier: ekubo-license-v1.eth
pragma solidity =0.8.33;
import {SqrtRatio} from "./sqrtRatio.sol";
type PoolState is bytes32;
using {sqrtRatio, tick, liquidity, isInitialized, parse} for PoolState global;
function sqrtRatio(PoolState state) pure returns (SqrtRatio r) {
assembly ("memory-safe") {
r := shr(160, state)
}
}
function tick(PoolState state) pure returns (int32 t) {
assembly ("memory-safe") {
t := signextend(3, shr(128, state))
}
}
function liquidity(PoolState state) pure returns (uint128 l) {
assembly ("memory-safe") {
l := shr(128, shl(128, state))
}
}
function isInitialized(PoolState state) pure returns (bool yes) {
assembly ("memory-safe") {
yes := iszero(iszero(state))
}
}
function parse(PoolState state) pure returns (SqrtRatio r, int32 t, uint128 l) {
assembly ("memory-safe") {
r := shr(160, state)
t := signextend(3, shr(128, state))
l := shr(128, shl(128, state))
}
}
function createPoolState(SqrtRatio _sqrtRatio, int32 _tick, uint128 _liquidity) pure returns (PoolState s) {
assembly ("memory-safe") {
// s = (sqrtRatio << 160) | (_tick << 128) | liquidity
s := or(shl(160, _sqrtRatio), or(shl(128, and(_tick, 0xFFFFFFFF)), shr(128, shl(128, _liquidity))))
}
}// SPDX-License-Identifier: ekubo-license-v1.eth
pragma solidity =0.8.33;
import {SqrtRatio, MIN_SQRT_RATIO_RAW, MAX_SQRT_RATIO_RAW} from "./sqrtRatio.sol";
type SwapParameters is bytes32;
using {
sqrtRatioLimit,
amount,
isToken1,
skipAhead,
isExactOut,
isPriceIncreasing,
withDefaultSqrtRatioLimit
} for SwapParameters global;
function sqrtRatioLimit(SwapParameters params) pure returns (SqrtRatio r) {
assembly ("memory-safe") {
r := shr(160, params)
}
}
function amount(SwapParameters params) pure returns (int128 a) {
assembly ("memory-safe") {
a := signextend(15, shr(32, params))
}
}
function isToken1(SwapParameters params) pure returns (bool t) {
assembly ("memory-safe") {
t := and(shr(31, params), 1)
}
}
function skipAhead(SwapParameters params) pure returns (uint256 s) {
assembly ("memory-safe") {
s := and(params, 0x7fffffff)
}
}
function createSwapParameters(SqrtRatio _sqrtRatioLimit, int128 _amount, bool _isToken1, uint256 _skipAhead)
pure
returns (SwapParameters p)
{
assembly ("memory-safe") {
// p = (sqrtRatioLimit << 160) | (amount << 32) | (isToken1 << 31) | skipAhead
// Mask each field to ensure dirty bits don't interfere
// For isToken1, use iszero(iszero()) to convert any non-zero value to 1
p := or(
shl(160, _sqrtRatioLimit),
or(
shl(32, and(_amount, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)),
or(shl(31, iszero(iszero(_isToken1))), and(_skipAhead, 0x7fffffff))
)
)
}
}
function isExactOut(SwapParameters params) pure returns (bool yes) {
assembly ("memory-safe") {
yes := and(shr(159, params), 1)
}
}
function isPriceIncreasing(SwapParameters params) pure returns (bool yes) {
bool _isExactOut = params.isExactOut();
bool _isToken1 = params.isToken1();
assembly ("memory-safe") {
yes := xor(_isExactOut, _isToken1)
}
}
function withDefaultSqrtRatioLimit(SwapParameters params) pure returns (SwapParameters updated) {
bool increasing = params.isPriceIncreasing();
assembly ("memory-safe") {
let replace := iszero(shr(160, params))
let orMask :=
shl(160, mul(replace, or(mul(increasing, MAX_SQRT_RATIO_RAW), mul(iszero(increasing), MIN_SQRT_RATIO_RAW))))
updated := or(orMask, params)
}
}// SPDX-License-Identifier: ekubo-license-v1.eth
pragma solidity =0.8.33;
type TickInfo is bytes32;
using {liquidityDelta, liquidityNet, parse} for TickInfo global;
function liquidityDelta(TickInfo info) pure returns (int128 delta) {
assembly ("memory-safe") {
delta := signextend(15, info)
}
}
function liquidityNet(TickInfo info) pure returns (uint128 net) {
assembly ("memory-safe") {
net := shr(128, info)
}
}
function parse(TickInfo info) pure returns (int128 delta, uint128 net) {
assembly ("memory-safe") {
delta := signextend(15, info)
net := shr(128, info)
}
}
function createTickInfo(int128 _liquidityDelta, uint128 _liquidityNet) pure returns (TickInfo info) {
assembly ("memory-safe") {
// info = (liquidityNet << 128) | liquidityDelta
info := or(shl(128, _liquidityNet), shr(128, shl(128, _liquidityDelta)))
}
}// SPDX-License-Identifier: ekubo-license-v1.eth
pragma solidity =0.8.33;
type PoolBalanceUpdate is bytes32;
using {delta0, delta1} for PoolBalanceUpdate global;
function delta0(PoolBalanceUpdate update) pure returns (int128 v) {
assembly ("memory-safe") {
v := signextend(15, shr(128, update))
}
}
function delta1(PoolBalanceUpdate update) pure returns (int128 v) {
assembly ("memory-safe") {
v := signextend(15, update)
}
}
function createPoolBalanceUpdate(int128 _delta0, int128 _delta1) pure returns (PoolBalanceUpdate update) {
assembly ("memory-safe") {
// update = (delta0 << 128) | delta1
update := or(shl(128, _delta0), and(_delta1, 0xffffffffffffffffffffffffffffffff))
}
}// SPDX-License-Identifier: ekubo-license-v1.eth pragma solidity =0.8.33; /// @notice Unique identifier for a pool /// @dev Wraps bytes32 to provide type safety for pool identifiers type PoolId is bytes32;
// SPDX-License-Identifier: ekubo-license-v1.eth
pragma solidity =0.8.33;
type Locker is bytes32;
using {id, addr, parse} for Locker global;
function id(Locker locker) pure returns (uint256 v) {
assembly ("memory-safe") {
v := sub(shr(160, locker), 1)
}
}
function addr(Locker locker) pure returns (address v) {
assembly ("memory-safe") {
v := shr(96, shl(96, locker))
}
}
function parse(Locker locker) pure returns (uint256 lockerId, address lockerAddr) {
assembly ("memory-safe") {
lockerId := sub(shr(160, locker), 1)
lockerAddr := shr(96, shl(96, locker))
}
}// SPDX-License-Identifier: ekubo-license-v1.eth
pragma solidity =0.8.33;
// Returns the fee to charge based on the amount, which is the fee (a 0.64 number) times the
// amount, rounded up
function computeFee(uint128 amount, uint64 fee) pure returns (uint128 result) {
assembly ("memory-safe") {
result := shr(64, add(mul(amount, fee), 0xffffffffffffffff))
}
}
error AmountBeforeFeeOverflow();
// Returns the amount before the fee is applied, which is the amount minus the fee, rounded up
function amountBeforeFee(uint128 afterFee, uint64 fee) pure returns (uint128 result) {
assembly ("memory-safe") {
let v := shl(64, afterFee)
let d := sub(0x10000000000000000, fee)
result := add(iszero(iszero(mod(v, d))), div(v, d))
if shr(128, result) {
mstore(0, 0x0d88f526)
revert(0x1c, 0x04)
}
}
}// SPDX-License-Identifier: ekubo-license-v1.eth
pragma solidity =0.8.33;
import {FixedPointMathLib} from "solady/utils/FixedPointMathLib.sol";
import {SqrtRatio, toSqrtRatio, MAX_FIXED_VALUE_ROUND_UP} from "../types/sqrtRatio.sol";
// Compute the next ratio from a delta amount0, always rounded towards starting price for input, and
// away from starting price for output
/// @dev Assumes sqrt ratio and liquidity are non-zero
function nextSqrtRatioFromAmount0(SqrtRatio _sqrtRatio, uint128 liquidity, int128 amount)
pure
returns (SqrtRatio sqrtRatioNext)
{
if (amount == 0) {
return _sqrtRatio;
}
uint256 sqrtRatio = _sqrtRatio.toFixed();
uint256 liquidityX128;
assembly ("memory-safe") {
liquidityX128 := shl(128, liquidity)
}
if (amount < 0) {
uint256 amountAbs;
assembly ("memory-safe") {
amountAbs := sub(0, amount)
}
unchecked {
// multiplication will revert on overflow, so we return the maximum value for the type
if (amountAbs > FixedPointMathLib.rawDiv(type(uint256).max, sqrtRatio)) {
return SqrtRatio.wrap(type(uint96).max);
}
uint256 product = sqrtRatio * amountAbs;
// again it will overflow if this is the case, so return the max value
if (product >= liquidityX128) {
return SqrtRatio.wrap(type(uint96).max);
}
uint256 denominator = liquidityX128 - product;
uint256 resultFixed = FixedPointMathLib.fullMulDivUp(liquidityX128, sqrtRatio, denominator);
if (resultFixed > MAX_FIXED_VALUE_ROUND_UP) {
return SqrtRatio.wrap(type(uint96).max);
}
sqrtRatioNext = toSqrtRatio(resultFixed, true);
}
} else {
uint256 sqrtRatioRaw;
assembly ("memory-safe") {
// this can never overflow, amountAbs is limited to 2**128-1 and liquidityX128 / sqrtRatio is limited to (2**128-1 << 128)
// adding the 2 values can at most equal type(uint256).max
let denominator := add(div(liquidityX128, sqrtRatio), amount)
sqrtRatioRaw := add(div(liquidityX128, denominator), iszero(iszero(mod(liquidityX128, denominator))))
}
sqrtRatioNext = toSqrtRatio(sqrtRatioRaw, true);
}
}
/// @dev Assumes liquidity is non-zero
function nextSqrtRatioFromAmount1(SqrtRatio _sqrtRatio, uint128 liquidity, int128 amount)
pure
returns (SqrtRatio sqrtRatioNext)
{
uint256 sqrtRatio = _sqrtRatio.toFixed();
unchecked {
uint256 liquidityU256;
assembly ("memory-safe") {
liquidityU256 := liquidity
}
if (amount < 0) {
uint256 quotient;
assembly ("memory-safe") {
let numerator := shl(128, sub(0, amount))
quotient := add(div(numerator, liquidityU256), iszero(iszero(mod(numerator, liquidityU256))))
}
uint256 sqrtRatioNextFixed = FixedPointMathLib.zeroFloorSub(sqrtRatio, quotient);
sqrtRatioNext = toSqrtRatio(sqrtRatioNextFixed, false);
} else {
uint256 quotient;
assembly ("memory-safe") {
quotient := div(shl(128, amount), liquidityU256)
}
uint256 sum = sqrtRatio + quotient;
if (sum < sqrtRatio || sum > type(uint192).max) {
return SqrtRatio.wrap(type(uint96).max);
}
sqrtRatioNext = toSqrtRatio(sum, false);
}
}
}// SPDX-License-Identifier: ekubo-license-v1.eth
pragma solidity =0.8.33;
import {FixedPointMathLib} from "solady/utils/FixedPointMathLib.sol";
import {SqrtRatio} from "../types/sqrtRatio.sol";
error Amount0DeltaOverflow();
error Amount1DeltaOverflow();
function sortAndConvertToFixedSqrtRatios(SqrtRatio sqrtRatioA, SqrtRatio sqrtRatioB)
pure
returns (uint256 sqrtRatioLower, uint256 sqrtRatioUpper)
{
sqrtRatioLower = sqrtRatioA.toFixed();
sqrtRatioUpper = sqrtRatioB.toFixed();
assembly ("memory-safe") {
let diff := mul(sub(sqrtRatioLower, sqrtRatioUpper), gt(sqrtRatioLower, sqrtRatioUpper))
sqrtRatioLower := sub(sqrtRatioLower, diff)
sqrtRatioUpper := add(sqrtRatioUpper, diff)
}
}
/// @dev Assumes that the sqrt ratios are valid
function amount0Delta(SqrtRatio sqrtRatioA, SqrtRatio sqrtRatioB, uint128 liquidity, bool roundUp)
pure
returns (uint128 amount0)
{
(uint256 sqrtRatioLower, uint256 sqrtRatioUpper) = sortAndConvertToFixedSqrtRatios(sqrtRatioA, sqrtRatioB);
amount0 = amount0DeltaSorted(sqrtRatioLower, sqrtRatioUpper, liquidity, roundUp);
}
/// @dev Assumes that the sqrt ratios are non-zero and sorted
function amount0DeltaSorted(uint256 sqrtRatioLower, uint256 sqrtRatioUpper, uint128 liquidity, bool roundUp)
pure
returns (uint128 amount0)
{
unchecked {
uint256 liquidityX128;
assembly ("memory-safe") {
liquidityX128 := shl(128, liquidity)
}
if (roundUp) {
uint256 result0 =
FixedPointMathLib.fullMulDivUp(liquidityX128, (sqrtRatioUpper - sqrtRatioLower), sqrtRatioUpper);
assembly ("memory-safe") {
let result := add(div(result0, sqrtRatioLower), iszero(iszero(mod(result0, sqrtRatioLower))))
if shr(128, result) {
// cast sig "Amount0DeltaOverflow()"
mstore(0, 0xb4ef2546)
revert(0x1c, 0x04)
}
amount0 := result
}
} else {
uint256 result0 =
FixedPointMathLib.fullMulDivUnchecked(liquidityX128, (sqrtRatioUpper - sqrtRatioLower), sqrtRatioUpper);
uint256 result = FixedPointMathLib.rawDiv(result0, sqrtRatioLower);
assembly ("memory-safe") {
if shr(128, result) {
// cast sig "Amount0DeltaOverflow()"
mstore(0, 0xb4ef2546)
revert(0x1c, 0x04)
}
amount0 := result
}
}
}
}
/// @dev Assumes that the sqrt ratios are valid
function amount1Delta(SqrtRatio sqrtRatioA, SqrtRatio sqrtRatioB, uint128 liquidity, bool roundUp)
pure
returns (uint128 amount1)
{
(uint256 sqrtRatioLower, uint256 sqrtRatioUpper) = sortAndConvertToFixedSqrtRatios(sqrtRatioA, sqrtRatioB);
amount1 = amount1DeltaSorted(sqrtRatioLower, sqrtRatioUpper, liquidity, roundUp);
}
function amount1DeltaSorted(uint256 sqrtRatioLower, uint256 sqrtRatioUpper, uint128 liquidity, bool roundUp)
pure
returns (uint128 amount1)
{
unchecked {
uint256 difference = sqrtRatioUpper - sqrtRatioLower;
uint256 liquidityU256;
assembly ("memory-safe") {
liquidityU256 := liquidity
}
if (roundUp) {
uint256 result = FixedPointMathLib.fullMulDivN(difference, liquidityU256, 128);
assembly ("memory-safe") {
// addition is safe from overflow because the result of fullMulDivN will never equal type(uint256).max
result := add(
result,
iszero(iszero(mulmod(difference, liquidityU256, 0x100000000000000000000000000000000)))
)
if shr(128, result) {
// cast sig "Amount1DeltaOverflow()"
mstore(0, 0x59d2b24a)
revert(0x1c, 0x04)
}
amount1 := result
}
} else {
uint256 result = FixedPointMathLib.fullMulDivN(difference, liquidityU256, 128);
assembly ("memory-safe") {
if shr(128, result) {
// cast sig "Amount1DeltaOverflow()"
mstore(0, 0x59d2b24a)
revert(0x1c, 0x04)
}
amount1 := result
}
}
}
}// SPDX-License-Identifier: ekubo-license-v1.eth
pragma solidity =0.8.33;
type StorageSlot is bytes32;
using {load, loadTwo, store, storeTwo, next, add, sub} for StorageSlot global;
function load(StorageSlot slot) view returns (bytes32 value) {
assembly ("memory-safe") {
value := sload(slot)
}
}
function loadTwo(StorageSlot slot) view returns (bytes32 value0, bytes32 value1) {
value0 = slot.load();
value1 = slot.next().load();
}
function store(StorageSlot slot, bytes32 value) {
assembly ("memory-safe") {
sstore(slot, value)
}
}
function storeTwo(StorageSlot slot, bytes32 value0, bytes32 value1) {
slot.store(value0);
slot.next().store(value1);
}
function next(StorageSlot slot) pure returns (StorageSlot nextSlot) {
assembly ("memory-safe") {
nextSlot := add(slot, 1)
}
}
function add(StorageSlot slot, uint256 addend) pure returns (StorageSlot summedSlot) {
assembly ("memory-safe") {
summedSlot := add(slot, addend)
}
}
function sub(StorageSlot slot, uint256 subtrahend) pure returns (StorageSlot differenceSlot) {
assembly ("memory-safe") {
differenceSlot := sub(slot, subtrahend)
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Library for bit twiddling and boolean operations.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibBit.sol)
/// @author Inspired by (https://graphics.stanford.edu/~seander/bithacks.html)
library LibBit {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* BIT TWIDDLING OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Find last set.
/// Returns the index of the most significant bit of `x`,
/// counting from the least significant bit position.
/// If `x` is zero, returns 256.
function fls(uint256 x) internal pure returns (uint256 r) {
/// @solidity memory-safe-assembly
assembly {
r := or(shl(8, iszero(x)), shl(7, lt(0xffffffffffffffffffffffffffffffff, x)))
r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
r := or(r, shl(4, lt(0xffff, shr(r, x))))
r := or(r, shl(3, lt(0xff, shr(r, x))))
// forgefmt: disable-next-item
r := or(r, byte(and(0x1f, shr(shr(r, x), 0x8421084210842108cc6318c6db6d54be)),
0x0706060506020504060203020504030106050205030304010505030400000000))
}
}
/// @dev Count leading zeros.
/// Returns the number of zeros preceding the most significant one bit.
/// If `x` is zero, returns 256.
function clz(uint256 x) internal pure returns (uint256 r) {
/// @solidity memory-safe-assembly
assembly {
r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x))
r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
r := or(r, shl(4, lt(0xffff, shr(r, x))))
r := or(r, shl(3, lt(0xff, shr(r, x))))
// forgefmt: disable-next-item
r := add(xor(r, byte(and(0x1f, shr(shr(r, x), 0x8421084210842108cc6318c6db6d54be)),
0xf8f9f9faf9fdfafbf9fdfcfdfafbfcfef9fafdfafcfcfbfefafafcfbffffffff)), iszero(x))
}
}
/// @dev Find first set.
/// Returns the index of the least significant bit of `x`,
/// counting from the least significant bit position.
/// If `x` is zero, returns 256.
/// Equivalent to `ctz` (count trailing zeros), which gives
/// the number of zeros following the least significant one bit.
function ffs(uint256 x) internal pure returns (uint256 r) {
/// @solidity memory-safe-assembly
assembly {
// Isolate the least significant bit.
x := and(x, add(not(x), 1))
// For the upper 3 bits of the result, use a De Bruijn-like lookup.
// Credit to adhusson: https://blog.adhusson.com/cheap-find-first-set-evm/
// forgefmt: disable-next-item
r := shl(5, shr(252, shl(shl(2, shr(250, mul(x,
0xb6db6db6ddddddddd34d34d349249249210842108c6318c639ce739cffffffff))),
0x8040405543005266443200005020610674053026020000107506200176117077)))
// For the lower 5 bits of the result, use a De Bruijn lookup.
// forgefmt: disable-next-item
r := or(r, byte(and(div(0xd76453e0, shr(r, x)), 0x1f),
0x001f0d1e100c1d070f090b19131c1706010e11080a1a141802121b1503160405))
}
}
/// @dev Returns the number of set bits in `x`.
function popCount(uint256 x) internal pure returns (uint256 c) {
/// @solidity memory-safe-assembly
assembly {
let max := not(0)
let isMax := eq(x, max)
x := sub(x, and(shr(1, x), div(max, 3)))
x := add(and(x, div(max, 5)), and(shr(2, x), div(max, 5)))
x := and(add(x, shr(4, x)), div(max, 17))
c := or(shl(8, isMax), shr(248, mul(x, div(max, 255))))
}
}
/// @dev Returns the number of zero bytes in `x`.
/// To get the number of non-zero bytes, simply do `32 - countZeroBytes(x)`.
function countZeroBytes(uint256 x) internal pure returns (uint256 c) {
/// @solidity memory-safe-assembly
assembly {
let m := 0x7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f
c := byte(0, mul(shr(7, not(m)), shr(7, not(or(or(add(and(x, m), m), x), m)))))
}
}
/// @dev Returns the number of zero bytes in `s`.
/// To get the number of non-zero bytes, simply do `s.length - countZeroBytes(s)`.
function countZeroBytes(bytes memory s) internal pure returns (uint256 c) {
/// @solidity memory-safe-assembly
assembly {
function czb(x_) -> _c {
let _m := 0x7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f
_c := shr(7, not(or(or(add(and(x_, _m), _m), x_), _m)))
_c := byte(0, mul(shr(7, not(_m)), _c))
}
let n := mload(s)
let l := shl(5, shr(5, n))
s := add(s, 0x20)
for { let i } xor(i, l) { i := add(i, 0x20) } { c := add(czb(mload(add(s, i))), c) }
if lt(l, n) { c := add(czb(or(shr(shl(3, sub(n, l)), not(0)), mload(add(s, l)))), c) }
}
}
/// @dev Returns the number of zero bytes in `s`.
/// To get the number of non-zero bytes, simply do `s.length - countZeroBytes(s)`.
function countZeroBytesCalldata(bytes calldata s) internal pure returns (uint256 c) {
/// @solidity memory-safe-assembly
assembly {
function czb(x_) -> _c {
let _m := 0x7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f
_c := shr(7, not(or(or(add(and(x_, _m), _m), x_), _m)))
_c := byte(0, mul(shr(7, not(_m)), _c))
}
let l := shl(5, shr(5, s.length))
for { let i } xor(i, l) { i := add(i, 0x20) } {
c := add(czb(calldataload(add(s.offset, i))), c)
}
if lt(l, s.length) {
let m := shr(shl(3, sub(s.length, l)), not(0))
c := add(czb(or(m, calldataload(add(s.offset, l)))), c)
}
}
}
/// @dev Returns whether `x` is a power of 2.
function isPo2(uint256 x) internal pure returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
// Equivalent to `x && !(x & (x - 1))`.
result := iszero(add(and(x, sub(x, 1)), iszero(x)))
}
}
/// @dev Returns `x` reversed at the bit level.
function reverseBits(uint256 x) internal pure returns (uint256 r) {
uint256 m0 = 0x0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f;
uint256 m1 = m0 ^ (m0 << 2);
uint256 m2 = m1 ^ (m1 << 1);
r = reverseBytes(x);
r = (m2 & (r >> 1)) | ((m2 & r) << 1);
r = (m1 & (r >> 2)) | ((m1 & r) << 2);
r = (m0 & (r >> 4)) | ((m0 & r) << 4);
}
/// @dev Returns `x` reversed at the byte level.
function reverseBytes(uint256 x) internal pure returns (uint256 r) {
unchecked {
// Computing masks on-the-fly reduces bytecode size by about 200 bytes.
uint256 m0 = 0x100000000000000000000000000000001 * (~toUint(x == uint256(0)) >> 192);
uint256 m1 = m0 ^ (m0 << 32);
uint256 m2 = m1 ^ (m1 << 16);
uint256 m3 = m2 ^ (m2 << 8);
r = (m3 & (x >> 8)) | ((m3 & x) << 8);
r = (m2 & (r >> 16)) | ((m2 & r) << 16);
r = (m1 & (r >> 32)) | ((m1 & r) << 32);
r = (m0 & (r >> 64)) | ((m0 & r) << 64);
r = (r >> 128) | (r << 128);
}
}
/// @dev Returns the common prefix of `x` and `y` at the bit level.
function commonBitPrefix(uint256 x, uint256 y) internal pure returns (uint256) {
unchecked {
uint256 s = 256 - clz(x ^ y);
return (x >> s) << s;
}
}
/// @dev Returns the common prefix of `x` and `y` at the nibble level.
function commonNibblePrefix(uint256 x, uint256 y) internal pure returns (uint256) {
unchecked {
uint256 s = (64 - (clz(x ^ y) >> 2)) << 2;
return (x >> s) << s;
}
}
/// @dev Returns the common prefix of `x` and `y` at the byte level.
function commonBytePrefix(uint256 x, uint256 y) internal pure returns (uint256) {
unchecked {
uint256 s = (32 - (clz(x ^ y) >> 3)) << 3;
return (x >> s) << s;
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* BOOLEAN OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
// A Solidity bool on the stack or memory is represented as a 256-bit word.
// Non-zero values are true, zero is false.
// A clean bool is either 0 (false) or 1 (true) under the hood.
// Usually, if not always, the bool result of a regular Solidity expression,
// or the argument of a public/external function will be a clean bool.
// You can usually use the raw variants for more performance.
// If uncertain, test (best with exact compiler settings).
// Or use the non-raw variants (compiler can sometimes optimize out the double `iszero`s).
/// @dev Returns `x & y`. Inputs must be clean.
function rawAnd(bool x, bool y) internal pure returns (bool z) {
/// @solidity memory-safe-assembly
assembly {
z := and(x, y)
}
}
/// @dev Returns `x & y`.
function and(bool x, bool y) internal pure returns (bool z) {
/// @solidity memory-safe-assembly
assembly {
z := and(iszero(iszero(x)), iszero(iszero(y)))
}
}
/// @dev Returns `w & x & y`.
function and(bool w, bool x, bool y) internal pure returns (bool z) {
/// @solidity memory-safe-assembly
assembly {
z := iszero(or(iszero(w), or(iszero(x), iszero(y))))
}
}
/// @dev Returns `v & w & x & y`.
function and(bool v, bool w, bool x, bool y) internal pure returns (bool z) {
/// @solidity memory-safe-assembly
assembly {
z := iszero(or(or(iszero(v), iszero(w)), or(iszero(x), iszero(y))))
}
}
/// @dev Returns `x | y`. Inputs must be clean.
function rawOr(bool x, bool y) internal pure returns (bool z) {
/// @solidity memory-safe-assembly
assembly {
z := or(x, y)
}
}
/// @dev Returns `x | y`.
function or(bool x, bool y) internal pure returns (bool z) {
/// @solidity memory-safe-assembly
assembly {
z := iszero(iszero(or(x, y)))
}
}
/// @dev Returns `w | x | y`.
function or(bool w, bool x, bool y) internal pure returns (bool z) {
/// @solidity memory-safe-assembly
assembly {
z := iszero(iszero(or(w, or(x, y))))
}
}
/// @dev Returns `v | w | x | y`.
function or(bool v, bool w, bool x, bool y) internal pure returns (bool z) {
/// @solidity memory-safe-assembly
assembly {
z := iszero(iszero(or(v, or(w, or(x, y)))))
}
}
/// @dev Returns 1 if `b` is true, else 0. Input must be clean.
function rawToUint(bool b) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := b
}
}
/// @dev Returns 1 if `b` is true, else 0.
function toUint(bool b) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := iszero(iszero(b))
}
}
}// SPDX-License-Identifier: ekubo-license-v1.eth
pragma solidity ^0.8.0;
/// @title IExposedStorage
/// @notice Interface for exposing contract storage via view methods
/// @dev This interface provides a way to access specific pieces of state in the inheriting contract.
/// It serves as a workaround in the absence of EIP-2330 (https://eips.ethereum.org/EIPS/eip-2330)
/// which would provide native support for exposing contract storage.
interface IExposedStorage {
/// @notice Loads storage slots from the contract's persistent storage
/// @dev Reads each 32-byte slot specified in the calldata (after the function selector) from storage
/// and returns all the loaded values concatenated together.
function sload() external view;
/// @notice Loads storage slots from the contract's transient storage
/// @dev Reads each 32-byte slot specified in the calldata (after the function selector) from transient storage
/// and returns all the loaded values concatenated together. Transient storage is cleared at the end
/// of each transaction.
function tload() external view;
}// SPDX-License-Identifier: ekubo-license-v1.eth
pragma solidity =0.8.33;
/**
* @title Bitmap (256-bit)
* @notice Lightweight helpers for treating a `uint256` as a 256-bit bitmap.
* @dev
* - Bit indices are in the range [0, 255].
* - All operations are O(1) and implemented with memory-safe assembly.
* - For search helpers `leSetBit` and `geSetBit`, the return value is
* one-based: it returns `index + 1` of the matching set bit, or `0` if none.
* This convention avoids the need for sentinels outside the 0..255 range.
*/
type Bitmap is uint256;
using {toggle, isSet, leSetBit, geSetBit} for Bitmap global;
/**
* @notice Toggle (flip) the bit at `index` in `bitmap`.
* @param bitmap The current bitmap value.
* @param index Bit position to toggle, in [0, 255].
* @return result A new bitmap with the bit at `index` flipped.
*/
function toggle(Bitmap bitmap, uint8 index) pure returns (Bitmap result) {
assembly ("memory-safe") {
result := xor(bitmap, shl(index, 1))
}
}
/**
* @notice Check whether the bit at `index` is set.
* @param bitmap The bitmap to read.
* @param index Bit position to test, in [0, 255].
* @return yes True if the bit is 1, false otherwise.
*/
function isSet(Bitmap bitmap, uint8 index) pure returns (bool yes) {
assembly ("memory-safe") {
yes := and(shr(index, bitmap), 1)
}
}
/**
* @notice Find the most significant set bit at or below `index`.
* @dev Returns one-based position: `pos = bitIndex + 1`, or `0` if none.
* Example: if bit 9 is set and `index >= 9`, returns `10`.
* For `index == 255`, the mask spans all bits (wraparound yields `2^256-1`).
* @param bitmap The bitmap to search.
* @param index Upper bound (inclusive) for the search, in [0, 255].
* @return v One-based position of MSB found (bitIndex + 1), or 0 if none.
*/
function leSetBit(Bitmap bitmap, uint8 index) pure returns (uint256 v) {
unchecked {
assembly ("memory-safe") {
let masked := and(bitmap, sub(shl(add(index, 1), 1), 1))
v := sub(256, clz(masked))
}
}
}
/**
* @notice Find the least significant set bit at or above `index`.
* @dev Returns one-based position: `pos = bitIndex + 1`, or `0` if none.
* Example: if bit 9 is set and `index <= 9`, returns `10`.
* @param bitmap The bitmap to search.
* @param index Lower bound (inclusive) for the search, in [0, 255].
* @return v One-based position of LSB found (bitIndex + 1), or 0 if none.
*/
function geSetBit(Bitmap bitmap, uint8 index) pure returns (uint256 v) {
assembly ("memory-safe") {
let masked := and(bitmap, not(sub(shl(index, 1), 1)))
v := sub(256, clz(and(masked, sub(0, masked))))
}
}// SPDX-License-Identifier: ekubo-license-v1.eth
pragma solidity ^0.8.0;
import {Locker} from "../types/locker.sol";
interface ILocker {
function locked_6416899205(uint256 id) external;
}
interface IForwardee {
function forwarded_2374103877(Locker original) external;
}
/// @title IFlashAccountant
/// @notice Interface for flash loan accounting functionality using transient storage
/// @dev This interface manages debt tracking for flash loans, allowing users to borrow tokens temporarily
/// and ensuring all debts are settled before the transaction completes. Uses transient storage
/// for gas-efficient temporary state management within a single transaction.
interface IFlashAccountant {
/// @notice Thrown when an operation is made that affects debts without an active lock
error NotLocked();
/// @notice Thrown when a method is called by an address other than the current locker
error LockerOnly();
/// @notice Thrown when the lock callback returns without clearing all the debts either via withdrawing from or paying to the accountant
error DebtsNotZeroed(uint256 id);
/// @notice Thrown if the contract receives a payment that exceeds type(uint128).max
error PaymentOverflow();
/// @notice Thrown if the contract receives a call to updateDebt that has a data length != 20
error UpdateDebtMessageLength();
/// @notice Creates a lock context and calls back to the caller's locked function
/// @dev The entrypoint for all operations on the core contract. Any data passed after the
/// function signature is passed through back to the caller after the locked function
/// signature and data, with no additional encoding. Any data returned from ILocker#locked
/// is also returned from this function exactly as is. Reverts are bubbled up.
/// Ensures all debts are zeroed before completing the lock.
function lock() external;
/// @notice Forwards the lock context to another actor, allowing them to act on the original locker's debt
/// @dev Temporarily changes the locker to the forwarded address for the duration of the forwarded call.
/// Any additional calldata is passed through to the forwardee with no additional encoding.
/// Any data returned from IForwardee#forwarded is returned exactly as is. Reverts are bubbled up.
/// @param to The address to forward the lock context to
function forward(address to) external;
/// @notice Initiates a payment operation by recording current token balances
/// @dev To make a payment to core, you must first call startPayments with all the tokens you'd like to send.
/// All the tokens that will be paid must be ABI-encoded immediately after the 4 byte function selector.
/// This function stores the current balance + 1 for each token to distinguish between zero balance
/// and uninitialized state. Returns the current balances of all specified tokens as ABI-encoded
/// raw bytes via assembly (no explicit Solidity return type).
function startPayments() external;
/// @notice Completes a payment operation by calculating and crediting token payments
/// @dev After tokens have been transferred, call completePayments to be credited for the tokens
/// that have been paid to core. The credit goes to the current locker. Compares current
/// balances with those recorded in startPayments to determine payment amounts.
/// The computed payments are applied to the current locker's debt.
/// Returns packed uint128 payment amounts (16 bytes each) in the same order as the tokens.
function completePayments() external;
/// @notice Withdraws tokens from the accountant to recipients using packed calldata
/// @dev The contract must be locked, as it tracks withdrawn amounts against the current locker's debt.
/// Calldata format: each withdrawal is 56 bytes: token (20) + recipient (20) + amount (16)
/// For native tokens, uses the NATIVE_TOKEN_ADDRESS constant and transfers ETH directly.
function withdraw() external;
/// @notice Updates debt for the current locker and for the token at the calling address
/// @dev This is for deeply-integrated tokens that allow flash operations via the accountant.
/// The calling address is treated as the token address.
/// @dev The debt change argument is an int128 encoded immediately after the selector.
/// @dev The calldata length must be exactly 20 bytes in order to avoid this being called unintentionally.
function updateDebt() external;
/// @notice Receives ETH payments and credits them against the current locker's native token debt
/// @dev This contract can receive ETH as a payment. The received amount is credited as a negative
/// debt change for the native token. Note: because we use msg.value here, this contract can
/// never be multicallable, i.e. it should never expose the ability to delegatecall itself
/// more than once in a single call.
receive() external payable;
}{
"remappings": [
"forge-std/=lib/forge-std/src/",
"solady/=lib/solady/src/"
],
"optimizer": {
"enabled": true,
"runs": 9999999
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "ipfs",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "osaka",
"viaIR": true
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[],"name":"BoundsOrder","type":"error"},{"inputs":[],"name":"BoundsTickSpacing","type":"error"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"DebtsNotZeroed","type":"error"},{"inputs":[],"name":"ExtensionAlreadyRegistered","type":"error"},{"inputs":[],"name":"ExtensionNotRegistered","type":"error"},{"inputs":[],"name":"FailedRegisterInvalidCallPoints","type":"error"},{"inputs":[],"name":"InvalidCenterTick","type":"error"},{"inputs":[],"name":"InvalidSqrtRatioLimit","type":"error"},{"inputs":[],"name":"InvalidStableswapAmplification","type":"error"},{"inputs":[{"internalType":"int32","name":"tick","type":"int32"}],"name":"InvalidTick","type":"error"},{"inputs":[],"name":"InvalidTickSpacing","type":"error"},{"inputs":[],"name":"LockerOnly","type":"error"},{"inputs":[{"internalType":"int32","name":"tick","type":"int32"},{"internalType":"uint128","name":"liquidityNet","type":"uint128"},{"internalType":"uint128","name":"maxLiquidityPerTick","type":"uint128"}],"name":"MaxLiquidityPerTickExceeded","type":"error"},{"inputs":[],"name":"MinMaxBounds","type":"error"},{"inputs":[],"name":"NotLocked","type":"error"},{"inputs":[],"name":"PaymentOverflow","type":"error"},{"inputs":[],"name":"PoolAlreadyInitialized","type":"error"},{"inputs":[],"name":"PoolNotInitialized","type":"error"},{"inputs":[],"name":"SavedBalanceOverflow","type":"error"},{"inputs":[],"name":"SavedBalanceTokensNotSorted","type":"error"},{"inputs":[],"name":"SqrtRatioLimitOutOfRange","type":"error"},{"inputs":[],"name":"SqrtRatioLimitWrongDirection","type":"error"},{"inputs":[],"name":"StableswapMustBeFullRange","type":"error"},{"inputs":[],"name":"TokensMustBeSorted","type":"error"},{"inputs":[],"name":"UpdateDebtMessageLength","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"extension","type":"address"}],"name":"ExtensionRegistered","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"PoolId","name":"poolId","type":"bytes32"},{"indexed":false,"internalType":"uint128","name":"amount0","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"amount1","type":"uint128"}],"name":"FeesAccumulated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"PoolId","name":"poolId","type":"bytes32"},{"components":[{"internalType":"address","name":"token0","type":"address"},{"internalType":"address","name":"token1","type":"address"},{"internalType":"PoolConfig","name":"config","type":"bytes32"}],"indexed":false,"internalType":"struct PoolKey","name":"poolKey","type":"tuple"},{"indexed":false,"internalType":"int32","name":"tick","type":"int32"},{"indexed":false,"internalType":"SqrtRatio","name":"sqrtRatio","type":"uint96"}],"name":"PoolInitialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"locker","type":"address"},{"indexed":false,"internalType":"PoolId","name":"poolId","type":"bytes32"},{"indexed":false,"internalType":"PositionId","name":"positionId","type":"bytes32"},{"indexed":false,"internalType":"uint128","name":"amount0","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"amount1","type":"uint128"}],"name":"PositionFeesCollected","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"locker","type":"address"},{"indexed":false,"internalType":"PoolId","name":"poolId","type":"bytes32"},{"indexed":false,"internalType":"PositionId","name":"positionId","type":"bytes32"},{"indexed":false,"internalType":"int128","name":"liquidityDelta","type":"int128"},{"indexed":false,"internalType":"PoolBalanceUpdate","name":"balanceUpdate","type":"bytes32"},{"indexed":false,"internalType":"PoolState","name":"stateAfter","type":"bytes32"}],"name":"PositionUpdated","type":"event"},{"inputs":[{"components":[{"internalType":"address","name":"token0","type":"address"},{"internalType":"address","name":"token1","type":"address"},{"internalType":"PoolConfig","name":"config","type":"bytes32"}],"internalType":"struct PoolKey","name":"poolKey","type":"tuple"},{"internalType":"uint128","name":"_amount0","type":"uint128"},{"internalType":"uint128","name":"_amount1","type":"uint128"}],"name":"accumulateAsFees","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"token0","type":"address"},{"internalType":"address","name":"token1","type":"address"},{"internalType":"PoolConfig","name":"config","type":"bytes32"}],"internalType":"struct PoolKey","name":"poolKey","type":"tuple"},{"internalType":"PositionId","name":"positionId","type":"bytes32"}],"name":"collectFees","outputs":[{"internalType":"uint128","name":"amount0","type":"uint128"},{"internalType":"uint128","name":"amount1","type":"uint128"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"completePayments","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"forward","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"PoolId","name":"poolId","type":"bytes32"},{"internalType":"int32","name":"tickLower","type":"int32"},{"internalType":"int32","name":"tickUpper","type":"int32"}],"name":"getPoolFeesPerLiquidityInside","outputs":[{"components":[{"internalType":"uint256","name":"value0","type":"uint256"},{"internalType":"uint256","name":"value1","type":"uint256"}],"internalType":"struct FeesPerLiquidity","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"token0","type":"address"},{"internalType":"address","name":"token1","type":"address"},{"internalType":"PoolConfig","name":"config","type":"bytes32"}],"internalType":"struct PoolKey","name":"poolKey","type":"tuple"},{"internalType":"int32","name":"tick","type":"int32"}],"name":"initializePool","outputs":[{"internalType":"SqrtRatio","name":"sqrtRatio","type":"uint96"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"lock","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"PoolId","name":"poolId","type":"bytes32"},{"internalType":"int32","name":"fromTick","type":"int32"},{"internalType":"uint32","name":"tickSpacing","type":"uint32"},{"internalType":"uint256","name":"skipAhead","type":"uint256"}],"name":"nextInitializedTick","outputs":[{"internalType":"int32","name":"tick","type":"int32"},{"internalType":"bool","name":"isInitialized","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"PoolId","name":"poolId","type":"bytes32"},{"internalType":"int32","name":"fromTick","type":"int32"},{"internalType":"uint32","name":"tickSpacing","type":"uint32"},{"internalType":"uint256","name":"skipAhead","type":"uint256"}],"name":"prevInitializedTick","outputs":[{"internalType":"int32","name":"tick","type":"int32"},{"internalType":"bool","name":"isInitialized","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"bool","name":"beforeInitializePool","type":"bool"},{"internalType":"bool","name":"afterInitializePool","type":"bool"},{"internalType":"bool","name":"beforeSwap","type":"bool"},{"internalType":"bool","name":"afterSwap","type":"bool"},{"internalType":"bool","name":"beforeUpdatePosition","type":"bool"},{"internalType":"bool","name":"afterUpdatePosition","type":"bool"},{"internalType":"bool","name":"beforeCollectFees","type":"bool"},{"internalType":"bool","name":"afterCollectFees","type":"bool"}],"internalType":"struct CallPoints","name":"expectedCallPoints","type":"tuple"}],"name":"registerExtension","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"PoolId","name":"poolId","type":"bytes32"},{"internalType":"PositionId","name":"positionId","type":"bytes32"},{"internalType":"bytes16","name":"_extraData","type":"bytes16"}],"name":"setExtraData","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"sload","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[],"name":"startPayments","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"swap_6269342730","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"tload","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[],"name":"updateDebt","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"token0","type":"address"},{"internalType":"address","name":"token1","type":"address"},{"internalType":"PoolConfig","name":"config","type":"bytes32"}],"internalType":"struct PoolKey","name":"poolKey","type":"tuple"},{"internalType":"PositionId","name":"positionId","type":"bytes32"},{"internalType":"int128","name":"liquidityDelta","type":"int128"}],"name":"updatePosition","outputs":[{"internalType":"PoolBalanceUpdate","name":"balanceUpdate","type":"bytes32"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"token0","type":"address"},{"internalType":"address","name":"token1","type":"address"},{"internalType":"bytes32","name":"","type":"bytes32"},{"internalType":"int256","name":"delta0","type":"int256"},{"internalType":"int256","name":"delta1","type":"int256"}],"name":"updateSavedBalances","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]Contract Creation Code
6080806040523460155761496d908161001a8239f35b5f80fdfe60806040526004361015610053575b3615610018575f80fd5b6100517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff610044614279565b60a01c01345f03906142cc565b005b5f3560e01c80156125c75780626c95a9146125435780630e7f263914612508578063101e8952146123f857806312e103f11461221c57806317c5da6a1461211d578063187437a114611fec578063380eb4e014611f805780633ccfd60b14611df85780635ebb090914611d3f57806366e064a814611ced57806379b45a071461198b578063c0530244146115ac578063cb48dadf146108af578063de6f935f14610553578063e96404f814610376578063ed8328301461030a578063f83d08ba146101f95763f9b6a7960361000e57346101f5577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36015f81126101f557306014526f70a082310000000000000000000000005f5260405160045b3681106101785750f35b8073ffffffffffffffffffffffffffffffffffffffff60209235167ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc82850101838160246010855afa9051601f3d1182160201907f6747da56dbd05b26a7ecd2a0106781585141cf07098ad54c0e049e4e86dccb8c015d0161016e565b5f80fd5b346101f5577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36015f81126101f5577f07cc7f5195d862f505d6b095c82f92e00cfc1766f5bca4383c28dc5fca1555fd5c8060a01c90336001830160a01b177f07cc7f5195d862f505d6b095c82f92e00cfc1766f5bca4383c28dc5fca1555fd5d604051925f8452826004850152600460248501375f80602036018582335af115610301577f07cc7f5195d862f505d6b095c82f92e00cfc1766f5bca4383c28dc5fca1555fd5d807f7772acfd7e0f66ebb20a058830296c3dc1301b111d23348e1c961d324223190d015c6102f157503d5f823e3d90f35b639731ba375f526020526024601cfd5b823d5f823e3d90fd5b346101f5577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36015f81126101f55760045b36811061034757505ff35b80355c7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc82015260200161033c565b60a07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f5576103a93661347f565b6064356fffffffffffffffffffffffffffffffff8116918282036101f557608435926fffffffffffffffffffffffffffffffff8416908185036101f5576103ee6135b7565b604084015160601c73ffffffffffffffffffffffffffffffffffffffff8216036101f5577ff7e050d866774820d81a86ca676f3afe7bc72603ee893f82e99c08fbde39af6c9560609561049a928787209682158015809161054a575b6104ad575b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff73ffffffffffffffffffffffffffffffffffffffff602081845116930151169260a01c016140ee565b60405192835260208301526040820152a1005b6fffffffffffffffffffffffffffffffff89541690816104ce575b5061044f565b7fb09b03866d96933565a9435bfb511c8ac5b2be454285ca331201452704799f728a0190610539575b5084610504575b806104c8565b7fb09b03866d96933565a9435bfb511c8ac5b2be454285ca331201452704799f738901908154908660801b040190558a6104fe565b8054828660801b040190558b6104f7565b5084151561044a565b346101f5576101007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f55761058b61345e565b60043580151581036101f557815260243580151581036101f557602082015260443580151581036101f557604082015260643580151581036101f557606082015260843580151581036101f557608082015260a43580151581036101f55760a082015260c43580151581036101f55760c082015260e43580151581036101f55760e082015261061861460f565b5061062161460f565b5060013360981c16159061063361345e565b82158082523360981c6080818116151560208086019190915260408084161515908601528216151560608501526010821615159084015260088116151560a084015260048116151560c0840152600216151560e09092019190915281511515149081610896575b8161087d575b81610864575b8161084b575b81610832575b81610819575b81610801575b501590811561075e575b5061073657335f525f60205260405f20805461070e57600190557fec1256266e470abb868620c851f6bde2a3ff602549dcad318ab9ccfcb2977f146020604051338152a1005b7f8b20f687000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f7865ebfd000000000000000000000000000000000000000000000000000000005f5260045ffd5b801591506107f2575b80156107e3575b80156107d4575b80156107c5575b80156107b6575b80156107a7575b8015610798575b15816106c8565b5060023360981c161515610791565b5060043360981c16151561078a565b5060083360981c161515610783565b5060103360981c16151561077c565b5060203360981c161515610775565b5060403360981c16151561076e565b5060803360981c161515610767565b60e091500151151560023360981c16151514826106be565b905060c0810151151560043360981c16151514906106b8565b905060a0810151151560083360981c16151514906106b2565b90506080810151151560103360981c16151514906106ac565b90506060810151151560203360981c16151514906106a6565b90506040810151151560403360981c16151514906106a0565b90506020810151151560803360981c161515149061069a565b60a07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f5576108e23661347f565b608435600f0b608435036101f55760408101515f9063800000008116156114bb5760643560201c60030b60643560030b9182821215611493577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffab6326d82128015611486575b61145e57637fffffff61095d911660030b809261444c565b60030b1591821592611448575b5050611420575b6109796135b7565b60408301517fffffffffffffffffffffffffffffffffffffffff00000000000000000000000081168260601b14158160fc1c166113c2575b506060832091825490811561139a57608435600f0b1561135657506109dd60643560201c60030b613856565b6109fc6109ee60643560030b613856565b8360a01c926084358461448b565b9091610a4060643573ffffffffffffffffffffffffffffffffffffffff871688906060929160405192835260208301526040820152205f52600160205260405f2090565b90610a51608435835460801c6145bc565b610a5961343e565b5f8152602081015f8152819360408c015163800000001615155f1461127c575050506fffffffffffffffffffffffffffffffff811615611255575b60408901517f435a5eb89a296820174331cf5a3902d9fca683928d56726d8e7acd6efb28c56860643560201c60030b8a0101908154610ad86084358260801c6145bc565b916f7fffffffffffffffffffffffffffffff600f83810b60843590910b019081137fffffffffffffffffffffffffffffffff8000000000000000000000000000000090911217611131576fffffffffffffffffffffffffffffffff637fffffff8216630549cd930460011b6001018104166fffffffffffffffffffffffffffffffff84169080821161121a57506fffffffffffffffffffffffffffffffff92918d91158360801c150361115e575b5050608435600f0b90600f0b01169060801b17905560408901517f435a5eb89a296820174331cf5a3902d9fca683928d56726d8e7acd6efb28c56860643560030b8a010154610bda6084358260801c6145bc565b917fffffffffffffffffffffffffffffffff80000000000000000000000000000000608435600f90810b9084900b039081126f7fffffffffffffffffffffffffffffff90911317611131576fffffffffffffffffffffffffffffffff637fffffff8216630549cd930460011b600101810416806fffffffffffffffffffffffffffffffff8516116110e75750906fffffffffffffffffffffffffffffffff91828416158260801c150361102f575b50608435600f0b90600f0b03169060801b177f435a5eb89a296820174331cf5a3902d9fca683928d56726d8e7acd6efb28c56860643560030b8a0101556fffffffffffffffffffffffffffffffff8116611008575b8560801c60030b60643560201c60030b81121580610ffa575b610f63575b50927f704b3ab4a76158ad4d66625a2a43be81edbffd24630e8fde5174e97035370a0794926fffffffffffffffffffffffffffffffff9260c0955b808516610ed0575050600281845f9354168155826020610d5461343e565b828152015282600182015501555b610dce73ffffffffffffffffffffffffffffffffffffffff8a511673ffffffffffffffffffffffffffffffffffffffff60208c0151169083600f0b9186600f0b917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8c60a01c016140ee565b169060801b17946040519073ffffffffffffffffffffffffffffffffffffffff8616825260208201526064356040820152608435600f0b60608201528560808201528360a0820152a15b6040840151907fffffffffffffffffffffffffffffffffffffffff00000000000000000000000082168360601b14158260fb1c16610e5c575b602084604051908152f35b5f92838093610104936060604051998a957f25fa4e690000000000000000000000000000000000000000000000000000000087526004870152602486015e606435608485015260843560a48501528760c485015260e484015260601c5af115610ec757818080610e51565b503d5f823e3d90fd5b918192610ee7600293610ee28461352f565b614339565b9490918354887fffffffffffffffffffffffffffffffff000000000000000000000000000000008360801b169116178455610f2061343e565b9581602088019460801b04875260801b0482526020610f3d61343e565b925f8452818401965f885251835103809452519101510380945260018201550155610d62565b926fffffffffffffffffffffffffffffffff92610fea60c09693987f704b3ab4a76158ad4d66625a2a43be81edbffd24630e8fde5174e97035370a079896610faf6084358884166145bc565b9160a01c916fffffffffffffffffffffffffffffffff73ffffffff0000000000000000000000000000000091169160801b16179060a01b1790565b97888b5592955092509294610cfb565b5060643560030b8112610cf6565b905061102960643560030b60643560201c60030b8760801c60030b8a614147565b90610cdd565b61107b637fffffff7f3def450d0010a2fef515ce5eba4b363b5a0f42fdd4c53e1c737975db05a2e3a5921660643560030b630554777f915f81830712910503019060ff8260081c921690565b908d019091018054600190921b909118905582151560643560030b8c017f5695060fdb9cfea656f872ae4887221aff7dbfefc45eaf753e4e70cdfb5cd19c81018290557fd0bf0a0be44c3c54cf61da346063acd98e9363a6de122b16472a1947527c5c9e01558c610c88565b6fffffffffffffffffffffffffffffffff847fe51274fb000000000000000000000000000000000000000000000000000000005f5260643560030b6004521660245260445260645ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b6111ad637fffffff7f3def450d0010a2fef515ce5eba4b363b5a0f42fdd4c53e1c737975db05a2e3a5921660643560201c60030b630554777f915f81830712910503019060ff8260081c921690565b9201018054600190921b909118905582151560643560201c60030b8d017f5695060fdb9cfea656f872ae4887221aff7dbfefc45eaf753e4e70cdfb5cd19c81018290557fd0bf0a0be44c3c54cf61da346063acd98e9363a6de122b16472a1947527c5c9e01558b8e610b86565b907fe51274fb000000000000000000000000000000000000000000000000000000005f5260643560201c60030b60045260245260445260645ffd5b905061127660643560030b60643560201c60030b8760801c60030b8a614147565b90610a94565b926fffffffffffffffffffffffffffffffff94916113017f704b3ab4a76158ad4d66625a2a43be81edbffd24630e8fde5174e97035370a079997949a60c099966112ca6084358a84166145bc565b60a09190911b73ffffffff000000000000000000000000000000009092166fffffffffffffffffffffffffffffffff909116171790565b998a8d557fb09b03866d96933565a9435bfb511c8ac5b2be454285ca331201452704799f728d015490527fb09b03866d96933565a9435bfb511c8ac5b2be454285ca331201452704799f738c01549052610d36565b92503415610e185761139561136a3461358b565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8460a01c016142cc565b610e18565b7f486aa307000000000000000000000000000000000000000000000000000000005f5260045ffd5b5f8060c481936040519485917e35c72300000000000000000000000000000000000000000000000000000000835287600484015260608a602485015e606435608484015260843560a484015260601c5af16109b1575b3d5f823e3d90fd5b7f89fd41b1000000000000000000000000000000000000000000000000000000005f5260045ffd5b611452925061444c565b60030b1515838061096a565b7fabfa4a31000000000000000000000000000000000000000000000000000000005f5260045ffd5b50630549cd938313610945565b7f68651c5c000000000000000000000000000000000000000000000000000000005f5260045ffd5b611554630549cd93607f62ffffff841660020b60041b9360181c161c808303807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffab6326d03817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffab6326d13020192017ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffab6326d8101630549cd93821302900390565b9060030b60643560201c60030b1490811591611599575b5015610971577f72b1019d000000000000000000000000000000000000000000000000000000005f5260045ffd5b905060030b60643560030b14158361156b565b346101f55760807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f5576115e43661347f565b6064358060030b8082036101f55773ffffffffffffffffffffffffffffffffffffffff835116602084019073ffffffffffffffffffffffffffffffffffffffff82511611156119635760408401805190939063800000008116156118b857637fffffff16620aa8ed81119081156118af575b50611887575b83518060601c93846117f2575b606087209586546117ca5773ffffffffffffffffffffffffffffffffffffffff61169285613856565b9573ffffffff000000000000000000000000000000008660801b168760a01b17895560017fb09b03866d96933565a9435bfb511c8ac5b2be454285ca331201452704799f728a015560017fb09b03866d96933565a9435bfb511c8ac5b2be454285ca331201452704799f738a0155604051988952818a511660208a01525116604088015251606087015260808601527f5e4688b340694b7c7fd30047fd082117dc46e32acfbf81a44bb1fac0ae65154d60c06bffffffffffffffffffffffff8516968760a0820152a160ff1c338414151661177257602084604051908152f35b6060945f8094819460c494604051998a957f948374ff000000000000000000000000000000000000000000000000000000008752336004880152602487015e608485015260a48401525af115610ec757818080610e51565b7f7983c051000000000000000000000000000000000000000000000000000000005f5260045ffd5b845f525f60205260405f20541561185f57843314158260f81c1615611669576040517f1fbbb462000000000000000000000000000000000000000000000000000000008152336004820152606088602483015e8360848201525f8060a483828a5af1156114185750611669565b7fcd72c3bc000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f270815a0000000000000000000000000000000000000000000000000000000005f5260045ffd5b90501586611656565b601a607f8260181c161161193b5762ffffff1660020b60041b60030b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffab6326d811290811561192d575b501561165c577f10de0c76000000000000000000000000000000000000000000000000000000005f5260045ffd5b630549cd93915013866118ff565b7f218b53af000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f88a2efcf000000000000000000000000000000000000000000000000000000005f5260045ffd5b346101f55760807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f5576119c33661347f565b6064356119ce6135b7565b90604083019182518160601b907fffffffffffffffffffffffffffffffffffffffff00000000000000000000000081168214158160fa1c16611c99575b50606085209273ffffffffffffffffffffffffffffffffffffffff831694611a53828787906060929160405192835260208301526040820152205f52600160205260405f2090565b91611a5c61343e565b5f81525f6020820152918051638000000016155f14611c73577fb09b03866d96933565a9435bfb511c8ac5b2be454285ca331201452704799f7287015483527fb09b03866d96933565a9435bfb511c8ac5b2be454285ca331201452704799f7387015460208401525b82611acf8561352f565b90611ad991614339565b94909380516001830155602001519060020155885173ffffffffffffffffffffffffffffffffffffffff169760208a015173ffffffffffffffffffffffffffffffffffffffff16976fffffffffffffffffffffffffffffffff851699611b3e8b61358b565b996fffffffffffffffffffffffffffffffff88169a611b5c8c61358b565b918b60a01c7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0193611b8d94614383565b604051918252602082015282604082015288606082015287608082015260a07fd76ec32fbc3f07c70828b4f94343ee73279d0e8d4d2f28b018a4e67f3749775391a151937fffffffffffffffffffffffffffffffffffffffff000000000000000000000000851614158460f91c16611c10575b6040878782519182526020820152f35b5f94919360e4938695869360606040519c8d977f751fd5df0000000000000000000000000000000000000000000000000000000089526004890152602488015e608486015260a485015260c484015260601c5af115610301578280808080611c00565b9150611c93865460801c60030b8260030b908360201c60030b9089614147565b91611ac5565b5f8060a481936040519485917fdf65d8d100000000000000000000000000000000000000000000000000000000835288600484015260608c602485015e89608484015260601c5af1611a0b573d5f823e3d90fd5b346101f557611d287f3def450d0010a2fef515ce5eba4b363b5a0f42fdd4c53e1c737975db05a2e3a5611d1f3661337b565b9390920161376c565b6040805160039390930b8352901515602083015290f35b346101f55760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f5576044357fffffffffffffffffffffffffffffffff00000000000000000000000000000000811681036101f557611dc860243533600435906060929160405192835260208301526040820152205f52600160205260405f2090565b907fffffffffffffffffffffffffffffffff000000000000000000000000000000008254169060801c1790555f80f35b346101f5575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f557611e2e6135b7565b5f9060a01c817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820160045b368110611e93575050611e6957005b7f7772acfd7e0f66ebb20a058830296c3dc1301b111d23348e1c961d324223190c0190815c01905d005b809192503560601c601482013560601c602883013560801c9081611ec0575b505050603801908391611e5a565b828560a01b017f753dfe4b4dfb3ff6c11bbf6a97f3c094e91c003ce904a55cc5662fbad220f5990196875c9083820191821590150301975d8215611f5c576014526034526fa9059cbb0000000000000000000000005f5260205f6044601082855af1908160015f51141615611f3e575b50506038905b908580611eb2565b3b153d171015611f4f578480611f30565b6390b8ec185f526004601cfd5b5f809350809281925af115611f7357603890611f36565b63b12d13eb5f526004601cfd5b346101f5577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36015f81126101f55760045b368110611fbd57505ff35b8035547ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc820152602001611fb2565b60a07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f55761201e6133ce565b60243573ffffffffffffffffffffffffffffffffffffffff8116908181036101f557606435906084359273ffffffffffffffffffffffffffffffffffffffff851610156120f557610051937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6120926135b7565b608060405173ffffffffffffffffffffffffffffffffffffffff831681526060600460208301372080546120e5886fffffffffffffffffffffffffffffffff6120de8a8560801c613503565b9316613503565b9060801b01905560a01c016140ee565b7f5b0110b2000000000000000000000000000000000000000000000000000000005f5260045ffd5b346101f5575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f557601436036121f45761215b614279565b60a01c337fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820160a01b017f753dfe4b4dfb3ff6c11bbf6a97f3c094e91c003ce904a55cc5662fbad220f59901805c9160043560801d830192831590150390816121c5575b50505d005b7f7772acfd7e0f66ebb20a058830296c3dc1301b111d23348e1c961d324223190c0190815c01905d82806121c0565b7ffea66952000000000000000000000000000000000000000000000000000000005f5260045ffd5b346101f5575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f557612252614279565b60a01c7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8101604051915f809260045b3681106122e85750506122b9575b3660011c7f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01683f35b7f7772acfd7e0f66ebb20a058830296c3dc1301b111d23348e1c961d324223190c0190815c01905d8180612290565b90915073ffffffffffffffffffffffffffffffffffffffff81351690817f6747da56dbd05b26a7ecd2a0106781585141cf07098ad54c0e049e4e86dccb8c015f815c915d306014526f70a082310000000000000000000000005f5260205f60246010865afa601f3d11165f5102908060018184030192101990151516028060801c6123eb57602092818060801b7f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff08560011c168a01526123ae575b505001908391612282565b8460a01b017f753dfe4b4dfb3ff6c11bbf6a97f3c094e91c003ce904a55cc5662fbad220f5990190815c96818814881503019603905d86806123a3565b639cac58ca5f526004601cfd5b346101f55760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f55761242f6133ce565b6124376135b7565b817fffffffffffffffffffffffff00000000000000000000000000000000000000008216177f07cc7f5195d862f505d6b095c82f92e00cfc1766f5bca4383c28dc5fca1555fd5d5f80604051937c010000000000000000000000000000000000000000000000000000000085528360048601527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc3601602480870137848236925af115610ec7577f07cc7f5195d862f505d6b095c82f92e00cfc1766f5bca4383c28dc5fca1555fd5d3d5f823e3d90f35b346101f557611d287f3def450d0010a2fef515ce5eba4b363b5a0f42fdd4c53e1c737975db05a2e3a561253a3661337b565b93909201613607565b346101f55760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f557600435602435908160030b82036101f5576044358060030b81036101f5576040926125b5925f60206125a261343e565b8281520152805460801c60030b90614147565b60208251918051835201516020820152f35b5f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f5576125f86133f1565b5f60208201525f60408201526060600482376bffff9a5889f795069a41a8a360643560a01c111567400065a8177fae2760643560a01c101516673fffffffffffffff7fffffffffffffffffffffffffffffffffffffffff3fffffffffffffffffffffff60643560a01c16111615613353576126716135b7565b7fffffffffffffffffffffffffffffffffffffffff000000000000000000000000604435168160601b141560443560fe1c166132fe575b6060822090815492831561139a5760643560201c600f0b805f94821515806132ec575b612777575b5050507fffffffffffffffffffffffffffffffffffffffff000000000000000000000000604435168260601b141560443560fd1c16612716575b50505f5260205260405ff35b606090604051927fa4e8f2880000000000000000000000000000000000000000000000000000000084526004840152602483015e60643560848201528160a48201528260c48201525f8060e4838260443560601c5af115611418578061270a565b90919580955060a01c946fffffffffffffffffffffffffffffffff8160801c60030b9116965f81128760643560a01c10816001606435601f1c161415146132c4575f915f925f9a5b5f95819b604435638000000016155f1461321b5760443563ffffffff1661308857606435601f1c600116861461305857630549cd93916bffff9a5889f795069a41a8a35b929d5b606435601f1c60011688146130435760643560a01c808518908511028418905b6fffffffffffffffffffffffffffffffff8116612d7f5750925b6bffffffffffffffffffffffff84811691168103612bc7575050509a846001606435601f1c1614810396612a3a575b505b82158015612a1e575b156127bf576074976002936128fa61295c947fffffffffffffffffffffffffffffffff800000000000000000000000000000008113907fffffffffffffffffffffffffffffffff8000000000000000000000000000000018027fffffffffffffffffffffffffffffffff80000000000000000000000000000000186140c0565b9103606435601f1c60011615612a00576fffffffffffffffffffffffffffffffff169060801b17955b869b916fffffffffffffffffffffffffffffffff73ffffffff0000000000000000000000000000000091169160801b16179060a01b1790565b998a8655146129ca575b50506129a781600f0b8260801d600f0b6024356004357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8a60a01c016140ee565b604051918560601b835260148301526034820152856054820152a08480806126d0565b606435601f1c6001161883017fb09b03866d96933565a9435bfb511c8ac5b2be454285ca331201452704799f7201558780612966565b906fffffffffffffffffffffffffffffffff169060801b1795612923565b5060643560a01c6bffffffffffffffffffffffff8c161461287a565b7f435a5eb89a296820174331cf5a3902d9fca683928d56726d8e7acd6efb28c56890880190810154600f0b606435601f1c6001168087149087180302909101908c15612b8a575b606435601f1c60011685148881017fb09b03866d96933565a9435bfb511c8ac5b2be454285ca331201452704799f72015490612b10577f5695060fdb9cfea656f872ae4887221aff7dbfefc45eaf753e4e70cdfb5cd19c8201805490910390557fd0bf0a0be44c3c54cf61da346063acd98e9363a6de122b16472a1947527c5c9e018054860390555b8c61286f565b7fd0bf0a0be44c3c54cf61da346063acd98e9363a6de122b16472a1947527c5c9e907f5695060fdb9cfea656f872ae4887221aff7dbfefc45eaf753e4e70cdfb5cd19c83015488037f5695060fdb9cfea656f872ae4887221aff7dbfefc45eaf753e4e70cdfb5cd19c840155818301549003910155612b0a565b9b5093507fb09b03866d96933565a9435bfb511c8ac5b2be454285ca331201452704799f72836001606435601f1c1618870101549360019b612a81565b919d509750806bffffffffffffffffffffffff8e1603612be9575b5050612871565b9196509a506a1527370de4706fc9ea63ab612c2f8c60607fffffffffffffffffffffffffffffffffffffffff3fffffffffffffffffffffff82169160591c166002011b90565b8060801c15908082150290825f0304016802e2a8eca5705fc2ee8160801c1e60ff0391600183011c7fffffffffffffffffffffffffffffffff800000000000000000000000000000006f8000000000000000000000000000000082019101607f1b04808002607f1c90818102607f1c828102607f1c838102607f1c90848202607f1c92858402607f1c946003600560076009600b600d600f8d8d02607f1c9d8e02607f1c049c049a0498049604940492040101010101010102607f1c9060401b01905f14612d7a575f035b026e872032ac1300872032ac13008720327fffffffffffffffffffffffffffffffffff78dfcd53ecff78dfcd53ecff78dfce820160801d60030b910160801d60030b91818303612d4f575b5050948c80612be2565b6bffffffffffffffffffffffff612d6584613856565b1611612d72575b80612d45565b90508c612d6c565b612cfa565b959695909390881561302557865b606435601f1c6001161561301457612da6818387613e01565b905b606435601f1c600116808c1888841116908c14888410161715612f2e5750507fffffffffffffffffffffffffffffffffffffffff3fffffffffffffffffffffff85166002605987901c606016011b7fffffffffffffffffffffffffffffffffffffffff3fffffffffffffffffffffff85166002605987901c606016011b808203818311029182900391018a83606435601f1c60011615612f1157612e5992612e53831583838861406a565b94613f9f565b905b8a15612ee457612e7a67ffffffffffffffff60443560201c1683613f70565b809a019801980360801b04935b935b80612e95575b50612840565b9198909f155f14612edd57507fb09b03866d96933565a9435bfb511c8ac5b2be454285ca331201452704799f72876001606435601f1c16188b01015401965b60029e5f612e8f565b0196612ed4565b9098612eff67ffffffffffffffff60443560201c1683613f70565b998a91039803980360801b0493612e87565b612f2892612f228315838388613f9f565b9461406a565b90612e5b565b919295509796896bffffffffffffffffffffffff86166bffffffffffffffffffffffff8b1614155f14612fce5750606435601f1c60011615612fbc57612f768a84878c613f21565b915b8a15612fac575050612f9967ffffffffffffffff60443560201c1682613f70565b809701960360801b04925b5f9693612e89565b91909703960360801b0492612fa4565b612fc88a84878c613ea0565b91612f78565b91509192949750612fe75760801b04915f958293612e89565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52600160045260245ffd5b61301f818387613d0a565b90612da8565b67ffffffffffffffff8060443560201c1688020160401c8703612d8d565b60643560a01c80851890851002841890612826565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffab6326d9167400065a8177fae27612803565b8161312960449e939e3590630549cd93607f62ffffff841660020b60041b9360181c161c808303807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffab6326d03817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffab6326d13020192017ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffab6326d8101630549cd93821302900390565b9182828212159112165f14613166576131579e9190606435601f1c600116891461315d57509d8e5b9e613856565b92612806565b90509d8e613151565b92508d8360030b9060030b125f146131cb5750606435601f1c600116861461319a5761319182613856565b919c5b5f612806565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffab6326d915067400065a8177fae27613191565b909150606435601f1c60011686146131f95750630549cd93906bffff9a5889f795069a41a8a35b919c613194565b906bffffffffffffffffffffffff61321083613856565b9260030b92166131f2565b6131579c919750606435601f1c600116861461327c5761326f637fffffff60643516637fffffff604435168a7f3def450d0010a2fef515ce5eba4b363b5a0f42fdd4c53e1c737975db05a2e3a58d0161376c565b98909d8e995b999e613856565b6132ba637fffffff60643516637fffffff604435168a7f3def450d0010a2fef515ce5eba4b363b5a0f42fdd4c53e1c737975db05a2e3a58d01613607565b98909d8e99613275565b7fa574a6b4000000000000000000000000000000000000000000000000000000005f5260045ffd5b5060643560a01c8760a01c14156126cb565b6040517fca11dba7000000000000000000000000000000000000000000000000000000008152816004820152606083602483015e60643560848201525f8060a4838260443560601c5af11561141857506126a8565b7f9cc3dd4a000000000000000000000000000000000000000000000000000000005f5260045ffd5b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60809101126101f557600435906024358060030b81036101f5579060443563ffffffff811681036101f5579060643590565b6004359073ffffffffffffffffffffffffffffffffffffffff821682036101f557565b604051906060820182811067ffffffffffffffff82111761341157604052565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b604051906040820182811067ffffffffffffffff82111761341157604052565b60405190610100820182811067ffffffffffffffff82111761341157604052565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60609101126101f5576134b16133f1565b9060043573ffffffffffffffffffffffffffffffffffffffff811681036101f557825260243573ffffffffffffffffffffffffffffffffffffffff811681036101f55760208301526044356040830152565b8181019160ff1c8183118116918310901516178160801c176135225790565b631293d6fa5f526004601cfd5b906135386133f1565b9180547fffffffffffffffffffffffffffffffff000000000000000000000000000000008160801b16845260801c6020840152600261357561343e565b9160018101548352015460208201526040830152565b7f80000000000000000000000000000000000000000000000000000000000000008114611131575f0390565b6135bf614279565b903373ffffffffffffffffffffffffffffffffffffffff8316036135df57565b7feed0f119000000000000000000000000000000000000000000000000000000005f5260045ffd5b5f929182810784139083900503630554777f01600881901c9060ff165b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60018060ff858701549416011b01161e806101000361373357507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffaab8881839160081b0102937ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffab6326d8560030b131561370a578015613703577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff908101946136249184910160030b630554777f915f81830712910503019060ff8260081c921690565b5050509091565b50505090507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffab6326d91565b945090507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffaab8880919250600193610100039060081b01010291565b91909161379f825f945b63ffffffff821660030b0160030b630554777f915f81830712910503019060ff8260081c921690565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600160ff8486015493161b011916805f03161e806101000361373357507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffaab8980839160081b010293630549cd938560030b1215613849578015613703577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff019361379f908390613776565b5050509050630549cd9391565b908160030b918260ff1d8084011890630549cd938211613cde576138d49293506d08637b66cd638344daef276cd7c56001831602700100000000000000000000000000000000039160fe8116613bdb575b617f008116613ad1575b621f800081166139e8575b6307e0000081166138fe575b505f126138d757614647565b90565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04614647565b6220000081166139cc575b6240000081166139b0575b628000008116613994575b63010000008116613979575b6302000000811661395f575b630400000016613948575b5f6138c8565b69c0d55d4d7152c25fb13990910260801c90613942565b6cde2ee4bc381afa7089aa84bb6690920260801c91613937565b916e0ee7e32d61fdb0a5e622b820f681d00260801c9161392b565b916f03dc5dac7376e20fc8679758d1bcdcfc0260801c9161391f565b916f1f703399d88f6aa83a28b22d4a1f56e30260801c91613914565b916f59b63684b86e9f486ec54727371ba6ca0260801c91613909565b6180008116613ab5575b620100008116613a99575b620200008116613a7d575b620400008116613a61575b620800008116613a45575b621000008116156138bc57916f978bcb9894317807e5fa4498eee7c0fa0260801c916138bc565b916fc4f76b68947482dc198a48a54348c4ed0260801c91613a1e565b916fe08d35706200796273f0b3a981d90cfd0260801c91613a13565b916fefc2bf59df33ecc28125cf78ec4f167f0260801c91613a08565b916ff7bf5211c72f5185f372aeb1d48f937e0260801c916139fd565b916ffbd701c7cbc4c8a6bb81efd232d1e4e70260801c916139f2565b6101008116613bbf575b6102008116613ba3575b6104008116613b87575b6108008116613b6b575b6110008116613b4f575b6120008116613b33575b6140008116156138b157916ffde95287d26d81bea159c37073122c730260801c916138b1565b916ffef41d1a5f2ae3a20676bec6f7f9459a0260801c91613b0d565b916fff79eb706b9a64c6431d76e63531e9290260801c91613b03565b916fffbceceeb791747f10df216f2e53ec570260801c91613af9565b916fffde7444b28145508125d10077ba83b80260801c91613aef565b916fffef3995a5b6a6267530f207142a57640260801c91613ae5565b916ffff79ca7a4d1bf1ee8556cea23cdbaa50260801c91613adb565b60028116613cc2575b60048116613ca6575b60088116613c8a575b60108116613c6e575b60208116613c52575b60408116613c36575b60808116156138a757916ffffbce4b06c196e9247ac87695d53c600260801c916138a7565b916ffffde72350725cc4ea8feece3b5f13c80260801c91613c11565b916ffffef3911b7cff24ba1b3dbb5f8f59740260801c91613c08565b916fffff79c86a8f6150a32d9778eceef97c0260801c91613bff565b916fffffbce42c7be6c998ad6318193c0b180260801c91613bf6565b916fffffde72140b00a354bd3dc828e976c90260801c91613bed565b916fffffef390978c398134b4ff3764fe4100260801c91613be4565b837f073ee172000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b909180600f0b918215613dfa57613d4d5f9160607fffffffffffffffffffffffffffffffffffffffff3fffffffffffffffffffffff82169160591c166002011b90565b9360801b921215613de6575f03827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048111613dd35782029181831015613dd357613d9a92820391614808565b77fffffffffffffffffffffffc0000000000000000000000008111613dc2576138d490614719565b506bffffffffffffffffffffffff90565b5050506bffffffffffffffffffffffff90565b6138d4928204018082061515910401614719565b9250505090565b613e369060607fffffffffffffffffffffffffffffffffffffffff3fffffffffffffffffffffff82169160591c166002011b90565b915f81600f0b125f14613e6257906138d492915f0360801b908082061515910401808203911102614647565b60801b0481019081108015613e7f575b613dc2576138d490614647565b5077ffffffffffffffffffffffffffffffffffffffffffffffff8111613e72565b90613f10613ede6138d495949360607fffffffffffffffffffffffffffffffffffffffff3fffffffffffffffffffffff82169160591c166002011b90565b9160607fffffffffffffffffffffffffffffffffffffffff3fffffffffffffffffffffff82169160591c166002011b90565b80821181830302809101910361406a565b90613f5f613ede6138d495949360607fffffffffffffffffffffffffffffffffffffffff3fffffffffffffffffffffff82169160591c166002011b90565b808211818303028091019103613f9f565b60401b9068010000000000000000038082049106151501908160801c613f9257565b630d88f5265f526004601cfd5b929160801b915f14613fd957613fb89183820390614808565b9080820615159104018060801c613fcc5790565b63b4ef25465f526004601cfd5b828103908183027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8385099382805f0381169485920992048060030260021880820260020302808202600203028082026002030280820260020302808202600203028091026002030293816001858583030495805f03040193119180821001900303021702048060801c613fcc5790565b900391156140ac578061408f70010000000000000000000000000000000092846148d2565b92091515018060801c61409f5790565b6359d2b24a5f526004601cfd5b6140b5916148d2565b8060801c61409f5790565b806f800000000000000000000000000000000160801c156140e8576335278d125f526004601cfd5b600f0b90565b909392919034614104579361410294614383565b565b9173ffffffffffffffffffffffffffffffffffffffff851661412e57936141029434900392614383565b8293916141029561413e94614383565b345f03906142cc565b93929161415261343e565b60208101938196838101907fd0bf0a0be44c3c54cf61da346063acd98e9363a6de122b16472a1947527c5c9e7f5695060fdb9cfea656f872ae4887221aff7dbfefc45eaf753e4e70cdfb5cd19c83015492015494838201937fd0bf0a0be44c3c54cf61da346063acd98e9363a6de122b16472a1947527c5c9e7f5695060fdb9cfea656f872ae4887221aff7dbfefc45eaf753e4e70cdfb5cd19c8601549501549760030b9160030b82125f1461420d57505050039052039052565b9596959194939160030b1315614270577fb09b03866d96933565a9435bfb511c8ac5b2be454285ca331201452704799f737fb09b03866d96933565a9435bfb511c8ac5b2be454285ca331201452704799f72850154940154930303905203039052565b03905250039052565b7f07cc7f5195d862f505d6b095c82f92e00cfc1766f5bca4383c28dc5fca1555fd5c9081156142a457565b7f1834e265000000000000000000000000000000000000000000000000000000005f5260045ffd5b8060a01b7f753dfe4b4dfb3ff6c11bbf6a97f3c094e91c003ce904a55cc5662fbad220f5990190815c928301928315901503908161430a575b50505d565b7f7772acfd7e0f66ebb20a058830296c3dc1301b111d23348e1c961d324223190d0190815c01905d5f80614305565b919061437f6fffffffffffffffffffffffffffffffff9182614378816040602089015198015193602080865183510396015191015103971680936148d2565b16946148d2565b1690565b92809492915f9561440d575b5050816143cc575b5050816143a2575050565b7f7772acfd7e0f66ebb20a058830296c3dc1301b111d23348e1c961d324223190d0190815c01905d565b8260a09492941b017f753dfe4b4dfb3ff6c11bbf6a97f3c094e91c003ce904a55cc5662fbad220f5990190815c938401938415901503019283915d5f614397565b839195508460a01b017f753dfe4b4dfb3ff6c11bbf6a97f3c094e91c003ce904a55cc5662fbad220f5990194855c908101908115901503955d5f61438f565b9060030b90811561445e5760030b0790565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b90925f905f94600f0b9384156145b0575f8513936fffffffffffffffffffffffffffffffff8560011b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01968060ff1d8091011816936bffffffffffffffffffffffff82166bffffffffffffffffffffffff84168111155f1461453657505050926fffffffffffffffffffffffffffffffff9261452c926145339695613f21565b16026140c0565b91565b929897509092916bffffffffffffffffffffffff8316111561458c57509282826145866fffffffffffffffffffffffffffffffff61457e8582986138d49b9a61452c99613f21565b1687026140c0565b98613ea0565b9661452c9250926138d49594916fffffffffffffffffffffffffffffffff94613ea0565b5050505050505f905f90565b01907fffffffffffffffffffffffffffffffff0000000000000000000000000000000082166145e757565b7f6d862c50000000000000000000000000000000000000000000000000000000005f5260045ffd5b61461761345e565b905f82525f60208301525f60408301525f60608301525f60808301525f60a08301525f60c08301525f60e0830152565b6c0100000000000000000000000081106147135770010000000000000000000000000000000081106146ff577401000000000000000000000000000000000000000081106146eb57780100000000000000000000000000000000000000000000000081106146d7577fa10459f4000000000000000000000000000000000000000000000000000000005f5260045ffd5b60621c6bc000000000000000000000001790565b60421c6b8000000000000000000000001790565b60221c6b4000000000000000000000001790565b60021c90565b600381016c01000000000000000000000000811061480057506403ffffffff810170010000000000000000000000000000000081106147ea57506803ffffffffffffffff8101907401000000000000000000000000000000000000000082106147d5576c03ffffffffffffffffffffffff915001780100000000000000000000000000000000000000000000000081106146d7577fa10459f4000000000000000000000000000000000000000000000000000000005f5260045ffd5b5060421c6b8000000000000000000000001790565b905060221c6b4000000000000000000000001790565b905060021c90565b92918082850281861585888404141702156148445704915b82940961482a5750565b6001019150811561483757565b63ae47f7025f526004601cfd5b90507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8386098181108201900382848709835f03841691808511156148375782850480600302600218808202600203028082026002030280820260020302808202600203028082026002030280910260020302936001848483030494805f0304019211900302170291614820565b818102918082840414821517156148eb57505060801c90565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff910981811082019003908160801c1561492c5763ae47f7025f526004601cfd5b60801c9060801b019056fea26469706673582212208f4790146f3838995be055c447caa079afb5b6eeabdaeb4a4501328670e4187e64736f6c63430008210033
Deployed Bytecode
0x60806040526004361015610053575b3615610018575f80fd5b6100517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff610044614279565b60a01c01345f03906142cc565b005b5f3560e01c80156125c75780626c95a9146125435780630e7f263914612508578063101e8952146123f857806312e103f11461221c57806317c5da6a1461211d578063187437a114611fec578063380eb4e014611f805780633ccfd60b14611df85780635ebb090914611d3f57806366e064a814611ced57806379b45a071461198b578063c0530244146115ac578063cb48dadf146108af578063de6f935f14610553578063e96404f814610376578063ed8328301461030a578063f83d08ba146101f95763f9b6a7960361000e57346101f5577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36015f81126101f557306014526f70a082310000000000000000000000005f5260405160045b3681106101785750f35b8073ffffffffffffffffffffffffffffffffffffffff60209235167ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc82850101838160246010855afa9051601f3d1182160201907f6747da56dbd05b26a7ecd2a0106781585141cf07098ad54c0e049e4e86dccb8c015d0161016e565b5f80fd5b346101f5577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36015f81126101f5577f07cc7f5195d862f505d6b095c82f92e00cfc1766f5bca4383c28dc5fca1555fd5c8060a01c90336001830160a01b177f07cc7f5195d862f505d6b095c82f92e00cfc1766f5bca4383c28dc5fca1555fd5d604051925f8452826004850152600460248501375f80602036018582335af115610301577f07cc7f5195d862f505d6b095c82f92e00cfc1766f5bca4383c28dc5fca1555fd5d807f7772acfd7e0f66ebb20a058830296c3dc1301b111d23348e1c961d324223190d015c6102f157503d5f823e3d90f35b639731ba375f526020526024601cfd5b823d5f823e3d90fd5b346101f5577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36015f81126101f55760045b36811061034757505ff35b80355c7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc82015260200161033c565b60a07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f5576103a93661347f565b6064356fffffffffffffffffffffffffffffffff8116918282036101f557608435926fffffffffffffffffffffffffffffffff8416908185036101f5576103ee6135b7565b604084015160601c73ffffffffffffffffffffffffffffffffffffffff8216036101f5577ff7e050d866774820d81a86ca676f3afe7bc72603ee893f82e99c08fbde39af6c9560609561049a928787209682158015809161054a575b6104ad575b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff73ffffffffffffffffffffffffffffffffffffffff602081845116930151169260a01c016140ee565b60405192835260208301526040820152a1005b6fffffffffffffffffffffffffffffffff89541690816104ce575b5061044f565b7fb09b03866d96933565a9435bfb511c8ac5b2be454285ca331201452704799f728a0190610539575b5084610504575b806104c8565b7fb09b03866d96933565a9435bfb511c8ac5b2be454285ca331201452704799f738901908154908660801b040190558a6104fe565b8054828660801b040190558b6104f7565b5084151561044a565b346101f5576101007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f55761058b61345e565b60043580151581036101f557815260243580151581036101f557602082015260443580151581036101f557604082015260643580151581036101f557606082015260843580151581036101f557608082015260a43580151581036101f55760a082015260c43580151581036101f55760c082015260e43580151581036101f55760e082015261061861460f565b5061062161460f565b5060013360981c16159061063361345e565b82158082523360981c6080818116151560208086019190915260408084161515908601528216151560608501526010821615159084015260088116151560a084015260048116151560c0840152600216151560e09092019190915281511515149081610896575b8161087d575b81610864575b8161084b575b81610832575b81610819575b81610801575b501590811561075e575b5061073657335f525f60205260405f20805461070e57600190557fec1256266e470abb868620c851f6bde2a3ff602549dcad318ab9ccfcb2977f146020604051338152a1005b7f8b20f687000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f7865ebfd000000000000000000000000000000000000000000000000000000005f5260045ffd5b801591506107f2575b80156107e3575b80156107d4575b80156107c5575b80156107b6575b80156107a7575b8015610798575b15816106c8565b5060023360981c161515610791565b5060043360981c16151561078a565b5060083360981c161515610783565b5060103360981c16151561077c565b5060203360981c161515610775565b5060403360981c16151561076e565b5060803360981c161515610767565b60e091500151151560023360981c16151514826106be565b905060c0810151151560043360981c16151514906106b8565b905060a0810151151560083360981c16151514906106b2565b90506080810151151560103360981c16151514906106ac565b90506060810151151560203360981c16151514906106a6565b90506040810151151560403360981c16151514906106a0565b90506020810151151560803360981c161515149061069a565b60a07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f5576108e23661347f565b608435600f0b608435036101f55760408101515f9063800000008116156114bb5760643560201c60030b60643560030b9182821215611493577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffab6326d82128015611486575b61145e57637fffffff61095d911660030b809261444c565b60030b1591821592611448575b5050611420575b6109796135b7565b60408301517fffffffffffffffffffffffffffffffffffffffff00000000000000000000000081168260601b14158160fc1c166113c2575b506060832091825490811561139a57608435600f0b1561135657506109dd60643560201c60030b613856565b6109fc6109ee60643560030b613856565b8360a01c926084358461448b565b9091610a4060643573ffffffffffffffffffffffffffffffffffffffff871688906060929160405192835260208301526040820152205f52600160205260405f2090565b90610a51608435835460801c6145bc565b610a5961343e565b5f8152602081015f8152819360408c015163800000001615155f1461127c575050506fffffffffffffffffffffffffffffffff811615611255575b60408901517f435a5eb89a296820174331cf5a3902d9fca683928d56726d8e7acd6efb28c56860643560201c60030b8a0101908154610ad86084358260801c6145bc565b916f7fffffffffffffffffffffffffffffff600f83810b60843590910b019081137fffffffffffffffffffffffffffffffff8000000000000000000000000000000090911217611131576fffffffffffffffffffffffffffffffff637fffffff8216630549cd930460011b6001018104166fffffffffffffffffffffffffffffffff84169080821161121a57506fffffffffffffffffffffffffffffffff92918d91158360801c150361115e575b5050608435600f0b90600f0b01169060801b17905560408901517f435a5eb89a296820174331cf5a3902d9fca683928d56726d8e7acd6efb28c56860643560030b8a010154610bda6084358260801c6145bc565b917fffffffffffffffffffffffffffffffff80000000000000000000000000000000608435600f90810b9084900b039081126f7fffffffffffffffffffffffffffffff90911317611131576fffffffffffffffffffffffffffffffff637fffffff8216630549cd930460011b600101810416806fffffffffffffffffffffffffffffffff8516116110e75750906fffffffffffffffffffffffffffffffff91828416158260801c150361102f575b50608435600f0b90600f0b03169060801b177f435a5eb89a296820174331cf5a3902d9fca683928d56726d8e7acd6efb28c56860643560030b8a0101556fffffffffffffffffffffffffffffffff8116611008575b8560801c60030b60643560201c60030b81121580610ffa575b610f63575b50927f704b3ab4a76158ad4d66625a2a43be81edbffd24630e8fde5174e97035370a0794926fffffffffffffffffffffffffffffffff9260c0955b808516610ed0575050600281845f9354168155826020610d5461343e565b828152015282600182015501555b610dce73ffffffffffffffffffffffffffffffffffffffff8a511673ffffffffffffffffffffffffffffffffffffffff60208c0151169083600f0b9186600f0b917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8c60a01c016140ee565b169060801b17946040519073ffffffffffffffffffffffffffffffffffffffff8616825260208201526064356040820152608435600f0b60608201528560808201528360a0820152a15b6040840151907fffffffffffffffffffffffffffffffffffffffff00000000000000000000000082168360601b14158260fb1c16610e5c575b602084604051908152f35b5f92838093610104936060604051998a957f25fa4e690000000000000000000000000000000000000000000000000000000087526004870152602486015e606435608485015260843560a48501528760c485015260e484015260601c5af115610ec757818080610e51565b503d5f823e3d90fd5b918192610ee7600293610ee28461352f565b614339565b9490918354887fffffffffffffffffffffffffffffffff000000000000000000000000000000008360801b169116178455610f2061343e565b9581602088019460801b04875260801b0482526020610f3d61343e565b925f8452818401965f885251835103809452519101510380945260018201550155610d62565b926fffffffffffffffffffffffffffffffff92610fea60c09693987f704b3ab4a76158ad4d66625a2a43be81edbffd24630e8fde5174e97035370a079896610faf6084358884166145bc565b9160a01c916fffffffffffffffffffffffffffffffff73ffffffff0000000000000000000000000000000091169160801b16179060a01b1790565b97888b5592955092509294610cfb565b5060643560030b8112610cf6565b905061102960643560030b60643560201c60030b8760801c60030b8a614147565b90610cdd565b61107b637fffffff7f3def450d0010a2fef515ce5eba4b363b5a0f42fdd4c53e1c737975db05a2e3a5921660643560030b630554777f915f81830712910503019060ff8260081c921690565b908d019091018054600190921b909118905582151560643560030b8c017f5695060fdb9cfea656f872ae4887221aff7dbfefc45eaf753e4e70cdfb5cd19c81018290557fd0bf0a0be44c3c54cf61da346063acd98e9363a6de122b16472a1947527c5c9e01558c610c88565b6fffffffffffffffffffffffffffffffff847fe51274fb000000000000000000000000000000000000000000000000000000005f5260643560030b6004521660245260445260645ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b6111ad637fffffff7f3def450d0010a2fef515ce5eba4b363b5a0f42fdd4c53e1c737975db05a2e3a5921660643560201c60030b630554777f915f81830712910503019060ff8260081c921690565b9201018054600190921b909118905582151560643560201c60030b8d017f5695060fdb9cfea656f872ae4887221aff7dbfefc45eaf753e4e70cdfb5cd19c81018290557fd0bf0a0be44c3c54cf61da346063acd98e9363a6de122b16472a1947527c5c9e01558b8e610b86565b907fe51274fb000000000000000000000000000000000000000000000000000000005f5260643560201c60030b60045260245260445260645ffd5b905061127660643560030b60643560201c60030b8760801c60030b8a614147565b90610a94565b926fffffffffffffffffffffffffffffffff94916113017f704b3ab4a76158ad4d66625a2a43be81edbffd24630e8fde5174e97035370a079997949a60c099966112ca6084358a84166145bc565b60a09190911b73ffffffff000000000000000000000000000000009092166fffffffffffffffffffffffffffffffff909116171790565b998a8d557fb09b03866d96933565a9435bfb511c8ac5b2be454285ca331201452704799f728d015490527fb09b03866d96933565a9435bfb511c8ac5b2be454285ca331201452704799f738c01549052610d36565b92503415610e185761139561136a3461358b565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8460a01c016142cc565b610e18565b7f486aa307000000000000000000000000000000000000000000000000000000005f5260045ffd5b5f8060c481936040519485917e35c72300000000000000000000000000000000000000000000000000000000835287600484015260608a602485015e606435608484015260843560a484015260601c5af16109b1575b3d5f823e3d90fd5b7f89fd41b1000000000000000000000000000000000000000000000000000000005f5260045ffd5b611452925061444c565b60030b1515838061096a565b7fabfa4a31000000000000000000000000000000000000000000000000000000005f5260045ffd5b50630549cd938313610945565b7f68651c5c000000000000000000000000000000000000000000000000000000005f5260045ffd5b611554630549cd93607f62ffffff841660020b60041b9360181c161c808303807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffab6326d03817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffab6326d13020192017ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffab6326d8101630549cd93821302900390565b9060030b60643560201c60030b1490811591611599575b5015610971577f72b1019d000000000000000000000000000000000000000000000000000000005f5260045ffd5b905060030b60643560030b14158361156b565b346101f55760807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f5576115e43661347f565b6064358060030b8082036101f55773ffffffffffffffffffffffffffffffffffffffff835116602084019073ffffffffffffffffffffffffffffffffffffffff82511611156119635760408401805190939063800000008116156118b857637fffffff16620aa8ed81119081156118af575b50611887575b83518060601c93846117f2575b606087209586546117ca5773ffffffffffffffffffffffffffffffffffffffff61169285613856565b9573ffffffff000000000000000000000000000000008660801b168760a01b17895560017fb09b03866d96933565a9435bfb511c8ac5b2be454285ca331201452704799f728a015560017fb09b03866d96933565a9435bfb511c8ac5b2be454285ca331201452704799f738a0155604051988952818a511660208a01525116604088015251606087015260808601527f5e4688b340694b7c7fd30047fd082117dc46e32acfbf81a44bb1fac0ae65154d60c06bffffffffffffffffffffffff8516968760a0820152a160ff1c338414151661177257602084604051908152f35b6060945f8094819460c494604051998a957f948374ff000000000000000000000000000000000000000000000000000000008752336004880152602487015e608485015260a48401525af115610ec757818080610e51565b7f7983c051000000000000000000000000000000000000000000000000000000005f5260045ffd5b845f525f60205260405f20541561185f57843314158260f81c1615611669576040517f1fbbb462000000000000000000000000000000000000000000000000000000008152336004820152606088602483015e8360848201525f8060a483828a5af1156114185750611669565b7fcd72c3bc000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f270815a0000000000000000000000000000000000000000000000000000000005f5260045ffd5b90501586611656565b601a607f8260181c161161193b5762ffffff1660020b60041b60030b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffab6326d811290811561192d575b501561165c577f10de0c76000000000000000000000000000000000000000000000000000000005f5260045ffd5b630549cd93915013866118ff565b7f218b53af000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f88a2efcf000000000000000000000000000000000000000000000000000000005f5260045ffd5b346101f55760807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f5576119c33661347f565b6064356119ce6135b7565b90604083019182518160601b907fffffffffffffffffffffffffffffffffffffffff00000000000000000000000081168214158160fa1c16611c99575b50606085209273ffffffffffffffffffffffffffffffffffffffff831694611a53828787906060929160405192835260208301526040820152205f52600160205260405f2090565b91611a5c61343e565b5f81525f6020820152918051638000000016155f14611c73577fb09b03866d96933565a9435bfb511c8ac5b2be454285ca331201452704799f7287015483527fb09b03866d96933565a9435bfb511c8ac5b2be454285ca331201452704799f7387015460208401525b82611acf8561352f565b90611ad991614339565b94909380516001830155602001519060020155885173ffffffffffffffffffffffffffffffffffffffff169760208a015173ffffffffffffffffffffffffffffffffffffffff16976fffffffffffffffffffffffffffffffff851699611b3e8b61358b565b996fffffffffffffffffffffffffffffffff88169a611b5c8c61358b565b918b60a01c7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0193611b8d94614383565b604051918252602082015282604082015288606082015287608082015260a07fd76ec32fbc3f07c70828b4f94343ee73279d0e8d4d2f28b018a4e67f3749775391a151937fffffffffffffffffffffffffffffffffffffffff000000000000000000000000851614158460f91c16611c10575b6040878782519182526020820152f35b5f94919360e4938695869360606040519c8d977f751fd5df0000000000000000000000000000000000000000000000000000000089526004890152602488015e608486015260a485015260c484015260601c5af115610301578280808080611c00565b9150611c93865460801c60030b8260030b908360201c60030b9089614147565b91611ac5565b5f8060a481936040519485917fdf65d8d100000000000000000000000000000000000000000000000000000000835288600484015260608c602485015e89608484015260601c5af1611a0b573d5f823e3d90fd5b346101f557611d287f3def450d0010a2fef515ce5eba4b363b5a0f42fdd4c53e1c737975db05a2e3a5611d1f3661337b565b9390920161376c565b6040805160039390930b8352901515602083015290f35b346101f55760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f5576044357fffffffffffffffffffffffffffffffff00000000000000000000000000000000811681036101f557611dc860243533600435906060929160405192835260208301526040820152205f52600160205260405f2090565b907fffffffffffffffffffffffffffffffff000000000000000000000000000000008254169060801c1790555f80f35b346101f5575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f557611e2e6135b7565b5f9060a01c817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820160045b368110611e93575050611e6957005b7f7772acfd7e0f66ebb20a058830296c3dc1301b111d23348e1c961d324223190c0190815c01905d005b809192503560601c601482013560601c602883013560801c9081611ec0575b505050603801908391611e5a565b828560a01b017f753dfe4b4dfb3ff6c11bbf6a97f3c094e91c003ce904a55cc5662fbad220f5990196875c9083820191821590150301975d8215611f5c576014526034526fa9059cbb0000000000000000000000005f5260205f6044601082855af1908160015f51141615611f3e575b50506038905b908580611eb2565b3b153d171015611f4f578480611f30565b6390b8ec185f526004601cfd5b5f809350809281925af115611f7357603890611f36565b63b12d13eb5f526004601cfd5b346101f5577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36015f81126101f55760045b368110611fbd57505ff35b8035547ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc820152602001611fb2565b60a07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f55761201e6133ce565b60243573ffffffffffffffffffffffffffffffffffffffff8116908181036101f557606435906084359273ffffffffffffffffffffffffffffffffffffffff851610156120f557610051937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6120926135b7565b608060405173ffffffffffffffffffffffffffffffffffffffff831681526060600460208301372080546120e5886fffffffffffffffffffffffffffffffff6120de8a8560801c613503565b9316613503565b9060801b01905560a01c016140ee565b7f5b0110b2000000000000000000000000000000000000000000000000000000005f5260045ffd5b346101f5575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f557601436036121f45761215b614279565b60a01c337fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820160a01b017f753dfe4b4dfb3ff6c11bbf6a97f3c094e91c003ce904a55cc5662fbad220f59901805c9160043560801d830192831590150390816121c5575b50505d005b7f7772acfd7e0f66ebb20a058830296c3dc1301b111d23348e1c961d324223190c0190815c01905d82806121c0565b7ffea66952000000000000000000000000000000000000000000000000000000005f5260045ffd5b346101f5575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f557612252614279565b60a01c7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8101604051915f809260045b3681106122e85750506122b9575b3660011c7f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01683f35b7f7772acfd7e0f66ebb20a058830296c3dc1301b111d23348e1c961d324223190c0190815c01905d8180612290565b90915073ffffffffffffffffffffffffffffffffffffffff81351690817f6747da56dbd05b26a7ecd2a0106781585141cf07098ad54c0e049e4e86dccb8c015f815c915d306014526f70a082310000000000000000000000005f5260205f60246010865afa601f3d11165f5102908060018184030192101990151516028060801c6123eb57602092818060801b7f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff08560011c168a01526123ae575b505001908391612282565b8460a01b017f753dfe4b4dfb3ff6c11bbf6a97f3c094e91c003ce904a55cc5662fbad220f5990190815c96818814881503019603905d86806123a3565b639cac58ca5f526004601cfd5b346101f55760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f55761242f6133ce565b6124376135b7565b817fffffffffffffffffffffffff00000000000000000000000000000000000000008216177f07cc7f5195d862f505d6b095c82f92e00cfc1766f5bca4383c28dc5fca1555fd5d5f80604051937c010000000000000000000000000000000000000000000000000000000085528360048601527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc3601602480870137848236925af115610ec7577f07cc7f5195d862f505d6b095c82f92e00cfc1766f5bca4383c28dc5fca1555fd5d3d5f823e3d90f35b346101f557611d287f3def450d0010a2fef515ce5eba4b363b5a0f42fdd4c53e1c737975db05a2e3a561253a3661337b565b93909201613607565b346101f55760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f557600435602435908160030b82036101f5576044358060030b81036101f5576040926125b5925f60206125a261343e565b8281520152805460801c60030b90614147565b60208251918051835201516020820152f35b5f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f5576125f86133f1565b5f60208201525f60408201526060600482376bffff9a5889f795069a41a8a360643560a01c111567400065a8177fae2760643560a01c101516673fffffffffffffff7fffffffffffffffffffffffffffffffffffffffff3fffffffffffffffffffffff60643560a01c16111615613353576126716135b7565b7fffffffffffffffffffffffffffffffffffffffff000000000000000000000000604435168160601b141560443560fe1c166132fe575b6060822090815492831561139a5760643560201c600f0b805f94821515806132ec575b612777575b5050507fffffffffffffffffffffffffffffffffffffffff000000000000000000000000604435168260601b141560443560fd1c16612716575b50505f5260205260405ff35b606090604051927fa4e8f2880000000000000000000000000000000000000000000000000000000084526004840152602483015e60643560848201528160a48201528260c48201525f8060e4838260443560601c5af115611418578061270a565b90919580955060a01c946fffffffffffffffffffffffffffffffff8160801c60030b9116965f81128760643560a01c10816001606435601f1c161415146132c4575f915f925f9a5b5f95819b604435638000000016155f1461321b5760443563ffffffff1661308857606435601f1c600116861461305857630549cd93916bffff9a5889f795069a41a8a35b929d5b606435601f1c60011688146130435760643560a01c808518908511028418905b6fffffffffffffffffffffffffffffffff8116612d7f5750925b6bffffffffffffffffffffffff84811691168103612bc7575050509a846001606435601f1c1614810396612a3a575b505b82158015612a1e575b156127bf576074976002936128fa61295c947fffffffffffffffffffffffffffffffff800000000000000000000000000000008113907fffffffffffffffffffffffffffffffff8000000000000000000000000000000018027fffffffffffffffffffffffffffffffff80000000000000000000000000000000186140c0565b9103606435601f1c60011615612a00576fffffffffffffffffffffffffffffffff169060801b17955b869b916fffffffffffffffffffffffffffffffff73ffffffff0000000000000000000000000000000091169160801b16179060a01b1790565b998a8655146129ca575b50506129a781600f0b8260801d600f0b6024356004357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8a60a01c016140ee565b604051918560601b835260148301526034820152856054820152a08480806126d0565b606435601f1c6001161883017fb09b03866d96933565a9435bfb511c8ac5b2be454285ca331201452704799f7201558780612966565b906fffffffffffffffffffffffffffffffff169060801b1795612923565b5060643560a01c6bffffffffffffffffffffffff8c161461287a565b7f435a5eb89a296820174331cf5a3902d9fca683928d56726d8e7acd6efb28c56890880190810154600f0b606435601f1c6001168087149087180302909101908c15612b8a575b606435601f1c60011685148881017fb09b03866d96933565a9435bfb511c8ac5b2be454285ca331201452704799f72015490612b10577f5695060fdb9cfea656f872ae4887221aff7dbfefc45eaf753e4e70cdfb5cd19c8201805490910390557fd0bf0a0be44c3c54cf61da346063acd98e9363a6de122b16472a1947527c5c9e018054860390555b8c61286f565b7fd0bf0a0be44c3c54cf61da346063acd98e9363a6de122b16472a1947527c5c9e907f5695060fdb9cfea656f872ae4887221aff7dbfefc45eaf753e4e70cdfb5cd19c83015488037f5695060fdb9cfea656f872ae4887221aff7dbfefc45eaf753e4e70cdfb5cd19c840155818301549003910155612b0a565b9b5093507fb09b03866d96933565a9435bfb511c8ac5b2be454285ca331201452704799f72836001606435601f1c1618870101549360019b612a81565b919d509750806bffffffffffffffffffffffff8e1603612be9575b5050612871565b9196509a506a1527370de4706fc9ea63ab612c2f8c60607fffffffffffffffffffffffffffffffffffffffff3fffffffffffffffffffffff82169160591c166002011b90565b8060801c15908082150290825f0304016802e2a8eca5705fc2ee8160801c1e60ff0391600183011c7fffffffffffffffffffffffffffffffff800000000000000000000000000000006f8000000000000000000000000000000082019101607f1b04808002607f1c90818102607f1c828102607f1c838102607f1c90848202607f1c92858402607f1c946003600560076009600b600d600f8d8d02607f1c9d8e02607f1c049c049a0498049604940492040101010101010102607f1c9060401b01905f14612d7a575f035b026e872032ac1300872032ac13008720327fffffffffffffffffffffffffffffffffff78dfcd53ecff78dfcd53ecff78dfce820160801d60030b910160801d60030b91818303612d4f575b5050948c80612be2565b6bffffffffffffffffffffffff612d6584613856565b1611612d72575b80612d45565b90508c612d6c565b612cfa565b959695909390881561302557865b606435601f1c6001161561301457612da6818387613e01565b905b606435601f1c600116808c1888841116908c14888410161715612f2e5750507fffffffffffffffffffffffffffffffffffffffff3fffffffffffffffffffffff85166002605987901c606016011b7fffffffffffffffffffffffffffffffffffffffff3fffffffffffffffffffffff85166002605987901c606016011b808203818311029182900391018a83606435601f1c60011615612f1157612e5992612e53831583838861406a565b94613f9f565b905b8a15612ee457612e7a67ffffffffffffffff60443560201c1683613f70565b809a019801980360801b04935b935b80612e95575b50612840565b9198909f155f14612edd57507fb09b03866d96933565a9435bfb511c8ac5b2be454285ca331201452704799f72876001606435601f1c16188b01015401965b60029e5f612e8f565b0196612ed4565b9098612eff67ffffffffffffffff60443560201c1683613f70565b998a91039803980360801b0493612e87565b612f2892612f228315838388613f9f565b9461406a565b90612e5b565b919295509796896bffffffffffffffffffffffff86166bffffffffffffffffffffffff8b1614155f14612fce5750606435601f1c60011615612fbc57612f768a84878c613f21565b915b8a15612fac575050612f9967ffffffffffffffff60443560201c1682613f70565b809701960360801b04925b5f9693612e89565b91909703960360801b0492612fa4565b612fc88a84878c613ea0565b91612f78565b91509192949750612fe75760801b04915f958293612e89565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52600160045260245ffd5b61301f818387613d0a565b90612da8565b67ffffffffffffffff8060443560201c1688020160401c8703612d8d565b60643560a01c80851890851002841890612826565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffab6326d9167400065a8177fae27612803565b8161312960449e939e3590630549cd93607f62ffffff841660020b60041b9360181c161c808303807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffab6326d03817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffab6326d13020192017ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffab6326d8101630549cd93821302900390565b9182828212159112165f14613166576131579e9190606435601f1c600116891461315d57509d8e5b9e613856565b92612806565b90509d8e613151565b92508d8360030b9060030b125f146131cb5750606435601f1c600116861461319a5761319182613856565b919c5b5f612806565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffab6326d915067400065a8177fae27613191565b909150606435601f1c60011686146131f95750630549cd93906bffff9a5889f795069a41a8a35b919c613194565b906bffffffffffffffffffffffff61321083613856565b9260030b92166131f2565b6131579c919750606435601f1c600116861461327c5761326f637fffffff60643516637fffffff604435168a7f3def450d0010a2fef515ce5eba4b363b5a0f42fdd4c53e1c737975db05a2e3a58d0161376c565b98909d8e995b999e613856565b6132ba637fffffff60643516637fffffff604435168a7f3def450d0010a2fef515ce5eba4b363b5a0f42fdd4c53e1c737975db05a2e3a58d01613607565b98909d8e99613275565b7fa574a6b4000000000000000000000000000000000000000000000000000000005f5260045ffd5b5060643560a01c8760a01c14156126cb565b6040517fca11dba7000000000000000000000000000000000000000000000000000000008152816004820152606083602483015e60643560848201525f8060a4838260443560601c5af11561141857506126a8565b7f9cc3dd4a000000000000000000000000000000000000000000000000000000005f5260045ffd5b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60809101126101f557600435906024358060030b81036101f5579060443563ffffffff811681036101f5579060643590565b6004359073ffffffffffffffffffffffffffffffffffffffff821682036101f557565b604051906060820182811067ffffffffffffffff82111761341157604052565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b604051906040820182811067ffffffffffffffff82111761341157604052565b60405190610100820182811067ffffffffffffffff82111761341157604052565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60609101126101f5576134b16133f1565b9060043573ffffffffffffffffffffffffffffffffffffffff811681036101f557825260243573ffffffffffffffffffffffffffffffffffffffff811681036101f55760208301526044356040830152565b8181019160ff1c8183118116918310901516178160801c176135225790565b631293d6fa5f526004601cfd5b906135386133f1565b9180547fffffffffffffffffffffffffffffffff000000000000000000000000000000008160801b16845260801c6020840152600261357561343e565b9160018101548352015460208201526040830152565b7f80000000000000000000000000000000000000000000000000000000000000008114611131575f0390565b6135bf614279565b903373ffffffffffffffffffffffffffffffffffffffff8316036135df57565b7feed0f119000000000000000000000000000000000000000000000000000000005f5260045ffd5b5f929182810784139083900503630554777f01600881901c9060ff165b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60018060ff858701549416011b01161e806101000361373357507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffaab8881839160081b0102937ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffab6326d8560030b131561370a578015613703577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff908101946136249184910160030b630554777f915f81830712910503019060ff8260081c921690565b5050509091565b50505090507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffab6326d91565b945090507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffaab8880919250600193610100039060081b01010291565b91909161379f825f945b63ffffffff821660030b0160030b630554777f915f81830712910503019060ff8260081c921690565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600160ff8486015493161b011916805f03161e806101000361373357507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffaab8980839160081b010293630549cd938560030b1215613849578015613703577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff019361379f908390613776565b5050509050630549cd9391565b908160030b918260ff1d8084011890630549cd938211613cde576138d49293506d08637b66cd638344daef276cd7c56001831602700100000000000000000000000000000000039160fe8116613bdb575b617f008116613ad1575b621f800081166139e8575b6307e0000081166138fe575b505f126138d757614647565b90565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04614647565b6220000081166139cc575b6240000081166139b0575b628000008116613994575b63010000008116613979575b6302000000811661395f575b630400000016613948575b5f6138c8565b69c0d55d4d7152c25fb13990910260801c90613942565b6cde2ee4bc381afa7089aa84bb6690920260801c91613937565b916e0ee7e32d61fdb0a5e622b820f681d00260801c9161392b565b916f03dc5dac7376e20fc8679758d1bcdcfc0260801c9161391f565b916f1f703399d88f6aa83a28b22d4a1f56e30260801c91613914565b916f59b63684b86e9f486ec54727371ba6ca0260801c91613909565b6180008116613ab5575b620100008116613a99575b620200008116613a7d575b620400008116613a61575b620800008116613a45575b621000008116156138bc57916f978bcb9894317807e5fa4498eee7c0fa0260801c916138bc565b916fc4f76b68947482dc198a48a54348c4ed0260801c91613a1e565b916fe08d35706200796273f0b3a981d90cfd0260801c91613a13565b916fefc2bf59df33ecc28125cf78ec4f167f0260801c91613a08565b916ff7bf5211c72f5185f372aeb1d48f937e0260801c916139fd565b916ffbd701c7cbc4c8a6bb81efd232d1e4e70260801c916139f2565b6101008116613bbf575b6102008116613ba3575b6104008116613b87575b6108008116613b6b575b6110008116613b4f575b6120008116613b33575b6140008116156138b157916ffde95287d26d81bea159c37073122c730260801c916138b1565b916ffef41d1a5f2ae3a20676bec6f7f9459a0260801c91613b0d565b916fff79eb706b9a64c6431d76e63531e9290260801c91613b03565b916fffbceceeb791747f10df216f2e53ec570260801c91613af9565b916fffde7444b28145508125d10077ba83b80260801c91613aef565b916fffef3995a5b6a6267530f207142a57640260801c91613ae5565b916ffff79ca7a4d1bf1ee8556cea23cdbaa50260801c91613adb565b60028116613cc2575b60048116613ca6575b60088116613c8a575b60108116613c6e575b60208116613c52575b60408116613c36575b60808116156138a757916ffffbce4b06c196e9247ac87695d53c600260801c916138a7565b916ffffde72350725cc4ea8feece3b5f13c80260801c91613c11565b916ffffef3911b7cff24ba1b3dbb5f8f59740260801c91613c08565b916fffff79c86a8f6150a32d9778eceef97c0260801c91613bff565b916fffffbce42c7be6c998ad6318193c0b180260801c91613bf6565b916fffffde72140b00a354bd3dc828e976c90260801c91613bed565b916fffffef390978c398134b4ff3764fe4100260801c91613be4565b837f073ee172000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b909180600f0b918215613dfa57613d4d5f9160607fffffffffffffffffffffffffffffffffffffffff3fffffffffffffffffffffff82169160591c166002011b90565b9360801b921215613de6575f03827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048111613dd35782029181831015613dd357613d9a92820391614808565b77fffffffffffffffffffffffc0000000000000000000000008111613dc2576138d490614719565b506bffffffffffffffffffffffff90565b5050506bffffffffffffffffffffffff90565b6138d4928204018082061515910401614719565b9250505090565b613e369060607fffffffffffffffffffffffffffffffffffffffff3fffffffffffffffffffffff82169160591c166002011b90565b915f81600f0b125f14613e6257906138d492915f0360801b908082061515910401808203911102614647565b60801b0481019081108015613e7f575b613dc2576138d490614647565b5077ffffffffffffffffffffffffffffffffffffffffffffffff8111613e72565b90613f10613ede6138d495949360607fffffffffffffffffffffffffffffffffffffffff3fffffffffffffffffffffff82169160591c166002011b90565b9160607fffffffffffffffffffffffffffffffffffffffff3fffffffffffffffffffffff82169160591c166002011b90565b80821181830302809101910361406a565b90613f5f613ede6138d495949360607fffffffffffffffffffffffffffffffffffffffff3fffffffffffffffffffffff82169160591c166002011b90565b808211818303028091019103613f9f565b60401b9068010000000000000000038082049106151501908160801c613f9257565b630d88f5265f526004601cfd5b929160801b915f14613fd957613fb89183820390614808565b9080820615159104018060801c613fcc5790565b63b4ef25465f526004601cfd5b828103908183027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8385099382805f0381169485920992048060030260021880820260020302808202600203028082026002030280820260020302808202600203028091026002030293816001858583030495805f03040193119180821001900303021702048060801c613fcc5790565b900391156140ac578061408f70010000000000000000000000000000000092846148d2565b92091515018060801c61409f5790565b6359d2b24a5f526004601cfd5b6140b5916148d2565b8060801c61409f5790565b806f800000000000000000000000000000000160801c156140e8576335278d125f526004601cfd5b600f0b90565b909392919034614104579361410294614383565b565b9173ffffffffffffffffffffffffffffffffffffffff851661412e57936141029434900392614383565b8293916141029561413e94614383565b345f03906142cc565b93929161415261343e565b60208101938196838101907fd0bf0a0be44c3c54cf61da346063acd98e9363a6de122b16472a1947527c5c9e7f5695060fdb9cfea656f872ae4887221aff7dbfefc45eaf753e4e70cdfb5cd19c83015492015494838201937fd0bf0a0be44c3c54cf61da346063acd98e9363a6de122b16472a1947527c5c9e7f5695060fdb9cfea656f872ae4887221aff7dbfefc45eaf753e4e70cdfb5cd19c8601549501549760030b9160030b82125f1461420d57505050039052039052565b9596959194939160030b1315614270577fb09b03866d96933565a9435bfb511c8ac5b2be454285ca331201452704799f737fb09b03866d96933565a9435bfb511c8ac5b2be454285ca331201452704799f72850154940154930303905203039052565b03905250039052565b7f07cc7f5195d862f505d6b095c82f92e00cfc1766f5bca4383c28dc5fca1555fd5c9081156142a457565b7f1834e265000000000000000000000000000000000000000000000000000000005f5260045ffd5b8060a01b7f753dfe4b4dfb3ff6c11bbf6a97f3c094e91c003ce904a55cc5662fbad220f5990190815c928301928315901503908161430a575b50505d565b7f7772acfd7e0f66ebb20a058830296c3dc1301b111d23348e1c961d324223190d0190815c01905d5f80614305565b919061437f6fffffffffffffffffffffffffffffffff9182614378816040602089015198015193602080865183510396015191015103971680936148d2565b16946148d2565b1690565b92809492915f9561440d575b5050816143cc575b5050816143a2575050565b7f7772acfd7e0f66ebb20a058830296c3dc1301b111d23348e1c961d324223190d0190815c01905d565b8260a09492941b017f753dfe4b4dfb3ff6c11bbf6a97f3c094e91c003ce904a55cc5662fbad220f5990190815c938401938415901503019283915d5f614397565b839195508460a01b017f753dfe4b4dfb3ff6c11bbf6a97f3c094e91c003ce904a55cc5662fbad220f5990194855c908101908115901503955d5f61438f565b9060030b90811561445e5760030b0790565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b90925f905f94600f0b9384156145b0575f8513936fffffffffffffffffffffffffffffffff8560011b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01968060ff1d8091011816936bffffffffffffffffffffffff82166bffffffffffffffffffffffff84168111155f1461453657505050926fffffffffffffffffffffffffffffffff9261452c926145339695613f21565b16026140c0565b91565b929897509092916bffffffffffffffffffffffff8316111561458c57509282826145866fffffffffffffffffffffffffffffffff61457e8582986138d49b9a61452c99613f21565b1687026140c0565b98613ea0565b9661452c9250926138d49594916fffffffffffffffffffffffffffffffff94613ea0565b5050505050505f905f90565b01907fffffffffffffffffffffffffffffffff0000000000000000000000000000000082166145e757565b7f6d862c50000000000000000000000000000000000000000000000000000000005f5260045ffd5b61461761345e565b905f82525f60208301525f60408301525f60608301525f60808301525f60a08301525f60c08301525f60e0830152565b6c0100000000000000000000000081106147135770010000000000000000000000000000000081106146ff577401000000000000000000000000000000000000000081106146eb57780100000000000000000000000000000000000000000000000081106146d7577fa10459f4000000000000000000000000000000000000000000000000000000005f5260045ffd5b60621c6bc000000000000000000000001790565b60421c6b8000000000000000000000001790565b60221c6b4000000000000000000000001790565b60021c90565b600381016c01000000000000000000000000811061480057506403ffffffff810170010000000000000000000000000000000081106147ea57506803ffffffffffffffff8101907401000000000000000000000000000000000000000082106147d5576c03ffffffffffffffffffffffff915001780100000000000000000000000000000000000000000000000081106146d7577fa10459f4000000000000000000000000000000000000000000000000000000005f5260045ffd5b5060421c6b8000000000000000000000001790565b905060221c6b4000000000000000000000001790565b905060021c90565b92918082850281861585888404141702156148445704915b82940961482a5750565b6001019150811561483757565b63ae47f7025f526004601cfd5b90507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8386098181108201900382848709835f03841691808511156148375782850480600302600218808202600203028082026002030280820260020302808202600203028082026002030280910260020302936001848483030494805f0304019211900302170291614820565b818102918082840414821517156148eb57505060801c90565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff910981811082019003908160801c1561492c5763ae47f7025f526004601cfd5b60801c9060801b019056fea26469706673582212208f4790146f3838995be055c447caa079afb5b6eeabdaeb4a4501328670e4187e64736f6c63430008210033
Loading...
Loading
Loading...
Loading
Net Worth in USD
$6,063,581.07
Net Worth in ETH
2,062.925399
Token Allocations
USDT
28.99%
ETH
19.58%
USDC
17.17%
Others
34.27%
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|---|---|---|---|---|
| ETH | 28.99% | $0.999029 | 1,759,240.6251 | $1,757,532.4 | |
| ETH | 19.51% | $2,939.75 | 402.4108 | $1,182,986.77 | |
| ETH | 17.11% | $0.999757 | 1,037,779.2299 | $1,037,527.05 | |
| ETH | 15.93% | $3,613.55 | 267.2483 | $965,715.16 | |
| ETH | 8.11% | $89,048 | 5.5237 | $491,871.59 | |
| ETH | 2.38% | $2.29 | 63,054.6564 | $144,395.16 | |
| ETH | 2.04% | $0.999354 | 123,782.0331 | $123,702.07 | |
| ETH | 1.88% | $89,231 | 1.2785 | $114,084.24 | |
| ETH | 1.22% | $89,144 | 0.8306 | $74,042.89 | |
| ETH | 0.75% | $0.99937 | 45,441.8226 | $45,413.19 | |
| ETH | 0.74% | $0.999419 | 44,961.9943 | $44,935.87 | |
| ETH | 0.67% | $1.79 | 22,859.7397 | $40,870.86 | |
| ETH | 0.29% | $1 | 17,598.6042 | $17,598.6 | |
| ETH | 0.17% | $1.18 | 8,535.2771 | $10,071.63 | |
| ETH | 0.09% | $4,941.85 | 1.0756 | $5,315.34 | |
| ETH | <0.01% | $0.999968 | 4.3206 | $4.32 | |
| ARB | 0.07% | $2,940.13 | 1.3625 | $4,005.89 | |
| ARB | 0.06% | $0.999701 | 3,506.1236 | $3,505.08 | |
| ARB | <0.01% | $0.998908 | 2.9534 | $2.95 |
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.