Source Code
Overview
ETH Balance
0 ETH
Eth Value
$0.00Latest 1 from a total of 1 transactions
| Transaction Hash |
Method
|
Block
|
From
|
|
To
|
||||
|---|---|---|---|---|---|---|---|---|---|
| Request Deposit | 24542520 | 8 days ago | IN | 0 ETH | 0.00003255 |
Latest 1 internal transaction
Advanced mode:
| Parent Transaction Hash | Method | Block |
From
|
|
To
|
||
|---|---|---|---|---|---|---|---|
| 0x60c03461 | 24191586 | 57 days ago | Contract Creation | 0 ETH |
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
Similar Match Source Code This contract matches the deployed Bytecode of the Source Code for Contract 0x01587FC1...558a4dd01 The constructor portion of the code might be different and could alter the actual behaviour of the contract
Contract Name:
Provisioner
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/token/ERC20/IERC20.sol";
import { SafeERC20 } from "@oz/token/ERC20/utils/SafeERC20.sol";
import { ReentrancyGuardTransient } from "@oz/utils/ReentrancyGuardTransient.sol";
import { Math } from "@oz/utils/math/Math.sol";
import { BitMaps } from "@oz/utils/structs/BitMaps.sol";
import { Authority } from "@solmate/auth/Auth.sol";
import { Auth2Step } from "src/core/Auth2Step.sol";
import {
AUTO_PRICE_FIXED_PRICE_FLAG,
DEPOSIT_REDEEM_FLAG,
MAX_DEPOSIT_REFUND_TIMEOUT,
MAX_SECONDS_TO_DEADLINE,
MIN_DEPOSIT_MULTIPLIER,
MIN_REDEEM_MULTIPLIER,
ONE_IN_BPS,
ONE_UNIT
} from "src/core/Constants.sol";
import { Request, RequestType, TokenDetails } from "src/core/Types.sol";
import { IMultiDepositorVault } from "src/core/interfaces/IMultiDepositorVault.sol";
import { IPriceAndFeeCalculator } from "src/core/interfaces/IPriceAndFeeCalculator.sol";
import { IProvisioner } from "src/core/interfaces/IProvisioner.sol";
/// @title Provisioner
/// @notice Entry and exit point for {MultiDepositorVault}. Handles all deposits and redemptions
/// Uses {IPriceAndFeeCalculator} to convert between tokens and vault units. Supports both sync and async deposits; only
/// async redeems. Manages deposit caps, refund timeouts, and request replay protection. All assets must flow through
/// this contract to enter or exit the vault. Sync deposits are processed instantly, but stay refundable for a period of
/// time. Async requests can either be solved by authorized solvers, going through the vault, or directly by anyone
/// willing to pay units (for deposits) or tokens (for redeems), pocketing the solver tip, always paid in tokens
contract Provisioner is IProvisioner, Auth2Step, ReentrancyGuardTransient {
using SafeERC20 for IERC20;
using BitMaps for BitMaps.BitMap;
////////////////////////////////////////////////////////////
// Immutables //
////////////////////////////////////////////////////////////
/// @notice The price and fee calculator contract
IPriceAndFeeCalculator public immutable PRICE_FEE_CALCULATOR;
/// @notice The multi depositor vault contract
address public immutable MULTI_DEPOSITOR_VAULT;
////////////////////////////////////////////////////////////
// Storage //
////////////////////////////////////////////////////////////
/// @notice Mapping of token to token details
mapping(IERC20 token => TokenDetails details) public tokensDetails;
/// @notice Maximum total value of deposits in numeraire terms
uint256 public depositCap;
/// @notice Time period in seconds during which sync deposits can be refunded
uint256 public depositRefundTimeout;
/// @notice Mapping of active sync deposit hashes
/// @dev True if a sync deposit is active with the hashed parameters
mapping(bytes32 syncDepositHash => bool exists) public syncDepositHashes;
/// @notice Mapping of async deposit hash to its existence
/// @dev True if deposit request exists, false if it was refunded or solved
mapping(bytes32 asyncDepositHash => bool exists) public asyncDepositHashes;
/// @notice Mapping of async redeem hash to its existence
/// @dev True if redeem request exists, false if it was refunded or solved
mapping(bytes32 asyncRedeemHash => bool exists) public asyncRedeemHashes;
/// @notice Mapping of user address to timestamp until which their units are locked
mapping(address user => uint256 unitsLockedUntil) public userUnitsRefundableUntil;
////////////////////////////////////////////////////////////
// Modifiers //
////////////////////////////////////////////////////////////
/// @notice Ensures the caller is not the vault
modifier anyoneButVault() {
// Requirements: check that the caller is not the vault
require(msg.sender != MULTI_DEPOSITOR_VAULT, Aera__CallerIsVault());
_;
}
constructor(
IPriceAndFeeCalculator priceAndFeeCalculator,
address multiDepositorVault,
address owner_,
Authority authority_
) Auth2Step(owner_, authority_) {
// Requirements: immutables are not zero addresses
require(address(priceAndFeeCalculator) != address(0), Aera__ZeroAddressPriceAndFeeCalculator());
require(multiDepositorVault != address(0), Aera__ZeroAddressMultiDepositorVault());
// Effects: set immutables
PRICE_FEE_CALCULATOR = priceAndFeeCalculator;
MULTI_DEPOSITOR_VAULT = multiDepositorVault;
}
////////////////////////////////////////////////////////////
// Public / External Functions //
////////////////////////////////////////////////////////////
/// @inheritdoc IProvisioner
function deposit(IERC20 token, uint256 tokensIn, uint256 minUnitsOut)
external
anyoneButVault
returns (uint256 unitsOut)
{
// Requirements: token amount and min units out are positive
require(tokensIn != 0, Aera__TokensInZero());
require(minUnitsOut != 0, Aera__MinUnitsOutZero());
// Requirements: sync deposits are enabled
TokenDetails storage tokenDetails = _requireSyncDepositsEnabled(token);
// Interactions: convert token amount to units out
unitsOut = _tokensToUnitsFloorIfActive(token, tokensIn, tokenDetails.depositMultiplier);
// Requirements: units out meets min units out
require(unitsOut >= minUnitsOut, Aera__MinUnitsOutNotMet());
// Requirements + interactions: convert new total units to numeraire and check against deposit cap
_requireDepositCapNotExceeded(unitsOut);
// Effects + interactions: sync deposit
_syncDeposit(token, tokensIn, unitsOut);
}
/// @inheritdoc IProvisioner
function mint(IERC20 token, uint256 unitsOut, uint256 maxTokensIn)
external
anyoneButVault
returns (uint256 tokensIn)
{
// Requirements: tokens and units amount are positive
require(unitsOut != 0, Aera__UnitsOutZero());
require(maxTokensIn != 0, Aera__MaxTokensInZero());
// Requirements: sync deposits are enabled
TokenDetails storage tokenDetails = _requireSyncDepositsEnabled(token);
// Requirements + interactions: convert new total units to numeraire and check against deposit cap
_requireDepositCapNotExceeded(unitsOut);
// Interactions: convert units to tokens
tokensIn = _unitsToTokensCeilIfActive(token, unitsOut, tokenDetails.depositMultiplier);
// Requirements: token in is less than or equal to max tokens in
require(tokensIn <= maxTokensIn, Aera__MaxTokensInExceeded());
// Effects + interactions: sync deposit
_syncDeposit(token, tokensIn, unitsOut);
}
/// @inheritdoc IProvisioner
function refundDeposit(
address sender,
IERC20 token,
uint256 tokenAmount,
uint256 unitsAmount,
uint256 refundableUntil
) external requiresAuth {
// Requirements: refundable timestamp is in the future
require(refundableUntil >= block.timestamp, Aera__RefundPeriodExpired());
bytes32 depositHash = _getDepositHash(sender, token, tokenAmount, unitsAmount, refundableUntil);
// Requirements: hash has been set
require(syncDepositHashes[depositHash], Aera__DepositHashNotFound());
// Effects: unset hash as used
syncDepositHashes[depositHash] = false;
// Interactions: exit vault
IMultiDepositorVault(MULTI_DEPOSITOR_VAULT).exit(sender, token, tokenAmount, unitsAmount, sender);
// Log emit deposit refunded event
emit DirectDepositRefunded(depositHash);
}
/// @inheritdoc IProvisioner
function requestDeposit(
IERC20 token,
uint256 tokensIn,
uint256 minUnitsOut,
uint256 solverTip,
uint256 deadline,
uint256 maxPriceAge,
bool isFixedPrice
) external anyoneButVault {
// Requirements: token amount and min units out are positive, deadline is in the future, deadline is not too far
// in the future, async deposits are enabled, vault is not paused in the PriceAndFeeCalculator
require(tokensIn != 0, Aera__TokensInZero());
require(minUnitsOut != 0, Aera__MinUnitsOutZero());
require(deadline > block.timestamp, Aera__DeadlineInPast());
unchecked {
require(deadline - block.timestamp <= MAX_SECONDS_TO_DEADLINE, Aera__DeadlineTooFarInFuture());
}
require(tokensDetails[token].asyncDepositEnabled, Aera__AsyncDepositDisabled());
require(!PRICE_FEE_CALCULATOR.isVaultPaused(MULTI_DEPOSITOR_VAULT), Aera__PriceAndFeeCalculatorVaultPaused());
require(solverTip == 0 || !isFixedPrice, Aera__FixedPriceSolverTipNotAllowed());
// Interactions: transfer tokens from sender to provisioner
token.safeTransferFrom(msg.sender, address(this), tokensIn);
RequestType requestType = isFixedPrice ? RequestType.DEPOSIT_FIXED_PRICE : RequestType.DEPOSIT_AUTO_PRICE;
bytes32 depositHash = _getRequestHashParams(
token, msg.sender, requestType, tokensIn, minUnitsOut, solverTip, deadline, maxPriceAge
);
// Requirements: hash has not been used
require(!asyncDepositHashes[depositHash], Aera__HashCollision());
// Effects: set hash as used
asyncDepositHashes[depositHash] = true;
// Log emit deposit requested event
emit DepositRequested(
msg.sender, token, tokensIn, minUnitsOut, solverTip, deadline, maxPriceAge, isFixedPrice, depositHash
);
}
/// @inheritdoc IProvisioner
function requestRedeem(
IERC20 token,
uint256 unitsIn,
uint256 minTokensOut,
uint256 solverTip,
uint256 deadline,
uint256 maxPriceAge,
bool isFixedPrice
) external anyoneButVault {
// Requirements: units amount is positive, min token out is positive, deadline is in the future, deadline is not
// too far in the future, async withdrawals are enabled, vault is not paused in the PriceAndFeeCalculator
require(unitsIn != 0, Aera__UnitsInZero());
require(minTokensOut != 0, Aera__MinTokenOutZero());
require(deadline > block.timestamp, Aera__DeadlineInPast());
unchecked {
require(deadline - block.timestamp <= MAX_SECONDS_TO_DEADLINE, Aera__DeadlineTooFarInFuture());
}
require(tokensDetails[token].asyncRedeemEnabled, Aera__AsyncRedeemDisabled());
require(!PRICE_FEE_CALCULATOR.isVaultPaused(MULTI_DEPOSITOR_VAULT), Aera__PriceAndFeeCalculatorVaultPaused());
require(solverTip == 0 || !isFixedPrice, Aera__FixedPriceSolverTipNotAllowed());
// Interactions: transfer units from sender to provisioner
IERC20(MULTI_DEPOSITOR_VAULT).safeTransferFrom(msg.sender, address(this), unitsIn);
RequestType requestType = isFixedPrice ? RequestType.REDEEM_FIXED_PRICE : RequestType.REDEEM_AUTO_PRICE;
bytes32 redeemHash = _getRequestHashParams(
token, msg.sender, requestType, minTokensOut, unitsIn, solverTip, deadline, maxPriceAge
);
// Requirements: hash has not been used
require(!asyncRedeemHashes[redeemHash], Aera__HashCollision());
// Effects: set hash as used
asyncRedeemHashes[redeemHash] = true;
// Log emit redeem requested event
emit RedeemRequested(
msg.sender, token, minTokensOut, unitsIn, solverTip, deadline, maxPriceAge, isFixedPrice, redeemHash
);
}
/// @inheritdoc IProvisioner
function refundRequest(IERC20 token, Request calldata request) external nonReentrant {
// Requirements: deadline is in the past or authorized
require(
request.deadline < block.timestamp || isAuthorized(msg.sender, msg.sig),
Aera__DeadlineInFutureAndUnauthorized()
);
bytes32 requestHash = _getRequestHash(token, request);
if (_isRequestTypeDeposit(request.requestType)) {
// Requirements: hash has been set
require(asyncDepositHashes[requestHash], Aera__HashNotFound());
// Effects: unset hash as used
asyncDepositHashes[requestHash] = false;
// Interactions: transfer tokens from provisioner to sender
token.safeTransfer(request.user, request.tokens);
// Log emit deposit refunded event
emit DepositRefunded(requestHash);
} else {
// Requirements: hash has been set
require(asyncRedeemHashes[requestHash], Aera__HashNotFound());
// Effects: unset hash as used
asyncRedeemHashes[requestHash] = false;
// Interactions: transfer units from provisioner to sender
IERC20(MULTI_DEPOSITOR_VAULT).safeTransfer(request.user, request.units);
// Log emit redeem refunded event
emit RedeemRefunded(requestHash);
}
}
/// @inheritdoc IProvisioner
// solhint-disable code-complexity
function solveRequestsVault(IERC20 token, Request[] calldata requests) external requiresAuth nonReentrant {
// Interactions: get price age
uint256 priceAge = PRICE_FEE_CALCULATOR.getVaultsPriceAge(MULTI_DEPOSITOR_VAULT);
uint256 solverTip;
Request calldata request;
uint256 length = requests.length;
TokenDetails memory tokenDetails = tokensDetails[token];
bool depositsExist;
for (uint256 i = 0; i < length; i++) {
request = requests[i];
if (_isRequestTypeDeposit(request.requestType)) {
// Requirements: async deposit is enabled
if (!tokenDetails.asyncDepositEnabled) {
// Log emit async deposit disabled event
emit AsyncDepositDisabled(i);
continue;
}
if (!depositsExist) {
depositsExist = true;
token.forceApprove(MULTI_DEPOSITOR_VAULT, type(uint256).max);
}
if (_isRequestTypeAutoPrice(request.requestType)) {
// Requirements + Effects + Interactions: solve auto price deposit
solverTip +=
_solveDepositVaultAutoPrice(token, tokenDetails.depositMultiplier, request, priceAge, i);
} else {
// Requirements + Effects + Interactions: solve fixed price deposit
solverTip +=
_solveDepositVaultFixedPrice(token, tokenDetails.depositMultiplier, request, priceAge, i);
}
} else {
// Requirements: async redeem is enabled
if (!tokenDetails.asyncRedeemEnabled) {
// Log emit async redeem disabled event
emit AsyncRedeemDisabled(i);
continue;
}
if (_isRequestTypeAutoPrice(request.requestType)) {
// Requirements + Effects + Interactions: solve auto price redeem
solverTip += _solveRedeemVaultAutoPrice(token, tokenDetails.redeemMultiplier, request, priceAge, i);
} else {
// Requirements + Effects + Interactions: solve fixed price redeem
solverTip += _solveRedeemVaultFixedPrice(token, tokenDetails.redeemMultiplier, request, priceAge, i);
}
}
}
if (solverTip != 0) {
// Interactions: transfer solver tip from provisioner to sender
token.safeTransfer(msg.sender, solverTip);
}
if (depositsExist) {
// Interactions: set approval to 0
token.forceApprove(MULTI_DEPOSITOR_VAULT, 0);
}
}
/// @inheritdoc IProvisioner
function solveRequestsDirect(IERC20 token, Request[] calldata requests) external nonReentrant {
// Requirements: vault is not paused in the priceAndFeeCalculator
require(!PRICE_FEE_CALCULATOR.isVaultPaused(MULTI_DEPOSITOR_VAULT), Aera__PriceAndFeeCalculatorVaultPaused());
uint256 length = requests.length;
TokenDetails storage tokenDetails = tokensDetails[token];
for (uint256 i = 0; i < length; i++) {
if (_isRequestTypeDeposit(requests[i].requestType)) {
// Requirements: async deposit is enabled
require(tokenDetails.asyncDepositEnabled, Aera__AsyncDepositDisabled());
// Requirements: direct deposits can only solve fixed price requests
require(!_isRequestTypeAutoPrice(requests[i].requestType), Aera__AutoPriceSolveNotAllowed());
// Requirements + Effects + Interactions: solve direct deposit
_solveDepositDirect(token, requests[i]);
} else {
// Requirements: async redeem is enabled
require(tokenDetails.asyncRedeemEnabled, Aera__AsyncRedeemDisabled());
// Requirements: direct redeems can only solve fixed price requests
require(!_isRequestTypeAutoPrice(requests[i].requestType), Aera__AutoPriceSolveNotAllowed());
// Requirements + Effects + Interactions: solve direct redeem
_solveRedeemDirect(token, requests[i]);
}
}
}
/// @inheritdoc IProvisioner
function setDepositDetails(uint256 depositCap_, uint256 depositRefundTimeout_) external requiresAuth {
// Requirements: deposit cap is not zero
require(depositCap_ != 0, Aera__DepositCapZero());
// Requirements: deposit refund timeout does not exceed the safety cap
require(depositRefundTimeout_ <= MAX_DEPOSIT_REFUND_TIMEOUT, Aera__MaxDepositRefundTimeoutExceeded());
// Effects: set deposit cap and refund timeout
depositCap = depositCap_;
depositRefundTimeout = depositRefundTimeout_;
// Log emit deposit details updated event
emit DepositDetailsUpdated(depositCap_, depositRefundTimeout_);
}
/// @inheritdoc IProvisioner
function setTokenDetails(IERC20 token, TokenDetails calldata details) external requiresAuth {
// Requirements: check that the token is not the vault’s own unit token
require(address(token) != MULTI_DEPOSITOR_VAULT, Aera__InvalidToken());
uint256 depositMultiplier = details.depositMultiplier;
// Requirements: deposit multiplier is greater than or equal to min deposit multiplier
require(depositMultiplier >= MIN_DEPOSIT_MULTIPLIER, Aera__DepositMultiplierTooLow());
require(depositMultiplier <= ONE_IN_BPS, Aera__DepositMultiplierTooHigh());
uint256 redeemMultiplier = details.redeemMultiplier;
// Requirements: redeem multiplier is greater than or equal to min redeem multiplier
require(redeemMultiplier >= MIN_REDEEM_MULTIPLIER, Aera__RedeemMultiplierTooLow());
// Requirements: redeem multiplier is less than or equal to one in BPS
require(redeemMultiplier <= ONE_IN_BPS, Aera__RedeemMultiplierTooHigh());
// Effects: set token details
tokensDetails[token] = details;
if (details.asyncRedeemEnabled || details.asyncDepositEnabled || details.syncDepositEnabled) {
// Requirements: check that the token can be priced
// convertUnitsToToken instead of convertTokensToUnits to avoid having to call token.decimals()
require(
PRICE_FEE_CALCULATOR.convertUnitsToToken(MULTI_DEPOSITOR_VAULT, token, ONE_UNIT) != 0,
Aera__TokenCantBePriced()
);
}
// Log emit token details set event
emit TokenDetailsSet(token, details);
}
/// @inheritdoc IProvisioner
function removeToken(IERC20 token) external requiresAuth {
// Effects: remove tokensDetails
delete tokensDetails[token];
// Log emit token removed event
emit TokenRemoved(token);
}
/// @inheritdoc IProvisioner
function maxDeposit() external view returns (uint256) {
// Interactions: get current total supply
uint256 totalSupply = IERC20(MULTI_DEPOSITOR_VAULT).totalSupply();
// Interactions: convert total supply to numeraire
uint256 totalAssets = PRICE_FEE_CALCULATOR.convertUnitsToNumeraire(MULTI_DEPOSITOR_VAULT, totalSupply);
// Return max of 0 or difference between deposit cap and total assets
return totalAssets < depositCap ? depositCap - totalAssets : 0;
}
/// @inheritdoc IProvisioner
function areUserUnitsLocked(address user) external view returns (bool) {
return userUnitsRefundableUntil[user] >= block.timestamp;
}
/// @inheritdoc IProvisioner
function getDepositHash(
address user,
IERC20 token,
uint256 tokenAmount,
uint256 unitsAmount,
uint256 refundableUntil
) external pure returns (bytes32) {
return _getDepositHash(user, token, tokenAmount, unitsAmount, refundableUntil);
}
/// @inheritdoc IProvisioner
function getRequestHash(IERC20 token, Request calldata request) external pure returns (bytes32) {
return _getRequestHash(token, request);
}
////////////////////////////////////////////////////////////
// Internal / Private Functions //
////////////////////////////////////////////////////////////
/// @notice Handles a synchronous deposit, records the deposit hash, and enters the vault
/// @dev Reverts if the deposit hash already exists. Sets the refundable period for the user
/// @param token The ERC20 token to deposit
/// @param tokenAmount The amount of tokens to deposit
/// @param unitAmount The amount of vault units to mint for the user
function _syncDeposit(IERC20 token, uint256 tokenAmount, uint256 unitAmount) internal {
uint256 refundableUntil = block.timestamp + depositRefundTimeout;
bytes32 depositHash = _getDepositHash(msg.sender, token, tokenAmount, unitAmount, refundableUntil);
// Requirements: deposit hash is not set
require(!syncDepositHashes[depositHash], Aera__HashCollision());
// Effects: set hash as used
syncDepositHashes[depositHash] = true;
// Effects: set user refundable until
userUnitsRefundableUntil[msg.sender] = refundableUntil;
// Interactions: enter vault
IMultiDepositorVault(MULTI_DEPOSITOR_VAULT).enter(msg.sender, token, tokenAmount, unitAmount, msg.sender);
// Log emit deposit event
emit Deposited(msg.sender, token, tokenAmount, unitAmount, depositHash);
}
/// @notice Solves an async deposit request for the vault, transfering tokens or refunding as needed
/// @dev
/// - Returns 0 if any of:
/// - price age is too high, emits PriceAgeExceeded
/// - request hash is not set, emits InvalidRequestHash
/// - units out is less than min required, emits AmountBoundExceeded
/// - deposit cap would be exceeded, emits DepositCapExceeded
/// - If deadline not passed, processes deposit and emits DepositSolved
/// - If deadline passed, refunds and emits DepositRefunded
/// - Always unsets hash after processing
/// @param token The ERC20 token being deposited
/// @param depositMultiplier The multiplier (in BPS) applied to the deposit for premium calculation
/// @param request The deposit request struct containing all user parameters
/// @param priceAge The age of the price data used for conversion
/// @param index The index of the request in the given solving batch
/// @return solverTip The tip amount paid to the solver, or 0 if not processed
function _solveDepositVaultAutoPrice(
IERC20 token,
uint256 depositMultiplier,
Request calldata request,
uint256 priceAge,
uint256 index
) internal returns (uint256 solverTip) {
// Requirements: price age is within user specified max price age
if (_guardPriceAge(priceAge, request.maxPriceAge, index)) return 0;
bytes32 depositHash = _getRequestHash(token, request);
// Requirements: hash has been set
if (_guardInvalidRequestHash(asyncDepositHashes[depositHash], depositHash)) return 0;
if (request.deadline >= block.timestamp) {
solverTip = request.solverTip;
uint256 tokens = request.tokens;
// Requirements: tokens are enough for tip
if (_guardInsufficientTokensForTip(tokens, solverTip, index)) return 0;
uint256 tokensAfterTip;
unchecked {
tokensAfterTip = tokens - solverTip;
}
// Interactions: apply premium and convert tokens in to units out
uint256 unitsOut = _tokensToUnitsFloorIfActive(token, tokensAfterTip, depositMultiplier);
// Requirements: units out meets min units out
if (_guardAmountBound(unitsOut, request.units, index)) return 0;
// Requirements + interactions: convert new total units to numeraire and check against deposit cap
if (_guardDepositCapExceeded(unitsOut, index)) return 0;
// Effects: unset hash as used
asyncDepositHashes[depositHash] = false;
// Interactions: enter vault
IMultiDepositorVault(MULTI_DEPOSITOR_VAULT).enter(
address(this), token, tokensAfterTip, unitsOut, request.user
);
// Log emit deposit solved event
emit DepositSolved(depositHash);
} else {
// Effects: unset hash as used
asyncDepositHashes[depositHash] = false;
// Interactions: transfer tokens from provisioner to sender
token.safeTransfer(request.user, request.tokens);
// Log emit deposit refunded event
emit DepositRefunded(depositHash);
}
}
/// @notice Solves a fixed price deposit request for the vault, transfering tokens or refunding as needed
/// @dev User gets exactly min units out, but may over‑fund, the difference is paid to the solver as a tip
/// @dev
/// - Returns 0 if any of:
/// - price age is too high, emits PriceAgeExceeded
/// - request hash is not set, emits InvalidRequestHash
/// - tokens needed exceed the maximum allowed, emits AmountBoundExceeded
/// - deposit cap would be exceeded, emits DepositCapExceeded
/// - If deadline not passed, processes deposit and emits DepositSolved
/// - If deadline passed, refunds and emits DepositRefunded
/// - Always unsets hash after processing
/// @param token The ERC20 token being deposited
/// @param depositMultiplier The multiplier (in BPS) applied to the deposit for premium calculation
/// @param request The deposit request struct containing all user parameters
/// @param priceAge The age of the price data used for conversion
/// @param index The index of the request in the given solving batch
/// @return solverTip The tip amount paid to the solver, or 0 if not processed
function _solveDepositVaultFixedPrice(
IERC20 token,
uint256 depositMultiplier,
Request calldata request,
uint256 priceAge,
uint256 index
) internal returns (uint256 solverTip) {
// Requirements: price age is within user specified max price age
if (_guardPriceAge(priceAge, request.maxPriceAge, index)) return 0;
bytes32 depositHash = _getRequestHash(token, request);
// Requirements: hash has been set
if (_guardInvalidRequestHash(asyncDepositHashes[depositHash], depositHash)) return 0;
if (request.deadline >= block.timestamp) {
// Interactions: convert units to tokens applying premium
uint256 tokensNeeded = _unitsToTokensCeilIfActive(token, request.units, depositMultiplier);
// Requirements: tokens needed is less than or equal to max tokens in
if (_guardAmountBound(request.tokens, tokensNeeded, index)) return 0;
// Requirements + interactions: convert new total units to numeraire and check against deposit cap
if (_guardDepositCapExceeded(request.units, index)) return 0;
// Effects: unset hash as used
asyncDepositHashes[depositHash] = false;
// Interactions: enter vault
IMultiDepositorVault(MULTI_DEPOSITOR_VAULT).enter(
address(this), token, tokensNeeded, request.units, request.user
);
unchecked {
solverTip = request.tokens - tokensNeeded;
}
// Log emit deposit solved event
emit DepositSolved(depositHash);
} else {
// Effects: unset hash as used
asyncDepositHashes[depositHash] = false;
// Interactions: transfer tokens from provisioner to sender
token.safeTransfer(request.user, request.tokens);
// Log emit deposit refunded event
emit DepositRefunded(depositHash);
}
}
/// @notice Solves an async redeem request for the vault, transfering tokens or refunding as needed
/// @dev
/// - Returns 0 if any of:
/// - price age is too high, emits PriceAgeExceeded
/// - request hash is not set, emits InvalidRequestHash
/// - token out after premium is less than min required, emits AmountBoundExceeded
/// - If deadline not passed, processes redeem and emits RedeemSolved
/// - If deadline passed, refunds and emits RedeemRefunded
/// - Always unsets hash after processing
/// @param token The ERC20 token being redeemed
/// @param redeemMultiplier The multiplier (in BPS) applied to the redeem for premium calculation
/// @param request The redeem request struct containing all user parameters
/// @param priceAge The age of the price data used for conversion
/// @param index The index of the request in the given solving batch
/// @return solverTip The tip amount paid to the solver, or 0 if not processed
function _solveRedeemVaultAutoPrice(
IERC20 token,
uint256 redeemMultiplier,
Request calldata request,
uint256 priceAge,
uint256 index
) internal returns (uint256 solverTip) {
// Requirements: price age is within user specified max price age
if (_guardPriceAge(priceAge, request.maxPriceAge, index)) return 0;
bytes32 redeemHash = _getRequestHash(token, request);
// Requirements: hash has been set
if (_guardInvalidRequestHash(asyncRedeemHashes[redeemHash], redeemHash)) return 0;
if (request.deadline >= block.timestamp) {
solverTip = request.solverTip;
// Interactions: convert units to token amount
uint256 tokenOut = _unitsToTokensFloorIfActive(token, request.units, redeemMultiplier);
// Requirements: tokens are enough for tip
if (_guardInsufficientTokensForTip(tokenOut, solverTip, index)) return 0;
uint256 tokenOutAfterTip;
unchecked {
tokenOutAfterTip = tokenOut - solverTip;
}
// Requirements: token amount is greater than or equal to net token amount
if (_guardAmountBound(tokenOutAfterTip, request.tokens, index)) return 0;
// Effects: unset hash as used
asyncRedeemHashes[redeemHash] = false;
// Interactions: exit vault
IMultiDepositorVault(MULTI_DEPOSITOR_VAULT).exit(
address(this), token, tokenOut, request.units, address(this)
);
// Interactions: transfer tokens from provisioner to sender
token.safeTransfer(request.user, tokenOutAfterTip);
// Log emit redeem solved event
emit RedeemSolved(redeemHash);
} else {
// Effects: unset hash as used
asyncRedeemHashes[redeemHash] = false;
// Interactions: transfer units from provisioner to sender
IERC20(MULTI_DEPOSITOR_VAULT).safeTransfer(request.user, request.units);
// Log emit redeem refunded event
emit RedeemRefunded(redeemHash);
}
}
/// @notice Solves a fixed price redeem request for the vault, transfering tokens or refunding as needed
/// @dev User gets exactly min tokens out, but may under‑fund, the difference is paid to the solver as a tip
/// @dev
/// - Returns 0 if any of:
/// - price age is too high, emits PriceAgeExceeded
/// - request hash is not set, emits InvalidRequestHash
/// - If deadline not passed, processes redeem and emits RedeemSolved
/// - If deadline passed, refunds and emits RedeemRefunded
/// - Always unsets hash after processing
/// @param token The ERC20 token being redeemed
/// @param redeemMultiplier The multiplier (in BPS) applied to the redeem for premium calculation
/// @param request The redeem request struct containing all user parameters
/// @param priceAge The age of the price data used for conversion
/// @param index The index of the request in the given solving batch
/// @return solverTip The tip amount paid to the solver, or 0 if not processed
function _solveRedeemVaultFixedPrice(
IERC20 token,
uint256 redeemMultiplier,
Request calldata request,
uint256 priceAge,
uint256 index
) internal returns (uint256 solverTip) {
// Requirements: price age is within user specified max price age
if (_guardPriceAge(priceAge, request.maxPriceAge, index)) return 0;
bytes32 redeemHash = _getRequestHash(token, request);
// Requirements: hash has been set
if (_guardInvalidRequestHash(asyncRedeemHashes[redeemHash], redeemHash)) return 0;
if (request.deadline >= block.timestamp) {
// Interactions: convert units to token amount
uint256 tokenOut = _unitsToTokensFloorIfActive(token, request.units, redeemMultiplier);
// Requirements: token amount is greater than or equal to net token amount
if (_guardAmountBound(tokenOut, request.tokens, index)) return 0;
// Effects: unset hash as used
asyncRedeemHashes[redeemHash] = false;
// Interactions: exit vault
IMultiDepositorVault(MULTI_DEPOSITOR_VAULT).exit(
address(this), token, tokenOut, request.units, address(this)
);
// Interactions: transfer tokens from provisioner to sender
token.safeTransfer(request.user, request.tokens);
unchecked {
solverTip = tokenOut - request.tokens;
}
// Log emit redeem solved event
emit RedeemSolved(redeemHash);
} else {
// Effects: unset hash as used
asyncRedeemHashes[redeemHash] = false;
// Interactions: transfer units from provisioner to sender
IERC20(MULTI_DEPOSITOR_VAULT).safeTransfer(request.user, request.units);
// Log emit redeem refunded event
emit RedeemRefunded(redeemHash);
}
}
/// @notice Solves a direct deposit request, transfering tokens and units between users
/// @dev
/// - Returns early if any of:
/// - request hash is not set, emits InvalidRequestHash
/// - If deadline not passed, transfers units and tokens, emits DepositSolved
/// - If deadline passed, refunds tokens, emits DepositRefunded
/// - Always unsets hash after processing
/// @param token The ERC20 token being deposited
/// @param request The deposit request struct containing all user parameters
function _solveDepositDirect(IERC20 token, Request calldata request) internal {
bytes32 depositHash = _getRequestHash(token, request);
// Requirements: hash has been set
if (!asyncDepositHashes[depositHash]) {
// Log emit invalid async deposit hash event
emit InvalidRequestHash(depositHash);
return;
}
// Effects: unset hash as used
asyncDepositHashes[depositHash] = false;
if (request.deadline >= block.timestamp) {
// Interactions: pull units from sender(solver) to user
IERC20(MULTI_DEPOSITOR_VAULT).safeTransferFrom(msg.sender, request.user, request.units);
// Interactions: transfer tokens from provisioner to sender
token.safeTransfer(msg.sender, request.tokens);
// Log emit deposit solved event
emit DepositSolved(depositHash);
} else {
// Interactions: transfer tokens from provisioner to sender
token.safeTransfer(request.user, request.tokens);
// Log emit deposit refunded event
emit DepositRefunded(depositHash);
}
}
/// @notice Solves a direct redeem request, transfering tokens and units between users
/// @dev
/// - Returns early if:
/// - request hash is not set, emits InvalidRequestHash
/// - If deadline not passed, transfers units and tokens, emits RedeemSolved
/// - If deadline passed, refunds units, emits RedeemRefunded
/// - Always unsets hash after processing
/// @param token The ERC20 token being redeemed
/// @param request The redeem request struct containing all user parameters
function _solveRedeemDirect(IERC20 token, Request calldata request) internal {
bytes32 redeemHash = _getRequestHash(token, request);
// Requirements: hash has been set
if (!asyncRedeemHashes[redeemHash]) {
// Log emit invalid async redeem hash event
emit InvalidRequestHash(redeemHash);
return;
}
// Effects: unset hash as used
asyncRedeemHashes[redeemHash] = false;
if (request.deadline >= block.timestamp) {
// Interactions: transfer units from provisioner to sender
IERC20(MULTI_DEPOSITOR_VAULT).safeTransfer(msg.sender, request.units);
// Interactions: pull tokens from sender(solver) to user
token.safeTransferFrom(msg.sender, request.user, request.tokens);
// Log emit redeem solved event
emit RedeemSolved(redeemHash);
} else {
// Interactions: transfer units from provisioner to sender
IERC20(MULTI_DEPOSITOR_VAULT).safeTransfer(request.user, request.units);
// Log emit redeem refunded event
emit RedeemRefunded(redeemHash);
}
}
/// @notice Checks if the price age exceeds the maximum allowed and emits an event if so
/// @param priceAge The difference between when price was measured and submitted onchain
/// @param maxPriceAge The maximum allowed price age
/// @param index The index of the request in the given solving batch
/// @return True if price age is too high, false otherwise
function _guardPriceAge(uint256 priceAge, uint256 maxPriceAge, uint256 index) internal returns (bool) {
if (priceAge > maxPriceAge) {
emit PriceAgeExceeded(index);
return true;
}
return false;
}
/// @notice Checks if the request hash exists and emits an event if not
/// @param hashExists Whether the hash exists
/// @param requestHash The request hash
/// @return True if hash does not exist, false otherwise
function _guardInvalidRequestHash(bool hashExists, bytes32 requestHash) internal returns (bool) {
if (!hashExists) {
// Log emit invalid request hash event
emit InvalidRequestHash(requestHash);
return true;
}
return false;
}
/// @notice Checks if there are enough tokens for the solver tip and emits an event if not
/// @param tokens The number of tokens
/// @param solverTip The solver tip amount
/// @param index The index of the request in the given solving batch
/// @return True if not enough tokens for tip, false otherwise
function _guardInsufficientTokensForTip(uint256 tokens, uint256 solverTip, uint256 index) internal returns (bool) {
if (tokens < solverTip) {
// Log emit insufficient tokens for tip event
emit InsufficientTokensForTip(index);
return true;
}
return false;
}
/// @notice Checks if the amount is less than the bound and emits an event if so
/// @param amount The actual amount
/// @param bound The minimum required amount
/// @param index The index of the request in the given solving batch
/// @return True if amount is less than bound, false otherwise
function _guardAmountBound(uint256 amount, uint256 bound, uint256 index) internal returns (bool) {
if (amount < bound) {
// Log emit amount bound exceeded event
emit AmountBoundExceeded(index, amount, bound);
return true;
}
return false;
}
/// @notice Checks if the deposit cap would be exceeded and emits an event if so
/// @param totalUnits The total units after deposit
/// @param index The index of the request in the given solving batch
/// @return True if deposit cap would be exceeded, false otherwise
function _guardDepositCapExceeded(uint256 totalUnits, uint256 index) internal returns (bool) {
// Interactions: check if deposit cap would be exceeded
if (_isDepositCapExceeded(totalUnits)) {
// Log emit deposit cap exceeded event
emit DepositCapExceeded(index);
return true;
}
return false;
}
/// @notice Reverts if sync deposits are not enabled for the token
/// @param token The ERC20 token to check
/// @return tokenDetails The token details storage reference
function _requireSyncDepositsEnabled(IERC20 token) internal view returns (TokenDetails storage tokenDetails) {
tokenDetails = tokensDetails[token];
// Requirements: sync deposits are enabled
require(tokenDetails.syncDepositEnabled, Aera__SyncDepositDisabled());
}
/// @notice Reverts if deposit cap would be exceeded by adding units
/// @param units The number of units to add
function _requireDepositCapNotExceeded(uint256 units) internal view {
// Requirements + interactions: deposit cap not exceeded
require(!_isDepositCapExceeded(units), Aera__DepositCapExceeded());
}
/// @notice Checks if deposit cap would be exceeded by adding units
/// @param units The number of units to add
/// @return True if deposit cap would be exceeded, false otherwise
function _isDepositCapExceeded(uint256 units) internal view returns (bool) {
// Interactions: get current total supply
uint256 newTotal = IERC20(MULTI_DEPOSITOR_VAULT).totalSupply() + units;
// Interactions: convert total supply to numeraire
return PRICE_FEE_CALCULATOR.convertUnitsToNumeraire(MULTI_DEPOSITOR_VAULT, newTotal) > depositCap;
}
/// @notice Converts token amount to units, applying multiplier and flooring
/// @param token The ERC20 token
/// @param tokens The amount of tokens
/// @param multiplier The multiplier to apply
/// @return The resulting units (floored)
function _tokensToUnitsFloorIfActive(IERC20 token, uint256 tokens, uint256 multiplier)
internal
view
returns (uint256)
{
uint256 tokensAdjusted = tokens * multiplier / ONE_IN_BPS;
// Interactions: convert tokens to units
return PRICE_FEE_CALCULATOR.convertTokenToUnitsIfActive(
MULTI_DEPOSITOR_VAULT, token, tokensAdjusted, Math.Rounding.Floor
);
}
/// @notice Converts units to token amount, applying multiplier and flooring
/// @param token The ERC20 token
/// @param units The amount of units
/// @param multiplier The multiplier to apply
/// @return The resulting token amount (floored)
function _unitsToTokensFloorIfActive(IERC20 token, uint256 units, uint256 multiplier)
internal
view
returns (uint256)
{
// Interactions: convert units to tokens
uint256 tokensAmount =
PRICE_FEE_CALCULATOR.convertUnitsToTokenIfActive(MULTI_DEPOSITOR_VAULT, token, units, Math.Rounding.Floor);
return tokensAmount * multiplier / ONE_IN_BPS;
}
/// @notice Converts units to token amount, applying multiplier and ceiling
/// @param token The ERC20 token
/// @param units The amount of units
/// @param multiplier The multiplier to apply
/// @return The resulting token amount (ceiled)
function _unitsToTokensCeilIfActive(IERC20 token, uint256 units, uint256 multiplier)
internal
view
returns (uint256)
{
// Interactions: convert units to tokens
uint256 tokensAmount =
PRICE_FEE_CALCULATOR.convertUnitsToTokenIfActive(MULTI_DEPOSITOR_VAULT, token, units, Math.Rounding.Ceil);
return Math.mulDiv(tokensAmount, ONE_IN_BPS, multiplier, Math.Rounding.Ceil);
}
/// @notice Get the hash of a deposit
/// @param user The user who made the deposit
/// @param token The token that was deposited
/// @param tokenAmount The amount of tokens deposited
/// @param unitsAmount The amount of units received
/// @param refundableUntil The timestamp at which the deposit can be refunded
/// @dev Since refundableUntil is block.timestamp + depositRefundTimeout (which is subject to change), it's
/// theoretically possible to have a hash collision, but the probability is negligible and we optimize for the
/// common case
function _getDepositHash(
address user,
IERC20 token,
uint256 tokenAmount,
uint256 unitsAmount,
uint256 refundableUntil
) internal pure returns (bytes32) {
return keccak256(abi.encodePacked(user, token, tokenAmount, unitsAmount, refundableUntil));
}
/// @notice Get the hash of a request from parameters
/// @param token The token that was deposited or redeemed
/// @param user The user who made the request
/// @param requestType The type of request
/// @param tokens The amount of tokens in the request
/// @param units The amount of units in the request
/// @param solverTip The tip paid to the solver
/// @param deadline The deadline of the request
/// @param maxPriceAge The maximum age of the price data
/// @return The hash of the request
function _getRequestHashParams(
IERC20 token,
address user,
RequestType requestType,
uint256 tokens,
uint256 units,
uint256 solverTip,
uint256 deadline,
uint256 maxPriceAge
) internal pure returns (bytes32) {
return keccak256(abi.encodePacked(token, user, requestType, tokens, units, solverTip, deadline, maxPriceAge));
}
/// @notice Get the hash of a request
/// @param token The token that was deposited or redeemed
/// @param request The request to get the hash of
function _getRequestHash(IERC20 token, Request calldata request) internal pure returns (bytes32) {
return keccak256(
abi.encodePacked(
token,
request.user,
request.requestType,
request.tokens,
request.units,
request.solverTip,
request.deadline,
request.maxPriceAge
)
);
}
/// @notice Returns true if the request type is a deposit
/// @param requestType The request type
/// @return True if deposit, false otherwise
function _isRequestTypeDeposit(RequestType requestType) internal pure returns (bool) {
return uint8(requestType) & DEPOSIT_REDEEM_FLAG == 0;
}
/// @notice Returns true if the request type is fixed price
/// @param requestType The request type
/// @return True if fixed price, false otherwise
function _isRequestTypeAutoPrice(RequestType requestType) internal pure returns (bool) {
return uint8(requestType) & AUTO_PRICE_FIXED_PRICE_FLAG == 0;
}
}// 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/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: 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
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol)
pragma solidity ^0.8.20;
import {Panic} from "../Panic.sol";
import {SafeCast} from "./SafeCast.sol";
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
enum Rounding {
Floor, // Toward negative infinity
Ceil, // Toward positive infinity
Trunc, // Toward zero
Expand // Away from zero
}
/**
* @dev Returns the addition of two unsigned integers, with an success flag (no overflow).
*/
function tryAdd(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the subtraction of two unsigned integers, with an success flag (no overflow).
*/
function trySub(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
if (b > a) return (false, 0);
return (true, a - b);
}
}
/**
* @dev Returns the multiplication of two unsigned integers, with an success flag (no overflow).
*/
function tryMul(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the division of two unsigned integers, with a success flag (no division by zero).
*/
function tryDiv(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
if (b == 0) return (false, 0);
return (true, a / b);
}
}
/**
* @dev Returns the remainder of dividing two unsigned integers, with a success flag (no division by zero).
*/
function tryMod(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
if (b == 0) return (false, 0);
return (true, a % b);
}
}
/**
* @dev Branchless ternary evaluation for `a ? b : c`. Gas costs are constant.
*
* IMPORTANT: This function may reduce bytecode size and consume less gas when used standalone.
* However, the compiler may optimize Solidity ternary operations (i.e. `a ? b : c`) to only compute
* one branch when needed, making this function more expensive.
*/
function ternary(bool condition, uint256 a, uint256 b) internal pure returns (uint256) {
unchecked {
// branchless ternary works because:
// b ^ (a ^ b) == a
// b ^ 0 == b
return b ^ ((a ^ b) * SafeCast.toUint(condition));
}
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return ternary(a > b, a, b);
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return ternary(a < b, a, b);
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds towards infinity instead
* of rounding towards zero.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
if (b == 0) {
// Guarantee the same behavior as in a regular Solidity division.
Panic.panic(Panic.DIVISION_BY_ZERO);
}
// The following calculation ensures accurate ceiling division without overflow.
// Since a is non-zero, (a - 1) / b will not overflow.
// The largest possible result occurs when (a - 1) / b is type(uint256).max,
// but the largest value we can obtain is type(uint256).max - 1, which happens
// when a = type(uint256).max and b = 1.
unchecked {
return SafeCast.toUint(a > 0) * ((a - 1) / b + 1);
}
}
/**
* @dev Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
* denominator == 0.
*
* Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by
* Uniswap Labs also under MIT license.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2²⁵⁶ and mod 2²⁵⁶ - 1, then use
// the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2²⁵⁶ + prod0.
uint256 prod0 = x * y; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
// Solidity will revert if denominator == 0, unlike the div opcode on its own.
// The surrounding unchecked block does not change this fact.
// See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
return prod0 / denominator;
}
// Make sure the result is less than 2²⁵⁶. Also prevents denominator == 0.
if (denominator <= prod1) {
Panic.panic(ternary(denominator == 0, Panic.DIVISION_BY_ZERO, Panic.UNDER_OVERFLOW));
}
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator.
// Always >= 1. See https://cs.stackexchange.com/q/138556/92363.
uint256 twos = denominator & (0 - denominator);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2²⁵⁶ / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2²⁵⁶. Now that denominator is an odd number, it has an inverse modulo 2²⁵⁶ such
// that denominator * inv ≡ 1 mod 2²⁵⁶. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv ≡ 1 mod 2⁴.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also
// works in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2⁸
inverse *= 2 - denominator * inverse; // inverse mod 2¹⁶
inverse *= 2 - denominator * inverse; // inverse mod 2³²
inverse *= 2 - denominator * inverse; // inverse mod 2⁶⁴
inverse *= 2 - denominator * inverse; // inverse mod 2¹²⁸
inverse *= 2 - denominator * inverse; // inverse mod 2²⁵⁶
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2²⁵⁶. Since the preconditions guarantee that the outcome is
// less than 2²⁵⁶, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @dev Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
return mulDiv(x, y, denominator) + SafeCast.toUint(unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0);
}
/**
* @dev Calculate the modular multiplicative inverse of a number in Z/nZ.
*
* If n is a prime, then Z/nZ is a field. In that case all elements are inversible, except 0.
* If n is not a prime, then Z/nZ is not a field, and some elements might not be inversible.
*
* If the input value is not inversible, 0 is returned.
*
* NOTE: If you know for sure that n is (big) a prime, it may be cheaper to use Fermat's little theorem and get the
* inverse using `Math.modExp(a, n - 2, n)`. See {invModPrime}.
*/
function invMod(uint256 a, uint256 n) internal pure returns (uint256) {
unchecked {
if (n == 0) return 0;
// The inverse modulo is calculated using the Extended Euclidean Algorithm (iterative version)
// Used to compute integers x and y such that: ax + ny = gcd(a, n).
// When the gcd is 1, then the inverse of a modulo n exists and it's x.
// ax + ny = 1
// ax = 1 + (-y)n
// ax ≡ 1 (mod n) # x is the inverse of a modulo n
// If the remainder is 0 the gcd is n right away.
uint256 remainder = a % n;
uint256 gcd = n;
// Therefore the initial coefficients are:
// ax + ny = gcd(a, n) = n
// 0a + 1n = n
int256 x = 0;
int256 y = 1;
while (remainder != 0) {
uint256 quotient = gcd / remainder;
(gcd, remainder) = (
// The old remainder is the next gcd to try.
remainder,
// Compute the next remainder.
// Can't overflow given that (a % gcd) * (gcd // (a % gcd)) <= gcd
// where gcd is at most n (capped to type(uint256).max)
gcd - remainder * quotient
);
(x, y) = (
// Increment the coefficient of a.
y,
// Decrement the coefficient of n.
// Can overflow, but the result is casted to uint256 so that the
// next value of y is "wrapped around" to a value between 0 and n - 1.
x - y * int256(quotient)
);
}
if (gcd != 1) return 0; // No inverse exists.
return ternary(x < 0, n - uint256(-x), uint256(x)); // Wrap the result if it's negative.
}
}
/**
* @dev Variant of {invMod}. More efficient, but only works if `p` is known to be a prime greater than `2`.
*
* From https://en.wikipedia.org/wiki/Fermat%27s_little_theorem[Fermat's little theorem], we know that if p is
* prime, then `a**(p-1) ≡ 1 mod p`. As a consequence, we have `a * a**(p-2) ≡ 1 mod p`, which means that
* `a**(p-2)` is the modular multiplicative inverse of a in Fp.
*
* NOTE: this function does NOT check that `p` is a prime greater than `2`.
*/
function invModPrime(uint256 a, uint256 p) internal view returns (uint256) {
unchecked {
return Math.modExp(a, p - 2, p);
}
}
/**
* @dev Returns the modular exponentiation of the specified base, exponent and modulus (b ** e % m)
*
* Requirements:
* - modulus can't be zero
* - underlying staticcall to precompile must succeed
*
* IMPORTANT: The result is only valid if the underlying call succeeds. When using this function, make
* sure the chain you're using it on supports the precompiled contract for modular exponentiation
* at address 0x05 as specified in https://eips.ethereum.org/EIPS/eip-198[EIP-198]. Otherwise,
* the underlying function will succeed given the lack of a revert, but the result may be incorrectly
* interpreted as 0.
*/
function modExp(uint256 b, uint256 e, uint256 m) internal view returns (uint256) {
(bool success, uint256 result) = tryModExp(b, e, m);
if (!success) {
Panic.panic(Panic.DIVISION_BY_ZERO);
}
return result;
}
/**
* @dev Returns the modular exponentiation of the specified base, exponent and modulus (b ** e % m).
* It includes a success flag indicating if the operation succeeded. Operation will be marked as failed if trying
* to operate modulo 0 or if the underlying precompile reverted.
*
* IMPORTANT: The result is only valid if the success flag is true. When using this function, make sure the chain
* you're using it on supports the precompiled contract for modular exponentiation at address 0x05 as specified in
* https://eips.ethereum.org/EIPS/eip-198[EIP-198]. Otherwise, the underlying function will succeed given the lack
* of a revert, but the result may be incorrectly interpreted as 0.
*/
function tryModExp(uint256 b, uint256 e, uint256 m) internal view returns (bool success, uint256 result) {
if (m == 0) return (false, 0);
assembly ("memory-safe") {
let ptr := mload(0x40)
// | Offset | Content | Content (Hex) |
// |-----------|------------|--------------------------------------------------------------------|
// | 0x00:0x1f | size of b | 0x0000000000000000000000000000000000000000000000000000000000000020 |
// | 0x20:0x3f | size of e | 0x0000000000000000000000000000000000000000000000000000000000000020 |
// | 0x40:0x5f | size of m | 0x0000000000000000000000000000000000000000000000000000000000000020 |
// | 0x60:0x7f | value of b | 0x<.............................................................b> |
// | 0x80:0x9f | value of e | 0x<.............................................................e> |
// | 0xa0:0xbf | value of m | 0x<.............................................................m> |
mstore(ptr, 0x20)
mstore(add(ptr, 0x20), 0x20)
mstore(add(ptr, 0x40), 0x20)
mstore(add(ptr, 0x60), b)
mstore(add(ptr, 0x80), e)
mstore(add(ptr, 0xa0), m)
// Given the result < m, it's guaranteed to fit in 32 bytes,
// so we can use the memory scratch space located at offset 0.
success := staticcall(gas(), 0x05, ptr, 0xc0, 0x00, 0x20)
result := mload(0x00)
}
}
/**
* @dev Variant of {modExp} that supports inputs of arbitrary length.
*/
function modExp(bytes memory b, bytes memory e, bytes memory m) internal view returns (bytes memory) {
(bool success, bytes memory result) = tryModExp(b, e, m);
if (!success) {
Panic.panic(Panic.DIVISION_BY_ZERO);
}
return result;
}
/**
* @dev Variant of {tryModExp} that supports inputs of arbitrary length.
*/
function tryModExp(
bytes memory b,
bytes memory e,
bytes memory m
) internal view returns (bool success, bytes memory result) {
if (_zeroBytes(m)) return (false, new bytes(0));
uint256 mLen = m.length;
// Encode call args in result and move the free memory pointer
result = abi.encodePacked(b.length, e.length, mLen, b, e, m);
assembly ("memory-safe") {
let dataPtr := add(result, 0x20)
// Write result on top of args to avoid allocating extra memory.
success := staticcall(gas(), 0x05, dataPtr, mload(result), dataPtr, mLen)
// Overwrite the length.
// result.length > returndatasize() is guaranteed because returndatasize() == m.length
mstore(result, mLen)
// Set the memory pointer after the returned data.
mstore(0x40, add(dataPtr, mLen))
}
}
/**
* @dev Returns whether the provided byte array is zero.
*/
function _zeroBytes(bytes memory byteArray) private pure returns (bool) {
for (uint256 i = 0; i < byteArray.length; ++i) {
if (byteArray[i] != 0) {
return false;
}
}
return true;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
* towards zero.
*
* This method is based on Newton's method for computing square roots; the algorithm is restricted to only
* using integer operations.
*/
function sqrt(uint256 a) internal pure returns (uint256) {
unchecked {
// Take care of easy edge cases when a == 0 or a == 1
if (a <= 1) {
return a;
}
// In this function, we use Newton's method to get a root of `f(x) := x² - a`. It involves building a
// sequence x_n that converges toward sqrt(a). For each iteration x_n, we also define the error between
// the current value as `ε_n = | x_n - sqrt(a) |`.
//
// For our first estimation, we consider `e` the smallest power of 2 which is bigger than the square root
// of the target. (i.e. `2**(e-1) ≤ sqrt(a) < 2**e`). We know that `e ≤ 128` because `(2¹²⁸)² = 2²⁵⁶` is
// bigger than any uint256.
//
// By noticing that
// `2**(e-1) ≤ sqrt(a) < 2**e → (2**(e-1))² ≤ a < (2**e)² → 2**(2*e-2) ≤ a < 2**(2*e)`
// we can deduce that `e - 1` is `log2(a) / 2`. We can thus compute `x_n = 2**(e-1)` using a method similar
// to the msb function.
uint256 aa = a;
uint256 xn = 1;
if (aa >= (1 << 128)) {
aa >>= 128;
xn <<= 64;
}
if (aa >= (1 << 64)) {
aa >>= 64;
xn <<= 32;
}
if (aa >= (1 << 32)) {
aa >>= 32;
xn <<= 16;
}
if (aa >= (1 << 16)) {
aa >>= 16;
xn <<= 8;
}
if (aa >= (1 << 8)) {
aa >>= 8;
xn <<= 4;
}
if (aa >= (1 << 4)) {
aa >>= 4;
xn <<= 2;
}
if (aa >= (1 << 2)) {
xn <<= 1;
}
// We now have x_n such that `x_n = 2**(e-1) ≤ sqrt(a) < 2**e = 2 * x_n`. This implies ε_n ≤ 2**(e-1).
//
// We can refine our estimation by noticing that the middle of that interval minimizes the error.
// If we move x_n to equal 2**(e-1) + 2**(e-2), then we reduce the error to ε_n ≤ 2**(e-2).
// This is going to be our x_0 (and ε_0)
xn = (3 * xn) >> 1; // ε_0 := | x_0 - sqrt(a) | ≤ 2**(e-2)
// From here, Newton's method give us:
// x_{n+1} = (x_n + a / x_n) / 2
//
// One should note that:
// x_{n+1}² - a = ((x_n + a / x_n) / 2)² - a
// = ((x_n² + a) / (2 * x_n))² - a
// = (x_n⁴ + 2 * a * x_n² + a²) / (4 * x_n²) - a
// = (x_n⁴ + 2 * a * x_n² + a² - 4 * a * x_n²) / (4 * x_n²)
// = (x_n⁴ - 2 * a * x_n² + a²) / (4 * x_n²)
// = (x_n² - a)² / (2 * x_n)²
// = ((x_n² - a) / (2 * x_n))²
// ≥ 0
// Which proves that for all n ≥ 1, sqrt(a) ≤ x_n
//
// This gives us the proof of quadratic convergence of the sequence:
// ε_{n+1} = | x_{n+1} - sqrt(a) |
// = | (x_n + a / x_n) / 2 - sqrt(a) |
// = | (x_n² + a - 2*x_n*sqrt(a)) / (2 * x_n) |
// = | (x_n - sqrt(a))² / (2 * x_n) |
// = | ε_n² / (2 * x_n) |
// = ε_n² / | (2 * x_n) |
//
// For the first iteration, we have a special case where x_0 is known:
// ε_1 = ε_0² / | (2 * x_0) |
// ≤ (2**(e-2))² / (2 * (2**(e-1) + 2**(e-2)))
// ≤ 2**(2*e-4) / (3 * 2**(e-1))
// ≤ 2**(e-3) / 3
// ≤ 2**(e-3-log2(3))
// ≤ 2**(e-4.5)
//
// For the following iterations, we use the fact that, 2**(e-1) ≤ sqrt(a) ≤ x_n:
// ε_{n+1} = ε_n² / | (2 * x_n) |
// ≤ (2**(e-k))² / (2 * 2**(e-1))
// ≤ 2**(2*e-2*k) / 2**e
// ≤ 2**(e-2*k)
xn = (xn + a / xn) >> 1; // ε_1 := | x_1 - sqrt(a) | ≤ 2**(e-4.5) -- special case, see above
xn = (xn + a / xn) >> 1; // ε_2 := | x_2 - sqrt(a) | ≤ 2**(e-9) -- general case with k = 4.5
xn = (xn + a / xn) >> 1; // ε_3 := | x_3 - sqrt(a) | ≤ 2**(e-18) -- general case with k = 9
xn = (xn + a / xn) >> 1; // ε_4 := | x_4 - sqrt(a) | ≤ 2**(e-36) -- general case with k = 18
xn = (xn + a / xn) >> 1; // ε_5 := | x_5 - sqrt(a) | ≤ 2**(e-72) -- general case with k = 36
xn = (xn + a / xn) >> 1; // ε_6 := | x_6 - sqrt(a) | ≤ 2**(e-144) -- general case with k = 72
// Because e ≤ 128 (as discussed during the first estimation phase), we know have reached a precision
// ε_6 ≤ 2**(e-144) < 1. Given we're operating on integers, then we can ensure that xn is now either
// sqrt(a) or sqrt(a) + 1.
return xn - SafeCast.toUint(xn > a / xn);
}
}
/**
* @dev Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + SafeCast.toUint(unsignedRoundsUp(rounding) && result * result < a);
}
}
/**
* @dev Return the log in base 2 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
uint256 exp;
unchecked {
exp = 128 * SafeCast.toUint(value > (1 << 128) - 1);
value >>= exp;
result += exp;
exp = 64 * SafeCast.toUint(value > (1 << 64) - 1);
value >>= exp;
result += exp;
exp = 32 * SafeCast.toUint(value > (1 << 32) - 1);
value >>= exp;
result += exp;
exp = 16 * SafeCast.toUint(value > (1 << 16) - 1);
value >>= exp;
result += exp;
exp = 8 * SafeCast.toUint(value > (1 << 8) - 1);
value >>= exp;
result += exp;
exp = 4 * SafeCast.toUint(value > (1 << 4) - 1);
value >>= exp;
result += exp;
exp = 2 * SafeCast.toUint(value > (1 << 2) - 1);
value >>= exp;
result += exp;
result += SafeCast.toUint(value > 1);
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + SafeCast.toUint(unsignedRoundsUp(rounding) && 1 << result < value);
}
}
/**
* @dev Return the log in base 10 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10 ** 64) {
value /= 10 ** 64;
result += 64;
}
if (value >= 10 ** 32) {
value /= 10 ** 32;
result += 32;
}
if (value >= 10 ** 16) {
value /= 10 ** 16;
result += 16;
}
if (value >= 10 ** 8) {
value /= 10 ** 8;
result += 8;
}
if (value >= 10 ** 4) {
value /= 10 ** 4;
result += 4;
}
if (value >= 10 ** 2) {
value /= 10 ** 2;
result += 2;
}
if (value >= 10 ** 1) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + SafeCast.toUint(unsignedRoundsUp(rounding) && 10 ** result < value);
}
}
/**
* @dev Return the log in base 256 of a positive value rounded towards zero.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
uint256 isGt;
unchecked {
isGt = SafeCast.toUint(value > (1 << 128) - 1);
value >>= isGt * 128;
result += isGt * 16;
isGt = SafeCast.toUint(value > (1 << 64) - 1);
value >>= isGt * 64;
result += isGt * 8;
isGt = SafeCast.toUint(value > (1 << 32) - 1);
value >>= isGt * 32;
result += isGt * 4;
isGt = SafeCast.toUint(value > (1 << 16) - 1);
value >>= isGt * 16;
result += isGt * 2;
result += SafeCast.toUint(value > (1 << 8) - 1);
}
return result;
}
/**
* @dev Return the log in base 256, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + SafeCast.toUint(unsignedRoundsUp(rounding) && 1 << (result << 3) < value);
}
}
/**
* @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
*/
function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
return uint8(rounding) % 2 == 1;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/structs/BitMaps.sol)
pragma solidity ^0.8.20;
/**
* @dev Library for managing uint256 to bool mapping in a compact and efficient way, provided the keys are sequential.
* Largely inspired by Uniswap's https://github.com/Uniswap/merkle-distributor/blob/master/contracts/MerkleDistributor.sol[merkle-distributor].
*
* BitMaps pack 256 booleans across each bit of a single 256-bit slot of `uint256` type.
* Hence booleans corresponding to 256 _sequential_ indices would only consume a single slot,
* unlike the regular `bool` which would consume an entire slot for a single value.
*
* This results in gas savings in two ways:
*
* - Setting a zero value to non-zero only once every 256 times
* - Accessing the same warm slot for every 256 _sequential_ indices
*/
library BitMaps {
struct BitMap {
mapping(uint256 bucket => uint256) _data;
}
/**
* @dev Returns whether the bit at `index` is set.
*/
function get(BitMap storage bitmap, uint256 index) internal view returns (bool) {
uint256 bucket = index >> 8;
uint256 mask = 1 << (index & 0xff);
return bitmap._data[bucket] & mask != 0;
}
/**
* @dev Sets the bit at `index` to the boolean `value`.
*/
function setTo(BitMap storage bitmap, uint256 index, bool value) internal {
if (value) {
set(bitmap, index);
} else {
unset(bitmap, index);
}
}
/**
* @dev Sets the bit at `index`.
*/
function set(BitMap storage bitmap, uint256 index) internal {
uint256 bucket = index >> 8;
uint256 mask = 1 << (index & 0xff);
bitmap._data[bucket] |= mask;
}
/**
* @dev Unsets the bit at `index`.
*/
function unset(BitMap storage bitmap, uint256 index) internal {
uint256 bucket = index >> 8;
uint256 mask = 1 << (index & 0xff);
bitmap._data[bucket] &= ~mask;
}
}// 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 { 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;
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 { IERC20 } from "@oz/token/ERC20/IERC20.sol";
import { Math } from "@oz/utils/math/Math.sol";
import { VaultAccruals, VaultPriceState } from "src/core/Types.sol";
/// @title IPriceAndFeeCalculator
/// @notice Interface for the unit price provider
interface IPriceAndFeeCalculator {
////////////////////////////////////////////////////////////
// Events //
////////////////////////////////////////////////////////////
/// @notice Emitted when thresholds are set for a vault
/// @param vault The address of the vault
/// @param minPriceToleranceRatio Minimum ratio (of a price decrease) in basis points
/// @param maxPriceToleranceRatio Maximum ratio (of a price increase) in basis points
/// @param minUpdateIntervalMinutes The minimum interval between updates in minutes
/// @param maxPriceAge Max delay between when a vault was priced and when the price is acceptable
event ThresholdsSet(
address indexed vault,
uint16 minPriceToleranceRatio,
uint16 maxPriceToleranceRatio,
uint16 minUpdateIntervalMinutes,
uint8 maxPriceAge
);
/// @notice Emitted when a vault's unit price is updated
/// @param vault The address of the vault
/// @param price The new unit price
/// @param timestamp The timestamp when the price was updated
event UnitPriceUpdated(address indexed vault, uint128 price, uint32 timestamp);
/// @notice Emitted when a vault's paused state is changed
/// @param vault The address of the vault
/// @param paused Whether the vault is paused
event VaultPausedChanged(address indexed vault, bool paused);
/// @notice Emitted when a vault's highest price is reset
/// @param vault The address of the vault
/// @param newHighestPrice The new highest price
event HighestPriceReset(address indexed vault, uint128 newHighestPrice);
////////////////////////////////////////////////////////////
// Errors //
////////////////////////////////////////////////////////////
error Aera__StalePrice();
error Aera__TimestampMustBeAfterLastUpdate();
error Aera__TimestampCantBeInFuture();
error Aera__ZeroAddressOracleRegistry();
error Aera__InvalidMaxPriceToleranceRatio();
error Aera__InvalidMinPriceToleranceRatio();
error Aera__InvalidMaxPriceAge();
error Aera__InvalidMaxUpdateDelayDays();
error Aera__ThresholdNotSet();
error Aera__VaultPaused();
error Aera__VaultNotPaused();
error Aera__UnitPriceMismatch();
error Aera__TimestampMismatch();
error Aera__VaultAlreadyInitialized();
error Aera__VaultNotInitialized();
error Aera__InvalidPrice();
error Aera__CurrentPriceAboveHighestPrice();
////////////////////////////////////////////////////////////
// Functions //
////////////////////////////////////////////////////////////
/// @notice Set the initial price state for the vault
/// @param vault Address of the vault
/// @param price New unit price
/// @param timestamp Timestamp when the price was measured
function setInitialPrice(address vault, uint128 price, uint32 timestamp) external;
/// @notice Set vault thresholds
/// @param vault Address of the vault
/// @param minPriceToleranceRatio Minimum ratio (of a price decrease) in basis points
/// @param maxPriceToleranceRatio Maximum ratio (of a price increase) in basis points
/// @param minUpdateIntervalMinutes The minimum interval between updates in minutes
/// @param maxPriceAge Max delay between when a vault was priced and when the price is acceptable
/// @param maxUpdateDelayDays Max delay between two price updates
function setThresholds(
address vault,
uint16 minPriceToleranceRatio,
uint16 maxPriceToleranceRatio,
uint16 minUpdateIntervalMinutes,
uint8 maxPriceAge,
uint8 maxUpdateDelayDays
) external;
/// @notice Set the unit price for the vault in numeraire terms
/// @param vault Address of the vault
/// @param price New unit price
/// @param timestamp Timestamp when the price was measured
function setUnitPrice(address vault, uint128 price, uint32 timestamp) external;
/// @notice Pause the vault
/// @param vault Address of the vault
function pauseVault(address vault) external;
/// @notice Unpause the vault
/// @param vault Address of the vault
/// @param price Expected price of the last update
/// @param timestamp Expected timestamp of the last update
/// @dev MUST revert if price or timestamp don't exactly match last update
function unpauseVault(address vault, uint128 price, uint32 timestamp) external;
/// @notice Resets the highest price for a vault to the current price
/// @param vault Address of the vault
function resetHighestPrice(address vault) external;
/// @notice Convert units to token amount
/// @param vault Address of the vault
/// @param token Address of the token
/// @param unitsAmount Amount of units
/// @return tokenAmount Amount of tokens
function convertUnitsToToken(address vault, IERC20 token, uint256 unitsAmount)
external
view
returns (uint256 tokenAmount);
/// @notice Convert units to token amount if vault is not paused
/// @param vault Address of the vault
/// @param token Address of the token
/// @param unitsAmount Amount of units
/// @param rounding The rounding mode
/// @return tokenAmount Amount of tokens
/// @dev MUST revert if vault is paused
function convertUnitsToTokenIfActive(address vault, IERC20 token, uint256 unitsAmount, Math.Rounding rounding)
external
view
returns (uint256 tokenAmount);
/// @notice Convert token amount to units
/// @param vault Address of the vault
/// @param token Address of the token
/// @param tokenAmount Amount of tokens
/// @return unitsAmount Amount of units
function convertTokenToUnits(address vault, IERC20 token, uint256 tokenAmount)
external
view
returns (uint256 unitsAmount);
/// @notice Convert token amount to units if vault is not paused
/// @param vault Address of the vault
/// @param token Address of the token
/// @param tokenAmount Amount of tokens
/// @param rounding The rounding mode
/// @return unitsAmount Amount of units
/// @dev MUST revert if vault is paused
function convertTokenToUnitsIfActive(address vault, IERC20 token, uint256 tokenAmount, Math.Rounding rounding)
external
view
returns (uint256 unitsAmount);
/// @notice Convert units to numeraire token amount
/// @param vault Address of the vault
/// @param unitsAmount Amount of units
/// @return numeraireAmount Amount of numeraire
function convertUnitsToNumeraire(address vault, uint256 unitsAmount)
external
view
returns (uint256 numeraireAmount);
/// @notice Return the state of the vault
/// @param vault Address of the vault
/// @return vaultPriceState The price state of the vault
/// @return vaultAccruals The accruals state of the vault
function getVaultState(address vault) external view returns (VaultPriceState memory, VaultAccruals memory);
/// @notice Returns the age of the last submitted price for a vault
/// @param vault Address of the vault
/// @return priceAge The difference between block.timestamp and vault's unit price timestamp
function getVaultsPriceAge(address vault) external view returns (uint256);
/// @notice Check if a vault is paused
/// @param vault The address of the vault
/// @return True if the vault is paused, false otherwise
function isVaultPaused(address vault) external view returns (bool);
}// 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) (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: 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
pragma solidity ^0.8.20;
/**
* @dev Helper library for emitting standardized panic codes.
*
* ```solidity
* contract Example {
* using Panic for uint256;
*
* // Use any of the declared internal constants
* function foo() { Panic.GENERIC.panic(); }
*
* // Alternatively
* function foo() { Panic.panic(Panic.GENERIC); }
* }
* ```
*
* Follows the list from https://github.com/ethereum/solidity/blob/v0.8.24/libsolutil/ErrorCodes.h[libsolutil].
*
* _Available since v5.1._
*/
// slither-disable-next-line unused-state
library Panic {
/// @dev generic / unspecified error
uint256 internal constant GENERIC = 0x00;
/// @dev used by the assert() builtin
uint256 internal constant ASSERT = 0x01;
/// @dev arithmetic underflow or overflow
uint256 internal constant UNDER_OVERFLOW = 0x11;
/// @dev division or modulo by zero
uint256 internal constant DIVISION_BY_ZERO = 0x12;
/// @dev enum conversion error
uint256 internal constant ENUM_CONVERSION_ERROR = 0x21;
/// @dev invalid encoding in storage
uint256 internal constant STORAGE_ENCODING_ERROR = 0x22;
/// @dev empty array pop
uint256 internal constant EMPTY_ARRAY_POP = 0x31;
/// @dev array out of bounds access
uint256 internal constant ARRAY_OUT_OF_BOUNDS = 0x32;
/// @dev resource error (too large allocation or too large array)
uint256 internal constant RESOURCE_ERROR = 0x41;
/// @dev calling invalid internal function
uint256 internal constant INVALID_INTERNAL_FUNCTION = 0x51;
/// @dev Reverts with a panic code. Recommended to use with
/// the internal constants with predefined codes.
function panic(uint256 code) internal pure {
assembly ("memory-safe") {
mstore(0x00, 0x4e487b71)
mstore(0x20, code)
revert(0x1c, 0x24)
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SafeCast.sol)
// This file was procedurally generated from scripts/generate/templates/SafeCast.js.
pragma solidity ^0.8.20;
/**
* @dev Wrappers over Solidity's uintXX/intXX/bool casting operators with added overflow
* checks.
*
* Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
* easily result in undesired exploitation or bugs, since developers usually
* assume that overflows raise errors. `SafeCast` restores this intuition by
* reverting the transaction when such an operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*/
library SafeCast {
/**
* @dev Value doesn't fit in an uint of `bits` size.
*/
error SafeCastOverflowedUintDowncast(uint8 bits, uint256 value);
/**
* @dev An int value doesn't fit in an uint of `bits` size.
*/
error SafeCastOverflowedIntToUint(int256 value);
/**
* @dev Value doesn't fit in an int of `bits` size.
*/
error SafeCastOverflowedIntDowncast(uint8 bits, int256 value);
/**
* @dev An uint value doesn't fit in an int of `bits` size.
*/
error SafeCastOverflowedUintToInt(uint256 value);
/**
* @dev Returns the downcasted uint248 from uint256, reverting on
* overflow (when the input is greater than largest uint248).
*
* Counterpart to Solidity's `uint248` operator.
*
* Requirements:
*
* - input must fit into 248 bits
*/
function toUint248(uint256 value) internal pure returns (uint248) {
if (value > type(uint248).max) {
revert SafeCastOverflowedUintDowncast(248, value);
}
return uint248(value);
}
/**
* @dev Returns the downcasted uint240 from uint256, reverting on
* overflow (when the input is greater than largest uint240).
*
* Counterpart to Solidity's `uint240` operator.
*
* Requirements:
*
* - input must fit into 240 bits
*/
function toUint240(uint256 value) internal pure returns (uint240) {
if (value > type(uint240).max) {
revert SafeCastOverflowedUintDowncast(240, value);
}
return uint240(value);
}
/**
* @dev Returns the downcasted uint232 from uint256, reverting on
* overflow (when the input is greater than largest uint232).
*
* Counterpart to Solidity's `uint232` operator.
*
* Requirements:
*
* - input must fit into 232 bits
*/
function toUint232(uint256 value) internal pure returns (uint232) {
if (value > type(uint232).max) {
revert SafeCastOverflowedUintDowncast(232, value);
}
return uint232(value);
}
/**
* @dev Returns the downcasted uint224 from uint256, reverting on
* overflow (when the input is greater than largest uint224).
*
* Counterpart to Solidity's `uint224` operator.
*
* Requirements:
*
* - input must fit into 224 bits
*/
function toUint224(uint256 value) internal pure returns (uint224) {
if (value > type(uint224).max) {
revert SafeCastOverflowedUintDowncast(224, value);
}
return uint224(value);
}
/**
* @dev Returns the downcasted uint216 from uint256, reverting on
* overflow (when the input is greater than largest uint216).
*
* Counterpart to Solidity's `uint216` operator.
*
* Requirements:
*
* - input must fit into 216 bits
*/
function toUint216(uint256 value) internal pure returns (uint216) {
if (value > type(uint216).max) {
revert SafeCastOverflowedUintDowncast(216, value);
}
return uint216(value);
}
/**
* @dev Returns the downcasted uint208 from uint256, reverting on
* overflow (when the input is greater than largest uint208).
*
* Counterpart to Solidity's `uint208` operator.
*
* Requirements:
*
* - input must fit into 208 bits
*/
function toUint208(uint256 value) internal pure returns (uint208) {
if (value > type(uint208).max) {
revert SafeCastOverflowedUintDowncast(208, value);
}
return uint208(value);
}
/**
* @dev Returns the downcasted uint200 from uint256, reverting on
* overflow (when the input is greater than largest uint200).
*
* Counterpart to Solidity's `uint200` operator.
*
* Requirements:
*
* - input must fit into 200 bits
*/
function toUint200(uint256 value) internal pure returns (uint200) {
if (value > type(uint200).max) {
revert SafeCastOverflowedUintDowncast(200, value);
}
return uint200(value);
}
/**
* @dev Returns the downcasted uint192 from uint256, reverting on
* overflow (when the input is greater than largest uint192).
*
* Counterpart to Solidity's `uint192` operator.
*
* Requirements:
*
* - input must fit into 192 bits
*/
function toUint192(uint256 value) internal pure returns (uint192) {
if (value > type(uint192).max) {
revert SafeCastOverflowedUintDowncast(192, value);
}
return uint192(value);
}
/**
* @dev Returns the downcasted uint184 from uint256, reverting on
* overflow (when the input is greater than largest uint184).
*
* Counterpart to Solidity's `uint184` operator.
*
* Requirements:
*
* - input must fit into 184 bits
*/
function toUint184(uint256 value) internal pure returns (uint184) {
if (value > type(uint184).max) {
revert SafeCastOverflowedUintDowncast(184, value);
}
return uint184(value);
}
/**
* @dev Returns the downcasted uint176 from uint256, reverting on
* overflow (when the input is greater than largest uint176).
*
* Counterpart to Solidity's `uint176` operator.
*
* Requirements:
*
* - input must fit into 176 bits
*/
function toUint176(uint256 value) internal pure returns (uint176) {
if (value > type(uint176).max) {
revert SafeCastOverflowedUintDowncast(176, value);
}
return uint176(value);
}
/**
* @dev Returns the downcasted uint168 from uint256, reverting on
* overflow (when the input is greater than largest uint168).
*
* Counterpart to Solidity's `uint168` operator.
*
* Requirements:
*
* - input must fit into 168 bits
*/
function toUint168(uint256 value) internal pure returns (uint168) {
if (value > type(uint168).max) {
revert SafeCastOverflowedUintDowncast(168, value);
}
return uint168(value);
}
/**
* @dev Returns the downcasted uint160 from uint256, reverting on
* overflow (when the input is greater than largest uint160).
*
* Counterpart to Solidity's `uint160` operator.
*
* Requirements:
*
* - input must fit into 160 bits
*/
function toUint160(uint256 value) internal pure returns (uint160) {
if (value > type(uint160).max) {
revert SafeCastOverflowedUintDowncast(160, value);
}
return uint160(value);
}
/**
* @dev Returns the downcasted uint152 from uint256, reverting on
* overflow (when the input is greater than largest uint152).
*
* Counterpart to Solidity's `uint152` operator.
*
* Requirements:
*
* - input must fit into 152 bits
*/
function toUint152(uint256 value) internal pure returns (uint152) {
if (value > type(uint152).max) {
revert SafeCastOverflowedUintDowncast(152, value);
}
return uint152(value);
}
/**
* @dev Returns the downcasted uint144 from uint256, reverting on
* overflow (when the input is greater than largest uint144).
*
* Counterpart to Solidity's `uint144` operator.
*
* Requirements:
*
* - input must fit into 144 bits
*/
function toUint144(uint256 value) internal pure returns (uint144) {
if (value > type(uint144).max) {
revert SafeCastOverflowedUintDowncast(144, value);
}
return uint144(value);
}
/**
* @dev Returns the downcasted uint136 from uint256, reverting on
* overflow (when the input is greater than largest uint136).
*
* Counterpart to Solidity's `uint136` operator.
*
* Requirements:
*
* - input must fit into 136 bits
*/
function toUint136(uint256 value) internal pure returns (uint136) {
if (value > type(uint136).max) {
revert SafeCastOverflowedUintDowncast(136, value);
}
return uint136(value);
}
/**
* @dev Returns the downcasted uint128 from uint256, reverting on
* overflow (when the input is greater than largest uint128).
*
* Counterpart to Solidity's `uint128` operator.
*
* Requirements:
*
* - input must fit into 128 bits
*/
function toUint128(uint256 value) internal pure returns (uint128) {
if (value > type(uint128).max) {
revert SafeCastOverflowedUintDowncast(128, value);
}
return uint128(value);
}
/**
* @dev Returns the downcasted uint120 from uint256, reverting on
* overflow (when the input is greater than largest uint120).
*
* Counterpart to Solidity's `uint120` operator.
*
* Requirements:
*
* - input must fit into 120 bits
*/
function toUint120(uint256 value) internal pure returns (uint120) {
if (value > type(uint120).max) {
revert SafeCastOverflowedUintDowncast(120, value);
}
return uint120(value);
}
/**
* @dev Returns the downcasted uint112 from uint256, reverting on
* overflow (when the input is greater than largest uint112).
*
* Counterpart to Solidity's `uint112` operator.
*
* Requirements:
*
* - input must fit into 112 bits
*/
function toUint112(uint256 value) internal pure returns (uint112) {
if (value > type(uint112).max) {
revert SafeCastOverflowedUintDowncast(112, value);
}
return uint112(value);
}
/**
* @dev Returns the downcasted uint104 from uint256, reverting on
* overflow (when the input is greater than largest uint104).
*
* Counterpart to Solidity's `uint104` operator.
*
* Requirements:
*
* - input must fit into 104 bits
*/
function toUint104(uint256 value) internal pure returns (uint104) {
if (value > type(uint104).max) {
revert SafeCastOverflowedUintDowncast(104, value);
}
return uint104(value);
}
/**
* @dev Returns the downcasted uint96 from uint256, reverting on
* overflow (when the input is greater than largest uint96).
*
* Counterpart to Solidity's `uint96` operator.
*
* Requirements:
*
* - input must fit into 96 bits
*/
function toUint96(uint256 value) internal pure returns (uint96) {
if (value > type(uint96).max) {
revert SafeCastOverflowedUintDowncast(96, value);
}
return uint96(value);
}
/**
* @dev Returns the downcasted uint88 from uint256, reverting on
* overflow (when the input is greater than largest uint88).
*
* Counterpart to Solidity's `uint88` operator.
*
* Requirements:
*
* - input must fit into 88 bits
*/
function toUint88(uint256 value) internal pure returns (uint88) {
if (value > type(uint88).max) {
revert SafeCastOverflowedUintDowncast(88, value);
}
return uint88(value);
}
/**
* @dev Returns the downcasted uint80 from uint256, reverting on
* overflow (when the input is greater than largest uint80).
*
* Counterpart to Solidity's `uint80` operator.
*
* Requirements:
*
* - input must fit into 80 bits
*/
function toUint80(uint256 value) internal pure returns (uint80) {
if (value > type(uint80).max) {
revert SafeCastOverflowedUintDowncast(80, value);
}
return uint80(value);
}
/**
* @dev Returns the downcasted uint72 from uint256, reverting on
* overflow (when the input is greater than largest uint72).
*
* Counterpart to Solidity's `uint72` operator.
*
* Requirements:
*
* - input must fit into 72 bits
*/
function toUint72(uint256 value) internal pure returns (uint72) {
if (value > type(uint72).max) {
revert SafeCastOverflowedUintDowncast(72, value);
}
return uint72(value);
}
/**
* @dev Returns the downcasted uint64 from uint256, reverting on
* overflow (when the input is greater than largest uint64).
*
* Counterpart to Solidity's `uint64` operator.
*
* Requirements:
*
* - input must fit into 64 bits
*/
function toUint64(uint256 value) internal pure returns (uint64) {
if (value > type(uint64).max) {
revert SafeCastOverflowedUintDowncast(64, value);
}
return uint64(value);
}
/**
* @dev Returns the downcasted uint56 from uint256, reverting on
* overflow (when the input is greater than largest uint56).
*
* Counterpart to Solidity's `uint56` operator.
*
* Requirements:
*
* - input must fit into 56 bits
*/
function toUint56(uint256 value) internal pure returns (uint56) {
if (value > type(uint56).max) {
revert SafeCastOverflowedUintDowncast(56, value);
}
return uint56(value);
}
/**
* @dev Returns the downcasted uint48 from uint256, reverting on
* overflow (when the input is greater than largest uint48).
*
* Counterpart to Solidity's `uint48` operator.
*
* Requirements:
*
* - input must fit into 48 bits
*/
function toUint48(uint256 value) internal pure returns (uint48) {
if (value > type(uint48).max) {
revert SafeCastOverflowedUintDowncast(48, value);
}
return uint48(value);
}
/**
* @dev Returns the downcasted uint40 from uint256, reverting on
* overflow (when the input is greater than largest uint40).
*
* Counterpart to Solidity's `uint40` operator.
*
* Requirements:
*
* - input must fit into 40 bits
*/
function toUint40(uint256 value) internal pure returns (uint40) {
if (value > type(uint40).max) {
revert SafeCastOverflowedUintDowncast(40, value);
}
return uint40(value);
}
/**
* @dev Returns the downcasted uint32 from uint256, reverting on
* overflow (when the input is greater than largest uint32).
*
* Counterpart to Solidity's `uint32` operator.
*
* Requirements:
*
* - input must fit into 32 bits
*/
function toUint32(uint256 value) internal pure returns (uint32) {
if (value > type(uint32).max) {
revert SafeCastOverflowedUintDowncast(32, value);
}
return uint32(value);
}
/**
* @dev Returns the downcasted uint24 from uint256, reverting on
* overflow (when the input is greater than largest uint24).
*
* Counterpart to Solidity's `uint24` operator.
*
* Requirements:
*
* - input must fit into 24 bits
*/
function toUint24(uint256 value) internal pure returns (uint24) {
if (value > type(uint24).max) {
revert SafeCastOverflowedUintDowncast(24, value);
}
return uint24(value);
}
/**
* @dev Returns the downcasted uint16 from uint256, reverting on
* overflow (when the input is greater than largest uint16).
*
* Counterpart to Solidity's `uint16` operator.
*
* Requirements:
*
* - input must fit into 16 bits
*/
function toUint16(uint256 value) internal pure returns (uint16) {
if (value > type(uint16).max) {
revert SafeCastOverflowedUintDowncast(16, value);
}
return uint16(value);
}
/**
* @dev Returns the downcasted uint8 from uint256, reverting on
* overflow (when the input is greater than largest uint8).
*
* Counterpart to Solidity's `uint8` operator.
*
* Requirements:
*
* - input must fit into 8 bits
*/
function toUint8(uint256 value) internal pure returns (uint8) {
if (value > type(uint8).max) {
revert SafeCastOverflowedUintDowncast(8, value);
}
return uint8(value);
}
/**
* @dev Converts a signed int256 into an unsigned uint256.
*
* Requirements:
*
* - input must be greater than or equal to 0.
*/
function toUint256(int256 value) internal pure returns (uint256) {
if (value < 0) {
revert SafeCastOverflowedIntToUint(value);
}
return uint256(value);
}
/**
* @dev Returns the downcasted int248 from int256, reverting on
* overflow (when the input is less than smallest int248 or
* greater than largest int248).
*
* Counterpart to Solidity's `int248` operator.
*
* Requirements:
*
* - input must fit into 248 bits
*/
function toInt248(int256 value) internal pure returns (int248 downcasted) {
downcasted = int248(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(248, value);
}
}
/**
* @dev Returns the downcasted int240 from int256, reverting on
* overflow (when the input is less than smallest int240 or
* greater than largest int240).
*
* Counterpart to Solidity's `int240` operator.
*
* Requirements:
*
* - input must fit into 240 bits
*/
function toInt240(int256 value) internal pure returns (int240 downcasted) {
downcasted = int240(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(240, value);
}
}
/**
* @dev Returns the downcasted int232 from int256, reverting on
* overflow (when the input is less than smallest int232 or
* greater than largest int232).
*
* Counterpart to Solidity's `int232` operator.
*
* Requirements:
*
* - input must fit into 232 bits
*/
function toInt232(int256 value) internal pure returns (int232 downcasted) {
downcasted = int232(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(232, value);
}
}
/**
* @dev Returns the downcasted int224 from int256, reverting on
* overflow (when the input is less than smallest int224 or
* greater than largest int224).
*
* Counterpart to Solidity's `int224` operator.
*
* Requirements:
*
* - input must fit into 224 bits
*/
function toInt224(int256 value) internal pure returns (int224 downcasted) {
downcasted = int224(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(224, value);
}
}
/**
* @dev Returns the downcasted int216 from int256, reverting on
* overflow (when the input is less than smallest int216 or
* greater than largest int216).
*
* Counterpart to Solidity's `int216` operator.
*
* Requirements:
*
* - input must fit into 216 bits
*/
function toInt216(int256 value) internal pure returns (int216 downcasted) {
downcasted = int216(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(216, value);
}
}
/**
* @dev Returns the downcasted int208 from int256, reverting on
* overflow (when the input is less than smallest int208 or
* greater than largest int208).
*
* Counterpart to Solidity's `int208` operator.
*
* Requirements:
*
* - input must fit into 208 bits
*/
function toInt208(int256 value) internal pure returns (int208 downcasted) {
downcasted = int208(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(208, value);
}
}
/**
* @dev Returns the downcasted int200 from int256, reverting on
* overflow (when the input is less than smallest int200 or
* greater than largest int200).
*
* Counterpart to Solidity's `int200` operator.
*
* Requirements:
*
* - input must fit into 200 bits
*/
function toInt200(int256 value) internal pure returns (int200 downcasted) {
downcasted = int200(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(200, value);
}
}
/**
* @dev Returns the downcasted int192 from int256, reverting on
* overflow (when the input is less than smallest int192 or
* greater than largest int192).
*
* Counterpart to Solidity's `int192` operator.
*
* Requirements:
*
* - input must fit into 192 bits
*/
function toInt192(int256 value) internal pure returns (int192 downcasted) {
downcasted = int192(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(192, value);
}
}
/**
* @dev Returns the downcasted int184 from int256, reverting on
* overflow (when the input is less than smallest int184 or
* greater than largest int184).
*
* Counterpart to Solidity's `int184` operator.
*
* Requirements:
*
* - input must fit into 184 bits
*/
function toInt184(int256 value) internal pure returns (int184 downcasted) {
downcasted = int184(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(184, value);
}
}
/**
* @dev Returns the downcasted int176 from int256, reverting on
* overflow (when the input is less than smallest int176 or
* greater than largest int176).
*
* Counterpart to Solidity's `int176` operator.
*
* Requirements:
*
* - input must fit into 176 bits
*/
function toInt176(int256 value) internal pure returns (int176 downcasted) {
downcasted = int176(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(176, value);
}
}
/**
* @dev Returns the downcasted int168 from int256, reverting on
* overflow (when the input is less than smallest int168 or
* greater than largest int168).
*
* Counterpart to Solidity's `int168` operator.
*
* Requirements:
*
* - input must fit into 168 bits
*/
function toInt168(int256 value) internal pure returns (int168 downcasted) {
downcasted = int168(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(168, value);
}
}
/**
* @dev Returns the downcasted int160 from int256, reverting on
* overflow (when the input is less than smallest int160 or
* greater than largest int160).
*
* Counterpart to Solidity's `int160` operator.
*
* Requirements:
*
* - input must fit into 160 bits
*/
function toInt160(int256 value) internal pure returns (int160 downcasted) {
downcasted = int160(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(160, value);
}
}
/**
* @dev Returns the downcasted int152 from int256, reverting on
* overflow (when the input is less than smallest int152 or
* greater than largest int152).
*
* Counterpart to Solidity's `int152` operator.
*
* Requirements:
*
* - input must fit into 152 bits
*/
function toInt152(int256 value) internal pure returns (int152 downcasted) {
downcasted = int152(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(152, value);
}
}
/**
* @dev Returns the downcasted int144 from int256, reverting on
* overflow (when the input is less than smallest int144 or
* greater than largest int144).
*
* Counterpart to Solidity's `int144` operator.
*
* Requirements:
*
* - input must fit into 144 bits
*/
function toInt144(int256 value) internal pure returns (int144 downcasted) {
downcasted = int144(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(144, value);
}
}
/**
* @dev Returns the downcasted int136 from int256, reverting on
* overflow (when the input is less than smallest int136 or
* greater than largest int136).
*
* Counterpart to Solidity's `int136` operator.
*
* Requirements:
*
* - input must fit into 136 bits
*/
function toInt136(int256 value) internal pure returns (int136 downcasted) {
downcasted = int136(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(136, value);
}
}
/**
* @dev Returns the downcasted int128 from int256, reverting on
* overflow (when the input is less than smallest int128 or
* greater than largest int128).
*
* Counterpart to Solidity's `int128` operator.
*
* Requirements:
*
* - input must fit into 128 bits
*/
function toInt128(int256 value) internal pure returns (int128 downcasted) {
downcasted = int128(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(128, value);
}
}
/**
* @dev Returns the downcasted int120 from int256, reverting on
* overflow (when the input is less than smallest int120 or
* greater than largest int120).
*
* Counterpart to Solidity's `int120` operator.
*
* Requirements:
*
* - input must fit into 120 bits
*/
function toInt120(int256 value) internal pure returns (int120 downcasted) {
downcasted = int120(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(120, value);
}
}
/**
* @dev Returns the downcasted int112 from int256, reverting on
* overflow (when the input is less than smallest int112 or
* greater than largest int112).
*
* Counterpart to Solidity's `int112` operator.
*
* Requirements:
*
* - input must fit into 112 bits
*/
function toInt112(int256 value) internal pure returns (int112 downcasted) {
downcasted = int112(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(112, value);
}
}
/**
* @dev Returns the downcasted int104 from int256, reverting on
* overflow (when the input is less than smallest int104 or
* greater than largest int104).
*
* Counterpart to Solidity's `int104` operator.
*
* Requirements:
*
* - input must fit into 104 bits
*/
function toInt104(int256 value) internal pure returns (int104 downcasted) {
downcasted = int104(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(104, value);
}
}
/**
* @dev Returns the downcasted int96 from int256, reverting on
* overflow (when the input is less than smallest int96 or
* greater than largest int96).
*
* Counterpart to Solidity's `int96` operator.
*
* Requirements:
*
* - input must fit into 96 bits
*/
function toInt96(int256 value) internal pure returns (int96 downcasted) {
downcasted = int96(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(96, value);
}
}
/**
* @dev Returns the downcasted int88 from int256, reverting on
* overflow (when the input is less than smallest int88 or
* greater than largest int88).
*
* Counterpart to Solidity's `int88` operator.
*
* Requirements:
*
* - input must fit into 88 bits
*/
function toInt88(int256 value) internal pure returns (int88 downcasted) {
downcasted = int88(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(88, value);
}
}
/**
* @dev Returns the downcasted int80 from int256, reverting on
* overflow (when the input is less than smallest int80 or
* greater than largest int80).
*
* Counterpart to Solidity's `int80` operator.
*
* Requirements:
*
* - input must fit into 80 bits
*/
function toInt80(int256 value) internal pure returns (int80 downcasted) {
downcasted = int80(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(80, value);
}
}
/**
* @dev Returns the downcasted int72 from int256, reverting on
* overflow (when the input is less than smallest int72 or
* greater than largest int72).
*
* Counterpart to Solidity's `int72` operator.
*
* Requirements:
*
* - input must fit into 72 bits
*/
function toInt72(int256 value) internal pure returns (int72 downcasted) {
downcasted = int72(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(72, value);
}
}
/**
* @dev Returns the downcasted int64 from int256, reverting on
* overflow (when the input is less than smallest int64 or
* greater than largest int64).
*
* Counterpart to Solidity's `int64` operator.
*
* Requirements:
*
* - input must fit into 64 bits
*/
function toInt64(int256 value) internal pure returns (int64 downcasted) {
downcasted = int64(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(64, value);
}
}
/**
* @dev Returns the downcasted int56 from int256, reverting on
* overflow (when the input is less than smallest int56 or
* greater than largest int56).
*
* Counterpart to Solidity's `int56` operator.
*
* Requirements:
*
* - input must fit into 56 bits
*/
function toInt56(int256 value) internal pure returns (int56 downcasted) {
downcasted = int56(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(56, value);
}
}
/**
* @dev Returns the downcasted int48 from int256, reverting on
* overflow (when the input is less than smallest int48 or
* greater than largest int48).
*
* Counterpart to Solidity's `int48` operator.
*
* Requirements:
*
* - input must fit into 48 bits
*/
function toInt48(int256 value) internal pure returns (int48 downcasted) {
downcasted = int48(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(48, value);
}
}
/**
* @dev Returns the downcasted int40 from int256, reverting on
* overflow (when the input is less than smallest int40 or
* greater than largest int40).
*
* Counterpart to Solidity's `int40` operator.
*
* Requirements:
*
* - input must fit into 40 bits
*/
function toInt40(int256 value) internal pure returns (int40 downcasted) {
downcasted = int40(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(40, value);
}
}
/**
* @dev Returns the downcasted int32 from int256, reverting on
* overflow (when the input is less than smallest int32 or
* greater than largest int32).
*
* Counterpart to Solidity's `int32` operator.
*
* Requirements:
*
* - input must fit into 32 bits
*/
function toInt32(int256 value) internal pure returns (int32 downcasted) {
downcasted = int32(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(32, value);
}
}
/**
* @dev Returns the downcasted int24 from int256, reverting on
* overflow (when the input is less than smallest int24 or
* greater than largest int24).
*
* Counterpart to Solidity's `int24` operator.
*
* Requirements:
*
* - input must fit into 24 bits
*/
function toInt24(int256 value) internal pure returns (int24 downcasted) {
downcasted = int24(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(24, value);
}
}
/**
* @dev Returns the downcasted int16 from int256, reverting on
* overflow (when the input is less than smallest int16 or
* greater than largest int16).
*
* Counterpart to Solidity's `int16` operator.
*
* Requirements:
*
* - input must fit into 16 bits
*/
function toInt16(int256 value) internal pure returns (int16 downcasted) {
downcasted = int16(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(16, value);
}
}
/**
* @dev Returns the downcasted int8 from int256, reverting on
* overflow (when the input is less than smallest int8 or
* greater than largest int8).
*
* Counterpart to Solidity's `int8` operator.
*
* Requirements:
*
* - input must fit into 8 bits
*/
function toInt8(int256 value) internal pure returns (int8 downcasted) {
downcasted = int8(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(8, value);
}
}
/**
* @dev Converts an unsigned uint256 into a signed int256.
*
* Requirements:
*
* - input must be less than or equal to maxInt256.
*/
function toInt256(uint256 value) internal pure returns (int256) {
// Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
if (value > uint256(type(int256).max)) {
revert SafeCastOverflowedUintToInt(value);
}
return int256(value);
}
/**
* @dev Cast a boolean (false or true) to a uint256 (0 or 1) with no jump.
*/
function toUint(bool b) internal pure returns (uint256 u) {
assembly ("memory-safe") {
u := iszero(iszero(b))
}
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.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;
}// 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: 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;
/// @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;
/// @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;
/// @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: 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: 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);
}{
"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":[{"internalType":"contract IPriceAndFeeCalculator","name":"priceAndFeeCalculator","type":"address"},{"internalType":"address","name":"multiDepositorVault","type":"address"},{"internalType":"address","name":"owner_","type":"address"},{"internalType":"contract Authority","name":"authority_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"Aera__AsyncDepositDisabled","type":"error"},{"inputs":[],"name":"Aera__AsyncRedeemDisabled","type":"error"},{"inputs":[],"name":"Aera__AutoPriceSolveNotAllowed","type":"error"},{"inputs":[],"name":"Aera__CallerIsVault","type":"error"},{"inputs":[],"name":"Aera__DeadlineInFutureAndUnauthorized","type":"error"},{"inputs":[],"name":"Aera__DeadlineInPast","type":"error"},{"inputs":[],"name":"Aera__DeadlineTooFarInFuture","type":"error"},{"inputs":[],"name":"Aera__DepositCapExceeded","type":"error"},{"inputs":[],"name":"Aera__DepositCapZero","type":"error"},{"inputs":[],"name":"Aera__DepositHashNotFound","type":"error"},{"inputs":[],"name":"Aera__DepositMultiplierTooHigh","type":"error"},{"inputs":[],"name":"Aera__DepositMultiplierTooLow","type":"error"},{"inputs":[],"name":"Aera__FixedPriceSolverTipNotAllowed","type":"error"},{"inputs":[],"name":"Aera__HashCollision","type":"error"},{"inputs":[],"name":"Aera__HashNotFound","type":"error"},{"inputs":[],"name":"Aera__InvalidToken","type":"error"},{"inputs":[],"name":"Aera__MaxDepositRefundTimeoutExceeded","type":"error"},{"inputs":[],"name":"Aera__MaxTokensInExceeded","type":"error"},{"inputs":[],"name":"Aera__MaxTokensInZero","type":"error"},{"inputs":[],"name":"Aera__MinTokenOutZero","type":"error"},{"inputs":[],"name":"Aera__MinUnitsOutNotMet","type":"error"},{"inputs":[],"name":"Aera__MinUnitsOutZero","type":"error"},{"inputs":[],"name":"Aera__PriceAndFeeCalculatorVaultPaused","type":"error"},{"inputs":[],"name":"Aera__RedeemMultiplierTooHigh","type":"error"},{"inputs":[],"name":"Aera__RedeemMultiplierTooLow","type":"error"},{"inputs":[],"name":"Aera__RefundPeriodExpired","type":"error"},{"inputs":[],"name":"Aera__SyncDepositDisabled","type":"error"},{"inputs":[],"name":"Aera__TokenCantBePriced","type":"error"},{"inputs":[],"name":"Aera__TokensInZero","type":"error"},{"inputs":[],"name":"Aera__Unauthorized","type":"error"},{"inputs":[],"name":"Aera__UnitsInZero","type":"error"},{"inputs":[],"name":"Aera__UnitsOutZero","type":"error"},{"inputs":[],"name":"Aera__ZeroAddressAuthority","type":"error"},{"inputs":[],"name":"Aera__ZeroAddressMultiDepositorVault","type":"error"},{"inputs":[],"name":"Aera__ZeroAddressPriceAndFeeCalculator","type":"error"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"index","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"bound","type":"uint256"}],"name":"AmountBoundExceeded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"index","type":"uint256"}],"name":"AsyncDepositDisabled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"index","type":"uint256"}],"name":"AsyncRedeemDisabled","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":"uint256","name":"index","type":"uint256"}],"name":"DepositCapExceeded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"depositCap","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"depositRefundTimeout","type":"uint256"}],"name":"DepositDetailsUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"depositHash","type":"bytes32"}],"name":"DepositRefunded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"contract IERC20","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokensIn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"minUnitsOut","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"solverTip","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"deadline","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"maxPriceAge","type":"uint256"},{"indexed":false,"internalType":"bool","name":"isFixedPrice","type":"bool"},{"indexed":false,"internalType":"bytes32","name":"depositRequestHash","type":"bytes32"}],"name":"DepositRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"depositHash","type":"bytes32"}],"name":"DepositSolved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"contract IERC20","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokensIn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"unitsOut","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"depositHash","type":"bytes32"}],"name":"Deposited","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"depositHash","type":"bytes32"}],"name":"DirectDepositRefunded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"index","type":"uint256"}],"name":"InsufficientTokensForTip","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"depositHash","type":"bytes32"}],"name":"InvalidRequestHash","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":true,"internalType":"uint256","name":"index","type":"uint256"}],"name":"PriceAgeExceeded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"redeemHash","type":"bytes32"}],"name":"RedeemRefunded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"contract IERC20","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"minTokensOut","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"unitsIn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"solverTip","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"deadline","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"maxPriceAge","type":"uint256"},{"indexed":false,"internalType":"bool","name":"isFixedPrice","type":"bool"},{"indexed":false,"internalType":"bytes32","name":"redeemRequestHash","type":"bytes32"}],"name":"RedeemRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"redeemHash","type":"bytes32"}],"name":"RedeemSolved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract IERC20","name":"token","type":"address"},{"components":[{"internalType":"bool","name":"asyncDepositEnabled","type":"bool"},{"internalType":"bool","name":"asyncRedeemEnabled","type":"bool"},{"internalType":"bool","name":"syncDepositEnabled","type":"bool"},{"internalType":"uint16","name":"depositMultiplier","type":"uint16"},{"internalType":"uint16","name":"redeemMultiplier","type":"uint16"}],"indexed":false,"internalType":"struct TokenDetails","name":"tokensDetails","type":"tuple"}],"name":"TokenDetailsSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract IERC20","name":"token","type":"address"}],"name":"TokenRemoved","type":"event"},{"inputs":[],"name":"MULTI_DEPOSITOR_VAULT","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PRICE_FEE_CALCULATOR","outputs":[{"internalType":"contract IPriceAndFeeCalculator","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"areUserUnitsLocked","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"asyncDepositHash","type":"bytes32"}],"name":"asyncDepositHashes","outputs":[{"internalType":"bool","name":"exists","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"asyncRedeemHash","type":"bytes32"}],"name":"asyncRedeemHashes","outputs":[{"internalType":"bool","name":"exists","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"authority","outputs":[{"internalType":"contract Authority","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint256","name":"tokensIn","type":"uint256"},{"internalType":"uint256","name":"minUnitsOut","type":"uint256"}],"name":"deposit","outputs":[{"internalType":"uint256","name":"unitsOut","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"depositCap","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"depositRefundTimeout","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint256","name":"tokenAmount","type":"uint256"},{"internalType":"uint256","name":"unitsAmount","type":"uint256"},{"internalType":"uint256","name":"refundableUntil","type":"uint256"}],"name":"getDepositHash","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"},{"components":[{"internalType":"enum RequestType","name":"requestType","type":"uint8"},{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"units","type":"uint256"},{"internalType":"uint256","name":"tokens","type":"uint256"},{"internalType":"uint256","name":"solverTip","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint256","name":"maxPriceAge","type":"uint256"}],"internalType":"struct Request","name":"request","type":"tuple"}],"name":"getRequestHash","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"maxDeposit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint256","name":"unitsOut","type":"uint256"},{"internalType":"uint256","name":"maxTokensIn","type":"uint256"}],"name":"mint","outputs":[{"internalType":"uint256","name":"tokensIn","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"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":"uint256","name":"refundableUntil","type":"uint256"}],"name":"refundDeposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"},{"components":[{"internalType":"enum RequestType","name":"requestType","type":"uint8"},{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"units","type":"uint256"},{"internalType":"uint256","name":"tokens","type":"uint256"},{"internalType":"uint256","name":"solverTip","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint256","name":"maxPriceAge","type":"uint256"}],"internalType":"struct Request","name":"request","type":"tuple"}],"name":"refundRequest","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"}],"name":"removeToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint256","name":"tokensIn","type":"uint256"},{"internalType":"uint256","name":"minUnitsOut","type":"uint256"},{"internalType":"uint256","name":"solverTip","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint256","name":"maxPriceAge","type":"uint256"},{"internalType":"bool","name":"isFixedPrice","type":"bool"}],"name":"requestDeposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint256","name":"unitsIn","type":"uint256"},{"internalType":"uint256","name":"minTokensOut","type":"uint256"},{"internalType":"uint256","name":"solverTip","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint256","name":"maxPriceAge","type":"uint256"},{"internalType":"bool","name":"isFixedPrice","type":"bool"}],"name":"requestRedeem","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract Authority","name":"newAuthority","type":"address"}],"name":"setAuthority","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"depositCap_","type":"uint256"},{"internalType":"uint256","name":"depositRefundTimeout_","type":"uint256"}],"name":"setDepositDetails","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"},{"components":[{"internalType":"bool","name":"asyncDepositEnabled","type":"bool"},{"internalType":"bool","name":"asyncRedeemEnabled","type":"bool"},{"internalType":"bool","name":"syncDepositEnabled","type":"bool"},{"internalType":"uint16","name":"depositMultiplier","type":"uint16"},{"internalType":"uint16","name":"redeemMultiplier","type":"uint16"}],"internalType":"struct TokenDetails","name":"details","type":"tuple"}],"name":"setTokenDetails","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"},{"components":[{"internalType":"enum RequestType","name":"requestType","type":"uint8"},{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"units","type":"uint256"},{"internalType":"uint256","name":"tokens","type":"uint256"},{"internalType":"uint256","name":"solverTip","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint256","name":"maxPriceAge","type":"uint256"}],"internalType":"struct Request[]","name":"requests","type":"tuple[]"}],"name":"solveRequestsDirect","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"},{"components":[{"internalType":"enum RequestType","name":"requestType","type":"uint8"},{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"units","type":"uint256"},{"internalType":"uint256","name":"tokens","type":"uint256"},{"internalType":"uint256","name":"solverTip","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint256","name":"maxPriceAge","type":"uint256"}],"internalType":"struct Request[]","name":"requests","type":"tuple[]"}],"name":"solveRequestsVault","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"syncDepositHash","type":"bytes32"}],"name":"syncDepositHashes","outputs":[{"internalType":"bool","name":"exists","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"}],"name":"tokensDetails","outputs":[{"internalType":"bool","name":"asyncDepositEnabled","type":"bool"},{"internalType":"bool","name":"asyncRedeemEnabled","type":"bool"},{"internalType":"bool","name":"syncDepositEnabled","type":"bool"},{"internalType":"uint16","name":"depositMultiplier","type":"uint16"},{"internalType":"uint16","name":"redeemMultiplier","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"userUnitsRefundableUntil","outputs":[{"internalType":"uint256","name":"unitsLockedUntil","type":"uint256"}],"stateMutability":"view","type":"function"}]Contract Creation Code
0x60c03461025157601f61465938819003918201601f19168301916001600160401b03831184841017610255578084926080946040528339810103126102515780516001600160a01b038116908181036102515761005e60208401610269565b91606061006d60408601610269565b9401516001600160a01b0381169490859003610251575f80546001600160a01b03199081166001600160a01b03939093169283178255600180549091168717905560405195919033907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09080a3337fa3396fd7f6e0a21b50e5089d2da70d5ac0a3bbbd1f617a93f134b763899801985f80a315610242576001600160a01b038216156102335760805260a0526143db908161027e8239608051818181610613015281816108f601528181610eea015281816114730152818161186801528181611d6b0152818161203601528181612f10015281816131cc0152818161404401526141e4015260a05181818161043d015281816105d1015281816108b401528181610a3001528181610acf01528181610c4a01528181610d0b015281816113b10152818161178b01528181611a5101528181611b3201528181611f77015281816121ec015281816123f30152818161266501528181612eb1015281816130330152818161315b01528181613422015281816135b6015281816136e0015281816138f801528181613b6201528181613d3201528181613e6301528181613f5301526141870152f35b630c2df1b760e31b5f5260045ffd5b6309427f1360e31b5f5260045ffd5b5f80fd5b634e487b7160e01b5f52604160045260245ffd5b51906001600160a01b03821682036102515756fe60806040526004361015610011575f80fd5b5f5f358060e01c9081630ab6f6c1146124855781630efe6a8b146123cd5781630f93397b146122ce578163156e29f6146121c557816321a3b5bd14611f4e57816323a79bd214611f3257816323d952ca14611ee55781635945aa1e14611a755781635acb46cf14611a075781635fa7b584146119565781636083e59a1461175b57816368fafe1314611720578163695dfe87146116d35781636be19ed3146113875781637035871c1461131257816370f07e431461127657816379ba5097146111a95781637a9e5e4b146110275781637d60449b14610fc45781638da5cb5b14610f745781639428b68e14610f0e57816397ac97db14610ea0578163a2cab0021461082c578163a912b86b1461057c578163b58c10c51461037e57508063bf7e214f1461032c578063dbd5edc7146102f0578063e30c39781461029e578063ecd253f2146102515763f2fde38b14610167575f80fd5b3461024e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261024e5761019e61270e565b73ffffffffffffffffffffffffffffffffffffffff825416908133036102265773ffffffffffffffffffffffffffffffffffffffff1690817fffffffffffffffffffffffff000000000000000000000000000000000000000060025416176002557f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e227008380a380f35b6004837f4bf4c244000000000000000000000000000000000000000000000000000000008152fd5b80fd5b503461024e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261024e5760ff60406020926004358152600884522054166040519015158152f35b503461024e57807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261024e57602073ffffffffffffffffffffffffffffffffffffffff60025416604051908152f35b503461024e57807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261024e576020600454604051908152f35b503461024e57807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261024e57602073ffffffffffffffffffffffffffffffffffffffff60015416604051908152f35b905034610528576103ca6103c57fffffffff00000000000000000000000000000000000000000000000000000000926103b6366127f4565b93969295919790941633612b46565b61297b565b428110610554576103de9082868587613308565b93845f52600660205260ff60405f2054161561052c57845f52600660205260405f207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00815416905573ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001690813b15610528576040517f7c554ed100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff958616600482018190529490951660248601526044850152606484019190915260848301919091525f90829060a490829084905af1801561051d57610508575b507f275beab13d802baff0e04dc25e12ee5c4aab679c3d713b00c9cf9bbaf3ed2b1c8280a280f35b6105159192505f906129e0565b5f905f6104e0565b6040513d5f823e3d90fd5b5f80fd5b7fc42fb23a000000000000000000000000000000000000000000000000000000005f5260045ffd5b7feec45d97000000000000000000000000000000000000000000000000000000005f5260045ffd5b346105285761058a3661286e565b9190610594612ad2565b6040517f8c436aea00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016600482015260208160248173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165afa90811561051d575f916107fd575b506107d55773ffffffffffffffffffffffffffffffffffffffff82165f52600360205260405f20915f5b848110610699575f7f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f005d005b6106a4818685612a88565b35600481101561052857806106ba600192612900565b1661075d5760ff84541615610735576106d4818685612a88565b35600481101561052857806106ea600292612900565b161561070d57806107076107016001938887612a88565b84613dd1565b0161066d565b7f5a0b2a90000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f996875dc000000000000000000000000000000000000000000000000000000005f5260045ffd5b60ff845460081c16156107ad57610775818685612a88565b356004811015610528578061078b600292612900565b161561070d57806107a86107a26001938887612a88565b84613caf565b610707565b7fd5bc5e6f000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f52646704000000000000000000000000000000000000000000000000000000005f5260045ffd5b61081f915060203d602011610825575b61081781836129e0565b810190612a21565b84610643565b503d61080d565b34610528576103c57fffffffff000000000000000000000000000000000000000000000000000000009161086e6108623661286e565b94919590931633612b46565b610876612ad2565b604051907f1a5f33c500000000000000000000000000000000000000000000000000000000825273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016600483015260208260248173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165afa91821561051d575f92610e6c575b505f9173ffffffffffffffffffffffffffffffffffffffff85165f52600360205260405f20906040519160a083019083821067ffffffffffffffff831117610e3f5761ffff9160409795949397525460ff81161515835260ff8160081c161515602084015260ff8160101c1615156040840152818160181c16606084015260281c1660808201525f945f935b808510610b5e5787878780610b4d575b506109ed575b5f7f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f005d005b60405160205f8183017f095ea7b300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016602485015281604485015260448452610a6a6064856129e0565b83519082865af15f513d82610b1b575b505015610a88575b506109c8565b610b1491610b0f6040517f095ea7b300000000000000000000000000000000000000000000000000000000602082015273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001660248201525f604482015260448152610b096064826129e0565b82613eca565b613eca565b8080610a82565b909150610b45575073ffffffffffffffffffffffffffffffffffffffff82163b15155b8380610a7a565b600114610b3e565b610b58903384612d92565b826109c2565b9091929394610b6e868387612a88565b6004813510158061052857610b838235612900565b8135600116610dba57855115610d8a578815610c01575b610528576001918482610bae8a9435612900565b8035600216610be25792610bd091610bd6948d61ffff60608c01511690613a7a565b90612ac5565b955b01939291906109b2565b92610bd091610bfb948d61ffff60608c01511690613827565b95610bd8565b975060019760405160205f8c8284017f095ea7b300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001660248601527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff604486015260448552610ca46064866129e0565b828551925af18a3d5f519083610d57575b50505015610cc4575b50610b9a565b610d5190610d4b6040517f095ea7b300000000000000000000000000000000000000000000000000000000602082015273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001660248201525f604482015260448152610d456064826129e0565b8d613eca565b8b613eca565b8a610cbe565b91925090610d8457505073ffffffffffffffffffffffffffffffffffffffff8b163b15155b8a8d80610cb5565b14610d7c565b505094806001917fd3c57cf980d98c7c1f4388b13364aab598e7f99287f61035c5672dc36d9f33e55f80a2610bd8565b50602085015115610e1057600191848892610dd58135612900565b8035600216610df75792610bd091610bfb948d61ffff60808c0151169061360b565b92610bd091610bfb948d61ffff60808c01511690613365565b5094806001917fdb285f4e589a584bb6253e7d2c91592c7706c14408177686ebfa13a1d3c25f5b5f80a2610bd8565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b9091506020813d602011610e98575b81610e88602093836129e0565b8101031261052857519084610926565b3d9150610e7b565b34610528575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261052857602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346105285760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105285773ffffffffffffffffffffffffffffffffffffffff610f5a61270e565b165f526009602052602060405f2054604051904211158152f35b34610528575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261052857602073ffffffffffffffffffffffffffffffffffffffff5f5416604051908152f35b346105285760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105285773ffffffffffffffffffffffffffffffffffffffff61101061270e565b165f526009602052602060405f2054604051908152f35b346105285760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610528576004359073ffffffffffffffffffffffffffffffffffffffff82168092036105285773ffffffffffffffffffffffffffffffffffffffff5f541633149081156110f1575b501561052857807fffffffffffffffffffffffff00000000000000000000000000000000000000006001541617600155337fa3396fd7f6e0a21b50e5089d2da70d5ac0a3bbbd1f617a93f134b763899801985f80a3005b6001546040517fb70096130000000000000000000000000000000000000000000000000000000081523360048201523060248201527fffffffff000000000000000000000000000000000000000000000000000000009290921660448301529091506020908290606490829073ffffffffffffffffffffffffffffffffffffffff165afa90811561051d575f9161118a575b508261109a565b6111a3915060203d6020116108255761081781836129e0565b82611183565b34610528575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105285760025473ffffffffffffffffffffffffffffffffffffffff81169081330361124e577fffffffffffffffffffffffff00000000000000000000000000000000000000009082825f5416175f5516600255337f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3005b7f4bf4c244000000000000000000000000000000000000000000000000000000005f5260045ffd5b346105285760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105285773ffffffffffffffffffffffffffffffffffffffff6112c261270e565b165f52600360205260a060405f205461ffff6040519160ff81161515835260ff8160081c161515602084015260ff8160101c1615156040840152818160181c16606084015260281c166080820152f35b34610528576101007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105285761134a61270e565b60e07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc3601126105285761137f602091612cb9565b604051908152f35b346105285761139536612786565b73ffffffffffffffffffffffffffffffffffffffff95929194957f000000000000000000000000000000000000000000000000000000000000000016968733146116ab57831561168357861561165b5742831115611633576301e133804284031161160b5773ffffffffffffffffffffffffffffffffffffffff811697885f52600360205260ff60405f205460081c16156107ad576040517f8c436aea00000000000000000000000000000000000000000000000000000000815281600482015260208160248173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165afa90811561051d575f916115ec575b506107d557851580156115e4575b156115bc578386868a6114de956114cd838d9730903390613282565b87156115b4576003905b33906132e9565b90815f52600860205260ff60405f20541661158c577fa486f7963c056b0ba4a04193f0b389be61cb325f0bc225780c662436c3930bb29561158793835f52600860205260405f2060017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00825416179055604051968796339a8893909796959260c0959260e086019986526020860152604085015260608401526080830152151560a08201520152565b0390a3005b7f33b5de71000000000000000000000000000000000000000000000000000000005f5260045ffd5b6001906114d7565b7fd8315b7e000000000000000000000000000000000000000000000000000000005f5260045ffd5b5082156114b1565b611605915060203d6020116108255761081781836129e0565b8a6114a3565b7f74f1eed3000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f30e98a30000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f7fcc5792000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f5df69010000000000000000000000000000000000000000000000000000000005f5260045ffd5b7fec0982e5000000000000000000000000000000000000000000000000000000005f5260045ffd5b346105285760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610528576004355f526007602052602060ff60405f2054166040519015158152f35b34610528575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610528576020600554604051908152f35b34610528575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610528577f00000000000000000000000000000000000000000000000000000000000000006040517f18160ddd00000000000000000000000000000000000000000000000000000000815260208160048173ffffffffffffffffffffffffffffffffffffffff86165afa90811561051d575f91611922575b506040517ffb34cbea00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9092166004830152602482015260208180604481015b038173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165afa90811561051d575f916118f0575b5060045490818110156118e65781039081116118b957602090604051908152f35b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b505060205f61137f565b90506020813d60201161191a575b8161190b602093836129e0565b81010312610528575181611898565b3d91506118fe565b90506020813d60201161194e575b8161193d602093836129e0565b81010312610528575161184f6117fd565b3d9150611930565b346105285760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105285773ffffffffffffffffffffffffffffffffffffffff906119d26103c57fffffffff000000000000000000000000000000000000000000000000000000006119ca61270e565b931633612b46565b16805f5260036020525f60408120557f4c910b69fe65a61f7531b9c5042b2329ca7179c77290aa7e2eb3afa3c8511fd35f80a2005b34610528575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261052857602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346105285760c07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261052857611aac61270e565b60a07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc36011261052857611b1a6103c57fffffffff0000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff941633612b46565b1673ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016808214611ebd5761ffff611b64612a39565b166113888110611e955761271010611e6d5761ffff611b81612a4a565b166113888110611e455761271010611e1d57815f52600360205260405f20611ba7612a5b565b151560ff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff008354169116178155611bdc612a6a565b1515815462ff0000611bec612a79565b151560101b167fffffffffffffffffffffffffffffffffffffffffffffffffff0000ffffffffff64ffff000000611c21612a39565b60181b16927fffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000ff61ff0066ffff0000000000611c5a612a4a565b60281b169660081b16911617161717179055611c74612a6a565b8015611e0f575b8015611e01575b611d0e575b5060405160243580151580910361052857815260443580151580910361052857602082015260643580151580910361052857604082015260843561ffff811680910361052857606082015260a43561ffff8116809103610528578160a09160807fdda77d5657df7cbe071c60ee7ff489407489b74a45333e789b05ba1fc82305dd940152a2005b604051907f4d4405620000000000000000000000000000000000000000000000000000000082526004820152816024820152670de0b6b3a7640000604482015260208160648173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165afa90811561051d575f91611dcf575b5015611da75781611c87565b7f35c2785e000000000000000000000000000000000000000000000000000000005f5260045ffd5b90506020813d602011611df9575b81611dea602093836129e0565b81010312610528575182611d9b565b3d9150611ddd565b50611e0a612a79565b611c82565b50611e18612a5b565b611c7b565b7f894fe36f000000000000000000000000000000000000000000000000000000005f5260045ffd5b7fc1cb2896000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f41d1d7cc000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f7830ab88000000000000000000000000000000000000000000000000000000005f5260045ffd5b7fe0c819cf000000000000000000000000000000000000000000000000000000005f5260045ffd5b346105285760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610528576004355f526006602052602060ff60405f2054166040519015158152f35b3461052857602061137f611f45366127f4565b93929092613308565b3461052857611f5c36612786565b73ffffffffffffffffffffffffffffffffffffffff949291947f000000000000000000000000000000000000000000000000000000000000000016968733146116ab57861561219d5783156121755742831115611633576301e133804284031161160b5773ffffffffffffffffffffffffffffffffffffffff811697885f52600360205260ff60405f2054161561073557604051907f8c436aea000000000000000000000000000000000000000000000000000000008252600482015260208160248173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165afa90811561051d575f91612156575b506107d5578415801561214e575b156115bc57858386868a61209e9561208e82303384613282565b87156121475760029033906132e9565b90815f52600760205260ff60405f20541661158c577f4d135ad145e872e343ba6681e2bf31b89f0ddc75aea8d31b98470f57ad6811cb9561158793835f52600760205260405f2060017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00825416179055604051968796339a8893909796959260c0959260e086019986526020860152604085015260608401526080830152151560a08201520152565b5f906114d7565b508115612074565b61216f915060203d6020116108255761081781836129e0565b89612066565b7f339bbcfd000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f7f3cecfb000000000000000000000000000000000000000000000000000000005f5260045ffd5b34610528576121d336612731565b909173ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001633146116ab5782156122a657811561227e5761224261ffff61222d83612df6565b61223686612f72565b5460181c168483613133565b918211612256576020928261137f92612fa9565b7fecf3d13f000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f6fce1504000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f1ee4dae8000000000000000000000000000000000000000000000000000000005f5260045ffd5b346105285760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261052857600435906123336103c57fffffffff00000000000000000000000000000000000000000000000000000000602435931633612b46565b81156123a55762278d00811161237d57816040917f5439ffe1dfe7a42332f002322d90e28ecc356c2830746daf8e001f5ef8576d67936004558060055582519182526020820152a1005b7fa6de76a1000000000000000000000000000000000000000000000000000000005f5260045ffd5b7fdc89b15a000000000000000000000000000000000000000000000000000000005f5260045ffd5b34610528576123db36612731565b9073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001633146116ab57801561219d5781156121755761244061ffff61243485612df6565b5460181c168285612e62565b91821061245d578161137f9160209461245883612f72565b612fa9565b7f72885d5b000000000000000000000000000000000000000000000000000000005f5260045ffd5b34610528576101007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610528576124bd61270e565b9060e07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc360112610528576124f0612ad2565b4260c435109081156126db575b50156126b35761250c81612cb9565b9060243560048110156105285780612525600192612900565b166125f757815f52600760205260ff60405f205416156125cf5761258690825f52600760205260405f207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00815416905561257d612937565b60843591612d92565b7f78cf1dc9a00a86b0daa3298b9de02c1418bf08316a8ee556c856889a41ab1e045f80a25f7f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f005d005b7f55dafa38000000000000000000000000000000000000000000000000000000005f5260045ffd5b50805f52600860205260ff60405f205416156125cf57805f52600860205260405f207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00815416905561268a61264a612937565b6064359073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016612d92565b7f795956e8a7396ce6380ad93558d36195a0038ddf1d1cafa320a5fc8fae94fdc45f80a26109c8565b7fbca6bcc5000000000000000000000000000000000000000000000000000000005f5260045ffd5b61270891507fffffffff000000000000000000000000000000000000000000000000000000001633612b46565b826124fd565b6004359073ffffffffffffffffffffffffffffffffffffffff8216820361052857565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60609101126105285760043573ffffffffffffffffffffffffffffffffffffffff8116810361052857906024359060443590565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60e09101126105285760043573ffffffffffffffffffffffffffffffffffffffff8116810361052857906024359060443590606435906084359060a4359060c43580151581036105285790565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60a09101126105285760043573ffffffffffffffffffffffffffffffffffffffff81168103610528579060243573ffffffffffffffffffffffffffffffffffffffff811681036105285790604435906064359060843590565b60407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc8201126105285760043573ffffffffffffffffffffffffffffffffffffffff81168103610528579160243567ffffffffffffffff811161052857826023820112156105285780600401359267ffffffffffffffff841161052857602460e0850283010111610528576024019190565b6004111561290a57565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b60443573ffffffffffffffffffffffffffffffffffffffff811681036105285790565b3573ffffffffffffffffffffffffffffffffffffffff811681036105285790565b1561298257565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a454400000000000000000000000000000000000000006044820152fd5b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff821117610e3f57604052565b90816020910312610528575180151581036105285790565b60843561ffff811681036105285790565b60a43561ffff811681036105285790565b60243580151581036105285790565b60443580151581036105285790565b60643580151581036105285790565b9190811015612a985760e0020190565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b919082018092116118b957565b7f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f005c612b1e5760017f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f005d565b7f3ee5aeb5000000000000000000000000000000000000000000000000000000005f5260045ffd5b73ffffffffffffffffffffffffffffffffffffffff60015416918215159283612b97575b50508115612b76575090565b905073ffffffffffffffffffffffffffffffffffffffff805f541691161490565b6040517fb700961300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff841660048201523060248201527fffffffff0000000000000000000000000000000000000000000000000000000092909216604483015291925090602090829060649082905afa90811561051d575f91612c30575b50905f80612b6a565b612c49915060203d6020116108255761081781836129e0565b5f612c27565b9694929060c9989694927fffffffffffffffffffffffffffffffffffffffff000000000000000000000000809260601b16895260601b166014880152612c9481612900565b60f81b6028870152602986015260498501526069840152608983015260a98201520190565b60443573ffffffffffffffffffffffffffffffffffffffff811681036105285760243591600483101561052857612d3e90612d12604051938492602084019660e4359260c4359260a4359260643592608435928c612c4f565b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081018352826129e0565b51902090565b612d506020830161295a565b90823591600483101561052857612d12612d3e92604051948593602085019760c08101359360a08201359360808301359360606040850135940135928c612c4f565b6040517fa9059cbb00000000000000000000000000000000000000000000000000000000602082015273ffffffffffffffffffffffffffffffffffffffff929092166024830152604480830193909352918152612df491610b0f6064836129e0565b565b73ffffffffffffffffffffffffffffffffffffffff165f52600360205260405f209060ff825460101c1615612e2757565b7f08f0878e000000000000000000000000000000000000000000000000000000005f5260045ffd5b818102929181159184041417156118b957565b612710612e74612ef794602094612e4f565b6040517f76bf1fbd00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000008116600483015290931660248401520460448201525f606482015291829081906084820190565b038173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165afa90811561051d575f91612f43575090565b90506020813d602011612f6a575b81612f5e602093836129e0565b81010312610528575190565b3d9150612f51565b612f7b90613f51565b612f8157565b7fbcffdc15000000000000000000000000000000000000000000000000000000005f5260045ffd5b919091612fb860055442612ac5565b612fc58184868533613308565b90815f52600660205260ff60405f20541661158c57815f52600660205260405f2060017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00825416179055335f52600960205260405f205573ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001692833b15610528576040517fbca563df000000000000000000000000000000000000000000000000000000008152336004820181905273ffffffffffffffffffffffffffffffffffffffff8516602483015260448201879052606482018390526084820152935f90859060a490829084905af193841561051d5773ffffffffffffffffffffffffffffffffffffffff94613123575b506040519485526020850152604084015216907fc6daf3f44b892b946e8f054dee61a5c544673a355b8ec6fd0ba713e6e67518db60603392a3565b5f61312d916129e0565b5f6130e8565b6040517fbd01e0100000000000000000000000000000000000000000000000000000000081527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff908116600483015290911660248201526044810191909152906001606483015260208260848173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165afa91821561051d575f9261324e575b50613207818361430c565b9080156132215761271061321e9309151590612ac5565b90565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b9091506020813d60201161327a575b8161326a602093836129e0565b810103126105285751905f6131fc565b3d915061325d565b90919273ffffffffffffffffffffffffffffffffffffffff612df49481604051957f23b872dd000000000000000000000000000000000000000000000000000000006020880152166024860152166044840152606483015260648252610b0f6084836129e0565b969195909394612d3e95612d1294604051988997602089019b8c612c4f565b93919290927fffffffffffffffffffffffffffffffffffffffff0000000000000000000000006040519481602087019760601b16875260601b16603485015260488401526068830152608882015260888152612d3e60a8826129e0565b949391945f9561337c845f9660c0840135906140e4565b6136015761338a8183612d44565b94855f5260086020526133a48660ff60405f205416614117565b613546574260a08301351061355157506133c4604082013593848461414a565b926133d5606083013595868661425a565b61354657855f52600860205260405f207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00815416905573ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016803b15610528576040517f7c554ed1000000000000000000000000000000000000000000000000000000008152306004820181905273ffffffffffffffffffffffffffffffffffffffff8616602483015260448201879052606482019390935260848101929092525f90829060a490829084905af1801561051d57613503575b50837fb16893cab9262f98d90b32ac8419c725af23f99658c1a389399d387cfea405229493926134f760206134fd940161295a565b90612d92565b039380a2565b6134fd919750938093926135385f7fb16893cab9262f98d90b32ac8419c725af23f99658c1a389399d387cfea40522976129e0565b5f98949550929391506134c2565b505f96505050505050565b96509150506135db9150825f52600860205260405f207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff008154169055604061359b6020830161295a565b9101359073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016612d92565b7f795956e8a7396ce6380ad93558d36195a0038ddf1d1cafa320a5fc8fae94fdc45f80a2565b505f955050505050565b94939190915f90613623815f9660c0860135906140e4565b613601576136318388612d44565b94855f52600860205261364b8660ff60405f205416614117565b613546574260a0850135106137de5750608083013596613671604085013595868361414a565b9461367d838a8861429e565b6137d2576136938987039360608701358561425a565b6137d257865f52600860205260405f207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00815416905573ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001695863b15610528576040517f7c554ed1000000000000000000000000000000000000000000000000000000008152306004820181905273ffffffffffffffffffffffffffffffffffffffff85166024830152604482019290925260648101929092526084820152945f90869060a490829084905af194851561051d577fb16893cab9262f98d90b32ac8419c725af23f99658c1a389399d387cfea40522956137b8575b506134f760206137b494950161295a565b80a2565b6137b493505f6137c7916129e0565b5f92506134f76137a3565b505f9750505050505050565b965050506135db9150825f52600860205260405f207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff008154169055604061359b6020830161295a565b949390915f9561383e835f9660c0850135906140e4565b6136015761384c8282612d44565b94855f5260076020526138668660ff60405f205416614117565b613546574260a084013510613a0257506138866040830135948583613133565b9360608301359361389881878761425a565b6137d2576138a690826142d1565b61354657855f52600760205260405f207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00815416905561391f602073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016940161295a565b92803b15610528576040517fbca563df00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff9384166024820152604481018790526064810192909252929091166084820152905f90829060a490829084905af1801561051d576139ca575b5003927fb638f61d6842803a2d7124f48035f62b92888a3a9eaf14dd1b0d62ee5974218e9080a2565b7fb638f61d6842803a2d7124f48035f62b92888a3a9eaf14dd1b0d62ee5974218e929195505f6139f9916129e0565b5f9490916139a1565b96509150613a549250835f52600760205260405f207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0081541690556060613a4b6020830161295a565b91013591612d92565b7f78cf1dc9a00a86b0daa3298b9de02c1418bf08316a8ee556c856889a41ab1e045f80a2565b94939091945f91613a92815f9660c08a0135906140e4565b61360157613aa08783612d44565b94855f526007602052613aba8660ff60405f205416614117565b613546574260a089013510613c6557506080870135966060810135613ae0838a8361429e565b6137d25788613af29103958685612e62565b91613b028160408401358561425a565b6137d257613b1090836142d1565b61354657855f52600760205260405f207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff008154169055613b89602073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016920161295a565b94813b15610528576040517fbca563df00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff948516602482015260448101919091526064810192909252939091166084820152915f90839060a490829084905af1801561051d57613c33575b7fb638f61d6842803a2d7124f48035f62b92888a3a9eaf14dd1b0d62ee5974218e915080a2565b505f613c3e916129e0565b7fb638f61d6842803a2d7124f48035f62b92888a3a9eaf14dd1b0d62ee5974218e5f613c0c565b9691925050613a549250835f52600760205260405f207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0081541690556060613a4b6020830161295a565b90613cba8183612d44565b91825f52600860205260ff60405f20541615613da9575f83815260086020526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001690554260a083013510613d965781613d576040613d709401353373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016612d92565b6060613d656020830161295a565b910135913390613282565b7fb16893cab9262f98d90b32ac8419c725af23f99658c1a389399d387cfea405225f80a2565b5080604061359b60206135db940161295a565b50507fa537f7582610185804288fbfe476e8ca59ef7c01be0690c6c7ee685cc4c9a9d95f80a2565b90613ddc8183612d44565b91825f52600760205260ff60405f20541615613da9575f83815260076020526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001690554260a083013510613eb857606082613e88613e456020613e92960161295a565b6040830135903373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016613282565b0135903390612d92565b7fb638f61d6842803a2d7124f48035f62b92888a3a9eaf14dd1b0d62ee5974218e5f80a2565b816060613a4b6020613a54950161295a565b905f602091828151910182855af11561051d575f513d613f48575073ffffffffffffffffffffffffffffffffffffffff81163b155b613f065750565b73ffffffffffffffffffffffffffffffffffffffff907f5274afe7000000000000000000000000000000000000000000000000000000005f521660045260245ffd5b60011415613eff565b7f0000000000000000000000000000000000000000000000000000000000000000906040517f18160ddd00000000000000000000000000000000000000000000000000000000815260208160048173ffffffffffffffffffffffffffffffffffffffff87165afa90811561051d575f916140ae575b5091613fd760209261402b94612ac5565b6040517ffb34cbea00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9092166004830152602482015291829081906044820190565b038173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165afa90811561051d575f9161407c575b506004541090565b90506020813d6020116140a6575b81614097602093836129e0565b8101031261052857515f614074565b3d915061408a565b9290506020833d6020116140dc575b816140ca602093836129e0565b81010312610528579151613fd7613fc6565b3d91506140bd565b116140ee57505f90565b7f70e5c3c8df8bd4682c58f6a384763684c37dfd6e3e9e82830975e046ec767f285f80a2600190565b1561412157505f90565b7fa537f7582610185804288fbfe476e8ca59ef7c01be0690c6c7ee685cc4c9a9d95f80a2600190565b6040517fbd01e01000000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000081166004830152909116602482015260448101919091525f60648201526020818060848101038173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165afa90811561051d575f91614226575b506127109161422291612e4f565b0490565b90506020813d602011614252575b81614241602093836129e0565b810103126105285751612710614214565b3d9150614234565b90808210614269575050505f90565b7f9bc04c9d3d2ad9d5e7a2d984cd41a3f16bbe32f66f2df5a35e2a1a79ae07c4b59160409182519182526020820152a2600190565b106142a857505f90565b7feaae26b2662e40917d5dc4a575e6887052a2522306e1b8547cbf2f625cbea5e55f80a2600190565b6142da90613f51565b6142e357505f90565b7f56848b1c5996accde9afa26c35895029f9f369205f496c09dd75357c2549d6f25f80a2600190565b906127108202907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6127108409928280851094039380850394146143c157838211156143a957612710829109815f0382168092046002816003021880820260020302808202600203028082026002030280820260020302808202600203028091026002030293600183805f03040190848311900302920304170290565b50634e487b715f52156003026011186020526024601cfd5b509150811561322157049056fea164736f6c634300081d000a000000000000000000000000811c6f0ef2e8f8a409306dae242ba70bd4f2467d000000000000000000000000eff0ae5b39271b33f448cd408b51dc8aa72a672b000000000000000000000000abbc2afd31f5aafefa4849309c49dd215793f692000000000000000000000000b74ea7210b8ddcd48b87b0c5af3e125754f8b6d9
Deployed Bytecode
0x60806040526004361015610011575f80fd5b5f5f358060e01c9081630ab6f6c1146124855781630efe6a8b146123cd5781630f93397b146122ce578163156e29f6146121c557816321a3b5bd14611f4e57816323a79bd214611f3257816323d952ca14611ee55781635945aa1e14611a755781635acb46cf14611a075781635fa7b584146119565781636083e59a1461175b57816368fafe1314611720578163695dfe87146116d35781636be19ed3146113875781637035871c1461131257816370f07e431461127657816379ba5097146111a95781637a9e5e4b146110275781637d60449b14610fc45781638da5cb5b14610f745781639428b68e14610f0e57816397ac97db14610ea0578163a2cab0021461082c578163a912b86b1461057c578163b58c10c51461037e57508063bf7e214f1461032c578063dbd5edc7146102f0578063e30c39781461029e578063ecd253f2146102515763f2fde38b14610167575f80fd5b3461024e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261024e5761019e61270e565b73ffffffffffffffffffffffffffffffffffffffff825416908133036102265773ffffffffffffffffffffffffffffffffffffffff1690817fffffffffffffffffffffffff000000000000000000000000000000000000000060025416176002557f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e227008380a380f35b6004837f4bf4c244000000000000000000000000000000000000000000000000000000008152fd5b80fd5b503461024e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261024e5760ff60406020926004358152600884522054166040519015158152f35b503461024e57807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261024e57602073ffffffffffffffffffffffffffffffffffffffff60025416604051908152f35b503461024e57807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261024e576020600454604051908152f35b503461024e57807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261024e57602073ffffffffffffffffffffffffffffffffffffffff60015416604051908152f35b905034610528576103ca6103c57fffffffff00000000000000000000000000000000000000000000000000000000926103b6366127f4565b93969295919790941633612b46565b61297b565b428110610554576103de9082868587613308565b93845f52600660205260ff60405f2054161561052c57845f52600660205260405f207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00815416905573ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000eff0ae5b39271b33f448cd408b51dc8aa72a672b1690813b15610528576040517f7c554ed100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff958616600482018190529490951660248601526044850152606484019190915260848301919091525f90829060a490829084905af1801561051d57610508575b507f275beab13d802baff0e04dc25e12ee5c4aab679c3d713b00c9cf9bbaf3ed2b1c8280a280f35b6105159192505f906129e0565b5f905f6104e0565b6040513d5f823e3d90fd5b5f80fd5b7fc42fb23a000000000000000000000000000000000000000000000000000000005f5260045ffd5b7feec45d97000000000000000000000000000000000000000000000000000000005f5260045ffd5b346105285761058a3661286e565b9190610594612ad2565b6040517f8c436aea00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000eff0ae5b39271b33f448cd408b51dc8aa72a672b16600482015260208160248173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000811c6f0ef2e8f8a409306dae242ba70bd4f2467d165afa90811561051d575f916107fd575b506107d55773ffffffffffffffffffffffffffffffffffffffff82165f52600360205260405f20915f5b848110610699575f7f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f005d005b6106a4818685612a88565b35600481101561052857806106ba600192612900565b1661075d5760ff84541615610735576106d4818685612a88565b35600481101561052857806106ea600292612900565b161561070d57806107076107016001938887612a88565b84613dd1565b0161066d565b7f5a0b2a90000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f996875dc000000000000000000000000000000000000000000000000000000005f5260045ffd5b60ff845460081c16156107ad57610775818685612a88565b356004811015610528578061078b600292612900565b161561070d57806107a86107a26001938887612a88565b84613caf565b610707565b7fd5bc5e6f000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f52646704000000000000000000000000000000000000000000000000000000005f5260045ffd5b61081f915060203d602011610825575b61081781836129e0565b810190612a21565b84610643565b503d61080d565b34610528576103c57fffffffff000000000000000000000000000000000000000000000000000000009161086e6108623661286e565b94919590931633612b46565b610876612ad2565b604051907f1a5f33c500000000000000000000000000000000000000000000000000000000825273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000eff0ae5b39271b33f448cd408b51dc8aa72a672b16600483015260208260248173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000811c6f0ef2e8f8a409306dae242ba70bd4f2467d165afa91821561051d575f92610e6c575b505f9173ffffffffffffffffffffffffffffffffffffffff85165f52600360205260405f20906040519160a083019083821067ffffffffffffffff831117610e3f5761ffff9160409795949397525460ff81161515835260ff8160081c161515602084015260ff8160101c1615156040840152818160181c16606084015260281c1660808201525f945f935b808510610b5e5787878780610b4d575b506109ed575b5f7f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f005d005b60405160205f8183017f095ea7b300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000eff0ae5b39271b33f448cd408b51dc8aa72a672b16602485015281604485015260448452610a6a6064856129e0565b83519082865af15f513d82610b1b575b505015610a88575b506109c8565b610b1491610b0f6040517f095ea7b300000000000000000000000000000000000000000000000000000000602082015273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000eff0ae5b39271b33f448cd408b51dc8aa72a672b1660248201525f604482015260448152610b096064826129e0565b82613eca565b613eca565b8080610a82565b909150610b45575073ffffffffffffffffffffffffffffffffffffffff82163b15155b8380610a7a565b600114610b3e565b610b58903384612d92565b826109c2565b9091929394610b6e868387612a88565b6004813510158061052857610b838235612900565b8135600116610dba57855115610d8a578815610c01575b610528576001918482610bae8a9435612900565b8035600216610be25792610bd091610bd6948d61ffff60608c01511690613a7a565b90612ac5565b955b01939291906109b2565b92610bd091610bfb948d61ffff60608c01511690613827565b95610bd8565b975060019760405160205f8c8284017f095ea7b300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000eff0ae5b39271b33f448cd408b51dc8aa72a672b1660248601527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff604486015260448552610ca46064866129e0565b828551925af18a3d5f519083610d57575b50505015610cc4575b50610b9a565b610d5190610d4b6040517f095ea7b300000000000000000000000000000000000000000000000000000000602082015273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000eff0ae5b39271b33f448cd408b51dc8aa72a672b1660248201525f604482015260448152610d456064826129e0565b8d613eca565b8b613eca565b8a610cbe565b91925090610d8457505073ffffffffffffffffffffffffffffffffffffffff8b163b15155b8a8d80610cb5565b14610d7c565b505094806001917fd3c57cf980d98c7c1f4388b13364aab598e7f99287f61035c5672dc36d9f33e55f80a2610bd8565b50602085015115610e1057600191848892610dd58135612900565b8035600216610df75792610bd091610bfb948d61ffff60808c0151169061360b565b92610bd091610bfb948d61ffff60808c01511690613365565b5094806001917fdb285f4e589a584bb6253e7d2c91592c7706c14408177686ebfa13a1d3c25f5b5f80a2610bd8565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b9091506020813d602011610e98575b81610e88602093836129e0565b8101031261052857519084610926565b3d9150610e7b565b34610528575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261052857602060405173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000811c6f0ef2e8f8a409306dae242ba70bd4f2467d168152f35b346105285760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105285773ffffffffffffffffffffffffffffffffffffffff610f5a61270e565b165f526009602052602060405f2054604051904211158152f35b34610528575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261052857602073ffffffffffffffffffffffffffffffffffffffff5f5416604051908152f35b346105285760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105285773ffffffffffffffffffffffffffffffffffffffff61101061270e565b165f526009602052602060405f2054604051908152f35b346105285760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610528576004359073ffffffffffffffffffffffffffffffffffffffff82168092036105285773ffffffffffffffffffffffffffffffffffffffff5f541633149081156110f1575b501561052857807fffffffffffffffffffffffff00000000000000000000000000000000000000006001541617600155337fa3396fd7f6e0a21b50e5089d2da70d5ac0a3bbbd1f617a93f134b763899801985f80a3005b6001546040517fb70096130000000000000000000000000000000000000000000000000000000081523360048201523060248201527fffffffff000000000000000000000000000000000000000000000000000000009290921660448301529091506020908290606490829073ffffffffffffffffffffffffffffffffffffffff165afa90811561051d575f9161118a575b508261109a565b6111a3915060203d6020116108255761081781836129e0565b82611183565b34610528575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105285760025473ffffffffffffffffffffffffffffffffffffffff81169081330361124e577fffffffffffffffffffffffff00000000000000000000000000000000000000009082825f5416175f5516600255337f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3005b7f4bf4c244000000000000000000000000000000000000000000000000000000005f5260045ffd5b346105285760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105285773ffffffffffffffffffffffffffffffffffffffff6112c261270e565b165f52600360205260a060405f205461ffff6040519160ff81161515835260ff8160081c161515602084015260ff8160101c1615156040840152818160181c16606084015260281c166080820152f35b34610528576101007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105285761134a61270e565b60e07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc3601126105285761137f602091612cb9565b604051908152f35b346105285761139536612786565b73ffffffffffffffffffffffffffffffffffffffff95929194957f000000000000000000000000eff0ae5b39271b33f448cd408b51dc8aa72a672b16968733146116ab57831561168357861561165b5742831115611633576301e133804284031161160b5773ffffffffffffffffffffffffffffffffffffffff811697885f52600360205260ff60405f205460081c16156107ad576040517f8c436aea00000000000000000000000000000000000000000000000000000000815281600482015260208160248173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000811c6f0ef2e8f8a409306dae242ba70bd4f2467d165afa90811561051d575f916115ec575b506107d557851580156115e4575b156115bc578386868a6114de956114cd838d9730903390613282565b87156115b4576003905b33906132e9565b90815f52600860205260ff60405f20541661158c577fa486f7963c056b0ba4a04193f0b389be61cb325f0bc225780c662436c3930bb29561158793835f52600860205260405f2060017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00825416179055604051968796339a8893909796959260c0959260e086019986526020860152604085015260608401526080830152151560a08201520152565b0390a3005b7f33b5de71000000000000000000000000000000000000000000000000000000005f5260045ffd5b6001906114d7565b7fd8315b7e000000000000000000000000000000000000000000000000000000005f5260045ffd5b5082156114b1565b611605915060203d6020116108255761081781836129e0565b8a6114a3565b7f74f1eed3000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f30e98a30000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f7fcc5792000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f5df69010000000000000000000000000000000000000000000000000000000005f5260045ffd5b7fec0982e5000000000000000000000000000000000000000000000000000000005f5260045ffd5b346105285760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610528576004355f526007602052602060ff60405f2054166040519015158152f35b34610528575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610528576020600554604051908152f35b34610528575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610528577f000000000000000000000000eff0ae5b39271b33f448cd408b51dc8aa72a672b6040517f18160ddd00000000000000000000000000000000000000000000000000000000815260208160048173ffffffffffffffffffffffffffffffffffffffff86165afa90811561051d575f91611922575b506040517ffb34cbea00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9092166004830152602482015260208180604481015b038173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000811c6f0ef2e8f8a409306dae242ba70bd4f2467d165afa90811561051d575f916118f0575b5060045490818110156118e65781039081116118b957602090604051908152f35b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b505060205f61137f565b90506020813d60201161191a575b8161190b602093836129e0565b81010312610528575181611898565b3d91506118fe565b90506020813d60201161194e575b8161193d602093836129e0565b81010312610528575161184f6117fd565b3d9150611930565b346105285760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105285773ffffffffffffffffffffffffffffffffffffffff906119d26103c57fffffffff000000000000000000000000000000000000000000000000000000006119ca61270e565b931633612b46565b16805f5260036020525f60408120557f4c910b69fe65a61f7531b9c5042b2329ca7179c77290aa7e2eb3afa3c8511fd35f80a2005b34610528575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261052857602060405173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000eff0ae5b39271b33f448cd408b51dc8aa72a672b168152f35b346105285760c07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261052857611aac61270e565b60a07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc36011261052857611b1a6103c57fffffffff0000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff941633612b46565b1673ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000eff0ae5b39271b33f448cd408b51dc8aa72a672b16808214611ebd5761ffff611b64612a39565b166113888110611e955761271010611e6d5761ffff611b81612a4a565b166113888110611e455761271010611e1d57815f52600360205260405f20611ba7612a5b565b151560ff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff008354169116178155611bdc612a6a565b1515815462ff0000611bec612a79565b151560101b167fffffffffffffffffffffffffffffffffffffffffffffffffff0000ffffffffff64ffff000000611c21612a39565b60181b16927fffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000ff61ff0066ffff0000000000611c5a612a4a565b60281b169660081b16911617161717179055611c74612a6a565b8015611e0f575b8015611e01575b611d0e575b5060405160243580151580910361052857815260443580151580910361052857602082015260643580151580910361052857604082015260843561ffff811680910361052857606082015260a43561ffff8116809103610528578160a09160807fdda77d5657df7cbe071c60ee7ff489407489b74a45333e789b05ba1fc82305dd940152a2005b604051907f4d4405620000000000000000000000000000000000000000000000000000000082526004820152816024820152670de0b6b3a7640000604482015260208160648173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000811c6f0ef2e8f8a409306dae242ba70bd4f2467d165afa90811561051d575f91611dcf575b5015611da75781611c87565b7f35c2785e000000000000000000000000000000000000000000000000000000005f5260045ffd5b90506020813d602011611df9575b81611dea602093836129e0565b81010312610528575182611d9b565b3d9150611ddd565b50611e0a612a79565b611c82565b50611e18612a5b565b611c7b565b7f894fe36f000000000000000000000000000000000000000000000000000000005f5260045ffd5b7fc1cb2896000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f41d1d7cc000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f7830ab88000000000000000000000000000000000000000000000000000000005f5260045ffd5b7fe0c819cf000000000000000000000000000000000000000000000000000000005f5260045ffd5b346105285760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610528576004355f526006602052602060ff60405f2054166040519015158152f35b3461052857602061137f611f45366127f4565b93929092613308565b3461052857611f5c36612786565b73ffffffffffffffffffffffffffffffffffffffff949291947f000000000000000000000000eff0ae5b39271b33f448cd408b51dc8aa72a672b16968733146116ab57861561219d5783156121755742831115611633576301e133804284031161160b5773ffffffffffffffffffffffffffffffffffffffff811697885f52600360205260ff60405f2054161561073557604051907f8c436aea000000000000000000000000000000000000000000000000000000008252600482015260208160248173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000811c6f0ef2e8f8a409306dae242ba70bd4f2467d165afa90811561051d575f91612156575b506107d5578415801561214e575b156115bc57858386868a61209e9561208e82303384613282565b87156121475760029033906132e9565b90815f52600760205260ff60405f20541661158c577f4d135ad145e872e343ba6681e2bf31b89f0ddc75aea8d31b98470f57ad6811cb9561158793835f52600760205260405f2060017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00825416179055604051968796339a8893909796959260c0959260e086019986526020860152604085015260608401526080830152151560a08201520152565b5f906114d7565b508115612074565b61216f915060203d6020116108255761081781836129e0565b89612066565b7f339bbcfd000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f7f3cecfb000000000000000000000000000000000000000000000000000000005f5260045ffd5b34610528576121d336612731565b909173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000eff0ae5b39271b33f448cd408b51dc8aa72a672b1633146116ab5782156122a657811561227e5761224261ffff61222d83612df6565b61223686612f72565b5460181c168483613133565b918211612256576020928261137f92612fa9565b7fecf3d13f000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f6fce1504000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f1ee4dae8000000000000000000000000000000000000000000000000000000005f5260045ffd5b346105285760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261052857600435906123336103c57fffffffff00000000000000000000000000000000000000000000000000000000602435931633612b46565b81156123a55762278d00811161237d57816040917f5439ffe1dfe7a42332f002322d90e28ecc356c2830746daf8e001f5ef8576d67936004558060055582519182526020820152a1005b7fa6de76a1000000000000000000000000000000000000000000000000000000005f5260045ffd5b7fdc89b15a000000000000000000000000000000000000000000000000000000005f5260045ffd5b34610528576123db36612731565b9073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000eff0ae5b39271b33f448cd408b51dc8aa72a672b1633146116ab57801561219d5781156121755761244061ffff61243485612df6565b5460181c168285612e62565b91821061245d578161137f9160209461245883612f72565b612fa9565b7f72885d5b000000000000000000000000000000000000000000000000000000005f5260045ffd5b34610528576101007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610528576124bd61270e565b9060e07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc360112610528576124f0612ad2565b4260c435109081156126db575b50156126b35761250c81612cb9565b9060243560048110156105285780612525600192612900565b166125f757815f52600760205260ff60405f205416156125cf5761258690825f52600760205260405f207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00815416905561257d612937565b60843591612d92565b7f78cf1dc9a00a86b0daa3298b9de02c1418bf08316a8ee556c856889a41ab1e045f80a25f7f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f005d005b7f55dafa38000000000000000000000000000000000000000000000000000000005f5260045ffd5b50805f52600860205260ff60405f205416156125cf57805f52600860205260405f207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00815416905561268a61264a612937565b6064359073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000eff0ae5b39271b33f448cd408b51dc8aa72a672b16612d92565b7f795956e8a7396ce6380ad93558d36195a0038ddf1d1cafa320a5fc8fae94fdc45f80a26109c8565b7fbca6bcc5000000000000000000000000000000000000000000000000000000005f5260045ffd5b61270891507fffffffff000000000000000000000000000000000000000000000000000000001633612b46565b826124fd565b6004359073ffffffffffffffffffffffffffffffffffffffff8216820361052857565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60609101126105285760043573ffffffffffffffffffffffffffffffffffffffff8116810361052857906024359060443590565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60e09101126105285760043573ffffffffffffffffffffffffffffffffffffffff8116810361052857906024359060443590606435906084359060a4359060c43580151581036105285790565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60a09101126105285760043573ffffffffffffffffffffffffffffffffffffffff81168103610528579060243573ffffffffffffffffffffffffffffffffffffffff811681036105285790604435906064359060843590565b60407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc8201126105285760043573ffffffffffffffffffffffffffffffffffffffff81168103610528579160243567ffffffffffffffff811161052857826023820112156105285780600401359267ffffffffffffffff841161052857602460e0850283010111610528576024019190565b6004111561290a57565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b60443573ffffffffffffffffffffffffffffffffffffffff811681036105285790565b3573ffffffffffffffffffffffffffffffffffffffff811681036105285790565b1561298257565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a454400000000000000000000000000000000000000006044820152fd5b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff821117610e3f57604052565b90816020910312610528575180151581036105285790565b60843561ffff811681036105285790565b60a43561ffff811681036105285790565b60243580151581036105285790565b60443580151581036105285790565b60643580151581036105285790565b9190811015612a985760e0020190565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b919082018092116118b957565b7f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f005c612b1e5760017f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f005d565b7f3ee5aeb5000000000000000000000000000000000000000000000000000000005f5260045ffd5b73ffffffffffffffffffffffffffffffffffffffff60015416918215159283612b97575b50508115612b76575090565b905073ffffffffffffffffffffffffffffffffffffffff805f541691161490565b6040517fb700961300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff841660048201523060248201527fffffffff0000000000000000000000000000000000000000000000000000000092909216604483015291925090602090829060649082905afa90811561051d575f91612c30575b50905f80612b6a565b612c49915060203d6020116108255761081781836129e0565b5f612c27565b9694929060c9989694927fffffffffffffffffffffffffffffffffffffffff000000000000000000000000809260601b16895260601b166014880152612c9481612900565b60f81b6028870152602986015260498501526069840152608983015260a98201520190565b60443573ffffffffffffffffffffffffffffffffffffffff811681036105285760243591600483101561052857612d3e90612d12604051938492602084019660e4359260c4359260a4359260643592608435928c612c4f565b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081018352826129e0565b51902090565b612d506020830161295a565b90823591600483101561052857612d12612d3e92604051948593602085019760c08101359360a08201359360808301359360606040850135940135928c612c4f565b6040517fa9059cbb00000000000000000000000000000000000000000000000000000000602082015273ffffffffffffffffffffffffffffffffffffffff929092166024830152604480830193909352918152612df491610b0f6064836129e0565b565b73ffffffffffffffffffffffffffffffffffffffff165f52600360205260405f209060ff825460101c1615612e2757565b7f08f0878e000000000000000000000000000000000000000000000000000000005f5260045ffd5b818102929181159184041417156118b957565b612710612e74612ef794602094612e4f565b6040517f76bf1fbd00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000eff0ae5b39271b33f448cd408b51dc8aa72a672b8116600483015290931660248401520460448201525f606482015291829081906084820190565b038173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000811c6f0ef2e8f8a409306dae242ba70bd4f2467d165afa90811561051d575f91612f43575090565b90506020813d602011612f6a575b81612f5e602093836129e0565b81010312610528575190565b3d9150612f51565b612f7b90613f51565b612f8157565b7fbcffdc15000000000000000000000000000000000000000000000000000000005f5260045ffd5b919091612fb860055442612ac5565b612fc58184868533613308565b90815f52600660205260ff60405f20541661158c57815f52600660205260405f2060017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00825416179055335f52600960205260405f205573ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000eff0ae5b39271b33f448cd408b51dc8aa72a672b1692833b15610528576040517fbca563df000000000000000000000000000000000000000000000000000000008152336004820181905273ffffffffffffffffffffffffffffffffffffffff8516602483015260448201879052606482018390526084820152935f90859060a490829084905af193841561051d5773ffffffffffffffffffffffffffffffffffffffff94613123575b506040519485526020850152604084015216907fc6daf3f44b892b946e8f054dee61a5c544673a355b8ec6fd0ba713e6e67518db60603392a3565b5f61312d916129e0565b5f6130e8565b6040517fbd01e0100000000000000000000000000000000000000000000000000000000081527f000000000000000000000000eff0ae5b39271b33f448cd408b51dc8aa72a672b73ffffffffffffffffffffffffffffffffffffffff908116600483015290911660248201526044810191909152906001606483015260208260848173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000811c6f0ef2e8f8a409306dae242ba70bd4f2467d165afa91821561051d575f9261324e575b50613207818361430c565b9080156132215761271061321e9309151590612ac5565b90565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b9091506020813d60201161327a575b8161326a602093836129e0565b810103126105285751905f6131fc565b3d915061325d565b90919273ffffffffffffffffffffffffffffffffffffffff612df49481604051957f23b872dd000000000000000000000000000000000000000000000000000000006020880152166024860152166044840152606483015260648252610b0f6084836129e0565b969195909394612d3e95612d1294604051988997602089019b8c612c4f565b93919290927fffffffffffffffffffffffffffffffffffffffff0000000000000000000000006040519481602087019760601b16875260601b16603485015260488401526068830152608882015260888152612d3e60a8826129e0565b949391945f9561337c845f9660c0840135906140e4565b6136015761338a8183612d44565b94855f5260086020526133a48660ff60405f205416614117565b613546574260a08301351061355157506133c4604082013593848461414a565b926133d5606083013595868661425a565b61354657855f52600860205260405f207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00815416905573ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000eff0ae5b39271b33f448cd408b51dc8aa72a672b16803b15610528576040517f7c554ed1000000000000000000000000000000000000000000000000000000008152306004820181905273ffffffffffffffffffffffffffffffffffffffff8616602483015260448201879052606482019390935260848101929092525f90829060a490829084905af1801561051d57613503575b50837fb16893cab9262f98d90b32ac8419c725af23f99658c1a389399d387cfea405229493926134f760206134fd940161295a565b90612d92565b039380a2565b6134fd919750938093926135385f7fb16893cab9262f98d90b32ac8419c725af23f99658c1a389399d387cfea40522976129e0565b5f98949550929391506134c2565b505f96505050505050565b96509150506135db9150825f52600860205260405f207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff008154169055604061359b6020830161295a565b9101359073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000eff0ae5b39271b33f448cd408b51dc8aa72a672b16612d92565b7f795956e8a7396ce6380ad93558d36195a0038ddf1d1cafa320a5fc8fae94fdc45f80a2565b505f955050505050565b94939190915f90613623815f9660c0860135906140e4565b613601576136318388612d44565b94855f52600860205261364b8660ff60405f205416614117565b613546574260a0850135106137de5750608083013596613671604085013595868361414a565b9461367d838a8861429e565b6137d2576136938987039360608701358561425a565b6137d257865f52600860205260405f207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00815416905573ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000eff0ae5b39271b33f448cd408b51dc8aa72a672b1695863b15610528576040517f7c554ed1000000000000000000000000000000000000000000000000000000008152306004820181905273ffffffffffffffffffffffffffffffffffffffff85166024830152604482019290925260648101929092526084820152945f90869060a490829084905af194851561051d577fb16893cab9262f98d90b32ac8419c725af23f99658c1a389399d387cfea40522956137b8575b506134f760206137b494950161295a565b80a2565b6137b493505f6137c7916129e0565b5f92506134f76137a3565b505f9750505050505050565b965050506135db9150825f52600860205260405f207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff008154169055604061359b6020830161295a565b949390915f9561383e835f9660c0850135906140e4565b6136015761384c8282612d44565b94855f5260076020526138668660ff60405f205416614117565b613546574260a084013510613a0257506138866040830135948583613133565b9360608301359361389881878761425a565b6137d2576138a690826142d1565b61354657855f52600760205260405f207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00815416905561391f602073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000eff0ae5b39271b33f448cd408b51dc8aa72a672b16940161295a565b92803b15610528576040517fbca563df00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff9384166024820152604481018790526064810192909252929091166084820152905f90829060a490829084905af1801561051d576139ca575b5003927fb638f61d6842803a2d7124f48035f62b92888a3a9eaf14dd1b0d62ee5974218e9080a2565b7fb638f61d6842803a2d7124f48035f62b92888a3a9eaf14dd1b0d62ee5974218e929195505f6139f9916129e0565b5f9490916139a1565b96509150613a549250835f52600760205260405f207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0081541690556060613a4b6020830161295a565b91013591612d92565b7f78cf1dc9a00a86b0daa3298b9de02c1418bf08316a8ee556c856889a41ab1e045f80a2565b94939091945f91613a92815f9660c08a0135906140e4565b61360157613aa08783612d44565b94855f526007602052613aba8660ff60405f205416614117565b613546574260a089013510613c6557506080870135966060810135613ae0838a8361429e565b6137d25788613af29103958685612e62565b91613b028160408401358561425a565b6137d257613b1090836142d1565b61354657855f52600760205260405f207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff008154169055613b89602073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000eff0ae5b39271b33f448cd408b51dc8aa72a672b16920161295a565b94813b15610528576040517fbca563df00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff948516602482015260448101919091526064810192909252939091166084820152915f90839060a490829084905af1801561051d57613c33575b7fb638f61d6842803a2d7124f48035f62b92888a3a9eaf14dd1b0d62ee5974218e915080a2565b505f613c3e916129e0565b7fb638f61d6842803a2d7124f48035f62b92888a3a9eaf14dd1b0d62ee5974218e5f613c0c565b9691925050613a549250835f52600760205260405f207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0081541690556060613a4b6020830161295a565b90613cba8183612d44565b91825f52600860205260ff60405f20541615613da9575f83815260086020526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001690554260a083013510613d965781613d576040613d709401353373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000eff0ae5b39271b33f448cd408b51dc8aa72a672b16612d92565b6060613d656020830161295a565b910135913390613282565b7fb16893cab9262f98d90b32ac8419c725af23f99658c1a389399d387cfea405225f80a2565b5080604061359b60206135db940161295a565b50507fa537f7582610185804288fbfe476e8ca59ef7c01be0690c6c7ee685cc4c9a9d95f80a2565b90613ddc8183612d44565b91825f52600760205260ff60405f20541615613da9575f83815260076020526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001690554260a083013510613eb857606082613e88613e456020613e92960161295a565b6040830135903373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000eff0ae5b39271b33f448cd408b51dc8aa72a672b16613282565b0135903390612d92565b7fb638f61d6842803a2d7124f48035f62b92888a3a9eaf14dd1b0d62ee5974218e5f80a2565b816060613a4b6020613a54950161295a565b905f602091828151910182855af11561051d575f513d613f48575073ffffffffffffffffffffffffffffffffffffffff81163b155b613f065750565b73ffffffffffffffffffffffffffffffffffffffff907f5274afe7000000000000000000000000000000000000000000000000000000005f521660045260245ffd5b60011415613eff565b7f000000000000000000000000eff0ae5b39271b33f448cd408b51dc8aa72a672b906040517f18160ddd00000000000000000000000000000000000000000000000000000000815260208160048173ffffffffffffffffffffffffffffffffffffffff87165afa90811561051d575f916140ae575b5091613fd760209261402b94612ac5565b6040517ffb34cbea00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9092166004830152602482015291829081906044820190565b038173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000811c6f0ef2e8f8a409306dae242ba70bd4f2467d165afa90811561051d575f9161407c575b506004541090565b90506020813d6020116140a6575b81614097602093836129e0565b8101031261052857515f614074565b3d915061408a565b9290506020833d6020116140dc575b816140ca602093836129e0565b81010312610528579151613fd7613fc6565b3d91506140bd565b116140ee57505f90565b7f70e5c3c8df8bd4682c58f6a384763684c37dfd6e3e9e82830975e046ec767f285f80a2600190565b1561412157505f90565b7fa537f7582610185804288fbfe476e8ca59ef7c01be0690c6c7ee685cc4c9a9d95f80a2600190565b6040517fbd01e01000000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000eff0ae5b39271b33f448cd408b51dc8aa72a672b81166004830152909116602482015260448101919091525f60648201526020818060848101038173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000811c6f0ef2e8f8a409306dae242ba70bd4f2467d165afa90811561051d575f91614226575b506127109161422291612e4f565b0490565b90506020813d602011614252575b81614241602093836129e0565b810103126105285751612710614214565b3d9150614234565b90808210614269575050505f90565b7f9bc04c9d3d2ad9d5e7a2d984cd41a3f16bbe32f66f2df5a35e2a1a79ae07c4b59160409182519182526020820152a2600190565b106142a857505f90565b7feaae26b2662e40917d5dc4a575e6887052a2522306e1b8547cbf2f625cbea5e55f80a2600190565b6142da90613f51565b6142e357505f90565b7f56848b1c5996accde9afa26c35895029f9f369205f496c09dd75357c2549d6f25f80a2600190565b906127108202907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6127108409928280851094039380850394146143c157838211156143a957612710829109815f0382168092046002816003021880820260020302808202600203028082026002030280820260020302808202600203028091026002030293600183805f03040190848311900302920304170290565b50634e487b715f52156003026011186020526024601cfd5b509150811561322157049056fea164736f6c634300081d000a
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 33 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
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.