Source Code
Overview
ETH Balance
ETH Value
$0.00View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Cross-Chain Transactions
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Name:
SettlementDispatcher
Compiler Version
v0.8.28+commit.7893614a
Optimization Enabled:
Yes with 200 runs
Other Settings:
cancun EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import {SafeERC20, IERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import { IStargate, Ticket } from "../interfaces/IStargate.sol";
import { IEtherFiDataProvider } from "../interfaces/IEtherFiDataProvider.sol";
import { IBoringOnChainQueue } from "../interfaces/IBoringOnChainQueue.sol";
import { BinSponsor } from "../interfaces/ICashModule.sol";
import { MessagingFee, OFTReceipt, SendParam } from "../interfaces/IOFT.sol";
import { IL2GatewayRouter, IL2Messenger } from "../interfaces/IScrollERC20Bridge.sol";
import { UpgradeableProxy } from "../utils/UpgradeableProxy.sol";
import { Constants } from "../utils/Constants.sol";
/**
* @title SettlementDispatcher
* @author [email protected]
* @notice This contract receives payments from user safes and bridges it to another chain to pay the fiat provider
*/
contract SettlementDispatcher is UpgradeableProxy, Constants {
using SafeERC20 for IERC20;
/**
* @notice Struct containing destination chain information for token bridging
* @dev Stores all data needed to bridge a token to its destination
*/
struct DestinationData {
/// @notice Endpoint ID of the destination chain
uint32 destEid;
/// @notice Recipient address on the destination chain
address destRecipient;
/// @notice Address of the Stargate router to use for this token
address stargate;
/// @notice Whether to use canonical bridge for the token
bool useCanonicalBridge;
/// @notice Minimum gas limit for the canonical bridge
uint64 minGasLimit;
}
/**
* @notice Role identifier for accounts permitted to bridge tokens
*/
bytes32 public constant SETTLEMENT_DISPATCHER_BRIDGER_ROLE = keccak256("SETTLEMENT_DISPATCHER_BRIDGER_ROLE");
/**
* @notice Address of the Scroll ERC20 Gateway Router
*/
address public constant GATEWAY_ROUTER = 0x4C0926FF5252A435FD19e10ED15e5a249Ba19d79;
/**
* @notice Address of the Scroll ETH Messenger
*/
address public constant ETH_MESSENGER = 0x781e90f1c8Fc4611c9b7497C3B47F99Ef6969CbC;
/**
* @notice Bin Sponsor for the settlement dispatcher
*/
BinSponsor public immutable binSponsor;
/**
* @notice Instance of the EtherFiDataProvider
*/
IEtherFiDataProvider public immutable dataProvider;
/// @custom:storage-location erc7201:etherfi.storage.SettlementDispatcher
/**
* @dev Storage struct for SettlementDispatcher (follows ERC-7201 naming convention)
*/
struct SettlementDispatcherStorage {
/// @notice Mapping of token addresses to their destination chain information
mapping(address token => DestinationData) destinationData;
/// @notice Mapping of liquid token address to its withdraw queue
mapping (address liquidToken => address boringQueue) liquidWithdrawQueue;
}
/**
* @notice Storage location for SettlementDispatcher (ERC-7201 compliant)
* @dev keccak256(abi.encode(uint256(keccak256("etherfi.storage.SettlementDispatcher")) - 1)) & ~bytes32(uint256(0xff))
*/
bytes32 private constant SettlementDispatcherStorageLocation = 0x78555946a409defb00fb08ff23c8988ad687a02e1525a4fc9b7fd83443409e00;
/**
* @notice Emitted when destination data is set for tokens
* @param tokens Array of token addresses that were configured
* @param destDatas Array of destination data corresponding to each token
*/
event DestinationDataSet(address[] tokens, DestinationData[] destDatas);
/**
* @notice Emitted when funds are successfully bridged via Stargate
* @param token Address of the token that was bridged
* @param amount Amount of tokens that were bridged
* @param ticket Stargate ticket containing details of the bridge transaction
*/
event FundsBridgedWithStargate(address indexed token, uint256 amount, Ticket ticket);
/**
* @notice Emitted when funds are withdrawn from the contract
* @param token Address of the token that was withdrawn (address(0) for ETH)
* @param amount Amount of tokens or ETH that was withdrawn
* @param recipient Address that received the withdrawn funds
*/
event FundsWithdrawn(address indexed token, uint256 amount, address indexed recipient);
/**
* @notice Emitted when liquid asset withdraw config is set
* @param token Address of the liquid asset
* @param boringQueue Address of the boring queue
*/
event LiquidWithdrawQueueSet(address indexed token, address indexed boringQueue);
/**
* @notice Emitted when liquid asset withdrawal is requested
* @param liquidToken Address of the liquid asset
* @param assetOut Address of the asset out
* @param amountToWithdraw Amount of liquid assets withdrawn
* @param amountOut Amount of underlying tokens to receive
*/
event LiquidWithdrawalRequested(address indexed liquidToken, address indexed assetOut, uint128 amountToWithdraw, uint128 amountOut);
/**
* @notice Emitted when funds are transferred to the refund wallet
* @param asset Address of the asset transferred
* @param refundWallet Address of the refund wallet
* @param amount Amount of the asset transferred
*/
event TransferToRefundWallet(address asset, address refundWallet, uint256 amount);
/**
* @notice Emitted when tokens are withdrawn from scroll with the canonical bridge
* @param token Address of the token that was withdrawn
* @param recipient Address of the recipient
* @param amount Amount of the token that was withdrawn
*/
event CanonicalBridgeWithdraw(address indexed token, address indexed recipient, uint256 amount);
/**
* @notice Thrown when input arrays have different lengths
*/
error ArrayLengthMismatch();
/**
* @notice Thrown when an invalid address or value is provided
*/
error InvalidValue();
/**
* @notice Thrown when trying to bridge a token with no destination data set
*/
error DestinationDataNotSet();
/**
* @notice Thrown when the provided Stargate router does not support the token
*/
error StargateValueInvalid();
/**
* @notice Thrown when the contract has insufficient token balance for an operation
*/
error InsufficientBalance();
/**
* @notice Thrown when a withdrawal of ETH fails
*/
error WithdrawFundsFailed();
/**
* @notice Thrown when attempting to withdraw zero tokens or ETH
*/
error CannotWithdrawZeroAmount();
/**
* @notice Thrown when the native fee for bridging is higher than the provided value
*/
error InsufficientFeeToCoverCost();
/**
* @notice Thrown when the minimum return amount is not satisfied
*/
error InsufficientMinReturn();
/**
* @notice Thrown when liquid withdraw config is not set for the liquid token
*/
error LiquidWithdrawConfigNotSet();
/**
* @notice Thrown when the boring queue has a different boring vault than expected
*/
error InvalidBoringQueue();
/**
* @notice Thrown when refund wallet is not set and trying to transfer to refund wallet
*/
error RefundWalletNotSet();
/**
* @notice Thrown when the return amount is less than min return
*/
error InsufficientReturnAmount();
/**
* @notice Constructor that disables initializers
* @dev Cannot be called again after deployment (UUPS pattern)
*/
/// @custom:oz-upgrades-unsafe-allow constructor
constructor(BinSponsor _binSponsor, address _dataProvider) {
_disableInitializers();
binSponsor = _binSponsor;
dataProvider = IEtherFiDataProvider(_dataProvider);
}
/**
* @notice Initializes the contract with role registry and token destination data
* @dev Can only be called once due to initializer modifier
* @param _roleRegistry Address of the role registry contract
* @param _tokens Array of token addresses to configure
* @param _destDatas Array of destination data corresponding to each token
* @custom:throws ArrayLengthMismatch If arrays have different lengths
* @custom:throws InvalidValue If any address parameter is zero
* @custom:throws StargateValueInvalid If the Stargate router doesn't support the token
*/
function initialize(address _roleRegistry, address[] calldata _tokens, DestinationData[] calldata _destDatas) external initializer {
__UpgradeableProxy_init(_roleRegistry);
_setDestinationData(_tokens, _destDatas);
}
/**
* @dev Internal function to access the contract's storage
* @return $ Storage pointer to the SettlementDispatcherStorage struct
*/
function _getSettlementDispatcherStorage() internal pure returns (SettlementDispatcherStorage storage $) {
assembly {
$.slot := SettlementDispatcherStorageLocation
}
}
/**
* @notice Function to fetch the destination data for a token
* @param token Address of the token
* @return Destination data struct for the specified token
*/
function destinationData(address token) public view returns (DestinationData memory) {
return _getSettlementDispatcherStorage().destinationData[token];
}
/**
* @notice Function to set the destination data for an array of tokens
* @dev Only callable by the role registry owner
* @param tokens Addresses of tokens to configure
* @param destDatas Destination data structs for respective tokens
* @custom:throws ArrayLengthMismatch If arrays have different lengths
* @custom:throws InvalidValue If any address parameter is zero
* @custom:throws StargateValueInvalid If the Stargate router doesn't support the token
*/
function setDestinationData(address[] calldata tokens, DestinationData[] calldata destDatas) external onlyRoleRegistryOwner {
_setDestinationData(tokens, destDatas);
}
/**
* @notice Function to set the liquid asset withdraw queue
* @dev Only callable by the role registry owner
* @param asset Address of the liquid asset
* @param boringQueue Address of the boring queue
* @custom:throws InvalidValue If any address parameter is zero
* @custom:throws InvalidBoringQueue If the queue does not belong to the liquid asset
*/
function setLiquidAssetWithdrawQueue(address asset, address boringQueue) external onlyRoleRegistryOwner {
if (asset == address(0) || boringQueue == address(0)) revert InvalidValue();
if (asset != address(IBoringOnChainQueue(boringQueue).boringVault())) revert InvalidBoringQueue();
_getSettlementDispatcherStorage().liquidWithdrawQueue[asset] = boringQueue;
emit LiquidWithdrawQueueSet(asset, boringQueue);
}
/**
* @notice Returns the liquid asset withdraw queue
* @param asset Address of the liquid asset
* @return Boring Queue for liquid asset
*/
function getLiquidAssetWithdrawQueue(address asset) external view returns (address) {
return _getSettlementDispatcherStorage().liquidWithdrawQueue[asset];
}
/**
* @notice Returns the refund wallet address set in the data provider contract
* @return Refund wallet address
*/
function getRefundWallet() public view returns (address) {
return dataProvider.getRefundWallet();
}
/**
* @notice Function to withdraw liquid tokens inside the Settlement Dispatcher
* @dev Only callable by addresses with SETTLEMENT_DISPATCHER_BRIDGER_ROLE
* @param liquidToken Address of the liquid token
* @param assetOut Address of the underlying token to receive
* @param amount Amount of liquid tokens to withdraw
* @param minReturn Acceptable min return amount of asset out
* @param discount Acceptable discount in bps
* @param secondsToDeadline Expiry deadline in seconds from now
*/
function withdrawLiquidAsset(address liquidToken, address assetOut, uint128 amount, uint128 minReturn, uint16 discount, uint24 secondsToDeadline) external onlyRole(SETTLEMENT_DISPATCHER_BRIDGER_ROLE) {
IBoringOnChainQueue boringQueue = IBoringOnChainQueue(_getSettlementDispatcherStorage().liquidWithdrawQueue[liquidToken]);
if (address(boringQueue) == address(0)) revert LiquidWithdrawConfigNotSet();
uint128 amountOutFromQueue = boringQueue.previewAssetsOut(assetOut, amount, discount);
if (amountOutFromQueue < minReturn) revert InsufficientReturnAmount();
IERC20(liquidToken).forceApprove(address(boringQueue), amount);
boringQueue.requestOnChainWithdraw(assetOut, amount, discount, secondsToDeadline);
emit LiquidWithdrawalRequested(liquidToken, assetOut, amount, amountOutFromQueue);
}
/**
* @notice Transfers funds to the refund wallet
* @dev Only callable by addresses with SETTLEMENT_DISPATCHER_BRIDGER_ROLE
* @param asset Address of the token to transfer
* @param amount Amount of tokens to transfer
* @custom:throws RefundWalletNotSet If the refund wallet address is not set
* @custom:throws CannotWithdrawZeroAmount If attempting to withdraw zero tokens or ETH
* @custom:throws WithdrawFundsFailed If ETH transfer fails
*/
function transferFundsToRefundWallet(address asset, uint256 amount) external nonReentrant onlyRole(SETTLEMENT_DISPATCHER_BRIDGER_ROLE) {
address refundWallet = getRefundWallet();
if (refundWallet == address(0)) revert RefundWalletNotSet();
amount = _withdrawFunds(asset, refundWallet, amount);
emit TransferToRefundWallet(asset, refundWallet, amount);
}
/**
* @notice Function to bridge tokens to another chain
* @dev Only callable by addresses with SETTLEMENT_DISPATCHER_BRIDGER_ROLE
* @param token Address of the token to bridge
* @param amount Amount of the token to bridge
* @param minReturnLD Minimum amount to receive on the destination chain
* @custom:throws InvalidValue If token is address(0) or amount is 0
* @custom:throws InsufficientBalance If the contract doesn't have enough tokens
* @custom:throws DestinationDataNotSet If destination data is not set for the token
* @custom:throws InsufficientMinReturn If the expected return is less than minReturnLD
* @custom:throws InsufficientFeeToCoverCost If not enough ETH is provided for fees
*/
function bridge(address token, uint256 amount, uint256 minReturnLD) external payable whenNotPaused onlyRole(SETTLEMENT_DISPATCHER_BRIDGER_ROLE) {
if (token == address(0) || amount == 0) revert InvalidValue();
uint256 balance = 0;
if (token == ETH) balance = address(this).balance;
else balance = IERC20(token).balanceOf(address(this));
if (balance < amount) revert InsufficientBalance();
DestinationData memory destData = _getSettlementDispatcherStorage().destinationData[token];
if (destData.useCanonicalBridge) {
_withdrawCanonicalBridge(token, destData.destRecipient, amount, destData.minGasLimit);
}
else {
(address stargate, uint256 valueToSend, uint256 minReturnFromStargate, SendParam memory sendParam, MessagingFee memory messagingFee) =
prepareRideBus(token, amount);
if (minReturnLD > minReturnFromStargate) revert InsufficientMinReturn();
if (address(this).balance < valueToSend) revert InsufficientFeeToCoverCost();
if (token != ETH) IERC20(token).forceApprove(stargate, amount);
(, , Ticket memory ticket) = IStargate(stargate).sendToken{ value: valueToSend }(sendParam, messagingFee, payable(address(this)));
emit FundsBridgedWithStargate(token, amount, ticket);
}
}
/**
* @notice Prepares parameters for the Stargate bridge transaction
* @dev Uses Stargate's "Ride the Bus" pattern for token bridging
* @param token Address of the token to bridge
* @param amount Amount of the token to bridge
* @return stargate Address of the Stargate router to use
* @return valueToSend Amount of ETH needed for the transaction
* @return minReturnFromStargate Minimum amount expected to be received on destination
* @return sendParam Stargate SendParam struct with bridging details
* @return messagingFee Stargate MessagingFee struct with fee details
* @custom:throws InvalidValue If token is address(0) or amount is 0
* @custom:throws InsufficientBalance If the contract doesn't have enough tokens
* @custom:throws DestinationDataNotSet If destination data is not set for the token
*/
function prepareRideBus(
address token,
uint256 amount
) public view returns (address stargate, uint256 valueToSend, uint256 minReturnFromStargate, SendParam memory sendParam, MessagingFee memory messagingFee) {
DestinationData memory destData = _getSettlementDispatcherStorage().destinationData[token];
if (destData.destRecipient == address(0)) revert DestinationDataNotSet();
stargate = destData.stargate;
sendParam = SendParam({
dstEid: destData.destEid,
to: bytes32(uint256(uint160(destData.destRecipient))),
amountLD: amount,
minAmountLD: amount,
extraOptions: new bytes(0),
composeMsg: new bytes(0),
oftCmd: new bytes(1)
});
(, , OFTReceipt memory receipt) = IStargate(stargate).quoteOFT(sendParam);
sendParam.minAmountLD = receipt.amountReceivedLD;
minReturnFromStargate = receipt.amountReceivedLD;
messagingFee = IStargate(stargate).quoteSend(sendParam, false);
valueToSend = messagingFee.nativeFee;
if (IStargate(stargate).token() == address(0x0)) {
valueToSend += sendParam.amountLD;
}
}
/**
* @notice Withdraws tokens or ETH from the contract
* @dev Only callable by the role registry owner
* @param token Address of the token to withdraw (address(0) for ETH)
* @param recipient Address to receive the withdrawn funds
* @param amount Amount to withdraw (0 to withdraw all)
* @custom:throws InvalidValue If recipient is address(0)
* @custom:throws CannotWithdrawZeroAmount If attempting to withdraw zero tokens or ETH
* @custom:throws WithdrawFundsFailed If ETH transfer fails
*/
function withdrawFunds(address token, address recipient, uint256 amount) external nonReentrant onlyRoleRegistryOwner() {
if (recipient == address(0)) revert InvalidValue();
amount = _withdrawFunds(token, recipient, amount);
emit FundsWithdrawn(token, amount, recipient);
}
/**
* @notice Withdraw tokens from scroll with the canonical bridge
* @dev Used by bridge function
* @param token Address of the token to withdraw
* @param recipient Address to receive the funds on ethereum
* @param amount Amount to withdraw
* @param minGasLimit Minimum gas limit for the withdrawal
*/
function _withdrawCanonicalBridge(address token, address recipient, uint256 amount, uint256 minGasLimit) internal returns (uint256) {
if (token == ETH) {
IL2Messenger(ETH_MESSENGER).sendMessage{value: amount}(recipient, amount, "", minGasLimit);
}
else {
address gateway = IL2GatewayRouter(GATEWAY_ROUTER).getERC20Gateway(token);
IERC20(token).forceApprove(gateway, amount);
IL2GatewayRouter(GATEWAY_ROUTER).withdrawERC20(token, recipient, amount, minGasLimit);
}
emit CanonicalBridgeWithdraw(token, recipient, amount);
return amount;
}
/**
* @notice Internal function to handle withdrawal of tokens or ETH
* @dev Used by both withdrawFunds and transferFundsToRefundWallet
* @param token Address of the token to withdraw
* @param recipient Address to receive the withdrawn funds
* @param amount Amount to withdraw (0 to withdraw all available balance)
* @custom:throws CannotWithdrawZeroAmount If attempting to withdraw zero tokens or ETH
* @custom:throws WithdrawFundsFailed If ETH transfer fails
*/
function _withdrawFunds(address token, address recipient, uint256 amount) internal returns (uint256) {
if (token == ETH) {
if (amount == 0) amount = address(this).balance;
if (amount == 0) revert CannotWithdrawZeroAmount();
(bool success, ) = payable(recipient).call{value: amount}("");
if (!success) revert WithdrawFundsFailed();
} else {
if (amount == 0) amount = IERC20(token).balanceOf(address(this));
if (amount == 0) revert CannotWithdrawZeroAmount();
IERC20(token).safeTransfer(recipient, amount);
}
return amount;
}
/**
* @notice Internal function to set destination data for tokens
* @dev Validates and stores destination data for each token
* @param tokens Array of token addresses to configure
* @param destDatas Array of destination data corresponding to each token
* @custom:throws ArrayLengthMismatch If arrays have different lengths
* @custom:throws InvalidValue If any address parameter is zero
* @custom:throws StargateValueInvalid If the Stargate router doesn't support the token
*/
function _setDestinationData(address[] calldata tokens, DestinationData[] calldata destDatas) internal {
uint256 len = tokens.length;
if (len != destDatas.length) revert ArrayLengthMismatch();
SettlementDispatcherStorage storage $ = _getSettlementDispatcherStorage();
for (uint256 i = 0; i < len; ) {
if (destDatas[i].useCanonicalBridge) {
if (tokens[i] == address(0) || destDatas[i].destRecipient == address(0) || destDatas[i].stargate != address(0) || destDatas[i].destEid != 0) revert InvalidValue();
}
else {
if (tokens[i] == address(0) || destDatas[i].destRecipient == address(0) || destDatas[i].stargate == address(0)) revert InvalidValue();
if (tokens[i] == ETH) {
if (IStargate(destDatas[i].stargate).token() != address(0)) revert StargateValueInvalid();
}
else if (IStargate(destDatas[i].stargate).token() != tokens[i]) revert StargateValueInvalid();
}
$.destinationData[tokens[i]] = destDatas[i];
unchecked {
++i;
}
}
emit DestinationDataSet(tokens, destDatas);
}
/**
* @notice Fallback function to receive ETH
* @dev Required to receive fee refunds from Stargate
*/
receive() external payable {}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.2.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
import {IERC1363} from "../../../interfaces/IERC1363.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC-20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
/**
* @dev An operation with an ERC-20 token failed.
*/
error SafeERC20FailedOperation(address token);
/**
* @dev Indicates a failed `decreaseAllowance` request.
*/
error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);
/**
* @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
}
/**
* @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
* calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
*/
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
}
/**
* @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*
* IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
* smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
* this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
* that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
*/
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
forceApprove(token, spender, oldAllowance + value);
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
* value, non-reverting calls are assumed to be successful.
*
* IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
* smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
* this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
* that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
*/
function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
unchecked {
uint256 currentAllowance = token.allowance(address(this), spender);
if (currentAllowance < requestedDecrease) {
revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
}
forceApprove(token, spender, currentAllowance - requestedDecrease);
}
}
/**
* @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
* to be set to zero before setting it to a non-zero value, such as USDT.
*
* NOTE: If the token implements ERC-7674, this function will not modify any temporary allowance. This function
* only sets the "standard" allowance. Any temporary allowance will remain active, in addition to the value being
* set here.
*/
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @dev Performs an {ERC1363} transferAndCall, with a fallback to the simple {ERC20} transfer if the target has no
* code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* Reverts if the returned value is other than `true`.
*/
function transferAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
if (to.code.length == 0) {
safeTransfer(token, to, value);
} else if (!token.transferAndCall(to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Performs an {ERC1363} transferFromAndCall, with a fallback to the simple {ERC20} transferFrom if the target
* has no code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* Reverts if the returned value is other than `true`.
*/
function transferFromAndCallRelaxed(
IERC1363 token,
address from,
address to,
uint256 value,
bytes memory data
) internal {
if (to.code.length == 0) {
safeTransferFrom(token, from, to, value);
} else if (!token.transferFromAndCall(from, to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Performs an {ERC1363} approveAndCall, with a fallback to the simple {ERC20} approve if the target has no
* code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* NOTE: When the recipient address (`to`) has no code (i.e. is an EOA), this function behaves as {forceApprove}.
* Opposedly, when the recipient address (`to`) has code, this function only attempts to call {ERC1363-approveAndCall}
* once without retrying, and relies on the returned value to be true.
*
* Reverts if the returned value is other than `true`.
*/
function approveAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
if (to.code.length == 0) {
forceApprove(token, to, value);
} else if (!token.approveAndCall(to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturnBool} that reverts if call fails to meet the requirements.
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
uint256 returnSize;
uint256 returnValue;
assembly ("memory-safe") {
let success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
// bubble errors
if iszero(success) {
let ptr := mload(0x40)
returndatacopy(ptr, 0, returndatasize())
revert(ptr, returndatasize())
}
returnSize := returndatasize()
returnValue := mload(0)
}
if (returnSize == 0 ? address(token).code.length == 0 : returnValue != 1) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturn} that silently catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
bool success;
uint256 returnSize;
uint256 returnValue;
assembly ("memory-safe") {
success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
returnSize := returndatasize()
returnValue := mload(0)
}
return success && (returnSize == 0 ? address(token).code.length > 0 : returnValue == 1);
}
}// 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);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.28;
// Solidity does not support splitting import across multiple lines
// solhint-disable-next-line max-line-length
import { IOFT, MessagingFee, MessagingReceipt, OFTReceipt, SendParam } from "./IOFT.sol";
/// @notice Stargate implementation type.
enum StargateType {
Pool,
OFT
}
/// @notice Ticket data for bus ride.
struct Ticket {
uint72 ticketId;
bytes passengerBytes;
}
/// @title Interface for Stargate.
/// @notice Defines an API for sending tokens to destination chains.
interface IStargate is IOFT {
/// @dev This function is same as `send` in OFT interface but returns the ticket data if in the bus ride mode,
/// which allows the caller to ride and drive the bus in the same transaction.
function sendToken(SendParam calldata _sendParam, MessagingFee calldata _fee, address _refundAddress) external payable returns (MessagingReceipt memory msgReceipt, OFTReceipt memory oftReceipt, Ticket memory ticket);
/// @notice Returns the Stargate implementation type.
function stargateType() external pure returns (StargateType);
function token() external view returns (address);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
import { IRoleRegistry } from "./IRoleRegistry.sol";
/**
* @title IEtherFiDataProvider
* @author ether.fi
* @notice Interface for the EtherFiDataProvider contract that manages important data for ether.fi
*/
interface IEtherFiDataProvider {
/**
* @notice Configures multiple modules' whitelist status
* @dev Only callable by addresses with ADMIN_ROLE
* @param modules Array of module addresses to configure
* @param shouldWhitelist Array of boolean values indicating whether each module should be whitelisted
*/
function configureModules(address[] calldata modules, bool[] calldata shouldWhitelist) external;
/**
* @notice Updates the hook address
* @dev Only callable by addresses with ADMIN_ROLE
* @param hook New hook address to set
*/
function setHookAddress(address hook) external;
/**
* @notice Updates the address of the Cash Module
* @dev Only callable by addresses with ADMIN_ROLE
* @param cashModule New cash module address to set
*/
function setCashModule(address cashModule) external;
/**
* @notice Checks if a module address is whitelisted
* @param module Address to check
* @return bool True if the module is whitelisted, false otherwise
*/
function isWhitelistedModule(address module) external view returns (bool);
/**
* @notice Checks if a module address is a whitelisted default module
* @param module Address to check
* @return bool True if the module is a whitelisted default module, false otherwise
*/
function isDefaultModule(address module) external view returns (bool);
/**
* @notice Retrieves all whitelisted module addresses
* @return address[] Array of whitelisted module addresses
*/
function getWhitelistedModules() external view returns (address[] memory);
/**
* @notice Returns the address of the Cash Module
* @return Address of the cash module
*/
function getCashModule() external view returns (address);
/**
* @notice Returns the address of the EtherFi Recovery signer
* @return Address of the EtherFi Recovery Signer
*/
function getEtherFiRecoverySigner() external view returns (address);
/**
* @notice Returns the address of the Third Party Recovery signer
* @return Address of the Third Party Recovery Signer
*/
function getThirdPartyRecoverySigner() external view returns (address);
/**
* @notice Returns the address of the Refund wallet
* @return Address of the Refund wallet
*/
function getRefundWallet() external view returns (address);
/**
* @notice Returns the Recovery delay period in seconds
* @return Recovery delay period in seconds
*/
function getRecoveryDelayPeriod() external view returns (uint256);
/**
* @notice Returns the address of the Cash Lens contract
* @return Address of the Cash Lens contract
*/
function getCashLens() external view returns (address);
/**
* @notice Returns the address of the Price provider contract
* @return Address of the Price provider contract
*/
function getPriceProvider() external view returns (address);
/**
* @notice Returns the current hook address
* @return address Current hook address
*/
function getHookAddress() external view returns (address);
function getEtherFiSafeFactory() external view returns (address);
/**
* @notice Function to check if an account is an EtherFiSafe
* @param account Address of the account to check
*/
function isEtherFiSafe(address account) external view returns (bool);
/**
* @notice Role identifier for administrative privileges
* @return bytes32 The keccak256 hash of "ADMIN_ROLE"
*/
function ADMIN_ROLE() external view returns (bytes32);
/**
* @notice Returns the address of the Role Registry contract
* @return roleRegistry Reference to the role registry contract
*/
function roleRegistry() external view returns (IRoleRegistry);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
interface IBoringOnChainQueue {
/**
* @param allowWithdraws Whether or not withdraws are allowed for this asset.
* @param secondsToMaturity The time in seconds it takes for the asset to mature.
* @param minimumSecondsToDeadline The minimum time in seconds a withdraw request must be valid for before it is expired
* @param minDiscount The minimum discount allowed for a withdraw request.
* @param maxDiscount The maximum discount allowed for a withdraw request.
* @param minimumShares The minimum amount of shares that can be withdrawn.
*/
struct WithdrawAsset {
bool allowWithdraws;
uint24 secondsToMaturity;
uint24 minimumSecondsToDeadline;
uint16 minDiscount;
uint16 maxDiscount;
uint96 minimumShares;
}
/**
* @notice Gets the withdrawal config for the asset out
* @param assetOut Address of asset out
* @return WithdrawAsset struct
*/
function withdrawAssets(address assetOut) external view returns (WithdrawAsset memory);
/**
* @notice Returns the boring vault address
*/
function boringVault() external view returns (BoringVault);
/**
* @notice Request an on-chain withdraw.
* @param assetOut The asset to withdraw.
* @param amountOfShares The amount of shares to withdraw.
* @param discount The discount to apply to the withdraw in bps.
* @param secondsToDeadline The time in seconds the request is valid for.
* @return requestId The request Id.
*/
function requestOnChainWithdraw(address assetOut, uint128 amountOfShares, uint16 discount, uint24 secondsToDeadline) external returns (bytes32 requestId);
/**
* @notice Preview assets out from a withdraw request.
* @param assetOut The asset to withdraw.
* @param amountOfShares The amount of shares to withdraw.
* @param discount The discount to apply to the withdraw in bps.
* @return amountOfAssets128 Amount of assets out.
*/
function previewAssetsOut(address assetOut, uint128 amountOfShares, uint16 discount) external view returns (uint128 amountOfAssets128);
}
contract BoringVault {}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
import { EnumerableSetLib } from "solady/utils/EnumerableSetLib.sol";
import { IDebtManager } from "../interfaces/IDebtManager.sol";
import { IEtherFiDataProvider } from "../interfaces/IEtherFiDataProvider.sol";
import { IPriceProvider } from "../interfaces/IPriceProvider.sol";
import { SpendingLimit, SpendingLimitLib } from "../libraries/SpendingLimitLib.sol";
/**
* @title CashbackTypes
* @notice Defines the different cashback types
*/
enum CashbackTypes {
Regular,
Spender,
Promotion,
Referral
}
/**
* @title Cashback
* @notice Defines the structure of Cashback
*/
struct Cashback {
address to;
CashbackTokens[] cashbackTokens;
}
/**
* @title CashbackTokens
* @notice Defines the structure of Cashback tokens
*/
struct CashbackTokens {
address token;
uint256 amountInUsd;
CashbackTypes cashbackType;
}
/**
* @title TokenDataInUsd
* @notice Defines the token data with amount in USD
*/
struct TokenDataInUsd {
address token;
uint256 amountInUsd;
}
/**
* @title BinSponsor
* @notice Defines the bin sponsors or card issuers
*/
enum BinSponsor {
Reap,
Rain
}
/**
* @title Mode
* @notice Defines the operating mode for cash spending operations
* @dev Credit mode borrows tokens, Debit mode uses balance from the safe
*/
enum Mode {
Credit,
Debit
}
/**
* @title WithdrawalRequest
* @notice Structure representing a pending withdrawal of tokens
* @dev Includes tokens, amounts, recipient, and finalization timestamp
*/
struct WithdrawalRequest {
address[] tokens;
uint256[] amounts;
address recipient;
uint96 finalizeTime;
}
/**
* @title SafeData
* @notice View-only representation of a Safe's cash configuration
* @dev Used for returning Safe data in external-facing functions
*/
struct SafeData {
/// @notice Spend limit for the user
SpendingLimit spendingLimit;
/// @notice Impending withdrawal request for the user
WithdrawalRequest pendingWithdrawalRequest;
/// @notice User's chosen mode
Mode mode;
/// @notice Start time for credit mode
uint256 incomingCreditModeStartTime;
/// @notice Running total of all cashback earned by this safe (and its spenders) in USD
uint256 totalCashbackEarnedInUsd;
}
/**
* @notice SafeTiers
* @dev Gets cashback based on the safe tier
*/
enum SafeTiers {
Pepe,
Wojak,
Chad,
Whale,
Business
}
/**
* @title SafeCashConfig
* @notice Complete storage representation of a Safe's cash configuration
* @dev Includes all data needed for managing a Safe's cash operations
*/
struct SafeCashConfig {
/// @notice Spending limit configuration including daily and monthly limits
SpendingLimit spendingLimit;
/// @notice Pending withdrawal request with token addresses, amounts, recipient, and finalization time
WithdrawalRequest pendingWithdrawalRequest;
/// @notice Current operating mode (Credit or Debit) for spending transactions
Mode mode;
/// @notice Timestamp when a pending change to Credit mode will take effect (0 if no pending change)
uint256 incomingCreditModeStartTime;
/// @notice Tier level of the safe that determines cashback percentages
SafeTiers safeTier;
/// @notice Mapping of transaction IDs to cleared status to prevent replay attacks
mapping(bytes32 txId => bool cleared) transactionCleared;
/// @notice Percentage of cashback allocated to the safe vs. the spender (in basis points, 5000 = 50%)
uint256 DEPRECATED_cashbackSplitToSafePercentage;
/// @notice Running total of all cashback earned by this safe (and its spenders) in USD
uint256 totalCashbackEarnedInUsd;
}
/**
* @title SafeCashData
* @notice Comprehensive data structure for front-end display of Safe cash state
* @dev Aggregates data from multiple sources for a complete view of a Safe's financial state
*/
struct SafeCashData {
/// @notice Current operating mode (Credit or Debit)
Mode mode;
/// @notice Array of collateral token balances
IDebtManager.TokenData[] collateralBalances;
/// @notice Array of borrowed token balances
IDebtManager.TokenData[] borrows;
/// @notice Array of token prices
IDebtManager.TokenData[] tokenPrices;
/// @notice Current withdrawal request
WithdrawalRequest withdrawalRequest;
/// @notice Total value of collateral in USD
uint256 totalCollateral;
/// @notice Total value of borrows in USD
uint256 totalBorrow;
/// @notice Maximum borrowing power in USD
uint256 maxBorrow;
/// @notice Maximum spendable amount in Credit mode (USD)
uint256 creditMaxSpend;
/// @notice Remaining spending limit allowance
uint256 spendingLimitAllowance;
/// @notice Running total of all cashback earned by this safe (and its spenders) in USD
uint256 totalCashbackEarnedInUsd;
/// @notice Timestamp when a pending change to Credit mode will take effect (0 if no pending change)
uint256 incomingCreditModeStartTime;
/// @notice Maximum spendable amount in Debit mode
DebitModeMaxSpend debitMaxSpend;
}
/**
* @title DebitModeMaxSpend
* @notice Data structure to return Debit mode max spend result
*/
struct DebitModeMaxSpend {
/// @notice Tokens that can be spent. Order of token matter, Order determines
/// priority for deficit coverage - earlier tokens are used first
address[] spendableTokens;
/// @notice Amounts of respective tokens that can be spent
uint256[] spendableAmounts;
/// @notice Amounts in USD of respective tokens that can be spent
uint256[] amountsInUsd;
/// @notice Total amount in USD that can be spent
uint256 totalSpendableInUsd;
}
interface ICashModule {
/// @notice Error thrown when a transaction has already been cleared
error TransactionAlreadyCleared();
/// @notice Error thrown when a non-EtherFi wallet tries to access restricted functions
error OnlyEtherFiWallet();
/// @notice Error thrown when an unsupported token is used
error UnsupportedToken();
/// @notice Error thrown when a token that is not a borrow token is used in certain operations
error OnlyBorrowToken();
/// @notice Error thrown when an operation amount is zero
error AmountZero();
/// @notice Error thrown when a balance is insufficient for an operation
error InsufficientBalance();
/// @notice Error thrown when borrowings would exceed maximum allowed after a spending operation
error BorrowingsExceedMaxBorrowAfterSpending();
/// @notice Error thrown when a recipient address is zero
error RecipientCannotBeAddressZero();
/// @notice Error thrown when caller doesn't have the Cash Module Controller role
error OnlyCashModuleController();
/// @notice Error thrown when a withdrawal is attempted before the delay period
error CannotWithdrawYet();
/// @notice Error thrown when signature verification fails
error InvalidSignatures();
/// @notice Error thrown when trying to set a mode that's already active
error ModeAlreadySet();
/// @notice Error thrown when a non-DebtManager contract calls restricted functions
error OnlyDebtManager();
/// @notice Error thrown when trying to set a tier that's already set
error AlreadyInSameTier(uint256 index);
/// @notice Error thrown when a cashback percentage exceeds max allowed
error CashbackPercentageGreaterThanMaxAllowed();
/// @notice Error thrown when trying to set a split percentage that's already set
error SplitAlreadyTheSame();
/// @notice Throws when the msg.sender is not an admin to the safe
error OnlySafeAdmin();
/// @notice Thrown when the input is invalid
error InvalidInput();
/// @notice Thrown when the signature verification fails
error InvalidSignature();
/// @notice Thrown when there is an array length mismatch
error ArrayLengthMismatch();
/// @notice Thrown when the caller is not an EtherFi Safe
error OnlyEtherFiSafe();
/// @notice Error thrown when trying to use multiple tokens in credit mode
error OnlyOneTokenAllowedInCreditMode();
/// @notice Error thrown when trying to withdraw an non whitelisted asset
/// @param asset The address of the invalid asset
error InvalidWithdrawAsset(address asset);
/**
* @notice Role identifier for EtherFi wallet access control
* @return The role identifier as bytes32
*/
function ETHER_FI_WALLET_ROLE() external pure returns (bytes32);
/**
* @notice Role identifier for Cash Module controller access
* @return The role identifier as bytes32
*/
function CASH_MODULE_CONTROLLER_ROLE() external pure returns (bytes32);
/**
* @notice Maximum allowed cashback percentage (10%)
* @return The maximum percentage in basis points
*/
function MAX_CASHBACK_PERCENTAGE() external pure returns (uint256);
/**
* @notice Represents 100% in basis points (10,000)
* @return 100% value in basis points
*/
function HUNDRED_PERCENT_IN_BPS() external pure returns (uint256);
/**
* @notice Returns if an asset is a whitelisted withdraw asset
* @param asset Address of the asset
* @return True if asset is whitelisted for withdrawals, false otherwise
*/
function isWhitelistedWithdrawAsset(address asset) external view returns (bool);
/**
* @notice Returns all the assets whitelisted for withdrawals
* @return Array of whitelisted withdraw assets
*/
function getWhitelistedWithdrawAssets() external view returns (address[] memory);
/**
* @notice Retrieves cash configuration data for a Safe
* @param safe Address of the EtherFi Safe
* @return Data structure containing Safe cash configuration
* @custom:throws OnlyEtherFiSafe if the address is not a valid EtherFi Safe
*/
function getData(address safe) external view returns (SafeData memory);
/**
* @notice Checks if a transaction has been cleared
* @param safe Address of the EtherFi Safe
* @param txId Transaction identifier
* @return Boolean indicating if the transaction is cleared
* @custom:throws OnlyEtherFiSafe if the address is not a valid EtherFi Safe
*/
function transactionCleared(address safe, bytes32 txId) external view returns (bool);
/**
* @notice Gets the debt manager contract
* @return IDebtManager instance
*/
function getDebtManager() external view returns (IDebtManager);
/**
* @notice Gets the settlement dispatcher address
* @dev The settlement dispatcher receives the funds that are spent
* @param binSponsor Bin sponsor for which the settlement dispatcher needs to be returned
* @return settlementDispatcher The address of the settlement dispatcher
*/
function getSettlementDispatcher(BinSponsor binSponsor) external view returns (address settlementDispatcher);
/**
* @notice Returns the EtherFiDataProvider contract reference
* @dev Used to access global system configuration and services
* @return The EtherFiDataProvider contract instance
*/
function etherFiDataProvider() external view returns (IEtherFiDataProvider);
/**
* @notice Gets the pending cashback amount for an account in USD
* @dev Returns the amount of cashback waiting to be claimed
* @param account Address of the account (safe or spender)
* @param tokens Addresses of tokens for cashback
* @return data Pending cashback data for tokens in USD
* @return totalCashbackInUsd Total pending cashback amount in USD
*/
function getPendingCashback(address account, address[] memory tokens) external view returns (TokenDataInUsd[] memory data, uint256 totalCashbackInUsd);
/**
* @notice Gets the pending cashback amount for an account in USD for a specific token
* @dev Returns the amount of cashback waiting to be claimed
* @param account Address of the account (safe or spender)
* @param token Address of tokens for cashback
* @return Pending cashback amount in USD for the token
*/
function getPendingCashbackForToken(address account, address token) external view returns (uint256);
/**
* @notice Gets the current delay settings for the module
* @return withdrawalDelay Delay in seconds before a withdrawal can be finalized
* @return spendLimitDelay Delay in seconds before spending limit changes take effect
* @return modeDelay Delay in seconds before a mode change takes effect
*/
function getDelays() external view returns (uint64, uint64, uint64);
/**
* @notice Gets the current operating mode of a safe
* @dev Considers pending mode changes that have passed their delay
* @param safe Address of the EtherFi Safe
* @return The current operating mode (Debit or Credit)
*/
function getMode(address safe) external view returns (Mode);
/**
* @notice Gets the timestamp when a pending credit mode change will take effect
* @dev Returns 0 if no pending change or if the safe uses debit mode
* @param safe Address of the EtherFi Safe
* @return Timestamp when credit mode will take effect, or 0 if not applicable
*/
function incomingCreditModeStartTime(address safe) external view returns (uint256);
/**
* @notice Gets the pending withdrawal amount for a token
* @dev Only callable for valid EtherFi Safe addresses
* @param safe Address of the EtherFi Safe
* @param token Address of the token to check
* @return Amount of tokens pending withdrawal
* @custom:throws OnlyEtherFiSafe if the address is not a valid EtherFi Safe
*/
function getPendingWithdrawalAmount(address safe, address token) external view returns (uint256);
/**
* @notice Sets up a new Safe's Cash Module with initial configuration
* @dev Creates default spending limits and sets initial mode to Debit with 50% cashback split
* @param data The encoded initialization data containing daily limit, monthly limit, and timezone offset
* @custom:throws OnlyEtherFiSafe if the caller is not a valid EtherFi Safe
*/
function setupModule(bytes calldata data) external;
/**
* @notice Initializes the CashModule contract
* @dev Sets up the role registry, debt manager, settlement dispatcher, and data providers
* @param _roleRegistry Address of the role registry contract
* @param _debtManager Address of the debt manager contract
* @param _settlementDispatcherReap Address of the settlement dispatcher for Reap
* @param _settlementDispatcherRain Address of the settlement dispatcher for Rain
* @param _cashbackDispatcher Address of the cashback dispatcher
* @param _cashEventEmitter Address of the cash event emitter
* @param _cashModuleSetters Address of the cash module setters contract
* @custom:throws InvalidInput if any essential address is zero
*/
function initialize(address _roleRegistry, address _debtManager, address _settlementDispatcherReap, address _settlementDispatcherRain, address _cashbackDispatcher, address _cashEventEmitter, address _cashModuleSetters) external;
/**
* @notice Configures the withdraw assets whitelist
* @dev Only callable by accounts with CASH_MODULE_CONTROLLER_ROLE
* @param assets Array of asset addresses to configure
* @param shouldWhitelist Array of boolean suggesting whether to whitelist the assets
* @custom:throws OnlyCashModuleController if the caller does not have CASH_MODULE_CONTROLLER_ROLE role
* @custom:throws InvalidInput If the arrays are empty
* @custom:throws ArrayLengthMismatch If the arrays have different lengths
* @custom:throws InvalidAddress If any address is the zero address
* @custom:throws DuplicateElementFound If any address appears more than once in the addrs array
*/
function configureWithdrawAssets(address[] calldata assets, bool[] calldata shouldWhitelist) external;
/**
* @notice Sets the settlement dispatcher address for a bin sponsor
* @dev Only callable by accounts with CASH_MODULE_CONTROLLER_ROLE
* @param binSponsor Bin sponsor for which the settlement dispatcher is updated
* @param dispatcher Address of the new settlement dispatcher for the bin sponsor
* @custom:throws InvalidInput if caller doesn't have the controller role
*/
function setSettlementDispatcher(BinSponsor binSponsor, address dispatcher) external;
/**
* @notice Sets the tier for one or more safes
* @dev Assigns tiers which determine cashback percentages
* @param safes Array of safe addresses to update
* @param tiers Array of tiers to assign to the corresponding safe addresses
* @custom:throws OnlyEtherFiWallet if caller doesn't have the wallet role
* @custom:throws ArrayLengthMismatch if arrays have different lengths
* @custom:throws OnlyEtherFiSafe if any address is not a valid EtherFi Safe
* @custom:throws AlreadyInSameTier if a safe is already in the specified tier
*/
function setSafeTier(address[] memory safes, SafeTiers[] memory tiers) external;
/**
* @notice Sets the time delays for withdrawals, spending limit changes, and mode changes
* @dev Only callable by accounts with CASH_MODULE_CONTROLLER_ROLE
* @param withdrawalDelay Delay in seconds before a withdrawal can be finalized
* @param spendLimitDelay Delay in seconds before spending limit changes take effect
* @param modeDelay Delay in seconds before a mode change takes effect
* @custom:throws OnlyCashModuleController if caller doesn't have the controller role
*/
function setDelays(uint64 withdrawalDelay, uint64 spendLimitDelay, uint64 modeDelay) external;
/**
* @notice Sets the operating mode for a safe
* @dev Switches between Debit and Credit modes, with possible delay for Credit mode
* @param safe Address of the EtherFi Safe
* @param mode The target mode (Debit or Credit)
* @param signer Address of the safe admin signing the transaction
* @param signature Signature from the signer authorizing this mode change
* @custom:throws OnlyEtherFiSafe if the caller is not a valid EtherFi Safe
* @custom:throws OnlySafeAdmin if signer is not a safe admin
* @custom:throws ModeAlreadySet if the safe is already in the requested mode
* @custom:throws InvalidSignatures if signature verification fails
*/
function setMode(address safe, Mode mode, address signer, bytes calldata signature) external;
/**
* @notice Updates the spending limits for a safe
* @dev Can only be called by the safe itself with a valid admin signature
* @param safe Address of the EtherFi Safe
* @param dailyLimitInUsd New daily spending limit in USD
* @param monthlyLimitInUsd New monthly spending limit in USD
* @param signer Address of the safe admin signing the transaction
* @param signature Signature from the signer authorizing this update
* @custom:throws OnlyEtherFiSafe if the caller is not a valid EtherFi Safe
* @custom:throws OnlySafeAdmin if signer is not a safe admin
* @custom:throws InvalidSignatures if signature verification fails
*/
function updateSpendingLimit(address safe, uint256 dailyLimitInUsd, uint256 monthlyLimitInUsd, address signer, bytes calldata signature) external;
/**
* @notice Processes a spending transaction with multiple tokens
* @dev Only callable by EtherFi wallet for valid EtherFi Safe addresses
* @param safe Address of the EtherFi Safe
* @param txId Transaction identifier
* @param binSponsor Bin sponsor used for spending
* @param tokens Array of addresses of the tokens to spend
* @param amountsInUsd Array of amounts to spend in USD (must match tokens array length)
* @param cashbacks Struct of Cashback to be given
* @custom:throws TransactionAlreadyCleared if the transaction was already processed
* @custom:throws UnsupportedToken if any token is not supported
* @custom:throws AmountZero if any converted amount is zero
* @custom:throws ArrayLengthMismatch if token and amount arrays have different lengths
* @custom:throws OnlyOneTokenAllowedInCreditMode if multiple tokens are used in credit mode
* @custom:throws If spending would exceed limits or balances
*/
function spend(address safe, bytes32 txId, BinSponsor binSponsor, address[] calldata tokens, uint256[] calldata amountsInUsd, Cashback[] calldata cashbacks) external;
/**
* @notice Clears pending cashback for users
* @param users Addresses of users to clear the pending cashback for
* @param tokens Addresses of cashback tokens
*/
function clearPendingCashback(address[] calldata users, address[] calldata tokens) external;
/**
* @notice Repays borrowed tokens
* @dev Only callable by EtherFi wallet for valid EtherFi Safe addresses
* @param safe Address of the EtherFi Safe
* @param token Address of the token to repay
* @param amountInUsd Amount to repay in USD
* @custom:throws OnlyEtherFiWallet if caller doesn't have the wallet role
* @custom:throws OnlyEtherFiSafe if the safe is not a valid EtherFi Safe
* @custom:throws OnlyBorrowToken if token is not a valid borrow token
* @custom:throws AmountZero if the converted amount is zero
* @custom:throws InsufficientBalance if there is not enough balance for the operation
*/
function repay(address safe, address token, uint256 amountInUsd) external;
/**
* @notice Requests a withdrawal of tokens to a recipient
* @dev Creates a pending withdrawal request that can be processed after the delay period
* @dev Can only be done by the quorum of owners of the safe
* @param safe Address of the EtherFi Safe
* @param tokens Array of token addresses to withdraw
* @param amounts Array of token amounts to withdraw
* @param recipient Address to receive the withdrawn tokens
* @param signers Array of safe owner addresses signing the transaction
* @param signatures Array of signatures from the safe owners
* @custom:throws OnlyEtherFiSafe if the caller is not a valid EtherFi Safe
* @custom:throws InvalidSignatures if signature verification fails
* @custom:throws RecipientCannotBeAddressZero if recipient is the zero address
* @custom:throws ArrayLengthMismatch if arrays have different lengths
* @custom:throws InsufficientBalance if any token has insufficient balance
*/
function requestWithdrawal(address safe, address[] calldata tokens, uint256[] calldata amounts, address recipient, address[] calldata signers, bytes[] calldata signatures) external;
/**
* @notice Processes a pending withdrawal request after the delay period
* @dev Executes the token transfers and clears the request
* @param safe Address of the EtherFi Safe
* @custom:throws OnlyEtherFiSafe if the caller is not a valid EtherFi Safe
* @custom:throws CannotWithdrawYet if the withdrawal delay period hasn't passed
*/
function processWithdrawal(address safe) external;
/**
* @notice Prepares a safe for liquidation by canceling any pending withdrawals
* @dev Only callable by the DebtManager
* @param safe Address of the EtherFi Safe being liquidated
* @custom:throws OnlyDebtManager if called by any address other than the DebtManager
*/
function preLiquidate(address safe) external;
/**
* @notice Executes post-liquidation logic to transfer tokens to the liquidator
* @dev Only callable by the DebtManager after a successful liquidation
* @param safe Address of the EtherFi Safe being liquidated
* @param liquidator Address that will receive the liquidated tokens
* @param tokensToSend Array of token data with amounts to send to the liquidator
* @custom:throws OnlyDebtManager if called by any address other than the DebtManager
*/
function postLiquidate(address safe, address liquidator, IDebtManager.LiquidationTokenData[] memory tokensToSend) external;
/**
* @notice Returns the current nonce for a Safe
* @param safe The Safe address to query
* @return Current nonce value
* @dev Nonces are used to prevent signature replay attacks
*/
function getNonce(address safe) external view returns (uint256);
/**
* @notice Sets the new CashModuleSetters implementation address
* @dev Only callable by accounts with CASH_MODULE_CONTROLLER_ROLE
* @param newCashModuleSetters Address of the new CashModuleSetters implementation
* @custom:throws OnlyCashModuleController if caller doesn't have the controller role
* @custom:throws InvalidInput if newCashModuleSetters = address(0)
*/
function setCashModuleSettersAddress(address newCashModuleSetters) external;
/**
* @notice Fetched the safe tier
* @param safe Address of the safe
* @return SafeTiers Tier of the safe
*/
function getSafeTier(address safe) external view returns (SafeTiers);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
struct SendParam {
uint32 dstEid; // Destination endpoint ID.
bytes32 to; // Recipient address.
uint256 amountLD; // Amount to send in local decimals.
uint256 minAmountLD; // Minimum amount to send in local decimals.
bytes extraOptions; // Additional options supplied by the caller to be used in the LayerZero message.
bytes composeMsg; // The composed message for the send() operation.
bytes oftCmd; // The OFT command to be executed, unused in default OFT implementations.
}
struct MessagingParams {
uint32 dstEid;
bytes32 receiver;
bytes message;
bytes options;
bool payInLzToken;
}
struct MessagingReceipt {
bytes32 guid;
uint64 nonce;
MessagingFee fee;
}
struct MessagingFee {
uint256 nativeFee;
uint256 lzTokenFee;
}
/**
* @dev Struct representing OFT limit information.
* @dev These amounts can change dynamically and are up the the specific oft implementation.
*/
struct OFTLimit {
uint256 minAmountLD; // Minimum amount in local decimals that can be sent to the recipient.
uint256 maxAmountLD; // Maximum amount in local decimals that can be sent to the recipient.
}
/**
* @dev Struct representing OFT receipt information.
*/
struct OFTReceipt {
uint256 amountSentLD; // Amount of tokens ACTUALLY debited from the sender in local decimals.
// @dev In non-default implementations, the amountReceivedLD COULD differ from this value.
uint256 amountReceivedLD; // Amount of tokens to be received on the remote side.
}
/**
* @dev Struct representing OFT fee details.
* @dev Future proof mechanism to provide a standardized way to communicate fees to things like a UI.
*/
struct OFTFeeDetail {
int256 feeAmountLD; // Amount of the fee in local decimals.
string description; // Description of the fee.
}
/**
* @title IOFT
* @dev Interface for the OftChain (OFT) token.
* @dev Does not inherit ERC20 to accommodate usage by OFTAdapter as well.
* @dev This specific interface ID is '0x02e49c2c'.
*/
interface IOFT {
// Custom error messages
error InvalidLocalDecimals();
error SlippageExceeded(uint256 amountLD, uint256 minAmountLD);
// Events
event OFTSent( // GUID of the OFT message.
// Destination Endpoint ID.
// Address of the sender on the src chain.
// Amount of tokens sent in local decimals.
// Amount of tokens received in local decimals.
bytes32 indexed guid, uint32 dstEid, address indexed fromAddress, uint256 amountSentLD, uint256 amountReceivedLD);
event OFTReceived( // GUID of the OFT message.
// Source Endpoint ID.
// Address of the recipient on the dst chain.
// Amount of tokens received in local decimals.
bytes32 indexed guid, uint32 srcEid, address indexed toAddress, uint256 amountReceivedLD);
/**
* @notice Retrieves interfaceID and the version of the OFT.
* @return interfaceId The interface ID.
* @return version The version.
*
* @dev interfaceId: This specific interface ID is '0x02e49c2c'.
* @dev version: Indicates a cross-chain compatible msg encoding with other OFTs.
* @dev If a new feature is added to the OFT cross-chain msg encoding, the version will be incremented.
* ie. localOFT version(x,1) CAN send messages to remoteOFT version(x,1)
*/
function oftVersion() external view returns (bytes4 interfaceId, uint64 version);
/**
* @notice Retrieves the address of the token associated with the OFT.
* @return token The address of the ERC20 token implementation.
*/
function token() external view returns (address);
/**
* @notice Indicates whether the OFT contract requires approval of the 'token()' to send.
* @return requiresApproval Needs approval of the underlying token implementation.
*
* @dev Allows things like wallet implementers to determine integration requirements,
* without understanding the underlying token implementation.
*/
function approvalRequired() external view returns (bool);
/**
* @notice Retrieves the shared decimals of the OFT.
* @return sharedDecimals The shared decimals of the OFT.
*/
function sharedDecimals() external view returns (uint8);
/**
* @notice Provides a quote for OFT-related operations.
* @param _sendParam The parameters for the send operation.
* @return limit The OFT limit information.
* @return oftFeeDetails The details of OFT fees.
* @return receipt The OFT receipt information.
*/
function quoteOFT(SendParam calldata _sendParam) external view returns (OFTLimit memory, OFTFeeDetail[] memory oftFeeDetails, OFTReceipt memory);
/**
* @notice Provides a quote for the send() operation.
* @param _sendParam The parameters for the send() operation.
* @param _payInLzToken Flag indicating whether the caller is paying in the LZ token.
* @return fee The calculated LayerZero messaging fee from the send() operation.
*
* @dev MessagingFee: LayerZero msg fee
* - nativeFee: The native fee.
* - lzTokenFee: The lzToken fee.
*/
function quoteSend(SendParam calldata _sendParam, bool _payInLzToken) external view returns (MessagingFee memory);
/**
* @notice Executes the send() operation.
* @param _sendParam The parameters for the send operation.
* @param _fee The fee information supplied by the caller.
* - nativeFee: The native fee.
* - lzTokenFee: The lzToken fee.
* @param _refundAddress The address to receive any excess funds from fees etc. on the src.
* @return receipt The LayerZero messaging receipt from the send() operation.
* @return oftReceipt The OFT receipt information.
*
* @dev MessagingReceipt: LayerZero msg receipt
* - guid: The unique identifier for the sent message.
* - nonce: The nonce of the sent message.
* - fee: The LayerZero fee incurred for the message.
*/
function send(SendParam calldata _sendParam, MessagingFee calldata _fee, address _refundAddress) external payable returns (MessagingReceipt memory, OFTReceipt memory);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
interface IL1MessageQueueV2 {
function estimateCrossDomainMessageFee(uint256 _gasLimit) external view returns (uint256);
}
interface IL1GatewayRouter {
function depositERC20(address _token, address _to, uint256 _amount, uint256 _gasLimit) external payable;
function getERC20Gateway(address _token) external view returns (address);
}
interface IL1ERC20Gateway {
function messenger() external view returns (address);
}
interface IL1ERC20Messenger {
function messageQueueV2() external view returns (address);
}
interface IL2GatewayRouter {
function withdrawERC20(address _token, address _to, uint256 _amount, uint256 _gasLimit) external payable;
function getERC20Gateway(address _token) external view returns (address);
}
interface IL2Messenger {
function sendMessage(address _to, uint256 _value, bytes memory _message, uint256 _gasLimit) external payable;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
import { UUPSUpgradeable } from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import { PausableUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol";
import { ReentrancyGuardTransientUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardTransientUpgradeable.sol";
import { IRoleRegistry } from "../interfaces/IRoleRegistry.sol";
/**
* @title UpgradeableProxy
* @author ether.fi
* @notice An UpgradeableProxy contract which can be upgraded by RoleRegistry contract
*/
contract UpgradeableProxy is UUPSUpgradeable, PausableUpgradeable, ReentrancyGuardTransientUpgradeable {
/// @custom:storage-location erc7201:etherfi.storage.UpgradeableProxy
struct UpgradeableProxyStorage {
/// @notice Reference to the role registry contract for access control
IRoleRegistry roleRegistry;
}
// keccak256(abi.encode(uint256(keccak256("etherfi.storage.UpgradeableProxy")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant UpgradeableProxyStorageLocation = 0xa5586bb7fe6c4d1a576fc53fefe6d5915940638d338769f6905020734977f500;
/// @notice Error thrown when caller is unauthorized to perform an operation
error Unauthorized();
/// @notice Error thrown when caller is not the role registry owner
error OnlyRoleRegistryOwner();
/**
* @notice Returns the address of the Role Registry contract
* @return roleRegistry Reference to the role registry contract
*/
function roleRegistry() public view returns (IRoleRegistry) {
UpgradeableProxyStorage storage $ = _getUpgradeableProxyStorage();
return $.roleRegistry;
}
/**
* @dev Initializes the contract with Role Registry
* @param _roleRegistry Address of the role registry contract
*/
function __UpgradeableProxy_init(address _roleRegistry) internal onlyInitializing {
UpgradeableProxyStorage storage $ = _getUpgradeableProxyStorage();
$.roleRegistry = IRoleRegistry(_roleRegistry);
__ReentrancyGuardTransient_init();
__Pausable_init_unchained();
}
/**
* @dev Returns the storage struct from the specified storage slot
* @return $ Reference to the UpgradeableProxyStorage struct
*/
function _getUpgradeableProxyStorage() internal pure returns (UpgradeableProxyStorage storage $) {
assembly {
$.slot := UpgradeableProxyStorageLocation
}
}
/**
* @dev Updates the role registry contract address
* @param _roleRegistry The address of the new role registry contract
* @custom:security This is a critical function that updates access control
*/
function _setRoleRegistry(address _roleRegistry) internal {
UpgradeableProxyStorage storage $ = _getUpgradeableProxyStorage();
$.roleRegistry = IRoleRegistry(_roleRegistry);
}
/**
* @dev Ensures only authorized upgraders can upgrade the contract
* @param newImplementation Address of the new implementation contract
*/
function _authorizeUpgrade(address newImplementation) internal view override {
UpgradeableProxyStorage storage $ = _getUpgradeableProxyStorage();
$.roleRegistry.onlyUpgrader(msg.sender);
// Silence compiler warning on unused variables.
newImplementation = newImplementation;
}
/**
* @notice Pauses the contract
* @dev Only callable by accounts with the pauser role
*/
function pause() external {
roleRegistry().onlyPauser(msg.sender);
_pause();
}
/**
* @notice Unpauses the contract
* @dev Only callable by accounts with the unpauser role
*/
function unpause() external {
roleRegistry().onlyUnpauser(msg.sender);
_unpause();
}
/**
* @dev Modifier to restrict access to specific roles
* @param role Role identifier
*/
modifier onlyRole(bytes32 role) {
if (!roleRegistry().hasRole(role, msg.sender)) revert Unauthorized();
_;
}
/**
* @dev Modifier to restrict access to owner of the role registry
*/
modifier onlyRoleRegistryOwner() {
if (roleRegistry().owner() != msg.sender) revert OnlyRoleRegistryOwner();
_;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
/**
* @title Constants
* @author ether.fi
* @notice Contract that defines commonly used constants across the ether.fi protocol
* @dev This contract is not meant to be deployed but to be inherited by other contracts
*/
contract Constants {
/**
* @notice Special address used to represent native ETH in the protocol
* @dev This address is used as a marker since ETH is not an ERC20 token
*/
address public constant ETH = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
}// 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);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (interfaces/IERC1363.sol)
pragma solidity ^0.8.20;
import {IERC20} from "./IERC20.sol";
import {IERC165} from "./IERC165.sol";
/**
* @title IERC1363
* @dev Interface of the ERC-1363 standard as defined in the https://eips.ethereum.org/EIPS/eip-1363[ERC-1363].
*
* Defines an extension interface for ERC-20 tokens that supports executing code on a recipient contract
* after `transfer` or `transferFrom`, or code on a spender contract after `approve`, in a single transaction.
*/
interface IERC1363 is IERC20, IERC165 {
/*
* Note: the ERC-165 identifier for this interface is 0xb0202a11.
* 0xb0202a11 ===
* bytes4(keccak256('transferAndCall(address,uint256)')) ^
* bytes4(keccak256('transferAndCall(address,uint256,bytes)')) ^
* bytes4(keccak256('transferFromAndCall(address,address,uint256)')) ^
* bytes4(keccak256('transferFromAndCall(address,address,uint256,bytes)')) ^
* bytes4(keccak256('approveAndCall(address,uint256)')) ^
* bytes4(keccak256('approveAndCall(address,uint256,bytes)'))
*/
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferAndCall(address to, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @param data Additional data with no specified format, sent in call to `to`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param from The address which you want to send tokens from.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferFromAndCall(address from, address to, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param from The address which you want to send tokens from.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @param data Additional data with no specified format, sent in call to `to`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferFromAndCall(address from, address to, uint256 value, bytes calldata data) external returns (bool);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
* @param spender The address which will spend the funds.
* @param value The amount of tokens to be spent.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function approveAndCall(address spender, uint256 value) external returns (bool);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
* @param spender The address which will spend the funds.
* @param value The amount of tokens to be spent.
* @param data Additional data with no specified format, sent in call to `spender`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function approveAndCall(address spender, uint256 value, bytes calldata data) external returns (bool);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
/**
* @title IRoleRegistry
* @notice Interface for role-based access control management
* @dev Provides functions for managing and querying role assignments
*/
interface IRoleRegistry {
/**
* @notice Verifies if an account has pauser privileges
* @param account The address to check for pauser role
* @custom:throws Reverts if account is not an authorized pauser
*/
function onlyPauser(address account) external view;
/**
* @notice Verifies if an account has unpauser privileges
* @param account The address to check for unpauser role
* @custom:throws Reverts if account is not an authorized unpauser
*/
function onlyUnpauser(address account) external view;
/**
* @notice Checks if an account has any of the specified roles
* @dev Reverts if the account doesn't have at least one of the roles
* @param account The address to check roles for
* @param encodedRoles ABI encoded roles using abi.encode(ROLE_1, ROLE_2, ...)
* @custom:throws Reverts if account has none of the specified roles
*/
function checkRoles(address account, bytes memory encodedRoles) external view;
/**
* @notice Checks if an account has a specific role
* @dev Direct query for a single role status
* @param role The role identifier to check
* @param account The address to check the role for
* @return True if the account has the role, false otherwise
*/
function hasRole(bytes32 role, address account) external view returns (bool);
/**
* @notice Grants a role to an account
* @dev Only callable by the contract owner
* @param role The role identifier to grant
* @param account The address to grant the role to
* @custom:access Restricted to contract owner
*/
function grantRole(bytes32 role, address account) external;
/**
* @notice Revokes a role from an account
* @dev Only callable by the contract owner
* @param role The role identifier to revoke
* @param account The address to revoke the role from
* @custom:access Restricted to contract owner
*/
function revokeRole(bytes32 role, address account) external;
/**
* @notice Retrieves all addresses that have a specific role
* @dev Wrapper around EnumerableRoles roleHolders function
* @param role The role identifier to query
* @return Array of addresses that have the specified role
*/
function roleHolders(bytes32 role) external view returns (address[] memory);
/**
* @notice Verifies if an account has upgrader privileges
* @dev Used for upgrade authorization checks
* @param account The address to check for upgrader role
* @custom:throws Reverts if account is not an authorized upgrader
*/
function onlyUpgrader(address account) external view;
/**
* @notice Returns the owner of the contract
* @return result Owner of the contract
*/
function owner() external view returns (address result);
/**
* @notice Generates a unique role identifier for safe administrators
* @dev Creates a unique bytes32 identifier by hashing the safe address with a role type
* @param safe The address of the safe for which to generate the admin role
* @return bytes32 A unique role identifier for the specified safe's admins
* @custom:throws InvalidInput if safe is a zero address
*/
function getSafeAdminRole(address safe) external pure returns (bytes32);
/**
* @notice Configures admin roles for a specific safe
* @dev Grants/revokes admin privileges to specified addresses for a particular safe
* @param accounts Array of admin addresses to configure
* @param shouldAdd Array indicating whether to add or remove each admin
* @custom:throws OnlyEtherFiSafe if called by any address other than a registered EtherFiSafe
* @custom:throws InvalidInput if the admins array is empty or contains a zero address
* @custom:throws ArrayLengthMismatch if the array lengths mismatch
*/
function configureSafeAdmins(address[] calldata accounts, bool[] calldata shouldAdd) external;
/**
* @notice Verifies if an account has safe admin privileges
* @param safe The address of the safe
* @param account The address to check for safe admin role
* @custom:throws OnlySafeAdmin if the account does not have the SafeAdmin role
*/
function onlySafeAdmin(address safe, address account) external view;
/**
* @notice Returns if an account has safe admin privileges
* @param safe The address of the safe
* @param account The address to check for safe admin role
* @return bool suggesting if the account has the safe admin role
*/
function isSafeAdmin(address safe, address account) external view returns (bool);
/**
* @notice Retrieves all addresses that have the safe admin role for a particular safe
* @param safe The address of the safe
* @return Array of addresses that have the safe admin role
*/
function getSafeAdmins(address safe) external view returns (address[] memory);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Library for managing enumerable sets in storage.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/EnumerableSetLib.sol)
///
/// @dev Note:
/// In many applications, the number of elements in an enumerable set is small.
/// This enumerable set implementation avoids storing the length and indices
/// for up to 3 elements. Once the length exceeds 3 for the first time, the length
/// and indices will be initialized. The amortized cost of adding elements is O(1).
///
/// The AddressSet implementation packs the length with the 0th entry.
///
/// All enumerable sets except Uint8Set use a pop and swap mechanism to remove elements.
/// This means that the iteration order of elements can change between element removals.
library EnumerableSetLib {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The index must be less than the length.
error IndexOutOfBounds();
/// @dev The value cannot be the zero sentinel.
error ValueIsZeroSentinel();
/// @dev Cannot accommodate a new unique value with the capacity.
error ExceedsCapacity();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTANTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev A sentinel value to denote the zero value in storage.
/// No elements can be equal to this value.
/// `uint72(bytes9(keccak256(bytes("_ZERO_SENTINEL"))))`.
uint256 private constant _ZERO_SENTINEL = 0xfbb67fda52d4bfb8bf;
/// @dev The storage layout is given by:
/// ```
/// mstore(0x04, _ENUMERABLE_ADDRESS_SET_SLOT_SEED)
/// mstore(0x00, set.slot)
/// let rootSlot := keccak256(0x00, 0x24)
/// mstore(0x20, rootSlot)
/// mstore(0x00, shr(96, shl(96, value)))
/// let positionSlot := keccak256(0x00, 0x40)
/// let valueSlot := add(rootSlot, sload(positionSlot))
/// let valueInStorage := shr(96, sload(valueSlot))
/// let lazyLength := shr(160, shl(160, sload(rootSlot)))
/// ```
uint256 private constant _ENUMERABLE_ADDRESS_SET_SLOT_SEED = 0x978aab92;
/// @dev The storage layout is given by:
/// ```
/// mstore(0x04, _ENUMERABLE_WORD_SET_SLOT_SEED)
/// mstore(0x00, set.slot)
/// let rootSlot := keccak256(0x00, 0x24)
/// mstore(0x20, rootSlot)
/// mstore(0x00, value)
/// let positionSlot := keccak256(0x00, 0x40)
/// let valueSlot := add(rootSlot, sload(positionSlot))
/// let valueInStorage := sload(valueSlot)
/// let lazyLength := sload(not(rootSlot))
/// ```
uint256 private constant _ENUMERABLE_WORD_SET_SLOT_SEED = 0x18fb5864;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* STRUCTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev An enumerable address set in storage.
struct AddressSet {
uint256 _spacer;
}
/// @dev An enumerable bytes32 set in storage.
struct Bytes32Set {
uint256 _spacer;
}
/// @dev An enumerable uint256 set in storage.
struct Uint256Set {
uint256 _spacer;
}
/// @dev An enumerable int256 set in storage.
struct Int256Set {
uint256 _spacer;
}
/// @dev An enumerable uint8 set in storage. Useful for enums.
struct Uint8Set {
uint256 data;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* GETTERS / SETTERS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the number of elements in the set.
function length(AddressSet storage set) internal view returns (uint256 result) {
bytes32 rootSlot = _rootSlot(set);
/// @solidity memory-safe-assembly
assembly {
let rootPacked := sload(rootSlot)
let n := shr(160, shl(160, rootPacked))
result := shr(1, n)
for {} iszero(or(iszero(shr(96, rootPacked)), n)) {} {
result := 1
if iszero(sload(add(rootSlot, result))) { break }
result := 2
if iszero(sload(add(rootSlot, result))) { break }
result := 3
break
}
}
}
/// @dev Returns the number of elements in the set.
function length(Bytes32Set storage set) internal view returns (uint256 result) {
bytes32 rootSlot = _rootSlot(set);
/// @solidity memory-safe-assembly
assembly {
let n := sload(not(rootSlot))
result := shr(1, n)
for {} iszero(n) {} {
result := 0
if iszero(sload(add(rootSlot, result))) { break }
result := 1
if iszero(sload(add(rootSlot, result))) { break }
result := 2
if iszero(sload(add(rootSlot, result))) { break }
result := 3
break
}
}
}
/// @dev Returns the number of elements in the set.
function length(Uint256Set storage set) internal view returns (uint256 result) {
result = length(_toBytes32Set(set));
}
/// @dev Returns the number of elements in the set.
function length(Int256Set storage set) internal view returns (uint256 result) {
result = length(_toBytes32Set(set));
}
/// @dev Returns the number of elements in the set.
function length(Uint8Set storage set) internal view returns (uint256 result) {
/// @solidity memory-safe-assembly
assembly {
for { let packed := sload(set.slot) } packed { result := add(1, result) } {
packed := xor(packed, and(packed, add(1, not(packed))))
}
}
}
/// @dev Returns whether `value` is in the set.
function contains(AddressSet storage set, address value) internal view returns (bool result) {
bytes32 rootSlot = _rootSlot(set);
/// @solidity memory-safe-assembly
assembly {
value := shr(96, shl(96, value))
if eq(value, _ZERO_SENTINEL) {
mstore(0x00, 0xf5a267f1) // `ValueIsZeroSentinel()`.
revert(0x1c, 0x04)
}
if iszero(value) { value := _ZERO_SENTINEL }
let rootPacked := sload(rootSlot)
for {} 1 {} {
if iszero(shr(160, shl(160, rootPacked))) {
result := 1
if eq(shr(96, rootPacked), value) { break }
if eq(shr(96, sload(add(rootSlot, 1))), value) { break }
if eq(shr(96, sload(add(rootSlot, 2))), value) { break }
result := 0
break
}
mstore(0x20, rootSlot)
mstore(0x00, value)
result := iszero(iszero(sload(keccak256(0x00, 0x40))))
break
}
}
}
/// @dev Returns whether `value` is in the set.
function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool result) {
bytes32 rootSlot = _rootSlot(set);
/// @solidity memory-safe-assembly
assembly {
if eq(value, _ZERO_SENTINEL) {
mstore(0x00, 0xf5a267f1) // `ValueIsZeroSentinel()`.
revert(0x1c, 0x04)
}
if iszero(value) { value := _ZERO_SENTINEL }
for {} 1 {} {
if iszero(sload(not(rootSlot))) {
result := 1
if eq(sload(rootSlot), value) { break }
if eq(sload(add(rootSlot, 1)), value) { break }
if eq(sload(add(rootSlot, 2)), value) { break }
result := 0
break
}
mstore(0x20, rootSlot)
mstore(0x00, value)
result := iszero(iszero(sload(keccak256(0x00, 0x40))))
break
}
}
}
/// @dev Returns whether `value` is in the set.
function contains(Uint256Set storage set, uint256 value) internal view returns (bool result) {
result = contains(_toBytes32Set(set), bytes32(value));
}
/// @dev Returns whether `value` is in the set.
function contains(Int256Set storage set, int256 value) internal view returns (bool result) {
result = contains(_toBytes32Set(set), bytes32(uint256(value)));
}
/// @dev Returns whether `value` is in the set.
function contains(Uint8Set storage set, uint8 value) internal view returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
result := and(1, shr(and(0xff, value), sload(set.slot)))
}
}
/// @dev Adds `value` to the set. Returns whether `value` was not in the set.
function add(AddressSet storage set, address value) internal returns (bool result) {
bytes32 rootSlot = _rootSlot(set);
/// @solidity memory-safe-assembly
assembly {
value := shr(96, shl(96, value))
if eq(value, _ZERO_SENTINEL) {
mstore(0x00, 0xf5a267f1) // `ValueIsZeroSentinel()`.
revert(0x1c, 0x04)
}
if iszero(value) { value := _ZERO_SENTINEL }
let rootPacked := sload(rootSlot)
for { let n := shr(160, shl(160, rootPacked)) } 1 {} {
mstore(0x20, rootSlot)
if iszero(n) {
let v0 := shr(96, rootPacked)
if iszero(v0) {
sstore(rootSlot, shl(96, value))
result := 1
break
}
if eq(v0, value) { break }
let v1 := shr(96, sload(add(rootSlot, 1)))
if iszero(v1) {
sstore(add(rootSlot, 1), shl(96, value))
result := 1
break
}
if eq(v1, value) { break }
let v2 := shr(96, sload(add(rootSlot, 2)))
if iszero(v2) {
sstore(add(rootSlot, 2), shl(96, value))
result := 1
break
}
if eq(v2, value) { break }
mstore(0x00, v0)
sstore(keccak256(0x00, 0x40), 1)
mstore(0x00, v1)
sstore(keccak256(0x00, 0x40), 2)
mstore(0x00, v2)
sstore(keccak256(0x00, 0x40), 3)
rootPacked := or(rootPacked, 7)
n := 7
}
mstore(0x00, value)
let p := keccak256(0x00, 0x40)
if iszero(sload(p)) {
n := shr(1, n)
result := 1
sstore(p, add(1, n))
if iszero(n) {
sstore(rootSlot, or(3, shl(96, value)))
break
}
sstore(add(rootSlot, n), shl(96, value))
sstore(rootSlot, add(2, rootPacked))
break
}
break
}
}
}
/// @dev Adds `value` to the set. Returns whether `value` was not in the set.
function add(Bytes32Set storage set, bytes32 value) internal returns (bool result) {
bytes32 rootSlot = _rootSlot(set);
/// @solidity memory-safe-assembly
assembly {
if eq(value, _ZERO_SENTINEL) {
mstore(0x00, 0xf5a267f1) // `ValueIsZeroSentinel()`.
revert(0x1c, 0x04)
}
if iszero(value) { value := _ZERO_SENTINEL }
for { let n := sload(not(rootSlot)) } 1 {} {
mstore(0x20, rootSlot)
if iszero(n) {
let v0 := sload(rootSlot)
if iszero(v0) {
sstore(rootSlot, value)
result := 1
break
}
if eq(v0, value) { break }
let v1 := sload(add(rootSlot, 1))
if iszero(v1) {
sstore(add(rootSlot, 1), value)
result := 1
break
}
if eq(v1, value) { break }
let v2 := sload(add(rootSlot, 2))
if iszero(v2) {
sstore(add(rootSlot, 2), value)
result := 1
break
}
if eq(v2, value) { break }
mstore(0x00, v0)
sstore(keccak256(0x00, 0x40), 1)
mstore(0x00, v1)
sstore(keccak256(0x00, 0x40), 2)
mstore(0x00, v2)
sstore(keccak256(0x00, 0x40), 3)
n := 7
}
mstore(0x00, value)
let p := keccak256(0x00, 0x40)
if iszero(sload(p)) {
n := shr(1, n)
sstore(add(rootSlot, n), value)
sstore(p, add(1, n))
sstore(not(rootSlot), or(1, shl(1, add(1, n))))
result := 1
break
}
break
}
}
}
/// @dev Adds `value` to the set. Returns whether `value` was not in the set.
function add(Uint256Set storage set, uint256 value) internal returns (bool result) {
result = add(_toBytes32Set(set), bytes32(value));
}
/// @dev Adds `value` to the set. Returns whether `value` was not in the set.
function add(Int256Set storage set, int256 value) internal returns (bool result) {
result = add(_toBytes32Set(set), bytes32(uint256(value)));
}
/// @dev Adds `value` to the set. Returns whether `value` was not in the set.
function add(Uint8Set storage set, uint8 value) internal returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
result := sload(set.slot)
let mask := shl(and(0xff, value), 1)
sstore(set.slot, or(result, mask))
result := iszero(and(result, mask))
}
}
/// @dev Adds `value` to the set. Returns whether `value` was not in the set.
/// Reverts if the set grows bigger than the custom on-the-fly capacity `cap`.
function add(AddressSet storage set, address value, uint256 cap)
internal
returns (bool result)
{
if (result = add(set, value)) if (length(set) > cap) revert ExceedsCapacity();
}
/// @dev Adds `value` to the set. Returns whether `value` was not in the set.
/// Reverts if the set grows bigger than the custom on-the-fly capacity `cap`.
function add(Bytes32Set storage set, bytes32 value, uint256 cap)
internal
returns (bool result)
{
if (result = add(set, value)) if (length(set) > cap) revert ExceedsCapacity();
}
/// @dev Adds `value` to the set. Returns whether `value` was not in the set.
/// Reverts if the set grows bigger than the custom on-the-fly capacity `cap`.
function add(Uint256Set storage set, uint256 value, uint256 cap)
internal
returns (bool result)
{
if (result = add(set, value)) if (length(set) > cap) revert ExceedsCapacity();
}
/// @dev Adds `value` to the set. Returns whether `value` was not in the set.
/// Reverts if the set grows bigger than the custom on-the-fly capacity `cap`.
function add(Int256Set storage set, int256 value, uint256 cap) internal returns (bool result) {
if (result = add(set, value)) if (length(set) > cap) revert ExceedsCapacity();
}
/// @dev Adds `value` to the set. Returns whether `value` was not in the set.
/// Reverts if the set grows bigger than the custom on-the-fly capacity `cap`.
function add(Uint8Set storage set, uint8 value, uint256 cap) internal returns (bool result) {
if (result = add(set, value)) if (length(set) > cap) revert ExceedsCapacity();
}
/// @dev Removes `value` from the set. Returns whether `value` was in the set.
function remove(AddressSet storage set, address value) internal returns (bool result) {
bytes32 rootSlot = _rootSlot(set);
/// @solidity memory-safe-assembly
assembly {
value := shr(96, shl(96, value))
if eq(value, _ZERO_SENTINEL) {
mstore(0x00, 0xf5a267f1) // `ValueIsZeroSentinel()`.
revert(0x1c, 0x04)
}
if iszero(value) { value := _ZERO_SENTINEL }
let rootPacked := sload(rootSlot)
for { let n := shr(160, shl(160, rootPacked)) } 1 {} {
if iszero(n) {
result := 1
if eq(shr(96, rootPacked), value) {
sstore(rootSlot, sload(add(rootSlot, 1)))
sstore(add(rootSlot, 1), sload(add(rootSlot, 2)))
sstore(add(rootSlot, 2), 0)
break
}
if eq(shr(96, sload(add(rootSlot, 1))), value) {
sstore(add(rootSlot, 1), sload(add(rootSlot, 2)))
sstore(add(rootSlot, 2), 0)
break
}
if eq(shr(96, sload(add(rootSlot, 2))), value) {
sstore(add(rootSlot, 2), 0)
break
}
result := 0
break
}
mstore(0x20, rootSlot)
mstore(0x00, value)
let p := keccak256(0x00, 0x40)
let position := sload(p)
if iszero(position) { break }
n := sub(shr(1, n), 1)
if iszero(eq(sub(position, 1), n)) {
let lastValue := shr(96, sload(add(rootSlot, n)))
sstore(add(rootSlot, sub(position, 1)), shl(96, lastValue))
mstore(0x00, lastValue)
sstore(keccak256(0x00, 0x40), position)
}
sstore(rootSlot, or(shl(96, shr(96, sload(rootSlot))), or(shl(1, n), 1)))
sstore(p, 0)
result := 1
break
}
}
}
/// @dev Removes `value` from the set. Returns whether `value` was in the set.
function remove(Bytes32Set storage set, bytes32 value) internal returns (bool result) {
bytes32 rootSlot = _rootSlot(set);
/// @solidity memory-safe-assembly
assembly {
if eq(value, _ZERO_SENTINEL) {
mstore(0x00, 0xf5a267f1) // `ValueIsZeroSentinel()`.
revert(0x1c, 0x04)
}
if iszero(value) { value := _ZERO_SENTINEL }
for { let n := sload(not(rootSlot)) } 1 {} {
if iszero(n) {
result := 1
if eq(sload(rootSlot), value) {
sstore(rootSlot, sload(add(rootSlot, 1)))
sstore(add(rootSlot, 1), sload(add(rootSlot, 2)))
sstore(add(rootSlot, 2), 0)
break
}
if eq(sload(add(rootSlot, 1)), value) {
sstore(add(rootSlot, 1), sload(add(rootSlot, 2)))
sstore(add(rootSlot, 2), 0)
break
}
if eq(sload(add(rootSlot, 2)), value) {
sstore(add(rootSlot, 2), 0)
break
}
result := 0
break
}
mstore(0x20, rootSlot)
mstore(0x00, value)
let p := keccak256(0x00, 0x40)
let position := sload(p)
if iszero(position) { break }
n := sub(shr(1, n), 1)
if iszero(eq(sub(position, 1), n)) {
let lastValue := sload(add(rootSlot, n))
sstore(add(rootSlot, sub(position, 1)), lastValue)
mstore(0x00, lastValue)
sstore(keccak256(0x00, 0x40), position)
}
sstore(not(rootSlot), or(shl(1, n), 1))
sstore(p, 0)
result := 1
break
}
}
}
/// @dev Removes `value` from the set. Returns whether `value` was in the set.
function remove(Uint256Set storage set, uint256 value) internal returns (bool result) {
result = remove(_toBytes32Set(set), bytes32(value));
}
/// @dev Removes `value` from the set. Returns whether `value` was in the set.
function remove(Int256Set storage set, int256 value) internal returns (bool result) {
result = remove(_toBytes32Set(set), bytes32(uint256(value)));
}
/// @dev Removes `value` from the set. Returns whether `value` was in the set.
function remove(Uint8Set storage set, uint8 value) internal returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
result := sload(set.slot)
let mask := shl(and(0xff, value), 1)
sstore(set.slot, and(result, not(mask)))
result := iszero(iszero(and(result, mask)))
}
}
/// @dev Shorthand for `isAdd ? set.add(value, cap) : set.remove(value)`.
function update(AddressSet storage set, address value, bool isAdd, uint256 cap)
internal
returns (bool)
{
return isAdd ? add(set, value, cap) : remove(set, value);
}
/// @dev Shorthand for `isAdd ? set.add(value, cap) : set.remove(value)`.
function update(Bytes32Set storage set, bytes32 value, bool isAdd, uint256 cap)
internal
returns (bool)
{
return isAdd ? add(set, value, cap) : remove(set, value);
}
/// @dev Shorthand for `isAdd ? set.add(value, cap) : set.remove(value)`.
function update(Uint256Set storage set, uint256 value, bool isAdd, uint256 cap)
internal
returns (bool)
{
return isAdd ? add(set, value, cap) : remove(set, value);
}
/// @dev Shorthand for `isAdd ? set.add(value, cap) : set.remove(value)`.
function update(Int256Set storage set, int256 value, bool isAdd, uint256 cap)
internal
returns (bool)
{
return isAdd ? add(set, value, cap) : remove(set, value);
}
/// @dev Shorthand for `isAdd ? set.add(value, cap) : set.remove(value)`.
function update(Uint8Set storage set, uint8 value, bool isAdd, uint256 cap)
internal
returns (bool)
{
return isAdd ? add(set, value, cap) : remove(set, value);
}
/// @dev Returns all of the values in the set.
/// Note: This can consume more gas than the block gas limit for large sets.
function values(AddressSet storage set) internal view returns (address[] memory result) {
bytes32 rootSlot = _rootSlot(set);
/// @solidity memory-safe-assembly
assembly {
let zs := _ZERO_SENTINEL
let rootPacked := sload(rootSlot)
let n := shr(160, shl(160, rootPacked))
result := mload(0x40)
let o := add(0x20, result)
let v := shr(96, rootPacked)
mstore(o, mul(v, iszero(eq(v, zs))))
for {} 1 {} {
if iszero(n) {
if v {
n := 1
v := shr(96, sload(add(rootSlot, n)))
if v {
n := 2
mstore(add(o, 0x20), mul(v, iszero(eq(v, zs))))
v := shr(96, sload(add(rootSlot, n)))
if v {
n := 3
mstore(add(o, 0x40), mul(v, iszero(eq(v, zs))))
}
}
}
break
}
n := shr(1, n)
for { let i := 1 } lt(i, n) { i := add(i, 1) } {
v := shr(96, sload(add(rootSlot, i)))
mstore(add(o, shl(5, i)), mul(v, iszero(eq(v, zs))))
}
break
}
mstore(result, n)
mstore(0x40, add(o, shl(5, n)))
}
}
/// @dev Returns all of the values in the set.
/// Note: This can consume more gas than the block gas limit for large sets.
function values(Bytes32Set storage set) internal view returns (bytes32[] memory result) {
bytes32 rootSlot = _rootSlot(set);
/// @solidity memory-safe-assembly
assembly {
let zs := _ZERO_SENTINEL
let n := sload(not(rootSlot))
result := mload(0x40)
let o := add(0x20, result)
for {} 1 {} {
if iszero(n) {
let v := sload(rootSlot)
if v {
n := 1
mstore(o, mul(v, iszero(eq(v, zs))))
v := sload(add(rootSlot, n))
if v {
n := 2
mstore(add(o, 0x20), mul(v, iszero(eq(v, zs))))
v := sload(add(rootSlot, n))
if v {
n := 3
mstore(add(o, 0x40), mul(v, iszero(eq(v, zs))))
}
}
}
break
}
n := shr(1, n)
for { let i := 0 } lt(i, n) { i := add(i, 1) } {
let v := sload(add(rootSlot, i))
mstore(add(o, shl(5, i)), mul(v, iszero(eq(v, zs))))
}
break
}
mstore(result, n)
mstore(0x40, add(o, shl(5, n)))
}
}
/// @dev Returns all of the values in the set.
/// Note: This can consume more gas than the block gas limit for large sets.
function values(Uint256Set storage set) internal view returns (uint256[] memory result) {
result = _toUints(values(_toBytes32Set(set)));
}
/// @dev Returns all of the values in the set.
/// Note: This can consume more gas than the block gas limit for large sets.
function values(Int256Set storage set) internal view returns (int256[] memory result) {
result = _toInts(values(_toBytes32Set(set)));
}
/// @dev Returns all of the values in the set.
function values(Uint8Set storage set) internal view returns (uint8[] memory result) {
/// @solidity memory-safe-assembly
assembly {
result := mload(0x40)
let ptr := add(result, 0x20)
let o := 0
for { let packed := sload(set.slot) } packed {} {
if iszero(and(packed, 0xffff)) {
o := add(o, 16)
packed := shr(16, packed)
continue
}
mstore(ptr, o)
ptr := add(ptr, shl(5, and(packed, 1)))
o := add(o, 1)
packed := shr(1, packed)
}
mstore(result, shr(5, sub(ptr, add(result, 0x20))))
mstore(0x40, ptr)
}
}
/// @dev Returns the element at index `i` in the set. Reverts if `i` is out-of-bounds.
function at(AddressSet storage set, uint256 i) internal view returns (address result) {
bytes32 rootSlot = _rootSlot(set);
/// @solidity memory-safe-assembly
assembly {
result := shr(96, sload(add(rootSlot, i)))
result := mul(result, iszero(eq(result, _ZERO_SENTINEL)))
}
if (i >= length(set)) revert IndexOutOfBounds();
}
/// @dev Returns the element at index `i` in the set. Reverts if `i` is out-of-bounds.
function at(Bytes32Set storage set, uint256 i) internal view returns (bytes32 result) {
result = _rootSlot(set);
/// @solidity memory-safe-assembly
assembly {
result := sload(add(result, i))
result := mul(result, iszero(eq(result, _ZERO_SENTINEL)))
}
if (i >= length(set)) revert IndexOutOfBounds();
}
/// @dev Returns the element at index `i` in the set. Reverts if `i` is out-of-bounds.
function at(Uint256Set storage set, uint256 i) internal view returns (uint256 result) {
result = uint256(at(_toBytes32Set(set), i));
}
/// @dev Returns the element at index `i` in the set. Reverts if `i` is out-of-bounds.
function at(Int256Set storage set, uint256 i) internal view returns (int256 result) {
result = int256(uint256(at(_toBytes32Set(set), i)));
}
/// @dev Returns the element at index `i` in the set. Reverts if `i` is out-of-bounds.
function at(Uint8Set storage set, uint256 i) internal view returns (uint8 result) {
/// @solidity memory-safe-assembly
assembly {
let packed := sload(set.slot)
for {} 1 {
mstore(0x00, 0x4e23d035) // `IndexOutOfBounds()`.
revert(0x1c, 0x04)
} {
if iszero(lt(i, 256)) { continue }
for { let j := 0 } iszero(eq(i, j)) {} {
packed := xor(packed, and(packed, add(1, not(packed))))
j := add(j, 1)
}
if iszero(packed) { continue }
break
}
// Find first set subroutine, optimized for smaller bytecode size.
let x := and(packed, add(1, not(packed)))
let r := shl(7, iszero(iszero(shr(128, x))))
r := or(r, shl(6, iszero(iszero(shr(64, shr(r, x))))))
r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
// For the lower 5 bits of the result, use a De Bruijn lookup.
// forgefmt: disable-next-item
result := or(r, byte(and(div(0xd76453e0, shr(r, x)), 0x1f),
0x001f0d1e100c1d070f090b19131c1706010e11080a1a141802121b1503160405))
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* PRIVATE HELPERS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the root slot.
function _rootSlot(AddressSet storage s) private pure returns (bytes32 r) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x04, _ENUMERABLE_ADDRESS_SET_SLOT_SEED)
mstore(0x00, s.slot)
r := keccak256(0x00, 0x24)
}
}
/// @dev Returns the root slot.
function _rootSlot(Bytes32Set storage s) private pure returns (bytes32 r) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x04, _ENUMERABLE_WORD_SET_SLOT_SEED)
mstore(0x00, s.slot)
r := keccak256(0x00, 0x24)
}
}
/// @dev Casts to a Bytes32Set.
function _toBytes32Set(Uint256Set storage s) private pure returns (Bytes32Set storage c) {
/// @solidity memory-safe-assembly
assembly {
c.slot := s.slot
}
}
/// @dev Casts to a Bytes32Set.
function _toBytes32Set(Int256Set storage s) private pure returns (Bytes32Set storage c) {
/// @solidity memory-safe-assembly
assembly {
c.slot := s.slot
}
}
/// @dev Casts to a uint256 array.
function _toUints(bytes32[] memory a) private pure returns (uint256[] memory c) {
/// @solidity memory-safe-assembly
assembly {
c := a
}
}
/// @dev Casts to a int256 array.
function _toInts(bytes32[] memory a) private pure returns (int256[] memory c) {
/// @solidity memory-safe-assembly
assembly {
c := a
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
import { IEtherFiDataProvider } from "./IEtherFiDataProvider.sol";
import { BinSponsor } from "./ICashModule.sol";
interface IDebtManager {
struct BorrowTokenConfigData {
uint64 borrowApy;
uint128 minShares;
}
struct BorrowTokenConfig {
uint256 interestIndexSnapshot;
uint256 totalBorrowingAmount;
uint256 totalSharesOfBorrowTokens;
uint64 lastUpdateTimestamp;
uint64 borrowApy;
uint128 minShares;
}
struct CollateralTokenConfig {
uint80 ltv;
uint80 liquidationThreshold;
uint96 liquidationBonus;
}
struct TokenData {
address token;
uint256 amount;
}
struct LiquidationTokenData {
address token;
uint256 amount;
uint256 liquidationBonus;
}
event Supplied(address indexed sender, address indexed user, address indexed token, uint256 amount);
event Borrowed(address indexed user, address indexed token, uint256 amount);
event Repaid(address indexed user, address indexed payer, address indexed token, uint256 amount);
event Liquidated(address indexed liquidator, address indexed user, address indexed debtTokenToLiquidate, LiquidationTokenData[] userCollateralLiquidated, uint256 beforeDebtAmount, uint256 debtAmountLiquidated);
event LiquidationThresholdUpdated(uint256 oldThreshold, uint256 newThreshold);
event CollateralTokenAdded(address token);
event CollateralTokenRemoved(address token);
event BorrowTokenAdded(address token);
event BorrowTokenRemoved(address token);
event BorrowApySet(address indexed token, uint256 oldApy, uint256 newApy);
event MinSharesOfBorrowTokenSet(address indexed token, uint128 oldMinShares, uint128 newMinShares);
event UserInterestAdded(address indexed user, uint256 borrowingAmtBeforeInterest, uint256 borrowingAmtAfterInterest);
event TotalBorrowingUpdated(address indexed borrowToken, uint256 totalBorrowingAmtBeforeInterest, uint256 totalBorrowingAmtAfterInterest);
event BorrowTokenConfigSet(address indexed token, BorrowTokenConfig config);
event CollateralTokenConfigSet(address indexed collateralToken, CollateralTokenConfig oldConfig, CollateralTokenConfig newConfig);
event WithdrawBorrowToken(address indexed withdrawer, address indexed borrowToken, uint256 amount);
event InterestIndexUpdated(address indexed borrowToken, uint256 oldIndex, uint256 newIndex);
error CollateralPreferenceIsEmpty();
error UnsupportedCollateralToken();
error UnsupportedRepayToken();
error UnsupportedBorrowToken();
error InsufficientCollateral();
error InsufficientCollateralToRepay();
error InsufficientLiquidity();
error CannotLiquidateYet();
error ZeroCollateralValue();
error OnlyUserCanRepayWithCollateral();
error InvalidValue();
error AlreadyCollateralToken();
error AlreadyBorrowToken();
error NotACollateralToken();
error NoCollateralTokenLeft();
error NotABorrowToken();
error NoBorrowTokenLeft();
error ArrayLengthMismatch();
error TotalCollateralAmountNotZero();
error InsufficientLiquidityPleaseTryAgainLater();
error LiquidAmountLesserThanRequired();
error ZeroTotalBorrowTokens();
error InsufficientBorrowShares();
error UserStillLiquidatable();
error TotalBorrowingsForUserNotZero();
error BorrowTokenConfigAlreadySet();
error AccountUnhealthy();
error BorrowTokenStillInTheSystem();
error RepaymentAmountIsZero();
error LiquidatableAmountIsZero();
error LtvCannotBeGreaterThanLiquidationThreshold();
error OraclePriceZero();
error BorrowAmountZero();
error SharesCannotBeZero();
error SharesCannotBeLessThanMinShares();
error SupplyCapBreached();
error OnlyEtherFiSafe();
error EtherFiSafeCannotSupplyDebtTokens();
error BorrowTokenCannotBeRemovedFromCollateral();
/**
* @notice Function to fetch the admin role
* @return DEBT_MANAGER_ADMIN_ROLE
*/
function DEBT_MANAGER_ADMIN_ROLE() external view returns (bytes32);
/**
* @notice Returns the max borrow apy
* @return Max borrow APY
*/
function MAX_BORROW_APY() external view returns (uint64);
/**
* @notice Returns the address of DebtManagerAdmin implementation
* @return address DebtManagerAdmin implmentaion
*/
function getDebtManagerAdmin() external view returns (address);
/**
* @notice Returns an instance of the EtherFiDataProvider
* @return EtherFiDataProvider instance
*/
function etherFiDataProvider() external view returns (IEtherFiDataProvider);
/**
* @notice Function to fetch the address of the Cash Data Provider.
* @return Cash Data Provider address
*/
function cashDataProvider() external view returns (address);
/**
* @notice Function to fetch the debt interest index snapshot.
* @param borrowToken Address of the borrow token.
* @return debt interest index snapshot
*/
function debtInterestIndexSnapshot(address borrowToken) external view returns (uint256);
/**
* @notice Function to fetch the borrow APY per second with 18 decimals.
* @param borrowToken Address of the borrow token.
* @return Borrow APY per second. Eg: 0.0001% -> 0.0001e18
*/
function borrowApyPerSecond(address borrowToken) external view returns (uint64);
/**
* @notice Function to fetch the min shares of borrow token that can be minted by a supplier.
* @param borrowToken Address of the borrow token.
* @return minShares
*/
function borrowTokenMinShares(address borrowToken) external view returns (uint128);
/**
* @notice Function to fetch the array of collateral tokens.
* @return Array of collateral tokens.
*/
function getCollateralTokens() external view returns (address[] memory);
/**
* @notice Function to fetch the array of borrow tokens.
* @return Array of borrow tokens.
*/
function getBorrowTokens() external view returns (address[] memory);
/**
* @notice Function to check whether a token is a collateral token.
* @return Boolean value suggesting if token is a collateral token.
*/
function isCollateralToken(address token) external view returns (bool);
/**
* @notice Function to check whether a token is a borrow token.
* @return Boolean value suggesting if token is a borrow token.
*/
function isBorrowToken(address token) external view returns (bool);
/**
* @notice Function to add support for a new collateral token.
* @dev Can only be called by an address with the DEBT_MANAGER_ADMIN_ROLE.
* @param token Address of the token to be supported as collateral.
* @param config Collateral token config.
*/
function supportCollateralToken(address token, CollateralTokenConfig memory config) external;
/**
* @notice Function to set the borrow APY per second for a borrow token.
* @dev Can only be called by an address with the DEBT_MANAGER_ADMIN_ROLE.
* @param token Address of the borrow token.
* @param apy Borrow APY per seconds with 18 decimals.
*/
function setBorrowApy(address token, uint64 apy) external;
/**
* @notice Function to set min borrow token shares to mint.
* @notice Implemented to prevent inflation attacks.
* @param token Address of the borrow token.
* @param shares Min shares of that borrow token to mint.
*/
function setMinBorrowTokenShares(address token, uint128 shares) external;
/**
* @notice Function to set the collateral token config.
* @param __collateralToken Address of the collateral token.
* @param __config Collateral token config.
*/
function setCollateralTokenConfig(address __collateralToken, CollateralTokenConfig memory __config) external;
/**
* @notice Function to remove support for a collateral token.
* @dev Can only be called by an address with the DEBT_MANAGER_ADMIN_ROLE.
* @param token Address of the token to be unsupported as collateral.
*/
function unsupportCollateralToken(address token) external;
/**
* @notice Function to add support for a new borrow token.
* @dev Can only be called by an address with the DEBT_MANAGER_ADMIN_ROLE.
* @param token Address of the token to be supported as borrow.
* @param borrowApy Borrow APY per second in 18 decimals.
*/
function supportBorrowToken(address token, uint64 borrowApy, uint128 minBorrowTokenShares) external;
/**
* @notice Function to remove support for a borrow token.
* @dev Can only be called by an address with the DEBT_MANAGER_ADMIN_ROLE.
* @param token Address of the token to be unsupported as borrow.
*/
function unsupportBorrowToken(address token) external;
/**
* @notice Function to ensure that the debt position of the user safe is healthy
* @param user Address of the user safe
*/
function ensureHealth(address user) external view;
/**
* @notice Function to supply borrow tokens to the debt manager.
* @param user Address of the user to register for supply.
* @param borrowToken Address of the borrow token to supply.
* @param amount Amount of the borrow token to supply.
*/
function supply(address user, address borrowToken, uint256 amount) external;
/**
* @notice Function to withdraw the borrow tokens.
* @param borrowToken Address of the borrow token.
* @param amount Amount of the borrow token to withdraw.
*/
function withdrawBorrowToken(address borrowToken, uint256 amount) external;
/**
* @notice Function for users to borrow funds for payment using the deposited collateral.
* @notice Borrowed tokens are transferred to the `etherFiCashSafe`
* @param binSponsor Bin sponsor used to spend.
* @param token Address of the token to borrow.
* @param amount Amount of the token to borrow.
*/
function borrow(BinSponsor binSponsor, address token, uint256 amount) external;
/**
* @notice Function for users to repay the borrowed funds back to the debt manager.
* @param user Address of the user safe for whom the payment is made.
* @param token Address of the token in which repayment is done.
* @param amount Amount of tokens to be repaid.
*/
function repay(address user, address token, uint256 amount) external;
// https://docs.aave.com/faq/liquidations
/**
* @notice Liquidate the user's debt by repaying the partial/entire debt using the collateral.
* @notice If user's 50% debt is repaid and user is healthy, then only 50% will be repaid. Otherwise entire debt is repaid.
* @dev do we need to add penalty?
* @param user Address of the user to liquidate.
* @param borrowToken Borrow token address to liquidate.
* @param collateralTokensPreference Preference of order of collateral tokens to liquidate the user for.
*/
function liquidate(address user, address borrowToken, address[] memory collateralTokensPreference) external;
/**
* @notice Function to determine if a user is liquidatable.
* @param user Address of the user.
* @return isLiquidatable boolean value.
*/
function liquidatable(address user) external view returns (bool isLiquidatable);
/**
* @notice Function to fetch the collateral amount for the user.
* @param user Address of the user.
* @return Array of TokenData struct, total collateral amount in usd.
*/
function collateralOf(address user) external view returns (TokenData[] memory, uint256);
/**
* @notice Function to fetch the borrowing amount of the user for a borrow token.
* @param user Address of the user.
* @param borrowToken Address of the borrow token.
* @return Borrow amount with interest.
*/
function borrowingOf(address user, address borrowToken) external view returns (uint256);
/**
* @notice Function to fetch the borrowing amount of the user for a all the borrow tokens.
* @param user Address of the user.
* @return Array of TokenData struct, total borrow amount in usd.
*/
function borrowingOf(address user) external view returns (TokenData[] memory, uint256);
/**
* @notice Function to fetch the max borrow amount for ltv or liquidation purpose.
* @notice Calculates user's total collateral amount in USD and finds max borrowable amount using liquidation threshold.
* @param user Address of the user.
* @param forLtv For ltv, pass true and for liquidation, pass false.
* @return Max borrow amount for liquidation purpose.
*/
function getMaxBorrowAmount(address user, bool forLtv) external view returns (uint256);
/**
* @notice Function to fetch the max borrow and total current borrowings
* @param user Address of the user safe
* @param tokenAmounts Token amounts of collateral
* @return Total max borrow for that user
* @return Current total borrowings of the user
*/
function getBorrowingPowerAndTotalBorrowing(address user, TokenData[] memory tokenAmounts) external view returns (uint256, uint256);
/**
* @notice Function to determine the current borrowable amount in USD for a user.
* @param user Address of the user.
* @return Current borrowable amount for the user.
*/
function remainingBorrowingCapacityInUSD(address user) external view returns (uint256);
/**
* @notice Function to get the withdrawable amount of borrow tokens for a supplier.
* @param supplier Address of the supplier.
* @param borrowToken Address of the borrow token.
* @return Amount of borrow tokens the supplier can withdraw.
*/
function supplierBalance(address supplier, address borrowToken) external view returns (uint256);
/**
* @notice Function to get the withdrawable amount of borrow tokens for a supplier.
* @param supplier Address of the supplier.
* @return Array of borrow tokens addresses and respective amounts.
* @return Total amount in USD.
*/
function supplierBalance(address supplier) external view returns (TokenData[] memory, uint256);
/**
* @notice Function to fetch the total supplies for a borrow token.
* @param borrowToken Address of the borrow token.
* @return Total amount supplied.
*/
function totalSupplies(address borrowToken) external view returns (uint256);
/**
* @notice Function to fetch the total supplies for each borrow token.
* @return Total amount supplied for each borrow token.
* @return Total amount supplied in USD combined.
*/
function totalSupplies() external view returns (TokenData[] memory, uint256);
/**
* @notice Function to convert collateral token amount to equivalent USD amount.
* @param collateralToken Address of collateral to convert.
* @param collateralAmount Amount of collateral token to convert.
* @return Equivalent USD amount.
*/
function convertCollateralTokenToUsd(address collateralToken, uint256 collateralAmount) external view returns (uint256);
/**
* @notice Function to convert usd amount to collateral token amount.
* @param collateralToken Address of the collateral token.
* @param debtUsdAmount Amount of USD for borrowing.
* @return Amount of collateral required.
*/
function convertUsdToCollateralToken(address collateralToken, uint256 debtUsdAmount) external view returns (uint256);
/**
* @notice Function to fetch the value of collateral deposited by the user in USD.
* @param user Address of the user.
* @return Total collateral value in USD for the user.
*/
function getCollateralValueInUsd(address user) external view returns (uint256);
/**
* @notice Function to fetch the user collateral for a particular token.
* @param user Address of the user.
* @param token Address of the token.
* @return Amount of collateral in tokens.
* @return Amount of collateral in USD.
*/
function getUserCollateralForToken(address user, address token) external view returns (uint256, uint256);
/**
* @notice Function to fetch the total borrowing amount for a token from this contract.
* @param borrowToken Address of the borrow token.
* @return Total borrowing amount in debt token with 6 decimals.
*/
function totalBorrowingAmount(address borrowToken) external view returns (uint256);
/**
* @notice Function to fetch the total borrowing amounts from this contract.
* @return Array of borrow tokens with respective amount in USD.
* @return Total borrowing amount in USD.
*/
function totalBorrowingAmounts() external view returns (TokenData[] memory, uint256);
/**
* @notice Function to fetch the borrow token config.
* @param borrowToken Address of the borrow token.
* @return BorrowTokenConfig struct.
*/
function borrowTokenConfig(address borrowToken) external view returns (BorrowTokenConfig memory);
/**
* @notice Function to fetch the collateral token config.
* @param collateralToken Address of the collateral token.
* @return CollateralTokenConfig.
*/
function collateralTokenConfig(address collateralToken) external view returns (CollateralTokenConfig memory);
/**
* @notice Function to fetch the current state of collaterals and borrowings.
* @return borrowings Array of borrowings in tuple(address token, uint256 amount) format.
* @return totalBorrowingsInUsd Total borrowing value in USD.
* @return totalLiquidStableAmounts Total liquid stable amounts in tuple(address token, uint256 amount) format.
*/
function getCurrentState() external view returns (TokenData[] memory borrowings, uint256 totalBorrowingsInUsd, TokenData[] memory totalLiquidStableAmounts);
/**
* @notice Function to fetch the current state of a user.
* @return totalCollaterals Array of collaterals in tuple(address token, uint256 amount) format.
* @return totalCollateralInUsd Total collateral value in USD.
* @return borrowings Array of borrowings in tuple(address token, uint256 amount) format.
* @return totalBorrowings Total borrowing value in USD.
*/
function getUserCurrentState(address user) external view returns (TokenData[] memory totalCollaterals, uint256 totalCollateralInUsd, TokenData[] memory borrowings, uint256 totalBorrowings);
/**
* @notice Sets a new DebtManagerAdmin implementation
* @dev Can only be called by an address with the DEBT_MANAGER_ADMIN_ROLE.
* @param newImpl Address of the new DebtManagerAdmin implementation
*/
function setAdminImpl(address newImpl) external;
/**
* @notice Calculates the current interest index for a borrow token
* @dev Computes accrued interest based on time elapsed since last update
* @param borrowToken Address of the borrow token
* @return The current interest index including all accrued interest
*/
function getCurrentIndex(address borrowToken) external view returns (uint256);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
interface IPriceProvider {
error UnknownToken();
/**
* @notice Function to get the price of a token in USD
* @return Price with 6 decimals
*/
function price(address token) external view returns (uint256);
/**
* @notice Function to fetch the admin role
* @return PRICE_PROVIDER_ADMIN_ROLE
*/
function PRICE_PROVIDER_ADMIN_ROLE() external view returns (bytes32);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import { Math } from "@openzeppelin/contracts/utils/math/Math.sol";
import { TimeLib } from "./TimeLib.sol";
/**
* @title SpendingLimit
* @notice Data structure for managing daily and monthly spending limits with time-based renewals
* @dev Includes current limits, spent amounts, pending limit changes, and renewal timestamps
*/
struct SpendingLimit {
uint256 dailyLimit; // in USD with 6 decimals
uint256 monthlyLimit; // in USD with 6 decimals
uint256 spentToday; // in USD with 6 decimals
uint256 spentThisMonth; // in USD with 6 decimals
uint256 newDailyLimit; // in USD with 6 decimals
uint256 newMonthlyLimit; // in USD with 6 decimals
uint64 dailyRenewalTimestamp;
uint64 monthlyRenewalTimestamp;
uint64 dailyLimitChangeActivationTime;
uint64 monthlyLimitChangeActivationTime;
int256 timezoneOffset;
}
/**
* @title SpendingLimitLib
* @notice Library for managing spending limits with daily and monthly caps
* @dev Provides functionality for initializing, updating, and enforcing spending limits
* @author ether.fi
*/
library SpendingLimitLib {
using TimeLib for uint256;
using Math for uint256;
/**
* @notice Error thrown when a spend would exceed the daily limit
*/
error ExceededDailySpendingLimit();
/**
* @notice Error thrown when a spend would exceed the monthly limit
*/
error ExceededMonthlySpendingLimit();
/**
* @notice Error thrown when daily limit is set higher than monthly limit
*/
error DailyLimitCannotBeGreaterThanMonthlyLimit();
/**
* @notice Error thrown when timezone offset is invalid
*/
error InvalidTimezoneOffset();
/**
* @notice Initializes a new SpendingLimit with daily and monthly caps
* @dev Sets up initial renewal timestamps based on current time and timezone offset
* @param limit Storage reference to the SpendingLimit to initialize
* @param dailyLimit Maximum amount that can be spent in a day (USD with 6 decimals)
* @param monthlyLimit Maximum amount that can be spent in a month (USD with 6 decimals)
* @param timezoneOffset User's timezone offset in seconds
* @return The initialized SpendingLimit
* @custom:throws DailyLimitCannotBeGreaterThanMonthlyLimit if daily limit exceeds monthly limit
* @custom:throws InvalidTimezoneOffset if timezone offset is outside valid range
*/
function initialize(SpendingLimit storage limit, uint256 dailyLimit, uint256 monthlyLimit, int256 timezoneOffset) internal sanity(dailyLimit, monthlyLimit) returns (SpendingLimit memory) {
if (timezoneOffset > 24 * 60 * 60 || timezoneOffset < -24 * 60 * 60) revert InvalidTimezoneOffset();
limit.dailyLimit = dailyLimit;
limit.monthlyLimit = monthlyLimit;
limit.timezoneOffset = timezoneOffset;
limit.dailyRenewalTimestamp = block.timestamp.getStartOfNextDay(limit.timezoneOffset);
limit.monthlyRenewalTimestamp = block.timestamp.getStartOfNextMonth(limit.timezoneOffset);
return limit;
}
/**
* @notice Updates storage with the current limit state
* @dev Refreshes all fields of the limit struct with current values
* @param limit Storage reference to the SpendingLimit to update
*/
function currentLimit(SpendingLimit storage limit) internal {
SpendingLimit memory finalLimit = getCurrentLimit(limit);
limit.dailyLimit = finalLimit.dailyLimit;
limit.monthlyLimit = finalLimit.monthlyLimit;
limit.spentToday = finalLimit.spentToday;
limit.spentThisMonth = finalLimit.spentThisMonth;
limit.newDailyLimit = finalLimit.newDailyLimit;
limit.newMonthlyLimit = finalLimit.newMonthlyLimit;
limit.dailyRenewalTimestamp = finalLimit.dailyRenewalTimestamp;
limit.monthlyRenewalTimestamp = finalLimit.monthlyRenewalTimestamp;
limit.dailyLimitChangeActivationTime = finalLimit.dailyLimitChangeActivationTime;
limit.monthlyLimitChangeActivationTime = finalLimit.monthlyLimitChangeActivationTime;
}
/**
* @notice Records a spend against the daily and monthly limits
* @dev Updates current limits first, then applies the spend if within limits
* @param limit Storage reference to the SpendingLimit
* @param amount Amount to spend (USD with 6 decimals)
* @custom:throws ExceededDailySpendingLimit if spend would exceed daily limit
* @custom:throws ExceededMonthlySpendingLimit if spend would exceed monthly limit
*/
function spend(SpendingLimit storage limit, uint256 amount) internal {
currentLimit(limit);
if (limit.spentToday + amount > limit.dailyLimit) revert ExceededDailySpendingLimit();
if (limit.spentThisMonth + amount > limit.monthlyLimit) revert ExceededMonthlySpendingLimit();
limit.spentToday += amount;
limit.spentThisMonth += amount;
}
/**
* @notice Updates spending limits with optional delay for decreases
* @dev Immediate increases, delayed decreases with activation timestamp
* @param limit Storage reference to the SpendingLimit
* @param newDailyLimit New daily spending limit (USD with 6 decimals)
* @param newMonthlyLimit New monthly spending limit (USD with 6 decimals)
* @param delay Seconds to delay limit decreases (0 for immediate)
* @return Original limit before changes
* @return Updated limit after changes
* @custom:throws DailyLimitCannotBeGreaterThanMonthlyLimit if daily limit exceeds monthly limit
*/
function updateSpendingLimit(SpendingLimit storage limit, uint256 newDailyLimit, uint256 newMonthlyLimit, uint64 delay) internal sanity(newDailyLimit, newMonthlyLimit) returns (SpendingLimit memory, SpendingLimit memory) {
currentLimit(limit);
SpendingLimit memory oldLimit = limit;
if (newDailyLimit < limit.dailyLimit) {
limit.newDailyLimit = newDailyLimit;
limit.dailyLimitChangeActivationTime = uint64(block.timestamp) + delay;
} else {
limit.dailyLimit = newDailyLimit;
limit.newDailyLimit = 0;
limit.dailyLimitChangeActivationTime = 0;
}
if (newMonthlyLimit < limit.monthlyLimit) {
limit.newMonthlyLimit = newMonthlyLimit;
limit.monthlyLimitChangeActivationTime = uint64(block.timestamp) + delay;
} else {
limit.monthlyLimit = newMonthlyLimit;
limit.newMonthlyLimit = 0;
limit.monthlyLimitChangeActivationTime = 0;
}
return (oldLimit, limit);
}
/**
* @notice Calculates the maximum amount that can be spent right now
* @dev Considers both daily and monthly limits, returning the lower of the two remaining amounts
* @param limit Memory copy of the SpendingLimit
* @return Maximum spendable amount in USD with 6 decimals
*/
function maxCanSpend(SpendingLimit memory limit) internal view returns (uint256) {
limit = getCurrentLimit(limit);
bool usingIncomingDailyLimit = false;
bool usingIncomingMonthlyLimit = false;
uint256 applicableDailyLimit = limit.dailyLimit;
uint256 applicableMonthlyLimit = limit.monthlyLimit;
if (limit.dailyLimitChangeActivationTime != 0) {
applicableDailyLimit = limit.newDailyLimit;
usingIncomingDailyLimit = true;
}
if (limit.monthlyLimitChangeActivationTime != 0) {
applicableMonthlyLimit = limit.newMonthlyLimit;
usingIncomingMonthlyLimit = true;
}
if (limit.spentToday > applicableDailyLimit) return 0;
if (limit.spentThisMonth > applicableMonthlyLimit) return 0;
return Math.min(applicableDailyLimit - limit.spentToday, applicableMonthlyLimit - limit.spentThisMonth);
}
/**
* @notice Checks if a specific amount can be spent
* @dev Considers both daily and monthly limits, including pending limit changes
* @param limit Memory copy of the SpendingLimit
* @param amount Amount to check if spendable (USD with 6 decimals)
* @return canSpend Boolean indicating if the amount can be spent
* @return message Error message if amount cannot be spent
*/
function canSpend(SpendingLimit memory limit, uint256 amount) internal view returns (bool, string memory) {
limit = getCurrentLimit(limit);
bool usingIncomingDailyLimit = false;
bool usingIncomingMonthlyLimit = false;
uint256 applicableDailyLimit = limit.dailyLimit;
uint256 applicableMonthlyLimit = limit.monthlyLimit;
if (limit.dailyLimitChangeActivationTime != 0) {
applicableDailyLimit = limit.newDailyLimit;
usingIncomingDailyLimit = true;
}
if (limit.monthlyLimitChangeActivationTime != 0) {
applicableMonthlyLimit = limit.newMonthlyLimit;
usingIncomingMonthlyLimit = true;
}
if (limit.spentToday > applicableDailyLimit) {
if (usingIncomingDailyLimit) return (false, "Incoming daily spending limit already exhausted");
else return (false, "Daily spending limit already exhausted");
}
if (limit.spentThisMonth > applicableMonthlyLimit) {
if (usingIncomingMonthlyLimit) return (false, "Incoming monthly spending limit already exhausted");
else return (false, "Monthly spending limit already exhausted");
}
uint256 availableDaily = applicableDailyLimit - limit.spentToday;
uint256 availableMonthly = applicableMonthlyLimit - limit.spentThisMonth;
if (amount > availableDaily) {
if (usingIncomingDailyLimit) return (false, "Incoming daily available spending limit less than amount requested");
return (false, "Daily available spending limit less than amount requested");
}
if (amount > availableMonthly) {
if (usingIncomingMonthlyLimit) return (false, "Incoming monthly available spending limit less than amount requested");
return (false, "Monthly available spending limit less than amount requested");
}
return (true, "");
}
/**
* @notice Gets the current limit state with all time-based updates applied
* @dev Applies pending limit changes and resets counters on renewal timestamps
* @param limit Memory copy of the SpendingLimit
* @return Updated SpendingLimit reflecting current state
*/
function getCurrentLimit(SpendingLimit memory limit) internal view returns (SpendingLimit memory) {
if (limit.dailyLimitChangeActivationTime != 0 && block.timestamp > limit.dailyLimitChangeActivationTime) {
limit.dailyLimit = limit.newDailyLimit;
limit.newDailyLimit = 0;
limit.dailyLimitChangeActivationTime = 0;
}
if (limit.monthlyLimitChangeActivationTime != 0 && block.timestamp > limit.monthlyLimitChangeActivationTime) {
limit.monthlyLimit = limit.newMonthlyLimit;
limit.newMonthlyLimit = 0;
limit.monthlyLimitChangeActivationTime = 0;
}
if (block.timestamp > limit.dailyRenewalTimestamp) {
limit.spentToday = 0;
limit.dailyRenewalTimestamp = getFinalDailyRenewalTimestamp(limit.dailyRenewalTimestamp, limit.timezoneOffset);
}
if (block.timestamp > limit.monthlyRenewalTimestamp) {
limit.spentThisMonth = 0;
limit.monthlyRenewalTimestamp = getFinalMonthlyRenewalTimestamp(limit.monthlyRenewalTimestamp, limit.timezoneOffset);
}
return limit;
}
/**
* @notice Calculates the next valid daily renewal timestamp
* @dev Handles cases where multiple renewal periods have elapsed
* @param renewalTimestamp Current renewal timestamp
* @param timezoneOffset User's timezone offset in seconds
* @return Next valid daily renewal timestamp
*/
function getFinalDailyRenewalTimestamp(uint64 renewalTimestamp, int256 timezoneOffset) internal view returns (uint64) {
do {
renewalTimestamp = uint256(renewalTimestamp).getStartOfNextDay(timezoneOffset);
} while (block.timestamp > renewalTimestamp);
return renewalTimestamp;
}
/**
* @notice Calculates the next valid monthly renewal timestamp
* @dev Handles cases where multiple renewal periods have elapsed
* @param renewalTimestamp Current renewal timestamp
* @param timezoneOffset User's timezone offset in seconds
* @return Next valid monthly renewal timestamp
*/
function getFinalMonthlyRenewalTimestamp(uint64 renewalTimestamp, int256 timezoneOffset) internal view returns (uint64) {
do {
renewalTimestamp = uint256(renewalTimestamp).getStartOfNextMonth(timezoneOffset);
} while (block.timestamp > renewalTimestamp);
return renewalTimestamp;
}
/**
* @dev Modifier to ensure daily limit is not greater than monthly limit
* @param dailyLimit Daily spending limit to validate
* @param monthlyLimit Monthly spending limit to validate
* @custom:throws DailyLimitCannotBeGreaterThanMonthlyLimit if daily limit exceeds monthly limit
*/
modifier sanity(uint256 dailyLimit, uint256 monthlyLimit) {
if (dailyLimit > monthlyLimit) revert DailyLimitCannotBeGreaterThanMonthlyLimit();
_;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.2.0) (proxy/utils/UUPSUpgradeable.sol)
pragma solidity ^0.8.22;
import {IERC1822Proxiable} from "@openzeppelin/contracts/interfaces/draft-IERC1822.sol";
import {ERC1967Utils} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol";
import {Initializable} from "./Initializable.sol";
/**
* @dev An upgradeability mechanism designed for UUPS proxies. The functions included here can perform an upgrade of an
* {ERC1967Proxy}, when this contract is set as the implementation behind such a proxy.
*
* A security mechanism ensures that an upgrade does not turn off upgradeability accidentally, although this risk is
* reinstated if the upgrade retains upgradeability but removes the security mechanism, e.g. by replacing
* `UUPSUpgradeable` with a custom implementation of upgrades.
*
* The {_authorizeUpgrade} function must be overridden to include access restriction to the upgrade mechanism.
*/
abstract contract UUPSUpgradeable is Initializable, IERC1822Proxiable {
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable
address private immutable __self = address(this);
/**
* @dev The version of the upgrade interface of the contract. If this getter is missing, both `upgradeTo(address)`
* and `upgradeToAndCall(address,bytes)` are present, and `upgradeTo` must be used if no function should be called,
* while `upgradeToAndCall` will invoke the `receive` function if the second argument is the empty byte string.
* If the getter returns `"5.0.0"`, only `upgradeToAndCall(address,bytes)` is present, and the second argument must
* be the empty byte string if no function should be called, making it impossible to invoke the `receive` function
* during an upgrade.
*/
string public constant UPGRADE_INTERFACE_VERSION = "5.0.0";
/**
* @dev The call is from an unauthorized context.
*/
error UUPSUnauthorizedCallContext();
/**
* @dev The storage `slot` is unsupported as a UUID.
*/
error UUPSUnsupportedProxiableUUID(bytes32 slot);
/**
* @dev Check that the execution is being performed through a delegatecall call and that the execution context is
* a proxy contract with an implementation (as defined in ERC-1967) pointing to self. This should only be the case
* for UUPS and transparent proxies that are using the current contract as their implementation. Execution of a
* function through ERC-1167 minimal proxies (clones) would not normally pass this test, but is not guaranteed to
* fail.
*/
modifier onlyProxy() {
_checkProxy();
_;
}
/**
* @dev Check that the execution is not being performed through a delegate call. This allows a function to be
* callable on the implementing contract but not through proxies.
*/
modifier notDelegated() {
_checkNotDelegated();
_;
}
function __UUPSUpgradeable_init() internal onlyInitializing {
}
function __UUPSUpgradeable_init_unchained() internal onlyInitializing {
}
/**
* @dev Implementation of the ERC-1822 {proxiableUUID} function. This returns the storage slot used by the
* implementation. It is used to validate the implementation's compatibility when performing an upgrade.
*
* IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
* bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
* function revert if invoked through a proxy. This is guaranteed by the `notDelegated` modifier.
*/
function proxiableUUID() external view virtual notDelegated returns (bytes32) {
return ERC1967Utils.IMPLEMENTATION_SLOT;
}
/**
* @dev Upgrade the implementation of the proxy to `newImplementation`, and subsequently execute the function call
* encoded in `data`.
*
* Calls {_authorizeUpgrade}.
*
* Emits an {Upgraded} event.
*
* @custom:oz-upgrades-unsafe-allow-reachable delegatecall
*/
function upgradeToAndCall(address newImplementation, bytes memory data) public payable virtual onlyProxy {
_authorizeUpgrade(newImplementation);
_upgradeToAndCallUUPS(newImplementation, data);
}
/**
* @dev Reverts if the execution is not performed via delegatecall or the execution
* context is not of a proxy with an ERC-1967 compliant implementation pointing to self.
* See {_onlyProxy}.
*/
function _checkProxy() internal view virtual {
if (
address(this) == __self || // Must be called through delegatecall
ERC1967Utils.getImplementation() != __self // Must be called through an active proxy
) {
revert UUPSUnauthorizedCallContext();
}
}
/**
* @dev Reverts if the execution is performed via delegatecall.
* See {notDelegated}.
*/
function _checkNotDelegated() internal view virtual {
if (address(this) != __self) {
// Must not be called through delegatecall
revert UUPSUnauthorizedCallContext();
}
}
/**
* @dev Function that should revert when `msg.sender` is not authorized to upgrade the contract. Called by
* {upgradeToAndCall}.
*
* Normally, this function will use an xref:access.adoc[access control] modifier such as {Ownable-onlyOwner}.
*
* ```solidity
* function _authorizeUpgrade(address) internal onlyOwner {}
* ```
*/
function _authorizeUpgrade(address newImplementation) internal virtual;
/**
* @dev Performs an implementation upgrade with a security check for UUPS proxies, and additional setup call.
*
* As a security check, {proxiableUUID} is invoked in the new implementation, and the return value
* is expected to be the implementation slot in ERC-1967.
*
* Emits an {IERC1967-Upgraded} event.
*/
function _upgradeToAndCallUUPS(address newImplementation, bytes memory data) private {
try IERC1822Proxiable(newImplementation).proxiableUUID() returns (bytes32 slot) {
if (slot != ERC1967Utils.IMPLEMENTATION_SLOT) {
revert UUPSUnsupportedProxiableUUID(slot);
}
ERC1967Utils.upgradeToAndCall(newImplementation, data);
} catch {
// The implementation is not UUPS
revert ERC1967Utils.ERC1967InvalidImplementation(newImplementation);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Pausable.sol)
pragma solidity ^0.8.20;
import {ContextUpgradeable} from "../utils/ContextUpgradeable.sol";
import {Initializable} from "../proxy/utils/Initializable.sol";
/**
* @dev Contract module which allows children to implement an emergency stop
* mechanism that can be triggered by an authorized account.
*
* This module is used through inheritance. It will make available the
* modifiers `whenNotPaused` and `whenPaused`, which can be applied to
* the functions of your contract. Note that they will not be pausable by
* simply including this module, only once the modifiers are put in place.
*/
abstract contract PausableUpgradeable is Initializable, ContextUpgradeable {
/// @custom:storage-location erc7201:openzeppelin.storage.Pausable
struct PausableStorage {
bool _paused;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Pausable")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant PausableStorageLocation = 0xcd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f03300;
function _getPausableStorage() private pure returns (PausableStorage storage $) {
assembly {
$.slot := PausableStorageLocation
}
}
/**
* @dev Emitted when the pause is triggered by `account`.
*/
event Paused(address account);
/**
* @dev Emitted when the pause is lifted by `account`.
*/
event Unpaused(address account);
/**
* @dev The operation failed because the contract is paused.
*/
error EnforcedPause();
/**
* @dev The operation failed because the contract is not paused.
*/
error ExpectedPause();
/**
* @dev Initializes the contract in unpaused state.
*/
function __Pausable_init() internal onlyInitializing {
__Pausable_init_unchained();
}
function __Pausable_init_unchained() internal onlyInitializing {
PausableStorage storage $ = _getPausableStorage();
$._paused = false;
}
/**
* @dev Modifier to make a function callable only when the contract is not paused.
*
* Requirements:
*
* - The contract must not be paused.
*/
modifier whenNotPaused() {
_requireNotPaused();
_;
}
/**
* @dev Modifier to make a function callable only when the contract is paused.
*
* Requirements:
*
* - The contract must be paused.
*/
modifier whenPaused() {
_requirePaused();
_;
}
/**
* @dev Returns true if the contract is paused, and false otherwise.
*/
function paused() public view virtual returns (bool) {
PausableStorage storage $ = _getPausableStorage();
return $._paused;
}
/**
* @dev Throws if the contract is paused.
*/
function _requireNotPaused() internal view virtual {
if (paused()) {
revert EnforcedPause();
}
}
/**
* @dev Throws if the contract is not paused.
*/
function _requirePaused() internal view virtual {
if (!paused()) {
revert ExpectedPause();
}
}
/**
* @dev Triggers stopped state.
*
* Requirements:
*
* - The contract must not be paused.
*/
function _pause() internal virtual whenNotPaused {
PausableStorage storage $ = _getPausableStorage();
$._paused = true;
emit Paused(_msgSender());
}
/**
* @dev Returns to normal state.
*
* Requirements:
*
* - The contract must be paused.
*/
function _unpause() internal virtual whenPaused {
PausableStorage storage $ = _getPausableStorage();
$._paused = false;
emit Unpaused(_msgSender());
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/ReentrancyGuardTransient.sol)
pragma solidity ^0.8.24;
import {TransientSlot} from "@openzeppelin/contracts/utils/TransientSlot.sol";
import {Initializable} from "../proxy/utils/Initializable.sol";
/**
* @dev Variant of {ReentrancyGuard} that uses transient storage.
*
* NOTE: This variant only works on networks where EIP-1153 is available.
*
* _Available since v5.1._
*/
abstract contract ReentrancyGuardTransientUpgradeable is Initializable {
using TransientSlot for *;
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ReentrancyGuard")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant REENTRANCY_GUARD_STORAGE =
0x9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00;
/**
* @dev Unauthorized reentrant call.
*/
error ReentrancyGuardReentrantCall();
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function __ReentrancyGuardTransient_init() internal onlyInitializing {
}
function __ReentrancyGuardTransient_init_unchained() internal onlyInitializing {
}
function _nonReentrantBefore() private {
// On the first call to nonReentrant, _status will be NOT_ENTERED
if (_reentrancyGuardEntered()) {
revert ReentrancyGuardReentrantCall();
}
// Any calls to nonReentrant after this point will fail
REENTRANCY_GUARD_STORAGE.asBoolean().tstore(true);
}
function _nonReentrantAfter() private {
REENTRANCY_GUARD_STORAGE.asBoolean().tstore(false);
}
/**
* @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
* `nonReentrant` function in the call stack.
*/
function _reentrancyGuardEntered() internal view returns (bool) {
return REENTRANCY_GUARD_STORAGE.asBoolean().tload();
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../token/ERC20/IERC20.sol";// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC165.sol)
pragma solidity ^0.8.20;
import {IERC165} from "../utils/introspection/IERC165.sol";// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/math/Math.sol)
pragma solidity ^0.8.20;
import {Panic} from "../Panic.sol";
import {SafeCast} from "./SafeCast.sol";
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
enum Rounding {
Floor, // Toward negative infinity
Ceil, // Toward positive infinity
Trunc, // Toward zero
Expand // Away from zero
}
/**
* @dev Returns the addition of two unsigned integers, with an success flag (no overflow).
*/
function tryAdd(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the subtraction of two unsigned integers, with an success flag (no overflow).
*/
function trySub(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
if (b > a) return (false, 0);
return (true, a - b);
}
}
/**
* @dev Returns the multiplication of two unsigned integers, with an success flag (no overflow).
*/
function tryMul(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the division of two unsigned integers, with a success flag (no division by zero).
*/
function tryDiv(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
if (b == 0) return (false, 0);
return (true, a / b);
}
}
/**
* @dev Returns the remainder of dividing two unsigned integers, with a success flag (no division by zero).
*/
function tryMod(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
if (b == 0) return (false, 0);
return (true, a % b);
}
}
/**
* @dev Branchless ternary evaluation for `a ? b : c`. Gas costs are constant.
*
* IMPORTANT: This function may reduce bytecode size and consume less gas when used standalone.
* However, the compiler may optimize Solidity ternary operations (i.e. `a ? b : c`) to only compute
* one branch when needed, making this function more expensive.
*/
function ternary(bool condition, uint256 a, uint256 b) internal pure returns (uint256) {
unchecked {
// branchless ternary works because:
// b ^ (a ^ b) == a
// b ^ 0 == b
return b ^ ((a ^ b) * SafeCast.toUint(condition));
}
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return ternary(a > b, a, b);
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return ternary(a < b, a, b);
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds towards infinity instead
* of rounding towards zero.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
if (b == 0) {
// Guarantee the same behavior as in a regular Solidity division.
Panic.panic(Panic.DIVISION_BY_ZERO);
}
// The following calculation ensures accurate ceiling division without overflow.
// Since a is non-zero, (a - 1) / b will not overflow.
// The largest possible result occurs when (a - 1) / b is type(uint256).max,
// but the largest value we can obtain is type(uint256).max - 1, which happens
// when a = type(uint256).max and b = 1.
unchecked {
return SafeCast.toUint(a > 0) * ((a - 1) / b + 1);
}
}
/**
* @dev Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
* denominator == 0.
*
* Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by
* Uniswap Labs also under MIT license.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2²⁵⁶ and mod 2²⁵⁶ - 1, then use
// the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2²⁵⁶ + prod0.
uint256 prod0 = x * y; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
// Solidity will revert if denominator == 0, unlike the div opcode on its own.
// The surrounding unchecked block does not change this fact.
// See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
return prod0 / denominator;
}
// Make sure the result is less than 2²⁵⁶. Also prevents denominator == 0.
if (denominator <= prod1) {
Panic.panic(ternary(denominator == 0, Panic.DIVISION_BY_ZERO, Panic.UNDER_OVERFLOW));
}
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator.
// Always >= 1. See https://cs.stackexchange.com/q/138556/92363.
uint256 twos = denominator & (0 - denominator);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2²⁵⁶ / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2²⁵⁶. Now that denominator is an odd number, it has an inverse modulo 2²⁵⁶ such
// that denominator * inv ≡ 1 mod 2²⁵⁶. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv ≡ 1 mod 2⁴.
uint256 inverse = (3 * denominator) ^ 2;
// Use the 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.
inverse *= 2 - denominator * inverse; // inverse mod 2⁸
inverse *= 2 - denominator * inverse; // inverse mod 2¹⁶
inverse *= 2 - denominator * inverse; // inverse mod 2³²
inverse *= 2 - denominator * inverse; // inverse mod 2⁶⁴
inverse *= 2 - denominator * inverse; // inverse mod 2¹²⁸
inverse *= 2 - denominator * inverse; // inverse mod 2²⁵⁶
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2²⁵⁶. Since the preconditions guarantee that the outcome is
// less than 2²⁵⁶, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @dev Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
return mulDiv(x, y, denominator) + SafeCast.toUint(unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0);
}
/**
* @dev Calculate the modular multiplicative inverse of a number in Z/nZ.
*
* If n is a prime, then Z/nZ is a field. In that case all elements are inversible, except 0.
* If n is not a prime, then Z/nZ is not a field, and some elements might not be inversible.
*
* If the input value is not inversible, 0 is returned.
*
* NOTE: If you know for sure that n is (big) a prime, it may be cheaper to use Fermat's little theorem and get the
* inverse using `Math.modExp(a, n - 2, n)`. See {invModPrime}.
*/
function invMod(uint256 a, uint256 n) internal pure returns (uint256) {
unchecked {
if (n == 0) return 0;
// The inverse modulo is calculated using the Extended Euclidean Algorithm (iterative version)
// Used to compute integers x and y such that: ax + ny = gcd(a, n).
// When the gcd is 1, then the inverse of a modulo n exists and it's x.
// ax + ny = 1
// ax = 1 + (-y)n
// ax ≡ 1 (mod n) # x is the inverse of a modulo n
// If the remainder is 0 the gcd is n right away.
uint256 remainder = a % n;
uint256 gcd = n;
// Therefore the initial coefficients are:
// ax + ny = gcd(a, n) = n
// 0a + 1n = n
int256 x = 0;
int256 y = 1;
while (remainder != 0) {
uint256 quotient = gcd / remainder;
(gcd, remainder) = (
// The old remainder is the next gcd to try.
remainder,
// Compute the next remainder.
// Can't overflow given that (a % gcd) * (gcd // (a % gcd)) <= gcd
// where gcd is at most n (capped to type(uint256).max)
gcd - remainder * quotient
);
(x, y) = (
// Increment the coefficient of a.
y,
// Decrement the coefficient of n.
// Can overflow, but the result is casted to uint256 so that the
// next value of y is "wrapped around" to a value between 0 and n - 1.
x - y * int256(quotient)
);
}
if (gcd != 1) return 0; // No inverse exists.
return ternary(x < 0, n - uint256(-x), uint256(x)); // Wrap the result if it's negative.
}
}
/**
* @dev Variant of {invMod}. More efficient, but only works if `p` is known to be a prime greater than `2`.
*
* From https://en.wikipedia.org/wiki/Fermat%27s_little_theorem[Fermat's little theorem], we know that if p is
* prime, then `a**(p-1) ≡ 1 mod p`. As a consequence, we have `a * a**(p-2) ≡ 1 mod p`, which means that
* `a**(p-2)` is the modular multiplicative inverse of a in Fp.
*
* NOTE: this function does NOT check that `p` is a prime greater than `2`.
*/
function invModPrime(uint256 a, uint256 p) internal view returns (uint256) {
unchecked {
return Math.modExp(a, p - 2, p);
}
}
/**
* @dev Returns the modular exponentiation of the specified base, exponent and modulus (b ** e % m)
*
* Requirements:
* - modulus can't be zero
* - underlying staticcall to precompile must succeed
*
* IMPORTANT: The result is only valid if the underlying call succeeds. When using this function, make
* sure the chain you're using it on supports the precompiled contract for modular exponentiation
* at address 0x05 as specified in https://eips.ethereum.org/EIPS/eip-198[EIP-198]. Otherwise,
* the underlying function will succeed given the lack of a revert, but the result may be incorrectly
* interpreted as 0.
*/
function modExp(uint256 b, uint256 e, uint256 m) internal view returns (uint256) {
(bool success, uint256 result) = tryModExp(b, e, m);
if (!success) {
Panic.panic(Panic.DIVISION_BY_ZERO);
}
return result;
}
/**
* @dev Returns the modular exponentiation of the specified base, exponent and modulus (b ** e % m).
* It includes a success flag indicating if the operation succeeded. Operation will be marked as failed if trying
* to operate modulo 0 or if the underlying precompile reverted.
*
* IMPORTANT: The result is only valid if the success flag is true. When using this function, make sure the chain
* you're using it on supports the precompiled contract for modular exponentiation at address 0x05 as specified in
* https://eips.ethereum.org/EIPS/eip-198[EIP-198]. Otherwise, the underlying function will succeed given the lack
* of a revert, but the result may be incorrectly interpreted as 0.
*/
function tryModExp(uint256 b, uint256 e, uint256 m) internal view returns (bool success, uint256 result) {
if (m == 0) return (false, 0);
assembly ("memory-safe") {
let ptr := mload(0x40)
// | Offset | Content | Content (Hex) |
// |-----------|------------|--------------------------------------------------------------------|
// | 0x00:0x1f | size of b | 0x0000000000000000000000000000000000000000000000000000000000000020 |
// | 0x20:0x3f | size of e | 0x0000000000000000000000000000000000000000000000000000000000000020 |
// | 0x40:0x5f | size of m | 0x0000000000000000000000000000000000000000000000000000000000000020 |
// | 0x60:0x7f | value of b | 0x<.............................................................b> |
// | 0x80:0x9f | value of e | 0x<.............................................................e> |
// | 0xa0:0xbf | value of m | 0x<.............................................................m> |
mstore(ptr, 0x20)
mstore(add(ptr, 0x20), 0x20)
mstore(add(ptr, 0x40), 0x20)
mstore(add(ptr, 0x60), b)
mstore(add(ptr, 0x80), e)
mstore(add(ptr, 0xa0), m)
// Given the result < m, it's guaranteed to fit in 32 bytes,
// so we can use the memory scratch space located at offset 0.
success := staticcall(gas(), 0x05, ptr, 0xc0, 0x00, 0x20)
result := mload(0x00)
}
}
/**
* @dev Variant of {modExp} that supports inputs of arbitrary length.
*/
function modExp(bytes memory b, bytes memory e, bytes memory m) internal view returns (bytes memory) {
(bool success, bytes memory result) = tryModExp(b, e, m);
if (!success) {
Panic.panic(Panic.DIVISION_BY_ZERO);
}
return result;
}
/**
* @dev Variant of {tryModExp} that supports inputs of arbitrary length.
*/
function tryModExp(
bytes memory b,
bytes memory e,
bytes memory m
) internal view returns (bool success, bytes memory result) {
if (_zeroBytes(m)) return (false, new bytes(0));
uint256 mLen = m.length;
// Encode call args in result and move the free memory pointer
result = abi.encodePacked(b.length, e.length, mLen, b, e, m);
assembly ("memory-safe") {
let dataPtr := add(result, 0x20)
// Write result on top of args to avoid allocating extra memory.
success := staticcall(gas(), 0x05, dataPtr, mload(result), dataPtr, mLen)
// Overwrite the length.
// result.length > returndatasize() is guaranteed because returndatasize() == m.length
mstore(result, mLen)
// Set the memory pointer after the returned data.
mstore(0x40, add(dataPtr, mLen))
}
}
/**
* @dev Returns whether the provided byte array is zero.
*/
function _zeroBytes(bytes memory byteArray) private pure returns (bool) {
for (uint256 i = 0; i < byteArray.length; ++i) {
if (byteArray[i] != 0) {
return false;
}
}
return true;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
* towards zero.
*
* This method is based on Newton's method for computing square roots; the algorithm is restricted to only
* using integer operations.
*/
function sqrt(uint256 a) internal pure returns (uint256) {
unchecked {
// Take care of easy edge cases when a == 0 or a == 1
if (a <= 1) {
return a;
}
// In this function, we use Newton's method to get a root of `f(x) := x² - a`. It involves building a
// sequence x_n that converges toward sqrt(a). For each iteration x_n, we also define the error between
// the current value as `ε_n = | x_n - sqrt(a) |`.
//
// For our first estimation, we consider `e` the smallest power of 2 which is bigger than the square root
// of the target. (i.e. `2**(e-1) ≤ sqrt(a) < 2**e`). We know that `e ≤ 128` because `(2¹²⁸)² = 2²⁵⁶` is
// bigger than any uint256.
//
// By noticing that
// `2**(e-1) ≤ sqrt(a) < 2**e → (2**(e-1))² ≤ a < (2**e)² → 2**(2*e-2) ≤ a < 2**(2*e)`
// we can deduce that `e - 1` is `log2(a) / 2`. We can thus compute `x_n = 2**(e-1)` using a method similar
// to the msb function.
uint256 aa = a;
uint256 xn = 1;
if (aa >= (1 << 128)) {
aa >>= 128;
xn <<= 64;
}
if (aa >= (1 << 64)) {
aa >>= 64;
xn <<= 32;
}
if (aa >= (1 << 32)) {
aa >>= 32;
xn <<= 16;
}
if (aa >= (1 << 16)) {
aa >>= 16;
xn <<= 8;
}
if (aa >= (1 << 8)) {
aa >>= 8;
xn <<= 4;
}
if (aa >= (1 << 4)) {
aa >>= 4;
xn <<= 2;
}
if (aa >= (1 << 2)) {
xn <<= 1;
}
// We now have x_n such that `x_n = 2**(e-1) ≤ sqrt(a) < 2**e = 2 * x_n`. This implies ε_n ≤ 2**(e-1).
//
// We can refine our estimation by noticing that the middle of that interval minimizes the error.
// If we move x_n to equal 2**(e-1) + 2**(e-2), then we reduce the error to ε_n ≤ 2**(e-2).
// This is going to be our x_0 (and ε_0)
xn = (3 * xn) >> 1; // ε_0 := | x_0 - sqrt(a) | ≤ 2**(e-2)
// From here, Newton's method give us:
// x_{n+1} = (x_n + a / x_n) / 2
//
// One should note that:
// x_{n+1}² - a = ((x_n + a / x_n) / 2)² - a
// = ((x_n² + a) / (2 * x_n))² - a
// = (x_n⁴ + 2 * a * x_n² + a²) / (4 * x_n²) - a
// = (x_n⁴ + 2 * a * x_n² + a² - 4 * a * x_n²) / (4 * x_n²)
// = (x_n⁴ - 2 * a * x_n² + a²) / (4 * x_n²)
// = (x_n² - a)² / (2 * x_n)²
// = ((x_n² - a) / (2 * x_n))²
// ≥ 0
// Which proves that for all n ≥ 1, sqrt(a) ≤ x_n
//
// This gives us the proof of quadratic convergence of the sequence:
// ε_{n+1} = | x_{n+1} - sqrt(a) |
// = | (x_n + a / x_n) / 2 - sqrt(a) |
// = | (x_n² + a - 2*x_n*sqrt(a)) / (2 * x_n) |
// = | (x_n - sqrt(a))² / (2 * x_n) |
// = | ε_n² / (2 * x_n) |
// = ε_n² / | (2 * x_n) |
//
// For the first iteration, we have a special case where x_0 is known:
// ε_1 = ε_0² / | (2 * x_0) |
// ≤ (2**(e-2))² / (2 * (2**(e-1) + 2**(e-2)))
// ≤ 2**(2*e-4) / (3 * 2**(e-1))
// ≤ 2**(e-3) / 3
// ≤ 2**(e-3-log2(3))
// ≤ 2**(e-4.5)
//
// For the following iterations, we use the fact that, 2**(e-1) ≤ sqrt(a) ≤ x_n:
// ε_{n+1} = ε_n² / | (2 * x_n) |
// ≤ (2**(e-k))² / (2 * 2**(e-1))
// ≤ 2**(2*e-2*k) / 2**e
// ≤ 2**(e-2*k)
xn = (xn + a / xn) >> 1; // ε_1 := | x_1 - sqrt(a) | ≤ 2**(e-4.5) -- special case, see above
xn = (xn + a / xn) >> 1; // ε_2 := | x_2 - sqrt(a) | ≤ 2**(e-9) -- general case with k = 4.5
xn = (xn + a / xn) >> 1; // ε_3 := | x_3 - sqrt(a) | ≤ 2**(e-18) -- general case with k = 9
xn = (xn + a / xn) >> 1; // ε_4 := | x_4 - sqrt(a) | ≤ 2**(e-36) -- general case with k = 18
xn = (xn + a / xn) >> 1; // ε_5 := | x_5 - sqrt(a) | ≤ 2**(e-72) -- general case with k = 36
xn = (xn + a / xn) >> 1; // ε_6 := | x_6 - sqrt(a) | ≤ 2**(e-144) -- general case with k = 72
// Because e ≤ 128 (as discussed during the first estimation phase), we know have reached a precision
// ε_6 ≤ 2**(e-144) < 1. Given we're operating on integers, then we can ensure that xn is now either
// sqrt(a) or sqrt(a) + 1.
return xn - SafeCast.toUint(xn > a / xn);
}
}
/**
* @dev Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + SafeCast.toUint(unsignedRoundsUp(rounding) && result * result < a);
}
}
/**
* @dev Return the log in base 2 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
uint256 exp;
unchecked {
exp = 128 * SafeCast.toUint(value > (1 << 128) - 1);
value >>= exp;
result += exp;
exp = 64 * SafeCast.toUint(value > (1 << 64) - 1);
value >>= exp;
result += exp;
exp = 32 * SafeCast.toUint(value > (1 << 32) - 1);
value >>= exp;
result += exp;
exp = 16 * SafeCast.toUint(value > (1 << 16) - 1);
value >>= exp;
result += exp;
exp = 8 * SafeCast.toUint(value > (1 << 8) - 1);
value >>= exp;
result += exp;
exp = 4 * SafeCast.toUint(value > (1 << 4) - 1);
value >>= exp;
result += exp;
exp = 2 * SafeCast.toUint(value > (1 << 2) - 1);
value >>= exp;
result += exp;
result += SafeCast.toUint(value > 1);
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + SafeCast.toUint(unsignedRoundsUp(rounding) && 1 << result < value);
}
}
/**
* @dev Return the log in base 10 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10 ** 64) {
value /= 10 ** 64;
result += 64;
}
if (value >= 10 ** 32) {
value /= 10 ** 32;
result += 32;
}
if (value >= 10 ** 16) {
value /= 10 ** 16;
result += 16;
}
if (value >= 10 ** 8) {
value /= 10 ** 8;
result += 8;
}
if (value >= 10 ** 4) {
value /= 10 ** 4;
result += 4;
}
if (value >= 10 ** 2) {
value /= 10 ** 2;
result += 2;
}
if (value >= 10 ** 1) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + SafeCast.toUint(unsignedRoundsUp(rounding) && 10 ** result < value);
}
}
/**
* @dev Return the log in base 256 of a positive value rounded towards zero.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
uint256 isGt;
unchecked {
isGt = SafeCast.toUint(value > (1 << 128) - 1);
value >>= isGt * 128;
result += isGt * 16;
isGt = SafeCast.toUint(value > (1 << 64) - 1);
value >>= isGt * 64;
result += isGt * 8;
isGt = SafeCast.toUint(value > (1 << 32) - 1);
value >>= isGt * 32;
result += isGt * 4;
isGt = SafeCast.toUint(value > (1 << 16) - 1);
value >>= isGt * 16;
result += isGt * 2;
result += SafeCast.toUint(value > (1 << 8) - 1);
}
return result;
}
/**
* @dev Return the log in base 256, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + SafeCast.toUint(unsignedRoundsUp(rounding) && 1 << (result << 3) < value);
}
}
/**
* @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
*/
function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
return uint8(rounding) % 2 == 1;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
library TimeLib {
// Function to find the timestamp for the start of the next day (00:00 in the user's time zone)
function getStartOfNextDay(uint256 timestamp, int256 timezoneOffset) internal pure returns (uint64) {
// Adjust the timestamp for the time zone offset
int256 adjustedTimestamp = int256(timestamp) + timezoneOffset;
// Calculate the current day in the adjusted time zone
uint256 currentDay = uint256(adjustedTimestamp / 1 days);
// Calculate the start of the next day in the adjusted time zone
uint256 startOfNextDay = (currentDay + 1) * 1 days;
// Adjust the result back to the user's local time zone
return uint64(uint256(int256(startOfNextDay) - timezoneOffset));
}
// Function to find the start of the next week (Monday 00:00 in the user's time zone)
function getStartOfNextWeek(uint256 timestamp, int256 timezoneOffset) internal pure returns (uint64) {
// Adjust the timestamp for the time zone offset
int256 adjustedTimestamp = int256(timestamp) + timezoneOffset;
// Calculate the current day of the week (0 = Monday, 6 = Sunday)
uint256 dayOfWeek = (uint256(adjustedTimestamp) / 1 days + 3) % 7;
// Calculate the number of days until the next Monday
uint256 daysUntilNextMonday = (dayOfWeek == 0) ? 7 : (7 - dayOfWeek);
// Calculate the start of the next week in the adjusted time zone
uint256 startOfNextWeek = ((uint256(adjustedTimestamp) / 1 days) + daysUntilNextMonday) * 1 days;
// Adjust the result back to the user's local time zone
return uint64(uint256(int256(startOfNextWeek) - timezoneOffset));
}
// Function to find the start of the next month (in the user's time zone)
function getStartOfNextMonth(uint256 timestamp, int256 timezoneOffset) internal pure returns (uint64) {
// Adjust the timestamp for the time zone offset
int256 adjustedTimestamp = int256(timestamp) + timezoneOffset;
// Get the current date in the adjusted time zone
(uint16 year, uint8 month,) = _daysToDate(uint256(adjustedTimestamp) / 1 days);
// Increment the month and adjust the year if necessary
month += 1;
if (month > 12) {
month = 1;
year += 1;
}
// Calculate the start of the next month in the adjusted time zone
uint256 startOfNextMonth = _daysFromDate(year, month, 1) * 1 days;
// Adjust the result back to the user's local time zone
return uint64(uint256(int256(startOfNextMonth) - timezoneOffset));
}
// Internal function to calculate days from date
function _daysFromDate(uint16 year, uint8 month, uint8 day) internal pure returns (uint256) {
int256 _year = int256(uint256(year));
int256 _month = int256(uint256(month));
int256 _day = int256(uint256(day));
int256 __days = _day - 32_075 + 1461 * (_year + 4800 + (_month - 14) / 12) / 4 + 367 * (_month - 2 - (_month - 14) / 12 * 12) / 12 - 3 * ((_year + 4900 + (_month - 14) / 12) / 100) / 4 - 2_440_588;
return uint256(__days);
}
// Internal function to convert days to date
function _daysToDate(uint256 _days) internal pure returns (uint16 year, uint8 month, uint8 day) {
int256 __days = int256(_days);
int256 L = __days + 68_569 + 2_440_588;
int256 N = 4 * L / 146_097;
L = L - (146_097 * N + 3) / 4;
int256 _year = 4000 * (L + 1) / 1_461_001;
L = L - 1461 * _year / 4 + 31;
int256 _month = 80 * L / 2447;
int256 _day = L - 2447 * _month / 80;
L = _month / 11;
_month = _month + 2 - 12 * L;
_year = 100 * (N - 49) + _year + L;
year = uint16(uint256(_year));
month = uint8(uint256(_month));
day = uint8(uint256(_day));
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (interfaces/draft-IERC1822.sol)
pragma solidity ^0.8.20;
/**
* @dev ERC-1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified
* proxy whose upgrades are fully controlled by the current implementation.
*/
interface IERC1822Proxiable {
/**
* @dev Returns the storage slot that the proxiable contract assumes is being used to store the implementation
* address.
*
* IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
* bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
* function revert if invoked through a proxy.
*/
function proxiableUUID() external view returns (bytes32);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.2.0) (proxy/ERC1967/ERC1967Utils.sol)
pragma solidity ^0.8.22;
import {IBeacon} from "../beacon/IBeacon.sol";
import {IERC1967} from "../../interfaces/IERC1967.sol";
import {Address} from "../../utils/Address.sol";
import {StorageSlot} from "../../utils/StorageSlot.sol";
/**
* @dev This library provides getters and event emitting update functions for
* https://eips.ethereum.org/EIPS/eip-1967[ERC-1967] slots.
*/
library ERC1967Utils {
/**
* @dev Storage slot with the address of the current implementation.
* This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1.
*/
// solhint-disable-next-line private-vars-leading-underscore
bytes32 internal constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
/**
* @dev The `implementation` of the proxy is invalid.
*/
error ERC1967InvalidImplementation(address implementation);
/**
* @dev The `admin` of the proxy is invalid.
*/
error ERC1967InvalidAdmin(address admin);
/**
* @dev The `beacon` of the proxy is invalid.
*/
error ERC1967InvalidBeacon(address beacon);
/**
* @dev An upgrade function sees `msg.value > 0` that may be lost.
*/
error ERC1967NonPayable();
/**
* @dev Returns the current implementation address.
*/
function getImplementation() internal view returns (address) {
return StorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value;
}
/**
* @dev Stores a new address in the ERC-1967 implementation slot.
*/
function _setImplementation(address newImplementation) private {
if (newImplementation.code.length == 0) {
revert ERC1967InvalidImplementation(newImplementation);
}
StorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value = newImplementation;
}
/**
* @dev Performs implementation upgrade with additional setup call if data is nonempty.
* This function is payable only if the setup call is performed, otherwise `msg.value` is rejected
* to avoid stuck value in the contract.
*
* Emits an {IERC1967-Upgraded} event.
*/
function upgradeToAndCall(address newImplementation, bytes memory data) internal {
_setImplementation(newImplementation);
emit IERC1967.Upgraded(newImplementation);
if (data.length > 0) {
Address.functionDelegateCall(newImplementation, data);
} else {
_checkNonPayable();
}
}
/**
* @dev Storage slot with the admin of the contract.
* This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1.
*/
// solhint-disable-next-line private-vars-leading-underscore
bytes32 internal constant ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
/**
* @dev Returns the current admin.
*
* TIP: To get this value clients can read directly from the storage slot shown below (specified by ERC-1967) using
* the https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
* `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`
*/
function getAdmin() internal view returns (address) {
return StorageSlot.getAddressSlot(ADMIN_SLOT).value;
}
/**
* @dev Stores a new address in the ERC-1967 admin slot.
*/
function _setAdmin(address newAdmin) private {
if (newAdmin == address(0)) {
revert ERC1967InvalidAdmin(address(0));
}
StorageSlot.getAddressSlot(ADMIN_SLOT).value = newAdmin;
}
/**
* @dev Changes the admin of the proxy.
*
* Emits an {IERC1967-AdminChanged} event.
*/
function changeAdmin(address newAdmin) internal {
emit IERC1967.AdminChanged(getAdmin(), newAdmin);
_setAdmin(newAdmin);
}
/**
* @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.
* This is the keccak-256 hash of "eip1967.proxy.beacon" subtracted by 1.
*/
// solhint-disable-next-line private-vars-leading-underscore
bytes32 internal constant BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;
/**
* @dev Returns the current beacon.
*/
function getBeacon() internal view returns (address) {
return StorageSlot.getAddressSlot(BEACON_SLOT).value;
}
/**
* @dev Stores a new beacon in the ERC-1967 beacon slot.
*/
function _setBeacon(address newBeacon) private {
if (newBeacon.code.length == 0) {
revert ERC1967InvalidBeacon(newBeacon);
}
StorageSlot.getAddressSlot(BEACON_SLOT).value = newBeacon;
address beaconImplementation = IBeacon(newBeacon).implementation();
if (beaconImplementation.code.length == 0) {
revert ERC1967InvalidImplementation(beaconImplementation);
}
}
/**
* @dev Change the beacon and trigger a setup call if data is nonempty.
* This function is payable only if the setup call is performed, otherwise `msg.value` is rejected
* to avoid stuck value in the contract.
*
* Emits an {IERC1967-BeaconUpgraded} event.
*
* CAUTION: Invoking this function has no effect on an instance of {BeaconProxy} since v5, since
* it uses an immutable beacon without looking at the value of the ERC-1967 beacon slot for
* efficiency.
*/
function upgradeBeaconToAndCall(address newBeacon, bytes memory data) internal {
_setBeacon(newBeacon);
emit IERC1967.BeaconUpgraded(newBeacon);
if (data.length > 0) {
Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);
} else {
_checkNonPayable();
}
}
/**
* @dev Reverts if `msg.value` is not zero. It can be used to avoid `msg.value` stuck in the contract
* if an upgrade doesn't perform an initialization call.
*/
function _checkNonPayable() private {
if (msg.value > 0) {
revert ERC1967NonPayable();
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/Initializable.sol)
pragma solidity ^0.8.20;
/**
* @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
* behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
* external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
* function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
*
* The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
* reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
* case an upgrade adds a module that needs to be initialized.
*
* For example:
*
* [.hljs-theme-light.nopadding]
* ```solidity
* contract MyToken is ERC20Upgradeable {
* function initialize() initializer public {
* __ERC20_init("MyToken", "MTK");
* }
* }
*
* contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
* function initializeV2() reinitializer(2) public {
* __ERC20Permit_init("MyToken");
* }
* }
* ```
*
* TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
* possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
*
* CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
* that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
*
* [CAUTION]
* ====
* Avoid leaving a contract uninitialized.
*
* An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
* contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
* the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
*
* [.hljs-theme-light.nopadding]
* ```
* /// @custom:oz-upgrades-unsafe-allow constructor
* constructor() {
* _disableInitializers();
* }
* ```
* ====
*/
abstract contract Initializable {
/**
* @dev Storage of the initializable contract.
*
* It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions
* when using with upgradeable contracts.
*
* @custom:storage-location erc7201:openzeppelin.storage.Initializable
*/
struct InitializableStorage {
/**
* @dev Indicates that the contract has been initialized.
*/
uint64 _initialized;
/**
* @dev Indicates that the contract is in the process of being initialized.
*/
bool _initializing;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00;
/**
* @dev The contract is already initialized.
*/
error InvalidInitialization();
/**
* @dev The contract is not initializing.
*/
error NotInitializing();
/**
* @dev Triggered when the contract has been initialized or reinitialized.
*/
event Initialized(uint64 version);
/**
* @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
* `onlyInitializing` functions can be used to initialize parent contracts.
*
* Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any
* number of times. This behavior in the constructor can be useful during testing and is not expected to be used in
* production.
*
* Emits an {Initialized} event.
*/
modifier initializer() {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
// Cache values to avoid duplicated sloads
bool isTopLevelCall = !$._initializing;
uint64 initialized = $._initialized;
// Allowed calls:
// - initialSetup: the contract is not in the initializing state and no previous version was
// initialized
// - construction: the contract is initialized at version 1 (no reininitialization) and the
// current contract is just being deployed
bool initialSetup = initialized == 0 && isTopLevelCall;
bool construction = initialized == 1 && address(this).code.length == 0;
if (!initialSetup && !construction) {
revert InvalidInitialization();
}
$._initialized = 1;
if (isTopLevelCall) {
$._initializing = true;
}
_;
if (isTopLevelCall) {
$._initializing = false;
emit Initialized(1);
}
}
/**
* @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
* contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
* used to initialize parent contracts.
*
* A reinitializer may be used after the original initialization step. This is essential to configure modules that
* are added through upgrades and that require initialization.
*
* When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
* cannot be nested. If one is invoked in the context of another, execution will revert.
*
* Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
* a contract, executing them in the right order is up to the developer or operator.
*
* WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization.
*
* Emits an {Initialized} event.
*/
modifier reinitializer(uint64 version) {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
if ($._initializing || $._initialized >= version) {
revert InvalidInitialization();
}
$._initialized = version;
$._initializing = true;
_;
$._initializing = false;
emit Initialized(version);
}
/**
* @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
* {initializer} and {reinitializer} modifiers, directly or indirectly.
*/
modifier onlyInitializing() {
_checkInitializing();
_;
}
/**
* @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}.
*/
function _checkInitializing() internal view virtual {
if (!_isInitializing()) {
revert NotInitializing();
}
}
/**
* @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
* Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
* to any version. It is recommended to use this to lock implementation contracts that are designed to be called
* through proxies.
*
* Emits an {Initialized} event the first time it is successfully executed.
*/
function _disableInitializers() internal virtual {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
if ($._initializing) {
revert InvalidInitialization();
}
if ($._initialized != type(uint64).max) {
$._initialized = type(uint64).max;
emit Initialized(type(uint64).max);
}
}
/**
* @dev Returns the highest version that has been initialized. See {reinitializer}.
*/
function _getInitializedVersion() internal view returns (uint64) {
return _getInitializableStorage()._initialized;
}
/**
* @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
*/
function _isInitializing() internal view returns (bool) {
return _getInitializableStorage()._initializing;
}
/**
* @dev Returns a pointer to the storage namespace.
*/
// solhint-disable-next-line var-name-mixedcase
function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
assembly {
$.slot := INITIALIZABLE_STORAGE
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
import {Initializable} from "../proxy/utils/Initializable.sol";
/**
* @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 ContextUpgradeable is Initializable {
function __Context_init() internal onlyInitializing {
}
function __Context_init_unchained() internal onlyInitializing {
}
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;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/TransientSlot.sol)
// This file was procedurally generated from scripts/generate/templates/TransientSlot.js.
pragma solidity ^0.8.24;
/**
* @dev Library for reading and writing value-types to specific transient storage slots.
*
* Transient slots are often used to store temporary values that are removed after the current transaction.
* This library helps with reading and writing to such slots without the need for inline assembly.
*
* * Example reading and writing values using transient storage:
* ```solidity
* contract Lock {
* using TransientSlot for *;
*
* // Define the slot. Alternatively, use the SlotDerivation library to derive the slot.
* bytes32 internal constant _LOCK_SLOT = 0xf4678858b2b588224636b8522b729e7722d32fc491da849ed75b3fdf3c84f542;
*
* modifier locked() {
* require(!_LOCK_SLOT.asBoolean().tload());
*
* _LOCK_SLOT.asBoolean().tstore(true);
* _;
* _LOCK_SLOT.asBoolean().tstore(false);
* }
* }
* ```
*
* TIP: Consider using this library along with {SlotDerivation}.
*/
library TransientSlot {
/**
* @dev UDVT that represent a slot holding a address.
*/
type AddressSlot is bytes32;
/**
* @dev Cast an arbitrary slot to a AddressSlot.
*/
function asAddress(bytes32 slot) internal pure returns (AddressSlot) {
return AddressSlot.wrap(slot);
}
/**
* @dev UDVT that represent a slot holding a bool.
*/
type BooleanSlot is bytes32;
/**
* @dev Cast an arbitrary slot to a BooleanSlot.
*/
function asBoolean(bytes32 slot) internal pure returns (BooleanSlot) {
return BooleanSlot.wrap(slot);
}
/**
* @dev UDVT that represent a slot holding a bytes32.
*/
type Bytes32Slot is bytes32;
/**
* @dev Cast an arbitrary slot to a Bytes32Slot.
*/
function asBytes32(bytes32 slot) internal pure returns (Bytes32Slot) {
return Bytes32Slot.wrap(slot);
}
/**
* @dev UDVT that represent a slot holding a uint256.
*/
type Uint256Slot is bytes32;
/**
* @dev Cast an arbitrary slot to a Uint256Slot.
*/
function asUint256(bytes32 slot) internal pure returns (Uint256Slot) {
return Uint256Slot.wrap(slot);
}
/**
* @dev UDVT that represent a slot holding a int256.
*/
type Int256Slot is bytes32;
/**
* @dev Cast an arbitrary slot to a Int256Slot.
*/
function asInt256(bytes32 slot) internal pure returns (Int256Slot) {
return Int256Slot.wrap(slot);
}
/**
* @dev Load the value held at location `slot` in transient storage.
*/
function tload(AddressSlot slot) internal view returns (address value) {
assembly ("memory-safe") {
value := tload(slot)
}
}
/**
* @dev Store `value` at location `slot` in transient storage.
*/
function tstore(AddressSlot slot, address value) internal {
assembly ("memory-safe") {
tstore(slot, value)
}
}
/**
* @dev Load the value held at location `slot` in transient storage.
*/
function tload(BooleanSlot slot) internal view returns (bool value) {
assembly ("memory-safe") {
value := tload(slot)
}
}
/**
* @dev Store `value` at location `slot` in transient storage.
*/
function tstore(BooleanSlot slot, bool value) internal {
assembly ("memory-safe") {
tstore(slot, value)
}
}
/**
* @dev Load the value held at location `slot` in transient storage.
*/
function tload(Bytes32Slot slot) internal view returns (bytes32 value) {
assembly ("memory-safe") {
value := tload(slot)
}
}
/**
* @dev Store `value` at location `slot` in transient storage.
*/
function tstore(Bytes32Slot slot, bytes32 value) internal {
assembly ("memory-safe") {
tstore(slot, value)
}
}
/**
* @dev Load the value held at location `slot` in transient storage.
*/
function tload(Uint256Slot slot) internal view returns (uint256 value) {
assembly ("memory-safe") {
value := tload(slot)
}
}
/**
* @dev Store `value` at location `slot` in transient storage.
*/
function tstore(Uint256Slot slot, uint256 value) internal {
assembly ("memory-safe") {
tstore(slot, value)
}
}
/**
* @dev Load the value held at location `slot` in transient storage.
*/
function tload(Int256Slot slot) internal view returns (int256 value) {
assembly ("memory-safe") {
value := tload(slot)
}
}
/**
* @dev Store `value` at location `slot` in transient storage.
*/
function tstore(Int256Slot slot, int256 value) internal {
assembly ("memory-safe") {
tstore(slot, value)
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/IERC165.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC-165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[ERC].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/Panic.sol)
pragma solidity ^0.8.20;
/**
* @dev Helper library for emitting standardized panic codes.
*
* ```solidity
* contract Example {
* using Panic for uint256;
*
* // Use any of the declared internal constants
* function foo() { Panic.GENERIC.panic(); }
*
* // Alternatively
* function foo() { Panic.panic(Panic.GENERIC); }
* }
* ```
*
* Follows the list from https://github.com/ethereum/solidity/blob/v0.8.24/libsolutil/ErrorCodes.h[libsolutil].
*
* _Available since v5.1._
*/
// slither-disable-next-line unused-state
library Panic {
/// @dev generic / unspecified error
uint256 internal constant GENERIC = 0x00;
/// @dev used by the assert() builtin
uint256 internal constant ASSERT = 0x01;
/// @dev arithmetic underflow or overflow
uint256 internal constant UNDER_OVERFLOW = 0x11;
/// @dev division or modulo by zero
uint256 internal constant DIVISION_BY_ZERO = 0x12;
/// @dev enum conversion error
uint256 internal constant ENUM_CONVERSION_ERROR = 0x21;
/// @dev invalid encoding in storage
uint256 internal constant STORAGE_ENCODING_ERROR = 0x22;
/// @dev empty array pop
uint256 internal constant EMPTY_ARRAY_POP = 0x31;
/// @dev array out of bounds access
uint256 internal constant ARRAY_OUT_OF_BOUNDS = 0x32;
/// @dev resource error (too large allocation or too large array)
uint256 internal constant RESOURCE_ERROR = 0x41;
/// @dev calling invalid internal function
uint256 internal constant INVALID_INTERNAL_FUNCTION = 0x51;
/// @dev Reverts with a panic code. Recommended to use with
/// the internal constants with predefined codes.
function panic(uint256 code) internal pure {
assembly ("memory-safe") {
mstore(0x00, 0x4e487b71)
mstore(0x20, code)
revert(0x1c, 0x24)
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/math/SafeCast.sol)
// This file was procedurally generated from scripts/generate/templates/SafeCast.js.
pragma solidity ^0.8.20;
/**
* @dev Wrappers over Solidity's uintXX/intXX/bool casting operators with added overflow
* checks.
*
* Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
* easily result in undesired exploitation or bugs, since developers usually
* assume that overflows raise errors. `SafeCast` restores this intuition by
* reverting the transaction when such an operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*/
library SafeCast {
/**
* @dev Value doesn't fit in an uint of `bits` size.
*/
error SafeCastOverflowedUintDowncast(uint8 bits, uint256 value);
/**
* @dev An int value doesn't fit in an uint of `bits` size.
*/
error SafeCastOverflowedIntToUint(int256 value);
/**
* @dev Value doesn't fit in an int of `bits` size.
*/
error SafeCastOverflowedIntDowncast(uint8 bits, int256 value);
/**
* @dev An uint value doesn't fit in an int of `bits` size.
*/
error SafeCastOverflowedUintToInt(uint256 value);
/**
* @dev Returns the downcasted uint248 from uint256, reverting on
* overflow (when the input is greater than largest uint248).
*
* Counterpart to Solidity's `uint248` operator.
*
* Requirements:
*
* - input must fit into 248 bits
*/
function toUint248(uint256 value) internal pure returns (uint248) {
if (value > type(uint248).max) {
revert SafeCastOverflowedUintDowncast(248, value);
}
return uint248(value);
}
/**
* @dev Returns the downcasted uint240 from uint256, reverting on
* overflow (when the input is greater than largest uint240).
*
* Counterpart to Solidity's `uint240` operator.
*
* Requirements:
*
* - input must fit into 240 bits
*/
function toUint240(uint256 value) internal pure returns (uint240) {
if (value > type(uint240).max) {
revert SafeCastOverflowedUintDowncast(240, value);
}
return uint240(value);
}
/**
* @dev Returns the downcasted uint232 from uint256, reverting on
* overflow (when the input is greater than largest uint232).
*
* Counterpart to Solidity's `uint232` operator.
*
* Requirements:
*
* - input must fit into 232 bits
*/
function toUint232(uint256 value) internal pure returns (uint232) {
if (value > type(uint232).max) {
revert SafeCastOverflowedUintDowncast(232, value);
}
return uint232(value);
}
/**
* @dev Returns the downcasted uint224 from uint256, reverting on
* overflow (when the input is greater than largest uint224).
*
* Counterpart to Solidity's `uint224` operator.
*
* Requirements:
*
* - input must fit into 224 bits
*/
function toUint224(uint256 value) internal pure returns (uint224) {
if (value > type(uint224).max) {
revert SafeCastOverflowedUintDowncast(224, value);
}
return uint224(value);
}
/**
* @dev Returns the downcasted uint216 from uint256, reverting on
* overflow (when the input is greater than largest uint216).
*
* Counterpart to Solidity's `uint216` operator.
*
* Requirements:
*
* - input must fit into 216 bits
*/
function toUint216(uint256 value) internal pure returns (uint216) {
if (value > type(uint216).max) {
revert SafeCastOverflowedUintDowncast(216, value);
}
return uint216(value);
}
/**
* @dev Returns the downcasted uint208 from uint256, reverting on
* overflow (when the input is greater than largest uint208).
*
* Counterpart to Solidity's `uint208` operator.
*
* Requirements:
*
* - input must fit into 208 bits
*/
function toUint208(uint256 value) internal pure returns (uint208) {
if (value > type(uint208).max) {
revert SafeCastOverflowedUintDowncast(208, value);
}
return uint208(value);
}
/**
* @dev Returns the downcasted uint200 from uint256, reverting on
* overflow (when the input is greater than largest uint200).
*
* Counterpart to Solidity's `uint200` operator.
*
* Requirements:
*
* - input must fit into 200 bits
*/
function toUint200(uint256 value) internal pure returns (uint200) {
if (value > type(uint200).max) {
revert SafeCastOverflowedUintDowncast(200, value);
}
return uint200(value);
}
/**
* @dev Returns the downcasted uint192 from uint256, reverting on
* overflow (when the input is greater than largest uint192).
*
* Counterpart to Solidity's `uint192` operator.
*
* Requirements:
*
* - input must fit into 192 bits
*/
function toUint192(uint256 value) internal pure returns (uint192) {
if (value > type(uint192).max) {
revert SafeCastOverflowedUintDowncast(192, value);
}
return uint192(value);
}
/**
* @dev Returns the downcasted uint184 from uint256, reverting on
* overflow (when the input is greater than largest uint184).
*
* Counterpart to Solidity's `uint184` operator.
*
* Requirements:
*
* - input must fit into 184 bits
*/
function toUint184(uint256 value) internal pure returns (uint184) {
if (value > type(uint184).max) {
revert SafeCastOverflowedUintDowncast(184, value);
}
return uint184(value);
}
/**
* @dev Returns the downcasted uint176 from uint256, reverting on
* overflow (when the input is greater than largest uint176).
*
* Counterpart to Solidity's `uint176` operator.
*
* Requirements:
*
* - input must fit into 176 bits
*/
function toUint176(uint256 value) internal pure returns (uint176) {
if (value > type(uint176).max) {
revert SafeCastOverflowedUintDowncast(176, value);
}
return uint176(value);
}
/**
* @dev Returns the downcasted uint168 from uint256, reverting on
* overflow (when the input is greater than largest uint168).
*
* Counterpart to Solidity's `uint168` operator.
*
* Requirements:
*
* - input must fit into 168 bits
*/
function toUint168(uint256 value) internal pure returns (uint168) {
if (value > type(uint168).max) {
revert SafeCastOverflowedUintDowncast(168, value);
}
return uint168(value);
}
/**
* @dev Returns the downcasted uint160 from uint256, reverting on
* overflow (when the input is greater than largest uint160).
*
* Counterpart to Solidity's `uint160` operator.
*
* Requirements:
*
* - input must fit into 160 bits
*/
function toUint160(uint256 value) internal pure returns (uint160) {
if (value > type(uint160).max) {
revert SafeCastOverflowedUintDowncast(160, value);
}
return uint160(value);
}
/**
* @dev Returns the downcasted uint152 from uint256, reverting on
* overflow (when the input is greater than largest uint152).
*
* Counterpart to Solidity's `uint152` operator.
*
* Requirements:
*
* - input must fit into 152 bits
*/
function toUint152(uint256 value) internal pure returns (uint152) {
if (value > type(uint152).max) {
revert SafeCastOverflowedUintDowncast(152, value);
}
return uint152(value);
}
/**
* @dev Returns the downcasted uint144 from uint256, reverting on
* overflow (when the input is greater than largest uint144).
*
* Counterpart to Solidity's `uint144` operator.
*
* Requirements:
*
* - input must fit into 144 bits
*/
function toUint144(uint256 value) internal pure returns (uint144) {
if (value > type(uint144).max) {
revert SafeCastOverflowedUintDowncast(144, value);
}
return uint144(value);
}
/**
* @dev Returns the downcasted uint136 from uint256, reverting on
* overflow (when the input is greater than largest uint136).
*
* Counterpart to Solidity's `uint136` operator.
*
* Requirements:
*
* - input must fit into 136 bits
*/
function toUint136(uint256 value) internal pure returns (uint136) {
if (value > type(uint136).max) {
revert SafeCastOverflowedUintDowncast(136, value);
}
return uint136(value);
}
/**
* @dev Returns the downcasted uint128 from uint256, reverting on
* overflow (when the input is greater than largest uint128).
*
* Counterpart to Solidity's `uint128` operator.
*
* Requirements:
*
* - input must fit into 128 bits
*/
function toUint128(uint256 value) internal pure returns (uint128) {
if (value > type(uint128).max) {
revert SafeCastOverflowedUintDowncast(128, value);
}
return uint128(value);
}
/**
* @dev Returns the downcasted uint120 from uint256, reverting on
* overflow (when the input is greater than largest uint120).
*
* Counterpart to Solidity's `uint120` operator.
*
* Requirements:
*
* - input must fit into 120 bits
*/
function toUint120(uint256 value) internal pure returns (uint120) {
if (value > type(uint120).max) {
revert SafeCastOverflowedUintDowncast(120, value);
}
return uint120(value);
}
/**
* @dev Returns the downcasted uint112 from uint256, reverting on
* overflow (when the input is greater than largest uint112).
*
* Counterpart to Solidity's `uint112` operator.
*
* Requirements:
*
* - input must fit into 112 bits
*/
function toUint112(uint256 value) internal pure returns (uint112) {
if (value > type(uint112).max) {
revert SafeCastOverflowedUintDowncast(112, value);
}
return uint112(value);
}
/**
* @dev Returns the downcasted uint104 from uint256, reverting on
* overflow (when the input is greater than largest uint104).
*
* Counterpart to Solidity's `uint104` operator.
*
* Requirements:
*
* - input must fit into 104 bits
*/
function toUint104(uint256 value) internal pure returns (uint104) {
if (value > type(uint104).max) {
revert SafeCastOverflowedUintDowncast(104, value);
}
return uint104(value);
}
/**
* @dev Returns the downcasted uint96 from uint256, reverting on
* overflow (when the input is greater than largest uint96).
*
* Counterpart to Solidity's `uint96` operator.
*
* Requirements:
*
* - input must fit into 96 bits
*/
function toUint96(uint256 value) internal pure returns (uint96) {
if (value > type(uint96).max) {
revert SafeCastOverflowedUintDowncast(96, value);
}
return uint96(value);
}
/**
* @dev Returns the downcasted uint88 from uint256, reverting on
* overflow (when the input is greater than largest uint88).
*
* Counterpart to Solidity's `uint88` operator.
*
* Requirements:
*
* - input must fit into 88 bits
*/
function toUint88(uint256 value) internal pure returns (uint88) {
if (value > type(uint88).max) {
revert SafeCastOverflowedUintDowncast(88, value);
}
return uint88(value);
}
/**
* @dev Returns the downcasted uint80 from uint256, reverting on
* overflow (when the input is greater than largest uint80).
*
* Counterpart to Solidity's `uint80` operator.
*
* Requirements:
*
* - input must fit into 80 bits
*/
function toUint80(uint256 value) internal pure returns (uint80) {
if (value > type(uint80).max) {
revert SafeCastOverflowedUintDowncast(80, value);
}
return uint80(value);
}
/**
* @dev Returns the downcasted uint72 from uint256, reverting on
* overflow (when the input is greater than largest uint72).
*
* Counterpart to Solidity's `uint72` operator.
*
* Requirements:
*
* - input must fit into 72 bits
*/
function toUint72(uint256 value) internal pure returns (uint72) {
if (value > type(uint72).max) {
revert SafeCastOverflowedUintDowncast(72, value);
}
return uint72(value);
}
/**
* @dev Returns the downcasted uint64 from uint256, reverting on
* overflow (when the input is greater than largest uint64).
*
* Counterpart to Solidity's `uint64` operator.
*
* Requirements:
*
* - input must fit into 64 bits
*/
function toUint64(uint256 value) internal pure returns (uint64) {
if (value > type(uint64).max) {
revert SafeCastOverflowedUintDowncast(64, value);
}
return uint64(value);
}
/**
* @dev Returns the downcasted uint56 from uint256, reverting on
* overflow (when the input is greater than largest uint56).
*
* Counterpart to Solidity's `uint56` operator.
*
* Requirements:
*
* - input must fit into 56 bits
*/
function toUint56(uint256 value) internal pure returns (uint56) {
if (value > type(uint56).max) {
revert SafeCastOverflowedUintDowncast(56, value);
}
return uint56(value);
}
/**
* @dev Returns the downcasted uint48 from uint256, reverting on
* overflow (when the input is greater than largest uint48).
*
* Counterpart to Solidity's `uint48` operator.
*
* Requirements:
*
* - input must fit into 48 bits
*/
function toUint48(uint256 value) internal pure returns (uint48) {
if (value > type(uint48).max) {
revert SafeCastOverflowedUintDowncast(48, value);
}
return uint48(value);
}
/**
* @dev Returns the downcasted uint40 from uint256, reverting on
* overflow (when the input is greater than largest uint40).
*
* Counterpart to Solidity's `uint40` operator.
*
* Requirements:
*
* - input must fit into 40 bits
*/
function toUint40(uint256 value) internal pure returns (uint40) {
if (value > type(uint40).max) {
revert SafeCastOverflowedUintDowncast(40, value);
}
return uint40(value);
}
/**
* @dev Returns the downcasted uint32 from uint256, reverting on
* overflow (when the input is greater than largest uint32).
*
* Counterpart to Solidity's `uint32` operator.
*
* Requirements:
*
* - input must fit into 32 bits
*/
function toUint32(uint256 value) internal pure returns (uint32) {
if (value > type(uint32).max) {
revert SafeCastOverflowedUintDowncast(32, value);
}
return uint32(value);
}
/**
* @dev Returns the downcasted uint24 from uint256, reverting on
* overflow (when the input is greater than largest uint24).
*
* Counterpart to Solidity's `uint24` operator.
*
* Requirements:
*
* - input must fit into 24 bits
*/
function toUint24(uint256 value) internal pure returns (uint24) {
if (value > type(uint24).max) {
revert SafeCastOverflowedUintDowncast(24, value);
}
return uint24(value);
}
/**
* @dev Returns the downcasted uint16 from uint256, reverting on
* overflow (when the input is greater than largest uint16).
*
* Counterpart to Solidity's `uint16` operator.
*
* Requirements:
*
* - input must fit into 16 bits
*/
function toUint16(uint256 value) internal pure returns (uint16) {
if (value > type(uint16).max) {
revert SafeCastOverflowedUintDowncast(16, value);
}
return uint16(value);
}
/**
* @dev Returns the downcasted uint8 from uint256, reverting on
* overflow (when the input is greater than largest uint8).
*
* Counterpart to Solidity's `uint8` operator.
*
* Requirements:
*
* - input must fit into 8 bits
*/
function toUint8(uint256 value) internal pure returns (uint8) {
if (value > type(uint8).max) {
revert SafeCastOverflowedUintDowncast(8, value);
}
return uint8(value);
}
/**
* @dev Converts a signed int256 into an unsigned uint256.
*
* Requirements:
*
* - input must be greater than or equal to 0.
*/
function toUint256(int256 value) internal pure returns (uint256) {
if (value < 0) {
revert SafeCastOverflowedIntToUint(value);
}
return uint256(value);
}
/**
* @dev Returns the downcasted int248 from int256, reverting on
* overflow (when the input is less than smallest int248 or
* greater than largest int248).
*
* Counterpart to Solidity's `int248` operator.
*
* Requirements:
*
* - input must fit into 248 bits
*/
function toInt248(int256 value) internal pure returns (int248 downcasted) {
downcasted = int248(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(248, value);
}
}
/**
* @dev Returns the downcasted int240 from int256, reverting on
* overflow (when the input is less than smallest int240 or
* greater than largest int240).
*
* Counterpart to Solidity's `int240` operator.
*
* Requirements:
*
* - input must fit into 240 bits
*/
function toInt240(int256 value) internal pure returns (int240 downcasted) {
downcasted = int240(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(240, value);
}
}
/**
* @dev Returns the downcasted int232 from int256, reverting on
* overflow (when the input is less than smallest int232 or
* greater than largest int232).
*
* Counterpart to Solidity's `int232` operator.
*
* Requirements:
*
* - input must fit into 232 bits
*/
function toInt232(int256 value) internal pure returns (int232 downcasted) {
downcasted = int232(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(232, value);
}
}
/**
* @dev Returns the downcasted int224 from int256, reverting on
* overflow (when the input is less than smallest int224 or
* greater than largest int224).
*
* Counterpart to Solidity's `int224` operator.
*
* Requirements:
*
* - input must fit into 224 bits
*/
function toInt224(int256 value) internal pure returns (int224 downcasted) {
downcasted = int224(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(224, value);
}
}
/**
* @dev Returns the downcasted int216 from int256, reverting on
* overflow (when the input is less than smallest int216 or
* greater than largest int216).
*
* Counterpart to Solidity's `int216` operator.
*
* Requirements:
*
* - input must fit into 216 bits
*/
function toInt216(int256 value) internal pure returns (int216 downcasted) {
downcasted = int216(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(216, value);
}
}
/**
* @dev Returns the downcasted int208 from int256, reverting on
* overflow (when the input is less than smallest int208 or
* greater than largest int208).
*
* Counterpart to Solidity's `int208` operator.
*
* Requirements:
*
* - input must fit into 208 bits
*/
function toInt208(int256 value) internal pure returns (int208 downcasted) {
downcasted = int208(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(208, value);
}
}
/**
* @dev Returns the downcasted int200 from int256, reverting on
* overflow (when the input is less than smallest int200 or
* greater than largest int200).
*
* Counterpart to Solidity's `int200` operator.
*
* Requirements:
*
* - input must fit into 200 bits
*/
function toInt200(int256 value) internal pure returns (int200 downcasted) {
downcasted = int200(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(200, value);
}
}
/**
* @dev Returns the downcasted int192 from int256, reverting on
* overflow (when the input is less than smallest int192 or
* greater than largest int192).
*
* Counterpart to Solidity's `int192` operator.
*
* Requirements:
*
* - input must fit into 192 bits
*/
function toInt192(int256 value) internal pure returns (int192 downcasted) {
downcasted = int192(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(192, value);
}
}
/**
* @dev Returns the downcasted int184 from int256, reverting on
* overflow (when the input is less than smallest int184 or
* greater than largest int184).
*
* Counterpart to Solidity's `int184` operator.
*
* Requirements:
*
* - input must fit into 184 bits
*/
function toInt184(int256 value) internal pure returns (int184 downcasted) {
downcasted = int184(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(184, value);
}
}
/**
* @dev Returns the downcasted int176 from int256, reverting on
* overflow (when the input is less than smallest int176 or
* greater than largest int176).
*
* Counterpart to Solidity's `int176` operator.
*
* Requirements:
*
* - input must fit into 176 bits
*/
function toInt176(int256 value) internal pure returns (int176 downcasted) {
downcasted = int176(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(176, value);
}
}
/**
* @dev Returns the downcasted int168 from int256, reverting on
* overflow (when the input is less than smallest int168 or
* greater than largest int168).
*
* Counterpart to Solidity's `int168` operator.
*
* Requirements:
*
* - input must fit into 168 bits
*/
function toInt168(int256 value) internal pure returns (int168 downcasted) {
downcasted = int168(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(168, value);
}
}
/**
* @dev Returns the downcasted int160 from int256, reverting on
* overflow (when the input is less than smallest int160 or
* greater than largest int160).
*
* Counterpart to Solidity's `int160` operator.
*
* Requirements:
*
* - input must fit into 160 bits
*/
function toInt160(int256 value) internal pure returns (int160 downcasted) {
downcasted = int160(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(160, value);
}
}
/**
* @dev Returns the downcasted int152 from int256, reverting on
* overflow (when the input is less than smallest int152 or
* greater than largest int152).
*
* Counterpart to Solidity's `int152` operator.
*
* Requirements:
*
* - input must fit into 152 bits
*/
function toInt152(int256 value) internal pure returns (int152 downcasted) {
downcasted = int152(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(152, value);
}
}
/**
* @dev Returns the downcasted int144 from int256, reverting on
* overflow (when the input is less than smallest int144 or
* greater than largest int144).
*
* Counterpart to Solidity's `int144` operator.
*
* Requirements:
*
* - input must fit into 144 bits
*/
function toInt144(int256 value) internal pure returns (int144 downcasted) {
downcasted = int144(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(144, value);
}
}
/**
* @dev Returns the downcasted int136 from int256, reverting on
* overflow (when the input is less than smallest int136 or
* greater than largest int136).
*
* Counterpart to Solidity's `int136` operator.
*
* Requirements:
*
* - input must fit into 136 bits
*/
function toInt136(int256 value) internal pure returns (int136 downcasted) {
downcasted = int136(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(136, value);
}
}
/**
* @dev Returns the downcasted int128 from int256, reverting on
* overflow (when the input is less than smallest int128 or
* greater than largest int128).
*
* Counterpart to Solidity's `int128` operator.
*
* Requirements:
*
* - input must fit into 128 bits
*/
function toInt128(int256 value) internal pure returns (int128 downcasted) {
downcasted = int128(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(128, value);
}
}
/**
* @dev Returns the downcasted int120 from int256, reverting on
* overflow (when the input is less than smallest int120 or
* greater than largest int120).
*
* Counterpart to Solidity's `int120` operator.
*
* Requirements:
*
* - input must fit into 120 bits
*/
function toInt120(int256 value) internal pure returns (int120 downcasted) {
downcasted = int120(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(120, value);
}
}
/**
* @dev Returns the downcasted int112 from int256, reverting on
* overflow (when the input is less than smallest int112 or
* greater than largest int112).
*
* Counterpart to Solidity's `int112` operator.
*
* Requirements:
*
* - input must fit into 112 bits
*/
function toInt112(int256 value) internal pure returns (int112 downcasted) {
downcasted = int112(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(112, value);
}
}
/**
* @dev Returns the downcasted int104 from int256, reverting on
* overflow (when the input is less than smallest int104 or
* greater than largest int104).
*
* Counterpart to Solidity's `int104` operator.
*
* Requirements:
*
* - input must fit into 104 bits
*/
function toInt104(int256 value) internal pure returns (int104 downcasted) {
downcasted = int104(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(104, value);
}
}
/**
* @dev Returns the downcasted int96 from int256, reverting on
* overflow (when the input is less than smallest int96 or
* greater than largest int96).
*
* Counterpart to Solidity's `int96` operator.
*
* Requirements:
*
* - input must fit into 96 bits
*/
function toInt96(int256 value) internal pure returns (int96 downcasted) {
downcasted = int96(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(96, value);
}
}
/**
* @dev Returns the downcasted int88 from int256, reverting on
* overflow (when the input is less than smallest int88 or
* greater than largest int88).
*
* Counterpart to Solidity's `int88` operator.
*
* Requirements:
*
* - input must fit into 88 bits
*/
function toInt88(int256 value) internal pure returns (int88 downcasted) {
downcasted = int88(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(88, value);
}
}
/**
* @dev Returns the downcasted int80 from int256, reverting on
* overflow (when the input is less than smallest int80 or
* greater than largest int80).
*
* Counterpart to Solidity's `int80` operator.
*
* Requirements:
*
* - input must fit into 80 bits
*/
function toInt80(int256 value) internal pure returns (int80 downcasted) {
downcasted = int80(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(80, value);
}
}
/**
* @dev Returns the downcasted int72 from int256, reverting on
* overflow (when the input is less than smallest int72 or
* greater than largest int72).
*
* Counterpart to Solidity's `int72` operator.
*
* Requirements:
*
* - input must fit into 72 bits
*/
function toInt72(int256 value) internal pure returns (int72 downcasted) {
downcasted = int72(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(72, value);
}
}
/**
* @dev Returns the downcasted int64 from int256, reverting on
* overflow (when the input is less than smallest int64 or
* greater than largest int64).
*
* Counterpart to Solidity's `int64` operator.
*
* Requirements:
*
* - input must fit into 64 bits
*/
function toInt64(int256 value) internal pure returns (int64 downcasted) {
downcasted = int64(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(64, value);
}
}
/**
* @dev Returns the downcasted int56 from int256, reverting on
* overflow (when the input is less than smallest int56 or
* greater than largest int56).
*
* Counterpart to Solidity's `int56` operator.
*
* Requirements:
*
* - input must fit into 56 bits
*/
function toInt56(int256 value) internal pure returns (int56 downcasted) {
downcasted = int56(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(56, value);
}
}
/**
* @dev Returns the downcasted int48 from int256, reverting on
* overflow (when the input is less than smallest int48 or
* greater than largest int48).
*
* Counterpart to Solidity's `int48` operator.
*
* Requirements:
*
* - input must fit into 48 bits
*/
function toInt48(int256 value) internal pure returns (int48 downcasted) {
downcasted = int48(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(48, value);
}
}
/**
* @dev Returns the downcasted int40 from int256, reverting on
* overflow (when the input is less than smallest int40 or
* greater than largest int40).
*
* Counterpart to Solidity's `int40` operator.
*
* Requirements:
*
* - input must fit into 40 bits
*/
function toInt40(int256 value) internal pure returns (int40 downcasted) {
downcasted = int40(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(40, value);
}
}
/**
* @dev Returns the downcasted int32 from int256, reverting on
* overflow (when the input is less than smallest int32 or
* greater than largest int32).
*
* Counterpart to Solidity's `int32` operator.
*
* Requirements:
*
* - input must fit into 32 bits
*/
function toInt32(int256 value) internal pure returns (int32 downcasted) {
downcasted = int32(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(32, value);
}
}
/**
* @dev Returns the downcasted int24 from int256, reverting on
* overflow (when the input is less than smallest int24 or
* greater than largest int24).
*
* Counterpart to Solidity's `int24` operator.
*
* Requirements:
*
* - input must fit into 24 bits
*/
function toInt24(int256 value) internal pure returns (int24 downcasted) {
downcasted = int24(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(24, value);
}
}
/**
* @dev Returns the downcasted int16 from int256, reverting on
* overflow (when the input is less than smallest int16 or
* greater than largest int16).
*
* Counterpart to Solidity's `int16` operator.
*
* Requirements:
*
* - input must fit into 16 bits
*/
function toInt16(int256 value) internal pure returns (int16 downcasted) {
downcasted = int16(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(16, value);
}
}
/**
* @dev Returns the downcasted int8 from int256, reverting on
* overflow (when the input is less than smallest int8 or
* greater than largest int8).
*
* Counterpart to Solidity's `int8` operator.
*
* Requirements:
*
* - input must fit into 8 bits
*/
function toInt8(int256 value) internal pure returns (int8 downcasted) {
downcasted = int8(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(8, value);
}
}
/**
* @dev Converts an unsigned uint256 into a signed int256.
*
* Requirements:
*
* - input must be less than or equal to maxInt256.
*/
function toInt256(uint256 value) internal pure returns (int256) {
// Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
if (value > uint256(type(int256).max)) {
revert SafeCastOverflowedUintToInt(value);
}
return int256(value);
}
/**
* @dev Cast a boolean (false or true) to a uint256 (0 or 1) with no jump.
*/
function toUint(bool b) internal pure returns (uint256 u) {
assembly ("memory-safe") {
u := iszero(iszero(b))
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/beacon/IBeacon.sol)
pragma solidity ^0.8.20;
/**
* @dev This is the interface that {BeaconProxy} expects of its beacon.
*/
interface IBeacon {
/**
* @dev Must return an address that can be used as a delegate call target.
*
* {UpgradeableBeacon} will check that this address is a contract.
*/
function implementation() external view returns (address);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC1967.sol)
pragma solidity ^0.8.20;
/**
* @dev ERC-1967: Proxy Storage Slots. This interface contains the events defined in the ERC.
*/
interface IERC1967 {
/**
* @dev Emitted when the implementation is upgraded.
*/
event Upgraded(address indexed implementation);
/**
* @dev Emitted when the admin account has changed.
*/
event AdminChanged(address previousAdmin, address newAdmin);
/**
* @dev Emitted when the beacon is changed.
*/
event BeaconUpgraded(address indexed beacon);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.2.0) (utils/Address.sol)
pragma solidity ^0.8.20;
import {Errors} from "./Errors.sol";
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev There's no code at `target` (it is not a contract).
*/
error AddressEmptyCode(address target);
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
if (address(this).balance < amount) {
revert Errors.InsufficientBalance(address(this).balance, amount);
}
(bool success, bytes memory returndata) = recipient.call{value: amount}("");
if (!success) {
_revert(returndata);
}
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason or custom error, it is bubbled
* up by this function (like regular Solidity function calls). However, if
* the call reverted with no returned reason, this function reverts with a
* {Errors.FailedCall} error.
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
if (address(this).balance < value) {
revert Errors.InsufficientBalance(address(this).balance, value);
}
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
* was not a contract or bubbling up the revert reason (falling back to {Errors.FailedCall}) in case
* of an unsuccessful call.
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata
) internal view returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
// only check if target is a contract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
if (returndata.length == 0 && target.code.length == 0) {
revert AddressEmptyCode(target);
}
return returndata;
}
}
/**
* @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
* revert reason or with a default {Errors.FailedCall} error.
*/
function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
return returndata;
}
}
/**
* @dev Reverts with returndata if present. Otherwise reverts with {Errors.FailedCall}.
*/
function _revert(bytes memory returndata) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
assembly ("memory-safe") {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert Errors.FailedCall();
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/StorageSlot.sol)
// This file was procedurally generated from scripts/generate/templates/StorageSlot.js.
pragma solidity ^0.8.20;
/**
* @dev Library for reading and writing primitive types to specific storage slots.
*
* Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
* This library helps with reading and writing to such slots without the need for inline assembly.
*
* The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
*
* Example usage to set ERC-1967 implementation slot:
* ```solidity
* contract ERC1967 {
* // Define the slot. Alternatively, use the SlotDerivation library to derive the slot.
* bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
*
* function _getImplementation() internal view returns (address) {
* return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
* }
*
* function _setImplementation(address newImplementation) internal {
* require(newImplementation.code.length > 0);
* StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
* }
* }
* ```
*
* TIP: Consider using this library along with {SlotDerivation}.
*/
library StorageSlot {
struct AddressSlot {
address value;
}
struct BooleanSlot {
bool value;
}
struct Bytes32Slot {
bytes32 value;
}
struct Uint256Slot {
uint256 value;
}
struct Int256Slot {
int256 value;
}
struct StringSlot {
string value;
}
struct BytesSlot {
bytes value;
}
/**
* @dev Returns an `AddressSlot` with member `value` located at `slot`.
*/
function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
assembly ("memory-safe") {
r.slot := slot
}
}
/**
* @dev Returns a `BooleanSlot` with member `value` located at `slot`.
*/
function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
assembly ("memory-safe") {
r.slot := slot
}
}
/**
* @dev Returns a `Bytes32Slot` with member `value` located at `slot`.
*/
function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
assembly ("memory-safe") {
r.slot := slot
}
}
/**
* @dev Returns a `Uint256Slot` with member `value` located at `slot`.
*/
function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
assembly ("memory-safe") {
r.slot := slot
}
}
/**
* @dev Returns a `Int256Slot` with member `value` located at `slot`.
*/
function getInt256Slot(bytes32 slot) internal pure returns (Int256Slot storage r) {
assembly ("memory-safe") {
r.slot := slot
}
}
/**
* @dev Returns a `StringSlot` with member `value` located at `slot`.
*/
function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
assembly ("memory-safe") {
r.slot := slot
}
}
/**
* @dev Returns an `StringSlot` representation of the string storage pointer `store`.
*/
function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {
assembly ("memory-safe") {
r.slot := store.slot
}
}
/**
* @dev Returns a `BytesSlot` with member `value` located at `slot`.
*/
function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
assembly ("memory-safe") {
r.slot := slot
}
}
/**
* @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.
*/
function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {
assembly ("memory-safe") {
r.slot := store.slot
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/Errors.sol)
pragma solidity ^0.8.20;
/**
* @dev Collection of common custom errors used in multiple contracts
*
* IMPORTANT: Backwards compatibility is not guaranteed in future versions of the library.
* It is recommended to avoid relying on the error API for critical functionality.
*
* _Available since v5.1._
*/
library Errors {
/**
* @dev The ETH balance of the account is not enough to perform the operation.
*/
error InsufficientBalance(uint256 balance, uint256 needed);
/**
* @dev A call to an address target failed. The target may have reverted.
*/
error FailedCall();
/**
* @dev The deployment failed.
*/
error FailedDeployment();
/**
* @dev A necessary precompile is missing.
*/
error MissingPrecompile(address);
}{
"remappings": [
"@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
"@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
"erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/",
"forge-std/=lib/forge-std/src/",
"halmos-cheatcodes/=lib/openzeppelin-contracts-upgradeable/lib/halmos-cheatcodes/src/",
"openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
"openzeppelin-contracts/=lib/openzeppelin-contracts/",
"solady/=lib/solady/src/"
],
"optimizer": {
"enabled": true,
"runs": 200
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "none",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "cancun",
"viaIR": false,
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"enum BinSponsor","name":"_binSponsor","type":"uint8"},{"internalType":"address","name":"_dataProvider","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[],"name":"ArrayLengthMismatch","type":"error"},{"inputs":[],"name":"CannotWithdrawZeroAmount","type":"error"},{"inputs":[],"name":"DestinationDataNotSet","type":"error"},{"inputs":[{"internalType":"address","name":"implementation","type":"address"}],"name":"ERC1967InvalidImplementation","type":"error"},{"inputs":[],"name":"ERC1967NonPayable","type":"error"},{"inputs":[],"name":"EnforcedPause","type":"error"},{"inputs":[],"name":"ExpectedPause","type":"error"},{"inputs":[],"name":"FailedCall","type":"error"},{"inputs":[],"name":"InsufficientBalance","type":"error"},{"inputs":[],"name":"InsufficientFeeToCoverCost","type":"error"},{"inputs":[],"name":"InsufficientMinReturn","type":"error"},{"inputs":[],"name":"InsufficientReturnAmount","type":"error"},{"inputs":[],"name":"InvalidBoringQueue","type":"error"},{"inputs":[],"name":"InvalidInitialization","type":"error"},{"inputs":[],"name":"InvalidValue","type":"error"},{"inputs":[],"name":"LiquidWithdrawConfigNotSet","type":"error"},{"inputs":[],"name":"NotInitializing","type":"error"},{"inputs":[],"name":"OnlyRoleRegistryOwner","type":"error"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"inputs":[],"name":"RefundWalletNotSet","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"inputs":[],"name":"StargateValueInvalid","type":"error"},{"inputs":[],"name":"UUPSUnauthorizedCallContext","type":"error"},{"inputs":[{"internalType":"bytes32","name":"slot","type":"bytes32"}],"name":"UUPSUnsupportedProxiableUUID","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"inputs":[],"name":"WithdrawFundsFailed","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"CanonicalBridgeWithdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address[]","name":"tokens","type":"address[]"},{"components":[{"internalType":"uint32","name":"destEid","type":"uint32"},{"internalType":"address","name":"destRecipient","type":"address"},{"internalType":"address","name":"stargate","type":"address"},{"internalType":"bool","name":"useCanonicalBridge","type":"bool"},{"internalType":"uint64","name":"minGasLimit","type":"uint64"}],"indexed":false,"internalType":"struct SettlementDispatcher.DestinationData[]","name":"destDatas","type":"tuple[]"}],"name":"DestinationDataSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"components":[{"internalType":"uint72","name":"ticketId","type":"uint72"},{"internalType":"bytes","name":"passengerBytes","type":"bytes"}],"indexed":false,"internalType":"struct Ticket","name":"ticket","type":"tuple"}],"name":"FundsBridgedWithStargate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"}],"name":"FundsWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"version","type":"uint64"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"address","name":"boringQueue","type":"address"}],"name":"LiquidWithdrawQueueSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"liquidToken","type":"address"},{"indexed":true,"internalType":"address","name":"assetOut","type":"address"},{"indexed":false,"internalType":"uint128","name":"amountToWithdraw","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"amountOut","type":"uint128"}],"name":"LiquidWithdrawalRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"asset","type":"address"},{"indexed":false,"internalType":"address","name":"refundWallet","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"TransferToRefundWallet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"implementation","type":"address"}],"name":"Upgraded","type":"event"},{"inputs":[],"name":"ETH","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ETH_MESSENGER","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"GATEWAY_ROUTER","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SETTLEMENT_DISPATCHER_BRIDGER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"UPGRADE_INTERFACE_VERSION","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"binSponsor","outputs":[{"internalType":"enum BinSponsor","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"minReturnLD","type":"uint256"}],"name":"bridge","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"dataProvider","outputs":[{"internalType":"contract IEtherFiDataProvider","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"destinationData","outputs":[{"components":[{"internalType":"uint32","name":"destEid","type":"uint32"},{"internalType":"address","name":"destRecipient","type":"address"},{"internalType":"address","name":"stargate","type":"address"},{"internalType":"bool","name":"useCanonicalBridge","type":"bool"},{"internalType":"uint64","name":"minGasLimit","type":"uint64"}],"internalType":"struct SettlementDispatcher.DestinationData","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"}],"name":"getLiquidAssetWithdrawQueue","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRefundWallet","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_roleRegistry","type":"address"},{"internalType":"address[]","name":"_tokens","type":"address[]"},{"components":[{"internalType":"uint32","name":"destEid","type":"uint32"},{"internalType":"address","name":"destRecipient","type":"address"},{"internalType":"address","name":"stargate","type":"address"},{"internalType":"bool","name":"useCanonicalBridge","type":"bool"},{"internalType":"uint64","name":"minGasLimit","type":"uint64"}],"internalType":"struct SettlementDispatcher.DestinationData[]","name":"_destDatas","type":"tuple[]"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"prepareRideBus","outputs":[{"internalType":"address","name":"stargate","type":"address"},{"internalType":"uint256","name":"valueToSend","type":"uint256"},{"internalType":"uint256","name":"minReturnFromStargate","type":"uint256"},{"components":[{"internalType":"uint32","name":"dstEid","type":"uint32"},{"internalType":"bytes32","name":"to","type":"bytes32"},{"internalType":"uint256","name":"amountLD","type":"uint256"},{"internalType":"uint256","name":"minAmountLD","type":"uint256"},{"internalType":"bytes","name":"extraOptions","type":"bytes"},{"internalType":"bytes","name":"composeMsg","type":"bytes"},{"internalType":"bytes","name":"oftCmd","type":"bytes"}],"internalType":"struct SendParam","name":"sendParam","type":"tuple"},{"components":[{"internalType":"uint256","name":"nativeFee","type":"uint256"},{"internalType":"uint256","name":"lzTokenFee","type":"uint256"}],"internalType":"struct MessagingFee","name":"messagingFee","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"proxiableUUID","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"roleRegistry","outputs":[{"internalType":"contract IRoleRegistry","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"tokens","type":"address[]"},{"components":[{"internalType":"uint32","name":"destEid","type":"uint32"},{"internalType":"address","name":"destRecipient","type":"address"},{"internalType":"address","name":"stargate","type":"address"},{"internalType":"bool","name":"useCanonicalBridge","type":"bool"},{"internalType":"uint64","name":"minGasLimit","type":"uint64"}],"internalType":"struct SettlementDispatcher.DestinationData[]","name":"destDatas","type":"tuple[]"}],"name":"setDestinationData","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"address","name":"boringQueue","type":"address"}],"name":"setLiquidAssetWithdrawQueue","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFundsToRefundWallet","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newImplementation","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"upgradeToAndCall","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdrawFunds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"liquidToken","type":"address"},{"internalType":"address","name":"assetOut","type":"address"},{"internalType":"uint128","name":"amount","type":"uint128"},{"internalType":"uint128","name":"minReturn","type":"uint128"},{"internalType":"uint16","name":"discount","type":"uint16"},{"internalType":"uint24","name":"secondsToDeadline","type":"uint24"}],"name":"withdrawLiquidAsset","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]Contract Creation Code
60e060405230608052348015610013575f5ffd5b5060405161357638038061357683398101604081905261003291610127565b61003a610075565b81600181111561004c5761004c61016d565b60a08160018111156100605761006061016d565b9052506001600160a01b031660c05250610181565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff16156100c55760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b03908116146101245780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b5f5f60408385031215610138575f5ffd5b825160028110610146575f5ffd5b60208401519092506001600160a01b0381168114610162575f5ffd5b809150509250929050565b634e487b7160e01b5f52602160045260245ffd5b60805160a05160c0516133b66101c05f395f818161045d0152610cb401525f6105b201525f818161212801528181612151015261230201526133b65ff3fe60806040526004361061014a575f3560e01c80634f1ef286116100b35780638322fff21161006d5780638322fff2146103d45780638456cb59146103fb578063ad3cb1cc1461040f578063b334ed861461044c578063be3319781461047f578063cc614d68146105a1575f5ffd5b80634f1ef2861461030b57806352d1902d1461031e578063579f69d2146103405780635c975abb146103675780635d8d032414610395578063666fbf73146103b5575f5ffd5b80632a82afe2116101045780632a82afe21461024e5780632b03a50c14610262578063391c13f5146102815780633c6f7179146102a05780633f4ba83a146102d057806347437514146102e4575f5ffd5b806308c73259146101555780630bf5d35e146101865780630df6e6bf1461019b5780631c20fadd146101ba5780631eb2b17e146101d957806322d932f41461022f575f5ffd5b3661015157005b5f5ffd5b348015610160575f5ffd5b506101696105e1565b6040516001600160a01b0390911681526020015b60405180910390f35b61019961019436600461274e565b61060f565b005b3480156101a6575f5ffd5b506101996101b5366004612807565b6109a9565b3480156101c5575f5ffd5b506101996101d4366004612871565b610a4a565b3480156101e4575f5ffd5b506101696101f33660046128af565b6001600160a01b039081165f9081527f78555946a409defb00fb08ff23c8988ad687a02e1525a4fc9b7fd83443409e0160205260409020541690565b34801561023a575f5ffd5b506101996102493660046128ca565b610b6f565b348015610259575f5ffd5b50610169610cb1565b34801561026d575f5ffd5b5061019961027c3660046128f4565b610d37565b34801561028c575f5ffd5b5061019961029b36600461293f565b610f07565b3480156102ab575f5ffd5b506102bf6102ba3660046128ca565b6111c1565b60405161017d959493929190612a6d565b3480156102db575f5ffd5b506101996114c9565b3480156102ef575f5ffd5b50610169734c0926ff5252a435fd19e10ed15e5a249ba19d7981565b610199610319366004612b6a565b611530565b348015610329575f5ffd5b5061033261154b565b60405190815260200161017d565b34801561034b575f5ffd5b5061016973781e90f1c8fc4611c9b7497c3b47f99ef6969cbc81565b348015610372575f5ffd5b505f51602061336a5f395f51905f525460ff16604051901515815260200161017d565b3480156103a0575f5ffd5b506103325f51602061338a5f395f51905f5281565b3480156103c0575f5ffd5b506101996103cf366004612bf6565b611566565b3480156103df575f5ffd5b5061016973eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee81565b348015610406575f5ffd5b50610199611682565b34801561041a575f5ffd5b5061043f604051806040016040528060058152602001640352e302e360dc1b81525081565b60405161017d9190612c76565b348015610457575f5ffd5b506101697f000000000000000000000000000000000000000000000000000000000000000081565b34801561048a575f5ffd5b506105456104993660046128af565b6040805160a0810182525f808252602082018190529181018290526060810182905260808101919091525f51602061334a5f395f51905f526001600160a01b039283165f9081526020918252604090819020815160a081018352815463ffffffff8116825264010000000090048616938101939093526001015493841690820152600160a01b830460ff1615156060820152600160a81b9092046001600160401b031660808301525090565b60408051825163ffffffff1681526020808401516001600160a01b03908116918301919091528383015116918101919091526060808301511515908201526080918201516001600160401b03169181019190915260a00161017d565b3480156105ac575f5ffd5b506105d47f000000000000000000000000000000000000000000000000000000000000000081565b60405161017d9190612c88565b7fa5586bb7fe6c4d1a576fc53fefe6d5915940638d338769f6905020734977f500546001600160a01b031690565b6106176116e7565b5f51602061338a5f395f51905f5261062d6105e1565b604051632474521560e21b8152600481018390523360248201526001600160a01b0391909116906391d1485490604401602060405180830381865afa158015610678573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061069c9190612cbb565b6106b8576040516282b42960e81b815260040160405180910390fd5b6001600160a01b03841615806106cc575082155b156106ea57604051632a9ffab760e21b815260040160405180910390fd5b5f73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeed196001600160a01b03861601610717575047610780565b6040516370a0823160e01b81523060048201526001600160a01b038616906370a0823190602401602060405180830381865afa158015610759573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061077d9190612cd6565b90505b838110156107a157604051631e9acf1760e31b815260040160405180910390fd5b5f5f51602061334a5f395f51905f526001600160a01b038781165f9081526020928352604090819020815160a081018352815463ffffffff8116825264010000000090048416948101949094526001015491821690830152600160a01b810460ff161580156060840152600160a81b9091046001600160401b0316608083015290915061084b576108458682602001518784608001516001600160401b0316611717565b506109a1565b5f5f5f5f5f61085a8b8b6111c1565b9450945094509450945082891115610885576040516395d2fadd60e01b815260040160405180910390fd5b834710156108a6576040516357630bad60e11b815260040160405180910390fd5b6001600160a01b038b1673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee146108de576108de6001600160a01b038c16868c611930565b5f856001600160a01b031663cbef2aa9868585306040518563ffffffff1660e01b815260040161091093929190612ced565b5f6040518083038185885af115801561092b573d5f5f3e3d5ffd5b50505050506040513d5f823e601f3d908101601f191682016040526109539190810190612daa565b925050508b6001600160a01b03167fe80703b9d39d09d58bef629ae1125035c47d1da0342880315557c8ca9a2151cb8c83604051610992929190612ea4565b60405180910390a25050505050505b505050505050565b336109b26105e1565b6001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156109ed573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610a119190612eda565b6001600160a01b031614610a385760405163ab1899a760e01b815260040160405180910390fd5b610a44848484846119ed565b50505050565b610a52611eb2565b33610a5b6105e1565b6001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610a96573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610aba9190612eda565b6001600160a01b031614610ae15760405163ab1899a760e01b815260040160405180910390fd5b6001600160a01b038216610b0857604051632a9ffab760e21b815260040160405180910390fd5b610b13838383611f1f565b9050816001600160a01b0316836001600160a01b03167fe9171aeff46c37ab955b8aff908e14ae9732bd06634fa131226cfe5c25bfd2fb83604051610b5a91815260200190565b60405180910390a3610b6a612094565b505050565b610b77611eb2565b5f51602061338a5f395f51905f52610b8d6105e1565b604051632474521560e21b8152600481018390523360248201526001600160a01b0391909116906391d1485490604401602060405180830381865afa158015610bd8573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610bfc9190612cbb565b610c18576040516282b42960e81b815260040160405180910390fd5b5f610c21610cb1565b90506001600160a01b038116610c4a57604051630e8db52160e41b815260040160405180910390fd5b610c55848285611f1f565b604080516001600160a01b038088168252841660208201529081018290529093507fd8f7c78f293a52ff06285b09819d7625a13eb9080c991b7fac88cbf2b7239e899060600160405180910390a15050610cad612094565b5050565b5f7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316632a82afe26040518163ffffffff1660e01b8152600401602060405180830381865afa158015610d0e573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610d329190612eda565b905090565b33610d406105e1565b6001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610d7b573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610d9f9190612eda565b6001600160a01b031614610dc65760405163ab1899a760e01b815260040160405180910390fd5b6001600160a01b0382161580610de357506001600160a01b038116155b15610e0157604051632a9ffab760e21b815260040160405180910390fd5b806001600160a01b031663f3b977846040518163ffffffff1660e01b8152600401602060405180830381865afa158015610e3d573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610e619190612eda565b6001600160a01b0316826001600160a01b031614610e92576040516335c66c9960e01b815260040160405180910390fd5b6001600160a01b038281165f8181527f78555946a409defb00fb08ff23c8988ad687a02e1525a4fc9b7fd83443409e01602052604080822080546001600160a01b0319169486169485179055517fdd001770b3f22fb8a32aee0705976d01646098f9c1254e8064d2ee09184490189190a35050565b5f51602061338a5f395f51905f52610f1d6105e1565b604051632474521560e21b8152600481018390523360248201526001600160a01b0391909116906391d1485490604401602060405180830381865afa158015610f68573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610f8c9190612cbb565b610fa8576040516282b42960e81b815260040160405180910390fd5b6001600160a01b038781165f9081527f78555946a409defb00fb08ff23c8988ad687a02e1525a4fc9b7fd83443409e0160205260409020541680610fff57604051631d08fca760e11b815260040160405180910390fd5b60405163734d18e160e11b81526001600160a01b0388811660048301526001600160801b038816602483015261ffff861660448301525f919083169063e69a31c290606401602060405180830381865afa15801561105f573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906110839190612ef5565b9050856001600160801b0316816001600160801b031610156110b85760405163cea9e31d60e01b815260040160405180910390fd5b6110d56001600160a01b038a16836001600160801b038a16611930565b6040516335d9da3b60e11b81526001600160a01b0389811660048301526001600160801b038916602483015261ffff8716604483015262ffffff86166064830152831690636bb3b476906084016020604051808303815f875af115801561113e573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906111629190612cd6565b50604080516001600160801b03808a168252831660208201526001600160a01b03808b1692908c16917f51bf130244aab3e8452888df64bfe19c8c35a258af2516c5c1b791db7eae2f85910160405180910390a3505050505050505050565b5f5f5f6112066040518060e001604052805f63ffffffff1681526020015f81526020015f81526020015f81526020016060815260200160608152602001606081525090565b604080518082019091525f80825260208201525f5f51602061334a5f395f51905f526001600160a01b038981165f9081526020928352604090819020815160a081018352815463ffffffff811682526401000000009004841694810185905260019091015492831691810191909152600160a01b820460ff1615156060820152600160a81b9091046001600160401b0316608082015291506112bb57604051633df3d86f60e01b815260040160405180910390fd5b604081810151815160e081018352835163ffffffff1681526020808501516001600160a01b0316818301528184018b9052606082018b905283515f808252818301865260808401919091528451908152808201855260a083015283516001808252818601909552929950909260c084019291820181803683375050509052604051630d35b41560e01b81529093505f906001600160a01b03881690630d35b4159061136a908790600401612f10565b5f60405180830381865afa158015611384573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526113ab9190810190612f22565b602081018051606089015251604051633b6f743b60e01b81529098509093506001600160a01b038a169250633b6f743b91506113ed9087905f9060040161306b565b6040805180830381865afa158015611407573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061142b919061308e565b9250825f015195505f6001600160a01b0316876001600160a01b031663fc0c546a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611479573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061149d9190612eda565b6001600160a01b0316036114bd5760408401516114ba90876130a8565b95505b50509295509295909350565b6114d16105e1565b6040516317bf301f60e21b81523360048201526001600160a01b039190911690635efcc07c906024015f6040518083038186803b158015611510575f5ffd5b505afa158015611522573d5f5f3e3d5ffd5b5050505061152e6120be565b565b61153861211d565b611541826121c1565b610cad8282612236565b5f6115546122f7565b505f51602061332a5f395f51905f5290565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff1615906001600160401b03165f811580156115aa5750825b90505f826001600160401b031660011480156115c55750303b155b9050811580156115d3575080155b156115f15760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff19166001178555831561161b57845460ff60401b1916600160401b1785555b6116248a612340565b611630898989896119ed565b831561167657845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050505050565b61168a6105e1565b604051632fd0067b60e21b81523360048201526001600160a01b03919091169063bf4019ec906024015f6040518083038186803b1580156116c9575f5ffd5b505afa1580156116db573d5f5f3e3d5ffd5b5050505061152e612392565b5f51602061336a5f395f51905f525460ff161561152e5760405163d93c066560e01b815260040160405180910390fd5b5f73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeed196001600160a01b038616016117c85760405163b2267a7b60e01b81526001600160a01b038516600482015260248101849052608060448201525f60848201526064810183905273781e90f1c8fc4611c9b7497c3b47f99ef6969cbc9063b2267a7b90859060a4015f604051808303818588803b1580156117ac575f5ffd5b505af11580156117be573d5f5f3e3d5ffd5b50505050506118da565b6040516343c6674160e01b81526001600160a01b03861660048201525f90734c0926ff5252a435fd19e10ed15e5a249ba19d79906343c6674190602401602060405180830381865afa158015611820573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906118449190612eda565b905061185a6001600160a01b0387168286611930565b60405163a93a4af960e01b81526001600160a01b038088166004830152861660248201526044810185905260648101849052734c0926ff5252a435fd19e10ed15e5a249ba19d799063a93a4af9906084015f604051808303815f87803b1580156118c2575f5ffd5b505af11580156118d4573d5f5f3e3d5ffd5b50505050505b836001600160a01b0316856001600160a01b03167f89fb53d0c0f0f36db1043a54a7bb546fd820379fef6a90aec7293c3cf7ca13c98560405161191f91815260200190565b60405180910390a350909392505050565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b17905261198184826123da565b610a44576040516001600160a01b0384811660248301525f60448301526119e391869182169063095ea7b3906064015b604051602081830303815290604052915060e01b6020820180516001600160e01b038381831617835250505050612425565b610a448482612425565b82818114611a0e5760405163512509d360e11b815260040160405180910390fd5b5f51602061334a5f395f51905f525f5b82811015611e6c57848482818110611a3857611a386130c7565b905060a002016060016020810190611a5091906130db565b15611b5a575f878783818110611a6857611a686130c7565b9050602002016020810190611a7d91906128af565b6001600160a01b03161480611ac357505f858583818110611aa057611aa06130c7565b905060a002016020016020810190611ab891906128af565b6001600160a01b0316145b80611b0057505f858583818110611adc57611adc6130c7565b905060a002016040016020810190611af491906128af565b6001600160a01b031614155b80611b375750848482818110611b1857611b186130c7565b611b2e92602060a0909202019081019150613107565b63ffffffff1615155b15611b5557604051632a9ffab760e21b815260040160405180910390fd5b611dfd565b5f878783818110611b6d57611b6d6130c7565b9050602002016020810190611b8291906128af565b6001600160a01b03161480611bc857505f858583818110611ba557611ba56130c7565b905060a002016020016020810190611bbd91906128af565b6001600160a01b0316145b80611c0457505f858583818110611be157611be16130c7565b905060a002016040016020810190611bf991906128af565b6001600160a01b0316145b15611c2257604051632a9ffab760e21b815260040160405180910390fd5b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee878783818110611c4957611c496130c7565b9050602002016020810190611c5e91906128af565b6001600160a01b031603611d1d575f858583818110611c7f57611c7f6130c7565b905060a002016040016020810190611c9791906128af565b6001600160a01b031663fc0c546a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611cd2573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611cf69190612eda565b6001600160a01b031614611b555760405163b0fcd49560e01b815260040160405180910390fd5b868682818110611d2f57611d2f6130c7565b9050602002016020810190611d4491906128af565b6001600160a01b0316858583818110611d5f57611d5f6130c7565b905060a002016040016020810190611d7791906128af565b6001600160a01b031663fc0c546a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611db2573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611dd69190612eda565b6001600160a01b031614611dfd5760405163b0fcd49560e01b815260040160405180910390fd5b848482818110611e0f57611e0f6130c7565b905060a00201825f015f898985818110611e2b57611e2b6130c7565b9050602002016020810190611e4091906128af565b6001600160a01b0316815260208101919091526040015f20611e62828261312e565b5050600101611a1e565b507f93c0d4c04381b5de5cd6f8911c1885ffa461192db849a9e32edaea31816d5a9c86868686604051611ea29493929190613217565b60405180910390a1505050505050565b7f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f005c15611ef257604051633ee5aeb560e01b815260040160405180910390fd5b61152e60017f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f005b90612491565b5f73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeed196001600160a01b03851601611fe657815f03611f50574791505b815f03611f705760405163c740c32d60e01b815260040160405180910390fd5b5f836001600160a01b0316836040515f6040518083038185875af1925050503d805f8114611fb9576040519150601f19603f3d011682016040523d82523d5f602084013e611fbe565b606091505b5050905080611fe057604051632607a81f60e11b815260040160405180910390fd5b5061208a565b815f03612056576040516370a0823160e01b81523060048201526001600160a01b038516906370a0823190602401602060405180830381865afa15801561202f573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906120539190612cd6565b91505b815f036120765760405163c740c32d60e01b815260040160405180910390fd5b61208a6001600160a01b0385168484612498565b50805b9392505050565b61152e5f7f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00611f19565b6120c66124c9565b5f51602061336a5f395f51905f52805460ff191681557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a150565b306001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614806121a357507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166121975f51602061332a5f395f51905f52546001600160a01b031690565b6001600160a01b031614155b1561152e5760405163703e46dd60e11b815260040160405180910390fd5b5f7fa5586bb7fe6c4d1a576fc53fefe6d5915940638d338769f6905020734977f5008054604051634766d28760e01b81523360048201529192506001600160a01b031690634766d287906024015f6040518083038186803b158015612224575f5ffd5b505afa1580156109a1573d5f5f3e3d5ffd5b816001600160a01b03166352d1902d6040518163ffffffff1660e01b8152600401602060405180830381865afa925050508015612290575060408051601f3d908101601f1916820190925261228d91810190612cd6565b60015b6122bd57604051634c9c8ce360e01b81526001600160a01b03831660048201526024015b60405180910390fd5b5f51602061332a5f395f51905f5281146122ed57604051632a87526960e21b8152600481018290526024016122b4565b610b6a83836124f8565b306001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461152e5760405163703e46dd60e11b815260040160405180910390fd5b61234861254d565b7fa5586bb7fe6c4d1a576fc53fefe6d5915940638d338769f6905020734977f50080546001600160a01b0319166001600160a01b03831617815561238a612596565b610cad61259e565b61239a6116e7565b5f51602061336a5f395f51905f52805460ff191660011781557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258336120ff565b5f5f5f5f60205f8651602088015f8a5af192503d91505f5190508280156124195750811561240b5780600114612419565b5f866001600160a01b03163b115b93505050505b92915050565b5f5f60205f8451602086015f885af180612444576040513d5f823e3d81fd5b50505f513d9150811561245b578060011415612468565b6001600160a01b0384163b155b15610a4457604051635274afe760e01b81526001600160a01b03851660048201526024016122b4565b80825d5050565b6040516001600160a01b03838116602483015260448201839052610b6a91859182169063a9059cbb906064016119b1565b5f51602061336a5f395f51905f525460ff1661152e57604051638dfc202b60e01b815260040160405180910390fd5b612501826125be565b6040516001600160a01b038316907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b905f90a280511561254557610b6a8282612621565b610cad612693565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0054600160401b900460ff1661152e57604051631afcd79f60e31b815260040160405180910390fd5b61152e61254d565b6125a661254d565b5f51602061336a5f395f51905f52805460ff19169055565b806001600160a01b03163b5f036125f357604051634c9c8ce360e01b81526001600160a01b03821660048201526024016122b4565b5f51602061332a5f395f51905f5280546001600160a01b0319166001600160a01b0392909216919091179055565b60605f5f846001600160a01b03168460405161263d9190613313565b5f60405180830381855af49150503d805f8114612675576040519150601f19603f3d011682016040523d82523d5f602084013e61267a565b606091505b509150915061268a8583836126b2565b95945050505050565b341561152e5760405163b398979f60e01b815260040160405180910390fd5b6060826126c7576126c28261270e565b61208d565b81511580156126de57506001600160a01b0384163b155b1561270757604051639996b31560e01b81526001600160a01b03851660048201526024016122b4565b508061208d565b80511561271e5780518082602001fd5b60405163d6bda27560e01b815260040160405180910390fd5b50565b6001600160a01b0381168114612737575f5ffd5b5f5f5f60608486031215612760575f5ffd5b833561276b8161273a565b95602085013595506040909401359392505050565b5f5f83601f840112612790575f5ffd5b5081356001600160401b038111156127a6575f5ffd5b6020830191508360208260051b85010111156127c0575f5ffd5b9250929050565b5f5f83601f8401126127d7575f5ffd5b5081356001600160401b038111156127ed575f5ffd5b60208301915083602060a0830285010111156127c0575f5ffd5b5f5f5f5f6040858703121561281a575f5ffd5b84356001600160401b0381111561282f575f5ffd5b61283b87828801612780565b90955093505060208501356001600160401b03811115612859575f5ffd5b612865878288016127c7565b95989497509550505050565b5f5f5f60608486031215612883575f5ffd5b833561288e8161273a565b9250602084013561289e8161273a565b929592945050506040919091013590565b5f602082840312156128bf575f5ffd5b813561208d8161273a565b5f5f604083850312156128db575f5ffd5b82356128e68161273a565b946020939093013593505050565b5f5f60408385031215612905575f5ffd5b82356129108161273a565b915060208301356129208161273a565b809150509250929050565b6001600160801b0381168114612737575f5ffd5b5f5f5f5f5f5f60c08789031215612954575f5ffd5b863561295f8161273a565b9550602087013561296f8161273a565b9450604087013561297f8161292b565b9350606087013561298f8161292b565b9250608087013561ffff811681146129a5575f5ffd5b915060a087013562ffffff811681146129bc575f5ffd5b809150509295509295509295565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b63ffffffff81511682526020810151602083015260408101516040830152606081015160608301525f608082015160e06080850152612a3a60e08501826129ca565b905060a083015184820360a0860152612a5382826129ca565b91505060c083015184820360c086015261268a82826129ca565b60018060a01b038616815284602082015283604082015260c060608201525f612a9960c08301856129f8565b83516080840152602084015160a084015290509695505050505050565b634e487b7160e01b5f52604160045260245ffd5b604080519081016001600160401b0381118282101715612aec57612aec612ab6565b60405290565b604051606081016001600160401b0381118282101715612aec57612aec612ab6565b604051601f8201601f191681016001600160401b0381118282101715612b3c57612b3c612ab6565b604052919050565b5f6001600160401b03821115612b5c57612b5c612ab6565b50601f01601f191660200190565b5f5f60408385031215612b7b575f5ffd5b8235612b868161273a565b915060208301356001600160401b03811115612ba0575f5ffd5b8301601f81018513612bb0575f5ffd5b8035612bc3612bbe82612b44565b612b14565b818152866020838501011115612bd7575f5ffd5b816020840160208301375f602083830101528093505050509250929050565b5f5f5f5f5f60608688031215612c0a575f5ffd5b8535612c158161273a565b945060208601356001600160401b03811115612c2f575f5ffd5b612c3b88828901612780565b90955093505060408601356001600160401b03811115612c59575f5ffd5b612c65888289016127c7565b969995985093965092949392505050565b602081525f61208d60208301846129ca565b6020810160028310612ca857634e487b7160e01b5f52602160045260245ffd5b91905290565b8015158114612737575f5ffd5b5f60208284031215612ccb575f5ffd5b815161208d81612cae565b5f60208284031215612ce6575f5ffd5b5051919050565b608081525f612cff60808301866129f8565b9050612d18602083018580518252602090810151910152565b6001600160a01b03929092166060919091015292915050565b6001600160401b0381168114612737575f5ffd5b5f60408284031215612d55575f5ffd5b612d5d612aca565b825181526020928301519281019290925250919050565b5f612d81612bbe84612b44565b9050828152838383011115612d94575f5ffd5b8282602083015e5f602084830101529392505050565b5f5f5f83850360e0811215612dbd575f5ffd5b6080811215612dca575f5ffd5b50612dd3612af2565b845181526020850151612de581612d31565b6020820152612df78660408701612d45565b60408201529250612e0b8560808601612d45565b915060c08401516001600160401b03811115612e25575f5ffd5b840160408187031215612e36575f5ffd5b612e3e612aca565b815168ffffffffffffffffff81168114612e56575f5ffd5b815260208201516001600160401b03811115612e70575f5ffd5b80830192505086601f830112612e84575f5ffd5b612e9387835160208501612d74565b602082015280925050509250925092565b8281526040602082015268ffffffffffffffffff82511660408201525f60208301516040606084015261268a60808401826129ca565b5f60208284031215612eea575f5ffd5b815161208d8161273a565b5f60208284031215612f05575f5ffd5b815161208d8161292b565b602081525f61208d60208301846129f8565b5f5f5f60a08486031215612f34575f5ffd5b612f3e8585612d45565b925060408401516001600160401b03811115612f58575f5ffd5b8401601f81018613612f68575f5ffd5b80516001600160401b03811115612f8157612f81612ab6565b8060051b612f9160208201612b14565b91825260208184018101929081019089841115612fac575f5ffd5b6020850192505b8383101561304d5782516001600160401b03811115612fd0575f5ffd5b85016040818c03601f19011215612fe5575f5ffd5b612fed612aca565b6020820151815260408201516001600160401b0381111561300c575f5ffd5b6020818401019250508b601f830112613023575f5ffd5b6130328c835160208501612d74565b60208201528084525050602082019150602083019250612fb3565b80965050505050506130628560608601612d45565b90509250925092565b604081525f61307d60408301856129f8565b905082151560208301529392505050565b5f6040828403121561309e575f5ffd5b61208d8383612d45565b8082018082111561241f57634e487b7160e01b5f52601160045260245ffd5b634e487b7160e01b5f52603260045260245ffd5b5f602082840312156130eb575f5ffd5b813561208d81612cae565b63ffffffff81168114612737575f5ffd5b5f60208284031215613117575f5ffd5b813561208d816130f6565b5f813561241f81612d31565b8135613139816130f6565b63ffffffff8116905081548163ffffffff198216178355602084013561315e8161273a565b6001600160c01b03199190911690911760209190911b640100000000600160c01b0316178155600181015f60408401356131978161273a565b82546001600160a01b0319166001600160a01b03909116178255505f60608401356131c181612cae565b825460ff60a01b191681151560a01b60ff60a01b16178355905050610b6a6131eb60808501613122565b82805467ffffffffffffffff60a81b191660a89290921b67ffffffffffffffff60a81b16919091179055565b604080825281018490525f8560608301825b8781101561325957823561323c8161273a565b6001600160a01b0316825260209283019290910190600101613229565b50838103602080860191909152858252019050845f5b85811015613306578135613282816130f6565b63ffffffff16835260208201356132988161273a565b6001600160a01b0316602084015260408201356132b48161273a565b6001600160a01b0316604084015260608201356132d081612cae565b1515606084015260808201356132e581612d31565b6001600160401b0316608084015260a092830192919091019060010161326f565b5090979650505050505050565b5f82518060208501845e5f92019182525091905056fe360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc78555946a409defb00fb08ff23c8988ad687a02e1525a4fc9b7fd83443409e00cd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f03300349c08c4c7eff6f18e11e7787aea968e8ed1e26c7307dfcd722a9d753313069da164736f6c634300081c000a0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000dc515cb479a64552c5a11a57109c314e40a1a778
Deployed Bytecode
0x60806040526004361061014a575f3560e01c80634f1ef286116100b35780638322fff21161006d5780638322fff2146103d45780638456cb59146103fb578063ad3cb1cc1461040f578063b334ed861461044c578063be3319781461047f578063cc614d68146105a1575f5ffd5b80634f1ef2861461030b57806352d1902d1461031e578063579f69d2146103405780635c975abb146103675780635d8d032414610395578063666fbf73146103b5575f5ffd5b80632a82afe2116101045780632a82afe21461024e5780632b03a50c14610262578063391c13f5146102815780633c6f7179146102a05780633f4ba83a146102d057806347437514146102e4575f5ffd5b806308c73259146101555780630bf5d35e146101865780630df6e6bf1461019b5780631c20fadd146101ba5780631eb2b17e146101d957806322d932f41461022f575f5ffd5b3661015157005b5f5ffd5b348015610160575f5ffd5b506101696105e1565b6040516001600160a01b0390911681526020015b60405180910390f35b61019961019436600461274e565b61060f565b005b3480156101a6575f5ffd5b506101996101b5366004612807565b6109a9565b3480156101c5575f5ffd5b506101996101d4366004612871565b610a4a565b3480156101e4575f5ffd5b506101696101f33660046128af565b6001600160a01b039081165f9081527f78555946a409defb00fb08ff23c8988ad687a02e1525a4fc9b7fd83443409e0160205260409020541690565b34801561023a575f5ffd5b506101996102493660046128ca565b610b6f565b348015610259575f5ffd5b50610169610cb1565b34801561026d575f5ffd5b5061019961027c3660046128f4565b610d37565b34801561028c575f5ffd5b5061019961029b36600461293f565b610f07565b3480156102ab575f5ffd5b506102bf6102ba3660046128ca565b6111c1565b60405161017d959493929190612a6d565b3480156102db575f5ffd5b506101996114c9565b3480156102ef575f5ffd5b50610169734c0926ff5252a435fd19e10ed15e5a249ba19d7981565b610199610319366004612b6a565b611530565b348015610329575f5ffd5b5061033261154b565b60405190815260200161017d565b34801561034b575f5ffd5b5061016973781e90f1c8fc4611c9b7497c3b47f99ef6969cbc81565b348015610372575f5ffd5b505f51602061336a5f395f51905f525460ff16604051901515815260200161017d565b3480156103a0575f5ffd5b506103325f51602061338a5f395f51905f5281565b3480156103c0575f5ffd5b506101996103cf366004612bf6565b611566565b3480156103df575f5ffd5b5061016973eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee81565b348015610406575f5ffd5b50610199611682565b34801561041a575f5ffd5b5061043f604051806040016040528060058152602001640352e302e360dc1b81525081565b60405161017d9190612c76565b348015610457575f5ffd5b506101697f000000000000000000000000dc515cb479a64552c5a11a57109c314e40a1a77881565b34801561048a575f5ffd5b506105456104993660046128af565b6040805160a0810182525f808252602082018190529181018290526060810182905260808101919091525f51602061334a5f395f51905f526001600160a01b039283165f9081526020918252604090819020815160a081018352815463ffffffff8116825264010000000090048616938101939093526001015493841690820152600160a01b830460ff1615156060820152600160a81b9092046001600160401b031660808301525090565b60408051825163ffffffff1681526020808401516001600160a01b03908116918301919091528383015116918101919091526060808301511515908201526080918201516001600160401b03169181019190915260a00161017d565b3480156105ac575f5ffd5b506105d47f000000000000000000000000000000000000000000000000000000000000000181565b60405161017d9190612c88565b7fa5586bb7fe6c4d1a576fc53fefe6d5915940638d338769f6905020734977f500546001600160a01b031690565b6106176116e7565b5f51602061338a5f395f51905f5261062d6105e1565b604051632474521560e21b8152600481018390523360248201526001600160a01b0391909116906391d1485490604401602060405180830381865afa158015610678573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061069c9190612cbb565b6106b8576040516282b42960e81b815260040160405180910390fd5b6001600160a01b03841615806106cc575082155b156106ea57604051632a9ffab760e21b815260040160405180910390fd5b5f73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeed196001600160a01b03861601610717575047610780565b6040516370a0823160e01b81523060048201526001600160a01b038616906370a0823190602401602060405180830381865afa158015610759573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061077d9190612cd6565b90505b838110156107a157604051631e9acf1760e31b815260040160405180910390fd5b5f5f51602061334a5f395f51905f526001600160a01b038781165f9081526020928352604090819020815160a081018352815463ffffffff8116825264010000000090048416948101949094526001015491821690830152600160a01b810460ff161580156060840152600160a81b9091046001600160401b0316608083015290915061084b576108458682602001518784608001516001600160401b0316611717565b506109a1565b5f5f5f5f5f61085a8b8b6111c1565b9450945094509450945082891115610885576040516395d2fadd60e01b815260040160405180910390fd5b834710156108a6576040516357630bad60e11b815260040160405180910390fd5b6001600160a01b038b1673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee146108de576108de6001600160a01b038c16868c611930565b5f856001600160a01b031663cbef2aa9868585306040518563ffffffff1660e01b815260040161091093929190612ced565b5f6040518083038185885af115801561092b573d5f5f3e3d5ffd5b50505050506040513d5f823e601f3d908101601f191682016040526109539190810190612daa565b925050508b6001600160a01b03167fe80703b9d39d09d58bef629ae1125035c47d1da0342880315557c8ca9a2151cb8c83604051610992929190612ea4565b60405180910390a25050505050505b505050505050565b336109b26105e1565b6001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156109ed573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610a119190612eda565b6001600160a01b031614610a385760405163ab1899a760e01b815260040160405180910390fd5b610a44848484846119ed565b50505050565b610a52611eb2565b33610a5b6105e1565b6001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610a96573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610aba9190612eda565b6001600160a01b031614610ae15760405163ab1899a760e01b815260040160405180910390fd5b6001600160a01b038216610b0857604051632a9ffab760e21b815260040160405180910390fd5b610b13838383611f1f565b9050816001600160a01b0316836001600160a01b03167fe9171aeff46c37ab955b8aff908e14ae9732bd06634fa131226cfe5c25bfd2fb83604051610b5a91815260200190565b60405180910390a3610b6a612094565b505050565b610b77611eb2565b5f51602061338a5f395f51905f52610b8d6105e1565b604051632474521560e21b8152600481018390523360248201526001600160a01b0391909116906391d1485490604401602060405180830381865afa158015610bd8573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610bfc9190612cbb565b610c18576040516282b42960e81b815260040160405180910390fd5b5f610c21610cb1565b90506001600160a01b038116610c4a57604051630e8db52160e41b815260040160405180910390fd5b610c55848285611f1f565b604080516001600160a01b038088168252841660208201529081018290529093507fd8f7c78f293a52ff06285b09819d7625a13eb9080c991b7fac88cbf2b7239e899060600160405180910390a15050610cad612094565b5050565b5f7f000000000000000000000000dc515cb479a64552c5a11a57109c314e40a1a7786001600160a01b0316632a82afe26040518163ffffffff1660e01b8152600401602060405180830381865afa158015610d0e573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610d329190612eda565b905090565b33610d406105e1565b6001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610d7b573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610d9f9190612eda565b6001600160a01b031614610dc65760405163ab1899a760e01b815260040160405180910390fd5b6001600160a01b0382161580610de357506001600160a01b038116155b15610e0157604051632a9ffab760e21b815260040160405180910390fd5b806001600160a01b031663f3b977846040518163ffffffff1660e01b8152600401602060405180830381865afa158015610e3d573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610e619190612eda565b6001600160a01b0316826001600160a01b031614610e92576040516335c66c9960e01b815260040160405180910390fd5b6001600160a01b038281165f8181527f78555946a409defb00fb08ff23c8988ad687a02e1525a4fc9b7fd83443409e01602052604080822080546001600160a01b0319169486169485179055517fdd001770b3f22fb8a32aee0705976d01646098f9c1254e8064d2ee09184490189190a35050565b5f51602061338a5f395f51905f52610f1d6105e1565b604051632474521560e21b8152600481018390523360248201526001600160a01b0391909116906391d1485490604401602060405180830381865afa158015610f68573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610f8c9190612cbb565b610fa8576040516282b42960e81b815260040160405180910390fd5b6001600160a01b038781165f9081527f78555946a409defb00fb08ff23c8988ad687a02e1525a4fc9b7fd83443409e0160205260409020541680610fff57604051631d08fca760e11b815260040160405180910390fd5b60405163734d18e160e11b81526001600160a01b0388811660048301526001600160801b038816602483015261ffff861660448301525f919083169063e69a31c290606401602060405180830381865afa15801561105f573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906110839190612ef5565b9050856001600160801b0316816001600160801b031610156110b85760405163cea9e31d60e01b815260040160405180910390fd5b6110d56001600160a01b038a16836001600160801b038a16611930565b6040516335d9da3b60e11b81526001600160a01b0389811660048301526001600160801b038916602483015261ffff8716604483015262ffffff86166064830152831690636bb3b476906084016020604051808303815f875af115801561113e573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906111629190612cd6565b50604080516001600160801b03808a168252831660208201526001600160a01b03808b1692908c16917f51bf130244aab3e8452888df64bfe19c8c35a258af2516c5c1b791db7eae2f85910160405180910390a3505050505050505050565b5f5f5f6112066040518060e001604052805f63ffffffff1681526020015f81526020015f81526020015f81526020016060815260200160608152602001606081525090565b604080518082019091525f80825260208201525f5f51602061334a5f395f51905f526001600160a01b038981165f9081526020928352604090819020815160a081018352815463ffffffff811682526401000000009004841694810185905260019091015492831691810191909152600160a01b820460ff1615156060820152600160a81b9091046001600160401b0316608082015291506112bb57604051633df3d86f60e01b815260040160405180910390fd5b604081810151815160e081018352835163ffffffff1681526020808501516001600160a01b0316818301528184018b9052606082018b905283515f808252818301865260808401919091528451908152808201855260a083015283516001808252818601909552929950909260c084019291820181803683375050509052604051630d35b41560e01b81529093505f906001600160a01b03881690630d35b4159061136a908790600401612f10565b5f60405180830381865afa158015611384573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526113ab9190810190612f22565b602081018051606089015251604051633b6f743b60e01b81529098509093506001600160a01b038a169250633b6f743b91506113ed9087905f9060040161306b565b6040805180830381865afa158015611407573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061142b919061308e565b9250825f015195505f6001600160a01b0316876001600160a01b031663fc0c546a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611479573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061149d9190612eda565b6001600160a01b0316036114bd5760408401516114ba90876130a8565b95505b50509295509295909350565b6114d16105e1565b6040516317bf301f60e21b81523360048201526001600160a01b039190911690635efcc07c906024015f6040518083038186803b158015611510575f5ffd5b505afa158015611522573d5f5f3e3d5ffd5b5050505061152e6120be565b565b61153861211d565b611541826121c1565b610cad8282612236565b5f6115546122f7565b505f51602061332a5f395f51905f5290565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff1615906001600160401b03165f811580156115aa5750825b90505f826001600160401b031660011480156115c55750303b155b9050811580156115d3575080155b156115f15760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff19166001178555831561161b57845460ff60401b1916600160401b1785555b6116248a612340565b611630898989896119ed565b831561167657845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050505050565b61168a6105e1565b604051632fd0067b60e21b81523360048201526001600160a01b03919091169063bf4019ec906024015f6040518083038186803b1580156116c9575f5ffd5b505afa1580156116db573d5f5f3e3d5ffd5b5050505061152e612392565b5f51602061336a5f395f51905f525460ff161561152e5760405163d93c066560e01b815260040160405180910390fd5b5f73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeed196001600160a01b038616016117c85760405163b2267a7b60e01b81526001600160a01b038516600482015260248101849052608060448201525f60848201526064810183905273781e90f1c8fc4611c9b7497c3b47f99ef6969cbc9063b2267a7b90859060a4015f604051808303818588803b1580156117ac575f5ffd5b505af11580156117be573d5f5f3e3d5ffd5b50505050506118da565b6040516343c6674160e01b81526001600160a01b03861660048201525f90734c0926ff5252a435fd19e10ed15e5a249ba19d79906343c6674190602401602060405180830381865afa158015611820573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906118449190612eda565b905061185a6001600160a01b0387168286611930565b60405163a93a4af960e01b81526001600160a01b038088166004830152861660248201526044810185905260648101849052734c0926ff5252a435fd19e10ed15e5a249ba19d799063a93a4af9906084015f604051808303815f87803b1580156118c2575f5ffd5b505af11580156118d4573d5f5f3e3d5ffd5b50505050505b836001600160a01b0316856001600160a01b03167f89fb53d0c0f0f36db1043a54a7bb546fd820379fef6a90aec7293c3cf7ca13c98560405161191f91815260200190565b60405180910390a350909392505050565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b17905261198184826123da565b610a44576040516001600160a01b0384811660248301525f60448301526119e391869182169063095ea7b3906064015b604051602081830303815290604052915060e01b6020820180516001600160e01b038381831617835250505050612425565b610a448482612425565b82818114611a0e5760405163512509d360e11b815260040160405180910390fd5b5f51602061334a5f395f51905f525f5b82811015611e6c57848482818110611a3857611a386130c7565b905060a002016060016020810190611a5091906130db565b15611b5a575f878783818110611a6857611a686130c7565b9050602002016020810190611a7d91906128af565b6001600160a01b03161480611ac357505f858583818110611aa057611aa06130c7565b905060a002016020016020810190611ab891906128af565b6001600160a01b0316145b80611b0057505f858583818110611adc57611adc6130c7565b905060a002016040016020810190611af491906128af565b6001600160a01b031614155b80611b375750848482818110611b1857611b186130c7565b611b2e92602060a0909202019081019150613107565b63ffffffff1615155b15611b5557604051632a9ffab760e21b815260040160405180910390fd5b611dfd565b5f878783818110611b6d57611b6d6130c7565b9050602002016020810190611b8291906128af565b6001600160a01b03161480611bc857505f858583818110611ba557611ba56130c7565b905060a002016020016020810190611bbd91906128af565b6001600160a01b0316145b80611c0457505f858583818110611be157611be16130c7565b905060a002016040016020810190611bf991906128af565b6001600160a01b0316145b15611c2257604051632a9ffab760e21b815260040160405180910390fd5b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee878783818110611c4957611c496130c7565b9050602002016020810190611c5e91906128af565b6001600160a01b031603611d1d575f858583818110611c7f57611c7f6130c7565b905060a002016040016020810190611c9791906128af565b6001600160a01b031663fc0c546a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611cd2573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611cf69190612eda565b6001600160a01b031614611b555760405163b0fcd49560e01b815260040160405180910390fd5b868682818110611d2f57611d2f6130c7565b9050602002016020810190611d4491906128af565b6001600160a01b0316858583818110611d5f57611d5f6130c7565b905060a002016040016020810190611d7791906128af565b6001600160a01b031663fc0c546a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611db2573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611dd69190612eda565b6001600160a01b031614611dfd5760405163b0fcd49560e01b815260040160405180910390fd5b848482818110611e0f57611e0f6130c7565b905060a00201825f015f898985818110611e2b57611e2b6130c7565b9050602002016020810190611e4091906128af565b6001600160a01b0316815260208101919091526040015f20611e62828261312e565b5050600101611a1e565b507f93c0d4c04381b5de5cd6f8911c1885ffa461192db849a9e32edaea31816d5a9c86868686604051611ea29493929190613217565b60405180910390a1505050505050565b7f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f005c15611ef257604051633ee5aeb560e01b815260040160405180910390fd5b61152e60017f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f005b90612491565b5f73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeed196001600160a01b03851601611fe657815f03611f50574791505b815f03611f705760405163c740c32d60e01b815260040160405180910390fd5b5f836001600160a01b0316836040515f6040518083038185875af1925050503d805f8114611fb9576040519150601f19603f3d011682016040523d82523d5f602084013e611fbe565b606091505b5050905080611fe057604051632607a81f60e11b815260040160405180910390fd5b5061208a565b815f03612056576040516370a0823160e01b81523060048201526001600160a01b038516906370a0823190602401602060405180830381865afa15801561202f573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906120539190612cd6565b91505b815f036120765760405163c740c32d60e01b815260040160405180910390fd5b61208a6001600160a01b0385168484612498565b50805b9392505050565b61152e5f7f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00611f19565b6120c66124c9565b5f51602061336a5f395f51905f52805460ff191681557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a150565b306001600160a01b037f00000000000000000000000049859e57be078a0b9a396cbbf1b5197d13caee921614806121a357507f00000000000000000000000049859e57be078a0b9a396cbbf1b5197d13caee926001600160a01b03166121975f51602061332a5f395f51905f52546001600160a01b031690565b6001600160a01b031614155b1561152e5760405163703e46dd60e11b815260040160405180910390fd5b5f7fa5586bb7fe6c4d1a576fc53fefe6d5915940638d338769f6905020734977f5008054604051634766d28760e01b81523360048201529192506001600160a01b031690634766d287906024015f6040518083038186803b158015612224575f5ffd5b505afa1580156109a1573d5f5f3e3d5ffd5b816001600160a01b03166352d1902d6040518163ffffffff1660e01b8152600401602060405180830381865afa925050508015612290575060408051601f3d908101601f1916820190925261228d91810190612cd6565b60015b6122bd57604051634c9c8ce360e01b81526001600160a01b03831660048201526024015b60405180910390fd5b5f51602061332a5f395f51905f5281146122ed57604051632a87526960e21b8152600481018290526024016122b4565b610b6a83836124f8565b306001600160a01b037f00000000000000000000000049859e57be078a0b9a396cbbf1b5197d13caee92161461152e5760405163703e46dd60e11b815260040160405180910390fd5b61234861254d565b7fa5586bb7fe6c4d1a576fc53fefe6d5915940638d338769f6905020734977f50080546001600160a01b0319166001600160a01b03831617815561238a612596565b610cad61259e565b61239a6116e7565b5f51602061336a5f395f51905f52805460ff191660011781557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258336120ff565b5f5f5f5f60205f8651602088015f8a5af192503d91505f5190508280156124195750811561240b5780600114612419565b5f866001600160a01b03163b115b93505050505b92915050565b5f5f60205f8451602086015f885af180612444576040513d5f823e3d81fd5b50505f513d9150811561245b578060011415612468565b6001600160a01b0384163b155b15610a4457604051635274afe760e01b81526001600160a01b03851660048201526024016122b4565b80825d5050565b6040516001600160a01b03838116602483015260448201839052610b6a91859182169063a9059cbb906064016119b1565b5f51602061336a5f395f51905f525460ff1661152e57604051638dfc202b60e01b815260040160405180910390fd5b612501826125be565b6040516001600160a01b038316907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b905f90a280511561254557610b6a8282612621565b610cad612693565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0054600160401b900460ff1661152e57604051631afcd79f60e31b815260040160405180910390fd5b61152e61254d565b6125a661254d565b5f51602061336a5f395f51905f52805460ff19169055565b806001600160a01b03163b5f036125f357604051634c9c8ce360e01b81526001600160a01b03821660048201526024016122b4565b5f51602061332a5f395f51905f5280546001600160a01b0319166001600160a01b0392909216919091179055565b60605f5f846001600160a01b03168460405161263d9190613313565b5f60405180830381855af49150503d805f8114612675576040519150601f19603f3d011682016040523d82523d5f602084013e61267a565b606091505b509150915061268a8583836126b2565b95945050505050565b341561152e5760405163b398979f60e01b815260040160405180910390fd5b6060826126c7576126c28261270e565b61208d565b81511580156126de57506001600160a01b0384163b155b1561270757604051639996b31560e01b81526001600160a01b03851660048201526024016122b4565b508061208d565b80511561271e5780518082602001fd5b60405163d6bda27560e01b815260040160405180910390fd5b50565b6001600160a01b0381168114612737575f5ffd5b5f5f5f60608486031215612760575f5ffd5b833561276b8161273a565b95602085013595506040909401359392505050565b5f5f83601f840112612790575f5ffd5b5081356001600160401b038111156127a6575f5ffd5b6020830191508360208260051b85010111156127c0575f5ffd5b9250929050565b5f5f83601f8401126127d7575f5ffd5b5081356001600160401b038111156127ed575f5ffd5b60208301915083602060a0830285010111156127c0575f5ffd5b5f5f5f5f6040858703121561281a575f5ffd5b84356001600160401b0381111561282f575f5ffd5b61283b87828801612780565b90955093505060208501356001600160401b03811115612859575f5ffd5b612865878288016127c7565b95989497509550505050565b5f5f5f60608486031215612883575f5ffd5b833561288e8161273a565b9250602084013561289e8161273a565b929592945050506040919091013590565b5f602082840312156128bf575f5ffd5b813561208d8161273a565b5f5f604083850312156128db575f5ffd5b82356128e68161273a565b946020939093013593505050565b5f5f60408385031215612905575f5ffd5b82356129108161273a565b915060208301356129208161273a565b809150509250929050565b6001600160801b0381168114612737575f5ffd5b5f5f5f5f5f5f60c08789031215612954575f5ffd5b863561295f8161273a565b9550602087013561296f8161273a565b9450604087013561297f8161292b565b9350606087013561298f8161292b565b9250608087013561ffff811681146129a5575f5ffd5b915060a087013562ffffff811681146129bc575f5ffd5b809150509295509295509295565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b63ffffffff81511682526020810151602083015260408101516040830152606081015160608301525f608082015160e06080850152612a3a60e08501826129ca565b905060a083015184820360a0860152612a5382826129ca565b91505060c083015184820360c086015261268a82826129ca565b60018060a01b038616815284602082015283604082015260c060608201525f612a9960c08301856129f8565b83516080840152602084015160a084015290509695505050505050565b634e487b7160e01b5f52604160045260245ffd5b604080519081016001600160401b0381118282101715612aec57612aec612ab6565b60405290565b604051606081016001600160401b0381118282101715612aec57612aec612ab6565b604051601f8201601f191681016001600160401b0381118282101715612b3c57612b3c612ab6565b604052919050565b5f6001600160401b03821115612b5c57612b5c612ab6565b50601f01601f191660200190565b5f5f60408385031215612b7b575f5ffd5b8235612b868161273a565b915060208301356001600160401b03811115612ba0575f5ffd5b8301601f81018513612bb0575f5ffd5b8035612bc3612bbe82612b44565b612b14565b818152866020838501011115612bd7575f5ffd5b816020840160208301375f602083830101528093505050509250929050565b5f5f5f5f5f60608688031215612c0a575f5ffd5b8535612c158161273a565b945060208601356001600160401b03811115612c2f575f5ffd5b612c3b88828901612780565b90955093505060408601356001600160401b03811115612c59575f5ffd5b612c65888289016127c7565b969995985093965092949392505050565b602081525f61208d60208301846129ca565b6020810160028310612ca857634e487b7160e01b5f52602160045260245ffd5b91905290565b8015158114612737575f5ffd5b5f60208284031215612ccb575f5ffd5b815161208d81612cae565b5f60208284031215612ce6575f5ffd5b5051919050565b608081525f612cff60808301866129f8565b9050612d18602083018580518252602090810151910152565b6001600160a01b03929092166060919091015292915050565b6001600160401b0381168114612737575f5ffd5b5f60408284031215612d55575f5ffd5b612d5d612aca565b825181526020928301519281019290925250919050565b5f612d81612bbe84612b44565b9050828152838383011115612d94575f5ffd5b8282602083015e5f602084830101529392505050565b5f5f5f83850360e0811215612dbd575f5ffd5b6080811215612dca575f5ffd5b50612dd3612af2565b845181526020850151612de581612d31565b6020820152612df78660408701612d45565b60408201529250612e0b8560808601612d45565b915060c08401516001600160401b03811115612e25575f5ffd5b840160408187031215612e36575f5ffd5b612e3e612aca565b815168ffffffffffffffffff81168114612e56575f5ffd5b815260208201516001600160401b03811115612e70575f5ffd5b80830192505086601f830112612e84575f5ffd5b612e9387835160208501612d74565b602082015280925050509250925092565b8281526040602082015268ffffffffffffffffff82511660408201525f60208301516040606084015261268a60808401826129ca565b5f60208284031215612eea575f5ffd5b815161208d8161273a565b5f60208284031215612f05575f5ffd5b815161208d8161292b565b602081525f61208d60208301846129f8565b5f5f5f60a08486031215612f34575f5ffd5b612f3e8585612d45565b925060408401516001600160401b03811115612f58575f5ffd5b8401601f81018613612f68575f5ffd5b80516001600160401b03811115612f8157612f81612ab6565b8060051b612f9160208201612b14565b91825260208184018101929081019089841115612fac575f5ffd5b6020850192505b8383101561304d5782516001600160401b03811115612fd0575f5ffd5b85016040818c03601f19011215612fe5575f5ffd5b612fed612aca565b6020820151815260408201516001600160401b0381111561300c575f5ffd5b6020818401019250508b601f830112613023575f5ffd5b6130328c835160208501612d74565b60208201528084525050602082019150602083019250612fb3565b80965050505050506130628560608601612d45565b90509250925092565b604081525f61307d60408301856129f8565b905082151560208301529392505050565b5f6040828403121561309e575f5ffd5b61208d8383612d45565b8082018082111561241f57634e487b7160e01b5f52601160045260245ffd5b634e487b7160e01b5f52603260045260245ffd5b5f602082840312156130eb575f5ffd5b813561208d81612cae565b63ffffffff81168114612737575f5ffd5b5f60208284031215613117575f5ffd5b813561208d816130f6565b5f813561241f81612d31565b8135613139816130f6565b63ffffffff8116905081548163ffffffff198216178355602084013561315e8161273a565b6001600160c01b03199190911690911760209190911b640100000000600160c01b0316178155600181015f60408401356131978161273a565b82546001600160a01b0319166001600160a01b03909116178255505f60608401356131c181612cae565b825460ff60a01b191681151560a01b60ff60a01b16178355905050610b6a6131eb60808501613122565b82805467ffffffffffffffff60a81b191660a89290921b67ffffffffffffffff60a81b16919091179055565b604080825281018490525f8560608301825b8781101561325957823561323c8161273a565b6001600160a01b0316825260209283019290910190600101613229565b50838103602080860191909152858252019050845f5b85811015613306578135613282816130f6565b63ffffffff16835260208201356132988161273a565b6001600160a01b0316602084015260408201356132b48161273a565b6001600160a01b0316604084015260608201356132d081612cae565b1515606084015260808201356132e581612d31565b6001600160401b0316608084015260a092830192919091019060010161326f565b5090979650505050505050565b5f82518060208501845e5f92019182525091905056fe360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc78555946a409defb00fb08ff23c8988ad687a02e1525a4fc9b7fd83443409e00cd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f03300349c08c4c7eff6f18e11e7787aea968e8ed1e26c7307dfcd722a9d753313069da164736f6c634300081c000a
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000dc515cb479a64552c5a11a57109c314e40a1a778
-----Decoded View---------------
Arg [0] : _binSponsor (uint8): 1
Arg [1] : _dataProvider (address): 0xDC515Cb479a64552c5A11a57109C314E40A1A778
-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 0000000000000000000000000000000000000000000000000000000000000001
Arg [1] : 000000000000000000000000dc515cb479a64552c5a11a57109c314e40a1a778
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.