Source Code
Overview
ETH Balance
0 ETH
ETH Value
$0.00| Transaction Hash |
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
Latest 1 internal transaction
Advanced mode:
| Parent Transaction Hash | Block | From | To | |||
|---|---|---|---|---|---|---|
| 4976860 | 193 days ago | Contract Creation | 0 ETH |
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:
BoringSolver
Compiler Version
v0.8.21+commit.d9974bed
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.21;
import {Auth, Authority} from "@solmate/auth/Auth.sol";
import {
BoringOnChainQueue, ERC20, SafeTransferLib
} from "../../../../src/base/Roles/BoringQueue/BoringOnChainQueue.sol";
import {IBoringSolver} from "../../../../src/base/Roles/BoringQueue/IBoringSolver.sol";
import {FixedPointMathLib} from "@solmate/utils/FixedPointMathLib.sol";
import {TellerWithMultiAssetSupport} from "../../../../src/base/Roles/TellerWithMultiAssetSupport.sol";
import {Multicall} from "@openzeppelin/contracts/utils/Multicall.sol";
contract BoringSolver is IBoringSolver, Auth, Multicall {
using SafeTransferLib for ERC20;
using FixedPointMathLib for uint256;
// ========================================= ENUMS =========================================
enum SolveType {
BORING_REDEEM, // Fill multiple user requests with a single transaction.
BORING_REDEEM_MINT // Fill multiple user requests to redeem shares and mint new shares.
}
//============================== ERRORS ===============================
error BoringSolver___WrongInitiator();
error BoringSolver___BoringVaultTellerMismatch(address boringVault, address teller);
error BoringSolver___OnlySelf();
error BoringSolver___FailedToSolve();
error BoringSolver___OnlyQueue();
//============================== IMMUTABLES ===============================
BoringOnChainQueue internal immutable queue;
constructor(address _owner, address _auth, address _queue) Auth(_owner, Authority(_auth)) {
queue = BoringOnChainQueue(_queue);
}
//============================== ADMIN FUNCTIONS ===============================
/**
* @notice Allows the owner to rescue tokens from the contract.
* @dev This should not normally be used, but it is possible that when performing a MIGRATION_REDEEM,
* the redemption of Cellar shares will return assets other than BoringVault shares.
* If the amount of assets is significant, it is very likely the solve will revert, but it is
* not guaranteed to revert, hence this function.
*/
function rescueTokens(ERC20 token, uint256 amount) external requiresAuth {
if (amount == type(uint256).max) amount = token.balanceOf(address(this));
token.safeTransfer(msg.sender, amount);
}
//============================== ADMIN SOLVE FUNCTIONS ===============================
/**
* @notice Solve multiple user requests to redeem Boring Vault shares.
*/
function boringRedeemSolve(BoringOnChainQueue.OnChainWithdraw[] calldata requests, address teller)
external
requiresAuth
{
bytes memory solveData = abi.encode(SolveType.BORING_REDEEM, msg.sender, teller, true);
queue.solveOnChainWithdraws(requests, solveData, address(this));
}
/**
* @notice Solve multiple user requests to redeem Boring Vault shares and mint new Boring Vault shares.
* @dev In order for this to work, the fromAccountant must have the toBoringVaults rate provider setup.
*/
function boringRedeemMintSolve(
BoringOnChainQueue.OnChainWithdraw[] calldata requests,
address fromTeller,
address toTeller,
address intermediateAsset
) external requiresAuth {
bytes memory solveData =
abi.encode(SolveType.BORING_REDEEM_MINT, msg.sender, fromTeller, toTeller, intermediateAsset, true);
queue.solveOnChainWithdraws(requests, solveData, address(this));
}
//============================== USER SOLVE FUNCTIONS ===============================
/**
* @notice Allows a user to solve their own request to redeem Boring Vault shares.
*/
function boringRedeemSelfSolve(BoringOnChainQueue.OnChainWithdraw calldata request, address teller)
external
requiresAuth
{
if (request.user != msg.sender) revert BoringSolver___OnlySelf();
BoringOnChainQueue.OnChainWithdraw[] memory requests = new BoringOnChainQueue.OnChainWithdraw[](1);
requests[0] = request;
bytes memory solveData = abi.encode(SolveType.BORING_REDEEM, msg.sender, teller, false);
queue.solveOnChainWithdraws(requests, solveData, address(this));
}
/**
* @notice Allows a user to solve their own request to redeem Boring Vault shares and mint new Boring Vault shares.
* @dev In order for this to work, the fromAccountant must have the toBoringVaults rate provider setup.
*/
function boringRedeemMintSelfSolve(
BoringOnChainQueue.OnChainWithdraw calldata request,
address fromTeller,
address toTeller,
address intermediateAsset
) external requiresAuth {
if (request.user != msg.sender) revert BoringSolver___OnlySelf();
BoringOnChainQueue.OnChainWithdraw[] memory requests = new BoringOnChainQueue.OnChainWithdraw[](1);
requests[0] = request;
bytes memory solveData =
abi.encode(SolveType.BORING_REDEEM_MINT, msg.sender, fromTeller, toTeller, intermediateAsset, false);
queue.solveOnChainWithdraws(requests, solveData, address(this));
}
//============================== IBORINGSOLVER FUNCTIONS ===============================
/**
* @notice Implementation of the IBoringSolver interface.
*/
function boringSolve(
address initiator,
address boringVault,
address solveAsset,
uint256 totalShares,
uint256 requiredAssets,
bytes calldata solveData
) external requiresAuth {
if (msg.sender != address(queue)) revert BoringSolver___OnlyQueue();
if (initiator != address(this)) revert BoringSolver___WrongInitiator();
SolveType solveType = abi.decode(solveData, (SolveType));
if (solveType == SolveType.BORING_REDEEM) {
_boringRedeemSolve(solveData, boringVault, solveAsset, totalShares, requiredAssets);
} else if (solveType == SolveType.BORING_REDEEM_MINT) {
_boringRedeemMintSolve(solveData, boringVault, solveAsset, totalShares, requiredAssets);
} else {
// Added for future protection, if another enum is added, txs with that enum will revert,
// if no changes are made here.
revert BoringSolver___FailedToSolve();
}
}
//============================== INTERNAL SOLVE FUNCTIONS ===============================
/**
* @notice Internal helper function to solve multiple user requests to redeem Boring Vault shares.
*/
function _boringRedeemSolve(
bytes calldata solveData,
address boringVault,
address solveAsset,
uint256 totalShares,
uint256 requiredAssets
) internal {
(, address solverOrigin, TellerWithMultiAssetSupport teller, bool excessToSolver) =
abi.decode(solveData, (SolveType, address, TellerWithMultiAssetSupport, bool));
if (boringVault != address(teller.vault())) {
revert BoringSolver___BoringVaultTellerMismatch(boringVault, address(teller));
}
ERC20 asset = ERC20(solveAsset);
// Redeem the Boring Vault shares for Solve Asset.
uint256 assetsOut = teller.bulkWithdraw(asset, totalShares, requiredAssets, address(this));
// Transfer excess assets to solver origin or Boring Vault.
// Assets are sent to solver to cover gas fees.
// But if users are self solving, then the excess assets go to the Boring Vault.
if (excessToSolver) {
asset.safeTransfer(solverOrigin, assetsOut - requiredAssets);
} else {
asset.safeTransfer(boringVault, assetsOut - requiredAssets);
}
// Approve Boring Queue to spend the required assets.
asset.approve(address(queue), requiredAssets);
}
/**
* @notice Internal helper function to solve multiple user requests to redeem Boring Vault shares and mint new Boring Vault shares.
*/
function _boringRedeemMintSolve(
bytes calldata solveData,
address fromBoringVault,
address toBoringVault,
uint256 totalShares,
uint256 requiredShares
) internal {
(
,
address solverOrigin,
TellerWithMultiAssetSupport fromTeller,
TellerWithMultiAssetSupport toTeller,
ERC20 intermediateAsset,
bool excessToSolver
) = abi.decode(
solveData, (SolveType, address, TellerWithMultiAssetSupport, TellerWithMultiAssetSupport, ERC20, bool)
);
if (fromBoringVault != address(fromTeller.vault())) {
revert BoringSolver___BoringVaultTellerMismatch(fromBoringVault, address(fromTeller));
}
if (toBoringVault != address(toTeller.vault())) {
revert BoringSolver___BoringVaultTellerMismatch(toBoringVault, address(toTeller));
}
// Redeem the fromBoringVault shares for Intermediate Asset.
uint256 excessAssets = fromTeller.bulkWithdraw(intermediateAsset, totalShares, 0, address(this));
{
// Determine how many assets are needed to mint requiredAssets worth of toBoringVault shares.
// Note mulDivUp is used to ensure we always mint enough assets to cover the requiredShares.
uint256 assetsToMintRequiredShares = requiredShares.mulDivUp(
toTeller.accountant().getRateInQuoteSafe(intermediateAsset), BoringOnChainQueue(queue).ONE_SHARE()
);
// Remove assetsToMintRequiredShares from excessAssets.
excessAssets = excessAssets - assetsToMintRequiredShares;
// Approve toBoringVault to spend the Intermediate Asset.
intermediateAsset.safeApprove(toBoringVault, assetsToMintRequiredShares);
// Mint to BoringVault shares using Intermediate Asset.
toTeller.bulkDeposit(intermediateAsset, assetsToMintRequiredShares, requiredShares, address(this));
}
// Transfer excess assets to solver origin or Boring Vault.
// Assets are sent to solver to cover gas fees.
// But if users are self solving, then the excess assets go to the from Boring Vault.
if (excessToSolver) {
intermediateAsset.safeTransfer(solverOrigin, excessAssets);
} else {
intermediateAsset.safeTransfer(fromBoringVault, excessAssets);
}
// Approve Boring Queue to spend the required assets.
ERC20(toBoringVault).approve(address(queue), requiredShares);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC1155/utils/ERC1155Holder.sol)
pragma solidity ^0.8.20;
import {IERC165, ERC165} from "../../../utils/introspection/ERC165.sol";
import {IERC1155Receiver} from "../IERC1155Receiver.sol";
/**
* @dev Simple implementation of `IERC1155Receiver` that will allow a contract to hold ERC-1155 tokens.
*
* IMPORTANT: When inheriting this contract, you must include a way to use the received tokens, otherwise they will be
* stuck.
*/
abstract contract ERC1155Holder is ERC165, IERC1155Receiver {
/// @inheritdoc IERC165
function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
return interfaceId == type(IERC1155Receiver).interfaceId || super.supportsInterface(interfaceId);
}
function onERC1155Received(
address,
address,
uint256,
uint256,
bytes memory
) public virtual override returns (bytes4) {
return this.onERC1155Received.selector;
}
function onERC1155BatchReceived(
address,
address,
uint256[] memory,
uint256[] memory,
bytes memory
) public virtual override returns (bytes4) {
return this.onERC1155BatchReceived.selector;
}
}// 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") {
revert(add(returndata, 0x20), mload(returndata))
}
} else {
revert Errors.FailedCall();
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/ERC165.sol)
pragma solidity ^0.8.20;
import {IERC165} from "./IERC165.sol";
/**
* @dev Implementation of the {IERC165} interface.
*
* Contracts that want to implement ERC-165 should inherit from this contract and override {supportsInterface} to check
* for the additional interface id that will be supported. For example:
*
* ```solidity
* function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
* return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
* }
* ```
*/
abstract contract ERC165 is IERC165 {
/// @inheritdoc IERC165
function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
/// @notice Gas optimized reentrancy protection for smart contracts.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/ReentrancyGuard.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/security/ReentrancyGuard.sol)
abstract contract ReentrancyGuard {
uint256 private locked = 1;
modifier nonReentrant() virtual {
require(locked == 1, "REENTRANCY");
locked = 2;
_;
locked = 1;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC1155/IERC1155Receiver.sol)
pragma solidity ^0.8.20;
import {IERC165} from "../../utils/introspection/IERC165.sol";
/**
* @dev Interface that must be implemented by smart contracts in order to receive
* ERC-1155 token transfers.
*/
interface IERC1155Receiver is IERC165 {
/**
* @dev Handles the receipt of a single ERC-1155 token type. This function is
* called at the end of a `safeTransferFrom` after the balance has been updated.
*
* NOTE: To accept the transfer, this must return
* `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
* (i.e. 0xf23a6e61, or its own function selector).
*
* @param operator The address which initiated the transfer (i.e. msg.sender)
* @param from The address which previously owned the token
* @param id The ID of the token being transferred
* @param value The amount of tokens being transferred
* @param data Additional data with no specified format
* @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` if transfer is allowed
*/
function onERC1155Received(
address operator,
address from,
uint256 id,
uint256 value,
bytes calldata data
) external returns (bytes4);
/**
* @dev Handles the receipt of a multiple ERC-1155 token types. This function
* is called at the end of a `safeBatchTransferFrom` after the balances have
* been updated.
*
* NOTE: To accept the transfer(s), this must return
* `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`
* (i.e. 0xbc197c81, or its own function selector).
*
* @param operator The address which initiated the batch transfer (i.e. msg.sender)
* @param from The address which previously owned the token
* @param ids An array containing ids of each token being transferred (order and length must match values array)
* @param values An array containing amounts of each token being transferred (order and length must match ids array)
* @param data Additional data with no specified format
* @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` if transfer is allowed
*/
function onERC1155BatchReceived(
address operator,
address from,
uint256[] calldata ids,
uint256[] calldata values,
bytes calldata data
) external returns (bytes4);
}// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
/// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)
/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)
/// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.
abstract contract ERC20 {
/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
event Transfer(address indexed from, address indexed to, uint256 amount);
event Approval(address indexed owner, address indexed spender, uint256 amount);
/*//////////////////////////////////////////////////////////////
METADATA STORAGE
//////////////////////////////////////////////////////////////*/
string public name;
string public symbol;
uint8 public immutable decimals;
/*//////////////////////////////////////////////////////////////
ERC20 STORAGE
//////////////////////////////////////////////////////////////*/
uint256 public totalSupply;
mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256)) public allowance;
/*//////////////////////////////////////////////////////////////
EIP-2612 STORAGE
//////////////////////////////////////////////////////////////*/
uint256 internal immutable INITIAL_CHAIN_ID;
bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;
mapping(address => uint256) public nonces;
/*//////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/
constructor(
string memory _name,
string memory _symbol,
uint8 _decimals
) {
name = _name;
symbol = _symbol;
decimals = _decimals;
INITIAL_CHAIN_ID = block.chainid;
INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
}
/*//////////////////////////////////////////////////////////////
ERC20 LOGIC
//////////////////////////////////////////////////////////////*/
function approve(address spender, uint256 amount) public virtual returns (bool) {
allowance[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
return true;
}
function transfer(address to, uint256 amount) public virtual returns (bool) {
balanceOf[msg.sender] -= amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(msg.sender, to, amount);
return true;
}
function transferFrom(
address from,
address to,
uint256 amount
) public virtual returns (bool) {
uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.
if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;
balanceOf[from] -= amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(from, to, amount);
return true;
}
/*//////////////////////////////////////////////////////////////
EIP-2612 LOGIC
//////////////////////////////////////////////////////////////*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) public virtual {
require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");
// Unchecked because the only math done is incrementing
// the owner's nonce which cannot realistically overflow.
unchecked {
address recoveredAddress = ecrecover(
keccak256(
abi.encodePacked(
"\x19\x01",
DOMAIN_SEPARATOR(),
keccak256(
abi.encode(
keccak256(
"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
),
owner,
spender,
value,
nonces[owner]++,
deadline
)
)
)
),
v,
r,
s
);
require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");
allowance[recoveredAddress][spender] = value;
}
emit Approval(owner, spender, value);
}
function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
}
function computeDomainSeparator() internal view virtual returns (bytes32) {
return
keccak256(
abi.encode(
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
keccak256(bytes(name)),
keccak256("1"),
block.chainid,
address(this)
)
);
}
/*//////////////////////////////////////////////////////////////
INTERNAL MINT/BURN LOGIC
//////////////////////////////////////////////////////////////*/
function _mint(address to, uint256 amount) internal virtual {
totalSupply += amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(address(0), to, amount);
}
function _burn(address from, uint256 amount) internal virtual {
balanceOf[from] -= amount;
// Cannot underflow because a user's balance
// will never be larger than the total supply.
unchecked {
totalSupply -= amount;
}
emit Transfer(from, address(0), amount);
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.21;
interface BeforeTransferHook {
function beforeTransfer(address from, address to, address operator) external view;
}// SPDX-License-Identifier: UNLICENSED
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
pragma solidity ^0.8.0;
interface IRateProvider {
function getRate() external view returns (uint256);
}// 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);
}// 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: AGPL-3.0-only
pragma solidity >=0.8.0;
import {ERC20} from "../tokens/ERC20.sol";
/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
/// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer.
library SafeTransferLib {
/*//////////////////////////////////////////////////////////////
ETH OPERATIONS
//////////////////////////////////////////////////////////////*/
function safeTransferETH(address to, uint256 amount) internal {
bool success;
/// @solidity memory-safe-assembly
assembly {
// Transfer the ETH and store if it succeeded or not.
success := call(gas(), to, amount, 0, 0, 0, 0)
}
require(success, "ETH_TRANSFER_FAILED");
}
/*//////////////////////////////////////////////////////////////
ERC20 OPERATIONS
//////////////////////////////////////////////////////////////*/
function safeTransferFrom(
ERC20 token,
address from,
address to,
uint256 amount
) internal {
bool success;
/// @solidity memory-safe-assembly
assembly {
// Get a pointer to some free memory.
let freeMemoryPointer := mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.
mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), and(from, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "from" argument.
mstore(add(freeMemoryPointer, 36), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
mstore(add(freeMemoryPointer, 68), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.
// We use 100 because the length of our calldata totals up like so: 4 + 32 * 3.
// We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
success := call(gas(), token, 0, freeMemoryPointer, 100, 0, 32)
// Set success to whether the call reverted, if not we check it either
// returned exactly 1 (can't just be non-zero data), or had no return data and token has code.
if and(iszero(and(eq(mload(0), 1), gt(returndatasize(), 31))), success) {
success := iszero(or(iszero(extcodesize(token)), returndatasize()))
}
}
require(success, "TRANSFER_FROM_FAILED");
}
function safeTransfer(
ERC20 token,
address to,
uint256 amount
) internal {
bool success;
/// @solidity memory-safe-assembly
assembly {
// Get a pointer to some free memory.
let freeMemoryPointer := mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.
mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.
// We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
// We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
success := call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
// Set success to whether the call reverted, if not we check it either
// returned exactly 1 (can't just be non-zero data), or had no return data and token has code.
if and(iszero(and(eq(mload(0), 1), gt(returndatasize(), 31))), success) {
success := iszero(or(iszero(extcodesize(token)), returndatasize()))
}
}
require(success, "TRANSFER_FAILED");
}
function safeApprove(
ERC20 token,
address to,
uint256 amount
) internal {
bool success;
/// @solidity memory-safe-assembly
assembly {
// Get a pointer to some free memory.
let freeMemoryPointer := mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.
mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.
// We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
// We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
success := call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
// Set success to whether the call reverted, if not we check it either
// returned exactly 1 (can't just be non-zero data), or had no return data and token has code.
if and(iszero(and(eq(mload(0), 1), gt(returndatasize(), 31))), success) {
success := iszero(or(iszero(extcodesize(token)), returndatasize()))
}
}
require(success, "APPROVE_FAILED");
}
}// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
/// @notice Provides a flexible and updatable auth pattern which is completely separate from application logic.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/auth/Auth.sol)
/// @author Modified from Dappsys (https://github.com/dapphub/ds-auth/blob/master/src/auth.sol)
abstract contract Auth {
event OwnershipTransferred(address indexed user, address indexed newOwner);
event AuthorityUpdated(address indexed user, Authority indexed newAuthority);
address public owner;
Authority public authority;
constructor(address _owner, Authority _authority) {
owner = _owner;
authority = _authority;
emit OwnershipTransferred(msg.sender, _owner);
emit AuthorityUpdated(msg.sender, _authority);
}
modifier requiresAuth() virtual {
require(isAuthorized(msg.sender, msg.sig), "UNAUTHORIZED");
_;
}
function isAuthorized(address user, bytes4 functionSig) internal view virtual returns (bool) {
Authority auth = authority; // Memoizing authority saves us a warm SLOAD, around 100 gas.
// Checking if the caller is the owner only after calling the authority saves gas in most cases, but be
// aware that this makes protected functions uncallable even to the owner if the authority is out of order.
return (address(auth) != address(0) && auth.canCall(user, address(this), functionSig)) || user == owner;
}
function setAuthority(Authority newAuthority) public virtual {
// We check if the caller is the owner first because we want to ensure they can
// always swap out the authority even if it's reverting or using up a lot of gas.
require(msg.sender == owner || authority.canCall(msg.sender, address(this), msg.sig));
authority = newAuthority;
emit AuthorityUpdated(msg.sender, newAuthority);
}
function transferOwnership(address newOwner) public virtual requiresAuth {
owner = newOwner;
emit OwnershipTransferred(msg.sender, newOwner);
}
}
/// @notice A generic interface for a contract which provides authorization data to an Auth instance.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/auth/Auth.sol)
/// @author Modified from Dappsys (https://github.com/dapphub/ds-auth/blob/master/src/auth.sol)
interface Authority {
function canCall(
address user,
address target,
bytes4 functionSig
) external view returns (bool);
}// 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: AGPL-3.0-only
pragma solidity >=0.8.0;
import {ERC20} from "./ERC20.sol";
import {SafeTransferLib} from "../utils/SafeTransferLib.sol";
/// @notice Minimalist and modern Wrapped Ether implementation.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/WETH.sol)
/// @author Inspired by WETH9 (https://github.com/dapphub/ds-weth/blob/master/src/weth9.sol)
contract WETH is ERC20("Wrapped Ether", "WETH", 18) {
using SafeTransferLib for address;
event Deposit(address indexed from, uint256 amount);
event Withdrawal(address indexed to, uint256 amount);
function deposit() public payable virtual {
_mint(msg.sender, msg.value);
emit Deposit(msg.sender, msg.value);
}
function withdraw(uint256 amount) public virtual {
_burn(msg.sender, amount);
emit Withdrawal(msg.sender, amount);
msg.sender.safeTransferETH(amount);
}
receive() external payable virtual {
deposit();
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.
pragma solidity ^0.8.20;
import {Arrays} from "../Arrays.sol";
/**
* @dev Library for managing
* https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
* types.
*
* Sets have the following properties:
*
* - Elements are added, removed, and checked for existence in constant time
* (O(1)).
* - Elements are enumerated in O(n). No guarantees are made on the ordering.
* - Set can be cleared (all elements removed) in O(n).
*
* ```solidity
* contract Example {
* // Add the library methods
* using EnumerableSet for EnumerableSet.AddressSet;
*
* // Declare a set state variable
* EnumerableSet.AddressSet private mySet;
* }
* ```
*
* As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
* and `uint256` (`UintSet`) are supported.
*
* [WARNING]
* ====
* Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
* unusable.
* See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
*
* In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
* array of EnumerableSet.
* ====
*/
library EnumerableSet {
// To implement this library for multiple types with as little code
// repetition as possible, we write it in terms of a generic Set type with
// bytes32 values.
// The Set implementation uses private functions, and user-facing
// implementations (such as AddressSet) are just wrappers around the
// underlying Set.
// This means that we can only create new EnumerableSets for types that fit
// in bytes32.
struct Set {
// Storage of set values
bytes32[] _values;
// Position is the index of the value in the `values` array plus 1.
// Position 0 is used to mean a value is not in the set.
mapping(bytes32 value => uint256) _positions;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function _add(Set storage set, bytes32 value) private returns (bool) {
if (!_contains(set, value)) {
set._values.push(value);
// The value is stored at length-1, but we add 1 to all indexes
// and use 0 as a sentinel value
set._positions[value] = set._values.length;
return true;
} else {
return false;
}
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function _remove(Set storage set, bytes32 value) private returns (bool) {
// We cache the value's position to prevent multiple reads from the same storage slot
uint256 position = set._positions[value];
if (position != 0) {
// Equivalent to contains(set, value)
// To delete an element from the _values array in O(1), we swap the element to delete with the last one in
// the array, and then remove the last element (sometimes called as 'swap and pop').
// This modifies the order of the array, as noted in {at}.
uint256 valueIndex = position - 1;
uint256 lastIndex = set._values.length - 1;
if (valueIndex != lastIndex) {
bytes32 lastValue = set._values[lastIndex];
// Move the lastValue to the index where the value to delete is
set._values[valueIndex] = lastValue;
// Update the tracked position of the lastValue (that was just moved)
set._positions[lastValue] = position;
}
// Delete the slot where the moved value was stored
set._values.pop();
// Delete the tracked position for the deleted slot
delete set._positions[value];
return true;
} else {
return false;
}
}
/**
* @dev Removes all the values from a set. O(n).
*
* WARNING: Developers should keep in mind that this function has an unbounded cost and using it may render the
* function uncallable if the set grows to the point where clearing it consumes too much gas to fit in a block.
*/
function _clear(Set storage set) private {
uint256 len = _length(set);
for (uint256 i = 0; i < len; ++i) {
delete set._positions[set._values[i]];
}
Arrays.unsafeSetLength(set._values, 0);
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function _contains(Set storage set, bytes32 value) private view returns (bool) {
return set._positions[value] != 0;
}
/**
* @dev Returns the number of values on the set. O(1).
*/
function _length(Set storage set) private view returns (uint256) {
return set._values.length;
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function _at(Set storage set, uint256 index) private view returns (bytes32) {
return set._values[index];
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function _values(Set storage set) private view returns (bytes32[] memory) {
return set._values;
}
// Bytes32Set
struct Bytes32Set {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _add(set._inner, value);
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _remove(set._inner, value);
}
/**
* @dev Removes all the values from a set. O(n).
*
* WARNING: Developers should keep in mind that this function has an unbounded cost and using it may render the
* function uncallable if the set grows to the point where clearing it consumes too much gas to fit in a block.
*/
function clear(Bytes32Set storage set) internal {
_clear(set._inner);
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
return _contains(set._inner, value);
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(Bytes32Set storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
return _at(set._inner, index);
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
bytes32[] memory store = _values(set._inner);
bytes32[] memory result;
assembly ("memory-safe") {
result := store
}
return result;
}
// AddressSet
struct AddressSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(AddressSet storage set, address value) internal returns (bool) {
return _add(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(AddressSet storage set, address value) internal returns (bool) {
return _remove(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Removes all the values from a set. O(n).
*
* WARNING: Developers should keep in mind that this function has an unbounded cost and using it may render the
* function uncallable if the set grows to the point where clearing it consumes too much gas to fit in a block.
*/
function clear(AddressSet storage set) internal {
_clear(set._inner);
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(AddressSet storage set, address value) internal view returns (bool) {
return _contains(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(AddressSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(AddressSet storage set, uint256 index) internal view returns (address) {
return address(uint160(uint256(_at(set._inner, index))));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(AddressSet storage set) internal view returns (address[] memory) {
bytes32[] memory store = _values(set._inner);
address[] memory result;
assembly ("memory-safe") {
result := store
}
return result;
}
// UintSet
struct UintSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(UintSet storage set, uint256 value) internal returns (bool) {
return _add(set._inner, bytes32(value));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(UintSet storage set, uint256 value) internal returns (bool) {
return _remove(set._inner, bytes32(value));
}
/**
* @dev Removes all the values from a set. O(n).
*
* WARNING: Developers should keep in mind that this function has an unbounded cost and using it may render the
* function uncallable if the set grows to the point where clearing it consumes too much gas to fit in a block.
*/
function clear(UintSet storage set) internal {
_clear(set._inner);
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(UintSet storage set, uint256 value) internal view returns (bool) {
return _contains(set._inner, bytes32(value));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(UintSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(UintSet storage set, uint256 index) internal view returns (uint256) {
return uint256(_at(set._inner, index));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(UintSet storage set) internal view returns (uint256[] memory) {
bytes32[] memory store = _values(set._inner);
uint256[] memory result;
assembly ("memory-safe") {
result := store
}
return result;
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.21;
import {FixedPointMathLib} from "@solmate/utils/FixedPointMathLib.sol";
import {IRateProvider} from "../../../src/interfaces/IRateProvider.sol";
import {ERC20} from "@solmate/tokens/ERC20.sol";
import {SafeTransferLib} from "@solmate/utils/SafeTransferLib.sol";
import {BoringVault} from "../../../src/base/BoringVault.sol";
import {Auth, Authority} from "@solmate/auth/Auth.sol";
import {IPausable} from "../../../src/interfaces/IPausable.sol";
contract AccountantWithRateProviders is Auth, IRateProvider, IPausable {
using FixedPointMathLib for uint256;
using SafeTransferLib for ERC20;
// ========================================= STRUCTS =========================================
/**
* @param payoutAddress the address `claimFees` sends fees to
* @param highwaterMark the highest value of the BoringVault's share price
* @param feesOwedInBase total pending fees owed in terms of base
* @param totalSharesLastUpdate total amount of shares the last exchange rate update
* @param exchangeRate the current exchange rate in terms of base
* @param allowedExchangeRateChangeUpper the max allowed change to exchange rate from an update
* @param allowedExchangeRateChangeLower the min allowed change to exchange rate from an update
* @param lastUpdateTimestamp the block timestamp of the last exchange rate update
* @param isPaused whether or not this contract is paused
* @param minimumUpdateDelayInSeconds the minimum amount of time that must pass between
* exchange rate updates, such that the update won't trigger the contract to be paused
* @param platformFee the platform fee
* @param performanceFee the performance fee
*/
struct AccountantState {
address payoutAddress;
uint96 highwaterMark;
uint128 feesOwedInBase;
uint128 totalSharesLastUpdate;
uint96 exchangeRate;
uint16 allowedExchangeRateChangeUpper;
uint16 allowedExchangeRateChangeLower;
uint64 lastUpdateTimestamp;
bool isPaused;
uint24 minimumUpdateDelayInSeconds;
uint16 platformFee;
uint16 performanceFee;
}
/**
* @param isPeggedToBase whether or not the asset is 1:1 with the base asset
* @param rateProvider the rate provider for this asset if `isPeggedToBase` is false
*/
struct RateProviderData {
bool isPeggedToBase;
IRateProvider rateProvider;
}
// ========================================= STATE =========================================
/**
* @notice Store the accountant state in 3 packed slots.
*/
AccountantState public accountantState;
/**
* @notice Maps ERC20s to their RateProviderData.
*/
mapping(ERC20 => RateProviderData) public rateProviderData;
//============================== ERRORS ===============================
error AccountantWithRateProviders__UpperBoundTooSmall();
error AccountantWithRateProviders__LowerBoundTooLarge();
error AccountantWithRateProviders__PlatformFeeTooLarge();
error AccountantWithRateProviders__PerformanceFeeTooLarge();
error AccountantWithRateProviders__Paused();
error AccountantWithRateProviders__ZeroFeesOwed();
error AccountantWithRateProviders__OnlyCallableByBoringVault();
error AccountantWithRateProviders__UpdateDelayTooLarge();
error AccountantWithRateProviders__ExchangeRateAboveHighwaterMark();
//============================== EVENTS ===============================
event Paused();
event Unpaused();
event DelayInSecondsUpdated(uint24 oldDelay, uint24 newDelay);
event UpperBoundUpdated(uint16 oldBound, uint16 newBound);
event LowerBoundUpdated(uint16 oldBound, uint16 newBound);
event PlatformFeeUpdated(uint16 oldFee, uint16 newFee);
event PerformanceFeeUpdated(uint16 oldFee, uint16 newFee);
event PayoutAddressUpdated(address oldPayout, address newPayout);
event RateProviderUpdated(address asset, bool isPegged, address rateProvider);
event ExchangeRateUpdated(uint96 oldRate, uint96 newRate, uint64 currentTime);
event FeesClaimed(address indexed feeAsset, uint256 amount);
event HighwaterMarkReset();
//============================== IMMUTABLES ===============================
/**
* @notice The base asset rates are provided in.
*/
ERC20 public immutable base;
/**
* @notice The decimals rates are provided in.
*/
uint8 public immutable decimals;
/**
* @notice The BoringVault this accountant is working with.
* Used to determine share supply for fee calculation.
*/
BoringVault public immutable vault;
/**
* @notice One share of the BoringVault.
*/
uint256 internal immutable ONE_SHARE;
constructor(
address _owner,
address _vault,
address payoutAddress,
uint96 startingExchangeRate,
address _base,
uint16 allowedExchangeRateChangeUpper,
uint16 allowedExchangeRateChangeLower,
uint24 minimumUpdateDelayInSeconds,
uint16 platformFee,
uint16 performanceFee
) Auth(_owner, Authority(address(0))) {
base = ERC20(_base);
decimals = ERC20(_base).decimals();
vault = BoringVault(payable(_vault));
ONE_SHARE = 10 ** vault.decimals();
accountantState = AccountantState({
payoutAddress: payoutAddress,
highwaterMark: startingExchangeRate,
feesOwedInBase: 0,
totalSharesLastUpdate: uint128(vault.totalSupply()),
exchangeRate: startingExchangeRate,
allowedExchangeRateChangeUpper: allowedExchangeRateChangeUpper,
allowedExchangeRateChangeLower: allowedExchangeRateChangeLower,
lastUpdateTimestamp: uint64(block.timestamp),
isPaused: false,
minimumUpdateDelayInSeconds: minimumUpdateDelayInSeconds,
platformFee: platformFee,
performanceFee: performanceFee
});
}
// ========================================= ADMIN FUNCTIONS =========================================
/**
* @notice Pause this contract, which prevents future calls to `updateExchangeRate`, and any safe rate
* calls will revert.
* @dev Callable by MULTISIG_ROLE.
*/
function pause() external requiresAuth {
accountantState.isPaused = true;
emit Paused();
}
/**
* @notice Unpause this contract, which allows future calls to `updateExchangeRate`, and any safe rate
* calls will stop reverting.
* @dev Callable by MULTISIG_ROLE.
*/
function unpause() external requiresAuth {
accountantState.isPaused = false;
emit Unpaused();
}
/**
* @notice Update the minimum time delay between `updateExchangeRate` calls.
* @dev There are no input requirements, as it is possible the admin would want
* the exchange rate updated as frequently as needed.
* @dev Callable by OWNER_ROLE.
*/
function updateDelay(uint24 minimumUpdateDelayInSeconds) external requiresAuth {
if (minimumUpdateDelayInSeconds > 14 days) revert AccountantWithRateProviders__UpdateDelayTooLarge();
uint24 oldDelay = accountantState.minimumUpdateDelayInSeconds;
accountantState.minimumUpdateDelayInSeconds = minimumUpdateDelayInSeconds;
emit DelayInSecondsUpdated(oldDelay, minimumUpdateDelayInSeconds);
}
/**
* @notice Update the allowed upper bound change of exchange rate between `updateExchangeRateCalls`.
* @dev Callable by OWNER_ROLE.
*/
function updateUpper(uint16 allowedExchangeRateChangeUpper) external requiresAuth {
if (allowedExchangeRateChangeUpper < 1e4) revert AccountantWithRateProviders__UpperBoundTooSmall();
uint16 oldBound = accountantState.allowedExchangeRateChangeUpper;
accountantState.allowedExchangeRateChangeUpper = allowedExchangeRateChangeUpper;
emit UpperBoundUpdated(oldBound, allowedExchangeRateChangeUpper);
}
/**
* @notice Update the allowed lower bound change of exchange rate between `updateExchangeRateCalls`.
* @dev Callable by OWNER_ROLE.
*/
function updateLower(uint16 allowedExchangeRateChangeLower) external requiresAuth {
if (allowedExchangeRateChangeLower > 1e4) revert AccountantWithRateProviders__LowerBoundTooLarge();
uint16 oldBound = accountantState.allowedExchangeRateChangeLower;
accountantState.allowedExchangeRateChangeLower = allowedExchangeRateChangeLower;
emit LowerBoundUpdated(oldBound, allowedExchangeRateChangeLower);
}
/**
* @notice Update the platform fee to a new value.
* @dev Callable by OWNER_ROLE.
*/
function updatePlatformFee(uint16 platformFee) external requiresAuth {
if (platformFee > 0.2e4) revert AccountantWithRateProviders__PlatformFeeTooLarge();
uint16 oldFee = accountantState.platformFee;
accountantState.platformFee = platformFee;
emit PlatformFeeUpdated(oldFee, platformFee);
}
/**
* @notice Update the performance fee to a new value.
* @dev Callable by OWNER_ROLE.
*/
function updatePerformanceFee(uint16 performanceFee) external requiresAuth {
if (performanceFee > 0.5e4) revert AccountantWithRateProviders__PerformanceFeeTooLarge();
uint16 oldFee = accountantState.performanceFee;
accountantState.performanceFee = performanceFee;
emit PerformanceFeeUpdated(oldFee, performanceFee);
}
/**
* @notice Update the payout address fees are sent to.
* @dev Callable by OWNER_ROLE.
*/
function updatePayoutAddress(address payoutAddress) external requiresAuth {
address oldPayout = accountantState.payoutAddress;
accountantState.payoutAddress = payoutAddress;
emit PayoutAddressUpdated(oldPayout, payoutAddress);
}
/**
* @notice Update the rate provider data for a specific `asset`.
* @dev Rate providers must return rates in terms of `base` or
* an asset pegged to base and they must use the same decimals
* as `asset`.
* @dev Callable by OWNER_ROLE.
*/
function setRateProviderData(ERC20 asset, bool isPeggedToBase, address rateProvider) external requiresAuth {
rateProviderData[asset] =
RateProviderData({isPeggedToBase: isPeggedToBase, rateProvider: IRateProvider(rateProvider)});
emit RateProviderUpdated(address(asset), isPeggedToBase, rateProvider);
}
/**
* @notice Reset the highwater mark to the current exchange rate.
* @dev Callable by OWNER_ROLE.
*/
function resetHighwaterMark() external virtual requiresAuth {
AccountantState storage state = accountantState;
if (state.exchangeRate > state.highwaterMark) {
revert AccountantWithRateProviders__ExchangeRateAboveHighwaterMark();
}
uint64 currentTime = uint64(block.timestamp);
uint256 currentTotalShares = vault.totalSupply();
_calculateFeesOwed(state, state.exchangeRate, state.exchangeRate, currentTotalShares, currentTime);
state.totalSharesLastUpdate = uint128(currentTotalShares);
state.highwaterMark = accountantState.exchangeRate;
state.lastUpdateTimestamp = currentTime;
emit HighwaterMarkReset();
}
// ========================================= UPDATE EXCHANGE RATE/FEES FUNCTIONS =========================================
/**
* @notice Updates this contract exchangeRate.
* @dev If new exchange rate is outside of accepted bounds, or if not enough time has passed, this
* will pause the contract, and this function will NOT calculate fees owed.
* @dev Callable by UPDATE_EXCHANGE_RATE_ROLE.
*/
function updateExchangeRate(uint96 newExchangeRate) external virtual requiresAuth {
(
bool shouldPause,
AccountantState storage state,
uint64 currentTime,
uint256 currentExchangeRate,
uint256 currentTotalShares
) = _beforeUpdateExchangeRate(newExchangeRate);
if (shouldPause) {
// Instead of reverting, pause the contract. This way the exchange rate updater is able to update the exchange rate
// to a better value, and pause it.
state.isPaused = true;
} else {
_calculateFeesOwed(state, newExchangeRate, currentExchangeRate, currentTotalShares, currentTime);
}
newExchangeRate = _setExchangeRate(newExchangeRate, state);
state.totalSharesLastUpdate = uint128(currentTotalShares);
state.lastUpdateTimestamp = currentTime;
emit ExchangeRateUpdated(uint96(currentExchangeRate), newExchangeRate, currentTime);
}
/**
* @notice Claim pending fees.
* @dev This function must be called by the BoringVault.
* @dev This function will lose precision if the exchange rate
* decimals is greater than the feeAsset's decimals.
*/
function claimFees(ERC20 feeAsset) external {
if (msg.sender != address(vault)) revert AccountantWithRateProviders__OnlyCallableByBoringVault();
AccountantState storage state = accountantState;
if (state.isPaused) revert AccountantWithRateProviders__Paused();
if (state.feesOwedInBase == 0) revert AccountantWithRateProviders__ZeroFeesOwed();
// Determine amount of fees owed in feeAsset.
uint256 feesOwedInFeeAsset;
RateProviderData memory data = rateProviderData[feeAsset];
if (address(feeAsset) == address(base)) {
feesOwedInFeeAsset = state.feesOwedInBase;
} else {
uint8 feeAssetDecimals = ERC20(feeAsset).decimals();
uint256 feesOwedInBaseUsingFeeAssetDecimals =
_changeDecimals(state.feesOwedInBase, decimals, feeAssetDecimals);
if (data.isPeggedToBase) {
feesOwedInFeeAsset = feesOwedInBaseUsingFeeAssetDecimals;
} else {
uint256 rate = data.rateProvider.getRate();
feesOwedInFeeAsset = feesOwedInBaseUsingFeeAssetDecimals.mulDivDown(10 ** feeAssetDecimals, rate);
}
}
// Zero out fees owed.
state.feesOwedInBase = 0;
// Transfer fee asset to payout address.
feeAsset.safeTransferFrom(msg.sender, state.payoutAddress, feesOwedInFeeAsset);
emit FeesClaimed(address(feeAsset), feesOwedInFeeAsset);
}
// ========================================= VIEW FUNCTIONS =========================================
/**
* @notice Get this BoringVault's current rate in the base.
*/
function getRate() public view returns (uint256 rate) {
rate = accountantState.exchangeRate;
}
/**
* @notice Get this BoringVault's current rate in the base.
* @dev Revert if paused.
*/
function getRateSafe() external view returns (uint256 rate) {
if (accountantState.isPaused) revert AccountantWithRateProviders__Paused();
rate = getRate();
}
/**
* @notice Get this BoringVault's current rate in the provided quote.
* @dev `quote` must have its RateProviderData set, else this will revert.
* @dev This function will lose precision if the exchange rate
* decimals is greater than the quote's decimals.
*/
function getRateInQuote(ERC20 quote) public view returns (uint256 rateInQuote) {
if (address(quote) == address(base)) {
rateInQuote = accountantState.exchangeRate;
} else {
RateProviderData memory data = rateProviderData[quote];
uint8 quoteDecimals = ERC20(quote).decimals();
uint256 exchangeRateInQuoteDecimals = _changeDecimals(accountantState.exchangeRate, decimals, quoteDecimals);
if (data.isPeggedToBase) {
rateInQuote = exchangeRateInQuoteDecimals;
} else {
uint256 quoteRate = data.rateProvider.getRate();
uint256 oneQuote = 10 ** quoteDecimals;
rateInQuote = oneQuote.mulDivDown(exchangeRateInQuoteDecimals, quoteRate);
}
}
}
/**
* @notice Get this BoringVault's current rate in the provided quote.
* @dev `quote` must have its RateProviderData set, else this will revert.
* @dev Revert if paused.
*/
function getRateInQuoteSafe(ERC20 quote) external view returns (uint256 rateInQuote) {
if (accountantState.isPaused) revert AccountantWithRateProviders__Paused();
rateInQuote = getRateInQuote(quote);
}
/**
* @notice Preview the result of an update to the exchange rate.
* @return updateWillPause Whether the update will pause the contract.
* @return newFeesOwedInBase The new fees owed in base.
* @return totalFeesOwedInBase The total fees owed in base.
*/
function previewUpdateExchangeRate(uint96 newExchangeRate)
external
view
virtual
returns (bool updateWillPause, uint256 newFeesOwedInBase, uint256 totalFeesOwedInBase)
{
(
bool shouldPause,
AccountantState storage state,
uint64 currentTime,
uint256 currentExchangeRate,
uint256 currentTotalShares
) = _beforeUpdateExchangeRate(newExchangeRate);
updateWillPause = shouldPause;
totalFeesOwedInBase = state.feesOwedInBase;
if (!shouldPause) {
(uint256 platformFeesOwedInBase, uint256 shareSupplyToUse) = _calculatePlatformFee(
state.totalSharesLastUpdate,
state.lastUpdateTimestamp,
state.platformFee,
newExchangeRate,
currentExchangeRate,
currentTotalShares,
currentTime
);
uint256 performanceFeesOwedInBase;
if (newExchangeRate > state.highwaterMark) {
(performanceFeesOwedInBase,) = _calculatePerformanceFee(
newExchangeRate, shareSupplyToUse, state.highwaterMark, state.performanceFee
);
}
newFeesOwedInBase = platformFeesOwedInBase + performanceFeesOwedInBase;
totalFeesOwedInBase += newFeesOwedInBase;
}
}
// ========================================= INTERNAL HELPER FUNCTIONS =========================================
/**
* @notice Used to change the decimals of precision used for an amount.
*/
function _changeDecimals(uint256 amount, uint8 fromDecimals, uint8 toDecimals) internal pure returns (uint256) {
if (fromDecimals == toDecimals) {
return amount;
} else if (fromDecimals < toDecimals) {
return amount * 10 ** (toDecimals - fromDecimals);
} else {
return amount / 10 ** (fromDecimals - toDecimals);
}
}
/**
* @notice Check if the new exchange rate is outside of the allowed bounds or if not enough time has passed.
*/
function _beforeUpdateExchangeRate(uint96 newExchangeRate)
internal
view
returns (
bool shouldPause,
AccountantState storage state,
uint64 currentTime,
uint256 currentExchangeRate,
uint256 currentTotalShares
)
{
state = accountantState;
if (state.isPaused) revert AccountantWithRateProviders__Paused();
currentTime = uint64(block.timestamp);
currentExchangeRate = state.exchangeRate;
currentTotalShares = vault.totalSupply();
shouldPause = currentTime < state.lastUpdateTimestamp + state.minimumUpdateDelayInSeconds
|| newExchangeRate > currentExchangeRate.mulDivDown(state.allowedExchangeRateChangeUpper, 1e4)
|| newExchangeRate < currentExchangeRate.mulDivDown(state.allowedExchangeRateChangeLower, 1e4);
}
/**
* @notice Set the exchange rate.
*/
function _setExchangeRate(uint96 newExchangeRate, AccountantState storage state)
internal
virtual
returns (uint96)
{
state.exchangeRate = newExchangeRate;
return newExchangeRate;
}
/**
* @notice Calculate platform fees.
*/
function _calculatePlatformFee(
uint128 totalSharesLastUpdate,
uint64 lastUpdateTimestamp,
uint16 platformFee,
uint96 newExchangeRate,
uint256 currentExchangeRate,
uint256 currentTotalShares,
uint64 currentTime
) internal view returns (uint256 platformFeesOwedInBase, uint256 shareSupplyToUse) {
shareSupplyToUse = currentTotalShares;
// Use the minimum between current total supply and total supply for last update.
if (totalSharesLastUpdate < shareSupplyToUse) {
shareSupplyToUse = totalSharesLastUpdate;
}
// Determine platform fees owned.
if (platformFee > 0) {
uint256 timeDelta = currentTime - lastUpdateTimestamp;
uint256 minimumAssets = newExchangeRate > currentExchangeRate
? shareSupplyToUse.mulDivDown(currentExchangeRate, ONE_SHARE)
: shareSupplyToUse.mulDivDown(newExchangeRate, ONE_SHARE);
uint256 platformFeesAnnual = minimumAssets.mulDivDown(platformFee, 1e4);
platformFeesOwedInBase = platformFeesAnnual.mulDivDown(timeDelta, 365 days);
}
}
/**
* @notice Calculate performance fees.
*/
function _calculatePerformanceFee(
uint96 newExchangeRate,
uint256 shareSupplyToUse,
uint96 datum,
uint16 performanceFee
) internal view returns (uint256 performanceFeesOwedInBase, uint256 yieldEarned) {
uint256 changeInExchangeRate = newExchangeRate - datum;
yieldEarned = changeInExchangeRate.mulDivDown(shareSupplyToUse, ONE_SHARE);
if (performanceFee > 0) {
performanceFeesOwedInBase = yieldEarned.mulDivDown(performanceFee, 1e4);
}
}
/**
* @notice Calculate fees owed in base.
* @dev This function will update the highwater mark if the new exchange rate is higher.
*/
function _calculateFeesOwed(
AccountantState storage state,
uint96 newExchangeRate,
uint256 currentExchangeRate,
uint256 currentTotalShares,
uint64 currentTime
) internal virtual {
// Only update fees if we are not paused.
// Update fee accounting.
(uint256 newFeesOwedInBase, uint256 shareSupplyToUse) = _calculatePlatformFee(
state.totalSharesLastUpdate,
state.lastUpdateTimestamp,
state.platformFee,
newExchangeRate,
currentExchangeRate,
currentTotalShares,
currentTime
);
// Account for performance fees.
if (newExchangeRate > state.highwaterMark) {
(uint256 performanceFeesOwedInBase,) =
_calculatePerformanceFee(newExchangeRate, shareSupplyToUse, state.highwaterMark, state.performanceFee);
// Add performance fees to fees owed.
newFeesOwedInBase += performanceFeesOwedInBase;
// Always update the highwater mark if the new exchange rate is higher.
// This way if we are not iniitiall taking performance fees, we can start taking them
// without back charging them on past performance.
state.highwaterMark = newExchangeRate;
}
state.feesOwedInBase += uint128(newFeesOwedInBase);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (utils/Multicall.sol)
pragma solidity ^0.8.20;
import {Address} from "./Address.sol";
import {Context} from "./Context.sol";
/**
* @dev Provides a function to batch together multiple calls in a single external call.
*
* Consider any assumption about calldata validation performed by the sender may be violated if it's not especially
* careful about sending transactions invoking {multicall}. For example, a relay address that filters function
* selectors won't filter calls nested within a {multicall} operation.
*
* NOTE: Since 5.0.1 and 4.9.4, this contract identifies non-canonical contexts (i.e. `msg.sender` is not {Context-_msgSender}).
* If a non-canonical context is identified, the following self `delegatecall` appends the last bytes of `msg.data`
* to the subcall. This makes it safe to use with {ERC2771Context}. Contexts that don't affect the resolution of
* {Context-_msgSender} are not propagated to subcalls.
*/
abstract contract Multicall is Context {
/**
* @dev Receives and executes a batch of function calls on this contract.
* @custom:oz-upgrades-unsafe-allow-reachable delegatecall
*/
function multicall(bytes[] calldata data) external virtual returns (bytes[] memory results) {
bytes memory context = msg.sender == _msgSender()
? new bytes(0)
: msg.data[msg.data.length - _contextSuffixLength():];
results = new bytes[](data.length);
for (uint256 i = 0; i < data.length; i++) {
results[i] = Address.functionDelegateCall(address(this), bytes.concat(data[i], context));
}
return results;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (utils/SlotDerivation.sol)
// This file was procedurally generated from scripts/generate/templates/SlotDerivation.js.
pragma solidity ^0.8.20;
/**
* @dev Library for computing storage (and transient storage) locations from namespaces and deriving slots
* corresponding to standard patterns. The derivation method for array and mapping matches the storage layout used by
* the solidity language / compiler.
*
* See https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html#mappings-and-dynamic-arrays[Solidity docs for mappings and dynamic arrays.].
*
* Example usage:
* ```solidity
* contract Example {
* // Add the library methods
* using StorageSlot for bytes32;
* using SlotDerivation for bytes32;
*
* // Declare a namespace
* string private constant _NAMESPACE = "<namespace>"; // eg. OpenZeppelin.Slot
*
* function setValueInNamespace(uint256 key, address newValue) internal {
* _NAMESPACE.erc7201Slot().deriveMapping(key).getAddressSlot().value = newValue;
* }
*
* function getValueInNamespace(uint256 key) internal view returns (address) {
* return _NAMESPACE.erc7201Slot().deriveMapping(key).getAddressSlot().value;
* }
* }
* ```
*
* TIP: Consider using this library along with {StorageSlot}.
*
* NOTE: This library provides a way to manipulate storage locations in a non-standard way. Tooling for checking
* upgrade safety will ignore the slots accessed through this library.
*
* _Available since v5.1._
*/
library SlotDerivation {
/**
* @dev Derive an ERC-7201 slot from a string (namespace).
*/
function erc7201Slot(string memory namespace) internal pure returns (bytes32 slot) {
assembly ("memory-safe") {
mstore(0x00, sub(keccak256(add(namespace, 0x20), mload(namespace)), 1))
slot := and(keccak256(0x00, 0x20), not(0xff))
}
}
/**
* @dev Add an offset to a slot to get the n-th element of a structure or an array.
*/
function offset(bytes32 slot, uint256 pos) internal pure returns (bytes32 result) {
unchecked {
return bytes32(uint256(slot) + pos);
}
}
/**
* @dev Derive the location of the first element in an array from the slot where the length is stored.
*/
function deriveArray(bytes32 slot) internal pure returns (bytes32 result) {
assembly ("memory-safe") {
mstore(0x00, slot)
result := keccak256(0x00, 0x20)
}
}
/**
* @dev Derive the location of a mapping element from the key.
*/
function deriveMapping(bytes32 slot, address key) internal pure returns (bytes32 result) {
assembly ("memory-safe") {
mstore(0x00, and(key, shr(96, not(0))))
mstore(0x20, slot)
result := keccak256(0x00, 0x40)
}
}
/**
* @dev Derive the location of a mapping element from the key.
*/
function deriveMapping(bytes32 slot, bool key) internal pure returns (bytes32 result) {
assembly ("memory-safe") {
mstore(0x00, iszero(iszero(key)))
mstore(0x20, slot)
result := keccak256(0x00, 0x40)
}
}
/**
* @dev Derive the location of a mapping element from the key.
*/
function deriveMapping(bytes32 slot, bytes32 key) internal pure returns (bytes32 result) {
assembly ("memory-safe") {
mstore(0x00, key)
mstore(0x20, slot)
result := keccak256(0x00, 0x40)
}
}
/**
* @dev Derive the location of a mapping element from the key.
*/
function deriveMapping(bytes32 slot, uint256 key) internal pure returns (bytes32 result) {
assembly ("memory-safe") {
mstore(0x00, key)
mstore(0x20, slot)
result := keccak256(0x00, 0x40)
}
}
/**
* @dev Derive the location of a mapping element from the key.
*/
function deriveMapping(bytes32 slot, int256 key) internal pure returns (bytes32 result) {
assembly ("memory-safe") {
mstore(0x00, key)
mstore(0x20, slot)
result := keccak256(0x00, 0x40)
}
}
/**
* @dev Derive the location of a mapping element from the key.
*/
function deriveMapping(bytes32 slot, string memory key) internal pure returns (bytes32 result) {
assembly ("memory-safe") {
let length := mload(key)
let begin := add(key, 0x20)
let end := add(begin, length)
let cache := mload(end)
mstore(end, slot)
result := keccak256(begin, add(length, 0x20))
mstore(end, cache)
}
}
/**
* @dev Derive the location of a mapping element from the key.
*/
function deriveMapping(bytes32 slot, bytes memory key) internal pure returns (bytes32 result) {
assembly ("memory-safe") {
let length := mload(key)
let begin := add(key, 0x20)
let end := add(begin, length)
let cache := mload(end)
mstore(end, slot)
result := keccak256(begin, add(length, 0x20))
mstore(end, cache)
}
}
}// 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.0.0) (token/ERC721/utils/ERC721Holder.sol)
pragma solidity ^0.8.20;
import {IERC721Receiver} from "../IERC721Receiver.sol";
/**
* @dev Implementation of the {IERC721Receiver} interface.
*
* Accepts all token transfers.
* Make sure the contract is able to use its token with {IERC721-safeTransferFrom}, {IERC721-approve} or
* {IERC721-setApprovalForAll}.
*/
abstract contract ERC721Holder is IERC721Receiver {
/**
* @dev See {IERC721Receiver-onERC721Received}.
*
* Always returns `IERC721Receiver.onERC721Received.selector`.
*/
function onERC721Received(address, address, uint256, bytes memory) public virtual returns (bytes4) {
return this.onERC721Received.selector;
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.21;
interface IBoringSolver {
function boringSolve(
address initiator,
address boringVault,
address solveAsset,
uint256 totalShares,
uint256 requiredAssets,
bytes calldata solveData
) external;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/Comparators.sol)
pragma solidity ^0.8.20;
/**
* @dev Provides a set of functions to compare values.
*
* _Available since v5.1._
*/
library Comparators {
function lt(uint256 a, uint256 b) internal pure returns (bool) {
return a < b;
}
function gt(uint256 a, uint256 b) internal pure returns (bool) {
return a > b;
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.21;
interface IPausable {
function pause() external;
function unpause() external;
}// 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: UNLICENSED
pragma solidity 0.8.21;
import {ERC20} from "@solmate/tokens/ERC20.sol";
import {WETH} from "@solmate/tokens/WETH.sol";
import {BoringVault} from "../../../../src/base/BoringVault.sol";
import {AccountantWithRateProviders} from "../../../../src/base/Roles/AccountantWithRateProviders.sol";
import {FixedPointMathLib} from "@solmate/utils/FixedPointMathLib.sol";
import {SafeTransferLib} from "@solmate/utils/SafeTransferLib.sol";
import {BeforeTransferHook} from "../../../../src/interfaces/BeforeTransferHook.sol";
import {Auth, Authority} from "@solmate/auth/Auth.sol";
import {ReentrancyGuard} from "@solmate/utils/ReentrancyGuard.sol";
import {IPausable} from "../../../../src/interfaces/IPausable.sol";
import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import {IBoringSolver} from "../../../../src/base/Roles/BoringQueue/IBoringSolver.sol";
contract BoringOnChainQueue is Auth, ReentrancyGuard, IPausable {
using EnumerableSet for EnumerableSet.Bytes32Set;
using SafeTransferLib for BoringVault;
using SafeTransferLib for ERC20;
using FixedPointMathLib for uint256;
// ========================================= STRUCTS =========================================
/**
* @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;
}
/**
* @param nonce The nonce of the request, used to make it impossible for request Ids to be repeated.
* @param user The user that made the request.
* @param assetOut The asset that the user wants to withdraw.
* @param amountOfShares The amount of shares the user wants to withdraw.
* @param amountOfAssets The amount of assets the user will receive.
* @param creationTime The time the request was made.
* @param secondsToMaturity The time in seconds it takes for the asset to mature.
* @param secondsToDeadline The time in seconds the request is valid for.
*/
struct OnChainWithdraw {
uint96 nonce; // read from state, used to make it impossible for request Ids to be repeated.
address user; // msg.sender
address assetOut; // input sanitized
uint128 amountOfShares; // input transfered in
uint128 amountOfAssets; // derived from amountOfShares and price
uint40 creationTime; // time withdraw was made
uint24 secondsToMaturity; // in contract, from withdrawAsset?
uint24 secondsToDeadline; // in contract, from withdrawAsset? To get the deadline you take the creationTime add seconds to maturity, add the secondsToDeadline
}
// ========================================= CONSTANTS =========================================
/**
* @notice The maximum discount allowed for a withdraw asset.
*/
uint16 internal constant MAX_DISCOUNT = 0.3e4;
/**
* @notice The maximum time in seconds a withdraw asset can take to mature.
*/
uint24 internal constant MAXIMUM_SECONDS_TO_MATURITY = 30 days;
/**
* @notice Caps the minimum time in seconds a withdraw request must be valid for before it is expired.
*/
uint24 internal constant MAXIMUM_MINIMUM_SECONDS_TO_DEADLINE = 30 days;
// ========================================= MODIFIERS =========================================
/**
* @notice Ensure that the request user is the same as the message sender.
*/
modifier onlyRequestUser(address requestUser, address msgSender) {
if (requestUser != msgSender) revert BoringOnChainQueue__BadUser();
_;
}
// ========================================= GLOBAL STATE =========================================
/**
* @notice Open Zeppelin EnumerableSet to store all withdraw requests, by there request Id.
*/
EnumerableSet.Bytes32Set private _withdrawRequests;
/**
* @notice Mapping of asset addresses to WithdrawAssets.
*/
mapping(address => WithdrawAsset) public withdrawAssets;
/**
* @notice The nonce of the next request.
* @dev The purpose of this nonce is to prevent request Ids from being repeated.
* @dev Start at 1, since 0 is considered invalid.
* @dev When incrementing the nonce, an unchecked block is used to save gas.
* This is safe because you can not feasibly make a request, and then cause an overflow
* in the same block such that you can make 2 requests with the same request Id.
* And even if you did, the tx would revert with a keccak256 collision error.
*/
uint96 public nonce = 1;
/**
* @notice Whether or not the contract is paused.
*/
bool public isPaused;
//============================== ERRORS ===============================
error BoringOnChainQueue__Paused();
error BoringOnChainQueue__WithdrawsNotAllowedForAsset();
error BoringOnChainQueue__BadDiscount();
error BoringOnChainQueue__BadShareAmount();
error BoringOnChainQueue__BadDeadline();
error BoringOnChainQueue__BadUser();
error BoringOnChainQueue__DeadlinePassed();
error BoringOnChainQueue__NotMatured();
error BoringOnChainQueue__Keccak256Collision();
error BoringOnChainQueue__RequestNotFound();
error BoringOnChainQueue__PermitFailedAndAllowanceTooLow();
error BoringOnChainQueue__MAX_DISCOUNT();
error BoringOnChainQueue__MAXIMUM_MINIMUM_SECONDS_TO_DEADLINE();
error BoringOnChainQueue__SolveAssetMismatch();
error BoringOnChainQueue__Overflow();
error BoringOnChainQueue__MAXIMUM_SECONDS_TO_MATURITY();
error BoringOnChainQueue__BadInput();
error BoringOnChainQueue__RescueCannotTakeSharesFromActiveRequests();
//============================== EVENTS ===============================
event OnChainWithdrawRequested(
bytes32 indexed requestId,
address indexed user,
address indexed assetOut,
uint96 nonce,
uint128 amountOfShares,
uint128 amountOfAssets,
uint40 creationTime,
uint24 secondsToMaturity,
uint24 secondsToDeadline
);
event OnChainWithdrawCancelled(bytes32 indexed requestId, address indexed user, uint256 timestamp);
event OnChainWithdrawSolved(bytes32 indexed requestId, address indexed user, uint256 timestamp);
event WithdrawAssetSetup(
address indexed assetOut,
uint24 secondsToMaturity,
uint24 minimumSecondsToDeadline,
uint16 minDiscount,
uint16 maxDiscount,
uint96 minimumShares
);
event WithdrawAssetStopped(address indexed assetOut);
event WithdrawAssetUpdated(
address indexed assetOut,
uint24 minimumSecondsToDeadline,
uint24 secondsToMaturity,
uint16 minDiscount,
uint16 maxDiscount,
uint96 minimumShares
);
event Paused();
event Unpaused();
//============================== IMMUTABLES ===============================
/**
* @notice The BoringVault contract to withdraw from.
*/
BoringVault public immutable boringVault;
/**
* @notice The AccountantWithRateProviders contract to get rates from.
*/
AccountantWithRateProviders public immutable accountant;
/**
* @notice One BoringVault share.
*/
uint256 public immutable ONE_SHARE;
constructor(address _owner, address _auth, address payable _boringVault, address _accountant)
Auth(_owner, Authority(_auth))
{
boringVault = BoringVault(_boringVault);
ONE_SHARE = 10 ** boringVault.decimals();
accountant = AccountantWithRateProviders(_accountant);
}
//=============================== ADMIN FUNCTIONS ================================
/**
* @notice Allows the owner to rescue tokens from the contract.
* @dev The owner can only withdraw BoringVault shares if they are accidentally sent to this contract.
* Shares from active withdraw requests are not withdrawable.
* @param token The token to rescue.
* @param amount The amount to rescue.
* @param to The address to send the rescued tokens to.
* @param activeRequests The active withdraw requests, query `getWithdrawRequests`, or read events to get them.
* @dev Provided activeRequests must match the order of active requests in the queue.
*/
function rescueTokens(ERC20 token, uint256 amount, address to, OnChainWithdraw[] calldata activeRequests)
external
requiresAuth
{
if (address(token) == address(boringVault)) {
bytes32[] memory requestIds = _withdrawRequests.values();
uint256 requestIdsLength = requestIds.length;
if (activeRequests.length != requestIdsLength) revert BoringOnChainQueue__BadInput();
// Iterate through provided activeRequests, and hash each one to compare to the requestIds.
// Also track the sum of shares to make sure it is less than or equal to the amount.
uint256 activeRequestShareSum;
for (uint256 i = 0; i < requestIdsLength; ++i) {
if (keccak256(abi.encode(activeRequests[i])) != requestIds[i]) revert BoringOnChainQueue__BadInput();
activeRequestShareSum += activeRequests[i].amountOfShares;
}
uint256 freeShares = boringVault.balanceOf(address(this)) - activeRequestShareSum;
if (amount == type(uint256).max) amount = freeShares;
else if (amount > freeShares) revert BoringOnChainQueue__RescueCannotTakeSharesFromActiveRequests();
} else {
if (amount == type(uint256).max) amount = token.balanceOf(address(this));
}
token.safeTransfer(to, amount);
}
/**
* @notice Pause this contract, which prevents future calls to any functions that
* create new requests, or solve active requests.
* @dev Callable by MULTISIG_ROLE.
*/
function pause() external requiresAuth {
isPaused = true;
emit Paused();
}
/**
* @notice Unpause this contract, which allows future calls to any functions that
* create new requests, or solve active requests.
* @dev Callable by MULTISIG_ROLE.
*/
function unpause() external requiresAuth {
isPaused = false;
emit Unpaused();
}
/**
* @notice Update a new withdraw asset or existing.
* @dev Callable by MULTISIG_ROLE.
* @param assetOut The asset to withdraw.
* @param secondsToMaturity The time in seconds it takes for the withdraw 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.
*/
function updateWithdrawAsset(
address assetOut,
uint24 secondsToMaturity,
uint24 minimumSecondsToDeadline,
uint16 minDiscount,
uint16 maxDiscount,
uint96 minimumShares
) external requiresAuth {
// Validate input.
if (maxDiscount > MAX_DISCOUNT) revert BoringOnChainQueue__MAX_DISCOUNT();
if (secondsToMaturity > MAXIMUM_SECONDS_TO_MATURITY) {
revert BoringOnChainQueue__MAXIMUM_SECONDS_TO_MATURITY();
}
if (minimumSecondsToDeadline > MAXIMUM_MINIMUM_SECONDS_TO_DEADLINE) {
revert BoringOnChainQueue__MAXIMUM_MINIMUM_SECONDS_TO_DEADLINE();
}
if (minDiscount > maxDiscount) revert BoringOnChainQueue__BadDiscount();
// Make sure accountant can price it.
accountant.getRateInQuoteSafe(ERC20(assetOut));
withdrawAssets[assetOut] = WithdrawAsset({
allowWithdraws: true,
secondsToMaturity: secondsToMaturity,
minimumSecondsToDeadline: minimumSecondsToDeadline,
minDiscount: minDiscount,
maxDiscount: maxDiscount,
minimumShares: minimumShares
});
emit WithdrawAssetUpdated(
assetOut, secondsToMaturity, minimumSecondsToDeadline, minDiscount, maxDiscount, minimumShares
);
}
/**
* @notice Stop withdraws in an asset.
* @dev Callable by MULTISIG_ROLE.
* @param assetOut The asset to stop withdraws in.
*/
function stopWithdrawsInAsset(address assetOut) external requiresAuth {
withdrawAssets[assetOut].allowWithdraws = false;
emit WithdrawAssetStopped(assetOut);
}
/**
* @notice Cancel multiple user withdraws.
* @dev Callable by STRATEGIST_MULTISIG_ROLE.
*/
function cancelUserWithdraws(OnChainWithdraw[] calldata requests)
external
requiresAuth
returns (bytes32[] memory canceledRequestIds)
{
uint256 requestsLength = requests.length;
canceledRequestIds = new bytes32[](requestsLength);
for (uint256 i = 0; i < requestsLength; ++i) {
canceledRequestIds[i] = _cancelOnChainWithdraw(requests[i]);
}
}
//=============================== USER FUNCTIONS ================================
/**
* @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
virtual
requiresAuth
returns (bytes32 requestId)
{
WithdrawAsset memory withdrawAsset = withdrawAssets[assetOut];
_beforeNewRequest(withdrawAsset, amountOfShares, discount, secondsToDeadline);
boringVault.safeTransferFrom(msg.sender, address(this), amountOfShares);
(requestId,) = _queueOnChainWithdraw(
msg.sender, assetOut, amountOfShares, discount, withdrawAsset.secondsToMaturity, secondsToDeadline
);
}
/**
* @notice Request an on-chain withdraw with permit.
* @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.
* @param permitDeadline The deadline for the permit.
* @param v The v value of the permit signature.
* @param r The r value of the permit signature.
* @param s The s value of the permit signature.
* @return requestId The request Id.
*/
function requestOnChainWithdrawWithPermit(
address assetOut,
uint128 amountOfShares,
uint16 discount,
uint24 secondsToDeadline,
uint256 permitDeadline,
uint8 v,
bytes32 r,
bytes32 s
) external virtual requiresAuth returns (bytes32 requestId) {
WithdrawAsset memory withdrawAsset = withdrawAssets[assetOut];
_beforeNewRequest(withdrawAsset, amountOfShares, discount, secondsToDeadline);
try boringVault.permit(msg.sender, address(this), amountOfShares, permitDeadline, v, r, s) {}
catch {
if (boringVault.allowance(msg.sender, address(this)) < amountOfShares) {
revert BoringOnChainQueue__PermitFailedAndAllowanceTooLow();
}
}
boringVault.safeTransferFrom(msg.sender, address(this), amountOfShares);
(requestId,) = _queueOnChainWithdraw(
msg.sender, assetOut, amountOfShares, discount, withdrawAsset.secondsToMaturity, secondsToDeadline
);
}
/**
* @notice Cancel an on-chain withdraw.
* @param request The request to cancel.
* @return requestId The request Id.
*/
function cancelOnChainWithdraw(OnChainWithdraw memory request)
external
virtual
requiresAuth
returns (bytes32 requestId)
{
requestId = _cancelOnChainWithdrawWithUserCheck(request);
}
/**
* @notice Replace an on-chain withdraw.
* @param oldRequest The request to replace.
* @param discount The discount to apply to the new withdraw request in bps.
* @param secondsToDeadline The time in seconds the new withdraw request is valid for.
* @return oldRequestId The request Id of the old withdraw request.
* @return newRequestId The request Id of the new withdraw request.
*/
function replaceOnChainWithdraw(OnChainWithdraw memory oldRequest, uint16 discount, uint24 secondsToDeadline)
external
virtual
requiresAuth
returns (bytes32 oldRequestId, bytes32 newRequestId)
{
(oldRequestId, newRequestId) = _replaceOnChainWithdrawWithUserCheck(oldRequest, discount, secondsToDeadline);
}
//============================== SOLVER FUNCTIONS ===============================
/**
* @notice Solve multiple on-chain withdraws.
* @dev If `solveData` is empty, this contract will skip the callback function.
* @param requests The requests to solve.
* @param solveData The data to use to solve the requests.
* @param solver The address of the solver.
*/
function solveOnChainWithdraws(OnChainWithdraw[] calldata requests, bytes calldata solveData, address solver)
external
requiresAuth
{
if (isPaused) revert BoringOnChainQueue__Paused();
ERC20 solveAsset = ERC20(requests[0].assetOut);
uint256 requiredAssets;
uint256 totalShares;
uint256 requestsLength = requests.length;
for (uint256 i = 0; i < requestsLength; ++i) {
if (address(solveAsset) != requests[i].assetOut) revert BoringOnChainQueue__SolveAssetMismatch();
uint256 maturity = requests[i].creationTime + requests[i].secondsToMaturity;
if (block.timestamp < maturity) revert BoringOnChainQueue__NotMatured();
uint256 deadline = maturity + requests[i].secondsToDeadline;
if (block.timestamp > deadline) revert BoringOnChainQueue__DeadlinePassed();
requiredAssets += requests[i].amountOfAssets;
totalShares += requests[i].amountOfShares;
bytes32 requestId = _dequeueOnChainWithdraw(requests[i]);
emit OnChainWithdrawSolved(requestId, requests[i].user, block.timestamp);
}
// Transfer shares to solver.
boringVault.safeTransfer(solver, totalShares);
// Run callback function if data is provided.
if (solveData.length > 0) {
IBoringSolver(solver).boringSolve(
msg.sender, address(boringVault), address(solveAsset), totalShares, requiredAssets, solveData
);
}
for (uint256 i = 0; i < requestsLength; ++i) {
solveAsset.safeTransferFrom(solver, requests[i].user, requests[i].amountOfAssets);
}
}
//============================== VIEW FUNCTIONS ===============================
/**
* @notice Get all request Ids currently in the queue.
* @dev Includes requests that are not mature, matured, and expired. But does not include requests that have been solved.
* @return requestIds The request Ids.
*/
function getRequestIds() public view returns (bytes32[] memory) {
return _withdrawRequests.values();
}
/**
* @notice Get the request Id for a request.
* @param request The request.
* @return requestId The request Id.
*/
function getRequestId(OnChainWithdraw calldata request) external pure returns (bytes32 requestId) {
return keccak256(abi.encode(request));
}
/**
* @notice Preview assets out from a withdraw request.
*/
function previewAssetsOut(address assetOut, uint128 amountOfShares, uint16 discount)
public
view
returns (uint128 amountOfAssets128)
{
uint256 price = accountant.getRateInQuoteSafe(ERC20(assetOut));
price = price.mulDivDown(1e4 - discount, 1e4);
uint256 amountOfAssets = uint256(amountOfShares).mulDivDown(price, ONE_SHARE);
if (amountOfAssets > type(uint128).max) revert BoringOnChainQueue__Overflow();
amountOfAssets128 = uint128(amountOfAssets);
}
//============================= INTERNAL FUNCTIONS ==============================
/**
* @notice Before a new request is made, validate the input.
* @param withdrawAsset The withdraw asset.
* @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.
*/
function _beforeNewRequest(
WithdrawAsset memory withdrawAsset,
uint128 amountOfShares,
uint16 discount,
uint24 secondsToDeadline
) internal view virtual {
if (isPaused) revert BoringOnChainQueue__Paused();
if (!withdrawAsset.allowWithdraws) revert BoringOnChainQueue__WithdrawsNotAllowedForAsset();
if (discount < withdrawAsset.minDiscount || discount > withdrawAsset.maxDiscount) {
revert BoringOnChainQueue__BadDiscount();
}
if (amountOfShares < withdrawAsset.minimumShares) revert BoringOnChainQueue__BadShareAmount();
if (secondsToDeadline < withdrawAsset.minimumSecondsToDeadline) revert BoringOnChainQueue__BadDeadline();
}
/**
* @notice Cancel an on-chain withdraw.
* @dev Verifies that the request user is the same as the msg.sender.
* @param request The request to cancel.
* @return requestId The request Id.
*/
function _cancelOnChainWithdrawWithUserCheck(OnChainWithdraw memory request)
internal
virtual
onlyRequestUser(request.user, msg.sender)
returns (bytes32 requestId)
{
requestId = _cancelOnChainWithdraw(request);
}
/**
* @notice Cancel an on-chain withdraw.
* @param request The request to cancel.
* @return requestId The request Id.
*/
function _cancelOnChainWithdraw(OnChainWithdraw memory request) internal virtual returns (bytes32 requestId) {
requestId = _dequeueOnChainWithdraw(request);
boringVault.safeTransfer(request.user, request.amountOfShares);
emit OnChainWithdrawCancelled(requestId, request.user, block.timestamp);
}
/**
* @notice Replace an on-chain withdraw.
* @dev Verifies that the request user is the same as the msg.sender.
* @param oldRequest The request to replace.
* @param discount The discount to apply to the new withdraw request in bps.
* @param secondsToDeadline The time in seconds the new withdraw request is valid for.
* @return oldRequestId The request Id of the old withdraw request.
* @return newRequestId The request Id of the new withdraw request.
*/
function _replaceOnChainWithdrawWithUserCheck(
OnChainWithdraw memory oldRequest,
uint16 discount,
uint24 secondsToDeadline
)
internal
virtual
onlyRequestUser(oldRequest.user, msg.sender)
returns (bytes32 oldRequestId, bytes32 newRequestId)
{
(oldRequestId, newRequestId) = _replaceOnChainWithdraw(oldRequest, discount, secondsToDeadline);
}
/**
* @notice Replace an on-chain withdraw.
* @param oldRequest The request to replace.
* @param discount The discount to apply to the new withdraw request in bps.
* @param secondsToDeadline The time in seconds the new withdraw request is valid for.
* @return oldRequestId The request Id of the old withdraw request.
* @return newRequestId The request Id of the new withdraw request.
*/
function _replaceOnChainWithdraw(OnChainWithdraw memory oldRequest, uint16 discount, uint24 secondsToDeadline)
internal
virtual
onlyRequestUser(oldRequest.user, msg.sender)
returns (bytes32 oldRequestId, bytes32 newRequestId)
{
WithdrawAsset memory withdrawAsset = withdrawAssets[oldRequest.assetOut];
_beforeNewRequest(withdrawAsset, oldRequest.amountOfShares, discount, secondsToDeadline);
oldRequestId = _dequeueOnChainWithdraw(oldRequest);
emit OnChainWithdrawCancelled(oldRequestId, oldRequest.user, block.timestamp);
// Create new request.
(newRequestId,) = _queueOnChainWithdraw(
oldRequest.user,
oldRequest.assetOut,
oldRequest.amountOfShares,
discount,
withdrawAsset.secondsToMaturity,
secondsToDeadline
);
}
/**
* @notice Queue an on-chain withdraw.
* @dev Reverts if the request is already in the queue. Though this should be impossible.
* @param user The user that made the 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.
* @param secondsToMaturity The time in seconds it takes for the asset to mature.
* @param secondsToDeadline The time in seconds the request is valid for.
* @return requestId The request Id.
*/
function _queueOnChainWithdraw(
address user,
address assetOut,
uint128 amountOfShares,
uint16 discount,
uint24 secondsToMaturity,
uint24 secondsToDeadline
) internal virtual returns (bytes32 requestId, OnChainWithdraw memory req) {
// Create new request.
uint96 requestNonce;
// See nonce definition for unchecked safety.
unchecked {
// Set request nonce as current nonce, then increment nonce.
requestNonce = nonce++;
}
uint128 amountOfAssets128 = previewAssetsOut(assetOut, amountOfShares, discount);
uint40 timeNow = uint40(block.timestamp); // Safe to cast to uint40 as it won't overflow for 10s of thousands of years
req = OnChainWithdraw({
nonce: requestNonce,
user: user,
assetOut: assetOut,
amountOfShares: amountOfShares,
amountOfAssets: amountOfAssets128,
creationTime: timeNow,
secondsToMaturity: secondsToMaturity,
secondsToDeadline: secondsToDeadline
});
requestId = keccak256(abi.encode(req));
bool addedToSet = _withdrawRequests.add(requestId);
if (!addedToSet) revert BoringOnChainQueue__Keccak256Collision();
emit OnChainWithdrawRequested(
requestId,
user,
assetOut,
requestNonce,
amountOfShares,
amountOfAssets128,
timeNow,
secondsToMaturity,
secondsToDeadline
);
}
/**
* @notice Dequeue an on-chain withdraw.
* @dev Reverts if the request is not in the queue.
* @dev Does not remove the request from the onChainWithdraws mapping, so that
* it can be referenced later by off-chain systems if needed.
* @param request The request to dequeue.
* @return requestId The request Id.
*/
function _dequeueOnChainWithdraw(OnChainWithdraw memory request) internal virtual returns (bytes32 requestId) {
// Remove request from queue.
requestId = keccak256(abi.encode(request));
bool removedFromSet = _withdrawRequests.remove(requestId);
if (!removedFromSet) revert BoringOnChainQueue__RequestNotFound();
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.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 Return the 512-bit addition of two uint256.
*
* The result is stored in two 256 variables such that sum = high * 2²56 + low.
*/
function add512(uint256 a, uint256 b) internal pure returns (uint256 high, uint256 low) {
assembly ("memory-safe") {
low := add(a, b)
high := lt(low, a)
}
}
/**
* @dev Return the 512-bit multiplication of two uint256.
*
* The result is stored in two 256 variables such that product = high * 2²56 + low.
*/
function mul512(uint256 a, uint256 b) internal pure returns (uint256 high, uint256 low) {
// 512-bit multiply [high low] = x * y. Compute the product mod 2²56 and mod 2²56 - 1, then use
// the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = high * 2²56 + low.
assembly ("memory-safe") {
let mm := mulmod(a, b, not(0))
low := mul(a, b)
high := sub(sub(mm, low), lt(mm, low))
}
}
/**
* @dev Returns the addition of two unsigned integers, with a success flag (no overflow).
*/
function tryAdd(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
uint256 c = a + b;
success = c >= a;
result = c * SafeCast.toUint(success);
}
}
/**
* @dev Returns the subtraction of two unsigned integers, with a success flag (no overflow).
*/
function trySub(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
uint256 c = a - b;
success = c <= a;
result = c * SafeCast.toUint(success);
}
}
/**
* @dev Returns the multiplication of two unsigned integers, with a success flag (no overflow).
*/
function tryMul(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
uint256 c = a * b;
assembly ("memory-safe") {
// Only true when the multiplication doesn't overflow
// (c / a == b) || (a == 0)
success := or(eq(div(c, a), b), iszero(a))
}
// equivalent to: success ? c : 0
result = c * SafeCast.toUint(success);
}
}
/**
* @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 {
success = b > 0;
assembly ("memory-safe") {
// The `DIV` opcode returns zero when the denominator is 0.
result := div(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 {
success = b > 0;
assembly ("memory-safe") {
// The `MOD` opcode returns zero when the denominator is 0.
result := mod(a, b)
}
}
}
/**
* @dev Unsigned saturating addition, bounds to `2²56 - 1` instead of overflowing.
*/
function saturatingAdd(uint256 a, uint256 b) internal pure returns (uint256) {
(bool success, uint256 result) = tryAdd(a, b);
return ternary(success, result, type(uint256).max);
}
/**
* @dev Unsigned saturating subtraction, bounds to zero instead of overflowing.
*/
function saturatingSub(uint256 a, uint256 b) internal pure returns (uint256) {
(, uint256 result) = trySub(a, b);
return result;
}
/**
* @dev Unsigned saturating multiplication, bounds to `2²56 - 1` instead of overflowing.
*/
function saturatingMul(uint256 a, uint256 b) internal pure returns (uint256) {
(bool success, uint256 result) = tryMul(a, b);
return ternary(success, result, type(uint256).max);
}
/**
* @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 {
(uint256 high, uint256 low) = mul512(x, y);
// Handle non-overflow cases, 256 by 256 division.
if (high == 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 low / denominator;
}
// Make sure the result is less than 2²56. Also prevents denominator == 0.
if (denominator <= high) {
Panic.panic(ternary(denominator == 0, Panic.DIVISION_BY_ZERO, Panic.UNDER_OVERFLOW));
}
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [high low].
uint256 remainder;
assembly ("memory-safe") {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
high := sub(high, gt(remainder, low))
low := sub(low, 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 ("memory-safe") {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [high low] by twos.
low := div(low, twos)
// Flip twos such that it is 2²56 / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from high into low.
low |= high * twos;
// Invert denominator mod 2²56. Now that denominator is an odd number, it has an inverse modulo 2²56 such
// that denominator * inv = 1 mod 2²56. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv = 1 mod 24.
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 28
inverse *= 2 - denominator * inverse; // inverse mod 2¹6
inverse *= 2 - denominator * inverse; // inverse mod 2³²
inverse *= 2 - denominator * inverse; // inverse mod 264
inverse *= 2 - denominator * inverse; // inverse mod 2¹²8
inverse *= 2 - denominator * inverse; // inverse mod 2²56
// 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²56. Since the preconditions guarantee that the outcome is
// less than 2²56, this is the final result. We don't need to compute the high bits of the result and high
// is no longer required.
result = low * 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 Calculates floor(x * y >> n) with full precision. Throws if result overflows a uint256.
*/
function mulShr(uint256 x, uint256 y, uint8 n) internal pure returns (uint256 result) {
unchecked {
(uint256 high, uint256 low) = mul512(x, y);
if (high >= 1 << n) {
Panic.panic(Panic.UNDER_OVERFLOW);
}
return (high << (256 - n)) | (low >> n);
}
}
/**
* @dev Calculates x * y >> n with full precision, following the selected rounding direction.
*/
function mulShr(uint256 x, uint256 y, uint8 n, Rounding rounding) internal pure returns (uint256) {
return mulShr(x, y, n) + SafeCast.toUint(unsignedRoundsUp(rounding) && mulmod(x, y, 1 << n) > 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 `e_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¹²8)² = 2²56` 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 e_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 e_n = 2**(e-2).
// This is going to be our x_0 (and e_0)
xn = (3 * xn) >> 1; // e_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_n4 + 2 * a * x_n² + a²) / (4 * x_n²) - a
// = (x_n4 + 2 * a * x_n² + a² - 4 * a * x_n²) / (4 * x_n²)
// = (x_n4 - 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:
// e_{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) |
// = | e_n² / (2 * x_n) |
// = e_n² / | (2 * x_n) |
//
// For the first iteration, we have a special case where x_0 is known:
// e_1 = e_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:
// e_{n+1} = e_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; // e_1 := | x_1 - sqrt(a) | = 2**(e-4.5) -- special case, see above
xn = (xn + a / xn) >> 1; // e_2 := | x_2 - sqrt(a) | = 2**(e-9) -- general case with k = 4.5
xn = (xn + a / xn) >> 1; // e_3 := | x_3 - sqrt(a) | = 2**(e-18) -- general case with k = 9
xn = (xn + a / xn) >> 1; // e_4 := | x_4 - sqrt(a) | = 2**(e-36) -- general case with k = 18
xn = (xn + a / xn) >> 1; // e_5 := | x_5 - sqrt(a) | = 2**(e-72) -- general case with k = 36
xn = (xn + a / xn) >> 1; // e_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
// e_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 x) internal pure returns (uint256 r) {
// If value has upper 128 bits set, log2 result is at least 128
r = SafeCast.toUint(x > 0xffffffffffffffffffffffffffffffff) << 7;
// If upper 64 bits of 128-bit half set, add 64 to result
r |= SafeCast.toUint((x >> r) > 0xffffffffffffffff) << 6;
// If upper 32 bits of 64-bit half set, add 32 to result
r |= SafeCast.toUint((x >> r) > 0xffffffff) << 5;
// If upper 16 bits of 32-bit half set, add 16 to result
r |= SafeCast.toUint((x >> r) > 0xffff) << 4;
// If upper 8 bits of 16-bit half set, add 8 to result
r |= SafeCast.toUint((x >> r) > 0xff) << 3;
// If upper 4 bits of 8-bit half set, add 4 to result
r |= SafeCast.toUint((x >> r) > 0xf) << 2;
// Shifts value right by the current result and use it as an index into this lookup table:
//
// | x (4 bits) | index | table[index] = MSB position |
// |------------|---------|-----------------------------|
// | 0000 | 0 | table[0] = 0 |
// | 0001 | 1 | table[1] = 0 |
// | 0010 | 2 | table[2] = 1 |
// | 0011 | 3 | table[3] = 1 |
// | 0100 | 4 | table[4] = 2 |
// | 0101 | 5 | table[5] = 2 |
// | 0110 | 6 | table[6] = 2 |
// | 0111 | 7 | table[7] = 2 |
// | 1000 | 8 | table[8] = 3 |
// | 1001 | 9 | table[9] = 3 |
// | 1010 | 10 | table[10] = 3 |
// | 1011 | 11 | table[11] = 3 |
// | 1100 | 12 | table[12] = 3 |
// | 1101 | 13 | table[13] = 3 |
// | 1110 | 14 | table[14] = 3 |
// | 1111 | 15 | table[15] = 3 |
//
// The lookup table is represented as a 32-byte value with the MSB positions for 0-15 in the last 16 bytes.
assembly ("memory-safe") {
r := or(r, byte(shr(r, x), 0x0000010102020202030303030303030300000000000000000000000000000000))
}
}
/**
* @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 x) internal pure returns (uint256 r) {
// If value has upper 128 bits set, log2 result is at least 128
r = SafeCast.toUint(x > 0xffffffffffffffffffffffffffffffff) << 7;
// If upper 64 bits of 128-bit half set, add 64 to result
r |= SafeCast.toUint((x >> r) > 0xffffffffffffffff) << 6;
// If upper 32 bits of 64-bit half set, add 32 to result
r |= SafeCast.toUint((x >> r) > 0xffffffff) << 5;
// If upper 16 bits of 32-bit half set, add 16 to result
r |= SafeCast.toUint((x >> r) > 0xffff) << 4;
// Add 1 if upper 8 bits of 16-bit half set, and divide accumulated result by 8
return (r >> 3) | SafeCast.toUint((x >> r) > 0xff);
}
/**
* @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
// OpenZeppelin Contracts (last updated v5.3.0) (utils/Arrays.sol)
// This file was procedurally generated from scripts/generate/templates/Arrays.js.
pragma solidity ^0.8.20;
import {Comparators} from "./Comparators.sol";
import {SlotDerivation} from "./SlotDerivation.sol";
import {StorageSlot} from "./StorageSlot.sol";
import {Math} from "./math/Math.sol";
/**
* @dev Collection of functions related to array types.
*/
library Arrays {
using SlotDerivation for bytes32;
using StorageSlot for bytes32;
/**
* @dev Sort an array of uint256 (in memory) following the provided comparator function.
*
* This function does the sorting "in place", meaning that it overrides the input. The object is returned for
* convenience, but that returned value can be discarded safely if the caller has a memory pointer to the array.
*
* NOTE: this function's cost is `O(n · log(n))` in average and `O(n²)` in the worst case, with n the length of the
* array. Using it in view functions that are executed through `eth_call` is safe, but one should be very careful
* when executing this as part of a transaction. If the array being sorted is too large, the sort operation may
* consume more gas than is available in a block, leading to potential DoS.
*
* IMPORTANT: Consider memory side-effects when using custom comparator functions that access memory in an unsafe way.
*/
function sort(
uint256[] memory array,
function(uint256, uint256) pure returns (bool) comp
) internal pure returns (uint256[] memory) {
_quickSort(_begin(array), _end(array), comp);
return array;
}
/**
* @dev Variant of {sort} that sorts an array of uint256 in increasing order.
*/
function sort(uint256[] memory array) internal pure returns (uint256[] memory) {
sort(array, Comparators.lt);
return array;
}
/**
* @dev Sort an array of address (in memory) following the provided comparator function.
*
* This function does the sorting "in place", meaning that it overrides the input. The object is returned for
* convenience, but that returned value can be discarded safely if the caller has a memory pointer to the array.
*
* NOTE: this function's cost is `O(n · log(n))` in average and `O(n²)` in the worst case, with n the length of the
* array. Using it in view functions that are executed through `eth_call` is safe, but one should be very careful
* when executing this as part of a transaction. If the array being sorted is too large, the sort operation may
* consume more gas than is available in a block, leading to potential DoS.
*
* IMPORTANT: Consider memory side-effects when using custom comparator functions that access memory in an unsafe way.
*/
function sort(
address[] memory array,
function(address, address) pure returns (bool) comp
) internal pure returns (address[] memory) {
sort(_castToUint256Array(array), _castToUint256Comp(comp));
return array;
}
/**
* @dev Variant of {sort} that sorts an array of address in increasing order.
*/
function sort(address[] memory array) internal pure returns (address[] memory) {
sort(_castToUint256Array(array), Comparators.lt);
return array;
}
/**
* @dev Sort an array of bytes32 (in memory) following the provided comparator function.
*
* This function does the sorting "in place", meaning that it overrides the input. The object is returned for
* convenience, but that returned value can be discarded safely if the caller has a memory pointer to the array.
*
* NOTE: this function's cost is `O(n · log(n))` in average and `O(n²)` in the worst case, with n the length of the
* array. Using it in view functions that are executed through `eth_call` is safe, but one should be very careful
* when executing this as part of a transaction. If the array being sorted is too large, the sort operation may
* consume more gas than is available in a block, leading to potential DoS.
*
* IMPORTANT: Consider memory side-effects when using custom comparator functions that access memory in an unsafe way.
*/
function sort(
bytes32[] memory array,
function(bytes32, bytes32) pure returns (bool) comp
) internal pure returns (bytes32[] memory) {
sort(_castToUint256Array(array), _castToUint256Comp(comp));
return array;
}
/**
* @dev Variant of {sort} that sorts an array of bytes32 in increasing order.
*/
function sort(bytes32[] memory array) internal pure returns (bytes32[] memory) {
sort(_castToUint256Array(array), Comparators.lt);
return array;
}
/**
* @dev Performs a quick sort of a segment of memory. The segment sorted starts at `begin` (inclusive), and stops
* at end (exclusive). Sorting follows the `comp` comparator.
*
* Invariant: `begin <= end`. This is the case when initially called by {sort} and is preserved in subcalls.
*
* IMPORTANT: Memory locations between `begin` and `end` are not validated/zeroed. This function should
* be used only if the limits are within a memory array.
*/
function _quickSort(uint256 begin, uint256 end, function(uint256, uint256) pure returns (bool) comp) private pure {
unchecked {
if (end - begin < 0x40) return;
// Use first element as pivot
uint256 pivot = _mload(begin);
// Position where the pivot should be at the end of the loop
uint256 pos = begin;
for (uint256 it = begin + 0x20; it < end; it += 0x20) {
if (comp(_mload(it), pivot)) {
// If the value stored at the iterator's position comes before the pivot, we increment the
// position of the pivot and move the value there.
pos += 0x20;
_swap(pos, it);
}
}
_swap(begin, pos); // Swap pivot into place
_quickSort(begin, pos, comp); // Sort the left side of the pivot
_quickSort(pos + 0x20, end, comp); // Sort the right side of the pivot
}
}
/**
* @dev Pointer to the memory location of the first element of `array`.
*/
function _begin(uint256[] memory array) private pure returns (uint256 ptr) {
assembly ("memory-safe") {
ptr := add(array, 0x20)
}
}
/**
* @dev Pointer to the memory location of the first memory word (32bytes) after `array`. This is the memory word
* that comes just after the last element of the array.
*/
function _end(uint256[] memory array) private pure returns (uint256 ptr) {
unchecked {
return _begin(array) + array.length * 0x20;
}
}
/**
* @dev Load memory word (as a uint256) at location `ptr`.
*/
function _mload(uint256 ptr) private pure returns (uint256 value) {
assembly {
value := mload(ptr)
}
}
/**
* @dev Swaps the elements memory location `ptr1` and `ptr2`.
*/
function _swap(uint256 ptr1, uint256 ptr2) private pure {
assembly {
let value1 := mload(ptr1)
let value2 := mload(ptr2)
mstore(ptr1, value2)
mstore(ptr2, value1)
}
}
/// @dev Helper: low level cast address memory array to uint256 memory array
function _castToUint256Array(address[] memory input) private pure returns (uint256[] memory output) {
assembly {
output := input
}
}
/// @dev Helper: low level cast bytes32 memory array to uint256 memory array
function _castToUint256Array(bytes32[] memory input) private pure returns (uint256[] memory output) {
assembly {
output := input
}
}
/// @dev Helper: low level cast address comp function to uint256 comp function
function _castToUint256Comp(
function(address, address) pure returns (bool) input
) private pure returns (function(uint256, uint256) pure returns (bool) output) {
assembly {
output := input
}
}
/// @dev Helper: low level cast bytes32 comp function to uint256 comp function
function _castToUint256Comp(
function(bytes32, bytes32) pure returns (bool) input
) private pure returns (function(uint256, uint256) pure returns (bool) output) {
assembly {
output := input
}
}
/**
* @dev Searches a sorted `array` and returns the first index that contains
* a value greater or equal to `element`. If no such index exists (i.e. all
* values in the array are strictly less than `element`), the array length is
* returned. Time complexity O(log n).
*
* NOTE: The `array` is expected to be sorted in ascending order, and to
* contain no repeated elements.
*
* IMPORTANT: Deprecated. This implementation behaves as {lowerBound} but lacks
* support for repeated elements in the array. The {lowerBound} function should
* be used instead.
*/
function findUpperBound(uint256[] storage array, uint256 element) internal view returns (uint256) {
uint256 low = 0;
uint256 high = array.length;
if (high == 0) {
return 0;
}
while (low < high) {
uint256 mid = Math.average(low, high);
// Note that mid will always be strictly less than high (i.e. it will be a valid array index)
// because Math.average rounds towards zero (it does integer division with truncation).
if (unsafeAccess(array, mid).value > element) {
high = mid;
} else {
low = mid + 1;
}
}
// At this point `low` is the exclusive upper bound. We will return the inclusive upper bound.
if (low > 0 && unsafeAccess(array, low - 1).value == element) {
return low - 1;
} else {
return low;
}
}
/**
* @dev Searches an `array` sorted in ascending order and returns the first
* index that contains a value greater or equal than `element`. If no such index
* exists (i.e. all values in the array are strictly less than `element`), the array
* length is returned. Time complexity O(log n).
*
* See C++'s https://en.cppreference.com/w/cpp/algorithm/lower_bound[lower_bound].
*/
function lowerBound(uint256[] storage array, uint256 element) internal view returns (uint256) {
uint256 low = 0;
uint256 high = array.length;
if (high == 0) {
return 0;
}
while (low < high) {
uint256 mid = Math.average(low, high);
// Note that mid will always be strictly less than high (i.e. it will be a valid array index)
// because Math.average rounds towards zero (it does integer division with truncation).
if (unsafeAccess(array, mid).value < element) {
// this cannot overflow because mid < high
unchecked {
low = mid + 1;
}
} else {
high = mid;
}
}
return low;
}
/**
* @dev Searches an `array` sorted in ascending order and returns the first
* index that contains a value strictly greater than `element`. If no such index
* exists (i.e. all values in the array are strictly less than `element`), the array
* length is returned. Time complexity O(log n).
*
* See C++'s https://en.cppreference.com/w/cpp/algorithm/upper_bound[upper_bound].
*/
function upperBound(uint256[] storage array, uint256 element) internal view returns (uint256) {
uint256 low = 0;
uint256 high = array.length;
if (high == 0) {
return 0;
}
while (low < high) {
uint256 mid = Math.average(low, high);
// Note that mid will always be strictly less than high (i.e. it will be a valid array index)
// because Math.average rounds towards zero (it does integer division with truncation).
if (unsafeAccess(array, mid).value > element) {
high = mid;
} else {
// this cannot overflow because mid < high
unchecked {
low = mid + 1;
}
}
}
return low;
}
/**
* @dev Same as {lowerBound}, but with an array in memory.
*/
function lowerBoundMemory(uint256[] memory array, uint256 element) internal pure returns (uint256) {
uint256 low = 0;
uint256 high = array.length;
if (high == 0) {
return 0;
}
while (low < high) {
uint256 mid = Math.average(low, high);
// Note that mid will always be strictly less than high (i.e. it will be a valid array index)
// because Math.average rounds towards zero (it does integer division with truncation).
if (unsafeMemoryAccess(array, mid) < element) {
// this cannot overflow because mid < high
unchecked {
low = mid + 1;
}
} else {
high = mid;
}
}
return low;
}
/**
* @dev Same as {upperBound}, but with an array in memory.
*/
function upperBoundMemory(uint256[] memory array, uint256 element) internal pure returns (uint256) {
uint256 low = 0;
uint256 high = array.length;
if (high == 0) {
return 0;
}
while (low < high) {
uint256 mid = Math.average(low, high);
// Note that mid will always be strictly less than high (i.e. it will be a valid array index)
// because Math.average rounds towards zero (it does integer division with truncation).
if (unsafeMemoryAccess(array, mid) > element) {
high = mid;
} else {
// this cannot overflow because mid < high
unchecked {
low = mid + 1;
}
}
}
return low;
}
/**
* @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
*
* WARNING: Only use if you are certain `pos` is lower than the array length.
*/
function unsafeAccess(address[] storage arr, uint256 pos) internal pure returns (StorageSlot.AddressSlot storage) {
bytes32 slot;
assembly ("memory-safe") {
slot := arr.slot
}
return slot.deriveArray().offset(pos).getAddressSlot();
}
/**
* @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
*
* WARNING: Only use if you are certain `pos` is lower than the array length.
*/
function unsafeAccess(bytes32[] storage arr, uint256 pos) internal pure returns (StorageSlot.Bytes32Slot storage) {
bytes32 slot;
assembly ("memory-safe") {
slot := arr.slot
}
return slot.deriveArray().offset(pos).getBytes32Slot();
}
/**
* @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
*
* WARNING: Only use if you are certain `pos` is lower than the array length.
*/
function unsafeAccess(uint256[] storage arr, uint256 pos) internal pure returns (StorageSlot.Uint256Slot storage) {
bytes32 slot;
assembly ("memory-safe") {
slot := arr.slot
}
return slot.deriveArray().offset(pos).getUint256Slot();
}
/**
* @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
*
* WARNING: Only use if you are certain `pos` is lower than the array length.
*/
function unsafeAccess(bytes[] storage arr, uint256 pos) internal pure returns (StorageSlot.BytesSlot storage) {
bytes32 slot;
assembly ("memory-safe") {
slot := arr.slot
}
return slot.deriveArray().offset(pos).getBytesSlot();
}
/**
* @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
*
* WARNING: Only use if you are certain `pos` is lower than the array length.
*/
function unsafeAccess(string[] storage arr, uint256 pos) internal pure returns (StorageSlot.StringSlot storage) {
bytes32 slot;
assembly ("memory-safe") {
slot := arr.slot
}
return slot.deriveArray().offset(pos).getStringSlot();
}
/**
* @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
*
* WARNING: Only use if you are certain `pos` is lower than the array length.
*/
function unsafeMemoryAccess(address[] memory arr, uint256 pos) internal pure returns (address res) {
assembly {
res := mload(add(add(arr, 0x20), mul(pos, 0x20)))
}
}
/**
* @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
*
* WARNING: Only use if you are certain `pos` is lower than the array length.
*/
function unsafeMemoryAccess(bytes32[] memory arr, uint256 pos) internal pure returns (bytes32 res) {
assembly {
res := mload(add(add(arr, 0x20), mul(pos, 0x20)))
}
}
/**
* @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
*
* WARNING: Only use if you are certain `pos` is lower than the array length.
*/
function unsafeMemoryAccess(uint256[] memory arr, uint256 pos) internal pure returns (uint256 res) {
assembly {
res := mload(add(add(arr, 0x20), mul(pos, 0x20)))
}
}
/**
* @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
*
* WARNING: Only use if you are certain `pos` is lower than the array length.
*/
function unsafeMemoryAccess(bytes[] memory arr, uint256 pos) internal pure returns (bytes memory res) {
assembly {
res := mload(add(add(arr, 0x20), mul(pos, 0x20)))
}
}
/**
* @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
*
* WARNING: Only use if you are certain `pos` is lower than the array length.
*/
function unsafeMemoryAccess(string[] memory arr, uint256 pos) internal pure returns (string memory res) {
assembly {
res := mload(add(add(arr, 0x20), mul(pos, 0x20)))
}
}
/**
* @dev Helper to set the length of a dynamic array. Directly writing to `.length` is forbidden.
*
* WARNING: this does not clear elements if length is reduced, of initialize elements if length is increased.
*/
function unsafeSetLength(address[] storage array, uint256 len) internal {
assembly ("memory-safe") {
sstore(array.slot, len)
}
}
/**
* @dev Helper to set the length of a dynamic array. Directly writing to `.length` is forbidden.
*
* WARNING: this does not clear elements if length is reduced, of initialize elements if length is increased.
*/
function unsafeSetLength(bytes32[] storage array, uint256 len) internal {
assembly ("memory-safe") {
sstore(array.slot, len)
}
}
/**
* @dev Helper to set the length of a dynamic array. Directly writing to `.length` is forbidden.
*
* WARNING: this does not clear elements if length is reduced, of initialize elements if length is increased.
*/
function unsafeSetLength(uint256[] storage array, uint256 len) internal {
assembly ("memory-safe") {
sstore(array.slot, len)
}
}
/**
* @dev Helper to set the length of a dynamic array. Directly writing to `.length` is forbidden.
*
* WARNING: this does not clear elements if length is reduced, of initialize elements if length is increased.
*/
function unsafeSetLength(bytes[] storage array, uint256 len) internal {
assembly ("memory-safe") {
sstore(array.slot, len)
}
}
/**
* @dev Helper to set the length of a dynamic array. Directly writing to `.length` is forbidden.
*
* WARNING: this does not clear elements if length is reduced, of initialize elements if length is increased.
*/
function unsafeSetLength(string[] storage array, uint256 len) internal {
assembly ("memory-safe") {
sstore(array.slot, len)
}
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.21;
import {ERC20} from "@solmate/tokens/ERC20.sol";
import {WETH} from "@solmate/tokens/WETH.sol";
import {BoringVault} from "../../../src/base/BoringVault.sol";
import {AccountantWithRateProviders} from "../../../src/base/Roles/AccountantWithRateProviders.sol";
import {FixedPointMathLib} from "@solmate/utils/FixedPointMathLib.sol";
import {SafeTransferLib} from "@solmate/utils/SafeTransferLib.sol";
import {BeforeTransferHook} from "../../../src/interfaces/BeforeTransferHook.sol";
import {Auth, Authority} from "@solmate/auth/Auth.sol";
import {ReentrancyGuard} from "@solmate/utils/ReentrancyGuard.sol";
import {IPausable} from "../../../src/interfaces/IPausable.sol";
contract TellerWithMultiAssetSupport is Auth, BeforeTransferHook, ReentrancyGuard, IPausable {
using FixedPointMathLib for uint256;
using SafeTransferLib for ERC20;
using SafeTransferLib for WETH;
// ========================================= STRUCTS =========================================
/**
* @param allowDeposits bool indicating whether or not deposits are allowed for this asset.
* @param allowWithdraws bool indicating whether or not withdraws are allowed for this asset.
* @param sharePremium uint16 indicating the premium to apply to the shares minted.
* where 40 represents a 40bps reduction in shares minted using this asset.
*/
struct Asset {
bool allowDeposits;
bool allowWithdraws;
uint16 sharePremium;
}
// ========================================= CONSTANTS =========================================
/**
* @notice Native address used to tell the contract to handle native asset deposits.
*/
address internal constant NATIVE = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
/**
* @notice The maximum possible share lock period.
*/
uint256 internal constant MAX_SHARE_LOCK_PERIOD = 3 days;
/**
* @notice The maximum possible share premium that can be set using `updateAssetData`.
* @dev 1,000 or 10%
*/
uint16 internal constant MAX_SHARE_PREMIUM = 1000;
// ========================================= STATE =========================================
/**
* @notice Mapping ERC20s to their assetData.
*/
mapping(ERC20 => Asset) public assetData;
/**
* @notice The deposit nonce used to map to a deposit hash.
*/
uint96 public depositNonce;
/**
* @notice After deposits, shares are locked to the msg.sender's address
* for `shareLockPeriod`.
* @dev During this time all trasnfers from msg.sender will revert, and
* deposits are refundable.
*/
uint64 public shareLockPeriod;
/**
* @notice Used to pause calls to `deposit` and `depositWithPermit`.
*/
bool public isPaused;
/**
* @dev Maps deposit nonce to keccak256(address receiver, address depositAsset, uint256 depositAmount, uint256 shareAmount, uint256 timestamp, uint256 shareLockPeriod).
*/
mapping(uint256 => bytes32) public publicDepositHistory;
/**
* @notice Maps user address to the time their shares will be unlocked.
*/
mapping(address => uint256) public shareUnlockTime;
/**
* @notice Mapping `from` address to a bool to deny them from transferring shares.
*/
mapping(address => bool) public fromDenyList;
/**
* @notice Mapping `to` address to a bool to deny them from receiving shares.
*/
mapping(address => bool) public toDenyList;
/**
* @notice Mapping `opeartor` address to a bool to deny them from calling `transfer` or `transferFrom`.
*/
mapping(address => bool) public operatorDenyList;
//============================== ERRORS ===============================
error TellerWithMultiAssetSupport__ShareLockPeriodTooLong();
error TellerWithMultiAssetSupport__SharesAreLocked();
error TellerWithMultiAssetSupport__SharesAreUnLocked();
error TellerWithMultiAssetSupport__BadDepositHash();
error TellerWithMultiAssetSupport__AssetNotSupported();
error TellerWithMultiAssetSupport__ZeroAssets();
error TellerWithMultiAssetSupport__MinimumMintNotMet();
error TellerWithMultiAssetSupport__MinimumAssetsNotMet();
error TellerWithMultiAssetSupport__PermitFailedAndAllowanceTooLow();
error TellerWithMultiAssetSupport__ZeroShares();
error TellerWithMultiAssetSupport__DualDeposit();
error TellerWithMultiAssetSupport__Paused();
error TellerWithMultiAssetSupport__TransferDenied(address from, address to, address operator);
error TellerWithMultiAssetSupport__SharePremiumTooLarge();
error TellerWithMultiAssetSupport__CannotDepositNative();
//============================== EVENTS ===============================
event Paused();
event Unpaused();
event AssetDataUpdated(address indexed asset, bool allowDeposits, bool allowWithdraws, uint16 sharePremium);
event Deposit(
uint256 indexed nonce,
address indexed receiver,
address indexed depositAsset,
uint256 depositAmount,
uint256 shareAmount,
uint256 depositTimestamp,
uint256 shareLockPeriodAtTimeOfDeposit
);
event BulkDeposit(address indexed asset, uint256 depositAmount);
event BulkWithdraw(address indexed asset, uint256 shareAmount);
event DepositRefunded(uint256 indexed nonce, bytes32 depositHash, address indexed user);
event DenyFrom(address indexed user);
event DenyTo(address indexed user);
event DenyOperator(address indexed user);
event AllowFrom(address indexed user);
event AllowTo(address indexed user);
event AllowOperator(address indexed user);
// =============================== MODIFIERS ===============================
/**
* @notice Reverts if the deposit asset is the native asset.
*/
modifier revertOnNativeDeposit(address depositAsset) {
if (depositAsset == NATIVE) revert TellerWithMultiAssetSupport__CannotDepositNative();
_;
}
//============================== IMMUTABLES ===============================
/**
* @notice The BoringVault this contract is working with.
*/
BoringVault public immutable vault;
/**
* @notice The AccountantWithRateProviders this contract is working with.
*/
AccountantWithRateProviders public immutable accountant;
/**
* @notice One share of the BoringVault.
*/
uint256 internal immutable ONE_SHARE;
/**
* @notice The native wrapper contract.
*/
WETH public immutable nativeWrapper;
constructor(address _owner, address _vault, address _accountant, address _weth)
Auth(_owner, Authority(address(0)))
{
vault = BoringVault(payable(_vault));
ONE_SHARE = 10 ** vault.decimals();
accountant = AccountantWithRateProviders(_accountant);
nativeWrapper = WETH(payable(_weth));
}
// ========================================= ADMIN FUNCTIONS =========================================
/**
* @notice Pause this contract, which prevents future calls to `deposit` and `depositWithPermit`.
* @dev Callable by MULTISIG_ROLE.
*/
function pause() external requiresAuth {
isPaused = true;
emit Paused();
}
/**
* @notice Unpause this contract, which allows future calls to `deposit` and `depositWithPermit`.
* @dev Callable by MULTISIG_ROLE.
*/
function unpause() external requiresAuth {
isPaused = false;
emit Unpaused();
}
/**
* @notice Updates the asset data for a given asset.
* @dev The accountant must also support pricing this asset, else the `deposit` call will revert.
* @dev Callable by OWNER_ROLE.
*/
function updateAssetData(ERC20 asset, bool allowDeposits, bool allowWithdraws, uint16 sharePremium)
external
requiresAuth
{
if (sharePremium > MAX_SHARE_PREMIUM) revert TellerWithMultiAssetSupport__SharePremiumTooLarge();
assetData[asset] = Asset(allowDeposits, allowWithdraws, sharePremium);
emit AssetDataUpdated(address(asset), allowDeposits, allowWithdraws, sharePremium);
}
/**
* @notice Sets the share lock period.
* @dev This not only locks shares to the user address, but also serves as the pending deposit period, where deposits can be reverted.
* @dev If a new shorter share lock period is set, users with pending share locks could make a new deposit to receive 1 wei shares,
* and have their shares unlock sooner than their original deposit allows. This state would allow for the user deposit to be refunded,
* but only if they have not transferred their shares out of there wallet. This is an accepted limitation, and should be known when decreasing
* the share lock period.
* @dev Callable by OWNER_ROLE.
*/
function setShareLockPeriod(uint64 _shareLockPeriod) external requiresAuth {
if (_shareLockPeriod > MAX_SHARE_LOCK_PERIOD) revert TellerWithMultiAssetSupport__ShareLockPeriodTooLong();
shareLockPeriod = _shareLockPeriod;
}
/**
* @notice Deny a user from transferring or receiving shares.
* @dev Callable by OWNER_ROLE, and DENIER_ROLE.
*/
function denyAll(address user) external requiresAuth {
fromDenyList[user] = true;
toDenyList[user] = true;
operatorDenyList[user] = true;
emit DenyFrom(user);
emit DenyTo(user);
emit DenyOperator(user);
}
/**
* @notice Allow a user to transfer or receive shares.
* @dev Callable by OWNER_ROLE, and DENIER_ROLE.
*/
function allowAll(address user) external requiresAuth {
fromDenyList[user] = false;
toDenyList[user] = false;
operatorDenyList[user] = false;
emit AllowFrom(user);
emit AllowTo(user);
emit AllowOperator(user);
}
/**
* @notice Deny a user from transferring shares.
* @dev Callable by OWNER_ROLE, and DENIER_ROLE.
*/
function denyFrom(address user) external requiresAuth {
fromDenyList[user] = true;
emit DenyFrom(user);
}
/**
* @notice Allow a user to transfer shares.
* @dev Callable by OWNER_ROLE, and DENIER_ROLE.
*/
function allowFrom(address user) external requiresAuth {
fromDenyList[user] = false;
emit AllowFrom(user);
}
/**
* @notice Deny a user from receiving shares.
* @dev Callable by OWNER_ROLE, and DENIER_ROLE.
*/
function denyTo(address user) external requiresAuth {
toDenyList[user] = true;
emit DenyTo(user);
}
/**
* @notice Allow a user to receive shares.
* @dev Callable by OWNER_ROLE, and DENIER_ROLE.
*/
function allowTo(address user) external requiresAuth {
toDenyList[user] = false;
emit AllowTo(user);
}
/**
* @notice Deny an operator from transferring shares.
* @dev Callable by OWNER_ROLE, and DENIER_ROLE.
*/
function denyOperator(address user) external requiresAuth {
operatorDenyList[user] = true;
emit DenyOperator(user);
}
/**
* @notice Allow an operator to transfer shares.
* @dev Callable by OWNER_ROLE, and DENIER_ROLE.
*/
function allowOperator(address user) external requiresAuth {
operatorDenyList[user] = false;
emit AllowOperator(user);
}
// ========================================= BeforeTransferHook FUNCTIONS =========================================
/**
* @notice Implement beforeTransfer hook to check if shares are locked, or if `from`, `to`, or `operator` are on the deny list.
* @notice If share lock period is set to zero, then users will be able to mint and transfer in the same tx.
* if this behavior is not desired then a share lock period of >=1 should be used.
*/
function beforeTransfer(address from, address to, address operator) public view virtual {
if (fromDenyList[from] || toDenyList[to] || operatorDenyList[operator]) {
revert TellerWithMultiAssetSupport__TransferDenied(from, to, operator);
}
if (shareUnlockTime[from] > block.timestamp) revert TellerWithMultiAssetSupport__SharesAreLocked();
}
// ========================================= REVERT DEPOSIT FUNCTIONS =========================================
/**
* @notice Allows DEPOSIT_REFUNDER_ROLE to revert a pending deposit.
* @dev Once a deposit share lock period has passed, it can no longer be reverted.
* @dev It is possible the admin does not setup the BoringVault to call the transfer hook,
* but this contract can still be saving share lock state. In the event this happens
* deposits are still refundable if the user has not transferred their shares.
* But there is no guarantee that the user has not transferred their shares.
* @dev Callable by STRATEGIST_MULTISIG_ROLE.
*/
function refundDeposit(
uint256 nonce,
address receiver,
address depositAsset,
uint256 depositAmount,
uint256 shareAmount,
uint256 depositTimestamp,
uint256 shareLockUpPeriodAtTimeOfDeposit
) external requiresAuth {
if ((block.timestamp - depositTimestamp) >= shareLockUpPeriodAtTimeOfDeposit) {
// Shares are already unlocked, so we can not revert deposit.
revert TellerWithMultiAssetSupport__SharesAreUnLocked();
}
bytes32 depositHash = keccak256(
abi.encode(
receiver, depositAsset, depositAmount, shareAmount, depositTimestamp, shareLockUpPeriodAtTimeOfDeposit
)
);
if (publicDepositHistory[nonce] != depositHash) revert TellerWithMultiAssetSupport__BadDepositHash();
// Delete hash to prevent refund gas.
delete publicDepositHistory[nonce];
// If deposit used native asset, send user back wrapped native asset.
depositAsset = depositAsset == NATIVE ? address(nativeWrapper) : depositAsset;
// Burn shares and refund assets to receiver.
vault.exit(receiver, ERC20(depositAsset), depositAmount, receiver, shareAmount);
emit DepositRefunded(nonce, depositHash, receiver);
}
// ========================================= USER FUNCTIONS =========================================
/**
* @notice Allows users to deposit into the BoringVault, if this contract is not paused.
* @dev Publicly callable.
*/
function deposit(ERC20 depositAsset, uint256 depositAmount, uint256 minimumMint)
external
payable
requiresAuth
nonReentrant
returns (uint256 shares)
{
Asset memory asset = _beforeDeposit(depositAsset);
address from;
if (address(depositAsset) == NATIVE) {
if (msg.value == 0) revert TellerWithMultiAssetSupport__ZeroAssets();
nativeWrapper.deposit{value: msg.value}();
// Set depositAmount to msg.value.
depositAmount = msg.value;
nativeWrapper.safeApprove(address(vault), depositAmount);
// Update depositAsset to nativeWrapper.
depositAsset = nativeWrapper;
// Set from to this address since user transferred value.
from = address(this);
} else {
if (msg.value > 0) revert TellerWithMultiAssetSupport__DualDeposit();
from = msg.sender;
}
shares = _erc20Deposit(depositAsset, depositAmount, minimumMint, from, msg.sender, asset);
_afterPublicDeposit(msg.sender, depositAsset, depositAmount, shares, shareLockPeriod);
}
/**
* @notice Allows users to deposit into BoringVault using permit.
* @dev Publicly callable.
*/
function depositWithPermit(
ERC20 depositAsset,
uint256 depositAmount,
uint256 minimumMint,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external requiresAuth nonReentrant revertOnNativeDeposit(address(depositAsset)) returns (uint256 shares) {
Asset memory asset = _beforeDeposit(depositAsset);
_handlePermit(depositAsset, depositAmount, deadline, v, r, s);
shares = _erc20Deposit(depositAsset, depositAmount, minimumMint, msg.sender, msg.sender, asset);
_afterPublicDeposit(msg.sender, depositAsset, depositAmount, shares, shareLockPeriod);
}
/**
* @notice Allows on ramp role to deposit into this contract.
* @dev Does NOT support native deposits.
* @dev Callable by SOLVER_ROLE.
*/
function bulkDeposit(ERC20 depositAsset, uint256 depositAmount, uint256 minimumMint, address to)
external
requiresAuth
nonReentrant
returns (uint256 shares)
{
Asset memory asset = _beforeDeposit(depositAsset);
shares = _erc20Deposit(depositAsset, depositAmount, minimumMint, msg.sender, to, asset);
emit BulkDeposit(address(depositAsset), depositAmount);
}
/**
* @notice Allows off ramp role to withdraw from this contract.
* @dev Callable by SOLVER_ROLE.
*/
function bulkWithdraw(ERC20 withdrawAsset, uint256 shareAmount, uint256 minimumAssets, address to)
external
requiresAuth
returns (uint256 assetsOut)
{
if (isPaused) revert TellerWithMultiAssetSupport__Paused();
Asset memory asset = assetData[withdrawAsset];
if (!asset.allowWithdraws) revert TellerWithMultiAssetSupport__AssetNotSupported();
if (shareAmount == 0) revert TellerWithMultiAssetSupport__ZeroShares();
assetsOut = shareAmount.mulDivDown(accountant.getRateInQuoteSafe(withdrawAsset), ONE_SHARE);
if (assetsOut < minimumAssets) revert TellerWithMultiAssetSupport__MinimumAssetsNotMet();
vault.exit(to, withdrawAsset, assetsOut, msg.sender, shareAmount);
emit BulkWithdraw(address(withdrawAsset), shareAmount);
}
// ========================================= INTERNAL HELPER FUNCTIONS =========================================
/**
* @notice Implements a common ERC20 deposit into BoringVault.
*/
function _erc20Deposit(
ERC20 depositAsset,
uint256 depositAmount,
uint256 minimumMint,
address from,
address to,
Asset memory asset
) internal returns (uint256 shares) {
if (depositAmount == 0) revert TellerWithMultiAssetSupport__ZeroAssets();
shares = depositAmount.mulDivDown(ONE_SHARE, accountant.getRateInQuoteSafe(depositAsset));
shares = asset.sharePremium > 0 ? shares.mulDivDown(1e4 - asset.sharePremium, 1e4) : shares;
if (shares < minimumMint) revert TellerWithMultiAssetSupport__MinimumMintNotMet();
vault.enter(from, depositAsset, depositAmount, to, shares);
}
/**
* @notice Handle pre-deposit checks.
*/
function _beforeDeposit(ERC20 depositAsset) internal view returns (Asset memory asset) {
if (isPaused) revert TellerWithMultiAssetSupport__Paused();
asset = assetData[depositAsset];
if (!asset.allowDeposits) revert TellerWithMultiAssetSupport__AssetNotSupported();
}
/**
* @notice Handle share lock logic, and event.
*/
function _afterPublicDeposit(
address user,
ERC20 depositAsset,
uint256 depositAmount,
uint256 shares,
uint256 currentShareLockPeriod
) internal {
// Increment then assign as its slightly more gas efficient.
uint256 nonce = ++depositNonce;
// Only set share unlock time and history if share lock period is greater than 0.
if (currentShareLockPeriod > 0) {
shareUnlockTime[user] = block.timestamp + currentShareLockPeriod;
publicDepositHistory[nonce] = keccak256(
abi.encode(user, depositAsset, depositAmount, shares, block.timestamp, currentShareLockPeriod)
);
}
emit Deposit(nonce, user, address(depositAsset), depositAmount, shares, block.timestamp, currentShareLockPeriod);
}
/**
* @notice Handle permit logic.
*/
function _handlePermit(ERC20 depositAsset, uint256 depositAmount, uint256 deadline, uint8 v, bytes32 r, bytes32 s)
internal
{
try depositAsset.permit(msg.sender, address(vault), depositAmount, deadline, v, r, s) {}
catch {
if (depositAsset.allowance(msg.sender, address(vault)) < depositAmount) {
revert TellerWithMultiAssetSupport__PermitFailedAndAllowanceTooLow();
}
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC721/IERC721Receiver.sol)
pragma solidity ^0.8.20;
/**
* @title ERC-721 token receiver interface
* @dev Interface for any contract that wants to support safeTransfers
* from ERC-721 asset contracts.
*/
interface IERC721Receiver {
/**
* @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
* by `operator` from `from`, this function is called.
*
* It must return its Solidity selector to confirm the token transfer.
* If any other value is returned or the interface is not implemented by the recipient, the transfer will be
* reverted.
*
* The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.
*/
function onERC721Received(
address operator,
address from,
uint256 tokenId,
bytes calldata data
) external returns (bytes4);
}// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.21;
import {Address} from "@openzeppelin/contracts/utils/Address.sol";
import {ERC721Holder} from "@openzeppelin/contracts/token/ERC721/utils/ERC721Holder.sol";
import {ERC1155Holder} from "@openzeppelin/contracts/token/ERC1155/utils/ERC1155Holder.sol";
import {FixedPointMathLib} from "@solmate/utils/FixedPointMathLib.sol";
import {SafeTransferLib} from "@solmate/utils/SafeTransferLib.sol";
import {ERC20} from "@solmate/tokens/ERC20.sol";
import {BeforeTransferHook} from "../../src/interfaces/BeforeTransferHook.sol";
import {Auth, Authority} from "@solmate/auth/Auth.sol";
contract BoringVault is ERC20, Auth, ERC721Holder, ERC1155Holder {
using Address for address;
using SafeTransferLib for ERC20;
using FixedPointMathLib for uint256;
// ========================================= STATE =========================================
/**
* @notice Contract responsbile for implementing `beforeTransfer`.
*/
BeforeTransferHook public hook;
//============================== EVENTS ===============================
event Enter(address indexed from, address indexed asset, uint256 amount, address indexed to, uint256 shares);
event Exit(address indexed to, address indexed asset, uint256 amount, address indexed from, uint256 shares);
//============================== CONSTRUCTOR ===============================
constructor(address _owner, string memory _name, string memory _symbol, uint8 _decimals)
ERC20(_name, _symbol, _decimals)
Auth(_owner, Authority(address(0)))
{}
//============================== MANAGE ===============================
/**
* @notice Allows manager to make an arbitrary function call from this contract.
* @dev Callable by MANAGER_ROLE.
*/
function manage(address target, bytes calldata data, uint256 value)
external
requiresAuth
returns (bytes memory result)
{
result = target.functionCallWithValue(data, value);
}
/**
* @notice Allows manager to make arbitrary function calls from this contract.
* @dev Callable by MANAGER_ROLE.
*/
function manage(address[] calldata targets, bytes[] calldata data, uint256[] calldata values)
external
requiresAuth
returns (bytes[] memory results)
{
uint256 targetsLength = targets.length;
results = new bytes[](targetsLength);
for (uint256 i; i < targetsLength; ++i) {
results[i] = targets[i].functionCallWithValue(data[i], values[i]);
}
}
//============================== ENTER ===============================
/**
* @notice Allows minter to mint shares, in exchange for assets.
* @dev If assetAmount is zero, no assets are transferred in.
* @dev Callable by MINTER_ROLE.
*/
function enter(address from, ERC20 asset, uint256 assetAmount, address to, uint256 shareAmount)
external
requiresAuth
{
// Transfer assets in
if (assetAmount > 0) asset.safeTransferFrom(from, address(this), assetAmount);
// Mint shares.
_mint(to, shareAmount);
emit Enter(from, address(asset), assetAmount, to, shareAmount);
}
//============================== EXIT ===============================
/**
* @notice Allows burner to burn shares, in exchange for assets.
* @dev If assetAmount is zero, no assets are transferred out.
* @dev Callable by BURNER_ROLE.
*/
function exit(address to, ERC20 asset, uint256 assetAmount, address from, uint256 shareAmount)
external
requiresAuth
{
// Burn shares.
_burn(from, shareAmount);
// Transfer assets out.
if (assetAmount > 0) asset.safeTransfer(to, assetAmount);
emit Exit(to, address(asset), assetAmount, from, shareAmount);
}
//============================== BEFORE TRANSFER HOOK ===============================
/**
* @notice Sets the share locker.
* @notice If set to zero address, the share locker logic is disabled.
* @dev Callable by OWNER_ROLE.
*/
function setBeforeTransferHook(address _hook) external requiresAuth {
hook = BeforeTransferHook(_hook);
}
/**
* @notice Call `beforeTransferHook` passing in `from` `to`, and `msg.sender`.
*/
function _callBeforeTransfer(address from, address to) internal view {
if (address(hook) != address(0)) hook.beforeTransfer(from, to, msg.sender);
}
function transfer(address to, uint256 amount) public override returns (bool) {
_callBeforeTransfer(msg.sender, to);
return super.transfer(to, amount);
}
function transferFrom(address from, address to, uint256 amount) public override returns (bool) {
_callBeforeTransfer(from, to);
return super.transferFrom(from, to, amount);
}
//============================== RECEIVE ===============================
receive() external payable {}
}// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
/// @notice Arithmetic library with operations for fixed-point numbers.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/FixedPointMathLib.sol)
/// @author Inspired by USM (https://github.com/usmfum/USM/blob/master/contracts/WadMath.sol)
library FixedPointMathLib {
/*//////////////////////////////////////////////////////////////
SIMPLIFIED FIXED POINT OPERATIONS
//////////////////////////////////////////////////////////////*/
uint256 internal constant MAX_UINT256 = 2**256 - 1;
uint256 internal constant WAD = 1e18; // The scalar of ETH and most ERC20s.
function mulWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivDown(x, y, WAD); // Equivalent to (x * y) / WAD rounded down.
}
function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivUp(x, y, WAD); // Equivalent to (x * y) / WAD rounded up.
}
function divWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivDown(x, WAD, y); // Equivalent to (x * WAD) / y rounded down.
}
function divWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivUp(x, WAD, y); // Equivalent to (x * WAD) / y rounded up.
}
/*//////////////////////////////////////////////////////////////
LOW LEVEL FIXED POINT OPERATIONS
//////////////////////////////////////////////////////////////*/
function mulDivDown(
uint256 x,
uint256 y,
uint256 denominator
) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))
if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {
revert(0, 0)
}
// Divide x * y by the denominator.
z := div(mul(x, y), denominator)
}
}
function mulDivUp(
uint256 x,
uint256 y,
uint256 denominator
) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))
if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {
revert(0, 0)
}
// If x * y modulo the denominator is strictly greater than 0,
// 1 is added to round up the division of x * y by the denominator.
z := add(gt(mod(mul(x, y), denominator), 0), div(mul(x, y), denominator))
}
}
function rpow(
uint256 x,
uint256 n,
uint256 scalar
) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
switch x
case 0 {
switch n
case 0 {
// 0 ** 0 = 1
z := scalar
}
default {
// 0 ** n = 0
z := 0
}
}
default {
switch mod(n, 2)
case 0 {
// If n is even, store scalar in z for now.
z := scalar
}
default {
// If n is odd, store x in z for now.
z := x
}
// Shifting right by 1 is like dividing by 2.
let half := shr(1, scalar)
for {
// Shift n right by 1 before looping to halve it.
n := shr(1, n)
} n {
// Shift n right by 1 each iteration to halve it.
n := shr(1, n)
} {
// Revert immediately if x ** 2 would overflow.
// Equivalent to iszero(eq(div(xx, x), x)) here.
if shr(128, x) {
revert(0, 0)
}
// Store x squared.
let xx := mul(x, x)
// Round to the nearest number.
let xxRound := add(xx, half)
// Revert if xx + half overflowed.
if lt(xxRound, xx) {
revert(0, 0)
}
// Set x to scaled xxRound.
x := div(xxRound, scalar)
// If n is even:
if mod(n, 2) {
// Compute z * x.
let zx := mul(z, x)
// If z * x overflowed:
if iszero(eq(div(zx, x), z)) {
// Revert if x is non-zero.
if iszero(iszero(x)) {
revert(0, 0)
}
}
// Round to the nearest number.
let zxRound := add(zx, half)
// Revert if zx + half overflowed.
if lt(zxRound, zx) {
revert(0, 0)
}
// Return properly scaled zxRound.
z := div(zxRound, scalar)
}
}
}
}
}
/*//////////////////////////////////////////////////////////////
GENERAL NUMBER UTILITIES
//////////////////////////////////////////////////////////////*/
function sqrt(uint256 x) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
let y := x // We start y at x, which will help us make our initial estimate.
z := 181 // The "correct" value is 1, but this saves a multiplication later.
// This segment is to get a reasonable initial estimate for the Babylonian method. With a bad
// start, the correct # of bits increases ~linearly each iteration instead of ~quadratically.
// We check y >= 2^(k + 8) but shift right by k bits
// each branch to ensure that if x >= 256, then y >= 256.
if iszero(lt(y, 0x10000000000000000000000000000000000)) {
y := shr(128, y)
z := shl(64, z)
}
if iszero(lt(y, 0x1000000000000000000)) {
y := shr(64, y)
z := shl(32, z)
}
if iszero(lt(y, 0x10000000000)) {
y := shr(32, y)
z := shl(16, z)
}
if iszero(lt(y, 0x1000000)) {
y := shr(16, y)
z := shl(8, z)
}
// Goal was to get z*z*y within a small factor of x. More iterations could
// get y in a tighter range. Currently, we will have y in [256, 256*2^16).
// We ensured y >= 256 so that the relative difference between y and y+1 is small.
// That's not possible if x < 256 but we can just verify those cases exhaustively.
// Now, z*z*y <= x < z*z*(y+1), and y <= 2^(16+8), and either y >= 256, or x < 256.
// Correctness can be checked exhaustively for x < 256, so we assume y >= 256.
// Then z*sqrt(y) is within sqrt(257)/sqrt(256) of sqrt(x), or about 20bps.
// For s in the range [1/256, 256], the estimate f(s) = (181/1024) * (s+1) is in the range
// (1/2.84 * sqrt(s), 2.84 * sqrt(s)), with largest error when s = 1 and when s = 256 or 1/256.
// Since y is in [256, 256*2^16), let a = y/65536, so that a is in [1/256, 256). Then we can estimate
// sqrt(y) using sqrt(65536) * 181/1024 * (a + 1) = 181/4 * (y + 65536)/65536 = 181 * (y + 65536)/2^18.
// There is no overflow risk here since y < 2^136 after the first branch above.
z := shr(18, mul(z, add(y, 65536))) // A mul() is saved from starting z at 181.
// Given the worst case multiplicative error of 2.84 above, 7 iterations should be enough.
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
// If x+1 is a perfect square, the Babylonian method cycles between
// floor(sqrt(x)) and ceil(sqrt(x)). This statement ensures we return floor.
// See: https://en.wikipedia.org/wiki/Integer_square_root#Using_only_integer_division
// Since the ceil is rare, we save gas on the assignment and repeat division in the rare case.
// If you don't care whether the floor or ceil square root is returned, you can remove this statement.
z := sub(z, lt(div(x, z), z))
}
}
function unsafeMod(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// Mod x by y. Note this will return
// 0 instead of reverting if y is zero.
z := mod(x, y)
}
}
function unsafeDiv(uint256 x, uint256 y) internal pure returns (uint256 r) {
/// @solidity memory-safe-assembly
assembly {
// Divide x by y. Note this will return
// 0 instead of reverting if y is zero.
r := div(x, y)
}
}
function unsafeDivUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// Add 1 to x * y if x % y > 0. Note this will
// return 0 instead of reverting if y is zero.
z := add(gt(mod(x, y), 0), div(x, y))
}
}
}{
"evmVersion": "shanghai",
"metadata": {
"appendCBOR": true,
"bytecodeHash": "ipfs",
"useLiteralContent": false
},
"optimizer": {
"enabled": true,
"runs": 200
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"abi"
]
}
},
"remappings": [
"@ccip/=lib/boring-vault/lib/ccip/",
"@devtools-oapp-evm/=lib/boring-vault/lib/OAppAuth/lib/devtools/packages/oapp-evm/contracts/oapp/",
"@ds-test/=lib/boring-vault/lib/forge-std/lib/ds-test/src/",
"@forge-std/=lib/boring-vault/lib/forge-std/src/",
"@layerzerolabs/lz-evm-messagelib-v2/=lib/boring-vault/lib/OAppAuth/node_modules/@layerzerolabs/lz-evm-messagelib-v2/",
"@layerzerolabs/lz-evm-protocol-v2/=lib/boring-vault/lib/OAppAuth/lib/LayerZero-V2/packages/layerzero-v2/evm/protocol/",
"@layerzerolabs/oapp-evm/=lib/boring-vault/lib/OAppAuth/lib/devtools/packages/oapp-evm/",
"@lz-oapp-evm/=lib/boring-vault/lib/OAppAuth/lib/LayerZero-V2/packages/layerzero-v2/evm/oapp/contracts/oapp/",
"@oapp-auth/=lib/boring-vault/lib/OAppAuth/src/",
"@openzeppelin/=lib/boring-vault/lib/openzeppelin-contracts/",
"@solmate/=lib/boring-vault/lib/solmate/src/",
"LayerZero-V2/=lib/boring-vault/lib/OAppAuth/lib/",
"OAppAuth/=lib/boring-vault/lib/OAppAuth/",
"boring-vault/=lib/boring-vault/",
"ccip/=lib/boring-vault/lib/ccip/contracts/",
"ds-test/=lib/solmate/lib/ds-test/src/",
"erc4626-tests/=lib/boring-vault/lib/openzeppelin-contracts/lib/erc4626-tests/",
"forge-std/=lib/forge-std/src/",
"halmos-cheatcodes/=lib/boring-vault/lib/OAppAuth/lib/openzeppelin-contracts/lib/halmos-cheatcodes/src/",
"openzeppelin-contracts/=lib/boring-vault/lib/openzeppelin-contracts/",
"solady/=lib/solady/src/",
"solidity-bytes-utils/=lib/boring-vault/lib/OAppAuth/node_modules/solidity-bytes-utils/",
"solmate/=lib/solmate/src/",
"yearn-vaults/=lib/yearn-vaults/contracts/",
"@sbu/=lib/boring-vault/lib/OAppAuth/lib/solidity-bytes-utils/",
"morpho-blue/=lib/boring-vault/lib/morpho-blue/"
],
"viaIR": false
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"address","name":"_auth","type":"address"},{"internalType":"address","name":"_queue","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[{"internalType":"address","name":"boringVault","type":"address"},{"internalType":"address","name":"teller","type":"address"}],"name":"BoringSolver___BoringVaultTellerMismatch","type":"error"},{"inputs":[],"name":"BoringSolver___FailedToSolve","type":"error"},{"inputs":[],"name":"BoringSolver___OnlyQueue","type":"error"},{"inputs":[],"name":"BoringSolver___OnlySelf","type":"error"},{"inputs":[],"name":"BoringSolver___WrongInitiator","type":"error"},{"inputs":[],"name":"FailedCall","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"contract Authority","name":"newAuthority","type":"address"}],"name":"AuthorityUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"inputs":[],"name":"authority","outputs":[{"internalType":"contract Authority","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint96","name":"nonce","type":"uint96"},{"internalType":"address","name":"user","type":"address"},{"internalType":"address","name":"assetOut","type":"address"},{"internalType":"uint128","name":"amountOfShares","type":"uint128"},{"internalType":"uint128","name":"amountOfAssets","type":"uint128"},{"internalType":"uint40","name":"creationTime","type":"uint40"},{"internalType":"uint24","name":"secondsToMaturity","type":"uint24"},{"internalType":"uint24","name":"secondsToDeadline","type":"uint24"}],"internalType":"struct BoringOnChainQueue.OnChainWithdraw","name":"request","type":"tuple"},{"internalType":"address","name":"fromTeller","type":"address"},{"internalType":"address","name":"toTeller","type":"address"},{"internalType":"address","name":"intermediateAsset","type":"address"}],"name":"boringRedeemMintSelfSolve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint96","name":"nonce","type":"uint96"},{"internalType":"address","name":"user","type":"address"},{"internalType":"address","name":"assetOut","type":"address"},{"internalType":"uint128","name":"amountOfShares","type":"uint128"},{"internalType":"uint128","name":"amountOfAssets","type":"uint128"},{"internalType":"uint40","name":"creationTime","type":"uint40"},{"internalType":"uint24","name":"secondsToMaturity","type":"uint24"},{"internalType":"uint24","name":"secondsToDeadline","type":"uint24"}],"internalType":"struct BoringOnChainQueue.OnChainWithdraw[]","name":"requests","type":"tuple[]"},{"internalType":"address","name":"fromTeller","type":"address"},{"internalType":"address","name":"toTeller","type":"address"},{"internalType":"address","name":"intermediateAsset","type":"address"}],"name":"boringRedeemMintSolve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint96","name":"nonce","type":"uint96"},{"internalType":"address","name":"user","type":"address"},{"internalType":"address","name":"assetOut","type":"address"},{"internalType":"uint128","name":"amountOfShares","type":"uint128"},{"internalType":"uint128","name":"amountOfAssets","type":"uint128"},{"internalType":"uint40","name":"creationTime","type":"uint40"},{"internalType":"uint24","name":"secondsToMaturity","type":"uint24"},{"internalType":"uint24","name":"secondsToDeadline","type":"uint24"}],"internalType":"struct BoringOnChainQueue.OnChainWithdraw","name":"request","type":"tuple"},{"internalType":"address","name":"teller","type":"address"}],"name":"boringRedeemSelfSolve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint96","name":"nonce","type":"uint96"},{"internalType":"address","name":"user","type":"address"},{"internalType":"address","name":"assetOut","type":"address"},{"internalType":"uint128","name":"amountOfShares","type":"uint128"},{"internalType":"uint128","name":"amountOfAssets","type":"uint128"},{"internalType":"uint40","name":"creationTime","type":"uint40"},{"internalType":"uint24","name":"secondsToMaturity","type":"uint24"},{"internalType":"uint24","name":"secondsToDeadline","type":"uint24"}],"internalType":"struct BoringOnChainQueue.OnChainWithdraw[]","name":"requests","type":"tuple[]"},{"internalType":"address","name":"teller","type":"address"}],"name":"boringRedeemSolve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"initiator","type":"address"},{"internalType":"address","name":"boringVault","type":"address"},{"internalType":"address","name":"solveAsset","type":"address"},{"internalType":"uint256","name":"totalShares","type":"uint256"},{"internalType":"uint256","name":"requiredAssets","type":"uint256"},{"internalType":"bytes","name":"solveData","type":"bytes"}],"name":"boringSolve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes[]","name":"data","type":"bytes[]"}],"name":"multicall","outputs":[{"internalType":"bytes[]","name":"results","type":"bytes[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract ERC20","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"rescueTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract Authority","name":"newAuthority","type":"address"}],"name":"setAuthority","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
60a060405234801562000010575f80fd5b5060405162002018380380620020188339810160408190526200003391620000f5565b5f80546001600160a01b03199081166001600160a01b0386811691821784556001805490931690861617909155604051859285929133917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a36040516001600160a01b0382169033907fa3396fd7f6e0a21b50e5089d2da70d5ac0a3bbbd1f617a93f134b76389980198905f90a350506001600160a01b0316608052506200013c9050565b80516001600160a01b0381168114620000f0575f80fd5b919050565b5f805f6060848603121562000108575f80fd5b6200011384620000d9565b92506200012360208501620000d9565b91506200013360408501620000d9565b90509250925092565b608051611e92620001865f395f81816102a501528181610497015281816106e9015281816108ba015281816109e601528181610cd701528181611008015261116e0152611e925ff3fe608060405234801561000f575f80fd5b50600436106100a6575f3560e01c80638f3866081161006e5780638f38660814610127578063ac9650d81461013a578063b7532db21461015a578063bf7e214f1461016d578063f2fde38b14610180578063ff011b6214610193575f80fd5b806357376198146100aa57806367aa0416146100bf57806372faf4a4146100d25780637a9e5e4b146100e55780638da5cb5b146100f8575b5f80fd5b6100bd6100b83660046113f4565b6101a6565b005b6100bd6100cd36600461142e565b610269565b6100bd6100e03660046114fa565b610390565b6100bd6100f3366004611532565b6104f9565b5f5461010a906001600160a01b031681565b6040516001600160a01b0390911681526020015b60405180910390f35b6100bd61013536600461154d565b6105dd565b61014d6101483660046115a9565b610757565b60405161011e9190611665565b6100bd61016836600461170d565b610849565b60015461010a906001600160a01b031681565b6100bd61018e366004611532565b6108f5565b6100bd6101a1366004611760565b610970565b6101bb335f356001600160e01b031916610a21565b6101e05760405162461bcd60e51b81526004016101d7906117d8565b60405180910390fd5b5f198103610251576040516370a0823160e01b81523060048201526001600160a01b038316906370a0823190602401602060405180830381865afa15801561022a573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061024e91906117fe565b90505b6102656001600160a01b0383163383610ac7565b5050565b61027e335f356001600160e01b031916610a21565b61029a5760405162461bcd60e51b81526004016101d7906117d8565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146102e3576040516337aab0fd60e11b815260040160405180910390fd5b6001600160a01b038716301461030c5760405163702093cb60e11b815260040160405180910390fd5b5f61031982840184611823565b90505f81600181111561032e5761032e61183c565b0361034657610341838389898989610b56565b610386565b600181600181111561035a5761035a61183c565b0361036d57610341838389898989610d5e565b6040516336ad3b5560e21b815260040160405180910390fd5b5050505050505050565b6103a5335f356001600160e01b031916610a21565b6103c15760405162461bcd60e51b81526004016101d7906117d8565b336103d26040840160208501611532565b6001600160a01b0316146103f9576040516303279bc360e41b815260040160405180910390fd5b6040805160018082528183019092525f91816020015b61041761139d565b81526020019060019003908161040f57905050905061043b368490038401846118b6565b815f8151811061044d5761044d611985565b60200260200101819052505f8033845f60405160200161047094939291906119b9565b60408051601f19818403018152908290526310498e3760e21b825291506001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063412638dc906104d0908590859030906004016119f0565b5f604051808303815f87803b1580156104e7575f80fd5b505af1158015610386573d5f803e3d5ffd5b5f546001600160a01b031633148061058a575060015460405163b700961360e01b81526001600160a01b039091169063b70096139061054b90339030906001600160e01b03195f351690600401611ad2565b602060405180830381865afa158015610566573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061058a9190611b0c565b610592575f80fd5b600180546001600160a01b0319166001600160a01b03831690811790915560405133907fa3396fd7f6e0a21b50e5089d2da70d5ac0a3bbbd1f617a93f134b76389980198905f90a350565b6105f2335f356001600160e01b031916610a21565b61060e5760405162461bcd60e51b81526004016101d7906117d8565b3361061f6040860160208701611532565b6001600160a01b031614610646576040516303279bc360e41b815260040160405180910390fd5b6040805160018082528183019092525f91816020015b61066461139d565b81526020019060019003908161065c579050509050610688368690038601866118b6565b815f8151811061069a5761069a611985565b60200260200101819052505f6001338686865f6040516020016106c296959493929190611b27565b60408051601f19818403018152908290526310498e3760e21b825291506001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063412638dc90610722908590859030906004016119f0565b5f604051808303815f87803b158015610739575f80fd5b505af115801561074b573d5f803e3d5ffd5b50505050505050505050565b604080515f8152602081019091526060908267ffffffffffffffff81111561078157610781611850565b6040519080825280602002602001820160405280156107b457816020015b606081526020019060019003908161079f5790505b5091505f5b8381101561084057610810308686848181106107d7576107d7611985565b90506020028101906107e99190611b94565b856040516020016107fc93929190611bd7565b6040516020818303038152906040526111f6565b83828151811061082257610822611985565b6020026020010181905250808061083890611bfc565b9150506107b9565b50505b92915050565b61085e335f356001600160e01b031916610a21565b61087a5760405162461bcd60e51b81526004016101d7906117d8565b5f803383600160405160200161089394939291906119b9565b60408051601f19818403018152908290526310498e3760e21b825291506001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063412638dc906104d0908790879086903090600401611c14565b61090a335f356001600160e01b031916610a21565b6109265760405162461bcd60e51b81526004016101d7906117d8565b5f80546001600160a01b0319166001600160a01b0383169081178255604051909133917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a350565b610985335f356001600160e01b031916610a21565b6109a15760405162461bcd60e51b81526004016101d7906117d8565b5f60013385858560016040516020016109bf96959493929190611b27565b60408051601f19818403018152908290526310498e3760e21b825291506001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063412638dc90610722908990899086903090600401611c14565b6001545f906001600160a01b03168015801590610aa8575060405163b700961360e01b81526001600160a01b0382169063b700961390610a6990879030908890600401611ad2565b602060405180830381865afa158015610a84573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610aa89190611b0c565b80610abf57505f546001600160a01b038581169116145b949350505050565b5f60405163a9059cbb60e01b81526001600160a01b038416600482015282602482015260205f6044835f895af191505080601f3d1160015f511416151615610b115750823b153d17155b80610b505760405162461bcd60e51b815260206004820152600f60248201526e1514905394d1915497d19052531151608a1b60448201526064016101d7565b50505050565b5f8080610b65888a018a611d33565b93509350935050816001600160a01b031663fbfa77cf6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610ba8573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610bcc9190611d7f565b6001600160a01b0316876001600160a01b031614610c1057604051631469fe1360e21b81526001600160a01b038089166004830152831660248201526044016101d7565b604051633e64ce9960e01b815286905f906001600160a01b03851690633e64ce9990610c469085908b908b903090600401611d9a565b6020604051808303815f875af1158015610c62573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c8691906117fe565b90508215610cb257610cad85610c9c8884611b81565b6001600160a01b0385169190610ac7565b610cc0565b610cc089610c9c8884611b81565b60405163095ea7b360e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811660048301526024820188905283169063095ea7b3906044016020604051808303815f875af1158015610d2c573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610d509190611b0c565b505050505050505050505050565b5f80808080610d6f8a8c018c611dc5565b9550955095509550955050836001600160a01b031663fbfa77cf6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610db6573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610dda9190611d7f565b6001600160a01b0316896001600160a01b031614610e1e57604051631469fe1360e21b81526001600160a01b03808b166004830152851660248201526044016101d7565b826001600160a01b031663fbfa77cf6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610e5a573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610e7e9190611d7f565b6001600160a01b0316886001600160a01b031614610ec257604051631469fe1360e21b81526001600160a01b03808a166004830152841660248201526044016101d7565b604051633e64ce9960e01b81525f906001600160a01b03861690633e64ce9990610ef69086908c9086903090600401611d9a565b6020604051808303815f875af1158015610f12573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610f3691906117fe565b90505f61108e856001600160a01b0316634fb3ccc56040518163ffffffff1660e01b8152600401602060405180830381865afa158015610f78573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610f9c9190611d7f565b604051634104b9ed60e11b81526001600160a01b038781166004830152919091169063820973da90602401602060405180830381865afa158015610fe2573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061100691906117fe565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663b7d122b56040518163ffffffff1660e01b8152600401602060405180830381865afa158015611062573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061108691906117fe565b8a9190611268565b905061109a8183611b81565b91506110b06001600160a01b0385168b8361128b565b6040516304eaba2160e51b81526001600160a01b03861690639d574420906110e290879085908d903090600401611d9a565b6020604051808303815f875af11580156110fe573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061112291906117fe565b505081156111435761113e6001600160a01b0384168783610ac7565b611157565b6111576001600160a01b0384168b83610ac7565b60405163095ea7b360e01b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000081166004830152602482018990528a169063095ea7b3906044016020604051808303815f875af11580156111c3573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906111e79190611b0c565b50505050505050505050505050565b60605f80846001600160a01b0316846040516112129190611e41565b5f60405180830381855af49150503d805f811461124a576040519150601f19603f3d011682016040523d82523d5f602084013e61124f565b606091505b509150915061125f858383611313565b95945050505050565b5f825f19048411830215820261127c575f80fd5b50910281810615159190040190565b5f60405163095ea7b360e01b81526001600160a01b038416600482015282602482015260205f6044835f895af191505080601f3d1160015f5114161516156112d55750823b153d17155b80610b505760405162461bcd60e51b815260206004820152600e60248201526d1054141493d59157d1905253115160921b60448201526064016101d7565b6060826113285761132382611372565b61136b565b815115801561133f57506001600160a01b0384163b155b1561136857604051639996b31560e01b81526001600160a01b03851660048201526024016101d7565b50805b9392505050565b80511561138157805160208201fd5b60405163d6bda27560e01b815260040160405180910390fd5b50565b60408051610100810182525f80825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e081019190915290565b6001600160a01b038116811461139a575f80fd5b5f8060408385031215611405575f80fd5b8235611410816113e0565b946020939093013593505050565b8035611429816113e0565b919050565b5f805f805f805f60c0888a031215611444575f80fd5b873561144f816113e0565b9650602088013561145f816113e0565b9550604088013561146f816113e0565b9450606088013593506080880135925060a088013567ffffffffffffffff80821115611499575f80fd5b818a0191508a601f8301126114ac575f80fd5b8135818111156114ba575f80fd5b8b60208285010111156114cb575f80fd5b60208301945080935050505092959891949750929550565b5f61010082840312156114f4575f80fd5b50919050565b5f80610120838503121561150c575f80fd5b61151684846114e3565b9150610100830135611527816113e0565b809150509250929050565b5f60208284031215611542575f80fd5b813561136b816113e0565b5f805f806101608587031215611561575f80fd5b61156b86866114e3565b935061010085013561157c816113e0565b925061012085013561158d816113e0565b915061014085013561159e816113e0565b939692955090935050565b5f80602083850312156115ba575f80fd5b823567ffffffffffffffff808211156115d1575f80fd5b818501915085601f8301126115e4575f80fd5b8135818111156115f2575f80fd5b8660208260051b8501011115611606575f80fd5b60209290920196919550909350505050565b5f5b8381101561163257818101518382015260200161161a565b50505f910152565b5f8151808452611651816020860160208601611618565b601f01601f19169290920160200192915050565b5f602080830181845280855180835260408601915060408160051b87010192508387015f5b828110156116b857603f198886030184526116a685835161163a565b9450928501929085019060010161168a565b5092979650505050505050565b5f8083601f8401126116d5575f80fd5b50813567ffffffffffffffff8111156116ec575f80fd5b6020830191508360208260081b8501011115611706575f80fd5b9250929050565b5f805f6040848603121561171f575f80fd5b833567ffffffffffffffff811115611735575f80fd5b611741868287016116c5565b9094509250506020840135611755816113e0565b809150509250925092565b5f805f805f60808688031215611774575f80fd5b853567ffffffffffffffff81111561178a575f80fd5b611796888289016116c5565b90965094505060208601356117aa816113e0565b925060408601356117ba816113e0565b915060608601356117ca816113e0565b809150509295509295909350565b6020808252600c908201526b15539055551213d49256915160a21b604082015260600190565b5f6020828403121561180e575f80fd5b5051919050565b803560028110611429575f80fd5b5f60208284031215611833575f80fd5b61136b82611815565b634e487b7160e01b5f52602160045260245ffd5b634e487b7160e01b5f52604160045260245ffd5b80356001600160601b0381168114611429575f80fd5b80356001600160801b0381168114611429575f80fd5b803564ffffffffff81168114611429575f80fd5b803562ffffff81168114611429575f80fd5b5f6101008083850312156118c8575f80fd5b6040519081019067ffffffffffffffff821181831017156118f757634e487b7160e01b5f52604160045260245ffd5b8160405261190484611864565b81526119126020850161141e565b60208201526119236040850161141e565b60408201526119346060850161187a565b60608201526119456080850161187a565b608082015261195660a08501611890565b60a082015261196760c085016118a4565b60c082015261197860e085016118a4565b60e0820152949350505050565b634e487b7160e01b5f52603260045260245ffd5b600281106119b557634e487b7160e01b5f52602160045260245ffd5b9052565b608081016119c78287611999565b6001600160a01b0394851660208301529290931660408401521515606090920191909152919050565b606080825284518282018190525f9190608090818501906020808a01865b83811015611aa457815180516001600160601b03168652838101516001600160a01b039081168588015260408083015190911690870152878101516001600160801b039081168988015287820151168787015260a08082015164ffffffffff169087015260c08082015162ffffff9081169188019190915260e09182015116908601526101009094019390820190600101611a0e565b50508683039087015250611ab8818861163a565b9350505050610abf60408301846001600160a01b03169052565b6001600160a01b0393841681529190921660208201526001600160e01b0319909116604082015260600190565b801515811461139a575f80fd5b5f60208284031215611b1c575f80fd5b815161136b81611aff565b60c08101611b358289611999565b6001600160a01b039687166020830152948616604082015292851660608401529316608082015291151560a090920191909152919050565b634e487b7160e01b5f52601160045260245ffd5b8181038181111561084357610843611b6d565b5f808335601e19843603018112611ba9575f80fd5b83018035915067ffffffffffffffff821115611bc3575f80fd5b602001915036819003821315611706575f80fd5b828482375f8382015f81528351611bf2818360208801611618565b0195945050505050565b5f60018201611c0d57611c0d611b6d565b5060010190565b60608082528181018590525f90608080840188845b89811015611d05576001600160601b03611c4283611864565b168352602080830135611c54816113e0565b6001600160a01b031690840152604082810135611c70816113e0565b6001600160a01b031690840152611c8882860161187a565b6001600160801b031685840152611ca082850161187a565b6001600160801b03168484015260a0611cba838201611890565b64ffffffffff169084015260c0611cd28382016118a4565b62ffffff169084015260e0611ce88382016118a4565b62ffffff1690840152610100928301929190910190600101611c29565b50508481036020860152611d19818861163a565b935050505061125f60408301846001600160a01b03169052565b5f805f8060808587031215611d46575f80fd5b611d4f85611815565b93506020850135611d5f816113e0565b92506040850135611d6f816113e0565b9150606085013561159e81611aff565b5f60208284031215611d8f575f80fd5b815161136b816113e0565b6001600160a01b03948516815260208101939093526040830191909152909116606082015260800190565b5f805f805f8060c08789031215611dda575f80fd5b611de387611815565b95506020870135611df3816113e0565b94506040870135611e03816113e0565b93506060870135611e13816113e0565b92506080870135611e23816113e0565b915060a0870135611e3381611aff565b809150509295509295509295565b5f8251611e52818460208701611618565b919091019291505056fea26469706673582212205f6034f2c1792be5f3ab06a566c42a514f4b675002e83b42654158df0f8f1f8064736f6c63430008150033000000000000000000000000771263e3bc6acda5ae388a3f8a0c2dd7a17275fc0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f632c10b19f2a0451cd4a653fc9ca0c15ea1040b
Deployed Bytecode
0x608060405234801561000f575f80fd5b50600436106100a6575f3560e01c80638f3866081161006e5780638f38660814610127578063ac9650d81461013a578063b7532db21461015a578063bf7e214f1461016d578063f2fde38b14610180578063ff011b6214610193575f80fd5b806357376198146100aa57806367aa0416146100bf57806372faf4a4146100d25780637a9e5e4b146100e55780638da5cb5b146100f8575b5f80fd5b6100bd6100b83660046113f4565b6101a6565b005b6100bd6100cd36600461142e565b610269565b6100bd6100e03660046114fa565b610390565b6100bd6100f3366004611532565b6104f9565b5f5461010a906001600160a01b031681565b6040516001600160a01b0390911681526020015b60405180910390f35b6100bd61013536600461154d565b6105dd565b61014d6101483660046115a9565b610757565b60405161011e9190611665565b6100bd61016836600461170d565b610849565b60015461010a906001600160a01b031681565b6100bd61018e366004611532565b6108f5565b6100bd6101a1366004611760565b610970565b6101bb335f356001600160e01b031916610a21565b6101e05760405162461bcd60e51b81526004016101d7906117d8565b60405180910390fd5b5f198103610251576040516370a0823160e01b81523060048201526001600160a01b038316906370a0823190602401602060405180830381865afa15801561022a573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061024e91906117fe565b90505b6102656001600160a01b0383163383610ac7565b5050565b61027e335f356001600160e01b031916610a21565b61029a5760405162461bcd60e51b81526004016101d7906117d8565b336001600160a01b037f000000000000000000000000f632c10b19f2a0451cd4a653fc9ca0c15ea1040b16146102e3576040516337aab0fd60e11b815260040160405180910390fd5b6001600160a01b038716301461030c5760405163702093cb60e11b815260040160405180910390fd5b5f61031982840184611823565b90505f81600181111561032e5761032e61183c565b0361034657610341838389898989610b56565b610386565b600181600181111561035a5761035a61183c565b0361036d57610341838389898989610d5e565b6040516336ad3b5560e21b815260040160405180910390fd5b5050505050505050565b6103a5335f356001600160e01b031916610a21565b6103c15760405162461bcd60e51b81526004016101d7906117d8565b336103d26040840160208501611532565b6001600160a01b0316146103f9576040516303279bc360e41b815260040160405180910390fd5b6040805160018082528183019092525f91816020015b61041761139d565b81526020019060019003908161040f57905050905061043b368490038401846118b6565b815f8151811061044d5761044d611985565b60200260200101819052505f8033845f60405160200161047094939291906119b9565b60408051601f19818403018152908290526310498e3760e21b825291506001600160a01b037f000000000000000000000000f632c10b19f2a0451cd4a653fc9ca0c15ea1040b169063412638dc906104d0908590859030906004016119f0565b5f604051808303815f87803b1580156104e7575f80fd5b505af1158015610386573d5f803e3d5ffd5b5f546001600160a01b031633148061058a575060015460405163b700961360e01b81526001600160a01b039091169063b70096139061054b90339030906001600160e01b03195f351690600401611ad2565b602060405180830381865afa158015610566573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061058a9190611b0c565b610592575f80fd5b600180546001600160a01b0319166001600160a01b03831690811790915560405133907fa3396fd7f6e0a21b50e5089d2da70d5ac0a3bbbd1f617a93f134b76389980198905f90a350565b6105f2335f356001600160e01b031916610a21565b61060e5760405162461bcd60e51b81526004016101d7906117d8565b3361061f6040860160208701611532565b6001600160a01b031614610646576040516303279bc360e41b815260040160405180910390fd5b6040805160018082528183019092525f91816020015b61066461139d565b81526020019060019003908161065c579050509050610688368690038601866118b6565b815f8151811061069a5761069a611985565b60200260200101819052505f6001338686865f6040516020016106c296959493929190611b27565b60408051601f19818403018152908290526310498e3760e21b825291506001600160a01b037f000000000000000000000000f632c10b19f2a0451cd4a653fc9ca0c15ea1040b169063412638dc90610722908590859030906004016119f0565b5f604051808303815f87803b158015610739575f80fd5b505af115801561074b573d5f803e3d5ffd5b50505050505050505050565b604080515f8152602081019091526060908267ffffffffffffffff81111561078157610781611850565b6040519080825280602002602001820160405280156107b457816020015b606081526020019060019003908161079f5790505b5091505f5b8381101561084057610810308686848181106107d7576107d7611985565b90506020028101906107e99190611b94565b856040516020016107fc93929190611bd7565b6040516020818303038152906040526111f6565b83828151811061082257610822611985565b6020026020010181905250808061083890611bfc565b9150506107b9565b50505b92915050565b61085e335f356001600160e01b031916610a21565b61087a5760405162461bcd60e51b81526004016101d7906117d8565b5f803383600160405160200161089394939291906119b9565b60408051601f19818403018152908290526310498e3760e21b825291506001600160a01b037f000000000000000000000000f632c10b19f2a0451cd4a653fc9ca0c15ea1040b169063412638dc906104d0908790879086903090600401611c14565b61090a335f356001600160e01b031916610a21565b6109265760405162461bcd60e51b81526004016101d7906117d8565b5f80546001600160a01b0319166001600160a01b0383169081178255604051909133917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a350565b610985335f356001600160e01b031916610a21565b6109a15760405162461bcd60e51b81526004016101d7906117d8565b5f60013385858560016040516020016109bf96959493929190611b27565b60408051601f19818403018152908290526310498e3760e21b825291506001600160a01b037f000000000000000000000000f632c10b19f2a0451cd4a653fc9ca0c15ea1040b169063412638dc90610722908990899086903090600401611c14565b6001545f906001600160a01b03168015801590610aa8575060405163b700961360e01b81526001600160a01b0382169063b700961390610a6990879030908890600401611ad2565b602060405180830381865afa158015610a84573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610aa89190611b0c565b80610abf57505f546001600160a01b038581169116145b949350505050565b5f60405163a9059cbb60e01b81526001600160a01b038416600482015282602482015260205f6044835f895af191505080601f3d1160015f511416151615610b115750823b153d17155b80610b505760405162461bcd60e51b815260206004820152600f60248201526e1514905394d1915497d19052531151608a1b60448201526064016101d7565b50505050565b5f8080610b65888a018a611d33565b93509350935050816001600160a01b031663fbfa77cf6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610ba8573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610bcc9190611d7f565b6001600160a01b0316876001600160a01b031614610c1057604051631469fe1360e21b81526001600160a01b038089166004830152831660248201526044016101d7565b604051633e64ce9960e01b815286905f906001600160a01b03851690633e64ce9990610c469085908b908b903090600401611d9a565b6020604051808303815f875af1158015610c62573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c8691906117fe565b90508215610cb257610cad85610c9c8884611b81565b6001600160a01b0385169190610ac7565b610cc0565b610cc089610c9c8884611b81565b60405163095ea7b360e01b81526001600160a01b037f000000000000000000000000f632c10b19f2a0451cd4a653fc9ca0c15ea1040b811660048301526024820188905283169063095ea7b3906044016020604051808303815f875af1158015610d2c573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610d509190611b0c565b505050505050505050505050565b5f80808080610d6f8a8c018c611dc5565b9550955095509550955050836001600160a01b031663fbfa77cf6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610db6573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610dda9190611d7f565b6001600160a01b0316896001600160a01b031614610e1e57604051631469fe1360e21b81526001600160a01b03808b166004830152851660248201526044016101d7565b826001600160a01b031663fbfa77cf6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610e5a573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610e7e9190611d7f565b6001600160a01b0316886001600160a01b031614610ec257604051631469fe1360e21b81526001600160a01b03808a166004830152841660248201526044016101d7565b604051633e64ce9960e01b81525f906001600160a01b03861690633e64ce9990610ef69086908c9086903090600401611d9a565b6020604051808303815f875af1158015610f12573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610f3691906117fe565b90505f61108e856001600160a01b0316634fb3ccc56040518163ffffffff1660e01b8152600401602060405180830381865afa158015610f78573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610f9c9190611d7f565b604051634104b9ed60e11b81526001600160a01b038781166004830152919091169063820973da90602401602060405180830381865afa158015610fe2573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061100691906117fe565b7f000000000000000000000000f632c10b19f2a0451cd4a653fc9ca0c15ea1040b6001600160a01b031663b7d122b56040518163ffffffff1660e01b8152600401602060405180830381865afa158015611062573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061108691906117fe565b8a9190611268565b905061109a8183611b81565b91506110b06001600160a01b0385168b8361128b565b6040516304eaba2160e51b81526001600160a01b03861690639d574420906110e290879085908d903090600401611d9a565b6020604051808303815f875af11580156110fe573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061112291906117fe565b505081156111435761113e6001600160a01b0384168783610ac7565b611157565b6111576001600160a01b0384168b83610ac7565b60405163095ea7b360e01b81526001600160a01b037f000000000000000000000000f632c10b19f2a0451cd4a653fc9ca0c15ea1040b81166004830152602482018990528a169063095ea7b3906044016020604051808303815f875af11580156111c3573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906111e79190611b0c565b50505050505050505050505050565b60605f80846001600160a01b0316846040516112129190611e41565b5f60405180830381855af49150503d805f811461124a576040519150601f19603f3d011682016040523d82523d5f602084013e61124f565b606091505b509150915061125f858383611313565b95945050505050565b5f825f19048411830215820261127c575f80fd5b50910281810615159190040190565b5f60405163095ea7b360e01b81526001600160a01b038416600482015282602482015260205f6044835f895af191505080601f3d1160015f5114161516156112d55750823b153d17155b80610b505760405162461bcd60e51b815260206004820152600e60248201526d1054141493d59157d1905253115160921b60448201526064016101d7565b6060826113285761132382611372565b61136b565b815115801561133f57506001600160a01b0384163b155b1561136857604051639996b31560e01b81526001600160a01b03851660048201526024016101d7565b50805b9392505050565b80511561138157805160208201fd5b60405163d6bda27560e01b815260040160405180910390fd5b50565b60408051610100810182525f80825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e081019190915290565b6001600160a01b038116811461139a575f80fd5b5f8060408385031215611405575f80fd5b8235611410816113e0565b946020939093013593505050565b8035611429816113e0565b919050565b5f805f805f805f60c0888a031215611444575f80fd5b873561144f816113e0565b9650602088013561145f816113e0565b9550604088013561146f816113e0565b9450606088013593506080880135925060a088013567ffffffffffffffff80821115611499575f80fd5b818a0191508a601f8301126114ac575f80fd5b8135818111156114ba575f80fd5b8b60208285010111156114cb575f80fd5b60208301945080935050505092959891949750929550565b5f61010082840312156114f4575f80fd5b50919050565b5f80610120838503121561150c575f80fd5b61151684846114e3565b9150610100830135611527816113e0565b809150509250929050565b5f60208284031215611542575f80fd5b813561136b816113e0565b5f805f806101608587031215611561575f80fd5b61156b86866114e3565b935061010085013561157c816113e0565b925061012085013561158d816113e0565b915061014085013561159e816113e0565b939692955090935050565b5f80602083850312156115ba575f80fd5b823567ffffffffffffffff808211156115d1575f80fd5b818501915085601f8301126115e4575f80fd5b8135818111156115f2575f80fd5b8660208260051b8501011115611606575f80fd5b60209290920196919550909350505050565b5f5b8381101561163257818101518382015260200161161a565b50505f910152565b5f8151808452611651816020860160208601611618565b601f01601f19169290920160200192915050565b5f602080830181845280855180835260408601915060408160051b87010192508387015f5b828110156116b857603f198886030184526116a685835161163a565b9450928501929085019060010161168a565b5092979650505050505050565b5f8083601f8401126116d5575f80fd5b50813567ffffffffffffffff8111156116ec575f80fd5b6020830191508360208260081b8501011115611706575f80fd5b9250929050565b5f805f6040848603121561171f575f80fd5b833567ffffffffffffffff811115611735575f80fd5b611741868287016116c5565b9094509250506020840135611755816113e0565b809150509250925092565b5f805f805f60808688031215611774575f80fd5b853567ffffffffffffffff81111561178a575f80fd5b611796888289016116c5565b90965094505060208601356117aa816113e0565b925060408601356117ba816113e0565b915060608601356117ca816113e0565b809150509295509295909350565b6020808252600c908201526b15539055551213d49256915160a21b604082015260600190565b5f6020828403121561180e575f80fd5b5051919050565b803560028110611429575f80fd5b5f60208284031215611833575f80fd5b61136b82611815565b634e487b7160e01b5f52602160045260245ffd5b634e487b7160e01b5f52604160045260245ffd5b80356001600160601b0381168114611429575f80fd5b80356001600160801b0381168114611429575f80fd5b803564ffffffffff81168114611429575f80fd5b803562ffffff81168114611429575f80fd5b5f6101008083850312156118c8575f80fd5b6040519081019067ffffffffffffffff821181831017156118f757634e487b7160e01b5f52604160045260245ffd5b8160405261190484611864565b81526119126020850161141e565b60208201526119236040850161141e565b60408201526119346060850161187a565b60608201526119456080850161187a565b608082015261195660a08501611890565b60a082015261196760c085016118a4565b60c082015261197860e085016118a4565b60e0820152949350505050565b634e487b7160e01b5f52603260045260245ffd5b600281106119b557634e487b7160e01b5f52602160045260245ffd5b9052565b608081016119c78287611999565b6001600160a01b0394851660208301529290931660408401521515606090920191909152919050565b606080825284518282018190525f9190608090818501906020808a01865b83811015611aa457815180516001600160601b03168652838101516001600160a01b039081168588015260408083015190911690870152878101516001600160801b039081168988015287820151168787015260a08082015164ffffffffff169087015260c08082015162ffffff9081169188019190915260e09182015116908601526101009094019390820190600101611a0e565b50508683039087015250611ab8818861163a565b9350505050610abf60408301846001600160a01b03169052565b6001600160a01b0393841681529190921660208201526001600160e01b0319909116604082015260600190565b801515811461139a575f80fd5b5f60208284031215611b1c575f80fd5b815161136b81611aff565b60c08101611b358289611999565b6001600160a01b039687166020830152948616604082015292851660608401529316608082015291151560a090920191909152919050565b634e487b7160e01b5f52601160045260245ffd5b8181038181111561084357610843611b6d565b5f808335601e19843603018112611ba9575f80fd5b83018035915067ffffffffffffffff821115611bc3575f80fd5b602001915036819003821315611706575f80fd5b828482375f8382015f81528351611bf2818360208801611618565b0195945050505050565b5f60018201611c0d57611c0d611b6d565b5060010190565b60608082528181018590525f90608080840188845b89811015611d05576001600160601b03611c4283611864565b168352602080830135611c54816113e0565b6001600160a01b031690840152604082810135611c70816113e0565b6001600160a01b031690840152611c8882860161187a565b6001600160801b031685840152611ca082850161187a565b6001600160801b03168484015260a0611cba838201611890565b64ffffffffff169084015260c0611cd28382016118a4565b62ffffff169084015260e0611ce88382016118a4565b62ffffff1690840152610100928301929190910190600101611c29565b50508481036020860152611d19818861163a565b935050505061125f60408301846001600160a01b03169052565b5f805f8060808587031215611d46575f80fd5b611d4f85611815565b93506020850135611d5f816113e0565b92506040850135611d6f816113e0565b9150606085013561159e81611aff565b5f60208284031215611d8f575f80fd5b815161136b816113e0565b6001600160a01b03948516815260208101939093526040830191909152909116606082015260800190565b5f805f805f8060c08789031215611dda575f80fd5b611de387611815565b95506020870135611df3816113e0565b94506040870135611e03816113e0565b93506060870135611e13816113e0565b92506080870135611e23816113e0565b915060a0870135611e3381611aff565b809150509295509295509295565b5f8251611e52818460208701611618565b919091019291505056fea26469706673582212205f6034f2c1792be5f3ab06a566c42a514f4b675002e83b42654158df0f8f1f8064736f6c63430008150033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000771263e3bc6acda5ae388a3f8a0c2dd7a17275fc0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f632c10b19f2a0451cd4a653fc9ca0c15ea1040b
-----Decoded View---------------
Arg [0] : _owner (address): 0x771263e3Bc6aCDa5aE388A3F8A0c2dd7A17275FC
Arg [1] : _auth (address): 0x0000000000000000000000000000000000000000
Arg [2] : _queue (address): 0xF632c10b19f2a0451cD4A653fC9ca0c15eA1040b
-----Encoded View---------------
3 Constructor Arguments found :
Arg [0] : 000000000000000000000000771263e3bc6acda5ae388a3f8a0c2dd7a17275fc
Arg [1] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [2] : 000000000000000000000000f632c10b19f2a0451cd4a653fc9ca0c15ea1040b
Deployed Bytecode Sourcemap
584:10022:27:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2164:210;;;;;;:::i;:::-;;:::i;:::-;;5377:998;;;;;;:::i;:::-;;:::i;3758:535::-;;;;;;:::i;:::-;;:::i;1523:434:18:-;;;;;;:::i;:::-;;:::i;562:20::-;;;;;-1:-1:-1;;;;;562:20:18;;;;;;-1:-1:-1;;;;;2939:32:33;;;2921:51;;2909:2;2894:18;562:20:18;;;;;;;;4543:656:27;;;;;;:::i;:::-;;:::i;1224:484:9:-;;;;;;:::i;:::-;;:::i;:::-;;;;;;;:::i;2563:318:27:-;;;;;;:::i;:::-;;:::i;589:26:18:-;;;;;-1:-1:-1;;;;;589:26:18;;;1963:164;;;;;;:::i;:::-;;:::i;3119:439:27:-;;;;;;:::i;:::-;;:::i;2164:210::-;902:33:18;915:10;927:7;;-1:-1:-1;;;;;;927:7:18;902:12;:33::i;:::-;894:58;;;;-1:-1:-1;;;894:58:18;;;;;;;:::i;:::-;;;;;;;;;-1:-1:-1;;2251:6:27::1;:27:::0;2247:72:::1;;2289:30;::::0;-1:-1:-1;;;2289:30:27;;2313:4:::1;2289:30;::::0;::::1;2921:51:33::0;-1:-1:-1;;;;;2289:15:27;::::1;::::0;::::1;::::0;2894:18:33;;2289:30:27::1;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;2280:39;;2247:72;2329:38;-1:-1:-1::0;;;;;2329:18:27;::::1;2348:10;2360:6:::0;2329:18:::1;:38::i;:::-;2164:210:::0;;:::o;5377:998::-;902:33:18;915:10;927:7;;-1:-1:-1;;;;;;927:7:18;902:12;:33::i;:::-;894:58;;;;-1:-1:-1;;;894:58:18;;;;;;;:::i;:::-;5619:10:27::1;-1:-1:-1::0;;;;;5641:5:27::1;5619:28;;5615:67;;5656:26;;-1:-1:-1::0;;;5656:26:27::1;;;;;;;;;;;5615:67;-1:-1:-1::0;;;;;5696:26:27;::::1;5717:4;5696:26;5692:70;;5731:31;;-1:-1:-1::0;;;5731:31:27::1;;;;;;;;;;;5692:70;5773:19;5795:34;::::0;;::::1;5806:9:::0;5795:34:::1;:::i;:::-;5773:56:::0;-1:-1:-1;5857:23:27::1;5844:9;:36;;;;;;;;:::i;:::-;::::0;5840:529:::1;;5896:83;5915:9;;5926:11;5939:10;5951:11;5964:14;5896:18;:83::i;:::-;5840:529;;;6013:28;6000:9;:41;;;;;;;;:::i;:::-;::::0;5996:373:::1;;6057:87;6080:9;;6091:11;6104:10;6116:11;6129:14;6057:22;:87::i;5996:373::-;6328:30;;-1:-1:-1::0;;;6328:30:27::1;;;;;;;;;;;5996:373;5605:770;5377:998:::0;;;;;;;:::o;3758:535::-;902:33:18;915:10;927:7;;-1:-1:-1;;;;;;927:7:18;902:12;:33::i;:::-;894:58;;;;-1:-1:-1;;;894:58:18;;;;;;;:::i;:::-;3930:10:27::1;3914:12;::::0;;;::::1;::::0;::::1;;:::i;:::-;-1:-1:-1::0;;;;;3914:26:27::1;;3910:64;;3949:25;;-1:-1:-1::0;;;3949:25:27::1;;;;;;;;;;;3910:64;4040:43;::::0;;4081:1:::1;4040:43:::0;;;;;::::1;::::0;;;3985:52:::1;::::0;4040:43:::1;;;;;;:::i;:::-;;;;;;;;;;;;;;;-1:-1:-1::0;3985:98:27;-1:-1:-1;4093:21:27::1;;::::0;;::::1;::::0;::::1;4107:7:::0;4093:21:::1;:::i;:::-;:8;4102:1;4093:11;;;;;;;;:::i;:::-;;;;;;:21;;;;4125:22;4161:23:::0;4186:10:::1;4198:6;4206:5;4150:62;;;;;;;;;;;:::i;:::-;;::::0;;-1:-1:-1;;4150:62:27;;::::1;::::0;;;;;;;-1:-1:-1;;;4223:63:27;;4150:62;-1:-1:-1;;;;;;4223:5:27::1;:27;::::0;::::1;::::0;:63:::1;::::0;4251:8;;4150:62;;4280:4:::1;::::0;4223:63:::1;;;:::i;:::-;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;1523:434:18::0;1794:5;;-1:-1:-1;;;;;1794:5:18;1780:10;:19;;:76;;-1:-1:-1;1803:9:18;;:53;;-1:-1:-1;;;1803:53:18;;-1:-1:-1;;;;;1803:9:18;;;;:17;;:53;;1821:10;;1841:4;;-1:-1:-1;;;;;;1803:9:18;1848:7;;;1803:53;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;1772:85;;;;;;1868:9;:24;;-1:-1:-1;;;;;;1868:24:18;-1:-1:-1;;;;;1868:24:18;;;;;;;;1908:42;;1925:10;;1908:42;;-1:-1:-1;;1908:42:18;1523:434;:::o;4543:656:27:-;902:33:18;915:10;927:7;;-1:-1:-1;;;;;;927:7:18;902:12;:33::i;:::-;894:58;;;;-1:-1:-1;;;894:58:18;;;;;;;:::i;:::-;4786:10:27::1;4770:12;::::0;;;::::1;::::0;::::1;;:::i;:::-;-1:-1:-1::0;;;;;4770:26:27::1;;4766:64;;4805:25;;-1:-1:-1::0;;;4805:25:27::1;;;;;;;;;;;4766:64;4896:43;::::0;;4937:1:::1;4896:43:::0;;;;;::::1;::::0;;;4841:52:::1;::::0;4896:43:::1;;;;;;:::i;:::-;;;;;;;;;;;;;;;-1:-1:-1::0;4841:98:27;-1:-1:-1;4949:21:27::1;;::::0;;::::1;::::0;::::1;4963:7:::0;4949:21:::1;:::i;:::-;:8;4958:1;4949:11;;;;;;;;:::i;:::-;;;;;;:21;;;;4981:22;5029:28;5059:10;5071;5083:8;5093:17;5112:5;5018:100;;;;;;;;;;;;;:::i;:::-;;::::0;;-1:-1:-1;;5018:100:27;;::::1;::::0;;;;;;;-1:-1:-1;;;5129:63:27;;5018:100;-1:-1:-1;;;;;;5129:5:27::1;:27;::::0;::::1;::::0;:63:::1;::::0;5157:8;;5018:100;;5186:4:::1;::::0;5129:63:::1;;;:::i;:::-;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;4756:443;;4543:656:::0;;;;:::o;1224:484:9:-;1390:12;;;1326:20;1390:12;;;;;;;;1292:22;;1501:4;1489:24;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1479:34;;1528:9;1523:155;1543:15;;;1523:155;;;1592:75;1629:4;1649;;1654:1;1649:7;;;;;;;:::i;:::-;;;;;;;;;;;;:::i;:::-;1658;1636:30;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;1592:28;:75::i;:::-;1579:7;1587:1;1579:10;;;;;;;;:::i;:::-;;;;;;:88;;;;1560:3;;;;;:::i;:::-;;;;1523:155;;;;1687:14;1224:484;;;;;:::o;2563:318:27:-;902:33:18;915:10;927:7;;-1:-1:-1;;;;;;927:7:18;902:12;:33::i;:::-;894:58;;;;-1:-1:-1;;;894:58:18;;;;;;;:::i;:::-;2714:22:27::1;2750:23:::0;2775:10:::1;2787:6;2795:4;2739:61;;;;;;;;;;;:::i;:::-;;::::0;;-1:-1:-1;;2739:61:27;;::::1;::::0;;;;;;;-1:-1:-1;;;2811:63:27;;2739:61;-1:-1:-1;;;;;;2811:5:27::1;:27;::::0;::::1;::::0;:63:::1;::::0;2839:8;;;;2739:61;;2868:4:::1;::::0;2811:63:::1;;;:::i;1963:164:18:-:0;902:33;915:10;927:7;;-1:-1:-1;;;;;;927:7:18;902:12;:33::i;:::-;894:58;;;;-1:-1:-1;;;894:58:18;;;;;;;:::i;:::-;2046:5:::1;:16:::0;;-1:-1:-1;;;;;;2046:16:18::1;-1:-1:-1::0;;;;;2046:16:18;::::1;::::0;;::::1;::::0;;2078:42:::1;::::0;2046:16;;2099:10:::1;::::0;2078:42:::1;::::0;2046:5;2078:42:::1;1963:164:::0;:::o;3119:439:27:-;902:33:18;915:10;927:7;;-1:-1:-1;;;;;;927:7:18;902:12;:33::i;:::-;894:58;;;;-1:-1:-1;;;894:58:18;;;;;;;:::i;:::-;3341:22:27::1;3389:28;3419:10;3431;3443:8;3453:17;3472:4;3378:99;;;;;;;;;;;;;:::i;:::-;;::::0;;-1:-1:-1;;3378:99:27;;::::1;::::0;;;;;;;-1:-1:-1;;;3488:63:27;;3378:99;-1:-1:-1;;;;;;3488:5:27::1;:27;::::0;::::1;::::0;:63:::1;::::0;3516:8;;;;3378:99;;3545:4:::1;::::0;3488:63:::1;;;:::i;977:540:18:-:0;1097:9;;1064:4;;-1:-1:-1;;;;;1097:9:18;1415:27;;;;;:77;;-1:-1:-1;1446:46:18;;-1:-1:-1;;;1446:46:18;;-1:-1:-1;;;;;1446:12:18;;;;;:46;;1459:4;;1473;;1480:11;;1446:46;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;1414:96;;;-1:-1:-1;1505:5:18;;-1:-1:-1;;;;;1497:13:18;;;1505:5;;1497:13;1414:96;1407:103;977:540;-1:-1:-1;;;;977:540:18:o;2832:1464:23:-;2944:12;3114:4;3108:11;-1:-1:-1;;;3237:17:23;3230:93;-1:-1:-1;;;;;3374:2:23;3370:51;3366:1;3347:17;3343:25;3336:86;3508:6;3503:2;3484:17;3480:26;3473:42;3855:2;3852:1;3848:2;3829:17;3826:1;3819:5;3812;3807:51;3796:62;;;4125:7;4118:2;4100:16;4097:24;4093:1;4089;4083:8;4080:15;4076:46;4069:54;4065:68;4062:172;;;-1:-1:-1;4180:18:23;;4173:26;4201:16;4170:48;4163:56;4062:172;4262:7;4254:35;;;;-1:-1:-1;;;4254:35:23;;19624:2:33;4254:35:23;;;19606:21:33;19663:2;19643:18;;;19636:30;-1:-1:-1;;;19682:18:33;;;19675:45;19737:18;;4254:35:23;19422:339:33;4254:35:23;2934:1362;2832:1464;;;:::o;6595:1279:27:-;6803:20;;;6896:78;;;;6907:9;6896:78;:::i;:::-;6800:174;;;;;;;7012:6;-1:-1:-1;;;;;7012:12:27;;:14;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;;;;;6989:38:27;:11;-1:-1:-1;;;;;6989:38:27;;6985:146;;7050:70;;-1:-1:-1;;;7050:70:27;;-1:-1:-1;;;;;20963:15:33;;;7050:70:27;;;20945:34:33;21015:15;;20995:18;;;20988:43;20880:18;;7050:70:27;20733:304:33;6985:146:27;7261:70;;-1:-1:-1;;;7261:70:27;;7161:10;;7141:11;;-1:-1:-1;;;;;7261:19:27;;;;;:70;;7161:10;;7288:11;;7301:14;;7325:4;;7261:70;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;7241:90;;7559:14;7555:195;;;7589:60;7608:12;7622:26;7634:14;7622:9;:26;:::i;:::-;-1:-1:-1;;;;;7589:18:27;;;:60;:18;:60::i;:::-;7555:195;;;7680:59;7699:11;7712:26;7724:14;7712:9;:26;:::i;7680:59::-;7822:45;;-1:-1:-1;;;7822:45:27;;-1:-1:-1;;;;;7844:5:27;21700:32:33;;7822:45:27;;;21682:51:33;21749:18;;;21742:34;;;7822:13:27;;;;;21655:18:33;;7822:45:27;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;6790:1084;;;;;6595:1279;;;;;;:::o;8032:2572::-;8276:20;;;;;8481:136;;;;8505:9;8481:136;:::i;:::-;8248:369;;;;;;;;;;;8659:10;-1:-1:-1;;;;;8659:16:27;;:18;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;;;;;8632:46:27;:15;-1:-1:-1;;;;;8632:46:27;;8628:162;;8701:78;;-1:-1:-1;;;8701:78:27;;-1:-1:-1;;;;;20963:15:33;;;8701:78:27;;;20945:34:33;21015:15;;20995:18;;;20988:43;20880:18;;8701:78:27;20733:304:33;8628:162:27;8829:8;-1:-1:-1;;;;;8829:14:27;;:16;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;;;;;8804:42:27;:13;-1:-1:-1;;;;;8804:42:27;;8800:154;;8869:74;;-1:-1:-1;;;8869:74:27;;-1:-1:-1;;;;;20963:15:33;;;8869:74:27;;;20945:34:33;21015:15;;20995:18;;;20988:43;20880:18;;8869:74:27;20733:304:33;8800:154:27;9056:73;;-1:-1:-1;;;9056:73:27;;9033:20;;-1:-1:-1;;;;;9056:23:27;;;;;:73;;9080:17;;9099:11;;9033:20;;9123:4;;9056:73;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;9033:96;;9364:34;9401:153;9442:8;-1:-1:-1;;;;;9442:19:27;;:21;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;:59;;-1:-1:-1;;;9442:59:27;;-1:-1:-1;;;;;2939:32:33;;;9442:59:27;;;2921:51:33;9442:40:27;;;;;;;2894:18:33;;9442:59:27;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;9522:5;-1:-1:-1;;;;;9503:35:27;;:37;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;9401:14;;:153;:23;:153::i;:::-;9364:190;-1:-1:-1;9652:41:27;9364:190;9652:12;:41;:::i;:::-;9637:56;-1:-1:-1;9778:72:27;-1:-1:-1;;;;;9778:29:27;;9808:13;9823:26;9778:29;:72::i;:::-;9933:98;;-1:-1:-1;;;9933:98:27;;-1:-1:-1;;;;;9933:20:27;;;;;:98;;9954:17;;9973:26;;10001:14;;10025:4;;9933:98;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;9139:903;10274:14;10270:195;;;10304:58;-1:-1:-1;;;;;10304:30:27;;10335:12;10349;10304:30;:58::i;:::-;10270:195;;;10393:61;-1:-1:-1;;;;;10393:30:27;;10424:15;10441:12;10393:30;:61::i;:::-;10537:60;;-1:-1:-1;;;10537:60:27;;-1:-1:-1;;;;;10574:5:27;21700:32:33;;10537:60:27;;;21682:51:33;21749:18;;;21742:34;;;10537:28:27;;;;;21655:18:33;;10537:60:27;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;8238:2366;;;;;;8032:2572;;;;;;:::o;3916:253:4:-;3999:12;4024;4038:23;4065:6;-1:-1:-1;;;;;4065:19:4;4085:4;4065:25;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4023:67;;;;4107:55;4134:6;4142:7;4151:10;4107:26;:55::i;:::-;4100:62;3916:253;-1:-1:-1;;;;;3916:253:4:o;2096:672:21:-;2210:9;2458:1;-1:-1:-1;;2441:19:21;2438:1;2435:26;2432:1;2428:34;2421:42;2408:11;2404:60;2394:116;;2494:1;2491;2484:12;2394:116;-1:-1:-1;2728:9:21;;2691:27;;;2688:34;;2724:27;;;2684:68;;2096:672::o;4302:1462:23:-;4413:12;4583:4;4577:11;-1:-1:-1;;;4706:17:23;4699:93;-1:-1:-1;;;;;4843:2:23;4839:51;4835:1;4816:17;4812:25;4805:86;4977:6;4972:2;4953:17;4949:26;4942:42;5324:2;5321:1;5317:2;5298:17;5295:1;5288:5;5281;5276:51;5265:62;;;5594:7;5587:2;5569:16;5566:24;5562:1;5558;5552:8;5549:15;5545:46;5538:54;5534:68;5531:172;;;-1:-1:-1;5649:18:23;;5642:26;5670:16;5639:48;5632:56;5531:172;5731:7;5723:34;;;;-1:-1:-1;;;5723:34:23;;24309:2:33;5723:34:23;;;24291:21:33;24348:2;24328:18;;;24321:30;-1:-1:-1;;;24367:18:33;;;24360:44;24421:18;;5723:34:23;24107:338:33;4437:582:4;4581:12;4610:7;4605:408;;4633:19;4641:10;4633:7;:19::i;:::-;4605:408;;;4857:17;;:22;:49;;;;-1:-1:-1;;;;;;4883:18:4;;;:23;4857:49;4853:119;;;4933:24;;-1:-1:-1;;;4933:24:4;;-1:-1:-1;;;;;2939:32:33;;4933:24:4;;;2921:51:33;2894:18;;4933:24:4;2775:203:33;4853:119:4;-1:-1:-1;4992:10:4;4605:408;4437:582;;;;;:::o;5559:434::-;5690:17;;:21;5686:301;;5894:10;5888:17;5881:4;5869:10;5865:21;5858:48;5686:301;5957:19;;-1:-1:-1;;;5957:19:4;;;;;;;;;;;5686:301;5559:434;:::o;-1:-1:-1:-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;14:138:33:-;-1:-1:-1;;;;;96:31:33;;86:42;;76:70;;142:1;139;132:12;157:336;239:6;247;300:2;288:9;279:7;275:23;271:32;268:52;;;316:1;313;306:12;268:52;355:9;342:23;374:38;406:5;374:38;:::i;:::-;431:5;483:2;468:18;;;;455:32;;-1:-1:-1;;;157:336:33:o;498:141::-;566:20;;595:38;566:20;595:38;:::i;:::-;498:141;;;:::o;644:1168::-;759:6;767;775;783;791;799;807;860:3;848:9;839:7;835:23;831:33;828:53;;;877:1;874;867:12;828:53;916:9;903:23;935:38;967:5;935:38;:::i;:::-;992:5;-1:-1:-1;1049:2:33;1034:18;;1021:32;1062:40;1021:32;1062:40;:::i;:::-;1121:7;-1:-1:-1;1180:2:33;1165:18;;1152:32;1193:40;1152:32;1193:40;:::i;:::-;1252:7;-1:-1:-1;1306:2:33;1291:18;;1278:32;;-1:-1:-1;1357:3:33;1342:19;;1329:33;;-1:-1:-1;1413:3:33;1398:19;;1385:33;1437:18;1467:14;;;1464:34;;;1494:1;1491;1484:12;1464:34;1532:6;1521:9;1517:22;1507:32;;1577:7;1570:4;1566:2;1562:13;1558:27;1548:55;;1599:1;1596;1589:12;1548:55;1639:2;1626:16;1665:2;1657:6;1654:14;1651:34;;;1681:1;1678;1671:12;1651:34;1726:7;1721:2;1712:6;1708:2;1704:15;1700:24;1697:37;1694:57;;;1747:1;1744;1737:12;1694:57;1778:2;1774;1770:11;1760:21;;1800:6;1790:16;;;;;644:1168;;;;;;;;;;:::o;1817:164::-;1885:5;1930:3;1921:6;1916:3;1912:16;1908:26;1905:46;;;1947:1;1944;1937:12;1905:46;-1:-1:-1;1969:6:33;1817:164;-1:-1:-1;1817:164:33:o;1986:398::-;2089:6;2097;2150:3;2138:9;2129:7;2125:23;2121:33;2118:53;;;2167:1;2164;2157:12;2118:53;2190:62;2244:7;2233:9;2190:62;:::i;:::-;2180:72;;2302:3;2291:9;2287:19;2274:33;2316:38;2348:5;2316:38;:::i;:::-;2373:5;2363:15;;;1986:398;;;;;:::o;2389:272::-;2466:6;2519:2;2507:9;2498:7;2494:23;2490:32;2487:52;;;2535:1;2532;2525:12;2487:52;2574:9;2561:23;2593:38;2625:5;2593:38;:::i;2983:696::-;3104:6;3112;3120;3128;3181:3;3169:9;3160:7;3156:23;3152:33;3149:53;;;3198:1;3195;3188:12;3149:53;3221:62;3275:7;3264:9;3221:62;:::i;:::-;3211:72;;3333:3;3322:9;3318:19;3305:33;3347:38;3379:5;3347:38;:::i;:::-;3404:5;-1:-1:-1;3461:3:33;3446:19;;3433:33;3475:40;3433:33;3475:40;:::i;:::-;3534:7;-1:-1:-1;3593:3:33;3578:19;;3565:33;3607:40;3565:33;3607:40;:::i;:::-;2983:696;;;;-1:-1:-1;2983:696:33;;-1:-1:-1;;2983:696:33:o;3684:626::-;3781:6;3789;3842:2;3830:9;3821:7;3817:23;3813:32;3810:52;;;3858:1;3855;3848:12;3810:52;3898:9;3885:23;3927:18;3968:2;3960:6;3957:14;3954:34;;;3984:1;3981;3974:12;3954:34;4022:6;4011:9;4007:22;3997:32;;4067:7;4060:4;4056:2;4052:13;4048:27;4038:55;;4089:1;4086;4079:12;4038:55;4129:2;4116:16;4155:2;4147:6;4144:14;4141:34;;;4171:1;4168;4161:12;4141:34;4224:7;4219:2;4209:6;4206:1;4202:14;4198:2;4194:23;4190:32;4187:45;4184:65;;;4245:1;4242;4235:12;4184:65;4276:2;4268:11;;;;;4298:6;;-1:-1:-1;3684:626:33;;-1:-1:-1;;;;3684:626:33:o;4315:250::-;4400:1;4410:113;4424:6;4421:1;4418:13;4410:113;;;4500:11;;;4494:18;4481:11;;;4474:39;4446:2;4439:10;4410:113;;;-1:-1:-1;;4557:1:33;4539:16;;4532:27;4315:250::o;4570:270::-;4611:3;4649:5;4643:12;4676:6;4671:3;4664:19;4692:76;4761:6;4754:4;4749:3;4745:14;4738:4;4731:5;4727:16;4692:76;:::i;:::-;4822:2;4801:15;-1:-1:-1;;4797:29:33;4788:39;;;;4829:4;4784:50;;4570:270;-1:-1:-1;;4570:270:33:o;4845:800::-;5005:4;5034:2;5074;5063:9;5059:18;5104:2;5093:9;5086:21;5127:6;5162;5156:13;5193:6;5185;5178:22;5231:2;5220:9;5216:18;5209:25;;5293:2;5283:6;5280:1;5276:14;5265:9;5261:30;5257:39;5243:53;;5331:2;5323:6;5319:15;5352:1;5362:254;5376:6;5373:1;5370:13;5362:254;;;5469:2;5465:7;5453:9;5445:6;5441:22;5437:36;5432:3;5425:49;5497:39;5529:6;5520;5514:13;5497:39;:::i;:::-;5487:49;-1:-1:-1;5594:12:33;;;;5559:15;;;;5398:1;5391:9;5362:254;;;-1:-1:-1;5633:6:33;;4845:800;-1:-1:-1;;;;;;;4845:800:33:o;5650:391::-;5737:8;5747:6;5801:3;5794:4;5786:6;5782:17;5778:27;5768:55;;5819:1;5816;5809:12;5768:55;-1:-1:-1;5842:20:33;;5885:18;5874:30;;5871:50;;;5917:1;5914;5907:12;5871:50;5954:4;5946:6;5942:17;5930:29;;6014:3;6007:4;5997:6;5994:1;5990:14;5982:6;5978:27;5974:38;5971:47;5968:67;;;6031:1;6028;6021:12;5968:67;5650:391;;;;;:::o;6046:638::-;6176:6;6184;6192;6245:2;6233:9;6224:7;6220:23;6216:32;6213:52;;;6261:1;6258;6251:12;6213:52;6301:9;6288:23;6334:18;6326:6;6323:30;6320:50;;;6366:1;6363;6356:12;6320:50;6405:94;6491:7;6482:6;6471:9;6467:22;6405:94;:::i;:::-;6518:8;;-1:-1:-1;6379:120:33;-1:-1:-1;;6603:2:33;6588:18;;6575:32;6616:38;6575:32;6616:38;:::i;:::-;6673:5;6663:15;;;6046:638;;;;;:::o;7174:935::-;7322:6;7330;7338;7346;7354;7407:3;7395:9;7386:7;7382:23;7378:33;7375:53;;;7424:1;7421;7414:12;7375:53;7464:9;7451:23;7497:18;7489:6;7486:30;7483:50;;;7529:1;7526;7519:12;7483:50;7568:94;7654:7;7645:6;7634:9;7630:22;7568:94;:::i;:::-;7681:8;;-1:-1:-1;7542:120:33;-1:-1:-1;;7766:2:33;7751:18;;7738:32;7779:38;7738:32;7779:38;:::i;:::-;7836:5;-1:-1:-1;7893:2:33;7878:18;;7865:32;7906:40;7865:32;7906:40;:::i;:::-;7965:7;-1:-1:-1;8024:2:33;8009:18;;7996:32;8037:40;7996:32;8037:40;:::i;:::-;8096:7;8086:17;;;7174:935;;;;;;;;:::o;8114:336::-;8316:2;8298:21;;;8355:2;8335:18;;;8328:30;-1:-1:-1;;;8389:2:33;8374:18;;8367:42;8441:2;8426:18;;8114:336::o;8455:184::-;8525:6;8578:2;8566:9;8557:7;8553:23;8549:32;8546:52;;;8594:1;8591;8584:12;8546:52;-1:-1:-1;8617:16:33;;8455:184;-1:-1:-1;8455:184:33:o;8644:150::-;8719:20;;8768:1;8758:12;;8748:40;;8784:1;8781;8774:12;8799:208;8873:6;8926:2;8914:9;8905:7;8901:23;8897:32;8894:52;;;8942:1;8939;8932:12;8894:52;8965:36;8991:9;8965:36;:::i;9012:127::-;9073:10;9068:3;9064:20;9061:1;9054:31;9104:4;9101:1;9094:15;9128:4;9125:1;9118:15;9144:127;9205:10;9200:3;9196:20;9193:1;9186:31;9236:4;9233:1;9226:15;9260:4;9257:1;9250:15;9276:179;9343:20;;-1:-1:-1;;;;;9392:38:33;;9382:49;;9372:77;;9445:1;9442;9435:12;9460:188;9528:20;;-1:-1:-1;;;;;9577:46:33;;9567:57;;9557:85;;9638:1;9635;9628:12;9653:165;9720:20;;9780:12;9769:24;;9759:35;;9749:63;;9808:1;9805;9798:12;9823:161;9890:20;;9950:8;9939:20;;9929:31;;9919:59;;9974:1;9971;9964:12;9989:1079;10081:6;10112:3;10156:2;10144:9;10135:7;10131:23;10127:32;10124:52;;;10172:1;10169;10162:12;10124:52;10205:2;10199:9;10235:15;;;;10280:18;10265:34;;10301:22;;;10262:62;10259:185;;;10366:10;10361:3;10357:20;10354:1;10347:31;10401:4;10398:1;10391:15;10429:4;10426:1;10419:15;10259:185;10464:10;10460:2;10453:22;10499:28;10517:9;10499:28;:::i;:::-;10491:6;10484:44;10561:38;10595:2;10584:9;10580:18;10561:38;:::i;:::-;10556:2;10548:6;10544:15;10537:63;10633:38;10667:2;10656:9;10652:18;10633:38;:::i;:::-;10628:2;10620:6;10616:15;10609:63;10705:38;10739:2;10728:9;10724:18;10705:38;:::i;:::-;10700:2;10692:6;10688:15;10681:63;10778:39;10812:3;10801:9;10797:19;10778:39;:::i;:::-;10772:3;10764:6;10760:16;10753:65;10852:38;10885:3;10874:9;10870:19;10852:38;:::i;:::-;10846:3;10838:6;10834:16;10827:64;10925:38;10958:3;10947:9;10943:19;10925:38;:::i;:::-;10919:3;10911:6;10907:16;10900:64;10998:38;11031:3;11020:9;11016:19;10998:38;:::i;:::-;10992:3;10980:16;;10973:64;10984:6;9989:1079;-1:-1:-1;;;;9989:1079:33:o;11073:127::-;11134:10;11129:3;11125:20;11122:1;11115:31;11165:4;11162:1;11155:15;11189:4;11186:1;11179:15;11205:237;11286:1;11279:5;11276:12;11266:143;;11331:10;11326:3;11322:20;11319:1;11312:31;11366:4;11363:1;11356:15;11394:4;11391:1;11384:15;11266:143;11418:18;;11205:237::o;11447:489::-;11672:3;11657:19;;11685:44;11661:9;11711:6;11685:44;:::i;:::-;-1:-1:-1;;;;;11803:15:33;;;11798:2;11783:18;;11776:43;11855:15;;;;11850:2;11835:18;;11828:43;11914:14;11907:22;11902:2;11887:18;;;11880:50;;;;11447:489;;-1:-1:-1;11447:489:33:o;12263:1911::-;12574:2;12626:21;;;12696:13;;12599:18;;;12718:22;;;12545:4;;12574:2;12759:3;;12778:18;;;;12815:4;12842:15;;;12545:4;12885:1124;12899:6;12896:1;12893:13;12885:1124;;;12958:13;;13000:9;;-1:-1:-1;;;;;12996:42:33;12984:55;;13078:11;;;13072:18;-1:-1:-1;;;;;13166:21:33;;;13152:12;;;13145:43;13211:4;13259:11;;;13253:18;13249:27;;;13235:12;;;13228:49;13318:11;;;13312:18;-1:-1:-1;;;;;12007:46:33;;;13378:12;;;11995:59;13432:11;;;13426:18;12007:46;13492:12;;;11995:59;13528:4;13573:11;;;13567:18;12141:12;12130:24;13632:12;;;12118:37;13668:4;13713:11;;;13707:18;12242:8;12231:20;;;13772:12;;;12219:33;;;;13808:4;13853:11;;;13847:18;12231:20;13912:12;;;12219:33;13954:6;13945:16;;;;13984:15;;;;12921:1;12914:9;12885:1124;;;-1:-1:-1;;14045:19:33;;;14025:18;;;14018:47;-1:-1:-1;14082:29:33;14049:3;14099:6;14082:29;:::i;:::-;14074:37;;;;;14120:48;14162:4;14151:9;14147:20;14139:6;-1:-1:-1;;;;;2732:31:33;2720:44;;2666:104;14179:400;-1:-1:-1;;;;;14435:15:33;;;14417:34;;14487:15;;;;14482:2;14467:18;;14460:43;-1:-1:-1;;;;;;14539:33:33;;;14534:2;14519:18;;14512:61;14367:2;14352:18;;14179:400::o;14584:118::-;14670:5;14663:13;14656:21;14649:5;14646:32;14636:60;;14692:1;14689;14682:12;14707:245;14774:6;14827:2;14815:9;14806:7;14802:23;14798:32;14795:52;;;14843:1;14840;14833:12;14795:52;14875:9;14869:16;14894:28;14916:5;14894:28;:::i;14957:651::-;15238:3;15223:19;;15251:44;15227:9;15277:6;15251:44;:::i;:::-;-1:-1:-1;;;;;15369:15:33;;;15364:2;15349:18;;15342:43;15421:15;;;15416:2;15401:18;;15394:43;15473:15;;;15468:2;15453:18;;15446:43;15526:15;;15520:3;15505:19;;15498:44;15586:14;;15579:22;15322:3;15558:19;;;15551:51;;;;15421:15;14957:651;-1:-1:-1;14957:651:33:o;15613:127::-;15674:10;15669:3;15665:20;15662:1;15655:31;15705:4;15702:1;15695:15;15729:4;15726:1;15719:15;15745:128;15812:9;;;15833:11;;;15830:37;;;15847:18;;:::i;16214:521::-;16291:4;16297:6;16357:11;16344:25;16451:2;16447:7;16436:8;16420:14;16416:29;16412:43;16392:18;16388:68;16378:96;;16470:1;16467;16460:12;16378:96;16497:33;;16549:20;;;-1:-1:-1;16592:18:33;16581:30;;16578:50;;;16624:1;16621;16614:12;16578:50;16657:4;16645:17;;-1:-1:-1;16688:14:33;16684:27;;;16674:38;;16671:58;;;16725:1;16722;16715:12;16740:440;16969:6;16961;16956:3;16943:33;16925:3;17004:6;16999:3;16995:16;17031:1;17027:2;17020:13;17062:6;17056:13;17078:65;17136:6;17132:2;17125:4;17117:6;17113:17;17078:65;:::i;:::-;17159:15;;16740:440;-1:-1:-1;;;;;16740:440:33:o;17185:135::-;17224:3;17245:17;;;17242:43;;17265:18;;:::i;:::-;-1:-1:-1;17312:1:33;17301:13;;17185:135::o;17325:2092::-;17648:2;17700:21;;;17673:18;;;17756:22;;;17619:4;;17797:3;17816:18;;;17857:6;17619:4;17891:1359;17905:6;17902:1;17899:13;17891:1359;;;-1:-1:-1;;;;;17970:25:33;17988:6;17970:25;:::i;:::-;17966:58;17961:3;17954:71;18048:4;18103:2;18095:6;18091:15;18078:29;18120:38;18152:5;18120:38;:::i;:::-;-1:-1:-1;;;;;18192:31:33;18178:12;;;18171:53;18247:4;18292:15;;;18279:29;18321:40;18279:29;18321:40;:::i;:::-;-1:-1:-1;;;;;2732:31:33;18402:12;;;2720:44;18448:35;18467:15;;;18448:35;:::i;:::-;-1:-1:-1;;;;;12007:46:33;18529:12;;;11995:59;18577:35;18596:15;;;18577:35;:::i;:::-;-1:-1:-1;;;;;12007:46:33;18660:12;;;11995:59;18696:4;18735:34;18753:15;;;18735:34;:::i;:::-;12141:12;12130:24;18816:12;;;12118:37;18852:4;18891:34;18909:15;;;18891:34;:::i;:::-;12242:8;12231:20;18972:12;;;12219:33;19008:4;19047:34;19065:15;;;19047:34;:::i;:::-;12242:8;12231:20;19128:12;;;12219:33;19164:6;19190:12;;;;19225:15;;;;;17927:1;17920:9;17891:1359;;;17895:3;;19297:9;19292:3;19288:19;19281:4;19270:9;19266:20;19259:49;19325:29;19350:3;19342:6;19325:29;:::i;:::-;19317:37;;;;;19363:48;19405:4;19394:9;19390:20;19382:6;-1:-1:-1;;;;;2732:31:33;2720:44;;2666:104;19766:679;19909:6;19917;19925;19933;19986:3;19974:9;19965:7;19961:23;19957:33;19954:53;;;20003:1;20000;19993:12;19954:53;20026:36;20052:9;20026:36;:::i;:::-;20016:46;;20112:2;20101:9;20097:18;20084:32;20125:38;20157:5;20125:38;:::i;:::-;20182:5;-1:-1:-1;20239:2:33;20224:18;;20211:32;20252:40;20211:32;20252:40;:::i;:::-;20311:7;-1:-1:-1;20370:2:33;20355:18;;20342:32;20383:30;20342:32;20383:30;:::i;20450:278::-;20540:6;20593:2;20581:9;20572:7;20568:23;20564:32;20561:52;;;20609:1;20606;20599:12;20561:52;20641:9;20635:16;20660:38;20692:5;20660:38;:::i;21042:461::-;-1:-1:-1;;;;;21343:15:33;;;21325:34;;21390:2;21375:18;;21368:34;;;;21433:2;21418:18;;21411:34;;;;21481:15;;;21476:2;21461:18;;21454:43;21274:3;21259:19;;21042:461::o;21787:1028::-;21999:6;22007;22015;22023;22031;22039;22092:3;22080:9;22071:7;22067:23;22063:33;22060:53;;;22109:1;22106;22099:12;22060:53;22132:36;22158:9;22132:36;:::i;:::-;22122:46;;22218:2;22207:9;22203:18;22190:32;22231:38;22263:5;22231:38;:::i;:::-;22288:5;-1:-1:-1;22345:2:33;22330:18;;22317:32;22358:40;22317:32;22358:40;:::i;:::-;22417:7;-1:-1:-1;22476:2:33;22461:18;;22448:32;22489:40;22448:32;22489:40;:::i;:::-;22548:7;-1:-1:-1;22607:3:33;22592:19;;22579:33;22621:40;22579:33;22621:40;:::i;:::-;22680:7;-1:-1:-1;22739:3:33;22724:19;;22711:33;22753:30;22711:33;22753:30;:::i;:::-;22802:7;22792:17;;;21787:1028;;;;;;;;:::o;23815:287::-;23944:3;23982:6;23976:13;23998:66;24057:6;24052:3;24045:4;24037:6;24033:17;23998:66;:::i;:::-;24080:16;;;;;23815:287;-1:-1:-1;;23815:287:33:o
Swarm Source
ipfs://5f6034f2c1792be5f3ab06a566c42a514f4b675002e83b42654158df0f8f1f80
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.