Transaction Hash:
Block:
24260635 at Jan-18-2026 09:06:35 AM +UTC
Transaction Fee:
0.000524123183552241 ETH
$1.02
Gas Used:
245,973 Gas / 2.130815917 Gwei
Emitted Events:
| 106 |
TetherToken.Transfer( from=[Sender] 0x60338b6c1f2906f33e449f6d7e47cf181b646bfa, to=0x74de5d4FCbf63E00296fd95d33236B9794016631, value=27166350 )
|
| 107 |
TetherToken.Transfer( from=0x74de5d4FCbf63E00296fd95d33236B9794016631, to=0xc92814c1974355122a8a43781a090552634EE567, value=27166350 )
|
| 108 |
Core.( 14f04a89d7912a7dc3d3ec35e830efb8d2ddbddb3dd345bcb302aa14cc644c60, ffffffffffffffffffe2e7329b50baa1000000000000000000000000019e868e, 000000000000000000796ee293e7a0b74000f18aa76ab8c80bf709b0fed614cd )
|
| 109 |
TetherToken.Transfer( from=0xc92814c1974355122a8a43781a090552634EE567, to=Core, value=27166350 )
|
| 110 |
WETH9.Deposit( dst=0xc92814c1974355122a8a43781a090552634EE567, wad=8190044761179487 )
|
| 111 |
WETH9.Withdrawal( src=0xc92814c1974355122a8a43781a090552634EE567, wad=8190044761179487 )
|
| 112 |
0x881d40237659c251811cec9c364ef91dc08d300c.0xbeee1e6e7fe307ddcf84b0a16137a4430ad5e2480fc4f4a8e250ab56ccd7630d( 0xbeee1e6e7fe307ddcf84b0a16137a4430ad5e2480fc4f4a8e250ab56ccd7630d, 0x83203d0886d1c2c42122151c4282225c18a1e1561f04ae69d57528da4bb99db6, 0x00000000000000000000000060338b6c1f2906f33e449f6d7e47cf181b646bfa )
|
Account State Difference:
| Address | Before | After | State Difference | ||
|---|---|---|---|---|---|
|
0x4838B106...B0BAD5f97
Miner
| (Titan Builder) | 3.333521031984643657 Eth | 3.33403757528488963 Eth | 0.000516543300245973 | |
| 0x60338b6C...81B646BfA |
0.000792403002285787 Eth
Nonce: 123
|
0.007483460207680255 Eth
Nonce: 124
| 0.006691057205394468 | ||
| 0xdAC17F95...13D831ec7 | |||||
| 0xe0e0e08A...48157d444 | (Ekubo: Core) | 120.651075697877158586 Eth | 120.642885653115979099 Eth | 0.008190044761179487 | |
| 0xe3478b0B...948Be1964 | (MetaMask: Gas Station Swap) | 428.739949821127917114 Eth | 428.740924685500149892 Eth | 0.000974864372232778 |
Execution Trace
Metamask: Swap Router.5f575529( )
-
TetherToken.transferFrom( _from=0x60338b6C1f2906F33e449f6d7e47Cf181B646BfA, _to=0x74de5d4FCbf63E00296fd95d33236B9794016631, _value=27166350 )
MetaMask: Swaps Spender.e3547335( )0x3261d7831dcd2ca040696a095a94b68f2cd204b8.92f5f037( )-
TetherToken.allowance( _owner=0x74de5d4FCbf63E00296fd95d33236B9794016631, _spender=0x0000000000001fF3684f28c67538d4D072C22734 ) => ( remaining=115792089237316195423570985008687907853269984665640564039457584007913129639935 )
AllowanceHolder.2213bc0b( )-
0xc92814c1974355122a8a43781a090552634ee567.70a08231( ) 0xc92814c1974355122a8a43781a090552634ee567.1fff991f( )AllowanceHolder.15dacbea( )-
TetherToken.transferFrom( _from=0x74de5d4FCbf63E00296fd95d33236B9794016631, _to=0xc92814c1974355122a8a43781a090552634EE567, _value=27166350 )
-
Core.lock( )-
0xc92814c1974355122a8a43781a090552634ee567.b45a3c0e( )
-
- ETH 0.008190044761179487
WETH9.deposit( )
-
WETH9.balanceOf( 0xc92814c1974355122a8a43781a090552634EE567 ) => ( 8190044761179487 )
WETH9.withdraw( wad=8190044761179487 )
- ETH 0.008190044761179487
0xc92814c1974355122a8a43781a090552634ee567.CALL( )
- ETH 0.008190044761179487
- ETH 0.008190044761179487
MetaMask: Swaps Spender.CALL( )
-
- ETH 0.000974864372232778
MetaMask: Gas Station Swap.CALL( ) -
TetherToken.balanceOf( who=0x74de5d4FCbf63E00296fd95d33236B9794016631 ) => ( 0 )
- ETH 0.007215180388946709
0x60338b6c1f2906f33e449f6d7e47cf181b646bfa.CALL( )
-
File 1 of 4: TetherToken
File 2 of 4: Core
File 3 of 4: WETH9
File 4 of 4: AllowanceHolder
pragma solidity ^0.4.17;
/**
* @title SafeMath
* @dev Math operations with safety checks that throw on error
*/
library SafeMath {
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
uint256 c = a * b;
assert(c / a == b);
return c;
}
function div(uint256 a, uint256 b) internal pure returns (uint256) {
// assert(b > 0); // Solidity automatically throws when dividing by 0
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
assert(b <= a);
return a - b;
}
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
assert(c >= a);
return c;
}
}
/**
* @title Ownable
* @dev The Ownable contract has an owner address, and provides basic authorization control
* functions, this simplifies the implementation of "user permissions".
*/
contract Ownable {
address public owner;
/**
* @dev The Ownable constructor sets the original `owner` of the contract to the sender
* account.
*/
function Ownable() public {
owner = msg.sender;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(msg.sender == owner);
_;
}
/**
* @dev Allows the current owner to transfer control of the contract to a newOwner.
* @param newOwner The address to transfer ownership to.
*/
function transferOwnership(address newOwner) public onlyOwner {
if (newOwner != address(0)) {
owner = newOwner;
}
}
}
/**
* @title ERC20Basic
* @dev Simpler version of ERC20 interface
* @dev see https://github.com/ethereum/EIPs/issues/20
*/
contract ERC20Basic {
uint public _totalSupply;
function totalSupply() public constant returns (uint);
function balanceOf(address who) public constant returns (uint);
function transfer(address to, uint value) public;
event Transfer(address indexed from, address indexed to, uint value);
}
/**
* @title ERC20 interface
* @dev see https://github.com/ethereum/EIPs/issues/20
*/
contract ERC20 is ERC20Basic {
function allowance(address owner, address spender) public constant returns (uint);
function transferFrom(address from, address to, uint value) public;
function approve(address spender, uint value) public;
event Approval(address indexed owner, address indexed spender, uint value);
}
/**
* @title Basic token
* @dev Basic version of StandardToken, with no allowances.
*/
contract BasicToken is Ownable, ERC20Basic {
using SafeMath for uint;
mapping(address => uint) public balances;
// additional variables for use if transaction fees ever became necessary
uint public basisPointsRate = 0;
uint public maximumFee = 0;
/**
* @dev Fix for the ERC20 short address attack.
*/
modifier onlyPayloadSize(uint size) {
require(!(msg.data.length < size + 4));
_;
}
/**
* @dev transfer token for a specified address
* @param _to The address to transfer to.
* @param _value The amount to be transferred.
*/
function transfer(address _to, uint _value) public onlyPayloadSize(2 * 32) {
uint fee = (_value.mul(basisPointsRate)).div(10000);
if (fee > maximumFee) {
fee = maximumFee;
}
uint sendAmount = _value.sub(fee);
balances[msg.sender] = balances[msg.sender].sub(_value);
balances[_to] = balances[_to].add(sendAmount);
if (fee > 0) {
balances[owner] = balances[owner].add(fee);
Transfer(msg.sender, owner, fee);
}
Transfer(msg.sender, _to, sendAmount);
}
/**
* @dev Gets the balance of the specified address.
* @param _owner The address to query the the balance of.
* @return An uint representing the amount owned by the passed address.
*/
function balanceOf(address _owner) public constant returns (uint balance) {
return balances[_owner];
}
}
/**
* @title Standard ERC20 token
*
* @dev Implementation of the basic standard token.
* @dev https://github.com/ethereum/EIPs/issues/20
* @dev Based oncode by FirstBlood: https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol
*/
contract StandardToken is BasicToken, ERC20 {
mapping (address => mapping (address => uint)) public allowed;
uint public constant MAX_UINT = 2**256 - 1;
/**
* @dev Transfer tokens from one address to another
* @param _from address The address which you want to send tokens from
* @param _to address The address which you want to transfer to
* @param _value uint the amount of tokens to be transferred
*/
function transferFrom(address _from, address _to, uint _value) public onlyPayloadSize(3 * 32) {
var _allowance = allowed[_from][msg.sender];
// Check is not needed because sub(_allowance, _value) will already throw if this condition is not met
// if (_value > _allowance) throw;
uint fee = (_value.mul(basisPointsRate)).div(10000);
if (fee > maximumFee) {
fee = maximumFee;
}
if (_allowance < MAX_UINT) {
allowed[_from][msg.sender] = _allowance.sub(_value);
}
uint sendAmount = _value.sub(fee);
balances[_from] = balances[_from].sub(_value);
balances[_to] = balances[_to].add(sendAmount);
if (fee > 0) {
balances[owner] = balances[owner].add(fee);
Transfer(_from, owner, fee);
}
Transfer(_from, _to, sendAmount);
}
/**
* @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender.
* @param _spender The address which will spend the funds.
* @param _value The amount of tokens to be spent.
*/
function approve(address _spender, uint _value) public onlyPayloadSize(2 * 32) {
// To change the approve amount you first have to reduce the addresses`
// allowance to zero by calling `approve(_spender, 0)` if it is not
// already 0 to mitigate the race condition described here:
// https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
require(!((_value != 0) && (allowed[msg.sender][_spender] != 0)));
allowed[msg.sender][_spender] = _value;
Approval(msg.sender, _spender, _value);
}
/**
* @dev Function to check the amount of tokens than an owner allowed to a spender.
* @param _owner address The address which owns the funds.
* @param _spender address The address which will spend the funds.
* @return A uint specifying the amount of tokens still available for the spender.
*/
function allowance(address _owner, address _spender) public constant returns (uint remaining) {
return allowed[_owner][_spender];
}
}
/**
* @title Pausable
* @dev Base contract which allows children to implement an emergency stop mechanism.
*/
contract Pausable is Ownable {
event Pause();
event Unpause();
bool public paused = false;
/**
* @dev Modifier to make a function callable only when the contract is not paused.
*/
modifier whenNotPaused() {
require(!paused);
_;
}
/**
* @dev Modifier to make a function callable only when the contract is paused.
*/
modifier whenPaused() {
require(paused);
_;
}
/**
* @dev called by the owner to pause, triggers stopped state
*/
function pause() onlyOwner whenNotPaused public {
paused = true;
Pause();
}
/**
* @dev called by the owner to unpause, returns to normal state
*/
function unpause() onlyOwner whenPaused public {
paused = false;
Unpause();
}
}
contract BlackList is Ownable, BasicToken {
/////// Getters to allow the same blacklist to be used also by other contracts (including upgraded Tether) ///////
function getBlackListStatus(address _maker) external constant returns (bool) {
return isBlackListed[_maker];
}
function getOwner() external constant returns (address) {
return owner;
}
mapping (address => bool) public isBlackListed;
function addBlackList (address _evilUser) public onlyOwner {
isBlackListed[_evilUser] = true;
AddedBlackList(_evilUser);
}
function removeBlackList (address _clearedUser) public onlyOwner {
isBlackListed[_clearedUser] = false;
RemovedBlackList(_clearedUser);
}
function destroyBlackFunds (address _blackListedUser) public onlyOwner {
require(isBlackListed[_blackListedUser]);
uint dirtyFunds = balanceOf(_blackListedUser);
balances[_blackListedUser] = 0;
_totalSupply -= dirtyFunds;
DestroyedBlackFunds(_blackListedUser, dirtyFunds);
}
event DestroyedBlackFunds(address _blackListedUser, uint _balance);
event AddedBlackList(address _user);
event RemovedBlackList(address _user);
}
contract UpgradedStandardToken is StandardToken{
// those methods are called by the legacy contract
// and they must ensure msg.sender to be the contract address
function transferByLegacy(address from, address to, uint value) public;
function transferFromByLegacy(address sender, address from, address spender, uint value) public;
function approveByLegacy(address from, address spender, uint value) public;
}
contract TetherToken is Pausable, StandardToken, BlackList {
string public name;
string public symbol;
uint public decimals;
address public upgradedAddress;
bool public deprecated;
// The contract can be initialized with a number of tokens
// All the tokens are deposited to the owner address
//
// @param _balance Initial supply of the contract
// @param _name Token Name
// @param _symbol Token symbol
// @param _decimals Token decimals
function TetherToken(uint _initialSupply, string _name, string _symbol, uint _decimals) public {
_totalSupply = _initialSupply;
name = _name;
symbol = _symbol;
decimals = _decimals;
balances[owner] = _initialSupply;
deprecated = false;
}
// Forward ERC20 methods to upgraded contract if this one is deprecated
function transfer(address _to, uint _value) public whenNotPaused {
require(!isBlackListed[msg.sender]);
if (deprecated) {
return UpgradedStandardToken(upgradedAddress).transferByLegacy(msg.sender, _to, _value);
} else {
return super.transfer(_to, _value);
}
}
// Forward ERC20 methods to upgraded contract if this one is deprecated
function transferFrom(address _from, address _to, uint _value) public whenNotPaused {
require(!isBlackListed[_from]);
if (deprecated) {
return UpgradedStandardToken(upgradedAddress).transferFromByLegacy(msg.sender, _from, _to, _value);
} else {
return super.transferFrom(_from, _to, _value);
}
}
// Forward ERC20 methods to upgraded contract if this one is deprecated
function balanceOf(address who) public constant returns (uint) {
if (deprecated) {
return UpgradedStandardToken(upgradedAddress).balanceOf(who);
} else {
return super.balanceOf(who);
}
}
// Forward ERC20 methods to upgraded contract if this one is deprecated
function approve(address _spender, uint _value) public onlyPayloadSize(2 * 32) {
if (deprecated) {
return UpgradedStandardToken(upgradedAddress).approveByLegacy(msg.sender, _spender, _value);
} else {
return super.approve(_spender, _value);
}
}
// Forward ERC20 methods to upgraded contract if this one is deprecated
function allowance(address _owner, address _spender) public constant returns (uint remaining) {
if (deprecated) {
return StandardToken(upgradedAddress).allowance(_owner, _spender);
} else {
return super.allowance(_owner, _spender);
}
}
// deprecate current contract in favour of a new one
function deprecate(address _upgradedAddress) public onlyOwner {
deprecated = true;
upgradedAddress = _upgradedAddress;
Deprecate(_upgradedAddress);
}
// deprecate current contract if favour of a new one
function totalSupply() public constant returns (uint) {
if (deprecated) {
return StandardToken(upgradedAddress).totalSupply();
} else {
return _totalSupply;
}
}
// Issue a new amount of tokens
// these tokens are deposited into the owner address
//
// @param _amount Number of tokens to be issued
function issue(uint amount) public onlyOwner {
require(_totalSupply + amount > _totalSupply);
require(balances[owner] + amount > balances[owner]);
balances[owner] += amount;
_totalSupply += amount;
Issue(amount);
}
// Redeem tokens.
// These tokens are withdrawn from the owner address
// if the balance must be enough to cover the redeem
// or the call will fail.
// @param _amount Number of tokens to be issued
function redeem(uint amount) public onlyOwner {
require(_totalSupply >= amount);
require(balances[owner] >= amount);
_totalSupply -= amount;
balances[owner] -= amount;
Redeem(amount);
}
function setParams(uint newBasisPoints, uint newMaxFee) public onlyOwner {
// Ensure transparency by hardcoding limit beyond which fees can never be added
require(newBasisPoints < 20);
require(newMaxFee < 50);
basisPointsRate = newBasisPoints;
maximumFee = newMaxFee.mul(10**decimals);
Params(basisPointsRate, maximumFee);
}
// Called when new token are issued
event Issue(uint amount);
// Called when tokens are redeemed
event Redeem(uint amount);
// Called when contract is deprecated
event Deprecate(address newAddress);
// Called if contract ever adds fees
event Params(uint feeBasisPoints, uint maxFee);
}File 2 of 4: Core
// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.28;
import {CallPoints, addressToCallPoints} from "./types/callPoints.sol";
import {PoolKey} from "./types/poolKey.sol";
import {PositionKey, Bounds} from "./types/positionKey.sol";
import {FeesPerLiquidity, feesPerLiquidityFromAmounts} from "./types/feesPerLiquidity.sol";
import {isPriceIncreasing, SqrtRatioLimitWrongDirection, SwapResult, swapResult} from "./math/swap.sol";
import {Position} from "./types/position.sol";
import {Ownable} from "solady/auth/Ownable.sol";
import {tickToSqrtRatio, sqrtRatioToTick} from "./math/ticks.sol";
import {Bitmap} from "./math/bitmap.sol";
import {
shouldCallBeforeInitializePool,
shouldCallAfterInitializePool,
shouldCallBeforeUpdatePosition,
shouldCallAfterUpdatePosition,
shouldCallBeforeSwap,
shouldCallAfterSwap,
shouldCallBeforeCollectFees,
shouldCallAfterCollectFees
} from "./types/callPoints.sol";
import {FixedPointMathLib} from "solady/utils/FixedPointMathLib.sol";
import {SafeTransferLib} from "solady/utils/SafeTransferLib.sol";
import {SafeCastLib} from "solady/utils/SafeCastLib.sol";
import {ExposedStorage} from "./base/ExposedStorage.sol";
import {liquidityDeltaToAmountDelta, addLiquidityDelta, subLiquidityDelta} from "./math/liquidity.sol";
import {computeFee} from "./math/fee.sol";
import {findNextInitializedTick, findPrevInitializedTick, flipTick} from "./math/tickBitmap.sol";
import {ICore, UpdatePositionParameters, IExtension} from "./interfaces/ICore.sol";
import {FlashAccountant} from "./base/FlashAccountant.sol";
import {EfficientHashLib} from "solady/utils/EfficientHashLib.sol";
import {
MIN_TICK,
MAX_TICK,
NATIVE_TOKEN_ADDRESS,
FULL_RANGE_ONLY_TICK_SPACING,
MAX_TICK_SPACING
} from "./math/constants.sol";
import {MIN_SQRT_RATIO, MAX_SQRT_RATIO, SqrtRatio} from "./types/sqrtRatio.sol";
/// @title Ekubo Protocol
/// @author Moody Salem <moody@ekubo.org>
/// @notice Singleton holding all the tokens and containing all the possible operations in Ekubo Protocol
contract Core is ICore, FlashAccountant, Ownable, ExposedStorage {
using {findNextInitializedTick, findPrevInitializedTick, flipTick} for mapping(uint256 word => Bitmap bitmap);
struct TickInfo {
int128 liquidityDelta;
uint128 liquidityNet;
}
struct PoolState {
SqrtRatio sqrtRatio;
int32 tick;
uint128 liquidity;
}
mapping(address extension => bool isRegistered) private isExtensionRegistered;
mapping(address token => uint256 amountCollected) private protocolFeesCollected;
mapping(bytes32 poolId => PoolState) private poolState;
mapping(bytes32 poolId => FeesPerLiquidity feesPerLiquidity) private poolFeesPerLiquidity;
mapping(bytes32 poolId => mapping(bytes32 positionId => Position position)) private poolPositions;
mapping(bytes32 poolId => mapping(int32 tick => TickInfo tickInfo)) private poolTicks;
mapping(bytes32 poolId => mapping(int32 tick => FeesPerLiquidity feesPerLiquidityOutside)) private
poolTickFeesPerLiquidityOutside;
mapping(bytes32 poolId => mapping(uint256 word => Bitmap bitmap)) private poolInitializedTickBitmaps;
mapping(bytes32 key => uint256) private savedBalances;
constructor(address owner) {
_initializeOwner(owner);
}
function withdrawProtocolFees(address recipient, address token, uint256 amount) external onlyOwner {
protocolFeesCollected[token] -= amount;
if (token == NATIVE_TOKEN_ADDRESS) {
SafeTransferLib.safeTransferETH(recipient, amount);
} else {
SafeTransferLib.safeTransfer(token, recipient, amount);
}
emit ProtocolFeesWithdrawn(recipient, token, amount);
}
// Extensions must call this function to become registered. The call points are validated against the caller address
function registerExtension(CallPoints memory expectedCallPoints) external {
CallPoints memory computed = addressToCallPoints(msg.sender);
if (!computed.eq(expectedCallPoints) || !computed.isValid()) revert FailedRegisterInvalidCallPoints();
if (isExtensionRegistered[msg.sender]) revert ExtensionAlreadyRegistered();
isExtensionRegistered[msg.sender] = true;
emit ExtensionRegistered(msg.sender);
}
function initializePool(PoolKey memory poolKey, int32 tick) external returns (SqrtRatio sqrtRatio) {
poolKey.validatePoolKey();
address extension = poolKey.extension();
if (extension != address(0)) {
if (!isExtensionRegistered[extension]) {
revert ExtensionNotRegistered();
}
if (shouldCallBeforeInitializePool(extension) && extension != msg.sender) {
IExtension(extension).beforeInitializePool(msg.sender, poolKey, tick);
}
}
bytes32 poolId = poolKey.toPoolId();
PoolState memory price = poolState[poolId];
if (SqrtRatio.unwrap(price.sqrtRatio) != 0) revert PoolAlreadyInitialized();
sqrtRatio = tickToSqrtRatio(tick);
poolState[poolId] = PoolState({sqrtRatio: sqrtRatio, tick: tick, liquidity: 0});
emit PoolInitialized(poolId, poolKey, tick, sqrtRatio);
if (shouldCallAfterInitializePool(extension) && extension != msg.sender) {
IExtension(extension).afterInitializePool(msg.sender, poolKey, tick, sqrtRatio);
}
}
function prevInitializedTick(bytes32 poolId, int32 fromTick, uint32 tickSpacing, uint256 skipAhead)
external
view
returns (int32 tick, bool isInitialized)
{
(tick, isInitialized) =
poolInitializedTickBitmaps[poolId].findPrevInitializedTick(fromTick, tickSpacing, skipAhead);
}
function nextInitializedTick(bytes32 poolId, int32 fromTick, uint32 tickSpacing, uint256 skipAhead)
external
view
returns (int32 tick, bool isInitialized)
{
(tick, isInitialized) =
poolInitializedTickBitmaps[poolId].findNextInitializedTick(fromTick, tickSpacing, skipAhead);
}
function load(address token0, address token1, bytes32 salt, uint128 amount0, uint128 amount1) public {
// note we do not check sort order because for save it must be sorted,
// so balances will always be zero if token0 and token1 are not sorted
// and this method will throw InsufficientSavedBalance for non-zero amount
(uint256 id,) = _getLocker();
bytes32 key = EfficientHashLib.hash(
bytes32(uint256(uint160(msg.sender))),
bytes32(uint256(uint160(token0))),
bytes32(uint256(uint160(token1))),
salt
);
unchecked {
uint256 packedBalance = savedBalances[key];
uint128 balance0 = uint128(packedBalance >> 128);
uint128 balance1 = uint128(packedBalance);
if (balance0 < amount0 || balance1 < amount1) {
revert InsufficientSavedBalance();
}
// unchecked is ok because we reverted if either balance < amount
savedBalances[key] = (uint256(balance0 - amount0) << 128) + uint256(balance1 - amount1);
_accountDebt(id, token0, -int256(uint256(amount0)));
_accountDebt(id, token1, -int256(uint256(amount1)));
}
}
function save(address owner, address token0, address token1, bytes32 salt, uint128 amount0, uint128 amount1)
public
payable
{
if (token0 >= token1) revert SavedBalanceTokensNotSorted();
(uint256 id,) = _requireLocker();
bytes32 key = EfficientHashLib.hash(
bytes32(uint256(uint160(owner))), bytes32(uint256(uint160(token0))), bytes32(uint256(uint160(token1))), salt
);
uint256 packedBalances = savedBalances[key];
uint128 balance0 = uint128(packedBalances >> 128);
uint128 balance1 = uint128(packedBalances);
// we are using checked math here to protect the uint128 additions from overflowing
savedBalances[key] = (uint256(balance0 + amount0) << 128) + uint256(balance1 + amount1);
_maybeAccountDebtToken0(id, token0, int256(uint256(amount0)));
_accountDebt(id, token1, int256(uint256(amount1)));
}
// Returns the pool fees per liquidity inside the given bounds.
function _getPoolFeesPerLiquidityInside(bytes32 poolId, Bounds memory bounds, uint32 tickSpacing)
internal
view
returns (FeesPerLiquidity memory)
{
if (tickSpacing == FULL_RANGE_ONLY_TICK_SPACING) return poolFeesPerLiquidity[poolId];
int32 tick = poolState[poolId].tick;
mapping(int32 => FeesPerLiquidity) storage poolIdEntry = poolTickFeesPerLiquidityOutside[poolId];
FeesPerLiquidity memory lower = poolIdEntry[bounds.lower];
FeesPerLiquidity memory upper = poolIdEntry[bounds.upper];
if (tick < bounds.lower) {
return lower.sub(upper);
} else if (tick < bounds.upper) {
FeesPerLiquidity memory fees = poolFeesPerLiquidity[poolId];
return fees.sub(lower).sub(upper);
} else {
return upper.sub(lower);
}
}
function getPoolFeesPerLiquidityInside(PoolKey memory poolKey, Bounds memory bounds)
external
view
returns (FeesPerLiquidity memory)
{
return _getPoolFeesPerLiquidityInside(poolKey.toPoolId(), bounds, poolKey.tickSpacing());
}
// Accumulates tokens to fees of a pool. Only callable by the extension of the specified pool
// key, i.e. the current locker _must_ be the extension.
// The extension must call this function within a lock callback.
function accumulateAsFees(PoolKey memory poolKey, uint128 amount0, uint128 amount1) external payable {
(uint256 id, address locker) = _requireLocker();
require(locker == poolKey.extension());
bytes32 poolId = poolKey.toPoolId();
// 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.
assembly ("memory-safe") {
if or(amount0, amount1) {
mstore(0, poolId)
mstore(32, 2)
let liquidity := shr(128, sload(keccak256(0, 64)))
if liquidity {
mstore(32, 3)
let slot0 := keccak256(0, 64)
if amount0 {
let v := div(shl(128, amount0), liquidity)
sstore(slot0, add(sload(slot0), v))
}
if amount1 {
let slot1 := add(slot0, 1)
let v := div(shl(128, amount1), liquidity)
sstore(slot1, add(sload(slot1), v))
}
}
}
}
// whether the fees are actually accounted to any position, the caller owes the debt
_maybeAccountDebtToken0(id, poolKey.token0, int256(uint256(amount0)));
_accountDebt(id, poolKey.token1, int256(uint256(amount1)));
emit FeesAccumulated(poolId, amount0, amount1);
}
function _updateTick(bytes32 poolId, int32 tick, uint32 tickSpacing, int128 liquidityDelta, bool isUpper) private {
TickInfo storage tickInfo = poolTicks[poolId][tick];
uint128 liquidityNetNext = addLiquidityDelta(tickInfo.liquidityNet, liquidityDelta);
// this is checked math
int128 liquidityDeltaNext =
isUpper ? tickInfo.liquidityDelta - liquidityDelta : tickInfo.liquidityDelta + liquidityDelta;
if ((tickInfo.liquidityNet == 0) != (liquidityNetNext == 0)) {
flipTick(poolInitializedTickBitmaps[poolId], tick, tickSpacing);
}
tickInfo.liquidityDelta = liquidityDeltaNext;
tickInfo.liquidityNet = liquidityNetNext;
}
function _maybeAccountDebtToken0(uint256 id, address token0, int256 debtChange) private {
if (msg.value == 0) {
_accountDebt(id, token0, debtChange);
} else {
if (msg.value > type(uint128).max) revert PaymentOverflow();
if (token0 == NATIVE_TOKEN_ADDRESS) {
unchecked {
_accountDebt(id, NATIVE_TOKEN_ADDRESS, debtChange - int256(msg.value));
}
} else {
unchecked {
_accountDebt(id, token0, debtChange);
_accountDebt(id, NATIVE_TOKEN_ADDRESS, -int256(msg.value));
}
}
}
}
function updatePosition(PoolKey memory poolKey, UpdatePositionParameters memory params)
external
payable
returns (int128 delta0, int128 delta1)
{
(uint256 id, address locker) = _requireLocker();
address extension = poolKey.extension();
if (shouldCallBeforeUpdatePosition(extension) && locker != extension) {
IExtension(extension).beforeUpdatePosition(locker, poolKey, params);
}
params.bounds.validateBounds(poolKey.tickSpacing());
if (params.liquidityDelta != 0) {
bytes32 poolId = poolKey.toPoolId();
PoolState memory price = poolState[poolId];
if (SqrtRatio.unwrap(price.sqrtRatio) == 0) revert PoolNotInitialized();
(SqrtRatio sqrtRatioLower, SqrtRatio sqrtRatioUpper) =
(tickToSqrtRatio(params.bounds.lower), tickToSqrtRatio(params.bounds.upper));
(delta0, delta1) =
liquidityDeltaToAmountDelta(price.sqrtRatio, params.liquidityDelta, sqrtRatioLower, sqrtRatioUpper);
PositionKey memory positionKey = PositionKey({salt: params.salt, owner: locker, bounds: params.bounds});
if (params.liquidityDelta < 0) {
if (poolKey.fee() != 0) {
unchecked {
// uint128(-delta0) is ok in unchecked block
uint128 protocolFees0 = computeFee(uint128(-delta0), poolKey.fee());
uint128 protocolFees1 = computeFee(uint128(-delta1), poolKey.fee());
if (protocolFees0 > 0) {
// this will never overflow for a well behaved token since protocol fees are stored as uint256
protocolFeesCollected[poolKey.token0] += protocolFees0;
// magnitude of protocolFees0 is at most equal to -delta0, so after addition delta0 will maximally reach 0 and no overflow/underflow check is needed
// in addition, casting is safe because computed fee is never g.t. the input amount, which is an int128
delta0 += int128(protocolFees0);
}
// same reasoning applies for the unchecked safety here
if (protocolFees1 > 0) {
protocolFeesCollected[poolKey.token1] += protocolFees1;
delta1 += int128(protocolFees1);
}
}
}
}
bytes32 positionId = positionKey.toPositionId();
Position storage position = poolPositions[poolId][positionId];
FeesPerLiquidity memory feesPerLiquidityInside =
_getPoolFeesPerLiquidityInside(poolId, params.bounds, poolKey.tickSpacing());
(uint128 fees0, uint128 fees1) = position.fees(feesPerLiquidityInside);
uint128 liquidityNext = addLiquidityDelta(position.liquidity, params.liquidityDelta);
if (liquidityNext != 0) {
position.liquidity = liquidityNext;
position.feesPerLiquidityInsideLast =
feesPerLiquidityInside.sub(feesPerLiquidityFromAmounts(fees0, fees1, liquidityNext));
} else {
if (fees0 != 0 || fees1 != 0) revert MustCollectFeesBeforeWithdrawingAllLiquidity();
position.liquidity = 0;
position.feesPerLiquidityInsideLast = FeesPerLiquidity(0, 0);
}
if (!poolKey.isFullRange()) {
_updateTick(poolId, params.bounds.lower, poolKey.tickSpacing(), params.liquidityDelta, false);
_updateTick(poolId, params.bounds.upper, poolKey.tickSpacing(), params.liquidityDelta, true);
if (price.tick >= params.bounds.lower && price.tick < params.bounds.upper) {
poolState[poolId].liquidity = addLiquidityDelta(poolState[poolId].liquidity, params.liquidityDelta);
}
} else {
poolState[poolId].liquidity = addLiquidityDelta(poolState[poolId].liquidity, params.liquidityDelta);
}
_maybeAccountDebtToken0(id, poolKey.token0, delta0);
_accountDebt(id, poolKey.token1, delta1);
emit PositionUpdated(locker, poolId, params, delta0, delta1);
}
if (shouldCallAfterUpdatePosition(extension) && locker != extension) {
IExtension(extension).afterUpdatePosition(locker, poolKey, params, delta0, delta1);
}
}
function collectFees(PoolKey memory poolKey, bytes32 salt, Bounds memory bounds)
external
returns (uint128 amount0, uint128 amount1)
{
(uint256 id, address locker) = _requireLocker();
address extension = poolKey.extension();
if (shouldCallBeforeCollectFees(extension) && locker != extension) {
IExtension(extension).beforeCollectFees(locker, poolKey, salt, bounds);
}
bytes32 poolId = poolKey.toPoolId();
PositionKey memory positionKey = PositionKey({salt: salt, owner: locker, bounds: bounds});
bytes32 positionId = positionKey.toPositionId();
Position memory position = poolPositions[poolId][positionId];
FeesPerLiquidity memory feesPerLiquidityInside =
_getPoolFeesPerLiquidityInside(poolId, bounds, poolKey.tickSpacing());
(amount0, amount1) = position.fees(feesPerLiquidityInside);
poolPositions[poolId][positionId] =
Position({liquidity: position.liquidity, feesPerLiquidityInsideLast: feesPerLiquidityInside});
_accountDebt(id, poolKey.token0, -int256(uint256(amount0)));
_accountDebt(id, poolKey.token1, -int256(uint256(amount1)));
emit PositionFeesCollected(poolId, positionKey, amount0, amount1);
if (shouldCallAfterCollectFees(extension) && locker != extension) {
IExtension(extension).afterCollectFees(locker, poolKey, salt, bounds, amount0, amount1);
}
}
function swap_611415377(
PoolKey memory poolKey,
int128 amount,
bool isToken1,
SqrtRatio sqrtRatioLimit,
uint256 skipAhead
) external payable returns (int128 delta0, int128 delta1) {
if (!sqrtRatioLimit.isValid()) revert InvalidSqrtRatioLimit();
(uint256 id, address locker) = _requireLocker();
address extension = poolKey.extension();
if (shouldCallBeforeSwap(extension) && locker != extension) {
IExtension(extension).beforeSwap(locker, poolKey, amount, isToken1, sqrtRatioLimit, skipAhead);
}
bytes32 poolId = poolKey.toPoolId();
SqrtRatio sqrtRatio;
int32 tick;
uint128 liquidity;
{
PoolState storage state = poolState[poolId];
(sqrtRatio, tick, liquidity) = (state.sqrtRatio, state.tick, state.liquidity);
}
if (sqrtRatio.isZero()) revert PoolNotInitialized();
// 0 swap amount is no-op
if (amount != 0) {
bool increasing = isPriceIncreasing(amount, isToken1);
if (increasing) {
if (sqrtRatioLimit < sqrtRatio) revert SqrtRatioLimitWrongDirection();
} else {
if (sqrtRatioLimit > sqrtRatio) revert SqrtRatioLimitWrongDirection();
}
int128 amountRemaining = amount;
uint128 calculatedAmount = 0;
// the slot where inputTokenFeesPerLiquidity is stored, reused later
bytes32 inputTokenFeesPerLiquiditySlot;
// fees per liquidity only for the input token
uint256 inputTokenFeesPerLiquidity;
// this loads only the input token fees per liquidity
if (poolKey.mustLoadFees()) {
assembly ("memory-safe") {
mstore(0, poolId)
mstore(32, 3)
inputTokenFeesPerLiquiditySlot := add(keccak256(0, 64), increasing)
inputTokenFeesPerLiquidity := sload(inputTokenFeesPerLiquiditySlot)
}
}
while (amountRemaining != 0 && sqrtRatio != sqrtRatioLimit) {
int32 nextTick;
bool isInitialized;
SqrtRatio nextTickSqrtRatio;
SwapResult memory result;
if (poolKey.tickSpacing() != FULL_RANGE_ONLY_TICK_SPACING) {
(nextTick, isInitialized) = increasing
? poolInitializedTickBitmaps[poolId].findNextInitializedTick(tick, poolKey.tickSpacing(), skipAhead)
: poolInitializedTickBitmaps[poolId].findPrevInitializedTick(tick, poolKey.tickSpacing(), skipAhead);
nextTickSqrtRatio = tickToSqrtRatio(nextTick);
} else {
// we never cross ticks in the full range version
// isInitialized = false;
(nextTick, nextTickSqrtRatio) = increasing ? (MAX_TICK, MAX_SQRT_RATIO) : (MIN_TICK, MIN_SQRT_RATIO);
}
SqrtRatio limitedNextSqrtRatio =
increasing ? nextTickSqrtRatio.min(sqrtRatioLimit) : nextTickSqrtRatio.max(sqrtRatioLimit);
result =
swapResult(sqrtRatio, liquidity, limitedNextSqrtRatio, amountRemaining, isToken1, poolKey.fee());
// this accounts the fees into the feesPerLiquidity memory struct
assembly ("memory-safe") {
// div by 0 returns 0, so it's ok
let v := div(shl(128, mload(add(result, 96))), liquidity)
inputTokenFeesPerLiquidity := add(inputTokenFeesPerLiquidity, v)
}
amountRemaining -= result.consumedAmount;
calculatedAmount += result.calculatedAmount;
if (result.sqrtRatioNext == nextTickSqrtRatio) {
sqrtRatio = result.sqrtRatioNext;
tick = increasing ? nextTick : nextTick - 1;
if (isInitialized) {
int128 liquidityDelta = poolTicks[poolId][nextTick].liquidityDelta;
liquidity = increasing
? addLiquidityDelta(liquidity, liquidityDelta)
: subLiquidityDelta(liquidity, liquidityDelta);
FeesPerLiquidity memory tickFpl = poolTickFeesPerLiquidityOutside[poolId][nextTick];
FeesPerLiquidity memory totalFpl;
// load only the slot we didn't load before into totalFpl
assembly ("memory-safe") {
mstore(add(totalFpl, mul(32, increasing)), inputTokenFeesPerLiquidity)
let outputTokenFeesPerLiquidity :=
sload(add(sub(inputTokenFeesPerLiquiditySlot, increasing), iszero(increasing)))
mstore(add(totalFpl, mul(32, iszero(increasing))), outputTokenFeesPerLiquidity)
}
poolTickFeesPerLiquidityOutside[poolId][nextTick] = totalFpl.sub(tickFpl);
}
} else if (sqrtRatio != result.sqrtRatioNext) {
sqrtRatio = result.sqrtRatioNext;
tick = sqrtRatioToTick(sqrtRatio);
}
}
unchecked {
int256 calculatedAmountSign = int256(FixedPointMathLib.ternary(amount < 0, 1, type(uint256).max));
int128 calculatedAmountDelta = SafeCastLib.toInt128(
FixedPointMathLib.max(type(int128).min, calculatedAmountSign * int256(uint256(calculatedAmount)))
);
(delta0, delta1) = isToken1
? (calculatedAmountDelta, amount - amountRemaining)
: (amount - amountRemaining, calculatedAmountDelta);
}
assembly ("memory-safe") {
mstore(0, poolId)
mstore(32, 2)
sstore(keccak256(0, 64), add(add(sqrtRatio, shl(96, and(tick, 0xffffffff))), shl(128, liquidity)))
}
if (poolKey.mustLoadFees()) {
assembly ("memory-safe") {
// this stores only the input token fees per liquidity
sstore(inputTokenFeesPerLiquiditySlot, inputTokenFeesPerLiquidity)
}
}
_maybeAccountDebtToken0(id, poolKey.token0, delta0);
_accountDebt(id, poolKey.token1, delta1);
assembly ("memory-safe") {
let o := mload(0x40)
mstore(o, shl(96, locker))
mstore(add(o, 20), poolId)
mstore(add(o, 52), or(shl(128, delta0), and(delta1, 0xffffffffffffffffffffffffffffffff)))
mstore(add(o, 84), shl(128, liquidity))
mstore(add(o, 100), shl(160, sqrtRatio))
mstore(add(o, 112), shl(224, tick))
log0(o, 116)
}
}
if (shouldCallAfterSwap(extension) && locker != extension) {
IExtension(extension).afterSwap(
locker, poolKey, amount, isToken1, sqrtRatioLimit, skipAhead, delta0, delta1
);
}
}
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.28;
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
});
}
function shouldCallBeforeInitializePool(address a) pure returns (bool yes) {
assembly ("memory-safe") {
yes := and(shr(152, a), 1)
}
}
function shouldCallAfterInitializePool(address a) pure returns (bool yes) {
assembly ("memory-safe") {
yes := and(shr(159, a), 1)
}
}
function shouldCallBeforeSwap(address a) pure returns (bool yes) {
assembly ("memory-safe") {
yes := and(shr(158, a), 1)
}
}
function shouldCallAfterSwap(address a) pure returns (bool yes) {
assembly ("memory-safe") {
yes := and(shr(157, a), 1)
}
}
function shouldCallBeforeUpdatePosition(address a) pure returns (bool yes) {
assembly ("memory-safe") {
yes := and(shr(156, a), 1)
}
}
function shouldCallAfterUpdatePosition(address a) pure returns (bool yes) {
assembly ("memory-safe") {
yes := and(shr(155, a), 1)
}
}
function shouldCallBeforeCollectFees(address a) pure returns (bool yes) {
assembly ("memory-safe") {
yes := and(shr(154, a), 1)
}
}
function shouldCallAfterCollectFees(address a) pure returns (bool yes) {
assembly ("memory-safe") {
yes := and(shr(153, a), 1)
}
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.28;
import {MAX_TICK_SPACING, FULL_RANGE_ONLY_TICK_SPACING} from "../math/constants.sol";
using {toPoolId, validatePoolKey, isFullRange, mustLoadFees, tickSpacing, fee, extension} for PoolKey global;
// address (20 bytes) | fee (8 bytes) | tickSpacing (4 bytes)
type Config is bytes32;
function tickSpacing(PoolKey memory pk) pure returns (uint32 r) {
assembly ("memory-safe") {
r := and(mload(add(64, pk)), 0xffffffff)
}
}
function fee(PoolKey memory pk) pure returns (uint64 r) {
assembly ("memory-safe") {
r := and(mload(add(60, pk)), 0xffffffffffffffff)
}
}
function extension(PoolKey memory pk) pure returns (address r) {
assembly ("memory-safe") {
r := and(mload(add(52, pk)), 0xffffffffffffffffffffffffffffffffffffffff)
}
}
function mustLoadFees(PoolKey memory pk) pure returns (bool r) {
assembly ("memory-safe") {
// only if either of tick spacing and fee are nonzero
// if _both_ are zero, then we know we do not need to load fees for swaps
r := iszero(iszero(and(mload(add(64, pk)), 0xffffffffffffffffffffffff)))
}
}
function isFullRange(PoolKey memory pk) pure returns (bool r) {
r = pk.tickSpacing() == FULL_RANGE_ONLY_TICK_SPACING;
}
function toConfig(uint64 _fee, uint32 _tickSpacing, address _extension) pure returns (Config c) {
assembly ("memory-safe") {
c := add(add(shl(96, _extension), shl(32, _fee)), _tickSpacing)
}
}
// Each pool has its own state associated with this key
struct PoolKey {
address token0;
address token1;
Config config;
}
error TokensMustBeSorted();
error InvalidTickSpacing();
function validatePoolKey(PoolKey memory key) pure {
if (key.token0 >= key.token1) revert TokensMustBeSorted();
if (key.tickSpacing() > MAX_TICK_SPACING) {
revert InvalidTickSpacing();
}
}
function toPoolId(PoolKey memory key) pure returns (bytes32 result) {
assembly ("memory-safe") {
// it's already copied into memory
result := keccak256(key, 96)
}
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.28;
import {MIN_TICK, MAX_TICK, FULL_RANGE_ONLY_TICK_SPACING} from "../math/constants.sol";
using {toPositionId} for PositionKey global;
using {validateBounds} for Bounds global;
// Bounds are lower and upper prices for which a position is active
struct Bounds {
int32 lower;
int32 upper;
}
error BoundsOrder();
error MinMaxBounds();
error BoundsTickSpacing();
error FullRangeOnlyPool();
function validateBounds(Bounds memory bounds, uint32 tickSpacing) pure {
if (tickSpacing == FULL_RANGE_ONLY_TICK_SPACING) {
if (bounds.lower != MIN_TICK || bounds.upper != MAX_TICK) revert FullRangeOnlyPool();
} else {
if (bounds.lower >= bounds.upper) revert BoundsOrder();
if (bounds.lower < MIN_TICK || bounds.upper > MAX_TICK) revert MinMaxBounds();
int32 spacing = int32(tickSpacing);
if (bounds.lower % spacing != 0 || bounds.upper % spacing != 0) revert BoundsTickSpacing();
}
}
// A position is keyed by the pool and this position key
struct PositionKey {
bytes32 salt;
address owner;
Bounds bounds;
}
function toPositionId(PositionKey memory key) pure returns (bytes32 result) {
assembly ("memory-safe") {
// salt and owner
mstore(0, keccak256(key, 64))
// bounds
mstore(32, keccak256(mload(add(key, 64)), 64))
result := keccak256(0, 64)
}
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.28;
// 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: UNLICENSED
pragma solidity =0.8.28;
import {FixedPointMathLib} from "solady/utils/FixedPointMathLib.sol";
import {computeFee, amountBeforeFee} from "./fee.sol";
import {nextSqrtRatioFromAmount0, nextSqrtRatioFromAmount1} from "./sqrtRatio.sol";
import {amount0Delta, amount1Delta} from "./delta.sol";
import {SafeCastLib} from "solady/utils/SafeCastLib.sol";
import {isPriceIncreasing} from "./isPriceIncreasing.sol";
import {SqrtRatio} from "../types/sqrtRatio.sol";
struct SwapResult {
int128 consumedAmount;
uint128 calculatedAmount;
SqrtRatio sqrtRatioNext;
uint128 feeAmount;
}
function noOpSwapResult(SqrtRatio sqrtRatioNext) pure returns (SwapResult memory) {
return SwapResult({consumedAmount: 0, calculatedAmount: 0, feeAmount: 0, sqrtRatioNext: sqrtRatioNext});
}
error SqrtRatioLimitWrongDirection();
function swapResult(
SqrtRatio sqrtRatio,
uint128 liquidity,
SqrtRatio sqrtRatioLimit,
int128 amount,
bool isToken1,
uint64 fee
) pure returns (SwapResult memory) {
if (amount == 0 || sqrtRatio == sqrtRatioLimit) {
return noOpSwapResult(sqrtRatio);
}
bool increasing = isPriceIncreasing(amount, isToken1);
// We know sqrtRatio != sqrtRatioLimit because we early return above if it is
if ((sqrtRatioLimit > sqrtRatio) != increasing) revert SqrtRatioLimitWrongDirection();
if (liquidity == 0) {
// if the pool is empty, the swap will always move all the way to the limit price
return noOpSwapResult(sqrtRatioLimit);
}
bool isExactOut = amount < 0;
// this amount is what moves the price
int128 priceImpactAmount;
if (isExactOut) {
priceImpactAmount = amount;
} else {
unchecked {
// cast is safe because amount is g.t.e. 0
// then cast back to int128 is also safe because computeFee never returns a value g.t. the input amount
priceImpactAmount = amount - int128(computeFee(uint128(amount), fee));
}
}
SqrtRatio sqrtRatioNextFromAmount;
if (isToken1) {
sqrtRatioNextFromAmount = nextSqrtRatioFromAmount1(sqrtRatio, liquidity, priceImpactAmount);
} else {
sqrtRatioNextFromAmount = nextSqrtRatioFromAmount0(sqrtRatio, liquidity, priceImpactAmount);
}
int128 consumedAmount;
uint128 calculatedAmount;
uint128 feeAmount;
// the amount requires a swapping past the sqrt ratio limit,
// so we need to compute the result of swapping only to the limit
if (
(increasing && sqrtRatioNextFromAmount > sqrtRatioLimit)
|| (!increasing && sqrtRatioNextFromAmount < sqrtRatioLimit)
) {
uint128 specifiedAmountDelta;
uint128 calculatedAmountDelta;
if (isToken1) {
specifiedAmountDelta = amount1Delta(sqrtRatioLimit, sqrtRatio, liquidity, !isExactOut);
calculatedAmountDelta = amount0Delta(sqrtRatioLimit, sqrtRatio, liquidity, isExactOut);
} else {
specifiedAmountDelta = amount0Delta(sqrtRatioLimit, sqrtRatio, liquidity, !isExactOut);
calculatedAmountDelta = amount1Delta(sqrtRatioLimit, sqrtRatio, liquidity, isExactOut);
}
if (isExactOut) {
uint128 beforeFee = amountBeforeFee(calculatedAmountDelta, fee);
consumedAmount = -SafeCastLib.toInt128(specifiedAmountDelta);
calculatedAmount = beforeFee;
feeAmount = beforeFee - calculatedAmountDelta;
} else {
uint128 beforeFee = amountBeforeFee(specifiedAmountDelta, fee);
consumedAmount = SafeCastLib.toInt128(beforeFee);
calculatedAmount = calculatedAmountDelta;
feeAmount = beforeFee - specifiedAmountDelta;
}
return SwapResult({
consumedAmount: consumedAmount,
calculatedAmount: calculatedAmount,
sqrtRatioNext: sqrtRatioLimit,
feeAmount: feeAmount
});
}
if (sqrtRatioNextFromAmount == sqrtRatio) {
assert(!isExactOut);
return SwapResult({
consumedAmount: amount,
calculatedAmount: 0,
sqrtRatioNext: sqrtRatio,
feeAmount: uint128(amount)
});
}
// rounds down for calculated == output, up for calculated == input
uint128 calculatedAmountWithoutFee;
if (isToken1) {
calculatedAmountWithoutFee = amount0Delta(sqrtRatioNextFromAmount, sqrtRatio, liquidity, isExactOut);
} else {
calculatedAmountWithoutFee = amount1Delta(sqrtRatioNextFromAmount, sqrtRatio, liquidity, isExactOut);
}
// add on the fee to calculated amount for exact output
if (isExactOut) {
uint128 includingFee = amountBeforeFee(calculatedAmountWithoutFee, fee);
calculatedAmount = includingFee;
feeAmount = includingFee - calculatedAmountWithoutFee;
} else {
calculatedAmount = calculatedAmountWithoutFee;
feeAmount = uint128(amount - priceImpactAmount);
}
return SwapResult({
consumedAmount: amount,
calculatedAmount: calculatedAmount,
sqrtRatioNext: sqrtRatioNextFromAmount,
feeAmount: feeAmount
});
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.28;
import {FeesPerLiquidity} from "./feesPerLiquidity.sol";
import {FixedPointMathLib} from "solady/utils/FixedPointMathLib.sol";
struct Position {
uint128 liquidity;
FeesPerLiquidity feesPerLiquidityInsideLast;
}
using {fees} for Position global;
/// @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 overflows the uint128 type, it will return only the lower 128 bits. It is assumed that accumulated
/// fees will never exceed type(uint128).max.
function fees(Position memory position, FeesPerLiquidity memory feesPerLiquidityInside)
pure
returns (uint128, uint128)
{
FeesPerLiquidity memory difference = feesPerLiquidityInside.sub(position.feesPerLiquidityInsideLast);
return (
uint128(FixedPointMathLib.fullMulDivN(difference.value0, position.liquidity, 128)),
uint128(FixedPointMathLib.fullMulDivN(difference.value1, position.liquidity, 128))
);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Simple single owner authorization mixin.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/auth/Ownable.sol)
///
/// @dev Note:
/// This implementation does NOT auto-initialize the owner to `msg.sender`.
/// You MUST call the `_initializeOwner` in the constructor / initializer.
///
/// While the ownable portion follows
/// [EIP-173](https://eips.ethereum.org/EIPS/eip-173) for compatibility,
/// the nomenclature for the 2-step ownership handover may be unique to this codebase.
abstract contract Ownable {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The caller is not authorized to call the function.
error Unauthorized();
/// @dev The `newOwner` cannot be the zero address.
error NewOwnerIsZeroAddress();
/// @dev The `pendingOwner` does not have a valid handover request.
error NoHandoverRequest();
/// @dev Cannot double-initialize.
error AlreadyInitialized();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EVENTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The ownership is transferred from `oldOwner` to `newOwner`.
/// This event is intentionally kept the same as OpenZeppelin's Ownable to be
/// compatible with indexers and [EIP-173](https://eips.ethereum.org/EIPS/eip-173),
/// despite it not being as lightweight as a single argument event.
event OwnershipTransferred(address indexed oldOwner, address indexed newOwner);
/// @dev An ownership handover to `pendingOwner` has been requested.
event OwnershipHandoverRequested(address indexed pendingOwner);
/// @dev The ownership handover to `pendingOwner` has been canceled.
event OwnershipHandoverCanceled(address indexed pendingOwner);
/// @dev `keccak256(bytes("OwnershipTransferred(address,address)"))`.
uint256 private constant _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE =
0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0;
/// @dev `keccak256(bytes("OwnershipHandoverRequested(address)"))`.
uint256 private constant _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE =
0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d;
/// @dev `keccak256(bytes("OwnershipHandoverCanceled(address)"))`.
uint256 private constant _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE =
0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* STORAGE */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The owner slot is given by:
/// `bytes32(~uint256(uint32(bytes4(keccak256("_OWNER_SLOT_NOT")))))`.
/// It is intentionally chosen to be a high value
/// to avoid collision with lower slots.
/// The choice of manual storage layout is to enable compatibility
/// with both regular and upgradeable contracts.
bytes32 internal constant _OWNER_SLOT =
0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927;
/// The ownership handover slot of `newOwner` is given by:
/// ```
/// mstore(0x00, or(shl(96, user), _HANDOVER_SLOT_SEED))
/// let handoverSlot := keccak256(0x00, 0x20)
/// ```
/// It stores the expiry timestamp of the two-step ownership handover.
uint256 private constant _HANDOVER_SLOT_SEED = 0x389a75e1;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* INTERNAL FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Override to return true to make `_initializeOwner` prevent double-initialization.
function _guardInitializeOwner() internal pure virtual returns (bool guard) {}
/// @dev Initializes the owner directly without authorization guard.
/// This function must be called upon initialization,
/// regardless of whether the contract is upgradeable or not.
/// This is to enable generalization to both regular and upgradeable contracts,
/// and to save gas in case the initial owner is not the caller.
/// For performance reasons, this function will not check if there
/// is an existing owner.
function _initializeOwner(address newOwner) internal virtual {
if (_guardInitializeOwner()) {
/// @solidity memory-safe-assembly
assembly {
let ownerSlot := _OWNER_SLOT
if sload(ownerSlot) {
mstore(0x00, 0x0dc149f0) // `AlreadyInitialized()`.
revert(0x1c, 0x04)
}
// Clean the upper 96 bits.
newOwner := shr(96, shl(96, newOwner))
// Store the new value.
sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner))))
// Emit the {OwnershipTransferred} event.
log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)
}
} else {
/// @solidity memory-safe-assembly
assembly {
// Clean the upper 96 bits.
newOwner := shr(96, shl(96, newOwner))
// Store the new value.
sstore(_OWNER_SLOT, newOwner)
// Emit the {OwnershipTransferred} event.
log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)
}
}
}
/// @dev Sets the owner directly without authorization guard.
function _setOwner(address newOwner) internal virtual {
if (_guardInitializeOwner()) {
/// @solidity memory-safe-assembly
assembly {
let ownerSlot := _OWNER_SLOT
// Clean the upper 96 bits.
newOwner := shr(96, shl(96, newOwner))
// Emit the {OwnershipTransferred} event.
log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)
// Store the new value.
sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner))))
}
} else {
/// @solidity memory-safe-assembly
assembly {
let ownerSlot := _OWNER_SLOT
// Clean the upper 96 bits.
newOwner := shr(96, shl(96, newOwner))
// Emit the {OwnershipTransferred} event.
log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)
// Store the new value.
sstore(ownerSlot, newOwner)
}
}
}
/// @dev Throws if the sender is not the owner.
function _checkOwner() internal view virtual {
/// @solidity memory-safe-assembly
assembly {
// If the caller is not the stored owner, revert.
if iszero(eq(caller(), sload(_OWNER_SLOT))) {
mstore(0x00, 0x82b42900) // `Unauthorized()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Returns how long a two-step ownership handover is valid for in seconds.
/// Override to return a different value if needed.
/// Made internal to conserve bytecode. Wrap it in a public function if needed.
function _ownershipHandoverValidFor() internal view virtual returns (uint64) {
return 48 * 3600;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* PUBLIC UPDATE FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Allows the owner to transfer the ownership to `newOwner`.
function transferOwnership(address newOwner) public payable virtual onlyOwner {
/// @solidity memory-safe-assembly
assembly {
if iszero(shl(96, newOwner)) {
mstore(0x00, 0x7448fbae) // `NewOwnerIsZeroAddress()`.
revert(0x1c, 0x04)
}
}
_setOwner(newOwner);
}
/// @dev Allows the owner to renounce their ownership.
function renounceOwnership() public payable virtual onlyOwner {
_setOwner(address(0));
}
/// @dev Request a two-step ownership handover to the caller.
/// The request will automatically expire in 48 hours (172800 seconds) by default.
function requestOwnershipHandover() public payable virtual {
unchecked {
uint256 expires = block.timestamp + _ownershipHandoverValidFor();
/// @solidity memory-safe-assembly
assembly {
// Compute and set the handover slot to `expires`.
mstore(0x0c, _HANDOVER_SLOT_SEED)
mstore(0x00, caller())
sstore(keccak256(0x0c, 0x20), expires)
// Emit the {OwnershipHandoverRequested} event.
log2(0, 0, _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE, caller())
}
}
}
/// @dev Cancels the two-step ownership handover to the caller, if any.
function cancelOwnershipHandover() public payable virtual {
/// @solidity memory-safe-assembly
assembly {
// Compute and set the handover slot to 0.
mstore(0x0c, _HANDOVER_SLOT_SEED)
mstore(0x00, caller())
sstore(keccak256(0x0c, 0x20), 0)
// Emit the {OwnershipHandoverCanceled} event.
log2(0, 0, _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE, caller())
}
}
/// @dev Allows the owner to complete the two-step ownership handover to `pendingOwner`.
/// Reverts if there is no existing ownership handover requested by `pendingOwner`.
function completeOwnershipHandover(address pendingOwner) public payable virtual onlyOwner {
/// @solidity memory-safe-assembly
assembly {
// Compute and set the handover slot to 0.
mstore(0x0c, _HANDOVER_SLOT_SEED)
mstore(0x00, pendingOwner)
let handoverSlot := keccak256(0x0c, 0x20)
// If the handover does not exist, or has expired.
if gt(timestamp(), sload(handoverSlot)) {
mstore(0x00, 0x6f5e8818) // `NoHandoverRequest()`.
revert(0x1c, 0x04)
}
// Set the handover slot to 0.
sstore(handoverSlot, 0)
}
_setOwner(pendingOwner);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* PUBLIC READ FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the owner of the contract.
function owner() public view virtual returns (address result) {
/// @solidity memory-safe-assembly
assembly {
result := sload(_OWNER_SLOT)
}
}
/// @dev Returns the expiry timestamp for the two-step ownership handover to `pendingOwner`.
function ownershipHandoverExpiresAt(address pendingOwner)
public
view
virtual
returns (uint256 result)
{
/// @solidity memory-safe-assembly
assembly {
// Compute the handover slot.
mstore(0x0c, _HANDOVER_SLOT_SEED)
mstore(0x00, pendingOwner)
// Load the handover slot.
result := sload(keccak256(0x0c, 0x20))
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* MODIFIERS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Marks a function as only callable by the owner.
modifier onlyOwner() virtual {
_checkOwner();
_;
}
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.28;
import {MAX_TICK_SPACING, MAX_TICK_MAGNITUDE} from "./constants.sol";
import {FixedPointMathLib} from "solady/utils/FixedPointMathLib.sol";
import {SqrtRatio, toSqrtRatio} from "../types/sqrtRatio.sol";
error InvalidTick(int32 tick);
// Returns the sqrtRatio corresponding for 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") {
ratio := sub(0x100000000000000000000000000000000, mul(and(t, 0x1), 0x8637b66cd638344daef276cd7c5))
}
if ((t & 0x2) != 0) {
ratio = (ratio * 0xffffef390978c398134b4ff3764fe410) >> 128;
}
if ((t & 0x4) != 0) {
ratio = (ratio * 0xffffde72140b00a354bd3dc828e976c9) >> 128;
}
if ((t & 0x8) != 0) {
ratio = (ratio * 0xffffbce42c7be6c998ad6318193c0b18) >> 128;
}
if ((t & 0x10) != 0) {
ratio = (ratio * 0xffff79c86a8f6150a32d9778eceef97c) >> 128;
}
if ((t & 0x20) != 0) {
ratio = (ratio * 0xfffef3911b7cff24ba1b3dbb5f8f5974) >> 128;
}
if ((t & 0x40) != 0) {
ratio = (ratio * 0xfffde72350725cc4ea8feece3b5f13c8) >> 128;
}
if ((t & 0x80) != 0) {
ratio = (ratio * 0xfffbce4b06c196e9247ac87695d53c60) >> 128;
}
if ((t & 0x100) != 0) {
ratio = (ratio * 0xfff79ca7a4d1bf1ee8556cea23cdbaa5) >> 128;
}
if ((t & 0x200) != 0) {
ratio = (ratio * 0xffef3995a5b6a6267530f207142a5764) >> 128;
}
if ((t & 0x400) != 0) {
ratio = (ratio * 0xffde7444b28145508125d10077ba83b8) >> 128;
}
if ((t & 0x800) != 0) {
ratio = (ratio * 0xffbceceeb791747f10df216f2e53ec57) >> 128;
}
if ((t & 0x1000) != 0) {
ratio = (ratio * 0xff79eb706b9a64c6431d76e63531e929) >> 128;
}
if ((t & 0x2000) != 0) {
ratio = (ratio * 0xfef41d1a5f2ae3a20676bec6f7f9459a) >> 128;
}
if ((t & 0x4000) != 0) {
ratio = (ratio * 0xfde95287d26d81bea159c37073122c73) >> 128;
}
if ((t & 0x8000) != 0) {
ratio = (ratio * 0xfbd701c7cbc4c8a6bb81efd232d1e4e7) >> 128;
}
if ((t & 0x10000) != 0) {
ratio = (ratio * 0xf7bf5211c72f5185f372aeb1d48f937e) >> 128;
}
if ((t & 0x20000) != 0) {
ratio = (ratio * 0xefc2bf59df33ecc28125cf78ec4f167f) >> 128;
}
if ((t & 0x40000) != 0) {
ratio = (ratio * 0xe08d35706200796273f0b3a981d90cfd) >> 128;
}
if ((t & 0x80000) != 0) {
ratio = (ratio * 0xc4f76b68947482dc198a48a54348c4ed) >> 128;
}
if ((t & 0x100000) != 0) {
ratio = (ratio * 0x978bcb9894317807e5fa4498eee7c0fa) >> 128;
}
if ((t & 0x200000) != 0) {
ratio = (ratio * 0x59b63684b86e9f486ec54727371ba6ca) >> 128;
}
if ((t & 0x400000) != 0) {
ratio = (ratio * 0x1f703399d88f6aa83a28b22d4a1f56e3) >> 128;
}
if ((t & 0x800000) != 0) {
ratio = (ratio * 0x3dc5dac7376e20fc8679758d1bcdcfc) >> 128;
}
if ((t & 0x1000000) != 0) {
ratio = (ratio * 0xee7e32d61fdb0a5e622b820f681d0) >> 128;
}
if ((t & 0x2000000) != 0) {
ratio = (ratio * 0xde2ee4bc381afa7089aa84bb66) >> 128;
}
if ((t & 0x4000000) != 0) {
ratio = (ratio * 0xc0d55d4d7152c25fb139) >> 128;
}
if (tick > 0) {
ratio = type(uint256).max / ratio;
}
r = toSqrtRatio(ratio, false);
}
}
function sqrtRatioToTick(SqrtRatio sqrtRatio) pure returns (int32) {
unchecked {
uint256 sqrtRatioFixed = sqrtRatio.toFixed();
bool negative = (sqrtRatioFixed >> 128) == 0;
uint256 x = negative ? (type(uint256).max / sqrtRatioFixed) : sqrtRatioFixed;
// we know x >> 128 is never zero because we check bounds above and then reciprocate sqrtRatio if the high 128 bits are zero
// so we don't need to handle the exceptional case of log2(0)
uint256 msbHigh = FixedPointMathLib.log2(x >> 128);
x = x >> (msbHigh + 1);
uint256 log2_unsigned = msbHigh * 0x10000000000000000;
assembly ("memory-safe") {
// 63
x := shr(127, mul(x, x))
let is_high_nonzero := eq(iszero(shr(128, x)), 0)
log2_unsigned := add(log2_unsigned, mul(is_high_nonzero, 0x8000000000000000))
x := shr(is_high_nonzero, x)
// 62
x := shr(127, mul(x, x))
is_high_nonzero := eq(iszero(shr(128, x)), 0)
log2_unsigned := add(log2_unsigned, mul(is_high_nonzero, 0x4000000000000000))
x := shr(is_high_nonzero, x)
// 61
x := shr(127, mul(x, x))
is_high_nonzero := eq(iszero(shr(128, x)), 0)
log2_unsigned := add(log2_unsigned, mul(is_high_nonzero, 0x2000000000000000))
x := shr(is_high_nonzero, x)
// 60
x := shr(127, mul(x, x))
is_high_nonzero := eq(iszero(shr(128, x)), 0)
log2_unsigned := add(log2_unsigned, mul(is_high_nonzero, 0x1000000000000000))
x := shr(is_high_nonzero, x)
// 59
x := shr(127, mul(x, x))
is_high_nonzero := eq(iszero(shr(128, x)), 0)
log2_unsigned := add(log2_unsigned, mul(is_high_nonzero, 0x800000000000000))
x := shr(is_high_nonzero, x)
// 58
x := shr(127, mul(x, x))
is_high_nonzero := eq(iszero(shr(128, x)), 0)
log2_unsigned := add(log2_unsigned, mul(is_high_nonzero, 0x400000000000000))
x := shr(is_high_nonzero, x)
// 57
x := shr(127, mul(x, x))
is_high_nonzero := eq(iszero(shr(128, x)), 0)
log2_unsigned := add(log2_unsigned, mul(is_high_nonzero, 0x200000000000000))
x := shr(is_high_nonzero, x)
// 56
x := shr(127, mul(x, x))
is_high_nonzero := eq(iszero(shr(128, x)), 0)
log2_unsigned := add(log2_unsigned, mul(is_high_nonzero, 0x100000000000000))
x := shr(is_high_nonzero, x)
// 55
x := shr(127, mul(x, x))
is_high_nonzero := eq(iszero(shr(128, x)), 0)
log2_unsigned := add(log2_unsigned, mul(is_high_nonzero, 0x80000000000000))
x := shr(is_high_nonzero, x)
// 54
x := shr(127, mul(x, x))
is_high_nonzero := eq(iszero(shr(128, x)), 0)
log2_unsigned := add(log2_unsigned, mul(is_high_nonzero, 0x40000000000000))
x := shr(is_high_nonzero, x)
// 53
x := shr(127, mul(x, x))
is_high_nonzero := eq(iszero(shr(128, x)), 0)
log2_unsigned := add(log2_unsigned, mul(is_high_nonzero, 0x20000000000000))
x := shr(is_high_nonzero, x)
// 52
x := shr(127, mul(x, x))
is_high_nonzero := eq(iszero(shr(128, x)), 0)
log2_unsigned := add(log2_unsigned, mul(is_high_nonzero, 0x10000000000000))
x := shr(is_high_nonzero, x)
// 51
x := shr(127, mul(x, x))
is_high_nonzero := eq(iszero(shr(128, x)), 0)
log2_unsigned := add(log2_unsigned, mul(is_high_nonzero, 0x8000000000000))
x := shr(is_high_nonzero, x)
// 50
x := shr(127, mul(x, x))
is_high_nonzero := eq(iszero(shr(128, x)), 0)
log2_unsigned := add(log2_unsigned, mul(is_high_nonzero, 0x4000000000000))
x := shr(is_high_nonzero, x)
// 49
x := shr(127, mul(x, x))
is_high_nonzero := eq(iszero(shr(128, x)), 0)
log2_unsigned := add(log2_unsigned, mul(is_high_nonzero, 0x2000000000000))
x := shr(is_high_nonzero, x)
// 48
x := shr(127, mul(x, x))
is_high_nonzero := eq(iszero(shr(128, x)), 0)
log2_unsigned := add(log2_unsigned, mul(is_high_nonzero, 0x1000000000000))
x := shr(is_high_nonzero, x)
// 47
x := shr(127, mul(x, x))
is_high_nonzero := eq(iszero(shr(128, x)), 0)
log2_unsigned := add(log2_unsigned, mul(is_high_nonzero, 0x800000000000))
x := shr(is_high_nonzero, x)
// 46
x := shr(127, mul(x, x))
is_high_nonzero := eq(iszero(shr(128, x)), 0)
log2_unsigned := add(log2_unsigned, mul(is_high_nonzero, 0x400000000000))
x := shr(is_high_nonzero, x)
// 45
x := shr(127, mul(x, x))
is_high_nonzero := eq(iszero(shr(128, x)), 0)
log2_unsigned := add(log2_unsigned, mul(is_high_nonzero, 0x200000000000))
x := shr(is_high_nonzero, x)
// 44
x := shr(127, mul(x, x))
is_high_nonzero := eq(iszero(shr(128, x)), 0)
log2_unsigned := add(log2_unsigned, mul(is_high_nonzero, 0x100000000000))
x := shr(is_high_nonzero, x)
// 43
x := shr(127, mul(x, x))
is_high_nonzero := eq(iszero(shr(128, x)), 0)
log2_unsigned := add(log2_unsigned, mul(is_high_nonzero, 0x80000000000))
x := shr(is_high_nonzero, x)
// 42
x := shr(127, mul(x, x))
is_high_nonzero := eq(iszero(shr(128, x)), 0)
log2_unsigned := add(log2_unsigned, mul(is_high_nonzero, 0x40000000000))
}
// 25572630076711825471857579 == 2**64/(log base 2 of sqrt tick size)
// https://www.wolframalpha.com/input?i=floor%28%281%2F+log+base+2+of+%28sqrt%281.000001%29%29%29*2**64%29
int256 logBaseTickSizeX128 =
(negative ? -int256(log2_unsigned) : int256(log2_unsigned)) * 25572630076711825471857579;
int32 tickLow;
int32 tickHigh;
if (negative) {
tickLow = int32((logBaseTickSizeX128 - 112469616488610087266845472033458199637) >> 128);
tickHigh = int32((logBaseTickSizeX128) >> 128);
} else {
tickLow = int32((logBaseTickSizeX128) >> 128);
tickHigh = int32((logBaseTickSizeX128 + 112469616488610087266845472033458199637) >> 128);
}
if (tickLow == tickHigh) {
return tickLow;
}
if (tickToSqrtRatio(tickHigh) <= sqrtRatio) return tickHigh;
return tickLow;
}
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.28;
import {LibBit} from "solady/utils/LibBit.sol";
type Bitmap is uint256;
using {toggle, isSet, leSetBit, geSetBit} for Bitmap global;
function toggle(Bitmap bitmap, uint8 index) pure returns (Bitmap result) {
assembly ("memory-safe") {
result := xor(bitmap, shl(index, 1))
}
}
function isSet(Bitmap bitmap, uint8 index) pure returns (bool yes) {
assembly ("memory-safe") {
yes := and(shr(index, bitmap), 1)
}
}
// Returns the index of the most significant bit that is set _and_ less or equally significant to index, or 256 if no such bit exists.
function leSetBit(Bitmap bitmap, uint8 index) pure returns (uint256) {
unchecked {
uint256 masked;
assembly ("memory-safe") {
masked := and(bitmap, sub(shl(add(index, 1), 1), 1))
}
return LibBit.fls(masked);
}
}
// Returns the index of the least significant bit that is set _and_ more or equally significant to index, or 256 if no such bit exists.
function geSetBit(Bitmap bitmap, uint8 index) pure returns (uint256) {
unchecked {
uint256 masked;
assembly ("memory-safe") {
masked := and(bitmap, not(sub(shl(index, 1), 1)))
}
return LibBit.ffs(masked);
}
}
// 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/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 ETH and ERC20 transfer library that gracefully handles missing return values.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SafeTransferLib.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
/// @author Permit2 operations from (https://github.com/Uniswap/permit2/blob/main/src/libraries/Permit2Lib.sol)
///
/// @dev Note:
/// - For ETH transfers, please use `forceSafeTransferETH` for DoS protection.
library SafeTransferLib {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The ETH transfer has failed.
error ETHTransferFailed();
/// @dev The ERC20 `transferFrom` has failed.
error TransferFromFailed();
/// @dev The ERC20 `transfer` has failed.
error TransferFailed();
/// @dev The ERC20 `approve` has failed.
error ApproveFailed();
/// @dev The ERC20 `totalSupply` query has failed.
error TotalSupplyQueryFailed();
/// @dev The Permit2 operation has failed.
error Permit2Failed();
/// @dev The Permit2 amount must be less than `2**160 - 1`.
error Permit2AmountOverflow();
/// @dev The Permit2 approve operation has failed.
error Permit2ApproveFailed();
/// @dev The Permit2 lockdown operation has failed.
error Permit2LockdownFailed();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTANTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Suggested gas stipend for contract receiving ETH that disallows any storage writes.
uint256 internal constant GAS_STIPEND_NO_STORAGE_WRITES = 2300;
/// @dev Suggested gas stipend for contract receiving ETH to perform a few
/// storage reads and writes, but low enough to prevent griefing.
uint256 internal constant GAS_STIPEND_NO_GRIEF = 100000;
/// @dev The unique EIP-712 domain domain separator for the DAI token contract.
bytes32 internal constant DAI_DOMAIN_SEPARATOR =
0xdbb8cf42e1ecb028be3f3dbc922e1d878b963f411dc388ced501601c60f7c6f7;
/// @dev The address for the WETH9 contract on Ethereum mainnet.
address internal constant WETH9 = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
/// @dev The canonical Permit2 address.
/// [Github](https://github.com/Uniswap/permit2)
/// [Etherscan](https://etherscan.io/address/0x000000000022D473030F116dDEE9F6B43aC78BA3)
address internal constant PERMIT2 = 0x000000000022D473030F116dDEE9F6B43aC78BA3;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ETH OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
// If the ETH transfer MUST succeed with a reasonable gas budget, use the force variants.
//
// The regular variants:
// - Forwards all remaining gas to the target.
// - Reverts if the target reverts.
// - Reverts if the current contract has insufficient balance.
//
// The force variants:
// - Forwards with an optional gas stipend
// (defaults to `GAS_STIPEND_NO_GRIEF`, which is sufficient for most cases).
// - If the target reverts, or if the gas stipend is exhausted,
// creates a temporary contract to force send the ETH via `SELFDESTRUCT`.
// Future compatible with `SENDALL`: https://eips.ethereum.org/EIPS/eip-4758.
// - Reverts if the current contract has insufficient balance.
//
// The try variants:
// - Forwards with a mandatory gas stipend.
// - Instead of reverting, returns whether the transfer succeeded.
/// @dev Sends `amount` (in wei) ETH to `to`.
function safeTransferETH(address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
if iszero(call(gas(), to, amount, codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Sends all the ETH in the current contract to `to`.
function safeTransferAllETH(address to) internal {
/// @solidity memory-safe-assembly
assembly {
// Transfer all the ETH and check if it succeeded or not.
if iszero(call(gas(), to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Force sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
function forceSafeTransferETH(address to, uint256 amount, uint256 gasStipend) internal {
/// @solidity memory-safe-assembly
assembly {
if lt(selfbalance(), amount) {
mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
revert(0x1c, 0x04)
}
if iszero(call(gasStipend, to, amount, codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, to) // Store the address in scratch space.
mstore8(0x0b, 0x73) // Opcode `PUSH20`.
mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
if iszero(create(amount, 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
}
}
}
/// @dev Force sends all the ETH in the current contract to `to`, with a `gasStipend`.
function forceSafeTransferAllETH(address to, uint256 gasStipend) internal {
/// @solidity memory-safe-assembly
assembly {
if iszero(call(gasStipend, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, to) // Store the address in scratch space.
mstore8(0x0b, 0x73) // Opcode `PUSH20`.
mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
if iszero(create(selfbalance(), 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
}
}
}
/// @dev Force sends `amount` (in wei) ETH to `to`, with `GAS_STIPEND_NO_GRIEF`.
function forceSafeTransferETH(address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
if lt(selfbalance(), amount) {
mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
revert(0x1c, 0x04)
}
if iszero(call(GAS_STIPEND_NO_GRIEF, to, amount, codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, to) // Store the address in scratch space.
mstore8(0x0b, 0x73) // Opcode `PUSH20`.
mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
if iszero(create(amount, 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
}
}
}
/// @dev Force sends all the ETH in the current contract to `to`, with `GAS_STIPEND_NO_GRIEF`.
function forceSafeTransferAllETH(address to) internal {
/// @solidity memory-safe-assembly
assembly {
// forgefmt: disable-next-item
if iszero(call(GAS_STIPEND_NO_GRIEF, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, to) // Store the address in scratch space.
mstore8(0x0b, 0x73) // Opcode `PUSH20`.
mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
if iszero(create(selfbalance(), 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
}
}
}
/// @dev Sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
function trySafeTransferETH(address to, uint256 amount, uint256 gasStipend)
internal
returns (bool success)
{
/// @solidity memory-safe-assembly
assembly {
success := call(gasStipend, to, amount, codesize(), 0x00, codesize(), 0x00)
}
}
/// @dev Sends all the ETH in the current contract to `to`, with a `gasStipend`.
function trySafeTransferAllETH(address to, uint256 gasStipend)
internal
returns (bool success)
{
/// @solidity memory-safe-assembly
assembly {
success := call(gasStipend, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ERC20 OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Sends `amount` of ERC20 `token` from `from` to `to`.
/// Reverts upon failure.
///
/// The `from` account must have at least `amount` approved for
/// the current contract to manage.
function safeTransferFrom(address token, address from, address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x60, amount) // Store the `amount` argument.
mstore(0x40, to) // Store the `to` argument.
mstore(0x2c, shl(96, from)) // Store the `from` argument.
mstore(0x0c, 0x23b872dd000000000000000000000000) // `transferFrom(address,address,uint256)`.
let success := call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
if iszero(and(eq(mload(0x00), 1), success)) {
if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
revert(0x1c, 0x04)
}
}
mstore(0x60, 0) // Restore the zero slot to zero.
mstore(0x40, m) // Restore the free memory pointer.
}
}
/// @dev Sends `amount` of ERC20 `token` from `from` to `to`.
///
/// The `from` account must have at least `amount` approved for the current contract to manage.
function trySafeTransferFrom(address token, address from, address to, uint256 amount)
internal
returns (bool success)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x60, amount) // Store the `amount` argument.
mstore(0x40, to) // Store the `to` argument.
mstore(0x2c, shl(96, from)) // Store the `from` argument.
mstore(0x0c, 0x23b872dd000000000000000000000000) // `transferFrom(address,address,uint256)`.
success := call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
if iszero(and(eq(mload(0x00), 1), success)) {
success := lt(or(iszero(extcodesize(token)), returndatasize()), success)
}
mstore(0x60, 0) // Restore the zero slot to zero.
mstore(0x40, m) // Restore the free memory pointer.
}
}
/// @dev Sends all of ERC20 `token` from `from` to `to`.
/// Reverts upon failure.
///
/// The `from` account must have their entire balance approved for the current contract to manage.
function safeTransferAllFrom(address token, address from, address to)
internal
returns (uint256 amount)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x40, to) // Store the `to` argument.
mstore(0x2c, shl(96, from)) // Store the `from` argument.
mstore(0x0c, 0x70a08231000000000000000000000000) // `balanceOf(address)`.
// Read the balance, reverting upon failure.
if iszero(
and( // The arguments of `and` are evaluated from right to left.
gt(returndatasize(), 0x1f), // At least 32 bytes returned.
staticcall(gas(), token, 0x1c, 0x24, 0x60, 0x20)
)
) {
mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
revert(0x1c, 0x04)
}
mstore(0x00, 0x23b872dd) // `transferFrom(address,address,uint256)`.
amount := mload(0x60) // The `amount` is already at 0x60. We'll need to return it.
// Perform the transfer, reverting upon failure.
let success := call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
if iszero(and(eq(mload(0x00), 1), success)) {
if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
revert(0x1c, 0x04)
}
}
mstore(0x60, 0) // Restore the zero slot to zero.
mstore(0x40, m) // Restore the free memory pointer.
}
}
/// @dev Sends `amount` of ERC20 `token` from the current contract to `to`.
/// Reverts upon failure.
function safeTransfer(address token, address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
mstore(0x14, to) // Store the `to` argument.
mstore(0x34, amount) // Store the `amount` argument.
mstore(0x00, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`.
// Perform the transfer, reverting upon failure.
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)
}
}
mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
}
}
/// @dev Sends all of ERC20 `token` from the current contract to `to`.
/// Reverts upon failure.
function safeTransferAll(address token, address to) internal returns (uint256 amount) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, 0x70a08231) // Store the function selector of `balanceOf(address)`.
mstore(0x20, address()) // Store the address of the current contract.
// Read the balance, reverting upon failure.
if iszero(
and( // The arguments of `and` are evaluated from right to left.
gt(returndatasize(), 0x1f), // At least 32 bytes returned.
staticcall(gas(), token, 0x1c, 0x24, 0x34, 0x20)
)
) {
mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
revert(0x1c, 0x04)
}
mstore(0x14, to) // Store the `to` argument.
amount := mload(0x34) // The `amount` is already at 0x34. We'll need to return it.
mstore(0x00, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`.
// Perform the transfer, reverting upon failure.
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)
}
}
mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
}
}
/// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract.
/// Reverts upon failure.
function safeApprove(address token, address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
mstore(0x14, to) // Store the `to` argument.
mstore(0x34, amount) // Store the `amount` argument.
mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
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, 0x3e3f8f73) // `ApproveFailed()`.
revert(0x1c, 0x04)
}
}
mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
}
}
/// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract.
/// If the initial attempt to approve fails, attempts to reset the approved amount to zero,
/// then retries the approval again (some tokens, e.g. USDT, requires this).
/// Reverts upon failure.
function safeApproveWithRetry(address token, address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
mstore(0x14, to) // Store the `to` argument.
mstore(0x34, amount) // Store the `amount` argument.
mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
// Perform the approval, retrying upon failure.
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(0x34, 0) // Store 0 for the `amount`.
mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
pop(call(gas(), token, 0, 0x10, 0x44, codesize(), 0x00)) // Reset the approval.
mstore(0x34, amount) // Store back the original `amount`.
// Retry the approval, reverting upon failure.
success := call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
if iszero(and(eq(mload(0x00), 1), success)) {
// Check the `extcodesize` again just in case the token selfdestructs lol.
if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
mstore(0x00, 0x3e3f8f73) // `ApproveFailed()`.
revert(0x1c, 0x04)
}
}
}
}
mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
}
}
/// @dev Returns the amount of ERC20 `token` owned by `account`.
/// Returns zero if the `token` does not exist.
function balanceOf(address token, address account) internal view returns (uint256 amount) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x14, account) // Store the `account` argument.
mstore(0x00, 0x70a08231000000000000000000000000) // `balanceOf(address)`.
amount :=
mul( // The arguments of `mul` are evaluated from right to left.
mload(0x20),
and( // The arguments of `and` are evaluated from right to left.
gt(returndatasize(), 0x1f), // At least 32 bytes returned.
staticcall(gas(), token, 0x10, 0x24, 0x20, 0x20)
)
)
}
}
/// @dev Returns the total supply of the `token`.
/// Reverts if the token does not exist or does not implement `totalSupply()`.
function totalSupply(address token) internal view returns (uint256 result) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, 0x18160ddd) // `totalSupply()`.
if iszero(
and(gt(returndatasize(), 0x1f), staticcall(gas(), token, 0x1c, 0x04, 0x00, 0x20))
) {
mstore(0x00, 0x54cd9435) // `TotalSupplyQueryFailed()`.
revert(0x1c, 0x04)
}
result := mload(0x00)
}
}
/// @dev Sends `amount` of ERC20 `token` from `from` to `to`.
/// If the initial attempt fails, try to use Permit2 to transfer the token.
/// Reverts upon failure.
///
/// The `from` account must have at least `amount` approved for the current contract to manage.
function safeTransferFrom2(address token, address from, address to, uint256 amount) internal {
if (!trySafeTransferFrom(token, from, to, amount)) {
permit2TransferFrom(token, from, to, amount);
}
}
/// @dev Sends `amount` of ERC20 `token` from `from` to `to` via Permit2.
/// Reverts upon failure.
function permit2TransferFrom(address token, address from, address to, uint256 amount)
internal
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(add(m, 0x74), shr(96, shl(96, token)))
mstore(add(m, 0x54), amount)
mstore(add(m, 0x34), to)
mstore(add(m, 0x20), shl(96, from))
// `transferFrom(address,address,uint160,address)`.
mstore(m, 0x36c78516000000000000000000000000)
let p := PERMIT2
let exists := eq(chainid(), 1)
if iszero(exists) { exists := iszero(iszero(extcodesize(p))) }
if iszero(
and(
call(gas(), p, 0, add(m, 0x10), 0x84, codesize(), 0x00),
lt(iszero(extcodesize(token)), exists) // Token has code and Permit2 exists.
)
) {
mstore(0x00, 0x7939f4248757f0fd) // `TransferFromFailed()` or `Permit2AmountOverflow()`.
revert(add(0x18, shl(2, iszero(iszero(shr(160, amount))))), 0x04)
}
}
}
/// @dev Permit a user to spend a given amount of
/// another user's tokens via native EIP-2612 permit if possible, falling
/// back to Permit2 if native permit fails or is not implemented on the token.
function permit2(
address token,
address owner,
address spender,
uint256 amount,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) internal {
bool success;
/// @solidity memory-safe-assembly
assembly {
for {} shl(96, xor(token, WETH9)) {} {
mstore(0x00, 0x3644e515) // `DOMAIN_SEPARATOR()`.
if iszero(
and( // The arguments of `and` are evaluated from right to left.
lt(iszero(mload(0x00)), eq(returndatasize(), 0x20)), // Returns 1 non-zero word.
// Gas stipend to limit gas burn for tokens that don't refund gas when
// an non-existing function is called. 5K should be enough for a SLOAD.
staticcall(5000, token, 0x1c, 0x04, 0x00, 0x20)
)
) { break }
// After here, we can be sure that token is a contract.
let m := mload(0x40)
mstore(add(m, 0x34), spender)
mstore(add(m, 0x20), shl(96, owner))
mstore(add(m, 0x74), deadline)
if eq(mload(0x00), DAI_DOMAIN_SEPARATOR) {
mstore(0x14, owner)
mstore(0x00, 0x7ecebe00000000000000000000000000) // `nonces(address)`.
mstore(
add(m, 0x94),
lt(iszero(amount), staticcall(gas(), token, 0x10, 0x24, add(m, 0x54), 0x20))
)
mstore(m, 0x8fcbaf0c000000000000000000000000) // `IDAIPermit.permit`.
// `nonces` is already at `add(m, 0x54)`.
// `amount != 0` is already stored at `add(m, 0x94)`.
mstore(add(m, 0xb4), and(0xff, v))
mstore(add(m, 0xd4), r)
mstore(add(m, 0xf4), s)
success := call(gas(), token, 0, add(m, 0x10), 0x104, codesize(), 0x00)
break
}
mstore(m, 0xd505accf000000000000000000000000) // `IERC20Permit.permit`.
mstore(add(m, 0x54), amount)
mstore(add(m, 0x94), and(0xff, v))
mstore(add(m, 0xb4), r)
mstore(add(m, 0xd4), s)
success := call(gas(), token, 0, add(m, 0x10), 0xe4, codesize(), 0x00)
break
}
}
if (!success) simplePermit2(token, owner, spender, amount, deadline, v, r, s);
}
/// @dev Simple permit on the Permit2 contract.
function simplePermit2(
address token,
address owner,
address spender,
uint256 amount,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) internal {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(m, 0x927da105) // `allowance(address,address,address)`.
{
let addressMask := shr(96, not(0))
mstore(add(m, 0x20), and(addressMask, owner))
mstore(add(m, 0x40), and(addressMask, token))
mstore(add(m, 0x60), and(addressMask, spender))
mstore(add(m, 0xc0), and(addressMask, spender))
}
let p := mul(PERMIT2, iszero(shr(160, amount)))
if iszero(
and( // The arguments of `and` are evaluated from right to left.
gt(returndatasize(), 0x5f), // Returns 3 words: `amount`, `expiration`, `nonce`.
staticcall(gas(), p, add(m, 0x1c), 0x64, add(m, 0x60), 0x60)
)
) {
mstore(0x00, 0x6b836e6b8757f0fd) // `Permit2Failed()` or `Permit2AmountOverflow()`.
revert(add(0x18, shl(2, iszero(p))), 0x04)
}
mstore(m, 0x2b67b570) // `Permit2.permit` (PermitSingle variant).
// `owner` is already `add(m, 0x20)`.
// `token` is already at `add(m, 0x40)`.
mstore(add(m, 0x60), amount)
mstore(add(m, 0x80), 0xffffffffffff) // `expiration = type(uint48).max`.
// `nonce` is already at `add(m, 0xa0)`.
// `spender` is already at `add(m, 0xc0)`.
mstore(add(m, 0xe0), deadline)
mstore(add(m, 0x100), 0x100) // `signature` offset.
mstore(add(m, 0x120), 0x41) // `signature` length.
mstore(add(m, 0x140), r)
mstore(add(m, 0x160), s)
mstore(add(m, 0x180), shl(248, v))
if iszero( // Revert if token does not have code, or if the call fails.
mul(extcodesize(token), call(gas(), p, 0, add(m, 0x1c), 0x184, codesize(), 0x00))) {
mstore(0x00, 0x6b836e6b) // `Permit2Failed()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Approves `spender` to spend `amount` of `token` for `address(this)`.
function permit2Approve(address token, address spender, uint160 amount, uint48 expiration)
internal
{
/// @solidity memory-safe-assembly
assembly {
let addressMask := shr(96, not(0))
let m := mload(0x40)
mstore(m, 0x87517c45) // `approve(address,address,uint160,uint48)`.
mstore(add(m, 0x20), and(addressMask, token))
mstore(add(m, 0x40), and(addressMask, spender))
mstore(add(m, 0x60), and(addressMask, amount))
mstore(add(m, 0x80), and(0xffffffffffff, expiration))
if iszero(call(gas(), PERMIT2, 0, add(m, 0x1c), 0xa0, codesize(), 0x00)) {
mstore(0x00, 0x324f14ae) // `Permit2ApproveFailed()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Revokes an approval for `token` and `spender` for `address(this)`.
function permit2Lockdown(address token, address spender) internal {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(m, 0xcc53287f) // `Permit2.lockdown`.
mstore(add(m, 0x20), 0x20) // Offset of the `approvals`.
mstore(add(m, 0x40), 1) // `approvals.length`.
mstore(add(m, 0x60), shr(96, shl(96, token)))
mstore(add(m, 0x80), shr(96, shl(96, spender)))
if iszero(call(gas(), PERMIT2, 0, add(m, 0x1c), 0xa0, codesize(), 0x00)) {
mstore(0x00, 0x96b3de23) // `Permit2LockdownFailed()`.
revert(0x1c, 0x04)
}
}
}
}
// 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: UNLICENSED
pragma solidity =0.8.28;
import {IExposedStorage} from "../interfaces/IExposedStorage.sol";
abstract contract ExposedStorage is IExposedStorage {
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))
}
}
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: UNLICENSED
pragma solidity =0.8.28;
import {FixedPointMathLib} from "solady/utils/FixedPointMathLib.sol";
import {SafeCastLib} from "solady/utils/SafeCastLib.sol";
import {amount0Delta, amount1Delta, sortAndConvertToFixedSqrtRatios} from "./delta.sol";
import {SqrtRatio} from "../types/sqrtRatio.sol";
/**
* @notice Returns the token0 and token1 delta owed for a given change in liquidity.
* @param sqrtRatio Current price (as a sqrt ratio).
* @param liquidityDelta Signed liquidity change; positive = added, negative = removed.
* @param sqrtRatioLower The lower bound of the price range (as a sqrt ratio).
* @param sqrtRatioUpper The upper bound of the price range (as a sqrt ratio).
*/
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);
// type(uint256).max cast to int256 is -1
int256 sign = int256(FixedPointMathLib.ternary(isPositive, 1, type(uint256).max));
// 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)))
);
}
}
}
function maxLiquidityForToken0(uint256 sqrtRatioLower, uint256 sqrtRatioUpper, uint128 amount) pure returns (uint256) {
unchecked {
uint256 numerator_1 = FixedPointMathLib.fullMulDivN(sqrtRatioLower, sqrtRatioUpper, 128);
return FixedPointMathLib.fullMulDiv(amount, numerator_1, (sqrtRatioUpper - sqrtRatioLower));
}
}
function maxLiquidityForToken1(uint256 sqrtRatioLower, uint256 sqrtRatioUpper, uint128 amount) pure returns (uint256) {
unchecked {
return (uint256(amount) << 128) / (sqrtRatioUpper - sqrtRatioLower);
}
}
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))
);
}
}
error LiquidityDeltaOverflow();
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)
}
}
}
function subLiquidityDelta(uint128 liquidity, int128 liquidityDelta) pure returns (uint128 result) {
assembly ("memory-safe") {
result := sub(liquidity, liquidityDelta)
if and(result, shl(128, 0xffffffffffffffffffffffffffffffff)) {
mstore(0, shl(224, 0x6d862c50))
revert(0, 4)
}
}
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.28;
// 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) {
uint256 r;
assembly ("memory-safe") {
let v := shl(64, afterFee)
let d := sub(0x10000000000000000, fee)
let q := div(v, d)
r := add(iszero(iszero(mod(v, d))), q)
}
if (r > type(uint128).max) {
revert AmountBeforeFeeOverflow();
}
result = uint128(r);
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.28;
import {Bitmap} from "../math/bitmap.sol";
import {MIN_TICK, MAX_TICK} from "../math/constants.sol";
import {FixedPointMathLib} from "solady/utils/FixedPointMathLib.sol";
// Returns the index of the word and the index _in_ that word which contains the bit representing whether the tick is initialized
// Addition of the 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
// 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)), 89421695)
word := div(rawIndex, 256)
index := mod(rawIndex, 256)
}
}
// 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(mul(word, 256), index)
tick := mul(sub(rawIndex, 89421695), tickSpacing)
}
}
// Flips the tick in the bitmap from true to false or vice versa
function flipTick(mapping(uint256 word => Bitmap bitmap) storage map, int32 tick, uint32 tickSpacing) {
(uint256 word, uint256 index) = tickToBitmapWordAndIndex(tick, tickSpacing);
assembly ("memory-safe") {
mstore(0, word)
mstore(32, map.slot)
let k := keccak256(0, 64)
let v := sload(k)
sstore(k, xor(v, shl(index, 1)))
}
}
function findNextInitializedTick(
mapping(uint256 word => Bitmap bitmap) storage map,
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);
// find the index of the previous tick in that word
uint256 nextIndex = map[word].geSetBit(uint8(index));
// if we found one, return it
if (nextIndex != 256) {
(nextTick, isInitialized) = (bitmapWordAndIndexToTick(word, nextIndex, 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(
mapping(uint256 word => Bitmap bitmap) storage map,
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);
// find the index of the previous tick in that word
uint256 prevIndex = map[word].leSetBit(uint8(index));
if (prevIndex != 256) {
(prevTick, isInitialized) = (bitmapWordAndIndexToTick(word, prevIndex, 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: UNLICENSED
pragma solidity =0.8.28;
import {CallPoints} from "../types/callPoints.sol";
import {PoolKey} from "../types/poolKey.sol";
import {PositionKey, Bounds} from "../types/positionKey.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";
struct UpdatePositionParameters {
bytes32 salt;
Bounds bounds;
int128 liquidityDelta;
}
interface IExtension {
function beforeInitializePool(address caller, PoolKey calldata key, int32 tick) external;
function afterInitializePool(address caller, PoolKey calldata key, int32 tick, SqrtRatio sqrtRatio) external;
function beforeUpdatePosition(address locker, PoolKey memory poolKey, UpdatePositionParameters memory params)
external;
function afterUpdatePosition(
address locker,
PoolKey memory poolKey,
UpdatePositionParameters memory params,
int128 delta0,
int128 delta1
) external;
function beforeSwap(
address locker,
PoolKey memory poolKey,
int128 amount,
bool isToken1,
SqrtRatio sqrtRatioLimit,
uint256 skipAhead
) external;
function afterSwap(
address locker,
PoolKey memory poolKey,
int128 amount,
bool isToken1,
SqrtRatio sqrtRatioLimit,
uint256 skipAhead,
int128 delta0,
int128 delta1
) external;
function beforeCollectFees(address locker, PoolKey memory poolKey, bytes32 salt, Bounds memory bounds) external;
function afterCollectFees(
address locker,
PoolKey memory poolKey,
bytes32 salt,
Bounds memory bounds,
uint128 amount0,
uint128 amount1
) external;
}
interface ICore is IFlashAccountant, IExposedStorage {
event ProtocolFeesWithdrawn(address recipient, address token, uint256 amount);
event ExtensionRegistered(address extension);
event PoolInitialized(bytes32 poolId, PoolKey poolKey, int32 tick, SqrtRatio sqrtRatio);
event PositionFeesCollected(bytes32 poolId, PositionKey positionKey, uint128 amount0, uint128 amount1);
event FeesAccumulated(bytes32 poolId, uint128 amount0, uint128 amount1);
event PositionUpdated(
address locker, bytes32 poolId, UpdatePositionParameters params, int128 delta0, int128 delta1
);
// This error is thrown by swaps and deposits when this particular deployment of the contract is expired.
error FailedRegisterInvalidCallPoints();
error ExtensionAlreadyRegistered();
error InsufficientSavedBalance();
error PoolAlreadyInitialized();
error ExtensionNotRegistered();
error PoolNotInitialized();
error MustCollectFeesBeforeWithdrawingAllLiquidity();
error SqrtRatioLimitOutOfRange();
error InvalidSqrtRatioLimit();
error SavedBalanceTokensNotSorted();
// Allows the owner of the contract to withdraw the protocol withdrawal fees collected
// To withdraw the native token protocol fees, call with token = NATIVE_TOKEN_ADDRESS
function withdrawProtocolFees(address recipient, address token, uint256 amount) external;
// Extensions must call this function to become registered. The call points are validated against the caller address
function registerExtension(CallPoints memory expectedCallPoints) external;
// Sets the initial price for a new pool in terms of tick.
function initializePool(PoolKey memory poolKey, int32 tick) external returns (SqrtRatio sqrtRatio);
function prevInitializedTick(bytes32 poolId, int32 fromTick, uint32 tickSpacing, uint256 skipAhead)
external
view
returns (int32 tick, bool isInitialized);
function nextInitializedTick(bytes32 poolId, int32 fromTick, uint32 tickSpacing, uint256 skipAhead)
external
view
returns (int32 tick, bool isInitialized);
// Loads 2 tokens from the saved balances of the caller as payment in the current context.
function load(address token0, address token1, bytes32 salt, uint128 amount0, uint128 amount1) external;
// Saves an amount of 2 tokens to be used later, in a single slot.
function save(address owner, address token0, address token1, bytes32 salt, uint128 amount0, uint128 amount1)
external
payable;
// Returns the pool fees per liquidity inside the given bounds.
function getPoolFeesPerLiquidityInside(PoolKey memory poolKey, Bounds memory bounds)
external
view
returns (FeesPerLiquidity memory);
// Accumulates tokens to fees of a pool. Only callable by the extension of the specified pool
// key, i.e. the current locker _must_ be the extension.
// The extension must call this function within a lock callback.
function accumulateAsFees(PoolKey memory poolKey, uint128 amount0, uint128 amount1) external payable;
function updatePosition(PoolKey memory poolKey, UpdatePositionParameters memory params)
external
payable
returns (int128 delta0, int128 delta1);
function collectFees(PoolKey memory poolKey, bytes32 salt, Bounds memory bounds)
external
returns (uint128 amount0, uint128 amount1);
function swap_611415377(
PoolKey memory poolKey,
int128 amount,
bool isToken1,
SqrtRatio sqrtRatioLimit,
uint256 skipAhead
) external payable returns (int128 delta0, int128 delta1);
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.28;
import {NATIVE_TOKEN_ADDRESS} from "../math/constants.sol";
import {IPayer, IFlashAccountant} from "../interfaces/IFlashAccountant.sol";
import {SafeTransferLib} from "solady/utils/SafeTransferLib.sol";
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
// cast keccak "FlashAccountant#CURRENT_LOCKER_SLOT"
uint256 private constant _CURRENT_LOCKER_SLOT = 0x07cc7f5195d862f505d6b095c82f92e00cfc1766f5bca4383c28dc5fca1555fd;
// cast keccak "FlashAccountant#NONZERO_DEBT_COUNT_OFFSET"
uint256 private constant _NONZERO_DEBT_COUNT_OFFSET =
0x7772acfd7e0f66ebb20a058830296c3dc1301b111d23348e1c961d324223190d;
// cast keccak "FlashAccountant#DEBT_HASH_OFFSET"
uint256 private constant _DEBT_HASH_OFFSET = 0x3fee1dc3ade45aa30d633b5b8645760533723e46597841ef1126c6577a091742;
// cast keccak "FlashAccountant#PAY_REENTRANCY_LOCK"
uint256 private constant _PAY_REENTRANCY_LOCK = 0xe1be600102d456bf2d4dee36e1641404df82292916888bf32557e00dfe166412;
function _getLocker() internal view returns (uint256 id, address locker) {
assembly ("memory-safe") {
let current := tload(_CURRENT_LOCKER_SLOT)
if iszero(current) {
// cast sig "NotLocked()"
mstore(0, shl(224, 0x1834e265))
revert(0, 4)
}
id := sub(shr(160, current), 1)
locker := shr(96, shl(96, current))
}
}
function _requireLocker() internal view returns (uint256 id, address locker) {
(id, locker) = _getLocker();
if (locker != msg.sender) revert LockerOnly();
}
// 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 means erasing debt, positive means adding debt
function _accountDebt(uint256 id, address token, int256 debtChange) internal {
assembly ("memory-safe") {
if iszero(iszero(debtChange)) {
mstore(0, add(add(shl(160, id), token), _DEBT_HASH_OFFSET))
let deltaSlot := keccak256(0, 32)
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 nextZero := iszero(next)
if xor(iszero(current), nextZero) {
let nzdCountSlot := add(id, _NONZERO_DEBT_COUNT_OFFSET)
tstore(nzdCountSlot, add(sub(tload(nzdCountSlot), nextZero), iszero(nextZero)))
}
tstore(deltaSlot, next)
}
}
}
// The entrypoint for all operations on the core contract
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 0xb45a3c0e
mstore(free, shl(224, 0xb45a3c0e))
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())
}
}
// Allows forwarding the lock context to another actor, allowing them to act on the original locker's debt
function forward(address to) external {
(uint256 id, address 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, add(id, 1)), to))
let free := mload(0x40)
// Prepare call to forwarded(uint256,address) -> selector 0x64919dea
mstore(free, shl(224, 0x64919dea))
mstore(add(free, 4), id)
mstore(add(free, 36), locker)
calldatacopy(add(free, 68), 36, sub(calldatasize(), 36))
// Call the forwardee with the packed data
let success := call(gas(), to, 0, free, add(32, calldatasize()), 0, 0)
// Pass through the error on failure
if iszero(success) {
returndatacopy(free, 0, returndatasize())
revert(free, returndatasize())
}
tstore(_CURRENT_LOCKER_SLOT, or(shl(160, add(id, 1)), locker))
// Directly return whatever the subcall returned
returndatacopy(free, 0, returndatasize())
return(free, returndatasize())
}
}
function pay(address token) external returns (uint128 payment) {
assembly ("memory-safe") {
if tload(_PAY_REENTRANCY_LOCK) {
// cast sig "PayReentrance()"
mstore(0, 0xced108be)
revert(0x1c, 0x04)
}
tstore(_PAY_REENTRANCY_LOCK, 1)
}
(uint256 id,) = _getLocker();
assembly ("memory-safe") {
let free := mload(0x40)
mstore(20, address()) // Store the `account` argument.
mstore(0, 0x70a08231000000000000000000000000) // `balanceOf(address)`.
let tokenBalanceBefore :=
mul( // The arguments of `mul` are evaluated from right to left.
mload(free),
and( // The arguments of `and` are evaluated from right to left.
gt(returndatasize(), 0x1f), // At least 32 bytes returned.
staticcall(gas(), token, 0x10, 0x24, free, 0x20)
)
)
// Prepare call to "payCallback(uint256,address)"
mstore(free, shl(224, 0x599d0714))
mstore(add(free, 4), id)
mstore(add(free, 36), token)
// copy the token, plus anything else that they wanted to forward
calldatacopy(add(free, 68), 36, sub(calldatasize(), 36))
// Call the forwardee with the packed data
// Pass through the error on failure
if iszero(call(gas(), caller(), 0, free, add(32, calldatasize()), 0, 0)) {
returndatacopy(free, 0, returndatasize())
revert(free, returndatasize())
}
// Arguments are still in scratch, we don't need to rewrite them
let tokenBalanceAfter :=
mul( // The arguments of `mul` are evaluated from right to left.
mload(0x20),
and( // The arguments of `and` are evaluated from right to left.
gt(returndatasize(), 0x1f), // At least 32 bytes returned.
staticcall(gas(), token, 0x10, 0x24, 0x20, 0x20)
)
)
if lt(tokenBalanceAfter, tokenBalanceBefore) {
// cast sig "NoPaymentMade()"
mstore(0x00, 0x01b243b9)
revert(0x1c, 4)
}
payment := sub(tokenBalanceAfter, tokenBalanceBefore)
// We never expect tokens to have this much total supply
if gt(payment, 0xffffffffffffffffffffffffffffffff) {
// cast sig "PaymentOverflow()"
mstore(0x00, 0x9cac58ca)
revert(0x1c, 4)
}
}
// The unary negative operator never fails because payment is less than max uint128
unchecked {
_accountDebt(id, token, -int256(uint256(payment)));
}
assembly ("memory-safe") {
tstore(_PAY_REENTRANCY_LOCK, 0)
}
}
function withdraw(address token, address recipient, uint128 amount) external {
(uint256 id,) = _requireLocker();
_accountDebt(id, token, int256(uint256(amount)));
if (token == NATIVE_TOKEN_ADDRESS) {
SafeTransferLib.safeTransferETH(recipient, amount);
} else {
SafeTransferLib.safeTransfer(token, recipient, amount);
}
}
receive() external payable {
(uint256 id,) = _getLocker();
// 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 never expect the native token to exceed this supply
if (msg.value > type(uint128).max) revert PaymentOverflow();
_accountDebt(id, NATIVE_TOKEN_ADDRESS, -int256(msg.value));
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Library for efficiently performing keccak256 hashes.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/EfficientHashLib.sol)
/// @dev To avoid stack-too-deep, you can use:
/// ```
/// bytes32[] memory buffer = EfficientHashLib.malloc(10);
/// EfficientHashLib.set(buffer, 0, value0);
/// ..
/// EfficientHashLib.set(buffer, 9, value9);
/// bytes32 finalHash = EfficientHashLib.hash(buffer);
/// ```
library EfficientHashLib {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* MALLOC-LESS HASHING OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns `keccak256(abi.encode(v0))`.
function hash(bytes32 v0) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, v0)
result := keccak256(0x00, 0x20)
}
}
/// @dev Returns `keccak256(abi.encode(v0))`.
function hash(uint256 v0) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, v0)
result := keccak256(0x00, 0x20)
}
}
/// @dev Returns `keccak256(abi.encode(v0, v1))`.
function hash(bytes32 v0, bytes32 v1) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, v0)
mstore(0x20, v1)
result := keccak256(0x00, 0x40)
}
}
/// @dev Returns `keccak256(abi.encode(v0, v1))`.
function hash(uint256 v0, uint256 v1) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, v0)
mstore(0x20, v1)
result := keccak256(0x00, 0x40)
}
}
/// @dev Returns `keccak256(abi.encode(v0, v1, v2))`.
function hash(bytes32 v0, bytes32 v1, bytes32 v2) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(m, v0)
mstore(add(m, 0x20), v1)
mstore(add(m, 0x40), v2)
result := keccak256(m, 0x60)
}
}
/// @dev Returns `keccak256(abi.encode(v0, v1, v2))`.
function hash(uint256 v0, uint256 v1, uint256 v2) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(m, v0)
mstore(add(m, 0x20), v1)
mstore(add(m, 0x40), v2)
result := keccak256(m, 0x60)
}
}
/// @dev Returns `keccak256(abi.encode(v0, v1, v2, v3))`.
function hash(bytes32 v0, bytes32 v1, bytes32 v2, bytes32 v3)
internal
pure
returns (bytes32 result)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(m, v0)
mstore(add(m, 0x20), v1)
mstore(add(m, 0x40), v2)
mstore(add(m, 0x60), v3)
result := keccak256(m, 0x80)
}
}
/// @dev Returns `keccak256(abi.encode(v0, v1, v2, v3))`.
function hash(uint256 v0, uint256 v1, uint256 v2, uint256 v3)
internal
pure
returns (bytes32 result)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(m, v0)
mstore(add(m, 0x20), v1)
mstore(add(m, 0x40), v2)
mstore(add(m, 0x60), v3)
result := keccak256(m, 0x80)
}
}
/// @dev Returns `keccak256(abi.encode(v0, .., v4))`.
function hash(bytes32 v0, bytes32 v1, bytes32 v2, bytes32 v3, bytes32 v4)
internal
pure
returns (bytes32 result)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(m, v0)
mstore(add(m, 0x20), v1)
mstore(add(m, 0x40), v2)
mstore(add(m, 0x60), v3)
mstore(add(m, 0x80), v4)
result := keccak256(m, 0xa0)
}
}
/// @dev Returns `keccak256(abi.encode(v0, .., v4))`.
function hash(uint256 v0, uint256 v1, uint256 v2, uint256 v3, uint256 v4)
internal
pure
returns (bytes32 result)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(m, v0)
mstore(add(m, 0x20), v1)
mstore(add(m, 0x40), v2)
mstore(add(m, 0x60), v3)
mstore(add(m, 0x80), v4)
result := keccak256(m, 0xa0)
}
}
/// @dev Returns `keccak256(abi.encode(v0, .., v5))`.
function hash(bytes32 v0, bytes32 v1, bytes32 v2, bytes32 v3, bytes32 v4, bytes32 v5)
internal
pure
returns (bytes32 result)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(m, v0)
mstore(add(m, 0x20), v1)
mstore(add(m, 0x40), v2)
mstore(add(m, 0x60), v3)
mstore(add(m, 0x80), v4)
mstore(add(m, 0xa0), v5)
result := keccak256(m, 0xc0)
}
}
/// @dev Returns `keccak256(abi.encode(v0, .., v5))`.
function hash(uint256 v0, uint256 v1, uint256 v2, uint256 v3, uint256 v4, uint256 v5)
internal
pure
returns (bytes32 result)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(m, v0)
mstore(add(m, 0x20), v1)
mstore(add(m, 0x40), v2)
mstore(add(m, 0x60), v3)
mstore(add(m, 0x80), v4)
mstore(add(m, 0xa0), v5)
result := keccak256(m, 0xc0)
}
}
/// @dev Returns `keccak256(abi.encode(v0, .., v6))`.
function hash(
bytes32 v0,
bytes32 v1,
bytes32 v2,
bytes32 v3,
bytes32 v4,
bytes32 v5,
bytes32 v6
) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(m, v0)
mstore(add(m, 0x20), v1)
mstore(add(m, 0x40), v2)
mstore(add(m, 0x60), v3)
mstore(add(m, 0x80), v4)
mstore(add(m, 0xa0), v5)
mstore(add(m, 0xc0), v6)
result := keccak256(m, 0xe0)
}
}
/// @dev Returns `keccak256(abi.encode(v0, .., v6))`.
function hash(
uint256 v0,
uint256 v1,
uint256 v2,
uint256 v3,
uint256 v4,
uint256 v5,
uint256 v6
) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(m, v0)
mstore(add(m, 0x20), v1)
mstore(add(m, 0x40), v2)
mstore(add(m, 0x60), v3)
mstore(add(m, 0x80), v4)
mstore(add(m, 0xa0), v5)
mstore(add(m, 0xc0), v6)
result := keccak256(m, 0xe0)
}
}
/// @dev Returns `keccak256(abi.encode(v0, .., v7))`.
function hash(
bytes32 v0,
bytes32 v1,
bytes32 v2,
bytes32 v3,
bytes32 v4,
bytes32 v5,
bytes32 v6,
bytes32 v7
) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(m, v0)
mstore(add(m, 0x20), v1)
mstore(add(m, 0x40), v2)
mstore(add(m, 0x60), v3)
mstore(add(m, 0x80), v4)
mstore(add(m, 0xa0), v5)
mstore(add(m, 0xc0), v6)
mstore(add(m, 0xe0), v7)
result := keccak256(m, 0x100)
}
}
/// @dev Returns `keccak256(abi.encode(v0, .., v7))`.
function hash(
uint256 v0,
uint256 v1,
uint256 v2,
uint256 v3,
uint256 v4,
uint256 v5,
uint256 v6,
uint256 v7
) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(m, v0)
mstore(add(m, 0x20), v1)
mstore(add(m, 0x40), v2)
mstore(add(m, 0x60), v3)
mstore(add(m, 0x80), v4)
mstore(add(m, 0xa0), v5)
mstore(add(m, 0xc0), v6)
mstore(add(m, 0xe0), v7)
result := keccak256(m, 0x100)
}
}
/// @dev Returns `keccak256(abi.encode(v0, .., v8))`.
function hash(
bytes32 v0,
bytes32 v1,
bytes32 v2,
bytes32 v3,
bytes32 v4,
bytes32 v5,
bytes32 v6,
bytes32 v7,
bytes32 v8
) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(m, v0)
mstore(add(m, 0x20), v1)
mstore(add(m, 0x40), v2)
mstore(add(m, 0x60), v3)
mstore(add(m, 0x80), v4)
mstore(add(m, 0xa0), v5)
mstore(add(m, 0xc0), v6)
mstore(add(m, 0xe0), v7)
mstore(add(m, 0x100), v8)
result := keccak256(m, 0x120)
}
}
/// @dev Returns `keccak256(abi.encode(v0, .., v8))`.
function hash(
uint256 v0,
uint256 v1,
uint256 v2,
uint256 v3,
uint256 v4,
uint256 v5,
uint256 v6,
uint256 v7,
uint256 v8
) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(m, v0)
mstore(add(m, 0x20), v1)
mstore(add(m, 0x40), v2)
mstore(add(m, 0x60), v3)
mstore(add(m, 0x80), v4)
mstore(add(m, 0xa0), v5)
mstore(add(m, 0xc0), v6)
mstore(add(m, 0xe0), v7)
mstore(add(m, 0x100), v8)
result := keccak256(m, 0x120)
}
}
/// @dev Returns `keccak256(abi.encode(v0, .., v9))`.
function hash(
bytes32 v0,
bytes32 v1,
bytes32 v2,
bytes32 v3,
bytes32 v4,
bytes32 v5,
bytes32 v6,
bytes32 v7,
bytes32 v8,
bytes32 v9
) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(m, v0)
mstore(add(m, 0x20), v1)
mstore(add(m, 0x40), v2)
mstore(add(m, 0x60), v3)
mstore(add(m, 0x80), v4)
mstore(add(m, 0xa0), v5)
mstore(add(m, 0xc0), v6)
mstore(add(m, 0xe0), v7)
mstore(add(m, 0x100), v8)
mstore(add(m, 0x120), v9)
result := keccak256(m, 0x140)
}
}
/// @dev Returns `keccak256(abi.encode(v0, .., v9))`.
function hash(
uint256 v0,
uint256 v1,
uint256 v2,
uint256 v3,
uint256 v4,
uint256 v5,
uint256 v6,
uint256 v7,
uint256 v8,
uint256 v9
) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(m, v0)
mstore(add(m, 0x20), v1)
mstore(add(m, 0x40), v2)
mstore(add(m, 0x60), v3)
mstore(add(m, 0x80), v4)
mstore(add(m, 0xa0), v5)
mstore(add(m, 0xc0), v6)
mstore(add(m, 0xe0), v7)
mstore(add(m, 0x100), v8)
mstore(add(m, 0x120), v9)
result := keccak256(m, 0x140)
}
}
/// @dev Returns `keccak256(abi.encode(v0, .., v10))`.
function hash(
bytes32 v0,
bytes32 v1,
bytes32 v2,
bytes32 v3,
bytes32 v4,
bytes32 v5,
bytes32 v6,
bytes32 v7,
bytes32 v8,
bytes32 v9,
bytes32 v10
) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(m, v0)
mstore(add(m, 0x20), v1)
mstore(add(m, 0x40), v2)
mstore(add(m, 0x60), v3)
mstore(add(m, 0x80), v4)
mstore(add(m, 0xa0), v5)
mstore(add(m, 0xc0), v6)
mstore(add(m, 0xe0), v7)
mstore(add(m, 0x100), v8)
mstore(add(m, 0x120), v9)
mstore(add(m, 0x140), v10)
result := keccak256(m, 0x160)
}
}
/// @dev Returns `keccak256(abi.encode(v0, .., v10))`.
function hash(
uint256 v0,
uint256 v1,
uint256 v2,
uint256 v3,
uint256 v4,
uint256 v5,
uint256 v6,
uint256 v7,
uint256 v8,
uint256 v9,
uint256 v10
) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(m, v0)
mstore(add(m, 0x20), v1)
mstore(add(m, 0x40), v2)
mstore(add(m, 0x60), v3)
mstore(add(m, 0x80), v4)
mstore(add(m, 0xa0), v5)
mstore(add(m, 0xc0), v6)
mstore(add(m, 0xe0), v7)
mstore(add(m, 0x100), v8)
mstore(add(m, 0x120), v9)
mstore(add(m, 0x140), v10)
result := keccak256(m, 0x160)
}
}
/// @dev Returns `keccak256(abi.encode(v0, .., v11))`.
function hash(
bytes32 v0,
bytes32 v1,
bytes32 v2,
bytes32 v3,
bytes32 v4,
bytes32 v5,
bytes32 v6,
bytes32 v7,
bytes32 v8,
bytes32 v9,
bytes32 v10,
bytes32 v11
) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(m, v0)
mstore(add(m, 0x20), v1)
mstore(add(m, 0x40), v2)
mstore(add(m, 0x60), v3)
mstore(add(m, 0x80), v4)
mstore(add(m, 0xa0), v5)
mstore(add(m, 0xc0), v6)
mstore(add(m, 0xe0), v7)
mstore(add(m, 0x100), v8)
mstore(add(m, 0x120), v9)
mstore(add(m, 0x140), v10)
mstore(add(m, 0x160), v11)
result := keccak256(m, 0x180)
}
}
/// @dev Returns `keccak256(abi.encode(v0, .., v11))`.
function hash(
uint256 v0,
uint256 v1,
uint256 v2,
uint256 v3,
uint256 v4,
uint256 v5,
uint256 v6,
uint256 v7,
uint256 v8,
uint256 v9,
uint256 v10,
uint256 v11
) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(m, v0)
mstore(add(m, 0x20), v1)
mstore(add(m, 0x40), v2)
mstore(add(m, 0x60), v3)
mstore(add(m, 0x80), v4)
mstore(add(m, 0xa0), v5)
mstore(add(m, 0xc0), v6)
mstore(add(m, 0xe0), v7)
mstore(add(m, 0x100), v8)
mstore(add(m, 0x120), v9)
mstore(add(m, 0x140), v10)
mstore(add(m, 0x160), v11)
result := keccak256(m, 0x180)
}
}
/// @dev Returns `keccak256(abi.encode(v0, .., v12))`.
function hash(
bytes32 v0,
bytes32 v1,
bytes32 v2,
bytes32 v3,
bytes32 v4,
bytes32 v5,
bytes32 v6,
bytes32 v7,
bytes32 v8,
bytes32 v9,
bytes32 v10,
bytes32 v11,
bytes32 v12
) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(m, v0)
mstore(add(m, 0x20), v1)
mstore(add(m, 0x40), v2)
mstore(add(m, 0x60), v3)
mstore(add(m, 0x80), v4)
mstore(add(m, 0xa0), v5)
mstore(add(m, 0xc0), v6)
mstore(add(m, 0xe0), v7)
mstore(add(m, 0x100), v8)
mstore(add(m, 0x120), v9)
mstore(add(m, 0x140), v10)
mstore(add(m, 0x160), v11)
mstore(add(m, 0x180), v12)
result := keccak256(m, 0x1a0)
}
}
/// @dev Returns `keccak256(abi.encode(v0, .., v12))`.
function hash(
uint256 v0,
uint256 v1,
uint256 v2,
uint256 v3,
uint256 v4,
uint256 v5,
uint256 v6,
uint256 v7,
uint256 v8,
uint256 v9,
uint256 v10,
uint256 v11,
uint256 v12
) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(m, v0)
mstore(add(m, 0x20), v1)
mstore(add(m, 0x40), v2)
mstore(add(m, 0x60), v3)
mstore(add(m, 0x80), v4)
mstore(add(m, 0xa0), v5)
mstore(add(m, 0xc0), v6)
mstore(add(m, 0xe0), v7)
mstore(add(m, 0x100), v8)
mstore(add(m, 0x120), v9)
mstore(add(m, 0x140), v10)
mstore(add(m, 0x160), v11)
mstore(add(m, 0x180), v12)
result := keccak256(m, 0x1a0)
}
}
/// @dev Returns `keccak256(abi.encode(v0, .., v13))`.
function hash(
bytes32 v0,
bytes32 v1,
bytes32 v2,
bytes32 v3,
bytes32 v4,
bytes32 v5,
bytes32 v6,
bytes32 v7,
bytes32 v8,
bytes32 v9,
bytes32 v10,
bytes32 v11,
bytes32 v12,
bytes32 v13
) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(m, v0)
mstore(add(m, 0x20), v1)
mstore(add(m, 0x40), v2)
mstore(add(m, 0x60), v3)
mstore(add(m, 0x80), v4)
mstore(add(m, 0xa0), v5)
mstore(add(m, 0xc0), v6)
mstore(add(m, 0xe0), v7)
mstore(add(m, 0x100), v8)
mstore(add(m, 0x120), v9)
mstore(add(m, 0x140), v10)
mstore(add(m, 0x160), v11)
mstore(add(m, 0x180), v12)
mstore(add(m, 0x1a0), v13)
result := keccak256(m, 0x1c0)
}
}
/// @dev Returns `keccak256(abi.encode(v0, .., v13))`.
function hash(
uint256 v0,
uint256 v1,
uint256 v2,
uint256 v3,
uint256 v4,
uint256 v5,
uint256 v6,
uint256 v7,
uint256 v8,
uint256 v9,
uint256 v10,
uint256 v11,
uint256 v12,
uint256 v13
) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(m, v0)
mstore(add(m, 0x20), v1)
mstore(add(m, 0x40), v2)
mstore(add(m, 0x60), v3)
mstore(add(m, 0x80), v4)
mstore(add(m, 0xa0), v5)
mstore(add(m, 0xc0), v6)
mstore(add(m, 0xe0), v7)
mstore(add(m, 0x100), v8)
mstore(add(m, 0x120), v9)
mstore(add(m, 0x140), v10)
mstore(add(m, 0x160), v11)
mstore(add(m, 0x180), v12)
mstore(add(m, 0x1a0), v13)
result := keccak256(m, 0x1c0)
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* BYTES32 BUFFER HASHING OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns `keccak256(abi.encode(buffer[0], .., buffer[buffer.length - 1]))`.
function hash(bytes32[] memory buffer) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
result := keccak256(add(buffer, 0x20), shl(5, mload(buffer)))
}
}
/// @dev Sets `buffer[i]` to `value`, without a bounds check.
/// Returns the `buffer` for function chaining.
function set(bytes32[] memory buffer, uint256 i, bytes32 value)
internal
pure
returns (bytes32[] memory)
{
/// @solidity memory-safe-assembly
assembly {
mstore(add(buffer, shl(5, add(1, i))), value)
}
return buffer;
}
/// @dev Sets `buffer[i]` to `value`, without a bounds check.
/// Returns the `buffer` for function chaining.
function set(bytes32[] memory buffer, uint256 i, uint256 value)
internal
pure
returns (bytes32[] memory)
{
/// @solidity memory-safe-assembly
assembly {
mstore(add(buffer, shl(5, add(1, i))), value)
}
return buffer;
}
/// @dev Returns `new bytes32[](n)`, without zeroing out the memory.
function malloc(uint256 n) internal pure returns (bytes32[] memory buffer) {
/// @solidity memory-safe-assembly
assembly {
buffer := mload(0x40)
mstore(buffer, n)
mstore(0x40, add(shl(5, add(1, n)), buffer))
}
}
/// @dev Frees memory that has been allocated for `buffer`.
/// No-op if `buffer.length` is zero, or if new memory has been allocated after `buffer`.
function free(bytes32[] memory buffer) internal pure {
/// @solidity memory-safe-assembly
assembly {
let n := mload(buffer)
mstore(shl(6, lt(iszero(n), eq(add(shl(5, add(1, n)), buffer), mload(0x40)))), buffer)
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EQUALITY CHECKS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns `a == abi.decode(b, (bytes32))`.
function eq(bytes32 a, bytes memory b) internal pure returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
result := and(eq(0x20, mload(b)), eq(a, mload(add(b, 0x20))))
}
}
/// @dev Returns `abi.decode(a, (bytes32)) == a`.
function eq(bytes memory a, bytes32 b) internal pure returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
result := and(eq(0x20, mload(a)), eq(b, mload(add(a, 0x20))))
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* BYTE SLICE HASHING OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the keccak256 of the slice from `start` to `end` (exclusive).
/// `start` and `end` are byte offsets.
function hash(bytes memory b, uint256 start, uint256 end)
internal
pure
returns (bytes32 result)
{
/// @solidity memory-safe-assembly
assembly {
let n := mload(b)
end := xor(end, mul(xor(end, n), lt(n, end)))
start := xor(start, mul(xor(start, n), lt(n, start)))
result := keccak256(add(add(b, 0x20), start), mul(gt(end, start), sub(end, start)))
}
}
/// @dev Returns the keccak256 of the slice from `start` to the end of the bytes.
function hash(bytes memory b, uint256 start) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
let n := mload(b)
start := xor(start, mul(xor(start, n), lt(n, start)))
result := keccak256(add(add(b, 0x20), start), mul(gt(n, start), sub(n, start)))
}
}
/// @dev Returns the keccak256 of the bytes.
function hash(bytes memory b) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
result := keccak256(add(b, 0x20), mload(b))
}
}
/// @dev Returns the keccak256 of the slice from `start` to `end` (exclusive).
/// `start` and `end` are byte offsets.
function hashCalldata(bytes calldata b, uint256 start, uint256 end)
internal
pure
returns (bytes32 result)
{
/// @solidity memory-safe-assembly
assembly {
end := xor(end, mul(xor(end, b.length), lt(b.length, end)))
start := xor(start, mul(xor(start, b.length), lt(b.length, start)))
let n := mul(gt(end, start), sub(end, start))
calldatacopy(mload(0x40), add(b.offset, start), n)
result := keccak256(mload(0x40), n)
}
}
/// @dev Returns the keccak256 of the slice from `start` to the end of the bytes.
function hashCalldata(bytes calldata b, uint256 start) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
start := xor(start, mul(xor(start, b.length), lt(b.length, start)))
let n := mul(gt(b.length, start), sub(b.length, start))
calldatacopy(mload(0x40), add(b.offset, start), n)
result := keccak256(mload(0x40), n)
}
}
/// @dev Returns the keccak256 of the bytes.
function hashCalldata(bytes calldata b) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
calldatacopy(mload(0x40), b.offset, b.length)
result := keccak256(mload(0x40), b.length)
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* SHA2-256 HELPERS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns `sha256(abi.encode(b))`. Yes, it's more efficient.
function sha2(bytes32 b) internal view returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, b)
result := mload(staticcall(gas(), 2, 0x00, 0x20, 0x01, 0x20))
if iszero(returndatasize()) { invalid() }
}
}
/// @dev Returns the sha256 of the slice from `start` to `end` (exclusive).
/// `start` and `end` are byte offsets.
function sha2(bytes memory b, uint256 start, uint256 end)
internal
view
returns (bytes32 result)
{
/// @solidity memory-safe-assembly
assembly {
let n := mload(b)
end := xor(end, mul(xor(end, n), lt(n, end)))
start := xor(start, mul(xor(start, n), lt(n, start)))
// forgefmt: disable-next-item
result := mload(staticcall(gas(), 2, add(add(b, 0x20), start),
mul(gt(end, start), sub(end, start)), 0x01, 0x20))
if iszero(returndatasize()) { invalid() }
}
}
/// @dev Returns the sha256 of the slice from `start` to the end of the bytes.
function sha2(bytes memory b, uint256 start) internal view returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
let n := mload(b)
start := xor(start, mul(xor(start, n), lt(n, start)))
// forgefmt: disable-next-item
result := mload(staticcall(gas(), 2, add(add(b, 0x20), start),
mul(gt(n, start), sub(n, start)), 0x01, 0x20))
if iszero(returndatasize()) { invalid() }
}
}
/// @dev Returns the sha256 of the bytes.
function sha2(bytes memory b) internal view returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
result := mload(staticcall(gas(), 2, add(b, 0x20), mload(b), 0x01, 0x20))
if iszero(returndatasize()) { invalid() }
}
}
/// @dev Returns the sha256 of the slice from `start` to `end` (exclusive).
/// `start` and `end` are byte offsets.
function sha2Calldata(bytes calldata b, uint256 start, uint256 end)
internal
view
returns (bytes32 result)
{
/// @solidity memory-safe-assembly
assembly {
end := xor(end, mul(xor(end, b.length), lt(b.length, end)))
start := xor(start, mul(xor(start, b.length), lt(b.length, start)))
let n := mul(gt(end, start), sub(end, start))
calldatacopy(mload(0x40), add(b.offset, start), n)
result := mload(staticcall(gas(), 2, mload(0x40), n, 0x01, 0x20))
if iszero(returndatasize()) { invalid() }
}
}
/// @dev Returns the sha256 of the slice from `start` to the end of the bytes.
function sha2Calldata(bytes calldata b, uint256 start) internal view returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
start := xor(start, mul(xor(start, b.length), lt(b.length, start)))
let n := mul(gt(b.length, start), sub(b.length, start))
calldatacopy(mload(0x40), add(b.offset, start), n)
result := mload(staticcall(gas(), 2, mload(0x40), n, 0x01, 0x20))
if iszero(returndatasize()) { invalid() }
}
}
/// @dev Returns the sha256 of the bytes.
function sha2Calldata(bytes calldata b) internal view returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
calldatacopy(mload(0x40), b.offset, b.length)
result := mload(staticcall(gas(), 2, mload(0x40), b.length, 0x01, 0x20))
if iszero(returndatasize()) { invalid() }
}
}
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.28;
int32 constant MIN_TICK = -88722835;
int32 constant MAX_TICK = 88722835;
uint32 constant MAX_TICK_MAGNITUDE = uint32(MAX_TICK);
uint32 constant MAX_TICK_SPACING = 698605;
uint32 constant FULL_RANGE_ONLY_TICK_SPACING = 0;
// We use this address to represent the native token within the protocol
address constant NATIVE_TOKEN_ADDRESS = address(0);
// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.28;
// 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") {
let addend := mul(roundUp, 0x3)
// lt 2**96 after rounding up
switch lt(sqrtRatio, sub(0x1000000000000000000000000, addend))
case 1 { r := shr(2, add(sqrtRatio, addend)) }
default {
// 2**34 - 1
addend := mul(roundUp, 0x3ffffffff)
// lt 2**128 after rounding up
switch lt(sqrtRatio, sub(0x100000000000000000000000000000000, addend))
case 1 { r := or(TWO_POW_94, shr(34, add(sqrtRatio, addend))) }
default {
addend := mul(roundUp, 0x3ffffffffffffffff)
// lt 2**160 after rounding up
switch lt(sqrtRatio, sub(0x10000000000000000000000000000000000000000, addend))
case 1 { r := or(TWO_POW_95, shr(66, add(sqrtRatio, addend))) }
default {
// 2**98 - 1
addend := mul(roundUp, 0x3ffffffffffffffffffffffff)
switch lt(sqrtRatio, sub(0x1000000000000000000000000000000000000000000000000, addend))
case 1 { r := or(BIT_MASK, shr(98, add(sqrtRatio, addend))) }
default {
// cast sig "ValueOverflowsSqrtRatioContainer()"
mstore(0, shl(224, 0xa10459f4))
revert(0, 4)
}
}
}
}
}
}
// 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: UNLICENSED
pragma solidity =0.8.28;
import {FixedPointMathLib} from "solady/utils/FixedPointMathLib.sol";
import {SqrtRatio, toSqrtRatio, MAX_FIXED_VALUE_ROUND_UP} from "../types/sqrtRatio.sol";
error ZeroLiquidityNextSqrtRatioFromAmount0();
// Compute the next ratio from a delta amount0, always rounded towards starting price for input, and
// away from starting price for output
function nextSqrtRatioFromAmount0(SqrtRatio _sqrtRatio, uint128 liquidity, int128 amount)
pure
returns (SqrtRatio sqrtRatioNext)
{
if (amount == 0) {
return _sqrtRatio;
}
if (liquidity == 0) {
revert ZeroLiquidityNextSqrtRatioFromAmount0();
}
uint256 sqrtRatio = _sqrtRatio.toFixed();
uint256 liquidityX128 = uint256(liquidity) << 128;
uint256 amountAbs = FixedPointMathLib.abs(int256(amount));
if (amount < 0) {
unchecked {
// multiplication will revert on overflow, so we return the maximum value for the type
if (amountAbs > 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 denominator;
unchecked {
uint256 denominatorP1 = liquidityX128 / sqrtRatio;
// 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
denominator = denominatorP1 + amountAbs;
}
sqrtRatioNext = toSqrtRatio(FixedPointMathLib.divUp(liquidityX128, denominator), true);
}
}
error ZeroLiquidityNextSqrtRatioFromAmount1();
function nextSqrtRatioFromAmount1(SqrtRatio _sqrtRatio, uint128 liquidity, int128 amount)
pure
returns (SqrtRatio sqrtRatioNext)
{
if (amount == 0) {
return _sqrtRatio;
}
if (liquidity == 0) {
revert ZeroLiquidityNextSqrtRatioFromAmount1();
}
uint256 sqrtRatio = _sqrtRatio.toFixed();
unchecked {
uint256 shiftedAmountAbs = FixedPointMathLib.abs(int256(amount)) << 128;
uint256 quotient = shiftedAmountAbs / liquidity;
if (amount < 0) {
if (quotient >= sqrtRatio) {
// Underflow => return 0
return SqrtRatio.wrap(0);
}
uint256 sqrtRatioNextFixed = sqrtRatio - quotient;
assembly ("memory-safe") {
// subtraction of 1 is safe because sqrtRatio > quotient => sqrtRatio - quotient >= 1
sqrtRatioNextFixed := sub(sqrtRatioNextFixed, iszero(iszero(mod(shiftedAmountAbs, liquidity))))
}
sqrtRatioNext = toSqrtRatio(sqrtRatioNextFixed, false);
} else {
uint256 sum = sqrtRatio + quotient;
if (sum < sqrtRatio || sum > type(uint192).max) {
return SqrtRatio.wrap(type(uint96).max);
}
sqrtRatioNext = toSqrtRatio(sum, false);
}
}
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.28;
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)
{
uint256 aFixed = sqrtRatioA.toFixed();
uint256 bFixed = sqrtRatioB.toFixed();
(sqrtRatioLower, sqrtRatioUpper) = (FixedPointMathLib.min(aFixed, bFixed), FixedPointMathLib.max(aFixed, bFixed));
}
function amount0Delta(SqrtRatio sqrtRatioA, SqrtRatio sqrtRatioB, uint128 liquidity, bool roundUp)
pure
returns (uint128 amount0)
{
unchecked {
(uint256 sqrtRatioLower, uint256 sqrtRatioUpper) = sortAndConvertToFixedSqrtRatios(sqrtRatioA, sqrtRatioB);
if (roundUp) {
uint256 result0 = FixedPointMathLib.fullMulDivUp(
(uint256(liquidity) << 128), (sqrtRatioUpper - sqrtRatioLower), sqrtRatioUpper
);
uint256 result = FixedPointMathLib.divUp(result0, sqrtRatioLower);
if (result > type(uint128).max) revert Amount0DeltaOverflow();
amount0 = uint128(result);
} else {
uint256 result0 = FixedPointMathLib.fullMulDiv(
(uint256(liquidity) << 128), (sqrtRatioUpper - sqrtRatioLower), sqrtRatioUpper
);
uint256 result = result0 / sqrtRatioLower;
if (result > type(uint128).max) revert Amount0DeltaOverflow();
amount0 = uint128(result);
}
}
}
function amount1Delta(SqrtRatio sqrtRatioA, SqrtRatio sqrtRatioB, uint128 liquidity, bool roundUp)
pure
returns (uint128 amount1)
{
unchecked {
(uint256 sqrtRatioLower, uint256 sqrtRatioUpper) = sortAndConvertToFixedSqrtRatios(sqrtRatioA, sqrtRatioB);
uint256 difference = sqrtRatioUpper - sqrtRatioLower;
if (roundUp) {
uint256 result = FixedPointMathLib.fullMulDivN(difference, liquidity, 128);
assembly {
// addition is safe from overflow because the result of fullMulDivN will never equal type(uint256).max
result :=
add(result, iszero(iszero(mulmod(difference, liquidity, 0x100000000000000000000000000000000))))
}
if (result > type(uint128).max) revert Amount1DeltaOverflow();
amount1 = uint128(result);
} else {
uint256 result = FixedPointMathLib.fullMulDivN(difference, liquidity, 128);
if (result > type(uint128).max) revert Amount1DeltaOverflow();
amount1 = uint128(result);
}
}
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.28;
function isPriceIncreasing(int128 amount, bool isToken1) pure returns (bool increasing) {
assembly ("memory-safe") {
increasing := xor(isToken1, slt(amount, 0))
}
}
// 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 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 `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 := or(iszero(iszero(x)), iszero(iszero(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: UNLICENSED
pragma solidity =0.8.28;
// Exposes all the storage of a contract via view methods.
// Absent https://eips.ethereum.org/EIPS/eip-2330 this makes it easier to access specific pieces of state in the inheriting contract.
interface IExposedStorage {
// Loads each slot after the function selector from the contract's storage and returns all of them.
function sload() external view;
// Loads each slot after the function selector from the contract's transient storage and returns all of them.
function tload() external view;
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.28;
interface ILocker {
function locked(uint256 id) external;
}
interface IForwardee {
function forwarded(uint256 id, address originalLocker) external;
}
interface IPayer {
function payCallback(uint256 id, address token) external;
}
interface IFlashAccountant {
error NotLocked();
error LockerOnly();
error NoPaymentMade();
error DebtsNotZeroed(uint256 id);
// Thrown if the contract receives too much payment in the payment callback or from a direct native token transfer
error PaymentOverflow();
error PayReentrance();
// Create a lock context
// 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
// In addition, any data returned from ILocker#locked is also returned from this function exactly as is, i.e. with no additional encoding or decoding
// Reverts are also bubbled up
function lock() external;
// Forward the lock from the current locker to the given address
// Any additional calldata is also passed through to the forwardee, with no additional encoding
// In addition, any data returned from IForwardee#forwarded is also returned from this function exactly as is, i.e. with no additional encoding or decoding
// Reverts are also bubbled up
function forward(address to) external;
// Pays the given amount of token, by calling the payCallback function on the caller to afford them the opportunity to make the payment.
// This function, unlike lock and forward, does not return any of the returndata from the callback.
// This function also cannot be re-entered like lock and forward.
// Must be locked, as the contract accounts the payment against the current locker's debts.
// Token must not be the NATIVE_TOKEN_ADDRESS, as the `balanceOf` calls will fail.
// If you want to pay in the chain's native token, simply transfer it to this contract using a call.
// The payer must implement payCallback in which they must transfer the token to Core.
function pay(address token) external returns (uint128 payment);
// Withdraws a token amount from the accountant to the given recipient.
// The contract must be locked, as it tracks the withdrawn amount against the current locker's delta.
function withdraw(address token, address recipient, uint128 amount) external;
// This contract can receive ETH as a payment as well
receive() external payable;
}
File 3 of 4: WETH9
// Copyright (C) 2015, 2016, 2017 Dapphub
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
pragma solidity ^0.4.18;
contract WETH9 {
string public name = "Wrapped Ether";
string public symbol = "WETH";
uint8 public decimals = 18;
event Approval(address indexed src, address indexed guy, uint wad);
event Transfer(address indexed src, address indexed dst, uint wad);
event Deposit(address indexed dst, uint wad);
event Withdrawal(address indexed src, uint wad);
mapping (address => uint) public balanceOf;
mapping (address => mapping (address => uint)) public allowance;
function() public payable {
deposit();
}
function deposit() public payable {
balanceOf[msg.sender] += msg.value;
Deposit(msg.sender, msg.value);
}
function withdraw(uint wad) public {
require(balanceOf[msg.sender] >= wad);
balanceOf[msg.sender] -= wad;
msg.sender.transfer(wad);
Withdrawal(msg.sender, wad);
}
function totalSupply() public view returns (uint) {
return this.balance;
}
function approve(address guy, uint wad) public returns (bool) {
allowance[msg.sender][guy] = wad;
Approval(msg.sender, guy, wad);
return true;
}
function transfer(address dst, uint wad) public returns (bool) {
return transferFrom(msg.sender, dst, wad);
}
function transferFrom(address src, address dst, uint wad)
public
returns (bool)
{
require(balanceOf[src] >= wad);
if (src != msg.sender && allowance[src][msg.sender] != uint(-1)) {
require(allowance[src][msg.sender] >= wad);
allowance[src][msg.sender] -= wad;
}
balanceOf[src] -= wad;
balanceOf[dst] += wad;
Transfer(src, dst, wad);
return true;
}
}
/*
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<http://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
*/File 4 of 4: AllowanceHolder
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;
import {AllowanceHolderBase} from "./AllowanceHolderBase.sol";
import {TransientStorage} from "./TransientStorage.sol";
/// @custom:security-contact security@0x.org
contract AllowanceHolder is TransientStorage, AllowanceHolderBase {
constructor() {
require(address(this) == 0x0000000000001fF3684f28c67538d4D072C22734 || block.chainid == 31337);
}
/// @inheritdoc AllowanceHolderBase
function exec(address operator, address token, uint256 amount, address payable target, bytes calldata data)
internal
override
returns (bytes memory)
{
(bytes memory result, address sender, TSlot allowance) = _exec(operator, token, amount, target, data);
// EIP-3074 seems unlikely
if (sender != tx.origin) {
_set(allowance, 0);
}
return result;
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;
import {IAllowanceHolder} from "./IAllowanceHolder.sol";
import {IERC20} from "../IERC20.sol";
import {SafeTransferLib} from "../vendor/SafeTransferLib.sol";
import {CheckCall} from "../utils/CheckCall.sol";
import {FreeMemory} from "../utils/FreeMemory.sol";
import {TransientStorageLayout} from "./TransientStorageLayout.sol";
/// @notice Thrown when validating the target, avoiding executing against an ERC20 directly
error ConfusedDeputy();
abstract contract AllowanceHolderBase is TransientStorageLayout, FreeMemory {
using SafeTransferLib for IERC20;
using CheckCall for address payable;
function _rejectIfERC20(address payable maybeERC20, bytes calldata data) private view DANGEROUS_freeMemory {
// We could just choose a random address for this check, but to make
// confused deputy attacks harder for tokens that might be badly behaved
// (e.g. tokens with blacklists), we choose to copy the first argument
// out of `data` and mask it as an address. If there isn't enough
// `data`, we use 0xdead instead.
address target;
if (data.length > 0x10) {
target = address(uint160(bytes20(data[0x10:])));
}
// EIP-1352 (not adopted) specifies 0xffff as the maximum precompile
if (target <= address(0xffff)) {
// 0xdead is a conventional burn address; we assume that it is not treated specially
target = address(0xdead);
}
bytes memory testData = abi.encodeCall(IERC20.balanceOf, target);
if (maybeERC20.checkCall(testData, 0x20)) revert ConfusedDeputy();
}
function _msgSender() private view returns (address sender) {
if ((sender = msg.sender) == address(this)) {
assembly ("memory-safe") {
sender := shr(0x60, calldataload(sub(calldatasize(), 0x14)))
}
}
}
/// @dev This virtual function provides the implementation for the function
/// of the same name in `IAllowanceHolder`. It is unimplemented in this
/// base contract to accommodate the customization required to support
/// both chains that have EIP-1153 (transient storage) and those that
/// don't.
function exec(address operator, address token, uint256 amount, address payable target, bytes calldata data)
internal
virtual
returns (bytes memory result);
/// @dev This is the majority of the implementation of IAllowanceHolder.exec
/// . The arguments have the same meaning as documented there.
/// @return result
/// @return sender The (possibly forwarded) message sender that is
/// requesting the allowance be set. Provided to avoid
/// duplicated computation in customized `exec`
/// @return allowance The slot where the ephemeral allowance is
/// stored. Provided to avoid duplicated computation in
/// customized `exec`
function _exec(address operator, address token, uint256 amount, address payable target, bytes calldata data)
internal
returns (bytes memory result, address sender, TSlot allowance)
{
// This contract has no special privileges, except for the allowances it
// holds. In order to prevent abusing those allowances, we prohibit
// sending arbitrary calldata (doing `target.call(data)`) to any
// contract that might be an ERC20.
_rejectIfERC20(target, data);
sender = _msgSender();
allowance = _ephemeralAllowance(operator, sender, token);
_set(allowance, amount);
// For gas efficiency we're omitting a bunch of checks here. Notably,
// we're omitting the check that `address(this)` has sufficient value to
// send (we know it does), and we're omitting the check that `target`
// contains code (we already checked in `_rejectIfERC20`).
assembly ("memory-safe") {
result := mload(0x40)
calldatacopy(result, data.offset, data.length)
// ERC-2771 style msgSender forwarding https://eips.ethereum.org/EIPS/eip-2771
mstore(add(result, data.length), shl(0x60, sender))
let success := call(gas(), target, callvalue(), result, add(data.length, 0x14), 0x00, 0x00)
let ptr := add(result, 0x20)
returndatacopy(ptr, 0x00, returndatasize())
switch success
case 0 { revert(ptr, returndatasize()) }
default {
mstore(result, returndatasize())
mstore(0x40, add(ptr, returndatasize()))
}
}
}
/// @dev This provides the implementation of the function of the same name
/// in `IAllowanceHolder`.
function transferFrom(address token, address owner, address recipient, uint256 amount) internal {
// msg.sender is the assumed and later validated operator
TSlot allowance = _ephemeralAllowance(msg.sender, owner, token);
// validation of the ephemeral allowance for operator, owner, token via
// uint underflow
_set(allowance, _get(allowance) - amount);
// `safeTransferFrom` does not check that `token` actually contains
// code. It is the responsibility of integrating code to check for that
// if vacuous success is a security concern.
IERC20(token).safeTransferFrom(owner, recipient, amount);
}
fallback() external payable {
uint256 selector;
assembly ("memory-safe") {
selector := shr(0xe0, calldataload(0x00))
}
if (selector == uint256(uint32(IAllowanceHolder.transferFrom.selector))) {
address token;
address owner;
address recipient;
uint256 amount;
assembly ("memory-safe") {
// We do not validate `calldatasize()`. If the calldata is short
// enough that `amount` is null, this call is a harmless no-op.
let err := callvalue()
token := calldataload(0x04)
err := or(err, shr(0xa0, token))
owner := calldataload(0x24)
err := or(err, shr(0xa0, owner))
recipient := calldataload(0x44)
err := or(err, shr(0xa0, recipient))
if err { revert(0x00, 0x00) }
amount := calldataload(0x64)
}
transferFrom(token, owner, recipient, amount);
// return true;
assembly ("memory-safe") {
mstore(0x00, 0x01)
return(0x00, 0x20)
}
} else if (selector == uint256(uint32(IAllowanceHolder.exec.selector))) {
address operator;
address token;
uint256 amount;
address payable target;
bytes calldata data;
assembly ("memory-safe") {
// We do not validate `calldatasize()`. If the calldata is short
// enough that `data` is null, it will alias `operator`. This
// results in either an OOG (because `operator` encodes a
// too-long `bytes`) or is a harmless no-op (because `operator`
// encodes a valid length, but not an address capable of making
// calls). If the calldata is _so_ sort that `target` is null,
// we will revert because it contains no code.
operator := calldataload(0x04)
let err := shr(0xa0, operator)
token := calldataload(0x24)
err := or(err, shr(0xa0, token))
amount := calldataload(0x44)
target := calldataload(0x64)
err := or(err, shr(0xa0, target))
if err { revert(0x00, 0x00) }
// We perform no validation that `data` is reasonable.
data.offset := add(0x04, calldataload(0x84))
data.length := calldataload(data.offset)
data.offset := add(0x20, data.offset)
}
bytes memory result = exec(operator, token, amount, target, data);
// return result;
assembly ("memory-safe") {
let returndata := sub(result, 0x20)
mstore(returndata, 0x20)
return(returndata, add(0x40, mload(result)))
}
} else if (selector == uint256(uint32(IERC20.balanceOf.selector))) {
// balanceOf(address) reverts with a single byte of returndata,
// making it more gas efficient to pass the `_rejectERC20` check
assembly ("memory-safe") {
revert(0x00, 0x01)
}
} else {
// emulate standard Solidity behavior
assembly ("memory-safe") {
revert(0x00, 0x00)
}
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;
import {TransientStorageBase} from "./TransientStorageBase.sol";
abstract contract TransientStorage is TransientStorageBase {
function _get(TSlot s) internal view override returns (uint256 r) {
assembly ("memory-safe") {
r := tload(s)
}
}
function _set(TSlot s, uint256 v) internal override {
assembly ("memory-safe") {
tstore(s, v)
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;
interface IAllowanceHolder {
/// @notice Executes against `target` with the `data` payload. Prior to execution, token permits
/// are temporarily stored for the duration of the transaction. These permits can be
/// consumed by the `operator` during the execution
/// @notice `operator` consumes the funds during its operations by calling back into
/// `AllowanceHolder` with `transferFrom`, consuming a token permit.
/// @dev Neither `exec` nor `transferFrom` check that `token` contains code.
/// @dev msg.sender is forwarded to target appended to the msg data (similar to ERC-2771)
/// @param operator An address which is allowed to consume the token permits
/// @param token The ERC20 token the caller has authorised to be consumed
/// @param amount The quantity of `token` the caller has authorised to be consumed
/// @param target A contract to execute operations with `data`
/// @param data The data to forward to `target`
/// @return result The returndata from calling `target` with `data`
/// @notice If calling `target` with `data` reverts, the revert is propagated
function exec(address operator, address token, uint256 amount, address payable target, bytes calldata data)
external
payable
returns (bytes memory result);
/// @notice The counterpart to `exec` which allows for the consumption of token permits later
/// during execution
/// @dev *DOES NOT* check that `token` contains code. This function vacuously succeeds if
/// `token` is empty.
/// @dev can only be called by the `operator` previously registered in `exec`
/// @param token The ERC20 token to transfer
/// @param owner The owner of tokens to transfer
/// @param recipient The destination/beneficiary of the ERC20 `transferFrom`
/// @param amount The quantity of `token` to transfer`
/// @return true
function transferFrom(address token, address owner, address recipient, uint256 amount) external returns (bool);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;
interface IERC20 {
function totalSupply() external view returns (uint256);
function balanceOf(address) external view returns (uint256);
function transfer(address, uint256) external returns (bool);
function transferFrom(address, address, uint256) external returns (bool);
function approve(address, uint256) external returns (bool);
function allowance(address, address) external view returns (uint256);
event Transfer(address indexed, address indexed, uint256);
event Approval(address indexed, address indexed, uint256);
}
interface IERC20Meta is IERC20 {
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function decimals() external view returns (uint8);
}
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.25;
import {IERC20} from "../IERC20.sol";
/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
/// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer.
/// @dev Note that none of the functions in this library check that a token has code at all! That responsibility is delegated to the caller.
library SafeTransferLib {
uint32 private constant _TRANSFER_FROM_FAILED_SELECTOR = 0x7939f424; // bytes4(keccak256("TransferFromFailed()"))
uint32 private constant _TRANSFER_FAILED_SELECTOR = 0x90b8ec18; // bytes4(keccak256("TransferFailed()"))
uint32 private constant _APPROVE_FAILED_SELECTOR = 0x3e3f8f73; // bytes4(keccak256("ApproveFailed()"))
/*//////////////////////////////////////////////////////////////
ETH OPERATIONS
//////////////////////////////////////////////////////////////*/
function safeTransferETH(address payable to, uint256 amount) internal {
assembly ("memory-safe") {
// Transfer the ETH and store if it succeeded or not.
if iszero(call(gas(), to, amount, 0, 0, 0, 0)) {
let freeMemoryPointer := mload(0x40)
returndatacopy(freeMemoryPointer, 0, returndatasize())
revert(freeMemoryPointer, returndatasize())
}
}
}
/*//////////////////////////////////////////////////////////////
ERC20 OPERATIONS
//////////////////////////////////////////////////////////////*/
function safeTransferFrom(IERC20 token, address from, address to, uint256 amount) internal {
assembly ("memory-safe") {
// Get a pointer to some free memory.
let freeMemoryPointer := mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.
mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), and(from, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "from" argument.
mstore(add(freeMemoryPointer, 36), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
mstore(add(freeMemoryPointer, 68), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.
// We use 100 because the length of our calldata totals up like so: 4 + 32 * 3.
// We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
if iszero(call(gas(), token, 0, freeMemoryPointer, 100, 0, 32)) {
returndatacopy(freeMemoryPointer, 0, returndatasize())
revert(freeMemoryPointer, returndatasize())
}
// We check that the call either returned exactly 1 (can't just be non-zero data), or had no
// return data.
if iszero(or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize()))) {
mstore(0, _TRANSFER_FROM_FAILED_SELECTOR)
revert(0x1c, 0x04)
}
}
}
function safeTransfer(IERC20 token, address to, uint256 amount) internal {
assembly ("memory-safe") {
// Get a pointer to some free memory.
let freeMemoryPointer := mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.
mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.
// We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
// We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
if iszero(call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)) {
returndatacopy(freeMemoryPointer, 0, returndatasize())
revert(freeMemoryPointer, returndatasize())
}
// We check that the call either returned exactly 1 (can't just be non-zero data), or had no
// return data.
if iszero(or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize()))) {
mstore(0, _TRANSFER_FAILED_SELECTOR)
revert(0x1c, 0x04)
}
}
}
function safeApprove(IERC20 token, address to, uint256 amount) internal {
assembly ("memory-safe") {
// Get a pointer to some free memory.
let freeMemoryPointer := mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.
mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.
// We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
// We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
if iszero(call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)) {
returndatacopy(freeMemoryPointer, 0, returndatasize())
revert(freeMemoryPointer, returndatasize())
}
// We check that the call either returned exactly 1 (can't just be non-zero data), or had no
// return data.
if iszero(or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize()))) {
mstore(0, _APPROVE_FAILED_SELECTOR)
revert(0x1c, 0x04)
}
}
}
function safeApproveIfBelow(IERC20 token, address spender, uint256 amount) internal {
uint256 allowance = token.allowance(address(this), spender);
if (allowance < amount) {
if (allowance != 0) {
safeApprove(token, spender, 0);
}
safeApprove(token, spender, type(uint256).max);
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;
library CheckCall {
/**
* @notice `staticcall` another contract. Check the length of the return without reading it.
* @dev contains protections against EIP-150-induced insufficient gas griefing
* @dev reverts iff the target is not a contract or we encounter an out-of-gas
* @return success true iff the call succeeded and returned at least `minReturnBytes` of return
* data
* @param target the contract (reverts if non-contract) on which to make the `staticcall`
* @param data the calldata to pass
* @param minReturnBytes `success` is false if the call doesn't return at least this much return
* data
*/
function checkCall(address target, bytes memory data, uint256 minReturnBytes)
internal
view
returns (bool success)
{
assembly ("memory-safe") {
let beforeGas
{
let offset := add(data, 0x20)
let length := mload(data)
beforeGas := gas()
success := staticcall(gas(), target, offset, length, 0x00, 0x00)
}
// `verbatim` can't work in inline assembly. Assignment of a value to a variable costs
// gas (although how much is unpredictable because it depends on the Yul/IR optimizer),
// as does the `GAS` opcode itself. Therefore, the `gas()` below returns less than the
// actual amount of gas available for computation at the end of the call. Also
// `beforeGas` above is exclusive of the preparing of the stack for `staticcall` as well
// as the gas costs of the `staticcall` paid by the caller (e.g. cold account
// access). All this makes the check below slightly too conservative. However, we do not
// correct this because the correction would become outdated (possibly too permissive)
// if the opcodes are repriced.
let afterGas := gas()
for {} 1 {} {
if iszero(returndatasize()) {
// The absence of returndata means that it's possible that either we called an
// address without code or that the call reverted due to out-of-gas. We must
// check.
switch success
case 0 {
// Check whether the call reverted due to out-of-gas.
// https://eips.ethereum.org/EIPS/eip-150
// https://ronan.eth.limo/blog/ethereum-gas-dangers/
// We apply the "all but one 64th" rule twice because `target` could
// plausibly be a proxy. We apply it only twice because we assume only a
// single level of indirection.
let remainingGas := shr(6, beforeGas)
remainingGas := add(remainingGas, shr(6, sub(beforeGas, remainingGas)))
if iszero(lt(remainingGas, afterGas)) {
// The call failed due to not enough gas left. We deliberately consume
// all remaining gas with `invalid` (instead of `revert`) to make this
// failure distinguishable to our caller.
invalid()
}
// `success` is false because the call reverted
}
default {
// Check whether we called an address with no code (gas expensive).
if iszero(extcodesize(target)) { revert(0x00, 0x00) }
// We called a contract which returned no data; this is only a success if we
// were expecting no data.
success := iszero(minReturnBytes)
}
break
}
// The presence of returndata indicates that we definitely executed code. It also
// means that the call didn't revert due to out-of-gas, if it reverted. We can omit
// a bunch of checks.
success := gt(success, lt(returndatasize(), minReturnBytes))
break
}
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;
abstract contract FreeMemory {
modifier DANGEROUS_freeMemory() {
uint256 freeMemPtr;
assembly ("memory-safe") {
freeMemPtr := mload(0x40)
}
_;
assembly ("memory-safe") {
mstore(0x40, freeMemPtr)
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;
import {TransientStorageBase} from "./TransientStorageBase.sol";
abstract contract TransientStorageLayout is TransientStorageBase {
/// @dev The key for this ephemeral allowance is keccak256(abi.encodePacked(operator, owner, token)).
function _ephemeralAllowance(address operator, address owner, address token) internal pure returns (TSlot r) {
assembly ("memory-safe") {
let ptr := mload(0x40)
mstore(0x28, token)
mstore(0x14, owner)
mstore(0x00, operator)
// allowance slot is keccak256(abi.encodePacked(operator, owner, token))
r := keccak256(0x0c, 0x3c)
// restore dirtied free pointer
mstore(0x40, ptr)
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;
abstract contract TransientStorageBase {
type TSlot is bytes32;
function _get(TSlot s) internal view virtual returns (uint256);
function _set(TSlot s, uint256 v) internal virtual;
}