Address Contract Verified
Address
0x9514D3496F46572e8461da381B200812D5Db202C
Balance
0 ETH
Nonce
1
Code Size
5041 bytes
Creator
0x6Ac005d9...cE42 at tx 0x5f3dea7d...f36ad5
Indexed Transactions
0
Contract Bytecode
5041 bytes
0x608060405234801561000f575f80fd5b50600436106100e5575f3560e01c80638786290e11610088578063e30c397811610063578063e30c3978146102f7578063ec41da2f14610315578063ef35e9211461031f578063f2fde38b14610329575f80fd5b80638786290e1461029c5780638da5cb5b146102a6578063bd1ef064146102e4575f80fd5b8063368e4d16116100c3578063368e4d161461012d578063715018a61461027757806379ba50971461028157806381d3637614610289575f80fd5b8063079766a8146100e957806314dc966d14610105578063191fe1ed1461010f575b5f80fd5b6100f261520881565b6040519081526020015b60405180910390f35b6100f262030d4081565b61011861271081565b60405163ffffffff90911681526020016100fc565b61026a60408051610120810182525f80825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e0810182905261010081019190915250604080516101208101825260025463ffffffff80821683526401000000008204811660208401526801000000000000000082048116938301939093526c010000000000000000000000008104909216606082015270010000000000000000000000000000000080830467ffffffffffffffff1660808301527801000000000000000000000000000000000000000000000000830461ffff90811660a08401527a01000000000000000000000000000000000000000000000000000090930490921660c08201526003546fffffffffffffffffffffffffffffffff80821660e084015292900490911661010082015290565b6040516100fc9190610ed2565b61027f61033c565b005b61027f61034f565b6100f261029736600461102c565b6103cb565b6100f2620493e081565b5f5473ffffffffffffffffffffffffffffffffffffffff165b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016100fc565b61027f6102f2366004611179565b610775565b60015473ffffffffffffffffffffffffffffffffffffffff166102bf565b6100f26207a12081565b6100f2620f424081565b61027f610337366004611229565b610b61565b610344610c10565b61034d5f610c62565b565b600154339073ffffffffffffffffffffffffffffffffffffffff1681146103bf576040517f118cdaa700000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff821660048201526024015b60405180910390fd5b6103c881610c62565b50565b5f8981036103da57505f610767565b604080516101208101825260025463ffffffff8082168084526401000000008304821660208501526801000000000000000083048216948401949094526c01000000000000000000000000820416606083015270010000000000000000000000000000000080820467ffffffffffffffff1660808401527801000000000000000000000000000000000000000000000000820461ffff90811660a08501527a01000000000000000000000000000000000000000000000000000090920490911660c08301526003546fffffffffffffffffffffffffffffffff80821660e085015291900416610100820152905f906104d3908d90611289565b615208836020015163ffffffff166104eb91906112a6565b6104f591906112a6565b905089156105155760408201516105129063ffffffff16826112a6565b90505b6003896006811115610529576105296112b9565b60ff16111561054a5760608201516105479063ffffffff16826112a6565b90505b5f61271063ffffffff168360a0015161ffff168361056b8660800151610c93565b6105759190611289565b61057f9190611289565b61058991906112e6565b8360e001516fffffffffffffffffffffffffffffffff166105aa91906112a6565b90505f6105bd8f8f8f8760c00151610cba565b6040517fbb2952fc000000000000000000000000000000000000000000000000000000008152600481018290529091507f0000000000000000000000007f39c581f595b53c5cb19bd0b3f8da6c935e2ca073ffffffffffffffffffffffffffffffffffffffff169063bb2952fc90602401602060405180830381865afa158015610649573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061066d919061131e565b61067790836112a6565b91508361010001516fffffffffffffffffffffffffffffffff168211156106b3578361010001516fffffffffffffffffffffffffffffffff1691505b6040517fb0e38900000000000000000000000000000000000000000000000000000000008152600481018390527f0000000000000000000000007f39c581f595b53c5cb19bd0b3f8da6c935e2ca073ffffffffffffffffffffffffffffffffffffffff169063b0e3890090602401602060405180830381865afa15801561073c573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610760919061131e565b9450505050505b9a9950505050505050505050565b61077d610c10565b6207a1208963ffffffff1611156107c8576040517f5b3d532400000000000000000000000000000000000000000000000000000000815263ffffffff8a1660048201526024016103b6565b620f42408863ffffffff161115610813576040517f627df79000000000000000000000000000000000000000000000000000000000815263ffffffff891660048201526024016103b6565b62030d408763ffffffff16111561085e576040517fbb94b87800000000000000000000000000000000000000000000000000000000815263ffffffff881660048201526024016103b6565b620493e08663ffffffff1611156108a9576040517fdfb3c83600000000000000000000000000000000000000000000000000000000815263ffffffff871660048201526024016103b6565b604080516101208101825263ffffffff808c168083528b8216602084018190528b8316848601819052928b166060850181905267ffffffffffffffff8b166080860181905261ffff808c1660a08801819052908b1660c088018190526fffffffffffffffffffffffffffffffff808c1660e08a01819052908b16610100909901899052600280547a0100000000000000000000000000000000000000000000000000009093027fffffffff0000ffffffffffffffffffffffffffffffffffffffffffffffffffff78010000000000000000000000000000000000000000000000009095027fffffffffffff0000ffffffffffffffffffffffffffffffffffffffffffffffff700100000000000000000000000000000000978802167fffffffffffff00000000000000000000ffffffffffffffffffffffffffffffff6c010000000000000000000000009099027fffffffffffffffffffffffffffffffff00000000ffffffffffffffffffffffff68010000000000000000909d029c909c167fffffffffffffffffffffffffffffffff0000000000000000ffffffffffffffff640100000000909b027fffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000909716909b17959095179890981698909817989098179490941617939093179290921692909217909255910217600355517f9911ad05010f8fa78761b8d8f6f758f663a83a510d4b528cffa6d3f7bcfa2dcd90610b4e908b908b908b908b908b908b908b908b908b9063ffffffff998a1681529789166020890152958816604088015293909616606086015267ffffffffffffffff91909116608085015261ffff90811660a08501529390931660c08301526fffffffffffffffffffffffffffffffff92831660e08301529091166101008201526101200190565b60405180910390a1505050505050505050565b610b69610c10565b6001805473ffffffffffffffffffffffffffffffffffffffff83167fffffffffffffffffffffffff00000000000000000000000000000000000000009091168117909155610bcb5f5473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270060405160405180910390a350565b5f5473ffffffffffffffffffffffffffffffffffffffff16331461034d576040517f118cdaa70000000000000000000000000000000000000000000000000000000081523360048201526024016103b6565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001690556103c881610dad565b5f610ca867ffffffffffffffff8316486112a6565b90503a811115610cb557503a5b919050565b5f83815b868682818110610cd057610cd0611335565b905060a002016060016020810190610ce89190611362565b6fffffffffffffffffffffffffffffffff168510610d0857600101610d80565b5f85888884818110610d1c57610d1c611335565b905060a002016060016020810190610d349190611362565b6fffffffffffffffffffffffffffffffff16039050610d6f888884818110610d5e57610d5e611335565b905060a00201602001358288610e21565b610d7990856112a6565b9350506001015b818110610cbe57612710610d9861ffff861685611289565b610da291906112e6565b979650505050505050565b5f805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b82820281838583041485151702610ec7577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8385098181108201900382848609835f038416828511610e7a5763ae47f7025f526004601cfd5b93849004938382119092035f839003839004600101029203041760026003830281188084028203028084028203028084028203028084028203028084028203028084029091030202610ecb565b8190045b9392505050565b5f6101208201905063ffffffff835116825263ffffffff60208401511660208301526040830151610f0b604084018263ffffffff169052565b506060830151610f23606084018263ffffffff169052565b506080830151610f3f608084018267ffffffffffffffff169052565b5060a0830151610f5560a084018261ffff169052565b5060c0830151610f6b60c084018261ffff169052565b5060e0830151610f8f60e08401826fffffffffffffffffffffffffffffffff169052565b50610100830151610fb56101008401826fffffffffffffffffffffffffffffffff169052565b5092915050565b80358015158114610cb5575f80fd5b803560078110610cb5575f80fd5b8035600b8110610cb5575f80fd5b5f8083601f840112610ff7575f80fd5b50813567ffffffffffffffff81111561100e575f80fd5b602083019150836020828501011115611025575f80fd5b9250929050565b5f805f805f805f805f8060e08b8d031215611045575f80fd5b8a3567ffffffffffffffff81111561105b575f80fd5b8b01601f81018d1361106b575f80fd5b803567ffffffffffffffff811115611081575f80fd5b8d602060a083028401011115611095575f80fd5b60209182019b5099508b013597506110af60408c01610fbc565b96506110bd60608c01610fcb565b95506110cb60808c01610fd9565b945060a08b013567ffffffffffffffff8111156110e6575f80fd5b6110f28d828e01610fe7565b90955093505060c08b013567ffffffffffffffff811115611111575f80fd5b61111d8d828e01610fe7565b915080935050809150509295989b9194979a5092959850565b803563ffffffff81168114610cb5575f80fd5b803561ffff81168114610cb5575f80fd5b80356fffffffffffffffffffffffffffffffff81168114610cb5575f80fd5b5f805f805f805f805f6101208a8c031215611192575f80fd5b61119b8a611136565b98506111a960208b01611136565b97506111b760408b01611136565b96506111c560608b01611136565b955060808a013567ffffffffffffffff811681146111e1575f80fd5b94506111ef60a08b01611149565b93506111fd60c08b01611149565b925061120b60e08b0161115a565b915061121a6101008b0161115a565b90509295985092959850929598565b5f60208284031215611239575f80fd5b813573ffffffffffffffffffffffffffffffffffffffff81168114610ecb575f80fd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b80820281158282048414176112a0576112a061125c565b92915050565b808201808211156112a0576112a061125c565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b5f82611319577f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b500490565b5f6020828403121561132e575f80fd5b5051919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b5f60208284031215611372575f80fd5b610ecb8261115a56fea2646970667358221220aa8f491aa51ca931bda614c3b4dfc4b4341c8f69f4a34ac67d79fc6adc57eeb464736f6c634300081a0033
Verified Source Code Full Match
Compiler: v0.8.26+commit.8a97fa7a
EVM: cancun
Optimization: Yes (20000 runs)
LiquidationRewardsManager.sol 213 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.26;
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { Ownable2Step } from "@openzeppelin/contracts/access/Ownable2Step.sol";
import { FixedPointMathLib } from "solady/src/utils/FixedPointMathLib.sol";
import { IWstETH } from "../interfaces/IWstETH.sol";
import { IBaseLiquidationRewardsManager } from
"../interfaces/LiquidationRewardsManager/IBaseLiquidationRewardsManager.sol";
import { ILiquidationRewardsManager } from "../interfaces/LiquidationRewardsManager/ILiquidationRewardsManager.sol";
import { IUsdnProtocolTypes as Types } from "../interfaces/UsdnProtocol/IUsdnProtocolTypes.sol";
/**
* @title Liquidation Rewards Manager
* @notice This contract calculates rewards for liquidators within the USDN protocol.
* @dev Rewards are computed based on gas costs, position size, and other parameters.
*/
contract LiquidationRewardsManager is ILiquidationRewardsManager, Ownable2Step {
/* -------------------------------------------------------------------------- */
/* Constants */
/* -------------------------------------------------------------------------- */
/// @inheritdoc ILiquidationRewardsManager
uint32 public constant BPS_DIVISOR = 10_000;
/// @inheritdoc ILiquidationRewardsManager
uint256 public constant BASE_GAS_COST = 21_000;
/// @inheritdoc ILiquidationRewardsManager
uint256 public constant MAX_GAS_USED_PER_TICK = 500_000;
/// @inheritdoc ILiquidationRewardsManager
uint256 public constant MAX_OTHER_GAS_USED = 1_000_000;
/// @inheritdoc ILiquidationRewardsManager
uint256 public constant MAX_REBASE_GAS_USED = 200_000;
/// @inheritdoc ILiquidationRewardsManager
uint256 public constant MAX_REBALANCER_GAS_USED = 300_000;
/* -------------------------------------------------------------------------- */
/* Storage Variables */
/* -------------------------------------------------------------------------- */
/// @notice The address of the wrapped stETH (wstETH) token contract.
IWstETH private immutable _wstEth;
/**
* @notice Holds the parameters used for rewards calculation.
* @dev Parameters should be updated to reflect changes in gas costs or protocol adjustments.
*/
RewardsParameters private _rewardsParameters;
/// @param wstETH The address of the wstETH token.
constructor(IWstETH wstETH) Ownable(msg.sender) {
_wstEth = wstETH;
_rewardsParameters = RewardsParameters({
gasUsedPerTick: 44_666,
otherGasUsed: 245_021,
rebaseGasUsed: 6955,
rebalancerGasUsed: 252_471,
baseFeeOffset: 2 gwei,
gasMultiplierBps: 10_500, // 1.05
positionBonusMultiplierBps: 200, // 0.02
fixedReward: 0.001 ether,
maxReward: 0.5 ether
});
}
/// @inheritdoc IBaseLiquidationRewardsManager
function getLiquidationRewards(
Types.LiqTickInfo[] calldata liquidatedTicks,
uint256 currentPrice,
bool rebased,
Types.RebalancerAction rebalancerAction,
Types.ProtocolAction,
bytes calldata,
bytes calldata
) external view returns (uint256 wstETHRewards_) {
if (liquidatedTicks.length == 0) {
return 0;
}
RewardsParameters memory rewardsParameters = _rewardsParameters;
// calculate the amount of gas spent during the liquidation
uint256 gasUsed = rewardsParameters.otherGasUsed + BASE_GAS_COST
+ uint256(rewardsParameters.gasUsedPerTick) * liquidatedTicks.length;
if (rebased) {
gasUsed += rewardsParameters.rebaseGasUsed;
}
if (uint8(rebalancerAction) > uint8(Types.RebalancerAction.NoCloseNoOpen)) {
gasUsed += rewardsParameters.rebalancerGasUsed;
}
uint256 totalRewardETH = rewardsParameters.fixedReward
+ _calcGasPrice(rewardsParameters.baseFeeOffset) * gasUsed * rewardsParameters.gasMultiplierBps / BPS_DIVISOR;
uint256 wstEthBonus =
_calcPositionSizeBonus(liquidatedTicks, currentPrice, rewardsParameters.positionBonusMultiplierBps);
totalRewardETH += _wstEth.getStETHByWstETH(wstEthBonus);
if (totalRewardETH > rewardsParameters.maxReward) {
totalRewardETH = rewardsParameters.maxReward;
}
// convert to wstETH
wstETHRewards_ = _wstEth.getWstETHByStETH(totalRewardETH);
}
/// @inheritdoc ILiquidationRewardsManager
function getRewardsParameters() external view returns (RewardsParameters memory) {
return _rewardsParameters;
}
/// @inheritdoc ILiquidationRewardsManager
function setRewardsParameters(
uint32 gasUsedPerTick,
uint32 otherGasUsed,
uint32 rebaseGasUsed,
uint32 rebalancerGasUsed,
uint64 baseFeeOffset,
uint16 gasMultiplierBps,
uint16 positionBonusMultiplierBps,
uint128 fixedReward,
uint128 maxReward
) external onlyOwner {
if (gasUsedPerTick > MAX_GAS_USED_PER_TICK) {
revert LiquidationRewardsManagerGasUsedPerTickTooHigh(gasUsedPerTick);
} else if (otherGasUsed > MAX_OTHER_GAS_USED) {
revert LiquidationRewardsManagerOtherGasUsedTooHigh(otherGasUsed);
} else if (rebaseGasUsed > MAX_REBASE_GAS_USED) {
revert LiquidationRewardsManagerRebaseGasUsedTooHigh(rebaseGasUsed);
} else if (rebalancerGasUsed > MAX_REBALANCER_GAS_USED) {
revert LiquidationRewardsManagerRebalancerGasUsedTooHigh(rebalancerGasUsed);
}
_rewardsParameters = RewardsParameters({
gasUsedPerTick: gasUsedPerTick,
otherGasUsed: otherGasUsed,
rebaseGasUsed: rebaseGasUsed,
rebalancerGasUsed: rebalancerGasUsed,
baseFeeOffset: baseFeeOffset,
gasMultiplierBps: gasMultiplierBps,
positionBonusMultiplierBps: positionBonusMultiplierBps,
fixedReward: fixedReward,
maxReward: maxReward
});
emit RewardsParametersUpdated(
gasUsedPerTick,
otherGasUsed,
rebaseGasUsed,
rebalancerGasUsed,
baseFeeOffset,
gasMultiplierBps,
positionBonusMultiplierBps,
fixedReward,
maxReward
);
}
/**
* @notice Calculates the gas price used for rewards calculations.
* @param baseFeeOffset An offset added to the block's base gas fee.
* @return gasPrice_ The gas price used for reward calculation.
*/
function _calcGasPrice(uint64 baseFeeOffset) internal view returns (uint256 gasPrice_) {
gasPrice_ = block.basefee + baseFeeOffset;
if (gasPrice_ > tx.gasprice) {
gasPrice_ = tx.gasprice;
}
}
/**
* @notice Computes the size and price-dependent bonus given for liquidating the ticks.
* @param liquidatedTicks Information about the liquidated ticks.
* @param currentPrice The current asset price.
* @param multiplier The bonus multiplier (in BPS).
* @return bonus_ The calculated bonus (in wstETH).
*/
function _calcPositionSizeBonus(
Types.LiqTickInfo[] calldata liquidatedTicks,
uint256 currentPrice,
uint16 multiplier
) internal pure returns (uint256 bonus_) {
uint256 length = liquidatedTicks.length;
uint256 i;
do {
if (currentPrice >= liquidatedTicks[i].tickPrice) {
// the currentPrice should never exceed the tick price, as a tick cannot be liquidated when the current
// price is greater than the tick price
// if this condition occurs, the bonus is clamped to 0
// additionally, when the `currentPrice` equals the tick price, the bonus is 0 by definition of the
// formula, so the calculation can be skipped
unchecked {
i++;
}
continue;
}
uint256 priceDiff;
unchecked {
priceDiff = liquidatedTicks[i].tickPrice - currentPrice;
}
bonus_ += FixedPointMathLib.fullMulDiv(liquidatedTicks[i].totalExpo, priceDiff, currentPrice);
unchecked {
i++;
}
} while (i < length);
bonus_ = bonus_ * multiplier / BPS_DIVISOR;
}
}
Ownable.sol 100 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
pragma solidity ^0.8.20;
import {Context} from "../utils/Context.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* The initial owner is set to the address provided by the deployer. This can
* later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract Ownable is Context {
address private _owner;
/**
* @dev The caller account is not authorized to perform an operation.
*/
error OwnableUnauthorizedAccount(address account);
/**
* @dev The owner is not a valid owner account. (eg. `address(0)`)
*/
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the address provided by the deployer as the initial owner.
*/
constructor(address initialOwner) {
if (initialOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
Ownable2Step.sol 67 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (access/Ownable2Step.sol)
pragma solidity ^0.8.20;
import {Ownable} from "./Ownable.sol";
/**
* @dev Contract module which provides access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* This extension of the {Ownable} contract includes a two-step mechanism to transfer
* ownership, where the new owner must call {acceptOwnership} in order to replace the
* old one. This can help prevent common mistakes, such as transfers of ownership to
* incorrect accounts, or to contracts that are unable to interact with the
* permission system.
*
* The initial owner is specified at deployment time in the constructor for `Ownable`. This
* can later be changed with {transferOwnership} and {acceptOwnership}.
*
* This module is used through inheritance. It will make available all functions
* from parent (Ownable).
*/
abstract contract Ownable2Step is Ownable {
address private _pendingOwner;
event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner);
/**
* @dev Returns the address of the pending owner.
*/
function pendingOwner() public view virtual returns (address) {
return _pendingOwner;
}
/**
* @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.
* Can only be called by the current owner.
*
* Setting `newOwner` to the zero address is allowed; this can be used to cancel an initiated ownership transfer.
*/
function transferOwnership(address newOwner) public virtual override onlyOwner {
_pendingOwner = newOwner;
emit OwnershipTransferStarted(owner(), newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual override {
delete _pendingOwner;
super._transferOwnership(newOwner);
}
/**
* @dev The new owner accepts the ownership transfer.
*/
function acceptOwnership() public virtual {
address sender = _msgSender();
if (pendingOwner() != sender) {
revert OwnableUnauthorizedAccount(sender);
}
_transferOwnership(sender);
}
}
FixedPointMathLib.sol 1161 lines
// 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 mul(y, gt(x, div(not(0), 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 {
// Equivalent to `require(y == 0 || x <= type(uint256).max / y)`.
if mul(y, gt(x, div(not(0), y))) {
mstore(0x00, 0xbac65e5b) // `MulWadFailed()`.
revert(0x1c, 0x04)
}
z := add(iszero(iszero(mod(mul(x, y), WAD))), div(mul(x, y), 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 && (WAD == 0 || x <= type(uint256).max / WAD))`.
if iszero(mul(y, iszero(mul(WAD, gt(x, 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(and(iszero(iszero(y)), eq(sdiv(z, WAD), x))) {
mstore(0x00, 0x5c43740d) // `SDivWadFailed()`.
revert(0x1c, 0x04)
}
z := sdiv(mul(x, WAD), 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 && (WAD == 0 || x <= type(uint256).max / WAD))`.
if iszero(mul(y, iszero(mul(WAD, gt(x, 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(WAD);
int256 p = 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 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 result) {
/// @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 `result` as `p0` to save gas.
result := mul(x, y) // Lower 256 bits of `x * y`.
for {} 1 {} {
// If overflows.
if iszero(mul(or(iszero(x), eq(div(result, x), y)), d)) {
let mm := mulmod(x, y, not(0))
let p1 := sub(mm, add(result, lt(mm, result))) // 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 the result 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
result :=
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, result)), add(div(sub(0, t), t), 1)),
div(sub(result, r), t)
),
mul(sub(2, mul(d, inv)), inv) // inverse mod 2**256
)
break
}
result := div(result, 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 result)
{
/// @solidity memory-safe-assembly
assembly {
result := mul(x, y)
let mm := mulmod(x, y, not(0))
let p1 := sub(mm, add(result, lt(mm, result)))
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)))
result :=
mul(
or(mul(sub(p1, gt(r, result)), add(div(sub(0, t), t), 1)), div(sub(result, 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 result) {
result = fullMulDiv(x, y, d);
/// @solidity memory-safe-assembly
assembly {
if mulmod(x, y, d) {
result := add(result, 1)
if iszero(result) {
mstore(0x00, 0xae47f702) // `FullMulDivFailed()`.
revert(0x1c, 0x04)
}
}
}
}
/// @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 `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)`.
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 `condition ? x : y`, without branching.
function ternary(bool condition, uint256 x, uint256 y) internal pure returns (uint256 result) {
/// @solidity memory-safe-assembly
assembly {
result := xor(x, mul(xor(x, y), iszero(condition)))
}
}
/// @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
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))))
z := div(shl(div(r, 3), shl(lt(0xf, shr(r, x)), 0xf)), xor(7, mod(r, 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)
z := div(add(add(div(x, mul(z, z)), z), z), 3)
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)))
}
}
/// @dev Returns the cube root of `x`, denominated in `WAD`, rounded down.
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;
x = fullMulDivUnchecked(x, 10 ** 36, z * z);
}
/// @solidity memory-safe-assembly
assembly {
z := sub(z, lt(x, z))
}
}
/// @dev Returns the factorial of `x`.
function factorial(uint256 x) internal pure returns (uint256 result) {
/// @solidity memory-safe-assembly
assembly {
result := 1
if iszero(lt(x, 58)) {
mstore(0x00, 0xaba0f2a2) // `FactorialOverflow()`.
revert(0x1c, 0x04)
}
for {} x { x := sub(x, 1) } { result := mul(result, 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) {
/// @solidity memory-safe-assembly
assembly {
z := xor(sar(255, x), add(sar(255, x), x))
}
}
/// @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 := xor(mul(xor(sub(y, x), sub(x, y)), gt(x, y)), sub(y, x))
}
}
/// @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 := xor(mul(xor(sub(y, x), sub(x, y)), sgt(x, y)), sub(y, x))
}
}
/// @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 = ~t;
begin = ~begin;
end = ~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 = int256(~uint256(t));
begin = int256(~uint256(begin));
end = int256(~uint256(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) - uint256(a),
uint256(t) - uint256(begin), uint256(end) - uint256(begin)));
return int256(uint256(a) - fullMulDiv(uint256(a) - uint256(b),
uint256(t) - uint256(begin), uint256(end) - uint256(begin)));
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* 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)
}
}
}
IWstETH.sol 61 lines
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import { IERC20Permit } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol";
interface IWstETH is IERC20Metadata, IERC20Permit {
/**
* @notice Exchanges stETH to wstETH
* @param _stETHAmount The amount of stETH to wrap in exchange for wstETH
* @dev Requirements:
* - `_stETHAmount` must be non-zero
* - msg.sender must approve at least `_stETHAmount` stETH to this contract
* - msg.sender must have at least `_stETHAmount` of stETH
* User should first approve `_stETHAmount` to the WstETH contract
* @return Amount of wstETH user receives after wrap
*/
function wrap(uint256 _stETHAmount) external returns (uint256);
/**
* @notice Exchanges wstETH to stETH
* @param _wstETHAmount The amount of wstETH to unwrap in exchange for stETH
* @dev Requirements:
* - `_wstETHAmount` must be non-zero
* - msg.sender must have at least `_wstETHAmount` wstETH
* @return The amount of stETH user receives after unwrap
*/
function unwrap(uint256 _wstETHAmount) external returns (uint256);
/**
* @notice Get the amount of wstETH for a given amount of stETH
* @param _stETHAmount The amount of stETH
* @return The amount of wstETH for a given stETH amount
*/
function getWstETHByStETH(uint256 _stETHAmount) external view returns (uint256);
/**
* @notice Get the amount of stETH for a given amount of wstETH
* @param _wstETHAmount The amount of wstETH
* @return The amount of stETH for a given wstETH amount
*/
function getStETHByWstETH(uint256 _wstETHAmount) external view returns (uint256);
/**
* @notice Get the amount of stETH for a one wstETH
* @return The amount of stETH for 1 wstETH
*/
function stEthPerToken() external view returns (uint256);
/**
* @notice Get the amount of wstETH for a one stETH
* @return The amount of wstETH for a 1 stETH
*/
function tokensPerStEth() external view returns (uint256);
/**
* @notice Get the address of stETH
* @return The address of stETH
*/
function stETH() external view returns (address);
}
IBaseLiquidationRewardsManager.sol 33 lines
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
import { IUsdnProtocolTypes as Types } from "../UsdnProtocol/IUsdnProtocolTypes.sol";
/**
* @title IBaseLiquidationRewardsManager
* @notice This interface exposes the only function used by the UsdnProtocol.
* @dev Future implementations of the rewards manager must implement this interface without modifications.
*/
interface IBaseLiquidationRewardsManager {
/**
* @notice Computes the amount of assets to reward a liquidator.
* @param liquidatedTicks Information about the liquidated ticks.
* @param currentPrice The current price of the asset.
* @param rebased Indicates whether a USDN rebase was performed.
* @param rebalancerAction The action performed by the {UsdnProtocolLongLibrary._triggerRebalancer} function.
* @param action The type of protocol action that triggered the liquidation.
* @param rebaseCallbackResult The result of the rebase callback, if any.
* @param priceData The oracle price data, if any. This can be used to differentiate rewards based on the oracle
* used to provide the liquidation price.
* @return assetRewards_ The amount of asset tokens to reward the liquidator.
*/
function getLiquidationRewards(
Types.LiqTickInfo[] calldata liquidatedTicks,
uint256 currentPrice,
bool rebased,
Types.RebalancerAction rebalancerAction,
Types.ProtocolAction action,
bytes calldata rebaseCallbackResult,
bytes calldata priceData
) external view returns (uint256 assetRewards_);
}
ILiquidationRewardsManager.sol 78 lines
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
import { IBaseLiquidationRewardsManager } from "./IBaseLiquidationRewardsManager.sol";
import { ILiquidationRewardsManagerErrorsEventsTypes } from "./ILiquidationRewardsManagerErrorsEventsTypes.sol";
/**
* @title ILiquidationRewardsManager
* @notice Interface for managing liquidation rewards within the protocol.
*/
interface ILiquidationRewardsManager is IBaseLiquidationRewardsManager, ILiquidationRewardsManagerErrorsEventsTypes {
/**
* @notice Gets the denominator used for the reward multipliers.
* @return The BPS divisor.
*/
function BPS_DIVISOR() external pure returns (uint32);
/**
* @notice Gets the fixed gas amount used as a base for transaction cost computations.
* @dev Stored as a uint256 to prevent overflow during gas usage computations.
* @return The base gas cost.
*/
function BASE_GAS_COST() external pure returns (uint256);
/**
* @notice Gets the maximum allowable gas usage per liquidated tick.
* @return The maximum gas used per tick.
*/
function MAX_GAS_USED_PER_TICK() external pure returns (uint256);
/**
* @notice Gets the maximum allowable gas usage for all other computations.
* @return The maximum gas used for additional computations.
*/
function MAX_OTHER_GAS_USED() external pure returns (uint256);
/**
* @notice Gets the maximum allowable gas usage for rebase operations.
* @return The maximum gas used for rebase operations.
*/
function MAX_REBASE_GAS_USED() external pure returns (uint256);
/**
* @notice Gets the maximum allowable gas usage for triggering the optional rebalancer.
* @return The maximum gas used for the optional rebalancer trigger.
*/
function MAX_REBALANCER_GAS_USED() external pure returns (uint256);
/**
* @notice Retrieves the current parameters used for reward calculations.
* @return rewardsParameters_ A struct containing the rewards parameters.
*/
function getRewardsParameters() external view returns (RewardsParameters memory);
/**
* @notice Updates the parameters used for calculating liquidation rewards.
* @param gasUsedPerTick The gas consumed per tick for liquidation.
* @param otherGasUsed The gas consumed for all additional computations.
* @param rebaseGasUsed The gas consumed for optional USDN rebase operation.
* @param rebalancerGasUsed The gas consumed for the optional rebalancer trigger.
* @param baseFeeOffset An offset added to the block's base gas fee.
* @param gasMultiplierBps The multiplier for the gas usage (in BPS).
* @param positionBonusMultiplierBps Multiplier for position size bonus (in BPS).
* @param fixedReward A fixed reward amount (in native currency, converted to wstETH).
* @param maxReward The maximum allowable reward amount (in native currency, converted to wstETH).
*/
function setRewardsParameters(
uint32 gasUsedPerTick,
uint32 otherGasUsed,
uint32 rebaseGasUsed,
uint32 rebalancerGasUsed,
uint64 baseFeeOffset,
uint16 gasMultiplierBps,
uint16 positionBonusMultiplierBps,
uint128 fixedReward,
uint128 maxReward
) external;
}
IUsdnProtocolTypes.sol 696 lines
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import { HugeUint } from "@smardex-solidity-libraries-1/HugeUint.sol";
import { LibBitmap } from "solady/src/utils/LibBitmap.sol";
import { DoubleEndedQueue } from "../../libraries/DoubleEndedQueue.sol";
import { IBaseLiquidationRewardsManager } from "../LiquidationRewardsManager/IBaseLiquidationRewardsManager.sol";
import { IBaseOracleMiddleware } from "../OracleMiddleware/IBaseOracleMiddleware.sol";
import { IBaseRebalancer } from "../Rebalancer/IBaseRebalancer.sol";
import { IUsdn } from "../Usdn/IUsdn.sol";
interface IUsdnProtocolTypes {
/**
* @notice All possible action types for the protocol.
* @dev This is used for pending actions and to interact with the oracle middleware.
* @param None No particular action.
* @param Initialize The contract is being initialized.
* @param InitiateDeposit Initiating a `deposit` action.
* @param ValidateDeposit Validating a `deposit` action.
* @param InitiateWithdrawal Initiating a `withdraw` action.
* @param ValidateWithdrawal Validating a `withdraw` action.
* @param InitiateOpenPosition Initiating an `open` position action.
* @param ValidateOpenPosition Validating an `open` position action.
* @param InitiateClosePosition Initiating a `close` position action.
* @param ValidateClosePosition Validating a `close` position action.
* @param Liquidation The price is requested for a liquidation action.
*/
enum ProtocolAction {
None,
Initialize,
InitiateDeposit,
ValidateDeposit,
InitiateWithdrawal,
ValidateWithdrawal,
InitiateOpenPosition,
ValidateOpenPosition,
InitiateClosePosition,
ValidateClosePosition,
Liquidation
}
/**
* @notice The outcome of the call targeting a long position.
* @param Processed The call did what it was supposed to do.
* An initiate close has been completed / a pending action was validated.
* @param Liquidated The position has been liquidated by this call.
* @param PendingLiquidations The call cannot be completed because of pending liquidations.
* Try calling the {IUsdnProtocolActions.liquidate} function with a fresh price to unblock the situation.
*/
enum LongActionOutcome {
Processed,
Liquidated,
PendingLiquidations
}
/**
* @notice Classifies how far in its logic the {UsdnProtocolLongLibrary._triggerRebalancer} function made it to.
* @dev Used to estimate the gas spent by the function call to more accurately calculate liquidation rewards.
* @param None The rebalancer is not set.
* @param NoImbalance The protocol imbalance is not reached.
* @param PendingLiquidation The rebalancer position should be liquidated.
* @param NoCloseNoOpen The action neither closes nor opens a position.
* @param Closed The action only closes a position.
* @param Opened The action only opens a position.
* @param ClosedOpened The action closes and opens a position.
*/
enum RebalancerAction {
None,
NoImbalance,
PendingLiquidation,
NoCloseNoOpen,
Closed,
Opened,
ClosedOpened
}
/**
* @notice Information about a long user position.
* @param validated Whether the position was validated.
* @param timestamp The timestamp of the position start.
* @param user The user's address.
* @param totalExpo The total exposure of the position (0 for vault deposits). The product of the initial
* collateral and the initial leverage.
* @param amount The amount of initial collateral in the position.
*/
struct Position {
bool validated; // 1 byte
uint40 timestamp; // 5 bytes. Max 1_099_511_627_775 (36812-02-20 01:36:15)
address user; // 20 bytes
uint128 totalExpo; // 16 bytes. Max 340_282_366_920_938_463_463.374_607_431_768_211_455 ether
uint128 amount; // 16 bytes
}
/**
* @notice A pending action in the queue.
* @param action The action type.
* @param timestamp The timestamp of the initiate action.
* @param var0 See {DepositPendingAction}, {WithdrawalPendingAction} and {LongPendingAction}.
* @param to The target of the action.
* @param validator The address that is supposed to validate the action.
* @param securityDepositValue The security deposit of the pending action.
* @param var1 See {DepositPendingAction}, {WithdrawalPendingAction} and {LongPendingAction}.
* @param var2 See {DepositPendingAction}, {WithdrawalPendingAction} and {LongPendingAction}.
* @param var3 See {DepositPendingAction}, {WithdrawalPendingAction} and {LongPendingAction}.
* @param var4 See {DepositPendingAction}, {WithdrawalPendingAction} and {LongPendingAction}.
* @param var5 See {DepositPendingAction}, {WithdrawalPendingAction} and {LongPendingAction}.
* @param var6 See {DepositPendingAction}, {WithdrawalPendingAction} and {LongPendingAction}.
* @param var7 See {DepositPendingAction}, {WithdrawalPendingAction} and {LongPendingAction}.
*/
struct PendingAction {
ProtocolAction action; // 1 byte
uint40 timestamp; // 5 bytes
uint24 var0; // 3 bytes
address to; // 20 bytes
address validator; // 20 bytes
uint64 securityDepositValue; // 8 bytes
int24 var1; // 3 bytes
uint128 var2; // 16 bytes
uint128 var3; // 16 bytes
uint256 var4; // 32 bytes
uint256 var5; // 32 bytes
uint256 var6; // 32 bytes
uint256 var7; // 32 bytes
}
/**
* @notice A pending action in the queue for a vault deposit.
* @param action The action type.
* @param timestamp The timestamp of the initiate action.
* @param feeBps Fee for the deposit, in BPS.
* @param to The recipient of the funds.
* @param validator The address that is supposed to validate the action.
* @param securityDepositValue The security deposit of the pending action.
* @param _unused Unused field to align the struct to `PendingAction`.
* @param amount The amount of assets of the pending deposit.
* @param assetPrice The price of the asset at the time of the last update.
* @param totalExpo The total exposure at the time of the last update.
* @param balanceVault The balance of the vault at the time of the last update.
* @param balanceLong The balance of the long position at the time of the last update.
* @param usdnTotalShares The total supply of USDN shares at the time of the action.
*/
struct DepositPendingAction {
ProtocolAction action; // 1 byte
uint40 timestamp; // 5 bytes
uint24 feeBps; // 3 bytes
address to; // 20 bytes
address validator; // 20 bytes
uint64 securityDepositValue; // 8 bytes
uint24 _unused; // 3 bytes
uint128 amount; // 16 bytes
uint128 assetPrice; // 16 bytes
uint256 totalExpo; // 32 bytes
uint256 balanceVault; // 32 bytes
uint256 balanceLong; // 32 bytes
uint256 usdnTotalShares; // 32 bytes
}
/**
* @notice A pending action in the queue for a vault withdrawal.
* @param action The action type.
* @param timestamp The timestamp of the initiate action.
* @param feeBps Fee for the withdrawal, in BPS.
* @param to The recipient of the funds.
* @param validator The address that is supposed to validate the action.
* @param securityDepositValue The security deposit of the pending action.
* @param sharesLSB 3 least significant bytes of the withdrawal shares amount (uint152).
* @param sharesMSB 16 most significant bytes of the withdrawal shares amount (uint152).
* @param assetPrice The price of the asset at the time of the last update.
* @param totalExpo The total exposure at the time of the last update.
* @param balanceVault The balance of the vault at the time of the last update.
* @param balanceLong The balance of the long position at the time of the last update.
* @param usdnTotalShares The total shares supply of USDN at the time of the action.
*/
struct WithdrawalPendingAction {
ProtocolAction action; // 1 byte
uint40 timestamp; // 5 bytes
uint24 feeBps; // 3 bytes
address to; // 20 bytes
address validator; // 20 bytes
uint64 securityDepositValue; // 8 bytes
uint24 sharesLSB; // 3 bytes
uint128 sharesMSB; // 16 bytes
uint128 assetPrice; // 16 bytes
uint256 totalExpo; // 32 bytes
uint256 balanceVault; // 32 bytes
uint256 balanceLong; // 32 bytes
uint256 usdnTotalShares; // 32 bytes
}
/**
* @notice A pending action in the queue for a long position.
* @param action The action type.
* @param timestamp The timestamp of the initiate action.
* @param closeLiqPenalty The liquidation penalty of the tick (only used when closing a position).
* @param to The recipient of the position.
* @param validator The address that is supposed to validate the action.
* @param securityDepositValue The security deposit of the pending action.
* @param tick The tick of the position.
* @param closeAmount The portion of the initial position amount to close (only used when closing a position).
* @param closePosTotalExpo The total expo of the position (only used when closing a position).
* @param tickVersion The version of the tick.
* @param index The index of the position in the tick list.
* @param liqMultiplier A fixed precision representation of the liquidation multiplier (with
* `LIQUIDATION_MULTIPLIER_DECIMALS` decimals) used to calculate the effective price for a given tick number.
* @param closeBoundedPositionValue The amount that was removed from the long balance on
* {IUsdnProtocolActions.initiateClosePosition} (only used when closing a position).
*/
struct LongPendingAction {
ProtocolAction action; // 1 byte
uint40 timestamp; // 5 bytes
uint24 closeLiqPenalty; // 3 bytes
address to; // 20 bytes
address validator; // 20 bytes
uint64 securityDepositValue; // 8 bytes
int24 tick; // 3 bytes
uint128 closeAmount; // 16 bytes
uint128 closePosTotalExpo; // 16 bytes
uint256 tickVersion; // 32 bytes
uint256 index; // 32 bytes
uint256 liqMultiplier; // 32 bytes
uint256 closeBoundedPositionValue; // 32 bytes
}
/**
* @notice The data allowing to validate an actionable pending action.
* @param priceData An array of bytes, each representing the data to be forwarded to the oracle middleware to
* validate a pending action in the queue.
* @param rawIndices An array of raw indices in the pending actions queue, in the same order as the corresponding
* priceData.
*/
struct PreviousActionsData {
bytes[] priceData;
uint128[] rawIndices;
}
/**
* @notice Information of a liquidated tick.
* @param totalPositions The total number of positions in the tick.
* @param totalExpo The total expo of the tick.
* @param remainingCollateral The remaining collateral after liquidation.
* @param tickPrice The corresponding price.
* @param priceWithoutPenalty The price without the liquidation penalty.
*/
struct LiqTickInfo {
uint256 totalPositions;
uint256 totalExpo;
int256 remainingCollateral;
uint128 tickPrice;
uint128 priceWithoutPenalty;
}
/**
* @notice The effects of executed liquidations on the protocol.
* @param liquidatedPositions The total number of liquidated positions.
* @param remainingCollateral The remaining collateral after liquidation.
* @param newLongBalance The new balance of the long side.
* @param newVaultBalance The new balance of the vault side.
* @param isLiquidationPending Whether some ticks are still populated above the current price (left to liquidate).
* @param liquidatedTicks Information about the liquidated ticks.
*/
struct LiquidationsEffects {
uint256 liquidatedPositions;
int256 remainingCollateral;
uint256 newLongBalance;
uint256 newVaultBalance;
bool isLiquidationPending;
LiqTickInfo[] liquidatedTicks;
}
/**
* @notice Accumulator for tick data.
* @param totalExpo The sum of the total expo of each position in the tick.
* @param totalPos The number of positions in the tick.
* @param liquidationPenalty The liquidation penalty for the positions in the tick.
* @dev Since the liquidation penalty is a parameter that can be updated, we need to ensure that positions that get
* created with a given penalty, use this penalty throughout their lifecycle. As such, once a tick gets populated by
* a first position, it gets assigned the current liquidation penalty parameter value and can't use another value
* until it gets liquidated or all positions exit the tick.
*/
struct TickData {
uint256 totalExpo;
uint248 totalPos;
uint24 liquidationPenalty;
}
/**
* @notice The unique identifier for a long position.
* @param tick The tick of the position.
* @param tickVersion The version of the tick.
* @param index The index of the position in the tick list.
*/
struct PositionId {
int24 tick;
uint256 tickVersion;
uint256 index;
}
/**
* @notice Parameters for the internal {UsdnProtocolActionsLongLibrary._initiateOpenPosition} function.
* @param user The address of the user initiating the open position.
* @param to The address that will be the owner of the position.
* @param validator The address that is supposed to validate the action.
* @param amount The amount of assets to deposit.
* @param desiredLiqPrice The desired liquidation price, including the liquidation penalty.
* @param userMaxPrice The maximum price at which the position can be opened. The userMaxPrice is compared with the
* price after confidence interval, penalty, etc...
* @param userMaxLeverage The maximum leverage for the newly created position.
* @param deadline The deadline of the open position to be initiated.
* @param securityDepositValue The value of the security deposit for the newly created pending action.
* @param currentPriceData The current price data (used to calculate the temporary leverage and entry price,
* pending validation).
*/
struct InitiateOpenPositionParams {
address user;
address to;
address validator;
uint128 amount;
uint128 desiredLiqPrice;
uint128 userMaxPrice;
uint256 userMaxLeverage;
uint256 deadline;
uint64 securityDepositValue;
}
/**
* @notice Parameters for the internal {UsdnProtocolLongLibrary._prepareInitiateOpenPosition} function.
* @param validator The address that is supposed to validate the action.
* @param amount The amount of assets to deposit.
* @param desiredLiqPrice The desired liquidation price, including the liquidation penalty.
* @param userMaxPrice The maximum price at which the position can be opened. The userMaxPrice is compared with the
* price after confidence interval, penalty, etc...
* @param userMaxLeverage The maximum leverage for the newly created position.
* @param currentPriceData The current price data.
*/
struct PrepareInitiateOpenPositionParams {
address validator;
uint128 amount;
uint128 desiredLiqPrice;
uint256 userMaxPrice;
uint256 userMaxLeverage;
bytes currentPriceData;
}
/**
* @notice Parameters for the internal {UsdnProtocolActionsUtilsLibrary._prepareClosePositionData} function.
* @param to The recipient of the funds.
* @param validator The address that is supposed to validate the action.
* @param posId The unique identifier of the position.
* @param amountToClose The amount of collateral to remove from the position's amount.
* @param userMinPrice The minimum price at which the position can be closed.
* @param deadline The deadline until the position can be closed.
* @param currentPriceData The current price data.
* @param delegationSignature An EIP712 signature that proves the caller is authorized by the owner of the position
* to close it on their behalf.
* @param domainSeparatorV4 The domain separator v4.
*/
struct PrepareInitiateClosePositionParams {
address to;
address validator;
PositionId posId;
uint128 amountToClose;
uint256 userMinPrice;
uint256 deadline;
bytes currentPriceData;
bytes delegationSignature;
bytes32 domainSeparatorV4;
}
/**
* @notice Parameters for the internal {UsdnProtocolActionsLongLibrary._initiateClosePosition} function.
* @param to The recipient of the funds.
* @param validator The address that is supposed to validate the action.
* @param posId The unique identifier of the position.
* @param amountToClose The amount to close.
* @param userMinPrice The minimum price at which the position can be closed.
* @param deadline The deadline of the close position to be initiated.
* @param securityDepositValue The value of the security deposit for the newly created pending action.
* @param domainSeparatorV4 The domain separator v4 for EIP712 signature.
*/
struct InitiateClosePositionParams {
address to;
address payable validator;
uint256 deadline;
PositionId posId;
uint128 amountToClose;
uint256 userMinPrice;
uint64 securityDepositValue;
bytes32 domainSeparatorV4;
}
/**
* @dev Structure to hold the transient data during {UsdnProtocolActionsLongLibrary._initiateClosePosition}
* @param pos The position to close.
* @param liquidationPenalty The liquidation penalty.
* @param totalExpoToClose The total expo to close.
* @param lastPrice The price after the last balances update.
* @param tempPositionValue The bounded value of the position that was removed from the long balance.
* @param longTradingExpo The long trading expo.
* @param liqMulAcc The liquidation multiplier accumulator.
* @param isLiquidationPending Whether some ticks are still populated above the current price (left to liquidate).
*/
struct ClosePositionData {
Position pos;
uint24 liquidationPenalty;
uint128 totalExpoToClose;
uint128 lastPrice;
uint256 tempPositionValue;
uint256 longTradingExpo;
HugeUint.Uint512 liqMulAcc;
bool isLiquidationPending;
}
/**
* @dev Structure to hold the transient data during {UsdnProtocolActionsLongLibrary._validateOpenPosition}.
* @param action The long pending action.
* @param startPrice The new entry price of the position.
* @param lastPrice The price of the last balances update.
* @param tickHash The tick hash.
* @param pos The position object.
* @param liqPriceWithoutPenaltyNorFunding The liquidation price without penalty nor funding used to calculate the
* user leverage and the new total expo.
* @param liqPriceWithoutPenalty The new liquidation price without penalty.
* @param leverage The new leverage.
* @param oldPosValue The value of the position according to the old entry price and the _lastPrice.
* @param liquidationPenalty The liquidation penalty for the position's tick.
* @param isLiquidationPending Whether some ticks are still populated above the current price (left to liquidate).
*/
struct ValidateOpenPositionData {
LongPendingAction action;
uint128 startPrice;
uint128 lastPrice;
bytes32 tickHash;
Position pos;
uint128 liqPriceWithoutPenaltyNorFunding;
uint128 liqPriceWithoutPenalty;
uint256 leverage;
uint256 oldPosValue;
uint24 liquidationPenalty;
bool isLiquidationPending;
}
/**
* @dev Structure to hold the transient data during {UsdnProtocolActionsLongLibrary._initiateOpenPosition}.
* @param adjustedPrice The adjusted price with position fees applied.
* @param posId The unique identifier of the position.
* @param liquidationPenalty The liquidation penalty.
* @param positionTotalExpo The total expo of the position. The product of the initial collateral and the initial
* leverage.
* @param positionValue The value of the position, taking into account the position fee.
* @param liqMultiplier The liquidation multiplier represented with fixed precision.
* @param isLiquidationPending Whether some ticks are still populated above the current price (left to liquidate).
*/
struct InitiateOpenPositionData {
uint128 adjustedPrice;
PositionId posId;
uint24 liquidationPenalty;
uint128 positionTotalExpo;
uint256 positionValue;
uint256 liqMultiplier;
bool isLiquidationPending;
}
/**
* @notice Structure to hold the state of the protocol.
* @param totalExpo The long total expo.
* @param tradingExpo The long trading expo.
* @param longBalance The long balance.
* @param vaultBalance The vault balance.
* @param liqMultiplierAccumulator The liquidation multiplier accumulator.
*/
struct CachedProtocolState {
uint256 totalExpo;
uint256 tradingExpo;
uint256 longBalance;
uint256 vaultBalance;
HugeUint.Uint512 liqMultiplierAccumulator;
}
/**
* @notice Structure to hold transient data during the {UsdnProtocolActionsLongLibrary._calcRebalancerPositionTick}
* function.
* @param protocolMaxLeverage The protocol maximum leverage.
* @param longImbalanceTargetBps The long imbalance target in basis points.
* @param tradingExpoToFill The trading expo to fill.
* @param highestUsableTradingExpo The highest usable trading expo.
* @param currentLiqPenalty The current liquidation penalty.
* @param liqPriceWithoutPenalty The liquidation price without penalty.
*/
struct CalcRebalancerPositionTickData {
uint256 protocolMaxLeverage;
int256 longImbalanceTargetBps;
uint256 tradingExpoToFill;
uint256 highestUsableTradingExpo;
uint24 currentLiqPenalty;
uint128 liqPriceWithoutPenalty;
}
/**
* @notice Structure to hold the return values of the {UsdnProtocolActionsLongLibrary._calcRebalancerPositionTick}
* function.
* @param tick The tick of the rebalancer position, includes liquidation penalty.
* @param totalExpo The total expo of the rebalancer position.
* @param liquidationPenalty The liquidation penalty of the tick.
*/
struct RebalancerPositionData {
int24 tick;
uint128 totalExpo;
uint24 liquidationPenalty;
}
/**
* @notice Data structure for the {UsdnProtocolCoreLibrary._applyPnlAndFunding} function.
* @param tempLongBalance The new balance of the long side, could be negative (temporarily).
* @param tempVaultBalance The new balance of the vault side, could be negative (temporarily).
* @param lastPrice The last price.
*/
struct ApplyPnlAndFundingData {
int256 tempLongBalance;
int256 tempVaultBalance;
uint128 lastPrice;
}
/**
* @notice Data structure for tick to price conversion functions.
* @param tradingExpo The long side trading expo.
* @param accumulator The liquidation multiplier accumulator.
* @param tickSpacing The tick spacing.
*/
struct TickPriceConversionData {
uint256 tradingExpo;
HugeUint.Uint512 accumulator;
int24 tickSpacing;
}
/**
* @custom:storage-location erc7201:UsdnProtocol.storage.main.
* @notice Structure to hold the state of the protocol.
* @param _tickSpacing The liquidation tick spacing for storing long positions.
* A tick spacing of 1 is equivalent to a 0.01% increase in liquidation price between ticks. A tick spacing of
* 100 is equivalent to a ~1.005% increase in liquidation price between ticks.
* @param _asset The asset ERC20 contract.
* Assets with a blacklist are not supported because the protocol would be DoS if transfers revert.
* @param _assetDecimals The number of decimals used by the `_asset`.
* @param _priceFeedDecimals The price feed decimals (18).
* @param _usdn The USDN ERC20 contract.
* @param _sdex The SDEX ERC20 contract.
* @param _usdnMinDivisor The minimum divisor for USDN.
* @param _oracleMiddleware The oracle middleware contract.
* @param _liquidationRewardsManager The liquidation rewards manager contract.
* @param _rebalancer The rebalancer contract.
* @param _isRebalancer Whether an address is or has been a rebalancer.
* @param _minLeverage The minimum leverage for a position.
* @param _maxLeverage The maximum leverage for a position.
* @param _lowLatencyValidatorDeadline The deadline for a user to confirm their action with a low-latency oracle.
* After this deadline, any user can validate the action with the low-latency oracle until the
* OracleMiddleware's _lowLatencyDelay. This is an offset compared to the timestamp of the initiate action.
* @param _onChainValidatorDeadline The deadline for a user to confirm their action with an on-chain oracle.
* After this deadline, any user can validate the action with the on-chain oracle. This is an offset compared
* to the timestamp of the initiate action + the oracle middleware's _lowLatencyDelay.
* @param _safetyMarginBps Safety margin for the liquidation price of newly open positions, in basis points.
* @param _liquidationIteration The number of iterations to perform during the user's action (in tick).
* @param _protocolFeeBps The protocol fee in basis points.
* @param _rebalancerBonusBps Part of the remaining collateral that is given as a bonus to the Rebalancer upon
* liquidation of a tick, in basis points. The rest is sent to the Vault balance.
* @param _liquidationPenalty The liquidation penalty (in ticks).
* @param _EMAPeriod The moving average period of the funding rate.
* @param _fundingSF The scaling factor (SF) of the funding rate.
* @param _feeThreshold The threshold above which the fee will be sent.
* @param _openExpoImbalanceLimitBps The imbalance limit of the long expo for open actions (in basis points).
* As soon as the difference between the vault expo and the long expo exceeds this basis point limit in favor
* of long the open rebalancing mechanism is triggered, preventing the opening of a new long position.
* @param _withdrawalExpoImbalanceLimitBps The imbalance limit of the long expo for withdrawal actions (in basis
* points). As soon as the difference between vault expo and long expo exceeds this basis point limit in favor of
* long, the withdrawal rebalancing mechanism is triggered, preventing the withdrawal of the existing vault
* position.
* @param _depositExpoImbalanceLimitBps The imbalance limit of the vault expo for deposit actions (in basis points).
* As soon as the difference between the vault expo and the long expo exceeds this basis point limit in favor
* of the vault, the deposit vault rebalancing mechanism is triggered, preventing the opening of a new vault
* position.
* @param _closeExpoImbalanceLimitBps The imbalance limit of the vault expo for close actions (in basis points).
* As soon as the difference between the vault expo and the long expo exceeds this basis point limit in favor
* of the vault, the close rebalancing mechanism is triggered, preventing the close of an existing long position.
* @param _rebalancerCloseExpoImbalanceLimitBps The imbalance limit of the vault expo for close actions from the
* rebalancer (in basis points). As soon as the difference between the vault expo and the long expo exceeds this
* basis point limit in favor of the vault, the close rebalancing mechanism is triggered, preventing the close of an
* existing long position from the rebalancer contract.
* @param _longImbalanceTargetBps The target imbalance on the long side (in basis points)
* This value will be used to calculate how much of the missing trading expo the rebalancer position will try
* to compensate. A negative value means the rebalancer will compensate enough to go above the equilibrium. A
* positive value means the rebalancer will compensate but stay below the equilibrium.
* @param _positionFeeBps The position fee in basis points.
* @param _vaultFeeBps The fee for vault deposits and withdrawals, in basis points.
* @param _sdexRewardsRatioBps The ratio of SDEX rewards to send to the user (in basis points).
* @param _sdexBurnOnDepositRatio The ratio of USDN to SDEX tokens to burn on deposit.
* @param _feeCollector The fee collector's address.
* @param _securityDepositValue The deposit required for a new position.
* @param _targetUsdnPrice The nominal (target) price of USDN (with _priceFeedDecimals).
* @param _usdnRebaseThreshold The USDN price threshold to trigger a rebase (with _priceFeedDecimals).
* @param _minLongPosition The minimum long position size (with `_assetDecimals`).
* @param _lastFundingPerDay The funding rate calculated at the last update timestamp.
* @param _lastPrice The price of the asset during the last balances update (with price feed decimals).
* @param _lastUpdateTimestamp The timestamp of the last balances update.
* @param _pendingProtocolFee The pending protocol fee accumulator.
* @param _pendingActions The pending actions by the user (1 per user max).
* The value stored is an index into the `pendingActionsQueue` deque, shifted by one. A value of 0 means no
* pending action. Since the deque uses uint128 indices, the highest index will not overflow when adding one.
* @param _pendingActionsQueue The queue of pending actions.
* @param _balanceVault The balance of deposits (with `_assetDecimals`).
* @param _pendingBalanceVault The unreflected balance change due to pending vault actions (with `_assetDecimals`).
* @param _EMA The exponential moving average of the funding (0.0003 at initialization).
* @param _balanceLong The balance of long positions (with `_assetDecimals`).
* @param _totalExpo The total exposure of the long positions (with `_assetDecimals`).
* @param _liqMultiplierAccumulator The accumulator used to calculate the liquidation multiplier.
* This is the sum, for all ticks, of the total expo of positions inside the tick, multiplied by the
* unadjusted price of the tick which is `_tickData[tickHash].liquidationPenalty` below
* The unadjusted price is obtained with `TickMath.getPriceAtTick.
* @param _tickVersion The liquidation tick version.
* @param _longPositions The long positions per versioned tick (liquidation price).
* @param _tickData Accumulated data for a given tick and tick version.
* @param _highestPopulatedTick The highest tick with a position.
* @param _totalLongPositions Cache of the total long positions count.
* @param _tickBitmap The bitmap used to quickly find populated ticks.
* @param _protocolFallbackAddr The address of the fallback contract.
* @param _nonce The user EIP712 nonce.
*/
struct Storage {
// immutable
int24 _tickSpacing;
IERC20Metadata _asset;
uint8 _assetDecimals;
uint8 _priceFeedDecimals;
IUsdn _usdn;
IERC20Metadata _sdex;
uint256 _usdnMinDivisor;
// parameters
IBaseOracleMiddleware _oracleMiddleware;
IBaseLiquidationRewardsManager _liquidationRewardsManager;
IBaseRebalancer _rebalancer;
mapping(address => bool) _isRebalancer;
uint256 _minLeverage;
uint256 _maxLeverage;
uint128 _lowLatencyValidatorDeadline;
uint128 _onChainValidatorDeadline;
uint256 _safetyMarginBps;
uint16 _liquidationIteration;
uint16 _protocolFeeBps;
uint16 _rebalancerBonusBps;
uint24 _liquidationPenalty;
uint128 _EMAPeriod;
uint256 _fundingSF;
uint256 _feeThreshold;
int256 _openExpoImbalanceLimitBps;
int256 _withdrawalExpoImbalanceLimitBps;
int256 _depositExpoImbalanceLimitBps;
int256 _closeExpoImbalanceLimitBps;
int256 _rebalancerCloseExpoImbalanceLimitBps;
int256 _longImbalanceTargetBps;
uint16 _positionFeeBps;
uint16 _vaultFeeBps;
uint16 _sdexRewardsRatioBps;
uint32 _sdexBurnOnDepositRatio;
address _feeCollector;
uint64 _securityDepositValue;
uint128 _targetUsdnPrice;
uint128 _usdnRebaseThreshold;
uint256 _minLongPosition;
// state
int256 _lastFundingPerDay;
uint128 _lastPrice;
uint128 _lastUpdateTimestamp;
uint256 _pendingProtocolFee;
// pending actions queue
mapping(address => uint256) _pendingActions;
DoubleEndedQueue.Deque _pendingActionsQueue;
// vault
uint256 _balanceVault;
int256 _pendingBalanceVault;
// long positions
int256 _EMA;
uint256 _balanceLong;
uint256 _totalExpo;
HugeUint.Uint512 _liqMultiplierAccumulator;
mapping(int24 => uint256) _tickVersion;
mapping(bytes32 => Position[]) _longPositions;
mapping(bytes32 => TickData) _tickData;
int24 _highestPopulatedTick;
uint256 _totalLongPositions;
LibBitmap.Bitmap _tickBitmap;
// fallback
address _protocolFallbackAddr;
// EIP712
mapping(address => uint256) _nonce;
}
}
Context.sol 28 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}
IERC20Metadata.sol 26 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/extensions/IERC20Metadata.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
/**
* @dev Interface for the optional metadata functions from the ERC-20 standard.
*/
interface IERC20Metadata is IERC20 {
/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);
/**
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the decimals places of the token.
*/
function decimals() external view returns (uint8);
}
IERC20Permit.sol 90 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/extensions/IERC20Permit.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC-20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[ERC-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC-20 allowance (see {IERC20-allowance}) by
* presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
* need to send a transaction, and thus is not required to hold Ether at all.
*
* ==== Security Considerations
*
* There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
* expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
* considered as an intention to spend the allowance in any specific way. The second is that because permits have
* built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
* take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
* generally recommended is:
*
* ```solidity
* function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
* try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
* doThing(..., value);
* }
*
* function doThing(..., uint256 value) public {
* token.safeTransferFrom(msg.sender, address(this), value);
* ...
* }
* ```
*
* Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
* `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
* {SafeERC20-safeTransferFrom}).
*
* Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
* contracts should have entry points that don't rely on permit.
*/
interface IERC20Permit {
/**
* @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
* given ``owner``'s signed approval.
*
* IMPORTANT: The same issues {IERC20-approve} has related to transaction
* ordering also apply here.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `deadline` must be a timestamp in the future.
* - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
* over the EIP712-formatted function arguments.
* - the signature must use ``owner``'s current nonce (see {nonces}).
*
* For more information on the signature format, see the
* https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
* section].
*
* CAUTION: See Security Considerations above.
*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @dev Returns the current nonce for `owner`. This value must be
* included whenever a signature is generated for {permit}.
*
* Every successful call to {permit} increases ``owner``'s nonce by one. This
* prevents a signature from being used multiple times.
*/
function nonces(address owner) external view returns (uint256);
/**
* @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view returns (bytes32);
}
ILiquidationRewardsManagerErrorsEventsTypes.sol 98 lines
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
/**
* @title ILiquidationRewardsManagerErrorsEventsTypes
* @notice Interface defining events, structs, and errors for the {LiquidationRewardsManager}.
*/
interface ILiquidationRewardsManagerErrorsEventsTypes {
/* -------------------------------------------------------------------------- */
/* Events */
/* -------------------------------------------------------------------------- */
/**
* @notice The rewards parameters are updated.
* @param gasUsedPerTick The amount of gas consumed per tick for liquidation.
* @param otherGasUsed The gas consumed for all additional computations.
* @param rebaseGasUsed The gas consumed for optional USDN rebase operation.
* @param rebalancerGasUsed The gas consumed for the optional rebalancer trigger.
* @param baseFeeOffset An offset added to the block's base gas fee.
* @param gasMultiplierBps The multiplier for the gas usage (in BPS).
* @param positionBonusMultiplierBps The multiplier for position size bonus (in BPS).
* @param fixedReward A fixed reward amount (in native currency, converted to wstETH).
* @param maxReward The maximum allowable reward amount (in native currency, converted to wstETH).
*/
event RewardsParametersUpdated(
uint32 gasUsedPerTick,
uint32 otherGasUsed,
uint32 rebaseGasUsed,
uint32 rebalancerGasUsed,
uint64 baseFeeOffset,
uint16 gasMultiplierBps,
uint16 positionBonusMultiplierBps,
uint128 fixedReward,
uint128 maxReward
);
/* -------------------------------------------------------------------------- */
/* Structs */
/* -------------------------------------------------------------------------- */
/**
* @notice The parameters used for calculating rewards.
* @param gasUsedPerTick The gas consumed per tick for liquidation.
* @param otherGasUsed The gas consumed for all additional computations.
* @param rebaseGasUsed The gas consumed for optional USDN rebase operation.
* @param rebalancerGasUsed The gas consumed for the optional rebalancer trigger.
* @param baseFeeOffset An offset added to the block's base gas fee.
* @param gasMultiplierBps The multiplier for the gas usage (in BPS).
* @param positionBonusMultiplierBps The multiplier for position size bonus (in BPS).
* @param fixedReward A fixed reward amount (in native currency, converted to wstETH).
* @param maxReward The maximum allowable reward amount (in native currency, converted to wstETH).
*/
struct RewardsParameters {
uint32 gasUsedPerTick;
uint32 otherGasUsed;
uint32 rebaseGasUsed;
uint32 rebalancerGasUsed;
uint64 baseFeeOffset;
uint16 gasMultiplierBps;
uint16 positionBonusMultiplierBps;
uint128 fixedReward;
uint128 maxReward;
}
/* -------------------------------------------------------------------------- */
/* Errors */
/* -------------------------------------------------------------------------- */
/**
* @notice The `gasUsedPerTick` parameter exceeds the allowable limit.
* @param value The given value.
*/
error LiquidationRewardsManagerGasUsedPerTickTooHigh(uint256 value);
/**
* @notice The `otherGasUsed` parameter exceeds the allowable limit.
* @param value The given value.
*/
error LiquidationRewardsManagerOtherGasUsedTooHigh(uint256 value);
/**
* @notice The `rebaseGasUsed` parameter exceeds the allowable limit.
* @param value The given value.
*/
error LiquidationRewardsManagerRebaseGasUsedTooHigh(uint256 value);
/**
* @notice The `rebalancerGasUsed` parameter exceeds the allowable limit.
* @param value The given value.
*/
error LiquidationRewardsManagerRebalancerGasUsedTooHigh(uint256 value);
/**
* @notice The `maxReward` parameter is below the allowable minimum.
* @param value The given value.
*/
error LiquidationRewardsManagerMaxRewardTooLow(uint256 value);
}
HugeUint.sol 428 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
/**
* @notice A library for manipulating uint512 quantities.
* @dev The 512-bit unsigned integers are represented as two uint256 "limbs", a `hi` limb for the most significant bits,
* and a `lo` limb for the least-significant bits. The resulting uint512 quantity is obtained with `hi * 2^256 + lo`.
*/
library HugeUint {
/// @notice Indicates that the division failed because the divisor is zero or the result overflows a uint256.
error HugeUintDivisionFailed();
/// @notice Indicates that the addition overflowed a uint512.
error HugeUintAddOverflow();
/// @notice Indicates that the subtraction underflowed.
error HugeUintSubUnderflow();
/// @notice Indicates that the multiplication overflowed a uint512.
error HugeUintMulOverflow();
/**
* @notice A 512-bit integer represented as two 256-bit limbs.
* @dev The integer value can be reconstructed as `hi * 2^256 + lo`.
* @param hi The most-significant bits (higher limb) of the integer.
* @param lo The least-significant bits (lower limb) of the integer.
*/
struct Uint512 {
uint256 hi;
uint256 lo;
}
/**
* @notice Wraps a uint256 into a {Uint512} integer.
* @param x A uint256 integer.
* @return The same value as a 512-bit integer.
*/
function wrap(uint256 x) internal pure returns (Uint512 memory) {
return Uint512({ hi: 0, lo: x });
}
/**
* @notice Calculates the sum `a + b` of two 512-bit unsigned integers.
* @dev This function will revert if the result overflows a uint512.
* @param a The first operand.
* @param b The second operand.
* @return res_ The sum of `a` and `b`.
*/
function add(Uint512 memory a, Uint512 memory b) internal pure returns (Uint512 memory res_) {
(res_.lo, res_.hi) = _add(a.lo, a.hi, b.lo, b.hi);
// check for overflow, i.e. if the result is less than b
if (res_.hi < b.hi || (res_.hi == b.hi && res_.lo < b.lo)) {
revert HugeUintAddOverflow();
}
}
/**
* @notice Calculates the difference `a - b` of two 512-bit unsigned integers.
* @dev This function will revert if `b > a`.
* @param a The first operand.
* @param b The second operand.
* @return res_ The difference `a - b`.
*/
function sub(Uint512 memory a, Uint512 memory b) internal pure returns (Uint512 memory res_) {
// check for underflow
if (a.hi < b.hi || (a.hi == b.hi && a.lo < b.lo)) {
revert HugeUintSubUnderflow();
}
(res_.lo, res_.hi) = _sub(a.lo, a.hi, b.lo, b.hi);
}
/**
* @notice Calculates the product `a * b` of two 256-bit unsigned integers using the Chinese remainder theorem.
* @param a The first operand.
* @param b The second operand.
* @return res_ The product `a * b` of the operands as an unsigned 512-bit integer.
*/
function mul(uint256 a, uint256 b) internal pure returns (Uint512 memory res_) {
(res_.lo, res_.hi) = _mul256(a, b);
}
/**
* @notice Calculates the product `a * b` of a 512-bit unsigned integer and a 256-bit unsigned integer.
* @dev This function reverts if the result overflows a uint512.
* @param a The first operand.
* @param b The second operand.
* @return res_ The product `a * b` of the operands as an unsigned 512-bit integer.
*/
function mul(Uint512 memory a, uint256 b) internal pure returns (Uint512 memory res_) {
if ((a.hi == 0 && a.lo == 0) || b == 0) {
return res_;
}
(res_.lo, res_.hi) = _mul256(a.lo, b);
unchecked {
uint256 p = a.hi * b;
if (p / b != a.hi) {
revert HugeUintMulOverflow();
}
res_.hi += p;
if (res_.hi < p) {
revert HugeUintMulOverflow();
}
}
}
/**
* @notice Calculates the division `floor(a / b)` of a 512-bit unsigned integer by an unsigned 256-bit integer.
* @dev The call will revert if the result doesn't fit inside a uint256 or if the denominator is zero.
* @param a The numerator as a 512-bit unsigned integer.
* @param b The denominator as a 256-bit unsigned integer.
* @return res_ The division `floor(a / b)` of the operands as an unsigned 256-bit integer.
*/
function div(Uint512 memory a, uint256 b) internal pure returns (uint256 res_) {
// make sure the output fits inside a uint256, also prevents b == 0
if (b <= a.hi) {
revert HugeUintDivisionFailed();
}
// if the numerator is smaller than the denominator, the result is zero
if (a.hi == 0 && a.lo < b) {
return 0;
}
// the first operand fits in 256 bits, we can use the Solidity division operator
if (a.hi == 0) {
unchecked {
return a.lo / b;
}
}
res_ = _div256(a.lo, a.hi, b);
}
/**
* @notice Computes the division `floor(a/b)` of two 512-bit integers, knowing the result fits inside a uint256.
* @dev Credits chfast (Apache 2.0 License): <https://github.com/chfast/intx>.
* This function will revert if the second operand is zero or if the result doesn't fit inside a uint256.
* @param a The numerator as a 512-bit integer.
* @param b The denominator as a 512-bit integer.
* @return res_ The quotient floor(a/b).
*/
function div(Uint512 memory a, Uint512 memory b) internal pure returns (uint256 res_) {
res_ = _div(a.lo, a.hi, b.lo, b.hi);
}
/**
* @notice Calculates the sum `a + b` of two 512-bit unsigned integers.
* @dev Credits Remco Bloemen (MIT license): <https://2π.com/17/512-bit-division>.
* The result is not checked for overflow, the caller must ensure that the result fits inside a uint512.
* @param a0 The low limb of the first operand.
* @param a1 The high limb of the first operand.
* @param b0 The low limb of the second operand.
* @param b1 The high limb of the second operand.
* @return lo_ The low limb of the result of `a + b`.
* @return hi_ The high limb of the result of `a + b`.
*/
function _add(uint256 a0, uint256 a1, uint256 b0, uint256 b1) internal pure returns (uint256 lo_, uint256 hi_) {
assembly {
lo_ := add(a0, b0)
hi_ := add(add(a1, b1), lt(lo_, a0))
}
}
/**
* @notice Calculates the difference `a - b` of two 512-bit unsigned integers.
* @dev Credits Remco Bloemen (MIT license): <https://2π.com/17/512-bit-division>.
* The result is not checked for underflow, the caller must ensure that the second operand is less than or equal to
* the first operand.
* @param a0 The low limb of the first operand.
* @param a1 The high limb of the first operand.
* @param b0 The low limb of the second operand.
* @param b1 The high limb of the second operand.
* @return lo_ The low limb of the result of `a - b`.
* @return hi_ The high limb of the result of `a - b`.
*/
function _sub(uint256 a0, uint256 a1, uint256 b0, uint256 b1) internal pure returns (uint256 lo_, uint256 hi_) {
assembly {
lo_ := sub(a0, b0)
hi_ := sub(sub(a1, b1), lt(a0, b0))
}
}
/**
* @notice Calculates the product `a * b` of two 256-bit unsigned integers using the Chinese remainder theorem.
* @dev Credits Remco Bloemen (MIT license): <https://2π.com/17/chinese-remainder-theorem>
* and Solady (MIT license): <https://github.com/Vectorized/solady>.
* @param a The first operand.
* @param b The second operand.
* @return lo_ The low limb of the result of `a * b`.
* @return hi_ The high limb of the result of `a * b`.
*/
function _mul256(uint256 a, uint256 b) internal pure returns (uint256 lo_, uint256 hi_) {
assembly {
lo_ := mul(a, b)
let mm := mulmod(a, b, not(0)) // (a * b) % uint256.max
hi_ := sub(mm, add(lo_, lt(mm, lo_)))
}
}
/**
* @notice Calculates the division `floor(a / b)` of a 512-bit unsigned integer by an unsigned 256-bit integer.
* @dev Credits Solady (MIT license): <https://github.com/Vectorized/solady>.
* The caller must ensure that the result fits inside a uint256 and that the division is non-zero.
* For performance reasons, the caller should ensure that the numerator high limb (hi) is non-zero.
* @param a0 The low limb of the numerator.
* @param a1 The high limb of the numerator.
* @param b The denominator as a 256-bit unsigned integer.
* @return res_ The division `floor(a / b)` of the operands as an unsigned 256-bit integer.
*/
function _div256(uint256 a0, uint256 a1, uint256 b) internal pure returns (uint256 res_) {
uint256 r;
assembly {
// to make the division exact, we find out the remainder of the division of a by b
r := mulmod(a1, not(0), b) // (a1 * uint256.max) % b
r := addmod(r, a1, b) // (r + a1) % b
r := addmod(r, a0, b) // (r + a0) % b
// `t` is the least significant bit of `b`
// always greater or equal to 1
let t := and(b, sub(0, b))
// divide `b` by `t`, which is a power of two
b := div(b, t)
// invert `b mod 2**256`
// now that `b` is an odd number, it has an inverse
// modulo `2**256` such that `b * inv = 1 mod 2**256`
// compute the inverse by starting with a seed that is
// correct for four bits. That is, `b * inv = 1 mod 2**4`
let inv := xor(2, mul(3, b))
// 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(b, inv))) // inverse mod 2**8
inv := mul(inv, sub(2, mul(b, inv))) // inverse mod 2**16
inv := mul(inv, sub(2, mul(b, inv))) // inverse mod 2**32
inv := mul(inv, sub(2, mul(b, inv))) // inverse mod 2**64
inv := mul(inv, sub(2, mul(b, inv))) // inverse mod 2**128
res_ :=
mul(
// divide [a1 a0] by the factors of two
// shift in bits from `a1` into `a0`
// for this we need to flip `t` such that it is `2**256 / t`
or(mul(sub(a1, gt(r, a0)), add(div(sub(0, t), t), 1)), div(sub(a0, r), t)),
// inverse mod 2**256
mul(inv, sub(2, mul(b, inv)))
)
}
}
/**
* @notice Computes the division of a 768-bit integer `a` by a 512-bit integer `b`, knowing the reciprocal of `b`.
* @dev Credits chfast (Apache 2.0 License): <https://github.com/chfast/intx>.
* @param a0 The LSB of the numerator.
* @param a1 The middle limb of the numerator.
* @param a2 The MSB of the numerator.
* @param b0 The low limb of the divisor.
* @param b1 The high limb of the divisor.
* @param v The reciprocal `v` as defined in `_reciprocal_2`.
* @return The quotient floor(a/b).
*/
function _div_2(uint256 a0, uint256 a1, uint256 a2, uint256 b0, uint256 b1, uint256 v)
internal
pure
returns (uint256)
{
(uint256 q0, uint256 q1) = _mul256(v, a2);
(q0, q1) = _add(q0, q1, a1, a2);
(uint256 t0, uint256 t1) = _mul256(b0, q1);
uint256 r1;
assembly {
r1 := sub(a1, mul(q1, b1))
}
uint256 r0;
(r0, r1) = _sub(a0, r1, t0, t1);
(r0, r1) = _sub(r0, r1, b0, b1);
assembly {
q1 := add(q1, 1)
}
if (r1 >= q0) {
assembly {
q1 := sub(q1, 1)
}
(r0, r1) = _add(r0, r1, b0, b1);
}
if (r1 > b1 || (r1 == b1 && r0 >= b0)) {
assembly {
q1 := add(q1, 1)
}
// we don't care about the remainder
// (r0, r1) = _sub(r0, r1, b0, b1);
}
return q1;
}
/**
* @notice Computes the division floor(a/b) of two 512-bit integers, knowing the result fits inside a uint256.
* @dev Credits chfast (Apache 2.0 License): <https://github.com/chfast/intx>.
* @param a0 LSB of the numerator.
* @param a1 MSB of the numerator.
* @param b0 LSB of the divisor.
* @param b1 MSB of the divisor.
* @return res_ The quotient floor(a/b).
*/
function _div(uint256 a0, uint256 a1, uint256 b0, uint256 b1) internal pure returns (uint256 res_) {
if (b1 == 0) {
// prevent division by zero
if (b0 == 0) {
revert HugeUintDivisionFailed();
}
// if both operands fit inside a uint256, we can use the Solidity division operator
if (a1 == 0) {
unchecked {
return a0 / b0;
}
}
// if the result fits inside a uint256, we can use the `div(Uint512,uint256)` function
if (b0 > a1) {
return _div256(a0, a1, b0);
}
revert HugeUintDivisionFailed();
}
// if the numerator is smaller than the denominator, the result is zero
if (a1 < b1 || (a1 == b1 && a0 < b0)) {
return 0;
}
// division algo
uint256 lsh = _clz(b1);
if (lsh == 0) {
// numerator is equal or larger than the denominator, and the denominator is at least 0b1000...
// the result is necessarily 1
return 1;
}
uint256 bn_lo;
uint256 bn_hi;
uint256 an_lo;
uint256 an_hi;
uint256 an_ex;
assembly {
let rsh := sub(256, lsh)
bn_lo := shl(lsh, b0)
bn_hi := or(shl(lsh, b1), shr(rsh, b0))
an_lo := shl(lsh, a0)
an_hi := or(shl(lsh, a1), shr(rsh, a0))
an_ex := shr(rsh, a1)
}
uint256 v = _reciprocal_2(bn_lo, bn_hi);
res_ = _div_2(an_lo, an_hi, an_ex, bn_lo, bn_hi, v);
}
/**
* @notice Computes the reciprocal `v = floor((2^512-1) / d) - 2^256`.
* @dev The input must be normalized (d >= 2^255).
* @param d The input value.
* @return v_ The reciprocal of d.
*/
function _reciprocal(uint256 d) internal pure returns (uint256 v_) {
if (d & 0x8000000000000000000000000000000000000000000000000000000000000000 == 0) {
revert HugeUintDivisionFailed();
}
v_ = _div256(type(uint256).max, type(uint256).max - d, d);
}
/**
* @notice Computes the reciprocal `v = floor((2^768-1) / d) - 2^256`, where d is a uint512 integer.
* @dev Credits chfast (Apache 2.0 License): <https://github.com/chfast/intx>.
* @param d0 LSB of the input.
* @param d1 MSB of the input.
* @return v_ The reciprocal of d.
*/
function _reciprocal_2(uint256 d0, uint256 d1) internal pure returns (uint256 v_) {
v_ = _reciprocal(d1);
uint256 p;
assembly {
p := mul(d1, v_)
p := add(p, d0)
if lt(p, d0) {
// carry out
v_ := sub(v_, 1)
if iszero(lt(p, d1)) {
v_ := sub(v_, 1)
p := sub(p, d1)
}
p := sub(p, d1)
}
}
(uint256 t0, uint256 t1) = _mul256(v_, d0);
assembly {
p := add(p, t1)
if lt(p, t1) {
// carry out
v_ := sub(v_, 1)
if and(iszero(lt(p, d1)), or(gt(p, d1), iszero(lt(t0, d0)))) {
// if (<p, t0> >= <d1, d0>)
v_ := sub(v_, 1)
}
}
}
}
/**
* @notice Counts the number of consecutive zero bits, starting from the left.
* @dev Credits Solady (MIT license): <https://github.com/Vectorized/solady>.
* @param x An unsigned integer.
* @return n_ The number of zeroes starting from the most significant bit.
*/
function _clz(uint256 x) internal pure returns (uint256 n_) {
if (x == 0) {
return 256;
}
assembly {
n_ := shl(7, lt(0xffffffffffffffffffffffffffffffff, x))
n_ := or(n_, shl(6, lt(0xffffffffffffffff, shr(n_, x))))
n_ := or(n_, shl(5, lt(0xffffffff, shr(n_, x))))
n_ := or(n_, shl(4, lt(0xffff, shr(n_, x))))
n_ := or(n_, shl(3, lt(0xff, shr(n_, x))))
n_ :=
add(
xor(
n_,
byte(
and(0x1f, shr(shr(n_, x), 0x8421084210842108cc6318c6db6d54be)),
0xf8f9f9faf9fdfafbf9fdfcfdfafbfcfef9fafdfafcfcfbfefafafcfbffffffff
)
),
iszero(x)
)
}
}
}
LibBitmap.sol 236 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import {LibBit} from "./LibBit.sol";
/// @notice Library for storage of packed unsigned booleans.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibBitmap.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/LibBitmap.sol)
/// @author Modified from Solidity-Bits (https://github.com/estarriolvetch/solidity-bits/blob/main/contracts/BitMaps.sol)
library LibBitmap {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTANTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The constant returned when a bitmap scan does not find a result.
uint256 internal constant NOT_FOUND = type(uint256).max;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* STRUCTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev A bitmap in storage.
struct Bitmap {
mapping(uint256 => uint256) map;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the boolean value of the bit at `index` in `bitmap`.
function get(Bitmap storage bitmap, uint256 index) internal view returns (bool isSet) {
// It is better to set `isSet` to either 0 or 1, than zero vs non-zero.
// Both cost the same amount of gas, but the former allows the returned value
// to be reused without cleaning the upper bits.
uint256 b = (bitmap.map[index >> 8] >> (index & 0xff)) & 1;
/// @solidity memory-safe-assembly
assembly {
isSet := b
}
}
/// @dev Updates the bit at `index` in `bitmap` to true.
function set(Bitmap storage bitmap, uint256 index) internal {
bitmap.map[index >> 8] |= (1 << (index & 0xff));
}
/// @dev Updates the bit at `index` in `bitmap` to false.
function unset(Bitmap storage bitmap, uint256 index) internal {
bitmap.map[index >> 8] &= ~(1 << (index & 0xff));
}
/// @dev Flips the bit at `index` in `bitmap`.
/// Returns the boolean result of the flipped bit.
function toggle(Bitmap storage bitmap, uint256 index) internal returns (bool newIsSet) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x20, bitmap.slot)
mstore(0x00, shr(8, index))
let storageSlot := keccak256(0x00, 0x40)
let shift := and(index, 0xff)
let storageValue := xor(sload(storageSlot), shl(shift, 1))
// It makes sense to return the `newIsSet`,
// as it allow us to skip an additional warm `sload`,
// and it costs minimal gas (about 15),
// which may be optimized away if the returned value is unused.
newIsSet := and(1, shr(shift, storageValue))
sstore(storageSlot, storageValue)
}
}
/// @dev Updates the bit at `index` in `bitmap` to `shouldSet`.
function setTo(Bitmap storage bitmap, uint256 index, bool shouldSet) internal {
/// @solidity memory-safe-assembly
assembly {
mstore(0x20, bitmap.slot)
mstore(0x00, shr(8, index))
let storageSlot := keccak256(0x00, 0x40)
let storageValue := sload(storageSlot)
let shift := and(index, 0xff)
sstore(
storageSlot,
// Unsets the bit at `shift` via `and`, then sets its new value via `or`.
or(and(storageValue, not(shl(shift, 1))), shl(shift, iszero(iszero(shouldSet))))
)
}
}
/// @dev Consecutively sets `amount` of bits starting from the bit at `start`.
function setBatch(Bitmap storage bitmap, uint256 start, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
let max := not(0)
let shift := and(start, 0xff)
mstore(0x20, bitmap.slot)
mstore(0x00, shr(8, start))
if iszero(lt(add(shift, amount), 257)) {
let storageSlot := keccak256(0x00, 0x40)
sstore(storageSlot, or(sload(storageSlot), shl(shift, max)))
let bucket := add(mload(0x00), 1)
let bucketEnd := add(mload(0x00), shr(8, add(amount, shift)))
amount := and(add(amount, shift), 0xff)
shift := 0
for {} iszero(eq(bucket, bucketEnd)) { bucket := add(bucket, 1) } {
mstore(0x00, bucket)
sstore(keccak256(0x00, 0x40), max)
}
mstore(0x00, bucket)
}
let storageSlot := keccak256(0x00, 0x40)
sstore(storageSlot, or(sload(storageSlot), shl(shift, shr(sub(256, amount), max))))
}
}
/// @dev Consecutively unsets `amount` of bits starting from the bit at `start`.
function unsetBatch(Bitmap storage bitmap, uint256 start, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
let shift := and(start, 0xff)
mstore(0x20, bitmap.slot)
mstore(0x00, shr(8, start))
if iszero(lt(add(shift, amount), 257)) {
let storageSlot := keccak256(0x00, 0x40)
sstore(storageSlot, and(sload(storageSlot), not(shl(shift, not(0)))))
let bucket := add(mload(0x00), 1)
let bucketEnd := add(mload(0x00), shr(8, add(amount, shift)))
amount := and(add(amount, shift), 0xff)
shift := 0
for {} iszero(eq(bucket, bucketEnd)) { bucket := add(bucket, 1) } {
mstore(0x00, bucket)
sstore(keccak256(0x00, 0x40), 0)
}
mstore(0x00, bucket)
}
let storageSlot := keccak256(0x00, 0x40)
sstore(
storageSlot, and(sload(storageSlot), not(shl(shift, shr(sub(256, amount), not(0)))))
)
}
}
/// @dev Returns number of set bits within a range by
/// scanning `amount` of bits starting from the bit at `start`.
function popCount(Bitmap storage bitmap, uint256 start, uint256 amount)
internal
view
returns (uint256 count)
{
unchecked {
uint256 bucket = start >> 8;
uint256 shift = start & 0xff;
if (!(amount + shift < 257)) {
count = LibBit.popCount(bitmap.map[bucket] >> shift);
uint256 bucketEnd = bucket + ((amount + shift) >> 8);
amount = (amount + shift) & 0xff;
shift = 0;
for (++bucket; bucket != bucketEnd; ++bucket) {
count += LibBit.popCount(bitmap.map[bucket]);
}
}
count += LibBit.popCount((bitmap.map[bucket] >> shift) << (256 - amount));
}
}
/// @dev Returns the index of the most significant set bit in `[0..upTo]`.
/// If no set bit is found, returns `NOT_FOUND`.
function findLastSet(Bitmap storage bitmap, uint256 upTo)
internal
view
returns (uint256 setBitIndex)
{
setBitIndex = NOT_FOUND;
uint256 bucket = upTo >> 8;
uint256 bits;
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, bucket)
mstore(0x20, bitmap.slot)
let offset := and(0xff, not(upTo)) // `256 - (255 & upTo) - 1`.
bits := shr(offset, shl(offset, sload(keccak256(0x00, 0x40))))
if iszero(or(bits, iszero(bucket))) {
for {} 1 {} {
bucket := add(bucket, setBitIndex) // `sub(bucket, 1)`.
mstore(0x00, bucket)
bits := sload(keccak256(0x00, 0x40))
if or(bits, iszero(bucket)) { break }
}
}
}
if (bits != 0) {
setBitIndex = (bucket << 8) | LibBit.fls(bits);
/// @solidity memory-safe-assembly
assembly {
setBitIndex := or(setBitIndex, sub(0, gt(setBitIndex, upTo)))
}
}
}
/// @dev Returns the index of the least significant unset bit in `[begin..upTo]`.
/// If no unset bit is found, returns `NOT_FOUND`.
function findFirstUnset(Bitmap storage bitmap, uint256 begin, uint256 upTo)
internal
view
returns (uint256 unsetBitIndex)
{
unsetBitIndex = NOT_FOUND;
uint256 bucket = begin >> 8;
uint256 negBits;
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, bucket)
mstore(0x20, bitmap.slot)
let offset := and(0xff, begin)
negBits := shl(offset, shr(offset, not(sload(keccak256(0x00, 0x40)))))
if iszero(negBits) {
let lastBucket := shr(8, upTo)
for {} 1 {} {
bucket := add(bucket, 1)
mstore(0x00, bucket)
negBits := not(sload(keccak256(0x00, 0x40)))
if or(negBits, gt(bucket, lastBucket)) { break }
}
if gt(bucket, lastBucket) {
negBits := shl(and(0xff, not(upTo)), shr(and(0xff, not(upTo)), negBits))
}
}
}
if (negBits != 0) {
uint256 r = (bucket << 8) | LibBit.ffs(negBits);
/// @solidity memory-safe-assembly
assembly {
unsetBitIndex := or(r, sub(0, or(gt(r, upTo), lt(r, begin))))
}
}
}
}
DoubleEndedQueue.sol 247 lines
// SPDX-License-Identifier: MIT
// based on the OpenZeppelin implementation
pragma solidity ^0.8.20;
import { IUsdnProtocolTypes as Types } from "../interfaces/UsdnProtocol/IUsdnProtocolTypes.sol";
/**
* @notice A sequence of items with the ability to efficiently push and pop items (i.e. insert and remove) on both ends
* of the sequence (called front and back).
* @dev Storage use is optimized, and all operations are O(1) constant time.
*
* The struct is called `Deque` and holds {IUsdnProtocolTypes.PendingAction}'s. This data structure can only be used in
* storage, and not in memory.
*/
library DoubleEndedQueue {
/// @dev An operation (e.g. {front}) couldn't be completed due to the queue being empty.
error QueueEmpty();
/// @dev A push operation couldn't be completed due to the queue being full.
error QueueFull();
/// @dev An operation (e.g. {atRaw}) couldn't be completed due to an index being out of bounds.
error QueueOutOfBounds();
/**
* @dev Indices are 128 bits so begin and end are packed in a single storage slot for efficient access.
*
* Struct members have an underscore prefix indicating that they are "private" and should not be read or written to
* directly. Use the functions provided below instead. Modifying the struct manually may violate assumptions and
* lead to unexpected behavior.
*
* The first item is at `data[begin]` and the last item is at `data[end - 1]`. This range can wrap around.
* @param _begin The index of the first item in the queue.
* @param _end The index of the item after the last item in the queue.
* @param _data The items in the queue.
*/
struct Deque {
uint128 _begin;
uint128 _end;
mapping(uint128 index => Types.PendingAction) _data;
}
/**
* @dev Inserts an item at the end of the queue.
* Reverts with {QueueFull} if the queue is full.
* @param deque The queue.
* @param value The item to insert.
* @return backIndex_ The raw index of the inserted item.
*/
function pushBack(Deque storage deque, Types.PendingAction memory value) external returns (uint128 backIndex_) {
unchecked {
backIndex_ = deque._end;
if (backIndex_ + 1 == deque._begin) {
revert QueueFull();
}
deque._data[backIndex_] = value;
deque._end = backIndex_ + 1;
}
}
/**
* @dev Removes the item at the end of the queue and returns it.
* Reverts with {QueueEmpty} if the queue is empty.
* @param deque The queue.
* @return value_ The removed item.
*/
function popBack(Deque storage deque) public returns (Types.PendingAction memory value_) {
unchecked {
uint128 backIndex = deque._end;
if (backIndex == deque._begin) {
revert QueueEmpty();
}
--backIndex;
value_ = deque._data[backIndex];
delete deque._data[backIndex];
deque._end = backIndex;
}
}
/**
* @dev Inserts an item at the beginning of the queue.
* Reverts with {QueueFull} if the queue is full.
* @param deque The queue.
* @param value The item to insert.
* @return frontIndex_ The raw index of the inserted item.
*/
function pushFront(Deque storage deque, Types.PendingAction memory value) external returns (uint128 frontIndex_) {
unchecked {
frontIndex_ = deque._begin - 1;
if (frontIndex_ == deque._end) {
revert QueueFull();
}
deque._data[frontIndex_] = value;
deque._begin = frontIndex_;
}
}
/**
* @dev Removes the item at the beginning of the queue and returns it.
* Reverts with {QueueEmpty} if the queue is empty.
* @param deque The queue.
* @return value_ The removed item.
*/
function popFront(Deque storage deque) public returns (Types.PendingAction memory value_) {
unchecked {
uint128 frontIndex = deque._begin;
if (frontIndex == deque._end) {
revert QueueEmpty();
}
value_ = deque._data[frontIndex];
delete deque._data[frontIndex];
deque._begin = frontIndex + 1;
}
}
/**
* @dev Returns the item at the beginning of the queue.
* Reverts with {QueueEmpty} if the queue is empty.
* @param deque The queue.
* @return value_ The item at the front of the queue.
* @return rawIndex_ The raw index of the returned item.
*/
function front(Deque storage deque) external view returns (Types.PendingAction memory value_, uint128 rawIndex_) {
if (empty(deque)) {
revert QueueEmpty();
}
rawIndex_ = deque._begin;
value_ = deque._data[rawIndex_];
}
/**
* @dev Returns the item at the end of the queue.
* Reverts with {QueueEmpty} if the queue is empty.
* @param deque The queue.
* @return value_ The item at the back of the queue.
* @return rawIndex_ The raw index of the returned item.
*/
function back(Deque storage deque) external view returns (Types.PendingAction memory value_, uint128 rawIndex_) {
if (empty(deque)) {
revert QueueEmpty();
}
unchecked {
rawIndex_ = deque._end - 1;
value_ = deque._data[rawIndex_];
}
}
/**
* @dev Returns the item at a position in the queue given by `index`, with the first item at 0 and the last item at
* `length(deque) - 1`.
* Reverts with {QueueOutOfBounds} if the index is out of bounds.
* @param deque The queue.
* @param index The index of the item to return.
* @return value_ The item at the given index.
* @return rawIndex_ The raw index of the item.
*/
function at(Deque storage deque, uint256 index)
external
view
returns (Types.PendingAction memory value_, uint128 rawIndex_)
{
if (index >= length(deque)) {
revert QueueOutOfBounds();
}
// by construction, length is a uint128, so the check above ensures that
// the index can be safely downcast to a uint128
unchecked {
rawIndex_ = deque._begin + uint128(index);
value_ = deque._data[rawIndex_];
}
}
/**
* @dev Returns the item at a position in the queue given by `rawIndex`, indexing into the underlying storage array
* directly.
* Reverts with {QueueOutOfBounds} if the index is out of bounds.
* @param deque The queue.
* @param rawIndex The index of the item to return.
* @return value_ The item at the given index.
*/
function atRaw(Deque storage deque, uint128 rawIndex) external view returns (Types.PendingAction memory value_) {
if (!isValid(deque, rawIndex)) {
revert QueueOutOfBounds();
}
value_ = deque._data[rawIndex];
}
/**
* @dev Deletes the item at a position in the queue given by `rawIndex`, indexing into the underlying storage array
* directly. If clearing the front or back item, then the bounds are updated. Otherwise, the values are simply set
* to zero and the queue's begin and end indices are not updated.
* @param deque The queue.
* @param rawIndex The index of the item to delete.
*/
function clearAt(Deque storage deque, uint128 rawIndex) external {
uint128 backIndex = deque._end;
unchecked {
backIndex--;
}
if (rawIndex == deque._begin) {
popFront(deque); // reverts if empty
} else if (rawIndex == backIndex) {
popBack(deque); // reverts if empty
} else {
// we don't care to revert if this is not a valid index, since we're just clearing it
delete deque._data[rawIndex];
}
}
/**
* @dev Checks if the raw index is valid (in bounds).
* @param deque The queue.
* @param rawIndex The raw index to check.
* @return valid_ Whether the raw index is valid.
*/
function isValid(Deque storage deque, uint128 rawIndex) public view returns (bool valid_) {
if (deque._begin > deque._end) {
// here the values are split at the beginning and end of the range, so invalid indices are in the middle
if (rawIndex < deque._begin && rawIndex >= deque._end) {
return false;
}
} else if (rawIndex < deque._begin || rawIndex >= deque._end) {
return false;
}
valid_ = true;
}
/**
* @dev Returns the number of items in the queue.
* @param deque The queue.
* @return length_ The number of items in the queue.
*/
function length(Deque storage deque) public view returns (uint256 length_) {
unchecked {
length_ = uint256(deque._end - deque._begin);
}
}
/**
* @dev Returns true if the queue is empty.
* @param deque The queue.
* @return empty_ True if the queue is empty.
*/
function empty(Deque storage deque) internal view returns (bool empty_) {
empty_ = deque._end == deque._begin;
}
}
IBaseOracleMiddleware.sol 63 lines
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
import { IUsdnProtocolTypes as Types } from "../UsdnProtocol/IUsdnProtocolTypes.sol";
import { PriceInfo } from "./IOracleMiddlewareTypes.sol";
/**
* @title Base Oracle Middleware interface
* @notice This interface exposes the only functions used or required by the USDN Protocol.
* @dev Any current or future implementation of the oracle middleware must be compatible with
* this interface without any modification.
*/
interface IBaseOracleMiddleware {
/**
* @notice Parse and validate `data` and returns the corresponding price data.
* @dev The data format is specific to the middleware and is simply forwarded from the user transaction's calldata.
* A fee amounting to exactly {validationCost} (with the same `data` and `action`) must be sent or the transaction
* will revert.
* @param actionId A unique identifier for the current action. This identifier can be used to link an `Initiate`
* call with the corresponding `Validate` call.
* @param targetTimestamp The target timestamp for validating the price data. For validation actions, this is the
* timestamp of the initiation.
* @param action Type of action for which the price is requested. The middleware may use this to alter the
* validation of the price or the returned price.
* @param data The data to be used to communicate with oracles, the format varies from middleware to middleware and
* can be different depending on the action.
* @return result_ The price and timestamp as {IOracleMiddlewareTypes.PriceInfo}.
*/
function parseAndValidatePrice(
bytes32 actionId,
uint128 targetTimestamp,
Types.ProtocolAction action,
bytes calldata data
) external payable returns (PriceInfo memory result_);
/**
* @notice Gets the required delay (in seconds) between the moment an action is initiated and the timestamp of the
* price data used to validate that action.
* @return delay_ The validation delay.
*/
function getValidationDelay() external view returns (uint256 delay_);
/**
* @notice Gets The maximum amount of time (in seconds) after initiation during which a low-latency price oracle can
* be used for validation.
* @return delay_ The maximum delay for low-latency validation.
*/
function getLowLatencyDelay() external view returns (uint16 delay_);
/**
* @notice Gets the number of decimals for the price.
* @return decimals_ The number of decimals.
*/
function getDecimals() external view returns (uint8 decimals_);
/**
* @notice Returns the cost of one price validation for the given action (in native token).
* @param data Price data for which to get the fee.
* @param action Type of the action for which the price is requested.
* @return cost_ The cost of one price validation (in native token).
*/
function validationCost(bytes calldata data, Types.ProtocolAction action) external view returns (uint256 cost_);
}
IBaseRebalancer.sol 51 lines
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
import { IUsdnProtocolTypes as Types } from "../UsdnProtocol/IUsdnProtocolTypes.sol";
import { IRebalancerTypes } from "./IRebalancerTypes.sol";
interface IBaseRebalancer {
/**
* @notice Returns the necessary data for the USDN protocol to update the position.
* @return pendingAssets_ The amount of assets that are pending inclusion in the protocol.
* @return maxLeverage_ The maximum leverage of the rebalancer.
* @return currentPosId_ The ID of the current position (`tick` == `NO_POSITION_TICK` if no position).
*/
function getCurrentStateData()
external
view
returns (uint128 pendingAssets_, uint256 maxLeverage_, Types.PositionId memory currentPosId_);
/**
* @notice Returns the minimum amount of assets a user can deposit in the rebalancer.
* @return minAssetDeposit_ The minimum amount of assets that can be deposited by a user.
*/
function getMinAssetDeposit() external view returns (uint256 minAssetDeposit_);
/**
* @notice Returns the data regarding the assets deposited by the provided user.
* @param user The address of the user.
* @return data_ The data regarding the assets deposited by the provided user.
*/
function getUserDepositData(address user) external view returns (IRebalancerTypes.UserDeposit memory data_);
/**
* @notice Indicates that the previous version of the position was closed and a new one was opened.
* @dev If `previousPosValue` equals 0, it means the previous version got liquidated.
* @param newPosId The position ID of the new position.
* @param previousPosValue The amount of assets left in the previous position.
*/
function updatePosition(Types.PositionId calldata newPosId, uint128 previousPosValue) external;
/* -------------------------------------------------------------------------- */
/* Admin */
/* -------------------------------------------------------------------------- */
/**
* @notice Sets the minimum amount of assets to be deposited by a user.
* @dev The new minimum amount must be greater than or equal to the minimum long position of the USDN protocol.
* This function can only be called by the owner or the USDN protocol.
* @param minAssetDeposit The new minimum amount of assets to be deposited.
*/
function setMinAssetDeposit(uint256 minAssetDeposit) external;
}
IUsdn.sol 201 lines
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import { IERC20Permit } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol";
import { IRebaseCallback } from "./IRebaseCallback.sol";
import { IUsdnErrors } from "./IUsdnErrors.sol";
import { IUsdnEvents } from "./IUsdnEvents.sol";
/**
* @title USDN token interface
* @notice Implements the ERC-20 token standard as well as the EIP-2612 permit extension. Additional functions related
* to the specifics of this token are included below.
*/
interface IUsdn is IERC20, IERC20Metadata, IERC20Permit, IUsdnEvents, IUsdnErrors {
/**
* @notice Returns the total number of shares in existence.
* @return shares_ The number of shares.
*/
function totalShares() external view returns (uint256 shares_);
/**
* @notice Returns the number of shares owned by `account`.
* @param account The account to query.
* @return shares_ The number of shares.
*/
function sharesOf(address account) external view returns (uint256 shares_);
/**
* @notice Transfers a given amount of shares from the `msg.sender` to `to`.
* @param to Recipient of the shares.
* @param value Number of shares to transfer.
* @return success_ Indicates whether the transfer was successfully executed.
*/
function transferShares(address to, uint256 value) external returns (bool success_);
/**
* @notice Transfers a given amount of shares from the `from` to `to`.
* @dev There should be sufficient allowance for the spender. Be mindful of the rebase logic. The allowance is in
* tokens. So, after a rebase, the same amount of shares will be worth a higher amount of tokens. In that case,
* the allowance of the initial approval will not be enough to transfer the new amount of tokens. This can
* also happen when your transaction is in the mempool and the rebase happens before your transaction. Also note
* that the amount of tokens deduced from the allowance is rounded up, so the `convertToTokensRoundUp` function
* should be used when converting shares into an allowance value.
* @param from The owner of the shares.
* @param to Recipient of the shares.
* @param value Number of shares to transfer.
* @return success_ Indicates whether the transfer was successfully executed.
*/
function transferSharesFrom(address from, address to, uint256 value) external returns (bool success_);
/**
* @notice Mints new shares, providing a token value.
* @dev Caller must have the MINTER_ROLE.
* @param to Account to receive the new shares.
* @param amount Amount of tokens to mint, is internally converted to the proper shares amounts.
*/
function mint(address to, uint256 amount) external;
/**
* @notice Mints new shares, providing a share value.
* @dev Caller must have the MINTER_ROLE.
* @param to Account to receive the new shares.
* @param amount Amount of shares to mint.
* @return mintedTokens_ Amount of tokens that were minted (informational).
*/
function mintShares(address to, uint256 amount) external returns (uint256 mintedTokens_);
/**
* @notice Destroys a `value` amount of tokens from the caller, reducing the total supply.
* @param value Amount of tokens to burn, is internally converted to the proper shares amounts.
*/
function burn(uint256 value) external;
/**
* @notice Destroys a `value` amount of tokens from `account`, deducting from the caller's allowance.
* @param account Account to burn tokens from.
* @param value Amount of tokens to burn, is internally converted to the proper shares amounts.
*/
function burnFrom(address account, uint256 value) external;
/**
* @notice Destroys a `value` amount of shares from the caller, reducing the total supply.
* @param value Amount of shares to burn.
*/
function burnShares(uint256 value) external;
/**
* @notice Destroys a `value` amount of shares from `account`, deducting from the caller's allowance.
* @dev There should be sufficient allowance for the spender. Be mindful of the rebase logic. The allowance is in
* tokens. So, after a rebase, the same amount of shares will be worth a higher amount of tokens. In that case,
* the allowance of the initial approval will not be enough to transfer the new amount of tokens. This can
* also happen when your transaction is in the mempool and the rebase happens before your transaction. Also note
* that the amount of tokens deduced from the allowance is rounded up, so the `convertToTokensRoundUp` function
* should be used when converting shares into an allowance value.
* @param account Account to burn shares from.
* @param value Amount of shares to burn.
*/
function burnSharesFrom(address account, uint256 value) external;
/**
* @notice Converts a number of tokens to the corresponding amount of shares.
* @dev The conversion reverts with `UsdnMaxTokensExceeded` if the corresponding amount of shares overflows.
* @param amountTokens The amount of tokens to convert to shares.
* @return shares_ The corresponding amount of shares.
*/
function convertToShares(uint256 amountTokens) external view returns (uint256 shares_);
/**
* @notice Converts a number of shares to the corresponding amount of tokens.
* @dev The conversion never overflows as we are performing a division. The conversion rounds to the nearest amount
* of tokens that minimizes the error when converting back to shares.
* @param amountShares The amount of shares to convert to tokens.
* @return tokens_ The corresponding amount of tokens.
*/
function convertToTokens(uint256 amountShares) external view returns (uint256 tokens_);
/**
* @notice Converts a number of shares to the corresponding amount of tokens, rounding up.
* @dev Use this function to determine the amount of a token approval, as we always round up when deducting from
* a token transfer allowance.
* @param amountShares The amount of shares to convert to tokens.
* @return tokens_ The corresponding amount of tokens, rounded up.
*/
function convertToTokensRoundUp(uint256 amountShares) external view returns (uint256 tokens_);
/**
* @notice Returns the current maximum tokens supply, given the current divisor.
* @dev This function is used to check if a conversion operation would overflow.
* @return maxTokens_ The maximum number of tokens that can exist.
*/
function maxTokens() external view returns (uint256 maxTokens_);
/**
* @notice Decreases the global divisor, which effectively grows all balances and the total supply.
* @dev If the provided divisor is larger than or equal to the current divisor value, no rebase will happen
* If the new divisor is smaller than `MIN_DIVISOR`, the value will be clamped to `MIN_DIVISOR`.
* Caller must have the `REBASER_ROLE`.
* @param newDivisor The new divisor, should be strictly smaller than the current one and greater or equal to
* `MIN_DIVISOR`.
* @return rebased_ Whether a rebase happened.
* @return oldDivisor_ The previous value of the divisor.
* @return callbackResult_ The result of the callback, if a rebase happened and a callback handler is defined.
*/
function rebase(uint256 newDivisor)
external
returns (bool rebased_, uint256 oldDivisor_, bytes memory callbackResult_);
/**
* @notice Sets the rebase handler address.
* @dev Emits a `RebaseHandlerUpdated` event.
* If set to the zero address, no handler will be called after a rebase.
* Caller must have the `DEFAULT_ADMIN_ROLE`.
* @param newHandler The new handler address.
*/
function setRebaseHandler(IRebaseCallback newHandler) external;
/* -------------------------------------------------------------------------- */
/* Dev view functions */
/* -------------------------------------------------------------------------- */
/**
* @notice Gets the current value of the divisor that converts between tokens and shares.
* @return divisor_ The current divisor.
*/
function divisor() external view returns (uint256 divisor_);
/**
* @notice Gets the rebase handler address, which is called whenever a rebase happens.
* @return rebaseHandler_ The rebase handler address.
*/
function rebaseHandler() external view returns (IRebaseCallback rebaseHandler_);
/**
* @notice Gets the minter role signature.
* @return minter_role_ The role signature.
*/
function MINTER_ROLE() external pure returns (bytes32 minter_role_);
/**
* @notice Gets the rebaser role signature.
* @return rebaser_role_ The role signature.
*/
function REBASER_ROLE() external pure returns (bytes32 rebaser_role_);
/**
* @notice Gets the maximum value of the divisor, which is also the initial value.
* @return maxDivisor_ The maximum divisor.
*/
function MAX_DIVISOR() external pure returns (uint256 maxDivisor_);
/**
* @notice Gets the minimum acceptable value of the divisor.
* @dev The minimum divisor that can be set. This corresponds to a growth of 1B times. Technically, 1e5 would still
* work without precision errors.
* @return minDivisor_ The minimum divisor.
*/
function MIN_DIVISOR() external pure returns (uint256 minDivisor_);
}
IERC20.sol 79 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC-20 standard as defined in the ERC.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 value) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 value) external returns (bool);
}
LibBit.sol 180 lines
// 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);
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* 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))
}
}
}
IOracleMiddlewareTypes.sol 59 lines
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
/**
* @notice The price and timestamp returned by the oracle middleware.
* @param price The validated asset price, potentially adjusted by the middleware.
* @param neutralPrice The neutral/average price of the asset.
* @param timestamp The timestamp of the price data.
*/
struct PriceInfo {
uint256 price;
uint256 neutralPrice;
uint256 timestamp;
}
/**
* @notice The price and timestamp returned by the Chainlink oracle.
* @param price The asset price formatted by the middleware.
* @param timestamp When the price was published on chain.
*/
struct ChainlinkPriceInfo {
int256 price;
uint256 timestamp;
}
/**
* @notice Representation of a Pyth price with a uint256 price.
* @param price The price of the asset.
* @param conf The confidence interval around the price (in dollars, absolute value).
* @param publishTime Unix timestamp describing when the price was published.
*/
struct FormattedPythPrice {
uint256 price;
uint256 conf;
uint256 publishTime;
}
/**
* @notice The price and timestamp returned by the Redstone oracle.
* @param price The asset price formatted by the middleware.
* @param timestamp The timestamp of the price data.
*/
struct RedstonePriceInfo {
uint256 price;
uint256 timestamp;
}
/**
* @notice The different confidence interval of a Pyth price.
* @dev Applied to the neutral price and available as `price`.
* @param Up Adjusted price at the upper bound of the confidence interval.
* @param Down Adjusted price at the lower bound of the confidence interval.
* @param None Neutral price without adjustment.
*/
enum ConfidenceInterval {
Up,
Down,
None
}
IRebalancerTypes.sol 59 lines
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
/**
* @title Rebalancer Types
* @notice Defines all custom types used by the Rebalancer contract.
*/
interface IRebalancerTypes {
/**
* @notice Represents the deposit data of a user.
* @dev A value of zero for `initiateTimestamp` indicates that the deposit or withdrawal has been validated.
* @param initiateTimestamp The timestamp when the deposit or withdrawal was initiated.
* @param amount The amount of assets deposited by the user.
* @param entryPositionVersion The version of the position the user entered.
*/
struct UserDeposit {
uint40 initiateTimestamp;
uint88 amount; // maximum 309'485'009 tokens with 18 decimals
uint128 entryPositionVersion;
}
/**
* @notice Represents data for a specific version of a position.
* @dev The difference between `amount` here and the amount saved in the USDN protocol is the liquidation bonus.
* @param amount The amount of assets used as collateral to open the position.
* @param tick The tick of the position.
* @param tickVersion The version of the tick.
* @param index The index of the position in the tick list.
* @param entryAccMultiplier The accumulated PnL multiplier of all positions up to this one.
*/
struct PositionData {
uint128 amount;
int24 tick;
uint256 tickVersion;
uint256 index;
uint256 entryAccMultiplier;
}
/**
* @notice Defines parameters related to the validation process for rebalancer deposits and withdrawals.
* @dev If `validationDeadline` has passed, the user must wait until the cooldown duration has elapsed. Then, for
* deposit actions, the user must retrieve its funds using {IRebalancer.resetDepositAssets}. For withdrawal actions,
* the user can simply initiate a new withdrawal.
* @param validationDelay The minimum duration in seconds between an initiate action and the corresponding validate
* action.
* @param validationDeadline The maximum duration in seconds between an initiate action and the corresponding
* validate action.
* @param actionCooldown The duration in seconds from the initiate action during which the user can't interact with
* the rebalancer if the `validationDeadline` is exceeded.
* @param closeDelay The Duration in seconds from the last rebalancer long position opening during which the user
* can't perform an {IRebalancer.initiateClosePosition}.
*/
struct TimeLimits {
uint64 validationDelay;
uint64 validationDeadline;
uint64 actionCooldown;
uint64 closeDelay;
}
}
IRebaseCallback.sol 12 lines
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
interface IRebaseCallback {
/**
* @notice Called by the USDN token after a rebase has happened.
* @param oldDivisor The value of the divisor before the rebase.
* @param newDivisor The value of the divisor after the rebase (necessarily smaller than `oldDivisor`).
* @return result_ Arbitrary data that will be forwarded to the caller of `rebase`.
*/
function rebaseCallback(uint256 oldDivisor, uint256 newDivisor) external returns (bytes memory result_);
}
IUsdnErrors.sol 25 lines
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
/**
* @title Errors for the USDN token contract
* @notice Defines all custom errors emitted by the USDN token contract.
*/
interface IUsdnErrors {
/**
* @dev The amount of tokens exceeds the maximum allowed limit.
* @param value The invalid token value.
*/
error UsdnMaxTokensExceeded(uint256 value);
/**
* @dev The sender's share balance is insufficient.
* @param sender The sender's address.
* @param balance The current share balance of the sender.
* @param needed The required amount of shares for the transfer.
*/
error UsdnInsufficientSharesBalance(address sender, uint256 balance, uint256 needed);
/// @dev The divisor value in storage is invalid (< 1).
error UsdnInvalidDivisor();
}
IUsdnEvents.sol 24 lines
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
import { IRebaseCallback } from "./IRebaseCallback.sol";
/**
* @title Events for the USDN token contract
* @notice Defines all custom events emitted by the USDN token contract.
*/
interface IUsdnEvents {
/**
* @notice The divisor was updated, emitted during a rebase.
* @param oldDivisor The divisor value before the rebase.
* @param newDivisor The new divisor value.
*/
event Rebase(uint256 oldDivisor, uint256 newDivisor);
/**
* @notice The rebase handler address was updated.
* @dev The rebase handler is a contract that is called when a rebase occurs.
* @param newHandler The address of the new rebase handler contract.
*/
event RebaseHandlerUpdated(IRebaseCallback newHandler);
}
Read Contract
BASE_GAS_COST 0x079766a8 → uint256
BPS_DIVISOR 0x191fe1ed → uint32
MAX_GAS_USED_PER_TICK 0xec41da2f → uint256
MAX_OTHER_GAS_USED 0xef35e921 → uint256
MAX_REBALANCER_GAS_USED 0x8786290e → uint256
MAX_REBASE_GAS_USED 0x14dc966d → uint256
getLiquidationRewards 0xd367b11b → uint256
getRewardsParameters 0x368e4d16 → tuple
owner 0x8da5cb5b → address
pendingOwner 0xe30c3978 → address
Write Contract 4 functions
These functions modify contract state and require a wallet transaction to execute.
acceptOwnership 0x79ba5097
No parameters
renounceOwnership 0x715018a6
No parameters
setRewardsParameters 0xbd1ef064
uint32 gasUsedPerTick
uint32 otherGasUsed
uint32 rebaseGasUsed
uint32 rebalancerGasUsed
uint64 baseFeeOffset
uint16 gasMultiplierBps
uint16 positionBonusMultiplierBps
uint128 fixedReward
uint128 maxReward
transferOwnership 0xf2fde38b
address newOwner
Recent Transactions
No transactions found for this address