Source Code
Latest 1 from a total of 1 transactions
| Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
| Transfer Ownersh... | 22875582 | 124 days ago | IN | 0 ETH | 0.00015624 |
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
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)
/**
*Submitted for verification at Etherscan.io on 2025-07-08
*/
// File: lib/solmate/src/auth/Auth.sol
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);
}
// File: lib/solmate/src/tokens/ERC20.sol
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);
}
}
// File: lib/solmate/src/utils/SafeTransferLib.sol
pragma solidity >=0.8.0;
/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
/// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer.
/// @dev Note that none of the functions in this library check that a token has code at all! That responsibility is delegated to the caller.
library SafeTransferLib {
/*//////////////////////////////////////////////////////////////
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.
success := and(
// 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.
or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
// 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.
// Counterintuitively, this call must be positioned second to the or() call in the
// surrounding and() call or else returndatasize() will be zero during the computation.
call(gas(), token, 0, freeMemoryPointer, 100, 0, 32)
)
}
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.
success := and(
// 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.
or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
// 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.
// Counterintuitively, this call must be positioned second to the or() call in the
// surrounding and() call or else returndatasize() will be zero during the computation.
call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
)
}
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.
success := and(
// 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.
or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
// 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.
// Counterintuitively, this call must be positioned second to the or() call in the
// surrounding and() call or else returndatasize() will be zero during the computation.
call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
)
}
require(success, "APPROVE_FAILED");
}
}
// File: lib/solmate/src/tokens/WETH.sol
pragma solidity >=0.8.0;
/// @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();
}
}
// File: lib/openzeppelin-contracts/contracts/utils/Address.sol
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)
pragma solidity ^0.8.20;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev The ETH balance of the account is not enough to perform the operation.
*/
error AddressInsufficientBalance(address account);
/**
* @dev There's no code at `target` (it is not a contract).
*/
error AddressEmptyCode(address target);
/**
* @dev A call to an address target failed. The target may have reverted.
*/
error FailedInnerCall();
/**
* @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 AddressInsufficientBalance(address(this));
}
(bool success, ) = recipient.call{value: amount}("");
if (!success) {
revert FailedInnerCall();
}
}
/**
* @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
* {FailedInnerCall} 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 AddressInsufficientBalance(address(this));
}
(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 {FailedInnerCall}) 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 {FailedInnerCall} 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 {FailedInnerCall}.
*/
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
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert FailedInnerCall();
}
}
}
// File: lib/openzeppelin-contracts/contracts/token/ERC721/IERC721Receiver.sol
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/IERC721Receiver.sol)
pragma solidity ^0.8.20;
/**
* @title ERC721 token receiver interface
* @dev Interface for any contract that wants to support safeTransfers
* from ERC721 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);
}
// File: lib/openzeppelin-contracts/contracts/token/ERC721/utils/ERC721Holder.sol
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/utils/ERC721Holder.sol)
pragma solidity ^0.8.20;
/**
* @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;
}
}
// File: lib/openzeppelin-contracts/contracts/utils/introspection/IERC165.sol
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[EIP].
*
* 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[EIP 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);
}
// File: lib/openzeppelin-contracts/contracts/utils/introspection/ERC165.sol
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/ERC165.sol)
pragma solidity ^0.8.20;
/**
* @dev Implementation of the {IERC165} interface.
*
* Contracts that want to implement ERC165 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 {
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}
// File: lib/openzeppelin-contracts/contracts/token/ERC1155/IERC1155Receiver.sol
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC1155/IERC1155Receiver.sol)
pragma solidity ^0.8.20;
/**
* @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 ERC1155 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 ERC1155 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);
}
// File: lib/openzeppelin-contracts/contracts/token/ERC1155/utils/ERC1155Holder.sol
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC1155/utils/ERC1155Holder.sol)
pragma solidity ^0.8.20;
/**
* @dev Simple implementation of `IERC1155Receiver` that will allow a contract to hold ERC1155 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 {
/**
* @dev See {IERC165-supportsInterface}.
*/
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;
}
}
// File: lib/solmate/src/utils/FixedPointMathLib.sol
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))
}
}
}
// File: src/interfaces/BeforeTransferHook.sol
pragma solidity 0.8.21;
interface BeforeTransferHook {
function beforeTransfer(address from, address to, address operator) external view;
}
// File: src/base/BoringVault.sol
pragma solidity 0.8.21;
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 {}
}
// File: src/interfaces/IRateProvider.sol
// 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);
}
// File: src/interfaces/IPausable.sol
pragma solidity 0.8.21;
interface IPausable {
function pause() external;
function unpause() external;
}
// File: src/base/Roles/AccountantWithRateProviders.sol
pragma solidity 0.8.21;
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);
}
}
// File: lib/solmate/src/utils/ReentrancyGuard.sol
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;
}
}
// File: lib/openzeppelin-contracts/contracts/utils/structs/EnumerableSet.sol
// OpenZeppelin Contracts (last updated v5.0.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.
pragma solidity ^0.8.20;
/**
* @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.
*
* ```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 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 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;
/// @solidity memory-safe-assembly
assembly {
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 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;
/// @solidity memory-safe-assembly
assembly {
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 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;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
}
// File: src/base/Roles/BoringQueue/IBoringSolver.sol
pragma solidity 0.8.21;
interface IBoringSolver {
function boringSolve(
address initiator,
address boringVault,
address solveAsset,
uint256 totalShares,
uint256 requiredAssets,
bytes calldata solveData
) external;
}
// File: src/base/Roles/BoringQueue/BoringOnChainQueue.sol
pragma solidity 0.8.21;
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.
* @param withdrawCapacity The maximum amount of total shares that can be withdrawn.
* - Can be set to type(uint256).max to allow unlimited withdraws.
* - Decremented when users make requests.
* - Incremented when users cancel requests.
* - Can be set by admin.
*/
struct WithdrawAsset {
bool allowWithdraws;
uint24 secondsToMaturity;
uint24 minimumSecondsToDeadline;
uint16 minDiscount;
uint16 maxDiscount;
uint96 minimumShares;
uint256 withdrawCapacity;
}
/**
* @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();
error BoringOnChainQueue__NotEnoughWithdrawCapacity();
//============================== 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 WithdrawAssetStopped(address indexed assetOut);
event WithdrawAssetUpdated(
address indexed assetOut,
uint24 secondsToMaturity,
uint24 minimumSecondsToDeadline,
uint16 minDiscount,
uint16 maxDiscount,
uint96 minimumShares
);
event WithdrawCapacityUpdated(address indexed assetOut, uint256 withdrawCapacity);
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,
withdrawCapacity: type(uint256).max
});
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 Set the withdraw capacity for an asset.
* @dev Callable by STRATEGIST_MULTISIG_ROLE.
* @param assetOut The asset to set the withdraw capacity for.
* @param withdrawCapacity The new withdraw capacity.
*/
function setWithdrawCapacity(address assetOut, uint256 withdrawCapacity) external requiresAuth {
withdrawAssets[assetOut].withdrawCapacity = withdrawCapacity;
emit WithdrawCapacityUpdated(assetOut, withdrawCapacity);
}
/**
* @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)
{
_decrementWithdrawCapacity(assetOut, amountOfShares);
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) {
_decrementWithdrawCapacity(assetOut, amountOfShares);
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) = _replaceOnChainWithdraw(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);
_incrementWithdrawCapacity(request.assetOut, request.amountOfShares);
boringVault.safeTransfer(request.user, request.amountOfShares);
emit OnChainWithdrawCancelled(requestId, request.user, block.timestamp);
}
/**
* @notice Replace an on-chain withdraw.
* @dev Does not check withdraw capacity since it is replacing an existing request.
* @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 Decrement the withdraw capacity for an asset.
* @param assetOut The asset to decrement the withdraw capacity for.
* @param amountOfShares The amount of shares to decrement the withdraw capacity for.
*/
function _decrementWithdrawCapacity(address assetOut, uint256 amountOfShares) internal {
WithdrawAsset storage withdrawAsset = withdrawAssets[assetOut];
if (withdrawAsset.withdrawCapacity < type(uint256).max) {
if (withdrawAsset.withdrawCapacity < amountOfShares) revert BoringOnChainQueue__NotEnoughWithdrawCapacity();
withdrawAsset.withdrawCapacity -= amountOfShares;
emit WithdrawCapacityUpdated(assetOut, withdrawAsset.withdrawCapacity);
}
}
/**
* @notice Increment the withdraw capacity for an asset.
* @param assetOut The asset to increment the withdraw capacity for.
* @param amountOfShares The amount of shares to increment the withdraw capacity for.
*/
function _incrementWithdrawCapacity(address assetOut, uint256 amountOfShares) internal {
WithdrawAsset storage withdrawAsset = withdrawAssets[assetOut];
if (withdrawAsset.withdrawCapacity < type(uint256).max) {
withdrawAsset.withdrawCapacity += amountOfShares;
emit WithdrawCapacityUpdated(assetOut, withdrawAsset.withdrawCapacity);
}
}
/**
* @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();
}
}
// File: src/base/Roles/TellerWithMultiAssetSupport.sol
pragma solidity 0.8.21;
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 = 1_000;
// ========================================= 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();
}
}
}
}
// File: lib/openzeppelin-contracts/contracts/utils/Context.sol
// 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;
}
}
// File: lib/openzeppelin-contracts/contracts/utils/Multicall.sol
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Multicall.sol)
pragma solidity ^0.8.20;
/**
* @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 {_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
* {_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;
}
}
// File: src/base/Roles/BoringQueue/BoringSolver.sol
pragma solidity 0.8.21;
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();
error BoringSolver___CannotCoverDeficit(uint256 deficit);
//============================== 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,
bool coverDeficit
) external requiresAuth {
bytes memory solveData = abi.encode(SolveType.BORING_REDEEM, msg.sender, teller, true, coverDeficit);
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,
bool coverDeficit
) external requiresAuth {
bytes memory solveData = abi.encode(
SolveType.BORING_REDEEM_MINT, msg.sender, fromTeller, toTeller, intermediateAsset, true, coverDeficit
);
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, 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, 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, bool coverDeficit) =
abi.decode(solveData, (SolveType, address, TellerWithMultiAssetSupport, bool, 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, 0, address(this));
if (assetsOut > requiredAssets) {
// 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);
}
} else if (assetsOut < requiredAssets) {
// We have a deficit, cover it using solver origin funds if allowed.
uint256 deficit = requiredAssets - assetsOut;
if (coverDeficit) {
asset.safeTransferFrom(solverOrigin, address(this), deficit);
} else {
revert BoringSolver___CannotCoverDeficit(deficit);
}
} // else nothing to do, we have exact change.
// 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,
bool coverDeficit
) = abi.decode(
solveData, (SolveType, address, TellerWithMultiAssetSupport, TellerWithMultiAssetSupport, ERC20, bool, 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()
);
if (excessAssets > assetsToMintRequiredShares) {
// Remove assetsToMintRequiredShares from excessAssets.
excessAssets = excessAssets - assetsToMintRequiredShares;
} else if (excessAssets < assetsToMintRequiredShares) {
// We have a deficit, cover it using solver origin funds if allowed.
uint256 deficit = assetsToMintRequiredShares - excessAssets;
if (coverDeficit) {
intermediateAsset.safeTransferFrom(solverOrigin, address(this), deficit);
} else {
revert BoringSolver___CannotCoverDeficit(deficit);
}
excessAssets = 0;
} else {
excessAssets = 0;
}
// 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 (excessAssets > 0) {
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);
}
}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":[{"internalType":"uint256","name":"deficit","type":"uint256"}],"name":"BoringSolver___CannotCoverDeficit","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":"FailedInnerCall","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"},{"internalType":"bool","name":"coverDeficit","type":"bool"}],"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"},{"internalType":"bool","name":"coverDeficit","type":"bool"}],"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
60a060405234801562000010575f80fd5b5060405162003b9038038062003b9083398181016040528101906200003691906200020f565b8282815f806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508060015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a38073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fa3396fd7f6e0a21b50e5089d2da70d5ac0a3bbbd1f617a93f134b7638998019860405160405180910390a350508073ffffffffffffffffffffffffffffffffffffffff1660808173ffffffffffffffffffffffffffffffffffffffff168152505050505062000268565b5f80fd5b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f620001d982620001ae565b9050919050565b620001eb81620001cd565b8114620001f6575f80fd5b50565b5f815190506200020981620001e0565b92915050565b5f805f60608486031215620002295762000228620001aa565b5b5f6200023886828701620001f9565b93505060206200024b86828701620001f9565b92505060406200025e86828701620001f9565b9150509250925092565b6080516138de620002b25f395f81816103ce015281816104cd0152818161081701528181610c1e01528181610f31015281816115850152818161195b0152611be201526138de5ff3fe608060405234801561000f575f80fd5b50600436106100a7575f3560e01c80638da5cb5b1161006f5780638da5cb5b146101375780638f38660814610155578063ac9650d814610171578063bc9961f7146101a1578063bf7e214f146101bd578063f2fde38b146101db576100a7565b806357376198146100ab5780635ff8a71f146100c757806367aa0416146100e357806372faf4a4146100ff5780637a9e5e4b1461011b575b5f80fd5b6100c560048036038101906100c091906120e0565b6101f7565b005b6100e160048036038101906100dc91906121df565b610334565b005b6100fd60048036038101906100f891906122a5565b61045f565b005b61011960048036038101906101149190612372565b610680565b005b610135600480360381019061013091906123ed565b6108a5565b005b61013f610a5f565b60405161014c9190612427565b60405180910390f35b61016f600480360381019061016a9190612440565b610a82565b005b61018b600480360381019061018691906124fd565b610cae565b604051610198919061268d565b60405180910390f35b6101bb60048036038101906101b691906126ad565b610e92565b005b6101c5610fc4565b6040516101d2919061279e565b60405180910390f35b6101f560048036038101906101f091906127b7565b610fe9565b005b610224335f357fffffffff00000000000000000000000000000000000000000000000000000000166110f1565b610263576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161025a9061283c565b60405180910390fd5b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8103610305578173ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b81526004016102c39190612427565b602060405180830381865afa1580156102de573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610302919061286e565b90505b61033033828473ffffffffffffffffffffffffffffffffffffffff166112299092919063ffffffff16565b5050565b610361335f357fffffffff00000000000000000000000000000000000000000000000000000000166110f1565b6103a0576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103979061283c565b60405180910390fd5b5f8033846001856040516020016103bb95949392919061291b565b60405160208183030381529060405290507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663412638dc868684306040518563ffffffff1660e01b815260040161042b9493929190612ce8565b5f604051808303815f87803b158015610442575f80fd5b505af1158015610454573d5f803e3d5ffd5b505050505050505050565b61048c335f357fffffffff00000000000000000000000000000000000000000000000000000000166110f1565b6104cb576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016104c29061283c565b60405180910390fd5b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610550576040517f6f5561fa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3073ffffffffffffffffffffffffffffffffffffffff168773ffffffffffffffffffffffffffffffffffffffff16146105b5576040517fe041279600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f82828101906105c59190612d50565b90505f60018111156105da576105d9612899565b5b8160018111156105ed576105ec612899565b5b03610605576106008383898989896112d3565b610676565b60018081111561061857610617612899565b5b81600181111561062b5761062a612899565b5b036106435761063e838389898989611611565b610675565b6040517fdab4ed5400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5b5050505050505050565b6106ad335f357fffffffff00000000000000000000000000000000000000000000000000000000166110f1565b6106ec576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106e39061283c565b60405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff1682602001602081019061071691906127b7565b73ffffffffffffffffffffffffffffffffffffffff1614610763576040517f3279bc3000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f600167ffffffffffffffff81111561077f5761077e612d7b565b5b6040519080825280602002602001820160405280156107b857816020015b6107a5611f85565b81526020019060019003908161079d5790505b509050828036038101906107cc9190612ebe565b815f815181106107df576107de612eea565b5b60200260200101819052505f8033845f8060405160200161080495949392919061291b565b60405160208183030381529060405290507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663412638dc8383306040518463ffffffff1660e01b815260040161087293929190613050565b5f604051808303815f87803b158015610889575f80fd5b505af115801561089b573d5f803e3d5ffd5b5050505050505050565b5f8054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614806109ba575060015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663b700961333305f357fffffffff00000000000000000000000000000000000000000000000000000000166040518463ffffffff1660e01b815260040161097a939291906130cd565b602060405180830381865afa158015610995573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906109b99190613116565b5b6109c2575f80fd5b8060015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fa3396fd7f6e0a21b50e5089d2da70d5ac0a3bbbd1f617a93f134b7638998019860405160405180910390a350565b5f8054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b610aaf335f357fffffffff00000000000000000000000000000000000000000000000000000000166110f1565b610aee576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610ae59061283c565b60405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff16846020016020810190610b1891906127b7565b73ffffffffffffffffffffffffffffffffffffffff1614610b65576040517f3279bc3000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f600167ffffffffffffffff811115610b8157610b80612d7b565b5b604051908082528060200260200182016040528015610bba57816020015b610ba7611f85565b815260200190600190039081610b9f5790505b50905084803603810190610bce9190612ebe565b815f81518110610be157610be0612eea565b5b60200260200101819052505f6001338686865f80604051602001610c0b9796959493929190613141565b60405160208183030381529060405290507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663412638dc8383306040518463ffffffff1660e01b8152600401610c7993929190613050565b5f604051808303815f87803b158015610c90575f80fd5b505af1158015610ca2573d5f803e3d5ffd5b50505050505050505050565b60605f610cb9611c6f565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610d59575f36610cf5611c76565b5f369050610d0391906131db565b908092610d1293929190613216565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f81840152601f19601f82011690508083019250505050505050610da7565b5f67ffffffffffffffff811115610d7357610d72612d7b565b5b6040519080825280601f01601f191660200182016040528015610da55781602001600182028036833780820191505090505b505b90508383905067ffffffffffffffff811115610dc657610dc5612d7b565b5b604051908082528060200260200182016040528015610df957816020015b6060815260200190600190039081610de45790505b5091505f5b84849050811015610e8a57610e5930868684818110610e2057610e1f612eea565b5b9050602002810190610e32919061325c565b85604051602001610e459392919061332a565b604051602081830303815290604052611c7a565b838281518110610e6c57610e6b612eea565b5b60200260200101819052508080610e829061334f565b915050610dfe565b505092915050565b610ebf335f357fffffffff00000000000000000000000000000000000000000000000000000000166110f1565b610efe576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610ef59061283c565b60405180910390fd5b5f600133868686600187604051602001610f1e9796959493929190613141565b60405160208183030381529060405290507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663412638dc888884306040518563ffffffff1660e01b8152600401610f8e9493929190612ce8565b5f604051808303815f87803b158015610fa5575f80fd5b505af1158015610fb7573d5f803e3d5ffd5b5050505050505050505050565b60015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b611016335f357fffffffff00000000000000000000000000000000000000000000000000000000166110f1565b611055576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161104c9061283c565b60405180910390fd5b805f806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a350565b5f8060015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690505f73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141580156111cb57508073ffffffffffffffffffffffffffffffffffffffff1663b70096138530866040518463ffffffff1660e01b815260040161118b939291906130cd565b602060405180830381865afa1580156111a6573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906111ca9190613116565b5b8061122057505f8054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff16145b91505092915050565b5f6040517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8416600482015282602482015260205f6044835f895af13d15601f3d1160015f5114161716915050806112cd576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016112c4906133e0565b60405180910390fd5b50505050565b5f805f8089898101906112e69190613474565b9450945094509450508273ffffffffffffffffffffffffffffffffffffffff1663fbfa77cf6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611338573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061135c9190613526565b73ffffffffffffffffffffffffffffffffffffffff168873ffffffffffffffffffffffffffffffffffffffff16146113cd5787836040517f51a7f84c0000000000000000000000000000000000000000000000000000000081526004016113c4929190613551565b60405180910390fd5b5f8790505f8473ffffffffffffffffffffffffffffffffffffffff16633e64ce99838a5f306040518563ffffffff1660e01b815260040161141194939291906135e0565b6020604051808303815f875af115801561142d573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611451919061286e565b9050868111156114d857831561149c5761149786888361147191906131db565b8473ffffffffffffffffffffffffffffffffffffffff166112299092919063ffffffff16565b6114d3565b6114d28a88836114ac91906131db565b8473ffffffffffffffffffffffffffffffffffffffff166112299092919063ffffffff16565b5b611567565b86811015611566575f81886114ed91906131db565b90508315611527576115228730838673ffffffffffffffffffffffffffffffffffffffff16611cfa909392919063ffffffff16565b611564565b806040517fc2fceaf900000000000000000000000000000000000000000000000000000000815260040161155b9190613623565b60405180910390fd5b505b5b8173ffffffffffffffffffffffffffffffffffffffff1663095ea7b37f0000000000000000000000000000000000000000000000000000000000000000896040518363ffffffff1660e01b81526004016115c292919061363c565b6020604051808303815f875af11580156115de573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906116029190613116565b50505050505050505050505050565b5f805f805f808b8b8101906116269190613663565b965096509650965096509650508473ffffffffffffffffffffffffffffffffffffffff1663fbfa77cf6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561167c573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906116a09190613526565b73ffffffffffffffffffffffffffffffffffffffff168a73ffffffffffffffffffffffffffffffffffffffff16146117115789856040517f51a7f84c000000000000000000000000000000000000000000000000000000008152600401611708929190613551565b60405180910390fd5b8373ffffffffffffffffffffffffffffffffffffffff1663fbfa77cf6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561175a573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061177e9190613526565b73ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff16146117ef5788846040517f51a7f84c0000000000000000000000000000000000000000000000000000000081526004016117e6929190613551565b60405180910390fd5b5f8573ffffffffffffffffffffffffffffffffffffffff16633e64ce99858b5f306040518563ffffffff1660e01b815260040161182f94939291906135e0565b6020604051808303815f875af115801561184b573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061186f919061286e565b90505f6119f68673ffffffffffffffffffffffffffffffffffffffff16634fb3ccc56040518163ffffffff1660e01b8152600401602060405180830381865afa1580156118be573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906118e2919061373b565b73ffffffffffffffffffffffffffffffffffffffff1663820973da876040518263ffffffff1660e01b815260040161191a9190613766565b602060405180830381865afa158015611935573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611959919061286e565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663b7d122b56040518163ffffffff1660e01b8152600401602060405180830381865afa1580156119c2573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906119e6919061286e565b8b611dc19092919063ffffffff16565b905080821115611a13578082611a0c91906131db565b9150611aad565b80821015611aa8575f8282611a2891906131db565b90508315611a6257611a5d8930838973ffffffffffffffffffffffffffffffffffffffff16611cfa909392919063ffffffff16565b611a9f565b806040517fc2fceaf9000000000000000000000000000000000000000000000000000000008152600401611a969190613623565b60405180910390fd5b5f925050611aac565b5f91505b5b611ad88b828773ffffffffffffffffffffffffffffffffffffffff16611e0a9092919063ffffffff16565b8573ffffffffffffffffffffffffffffffffffffffff16639d57442086838c306040518563ffffffff1660e01b8152600401611b17949392919061377f565b6020604051808303815f875af1158015611b33573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611b57919061286e565b50505f811115611bc4578215611b9757611b9287828673ffffffffffffffffffffffffffffffffffffffff166112299092919063ffffffff16565b611bc3565b611bc28b828673ffffffffffffffffffffffffffffffffffffffff166112299092919063ffffffff16565b5b5b8973ffffffffffffffffffffffffffffffffffffffff1663095ea7b37f00000000000000000000000000000000000000000000000000000000000000008a6040518363ffffffff1660e01b8152600401611c1f92919061363c565b6020604051808303815f875af1158015611c3b573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611c5f9190613116565b5050505050505050505050505050565b5f33905090565b5f90565b60605f808473ffffffffffffffffffffffffffffffffffffffff1684604051611ca391906137c2565b5f60405180830381855af49150503d805f8114611cdb576040519150601f19603f3d011682016040523d82523d5f602084013e611ce0565b606091505b5091509150611cf0858383611eb4565b9250505092915050565b5f6040517f23b872dd00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8516600482015273ffffffffffffffffffffffffffffffffffffffff8416602482015282604482015260205f6064835f8a5af13d15601f3d1160015f511416171691505080611dba576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611db190613822565b60405180910390fd5b5050505050565b5f827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0484118302158202611df4575f80fd5b81838502045f8385870206110190509392505050565b5f6040517f095ea7b300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8416600482015282602482015260205f6044835f895af13d15601f3d1160015f511416171691505080611eae576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611ea59061388a565b60405180910390fd5b50505050565b606082611ec957611ec482611f41565b611f39565b5f8251148015611eef57505f8473ffffffffffffffffffffffffffffffffffffffff163b145b15611f3157836040517f9996b315000000000000000000000000000000000000000000000000000000008152600401611f289190612427565b60405180910390fd5b819050611f3a565b5b9392505050565b5f81511115611f535780518082602001fd5b6040517f1425ea4200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040518061010001604052805f6bffffffffffffffffffffffff1681526020015f73ffffffffffffffffffffffffffffffffffffffff1681526020015f73ffffffffffffffffffffffffffffffffffffffff1681526020015f6fffffffffffffffffffffffffffffffff1681526020015f6fffffffffffffffffffffffffffffffff1681526020015f64ffffffffff1681526020015f62ffffff1681526020015f62ffffff1681525090565b5f604051905090565b5f80fd5b5f80fd5b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f61206b82612042565b9050919050565b5f61207c82612061565b9050919050565b61208c81612072565b8114612096575f80fd5b50565b5f813590506120a781612083565b92915050565b5f819050919050565b6120bf816120ad565b81146120c9575f80fd5b50565b5f813590506120da816120b6565b92915050565b5f80604083850312156120f6576120f561203a565b5b5f61210385828601612099565b9250506020612114858286016120cc565b9150509250929050565b5f80fd5b5f80fd5b5f80fd5b5f8083601f84011261213f5761213e61211e565b5b8235905067ffffffffffffffff81111561215c5761215b612122565b5b6020830191508361010082028301111561217957612178612126565b5b9250929050565b61218981612061565b8114612193575f80fd5b50565b5f813590506121a481612180565b92915050565b5f8115159050919050565b6121be816121aa565b81146121c8575f80fd5b50565b5f813590506121d9816121b5565b92915050565b5f805f80606085870312156121f7576121f661203a565b5b5f85013567ffffffffffffffff8111156122145761221361203e565b5b6122208782880161212a565b9450945050602061223387828801612196565b9250506040612244878288016121cb565b91505092959194509250565b5f8083601f8401126122655761226461211e565b5b8235905067ffffffffffffffff81111561228257612281612122565b5b60208301915083600182028301111561229e5761229d612126565b5b9250929050565b5f805f805f805f60c0888a0312156122c0576122bf61203a565b5b5f6122cd8a828b01612196565b97505060206122de8a828b01612196565b96505060406122ef8a828b01612196565b95505060606123008a828b016120cc565b94505060806123118a828b016120cc565b93505060a088013567ffffffffffffffff8111156123325761233161203e565b5b61233e8a828b01612250565b925092505092959891949750929550565b5f80fd5b5f61010082840312156123695761236861234f565b5b81905092915050565b5f8061012083850312156123895761238861203a565b5b5f61239685828601612353565b9250506101006123a885828601612196565b9150509250929050565b5f6123bc82612061565b9050919050565b6123cc816123b2565b81146123d6575f80fd5b50565b5f813590506123e7816123c3565b92915050565b5f602082840312156124025761240161203a565b5b5f61240f848285016123d9565b91505092915050565b61242181612061565b82525050565b5f60208201905061243a5f830184612418565b92915050565b5f805f8061016085870312156124595761245861203a565b5b5f61246687828801612353565b94505061010061247887828801612196565b93505061012061248a87828801612196565b92505061014061249c87828801612196565b91505092959194509250565b5f8083601f8401126124bd576124bc61211e565b5b8235905067ffffffffffffffff8111156124da576124d9612122565b5b6020830191508360208202830111156124f6576124f5612126565b5b9250929050565b5f80602083850312156125135761251261203a565b5b5f83013567ffffffffffffffff8111156125305761252f61203e565b5b61253c858286016124a8565b92509250509250929050565b5f81519050919050565b5f82825260208201905092915050565b5f819050602082019050919050565b5f81519050919050565b5f82825260208201905092915050565b5f5b838110156125a857808201518184015260208101905061258d565b5f8484015250505050565b5f601f19601f8301169050919050565b5f6125cd82612571565b6125d7818561257b565b93506125e781856020860161258b565b6125f0816125b3565b840191505092915050565b5f61260683836125c3565b905092915050565b5f602082019050919050565b5f61262482612548565b61262e8185612552565b93508360208202850161264085612562565b805f5b8581101561267b578484038952815161265c85826125fb565b94506126678361260e565b925060208a01995050600181019050612643565b50829750879550505050505092915050565b5f6020820190508181035f8301526126a5818461261a565b905092915050565b5f805f805f8060a087890312156126c7576126c661203a565b5b5f87013567ffffffffffffffff8111156126e4576126e361203e565b5b6126f089828a0161212a565b9650965050602061270389828a01612196565b945050604061271489828a01612196565b935050606061272589828a01612196565b925050608061273689828a016121cb565b9150509295509295509295565b5f819050919050565b5f61276661276161275c84612042565b612743565b612042565b9050919050565b5f6127778261274c565b9050919050565b5f6127888261276d565b9050919050565b6127988161277e565b82525050565b5f6020820190506127b15f83018461278f565b92915050565b5f602082840312156127cc576127cb61203a565b5b5f6127d984828501612196565b91505092915050565b5f82825260208201905092915050565b7f554e415554484f52495a454400000000000000000000000000000000000000005f82015250565b5f612826600c836127e2565b9150612831826127f2565b602082019050919050565b5f6020820190508181035f8301526128538161281a565b9050919050565b5f81519050612868816120b6565b92915050565b5f602082840312156128835761288261203a565b5b5f6128908482850161285a565b91505092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b600281106128d7576128d6612899565b5b50565b5f8190506128e7826128c6565b919050565b5f6128f6826128da565b9050919050565b612906816128ec565b82525050565b612915816121aa565b82525050565b5f60a08201905061292e5f8301886128fd565b61293b6020830187612418565b6129486040830186612418565b612955606083018561290c565b612962608083018461290c565b9695505050505050565b5f82825260208201905092915050565b5f819050919050565b5f6bffffffffffffffffffffffff82169050919050565b6129a581612985565b81146129af575f80fd5b50565b5f813590506129c08161299c565b92915050565b5f6129d460208401846129b2565b905092915050565b6129e581612985565b82525050565b5f6129f96020840184612196565b905092915050565b612a0a81612061565b82525050565b5f6fffffffffffffffffffffffffffffffff82169050919050565b612a3481612a10565b8114612a3e575f80fd5b50565b5f81359050612a4f81612a2b565b92915050565b5f612a636020840184612a41565b905092915050565b612a7481612a10565b82525050565b5f64ffffffffff82169050919050565b612a9381612a7a565b8114612a9d575f80fd5b50565b5f81359050612aae81612a8a565b92915050565b5f612ac26020840184612aa0565b905092915050565b612ad381612a7a565b82525050565b5f62ffffff82169050919050565b612af081612ad9565b8114612afa575f80fd5b50565b5f81359050612b0b81612ae7565b92915050565b5f612b1f6020840184612afd565b905092915050565b612b3081612ad9565b82525050565b6101008201612b475f8301836129c6565b612b535f8501826129dc565b50612b6160208301836129eb565b612b6e6020850182612a01565b50612b7c60408301836129eb565b612b896040850182612a01565b50612b976060830183612a55565b612ba46060850182612a6b565b50612bb26080830183612a55565b612bbf6080850182612a6b565b50612bcd60a0830183612ab4565b612bda60a0850182612aca565b50612be860c0830183612b11565b612bf560c0850182612b27565b50612c0360e0830183612b11565b612c1060e0850182612b27565b50505050565b5f612c218383612b36565b6101008301905092915050565b5f82905092915050565b5f61010082019050919050565b5f612c50838561296c565b9350612c5b8261297c565b805f5b85811015612c9357612c708284612c2e565b612c7a8882612c16565b9750612c8583612c38565b925050600181019050612c5e565b5085925050509392505050565b5f82825260208201905092915050565b5f612cba82612571565b612cc48185612ca0565b9350612cd481856020860161258b565b612cdd816125b3565b840191505092915050565b5f6060820190508181035f830152612d01818688612c45565b90508181036020830152612d158185612cb0565b9050612d246040830184612418565b95945050505050565b60028110612d39575f80fd5b50565b5f81359050612d4a81612d2d565b92915050565b5f60208284031215612d6557612d6461203a565b5b5f612d7284828501612d3c565b91505092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b5f80fd5b612db5826125b3565b810181811067ffffffffffffffff82111715612dd457612dd3612d7b565b5b80604052505050565b5f612de6612031565b9050612df28282612dac565b919050565b5f6101008284031215612e0d57612e0c612da8565b5b612e18610100612ddd565b90505f612e27848285016129b2565b5f830152506020612e3a84828501612196565b6020830152506040612e4e84828501612196565b6040830152506060612e6284828501612a41565b6060830152506080612e7684828501612a41565b60808301525060a0612e8a84828501612aa0565b60a08301525060c0612e9e84828501612afd565b60c08301525060e0612eb284828501612afd565b60e08301525092915050565b5f6101008284031215612ed457612ed361203a565b5b5f612ee184828501612df7565b91505092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b5f81519050919050565b5f819050602082019050919050565b61010082015f820151612f455f8501826129dc565b506020820151612f586020850182612a01565b506040820151612f6b6040850182612a01565b506060820151612f7e6060850182612a6b565b506080820151612f916080850182612a6b565b5060a0820151612fa460a0850182612aca565b5060c0820151612fb760c0850182612b27565b5060e0820151612fca60e0850182612b27565b50505050565b5f612fdb8383612f30565b6101008301905092915050565b5f602082019050919050565b5f612ffe82612f17565b613008818561296c565b935061301383612f21565b805f5b8381101561304357815161302a8882612fd0565b975061303583612fe8565b925050600181019050613016565b5085935050505092915050565b5f6060820190508181035f8301526130688186612ff4565b9050818103602083015261307c8185612cb0565b905061308b6040830184612418565b949350505050565b5f7fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b6130c781613093565b82525050565b5f6060820190506130e05f830186612418565b6130ed6020830185612418565b6130fa60408301846130be565b949350505050565b5f81519050613110816121b5565b92915050565b5f6020828403121561312b5761312a61203a565b5b5f61313884828501613102565b91505092915050565b5f60e0820190506131545f83018a6128fd565b6131616020830189612418565b61316e6040830188612418565b61317b6060830187612418565b6131886080830186612418565b61319560a083018561290c565b6131a260c083018461290c565b98975050505050505050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f6131e5826120ad565b91506131f0836120ad565b9250828203905081811115613208576132076131ae565b5b92915050565b5f80fd5b5f80fd5b5f80858511156132295761322861320e565b5b8386111561323a57613239613212565b5b6001850283019150848603905094509492505050565b5f80fd5b5f80fd5b5f80fd5b5f808335600160200384360303811261327857613277613250565b5b80840192508235915067ffffffffffffffff82111561329a57613299613254565b5b6020830192506001820236038313156132b6576132b5613258565b5b509250929050565b5f81905092915050565b828183375f83830152505050565b5f6132e183856132be565b93506132ee8385846132c8565b82840190509392505050565b5f61330482612571565b61330e81856132be565b935061331e81856020860161258b565b80840191505092915050565b5f6133368285876132d6565b915061334282846132fa565b9150819050949350505050565b5f613359826120ad565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361338b5761338a6131ae565b5b600182019050919050565b7f5452414e534645525f4641494c454400000000000000000000000000000000005f82015250565b5f6133ca600f836127e2565b91506133d582613396565b602082019050919050565b5f6020820190508181035f8301526133f7816133be565b9050919050565b5f61340882612042565b9050919050565b613418816133fe565b8114613422575f80fd5b50565b5f813590506134338161340f565b92915050565b5f61344382612061565b9050919050565b61345381613439565b811461345d575f80fd5b50565b5f8135905061346e8161344a565b92915050565b5f805f805f60a0868803121561348d5761348c61203a565b5b5f61349a88828901612d3c565b95505060206134ab88828901613425565b94505060406134bc88828901613460565b93505060606134cd888289016121cb565b92505060806134de888289016121cb565b9150509295509295909350565b5f6134f5826133fe565b9050919050565b613505816134eb565b811461350f575f80fd5b50565b5f81519050613520816134fc565b92915050565b5f6020828403121561353b5761353a61203a565b5b5f61354884828501613512565b91505092915050565b5f6040820190506135645f830185612418565b6135716020830184612418565b9392505050565b5f6135828261276d565b9050919050565b61359281613578565b82525050565b6135a1816120ad565b82525050565b5f819050919050565b5f6135ca6135c56135c0846135a7565b612743565b6120ad565b9050919050565b6135da816135b0565b82525050565b5f6080820190506135f35f830187613589565b6136006020830186613598565b61360d60408301856135d1565b61361a6060830184612418565b95945050505050565b5f6020820190506136365f830184613598565b92915050565b5f60408201905061364f5f830185612418565b61365c6020830184613598565b9392505050565b5f805f805f805f60e0888a03121561367e5761367d61203a565b5b5f61368b8a828b01612d3c565b975050602061369c8a828b01613425565b96505060406136ad8a828b01613460565b95505060606136be8a828b01613460565b94505060806136cf8a828b01612099565b93505060a06136e08a828b016121cb565b92505060c06136f18a828b016121cb565b91505092959891949750929550565b5f61370a82612061565b9050919050565b61371a81613700565b8114613724575f80fd5b50565b5f8151905061373581613711565b92915050565b5f602082840312156137505761374f61203a565b5b5f61375d84828501613727565b91505092915050565b5f6020820190506137795f830184613589565b92915050565b5f6080820190506137925f830187613589565b61379f6020830186613598565b6137ac6040830185613598565b6137b96060830184612418565b95945050505050565b5f6137cd82846132fa565b915081905092915050565b7f5452414e534645525f46524f4d5f4641494c45440000000000000000000000005f82015250565b5f61380c6014836127e2565b9150613817826137d8565b602082019050919050565b5f6020820190508181035f83015261383981613800565b9050919050565b7f415050524f56455f4641494c45440000000000000000000000000000000000005f82015250565b5f613874600e836127e2565b915061387f82613840565b602082019050919050565b5f6020820190508181035f8301526138a181613868565b905091905056fea2646970667358221220d8534abbe2f413284b7937559a4ea2f330b89f940c4e92d6bc54b1ee3ff6939664736f6c634300081500330000000000000000000000003b694d634981ace4b64a27c48bffe19f1447779b000000000000000000000000110924f5b072679147785e9211612d43490718cf0000000000000000000000005514a3360b460675db9c9414c3cad357098fa964
Deployed Bytecode
0x608060405234801561000f575f80fd5b50600436106100a7575f3560e01c80638da5cb5b1161006f5780638da5cb5b146101375780638f38660814610155578063ac9650d814610171578063bc9961f7146101a1578063bf7e214f146101bd578063f2fde38b146101db576100a7565b806357376198146100ab5780635ff8a71f146100c757806367aa0416146100e357806372faf4a4146100ff5780637a9e5e4b1461011b575b5f80fd5b6100c560048036038101906100c091906120e0565b6101f7565b005b6100e160048036038101906100dc91906121df565b610334565b005b6100fd60048036038101906100f891906122a5565b61045f565b005b61011960048036038101906101149190612372565b610680565b005b610135600480360381019061013091906123ed565b6108a5565b005b61013f610a5f565b60405161014c9190612427565b60405180910390f35b61016f600480360381019061016a9190612440565b610a82565b005b61018b600480360381019061018691906124fd565b610cae565b604051610198919061268d565b60405180910390f35b6101bb60048036038101906101b691906126ad565b610e92565b005b6101c5610fc4565b6040516101d2919061279e565b60405180910390f35b6101f560048036038101906101f091906127b7565b610fe9565b005b610224335f357fffffffff00000000000000000000000000000000000000000000000000000000166110f1565b610263576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161025a9061283c565b60405180910390fd5b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8103610305578173ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b81526004016102c39190612427565b602060405180830381865afa1580156102de573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610302919061286e565b90505b61033033828473ffffffffffffffffffffffffffffffffffffffff166112299092919063ffffffff16565b5050565b610361335f357fffffffff00000000000000000000000000000000000000000000000000000000166110f1565b6103a0576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103979061283c565b60405180910390fd5b5f8033846001856040516020016103bb95949392919061291b565b60405160208183030381529060405290507f0000000000000000000000005514a3360b460675db9c9414c3cad357098fa96473ffffffffffffffffffffffffffffffffffffffff1663412638dc868684306040518563ffffffff1660e01b815260040161042b9493929190612ce8565b5f604051808303815f87803b158015610442575f80fd5b505af1158015610454573d5f803e3d5ffd5b505050505050505050565b61048c335f357fffffffff00000000000000000000000000000000000000000000000000000000166110f1565b6104cb576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016104c29061283c565b60405180910390fd5b7f0000000000000000000000005514a3360b460675db9c9414c3cad357098fa96473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610550576040517f6f5561fa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3073ffffffffffffffffffffffffffffffffffffffff168773ffffffffffffffffffffffffffffffffffffffff16146105b5576040517fe041279600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f82828101906105c59190612d50565b90505f60018111156105da576105d9612899565b5b8160018111156105ed576105ec612899565b5b03610605576106008383898989896112d3565b610676565b60018081111561061857610617612899565b5b81600181111561062b5761062a612899565b5b036106435761063e838389898989611611565b610675565b6040517fdab4ed5400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5b5050505050505050565b6106ad335f357fffffffff00000000000000000000000000000000000000000000000000000000166110f1565b6106ec576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106e39061283c565b60405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff1682602001602081019061071691906127b7565b73ffffffffffffffffffffffffffffffffffffffff1614610763576040517f3279bc3000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f600167ffffffffffffffff81111561077f5761077e612d7b565b5b6040519080825280602002602001820160405280156107b857816020015b6107a5611f85565b81526020019060019003908161079d5790505b509050828036038101906107cc9190612ebe565b815f815181106107df576107de612eea565b5b60200260200101819052505f8033845f8060405160200161080495949392919061291b565b60405160208183030381529060405290507f0000000000000000000000005514a3360b460675db9c9414c3cad357098fa96473ffffffffffffffffffffffffffffffffffffffff1663412638dc8383306040518463ffffffff1660e01b815260040161087293929190613050565b5f604051808303815f87803b158015610889575f80fd5b505af115801561089b573d5f803e3d5ffd5b5050505050505050565b5f8054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614806109ba575060015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663b700961333305f357fffffffff00000000000000000000000000000000000000000000000000000000166040518463ffffffff1660e01b815260040161097a939291906130cd565b602060405180830381865afa158015610995573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906109b99190613116565b5b6109c2575f80fd5b8060015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fa3396fd7f6e0a21b50e5089d2da70d5ac0a3bbbd1f617a93f134b7638998019860405160405180910390a350565b5f8054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b610aaf335f357fffffffff00000000000000000000000000000000000000000000000000000000166110f1565b610aee576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610ae59061283c565b60405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff16846020016020810190610b1891906127b7565b73ffffffffffffffffffffffffffffffffffffffff1614610b65576040517f3279bc3000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f600167ffffffffffffffff811115610b8157610b80612d7b565b5b604051908082528060200260200182016040528015610bba57816020015b610ba7611f85565b815260200190600190039081610b9f5790505b50905084803603810190610bce9190612ebe565b815f81518110610be157610be0612eea565b5b60200260200101819052505f6001338686865f80604051602001610c0b9796959493929190613141565b60405160208183030381529060405290507f0000000000000000000000005514a3360b460675db9c9414c3cad357098fa96473ffffffffffffffffffffffffffffffffffffffff1663412638dc8383306040518463ffffffff1660e01b8152600401610c7993929190613050565b5f604051808303815f87803b158015610c90575f80fd5b505af1158015610ca2573d5f803e3d5ffd5b50505050505050505050565b60605f610cb9611c6f565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610d59575f36610cf5611c76565b5f369050610d0391906131db565b908092610d1293929190613216565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f81840152601f19601f82011690508083019250505050505050610da7565b5f67ffffffffffffffff811115610d7357610d72612d7b565b5b6040519080825280601f01601f191660200182016040528015610da55781602001600182028036833780820191505090505b505b90508383905067ffffffffffffffff811115610dc657610dc5612d7b565b5b604051908082528060200260200182016040528015610df957816020015b6060815260200190600190039081610de45790505b5091505f5b84849050811015610e8a57610e5930868684818110610e2057610e1f612eea565b5b9050602002810190610e32919061325c565b85604051602001610e459392919061332a565b604051602081830303815290604052611c7a565b838281518110610e6c57610e6b612eea565b5b60200260200101819052508080610e829061334f565b915050610dfe565b505092915050565b610ebf335f357fffffffff00000000000000000000000000000000000000000000000000000000166110f1565b610efe576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610ef59061283c565b60405180910390fd5b5f600133868686600187604051602001610f1e9796959493929190613141565b60405160208183030381529060405290507f0000000000000000000000005514a3360b460675db9c9414c3cad357098fa96473ffffffffffffffffffffffffffffffffffffffff1663412638dc888884306040518563ffffffff1660e01b8152600401610f8e9493929190612ce8565b5f604051808303815f87803b158015610fa5575f80fd5b505af1158015610fb7573d5f803e3d5ffd5b5050505050505050505050565b60015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b611016335f357fffffffff00000000000000000000000000000000000000000000000000000000166110f1565b611055576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161104c9061283c565b60405180910390fd5b805f806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a350565b5f8060015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690505f73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141580156111cb57508073ffffffffffffffffffffffffffffffffffffffff1663b70096138530866040518463ffffffff1660e01b815260040161118b939291906130cd565b602060405180830381865afa1580156111a6573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906111ca9190613116565b5b8061122057505f8054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff16145b91505092915050565b5f6040517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8416600482015282602482015260205f6044835f895af13d15601f3d1160015f5114161716915050806112cd576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016112c4906133e0565b60405180910390fd5b50505050565b5f805f8089898101906112e69190613474565b9450945094509450508273ffffffffffffffffffffffffffffffffffffffff1663fbfa77cf6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611338573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061135c9190613526565b73ffffffffffffffffffffffffffffffffffffffff168873ffffffffffffffffffffffffffffffffffffffff16146113cd5787836040517f51a7f84c0000000000000000000000000000000000000000000000000000000081526004016113c4929190613551565b60405180910390fd5b5f8790505f8473ffffffffffffffffffffffffffffffffffffffff16633e64ce99838a5f306040518563ffffffff1660e01b815260040161141194939291906135e0565b6020604051808303815f875af115801561142d573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611451919061286e565b9050868111156114d857831561149c5761149786888361147191906131db565b8473ffffffffffffffffffffffffffffffffffffffff166112299092919063ffffffff16565b6114d3565b6114d28a88836114ac91906131db565b8473ffffffffffffffffffffffffffffffffffffffff166112299092919063ffffffff16565b5b611567565b86811015611566575f81886114ed91906131db565b90508315611527576115228730838673ffffffffffffffffffffffffffffffffffffffff16611cfa909392919063ffffffff16565b611564565b806040517fc2fceaf900000000000000000000000000000000000000000000000000000000815260040161155b9190613623565b60405180910390fd5b505b5b8173ffffffffffffffffffffffffffffffffffffffff1663095ea7b37f0000000000000000000000005514a3360b460675db9c9414c3cad357098fa964896040518363ffffffff1660e01b81526004016115c292919061363c565b6020604051808303815f875af11580156115de573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906116029190613116565b50505050505050505050505050565b5f805f805f808b8b8101906116269190613663565b965096509650965096509650508473ffffffffffffffffffffffffffffffffffffffff1663fbfa77cf6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561167c573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906116a09190613526565b73ffffffffffffffffffffffffffffffffffffffff168a73ffffffffffffffffffffffffffffffffffffffff16146117115789856040517f51a7f84c000000000000000000000000000000000000000000000000000000008152600401611708929190613551565b60405180910390fd5b8373ffffffffffffffffffffffffffffffffffffffff1663fbfa77cf6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561175a573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061177e9190613526565b73ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff16146117ef5788846040517f51a7f84c0000000000000000000000000000000000000000000000000000000081526004016117e6929190613551565b60405180910390fd5b5f8573ffffffffffffffffffffffffffffffffffffffff16633e64ce99858b5f306040518563ffffffff1660e01b815260040161182f94939291906135e0565b6020604051808303815f875af115801561184b573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061186f919061286e565b90505f6119f68673ffffffffffffffffffffffffffffffffffffffff16634fb3ccc56040518163ffffffff1660e01b8152600401602060405180830381865afa1580156118be573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906118e2919061373b565b73ffffffffffffffffffffffffffffffffffffffff1663820973da876040518263ffffffff1660e01b815260040161191a9190613766565b602060405180830381865afa158015611935573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611959919061286e565b7f0000000000000000000000005514a3360b460675db9c9414c3cad357098fa96473ffffffffffffffffffffffffffffffffffffffff1663b7d122b56040518163ffffffff1660e01b8152600401602060405180830381865afa1580156119c2573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906119e6919061286e565b8b611dc19092919063ffffffff16565b905080821115611a13578082611a0c91906131db565b9150611aad565b80821015611aa8575f8282611a2891906131db565b90508315611a6257611a5d8930838973ffffffffffffffffffffffffffffffffffffffff16611cfa909392919063ffffffff16565b611a9f565b806040517fc2fceaf9000000000000000000000000000000000000000000000000000000008152600401611a969190613623565b60405180910390fd5b5f925050611aac565b5f91505b5b611ad88b828773ffffffffffffffffffffffffffffffffffffffff16611e0a9092919063ffffffff16565b8573ffffffffffffffffffffffffffffffffffffffff16639d57442086838c306040518563ffffffff1660e01b8152600401611b17949392919061377f565b6020604051808303815f875af1158015611b33573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611b57919061286e565b50505f811115611bc4578215611b9757611b9287828673ffffffffffffffffffffffffffffffffffffffff166112299092919063ffffffff16565b611bc3565b611bc28b828673ffffffffffffffffffffffffffffffffffffffff166112299092919063ffffffff16565b5b5b8973ffffffffffffffffffffffffffffffffffffffff1663095ea7b37f0000000000000000000000005514a3360b460675db9c9414c3cad357098fa9648a6040518363ffffffff1660e01b8152600401611c1f92919061363c565b6020604051808303815f875af1158015611c3b573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611c5f9190613116565b5050505050505050505050505050565b5f33905090565b5f90565b60605f808473ffffffffffffffffffffffffffffffffffffffff1684604051611ca391906137c2565b5f60405180830381855af49150503d805f8114611cdb576040519150601f19603f3d011682016040523d82523d5f602084013e611ce0565b606091505b5091509150611cf0858383611eb4565b9250505092915050565b5f6040517f23b872dd00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8516600482015273ffffffffffffffffffffffffffffffffffffffff8416602482015282604482015260205f6064835f8a5af13d15601f3d1160015f511416171691505080611dba576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611db190613822565b60405180910390fd5b5050505050565b5f827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0484118302158202611df4575f80fd5b81838502045f8385870206110190509392505050565b5f6040517f095ea7b300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8416600482015282602482015260205f6044835f895af13d15601f3d1160015f511416171691505080611eae576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611ea59061388a565b60405180910390fd5b50505050565b606082611ec957611ec482611f41565b611f39565b5f8251148015611eef57505f8473ffffffffffffffffffffffffffffffffffffffff163b145b15611f3157836040517f9996b315000000000000000000000000000000000000000000000000000000008152600401611f289190612427565b60405180910390fd5b819050611f3a565b5b9392505050565b5f81511115611f535780518082602001fd5b6040517f1425ea4200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040518061010001604052805f6bffffffffffffffffffffffff1681526020015f73ffffffffffffffffffffffffffffffffffffffff1681526020015f73ffffffffffffffffffffffffffffffffffffffff1681526020015f6fffffffffffffffffffffffffffffffff1681526020015f6fffffffffffffffffffffffffffffffff1681526020015f64ffffffffff1681526020015f62ffffff1681526020015f62ffffff1681525090565b5f604051905090565b5f80fd5b5f80fd5b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f61206b82612042565b9050919050565b5f61207c82612061565b9050919050565b61208c81612072565b8114612096575f80fd5b50565b5f813590506120a781612083565b92915050565b5f819050919050565b6120bf816120ad565b81146120c9575f80fd5b50565b5f813590506120da816120b6565b92915050565b5f80604083850312156120f6576120f561203a565b5b5f61210385828601612099565b9250506020612114858286016120cc565b9150509250929050565b5f80fd5b5f80fd5b5f80fd5b5f8083601f84011261213f5761213e61211e565b5b8235905067ffffffffffffffff81111561215c5761215b612122565b5b6020830191508361010082028301111561217957612178612126565b5b9250929050565b61218981612061565b8114612193575f80fd5b50565b5f813590506121a481612180565b92915050565b5f8115159050919050565b6121be816121aa565b81146121c8575f80fd5b50565b5f813590506121d9816121b5565b92915050565b5f805f80606085870312156121f7576121f661203a565b5b5f85013567ffffffffffffffff8111156122145761221361203e565b5b6122208782880161212a565b9450945050602061223387828801612196565b9250506040612244878288016121cb565b91505092959194509250565b5f8083601f8401126122655761226461211e565b5b8235905067ffffffffffffffff81111561228257612281612122565b5b60208301915083600182028301111561229e5761229d612126565b5b9250929050565b5f805f805f805f60c0888a0312156122c0576122bf61203a565b5b5f6122cd8a828b01612196565b97505060206122de8a828b01612196565b96505060406122ef8a828b01612196565b95505060606123008a828b016120cc565b94505060806123118a828b016120cc565b93505060a088013567ffffffffffffffff8111156123325761233161203e565b5b61233e8a828b01612250565b925092505092959891949750929550565b5f80fd5b5f61010082840312156123695761236861234f565b5b81905092915050565b5f8061012083850312156123895761238861203a565b5b5f61239685828601612353565b9250506101006123a885828601612196565b9150509250929050565b5f6123bc82612061565b9050919050565b6123cc816123b2565b81146123d6575f80fd5b50565b5f813590506123e7816123c3565b92915050565b5f602082840312156124025761240161203a565b5b5f61240f848285016123d9565b91505092915050565b61242181612061565b82525050565b5f60208201905061243a5f830184612418565b92915050565b5f805f8061016085870312156124595761245861203a565b5b5f61246687828801612353565b94505061010061247887828801612196565b93505061012061248a87828801612196565b92505061014061249c87828801612196565b91505092959194509250565b5f8083601f8401126124bd576124bc61211e565b5b8235905067ffffffffffffffff8111156124da576124d9612122565b5b6020830191508360208202830111156124f6576124f5612126565b5b9250929050565b5f80602083850312156125135761251261203a565b5b5f83013567ffffffffffffffff8111156125305761252f61203e565b5b61253c858286016124a8565b92509250509250929050565b5f81519050919050565b5f82825260208201905092915050565b5f819050602082019050919050565b5f81519050919050565b5f82825260208201905092915050565b5f5b838110156125a857808201518184015260208101905061258d565b5f8484015250505050565b5f601f19601f8301169050919050565b5f6125cd82612571565b6125d7818561257b565b93506125e781856020860161258b565b6125f0816125b3565b840191505092915050565b5f61260683836125c3565b905092915050565b5f602082019050919050565b5f61262482612548565b61262e8185612552565b93508360208202850161264085612562565b805f5b8581101561267b578484038952815161265c85826125fb565b94506126678361260e565b925060208a01995050600181019050612643565b50829750879550505050505092915050565b5f6020820190508181035f8301526126a5818461261a565b905092915050565b5f805f805f8060a087890312156126c7576126c661203a565b5b5f87013567ffffffffffffffff8111156126e4576126e361203e565b5b6126f089828a0161212a565b9650965050602061270389828a01612196565b945050604061271489828a01612196565b935050606061272589828a01612196565b925050608061273689828a016121cb565b9150509295509295509295565b5f819050919050565b5f61276661276161275c84612042565b612743565b612042565b9050919050565b5f6127778261274c565b9050919050565b5f6127888261276d565b9050919050565b6127988161277e565b82525050565b5f6020820190506127b15f83018461278f565b92915050565b5f602082840312156127cc576127cb61203a565b5b5f6127d984828501612196565b91505092915050565b5f82825260208201905092915050565b7f554e415554484f52495a454400000000000000000000000000000000000000005f82015250565b5f612826600c836127e2565b9150612831826127f2565b602082019050919050565b5f6020820190508181035f8301526128538161281a565b9050919050565b5f81519050612868816120b6565b92915050565b5f602082840312156128835761288261203a565b5b5f6128908482850161285a565b91505092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b600281106128d7576128d6612899565b5b50565b5f8190506128e7826128c6565b919050565b5f6128f6826128da565b9050919050565b612906816128ec565b82525050565b612915816121aa565b82525050565b5f60a08201905061292e5f8301886128fd565b61293b6020830187612418565b6129486040830186612418565b612955606083018561290c565b612962608083018461290c565b9695505050505050565b5f82825260208201905092915050565b5f819050919050565b5f6bffffffffffffffffffffffff82169050919050565b6129a581612985565b81146129af575f80fd5b50565b5f813590506129c08161299c565b92915050565b5f6129d460208401846129b2565b905092915050565b6129e581612985565b82525050565b5f6129f96020840184612196565b905092915050565b612a0a81612061565b82525050565b5f6fffffffffffffffffffffffffffffffff82169050919050565b612a3481612a10565b8114612a3e575f80fd5b50565b5f81359050612a4f81612a2b565b92915050565b5f612a636020840184612a41565b905092915050565b612a7481612a10565b82525050565b5f64ffffffffff82169050919050565b612a9381612a7a565b8114612a9d575f80fd5b50565b5f81359050612aae81612a8a565b92915050565b5f612ac26020840184612aa0565b905092915050565b612ad381612a7a565b82525050565b5f62ffffff82169050919050565b612af081612ad9565b8114612afa575f80fd5b50565b5f81359050612b0b81612ae7565b92915050565b5f612b1f6020840184612afd565b905092915050565b612b3081612ad9565b82525050565b6101008201612b475f8301836129c6565b612b535f8501826129dc565b50612b6160208301836129eb565b612b6e6020850182612a01565b50612b7c60408301836129eb565b612b896040850182612a01565b50612b976060830183612a55565b612ba46060850182612a6b565b50612bb26080830183612a55565b612bbf6080850182612a6b565b50612bcd60a0830183612ab4565b612bda60a0850182612aca565b50612be860c0830183612b11565b612bf560c0850182612b27565b50612c0360e0830183612b11565b612c1060e0850182612b27565b50505050565b5f612c218383612b36565b6101008301905092915050565b5f82905092915050565b5f61010082019050919050565b5f612c50838561296c565b9350612c5b8261297c565b805f5b85811015612c9357612c708284612c2e565b612c7a8882612c16565b9750612c8583612c38565b925050600181019050612c5e565b5085925050509392505050565b5f82825260208201905092915050565b5f612cba82612571565b612cc48185612ca0565b9350612cd481856020860161258b565b612cdd816125b3565b840191505092915050565b5f6060820190508181035f830152612d01818688612c45565b90508181036020830152612d158185612cb0565b9050612d246040830184612418565b95945050505050565b60028110612d39575f80fd5b50565b5f81359050612d4a81612d2d565b92915050565b5f60208284031215612d6557612d6461203a565b5b5f612d7284828501612d3c565b91505092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b5f80fd5b612db5826125b3565b810181811067ffffffffffffffff82111715612dd457612dd3612d7b565b5b80604052505050565b5f612de6612031565b9050612df28282612dac565b919050565b5f6101008284031215612e0d57612e0c612da8565b5b612e18610100612ddd565b90505f612e27848285016129b2565b5f830152506020612e3a84828501612196565b6020830152506040612e4e84828501612196565b6040830152506060612e6284828501612a41565b6060830152506080612e7684828501612a41565b60808301525060a0612e8a84828501612aa0565b60a08301525060c0612e9e84828501612afd565b60c08301525060e0612eb284828501612afd565b60e08301525092915050565b5f6101008284031215612ed457612ed361203a565b5b5f612ee184828501612df7565b91505092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b5f81519050919050565b5f819050602082019050919050565b61010082015f820151612f455f8501826129dc565b506020820151612f586020850182612a01565b506040820151612f6b6040850182612a01565b506060820151612f7e6060850182612a6b565b506080820151612f916080850182612a6b565b5060a0820151612fa460a0850182612aca565b5060c0820151612fb760c0850182612b27565b5060e0820151612fca60e0850182612b27565b50505050565b5f612fdb8383612f30565b6101008301905092915050565b5f602082019050919050565b5f612ffe82612f17565b613008818561296c565b935061301383612f21565b805f5b8381101561304357815161302a8882612fd0565b975061303583612fe8565b925050600181019050613016565b5085935050505092915050565b5f6060820190508181035f8301526130688186612ff4565b9050818103602083015261307c8185612cb0565b905061308b6040830184612418565b949350505050565b5f7fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b6130c781613093565b82525050565b5f6060820190506130e05f830186612418565b6130ed6020830185612418565b6130fa60408301846130be565b949350505050565b5f81519050613110816121b5565b92915050565b5f6020828403121561312b5761312a61203a565b5b5f61313884828501613102565b91505092915050565b5f60e0820190506131545f83018a6128fd565b6131616020830189612418565b61316e6040830188612418565b61317b6060830187612418565b6131886080830186612418565b61319560a083018561290c565b6131a260c083018461290c565b98975050505050505050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f6131e5826120ad565b91506131f0836120ad565b9250828203905081811115613208576132076131ae565b5b92915050565b5f80fd5b5f80fd5b5f80858511156132295761322861320e565b5b8386111561323a57613239613212565b5b6001850283019150848603905094509492505050565b5f80fd5b5f80fd5b5f80fd5b5f808335600160200384360303811261327857613277613250565b5b80840192508235915067ffffffffffffffff82111561329a57613299613254565b5b6020830192506001820236038313156132b6576132b5613258565b5b509250929050565b5f81905092915050565b828183375f83830152505050565b5f6132e183856132be565b93506132ee8385846132c8565b82840190509392505050565b5f61330482612571565b61330e81856132be565b935061331e81856020860161258b565b80840191505092915050565b5f6133368285876132d6565b915061334282846132fa565b9150819050949350505050565b5f613359826120ad565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361338b5761338a6131ae565b5b600182019050919050565b7f5452414e534645525f4641494c454400000000000000000000000000000000005f82015250565b5f6133ca600f836127e2565b91506133d582613396565b602082019050919050565b5f6020820190508181035f8301526133f7816133be565b9050919050565b5f61340882612042565b9050919050565b613418816133fe565b8114613422575f80fd5b50565b5f813590506134338161340f565b92915050565b5f61344382612061565b9050919050565b61345381613439565b811461345d575f80fd5b50565b5f8135905061346e8161344a565b92915050565b5f805f805f60a0868803121561348d5761348c61203a565b5b5f61349a88828901612d3c565b95505060206134ab88828901613425565b94505060406134bc88828901613460565b93505060606134cd888289016121cb565b92505060806134de888289016121cb565b9150509295509295909350565b5f6134f5826133fe565b9050919050565b613505816134eb565b811461350f575f80fd5b50565b5f81519050613520816134fc565b92915050565b5f6020828403121561353b5761353a61203a565b5b5f61354884828501613512565b91505092915050565b5f6040820190506135645f830185612418565b6135716020830184612418565b9392505050565b5f6135828261276d565b9050919050565b61359281613578565b82525050565b6135a1816120ad565b82525050565b5f819050919050565b5f6135ca6135c56135c0846135a7565b612743565b6120ad565b9050919050565b6135da816135b0565b82525050565b5f6080820190506135f35f830187613589565b6136006020830186613598565b61360d60408301856135d1565b61361a6060830184612418565b95945050505050565b5f6020820190506136365f830184613598565b92915050565b5f60408201905061364f5f830185612418565b61365c6020830184613598565b9392505050565b5f805f805f805f60e0888a03121561367e5761367d61203a565b5b5f61368b8a828b01612d3c565b975050602061369c8a828b01613425565b96505060406136ad8a828b01613460565b95505060606136be8a828b01613460565b94505060806136cf8a828b01612099565b93505060a06136e08a828b016121cb565b92505060c06136f18a828b016121cb565b91505092959891949750929550565b5f61370a82612061565b9050919050565b61371a81613700565b8114613724575f80fd5b50565b5f8151905061373581613711565b92915050565b5f602082840312156137505761374f61203a565b5b5f61375d84828501613727565b91505092915050565b5f6020820190506137795f830184613589565b92915050565b5f6080820190506137925f830187613589565b61379f6020830186613598565b6137ac6040830185613598565b6137b96060830184612418565b95945050505050565b5f6137cd82846132fa565b915081905092915050565b7f5452414e534645525f46524f4d5f4641494c45440000000000000000000000005f82015250565b5f61380c6014836127e2565b9150613817826137d8565b602082019050919050565b5f6020820190508181035f83015261383981613800565b9050919050565b7f415050524f56455f4641494c45440000000000000000000000000000000000005f82015250565b5f613874600e836127e2565b915061387f82613840565b602082019050919050565b5f6020820190508181035f8301526138a181613868565b905091905056fea2646970667358221220d8534abbe2f413284b7937559a4ea2f330b89f940c4e92d6bc54b1ee3ff6939664736f6c63430008150033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000003b694d634981ace4b64a27c48bffe19f1447779b000000000000000000000000110924f5b072679147785e9211612d43490718cf0000000000000000000000005514a3360b460675db9c9414c3cad357098fa964
-----Decoded View---------------
Arg [0] : _owner (address): 0x3B694d634981Ace4B64a27c48bffe19f1447779B
Arg [1] : _auth (address): 0x110924f5b072679147785e9211612d43490718Cf
Arg [2] : _queue (address): 0x5514A3360B460675dB9c9414c3CAd357098FA964
-----Encoded View---------------
3 Constructor Arguments found :
Arg [0] : 0000000000000000000000003b694d634981ace4b64a27c48bffe19f1447779b
Arg [1] : 000000000000000000000000110924f5b072679147785e9211612d43490718cf
Arg [2] : 0000000000000000000000005514a3360b460675db9c9414c3cad357098fa964
Deployed Bytecode Sourcemap
138972:11739:0:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;140648:213;;;;;;;;;;;;;:::i;:::-;;:::i;:::-;;141057:369;;;;;;;;;;;;;:::i;:::-;;:::i;:::-;;144052:1020;;;;;;;;;;;;;:::i;:::-;;:::i;:::-;;142379:554;;;;;;;;;;;;;:::i;:::-;;:::i;:::-;;1561:442;;;;;;;;;;;;;:::i;:::-;;:::i;:::-;;574:20;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;143189:678;;;;;;;;;;;;;:::i;:::-;;:::i;:::-;;138374:494;;;;;;;;;;;;;:::i;:::-;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;141670:502;;;;;;;;;;;;;:::i;:::-;;:::i;:::-;;603:26;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;2011:168;;;;;;;;;;;;;:::i;:::-;;:::i;:::-;;140648:213;927:33;940:10;952:7;;;;927:12;:33::i;:::-;919:58;;;;;;;;;;;;:::i;:::-;;;;;;;;;140746:17:::1;140736:6;:27:::0;140732:72:::1;;140774:5;:15;;;140798:4;140774:30;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;140765:39;;140732:72;140815:38;140834:10;140846:6;140815:5;:18;;;;:38;;;;;:::i;:::-;140648:213:::0;;:::o;141057:369::-;927:33;940:10;952:7;;;;927:12;:33::i;:::-;919:58;;;;;;;;;;;;:::i;:::-;;;;;;;;;141242:22:::1;141278:23:::0;141303:10:::1;141315:6;141323:4;141329:12;141267:75;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;141242:100;;141355:5;:27;;;141383:8;;141393:9;141412:4;141355:63;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;141231:195;141057:369:::0;;;;:::o;144052:1020::-;927:33;940:10;952:7;;;;927:12;:33::i;:::-;919:58;;;;;;;;;;;;:::i;:::-;;;;;;;;;144324:5:::1;144302:28;;:10;:28;;;144298:67;;144339:26;;;;;;;;;;;;;;144298:67;144401:4;144380:26;;:9;:26;;;144376:70;;144415:31;;;;;;;;;;;;;;144376:70;144459:19;144492:9;;144481:34;;;;;;;:::i;:::-;144459:56;;144545:23;144532:36;;;;;;;;:::i;:::-;;:9;:36;;;;;;;;:::i;:::-;;::::0;144528:537:::1;;144585:83;144604:9;;144615:11;144628:10;144640:11;144653:14;144585:18;:83::i;:::-;144528:537;;;144703:28;144690:41:::0;::::1;;;;;;;:::i;:::-;;:9;:41;;;;;;;;:::i;:::-;;::::0;144686:379:::1;;144748:87;144771:9;;144782:11;144795:10;144807:11;144820:14;144748:22;:87::i;:::-;144686:379;;;145023:30;;;;;;;;;;;;;;144686:379;144528:537;144287:785;144052:1020:::0;;;;;;;:::o;142379:554::-;927:33;940:10;952:7;;;;927:12;:33::i;:::-;919:58;;;;;;;;;;;;:::i;:::-;;;;;;;;;142555:10:::1;142539:26;;:7;:12;;;;;;;;;;:::i;:::-;:26;;;142535:64;;142574:25;;;;;;;;;;;;;;142535:64;142612:52;142708:1;142667:43;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;142612:98;;142735:7;142721:21;;;;;;;;;;:::i;:::-;:8;142730:1;142721:11;;;;;;;;:::i;:::-;;;;;;;:21;;;;142755:22;142791:23:::0;142816:10:::1;142828:6;142836:5;142843::::0;142780:69:::1;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;142755:94;;142862:5;:27;;;142890:8;142900:9;142919:4;142862:63;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;142524:409;;142379:554:::0;;:::o;1561:442::-;1835:5;;;;;;;;;;1821:19;;:10;:19;;;:76;;;;1844:9;;;;;;;;;;;:17;;;1862:10;1882:4;1889:7;;;;1844:53;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;1821:76;1813:85;;;;;;1923:12;1911:9;;:24;;;;;;;;;;;;;;;;;;1982:12;1953:42;;1970:10;1953:42;;;;;;;;;;;;1561:442;:::o;574:20::-;;;;;;;;;;;;:::o;143189:678::-;927:33;940:10;952:7;;;;927:12;:33::i;:::-;919:58;;;;;;;;;;;;:::i;:::-;;;;;;;;;143438:10:::1;143422:26;;:7;:12;;;;;;;;;;:::i;:::-;:26;;;143418:64;;143457:25;;;;;;;;;;;;;;143418:64;143495:52;143591:1;143550:43;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;143495:98;;143618:7;143604:21;;;;;;;;;;:::i;:::-;:8;143613:1;143604:11;;;;;;;;:::i;:::-;;;;;;;:21;;;;143638:22;143687:28;143717:10;143729;143741:8;143751:17;143770:5;143777::::0;143676:107:::1;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;143638:145;;143796:5;:27;;;143824:8;143834:9;143853:4;143796:63;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;143407:460;;143189:678:::0;;;;:::o;138374:494::-;138442:22;138477:20;138514:12;:10;:12::i;:::-;138500:26;;:10;:26;;;:121;;138570:8;;138597:22;:20;:22::i;:::-;138579:8;;:15;;:40;;;;:::i;:::-;138570:51;;;;;;;;;:::i;:::-;138500:121;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;138552:1;138542:12;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;138500:121;138477:144;;138656:4;;:11;;138644:24;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;138634:34;;138684:9;138679:157;138703:4;;:11;;138699:1;:15;138679:157;;;138749:75;138786:4;138806;;138811:1;138806:7;;;;;;;:::i;:::-;;;;;;;;;;;;;:::i;:::-;138815;138793:30;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;138749:28;:75::i;:::-;138736:7;138744:1;138736:10;;;;;;;;:::i;:::-;;;;;;;:88;;;;138716:3;;;;;:::i;:::-;;;;138679:157;;;;138846:14;138374:494;;;;:::o;141670:502::-;927:33;940:10;952:7;;;;927:12;:33::i;:::-;919:58;;;;;;;;;;;;:::i;:::-;;;;;;;;;141926:22:::1;141976:28;142006:10;142018;142030:8;142040:17;142059:4;142065:12;141951:137;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;141926:162;;142101:5;:27;;;142129:8;;142139:9;142158:4;142101:63;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;141915:257;141670:502:::0;;;;;;:::o;603:26::-;;;;;;;;;;;;;:::o;2011:168::-;927:33;940:10;952:7;;;;927:12;:33::i;:::-;919:58;;;;;;;;;;;;:::i;:::-;;;;;;;;;2103:8:::1;2095:5;::::0;:16:::1;;;;;;;;;;;;;;;;;;2162:8;2129:42;;2150:10;2129:42;;;;;;;;;;;;2011:168:::0;:::o;1007:546::-;1094:4;1111:14;1128:9;;;;;;;;;;;1111:26;;1475:1;1450:27;;1458:4;1450:27;;;;:77;;;;;1481:4;:12;;;1494:4;1508;1515:11;1481:46;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;1450:77;1449:96;;;;1540:5;;;;;;;;;;1532:13;;:4;:13;;;1449:96;1442:103;;;1007:546;;;;:::o;12817:1637::-;12934:12;13109:4;13103:11;13254:66;13235:17;13228:93;13377:42;13373:2;13369:51;13365:1;13346:17;13342:25;13335:86;13508:6;13503:2;13484:17;13480:26;13473:42;14370:2;14367:1;14363:2;14344:17;14341:1;14334:5;14327;14322:51;13886:16;13879:24;13873:2;13855:16;13852:24;13848:1;13844;13838:8;13835:15;13831:46;13828:76;13625:763;13614:774;;13012:1387;14419:7;14411:35;;;;;;;;;;;;:::i;:::-;;;;;;;;;12923:1531;12817:1637;;;:::o;145299:1859::-;145514:20;145536:34;145572:19;145593:17;145638:9;;145627:84;;;;;;;:::i;:::-;145511:200;;;;;;;;;145751:6;:12;;;:14;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;145728:38;;:11;:38;;;145724:148;;145831:11;145852:6;145790:70;;;;;;;;;;;;:::i;:::-;;;;;;;;145724:148;145884:11;145904:10;145884:31;;145986:17;146006:6;:19;;;146026:5;146033:11;146046:1;146057:4;146006:57;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;145986:77;;146092:14;146080:9;:26;146076:909;;;146355:14;146351:215;;;146390:60;146409:12;146435:14;146423:9;:26;;;;:::i;:::-;146390:5;:18;;;;:60;;;;;:::i;:::-;146351:215;;;146491:59;146510:11;146535:14;146523:9;:26;;;;:::i;:::-;146491:5;:18;;;;:59;;;;;:::i;:::-;146351:215;146076:909;;;146599:14;146587:9;:26;146583:402;;;146712:15;146747:9;146730:14;:26;;;;:::i;:::-;146712:44;;146775:12;146771:203;;;146808:60;146831:12;146853:4;146860:7;146808:5;:22;;;;:60;;;;;;:::i;:::-;146771:203;;;146950:7;146916:42;;;;;;;;;;;:::i;:::-;;;;;;;;146771:203;146615:370;146583:402;146076:909;147105:5;:13;;;147127:5;147135:14;147105:45;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;145500:1658;;;;;;145299:1859;;;;;;:::o;147321:3387::-;147574:20;147609:38;147662:36;147713:23;147751:19;147785:17;147841:9;;147816:144;;;;;;;:::i;:::-;147544:416;;;;;;;;;;;;;148004:10;:16;;;:18;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;147977:46;;:15;:46;;;147973:164;;148088:15;148113:10;148047:78;;;;;;;;;;;;:::i;:::-;;;;;;;;147973:164;148178:8;:14;;;:16;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;148153:42;;:13;:42;;;148149:156;;148260:13;148283:8;148219:74;;;;;;;;;;;;:::i;:::-;;;;;;;;148149:156;148387:20;148410:10;:23;;;148434:17;148453:11;148466:1;148477:4;148410:73;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;148387:96;;148722:34;148759:155;148801:8;:19;;;:21;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;:40;;;148842:17;148801:59;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;148881:5;148862:35;;;:37;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;148759:14;:23;;:155;;;;;:::i;:::-;148722:192;;148948:26;148933:12;:41;148929:785;;;149098:26;149083:12;:41;;;;:::i;:::-;149068:56;;148929:785;;;149165:26;149150:12;:41;149146:568;;;149298:15;149345:12;149316:26;:41;;;;:::i;:::-;149298:59;;149380:12;149376:231;;;149417:72;149452:12;149474:4;149481:7;149417:17;:34;;;;:72;;;;;;:::i;:::-;149376:231;;;149579:7;149545:42;;;;;;;;;;;:::i;:::-;;;;;;;;149376:231;149640:1;149625:16;;149193:464;149146:568;;;149697:1;149682:16;;149146:568;148929:785;149801:72;149831:13;149846:26;149801:17;:29;;;;:72;;;;;:::i;:::-;149959:8;:20;;;149980:17;149999:26;150027:14;150051:4;149959:98;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;148494:1575;150321:1;150306:12;:16;150302:263;;;150343:14;150339:215;;;150378:58;150409:12;150423;150378:17;:30;;;;:58;;;;;:::i;:::-;150339:215;;;150477:61;150508:15;150525:12;150477:17;:30;;;;:61;;;;;:::i;:::-;150339:215;150302:263;150646:13;150640:28;;;150677:5;150685:14;150640:60;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;147533:3175;;;;;;;147321:3387;;;;;;:::o;136860:98::-;136913:7;136940:10;136933:17;;136860:98;:::o;137075:99::-;137138:7;137075:99;:::o;21315:256::-;21398:12;21424;21438:23;21465:6;:19;;21485:4;21465:25;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;21423:67;;;;21508:55;21535:6;21543:7;21552:10;21508:26;:55::i;:::-;21501:62;;;;21315:256;;;;:::o;10995:1814::-;11139:12;11314:4;11308:11;11459:66;11440:17;11433:93;11584:42;11578:4;11574:53;11570:1;11551:17;11547:25;11540:88;11725:42;11721:2;11717:51;11712:2;11693:17;11689:26;11682:87;11856:6;11851:2;11832:17;11828:26;11821:42;12720:2;12717:1;12712:3;12693:17;12690:1;12683:5;12676;12671:52;12234:16;12227:24;12221:2;12203:16;12200:24;12196:1;12192;12186:8;12183:15;12179:46;12176:76;11973:765;11962:776;;11217:1532;12769:7;12761:40;;;;;;;;;;;;:::i;:::-;;;;;;;;;11128:1681;10995:1814;;;;:::o;33448:688::-;33566:9;33818:1;33805:11;33801:19;33798:1;33795:26;33792:1;33788:34;33781:42;33768:11;33764:60;33754:118;;33855:1;33852;33845:12;33754:118;34105:11;34101:1;34098;34094:9;34090:27;34086:1;34072:11;34068:1;34065;34061:9;34057:27;34054:34;34050:68;34045:73;;33448:688;;;;;:::o;14462:1635::-;14578:12;14753:4;14747:11;14898:66;14879:17;14872:93;15021:42;15017:2;15013:51;15009:1;14990:17;14986:25;14979:86;15152:6;15147:2;15128:17;15124:26;15117:42;16014:2;16011:1;16007:2;15988:17;15985:1;15978:5;15971;15966:51;15530:16;15523:24;15517:2;15499:16;15496:24;15492:1;15488;15482:8;15479:15;15475:46;15472:76;15269:763;15258:774;;14656:1387;16063:7;16055:34;;;;;;;;;;;;:::i;:::-;;;;;;;;;14567:1530;14462:1635;;;:::o;21844:597::-;21992:12;22022:7;22017:417;;22046:19;22054:10;22046:7;:19::i;:::-;22017:417;;;22295:1;22274:10;:17;:22;:49;;;;;22322:1;22300:6;:18;;;:23;22274:49;22270:121;;;22368:6;22351:24;;;;;;;;;;;:::i;:::-;;;;;;;;22270:121;22412:10;22405:17;;;;22017:417;21844:597;;;;;;:::o;22994:528::-;23147:1;23127:10;:17;:21;23123:392;;;23359:10;23353:17;23416:15;23403:10;23399:2;23395:19;23388:44;23123:392;23486:17;;;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;7:75:1:-;40:6;73:2;67:9;57:19;;7:75;:::o;88:117::-;197:1;194;187:12;211:117;320:1;317;310:12;334:126;371:7;411:42;404:5;400:54;389:65;;334:126;;;:::o;466:96::-;503:7;532:24;550:5;532:24;:::i;:::-;521:35;;466:96;;;:::o;568:109::-;618:7;647:24;665:5;647:24;:::i;:::-;636:35;;568:109;;;:::o;683:148::-;769:37;800:5;769:37;:::i;:::-;762:5;759:48;749:76;;821:1;818;811:12;749:76;683:148;:::o;837:165::-;896:5;934:6;921:20;912:29;;950:46;990:5;950:46;:::i;:::-;837:165;;;;:::o;1008:77::-;1045:7;1074:5;1063:16;;1008:77;;;:::o;1091:122::-;1164:24;1182:5;1164:24;:::i;:::-;1157:5;1154:35;1144:63;;1203:1;1200;1193:12;1144:63;1091:122;:::o;1219:139::-;1265:5;1303:6;1290:20;1281:29;;1319:33;1346:5;1319:33;:::i;:::-;1219:139;;;;:::o;1364:500::-;1445:6;1453;1502:2;1490:9;1481:7;1477:23;1473:32;1470:119;;;1508:79;;:::i;:::-;1470:119;1628:1;1653:66;1711:7;1702:6;1691:9;1687:22;1653:66;:::i;:::-;1643:76;;1599:130;1768:2;1794:53;1839:7;1830:6;1819:9;1815:22;1794:53;:::i;:::-;1784:63;;1739:118;1364:500;;;;;:::o;1870:117::-;1979:1;1976;1969:12;1993:117;2102:1;2099;2092:12;2116:117;2225:1;2222;2215:12;2290:605;2398:8;2408:6;2458:3;2451:4;2443:6;2439:17;2435:27;2425:122;;2466:79;;:::i;:::-;2425:122;2579:6;2566:20;2556:30;;2609:18;2601:6;2598:30;2595:117;;;2631:79;;:::i;:::-;2595:117;2745:4;2737:6;2733:17;2721:29;;2801:3;2791:6;2783;2779:19;2769:8;2765:34;2762:43;2759:130;;;2808:79;;:::i;:::-;2759:130;2290:605;;;;;:::o;2901:122::-;2974:24;2992:5;2974:24;:::i;:::-;2967:5;2964:35;2954:63;;3013:1;3010;3003:12;2954:63;2901:122;:::o;3029:139::-;3075:5;3113:6;3100:20;3091:29;;3129:33;3156:5;3129:33;:::i;:::-;3029:139;;;;:::o;3174:90::-;3208:7;3251:5;3244:13;3237:21;3226:32;;3174:90;;;:::o;3270:116::-;3340:21;3355:5;3340:21;:::i;:::-;3333:5;3330:32;3320:60;;3376:1;3373;3366:12;3320:60;3270:116;:::o;3392:133::-;3435:5;3473:6;3460:20;3451:29;;3489:30;3513:5;3489:30;:::i;:::-;3392:133;;;;:::o;3531:913::-;3667:6;3675;3683;3691;3740:2;3728:9;3719:7;3715:23;3711:32;3708:119;;;3746:79;;:::i;:::-;3708:119;3894:1;3883:9;3879:17;3866:31;3924:18;3916:6;3913:30;3910:117;;;3946:79;;:::i;:::-;3910:117;4059:115;4166:7;4157:6;4146:9;4142:22;4059:115;:::i;:::-;4041:133;;;;3837:347;4223:2;4249:53;4294:7;4285:6;4274:9;4270:22;4249:53;:::i;:::-;4239:63;;4194:118;4351:2;4377:50;4419:7;4410:6;4399:9;4395:22;4377:50;:::i;:::-;4367:60;;4322:115;3531:913;;;;;;;:::o;4463:552::-;4520:8;4530:6;4580:3;4573:4;4565:6;4561:17;4557:27;4547:122;;4588:79;;:::i;:::-;4547:122;4701:6;4688:20;4678:30;;4731:18;4723:6;4720:30;4717:117;;;4753:79;;:::i;:::-;4717:117;4867:4;4859:6;4855:17;4843:29;;4921:3;4913:4;4905:6;4901:17;4891:8;4887:32;4884:41;4881:128;;;4928:79;;:::i;:::-;4881:128;4463:552;;;;;:::o;5021:1255::-;5136:6;5144;5152;5160;5168;5176;5184;5233:3;5221:9;5212:7;5208:23;5204:33;5201:120;;;5240:79;;:::i;:::-;5201:120;5360:1;5385:53;5430:7;5421:6;5410:9;5406:22;5385:53;:::i;:::-;5375:63;;5331:117;5487:2;5513:53;5558:7;5549:6;5538:9;5534:22;5513:53;:::i;:::-;5503:63;;5458:118;5615:2;5641:53;5686:7;5677:6;5666:9;5662:22;5641:53;:::i;:::-;5631:63;;5586:118;5743:2;5769:53;5814:7;5805:6;5794:9;5790:22;5769:53;:::i;:::-;5759:63;;5714:118;5871:3;5898:53;5943:7;5934:6;5923:9;5919:22;5898:53;:::i;:::-;5888:63;;5842:119;6028:3;6017:9;6013:19;6000:33;6060:18;6052:6;6049:30;6046:117;;;6082:79;;:::i;:::-;6046:117;6195:64;6251:7;6242:6;6231:9;6227:22;6195:64;:::i;:::-;6177:82;;;;5971:298;5021:1255;;;;;;;;;;:::o;6282:117::-;6391:1;6388;6381:12;6454:240;6535:5;6576:3;6567:6;6562:3;6558:16;6554:26;6551:113;;;6583:79;;:::i;:::-;6551:113;6682:6;6673:15;;6454:240;;;;:::o;6700:546::-;6803:6;6811;6860:3;6848:9;6839:7;6835:23;6831:33;6828:120;;;6867:79;;:::i;:::-;6828:120;6987:1;7012:88;7092:7;7083:6;7072:9;7068:22;7012:88;:::i;:::-;7002:98;;6958:152;7149:3;7176:53;7221:7;7212:6;7201:9;7197:22;7176:53;:::i;:::-;7166:63;;7120:119;6700:546;;;;;:::o;7252:113::-;7306:7;7335:24;7353:5;7335:24;:::i;:::-;7324:35;;7252:113;;;:::o;7371:156::-;7461:41;7496:5;7461:41;:::i;:::-;7454:5;7451:52;7441:80;;7517:1;7514;7507:12;7441:80;7371:156;:::o;7533:173::-;7596:5;7634:6;7621:20;7612:29;;7650:50;7694:5;7650:50;:::i;:::-;7533:173;;;;:::o;7712:363::-;7788:6;7837:2;7825:9;7816:7;7812:23;7808:32;7805:119;;;7843:79;;:::i;:::-;7805:119;7963:1;7988:70;8050:7;8041:6;8030:9;8026:22;7988:70;:::i;:::-;7978:80;;7934:134;7712:363;;;;:::o;8081:118::-;8168:24;8186:5;8168:24;:::i;:::-;8163:3;8156:37;8081:118;;:::o;8205:222::-;8298:4;8336:2;8325:9;8321:18;8313:26;;8349:71;8417:1;8406:9;8402:17;8393:6;8349:71;:::i;:::-;8205:222;;;;:::o;8433:838::-;8554:6;8562;8570;8578;8627:3;8615:9;8606:7;8602:23;8598:33;8595:120;;;8634:79;;:::i;:::-;8595:120;8754:1;8779:88;8859:7;8850:6;8839:9;8835:22;8779:88;:::i;:::-;8769:98;;8725:152;8916:3;8943:53;8988:7;8979:6;8968:9;8964:22;8943:53;:::i;:::-;8933:63;;8887:119;9045:3;9072:53;9117:7;9108:6;9097:9;9093:22;9072:53;:::i;:::-;9062:63;;9016:119;9174:3;9201:53;9246:7;9237:6;9226:9;9222:22;9201:53;:::i;:::-;9191:63;;9145:119;8433:838;;;;;;;:::o;9292:579::-;9376:8;9386:6;9436:3;9429:4;9421:6;9417:17;9413:27;9403:122;;9444:79;;:::i;:::-;9403:122;9557:6;9544:20;9534:30;;9587:18;9579:6;9576:30;9573:117;;;9609:79;;:::i;:::-;9573:117;9723:4;9715:6;9711:17;9699:29;;9777:3;9769:4;9761:6;9757:17;9747:8;9743:32;9740:41;9737:128;;;9784:79;;:::i;:::-;9737:128;9292:579;;;;;:::o;9877:581::-;9974:6;9982;10031:2;10019:9;10010:7;10006:23;10002:32;9999:119;;;10037:79;;:::i;:::-;9999:119;10185:1;10174:9;10170:17;10157:31;10215:18;10207:6;10204:30;10201:117;;;10237:79;;:::i;:::-;10201:117;10350:91;10433:7;10424:6;10413:9;10409:22;10350:91;:::i;:::-;10332:109;;;;10128:323;9877:581;;;;;:::o;10464:123::-;10540:6;10574:5;10568:12;10558:22;;10464:123;;;:::o;10593:193::-;10701:11;10735:6;10730:3;10723:19;10775:4;10770:3;10766:14;10751:29;;10593:193;;;;:::o;10792:141::-;10868:4;10891:3;10883:11;;10921:4;10916:3;10912:14;10904:22;;10792:141;;;:::o;10939:98::-;10990:6;11024:5;11018:12;11008:22;;10939:98;;;:::o;11043:158::-;11116:11;11150:6;11145:3;11138:19;11190:4;11185:3;11181:14;11166:29;;11043:158;;;;:::o;11207:246::-;11288:1;11298:113;11312:6;11309:1;11306:13;11298:113;;;11397:1;11392:3;11388:11;11382:18;11378:1;11373:3;11369:11;11362:39;11334:2;11331:1;11327:10;11322:15;;11298:113;;;11445:1;11436:6;11431:3;11427:16;11420:27;11269:184;11207:246;;;:::o;11459:102::-;11500:6;11551:2;11547:7;11542:2;11535:5;11531:14;11527:28;11517:38;;11459:102;;;:::o;11567:353::-;11643:3;11671:38;11703:5;11671:38;:::i;:::-;11725:60;11778:6;11773:3;11725:60;:::i;:::-;11718:67;;11794:65;11852:6;11847:3;11840:4;11833:5;11829:16;11794:65;:::i;:::-;11884:29;11906:6;11884:29;:::i;:::-;11879:3;11875:39;11868:46;;11647:273;11567:353;;;;:::o;11926:192::-;12013:10;12048:64;12108:3;12100:6;12048:64;:::i;:::-;12034:78;;11926:192;;;;:::o;12124:122::-;12203:4;12235;12230:3;12226:14;12218:22;;12124:122;;;:::o;12278:983::-;12415:3;12444:63;12501:5;12444:63;:::i;:::-;12523:95;12611:6;12606:3;12523:95;:::i;:::-;12516:102;;12644:3;12689:4;12681:6;12677:17;12672:3;12668:27;12719:65;12778:5;12719:65;:::i;:::-;12807:7;12838:1;12823:393;12848:6;12845:1;12842:13;12823:393;;;12919:9;12913:4;12909:20;12904:3;12897:33;12970:6;12964:13;12998:82;13075:4;13060:13;12998:82;:::i;:::-;12990:90;;13103:69;13165:6;13103:69;:::i;:::-;13093:79;;13201:4;13196:3;13192:14;13185:21;;12883:333;12870:1;12867;12863:9;12858:14;;12823:393;;;12827:14;13232:4;13225:11;;13252:3;13245:10;;12420:841;;;;;12278:983;;;;:::o;13267:409::-;13428:4;13466:2;13455:9;13451:18;13443:26;;13515:9;13509:4;13505:20;13501:1;13490:9;13486:17;13479:47;13543:126;13664:4;13655:6;13543:126;:::i;:::-;13535:134;;13267:409;;;;:::o;13682:1205::-;13836:6;13844;13852;13860;13868;13876;13925:3;13913:9;13904:7;13900:23;13896:33;13893:120;;;13932:79;;:::i;:::-;13893:120;14080:1;14069:9;14065:17;14052:31;14110:18;14102:6;14099:30;14096:117;;;14132:79;;:::i;:::-;14096:117;14245:115;14352:7;14343:6;14332:9;14328:22;14245:115;:::i;:::-;14227:133;;;;14023:347;14409:2;14435:53;14480:7;14471:6;14460:9;14456:22;14435:53;:::i;:::-;14425:63;;14380:118;14537:2;14563:53;14608:7;14599:6;14588:9;14584:22;14563:53;:::i;:::-;14553:63;;14508:118;14665:2;14691:53;14736:7;14727:6;14716:9;14712:22;14691:53;:::i;:::-;14681:63;;14636:118;14793:3;14820:50;14862:7;14853:6;14842:9;14838:22;14820:50;:::i;:::-;14810:60;;14764:116;13682:1205;;;;;;;;:::o;14893:60::-;14921:3;14942:5;14935:12;;14893:60;;;:::o;14959:142::-;15009:9;15042:53;15060:34;15069:24;15087:5;15069:24;:::i;:::-;15060:34;:::i;:::-;15042:53;:::i;:::-;15029:66;;14959:142;;;:::o;15107:126::-;15157:9;15190:37;15221:5;15190:37;:::i;:::-;15177:50;;15107:126;;;:::o;15239:143::-;15306:9;15339:37;15370:5;15339:37;:::i;:::-;15326:50;;15239:143;;;:::o;15388:165::-;15492:54;15540:5;15492:54;:::i;:::-;15487:3;15480:67;15388:165;;:::o;15559:256::-;15669:4;15707:2;15696:9;15692:18;15684:26;;15720:88;15805:1;15794:9;15790:17;15781:6;15720:88;:::i;:::-;15559:256;;;;:::o;15821:329::-;15880:6;15929:2;15917:9;15908:7;15904:23;15900:32;15897:119;;;15935:79;;:::i;:::-;15897:119;16055:1;16080:53;16125:7;16116:6;16105:9;16101:22;16080:53;:::i;:::-;16070:63;;16026:117;15821:329;;;;:::o;16156:169::-;16240:11;16274:6;16269:3;16262:19;16314:4;16309:3;16305:14;16290:29;;16156:169;;;;:::o;16331:162::-;16471:14;16467:1;16459:6;16455:14;16448:38;16331:162;:::o;16499:366::-;16641:3;16662:67;16726:2;16721:3;16662:67;:::i;:::-;16655:74;;16738:93;16827:3;16738:93;:::i;:::-;16856:2;16851:3;16847:12;16840:19;;16499:366;;;:::o;16871:419::-;17037:4;17075:2;17064:9;17060:18;17052:26;;17124:9;17118:4;17114:20;17110:1;17099:9;17095:17;17088:47;17152:131;17278:4;17152:131;:::i;:::-;17144:139;;16871:419;;;:::o;17296:143::-;17353:5;17384:6;17378:13;17369:22;;17400:33;17427:5;17400:33;:::i;:::-;17296:143;;;;:::o;17445:351::-;17515:6;17564:2;17552:9;17543:7;17539:23;17535:32;17532:119;;;17570:79;;:::i;:::-;17532:119;17690:1;17715:64;17771:7;17762:6;17751:9;17747:22;17715:64;:::i;:::-;17705:74;;17661:128;17445:351;;;;:::o;17802:180::-;17850:77;17847:1;17840:88;17947:4;17944:1;17937:15;17971:4;17968:1;17961:15;17988:119;18075:1;18068:5;18065:12;18055:46;;18081:18;;:::i;:::-;18055:46;17988:119;:::o;18113:139::-;18164:7;18193:5;18182:16;;18199:47;18240:5;18199:47;:::i;:::-;18113:139;;;:::o;18258:::-;18320:9;18353:38;18385:5;18353:38;:::i;:::-;18340:51;;18258:139;;;:::o;18403:155::-;18502:49;18545:5;18502:49;:::i;:::-;18497:3;18490:62;18403:155;;:::o;18564:109::-;18645:21;18660:5;18645:21;:::i;:::-;18640:3;18633:34;18564:109;;:::o;18679:664::-;18884:4;18922:3;18911:9;18907:19;18899:27;;18936:83;19016:1;19005:9;19001:17;18992:6;18936:83;:::i;:::-;19029:72;19097:2;19086:9;19082:18;19073:6;19029:72;:::i;:::-;19111;19179:2;19168:9;19164:18;19155:6;19111:72;:::i;:::-;19193:66;19255:2;19244:9;19240:18;19231:6;19193:66;:::i;:::-;19269:67;19331:3;19320:9;19316:19;19307:6;19269:67;:::i;:::-;18679:664;;;;;;;;:::o;19349:217::-;19481:11;19515:6;19510:3;19503:19;19555:4;19550:3;19546:14;19531:29;;19349:217;;;;:::o;19572:137::-;19676:4;19699:3;19691:11;;19572:137;;;:::o;19715:109::-;19751:7;19791:26;19784:5;19780:38;19769:49;;19715:109;;;:::o;19830:120::-;19902:23;19919:5;19902:23;:::i;:::-;19895:5;19892:34;19882:62;;19940:1;19937;19930:12;19882:62;19830:120;:::o;19956:137::-;20001:5;20039:6;20026:20;20017:29;;20055:32;20081:5;20055:32;:::i;:::-;19956:137;;;;:::o;20099:120::-;20150:5;20175:38;20209:2;20204:3;20200:12;20195:3;20175:38;:::i;:::-;20166:47;;20099:120;;;;:::o;20225:105::-;20300:23;20317:5;20300:23;:::i;:::-;20295:3;20288:36;20225:105;;:::o;20336:122::-;20388:5;20413:39;20448:2;20443:3;20439:12;20434:3;20413:39;:::i;:::-;20404:48;;20336:122;;;;:::o;20464:108::-;20541:24;20559:5;20541:24;:::i;:::-;20536:3;20529:37;20464:108;;:::o;20578:118::-;20615:7;20655:34;20648:5;20644:46;20633:57;;20578:118;;;:::o;20702:122::-;20775:24;20793:5;20775:24;:::i;:::-;20768:5;20765:35;20755:63;;20814:1;20811;20804:12;20755:63;20702:122;:::o;20830:139::-;20876:5;20914:6;20901:20;20892:29;;20930:33;20957:5;20930:33;:::i;:::-;20830:139;;;;:::o;20975:122::-;21027:5;21052:39;21087:2;21082:3;21078:12;21073:3;21052:39;:::i;:::-;21043:48;;20975:122;;;;:::o;21103:108::-;21180:24;21198:5;21180:24;:::i;:::-;21175:3;21168:37;21103:108;;:::o;21217:95::-;21253:7;21293:12;21286:5;21282:24;21271:35;;21217:95;;;:::o;21318:120::-;21390:23;21407:5;21390:23;:::i;:::-;21383:5;21380:34;21370:62;;21428:1;21425;21418:12;21370:62;21318:120;:::o;21444:137::-;21489:5;21527:6;21514:20;21505:29;;21543:32;21569:5;21543:32;:::i;:::-;21444:137;;;;:::o;21587:120::-;21638:5;21663:38;21697:2;21692:3;21688:12;21683:3;21663:38;:::i;:::-;21654:47;;21587:120;;;;:::o;21713:105::-;21788:23;21805:5;21788:23;:::i;:::-;21783:3;21776:36;21713:105;;:::o;21824:91::-;21860:7;21900:8;21893:5;21889:20;21878:31;;21824:91;;;:::o;21921:120::-;21993:23;22010:5;21993:23;:::i;:::-;21986:5;21983:34;21973:62;;22031:1;22028;22021:12;21973:62;21921:120;:::o;22047:137::-;22092:5;22130:6;22117:20;22108:29;;22146:32;22172:5;22146:32;:::i;:::-;22047:137;;;;:::o;22190:120::-;22241:5;22266:38;22300:2;22295:3;22291:12;22286:3;22266:38;:::i;:::-;22257:47;;22190:120;;;;:::o;22316:105::-;22391:23;22408:5;22391:23;:::i;:::-;22386:3;22379:36;22316:105;;:::o;22521:1824::-;22676:6;22671:3;22667:16;22749:49;22792:4;22785:5;22781:16;22774:5;22749:49;:::i;:::-;22811:61;22866:4;22861:3;22857:14;22843:12;22811:61;:::i;:::-;22693:189;22947:50;22991:4;22984:5;22980:16;22973:5;22947:50;:::i;:::-;23010:63;23067:4;23062:3;23058:14;23044:12;23010:63;:::i;:::-;22892:191;23152:50;23196:4;23189:5;23185:16;23178:5;23152:50;:::i;:::-;23215:63;23272:4;23267:3;23263:14;23249:12;23215:63;:::i;:::-;23093:195;23363:50;23407:4;23400:5;23396:16;23389:5;23363:50;:::i;:::-;23426:63;23483:4;23478:3;23474:14;23460:12;23426:63;:::i;:::-;23298:201;23574:50;23618:4;23611:5;23607:16;23600:5;23574:50;:::i;:::-;23637:63;23694:4;23689:3;23685:14;23671:12;23637:63;:::i;:::-;23509:201;23783:49;23826:4;23819:5;23815:16;23808:5;23783:49;:::i;:::-;23845:61;23900:4;23895:3;23891:14;23877:12;23845:61;:::i;:::-;23720:196;23994:49;24037:4;24030:5;24026:16;24019:5;23994:49;:::i;:::-;24056:61;24111:4;24106:3;24102:14;24088:12;24056:61;:::i;:::-;23926:201;24205:49;24248:4;24241:5;24237:16;24230:5;24205:49;:::i;:::-;24267:61;24322:4;24317:3;24313:14;24299:12;24267:61;:::i;:::-;24137:201;22645:1700;22521:1824;;:::o;24351:317::-;24488:10;24509:114;24619:3;24611:6;24509:114;:::i;:::-;24655:6;24650:3;24646:16;24632:30;;24351:317;;;;:::o;24674:121::-;24761:5;24786:3;24777:12;;24674:121;;;;:::o;24801:152::-;24908:4;24940:6;24935:3;24931:16;24923:24;;24801:152;;;:::o;25057:973::-;25254:3;25277:119;25389:6;25384:3;25277:119;:::i;:::-;25270:126;;25420:93;25507:5;25420:93;:::i;:::-;25536:7;25567:1;25552:453;25577:6;25574:1;25571:13;25552:453;;;25647:77;25717:6;25708:7;25647:77;:::i;:::-;25744:131;25871:3;25856:13;25744:131;:::i;:::-;25737:138;;25898:97;25988:6;25898:97;:::i;:::-;25888:107;;25612:393;25599:1;25596;25592:9;25587:14;;25552:453;;;25556:14;26021:3;26014:10;;25259:771;;25057:973;;;;;:::o;26036:168::-;26119:11;26153:6;26148:3;26141:19;26193:4;26188:3;26184:14;26169:29;;26036:168;;;;:::o;26210:373::-;26296:3;26324:38;26356:5;26324:38;:::i;:::-;26378:70;26441:6;26436:3;26378:70;:::i;:::-;26371:77;;26457:65;26515:6;26510:3;26503:4;26496:5;26492:16;26457:65;:::i;:::-;26547:29;26569:6;26547:29;:::i;:::-;26542:3;26538:39;26531:46;;26300:283;26210:373;;;;:::o;26589:836::-;26884:4;26922:2;26911:9;26907:18;26899:26;;26971:9;26965:4;26961:20;26957:1;26946:9;26942:17;26935:47;26999:186;27180:4;27171:6;27163;26999:186;:::i;:::-;26991:194;;27232:9;27226:4;27222:20;27217:2;27206:9;27202:18;27195:48;27260:76;27331:4;27322:6;27260:76;:::i;:::-;27252:84;;27346:72;27414:2;27403:9;27399:18;27390:6;27346:72;:::i;:::-;26589:836;;;;;;;:::o;27431:113::-;27518:1;27511:5;27508:12;27498:40;;27534:1;27531;27524:12;27498:40;27431:113;:::o;27550:167::-;27610:5;27648:6;27635:20;27626:29;;27664:47;27705:5;27664:47;:::i;:::-;27550:167;;;;:::o;27723:357::-;27796:6;27845:2;27833:9;27824:7;27820:23;27816:32;27813:119;;;27851:79;;:::i;:::-;27813:119;27971:1;27996:67;28055:7;28046:6;28035:9;28031:22;27996:67;:::i;:::-;27986:77;;27942:131;27723:357;;;;:::o;28086:180::-;28134:77;28131:1;28124:88;28231:4;28228:1;28221:15;28255:4;28252:1;28245:15;28272:117;28381:1;28378;28371:12;28395:281;28478:27;28500:4;28478:27;:::i;:::-;28470:6;28466:40;28608:6;28596:10;28593:22;28572:18;28560:10;28557:34;28554:62;28551:88;;;28619:18;;:::i;:::-;28551:88;28659:10;28655:2;28648:22;28438:238;28395:281;;:::o;28682:129::-;28716:6;28743:20;;:::i;:::-;28733:30;;28772:33;28800:4;28792:6;28772:33;:::i;:::-;28682:129;;;:::o;28989:1611::-;29071:5;29115:6;29103:9;29098:3;29094:19;29090:32;29087:119;;;29125:79;;:::i;:::-;29087:119;29224:23;29240:6;29224:23;:::i;:::-;29215:32;;29307:1;29347:48;29391:3;29382:6;29371:9;29367:22;29347:48;:::i;:::-;29340:4;29333:5;29329:16;29322:74;29257:150;29466:2;29507:49;29552:3;29543:6;29532:9;29528:22;29507:49;:::i;:::-;29500:4;29493:5;29489:16;29482:75;29417:151;29631:2;29672:49;29717:3;29708:6;29697:9;29693:22;29672:49;:::i;:::-;29665:4;29658:5;29654:16;29647:75;29578:155;29802:2;29843:49;29888:3;29879:6;29868:9;29864:22;29843:49;:::i;:::-;29836:4;29829:5;29825:16;29818:75;29743:161;29973:3;30015:49;30060:3;30051:6;30040:9;30036:22;30015:49;:::i;:::-;30008:4;30001:5;29997:16;29990:75;29914:162;30143:3;30185:48;30229:3;30220:6;30209:9;30205:22;30185:48;:::i;:::-;30178:4;30171:5;30167:16;30160:74;30086:159;30317:3;30359:48;30403:3;30394:6;30383:9;30379:22;30359:48;:::i;:::-;30352:4;30345:5;30341:16;30334:74;30255:164;30491:3;30533:48;30577:3;30568:6;30557:9;30553:22;30533:48;:::i;:::-;30526:4;30519:5;30515:16;30508:74;30429:164;28989:1611;;;;:::o;30606:396::-;30698:6;30747:3;30735:9;30726:7;30722:23;30718:33;30715:120;;;30754:79;;:::i;:::-;30715:120;30874:1;30899:86;30977:7;30968:6;30957:9;30953:22;30899:86;:::i;:::-;30889:96;;30845:150;30606:396;;;;:::o;31008:180::-;31056:77;31053:1;31046:88;31153:4;31150:1;31143:15;31177:4;31174:1;31167:15;31194:147;31294:6;31328:5;31322:12;31312:22;;31194:147;;;:::o;31347:165::-;31447:4;31470:3;31462:11;;31500:4;31495:3;31491:14;31483:22;;31347:165;;;:::o;31612:1610::-;31765:6;31760:3;31756:16;31855:4;31848:5;31844:16;31838:23;31874:61;31929:4;31924:3;31920:14;31906:12;31874:61;:::i;:::-;31782:163;32027:4;32020:5;32016:16;32010:23;32046:63;32103:4;32098:3;32094:14;32080:12;32046:63;:::i;:::-;31955:164;32205:4;32198:5;32194:16;32188:23;32224:63;32281:4;32276:3;32272:14;32258:12;32224:63;:::i;:::-;32129:168;32389:4;32382:5;32378:16;32372:23;32408:63;32465:4;32460:3;32456:14;32442:12;32408:63;:::i;:::-;32307:174;32573:4;32566:5;32562:16;32556:23;32592:63;32649:4;32644:3;32640:14;32626:12;32592:63;:::i;:::-;32491:174;32755:4;32748:5;32744:16;32738:23;32774:61;32829:4;32824:3;32820:14;32806:12;32774:61;:::i;:::-;32675:170;32940:4;32933:5;32929:16;32923:23;32959:61;33014:4;33009:3;33005:14;32991:12;32959:61;:::i;:::-;32855:175;33125:4;33118:5;33114:16;33108:23;33144:61;33199:4;33194:3;33190:14;33176:12;33144:61;:::i;:::-;33040:175;31734:1488;31612:1610;;:::o;33228:313::-;33363:10;33384:112;33492:3;33484:6;33384:112;:::i;:::-;33528:6;33523:3;33519:16;33505:30;;33228:313;;;;:::o;33547:146::-;33650:4;33682;33677:3;33673:14;33665:22;;33547:146;;;:::o;33797:996::-;33982:3;34011:87;34092:5;34011:87;:::i;:::-;34114:119;34226:6;34221:3;34114:119;:::i;:::-;34107:126;;34257:89;34340:5;34257:89;:::i;:::-;34369:7;34400:1;34385:383;34410:6;34407:1;34404:13;34385:383;;;34486:6;34480:13;34513:129;34638:3;34623:13;34513:129;:::i;:::-;34506:136;;34665:93;34751:6;34665:93;:::i;:::-;34655:103;;34445:323;34432:1;34429;34425:9;34420:14;;34385:383;;;34389:14;34784:3;34777:10;;33987:806;;;33797:996;;;;:::o;34799:812::-;35082:4;35120:2;35109:9;35105:18;35097:26;;35169:9;35163:4;35159:20;35155:1;35144:9;35140:17;35133:47;35197:174;35366:4;35357:6;35197:174;:::i;:::-;35189:182;;35418:9;35412:4;35408:20;35403:2;35392:9;35388:18;35381:48;35446:76;35517:4;35508:6;35446:76;:::i;:::-;35438:84;;35532:72;35600:2;35589:9;35585:18;35576:6;35532:72;:::i;:::-;34799:812;;;;;;:::o;35617:149::-;35653:7;35693:66;35686:5;35682:78;35671:89;;35617:149;;;:::o;35772:115::-;35857:23;35874:5;35857:23;:::i;:::-;35852:3;35845:36;35772:115;;:::o;35893:438::-;36040:4;36078:2;36067:9;36063:18;36055:26;;36091:71;36159:1;36148:9;36144:17;36135:6;36091:71;:::i;:::-;36172:72;36240:2;36229:9;36225:18;36216:6;36172:72;:::i;:::-;36254:70;36320:2;36309:9;36305:18;36296:6;36254:70;:::i;:::-;35893:438;;;;;;:::o;36337:137::-;36391:5;36422:6;36416:13;36407:22;;36438:30;36462:5;36438:30;:::i;:::-;36337:137;;;;:::o;36480:345::-;36547:6;36596:2;36584:9;36575:7;36571:23;36567:32;36564:119;;;36602:79;;:::i;:::-;36564:119;36722:1;36747:61;36800:7;36791:6;36780:9;36776:22;36747:61;:::i;:::-;36737:71;;36693:125;36480:345;;;;:::o;36831:886::-;37092:4;37130:3;37119:9;37115:19;37107:27;;37144:83;37224:1;37213:9;37209:17;37200:6;37144:83;:::i;:::-;37237:72;37305:2;37294:9;37290:18;37281:6;37237:72;:::i;:::-;37319;37387:2;37376:9;37372:18;37363:6;37319:72;:::i;:::-;37401;37469:2;37458:9;37454:18;37445:6;37401:72;:::i;:::-;37483:73;37551:3;37540:9;37536:19;37527:6;37483:73;:::i;:::-;37566:67;37628:3;37617:9;37613:19;37604:6;37566:67;:::i;:::-;37643;37705:3;37694:9;37690:19;37681:6;37643:67;:::i;:::-;36831:886;;;;;;;;;;:::o;37723:180::-;37771:77;37768:1;37761:88;37868:4;37865:1;37858:15;37892:4;37889:1;37882:15;37909:194;37949:4;37969:20;37987:1;37969:20;:::i;:::-;37964:25;;38003:20;38021:1;38003:20;:::i;:::-;37998:25;;38047:1;38044;38040:9;38032:17;;38071:1;38065:4;38062:11;38059:37;;;38076:18;;:::i;:::-;38059:37;37909:194;;;;:::o;38109:117::-;38218:1;38215;38208:12;38232:117;38341:1;38338;38331:12;38355:469;38460:9;38471;38509:8;38497:10;38494:24;38491:111;;;38521:79;;:::i;:::-;38491:111;38627:6;38617:8;38614:20;38611:107;;;38637:79;;:::i;:::-;38611:107;38768:1;38756:10;38752:18;38744:6;38740:31;38727:44;;38807:10;38797:8;38793:25;38780:38;;38355:469;;;;;;;:::o;38830:117::-;38939:1;38936;38929:12;38953:117;39062:1;39059;39052:12;39076:117;39185:1;39182;39175:12;39199:724;39276:4;39282:6;39338:11;39325:25;39438:1;39432:4;39428:12;39417:8;39401:14;39397:29;39393:48;39373:18;39369:73;39359:168;;39446:79;;:::i;:::-;39359:168;39558:18;39548:8;39544:33;39536:41;;39610:4;39597:18;39587:28;;39638:18;39630:6;39627:30;39624:117;;;39660:79;;:::i;:::-;39624:117;39768:2;39762:4;39758:13;39750:21;;39825:4;39817:6;39813:17;39797:14;39793:38;39787:4;39783:49;39780:136;;;39835:79;;:::i;:::-;39780:136;39289:634;39199:724;;;;;:::o;39929:147::-;40030:11;40067:3;40052:18;;39929:147;;;;:::o;40082:146::-;40179:6;40174:3;40169;40156:30;40220:1;40211:6;40206:3;40202:16;40195:27;40082:146;;;:::o;40256:327::-;40370:3;40391:88;40472:6;40467:3;40391:88;:::i;:::-;40384:95;;40489:56;40538:6;40533:3;40526:5;40489:56;:::i;:::-;40570:6;40565:3;40561:16;40554:23;;40256:327;;;;;:::o;40589:386::-;40693:3;40721:38;40753:5;40721:38;:::i;:::-;40775:88;40856:6;40851:3;40775:88;:::i;:::-;40768:95;;40872:65;40930:6;40925:3;40918:4;40911:5;40907:16;40872:65;:::i;:::-;40962:6;40957:3;40953:16;40946:23;;40697:278;40589:386;;;;:::o;40981:447::-;41167:3;41189:103;41288:3;41279:6;41271;41189:103;:::i;:::-;41182:110;;41309:93;41398:3;41389:6;41309:93;:::i;:::-;41302:100;;41419:3;41412:10;;40981:447;;;;;;:::o;41434:233::-;41473:3;41496:24;41514:5;41496:24;:::i;:::-;41487:33;;41542:66;41535:5;41532:77;41529:103;;41612:18;;:::i;:::-;41529:103;41659:1;41652:5;41648:13;41641:20;;41434:233;;;:::o;41673:165::-;41813:17;41809:1;41801:6;41797:14;41790:41;41673:165;:::o;41844:366::-;41986:3;42007:67;42071:2;42066:3;42007:67;:::i;:::-;42000:74;;42083:93;42172:3;42083:93;:::i;:::-;42201:2;42196:3;42192:12;42185:19;;41844:366;;;:::o;42216:419::-;42382:4;42420:2;42409:9;42405:18;42397:26;;42469:9;42463:4;42459:20;42455:1;42444:9;42440:17;42433:47;42497:131;42623:4;42497:131;:::i;:::-;42489:139;;42216:419;;;:::o;42641:104::-;42686:7;42715:24;42733:5;42715:24;:::i;:::-;42704:35;;42641:104;;;:::o;42751:138::-;42832:32;42858:5;42832:32;:::i;:::-;42825:5;42822:43;42812:71;;42879:1;42876;42869:12;42812:71;42751:138;:::o;42895:155::-;42949:5;42987:6;42974:20;42965:29;;43003:41;43038:5;43003:41;:::i;:::-;42895:155;;;;:::o;43056:132::-;43129:7;43158:24;43176:5;43158:24;:::i;:::-;43147:35;;43056:132;;;:::o;43194:194::-;43303:60;43357:5;43303:60;:::i;:::-;43296:5;43293:71;43283:99;;43378:1;43375;43368:12;43283:99;43194:194;:::o;43394:211::-;43476:5;43514:6;43501:20;43492:29;;43530:69;43593:5;43530:69;:::i;:::-;43394:211;;;;:::o;43611:1015::-;43758:6;43766;43774;43782;43790;43839:3;43827:9;43818:7;43814:23;43810:33;43807:120;;;43846:79;;:::i;:::-;43807:120;43966:1;43991:67;44050:7;44041:6;44030:9;44026:22;43991:67;:::i;:::-;43981:77;;43937:131;44107:2;44133:61;44186:7;44177:6;44166:9;44162:22;44133:61;:::i;:::-;44123:71;;44078:126;44243:2;44269:89;44350:7;44341:6;44330:9;44326:22;44269:89;:::i;:::-;44259:99;;44214:154;44407:2;44433:50;44475:7;44466:6;44455:9;44451:22;44433:50;:::i;:::-;44423:60;;44378:115;44532:3;44559:50;44601:7;44592:6;44581:9;44577:22;44559:50;:::i;:::-;44549:60;;44503:116;43611:1015;;;;;;;;:::o;44632:124::-;44689:7;44718:32;44744:5;44718:32;:::i;:::-;44707:43;;44632:124;;;:::o;44762:162::-;44855:44;44893:5;44855:44;:::i;:::-;44848:5;44845:55;44835:83;;44914:1;44911;44904:12;44835:83;44762:162;:::o;44930:183::-;45007:5;45038:6;45032:13;45023:22;;45054:53;45101:5;45054:53;:::i;:::-;44930:183;;;;:::o;45119:391::-;45209:6;45258:2;45246:9;45237:7;45233:23;45229:32;45226:119;;;45264:79;;:::i;:::-;45226:119;45384:1;45409:84;45485:7;45476:6;45465:9;45461:22;45409:84;:::i;:::-;45399:94;;45355:148;45119:391;;;;:::o;45516:332::-;45637:4;45675:2;45664:9;45660:18;45652:26;;45688:71;45756:1;45745:9;45741:17;45732:6;45688:71;:::i;:::-;45769:72;45837:2;45826:9;45822:18;45813:6;45769:72;:::i;:::-;45516:332;;;;;:::o;45854:139::-;45917:9;45950:37;45981:5;45950:37;:::i;:::-;45937:50;;45854:139;;;:::o;45999:157::-;46099:50;46143:5;46099:50;:::i;:::-;46094:3;46087:63;45999:157;;:::o;46162:118::-;46249:24;46267:5;46249:24;:::i;:::-;46244:3;46237:37;46162:118;;:::o;46286:85::-;46331:7;46360:5;46349:16;;46286:85;;;:::o;46377:158::-;46435:9;46468:61;46486:42;46495:32;46521:5;46495:32;:::i;:::-;46486:42;:::i;:::-;46468:61;:::i;:::-;46455:74;;46377:158;;;:::o;46541:147::-;46636:45;46675:5;46636:45;:::i;:::-;46631:3;46624:58;46541:147;;:::o;46694:595::-;46892:4;46930:3;46919:9;46915:19;46907:27;;46944:84;47025:1;47014:9;47010:17;47001:6;46944:84;:::i;:::-;47038:72;47106:2;47095:9;47091:18;47082:6;47038:72;:::i;:::-;47120:80;47196:2;47185:9;47181:18;47172:6;47120:80;:::i;:::-;47210:72;47278:2;47267:9;47263:18;47254:6;47210:72;:::i;:::-;46694:595;;;;;;;:::o;47295:222::-;47388:4;47426:2;47415:9;47411:18;47403:26;;47439:71;47507:1;47496:9;47492:17;47483:6;47439:71;:::i;:::-;47295:222;;;;:::o;47523:332::-;47644:4;47682:2;47671:9;47667:18;47659:26;;47695:71;47763:1;47752:9;47748:17;47739:6;47695:71;:::i;:::-;47776:72;47844:2;47833:9;47829:18;47820:6;47776:72;:::i;:::-;47523:332;;;;;:::o;47861:1405::-;48075:6;48083;48091;48099;48107;48115;48123;48172:3;48160:9;48151:7;48147:23;48143:33;48140:120;;;48179:79;;:::i;:::-;48140:120;48299:1;48324:67;48383:7;48374:6;48363:9;48359:22;48324:67;:::i;:::-;48314:77;;48270:131;48440:2;48466:61;48519:7;48510:6;48499:9;48495:22;48466:61;:::i;:::-;48456:71;;48411:126;48576:2;48602:89;48683:7;48674:6;48663:9;48659:22;48602:89;:::i;:::-;48592:99;;48547:154;48740:2;48766:89;48847:7;48838:6;48827:9;48823:22;48766:89;:::i;:::-;48756:99;;48711:154;48904:3;48931:66;48989:7;48980:6;48969:9;48965:22;48931:66;:::i;:::-;48921:76;;48875:132;49046:3;49073:50;49115:7;49106:6;49095:9;49091:22;49073:50;:::i;:::-;49063:60;;49017:116;49172:3;49199:50;49241:7;49232:6;49221:9;49217:22;49199:50;:::i;:::-;49189:60;;49143:116;47861:1405;;;;;;;;;;:::o;49272:132::-;49345:7;49374:24;49392:5;49374:24;:::i;:::-;49363:35;;49272:132;;;:::o;49410:194::-;49519:60;49573:5;49519:60;:::i;:::-;49512:5;49509:71;49499:99;;49594:1;49591;49584:12;49499:99;49410:194;:::o;49610:215::-;49703:5;49734:6;49728:13;49719:22;;49750:69;49813:5;49750:69;:::i;:::-;49610:215;;;;:::o;49831:423::-;49937:6;49986:2;49974:9;49965:7;49961:23;49957:32;49954:119;;;49992:79;;:::i;:::-;49954:119;50112:1;50137:100;50229:7;50220:6;50209:9;50205:22;50137:100;:::i;:::-;50127:110;;50083:164;49831:423;;;;:::o;50260:248::-;50366:4;50404:2;50393:9;50389:18;50381:26;;50417:84;50498:1;50487:9;50483:17;50474:6;50417:84;:::i;:::-;50260:248;;;;:::o;50514:579::-;50704:4;50742:3;50731:9;50727:19;50719:27;;50756:84;50837:1;50826:9;50822:17;50813:6;50756:84;:::i;:::-;50850:72;50918:2;50907:9;50903:18;50894:6;50850:72;:::i;:::-;50932;51000:2;50989:9;50985:18;50976:6;50932:72;:::i;:::-;51014;51082:2;51071:9;51067:18;51058:6;51014:72;:::i;:::-;50514:579;;;;;;;:::o;51099:271::-;51229:3;51251:93;51340:3;51331:6;51251:93;:::i;:::-;51244:100;;51361:3;51354:10;;51099:271;;;;:::o;51376:170::-;51516:22;51512:1;51504:6;51500:14;51493:46;51376:170;:::o;51552:366::-;51694:3;51715:67;51779:2;51774:3;51715:67;:::i;:::-;51708:74;;51791:93;51880:3;51791:93;:::i;:::-;51909:2;51904:3;51900:12;51893:19;;51552:366;;;:::o;51924:419::-;52090:4;52128:2;52117:9;52113:18;52105:26;;52177:9;52171:4;52167:20;52163:1;52152:9;52148:17;52141:47;52205:131;52331:4;52205:131;:::i;:::-;52197:139;;51924:419;;;:::o;52349:164::-;52489:16;52485:1;52477:6;52473:14;52466:40;52349:164;:::o;52519:366::-;52661:3;52682:67;52746:2;52741:3;52682:67;:::i;:::-;52675:74;;52758:93;52847:3;52758:93;:::i;:::-;52876:2;52871:3;52867:12;52860:19;;52519:366;;;:::o;52891:419::-;53057:4;53095:2;53084:9;53080:18;53072:26;;53144:9;53138:4;53134:20;53130:1;53119:9;53115:17;53108:47;53172:131;53298:4;53172:131;:::i;:::-;53164:139;;52891:419;;;:::o
Swarm Source
ipfs://d8534abbe2f413284b7937559a4ea2f330b89f940c4e92d6bc54b1ee3ff69396
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 34 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.