Source Code
More Info
Private Name Tags
ContractCreator
TokenTracker
Latest 7 from a total of 7 transactions
| Transaction Hash |
Method
|
Block
|
From
|
|
To
|
||||
|---|---|---|---|---|---|---|---|---|---|
| Approve | 23121025 | 178 days ago | IN | 0 ETH | 0.00010841 | ||||
| Approve | 23119713 | 178 days ago | IN | 0 ETH | 0.00013347 | ||||
| Approve | 23117059 | 179 days ago | IN | 0 ETH | 0.0000224 | ||||
| Approve | 23117022 | 179 days ago | IN | 0 ETH | 0.00011504 | ||||
| Approve | 23116911 | 179 days ago | IN | 0 ETH | 0.00001833 | ||||
| Approve | 23098635 | 181 days ago | IN | 0 ETH | 0.00012361 | ||||
| Approve | 23092130 | 182 days ago | IN | 0 ETH | 0.00010937 |
Latest 1 internal transaction
Advanced mode:
| Parent Transaction Hash | Method | Block |
From
|
|
To
|
||
|---|---|---|---|---|---|---|---|
| 0x60c08060 | 22976751 | 198 days ago | Contract Creation | 0 ETH |
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
Contract Name:
MultiDepositorVault
Compiler Version
v0.8.29+commit.ab55807c
Optimization Enabled:
Yes with 100000 runs
Other Settings:
cancun EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.29;
import { IERC20 } from "@oz/interfaces/IERC20.sol";
import { ERC20 } from "@oz/token/ERC20/ERC20.sol";
import { SafeERC20 } from "@oz/token/ERC20/utils/SafeERC20.sol";
import { FeeVault } from "src/core/FeeVault.sol";
import { IBeforeTransferHook } from "src/core/interfaces/IBeforeTransferHook.sol";
import { IMultiDepositorVault } from "src/core/interfaces/IMultiDepositorVault.sol";
import { IMultiDepositorVaultFactory } from "src/core/interfaces/IMultiDepositorVaultFactory.sol";
import { IProvisioner } from "src/core/interfaces/IProvisioner.sol";
/// @title MultiDepositorVault
/// @notice A vault that allows users to deposit and withdraw multiple tokens. This contract just mints and burns unit
/// tokens and all logic and validation is handled by the provisioner
contract MultiDepositorVault is IMultiDepositorVault, ERC20, FeeVault {
using SafeERC20 for IERC20;
////////////////////////////////////////////////////////////
// Storage //
////////////////////////////////////////////////////////////
/// @notice Hooks contract called before unit transfers/mints/burns
IBeforeTransferHook public beforeTransferHook;
/// @notice Role that can mint/burn vault units
address public provisioner;
/// @notice Ensures caller is the provisioner
modifier onlyProvisioner() {
// Requirements: check that the caller is the provisioner
require(msg.sender == provisioner, Aera__CallerIsNotProvisioner());
_;
}
////////////////////////////////////////////////////////////
// Constructor //
////////////////////////////////////////////////////////////
constructor()
ERC20(
IMultiDepositorVaultFactory(msg.sender).getERC20Name(),
IMultiDepositorVaultFactory(msg.sender).getERC20Symbol()
)
FeeVault()
{
// Interactions: get the before transfer hook contract
IBeforeTransferHook beforeTransferHook_ =
IMultiDepositorVaultFactory(msg.sender).multiDepositorVaultParameters();
// Effects: set the before transfer hook contract
_setBeforeTransferHook(beforeTransferHook_);
}
////////////////////////////////////////////////////////////
// Public / External Functions //
////////////////////////////////////////////////////////////
/// @inheritdoc IMultiDepositorVault
function enter(address sender, IERC20 token, uint256 tokenAmount, uint256 unitsAmount, address recipient)
external
whenNotPaused
onlyProvisioner
{
// Interactions: pull tokens from the sender
if (tokenAmount > 0) token.safeTransferFrom(sender, address(this), tokenAmount);
// Effects: mint units to the recipient
_mint(recipient, unitsAmount);
// Log the enter event
emit Enter(sender, recipient, token, tokenAmount, unitsAmount);
}
/// @inheritdoc IMultiDepositorVault
function exit(address sender, IERC20 token, uint256 tokenAmount, uint256 unitsAmount, address recipient)
external
whenNotPaused
onlyProvisioner
{
// Effects: burn units from the sender
_burn(sender, unitsAmount);
// Interactions: transfer tokens to the recipient
if (tokenAmount > 0) token.safeTransfer(recipient, tokenAmount);
// Log the exit event
emit Exit(sender, recipient, token, tokenAmount, unitsAmount);
}
function setProvisioner(address provisioner_) external requiresAuth {
// Effects: set the provisioner
_setProvisioner(provisioner_);
}
/// @inheritdoc IMultiDepositorVault
function setBeforeTransferHook(IBeforeTransferHook hook) external requiresAuth {
// Effects: set the transfer hook
_setBeforeTransferHook(hook);
}
////////////////////////////////////////////////////////////
// Private / Internal Functions //
////////////////////////////////////////////////////////////
/// @inheritdoc ERC20
function _update(address from, address to, uint256 amount) internal override {
IBeforeTransferHook hook = beforeTransferHook;
if (address(hook) != address(0)) {
// Requirements: perform before transfer checks
hook.beforeTransfer(from, to, provisioner);
}
// Requirements: check that the from address does not have its units locked
// from == address(0) is to allow minting further units for user with locked units
// to == address(0) is to allow burning units in refundDeposit
require(
from == address(0) || to == address(0) || !IProvisioner(provisioner).areUserUnitsLocked(from),
Aera__UnitsLocked()
);
// Effects: transfer the tokens
return super._update(from, to, amount);
}
/// @notice Set the transfer hook
/// @param hook_ The transfer hook address
function _setBeforeTransferHook(IBeforeTransferHook hook_) internal {
// Effects: set the transfer hook contract
beforeTransferHook = hook_;
// Log that the transfer hook contract has been set
emit BeforeTransferHookSet(address(hook_));
}
/// @notice Set the provisioner
/// @param provisioner_ The provisioner address
function _setProvisioner(address provisioner_) internal {
// Requirements: check that the provisioner is not zero
require(provisioner_ != address(0), Aera__ZeroAddressProvisioner());
// Effects: set the provisioner
provisioner = provisioner_;
// Log that provisioner was set
emit ProvisionerSet(provisioner_);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../token/ERC20/IERC20.sol";// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/ERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "./IERC20.sol";
import {IERC20Metadata} from "./extensions/IERC20Metadata.sol";
import {Context} from "../../utils/Context.sol";
import {IERC20Errors} from "../../interfaces/draft-IERC6093.sol";
/**
* @dev Implementation of the {IERC20} interface.
*
* This implementation is agnostic to the way tokens are created. This means
* that a supply mechanism has to be added in a derived contract using {_mint}.
*
* TIP: For a detailed writeup see our guide
* https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
* to implement supply mechanisms].
*
* The default value of {decimals} is 18. To change this, you should override
* this function so it returns a different value.
*
* We have followed general OpenZeppelin Contracts guidelines: functions revert
* instead returning `false` on failure. This behavior is nonetheless
* conventional and does not conflict with the expectations of ERC-20
* applications.
*/
abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors {
mapping(address account => uint256) private _balances;
mapping(address account => mapping(address spender => uint256)) private _allowances;
uint256 private _totalSupply;
string private _name;
string private _symbol;
/**
* @dev Sets the values for {name} and {symbol}.
*
* All two of these values are immutable: they can only be set once during
* construction.
*/
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
}
/**
* @dev Returns the name of the token.
*/
function name() public view virtual returns (string memory) {
return _name;
}
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() public view virtual returns (string memory) {
return _symbol;
}
/**
* @dev Returns the number of decimals used to get its user representation.
* For example, if `decimals` equals `2`, a balance of `505` tokens should
* be displayed to a user as `5.05` (`505 / 10 ** 2`).
*
* Tokens usually opt for a value of 18, imitating the relationship between
* Ether and Wei. This is the default value returned by this function, unless
* it's overridden.
*
* NOTE: This information is only used for _display_ purposes: it in
* no way affects any of the arithmetic of the contract, including
* {IERC20-balanceOf} and {IERC20-transfer}.
*/
function decimals() public view virtual returns (uint8) {
return 18;
}
/**
* @dev See {IERC20-totalSupply}.
*/
function totalSupply() public view virtual returns (uint256) {
return _totalSupply;
}
/**
* @dev See {IERC20-balanceOf}.
*/
function balanceOf(address account) public view virtual returns (uint256) {
return _balances[account];
}
/**
* @dev See {IERC20-transfer}.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - the caller must have a balance of at least `value`.
*/
function transfer(address to, uint256 value) public virtual returns (bool) {
address owner = _msgSender();
_transfer(owner, to, value);
return true;
}
/**
* @dev See {IERC20-allowance}.
*/
function allowance(address owner, address spender) public view virtual returns (uint256) {
return _allowances[owner][spender];
}
/**
* @dev See {IERC20-approve}.
*
* NOTE: If `value` is the maximum `uint256`, the allowance is not updated on
* `transferFrom`. This is semantically equivalent to an infinite approval.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function approve(address spender, uint256 value) public virtual returns (bool) {
address owner = _msgSender();
_approve(owner, spender, value);
return true;
}
/**
* @dev See {IERC20-transferFrom}.
*
* Skips emitting an {Approval} event indicating an allowance update. This is not
* required by the ERC. See {xref-ERC20-_approve-address-address-uint256-bool-}[_approve].
*
* NOTE: Does not update the allowance if the current allowance
* is the maximum `uint256`.
*
* Requirements:
*
* - `from` and `to` cannot be the zero address.
* - `from` must have a balance of at least `value`.
* - the caller must have allowance for ``from``'s tokens of at least
* `value`.
*/
function transferFrom(address from, address to, uint256 value) public virtual returns (bool) {
address spender = _msgSender();
_spendAllowance(from, spender, value);
_transfer(from, to, value);
return true;
}
/**
* @dev Moves a `value` amount of tokens from `from` to `to`.
*
* This internal function is equivalent to {transfer}, and can be used to
* e.g. implement automatic token fees, slashing mechanisms, etc.
*
* Emits a {Transfer} event.
*
* NOTE: This function is not virtual, {_update} should be overridden instead.
*/
function _transfer(address from, address to, uint256 value) internal {
if (from == address(0)) {
revert ERC20InvalidSender(address(0));
}
if (to == address(0)) {
revert ERC20InvalidReceiver(address(0));
}
_update(from, to, value);
}
/**
* @dev Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from`
* (or `to`) is the zero address. All customizations to transfers, mints, and burns should be done by overriding
* this function.
*
* Emits a {Transfer} event.
*/
function _update(address from, address to, uint256 value) internal virtual {
if (from == address(0)) {
// Overflow check required: The rest of the code assumes that totalSupply never overflows
_totalSupply += value;
} else {
uint256 fromBalance = _balances[from];
if (fromBalance < value) {
revert ERC20InsufficientBalance(from, fromBalance, value);
}
unchecked {
// Overflow not possible: value <= fromBalance <= totalSupply.
_balances[from] = fromBalance - value;
}
}
if (to == address(0)) {
unchecked {
// Overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply.
_totalSupply -= value;
}
} else {
unchecked {
// Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256.
_balances[to] += value;
}
}
emit Transfer(from, to, value);
}
/**
* @dev Creates a `value` amount of tokens and assigns them to `account`, by transferring it from address(0).
* Relies on the `_update` mechanism
*
* Emits a {Transfer} event with `from` set to the zero address.
*
* NOTE: This function is not virtual, {_update} should be overridden instead.
*/
function _mint(address account, uint256 value) internal {
if (account == address(0)) {
revert ERC20InvalidReceiver(address(0));
}
_update(address(0), account, value);
}
/**
* @dev Destroys a `value` amount of tokens from `account`, lowering the total supply.
* Relies on the `_update` mechanism.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* NOTE: This function is not virtual, {_update} should be overridden instead
*/
function _burn(address account, uint256 value) internal {
if (account == address(0)) {
revert ERC20InvalidSender(address(0));
}
_update(account, address(0), value);
}
/**
* @dev Sets `value` as the allowance of `spender` over the `owner` s tokens.
*
* This internal function is equivalent to `approve`, and can be used to
* e.g. set automatic allowances for certain subsystems, etc.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `owner` cannot be the zero address.
* - `spender` cannot be the zero address.
*
* Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument.
*/
function _approve(address owner, address spender, uint256 value) internal {
_approve(owner, spender, value, true);
}
/**
* @dev Variant of {_approve} with an optional flag to enable or disable the {Approval} event.
*
* By default (when calling {_approve}) the flag is set to true. On the other hand, approval changes made by
* `_spendAllowance` during the `transferFrom` operation set the flag to false. This saves gas by not emitting any
* `Approval` event during `transferFrom` operations.
*
* Anyone who wishes to continue emitting `Approval` events on the`transferFrom` operation can force the flag to
* true using the following override:
*
* ```solidity
* function _approve(address owner, address spender, uint256 value, bool) internal virtual override {
* super._approve(owner, spender, value, true);
* }
* ```
*
* Requirements are the same as {_approve}.
*/
function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual {
if (owner == address(0)) {
revert ERC20InvalidApprover(address(0));
}
if (spender == address(0)) {
revert ERC20InvalidSpender(address(0));
}
_allowances[owner][spender] = value;
if (emitEvent) {
emit Approval(owner, spender, value);
}
}
/**
* @dev Updates `owner` s allowance for `spender` based on spent `value`.
*
* Does not update the allowance value in case of infinite allowance.
* Revert if not enough allowance is available.
*
* Does not emit an {Approval} event.
*/
function _spendAllowance(address owner, address spender, uint256 value) internal virtual {
uint256 currentAllowance = allowance(owner, spender);
if (currentAllowance != type(uint256).max) {
if (currentAllowance < value) {
revert ERC20InsufficientAllowance(spender, currentAllowance, value);
}
unchecked {
_approve(owner, spender, currentAllowance - value, false);
}
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
import {IERC1363} from "../../../interfaces/IERC1363.sol";
import {Address} from "../../../utils/Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC-20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
/**
* @dev An operation with an ERC-20 token failed.
*/
error SafeERC20FailedOperation(address token);
/**
* @dev Indicates a failed `decreaseAllowance` request.
*/
error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);
/**
* @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
}
/**
* @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
* calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
*/
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
}
/**
* @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*
* IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
* smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
* this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
* that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
*/
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
forceApprove(token, spender, oldAllowance + value);
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
* value, non-reverting calls are assumed to be successful.
*
* IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
* smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
* this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
* that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
*/
function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
unchecked {
uint256 currentAllowance = token.allowance(address(this), spender);
if (currentAllowance < requestedDecrease) {
revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
}
forceApprove(token, spender, currentAllowance - requestedDecrease);
}
}
/**
* @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
* to be set to zero before setting it to a non-zero value, such as USDT.
*
* NOTE: If the token implements ERC-7674, this function will not modify any temporary allowance. This function
* only sets the "standard" allowance. Any temporary allowance will remain active, in addition to the value being
* set here.
*/
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @dev Performs an {ERC1363} transferAndCall, with a fallback to the simple {ERC20} transfer if the target has no
* code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* Reverts if the returned value is other than `true`.
*/
function transferAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
if (to.code.length == 0) {
safeTransfer(token, to, value);
} else if (!token.transferAndCall(to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Performs an {ERC1363} transferFromAndCall, with a fallback to the simple {ERC20} transferFrom if the target
* has no code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* Reverts if the returned value is other than `true`.
*/
function transferFromAndCallRelaxed(
IERC1363 token,
address from,
address to,
uint256 value,
bytes memory data
) internal {
if (to.code.length == 0) {
safeTransferFrom(token, from, to, value);
} else if (!token.transferFromAndCall(from, to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Performs an {ERC1363} approveAndCall, with a fallback to the simple {ERC20} approve if the target has no
* code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* NOTE: When the recipient address (`to`) has no code (i.e. is an EOA), this function behaves as {forceApprove}.
* Opposedly, when the recipient address (`to`) has code, this function only attempts to call {ERC1363-approveAndCall}
* once without retrying, and relies on the returned value to be true.
*
* Reverts if the returned value is other than `true`.
*/
function approveAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
if (to.code.length == 0) {
forceApprove(token, to, value);
} else if (!token.approveAndCall(to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturnBool} that reverts if call fails to meet the requirements.
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
uint256 returnSize;
uint256 returnValue;
assembly ("memory-safe") {
let success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
// bubble errors
if iszero(success) {
let ptr := mload(0x40)
returndatacopy(ptr, 0, returndatasize())
revert(ptr, returndatasize())
}
returnSize := returndatasize()
returnValue := mload(0)
}
if (returnSize == 0 ? address(token).code.length == 0 : returnValue != 1) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturn} that silently catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
bool success;
uint256 returnSize;
uint256 returnValue;
assembly ("memory-safe") {
success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
returnSize := returndatasize()
returnValue := mload(0)
}
return success && (returnSize == 0 ? address(token).code.length > 0 : returnValue == 1);
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.29;
import { IERC20 } from "@oz/interfaces/IERC20.sol";
import { SafeERC20 } from "@oz/token/ERC20/utils/SafeERC20.sol";
import { BaseVault } from "src/core/BaseVault.sol";
import { FeeVaultParameters } from "src/core/Types.sol";
import { IFeeCalculator } from "src/core/interfaces/IFeeCalculator.sol";
import { IFeeVault } from "src/core/interfaces/IFeeVault.sol";
import { IFeeVaultDeployer } from "src/core/interfaces/IFeeVaultDeployer.sol";
/// @title FeeVault
/// @notice This contract extends BaseVault with fee capabilities for vaults that have a single logical owner of all
/// assets. The vault relies on an external contract called the fee calculator which is shared across multiple vaults
/// The fee calculator is responsible for calculating the TVL and performance fees for the vault, but
/// the vault has control over those fees. Fee claims are initiated via the vault, which consults and updates the fee
/// calculator upon successful claims
abstract contract FeeVault is IFeeVault, BaseVault {
using SafeERC20 for IERC20;
////////////////////////////////////////////////////////////
// Immutables //
////////////////////////////////////////////////////////////
/// @notice Address of the fee token
IERC20 public immutable FEE_TOKEN;
////////////////////////////////////////////////////////////
// Storage //
////////////////////////////////////////////////////////////
/// @notice Address of the fee calculator contract
IFeeCalculator public feeCalculator;
/// @notice Address of the fee recipient
address public feeRecipient;
////////////////////////////////////////////////////////////
// Modifiers //
////////////////////////////////////////////////////////////
/// @notice Modifier to check that the caller is the fee recipient
modifier onlyFeeRecipient() {
// Requirements: check that the caller is the fee recipient
require(msg.sender == feeRecipient, Aera__CallerIsNotFeeRecipient());
_;
}
constructor() BaseVault() {
// Interactions: get the fee vault parameters
FeeVaultParameters memory params = IFeeVaultDeployer(msg.sender).feeVaultParameters();
IFeeCalculator feeCalculator_ = params.feeCalculator;
IERC20 feeToken_ = params.feeToken;
// Requirements: check that the fee calculator and fee token are not zero addresses
require(address(feeCalculator_) != address(0), Aera__ZeroAddressFeeCalculator());
require(address(feeToken_) != address(0), Aera__ZeroAddressFeeToken());
// Interactions: register the vault with the fee calculator
feeCalculator_.registerVault();
address feeRecipient_ = params.feeRecipient;
// Requirements: check that the fee recipient is not the zero address
require(feeRecipient_ != address(0), Aera__ZeroAddressFeeRecipient());
// Effects: set the fee recipient and the fee calculator
feeRecipient = feeRecipient_;
feeCalculator = feeCalculator_;
// Effects: set the fee token immutable
FEE_TOKEN = feeToken_;
}
////////////////////////////////////////////////////////////
// Public / External Functions //
////////////////////////////////////////////////////////////
/// @inheritdoc IFeeVault
function setFeeCalculator(IFeeCalculator newFeeCalculator) external requiresAuth {
// Effects: set the new fee calculator
feeCalculator = newFeeCalculator;
// Log the fee calculator updated event
emit FeeCalculatorUpdated(address(newFeeCalculator));
// Interactions: register vault only if the new calculator is not address(0)
if (address(newFeeCalculator) != address(0)) {
newFeeCalculator.registerVault();
}
}
/// @inheritdoc IFeeVault
function setFeeRecipient(address newFeeRecipient) external requiresAuth {
// Requirements: check that the new fee recipient is not the zero address
require(newFeeRecipient != address(0), Aera__ZeroAddressFeeRecipient());
// Effects: set the new fee recipient
feeRecipient = newFeeRecipient;
// Log the fee recipient updated event
emit FeeRecipientUpdated(newFeeRecipient);
}
/// @inheritdoc IFeeVault
function claimFees() external onlyFeeRecipient returns (uint256 feeRecipientFees, uint256 protocolFees) {
address protocolFeeRecipient;
// Interactions: claim the fees
(feeRecipientFees, protocolFees, protocolFeeRecipient) =
feeCalculator.claimFees(FEE_TOKEN.balanceOf(address(this)));
// Requirements: check that the fee recipient has earned fees
require(feeRecipientFees != 0, Aera__NoFeesToClaim());
// Interactions: transfer the fees to the fee recipient
FEE_TOKEN.safeTransfer(msg.sender, feeRecipientFees);
// Log the fees claimed event
emit FeesClaimed(msg.sender, feeRecipientFees);
if (protocolFees != 0) {
// Interactions: transfer the protocol fees to the protocol fee recipient
FEE_TOKEN.safeTransfer(protocolFeeRecipient, protocolFees);
// Log the protocol fees claimed event
emit ProtocolFeesClaimed(protocolFeeRecipient, protocolFees);
}
}
/// @inheritdoc IFeeVault
function claimProtocolFees() external returns (uint256 protocolFees) {
address protocolFeeRecipient;
// Interactions: claim the protocol fees
(protocolFees, protocolFeeRecipient) = feeCalculator.claimProtocolFees(FEE_TOKEN.balanceOf(address(this)));
// Requirements: check that the caller is the protocol fee recipient
require(msg.sender == protocolFeeRecipient, Aera__CallerIsNotProtocolFeeRecipient());
// Requirements: check that the protocol has earned fees
require(protocolFees != 0, Aera__NoFeesToClaim());
// Interactions: transfer the protocol fees to the protocol fee recipient
FEE_TOKEN.safeTransfer(protocolFeeRecipient, protocolFees);
// Log the protocol fees claimed event
emit ProtocolFeesClaimed(protocolFeeRecipient, protocolFees);
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.29;
/// @title IBeforeTransferHook
/// @notice Interface for token transfer hooks used for vault units in multi-depositor vaults
interface IBeforeTransferHook {
////////////////////////////////////////////////////////////
// Events //
////////////////////////////////////////////////////////////
event VaultUnitTransferableSet(address indexed vault, bool isTransferable);
////////////////////////////////////////////////////////////
// Errors //
////////////////////////////////////////////////////////////
error Aera__NotVaultOwner();
error Aera__VaultUnitsNotTransferable(address vault);
////////////////////////////////////////////////////////////
// Functions //
////////////////////////////////////////////////////////////
/// @notice Set whether vault units should be transferable
/// @param vault The vault to update status for
/// @param isTransferable Whether the vault units are transferable
function setIsVaultUnitsTransferable(address vault, bool isTransferable) external;
/// @notice Perform before transfer checks
/// @param from Address that is sending the units
/// @param to Address that is receiving the units
/// @param transferAgent Address that is always allowed to transfer the units
function beforeTransfer(address from, address to, address transferAgent) external view;
}// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.29;
import { IERC20 } from "@oz/interfaces/IERC20.sol";
import { IBeforeTransferHook } from "src/core/interfaces/IBeforeTransferHook.sol";
/// @title IMultiDepositorVault
/// @notice Interface for vaults that can accept deposits from multiple addresses
interface IMultiDepositorVault {
////////////////////////////////////////////////////////////
// Events //
////////////////////////////////////////////////////////////
event BeforeTransferHookSet(address indexed beforeTransferHook);
event ProvisionerSet(address indexed provisioner);
event Enter(
address indexed sender,
address indexed recipient,
IERC20 indexed token,
uint256 tokenAmount,
uint256 unitsAmount
);
event Exit(
address indexed sender,
address indexed recipient,
IERC20 indexed token,
uint256 tokenAmount,
uint256 unitsAmount
);
////////////////////////////////////////////////////////////
// Errors //
////////////////////////////////////////////////////////////
error Aera__UnitsLocked();
error Aera__ZeroAddressProvisioner();
error Aera__CallerIsNotProvisioner();
////////////////////////////////////////////////////////////
// Functions //
////////////////////////////////////////////////////////////
/// @notice Set the before transfer hooks
/// @param hooks The before transfer hooks address
function setBeforeTransferHook(IBeforeTransferHook hooks) external;
/// @notice Deposit tokens into the vault and mint units
/// @param sender The sender of the tokens
/// @param token The token to deposit
/// @param tokenAmount The amount of token to deposit
/// @param unitsAmount The amount of units to mint
/// @param recipient The recipient of the units
function enter(address sender, IERC20 token, uint256 tokenAmount, uint256 unitsAmount, address recipient)
external;
/// @notice Withdraw tokens from the vault and burn units
/// @param sender The sender of the units
/// @param token The token to withdraw
/// @param tokenAmount The amount of token to withdraw
/// @param unitsAmount The amount of units to burn
/// @param recipient The recipient of the tokens
function exit(address sender, IERC20 token, uint256 tokenAmount, uint256 unitsAmount, address recipient) external;
}// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.29;
import { BaseVaultParameters, ERC20Parameters, FeeVaultParameters } from "src/core/Types.sol";
import { IBeforeTransferHook } from "src/core/interfaces/IBeforeTransferHook.sol";
import { IFeeVaultDeployer } from "src/core/interfaces/IFeeVaultDeployer.sol";
/// @title IMultiDepositorVaultFactory
/// @notice Interface for the multi depositor vault factory
interface IMultiDepositorVaultFactory is IFeeVaultDeployer {
////////////////////////////////////////////////////////////
// Events //
////////////////////////////////////////////////////////////
/// @notice Emitted when the vault is created
/// @param vault Vault address
/// @param owner Initial owner address
/// @param hooks Vault hooks address
/// @param erc20Params ERC20 parameters
/// @param feeVaultParams Fee vault parameters
/// @param beforeTransferHook Before transfer hooks
/// @param description Vault description
event VaultCreated(
address indexed vault,
address indexed owner,
address hooks,
ERC20Parameters erc20Params,
FeeVaultParameters feeVaultParams,
IBeforeTransferHook beforeTransferHook,
string description
);
////////////////////////////////////////////////////////////
// Errors //
////////////////////////////////////////////////////////////
/// @notice Thrown when deploy delegate is the zero address
error Aera__ZeroAddressDeployDelegate();
////////////////////////////////////////////////////////////
// Functions //
////////////////////////////////////////////////////////////
/// @notice Create multi depositor vault
/// @param salt The salt used to generate the vault address
/// @param description Vault description
/// @param erc20Params ERC20 parameters for deployment
/// @param baseVaultParams Base vault parameters for deployment
/// @param feeVaultParams Fee vault parameters for deployment
/// @param beforeTransferHook Before transfer hooks for deployment
/// @param expectedVaultAddress Expected vault address to check against deployed vault address
/// @return deployedVault Deployed vault address
function create(
bytes32 salt,
string calldata description,
ERC20Parameters calldata erc20Params,
BaseVaultParameters calldata baseVaultParams,
FeeVaultParameters calldata feeVaultParams,
IBeforeTransferHook beforeTransferHook,
address expectedVaultAddress
) external returns (address deployedVault);
/// @notice Get the ERC20 name of vault units
/// @return name The name of the vault ERC20 token
function getERC20Name() external view returns (string memory name);
/// @notice Get the ERC20 symbol of vault units
/// @return symbol The symbol of the vault ERC20 token
function getERC20Symbol() external view returns (string memory symbol);
/// @notice Get the vault parameters
/// @return beforeTransferHook The hooks called before vault unit transfers
function multiDepositorVaultParameters() external view returns (IBeforeTransferHook beforeTransferHook);
}// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.29;
import { IERC20 } from "@oz/token/ERC20/IERC20.sol";
import { Request, TokenDetails } from "src/core/Types.sol";
/// @title IProvisioner
/// @notice Interface for the contract that can mint and burn vault units in exchange for tokens
interface IProvisioner {
////////////////////////////////////////////////////////////
// Events //
////////////////////////////////////////////////////////////
/// @notice Emitted when a user deposits tokens directly into the vault
/// @param user The address of the depositor
/// @param token The token being deposited
/// @param tokensIn The amount of tokens deposited
/// @param unitsOut The amount of units minted
/// @param depositHash Unique identifier for this deposit
event Deposited(
address indexed user, IERC20 indexed token, uint256 tokensIn, uint256 unitsOut, bytes32 depositHash
);
/// @notice Emitted when a deposit is refunded
/// @param depositHash The hash of the deposit being refunded
event DepositRefunded(bytes32 indexed depositHash);
/// @notice Emitted when a direct (sync) deposit is refunded
/// @param depositHash The hash of the deposit being refunded
event DirectDepositRefunded(bytes32 indexed depositHash);
/// @notice Emitted when a user creates a deposit request
/// @param user The address requesting the deposit
/// @param token The token being deposited
/// @param tokensIn The amount of tokens to deposit
/// @param minUnitsOut The minimum amount of units expected
/// @param solverTip The tip offered to the solver in deposit token terms
/// @param deadline Timestamp until which the request is valid
/// @param maxPriceAge Maximum age of price data that solver can use
/// @param isFixedPrice Whether the request is a fixed price request
/// @param depositRequestHash The hash of the deposit request
event DepositRequested(
address indexed user,
IERC20 indexed token,
uint256 tokensIn,
uint256 minUnitsOut,
uint256 solverTip,
uint256 deadline,
uint256 maxPriceAge,
bool isFixedPrice,
bytes32 depositRequestHash
);
/// @notice Emitted when a user creates a redeem request
/// @param user The address requesting the redemption
/// @param token The token requested in return for units
/// @param minTokensOut The minimum amount of tokens the user expects to receive
/// @param unitsIn The amount of units being redeemed
/// @param solverTip The tip offered to the solver in redeem token terms
/// @param deadline The timestamp until which this request is valid
/// @param maxPriceAge Maximum age of price data that solver can use
/// @param isFixedPrice Whether the request is a fixed price request
/// @param redeemRequestHash The hash of the redeem request
event RedeemRequested(
address indexed user,
IERC20 indexed token,
uint256 minTokensOut,
uint256 unitsIn,
uint256 solverTip,
uint256 deadline,
uint256 maxPriceAge,
bool isFixedPrice,
bytes32 redeemRequestHash
);
/// @notice Emitted when a deposit request is solved successfully
/// @param depositHash The unique identifier of the deposit request that was solved
event DepositSolved(bytes32 indexed depositHash);
/// @notice Emitted when a redeem request is solved successfully
/// @param redeemHash The unique identifier of the redeem request that was solved
event RedeemSolved(bytes32 indexed redeemHash);
/// @notice Emitted when an unrecognized async deposit hash is used
/// @param depositHash The deposit hash that was not found in async records
event InvalidRequestHash(bytes32 indexed depositHash);
/// @notice Emitted when async deposits are disabled and a deposit request cannot be processed
/// @param index The index of the deposit request that was rejected
event AsyncDepositDisabled(uint256 indexed index);
/// @notice Emitted when async redeems are disabled and a redeem request cannot be processed
/// @param index The index of the redeem request that was rejected
event AsyncRedeemDisabled(uint256 indexed index);
/// @notice Emitted when the price age exceeds the maximum allowed for a request
/// @param index The index of the request that was rejected
event PriceAgeExceeded(uint256 indexed index);
/// @notice Emitted when a deposit exceeds the vault's configured deposit cap
/// @param index The index of the request that was rejected
event DepositCapExceeded(uint256 indexed index);
/// @notice Emitted when there are not enough tokens to cover the required solver tip
/// @param index The index of the request that was rejected
event InsufficientTokensForTip(uint256 indexed index);
/// @notice Emitted when the output units are less than the amount requested
/// @param index The index of the request that was rejected
/// @param amount The actual amount
/// @param bound The minimum amount
event AmountBoundExceeded(uint256 indexed index, uint256 amount, uint256 bound);
/// @notice Emitted when a redeem request is refunded due to expiration or cancellation
/// @param redeemHash The unique identifier of the redeem request that was refunded
event RedeemRefunded(bytes32 indexed redeemHash);
/// @notice Emitted when the vault's deposit limits are updated
/// @param depositCap The new maximum total value that can be deposited into the vault
/// @param depositRefundTimeout The new time window during which deposits can be refunded
event DepositDetailsUpdated(uint256 depositCap, uint256 depositRefundTimeout);
/// @notice Emitted when a token's deposit/withdrawal settings are updated
/// @param token The token whose settings are being updated
/// @param tokensDetails The new token details
event TokenDetailsSet(IERC20 indexed token, TokenDetails tokensDetails);
/// @notice Emitted when a token is removed from the provisioner
/// @param token The token that was removed
event TokenRemoved(IERC20 indexed token);
////////////////////////////////////////////////////////////
// Errors //
////////////////////////////////////////////////////////////
error Aera__SyncDepositDisabled();
error Aera__AsyncDepositDisabled();
error Aera__AsyncRedeemDisabled();
error Aera__DepositCapExceeded();
error Aera__MinUnitsOutNotMet();
error Aera__TokensInZero();
error Aera__UnitsInZero();
error Aera__UnitsOutZero();
error Aera__MinUnitsOutZero();
error Aera__MaxTokensInZero();
error Aera__MaxTokensInExceeded();
error Aera__MaxDepositRefundTimeoutExceeded();
error Aera__DepositHashNotFound();
error Aera__HashNotFound();
error Aera__RefundPeriodExpired();
error Aera__DeadlineInPast();
error Aera__DeadlineTooFarInFuture();
error Aera__DeadlineInFutureAndUnauthorized();
error Aera__MinTokenOutZero();
error Aera__HashCollision();
error Aera__ZeroAddressPriceAndFeeCalculator();
error Aera__ZeroAddressMultiDepositorVault();
error Aera__DepositMultiplierTooLow();
error Aera__DepositMultiplierTooHigh();
error Aera__RedeemMultiplierTooLow();
error Aera__RedeemMultiplierTooHigh();
error Aera__DepositCapZero();
error Aera__PriceAndFeeCalculatorVaultPaused();
error Aera__AutoPriceSolveNotAllowed();
error Aera__FixedPriceSolverTipNotAllowed();
error Aera__TokenCantBePriced();
error Aera__CallerIsVault();
error Aera__InvalidToken();
////////////////////////////////////////////////////////////
// Functions //
////////////////////////////////////////////////////////////
/// @notice Deposit tokens directly into the vault
/// @param token The token to deposit
/// @param tokensIn The amount of tokens to deposit
/// @param minUnitsOut The minimum amount of units expected
/// @dev MUST revert if tokensIn is 0, minUnitsOut is 0, or sync deposits are disabled
/// @return unitsOut The amount of shares minted to the receiver
function deposit(IERC20 token, uint256 tokensIn, uint256 minUnitsOut) external returns (uint256 unitsOut);
/// @notice Mint exact amount of units by depositing required tokens
/// @param token The token to deposit
/// @param unitsOut The exact amount of units to mint
/// @param maxTokensIn Maximum amount of tokens willing to deposit
/// @return tokensIn The amount of tokens used to mint the requested shares
function mint(IERC20 token, uint256 unitsOut, uint256 maxTokensIn) external returns (uint256 tokensIn);
/// @notice Refund a deposit within the refund period
/// @param sender The original depositor
/// @param token The deposited token
/// @param tokenAmount The amount of tokens deposited
/// @param unitsAmount The amount of units minted
/// @param refundableUntil Timestamp until which refund is possible
/// @dev Only callable by authorized addresses
function refundDeposit(
address sender,
IERC20 token,
uint256 tokenAmount,
uint256 unitsAmount,
uint256 refundableUntil
) external;
/// @notice Refund an expired deposit or redeem request
/// @param token The token involved in the request
/// @param request The request to refund
/// @dev Can only be called after request deadline has passed
function refundRequest(IERC20 token, Request calldata request) external;
/// @notice Create a new deposit request to be solved by solvers
/// @param token The token to deposit
/// @param tokensIn The amount of tokens to deposit
/// @param minUnitsOut The minimum amount of units expected
/// @param solverTip The tip offered to the solver
/// @param deadline Duration in seconds for which the request is valid
/// @param maxPriceAge Maximum age of price data that solver can use
/// @param isFixedPrice Whether the request is a fixed price request
function requestDeposit(
IERC20 token,
uint256 tokensIn,
uint256 minUnitsOut,
uint256 solverTip,
uint256 deadline,
uint256 maxPriceAge,
bool isFixedPrice
) external;
/// @notice Create a new redeem request to be solved by solvers
/// @param token The token to receive
/// @param unitsIn The amount of units to redeem
/// @param minTokensOut The minimum amount of tokens expected
/// @param solverTip The tip offered to the solver
/// @param deadline Duration in seconds for which the request is valid
/// @param maxPriceAge Maximum age of price data that solver can use
function requestRedeem(
IERC20 token,
uint256 unitsIn,
uint256 minTokensOut,
uint256 solverTip,
uint256 deadline,
uint256 maxPriceAge,
bool isFixedPrice
) external;
/// @notice Solve multiple requests using vault's liquidity
/// @param token The token for which to solve requests
/// @param requests Array of requests to solve
/// @dev Only callable by authorized addresses
function solveRequestsVault(IERC20 token, Request[] calldata requests) external;
/// @notice Solve multiple requests using solver's own liquidity
/// @param token The token for which to solve requests
/// @param requests Array of requests to solve
function solveRequestsDirect(IERC20 token, Request[] calldata requests) external;
/// @notice Update token parameters
/// @param token The token to update
/// @param tokensDetails The new token details
function setTokenDetails(IERC20 token, TokenDetails calldata tokensDetails) external;
/// @notice Removes token from provisioner
/// @param token The token to be removed
function removeToken(IERC20 token) external;
/// @notice Update deposit parameters
/// @param depositCap_ New maximum total value that can be deposited
/// @param depositRefundTimeout_ New time window for deposit refunds
function setDepositDetails(uint256 depositCap_, uint256 depositRefundTimeout_) external;
/// @notice Return maximum amount that can still be deposited
/// @return Amount of deposit capacity remaining
function maxDeposit() external view returns (uint256);
/// @notice Check if a user's units are currently locked
/// @param user The address to check
/// @return True if user's units are locked, false otherwise
function areUserUnitsLocked(address user) external view returns (bool);
/// @notice Computes the hash for a sync deposit
/// @param user The address making the deposit
/// @param token The token being deposited
/// @param tokenAmount The amount of tokens to deposit
/// @param unitsAmount Minimum amount of units to receive
/// @param refundableUntil The timestamp until which the deposit is refundable
/// @return The hash of the deposit
function getDepositHash(
address user,
IERC20 token,
uint256 tokenAmount,
uint256 unitsAmount,
uint256 refundableUntil
) external pure returns (bytes32);
/// @notice Computes the hash for a generic request
/// @param token The token involved in the request
/// @param request The request struct
/// @return The hash of the request
function getRequestHash(IERC20 token, Request calldata request) external pure returns (bytes32);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC-20 standard as defined in the ERC.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 value) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 value) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Metadata.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
/**
* @dev Interface for the optional metadata functions from the ERC-20 standard.
*/
interface IERC20Metadata is IERC20 {
/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);
/**
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the decimals places of the token.
*/
function decimals() external view returns (uint8);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/draft-IERC6093.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard ERC-20 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-20 tokens.
*/
interface IERC20Errors {
/**
* @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param balance Current balance for the interacting account.
* @param needed Minimum amount required to perform a transfer.
*/
error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC20InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC20InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers.
* @param spender Address that may be allowed to operate on tokens without being their owner.
* @param allowance Amount of tokens a `spender` is allowed to operate with.
* @param needed Minimum amount required to perform a transfer.
*/
error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC20InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `spender` to be approved. Used in approvals.
* @param spender Address that may be allowed to operate on tokens without being their owner.
*/
error ERC20InvalidSpender(address spender);
}
/**
* @dev Standard ERC-721 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-721 tokens.
*/
interface IERC721Errors {
/**
* @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in ERC-20.
* Used in balance queries.
* @param owner Address of the current owner of a token.
*/
error ERC721InvalidOwner(address owner);
/**
* @dev Indicates a `tokenId` whose `owner` is the zero address.
* @param tokenId Identifier number of a token.
*/
error ERC721NonexistentToken(uint256 tokenId);
/**
* @dev Indicates an error related to the ownership over a particular token. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param tokenId Identifier number of a token.
* @param owner Address of the current owner of a token.
*/
error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC721InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC721InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `operator`’s approval. Used in transfers.
* @param operator Address that may be allowed to operate on tokens without being their owner.
* @param tokenId Identifier number of a token.
*/
error ERC721InsufficientApproval(address operator, uint256 tokenId);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC721InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `operator` to be approved. Used in approvals.
* @param operator Address that may be allowed to operate on tokens without being their owner.
*/
error ERC721InvalidOperator(address operator);
}
/**
* @dev Standard ERC-1155 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-1155 tokens.
*/
interface IERC1155Errors {
/**
* @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param balance Current balance for the interacting account.
* @param needed Minimum amount required to perform a transfer.
* @param tokenId Identifier number of a token.
*/
error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC1155InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC1155InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `operator`’s approval. Used in transfers.
* @param operator Address that may be allowed to operate on tokens without being their owner.
* @param owner Address of the current owner of a token.
*/
error ERC1155MissingApprovalForAll(address operator, address owner);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC1155InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `operator` to be approved. Used in approvals.
* @param operator Address that may be allowed to operate on tokens without being their owner.
*/
error ERC1155InvalidOperator(address operator);
/**
* @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation.
* Used in batch transfers.
* @param idsLength Length of the array of token identifiers
* @param valuesLength Length of the array of token amounts
*/
error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC1363.sol)
pragma solidity ^0.8.20;
import {IERC20} from "./IERC20.sol";
import {IERC165} from "./IERC165.sol";
/**
* @title IERC1363
* @dev Interface of the ERC-1363 standard as defined in the https://eips.ethereum.org/EIPS/eip-1363[ERC-1363].
*
* Defines an extension interface for ERC-20 tokens that supports executing code on a recipient contract
* after `transfer` or `transferFrom`, or code on a spender contract after `approve`, in a single transaction.
*/
interface IERC1363 is IERC20, IERC165 {
/*
* Note: the ERC-165 identifier for this interface is 0xb0202a11.
* 0xb0202a11 ===
* bytes4(keccak256('transferAndCall(address,uint256)')) ^
* bytes4(keccak256('transferAndCall(address,uint256,bytes)')) ^
* bytes4(keccak256('transferFromAndCall(address,address,uint256)')) ^
* bytes4(keccak256('transferFromAndCall(address,address,uint256,bytes)')) ^
* bytes4(keccak256('approveAndCall(address,uint256)')) ^
* bytes4(keccak256('approveAndCall(address,uint256,bytes)'))
*/
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferAndCall(address to, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @param data Additional data with no specified format, sent in call to `to`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param from The address which you want to send tokens from.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferFromAndCall(address from, address to, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param from The address which you want to send tokens from.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @param data Additional data with no specified format, sent in call to `to`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferFromAndCall(address from, address to, uint256 value, bytes calldata data) external returns (bool);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
* @param spender The address which will spend the funds.
* @param value The amount of tokens to be spent.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function approveAndCall(address spender, uint256 value) external returns (bool);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
* @param spender The address which will spend the funds.
* @param value The amount of tokens to be spent.
* @param data Additional data with no specified format, sent in call to `spender`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function approveAndCall(address spender, uint256 value, bytes calldata data) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)
pragma solidity ^0.8.20;
import {Errors} from "./Errors.sol";
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev There's no code at `target` (it is not a contract).
*/
error AddressEmptyCode(address target);
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
if (address(this).balance < amount) {
revert Errors.InsufficientBalance(address(this).balance, amount);
}
(bool success, ) = recipient.call{value: amount}("");
if (!success) {
revert Errors.FailedCall();
}
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason or custom error, it is bubbled
* up by this function (like regular Solidity function calls). However, if
* the call reverted with no returned reason, this function reverts with a
* {Errors.FailedCall} error.
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
if (address(this).balance < value) {
revert Errors.InsufficientBalance(address(this).balance, value);
}
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
* was not a contract or bubbling up the revert reason (falling back to {Errors.FailedCall}) in case
* of an unsuccessful call.
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata
) internal view returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
// only check if target is a contract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
if (returndata.length == 0 && target.code.length == 0) {
revert AddressEmptyCode(target);
}
return returndata;
}
}
/**
* @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
* revert reason or with a default {Errors.FailedCall} error.
*/
function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
return returndata;
}
}
/**
* @dev Reverts with returndata if present. Otherwise reverts with {Errors.FailedCall}.
*/
function _revert(bytes memory returndata) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
assembly ("memory-safe") {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert Errors.FailedCall();
}
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.29;
import { CallbackHandler } from "src/core/CallbackHandler.sol";
// solhint-disable no-unused-import
import { IERC20 } from "@oz/interfaces/IERC20.sol";
import { IERC721Receiver } from "@oz/interfaces/IERC721Receiver.sol";
import { Pausable } from "@oz/utils/Pausable.sol";
import { ReentrancyGuardTransient } from "@oz/utils/ReentrancyGuardTransient.sol";
import { TransientSlot } from "@oz/utils/TransientSlot.sol";
import { MerkleProof } from "@oz/utils/cryptography/MerkleProof.sol";
import { EnumerableMap } from "@oz/utils/structs/EnumerableMap.sol";
import { Authority } from "@solmate/auth/Auth.sol";
import { Auth2Step } from "src/core/Auth2Step.sol";
import {
ADDRESS_SIZE_BITS,
AFTER_HOOK_MASK,
BEFORE_HOOK_MASK,
CONFIGURABLE_HOOKS_LENGTH_MASK,
ERC20_SPENDER_OFFSET,
HOOKS_FLAG_MASK,
WORD_SIZE
} from "src/core/Constants.sol";
import { Approval, BaseVaultParameters, HookCallType, OperationContext, ReturnValueType } from "src/core/Types.sol";
import { IBaseVault } from "src/core/interfaces/IBaseVault.sol";
import { IBaseVaultFactory } from "src/core/interfaces/IBaseVaultFactory.sol";
import { ISubmitHooks } from "src/core/interfaces/ISubmitHooks.sol";
import { IWhitelist } from "src/core/interfaces/IWhitelist.sol";
import { CalldataExtractor } from "src/core/libraries/CalldataExtractor.sol";
import { CalldataReader, CalldataReaderLib } from "src/core/libraries/CalldataReader.sol";
import { Pipeline } from "src/core/libraries/Pipeline.sol";
import { IERC20WithAllowance } from "src/dependencies/openzeppelin/token/ERC20/IERC20WithAllowance.sol";
/// @title BaseVault
/// @notice This contract embeds core Aera platform functionality: the ability to enlist off-chain guardians to take
/// guarded actions on a vault. It is meant to either be extended with deposit/withdraw capabilities for users or used
/// directly. When used directly, a depositor can simply transfer assets to the vault and a guardian can transfer them
/// out when needed
///
/// Registered guardians call the submit function and trigger vault operations. The vault may run before and after
/// submit
/// hooks and revert if a guardian is using an unauthorized operation. Authorized operations are configured in an
/// off-chain merkle tree and guardians need to provide a merkle proof for each operation. In addition to validating
/// operation targets (the contract and function being called), the merkle tree can maintain custom per-operation hooks
/// that extract specific parts of the calldata for validation or even perform (possibly stateful) validation during
/// the submit call
contract BaseVault is IBaseVault, Pausable, CallbackHandler, ReentrancyGuardTransient, Auth2Step, IERC721Receiver {
using Pipeline for bytes;
using CalldataExtractor for bytes;
using EnumerableMap for EnumerableMap.AddressToBytes32Map;
using TransientSlot for *;
///////////////////////////////////////////////////////////
// Constants //
///////////////////////////////////////////////////////////
/// @notice ERC7201-compliant transient storage slot for the current hook call type flag
/// @dev Equal to keccak256(abi.encode(uint256(keccak256("aera.basevault.hookCallType")) - 1)) &
/// ~bytes32(uint256(0xff));
bytes32 internal constant HOOK_CALL_TYPE_SLOT = 0xb8706f504833578f7e830b12e31c3cfba31669a85b02596177f00c6a7faf6e00;
////////////////////////////////////////////////////////////
// Immutables //
////////////////////////////////////////////////////////////
IWhitelist public immutable WHITELIST;
////////////////////////////////////////////////////////////
// Storage //
////////////////////////////////////////////////////////////
/// @notice Address of the submit hooks contract for vault-level operations
ISubmitHooks public submitHooks;
/// @notice Enumerable map of each guardian address to their merkle root
EnumerableMap.AddressToBytes32Map internal guardianRoots;
////////////////////////////////////////////////////////////
// Modifiers //
////////////////////////////////////////////////////////////
/// @notice Ensures caller either has auth authorization requiresAuth (owner or authorized role) or is a guardian
modifier onlyAuthOrGuardian() {
require(
isAuthorized(msg.sender, msg.sig) || guardianRoots.contains(msg.sender), Aera__CallerIsNotAuthOrGuardian()
);
_;
}
constructor() Pausable() Auth2Step(msg.sender, Authority(address(0))) {
// Interactions: get initialization parameters from the factory
BaseVaultParameters memory params = IBaseVaultFactory(msg.sender).baseVaultParameters();
address initialOwner = params.owner;
// Requirements: check that the owner address is not zero
require(initialOwner != address(0), Aera__ZeroAddressOwner());
// Effects: sets the pending owner via Auth2Step two-step process
transferOwnership(initialOwner);
if (params.authority != Authority(address(0))) {
// Effects: set the authority
setAuthority(params.authority);
}
// Effects: set the whitelist
WHITELIST = params.whitelist;
// Effects: set vault-level submit hooks
ISubmitHooks submitHooks_ = params.submitHooks;
if (address(submitHooks_) != address(0)) {
_setSubmitHooks(submitHooks_);
}
}
////////////////////////////////////////////////////////////
// Public / External Functions //
////////////////////////////////////////////////////////////
/// @notice Receive function to allow the vault to receive native tokens
receive() external payable { }
/// @inheritdoc IBaseVault
function submit(bytes calldata data) external whenNotPaused nonReentrant {
(bool success, bytes32 root) = guardianRoots.tryGet(msg.sender);
// Requirements: check that the caller is a guardian
require(success, Aera__CallerIsNotGuardian());
address submitHooks_ = address(submitHooks);
// Requirements + Interactions: call the before submit hooks if defined
_beforeSubmitHooks(submitHooks_, data);
CalldataReader reader = CalldataReaderLib.from(data);
CalldataReader end = reader.readBytesEnd(data);
Approval[] memory approvals;
uint256 approvalsLength;
// Requirements + Interactions: execute operations
(approvals, approvalsLength,, reader) = _executeSubmit(root, reader, false);
// Requirements + Interactions: call the after submit hooks if defined
_afterSubmitHooks(submitHooks_, data);
// Invariants: verify no outgoing approvals are left
_noPendingApprovalsInvariant(approvals, approvalsLength);
// Invariants: check that the reader is at the end of the calldata
reader.requireAtEndOf(end);
}
/// @inheritdoc IBaseVault
function setGuardianRoot(address guardian, bytes32 root) external virtual requiresAuth {
// Requirements + Effects: set the guardian root
_setGuardianRoot(guardian, root);
}
/// @inheritdoc IBaseVault
function removeGuardian(address guardian) external virtual requiresAuth {
// Effects: set the guardian root to zero
guardianRoots.remove(guardian);
// Log emit guardian root set event
emit GuardianRootSet(guardian, bytes32(0));
}
/// @inheritdoc IBaseVault
function checkGuardianWhitelist(address guardian) external returns (bool isRemoved) {
// Requirements: check that the guardian is not in the whitelist
if (!WHITELIST.isWhitelisted(guardian)) {
// Effects: set the guardian root to zero
guardianRoots.remove(guardian);
isRemoved = true;
// Log guardian root set
emit GuardianRootSet(guardian, bytes32(0));
}
}
/// @inheritdoc IBaseVault
function setSubmitHooks(ISubmitHooks newSubmitHooks) external virtual requiresAuth {
// Requirements + Effects: set the submit hooks address
_setSubmitHooks(newSubmitHooks);
}
/// @inheritdoc IBaseVault
function pause() external onlyAuthOrGuardian {
// Effects: pause the vault
_pause();
}
/// @inheritdoc IBaseVault
function unpause() external requiresAuth {
// Effects: unpause the vault
_unpause();
}
/// @inheritdoc IBaseVault
function getActiveGuardians() external view returns (address[] memory) {
return guardianRoots.keys();
}
/// @inheritdoc IBaseVault
function getGuardianRoot(address guardian) external view returns (bytes32) {
(bool success, bytes32 root) = guardianRoots.tryGet(guardian);
return success ? root : bytes32(0);
}
/// @inheritdoc IBaseVault
function getCurrentHookCallType() external view returns (HookCallType) {
return HookCallType(HOOK_CALL_TYPE_SLOT.asUint256().tload());
}
/// @inheritdoc IERC721Receiver
function onERC721Received(address, address, uint256, bytes calldata) external pure returns (bytes4) {
return IERC721Receiver.onERC721Received.selector;
}
////////////////////////////////////////////////////////////
// Private / Internal Functions //
////////////////////////////////////////////////////////////
/// @inheritdoc CallbackHandler
function _handleCallbackOperations(bytes32 root, uint256 cursor)
internal
virtual
override
returns (bytes memory returnValue)
{
CalldataReader reader = CalldataReader.wrap(cursor);
CalldataReader end = reader.readBytesEnd();
Approval[] memory approvals;
uint256 approvalsLength;
bytes[] memory results;
// Requirements + Interactions: execute vault operations received from the callback
(approvals, approvalsLength, results, reader) = _executeSubmit(root, reader, true);
// Effects: store the history of outgoing token approvals for later verification in submit
_storeCallbackApprovals(approvals, approvalsLength);
(reader, returnValue) = _getReturnValue(reader, results);
// Invariants: check that the reader is at the end of the calldata
reader.requireAtEndOf(end);
return returnValue;
}
/// @notice Prepare for a callback if the guardian expects one
/// @dev Writes to transient storage to encode callback expectations
/// @param reader Current position in the calldata
/// @param root The merkle root of the active guardian that triggered the callback
/// @return Updated cursor position
/// @return Packed callback data
function _processExpectedCallback(CalldataReader reader, bytes32 root) internal returns (CalldataReader, uint208) {
bool hasCallback;
(reader, hasCallback) = reader.readBool();
if (!hasCallback) {
return (reader, 0);
}
uint208 packedCallbackData;
(reader, packedCallbackData) = reader.readU208();
// Requirements + Effects: allow the callback
_allowCallback(root, packedCallbackData);
return (reader, packedCallbackData);
}
/// @notice Call the before submit hooks if defined
/// @param hooks Address of the submit hooks contract
/// @dev Submit hooks passed as an argument to reduce storage loading
function _beforeSubmitHooks(address hooks, bytes calldata data) internal {
if (_hasBeforeHooks(hooks)) {
// Interactions: call the before submit hooks
(bool success, bytes memory result) =
hooks.call(abi.encodeWithSelector(ISubmitHooks.beforeSubmit.selector, data, msg.sender));
// Requirements: check that the hooks call succeeded
require(success, Aera__BeforeSubmitHooksFailed(result));
}
}
/// @notice Call the after submit hooks if defined
/// @param hooks Address of the submit hooks contract
/// @dev Submit hooks passed as an argument to reduce storage loading
function _afterSubmitHooks(address hooks, bytes calldata data) internal {
if (_hasAfterHooks(hooks)) {
// Interactions: call the after submit hooks
(bool success, bytes memory result) =
hooks.call(abi.encodeWithSelector(ISubmitHooks.afterSubmit.selector, data, msg.sender));
// Invariants: check that the hooks call succeeded
require(success, Aera__AfterSubmitHooksFailed(result));
}
}
/// @notice Call the before operation hooks if defined
/// @param operationHooks Address of the operation-specific hooks
/// @param data Operation calldata
/// @param i Operation index
/// @return result Result of the hooks call
function _beforeOperationHooks(address operationHooks, bytes memory data, uint256 i)
internal
returns (bytes memory result)
{
if (_hasBeforeHooks(operationHooks)) {
// Effects: set the hook call type to before
_setHookCallType(HookCallType.BEFORE);
// Interactions: call the before operation hooks
(bool success, bytes memory returnValue) = operationHooks.call(data);
// Requirements: check that the hooks call succeeded
require(success, Aera__BeforeOperationHooksFailed(i, returnValue));
// Requirements: check that the return data length is a multiple of 32
require(returnValue.length % WORD_SIZE == 0, Aera__InvalidBeforeOperationHooksReturnDataLength());
// Effects: set the hook call type to none
_setHookCallType(HookCallType.NONE);
// Return value is ABI encoded so we need to decode it to get to the actual
// bytes value that we returned from the hooks
(result) = abi.decode(returnValue, (bytes));
}
}
/// @notice Call the after operation hooks if defined
/// @param operationHooks Address of the operation-specific hooks
/// @param data Operation calldata
/// @param i Operation index
function _afterOperationHooks(address operationHooks, bytes memory data, uint256 i) internal {
if (_hasAfterHooks(operationHooks)) {
// Effects: set the hook call type to after
_setHookCallType(HookCallType.AFTER);
// Interactions: call the after operation hooks
(bool success, bytes memory result) = operationHooks.call(data);
// Requirements: check that the hooks call succeeded
require(success, Aera__AfterOperationHooksFailed(i, result));
// Effects: set the hook call type to none
_setHookCallType(HookCallType.NONE);
}
}
/// @notice Executes a series of operations
/// @param root The merkle root of the active guardian that triggered the callback
/// @param reader Current position in the calldata
/// @param isCalledFromCallback Whether the submit is called from a callback
/// @return approvals Array of outgoing approvals created during execution
/// @return approvalsLength Length of approvals array
/// @return results Array of results from the operations
/// @return newReader Updated cursor position
/// @dev Approvals are tracked so we can verify if they have been zeroed out at the end of submit
function _executeSubmit(bytes32 root, CalldataReader reader, bool isCalledFromCallback)
internal
returns (Approval[] memory approvals, uint256 approvalsLength, bytes[] memory results, CalldataReader newReader)
{
uint256 operationsLength;
(reader, operationsLength) = reader.readU8();
results = new bytes[](operationsLength);
// There cannot be more approvals than operations
approvals = new Approval[](operationsLength);
// Safe to reuse the same variable because all its parameters get overwritten every time, except in static call
// branch where we don't verify against the merkle root
OperationContext memory ctx;
for (uint256 i = 0; i < operationsLength; ++i) {
(reader, ctx.target) = reader.readAddr();
bytes memory callData;
(reader, callData) = reader.readBytesToMemory();
reader = callData.pipe(reader, results);
bool isStaticCall;
(reader, isStaticCall) = reader.readBool();
if (isStaticCall) {
// Interactions: perform external static call
(bool success, bytes memory result) = ctx.target.staticcall(callData);
// Requirements: verify static call succeeded
require(success, Aera__SubmissionFailed(i, result));
results[i] = result;
} else {
ctx.selector = bytes4(callData);
if (_isAllowanceSelector(ctx.selector)) {
unchecked {
approvals[approvalsLength++] =
Approval({ token: ctx.target, spender: _extractApprovalSpender(callData) });
}
}
// Requirements + Effects: prepare to handle a callback if defined
(reader, ctx.callbackData) = _processExpectedCallback(reader, root);
bytes memory extractedData;
// Requirements + possible Interactions: process the operation hooks
(reader, extractedData, ctx.configurableOperationHooks, ctx.operationHooks) =
_processBeforeOperationHooks(reader, callData, i);
bytes32[] memory proof;
(reader, proof) = reader.readBytes32Array();
(reader, ctx.value) = reader.readOptionalU256();
// Requirements: verify merkle proof
_verifyOperation(proof, root, _createMerkleLeaf(ctx, extractedData));
//slither-disable-next-line arbitrary-send-eth
(bool success, bytes memory result) = ctx.target.call{ value: ctx.value }(callData);
// Requirements: check that the submission succeeded
require(success, Aera__SubmissionFailed(i, result));
if (ctx.callbackData != 0) {
// Requirements: check that the callback was received
require(_hasCallbackBeenCalled(), Aera__ExpectedCallbackNotReceived());
if (!isCalledFromCallback) {
// Effects: get the callback approvals and clear the transient storage
Approval[] memory callbackApprovals = _getCallbackApprovals();
// Invariants: verify no pending approvals from the callback
_noPendingApprovalsInvariant(callbackApprovals, callbackApprovals.length);
}
}
// possible Interactions + Requirements: call the after operation hooks if defined
_afterOperationHooks(ctx.operationHooks, callData, i);
results[i] = result;
}
}
return (approvals, approvalsLength, results, reader);
}
/// @notice Processes all hooks for operation
/// @notice Returns extracted data if configurable or contract before operation hooks is defined
/// @param reader Current position in the calldata
/// @param callData Operation calldata
/// @param i Operation index
/// @return reader Updated reader position
/// @return extractedData Extracted chunks of calldata
/// @return hooksConfigBytes hooks configuration bytes
/// @return operationHooks Operation hooks address
/// @dev Custom hooks (with contracts) can run before and after each operation but a configurable hooks
/// can only run before an operation. This function processes all of the possible configurations of hooks
/// which doesn't allow using both a custom before hook and a configurable before hook
function _processBeforeOperationHooks(CalldataReader reader, bytes memory callData, uint256 i)
internal
returns (CalldataReader, bytes memory, uint256, address)
{
uint8 hooksConfigFlag;
(reader, hooksConfigFlag) = reader.readU8();
if (hooksConfigFlag == 0) {
return (reader, "", 0, address(0));
}
uint256 calldataOffsetsCount = hooksConfigFlag & CONFIGURABLE_HOOKS_LENGTH_MASK;
// Case A: Only configurable hooks defined
if (hooksConfigFlag & HOOKS_FLAG_MASK == 0) {
uint256 calldataOffsetsPacked;
(reader, calldataOffsetsPacked) = reader.readU256();
return (
reader, callData.extract(calldataOffsetsPacked, calldataOffsetsCount), calldataOffsetsPacked, address(0)
);
}
address operationHooks;
// Case B: Both configurable and custom hooks defined
if (calldataOffsetsCount != 0) {
uint256 calldataOffsetsPacked;
(reader, calldataOffsetsPacked) = reader.readU256();
(reader, operationHooks) = reader.readAddr();
// Requirements: check that the operation hooks is not a before submit hooks
require(!_hasBeforeHooks(operationHooks), Aera__BeforeOperationHooksWithConfigurableHooks());
return (
reader,
callData.extract(calldataOffsetsPacked, calldataOffsetsCount),
calldataOffsetsPacked,
operationHooks
);
}
// Case C: only a custom hooks defined
(reader, operationHooks) = reader.readAddr();
return (
reader,
// Requirements + Interactions: call the before operation hooks if defined
_beforeOperationHooks(operationHooks, callData, i),
0,
operationHooks
);
}
/// @notice Set the submit hooks address
/// @param submitHooks_ Address of the submit hooks contract
function _setSubmitHooks(ISubmitHooks submitHooks_) internal {
// Effects: set submit hooks address
submitHooks = submitHooks_;
// Log submit hooks set
emit SubmitHooksSet(address(submitHooks_));
}
/// @notice Set the guardian root
/// @param guardian Address of the guardian
/// @param root Merkle root
function _setGuardianRoot(address guardian, bytes32 root) internal virtual {
// Requirements: check that the guardian address is not zero
require(guardian != address(0), Aera__ZeroAddressGuardian());
// Requirements: check that the guardian is whitelisted
require(WHITELIST.isWhitelisted(guardian), Aera__GuardianNotWhitelisted());
// Requirements: check that root is not zero
require(root != bytes32(0), Aera__ZeroAddressMerkleRoot());
// Effects: set guardian root
guardianRoots.set(guardian, root);
// Log guardian root set
emit GuardianRootSet(guardian, root);
}
/// @notice Set the hook call type
/// @param hookCallType The hook call type
function _setHookCallType(HookCallType hookCallType) internal {
// Effects: store the hook call type
HOOK_CALL_TYPE_SLOT.asUint256().tstore(uint8(hookCallType));
}
/// @notice Verify no pending approvals remain at the end of a submit
/// @param approvals Array of approvals to check
/// @param approvalsLength Length of approvals array
/// @dev We iterate backwards to avoid extra i variable
/// @dev While loop is preferred over for(;approvalsLength != 0;)
/// @dev Iterator variable is not used because it's not needed and decrement needs to be unchecked
function _noPendingApprovalsInvariant(Approval[] memory approvals, uint256 approvalsLength) internal view {
Approval memory approval;
while (approvalsLength != 0) {
unchecked {
--approvalsLength;
}
approval = approvals[approvalsLength];
// Requirements: verify allowance is zero
require(
// Interactions: get allowance
IERC20(approval.token).allowance(address(this), approval.spender) == 0,
Aera__AllowanceIsNotZero(approval.token, approval.spender)
);
}
}
/// @notice Get the return value from the operations
/// @param reader Current position in the calldata
/// @param results Array of results from the operations
/// @return newReader Updated reader position
/// @return returnValue Return value from the operations
function _getReturnValue(CalldataReader reader, bytes[] memory results)
internal
pure
returns (CalldataReader newReader, bytes memory returnValue)
{
uint8 returnTypeFlag;
(reader, returnTypeFlag) = reader.readU8();
if (returnTypeFlag == uint8(ReturnValueType.STATIC_RETURN)) {
(reader, returnValue) = reader.readBytesToMemory();
} else if (returnTypeFlag == uint8(ReturnValueType.DYNAMIC_RETURN)) {
uint256 length = results.length;
require(length > 0, Aera__NoResults());
unchecked {
returnValue = results[length - 1];
}
}
return (reader, returnValue);
}
/// @notice Verify an operation by validating the merkle proof
/// @param proof The merkle proof
/// @param root The merkle root
/// @param leaf The merkle leaf to verify
function _verifyOperation(bytes32[] memory proof, bytes32 root, bytes32 leaf) internal pure {
require(MerkleProof.verify(proof, root, leaf), Aera__ProofVerificationFailed());
}
/// @notice Create a merkle leaf
/// @param ctx The operation context
/// @param extractedData The extracted data
/// @return leaf The merkle leaf
function _createMerkleLeaf(OperationContext memory ctx, bytes memory extractedData)
internal
pure
returns (bytes32)
{
return keccak256(
abi.encodePacked(
ctx.target,
ctx.selector,
ctx.value > 0,
ctx.operationHooks,
ctx.configurableOperationHooks,
ctx.callbackData,
extractedData
)
);
}
/// @dev Extract spender address from approval data
/// @param data Approval calldata
/// @return spender Address of the spender
function _extractApprovalSpender(bytes memory data) internal pure returns (address spender) {
// Length check skipped intentionally, call reverts on misuse
// "memory-safe" retained for compiler optimization
assembly ("memory-safe") {
let offset := add(data, ERC20_SPENDER_OFFSET)
spender := mload(offset)
}
}
/// @dev Check if hooks needs to be called before the submit/operation
/// @param hooks Hooks address to check
/// @return True if hooks needs to be called before the submit/operation
function _hasBeforeHooks(address hooks) internal pure returns (bool) {
/// least significant bit is 1 indicating it's a before hooks
return uint160(hooks) & BEFORE_HOOK_MASK != 0;
}
/// @dev Check if submit hooks needs to be called after the submit/operation
/// @param hooks Submit hooks address to check
/// @return True if submit hooks needs to be called after the submit/operation
function _hasAfterHooks(address hooks) internal pure returns (bool) {
/// second least significant bit is 1 indicating it's a after hooks
return uint160(hooks) & AFTER_HOOK_MASK != 0;
}
/// @dev Check if the selector is an allowance handling selector
/// @param selector Selector to check
/// @return True if the selector is an allowance handling selector
function _isAllowanceSelector(bytes4 selector) internal pure returns (bool) {
return selector == IERC20.approve.selector || selector == IERC20WithAllowance.increaseAllowance.selector;
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.29;
import { IERC20 } from "@oz/interfaces/IERC20.sol";
import { Authority } from "@solmate/auth/Auth.sol";
import { IFeeCalculator } from "src/core/interfaces/IFeeCalculator.sol";
import { ISubmitHooks } from "src/core/interfaces/ISubmitHooks.sol";
import { IWhitelist } from "src/core/interfaces/IWhitelist.sol";
import { IOracle } from "src/dependencies/oracles/IOracle.sol";
/// @notice Type of request: deposit/redeem and auto/fixed price
/// @dev
/// - The order is chosen so each bit encodes a property:
/// - Bit 0: 0 = deposit, 1 = redeem
/// - Bit 1: 0 = auto price, 1 = fixed price
enum RequestType {
DEPOSIT_AUTO_PRICE, // 00: deposit, auto price
REDEEM_AUTO_PRICE, // 01: redeem, auto price
DEPOSIT_FIXED_PRICE, // 10: deposit, fixed price
REDEEM_FIXED_PRICE // 11: redeem, fixed price
}
/// @notice Type of return value: no return, static return, dynamic return
/// @dev
/// - 00: no return
/// - 01: static return - hardcoded return data
/// - 10: dynamic return - return data is extracted from the results array
enum ReturnValueType {
NO_RETURN,
STATIC_RETURN,
DYNAMIC_RETURN
}
/// @notice Type of hook call: before, after, or none
enum HookCallType {
NONE,
BEFORE,
AFTER
}
/// @notice Operation struct for vault operations
/// @dev This struct is not used directly in core logic, but included for reference and clarity
/// It illustrates the full structure of an operation without storage packing
struct Operation {
/// @notice Target contract address to call
address target;
/// @notice Calldata for the target contract
bytes data;
/// @notice Array of clipboard operations for copying return data
Clipboard[] clipboards;
/// @notice Whether to perform a static call
bool isStaticCall;
/// @notice Callback data for post-operation processing
CallbackData callbackData;
/// @notice Address of the hooks contract
address hooks;
/// @notice Array of offsets for extracting calldata
uint16[] configurableHooksOffsets;
/// @notice Merkle proof for operation verification
bytes32[] proof;
/// @notice ETH value to send with the call
uint256 value;
}
/// @notice Operation execution context data
/// @dev Used to avoid stack too deep in BaseVault._executeSubmit function
struct OperationContext {
/// @notice Address of the target contract to call
address target;
/// @notice Function selector extracted from calldata
bytes4 selector;
/// @notice Callback data packed
uint208 callbackData;
/// @notice ETH value to send with the call
uint256 value;
/// @notice Address of the operation-specific hooks contract
address operationHooks;
/// @notice Offset of the calldata extraction offsets packed in uint256
uint256 configurableOperationHooks;
}
/// @notice Struct for payable operations
struct OperationPayable {
/// @notice Target contract address
address target;
/// @notice Calldata for the target contract
bytes data;
/// @notice ETH value to send with the call
uint256 value;
}
/// @notice Struct for token approvals
struct Approval {
/// @notice Token address to approve
address token;
/// @notice Address to approve spending for
address spender;
}
/// @notice Struct for token amounts
struct TokenAmount {
/// @notice ERC20 token address
IERC20 token;
/// @notice Amount of tokens
uint256 amount;
}
/// @notice Struct for clipboard operations
struct Clipboard {
/// @notice Index of the result to copy from
uint8 resultIndex;
/// @notice Which word to copy from the result
uint8 copyWord;
/// @notice Offset to paste the copied data
uint16 pasteOffset;
}
/// @notice Struct for callback data
struct CallbackData {
/// @notice Address allowed to execute the callback
address caller;
/// @notice Function selector for the callback
bytes4 selector;
/// @notice Offset in calldata for the callback
uint16 calldataOffset;
}
/// @notice Vault parameters for vault deployment
struct BaseVaultParameters {
/// @notice Initial owner address
address owner;
/// @notice Initial authority address
Authority authority;
/// @notice Submit hooks address
ISubmitHooks submitHooks;
/// @notice Whitelist contract address
IWhitelist whitelist;
}
/// @notice Parameters for fee vault deployment
struct FeeVaultParameters {
/// @notice The fee calculator address
IFeeCalculator feeCalculator;
/// @notice The fee token address
IERC20 feeToken;
/// @notice The fee recipient address
address feeRecipient;
}
/// @notice Parameters for ERC20 deployment
struct ERC20Parameters {
/// @notice ERC20 token name
string name;
/// @notice ERC20 token symbol
string symbol;
}
/// @notice Fee structure for TVL and performance fees
/// @dev All fees are in basis points (1/10000)
struct Fee {
/// @notice TVL fee in basis points
uint16 tvl;
/// @notice Performance fee in basis points
uint16 performance;
}
/// @notice Tracks fee configuration and accrued fees for a vault
struct VaultAccruals {
/// @notice Current fee rates for the vault
Fee fees;
/// @notice Accrued fees for the vault fee recipient
uint112 accruedFees;
/// @notice Total protocol fees accrued but not claimed
uint112 accruedProtocolFees;
}
/// @notice Complete state of a vault's fee configuration and accruals
struct VaultSnapshot {
/// @notice Timestamp of last fee accrual
uint32 lastFeeAccrual;
/// @notice Timestamp when snapshot was taken
uint32 timestamp;
/// @notice Timestamp when snapshot is finalized after dispute period
uint32 finalizedAt;
/// @notice Average value of vault assets during snapshot period
uint160 averageValue;
/// @notice Highest profit achieved during snapshot period
uint128 highestProfit;
/// @notice Highest profit achieved in previous periods
uint128 lastHighestProfit;
}
/// @notice Struct for target and calldata
struct TargetCalldata {
/// @notice Target contract address
address target;
/// @notice Calldata for the target contract
bytes data;
}
/// @notice Vault price information and configuration
struct VaultPriceState {
/// @notice Whether vault price updates are paused
bool paused;
/// @notice Maximum age of price data in seconds before it is considered stale
uint8 maxPriceAge;
/// @notice Minimum time between price updates in minutes
uint16 minUpdateIntervalMinutes;
/// @notice Maximum allowed price increase ratio in basis points
uint16 maxPriceToleranceRatio;
/// @notice Minimum allowed price decrease ratio in basis points
uint16 minPriceToleranceRatio;
/// @notice Maximum allowed delay in price updates in days
uint8 maxUpdateDelayDays;
/// @notice Timestamp of last price update
uint32 timestamp;
/// @notice Seconds between last fee accrual and last price update
uint24 accrualLag;
/// @notice Current unit price
uint128 unitPrice;
/// @notice Highest historical unit price
uint128 highestPrice;
/// @notice Total supply at last price update
uint128 lastTotalSupply;
}
/// @notice Token configuration for deposits and redemptions
struct TokenDetails {
/// @notice Whether async deposits are enabled
bool asyncDepositEnabled;
/// @notice Whether async redemptions are enabled
bool asyncRedeemEnabled;
/// @notice Whether sync deposits are enabled
bool syncDepositEnabled;
/// @notice Premium multiplier applied to deposits in basis points (9999 = 0.1% premium)
uint16 depositMultiplier;
/// @notice Premium multiplier applied to redemptions in basis points (9999 = 0.1% premium)
uint16 redeemMultiplier;
}
/// @notice Request parameters for deposits and redemptions
/// @dev
/// - For deposits:
/// - units: minimum units the user wants to receive (minUnitsOut)
/// - tokens: amount of tokens the user is providing (tokensIn)
/// - For redemptions:
/// - units: amount of units the user is redeeming (unitsIn)
/// - tokens: minimum tokens the user wants to receive (minTokensOut)
struct Request {
/// @notice Request type(deposit/redeem + auto/fixed price)
RequestType requestType;
/// @notice User address making the request
address user;
/// @notice Amount of vault units
uint256 units;
/// @notice Amount of underlying tokens
uint256 tokens;
/// @notice Tip paid to solver, always in tokens
uint256 solverTip;
/// @notice Timestamp after which request expires
uint256 deadline;
/// @notice Maximum age of price data allowed
uint256 maxPriceAge;
}
/// @notice Oracle data for a base/quote pair, including current, pending, and status flags
struct OracleData {
/// @notice True if an oracle update is scheduled
bool isScheduledForUpdate;
/// @notice True if the current oracle is disabled
bool isDisabled;
/// @notice The currently active oracle
IOracle oracle;
/// @notice The pending oracle to be activated after delay
IOracle pendingOracle;
/// @notice Timestamp at which the pending oracle can be committed
uint32 commitTimestamp;
}// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.29;
/// @title IFeeCalculator
/// @notice Interface for a contract that calculates fees for a vault and protocol
interface IFeeCalculator {
////////////////////////////////////////////////////////////
// Events //
////////////////////////////////////////////////////////////
/// @notice Emitted when a new vault is registered
/// @param vault The address of the registered vault
event VaultRegistered(address indexed vault);
////////////////////////////////////////////////////////////
// Errors //
////////////////////////////////////////////////////////////
/// @notice Thrown when attempting to register an already registered vault
error Aera__VaultAlreadyRegistered();
////////////////////////////////////////////////////////////
// Functions //
////////////////////////////////////////////////////////////
/// @notice Register a new vault with the fee calculator
function registerVault() external;
/// @notice Process a fee claim for a specific vault
/// @param feeTokenBalance Available fee token balance to distribute
/// @return earnedFees The amount of fees to be claimed by the fee recipient
/// @return protocolEarnedFees The amount of protocol fees to be claimed by the protocol
/// @return protocolFeeRecipient The address of the protocol fee recipient
/// @dev Expected to be called by the vault only when claiming fees
/// Only accrues fees and updates stored values; does not transfer tokens
/// Caller must perform the actual transfers to avoid permanent fee loss
function claimFees(uint256 feeTokenBalance) external returns (uint256, uint256, address);
/// @notice Process a protocol fee claim for a vault
/// @param feeTokenBalance Available fee token balance to distribute
/// @return accruedFees The amount of protocol fees claimed
/// @return protocolFeeRecipient The address of the protocol fee recipient
/// @dev Expected to be called by the vault only when claiming protocol fees
/// Only accrues protocol fees and updates stored values; does not transfer tokens
/// Caller must perform the actual transfers to avoid permanent protocol fee loss
function claimProtocolFees(uint256 feeTokenBalance) external returns (uint256, address);
/// @notice Returns the current claimable fees for the given vault, as if a claim was made now
/// @param vault The address of the vault to preview fees for
/// @param feeTokenBalance Available fee token balance to distribute
/// If set to `type(uint256).max`, the function returns all accrued fees
/// If set to an actual balance, the result is capped to that claimable amount
/// @return vaultFees The amount of claimable fees for the vault
/// @return protocolFees The amount of claimable protocol fees
function previewFees(address vault, uint256 feeTokenBalance)
external
view
returns (uint256 vaultFees, uint256 protocolFees);
/// @notice Returns the address that receives protocol fees
/// @return The address that receives the protocol fees
function protocolFeeRecipient() external view returns (address);
}// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.29;
import { IERC20 } from "@oz/interfaces/IERC20.sol";
import { IFeeCalculator } from "src/core/interfaces/IFeeCalculator.sol";
/// @title IFeeVault
/// @notice Interface for vaults that support fees but don't have multiple depositors
interface IFeeVault {
////////////////////////////////////////////////////////////
// Events //
////////////////////////////////////////////////////////////
event FeesClaimed(address indexed feeRecipient, uint256 fees);
event ProtocolFeesClaimed(address indexed protocolFeeRecipient, uint256 protocolEarnedFees);
event FeeRecipientUpdated(address indexed newFeeRecipient);
event FeeCalculatorUpdated(address indexed newFeeCalculator);
////////////////////////////////////////////////////////////
// Errors //
////////////////////////////////////////////////////////////
error Aera__ZeroAddressFeeCalculator();
error Aera__ZeroAddressFeeToken();
error Aera__ZeroAddressFeeRecipient();
error Aera__NoFeesToClaim();
error Aera__CallerIsNotFeeRecipient();
error Aera__CallerIsNotProtocolFeeRecipient();
////////////////////////////////////////////////////////////
// Functions //
////////////////////////////////////////////////////////////
/// @notice Set the fee recipient
/// @param newFeeRecipient The new fee recipient address
function setFeeRecipient(address newFeeRecipient) external;
/// @notice Claim accrued fees for msg.sender
/// @dev Automatically claims any earned protocol fees for the protocol
/// @return feeRecipientFees The amount of fees to be claimed by the fee recipient
/// @return protocolFees The amount of protocol fees to be claimed by the protocol
function claimFees() external returns (uint256 feeRecipientFees, uint256 protocolFees);
/// @notice Claim accrued protocol fees
/// @return protocolFees The amount of protocol fees to be claimed by the protocol
function claimProtocolFees() external returns (uint256 protocolFees);
/// @notice Set the fee calculator
/// @dev newFeeCalculator can be zero, which has the effect as disabling the fee calculator
/// @param newFeeCalculator The new fee calculator
function setFeeCalculator(IFeeCalculator newFeeCalculator) external;
/// @notice Get the fee calculator
// solhint-disable-next-line func-name-mixedcase
function feeCalculator() external view returns (IFeeCalculator);
/// @notice Get the fee token
// solhint-disable-next-line func-name-mixedcase
function FEE_TOKEN() external view returns (IERC20);
}// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.29;
import { FeeVaultParameters } from "src/core/Types.sol";
import { IBaseVaultDeployer } from "src/core/interfaces/IBaseVaultDeployer.sol";
/// @title IFeeVaultDeployer
/// @notice Interface for the fee vault deployer
interface IFeeVaultDeployer is IBaseVaultDeployer {
////////////////////////////////////////////////////////////
// Functions //
////////////////////////////////////////////////////////////
/// @notice Get the deployment parameters for the fee vault
/// @return params Deployment parameters for the fee vault
function feeVaultParameters() external view returns (FeeVaultParameters memory params);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC165.sol)
pragma solidity ^0.8.20;
import {IERC165} from "../utils/introspection/IERC165.sol";// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
/**
* @dev Collection of common custom errors used in multiple contracts
*
* IMPORTANT: Backwards compatibility is not guaranteed in future versions of the library.
* It is recommended to avoid relying on the error API for critical functionality.
*
* _Available since v5.1._
*/
library Errors {
/**
* @dev The ETH balance of the account is not enough to perform the operation.
*/
error InsufficientBalance(uint256 balance, uint256 needed);
/**
* @dev A call to an address target failed. The target may have reverted.
*/
error FailedCall();
/**
* @dev The deployment failed.
*/
error FailedDeployment();
/**
* @dev A necessary precompile is missing.
*/
error MissingPrecompile(address);
}// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.29;
import { TransientSlot } from "@oz/utils/TransientSlot.sol";
import {
ADDRESS_SIZE_BITS,
CALLBACK_DATA_OFFSET,
NO_CALLBACK_DATA,
SELECTOR_OFFSET,
// solhint-disable-next-line no-unused-import
WORD_SIZE
} from "src/core/Constants.sol";
import { Approval } from "src/core/Types.sol";
import { ICallbackHandler } from "src/core/interfaces/ICallbackHandler.sol";
/// @title CallbackHandler
/// @notice Handles callback validation and execution for vault operations. This contract is designed to be
/// used as a mixin in BaseVault, providing the ability to register logic for safely handling callbacks during
/// guardian submissions. A common use case for handlers is receiving a flash loan. To receive a flashloan, the vault
/// has to cede control when requesting a flashloan and then atomically handle the callback to repay the flashloan
/// This requires two capabilities: the ability to register new handlers and the ability to initiate additional
/// operations in the handle while being restricted by the merkle tree constraints. The callback handler contract
/// achieves this by allowing guardians to "prepare" for a callback when they construct a given operation. If the
/// operation "has a callback" then the fallback function in this contract will handle it. It will use transient storage
/// to preserve information such as the expected callback caller, function selector of the callback and any
/// approvals that are created during the callback
/// @dev Uses transient storage to manage callback state and approvals
abstract contract CallbackHandler is ICallbackHandler {
using TransientSlot for *;
////////////////////////////////////////////////////////////
// Constants //
////////////////////////////////////////////////////////////
/// @notice ERC7201-compliant transient storage slot for storing the next authorized selector + caller
/// @dev Equal to keccak256(abi.encode(uint256(keccak256("aera.callbackHandler.call")) - 1)) &
/// ~bytes32(uint256(0xff));
/// @custom:security Critical for callback validation
bytes32 internal constant CALLBACK_CALL_SLOT = 0xa48fd101fc9f41f09dc754b3b14722487070ffbd61259b49558564a3296a3f00;
/// @notice ERC7201-compliant transient storage slot for storing the callback merkle root
/// @dev Equal to keccak256(abi.encode(uint256(keccak256("aera.callbackHandler.merkleRoot")) - 1) &
/// ~bytes32(uint256(0xff));
/// @custom:security Critical for callback validation
bytes32 internal constant CALLBACK_MERKLE_ROOT_SLOT =
0x30fb041442610fd0a22e4654f60ea1c715088ef7320b5ec0c4e75cbdd99dbe00;
/// @notice ERC7201-compliant transient storage slot for storing the approval tracking
/// @dev Equal to keccak256(abi.encode(uint256(keccak256("aera.callbackHandler.approvals")) - 1)) &
/// ~bytes32(uint256(0xff));
/// @custom:security Critical for tracking token approvals during callbacks
bytes32 internal constant APPROVALS_SLOT = 0xba2cfcc1b17a97110b1fb218b61c42c0e510c6913e669a69d4ade619ace66c00;
////////////////////////////////////////////////////////////
// Public / External Functions //
////////////////////////////////////////////////////////////
/// @notice Handle incoming callbacks and validates their authorization
/// @dev Extracts callback data and forwards to _handleCallbackOperations if valid
fallback(bytes calldata) external returns (bytes memory returnValue) {
(address caller, bytes4 selector, uint16 userDataOffset) = _getAllowedCallback();
// Requirements: check that the selector matches
require(msg.sig == selector, Aera__UnauthorizedCallback());
// Requirements: check that the caller matches
require(msg.sender == caller, Aera__UnauthorizedCallback());
bytes32 root = _getAllowedMerkleRoot();
// Requirements: check that the merkle root is not zero
require(root != bytes32(0), Aera__UnauthorizedCallback());
// If NO_CALLBACK_DATA magic value is sent, we don't need to return anything
if (userDataOffset == NO_CALLBACK_DATA) return bytes("");
// Effects, Interactions: handle callback operations
returnValue = _handleCallbackOperations(root, userDataOffset);
}
////////////////////////////////////////////////////////////
// Private / Internal Functions //
////////////////////////////////////////////////////////////
/// @notice Internal handler for validated callbacks
/// @dev Callback operations are like regular operations, but with a return value, which are encoded after
/// operations array
/// ┌─────────────────────────────┬─────────────────────────┬───────────────────────────────────────────────┐
/// │ FIELDS │ SIZE │ DESCRIPTION │
/// ├─────────────────────────────┴─────────────────────────┴───────────────────────────────────────────────┤
/// │ returnTypeFlag 1 byte 0 = no return, 1 = static, 2 = dynamic │
/// │ [if returnTypeFlag == 1]: │
/// │ returnDataLength 2 bytes Length of return data │
/// │ returnData <returnDataLength> bytes Static return data │
/// └───────────────────────────────────────────────────────────────────────────────────────────────────────┘
/// @param root The merkle root of the callback
/// @param cursor The cursor to the callback data
/// @return returnValue The return value of the callback
function _handleCallbackOperations(bytes32 root, uint256 cursor)
internal
virtual
returns (bytes memory returnValue);
/// @notice Whitelist a function selector and caller as a valid callback
/// @dev Uses transient storage to store the callback data
/// @param root The merkle root of the callback
/// @param packedCallbackData Packed data containing caller, selector, and offsets
function _allowCallback(bytes32 root, uint256 packedCallbackData) internal {
// Effects: store the callback data
CALLBACK_CALL_SLOT.asUint256().tstore(packedCallbackData);
// Effects: store the merkle root
CALLBACK_MERKLE_ROOT_SLOT.asBytes32().tstore(root);
}
/// @notice Store approvals for the current callback context
/// @param approvals Array of token approvals to store
/// @param length Length of the array
/// @dev Uses transient storage to track approvals during callback execution
/// @dev Length of the array, packed with the token address will be stored in the first slot
/// @dev All other elements are laid out sequentially after the first slot, taking 2 slots per approval
/// @dev If there are existing approvals, we will update length in the slot zero and append new approvals
function _storeCallbackApprovals(Approval[] memory approvals, uint256 length) internal {
if (length == 0) return;
uint256 existingApproval = APPROVALS_SLOT.asUint256().tload();
uint256 existingLength = existingApproval >> ADDRESS_SIZE_BITS;
uint256 i;
uint256 currentSlot = uint256(APPROVALS_SLOT);
Approval memory approval;
if (existingLength == 0) {
approval = approvals[0];
unchecked {
// Effects: store the token and spender
/// @dev Store packed token and length in the zero slot, and spender in the second
bytes32(currentSlot).asUint256().tstore(_packLengthAndToken(length, approval.token));
bytes32(++currentSlot).asAddress().tstore(approval.spender);
}
i = 1;
} else {
unchecked {
uint256 newLength = existingLength + length;
// Effects: store the token and spender
/// @dev Update the length and preserve the token in the zero slot
bytes32(currentSlot).asUint256().tstore(
_packLengthAndToken(newLength, address(uint160(existingApproval)))
);
/// Minus one to compensate for pre-increment in upcoming storage loop
currentSlot += existingLength * 2 - 1;
}
}
for (; i < length; ++i) {
approval = approvals[i];
unchecked {
// Effects: store the token and spender
bytes32(++currentSlot).asAddress().tstore(approval.token);
bytes32(++currentSlot).asAddress().tstore(approval.spender);
}
}
}
/// @notice Retrieve the currently allowed callback data
/// @dev Unpacks data from transient storage
/// @return caller The authorized caller address
/// @return selector The authorized function selector
/// @return userDataOffset The offset in calldata where user data begins
/// @custom:security Critical for callback validation
function _getAllowedCallback() internal returns (address caller, bytes4 selector, uint16 userDataOffset) {
(caller, selector, userDataOffset) = _unpackCallbackData(CALLBACK_CALL_SLOT.asUint256().tload());
// Effects: clear the transient storage slot
CALLBACK_CALL_SLOT.asUint256().tstore(0);
}
/// @notice Retrieves the currently allowed merkle root
/// @dev Unpacks data from transient storage
/// @return root The authorized merkle root
/// @custom:security Critical for callback validation
function _getAllowedMerkleRoot() internal returns (bytes32 root) {
root = CALLBACK_MERKLE_ROOT_SLOT.asBytes32().tload();
// Effects: clear the merkle root slot
CALLBACK_MERKLE_ROOT_SLOT.asBytes32().tstore(bytes32(0));
}
/// @notice Retrieves the current callback approvals
/// @dev Decodes approvals from transient storage
/// @return approvals Array of current token approvals
/// @dev The first slot contains the length of the array, packed with the token address
/// @dev All other elements are laid out sequentially after the first slot, taking 2 slots per approval
/// @dev Only length slot is cleared, the rest of the approvals are left in the transient storage
/// @dev This is safe because even if new approvals are added, old ones will be overwritten for length slots
function _getCallbackApprovals() internal returns (Approval[] memory approvals) {
uint256 lengthWithToken = APPROVALS_SLOT.asUint256().tload();
uint256 length = lengthWithToken >> ADDRESS_SIZE_BITS;
if (length == 0) return approvals;
// Effects: clear the approvals length slot
APPROVALS_SLOT.asUint256().tstore(0);
approvals = new Approval[](length);
address token = address(uint160(lengthWithToken));
uint256 slotUint256 = uint256(APPROVALS_SLOT);
address spender;
unchecked {
spender = bytes32(++slotUint256).asAddress().tload();
}
approvals[0] = Approval({ token: token, spender: spender });
for (uint256 i = 1; i < length; ++i) {
unchecked {
token = bytes32(++slotUint256).asAddress().tload();
spender = bytes32(++slotUint256).asAddress().tload();
}
approvals[i] = Approval({ token: token, spender: spender });
}
}
/// @notice Checks if an expected callback has been called
/// @dev If callback was expected but not received, CALLBACK_CALL_SLOT will not be reset to 0
/// @return True if an expected callback has been called, false otherwise
function _hasCallbackBeenCalled() internal view returns (bool) {
return CALLBACK_CALL_SLOT.asUint256().tload() == 0;
}
/// @notice Unpacks callback data from a packed uint256
/// @param packed The packed uint256 containing callback data
/// @return target The target address
/// @return selector The function selector
/// @return dataOffset The offset in calldata where user data begins
function _unpackCallbackData(uint256 packed)
private
pure
returns (address target, bytes4 selector, uint16 dataOffset)
{
target = address(uint160(packed));
selector = bytes4(bytes32(packed << SELECTOR_OFFSET));
dataOffset = uint16(packed >> CALLBACK_DATA_OFFSET);
}
/// @notice Packs a token address and length into a uint256
/// @dev Used in transient storage slot zero
/// @dev `length` is required to be less than `type(uint96).max + 1`
/// @param length The length of the approvals array
/// @param token The token address
/// @return packed The packed uint256
function _packLengthAndToken(uint256 length, address token) private pure returns (uint256) {
return uint160(token) | (length << ADDRESS_SIZE_BITS);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC721Receiver.sol)
pragma solidity ^0.8.20;
import {IERC721Receiver} from "../token/ERC721/IERC721Receiver.sol";// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Pausable.sol)
pragma solidity ^0.8.20;
import {Context} from "../utils/Context.sol";
/**
* @dev Contract module which allows children to implement an emergency stop
* mechanism that can be triggered by an authorized account.
*
* This module is used through inheritance. It will make available the
* modifiers `whenNotPaused` and `whenPaused`, which can be applied to
* the functions of your contract. Note that they will not be pausable by
* simply including this module, only once the modifiers are put in place.
*/
abstract contract Pausable is Context {
bool private _paused;
/**
* @dev Emitted when the pause is triggered by `account`.
*/
event Paused(address account);
/**
* @dev Emitted when the pause is lifted by `account`.
*/
event Unpaused(address account);
/**
* @dev The operation failed because the contract is paused.
*/
error EnforcedPause();
/**
* @dev The operation failed because the contract is not paused.
*/
error ExpectedPause();
/**
* @dev Initializes the contract in unpaused state.
*/
constructor() {
_paused = false;
}
/**
* @dev Modifier to make a function callable only when the contract is not paused.
*
* Requirements:
*
* - The contract must not be paused.
*/
modifier whenNotPaused() {
_requireNotPaused();
_;
}
/**
* @dev Modifier to make a function callable only when the contract is paused.
*
* Requirements:
*
* - The contract must be paused.
*/
modifier whenPaused() {
_requirePaused();
_;
}
/**
* @dev Returns true if the contract is paused, and false otherwise.
*/
function paused() public view virtual returns (bool) {
return _paused;
}
/**
* @dev Throws if the contract is paused.
*/
function _requireNotPaused() internal view virtual {
if (paused()) {
revert EnforcedPause();
}
}
/**
* @dev Throws if the contract is not paused.
*/
function _requirePaused() internal view virtual {
if (!paused()) {
revert ExpectedPause();
}
}
/**
* @dev Triggers stopped state.
*
* Requirements:
*
* - The contract must not be paused.
*/
function _pause() internal virtual whenNotPaused {
_paused = true;
emit Paused(_msgSender());
}
/**
* @dev Returns to normal state.
*
* Requirements:
*
* - The contract must be paused.
*/
function _unpause() internal virtual whenPaused {
_paused = false;
emit Unpaused(_msgSender());
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import {TransientSlot} from "./TransientSlot.sol";
/**
* @dev Variant of {ReentrancyGuard} that uses transient storage.
*
* NOTE: This variant only works on networks where EIP-1153 is available.
*
* _Available since v5.1._
*/
abstract contract ReentrancyGuardTransient {
using TransientSlot for *;
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ReentrancyGuard")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant REENTRANCY_GUARD_STORAGE =
0x9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00;
/**
* @dev Unauthorized reentrant call.
*/
error ReentrancyGuardReentrantCall();
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
// On the first call to nonReentrant, _status will be NOT_ENTERED
if (_reentrancyGuardEntered()) {
revert ReentrancyGuardReentrantCall();
}
// Any calls to nonReentrant after this point will fail
REENTRANCY_GUARD_STORAGE.asBoolean().tstore(true);
}
function _nonReentrantAfter() private {
REENTRANCY_GUARD_STORAGE.asBoolean().tstore(false);
}
/**
* @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
* `nonReentrant` function in the call stack.
*/
function _reentrancyGuardEntered() internal view returns (bool) {
return REENTRANCY_GUARD_STORAGE.asBoolean().tload();
}
}// SPDX-License-Identifier: MIT
// This file was procedurally generated from scripts/generate/templates/TransientSlot.js.
pragma solidity ^0.8.24;
/**
* @dev Library for reading and writing value-types to specific transient storage slots.
*
* Transient slots are often used to store temporary values that are removed after the current transaction.
* This library helps with reading and writing to such slots without the need for inline assembly.
*
* * Example reading and writing values using transient storage:
* ```solidity
* contract Lock {
* using TransientSlot for *;
*
* // Define the slot. Alternatively, use the SlotDerivation library to derive the slot.
* bytes32 internal constant _LOCK_SLOT = 0xf4678858b2b588224636b8522b729e7722d32fc491da849ed75b3fdf3c84f542;
*
* modifier locked() {
* require(!_LOCK_SLOT.asBoolean().tload());
*
* _LOCK_SLOT.asBoolean().tstore(true);
* _;
* _LOCK_SLOT.asBoolean().tstore(false);
* }
* }
* ```
*
* TIP: Consider using this library along with {SlotDerivation}.
*/
library TransientSlot {
/**
* @dev UDVT that represent a slot holding a address.
*/
type AddressSlot is bytes32;
/**
* @dev Cast an arbitrary slot to a AddressSlot.
*/
function asAddress(bytes32 slot) internal pure returns (AddressSlot) {
return AddressSlot.wrap(slot);
}
/**
* @dev UDVT that represent a slot holding a bool.
*/
type BooleanSlot is bytes32;
/**
* @dev Cast an arbitrary slot to a BooleanSlot.
*/
function asBoolean(bytes32 slot) internal pure returns (BooleanSlot) {
return BooleanSlot.wrap(slot);
}
/**
* @dev UDVT that represent a slot holding a bytes32.
*/
type Bytes32Slot is bytes32;
/**
* @dev Cast an arbitrary slot to a Bytes32Slot.
*/
function asBytes32(bytes32 slot) internal pure returns (Bytes32Slot) {
return Bytes32Slot.wrap(slot);
}
/**
* @dev UDVT that represent a slot holding a uint256.
*/
type Uint256Slot is bytes32;
/**
* @dev Cast an arbitrary slot to a Uint256Slot.
*/
function asUint256(bytes32 slot) internal pure returns (Uint256Slot) {
return Uint256Slot.wrap(slot);
}
/**
* @dev UDVT that represent a slot holding a int256.
*/
type Int256Slot is bytes32;
/**
* @dev Cast an arbitrary slot to a Int256Slot.
*/
function asInt256(bytes32 slot) internal pure returns (Int256Slot) {
return Int256Slot.wrap(slot);
}
/**
* @dev Load the value held at location `slot` in transient storage.
*/
function tload(AddressSlot slot) internal view returns (address value) {
assembly ("memory-safe") {
value := tload(slot)
}
}
/**
* @dev Store `value` at location `slot` in transient storage.
*/
function tstore(AddressSlot slot, address value) internal {
assembly ("memory-safe") {
tstore(slot, value)
}
}
/**
* @dev Load the value held at location `slot` in transient storage.
*/
function tload(BooleanSlot slot) internal view returns (bool value) {
assembly ("memory-safe") {
value := tload(slot)
}
}
/**
* @dev Store `value` at location `slot` in transient storage.
*/
function tstore(BooleanSlot slot, bool value) internal {
assembly ("memory-safe") {
tstore(slot, value)
}
}
/**
* @dev Load the value held at location `slot` in transient storage.
*/
function tload(Bytes32Slot slot) internal view returns (bytes32 value) {
assembly ("memory-safe") {
value := tload(slot)
}
}
/**
* @dev Store `value` at location `slot` in transient storage.
*/
function tstore(Bytes32Slot slot, bytes32 value) internal {
assembly ("memory-safe") {
tstore(slot, value)
}
}
/**
* @dev Load the value held at location `slot` in transient storage.
*/
function tload(Uint256Slot slot) internal view returns (uint256 value) {
assembly ("memory-safe") {
value := tload(slot)
}
}
/**
* @dev Store `value` at location `slot` in transient storage.
*/
function tstore(Uint256Slot slot, uint256 value) internal {
assembly ("memory-safe") {
tstore(slot, value)
}
}
/**
* @dev Load the value held at location `slot` in transient storage.
*/
function tload(Int256Slot slot) internal view returns (int256 value) {
assembly ("memory-safe") {
value := tload(slot)
}
}
/**
* @dev Store `value` at location `slot` in transient storage.
*/
function tstore(Int256Slot slot, int256 value) internal {
assembly ("memory-safe") {
tstore(slot, value)
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/MerkleProof.sol)
// This file was procedurally generated from scripts/generate/templates/MerkleProof.js.
pragma solidity ^0.8.20;
import {Hashes} from "./Hashes.sol";
/**
* @dev These functions deal with verification of Merkle Tree proofs.
*
* The tree and the proofs can be generated using our
* https://github.com/OpenZeppelin/merkle-tree[JavaScript library].
* You will find a quickstart guide in the readme.
*
* WARNING: You should avoid using leaf values that are 64 bytes long prior to
* hashing, or use a hash function other than keccak256 for hashing leaves.
* This is because the concatenation of a sorted pair of internal nodes in
* the Merkle tree could be reinterpreted as a leaf value.
* OpenZeppelin's JavaScript library generates Merkle trees that are safe
* against this attack out of the box.
*
* IMPORTANT: Consider memory side-effects when using custom hashing functions
* that access memory in an unsafe way.
*
* NOTE: This library supports proof verification for merkle trees built using
* custom _commutative_ hashing functions (i.e. `H(a, b) == H(b, a)`). Proving
* leaf inclusion in trees built using non-commutative hashing functions requires
* additional logic that is not supported by this library.
*/
library MerkleProof {
/**
*@dev The multiproof provided is not valid.
*/
error MerkleProofInvalidMultiproof();
/**
* @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree
* defined by `root`. For this, a `proof` must be provided, containing
* sibling hashes on the branch from the leaf to the root of the tree. Each
* pair of leaves and each pair of pre-images are assumed to be sorted.
*
* This version handles proofs in memory with the default hashing function.
*/
function verify(bytes32[] memory proof, bytes32 root, bytes32 leaf) internal pure returns (bool) {
return processProof(proof, leaf) == root;
}
/**
* @dev Returns the rebuilt hash obtained by traversing a Merkle tree up
* from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt
* hash matches the root of the tree. When processing the proof, the pairs
* of leaves & pre-images are assumed to be sorted.
*
* This version handles proofs in memory with the default hashing function.
*/
function processProof(bytes32[] memory proof, bytes32 leaf) internal pure returns (bytes32) {
bytes32 computedHash = leaf;
for (uint256 i = 0; i < proof.length; i++) {
computedHash = Hashes.commutativeKeccak256(computedHash, proof[i]);
}
return computedHash;
}
/**
* @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree
* defined by `root`. For this, a `proof` must be provided, containing
* sibling hashes on the branch from the leaf to the root of the tree. Each
* pair of leaves and each pair of pre-images are assumed to be sorted.
*
* This version handles proofs in memory with a custom hashing function.
*/
function verify(
bytes32[] memory proof,
bytes32 root,
bytes32 leaf,
function(bytes32, bytes32) view returns (bytes32) hasher
) internal view returns (bool) {
return processProof(proof, leaf, hasher) == root;
}
/**
* @dev Returns the rebuilt hash obtained by traversing a Merkle tree up
* from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt
* hash matches the root of the tree. When processing the proof, the pairs
* of leaves & pre-images are assumed to be sorted.
*
* This version handles proofs in memory with a custom hashing function.
*/
function processProof(
bytes32[] memory proof,
bytes32 leaf,
function(bytes32, bytes32) view returns (bytes32) hasher
) internal view returns (bytes32) {
bytes32 computedHash = leaf;
for (uint256 i = 0; i < proof.length; i++) {
computedHash = hasher(computedHash, proof[i]);
}
return computedHash;
}
/**
* @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree
* defined by `root`. For this, a `proof` must be provided, containing
* sibling hashes on the branch from the leaf to the root of the tree. Each
* pair of leaves and each pair of pre-images are assumed to be sorted.
*
* This version handles proofs in calldata with the default hashing function.
*/
function verifyCalldata(bytes32[] calldata proof, bytes32 root, bytes32 leaf) internal pure returns (bool) {
return processProofCalldata(proof, leaf) == root;
}
/**
* @dev Returns the rebuilt hash obtained by traversing a Merkle tree up
* from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt
* hash matches the root of the tree. When processing the proof, the pairs
* of leaves & pre-images are assumed to be sorted.
*
* This version handles proofs in calldata with the default hashing function.
*/
function processProofCalldata(bytes32[] calldata proof, bytes32 leaf) internal pure returns (bytes32) {
bytes32 computedHash = leaf;
for (uint256 i = 0; i < proof.length; i++) {
computedHash = Hashes.commutativeKeccak256(computedHash, proof[i]);
}
return computedHash;
}
/**
* @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree
* defined by `root`. For this, a `proof` must be provided, containing
* sibling hashes on the branch from the leaf to the root of the tree. Each
* pair of leaves and each pair of pre-images are assumed to be sorted.
*
* This version handles proofs in calldata with a custom hashing function.
*/
function verifyCalldata(
bytes32[] calldata proof,
bytes32 root,
bytes32 leaf,
function(bytes32, bytes32) view returns (bytes32) hasher
) internal view returns (bool) {
return processProofCalldata(proof, leaf, hasher) == root;
}
/**
* @dev Returns the rebuilt hash obtained by traversing a Merkle tree up
* from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt
* hash matches the root of the tree. When processing the proof, the pairs
* of leaves & pre-images are assumed to be sorted.
*
* This version handles proofs in calldata with a custom hashing function.
*/
function processProofCalldata(
bytes32[] calldata proof,
bytes32 leaf,
function(bytes32, bytes32) view returns (bytes32) hasher
) internal view returns (bytes32) {
bytes32 computedHash = leaf;
for (uint256 i = 0; i < proof.length; i++) {
computedHash = hasher(computedHash, proof[i]);
}
return computedHash;
}
/**
* @dev Returns true if the `leaves` can be simultaneously proven to be a part of a Merkle tree defined by
* `root`, according to `proof` and `proofFlags` as described in {processMultiProof}.
*
* This version handles multiproofs in memory with the default hashing function.
*
* CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details.
*
* NOTE: Consider the case where `root == proof[0] && leaves.length == 0` as it will return `true`.
* The `leaves` must be validated independently. See {processMultiProof}.
*/
function multiProofVerify(
bytes32[] memory proof,
bool[] memory proofFlags,
bytes32 root,
bytes32[] memory leaves
) internal pure returns (bool) {
return processMultiProof(proof, proofFlags, leaves) == root;
}
/**
* @dev Returns the root of a tree reconstructed from `leaves` and sibling nodes in `proof`. The reconstruction
* proceeds by incrementally reconstructing all inner nodes by combining a leaf/inner node with either another
* leaf/inner node or a proof sibling node, depending on whether each `proofFlags` item is true or false
* respectively.
*
* This version handles multiproofs in memory with the default hashing function.
*
* CAUTION: Not all Merkle trees admit multiproofs. To use multiproofs, it is sufficient to ensure that: 1) the tree
* is complete (but not necessarily perfect), 2) the leaves to be proven are in the opposite order they are in the
* tree (i.e., as seen from right to left starting at the deepest layer and continuing at the next layer).
*
* NOTE: The _empty set_ (i.e. the case where `proof.length == 1 && leaves.length == 0`) is considered a no-op,
* and therefore a valid multiproof (i.e. it returns `proof[0]`). Consider disallowing this case if you're not
* validating the leaves elsewhere.
*/
function processMultiProof(
bytes32[] memory proof,
bool[] memory proofFlags,
bytes32[] memory leaves
) internal pure returns (bytes32 merkleRoot) {
// This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by
// consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
// `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
// the Merkle tree.
uint256 leavesLen = leaves.length;
uint256 proofFlagsLen = proofFlags.length;
// Check proof validity.
if (leavesLen + proof.length != proofFlagsLen + 1) {
revert MerkleProofInvalidMultiproof();
}
// The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
// `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
bytes32[] memory hashes = new bytes32[](proofFlagsLen);
uint256 leafPos = 0;
uint256 hashPos = 0;
uint256 proofPos = 0;
// At each step, we compute the next hash using two values:
// - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
// get the next hash.
// - depending on the flag, either another value from the "main queue" (merging branches) or an element from the
// `proof` array.
for (uint256 i = 0; i < proofFlagsLen; i++) {
bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
bytes32 b = proofFlags[i]
? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++])
: proof[proofPos++];
hashes[i] = Hashes.commutativeKeccak256(a, b);
}
if (proofFlagsLen > 0) {
if (proofPos != proof.length) {
revert MerkleProofInvalidMultiproof();
}
unchecked {
return hashes[proofFlagsLen - 1];
}
} else if (leavesLen > 0) {
return leaves[0];
} else {
return proof[0];
}
}
/**
* @dev Returns true if the `leaves` can be simultaneously proven to be a part of a Merkle tree defined by
* `root`, according to `proof` and `proofFlags` as described in {processMultiProof}.
*
* This version handles multiproofs in memory with a custom hashing function.
*
* CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details.
*
* NOTE: Consider the case where `root == proof[0] && leaves.length == 0` as it will return `true`.
* The `leaves` must be validated independently. See {processMultiProof}.
*/
function multiProofVerify(
bytes32[] memory proof,
bool[] memory proofFlags,
bytes32 root,
bytes32[] memory leaves,
function(bytes32, bytes32) view returns (bytes32) hasher
) internal view returns (bool) {
return processMultiProof(proof, proofFlags, leaves, hasher) == root;
}
/**
* @dev Returns the root of a tree reconstructed from `leaves` and sibling nodes in `proof`. The reconstruction
* proceeds by incrementally reconstructing all inner nodes by combining a leaf/inner node with either another
* leaf/inner node or a proof sibling node, depending on whether each `proofFlags` item is true or false
* respectively.
*
* This version handles multiproofs in memory with a custom hashing function.
*
* CAUTION: Not all Merkle trees admit multiproofs. To use multiproofs, it is sufficient to ensure that: 1) the tree
* is complete (but not necessarily perfect), 2) the leaves to be proven are in the opposite order they are in the
* tree (i.e., as seen from right to left starting at the deepest layer and continuing at the next layer).
*
* NOTE: The _empty set_ (i.e. the case where `proof.length == 1 && leaves.length == 0`) is considered a no-op,
* and therefore a valid multiproof (i.e. it returns `proof[0]`). Consider disallowing this case if you're not
* validating the leaves elsewhere.
*/
function processMultiProof(
bytes32[] memory proof,
bool[] memory proofFlags,
bytes32[] memory leaves,
function(bytes32, bytes32) view returns (bytes32) hasher
) internal view returns (bytes32 merkleRoot) {
// This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by
// consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
// `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
// the Merkle tree.
uint256 leavesLen = leaves.length;
uint256 proofFlagsLen = proofFlags.length;
// Check proof validity.
if (leavesLen + proof.length != proofFlagsLen + 1) {
revert MerkleProofInvalidMultiproof();
}
// The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
// `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
bytes32[] memory hashes = new bytes32[](proofFlagsLen);
uint256 leafPos = 0;
uint256 hashPos = 0;
uint256 proofPos = 0;
// At each step, we compute the next hash using two values:
// - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
// get the next hash.
// - depending on the flag, either another value from the "main queue" (merging branches) or an element from the
// `proof` array.
for (uint256 i = 0; i < proofFlagsLen; i++) {
bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
bytes32 b = proofFlags[i]
? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++])
: proof[proofPos++];
hashes[i] = hasher(a, b);
}
if (proofFlagsLen > 0) {
if (proofPos != proof.length) {
revert MerkleProofInvalidMultiproof();
}
unchecked {
return hashes[proofFlagsLen - 1];
}
} else if (leavesLen > 0) {
return leaves[0];
} else {
return proof[0];
}
}
/**
* @dev Returns true if the `leaves` can be simultaneously proven to be a part of a Merkle tree defined by
* `root`, according to `proof` and `proofFlags` as described in {processMultiProof}.
*
* This version handles multiproofs in calldata with the default hashing function.
*
* CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details.
*
* NOTE: Consider the case where `root == proof[0] && leaves.length == 0` as it will return `true`.
* The `leaves` must be validated independently. See {processMultiProofCalldata}.
*/
function multiProofVerifyCalldata(
bytes32[] calldata proof,
bool[] calldata proofFlags,
bytes32 root,
bytes32[] memory leaves
) internal pure returns (bool) {
return processMultiProofCalldata(proof, proofFlags, leaves) == root;
}
/**
* @dev Returns the root of a tree reconstructed from `leaves` and sibling nodes in `proof`. The reconstruction
* proceeds by incrementally reconstructing all inner nodes by combining a leaf/inner node with either another
* leaf/inner node or a proof sibling node, depending on whether each `proofFlags` item is true or false
* respectively.
*
* This version handles multiproofs in calldata with the default hashing function.
*
* CAUTION: Not all Merkle trees admit multiproofs. To use multiproofs, it is sufficient to ensure that: 1) the tree
* is complete (but not necessarily perfect), 2) the leaves to be proven are in the opposite order they are in the
* tree (i.e., as seen from right to left starting at the deepest layer and continuing at the next layer).
*
* NOTE: The _empty set_ (i.e. the case where `proof.length == 1 && leaves.length == 0`) is considered a no-op,
* and therefore a valid multiproof (i.e. it returns `proof[0]`). Consider disallowing this case if you're not
* validating the leaves elsewhere.
*/
function processMultiProofCalldata(
bytes32[] calldata proof,
bool[] calldata proofFlags,
bytes32[] memory leaves
) internal pure returns (bytes32 merkleRoot) {
// This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by
// consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
// `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
// the Merkle tree.
uint256 leavesLen = leaves.length;
uint256 proofFlagsLen = proofFlags.length;
// Check proof validity.
if (leavesLen + proof.length != proofFlagsLen + 1) {
revert MerkleProofInvalidMultiproof();
}
// The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
// `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
bytes32[] memory hashes = new bytes32[](proofFlagsLen);
uint256 leafPos = 0;
uint256 hashPos = 0;
uint256 proofPos = 0;
// At each step, we compute the next hash using two values:
// - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
// get the next hash.
// - depending on the flag, either another value from the "main queue" (merging branches) or an element from the
// `proof` array.
for (uint256 i = 0; i < proofFlagsLen; i++) {
bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
bytes32 b = proofFlags[i]
? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++])
: proof[proofPos++];
hashes[i] = Hashes.commutativeKeccak256(a, b);
}
if (proofFlagsLen > 0) {
if (proofPos != proof.length) {
revert MerkleProofInvalidMultiproof();
}
unchecked {
return hashes[proofFlagsLen - 1];
}
} else if (leavesLen > 0) {
return leaves[0];
} else {
return proof[0];
}
}
/**
* @dev Returns true if the `leaves` can be simultaneously proven to be a part of a Merkle tree defined by
* `root`, according to `proof` and `proofFlags` as described in {processMultiProof}.
*
* This version handles multiproofs in calldata with a custom hashing function.
*
* CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details.
*
* NOTE: Consider the case where `root == proof[0] && leaves.length == 0` as it will return `true`.
* The `leaves` must be validated independently. See {processMultiProofCalldata}.
*/
function multiProofVerifyCalldata(
bytes32[] calldata proof,
bool[] calldata proofFlags,
bytes32 root,
bytes32[] memory leaves,
function(bytes32, bytes32) view returns (bytes32) hasher
) internal view returns (bool) {
return processMultiProofCalldata(proof, proofFlags, leaves, hasher) == root;
}
/**
* @dev Returns the root of a tree reconstructed from `leaves` and sibling nodes in `proof`. The reconstruction
* proceeds by incrementally reconstructing all inner nodes by combining a leaf/inner node with either another
* leaf/inner node or a proof sibling node, depending on whether each `proofFlags` item is true or false
* respectively.
*
* This version handles multiproofs in calldata with a custom hashing function.
*
* CAUTION: Not all Merkle trees admit multiproofs. To use multiproofs, it is sufficient to ensure that: 1) the tree
* is complete (but not necessarily perfect), 2) the leaves to be proven are in the opposite order they are in the
* tree (i.e., as seen from right to left starting at the deepest layer and continuing at the next layer).
*
* NOTE: The _empty set_ (i.e. the case where `proof.length == 1 && leaves.length == 0`) is considered a no-op,
* and therefore a valid multiproof (i.e. it returns `proof[0]`). Consider disallowing this case if you're not
* validating the leaves elsewhere.
*/
function processMultiProofCalldata(
bytes32[] calldata proof,
bool[] calldata proofFlags,
bytes32[] memory leaves,
function(bytes32, bytes32) view returns (bytes32) hasher
) internal view returns (bytes32 merkleRoot) {
// This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by
// consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
// `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
// the Merkle tree.
uint256 leavesLen = leaves.length;
uint256 proofFlagsLen = proofFlags.length;
// Check proof validity.
if (leavesLen + proof.length != proofFlagsLen + 1) {
revert MerkleProofInvalidMultiproof();
}
// The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
// `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
bytes32[] memory hashes = new bytes32[](proofFlagsLen);
uint256 leafPos = 0;
uint256 hashPos = 0;
uint256 proofPos = 0;
// At each step, we compute the next hash using two values:
// - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
// get the next hash.
// - depending on the flag, either another value from the "main queue" (merging branches) or an element from the
// `proof` array.
for (uint256 i = 0; i < proofFlagsLen; i++) {
bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
bytes32 b = proofFlags[i]
? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++])
: proof[proofPos++];
hashes[i] = hasher(a, b);
}
if (proofFlagsLen > 0) {
if (proofPos != proof.length) {
revert MerkleProofInvalidMultiproof();
}
unchecked {
return hashes[proofFlagsLen - 1];
}
} else if (leavesLen > 0) {
return leaves[0];
} else {
return proof[0];
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/structs/EnumerableMap.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableMap.js.
pragma solidity ^0.8.20;
import {EnumerableSet} from "./EnumerableSet.sol";
/**
* @dev Library for managing an enumerable variant of Solidity's
* https://solidity.readthedocs.io/en/latest/types.html#mapping-types[`mapping`]
* type.
*
* Maps have the following properties:
*
* - Entries are added, removed, and checked for existence in constant time
* (O(1)).
* - Entries are enumerated in O(n). No guarantees are made on the ordering.
*
* ```solidity
* contract Example {
* // Add the library methods
* using EnumerableMap for EnumerableMap.UintToAddressMap;
*
* // Declare a set state variable
* EnumerableMap.UintToAddressMap private myMap;
* }
* ```
*
* The following map types are supported:
*
* - `uint256 -> address` (`UintToAddressMap`) since v3.0.0
* - `address -> uint256` (`AddressToUintMap`) since v4.6.0
* - `bytes32 -> bytes32` (`Bytes32ToBytes32Map`) since v4.6.0
* - `uint256 -> uint256` (`UintToUintMap`) since v4.7.0
* - `bytes32 -> uint256` (`Bytes32ToUintMap`) since v4.7.0
* - `uint256 -> bytes32` (`UintToBytes32Map`) since v5.1.0
* - `address -> address` (`AddressToAddressMap`) since v5.1.0
* - `address -> bytes32` (`AddressToBytes32Map`) since v5.1.0
* - `bytes32 -> address` (`Bytes32ToAddressMap`) since v5.1.0
*
* [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 EnumerableMap, you can either remove all elements one by one or create a fresh instance using an
* array of EnumerableMap.
* ====
*/
library EnumerableMap {
using EnumerableSet for EnumerableSet.Bytes32Set;
// To implement this library for multiple types with as little code repetition as possible, we write it in
// terms of a generic Map type with bytes32 keys and values. The Map implementation uses private functions,
// and user-facing implementations such as `UintToAddressMap` are just wrappers around the underlying Map.
// This means that we can only create new EnumerableMaps for types that fit in bytes32.
/**
* @dev Query for a nonexistent map key.
*/
error EnumerableMapNonexistentKey(bytes32 key);
struct Bytes32ToBytes32Map {
// Storage of keys
EnumerableSet.Bytes32Set _keys;
mapping(bytes32 key => bytes32) _values;
}
/**
* @dev Adds a key-value pair to a map, or updates the value for an existing
* key. O(1).
*
* Returns true if the key was added to the map, that is if it was not
* already present.
*/
function set(Bytes32ToBytes32Map storage map, bytes32 key, bytes32 value) internal returns (bool) {
map._values[key] = value;
return map._keys.add(key);
}
/**
* @dev Removes a key-value pair from a map. O(1).
*
* Returns true if the key was removed from the map, that is if it was present.
*/
function remove(Bytes32ToBytes32Map storage map, bytes32 key) internal returns (bool) {
delete map._values[key];
return map._keys.remove(key);
}
/**
* @dev Returns true if the key is in the map. O(1).
*/
function contains(Bytes32ToBytes32Map storage map, bytes32 key) internal view returns (bool) {
return map._keys.contains(key);
}
/**
* @dev Returns the number of key-value pairs in the map. O(1).
*/
function length(Bytes32ToBytes32Map storage map) internal view returns (uint256) {
return map._keys.length();
}
/**
* @dev Returns the key-value pair stored at position `index` in the map. O(1).
*
* Note that there are no guarantees on the ordering of entries inside the
* array, and it may change when more entries are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(Bytes32ToBytes32Map storage map, uint256 index) internal view returns (bytes32 key, bytes32 value) {
bytes32 atKey = map._keys.at(index);
return (atKey, map._values[atKey]);
}
/**
* @dev Tries to returns the value associated with `key`. O(1).
* Does not revert if `key` is not in the map.
*/
function tryGet(Bytes32ToBytes32Map storage map, bytes32 key) internal view returns (bool exists, bytes32 value) {
bytes32 val = map._values[key];
if (val == bytes32(0)) {
return (contains(map, key), bytes32(0));
} else {
return (true, val);
}
}
/**
* @dev Returns the value associated with `key`. O(1).
*
* Requirements:
*
* - `key` must be in the map.
*/
function get(Bytes32ToBytes32Map storage map, bytes32 key) internal view returns (bytes32) {
bytes32 value = map._values[key];
if (value == 0 && !contains(map, key)) {
revert EnumerableMapNonexistentKey(key);
}
return value;
}
/**
* @dev Return the an array containing all the keys
*
* 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 map grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function keys(Bytes32ToBytes32Map storage map) internal view returns (bytes32[] memory) {
return map._keys.values();
}
// UintToUintMap
struct UintToUintMap {
Bytes32ToBytes32Map _inner;
}
/**
* @dev Adds a key-value pair to a map, or updates the value for an existing
* key. O(1).
*
* Returns true if the key was added to the map, that is if it was not
* already present.
*/
function set(UintToUintMap storage map, uint256 key, uint256 value) internal returns (bool) {
return set(map._inner, bytes32(key), bytes32(value));
}
/**
* @dev Removes a value from a map. O(1).
*
* Returns true if the key was removed from the map, that is if it was present.
*/
function remove(UintToUintMap storage map, uint256 key) internal returns (bool) {
return remove(map._inner, bytes32(key));
}
/**
* @dev Returns true if the key is in the map. O(1).
*/
function contains(UintToUintMap storage map, uint256 key) internal view returns (bool) {
return contains(map._inner, bytes32(key));
}
/**
* @dev Returns the number of elements in the map. O(1).
*/
function length(UintToUintMap storage map) internal view returns (uint256) {
return length(map._inner);
}
/**
* @dev Returns the element stored at position `index` in the map. 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(UintToUintMap storage map, uint256 index) internal view returns (uint256 key, uint256 value) {
(bytes32 atKey, bytes32 val) = at(map._inner, index);
return (uint256(atKey), uint256(val));
}
/**
* @dev Tries to returns the value associated with `key`. O(1).
* Does not revert if `key` is not in the map.
*/
function tryGet(UintToUintMap storage map, uint256 key) internal view returns (bool exists, uint256 value) {
(bool success, bytes32 val) = tryGet(map._inner, bytes32(key));
return (success, uint256(val));
}
/**
* @dev Returns the value associated with `key`. O(1).
*
* Requirements:
*
* - `key` must be in the map.
*/
function get(UintToUintMap storage map, uint256 key) internal view returns (uint256) {
return uint256(get(map._inner, bytes32(key)));
}
/**
* @dev Return the an array containing all the keys
*
* 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 map grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function keys(UintToUintMap storage map) internal view returns (uint256[] memory) {
bytes32[] memory store = keys(map._inner);
uint256[] memory result;
assembly ("memory-safe") {
result := store
}
return result;
}
// UintToAddressMap
struct UintToAddressMap {
Bytes32ToBytes32Map _inner;
}
/**
* @dev Adds a key-value pair to a map, or updates the value for an existing
* key. O(1).
*
* Returns true if the key was added to the map, that is if it was not
* already present.
*/
function set(UintToAddressMap storage map, uint256 key, address value) internal returns (bool) {
return set(map._inner, bytes32(key), bytes32(uint256(uint160(value))));
}
/**
* @dev Removes a value from a map. O(1).
*
* Returns true if the key was removed from the map, that is if it was present.
*/
function remove(UintToAddressMap storage map, uint256 key) internal returns (bool) {
return remove(map._inner, bytes32(key));
}
/**
* @dev Returns true if the key is in the map. O(1).
*/
function contains(UintToAddressMap storage map, uint256 key) internal view returns (bool) {
return contains(map._inner, bytes32(key));
}
/**
* @dev Returns the number of elements in the map. O(1).
*/
function length(UintToAddressMap storage map) internal view returns (uint256) {
return length(map._inner);
}
/**
* @dev Returns the element stored at position `index` in the map. 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(UintToAddressMap storage map, uint256 index) internal view returns (uint256 key, address value) {
(bytes32 atKey, bytes32 val) = at(map._inner, index);
return (uint256(atKey), address(uint160(uint256(val))));
}
/**
* @dev Tries to returns the value associated with `key`. O(1).
* Does not revert if `key` is not in the map.
*/
function tryGet(UintToAddressMap storage map, uint256 key) internal view returns (bool exists, address value) {
(bool success, bytes32 val) = tryGet(map._inner, bytes32(key));
return (success, address(uint160(uint256(val))));
}
/**
* @dev Returns the value associated with `key`. O(1).
*
* Requirements:
*
* - `key` must be in the map.
*/
function get(UintToAddressMap storage map, uint256 key) internal view returns (address) {
return address(uint160(uint256(get(map._inner, bytes32(key)))));
}
/**
* @dev Return the an array containing all the keys
*
* 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 map grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function keys(UintToAddressMap storage map) internal view returns (uint256[] memory) {
bytes32[] memory store = keys(map._inner);
uint256[] memory result;
assembly ("memory-safe") {
result := store
}
return result;
}
// UintToBytes32Map
struct UintToBytes32Map {
Bytes32ToBytes32Map _inner;
}
/**
* @dev Adds a key-value pair to a map, or updates the value for an existing
* key. O(1).
*
* Returns true if the key was added to the map, that is if it was not
* already present.
*/
function set(UintToBytes32Map storage map, uint256 key, bytes32 value) internal returns (bool) {
return set(map._inner, bytes32(key), value);
}
/**
* @dev Removes a value from a map. O(1).
*
* Returns true if the key was removed from the map, that is if it was present.
*/
function remove(UintToBytes32Map storage map, uint256 key) internal returns (bool) {
return remove(map._inner, bytes32(key));
}
/**
* @dev Returns true if the key is in the map. O(1).
*/
function contains(UintToBytes32Map storage map, uint256 key) internal view returns (bool) {
return contains(map._inner, bytes32(key));
}
/**
* @dev Returns the number of elements in the map. O(1).
*/
function length(UintToBytes32Map storage map) internal view returns (uint256) {
return length(map._inner);
}
/**
* @dev Returns the element stored at position `index` in the map. 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(UintToBytes32Map storage map, uint256 index) internal view returns (uint256 key, bytes32 value) {
(bytes32 atKey, bytes32 val) = at(map._inner, index);
return (uint256(atKey), val);
}
/**
* @dev Tries to returns the value associated with `key`. O(1).
* Does not revert if `key` is not in the map.
*/
function tryGet(UintToBytes32Map storage map, uint256 key) internal view returns (bool exists, bytes32 value) {
(bool success, bytes32 val) = tryGet(map._inner, bytes32(key));
return (success, val);
}
/**
* @dev Returns the value associated with `key`. O(1).
*
* Requirements:
*
* - `key` must be in the map.
*/
function get(UintToBytes32Map storage map, uint256 key) internal view returns (bytes32) {
return get(map._inner, bytes32(key));
}
/**
* @dev Return the an array containing all the keys
*
* 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 map grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function keys(UintToBytes32Map storage map) internal view returns (uint256[] memory) {
bytes32[] memory store = keys(map._inner);
uint256[] memory result;
assembly ("memory-safe") {
result := store
}
return result;
}
// AddressToUintMap
struct AddressToUintMap {
Bytes32ToBytes32Map _inner;
}
/**
* @dev Adds a key-value pair to a map, or updates the value for an existing
* key. O(1).
*
* Returns true if the key was added to the map, that is if it was not
* already present.
*/
function set(AddressToUintMap storage map, address key, uint256 value) internal returns (bool) {
return set(map._inner, bytes32(uint256(uint160(key))), bytes32(value));
}
/**
* @dev Removes a value from a map. O(1).
*
* Returns true if the key was removed from the map, that is if it was present.
*/
function remove(AddressToUintMap storage map, address key) internal returns (bool) {
return remove(map._inner, bytes32(uint256(uint160(key))));
}
/**
* @dev Returns true if the key is in the map. O(1).
*/
function contains(AddressToUintMap storage map, address key) internal view returns (bool) {
return contains(map._inner, bytes32(uint256(uint160(key))));
}
/**
* @dev Returns the number of elements in the map. O(1).
*/
function length(AddressToUintMap storage map) internal view returns (uint256) {
return length(map._inner);
}
/**
* @dev Returns the element stored at position `index` in the map. 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(AddressToUintMap storage map, uint256 index) internal view returns (address key, uint256 value) {
(bytes32 atKey, bytes32 val) = at(map._inner, index);
return (address(uint160(uint256(atKey))), uint256(val));
}
/**
* @dev Tries to returns the value associated with `key`. O(1).
* Does not revert if `key` is not in the map.
*/
function tryGet(AddressToUintMap storage map, address key) internal view returns (bool exists, uint256 value) {
(bool success, bytes32 val) = tryGet(map._inner, bytes32(uint256(uint160(key))));
return (success, uint256(val));
}
/**
* @dev Returns the value associated with `key`. O(1).
*
* Requirements:
*
* - `key` must be in the map.
*/
function get(AddressToUintMap storage map, address key) internal view returns (uint256) {
return uint256(get(map._inner, bytes32(uint256(uint160(key)))));
}
/**
* @dev Return the an array containing all the keys
*
* 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 map grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function keys(AddressToUintMap storage map) internal view returns (address[] memory) {
bytes32[] memory store = keys(map._inner);
address[] memory result;
assembly ("memory-safe") {
result := store
}
return result;
}
// AddressToAddressMap
struct AddressToAddressMap {
Bytes32ToBytes32Map _inner;
}
/**
* @dev Adds a key-value pair to a map, or updates the value for an existing
* key. O(1).
*
* Returns true if the key was added to the map, that is if it was not
* already present.
*/
function set(AddressToAddressMap storage map, address key, address value) internal returns (bool) {
return set(map._inner, bytes32(uint256(uint160(key))), bytes32(uint256(uint160(value))));
}
/**
* @dev Removes a value from a map. O(1).
*
* Returns true if the key was removed from the map, that is if it was present.
*/
function remove(AddressToAddressMap storage map, address key) internal returns (bool) {
return remove(map._inner, bytes32(uint256(uint160(key))));
}
/**
* @dev Returns true if the key is in the map. O(1).
*/
function contains(AddressToAddressMap storage map, address key) internal view returns (bool) {
return contains(map._inner, bytes32(uint256(uint160(key))));
}
/**
* @dev Returns the number of elements in the map. O(1).
*/
function length(AddressToAddressMap storage map) internal view returns (uint256) {
return length(map._inner);
}
/**
* @dev Returns the element stored at position `index` in the map. 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(AddressToAddressMap storage map, uint256 index) internal view returns (address key, address value) {
(bytes32 atKey, bytes32 val) = at(map._inner, index);
return (address(uint160(uint256(atKey))), address(uint160(uint256(val))));
}
/**
* @dev Tries to returns the value associated with `key`. O(1).
* Does not revert if `key` is not in the map.
*/
function tryGet(AddressToAddressMap storage map, address key) internal view returns (bool exists, address value) {
(bool success, bytes32 val) = tryGet(map._inner, bytes32(uint256(uint160(key))));
return (success, address(uint160(uint256(val))));
}
/**
* @dev Returns the value associated with `key`. O(1).
*
* Requirements:
*
* - `key` must be in the map.
*/
function get(AddressToAddressMap storage map, address key) internal view returns (address) {
return address(uint160(uint256(get(map._inner, bytes32(uint256(uint160(key)))))));
}
/**
* @dev Return the an array containing all the keys
*
* 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 map grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function keys(AddressToAddressMap storage map) internal view returns (address[] memory) {
bytes32[] memory store = keys(map._inner);
address[] memory result;
assembly ("memory-safe") {
result := store
}
return result;
}
// AddressToBytes32Map
struct AddressToBytes32Map {
Bytes32ToBytes32Map _inner;
}
/**
* @dev Adds a key-value pair to a map, or updates the value for an existing
* key. O(1).
*
* Returns true if the key was added to the map, that is if it was not
* already present.
*/
function set(AddressToBytes32Map storage map, address key, bytes32 value) internal returns (bool) {
return set(map._inner, bytes32(uint256(uint160(key))), value);
}
/**
* @dev Removes a value from a map. O(1).
*
* Returns true if the key was removed from the map, that is if it was present.
*/
function remove(AddressToBytes32Map storage map, address key) internal returns (bool) {
return remove(map._inner, bytes32(uint256(uint160(key))));
}
/**
* @dev Returns true if the key is in the map. O(1).
*/
function contains(AddressToBytes32Map storage map, address key) internal view returns (bool) {
return contains(map._inner, bytes32(uint256(uint160(key))));
}
/**
* @dev Returns the number of elements in the map. O(1).
*/
function length(AddressToBytes32Map storage map) internal view returns (uint256) {
return length(map._inner);
}
/**
* @dev Returns the element stored at position `index` in the map. 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(AddressToBytes32Map storage map, uint256 index) internal view returns (address key, bytes32 value) {
(bytes32 atKey, bytes32 val) = at(map._inner, index);
return (address(uint160(uint256(atKey))), val);
}
/**
* @dev Tries to returns the value associated with `key`. O(1).
* Does not revert if `key` is not in the map.
*/
function tryGet(AddressToBytes32Map storage map, address key) internal view returns (bool exists, bytes32 value) {
(bool success, bytes32 val) = tryGet(map._inner, bytes32(uint256(uint160(key))));
return (success, val);
}
/**
* @dev Returns the value associated with `key`. O(1).
*
* Requirements:
*
* - `key` must be in the map.
*/
function get(AddressToBytes32Map storage map, address key) internal view returns (bytes32) {
return get(map._inner, bytes32(uint256(uint160(key))));
}
/**
* @dev Return the an array containing all the keys
*
* 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 map grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function keys(AddressToBytes32Map storage map) internal view returns (address[] memory) {
bytes32[] memory store = keys(map._inner);
address[] memory result;
assembly ("memory-safe") {
result := store
}
return result;
}
// Bytes32ToUintMap
struct Bytes32ToUintMap {
Bytes32ToBytes32Map _inner;
}
/**
* @dev Adds a key-value pair to a map, or updates the value for an existing
* key. O(1).
*
* Returns true if the key was added to the map, that is if it was not
* already present.
*/
function set(Bytes32ToUintMap storage map, bytes32 key, uint256 value) internal returns (bool) {
return set(map._inner, key, bytes32(value));
}
/**
* @dev Removes a value from a map. O(1).
*
* Returns true if the key was removed from the map, that is if it was present.
*/
function remove(Bytes32ToUintMap storage map, bytes32 key) internal returns (bool) {
return remove(map._inner, key);
}
/**
* @dev Returns true if the key is in the map. O(1).
*/
function contains(Bytes32ToUintMap storage map, bytes32 key) internal view returns (bool) {
return contains(map._inner, key);
}
/**
* @dev Returns the number of elements in the map. O(1).
*/
function length(Bytes32ToUintMap storage map) internal view returns (uint256) {
return length(map._inner);
}
/**
* @dev Returns the element stored at position `index` in the map. 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(Bytes32ToUintMap storage map, uint256 index) internal view returns (bytes32 key, uint256 value) {
(bytes32 atKey, bytes32 val) = at(map._inner, index);
return (atKey, uint256(val));
}
/**
* @dev Tries to returns the value associated with `key`. O(1).
* Does not revert if `key` is not in the map.
*/
function tryGet(Bytes32ToUintMap storage map, bytes32 key) internal view returns (bool exists, uint256 value) {
(bool success, bytes32 val) = tryGet(map._inner, key);
return (success, uint256(val));
}
/**
* @dev Returns the value associated with `key`. O(1).
*
* Requirements:
*
* - `key` must be in the map.
*/
function get(Bytes32ToUintMap storage map, bytes32 key) internal view returns (uint256) {
return uint256(get(map._inner, key));
}
/**
* @dev Return the an array containing all the keys
*
* 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 map grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function keys(Bytes32ToUintMap storage map) internal view returns (bytes32[] memory) {
bytes32[] memory store = keys(map._inner);
bytes32[] memory result;
assembly ("memory-safe") {
result := store
}
return result;
}
// Bytes32ToAddressMap
struct Bytes32ToAddressMap {
Bytes32ToBytes32Map _inner;
}
/**
* @dev Adds a key-value pair to a map, or updates the value for an existing
* key. O(1).
*
* Returns true if the key was added to the map, that is if it was not
* already present.
*/
function set(Bytes32ToAddressMap storage map, bytes32 key, address value) internal returns (bool) {
return set(map._inner, key, bytes32(uint256(uint160(value))));
}
/**
* @dev Removes a value from a map. O(1).
*
* Returns true if the key was removed from the map, that is if it was present.
*/
function remove(Bytes32ToAddressMap storage map, bytes32 key) internal returns (bool) {
return remove(map._inner, key);
}
/**
* @dev Returns true if the key is in the map. O(1).
*/
function contains(Bytes32ToAddressMap storage map, bytes32 key) internal view returns (bool) {
return contains(map._inner, key);
}
/**
* @dev Returns the number of elements in the map. O(1).
*/
function length(Bytes32ToAddressMap storage map) internal view returns (uint256) {
return length(map._inner);
}
/**
* @dev Returns the element stored at position `index` in the map. 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(Bytes32ToAddressMap storage map, uint256 index) internal view returns (bytes32 key, address value) {
(bytes32 atKey, bytes32 val) = at(map._inner, index);
return (atKey, address(uint160(uint256(val))));
}
/**
* @dev Tries to returns the value associated with `key`. O(1).
* Does not revert if `key` is not in the map.
*/
function tryGet(Bytes32ToAddressMap storage map, bytes32 key) internal view returns (bool exists, address value) {
(bool success, bytes32 val) = tryGet(map._inner, key);
return (success, address(uint160(uint256(val))));
}
/**
* @dev Returns the value associated with `key`. O(1).
*
* Requirements:
*
* - `key` must be in the map.
*/
function get(Bytes32ToAddressMap storage map, bytes32 key) internal view returns (address) {
return address(uint160(uint256(get(map._inner, key))));
}
/**
* @dev Return the an array containing all the keys
*
* 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 map grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function keys(Bytes32ToAddressMap storage map) internal view returns (bytes32[] memory) {
bytes32[] memory store = keys(map._inner);
bytes32[] memory result;
assembly ("memory-safe") {
result := store
}
return result;
}
}// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.29;
/// @notice Provides a flexible and updatable auth pattern which is completely separate from application logic.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/auth/Auth.sol)
/// @author Modified from Dappsys (https://github.com/dapphub/ds-auth/blob/master/src/auth.sol)
abstract contract Auth {
event OwnershipTransferred(address indexed user, address indexed newOwner);
event AuthorityUpdated(address indexed user, Authority indexed newAuthority);
address public owner;
Authority public authority;
constructor(address _owner, Authority _authority) {
owner = _owner;
authority = _authority;
emit OwnershipTransferred(msg.sender, _owner);
emit AuthorityUpdated(msg.sender, _authority);
}
modifier requiresAuth() virtual {
require(isAuthorized(msg.sender, msg.sig), "UNAUTHORIZED");
_;
}
function isAuthorized(address user, bytes4 functionSig) internal view virtual returns (bool) {
Authority auth = authority; // Memoizing authority saves us a warm SLOAD, around 100 gas.
// Checking if the caller is the owner only after calling the authority saves gas in most cases, but be
// aware that this makes protected functions uncallable even to the owner if the authority is out of order.
return (address(auth) != address(0) && auth.canCall(user, address(this), functionSig)) || user == owner;
}
function setAuthority(Authority newAuthority) public virtual {
// We check if the caller is the owner first because we want to ensure they can
// always swap out the authority even if it's reverting or using up a lot of gas.
require(msg.sender == owner || authority.canCall(msg.sender, address(this), msg.sig));
authority = newAuthority;
emit AuthorityUpdated(msg.sender, newAuthority);
}
function transferOwnership(address newOwner) public virtual requiresAuth {
owner = newOwner;
emit OwnershipTransferred(msg.sender, newOwner);
}
}
/// @notice A generic interface for a contract which provides authorization data to an Auth instance.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/auth/Auth.sol)
/// @author Modified from Dappsys (https://github.com/dapphub/ds-auth/blob/master/src/auth.sol)
interface Authority {
function canCall(address user, address target, bytes4 functionSig) external view returns (bool);
}// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.29;
import { Auth, Authority } from "@solmate/auth/Auth.sol";
import { IAuth2Step } from "src/core/interfaces/IAuth2Step.sol";
/// @title Auth2Step
/// @notice An extension of Auth.sol that supports two-step ownership transfer
contract Auth2Step is IAuth2Step, Auth {
////////////////////////////////////////////////////////////
// Storage //
////////////////////////////////////////////////////////////
/// @notice Address of the pending owner
address public pendingOwner;
////////////////////////////////////////////////////////////
// Modifiers //
////////////////////////////////////////////////////////////
modifier onlyOwner() virtual {
require(msg.sender == owner, Aera__Unauthorized());
_;
}
constructor(address newOwner_, Authority authority_) Auth(newOwner_, authority_) { }
////////////////////////////////////////////////////////////
// Public / External Functions //
////////////////////////////////////////////////////////////
/// @inheritdoc IAuth2Step
function acceptOwnership() external virtual override {
address pendingOwner_ = pendingOwner;
// Requirements: the caller must be the pending owner
require(msg.sender == pendingOwner_, Aera__Unauthorized());
// Effects: set the owner to the pending owner and delete the pending owner
owner = pendingOwner_;
delete pendingOwner;
// Log the ownership transfer
emit OwnershipTransferred(msg.sender, pendingOwner_);
}
/// @notice Start the ownership transfer of the contract to a new account
/// @param newOwner Address to transfer ownership to
/// @dev Replaces the pending transfer if there is one
/// @dev Overrides the `Auth` contract's `transferOwnership` function
/// @dev Zero check is not needed because pendingOwner can always be overwritten
function transferOwnership(address newOwner) public virtual override onlyOwner {
// Effects: set the pending owner
//slither-disable-next-line missing-zero-check
pendingOwner = newOwner;
// Log the ownership transfer start
emit OwnershipTransferStarted(owner, newOwner);
}
}// SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.29; //////////////////////////////////////////////////////////// // Memory & Calldata // //////////////////////////////////////////////////////////// // Size of a word in bytes uint256 constant WORD_SIZE = 32; // Size of a function selector in bytes uint256 constant SELECTOR_SIZE = 4; // Minimum valid calldata size (selector + one word = 36) uint256 constant MINIMUM_CALLDATA_LENGTH = WORD_SIZE + SELECTOR_SIZE; // Offset to skip selector and first word in calldata uint256 constant CALLDATA_OFFSET = MINIMUM_CALLDATA_LENGTH; // Offset for extracting spender address from approval calldata uint256 constant ERC20_SPENDER_OFFSET = 36; // Size of an address in bits uint256 constant ADDRESS_SIZE_BITS = 160; //////////////////////////////////////////////////////////// // Hooks Constants // //////////////////////////////////////////////////////////// // Mask for a bit indicating whether a hooks has before submit call uint256 constant BEFORE_HOOK_MASK = 1; // Mask for a bit indicating whether a hooks has after submit call uint256 constant AFTER_HOOK_MASK = 2; // Mask for a bit indicating whether a hooks exists uint256 constant HOOKS_FLAG_MASK = 0x80; // Mask for 7 bits indicating the number of configurable hooks offsets uint256 constant CONFIGURABLE_HOOKS_LENGTH_MASK = 0x7F; //////////////////////////////////////////////////////////// // Bit Operations // //////////////////////////////////////////////////////////// // Mask for extracting 8-bit values uint256 constant MASK_8_BIT = 0xff; // Mask for extracting 16-bit values uint256 constant MASK_16_BIT = 0xffff; //////////////////////////////////////////////////////////// // Pipeline Constants // //////////////////////////////////////////////////////////// // Bit offset for results index in packed clipboard data uint256 constant RESULTS_INDEX_OFFSET = 24; // Bit offset for copy word position in packed clipboard data uint256 constant COPY_WORD_OFFSET = 16; //////////////////////////////////////////////////////////// // Extractor Constants // //////////////////////////////////////////////////////////// // Number of bits per extraction offset uint256 constant EXTRACT_OFFSET_SIZE_BITS = 16; // Number of bits to shift to get the offset (256 - 16) uint256 constant EXTRACTION_OFFSET_SHIFT_BITS = 240; /// @dev Maximum number of extraction offsets(16) + 1 uint256 constant MAX_EXTRACT_OFFSETS_EXCLUSIVE = 17; //////////////////////////////////////////////////////////// // Callback Constants // //////////////////////////////////////////////////////////// // Maximum value for uint16, used to indicate no callback data uint16 constant NO_CALLBACK_DATA = type(uint16).max; // Offset for selector in callback data uint256 constant SELECTOR_OFFSET = 48; // Offset for callback data uint256 constant CALLBACK_DATA_OFFSET = 160; //////////////////////////////////////////////////////////// // Fee Constants // //////////////////////////////////////////////////////////// // Basis points denominator (100%) uint256 constant ONE_IN_BPS = 1e4; // Maximum TVL fee uint256 constant MAX_TVL_FEE = 2000; // 20% // Maximum performance fee uint256 constant MAX_PERFORMANCE_FEE = ONE_IN_BPS; // Seconds in a year for fee calculations uint256 constant SECONDS_PER_YEAR = 365 days; // Maximum dispute period uint256 constant MAX_DISPUTE_PERIOD = 30 days; //////////////////////////////////////////////////////////// // Unit Price Constants // //////////////////////////////////////////////////////////// /// @dev Precision for unit price calculations (18 decimals) uint256 constant UNIT_PRICE_PRECISION = 1e18; /// @dev One minute in seconds uint256 constant ONE_MINUTE = 1 minutes; /// @dev One day in seconds uint256 constant ONE_DAY = 1 days; //////////////////////////////////////////////////////////// // Provisioner Constants // //////////////////////////////////////////////////////////// /// @dev Minimum deposit multiplier 50% uint256 constant MIN_DEPOSIT_MULTIPLIER = 5000; /// @dev Minimum redeem multiplier 50% uint256 constant MIN_REDEEM_MULTIPLIER = 5000; /// @dev Deposit/Redeem flag in RequestType enum uint256 constant DEPOSIT_REDEEM_FLAG = 1; /// @dev Auto/Fixed price flag in RequestType enum uint256 constant AUTO_PRICE_FIXED_PRICE_FLAG = 2; /// @dev One unit with 18 decimals uint256 constant ONE_UNIT = 1e18; /// @dev Maximum seconds between request deadline and current timestamp uint256 constant MAX_SECONDS_TO_DEADLINE = 365 days; /// @dev Upper bound for depositRefundTimeout to prevent indefinite user lockout uint256 constant MAX_DEPOSIT_REFUND_TIMEOUT = 30 days; //////////////////////////////////////////////////////////// // Whitelist Constants // //////////////////////////////////////////////////////////// /// @dev Whitelist flag in AddressToUintMap uint8 constant IS_WHITELISTED_FLAG = 1;
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.29;
import { HookCallType } from "src/core/Types.sol";
import { ISubmitHooks } from "src/core/interfaces/ISubmitHooks.sol";
/// @notice Interface for the BaseVault
interface IBaseVault {
////////////////////////////////////////////////////////////
// Events //
////////////////////////////////////////////////////////////
event SubmitHooksSet(address indexed submitHooksAddress);
event GuardianRootSet(address indexed guardian, bytes32 indexed root);
////////////////////////////////////////////////////////////
// Errors //
////////////////////////////////////////////////////////////
error Aera__ZeroAddressGuardian();
error Aera__ZeroAddressOwner();
error Aera__CallerIsNotGuardian();
error Aera__CallerIsNotAuthOrGuardian();
error Aera__SubmissionFailed(uint256 index, bytes result);
error Aera__AllowanceIsNotZero(address token, address spender);
error Aera__ZeroAddressMerkleRoot();
error Aera__BeforeSubmitHooksFailed(bytes result);
error Aera__AfterSubmitHooksFailed(bytes result);
error Aera__BeforeOperationHooksFailed(uint256 index, bytes result);
error Aera__AfterOperationHooksFailed(uint256 index, bytes result);
error Aera__BeforeOperationHooksWithConfigurableHooks();
error Aera__ProofVerificationFailed();
error Aera__InvalidBeforeOperationHooksReturnDataLength();
error Aera__GuardianNotWhitelisted();
error Aera__ExpectedCallbackNotReceived();
error Aera__NoResults();
////////////////////////////////////////////////////////////
// Functions //
////////////////////////////////////////////////////////////
/// @notice Submit a series of operations to the vault
/// @param data Encoded array of operations to submit
/// ┌─────────────────────────────┬─────────────────────────┬───────────────────────────────────────────────┐
/// │ FIELDS │ SIZE │ DESCRIPTION │
/// ├─────────────────────────────┴─────────────────────────┴───────────────────────────────────────────────┤
/// │ operationsLength 1 byte Number of operations in the array │
/// │ │
/// │ [for each operation]: │
/// │ │
/// │ SIGNATURE │
/// │ target 20 bytes Target contract address │
/// │ calldataLength 2 bytes Length of calldata │
/// │ calldata <calldataLength> bytes Calldata (before pipelining) │
/// │ │
/// │ CLIPBOARD │
/// │ clipboardsLength 1 byte Number of clipboards │
/// │ [for each clipboard entry]: │
/// │ resultIndex 1 byte Which operation to take from │
/// │ copyWord 1 byte Which word to copy │
/// │ pasteOffset 2 bytes What offset to paste it at │
/// │ │
/// │ CALL TYPE │
/// │ isStaticCall 1 byte 1 if static, 0 if a regular call │
/// │ [if isStaticCall == 0]: │
/// │ │
/// │ CALLBACK HANDLING │
/// │ hasCallback 1 byte Whether to allow callbacks during operation │
/// │ [if hasCallback == 1]: │
/// │ callbackData = 26 bytes Expected callback info │
/// │ ┌────────────────────┬──────────────────────────┬───────────────────┐ │
/// │ │ selector (4 bytes) │ calldataOffset (2 bytes) │ caller (20 bytes) │ │
/// │ └────────────────────┴──────────────────────────┴───────────────────┘ │
/// │ │
/// │ HOOKS │
/// │ hookConfig = 1 byte Hook configuration │
/// │ ┌─────────────────┬────────────────────────────────────────┐ │
/// │ │ hasHook (1 bit) │ configurableHookOffsetsLength (7 bits) │ │
/// │ └─────────────────┴────────────────────────────────────────┘ │
/// │ if configurableHookOffsetsLength > 0: │
/// │ configurableHookOffsets 32 bytes Packed configurable hook offsets │
/// │ if hasHook == 1: │
/// │ hook 20 bytes Hook contract address │
/// │ │
/// │ MERKLE PROOF │
/// │ proofLength 1 byte Merkle proof length │
/// │ proof <proofLength> * 32 bytes Merkle proof data │
/// │ │
/// │ PAYABILITY │
/// │ hasValue 1 byte Whether to send native token with the call │
/// │ [if hasValue == 1]: │
/// │ value 32 bytes Amount of native token to send │
/// └───────────────────────────────────────────────────────────────────────────────────────────────────────┘
function submit(bytes calldata data) external;
/// @notice Set the merkle root for a guardian
/// Used to add guardians and update their permissions
/// @param guardian Address of the guardian
/// @param root Merkle root
function setGuardianRoot(address guardian, bytes32 root) external;
/// @notice Removes a guardian from the vault
/// @param guardian Address of the guardian
function removeGuardian(address guardian) external;
/// @notice Set the submit hooks address
/// @param newSubmitHooks Address of the new submit hooks contract
function setSubmitHooks(ISubmitHooks newSubmitHooks) external;
/// @notice Pause the vault, halting the ability for guardians to submit
function pause() external;
/// @notice Unpause the vault, allowing guardians to submit operations
function unpause() external;
/// @notice Check if the guardian is whitelisted and set the root to zero if not
/// Used to disable guardians who were removed from the whitelist
/// after being selected as guardians
/// @param guardian The guardian address
/// @return isRemoved Whether the guardian was removed from the whitelist
function checkGuardianWhitelist(address guardian) external returns (bool isRemoved);
/// @notice Get all active guardians
/// @return Array of active guardian addresses
function getActiveGuardians() external view returns (address[] memory);
/// @notice Get the guardian root for a guardian
/// @param guardian The guardian address
/// @return The guardian root
function getGuardianRoot(address guardian) external view returns (bytes32);
/// @notice Get the current hook call type
/// @return The current hook call type
function getCurrentHookCallType() external view returns (HookCallType);
}// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.29;
import { BaseVaultParameters } from "src/core/Types.sol";
import { IBaseVaultDeployer } from "src/core/interfaces/IBaseVaultDeployer.sol";
/// @title IBaseVaultFactory
/// @notice Interface for the base vault factory
interface IBaseVaultFactory is IBaseVaultDeployer {
////////////////////////////////////////////////////////////
// Events //
////////////////////////////////////////////////////////////
/// @notice Emitted when the vault is created
/// @param vault Vault address
/// @param owner Initial owner address
/// @param submitHooks Submit hooks address
/// @param description Vault description
event VaultCreated(address indexed vault, address indexed owner, address submitHooks, string description);
////////////////////////////////////////////////////////////
// Functions //
////////////////////////////////////////////////////////////
/// @notice Create a new vault with the given parameters
/// @param salt The salt value to use for create2
/// @param description Vault description
/// @param baseVaultParams Parameters for vault deployment
/// @param expectedVaultAddress Expected address of the deployed vault
/// @return deployedVault Address of the deployed vault
function create(
bytes32 salt,
string calldata description,
BaseVaultParameters calldata baseVaultParams,
address expectedVaultAddress
) external returns (address deployedVault);
}// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.29;
/// @title ISubmitHooks
/// @notice Interface for hooks that execute before and after submit calls
interface ISubmitHooks {
////////////////////////////////////////////////////////////
// Functions //
////////////////////////////////////////////////////////////
/// @notice Called before a submit
/// @param data Encoded data of the submit
/// @param guardian Address of the guardian that submitted
function beforeSubmit(bytes memory data, address guardian) external;
/// @notice Called after a submit
/// @param data Encoded data of the submit
/// @param guardian Address of the guardian that submitted
function afterSubmit(bytes memory data, address guardian) external;
}// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.29;
interface IWhitelist {
////////////////////////////////////////////////////////////
// Events //
////////////////////////////////////////////////////////////
event WhitelistSet(address indexed addr, bool isAddressWhitelisted);
////////////////////////////////////////////////////////////
// Functions //
////////////////////////////////////////////////////////////
/// @notice Set the address whitelisted status
/// @param addr The address to add/remove from the whitelist
/// @param isAddressWhitelisted Whether address should be whitelisted going forward
function setWhitelisted(address addr, bool isAddressWhitelisted) external;
/// @notice Checks if the address is whitelisted
/// @param addr The address to check
/// @return True if the addr is whitelisted, false otherwise
function isWhitelisted(address addr) external view returns (bool);
/// @notice Get all whitelisted addresses
/// @return An array of all whitelisted addresses
function getAllWhitelisted() external view returns (address[] memory);
}// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.29;
import {
EXTRACTION_OFFSET_SHIFT_BITS,
EXTRACT_OFFSET_SIZE_BITS,
MAX_EXTRACT_OFFSETS_EXCLUSIVE,
MINIMUM_CALLDATA_LENGTH,
SELECTOR_SIZE,
WORD_SIZE
} from "src/core/Constants.sol";
/// @title CalldataExtractor
/// @notice Library for extracting specific chunks of calldata based on configured offsets
/// used in configurable hooks to extract 32 byte chunks from calldata and check them against
/// expected values in the merkle tree
library CalldataExtractor {
////////////////////////////////////////////////////////////
// Errors //
////////////////////////////////////////////////////////////
error Aera__ExtractionNumberTooLarge();
error Aera__CalldataTooShort();
error Aera__OffsetOutOfBounds();
////////////////////////////////////////////////////////////
// Private / Internal Functions //
////////////////////////////////////////////////////////////
/// @notice Extract 32-byte chunks from calldata based on config offsets
/// @param callData The calldata to extract from
/// @param calldataOffsetsPacked Packed 16-bit extraction offsets
/// @param calldataOffsetsCount Number of extractions to perform
/// @return result Concatenated byte values at specific offsets
/// @dev Number of provided offsets must be <= 16 because that's how many fit in uint256
/// @dev Calldata must be at least 36 bytes long to be considered valid
/// @dev All math is unchecked because we validate everything before doing any operations
function extract(bytes memory callData, uint256 calldataOffsetsPacked, uint256 calldataOffsetsCount)
internal
pure
returns (bytes memory)
{
unchecked {
// Check that the number of extractions is less than the maximum allowed
require(calldataOffsetsCount < MAX_EXTRACT_OFFSETS_EXCLUSIVE, Aera__ExtractionNumberTooLarge());
// Initialize result bytes array with the length being number of extractions times 32 bytes
bytes memory result = new bytes(calldataOffsetsCount * WORD_SIZE);
uint256 resultPtr;
assembly ("memory-safe") {
resultPtr := result
}
// Skip the dynamic array length word
resultPtr += WORD_SIZE;
uint256 callDataLength = callData.length;
// Requirements: check that the calldata is at least 36 bytes long (selector + one word)
require(callDataLength >= MINIMUM_CALLDATA_LENGTH, Aera__CalldataTooShort());
// Max valid offset is the length of callData minus 36 bytes(selector + one word)
uint256 maxValidOffset = callDataLength - WORD_SIZE;
uint256 calldataPointer;
assembly ("memory-safe") {
calldataPointer := callData
}
// Skip the dynamic array length word
calldataPointer += WORD_SIZE;
uint256 resultWriteOffset;
for (uint256 i = 0; i < calldataOffsetsCount; ++i) {
uint256 extractionOffset = (calldataOffsetsPacked >> EXTRACTION_OFFSET_SHIFT_BITS) + SELECTOR_SIZE;
// Requirements: check that the offset is within the calldata bounds
require(extractionOffset <= maxValidOffset, Aera__OffsetOutOfBounds());
uint256 calldataOffsetPointer = calldataPointer + extractionOffset;
// Extract 32 bytes from calldata at offset
// mload from callData pointer + extraction offset
bytes32 extracted;
assembly ("memory-safe") {
extracted := mload(calldataOffsetPointer)
}
// Store extracted value in the result
// mstore extracted word to result pointer + current result offset
uint256 resultOffsetPointer = resultPtr + resultWriteOffset;
assembly ("memory-safe") {
mstore(resultOffsetPointer, extracted)
}
resultWriteOffset += WORD_SIZE;
calldataOffsetsPacked = calldataOffsetsPacked << EXTRACT_OFFSET_SIZE_BITS;
}
return result;
}
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.29;
// slither-disable-start write-after-write,dead-code
/// @dev Represents a raw calldata offset
type CalldataReader is uint256;
using CalldataReaderLib for CalldataReader global;
using { neq as !=, eq as ==, gt as >, lt as <, ge as >=, le as <= } for CalldataReader global;
function neq(CalldataReader a, CalldataReader b) pure returns (bool) {
return CalldataReader.unwrap(a) != CalldataReader.unwrap(b);
}
function eq(CalldataReader a, CalldataReader b) pure returns (bool) {
return CalldataReader.unwrap(a) == CalldataReader.unwrap(b);
}
function gt(CalldataReader a, CalldataReader b) pure returns (bool) {
return CalldataReader.unwrap(a) > CalldataReader.unwrap(b);
}
function lt(CalldataReader a, CalldataReader b) pure returns (bool) {
return CalldataReader.unwrap(a) < CalldataReader.unwrap(b);
}
function ge(CalldataReader a, CalldataReader b) pure returns (bool) {
return CalldataReader.unwrap(a) >= CalldataReader.unwrap(b);
}
function le(CalldataReader a, CalldataReader b) pure returns (bool) {
return CalldataReader.unwrap(a) <= CalldataReader.unwrap(b);
}
/// @notice Modified version of the original CalldataReaderLib
/// @notice No functions were changed, only added new functions
/// @author Aera https://github.com/aera-finance
/// @author philogy <https://github.com/philogy>
library CalldataReaderLib {
////////////////////////////////////////////////////////////
// Errors //
////////////////////////////////////////////////////////////
error ReaderNotAtEnd();
////////////////////////////////////////////////////////////
// Private / Internal Functions //
////////////////////////////////////////////////////////////
function from(bytes calldata data) internal pure returns (CalldataReader reader) {
assembly ("memory-safe") {
reader := data.offset
}
}
function requireAtEndOf(CalldataReader self, bytes calldata data) internal pure {
assembly ("memory-safe") {
let end := add(data.offset, data.length)
if iszero(eq(self, end)) {
mstore(0x00, 0x01842f8c /* ReaderNotAtEnd() */ )
revert(0x1c, 0x04)
}
}
}
function requireAtEndOf(CalldataReader self, CalldataReader end) internal pure {
if (self != end) revert ReaderNotAtEnd();
}
function offset(CalldataReader self) internal pure returns (uint256) {
return CalldataReader.unwrap(self);
}
function readBool(CalldataReader self) internal pure returns (CalldataReader, bool value) {
assembly ("memory-safe") {
value := gt(byte(0, calldataload(self)), 0)
self := add(self, 1)
}
return (self, value);
}
function readU8(CalldataReader self) internal pure returns (CalldataReader, uint8 value) {
assembly ("memory-safe") {
value := byte(0, calldataload(self))
self := add(self, 1)
}
return (self, value);
}
function readU16(CalldataReader self) internal pure returns (CalldataReader, uint16 value) {
assembly ("memory-safe") {
value := shr(240, calldataload(self))
self := add(self, 2)
}
return (self, value);
}
function readU32(CalldataReader self) internal pure returns (CalldataReader, uint32 value) {
assembly ("memory-safe") {
value := shr(224, calldataload(self))
self := add(self, 4)
}
return (self, value);
}
function readI24(CalldataReader self) internal pure returns (CalldataReader, int24 value) {
assembly ("memory-safe") {
value := sar(232, calldataload(self))
self := add(self, 3)
}
return (self, value);
}
function readU40(CalldataReader self) internal pure returns (CalldataReader, uint40 value) {
assembly ("memory-safe") {
value := shr(216, calldataload(self))
self := add(self, 5)
}
return (self, value);
}
function readU64(CalldataReader self) internal pure returns (CalldataReader, uint64 value) {
assembly ("memory-safe") {
value := shr(192, calldataload(self))
self := add(self, 8)
}
return (self, value);
}
function readU128(CalldataReader self) internal pure returns (CalldataReader, uint128 value) {
assembly ("memory-safe") {
value := shr(128, calldataload(self))
self := add(self, 16)
}
return (self, value);
}
function readAddr(CalldataReader self) internal pure returns (CalldataReader, address addr) {
assembly ("memory-safe") {
addr := shr(96, calldataload(self))
self := add(self, 20)
}
return (self, addr);
}
function readU256(CalldataReader self) internal pure returns (CalldataReader, uint256 value) {
assembly ("memory-safe") {
value := calldataload(self)
self := add(self, 32)
}
return (self, value);
}
function readU24End(CalldataReader self) internal pure returns (CalldataReader, CalldataReader end) {
assembly ("memory-safe") {
let len := shr(232, calldataload(self))
self := add(self, 3)
end := add(self, len)
}
return (self, end);
}
function readBytes(CalldataReader self) internal pure returns (CalldataReader, bytes calldata slice) {
assembly ("memory-safe") {
slice.length := shr(232, calldataload(self))
self := add(self, 3)
slice.offset := self
self := add(self, slice.length)
}
return (self, slice);
}
/// ADDED BY AERA
function readU208(CalldataReader self) internal pure returns (CalldataReader, uint208 value) {
assembly ("memory-safe") {
value := shr(48, calldataload(self))
self := add(self, 26)
}
return (self, value);
}
function readOptionalU256(CalldataReader reader) internal pure returns (CalldataReader, uint256 u256) {
bool hasU256;
(reader, hasU256) = reader.readBool();
if (hasU256) {
(reader, u256) = reader.readU256();
}
return (reader, u256);
}
function readBytes32Array(CalldataReader self) internal pure returns (CalldataReader, bytes32[] memory array) {
uint256 length;
(self, length) = readU8(self);
array = new bytes32[](length);
assembly ("memory-safe") {
calldatacopy(add(array, 32), self, mul(length, 32))
self := add(self, mul(length, 32))
}
return (self, array);
}
function readBytesEnd(CalldataReader self) internal pure returns (CalldataReader end) {
assembly ("memory-safe") {
let length := calldataload(sub(self, 32))
end := add(self, length)
}
}
function readBytesEnd(CalldataReader self, bytes calldata data) internal pure returns (CalldataReader end) {
assembly ("memory-safe") {
end := add(self, data.length)
}
}
function readBytesToMemory(CalldataReader self) internal pure returns (CalldataReader, bytes memory data) {
uint256 length;
(self, length) = readU16(self);
return readBytesToMemory(self, length);
}
function readBytesToMemory(CalldataReader self, uint256 length)
internal
pure
returns (CalldataReader, bytes memory data)
{
data = new bytes(length);
assembly ("memory-safe") {
calldatacopy(add(data, 32), self, length)
self := add(self, length)
}
return (self, data);
}
}
// slither-disable-end write-after-write,dead-code// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.29;
import {
CALLDATA_OFFSET,
COPY_WORD_OFFSET,
MASK_16_BIT,
MASK_8_BIT,
RESULTS_INDEX_OFFSET,
WORD_SIZE
} from "src/core/Constants.sol";
import { CalldataReader } from "src/core/libraries/CalldataReader.sol";
/// @title Pipeline
/// @notice Library for handling pipeline operations that copy and paste data between operations
/// @dev Uses bit manipulation and assembly for efficient data movement
library Pipeline {
////////////////////////////////////////////////////////////
// Errors //
////////////////////////////////////////////////////////////
/// @notice Thrown when trying to copy from an invalid position in source data
error Aera__CopyOffsetOutOfBounds();
/// @notice Thrown when trying to paste to an invalid position in target data
error Aera__PasteOffsetOutOfBounds();
////////////////////////////////////////////////////////////
// Private / Internal Functions //
////////////////////////////////////////////////////////////
/// @notice Process pipeline operations by copying data between operations
/// @param data The calldata to modify
/// @param reader Current position in the calldata
/// @param results Array of previous operation results to copy from
function pipe(bytes memory data, CalldataReader reader, bytes[] memory results)
internal
pure
returns (CalldataReader)
{
uint256 clipboardCount;
(reader, clipboardCount) = reader.readU8();
unchecked {
for (; clipboardCount != 0; --clipboardCount) {
uint256 clipboard;
(reader, clipboard) = reader.readU32();
uint256 resultIndex = clipboard >> RESULTS_INDEX_OFFSET;
// Check that the result index is within the results bounds
// will Panic(uint256) revert on out of bounds index
bytes memory result = results[resultIndex];
uint256 copyOffset = (clipboard >> COPY_WORD_OFFSET & MASK_8_BIT) * WORD_SIZE;
// Check that the copy offset is within the result bounds
require(copyOffset + WORD_SIZE <= result.length, Aera__CopyOffsetOutOfBounds());
uint256 pasteOffset = clipboard & MASK_16_BIT;
// Check that the paste offset is within the data bounds
require(pasteOffset + WORD_SIZE <= data.length, Aera__PasteOffsetOutOfBounds());
uint256 operationCalldataPointer;
uint256 resultPointer;
assembly ("memory-safe") {
// Since most operations don’t enter the loop, we avoid caching
// operationCalldataPointer upfront to save gas
operationCalldataPointer := data
resultPointer := result
}
uint256 pastePointer = operationCalldataPointer + pasteOffset + CALLDATA_OFFSET;
uint256 copyPointer = resultPointer + WORD_SIZE + copyOffset;
assembly ("memory-safe") {
mcopy(pastePointer, copyPointer, WORD_SIZE)
}
}
}
return reader;
}
}pragma solidity 0.8.29;
// Sampled from
// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/63851f8de5a6e560e9774832d1a31c43645b73d2/contracts/token/ERC20/ERC20.sol
interface IERC20WithAllowance {
function increaseAllowance(address spender, uint256 addedValue) external returns (bool);
function decreaseAllowance(address spender, uint256 requestedDecrease) external returns (bool);
}// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.29;
/// @dev Implements the spec at https://eips.ethereum.org/EIPS/eip-7726
interface IOracle {
////////////////////////////////////////////////////////////
// Functions //
////////////////////////////////////////////////////////////
/// @notice Returns the value of `baseAmount` of `base` in `quote` terms
/// @dev MUST round down towards 0
/// MUST revert with `OracleUnsupportedPair` if not capable to provide data for the specified `base`
/// and `quote` pair
/// MUST revert with `OracleUntrustedData` if not capable to provide data within a degree of
/// confidence publicly specified
/// @param baseAmount The amount of `base` to convert
/// @param base The asset that the user needs to know the value for
/// @param quote The asset in which the user needs to value the base
/// @return quoteAmount The value of `baseAmount` of `base` in `quote` terms
function getQuote(uint256 baseAmount, address base, address quote) external view returns (uint256 quoteAmount);
}// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.29;
import { BaseVaultParameters } from "src/core/Types.sol";
/// @notice Interface for vault deployer
interface IBaseVaultDeployer {
////////////////////////////////////////////////////////////
// Errors //
////////////////////////////////////////////////////////////
/// @notice Thrown when vault description is empty
error Aera__DescriptionIsEmpty();
/// @notice Thrown when deployed vault address doesn't match expected address
/// @param deployed Address of the deployed vault
/// @param expected Expected address of the vault
error Aera__VaultAddressMismatch(address deployed, address expected);
////////////////////////////////////////////////////////////
// Functions //
////////////////////////////////////////////////////////////
/// @notice Vault parameters for vault deployment
/// @return parameters Parameters used for vault deployment, including owner, submit hooks, and whitelist
/// @dev Necessary to support deterministic vault deployments
function baseVaultParameters() external view returns (BaseVaultParameters memory);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC-165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[ERC].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.29;
/// @title ICallbackHandler
/// @notice Errors used in the CallbackHandler mixin
interface ICallbackHandler {
////////////////////////////////////////////////////////////
// Errors //
////////////////////////////////////////////////////////////
/// @notice Thrown when we receive a callback (or a regular call) that wasn't authorized
error Aera__UnauthorizedCallback();
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/IERC721Receiver.sol)
pragma solidity ^0.8.20;
/**
* @title ERC-721 token receiver interface
* @dev Interface for any contract that wants to support safeTransfers
* from ERC-721 asset contracts.
*/
interface IERC721Receiver {
/**
* @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
* by `operator` from `from`, this function is called.
*
* It must return its Solidity selector to confirm the token transfer.
* If any other value is returned or the interface is not implemented by the recipient, the transfer will be
* reverted.
*
* The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.
*/
function onERC721Received(
address operator,
address from,
uint256 tokenId,
bytes calldata data
) external returns (bytes4);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
/**
* @dev Library of standard hash functions.
*
* _Available since v5.1._
*/
library Hashes {
/**
* @dev Commutative Keccak256 hash of a sorted pair of bytes32. Frequently used when working with merkle proofs.
*
* NOTE: Equivalent to the `standardNodeHash` in our https://github.com/OpenZeppelin/merkle-tree[JavaScript library].
*/
function commutativeKeccak256(bytes32 a, bytes32 b) internal pure returns (bytes32) {
return a < b ? _efficientKeccak256(a, b) : _efficientKeccak256(b, a);
}
/**
* @dev Implementation of keccak256(abi.encode(a, b)) that doesn't allocate or expand memory.
*/
function _efficientKeccak256(bytes32 a, bytes32 b) private pure returns (bytes32 value) {
assembly ("memory-safe") {
mstore(0x00, a)
mstore(0x20, b)
value := keccak256(0x00, 0x40)
}
}
}// SPDX-License-Identifier: MIT
// 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;
assembly ("memory-safe") {
result := store
}
return result;
}
// AddressSet
struct AddressSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(AddressSet storage set, address value) internal returns (bool) {
return _add(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(AddressSet storage set, address value) internal returns (bool) {
return _remove(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(AddressSet storage set, address value) internal view returns (bool) {
return _contains(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(AddressSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(AddressSet storage set, uint256 index) internal view returns (address) {
return address(uint160(uint256(_at(set._inner, index))));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(AddressSet storage set) internal view returns (address[] memory) {
bytes32[] memory store = _values(set._inner);
address[] memory result;
assembly ("memory-safe") {
result := store
}
return result;
}
// UintSet
struct UintSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(UintSet storage set, uint256 value) internal returns (bool) {
return _add(set._inner, bytes32(value));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(UintSet storage set, uint256 value) internal returns (bool) {
return _remove(set._inner, bytes32(value));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(UintSet storage set, uint256 value) internal view returns (bool) {
return _contains(set._inner, bytes32(value));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(UintSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(UintSet storage set, uint256 index) internal view returns (uint256) {
return uint256(_at(set._inner, index));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(UintSet storage set) internal view returns (uint256[] memory) {
bytes32[] memory store = _values(set._inner);
uint256[] memory result;
assembly ("memory-safe") {
result := store
}
return result;
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.29;
/// @title IAuth2Step
/// @notice Interface for the Auth2Step contract
interface IAuth2Step {
////////////////////////////////////////////////////////////
// Events //
////////////////////////////////////////////////////////////
/// @notice Emitted when ownership transfer is initiated
event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner);
////////////////////////////////////////////////////////////
// Errors //
////////////////////////////////////////////////////////////
error Aera__ZeroAddressAuthority();
error Aera__Unauthorized();
////////////////////////////////////////////////////////////
// Functions //
////////////////////////////////////////////////////////////
/// @notice Accept ownership transfer
function acceptOwnership() external;
}{
"remappings": [
"@oz/=lib/openzeppelin-contracts/contracts/",
"@solmate/=src/dependencies/solmate/",
"@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
"erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
"forge-std/=lib/forge-std/src/",
"halmos-cheatcodes/=lib/openzeppelin-contracts/lib/halmos-cheatcodes/src/",
"openzeppelin-contracts/=lib/openzeppelin-contracts/"
],
"optimizer": {
"enabled": true,
"runs": 100000
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "none",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "cancun",
"viaIR": true,
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"bytes","name":"result","type":"bytes"}],"name":"Aera__AfterOperationHooksFailed","type":"error"},{"inputs":[{"internalType":"bytes","name":"result","type":"bytes"}],"name":"Aera__AfterSubmitHooksFailed","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"Aera__AllowanceIsNotZero","type":"error"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"bytes","name":"result","type":"bytes"}],"name":"Aera__BeforeOperationHooksFailed","type":"error"},{"inputs":[],"name":"Aera__BeforeOperationHooksWithConfigurableHooks","type":"error"},{"inputs":[{"internalType":"bytes","name":"result","type":"bytes"}],"name":"Aera__BeforeSubmitHooksFailed","type":"error"},{"inputs":[],"name":"Aera__CalldataTooShort","type":"error"},{"inputs":[],"name":"Aera__CallerIsNotAuthOrGuardian","type":"error"},{"inputs":[],"name":"Aera__CallerIsNotFeeRecipient","type":"error"},{"inputs":[],"name":"Aera__CallerIsNotGuardian","type":"error"},{"inputs":[],"name":"Aera__CallerIsNotProtocolFeeRecipient","type":"error"},{"inputs":[],"name":"Aera__CallerIsNotProvisioner","type":"error"},{"inputs":[],"name":"Aera__CopyOffsetOutOfBounds","type":"error"},{"inputs":[],"name":"Aera__ExpectedCallbackNotReceived","type":"error"},{"inputs":[],"name":"Aera__ExtractionNumberTooLarge","type":"error"},{"inputs":[],"name":"Aera__GuardianNotWhitelisted","type":"error"},{"inputs":[],"name":"Aera__InvalidBeforeOperationHooksReturnDataLength","type":"error"},{"inputs":[],"name":"Aera__NoFeesToClaim","type":"error"},{"inputs":[],"name":"Aera__NoResults","type":"error"},{"inputs":[],"name":"Aera__OffsetOutOfBounds","type":"error"},{"inputs":[],"name":"Aera__PasteOffsetOutOfBounds","type":"error"},{"inputs":[],"name":"Aera__ProofVerificationFailed","type":"error"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"bytes","name":"result","type":"bytes"}],"name":"Aera__SubmissionFailed","type":"error"},{"inputs":[],"name":"Aera__Unauthorized","type":"error"},{"inputs":[],"name":"Aera__UnauthorizedCallback","type":"error"},{"inputs":[],"name":"Aera__UnitsLocked","type":"error"},{"inputs":[],"name":"Aera__ZeroAddressAuthority","type":"error"},{"inputs":[],"name":"Aera__ZeroAddressFeeCalculator","type":"error"},{"inputs":[],"name":"Aera__ZeroAddressFeeRecipient","type":"error"},{"inputs":[],"name":"Aera__ZeroAddressFeeToken","type":"error"},{"inputs":[],"name":"Aera__ZeroAddressGuardian","type":"error"},{"inputs":[],"name":"Aera__ZeroAddressMerkleRoot","type":"error"},{"inputs":[],"name":"Aera__ZeroAddressOwner","type":"error"},{"inputs":[],"name":"Aera__ZeroAddressProvisioner","type":"error"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"allowance","type":"uint256"},{"internalType":"uint256","name":"needed","type":"uint256"}],"name":"ERC20InsufficientAllowance","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"uint256","name":"balance","type":"uint256"},{"internalType":"uint256","name":"needed","type":"uint256"}],"name":"ERC20InsufficientBalance","type":"error"},{"inputs":[{"internalType":"address","name":"approver","type":"address"}],"name":"ERC20InvalidApprover","type":"error"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"}],"name":"ERC20InvalidReceiver","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"}],"name":"ERC20InvalidSender","type":"error"},{"inputs":[{"internalType":"address","name":"spender","type":"address"}],"name":"ERC20InvalidSpender","type":"error"},{"inputs":[],"name":"EnforcedPause","type":"error"},{"inputs":[],"name":"ExpectedPause","type":"error"},{"inputs":[],"name":"ReaderNotAtEnd","type":"error"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"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":"beforeTransferHook","type":"address"}],"name":"BeforeTransferHookSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":true,"internalType":"contract IERC20","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"unitsAmount","type":"uint256"}],"name":"Enter","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":true,"internalType":"contract IERC20","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"unitsAmount","type":"uint256"}],"name":"Exit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"newFeeCalculator","type":"address"}],"name":"FeeCalculatorUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"newFeeRecipient","type":"address"}],"name":"FeeRecipientUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"feeRecipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"fees","type":"uint256"}],"name":"FeesClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"guardian","type":"address"},{"indexed":true,"internalType":"bytes32","name":"root","type":"bytes32"}],"name":"GuardianRootSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferStarted","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"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"protocolFeeRecipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"protocolEarnedFees","type":"uint256"}],"name":"ProtocolFeesClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"provisioner","type":"address"}],"name":"ProvisionerSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"submitHooksAddress","type":"address"}],"name":"SubmitHooksSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"stateMutability":"nonpayable","type":"fallback"},{"inputs":[],"name":"FEE_TOKEN","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"WHITELIST","outputs":[{"internalType":"contract IWhitelist","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"authority","outputs":[{"internalType":"contract Authority","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"beforeTransferHook","outputs":[{"internalType":"contract IBeforeTransferHook","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"guardian","type":"address"}],"name":"checkGuardianWhitelist","outputs":[{"internalType":"bool","name":"isRemoved","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claimFees","outputs":[{"internalType":"uint256","name":"feeRecipientFees","type":"uint256"},{"internalType":"uint256","name":"protocolFees","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claimProtocolFees","outputs":[{"internalType":"uint256","name":"protocolFees","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint256","name":"tokenAmount","type":"uint256"},{"internalType":"uint256","name":"unitsAmount","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"}],"name":"enter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint256","name":"tokenAmount","type":"uint256"},{"internalType":"uint256","name":"unitsAmount","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"}],"name":"exit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"feeCalculator","outputs":[{"internalType":"contract IFeeCalculator","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"feeRecipient","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getActiveGuardians","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrentHookCallType","outputs":[{"internalType":"enum HookCallType","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"guardian","type":"address"}],"name":"getGuardianRoot","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC721Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"provisioner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"guardian","type":"address"}],"name":"removeGuardian","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract Authority","name":"newAuthority","type":"address"}],"name":"setAuthority","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IBeforeTransferHook","name":"hook","type":"address"}],"name":"setBeforeTransferHook","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IFeeCalculator","name":"newFeeCalculator","type":"address"}],"name":"setFeeCalculator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newFeeRecipient","type":"address"}],"name":"setFeeRecipient","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"guardian","type":"address"},{"internalType":"bytes32","name":"root","type":"bytes32"}],"name":"setGuardianRoot","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"provisioner_","type":"address"}],"name":"setProvisioner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract ISubmitHooks","name":"newSubmitHooks","type":"address"}],"name":"setSubmitHooks","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"data","type":"bytes"}],"name":"submit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"submitHooks","outputs":[{"internalType":"contract ISubmitHooks","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]Contract Creation Code
60c0806040523461040f57638b6a7af160e01b81525f8082600481335afa918215610404575f926107e2575b5060405163bf7be0bb60e01b8152905f82600481335afa918215610404575f926107be575b5082516001600160401b0381116104b657600354600181811c911680156107b4575b60208210146106c557601f8111610751575b506020601f82116001146106ee57819293945f926106e3575b50508160011b915f199060031b1c1916176003555b81516001600160401b0381116104b657600454600181811c911680156106d9575b60208210146106c557601f8111610662575b50602092601f821160011461060157928192935f926105f6575b50508160011b915f199060031b1c1916176004555b600580546001600160a81b03191633600881901b610100600160a81b03169190911791829055600680546001600160a01b03191690819055604051929091807f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a35f335f5160206157615f395f51905f528280a363d2c0fc8360e01b8352608083600481335afa928315610404575f9361054a575b5082516001600160a01b031690811561053b5760081c6001600160a01b03163381900361052c57600780546001600160a01b031916831790557f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e227005f80a360208201516001600160a01b0316908161050e575b505060608101516001600160a01b0390811660805260409091015116806104d2575b506040516360f1b94960e01b8152606081600481335afa908115610404575f91610431575b50805160208201516001600160a01b0390811692911690811561042257821561041357813b1561040f57604051630bb621bf60e11b81525f8160048183875af18015610404576103ef575b50604001516001600160a01b031680156103e057600d80546001600160a01b0319908116929092179055600c805490911691909117905560a05260405163cacf144b60e01b8152602081600481335afa9081156103d557829161038f575b50600e80546001600160a01b0319166001600160a01b03929092169182179055604051917f6b354c5f163626c6c1f2a0a7b551ee3cd06cbe5fd35e8fe74388b9c28f05d7689080a2614eba90816108a78239608051818181611be4015281816127d50152612dea015260a05181818161117e015281816125d101526128b90152f35b90506020813d6020116103cd575b816103aa602093836107fe565b810103126103c957516001600160a01b03811681036103c9575f61030d565b5080fd5b3d915061039d565b6040513d84823e3d90fd5b638d107ec560e01b8452600484fd5b6103fc9194505f906107fe565b5f925f6102af565b6040513d5f823e3d90fd5b5f80fd5b632cfbb22360e11b5f5260045ffd5b637cea400f60e01b5f5260045ffd5b90506060813d6060116104ca575b8161044c606093836107fe565b8101031261040f5760405190606082016001600160401b038111838210176104b65760405280516001600160a01b038116810361040f57825260208101516001600160a01b038116810361040f5760208301526104ab90604001610892565b60408201525f610264565b634e487b7160e01b5f52604160045260245ffd5b3d915061043f565b600880546001600160a01b031916821790557f783744567638c41aa4016f43e64e6bc0e58913f22df8da2ee0b6e8f52e4a06585f80a25f61023f565b8117600655335f5160206157615f395f51905f525f80a35f8061021d565b6312fd309160e21b5f5260045ffd5b6320955e0760e21b5f5260045ffd5b9092506080813d6080116105ee575b81610566608093836107fe565b8101031261040f5760405190608082016001600160401b038111838210176104b65760405261059481610892565b825260208101516001600160a01b038116810361040f5760208301526040810151906001600160a01b038216820361040f576040830191909152606001516001600160a01b038116810361040f576060820152915f6101ab565b3d9150610559565b015190505f806100ff565b601f1982169360045f52805f20915f5b86811061064a5750836001959610610632575b505050811b01600455610114565b01515f1960f88460031b161c191690555f8080610624565b91926020600181928685015181550194019201610611565b60045f527f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b601f830160051c810191602084106106bb575b601f0160051c01905b8181106106b057506100e5565b5f81556001016106a3565b909150819061069a565b634e487b7160e01b5f52602260045260245ffd5b90607f16906100d3565b015190505f8061009d565b601f1982169060035f52805f20915f5b81811061073957509583600195969710610721575b505050811b016003556100b2565b01515f1960f88460031b161c191690555f8080610713565b9192602060018192868b0151815501940192016106fe565b60035f527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b601f830160051c810191602084106107aa575b601f0160051c01905b81811061079f5750610084565b5f8155600101610792565b9091508190610789565b90607f1690610072565b6107db9192503d805f833e6107d381836107fe565b810190610821565b905f610050565b6107f79192503d805f833e6107d381836107fe565b905f61002b565b601f909101601f19168101906001600160401b038211908210176104b657604052565b60208183031261040f578051906001600160401b03821161040f570181601f8201121561040f578051906001600160401b0382116104b65760405192610871601f8401601f1916602001856107fe565b8284526020838301011161040f57815f9260208093018386015e8301015290565b51906001600160a01b038216820361040f5756fe60e0806040526004361015610031575b50361561002f573461002b576100236134bf565b602081519101f35b5f80fd5b005b5f905f358060e01c91826306fdde031461307657508163095ea7b314612fc957816310173fbb14612ed6578163150b7a0214612e4957816318160ddd14612e0e5781631bb7cc9914612da057816323b872dd14612c0c578163313ce56714612bd35781633f4ba83a14612af15781634690484014612aa05781634a7d036914612870578163530e1f491461273d5781635c975abb146126fd57816370a082311461269b57816371404156146125f557816373717b081461258757816379ba50971461247d5781637a9e5e4b146122f75781637c554ed1146120b45781637e94eab9146120625781638456cb5914611f5d5781638929565f14611e8a578282638c66d04f14611d56575081638da5cb5b14611d015781639562029414611b1457816395d89b41146119ae57816397a175641461193d57816399588e2b1461186a578163a335811f14611818578163a9059cbb146117c8578163b00eb9fe14611776578163bca563df14611420578163bf7e214f146113ce578163d294f09314611114578163d72c994f1461102a578163dd62ed3e14610fb1578163e30c397814610f5f578163e74b981b14610e5c57508063ef7fa71b146103b8578063f2fde38b146102cc578063f5a689301461027a5763fd8fff570361000f573461027757807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102775760207fb8706f504833578f7e830b12e31c3cfba31669a85b02596177f00c6a7faf6e005c6102668161330a565b604051906102738161330a565b8152f35b80fd5b503461027757807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261027757602073ffffffffffffffffffffffffffffffffffffffff60085416604051908152f35b50346102775760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610277576103046131ff565b73ffffffffffffffffffffffffffffffffffffffff60055460081c16908133036103905773ffffffffffffffffffffffffffffffffffffffff1690817fffffffffffffffffffffffff000000000000000000000000000000000000000060075416176007557f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e227008380a380f35b6004837f4bf4c244000000000000000000000000000000000000000000000000000000008152fd5b50346102775760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102775760043567ffffffffffffffff8111610e5857610408903690600401613245565b91906104126141ce565b7f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f005c610e305760017f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f005d6104653361460e565b939015610e08576008549160018316610d5a575b8394600182019361048c8335871a614295565b9661049a60405198896133a6565b8335871a8089527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0906104cc90614295565b01875b818110610d495750506104e48435881a6142c5565b926040516080526104f660805161338a565b8760805152876020608051015287604060805101528760606080510152876080805101528760a06080510152875b8535891a811061064e575050965060028116610577575b50610551949561054a916143e0565b019061453f565b807f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f005d80f35b8580916040518273ffffffffffffffffffffffffffffffffffffffff60208301927f3a11805f0000000000000000000000000000000000000000000000000000000084526105fa816105ce338d8d60248501614202565b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081018352826133a6565b5193165af1610607614266565b9061053b5761064a906040519182917f4c760ca80000000000000000000000000000000000000000000000000000000083526020600484015260248301906131bc565b0390fd5b61066560148960019a3560601c608051520161463f565b9881019890358a1a805b610c8357506001890198358a1a156106db5790806106be8b8060019573ffffffffffffffffffffffffffffffffffffffff6080515116602082519201905afa6106b6614266565b9283916143a2565b6106c8828d614377565b526106d3818c614377565b505b01610524565b9392999581858b9a859c9a94989a507fffffffff00000000000000000000000000000000000000000000000000000000825160208401518281169160048110610c6e575b50501680602060805101527f095ea7b3000000000000000000000000000000000000000000000000000000008114908115610c44575b50610bc0575b9079ffffffffffffffffffffffffffffffffffffffffffffffffffff61079a73ffffffffffffffffffffffffffffffffffffffff976107aa9594614660565b91909116604060805101526146cb565b9a929a959091951660808051015260a060805101526107cb89358b1a614295565b986107d96040519a8b6133a6565b80358b1a808b526107e990614295565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe060208c01920136833780358c1a60051b80926001830190370193600185018b956002810191358d1a610bb3575b508560805160600152946080515191608051602001517fffffffff0000000000000000000000000000000000000000000000000000000016906080516080015160805160a001516080516040015191604051958695602087019860601b7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000001689526034870152151560f81b603886015260601b7fffffffffffffffffffffffffffffffffffffffff000000000000000000000000166039850152604d84015260301b7fffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000016606d83015280516020819201608784015e81018d6087820152036087017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08101825261096890826133a6565b519020948a955b8a518710156109b157610982878c614377565b5190818110156109a0578c52602052600160408c205b96019561096f565b908c52602052600160408c20610998565b909692989c95508b949a9b919793995003610b8b57898291610a0a828073ffffffffffffffffffffffffffffffffffffffff6080515116606060805101519085519160208701915af1610a02614266565b9485916143a2565b79ffffffffffffffffffffffffffffffffffffffffffffffffffff6040608051015116610b21575b60808051015160028116610a63575b50505090600191610a52828d614377565b52610a5d818c614377565b506106d5565b90829160027fb8706f504833578f7e830b12e31c3cfba31669a85b02596177f00c6a7faf6e005d8273ffffffffffffffffffffffffffffffffffffffff60208451940192165af1610ab2614266565b9015610aeb5750906001918a7fb8706f504833578f7e830b12e31c3cfba31669a85b02596177f00c6a7faf6e005d909150895f80610a41565b8261064a6040519283927f6f9b57ff0000000000000000000000000000000000000000000000000000000084526004840161438b565b90507fa48fd101fc9f41f09dc754b3b14722487070ffbd61259b49558564a3296a3f005c610b63578a90610b5e610b566148e4565b8051906143e0565b610a32565b60048b7f831579e8000000000000000000000000000000000000000000000000000000008152fd5b60048a7fdb87823d000000000000000000000000000000000000000000000000000000008152fd5b903595506022015f610838565b9150506080515173ffffffffffffffffffffffffffffffffffffffff16602487015160c05260405160a05260a051610bf790613341565b60a0515260c05173ffffffffffffffffffffffffffffffffffffffff1660a0516020015286868d6001019d808c60a05191610c3191614377565b52610c3c908c614377565b50909161075b565b7f39509351000000000000000000000000000000000000000000000000000000009150145f610755565b839250829060040360031b1b16165f8061071f565b60048a019935610c9760f882901c8e614377565b5190611fe08160eb1c169082516020830111610d215761ffff9060e01c169184516020840111610cf957916024602080937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff96950101918601015e018061066f565b60048e7f7ea37aee000000000000000000000000000000000000000000000000000000008152fd5b60048e7fc87025ed000000000000000000000000000000000000000000000000000000008152fd5b806060602080938d010152016104cf565b838060405160208101907f772cb3e6000000000000000000000000000000000000000000000000000000008252610d9a816105ce33898960248501614202565b51908273ffffffffffffffffffffffffffffffffffffffff88165af1610dbe614266565b9015610dca5750610479565b61064a906040519182917f072a446d0000000000000000000000000000000000000000000000000000000083526020600484015260248301906131bc565b6004837ff5185ed1000000000000000000000000000000000000000000000000000000008152fd5b6004827f3ee5aeb5000000000000000000000000000000000000000000000000000000008152fd5b5080fd5b905034610e585760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610e585773ffffffffffffffffffffffffffffffffffffffff90610edf610eda7fffffffff00000000000000000000000000000000000000000000000000000000610ed26131ff565b931633613e1c565b6133e7565b168015610f3757807fffffffffffffffffffffffff0000000000000000000000000000000000000000600d541617600d557f7a7b5a0a132f9e0581eb8527f66eae9ee89c2a3e79d4ac7e41a1f1f4d48a7fc28280a280f35b6004827f8d107ec5000000000000000000000000000000000000000000000000000000008152fd5b823461027757807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261027757602073ffffffffffffffffffffffffffffffffffffffff60075416604051908152f35b82346102775760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102775773ffffffffffffffffffffffffffffffffffffffff60406110006131ff565b928261100a613222565b9416815260016020522091165f52602052602060405f2054604051908152f35b823461027757807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102775760405180602060095491828152018091600985527f6e1540171b6c0c960b71a7020d9f60077f6af931a8bbf590da0223dacf75c7af90855b8181106110fe57505050826110a79103836133a6565b604051928392602084019060208552518091526040840192915b8181106110cf575050500390f35b825173ffffffffffffffffffffffffffffffffffffffff168452859450602093840193909201916001016110c1565b8254845260209093019260019283019201611091565b823461027757807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102775773ffffffffffffffffffffffffffffffffffffffff600d541633036113a65773ffffffffffffffffffffffffffffffffffffffff600c5416817f0000000000000000000000000000000000000000000000000000000000000000916040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015260208160248173ffffffffffffffffffffffffffffffffffffffff88165afa90811561136357839161136e575b5090602460609260405194859384927fac68a74800000000000000000000000000000000000000000000000000000000845260048401525af1801561136357839284928592611313575b5083156112eb576040945061125784338361416a565b84518481527f9493e5bbe4e8e0ac67284469a2d677403d0378a85a59e341d3abc433d0d9a20960203392a28280611298575b50505082519182526020820152f35b826112a29261416a565b7f0f58cb6262d88971b87878468debb7bc81f1714be597710e16a9fb84c66ff7b5602073ffffffffffffffffffffffffffffffffffffffff8651938585521692a2838082611289565b6004857f7c29a484000000000000000000000000000000000000000000000000000000008152fd5b93509150506060823d60601161135b575b81611331606093836133a6565b810103126113575781519061134d60406020850151940161344c565b9192919085611241565b8280fd5b3d9150611324565b6040513d85823e3d90fd5b919250506020813d60201161139e575b8161138b602093836133a6565b8101031261002b575183919060246111f7565b3d915061137e565b807f286713000000000000000000000000000000000000000000000000000000000060049252fd5b823461027757807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261027757602073ffffffffffffffffffffffffffffffffffffffff60065416604051908152f35b82346102775761142f36613273565b61143a9391936141ce565b73ffffffffffffffffffffffffffffffffffffffff600f5416330361174e5773ffffffffffffffffffffffffffffffffffffffff90836116f4575b169283156116c8578573ffffffffffffffffffffffffffffffffffffffff600e541680611638575b50600190611599575b15611571576002548181018091116115445773ffffffffffffffffffffffffffffffffffffffff7f59009eaf55f74c19a447f53174708d6c3b16e27d0dd94f6a2a8845f6728b1614936040938293600255878a5289602052848a20818154019055878a7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60208851858152a38451968752602087015216951692a480f35b6024877f4e487b710000000000000000000000000000000000000000000000000000000081526011600452fd5b6004867f60134b78000000000000000000000000000000000000000000000000000000008152fd5b506024602073ffffffffffffffffffffffffffffffffffffffff600f5416604051928380927f9428b68e0000000000000000000000000000000000000000000000000000000082528b60048301525afa90811561162d5787916115fe575b50156114a6565b611620915060203d602011611626575b61161881836133a6565b81019061346d565b876115f7565b503d61160e565b6040513d89823e3d90fd5b73ffffffffffffffffffffffffffffffffffffffff600f5416813b156113575782906064604051809481937fabd626b00000000000000000000000000000000000000000000000000000000083528560048401528b602484015260448301525afa80156116bd571561149d57816116ae916133a6565b6116b957858761149d565b8580fd5b6040513d84823e3d90fd5b6024867fec442f0500000000000000000000000000000000000000000000000000000000815280600452fd5b6117496040517f23b872dd0000000000000000000000000000000000000000000000000000000060208201528388166024820152306044820152856064820152606481526117436084826133a6565b8461456e565b611475565b6004867f18b2ef41000000000000000000000000000000000000000000000000000000008152fd5b823461027757807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261027757602073ffffffffffffffffffffffffffffffffffffffff600c5416604051908152f35b82346102775760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102775761180d6118036131ff565b6024359033613f29565b602060405160018152f35b823461027757807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261027757602073ffffffffffffffffffffffffffffffffffffffff600e5416604051908152f35b905034610e585760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610e58576004359073ffffffffffffffffffffffffffffffffffffffff821680920361135757610eda7fffffffff000000000000000000000000000000000000000000000000000000006118ec921633613e1c565b807fffffffffffffffffffffffff000000000000000000000000000000000000000060085416176008557f783744567638c41aa4016f43e64e6bc0e58913f22df8da2ee0b6e8f52e4a06588280a280f35b82346102775760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102775760209061199673ffffffffffffffffffffffffffffffffffffffff6119906131ff565b1661460e565b9190156119a757505b604051908152f35b905061199f565b823461027757807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610277576040519080600454908160011c91600181168015611b0a575b602084108114611add57838652908115611a985750600114611a3b575b611a3784611a23818603826133a6565b6040519182916020835260208301906131bc565b0390f35b600481527f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b939250905b808210611a7e57509091508101602001611a2382611a13565b919260018160209254838588010152019101909291611a65565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660208087019190915292151560051b85019092019250611a239150839050611a13565b6024837f4e487b710000000000000000000000000000000000000000000000000000000081526022600452fd5b92607f16926119f6565b905034610e585760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610e585773ffffffffffffffffffffffffffffffffffffffff611b626131ff565b611b94610eda7fffffffff00000000000000000000000000000000000000000000000000000000602435951633613e1c565b168015611cd9576040517f3af32abf00000000000000000000000000000000000000000000000000000000815281600482015260208160248173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165afa908115611cce578491611caf575b5015611c87578115611c5f57808352600b602052816040842055611c3781614e53565b507f4b06eaf2f723ef457d7e5a4bdd29d59455b133f37737541f209bbe963aba77fe8380a380f35b6004837fc8ddd4ba000000000000000000000000000000000000000000000000000000008152fd5b6004837feb1c650e000000000000000000000000000000000000000000000000000000008152fd5b611cc8915060203d6020116116265761161881836133a6565b5f611c14565b6040513d86823e3d90fd5b6004837fa9debd38000000000000000000000000000000000000000000000000000000008152fd5b823461027757807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261027757602073ffffffffffffffffffffffffffffffffffffffff60055460081c16604051908152f35b915034611e875760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112611e87576004359073ffffffffffffffffffffffffffffffffffffffff8216809203611e8557610eda7fffffffff00000000000000000000000000000000000000000000000000000000611dd8921633613e1c565b807fffffffffffffffffffffffff0000000000000000000000000000000000000000600c541617600c5560405190807f3e762c7e655633ce63121393b9694f9ca1883d14d18f48f1be55e5dc7a9fb6c18480a280611e34575050f35b803b15611e8557816004818580947f176c437e0000000000000000000000000000000000000000000000000000000083525af180156116bd57611e745750f35b81611e7e916133a6565b6102775780f35b505b50fd5b905034610e585760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610e58576004359073ffffffffffffffffffffffffffffffffffffffff821680920361135757610eda7fffffffff00000000000000000000000000000000000000000000000000000000611f0c921633613e1c565b807fffffffffffffffffffffffff0000000000000000000000000000000000000000600e541617600e557f6b354c5f163626c6c1f2a0a7b551ee3cd06cbe5fd35e8fe74388b9c28f05d7688280a280f35b905034610e5857817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610e58577fffffffff00000000000000000000000000000000000000000000000000000000611fb9911633613e1c565b801561204d575b1561202557611fcd6141ce565b60017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0060055416176005557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586020604051338152a180f35b807fdb78883e0000000000000000000000000000000000000000000000000000000060049252fd5b50338152600a60205260408120541515611fc0565b823461027757807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261027757602073ffffffffffffffffffffffffffffffffffffffff600f5416604051908152f35b823461002b576120c336613273565b936120d0939192936141ce565b73ffffffffffffffffffffffffffffffffffffffff600f541633036122cf5773ffffffffffffffffffffffffffffffffffffffff169182156122a35773ffffffffffffffffffffffffffffffffffffffff600e541680612217575b508286528560205260408620548181106121e35773ffffffffffffffffffffffffffffffffffffffff7ffb39f3e6cf68759732e3db2247713a4eca21e27eab5d056a07f276c35867f73f93836040948394888c528b60205203858b2055806002540360025589877fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60208851858152a3876121d3575b8451978852602088015216951693a480f35b6121de888a8461416a565b6121c1565b8692506064937fe450d38c000000000000000000000000000000000000000000000000000000008452600452602452604452fd5b73ffffffffffffffffffffffffffffffffffffffff600f5416813b1561002b575f906064604051809481937fabd626b000000000000000000000000000000000000000000000000000000000835289600484015285602484015260448301525afa8015612298571561212b576122909196505f906133a6565b5f948661212b565b6040513d5f823e3d90fd5b7f96c6fd1e000000000000000000000000000000000000000000000000000000005f525f60045260245ffd5b7f18b2ef41000000000000000000000000000000000000000000000000000000005f5260045ffd5b3461002b5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261002b576004359073ffffffffffffffffffffffffffffffffffffffff821680920361002b5773ffffffffffffffffffffffffffffffffffffffff60055460081c1633149081156123c5575b501561002b57807fffffffffffffffffffffffff00000000000000000000000000000000000000006006541617600655337fa3396fd7f6e0a21b50e5089d2da70d5ac0a3bbbd1f617a93f134b763899801985f80a3005b6006546040517fb70096130000000000000000000000000000000000000000000000000000000081523360048201523060248201527fffffffff000000000000000000000000000000000000000000000000000000009290921660448301529091506020908290606490829073ffffffffffffffffffffffffffffffffffffffff165afa908115612298575f9161245e575b508261236e565b612477915060203d6020116116265761161881836133a6565b82612457565b3461002b575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261002b5760075473ffffffffffffffffffffffffffffffffffffffff81169081330361255f577fffffffffffffffffffffffff0000000000000000000000000000000000000000906005547fffffffffffffffffffffff0000000000000000000000000000000000000000ff74ffffffffffffffffffffffffffffffffffffffff008360081b1691161760055516600755337f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3005b7f4bf4c244000000000000000000000000000000000000000000000000000000005f5260045ffd5b3461002b575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261002b57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b3461002b5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261002b5773ffffffffffffffffffffffffffffffffffffffff5f9161266a610eda7fffffffff00000000000000000000000000000000000000000000000000000000610ed26131ff565b16612674816145f5565b507f4b06eaf2f723ef457d7e5a4bdd29d59455b133f37737541f209bbe963aba77fe8280a3005b3461002b5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261002b5773ffffffffffffffffffffffffffffffffffffffff6126e76131ff565b165f525f602052602060405f2054604051908152f35b3461002b575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261002b57602060ff600554166040519015158152f35b3461002b5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261002b576127746131ff565b5f73ffffffffffffffffffffffffffffffffffffffff604051927f3af32abf0000000000000000000000000000000000000000000000000000000084521680600484015260208360248173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165afa8015612298576020935f91612853575b5015612819575b506040519015158152f35b9050612824816145f5565b505f6001917f4b06eaf2f723ef457d7e5a4bdd29d59455b133f37737541f209bbe963aba77fe8280a38261280e565b61286a9150843d86116116265761161881836133a6565b84612807565b3461002b575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261002b5773ffffffffffffffffffffffffffffffffffffffff600c54167f0000000000000000000000000000000000000000000000000000000000000000906040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015260208160248173ffffffffffffffffffffffffffffffffffffffff87165afa908115612298575f91612a6e575b505f916024604092835194859384927f01c621c100000000000000000000000000000000000000000000000000000000845260048401525af18015612298575f915f91612a2e575b5073ffffffffffffffffffffffffffffffffffffffff811690813303612a065782156129de57826129ad9160209561416a565b7f0f58cb6262d88971b87878468debb7bc81f1714be597710e16a9fb84c66ff7b583604051848152a2604051908152f35b7f7c29a484000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f3b4f2663000000000000000000000000000000000000000000000000000000005f5260045ffd5b9150506040813d604011612a66575b81612a4a604093836133a6565b8101031261002b57612a6060208251920161344c565b8361297a565b3d9150612a3d565b90506020813d602011612a98575b81612a89602093836133a6565b8101031261002b57515f612932565b3d9150612a7c565b3461002b575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261002b57602073ffffffffffffffffffffffffffffffffffffffff600d5416604051908152f35b3461002b575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261002b57610eda7fffffffff00000000000000000000000000000000000000000000000000000000612b4e921633613e1c565b60055460ff811615612bab577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166005557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa6020604051338152a1005b7f8dfc202b000000000000000000000000000000000000000000000000000000005f5260045ffd5b3461002b575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261002b57602060405160128152f35b3461002b5760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261002b57612c436131ff565b612c4b613222565b6044359073ffffffffffffffffffffffffffffffffffffffff831692835f52600160205260405f2073ffffffffffffffffffffffffffffffffffffffff33165f5260205260405f20547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8103612cc7575b5061180d9350613f29565b838110612d6c578415612d40573315612d145761180d945f52600160205260405f2073ffffffffffffffffffffffffffffffffffffffff33165f526020528360405f209103905584612cbc565b7f94280d62000000000000000000000000000000000000000000000000000000005f525f60045260245ffd5b7fe602df05000000000000000000000000000000000000000000000000000000005f525f60045260245ffd5b83907ffb8f41b2000000000000000000000000000000000000000000000000000000005f523360045260245260445260645ffd5b3461002b575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261002b57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b3461002b575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261002b576020600254604051908152f35b3461002b5760807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261002b57612e806131ff565b50612e89613222565b5060643567ffffffffffffffff811161002b57612eaa903690600401613245565b505060206040517f150b7a02000000000000000000000000000000000000000000000000000000008152f35b3461002b5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261002b5773ffffffffffffffffffffffffffffffffffffffff90612f4a610eda7fffffffff00000000000000000000000000000000000000000000000000000000610ed26131ff565b168015612fa157807fffffffffffffffffffffffff0000000000000000000000000000000000000000600f541617600f557f606753945c222bd6eb6533c506303e56af1df290ae8e1557219f83770cfa35155f80a2005b7f17c1ab6a000000000000000000000000000000000000000000000000000000005f5260045ffd5b3461002b5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261002b576130006131ff565b602435903315612d405773ffffffffffffffffffffffffffffffffffffffff16908115612d1457335f52600160205260405f20825f526020528060405f20556040519081527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560203392a3602060405160018152f35b3461002b575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261002b575f6003548060011c906001811680156131b2575b6020831081146131855782855290811561314357506001146130e5575b611a3783611a23818503826133a6565b91905060035f527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b915f905b80821061312957509091508101602001611a236130d5565b919260018160209254838588010152019101909291613111565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660208086019190915291151560051b84019091019150611a2390506130d5565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b91607f16916130b8565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f602080948051918291828752018686015e5f8582860101520116010190565b6004359073ffffffffffffffffffffffffffffffffffffffff8216820361002b57565b6024359073ffffffffffffffffffffffffffffffffffffffff8216820361002b57565b9181601f8401121561002b5782359167ffffffffffffffff831161002b576020838186019501011161002b57565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60a091011261002b5760043573ffffffffffffffffffffffffffffffffffffffff8116810361002b579060243573ffffffffffffffffffffffffffffffffffffffff8116810361002b5790604435906064359060843573ffffffffffffffffffffffffffffffffffffffff8116810361002b5790565b6003111561331457565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b6040810190811067ffffffffffffffff82111761335d57604052565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b60c0810190811067ffffffffffffffff82111761335d57604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761335d57604052565b156133ee57565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a454400000000000000000000000000000000000000006044820152fd5b519073ffffffffffffffffffffffffffffffffffffffff8216820361002b57565b9081602091031261002b5751801515810361002b5790565b67ffffffffffffffff811161335d57601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b7fa48fd101fc9f41f09dc754b3b14722487070ffbd61259b49558564a3296a3f005c5f7fa48fd101fc9f41f09dc754b3b14722487070ffbd61259b49558564a3296a3f005d7fffffffff000000000000000000000000000000000000000000000000000000008160301b167fffffffff000000000000000000000000000000000000000000000000000000005f351603613df45773ffffffffffffffffffffffffffffffffffffffff81163303613df4577f30fb041442610fd0a22e4654f60ea1c715088ef7320b5ec0c4e75cbdd99dbe005c5f7f30fb041442610fd0a22e4654f60ea1c715088ef7320b5ec0c4e75cbdd99dbe005d8015613df45761ffff808360a01c1614613dde575f9060a083901c61ffff166001810191906135e69035841a614295565b926135f460405194856133a6565b60a085901c61ffff16355f1a8085527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09061362e90614295565b015f5b818110613dcd57505061364d61ffff8660a01c16355f1a6142c5565b9160405161365a8161338a565b5f81525f60208201525f60408201525f60608201525f60808201525f60a08201525f5b61ffff8860a01c16355f1a8110613791575050509061369c9190614a0e565b6060916001820191355f1a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff810161371557505061371291506136de9061463f565b925b60a01c61ffff167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0810135019061453f565b90565b600214613728575b5061371291926136e0565b80925051801561376957613712927fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff613762920190614377565b519161371d565b7f26779f3c000000000000000000000000000000000000000000000000000000005f5260045ffd5b6137a66014876001983560601c85520161463f565b9681019690355f1a805b613d0a57506001870196355f1a156138125790806137f55f8060019573ffffffffffffffffffffffffffffffffffffffff885116602082519201905afa6106b6614266565b6137ff828a614377565b5261380a8189614377565b505b0161367d565b818194999a9296939598978a7fffffffff00000000000000000000000000000000000000000000000000000000845160208601518281169160048110613cf5575b5050168060208a01527f095ea7b3000000000000000000000000000000000000000000000000000000008114908115613ccb575b50613c63575b61389691614660565b79ffffffffffffffffffffffffffffffffffffffffffffffffffff1660408801526138c192906146cb565b73ffffffffffffffffffffffffffffffffffffffff16608088015260a08701529099906138f08b355f1a614295565b9a6040519b6138ff908d6133a6565b80355f1a808d5261390f90614295565b9060208d01917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe00136833780355f1a60051b80926001830190370186600182015f926002810191355f1a151593613a67926087927fffffffffffffffffffffffffffffffffffffffff00000000000000000000000096613c55575b50602090826060870152967fffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000008651937fffffffff00000000000000000000000000000000000000000000000000000000848901511697896080820151604060a0840151930151936040519b8c99898b019e8f9160601b16905260348a0152151560f81b603889015260601b166039870152604d86015260301b16606d8401528051918291018484015e81015f8382015203017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081018352826133a6565b519020935f945b8c51861015613ab057613a81868e614377565b519081811015613a9f575f52602052600160405f205b950194613a6e565b905f52602052600160405f20613a97565b9097939b508a95929a91989996945003613c2d578190613b035f8073ffffffffffffffffffffffffffffffffffffffff87511660608801519085519160208701915af1613afb614266565b9384916143a2565b79ffffffffffffffffffffffffffffffffffffffffffffffffffff604085015116613bde575b608084015160028116613b58575b505090600191613b47828a614377565b52613b528189614377565b5061380c565b5f91829160027fb8706f504833578f7e830b12e31c3cfba31669a85b02596177f00c6a7faf6e005d8273ffffffffffffffffffffffffffffffffffffffff60208451940192165af1613ba8614266565b9015610aeb5750906001915f7fb8706f504833578f7e830b12e31c3cfba31669a85b02596177f00c6a7faf6e005d90915f613b37565b7fa48fd101fc9f41f09dc754b3b14722487070ffbd61259b49558564a3296a3f005c15613b29577f831579e8000000000000000000000000000000000000000000000000000000005f5260045ffd5b7fdb87823d000000000000000000000000000000000000000000000000000000005f5260045ffd5b90359150602201602061398a565b875173ffffffffffffffffffffffffffffffffffffffff169a60248501516040519c613c8e8e613341565b8d5273ffffffffffffffffffffffffffffffffffffffff1660208d0152806001019b613cba8289614377565b52613cc59087614377565b5061388d565b7f39509351000000000000000000000000000000000000000000000000000000009150145f613887565b839250829060040360031b1b16165f80613853565b9660048135910197613d1f8260f81c8b614377565b51611fe08360eb1c169281516020850111613da55761ffff9060e01c1684516020820111613d7d57602460207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9581940101918601015e01806137b0565b7f7ea37aee000000000000000000000000000000000000000000000000000000005f5260045ffd5b7fc87025ed000000000000000000000000000000000000000000000000000000005f5260045ffd5b806060602080938901015201613631565b5050604051613dee6020826133a6565b5f815290565b7fc6248746000000000000000000000000000000000000000000000000000000005f5260045ffd5b73ffffffffffffffffffffffffffffffffffffffff60065416918215159283613e71575b50508115613e4c575090565b905073ffffffffffffffffffffffffffffffffffffffff8060055460081c1691161490565b6040517fb700961300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff841660048201523060248201527fffffffff0000000000000000000000000000000000000000000000000000000092909216604483015291925090602090829060649082905afa908115612298575f91613f0a575b50905f80613e40565b613f23915060203d6020116116265761161881836133a6565b5f613f01565b9173ffffffffffffffffffffffffffffffffffffffff5f93169182156122a35773ffffffffffffffffffffffffffffffffffffffff1692831561413e5773ffffffffffffffffffffffffffffffffffffffff600e5416806140bf575b506024602073ffffffffffffffffffffffffffffffffffffffff600f5416604051928380927f9428b68e0000000000000000000000000000000000000000000000000000000082528860048301525afa9081156116bd5782916140a0575b50614078578281528060205260408120548281106140455791604082827fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef958760209652828652038282205586815280845220818154019055604051908152a3565b6064937fe450d38c0000000000000000000000000000000000000000000000000000000083949352600452602452604452fd5b807f60134b780000000000000000000000000000000000000000000000000000000060049252fd5b6140b9915060203d6020116116265761161881836133a6565b5f613fe3565b73ffffffffffffffffffffffffffffffffffffffff600f5416813b1561002b575f906064604051809481937fabd626b00000000000000000000000000000000000000000000000000000000083528960048401528a602484015260448301525afa80156122985715613f855761413791505f906133a6565b5f5f613f85565b7fec442f05000000000000000000000000000000000000000000000000000000005f525f60045260245ffd5b6141cc9273ffffffffffffffffffffffffffffffffffffffff604051937fa9059cbb0000000000000000000000000000000000000000000000000000000060208601521660248401526044830152604482526141c76064836133a6565b61456e565b565b60ff600554166141da57565b7fd93c0665000000000000000000000000000000000000000000000000000000005f5260045ffd5b9160607fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8373ffffffffffffffffffffffffffffffffffffffff94602096999899604089528160408a0152858901375f84828901015201168401019416910152565b3d15614290573d9061427782613485565b9161428560405193846133a6565b82523d5f602084013e565b606090565b67ffffffffffffffff811161335d5760051b60200190565b604051906142ba82613341565b5f6020838281520152565b906142cf82614295565b6142dc60405191826133a6565b8281527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe061430a8294614295565b01905f5b82811061431a57505050565b60209060405161432981613341565b5f81525f838201528282850101520161430e565b80511561434a5760200190565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b805182101561434a5760209160051b010190565b6040906137129392815281602082015201906131bc565b156143ab575050565b61064a6040519283927f248ef89c0000000000000000000000000000000000000000000000000000000084526004840161438b565b90806143ea6142ad565b505b6143f4575050565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff016144208183614377565b5173ffffffffffffffffffffffffffffffffffffffff815116906020810190602073ffffffffffffffffffffffffffffffffffffffff8351166044604051809681937fdd62ed3e00000000000000000000000000000000000000000000000000000000835230600484015260248301525afa928315612298575f936144f8575b5073ffffffffffffffffffffffffffffffffffffffff80915116915116916144ca575050806143ec565b7f715879af000000000000000000000000000000000000000000000000000000005f5260045260245260445ffd5b9092506020813d8211614537575b81614513602093836133a6565b8101031261002b57519173ffffffffffffffffffffffffffffffffffffffff6144a0565b3d9150614506565b0361454657565b7f01842f8c000000000000000000000000000000000000000000000000000000005f5260045ffd5b905f602091828151910182855af115612298575f513d6145ec575073ffffffffffffffffffffffffffffffffffffffff81163b155b6145aa5750565b73ffffffffffffffffffffffffffffffffffffffff907f5274afe7000000000000000000000000000000000000000000000000000000005f521660045260245ffd5b600114156145a3565b61371290805f52600b6020525f60408120556009614c98565b805f52600b60205260405f205480155f1461463757505f52600a60205260405f20541515905f90565b600192909150565b906002823560f01c61465081614b35565b9381838201602087013701019190565b9190916001810181355f1a156146c35790601b913560301c93847fa48fd101fc9f41f09dc754b3b14722487070ffbd61259b49558564a3296a3f005d7f30fb041442610fd0a22e4654f60ea1c715088ef7320b5ec0c4e75cbdd99dbe005d019190565b92505f919050565b909291906001810181355f1a80156148c5576080607f82169116156148a9578061485457503560601c916060946001841661470d575b505060150192915f9190565b5f91929550819060017fb8706f504833578f7e830b12e31c3cfba31669a85b02596177f00c6a7faf6e005d60208151910182865af19061474b614266565b911561481f5750601f8151166147f7575f7fb8706f504833578f7e830b12e31c3cfba31669a85b02596177f00c6a7faf6e005d805181019060208181840193031261002b5760208101519067ffffffffffffffff821161002b57019080603f8301121561002b5760208201516147c081613485565b916147ce60405193846133a6565b8183526040848301011161002b576020815f926040601596018386015e8301015293905f614701565b7fbe72399a000000000000000000000000000000000000000000000000000000005f5260045ffd5b61064a6040519283927f883d36b40000000000000000000000000000000000000000000000000000000084526004840161438b565b93919492503592602185013560601c9260018416614881576035918561487992614b84565b940193929190565b7ffafeed10000000000000000000000000000000000000000000000000000000005f5260045ffd5b602192959493506148bc91358095614b84565b93019291905f90565b509293505050906040516148da6020826133a6565b5f8152905f905f90565b7fba2cfcc1b17a97110b1fb218b61c42c0e510c6913e669a69d4ade619ace66c005c908160a01c8015614a07575f7fba2cfcc1b17a97110b1fb218b61c42c0e510c6913e669a69d4ade619ace66c005d61493d816142c5565b927fba2cfcc1b17a97110b1fb218b61c42c0e510c6913e669a69d4ade619ace66c019073ffffffffffffffffffffffffffffffffffffffff825c816040519361498585613341565b1683521660208201526149978561433d565b526149a18461433d565b5060015b8281106149b157505050565b60019060028284015c93019273ffffffffffffffffffffffffffffffffffffffff845c81604051936149e285613341565b1683521660208201526149f58288614377565b52614a008187614377565b50016149a5565b5060609150565b8115614b31577fba2cfcc1b17a97110b1fb218b61c42c0e510c6913e669a69d4ade619ace66c00905f825c60a081901c614a466142ad565b5080614ad25750505073ffffffffffffffffffffffffffffffffffffffff60206001614a718461433d565b5194838651168760a01b17815d0193015116825d60015b838110614a955750505050565b8073ffffffffffffffffffffffffffffffffffffffff60206002614abb60019587614377565b5196838851168682015d0195015116845d01614a88565b9085947fba2cfcc1b17a97110b1fb218b61c42c0e510c6913e669a69d4ade619ace66bff93966c01fffffffffffffffffffffffe930160a01b73ffffffffffffffffffffffffffffffffffffffff831617905d609f1c16019192614a88565b5050565b90614b3f82613485565b614b4c60405191826133a6565b8281527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0614b7a8294613485565b0190602036910137565b90916011811015614c5b57614b9b8160051b614b35565b92825160248110614c33577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015f915f925b848410614bdd5750505050505090565b8160f01c836004820111614c0b5760019160246020928901015182828b010152019160101b93019290614bcd565b7f66a468ef000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f6809cb33000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f9bbf46dc000000000000000000000000000000000000000000000000000000005f5260045ffd5b805482101561434a575f5260205f2001905f90565b906001820191815f528260205260405f20548015155f14614e4b577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8101818111614e1e578254907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8201918211614e1e57818103614db4575b50505080548015614d87577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190614d4a8282614c83565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82549160031b1b19169055555f526020525f6040812055600190565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603160045260245ffd5b614e09614dc4614dd49386614c83565b90549060031b1c92839286614c83565b81939154907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9060031b92831b921b19161790565b90555f528360205260405f20555f8080614d12565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b505050505f90565b805f52600a60205260405f2054155f14614ea8576009546801000000000000000081101561335d57614e91614dd48260018594016009556009614c83565b9055600954905f52600a60205260405f2055600190565b505f9056fea164736f6c634300081d000aa3396fd7f6e0a21b50e5089d2da70d5ac0a3bbbd1f617a93f134b76389980198
Deployed Bytecode
0x60e0806040526004361015610031575b50361561002f573461002b576100236134bf565b602081519101f35b5f80fd5b005b5f905f358060e01c91826306fdde031461307657508163095ea7b314612fc957816310173fbb14612ed6578163150b7a0214612e4957816318160ddd14612e0e5781631bb7cc9914612da057816323b872dd14612c0c578163313ce56714612bd35781633f4ba83a14612af15781634690484014612aa05781634a7d036914612870578163530e1f491461273d5781635c975abb146126fd57816370a082311461269b57816371404156146125f557816373717b081461258757816379ba50971461247d5781637a9e5e4b146122f75781637c554ed1146120b45781637e94eab9146120625781638456cb5914611f5d5781638929565f14611e8a578282638c66d04f14611d56575081638da5cb5b14611d015781639562029414611b1457816395d89b41146119ae57816397a175641461193d57816399588e2b1461186a578163a335811f14611818578163a9059cbb146117c8578163b00eb9fe14611776578163bca563df14611420578163bf7e214f146113ce578163d294f09314611114578163d72c994f1461102a578163dd62ed3e14610fb1578163e30c397814610f5f578163e74b981b14610e5c57508063ef7fa71b146103b8578063f2fde38b146102cc578063f5a689301461027a5763fd8fff570361000f573461027757807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102775760207fb8706f504833578f7e830b12e31c3cfba31669a85b02596177f00c6a7faf6e005c6102668161330a565b604051906102738161330a565b8152f35b80fd5b503461027757807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261027757602073ffffffffffffffffffffffffffffffffffffffff60085416604051908152f35b50346102775760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610277576103046131ff565b73ffffffffffffffffffffffffffffffffffffffff60055460081c16908133036103905773ffffffffffffffffffffffffffffffffffffffff1690817fffffffffffffffffffffffff000000000000000000000000000000000000000060075416176007557f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e227008380a380f35b6004837f4bf4c244000000000000000000000000000000000000000000000000000000008152fd5b50346102775760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102775760043567ffffffffffffffff8111610e5857610408903690600401613245565b91906104126141ce565b7f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f005c610e305760017f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f005d6104653361460e565b939015610e08576008549160018316610d5a575b8394600182019361048c8335871a614295565b9661049a60405198896133a6565b8335871a8089527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0906104cc90614295565b01875b818110610d495750506104e48435881a6142c5565b926040516080526104f660805161338a565b8760805152876020608051015287604060805101528760606080510152876080805101528760a06080510152875b8535891a811061064e575050965060028116610577575b50610551949561054a916143e0565b019061453f565b807f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f005d80f35b8580916040518273ffffffffffffffffffffffffffffffffffffffff60208301927f3a11805f0000000000000000000000000000000000000000000000000000000084526105fa816105ce338d8d60248501614202565b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081018352826133a6565b5193165af1610607614266565b9061053b5761064a906040519182917f4c760ca80000000000000000000000000000000000000000000000000000000083526020600484015260248301906131bc565b0390fd5b61066560148960019a3560601c608051520161463f565b9881019890358a1a805b610c8357506001890198358a1a156106db5790806106be8b8060019573ffffffffffffffffffffffffffffffffffffffff6080515116602082519201905afa6106b6614266565b9283916143a2565b6106c8828d614377565b526106d3818c614377565b505b01610524565b9392999581858b9a859c9a94989a507fffffffff00000000000000000000000000000000000000000000000000000000825160208401518281169160048110610c6e575b50501680602060805101527f095ea7b3000000000000000000000000000000000000000000000000000000008114908115610c44575b50610bc0575b9079ffffffffffffffffffffffffffffffffffffffffffffffffffff61079a73ffffffffffffffffffffffffffffffffffffffff976107aa9594614660565b91909116604060805101526146cb565b9a929a959091951660808051015260a060805101526107cb89358b1a614295565b986107d96040519a8b6133a6565b80358b1a808b526107e990614295565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe060208c01920136833780358c1a60051b80926001830190370193600185018b956002810191358d1a610bb3575b508560805160600152946080515191608051602001517fffffffff0000000000000000000000000000000000000000000000000000000016906080516080015160805160a001516080516040015191604051958695602087019860601b7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000001689526034870152151560f81b603886015260601b7fffffffffffffffffffffffffffffffffffffffff000000000000000000000000166039850152604d84015260301b7fffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000016606d83015280516020819201608784015e81018d6087820152036087017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08101825261096890826133a6565b519020948a955b8a518710156109b157610982878c614377565b5190818110156109a0578c52602052600160408c205b96019561096f565b908c52602052600160408c20610998565b909692989c95508b949a9b919793995003610b8b57898291610a0a828073ffffffffffffffffffffffffffffffffffffffff6080515116606060805101519085519160208701915af1610a02614266565b9485916143a2565b79ffffffffffffffffffffffffffffffffffffffffffffffffffff6040608051015116610b21575b60808051015160028116610a63575b50505090600191610a52828d614377565b52610a5d818c614377565b506106d5565b90829160027fb8706f504833578f7e830b12e31c3cfba31669a85b02596177f00c6a7faf6e005d8273ffffffffffffffffffffffffffffffffffffffff60208451940192165af1610ab2614266565b9015610aeb5750906001918a7fb8706f504833578f7e830b12e31c3cfba31669a85b02596177f00c6a7faf6e005d909150895f80610a41565b8261064a6040519283927f6f9b57ff0000000000000000000000000000000000000000000000000000000084526004840161438b565b90507fa48fd101fc9f41f09dc754b3b14722487070ffbd61259b49558564a3296a3f005c610b63578a90610b5e610b566148e4565b8051906143e0565b610a32565b60048b7f831579e8000000000000000000000000000000000000000000000000000000008152fd5b60048a7fdb87823d000000000000000000000000000000000000000000000000000000008152fd5b903595506022015f610838565b9150506080515173ffffffffffffffffffffffffffffffffffffffff16602487015160c05260405160a05260a051610bf790613341565b60a0515260c05173ffffffffffffffffffffffffffffffffffffffff1660a0516020015286868d6001019d808c60a05191610c3191614377565b52610c3c908c614377565b50909161075b565b7f39509351000000000000000000000000000000000000000000000000000000009150145f610755565b839250829060040360031b1b16165f8061071f565b60048a019935610c9760f882901c8e614377565b5190611fe08160eb1c169082516020830111610d215761ffff9060e01c169184516020840111610cf957916024602080937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff96950101918601015e018061066f565b60048e7f7ea37aee000000000000000000000000000000000000000000000000000000008152fd5b60048e7fc87025ed000000000000000000000000000000000000000000000000000000008152fd5b806060602080938d010152016104cf565b838060405160208101907f772cb3e6000000000000000000000000000000000000000000000000000000008252610d9a816105ce33898960248501614202565b51908273ffffffffffffffffffffffffffffffffffffffff88165af1610dbe614266565b9015610dca5750610479565b61064a906040519182917f072a446d0000000000000000000000000000000000000000000000000000000083526020600484015260248301906131bc565b6004837ff5185ed1000000000000000000000000000000000000000000000000000000008152fd5b6004827f3ee5aeb5000000000000000000000000000000000000000000000000000000008152fd5b5080fd5b905034610e585760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610e585773ffffffffffffffffffffffffffffffffffffffff90610edf610eda7fffffffff00000000000000000000000000000000000000000000000000000000610ed26131ff565b931633613e1c565b6133e7565b168015610f3757807fffffffffffffffffffffffff0000000000000000000000000000000000000000600d541617600d557f7a7b5a0a132f9e0581eb8527f66eae9ee89c2a3e79d4ac7e41a1f1f4d48a7fc28280a280f35b6004827f8d107ec5000000000000000000000000000000000000000000000000000000008152fd5b823461027757807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261027757602073ffffffffffffffffffffffffffffffffffffffff60075416604051908152f35b82346102775760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102775773ffffffffffffffffffffffffffffffffffffffff60406110006131ff565b928261100a613222565b9416815260016020522091165f52602052602060405f2054604051908152f35b823461027757807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102775760405180602060095491828152018091600985527f6e1540171b6c0c960b71a7020d9f60077f6af931a8bbf590da0223dacf75c7af90855b8181106110fe57505050826110a79103836133a6565b604051928392602084019060208552518091526040840192915b8181106110cf575050500390f35b825173ffffffffffffffffffffffffffffffffffffffff168452859450602093840193909201916001016110c1565b8254845260209093019260019283019201611091565b823461027757807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102775773ffffffffffffffffffffffffffffffffffffffff600d541633036113a65773ffffffffffffffffffffffffffffffffffffffff600c5416817f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48916040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015260208160248173ffffffffffffffffffffffffffffffffffffffff88165afa90811561136357839161136e575b5090602460609260405194859384927fac68a74800000000000000000000000000000000000000000000000000000000845260048401525af1801561136357839284928592611313575b5083156112eb576040945061125784338361416a565b84518481527f9493e5bbe4e8e0ac67284469a2d677403d0378a85a59e341d3abc433d0d9a20960203392a28280611298575b50505082519182526020820152f35b826112a29261416a565b7f0f58cb6262d88971b87878468debb7bc81f1714be597710e16a9fb84c66ff7b5602073ffffffffffffffffffffffffffffffffffffffff8651938585521692a2838082611289565b6004857f7c29a484000000000000000000000000000000000000000000000000000000008152fd5b93509150506060823d60601161135b575b81611331606093836133a6565b810103126113575781519061134d60406020850151940161344c565b9192919085611241565b8280fd5b3d9150611324565b6040513d85823e3d90fd5b919250506020813d60201161139e575b8161138b602093836133a6565b8101031261002b575183919060246111f7565b3d915061137e565b807f286713000000000000000000000000000000000000000000000000000000000060049252fd5b823461027757807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261027757602073ffffffffffffffffffffffffffffffffffffffff60065416604051908152f35b82346102775761142f36613273565b61143a9391936141ce565b73ffffffffffffffffffffffffffffffffffffffff600f5416330361174e5773ffffffffffffffffffffffffffffffffffffffff90836116f4575b169283156116c8578573ffffffffffffffffffffffffffffffffffffffff600e541680611638575b50600190611599575b15611571576002548181018091116115445773ffffffffffffffffffffffffffffffffffffffff7f59009eaf55f74c19a447f53174708d6c3b16e27d0dd94f6a2a8845f6728b1614936040938293600255878a5289602052848a20818154019055878a7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60208851858152a38451968752602087015216951692a480f35b6024877f4e487b710000000000000000000000000000000000000000000000000000000081526011600452fd5b6004867f60134b78000000000000000000000000000000000000000000000000000000008152fd5b506024602073ffffffffffffffffffffffffffffffffffffffff600f5416604051928380927f9428b68e0000000000000000000000000000000000000000000000000000000082528b60048301525afa90811561162d5787916115fe575b50156114a6565b611620915060203d602011611626575b61161881836133a6565b81019061346d565b876115f7565b503d61160e565b6040513d89823e3d90fd5b73ffffffffffffffffffffffffffffffffffffffff600f5416813b156113575782906064604051809481937fabd626b00000000000000000000000000000000000000000000000000000000083528560048401528b602484015260448301525afa80156116bd571561149d57816116ae916133a6565b6116b957858761149d565b8580fd5b6040513d84823e3d90fd5b6024867fec442f0500000000000000000000000000000000000000000000000000000000815280600452fd5b6117496040517f23b872dd0000000000000000000000000000000000000000000000000000000060208201528388166024820152306044820152856064820152606481526117436084826133a6565b8461456e565b611475565b6004867f18b2ef41000000000000000000000000000000000000000000000000000000008152fd5b823461027757807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261027757602073ffffffffffffffffffffffffffffffffffffffff600c5416604051908152f35b82346102775760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102775761180d6118036131ff565b6024359033613f29565b602060405160018152f35b823461027757807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261027757602073ffffffffffffffffffffffffffffffffffffffff600e5416604051908152f35b905034610e585760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610e58576004359073ffffffffffffffffffffffffffffffffffffffff821680920361135757610eda7fffffffff000000000000000000000000000000000000000000000000000000006118ec921633613e1c565b807fffffffffffffffffffffffff000000000000000000000000000000000000000060085416176008557f783744567638c41aa4016f43e64e6bc0e58913f22df8da2ee0b6e8f52e4a06588280a280f35b82346102775760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102775760209061199673ffffffffffffffffffffffffffffffffffffffff6119906131ff565b1661460e565b9190156119a757505b604051908152f35b905061199f565b823461027757807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610277576040519080600454908160011c91600181168015611b0a575b602084108114611add57838652908115611a985750600114611a3b575b611a3784611a23818603826133a6565b6040519182916020835260208301906131bc565b0390f35b600481527f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b939250905b808210611a7e57509091508101602001611a2382611a13565b919260018160209254838588010152019101909291611a65565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660208087019190915292151560051b85019092019250611a239150839050611a13565b6024837f4e487b710000000000000000000000000000000000000000000000000000000081526022600452fd5b92607f16926119f6565b905034610e585760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610e585773ffffffffffffffffffffffffffffffffffffffff611b626131ff565b611b94610eda7fffffffff00000000000000000000000000000000000000000000000000000000602435951633613e1c565b168015611cd9576040517f3af32abf00000000000000000000000000000000000000000000000000000000815281600482015260208160248173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000ddfd960a7150520548dd1f6e53cc2f201b364692165afa908115611cce578491611caf575b5015611c87578115611c5f57808352600b602052816040842055611c3781614e53565b507f4b06eaf2f723ef457d7e5a4bdd29d59455b133f37737541f209bbe963aba77fe8380a380f35b6004837fc8ddd4ba000000000000000000000000000000000000000000000000000000008152fd5b6004837feb1c650e000000000000000000000000000000000000000000000000000000008152fd5b611cc8915060203d6020116116265761161881836133a6565b5f611c14565b6040513d86823e3d90fd5b6004837fa9debd38000000000000000000000000000000000000000000000000000000008152fd5b823461027757807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261027757602073ffffffffffffffffffffffffffffffffffffffff60055460081c16604051908152f35b915034611e875760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112611e87576004359073ffffffffffffffffffffffffffffffffffffffff8216809203611e8557610eda7fffffffff00000000000000000000000000000000000000000000000000000000611dd8921633613e1c565b807fffffffffffffffffffffffff0000000000000000000000000000000000000000600c541617600c5560405190807f3e762c7e655633ce63121393b9694f9ca1883d14d18f48f1be55e5dc7a9fb6c18480a280611e34575050f35b803b15611e8557816004818580947f176c437e0000000000000000000000000000000000000000000000000000000083525af180156116bd57611e745750f35b81611e7e916133a6565b6102775780f35b505b50fd5b905034610e585760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610e58576004359073ffffffffffffffffffffffffffffffffffffffff821680920361135757610eda7fffffffff00000000000000000000000000000000000000000000000000000000611f0c921633613e1c565b807fffffffffffffffffffffffff0000000000000000000000000000000000000000600e541617600e557f6b354c5f163626c6c1f2a0a7b551ee3cd06cbe5fd35e8fe74388b9c28f05d7688280a280f35b905034610e5857817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610e58577fffffffff00000000000000000000000000000000000000000000000000000000611fb9911633613e1c565b801561204d575b1561202557611fcd6141ce565b60017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0060055416176005557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586020604051338152a180f35b807fdb78883e0000000000000000000000000000000000000000000000000000000060049252fd5b50338152600a60205260408120541515611fc0565b823461027757807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261027757602073ffffffffffffffffffffffffffffffffffffffff600f5416604051908152f35b823461002b576120c336613273565b936120d0939192936141ce565b73ffffffffffffffffffffffffffffffffffffffff600f541633036122cf5773ffffffffffffffffffffffffffffffffffffffff169182156122a35773ffffffffffffffffffffffffffffffffffffffff600e541680612217575b508286528560205260408620548181106121e35773ffffffffffffffffffffffffffffffffffffffff7ffb39f3e6cf68759732e3db2247713a4eca21e27eab5d056a07f276c35867f73f93836040948394888c528b60205203858b2055806002540360025589877fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60208851858152a3876121d3575b8451978852602088015216951693a480f35b6121de888a8461416a565b6121c1565b8692506064937fe450d38c000000000000000000000000000000000000000000000000000000008452600452602452604452fd5b73ffffffffffffffffffffffffffffffffffffffff600f5416813b1561002b575f906064604051809481937fabd626b000000000000000000000000000000000000000000000000000000000835289600484015285602484015260448301525afa8015612298571561212b576122909196505f906133a6565b5f948661212b565b6040513d5f823e3d90fd5b7f96c6fd1e000000000000000000000000000000000000000000000000000000005f525f60045260245ffd5b7f18b2ef41000000000000000000000000000000000000000000000000000000005f5260045ffd5b3461002b5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261002b576004359073ffffffffffffffffffffffffffffffffffffffff821680920361002b5773ffffffffffffffffffffffffffffffffffffffff60055460081c1633149081156123c5575b501561002b57807fffffffffffffffffffffffff00000000000000000000000000000000000000006006541617600655337fa3396fd7f6e0a21b50e5089d2da70d5ac0a3bbbd1f617a93f134b763899801985f80a3005b6006546040517fb70096130000000000000000000000000000000000000000000000000000000081523360048201523060248201527fffffffff000000000000000000000000000000000000000000000000000000009290921660448301529091506020908290606490829073ffffffffffffffffffffffffffffffffffffffff165afa908115612298575f9161245e575b508261236e565b612477915060203d6020116116265761161881836133a6565b82612457565b3461002b575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261002b5760075473ffffffffffffffffffffffffffffffffffffffff81169081330361255f577fffffffffffffffffffffffff0000000000000000000000000000000000000000906005547fffffffffffffffffffffff0000000000000000000000000000000000000000ff74ffffffffffffffffffffffffffffffffffffffff008360081b1691161760055516600755337f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3005b7f4bf4c244000000000000000000000000000000000000000000000000000000005f5260045ffd5b3461002b575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261002b57602060405173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48168152f35b3461002b5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261002b5773ffffffffffffffffffffffffffffffffffffffff5f9161266a610eda7fffffffff00000000000000000000000000000000000000000000000000000000610ed26131ff565b16612674816145f5565b507f4b06eaf2f723ef457d7e5a4bdd29d59455b133f37737541f209bbe963aba77fe8280a3005b3461002b5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261002b5773ffffffffffffffffffffffffffffffffffffffff6126e76131ff565b165f525f602052602060405f2054604051908152f35b3461002b575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261002b57602060ff600554166040519015158152f35b3461002b5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261002b576127746131ff565b5f73ffffffffffffffffffffffffffffffffffffffff604051927f3af32abf0000000000000000000000000000000000000000000000000000000084521680600484015260208360248173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000ddfd960a7150520548dd1f6e53cc2f201b364692165afa8015612298576020935f91612853575b5015612819575b506040519015158152f35b9050612824816145f5565b505f6001917f4b06eaf2f723ef457d7e5a4bdd29d59455b133f37737541f209bbe963aba77fe8280a38261280e565b61286a9150843d86116116265761161881836133a6565b84612807565b3461002b575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261002b5773ffffffffffffffffffffffffffffffffffffffff600c54167f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48906040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015260208160248173ffffffffffffffffffffffffffffffffffffffff87165afa908115612298575f91612a6e575b505f916024604092835194859384927f01c621c100000000000000000000000000000000000000000000000000000000845260048401525af18015612298575f915f91612a2e575b5073ffffffffffffffffffffffffffffffffffffffff811690813303612a065782156129de57826129ad9160209561416a565b7f0f58cb6262d88971b87878468debb7bc81f1714be597710e16a9fb84c66ff7b583604051848152a2604051908152f35b7f7c29a484000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f3b4f2663000000000000000000000000000000000000000000000000000000005f5260045ffd5b9150506040813d604011612a66575b81612a4a604093836133a6565b8101031261002b57612a6060208251920161344c565b8361297a565b3d9150612a3d565b90506020813d602011612a98575b81612a89602093836133a6565b8101031261002b57515f612932565b3d9150612a7c565b3461002b575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261002b57602073ffffffffffffffffffffffffffffffffffffffff600d5416604051908152f35b3461002b575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261002b57610eda7fffffffff00000000000000000000000000000000000000000000000000000000612b4e921633613e1c565b60055460ff811615612bab577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166005557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa6020604051338152a1005b7f8dfc202b000000000000000000000000000000000000000000000000000000005f5260045ffd5b3461002b575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261002b57602060405160128152f35b3461002b5760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261002b57612c436131ff565b612c4b613222565b6044359073ffffffffffffffffffffffffffffffffffffffff831692835f52600160205260405f2073ffffffffffffffffffffffffffffffffffffffff33165f5260205260405f20547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8103612cc7575b5061180d9350613f29565b838110612d6c578415612d40573315612d145761180d945f52600160205260405f2073ffffffffffffffffffffffffffffffffffffffff33165f526020528360405f209103905584612cbc565b7f94280d62000000000000000000000000000000000000000000000000000000005f525f60045260245ffd5b7fe602df05000000000000000000000000000000000000000000000000000000005f525f60045260245ffd5b83907ffb8f41b2000000000000000000000000000000000000000000000000000000005f523360045260245260445260645ffd5b3461002b575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261002b57602060405173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000ddfd960a7150520548dd1f6e53cc2f201b364692168152f35b3461002b575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261002b576020600254604051908152f35b3461002b5760807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261002b57612e806131ff565b50612e89613222565b5060643567ffffffffffffffff811161002b57612eaa903690600401613245565b505060206040517f150b7a02000000000000000000000000000000000000000000000000000000008152f35b3461002b5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261002b5773ffffffffffffffffffffffffffffffffffffffff90612f4a610eda7fffffffff00000000000000000000000000000000000000000000000000000000610ed26131ff565b168015612fa157807fffffffffffffffffffffffff0000000000000000000000000000000000000000600f541617600f557f606753945c222bd6eb6533c506303e56af1df290ae8e1557219f83770cfa35155f80a2005b7f17c1ab6a000000000000000000000000000000000000000000000000000000005f5260045ffd5b3461002b5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261002b576130006131ff565b602435903315612d405773ffffffffffffffffffffffffffffffffffffffff16908115612d1457335f52600160205260405f20825f526020528060405f20556040519081527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560203392a3602060405160018152f35b3461002b575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261002b575f6003548060011c906001811680156131b2575b6020831081146131855782855290811561314357506001146130e5575b611a3783611a23818503826133a6565b91905060035f527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b915f905b80821061312957509091508101602001611a236130d5565b919260018160209254838588010152019101909291613111565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660208086019190915291151560051b84019091019150611a2390506130d5565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b91607f16916130b8565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f602080948051918291828752018686015e5f8582860101520116010190565b6004359073ffffffffffffffffffffffffffffffffffffffff8216820361002b57565b6024359073ffffffffffffffffffffffffffffffffffffffff8216820361002b57565b9181601f8401121561002b5782359167ffffffffffffffff831161002b576020838186019501011161002b57565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60a091011261002b5760043573ffffffffffffffffffffffffffffffffffffffff8116810361002b579060243573ffffffffffffffffffffffffffffffffffffffff8116810361002b5790604435906064359060843573ffffffffffffffffffffffffffffffffffffffff8116810361002b5790565b6003111561331457565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b6040810190811067ffffffffffffffff82111761335d57604052565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b60c0810190811067ffffffffffffffff82111761335d57604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761335d57604052565b156133ee57565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a454400000000000000000000000000000000000000006044820152fd5b519073ffffffffffffffffffffffffffffffffffffffff8216820361002b57565b9081602091031261002b5751801515810361002b5790565b67ffffffffffffffff811161335d57601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b7fa48fd101fc9f41f09dc754b3b14722487070ffbd61259b49558564a3296a3f005c5f7fa48fd101fc9f41f09dc754b3b14722487070ffbd61259b49558564a3296a3f005d7fffffffff000000000000000000000000000000000000000000000000000000008160301b167fffffffff000000000000000000000000000000000000000000000000000000005f351603613df45773ffffffffffffffffffffffffffffffffffffffff81163303613df4577f30fb041442610fd0a22e4654f60ea1c715088ef7320b5ec0c4e75cbdd99dbe005c5f7f30fb041442610fd0a22e4654f60ea1c715088ef7320b5ec0c4e75cbdd99dbe005d8015613df45761ffff808360a01c1614613dde575f9060a083901c61ffff166001810191906135e69035841a614295565b926135f460405194856133a6565b60a085901c61ffff16355f1a8085527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09061362e90614295565b015f5b818110613dcd57505061364d61ffff8660a01c16355f1a6142c5565b9160405161365a8161338a565b5f81525f60208201525f60408201525f60608201525f60808201525f60a08201525f5b61ffff8860a01c16355f1a8110613791575050509061369c9190614a0e565b6060916001820191355f1a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff810161371557505061371291506136de9061463f565b925b60a01c61ffff167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0810135019061453f565b90565b600214613728575b5061371291926136e0565b80925051801561376957613712927fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff613762920190614377565b519161371d565b7f26779f3c000000000000000000000000000000000000000000000000000000005f5260045ffd5b6137a66014876001983560601c85520161463f565b9681019690355f1a805b613d0a57506001870196355f1a156138125790806137f55f8060019573ffffffffffffffffffffffffffffffffffffffff885116602082519201905afa6106b6614266565b6137ff828a614377565b5261380a8189614377565b505b0161367d565b818194999a9296939598978a7fffffffff00000000000000000000000000000000000000000000000000000000845160208601518281169160048110613cf5575b5050168060208a01527f095ea7b3000000000000000000000000000000000000000000000000000000008114908115613ccb575b50613c63575b61389691614660565b79ffffffffffffffffffffffffffffffffffffffffffffffffffff1660408801526138c192906146cb565b73ffffffffffffffffffffffffffffffffffffffff16608088015260a08701529099906138f08b355f1a614295565b9a6040519b6138ff908d6133a6565b80355f1a808d5261390f90614295565b9060208d01917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe00136833780355f1a60051b80926001830190370186600182015f926002810191355f1a151593613a67926087927fffffffffffffffffffffffffffffffffffffffff00000000000000000000000096613c55575b50602090826060870152967fffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000008651937fffffffff00000000000000000000000000000000000000000000000000000000848901511697896080820151604060a0840151930151936040519b8c99898b019e8f9160601b16905260348a0152151560f81b603889015260601b166039870152604d86015260301b16606d8401528051918291018484015e81015f8382015203017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081018352826133a6565b519020935f945b8c51861015613ab057613a81868e614377565b519081811015613a9f575f52602052600160405f205b950194613a6e565b905f52602052600160405f20613a97565b9097939b508a95929a91989996945003613c2d578190613b035f8073ffffffffffffffffffffffffffffffffffffffff87511660608801519085519160208701915af1613afb614266565b9384916143a2565b79ffffffffffffffffffffffffffffffffffffffffffffffffffff604085015116613bde575b608084015160028116613b58575b505090600191613b47828a614377565b52613b528189614377565b5061380c565b5f91829160027fb8706f504833578f7e830b12e31c3cfba31669a85b02596177f00c6a7faf6e005d8273ffffffffffffffffffffffffffffffffffffffff60208451940192165af1613ba8614266565b9015610aeb5750906001915f7fb8706f504833578f7e830b12e31c3cfba31669a85b02596177f00c6a7faf6e005d90915f613b37565b7fa48fd101fc9f41f09dc754b3b14722487070ffbd61259b49558564a3296a3f005c15613b29577f831579e8000000000000000000000000000000000000000000000000000000005f5260045ffd5b7fdb87823d000000000000000000000000000000000000000000000000000000005f5260045ffd5b90359150602201602061398a565b875173ffffffffffffffffffffffffffffffffffffffff169a60248501516040519c613c8e8e613341565b8d5273ffffffffffffffffffffffffffffffffffffffff1660208d0152806001019b613cba8289614377565b52613cc59087614377565b5061388d565b7f39509351000000000000000000000000000000000000000000000000000000009150145f613887565b839250829060040360031b1b16165f80613853565b9660048135910197613d1f8260f81c8b614377565b51611fe08360eb1c169281516020850111613da55761ffff9060e01c1684516020820111613d7d57602460207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9581940101918601015e01806137b0565b7f7ea37aee000000000000000000000000000000000000000000000000000000005f5260045ffd5b7fc87025ed000000000000000000000000000000000000000000000000000000005f5260045ffd5b806060602080938901015201613631565b5050604051613dee6020826133a6565b5f815290565b7fc6248746000000000000000000000000000000000000000000000000000000005f5260045ffd5b73ffffffffffffffffffffffffffffffffffffffff60065416918215159283613e71575b50508115613e4c575090565b905073ffffffffffffffffffffffffffffffffffffffff8060055460081c1691161490565b6040517fb700961300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff841660048201523060248201527fffffffff0000000000000000000000000000000000000000000000000000000092909216604483015291925090602090829060649082905afa908115612298575f91613f0a575b50905f80613e40565b613f23915060203d6020116116265761161881836133a6565b5f613f01565b9173ffffffffffffffffffffffffffffffffffffffff5f93169182156122a35773ffffffffffffffffffffffffffffffffffffffff1692831561413e5773ffffffffffffffffffffffffffffffffffffffff600e5416806140bf575b506024602073ffffffffffffffffffffffffffffffffffffffff600f5416604051928380927f9428b68e0000000000000000000000000000000000000000000000000000000082528860048301525afa9081156116bd5782916140a0575b50614078578281528060205260408120548281106140455791604082827fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef958760209652828652038282205586815280845220818154019055604051908152a3565b6064937fe450d38c0000000000000000000000000000000000000000000000000000000083949352600452602452604452fd5b807f60134b780000000000000000000000000000000000000000000000000000000060049252fd5b6140b9915060203d6020116116265761161881836133a6565b5f613fe3565b73ffffffffffffffffffffffffffffffffffffffff600f5416813b1561002b575f906064604051809481937fabd626b00000000000000000000000000000000000000000000000000000000083528960048401528a602484015260448301525afa80156122985715613f855761413791505f906133a6565b5f5f613f85565b7fec442f05000000000000000000000000000000000000000000000000000000005f525f60045260245ffd5b6141cc9273ffffffffffffffffffffffffffffffffffffffff604051937fa9059cbb0000000000000000000000000000000000000000000000000000000060208601521660248401526044830152604482526141c76064836133a6565b61456e565b565b60ff600554166141da57565b7fd93c0665000000000000000000000000000000000000000000000000000000005f5260045ffd5b9160607fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8373ffffffffffffffffffffffffffffffffffffffff94602096999899604089528160408a0152858901375f84828901015201168401019416910152565b3d15614290573d9061427782613485565b9161428560405193846133a6565b82523d5f602084013e565b606090565b67ffffffffffffffff811161335d5760051b60200190565b604051906142ba82613341565b5f6020838281520152565b906142cf82614295565b6142dc60405191826133a6565b8281527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe061430a8294614295565b01905f5b82811061431a57505050565b60209060405161432981613341565b5f81525f838201528282850101520161430e565b80511561434a5760200190565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b805182101561434a5760209160051b010190565b6040906137129392815281602082015201906131bc565b156143ab575050565b61064a6040519283927f248ef89c0000000000000000000000000000000000000000000000000000000084526004840161438b565b90806143ea6142ad565b505b6143f4575050565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff016144208183614377565b5173ffffffffffffffffffffffffffffffffffffffff815116906020810190602073ffffffffffffffffffffffffffffffffffffffff8351166044604051809681937fdd62ed3e00000000000000000000000000000000000000000000000000000000835230600484015260248301525afa928315612298575f936144f8575b5073ffffffffffffffffffffffffffffffffffffffff80915116915116916144ca575050806143ec565b7f715879af000000000000000000000000000000000000000000000000000000005f5260045260245260445ffd5b9092506020813d8211614537575b81614513602093836133a6565b8101031261002b57519173ffffffffffffffffffffffffffffffffffffffff6144a0565b3d9150614506565b0361454657565b7f01842f8c000000000000000000000000000000000000000000000000000000005f5260045ffd5b905f602091828151910182855af115612298575f513d6145ec575073ffffffffffffffffffffffffffffffffffffffff81163b155b6145aa5750565b73ffffffffffffffffffffffffffffffffffffffff907f5274afe7000000000000000000000000000000000000000000000000000000005f521660045260245ffd5b600114156145a3565b61371290805f52600b6020525f60408120556009614c98565b805f52600b60205260405f205480155f1461463757505f52600a60205260405f20541515905f90565b600192909150565b906002823560f01c61465081614b35565b9381838201602087013701019190565b9190916001810181355f1a156146c35790601b913560301c93847fa48fd101fc9f41f09dc754b3b14722487070ffbd61259b49558564a3296a3f005d7f30fb041442610fd0a22e4654f60ea1c715088ef7320b5ec0c4e75cbdd99dbe005d019190565b92505f919050565b909291906001810181355f1a80156148c5576080607f82169116156148a9578061485457503560601c916060946001841661470d575b505060150192915f9190565b5f91929550819060017fb8706f504833578f7e830b12e31c3cfba31669a85b02596177f00c6a7faf6e005d60208151910182865af19061474b614266565b911561481f5750601f8151166147f7575f7fb8706f504833578f7e830b12e31c3cfba31669a85b02596177f00c6a7faf6e005d805181019060208181840193031261002b5760208101519067ffffffffffffffff821161002b57019080603f8301121561002b5760208201516147c081613485565b916147ce60405193846133a6565b8183526040848301011161002b576020815f926040601596018386015e8301015293905f614701565b7fbe72399a000000000000000000000000000000000000000000000000000000005f5260045ffd5b61064a6040519283927f883d36b40000000000000000000000000000000000000000000000000000000084526004840161438b565b93919492503592602185013560601c9260018416614881576035918561487992614b84565b940193929190565b7ffafeed10000000000000000000000000000000000000000000000000000000005f5260045ffd5b602192959493506148bc91358095614b84565b93019291905f90565b509293505050906040516148da6020826133a6565b5f8152905f905f90565b7fba2cfcc1b17a97110b1fb218b61c42c0e510c6913e669a69d4ade619ace66c005c908160a01c8015614a07575f7fba2cfcc1b17a97110b1fb218b61c42c0e510c6913e669a69d4ade619ace66c005d61493d816142c5565b927fba2cfcc1b17a97110b1fb218b61c42c0e510c6913e669a69d4ade619ace66c019073ffffffffffffffffffffffffffffffffffffffff825c816040519361498585613341565b1683521660208201526149978561433d565b526149a18461433d565b5060015b8281106149b157505050565b60019060028284015c93019273ffffffffffffffffffffffffffffffffffffffff845c81604051936149e285613341565b1683521660208201526149f58288614377565b52614a008187614377565b50016149a5565b5060609150565b8115614b31577fba2cfcc1b17a97110b1fb218b61c42c0e510c6913e669a69d4ade619ace66c00905f825c60a081901c614a466142ad565b5080614ad25750505073ffffffffffffffffffffffffffffffffffffffff60206001614a718461433d565b5194838651168760a01b17815d0193015116825d60015b838110614a955750505050565b8073ffffffffffffffffffffffffffffffffffffffff60206002614abb60019587614377565b5196838851168682015d0195015116845d01614a88565b9085947fba2cfcc1b17a97110b1fb218b61c42c0e510c6913e669a69d4ade619ace66bff93966c01fffffffffffffffffffffffe930160a01b73ffffffffffffffffffffffffffffffffffffffff831617905d609f1c16019192614a88565b5050565b90614b3f82613485565b614b4c60405191826133a6565b8281527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0614b7a8294613485565b0190602036910137565b90916011811015614c5b57614b9b8160051b614b35565b92825160248110614c33577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015f915f925b848410614bdd5750505050505090565b8160f01c836004820111614c0b5760019160246020928901015182828b010152019160101b93019290614bcd565b7f66a468ef000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f6809cb33000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f9bbf46dc000000000000000000000000000000000000000000000000000000005f5260045ffd5b805482101561434a575f5260205f2001905f90565b906001820191815f528260205260405f20548015155f14614e4b577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8101818111614e1e578254907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8201918211614e1e57818103614db4575b50505080548015614d87577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190614d4a8282614c83565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82549160031b1b19169055555f526020525f6040812055600190565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603160045260245ffd5b614e09614dc4614dd49386614c83565b90549060031b1c92839286614c83565b81939154907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9060031b92831b921b19161790565b90555f528360205260405f20555f8080614d12565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b505050505f90565b805f52600a60205260405f2054155f14614ea8576009546801000000000000000081101561335d57614e91614dd48260018594016009556009614c83565b9055600954905f52600a60205260405f2055600190565b505f9056fea164736f6c634300081d000a
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.22
Net Worth in ETH
0.000108
Token Allocations
USDC
100.00%
Multichain Portfolio | 34 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|---|---|---|---|---|
| ETH | 100.00% | $0.999965 | 0.2224 | $0.2223 |
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ 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.